/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include "volume_error.h" #include "volume_devconfig.h" #include "volume_dlist.h" #include "volume_output.h" #include "layout_device_cache.h" #include "layout_device_util.h" #include "layout_discovery.h" #include "layout_dlist_util.h" #include "layout_messages.h" #include "layout_request.h" #include "layout_slice.h" #include "layout_svm_util.h" #define _LAYOUT_STRIPE_C static int compose_stripe( devconfig_t *request, uint64_t nbytes, dlist_t *disks, int max, int min, dlist_t *othervols, devconfig_t **stripe); static int compose_stripe_within_hba( devconfig_t *request, dlist_t *hbas, uint64_t nbytes, uint16_t min, uint16_t max, devconfig_t **stripe); static int assemble_stripe( devconfig_t *request, dlist_t *comps, devconfig_t **stripe); static dlist_t * order_stripe_components_alternate_hbas( dlist_t *comps); static int compute_usable_stripe_capacity( dlist_t *comps, uint64_t ilace, uint64_t *nbytes); /* * FUNCTION: layout_stripe(devconfig_t *request, uint64_t nbytes, * dlist_t **results) * * INPUT: request - pointer to a devconfig_t of the current request * nbytes - the desired capacity of the stripe * * OUPUT: results - pointer to a list of composed volumes * * RETURNS: int - 0 on success * !0 otherwise. * * PURPOSE: Main layout driver for composing stripe volumes. * * Attempts to construct a stripe of size nbytes. * * Basic goal of all strategies is to build wide-thin stripes: * build widest stripe possible across as many HBAs as possible. * * Several different layout strategies are tried in order * of preference until one succeeds or there are none left. * * 1 - stripe across similar HBAs * . number of components is driven by # of HBAs * . requires mincomp available HBAs * * 2 - stripe within a single HBA * . number of components is driven by # of disks * . requires at least 1 HBA with mincomp disks * * 3 - stripe across all available disks on similar HBAs * . number of components is driven by # of disk * . requires at least mincomp disks * * 4 - stripe across all available HBAs * . number of components is driven by # of HBAs * . requires at least mincomp HBAs * * 5 - stripe across all available disks on all HBAs * . number of components is driven by # of disks * . requires at least mincomp disks * * Each strategy tries to compose a stripe with the * maximum number of components first then reduces the * number of components down to mincomp. * * get allowed minimum number of stripe components * get allowed maximum number of stripe components * get available HBAs * * group HBAs by characteristics * for (each HBA grouping) and (stripe not composed) { * select next HBA group * for (strategy[1,2,3]) and (stripe not composed) { * compose stripe using HBAs in group * } * } * * if (stripe not composed) { * for (strategy[4,5]) and (stripe not composed) { * compose stripe using all HBAs * } * } * * if (stripe composed) { * append composed stripe to results * } * */ int layout_stripe( devconfig_t *request, uint64_t nbytes, dlist_t **results) { /* * these enums define the # of strategies and the preference order * in which they are tried */ typedef enum { STRIPE_ACROSS_SIMILAR_HBAS_DISK_PER = 0, STRIPE_WITHIN_SIMILAR_HBA, STRIPE_ACROSS_SIMILAR_HBAS, N_SIMILAR_HBA_STRATEGIES } similar_hba_strategy_order_t; typedef enum { STRIPE_ACROSS_ANY_HBAS_DISK_PER = 0, STRIPE_ACROSS_ANY_HBAS, N_ANY_HBA_STRATEGIES } any_hba_strategy_order_t; dlist_t *usable_hbas = NULL; dlist_t *similar_hba_groups = NULL; dlist_t *iter = NULL; devconfig_t *stripe = NULL; uint16_t mincomp = 0; uint16_t maxcomp = 0; int error = 0; (error = get_usable_hbas(&usable_hbas)); if (error != 0) { return (error); } print_layout_volume_msg(devconfig_type_to_str(TYPE_STRIPE), nbytes); if (dlist_length(usable_hbas) == 0) { print_no_hbas_msg(); volume_set_error(gettext("There are no usable HBAs.")); return (-1); } ((error = group_similar_hbas(usable_hbas, &similar_hba_groups)) != 0) || /* * determine the min/max number of stripe components * based on the request, the diskset defaults or the * global defaults. These are absolute limits, the * actual values are determined by the number of HBAs * and/or disks available. */ (error = get_stripe_min_comp(request, &mincomp)) || (error = get_stripe_max_comp(request, &maxcomp)); if (error != 0) { return (error); } for (iter = similar_hba_groups; (error == 0) && (stripe == NULL) && (iter != NULL); iter = iter->next) { dlist_t *hbas = (dlist_t *)iter->obj; similar_hba_strategy_order_t order; for (order = STRIPE_ACROSS_SIMILAR_HBAS_DISK_PER; (order < N_SIMILAR_HBA_STRATEGIES) && (stripe == NULL) && (error == 0); order++) { dlist_t *selhbas = NULL; dlist_t *disks = NULL; int n = 0; switch (order) { case STRIPE_ACROSS_SIMILAR_HBAS_DISK_PER: error = select_hbas_with_n_disks( request, hbas, 1, &selhbas, &disks); if (error == 0) { /* BEGIN CSTYLED */ oprintf(OUTPUT_TERSE, gettext(" -->Strategy 1: use 1 disk from %d-%d similar HBAs - stripe across HBAs\n"), mincomp, maxcomp); /* END CSTYLED */ if ((n = dlist_length(selhbas)) >= mincomp) { n = ((n > maxcomp) ? maxcomp : n); error = compose_stripe( request, nbytes, disks, n, mincomp, NULL, &stripe); } else { print_insufficient_hbas_msg(n); } } break; case STRIPE_WITHIN_SIMILAR_HBA: error = select_hbas_with_n_disks( request, hbas, mincomp, &selhbas, &disks); if (error == 0) { /* BEGIN CSTYLED */ oprintf(OUTPUT_TERSE, gettext(" -->Strategy 2: use %d-%d disks from any single HBA - stripe within HBA\n"), mincomp, maxcomp); /* END CSTYLED */ if ((n = dlist_length(selhbas)) > 0) { error = compose_stripe_within_hba( request, selhbas, nbytes, mincomp, maxcomp, &stripe); } else { print_insufficient_disks_msg(n); } } break; case STRIPE_ACROSS_SIMILAR_HBAS: error = select_hbas_with_n_disks( request, hbas, 1, &selhbas, &disks); if (error == 0) { /* BEGIN CSTYLED */ oprintf(OUTPUT_TERSE, gettext(" -->Strategy 3: use %d-%d disks from %d similar HBAs - stripe across HBAs\n"), mincomp, maxcomp, dlist_length(hbas)); /* END CSTYLED */ if ((n = dlist_length(selhbas)) > 0) { if ((n = dlist_length(disks)) >= mincomp) { n = ((n > maxcomp) ? maxcomp : n); error = compose_stripe( request, nbytes, disks, n, mincomp, NULL, &stripe); } else { print_insufficient_disks_msg(n); } } else { print_insufficient_hbas_msg(n); } } break; default: break; } dlist_free_items(disks, NULL); dlist_free_items(selhbas, NULL); } } for (iter = similar_hba_groups; iter != NULL; iter = iter->next) { dlist_free_items((dlist_t *)iter->obj, NULL); } dlist_free_items(similar_hba_groups, NULL); /* * if striping within similar HBA groups failed, * try across all available HBAs */ if ((stripe == NULL) && (error == 0)) { any_hba_strategy_order_t order; for (order = STRIPE_ACROSS_ANY_HBAS_DISK_PER; (order < N_ANY_HBA_STRATEGIES) && (stripe == NULL) && (error == 0); order++) { dlist_t *selhbas = NULL; dlist_t *disks = NULL; int n = 0; switch (order) { case STRIPE_ACROSS_ANY_HBAS_DISK_PER: error = select_hbas_with_n_disks( request, usable_hbas, 1, &selhbas, &disks); if (error == 0) { /* BEGIN CSTYLED */ oprintf(OUTPUT_TERSE, gettext(" -->Strategy 4: use 1 disk from %d-%d available HBAs - stripe across any HBAs\n"), mincomp, maxcomp); /* END CSTYLED */ if ((n = dlist_length(selhbas)) >= mincomp) { n = ((n > maxcomp) ? maxcomp : n); error = compose_stripe( request, nbytes, disks, n, mincomp, NULL, &stripe); } else { print_insufficient_hbas_msg(n); } } break; case STRIPE_ACROSS_ANY_HBAS: error = select_hbas_with_n_disks( request, usable_hbas, 1, &selhbas, &disks); if (error == 0) { /* BEGIN CSTYLED */ oprintf(OUTPUT_TERSE, gettext(" -->Strategy 5: use %d-%d disks from %d available HBA - stripe across any HBAs\n"), mincomp, maxcomp, dlist_length(selhbas)); /* END CSTYLED */ if ((n = dlist_length(disks)) >= mincomp) { n = ((n > maxcomp) ? maxcomp : n); error = compose_stripe( request, nbytes, disks, n, mincomp, NULL, &stripe); } else { print_insufficient_disks_msg(n); } } break; } dlist_free_items(disks, NULL); dlist_free_items(selhbas, NULL); } } if (stripe != NULL) { dlist_t *item = NULL; if ((item = dlist_new_item(stripe)) == NULL) { error = ENOMEM; } else { *results = dlist_append(item, *results, AT_TAIL); print_layout_success_msg(); } } else if (error != 0) { print_debug_failure_msg( devconfig_type_to_str(TYPE_STRIPE), get_error_string(error)); } else { print_insufficient_resources_msg( devconfig_type_to_str(TYPE_STRIPE)); error = -1; } return (error); } /* * FUNCTION: populate_stripe(devconfig_t *request, uint64_t nbytes, * dlist_t *disks, uint16_t ncomp, dlist_t *othervols, * devconfig_t **stripe) * * INPUT: request - pointer to a request devconfig_t * nbytes - desired stripe size * disks - pointer to a list of availalb disks * ncomp - number of components desired * othervols - pointer to a list of other volumes whose * composition may affect this stripe * (e.g., submirrors of the same mirror) * * OUTPUT: stripe - pointer to a devconfig_t to hold resulting stripe * * RETURNS: int - 0 on success * !0 otherwise. * * PURPOSE: Helper to populate a stripe with the specified number of * components and aggregate capacity using slices on disks * in the input list. * * If the othervols list is not empty, the slice components * chosen for the stripe must not on the same disks as any * of the other volumes. * * If sufficient slice components can be found, the stripe * is assembled and returned. */ int populate_stripe( devconfig_t *request, uint64_t nbytes, dlist_t *disks, uint16_t ncomp, dlist_t *othervols, devconfig_t **stripe) { uint16_t npaths = 0; uint16_t ncomps = 0; /* number of components found */ uint64_t rsize = 0; /* reqd component size */ dlist_t *other_hbas = NULL; dlist_t *other_disks = NULL; dlist_t *slices = NULL; dlist_t *comps = NULL; int error = 0; *stripe = NULL; ((error = disks_get_avail_slices(request, disks, &slices)) != 0) || (error = get_volume_npaths(request, &npaths)); if (error != 0) { return (error); } print_populate_volume_ncomps_msg( devconfig_type_to_str(TYPE_STRIPE), nbytes, ncomp); if (slices == NULL) { print_populate_no_slices_msg(); return (0); } /* determine HBAs and disks used by othervols */ error = get_hbas_and_disks_used_by_volumes(othervols, &other_hbas, &other_disks); if (error != 0) { dlist_free_items(other_hbas, NULL); dlist_free_items(other_disks, NULL); return (error); } print_populate_choose_slices_msg(); /* * each stripe component needs to be this size. * Note that the stripe interlace doesn't need to be * taken into account in this computation because any * slice selected as a stripe component will be oversized * to account for interlace and cylinder rounding done * by libmeta. */ rsize = nbytes / ncomp; /* * need to select 'ncomp' slices that are at least 'rsize' * large in order to reach the desired capacity. */ ncomps = 0; while ((ncomps < ncomp) && (error == 0)) { devconfig_t *comp = NULL; dlist_t *item = NULL; dlist_t *rmvd = NULL; char *cname = NULL; /* BEGIN CSTYLED */ /* * 1st B_TRUE: require a different disk than those used by * comps and othervols * 2nd B_TRUE: requested size is minimum acceptable * 3rd B_TRUE: add an extra cylinder to the resulting slice, this is * necessary for Stripe components whose sizes get rounded * down to an interlace multiple and then down to a cylinder * boundary. */ /* END CSTYLED */ error = choose_slice(rsize, npaths, slices, comps, other_hbas, other_disks, B_TRUE, B_TRUE, B_TRUE, &comp); if ((error == 0) && (comp != NULL)) { ++ncomps; item = dlist_new_item(comp); if (item == NULL) { error = ENOMEM; } else { /* add selected component to comp list */ comps = dlist_insert_ordered( item, comps, ASCENDING, compare_devconfig_sizes); /* remove it from the available list */ slices = dlist_remove_equivalent_item(slices, (void *) comp, compare_devconfig_and_descriptor_names, &rmvd); if (rmvd != NULL) { free(rmvd); } /* add the component slice to the used list */ if ((error = devconfig_get_name(comp, &cname)) == 0) { error = add_used_slice_by_name(cname); } } } else if (comp == NULL) { /* no possible slice */ break; } } dlist_free_items(slices, NULL); dlist_free_items(other_hbas, NULL); dlist_free_items(other_disks, NULL); if (ncomps == ncomp) { if ((error = assemble_stripe(request, comps, stripe)) == 0) { print_populate_success_msg(); } else { dlist_free_items(comps, free_devconfig_object); } } else if (error == 0) { if (ncomps > 0) { print_insufficient_components_msg(ncomps); dlist_free_items(comps, free_devconfig_object); } else { print_populate_no_slices_msg(); } } return (error); } /* * FUNCTION: populate_explicit_stripe(devconfig_t *request, * dlist_t **results) * * INPUT: request - pointer to a request devconfig_t * * OUTPUT: results - pointer to a list of volume devconfig_t results * * RETURNS: int - 0 on success * !0 otherwise. * * PURPOSE: Processes the input stripe request that specifies explicit * slice components. * * The components have already been validated and reserved, * all that is required is to create devconfig_t structs * for each requested slice. * * The net size of the stripe is determined by the slice * components. * * The stripe devconfig_t is assembled and appended to the * results list. * * This function is also called from * layout_mirror.populate_explicit_mirror() */ int populate_explicit_stripe( devconfig_t *request, dlist_t **results) { devconfig_t *stripe = NULL; int error = 0; dlist_t *comps = NULL; dlist_t *iter = NULL; dlist_t *item = NULL; print_layout_explicit_msg(devconfig_type_to_str(TYPE_STRIPE)); /* assemble components */ iter = devconfig_get_components(request); for (; (iter != NULL) && (error == 0); iter = iter->next) { devconfig_t *rqst = (devconfig_t *)iter->obj; dm_descriptor_t rqst_slice = NULL; char *rqst_name = NULL; devconfig_t *comp = NULL; /* slice components have been validated */ /* turn each into a devconfig_t */ ((error = devconfig_get_name(rqst, &rqst_name)) != 0) || (error = slice_get_by_name(rqst_name, &rqst_slice)) || (error = create_devconfig_for_slice(rqst_slice, &comp)); if (error == 0) { print_layout_explicit_added_msg(rqst_name); item = dlist_new_item((void *)comp); if (item == NULL) { error = ENOMEM; } else { comps = dlist_append(item, comps, AT_TAIL); } } } if (error == 0) { error = assemble_stripe(request, comps, &stripe); } if (error == 0) { if ((item = dlist_new_item(stripe)) == NULL) { error = ENOMEM; } else { *results = dlist_append(item, *results, AT_TAIL); print_populate_success_msg(); } } else { dlist_free_items(comps, free_devconfig); } return (error); } /* * FUNCTION: compose_stripe(devconfig_t *request, uint64_t nbytes, * dlist_t *disks, uint16_t max, uint16_t min, * dlist_t *othervols, devconfig_t **stripe) * * INPUT: request - pointer to a request devconfig_t * nbytes - desired stripe size * disks - pointer to a list of availalb disks * max - maximum number of components allowed * min - minimum number of components allowed * othervols - pointer to a list of other volumes whose * composition may affect this stripe * (e.g., submirrors of the same mirror) * * OUTPUT: stripe - pointer to a devconfig_t to hold resulting stripe * * RETURNS: int - 0 on success * !0 otherwise. * * PURPOSE: Attempt to compose a stripe of capacity nbytes, with * component slices chosen from the input list of disks. * The number of components in the stripe should be in the * range min <= N <= max, more components are preferred. * * If a stripe can be composed, a pointer to it will be * returned in the stripe devconfig_t. * * This is a loop wrapped around populate_stripe which * varies the number of components between 'max' and 'min'. */ static int compose_stripe( devconfig_t *request, uint64_t nbytes, dlist_t *disks, int max, int min, dlist_t *othervols, devconfig_t **stripe) { int error = 0; *stripe = NULL; for (; (error == 0) && (*stripe == NULL) && (max >= min); max--) { error = populate_stripe( request, nbytes, disks, max, othervols, stripe); } return (error); } /* * FUNCTION: compose_stripe_within_hba(devconfig_t *request, * dlist_t *hbas, uint64_t nbytes, * int maxcomp, int mincomp, dlist_t **stripe) * * INPUT: request - pointer to a devconfig_t of the current request * hbas - pointer to a list of available HBAs * nbytes - the desired capacity for the stripe * maxcomp - the maximum number of stripe components * mincomp - the minimum number of stripe components * * OUTPUT: stripe - pointer to a stripe devconfig_t result * * RETURNS: int - 0 on success * !0 otherwise. * * PURPOSE: Layout function which compose a stripe of the desired size * using available disks within any single HBA from the input list. * * The number of components within the composed stripe will be * in the range of min to max, preferring more components * over fewer. * * All input HBAs are expected to have at least mincomp * available disks and total space sufficient for the stripe. * * If the stripe can be composed, a pointer to it is returned in * the stripe devconfig_t *. * * * while (more hbas and stripe not composed) { * select HBA * if (not enough available space on this HBA) { * continue; * } * get available disks for HBA * use # disks as max # of stripe components * try to compose stripe * } * */ static int compose_stripe_within_hba( devconfig_t *request, dlist_t *hbas, uint64_t nbytes, uint16_t min, uint16_t max, devconfig_t **stripe) { int error = 0; dlist_t *iter = NULL; *stripe = NULL; for (iter = hbas; (iter != NULL) && (error == 0) && (*stripe == NULL); iter = iter->next) { dm_descriptor_t hba = (uintptr_t)iter->obj; dlist_t *disks = NULL; uint64_t space = 0; uint16_t ncomp = 0; char *name; ((error = get_display_name(hba, &name)) != 0) || (error = hba_get_avail_disks_and_space(request, hba, &disks, &space)); if (error == 0) { if (space >= nbytes) { ncomp = dlist_length(disks); ncomp = ((ncomp > max) ? max : ncomp); error = compose_stripe( request, nbytes, disks, ncomp, min, NULL, stripe); } else { print_hba_insufficient_space_msg(name, space); } } dlist_free_items(disks, NULL); } return (error); } /* * FUNCTION: assemble_stripe(devconfig_t *request, dlist_t *comps, * devconfig_t **stripe) * * INPUT: request - pointer to a devconfig_t of the current request * comps - pointer to a list of slice components * * OUPUT: stripe - pointer to a devconfig_t to hold final stripe * * RETURNS: int - 0 on success * !0 otherwise. * * PURPOSE: Helper which creates and populates a stripe devconfig_t * struct using information from the input request and the * list of slice components. * * Determines the name of the stripe either from the request * or from the default naming scheme. * * Sets the interlace for the stripe if a value is specified * in the request. * * Attaches the input list of components to the devconfig. */ static int assemble_stripe( devconfig_t *request, dlist_t *comps, devconfig_t **stripe) { uint64_t ilace = 0; char *name = NULL; int error = 0; if ((error = new_devconfig(stripe, TYPE_STRIPE)) == 0) { /* set stripe name, use requested name if specified */ if ((error = devconfig_get_name(request, &name)) != 0) { if (error != ERR_ATTR_UNSET) { volume_set_error(gettext("error getting requested name\n")); } else { error = 0; } } if (error == 0) { if (name == NULL) { if ((error = get_next_volume_name(&name, TYPE_STRIPE)) == 0) { error = devconfig_set_name(*stripe, name); free(name); } } else { error = devconfig_set_name(*stripe, name); } } } if (error == 0) { if ((error = get_stripe_interlace(request, &ilace)) == 0) { error = devconfig_set_stripe_interlace(*stripe, ilace); } else if (error == ENOENT) { ilace = get_default_stripe_interlace(); error = 0; } } if (error == 0) { uint64_t nbytes = 0; if ((error = compute_usable_stripe_capacity(comps, ilace, &nbytes)) == 0) { error = devconfig_set_size_in_blocks(*stripe, nbytes/DEV_BSIZE); } } if (error == 0) { comps = order_stripe_components_alternate_hbas(comps); devconfig_set_components(*stripe, comps); } else { free_devconfig(*stripe); *stripe = NULL; } return (error); } /* * Order the given stripe component list such that the number of * slices on the same hba adjacent to each other in the list are * minimized. * * @param comps * the slice component list to order * * @return the first element of the resulting list */ static dlist_t * order_stripe_components_alternate_hbas( dlist_t *comps) { dlist_t *iter; oprintf(OUTPUT_DEBUG, gettext("Stripe components before ordering to alternate HBAs:\n")); for (iter = comps; iter != NULL; iter = iter->next) { devconfig_t *slice = (devconfig_t *)(iter->obj); char *name; devconfig_get_name(slice, &name); oprintf(OUTPUT_DEBUG, " %s\n", name); } return (dlist_separate_similar_elements( comps, compare_slices_on_same_hba)); } /* * FUNCTION: compute_usable_stripe_capacity(dlist_t *comps, uint64_t ilace, * uint64_t *nbytes) * * INPUT: comps - pointer to a list of stripe components * ilace - the expected stripe interlace in bytes * * OUPUT: nbytes - pointer to hold the computed capacity * * RETURNS: int - 0 on success * !0 otherwise. * * PURPOSE: Helper which computes the usable size of a stripe taking * into account the interlace and cylinder rounding that * libmeta uses: a stripe component's size is rounded down to * an integral multiple of the interlace and then rounded down * to a cylinder boundary on VTOC labeled disks. * * (These libmeta computations are in the meta_stripe_attach() * function of .../lib/lvm/libmeta/common/meta_stripe.c and * meta_adjust_geom() in .../lib/lvm/libmeta/common/meta_init.c) * * This function's implementation iterates the input list of * stripe component slices and determines the smallest usable * component capacity. * * The usable stripe capacity is then that component capacity * times the number of components. */ static int compute_usable_stripe_capacity( dlist_t *comps, uint64_t ilace, uint64_t *nbytes) { uint64_t bytes_per_component = 0; dlist_t *iter; int ncomps = 0; int error = 0; for (iter = comps; (iter != NULL) && (error == 0); iter = iter->next) { devconfig_t *comp = (devconfig_t *)iter->obj; char *comp_name = NULL; uint64_t comp_nbytes = 0; dm_descriptor_t comp_disk; boolean_t comp_disk_efi = B_FALSE; uint64_t comp_disk_bps = 0; /* disk bytes per sector */ ((error = devconfig_get_size(comp, &comp_nbytes)) != 0) || (error = devconfig_get_name(comp, &comp_name)) || (error = get_disk_for_named_slice(comp_name, &comp_disk)) || (error = disk_get_blocksize(comp_disk, &comp_disk_bps)) || (error = disk_get_is_efi(comp_disk, &comp_disk_efi)); if (error == 0) { if (comp_disk_efi == B_FALSE) { uint64_t nhead = 0; uint64_t nsect = 0; uint64_t ncyls = 0; /* do cylinder and interlace rounding for non-EFI disks */ ((error = disk_get_ncylinders(comp_disk, &ncyls)) != 0) || (error = disk_get_nheads(comp_disk, &nhead)) || (error = disk_get_nsectors(comp_disk, &nsect)); if (error == 0) { /* compute bytes per cyl */ uint64_t bpc = nhead * nsect * comp_disk_bps; /* round nbytes down to a multiple of interlace */ comp_nbytes = (comp_nbytes / ilace) * ilace; /* round nbytes down to a cylinder boundary */ comp_nbytes = (comp_nbytes / bpc) * bpc; } } /* save smallest component size */ if ((bytes_per_component == 0) || (comp_nbytes < bytes_per_component)) { bytes_per_component = comp_nbytes; } ++ncomps; } } if (error == 0) { /* size of stripe = smallest component size * n components */ *nbytes = (bytes_per_component * ncomps); } return (error); }