* than the level indicated above to trigger output.
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#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>
*/
#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.
*/
#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 class_device cdev;
+ struct kref kref;
struct gendisk *disk;
unsigned int openers; /* protected by BKL for now, yuck */
sector_t capacity; /* size in 512-byte sectors */
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);
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 < ARRAY_SIZE(sd_cache_types); 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_store_allow_restart(struct class_device *cdev, const char *buf,
- size_t count)
-{
- struct scsi_disk *sdkp = to_scsi_disk(cdev);
- struct scsi_device *sdp = sdkp->device;
-
- if (!capable(CAP_SYS_ADMIN))
- return -EACCES;
-
- if (sdp->type != TYPE_DISK)
- return -EINVAL;
-
- sdp->allow_restart = simple_strtoul(buf, NULL, 10);
-
- 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 ssize_t sd_show_allow_restart(struct class_device *cdev, char *buf)
-{
- struct scsi_disk *sdkp = to_scsi_disk(cdev);
-
- return snprintf(buf, 40, "%d\n", sdkp->device->allow_restart);
-}
-
-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(allow_restart, S_IRUGO|S_IWUSR, sd_show_allow_restart,
- sd_store_allow_restart),
- __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,
}
}
+#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);
if (disk->private_data) {
sdkp = scsi_disk(disk);
if (scsi_device_get(sdkp->device) == 0)
- class_device_get(&sdkp->cdev);
+ kref_get(&sdkp->kref);
else
sdkp = NULL;
}
struct scsi_device *sdev = sdkp->device;
mutex_lock(&sd_ref_mutex);
- class_device_put(&sdkp->cdev);
+ kref_put(&sdkp->kref, scsi_disk_release);
scsi_device_put(sdev);
mutex_unlock(&sd_ref_mutex);
}
SCpnt->cmnd[4] = (unsigned char) this_count;
SCpnt->cmnd[5] = 0;
}
- SCpnt->request_bufflen = this_count * sdp->sector_size;
+ SCpnt->request_bufflen = SCpnt->bufflen =
+ this_count * sdp->sector_size;
/*
* We shouldn't disconnect in the middle of a sector, so with a dumb
static void sd_rw_intr(struct scsi_cmnd * SCpnt)
{
int result = SCpnt->result;
- unsigned int xfer_size = SCpnt->request_bufflen;
- unsigned int good_bytes = result ? 0 : xfer_size;
- u64 start_lba = SCpnt->request->sector;
- u64 bad_lba;
+ int this_count = SCpnt->bufflen;
+ int good_bytes = (result == 0 ? this_count : 0);
+ sector_t block_sectors = 1;
+ u64 first_err_block;
+ sector_t error_sector;
struct scsi_sense_hdr sshdr;
int sense_valid = 0;
int sense_deferred = 0;
if (sense_valid)
sense_deferred = scsi_sense_is_deferred(&sshdr);
}
+
#ifdef CONFIG_SCSI_LOGGING
SCSI_LOG_HLCOMPLETE(1, printk("sd_rw_intr: %s: res=0x%x\n",
SCpnt->request->rq_disk->disk_name, result));
sshdr.sense_key, sshdr.asc, sshdr.ascq));
}
#endif
- if (driver_byte(result) != DRIVER_SENSE &&
- (!sense_valid || sense_deferred))
- goto out;
+ /*
+ Handle MEDIUM ERRORs that indicate partial success. Since this is a
+ relatively rare error condition, no care is taken to avoid
+ unnecessary additional work such as memcpy's that could be avoided.
+ */
+ if (driver_byte(result) != 0 &&
+ sense_valid && !sense_deferred) {
+ switch (sshdr.sense_key) {
+ case MEDIUM_ERROR:
+ if (!blk_fs_request(SCpnt->request))
+ break;
+ info_valid = scsi_get_sense_info_fld(
+ SCpnt->sense_buffer, SCSI_SENSE_BUFFERSIZE,
+ &first_err_block);
+ /*
+ * May want to warn and skip if following cast results
+ * in actual truncation (if sector_t < 64 bits)
+ */
+ error_sector = (sector_t)first_err_block;
+ if (SCpnt->request->bio != NULL)
+ block_sectors = bio_sectors(SCpnt->request->bio);
+ switch (SCpnt->device->sector_size) {
+ case 1024:
+ error_sector <<= 1;
+ if (block_sectors < 2)
+ block_sectors = 2;
+ break;
+ case 2048:
+ error_sector <<= 2;
+ if (block_sectors < 4)
+ block_sectors = 4;
+ break;
+ case 4096:
+ error_sector <<=3;
+ if (block_sectors < 8)
+ block_sectors = 8;
+ break;
+ case 256:
+ error_sector >>= 1;
+ break;
+ default:
+ break;
+ }
- switch (sshdr.sense_key) {
- case HARDWARE_ERROR:
- case MEDIUM_ERROR:
- if (!blk_fs_request(SCpnt->request))
- goto out;
- info_valid = scsi_get_sense_info_fld(SCpnt->sense_buffer,
- SCSI_SENSE_BUFFERSIZE,
- &bad_lba);
- if (!info_valid)
- goto out;
- if (xfer_size <= SCpnt->device->sector_size)
- goto out;
- switch (SCpnt->device->sector_size) {
- case 256:
- start_lba <<= 1;
+ error_sector &= ~(block_sectors - 1);
+ good_bytes = (error_sector - SCpnt->request->sector) << 9;
+ if (good_bytes < 0 || good_bytes >= this_count)
+ good_bytes = 0;
break;
- case 512:
- break;
- case 1024:
- start_lba >>= 1;
- break;
- case 2048:
- start_lba >>= 2;
+
+ case RECOVERED_ERROR: /* an error occurred, but it recovered */
+ case NO_SENSE: /* LLDD got sense data */
+ /*
+ * Inform the user, but make sure that it's not treated
+ * as a hard error.
+ */
+ scsi_print_sense("sd", SCpnt);
+ SCpnt->result = 0;
+ memset(SCpnt->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+ good_bytes = this_count;
break;
- case 4096:
- start_lba >>= 3;
+
+ case ILLEGAL_REQUEST:
+ if (SCpnt->device->use_10_for_rw &&
+ (SCpnt->cmnd[0] == READ_10 ||
+ SCpnt->cmnd[0] == WRITE_10))
+ SCpnt->device->use_10_for_rw = 0;
+ if (SCpnt->device->use_10_for_ms &&
+ (SCpnt->cmnd[0] == MODE_SENSE_10 ||
+ SCpnt->cmnd[0] == MODE_SELECT_10))
+ SCpnt->device->use_10_for_ms = 0;
break;
+
default:
- /* Print something here with limiting frequency. */
- goto out;
break;
}
- /* This computation should always be done in terms of
- * the resolution of the device's medium.
- */
- good_bytes = (bad_lba - start_lba)*SCpnt->device->sector_size;
- break;
- case RECOVERED_ERROR:
- case NO_SENSE:
- /* Inform the user, but make sure that it's not treated
- * as a hard error.
- */
- scsi_print_sense("sd", SCpnt);
- SCpnt->result = 0;
- memset(SCpnt->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
- good_bytes = xfer_size;
- break;
- case ILLEGAL_REQUEST:
- if (SCpnt->device->use_10_for_rw &&
- (SCpnt->cmnd[0] == READ_10 ||
- SCpnt->cmnd[0] == WRITE_10))
- SCpnt->device->use_10_for_rw = 0;
- if (SCpnt->device->use_10_for_ms &&
- (SCpnt->cmnd[0] == MODE_SENSE_10 ||
- SCpnt->cmnd[0] == MODE_SELECT_10))
- SCpnt->device->use_10_for_ms = 0;
- break;
- default:
- break;
}
- out:
- scsi_io_completion(SCpnt, good_bytes);
+ /*
+ * This calls the generic completion function, now that we know
+ * how many actual sectors finished, and how many sectors we need
+ * to say have failed.
+ */
+ scsi_io_completion(SCpnt, good_bytes, block_sectors << 9);
}
static int media_not_present(struct scsi_disk *sdkp,
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;
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;
ct = sdkp->RCD + 2*sdkp->WCE;
printk(KERN_NOTICE "SCSI device %s: drive cache: %s%s\n",
- diskname, sd_cache_types[ct],
+ diskname, types[ct],
sdkp->DPOFUA ? " w/ FUA" : "");
return;
"sd_attach\n"));
error = -ENOMEM;
- sdkp = kzalloc(sizeof(*sdkp), GFP_KERNEL);
+ sdkp = kmalloc(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;
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;
'a' + m1, 'a' + m2, 'a' + m3);
}
+ strcpy(gd->devfs_name, sdp->devfs_name);
+
gd->private_data = &sdkp->driver;
gd->queue = sdkp->device->request_queue;
return 0;
- out_put:
+out_put:
put_disk(gd);
- out_free:
+out_free:
kfree(sdkp);
- out:
+out:
return error;
}
{
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);
- class_device_put(&sdkp->cdev);
+ kref_put(&sdkp->kref, scsi_disk_release);
mutex_unlock(&sd_ref_mutex);
return 0;
/**
* scsi_disk_release - Called to free the scsi_disk structure
- * @cdev: pointer to embedded class device
+ * @kref: pointer to embedded kref
*
* 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 class_device_put().
+ * and never do a direct kref_put().
**/
-static void scsi_disk_release(struct class_device *cdev)
+static void scsi_disk_release(struct kref *kref)
{
- struct scsi_disk *sdkp = to_scsi_disk(cdev);
+ struct scsi_disk *sdkp = to_scsi_disk(kref);
struct gendisk *disk = sdkp->disk;
spin_lock(&sd_index_lock);
if (!majors)
return -ENODEV;
- class_register(&sd_disk_class);
-
return scsi_register_driver(&sd_template.gendrv);
}
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_LICENSE("GPL");
+MODULE_AUTHOR("Eric Youngdale");
+MODULE_DESCRIPTION("SCSI disk (sd) driver");
+
module_init(init_sd);
module_exit(exit_sd);