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