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