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