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