From ed09dcc8bd7fe0991af7737e675996cbd022f38f Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 30 Dec 2014 14:46:14 -0800 Subject: ses: close potential registration race The slot and address fields have a small window of instability when userspace can read them before initialization. Separate enclosure_component allocation from registration. Signed-off-by: Dan Williams Signed-off-by: Song Liu Reviewed-by: Jens Axboe Cc: Hannes Reinecke Signed-off-by: Christoph Hellwig --- drivers/misc/enclosure.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index 180a5442fd4b..e18278aa6bbb 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -273,27 +273,26 @@ enclosure_component_find_by_name(struct enclosure_device *edev, static const struct attribute_group *enclosure_component_groups[]; /** - * enclosure_component_register - add a particular component to an enclosure + * enclosure_component_alloc - prepare a new enclosure component * @edev: the enclosure to add the component * @num: the device number * @type: the type of component being added * @name: an optional name to appear in sysfs (leave NULL if none) * - * Registers the component. The name is optional for enclosures that - * give their components a unique name. If not, leave the field NULL - * and a name will be assigned. + * The name is optional for enclosures that give their components a unique + * name. If not, leave the field NULL and a name will be assigned. * * Returns a pointer to the enclosure component or an error. */ struct enclosure_component * -enclosure_component_register(struct enclosure_device *edev, - unsigned int number, - enum enclosure_component_type type, - const char *name) +enclosure_component_alloc(struct enclosure_device *edev, + unsigned int number, + enum enclosure_component_type type, + const char *name) { struct enclosure_component *ecomp; struct device *cdev; - int err, i; + int i; char newname[COMPONENT_NAME_SIZE]; if (number >= edev->components) @@ -327,14 +326,30 @@ enclosure_component_register(struct enclosure_device *edev, cdev->release = enclosure_component_release; cdev->groups = enclosure_component_groups; + return ecomp; +} +EXPORT_SYMBOL_GPL(enclosure_component_alloc); + +/** + * enclosure_component_register - publishes an initialized enclosure component + * @ecomp: component to add + * + * Returns 0 on successful registration, releases the component otherwise + */ +int enclosure_component_register(struct enclosure_component *ecomp) +{ + struct device *cdev; + int err; + + cdev = &ecomp->cdev; err = device_register(cdev); if (err) { ecomp->number = -1; put_device(cdev); - return ERR_PTR(err); + return err; } - return ecomp; + return 0; } EXPORT_SYMBOL_GPL(enclosure_component_register); -- cgit v1.2.3 From 967f7bab0eaaa74d7d01a56d45aa309f78fb87dd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 30 Dec 2014 14:46:16 -0800 Subject: ses: add enclosure logical id Export the NAA logical id for the enclosure. This is optionally available from the sas_transport_class, but it is really a property of the enclosure. Signed-off-by: Dan Williams Signed-off-by: Song Liu Reviewed-by: Jens Axboe Cc: Hannes Reinecke Signed-off-by: Christoph Hellwig --- drivers/misc/enclosure.c | 13 +++++++++++++ drivers/scsi/ses.c | 9 +++++++++ include/linux/enclosure.h | 1 + 3 files changed, 23 insertions(+) (limited to 'drivers/misc') diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index e18278aa6bbb..958ee988a1e1 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -432,8 +432,21 @@ static ssize_t components_show(struct device *cdev, } static DEVICE_ATTR_RO(components); +static ssize_t id_show(struct device *cdev, + struct device_attribute *attr, + char *buf) +{ + struct enclosure_device *edev = to_enclosure_device(cdev); + + if (edev->cb->show_id) + return edev->cb->show_id(edev, buf); + return -EINVAL; +} +static DEVICE_ATTR_RO(id); + static struct attribute *enclosure_class_attrs[] = { &dev_attr_components.attr, + &dev_attr_id.attr, NULL, }; ATTRIBUTE_GROUPS(enclosure_class); diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 6662b0ca6cfd..1041556cdbf3 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -258,6 +258,14 @@ static int ses_set_active(struct enclosure_device *edev, return ses_set_page2_descriptor(edev, ecomp, desc); } +static int ses_show_id(struct enclosure_device *edev, char *buf) +{ + struct ses_device *ses_dev = edev->scratch; + unsigned long long id = get_unaligned_be64(ses_dev->page1+8+4); + + return sprintf(buf, "%#llx\n", id); +} + static struct enclosure_component_callbacks ses_enclosure_callbacks = { .get_fault = ses_get_fault, .set_fault = ses_set_fault, @@ -265,6 +273,7 @@ static struct enclosure_component_callbacks ses_enclosure_callbacks = { .get_locate = ses_get_locate, .set_locate = ses_set_locate, .set_active = ses_set_active, + .show_id = ses_show_id, }; struct ses_host_edev { diff --git a/include/linux/enclosure.h b/include/linux/enclosure.h index a835d335b924..807622b252a4 100644 --- a/include/linux/enclosure.h +++ b/include/linux/enclosure.h @@ -79,6 +79,7 @@ struct enclosure_component_callbacks { int (*set_locate)(struct enclosure_device *, struct enclosure_component *, enum enclosure_component_setting); + int (*show_id)(struct enclosure_device *, char *buf); }; -- cgit v1.2.3 From 921ce7f5786052749a22a75780f5ce1a456bcdc6 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 30 Dec 2014 14:46:17 -0800 Subject: ses: add reliable slot attribute The name provided by firmware is in a vendor specific format, publish the slot number to have a reliable mechanism for identifying slots across firmware implementations. If the enclosure does not provide a slot number fallback to the component number which is guaranteed unique, and usually mirrors the slot number. Cleaned up the unused ses_component.desc in the process. Signed-off-by: Dan Williams Signed-off-by: Song Liu Reviewed-by: Jens Axboe Reviewed-by: Hannes Reinecke Signed-off-by: Christoph Hellwig --- drivers/misc/enclosure.c | 20 +++++++++++++++++++- drivers/scsi/ses.c | 17 ++++++++++++----- include/linux/enclosure.h | 1 + 3 files changed, 32 insertions(+), 6 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index 958ee988a1e1..b62314d627ae 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -145,8 +145,10 @@ enclosure_register(struct device *dev, const char *name, int components, if (err) goto err; - for (i = 0; i < components; i++) + for (i = 0; i < components; i++) { edev->component[i].number = -1; + edev->component[i].slot = -1; + } mutex_lock(&container_list_lock); list_add_tail(&edev->node, &container_list); @@ -589,6 +591,20 @@ static ssize_t get_component_type(struct device *cdev, return snprintf(buf, 40, "%s\n", enclosure_type[ecomp->type]); } +static ssize_t get_component_slot(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct enclosure_component *ecomp = to_enclosure_component(cdev); + int slot; + + /* if the enclosure does not override then use 'number' as a stand-in */ + if (ecomp->slot >= 0) + slot = ecomp->slot; + else + slot = ecomp->number; + + return snprintf(buf, 40, "%d\n", slot); +} static DEVICE_ATTR(fault, S_IRUGO | S_IWUSR, get_component_fault, set_component_fault); @@ -599,6 +615,7 @@ static DEVICE_ATTR(active, S_IRUGO | S_IWUSR, get_component_active, static DEVICE_ATTR(locate, S_IRUGO | S_IWUSR, get_component_locate, set_component_locate); static DEVICE_ATTR(type, S_IRUGO, get_component_type, NULL); +static DEVICE_ATTR(slot, S_IRUGO, get_component_slot, NULL); static struct attribute *enclosure_component_attrs[] = { &dev_attr_fault.attr, @@ -606,6 +623,7 @@ static struct attribute *enclosure_component_attrs[] = { &dev_attr_active.attr, &dev_attr_locate.attr, &dev_attr_type.attr, + &dev_attr_slot.attr, NULL }; ATTRIBUTE_GROUPS(enclosure_component); diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 1041556cdbf3..433de8e6f538 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -47,7 +47,6 @@ struct ses_device { struct ses_component { u64 addr; - unsigned char *desc; }; static int ses_probe(struct device *dev) @@ -307,19 +306,26 @@ static void ses_process_descriptor(struct enclosure_component *ecomp, int invalid = desc[0] & 0x80; enum scsi_protocol proto = desc[0] & 0x0f; u64 addr = 0; + int slot = -1; struct ses_component *scomp = ecomp->scratch; unsigned char *d; - scomp->desc = desc; - if (invalid) return; switch (proto) { + case SCSI_PROTOCOL_FCP: + if (eip) { + d = desc + 4; + slot = d[3]; + } + break; case SCSI_PROTOCOL_SAS: - if (eip) + if (eip) { + d = desc + 4; + slot = d[3]; d = desc + 8; - else + } else d = desc + 4; /* only take the phy0 addr */ addr = (u64)d[12] << 56 | @@ -335,6 +341,7 @@ static void ses_process_descriptor(struct enclosure_component *ecomp, /* FIXME: Need to add more protocols than just SAS */ break; } + ecomp->slot = slot; scomp->addr = addr; } diff --git a/include/linux/enclosure.h b/include/linux/enclosure.h index 807622b252a4..0f826c14a337 100644 --- a/include/linux/enclosure.h +++ b/include/linux/enclosure.h @@ -92,6 +92,7 @@ struct enclosure_component { int fault; int active; int locate; + int slot; enum enclosure_status status; }; -- cgit v1.2.3 From 08024885a2a3ed432716e9d50046a620a5b2df05 Mon Sep 17 00:00:00 2001 From: Song Liu Date: Tue, 30 Dec 2014 14:46:18 -0800 Subject: ses: Add power_status to SES device slot Add power_status to SES device slot, so we can power on/off the HDDs behind the enclosure. Check firmware status in ses_set_* before sending control pages to firmware. Signed-off-by: Song Liu Acked-by: Dan Williams Reviewed-by: Jens Axboe Cc: Hannes Reinecke Signed-off-by: Christoph Hellwig --- drivers/misc/enclosure.c | 38 ++++++++++++++++++ drivers/scsi/ses.c | 98 ++++++++++++++++++++++++++++++++++++++++++----- include/linux/enclosure.h | 6 +++ 3 files changed, 133 insertions(+), 9 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index b62314d627ae..38552a31304a 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -148,6 +148,7 @@ enclosure_register(struct device *dev, const char *name, int components, for (i = 0; i < components; i++) { edev->component[i].number = -1; edev->component[i].slot = -1; + edev->component[i].power_status = 1; } mutex_lock(&container_list_lock); @@ -583,6 +584,40 @@ static ssize_t set_component_locate(struct device *cdev, return count; } +static ssize_t get_component_power_status(struct device *cdev, + struct device_attribute *attr, + char *buf) +{ + struct enclosure_device *edev = to_enclosure_device(cdev->parent); + struct enclosure_component *ecomp = to_enclosure_component(cdev); + + if (edev->cb->get_power_status) + edev->cb->get_power_status(edev, ecomp); + return snprintf(buf, 40, "%s\n", ecomp->power_status ? "on" : "off"); +} + +static ssize_t set_component_power_status(struct device *cdev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct enclosure_device *edev = to_enclosure_device(cdev->parent); + struct enclosure_component *ecomp = to_enclosure_component(cdev); + int val; + + if (strncmp(buf, "on", 2) == 0 && + (buf[2] == '\n' || buf[2] == '\0')) + val = 1; + else if (strncmp(buf, "off", 3) == 0 && + (buf[3] == '\n' || buf[3] == '\0')) + val = 0; + else + return -EINVAL; + + if (edev->cb->set_power_status) + edev->cb->set_power_status(edev, ecomp, val); + return count; +} + static ssize_t get_component_type(struct device *cdev, struct device_attribute *attr, char *buf) { @@ -614,6 +649,8 @@ static DEVICE_ATTR(active, S_IRUGO | S_IWUSR, get_component_active, set_component_active); static DEVICE_ATTR(locate, S_IRUGO | S_IWUSR, get_component_locate, set_component_locate); +static DEVICE_ATTR(power_status, S_IRUGO | S_IWUSR, get_component_power_status, + set_component_power_status); static DEVICE_ATTR(type, S_IRUGO, get_component_type, NULL); static DEVICE_ATTR(slot, S_IRUGO, get_component_slot, NULL); @@ -622,6 +659,7 @@ static struct attribute *enclosure_component_attrs[] = { &dev_attr_status.attr, &dev_attr_active.attr, &dev_attr_locate.attr, + &dev_attr_power_status.attr, &dev_attr_type.attr, &dev_attr_slot.attr, NULL diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 433de8e6f538..dcb0d76d7312 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -67,6 +67,20 @@ static int ses_probe(struct device *dev) #define SES_TIMEOUT (30 * HZ) #define SES_RETRIES 3 +static void init_device_slot_control(unsigned char *dest_desc, + struct enclosure_component *ecomp, + unsigned char *status) +{ + memcpy(dest_desc, status, 4); + dest_desc[0] = 0; + /* only clear byte 1 for ENCLOSURE_COMPONENT_DEVICE */ + if (ecomp->type == ENCLOSURE_COMPONENT_DEVICE) + dest_desc[1] = 0; + dest_desc[2] &= 0xde; + dest_desc[3] &= 0x3c; +} + + static int ses_recv_diag(struct scsi_device *sdev, int page_code, void *buf, int bufflen) { @@ -178,14 +192,22 @@ static int ses_set_fault(struct enclosure_device *edev, struct enclosure_component *ecomp, enum enclosure_component_setting val) { - unsigned char desc[4] = {0 }; + unsigned char desc[4]; + unsigned char *desc_ptr; + + desc_ptr = ses_get_page2_descriptor(edev, ecomp); + + if (!desc_ptr) + return -EIO; + + init_device_slot_control(desc, ecomp, desc_ptr); switch (val) { case ENCLOSURE_SETTING_DISABLED: - /* zero is disabled */ + desc[3] &= 0xdf; break; case ENCLOSURE_SETTING_ENABLED: - desc[3] = 0x20; + desc[3] |= 0x20; break; default: /* SES doesn't do the SGPIO blink settings */ @@ -219,14 +241,22 @@ static int ses_set_locate(struct enclosure_device *edev, struct enclosure_component *ecomp, enum enclosure_component_setting val) { - unsigned char desc[4] = {0 }; + unsigned char desc[4]; + unsigned char *desc_ptr; + + desc_ptr = ses_get_page2_descriptor(edev, ecomp); + + if (!desc_ptr) + return -EIO; + + init_device_slot_control(desc, ecomp, desc_ptr); switch (val) { case ENCLOSURE_SETTING_DISABLED: - /* zero is disabled */ + desc[2] &= 0xfd; break; case ENCLOSURE_SETTING_ENABLED: - desc[2] = 0x02; + desc[2] |= 0x02; break; default: /* SES doesn't do the SGPIO blink settings */ @@ -239,15 +269,23 @@ static int ses_set_active(struct enclosure_device *edev, struct enclosure_component *ecomp, enum enclosure_component_setting val) { - unsigned char desc[4] = {0 }; + unsigned char desc[4]; + unsigned char *desc_ptr; + + desc_ptr = ses_get_page2_descriptor(edev, ecomp); + + if (!desc_ptr) + return -EIO; + + init_device_slot_control(desc, ecomp, desc_ptr); switch (val) { case ENCLOSURE_SETTING_DISABLED: - /* zero is disabled */ + desc[2] &= 0x7f; ecomp->active = 0; break; case ENCLOSURE_SETTING_ENABLED: - desc[2] = 0x80; + desc[2] |= 0x80; ecomp->active = 1; break; default: @@ -265,12 +303,53 @@ static int ses_show_id(struct enclosure_device *edev, char *buf) return sprintf(buf, "%#llx\n", id); } +static void ses_get_power_status(struct enclosure_device *edev, + struct enclosure_component *ecomp) +{ + unsigned char *desc; + + desc = ses_get_page2_descriptor(edev, ecomp); + if (desc) + ecomp->power_status = (desc[3] & 0x10) ? 0 : 1; +} + +static int ses_set_power_status(struct enclosure_device *edev, + struct enclosure_component *ecomp, + int val) +{ + unsigned char desc[4]; + unsigned char *desc_ptr; + + desc_ptr = ses_get_page2_descriptor(edev, ecomp); + + if (!desc_ptr) + return -EIO; + + init_device_slot_control(desc, ecomp, desc_ptr); + + switch (val) { + /* power = 1 is device_off = 0 and vice versa */ + case 0: + desc[3] |= 0x10; + break; + case 1: + desc[3] &= 0xef; + break; + default: + return -EINVAL; + } + ecomp->power_status = val; + return ses_set_page2_descriptor(edev, ecomp, desc); +} + static struct enclosure_component_callbacks ses_enclosure_callbacks = { .get_fault = ses_get_fault, .set_fault = ses_set_fault, .get_status = ses_get_status, .get_locate = ses_get_locate, .set_locate = ses_set_locate, + .get_power_status = ses_get_power_status, + .set_power_status = ses_set_power_status, .set_active = ses_set_active, .show_id = ses_show_id, }; @@ -449,6 +528,7 @@ static void ses_enclosure_data_process(struct enclosure_device *edev, ecomp = &edev->component[components++]; if (!IS_ERR(ecomp)) { + ses_get_power_status(edev, ecomp); if (addl_desc_ptr) ses_process_descriptor( ecomp, diff --git a/include/linux/enclosure.h b/include/linux/enclosure.h index 0f826c14a337..7be22da321f3 100644 --- a/include/linux/enclosure.h +++ b/include/linux/enclosure.h @@ -79,6 +79,11 @@ struct enclosure_component_callbacks { int (*set_locate)(struct enclosure_device *, struct enclosure_component *, enum enclosure_component_setting); + void (*get_power_status)(struct enclosure_device *, + struct enclosure_component *); + int (*set_power_status)(struct enclosure_device *, + struct enclosure_component *, + int); int (*show_id)(struct enclosure_device *, char *buf); }; @@ -94,6 +99,7 @@ struct enclosure_component { int locate; int slot; enum enclosure_status status; + int power_status; }; struct enclosure_device { -- cgit v1.2.3