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