diff options
-rw-r--r-- | drivers/scsi/Kconfig | 2 | ||||
-rw-r--r-- | drivers/scsi/Makefile | 1 | ||||
-rw-r--r-- | drivers/scsi/device_handler/Kconfig | 12 | ||||
-rw-r--r-- | drivers/scsi/device_handler/Makefile | 4 | ||||
-rw-r--r-- | drivers/scsi/device_handler/scsi_dh.c | 162 | ||||
-rw-r--r-- | drivers/scsi/scsi_error.c | 11 | ||||
-rw-r--r-- | drivers/scsi/scsi_lib.c | 8 | ||||
-rw-r--r-- | drivers/scsi/scsi_sysfs.c | 1 | ||||
-rw-r--r-- | include/scsi/scsi.h | 1 | ||||
-rw-r--r-- | include/scsi/scsi_device.h | 22 | ||||
-rw-r--r-- | include/scsi/scsi_dh.h | 59 |
11 files changed, 283 insertions, 0 deletions
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 81ccbd7f9e34..7886ec6e5f87 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig | |||
@@ -1771,4 +1771,6 @@ endif # SCSI_LOWLEVEL | |||
1771 | 1771 | ||
1772 | source "drivers/scsi/pcmcia/Kconfig" | 1772 | source "drivers/scsi/pcmcia/Kconfig" |
1773 | 1773 | ||
1774 | source "drivers/scsi/device_handler/Kconfig" | ||
1775 | |||
1774 | endmenu | 1776 | endmenu |
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 6c775e350c98..aa8272e188ea 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile | |||
@@ -34,6 +34,7 @@ obj-$(CONFIG_SCSI_ISCSI_ATTRS) += scsi_transport_iscsi.o | |||
34 | obj-$(CONFIG_SCSI_SAS_ATTRS) += scsi_transport_sas.o | 34 | obj-$(CONFIG_SCSI_SAS_ATTRS) += scsi_transport_sas.o |
35 | obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas/ | 35 | obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas/ |
36 | obj-$(CONFIG_SCSI_SRP_ATTRS) += scsi_transport_srp.o | 36 | obj-$(CONFIG_SCSI_SRP_ATTRS) += scsi_transport_srp.o |
37 | obj-$(CONFIG_SCSI_DH) += device_handler/ | ||
37 | 38 | ||
38 | obj-$(CONFIG_ISCSI_TCP) += libiscsi.o iscsi_tcp.o | 39 | obj-$(CONFIG_ISCSI_TCP) += libiscsi.o iscsi_tcp.o |
39 | obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o | 40 | obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o |
diff --git a/drivers/scsi/device_handler/Kconfig b/drivers/scsi/device_handler/Kconfig new file mode 100644 index 000000000000..228241d1d98c --- /dev/null +++ b/drivers/scsi/device_handler/Kconfig | |||
@@ -0,0 +1,12 @@ | |||
1 | # | ||
2 | # SCSI Device Handler configuration | ||
3 | # | ||
4 | |||
5 | menuconfig SCSI_DH | ||
6 | tristate "SCSI Device Handlers" | ||
7 | depends on SCSI | ||
8 | default n | ||
9 | help | ||
10 | SCSI Device Handlers provide device specific support for | ||
11 | devices utilized in multipath configurations. Say Y here to | ||
12 | select support for specific hardware. | ||
diff --git a/drivers/scsi/device_handler/Makefile b/drivers/scsi/device_handler/Makefile new file mode 100644 index 000000000000..f306e44e3837 --- /dev/null +++ b/drivers/scsi/device_handler/Makefile | |||
@@ -0,0 +1,4 @@ | |||
1 | # | ||
2 | # SCSI Device Handler | ||
3 | # | ||
4 | obj-$(CONFIG_SCSI_DH) += scsi_dh.o | ||
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c new file mode 100644 index 000000000000..ab6c21cd9689 --- /dev/null +++ b/drivers/scsi/device_handler/scsi_dh.c | |||
@@ -0,0 +1,162 @@ | |||
1 | /* | ||
2 | * SCSI device handler infrastruture. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License as published by the | ||
6 | * Free Software Foundation; either version 2 of the License, or (at your | ||
7 | * option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but | ||
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | * General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along | ||
15 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | * | ||
18 | * Copyright IBM Corporation, 2007 | ||
19 | * Authors: | ||
20 | * Chandra Seetharaman <sekharan@us.ibm.com> | ||
21 | * Mike Anderson <andmike@linux.vnet.ibm.com> | ||
22 | */ | ||
23 | |||
24 | #include <scsi/scsi_dh.h> | ||
25 | #include "../scsi_priv.h" | ||
26 | |||
27 | static DEFINE_SPINLOCK(list_lock); | ||
28 | static LIST_HEAD(scsi_dh_list); | ||
29 | |||
30 | static struct scsi_device_handler *get_device_handler(const char *name) | ||
31 | { | ||
32 | struct scsi_device_handler *tmp, *found = NULL; | ||
33 | |||
34 | spin_lock(&list_lock); | ||
35 | list_for_each_entry(tmp, &scsi_dh_list, list) { | ||
36 | if (!strcmp(tmp->name, name)) { | ||
37 | found = tmp; | ||
38 | break; | ||
39 | } | ||
40 | } | ||
41 | spin_unlock(&list_lock); | ||
42 | return found; | ||
43 | } | ||
44 | |||
45 | static int scsi_dh_notifier_add(struct device *dev, void *data) | ||
46 | { | ||
47 | struct scsi_device_handler *scsi_dh = data; | ||
48 | |||
49 | scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_ADD_DEVICE, dev); | ||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | /* | ||
54 | * scsi_register_device_handler - register a device handler personality | ||
55 | * module. | ||
56 | * @scsi_dh - device handler to be registered. | ||
57 | * | ||
58 | * Returns 0 on success, -EBUSY if handler already registered. | ||
59 | */ | ||
60 | int scsi_register_device_handler(struct scsi_device_handler *scsi_dh) | ||
61 | { | ||
62 | int ret = -EBUSY; | ||
63 | struct scsi_device_handler *tmp; | ||
64 | |||
65 | tmp = get_device_handler(scsi_dh->name); | ||
66 | if (tmp) | ||
67 | goto done; | ||
68 | |||
69 | ret = bus_register_notifier(&scsi_bus_type, &scsi_dh->nb); | ||
70 | |||
71 | bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add); | ||
72 | spin_lock(&list_lock); | ||
73 | list_add(&scsi_dh->list, &scsi_dh_list); | ||
74 | spin_unlock(&list_lock); | ||
75 | |||
76 | done: | ||
77 | return ret; | ||
78 | } | ||
79 | EXPORT_SYMBOL_GPL(scsi_register_device_handler); | ||
80 | |||
81 | static int scsi_dh_notifier_remove(struct device *dev, void *data) | ||
82 | { | ||
83 | struct scsi_device_handler *scsi_dh = data; | ||
84 | |||
85 | scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_DEL_DEVICE, dev); | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * scsi_unregister_device_handler - register a device handler personality | ||
91 | * module. | ||
92 | * @scsi_dh - device handler to be unregistered. | ||
93 | * | ||
94 | * Returns 0 on success, -ENODEV if handler not registered. | ||
95 | */ | ||
96 | int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) | ||
97 | { | ||
98 | int ret = -ENODEV; | ||
99 | struct scsi_device_handler *tmp; | ||
100 | |||
101 | tmp = get_device_handler(scsi_dh->name); | ||
102 | if (!tmp) | ||
103 | goto done; | ||
104 | |||
105 | ret = bus_unregister_notifier(&scsi_bus_type, &scsi_dh->nb); | ||
106 | |||
107 | bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, | ||
108 | scsi_dh_notifier_remove); | ||
109 | spin_lock(&list_lock); | ||
110 | list_del(&scsi_dh->list); | ||
111 | spin_unlock(&list_lock); | ||
112 | |||
113 | done: | ||
114 | return ret; | ||
115 | } | ||
116 | EXPORT_SYMBOL_GPL(scsi_unregister_device_handler); | ||
117 | |||
118 | /* | ||
119 | * scsi_dh_activate - activate the path associated with the scsi_device | ||
120 | * corresponding to the given request queue. | ||
121 | * @q - Request queue that is associated with the scsi_device to be | ||
122 | * activated. | ||
123 | */ | ||
124 | int scsi_dh_activate(struct request_queue *q) | ||
125 | { | ||
126 | int err = 0; | ||
127 | unsigned long flags; | ||
128 | struct scsi_device *sdev; | ||
129 | struct scsi_device_handler *scsi_dh = NULL; | ||
130 | |||
131 | spin_lock_irqsave(q->queue_lock, flags); | ||
132 | sdev = q->queuedata; | ||
133 | if (sdev && sdev->scsi_dh_data) | ||
134 | scsi_dh = sdev->scsi_dh_data->scsi_dh; | ||
135 | if (!scsi_dh || !get_device(&sdev->sdev_gendev)) | ||
136 | err = SCSI_DH_NOSYS; | ||
137 | spin_unlock_irqrestore(q->queue_lock, flags); | ||
138 | |||
139 | if (err) | ||
140 | return err; | ||
141 | |||
142 | if (scsi_dh->activate) | ||
143 | err = scsi_dh->activate(sdev); | ||
144 | put_device(&sdev->sdev_gendev); | ||
145 | return err; | ||
146 | } | ||
147 | EXPORT_SYMBOL_GPL(scsi_dh_activate); | ||
148 | |||
149 | /* | ||
150 | * scsi_dh_handler_exist - Return TRUE(1) if a device handler exists for | ||
151 | * the given name. FALSE(0) otherwise. | ||
152 | * @name - name of the device handler. | ||
153 | */ | ||
154 | int scsi_dh_handler_exist(const char *name) | ||
155 | { | ||
156 | return (get_device_handler(name) != NULL); | ||
157 | } | ||
158 | EXPORT_SYMBOL_GPL(scsi_dh_handler_exist); | ||
159 | |||
160 | MODULE_DESCRIPTION("SCSI device handler"); | ||
161 | MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>"); | ||
162 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index eaf5a8add1ba..006a95916f72 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c | |||
@@ -298,6 +298,7 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost, | |||
298 | */ | 298 | */ |
299 | static int scsi_check_sense(struct scsi_cmnd *scmd) | 299 | static int scsi_check_sense(struct scsi_cmnd *scmd) |
300 | { | 300 | { |
301 | struct scsi_device *sdev = scmd->device; | ||
301 | struct scsi_sense_hdr sshdr; | 302 | struct scsi_sense_hdr sshdr; |
302 | 303 | ||
303 | if (! scsi_command_normalize_sense(scmd, &sshdr)) | 304 | if (! scsi_command_normalize_sense(scmd, &sshdr)) |
@@ -306,6 +307,16 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) | |||
306 | if (scsi_sense_is_deferred(&sshdr)) | 307 | if (scsi_sense_is_deferred(&sshdr)) |
307 | return NEEDS_RETRY; | 308 | return NEEDS_RETRY; |
308 | 309 | ||
310 | if (sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh && | ||
311 | sdev->scsi_dh_data->scsi_dh->check_sense) { | ||
312 | int rc; | ||
313 | |||
314 | rc = sdev->scsi_dh_data->scsi_dh->check_sense(sdev, &sshdr); | ||
315 | if (rc != SCSI_RETURN_NOT_HANDLED) | ||
316 | return rc; | ||
317 | /* handler does not care. Drop down to default handling */ | ||
318 | } | ||
319 | |||
309 | /* | 320 | /* |
310 | * Previous logic looked for FILEMARK, EOM or ILI which are | 321 | * Previous logic looked for FILEMARK, EOM or ILI which are |
311 | * mainly associated with tapes and returned SUCCESS. | 322 | * mainly associated with tapes and returned SUCCESS. |
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index a82d2fe80fb5..033c58a65f50 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c | |||
@@ -1160,6 +1160,14 @@ int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req) | |||
1160 | 1160 | ||
1161 | if (ret != BLKPREP_OK) | 1161 | if (ret != BLKPREP_OK) |
1162 | return ret; | 1162 | return ret; |
1163 | |||
1164 | if (unlikely(sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh | ||
1165 | && sdev->scsi_dh_data->scsi_dh->prep_fn)) { | ||
1166 | ret = sdev->scsi_dh_data->scsi_dh->prep_fn(sdev, req); | ||
1167 | if (ret != BLKPREP_OK) | ||
1168 | return ret; | ||
1169 | } | ||
1170 | |||
1163 | /* | 1171 | /* |
1164 | * Filesystem requests must transfer data. | 1172 | * Filesystem requests must transfer data. |
1165 | */ | 1173 | */ |
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 93d2b6714453..b6e561059779 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c | |||
@@ -439,6 +439,7 @@ struct bus_type scsi_bus_type = { | |||
439 | .resume = scsi_bus_resume, | 439 | .resume = scsi_bus_resume, |
440 | .remove = scsi_bus_remove, | 440 | .remove = scsi_bus_remove, |
441 | }; | 441 | }; |
442 | EXPORT_SYMBOL_GPL(scsi_bus_type); | ||
442 | 443 | ||
443 | int scsi_sysfs_register(void) | 444 | int scsi_sysfs_register(void) |
444 | { | 445 | { |
diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index 32742c4563de..2b5b9356c314 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h | |||
@@ -400,6 +400,7 @@ struct scsi_lun { | |||
400 | #define SOFT_ERROR 0x2005 | 400 | #define SOFT_ERROR 0x2005 |
401 | #define ADD_TO_MLQUEUE 0x2006 | 401 | #define ADD_TO_MLQUEUE 0x2006 |
402 | #define TIMEOUT_ERROR 0x2007 | 402 | #define TIMEOUT_ERROR 0x2007 |
403 | #define SCSI_RETURN_NOT_HANDLED 0x2008 | ||
403 | 404 | ||
404 | /* | 405 | /* |
405 | * Midlevel queue return values. | 406 | * Midlevel queue return values. |
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index f6a9fe0ef09c..06b979f105b7 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h | |||
@@ -161,9 +161,29 @@ struct scsi_device { | |||
161 | 161 | ||
162 | struct execute_work ew; /* used to get process context on put */ | 162 | struct execute_work ew; /* used to get process context on put */ |
163 | 163 | ||
164 | struct scsi_dh_data *scsi_dh_data; | ||
164 | enum scsi_device_state sdev_state; | 165 | enum scsi_device_state sdev_state; |
165 | unsigned long sdev_data[0]; | 166 | unsigned long sdev_data[0]; |
166 | } __attribute__((aligned(sizeof(unsigned long)))); | 167 | } __attribute__((aligned(sizeof(unsigned long)))); |
168 | |||
169 | struct scsi_device_handler { | ||
170 | /* Used by the infrastructure */ | ||
171 | struct list_head list; /* list of scsi_device_handlers */ | ||
172 | struct notifier_block nb; | ||
173 | |||
174 | /* Filled by the hardware handler */ | ||
175 | struct module *module; | ||
176 | const char *name; | ||
177 | int (*check_sense)(struct scsi_device *, struct scsi_sense_hdr *); | ||
178 | int (*activate)(struct scsi_device *); | ||
179 | int (*prep_fn)(struct scsi_device *, struct request *); | ||
180 | }; | ||
181 | |||
182 | struct scsi_dh_data { | ||
183 | struct scsi_device_handler *scsi_dh; | ||
184 | char buf[0]; | ||
185 | }; | ||
186 | |||
167 | #define to_scsi_device(d) \ | 187 | #define to_scsi_device(d) \ |
168 | container_of(d, struct scsi_device, sdev_gendev) | 188 | container_of(d, struct scsi_device, sdev_gendev) |
169 | #define class_to_sdev(d) \ | 189 | #define class_to_sdev(d) \ |
@@ -230,7 +250,9 @@ extern struct scsi_device *__scsi_add_device(struct Scsi_Host *, | |||
230 | uint, uint, uint, void *hostdata); | 250 | uint, uint, uint, void *hostdata); |
231 | extern int scsi_add_device(struct Scsi_Host *host, uint channel, | 251 | extern int scsi_add_device(struct Scsi_Host *host, uint channel, |
232 | uint target, uint lun); | 252 | uint target, uint lun); |
253 | extern int scsi_register_device_handler(struct scsi_device_handler *scsi_dh); | ||
233 | extern void scsi_remove_device(struct scsi_device *); | 254 | extern void scsi_remove_device(struct scsi_device *); |
255 | extern int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh); | ||
234 | 256 | ||
235 | extern int scsi_device_get(struct scsi_device *); | 257 | extern int scsi_device_get(struct scsi_device *); |
236 | extern void scsi_device_put(struct scsi_device *); | 258 | extern void scsi_device_put(struct scsi_device *); |
diff --git a/include/scsi/scsi_dh.h b/include/scsi/scsi_dh.h new file mode 100644 index 000000000000..04d0d8495c83 --- /dev/null +++ b/include/scsi/scsi_dh.h | |||
@@ -0,0 +1,59 @@ | |||
1 | /* | ||
2 | * Header file for SCSI device handler infrastruture. | ||
3 | * | ||
4 | * Modified version of patches posted by Mike Christie <michaelc@cs.wisc.edu> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the | ||
8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
9 | * option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, but | ||
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | * General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
19 | * | ||
20 | * Copyright IBM Corporation, 2007 | ||
21 | * Authors: | ||
22 | * Chandra Seetharaman <sekharan@us.ibm.com> | ||
23 | * Mike Anderson <andmike@linux.vnet.ibm.com> | ||
24 | */ | ||
25 | |||
26 | #include <scsi/scsi_device.h> | ||
27 | |||
28 | enum { | ||
29 | SCSI_DH_OK = 0, | ||
30 | /* | ||
31 | * device errors | ||
32 | */ | ||
33 | SCSI_DH_DEV_FAILED, /* generic device error */ | ||
34 | SCSI_DH_DEV_TEMP_BUSY, | ||
35 | SCSI_DH_DEVICE_MAX, /* max device blkerr definition */ | ||
36 | |||
37 | /* | ||
38 | * transport errors | ||
39 | */ | ||
40 | SCSI_DH_NOTCONN = SCSI_DH_DEVICE_MAX + 1, | ||
41 | SCSI_DH_CONN_FAILURE, | ||
42 | SCSI_DH_TRANSPORT_MAX, /* max transport blkerr definition */ | ||
43 | |||
44 | /* | ||
45 | * driver and generic errors | ||
46 | */ | ||
47 | SCSI_DH_IO = SCSI_DH_TRANSPORT_MAX + 1, /* generic error */ | ||
48 | SCSI_DH_INVALID_IO, | ||
49 | SCSI_DH_RETRY, /* retry the req, but not immediately */ | ||
50 | SCSI_DH_IMM_RETRY, /* immediately retry the req */ | ||
51 | SCSI_DH_TIMED_OUT, | ||
52 | SCSI_DH_RES_TEMP_UNAVAIL, | ||
53 | SCSI_DH_DEV_OFFLINED, | ||
54 | SCSI_DH_NOSYS, | ||
55 | SCSI_DH_DRIVER_MAX, | ||
56 | }; | ||
57 | |||
58 | extern int scsi_dh_activate(struct request_queue *); | ||
59 | extern int scsi_dh_handler_exist(const char *); | ||