diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2005-11-04 14:44:41 -0500 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.(none)> | 2005-11-05 10:52:24 -0500 |
commit | 39b7f1e25a412b0ef31e516cfc2fa4f40235f263 (patch) | |
tree | ff9d9c44b6199624d87af0d4776f2ecfd18f2635 | |
parent | 0ee957cb7a8f6e4fb8c6f943dbc451957bc0c6bc (diff) |
[SCSI] sd: Fix refcounting
Currently the driver takes a reference only for requests coming by way
of the gendisk, not for requests coming by way of the struct device or
struct scsi_device. Such requests can arrive in the rescan, flush,
and shutdown pathways.
The patch also makes the scsi_disk keep a reference to the underlying
scsi_device, and it erases the scsi_device's pointer to the scsi_disk
when the scsi_device is removed (since the pointer should no longer be
used).
This resolves Bugzilla entry #5237.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
-rw-r--r-- | drivers/scsi/sd.c | 99 |
1 files changed, 62 insertions, 37 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 9de8e186cb69..bb5b242ac6b4 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c | |||
@@ -177,24 +177,38 @@ static inline struct scsi_disk *scsi_disk(struct gendisk *disk) | |||
177 | return container_of(disk->private_data, struct scsi_disk, driver); | 177 | return container_of(disk->private_data, struct scsi_disk, driver); |
178 | } | 178 | } |
179 | 179 | ||
180 | static struct scsi_disk *scsi_disk_get(struct gendisk *disk) | 180 | static struct scsi_disk *__scsi_disk_get(struct gendisk *disk) |
181 | { | 181 | { |
182 | struct scsi_disk *sdkp = NULL; | 182 | struct scsi_disk *sdkp = NULL; |
183 | 183 | ||
184 | if (disk->private_data) { | ||
185 | sdkp = scsi_disk(disk); | ||
186 | if (scsi_device_get(sdkp->device) == 0) | ||
187 | kref_get(&sdkp->kref); | ||
188 | else | ||
189 | sdkp = NULL; | ||
190 | } | ||
191 | return sdkp; | ||
192 | } | ||
193 | |||
194 | static struct scsi_disk *scsi_disk_get(struct gendisk *disk) | ||
195 | { | ||
196 | struct scsi_disk *sdkp; | ||
197 | |||
184 | down(&sd_ref_sem); | 198 | down(&sd_ref_sem); |
185 | if (disk->private_data == NULL) | 199 | sdkp = __scsi_disk_get(disk); |
186 | goto out; | ||
187 | sdkp = scsi_disk(disk); | ||
188 | kref_get(&sdkp->kref); | ||
189 | if (scsi_device_get(sdkp->device)) | ||
190 | goto out_put; | ||
191 | up(&sd_ref_sem); | 200 | up(&sd_ref_sem); |
192 | return sdkp; | 201 | return sdkp; |
202 | } | ||
193 | 203 | ||
194 | out_put: | 204 | static struct scsi_disk *scsi_disk_get_from_dev(struct device *dev) |
195 | kref_put(&sdkp->kref, scsi_disk_release); | 205 | { |
196 | sdkp = NULL; | 206 | struct scsi_disk *sdkp; |
197 | out: | 207 | |
208 | down(&sd_ref_sem); | ||
209 | sdkp = dev_get_drvdata(dev); | ||
210 | if (sdkp) | ||
211 | sdkp = __scsi_disk_get(sdkp->disk); | ||
198 | up(&sd_ref_sem); | 212 | up(&sd_ref_sem); |
199 | return sdkp; | 213 | return sdkp; |
200 | } | 214 | } |
@@ -716,16 +730,17 @@ static int sd_sync_cache(struct scsi_device *sdp) | |||
716 | 730 | ||
717 | static int sd_issue_flush(struct device *dev, sector_t *error_sector) | 731 | static int sd_issue_flush(struct device *dev, sector_t *error_sector) |
718 | { | 732 | { |
733 | int ret = 0; | ||
719 | struct scsi_device *sdp = to_scsi_device(dev); | 734 | struct scsi_device *sdp = to_scsi_device(dev); |
720 | struct scsi_disk *sdkp = dev_get_drvdata(dev); | 735 | struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev); |
721 | 736 | ||
722 | if (!sdkp) | 737 | if (!sdkp) |
723 | return -ENODEV; | 738 | return -ENODEV; |
724 | 739 | ||
725 | if (!sdkp->WCE) | 740 | if (sdkp->WCE) |
726 | return 0; | 741 | ret = sd_sync_cache(sdp); |
727 | 742 | scsi_disk_put(sdkp); | |
728 | return sd_sync_cache(sdp); | 743 | return ret; |
729 | } | 744 | } |
730 | 745 | ||
731 | static void sd_end_flush(request_queue_t *q, struct request *flush_rq) | 746 | static void sd_end_flush(request_queue_t *q, struct request *flush_rq) |
@@ -754,23 +769,30 @@ static void sd_end_flush(request_queue_t *q, struct request *flush_rq) | |||
754 | static int sd_prepare_flush(request_queue_t *q, struct request *rq) | 769 | static int sd_prepare_flush(request_queue_t *q, struct request *rq) |
755 | { | 770 | { |
756 | struct scsi_device *sdev = q->queuedata; | 771 | struct scsi_device *sdev = q->queuedata; |
757 | struct scsi_disk *sdkp = dev_get_drvdata(&sdev->sdev_gendev); | 772 | struct scsi_disk *sdkp = scsi_disk_get_from_dev(&sdev->sdev_gendev); |
758 | 773 | int ret = 0; | |
759 | if (sdkp->WCE) { | 774 | |
760 | memset(rq->cmd, 0, sizeof(rq->cmd)); | 775 | if (sdkp) { |
761 | rq->flags |= REQ_BLOCK_PC | REQ_SOFTBARRIER; | 776 | if (sdkp->WCE) { |
762 | rq->timeout = SD_TIMEOUT; | 777 | memset(rq->cmd, 0, sizeof(rq->cmd)); |
763 | rq->cmd[0] = SYNCHRONIZE_CACHE; | 778 | rq->flags |= REQ_BLOCK_PC | REQ_SOFTBARRIER; |
764 | return 1; | 779 | rq->timeout = SD_TIMEOUT; |
780 | rq->cmd[0] = SYNCHRONIZE_CACHE; | ||
781 | ret = 1; | ||
782 | } | ||
783 | scsi_disk_put(sdkp); | ||
765 | } | 784 | } |
766 | 785 | return ret; | |
767 | return 0; | ||
768 | } | 786 | } |
769 | 787 | ||
770 | static void sd_rescan(struct device *dev) | 788 | static void sd_rescan(struct device *dev) |
771 | { | 789 | { |
772 | struct scsi_disk *sdkp = dev_get_drvdata(dev); | 790 | struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev); |
773 | sd_revalidate_disk(sdkp->disk); | 791 | |
792 | if (sdkp) { | ||
793 | sd_revalidate_disk(sdkp->disk); | ||
794 | scsi_disk_put(sdkp); | ||
795 | } | ||
774 | } | 796 | } |
775 | 797 | ||
776 | 798 | ||
@@ -1561,6 +1583,7 @@ static int sd_probe(struct device *dev) | |||
1561 | if (error) | 1583 | if (error) |
1562 | goto out_put; | 1584 | goto out_put; |
1563 | 1585 | ||
1586 | get_device(&sdp->sdev_gendev); | ||
1564 | sdkp->device = sdp; | 1587 | sdkp->device = sdp; |
1565 | sdkp->driver = &sd_template; | 1588 | sdkp->driver = &sd_template; |
1566 | sdkp->disk = gd; | 1589 | sdkp->disk = gd; |
@@ -1637,7 +1660,9 @@ static int sd_remove(struct device *dev) | |||
1637 | 1660 | ||
1638 | del_gendisk(sdkp->disk); | 1661 | del_gendisk(sdkp->disk); |
1639 | sd_shutdown(dev); | 1662 | sd_shutdown(dev); |
1663 | |||
1640 | down(&sd_ref_sem); | 1664 | down(&sd_ref_sem); |
1665 | dev_set_drvdata(dev, NULL); | ||
1641 | kref_put(&sdkp->kref, scsi_disk_release); | 1666 | kref_put(&sdkp->kref, scsi_disk_release); |
1642 | up(&sd_ref_sem); | 1667 | up(&sd_ref_sem); |
1643 | 1668 | ||
@@ -1663,8 +1688,8 @@ static void scsi_disk_release(struct kref *kref) | |||
1663 | spin_unlock(&sd_index_lock); | 1688 | spin_unlock(&sd_index_lock); |
1664 | 1689 | ||
1665 | disk->private_data = NULL; | 1690 | disk->private_data = NULL; |
1666 | |||
1667 | put_disk(disk); | 1691 | put_disk(disk); |
1692 | put_device(&sdkp->device->sdev_gendev); | ||
1668 | 1693 | ||
1669 | kfree(sdkp); | 1694 | kfree(sdkp); |
1670 | } | 1695 | } |
@@ -1677,18 +1702,18 @@ static void scsi_disk_release(struct kref *kref) | |||
1677 | static void sd_shutdown(struct device *dev) | 1702 | static void sd_shutdown(struct device *dev) |
1678 | { | 1703 | { |
1679 | struct scsi_device *sdp = to_scsi_device(dev); | 1704 | struct scsi_device *sdp = to_scsi_device(dev); |
1680 | struct scsi_disk *sdkp = dev_get_drvdata(dev); | 1705 | struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev); |
1681 | 1706 | ||
1682 | if (!sdkp) | 1707 | if (!sdkp) |
1683 | return; /* this can happen */ | 1708 | return; /* this can happen */ |
1684 | 1709 | ||
1685 | if (!sdkp->WCE) | 1710 | if (sdkp->WCE) { |
1686 | return; | 1711 | printk(KERN_NOTICE "Synchronizing SCSI cache for disk %s: \n", |
1687 | 1712 | sdkp->disk->disk_name); | |
1688 | printk(KERN_NOTICE "Synchronizing SCSI cache for disk %s: \n", | 1713 | sd_sync_cache(sdp); |
1689 | sdkp->disk->disk_name); | 1714 | } |
1690 | sd_sync_cache(sdp); | 1715 | scsi_disk_put(sdkp); |
1691 | } | 1716 | } |
1692 | 1717 | ||
1693 | /** | 1718 | /** |
1694 | * init_sd - entry point for this driver (both when built in or when | 1719 | * init_sd - entry point for this driver (both when built in or when |