Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
a6a8d9f8 CS |
2 | /* |
3 | * SCSI device handler infrastruture. | |
4 | * | |
a6a8d9f8 CS |
5 | * Copyright IBM Corporation, 2007 |
6 | * Authors: | |
7 | * Chandra Seetharaman <sekharan@us.ibm.com> | |
8 | * Mike Anderson <andmike@linux.vnet.ibm.com> | |
9 | */ | |
10 | ||
5a0e3ad6 | 11 | #include <linux/slab.h> |
acf3368f | 12 | #include <linux/module.h> |
a6a8d9f8 | 13 | #include <scsi/scsi_dh.h> |
daaa858b | 14 | #include "scsi_priv.h" |
a6a8d9f8 CS |
15 | |
16 | static DEFINE_SPINLOCK(list_lock); | |
17 | static LIST_HEAD(scsi_dh_list); | |
18 | ||
d95dbff2 CH |
19 | struct scsi_dh_blist { |
20 | const char *vendor; | |
21 | const char *model; | |
22 | const char *driver; | |
23 | }; | |
24 | ||
25 | static const struct scsi_dh_blist scsi_dh_blist[] = { | |
0ba43a81 XVP |
26 | {"DGC", "RAID", "emc" }, |
27 | {"DGC", "DISK", "emc" }, | |
28 | {"DGC", "VRAID", "emc" }, | |
d95dbff2 CH |
29 | |
30 | {"COMPAQ", "MSA1000 VOLUME", "hp_sw" }, | |
31 | {"COMPAQ", "HSV110", "hp_sw" }, | |
32 | {"HP", "HSV100", "hp_sw"}, | |
33 | {"DEC", "HSG80", "hp_sw"}, | |
34 | ||
35 | {"IBM", "1722", "rdac", }, | |
36 | {"IBM", "1724", "rdac", }, | |
37 | {"IBM", "1726", "rdac", }, | |
38 | {"IBM", "1742", "rdac", }, | |
39 | {"IBM", "1745", "rdac", }, | |
40 | {"IBM", "1746", "rdac", }, | |
41 | {"IBM", "1813", "rdac", }, | |
42 | {"IBM", "1814", "rdac", }, | |
43 | {"IBM", "1815", "rdac", }, | |
44 | {"IBM", "1818", "rdac", }, | |
45 | {"IBM", "3526", "rdac", }, | |
4b3aec2b XVP |
46 | {"IBM", "3542", "rdac", }, |
47 | {"IBM", "3552", "rdac", }, | |
37b37d26 XVP |
48 | {"SGI", "TP9300", "rdac", }, |
49 | {"SGI", "TP9400", "rdac", }, | |
50 | {"SGI", "TP9500", "rdac", }, | |
51 | {"SGI", "TP9700", "rdac", }, | |
d95dbff2 | 52 | {"SGI", "IS", "rdac", }, |
4b3aec2b | 53 | {"STK", "OPENstorage", "rdac", }, |
d95dbff2 | 54 | {"STK", "FLEXLINE 380", "rdac", }, |
4b3aec2b | 55 | {"STK", "BladeCtlr", "rdac", }, |
d95dbff2 CH |
56 | {"SUN", "CSM", "rdac", }, |
57 | {"SUN", "LCSM100", "rdac", }, | |
58 | {"SUN", "STK6580_6780", "rdac", }, | |
59 | {"SUN", "SUN_6180", "rdac", }, | |
60 | {"SUN", "ArrayStorage", "rdac", }, | |
61 | {"DELL", "MD3", "rdac", }, | |
62 | {"NETAPP", "INF-01-00", "rdac", }, | |
63 | {"LSI", "INF-01-00", "rdac", }, | |
64 | {"ENGENIO", "INF-01-00", "rdac", }, | |
1cb1d2c6 | 65 | {"LENOVO", "DE_Series", "rdac", }, |
d95dbff2 CH |
66 | {NULL, NULL, NULL }, |
67 | }; | |
68 | ||
69 | static const char * | |
70 | scsi_dh_find_driver(struct scsi_device *sdev) | |
71 | { | |
72 | const struct scsi_dh_blist *b; | |
73 | ||
74 | if (scsi_device_tpgs(sdev)) | |
75 | return "alua"; | |
76 | ||
77 | for (b = scsi_dh_blist; b->vendor; b++) { | |
78 | if (!strncmp(sdev->vendor, b->vendor, strlen(b->vendor)) && | |
79 | !strncmp(sdev->model, b->model, strlen(b->model))) { | |
80 | return b->driver; | |
81 | } | |
82 | } | |
83 | return NULL; | |
84 | } | |
85 | ||
86 | ||
566079c8 | 87 | static struct scsi_device_handler *__scsi_dh_lookup(const char *name) |
a6a8d9f8 CS |
88 | { |
89 | struct scsi_device_handler *tmp, *found = NULL; | |
90 | ||
91 | spin_lock(&list_lock); | |
92 | list_for_each_entry(tmp, &scsi_dh_list, list) { | |
765cbc6d | 93 | if (!strncmp(tmp->name, name, strlen(tmp->name))) { |
a6a8d9f8 CS |
94 | found = tmp; |
95 | break; | |
96 | } | |
97 | } | |
98 | spin_unlock(&list_lock); | |
99 | return found; | |
100 | } | |
101 | ||
566079c8 CH |
102 | static struct scsi_device_handler *scsi_dh_lookup(const char *name) |
103 | { | |
104 | struct scsi_device_handler *dh; | |
105 | ||
2ee5671e JT |
106 | if (!name || strlen(name) == 0) |
107 | return NULL; | |
108 | ||
566079c8 CH |
109 | dh = __scsi_dh_lookup(name); |
110 | if (!dh) { | |
1378889c | 111 | request_module("scsi_dh_%s", name); |
566079c8 CH |
112 | dh = __scsi_dh_lookup(name); |
113 | } | |
114 | ||
115 | return dh; | |
116 | } | |
117 | ||
a6a8d9f8 | 118 | /* |
765cbc6d HR |
119 | * scsi_dh_handler_attach - Attach a device handler to a device |
120 | * @sdev - SCSI device the device handler should attach to | |
121 | * @scsi_dh - The device handler to attach | |
122 | */ | |
123 | static int scsi_dh_handler_attach(struct scsi_device *sdev, | |
124 | struct scsi_device_handler *scsi_dh) | |
125 | { | |
2a8f7a03 | 126 | int error, ret = 0; |
765cbc6d | 127 | |
1f12ffa5 CH |
128 | if (!try_module_get(scsi_dh->module)) |
129 | return -EINVAL; | |
27c888f0 | 130 | |
ee14c674 | 131 | error = scsi_dh->attach(sdev); |
2a8f7a03 HR |
132 | if (error != SCSI_DH_OK) { |
133 | switch (error) { | |
134 | case SCSI_DH_NOMEM: | |
135 | ret = -ENOMEM; | |
136 | break; | |
137 | case SCSI_DH_RES_TEMP_UNAVAIL: | |
138 | ret = -EAGAIN; | |
139 | break; | |
2930f817 HR |
140 | case SCSI_DH_DEV_UNSUPP: |
141 | case SCSI_DH_NOSYS: | |
142 | ret = -ENODEV; | |
143 | break; | |
2a8f7a03 HR |
144 | default: |
145 | ret = -EINVAL; | |
146 | break; | |
147 | } | |
2930f817 HR |
148 | if (ret != -ENODEV) |
149 | sdev_printk(KERN_ERR, sdev, "%s: Attach failed (%d)\n", | |
150 | scsi_dh->name, error); | |
1f12ffa5 | 151 | module_put(scsi_dh->module); |
ee14c674 CH |
152 | } else |
153 | sdev->handler = scsi_dh; | |
1d520328 | 154 | |
2a8f7a03 | 155 | return ret; |
765cbc6d HR |
156 | } |
157 | ||
1bab0de0 CH |
158 | /* |
159 | * scsi_dh_handler_detach - Detach a device handler from a device | |
160 | * @sdev - SCSI device the device handler should be detached from | |
161 | */ | |
162 | static void scsi_dh_handler_detach(struct scsi_device *sdev) | |
6c10db72 | 163 | { |
ee14c674 CH |
164 | sdev->handler->detach(sdev); |
165 | sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", sdev->handler->name); | |
166 | module_put(sdev->handler->module); | |
6c10db72 CS |
167 | } |
168 | ||
2930f817 | 169 | void scsi_dh_add_device(struct scsi_device *sdev) |
4c05ae52 | 170 | { |
d95dbff2 CH |
171 | struct scsi_device_handler *devinfo = NULL; |
172 | const char *drv; | |
4c05ae52 | 173 | |
d95dbff2 CH |
174 | drv = scsi_dh_find_driver(sdev); |
175 | if (drv) | |
d6a32b98 | 176 | devinfo = __scsi_dh_lookup(drv); |
2930f817 HR |
177 | /* |
178 | * device_handler is optional, so ignore errors | |
179 | * from scsi_dh_handler_attach() | |
180 | */ | |
086b91d0 | 181 | if (devinfo) |
2930f817 | 182 | (void)scsi_dh_handler_attach(sdev, devinfo); |
a6a8d9f8 | 183 | } |
a6a8d9f8 | 184 | |
23695e41 | 185 | void scsi_dh_release_device(struct scsi_device *sdev) |
765cbc6d | 186 | { |
ee14c674 | 187 | if (sdev->handler) |
1bab0de0 | 188 | scsi_dh_handler_detach(sdev); |
23695e41 JN |
189 | } |
190 | ||
765cbc6d HR |
191 | /* |
192 | * scsi_register_device_handler - register a device handler personality | |
193 | * module. | |
194 | * @scsi_dh - device handler to be registered. | |
195 | * | |
196 | * Returns 0 on success, -EBUSY if handler already registered. | |
197 | */ | |
198 | int scsi_register_device_handler(struct scsi_device_handler *scsi_dh) | |
199 | { | |
566079c8 | 200 | if (__scsi_dh_lookup(scsi_dh->name)) |
765cbc6d HR |
201 | return -EBUSY; |
202 | ||
1f12ffa5 CH |
203 | if (!scsi_dh->attach || !scsi_dh->detach) |
204 | return -EINVAL; | |
205 | ||
765cbc6d HR |
206 | spin_lock(&list_lock); |
207 | list_add(&scsi_dh->list, &scsi_dh_list); | |
208 | spin_unlock(&list_lock); | |
940d7faa | 209 | |
765cbc6d HR |
210 | printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name); |
211 | ||
212 | return SCSI_DH_OK; | |
213 | } | |
214 | EXPORT_SYMBOL_GPL(scsi_register_device_handler); | |
215 | ||
a6a8d9f8 CS |
216 | /* |
217 | * scsi_unregister_device_handler - register a device handler personality | |
218 | * module. | |
219 | * @scsi_dh - device handler to be unregistered. | |
220 | * | |
221 | * Returns 0 on success, -ENODEV if handler not registered. | |
222 | */ | |
223 | int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) | |
224 | { | |
566079c8 | 225 | if (!__scsi_dh_lookup(scsi_dh->name)) |
765cbc6d | 226 | return -ENODEV; |
a6a8d9f8 | 227 | |
a6a8d9f8 CS |
228 | spin_lock(&list_lock); |
229 | list_del(&scsi_dh->list); | |
230 | spin_unlock(&list_lock); | |
765cbc6d | 231 | printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name); |
a6a8d9f8 | 232 | |
765cbc6d | 233 | return SCSI_DH_OK; |
a6a8d9f8 CS |
234 | } |
235 | EXPORT_SYMBOL_GPL(scsi_unregister_device_handler); | |
236 | ||
237 | /* | |
238 | * scsi_dh_activate - activate the path associated with the scsi_device | |
239 | * corresponding to the given request queue. | |
3ae31f6a CS |
240 | * Returns immediately without waiting for activation to be completed. |
241 | * @q - Request queue that is associated with the scsi_device to be | |
242 | * activated. | |
243 | * @fn - Function to be called upon completion of the activation. | |
244 | * Function fn is called with data (below) and the error code. | |
245 | * Function fn may be called from the same calling context. So, | |
246 | * do not hold the lock in the caller which may be needed in fn. | |
247 | * @data - data passed to the function fn upon completion. | |
248 | * | |
a6a8d9f8 | 249 | */ |
3ae31f6a | 250 | int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) |
a6a8d9f8 | 251 | { |
a6a8d9f8 | 252 | struct scsi_device *sdev; |
e959ed9a | 253 | int err = SCSI_DH_NOSYS; |
a6a8d9f8 | 254 | |
857de6e0 | 255 | sdev = scsi_device_from_queue(q); |
a18a920c | 256 | if (!sdev) { |
a18a920c MB |
257 | if (fn) |
258 | fn(data, err); | |
259 | return err; | |
260 | } | |
261 | ||
e959ed9a CH |
262 | if (!sdev->handler) |
263 | goto out_fn; | |
710105fd | 264 | err = SCSI_DH_NOTCONN; |
e959ed9a | 265 | if (sdev->sdev_state == SDEV_CANCEL || |
db422318 | 266 | sdev->sdev_state == SDEV_DEL) |
e959ed9a | 267 | goto out_fn; |
a6a8d9f8 | 268 | |
e959ed9a CH |
269 | err = SCSI_DH_DEV_OFFLINED; |
270 | if (sdev->sdev_state == SDEV_OFFLINE) | |
271 | goto out_fn; | |
a6a8d9f8 | 272 | |
ee14c674 CH |
273 | if (sdev->handler->activate) |
274 | err = sdev->handler->activate(sdev, fn, data); | |
e959ed9a CH |
275 | |
276 | out_put_device: | |
277 | put_device(&sdev->sdev_gendev); | |
a6a8d9f8 | 278 | return err; |
e959ed9a CH |
279 | |
280 | out_fn: | |
281 | if (fn) | |
282 | fn(data, err); | |
283 | goto out_put_device; | |
a6a8d9f8 CS |
284 | } |
285 | EXPORT_SYMBOL_GPL(scsi_dh_activate); | |
286 | ||
18ee70c9 CS |
287 | /* |
288 | * scsi_dh_set_params - set the parameters for the device as per the | |
289 | * string specified in params. | |
290 | * @q - Request queue that is associated with the scsi_device for | |
291 | * which the parameters to be set. | |
292 | * @params - parameters in the following format | |
293 | * "no_of_params\0param1\0param2\0param3\0...\0" | |
294 | * for example, string for 2 parameters with value 10 and 21 | |
295 | * is specified as "2\010\021\0". | |
296 | */ | |
297 | int scsi_dh_set_params(struct request_queue *q, const char *params) | |
298 | { | |
18ee70c9 | 299 | struct scsi_device *sdev; |
e959ed9a | 300 | int err = -SCSI_DH_NOSYS; |
18ee70c9 | 301 | |
857de6e0 | 302 | sdev = scsi_device_from_queue(q); |
e959ed9a | 303 | if (!sdev) |
18ee70c9 | 304 | return err; |
e959ed9a CH |
305 | |
306 | if (sdev->handler && sdev->handler->set_params) | |
307 | err = sdev->handler->set_params(sdev, params); | |
18ee70c9 CS |
308 | put_device(&sdev->sdev_gendev); |
309 | return err; | |
310 | } | |
311 | EXPORT_SYMBOL_GPL(scsi_dh_set_params); | |
312 | ||
ae11b1b3 | 313 | /* |
2a9ab40f | 314 | * scsi_dh_attach - Attach device handler |
7e8a74b1 MS |
315 | * @q - Request queue that is associated with the scsi_device |
316 | * the handler should be attached to | |
ae11b1b3 HR |
317 | * @name - name of the handler to attach |
318 | */ | |
319 | int scsi_dh_attach(struct request_queue *q, const char *name) | |
320 | { | |
ae11b1b3 HR |
321 | struct scsi_device *sdev; |
322 | struct scsi_device_handler *scsi_dh; | |
323 | int err = 0; | |
324 | ||
857de6e0 | 325 | sdev = scsi_device_from_queue(q); |
e959ed9a CH |
326 | if (!sdev) |
327 | return -ENODEV; | |
ae11b1b3 | 328 | |
e959ed9a CH |
329 | scsi_dh = scsi_dh_lookup(name); |
330 | if (!scsi_dh) { | |
331 | err = -EINVAL; | |
332 | goto out_put_device; | |
333 | } | |
ae11b1b3 | 334 | |
ee14c674 CH |
335 | if (sdev->handler) { |
336 | if (sdev->handler != scsi_dh) | |
1bab0de0 CH |
337 | err = -EBUSY; |
338 | goto out_put_device; | |
ae11b1b3 | 339 | } |
1bab0de0 CH |
340 | |
341 | err = scsi_dh_handler_attach(sdev, scsi_dh); | |
342 | ||
343 | out_put_device: | |
ae11b1b3 | 344 | put_device(&sdev->sdev_gendev); |
1bab0de0 | 345 | return err; |
ae11b1b3 | 346 | } |
1bab0de0 | 347 | EXPORT_SYMBOL_GPL(scsi_dh_attach); |
ae11b1b3 | 348 | |
7e8a74b1 MS |
349 | /* |
350 | * scsi_dh_attached_handler_name - Get attached device handler's name | |
351 | * @q - Request queue that is associated with the scsi_device | |
352 | * that may have a device handler attached | |
353 | * @gfp - the GFP mask used in the kmalloc() call when allocating memory | |
354 | * | |
355 | * Returns name of attached handler, NULL if no handler is attached. | |
356 | * Caller must take care to free the returned string. | |
357 | */ | |
358 | const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp) | |
359 | { | |
7e8a74b1 MS |
360 | struct scsi_device *sdev; |
361 | const char *handler_name = NULL; | |
362 | ||
857de6e0 | 363 | sdev = scsi_device_from_queue(q); |
7e8a74b1 MS |
364 | if (!sdev) |
365 | return NULL; | |
366 | ||
ee14c674 CH |
367 | if (sdev->handler) |
368 | handler_name = kstrdup(sdev->handler->name, gfp); | |
7e8a74b1 MS |
369 | put_device(&sdev->sdev_gendev); |
370 | return handler_name; | |
371 | } | |
372 | EXPORT_SYMBOL_GPL(scsi_dh_attached_handler_name); |