This patch adds support for SAS controller(s) built in Intel(R) Patsburg chipset. The patch is for IMSM metadata only. The patch maintains compatibility between Linux and Windows IMSM RAID stacks - Windows IMSM does not allow arrays to span on devices attached to different storage controllers. Signed-off-by: Artur Wojcik <artur.wojcik@xxxxxxxxx> --- platform-intel.c | 89 ++++++++-------- platform-intel.h | 12 ++ super-intel.c | 296 ++++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 287 insertions(+), 110 deletions(-) diff --git a/platform-intel.c b/platform-intel.c index 30f7914..31639c3 100644 --- a/platform-intel.c +++ b/platform-intel.c @@ -41,7 +41,7 @@ void free_sys_dev(struct sys_dev **list) } } -struct sys_dev *find_driver_devices(const char *bus, const char *driver) +static struct sys_dev *find_driver_devices(const char *bus, const char *driver) { /* search sysfs for devices driven by 'driver' */ char path[292]; @@ -51,6 +51,14 @@ struct sys_dev *find_driver_devices(const char *bus, const char *driver) struct dirent *de; struct sys_dev *head = NULL; struct sys_dev *list = NULL; + enum sys_dev_type type; + + if (strcmp(driver, "isci_mod") == 0) + type = SYS_DEV_SAS; + else if (strcmp(driver, "ahci") == 0) + type = SYS_DEV_SATA; + else + type = SYS_DEV_UNKNOWN; sprintf(path, "/sys/bus/%s/drivers/%s", bus, driver); driver_dir = opendir(path); @@ -74,6 +82,13 @@ struct sys_dev *find_driver_devices(const char *bus, const char *driver) if (strncmp(bus, c+1, strlen(bus)) != 0) continue; + sprintf(path, "/sys/bus/%s/drivers/%s/%s", + bus, driver, de->d_name); + + /* if it's not Intel device skip it. */ + if (devpath_to_vendor(path) != 0x8086) + continue; + /* start / add list entry */ if (!head) { head = malloc(sizeof(*head)); @@ -88,11 +103,11 @@ struct sys_dev *find_driver_devices(const char *bus, const char *driver) break; } - /* generate canonical path name for the device */ - sprintf(path, "/sys/bus/%s/drivers/%s/%s", - bus, driver, de->d_name); + list->type = type; list->path = canonicalize_file_name(path); list->next = NULL; + if ((list->pci_id = strrchr(list->path, '/')) != NULL) + list->pci_id++; } closedir(driver_dir); return head; @@ -122,23 +137,34 @@ __u16 devpath_to_vendor(const char *dev_path) return id; } -static int platform_has_intel_ahci(void) +struct sys_dev *find_intel_devices(void) { - struct sys_dev *devices = find_driver_devices("pci", "ahci"); - struct sys_dev *dev; - int ret = 0; - - for (dev = devices; dev; dev = dev->next) - if (devpath_to_vendor(dev->path) == 0x8086) { - ret = 1; - break; - } - - free_sys_dev(&devices); - - return ret; + struct sys_dev *ahci, *isci; + + isci = find_driver_devices("pci", "isci_mod"); + ahci = find_driver_devices("pci", "ahci"); + + if (!ahci) { + ahci = isci; + } else { + struct sys_dev *elem = ahci; + while (elem->next) + elem = elem->next; + elem->next = isci; + } + return ahci; } +static int platform_has_intel_devices(void) +{ + struct sys_dev *devices; + devices = find_intel_devices(); + if (devices) { + free_sys_dev(&devices); + return 1; + } + return 0; +} static struct imsm_orom imsm_orom; static int scan(const void *start, const void *end) @@ -185,7 +211,7 @@ const struct imsm_orom *find_imsm_orom(void) return &imsm_orom; } - if (!platform_has_intel_ahci()) + if (!platform_has_intel_devices()) return NULL; /* scan option-rom memory looking for an imsm signature */ @@ -212,20 +238,6 @@ char *devt_to_devpath(dev_t dev) return canonicalize_file_name(device); } -static char *diskfd_to_devpath(int fd) -{ - /* return the device path for a disk, return NULL on error or fd - * refers to a partition - */ - struct stat st; - - if (fstat(fd, &st) != 0) - return NULL; - if (!S_ISBLK(st.st_mode)) - return NULL; - - return devt_to_devpath(st.st_rdev); -} int path_attached_to_hba(const char *disk_path, const char *hba_path) { @@ -253,14 +265,3 @@ int devt_attached_to_hba(dev_t dev, const char *hba_path) return rc; } -int disk_attached_to_hba(int fd, const char *hba_path) -{ - char *disk_path = diskfd_to_devpath(fd); - int rc = path_attached_to_hba(disk_path, hba_path); - - if (disk_path) - free(disk_path); - - return rc; -} - diff --git a/platform-intel.h b/platform-intel.h index bbdc9f9..df6c78f 100644 --- a/platform-intel.h +++ b/platform-intel.h @@ -115,15 +115,23 @@ static inline int imsm_orom_has_chunk(const struct imsm_orom *orom, int chunk) return !!(orom->sss & (1 << (fs - 1))); } +enum sys_dev_type { + SYS_DEV_UNKNOWN = 0, + SYS_DEV_SAS, + SYS_DEV_SATA +}; + struct sys_dev { + enum sys_dev_type type; char *path; + char *pci_id; struct sys_dev *next; }; -struct sys_dev *find_driver_devices(const char *bus, const char *driver); +struct sys_dev *find_intel_devices(void); __u16 devpath_to_vendor(const char *dev_path); void free_sys_dev(struct sys_dev **list); const struct imsm_orom *find_imsm_orom(void); -int disk_attached_to_hba(int fd, const char *hba_path); char *devt_to_devpath(dev_t dev); int path_attached_to_hba(const char *disk_path, const char *hba_path); +const char *get_sys_dev_type(enum sys_dev_type); diff --git a/super-intel.c b/super-intel.c index 8e20a81..2331570 100644 --- a/super-intel.c +++ b/super-intel.c @@ -233,6 +233,13 @@ struct intel_dev { int index; }; +struct intel_hba { + enum sys_dev_type type; + char *path; + char *pci_id; + struct intel_hba *next; +}; + /* internal representation of IMSM metadata */ struct intel_super { union { @@ -263,7 +270,7 @@ struct intel_super { struct dl *add; /* list of disks to add while mdmon active */ struct dl *missing; /* disks removed while we weren't looking */ struct bbm_log *bbm_log; - const char *hba; /* device path of the raid controller for this metadata */ + struct intel_hba *hba; /* device path of the raid controller for this metadata */ const struct imsm_orom *orom; /* platform firmware support */ struct intel_super *next; /* (temp) list for disambiguating family_num */ }; @@ -308,6 +315,132 @@ struct imsm_update_add_disk { enum imsm_update_type type; }; +static const char *_sys_dev_type[] = { + [SYS_DEV_UNKNOWN] = "Unknown", + [SYS_DEV_SAS] = "SAS", + [SYS_DEV_SATA] = "SATA" +}; + +const char *get_sys_dev_type(enum sys_dev_type type) +{ + if (type < SYS_DEV_UNKNOWN) + type = SYS_DEV_UNKNOWN; + else if (type > SYS_DEV_SATA) + type = SYS_DEV_SATA; + + return _sys_dev_type[type]; +} + +static struct intel_hba * alloc_intel_hba(struct sys_dev *device) +{ + struct intel_hba *result = malloc(sizeof(*result)); + if (result) { + result->type = device->type; + result->path = strdup(device->path); + result->next = NULL; + if (result->path && (result->pci_id = strrchr(result->path, '/')) != NULL) + result->pci_id++; + } + return result; +} + +static struct intel_hba * find_intel_hba(struct intel_super *super, struct sys_dev *device) +{ + struct intel_hba *result; + for (result = super->hba; result; result = result->next) { + if (result->type == device->type && strcmp(result->path, device->path) == 0) + break; + } + return result; +} + +static char *diskfd_to_devpath(int fd) +{ + /* return the device path for a disk, return NULL on error or fd + * refers to a partition + */ + struct stat st; + + if (fstat(fd, &st) != 0) + return NULL; + if (!S_ISBLK(st.st_mode)) + return NULL; + + return devt_to_devpath(st.st_rdev); +} + +static int attach_hba_to_super(struct intel_super *super, struct sys_dev *device, + const char *devname) +{ + struct intel_hba *hba; + + /* Check if HBA is already attached to super */ + if ((hba = find_intel_hba(super, device)) != NULL) + return 1; + + if (super->hba == NULL) { + super->hba = alloc_intel_hba(device); + return 1; + } + + hba = super->hba; + if (device->type != hba->type) { + fprintf(stderr, Name ": %s is attached to Intel(R) %s RAID " + "controller (%s),\n but the container is assigned to Intel(R) " + "%s RAID controller (", + devname, + get_sys_dev_type(device->type), + device->pci_id ? : "Err!", + get_sys_dev_type(hba->type)); + + while (hba) { + fprintf(stderr, "%s", hba->pci_id ? : "Err!"); + if (hba->next) + fprintf(stderr, ", "); + hba = hba->next; + } + + fprintf(stderr, ").\n" + " Mixing devices attached to different controllers " + "is not allowed.\n"); + return 2; + } + while (hba->next) + hba = hba->next; + + hba->next = alloc_intel_hba(device); + return 1; +} + +static int disk_attached_to_hba(int fd, struct intel_super *super, const char *devname) +{ + int result = 0; + struct sys_dev *list, *elem; + char *disk_path; + + if ((list = find_intel_devices()) == NULL) + return 0; + + disk_path = diskfd_to_devpath(fd); + if (!disk_path) { + free_sys_dev(&list); + return 0; + } + + for (elem = list; elem; elem = elem->next) { + if (path_attached_to_hba(disk_path, elem->path)) + break; + } + + if (elem) + result = attach_hba_to_super(super, elem, devname); + + free(disk_path); + free_sys_dev(&list); + + return result; +} + static struct supertype *match_metadata_desc_imsm(char *arg) { struct supertype *st; @@ -863,10 +996,10 @@ static void brief_detail_super_imsm(struct supertype *st) static int imsm_read_serial(int fd, char *devname, __u8 *serial); static void fd2devname(int fd, char *name); -static int imsm_enumerate_ports(const char *hba_path, int port_count, int host_base, int verbose) +static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_base, int verbose) { - /* dump an unsorted list of devices attached to ahci, as well as - * non-connected ports + /* dump an unsorted list of devices attached to Intel storage + * controller, as well as non-connected ports */ int hba_len = strlen(hba_path) + 1; struct dirent *ent; @@ -1026,6 +1159,56 @@ static int imsm_enumerate_ports(const char *hba_path, int port_count, int host_b return err; } +static int isci_enumerate_devices(const char *hba_path, int verbose) +{ + (void)hba_path; + (void)verbose; + return 0; /* TBD */ +} + +static void print_found_intel_controllers(struct sys_dev *elem) +{ + for (; elem; elem = elem->next) { + fprintf(stderr, Name ": found Intel(R) "); + if (elem->type == SYS_DEV_SATA) + fprintf(stderr, "SATA "); + else if (elem->type == SYS_DEV_SAS) + fprintf(stderr, "SAS "); + fprintf(stderr, "RAID controller"); + if (elem->pci_id) + fprintf(stderr, " at %s", elem->pci_id); + fprintf(stderr, ".\n"); + } + fflush(stderr); +} + +static int ahci_get_port_count(const char *hba_path, int *port_count) +{ + struct dirent *ent; + DIR *dir; + int host_base = -1; + + *port_count = 0; + if ((dir = opendir(hba_path)) == NULL) + return -1; + + for (ent = readdir(dir); ent; ent = readdir(dir)) { + int host; + + if (sscanf(ent->d_name, "host%d", &host) != 1) + continue; + if (*port_count == 0) + host_base = host; + else if (host < host_base) + host_base = host; + + if (host + 1 > *port_count + host_base) + *port_count = host + 1 - host_base; + } + closedir(dir); + return host_base; +} + static int detail_platform_imsm(int verbose, int enumerate_only) { /* There are two components to imsm platform support, the ahci SATA @@ -1041,11 +1224,9 @@ static int detail_platform_imsm(int verbose, int enumerate_only) */ const struct imsm_orom *orom; struct sys_dev *list, *hba; - DIR *dir; - struct dirent *ent; - const char *hba_path; int host_base = 0; int port_count = 0; + int result = 0; if (enumerate_only) { if (check_env("IMSM_NO_PLATFORM") || find_imsm_orom()) @@ -1053,24 +1234,19 @@ static int detail_platform_imsm(int verbose, int enumerate_only) return 2; } - list = find_driver_devices("pci", "ahci"); - for (hba = list; hba; hba = hba->next) - if (devpath_to_vendor(hba->path) == 0x8086) - break; - - if (!hba) { + list = find_intel_devices(); + if (!list) { if (verbose) - fprintf(stderr, Name ": unable to find active ahci controller\n"); + fprintf(stderr, Name ": no active Intel(R) RAID " + "controller found.\n"); free_sys_dev(&list); return 2; } else if (verbose) - fprintf(stderr, Name ": found Intel SATA AHCI Controller\n"); - hba_path = hba->path; - hba->path = NULL; - free_sys_dev(&list); + print_found_intel_controllers(list); orom = find_imsm_orom(); if (!orom) { + free_sys_dev(&list); if (verbose) fprintf(stderr, Name ": imsm option-rom not found\n"); return 2; @@ -1104,35 +1280,31 @@ static int detail_platform_imsm(int verbose, int enumerate_only) imsm_orom_has_chunk(orom, 1024*64) ? " 64M" : ""); printf(" Max Disks : %d\n", orom->tds); printf(" Max Volumes : %d\n", orom->vpa); - printf(" I/O Controller : %s\n", hba_path); - - /* find the smallest scsi host number to determine a port number base */ - dir = opendir(hba_path); - for (ent = dir ? readdir(dir) : NULL; ent; ent = readdir(dir)) { - int host; - - if (sscanf(ent->d_name, "host%d", &host) != 1) - continue; - if (port_count == 0) - host_base = host; - else if (host < host_base) - host_base = host; - - if (host + 1 > port_count + host_base) - port_count = host + 1 - host_base; - - } - if (dir) - closedir(dir); - if (!port_count || imsm_enumerate_ports(hba_path, port_count, - host_base, verbose) != 0) { - if (verbose) - fprintf(stderr, Name ": failed to enumerate ports\n"); - return 2; + for (hba = list; hba; hba = hba->next) { + printf(" I/O Controller : %s (%s)\n", + hba->path, get_sys_dev_type(hba->type)); + + if (hba->type == SYS_DEV_SATA) { + host_base = ahci_get_port_count(hba->path, &port_count); + if (ahci_enumerate_ports(hba->path, port_count, host_base, verbose)) { + if (verbose) + fprintf(stderr, Name ": failed to enumerate " + "ports on SATA controller at %s.", hba->pci_id); + result |= 2; + } + } else if (hba->type == SYS_DEV_SAS) { + if (isci_enumerate_devices(hba->path, verbose)) { + if (verbose) + fprintf(stderr, Name ": failed to enumerate " + "devices on SAS controller at %s.", hba->pci_id); + result |= 2; + } + } } - return 0; + free_sys_dev(&list); + return result; } #endif @@ -2266,6 +2438,8 @@ static void free_imsm_disks(struct intel_super *super) /* free all the pieces hanging off of a super pointer */ static void __free_imsm(struct intel_super *super, int free_disks) { + struct intel_hba *elem, *next; + if (super->buf) { free(super->buf); super->buf = NULL; @@ -2273,10 +2447,15 @@ static void __free_imsm(struct intel_super *super, int free_disks) if (free_disks) free_imsm_disks(super); free_devlist(super); - if (super->hba) { - free((void *) super->hba); - super->hba = NULL; + elem = super->hba; + while (elem) { + if (elem->path) + free((void *)elem->path); + next = elem->next; + free(elem); + elem = next; } + super->hba = NULL; } static void free_imsm(struct intel_super *super) @@ -2307,20 +2486,6 @@ static struct intel_super *alloc_super(int creating_imsm) super->create_offset = ~((__u32 ) 0); if (!check_env("IMSM_NO_PLATFORM")) super->orom = find_imsm_orom(); - if (super->orom && !check_env("IMSM_TEST_OROM")) { - struct sys_dev *list, *ent; - - /* find the first intel ahci controller */ - list = find_driver_devices("pci", "ahci"); - for (ent = list; ent; ent = ent->next) - if (devpath_to_vendor(ent->path) == 0x8086) - break; - if (ent) { - super->hba = ent->path; - ent->path = NULL; - } - free_sys_dev(&list); - } } return super; @@ -3185,10 +3350,13 @@ static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk, /* if we are on an RAID enabled platform check that the disk is * attached to the raid controller */ - if (super->hba && !disk_attached_to_hba(fd, super->hba)) { + rv = disk_attached_to_hba(fd, super, devname); + switch (rv) { + case 0: fprintf(stderr, - Name ": %s is not attached to the raid controller: %s\n", - devname ? : "disk", super->hba); + Name ": %s is not attached to Intel(R) RAID controller.\n", + devname ? : "disk"); + case 2: return 1; } -- To unsubscribe from this list: send the line "unsubscribe linux-raid" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html