1 /* daemon.c: kernel part of Vinum daemon */ 2 /*- 3 * Copyright (c) 1997, 1998 4 * Nan Yang Computer Services Limited. All rights reserved. 5 * 6 * This software is distributed under the so-called ``Berkeley 7 * License'': 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by Nan Yang Computer 20 * Services Limited. 21 * 4. Neither the name of the Company nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * This software is provided ``as is'', and any express or implied 26 * warranties, including, but not limited to, the implied warranties of 27 * merchantability and fitness for a particular purpose are disclaimed. 28 * In no event shall the company or contributors be liable for any 29 * direct, indirect, incidental, special, exemplary, or consequential 30 * damages (including, but not limited to, procurement of substitute 31 * goods or services; loss of use, data, or profits; or business 32 * interruption) however caused and on any theory of liability, whether 33 * in contract, strict liability, or tort (including negligence or 34 * otherwise) arising in any way out of the use of this software, even if 35 * advised of the possibility of such damage. 36 * 37 * $Id: vinumdaemon.c,v 1.8 2000/01/03 05:22:03 grog Exp grog $ 38 * $FreeBSD: src/sys/dev/vinum/vinumdaemon.c,v 1.16 2000/01/05 06:03:56 grog Exp $ 39 * $DragonFly: src/sys/dev/raid/vinum/vinumdaemon.c,v 1.3 2003/07/19 21:14:31 dillon Exp $ 40 */ 41 42 #include <dev/vinum/vinumhdr.h> 43 #include <dev/vinum/request.h> 44 45 #ifdef VINUMDEBUG 46 #include <sys/reboot.h> 47 #endif 48 49 /* declarations */ 50 void recover_io(struct request *rq); 51 52 int daemon_options = 0; /* options */ 53 int daemonpid; /* PID of daemon */ 54 struct daemonq *daemonq; /* daemon's work queue */ 55 struct daemonq *dqend; /* and the end of the queue */ 56 57 /* 58 * We normally call Malloc to get a queue element. In interrupt 59 * context, we can't guarantee that we'll get one, since we're not 60 * allowed to wait. If malloc fails, use one of these elements. 61 */ 62 63 #define INTQSIZE 4 64 struct daemonq intq[INTQSIZE]; /* queue elements for interrupt context */ 65 struct daemonq *intqp; /* and pointer in it */ 66 67 void 68 vinum_daemon(void) 69 { 70 int s; 71 struct daemonq *request; 72 73 curproc->p_flag |= P_INMEM | P_SYSTEM; /* we're a system process */ 74 daemon_save_config(); /* start by saving the configuration */ 75 daemonpid = curproc->p_pid; /* mark our territory */ 76 while (1) { 77 tsleep(&vinum_daemon, 0, "vinum", 0); /* wait for something to happen */ 78 79 /* 80 * It's conceivable that, as the result of an 81 * I/O error, we'll be out of action long 82 * enough that another daemon gets started. 83 * That's OK, just give up gracefully. 84 */ 85 if (curproc->p_pid != daemonpid) { /* we've been ousted in our sleep */ 86 if (daemon_options & daemon_verbose) 87 log(LOG_INFO, "vinum: abdicating\n"); 88 return; 89 } 90 while (daemonq != NULL) { /* we have work to do, */ 91 s = splhigh(); /* don't get interrupted here */ 92 request = daemonq; /* get the request */ 93 daemonq = daemonq->next; /* and detach it */ 94 if (daemonq == NULL) /* got to the end, */ 95 dqend = NULL; /* no end any more */ 96 splx(s); 97 98 switch (request->type) { 99 /* 100 * We had an I/O error on a request. Go through the 101 * request and try to salvage it 102 */ 103 case daemonrq_ioerror: 104 if (daemon_options & daemon_verbose) { 105 struct request *rq = request->info.rq; 106 107 log(LOG_WARNING, 108 "vinum: recovering I/O request: %p\n%s dev %d.%d, offset 0x%x, length %ld\n", 109 rq, 110 rq->bp->b_flags & B_READ ? "Read" : "Write", 111 major(rq->bp->b_dev), 112 minor(rq->bp->b_dev), 113 rq->bp->b_blkno, 114 rq->bp->b_bcount); 115 } 116 recover_io(request->info.rq); /* the failed request */ 117 break; 118 119 /* 120 * Write the config to disk. We could end up with 121 * quite a few of these in a row. Only honour the 122 * last one 123 */ 124 case daemonrq_saveconfig: 125 if ((daemonq == NULL) /* no more requests */ 126 ||(daemonq->type != daemonrq_saveconfig)) { /* or the next isn't the same */ 127 if (((daemon_options & daemon_noupdate) == 0) /* we're allowed to do it */ 128 &&((vinum_conf.flags & VF_READING_CONFIG) == 0)) { /* and we're not building the config now */ 129 /* 130 * We obviously don't want to save a 131 * partial configuration. Less obviously, 132 * we don't need to do anything if we're 133 * asked to write the config when we're 134 * building it up, because we save it at 135 * the end. 136 */ 137 if (daemon_options & daemon_verbose) 138 log(LOG_INFO, "vinum: saving config\n"); 139 daemon_save_config(); /* save it */ 140 } 141 } 142 break; 143 144 case daemonrq_return: /* been told to stop */ 145 if (daemon_options & daemon_verbose) 146 log(LOG_INFO, "vinum: stopping\n"); 147 daemon_options |= daemon_stopped; /* note that we've stopped */ 148 Free(request); 149 while (daemonq != NULL) { /* backed up requests, */ 150 request = daemonq; /* get the request */ 151 daemonq = daemonq->next; /* and detach it */ 152 Free(request); /* then free it */ 153 } 154 wakeup(&vinumclose); /* and wake any waiting vinum(8)s */ 155 return; 156 157 case daemonrq_ping: /* tell the caller we're here */ 158 if (daemon_options & daemon_verbose) 159 log(LOG_INFO, "vinum: ping reply\n"); 160 wakeup(&vinum_finddaemon); /* wake up the caller */ 161 break; 162 163 case daemonrq_closedrive: /* close a drive */ 164 close_drive(request->info.drive); /* do it */ 165 break; 166 167 case daemonrq_init: /* initialize a plex */ 168 /* XXX */ 169 case daemonrq_revive: /* revive a subdisk */ 170 /* XXX */ 171 /* FALLTHROUGH */ 172 default: 173 log(LOG_WARNING, "Invalid request\n"); 174 break; 175 } 176 if (request->privateinuse) /* one of ours, */ 177 request->privateinuse = 0; /* no longer in use */ 178 else 179 Free(request); /* return it */ 180 } 181 } 182 } 183 184 /* 185 * Recover a failed I/O operation. 186 * 187 * The correct way to do this is to examine the request and determine 188 * how to recover each individual failure. In the case of a write, 189 * this could be as simple as doing nothing: the defective drives may 190 * already be down, and there may be nothing else to do. In case of 191 * a read, it will be necessary to retry if there are alternative 192 * copies of the data. 193 * 194 * The easy way (here) is just to reissue the request. This will take 195 * a little longer, but nothing like as long as the failure will have 196 * taken. 197 * 198 */ 199 void 200 recover_io(struct request *rq) 201 { 202 /* 203 * This should read: 204 * 205 * vinumstrategy(rq->bp); 206 * 207 * Negotiate with phk to get it fixed. 208 */ 209 BUF_STRATEGY(rq->bp, 0); /* reissue the command */ 210 } 211 212 /* Functions called to interface with the daemon */ 213 214 /* queue a request for the daemon */ 215 void 216 queue_daemon_request(enum daemonrq type, union daemoninfo info) 217 { 218 int s; 219 220 struct daemonq *qelt = (struct daemonq *) Malloc(sizeof(struct daemonq)); 221 222 if (qelt == NULL) { /* malloc failed, we're prepared for that */ 223 /* 224 * Take one of our spares. Give up if it's still in use; the only 225 * message we're likely to get here is a 'drive failed' message, 226 * and that'll come by again if we miss it. 227 */ 228 if (intqp->privateinuse) /* still in use? */ 229 return; /* yes, give up */ 230 qelt = intqp++; 231 if (intqp == &intq[INTQSIZE]) /* got to the end, */ 232 intqp = intq; /* wrap around */ 233 qelt->privateinuse = 1; /* it's ours, and it's in use */ 234 } else 235 qelt->privateinuse = 0; 236 237 qelt->next = NULL; /* end of the chain */ 238 qelt->type = type; 239 qelt->info = info; 240 s = splhigh(); 241 if (daemonq) { /* something queued already */ 242 dqend->next = qelt; 243 dqend = qelt; 244 } else { /* queue is empty, */ 245 daemonq = qelt; /* this is the whole queue */ 246 dqend = qelt; 247 } 248 splx(s); 249 wakeup(&vinum_daemon); /* and give the d�mon a kick */ 250 } 251 252 /* 253 * see if the daemon is running. Return 0 (no error) 254 * if it is, ESRCH otherwise 255 */ 256 int 257 vinum_finddaemon() 258 { 259 int result; 260 261 if (daemonpid != 0) { /* we think we have a daemon, */ 262 queue_daemon_request(daemonrq_ping, (union daemoninfo) NULL); /* queue a ping */ 263 result = tsleep(&vinum_finddaemon, 0, "reap", 2 * hz); 264 if (result == 0) /* yup, the daemon's up and running */ 265 return 0; 266 } 267 /* no daemon, or we couldn't talk to it: start it */ 268 vinum_daemon(); /* start the daemon */ 269 return 0; 270 } 271 272 int 273 vinum_setdaemonopts(int options) 274 { 275 daemon_options = options; 276 return 0; 277 } 278