source: trunk/minix/drivers/log/log.c@ 20

Last change on this file since 20 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 13.6 KB
Line 
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
22PUBLIC struct logdevice logdevices[NR_DEVS];
23PRIVATE struct device log_geom[NR_DEVS]; /* base and size of devices */
24PRIVATE int log_device = -1; /* current device */
25
26FORWARD _PROTOTYPE( char *log_name, (void) );
27FORWARD _PROTOTYPE( struct device *log_prepare, (int device) );
28FORWARD _PROTOTYPE( int log_transfer, (int proc_nr, int opcode, off_t position,
29 iovec_t *iov, unsigned nr_req) );
30FORWARD _PROTOTYPE( int log_do_open, (struct driver *dp, message *m_ptr) );
31FORWARD _PROTOTYPE( int log_cancel, (struct driver *dp, message *m_ptr) );
32FORWARD _PROTOTYPE( int log_select, (struct driver *dp, message *m_ptr) );
33FORWARD _PROTOTYPE( void log_signal, (struct driver *dp, message *m_ptr) );
34FORWARD _PROTOTYPE( int log_other, (struct driver *dp, message *m_ptr) );
35FORWARD _PROTOTYPE( void log_geometry, (struct partition *entry) );
36FORWARD _PROTOTYPE( int subread, (struct logdevice *log, int count, int proc_nr, vir_bytes user_vir) );
37
38/* Entry points to this driver. */
39PRIVATE 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
56extern int device_caller;
57
58/*===========================================================================*
59 * main *
60 *===========================================================================*/
61PUBLIC 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 *===========================================================================*/
82PRIVATE 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 *===========================================================================*/
92PRIVATE struct device *log_prepare(device)
93int 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 *===========================================================================*/
106PRIVATE int
107subwrite(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 *===========================================================================*/
168PUBLIC void
169log_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 *===========================================================================*/
187PRIVATE int
188subread(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 *===========================================================================*/
210PRIVATE int log_transfer(proc_nr, opcode, position, iov, nr_req)
211int proc_nr; /* process doing the request */
212int opcode; /* DEV_GATHER or DEV_SCATTER */
213off_t position; /* offset on device to read or write */
214iovec_t *iov; /* pointer to read or write request vector */
215unsigned 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 *============================================================================*/
293PRIVATE int log_do_open(dp, m_ptr)
294struct driver *dp;
295message *m_ptr;
296{
297 if (log_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO);
298 return(OK);
299}
300
301/*============================================================================*
302 * log_geometry *
303 *============================================================================*/
304PRIVATE void log_geometry(entry)
305struct 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 *============================================================================*/
317PRIVATE int log_cancel(dp, m_ptr)
318struct driver *dp;
319message *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 *============================================================================*/
333PRIVATE 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 *============================================================================*/
390PRIVATE void log_signal(dp, m_ptr)
391struct driver *dp;
392message *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 *============================================================================*/
404PRIVATE int log_other(dp, m_ptr)
405struct driver *dp;
406message *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 *============================================================================*/
437PRIVATE int log_select(dp, m_ptr)
438struct driver *dp;
439message *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}
Note: See TracBrowser for help on using the repository browser.