Commit | Line | Data |
---|---|---|
984263bc MD |
1 | /*- |
2 | * Copyright (c) 1997, 1998 | |
3 | * Nan Yang Computer Services Limited. All rights reserved. | |
4 | * | |
5 | * This software is distributed under the so-called ``Berkeley | |
6 | * License'': | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * 1. Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in the | |
15 | * documentation and/or other materials provided with the distribution. | |
16 | * 3. All advertising materials mentioning features or use of this software | |
17 | * must display the following acknowledgement: | |
18 | * This product includes software developed by Nan Yang Computer | |
19 | * Services Limited. | |
20 | * 4. Neither the name of the Company nor the names of its contributors | |
21 | * may be used to endorse or promote products derived from this software | |
22 | * without specific prior written permission. | |
23 | * | |
24 | * This software is provided ``as is'', and any express or implied | |
25 | * warranties, including, but not limited to, the implied warranties of | |
26 | * merchantability and fitness for a particular purpose are disclaimed. | |
27 | * In no event shall the company or contributors be liable for any | |
28 | * direct, indirect, incidental, special, exemplary, or consequential | |
29 | * damages (including, but not limited to, procurement of substitute | |
30 | * goods or services; loss of use, data, or profits; or business | |
31 | * interruption) however caused and on any theory of liability, whether | |
32 | * in contract, strict liability, or tort (including negligence or | |
33 | * otherwise) arising in any way out of the use of this software, even if | |
34 | * advised of the possibility of such damage. | |
35 | * | |
36 | * $Id: vinumio.c,v 1.30 2000/05/10 23:23:30 grog Exp grog $ | |
37 | * $FreeBSD: src/sys/dev/vinum/vinumio.c,v 1.52.2.6 2002/05/02 08:43:44 grog Exp $ | |
38 | */ | |
39 | ||
1f2de5d4 MD |
40 | #include "vinumhdr.h" |
41 | #include "request.h" | |
984263bc | 42 | #include <vm/vm_zone.h> |
c1a85d27 | 43 | #include <sys/nlookup.h> |
984263bc MD |
44 | |
45 | static char *sappend(char *txt, char *s); | |
46 | static int drivecmp(const void *va, const void *vb); | |
47 | ||
48 | /* | |
49 | * Open the device associated with the drive, and set drive's vp. | |
50 | * Return an error number | |
51 | */ | |
52 | int | |
53 | open_drive(struct drive *drive, struct proc *p, int verbose) | |
54 | { | |
c1a85d27 MD |
55 | struct nlookupdata nd; |
56 | int error; | |
984263bc MD |
57 | |
58 | /* | |
c1a85d27 | 59 | * Fail if already open |
984263bc | 60 | */ |
c1a85d27 MD |
61 | if (drive->flags & VF_OPEN) |
62 | return EBUSY; | |
984263bc | 63 | |
c1a85d27 MD |
64 | if (rootdev) { |
65 | /* | |
66 | * Open via filesystem (future) | |
67 | */ | |
68 | error = nlookup_init(&nd, drive->devicename, UIO_SYSSPACE, NLC_FOLLOW); | |
69 | if (error) | |
70 | return error; | |
71 | error = vn_open(&nd, NULL, FREAD|FWRITE, 0); | |
72 | drive->vp = nd.nl_open_vp; | |
73 | nd.nl_open_vp = NULL; | |
74 | nlookup_done(&nd); | |
75 | } else { | |
76 | /* | |
77 | * Open via synthesized vnode backed by disk device | |
78 | */ | |
79 | error = vn_opendisk(drive->devicename, FREAD|FWRITE, &drive->vp); | |
80 | if (error) | |
81 | return error; | |
82 | } | |
2f4ec5c1 | 83 | |
c1a85d27 MD |
84 | if (error == 0 && drive->vp == NULL) |
85 | error = ENODEV; | |
984263bc | 86 | |
83d36d43 | 87 | /* |
c1a85d27 MD |
88 | * A huge amount of pollution all over vinum requires that our low |
89 | * level drive be a device. | |
83d36d43 | 90 | */ |
c1a85d27 | 91 | if (error == 0 && drive->vp->v_type != VCHR) { |
3596743e | 92 | vn_close(drive->vp, FREAD|FWRITE, NULL); |
c1a85d27 MD |
93 | drive->vp = NULL; |
94 | error = ENODEV; | |
95 | } | |
96 | if (error) { | |
97 | drive->state = drive_down; | |
98 | if (verbose) { | |
984263bc MD |
99 | log(LOG_WARNING, |
100 | "vinum open_drive %s: failed with error %d\n", | |
c1a85d27 MD |
101 | drive->devicename, error); |
102 | } | |
103 | } else { | |
104 | drive->dev = drive->vp->v_rdev; | |
105 | drive->flags |= VF_OPEN; | |
106 | } | |
107 | drive->lasterror = error; | |
108 | return error; | |
984263bc MD |
109 | } |
110 | ||
111 | /* | |
112 | * Set some variables in the drive struct | |
113 | * in more convenient form. Return error indication | |
114 | */ | |
115 | int | |
116 | set_drive_parms(struct drive *drive) | |
117 | { | |
118 | drive->blocksize = BLKDEV_IOSIZE; /* do we need this? */ | |
119 | drive->secsperblock = drive->blocksize /* number of sectors per block */ | |
2ec8fb79 | 120 | / drive->partinfo.media_blksize; |
984263bc MD |
121 | |
122 | /* Now update the label part */ | |
123 | bcopy(hostname, drive->label.sysname, VINUMHOSTNAMELEN); /* put in host name */ | |
124 | getmicrotime(&drive->label.date_of_birth); /* and current time */ | |
2ec8fb79 | 125 | drive->label.drive_size = drive->partinfo.media_size; |
0dcae153 | 126 | #ifdef VINUMDEBUG |
984263bc MD |
127 | if (debug & DEBUG_BIGDRIVE) /* pretend we're 100 times as big */ |
128 | drive->label.drive_size *= 100; | |
129 | #endif | |
130 | ||
131 | /* number of sectors available for subdisks */ | |
132 | drive->sectors_available = drive->label.drive_size / DEV_BSIZE - DATASTART; | |
133 | ||
134 | /* | |
135 | * Bug in 3.0 as of January 1998: you can open | |
136 | * non-existent slices. They have a length of 0. | |
137 | */ | |
138 | if (drive->label.drive_size < MINVINUMSLICE) { /* too small to worry about */ | |
139 | set_drive_state(drive->driveno, drive_down, setstate_force); | |
140 | drive->lasterror = ENOSPC; | |
141 | return ENOSPC; | |
142 | } | |
143 | drive->freelist_size = INITIAL_DRIVE_FREELIST; /* initial number of entries */ | |
144 | drive->freelist = (struct drive_freelist *) | |
145 | Malloc(INITIAL_DRIVE_FREELIST * sizeof(struct drive_freelist)); | |
146 | if (drive->freelist == NULL) /* can't malloc, dammit */ | |
147 | return ENOSPC; | |
148 | drive->freelist_entries = 1; /* just (almost) the complete drive */ | |
149 | drive->freelist[0].offset = DATASTART; /* starts here */ | |
150 | drive->freelist[0].sectors = (drive->label.drive_size >> DEV_BSHIFT) - DATASTART; /* and it's this long */ | |
151 | if (drive->label.name[0] != '\0') /* got a name */ | |
152 | set_drive_state(drive->driveno, drive_up, setstate_force); /* our drive is accessible */ | |
153 | else /* we know about it, but that's all */ | |
154 | drive->state = drive_referenced; | |
155 | return 0; | |
156 | } | |
157 | ||
158 | /* | |
159 | * Initialize a drive: open the device and add device | |
160 | * information | |
161 | */ | |
162 | int | |
163 | init_drive(struct drive *drive, int verbose) | |
164 | { | |
165 | if (drive->devicename[0] != '/') { | |
166 | drive->lasterror = EINVAL; | |
5ad175c9 MD |
167 | log(LOG_ERR, "vinum: Can't open drive without drive name (%s)\n", |
168 | drive->devicename); | |
984263bc MD |
169 | return EINVAL; |
170 | } | |
171 | drive->lasterror = open_drive(drive, curproc, verbose); /* open the drive */ | |
172 | if (drive->lasterror) | |
173 | return drive->lasterror; | |
174 | ||
c1a85d27 | 175 | drive->lasterror = VOP_IOCTL(drive->vp, DIOCGPART, |
87baaf0c MD |
176 | (caddr_t)&drive->partinfo, FREAD|FWRITE, |
177 | proc0.p_ucred, NULL); | |
984263bc MD |
178 | if (drive->lasterror) { |
179 | if (verbose) | |
180 | log(LOG_WARNING, | |
181 | "vinum open_drive %s: Can't get partition information, drive->lasterror %d\n", | |
182 | drive->devicename, | |
183 | drive->lasterror); | |
184 | close_drive(drive); | |
185 | return drive->lasterror; | |
186 | } | |
2ec8fb79 | 187 | if (drive->partinfo.fstype != FS_VINUM && |
18cb7add | 188 | !kuuid_is_vinum(&drive->partinfo.fstype_uuid) |
2ec8fb79 | 189 | ) { |
984263bc MD |
190 | drive->lasterror = EFTYPE; |
191 | if (verbose) | |
192 | log(LOG_WARNING, | |
193 | "vinum open_drive %s: Wrong partition type for vinum\n", | |
194 | drive->devicename); | |
195 | close_drive(drive); | |
196 | return EFTYPE; | |
197 | } | |
198 | return set_drive_parms(drive); /* set various odds and ends */ | |
199 | } | |
200 | ||
201 | /* Close a drive if it's open. */ | |
202 | void | |
203 | close_drive(struct drive *drive) | |
204 | { | |
205 | LOCKDRIVE(drive); /* keep the daemon out */ | |
206 | if (drive->flags & VF_OPEN) | |
207 | close_locked_drive(drive); /* and close it */ | |
208 | if (drive->state > drive_down) /* if it's up */ | |
209 | drive->state = drive_down; /* make sure it's down */ | |
210 | unlockdrive(drive); | |
211 | } | |
212 | ||
213 | /* | |
214 | * Real drive close code, called with drive already locked. | |
215 | * We have also checked that the drive is open. No errors. | |
216 | */ | |
217 | void | |
218 | close_locked_drive(struct drive *drive) | |
219 | { | |
220 | /* | |
221 | * If we can't access the drive, we can't flush | |
222 | * the queues, which spec_close() will try to | |
223 | * do. Get rid of them here first. | |
224 | */ | |
c1a85d27 | 225 | if (drive->vp) { |
3596743e | 226 | drive->lasterror = vn_close(drive->vp, FREAD|FWRITE, NULL); |
c1a85d27 MD |
227 | drive->vp = NULL; |
228 | } | |
229 | drive->flags &= ~VF_OPEN; | |
984263bc MD |
230 | } |
231 | ||
232 | /* | |
233 | * Remove drive from the configuration. | |
234 | * Caller must ensure that it isn't active. | |
235 | */ | |
236 | void | |
237 | remove_drive(int driveno) | |
238 | { | |
239 | struct drive *drive = &vinum_conf.drive[driveno]; | |
240 | struct vinum_hdr *vhdr; /* buffer for header */ | |
241 | int error; | |
242 | ||
243 | if (drive->state > drive_referenced) { /* real drive */ | |
244 | if (drive->state == drive_up) { | |
245 | vhdr = (struct vinum_hdr *) Malloc(VINUMHEADERLEN); /* allocate buffer */ | |
246 | CHECKALLOC(vhdr, "Can't allocate memory"); | |
247 | error = read_drive(drive, (void *) vhdr, VINUMHEADERLEN, VINUM_LABEL_OFFSET); | |
248 | if (error) | |
249 | drive->lasterror = error; | |
250 | else { | |
251 | vhdr->magic = VINUM_NOMAGIC; /* obliterate the magic, but leave the rest */ | |
252 | write_drive(drive, (void *) vhdr, VINUMHEADERLEN, VINUM_LABEL_OFFSET); | |
253 | } | |
254 | Free(vhdr); | |
255 | } | |
256 | free_drive(drive); /* close it and free resources */ | |
257 | save_config(); /* and save the updated configuration */ | |
258 | } | |
259 | } | |
260 | ||
261 | /* | |
262 | * Transfer drive data. Usually called from one of these defines; | |
10f3fee5 MD |
263 | * #define read_drive(a, b, c, d) driveio (a, b, c, d, BUF_CMD_READ) |
264 | * #define write_drive(a, b, c, d) driveio (a, b, c, d, BUF_CMD_WRITE) | |
984263bc MD |
265 | * |
266 | * length and offset are in bytes, but must be multiples of sector | |
267 | * size. The function *does not check* for this condition, and | |
268 | * truncates ruthlessly. | |
269 | * Return error number | |
270 | */ | |
271 | int | |
10f3fee5 | 272 | driveio(struct drive *drive, char *buf, size_t length, off_t offset, buf_cmd_t cmd) |
984263bc MD |
273 | { |
274 | int error; | |
275 | struct buf *bp; | |
3591bbc6 | 276 | caddr_t saveaddr; |
984263bc MD |
277 | |
278 | error = 0; /* to keep the compiler happy */ | |
279 | while (length) { /* divide into small enough blocks */ | |
0d0607d0 | 280 | int len = umin(length, MAXBSIZE); /* maximum block device transfer is MAXBSIZE */ |
984263bc | 281 | |
bf20632c MD |
282 | bp = getpbuf_kva(NULL); /* get a buffer header */ |
283 | KKASSERT(len <= bp->b_bufsize); | |
10f3fee5 | 284 | bp->b_cmd = cmd; |
54078292 | 285 | bp->b_bio1.bio_offset = offset; /* disk offset */ |
ae8e83e6 MD |
286 | bp->b_bio1.bio_done = biodone_sync; |
287 | bp->b_bio1.bio_flags |= BIO_SYNC; | |
3591bbc6 | 288 | saveaddr = bp->b_data; |
984263bc MD |
289 | bp->b_data = buf; |
290 | bp->b_bcount = len; | |
c1a85d27 | 291 | vn_strategy(drive->vp, &bp->b_bio1); |
ae8e83e6 | 292 | error = biowait(&bp->b_bio1, (cmd == BUF_CMD_READ ? "drvrd" : "drvwr")); |
3591bbc6 | 293 | bp->b_data = saveaddr; |
984263bc MD |
294 | bp->b_flags |= B_INVAL | B_AGE; |
295 | bp->b_flags &= ~B_ERROR; | |
bf20632c | 296 | relpbuf(bp, NULL); |
984263bc MD |
297 | if (error) |
298 | break; | |
299 | length -= len; /* update pointers */ | |
300 | buf += len; | |
301 | offset += len; | |
302 | } | |
303 | return error; | |
304 | } | |
305 | ||
306 | /* | |
307 | * Check a drive for a vinum header. If found, | |
308 | * update the drive information. We come here | |
309 | * with a partially populated drive structure | |
310 | * which includes the device name. | |
311 | * | |
312 | * Return information on what we found. | |
313 | * | |
314 | * This function is called from two places: check_drive, | |
315 | * which wants to find out whether the drive is a | |
316 | * Vinum drive, and config_drive, which asserts that | |
317 | * it is a vinum drive. In the first case, we don't | |
318 | * print error messages (verbose==0), in the second | |
319 | * we do (verbose==1). | |
320 | */ | |
321 | enum drive_label_info | |
322 | read_drive_label(struct drive *drive, int verbose) | |
323 | { | |
324 | int error; | |
5ad175c9 MD |
325 | int result; |
326 | struct vinum_hdr *vhdr; | |
984263bc | 327 | |
d4845858 | 328 | error = init_drive(drive, verbose); /* find the drive */ |
984263bc MD |
329 | if (error) /* find the drive */ |
330 | return DL_CANT_OPEN; /* not ours */ | |
331 | ||
332 | vhdr = (struct vinum_hdr *) Malloc(VINUMHEADERLEN); /* allocate buffers */ | |
333 | CHECKALLOC(vhdr, "Can't allocate memory"); | |
334 | ||
335 | drive->state = drive_up; /* be optimistic */ | |
336 | error = read_drive(drive, (void *) vhdr, VINUMHEADERLEN, VINUM_LABEL_OFFSET); | |
337 | if (vhdr->magic == VINUM_MAGIC) { /* ours! */ | |
338 | if (drive->label.name[0] /* we have a name for this drive */ | |
339 | &&(strcmp(drive->label.name, vhdr->label.name))) { /* but it doesn't match the real name */ | |
340 | drive->lasterror = EINVAL; | |
341 | result = DL_WRONG_DRIVE; /* it's the wrong drive */ | |
342 | drive->state = drive_unallocated; /* put it back, it's not ours */ | |
343 | } else | |
344 | result = DL_OURS; | |
345 | /* | |
346 | * We copy the drive anyway so that we have | |
347 | * the correct name in the drive info. This | |
348 | * may not be the name specified | |
349 | */ | |
350 | drive->label = vhdr->label; /* put in the label information */ | |
351 | } else if (vhdr->magic == VINUM_NOMAGIC) /* was ours, but we gave it away */ | |
352 | result = DL_DELETED_LABEL; /* and return the info */ | |
353 | else | |
354 | result = DL_NOT_OURS; /* we could have it, but we don't yet */ | |
355 | Free(vhdr); /* that's all. */ | |
356 | return result; | |
357 | } | |
358 | ||
359 | /* | |
360 | * Check a drive for a vinum header. If found, | |
361 | * read configuration information from the drive and | |
362 | * incorporate the data into the configuration. | |
363 | * | |
364 | * Return drive number. | |
365 | */ | |
366 | struct drive * | |
367 | check_drive(char *devicename) | |
368 | { | |
369 | int driveno; | |
370 | int i; | |
371 | struct drive *drive; | |
372 | ||
373 | driveno = find_drive_by_dev(devicename, 1); /* if entry doesn't exist, create it */ | |
374 | drive = &vinum_conf.drive[driveno]; /* and get a pointer */ | |
375 | ||
376 | if (read_drive_label(drive, 0) == DL_OURS) { /* one of ours */ | |
377 | for (i = 0; i < vinum_conf.drives_allocated; i++) { /* see if the name already exists */ | |
378 | if ((i != driveno) /* not this drive */ | |
379 | &&(DRIVE[i].state != drive_unallocated) /* and it's allocated */ | |
380 | &&(strcmp(DRIVE[i].label.name, | |
381 | DRIVE[driveno].label.name) == 0)) { /* and it has the same name */ | |
382 | struct drive *mydrive = &DRIVE[i]; | |
383 | ||
384 | if (mydrive->devicename[0] == '/') { /* we know a device name for it */ | |
385 | /* | |
386 | * set an error, but don't take the | |
387 | * drive down: that would cause unneeded | |
388 | * error messages. | |
389 | */ | |
390 | drive->lasterror = EEXIST; | |
391 | break; | |
392 | } else { /* it's just a place holder, */ | |
393 | int sdno; | |
394 | ||
395 | for (sdno = 0; sdno < vinum_conf.subdisks_allocated; sdno++) { /* look at each subdisk */ | |
396 | if ((SD[sdno].driveno == i) /* it's pointing to this one, */ | |
397 | &&(SD[sdno].state != sd_unallocated)) { /* and it's a real subdisk */ | |
398 | SD[sdno].driveno = drive->driveno; /* point to the one we found */ | |
399 | update_sd_state(sdno); /* and update its state */ | |
400 | } | |
401 | } | |
402 | bzero(mydrive, sizeof(struct drive)); /* don't deallocate it, just remove it */ | |
403 | } | |
404 | } | |
405 | } | |
406 | } else { | |
407 | if (drive->lasterror == 0) | |
408 | drive->lasterror = ENODEV; | |
409 | close_drive(drive); | |
410 | drive->state = drive_down; | |
411 | } | |
412 | return drive; | |
413 | } | |
414 | ||
415 | static char * | |
416 | sappend(char *txt, char *s) | |
417 | { | |
418 | while ((*s++ = *txt++) != 0); | |
419 | return s - 1; | |
420 | } | |
421 | ||
422 | void | |
423 | format_config(char *config, int len) | |
424 | { | |
425 | int i; | |
426 | int j; | |
427 | char *s = config; | |
428 | char *configend = &config[len]; | |
429 | ||
430 | bzero(config, len); | |
431 | ||
432 | /* First write the volume configuration */ | |
433 | for (i = 0; i < vinum_conf.volumes_allocated; i++) { | |
434 | struct volume *vol; | |
435 | ||
436 | vol = &vinum_conf.volume[i]; | |
437 | if ((vol->state > volume_uninit) | |
438 | && (vol->name[0] != '\0')) { /* paranoia */ | |
f8c7a42d | 439 | ksnprintf(s, |
984263bc MD |
440 | configend - s, |
441 | "volume %s state %s", | |
442 | vol->name, | |
443 | volume_state(vol->state)); | |
444 | while (*s) | |
445 | s++; /* find the end */ | |
446 | if (vol->preferred_plex >= 0) /* preferences, */ | |
f8c7a42d | 447 | ksnprintf(s, |
984263bc MD |
448 | configend - s, |
449 | " readpol prefer %s", | |
450 | vinum_conf.plex[vol->preferred_plex].name); | |
451 | while (*s) | |
452 | s++; /* find the end */ | |
453 | s = sappend("\n", s); | |
454 | } | |
455 | } | |
456 | ||
457 | /* Then the plex configuration */ | |
458 | for (i = 0; i < vinum_conf.plexes_allocated; i++) { | |
459 | struct plex *plex; | |
460 | ||
461 | plex = &vinum_conf.plex[i]; | |
462 | if ((plex->state > plex_referenced) | |
463 | && (plex->name[0] != '\0')) { /* paranoia */ | |
f8c7a42d | 464 | ksnprintf(s, |
984263bc MD |
465 | configend - s, |
466 | "plex name %s state %s org %s ", | |
467 | plex->name, | |
468 | plex_state(plex->state), | |
469 | plex_org(plex->organization)); | |
470 | while (*s) | |
471 | s++; /* find the end */ | |
472 | if (isstriped(plex)) { | |
f8c7a42d | 473 | ksnprintf(s, |
984263bc MD |
474 | configend - s, |
475 | "%ds ", | |
476 | (int) plex->stripesize); | |
477 | while (*s) | |
478 | s++; /* find the end */ | |
479 | } | |
480 | if (plex->volno >= 0) /* we have a volume */ | |
f8c7a42d | 481 | ksnprintf(s, |
984263bc MD |
482 | configend - s, |
483 | "vol %s ", | |
484 | vinum_conf.volume[plex->volno].name); | |
485 | while (*s) | |
486 | s++; /* find the end */ | |
487 | for (j = 0; j < plex->subdisks; j++) { | |
f8c7a42d | 488 | ksnprintf(s, |
984263bc MD |
489 | configend - s, |
490 | " sd %s", | |
491 | vinum_conf.sd[plex->sdnos[j]].name); | |
492 | } | |
493 | s = sappend("\n", s); | |
494 | } | |
495 | } | |
496 | ||
497 | /* And finally the subdisk configuration */ | |
498 | for (i = 0; i < vinum_conf.subdisks_allocated; i++) { | |
499 | struct sd *sd; | |
500 | char *drivename; | |
501 | ||
502 | sd = &SD[i]; | |
503 | if ((sd->state != sd_referenced) | |
504 | && (sd->state != sd_unallocated) | |
505 | && (sd->name[0] != '\0')) { /* paranoia */ | |
506 | drivename = vinum_conf.drive[sd->driveno].label.name; | |
507 | /* | |
508 | * XXX We've seen cases of dead subdisks | |
509 | * which don't have a drive. If we let them | |
510 | * through here, the drive name is null, so | |
511 | * they get the drive named 'plex'. | |
512 | * | |
513 | * This is a breakage limiter, not a fix. | |
514 | */ | |
515 | if (drivename[0] == '\0') | |
516 | drivename = "*invalid*"; | |
f8c7a42d | 517 | ksnprintf(s, |
984263bc MD |
518 | configend - s, |
519 | "sd name %s drive %s plex %s len %llus driveoffset %llus state %s", | |
520 | sd->name, | |
521 | drivename, | |
522 | vinum_conf.plex[sd->plexno].name, | |
523 | (unsigned long long) sd->sectors, | |
524 | (unsigned long long) sd->driveoffset, | |
525 | sd_state(sd->state)); | |
526 | while (*s) | |
527 | s++; /* find the end */ | |
528 | if (sd->plexno >= 0) | |
f8c7a42d | 529 | ksnprintf(s, |
984263bc MD |
530 | configend - s, |
531 | " plexoffset %llds", | |
532 | (long long) sd->plexoffset); | |
533 | else | |
f8c7a42d | 534 | ksnprintf(s, configend - s, " detached"); |
984263bc MD |
535 | while (*s) |
536 | s++; /* find the end */ | |
537 | if (sd->flags & VF_RETRYERRORS) { | |
f8c7a42d | 538 | ksnprintf(s, configend - s, " retryerrors"); |
984263bc MD |
539 | while (*s) |
540 | s++; /* find the end */ | |
541 | } | |
f8c7a42d | 542 | ksnprintf(s, configend - s, " \n"); |
984263bc MD |
543 | while (*s) |
544 | s++; /* find the end */ | |
545 | } | |
546 | } | |
547 | if (s > &config[len - 2]) | |
548 | panic("vinum: configuration data overflow"); | |
549 | } | |
550 | ||
551 | /* | |
5c0a8865 | 552 | * issue a save config request to the daemon. The actual work |
984263bc MD |
553 | * is done in process context by daemon_save_config |
554 | */ | |
555 | void | |
556 | save_config(void) | |
557 | { | |
fdd5d60b SW |
558 | union daemoninfo di = { .nothing = 0 }; |
559 | ||
560 | queue_daemon_request(daemonrq_saveconfig, di); | |
984263bc MD |
561 | } |
562 | ||
563 | /* | |
564 | * Write the configuration to all vinum slices. This | |
5c0a8865 | 565 | * is performed by the daemon only |
984263bc MD |
566 | */ |
567 | void | |
568 | daemon_save_config(void) | |
569 | { | |
570 | int error; | |
984263bc MD |
571 | int driveno; |
572 | struct drive *drive; /* point to current drive info */ | |
573 | struct vinum_hdr *vhdr; /* and as header */ | |
574 | char *config; /* point to config data */ | |
575 | int wlabel_on; /* to set writing label on/off */ | |
576 | ||
577 | /* don't save the configuration while we're still working on it */ | |
578 | if (vinum_conf.flags & VF_CONFIGURING) | |
579 | return; | |
984263bc MD |
580 | /* Build a volume header */ |
581 | vhdr = (struct vinum_hdr *) Malloc(VINUMHEADERLEN); /* get space for the config data */ | |
582 | CHECKALLOC(vhdr, "Can't allocate config data"); | |
583 | vhdr->magic = VINUM_MAGIC; /* magic number */ | |
584 | vhdr->config_length = MAXCONFIG; /* length of following config info */ | |
585 | ||
586 | config = Malloc(MAXCONFIG); /* get space for the config data */ | |
587 | CHECKALLOC(config, "Can't allocate config data"); | |
588 | ||
589 | format_config(config, MAXCONFIG); | |
590 | error = 0; /* no errors yet */ | |
591 | for (driveno = 0; driveno < vinum_conf.drives_allocated; driveno++) { | |
592 | drive = &vinum_conf.drive[driveno]; /* point to drive */ | |
593 | if (drive->state > drive_referenced) { | |
594 | LOCKDRIVE(drive); /* don't let it change */ | |
595 | ||
596 | /* | |
597 | * First, do some drive consistency checks. Some | |
598 | * of these are kludges, others require a process | |
599 | * context and couldn't be done before | |
600 | */ | |
601 | if ((drive->devicename[0] == '\0') | |
602 | || (drive->label.name[0] == '\0')) { | |
603 | unlockdrive(drive); | |
604 | free_drive(drive); /* get rid of it */ | |
605 | break; | |
606 | } | |
607 | if (((drive->flags & VF_OPEN) == 0) /* drive not open */ | |
608 | &&(drive->state > drive_down)) { /* and it thinks it's not down */ | |
609 | unlockdrive(drive); | |
610 | set_drive_state(driveno, drive_down, setstate_force); /* tell it what's what */ | |
611 | continue; | |
612 | } | |
613 | if ((drive->state == drive_down) /* it's down */ | |
614 | &&(drive->flags & VF_OPEN)) { /* but open, */ | |
615 | unlockdrive(drive); | |
616 | close_drive(drive); /* close it */ | |
617 | } else if (drive->state > drive_down) { | |
618 | getmicrotime(&drive->label.last_update); /* time of last update is now */ | |
619 | bcopy((char *) &drive->label, /* and the label info from the drive structure */ | |
620 | (char *) &vhdr->label, | |
621 | sizeof(vhdr->label)); | |
622 | if ((drive->state != drive_unallocated) | |
623 | && (drive->state != drive_referenced)) { /* and it's a real drive */ | |
624 | wlabel_on = 1; /* enable writing the label */ | |
2f4ec5c1 | 625 | error = 0; |
d1de5ee6 | 626 | #if 1 |
c1a85d27 | 627 | error = VOP_IOCTL(drive->vp, DIOCWLABEL, |
87baaf0c MD |
628 | (caddr_t)&wlabel_on, FREAD|FWRITE, |
629 | proc0.p_ucred, NULL); | |
2f4ec5c1 | 630 | #endif |
984263bc MD |
631 | if (error == 0) |
632 | error = write_drive(drive, (char *) vhdr, VINUMHEADERLEN, VINUM_LABEL_OFFSET); | |
633 | if (error == 0) | |
634 | error = write_drive(drive, config, MAXCONFIG, VINUM_CONFIG_OFFSET); /* first config copy */ | |
635 | if (error == 0) | |
636 | error = write_drive(drive, config, MAXCONFIG, VINUM_CONFIG_OFFSET + MAXCONFIG); /* second copy */ | |
637 | wlabel_on = 0; /* enable writing the label */ | |
d1de5ee6 | 638 | #if 1 |
c1a85d27 MD |
639 | if (error == 0) { |
640 | error = VOP_IOCTL(drive->vp, DIOCWLABEL, | |
87baaf0c MD |
641 | (caddr_t)&wlabel_on, FREAD|FWRITE, |
642 | proc0.p_ucred, NULL); | |
c1a85d27 | 643 | } |
2f4ec5c1 | 644 | #endif |
984263bc MD |
645 | unlockdrive(drive); |
646 | if (error) { | |
647 | log(LOG_ERR, | |
648 | "vinum: Can't write config to %s, error %d\n", | |
649 | drive->devicename, | |
650 | error); | |
651 | set_drive_state(drive->driveno, drive_down, setstate_force); | |
489fe090 | 652 | } |
984263bc MD |
653 | } |
654 | } else /* not worth looking at, */ | |
655 | unlockdrive(drive); /* just unlock it again */ | |
656 | } | |
657 | } | |
658 | Free(vhdr); | |
659 | Free(config); | |
660 | } | |
661 | ||
984263bc MD |
662 | /* Look at all disks on the system for vinum slices */ |
663 | int | |
664 | vinum_scandisk(char *devicename[], int drives) | |
665 | { | |
666 | struct drive *volatile drive; | |
667 | volatile int driveno; | |
984263bc MD |
668 | volatile int gooddrives; /* number of usable drives found */ |
669 | int firsttime; /* set if we have never configured before */ | |
670 | int error; | |
671 | char *config_text; /* read the config info from disk into here */ | |
672 | char *volatile cptr; /* pointer into config information */ | |
673 | char *eptr; /* end pointer into config information */ | |
674 | char *config_line; /* copy the config line to */ | |
675 | volatile int status; | |
676 | int *volatile drivelist; /* list of drive indices */ | |
677 | #define DRIVENAMELEN 64 | |
678 | #define DRIVEPARTS 35 /* max partitions per drive, excluding c */ | |
679 | char partname[DRIVENAMELEN]; /* for creating partition names */ | |
680 | ||
681 | status = 0; /* success indication */ | |
682 | vinum_conf.flags |= VF_READING_CONFIG; /* reading config from disk */ | |
683 | ||
684 | gooddrives = 0; /* number of usable drives found */ | |
984263bc MD |
685 | firsttime = vinum_conf.drives_used == 0; /* are we a virgin? */ |
686 | ||
687 | /* allocate a drive pointer list */ | |
688 | drivelist = (int *) Malloc(drives * DRIVEPARTS * sizeof(int)); | |
689 | CHECKALLOC(drivelist, "Can't allocate memory"); | |
d1df4cee SS |
690 | error = setjmp(command_fail); /* come back here on error */ |
691 | if (error) { /* longjmped out */ | |
d1df4cee SS |
692 | return error; |
693 | } | |
984263bc MD |
694 | |
695 | /* Open all drives and find which was modified most recently */ | |
696 | for (driveno = 0; driveno < drives; driveno++) { | |
ac546af3 | 697 | char part, has_part = 0; /* UNIX partition */ |
984263bc | 698 | int slice; |
d4845858 | 699 | int has_slice = -1; |
83d36d43 | 700 | char *tmp; |
984263bc | 701 | |
83d36d43 MD |
702 | /* |
703 | * If the device path contains a slice we do not try to tack on | |
704 | * another slice. If the device path has a partition we only check | |
705 | * that partition. | |
706 | */ | |
707 | if ((tmp = rindex(devicename[driveno], '/')) == NULL) | |
708 | tmp = devicename[driveno]; | |
d4845858 AH |
709 | else |
710 | tmp++; | |
711 | ksscanf(tmp, "%*[a-z]%*d%*[s]%d%c", &has_slice, &has_part); | |
83d36d43 | 712 | |
8157029b | 713 | for (slice = 0; slice < MAX_SLICES; slice++) { |
d4845858 | 714 | if (has_slice >= 0 && slice != has_slice) |
83d36d43 MD |
715 | continue; |
716 | ||
f0b5dad4 | 717 | for (part = 'a'; part < 'a' + MAXPARTITIONS; part++) { |
83d36d43 MD |
718 | if (part == 'c') |
719 | continue; | |
d4845858 AH |
720 | if (has_part && part != has_part) |
721 | continue; | |
722 | if (has_slice >= 0 && has_part) | |
723 | strncpy(partname, devicename[driveno], DRIVENAMELEN); | |
724 | else if (has_slice >= 0) | |
725 | ksnprintf(partname, DRIVENAMELEN, | |
726 | "%s%c", devicename[driveno], part); | |
727 | else | |
728 | ksnprintf(partname, DRIVENAMELEN, | |
729 | "%ss%d%c", devicename[driveno], slice, part); | |
83d36d43 MD |
730 | drive = check_drive(partname); /* try to open it */ |
731 | if ((drive->lasterror != 0) /* didn't work, */ | |
984263bc | 732 | ||(drive->state != drive_up)) |
83d36d43 MD |
733 | free_drive(drive); /* get rid of it */ |
734 | else if (drive->flags & VF_CONFIGURED) /* already read this config, */ | |
735 | log(LOG_WARNING, | |
736 | "vinum: already read config from %s\n", /* say so */ | |
737 | drive->label.name); | |
738 | else { | |
739 | drivelist[gooddrives] = drive->driveno; /* keep the drive index */ | |
740 | drive->flags &= ~VF_NEWBORN; /* which is no longer newly born */ | |
741 | gooddrives++; | |
984263bc MD |
742 | } |
743 | } | |
83d36d43 | 744 | } |
984263bc MD |
745 | } |
746 | ||
747 | if (gooddrives == 0) { | |
748 | if (firsttime) | |
749 | log(LOG_WARNING, "vinum: no drives found\n"); | |
750 | else | |
751 | log(LOG_INFO, "vinum: no additional drives found\n"); | |
752 | return ENOENT; | |
753 | } | |
754 | /* | |
755 | * We now have at least one drive | |
756 | * open. Sort them in order of config time | |
757 | * and merge the config info with what we | |
758 | * have already. | |
759 | */ | |
e93690c2 | 760 | kqsort(drivelist, gooddrives, sizeof(int), drivecmp); |
984263bc MD |
761 | config_text = (char *) Malloc(MAXCONFIG * 2); /* allocate buffers */ |
762 | CHECKALLOC(config_text, "Can't allocate memory"); | |
763 | config_line = (char *) Malloc(MAXCONFIGLINE * 2); /* allocate buffers */ | |
764 | CHECKALLOC(config_line, "Can't allocate memory"); | |
765 | for (driveno = 0; driveno < gooddrives; driveno++) { /* now include the config */ | |
766 | drive = &DRIVE[drivelist[driveno]]; /* point to the drive */ | |
767 | ||
768 | if (firsttime && (driveno == 0)) /* we've never configured before, */ | |
769 | log(LOG_INFO, "vinum: reading configuration from %s\n", drive->devicename); | |
770 | else | |
771 | log(LOG_INFO, "vinum: updating configuration from %s\n", drive->devicename); | |
772 | ||
773 | if (drive->state == drive_up) | |
774 | /* Read in both copies of the configuration information */ | |
775 | error = read_drive(drive, config_text, MAXCONFIG * 2, VINUM_CONFIG_OFFSET); | |
776 | else { | |
777 | error = EIO; | |
e3869ec7 | 778 | kprintf("vinum_scandisk: %s is %s\n", drive->devicename, drive_state(drive->state)); |
984263bc MD |
779 | } |
780 | ||
781 | if (error != 0) { | |
782 | log(LOG_ERR, "vinum: Can't read device %s, error %d\n", drive->devicename, error); | |
783 | free_drive(drive); /* give it back */ | |
784 | status = error; | |
785 | } | |
786 | /* | |
787 | * At this point, check that the two copies | |
788 | * are the same, and do something useful if | |
789 | * not. In particular, consider which is | |
790 | * newer, and what this means for the | |
791 | * integrity of the data on the drive. | |
792 | */ | |
793 | else { | |
794 | vinum_conf.drives_used++; /* another drive in use */ | |
795 | /* Parse the configuration, and add it to the global configuration */ | |
796 | for (cptr = config_text; *cptr != '\0';) { /* love this style(9) */ | |
797 | volatile int parse_status; /* return value from parse_config */ | |
798 | ||
799 | for (eptr = config_line; (*cptr != '\n') && (*cptr != '\0');) /* until the end of the line */ | |
800 | *eptr++ = *cptr++; | |
801 | *eptr = '\0'; /* and delimit */ | |
802 | if (setjmp(command_fail) == 0) { /* come back here on error and continue */ | |
803 | parse_status = parse_config(config_line, &keyword_set, 1); /* parse the config line */ | |
804 | if (parse_status < 0) { /* error in config */ | |
805 | /* | |
806 | * This config should have been parsed in user | |
807 | * space. If we run into problems here, something | |
808 | * serious is afoot. Complain and let the user | |
809 | * snarf the config to see what's wrong. | |
810 | */ | |
811 | log(LOG_ERR, | |
812 | "vinum: Config error on %s, aborting integration\n", | |
813 | drive->devicename); | |
814 | free_drive(drive); /* give it back */ | |
815 | status = EINVAL; | |
816 | } | |
817 | } | |
818 | while (*cptr == '\n') | |
819 | cptr++; /* skip to next line */ | |
820 | } | |
821 | } | |
822 | drive->flags |= VF_CONFIGURED; /* read this drive's configuration */ | |
823 | } | |
824 | ||
9c29fd6b | 825 | Free(config_line); |
984263bc MD |
826 | Free(config_text); |
827 | Free(drivelist); | |
828 | vinum_conf.flags &= ~VF_READING_CONFIG; /* no longer reading from disk */ | |
829 | if (status != 0) | |
e3869ec7 | 830 | kprintf("vinum: couldn't read configuration"); |
984263bc MD |
831 | else |
832 | updateconfig(VF_READING_CONFIG); /* update from disk config */ | |
833 | return status; | |
834 | } | |
835 | ||
836 | /* | |
837 | * Compare the modification dates of the drives, for qsort. | |
838 | * Return 1 if a < b, 0 if a == b, 01 if a > b: in other | |
839 | * words, sort backwards. | |
840 | */ | |
8406cf70 | 841 | static int |
984263bc MD |
842 | drivecmp(const void *va, const void *vb) |
843 | { | |
844 | const struct drive *a = &DRIVE[*(const int *) va]; | |
845 | const struct drive *b = &DRIVE[*(const int *) vb]; | |
846 | ||
847 | if ((a->label.last_update.tv_sec == b->label.last_update.tv_sec) | |
848 | && (a->label.last_update.tv_usec == b->label.last_update.tv_usec)) | |
849 | return 0; | |
850 | else if ((a->label.last_update.tv_sec > b->label.last_update.tv_sec) | |
851 | || ((a->label.last_update.tv_sec == b->label.last_update.tv_sec) | |
852 | && (a->label.last_update.tv_usec > b->label.last_update.tv_usec))) | |
853 | return -1; | |
854 | else | |
855 | return 1; | |
856 | } |