xref: /minix3/minix/drivers/system/log/log.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1*433d6423SLionel Sambuc /* This file contains a driver for:
2*433d6423SLionel Sambuc  *     /dev/klog	- system log device
3*433d6423SLionel Sambuc  *
4*433d6423SLionel Sambuc  * Changes:
5*433d6423SLionel Sambuc  *   21 July 2005   - Support for diagnostic messages (Jorrit N. Herder)
6*433d6423SLionel Sambuc  *    7 July 2005   - Created (Ben Gras)
7*433d6423SLionel Sambuc  */
8*433d6423SLionel Sambuc 
9*433d6423SLionel Sambuc #include "log.h"
10*433d6423SLionel Sambuc #include <sys/time.h>
11*433d6423SLionel Sambuc #include <sys/select.h>
12*433d6423SLionel Sambuc #include <minix/endpoint.h>
13*433d6423SLionel Sambuc 
14*433d6423SLionel Sambuc #define LOG_DEBUG		0	/* enable/ disable debugging */
15*433d6423SLionel Sambuc 
16*433d6423SLionel Sambuc #define NR_DEVS            	1	/* number of minor devices */
17*433d6423SLionel Sambuc #define MINOR_KLOG		0	/* /dev/klog */
18*433d6423SLionel Sambuc 
19*433d6423SLionel Sambuc #define LOGINC(n, i)	do { (n) = (((n) + (i)) % LOG_SIZE); } while(0)
20*433d6423SLionel Sambuc 
21*433d6423SLionel Sambuc struct logdevice logdevices[NR_DEVS];
22*433d6423SLionel Sambuc 
23*433d6423SLionel Sambuc static ssize_t log_read(devminor_t minor, u64_t position, endpoint_t endpt,
24*433d6423SLionel Sambuc 	cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
25*433d6423SLionel Sambuc static ssize_t log_write(devminor_t minor, u64_t position, endpoint_t endpt,
26*433d6423SLionel Sambuc 	cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
27*433d6423SLionel Sambuc static int log_open(devminor_t minor, int access, endpoint_t user_endpt);
28*433d6423SLionel Sambuc static int log_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id);
29*433d6423SLionel Sambuc static int log_select(devminor_t minor, unsigned int ops, endpoint_t endpt);
30*433d6423SLionel Sambuc static int subread(struct logdevice *log, size_t size, endpoint_t endpt,
31*433d6423SLionel Sambuc 	cp_grant_id_t grant);
32*433d6423SLionel Sambuc 
33*433d6423SLionel Sambuc /* Entry points to this driver. */
34*433d6423SLionel Sambuc static struct chardriver log_dtab = {
35*433d6423SLionel Sambuc   .cdr_open	= log_open,
36*433d6423SLionel Sambuc   .cdr_read	= log_read,
37*433d6423SLionel Sambuc   .cdr_write	= log_write,
38*433d6423SLionel Sambuc   .cdr_cancel	= log_cancel,
39*433d6423SLionel Sambuc   .cdr_select	= log_select
40*433d6423SLionel Sambuc };
41*433d6423SLionel Sambuc 
42*433d6423SLionel Sambuc /* SEF functions and variables. */
43*433d6423SLionel Sambuc static void sef_local_startup(void);
44*433d6423SLionel Sambuc static int sef_cb_init_fresh(int type, sef_init_info_t *info);
45*433d6423SLionel Sambuc EXTERN int sef_cb_lu_prepare(int state);
46*433d6423SLionel Sambuc EXTERN int sef_cb_lu_state_isvalid(int state);
47*433d6423SLionel Sambuc EXTERN void sef_cb_lu_state_dump(int state);
48*433d6423SLionel Sambuc static void sef_cb_signal_handler(int signo);
49*433d6423SLionel Sambuc 
50*433d6423SLionel Sambuc /*===========================================================================*
51*433d6423SLionel Sambuc  *				   main 				     *
52*433d6423SLionel Sambuc  *===========================================================================*/
53*433d6423SLionel Sambuc int main(void)
54*433d6423SLionel Sambuc {
55*433d6423SLionel Sambuc   /* SEF local startup. */
56*433d6423SLionel Sambuc   sef_local_startup();
57*433d6423SLionel Sambuc 
58*433d6423SLionel Sambuc   /* Call the generic receive loop. */
59*433d6423SLionel Sambuc   chardriver_task(&log_dtab);
60*433d6423SLionel Sambuc 
61*433d6423SLionel Sambuc   return(OK);
62*433d6423SLionel Sambuc }
63*433d6423SLionel Sambuc 
64*433d6423SLionel Sambuc /*===========================================================================*
65*433d6423SLionel Sambuc  *			       sef_local_startup			     *
66*433d6423SLionel Sambuc  *===========================================================================*/
67*433d6423SLionel Sambuc static void sef_local_startup()
68*433d6423SLionel Sambuc {
69*433d6423SLionel Sambuc   /* Register init callbacks. */
70*433d6423SLionel Sambuc   sef_setcb_init_fresh(sef_cb_init_fresh);
71*433d6423SLionel Sambuc   sef_setcb_init_lu(sef_cb_init_fresh);
72*433d6423SLionel Sambuc   sef_setcb_init_restart(sef_cb_init_fresh);
73*433d6423SLionel Sambuc 
74*433d6423SLionel Sambuc   /* Register live update callbacks. */
75*433d6423SLionel Sambuc   sef_setcb_lu_prepare(sef_cb_lu_prepare);
76*433d6423SLionel Sambuc   sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid);
77*433d6423SLionel Sambuc   sef_setcb_lu_state_dump(sef_cb_lu_state_dump);
78*433d6423SLionel Sambuc 
79*433d6423SLionel Sambuc   /* Register signal callbacks. */
80*433d6423SLionel Sambuc   sef_setcb_signal_handler(sef_cb_signal_handler);
81*433d6423SLionel Sambuc 
82*433d6423SLionel Sambuc   /* Let SEF perform startup. */
83*433d6423SLionel Sambuc   sef_startup();
84*433d6423SLionel Sambuc }
85*433d6423SLionel Sambuc 
86*433d6423SLionel Sambuc /*===========================================================================*
87*433d6423SLionel Sambuc  *		            sef_cb_init_fresh                                *
88*433d6423SLionel Sambuc  *===========================================================================*/
89*433d6423SLionel Sambuc static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
90*433d6423SLionel Sambuc {
91*433d6423SLionel Sambuc /* Initialize the log driver. */
92*433d6423SLionel Sambuc   int i;
93*433d6423SLionel Sambuc 
94*433d6423SLionel Sambuc   /* Initialize log devices. */
95*433d6423SLionel Sambuc   for(i = 0; i < NR_DEVS; i++) {
96*433d6423SLionel Sambuc  	logdevices[i].log_size = logdevices[i].log_read =
97*433d6423SLionel Sambuc 	 	logdevices[i].log_write =
98*433d6423SLionel Sambuc 		logdevices[i].log_selected = 0;
99*433d6423SLionel Sambuc  	logdevices[i].log_source = NONE;
100*433d6423SLionel Sambuc   }
101*433d6423SLionel Sambuc 
102*433d6423SLionel Sambuc   /* Register for diagnostics notifications. */
103*433d6423SLionel Sambuc   sys_diagctl_register();
104*433d6423SLionel Sambuc 
105*433d6423SLionel Sambuc   return(OK);
106*433d6423SLionel Sambuc }
107*433d6423SLionel Sambuc 
108*433d6423SLionel Sambuc /*===========================================================================*
109*433d6423SLionel Sambuc  *		           sef_cb_signal_handler                             *
110*433d6423SLionel Sambuc  *===========================================================================*/
111*433d6423SLionel Sambuc static void sef_cb_signal_handler(int signo)
112*433d6423SLionel Sambuc {
113*433d6423SLionel Sambuc   /* Only check for a pending message from the kernel, ignore anything else. */
114*433d6423SLionel Sambuc   if (signo != SIGKMESS) return;
115*433d6423SLionel Sambuc 
116*433d6423SLionel Sambuc   do_new_kmess();
117*433d6423SLionel Sambuc }
118*433d6423SLionel Sambuc 
119*433d6423SLionel Sambuc /*===========================================================================*
120*433d6423SLionel Sambuc  *				subwrite				     *
121*433d6423SLionel Sambuc  *===========================================================================*/
122*433d6423SLionel Sambuc static int
123*433d6423SLionel Sambuc subwrite(struct logdevice *log, size_t size, endpoint_t endpt,
124*433d6423SLionel Sambuc 	cp_grant_id_t grant, char *localbuf)
125*433d6423SLionel Sambuc {
126*433d6423SLionel Sambuc   size_t count, offset;
127*433d6423SLionel Sambuc   int overflow, r;
128*433d6423SLionel Sambuc   devminor_t minor;
129*433d6423SLionel Sambuc   char *buf;
130*433d6423SLionel Sambuc   message m;
131*433d6423SLionel Sambuc 
132*433d6423SLionel Sambuc   /* With a sufficiently large input size, we might wrap around the ring buffer
133*433d6423SLionel Sambuc    * multiple times.
134*433d6423SLionel Sambuc    */
135*433d6423SLionel Sambuc   for (offset = 0; offset < size; offset += count) {
136*433d6423SLionel Sambuc 	count = size - offset;
137*433d6423SLionel Sambuc 
138*433d6423SLionel Sambuc 	if (log->log_write + count > LOG_SIZE)
139*433d6423SLionel Sambuc 		count = LOG_SIZE - log->log_write;
140*433d6423SLionel Sambuc 	buf = log->log_buffer + log->log_write;
141*433d6423SLionel Sambuc 
142*433d6423SLionel Sambuc 	if(localbuf != NULL) {
143*433d6423SLionel Sambuc 		memcpy(buf, localbuf, count);
144*433d6423SLionel Sambuc 		localbuf += count;
145*433d6423SLionel Sambuc 	}
146*433d6423SLionel Sambuc 	else {
147*433d6423SLionel Sambuc 		if((r=sys_safecopyfrom(endpt, grant, offset,
148*433d6423SLionel Sambuc 			(vir_bytes)buf, count)) != OK)
149*433d6423SLionel Sambuc 			break; /* do process partial write upon error */
150*433d6423SLionel Sambuc 	}
151*433d6423SLionel Sambuc 
152*433d6423SLionel Sambuc 	LOGINC(log->log_write, count);
153*433d6423SLionel Sambuc 	log->log_size += count;
154*433d6423SLionel Sambuc 
155*433d6423SLionel Sambuc         if(log->log_size > LOG_SIZE) {
156*433d6423SLionel Sambuc         	overflow = log->log_size - LOG_SIZE;
157*433d6423SLionel Sambuc         	log->log_size -= overflow;
158*433d6423SLionel Sambuc         	LOGINC(log->log_read, overflow);
159*433d6423SLionel Sambuc         }
160*433d6423SLionel Sambuc 
161*433d6423SLionel Sambuc 	r = offset; /* this will be the return value upon success */
162*433d6423SLionel Sambuc   }
163*433d6423SLionel Sambuc 
164*433d6423SLionel Sambuc   if (log->log_size > 0 && log->log_source != NONE) {
165*433d6423SLionel Sambuc 	/* Someone who was suspended on read can now be revived. */
166*433d6423SLionel Sambuc 	r = subread(log, log->log_iosize, log->log_source, log->log_grant);
167*433d6423SLionel Sambuc 
168*433d6423SLionel Sambuc 	chardriver_reply_task(log->log_source, log->log_id, r);
169*433d6423SLionel Sambuc 
170*433d6423SLionel Sambuc 	log->log_source = NONE;
171*433d6423SLionel Sambuc   }
172*433d6423SLionel Sambuc 
173*433d6423SLionel Sambuc   if (log->log_size > 0 && (log->log_selected & CDEV_OP_RD)) {
174*433d6423SLionel Sambuc 	/* Someone(s) who was/were select()ing can now be awoken. If there was
175*433d6423SLionel Sambuc 	 * a blocking read (above), this can only happen if the blocking read
176*433d6423SLionel Sambuc 	 * didn't swallow all the data (log_size > 0).
177*433d6423SLionel Sambuc 	 */
178*433d6423SLionel Sambuc 	minor = log-logdevices;
179*433d6423SLionel Sambuc #if LOG_DEBUG
180*433d6423SLionel Sambuc 	printf("select sending CDEV_SEL2_REPLY\n");
181*433d6423SLionel Sambuc #endif
182*433d6423SLionel Sambuc 	chardriver_reply_select(log->log_select_proc, minor, CDEV_OP_RD);
183*433d6423SLionel Sambuc 	log->log_selected &= ~CDEV_OP_RD;
184*433d6423SLionel Sambuc   }
185*433d6423SLionel Sambuc 
186*433d6423SLionel Sambuc   return r;
187*433d6423SLionel Sambuc }
188*433d6423SLionel Sambuc 
189*433d6423SLionel Sambuc /*===========================================================================*
190*433d6423SLionel Sambuc  *				log_append				     *
191*433d6423SLionel Sambuc  *===========================================================================*/
192*433d6423SLionel Sambuc void
193*433d6423SLionel Sambuc log_append(char *buf, int count)
194*433d6423SLionel Sambuc {
195*433d6423SLionel Sambuc   int skip = 0;
196*433d6423SLionel Sambuc 
197*433d6423SLionel Sambuc   if(count < 1) return;
198*433d6423SLionel Sambuc   if(count > LOG_SIZE) skip = count - LOG_SIZE;
199*433d6423SLionel Sambuc   count -= skip;
200*433d6423SLionel Sambuc   buf += skip;
201*433d6423SLionel Sambuc 
202*433d6423SLionel Sambuc   subwrite(&logdevices[0], count, SELF, GRANT_INVALID, buf);
203*433d6423SLionel Sambuc }
204*433d6423SLionel Sambuc 
205*433d6423SLionel Sambuc /*===========================================================================*
206*433d6423SLionel Sambuc  *				subread					     *
207*433d6423SLionel Sambuc  *===========================================================================*/
208*433d6423SLionel Sambuc static int
209*433d6423SLionel Sambuc subread(struct logdevice *log, size_t size, endpoint_t endpt,
210*433d6423SLionel Sambuc 	cp_grant_id_t grant)
211*433d6423SLionel Sambuc {
212*433d6423SLionel Sambuc   size_t offset, count;
213*433d6423SLionel Sambuc   char *buf;
214*433d6423SLionel Sambuc   int r;
215*433d6423SLionel Sambuc 
216*433d6423SLionel Sambuc   for (offset = 0; log->log_size > 0 && offset < size; offset += count) {
217*433d6423SLionel Sambuc 	count = size - offset;
218*433d6423SLionel Sambuc 
219*433d6423SLionel Sambuc     	if (count > log->log_size)
220*433d6423SLionel Sambuc     		count = log->log_size;
221*433d6423SLionel Sambuc         if (log->log_read + count > LOG_SIZE)
222*433d6423SLionel Sambuc         	count = LOG_SIZE - log->log_read;
223*433d6423SLionel Sambuc 
224*433d6423SLionel Sambuc     	buf = log->log_buffer + log->log_read;
225*433d6423SLionel Sambuc 	if((r=sys_safecopyto(endpt, grant, offset, (vir_bytes)buf,
226*433d6423SLionel Sambuc 		count)) != OK)
227*433d6423SLionel Sambuc 		return r;
228*433d6423SLionel Sambuc 
229*433d6423SLionel Sambuc   	LOGINC(log->log_read, count);
230*433d6423SLionel Sambuc         log->log_size -= count;
231*433d6423SLionel Sambuc   }
232*433d6423SLionel Sambuc 
233*433d6423SLionel Sambuc   return offset;
234*433d6423SLionel Sambuc }
235*433d6423SLionel Sambuc 
236*433d6423SLionel Sambuc /*===========================================================================*
237*433d6423SLionel Sambuc  *				log_read				     *
238*433d6423SLionel Sambuc  *===========================================================================*/
239*433d6423SLionel Sambuc static ssize_t log_read(devminor_t minor, u64_t UNUSED(position),
240*433d6423SLionel Sambuc 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
241*433d6423SLionel Sambuc 	cdev_id_t id)
242*433d6423SLionel Sambuc {
243*433d6423SLionel Sambuc /* Read from one of the driver's minor devices. */
244*433d6423SLionel Sambuc   struct logdevice *log;
245*433d6423SLionel Sambuc   int r;
246*433d6423SLionel Sambuc 
247*433d6423SLionel Sambuc   if (minor < 0 || minor >= NR_DEVS) return EIO;
248*433d6423SLionel Sambuc   log = &logdevices[minor];
249*433d6423SLionel Sambuc 
250*433d6423SLionel Sambuc   /* If there's already someone hanging to read, don't accept new work. */
251*433d6423SLionel Sambuc   if (log->log_source != NONE) return OK;
252*433d6423SLionel Sambuc 
253*433d6423SLionel Sambuc   if (!log->log_size && size > 0) {
254*433d6423SLionel Sambuc 	if (flags & CDEV_NONBLOCK) return EAGAIN;
255*433d6423SLionel Sambuc 
256*433d6423SLionel Sambuc 	/* No data available; let caller block. */
257*433d6423SLionel Sambuc 	log->log_source = endpt;
258*433d6423SLionel Sambuc 	log->log_iosize = size;
259*433d6423SLionel Sambuc 	log->log_grant = grant;
260*433d6423SLionel Sambuc 	log->log_id = id;
261*433d6423SLionel Sambuc #if LOG_DEBUG
262*433d6423SLionel Sambuc 	printf("blocked %d (%d)\n", log->log_source, id);
263*433d6423SLionel Sambuc #endif
264*433d6423SLionel Sambuc 	return EDONTREPLY;
265*433d6423SLionel Sambuc   }
266*433d6423SLionel Sambuc 
267*433d6423SLionel Sambuc   return subread(log, size, endpt, grant);
268*433d6423SLionel Sambuc }
269*433d6423SLionel Sambuc 
270*433d6423SLionel Sambuc /*===========================================================================*
271*433d6423SLionel Sambuc  *				log_write				     *
272*433d6423SLionel Sambuc  *===========================================================================*/
273*433d6423SLionel Sambuc static ssize_t log_write(devminor_t minor, u64_t UNUSED(position),
274*433d6423SLionel Sambuc 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int UNUSED(flags),
275*433d6423SLionel Sambuc 	cdev_id_t UNUSED(id))
276*433d6423SLionel Sambuc {
277*433d6423SLionel Sambuc /* Write to one of the driver's minor devices. */
278*433d6423SLionel Sambuc   struct logdevice *log;
279*433d6423SLionel Sambuc   int r;
280*433d6423SLionel Sambuc 
281*433d6423SLionel Sambuc   if (minor < 0 || minor >= NR_DEVS) return EIO;
282*433d6423SLionel Sambuc   log = &logdevices[minor];
283*433d6423SLionel Sambuc 
284*433d6423SLionel Sambuc   return subwrite(log, size, endpt, grant, NULL);
285*433d6423SLionel Sambuc }
286*433d6423SLionel Sambuc 
287*433d6423SLionel Sambuc /*============================================================================*
288*433d6423SLionel Sambuc  *				log_open				      *
289*433d6423SLionel Sambuc  *============================================================================*/
290*433d6423SLionel Sambuc static int log_open(devminor_t minor, int UNUSED(access),
291*433d6423SLionel Sambuc 	endpoint_t UNUSED(user_endpt))
292*433d6423SLionel Sambuc {
293*433d6423SLionel Sambuc   if (minor < 0 || minor >= NR_DEVS) return(ENXIO);
294*433d6423SLionel Sambuc 
295*433d6423SLionel Sambuc   return(OK);
296*433d6423SLionel Sambuc }
297*433d6423SLionel Sambuc 
298*433d6423SLionel Sambuc /*============================================================================*
299*433d6423SLionel Sambuc  *				log_cancel				      *
300*433d6423SLionel Sambuc  *============================================================================*/
301*433d6423SLionel Sambuc static int log_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id)
302*433d6423SLionel Sambuc {
303*433d6423SLionel Sambuc   if (minor < 0 || minor >= NR_DEVS)
304*433d6423SLionel Sambuc   	return EINVAL;
305*433d6423SLionel Sambuc 
306*433d6423SLionel Sambuc   /* Not for the suspended request? Must be a stale cancel request. Ignore. */
307*433d6423SLionel Sambuc   if (logdevices[minor].log_source != endpt || logdevices[minor].log_id != id)
308*433d6423SLionel Sambuc 	return EDONTREPLY;
309*433d6423SLionel Sambuc 
310*433d6423SLionel Sambuc   logdevices[minor].log_source = NONE;
311*433d6423SLionel Sambuc 
312*433d6423SLionel Sambuc   return EINTR;	/* this is the reply to the original, interrupted request */
313*433d6423SLionel Sambuc }
314*433d6423SLionel Sambuc 
315*433d6423SLionel Sambuc /*============================================================================*
316*433d6423SLionel Sambuc  *				log_select				      *
317*433d6423SLionel Sambuc  *============================================================================*/
318*433d6423SLionel Sambuc static int log_select(devminor_t minor, unsigned int ops, endpoint_t endpt)
319*433d6423SLionel Sambuc {
320*433d6423SLionel Sambuc   int want_ops, ready_ops = 0;
321*433d6423SLionel Sambuc 
322*433d6423SLionel Sambuc   if (minor < 0 || minor >= NR_DEVS)
323*433d6423SLionel Sambuc 	return ENXIO;
324*433d6423SLionel Sambuc 
325*433d6423SLionel Sambuc   want_ops = ops & (CDEV_OP_RD | CDEV_OP_WR | CDEV_OP_ERR);
326*433d6423SLionel Sambuc 
327*433d6423SLionel Sambuc   /* Read blocks when there is no log. */
328*433d6423SLionel Sambuc   if ((want_ops & CDEV_OP_RD) && logdevices[minor].log_size > 0) {
329*433d6423SLionel Sambuc #if LOG_DEBUG
330*433d6423SLionel Sambuc 	printf("log can read; size %d\n", logdevices[minor].log_size);
331*433d6423SLionel Sambuc #endif
332*433d6423SLionel Sambuc 	ready_ops |= CDEV_OP_RD;
333*433d6423SLionel Sambuc   }
334*433d6423SLionel Sambuc 
335*433d6423SLionel Sambuc   /* Write never blocks. */
336*433d6423SLionel Sambuc   if (want_ops & CDEV_OP_WR) ready_ops |= CDEV_OP_WR;
337*433d6423SLionel Sambuc 
338*433d6423SLionel Sambuc   /* Enable select calback if not all requested operations were ready to go,
339*433d6423SLionel Sambuc    * and notify was enabled.
340*433d6423SLionel Sambuc    */
341*433d6423SLionel Sambuc   want_ops &= ~ready_ops;
342*433d6423SLionel Sambuc   if ((ops & CDEV_NOTIFY) && want_ops) {
343*433d6423SLionel Sambuc 	logdevices[minor].log_selected |= want_ops;
344*433d6423SLionel Sambuc 	logdevices[minor].log_select_proc = endpt;
345*433d6423SLionel Sambuc #if LOG_DEBUG
346*433d6423SLionel Sambuc   	printf("log setting selector.\n");
347*433d6423SLionel Sambuc #endif
348*433d6423SLionel Sambuc   }
349*433d6423SLionel Sambuc 
350*433d6423SLionel Sambuc #if LOG_DEBUG
351*433d6423SLionel Sambuc   printf("log returning ops %d\n", ready_ops);
352*433d6423SLionel Sambuc #endif
353*433d6423SLionel Sambuc 
354*433d6423SLionel Sambuc   return(ready_ops);
355*433d6423SLionel Sambuc }
356