platform-intel.c (mdadm-4.1) | : | platform-intel.c (mdadm-4.2) | ||
---|---|---|---|---|
skipping to change at line 33 | skipping to change at line 33 | |||
#include <stdlib.h> | #include <stdlib.h> | |||
#include <string.h> | #include <string.h> | |||
#include <unistd.h> | #include <unistd.h> | |||
#include <dirent.h> | #include <dirent.h> | |||
#include <fcntl.h> | #include <fcntl.h> | |||
#include <sys/mman.h> | #include <sys/mman.h> | |||
#include <sys/types.h> | #include <sys/types.h> | |||
#include <sys/stat.h> | #include <sys/stat.h> | |||
#include <limits.h> | #include <limits.h> | |||
#define NVME_SUBSYS_PATH "/sys/devices/virtual/nvme-subsystem/" | ||||
static int devpath_to_ll(const char *dev_path, const char *entry, | static int devpath_to_ll(const char *dev_path, const char *entry, | |||
unsigned long long *val); | unsigned long long *val); | |||
static void free_sys_dev(struct sys_dev **list) | static void free_sys_dev(struct sys_dev **list) | |||
{ | { | |||
while (*list) { | while (*list) { | |||
struct sys_dev *next = (*list)->next; | struct sys_dev *next = (*list)->next; | |||
if ((*list)->path) | if ((*list)->path) | |||
free((*list)->path); | free((*list)->path); | |||
skipping to change at line 240 | skipping to change at line 242 | |||
n = read(fd, vendor, sizeof(vendor)); | n = read(fd, vendor, sizeof(vendor)); | |||
if (n == sizeof(vendor)) { | if (n == sizeof(vendor)) { | |||
vendor[n - 1] = '\0'; | vendor[n - 1] = '\0'; | |||
id = strtoul(vendor, NULL, 16); | id = strtoul(vendor, NULL, 16); | |||
} | } | |||
close(fd); | close(fd); | |||
return id; | return id; | |||
} | } | |||
/* Description: Read text value of dev_path/entry field | ||||
* Parameters: | ||||
* dev_path - sysfs path to the device | ||||
* entry - entry to be read | ||||
* buf - buffer for read value | ||||
* len - size of buf | ||||
* verbose - error logging level | ||||
*/ | ||||
int devpath_to_char(const char *dev_path, const char *entry, char *buf, int len, | ||||
int verbose) | ||||
{ | ||||
char path[PATH_MAX]; | ||||
snprintf(path, sizeof(path), "%s/%s", dev_path, entry); | ||||
if (load_sys(path, buf, len)) { | ||||
if (verbose) | ||||
pr_err("Cannot read %s, aborting\n", path); | ||||
return 1; | ||||
} | ||||
return 0; | ||||
} | ||||
struct sys_dev *find_intel_devices(void) | struct sys_dev *find_intel_devices(void) | |||
{ | { | |||
struct sys_dev *ahci, *isci, *nvme; | struct sys_dev *ahci, *isci, *nvme; | |||
if (valid_time > time(0) - 10) | if (valid_time > time(0) - 10) | |||
return intel_devices; | return intel_devices; | |||
if (intel_devices) | if (intel_devices) | |||
free_sys_dev(&intel_devices); | free_sys_dev(&intel_devices); | |||
skipping to change at line 489 | skipping to change at line 514 | |||
{{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ | {{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ | |||
(b) & 0xff, ((b) >> 8) & 0xff, \ | (b) & 0xff, ((b) >> 8) & 0xff, \ | |||
(c) & 0xff, ((c) >> 8) & 0xff, \ | (c) & 0xff, ((c) >> 8) & 0xff, \ | |||
(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }}) | (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }}) | |||
#define SYS_EFI_VAR_PATH "/sys/firmware/efi/vars" | #define SYS_EFI_VAR_PATH "/sys/firmware/efi/vars" | |||
#define SYS_EFIVARS_PATH "/sys/firmware/efi/efivars" | #define SYS_EFIVARS_PATH "/sys/firmware/efi/efivars" | |||
#define SCU_PROP "RstScuV" | #define SCU_PROP "RstScuV" | |||
#define AHCI_PROP "RstSataV" | #define AHCI_PROP "RstSataV" | |||
#define AHCI_SSATA_PROP "RstsSatV" | #define AHCI_SSATA_PROP "RstsSatV" | |||
#define AHCI_CSATA_PROP "RstCSatV" | #define AHCI_TSATA_PROP "RsttSatV" | |||
#define VMD_PROP "RstUefiV" | #define VMD_PROP "RstUefiV" | |||
#define VENDOR_GUID \ | #define VENDOR_GUID \ | |||
EFI_GUID(0x193dfefa, 0xa445, 0x4302, 0x99, 0xd8, 0xef, 0x3a, 0xad, 0x1a, 0x04, 0xc6) | EFI_GUID(0x193dfefa, 0xa445, 0x4302, 0x99, 0xd8, 0xef, 0x3a, 0xad, 0x1a, 0x04, 0xc6) | |||
#define PCI_CLASS_RAID_CNTRL 0x010400 | #define PCI_CLASS_RAID_CNTRL 0x010400 | |||
static int read_efi_var(void *buffer, ssize_t buf_size, char *variable_name, str | static int read_efi_var(void *buffer, ssize_t buf_size, | |||
uct efi_guid guid) | const char *variable_name, struct efi_guid guid) | |||
{ | { | |||
char path[PATH_MAX]; | char path[PATH_MAX]; | |||
char buf[GUID_STR_MAX]; | char buf[GUID_STR_MAX]; | |||
int fd; | int fd; | |||
ssize_t n; | ssize_t n; | |||
snprintf(path, PATH_MAX, "%s/%s-%s", SYS_EFIVARS_PATH, variable_name, gui d_str(buf, guid)); | snprintf(path, PATH_MAX, "%s/%s-%s", SYS_EFIVARS_PATH, variable_name, gui d_str(buf, guid)); | |||
fd = open(path, O_RDONLY); | fd = open(path, O_RDONLY); | |||
if (fd < 0) | if (fd < 0) | |||
skipping to change at line 526 | skipping to change at line 552 | |||
/* read the variable data */ | /* read the variable data */ | |||
n = read(fd, buffer, buf_size); | n = read(fd, buffer, buf_size); | |||
close(fd); | close(fd); | |||
if (n < buf_size) | if (n < buf_size) | |||
return 1; | return 1; | |||
return 0; | return 0; | |||
} | } | |||
static int read_efi_variable(void *buffer, ssize_t buf_size, char *variable_name | static int read_efi_variable(void *buffer, ssize_t buf_size, | |||
, struct efi_guid guid) | const char *variable_name, struct efi_guid guid) | |||
{ | { | |||
char path[PATH_MAX]; | char path[PATH_MAX]; | |||
char buf[GUID_STR_MAX]; | char buf[GUID_STR_MAX]; | |||
int dfd; | int dfd; | |||
ssize_t n, var_data_len; | ssize_t n, var_data_len; | |||
/* Try to read the variable using the new efivarfs interface first. | /* Try to read the variable using the new efivarfs interface first. | |||
* If that fails, fall back to the old sysfs-efivars interface. */ | * If that fails, fall back to the old sysfs-efivars interface. */ | |||
if (!read_efi_var(buffer, buf_size, variable_name, guid)) | if (!read_efi_var(buffer, buf_size, variable_name, guid)) | |||
return 0; | return 0; | |||
skipping to change at line 579 | skipping to change at line 606 | |||
return 1; | return 1; | |||
} | } | |||
return 0; | return 0; | |||
} | } | |||
const struct imsm_orom *find_imsm_efi(struct sys_dev *hba) | const struct imsm_orom *find_imsm_efi(struct sys_dev *hba) | |||
{ | { | |||
struct imsm_orom orom; | struct imsm_orom orom; | |||
struct orom_entry *ret; | struct orom_entry *ret; | |||
int err; | static const char * const sata_efivars[] = {AHCI_PROP, AHCI_SSATA_PROP, | |||
AHCI_TSATA_PROP}; | ||||
unsigned long i; | ||||
if (check_env("IMSM_TEST_AHCI_EFI") || check_env("IMSM_TEST_SCU_EFI")) | if (check_env("IMSM_TEST_AHCI_EFI") || check_env("IMSM_TEST_SCU_EFI")) | |||
return imsm_platform_test(hba); | return imsm_platform_test(hba); | |||
/* OROM test is set, return that there is no EFI capabilities */ | /* OROM test is set, return that there is no EFI capabilities */ | |||
if (check_env("IMSM_TEST_OROM")) | if (check_env("IMSM_TEST_OROM")) | |||
return NULL; | return NULL; | |||
if (hba->type == SYS_DEV_SATA && hba->class != PCI_CLASS_RAID_CNTRL) | switch (hba->type) { | |||
return NULL; | case SYS_DEV_SAS: | |||
if (!read_efi_variable(&orom, sizeof(orom), SCU_PROP, | ||||
VENDOR_GUID)) | ||||
break; | ||||
err = read_efi_variable(&orom, sizeof(orom), hba->type == SYS_DEV_SAS ? S | return NULL; | |||
CU_PROP : AHCI_PROP, VENDOR_GUID); | case SYS_DEV_SATA: | |||
if (hba->class != PCI_CLASS_RAID_CNTRL) | ||||
return NULL; | ||||
for (i = 0; i < ARRAY_SIZE(sata_efivars); i++) { | ||||
if (!read_efi_variable(&orom, sizeof(orom), | ||||
sata_efivars[i], VENDOR_GUID)) | ||||
break; | ||||
/* try to read variable for second AHCI controller */ | ||||
if (err && hba->type == SYS_DEV_SATA) | ||||
err = read_efi_variable(&orom, sizeof(orom), AHCI_SSATA_PROP, VEN | ||||
DOR_GUID); | ||||
/* try to read variable for combined AHCI controllers */ | ||||
if (err && hba->type == SYS_DEV_SATA) { | ||||
static struct orom_entry *csata; | ||||
err = read_efi_variable(&orom, sizeof(orom), AHCI_CSATA_PROP, VEN | ||||
DOR_GUID); | ||||
if (!err) { | ||||
if (!csata) | ||||
csata = add_orom(&orom); | ||||
add_orom_device_id(csata, hba->dev_id); | ||||
csata->type = hba->type; | ||||
return &csata->orom; | ||||
} | } | |||
} | if (i == ARRAY_SIZE(sata_efivars)) | |||
return NULL; | ||||
if (hba->type == SYS_DEV_VMD) { | break; | |||
err = read_efi_variable(&orom, sizeof(orom), VMD_PROP, VENDOR_GUI | case SYS_DEV_VMD: | |||
D); | if (!read_efi_variable(&orom, sizeof(orom), VMD_PROP, | |||
} | VENDOR_GUID)) | |||
break; | ||||
if (err) | ||||
return NULL; | return NULL; | |||
default: | ||||
return NULL; | ||||
} | ||||
ret = add_orom(&orom); | ret = add_orom(&orom); | |||
add_orom_device_id(ret, hba->dev_id); | add_orom_device_id(ret, hba->dev_id); | |||
ret->type = hba->type; | ret->type = hba->type; | |||
return &ret->orom; | return &ret->orom; | |||
} | } | |||
const struct imsm_orom *find_imsm_nvme(struct sys_dev *hba) | const struct imsm_orom *find_imsm_nvme(struct sys_dev *hba) | |||
{ | { | |||
skipping to change at line 671 | skipping to change at line 700 | |||
if (hba->type == SYS_DEV_NVME) | if (hba->type == SYS_DEV_NVME) | |||
return find_imsm_nvme(hba); | return find_imsm_nvme(hba); | |||
if ((cap = find_imsm_efi(hba)) != NULL) | if ((cap = find_imsm_efi(hba)) != NULL) | |||
return cap; | return cap; | |||
if ((cap = find_imsm_hba_orom(hba)) != NULL) | if ((cap = find_imsm_hba_orom(hba)) != NULL) | |||
return cap; | return cap; | |||
return NULL; | return NULL; | |||
} | } | |||
char *devt_to_devpath(dev_t dev) | /* Check whether the nvme device is represented by nvme subsytem, | |||
* if yes virtual path should be changed to hardware device path, | ||||
* to allow IMSM capabilities detection. | ||||
* Returns: | ||||
* hardware path to device - if the device is represented via | ||||
* nvme virtual subsytem | ||||
* NULL - if the device is not represented via nvme virtual subsytem | ||||
*/ | ||||
char *get_nvme_multipath_dev_hw_path(const char *dev_path) | ||||
{ | { | |||
char device[46]; | DIR *dir; | |||
struct dirent *ent; | ||||
char *rp = NULL; | ||||
if (strncmp(dev_path, NVME_SUBSYS_PATH, strlen(NVME_SUBSYS_PATH)) != 0) | ||||
return NULL; | ||||
dir = opendir(dev_path); | ||||
if (!dir) | ||||
return NULL; | ||||
for (ent = readdir(dir); ent; ent = readdir(dir)) { | ||||
char buf[strlen(dev_path) + strlen(ent->d_name) + 1]; | ||||
sprintf(device, "/sys/dev/block/%d:%d/device", major(dev), minor(dev)); | /* Check if dir is a controller, ignore namespaces*/ | |||
return realpath(device, NULL); | if (!(strncmp(ent->d_name, "nvme", 4) == 0) || | |||
(strrchr(ent->d_name, 'n') != &ent->d_name[0])) | ||||
continue; | ||||
sprintf(buf, "%s/%s", dev_path, ent->d_name); | ||||
rp = realpath(buf, NULL); | ||||
break; | ||||
} | ||||
closedir(dir); | ||||
return rp; | ||||
} | } | |||
char *diskfd_to_devpath(int fd) | /* Description: Return part or whole realpath for the dev | |||
* Parameters: | ||||
* dev - the device to be quered | ||||
* dev_level - level of "/device" entries. It allows to caller to access | ||||
* virtual or physical devices which are on "path" to quered | ||||
* one. | ||||
* buf - optional, must be PATH_MAX size. If set, then will be used. | ||||
*/ | ||||
char *devt_to_devpath(dev_t dev, int dev_level, char *buf) | ||||
{ | ||||
char device[PATH_MAX]; | ||||
char *hw_path; | ||||
int i; | ||||
unsigned long device_free_len = sizeof(device) - 1; | ||||
char dev_str[] = "/device"; | ||||
unsigned long dev_str_len = strlen(dev_str); | ||||
snprintf(device, sizeof(device), "/sys/dev/block/%d:%d", major(dev), | ||||
minor(dev)); | ||||
/* If caller wants block device, return path to it even if it is exposed | ||||
* via virtual layer. | ||||
*/ | ||||
if (dev_level == 0) | ||||
return realpath(device, buf); | ||||
device_free_len -= strlen(device); | ||||
for (i = 0; i < dev_level; i++) { | ||||
if (device_free_len < dev_str_len) | ||||
return NULL; | ||||
strncat(device, dev_str, device_free_len); | ||||
/* Resolve nvme-subsystem abstraction if needed | ||||
*/ | ||||
device_free_len -= dev_str_len; | ||||
if (i == 0) { | ||||
char rp[PATH_MAX]; | ||||
if (!realpath(device, rp)) | ||||
return NULL; | ||||
hw_path = get_nvme_multipath_dev_hw_path(rp); | ||||
if (hw_path) { | ||||
strcpy(device, hw_path); | ||||
device_free_len = sizeof(device) - | ||||
strlen(device) - 1; | ||||
free(hw_path); | ||||
} | ||||
} | ||||
} | ||||
return realpath(device, buf); | ||||
} | ||||
char *diskfd_to_devpath(int fd, int dev_level, char *buf) | ||||
{ | { | |||
/* return the device path for a disk, return NULL on error or fd | /* return the device path for a disk, return NULL on error or fd | |||
* refers to a partition | * refers to a partition | |||
*/ | */ | |||
struct stat st; | struct stat st; | |||
if (fstat(fd, &st) != 0) | if (fstat(fd, &st) != 0) | |||
return NULL; | return NULL; | |||
if (!S_ISBLK(st.st_mode)) | if (!S_ISBLK(st.st_mode)) | |||
return NULL; | return NULL; | |||
return devt_to_devpath(st.st_rdev); | return devt_to_devpath(st.st_rdev, dev_level, buf); | |||
} | } | |||
int path_attached_to_hba(const char *disk_path, const char *hba_path) | int path_attached_to_hba(const char *disk_path, const char *hba_path) | |||
{ | { | |||
int rc; | int rc; | |||
if (check_env("IMSM_TEST_AHCI_DEV") || | if (check_env("IMSM_TEST_AHCI_DEV") || | |||
check_env("IMSM_TEST_SCU_DEV")) { | check_env("IMSM_TEST_SCU_DEV")) { | |||
return 1; | return 1; | |||
} | } | |||
skipping to change at line 716 | skipping to change at line 829 | |||
if (strncmp(disk_path, hba_path, strlen(hba_path)) == 0) | if (strncmp(disk_path, hba_path, strlen(hba_path)) == 0) | |||
rc = 1; | rc = 1; | |||
else | else | |||
rc = 0; | rc = 0; | |||
return rc; | return rc; | |||
} | } | |||
int devt_attached_to_hba(dev_t dev, const char *hba_path) | int devt_attached_to_hba(dev_t dev, const char *hba_path) | |||
{ | { | |||
char *disk_path = devt_to_devpath(dev); | char *disk_path = devt_to_devpath(dev, 1, NULL); | |||
int rc = path_attached_to_hba(disk_path, hba_path); | int rc = path_attached_to_hba(disk_path, hba_path); | |||
if (disk_path) | if (disk_path) | |||
free(disk_path); | free(disk_path); | |||
return rc; | return rc; | |||
} | } | |||
int disk_attached_to_hba(int fd, const char *hba_path) | int disk_attached_to_hba(int fd, const char *hba_path) | |||
{ | { | |||
char *disk_path = diskfd_to_devpath(fd); | char *disk_path = diskfd_to_devpath(fd, 1, NULL); | |||
int rc = path_attached_to_hba(disk_path, hba_path); | int rc = path_attached_to_hba(disk_path, hba_path); | |||
if (disk_path) | if (disk_path) | |||
free(disk_path); | free(disk_path); | |||
return rc; | return rc; | |||
} | } | |||
char *vmd_domain_to_controller(struct sys_dev *hba, char *buf) | char *vmd_domain_to_controller(struct sys_dev *hba, char *buf) | |||
{ | { | |||
skipping to change at line 769 | skipping to change at line 882 | |||
if (strncmp(buf, hba->path, strlen(buf)) == 0) { | if (strncmp(buf, hba->path, strlen(buf)) == 0) { | |||
sprintf(path, "/sys/bus/pci/drivers/vmd/%s", ent->d_name) ; | sprintf(path, "/sys/bus/pci/drivers/vmd/%s", ent->d_name) ; | |||
closedir(dir); | closedir(dir); | |||
return realpath(path, buf); | return realpath(path, buf); | |||
} | } | |||
} | } | |||
closedir(dir); | closedir(dir); | |||
return NULL; | return NULL; | |||
} | } | |||
/* Scan over all controller's namespaces and compare nsid value to verify if | ||||
* current one is supported. The routine doesn't check IMSM capabilities for | ||||
* namespace. Only one nvme namespace is supported by IMSM. | ||||
* Paramteres: | ||||
* fd - open descriptor to the nvme namespace | ||||
* verbose - error logging level | ||||
* Returns: | ||||
* 1 - if namespace is supported | ||||
* 0 - otherwise | ||||
*/ | ||||
int imsm_is_nvme_namespace_supported(int fd, int verbose) | ||||
{ | ||||
DIR *dir = NULL; | ||||
struct dirent *ent; | ||||
char cntrl_path[PATH_MAX]; | ||||
char ns_path[PATH_MAX]; | ||||
unsigned long long lowest_nsid = ULLONG_MAX; | ||||
unsigned long long this_nsid; | ||||
int rv = 0; | ||||
if (!diskfd_to_devpath(fd, 1, cntrl_path) || | ||||
!diskfd_to_devpath(fd, 0, ns_path)) { | ||||
if (verbose) | ||||
pr_err("Cannot get device paths\n"); | ||||
goto abort; | ||||
} | ||||
if (devpath_to_ll(ns_path, "nsid", &this_nsid)) { | ||||
if (verbose) | ||||
pr_err("Cannot read nsid value for %s", | ||||
basename(ns_path)); | ||||
goto abort; | ||||
} | ||||
dir = opendir(cntrl_path); | ||||
if (!dir) | ||||
goto abort; | ||||
/* The lowest nvme namespace is supported */ | ||||
for (ent = readdir(dir); ent; ent = readdir(dir)) { | ||||
unsigned long long curr_nsid; | ||||
char curr_ns_path[PATH_MAX + 256]; | ||||
if (!strstr(ent->d_name, "nvme")) | ||||
continue; | ||||
snprintf(curr_ns_path, sizeof(curr_ns_path), "%s/%s", | ||||
cntrl_path, ent->d_name); | ||||
if (devpath_to_ll(curr_ns_path, "nsid", &curr_nsid)) | ||||
goto abort; | ||||
if (lowest_nsid > curr_nsid) | ||||
lowest_nsid = curr_nsid; | ||||
} | ||||
if (this_nsid == lowest_nsid) | ||||
rv = 1; | ||||
else if (verbose) | ||||
pr_err("IMSM is supported on the lowest NVMe namespace\n"); | ||||
abort: | ||||
if (dir) | ||||
closedir(dir); | ||||
return rv; | ||||
} | ||||
/* Verify if multipath is supported by NVMe controller | ||||
* Returns: | ||||
* 0 - not supported | ||||
* 1 - supported | ||||
*/ | ||||
int is_multipath_nvme(int disk_fd) | ||||
{ | ||||
char ns_path[PATH_MAX]; | ||||
if (!diskfd_to_devpath(disk_fd, 0, ns_path)) | ||||
return 0; | ||||
if (strncmp(ns_path, NVME_SUBSYS_PATH, strlen(NVME_SUBSYS_PATH)) == 0) | ||||
return 1; | ||||
return 0; | ||||
} | ||||
End of changes. 20 change blocks. | ||||
42 lines changed or deleted | 149 lines changed or added |