* 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/sched.h>
#include <linux/mm.h>
#include <linux/bio.h>
#include <linux/genhd.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>
#include <scsi/scsi_ioctl.h>
#include <scsi/scsicam.h>
+#include "sd.h"
#include "scsi_logging.h"
-/*
- * More than enough for everybody ;) The huge number of majors
- * is a leftover from 16bit dev_t days, we don't really need that
- * much numberspace.
- */
-#define SD_MAJORS 16
-
-/*
- * This is limited by the naming scheme enforced in sd_probe,
- * add another character to it if you really need more disks.
- */
-#define SD_MAX_DISKS (((26 * 26) + 26 + 1) * 26)
-
-/*
- * Time out in seconds for disks and Magneto-opticals (which are slower).
- */
-#define SD_TIMEOUT (30 * HZ)
-#define SD_MOD_TIMEOUT (75 * HZ)
+MODULE_AUTHOR("Eric Youngdale");
+MODULE_DESCRIPTION("SCSI disk (sd) driver");
+MODULE_LICENSE("GPL");
-/*
- * Number of allowed retries
- */
-#define SD_MAX_RETRIES 5
-#define SD_PASSTHROUGH_RETRIES 1
-
-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 gendisk *disk;
- unsigned int openers; /* protected by BKL for now, yuck */
- sector_t capacity; /* size in 512-byte sectors */
- u32 index;
- u8 media_present;
- u8 write_prot;
- unsigned WCE : 1; /* state of disk WCE bit */
- unsigned RCD : 1; /* state of disk RCD bit, unused */
- unsigned DPOFUA : 1; /* state of disk DPOFUA bit */
-};
+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);
+MODULE_ALIAS_SCSI_DEVICE(TYPE_DISK);
+MODULE_ALIAS_SCSI_DEVICE(TYPE_MOD);
+MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC);
+
+static int sd_revalidate_disk(struct gendisk *);
+static int sd_probe(struct device *);
+static int sd_remove(struct device *);
+static void sd_shutdown(struct device *);
+static int sd_suspend(struct device *, pm_message_t state);
+static int sd_resume(struct device *);
+static void sd_rescan(struct device *);
+static int sd_done(struct scsi_cmnd *);
+static void sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer);
+static void scsi_disk_release(struct device *cdev);
+static void sd_print_sense_hdr(struct scsi_disk *, struct scsi_sense_hdr *);
+static void sd_print_result(struct scsi_disk *, int);
static DEFINE_IDR(sd_index_idr);
static DEFINE_SPINLOCK(sd_index_lock);
* object after last put) */
static DEFINE_MUTEX(sd_ref_mutex);
-static int sd_revalidate_disk(struct gendisk *disk);
-static void sd_rw_intr(struct scsi_cmnd * SCpnt);
+static const char *sd_cache_types[] = {
+ "write through", "none", "write back",
+ "write back, no read (daft)"
+};
-static int sd_probe(struct device *);
-static int sd_remove(struct device *);
-static void sd_shutdown(struct device *dev);
-static void sd_rescan(struct device *);
-static int sd_init_command(struct scsi_cmnd *);
-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 ssize_t
+sd_store_cache_type(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int i, ct = -1, rcd, wce, sp;
+ struct scsi_disk *sdkp = to_scsi_disk(dev);
+ 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))
+ sd_print_sense_hdr(sdkp, &sshdr);
+ return -EINVAL;
+ }
+ sd_revalidate_disk(sdkp->disk);
+ return count;
+}
+
+static ssize_t
+sd_store_manage_start_stop(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct scsi_disk *sdkp = to_scsi_disk(dev);
+ struct scsi_device *sdp = sdkp->device;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ sdp->manage_start_stop = simple_strtoul(buf, NULL, 10);
+
+ return count;
+}
+
+static ssize_t
+sd_store_allow_restart(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct scsi_disk *sdkp = to_scsi_disk(dev);
+ 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 device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_disk *sdkp = to_scsi_disk(dev);
+ int ct = sdkp->RCD + 2*sdkp->WCE;
+
+ return snprintf(buf, 40, "%s\n", sd_cache_types[ct]);
+}
+
+static ssize_t
+sd_show_fua(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct scsi_disk *sdkp = to_scsi_disk(dev);
+
+ return snprintf(buf, 20, "%u\n", sdkp->DPOFUA);
+}
+
+static ssize_t
+sd_show_manage_start_stop(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_disk *sdkp = to_scsi_disk(dev);
+ struct scsi_device *sdp = sdkp->device;
+
+ return snprintf(buf, 20, "%u\n", sdp->manage_start_stop);
+}
+
+static ssize_t
+sd_show_allow_restart(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_disk *sdkp = to_scsi_disk(dev);
+
+ return snprintf(buf, 40, "%d\n", sdkp->device->allow_restart);
+}
+
+static struct 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(manage_start_stop, S_IRUGO|S_IWUSR, sd_show_manage_start_stop,
+ sd_store_manage_start_stop),
+ __ATTR_NULL,
+};
+
+static struct class sd_disk_class = {
+ .name = "scsi_disk",
+ .owner = THIS_MODULE,
+ .dev_release = scsi_disk_release,
+ .dev_attrs = sd_disk_attrs,
+};
static struct scsi_driver sd_template = {
.owner = THIS_MODULE,
.name = "sd",
.probe = sd_probe,
.remove = sd_remove,
+ .suspend = sd_suspend,
+ .resume = sd_resume,
.shutdown = sd_shutdown,
},
.rescan = sd_rescan,
- .init_command = sd_init_command,
- .issue_flush = sd_issue_flush,
+ .done = sd_done,
};
/*
}
}
-#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);
-}
-
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);
+ get_device(&sdkp->dev);
else
sdkp = NULL;
}
struct scsi_device *sdev = sdkp->device;
mutex_lock(&sd_ref_mutex);
- kref_put(&sdkp->kref, scsi_disk_release);
+ put_device(&sdkp->dev);
scsi_device_put(sdev);
mutex_unlock(&sd_ref_mutex);
}
*
* Returns 1 if successful and 0 if error (or cannot be done now).
**/
-static int sd_init_command(struct scsi_cmnd * SCpnt)
+static int sd_prep_fn(struct request_queue *q, struct request *rq)
{
- struct scsi_device *sdp = SCpnt->device;
- struct request *rq = SCpnt->request;
+ struct scsi_cmnd *SCpnt;
+ struct scsi_device *sdp = q->queuedata;
struct gendisk *disk = rq->rq_disk;
sector_t block = rq->sector;
- unsigned int this_count = SCpnt->request_bufflen >> 9;
+ unsigned int this_count = rq->nr_sectors;
unsigned int timeout = sdp->timeout;
+ int ret;
- SCSI_LOG_HLQUEUE(1, printk("sd_init_command: disk=%s, block=%llu, "
- "count=%d\n", disk->disk_name,
- (unsigned long long)block, this_count));
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
+ ret = scsi_setup_blk_pc_cmnd(sdp, rq);
+ goto out;
+ } else if (rq->cmd_type != REQ_TYPE_FS) {
+ ret = BLKPREP_KILL;
+ goto out;
+ }
+ ret = scsi_setup_fs_cmnd(sdp, rq);
+ if (ret != BLKPREP_OK)
+ goto out;
+ SCpnt = rq->special;
+
+ /* from here on until we're complete, any goto out
+ * is used for a killable error condition */
+ ret = BLKPREP_KILL;
+
+ SCSI_LOG_HLQUEUE(1, scmd_printk(KERN_INFO, SCpnt,
+ "sd_init_command: block=%llu, "
+ "count=%d\n",
+ (unsigned long long)block,
+ this_count));
if (!sdp || !scsi_device_online(sdp) ||
block + rq->nr_sectors > get_capacity(disk)) {
- SCSI_LOG_HLQUEUE(2, printk("Finishing %ld sectors\n",
- rq->nr_sectors));
- SCSI_LOG_HLQUEUE(2, printk("Retry with 0x%p\n", SCpnt));
- return 0;
+ SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt,
+ "Finishing %ld sectors\n",
+ rq->nr_sectors));
+ SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt,
+ "Retry with 0x%p\n", SCpnt));
+ goto out;
}
if (sdp->changed) {
* the changed bit has been reset
*/
/* printk("SCSI disk has been changed. Prohibiting further I/O.\n"); */
- return 0;
+ goto out;
}
- SCSI_LOG_HLQUEUE(2, printk("%s : block=%llu\n",
- disk->disk_name, (unsigned long long)block));
+
+ /*
+ * Some devices (some sdcards for one) don't like it if the
+ * last sector gets read in a larger then 1 sector read.
+ */
+ if (unlikely(sdp->last_sector_bug &&
+ rq->nr_sectors > sdp->sector_size / 512 &&
+ block + this_count == get_capacity(disk)))
+ this_count -= sdp->sector_size / 512;
+
+ SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "block=%llu\n",
+ (unsigned long long)block));
/*
* If we have a 1K hardware sectorsize, prevent access to single
*/
if (sdp->sector_size == 1024) {
if ((block & 1) || (rq->nr_sectors & 1)) {
- printk(KERN_ERR "sd: Bad block number requested");
- return 0;
+ scmd_printk(KERN_ERR, SCpnt,
+ "Bad block number requested\n");
+ goto out;
} else {
block = block >> 1;
this_count = this_count >> 1;
}
if (sdp->sector_size == 2048) {
if ((block & 3) || (rq->nr_sectors & 3)) {
- printk(KERN_ERR "sd: Bad block number requested");
- return 0;
+ scmd_printk(KERN_ERR, SCpnt,
+ "Bad block number requested\n");
+ goto out;
} else {
block = block >> 2;
this_count = this_count >> 2;
}
if (sdp->sector_size == 4096) {
if ((block & 7) || (rq->nr_sectors & 7)) {
- printk(KERN_ERR "sd: Bad block number requested");
- return 0;
+ scmd_printk(KERN_ERR, SCpnt,
+ "Bad block number requested\n");
+ goto out;
} else {
block = block >> 3;
this_count = this_count >> 3;
}
if (rq_data_dir(rq) == WRITE) {
if (!sdp->writeable) {
- return 0;
+ goto out;
}
SCpnt->cmnd[0] = WRITE_6;
SCpnt->sc_data_direction = DMA_TO_DEVICE;
SCpnt->cmnd[0] = READ_6;
SCpnt->sc_data_direction = DMA_FROM_DEVICE;
} else {
- printk(KERN_ERR "sd: Unknown command %lx\n", rq->flags);
-/* overkill panic("Unknown sd command %lx\n", rq->flags); */
- return 0;
+ scmd_printk(KERN_ERR, SCpnt, "Unknown command %x\n", rq->cmd_flags);
+ goto out;
}
- SCSI_LOG_HLQUEUE(2, printk("%s : %s %d/%ld 512 byte blocks.\n",
- disk->disk_name, (rq_data_dir(rq) == WRITE) ?
- "writing" : "reading", this_count, rq->nr_sectors));
+ SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt,
+ "%s %d/%ld 512 byte blocks.\n",
+ (rq_data_dir(rq) == WRITE) ?
+ "writing" : "reading", this_count,
+ rq->nr_sectors));
SCpnt->cmnd[1] = 0;
* during operation and thus turned off
* use_10_for_rw.
*/
- printk(KERN_ERR "sd: FUA write on READ/WRITE(6) drive\n");
- return 0;
+ scmd_printk(KERN_ERR, SCpnt,
+ "FUA write on READ/WRITE(6) drive\n");
+ goto out;
}
SCpnt->cmnd[1] |= (unsigned char) ((block >> 16) & 0x1f);
SCpnt->cmnd[4] = (unsigned char) this_count;
SCpnt->cmnd[5] = 0;
}
- SCpnt->request_bufflen = SCpnt->bufflen =
- this_count * sdp->sector_size;
+ SCpnt->sdb.length = this_count * sdp->sector_size;
/*
* We shouldn't disconnect in the middle of a sector, so with a dumb
SCpnt->timeout_per_command = timeout;
/*
- * This is the completion routine we use. This is matched in terms
- * of capability to this function.
- */
- SCpnt->done = sd_rw_intr;
-
- /*
* This indicates that the command is ready from our end to be
* queued.
*/
- return 1;
+ ret = BLKPREP_OK;
+ out:
+ return scsi_prep_return(q, rq, ret);
}
/**
return -ENXIO;
- SCSI_LOG_HLQUEUE(3, printk("sd_open: disk=%s\n", disk->disk_name));
+ SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_open\n"));
sdev = sdkp->device;
struct scsi_disk *sdkp = scsi_disk(disk);
struct scsi_device *sdev = sdkp->device;
- SCSI_LOG_HLQUEUE(3, printk("sd_release: disk=%s\n", disk->disk_name));
+ SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_release\n"));
if (!--sdkp->openers && sdev->removable) {
if (scsi_block_when_processing_errors(sdev))
* success as well). Returns a negated errno value in case of error.
*
* Note: most ioctls are forward onto the block subsystem or further
- * down in the scsi subsytem.
+ * down in the scsi subsystem.
**/
static int sd_ioctl(struct inode * inode, struct file * filp,
unsigned int cmd, unsigned long arg)
case SCSI_IOCTL_GET_BUS_NUMBER:
return scsi_ioctl(sdp, cmd, p);
default:
- error = scsi_cmd_ioctl(filp, disk, cmd, p);
+ error = scsi_cmd_ioctl(filp, disk->queue, disk, cmd, p);
if (error != -ENOTTY)
return error;
}
{
struct scsi_disk *sdkp = scsi_disk(disk);
struct scsi_device *sdp = sdkp->device;
+ struct scsi_sense_hdr *sshdr = NULL;
int retval;
- SCSI_LOG_HLQUEUE(3, printk("sd_media_changed: disk=%s\n",
- disk->disk_name));
+ SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_media_changed\n"));
if (!sdp->removable)
return 0;
* can deal with it then. It is only because of unrecoverable errors
* that we would ever take a device offline in the first place.
*/
- if (!scsi_device_online(sdp))
- goto not_present;
+ if (!scsi_device_online(sdp)) {
+ set_media_not_present(sdkp);
+ retval = 1;
+ goto out;
+ }
/*
* Using TEST_UNIT_READY enables differentiation between drive with
* sd_revalidate() is called.
*/
retval = -ENODEV;
- if (scsi_block_when_processing_errors(sdp))
- retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES);
+
+ if (scsi_block_when_processing_errors(sdp)) {
+ sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL);
+ retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES,
+ sshdr);
+ }
/*
* Unable to test, unit probably not ready. This usually
* and we will figure it out later once the drive is
* available again.
*/
- if (retval)
- goto not_present;
+ if (retval || (scsi_sense_valid(sshdr) &&
+ /* 0x3a is medium not present */
+ sshdr->asc == 0x3a)) {
+ set_media_not_present(sdkp);
+ retval = 1;
+ goto out;
+ }
/*
* For removable scsi disk we have to recognise the presence
retval = sdp->changed;
sdp->changed = 0;
-
+out:
+ if (retval != sdkp->previous_state)
+ sdev_evt_send_simple(sdp, SDEV_EVT_MEDIA_CHANGE, GFP_KERNEL);
+ sdkp->previous_state = retval;
+ kfree(sshdr);
return retval;
-
-not_present:
- set_media_not_present(sdkp);
- return 1;
}
-static int sd_sync_cache(struct scsi_device *sdp)
+static int sd_sync_cache(struct scsi_disk *sdkp)
{
int retries, res;
+ struct scsi_device *sdp = sdkp->device;
struct scsi_sense_hdr sshdr;
if (!scsi_device_online(sdp))
break;
}
- if (res) { printk(KERN_WARNING "FAILED\n status = %x, message = %02x, "
- "host = %d, driver = %02x\n ",
- status_byte(res), msg_byte(res),
- host_byte(res), driver_byte(res));
- if (driver_byte(res) & DRIVER_SENSE)
- scsi_print_sense_hdr("sd", &sshdr);
+ if (res) {
+ sd_print_result(sdkp, res);
+ if (driver_byte(res) & DRIVER_SENSE)
+ sd_print_sense_hdr(sdkp, &sshdr);
}
- return res;
-}
-
-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 = scsi_disk_get_from_dev(dev);
-
- if (!sdkp)
- return -ENODEV;
-
- if (sdkp->WCE)
- ret = sd_sync_cache(sdp);
- scsi_disk_put(sdkp);
- return ret;
+ if (res)
+ return -EIO;
+ return 0;
}
-static void sd_prepare_flush(request_queue_t *q, struct request *rq)
+static void sd_prepare_flush(struct request_queue *q, struct request *rq)
{
- memset(rq->cmd, 0, sizeof(rq->cmd));
- rq->flags |= REQ_BLOCK_PC;
+ rq->cmd_type = REQ_TYPE_BLOCK_PC;
rq->timeout = SD_TIMEOUT;
rq->cmd[0] = SYNCHRONIZE_CACHE;
rq->cmd_len = 10;
*/
static long sd_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- struct block_device *bdev = file->f_dentry->d_inode->i_bdev;
+ struct block_device *bdev = file->f_path.dentry->d_inode->i_bdev;
struct gendisk *disk = bdev->bd_disk;
struct scsi_device *sdev = scsi_disk(disk)->device;
};
/**
- * sd_rw_intr - bottom half handler: called when the lower level
+ * sd_done - bottom half handler: called when the lower level
* driver has completed (successfully or otherwise) a scsi command.
* @SCpnt: mid-level's per command structure.
*
* Note: potentially run from within an ISR. Must not block.
**/
-static void sd_rw_intr(struct scsi_cmnd * SCpnt)
+static int sd_done(struct scsi_cmnd *SCpnt)
{
int result = SCpnt->result;
- 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;
+ unsigned int xfer_size = scsi_bufflen(SCpnt);
+ unsigned int good_bytes = result ? 0 : xfer_size;
+ u64 start_lba = SCpnt->request->sector;
+ u64 end_lba = SCpnt->request->sector + (xfer_size / 512);
+ u64 bad_lba;
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));
+ SCSI_LOG_HLCOMPLETE(1, scsi_print_result(SCpnt));
if (sense_valid) {
- SCSI_LOG_HLCOMPLETE(1, printk("sd_rw_intr: sb[respc,sk,asc,"
- "ascq]=%x,%x,%x,%x\n", sshdr.response_code,
- sshdr.sense_key, sshdr.asc, sshdr.ascq));
+ SCSI_LOG_HLCOMPLETE(1, scmd_printk(KERN_INFO, SCpnt,
+ "sd_done: sb[respc,sk,asc,"
+ "ascq]=%x,%x,%x,%x\n",
+ sshdr.response_code,
+ sshdr.sense_key, sshdr.asc,
+ sshdr.ascq));
}
#endif
- /*
- 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;
- }
+ if (driver_byte(result) != DRIVER_SENSE &&
+ (!sense_valid || sense_deferred))
+ goto out;
- 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;
+ 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;
+ if (SCpnt->device->sector_size < 512) {
+ /* only legitimate sector_size here is 256 */
+ start_lba <<= 1;
+ end_lba <<= 1;
+ } else {
+ /* be careful ... don't want any overflows */
+ u64 factor = SCpnt->device->sector_size / 512;
+ do_div(start_lba, factor);
+ do_div(end_lba, factor);
+ }
- 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.
+ if (bad_lba < start_lba || bad_lba >= end_lba)
+ /* the bad lba was reported incorrectly, we have
+ * no idea where the error is
*/
- scsi_print_sense("sd", SCpnt);
- SCpnt->result = 0;
- memset(SCpnt->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
- good_bytes = this_count;
- 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;
+ goto out;
- default:
- 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;
}
- /*
- * 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);
+ out:
+ return good_bytes;
}
static int media_not_present(struct scsi_disk *sdkp,
* spinup disk - called only in sd_revalidate_disk()
*/
static void
-sd_spinup_disk(struct scsi_disk *sdkp, char *diskname)
+sd_spinup_disk(struct scsi_disk *sdkp)
{
unsigned char cmd[10];
unsigned long spintime_expire = 0;
&sshdr, SD_TIMEOUT,
SD_MAX_RETRIES);
+ /*
+ * If the drive has indicated to us that it
+ * doesn't have any media in it, don't bother
+ * with any more polling.
+ */
+ if (media_not_present(sdkp, &sshdr))
+ return;
+
if (the_result)
sense_valid = scsi_sense_valid(&sshdr);
retries++;
((driver_byte(the_result) & DRIVER_SENSE) &&
sense_valid && sshdr.sense_key == UNIT_ATTENTION)));
- /*
- * If the drive has indicated to us that it doesn't have
- * any media in it, don't bother with any of the rest of
- * this crap.
- */
- if (media_not_present(sdkp, &sshdr))
- return;
-
if ((driver_byte(the_result) & DRIVER_SENSE) == 0) {
/* no sense, TUR either succeeded or failed
* with a status error */
- if(!spintime && !scsi_status_is_good(the_result))
- printk(KERN_NOTICE "%s: Unit Not Ready, "
- "error = 0x%x\n", diskname, the_result);
+ if(!spintime && !scsi_status_is_good(the_result)) {
+ sd_printk(KERN_NOTICE, sdkp, "Unit Not Ready\n");
+ sd_print_result(sdkp, the_result);
+ }
break;
}
*/
} else if (sense_valid && sshdr.sense_key == NOT_READY) {
if (!spintime) {
- printk(KERN_NOTICE "%s: Spinning up disk...",
- diskname);
+ sd_printk(KERN_NOTICE, sdkp, "Spinning up disk...");
cmd[0] = START_STOP;
cmd[1] = 1; /* Return immediately */
memset((void *) &cmd[2], 0, 8);
cmd[4] = 1; /* Start spin cycle */
+ if (sdkp->device->start_stop_pwr_cond)
+ cmd[4] |= 1 << 4;
scsi_execute_req(sdkp->device, cmd, DMA_NONE,
NULL, 0, &sshdr,
SD_TIMEOUT, SD_MAX_RETRIES);
/* we don't understand the sense code, so it's
* probably pointless to loop */
if(!spintime) {
- printk(KERN_NOTICE "%s: Unit Not Ready, "
- "sense:\n", diskname);
- scsi_print_sense_hdr("", &sshdr);
+ sd_printk(KERN_NOTICE, sdkp, "Unit Not Ready\n");
+ sd_print_sense_hdr(sdkp, &sshdr);
}
break;
}
* read disk capacity
*/
static void
-sd_read_capacity(struct scsi_disk *sdkp, char *diskname,
- unsigned char *buffer)
+sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer)
{
unsigned char cmd[16];
int the_result, retries;
} while (the_result && retries);
if (the_result && !longrc) {
- printk(KERN_NOTICE "%s : READ CAPACITY failed.\n"
- "%s : status=%x, message=%02x, host=%d, driver=%02x \n",
- diskname, diskname,
- status_byte(the_result),
- msg_byte(the_result),
- host_byte(the_result),
- driver_byte(the_result));
-
+ sd_printk(KERN_NOTICE, sdkp, "READ CAPACITY failed\n");
+ sd_print_result(sdkp, the_result);
if (driver_byte(the_result) & DRIVER_SENSE)
- scsi_print_sense_hdr("sd", &sshdr);
+ sd_print_sense_hdr(sdkp, &sshdr);
else
- printk("%s : sense not available. \n", diskname);
+ sd_printk(KERN_NOTICE, sdkp, "Sense not available.\n");
/* Set dirty bit for removable devices if not ready -
* sometimes drives will not report this properly. */
/* Either no media are present but the drive didn't tell us,
or they are present but the read capacity command fails */
/* sdkp->media_present = 0; -- not always correct */
- sdkp->capacity = 0x200000; /* 1 GB - random */
+ sdkp->capacity = 0; /* unknown mapped to zero - as usual */
return;
} else if (the_result && longrc) {
/* READ CAPACITY(16) has been failed */
- printk(KERN_NOTICE "%s : READ CAPACITY(16) failed.\n"
- "%s : status=%x, message=%02x, host=%d, driver=%02x \n",
- diskname, diskname,
- status_byte(the_result),
- msg_byte(the_result),
- host_byte(the_result),
- driver_byte(the_result));
- printk(KERN_NOTICE "%s : use 0xffffffff as device size\n",
- diskname);
-
+ sd_printk(KERN_NOTICE, sdkp, "READ CAPACITY(16) failed\n");
+ sd_print_result(sdkp, the_result);
+ sd_printk(KERN_NOTICE, sdkp, "Use 0xffffffff as device size\n");
+
sdkp->capacity = 1 + (sector_t) 0xffffffff;
goto got_data;
}
if (buffer[0] == 0xff && buffer[1] == 0xff &&
buffer[2] == 0xff && buffer[3] == 0xff) {
if(sizeof(sdkp->capacity) > 4) {
- printk(KERN_NOTICE "%s : very big device. try to use"
- " READ CAPACITY(16).\n", diskname);
+ sd_printk(KERN_NOTICE, sdkp, "Very big device. "
+ "Trying to use READ CAPACITY(16).\n");
longrc = 1;
goto repeat;
}
- printk(KERN_ERR "%s: too big for this kernel. Use a "
- "kernel compiled with support for large block "
- "devices.\n", diskname);
+ sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use "
+ "a kernel compiled with support for large "
+ "block devices.\n");
sdkp->capacity = 0;
goto got_data;
}
/* Some devices return the total number of sectors, not the
* highest sector number. Make the necessary adjustment. */
- if (sdp->fix_capacity)
+ if (sdp->fix_capacity) {
--sdkp->capacity;
+ /* Some devices have version which report the correct sizes
+ * and others which do not. We guess size according to a heuristic
+ * and err on the side of lowering the capacity. */
+ } else {
+ if (sdp->guess_capacity)
+ if (sdkp->capacity & 0x01) /* odd sizes are odd */
+ --sdkp->capacity;
+ }
+
got_data:
if (sector_size == 0) {
sector_size = 512;
- printk(KERN_NOTICE "%s : sector size 0 reported, "
- "assuming 512.\n", diskname);
+ sd_printk(KERN_NOTICE, sdkp, "Sector size 0 reported, "
+ "assuming 512.\n");
}
if (sector_size != 512 &&
sector_size != 2048 &&
sector_size != 4096 &&
sector_size != 256) {
- printk(KERN_NOTICE "%s : unsupported sector size "
- "%d.\n", diskname, sector_size);
+ sd_printk(KERN_NOTICE, sdkp, "Unsupported sector size %d.\n",
+ sector_size);
/*
* The user might want to re-format the drive with
* a supported sectorsize. Once this happens, it
*/
int hard_sector = sector_size;
sector_t sz = (sdkp->capacity/2) * (hard_sector/256);
- request_queue_t *queue = sdp->request_queue;
+ struct request_queue *queue = sdp->request_queue;
sector_t mb = sz;
blk_queue_hardsect_size(queue, hard_sector);
mb -= sz - 974;
sector_div(mb, 1950);
- printk(KERN_NOTICE "SCSI device %s: "
- "%llu %d-byte hdwr sectors (%llu MB)\n",
- diskname, (unsigned long long)sdkp->capacity,
- hard_sector, (unsigned long long)mb);
+ sd_printk(KERN_NOTICE, sdkp,
+ "%llu %d-byte hardware sectors (%llu MB)\n",
+ (unsigned long long)sdkp->capacity,
+ hard_sector, (unsigned long long)mb);
}
/* Rescale capacity to 512-byte units */
/*
* 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,
- unsigned char *buffer)
+sd_read_write_protect_flag(struct scsi_disk *sdkp, unsigned char *buffer)
{
int res;
struct scsi_device *sdp = sdkp->device;
set_disk_ro(sdkp->disk, 0);
if (sdp->skip_ms_page_3f) {
- printk(KERN_NOTICE "%s: assuming Write Enabled\n", diskname);
+ sd_printk(KERN_NOTICE, sdkp, "Assuming Write Enabled\n");
return;
}
}
if (!scsi_status_is_good(res)) {
- printk(KERN_WARNING
- "%s: test WP failed, assume Write Enabled\n", diskname);
+ sd_printk(KERN_WARNING, sdkp,
+ "Test WP failed, assume Write Enabled\n");
} else {
sdkp->write_prot = ((data.device_specific & 0x80) != 0);
set_disk_ro(sdkp->disk, sdkp->write_prot);
- printk(KERN_NOTICE "%s: Write Protect is %s\n", diskname,
- sdkp->write_prot ? "on" : "off");
- printk(KERN_DEBUG "%s: Mode Sense: %02x %02x %02x %02x\n",
- diskname, buffer[0], buffer[1], buffer[2], buffer[3]);
+ sd_printk(KERN_NOTICE, sdkp, "Write Protect is %s\n",
+ sdkp->write_prot ? "on" : "off");
+ sd_printk(KERN_DEBUG, sdkp,
+ "Mode Sense: %02x %02x %02x %02x\n",
+ buffer[0], buffer[1], buffer[2], buffer[3]);
}
}
/*
* 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,
- unsigned char *buffer)
+sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
{
int len = 0, res;
struct scsi_device *sdp = sdkp->device;
if (!scsi_status_is_good(res))
goto bad_sense;
+ if (!data.header_length) {
+ modepage = 6;
+ sd_printk(KERN_ERR, sdkp, "Missing header in MODE_SENSE response\n");
+ }
+
/* that went OK, now ask for the proper length */
len = data.length;
/* 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) {
+ sd_printk(KERN_ERR, sdkp, "Malformed MODE SENSE response\n");
+ goto defaults;
+ }
+
if ((buffer[offset] & 0x3f) != modepage) {
- printk(KERN_ERR "%s: got wrong page\n", diskname);
+ sd_printk(KERN_ERR, sdkp, "Got wrong page\n");
goto defaults;
}
sdkp->DPOFUA = (data.device_specific & 0x10) != 0;
if (sdkp->DPOFUA && !sdkp->device->use_10_for_rw) {
- printk(KERN_NOTICE "SCSI device %s: uses "
- "READ/WRITE(6), disabling FUA\n", diskname);
+ sd_printk(KERN_NOTICE, sdkp,
+ "Uses READ/WRITE(6), disabling FUA\n");
sdkp->DPOFUA = 0;
}
- ct = sdkp->RCD + 2*sdkp->WCE;
-
- printk(KERN_NOTICE "SCSI device %s: drive cache: %s%s\n",
- diskname, types[ct],
- sdkp->DPOFUA ? " w/ FUA" : "");
+ sd_printk(KERN_NOTICE, sdkp,
+ "Write cache: %s, read cache: %s, %s\n",
+ sdkp->WCE ? "enabled" : "disabled",
+ sdkp->RCD ? "disabled" : "enabled",
+ sdkp->DPOFUA ? "supports DPO and FUA"
+ : "doesn't support DPO or FUA");
return;
}
if (scsi_sense_valid(&sshdr) &&
sshdr.sense_key == ILLEGAL_REQUEST &&
sshdr.asc == 0x24 && sshdr.ascq == 0x0)
- printk(KERN_NOTICE "%s: cache data unavailable\n",
- diskname); /* Invalid field in CDB */
+ /* Invalid field in CDB */
+ sd_printk(KERN_NOTICE, sdkp, "Cache data unavailable\n");
else
- printk(KERN_ERR "%s: asking for cache data failed\n",
- diskname);
+ sd_printk(KERN_ERR, sdkp, "Asking for cache data failed\n");
defaults:
- printk(KERN_ERR "%s: assuming drive cache: write through\n",
- diskname);
+ sd_printk(KERN_ERR, sdkp, "Assuming drive cache: write through\n");
sdkp->WCE = 0;
sdkp->RCD = 0;
+ sdkp->DPOFUA = 0;
}
/**
unsigned char *buffer;
unsigned ordered;
- SCSI_LOG_HLQUEUE(3, printk("sd_revalidate_disk: disk=%s\n", disk->disk_name));
+ SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp,
+ "sd_revalidate_disk\n"));
/*
* If the device is offline, don't try and read capacity or any
if (!scsi_device_online(sdp))
goto out;
- buffer = kmalloc(512, GFP_KERNEL | __GFP_DMA);
+ buffer = kmalloc(SD_BUF_SIZE, GFP_KERNEL);
if (!buffer) {
- printk(KERN_WARNING "(sd_revalidate_disk:) Memory allocation "
- "failure.\n");
+ sd_printk(KERN_WARNING, sdkp, "sd_revalidate_disk: Memory "
+ "allocation failure.\n");
goto out;
}
sdkp->WCE = 0;
sdkp->RCD = 0;
- sd_spinup_disk(sdkp, disk->disk_name);
+ sd_spinup_disk(sdkp);
/*
* Without media there is no reason to ask; moreover, some devices
* react badly if we do.
*/
if (sdkp->media_present) {
- sd_read_capacity(sdkp, disk->disk_name, buffer);
- sd_read_write_protect_flag(sdkp, disk->disk_name, buffer);
- sd_read_cache_type(sdkp, disk->disk_name, buffer);
+ sd_read_capacity(sdkp, buffer);
+ sd_read_write_protect_flag(sdkp, buffer);
+ sd_read_cache_type(sdkp, buffer);
}
/*
"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;
if (error)
goto out_put;
- get_device(&sdp->sdev_gendev);
sdkp->device = sdp;
sdkp->driver = &sd_template;
sdkp->disk = gd;
sdkp->index = index;
sdkp->openers = 0;
+ sdkp->previous_state = 1;
if (!sdp->timeout) {
if (sdp->type != TYPE_MOD)
sdp->timeout = SD_MOD_TIMEOUT;
}
+ device_initialize(&sdkp->dev);
+ sdkp->dev.parent = &sdp->sdev_gendev;
+ sdkp->dev.class = &sd_disk_class;
+ strncpy(sdkp->dev.bus_id, sdp->sdev_gendev.bus_id, BUS_ID_SIZE);
+
+ if (device_add(&sdkp->dev))
+ goto out_put;
+
+ get_device(&sdp->sdev_gendev);
+
gd->major = sd_major((index & 0xf0) >> 4);
gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00);
gd->minors = 16;
'a' + m1, 'a' + m2, 'a' + m3);
}
- strcpy(gd->devfs_name, sdp->devfs_name);
-
gd->private_data = &sdkp->driver;
gd->queue = sdkp->device->request_queue;
sd_revalidate_disk(gd);
+ blk_queue_prep_rq(sdp->request_queue, sd_prep_fn);
+
gd->driverfs_dev = &sdp->sdev_gendev;
gd->flags = GENHD_FL_DRIVERFS;
if (sdp->removable)
dev_set_drvdata(dev, sdkp);
add_disk(gd);
- sdev_printk(KERN_NOTICE, sdp, "Attached scsi %sdisk %s\n",
- sdp->removable ? "removable " : "", gd->disk_name);
+ sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
+ sdp->removable ? "removable " : "");
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);
+ device_del(&sdkp->dev);
del_gendisk(sdkp->disk);
sd_shutdown(dev);
mutex_lock(&sd_ref_mutex);
dev_set_drvdata(dev, NULL);
- kref_put(&sdkp->kref, scsi_disk_release);
+ put_device(&sdkp->dev);
mutex_unlock(&sd_ref_mutex);
return 0;
/**
* scsi_disk_release - Called to free the scsi_disk structure
- * @kref: pointer to embedded kref
+ * @dev: 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 put_device.
**/
-static void scsi_disk_release(struct kref *kref)
+static void scsi_disk_release(struct device *dev)
{
- struct scsi_disk *sdkp = to_scsi_disk(kref);
+ struct scsi_disk *sdkp = to_scsi_disk(dev);
struct gendisk *disk = sdkp->disk;
spin_lock(&sd_index_lock);
kfree(sdkp);
}
+static int sd_start_stop_device(struct scsi_disk *sdkp, int start)
+{
+ unsigned char cmd[6] = { START_STOP }; /* START_VALID */
+ struct scsi_sense_hdr sshdr;
+ struct scsi_device *sdp = sdkp->device;
+ int res;
+
+ if (start)
+ cmd[4] |= 1; /* START */
+
+ if (sdp->start_stop_pwr_cond)
+ cmd[4] |= start ? 1 << 4 : 3 << 4; /* Active or Standby */
+
+ if (!scsi_device_online(sdp))
+ return -ENODEV;
+
+ res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
+ SD_TIMEOUT, SD_MAX_RETRIES);
+ if (res) {
+ sd_printk(KERN_WARNING, sdkp, "START_STOP FAILED\n");
+ sd_print_result(sdkp, res);
+ if (driver_byte(res) & DRIVER_SENSE)
+ sd_print_sense_hdr(sdkp, &sshdr);
+ }
+
+ return res;
+}
+
/*
* Send a SYNCHRONIZE CACHE instruction down to the device through
* the normal SCSI command structure. Wait for the command to
*/
static void sd_shutdown(struct device *dev)
{
- struct scsi_device *sdp = to_scsi_device(dev);
struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev);
if (!sdkp)
return; /* this can happen */
if (sdkp->WCE) {
- printk(KERN_NOTICE "Synchronizing SCSI cache for disk %s: \n",
- sdkp->disk->disk_name);
- sd_sync_cache(sdp);
+ sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n");
+ sd_sync_cache(sdkp);
}
+
+ if (system_state != SYSTEM_RESTART && sdkp->device->manage_start_stop) {
+ sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n");
+ sd_start_stop_device(sdkp, 0);
+ }
+
+ scsi_disk_put(sdkp);
+}
+
+static int sd_suspend(struct device *dev, pm_message_t mesg)
+{
+ struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev);
+ int ret = 0;
+
+ if (!sdkp)
+ return 0; /* this can happen */
+
+ if (sdkp->WCE) {
+ sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n");
+ ret = sd_sync_cache(sdkp);
+ if (ret)
+ goto done;
+ }
+
+ if ((mesg.event & PM_EVENT_SLEEP) && sdkp->device->manage_start_stop) {
+ sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n");
+ ret = sd_start_stop_device(sdkp, 0);
+ }
+
+done:
scsi_disk_put(sdkp);
+ return ret;
+}
+
+static int sd_resume(struct device *dev)
+{
+ struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev);
+ int ret = 0;
+
+ if (!sdkp->device->manage_start_stop)
+ goto done;
+
+ sd_printk(KERN_NOTICE, sdkp, "Starting disk\n");
+ ret = sd_start_stop_device(sdkp, 1);
+
+done:
+ scsi_disk_put(sdkp);
+ return ret;
}
/**
**/
static int __init init_sd(void)
{
- int majors = 0, i;
+ int majors = 0, i, err;
SCSI_LOG_HLQUEUE(3, printk("init_sd: sd driver entry point\n"));
if (!majors)
return -ENODEV;
- return scsi_register_driver(&sd_template.gendrv);
+ err = class_register(&sd_disk_class);
+ if (err)
+ goto err_out;
+
+ err = scsi_register_driver(&sd_template.gendrv);
+ if (err)
+ goto err_out_class;
+
+ return 0;
+
+err_out_class:
+ class_unregister(&sd_disk_class);
+err_out:
+ for (i = 0; i < SD_MAJORS; i++)
+ unregister_blkdev(sd_major(i), "sd");
+ return err;
}
/**
SCSI_LOG_HLQUEUE(3, printk("exit_sd: exiting sd driver\n"));
scsi_unregister_driver(&sd_template.gendrv);
+ class_unregister(&sd_disk_class);
+
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");
-
module_init(init_sd);
module_exit(exit_sd);
+
+static void sd_print_sense_hdr(struct scsi_disk *sdkp,
+ struct scsi_sense_hdr *sshdr)
+{
+ sd_printk(KERN_INFO, sdkp, "");
+ scsi_show_sense_hdr(sshdr);
+ sd_printk(KERN_INFO, sdkp, "");
+ scsi_show_extd_sense(sshdr->asc, sshdr->ascq);
+}
+
+static void sd_print_result(struct scsi_disk *sdkp, int result)
+{
+ sd_printk(KERN_INFO, sdkp, "");
+ scsi_show_result(result);
+}
+