[SCSI] scsi_dh: Add 'dh_state' sysfs attribute

Implement a 'dh_state' sdev attribute for dynamic device handler
manipulation. A read on the attribute will return the name of
the currently attached device handler or 'detached' if no handler
is attached.
The attribute allows the following strings to be written:
- The name of the device handler to be attached if the state is
'detached'.
- 'activate' to trigger path activation if a device handler
is attached.
- 'detach' to detach the currently attached device handler.

Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>

authored by

Hannes Reinecke and committed by
James Bottomley
4c05ae52 765cbc6d

+102 -1
+102 -1
drivers/scsi/device_handler/scsi_dh.c
··· 84 84 * @scsi_dh - Device handler to be detached 85 85 * 86 86 * Detach from a device handler. If a device handler is specified, 87 - * only detach if the currently attached handler is equal to it. 87 + * only detach if the currently attached handler matches @scsi_dh. 88 88 */ 89 89 static void scsi_dh_handler_detach(struct scsi_device *sdev, 90 90 struct scsi_device_handler *scsi_dh) ··· 100 100 101 101 if (scsi_dh && scsi_dh->detach) 102 102 scsi_dh->detach(sdev); 103 + } 104 + 105 + /* 106 + * Functions for sysfs attribute 'dh_state' 107 + */ 108 + static ssize_t 109 + store_dh_state(struct device *dev, struct device_attribute *attr, 110 + const char *buf, size_t count) 111 + { 112 + struct scsi_device *sdev = to_scsi_device(dev); 113 + struct scsi_device_handler *scsi_dh; 114 + int err = -EINVAL; 115 + 116 + if (!sdev->scsi_dh_data) { 117 + /* 118 + * Attach to a device handler 119 + */ 120 + if (!(scsi_dh = get_device_handler(buf))) 121 + return err; 122 + err = scsi_dh_handler_attach(sdev, scsi_dh); 123 + } else { 124 + scsi_dh = sdev->scsi_dh_data->scsi_dh; 125 + if (!strncmp(buf, "detach", 6)) { 126 + /* 127 + * Detach from a device handler 128 + */ 129 + scsi_dh_handler_detach(sdev, scsi_dh); 130 + err = 0; 131 + } else if (!strncmp(buf, "activate", 8)) { 132 + /* 133 + * Activate a device handler 134 + */ 135 + if (scsi_dh->activate) 136 + err = scsi_dh->activate(sdev); 137 + else 138 + err = 0; 139 + } 140 + } 141 + 142 + return err<0?err:count; 143 + } 144 + 145 + static ssize_t 146 + show_dh_state(struct device *dev, struct device_attribute *attr, char *buf) 147 + { 148 + struct scsi_device *sdev = to_scsi_device(dev); 149 + 150 + if (!sdev->scsi_dh_data) 151 + return snprintf(buf, 20, "detached\n"); 152 + 153 + return snprintf(buf, 20, "%s\n", sdev->scsi_dh_data->scsi_dh->name); 154 + } 155 + 156 + static struct device_attribute scsi_dh_state_attr = 157 + __ATTR(dh_state, S_IRUGO | S_IWUSR, show_dh_state, 158 + store_dh_state); 159 + 160 + /* 161 + * scsi_dh_sysfs_attr_add - Callback for scsi_init_dh 162 + */ 163 + static int scsi_dh_sysfs_attr_add(struct device *dev, void *data) 164 + { 165 + struct scsi_device *sdev; 166 + int err; 167 + 168 + if (!scsi_is_sdev_device(dev)) 169 + return 0; 170 + 171 + sdev = to_scsi_device(dev); 172 + 173 + err = device_create_file(&sdev->sdev_gendev, 174 + &scsi_dh_state_attr); 175 + 176 + return 0; 177 + } 178 + 179 + /* 180 + * scsi_dh_sysfs_attr_remove - Callback for scsi_exit_dh 181 + */ 182 + static int scsi_dh_sysfs_attr_remove(struct device *dev, void *data) 183 + { 184 + struct scsi_device *sdev; 185 + 186 + if (!scsi_is_sdev_device(dev)) 187 + return 0; 188 + 189 + sdev = to_scsi_device(dev); 190 + 191 + device_remove_file(&sdev->sdev_gendev, 192 + &scsi_dh_state_attr); 193 + 194 + return 0; 103 195 } 104 196 105 197 /* ··· 224 132 225 133 if (action == BUS_NOTIFY_ADD_DEVICE) { 226 134 err = scsi_dh_handler_attach(sdev, devinfo); 135 + if (!err) 136 + err = device_create_file(dev, &scsi_dh_state_attr); 227 137 } else if (action == BUS_NOTIFY_DEL_DEVICE) { 138 + device_remove_file(dev, &scsi_dh_state_attr); 228 139 scsi_dh_handler_detach(sdev, NULL); 229 140 } 230 141 out: ··· 379 284 380 285 r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb); 381 286 287 + if (!r) 288 + bus_for_each_dev(&scsi_bus_type, NULL, NULL, 289 + scsi_dh_sysfs_attr_add); 290 + 382 291 return r; 383 292 } 384 293 385 294 static void __exit scsi_dh_exit(void) 386 295 { 296 + bus_for_each_dev(&scsi_bus_type, NULL, NULL, 297 + scsi_dh_sysfs_attr_remove); 387 298 bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb); 388 299 } 389 300