/* * High-level interface to adapter information. * * Copyright (c) 2007 by Jefferson Ogata */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "megaioctl.h" #include "logpage.h" #include "ntrim.h" #include #include #include #include #include static void batteryStatus (struct adapter_config *a, uint8_t status) { a->battery.module_missing = (status & BATTERY_MODULE_MISSING) != 0; a->battery.pack_missing = (status & BATTERY_PACK_MISSING) != 0; a->battery.low_voltage = (status & BATTERY_LOW_VOLTAGE) != 0; a->battery.high_temperature = (status & BATTERY_TEMP_HIGH) != 0; a->battery.cycles_exceeded = (status & BATTERY_CYCLES_EXCEEDED) != 0; switch (status & BATTERY_CHARGE_MASK) { case BATTERY_CHARGE_FAIL: a->battery.charger_state = ChargerStateFailed; break; case BATTERY_CHARGE_DONE: a->battery.charger_state = ChargerStateComplete; break; case BATTERY_CHARGE_INPROG: a->battery.charger_state = ChargerStateInProgress; break; default: a->battery.charger_state = ChargerStateUnknown; break; } a->battery.voltage = -1; a->battery.temperature = -1; a->battery.healthy = !(a->battery.module_missing || a->battery.pack_missing || a->battery.low_voltage || a->battery.high_temperature || a->battery.cycles_exceeded || (a->battery.charger_state != ChargerStateComplete)); } static void batteryStatus5 (struct adapter_config *a) { struct mega_battery_state_sas *b = &a->q.v5.battery.state; a->battery.module_missing = !(a->q.v5.adapinfo.hw_present.bbu); a->battery.pack_missing = b->type == MEGA_BATTERY_TYPE_NONE; /*a->battery.low_voltage = b->remaining_capacity_alarm || b->remaining_time_alarm || b->fully_discharged;*/ a->battery.low_voltage = !b->fully_charged && (b->remaining_time_alarm || b->remaining_capacity_alarm || b->fully_discharged) ; a->battery.high_temperature = b->over_temperature != 0; a->battery.over_charged = b->over_charged != 0; switch (b->charger_status) { case 0: a->battery.charger_state = ChargerStateFailed; break; case 1: a->battery.charger_state = ChargerStateComplete; break; case 2: a->battery.charger_state = ChargerStateInProgress; break; default: a->battery.charger_state = ChargerStateUnknown; break; } a->battery.voltage = b->voltage; a->battery.temperature = b->temperature; a->battery.healthy = !(a->battery.module_missing || a->battery.pack_missing || a->battery.low_voltage || a->battery.high_temperature /*|| a->battery.cycles_exceeded */|| (a->battery.charger_state != ChargerStateComplete) || (!b->health)); } static struct log_page_list *getPage (struct physical_drive_info *d, uint8_t page) { struct log_page_list *p; if ((p = (struct log_page_list *) malloc (sizeof (*p))) == NULL) return NULL; memset (p, 0, sizeof (*p)); if (megaScsiLogSense (&d->adapter->target, d->target, &p->buf, sizeof (p->buf), 1, page, 0) < 0) { free (p); return NULL; } if (parseLogPage (&p->buf, sizeof (p->buf), &p->log) < 0) { free (p); return NULL; } return p; } struct log_page_list *getDriveLogPage (struct physical_drive_info *d, uint8_t page) { struct supportedLogsPage *supported = NULL; struct log_page_list *p; for (p = d->log; p; p = p->next) { if (p->log.h.page_code == page) return p; if (p->log.h.page_code == 0) supported = &p->log.u.supported; } if (supported == NULL) { if ((p = getPage (d, 0)) == NULL) return NULL; p->next = d->log; d->log = p; if (page == 0) return p; supported = &p->log.u.supported; } /* Is the requested page supported? */ if (supported->page[page] == 0) return NULL; if ((p = getPage (d, page)) == NULL) return NULL; p->next = d->log; d->log = p; return p; } static int cmpPhysical (const void *a, const void *b) { struct physical_drive_info *x = *((struct physical_drive_info **) a); struct physical_drive_info *y = *((struct physical_drive_info **) b); if (x->adapter->target.adapno != y->adapter->target.adapno) return (int) (x->adapter->target.adapno) - (int) (y->adapter->target.adapno); if (x->channel != y->channel) return (int) (x->channel) - (int) (y->channel); if (x->id != y->id) return (int) (x->id) - (int) (y->id); return 0; } struct physical_drive_info *getPhysicalDriveInfo (struct adapter_config *a, uint16_t target, int fetch) { int k; struct physical_drive_info *d; /* Look for it. */ for (k = 0, d = a->physical; k < a->num_physicals; ++k, ++d) { if (d->adapter == NULL) break; if (d->target == target) return d->present ? d : NULL; } /* Not there and no place for it. That's just wrong. */ if (k >= a->num_physicals) { fprintf (stderr, "me so crazy, me think adapter crazy too. sorry, mister.\n"); return NULL; } /* If we don't want to query it, we're done. */ if (!fetch) return NULL; d->adapter = a; d->target = target; if (a->is_sas) { struct mega_physical_disk_info_sas *info = &d->q.v5.info; if (megaSasGetDiskInfo (&a->target, target, info) < 0) { d->error_string = megaErrorString (); d->present = 0; return NULL; } d->channel = info->enclosure; d->id = info->slot; if (d->channel == DISK_NOENC) snprintf (d->name, sizeof (d->name), "%se*s%u", a->name, d->id); else snprintf (d->name, sizeof (d->name), "%se%us%u", a->name, d->channel, d->id); d->inquiry = info->inquiry.inq; strncpy (d->vendor, d->inquiry.vendor_info, sizeof (d->vendor) - 1); d->vendor[sizeof (d->vendor) - 1] = '\0'; ntrim (d->vendor); strncpy (d->model, d->inquiry.prod_ident, sizeof (d->model) - 1); d->model[sizeof (d->model) - 1] = '\0'; ntrim (d->model); strncpy (d->revision, d->inquiry.prod_revision, sizeof (d->revision) - 1); d->revision[sizeof (d->revision) - 1] = '\0'; ntrim (d->revision); if ((d->inquiry.qualifier == INQ_DEV_PRESENT) && (d->inquiry.type == INQ_DASD)) { d->present = 1; } else { d->present = 0; return NULL; } strncpy (d->serial, (char *) info->inquiry.buf + sizeof (info->inquiry.inq), sizeof (d->serial)); d->serial[sizeof (d->serial) - 1] = '\0'; ntrim (d->serial); if (info->configured) { if (info->online) d->state = PdStateOnline; else if (info->rebuild) d->state = PdStateRebuild; else if (info->failure) d->state = PdStateFailed; else d->state = PdStateUnknown; } else { if (info->hotspare) d->state = PdStateHotspare; else if (info->failure) d->state = PdStateUnconfiguredBad; else d->state = PdStateUnconfiguredGood; } d->blocks = info->raw_size; d->media_errors = info->media_errors; d->other_errors = info->other_errors; d->predictive_failures = info->predictive_failures; } else { int status; struct scsi_inquiry inq; uint8_t evpd[128]; struct mega_physical_drive_error_info errors; d->channel = (target >> 4) & 0xf; d->id = target & 0xf; snprintf (d->name, sizeof (d->name), "%sc%ut%u", a->name, d->channel, d->id); if (megaScsiDriveInquiry (&a->target, target, &inq, sizeof (inq), 0, 0) == 0) { d->inquiry = inq; strncpy (d->vendor, d->inquiry.vendor_info, sizeof (d->vendor) - 1); d->vendor[sizeof (d->vendor) - 1] = '\0'; ntrim (d->vendor); strncpy (d->model, d->inquiry.prod_ident, sizeof (d->model) - 1); d->model[sizeof (d->model) - 1] = '\0'; ntrim (d->model); strncpy (d->revision, d->inquiry.prod_revision, sizeof (d->revision) - 1); d->revision[sizeof (d->revision) - 1] = '\0'; ntrim (d->revision); if ((d->inquiry.qualifier == INQ_DEV_PRESENT) && (d->inquiry.type == INQ_DASD)) { d->present = 1; } else { d->present = 0; return NULL; } } else { d->error_string = megaErrorString (); d->present = 0; return NULL; } if (megaScsiDriveInquiry (&a->target, target, evpd, sizeof evpd, 0x80, 1) == 0) { uint8_t len = evpd[3]; if ((evpd[1] == 0x80) && (len + 4 <= sizeof evpd)) { if (len > sizeof (d->serial) - 1) len = sizeof (d->serial) - 1; strncpy (d->serial, (char *) evpd + 4, len); d->serial[len] = '\0'; ntrim (d->serial); } } if ((status = megaGetDriveErrorCount (&a->target, target, &errors)) == 0) { d->media_errors = errors.media; d->other_errors = errors.other; } else d->error_string = megaErrorString (); } /* Add it to the device list and sort it. */ for (k = 0; k < a->num_physicals; ++k) if (a->physical_list[k] == NULL) break; if (k >= a->num_physicals) { fprintf (stderr, "not ok at the ok corral. freak out, mama!\n"); return NULL; } a->physical_list[k++] = d; qsort (a->physical_list, k, sizeof (*a->physical_list), cmpPhysical); return d; } /* Adapter handling for PERC2. */ static char *getAdapterConfig2 (struct adapter_config *a) { int k; logdrv_8ld_span8_t *ml; int spanIndex; mraid_adapinfo1_t *pinfo = &a->q.v2.inquiry.adapter_info; mraid_inquiry1_t *inquiry = &a->q.v2.inquiry; disk_array_8ld_span8_t *config = &a->q.v2.config; a->target.type = MEGA_ADAPTER_V2; if (megaGetAdapterInquiry (&a->target, inquiry) < 0) return "cannot query adapter"; if (megaGetAdapterConfig8 (&a->target, config) < 0) return "cannot read adapter config"; if (megaGetPredictiveMap (&a->target, &a->q.v2.map) < 0) return "cannot read adapter predictive map"; a->rebuild_rate = pinfo->rebuild_rate; a->dram_size = pinfo->dram_size; snprintf (a->name, sizeof (a->name), "a%u", a->target.adapno); strcpy (a->product, "PERC2/"); switch (pinfo->nchannels) { case 1: strcat (a->product, "SC"); break; case 2: strcat (a->product, "DC"); break; case 4: strcat (a->product, "QC"); break; default: return "invalid number of channels"; } strncpy (a->bios, (char *) pinfo->bios_version, sizeof (a->bios)); a->bios[sizeof (a->bios) - 1] = '\0'; ntrim (a->bios); strncpy (a->firmware, (char *) pinfo->fw_version, sizeof (a->firmware)); a->firmware[sizeof (a->firmware) - 1] = '\0'; ntrim (a->firmware); batteryStatus (a, pinfo->battery_status); if (config->numldrv > sizeof (config->ldrv) / sizeof (config->ldrv[0])) return "invalid number of logical drives"; a->num_channels = pinfo->nchannels; if ((a->channel = (uint16_t *) malloc (a->num_channels * sizeof (*a->channel))) == NULL) return "out of memory (channels)"; for (k = 0; k < a->num_channels; ++k) a->channel[k] = k; a->num_physicals = FC_MAX_PHYSICAL_DEVICES; if ((a->physical = (struct physical_drive_info *) malloc (a->num_physicals * sizeof (*a->physical))) == NULL) return "out of memory (physical drives)"; memset (a->physical, 0, a->num_physicals * sizeof (*a->physical)); if ((a->physical_list = (struct physical_drive_info **) malloc (a->num_physicals * sizeof (*a->physical_list))) == NULL) return "out of memory (physical drives)"; memset (a->physical_list, 0, a->num_physicals * sizeof (*a->physical_list)); a->num_logicals = config->numldrv; if ((a->logical = (struct logical_drive_info *) malloc (a->num_logicals * sizeof (*a->logical))) == NULL) return "out of memory (logical drives)"; memset (a->logical, 0, a->num_logicals * sizeof (*a->logical)); /* Count how many spans there are. */ for (k = 0, ml = config->ldrv, a->num_spans = 0; k < config->numldrv; ++k, ++ml) a->num_spans += ml->lparam.span_depth; if ((a->span = (struct span_info *) malloc (a->num_spans * sizeof (*a->span))) == NULL) return "out of memory (spans)"; memset (a->span, 0, a->num_spans * sizeof (*a->span)); /* Copy drive states. */ for (k = 0; k < sizeof (inquiry->pdrv_info.pdrv_state) / sizeof (inquiry->pdrv_info.pdrv_state[0]); ++k) switch (inquiry->pdrv_info.pdrv_state[k] & 0xf) { case PDRV_UNCNF: a->physical[k].state = PdStateUnconfiguredGood; continue; case PDRV_ONLINE: a->physical[k].state = PdStateOnline; continue; case PDRV_FAILED: a->physical[k].state = PdStateFailed; continue; case PDRV_RBLD: a->physical[k].state = PdStateRebuild; continue; case PDRV_HOTSPARE: a->physical[k].state = PdStateHotspare; continue; default: a->physical[k].state = PdStateUnknown; continue; } /* Copy drive sizes. */ for (k = 0; k < sizeof (config->pdrv) / sizeof (config->pdrv[0]); ++k) a->physical[k].blocks = config->pdrv[k].size; /* Copy drive predictive failures flag */ for (k = 0; k < 8 * sizeof (a->q.v2.map.map) / sizeof (a->q.v2.map.map[0]); ++k) a->physical[k].predictive_failures = ((a->q.v2.map.map[k >> 3] & (1 << (k & 0x7))) != 0); /* Examine all the logical drives. */ for (k = 0, ml = config->ldrv, spanIndex = 0; k < config->numldrv; ++k, ++ml) { struct span_info *span; adap_span_8ld_t *mr; int j; struct logical_drive_info *l = &a->logical[k]; l->adapter = a; snprintf (l->name, sizeof (l->name), "a%ud%u", a->target.adapno, k); l->target = k; switch (ml->lparam.status) { case RDRV_OFFLINE: l->state = LdStateOffline; break; case RDRV_DEGRADED: l->state = LdStateDegraded; break; case RDRV_OPTIMAL: l->state = LdStateOptimal; break; case RDRV_DELETED: l->state = LdStateDeleted; break; default: l->state = LdStateUnknown; break; } l->raid_level = ml->lparam.level; l->span_size = ml->lparam.row_size; l->num_spans = ml->lparam.span_depth; if ((l->span = (struct span_reference *) malloc (l->num_spans * sizeof (*l->span))) == NULL) return "out of memory (span references)"; for (j = 0, mr = ml->span; j < ml->lparam.span_depth; ++j, ++mr) { int i; span = &a->span[spanIndex++]; span->adapter = a; span->num_logical_drives = 1; if ((span->logical_drive = (struct logical_drive_info **) malloc (span->num_logical_drives * sizeof (*span->logical_drive))) == NULL) return "out of memory (span -> ldrv pointers)"; span->logical_drive[0] = l; span->blocks_per_disk = mr->num_blks; span->num_disks = ml->lparam.row_size; if ((span->disk = (struct physical_drive_info **) malloc (span->num_disks * sizeof (*span->disk))) == NULL) return "out of memory (span -> disk pointers)"; /* Logical drives use the whole span. */ l->span[j].offset = 0; l->span[j].blocks_per_disk = span->blocks_per_disk; l->span[j].span = span; for (i = 0; i < span->num_disks; ++i) { span->disk[i] = &a->physical[mr->device[i].target]; span->disk[i]->span = span; } } } return NULL; } /* Adapter handling for PERC3 and PERC4 adapters. */ static char *getAdapterConfig3 (struct adapter_config *a) { int k; logdrv_40ld_t *ml; int spanIndex; mraid_pinfo_t *pinfo = &a->q.v3.adapinfo; mraid_inquiry3_t *enquiry3 = &a->q.v3.enquiry3; disk_array_40ld_t *config = &a->q.v3.config; a->target.type = MEGA_ADAPTER_V34; if (megaGetAdapterEnquiry3 (&a->target, &a->q.v3.enquiry3) < 0) return "cannot query adapter"; if (megaGetAdapterConfig40 (&a->target, config) < 0) return "cannot read adapter config"; if (megaGetPredictiveMap (&a->target, &a->q.v3.map) < 0) return "cannot read adapter predictive map"; a->rebuild_rate = enquiry3->rebuild_rate; a->dram_size = pinfo->dram_size; snprintf (a->name, sizeof (a->name), "a%u", a->target.adapno); switch (pinfo->nchannels) { case 1: break; case 2: break; case 4: break; default: return "invalid number of channels"; } strncpy (a->product, (char *) pinfo->product_name, sizeof (a->product)); a->product[sizeof (a->product) - 1] = '\0'; ntrim (a->product); strncpy (a->bios, (char *) pinfo->bios_version, sizeof (a->bios)); a->bios[sizeof (a->bios) - 1] = '\0'; ntrim (a->bios); strncpy (a->firmware, (char *) pinfo->fw_version, sizeof (a->firmware)); a->firmware[sizeof (a->firmware) - 1] = '\0'; ntrim (a->firmware); batteryStatus (a, enquiry3->battery_status); if (config->numldrv > sizeof (config->ldrv) / sizeof (config->ldrv[0])) return "invalid number of logical drives"; a->num_channels = pinfo->nchannels; if ((a->channel = (uint16_t *) malloc (a->num_channels * sizeof (*a->channel))) == NULL) return "out of memory (channels)"; for (k = 0; k < a->num_channels; ++k) a->channel[k] = k; a->num_physicals = FC_MAX_PHYSICAL_DEVICES; if ((a->physical = (struct physical_drive_info *) malloc (a->num_physicals * sizeof (*a->physical))) == NULL) return "out of memory (physical drives)"; memset (a->physical, 0, a->num_physicals * sizeof (*a->physical)); if ((a->physical_list = (struct physical_drive_info **) malloc (a->num_physicals * sizeof (*a->physical_list))) == NULL) return "out of memory (physical drives)"; memset (a->physical_list, 0, a->num_physicals * sizeof (*a->physical_list)); a->num_logicals = config->numldrv; if ((a->logical = (struct logical_drive_info *) malloc (a->num_logicals * sizeof (*a->logical))) == NULL) return "out of memory (logical drives)"; memset (a->logical, 0, a->num_logicals * sizeof (*a->logical)); /* Count how many spans there are. */ for (k = 0, ml = config->ldrv, a->num_spans = 0; k < config->numldrv; ++k, ++ml) a->num_spans += ml->lparam.span_depth; if ((a->span = (struct span_info *) malloc (a->num_spans * sizeof (*a->span))) == NULL) return "out of memory (spans)"; memset (a->span, 0, a->num_spans * sizeof (*a->span)); /* Copy drive states. */ for (k = 0; k < sizeof (enquiry3->pdrv_state) / sizeof (enquiry3->pdrv_state[0]); ++k) switch (enquiry3->pdrv_state[k] & 0xf) { case PDRV_UNCNF: a->physical[k].state = PdStateUnconfiguredGood; continue; case PDRV_ONLINE: a->physical[k].state = PdStateOnline; continue; case PDRV_FAILED: a->physical[k].state = PdStateFailed; continue; case PDRV_RBLD: a->physical[k].state = PdStateRebuild; continue; case PDRV_HOTSPARE: a->physical[k].state = PdStateHotspare; continue; default: a->physical[k].state = PdStateUnknown; continue; } /* Copy drive sizes. */ for (k = 0; k < sizeof (config->pdrv) / sizeof (config->pdrv[0]); ++k) a->physical[k].blocks = config->pdrv[k].size; /* Copy drive predictive failures flag */ for (k = 0; k < 8 * sizeof (a->q.v3.map.map) / sizeof (a->q.v3.map.map[0]); ++k) a->physical[k].predictive_failures = ((a->q.v3.map.map[k >> 3] & (1 << (k & 0x7))) != 0); /* Examine all the logical drives. */ for (k = 0, ml = config->ldrv, spanIndex = 0; k < config->numldrv; ++k, ++ml) { struct span_info *span; adap_span_40ld_t *mr; int j; struct logical_drive_info *l = &a->logical[k]; l->adapter = a; snprintf (l->name, sizeof (l->name), "a%ud%u", a->target.adapno, k); l->target = k; switch (ml->lparam.status) { case RDRV_OFFLINE: l->state = LdStateOffline; break; case RDRV_DEGRADED: l->state = LdStateDegraded; break; case RDRV_OPTIMAL: l->state = LdStateOptimal; break; case RDRV_DELETED: l->state = LdStateDeleted; break; default: l->state = LdStateUnknown; break; } l->raid_level = ml->lparam.level; l->span_size = ml->lparam.row_size; l->num_spans = ml->lparam.span_depth; if ((l->span = (struct span_reference *) malloc (l->num_spans * sizeof (*l->span))) == NULL) return "out of memory (span references)"; for (j = 0, mr = ml->span; j < ml->lparam.span_depth; ++j, ++mr) { int i; span = &a->span[spanIndex++]; span->adapter = a; span->num_logical_drives = 1; if ((span->logical_drive = (struct logical_drive_info **) malloc (span->num_logical_drives * sizeof (*span->logical_drive))) == NULL) return "out of memory (span -> ldrv pointers)"; span->logical_drive[0] = l; span->blocks_per_disk = mr->num_blks; span->num_disks = ml->lparam.row_size; if ((span->disk = (struct physical_drive_info **) malloc (span->num_disks * sizeof (*span->disk))) == NULL) return "out of memory (span -> disk pointers)"; /* Logical drives use the whole span. */ l->span[j].offset = 0; l->span[j].blocks_per_disk = span->blocks_per_disk; l->span[j].span = span; for (i = 0; i < span->num_disks; ++i) { span->disk[i] = &a->physical[mr->device[i].target]; span->disk[i]->span = span; } } } #if 0 /* Go ahead and hit all the other devices that have a non-zero scsi transfer rate. */ for (k = 0; k < sizeof (a->q.v3.enquiry3.targ_xfer) / sizeof (a->q.v3.enquiry3.targ_xfer[0]); ++k) if (a->q.v3.enquiry3.targ_xfer[k]) (void) getPhysicalDriveInfo (a, (uint8_t) k, 1); #endif return NULL; } static int cmpChannel (const void *a, const void *b) { int x = (int) *((uint8_t *) a); int y = (int) *((uint8_t *) b); return x - y; } /* Adapter handling for PERC5 adapters. */ static char *getAdapterConfig5 (struct adapter_config *a) { int k; struct mega_array_span_def_sas *ms; struct mega_array_disk_def_sas *ml; struct megasas_ctrl_info *pinfo = &a->q.v5.adapinfo; struct mega_device_list_sas *device; struct mega_array_config_sas *config = &a->q.v5.config; a->target.type = MEGA_ADAPTER_V5; if (megaSasGetDeviceList (&a->target, &(a->q.v5.device)) < 0) return "cannot retrieve device list"; device = a->q.v5.device; if (megaSasGetArrayConfig (&a->target, &(a->q.v5.config)) < 0) return "cannot retrieve array configuration"; if (megaSasGetBatteryInfo (&a->target, &(a->q.v5.battery)) < 0) return "cannot retrieve battery info"; a->rebuild_rate = pinfo->properties.rebuild_rate; a->dram_size = pinfo->memory_size; snprintf (a->name, sizeof (a->name), "a%u", a->target.adapno); strncpy (a->product, (char *) pinfo->product_name, sizeof (a->product)); a->product[sizeof (a->product) - 1] = '\0'; ntrim (a->product); for (k = 0; k < pinfo->image_component_count; ++k) { if (!strcmp (pinfo->image_component[k].name, "BIOS")) { strncpy (a->bios, pinfo->image_component[k].version, sizeof (a->bios)); a->bios[sizeof (a->bios) - 1] = '\0'; ntrim (a->bios); } else if (!strcmp (pinfo->image_component[k].name, "APP ")) { strncpy (a->firmware, pinfo->image_component[k].version, sizeof (a->firmware)); a->firmware[sizeof (a->firmware) - 1] = '\0'; ntrim (a->firmware); } } batteryStatus5 (a); /* Build enclosure map. */ for (k = 0, a->num_channels = 0, a->channel = NULL; k < device->num_devices; ++k) { int j; for (j = 0; j < a->num_channels; ++j) if (device->device[k].enclosure == a->channel[j]) break; if (j < a->num_channels) continue; /* Didn't find this enclosure; extend the map */ ++a->num_channels; if ((a->channel = (uint16_t *) realloc (a->channel, a->num_channels * sizeof (*a->channel))) == NULL) return "out of memory (channels)"; a->channel[a->num_channels - 1] = device->device[k].enclosure; } qsort (a->channel, a->num_channels, sizeof (*a->channel), cmpChannel); /* Some notes: Different meanings on different models. - FC_MAX_PHYSICAL_DEVICES used on older controllers, which is 256 disks (overallocation) - pd_disk_present_count is number of working drives, not counting missing drives - pd_present_count is unclear. It is pd_disk_present_count + 1 on some controllers - device_interface.port_count contains number of physical ports on the controller pd_present_count was used here, but in some controllers causes segfaults when there is a failed drive, and not enough space is allocated. Since there cannot be more devices than there are ports, that is a safe number to set without going overboard. */ a->num_physicals = pinfo->device_interface.port_count; /* On some controllers, namely the PERC6e, the controller does not know how many ports there are in the enclosure. Fall back to the worst case scenario. */ if (a->num_physicals < pinfo->pd_disk_present_count) a->num_physicals = FC_MAX_PHYSICAL_DEVICES; if ((a->physical = (struct physical_drive_info *) malloc (a->num_physicals * sizeof (*a->physical))) == NULL) return "out of memory (physical drives)"; memset (a->physical, 0, a->num_physicals * sizeof (*a->physical)); if ((a->physical_list = (struct physical_drive_info **) malloc (a->num_physicals * sizeof (*a->physical_list))) == NULL) return "out of memory (physical drives)"; memset (a->physical_list, 0, a->num_physicals * sizeof (*a->physical_list)); a->num_logicals = config->header->num_disk_defs; if ((a->logical = (struct logical_drive_info *) malloc (a->num_logicals * sizeof (*a->logical))) == NULL) return "out of memory (logical drives)"; memset (a->logical, 0, a->num_logicals * sizeof (*a->logical)); a->num_spans = config->header->num_span_defs; if ((a->span = (struct span_info *) malloc (a->num_spans * sizeof (*a->span))) == NULL) return "out of memory (spans)"; memset (a->span, 0, a->num_spans * sizeof (*a->span)); /* Get drive info. (This is fast on a PERC5.) */ for (k = 0; k < device->num_devices; ++k) if ((device->device[k].type == INQ_DASD) && (getPhysicalDriveInfo (a, device->device[k].device_id, 1) == NULL)) return "cannot get physical device info"; /* Examine all the spans. */ for (k = 0, ms = config->span; k < config->header->num_span_defs; ++k, ++ms) { struct span_info *span = &a->span[k]; int i; span->adapter = a; span->num_logical_drives = 0; span->logical_drive = NULL; span->blocks_per_disk = ms->sectors_per_disk; span->num_disks = ms->span_size; if ((span->disk = (struct physical_drive_info **) malloc (span->num_disks * sizeof (*span->disk))) == NULL) return "out of memory (span -> disk pointers)"; for (i = 0; i < span->num_disks; ++i) { span->disk[i] = getPhysicalDriveInfo (a, ms->disk[i].device_id, 1); span->disk[i]->span = span; } } /* Examine all the logical drives. */ for (k = 0, ml = config->disk; k < config->header->num_disk_defs; ++k, ++ml) { struct span_info *span; struct mega_array_disk_entry_sas *mr; int j; struct logical_drive_info *l = &a->logical[k]; l->adapter = a; snprintf (l->name, sizeof (l->name), "a%ud%u", a->target.adapno, k); l->target = k; switch (ml->state) { case MEGA_SAS_LD_OFFLINE: l->state = LdStateOffline; break; case MEGA_SAS_LD_PARTIALLY_DEGRADED: l->state = LdStatePartiallyDegraded; break; case MEGA_SAS_LD_DEGRADED: l->state = LdStateDegraded; break; case MEGA_SAS_LD_OPTIMAL: l->state = LdStateOptimal; break; default: l->state = LdStateUnknown; break; } l->raid_level = ml->raid_level; l->span_size = ml->disks_per_span; l->num_spans = ml->num_spans; if ((l->span = (struct span_reference *) malloc (l->num_spans * sizeof (*l->span))) == NULL) return "out of memory (span references)"; for (j = 0, mr = ml->span; j < ml->num_spans; ++j, ++mr) { span = &a->span[mr->span_index]; ++(span->num_logical_drives); if ((span->logical_drive = (struct logical_drive_info **) realloc (span->logical_drive, span->num_logical_drives * sizeof (*span->logical_drive))) == NULL) return "out of memory (span -> ldrv pointers)"; span->logical_drive[span->num_logical_drives - 1] = l; l->span[j].offset = mr->offset; l->span[j].blocks_per_disk = mr->sectors_per_disk; l->span[j].span = span; } } return NULL; } struct adapter_config *getAdapterConfig (int fd, uint8_t adapno, int sas) { static struct adapter_config *cf = NULL; struct adapter_config *a; char *status; for (a = cf; a; a = a->next) if ((a->target.adapno == adapno) && (a->is_sas == sas)) return a; if ((a = (struct adapter_config *) malloc (sizeof (*a))) == NULL) return NULL; memset (a, 0, sizeof (*a)); a->target.fd = fd; a->target.adapno = adapno; a->is_sas = sas; if (sas) { if (megaSasGetAdapterProductInfo (fd, adapno, &a->q.v5.adapinfo) < 0) return NULL; status = getAdapterConfig5 (a); } else { mraid_pinfo_t pinfo; if (megaGetAdapterProductInfo (fd, adapno, &pinfo) < 0) return NULL; if (pinfo.data_size == 0) status = getAdapterConfig2 (a); else { a->q.v3.adapinfo = pinfo; status = getAdapterConfig3 (a); } } if (status) { free (a); fprintf (stderr, "adapter %d: %s\n", adapno, status); return NULL; } a->next = cf; cf = a; return a; }