Commit | Line | Data |
---|---|---|
a6a8d9f8 CS |
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"); |