diff options
Diffstat (limited to 'drivers/scsi/sd.c')
-rw-r--r-- | drivers/scsi/sd.c | 108 |
1 files changed, 63 insertions, 45 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 9a1dc0cea03..8613a131771 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) return container_of(disk->private_data, struct scsi_disk, driver); } -static struct scsi_disk *scsi_disk_get(struct gendisk *disk) +static struct scsi_disk *__scsi_disk_get(struct gendisk *disk) { struct scsi_disk *sdkp = NULL; + if (disk->private_data) { + sdkp = scsi_disk(disk); + if (scsi_device_get(sdkp->device) == 0) + kref_get(&sdkp->kref); + else + sdkp = NULL; + } + return sdkp; +} + +static struct scsi_disk *scsi_disk_get(struct gendisk *disk) +{ + struct scsi_disk *sdkp; + down(&sd_ref_sem); - if (disk->private_data == NULL) - goto out; - sdkp = scsi_disk(disk); - kref_get(&sdkp->kref); - if (scsi_device_get(sdkp->device)) - goto out_put; + sdkp = __scsi_disk_get(disk); up(&sd_ref_sem); return sdkp; +} - out_put: - kref_put(&sdkp->kref, scsi_disk_release); - sdkp = NULL; - out: +static struct scsi_disk *scsi_disk_get_from_dev(struct device *dev) +{ + struct scsi_disk *sdkp; + + down(&sd_ref_sem); + sdkp = dev_get_drvdata(dev); + if (sdkp) + sdkp = __scsi_disk_get(sdkp->disk); up(&sd_ref_sem); return sdkp; } @@ -716,16 +730,17 @@ static int sd_sync_cache(struct scsi_device *sdp) static int sd_issue_flush(struct device *dev, sector_t *error_sector) { + int ret = 0; struct scsi_device *sdp = to_scsi_device(dev); - struct scsi_disk *sdkp = dev_get_drvdata(dev); + struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev); if (!sdkp) return -ENODEV; - if (!sdkp->WCE) - return 0; - - return sd_sync_cache(sdp); + if (sdkp->WCE) + ret = sd_sync_cache(sdp); + scsi_disk_put(sdkp); + return ret; } static void sd_end_flush(request_queue_t *q, struct request *flush_rq) @@ -756,21 +771,24 @@ static int sd_prepare_flush(request_queue_t *q, struct request *rq) struct scsi_device *sdev = q->queuedata; struct scsi_disk *sdkp = dev_get_drvdata(&sdev->sdev_gendev); - if (sdkp->WCE) { - memset(rq->cmd, 0, sizeof(rq->cmd)); - rq->flags |= REQ_BLOCK_PC | REQ_SOFTBARRIER; - rq->timeout = SD_TIMEOUT; - rq->cmd[0] = SYNCHRONIZE_CACHE; - return 1; - } + if (!sdkp || !sdkp->WCE) + return 0; - return 0; + memset(rq->cmd, 0, sizeof(rq->cmd)); + rq->flags |= REQ_BLOCK_PC | REQ_SOFTBARRIER; + rq->timeout = SD_TIMEOUT; + rq->cmd[0] = SYNCHRONIZE_CACHE; + return 1; } static void sd_rescan(struct device *dev) { - struct scsi_disk *sdkp = dev_get_drvdata(dev); - sd_revalidate_disk(sdkp->disk); + struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev); + + if (sdkp) { + sd_revalidate_disk(sdkp->disk); + scsi_disk_put(sdkp); + } } @@ -1253,14 +1271,13 @@ got_data: * Jacques Gelinas (Jacques@solucorp.qc.ca) */ int hard_sector = sector_size; - sector_t sz = sdkp->capacity * (hard_sector/256); + sector_t sz = (sdkp->capacity/2) * (hard_sector/256); request_queue_t *queue = sdp->request_queue; - sector_t mb; + sector_t mb = sz; blk_queue_hardsect_size(queue, hard_sector); /* avoid 64-bit division on 32-bit platforms */ - mb = sz >> 1; - sector_div(sz, 1250); + sector_div(sz, 625); mb -= sz - 974; sector_div(mb, 1950); @@ -1535,8 +1552,8 @@ static int sd_probe(struct device *dev) if (sdp->type != TYPE_DISK && sdp->type != TYPE_MOD && sdp->type != TYPE_RBC) goto out; - SCSI_LOG_HLQUEUE(3, printk("sd_attach: scsi device: <%d,%d,%d,%d>\n", - sdp->host->host_no, sdp->channel, sdp->id, sdp->lun)); + SCSI_LOG_HLQUEUE(3, sdev_printk(KERN_INFO, sdp, + "sd_attach\n")); error = -ENOMEM; sdkp = kmalloc(sizeof(*sdkp), GFP_KERNEL); @@ -1562,6 +1579,7 @@ static int sd_probe(struct device *dev) if (error) goto out_put; + get_device(&sdp->sdev_gendev); sdkp->device = sdp; sdkp->driver = &sd_template; sdkp->disk = gd; @@ -1608,10 +1626,8 @@ static int sd_probe(struct device *dev) dev_set_drvdata(dev, sdkp); add_disk(gd); - printk(KERN_NOTICE "Attached scsi %sdisk %s at scsi%d, channel %d, " - "id %d, lun %d\n", sdp->removable ? "removable " : "", - gd->disk_name, sdp->host->host_no, sdp->channel, - sdp->id, sdp->lun); + sdev_printk(KERN_NOTICE, sdp, "Attached scsi %sdisk %s\n", + sdp->removable ? "removable " : "", gd->disk_name); return 0; @@ -1640,7 +1656,9 @@ static int sd_remove(struct device *dev) del_gendisk(sdkp->disk); sd_shutdown(dev); + down(&sd_ref_sem); + dev_set_drvdata(dev, NULL); kref_put(&sdkp->kref, scsi_disk_release); up(&sd_ref_sem); @@ -1666,8 +1684,8 @@ static void scsi_disk_release(struct kref *kref) spin_unlock(&sd_index_lock); disk->private_data = NULL; - put_disk(disk); + put_device(&sdkp->device->sdev_gendev); kfree(sdkp); } @@ -1680,18 +1698,18 @@ static void scsi_disk_release(struct kref *kref) static void sd_shutdown(struct device *dev) { struct scsi_device *sdp = to_scsi_device(dev); - struct scsi_disk *sdkp = dev_get_drvdata(dev); + struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev); if (!sdkp) return; /* this can happen */ - if (!sdkp->WCE) - return; - - printk(KERN_NOTICE "Synchronizing SCSI cache for disk %s: \n", - sdkp->disk->disk_name); - sd_sync_cache(sdp); -} + if (sdkp->WCE) { + printk(KERN_NOTICE "Synchronizing SCSI cache for disk %s: \n", + sdkp->disk->disk_name); + sd_sync_cache(sdp); + } + scsi_disk_put(sdkp); +} /** * init_sd - entry point for this driver (both when built in or when |