Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <songliubraving@fb.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Jens Axboe <axboe@fb.com>
Cc: Hannes Reinecke <hare@suse.de>
Signed-off-by: Christoph Hellwig <hch@lst.de>

authored by

Song Liu and committed by
Christoph Hellwig
08024885 921ce7f5

+133 -9
+38
drivers/misc/enclosure.c
··· 148 148 for (i = 0; i < components; i++) { 149 149 edev->component[i].number = -1; 150 150 edev->component[i].slot = -1; 151 + edev->component[i].power_status = 1; 151 152 } 152 153 153 154 mutex_lock(&container_list_lock); ··· 584 583 return count; 585 584 } 586 585 586 + static ssize_t get_component_power_status(struct device *cdev, 587 + struct device_attribute *attr, 588 + char *buf) 589 + { 590 + struct enclosure_device *edev = to_enclosure_device(cdev->parent); 591 + struct enclosure_component *ecomp = to_enclosure_component(cdev); 592 + 593 + if (edev->cb->get_power_status) 594 + edev->cb->get_power_status(edev, ecomp); 595 + return snprintf(buf, 40, "%s\n", ecomp->power_status ? "on" : "off"); 596 + } 597 + 598 + static ssize_t set_component_power_status(struct device *cdev, 599 + struct device_attribute *attr, 600 + const char *buf, size_t count) 601 + { 602 + struct enclosure_device *edev = to_enclosure_device(cdev->parent); 603 + struct enclosure_component *ecomp = to_enclosure_component(cdev); 604 + int val; 605 + 606 + if (strncmp(buf, "on", 2) == 0 && 607 + (buf[2] == '\n' || buf[2] == '\0')) 608 + val = 1; 609 + else if (strncmp(buf, "off", 3) == 0 && 610 + (buf[3] == '\n' || buf[3] == '\0')) 611 + val = 0; 612 + else 613 + return -EINVAL; 614 + 615 + if (edev->cb->set_power_status) 616 + edev->cb->set_power_status(edev, ecomp, val); 617 + return count; 618 + } 619 + 587 620 static ssize_t get_component_type(struct device *cdev, 588 621 struct device_attribute *attr, char *buf) 589 622 { ··· 649 614 set_component_active); 650 615 static DEVICE_ATTR(locate, S_IRUGO | S_IWUSR, get_component_locate, 651 616 set_component_locate); 617 + static DEVICE_ATTR(power_status, S_IRUGO | S_IWUSR, get_component_power_status, 618 + set_component_power_status); 652 619 static DEVICE_ATTR(type, S_IRUGO, get_component_type, NULL); 653 620 static DEVICE_ATTR(slot, S_IRUGO, get_component_slot, NULL); 654 621 ··· 659 622 &dev_attr_status.attr, 660 623 &dev_attr_active.attr, 661 624 &dev_attr_locate.attr, 625 + &dev_attr_power_status.attr, 662 626 &dev_attr_type.attr, 663 627 &dev_attr_slot.attr, 664 628 NULL
+89 -9
drivers/scsi/ses.c
··· 67 67 #define SES_TIMEOUT (30 * HZ) 68 68 #define SES_RETRIES 3 69 69 70 + static void init_device_slot_control(unsigned char *dest_desc, 71 + struct enclosure_component *ecomp, 72 + unsigned char *status) 73 + { 74 + memcpy(dest_desc, status, 4); 75 + dest_desc[0] = 0; 76 + /* only clear byte 1 for ENCLOSURE_COMPONENT_DEVICE */ 77 + if (ecomp->type == ENCLOSURE_COMPONENT_DEVICE) 78 + dest_desc[1] = 0; 79 + dest_desc[2] &= 0xde; 80 + dest_desc[3] &= 0x3c; 81 + } 82 + 83 + 70 84 static int ses_recv_diag(struct scsi_device *sdev, int page_code, 71 85 void *buf, int bufflen) 72 86 { ··· 192 178 struct enclosure_component *ecomp, 193 179 enum enclosure_component_setting val) 194 180 { 195 - unsigned char desc[4] = {0 }; 181 + unsigned char desc[4]; 182 + unsigned char *desc_ptr; 183 + 184 + desc_ptr = ses_get_page2_descriptor(edev, ecomp); 185 + 186 + if (!desc_ptr) 187 + return -EIO; 188 + 189 + init_device_slot_control(desc, ecomp, desc_ptr); 196 190 197 191 switch (val) { 198 192 case ENCLOSURE_SETTING_DISABLED: 199 - /* zero is disabled */ 193 + desc[3] &= 0xdf; 200 194 break; 201 195 case ENCLOSURE_SETTING_ENABLED: 202 - desc[3] = 0x20; 196 + desc[3] |= 0x20; 203 197 break; 204 198 default: 205 199 /* SES doesn't do the SGPIO blink settings */ ··· 241 219 struct enclosure_component *ecomp, 242 220 enum enclosure_component_setting val) 243 221 { 244 - unsigned char desc[4] = {0 }; 222 + unsigned char desc[4]; 223 + unsigned char *desc_ptr; 224 + 225 + desc_ptr = ses_get_page2_descriptor(edev, ecomp); 226 + 227 + if (!desc_ptr) 228 + return -EIO; 229 + 230 + init_device_slot_control(desc, ecomp, desc_ptr); 245 231 246 232 switch (val) { 247 233 case ENCLOSURE_SETTING_DISABLED: 248 - /* zero is disabled */ 234 + desc[2] &= 0xfd; 249 235 break; 250 236 case ENCLOSURE_SETTING_ENABLED: 251 - desc[2] = 0x02; 237 + desc[2] |= 0x02; 252 238 break; 253 239 default: 254 240 /* SES doesn't do the SGPIO blink settings */ ··· 269 239 struct enclosure_component *ecomp, 270 240 enum enclosure_component_setting val) 271 241 { 272 - unsigned char desc[4] = {0 }; 242 + unsigned char desc[4]; 243 + unsigned char *desc_ptr; 244 + 245 + desc_ptr = ses_get_page2_descriptor(edev, ecomp); 246 + 247 + if (!desc_ptr) 248 + return -EIO; 249 + 250 + init_device_slot_control(desc, ecomp, desc_ptr); 273 251 274 252 switch (val) { 275 253 case ENCLOSURE_SETTING_DISABLED: 276 - /* zero is disabled */ 254 + desc[2] &= 0x7f; 277 255 ecomp->active = 0; 278 256 break; 279 257 case ENCLOSURE_SETTING_ENABLED: 280 - desc[2] = 0x80; 258 + desc[2] |= 0x80; 281 259 ecomp->active = 1; 282 260 break; 283 261 default: ··· 303 265 return sprintf(buf, "%#llx\n", id); 304 266 } 305 267 268 + static void ses_get_power_status(struct enclosure_device *edev, 269 + struct enclosure_component *ecomp) 270 + { 271 + unsigned char *desc; 272 + 273 + desc = ses_get_page2_descriptor(edev, ecomp); 274 + if (desc) 275 + ecomp->power_status = (desc[3] & 0x10) ? 0 : 1; 276 + } 277 + 278 + static int ses_set_power_status(struct enclosure_device *edev, 279 + struct enclosure_component *ecomp, 280 + int val) 281 + { 282 + unsigned char desc[4]; 283 + unsigned char *desc_ptr; 284 + 285 + desc_ptr = ses_get_page2_descriptor(edev, ecomp); 286 + 287 + if (!desc_ptr) 288 + return -EIO; 289 + 290 + init_device_slot_control(desc, ecomp, desc_ptr); 291 + 292 + switch (val) { 293 + /* power = 1 is device_off = 0 and vice versa */ 294 + case 0: 295 + desc[3] |= 0x10; 296 + break; 297 + case 1: 298 + desc[3] &= 0xef; 299 + break; 300 + default: 301 + return -EINVAL; 302 + } 303 + ecomp->power_status = val; 304 + return ses_set_page2_descriptor(edev, ecomp, desc); 305 + } 306 + 306 307 static struct enclosure_component_callbacks ses_enclosure_callbacks = { 307 308 .get_fault = ses_get_fault, 308 309 .set_fault = ses_set_fault, 309 310 .get_status = ses_get_status, 310 311 .get_locate = ses_get_locate, 311 312 .set_locate = ses_set_locate, 313 + .get_power_status = ses_get_power_status, 314 + .set_power_status = ses_set_power_status, 312 315 .set_active = ses_set_active, 313 316 .show_id = ses_show_id, 314 317 }; ··· 528 449 ecomp = &edev->component[components++]; 529 450 530 451 if (!IS_ERR(ecomp)) { 452 + ses_get_power_status(edev, ecomp); 531 453 if (addl_desc_ptr) 532 454 ses_process_descriptor( 533 455 ecomp,
+6
include/linux/enclosure.h
··· 79 79 int (*set_locate)(struct enclosure_device *, 80 80 struct enclosure_component *, 81 81 enum enclosure_component_setting); 82 + void (*get_power_status)(struct enclosure_device *, 83 + struct enclosure_component *); 84 + int (*set_power_status)(struct enclosure_device *, 85 + struct enclosure_component *, 86 + int); 82 87 int (*show_id)(struct enclosure_device *, char *buf); 83 88 }; 84 89 ··· 99 94 int locate; 100 95 int slot; 101 96 enum enclosure_status status; 97 + int power_status; 102 98 }; 103 99 104 100 struct enclosure_device {