diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-27 13:04:52 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-27 13:04:52 -0400 |
| commit | 211c8d4942edf2f3337820dda101da6b13c8a19a (patch) | |
| tree | a2a107acb80a61623d27fa3affe813eab5f4b2a3 /drivers/scsi/device_handler | |
| parent | 7a82323da3d21ea59a0509569fc5c7bc5aa7eed7 (diff) | |
| parent | cadbd4a5e36dde7e6c49b587b2c419103c0b7218 (diff) | |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6: (59 commits)
[SCSI] replace __FUNCTION__ with __func__
[SCSI] extend the last_sector_bug flag to cover more sectors
[SCSI] qla2xxx: Update version number to 8.02.01-k6.
[SCSI] qla2xxx: Additional NPIV corrections.
[SCSI] qla2xxx: suppress uninitialized-var warning
[SCSI] qla2xxx: use memory_read_from_buffer()
[SCSI] qla2xxx: Issue proper ISP callbacks during stop-firmware.
[SCSI] ch: fix ch_remove oops
[SCSI] 3w-9xxx: add MSI support and misc fixes
[SCSI] scsi_lib: use blk_rq_tagged in scsi_request_fn
[SCSI] ibmvfc: Update driver version to 1.0.1
[SCSI] ibmvfc: Add ADISC support
[SCSI] ibmvfc: Miscellaneous fixes
[SCSI] ibmvfc: Fix hang on module removal
[SCSI] ibmvfc: Target refcounting fixes
[SCSI] ibmvfc: Reduce unnecessary log noise
[SCSI] sym53c8xx: free luntbl in sym_hcb_free
[SCSI] scsi_scan.c: Release mutex in error handling code
[SCSI] scsi_eh_prep_cmnd should save scmd->underflow
[SCSI] sd: Support for SCSI disk (SBC) Data Integrity Field
...
Diffstat (limited to 'drivers/scsi/device_handler')
| -rw-r--r-- | drivers/scsi/device_handler/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/Makefile | 1 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh.c | 446 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh_alua.c | 802 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh_emc.c | 644 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh_hp_sw.c | 348 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh_rdac.c | 262 |
7 files changed, 2028 insertions, 483 deletions
diff --git a/drivers/scsi/device_handler/Kconfig b/drivers/scsi/device_handler/Kconfig index 2adc0f666b68..67070257919f 100644 --- a/drivers/scsi/device_handler/Kconfig +++ b/drivers/scsi/device_handler/Kconfig | |||
| @@ -30,3 +30,11 @@ config SCSI_DH_EMC | |||
| 30 | depends on SCSI_DH | 30 | depends on SCSI_DH |
| 31 | help | 31 | help |
| 32 | If you have a EMC CLARiiON select y. Otherwise, say N. | 32 | If you have a EMC CLARiiON select y. Otherwise, say N. |
| 33 | |||
| 34 | config SCSI_DH_ALUA | ||
| 35 | tristate "SPC-3 ALUA Device Handler (EXPERIMENTAL)" | ||
| 36 | depends on SCSI_DH && EXPERIMENTAL | ||
| 37 | help | ||
| 38 | SCSI Device handler for generic SPC-3 Asymmetric Logical Unit | ||
| 39 | Access (ALUA). | ||
| 40 | |||
diff --git a/drivers/scsi/device_handler/Makefile b/drivers/scsi/device_handler/Makefile index 35272e93b1c8..e1d2ea083e15 100644 --- a/drivers/scsi/device_handler/Makefile +++ b/drivers/scsi/device_handler/Makefile | |||
| @@ -5,3 +5,4 @@ obj-$(CONFIG_SCSI_DH) += scsi_dh.o | |||
| 5 | obj-$(CONFIG_SCSI_DH_RDAC) += scsi_dh_rdac.o | 5 | obj-$(CONFIG_SCSI_DH_RDAC) += scsi_dh_rdac.o |
| 6 | obj-$(CONFIG_SCSI_DH_HP_SW) += scsi_dh_hp_sw.o | 6 | obj-$(CONFIG_SCSI_DH_HP_SW) += scsi_dh_hp_sw.o |
| 7 | obj-$(CONFIG_SCSI_DH_EMC) += scsi_dh_emc.o | 7 | obj-$(CONFIG_SCSI_DH_EMC) += scsi_dh_emc.o |
| 8 | obj-$(CONFIG_SCSI_DH_ALUA) += scsi_dh_alua.o | ||
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index ab6c21cd9689..a518f2eff19a 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c | |||
| @@ -24,8 +24,16 @@ | |||
| 24 | #include <scsi/scsi_dh.h> | 24 | #include <scsi/scsi_dh.h> |
| 25 | #include "../scsi_priv.h" | 25 | #include "../scsi_priv.h" |
| 26 | 26 | ||
| 27 | struct scsi_dh_devinfo_list { | ||
| 28 | struct list_head node; | ||
| 29 | char vendor[9]; | ||
| 30 | char model[17]; | ||
| 31 | struct scsi_device_handler *handler; | ||
| 32 | }; | ||
| 33 | |||
| 27 | static DEFINE_SPINLOCK(list_lock); | 34 | static DEFINE_SPINLOCK(list_lock); |
| 28 | static LIST_HEAD(scsi_dh_list); | 35 | static LIST_HEAD(scsi_dh_list); |
| 36 | static LIST_HEAD(scsi_dh_dev_list); | ||
| 29 | 37 | ||
| 30 | static struct scsi_device_handler *get_device_handler(const char *name) | 38 | static struct scsi_device_handler *get_device_handler(const char *name) |
| 31 | { | 39 | { |
| @@ -33,7 +41,7 @@ static struct scsi_device_handler *get_device_handler(const char *name) | |||
| 33 | 41 | ||
| 34 | spin_lock(&list_lock); | 42 | spin_lock(&list_lock); |
| 35 | list_for_each_entry(tmp, &scsi_dh_list, list) { | 43 | list_for_each_entry(tmp, &scsi_dh_list, list) { |
| 36 | if (!strcmp(tmp->name, name)) { | 44 | if (!strncmp(tmp->name, name, strlen(tmp->name))) { |
| 37 | found = tmp; | 45 | found = tmp; |
| 38 | break; | 46 | break; |
| 39 | } | 47 | } |
| @@ -42,11 +50,307 @@ static struct scsi_device_handler *get_device_handler(const char *name) | |||
| 42 | return found; | 50 | return found; |
| 43 | } | 51 | } |
| 44 | 52 | ||
| 53 | |||
| 54 | static struct scsi_device_handler * | ||
| 55 | scsi_dh_cache_lookup(struct scsi_device *sdev) | ||
| 56 | { | ||
| 57 | struct scsi_dh_devinfo_list *tmp; | ||
| 58 | struct scsi_device_handler *found_dh = NULL; | ||
| 59 | |||
| 60 | spin_lock(&list_lock); | ||
| 61 | list_for_each_entry(tmp, &scsi_dh_dev_list, node) { | ||
| 62 | if (!strncmp(sdev->vendor, tmp->vendor, strlen(tmp->vendor)) && | ||
| 63 | !strncmp(sdev->model, tmp->model, strlen(tmp->model))) { | ||
| 64 | found_dh = tmp->handler; | ||
| 65 | break; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | spin_unlock(&list_lock); | ||
| 69 | |||
| 70 | return found_dh; | ||
| 71 | } | ||
| 72 | |||
| 73 | static int scsi_dh_handler_lookup(struct scsi_device_handler *scsi_dh, | ||
| 74 | struct scsi_device *sdev) | ||
| 75 | { | ||
| 76 | int i, found = 0; | ||
| 77 | |||
| 78 | for(i = 0; scsi_dh->devlist[i].vendor; i++) { | ||
| 79 | if (!strncmp(sdev->vendor, scsi_dh->devlist[i].vendor, | ||
| 80 | strlen(scsi_dh->devlist[i].vendor)) && | ||
| 81 | !strncmp(sdev->model, scsi_dh->devlist[i].model, | ||
| 82 | strlen(scsi_dh->devlist[i].model))) { | ||
| 83 | found = 1; | ||
| 84 | break; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | return found; | ||
| 88 | } | ||
| 89 | |||
| 90 | /* | ||
| 91 | * device_handler_match - Attach a device handler to a device | ||
| 92 | * @scsi_dh - The device handler to match against or NULL | ||
| 93 | * @sdev - SCSI device to be tested against @scsi_dh | ||
| 94 | * | ||
| 95 | * Tests @sdev against the device handler @scsi_dh or against | ||
| 96 | * all registered device_handler if @scsi_dh == NULL. | ||
| 97 | * Returns the found device handler or NULL if not found. | ||
| 98 | */ | ||
| 99 | static struct scsi_device_handler * | ||
| 100 | device_handler_match(struct scsi_device_handler *scsi_dh, | ||
| 101 | struct scsi_device *sdev) | ||
| 102 | { | ||
| 103 | struct scsi_device_handler *found_dh = NULL; | ||
| 104 | struct scsi_dh_devinfo_list *tmp; | ||
| 105 | |||
| 106 | found_dh = scsi_dh_cache_lookup(sdev); | ||
| 107 | if (found_dh) | ||
| 108 | return found_dh; | ||
| 109 | |||
| 110 | if (scsi_dh) { | ||
| 111 | if (scsi_dh_handler_lookup(scsi_dh, sdev)) | ||
| 112 | found_dh = scsi_dh; | ||
| 113 | } else { | ||
| 114 | struct scsi_device_handler *tmp_dh; | ||
| 115 | |||
| 116 | spin_lock(&list_lock); | ||
| 117 | list_for_each_entry(tmp_dh, &scsi_dh_list, list) { | ||
| 118 | if (scsi_dh_handler_lookup(tmp_dh, sdev)) | ||
| 119 | found_dh = tmp_dh; | ||
| 120 | } | ||
| 121 | spin_unlock(&list_lock); | ||
| 122 | } | ||
| 123 | |||
| 124 | if (found_dh) { /* If device is found, add it to the cache */ | ||
| 125 | tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); | ||
| 126 | if (tmp) { | ||
| 127 | strncpy(tmp->vendor, sdev->vendor, 8); | ||
| 128 | strncpy(tmp->model, sdev->model, 16); | ||
| 129 | tmp->vendor[8] = '\0'; | ||
| 130 | tmp->model[16] = '\0'; | ||
| 131 | tmp->handler = found_dh; | ||
| 132 | spin_lock(&list_lock); | ||
| 133 | list_add(&tmp->node, &scsi_dh_dev_list); | ||
| 134 | spin_unlock(&list_lock); | ||
| 135 | } else { | ||
| 136 | found_dh = NULL; | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | return found_dh; | ||
| 141 | } | ||
| 142 | |||
| 143 | /* | ||
| 144 | * scsi_dh_handler_attach - Attach a device handler to a device | ||
| 145 | * @sdev - SCSI device the device handler should attach to | ||
| 146 | * @scsi_dh - The device handler to attach | ||
| 147 | */ | ||
| 148 | static int scsi_dh_handler_attach(struct scsi_device *sdev, | ||
| 149 | struct scsi_device_handler *scsi_dh) | ||
| 150 | { | ||
| 151 | int err = 0; | ||
| 152 | |||
| 153 | if (sdev->scsi_dh_data) { | ||
| 154 | if (sdev->scsi_dh_data->scsi_dh != scsi_dh) | ||
| 155 | err = -EBUSY; | ||
| 156 | } else if (scsi_dh->attach) | ||
| 157 | err = scsi_dh->attach(sdev); | ||
| 158 | |||
| 159 | return err; | ||
| 160 | } | ||
| 161 | |||
| 162 | /* | ||
| 163 | * scsi_dh_handler_detach - Detach a device handler from a device | ||
| 164 | * @sdev - SCSI device the device handler should be detached from | ||
| 165 | * @scsi_dh - Device handler to be detached | ||
| 166 | * | ||
| 167 | * Detach from a device handler. If a device handler is specified, | ||
| 168 | * only detach if the currently attached handler matches @scsi_dh. | ||
| 169 | */ | ||
| 170 | static void scsi_dh_handler_detach(struct scsi_device *sdev, | ||
| 171 | struct scsi_device_handler *scsi_dh) | ||
| 172 | { | ||
| 173 | if (!sdev->scsi_dh_data) | ||
| 174 | return; | ||
| 175 | |||
| 176 | if (scsi_dh && scsi_dh != sdev->scsi_dh_data->scsi_dh) | ||
| 177 | return; | ||
| 178 | |||
| 179 | if (!scsi_dh) | ||
| 180 | scsi_dh = sdev->scsi_dh_data->scsi_dh; | ||
| 181 | |||
| 182 | if (scsi_dh && scsi_dh->detach) | ||
| 183 | scsi_dh->detach(sdev); | ||
| 184 | } | ||
| 185 | |||
| 186 | /* | ||
| 187 | * Functions for sysfs attribute 'dh_state' | ||
| 188 | */ | ||
| 189 | static ssize_t | ||
| 190 | store_dh_state(struct device *dev, struct device_attribute *attr, | ||
| 191 | const char *buf, size_t count) | ||
| 192 | { | ||
| 193 | struct scsi_device *sdev = to_scsi_device(dev); | ||
| 194 | struct scsi_device_handler *scsi_dh; | ||
| 195 | int err = -EINVAL; | ||
| 196 | |||
| 197 | if (!sdev->scsi_dh_data) { | ||
| 198 | /* | ||
| 199 | * Attach to a device handler | ||
| 200 | */ | ||
| 201 | if (!(scsi_dh = get_device_handler(buf))) | ||
| 202 | return err; | ||
| 203 | err = scsi_dh_handler_attach(sdev, scsi_dh); | ||
| 204 | } else { | ||
| 205 | scsi_dh = sdev->scsi_dh_data->scsi_dh; | ||
| 206 | if (!strncmp(buf, "detach", 6)) { | ||
| 207 | /* | ||
| 208 | * Detach from a device handler | ||
| 209 | */ | ||
| 210 | scsi_dh_handler_detach(sdev, scsi_dh); | ||
| 211 | err = 0; | ||
| 212 | } else if (!strncmp(buf, "activate", 8)) { | ||
| 213 | /* | ||
| 214 | * Activate a device handler | ||
| 215 | */ | ||
| 216 | if (scsi_dh->activate) | ||
| 217 | err = scsi_dh->activate(sdev); | ||
| 218 | else | ||
| 219 | err = 0; | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | return err<0?err:count; | ||
| 224 | } | ||
| 225 | |||
| 226 | static ssize_t | ||
| 227 | show_dh_state(struct device *dev, struct device_attribute *attr, char *buf) | ||
| 228 | { | ||
| 229 | struct scsi_device *sdev = to_scsi_device(dev); | ||
| 230 | |||
| 231 | if (!sdev->scsi_dh_data) | ||
| 232 | return snprintf(buf, 20, "detached\n"); | ||
| 233 | |||
| 234 | return snprintf(buf, 20, "%s\n", sdev->scsi_dh_data->scsi_dh->name); | ||
| 235 | } | ||
| 236 | |||
| 237 | static struct device_attribute scsi_dh_state_attr = | ||
| 238 | __ATTR(dh_state, S_IRUGO | S_IWUSR, show_dh_state, | ||
| 239 | store_dh_state); | ||
| 240 | |||
| 241 | /* | ||
| 242 | * scsi_dh_sysfs_attr_add - Callback for scsi_init_dh | ||
| 243 | */ | ||
| 244 | static int scsi_dh_sysfs_attr_add(struct device *dev, void *data) | ||
| 245 | { | ||
| 246 | struct scsi_device *sdev; | ||
| 247 | int err; | ||
| 248 | |||
| 249 | if (!scsi_is_sdev_device(dev)) | ||
| 250 | return 0; | ||
| 251 | |||
| 252 | sdev = to_scsi_device(dev); | ||
| 253 | |||
| 254 | err = device_create_file(&sdev->sdev_gendev, | ||
| 255 | &scsi_dh_state_attr); | ||
| 256 | |||
| 257 | return 0; | ||
| 258 | } | ||
| 259 | |||
| 260 | /* | ||
| 261 | * scsi_dh_sysfs_attr_remove - Callback for scsi_exit_dh | ||
| 262 | */ | ||
| 263 | static int scsi_dh_sysfs_attr_remove(struct device *dev, void *data) | ||
| 264 | { | ||
| 265 | struct scsi_device *sdev; | ||
| 266 | |||
| 267 | if (!scsi_is_sdev_device(dev)) | ||
| 268 | return 0; | ||
| 269 | |||
| 270 | sdev = to_scsi_device(dev); | ||
| 271 | |||
| 272 | device_remove_file(&sdev->sdev_gendev, | ||
| 273 | &scsi_dh_state_attr); | ||
| 274 | |||
| 275 | return 0; | ||
| 276 | } | ||
| 277 | |||
| 278 | /* | ||
| 279 | * scsi_dh_notifier - notifier chain callback | ||
| 280 | */ | ||
| 281 | static int scsi_dh_notifier(struct notifier_block *nb, | ||
| 282 | unsigned long action, void *data) | ||
| 283 | { | ||
| 284 | struct device *dev = data; | ||
| 285 | struct scsi_device *sdev; | ||
| 286 | int err = 0; | ||
| 287 | struct scsi_device_handler *devinfo = NULL; | ||
| 288 | |||
| 289 | if (!scsi_is_sdev_device(dev)) | ||
| 290 | return 0; | ||
| 291 | |||
| 292 | sdev = to_scsi_device(dev); | ||
| 293 | |||
| 294 | if (action == BUS_NOTIFY_ADD_DEVICE) { | ||
| 295 | devinfo = device_handler_match(NULL, sdev); | ||
| 296 | if (!devinfo) | ||
| 297 | goto out; | ||
| 298 | |||
| 299 | err = scsi_dh_handler_attach(sdev, devinfo); | ||
| 300 | if (!err) | ||
| 301 | err = device_create_file(dev, &scsi_dh_state_attr); | ||
| 302 | } else if (action == BUS_NOTIFY_DEL_DEVICE) { | ||
| 303 | device_remove_file(dev, &scsi_dh_state_attr); | ||
| 304 | scsi_dh_handler_detach(sdev, NULL); | ||
| 305 | } | ||
| 306 | out: | ||
| 307 | return err; | ||
| 308 | } | ||
| 309 | |||
| 310 | /* | ||
| 311 | * scsi_dh_notifier_add - Callback for scsi_register_device_handler | ||
| 312 | */ | ||
| 45 | static int scsi_dh_notifier_add(struct device *dev, void *data) | 313 | static int scsi_dh_notifier_add(struct device *dev, void *data) |
| 46 | { | 314 | { |
| 47 | struct scsi_device_handler *scsi_dh = data; | 315 | struct scsi_device_handler *scsi_dh = data; |
| 316 | struct scsi_device *sdev; | ||
| 317 | |||
| 318 | if (!scsi_is_sdev_device(dev)) | ||
| 319 | return 0; | ||
| 320 | |||
| 321 | if (!get_device(dev)) | ||
| 322 | return 0; | ||
| 323 | |||
| 324 | sdev = to_scsi_device(dev); | ||
| 325 | |||
| 326 | if (device_handler_match(scsi_dh, sdev)) | ||
| 327 | scsi_dh_handler_attach(sdev, scsi_dh); | ||
| 328 | |||
| 329 | put_device(dev); | ||
| 330 | |||
| 331 | return 0; | ||
| 332 | } | ||
| 333 | |||
| 334 | /* | ||
| 335 | * scsi_dh_notifier_remove - Callback for scsi_unregister_device_handler | ||
| 336 | */ | ||
| 337 | static int scsi_dh_notifier_remove(struct device *dev, void *data) | ||
| 338 | { | ||
| 339 | struct scsi_device_handler *scsi_dh = data; | ||
| 340 | struct scsi_device *sdev; | ||
| 341 | |||
| 342 | if (!scsi_is_sdev_device(dev)) | ||
| 343 | return 0; | ||
| 344 | |||
| 345 | if (!get_device(dev)) | ||
| 346 | return 0; | ||
| 347 | |||
| 348 | sdev = to_scsi_device(dev); | ||
| 349 | |||
| 350 | scsi_dh_handler_detach(sdev, scsi_dh); | ||
| 351 | |||
| 352 | put_device(dev); | ||
| 48 | 353 | ||
| 49 | scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_ADD_DEVICE, dev); | ||
| 50 | return 0; | 354 | return 0; |
| 51 | } | 355 | } |
| 52 | 356 | ||
| @@ -59,33 +363,19 @@ static int scsi_dh_notifier_add(struct device *dev, void *data) | |||
| 59 | */ | 363 | */ |
| 60 | int scsi_register_device_handler(struct scsi_device_handler *scsi_dh) | 364 | int scsi_register_device_handler(struct scsi_device_handler *scsi_dh) |
| 61 | { | 365 | { |
| 62 | int ret = -EBUSY; | 366 | if (get_device_handler(scsi_dh->name)) |
| 63 | struct scsi_device_handler *tmp; | 367 | return -EBUSY; |
| 64 | 368 | ||
| 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); | 369 | spin_lock(&list_lock); |
| 73 | list_add(&scsi_dh->list, &scsi_dh_list); | 370 | list_add(&scsi_dh->list, &scsi_dh_list); |
| 74 | spin_unlock(&list_lock); | 371 | spin_unlock(&list_lock); |
| 372 | bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add); | ||
| 373 | printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name); | ||
| 75 | 374 | ||
| 76 | done: | 375 | return SCSI_DH_OK; |
| 77 | return ret; | ||
| 78 | } | 376 | } |
| 79 | EXPORT_SYMBOL_GPL(scsi_register_device_handler); | 377 | EXPORT_SYMBOL_GPL(scsi_register_device_handler); |
| 80 | 378 | ||
| 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 | /* | 379 | /* |
| 90 | * scsi_unregister_device_handler - register a device handler personality | 380 | * scsi_unregister_device_handler - register a device handler personality |
| 91 | * module. | 381 | * module. |
| @@ -95,23 +385,26 @@ static int scsi_dh_notifier_remove(struct device *dev, void *data) | |||
| 95 | */ | 385 | */ |
| 96 | int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) | 386 | int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) |
| 97 | { | 387 | { |
| 98 | int ret = -ENODEV; | 388 | struct scsi_dh_devinfo_list *tmp, *pos; |
| 99 | struct scsi_device_handler *tmp; | ||
| 100 | |||
| 101 | tmp = get_device_handler(scsi_dh->name); | ||
| 102 | if (!tmp) | ||
| 103 | goto done; | ||
| 104 | 389 | ||
| 105 | ret = bus_unregister_notifier(&scsi_bus_type, &scsi_dh->nb); | 390 | if (!get_device_handler(scsi_dh->name)) |
| 391 | return -ENODEV; | ||
| 106 | 392 | ||
| 107 | bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, | 393 | bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, |
| 108 | scsi_dh_notifier_remove); | 394 | scsi_dh_notifier_remove); |
| 395 | |||
| 109 | spin_lock(&list_lock); | 396 | spin_lock(&list_lock); |
| 110 | list_del(&scsi_dh->list); | 397 | list_del(&scsi_dh->list); |
| 398 | list_for_each_entry_safe(pos, tmp, &scsi_dh_dev_list, node) { | ||
| 399 | if (pos->handler == scsi_dh) { | ||
| 400 | list_del(&pos->node); | ||
| 401 | kfree(pos); | ||
| 402 | } | ||
| 403 | } | ||
| 111 | spin_unlock(&list_lock); | 404 | spin_unlock(&list_lock); |
| 405 | printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name); | ||
| 112 | 406 | ||
| 113 | done: | 407 | return SCSI_DH_OK; |
| 114 | return ret; | ||
| 115 | } | 408 | } |
| 116 | EXPORT_SYMBOL_GPL(scsi_unregister_device_handler); | 409 | EXPORT_SYMBOL_GPL(scsi_unregister_device_handler); |
| 117 | 410 | ||
| @@ -157,6 +450,97 @@ int scsi_dh_handler_exist(const char *name) | |||
| 157 | } | 450 | } |
| 158 | EXPORT_SYMBOL_GPL(scsi_dh_handler_exist); | 451 | EXPORT_SYMBOL_GPL(scsi_dh_handler_exist); |
| 159 | 452 | ||
| 453 | /* | ||
| 454 | * scsi_dh_handler_attach - Attach device handler | ||
| 455 | * @sdev - sdev the handler should be attached to | ||
| 456 | * @name - name of the handler to attach | ||
| 457 | */ | ||
| 458 | int scsi_dh_attach(struct request_queue *q, const char *name) | ||
| 459 | { | ||
| 460 | unsigned long flags; | ||
| 461 | struct scsi_device *sdev; | ||
| 462 | struct scsi_device_handler *scsi_dh; | ||
| 463 | int err = 0; | ||
| 464 | |||
| 465 | scsi_dh = get_device_handler(name); | ||
| 466 | if (!scsi_dh) | ||
| 467 | return -EINVAL; | ||
| 468 | |||
| 469 | spin_lock_irqsave(q->queue_lock, flags); | ||
| 470 | sdev = q->queuedata; | ||
| 471 | if (!sdev || !get_device(&sdev->sdev_gendev)) | ||
| 472 | err = -ENODEV; | ||
| 473 | spin_unlock_irqrestore(q->queue_lock, flags); | ||
| 474 | |||
| 475 | if (!err) { | ||
| 476 | err = scsi_dh_handler_attach(sdev, scsi_dh); | ||
| 477 | |||
| 478 | put_device(&sdev->sdev_gendev); | ||
| 479 | } | ||
| 480 | return err; | ||
| 481 | } | ||
| 482 | EXPORT_SYMBOL_GPL(scsi_dh_attach); | ||
| 483 | |||
| 484 | /* | ||
| 485 | * scsi_dh_handler_detach - Detach device handler | ||
| 486 | * @sdev - sdev the handler should be detached from | ||
| 487 | * | ||
| 488 | * This function will detach the device handler only | ||
| 489 | * if the sdev is not part of the internal list, ie | ||
| 490 | * if it has been attached manually. | ||
| 491 | */ | ||
| 492 | void scsi_dh_detach(struct request_queue *q) | ||
| 493 | { | ||
| 494 | unsigned long flags; | ||
| 495 | struct scsi_device *sdev; | ||
| 496 | struct scsi_device_handler *scsi_dh = NULL; | ||
| 497 | |||
| 498 | spin_lock_irqsave(q->queue_lock, flags); | ||
| 499 | sdev = q->queuedata; | ||
| 500 | if (!sdev || !get_device(&sdev->sdev_gendev)) | ||
| 501 | sdev = NULL; | ||
| 502 | spin_unlock_irqrestore(q->queue_lock, flags); | ||
| 503 | |||
| 504 | if (!sdev) | ||
| 505 | return; | ||
| 506 | |||
| 507 | if (sdev->scsi_dh_data) { | ||
| 508 | /* if sdev is not on internal list, detach */ | ||
| 509 | scsi_dh = sdev->scsi_dh_data->scsi_dh; | ||
| 510 | if (!device_handler_match(scsi_dh, sdev)) | ||
| 511 | scsi_dh_handler_detach(sdev, scsi_dh); | ||
| 512 | } | ||
| 513 | put_device(&sdev->sdev_gendev); | ||
| 514 | } | ||
| 515 | EXPORT_SYMBOL_GPL(scsi_dh_detach); | ||
| 516 | |||
| 517 | static struct notifier_block scsi_dh_nb = { | ||
| 518 | .notifier_call = scsi_dh_notifier | ||
| 519 | }; | ||
| 520 | |||
| 521 | static int __init scsi_dh_init(void) | ||
| 522 | { | ||
| 523 | int r; | ||
| 524 | |||
| 525 | r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb); | ||
| 526 | |||
| 527 | if (!r) | ||
| 528 | bus_for_each_dev(&scsi_bus_type, NULL, NULL, | ||
| 529 | scsi_dh_sysfs_attr_add); | ||
| 530 | |||
| 531 | return r; | ||
| 532 | } | ||
| 533 | |||
| 534 | static void __exit scsi_dh_exit(void) | ||
| 535 | { | ||
| 536 | bus_for_each_dev(&scsi_bus_type, NULL, NULL, | ||
| 537 | scsi_dh_sysfs_attr_remove); | ||
| 538 | bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb); | ||
| 539 | } | ||
| 540 | |||
| 541 | module_init(scsi_dh_init); | ||
| 542 | module_exit(scsi_dh_exit); | ||
| 543 | |||
| 160 | MODULE_DESCRIPTION("SCSI device handler"); | 544 | MODULE_DESCRIPTION("SCSI device handler"); |
| 161 | MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>"); | 545 | MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>"); |
| 162 | MODULE_LICENSE("GPL"); | 546 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c new file mode 100644 index 000000000000..fcdd73f25625 --- /dev/null +++ b/drivers/scsi/device_handler/scsi_dh_alua.c | |||
| @@ -0,0 +1,802 @@ | |||
| 1 | /* | ||
| 2 | * Generic SCSI-3 ALUA SCSI Device Handler | ||
| 3 | * | ||
| 4 | * Copyright (C) 2007, 2008 Hannes Reinecke, SUSE Linux Products GmbH. | ||
| 5 | * All rights reserved. | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 20 | * | ||
| 21 | */ | ||
| 22 | #include <scsi/scsi.h> | ||
| 23 | #include <scsi/scsi_eh.h> | ||
| 24 | #include <scsi/scsi_dh.h> | ||
| 25 | |||
| 26 | #define ALUA_DH_NAME "alua" | ||
| 27 | #define ALUA_DH_VER "1.2" | ||
| 28 | |||
| 29 | #define TPGS_STATE_OPTIMIZED 0x0 | ||
| 30 | #define TPGS_STATE_NONOPTIMIZED 0x1 | ||
| 31 | #define TPGS_STATE_STANDBY 0x2 | ||
| 32 | #define TPGS_STATE_UNAVAILABLE 0x3 | ||
| 33 | #define TPGS_STATE_OFFLINE 0xe | ||
| 34 | #define TPGS_STATE_TRANSITIONING 0xf | ||
| 35 | |||
| 36 | #define TPGS_SUPPORT_NONE 0x00 | ||
| 37 | #define TPGS_SUPPORT_OPTIMIZED 0x01 | ||
| 38 | #define TPGS_SUPPORT_NONOPTIMIZED 0x02 | ||
| 39 | #define TPGS_SUPPORT_STANDBY 0x04 | ||
| 40 | #define TPGS_SUPPORT_UNAVAILABLE 0x08 | ||
| 41 | #define TPGS_SUPPORT_OFFLINE 0x40 | ||
| 42 | #define TPGS_SUPPORT_TRANSITION 0x80 | ||
| 43 | |||
| 44 | #define TPGS_MODE_UNINITIALIZED -1 | ||
| 45 | #define TPGS_MODE_NONE 0x0 | ||
| 46 | #define TPGS_MODE_IMPLICIT 0x1 | ||
| 47 | #define TPGS_MODE_EXPLICIT 0x2 | ||
| 48 | |||
| 49 | #define ALUA_INQUIRY_SIZE 36 | ||
| 50 | #define ALUA_FAILOVER_TIMEOUT (60 * HZ) | ||
| 51 | #define ALUA_FAILOVER_RETRIES 5 | ||
| 52 | |||
| 53 | struct alua_dh_data { | ||
| 54 | int group_id; | ||
| 55 | int rel_port; | ||
| 56 | int tpgs; | ||
| 57 | int state; | ||
| 58 | unsigned char inq[ALUA_INQUIRY_SIZE]; | ||
| 59 | unsigned char *buff; | ||
| 60 | int bufflen; | ||
| 61 | unsigned char sense[SCSI_SENSE_BUFFERSIZE]; | ||
| 62 | int senselen; | ||
| 63 | }; | ||
| 64 | |||
| 65 | #define ALUA_POLICY_SWITCH_CURRENT 0 | ||
| 66 | #define ALUA_POLICY_SWITCH_ALL 1 | ||
| 67 | |||
| 68 | static inline struct alua_dh_data *get_alua_data(struct scsi_device *sdev) | ||
| 69 | { | ||
| 70 | struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data; | ||
| 71 | BUG_ON(scsi_dh_data == NULL); | ||
| 72 | return ((struct alua_dh_data *) scsi_dh_data->buf); | ||
| 73 | } | ||
| 74 | |||
| 75 | static int realloc_buffer(struct alua_dh_data *h, unsigned len) | ||
| 76 | { | ||
| 77 | if (h->buff && h->buff != h->inq) | ||
| 78 | kfree(h->buff); | ||
| 79 | |||
| 80 | h->buff = kmalloc(len, GFP_NOIO); | ||
| 81 | if (!h->buff) { | ||
| 82 | h->buff = h->inq; | ||
| 83 | h->bufflen = ALUA_INQUIRY_SIZE; | ||
| 84 | return 1; | ||
| 85 | } | ||
| 86 | h->bufflen = len; | ||
| 87 | return 0; | ||
| 88 | } | ||
| 89 | |||
| 90 | static struct request *get_alua_req(struct scsi_device *sdev, | ||
| 91 | void *buffer, unsigned buflen, int rw) | ||
| 92 | { | ||
| 93 | struct request *rq; | ||
| 94 | struct request_queue *q = sdev->request_queue; | ||
| 95 | |||
| 96 | rq = blk_get_request(q, rw, GFP_NOIO); | ||
| 97 | |||
| 98 | if (!rq) { | ||
| 99 | sdev_printk(KERN_INFO, sdev, | ||
| 100 | "%s: blk_get_request failed\n", __func__); | ||
| 101 | return NULL; | ||
| 102 | } | ||
| 103 | |||
| 104 | if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_NOIO)) { | ||
| 105 | blk_put_request(rq); | ||
| 106 | sdev_printk(KERN_INFO, sdev, | ||
| 107 | "%s: blk_rq_map_kern failed\n", __func__); | ||
| 108 | return NULL; | ||
| 109 | } | ||
| 110 | |||
| 111 | rq->cmd_type = REQ_TYPE_BLOCK_PC; | ||
| 112 | rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE; | ||
| 113 | rq->retries = ALUA_FAILOVER_RETRIES; | ||
| 114 | rq->timeout = ALUA_FAILOVER_TIMEOUT; | ||
| 115 | |||
| 116 | return rq; | ||
| 117 | } | ||
| 118 | |||
| 119 | /* | ||
| 120 | * submit_std_inquiry - Issue a standard INQUIRY command | ||
| 121 | * @sdev: sdev the command should be send to | ||
| 122 | */ | ||
| 123 | static int submit_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) | ||
| 124 | { | ||
| 125 | struct request *rq; | ||
| 126 | int err = SCSI_DH_RES_TEMP_UNAVAIL; | ||
| 127 | |||
| 128 | rq = get_alua_req(sdev, h->inq, ALUA_INQUIRY_SIZE, READ); | ||
| 129 | if (!rq) | ||
| 130 | goto done; | ||
| 131 | |||
| 132 | /* Prepare the command. */ | ||
| 133 | rq->cmd[0] = INQUIRY; | ||
| 134 | rq->cmd[1] = 0; | ||
| 135 | rq->cmd[2] = 0; | ||
| 136 | rq->cmd[4] = ALUA_INQUIRY_SIZE; | ||
| 137 | rq->cmd_len = COMMAND_SIZE(INQUIRY); | ||
| 138 | |||
| 139 | rq->sense = h->sense; | ||
| 140 | memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); | ||
| 141 | rq->sense_len = h->senselen = 0; | ||
| 142 | |||
| 143 | err = blk_execute_rq(rq->q, NULL, rq, 1); | ||
| 144 | if (err == -EIO) { | ||
| 145 | sdev_printk(KERN_INFO, sdev, | ||
| 146 | "%s: std inquiry failed with %x\n", | ||
| 147 | ALUA_DH_NAME, rq->errors); | ||
| 148 | h->senselen = rq->sense_len; | ||
| 149 | err = SCSI_DH_IO; | ||
| 150 | } | ||
| 151 | blk_put_request(rq); | ||
| 152 | done: | ||
| 153 | return err; | ||
| 154 | } | ||
| 155 | |||
| 156 | /* | ||
| 157 | * submit_vpd_inquiry - Issue an INQUIRY VPD page 0x83 command | ||
| 158 | * @sdev: sdev the command should be sent to | ||
| 159 | */ | ||
| 160 | static int submit_vpd_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) | ||
| 161 | { | ||
| 162 | struct request *rq; | ||
| 163 | int err = SCSI_DH_RES_TEMP_UNAVAIL; | ||
| 164 | |||
| 165 | rq = get_alua_req(sdev, h->buff, h->bufflen, READ); | ||
| 166 | if (!rq) | ||
| 167 | goto done; | ||
| 168 | |||
| 169 | /* Prepare the command. */ | ||
| 170 | rq->cmd[0] = INQUIRY; | ||
| 171 | rq->cmd[1] = 1; | ||
| 172 | rq->cmd[2] = 0x83; | ||
| 173 | rq->cmd[4] = h->bufflen; | ||
| 174 | rq->cmd_len = COMMAND_SIZE(INQUIRY); | ||
| 175 | |||
| 176 | rq->sense = h->sense; | ||
| 177 | memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); | ||
| 178 | rq->sense_len = h->senselen = 0; | ||
| 179 | |||
| 180 | err = blk_execute_rq(rq->q, NULL, rq, 1); | ||
| 181 | if (err == -EIO) { | ||
| 182 | sdev_printk(KERN_INFO, sdev, | ||
| 183 | "%s: evpd inquiry failed with %x\n", | ||
| 184 | ALUA_DH_NAME, rq->errors); | ||
| 185 | h->senselen = rq->sense_len; | ||
| 186 | err = SCSI_DH_IO; | ||
| 187 | } | ||
| 188 | blk_put_request(rq); | ||
| 189 | done: | ||
| 190 | return err; | ||
| 191 | } | ||
| 192 | |||
| 193 | /* | ||
| 194 | * submit_rtpg - Issue a REPORT TARGET GROUP STATES command | ||
| 195 | * @sdev: sdev the command should be sent to | ||
| 196 | */ | ||
| 197 | static unsigned submit_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) | ||
| 198 | { | ||
| 199 | struct request *rq; | ||
| 200 | int err = SCSI_DH_RES_TEMP_UNAVAIL; | ||
| 201 | |||
| 202 | rq = get_alua_req(sdev, h->buff, h->bufflen, READ); | ||
| 203 | if (!rq) | ||
| 204 | goto done; | ||
| 205 | |||
| 206 | /* Prepare the command. */ | ||
| 207 | rq->cmd[0] = MAINTENANCE_IN; | ||
| 208 | rq->cmd[1] = MI_REPORT_TARGET_PGS; | ||
| 209 | rq->cmd[6] = (h->bufflen >> 24) & 0xff; | ||
| 210 | rq->cmd[7] = (h->bufflen >> 16) & 0xff; | ||
| 211 | rq->cmd[8] = (h->bufflen >> 8) & 0xff; | ||
| 212 | rq->cmd[9] = h->bufflen & 0xff; | ||
| 213 | rq->cmd_len = COMMAND_SIZE(MAINTENANCE_IN); | ||
| 214 | |||
| 215 | rq->sense = h->sense; | ||
| 216 | memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); | ||
| 217 | rq->sense_len = h->senselen = 0; | ||
| 218 | |||
| 219 | err = blk_execute_rq(rq->q, NULL, rq, 1); | ||
| 220 | if (err == -EIO) { | ||
| 221 | sdev_printk(KERN_INFO, sdev, | ||
| 222 | "%s: rtpg failed with %x\n", | ||
| 223 | ALUA_DH_NAME, rq->errors); | ||
| 224 | h->senselen = rq->sense_len; | ||
| 225 | err = SCSI_DH_IO; | ||
| 226 | } | ||
| 227 | blk_put_request(rq); | ||
| 228 | done: | ||
| 229 | return err; | ||
| 230 | } | ||
| 231 | |||
| 232 | /* | ||
| 233 | * submit_stpg - Issue a SET TARGET GROUP STATES command | ||
| 234 | * @sdev: sdev the command should be sent to | ||
| 235 | * | ||
| 236 | * Currently we're only setting the current target port group state | ||
| 237 | * to 'active/optimized' and let the array firmware figure out | ||
| 238 | * the states of the remaining groups. | ||
| 239 | */ | ||
| 240 | static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h) | ||
| 241 | { | ||
| 242 | struct request *rq; | ||
| 243 | int err = SCSI_DH_RES_TEMP_UNAVAIL; | ||
| 244 | int stpg_len = 8; | ||
| 245 | |||
| 246 | /* Prepare the data buffer */ | ||
| 247 | memset(h->buff, 0, stpg_len); | ||
| 248 | h->buff[4] = TPGS_STATE_OPTIMIZED & 0x0f; | ||
| 249 | h->buff[6] = (h->group_id >> 8) & 0x0f; | ||
| 250 | h->buff[7] = h->group_id & 0x0f; | ||
| 251 | |||
| 252 | rq = get_alua_req(sdev, h->buff, stpg_len, WRITE); | ||
| 253 | if (!rq) | ||
| 254 | goto done; | ||
| 255 | |||
| 256 | /* Prepare the command. */ | ||
| 257 | rq->cmd[0] = MAINTENANCE_OUT; | ||
| 258 | rq->cmd[1] = MO_SET_TARGET_PGS; | ||
| 259 | rq->cmd[6] = (stpg_len >> 24) & 0xff; | ||
| 260 | rq->cmd[7] = (stpg_len >> 16) & 0xff; | ||
| 261 | rq->cmd[8] = (stpg_len >> 8) & 0xff; | ||
| 262 | rq->cmd[9] = stpg_len & 0xff; | ||
| 263 | rq->cmd_len = COMMAND_SIZE(MAINTENANCE_OUT); | ||
| 264 | |||
| 265 | rq->sense = h->sense; | ||
| 266 | memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); | ||
| 267 | rq->sense_len = h->senselen = 0; | ||
| 268 | |||
| 269 | err = blk_execute_rq(rq->q, NULL, rq, 1); | ||
| 270 | if (err == -EIO) { | ||
| 271 | sdev_printk(KERN_INFO, sdev, | ||
| 272 | "%s: stpg failed with %x\n", | ||
| 273 | ALUA_DH_NAME, rq->errors); | ||
| 274 | h->senselen = rq->sense_len; | ||
| 275 | err = SCSI_DH_IO; | ||
| 276 | } | ||
| 277 | blk_put_request(rq); | ||
| 278 | done: | ||
| 279 | return err; | ||
| 280 | } | ||
| 281 | |||
| 282 | /* | ||
| 283 | * alua_std_inquiry - Evaluate standard INQUIRY command | ||
| 284 | * @sdev: device to be checked | ||
| 285 | * | ||
| 286 | * Just extract the TPGS setting to find out if ALUA | ||
| 287 | * is supported. | ||
| 288 | */ | ||
| 289 | static int alua_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) | ||
| 290 | { | ||
| 291 | int err; | ||
| 292 | |||
| 293 | err = submit_std_inquiry(sdev, h); | ||
| 294 | |||
| 295 | if (err != SCSI_DH_OK) | ||
| 296 | return err; | ||
| 297 | |||
| 298 | /* Check TPGS setting */ | ||
| 299 | h->tpgs = (h->inq[5] >> 4) & 0x3; | ||
| 300 | switch (h->tpgs) { | ||
| 301 | case TPGS_MODE_EXPLICIT|TPGS_MODE_IMPLICIT: | ||
| 302 | sdev_printk(KERN_INFO, sdev, | ||
| 303 | "%s: supports implicit and explicit TPGS\n", | ||
| 304 | ALUA_DH_NAME); | ||
| 305 | break; | ||
| 306 | case TPGS_MODE_EXPLICIT: | ||
| 307 | sdev_printk(KERN_INFO, sdev, "%s: supports explicit TPGS\n", | ||
| 308 | ALUA_DH_NAME); | ||
| 309 | break; | ||
| 310 | case TPGS_MODE_IMPLICIT: | ||
| 311 | sdev_printk(KERN_INFO, sdev, "%s: supports implicit TPGS\n", | ||
| 312 | ALUA_DH_NAME); | ||
| 313 | break; | ||
| 314 | default: | ||
| 315 | h->tpgs = TPGS_MODE_NONE; | ||
| 316 | sdev_printk(KERN_INFO, sdev, "%s: not supported\n", | ||
| 317 | ALUA_DH_NAME); | ||
| 318 | err = SCSI_DH_DEV_UNSUPP; | ||
| 319 | break; | ||
| 320 | } | ||
| 321 | |||
| 322 | return err; | ||
| 323 | } | ||
| 324 | |||
| 325 | /* | ||
| 326 | * alua_vpd_inquiry - Evaluate INQUIRY vpd page 0x83 | ||
| 327 | * @sdev: device to be checked | ||
| 328 | * | ||
| 329 | * Extract the relative target port and the target port group | ||
| 330 | * descriptor from the list of identificators. | ||
| 331 | */ | ||
| 332 | static int alua_vpd_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) | ||
| 333 | { | ||
| 334 | int len; | ||
| 335 | unsigned err; | ||
| 336 | unsigned char *d; | ||
| 337 | |||
| 338 | retry: | ||
| 339 | err = submit_vpd_inquiry(sdev, h); | ||
| 340 | |||
| 341 | if (err != SCSI_DH_OK) | ||
| 342 | return err; | ||
| 343 | |||
| 344 | /* Check if vpd page exceeds initial buffer */ | ||
| 345 | len = (h->buff[2] << 8) + h->buff[3] + 4; | ||
| 346 | if (len > h->bufflen) { | ||
| 347 | /* Resubmit with the correct length */ | ||
| 348 | if (realloc_buffer(h, len)) { | ||
| 349 | sdev_printk(KERN_WARNING, sdev, | ||
| 350 | "%s: kmalloc buffer failed\n", | ||
| 351 | ALUA_DH_NAME); | ||
| 352 | /* Temporary failure, bypass */ | ||
| 353 | return SCSI_DH_DEV_TEMP_BUSY; | ||
| 354 | } | ||
| 355 | goto retry; | ||
| 356 | } | ||
| 357 | |||
| 358 | /* | ||
| 359 | * Now look for the correct descriptor. | ||
| 360 | */ | ||
| 361 | d = h->buff + 4; | ||
| 362 | while (d < h->buff + len) { | ||
| 363 | switch (d[1] & 0xf) { | ||
| 364 | case 0x4: | ||
| 365 | /* Relative target port */ | ||
| 366 | h->rel_port = (d[6] << 8) + d[7]; | ||
| 367 | break; | ||
| 368 | case 0x5: | ||
| 369 | /* Target port group */ | ||
| 370 | h->group_id = (d[6] << 8) + d[7]; | ||
| 371 | break; | ||
| 372 | default: | ||
| 373 | break; | ||
| 374 | } | ||
| 375 | d += d[3] + 4; | ||
| 376 | } | ||
| 377 | |||
| 378 | if (h->group_id == -1) { | ||
| 379 | /* | ||
| 380 | * Internal error; TPGS supported but required | ||
| 381 | * VPD identification descriptors not present. | ||
| 382 | * Disable ALUA support | ||
| 383 | */ | ||
| 384 | sdev_printk(KERN_INFO, sdev, | ||
| 385 | "%s: No target port descriptors found\n", | ||
| 386 | ALUA_DH_NAME); | ||
| 387 | h->state = TPGS_STATE_OPTIMIZED; | ||
| 388 | h->tpgs = TPGS_MODE_NONE; | ||
| 389 | err = SCSI_DH_DEV_UNSUPP; | ||
| 390 | } else { | ||
| 391 | sdev_printk(KERN_INFO, sdev, | ||
| 392 | "%s: port group %02x rel port %02x\n", | ||
| 393 | ALUA_DH_NAME, h->group_id, h->rel_port); | ||
| 394 | } | ||
| 395 | |||
| 396 | return err; | ||
| 397 | } | ||
| 398 | |||
| 399 | static char print_alua_state(int state) | ||
| 400 | { | ||
| 401 | switch (state) { | ||
| 402 | case TPGS_STATE_OPTIMIZED: | ||
| 403 | return 'A'; | ||
| 404 | case TPGS_STATE_NONOPTIMIZED: | ||
| 405 | return 'N'; | ||
| 406 | case TPGS_STATE_STANDBY: | ||
| 407 | return 'S'; | ||
| 408 | case TPGS_STATE_UNAVAILABLE: | ||
| 409 | return 'U'; | ||
| 410 | case TPGS_STATE_OFFLINE: | ||
| 411 | return 'O'; | ||
| 412 | case TPGS_STATE_TRANSITIONING: | ||
| 413 | return 'T'; | ||
| 414 | default: | ||
| 415 | return 'X'; | ||
| 416 | } | ||
| 417 | } | ||
| 418 | |||
| 419 | static int alua_check_sense(struct scsi_device *sdev, | ||
| 420 | struct scsi_sense_hdr *sense_hdr) | ||
| 421 | { | ||
| 422 | switch (sense_hdr->sense_key) { | ||
| 423 | case NOT_READY: | ||
| 424 | if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a) | ||
| 425 | /* | ||
| 426 | * LUN Not Accessible - ALUA state transition | ||
| 427 | */ | ||
| 428 | return NEEDS_RETRY; | ||
| 429 | if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0b) | ||
| 430 | /* | ||
| 431 | * LUN Not Accessible -- Target port in standby state | ||
| 432 | */ | ||
| 433 | return SUCCESS; | ||
| 434 | if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0c) | ||
| 435 | /* | ||
| 436 | * LUN Not Accessible -- Target port in unavailable state | ||
| 437 | */ | ||
| 438 | return SUCCESS; | ||
| 439 | if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x12) | ||
| 440 | /* | ||
| 441 | * LUN Not Ready -- Offline | ||
| 442 | */ | ||
| 443 | return SUCCESS; | ||
| 444 | break; | ||
| 445 | case UNIT_ATTENTION: | ||
| 446 | if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00) | ||
| 447 | /* | ||
| 448 | * Power On, Reset, or Bus Device Reset, just retry. | ||
| 449 | */ | ||
| 450 | return NEEDS_RETRY; | ||
| 451 | if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x06) { | ||
| 452 | /* | ||
| 453 | * ALUA state changed | ||
| 454 | */ | ||
| 455 | return NEEDS_RETRY; | ||
| 456 | } | ||
| 457 | if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x07) { | ||
| 458 | /* | ||
| 459 | * Implicit ALUA state transition failed | ||
| 460 | */ | ||
| 461 | return NEEDS_RETRY; | ||
| 462 | } | ||
| 463 | break; | ||
| 464 | } | ||
| 465 | |||
| 466 | return SCSI_RETURN_NOT_HANDLED; | ||
| 467 | } | ||
| 468 | |||
| 469 | /* | ||
| 470 | * alua_stpg - Evaluate SET TARGET GROUP STATES | ||
| 471 | * @sdev: the device to be evaluated | ||
| 472 | * @state: the new target group state | ||
| 473 | * | ||
| 474 | * Send a SET TARGET GROUP STATES command to the device. | ||
| 475 | * We only have to test here if we should resubmit the command; | ||
| 476 | * any other error is assumed as a failure. | ||
| 477 | */ | ||
| 478 | static int alua_stpg(struct scsi_device *sdev, int state, | ||
| 479 | struct alua_dh_data *h) | ||
| 480 | { | ||
| 481 | struct scsi_sense_hdr sense_hdr; | ||
| 482 | unsigned err; | ||
| 483 | int retry = ALUA_FAILOVER_RETRIES; | ||
| 484 | |||
| 485 | retry: | ||
| 486 | err = submit_stpg(sdev, h); | ||
| 487 | if (err == SCSI_DH_IO && h->senselen > 0) { | ||
| 488 | err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, | ||
| 489 | &sense_hdr); | ||
| 490 | if (!err) | ||
| 491 | return SCSI_DH_IO; | ||
| 492 | err = alua_check_sense(sdev, &sense_hdr); | ||
| 493 | if (retry > 0 && err == NEEDS_RETRY) { | ||
| 494 | retry--; | ||
| 495 | goto retry; | ||
| 496 | } | ||
| 497 | sdev_printk(KERN_INFO, sdev, | ||
| 498 | "%s: stpg sense code: %02x/%02x/%02x\n", | ||
| 499 | ALUA_DH_NAME, sense_hdr.sense_key, | ||
| 500 | sense_hdr.asc, sense_hdr.ascq); | ||
| 501 | err = SCSI_DH_IO; | ||
| 502 | } | ||
| 503 | if (err == SCSI_DH_OK) { | ||
| 504 | h->state = state; | ||
| 505 | sdev_printk(KERN_INFO, sdev, | ||
| 506 | "%s: port group %02x switched to state %c\n", | ||
| 507 | ALUA_DH_NAME, h->group_id, | ||
| 508 | print_alua_state(h->state) ); | ||
| 509 | } | ||
| 510 | return err; | ||
| 511 | } | ||
| 512 | |||
| 513 | /* | ||
| 514 | * alua_rtpg - Evaluate REPORT TARGET GROUP STATES | ||
| 515 | * @sdev: the device to be evaluated. | ||
| 516 | * | ||
| 517 | * Evaluate the Target Port Group State. | ||
| 518 | * Returns SCSI_DH_DEV_OFFLINED if the path is | ||
| 519 | * found to be unuseable. | ||
| 520 | */ | ||
| 521 | static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) | ||
| 522 | { | ||
| 523 | struct scsi_sense_hdr sense_hdr; | ||
| 524 | int len, k, off, valid_states = 0; | ||
| 525 | char *ucp; | ||
| 526 | unsigned err; | ||
| 527 | |||
| 528 | retry: | ||
| 529 | err = submit_rtpg(sdev, h); | ||
| 530 | |||
| 531 | if (err == SCSI_DH_IO && h->senselen > 0) { | ||
| 532 | err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, | ||
| 533 | &sense_hdr); | ||
| 534 | if (!err) | ||
| 535 | return SCSI_DH_IO; | ||
| 536 | |||
| 537 | err = alua_check_sense(sdev, &sense_hdr); | ||
| 538 | if (err == NEEDS_RETRY) | ||
| 539 | goto retry; | ||
| 540 | sdev_printk(KERN_INFO, sdev, | ||
| 541 | "%s: rtpg sense code %02x/%02x/%02x\n", | ||
| 542 | ALUA_DH_NAME, sense_hdr.sense_key, | ||
| 543 | sense_hdr.asc, sense_hdr.ascq); | ||
| 544 | err = SCSI_DH_IO; | ||
| 545 | } | ||
| 546 | if (err != SCSI_DH_OK) | ||
| 547 | return err; | ||
| 548 | |||
| 549 | len = (h->buff[0] << 24) + (h->buff[1] << 16) + | ||
| 550 | (h->buff[2] << 8) + h->buff[3] + 4; | ||
| 551 | |||
| 552 | if (len > h->bufflen) { | ||
| 553 | /* Resubmit with the correct length */ | ||
| 554 | if (realloc_buffer(h, len)) { | ||
| 555 | sdev_printk(KERN_WARNING, sdev, | ||
| 556 | "%s: kmalloc buffer failed\n",__func__); | ||
| 557 | /* Temporary failure, bypass */ | ||
| 558 | return SCSI_DH_DEV_TEMP_BUSY; | ||
| 559 | } | ||
| 560 | goto retry; | ||
| 561 | } | ||
| 562 | |||
| 563 | for (k = 4, ucp = h->buff + 4; k < len; k += off, ucp += off) { | ||
| 564 | if (h->group_id == (ucp[2] << 8) + ucp[3]) { | ||
| 565 | h->state = ucp[0] & 0x0f; | ||
| 566 | valid_states = ucp[1]; | ||
| 567 | } | ||
| 568 | off = 8 + (ucp[7] * 4); | ||
| 569 | } | ||
| 570 | |||
| 571 | sdev_printk(KERN_INFO, sdev, | ||
| 572 | "%s: port group %02x state %c supports %c%c%c%c%c%c\n", | ||
| 573 | ALUA_DH_NAME, h->group_id, print_alua_state(h->state), | ||
| 574 | valid_states&TPGS_SUPPORT_TRANSITION?'T':'t', | ||
| 575 | valid_states&TPGS_SUPPORT_OFFLINE?'O':'o', | ||
| 576 | valid_states&TPGS_SUPPORT_UNAVAILABLE?'U':'u', | ||
| 577 | valid_states&TPGS_SUPPORT_STANDBY?'S':'s', | ||
| 578 | valid_states&TPGS_SUPPORT_NONOPTIMIZED?'N':'n', | ||
| 579 | valid_states&TPGS_SUPPORT_OPTIMIZED?'A':'a'); | ||
| 580 | |||
| 581 | if (h->tpgs & TPGS_MODE_EXPLICIT) { | ||
| 582 | switch (h->state) { | ||
| 583 | case TPGS_STATE_TRANSITIONING: | ||
| 584 | /* State transition, retry */ | ||
| 585 | goto retry; | ||
| 586 | break; | ||
| 587 | case TPGS_STATE_OFFLINE: | ||
| 588 | /* Path is offline, fail */ | ||
| 589 | err = SCSI_DH_DEV_OFFLINED; | ||
| 590 | break; | ||
| 591 | default: | ||
| 592 | break; | ||
| 593 | } | ||
| 594 | } else { | ||
| 595 | /* Only Implicit ALUA support */ | ||
| 596 | if (h->state == TPGS_STATE_OPTIMIZED || | ||
| 597 | h->state == TPGS_STATE_NONOPTIMIZED || | ||
| 598 | h->state == TPGS_STATE_STANDBY) | ||
| 599 | /* Useable path if active */ | ||
| 600 | err = SCSI_DH_OK; | ||
| 601 | else | ||
| 602 | /* Path unuseable for unavailable/offline */ | ||
| 603 | err = SCSI_DH_DEV_OFFLINED; | ||
| 604 | } | ||
| 605 | return err; | ||
| 606 | } | ||
| 607 | |||
| 608 | /* | ||
| 609 | * alua_initialize - Initialize ALUA state | ||
| 610 | * @sdev: the device to be initialized | ||
| 611 | * | ||
| 612 | * For the prep_fn to work correctly we have | ||
| 613 | * to initialize the ALUA state for the device. | ||
| 614 | */ | ||
| 615 | static int alua_initialize(struct scsi_device *sdev, struct alua_dh_data *h) | ||
| 616 | { | ||
| 617 | int err; | ||
| 618 | |||
| 619 | err = alua_std_inquiry(sdev, h); | ||
| 620 | if (err != SCSI_DH_OK) | ||
| 621 | goto out; | ||
| 622 | |||
| 623 | err = alua_vpd_inquiry(sdev, h); | ||
| 624 | if (err != SCSI_DH_OK) | ||
| 625 | goto out; | ||
| 626 | |||
| 627 | err = alua_rtpg(sdev, h); | ||
| 628 | if (err != SCSI_DH_OK) | ||
| 629 | goto out; | ||
| 630 | |||
| 631 | out: | ||
| 632 | return err; | ||
| 633 | } | ||
| 634 | |||
| 635 | /* | ||
| 636 | * alua_activate - activate a path | ||
| 637 | * @sdev: device on the path to be activated | ||
| 638 | * | ||
| 639 | * We're currently switching the port group to be activated only and | ||
| 640 | * let the array figure out the rest. | ||
| 641 | * There may be other arrays which require us to switch all port groups | ||
| 642 | * based on a certain policy. But until we actually encounter them it | ||
| 643 | * should be okay. | ||
| 644 | */ | ||
| 645 | static int alua_activate(struct scsi_device *sdev) | ||
| 646 | { | ||
| 647 | struct alua_dh_data *h = get_alua_data(sdev); | ||
| 648 | int err = SCSI_DH_OK; | ||
| 649 | |||
| 650 | if (h->group_id != -1) { | ||
| 651 | err = alua_rtpg(sdev, h); | ||
| 652 | if (err != SCSI_DH_OK) | ||
| 653 | goto out; | ||
| 654 | } | ||
| 655 | |||
| 656 | if (h->tpgs == TPGS_MODE_EXPLICIT && h->state != TPGS_STATE_OPTIMIZED) | ||
| 657 | err = alua_stpg(sdev, TPGS_STATE_OPTIMIZED, h); | ||
| 658 | |||
| 659 | out: | ||
| 660 | return err; | ||
| 661 | } | ||
| 662 | |||
| 663 | /* | ||
| 664 | * alua_prep_fn - request callback | ||
| 665 | * | ||
| 666 | * Fail I/O to all paths not in state | ||
| 667 | * active/optimized or active/non-optimized. | ||
| 668 | */ | ||
| 669 | static int alua_prep_fn(struct scsi_device *sdev, struct request *req) | ||
| 670 | { | ||
| 671 | struct alua_dh_data *h = get_alua_data(sdev); | ||
| 672 | int ret = BLKPREP_OK; | ||
| 673 | |||
| 674 | if (h->state != TPGS_STATE_OPTIMIZED && | ||
| 675 | h->state != TPGS_STATE_NONOPTIMIZED) { | ||
| 676 | ret = BLKPREP_KILL; | ||
| 677 | req->cmd_flags |= REQ_QUIET; | ||
| 678 | } | ||
| 679 | return ret; | ||
| 680 | |||
| 681 | } | ||
| 682 | |||
| 683 | const struct scsi_dh_devlist alua_dev_list[] = { | ||
| 684 | {"HP", "MSA VOLUME" }, | ||
| 685 | {"HP", "HSV101" }, | ||
| 686 | {"HP", "HSV111" }, | ||
| 687 | {"HP", "HSV200" }, | ||
| 688 | {"HP", "HSV210" }, | ||
| 689 | {"HP", "HSV300" }, | ||
| 690 | {"IBM", "2107900" }, | ||
| 691 | {"IBM", "2145" }, | ||
| 692 | {"Pillar", "Axiom" }, | ||
| 693 | {NULL, NULL} | ||
| 694 | }; | ||
| 695 | |||
| 696 | static int alua_bus_attach(struct scsi_device *sdev); | ||
| 697 | static void alua_bus_detach(struct scsi_device *sdev); | ||
| 698 | |||
| 699 | static struct scsi_device_handler alua_dh = { | ||
| 700 | .name = ALUA_DH_NAME, | ||
| 701 | .module = THIS_MODULE, | ||
| 702 | .devlist = alua_dev_list, | ||
| 703 | .attach = alua_bus_attach, | ||
| 704 | .detach = alua_bus_detach, | ||
| 705 | .prep_fn = alua_prep_fn, | ||
| 706 | .check_sense = alua_check_sense, | ||
| 707 | .activate = alua_activate, | ||
| 708 | }; | ||
| 709 | |||
| 710 | /* | ||
| 711 | * alua_bus_attach - Attach device handler | ||
| 712 | * @sdev: device to be attached to | ||
| 713 | */ | ||
| 714 | static int alua_bus_attach(struct scsi_device *sdev) | ||
| 715 | { | ||
| 716 | struct scsi_dh_data *scsi_dh_data; | ||
| 717 | struct alua_dh_data *h; | ||
| 718 | unsigned long flags; | ||
| 719 | int err = SCSI_DH_OK; | ||
| 720 | |||
| 721 | scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) | ||
| 722 | + sizeof(*h) , GFP_KERNEL); | ||
| 723 | if (!scsi_dh_data) { | ||
| 724 | sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n", | ||
| 725 | ALUA_DH_NAME); | ||
| 726 | return -ENOMEM; | ||
| 727 | } | ||
| 728 | |||
| 729 | scsi_dh_data->scsi_dh = &alua_dh; | ||
| 730 | h = (struct alua_dh_data *) scsi_dh_data->buf; | ||
| 731 | h->tpgs = TPGS_MODE_UNINITIALIZED; | ||
| 732 | h->state = TPGS_STATE_OPTIMIZED; | ||
| 733 | h->group_id = -1; | ||
| 734 | h->rel_port = -1; | ||
| 735 | h->buff = h->inq; | ||
| 736 | h->bufflen = ALUA_INQUIRY_SIZE; | ||
| 737 | |||
| 738 | err = alua_initialize(sdev, h); | ||
| 739 | if (err != SCSI_DH_OK) | ||
| 740 | goto failed; | ||
| 741 | |||
| 742 | if (!try_module_get(THIS_MODULE)) | ||
| 743 | goto failed; | ||
| 744 | |||
| 745 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); | ||
| 746 | sdev->scsi_dh_data = scsi_dh_data; | ||
| 747 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | ||
| 748 | |||
| 749 | return 0; | ||
| 750 | |||
| 751 | failed: | ||
| 752 | kfree(scsi_dh_data); | ||
| 753 | sdev_printk(KERN_ERR, sdev, "%s: not attached\n", ALUA_DH_NAME); | ||
| 754 | return -EINVAL; | ||
| 755 | } | ||
| 756 | |||
| 757 | /* | ||
| 758 | * alua_bus_detach - Detach device handler | ||
| 759 | * @sdev: device to be detached from | ||
| 760 | */ | ||
| 761 | static void alua_bus_detach(struct scsi_device *sdev) | ||
| 762 | { | ||
| 763 | struct scsi_dh_data *scsi_dh_data; | ||
| 764 | struct alua_dh_data *h; | ||
| 765 | unsigned long flags; | ||
| 766 | |||
| 767 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); | ||
| 768 | scsi_dh_data = sdev->scsi_dh_data; | ||
| 769 | sdev->scsi_dh_data = NULL; | ||
| 770 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | ||
| 771 | |||
| 772 | h = (struct alua_dh_data *) scsi_dh_data->buf; | ||
| 773 | if (h->buff && h->inq != h->buff) | ||
| 774 | kfree(h->buff); | ||
| 775 | kfree(scsi_dh_data); | ||
| 776 | module_put(THIS_MODULE); | ||
| 777 | sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", ALUA_DH_NAME); | ||
| 778 | } | ||
| 779 | |||
| 780 | static int __init alua_init(void) | ||
| 781 | { | ||
| 782 | int r; | ||
| 783 | |||
| 784 | r = scsi_register_device_handler(&alua_dh); | ||
| 785 | if (r != 0) | ||
| 786 | printk(KERN_ERR "%s: Failed to register scsi device handler", | ||
| 787 | ALUA_DH_NAME); | ||
| 788 | return r; | ||
| 789 | } | ||
| 790 | |||
| 791 | static void __exit alua_exit(void) | ||
| 792 | { | ||
| 793 | scsi_unregister_device_handler(&alua_dh); | ||
| 794 | } | ||
| 795 | |||
| 796 | module_init(alua_init); | ||
| 797 | module_exit(alua_exit); | ||
| 798 | |||
| 799 | MODULE_DESCRIPTION("DM Multipath ALUA support"); | ||
| 800 | MODULE_AUTHOR("Hannes Reinecke <hare@suse.de>"); | ||
| 801 | MODULE_LICENSE("GPL"); | ||
| 802 | MODULE_VERSION(ALUA_DH_VER); | ||
diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c index f2467e936e55..aa46b131b20e 100644 --- a/drivers/scsi/device_handler/scsi_dh_emc.c +++ b/drivers/scsi/device_handler/scsi_dh_emc.c | |||
| @@ -25,28 +25,31 @@ | |||
| 25 | #include <scsi/scsi_dh.h> | 25 | #include <scsi/scsi_dh.h> |
| 26 | #include <scsi/scsi_device.h> | 26 | #include <scsi/scsi_device.h> |
| 27 | 27 | ||
| 28 | #define CLARIION_NAME "emc_clariion" | 28 | #define CLARIION_NAME "emc" |
| 29 | 29 | ||
| 30 | #define CLARIION_TRESPASS_PAGE 0x22 | 30 | #define CLARIION_TRESPASS_PAGE 0x22 |
| 31 | #define CLARIION_BUFFER_SIZE 0x80 | 31 | #define CLARIION_BUFFER_SIZE 0xFC |
| 32 | #define CLARIION_TIMEOUT (60 * HZ) | 32 | #define CLARIION_TIMEOUT (60 * HZ) |
| 33 | #define CLARIION_RETRIES 3 | 33 | #define CLARIION_RETRIES 3 |
| 34 | #define CLARIION_UNBOUND_LU -1 | 34 | #define CLARIION_UNBOUND_LU -1 |
| 35 | #define CLARIION_SP_A 0 | ||
| 36 | #define CLARIION_SP_B 1 | ||
| 35 | 37 | ||
| 36 | static unsigned char long_trespass[] = { | 38 | /* Flags */ |
| 37 | 0, 0, 0, 0, | 39 | #define CLARIION_SHORT_TRESPASS 1 |
| 38 | CLARIION_TRESPASS_PAGE, /* Page code */ | 40 | #define CLARIION_HONOR_RESERVATIONS 2 |
| 39 | 0x09, /* Page length - 2 */ | ||
| 40 | 0x81, /* Trespass code + Honor reservation bit */ | ||
| 41 | 0xff, 0xff, /* Trespass target */ | ||
| 42 | 0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */ | ||
| 43 | }; | ||
| 44 | 41 | ||
| 45 | static unsigned char long_trespass_hr[] = { | 42 | /* LUN states */ |
| 46 | 0, 0, 0, 0, | 43 | #define CLARIION_LUN_UNINITIALIZED -1 |
| 44 | #define CLARIION_LUN_UNBOUND 0 | ||
| 45 | #define CLARIION_LUN_BOUND 1 | ||
| 46 | #define CLARIION_LUN_OWNED 2 | ||
| 47 | |||
| 48 | static unsigned char long_trespass[] = { | ||
| 49 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 47 | CLARIION_TRESPASS_PAGE, /* Page code */ | 50 | CLARIION_TRESPASS_PAGE, /* Page code */ |
| 48 | 0x09, /* Page length - 2 */ | 51 | 0x09, /* Page length - 2 */ |
| 49 | 0x01, /* Trespass code + Honor reservation bit */ | 52 | 0x01, /* Trespass code */ |
| 50 | 0xff, 0xff, /* Trespass target */ | 53 | 0xff, 0xff, /* Trespass target */ |
| 51 | 0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */ | 54 | 0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */ |
| 52 | }; | 55 | }; |
| @@ -55,39 +58,56 @@ static unsigned char short_trespass[] = { | |||
| 55 | 0, 0, 0, 0, | 58 | 0, 0, 0, 0, |
| 56 | CLARIION_TRESPASS_PAGE, /* Page code */ | 59 | CLARIION_TRESPASS_PAGE, /* Page code */ |
| 57 | 0x02, /* Page length - 2 */ | 60 | 0x02, /* Page length - 2 */ |
| 58 | 0x81, /* Trespass code + Honor reservation bit */ | 61 | 0x01, /* Trespass code */ |
| 59 | 0xff, /* Trespass target */ | 62 | 0xff, /* Trespass target */ |
| 60 | }; | 63 | }; |
| 61 | 64 | ||
| 62 | static unsigned char short_trespass_hr[] = { | 65 | static const char * lun_state[] = |
| 63 | 0, 0, 0, 0, | 66 | { |
| 64 | CLARIION_TRESPASS_PAGE, /* Page code */ | 67 | "not bound", |
| 65 | 0x02, /* Page length - 2 */ | 68 | "bound", |
| 66 | 0x01, /* Trespass code + Honor reservation bit */ | 69 | "owned", |
| 67 | 0xff, /* Trespass target */ | ||
| 68 | }; | 70 | }; |
| 69 | 71 | ||
| 70 | struct clariion_dh_data { | 72 | struct clariion_dh_data { |
| 71 | /* | 73 | /* |
| 74 | * Flags: | ||
| 75 | * CLARIION_SHORT_TRESPASS | ||
| 72 | * Use short trespass command (FC-series) or the long version | 76 | * Use short trespass command (FC-series) or the long version |
| 73 | * (default for AX/CX CLARiiON arrays). | 77 | * (default for AX/CX CLARiiON arrays). |
| 74 | */ | 78 | * |
| 75 | unsigned short_trespass; | 79 | * CLARIION_HONOR_RESERVATIONS |
| 76 | /* | ||
| 77 | * Whether or not (default) to honor SCSI reservations when | 80 | * Whether or not (default) to honor SCSI reservations when |
| 78 | * initiating a switch-over. | 81 | * initiating a switch-over. |
| 79 | */ | 82 | */ |
| 80 | unsigned hr; | 83 | unsigned flags; |
| 81 | /* I/O buffer for both MODE_SELECT and INQUIRY commands. */ | 84 | /* |
| 85 | * I/O buffer for both MODE_SELECT and INQUIRY commands. | ||
| 86 | */ | ||
| 82 | char buffer[CLARIION_BUFFER_SIZE]; | 87 | char buffer[CLARIION_BUFFER_SIZE]; |
| 83 | /* | 88 | /* |
| 84 | * SCSI sense buffer for commands -- assumes serial issuance | 89 | * SCSI sense buffer for commands -- assumes serial issuance |
| 85 | * and completion sequence of all commands for same multipath. | 90 | * and completion sequence of all commands for same multipath. |
| 86 | */ | 91 | */ |
| 87 | unsigned char sense[SCSI_SENSE_BUFFERSIZE]; | 92 | unsigned char sense[SCSI_SENSE_BUFFERSIZE]; |
| 88 | /* which SP (A=0,B=1,UNBOUND=-1) is dflt SP for path's mapped dev */ | 93 | unsigned int senselen; |
| 94 | /* | ||
| 95 | * LUN state | ||
| 96 | */ | ||
| 97 | int lun_state; | ||
| 98 | /* | ||
| 99 | * SP Port number | ||
| 100 | */ | ||
| 101 | int port; | ||
| 102 | /* | ||
| 103 | * which SP (A=0,B=1,UNBOUND=-1) is the default SP for this | ||
| 104 | * path's mapped LUN | ||
| 105 | */ | ||
| 89 | int default_sp; | 106 | int default_sp; |
| 90 | /* which SP (A=0,B=1,UNBOUND=-1) is active for path's mapped dev */ | 107 | /* |
| 108 | * which SP (A=0,B=1,UNBOUND=-1) is the active SP for this | ||
| 109 | * path's mapped LUN | ||
| 110 | */ | ||
| 91 | int current_sp; | 111 | int current_sp; |
| 92 | }; | 112 | }; |
| 93 | 113 | ||
| @@ -102,19 +122,16 @@ static inline struct clariion_dh_data | |||
| 102 | /* | 122 | /* |
| 103 | * Parse MODE_SELECT cmd reply. | 123 | * Parse MODE_SELECT cmd reply. |
| 104 | */ | 124 | */ |
| 105 | static int trespass_endio(struct scsi_device *sdev, int result) | 125 | static int trespass_endio(struct scsi_device *sdev, char *sense) |
| 106 | { | 126 | { |
| 107 | int err = SCSI_DH_OK; | 127 | int err = SCSI_DH_IO; |
| 108 | struct scsi_sense_hdr sshdr; | 128 | struct scsi_sense_hdr sshdr; |
| 109 | struct clariion_dh_data *csdev = get_clariion_data(sdev); | ||
| 110 | char *sense = csdev->sense; | ||
| 111 | 129 | ||
| 112 | if (status_byte(result) == CHECK_CONDITION && | 130 | if (!scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) { |
| 113 | scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) { | 131 | sdev_printk(KERN_ERR, sdev, "%s: Found valid sense data 0x%2x, " |
| 114 | sdev_printk(KERN_ERR, sdev, "Found valid sense data 0x%2x, " | ||
| 115 | "0x%2x, 0x%2x while sending CLARiiON trespass " | 132 | "0x%2x, 0x%2x while sending CLARiiON trespass " |
| 116 | "command.\n", sshdr.sense_key, sshdr.asc, | 133 | "command.\n", CLARIION_NAME, sshdr.sense_key, |
| 117 | sshdr.ascq); | 134 | sshdr.asc, sshdr.ascq); |
| 118 | 135 | ||
| 119 | if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) && | 136 | if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) && |
| 120 | (sshdr.ascq == 0x00)) { | 137 | (sshdr.ascq == 0x00)) { |
| @@ -122,9 +139,9 @@ static int trespass_endio(struct scsi_device *sdev, int result) | |||
| 122 | * Array based copy in progress -- do not send | 139 | * Array based copy in progress -- do not send |
| 123 | * mode_select or copy will be aborted mid-stream. | 140 | * mode_select or copy will be aborted mid-stream. |
| 124 | */ | 141 | */ |
| 125 | sdev_printk(KERN_INFO, sdev, "Array Based Copy in " | 142 | sdev_printk(KERN_INFO, sdev, "%s: Array Based Copy in " |
| 126 | "progress while sending CLARiiON trespass " | 143 | "progress while sending CLARiiON trespass " |
| 127 | "command.\n"); | 144 | "command.\n", CLARIION_NAME); |
| 128 | err = SCSI_DH_DEV_TEMP_BUSY; | 145 | err = SCSI_DH_DEV_TEMP_BUSY; |
| 129 | } else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) && | 146 | } else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) && |
| 130 | (sshdr.ascq == 0x03)) { | 147 | (sshdr.ascq == 0x03)) { |
| @@ -132,160 +149,153 @@ static int trespass_endio(struct scsi_device *sdev, int result) | |||
| 132 | * LUN Not Ready - Manual Intervention Required | 149 | * LUN Not Ready - Manual Intervention Required |
| 133 | * indicates in-progress ucode upgrade (NDU). | 150 | * indicates in-progress ucode upgrade (NDU). |
| 134 | */ | 151 | */ |
| 135 | sdev_printk(KERN_INFO, sdev, "Detected in-progress " | 152 | sdev_printk(KERN_INFO, sdev, "%s: Detected in-progress " |
| 136 | "ucode upgrade NDU operation while sending " | 153 | "ucode upgrade NDU operation while sending " |
| 137 | "CLARiiON trespass command.\n"); | 154 | "CLARiiON trespass command.\n", CLARIION_NAME); |
| 138 | err = SCSI_DH_DEV_TEMP_BUSY; | 155 | err = SCSI_DH_DEV_TEMP_BUSY; |
| 139 | } else | 156 | } else |
| 140 | err = SCSI_DH_DEV_FAILED; | 157 | err = SCSI_DH_DEV_FAILED; |
| 141 | } else if (result) { | 158 | } else { |
| 142 | sdev_printk(KERN_ERR, sdev, "Error 0x%x while sending " | 159 | sdev_printk(KERN_INFO, sdev, |
| 143 | "CLARiiON trespass command.\n", result); | 160 | "%s: failed to send MODE SELECT, no sense available\n", |
| 144 | err = SCSI_DH_IO; | 161 | CLARIION_NAME); |
| 145 | } | 162 | } |
| 146 | |||
| 147 | return err; | 163 | return err; |
| 148 | } | 164 | } |
| 149 | 165 | ||
| 150 | static int parse_sp_info_reply(struct scsi_device *sdev, int result, | 166 | static int parse_sp_info_reply(struct scsi_device *sdev, |
| 151 | int *default_sp, int *current_sp, int *new_current_sp) | 167 | struct clariion_dh_data *csdev) |
| 152 | { | 168 | { |
| 153 | int err = SCSI_DH_OK; | 169 | int err = SCSI_DH_OK; |
| 154 | struct clariion_dh_data *csdev = get_clariion_data(sdev); | ||
| 155 | 170 | ||
| 156 | if (result == 0) { | 171 | /* check for in-progress ucode upgrade (NDU) */ |
| 157 | /* check for in-progress ucode upgrade (NDU) */ | 172 | if (csdev->buffer[48] != 0) { |
| 158 | if (csdev->buffer[48] != 0) { | 173 | sdev_printk(KERN_NOTICE, sdev, "%s: Detected in-progress " |
| 159 | sdev_printk(KERN_NOTICE, sdev, "Detected in-progress " | 174 | "ucode upgrade NDU operation while finding " |
| 160 | "ucode upgrade NDU operation while finding " | 175 | "current active SP.", CLARIION_NAME); |
| 161 | "current active SP."); | 176 | err = SCSI_DH_DEV_TEMP_BUSY; |
| 162 | err = SCSI_DH_DEV_TEMP_BUSY; | 177 | goto out; |
| 163 | } else { | 178 | } |
| 164 | *default_sp = csdev->buffer[5]; | 179 | if (csdev->buffer[4] < 0 || csdev->buffer[4] > 2) { |
| 165 | 180 | /* Invalid buffer format */ | |
| 166 | if (csdev->buffer[4] == 2) | 181 | sdev_printk(KERN_NOTICE, sdev, |
| 167 | /* SP for path is current */ | 182 | "%s: invalid VPD page 0xC0 format\n", |
| 168 | *current_sp = csdev->buffer[8]; | 183 | CLARIION_NAME); |
| 169 | else { | 184 | err = SCSI_DH_NOSYS; |
| 170 | if (csdev->buffer[4] == 1) | 185 | goto out; |
| 171 | /* SP for this path is NOT current */ | 186 | } |
| 172 | if (csdev->buffer[8] == 0) | 187 | switch (csdev->buffer[28] & 0x0f) { |
| 173 | *current_sp = 1; | 188 | case 6: |
| 174 | else | 189 | sdev_printk(KERN_NOTICE, sdev, |
| 175 | *current_sp = 0; | 190 | "%s: ALUA failover mode detected\n", |
| 176 | else | 191 | CLARIION_NAME); |
| 177 | /* unbound LU or LUNZ */ | 192 | break; |
| 178 | *current_sp = CLARIION_UNBOUND_LU; | 193 | case 4: |
| 179 | } | 194 | /* Linux failover */ |
| 180 | *new_current_sp = csdev->buffer[8]; | 195 | break; |
| 181 | } | 196 | default: |
| 182 | } else { | 197 | sdev_printk(KERN_WARNING, sdev, |
| 183 | struct scsi_sense_hdr sshdr; | 198 | "%s: Invalid failover mode %d\n", |
| 184 | 199 | CLARIION_NAME, csdev->buffer[28] & 0x0f); | |
| 185 | err = SCSI_DH_IO; | 200 | err = SCSI_DH_NOSYS; |
| 186 | 201 | goto out; | |
| 187 | if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE, | ||
| 188 | &sshdr)) | ||
| 189 | sdev_printk(KERN_ERR, sdev, "Found valid sense data " | ||
| 190 | "0x%2x, 0x%2x, 0x%2x while finding current " | ||
| 191 | "active SP.", sshdr.sense_key, sshdr.asc, | ||
| 192 | sshdr.ascq); | ||
| 193 | else | ||
| 194 | sdev_printk(KERN_ERR, sdev, "Error 0x%x finding " | ||
| 195 | "current active SP.", result); | ||
| 196 | } | 202 | } |
| 197 | 203 | ||
| 204 | csdev->default_sp = csdev->buffer[5]; | ||
| 205 | csdev->lun_state = csdev->buffer[4]; | ||
| 206 | csdev->current_sp = csdev->buffer[8]; | ||
| 207 | csdev->port = csdev->buffer[7]; | ||
| 208 | |||
| 209 | out: | ||
| 198 | return err; | 210 | return err; |
| 199 | } | 211 | } |
| 200 | 212 | ||
| 201 | static int sp_info_endio(struct scsi_device *sdev, int result, | 213 | #define emc_default_str "FC (Legacy)" |
| 202 | int mode_select_sent, int *done) | 214 | |
| 215 | static char * parse_sp_model(struct scsi_device *sdev, unsigned char *buffer) | ||
| 203 | { | 216 | { |
| 204 | struct clariion_dh_data *csdev = get_clariion_data(sdev); | 217 | unsigned char len = buffer[4] + 5; |
| 205 | int err_flags, default_sp, current_sp, new_current_sp; | 218 | char *sp_model = NULL; |
| 219 | unsigned char sp_len, serial_len; | ||
| 220 | |||
| 221 | if (len < 160) { | ||
| 222 | sdev_printk(KERN_WARNING, sdev, | ||
| 223 | "%s: Invalid information section length %d\n", | ||
| 224 | CLARIION_NAME, len); | ||
| 225 | /* Check for old FC arrays */ | ||
| 226 | if (!strncmp(buffer + 8, "DGC", 3)) { | ||
| 227 | /* Old FC array, not supporting extended information */ | ||
| 228 | sp_model = emc_default_str; | ||
| 229 | } | ||
| 230 | goto out; | ||
| 231 | } | ||
| 206 | 232 | ||
| 207 | err_flags = parse_sp_info_reply(sdev, result, &default_sp, | 233 | /* |
| 208 | ¤t_sp, &new_current_sp); | 234 | * Parse extended information for SP model number |
| 235 | */ | ||
| 236 | serial_len = buffer[160]; | ||
| 237 | if (serial_len == 0 || serial_len + 161 > len) { | ||
| 238 | sdev_printk(KERN_WARNING, sdev, | ||
| 239 | "%s: Invalid array serial number length %d\n", | ||
| 240 | CLARIION_NAME, serial_len); | ||
| 241 | goto out; | ||
| 242 | } | ||
| 243 | sp_len = buffer[99]; | ||
| 244 | if (sp_len == 0 || serial_len + sp_len + 161 > len) { | ||
| 245 | sdev_printk(KERN_WARNING, sdev, | ||
| 246 | "%s: Invalid model number length %d\n", | ||
| 247 | CLARIION_NAME, sp_len); | ||
| 248 | goto out; | ||
| 249 | } | ||
| 250 | sp_model = &buffer[serial_len + 161]; | ||
| 251 | /* Strip whitespace at the end */ | ||
| 252 | while (sp_len > 1 && sp_model[sp_len - 1] == ' ') | ||
| 253 | sp_len--; | ||
| 209 | 254 | ||
| 210 | if (err_flags != SCSI_DH_OK) | 255 | sp_model[sp_len] = '\0'; |
| 211 | goto done; | ||
| 212 | 256 | ||
| 213 | if (mode_select_sent) { | 257 | out: |
| 214 | csdev->default_sp = default_sp; | 258 | return sp_model; |
| 215 | csdev->current_sp = current_sp; | ||
| 216 | } else { | ||
| 217 | /* | ||
| 218 | * Issue the actual module_selec request IFF either | ||
| 219 | * (1) we do not know the identity of the current SP OR | ||
| 220 | * (2) what we think we know is actually correct. | ||
| 221 | */ | ||
| 222 | if ((current_sp != CLARIION_UNBOUND_LU) && | ||
| 223 | (new_current_sp != current_sp)) { | ||
| 224 | |||
| 225 | csdev->default_sp = default_sp; | ||
| 226 | csdev->current_sp = current_sp; | ||
| 227 | |||
| 228 | sdev_printk(KERN_INFO, sdev, "Ignoring path group " | ||
| 229 | "switch-over command for CLARiiON SP%s since " | ||
| 230 | " mapped device is already initialized.", | ||
| 231 | current_sp ? "B" : "A"); | ||
| 232 | if (done) | ||
| 233 | *done = 1; /* as good as doing it */ | ||
| 234 | } | ||
| 235 | } | ||
| 236 | done: | ||
| 237 | return err_flags; | ||
| 238 | } | 259 | } |
| 239 | 260 | ||
| 240 | /* | 261 | /* |
| 241 | * Get block request for REQ_BLOCK_PC command issued to path. Currently | 262 | * Get block request for REQ_BLOCK_PC command issued to path. Currently |
| 242 | * limited to MODE_SELECT (trespass) and INQUIRY (VPD page 0xC0) commands. | 263 | * limited to MODE_SELECT (trespass) and INQUIRY (VPD page 0xC0) commands. |
| 243 | * | 264 | * |
| 244 | * Uses data and sense buffers in hardware handler context structure and | 265 | * Uses data and sense buffers in hardware handler context structure and |
| 245 | * assumes serial servicing of commands, both issuance and completion. | 266 | * assumes serial servicing of commands, both issuance and completion. |
| 246 | */ | 267 | */ |
| 247 | static struct request *get_req(struct scsi_device *sdev, int cmd) | 268 | static struct request *get_req(struct scsi_device *sdev, int cmd, |
| 269 | unsigned char *buffer) | ||
| 248 | { | 270 | { |
| 249 | struct clariion_dh_data *csdev = get_clariion_data(sdev); | ||
| 250 | struct request *rq; | 271 | struct request *rq; |
| 251 | unsigned char *page22; | ||
| 252 | int len = 0; | 272 | int len = 0; |
| 253 | 273 | ||
| 254 | rq = blk_get_request(sdev->request_queue, | 274 | rq = blk_get_request(sdev->request_queue, |
| 255 | (cmd == MODE_SELECT) ? WRITE : READ, GFP_ATOMIC); | 275 | (cmd == MODE_SELECT) ? WRITE : READ, GFP_NOIO); |
| 256 | if (!rq) { | 276 | if (!rq) { |
| 257 | sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed"); | 277 | sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed"); |
| 258 | return NULL; | 278 | return NULL; |
| 259 | } | 279 | } |
| 260 | 280 | ||
| 261 | memset(&rq->cmd, 0, BLK_MAX_CDB); | 281 | memset(rq->cmd, 0, BLK_MAX_CDB); |
| 282 | rq->cmd_len = COMMAND_SIZE(cmd); | ||
| 262 | rq->cmd[0] = cmd; | 283 | rq->cmd[0] = cmd; |
| 263 | rq->cmd_len = COMMAND_SIZE(rq->cmd[0]); | ||
| 264 | 284 | ||
| 265 | switch (cmd) { | 285 | switch (cmd) { |
| 266 | case MODE_SELECT: | 286 | case MODE_SELECT: |
| 267 | if (csdev->short_trespass) { | 287 | len = sizeof(short_trespass); |
| 268 | page22 = csdev->hr ? short_trespass_hr : short_trespass; | 288 | rq->cmd_flags |= REQ_RW; |
| 269 | len = sizeof(short_trespass); | 289 | rq->cmd[1] = 0x10; |
| 270 | } else { | 290 | break; |
| 271 | page22 = csdev->hr ? long_trespass_hr : long_trespass; | 291 | case MODE_SELECT_10: |
| 272 | len = sizeof(long_trespass); | 292 | len = sizeof(long_trespass); |
| 273 | } | ||
| 274 | /* | ||
| 275 | * Can't DMA from kernel BSS -- must copy selected trespass | ||
| 276 | * command mode page contents to context buffer which is | ||
| 277 | * allocated by kmalloc. | ||
| 278 | */ | ||
| 279 | BUG_ON((len > CLARIION_BUFFER_SIZE)); | ||
| 280 | memcpy(csdev->buffer, page22, len); | ||
| 281 | rq->cmd_flags |= REQ_RW; | 293 | rq->cmd_flags |= REQ_RW; |
| 282 | rq->cmd[1] = 0x10; | 294 | rq->cmd[1] = 0x10; |
| 283 | break; | 295 | break; |
| 284 | case INQUIRY: | 296 | case INQUIRY: |
| 285 | rq->cmd[1] = 0x1; | ||
| 286 | rq->cmd[2] = 0xC0; | ||
| 287 | len = CLARIION_BUFFER_SIZE; | 297 | len = CLARIION_BUFFER_SIZE; |
| 288 | memset(csdev->buffer, 0, CLARIION_BUFFER_SIZE); | 298 | memset(buffer, 0, len); |
| 289 | break; | 299 | break; |
| 290 | default: | 300 | default: |
| 291 | BUG_ON(1); | 301 | BUG_ON(1); |
| @@ -298,47 +308,94 @@ static struct request *get_req(struct scsi_device *sdev, int cmd) | |||
| 298 | rq->timeout = CLARIION_TIMEOUT; | 308 | rq->timeout = CLARIION_TIMEOUT; |
| 299 | rq->retries = CLARIION_RETRIES; | 309 | rq->retries = CLARIION_RETRIES; |
| 300 | 310 | ||
| 301 | rq->sense = csdev->sense; | 311 | if (blk_rq_map_kern(rq->q, rq, buffer, len, GFP_NOIO)) { |
| 302 | memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); | 312 | blk_put_request(rq); |
| 303 | rq->sense_len = 0; | ||
| 304 | |||
| 305 | if (blk_rq_map_kern(sdev->request_queue, rq, csdev->buffer, | ||
| 306 | len, GFP_ATOMIC)) { | ||
| 307 | __blk_put_request(rq->q, rq); | ||
| 308 | return NULL; | 313 | return NULL; |
| 309 | } | 314 | } |
| 310 | 315 | ||
| 311 | return rq; | 316 | return rq; |
| 312 | } | 317 | } |
| 313 | 318 | ||
| 314 | static int send_cmd(struct scsi_device *sdev, int cmd) | 319 | static int send_inquiry_cmd(struct scsi_device *sdev, int page, |
| 320 | struct clariion_dh_data *csdev) | ||
| 315 | { | 321 | { |
| 316 | struct request *rq = get_req(sdev, cmd); | 322 | struct request *rq = get_req(sdev, INQUIRY, csdev->buffer); |
| 323 | int err; | ||
| 317 | 324 | ||
| 318 | if (!rq) | 325 | if (!rq) |
| 319 | return SCSI_DH_RES_TEMP_UNAVAIL; | 326 | return SCSI_DH_RES_TEMP_UNAVAIL; |
| 320 | 327 | ||
| 321 | return blk_execute_rq(sdev->request_queue, NULL, rq, 1); | 328 | rq->sense = csdev->sense; |
| 329 | memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); | ||
| 330 | rq->sense_len = csdev->senselen = 0; | ||
| 331 | |||
| 332 | rq->cmd[0] = INQUIRY; | ||
| 333 | if (page != 0) { | ||
| 334 | rq->cmd[1] = 1; | ||
| 335 | rq->cmd[2] = page; | ||
| 336 | } | ||
| 337 | err = blk_execute_rq(sdev->request_queue, NULL, rq, 1); | ||
| 338 | if (err == -EIO) { | ||
| 339 | sdev_printk(KERN_INFO, sdev, | ||
| 340 | "%s: failed to send %s INQUIRY: %x\n", | ||
| 341 | CLARIION_NAME, page?"EVPD":"standard", | ||
| 342 | rq->errors); | ||
| 343 | csdev->senselen = rq->sense_len; | ||
| 344 | err = SCSI_DH_IO; | ||
| 345 | } | ||
| 346 | |||
| 347 | blk_put_request(rq); | ||
| 348 | |||
| 349 | return err; | ||
| 322 | } | 350 | } |
| 323 | 351 | ||
| 324 | static int clariion_activate(struct scsi_device *sdev) | 352 | static int send_trespass_cmd(struct scsi_device *sdev, |
| 353 | struct clariion_dh_data *csdev) | ||
| 325 | { | 354 | { |
| 326 | int result, done = 0; | 355 | struct request *rq; |
| 356 | unsigned char *page22; | ||
| 357 | int err, len, cmd; | ||
| 358 | |||
| 359 | if (csdev->flags & CLARIION_SHORT_TRESPASS) { | ||
| 360 | page22 = short_trespass; | ||
| 361 | if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS)) | ||
| 362 | /* Set Honor Reservations bit */ | ||
| 363 | page22[6] |= 0x80; | ||
| 364 | len = sizeof(short_trespass); | ||
| 365 | cmd = MODE_SELECT; | ||
| 366 | } else { | ||
| 367 | page22 = long_trespass; | ||
| 368 | if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS)) | ||
| 369 | /* Set Honor Reservations bit */ | ||
| 370 | page22[10] |= 0x80; | ||
| 371 | len = sizeof(long_trespass); | ||
| 372 | cmd = MODE_SELECT_10; | ||
| 373 | } | ||
| 374 | BUG_ON((len > CLARIION_BUFFER_SIZE)); | ||
| 375 | memcpy(csdev->buffer, page22, len); | ||
| 327 | 376 | ||
| 328 | result = send_cmd(sdev, INQUIRY); | 377 | rq = get_req(sdev, cmd, csdev->buffer); |
| 329 | result = sp_info_endio(sdev, result, 0, &done); | 378 | if (!rq) |
| 330 | if (result || done) | 379 | return SCSI_DH_RES_TEMP_UNAVAIL; |
| 331 | goto done; | ||
| 332 | 380 | ||
| 333 | result = send_cmd(sdev, MODE_SELECT); | 381 | rq->sense = csdev->sense; |
| 334 | result = trespass_endio(sdev, result); | 382 | memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); |
| 335 | if (result) | 383 | rq->sense_len = csdev->senselen = 0; |
| 336 | goto done; | ||
| 337 | 384 | ||
| 338 | result = send_cmd(sdev, INQUIRY); | 385 | err = blk_execute_rq(sdev->request_queue, NULL, rq, 1); |
| 339 | result = sp_info_endio(sdev, result, 1, NULL); | 386 | if (err == -EIO) { |
| 340 | done: | 387 | if (rq->sense_len) { |
| 341 | return result; | 388 | err = trespass_endio(sdev, csdev->sense); |
| 389 | } else { | ||
| 390 | sdev_printk(KERN_INFO, sdev, | ||
| 391 | "%s: failed to send MODE SELECT: %x\n", | ||
| 392 | CLARIION_NAME, rq->errors); | ||
| 393 | } | ||
| 394 | } | ||
| 395 | |||
| 396 | blk_put_request(rq); | ||
| 397 | |||
| 398 | return err; | ||
| 342 | } | 399 | } |
| 343 | 400 | ||
| 344 | static int clariion_check_sense(struct scsi_device *sdev, | 401 | static int clariion_check_sense(struct scsi_device *sdev, |
| @@ -386,99 +443,215 @@ static int clariion_check_sense(struct scsi_device *sdev, | |||
| 386 | break; | 443 | break; |
| 387 | } | 444 | } |
| 388 | 445 | ||
| 389 | /* success just means we do not care what scsi-ml does */ | 446 | return SCSI_RETURN_NOT_HANDLED; |
| 390 | return SUCCESS; | 447 | } |
| 448 | |||
| 449 | static int clariion_prep_fn(struct scsi_device *sdev, struct request *req) | ||
| 450 | { | ||
| 451 | struct clariion_dh_data *h = get_clariion_data(sdev); | ||
| 452 | int ret = BLKPREP_OK; | ||
| 453 | |||
| 454 | if (h->lun_state != CLARIION_LUN_OWNED) { | ||
| 455 | ret = BLKPREP_KILL; | ||
| 456 | req->cmd_flags |= REQ_QUIET; | ||
| 457 | } | ||
| 458 | return ret; | ||
| 459 | |||
| 460 | } | ||
| 461 | |||
| 462 | static int clariion_std_inquiry(struct scsi_device *sdev, | ||
| 463 | struct clariion_dh_data *csdev) | ||
| 464 | { | ||
| 465 | int err; | ||
| 466 | char *sp_model; | ||
| 467 | |||
| 468 | err = send_inquiry_cmd(sdev, 0, csdev); | ||
| 469 | if (err != SCSI_DH_OK && csdev->senselen) { | ||
| 470 | struct scsi_sense_hdr sshdr; | ||
| 471 | |||
| 472 | if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE, | ||
| 473 | &sshdr)) { | ||
| 474 | sdev_printk(KERN_ERR, sdev, "%s: INQUIRY sense code " | ||
| 475 | "%02x/%02x/%02x\n", CLARIION_NAME, | ||
| 476 | sshdr.sense_key, sshdr.asc, sshdr.ascq); | ||
| 477 | } | ||
| 478 | err = SCSI_DH_IO; | ||
| 479 | goto out; | ||
| 480 | } | ||
| 481 | |||
| 482 | sp_model = parse_sp_model(sdev, csdev->buffer); | ||
| 483 | if (!sp_model) { | ||
| 484 | err = SCSI_DH_DEV_UNSUPP; | ||
| 485 | goto out; | ||
| 486 | } | ||
| 487 | |||
| 488 | /* | ||
| 489 | * FC Series arrays do not support long trespass | ||
| 490 | */ | ||
| 491 | if (!strlen(sp_model) || !strncmp(sp_model, "FC",2)) | ||
| 492 | csdev->flags |= CLARIION_SHORT_TRESPASS; | ||
| 493 | |||
| 494 | sdev_printk(KERN_INFO, sdev, | ||
| 495 | "%s: detected Clariion %s, flags %x\n", | ||
| 496 | CLARIION_NAME, sp_model, csdev->flags); | ||
| 497 | out: | ||
| 498 | return err; | ||
| 391 | } | 499 | } |
| 392 | 500 | ||
| 393 | static const struct { | 501 | static int clariion_send_inquiry(struct scsi_device *sdev, |
| 394 | char *vendor; | 502 | struct clariion_dh_data *csdev) |
| 395 | char *model; | 503 | { |
| 396 | } clariion_dev_list[] = { | 504 | int err, retry = CLARIION_RETRIES; |
| 505 | |||
| 506 | retry: | ||
| 507 | err = send_inquiry_cmd(sdev, 0xC0, csdev); | ||
| 508 | if (err != SCSI_DH_OK && csdev->senselen) { | ||
| 509 | struct scsi_sense_hdr sshdr; | ||
| 510 | |||
| 511 | err = scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE, | ||
| 512 | &sshdr); | ||
| 513 | if (!err) | ||
| 514 | return SCSI_DH_IO; | ||
| 515 | |||
| 516 | err = clariion_check_sense(sdev, &sshdr); | ||
| 517 | if (retry > 0 && err == NEEDS_RETRY) { | ||
| 518 | retry--; | ||
| 519 | goto retry; | ||
| 520 | } | ||
| 521 | sdev_printk(KERN_ERR, sdev, "%s: INQUIRY sense code " | ||
| 522 | "%02x/%02x/%02x\n", CLARIION_NAME, | ||
| 523 | sshdr.sense_key, sshdr.asc, sshdr.ascq); | ||
| 524 | err = SCSI_DH_IO; | ||
| 525 | } else { | ||
| 526 | err = parse_sp_info_reply(sdev, csdev); | ||
| 527 | } | ||
| 528 | return err; | ||
| 529 | } | ||
| 530 | |||
| 531 | static int clariion_activate(struct scsi_device *sdev) | ||
| 532 | { | ||
| 533 | struct clariion_dh_data *csdev = get_clariion_data(sdev); | ||
| 534 | int result; | ||
| 535 | |||
| 536 | result = clariion_send_inquiry(sdev, csdev); | ||
| 537 | if (result != SCSI_DH_OK) | ||
| 538 | goto done; | ||
| 539 | |||
| 540 | if (csdev->lun_state == CLARIION_LUN_OWNED) | ||
| 541 | goto done; | ||
| 542 | |||
| 543 | result = send_trespass_cmd(sdev, csdev); | ||
| 544 | if (result != SCSI_DH_OK) | ||
| 545 | goto done; | ||
| 546 | sdev_printk(KERN_INFO, sdev,"%s: %s trespass command sent\n", | ||
| 547 | CLARIION_NAME, | ||
| 548 | csdev->flags&CLARIION_SHORT_TRESPASS?"short":"long" ); | ||
| 549 | |||
| 550 | /* Update status */ | ||
| 551 | result = clariion_send_inquiry(sdev, csdev); | ||
| 552 | if (result != SCSI_DH_OK) | ||
| 553 | goto done; | ||
| 554 | |||
| 555 | done: | ||
| 556 | sdev_printk(KERN_INFO, sdev, | ||
| 557 | "%s: at SP %c Port %d (%s, default SP %c)\n", | ||
| 558 | CLARIION_NAME, csdev->current_sp + 'A', | ||
| 559 | csdev->port, lun_state[csdev->lun_state], | ||
| 560 | csdev->default_sp + 'A'); | ||
| 561 | |||
| 562 | return result; | ||
| 563 | } | ||
| 564 | |||
| 565 | const struct scsi_dh_devlist clariion_dev_list[] = { | ||
| 397 | {"DGC", "RAID"}, | 566 | {"DGC", "RAID"}, |
| 398 | {"DGC", "DISK"}, | 567 | {"DGC", "DISK"}, |
| 568 | {"DGC", "VRAID"}, | ||
| 399 | {NULL, NULL}, | 569 | {NULL, NULL}, |
| 400 | }; | 570 | }; |
| 401 | 571 | ||
| 402 | static int clariion_bus_notify(struct notifier_block *, unsigned long, void *); | 572 | static int clariion_bus_attach(struct scsi_device *sdev); |
| 573 | static void clariion_bus_detach(struct scsi_device *sdev); | ||
| 403 | 574 | ||
| 404 | static struct scsi_device_handler clariion_dh = { | 575 | static struct scsi_device_handler clariion_dh = { |
| 405 | .name = CLARIION_NAME, | 576 | .name = CLARIION_NAME, |
| 406 | .module = THIS_MODULE, | 577 | .module = THIS_MODULE, |
| 407 | .nb.notifier_call = clariion_bus_notify, | 578 | .devlist = clariion_dev_list, |
| 579 | .attach = clariion_bus_attach, | ||
| 580 | .detach = clariion_bus_detach, | ||
| 408 | .check_sense = clariion_check_sense, | 581 | .check_sense = clariion_check_sense, |
| 409 | .activate = clariion_activate, | 582 | .activate = clariion_activate, |
| 583 | .prep_fn = clariion_prep_fn, | ||
| 410 | }; | 584 | }; |
| 411 | 585 | ||
| 412 | /* | 586 | /* |
| 413 | * TODO: need some interface so we can set trespass values | 587 | * TODO: need some interface so we can set trespass values |
| 414 | */ | 588 | */ |
| 415 | static int clariion_bus_notify(struct notifier_block *nb, | 589 | static int clariion_bus_attach(struct scsi_device *sdev) |
| 416 | unsigned long action, void *data) | ||
| 417 | { | 590 | { |
| 418 | struct device *dev = data; | ||
| 419 | struct scsi_device *sdev; | ||
| 420 | struct scsi_dh_data *scsi_dh_data; | 591 | struct scsi_dh_data *scsi_dh_data; |
| 421 | struct clariion_dh_data *h; | 592 | struct clariion_dh_data *h; |
| 422 | int i, found = 0; | ||
| 423 | unsigned long flags; | 593 | unsigned long flags; |
| 594 | int err; | ||
| 424 | 595 | ||
| 425 | if (!scsi_is_sdev_device(dev)) | 596 | scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) |
| 426 | return 0; | 597 | + sizeof(*h) , GFP_KERNEL); |
| 598 | if (!scsi_dh_data) { | ||
| 599 | sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n", | ||
| 600 | CLARIION_NAME); | ||
| 601 | return -ENOMEM; | ||
| 602 | } | ||
| 427 | 603 | ||
| 428 | sdev = to_scsi_device(dev); | 604 | scsi_dh_data->scsi_dh = &clariion_dh; |
| 605 | h = (struct clariion_dh_data *) scsi_dh_data->buf; | ||
| 606 | h->lun_state = CLARIION_LUN_UNINITIALIZED; | ||
| 607 | h->default_sp = CLARIION_UNBOUND_LU; | ||
| 608 | h->current_sp = CLARIION_UNBOUND_LU; | ||
| 429 | 609 | ||
| 430 | if (action == BUS_NOTIFY_ADD_DEVICE) { | 610 | err = clariion_std_inquiry(sdev, h); |
| 431 | for (i = 0; clariion_dev_list[i].vendor; i++) { | 611 | if (err != SCSI_DH_OK) |
| 432 | if (!strncmp(sdev->vendor, clariion_dev_list[i].vendor, | 612 | goto failed; |
| 433 | strlen(clariion_dev_list[i].vendor)) && | ||
| 434 | !strncmp(sdev->model, clariion_dev_list[i].model, | ||
| 435 | strlen(clariion_dev_list[i].model))) { | ||
| 436 | found = 1; | ||
| 437 | break; | ||
| 438 | } | ||
| 439 | } | ||
| 440 | if (!found) | ||
| 441 | goto out; | ||
| 442 | |||
| 443 | scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) | ||
| 444 | + sizeof(*h) , GFP_KERNEL); | ||
| 445 | if (!scsi_dh_data) { | ||
| 446 | sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n", | ||
| 447 | CLARIION_NAME); | ||
| 448 | goto out; | ||
| 449 | } | ||
| 450 | 613 | ||
| 451 | scsi_dh_data->scsi_dh = &clariion_dh; | 614 | err = clariion_send_inquiry(sdev, h); |
| 452 | h = (struct clariion_dh_data *) scsi_dh_data->buf; | 615 | if (err != SCSI_DH_OK) |
| 453 | h->default_sp = CLARIION_UNBOUND_LU; | 616 | goto failed; |
| 454 | h->current_sp = CLARIION_UNBOUND_LU; | ||
| 455 | 617 | ||
| 456 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); | 618 | if (!try_module_get(THIS_MODULE)) |
| 457 | sdev->scsi_dh_data = scsi_dh_data; | 619 | goto failed; |
| 458 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | ||
| 459 | 620 | ||
| 460 | sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", CLARIION_NAME); | 621 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); |
| 461 | try_module_get(THIS_MODULE); | 622 | sdev->scsi_dh_data = scsi_dh_data; |
| 623 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | ||
| 462 | 624 | ||
| 463 | } else if (action == BUS_NOTIFY_DEL_DEVICE) { | 625 | sdev_printk(KERN_INFO, sdev, |
| 464 | if (sdev->scsi_dh_data == NULL || | 626 | "%s: connected to SP %c Port %d (%s, default SP %c)\n", |
| 465 | sdev->scsi_dh_data->scsi_dh != &clariion_dh) | 627 | CLARIION_NAME, h->current_sp + 'A', |
| 466 | goto out; | 628 | h->port, lun_state[h->lun_state], |
| 629 | h->default_sp + 'A'); | ||
| 467 | 630 | ||
| 468 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); | 631 | return 0; |
| 469 | scsi_dh_data = sdev->scsi_dh_data; | ||
| 470 | sdev->scsi_dh_data = NULL; | ||
| 471 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | ||
| 472 | 632 | ||
| 473 | sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n", | 633 | failed: |
| 474 | CLARIION_NAME); | 634 | kfree(scsi_dh_data); |
| 635 | sdev_printk(KERN_ERR, sdev, "%s: not attached\n", | ||
| 636 | CLARIION_NAME); | ||
| 637 | return -EINVAL; | ||
| 638 | } | ||
| 475 | 639 | ||
| 476 | kfree(scsi_dh_data); | 640 | static void clariion_bus_detach(struct scsi_device *sdev) |
| 477 | module_put(THIS_MODULE); | 641 | { |
| 478 | } | 642 | struct scsi_dh_data *scsi_dh_data; |
| 643 | unsigned long flags; | ||
| 479 | 644 | ||
| 480 | out: | 645 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); |
| 481 | return 0; | 646 | scsi_dh_data = sdev->scsi_dh_data; |
| 647 | sdev->scsi_dh_data = NULL; | ||
| 648 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | ||
| 649 | |||
| 650 | sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", | ||
| 651 | CLARIION_NAME); | ||
| 652 | |||
| 653 | kfree(scsi_dh_data); | ||
| 654 | module_put(THIS_MODULE); | ||
| 482 | } | 655 | } |
| 483 | 656 | ||
| 484 | static int __init clariion_init(void) | 657 | static int __init clariion_init(void) |
| @@ -487,7 +660,8 @@ static int __init clariion_init(void) | |||
| 487 | 660 | ||
| 488 | r = scsi_register_device_handler(&clariion_dh); | 661 | r = scsi_register_device_handler(&clariion_dh); |
| 489 | if (r != 0) | 662 | if (r != 0) |
| 490 | printk(KERN_ERR "Failed to register scsi device handler."); | 663 | printk(KERN_ERR "%s: Failed to register scsi device handler.", |
| 664 | CLARIION_NAME); | ||
| 491 | return r; | 665 | return r; |
| 492 | } | 666 | } |
| 493 | 667 | ||
diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c index ae6be87d6a83..9c7a1f8ebb72 100644 --- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c +++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | * | 4 | * |
| 5 | * Copyright (C) 2006 Red Hat, Inc. All rights reserved. | 5 | * Copyright (C) 2006 Red Hat, Inc. All rights reserved. |
| 6 | * Copyright (C) 2006 Mike Christie | 6 | * Copyright (C) 2006 Mike Christie |
| 7 | * Copyright (C) 2008 Hannes Reinecke <hare@suse.de> | ||
| 7 | * | 8 | * |
| 8 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
| 9 | * it under the terms of the GNU General Public License as published by | 10 | * it under the terms of the GNU General Public License as published by |
| @@ -25,13 +26,18 @@ | |||
| 25 | #include <scsi/scsi_eh.h> | 26 | #include <scsi/scsi_eh.h> |
| 26 | #include <scsi/scsi_dh.h> | 27 | #include <scsi/scsi_dh.h> |
| 27 | 28 | ||
| 28 | #define HP_SW_NAME "hp_sw" | 29 | #define HP_SW_NAME "hp_sw" |
| 29 | 30 | ||
| 30 | #define HP_SW_TIMEOUT (60 * HZ) | 31 | #define HP_SW_TIMEOUT (60 * HZ) |
| 31 | #define HP_SW_RETRIES 3 | 32 | #define HP_SW_RETRIES 3 |
| 33 | |||
| 34 | #define HP_SW_PATH_UNINITIALIZED -1 | ||
| 35 | #define HP_SW_PATH_ACTIVE 0 | ||
| 36 | #define HP_SW_PATH_PASSIVE 1 | ||
| 32 | 37 | ||
| 33 | struct hp_sw_dh_data { | 38 | struct hp_sw_dh_data { |
| 34 | unsigned char sense[SCSI_SENSE_BUFFERSIZE]; | 39 | unsigned char sense[SCSI_SENSE_BUFFERSIZE]; |
| 40 | int path_state; | ||
| 35 | int retries; | 41 | int retries; |
| 36 | }; | 42 | }; |
| 37 | 43 | ||
| @@ -42,51 +48,161 @@ static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev) | |||
| 42 | return ((struct hp_sw_dh_data *) scsi_dh_data->buf); | 48 | return ((struct hp_sw_dh_data *) scsi_dh_data->buf); |
| 43 | } | 49 | } |
| 44 | 50 | ||
| 45 | static int hp_sw_done(struct scsi_device *sdev) | 51 | /* |
| 52 | * tur_done - Handle TEST UNIT READY return status | ||
| 53 | * @sdev: sdev the command has been sent to | ||
| 54 | * @errors: blk error code | ||
| 55 | * | ||
| 56 | * Returns SCSI_DH_DEV_OFFLINED if the sdev is on the passive path | ||
| 57 | */ | ||
| 58 | static int tur_done(struct scsi_device *sdev, unsigned char *sense) | ||
| 46 | { | 59 | { |
| 47 | struct hp_sw_dh_data *h = get_hp_sw_data(sdev); | ||
| 48 | struct scsi_sense_hdr sshdr; | 60 | struct scsi_sense_hdr sshdr; |
| 49 | int rc; | 61 | int ret; |
| 50 | |||
| 51 | sdev_printk(KERN_INFO, sdev, "hp_sw_done\n"); | ||
| 52 | 62 | ||
| 53 | rc = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sshdr); | 63 | ret = scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr); |
| 54 | if (!rc) | 64 | if (!ret) { |
| 65 | sdev_printk(KERN_WARNING, sdev, | ||
| 66 | "%s: sending tur failed, no sense available\n", | ||
| 67 | HP_SW_NAME); | ||
| 68 | ret = SCSI_DH_IO; | ||
| 55 | goto done; | 69 | goto done; |
| 70 | } | ||
| 56 | switch (sshdr.sense_key) { | 71 | switch (sshdr.sense_key) { |
| 72 | case UNIT_ATTENTION: | ||
| 73 | ret = SCSI_DH_IMM_RETRY; | ||
| 74 | break; | ||
| 57 | case NOT_READY: | 75 | case NOT_READY: |
| 58 | if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) { | 76 | if ((sshdr.asc == 0x04) && (sshdr.ascq == 2)) { |
| 59 | rc = SCSI_DH_RETRY; | 77 | /* |
| 60 | h->retries++; | 78 | * LUN not ready - Initialization command required |
| 79 | * | ||
| 80 | * This is the passive path | ||
| 81 | */ | ||
| 82 | ret = SCSI_DH_DEV_OFFLINED; | ||
| 61 | break; | 83 | break; |
| 62 | } | 84 | } |
| 63 | /* fall through */ | 85 | /* Fallthrough */ |
| 64 | default: | 86 | default: |
| 65 | h->retries++; | 87 | sdev_printk(KERN_WARNING, sdev, |
| 66 | rc = SCSI_DH_IMM_RETRY; | 88 | "%s: sending tur failed, sense %x/%x/%x\n", |
| 89 | HP_SW_NAME, sshdr.sense_key, sshdr.asc, | ||
| 90 | sshdr.ascq); | ||
| 91 | break; | ||
| 67 | } | 92 | } |
| 68 | 93 | ||
| 69 | done: | 94 | done: |
| 70 | if (rc == SCSI_DH_OK || rc == SCSI_DH_IO) | 95 | return ret; |
| 71 | h->retries = 0; | 96 | } |
| 72 | else if (h->retries > HP_SW_RETRIES) { | 97 | |
| 73 | h->retries = 0; | 98 | /* |
| 99 | * hp_sw_tur - Send TEST UNIT READY | ||
| 100 | * @sdev: sdev command should be sent to | ||
| 101 | * | ||
| 102 | * Use the TEST UNIT READY command to determine | ||
| 103 | * the path state. | ||
| 104 | */ | ||
| 105 | static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h) | ||
| 106 | { | ||
| 107 | struct request *req; | ||
| 108 | int ret; | ||
| 109 | |||
| 110 | req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO); | ||
| 111 | if (!req) | ||
| 112 | return SCSI_DH_RES_TEMP_UNAVAIL; | ||
| 113 | |||
| 114 | req->cmd_type = REQ_TYPE_BLOCK_PC; | ||
| 115 | req->cmd_flags |= REQ_FAILFAST; | ||
| 116 | req->cmd_len = COMMAND_SIZE(TEST_UNIT_READY); | ||
| 117 | memset(req->cmd, 0, MAX_COMMAND_SIZE); | ||
| 118 | req->cmd[0] = TEST_UNIT_READY; | ||
| 119 | req->timeout = HP_SW_TIMEOUT; | ||
| 120 | req->sense = h->sense; | ||
| 121 | memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE); | ||
| 122 | req->sense_len = 0; | ||
| 123 | |||
| 124 | retry: | ||
| 125 | ret = blk_execute_rq(req->q, NULL, req, 1); | ||
| 126 | if (ret == -EIO) { | ||
| 127 | if (req->sense_len > 0) { | ||
| 128 | ret = tur_done(sdev, h->sense); | ||
| 129 | } else { | ||
| 130 | sdev_printk(KERN_WARNING, sdev, | ||
| 131 | "%s: sending tur failed with %x\n", | ||
| 132 | HP_SW_NAME, req->errors); | ||
| 133 | ret = SCSI_DH_IO; | ||
| 134 | } | ||
| 135 | } else { | ||
| 136 | h->path_state = HP_SW_PATH_ACTIVE; | ||
| 137 | ret = SCSI_DH_OK; | ||
| 138 | } | ||
| 139 | if (ret == SCSI_DH_IMM_RETRY) | ||
| 140 | goto retry; | ||
| 141 | if (ret == SCSI_DH_DEV_OFFLINED) { | ||
| 142 | h->path_state = HP_SW_PATH_PASSIVE; | ||
| 143 | ret = SCSI_DH_OK; | ||
| 144 | } | ||
| 145 | |||
| 146 | blk_put_request(req); | ||
| 147 | |||
| 148 | return ret; | ||
| 149 | } | ||
| 150 | |||
| 151 | /* | ||
| 152 | * start_done - Handle START STOP UNIT return status | ||
| 153 | * @sdev: sdev the command has been sent to | ||
| 154 | * @errors: blk error code | ||
| 155 | */ | ||
| 156 | static int start_done(struct scsi_device *sdev, unsigned char *sense) | ||
| 157 | { | ||
| 158 | struct scsi_sense_hdr sshdr; | ||
| 159 | int rc; | ||
| 160 | |||
| 161 | rc = scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr); | ||
| 162 | if (!rc) { | ||
| 163 | sdev_printk(KERN_WARNING, sdev, | ||
| 164 | "%s: sending start_stop_unit failed, " | ||
| 165 | "no sense available\n", | ||
| 166 | HP_SW_NAME); | ||
| 167 | return SCSI_DH_IO; | ||
| 168 | } | ||
| 169 | switch (sshdr.sense_key) { | ||
| 170 | case NOT_READY: | ||
| 171 | if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) { | ||
| 172 | /* | ||
| 173 | * LUN not ready - manual intervention required | ||
| 174 | * | ||
| 175 | * Switch-over in progress, retry. | ||
| 176 | */ | ||
| 177 | rc = SCSI_DH_RETRY; | ||
| 178 | break; | ||
| 179 | } | ||
| 180 | /* fall through */ | ||
| 181 | default: | ||
| 182 | sdev_printk(KERN_WARNING, sdev, | ||
| 183 | "%s: sending start_stop_unit failed, sense %x/%x/%x\n", | ||
| 184 | HP_SW_NAME, sshdr.sense_key, sshdr.asc, | ||
| 185 | sshdr.ascq); | ||
| 74 | rc = SCSI_DH_IO; | 186 | rc = SCSI_DH_IO; |
| 75 | } | 187 | } |
| 188 | |||
| 76 | return rc; | 189 | return rc; |
| 77 | } | 190 | } |
| 78 | 191 | ||
| 79 | static int hp_sw_activate(struct scsi_device *sdev) | 192 | /* |
| 193 | * hp_sw_start_stop - Send START STOP UNIT command | ||
| 194 | * @sdev: sdev command should be sent to | ||
| 195 | * | ||
| 196 | * Sending START STOP UNIT activates the SP. | ||
| 197 | */ | ||
| 198 | static int hp_sw_start_stop(struct scsi_device *sdev, struct hp_sw_dh_data *h) | ||
| 80 | { | 199 | { |
| 81 | struct hp_sw_dh_data *h = get_hp_sw_data(sdev); | ||
| 82 | struct request *req; | 200 | struct request *req; |
| 83 | int ret = SCSI_DH_RES_TEMP_UNAVAIL; | 201 | int ret, retry; |
| 84 | 202 | ||
| 85 | req = blk_get_request(sdev->request_queue, WRITE, GFP_ATOMIC); | 203 | req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO); |
| 86 | if (!req) | 204 | if (!req) |
| 87 | goto done; | 205 | return SCSI_DH_RES_TEMP_UNAVAIL; |
| 88 | |||
| 89 | sdev_printk(KERN_INFO, sdev, "sending START_STOP."); | ||
| 90 | 206 | ||
| 91 | req->cmd_type = REQ_TYPE_BLOCK_PC; | 207 | req->cmd_type = REQ_TYPE_BLOCK_PC; |
| 92 | req->cmd_flags |= REQ_FAILFAST; | 208 | req->cmd_flags |= REQ_FAILFAST; |
| @@ -98,95 +214,153 @@ static int hp_sw_activate(struct scsi_device *sdev) | |||
| 98 | req->sense = h->sense; | 214 | req->sense = h->sense; |
| 99 | memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE); | 215 | memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE); |
| 100 | req->sense_len = 0; | 216 | req->sense_len = 0; |
| 217 | retry = h->retries; | ||
| 101 | 218 | ||
| 219 | retry: | ||
| 102 | ret = blk_execute_rq(req->q, NULL, req, 1); | 220 | ret = blk_execute_rq(req->q, NULL, req, 1); |
| 103 | if (!ret) /* SUCCESS */ | 221 | if (ret == -EIO) { |
| 104 | ret = hp_sw_done(sdev); | 222 | if (req->sense_len > 0) { |
| 105 | else | 223 | ret = start_done(sdev, h->sense); |
| 224 | } else { | ||
| 225 | sdev_printk(KERN_WARNING, sdev, | ||
| 226 | "%s: sending start_stop_unit failed with %x\n", | ||
| 227 | HP_SW_NAME, req->errors); | ||
| 228 | ret = SCSI_DH_IO; | ||
| 229 | } | ||
| 230 | } else | ||
| 231 | ret = SCSI_DH_OK; | ||
| 232 | |||
| 233 | if (ret == SCSI_DH_RETRY) { | ||
| 234 | if (--retry) | ||
| 235 | goto retry; | ||
| 106 | ret = SCSI_DH_IO; | 236 | ret = SCSI_DH_IO; |
| 107 | done: | 237 | } |
| 238 | |||
| 239 | blk_put_request(req); | ||
| 240 | |||
| 241 | return ret; | ||
| 242 | } | ||
| 243 | |||
| 244 | static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req) | ||
| 245 | { | ||
| 246 | struct hp_sw_dh_data *h = get_hp_sw_data(sdev); | ||
| 247 | int ret = BLKPREP_OK; | ||
| 248 | |||
| 249 | if (h->path_state != HP_SW_PATH_ACTIVE) { | ||
| 250 | ret = BLKPREP_KILL; | ||
| 251 | req->cmd_flags |= REQ_QUIET; | ||
| 252 | } | ||
| 253 | return ret; | ||
| 254 | |||
| 255 | } | ||
| 256 | |||
| 257 | /* | ||
| 258 | * hp_sw_activate - Activate a path | ||
| 259 | * @sdev: sdev on the path to be activated | ||
| 260 | * | ||
| 261 | * The HP Active/Passive firmware is pretty simple; | ||
| 262 | * the passive path reports NOT READY with sense codes | ||
| 263 | * 0x04/0x02; a START STOP UNIT command will then | ||
| 264 | * activate the passive path (and deactivate the | ||
| 265 | * previously active one). | ||
| 266 | */ | ||
| 267 | static int hp_sw_activate(struct scsi_device *sdev) | ||
| 268 | { | ||
| 269 | int ret = SCSI_DH_OK; | ||
| 270 | struct hp_sw_dh_data *h = get_hp_sw_data(sdev); | ||
| 271 | |||
| 272 | ret = hp_sw_tur(sdev, h); | ||
| 273 | |||
| 274 | if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE) { | ||
| 275 | ret = hp_sw_start_stop(sdev, h); | ||
| 276 | if (ret == SCSI_DH_OK) | ||
| 277 | sdev_printk(KERN_INFO, sdev, | ||
| 278 | "%s: activated path\n", | ||
| 279 | HP_SW_NAME); | ||
| 280 | } | ||
| 281 | |||
| 108 | return ret; | 282 | return ret; |
| 109 | } | 283 | } |
| 110 | 284 | ||
| 111 | static const struct { | 285 | const struct scsi_dh_devlist hp_sw_dh_data_list[] = { |
| 112 | char *vendor; | 286 | {"COMPAQ", "MSA1000 VOLUME"}, |
| 113 | char *model; | 287 | {"COMPAQ", "HSV110"}, |
| 114 | } hp_sw_dh_data_list[] = { | 288 | {"HP", "HSV100"}, |
| 115 | {"COMPAQ", "MSA"}, | ||
| 116 | {"HP", "HSV"}, | ||
| 117 | {"DEC", "HSG80"}, | 289 | {"DEC", "HSG80"}, |
| 118 | {NULL, NULL}, | 290 | {NULL, NULL}, |
| 119 | }; | 291 | }; |
| 120 | 292 | ||
| 121 | static int hp_sw_bus_notify(struct notifier_block *, unsigned long, void *); | 293 | static int hp_sw_bus_attach(struct scsi_device *sdev); |
| 294 | static void hp_sw_bus_detach(struct scsi_device *sdev); | ||
| 122 | 295 | ||
| 123 | static struct scsi_device_handler hp_sw_dh = { | 296 | static struct scsi_device_handler hp_sw_dh = { |
| 124 | .name = HP_SW_NAME, | 297 | .name = HP_SW_NAME, |
| 125 | .module = THIS_MODULE, | 298 | .module = THIS_MODULE, |
| 126 | .nb.notifier_call = hp_sw_bus_notify, | 299 | .devlist = hp_sw_dh_data_list, |
| 300 | .attach = hp_sw_bus_attach, | ||
| 301 | .detach = hp_sw_bus_detach, | ||
| 127 | .activate = hp_sw_activate, | 302 | .activate = hp_sw_activate, |
| 303 | .prep_fn = hp_sw_prep_fn, | ||
| 128 | }; | 304 | }; |
| 129 | 305 | ||
| 130 | static int hp_sw_bus_notify(struct notifier_block *nb, | 306 | static int hp_sw_bus_attach(struct scsi_device *sdev) |
| 131 | unsigned long action, void *data) | ||
| 132 | { | 307 | { |
| 133 | struct device *dev = data; | ||
| 134 | struct scsi_device *sdev; | ||
| 135 | struct scsi_dh_data *scsi_dh_data; | 308 | struct scsi_dh_data *scsi_dh_data; |
| 136 | int i, found = 0; | 309 | struct hp_sw_dh_data *h; |
| 137 | unsigned long flags; | 310 | unsigned long flags; |
| 311 | int ret; | ||
| 138 | 312 | ||
| 139 | if (!scsi_is_sdev_device(dev)) | 313 | scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) |
| 314 | + sizeof(struct hp_sw_dh_data) , GFP_KERNEL); | ||
| 315 | if (!scsi_dh_data) { | ||
| 316 | sdev_printk(KERN_ERR, sdev, "%s: Attach Failed\n", | ||
| 317 | HP_SW_NAME); | ||
| 140 | return 0; | 318 | return 0; |
| 319 | } | ||
| 141 | 320 | ||
| 142 | sdev = to_scsi_device(dev); | 321 | scsi_dh_data->scsi_dh = &hp_sw_dh; |
| 143 | 322 | h = (struct hp_sw_dh_data *) scsi_dh_data->buf; | |
| 144 | if (action == BUS_NOTIFY_ADD_DEVICE) { | 323 | h->path_state = HP_SW_PATH_UNINITIALIZED; |
| 145 | for (i = 0; hp_sw_dh_data_list[i].vendor; i++) { | 324 | h->retries = HP_SW_RETRIES; |
| 146 | if (!strncmp(sdev->vendor, hp_sw_dh_data_list[i].vendor, | ||
| 147 | strlen(hp_sw_dh_data_list[i].vendor)) && | ||
| 148 | !strncmp(sdev->model, hp_sw_dh_data_list[i].model, | ||
| 149 | strlen(hp_sw_dh_data_list[i].model))) { | ||
| 150 | found = 1; | ||
| 151 | break; | ||
| 152 | } | ||
| 153 | } | ||
| 154 | if (!found) | ||
| 155 | goto out; | ||
| 156 | 325 | ||
| 157 | scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) | 326 | ret = hp_sw_tur(sdev, h); |
| 158 | + sizeof(struct hp_sw_dh_data) , GFP_KERNEL); | 327 | if (ret != SCSI_DH_OK || h->path_state == HP_SW_PATH_UNINITIALIZED) |
| 159 | if (!scsi_dh_data) { | 328 | goto failed; |
| 160 | sdev_printk(KERN_ERR, sdev, "Attach Failed %s.\n", | ||
| 161 | HP_SW_NAME); | ||
| 162 | goto out; | ||
| 163 | } | ||
| 164 | 329 | ||
| 165 | scsi_dh_data->scsi_dh = &hp_sw_dh; | 330 | if (!try_module_get(THIS_MODULE)) |
| 166 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); | 331 | goto failed; |
| 167 | sdev->scsi_dh_data = scsi_dh_data; | ||
| 168 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | ||
| 169 | try_module_get(THIS_MODULE); | ||
| 170 | 332 | ||
| 171 | sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", HP_SW_NAME); | 333 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); |
| 172 | } else if (action == BUS_NOTIFY_DEL_DEVICE) { | 334 | sdev->scsi_dh_data = scsi_dh_data; |
| 173 | if (sdev->scsi_dh_data == NULL || | 335 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); |
| 174 | sdev->scsi_dh_data->scsi_dh != &hp_sw_dh) | ||
| 175 | goto out; | ||
| 176 | 336 | ||
| 177 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); | 337 | sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n", |
| 178 | scsi_dh_data = sdev->scsi_dh_data; | 338 | HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE? |
| 179 | sdev->scsi_dh_data = NULL; | 339 | "active":"passive"); |
| 180 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | ||
| 181 | module_put(THIS_MODULE); | ||
| 182 | 340 | ||
| 183 | sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n", HP_SW_NAME); | 341 | return 0; |
| 184 | 342 | ||
| 185 | kfree(scsi_dh_data); | 343 | failed: |
| 186 | } | 344 | kfree(scsi_dh_data); |
| 345 | sdev_printk(KERN_ERR, sdev, "%s: not attached\n", | ||
| 346 | HP_SW_NAME); | ||
| 347 | return -EINVAL; | ||
| 348 | } | ||
| 187 | 349 | ||
| 188 | out: | 350 | static void hp_sw_bus_detach( struct scsi_device *sdev ) |
| 189 | return 0; | 351 | { |
| 352 | struct scsi_dh_data *scsi_dh_data; | ||
| 353 | unsigned long flags; | ||
| 354 | |||
| 355 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); | ||
| 356 | scsi_dh_data = sdev->scsi_dh_data; | ||
| 357 | sdev->scsi_dh_data = NULL; | ||
| 358 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | ||
| 359 | module_put(THIS_MODULE); | ||
| 360 | |||
| 361 | sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", HP_SW_NAME); | ||
| 362 | |||
| 363 | kfree(scsi_dh_data); | ||
| 190 | } | 364 | } |
| 191 | 365 | ||
| 192 | static int __init hp_sw_init(void) | 366 | static int __init hp_sw_init(void) |
| @@ -202,6 +376,6 @@ static void __exit hp_sw_exit(void) | |||
| 202 | module_init(hp_sw_init); | 376 | module_init(hp_sw_init); |
| 203 | module_exit(hp_sw_exit); | 377 | module_exit(hp_sw_exit); |
| 204 | 378 | ||
| 205 | MODULE_DESCRIPTION("HP MSA 1000"); | 379 | MODULE_DESCRIPTION("HP Active/Passive driver"); |
| 206 | MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu"); | 380 | MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu"); |
| 207 | MODULE_LICENSE("GPL"); | 381 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c index fdf34b0ec6e1..b093a501f8ae 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c | |||
| @@ -173,6 +173,11 @@ struct rdac_dh_data { | |||
| 173 | #define RDAC_STATE_ACTIVE 0 | 173 | #define RDAC_STATE_ACTIVE 0 |
| 174 | #define RDAC_STATE_PASSIVE 1 | 174 | #define RDAC_STATE_PASSIVE 1 |
| 175 | unsigned char state; | 175 | unsigned char state; |
| 176 | |||
| 177 | #define RDAC_LUN_UNOWNED 0 | ||
| 178 | #define RDAC_LUN_OWNED 1 | ||
| 179 | #define RDAC_LUN_AVT 2 | ||
| 180 | char lun_state; | ||
| 176 | unsigned char sense[SCSI_SENSE_BUFFERSIZE]; | 181 | unsigned char sense[SCSI_SENSE_BUFFERSIZE]; |
| 177 | union { | 182 | union { |
| 178 | struct c2_inquiry c2; | 183 | struct c2_inquiry c2; |
| @@ -182,6 +187,13 @@ struct rdac_dh_data { | |||
| 182 | } inq; | 187 | } inq; |
| 183 | }; | 188 | }; |
| 184 | 189 | ||
| 190 | static const char *lun_state[] = | ||
| 191 | { | ||
| 192 | "unowned", | ||
| 193 | "owned", | ||
| 194 | "owned (AVT mode)", | ||
| 195 | }; | ||
| 196 | |||
| 185 | static LIST_HEAD(ctlr_list); | 197 | static LIST_HEAD(ctlr_list); |
| 186 | static DEFINE_SPINLOCK(list_lock); | 198 | static DEFINE_SPINLOCK(list_lock); |
| 187 | 199 | ||
| @@ -197,9 +209,8 @@ static struct request *get_rdac_req(struct scsi_device *sdev, | |||
| 197 | { | 209 | { |
| 198 | struct request *rq; | 210 | struct request *rq; |
| 199 | struct request_queue *q = sdev->request_queue; | 211 | struct request_queue *q = sdev->request_queue; |
| 200 | struct rdac_dh_data *h = get_rdac_data(sdev); | ||
| 201 | 212 | ||
| 202 | rq = blk_get_request(q, rw, GFP_KERNEL); | 213 | rq = blk_get_request(q, rw, GFP_NOIO); |
| 203 | 214 | ||
| 204 | if (!rq) { | 215 | if (!rq) { |
| 205 | sdev_printk(KERN_INFO, sdev, | 216 | sdev_printk(KERN_INFO, sdev, |
| @@ -207,17 +218,14 @@ static struct request *get_rdac_req(struct scsi_device *sdev, | |||
| 207 | return NULL; | 218 | return NULL; |
| 208 | } | 219 | } |
| 209 | 220 | ||
| 210 | if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_KERNEL)) { | 221 | if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_NOIO)) { |
| 211 | blk_put_request(rq); | 222 | blk_put_request(rq); |
| 212 | sdev_printk(KERN_INFO, sdev, | 223 | sdev_printk(KERN_INFO, sdev, |
| 213 | "get_rdac_req: blk_rq_map_kern failed.\n"); | 224 | "get_rdac_req: blk_rq_map_kern failed.\n"); |
| 214 | return NULL; | 225 | return NULL; |
| 215 | } | 226 | } |
| 216 | 227 | ||
| 217 | memset(&rq->cmd, 0, BLK_MAX_CDB); | 228 | memset(rq->cmd, 0, BLK_MAX_CDB); |
| 218 | rq->sense = h->sense; | ||
| 219 | memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); | ||
| 220 | rq->sense_len = 0; | ||
| 221 | 229 | ||
| 222 | rq->cmd_type = REQ_TYPE_BLOCK_PC; | 230 | rq->cmd_type = REQ_TYPE_BLOCK_PC; |
| 223 | rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE; | 231 | rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE; |
| @@ -227,12 +235,12 @@ static struct request *get_rdac_req(struct scsi_device *sdev, | |||
| 227 | return rq; | 235 | return rq; |
| 228 | } | 236 | } |
| 229 | 237 | ||
| 230 | static struct request *rdac_failover_get(struct scsi_device *sdev) | 238 | static struct request *rdac_failover_get(struct scsi_device *sdev, |
| 239 | struct rdac_dh_data *h) | ||
| 231 | { | 240 | { |
| 232 | struct request *rq; | 241 | struct request *rq; |
| 233 | struct rdac_mode_common *common; | 242 | struct rdac_mode_common *common; |
| 234 | unsigned data_size; | 243 | unsigned data_size; |
| 235 | struct rdac_dh_data *h = get_rdac_data(sdev); | ||
| 236 | 244 | ||
| 237 | if (h->ctlr->use_ms10) { | 245 | if (h->ctlr->use_ms10) { |
| 238 | struct rdac_pg_expanded *rdac_pg; | 246 | struct rdac_pg_expanded *rdac_pg; |
| @@ -277,6 +285,10 @@ static struct request *rdac_failover_get(struct scsi_device *sdev) | |||
| 277 | } | 285 | } |
| 278 | rq->cmd_len = COMMAND_SIZE(rq->cmd[0]); | 286 | rq->cmd_len = COMMAND_SIZE(rq->cmd[0]); |
| 279 | 287 | ||
| 288 | rq->sense = h->sense; | ||
| 289 | memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); | ||
| 290 | rq->sense_len = 0; | ||
| 291 | |||
| 280 | return rq; | 292 | return rq; |
| 281 | } | 293 | } |
| 282 | 294 | ||
| @@ -321,11 +333,10 @@ done: | |||
| 321 | } | 333 | } |
| 322 | 334 | ||
| 323 | static int submit_inquiry(struct scsi_device *sdev, int page_code, | 335 | static int submit_inquiry(struct scsi_device *sdev, int page_code, |
| 324 | unsigned int len) | 336 | unsigned int len, struct rdac_dh_data *h) |
| 325 | { | 337 | { |
| 326 | struct request *rq; | 338 | struct request *rq; |
| 327 | struct request_queue *q = sdev->request_queue; | 339 | struct request_queue *q = sdev->request_queue; |
| 328 | struct rdac_dh_data *h = get_rdac_data(sdev); | ||
| 329 | int err = SCSI_DH_RES_TEMP_UNAVAIL; | 340 | int err = SCSI_DH_RES_TEMP_UNAVAIL; |
| 330 | 341 | ||
| 331 | rq = get_rdac_req(sdev, &h->inq, len, READ); | 342 | rq = get_rdac_req(sdev, &h->inq, len, READ); |
| @@ -338,59 +349,68 @@ static int submit_inquiry(struct scsi_device *sdev, int page_code, | |||
| 338 | rq->cmd[2] = page_code; | 349 | rq->cmd[2] = page_code; |
| 339 | rq->cmd[4] = len; | 350 | rq->cmd[4] = len; |
| 340 | rq->cmd_len = COMMAND_SIZE(INQUIRY); | 351 | rq->cmd_len = COMMAND_SIZE(INQUIRY); |
| 352 | |||
| 353 | rq->sense = h->sense; | ||
| 354 | memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); | ||
| 355 | rq->sense_len = 0; | ||
| 356 | |||
| 341 | err = blk_execute_rq(q, NULL, rq, 1); | 357 | err = blk_execute_rq(q, NULL, rq, 1); |
| 342 | if (err == -EIO) | 358 | if (err == -EIO) |
| 343 | err = SCSI_DH_IO; | 359 | err = SCSI_DH_IO; |
| 360 | |||
| 361 | blk_put_request(rq); | ||
| 344 | done: | 362 | done: |
| 345 | return err; | 363 | return err; |
| 346 | } | 364 | } |
| 347 | 365 | ||
| 348 | static int get_lun(struct scsi_device *sdev) | 366 | static int get_lun(struct scsi_device *sdev, struct rdac_dh_data *h) |
| 349 | { | 367 | { |
| 350 | int err; | 368 | int err; |
| 351 | struct c8_inquiry *inqp; | 369 | struct c8_inquiry *inqp; |
| 352 | struct rdac_dh_data *h = get_rdac_data(sdev); | ||
| 353 | 370 | ||
| 354 | err = submit_inquiry(sdev, 0xC8, sizeof(struct c8_inquiry)); | 371 | err = submit_inquiry(sdev, 0xC8, sizeof(struct c8_inquiry), h); |
| 355 | if (err == SCSI_DH_OK) { | 372 | if (err == SCSI_DH_OK) { |
| 356 | inqp = &h->inq.c8; | 373 | inqp = &h->inq.c8; |
| 357 | h->lun = inqp->lun[7]; /* currently it uses only one byte */ | 374 | if (inqp->page_code != 0xc8) |
| 375 | return SCSI_DH_NOSYS; | ||
| 376 | if (inqp->page_id[0] != 'e' || inqp->page_id[1] != 'd' || | ||
| 377 | inqp->page_id[2] != 'i' || inqp->page_id[3] != 'd') | ||
| 378 | return SCSI_DH_NOSYS; | ||
| 379 | h->lun = scsilun_to_int((struct scsi_lun *)inqp->lun); | ||
| 358 | } | 380 | } |
| 359 | return err; | 381 | return err; |
| 360 | } | 382 | } |
| 361 | 383 | ||
| 362 | #define RDAC_OWNED 0 | 384 | static int check_ownership(struct scsi_device *sdev, struct rdac_dh_data *h) |
| 363 | #define RDAC_UNOWNED 1 | ||
| 364 | #define RDAC_FAILED 2 | ||
| 365 | static int check_ownership(struct scsi_device *sdev) | ||
| 366 | { | 385 | { |
| 367 | int err; | 386 | int err; |
| 368 | struct c9_inquiry *inqp; | 387 | struct c9_inquiry *inqp; |
| 369 | struct rdac_dh_data *h = get_rdac_data(sdev); | ||
| 370 | 388 | ||
| 371 | err = submit_inquiry(sdev, 0xC9, sizeof(struct c9_inquiry)); | 389 | err = submit_inquiry(sdev, 0xC9, sizeof(struct c9_inquiry), h); |
| 372 | if (err == SCSI_DH_OK) { | 390 | if (err == SCSI_DH_OK) { |
| 373 | err = RDAC_UNOWNED; | ||
| 374 | inqp = &h->inq.c9; | 391 | inqp = &h->inq.c9; |
| 375 | /* | 392 | if ((inqp->avte_cvp >> 7) == 0x1) { |
| 376 | * If in AVT mode or if the path already owns the LUN, | 393 | /* LUN in AVT mode */ |
| 377 | * return RDAC_OWNED; | 394 | sdev_printk(KERN_NOTICE, sdev, |
| 378 | */ | 395 | "%s: AVT mode detected\n", |
| 379 | if (((inqp->avte_cvp >> 7) == 0x1) || | 396 | RDAC_NAME); |
| 380 | ((inqp->avte_cvp & 0x1) != 0)) | 397 | h->lun_state = RDAC_LUN_AVT; |
| 381 | err = RDAC_OWNED; | 398 | } else if ((inqp->avte_cvp & 0x1) != 0) { |
| 382 | } else | 399 | /* LUN was owned by the controller */ |
| 383 | err = RDAC_FAILED; | 400 | h->lun_state = RDAC_LUN_OWNED; |
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 384 | return err; | 404 | return err; |
| 385 | } | 405 | } |
| 386 | 406 | ||
| 387 | static int initialize_controller(struct scsi_device *sdev) | 407 | static int initialize_controller(struct scsi_device *sdev, |
| 408 | struct rdac_dh_data *h) | ||
| 388 | { | 409 | { |
| 389 | int err; | 410 | int err; |
| 390 | struct c4_inquiry *inqp; | 411 | struct c4_inquiry *inqp; |
| 391 | struct rdac_dh_data *h = get_rdac_data(sdev); | ||
| 392 | 412 | ||
| 393 | err = submit_inquiry(sdev, 0xC4, sizeof(struct c4_inquiry)); | 413 | err = submit_inquiry(sdev, 0xC4, sizeof(struct c4_inquiry), h); |
| 394 | if (err == SCSI_DH_OK) { | 414 | if (err == SCSI_DH_OK) { |
| 395 | inqp = &h->inq.c4; | 415 | inqp = &h->inq.c4; |
| 396 | h->ctlr = get_controller(inqp->subsys_id, inqp->slot_id); | 416 | h->ctlr = get_controller(inqp->subsys_id, inqp->slot_id); |
| @@ -400,13 +420,12 @@ static int initialize_controller(struct scsi_device *sdev) | |||
| 400 | return err; | 420 | return err; |
| 401 | } | 421 | } |
| 402 | 422 | ||
| 403 | static int set_mode_select(struct scsi_device *sdev) | 423 | static int set_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h) |
| 404 | { | 424 | { |
| 405 | int err; | 425 | int err; |
| 406 | struct c2_inquiry *inqp; | 426 | struct c2_inquiry *inqp; |
| 407 | struct rdac_dh_data *h = get_rdac_data(sdev); | ||
| 408 | 427 | ||
| 409 | err = submit_inquiry(sdev, 0xC2, sizeof(struct c2_inquiry)); | 428 | err = submit_inquiry(sdev, 0xC2, sizeof(struct c2_inquiry), h); |
| 410 | if (err == SCSI_DH_OK) { | 429 | if (err == SCSI_DH_OK) { |
| 411 | inqp = &h->inq.c2; | 430 | inqp = &h->inq.c2; |
| 412 | /* | 431 | /* |
| @@ -421,13 +440,13 @@ static int set_mode_select(struct scsi_device *sdev) | |||
| 421 | return err; | 440 | return err; |
| 422 | } | 441 | } |
| 423 | 442 | ||
| 424 | static int mode_select_handle_sense(struct scsi_device *sdev) | 443 | static int mode_select_handle_sense(struct scsi_device *sdev, |
| 444 | unsigned char *sensebuf) | ||
| 425 | { | 445 | { |
| 426 | struct scsi_sense_hdr sense_hdr; | 446 | struct scsi_sense_hdr sense_hdr; |
| 427 | struct rdac_dh_data *h = get_rdac_data(sdev); | ||
| 428 | int sense, err = SCSI_DH_IO, ret; | 447 | int sense, err = SCSI_DH_IO, ret; |
| 429 | 448 | ||
| 430 | ret = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sense_hdr); | 449 | ret = scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE, &sense_hdr); |
| 431 | if (!ret) | 450 | if (!ret) |
| 432 | goto done; | 451 | goto done; |
| 433 | 452 | ||
| @@ -451,14 +470,13 @@ done: | |||
| 451 | return err; | 470 | return err; |
| 452 | } | 471 | } |
| 453 | 472 | ||
| 454 | static int send_mode_select(struct scsi_device *sdev) | 473 | static int send_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h) |
| 455 | { | 474 | { |
| 456 | struct request *rq; | 475 | struct request *rq; |
| 457 | struct request_queue *q = sdev->request_queue; | 476 | struct request_queue *q = sdev->request_queue; |
| 458 | struct rdac_dh_data *h = get_rdac_data(sdev); | ||
| 459 | int err = SCSI_DH_RES_TEMP_UNAVAIL; | 477 | int err = SCSI_DH_RES_TEMP_UNAVAIL; |
| 460 | 478 | ||
| 461 | rq = rdac_failover_get(sdev); | 479 | rq = rdac_failover_get(sdev, h); |
| 462 | if (!rq) | 480 | if (!rq) |
| 463 | goto done; | 481 | goto done; |
| 464 | 482 | ||
| @@ -466,9 +484,11 @@ static int send_mode_select(struct scsi_device *sdev) | |||
| 466 | 484 | ||
| 467 | err = blk_execute_rq(q, NULL, rq, 1); | 485 | err = blk_execute_rq(q, NULL, rq, 1); |
| 468 | if (err != SCSI_DH_OK) | 486 | if (err != SCSI_DH_OK) |
| 469 | err = mode_select_handle_sense(sdev); | 487 | err = mode_select_handle_sense(sdev, h->sense); |
| 470 | if (err == SCSI_DH_OK) | 488 | if (err == SCSI_DH_OK) |
| 471 | h->state = RDAC_STATE_ACTIVE; | 489 | h->state = RDAC_STATE_ACTIVE; |
| 490 | |||
| 491 | blk_put_request(rq); | ||
| 472 | done: | 492 | done: |
| 473 | return err; | 493 | return err; |
| 474 | } | 494 | } |
| @@ -478,38 +498,23 @@ static int rdac_activate(struct scsi_device *sdev) | |||
| 478 | struct rdac_dh_data *h = get_rdac_data(sdev); | 498 | struct rdac_dh_data *h = get_rdac_data(sdev); |
| 479 | int err = SCSI_DH_OK; | 499 | int err = SCSI_DH_OK; |
| 480 | 500 | ||
| 481 | if (h->lun == UNINITIALIZED_LUN) { | 501 | err = check_ownership(sdev, h); |
| 482 | err = get_lun(sdev); | 502 | if (err != SCSI_DH_OK) |
| 483 | if (err != SCSI_DH_OK) | ||
| 484 | goto done; | ||
| 485 | } | ||
| 486 | |||
| 487 | err = check_ownership(sdev); | ||
| 488 | switch (err) { | ||
| 489 | case RDAC_UNOWNED: | ||
| 490 | break; | ||
| 491 | case RDAC_OWNED: | ||
| 492 | err = SCSI_DH_OK; | ||
| 493 | goto done; | ||
| 494 | case RDAC_FAILED: | ||
| 495 | default: | ||
| 496 | err = SCSI_DH_IO; | ||
| 497 | goto done; | 503 | goto done; |
| 498 | } | ||
| 499 | 504 | ||
| 500 | if (!h->ctlr) { | 505 | if (!h->ctlr) { |
| 501 | err = initialize_controller(sdev); | 506 | err = initialize_controller(sdev, h); |
| 502 | if (err != SCSI_DH_OK) | 507 | if (err != SCSI_DH_OK) |
| 503 | goto done; | 508 | goto done; |
| 504 | } | 509 | } |
| 505 | 510 | ||
| 506 | if (h->ctlr->use_ms10 == -1) { | 511 | if (h->ctlr->use_ms10 == -1) { |
| 507 | err = set_mode_select(sdev); | 512 | err = set_mode_select(sdev, h); |
| 508 | if (err != SCSI_DH_OK) | 513 | if (err != SCSI_DH_OK) |
| 509 | goto done; | 514 | goto done; |
| 510 | } | 515 | } |
| 511 | 516 | if (h->lun_state == RDAC_LUN_UNOWNED) | |
| 512 | err = send_mode_select(sdev); | 517 | err = send_mode_select(sdev, h); |
| 513 | done: | 518 | done: |
| 514 | return err; | 519 | return err; |
| 515 | } | 520 | } |
| @@ -569,10 +574,7 @@ static int rdac_check_sense(struct scsi_device *sdev, | |||
| 569 | return SCSI_RETURN_NOT_HANDLED; | 574 | return SCSI_RETURN_NOT_HANDLED; |
| 570 | } | 575 | } |
| 571 | 576 | ||
| 572 | static const struct { | 577 | const struct scsi_dh_devlist rdac_dev_list[] = { |
| 573 | char *vendor; | ||
| 574 | char *model; | ||
| 575 | } rdac_dev_list[] = { | ||
| 576 | {"IBM", "1722"}, | 578 | {"IBM", "1722"}, |
| 577 | {"IBM", "1724"}, | 579 | {"IBM", "1724"}, |
| 578 | {"IBM", "1726"}, | 580 | {"IBM", "1726"}, |
| @@ -590,89 +592,89 @@ static const struct { | |||
| 590 | {NULL, NULL}, | 592 | {NULL, NULL}, |
| 591 | }; | 593 | }; |
| 592 | 594 | ||
| 593 | static int rdac_bus_notify(struct notifier_block *, unsigned long, void *); | 595 | static int rdac_bus_attach(struct scsi_device *sdev); |
| 596 | static void rdac_bus_detach(struct scsi_device *sdev); | ||
| 594 | 597 | ||
| 595 | static struct scsi_device_handler rdac_dh = { | 598 | static struct scsi_device_handler rdac_dh = { |
| 596 | .name = RDAC_NAME, | 599 | .name = RDAC_NAME, |
| 597 | .module = THIS_MODULE, | 600 | .module = THIS_MODULE, |
| 598 | .nb.notifier_call = rdac_bus_notify, | 601 | .devlist = rdac_dev_list, |
| 599 | .prep_fn = rdac_prep_fn, | 602 | .prep_fn = rdac_prep_fn, |
| 600 | .check_sense = rdac_check_sense, | 603 | .check_sense = rdac_check_sense, |
| 604 | .attach = rdac_bus_attach, | ||
| 605 | .detach = rdac_bus_detach, | ||
| 601 | .activate = rdac_activate, | 606 | .activate = rdac_activate, |
| 602 | }; | 607 | }; |
| 603 | 608 | ||
| 604 | /* | 609 | static int rdac_bus_attach(struct scsi_device *sdev) |
| 605 | * TODO: need some interface so we can set trespass values | ||
| 606 | */ | ||
| 607 | static int rdac_bus_notify(struct notifier_block *nb, | ||
| 608 | unsigned long action, void *data) | ||
| 609 | { | 610 | { |
| 610 | struct device *dev = data; | ||
| 611 | struct scsi_device *sdev; | ||
| 612 | struct scsi_dh_data *scsi_dh_data; | 611 | struct scsi_dh_data *scsi_dh_data; |
| 613 | struct rdac_dh_data *h; | 612 | struct rdac_dh_data *h; |
| 614 | int i, found = 0; | ||
| 615 | unsigned long flags; | 613 | unsigned long flags; |
| 614 | int err; | ||
| 616 | 615 | ||
| 617 | if (!scsi_is_sdev_device(dev)) | 616 | scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) |
| 617 | + sizeof(*h) , GFP_KERNEL); | ||
| 618 | if (!scsi_dh_data) { | ||
| 619 | sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n", | ||
| 620 | RDAC_NAME); | ||
| 618 | return 0; | 621 | return 0; |
| 622 | } | ||
| 619 | 623 | ||
| 620 | sdev = to_scsi_device(dev); | 624 | scsi_dh_data->scsi_dh = &rdac_dh; |
| 621 | 625 | h = (struct rdac_dh_data *) scsi_dh_data->buf; | |
| 622 | if (action == BUS_NOTIFY_ADD_DEVICE) { | 626 | h->lun = UNINITIALIZED_LUN; |
| 623 | for (i = 0; rdac_dev_list[i].vendor; i++) { | 627 | h->state = RDAC_STATE_ACTIVE; |
| 624 | if (!strncmp(sdev->vendor, rdac_dev_list[i].vendor, | ||
| 625 | strlen(rdac_dev_list[i].vendor)) && | ||
| 626 | !strncmp(sdev->model, rdac_dev_list[i].model, | ||
| 627 | strlen(rdac_dev_list[i].model))) { | ||
| 628 | found = 1; | ||
| 629 | break; | ||
| 630 | } | ||
| 631 | } | ||
| 632 | if (!found) | ||
| 633 | goto out; | ||
| 634 | 628 | ||
| 635 | scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) | 629 | err = get_lun(sdev, h); |
| 636 | + sizeof(*h) , GFP_KERNEL); | 630 | if (err != SCSI_DH_OK) |
| 637 | if (!scsi_dh_data) { | 631 | goto failed; |
| 638 | sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n", | ||
| 639 | RDAC_NAME); | ||
| 640 | goto out; | ||
| 641 | } | ||
| 642 | 632 | ||
| 643 | scsi_dh_data->scsi_dh = &rdac_dh; | 633 | err = check_ownership(sdev, h); |
| 644 | h = (struct rdac_dh_data *) scsi_dh_data->buf; | 634 | if (err != SCSI_DH_OK) |
| 645 | h->lun = UNINITIALIZED_LUN; | 635 | goto failed; |
| 646 | h->state = RDAC_STATE_ACTIVE; | 636 | |
| 647 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); | 637 | if (!try_module_get(THIS_MODULE)) |
| 648 | sdev->scsi_dh_data = scsi_dh_data; | 638 | goto failed; |
| 649 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | 639 | |
| 650 | try_module_get(THIS_MODULE); | 640 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); |
| 651 | 641 | sdev->scsi_dh_data = scsi_dh_data; | |
| 652 | sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", RDAC_NAME); | 642 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); |
| 653 | 643 | ||
| 654 | } else if (action == BUS_NOTIFY_DEL_DEVICE) { | 644 | sdev_printk(KERN_NOTICE, sdev, |
| 655 | if (sdev->scsi_dh_data == NULL || | 645 | "%s: LUN %d (%s)\n", |
| 656 | sdev->scsi_dh_data->scsi_dh != &rdac_dh) | 646 | RDAC_NAME, h->lun, lun_state[(int)h->lun_state]); |
| 657 | goto out; | ||
| 658 | |||
| 659 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); | ||
| 660 | scsi_dh_data = sdev->scsi_dh_data; | ||
| 661 | sdev->scsi_dh_data = NULL; | ||
| 662 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | ||
| 663 | |||
| 664 | h = (struct rdac_dh_data *) scsi_dh_data->buf; | ||
| 665 | if (h->ctlr) | ||
| 666 | kref_put(&h->ctlr->kref, release_controller); | ||
| 667 | kfree(scsi_dh_data); | ||
| 668 | module_put(THIS_MODULE); | ||
| 669 | sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n", RDAC_NAME); | ||
| 670 | } | ||
| 671 | 647 | ||
| 672 | out: | ||
| 673 | return 0; | 648 | return 0; |
| 649 | |||
| 650 | failed: | ||
| 651 | kfree(scsi_dh_data); | ||
| 652 | sdev_printk(KERN_ERR, sdev, "%s: not attached\n", | ||
| 653 | RDAC_NAME); | ||
| 654 | return -EINVAL; | ||
| 655 | } | ||
| 656 | |||
| 657 | static void rdac_bus_detach( struct scsi_device *sdev ) | ||
| 658 | { | ||
| 659 | struct scsi_dh_data *scsi_dh_data; | ||
| 660 | struct rdac_dh_data *h; | ||
| 661 | unsigned long flags; | ||
| 662 | |||
| 663 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); | ||
| 664 | scsi_dh_data = sdev->scsi_dh_data; | ||
| 665 | sdev->scsi_dh_data = NULL; | ||
| 666 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | ||
| 667 | |||
| 668 | h = (struct rdac_dh_data *) scsi_dh_data->buf; | ||
| 669 | if (h->ctlr) | ||
| 670 | kref_put(&h->ctlr->kref, release_controller); | ||
| 671 | kfree(scsi_dh_data); | ||
| 672 | module_put(THIS_MODULE); | ||
| 673 | sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", RDAC_NAME); | ||
| 674 | } | 674 | } |
| 675 | 675 | ||
| 676 | |||
| 677 | |||
| 676 | static int __init rdac_init(void) | 678 | static int __init rdac_init(void) |
| 677 | { | 679 | { |
| 678 | int r; | 680 | int r; |
