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