1433d6423SLionel Sambuc /* This file contains a driver for: 2433d6423SLionel Sambuc * /dev/klog - system log device 3433d6423SLionel Sambuc * 4433d6423SLionel Sambuc * Changes: 5433d6423SLionel Sambuc * 21 July 2005 - Support for diagnostic messages (Jorrit N. Herder) 6433d6423SLionel Sambuc * 7 July 2005 - Created (Ben Gras) 7433d6423SLionel Sambuc */ 8433d6423SLionel Sambuc 9433d6423SLionel Sambuc #include "log.h" 10433d6423SLionel Sambuc #include <sys/time.h> 11433d6423SLionel Sambuc #include <sys/select.h> 12433d6423SLionel Sambuc #include <minix/endpoint.h> 13433d6423SLionel Sambuc 14433d6423SLionel Sambuc #define LOG_DEBUG 0 /* enable/ disable debugging */ 15433d6423SLionel Sambuc 16433d6423SLionel Sambuc #define NR_DEVS 1 /* number of minor devices */ 17433d6423SLionel Sambuc #define MINOR_KLOG 0 /* /dev/klog */ 18433d6423SLionel Sambuc 19433d6423SLionel Sambuc #define LOGINC(n, i) do { (n) = (((n) + (i)) % LOG_SIZE); } while(0) 20433d6423SLionel Sambuc 21433d6423SLionel Sambuc struct logdevice logdevices[NR_DEVS]; 22433d6423SLionel Sambuc 23433d6423SLionel Sambuc static ssize_t log_read(devminor_t minor, u64_t position, endpoint_t endpt, 24433d6423SLionel Sambuc cp_grant_id_t grant, size_t size, int flags, cdev_id_t id); 25433d6423SLionel Sambuc static ssize_t log_write(devminor_t minor, u64_t position, endpoint_t endpt, 26433d6423SLionel Sambuc cp_grant_id_t grant, size_t size, int flags, cdev_id_t id); 27433d6423SLionel Sambuc static int log_open(devminor_t minor, int access, endpoint_t user_endpt); 28433d6423SLionel Sambuc static int log_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id); 29433d6423SLionel Sambuc static int log_select(devminor_t minor, unsigned int ops, endpoint_t endpt); 30433d6423SLionel Sambuc static int subread(struct logdevice *log, size_t size, endpoint_t endpt, 31433d6423SLionel Sambuc cp_grant_id_t grant); 32433d6423SLionel Sambuc 33433d6423SLionel Sambuc /* Entry points to this driver. */ 34433d6423SLionel Sambuc static struct chardriver log_dtab = { 35433d6423SLionel Sambuc .cdr_open = log_open, 36433d6423SLionel Sambuc .cdr_read = log_read, 37433d6423SLionel Sambuc .cdr_write = log_write, 38433d6423SLionel Sambuc .cdr_cancel = log_cancel, 39433d6423SLionel Sambuc .cdr_select = log_select 40433d6423SLionel Sambuc }; 41433d6423SLionel Sambuc 42433d6423SLionel Sambuc /* SEF functions and variables. */ 43433d6423SLionel Sambuc static void sef_local_startup(void); 44433d6423SLionel Sambuc static int sef_cb_init_fresh(int type, sef_init_info_t *info); 45433d6423SLionel Sambuc EXTERN int sef_cb_lu_prepare(int state); 46*e1f889d2SCristiano Giuffrida EXTERN int sef_cb_lu_state_isvalid(int state, int flags); 47433d6423SLionel Sambuc EXTERN void sef_cb_lu_state_dump(int state); 48433d6423SLionel Sambuc static void sef_cb_signal_handler(int signo); 49433d6423SLionel Sambuc 50433d6423SLionel Sambuc /*===========================================================================* 51433d6423SLionel Sambuc * main * 52433d6423SLionel Sambuc *===========================================================================*/ 53433d6423SLionel Sambuc int main(void) 54433d6423SLionel Sambuc { 55433d6423SLionel Sambuc /* SEF local startup. */ 56433d6423SLionel Sambuc sef_local_startup(); 57433d6423SLionel Sambuc 58433d6423SLionel Sambuc /* Call the generic receive loop. */ 59433d6423SLionel Sambuc chardriver_task(&log_dtab); 60433d6423SLionel Sambuc 61433d6423SLionel Sambuc return(OK); 62433d6423SLionel Sambuc } 63433d6423SLionel Sambuc 64433d6423SLionel Sambuc /*===========================================================================* 65433d6423SLionel Sambuc * sef_local_startup * 66433d6423SLionel Sambuc *===========================================================================*/ 67433d6423SLionel Sambuc static void sef_local_startup() 68433d6423SLionel Sambuc { 69433d6423SLionel Sambuc /* Register init callbacks. */ 70433d6423SLionel Sambuc sef_setcb_init_fresh(sef_cb_init_fresh); 71433d6423SLionel Sambuc sef_setcb_init_lu(sef_cb_init_fresh); 72433d6423SLionel Sambuc 73433d6423SLionel Sambuc /* Register live update callbacks. */ 74433d6423SLionel Sambuc sef_setcb_lu_prepare(sef_cb_lu_prepare); 75433d6423SLionel Sambuc sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid); 76433d6423SLionel Sambuc sef_setcb_lu_state_dump(sef_cb_lu_state_dump); 77433d6423SLionel Sambuc 78433d6423SLionel Sambuc /* Register signal callbacks. */ 79433d6423SLionel Sambuc sef_setcb_signal_handler(sef_cb_signal_handler); 80433d6423SLionel Sambuc 81433d6423SLionel Sambuc /* Let SEF perform startup. */ 82433d6423SLionel Sambuc sef_startup(); 83433d6423SLionel Sambuc } 84433d6423SLionel Sambuc 85433d6423SLionel Sambuc /*===========================================================================* 86433d6423SLionel Sambuc * sef_cb_init_fresh * 87433d6423SLionel Sambuc *===========================================================================*/ 88433d6423SLionel Sambuc static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info)) 89433d6423SLionel Sambuc { 90433d6423SLionel Sambuc /* Initialize the log driver. */ 91433d6423SLionel Sambuc int i; 92433d6423SLionel Sambuc 93433d6423SLionel Sambuc /* Initialize log devices. */ 94433d6423SLionel Sambuc for(i = 0; i < NR_DEVS; i++) { 95433d6423SLionel Sambuc logdevices[i].log_size = logdevices[i].log_read = 96433d6423SLionel Sambuc logdevices[i].log_write = 97433d6423SLionel Sambuc logdevices[i].log_selected = 0; 98433d6423SLionel Sambuc logdevices[i].log_source = NONE; 99433d6423SLionel Sambuc } 100433d6423SLionel Sambuc 101433d6423SLionel Sambuc /* Register for diagnostics notifications. */ 102433d6423SLionel Sambuc sys_diagctl_register(); 103433d6423SLionel Sambuc 10467b47183SLionel Sambuc /* Announce we are up! */ 10567b47183SLionel Sambuc chardriver_announce(); 10667b47183SLionel Sambuc 107433d6423SLionel Sambuc return(OK); 108433d6423SLionel Sambuc } 109433d6423SLionel Sambuc 110433d6423SLionel Sambuc /*===========================================================================* 111433d6423SLionel Sambuc * sef_cb_signal_handler * 112433d6423SLionel Sambuc *===========================================================================*/ 113433d6423SLionel Sambuc static void sef_cb_signal_handler(int signo) 114433d6423SLionel Sambuc { 115433d6423SLionel Sambuc /* Only check for a pending message from the kernel, ignore anything else. */ 116433d6423SLionel Sambuc if (signo != SIGKMESS) return; 117433d6423SLionel Sambuc 118433d6423SLionel Sambuc do_new_kmess(); 119433d6423SLionel Sambuc } 120433d6423SLionel Sambuc 121433d6423SLionel Sambuc /*===========================================================================* 122433d6423SLionel Sambuc * subwrite * 123433d6423SLionel Sambuc *===========================================================================*/ 124433d6423SLionel Sambuc static int 125433d6423SLionel Sambuc subwrite(struct logdevice *log, size_t size, endpoint_t endpt, 126433d6423SLionel Sambuc cp_grant_id_t grant, char *localbuf) 127433d6423SLionel Sambuc { 128433d6423SLionel Sambuc size_t count, offset; 129818c405aSDavid van Moolenbroek int overflow, r, result; 130433d6423SLionel Sambuc devminor_t minor; 131433d6423SLionel Sambuc char *buf; 132433d6423SLionel Sambuc message m; 133433d6423SLionel Sambuc 134433d6423SLionel Sambuc /* With a sufficiently large input size, we might wrap around the ring buffer 135433d6423SLionel Sambuc * multiple times. 136433d6423SLionel Sambuc */ 137818c405aSDavid van Moolenbroek result = 0; 138433d6423SLionel Sambuc for (offset = 0; offset < size; offset += count) { 139433d6423SLionel Sambuc count = size - offset; 140433d6423SLionel Sambuc 141433d6423SLionel Sambuc if (log->log_write + count > LOG_SIZE) 142433d6423SLionel Sambuc count = LOG_SIZE - log->log_write; 143433d6423SLionel Sambuc buf = log->log_buffer + log->log_write; 144433d6423SLionel Sambuc 145433d6423SLionel Sambuc if(localbuf != NULL) { 146433d6423SLionel Sambuc memcpy(buf, localbuf, count); 147433d6423SLionel Sambuc localbuf += count; 148433d6423SLionel Sambuc } 149433d6423SLionel Sambuc else { 150433d6423SLionel Sambuc if((r=sys_safecopyfrom(endpt, grant, offset, 151818c405aSDavid van Moolenbroek (vir_bytes)buf, count)) != OK) { 152818c405aSDavid van Moolenbroek /* return any partial success upon error */ 153818c405aSDavid van Moolenbroek result = (offset > 0) ? (int)offset : r; 154818c405aSDavid van Moolenbroek break; 155818c405aSDavid van Moolenbroek } 156433d6423SLionel Sambuc } 157433d6423SLionel Sambuc 158433d6423SLionel Sambuc LOGINC(log->log_write, count); 159433d6423SLionel Sambuc log->log_size += count; 160433d6423SLionel Sambuc 161433d6423SLionel Sambuc if(log->log_size > LOG_SIZE) { 162433d6423SLionel Sambuc overflow = log->log_size - LOG_SIZE; 163433d6423SLionel Sambuc log->log_size -= overflow; 164433d6423SLionel Sambuc LOGINC(log->log_read, overflow); 165433d6423SLionel Sambuc } 166433d6423SLionel Sambuc 167818c405aSDavid van Moolenbroek result += (int)count; 168433d6423SLionel Sambuc } 169433d6423SLionel Sambuc 170433d6423SLionel Sambuc if (log->log_size > 0 && log->log_source != NONE) { 171433d6423SLionel Sambuc /* Someone who was suspended on read can now be revived. */ 172433d6423SLionel Sambuc r = subread(log, log->log_iosize, log->log_source, log->log_grant); 173433d6423SLionel Sambuc 174433d6423SLionel Sambuc chardriver_reply_task(log->log_source, log->log_id, r); 175433d6423SLionel Sambuc 176433d6423SLionel Sambuc log->log_source = NONE; 177433d6423SLionel Sambuc } 178433d6423SLionel Sambuc 179433d6423SLionel Sambuc if (log->log_size > 0 && (log->log_selected & CDEV_OP_RD)) { 180433d6423SLionel Sambuc /* Someone(s) who was/were select()ing can now be awoken. If there was 181433d6423SLionel Sambuc * a blocking read (above), this can only happen if the blocking read 182433d6423SLionel Sambuc * didn't swallow all the data (log_size > 0). 183433d6423SLionel Sambuc */ 184433d6423SLionel Sambuc minor = log-logdevices; 185433d6423SLionel Sambuc #if LOG_DEBUG 186433d6423SLionel Sambuc printf("select sending CDEV_SEL2_REPLY\n"); 187433d6423SLionel Sambuc #endif 188433d6423SLionel Sambuc chardriver_reply_select(log->log_select_proc, minor, CDEV_OP_RD); 189433d6423SLionel Sambuc log->log_selected &= ~CDEV_OP_RD; 190433d6423SLionel Sambuc } 191433d6423SLionel Sambuc 192818c405aSDavid van Moolenbroek return result; 193433d6423SLionel Sambuc } 194433d6423SLionel Sambuc 195433d6423SLionel Sambuc /*===========================================================================* 196433d6423SLionel Sambuc * log_append * 197433d6423SLionel Sambuc *===========================================================================*/ 198433d6423SLionel Sambuc void 199433d6423SLionel Sambuc log_append(char *buf, int count) 200433d6423SLionel Sambuc { 201433d6423SLionel Sambuc int skip = 0; 202433d6423SLionel Sambuc 203433d6423SLionel Sambuc if(count < 1) return; 204433d6423SLionel Sambuc if(count > LOG_SIZE) skip = count - LOG_SIZE; 205433d6423SLionel Sambuc count -= skip; 206433d6423SLionel Sambuc buf += skip; 207433d6423SLionel Sambuc 208433d6423SLionel Sambuc subwrite(&logdevices[0], count, SELF, GRANT_INVALID, buf); 209433d6423SLionel Sambuc } 210433d6423SLionel Sambuc 211433d6423SLionel Sambuc /*===========================================================================* 212433d6423SLionel Sambuc * subread * 213433d6423SLionel Sambuc *===========================================================================*/ 214433d6423SLionel Sambuc static int 215433d6423SLionel Sambuc subread(struct logdevice *log, size_t size, endpoint_t endpt, 216433d6423SLionel Sambuc cp_grant_id_t grant) 217433d6423SLionel Sambuc { 218433d6423SLionel Sambuc size_t offset, count; 219433d6423SLionel Sambuc char *buf; 220433d6423SLionel Sambuc int r; 221433d6423SLionel Sambuc 222433d6423SLionel Sambuc for (offset = 0; log->log_size > 0 && offset < size; offset += count) { 223433d6423SLionel Sambuc count = size - offset; 224433d6423SLionel Sambuc 225433d6423SLionel Sambuc if (count > log->log_size) 226433d6423SLionel Sambuc count = log->log_size; 227433d6423SLionel Sambuc if (log->log_read + count > LOG_SIZE) 228433d6423SLionel Sambuc count = LOG_SIZE - log->log_read; 229433d6423SLionel Sambuc 230433d6423SLionel Sambuc buf = log->log_buffer + log->log_read; 231433d6423SLionel Sambuc if((r=sys_safecopyto(endpt, grant, offset, (vir_bytes)buf, 232433d6423SLionel Sambuc count)) != OK) 233433d6423SLionel Sambuc return r; 234433d6423SLionel Sambuc 235433d6423SLionel Sambuc LOGINC(log->log_read, count); 236433d6423SLionel Sambuc log->log_size -= count; 237433d6423SLionel Sambuc } 238433d6423SLionel Sambuc 239433d6423SLionel Sambuc return offset; 240433d6423SLionel Sambuc } 241433d6423SLionel Sambuc 242433d6423SLionel Sambuc /*===========================================================================* 243433d6423SLionel Sambuc * log_read * 244433d6423SLionel Sambuc *===========================================================================*/ 245433d6423SLionel Sambuc static ssize_t log_read(devminor_t minor, u64_t UNUSED(position), 246433d6423SLionel Sambuc endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags, 247433d6423SLionel Sambuc cdev_id_t id) 248433d6423SLionel Sambuc { 249433d6423SLionel Sambuc /* Read from one of the driver's minor devices. */ 250433d6423SLionel Sambuc struct logdevice *log; 251433d6423SLionel Sambuc int r; 252433d6423SLionel Sambuc 253433d6423SLionel Sambuc if (minor < 0 || minor >= NR_DEVS) return EIO; 254433d6423SLionel Sambuc log = &logdevices[minor]; 255433d6423SLionel Sambuc 256433d6423SLionel Sambuc /* If there's already someone hanging to read, don't accept new work. */ 257433d6423SLionel Sambuc if (log->log_source != NONE) return OK; 258433d6423SLionel Sambuc 259433d6423SLionel Sambuc if (!log->log_size && size > 0) { 260433d6423SLionel Sambuc if (flags & CDEV_NONBLOCK) return EAGAIN; 261433d6423SLionel Sambuc 262433d6423SLionel Sambuc /* No data available; let caller block. */ 263433d6423SLionel Sambuc log->log_source = endpt; 264433d6423SLionel Sambuc log->log_iosize = size; 265433d6423SLionel Sambuc log->log_grant = grant; 266433d6423SLionel Sambuc log->log_id = id; 267433d6423SLionel Sambuc #if LOG_DEBUG 268433d6423SLionel Sambuc printf("blocked %d (%d)\n", log->log_source, id); 269433d6423SLionel Sambuc #endif 270433d6423SLionel Sambuc return EDONTREPLY; 271433d6423SLionel Sambuc } 272433d6423SLionel Sambuc 273433d6423SLionel Sambuc return subread(log, size, endpt, grant); 274433d6423SLionel Sambuc } 275433d6423SLionel Sambuc 276433d6423SLionel Sambuc /*===========================================================================* 277433d6423SLionel Sambuc * log_write * 278433d6423SLionel Sambuc *===========================================================================*/ 279433d6423SLionel Sambuc static ssize_t log_write(devminor_t minor, u64_t UNUSED(position), 280433d6423SLionel Sambuc endpoint_t endpt, cp_grant_id_t grant, size_t size, int UNUSED(flags), 281433d6423SLionel Sambuc cdev_id_t UNUSED(id)) 282433d6423SLionel Sambuc { 283433d6423SLionel Sambuc /* Write to one of the driver's minor devices. */ 284433d6423SLionel Sambuc struct logdevice *log; 285433d6423SLionel Sambuc int r; 286433d6423SLionel Sambuc 287433d6423SLionel Sambuc if (minor < 0 || minor >= NR_DEVS) return EIO; 288433d6423SLionel Sambuc log = &logdevices[minor]; 289433d6423SLionel Sambuc 290433d6423SLionel Sambuc return subwrite(log, size, endpt, grant, NULL); 291433d6423SLionel Sambuc } 292433d6423SLionel Sambuc 293433d6423SLionel Sambuc /*============================================================================* 294433d6423SLionel Sambuc * log_open * 295433d6423SLionel Sambuc *============================================================================*/ 296433d6423SLionel Sambuc static int log_open(devminor_t minor, int UNUSED(access), 297433d6423SLionel Sambuc endpoint_t UNUSED(user_endpt)) 298433d6423SLionel Sambuc { 299433d6423SLionel Sambuc if (minor < 0 || minor >= NR_DEVS) return(ENXIO); 300433d6423SLionel Sambuc 301433d6423SLionel Sambuc return(OK); 302433d6423SLionel Sambuc } 303433d6423SLionel Sambuc 304433d6423SLionel Sambuc /*============================================================================* 305433d6423SLionel Sambuc * log_cancel * 306433d6423SLionel Sambuc *============================================================================*/ 307433d6423SLionel Sambuc static int log_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id) 308433d6423SLionel Sambuc { 309433d6423SLionel Sambuc if (minor < 0 || minor >= NR_DEVS) 310433d6423SLionel Sambuc return EINVAL; 311433d6423SLionel Sambuc 312433d6423SLionel Sambuc /* Not for the suspended request? Must be a stale cancel request. Ignore. */ 313433d6423SLionel Sambuc if (logdevices[minor].log_source != endpt || logdevices[minor].log_id != id) 314433d6423SLionel Sambuc return EDONTREPLY; 315433d6423SLionel Sambuc 316433d6423SLionel Sambuc logdevices[minor].log_source = NONE; 317433d6423SLionel Sambuc 318433d6423SLionel Sambuc return EINTR; /* this is the reply to the original, interrupted request */ 319433d6423SLionel Sambuc } 320433d6423SLionel Sambuc 321433d6423SLionel Sambuc /*============================================================================* 322433d6423SLionel Sambuc * log_select * 323433d6423SLionel Sambuc *============================================================================*/ 324433d6423SLionel Sambuc static int log_select(devminor_t minor, unsigned int ops, endpoint_t endpt) 325433d6423SLionel Sambuc { 326433d6423SLionel Sambuc int want_ops, ready_ops = 0; 327433d6423SLionel Sambuc 328433d6423SLionel Sambuc if (minor < 0 || minor >= NR_DEVS) 329433d6423SLionel Sambuc return ENXIO; 330433d6423SLionel Sambuc 331433d6423SLionel Sambuc want_ops = ops & (CDEV_OP_RD | CDEV_OP_WR | CDEV_OP_ERR); 332433d6423SLionel Sambuc 333433d6423SLionel Sambuc /* Read blocks when there is no log. */ 334433d6423SLionel Sambuc if ((want_ops & CDEV_OP_RD) && logdevices[minor].log_size > 0) { 335433d6423SLionel Sambuc #if LOG_DEBUG 336433d6423SLionel Sambuc printf("log can read; size %d\n", logdevices[minor].log_size); 337433d6423SLionel Sambuc #endif 338433d6423SLionel Sambuc ready_ops |= CDEV_OP_RD; 339433d6423SLionel Sambuc } 340433d6423SLionel Sambuc 341433d6423SLionel Sambuc /* Write never blocks. */ 342433d6423SLionel Sambuc if (want_ops & CDEV_OP_WR) ready_ops |= CDEV_OP_WR; 343433d6423SLionel Sambuc 344433d6423SLionel Sambuc /* Enable select calback if not all requested operations were ready to go, 345433d6423SLionel Sambuc * and notify was enabled. 346433d6423SLionel Sambuc */ 347433d6423SLionel Sambuc want_ops &= ~ready_ops; 348433d6423SLionel Sambuc if ((ops & CDEV_NOTIFY) && want_ops) { 349433d6423SLionel Sambuc logdevices[minor].log_selected |= want_ops; 350433d6423SLionel Sambuc logdevices[minor].log_select_proc = endpt; 351433d6423SLionel Sambuc #if LOG_DEBUG 352433d6423SLionel Sambuc printf("log setting selector.\n"); 353433d6423SLionel Sambuc #endif 354433d6423SLionel Sambuc } 355433d6423SLionel Sambuc 356433d6423SLionel Sambuc #if LOG_DEBUG 357433d6423SLionel Sambuc printf("log returning ops %d\n", ready_ops); 358433d6423SLionel Sambuc #endif 359433d6423SLionel Sambuc 360433d6423SLionel Sambuc return(ready_ops); 361433d6423SLionel Sambuc } 362