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