diff options
| author | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-03-22 23:42:27 -0400 |
|---|---|---|
| committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-04-22 16:16:31 -0400 |
| commit | 643eb2d932c97a0583381629d632d486934cf7ee (patch) | |
| tree | 514d860d8b6c22f50b8fdd7afd25047707321650 | |
| parent | f7120a4f75168df3c02efacd10403a4ba0bcb29d (diff) | |
[SCSI] rework scsi_target allocation
The current target allocation code registeres each possible target
with sysfs; it will be deleted again if no useable LUN on this target
was found. This results in a string of 'target add/target remove' uevents.
Based on a patch by Hannes Reinecke <hare@suse.de> this patch reworks
the target allocation code so that only uevents for existing targets
are sent. The sysfs registration is split off from the existing
scsi_target_alloc() into a in a new scsi_add_target() function, which
should be called whenever an existing target is found. Only then a
uevent is sent, so we'll be generating events for existing targets
only.
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
| -rw-r--r-- | drivers/scsi/scsi_scan.c | 72 | ||||
| -rw-r--r-- | drivers/scsi/scsi_sysfs.c | 27 | ||||
| -rw-r--r-- | include/scsi/scsi_device.h | 3 |
3 files changed, 63 insertions, 39 deletions
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index e1644b270cdc..fcd7455ffc39 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c | |||
| @@ -322,6 +322,21 @@ out: | |||
| 322 | return NULL; | 322 | return NULL; |
| 323 | } | 323 | } |
| 324 | 324 | ||
| 325 | static void scsi_target_destroy(struct scsi_target *starget) | ||
| 326 | { | ||
| 327 | struct device *dev = &starget->dev; | ||
| 328 | struct Scsi_Host *shost = dev_to_shost(dev->parent); | ||
| 329 | unsigned long flags; | ||
| 330 | |||
| 331 | transport_destroy_device(dev); | ||
| 332 | spin_lock_irqsave(shost->host_lock, flags); | ||
| 333 | if (shost->hostt->target_destroy) | ||
| 334 | shost->hostt->target_destroy(starget); | ||
| 335 | list_del_init(&starget->siblings); | ||
| 336 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
| 337 | put_device(dev); | ||
| 338 | } | ||
| 339 | |||
| 325 | static void scsi_target_dev_release(struct device *dev) | 340 | static void scsi_target_dev_release(struct device *dev) |
| 326 | { | 341 | { |
| 327 | struct device *parent = dev->parent; | 342 | struct device *parent = dev->parent; |
| @@ -406,7 +421,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, | |||
| 406 | starget->channel = channel; | 421 | starget->channel = channel; |
| 407 | INIT_LIST_HEAD(&starget->siblings); | 422 | INIT_LIST_HEAD(&starget->siblings); |
| 408 | INIT_LIST_HEAD(&starget->devices); | 423 | INIT_LIST_HEAD(&starget->devices); |
| 409 | starget->state = STARGET_RUNNING; | 424 | starget->state = STARGET_CREATED; |
| 410 | starget->scsi_level = SCSI_2; | 425 | starget->scsi_level = SCSI_2; |
| 411 | retry: | 426 | retry: |
| 412 | spin_lock_irqsave(shost->host_lock, flags); | 427 | spin_lock_irqsave(shost->host_lock, flags); |
| @@ -419,18 +434,6 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, | |||
| 419 | spin_unlock_irqrestore(shost->host_lock, flags); | 434 | spin_unlock_irqrestore(shost->host_lock, flags); |
| 420 | /* allocate and add */ | 435 | /* allocate and add */ |
| 421 | transport_setup_device(dev); | 436 | transport_setup_device(dev); |
| 422 | error = device_add(dev); | ||
| 423 | if (error) { | ||
| 424 | dev_err(dev, "target device_add failed, error %d\n", error); | ||
| 425 | spin_lock_irqsave(shost->host_lock, flags); | ||
| 426 | list_del_init(&starget->siblings); | ||
| 427 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
| 428 | transport_destroy_device(dev); | ||
| 429 | put_device(parent); | ||
| 430 | kfree(starget); | ||
| 431 | return NULL; | ||
| 432 | } | ||
| 433 | transport_add_device(dev); | ||
| 434 | if (shost->hostt->target_alloc) { | 437 | if (shost->hostt->target_alloc) { |
| 435 | error = shost->hostt->target_alloc(starget); | 438 | error = shost->hostt->target_alloc(starget); |
| 436 | 439 | ||
| @@ -438,9 +441,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, | |||
| 438 | dev_printk(KERN_ERR, dev, "target allocation failed, error %d\n", error); | 441 | dev_printk(KERN_ERR, dev, "target allocation failed, error %d\n", error); |
| 439 | /* don't want scsi_target_reap to do the final | 442 | /* don't want scsi_target_reap to do the final |
| 440 | * put because it will be under the host lock */ | 443 | * put because it will be under the host lock */ |
| 441 | get_device(dev); | 444 | scsi_target_destroy(starget); |
| 442 | scsi_target_reap(starget); | ||
| 443 | put_device(dev); | ||
| 444 | return NULL; | 445 | return NULL; |
| 445 | } | 446 | } |
| 446 | } | 447 | } |
| @@ -467,18 +468,10 @@ static void scsi_target_reap_usercontext(struct work_struct *work) | |||
| 467 | { | 468 | { |
| 468 | struct scsi_target *starget = | 469 | struct scsi_target *starget = |
| 469 | container_of(work, struct scsi_target, ew.work); | 470 | container_of(work, struct scsi_target, ew.work); |
| 470 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); | ||
| 471 | unsigned long flags; | ||
| 472 | 471 | ||
| 473 | transport_remove_device(&starget->dev); | 472 | transport_remove_device(&starget->dev); |
| 474 | device_del(&starget->dev); | 473 | device_del(&starget->dev); |
| 475 | transport_destroy_device(&starget->dev); | 474 | scsi_target_destroy(starget); |
| 476 | spin_lock_irqsave(shost->host_lock, flags); | ||
| 477 | if (shost->hostt->target_destroy) | ||
| 478 | shost->hostt->target_destroy(starget); | ||
| 479 | list_del_init(&starget->siblings); | ||
| 480 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
| 481 | put_device(&starget->dev); | ||
| 482 | } | 475 | } |
| 483 | 476 | ||
| 484 | /** | 477 | /** |
| @@ -493,21 +486,25 @@ void scsi_target_reap(struct scsi_target *starget) | |||
| 493 | { | 486 | { |
| 494 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); | 487 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); |
| 495 | unsigned long flags; | 488 | unsigned long flags; |
| 489 | enum scsi_target_state state; | ||
| 490 | int empty; | ||
| 496 | 491 | ||
| 497 | spin_lock_irqsave(shost->host_lock, flags); | 492 | spin_lock_irqsave(shost->host_lock, flags); |
| 493 | state = starget->state; | ||
| 494 | empty = --starget->reap_ref == 0 && | ||
| 495 | list_empty(&starget->devices) ? 1 : 0; | ||
| 496 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
| 498 | 497 | ||
| 499 | if (--starget->reap_ref == 0 && list_empty(&starget->devices)) { | 498 | if (!empty) |
| 500 | BUG_ON(starget->state == STARGET_DEL); | ||
| 501 | starget->state = STARGET_DEL; | ||
| 502 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
| 503 | execute_in_process_context(scsi_target_reap_usercontext, | ||
| 504 | &starget->ew); | ||
| 505 | return; | 499 | return; |
| 506 | 500 | ||
| 507 | } | 501 | BUG_ON(state == STARGET_DEL); |
| 508 | spin_unlock_irqrestore(shost->host_lock, flags); | 502 | starget->state = STARGET_DEL; |
| 509 | 503 | if (state == STARGET_CREATED) | |
| 510 | return; | 504 | scsi_target_destroy(starget); |
| 505 | else | ||
| 506 | execute_in_process_context(scsi_target_reap_usercontext, | ||
| 507 | &starget->ew); | ||
| 511 | } | 508 | } |
| 512 | 509 | ||
| 513 | /** | 510 | /** |
| @@ -1056,8 +1053,9 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, | |||
| 1056 | scsi_inq_str(vend, result, 8, 16), | 1053 | scsi_inq_str(vend, result, 8, 16), |
| 1057 | scsi_inq_str(mod, result, 16, 32)); | 1054 | scsi_inq_str(mod, result, 16, 32)); |
| 1058 | }); | 1055 | }); |
| 1056 | |||
| 1059 | } | 1057 | } |
| 1060 | 1058 | ||
| 1061 | res = SCSI_SCAN_TARGET_PRESENT; | 1059 | res = SCSI_SCAN_TARGET_PRESENT; |
| 1062 | goto out_free_result; | 1060 | goto out_free_result; |
| 1063 | } | 1061 | } |
| @@ -1497,7 +1495,6 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, | |||
| 1497 | if (scsi_host_scan_allowed(shost)) | 1495 | if (scsi_host_scan_allowed(shost)) |
| 1498 | scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata); | 1496 | scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata); |
| 1499 | mutex_unlock(&shost->scan_mutex); | 1497 | mutex_unlock(&shost->scan_mutex); |
| 1500 | transport_configure_device(&starget->dev); | ||
| 1501 | scsi_target_reap(starget); | 1498 | scsi_target_reap(starget); |
| 1502 | put_device(&starget->dev); | 1499 | put_device(&starget->dev); |
| 1503 | 1500 | ||
| @@ -1578,7 +1575,6 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel, | |||
| 1578 | out_reap: | 1575 | out_reap: |
| 1579 | /* now determine if the target has any children at all | 1576 | /* now determine if the target has any children at all |
| 1580 | * and if not, nuke it */ | 1577 | * and if not, nuke it */ |
| 1581 | transport_configure_device(&starget->dev); | ||
| 1582 | scsi_target_reap(starget); | 1578 | scsi_target_reap(starget); |
| 1583 | 1579 | ||
| 1584 | put_device(&starget->dev); | 1580 | put_device(&starget->dev); |
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 84e2a8ad83c9..198aa4571e35 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c | |||
| @@ -809,6 +809,27 @@ sdev_store_queue_type_rw(struct device *dev, struct device_attribute *attr, | |||
| 809 | return count; | 809 | return count; |
| 810 | } | 810 | } |
| 811 | 811 | ||
| 812 | static int scsi_target_add(struct scsi_target *starget) | ||
| 813 | { | ||
| 814 | int error; | ||
| 815 | |||
| 816 | if (starget->state != STARGET_CREATED) | ||
| 817 | return 0; | ||
| 818 | |||
| 819 | error = device_add(&starget->dev); | ||
| 820 | if (error) { | ||
| 821 | dev_err(&starget->dev, "target device_add failed, error %d\n", error); | ||
| 822 | get_device(&starget->dev); | ||
| 823 | scsi_target_reap(starget); | ||
| 824 | put_device(&starget->dev); | ||
| 825 | return error; | ||
| 826 | } | ||
| 827 | transport_add_device(&starget->dev); | ||
| 828 | starget->state = STARGET_RUNNING; | ||
| 829 | |||
| 830 | return 0; | ||
| 831 | } | ||
| 832 | |||
| 812 | static struct device_attribute sdev_attr_queue_type_rw = | 833 | static struct device_attribute sdev_attr_queue_type_rw = |
| 813 | __ATTR(queue_type, S_IRUGO | S_IWUSR, show_queue_type_field, | 834 | __ATTR(queue_type, S_IRUGO | S_IWUSR, show_queue_type_field, |
| 814 | sdev_store_queue_type_rw); | 835 | sdev_store_queue_type_rw); |
| @@ -824,10 +845,16 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) | |||
| 824 | { | 845 | { |
| 825 | int error, i; | 846 | int error, i; |
| 826 | struct request_queue *rq = sdev->request_queue; | 847 | struct request_queue *rq = sdev->request_queue; |
| 848 | struct scsi_target *starget = sdev->sdev_target; | ||
| 827 | 849 | ||
| 828 | if ((error = scsi_device_set_state(sdev, SDEV_RUNNING)) != 0) | 850 | if ((error = scsi_device_set_state(sdev, SDEV_RUNNING)) != 0) |
| 829 | return error; | 851 | return error; |
| 830 | 852 | ||
| 853 | error = scsi_target_add(starget); | ||
| 854 | if (error) | ||
| 855 | return error; | ||
| 856 | |||
| 857 | transport_configure_device(&starget->dev); | ||
| 831 | error = device_add(&sdev->sdev_gendev); | 858 | error = device_add(&sdev->sdev_gendev); |
| 832 | if (error) { | 859 | if (error) { |
| 833 | put_device(sdev->sdev_gendev.parent); | 860 | put_device(sdev->sdev_gendev.parent); |
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index b8b19e2f57bb..f6a9fe0ef09c 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h | |||
| @@ -181,7 +181,8 @@ struct scsi_device { | |||
| 181 | sdev_printk(prefix, (scmd)->device, fmt, ##a) | 181 | sdev_printk(prefix, (scmd)->device, fmt, ##a) |
| 182 | 182 | ||
| 183 | enum scsi_target_state { | 183 | enum scsi_target_state { |
| 184 | STARGET_RUNNING = 1, | 184 | STARGET_CREATED = 1, |
| 185 | STARGET_RUNNING, | ||
| 185 | STARGET_DEL, | 186 | STARGET_DEL, |
| 186 | }; | 187 | }; |
| 187 | 188 | ||
