FreeBSD/Linux Kernel Cross Reference
sys/drivers/log/log.c
1 /* This file contains a driver for:
2 * /dev/klog - system log device
3 *
4 * Changes:
5 * 21 July 2005 - Support for diagnostic messages (Jorrit N. Herder)
6 * 7 July 2005 - Created (Ben Gras)
7 */
8
9 #include "log.h"
10 #include <sys/time.h>
11 #include <sys/select.h>
12 #include "../../kernel/const.h"
13 #include "../../kernel/type.h"
14
15 #define LOG_DEBUG 0 /* enable/ disable debugging */
16
17 #define NR_DEVS 1 /* number of minor devices */
18 #define MINOR_KLOG 0 /* /dev/klog */
19
20 #define LOGINC(n, i) do { (n) = (((n) + (i)) % LOG_SIZE); } while(0)
21
22 PUBLIC struct logdevice logdevices[NR_DEVS];
23 PRIVATE struct device log_geom[NR_DEVS]; /* base and size of devices */
24 PRIVATE int log_device = -1; /* current device */
25
26 FORWARD _PROTOTYPE( char *log_name, (void) );
27 FORWARD _PROTOTYPE( struct device *log_prepare, (int device) );
28 FORWARD _PROTOTYPE( int log_transfer, (int proc_nr, int opcode, off_t position,
29 iovec_t *iov, unsigned nr_req) );
30 FORWARD _PROTOTYPE( int log_do_open, (struct driver *dp, message *m_ptr) );
31 FORWARD _PROTOTYPE( int log_cancel, (struct driver *dp, message *m_ptr) );
32 FORWARD _PROTOTYPE( int log_select, (struct driver *dp, message *m_ptr) );
33 FORWARD _PROTOTYPE( void log_signal, (struct driver *dp, message *m_ptr) );
34 FORWARD _PROTOTYPE( int log_other, (struct driver *dp, message *m_ptr) );
35 FORWARD _PROTOTYPE( void log_geometry, (struct partition *entry) );
36 FORWARD _PROTOTYPE( int subread, (struct logdevice *log, int count, int proc_nr, vir_bytes user_vir) );
37
38 /* Entry points to this driver. */
39 PRIVATE struct driver log_dtab = {
40 log_name, /* current device's name */
41 log_do_open, /* open or mount */
42 do_nop, /* nothing on a close */
43 do_nop, /* ioctl nop */
44 log_prepare, /* prepare for I/O on a given minor device */
45 log_transfer, /* do the I/O */
46 nop_cleanup, /* no need to clean up */
47 log_geometry, /* geometry */
48 log_signal, /* handle system signal */
49 nop_alarm, /* no alarm */
50 log_cancel, /* CANCEL request */
51 log_select, /* DEV_SELECT request */
52 log_other, /* Unrecognized messages */
53 NULL /* HW int */
54 };
55
56 extern int device_caller;
57
58 /*===========================================================================*
59 * main *
60 *===========================================================================*/
61 PUBLIC int main(void)
62 {
63 int i;
64 for(i = 0; i < NR_DEVS; i++) {
65 log_geom[i].dv_size = cvul64(LOG_SIZE);
66 log_geom[i].dv_base = cvul64((long)logdevices[i].log_buffer);
67 logdevices[i].log_size = logdevices[i].log_read =
68 logdevices[i].log_write =
69 logdevices[i].log_select_alerted =
70 logdevices[i].log_selected =
71 logdevices[i].log_select_ready_ops = 0;
72 logdevices[i].log_proc_nr = 0;
73 logdevices[i].log_revive_alerted = 0;
74 }
75 driver_task(&log_dtab);
76 return(OK);
77 }
78
79 /*===========================================================================*
80 * log_name *
81 *===========================================================================*/
82 PRIVATE char *log_name()
83 {
84 /* Return a name for the current device. */
85 static char name[] = "log";
86 return name;
87 }
88
89 /*===========================================================================*
90 * log_prepare *
91 *===========================================================================*/
92 PRIVATE struct device *log_prepare(device)
93 int device;
94 {
95 /* Prepare for I/O on a device: check if the minor device number is ok. */
96
97 if (device < 0 || device >= NR_DEVS) return(NIL_DEV);
98 log_device = device;
99
100 return(&log_geom[device]);
101 }
102
103 /*===========================================================================*
104 * subwrite *
105 *===========================================================================*/
106 PRIVATE int
107 subwrite(struct logdevice *log, int count, int proc_nr, vir_bytes user_vir)
108 {
109 char *buf;
110 int r;
111 if (log->log_write + count > LOG_SIZE)
112 count = LOG_SIZE - log->log_write;
113 buf = log->log_buffer + log->log_write;
114
115 if(proc_nr == SELF) {
116 memcpy(buf, (char *) user_vir, count);
117 }
118 else {
119 if((r=sys_vircopy(proc_nr,D,user_vir, SELF,D,(int)buf, count)) != OK)
120 return r;
121 }
122
123 LOGINC(log->log_write, count);
124 log->log_size += count;
125
126 if(log->log_size > LOG_SIZE) {
127 int overflow;
128 overflow = log->log_size - LOG_SIZE;
129 log->log_size -= overflow;
130 LOGINC(log->log_read, overflow);
131 }
132
133 if(log->log_size > 0 && log->log_proc_nr && !log->log_revive_alerted) {
134 /* Someone who was suspended on read can now
135 * be revived.
136 */
137 log->log_status = subread(log, log->log_iosize,
138 log->log_proc_nr, log->log_user_vir);
139 notify(log->log_source);
140 log->log_revive_alerted = 1;
141 }
142
143 if(log->log_size > 0)
144 log->log_select_ready_ops |= SEL_RD;
145
146 if(log->log_size > 0 && log->log_selected &&
147 !(log->log_select_alerted)) {
148 /* Someone(s) who was/were select()ing can now
149 * be awoken. If there was a blocking read (above),
150 * this can only happen if the blocking read didn't
151 * swallow all the data (log_size > 0).
152 */
153 if(log->log_selected & SEL_RD) {
154 notify(log->log_select_proc);
155 log->log_select_alerted = 1;
156 #if LOG_DEBUG
157 printf("log notified %d\n", log->log_select_proc);
158 #endif
159 }
160 }
161
162 return count;
163 }
164
165 /*===========================================================================*
166 * log_append *
167 *===========================================================================*/
168 PUBLIC void
169 log_append(char *buf, int count)
170 {
171 int w = 0, skip = 0;
172
173 if(count < 1) return;
174 if(count > LOG_SIZE) skip = count - LOG_SIZE;
175 count -= skip;
176 buf += skip;
177 w = subwrite(&logdevices[0], count, SELF, (vir_bytes) buf);
178
179 if(w > 0 && w < count)
180 subwrite(&logdevices[0], count-w, SELF, (vir_bytes) buf+w);
181 return;
182 }
183
184 /*===========================================================================*
185 * subread *
186 *===========================================================================*/
187 PRIVATE int
188 subread(struct logdevice *log, int count, int proc_nr, vir_bytes user_vir)
189 {
190 char *buf;
191 int r;
192 if (count > log->log_size)
193 count = log->log_size;
194 if (log->log_read + count > LOG_SIZE)
195 count = LOG_SIZE - log->log_read;
196
197 buf = log->log_buffer + log->log_read;
198 if((r=sys_vircopy(SELF,D,(int)buf,proc_nr,D,user_vir, count)) != OK)
199 return r;
200
201 LOGINC(log->log_read, count);
202 log->log_size -= count;
203
204 return count;
205 }
206
207 /*===========================================================================*
208 * log_transfer *
209 *===========================================================================*/
210 PRIVATE int log_transfer(proc_nr, opcode, position, iov, nr_req)
211 int proc_nr; /* process doing the request */
212 int opcode; /* DEV_GATHER or DEV_SCATTER */
213 off_t position; /* offset on device to read or write */
214 iovec_t *iov; /* pointer to read or write request vector */
215 unsigned nr_req; /* length of request vector */
216 {
217 /* Read or write one the driver's minor devices. */
218 unsigned count;
219 vir_bytes user_vir;
220 struct device *dv;
221 unsigned long dv_size;
222 int accumulated_read = 0;
223 struct logdevice *log;
224 static int f;
225
226 if(log_device < 0 || log_device >= NR_DEVS)
227 return EIO;
228
229 /* Get minor device number and check for /dev/null. */
230 dv = &log_geom[log_device];
231 dv_size = cv64ul(dv->dv_size);
232 log = &logdevices[log_device];
233
234 while (nr_req > 0) {
235 /* How much to transfer and where to / from. */
236 count = iov->iov_size;
237 user_vir = iov->iov_addr;
238
239 switch (log_device) {
240
241 case MINOR_KLOG:
242 if (opcode == DEV_GATHER) {
243 if (log->log_proc_nr || count < 1) {
244 /* There's already someone hanging to read, or
245 * no real I/O requested.
246 */
247 return(OK);
248 }
249
250 if (!log->log_size) {
251 if(accumulated_read)
252 return OK;
253 /* No data available; let caller block. */
254 log->log_proc_nr = proc_nr;
255 log->log_iosize = count;
256 log->log_user_vir = user_vir;
257 log->log_revive_alerted = 0;
258
259 /* Device_caller is a global in drivers library. */
260 log->log_source = device_caller;
261 #if LOG_DEBUG
262 printf("blocked %d (%d)\n",
263 log->log_source, log->log_proc_nr);
264 #endif
265 return(SUSPEND);
266 }
267 count = subread(log, count, proc_nr, user_vir);
268 if(count < 0) {
269 return count;
270 }
271 accumulated_read += count;
272 } else {
273 count = subwrite(log, count, proc_nr, user_vir);
274 if(count < 0)
275 return count;
276 }
277 break;
278 /* Unknown (illegal) minor device. */
279 default:
280 return(EINVAL);
281 }
282
283 /* Book the number of bytes transferred. */
284 iov->iov_addr += count;
285 if ((iov->iov_size -= count) == 0) { iov++; nr_req--; }
286 }
287 return(OK);
288 }
289
290 /*============================================================================*
291 * log_do_open *
292 *============================================================================*/
293 PRIVATE int log_do_open(dp, m_ptr)
294 struct driver *dp;
295 message *m_ptr;
296 {
297 if (log_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO);
298 return(OK);
299 }
300
301 /*============================================================================*
302 * log_geometry *
303 *============================================================================*/
304 PRIVATE void log_geometry(entry)
305 struct partition *entry;
306 {
307 /* take a page from the fake memory device geometry */
308 entry->heads = 64;
309 entry->sectors = 32;
310 entry->cylinders = div64u(log_geom[log_device].dv_size, SECTOR_SIZE) /
311 (entry->heads * entry->sectors);
312 }
313
314 /*============================================================================*
315 * log_cancel *
316 *============================================================================*/
317 PRIVATE int log_cancel(dp, m_ptr)
318 struct driver *dp;
319 message *m_ptr;
320 {
321 int d;
322 d = m_ptr->TTY_LINE;
323 if(d < 0 || d >= NR_DEVS)
324 return EINVAL;
325 logdevices[d].log_proc_nr = 0;
326 logdevices[d].log_revive_alerted = 0;
327 return(OK);
328 }
329
330 /*============================================================================*
331 * do_status *
332 *============================================================================*/
333 PRIVATE void do_status(message *m_ptr)
334 {
335 int d;
336 message m;
337
338 /* Caller has requested pending status information, which currently
339 * can be pending available select()s, or REVIVE events. One message
340 * is returned for every event, or DEV_NO_STATUS if no (more) events
341 * are to be returned.
342 */
343
344 for(d = 0; d < NR_DEVS; d++) {
345 /* Check for revive callback. */
346 if(logdevices[d].log_proc_nr && logdevices[d].log_revive_alerted
347 && logdevices[d].log_source == m_ptr->m_source) {
348 m.m_type = DEV_REVIVE;
349 m.REP_PROC_NR = logdevices[d].log_proc_nr;
350 m.REP_STATUS = logdevices[d].log_status;
351 send(m_ptr->m_source, &m);
352 logdevices[d].log_proc_nr = 0;
353 logdevices[d].log_revive_alerted = 0;
354 #if LOG_DEBUG
355 printf("revived %d with %d bytes\n",
356 m.REP_PROC_NR, m.REP_STATUS);
357 #endif
358 return;
359 }
360
361 /* Check for select callback. */
362 if(logdevices[d].log_selected && logdevices[d].log_select_proc == m_ptr->m_source
363 && logdevices[d].log_select_alerted) {
364 m.m_type = DEV_IO_READY;
365 m.DEV_SEL_OPS = logdevices[d].log_select_ready_ops;
366 m.DEV_MINOR = d;
367 #if LOG_DEBUG
368 printf("select sending sent\n");
369 #endif
370 send(m_ptr->m_source, &m);
371 logdevices[d].log_selected &= ~logdevices[d].log_select_ready_ops;
372 logdevices[d].log_select_alerted = 0;
373 #if LOG_DEBUG
374 printf("select send sent\n");
375 #endif
376 return;
377 }
378 }
379
380 /* No event found. */
381 m.m_type = DEV_NO_STATUS;
382 send(m_ptr->m_source, &m);
383
384 return;
385 }
386
387 /*============================================================================*
388 * log_signal *
389 *============================================================================*/
390 PRIVATE void log_signal(dp, m_ptr)
391 struct driver *dp;
392 message *m_ptr;
393 {
394 sigset_t sigset = m_ptr->NOTIFY_ARG;
395 if (sigismember(&sigset, SIGKMESS)) {
396 do_new_kmess(m_ptr);
397 }
398 }
399
400
401 /*============================================================================*
402 * log_other *
403 *============================================================================*/
404 PRIVATE int log_other(dp, m_ptr)
405 struct driver *dp;
406 message *m_ptr;
407 {
408 int r;
409
410 /* This function gets messages that the generic driver doesn't
411 * understand.
412 */
413 switch(m_ptr->m_type) {
414 case DIAGNOSTICS: {
415 r = do_diagnostics(m_ptr);
416 break;
417 }
418 case DEV_STATUS: {
419 do_status(m_ptr);
420 r = EDONTREPLY;
421 break;
422 }
423 case NOTIFY_FROM(TTY_PROC_NR):
424 do_new_kmess(m_ptr);
425 r = EDONTREPLY;
426 break;
427 default:
428 r = EINVAL;
429 break;
430 }
431 return r;
432 }
433
434 /*============================================================================*
435 * log_select *
436 *============================================================================*/
437 PRIVATE int log_select(dp, m_ptr)
438 struct driver *dp;
439 message *m_ptr;
440 {
441 int d, ready_ops = 0, ops = 0;
442 d = m_ptr->TTY_LINE;
443 if(d < 0 || d >= NR_DEVS) {
444 #if LOG_DEBUG
445 printf("line %d? EINVAL\n", d);
446 #endif
447 return EINVAL;
448 }
449
450 ops = m_ptr->PROC_NR & (SEL_RD|SEL_WR|SEL_ERR);
451
452 /* Read blocks when there is no log. */
453 if((m_ptr->PROC_NR & SEL_RD) && logdevices[d].log_size > 0) {
454 #if LOG_DEBUG
455 printf("log can read; size %d\n", logdevices[d].log_size);
456 #endif
457 ready_ops |= SEL_RD; /* writes never block */
458 }
459
460 /* Write never blocks. */
461 if(m_ptr->PROC_NR & SEL_WR) ready_ops |= SEL_WR;
462
463 /* Enable select calback if no operations were
464 * ready to go, but operations were requested,
465 * and notify was enabled.
466 */
467 if((m_ptr->PROC_NR & SEL_NOTIFY) && ops && !ready_ops) {
468 logdevices[d].log_selected |= ops;
469 logdevices[d].log_select_proc = m_ptr->m_source;
470 #if LOG_DEBUG
471 printf("log setting selector.\n");
472 #endif
473 }
474
475 #if LOG_DEBUG
476 printf("log returning ops %d\n", ready_ops);
477 #endif
478
479 return(ready_ops);
480 }
Cache object: 1ec99a269414280f4fd93e807512cfa1
|