|
@@ -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>
|
|
@@ -115,12 +114,10 @@ MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK15_MAJOR);
|
|
|
*/
|
|
|
#define SD_BUF_SIZE 512
|
|
|
|
|
|
-static void scsi_disk_release(struct kref *kref);
|
|
|
-
|
|
|
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 */
|
|
@@ -131,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);
|
|
@@ -152,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(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,
|
|
@@ -195,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);
|
|
@@ -209,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;
|
|
|
}
|
|
@@ -243,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);
|
|
|
}
|
|
@@ -1381,10 +1463,6 @@ sd_read_cache_type(struct scsi_disk *sdkp, char *diskname,
|
|
|
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;
|
|
|
|
|
@@ -1417,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;
|
|
@@ -1548,8 +1626,6 @@ static int sd_probe(struct device *dev)
|
|
|
if (!sdkp)
|
|
|
goto out;
|
|
|
|
|
|
- kref_init(&sdkp->kref);
|
|
|
-
|
|
|
gd = alloc_disk(16);
|
|
|
if (!gd)
|
|
|
goto out_free;
|
|
@@ -1566,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;
|
|
@@ -1616,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;
|
|
|
}
|
|
|
|
|
@@ -1639,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;
|
|
@@ -1652,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);
|
|
@@ -1715,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);
|
|
|
}
|
|
|
|
|
@@ -1732,6 +1820,8 @@ 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");
|
|
|
+
|
|
|
+ class_unregister(&sd_disk_class);
|
|
|
}
|
|
|
|
|
|
module_init(init_sd);
|