diff options
Diffstat (limited to 'drivers/scsi/sd.c')
-rw-r--r-- | drivers/scsi/sd.c | 190 |
1 files changed, 157 insertions, 33 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 930db398d107..c647d85d97d1 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -47,7 +47,6 @@ #include <linux/init.h> #include <linux/blkdev.h> #include <linux/blkpg.h> -#include <linux/kref.h> #include <linux/delay.h> #include <linux/mutex.h> #include <asm/uaccess.h> @@ -71,6 +70,27 @@ */ #define SD_MAJORS 16 +MODULE_AUTHOR("Eric Youngdale"); +MODULE_DESCRIPTION("SCSI disk (sd) driver"); +MODULE_LICENSE("GPL"); + +MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK0_MAJOR); +MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK1_MAJOR); +MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK2_MAJOR); +MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK3_MAJOR); +MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK4_MAJOR); +MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK5_MAJOR); +MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK6_MAJOR); +MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK7_MAJOR); +MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK8_MAJOR); +MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK9_MAJOR); +MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK10_MAJOR); +MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK11_MAJOR); +MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK12_MAJOR); +MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK13_MAJOR); +MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK14_MAJOR); +MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK15_MAJOR); + /* * This is limited by the naming scheme enforced in sd_probe, * add another character to it if you really need more disks. @@ -89,12 +109,15 @@ #define SD_MAX_RETRIES 5 #define SD_PASSTHROUGH_RETRIES 1 -static void scsi_disk_release(struct kref *kref); +/* + * Size of the initial data buffer for mode and read capacity data + */ +#define SD_BUF_SIZE 512 struct scsi_disk { struct scsi_driver *driver; /* always &sd_template */ struct scsi_device *device; - struct kref kref; + struct class_device cdev; struct gendisk *disk; unsigned int openers; /* protected by BKL for now, yuck */ sector_t capacity; /* size in 512-byte sectors */ @@ -105,6 +128,7 @@ struct scsi_disk { unsigned RCD : 1; /* state of disk RCD bit, unused */ unsigned DPOFUA : 1; /* state of disk DPOFUA bit */ }; +#define to_scsi_disk(obj) container_of(obj,struct scsi_disk,cdev) static DEFINE_IDR(sd_index_idr); static DEFINE_SPINLOCK(sd_index_lock); @@ -126,6 +150,92 @@ static int sd_issue_flush(struct device *, sector_t *); static void sd_prepare_flush(request_queue_t *, struct request *); static void sd_read_capacity(struct scsi_disk *sdkp, char *diskname, unsigned char *buffer); +static void scsi_disk_release(struct class_device *cdev); + +static const char *sd_cache_types[] = { + "write through", "none", "write back", + "write back, no read (daft)" +}; + +static ssize_t sd_store_cache_type(struct class_device *cdev, const char *buf, + size_t count) +{ + int i, ct = -1, rcd, wce, sp; + struct scsi_disk *sdkp = to_scsi_disk(cdev); + struct scsi_device *sdp = sdkp->device; + char buffer[64]; + char *buffer_data; + struct scsi_mode_data data; + struct scsi_sense_hdr sshdr; + int len; + + if (sdp->type != TYPE_DISK) + /* no cache control on RBC devices; theoretically they + * can do it, but there's probably so many exceptions + * it's not worth the risk */ + return -EINVAL; + + for (i = 0; i < sizeof(sd_cache_types)/sizeof(sd_cache_types[0]); i++) { + const int len = strlen(sd_cache_types[i]); + if (strncmp(sd_cache_types[i], buf, len) == 0 && + buf[len] == '\n') { + ct = i; + break; + } + } + if (ct < 0) + return -EINVAL; + rcd = ct & 0x01 ? 1 : 0; + wce = ct & 0x02 ? 1 : 0; + if (scsi_mode_sense(sdp, 0x08, 8, buffer, sizeof(buffer), SD_TIMEOUT, + SD_MAX_RETRIES, &data, NULL)) + return -EINVAL; + len = min_t(size_t, sizeof(buffer), data.length - data.header_length - + data.block_descriptor_length); + buffer_data = buffer + data.header_length + + data.block_descriptor_length; + buffer_data[2] &= ~0x05; + buffer_data[2] |= wce << 2 | rcd; + sp = buffer_data[0] & 0x80 ? 1 : 0; + + if (scsi_mode_select(sdp, 1, sp, 8, buffer_data, len, SD_TIMEOUT, + SD_MAX_RETRIES, &data, &sshdr)) { + if (scsi_sense_valid(&sshdr)) + scsi_print_sense_hdr(sdkp->disk->disk_name, &sshdr); + return -EINVAL; + } + sd_revalidate_disk(sdkp->disk); + return count; +} + +static ssize_t sd_show_cache_type(struct class_device *cdev, char *buf) +{ + struct scsi_disk *sdkp = to_scsi_disk(cdev); + int ct = sdkp->RCD + 2*sdkp->WCE; + + return snprintf(buf, 40, "%s\n", sd_cache_types[ct]); +} + +static ssize_t sd_show_fua(struct class_device *cdev, char *buf) +{ + struct scsi_disk *sdkp = to_scsi_disk(cdev); + + return snprintf(buf, 20, "%u\n", sdkp->DPOFUA); +} + +static struct class_device_attribute sd_disk_attrs[] = { + __ATTR(cache_type, S_IRUGO|S_IWUSR, sd_show_cache_type, + sd_store_cache_type), + __ATTR(FUA, S_IRUGO, sd_show_fua, NULL), + __ATTR_NULL, +}; + +static struct class sd_disk_class = { + .name = "scsi_disk", + .owner = THIS_MODULE, + .release = scsi_disk_release, + .class_dev_attrs = sd_disk_attrs, +}; static struct scsi_driver sd_template = { .owner = THIS_MODULE, @@ -169,8 +279,6 @@ static int sd_major(int major_idx) } } -#define to_scsi_disk(obj) container_of(obj,struct scsi_disk,kref) - static inline struct scsi_disk *scsi_disk(struct gendisk *disk) { return container_of(disk->private_data, struct scsi_disk, driver); @@ -183,7 +291,7 @@ static struct scsi_disk *__scsi_disk_get(struct gendisk *disk) if (disk->private_data) { sdkp = scsi_disk(disk); if (scsi_device_get(sdkp->device) == 0) - kref_get(&sdkp->kref); + class_device_get(&sdkp->cdev); else sdkp = NULL; } @@ -217,7 +325,7 @@ static void scsi_disk_put(struct scsi_disk *sdkp) struct scsi_device *sdev = sdkp->device; mutex_lock(&sd_ref_mutex); - kref_put(&sdkp->kref, scsi_disk_release); + class_device_put(&sdkp->cdev); scsi_device_put(sdev); mutex_unlock(&sd_ref_mutex); } @@ -1239,7 +1347,7 @@ sd_do_mode_sense(struct scsi_device *sdp, int dbd, int modepage, /* * read write protect setting, if possible - called only in sd_revalidate_disk() - * called with buffer of length 512 + * called with buffer of length SD_BUF_SIZE */ static void sd_read_write_protect_flag(struct scsi_disk *sdkp, char *diskname, @@ -1297,7 +1405,7 @@ sd_read_write_protect_flag(struct scsi_disk *sdkp, char *diskname, /* * sd_read_cache_type - called only from sd_revalidate_disk() - * called with buffer of length 512 + * called with buffer of length SD_BUF_SIZE */ static void sd_read_cache_type(struct scsi_disk *sdkp, char *diskname, @@ -1328,6 +1436,12 @@ sd_read_cache_type(struct scsi_disk *sdkp, char *diskname, if (!scsi_status_is_good(res)) goto bad_sense; + if (!data.header_length) { + modepage = 6; + printk(KERN_ERR "%s: missing header in MODE_SENSE response\n", + diskname); + } + /* that went OK, now ask for the proper length */ len = data.length; @@ -1342,18 +1456,22 @@ sd_read_cache_type(struct scsi_disk *sdkp, char *diskname, /* Take headers and block descriptors into account */ len += data.header_length + data.block_descriptor_length; + if (len > SD_BUF_SIZE) + goto bad_sense; /* Get the data */ res = sd_do_mode_sense(sdp, dbd, modepage, buffer, len, &data, &sshdr); if (scsi_status_is_good(res)) { - const char *types[] = { - "write through", "none", "write back", - "write back, no read (daft)" - }; int ct = 0; int offset = data.header_length + data.block_descriptor_length; + if (offset >= SD_BUF_SIZE - 2) { + printk(KERN_ERR "%s: malformed MODE SENSE response", + diskname); + goto defaults; + } + if ((buffer[offset] & 0x3f) != modepage) { printk(KERN_ERR "%s: got wrong page\n", diskname); goto defaults; @@ -1377,7 +1495,7 @@ sd_read_cache_type(struct scsi_disk *sdkp, char *diskname, ct = sdkp->RCD + 2*sdkp->WCE; printk(KERN_NOTICE "SCSI device %s: drive cache: %s%s\n", - diskname, types[ct], + diskname, sd_cache_types[ct], sdkp->DPOFUA ? " w/ FUA" : ""); return; @@ -1398,6 +1516,7 @@ defaults: diskname); sdkp->WCE = 0; sdkp->RCD = 0; + sdkp->DPOFUA = 0; } /** @@ -1421,7 +1540,7 @@ static int sd_revalidate_disk(struct gendisk *disk) if (!scsi_device_online(sdp)) goto out; - buffer = kmalloc(512, GFP_KERNEL | __GFP_DMA); + buffer = kmalloc(SD_BUF_SIZE, GFP_KERNEL | __GFP_DMA); if (!buffer) { printk(KERN_WARNING "(sd_revalidate_disk:) Memory allocation " "failure.\n"); @@ -1503,13 +1622,10 @@ static int sd_probe(struct device *dev) "sd_attach\n")); error = -ENOMEM; - sdkp = kmalloc(sizeof(*sdkp), GFP_KERNEL); + sdkp = kzalloc(sizeof(*sdkp), GFP_KERNEL); if (!sdkp) goto out; - memset (sdkp, 0, sizeof(*sdkp)); - kref_init(&sdkp->kref); - gd = alloc_disk(16); if (!gd) goto out_free; @@ -1526,7 +1642,16 @@ static int sd_probe(struct device *dev) if (error) goto out_put; + class_device_initialize(&sdkp->cdev); + sdkp->cdev.dev = &sdp->sdev_gendev; + sdkp->cdev.class = &sd_disk_class; + strncpy(sdkp->cdev.class_id, sdp->sdev_gendev.bus_id, BUS_ID_SIZE); + + if (class_device_add(&sdkp->cdev)) + goto out_put; + get_device(&sdp->sdev_gendev); + sdkp->device = sdp; sdkp->driver = &sd_template; sdkp->disk = gd; @@ -1558,8 +1683,6 @@ static int sd_probe(struct device *dev) 'a' + m1, 'a' + m2, 'a' + m3); } - strcpy(gd->devfs_name, sdp->devfs_name); - gd->private_data = &sdkp->driver; gd->queue = sdkp->device->request_queue; @@ -1578,11 +1701,11 @@ static int sd_probe(struct device *dev) return 0; -out_put: + out_put: put_disk(gd); -out_free: + out_free: kfree(sdkp); -out: + out: return error; } @@ -1601,12 +1724,13 @@ static int sd_remove(struct device *dev) { struct scsi_disk *sdkp = dev_get_drvdata(dev); + class_device_del(&sdkp->cdev); del_gendisk(sdkp->disk); sd_shutdown(dev); mutex_lock(&sd_ref_mutex); dev_set_drvdata(dev, NULL); - kref_put(&sdkp->kref, scsi_disk_release); + class_device_put(&sdkp->cdev); mutex_unlock(&sd_ref_mutex); return 0; @@ -1614,16 +1738,16 @@ static int sd_remove(struct device *dev) /** * scsi_disk_release - Called to free the scsi_disk structure - * @kref: pointer to embedded kref + * @cdev: pointer to embedded class device * * sd_ref_mutex must be held entering this routine. Because it is * called on last put, you should always use the scsi_disk_get() * scsi_disk_put() helpers which manipulate the semaphore directly - * and never do a direct kref_put(). + * and never do a direct class_device_put(). **/ -static void scsi_disk_release(struct kref *kref) +static void scsi_disk_release(struct class_device *cdev) { - struct scsi_disk *sdkp = to_scsi_disk(kref); + struct scsi_disk *sdkp = to_scsi_disk(cdev); struct gendisk *disk = sdkp->disk; spin_lock(&sd_index_lock); @@ -1677,6 +1801,8 @@ static int __init init_sd(void) if (!majors) return -ENODEV; + class_register(&sd_disk_class); + return scsi_register_driver(&sd_template.gendrv); } @@ -1694,11 +1820,9 @@ static void __exit exit_sd(void) scsi_unregister_driver(&sd_template.gendrv); for (i = 0; i < SD_MAJORS; i++) unregister_blkdev(sd_major(i), "sd"); -} -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Eric Youngdale"); -MODULE_DESCRIPTION("SCSI disk (sd) driver"); + class_unregister(&sd_disk_class); +} module_init(init_sd); module_exit(exit_sd); |