| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
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 $ | |
| 3641b7ca | 39 | * $DragonFly: src/sys/dev/raid/vinum/vinumdaemon.c,v 1.12 2008/06/05 18:06:31 swildner Exp $ |
| 984263bc MD |
40 | */ |
| 41 | ||
| 1f2de5d4 MD |
42 | #include "vinumhdr.h" |
| 43 | #include "request.h" | |
| 984263bc MD |
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 | { | |
| 984263bc MD |
70 | struct daemonq *request; |
| 71 | ||
| 4643740a | 72 | curproc->p_flags |= P_SYSTEM; /* we're a system process */ |
| 984263bc MD |
73 | daemon_save_config(); /* start by saving the configuration */ |
| 74 | daemonpid = curproc->p_pid; /* mark our territory */ | |
| 75 | while (1) { | |
| 377d4740 | 76 | tsleep(&vinum_daemon, 0, "vinum", 0); /* wait for something to happen */ |
| 984263bc MD |
77 | |
| 78 | /* | |
| 79 | * It's conceivable that, as the result of an | |
| 80 | * I/O error, we'll be out of action long | |
| 81 | * enough that another daemon gets started. | |
| 82 | * That's OK, just give up gracefully. | |
| 83 | */ | |
| 84 | if (curproc->p_pid != daemonpid) { /* we've been ousted in our sleep */ | |
| 85 | if (daemon_options & daemon_verbose) | |
| 86 | log(LOG_INFO, "vinum: abdicating\n"); | |
| 87 | return; | |
| 88 | } | |
| 89 | while (daemonq != NULL) { /* we have work to do, */ | |
| 407c6ab2 | 90 | crit_enter(); |
| 984263bc MD |
91 | request = daemonq; /* get the request */ |
| 92 | daemonq = daemonq->next; /* and detach it */ | |
| 93 | if (daemonq == NULL) /* got to the end, */ | |
| 94 | dqend = NULL; /* no end any more */ | |
| 407c6ab2 | 95 | crit_exit(); |
| 984263bc MD |
96 | |
| 97 | switch (request->type) { | |
| 98 | /* | |
| 99 | * We had an I/O error on a request. Go through the | |
| 100 | * request and try to salvage it | |
| 101 | */ | |
| 102 | case daemonrq_ioerror: | |
| 103 | if (daemon_options & daemon_verbose) { | |
| 104 | struct request *rq = request->info.rq; | |
| 105 | ||
| 106 | log(LOG_WARNING, | |
| 54078292 | 107 | "vinum: recovering I/O request: %p\n%s dev %d.%d, offset 0x%012llx, length %d\n", |
| 984263bc | 108 | rq, |
| 10f3fee5 | 109 | (rq->bio->bio_buf->b_cmd == BUF_CMD_READ) ? "Read" : "Write", |
| b13267a5 MD |
110 | major((cdev_t)rq->bio->bio_driver_info), |
| 111 | minor((cdev_t)rq->bio->bio_driver_info), | |
| 5bcacc1c | 112 | (long long)rq->bio->bio_offset, |
| 81b5c339 | 113 | rq->bio->bio_buf->b_bcount); |
| 984263bc MD |
114 | } |
| 115 | recover_io(request->info.rq); /* the failed request */ | |
| 116 | break; | |
| 117 | ||
| 118 | /* | |
| 119 | * Write the config to disk. We could end up with | |
| 120 | * quite a few of these in a row. Only honour the | |
| 121 | * last one | |
| 122 | */ | |
| 123 | case daemonrq_saveconfig: | |
| 124 | if ((daemonq == NULL) /* no more requests */ | |
| 125 | ||(daemonq->type != daemonrq_saveconfig)) { /* or the next isn't the same */ | |
| 126 | if (((daemon_options & daemon_noupdate) == 0) /* we're allowed to do it */ | |
| 127 | &&((vinum_conf.flags & VF_READING_CONFIG) == 0)) { /* and we're not building the config now */ | |
| 128 | /* | |
| 129 | * We obviously don't want to save a | |
| 130 | * partial configuration. Less obviously, | |
| 131 | * we don't need to do anything if we're | |
| 132 | * asked to write the config when we're | |
| 133 | * building it up, because we save it at | |
| 134 | * the end. | |
| 135 | */ | |
| 136 | if (daemon_options & daemon_verbose) | |
| 137 | log(LOG_INFO, "vinum: saving config\n"); | |
| 138 | daemon_save_config(); /* save it */ | |
| 139 | } | |
| 140 | } | |
| 141 | break; | |
| 142 | ||
| 143 | case daemonrq_return: /* been told to stop */ | |
| 144 | if (daemon_options & daemon_verbose) | |
| 145 | log(LOG_INFO, "vinum: stopping\n"); | |
| 146 | daemon_options |= daemon_stopped; /* note that we've stopped */ | |
| 147 | Free(request); | |
| 148 | while (daemonq != NULL) { /* backed up requests, */ | |
| 149 | request = daemonq; /* get the request */ | |
| 150 | daemonq = daemonq->next; /* and detach it */ | |
| 151 | Free(request); /* then free it */ | |
| 152 | } | |
| 153 | wakeup(&vinumclose); /* and wake any waiting vinum(8)s */ | |
| 154 | return; | |
| 155 | ||
| 156 | case daemonrq_ping: /* tell the caller we're here */ | |
| 157 | if (daemon_options & daemon_verbose) | |
| 158 | log(LOG_INFO, "vinum: ping reply\n"); | |
| 159 | wakeup(&vinum_finddaemon); /* wake up the caller */ | |
| 160 | break; | |
| 161 | ||
| 162 | case daemonrq_closedrive: /* close a drive */ | |
| 163 | close_drive(request->info.drive); /* do it */ | |
| 164 | break; | |
| 165 | ||
| 166 | case daemonrq_init: /* initialize a plex */ | |
| 167 | /* XXX */ | |
| 168 | case daemonrq_revive: /* revive a subdisk */ | |
| 169 | /* XXX */ | |
| 170 | /* FALLTHROUGH */ | |
| 171 | default: | |
| 172 | log(LOG_WARNING, "Invalid request\n"); | |
| 173 | break; | |
| 174 | } | |
| 175 | if (request->privateinuse) /* one of ours, */ | |
| 176 | request->privateinuse = 0; /* no longer in use */ | |
| 177 | else | |
| 178 | Free(request); /* return it */ | |
| 179 | } | |
| 180 | } | |
| 181 | } | |
| 182 | ||
| 183 | /* | |
| 184 | * Recover a failed I/O operation. | |
| 185 | * | |
| 186 | * The correct way to do this is to examine the request and determine | |
| 187 | * how to recover each individual failure. In the case of a write, | |
| 188 | * this could be as simple as doing nothing: the defective drives may | |
| 189 | * already be down, and there may be nothing else to do. In case of | |
| 190 | * a read, it will be necessary to retry if there are alternative | |
| 191 | * copies of the data. | |
| 192 | * | |
| 193 | * The easy way (here) is just to reissue the request. This will take | |
| 194 | * a little longer, but nothing like as long as the failure will have | |
| 195 | * taken. | |
| 196 | * | |
| 197 | */ | |
| 198 | void | |
| 199 | recover_io(struct request *rq) | |
| 200 | { | |
| 201 | /* | |
| 202 | * This should read: | |
| 203 | * | |
| 81b5c339 | 204 | * vinumstrategy(rq->bio); |
| 984263bc MD |
205 | * |
| 206 | * Negotiate with phk to get it fixed. | |
| 81b5c339 | 207 | * Reissue the command. |
| 984263bc | 208 | */ |
| b13267a5 | 209 | dev_dstrategy((cdev_t)rq->bio->bio_driver_info, rq->bio); |
| 984263bc MD |
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 | { | |
| 984263bc MD |
218 | struct daemonq *qelt = (struct daemonq *) Malloc(sizeof(struct daemonq)); |
| 219 | ||
| 220 | if (qelt == NULL) { /* malloc failed, we're prepared for that */ | |
| 221 | /* | |
| 222 | * Take one of our spares. Give up if it's still in use; the only | |
| 223 | * message we're likely to get here is a 'drive failed' message, | |
| 224 | * and that'll come by again if we miss it. | |
| 225 | */ | |
| 226 | if (intqp->privateinuse) /* still in use? */ | |
| 227 | return; /* yes, give up */ | |
| 228 | qelt = intqp++; | |
| 229 | if (intqp == &intq[INTQSIZE]) /* got to the end, */ | |
| 230 | intqp = intq; /* wrap around */ | |
| 231 | qelt->privateinuse = 1; /* it's ours, and it's in use */ | |
| 232 | } else | |
| 233 | qelt->privateinuse = 0; | |
| 234 | ||
| 235 | qelt->next = NULL; /* end of the chain */ | |
| 236 | qelt->type = type; | |
| 237 | qelt->info = info; | |
| 407c6ab2 | 238 | crit_enter(); |
| 984263bc MD |
239 | if (daemonq) { /* something queued already */ |
| 240 | dqend->next = qelt; | |
| 241 | dqend = qelt; | |
| 242 | } else { /* queue is empty, */ | |
| 243 | daemonq = qelt; /* this is the whole queue */ | |
| 244 | dqend = qelt; | |
| 245 | } | |
| 407c6ab2 | 246 | crit_exit(); |
| 984263bc MD |
247 | wakeup(&vinum_daemon); /* and give the dæmon a kick */ |
| 248 | } | |
| 249 | ||
| 250 | /* | |
| 251 | * see if the daemon is running. Return 0 (no error) | |
| 252 | * if it is, ESRCH otherwise | |
| 253 | */ | |
| 254 | int | |
| c436375a | 255 | vinum_finddaemon(void) |
| 984263bc MD |
256 | { |
| 257 | int result; | |
| 258 | ||
| 259 | if (daemonpid != 0) { /* we think we have a daemon, */ | |
| 3641b7ca | 260 | queue_daemon_request(daemonrq_ping, (union daemoninfo) 0); /* queue a ping */ |
| 377d4740 | 261 | result = tsleep(&vinum_finddaemon, 0, "reap", 2 * hz); |
| 984263bc MD |
262 | if (result == 0) /* yup, the daemon's up and running */ |
| 263 | return 0; | |
| 264 | } | |
| 265 | /* no daemon, or we couldn't talk to it: start it */ | |
| 266 | vinum_daemon(); /* start the daemon */ | |
| 267 | return 0; | |
| 268 | } | |
| 269 | ||
| 270 | int | |
| 271 | vinum_setdaemonopts(int options) | |
| 272 | { | |
| 273 | daemon_options = options; | |
| 274 | return 0; | |
| 275 | } |