xref: /dragonfly/sys/dev/raid/vinum/vinumdaemon.c (revision 4a65f651)
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.12 2008/06/05 18:06:31 swildner Exp $
40  */
41 
42 #include "vinumhdr.h"
43 #include "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     struct daemonq *request;
71 
72     curproc->p_flag |= P_SYSTEM;		    /* we're a system process */
73     daemon_save_config();				    /* start by saving the configuration */
74     daemonpid = curproc->p_pid;				    /* mark our territory */
75     while (1) {
76 	tsleep(&vinum_daemon, 0, "vinum", 0);	    /* wait for something to happen */
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, */
90 	    crit_enter();
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 */
95 	    crit_exit();
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,
107 			"vinum: recovering I/O request: %p\n%s dev %d.%d, offset 0x%012llx, length %d\n",
108 			rq,
109 			(rq->bio->bio_buf->b_cmd == BUF_CMD_READ) ? "Read" : "Write",
110 			major((cdev_t)rq->bio->bio_driver_info),
111 			minor((cdev_t)rq->bio->bio_driver_info),
112 			(long long)rq->bio->bio_offset,
113 			rq->bio->bio_buf->b_bcount);
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      *
204      *     vinumstrategy(rq->bio);
205      *
206      * Negotiate with phk to get it fixed.
207      * Reissue the command.
208      */
209     dev_dstrategy((cdev_t)rq->bio->bio_driver_info, rq->bio);
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     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;
238     crit_enter();
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     }
246     crit_exit();
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
255 vinum_finddaemon(void)
256 {
257     int result;
258 
259     if (daemonpid != 0) {				    /* we think we have a daemon, */
260 	queue_daemon_request(daemonrq_ping, (union daemoninfo) 0); /* 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
271 vinum_setdaemonopts(int options)
272 {
273     daemon_options = options;
274     return 0;
275 }
276