1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include <assert.h>
30 #include <string.h>
31 #include <libintl.h>
32
33 #include "volume_error.h"
34 #include "volume_defaults.h"
35 #include "volume_dlist.h"
36 #include "volume_output.h"
37 #include "volume_request.h"
38
39 #include "layout.h"
40 #include "layout_request.h"
41
42 #include "layout_concat.h"
43 #include "layout_discovery.h"
44 #include "layout_device_cache.h"
45 #include "layout_device_util.h"
46 #include "layout_dlist_util.h"
47 #include "layout_hsp.h"
48 #include "layout_mirror.h"
49 #include "layout_slice.h"
50 #include "layout_stripe.h"
51 #include "layout_svm_util.h"
52 #include "layout_validate.h"
53
54 #define _LAYOUT_C
55
56 static int layout_init(devconfig_t *request, defaults_t *defaults);
57 static int layout_diskset(request_t *request, dlist_t *results);
58
59 static int process_request(devconfig_t *request, dlist_t **results);
60 static int process_qos_request(devconfig_t *request, dlist_t **results);
61 static int process_hsp_request(devconfig_t *request, dlist_t **results);
62
63 /*
64 * stuff for making/updating the HSP to service devices
65 * created by the toplevel request
66 */
67 static devconfig_t *_hsp_request = NULL;
68 static dlist_t *_hsp_devices = NULL;
69 static void set_hsp_request(devconfig_t *request);
70 static void unset_hsp_request();
71
72 /*
73 * struct to track which disks have been explicitly modified
74 * during the layout process...
75 *
76 * disk is the dm_descriptor_t of the modified disk
77 * accessname is the name to access the disk thru
78 * slices is the list of modified slices on the disk
79 */
80 typedef struct {
81 dm_descriptor_t disk;
82 char *accessname;
83 dlist_t *slices;
84 } moddisk_t;
85
86 /*
87 * modified_disks is a list of moddisk_t structs
88 * tracking disks have been modified during layout.
89 */
90 static dlist_t *_modified_disks = NULL;
91
92 static int collect_modified_disks(devconfig_t *request, dlist_t *results);
93 static int add_modified_disks_to_diskset(
94 dlist_t *devices,
95 devconfig_t *diskset);
96 static int release_modified_disks();
97 static int get_removed_slices_for_disks(
98 dlist_t *mod_disks);
99 static int get_modified_slices_for_disks(
100 dlist_t *moddisks);
101 static int compare_disk_to_moddisk_disk(
102 void *disk,
103 void *moddisk);
104
105 static int convert_device_names(devconfig_t *request, dlist_t *devs);
106
107 /*
108 * FUNCTION: get_layout(devconfig_t *request, defaults_t *defaults)
109 *
110 * INPUT: request - a devconfig_t pointer to the toplevel request
111 * defaults - a results_t pointer to the defaults
112 *
113 * RETURNS: int - 0 - on success
114 * !0 - otherwise
115 *
116 * PURPOSE: Public entry point to layout module.
117 */
118 int
get_layout(request_t * request,defaults_t * defaults)119 get_layout(
120 request_t *request,
121 defaults_t *defaults)
122 {
123 devconfig_t *diskset_req = NULL;
124 dlist_t *iter = NULL;
125 dlist_t *results = NULL;
126 int error = 0;
127
128 if ((diskset_req = request_get_diskset_req(request)) != NULL) {
129
130 /* initialize using the the top-level disk set request... */
131 if ((error = layout_init(diskset_req, defaults)) != 0) {
132 return (error);
133 }
134
135 oprintf(OUTPUT_TERSE,
136 gettext("\nProcessing volume request...\n"));
137
138 iter = devconfig_get_components(diskset_req);
139 for (; (iter != NULL) && (error == 0); iter = iter->next) {
140
141 /* process each volume request, stop on any error */
142 devconfig_t *subreq = (devconfig_t *)iter->obj;
143 dlist_t *subres = NULL;
144
145 ((error = process_request(subreq, &subres)) != 0) ||
146 (error = collect_modified_disks(subreq, subres)) ||
147 (error = convert_device_names(subreq, subres));
148 if (error == 0) {
149 results = dlist_append(subres, results, AT_TAIL);
150 }
151 }
152
153 if (error == 0) {
154 /* process HSP request */
155 dlist_t *subres = NULL;
156 error = process_hsp_request(diskset_req, &subres);
157 if (error == 0) {
158 results = dlist_append(subres, results, AT_TAIL);
159 }
160 }
161
162 if (error == 0) {
163 oprintf(OUTPUT_TERSE,
164 gettext("\nAssembling volume specification...\n"));
165 /* determine required diskset modifications */
166 error = layout_diskset(request, results);
167 }
168
169 layout_clean_up();
170
171 if (error == 0) {
172 oprintf(OUTPUT_TERSE,
173 gettext("\nVolume request completed successfully.\n"));
174 }
175
176 } else {
177 volume_set_error(
178 gettext("Malformed request, missing top level "
179 "disk set request."));
180 }
181
182 return (error);
183 }
184
185 /*
186 * FUNCTION: layout_clean_up()
187 *
188 * PURPOSE: function which handles the details of cleaning up cached
189 * data and any other memory allocated during the layout
190 * process.
191 *
192 * release physical device data structs
193 * release SVM logical device data structs
194 * release validation data structs
195 * release modified device data structs
196 * release request processing data structs
197 *
198 * This function is also exported as part of the public
199 * interface to the layout module, clients of layout
200 * are required to call this function if get_layout()
201 * was called and was not allowed to return. For example,
202 * if SIGINT was received while a layout request was in
203 * process.
204 */
205 void
layout_clean_up()206 layout_clean_up()
207 {
208 (void) release_request_caches();
209 (void) release_validation_caches();
210
211 (void) release_slices_to_remove();
212 (void) release_modified_slices();
213 (void) release_modified_disks();
214
215 (void) release_reserved_slices();
216 (void) release_used_slices();
217
218 (void) release_usable_devices();
219 (void) release_svm_names(get_request_diskset());
220 (void) release_known_devices();
221
222 (void) unset_hsp_request(NULL);
223 (void) unset_request_defaults(NULL);
224 (void) unset_request_diskset(NULL);
225 (void) unset_toplevel_request(NULL);
226 }
227
228 /*
229 * FUNCTION: layout_init(devconfig_t *diskset, defaults_t *defaults)
230 *
231 * INPUT: diskset - a devconfig_t pointer to the toplevel request
232 * defaults - a results_t pointer to the defaults
233 *
234 * RETURNS: int - 0 - on success
235 * !0 - otherwise
236 *
237 * PURPOSE: function which handles the details of initializing the layout
238 * module prior to processing a request.
239 *
240 * Determines the requested disk set and validates it.
241 *
242 * Scans the physical device configuration.
243 * Scans the SVM logical device configuration.
244 *
245 * Initializes layout private global data structures and does
246 * semantic validation of the request.
247 */
248 static int
layout_init(devconfig_t * diskset,defaults_t * defaults)249 layout_init(
250 devconfig_t *diskset,
251 defaults_t *defaults)
252 {
253 dlist_t *iter = NULL;
254 int error = 0;
255 char *dsname = NULL;
256
257 ((error = validate_basic_svm_config()) != 0) ||
258
259 /* determine & validate requested disk set name */
260 (error = devconfig_get_name(diskset, &dsname)) ||
261 (error = set_request_diskset(dsname)) ||
262
263 /* discover known physical and logical devices */
264 (error = discover_known_devices()) ||
265 (error = scan_svm_names(dsname)) ||
266
267 /* validate and remember toplevel request */
268 (error = set_toplevel_request(diskset)) ||
269
270 /* validate and remember defaults for this request */
271 (error = set_request_defaults(defaults));
272
273 if (error != 0) {
274 return (error);
275 }
276
277 oprintf(OUTPUT_TERSE,
278 gettext("\nValidating volume request...\n"));
279
280 iter = devconfig_get_components(diskset);
281 for (; (iter != NULL) && (error == 0); iter = iter->next) {
282 devconfig_t *subreq = (devconfig_t *)iter->obj;
283 error = validate_request(subreq);
284 }
285
286 if (error == 0) {
287 error = discover_usable_devices(dsname);
288 }
289
290 if (error == 0) {
291 /* final validation on explicitly requested components */
292 error = validate_reserved_slices();
293 }
294
295 if (error == 0) {
296 /* final validation on request sizes vs. actual avail space */
297 error = validate_request_sizes(diskset);
298 }
299
300 return (error);
301 }
302
303 /*
304 * FUNCTION: process_request(devconfig_t *req, dlist_t **results)
305 *
306 * INPUT: req - a devconfig_t pointer to the current request
307 * results - pointer to a list of resulting volumes
308 *
309 * RETURNS: int - 0 - on success
310 * !0 - otherwise
311 *
312 * PURPOSE: function which handles the details of an explicit
313 * volume request.
314 *
315 * Determines the requested volume type, whether the
316 * request contains specific subcomponents and dispatches
317 * to the appropriate layout function for that type.
318 *
319 * Resulting volumes are appended to the results list.
320 *
321 * Note that an HSP request is held until all the volumes
322 * in the request have been successfully composed. This
323 * ensures that HSP spare sizing can be appropriate to
324 * those volumes.
325 */
326 static int
process_request(devconfig_t * req,dlist_t ** results)327 process_request(
328 devconfig_t *req,
329 dlist_t **results)
330 {
331 component_type_t type = TYPE_UNKNOWN;
332 uint64_t nbytes = 0; /* requested volume size */
333 dlist_t *comps = NULL;
334 int ncomps = 0;
335 int error = 0;
336
337 (void) devconfig_get_type(req, &type);
338 (void) devconfig_get_size(req, &nbytes);
339 comps = devconfig_get_components(req);
340
341 if (type == TYPE_HSP) {
342 /* HSP processing needs to happen after all other volumes. */
343 /* set the HSP request aside until all other requests have */
344 /* been completed successfully */
345 set_hsp_request(req);
346 return (0);
347 }
348
349 oprintf(OUTPUT_TERSE, "\n");
350 oprintf(OUTPUT_VERBOSE, "******************\n");
351
352 ncomps = dlist_length(comps);
353
354 if (type == TYPE_STRIPE) {
355 if (ncomps > 0) {
356 return (populate_explicit_stripe(req, results));
357 } else {
358 return (layout_stripe(req, nbytes, results));
359 }
360 }
361
362 if (type == TYPE_CONCAT) {
363 if (ncomps > 0) {
364 return (populate_explicit_concat(req, results));
365 } else {
366 return (layout_concat(req, nbytes, results));
367 }
368 }
369
370 if (type == TYPE_MIRROR) {
371 if (ncomps > 0) {
372 return (populate_explicit_mirror(req, results));
373 } else {
374 uint16_t nsubs = 0;
375 if ((error = get_mirror_nsubs(req, &nsubs)) != 0) {
376 return (error);
377 } else {
378 return (layout_mirror(req, nsubs, nbytes, results));
379 }
380 }
381 }
382
383 if (type == TYPE_VOLUME) {
384 error = process_qos_request(req, results);
385 }
386
387 return (error);
388 }
389
390 /*
391 * FUNCTION: process_qos_request(devconfig_t *req, dlist_t **results)
392 *
393 * INPUT: req - a devconfig_t pointer to the current request
394 * results - pointer to a list of resulting volumes
395 *
396 * RETURNS: int - 0 - on success
397 * !0 - otherwise
398 *
399 * PURPOSE: function which handles the details of mapping an implicit
400 * volume request of QoS attributes into a volume type.
401 *
402 * Resulting volumes are appended to the results list.
403 */
404 static int
process_qos_request(devconfig_t * req,dlist_t ** results)405 process_qos_request(
406 devconfig_t *req,
407 dlist_t **results)
408 {
409 int error = 0;
410
411 uint64_t nbytes = 0;
412 uint16_t rlevel = 0;
413
414 /* get QoS attributes */
415 (void) devconfig_get_size(req, &nbytes);
416
417 if ((error = get_volume_redundancy_level(req, &rlevel)) != 0) {
418 if (error == ERR_ATTR_UNSET) {
419 error = 0;
420 rlevel = 0;
421 }
422 }
423
424 if (error == 0) {
425 if (rlevel == 0) {
426 error = layout_stripe(req, nbytes, results);
427 } else {
428 error = layout_mirror(req, rlevel, nbytes, results);
429 }
430 }
431
432 return (error);
433 }
434
435 /*
436 * FUNCTION: layout_diskset(request_t *req, dlist_t **results)
437 *
438 * INPUT: req - a request_t pointer to the toplevel request
439 * results - pointer to the list of composed result volumes
440 *
441 * RETURNS: int - 0 - on success
442 * !0 - otherwise
443 *
444 * PURPOSE: function which handles the details of completing an layout
445 * request.
446 *
447 * Determines if the disk set specified in the request currently
448 * exists and sets it up for creation if it doesn't.
449 *
450 * Adds new disks required by the result volumes to the disk set.
451 *
452 * Attaches the result volumes to the disk set result.
453 *
454 * Convert slice and disk names to preferred names.
455 *
456 * Attaches the disk set result to the toplevel request.
457 */
458 static int
layout_diskset(request_t * request,dlist_t * results)459 layout_diskset(
460 request_t *request,
461 dlist_t *results)
462 {
463 int error = 0;
464 devconfig_t *diskset = NULL;
465 dlist_t *comps = NULL;
466
467 ((error = new_devconfig(&diskset, TYPE_DISKSET)) != 0) ||
468 (error = devconfig_set_name(diskset, get_request_diskset())) ||
469 (error = add_modified_disks_to_diskset(results, diskset));
470 if (error != 0) {
471 free_devconfig(diskset);
472 return (error);
473 }
474
475 /* add resulting volumes */
476 if (results != NULL) {
477 comps = devconfig_get_components(diskset);
478 comps = dlist_append(results, comps, AT_TAIL);
479 devconfig_set_components(diskset, comps);
480 }
481
482 request_set_diskset_config(request, diskset);
483
484 return (error);
485 }
486
487 /*
488 * FUNCTION: convert_device_names(devconfig_t request, dlist_t *devices)
489 *
490 * INPUT: request - a devconfig_t request pointer
491 * devices - a list of devconfig_t devices
492 *
493 * RETURNS: int - 0 - on success
494 * !0 - on any error
495 *
496 * PURPOSE: Utility function to convert any slice or disk drive
497 * names in a result devconfig_t to the preferred name
498 * which should be used to access the device.
499 *
500 * This convert the temporary names used by layout to
501 * the proper DID or /dev/dsk alias.
502 */
503 static int
convert_device_names(devconfig_t * request,dlist_t * devices)504 convert_device_names(
505 devconfig_t *request,
506 dlist_t *devices)
507 {
508 int error = 0;
509 dlist_t *iter;
510
511 for (iter = devices;
512 (iter != NULL) && (error == 0);
513 iter = iter->next) {
514
515 devconfig_t *dev = (devconfig_t *)iter->obj;
516 component_type_t type = TYPE_UNKNOWN;
517 dm_descriptor_t disk = (dm_descriptor_t)0;
518 char *devname = NULL;
519 char *diskname = NULL;
520 char *slicename = NULL;
521 uint16_t index;
522
523 if ((error = devconfig_get_type(dev, &type)) == 0) {
524 switch (type) {
525
526 case TYPE_MIRROR:
527 case TYPE_STRIPE:
528 case TYPE_CONCAT:
529 case TYPE_HSP:
530
531 error = convert_device_names(request,
532 devconfig_get_components(dev));
533
534 break;
535
536 case TYPE_SLICE:
537
538 ((error = devconfig_get_name(dev, &devname)) != 0) ||
539 (error = devconfig_get_slice_index(dev, &index)) ||
540 (error = get_disk_for_named_slice(devname, &disk)) ||
541 (error = get_device_access_name(request, disk,
542 &diskname)) ||
543 (error = make_slicename_for_diskname_and_index(
544 diskname, index, &slicename));
545
546 if ((error == 0) && (slicename != NULL)) {
547 error = devconfig_set_name(dev, slicename);
548 free(slicename);
549 }
550
551 break;
552 }
553 }
554 }
555
556 return (error);
557 }
558
559 /*
560 * FUNCTION: add_modified_disk(devconfig_t request, dm_descriptor_t disk);
561 *
562 * INPUT: request - a pointr to a devconfig_t request
563 * disk - dm_descriptor_t handle for a disk that has been modified
564 *
565 * SIDEEFFECTS: adds an entry to the _modified_disks list which tracks
566 * the disks that have been explicitly modified by
567 * the layout code.
568 *
569 * RETURNS: int - 0 on success
570 * !0 otherwise
571 *
572 * PURPOSE: Adds the input disk to the list of those that have been
573 * modified.
574 *
575 * Disks are modified during layout for two reasons:
576 *
577 * 1. any disk that is to be added to the disk set gets
578 * an explicitly updated label.
579 *
580 * 2. once a disk is in the disk set, existing slices
581 * may be resized or new slices can be added.
582 */
583 int
add_modified_disk(devconfig_t * request,dm_descriptor_t disk)584 add_modified_disk(
585 devconfig_t *request,
586 dm_descriptor_t disk)
587 {
588 dlist_t *iter = NULL;
589 moddisk_t *moddisk = NULL;
590 dlist_t *item = NULL;
591 int error = 0;
592
593 for (iter = _modified_disks; iter != NULL; iter = iter->next) {
594 moddisk = (moddisk_t *)iter->obj;
595 if (compare_descriptor_names(
596 (void *)(uintptr_t)moddisk->disk,
597 (void *)(uintptr_t)disk) == 0) {
598 /* already in list */
599 return (0);
600 }
601 }
602
603 moddisk = (moddisk_t *)calloc(1, sizeof (moddisk_t));
604 if (moddisk == NULL) {
605 error = ENOMEM;
606 } else {
607 char *aname = NULL;
608 error = get_device_access_name(request, disk, &aname);
609 if (error == 0) {
610
611 /* add to list of modified disks */
612 moddisk->disk = disk;
613 moddisk->accessname = aname;
614 moddisk->slices = NULL;
615
616 if ((item = dlist_new_item((void *)moddisk)) == NULL) {
617 free(moddisk);
618 error = ENOMEM;
619 } else {
620 _modified_disks =
621 dlist_append(item, _modified_disks, AT_HEAD);
622 }
623 }
624 }
625
626 return (error);
627 }
628
629 /*
630 * FUNCTION: collect_modified_disks(devconfig_t *request, dlist_t* devs)
631 *
632 * INPUT: devs - pointer to a list of composed volumes
633 * OUTPUT: none -
634 * SIDEEFFECT: updates the module global list _modified_disks
635 *
636 * RETURNS: int - 0 - success
637 * !0 - failure
638 *
639 * PURPOSE: Helper to maintain the list of disks to be added to the
640 * disk set.
641 *
642 * Iterates the input list of devices and determines which
643 * disks they use. If a disk is not in the _modified_disks
644 * list, it is added.
645 */
646 static int
collect_modified_disks(devconfig_t * request,dlist_t * devs)647 collect_modified_disks(
648 devconfig_t *request,
649 dlist_t *devs)
650 {
651 int error = 0;
652
653 char *sname = NULL;
654 dm_descriptor_t disk = (dm_descriptor_t)0;
655
656 for (; (devs != NULL) && (error == 0); devs = devs->next) {
657
658 devconfig_t *dev = (devconfig_t *)devs->obj;
659 component_type_t type = TYPE_UNKNOWN;
660
661 if ((error = devconfig_get_type(dev, &type)) == 0) {
662
663 switch (type) {
664 case TYPE_MIRROR:
665 case TYPE_STRIPE:
666 case TYPE_CONCAT:
667 case TYPE_HSP:
668
669 error = collect_modified_disks(request,
670 devconfig_get_components(dev));
671 break;
672
673 case TYPE_SLICE:
674
675 ((error = devconfig_get_name(dev, &sname)) != 0) ||
676 (error = get_disk_for_named_slice(sname, &disk)) ||
677 (error = add_modified_disk(request, disk));
678
679 break;
680 }
681 }
682 }
683
684 return (error);
685 }
686
687 /*
688 * FUNCTION: add_modified_disks_to_diskset(dlist_t *devices,
689 * devconfig_t *diskset)
690 *
691 * INPUT: devices - pointer to a list of devices
692 *
693 * OUTPUT: diskset - pointer to a devconfig_t representing the disk set,
694 * updated to include modified disks and slices as
695 * components.
696 *
697 * RETURNS: int - 0 - success
698 * !0 - failure
699 *
700 * PURPOSE: Helper to add devconfig_t structs for disks and slices
701 * to the disk set.
702 *
703 * Updates the list of _modified_disks by examining the input
704 * list of composed devices.
705 *
706 * Iterates _modified_disks and creates a devconfig_t component
707 * for each disk in the list, the list of disks is then attached
708 * to the input disk set.
709 *
710 * Modified slices for disks in the disk set are added as well.
711 */
712 static int
add_modified_disks_to_diskset(dlist_t * results,devconfig_t * diskset)713 add_modified_disks_to_diskset(
714 dlist_t *results,
715 devconfig_t *diskset)
716 {
717 int error = 0;
718
719 dlist_t *iter;
720 dlist_t *list = NULL;
721 char *dsname = get_request_diskset();
722
723 /* add modified disks to disk set's component list */
724 list = devconfig_get_components(diskset);
725
726 oprintf(OUTPUT_TERSE,
727 gettext(" Collecting modified disks...\n"));
728
729 /* collect removed slices for modified disks */
730 error = get_removed_slices_for_disks(_modified_disks);
731
732 /* collect modified slices for modified disks */
733 error = get_modified_slices_for_disks(_modified_disks);
734
735 for (iter = _modified_disks;
736 (iter != NULL) && (error == 0);
737 iter = iter->next) {
738
739 moddisk_t *moddisk = (moddisk_t *)iter->obj;
740 dm_descriptor_t disk = moddisk->disk;
741 devconfig_t *newdisk = NULL;
742 boolean_t in_set = B_FALSE;
743
744 oprintf(OUTPUT_VERBOSE, " %s\n", moddisk->accessname);
745
746 error = is_disk_in_diskset(disk, dsname, &in_set);
747 if ((error == 0) && (in_set != B_TRUE)) {
748 /* New disk, add it to the disk set */
749 ((error = new_devconfig(&newdisk, TYPE_DRIVE)) != 0) ||
750 (error = devconfig_set_name(newdisk, moddisk->accessname));
751 if (error == 0) {
752 dlist_t *item = dlist_new_item(newdisk);
753 if (item == NULL) {
754 error = ENOMEM;
755 } else {
756 list = dlist_append(item, list, AT_TAIL);
757 oprintf(OUTPUT_DEBUG,
758 gettext(" must add %s to disk set \"%s\"\n"),
759 moddisk->accessname, dsname);
760 }
761 } else {
762 free_devconfig(newdisk);
763 }
764 }
765
766 if ((error == 0) && (moddisk->slices != NULL)) {
767 /* move moddisk's slice list to disk set comp list */
768 list = dlist_append(moddisk->slices, list, AT_TAIL);
769 moddisk->slices = NULL;
770 }
771 }
772
773 if (error == 0) {
774 devconfig_set_components(diskset, list);
775 } else {
776 dlist_free_items(list, NULL);
777 }
778
779 return (error);
780 }
781
782 /*
783 * FUNCTIONS: void release_modified_disks()
784 *
785 * INPUT: none -
786 * OUTPUT: none -
787 *
788 * PURPOSE: cleanup the module global list of disks that need
789 * to be added to the disk set to satisfy the request.
790 */
791 static int
release_modified_disks()792 release_modified_disks()
793 {
794 dlist_t *iter = _modified_disks;
795
796 for (; iter != NULL; iter = iter->next) {
797 moddisk_t *moddisk = (moddisk_t *)iter->obj;
798 if (moddisk->slices != NULL) {
799 dlist_free_items(moddisk->slices, free_devconfig);
800 moddisk->slices = NULL;
801 }
802 free(moddisk);
803 iter->obj = NULL;
804 }
805
806 dlist_free_items(_modified_disks, NULL);
807 _modified_disks = NULL;
808
809 return (0);
810 }
811
812 /*
813 * FUNCTION: get_removed_slices_for_disks(dlist_t *mod_disks)
814 *
815 * INPUT: mod_disks - a list of moddisk_t structs
816 *
817 * OUTPUT: mod_disks - the list of moddisk_t structs updated with
818 * the slices to be removed for each disk
819 *
820 * RETURNS: int - 0 - success
821 * !0 - failure
822 *
823 * PURPOSE: Helper to create a list of devconfig_t structs
824 * for slices on the input disks which need to be
825 * removed from the system.
826 *
827 * Iterates the list of slices to be removed and
828 * creates a devconfig_t component for each slice
829 * in the list that is on any of the input modified
830 * disks.
831 *
832 * Slice names are constructed using the modified disk's
833 * access name to ensure that the correct alias is
834 * used to get to the slice.
835 */
836 static int
get_removed_slices_for_disks(dlist_t * mod_disks)837 get_removed_slices_for_disks(
838 dlist_t *mod_disks)
839 {
840 int error = 0;
841 dlist_t *iter = NULL;
842
843 /* collect slices to be removed for the modified disks */
844 for (iter = get_slices_to_remove();
845 (iter != NULL) && (error == 0);
846 iter = iter->next) {
847
848 rmvdslice_t *rmvd = (rmvdslice_t *)iter->obj;
849 dm_descriptor_t disk = (dm_descriptor_t)0;
850 moddisk_t *moddisk = NULL;
851 char *sname = NULL;
852 devconfig_t *newslice = NULL;
853 dlist_t *item = NULL;
854
855 (void) get_disk_for_named_slice(rmvd->slice_name, &disk);
856
857 if ((item = dlist_find(mod_disks, (void *)(uintptr_t)disk,
858 compare_disk_to_moddisk_disk)) == NULL) {
859 /* slice on disk that we don't care about */
860 continue;
861 }
862
863 moddisk = (moddisk_t *)item->obj;
864
865 /* create output slice struct for the removed slice */
866 ((error = make_slicename_for_diskname_and_index(
867 moddisk->accessname, rmvd->slice_index, &sname)) != 0) ||
868 (error = new_devconfig(&newslice, TYPE_SLICE)) ||
869 (error = devconfig_set_name(newslice, sname)) ||
870 (error = devconfig_set_size_in_blocks(newslice, 0));
871
872 /* add to the moddisk's list of slices */
873 if (error == 0) {
874 if ((item = dlist_new_item(newslice)) == NULL) {
875 free_devconfig(newslice);
876 error = ENOMEM;
877 } else {
878 moddisk->slices =
879 dlist_append(item, moddisk->slices, AT_TAIL);
880 }
881 } else {
882 free_devconfig(newslice);
883 }
884 }
885
886 return (error);
887 }
888
889 /*
890 * FUNCTION: get_modified_slices_for_disks(dlist_t *mod_disks)
891 *
892 * INPUT: mod_disks - a list of moddisk_t structs
893 *
894 * OUTPUT: mod_disks - the list of moddisk_t structs updated with
895 * the modified slices for each disk
896 *
897 * RETURNS: int - 0 - success
898 * !0 - failure
899 *
900 * PURPOSE: Helper to create a list of devconfig_t structs
901 * for slices on the input disks which have been
902 * modified for use by layout.
903 *
904 * Iterates the list of modified slices and creates a
905 * devconfig_t component for each slice in the list
906 * that is on any of the input modified disks.
907 *
908 * Slice names are constructed using the modified disk's
909 * access name to ensure that the correct alias is
910 * used to get to the slice.
911 */
912 int
get_modified_slices_for_disks(dlist_t * mod_disks)913 get_modified_slices_for_disks(
914 dlist_t *mod_disks)
915 {
916 int error = 0;
917 dlist_t *iter = NULL;
918
919 for (iter = get_modified_slices();
920 (iter != NULL) && (error == 0);
921 iter = iter->next) {
922
923 modslice_t *mods = (modslice_t *)iter->obj;
924 devconfig_t *slice = mods->slice_devcfg;
925 devconfig_t *newslice = NULL;
926 dm_descriptor_t disk;
927 moddisk_t *moddisk;
928 dlist_t *item;
929 char *sname = NULL;
930 uint64_t stblk = 0;
931 uint64_t nblks = 0;
932 uint16_t index;
933
934 /* only add modified slices that were sources */
935 if ((mods->times_modified == 0) ||
936 (mods->src_slice_desc != (dm_descriptor_t)0)) {
937 continue;
938 }
939
940 (void) devconfig_get_name(slice, &sname);
941 (void) get_disk_for_named_slice(sname, &disk);
942
943 if ((item = dlist_find(mod_disks, (void *)(uintptr_t)disk,
944 compare_disk_to_moddisk_disk)) == NULL) {
945 /* slice on disk that we don't care about */
946 continue;
947 }
948
949 moddisk = (moddisk_t *)item->obj;
950
951 /* create output slice struct for the modified slice */
952 ((error = devconfig_get_slice_start_block(slice,
953 &stblk)) != 0) ||
954 (error = devconfig_get_size_in_blocks(slice, &nblks)) ||
955 (error = devconfig_get_slice_index(slice, &index)) ||
956 (error = make_slicename_for_diskname_and_index(
957 moddisk->accessname, index, &sname)) ||
958 (error = new_devconfig(&newslice, TYPE_SLICE)) ||
959 (error = devconfig_set_name(newslice, sname)) ||
960 (error = devconfig_set_slice_start_block(newslice, stblk)) ||
961 (error = devconfig_set_size_in_blocks(newslice, nblks));
962
963 /* add to the moddisk's list of slices */
964 if (error == 0) {
965 if ((item = dlist_new_item(newslice)) == NULL) {
966 free_devconfig(newslice);
967 error = ENOMEM;
968 } else {
969 moddisk->slices =
970 dlist_append(item, moddisk->slices, AT_TAIL);
971 }
972 } else {
973 free_devconfig(newslice);
974 }
975 }
976
977 return (error);
978 }
979
980 /*
981 * FUNCTION: compare_disk_to_moddisk_disk(void *disk, void *moddisk)
982 *
983 * INPUT: disk - opaque pointer to a dm_descriptor_t
984 * moddisk - opaque moddisk_t pointer
985 *
986 * RETURNS: int - 0 - if disk == moddisk->disk
987 * !0 - otherwise
988 *
989 * PURPOSE: dlist_t helper which compares the input disk dm_descriptor_t
990 * handle to the disk dm_descriptor_t handle in the input
991 * moddisk_t struct.
992 *
993 * Comparison is done via compare_descriptor_names.
994 */
995 static int
compare_disk_to_moddisk_disk(void * disk,void * moddisk)996 compare_disk_to_moddisk_disk(
997 void *disk,
998 void *moddisk)
999 {
1000 assert(disk != (dm_descriptor_t)0);
1001 assert(moddisk != NULL);
1002
1003 return (compare_descriptor_names((void *)disk,
1004 (void *)(uintptr_t)((moddisk_t *)moddisk)->disk));
1005 }
1006
1007 /*
1008 * FUNCTIONS: void set_hsp_request()
1009 *
1010 * INPUT: none -
1011 * OUTPUT: none -
1012 *
1013 * PURPOSE: set the module global HSP request struct.
1014 */
1015 static void
set_hsp_request(devconfig_t * req)1016 set_hsp_request(
1017 devconfig_t *req)
1018 {
1019 _hsp_request = req;
1020 }
1021
1022 /*
1023 * FUNCTIONS: void unset_hsp_request()
1024 *
1025 * INPUT: none -
1026 * OUTPUT: none -
1027 *
1028 * PURPOSE: unset the module global HSP request struct.
1029 */
1030 static void
unset_hsp_request()1031 unset_hsp_request()
1032 {
1033 _hsp_request = NULL;
1034 }
1035
1036 /*
1037 * FUNCTION: process_hsp_request(devconfig_t *req, dlist_t **results)
1038 * INPUT: req - pointer to the toplevel disk set devconfig_t request
1039 * results - pointer to a list of composed results
1040 *
1041 * RETURNS: int - 0 - success
1042 * !0 - failure
1043 *
1044 * PURPOSE: Helper which determines HSP processing for the
1045 * composed volumes which need HSP spares.
1046 */
1047 static int
process_hsp_request(devconfig_t * req,dlist_t ** results)1048 process_hsp_request(
1049 devconfig_t *req,
1050 dlist_t **results)
1051 {
1052 int error = 0;
1053
1054 if (_hsp_request != NULL) {
1055 oprintf(OUTPUT_TERSE,
1056 gettext("\nProcessing HSP...\n"));
1057 }
1058
1059 if (_hsp_devices == NULL) {
1060 /* no devices -> no HSP */
1061 oprintf(OUTPUT_VERBOSE,
1062 gettext(" No devices require hot spares...\n"));
1063 } else {
1064
1065 oprintf(OUTPUT_TERSE, "\n");
1066
1067 ((error = layout_hsp(req, _hsp_request, _hsp_devices,
1068 results)) != 0) ||
1069 (error = collect_modified_disks(_hsp_request, *results)) ||
1070 (error = convert_device_names(_hsp_request, *results));
1071 }
1072
1073 return (error);
1074 }
1075
1076 /*
1077 * FUNCTION: add_to_hsp_list(dlist_t* list)
1078 * INPUT: devs - pointer to a list of composed volumes
1079 * OUTPUT: none -
1080 * SIDEEFFECT: updates the module global list _hsp_devices
1081 *
1082 * RETURNS: int - 0 - success
1083 * !0 - failure
1084 *
1085 * PURPOSE: Helper to update the list of devices which need HSP spares.
1086 *
1087 * Iterates the input list of devices and adds them them to the
1088 * module provate list of devices needing spares.
1089 */
1090 int
add_to_hsp_list(dlist_t * list)1091 add_to_hsp_list(
1092 dlist_t *list)
1093 {
1094 dlist_t *iter = NULL;
1095 int error = 0;
1096
1097 for (iter = list; iter != NULL; iter = iter->next) {
1098 dlist_t *item = NULL;
1099
1100 if ((item = dlist_new_item(iter->obj)) == NULL) {
1101 error = ENOMEM;
1102 break;
1103 }
1104 _hsp_devices = dlist_append(item, _hsp_devices, AT_HEAD);
1105 }
1106
1107 return (error);
1108 }
1109
1110 /*
1111 * FUNCTION: string_case_compare(
1112 * char *str1, char *str2)
1113 *
1114 * INPUT: str1 - char *
1115 * str2 - char *
1116 *
1117 * RETURNS: int - <0 - if str1 < str2
1118 * 0 - if str1 == str2
1119 * >0 - if str1 > str2
1120 *
1121 * PURPOSE: More robust case independent string comparison function.
1122 *
1123 * Assumes str1 and str2 are both char *
1124 *
1125 * Compares the lengths of each and if equivalent compares
1126 * the strings using strcasecmp.
1127 */
1128 int
string_case_compare(char * str1,char * str2)1129 string_case_compare(
1130 char *str1,
1131 char *str2)
1132 {
1133 int result = 0;
1134
1135 assert(str1 != NULL);
1136 assert(str2 != NULL);
1137
1138 if ((result = (strlen(str1) - strlen(str2))) == 0) {
1139 result = strcasecmp(str1, str2);
1140 }
1141
1142 return (result);
1143 }
1144