Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.0-rc1 565 lines 14 kB view raw
1/* 2 * SCSI device handler infrastruture. 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License as published by the 6 * Free Software Foundation; either version 2 of the License, or (at your 7 * option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License along 15 * with this program; if not, write to the Free Software Foundation, Inc., 16 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 * 18 * Copyright IBM Corporation, 2007 19 * Authors: 20 * Chandra Seetharaman <sekharan@us.ibm.com> 21 * Mike Anderson <andmike@linux.vnet.ibm.com> 22 */ 23 24#include <linux/slab.h> 25#include <scsi/scsi_dh.h> 26#include "../scsi_priv.h" 27 28static DEFINE_SPINLOCK(list_lock); 29static LIST_HEAD(scsi_dh_list); 30static int scsi_dh_list_idx = 1; 31 32static struct scsi_device_handler *get_device_handler(const char *name) 33{ 34 struct scsi_device_handler *tmp, *found = NULL; 35 36 spin_lock(&list_lock); 37 list_for_each_entry(tmp, &scsi_dh_list, list) { 38 if (!strncmp(tmp->name, name, strlen(tmp->name))) { 39 found = tmp; 40 break; 41 } 42 } 43 spin_unlock(&list_lock); 44 return found; 45} 46 47static struct scsi_device_handler *get_device_handler_by_idx(int idx) 48{ 49 struct scsi_device_handler *tmp, *found = NULL; 50 51 spin_lock(&list_lock); 52 list_for_each_entry(tmp, &scsi_dh_list, list) { 53 if (tmp->idx == idx) { 54 found = tmp; 55 break; 56 } 57 } 58 spin_unlock(&list_lock); 59 return found; 60} 61 62/* 63 * device_handler_match - Attach a device handler to a device 64 * @scsi_dh - The device handler to match against or NULL 65 * @sdev - SCSI device to be tested against @scsi_dh 66 * 67 * Tests @sdev against the device handler @scsi_dh or against 68 * all registered device_handler if @scsi_dh == NULL. 69 * Returns the found device handler or NULL if not found. 70 */ 71static struct scsi_device_handler * 72device_handler_match(struct scsi_device_handler *scsi_dh, 73 struct scsi_device *sdev) 74{ 75 struct scsi_device_handler *found_dh = NULL; 76 int idx; 77 78 idx = scsi_get_device_flags_keyed(sdev, sdev->vendor, sdev->model, 79 SCSI_DEVINFO_DH); 80 found_dh = get_device_handler_by_idx(idx); 81 82 if (scsi_dh && found_dh != scsi_dh) 83 found_dh = NULL; 84 85 return found_dh; 86} 87 88/* 89 * scsi_dh_handler_attach - Attach a device handler to a device 90 * @sdev - SCSI device the device handler should attach to 91 * @scsi_dh - The device handler to attach 92 */ 93static int scsi_dh_handler_attach(struct scsi_device *sdev, 94 struct scsi_device_handler *scsi_dh) 95{ 96 int err = 0; 97 98 if (sdev->scsi_dh_data) { 99 if (sdev->scsi_dh_data->scsi_dh != scsi_dh) 100 err = -EBUSY; 101 else 102 kref_get(&sdev->scsi_dh_data->kref); 103 } else if (scsi_dh->attach) { 104 err = scsi_dh->attach(sdev); 105 if (!err) { 106 kref_init(&sdev->scsi_dh_data->kref); 107 sdev->scsi_dh_data->sdev = sdev; 108 } 109 } 110 return err; 111} 112 113static void __detach_handler (struct kref *kref) 114{ 115 struct scsi_dh_data *scsi_dh_data = container_of(kref, struct scsi_dh_data, kref); 116 scsi_dh_data->scsi_dh->detach(scsi_dh_data->sdev); 117} 118 119/* 120 * scsi_dh_handler_detach - Detach a device handler from a device 121 * @sdev - SCSI device the device handler should be detached from 122 * @scsi_dh - Device handler to be detached 123 * 124 * Detach from a device handler. If a device handler is specified, 125 * only detach if the currently attached handler matches @scsi_dh. 126 */ 127static void scsi_dh_handler_detach(struct scsi_device *sdev, 128 struct scsi_device_handler *scsi_dh) 129{ 130 if (!sdev->scsi_dh_data) 131 return; 132 133 if (scsi_dh && scsi_dh != sdev->scsi_dh_data->scsi_dh) 134 return; 135 136 if (!scsi_dh) 137 scsi_dh = sdev->scsi_dh_data->scsi_dh; 138 139 if (scsi_dh && scsi_dh->detach) 140 kref_put(&sdev->scsi_dh_data->kref, __detach_handler); 141} 142 143/* 144 * Functions for sysfs attribute 'dh_state' 145 */ 146static ssize_t 147store_dh_state(struct device *dev, struct device_attribute *attr, 148 const char *buf, size_t count) 149{ 150 struct scsi_device *sdev = to_scsi_device(dev); 151 struct scsi_device_handler *scsi_dh; 152 int err = -EINVAL; 153 154 if (!sdev->scsi_dh_data) { 155 /* 156 * Attach to a device handler 157 */ 158 if (!(scsi_dh = get_device_handler(buf))) 159 return err; 160 err = scsi_dh_handler_attach(sdev, scsi_dh); 161 } else { 162 scsi_dh = sdev->scsi_dh_data->scsi_dh; 163 if (!strncmp(buf, "detach", 6)) { 164 /* 165 * Detach from a device handler 166 */ 167 scsi_dh_handler_detach(sdev, scsi_dh); 168 err = 0; 169 } else if (!strncmp(buf, "activate", 8)) { 170 /* 171 * Activate a device handler 172 */ 173 if (scsi_dh->activate) 174 err = scsi_dh->activate(sdev, NULL, NULL); 175 else 176 err = 0; 177 } 178 } 179 180 return err<0?err:count; 181} 182 183static ssize_t 184show_dh_state(struct device *dev, struct device_attribute *attr, char *buf) 185{ 186 struct scsi_device *sdev = to_scsi_device(dev); 187 188 if (!sdev->scsi_dh_data) 189 return snprintf(buf, 20, "detached\n"); 190 191 return snprintf(buf, 20, "%s\n", sdev->scsi_dh_data->scsi_dh->name); 192} 193 194static struct device_attribute scsi_dh_state_attr = 195 __ATTR(dh_state, S_IRUGO | S_IWUSR, show_dh_state, 196 store_dh_state); 197 198/* 199 * scsi_dh_sysfs_attr_add - Callback for scsi_init_dh 200 */ 201static int scsi_dh_sysfs_attr_add(struct device *dev, void *data) 202{ 203 struct scsi_device *sdev; 204 int err; 205 206 if (!scsi_is_sdev_device(dev)) 207 return 0; 208 209 sdev = to_scsi_device(dev); 210 211 err = device_create_file(&sdev->sdev_gendev, 212 &scsi_dh_state_attr); 213 214 return 0; 215} 216 217/* 218 * scsi_dh_sysfs_attr_remove - Callback for scsi_exit_dh 219 */ 220static int scsi_dh_sysfs_attr_remove(struct device *dev, void *data) 221{ 222 struct scsi_device *sdev; 223 224 if (!scsi_is_sdev_device(dev)) 225 return 0; 226 227 sdev = to_scsi_device(dev); 228 229 device_remove_file(&sdev->sdev_gendev, 230 &scsi_dh_state_attr); 231 232 return 0; 233} 234 235/* 236 * scsi_dh_notifier - notifier chain callback 237 */ 238static int scsi_dh_notifier(struct notifier_block *nb, 239 unsigned long action, void *data) 240{ 241 struct device *dev = data; 242 struct scsi_device *sdev; 243 int err = 0; 244 struct scsi_device_handler *devinfo = NULL; 245 246 if (!scsi_is_sdev_device(dev)) 247 return 0; 248 249 sdev = to_scsi_device(dev); 250 251 if (action == BUS_NOTIFY_ADD_DEVICE) { 252 err = device_create_file(dev, &scsi_dh_state_attr); 253 /* don't care about err */ 254 devinfo = device_handler_match(NULL, sdev); 255 if (devinfo) 256 err = scsi_dh_handler_attach(sdev, devinfo); 257 } else if (action == BUS_NOTIFY_DEL_DEVICE) { 258 device_remove_file(dev, &scsi_dh_state_attr); 259 scsi_dh_handler_detach(sdev, NULL); 260 } 261 return err; 262} 263 264/* 265 * scsi_dh_notifier_add - Callback for scsi_register_device_handler 266 */ 267static int scsi_dh_notifier_add(struct device *dev, void *data) 268{ 269 struct scsi_device_handler *scsi_dh = data; 270 struct scsi_device *sdev; 271 272 if (!scsi_is_sdev_device(dev)) 273 return 0; 274 275 if (!get_device(dev)) 276 return 0; 277 278 sdev = to_scsi_device(dev); 279 280 if (device_handler_match(scsi_dh, sdev)) 281 scsi_dh_handler_attach(sdev, scsi_dh); 282 283 put_device(dev); 284 285 return 0; 286} 287 288/* 289 * scsi_dh_notifier_remove - Callback for scsi_unregister_device_handler 290 */ 291static int scsi_dh_notifier_remove(struct device *dev, void *data) 292{ 293 struct scsi_device_handler *scsi_dh = data; 294 struct scsi_device *sdev; 295 296 if (!scsi_is_sdev_device(dev)) 297 return 0; 298 299 if (!get_device(dev)) 300 return 0; 301 302 sdev = to_scsi_device(dev); 303 304 scsi_dh_handler_detach(sdev, scsi_dh); 305 306 put_device(dev); 307 308 return 0; 309} 310 311/* 312 * scsi_register_device_handler - register a device handler personality 313 * module. 314 * @scsi_dh - device handler to be registered. 315 * 316 * Returns 0 on success, -EBUSY if handler already registered. 317 */ 318int scsi_register_device_handler(struct scsi_device_handler *scsi_dh) 319{ 320 int i; 321 322 if (get_device_handler(scsi_dh->name)) 323 return -EBUSY; 324 325 spin_lock(&list_lock); 326 scsi_dh->idx = scsi_dh_list_idx++; 327 list_add(&scsi_dh->list, &scsi_dh_list); 328 spin_unlock(&list_lock); 329 330 for (i = 0; scsi_dh->devlist[i].vendor; i++) { 331 scsi_dev_info_list_add_keyed(0, 332 scsi_dh->devlist[i].vendor, 333 scsi_dh->devlist[i].model, 334 NULL, 335 scsi_dh->idx, 336 SCSI_DEVINFO_DH); 337 } 338 339 bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add); 340 printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name); 341 342 return SCSI_DH_OK; 343} 344EXPORT_SYMBOL_GPL(scsi_register_device_handler); 345 346/* 347 * scsi_unregister_device_handler - register a device handler personality 348 * module. 349 * @scsi_dh - device handler to be unregistered. 350 * 351 * Returns 0 on success, -ENODEV if handler not registered. 352 */ 353int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) 354{ 355 int i; 356 357 if (!get_device_handler(scsi_dh->name)) 358 return -ENODEV; 359 360 bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, 361 scsi_dh_notifier_remove); 362 363 for (i = 0; scsi_dh->devlist[i].vendor; i++) { 364 scsi_dev_info_list_del_keyed(scsi_dh->devlist[i].vendor, 365 scsi_dh->devlist[i].model, 366 SCSI_DEVINFO_DH); 367 } 368 369 spin_lock(&list_lock); 370 list_del(&scsi_dh->list); 371 spin_unlock(&list_lock); 372 printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name); 373 374 return SCSI_DH_OK; 375} 376EXPORT_SYMBOL_GPL(scsi_unregister_device_handler); 377 378/* 379 * scsi_dh_activate - activate the path associated with the scsi_device 380 * corresponding to the given request queue. 381 * Returns immediately without waiting for activation to be completed. 382 * @q - Request queue that is associated with the scsi_device to be 383 * activated. 384 * @fn - Function to be called upon completion of the activation. 385 * Function fn is called with data (below) and the error code. 386 * Function fn may be called from the same calling context. So, 387 * do not hold the lock in the caller which may be needed in fn. 388 * @data - data passed to the function fn upon completion. 389 * 390 */ 391int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) 392{ 393 int err = 0; 394 unsigned long flags; 395 struct scsi_device *sdev; 396 struct scsi_device_handler *scsi_dh = NULL; 397 struct device *dev = NULL; 398 399 spin_lock_irqsave(q->queue_lock, flags); 400 sdev = q->queuedata; 401 if (sdev && sdev->scsi_dh_data) 402 scsi_dh = sdev->scsi_dh_data->scsi_dh; 403 dev = get_device(&sdev->sdev_gendev); 404 if (!scsi_dh || !dev || 405 sdev->sdev_state == SDEV_CANCEL || 406 sdev->sdev_state == SDEV_DEL) 407 err = SCSI_DH_NOSYS; 408 if (sdev->sdev_state == SDEV_OFFLINE) 409 err = SCSI_DH_DEV_OFFLINED; 410 spin_unlock_irqrestore(q->queue_lock, flags); 411 412 if (err) { 413 if (fn) 414 fn(data, err); 415 goto out; 416 } 417 418 if (scsi_dh->activate) 419 err = scsi_dh->activate(sdev, fn, data); 420out: 421 put_device(dev); 422 return err; 423} 424EXPORT_SYMBOL_GPL(scsi_dh_activate); 425 426/* 427 * scsi_dh_set_params - set the parameters for the device as per the 428 * string specified in params. 429 * @q - Request queue that is associated with the scsi_device for 430 * which the parameters to be set. 431 * @params - parameters in the following format 432 * "no_of_params\0param1\0param2\0param3\0...\0" 433 * for example, string for 2 parameters with value 10 and 21 434 * is specified as "2\010\021\0". 435 */ 436int scsi_dh_set_params(struct request_queue *q, const char *params) 437{ 438 int err = -SCSI_DH_NOSYS; 439 unsigned long flags; 440 struct scsi_device *sdev; 441 struct scsi_device_handler *scsi_dh = NULL; 442 443 spin_lock_irqsave(q->queue_lock, flags); 444 sdev = q->queuedata; 445 if (sdev && sdev->scsi_dh_data) 446 scsi_dh = sdev->scsi_dh_data->scsi_dh; 447 if (scsi_dh && scsi_dh->set_params && get_device(&sdev->sdev_gendev)) 448 err = 0; 449 spin_unlock_irqrestore(q->queue_lock, flags); 450 451 if (err) 452 return err; 453 err = scsi_dh->set_params(sdev, params); 454 put_device(&sdev->sdev_gendev); 455 return err; 456} 457EXPORT_SYMBOL_GPL(scsi_dh_set_params); 458 459/* 460 * scsi_dh_handler_exist - Return TRUE(1) if a device handler exists for 461 * the given name. FALSE(0) otherwise. 462 * @name - name of the device handler. 463 */ 464int scsi_dh_handler_exist(const char *name) 465{ 466 return (get_device_handler(name) != NULL); 467} 468EXPORT_SYMBOL_GPL(scsi_dh_handler_exist); 469 470/* 471 * scsi_dh_handler_attach - Attach device handler 472 * @sdev - sdev the handler should be attached to 473 * @name - name of the handler to attach 474 */ 475int scsi_dh_attach(struct request_queue *q, const char *name) 476{ 477 unsigned long flags; 478 struct scsi_device *sdev; 479 struct scsi_device_handler *scsi_dh; 480 int err = 0; 481 482 scsi_dh = get_device_handler(name); 483 if (!scsi_dh) 484 return -EINVAL; 485 486 spin_lock_irqsave(q->queue_lock, flags); 487 sdev = q->queuedata; 488 if (!sdev || !get_device(&sdev->sdev_gendev)) 489 err = -ENODEV; 490 spin_unlock_irqrestore(q->queue_lock, flags); 491 492 if (!err) { 493 err = scsi_dh_handler_attach(sdev, scsi_dh); 494 put_device(&sdev->sdev_gendev); 495 } 496 return err; 497} 498EXPORT_SYMBOL_GPL(scsi_dh_attach); 499 500/* 501 * scsi_dh_handler_detach - Detach device handler 502 * @sdev - sdev the handler should be detached from 503 * 504 * This function will detach the device handler only 505 * if the sdev is not part of the internal list, ie 506 * if it has been attached manually. 507 */ 508void scsi_dh_detach(struct request_queue *q) 509{ 510 unsigned long flags; 511 struct scsi_device *sdev; 512 struct scsi_device_handler *scsi_dh = NULL; 513 514 spin_lock_irqsave(q->queue_lock, flags); 515 sdev = q->queuedata; 516 if (!sdev || !get_device(&sdev->sdev_gendev)) 517 sdev = NULL; 518 spin_unlock_irqrestore(q->queue_lock, flags); 519 520 if (!sdev) 521 return; 522 523 if (sdev->scsi_dh_data) { 524 scsi_dh = sdev->scsi_dh_data->scsi_dh; 525 scsi_dh_handler_detach(sdev, scsi_dh); 526 } 527 put_device(&sdev->sdev_gendev); 528} 529EXPORT_SYMBOL_GPL(scsi_dh_detach); 530 531static struct notifier_block scsi_dh_nb = { 532 .notifier_call = scsi_dh_notifier 533}; 534 535static int __init scsi_dh_init(void) 536{ 537 int r; 538 539 r = scsi_dev_info_add_list(SCSI_DEVINFO_DH, "SCSI Device Handler"); 540 if (r) 541 return r; 542 543 r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb); 544 545 if (!r) 546 bus_for_each_dev(&scsi_bus_type, NULL, NULL, 547 scsi_dh_sysfs_attr_add); 548 549 return r; 550} 551 552static void __exit scsi_dh_exit(void) 553{ 554 bus_for_each_dev(&scsi_bus_type, NULL, NULL, 555 scsi_dh_sysfs_attr_remove); 556 bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb); 557 scsi_dev_info_remove_list(SCSI_DEVINFO_DH); 558} 559 560module_init(scsi_dh_init); 561module_exit(scsi_dh_exit); 562 563MODULE_DESCRIPTION("SCSI device handler"); 564MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>"); 565MODULE_LICENSE("GPL");