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_ENDPT = 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_ENDPT, 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->IO_ENDPT & (SEL_RD|SEL_WR|SEL_ERR);
|
---|
451 |
|
---|
452 | /* Read blocks when there is no log. */
|
---|
453 | if((m_ptr->IO_ENDPT & 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->IO_ENDPT & 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->IO_ENDPT & 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 | }
|
---|