aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHannes Reinecke <hare@suse.de>2008-07-17 19:52:57 -0400
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2008-07-26 15:14:51 -0400
commit4c05ae52fcb0e27a2ee4a16d1f31f8c547fd4886 (patch)
tree4a577f6a1932923bfbcb8592f4a4f2a7935616dd
parent765cbc6dad16b87724803e359d6be792ddf08614 (diff)
[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>
-rw-r--r--drivers/scsi/device_handler/scsi_dh.c103
1 files changed, 102 insertions, 1 deletions
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c
index 3f798171ed69..e90df9dd3e65 100644
--- a/drivers/scsi/device_handler/scsi_dh.c
+++ b/drivers/scsi/device_handler/scsi_dh.c
@@ -84,7 +84,7 @@ static int scsi_dh_handler_attach(struct scsi_device *sdev,
84 * @scsi_dh - Device handler to be detached 84 * @scsi_dh - Device handler to be detached
85 * 85 *
86 * Detach from a device handler. If a device handler is specified, 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 */
89static void scsi_dh_handler_detach(struct scsi_device *sdev, 89static void scsi_dh_handler_detach(struct scsi_device *sdev,
90 struct scsi_device_handler *scsi_dh) 90 struct scsi_device_handler *scsi_dh)
@@ -103,6 +103,98 @@ static void scsi_dh_handler_detach(struct scsi_device *sdev,
103} 103}
104 104
105/* 105/*
106 * Functions for sysfs attribute 'dh_state'
107 */
108static ssize_t
109store_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
145static ssize_t
146show_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
156static 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 */
163static 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 */
182static 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;
195}
196
197/*
106 * scsi_dh_notifier - notifier chain callback 198 * scsi_dh_notifier - notifier chain callback
107 */ 199 */
108static int scsi_dh_notifier(struct notifier_block *nb, 200static int scsi_dh_notifier(struct notifier_block *nb,
@@ -132,7 +224,10 @@ static int scsi_dh_notifier(struct notifier_block *nb,
132 224
133 if (action == BUS_NOTIFY_ADD_DEVICE) { 225 if (action == BUS_NOTIFY_ADD_DEVICE) {
134 err = scsi_dh_handler_attach(sdev, devinfo); 226 err = scsi_dh_handler_attach(sdev, devinfo);
227 if (!err)
228 err = device_create_file(dev, &scsi_dh_state_attr);
135 } else if (action == BUS_NOTIFY_DEL_DEVICE) { 229 } else if (action == BUS_NOTIFY_DEL_DEVICE) {
230 device_remove_file(dev, &scsi_dh_state_attr);
136 scsi_dh_handler_detach(sdev, NULL); 231 scsi_dh_handler_detach(sdev, NULL);
137 } 232 }
138out: 233out:
@@ -284,11 +379,17 @@ static int __init scsi_dh_init(void)
284 379
285 r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb); 380 r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb);
286 381
382 if (!r)
383 bus_for_each_dev(&scsi_bus_type, NULL, NULL,
384 scsi_dh_sysfs_attr_add);
385
287 return r; 386 return r;
288} 387}
289 388
290static void __exit scsi_dh_exit(void) 389static void __exit scsi_dh_exit(void)
291{ 390{
391 bus_for_each_dev(&scsi_bus_type, NULL, NULL,
392 scsi_dh_sysfs_attr_remove);
292 bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb); 393 bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb);
293} 394}
294 395