forked from mirrors/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>
This commit is contained in:
		
							parent
							
								
									921ce7f578
								
							
						
					
					
						commit
						08024885a2
					
				
					 3 changed files with 133 additions and 9 deletions
				
			
		|  | @ -148,6 +148,7 @@ enclosure_register(struct device *dev, const char *name, int components, | ||||||
| 	for (i = 0; i < components; i++) { | 	for (i = 0; i < components; i++) { | ||||||
| 		edev->component[i].number = -1; | 		edev->component[i].number = -1; | ||||||
| 		edev->component[i].slot = -1; | 		edev->component[i].slot = -1; | ||||||
|  | 		edev->component[i].power_status = 1; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	mutex_lock(&container_list_lock); | 	mutex_lock(&container_list_lock); | ||||||
|  | @ -583,6 +584,40 @@ static ssize_t set_component_locate(struct device *cdev, | ||||||
| 	return count; | 	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, | static ssize_t get_component_type(struct device *cdev, | ||||||
| 				  struct device_attribute *attr, char *buf) | 				  struct device_attribute *attr, char *buf) | ||||||
| { | { | ||||||
|  | @ -614,6 +649,8 @@ static DEVICE_ATTR(active, S_IRUGO | S_IWUSR, get_component_active, | ||||||
| 		   set_component_active); | 		   set_component_active); | ||||||
| static DEVICE_ATTR(locate, S_IRUGO | S_IWUSR, get_component_locate, | static DEVICE_ATTR(locate, S_IRUGO | S_IWUSR, get_component_locate, | ||||||
| 		   set_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(type, S_IRUGO, get_component_type, NULL); | ||||||
| static DEVICE_ATTR(slot, S_IRUGO, get_component_slot, 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_status.attr, | ||||||
| 	&dev_attr_active.attr, | 	&dev_attr_active.attr, | ||||||
| 	&dev_attr_locate.attr, | 	&dev_attr_locate.attr, | ||||||
|  | 	&dev_attr_power_status.attr, | ||||||
| 	&dev_attr_type.attr, | 	&dev_attr_type.attr, | ||||||
| 	&dev_attr_slot.attr, | 	&dev_attr_slot.attr, | ||||||
| 	NULL | 	NULL | ||||||
|  |  | ||||||
|  | @ -67,6 +67,20 @@ static int ses_probe(struct device *dev) | ||||||
| #define SES_TIMEOUT (30 * HZ) | #define SES_TIMEOUT (30 * HZ) | ||||||
| #define SES_RETRIES 3 | #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, | static int ses_recv_diag(struct scsi_device *sdev, int page_code, | ||||||
| 			 void *buf, int bufflen) | 			 void *buf, int bufflen) | ||||||
| { | { | ||||||
|  | @ -178,14 +192,22 @@ static int ses_set_fault(struct enclosure_device *edev, | ||||||
| 			  struct enclosure_component *ecomp, | 			  struct enclosure_component *ecomp, | ||||||
| 			 enum enclosure_component_setting val) | 			 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) { | 	switch (val) { | ||||||
| 	case ENCLOSURE_SETTING_DISABLED: | 	case ENCLOSURE_SETTING_DISABLED: | ||||||
| 		/* zero is disabled */ | 		desc[3] &= 0xdf; | ||||||
| 		break; | 		break; | ||||||
| 	case ENCLOSURE_SETTING_ENABLED: | 	case ENCLOSURE_SETTING_ENABLED: | ||||||
| 		desc[3] = 0x20; | 		desc[3] |= 0x20; | ||||||
| 		break; | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		/* SES doesn't do the SGPIO blink settings */ | 		/* 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, | 			  struct enclosure_component *ecomp, | ||||||
| 			  enum enclosure_component_setting val) | 			  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) { | 	switch (val) { | ||||||
| 	case ENCLOSURE_SETTING_DISABLED: | 	case ENCLOSURE_SETTING_DISABLED: | ||||||
| 		/* zero is disabled */ | 		desc[2] &= 0xfd; | ||||||
| 		break; | 		break; | ||||||
| 	case ENCLOSURE_SETTING_ENABLED: | 	case ENCLOSURE_SETTING_ENABLED: | ||||||
| 		desc[2] = 0x02; | 		desc[2] |= 0x02; | ||||||
| 		break; | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		/* SES doesn't do the SGPIO blink settings */ | 		/* 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, | 			  struct enclosure_component *ecomp, | ||||||
| 			  enum enclosure_component_setting val) | 			  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) { | 	switch (val) { | ||||||
| 	case ENCLOSURE_SETTING_DISABLED: | 	case ENCLOSURE_SETTING_DISABLED: | ||||||
| 		/* zero is disabled */ | 		desc[2] &= 0x7f; | ||||||
| 		ecomp->active = 0; | 		ecomp->active = 0; | ||||||
| 		break; | 		break; | ||||||
| 	case ENCLOSURE_SETTING_ENABLED: | 	case ENCLOSURE_SETTING_ENABLED: | ||||||
| 		desc[2] = 0x80; | 		desc[2] |= 0x80; | ||||||
| 		ecomp->active = 1; | 		ecomp->active = 1; | ||||||
| 		break; | 		break; | ||||||
| 	default: | 	default: | ||||||
|  | @ -265,12 +303,53 @@ static int ses_show_id(struct enclosure_device *edev, char *buf) | ||||||
| 	return sprintf(buf, "%#llx\n", id); | 	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 = { | static struct enclosure_component_callbacks ses_enclosure_callbacks = { | ||||||
| 	.get_fault		= ses_get_fault, | 	.get_fault		= ses_get_fault, | ||||||
| 	.set_fault		= ses_set_fault, | 	.set_fault		= ses_set_fault, | ||||||
| 	.get_status		= ses_get_status, | 	.get_status		= ses_get_status, | ||||||
| 	.get_locate		= ses_get_locate, | 	.get_locate		= ses_get_locate, | ||||||
| 	.set_locate		= ses_set_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, | 	.set_active		= ses_set_active, | ||||||
| 	.show_id		= ses_show_id, | 	.show_id		= ses_show_id, | ||||||
| }; | }; | ||||||
|  | @ -449,6 +528,7 @@ static void ses_enclosure_data_process(struct enclosure_device *edev, | ||||||
| 					ecomp = &edev->component[components++]; | 					ecomp = &edev->component[components++]; | ||||||
| 
 | 
 | ||||||
| 				if (!IS_ERR(ecomp)) { | 				if (!IS_ERR(ecomp)) { | ||||||
|  | 					ses_get_power_status(edev, ecomp); | ||||||
| 					if (addl_desc_ptr) | 					if (addl_desc_ptr) | ||||||
| 						ses_process_descriptor( | 						ses_process_descriptor( | ||||||
| 							ecomp, | 							ecomp, | ||||||
|  |  | ||||||
|  | @ -79,6 +79,11 @@ struct enclosure_component_callbacks { | ||||||
| 	int (*set_locate)(struct enclosure_device *, | 	int (*set_locate)(struct enclosure_device *, | ||||||
| 			  struct enclosure_component *, | 			  struct enclosure_component *, | ||||||
| 			  enum enclosure_component_setting); | 			  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); | 	int (*show_id)(struct enclosure_device *, char *buf); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -94,6 +99,7 @@ struct enclosure_component { | ||||||
| 	int locate; | 	int locate; | ||||||
| 	int slot; | 	int slot; | ||||||
| 	enum enclosure_status status; | 	enum enclosure_status status; | ||||||
|  | 	int power_status; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct enclosure_device { | struct enclosure_device { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Song Liu
						Song Liu