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