#include <linux/blkdev.h>
#include <linux/genhd.h>
#include <linux/completion.h>
+#include <scsi/scsi.h>
+#include <scsi/sg.h>
+#include <scsi/scsi_ioctl.h>
+#include <linux/cdrom.h>
#define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
#define DRIVER_NAME "HP CISS Driver (v 3.6.14)"
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSD, 0x103C, 0x3213},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSD, 0x103C, 0x3214},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSD, 0x103C, 0x3215},
- {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSC, 0x103C, 0x3233},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSC, 0x103C, 0x3237},
{PCI_VENDOR_ID_HP, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0},
{0,}
{0x3213103C, "Smart Array E200i", &SA5_access, 120},
{0x3214103C, "Smart Array E200i", &SA5_access, 120},
{0x3215103C, "Smart Array E200i", &SA5_access, 120},
- {0x3233103C, "Smart Array E500", &SA5_access, 512},
+ {0x3237103C, "Smart Array E500", &SA5_access, 512},
{0xFFFF103C, "Unknown Smart Array", &SA5_access, 120},
};
unsigned int cmd, unsigned long arg);
static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo);
-static int revalidate_allvol(ctlr_info_t *host);
static int cciss_revalidate(struct gendisk *disk);
static int rebuild_lun_table(ctlr_info_t *h, struct gendisk *del_disk);
static int deregister_disk(struct gendisk *disk, drive_info_struct *drv,
#include "cciss_scsi.c" /* For SCSI tape support */
+#define RAID_UNKNOWN 6
+
#ifdef CONFIG_PROC_FS
/*
*/
#define ENG_GIG 1000000000
#define ENG_GIG_FACTOR (ENG_GIG/512)
-#define RAID_UNKNOWN 6
static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG",
"UNKNOWN"
};
"Firmware Version: %c%c%c%c\n"
"IRQ: %d\n"
"Logical drives: %d\n"
+ "Max sectors: %d\n"
"Current Q depth: %d\n"
"Current # commands on controller: %d\n"
"Max Q depth since init: %d\n"
(unsigned long)h->board_id,
h->firm_ver[0], h->firm_ver[1], h->firm_ver[2],
h->firm_ver[3], (unsigned int)h->intr[SIMPLE_MODE_INT],
- h->num_luns, h->Qdepth, h->commands_outstanding,
+ h->num_luns,
+ h->cciss_max_sectors,
+ h->Qdepth, h->commands_outstanding,
h->maxQsinceinit, h->max_outstanding, h->maxSG);
pos += size;
* but I'm already using way to many device nodes to claim another one
* for "raw controller".
*/
- if (drv->nr_blocks == 0) {
+ if (drv->heads == 0) {
if (iminor(inode) != 0) { /* not node 0? */
/* if not node 0 make sure it is a partition = 0 */
if (iminor(inode) & 0x0f) {
{
int ret;
lock_kernel();
- ret = cciss_ioctl(f->f_dentry->d_inode, f, cmd, arg);
+ ret = cciss_ioctl(f->f_path.dentry->d_inode, f, cmd, arg);
unlock_kernel();
return ret;
}
}
case CCISS_REVALIDVOLS:
- if (bdev != bdev->bd_contains || drv != host->drv)
- return -ENXIO;
- return revalidate_allvol(host);
+ return rebuild_lun_table(host, NULL);
case CCISS_GETLUNINFO:{
LogvolInfo_struct luninfo;
status = -ENOMEM;
goto cleanup1;
}
- buff_size = (int *)kmalloc(MAXSGENTRIES * sizeof(int),
+ buff_size = kmalloc(MAXSGENTRIES * sizeof(int),
GFP_KERNEL);
if (!buff_size) {
status = -ENOMEM;
kfree(ioc);
return status;
}
+
+ /* scsi_cmd_ioctl handles these, below, though some are not */
+ /* very meaningful for cciss. SG_IO is the main one people want. */
+
+ case SG_GET_VERSION_NUM:
+ case SG_SET_TIMEOUT:
+ case SG_GET_TIMEOUT:
+ case SG_GET_RESERVED_SIZE:
+ case SG_SET_RESERVED_SIZE:
+ case SG_EMULATED_HOST:
+ case SG_IO:
+ case SCSI_IOCTL_SEND_COMMAND:
+ return scsi_cmd_ioctl(filep, disk, cmd, argp);
+
+ /* scsi_cmd_ioctl would normally handle these, below, but */
+ /* they aren't a good fit for cciss, as CD-ROMs are */
+ /* not supported, and we don't have any bus/target/lun */
+ /* which we present to the kernel. */
+
+ case CDROM_SEND_PACKET:
+ case CDROMCLOSETRAY:
+ case CDROMEJECT:
+ case SCSI_IOCTL_GET_IDLUN:
+ case SCSI_IOCTL_GET_BUS_NUMBER:
default:
return -ENOTTY;
}
}
-/*
- * revalidate_allvol is for online array config utilities. After a
- * utility reconfigures the drives in the array, it can use this function
- * (through an ioctl) to make the driver zap any previous disk structs for
- * that controller and get new ones.
- *
- * Right now I'm using the getgeometry() function to do this, but this
- * function should probably be finer grained and allow you to revalidate one
- * particular logical volume (instead of all of them on a particular
- * controller).
- */
-static int revalidate_allvol(ctlr_info_t *host)
-{
- int ctlr = host->ctlr, i;
- unsigned long flags;
-
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
- if (host->usage_count > 1) {
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
- printk(KERN_WARNING "cciss: Device busy for volume"
- " revalidation (usage=%d)\n", host->usage_count);
- return -EBUSY;
- }
- host->usage_count++;
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
-
- for (i = 0; i < NWD; i++) {
- struct gendisk *disk = host->gendisk[i];
- if (disk) {
- request_queue_t *q = disk->queue;
-
- if (disk->flags & GENHD_FL_UP)
- del_gendisk(disk);
- if (q)
- blk_cleanup_queue(q);
- }
- }
-
- /*
- * Set the partition and block size structures for all volumes
- * on this controller to zero. We will reread all of this data
- */
- memset(host->drv, 0, sizeof(drive_info_struct)
- * CISS_MAX_LUN);
- /*
- * Tell the array controller not to give us any interrupts while
- * we check the new geometry. Then turn interrupts back on when
- * we're done.
- */
- host->access.set_intr_mask(host, CCISS_INTR_OFF);
- cciss_getgeometry(ctlr);
- host->access.set_intr_mask(host, CCISS_INTR_ON);
-
- /* Loop through each real device */
- for (i = 0; i < NWD; i++) {
- struct gendisk *disk = host->gendisk[i];
- drive_info_struct *drv = &(host->drv[i]);
- /* we must register the controller even if no disks exist */
- /* this is for the online array utilities */
- if (!drv->heads && i)
- continue;
- blk_queue_hardsect_size(drv->queue, drv->block_size);
- set_capacity(disk, drv->nr_blocks);
- add_disk(disk);
- }
- host->usage_count--;
- return 0;
-}
-
static inline void complete_buffers(struct bio *bio, int status)
{
while (bio) {
pci_unmap_page(h->pdev, temp64.val, cmd->SG[i].Len, ddir);
}
- complete_buffers(rq->bio, rq->errors);
+ complete_buffers(rq->bio, (rq->errors == 0));
if (blk_fs_request(rq)) {
const int rw = rq_data_dir(rq);
add_disk_randomness(rq->rq_disk);
spin_lock_irqsave(&h->lock, flags);
- end_that_request_last(rq, rq->errors);
+ end_that_request_last(rq, (rq->errors == 0));
cmd_free(h, cmd, 1);
cciss_check_queues(h);
spin_unlock_irqrestore(&h->lock, flags);
if (inq_buff == NULL)
goto mem_msg;
+ /* testing to see if 16-byte CDBs are already being used */
+ if (h->cciss_read == CCISS_READ_16) {
+ cciss_read_capacity_16(h->ctlr, drv_index, 1,
+ &total_size, &block_size);
+ goto geo_inq;
+ }
+
cciss_read_capacity(ctlr, drv_index, 1,
&total_size, &block_size);
- /* total size = last LBA + 1 */
- /* FFFFFFFF + 1 = 0, cannot have a logical volume of size 0 */
- /* so we assume this volume this must be >2TB in size */
- if (total_size == (__u32) 0) {
+ /* if read_capacity returns all F's this volume is >2TB in size */
+ /* so we switch to 16-byte CDB's for all read/write ops */
+ if (total_size == 0xFFFFFFFFULL) {
cciss_read_capacity_16(ctlr, drv_index, 1,
&total_size, &block_size);
h->cciss_read = CCISS_READ_16;
h->cciss_read = CCISS_READ_10;
h->cciss_write = CCISS_WRITE_10;
}
+geo_inq:
cciss_geometry_inquiry(ctlr, drv_index, 1, total_size, block_size,
inq_buff, &h->drv[drv_index]);
/* if it's the controller it's already added */
if (drv_index) {
disk->queue = blk_init_queue(do_cciss_request, &h->lock);
+ sprintf(disk->disk_name, "cciss/c%dd%d", ctlr, drv_index);
+ disk->major = h->major;
+ disk->first_minor = drv_index << NWD_SHIFT;
+ disk->fops = &cciss_fops;
+ disk->private_data = &h->drv[drv_index];
/* Set up queue information */
disk->queue->backing_dev_info.ra_pages = READ_AHEAD;
/* This is a limit in the driver and could be eliminated. */
blk_queue_max_phys_segments(disk->queue, MAXSGENTRIES);
- blk_queue_max_sectors(disk->queue, 512);
+ blk_queue_max_sectors(disk->queue, h->cciss_max_sectors);
blk_queue_softirq_done(disk->queue, cciss_softirq_done);
/* Set busy_configuring flag for this operation */
spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
- if (h->num_luns >= CISS_MAX_LUN) {
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
- return -EINVAL;
- }
-
if (h->busy_configuring) {
spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
return -EBUSY;
0, 0, TYPE_CMD);
if (return_code == IO_OK) {
- listlength |=
- (0xff & (unsigned int)(ld_buff->LUNListLength[0]))
- << 24;
- listlength |=
- (0xff & (unsigned int)(ld_buff->LUNListLength[1]))
- << 16;
- listlength |=
- (0xff & (unsigned int)(ld_buff->LUNListLength[2]))
- << 8;
- listlength |=
- 0xff & (unsigned int)(ld_buff->LUNListLength[3]);
+ listlength =
+ be32_to_cpu(*(__be32 *) ld_buff->LUNListLength);
} else { /* reading number of logical volumes failed */
printk(KERN_WARNING "cciss: report logical volume"
" command failed\n");
if (drv_index == -1)
goto freeret;
+ /*Check if the gendisk needs to be allocated */
+ if (!h->gendisk[drv_index]){
+ h->gendisk[drv_index] = alloc_disk(1 << NWD_SHIFT);
+ if (!h->gendisk[drv_index]){
+ printk(KERN_ERR "cciss: could not allocate new disk %d\n", drv_index);
+ goto mem_msg;
+ }
+ }
}
h->drv[drv_index].LunID = lunid;
cciss_update_drive_info(ctlr, drv_index);
static int deregister_disk(struct gendisk *disk, drive_info_struct *drv,
int clear_all)
{
+ int i;
ctlr_info_t *h = get_host(disk);
if (!capable(CAP_SYS_RAWIO))
del_gendisk(disk);
if (q) {
blk_cleanup_queue(q);
+ /* Set drv->queue to NULL so that we do not try
+ * to call blk_start_queue on this queue in the
+ * interrupt handler
+ */
drv->queue = NULL;
}
+ /* If clear_all is set then we are deleting the logical
+ * drive, not just refreshing its info. For drives
+ * other than disk 0 we will call put_disk. We do not
+ * do this for disk 0 as we need it to be able to
+ * configure the controller.
+ */
+ if (clear_all){
+ /* This isn't pretty, but we need to find the
+ * disk in our array and NULL our the pointer.
+ * This is so that we will call alloc_disk if
+ * this index is used again later.
+ */
+ for (i=0; i < CISS_MAX_LUN; i++){
+ if(h->gendisk[i] == disk){
+ h->gendisk[i] = NULL;
+ break;
+ }
+ }
+ put_disk(disk);
+ }
}
+ } else {
+ set_capacity(disk, 0);
}
--h->num_luns;
"does not support reading geometry\n");
drv->heads = 255;
drv->sectors = 32; // Sectors per track
+ drv->cylinders = total_size + 1;
+ drv->raid_level = RAID_UNKNOWN;
} else {
drv->heads = inq_buff->data_byte[6];
drv->sectors = inq_buff->data_byte[7];
drv->raid_level = inq_buff->data_byte[8];
}
drv->block_size = block_size;
- drv->nr_blocks = total_size;
+ drv->nr_blocks = total_size + 1;
t = drv->heads * drv->sectors;
if (t > 1) {
- unsigned rem = sector_div(total_size, t);
+ sector_t real_size = total_size + 1;
+ unsigned long rem = sector_div(real_size, t);
if (rem)
- total_size++;
- drv->cylinders = total_size;
+ real_size++;
+ drv->cylinders = real_size;
}
} else { /* Get geometry failed */
printk(KERN_WARNING "cciss: reading geometry failed\n");
ctlr, buf, sizeof(ReadCapdata_struct),
1, logvol, 0, NULL, TYPE_CMD);
if (return_code == IO_OK) {
- *total_size = be32_to_cpu(*(__u32 *) buf->total_size)+1;
- *block_size = be32_to_cpu(*(__u32 *) buf->block_size);
+ *total_size = be32_to_cpu(*(__be32 *) buf->total_size);
+ *block_size = be32_to_cpu(*(__be32 *) buf->block_size);
} else { /* read capacity command failed */
printk(KERN_WARNING "cciss: read capacity failed\n");
*total_size = 0;
*block_size = BLOCK_SIZE;
}
- if (*total_size != (__u32) 0)
+ if (*total_size != 0)
printk(KERN_INFO " blocks= %llu block_size= %d\n",
- (unsigned long long)*total_size, *block_size);
+ (unsigned long long)*total_size+1, *block_size);
kfree(buf);
return;
}
1, logvol, 0, NULL, TYPE_CMD);
}
if (return_code == IO_OK) {
- *total_size = be64_to_cpu(*(__u64 *) buf->total_size)+1;
- *block_size = be32_to_cpu(*(__u32 *) buf->block_size);
+ *total_size = be64_to_cpu(*(__be64 *) buf->total_size);
+ *block_size = be32_to_cpu(*(__be32 *) buf->block_size);
} else { /* read capacity command failed */
printk(KERN_WARNING "cciss: read capacity failed\n");
*total_size = 0;
*block_size = BLOCK_SIZE;
}
printk(KERN_INFO " blocks= %llu block_size= %d\n",
- (unsigned long long)*total_size, *block_size);
+ (unsigned long long)*total_size+1, *block_size);
kfree(buf);
return;
}
start_io(h);
}
+static inline int evaluate_target_status(CommandList_struct *cmd)
+{
+ unsigned char sense_key;
+ int error_count = 1;
+
+ if (cmd->err_info->ScsiStatus != 0x02) { /* not check condition? */
+ if (!blk_pc_request(cmd->rq))
+ printk(KERN_WARNING "cciss: cmd %p "
+ "has SCSI Status 0x%x\n",
+ cmd, cmd->err_info->ScsiStatus);
+ return error_count;
+ }
+
+ /* check the sense key */
+ sense_key = 0xf & cmd->err_info->SenseInfo[2];
+ /* no status or recovered error */
+ if ((sense_key == 0x0) || (sense_key == 0x1))
+ error_count = 0;
+
+ if (!blk_pc_request(cmd->rq)) { /* Not SG_IO or similar? */
+ if (error_count != 0)
+ printk(KERN_WARNING "cciss: cmd %p has CHECK CONDITION"
+ " sense key = 0x%x\n", cmd, sense_key);
+ return error_count;
+ }
+
+ /* SG_IO or similar, copy sense data back */
+ if (cmd->rq->sense) {
+ if (cmd->rq->sense_len > cmd->err_info->SenseLen)
+ cmd->rq->sense_len = cmd->err_info->SenseLen;
+ memcpy(cmd->rq->sense, cmd->err_info->SenseInfo,
+ cmd->rq->sense_len);
+ } else
+ cmd->rq->sense_len = 0;
+
+ return error_count;
+}
+
/* checks the status of the job and calls complete buffers to mark all
* buffers for the completed job. Note that this function does not need
* to hold the hba/queue lock.
static inline void complete_command(ctlr_info_t *h, CommandList_struct *cmd,
int timeout)
{
- int status = 1;
int retry_cmd = 0;
+ struct request *rq = cmd->rq;
+
+ rq->errors = 0;
if (timeout)
- status = 0;
+ rq->errors = 1;
- if (cmd->err_info->CommandStatus != 0) { /* an error has occurred */
- switch (cmd->err_info->CommandStatus) {
- unsigned char sense_key;
- case CMD_TARGET_STATUS:
- status = 0;
+ if (cmd->err_info->CommandStatus == 0) /* no error has occurred */
+ goto after_error_processing;
- if (cmd->err_info->ScsiStatus == 0x02) {
- printk(KERN_WARNING "cciss: cmd %p "
- "has CHECK CONDITION "
- " byte 2 = 0x%x\n", cmd,
- cmd->err_info->SenseInfo[2]
- );
- /* check the sense key */
- sense_key = 0xf & cmd->err_info->SenseInfo[2];
- /* no status or recovered error */
- if ((sense_key == 0x0) || (sense_key == 0x1)) {
- status = 1;
- }
- } else {
- printk(KERN_WARNING "cciss: cmd %p "
- "has SCSI Status 0x%x\n",
- cmd, cmd->err_info->ScsiStatus);
- }
- break;
- case CMD_DATA_UNDERRUN:
+ switch (cmd->err_info->CommandStatus) {
+ case CMD_TARGET_STATUS:
+ rq->errors = evaluate_target_status(cmd);
+ break;
+ case CMD_DATA_UNDERRUN:
+ if (blk_fs_request(cmd->rq)) {
printk(KERN_WARNING "cciss: cmd %p has"
" completed with data underrun "
"reported\n", cmd);
- break;
- case CMD_DATA_OVERRUN:
+ cmd->rq->data_len = cmd->err_info->ResidualCnt;
+ }
+ break;
+ case CMD_DATA_OVERRUN:
+ if (blk_fs_request(cmd->rq))
printk(KERN_WARNING "cciss: cmd %p has"
" completed with data overrun "
"reported\n", cmd);
- break;
- case CMD_INVALID:
- printk(KERN_WARNING "cciss: cmd %p is "
- "reported invalid\n", cmd);
- status = 0;
- break;
- case CMD_PROTOCOL_ERR:
- printk(KERN_WARNING "cciss: cmd %p has "
- "protocol error \n", cmd);
- status = 0;
- break;
- case CMD_HARDWARE_ERR:
- printk(KERN_WARNING "cciss: cmd %p had "
- " hardware error\n", cmd);
- status = 0;
- break;
- case CMD_CONNECTION_LOST:
- printk(KERN_WARNING "cciss: cmd %p had "
- "connection lost\n", cmd);
- status = 0;
- break;
- case CMD_ABORTED:
- printk(KERN_WARNING "cciss: cmd %p was "
- "aborted\n", cmd);
- status = 0;
- break;
- case CMD_ABORT_FAILED:
- printk(KERN_WARNING "cciss: cmd %p reports "
- "abort failed\n", cmd);
- status = 0;
- break;
- case CMD_UNSOLICITED_ABORT:
- printk(KERN_WARNING "cciss%d: unsolicited "
- "abort %p\n", h->ctlr, cmd);
- if (cmd->retry_count < MAX_CMD_RETRIES) {
- retry_cmd = 1;
- printk(KERN_WARNING
- "cciss%d: retrying %p\n", h->ctlr, cmd);
- cmd->retry_count++;
- } else
- printk(KERN_WARNING
- "cciss%d: %p retried too "
- "many times\n", h->ctlr, cmd);
- status = 0;
- break;
- case CMD_TIMEOUT:
- printk(KERN_WARNING "cciss: cmd %p timedout\n", cmd);
- status = 0;
- break;
- default:
- printk(KERN_WARNING "cciss: cmd %p returned "
- "unknown status %x\n", cmd,
- cmd->err_info->CommandStatus);
- status = 0;
- }
+ break;
+ case CMD_INVALID:
+ printk(KERN_WARNING "cciss: cmd %p is "
+ "reported invalid\n", cmd);
+ rq->errors = 1;
+ break;
+ case CMD_PROTOCOL_ERR:
+ printk(KERN_WARNING "cciss: cmd %p has "
+ "protocol error \n", cmd);
+ rq->errors = 1;
+ break;
+ case CMD_HARDWARE_ERR:
+ printk(KERN_WARNING "cciss: cmd %p had "
+ " hardware error\n", cmd);
+ rq->errors = 1;
+ break;
+ case CMD_CONNECTION_LOST:
+ printk(KERN_WARNING "cciss: cmd %p had "
+ "connection lost\n", cmd);
+ rq->errors = 1;
+ break;
+ case CMD_ABORTED:
+ printk(KERN_WARNING "cciss: cmd %p was "
+ "aborted\n", cmd);
+ rq->errors = 1;
+ break;
+ case CMD_ABORT_FAILED:
+ printk(KERN_WARNING "cciss: cmd %p reports "
+ "abort failed\n", cmd);
+ rq->errors = 1;
+ break;
+ case CMD_UNSOLICITED_ABORT:
+ printk(KERN_WARNING "cciss%d: unsolicited "
+ "abort %p\n", h->ctlr, cmd);
+ if (cmd->retry_count < MAX_CMD_RETRIES) {
+ retry_cmd = 1;
+ printk(KERN_WARNING
+ "cciss%d: retrying %p\n", h->ctlr, cmd);
+ cmd->retry_count++;
+ } else
+ printk(KERN_WARNING
+ "cciss%d: %p retried too "
+ "many times\n", h->ctlr, cmd);
+ rq->errors = 1;
+ break;
+ case CMD_TIMEOUT:
+ printk(KERN_WARNING "cciss: cmd %p timedout\n", cmd);
+ rq->errors = 1;
+ break;
+ default:
+ printk(KERN_WARNING "cciss: cmd %p returned "
+ "unknown status %x\n", cmd,
+ cmd->err_info->CommandStatus);
+ rq->errors = 1;
}
+
+after_error_processing:
+
/* We need to return this command */
if (retry_cmd) {
resend_cciss_cmd(h, cmd);
return;
}
-
+ cmd->rq->data_len = 0;
cmd->rq->completion_data = cmd;
- cmd->rq->errors = status;
blk_add_trace_rq(cmd->rq->q, cmd->rq, BLK_TA_COMPLETE);
blk_complete_request(cmd->rq);
}
c->Request.Type.Type = TYPE_CMD; // It is a command.
c->Request.Type.Attribute = ATTR_SIMPLE;
c->Request.Type.Direction =
- (rq_data_dir(creq) == READ) ? h->cciss_read : h->cciss_write;
+ (rq_data_dir(creq) == READ) ? XFER_READ : XFER_WRITE;
c->Request.Timeout = 0; // Don't time out
c->Request.CDB[0] =
(rq_data_dir(creq) == READ) ? h->cciss_read : h->cciss_write;
#endif /* CCISS_DEBUG */
c->Header.SGList = c->Header.SGTotal = seg;
- if(h->cciss_read == CCISS_READ_10) {
- c->Request.CDB[1] = 0;
- c->Request.CDB[2] = (start_blk >> 24) & 0xff; //MSB
- c->Request.CDB[3] = (start_blk >> 16) & 0xff;
- c->Request.CDB[4] = (start_blk >> 8) & 0xff;
- c->Request.CDB[5] = start_blk & 0xff;
- c->Request.CDB[6] = 0; // (sect >> 24) & 0xff; MSB
- c->Request.CDB[7] = (creq->nr_sectors >> 8) & 0xff;
- c->Request.CDB[8] = creq->nr_sectors & 0xff;
- c->Request.CDB[9] = c->Request.CDB[11] = c->Request.CDB[12] = 0;
+ if (likely(blk_fs_request(creq))) {
+ if(h->cciss_read == CCISS_READ_10) {
+ c->Request.CDB[1] = 0;
+ c->Request.CDB[2] = (start_blk >> 24) & 0xff; //MSB
+ c->Request.CDB[3] = (start_blk >> 16) & 0xff;
+ c->Request.CDB[4] = (start_blk >> 8) & 0xff;
+ c->Request.CDB[5] = start_blk & 0xff;
+ c->Request.CDB[6] = 0; // (sect >> 24) & 0xff; MSB
+ c->Request.CDB[7] = (creq->nr_sectors >> 8) & 0xff;
+ c->Request.CDB[8] = creq->nr_sectors & 0xff;
+ c->Request.CDB[9] = c->Request.CDB[11] = c->Request.CDB[12] = 0;
+ } else {
+ c->Request.CDBLen = 16;
+ c->Request.CDB[1]= 0;
+ c->Request.CDB[2]= (start_blk >> 56) & 0xff; //MSB
+ c->Request.CDB[3]= (start_blk >> 48) & 0xff;
+ c->Request.CDB[4]= (start_blk >> 40) & 0xff;
+ c->Request.CDB[5]= (start_blk >> 32) & 0xff;
+ c->Request.CDB[6]= (start_blk >> 24) & 0xff;
+ c->Request.CDB[7]= (start_blk >> 16) & 0xff;
+ c->Request.CDB[8]= (start_blk >> 8) & 0xff;
+ c->Request.CDB[9]= start_blk & 0xff;
+ c->Request.CDB[10]= (creq->nr_sectors >> 24) & 0xff;
+ c->Request.CDB[11]= (creq->nr_sectors >> 16) & 0xff;
+ c->Request.CDB[12]= (creq->nr_sectors >> 8) & 0xff;
+ c->Request.CDB[13]= creq->nr_sectors & 0xff;
+ c->Request.CDB[14] = c->Request.CDB[15] = 0;
+ }
+ } else if (blk_pc_request(creq)) {
+ c->Request.CDBLen = creq->cmd_len;
+ memcpy(c->Request.CDB, creq->cmd, BLK_MAX_CDB);
} else {
- c->Request.CDBLen = 16;
- c->Request.CDB[1]= 0;
- c->Request.CDB[2]= (start_blk >> 56) & 0xff; //MSB
- c->Request.CDB[3]= (start_blk >> 48) & 0xff;
- c->Request.CDB[4]= (start_blk >> 40) & 0xff;
- c->Request.CDB[5]= (start_blk >> 32) & 0xff;
- c->Request.CDB[6]= (start_blk >> 24) & 0xff;
- c->Request.CDB[7]= (start_blk >> 16) & 0xff;
- c->Request.CDB[8]= (start_blk >> 8) & 0xff;
- c->Request.CDB[9]= start_blk & 0xff;
- c->Request.CDB[10]= (creq->nr_sectors >> 24) & 0xff;
- c->Request.CDB[11]= (creq->nr_sectors >> 16) & 0xff;
- c->Request.CDB[12]= (creq->nr_sectors >> 8) & 0xff;
- c->Request.CDB[13]= creq->nr_sectors & 0xff;
- c->Request.CDB[14] = c->Request.CDB[15] = 0;
+ printk(KERN_WARNING "cciss%d: bad request type %d\n", h->ctlr, creq->cmd_type);
+ BUG();
}
spin_lock_irq(q->queue_lock);
if (err > 0) {
printk(KERN_WARNING "cciss: only %d MSI-X vectors "
"available\n", err);
+ goto default_int_mode;
} else {
printk(KERN_WARNING "cciss: MSI-X init failed %d\n",
err);
+ goto default_int_mode;
}
}
if (pci_find_capability(pdev, PCI_CAP_ID_MSI)) {
if (!pci_enable_msi(pdev)) {
- c->intr[SIMPLE_MODE_INT] = pdev->irq;
c->msi_vector = 1;
- return;
} else {
printk(KERN_WARNING "cciss: MSI init failed\n");
- c->intr[SIMPLE_MODE_INT] = pdev->irq;
- return;
}
}
- default_int_mode:
+default_int_mode:
#endif /* CONFIG_PCI_MSI */
/* if we get here we're going to use the default interrupt mode */
c->intr[SIMPLE_MODE_INT] = pdev->irq;
if (err) {
printk(KERN_ERR "cciss: Cannot obtain PCI resources, "
"aborting\n");
- goto err_out_disable_pdev;
+ return err;
}
subsystem_vendor_id = pdev->subsystem_vendor;
#ifdef CCISS_DEBUG
printk("address 0 = %x\n", c->paddr);
#endif /* CCISS_DEBUG */
- c->vaddr = remap_pci_mem(c->paddr, 200);
+ c->vaddr = remap_pci_mem(c->paddr, 0x250);
/* Wait for the board to become ready. (PCI hotplug needs this.)
* We poll for up to 120 secs, once per 100ms. */
}
#endif
+ /* Disabling DMA prefetch for the P600
+ * An ASIC bug may result in a prefetch beyond
+ * physical memory.
+ */
+ if(board_id == 0x3225103C) {
+ __u32 dma_prefetch;
+ dma_prefetch = readl(c->vaddr + I2O_DMA1_CFG);
+ dma_prefetch |= 0x8000;
+ writel(dma_prefetch, c->vaddr + I2O_DMA1_CFG);
+ }
+
#ifdef CCISS_DEBUG
printk("Trying to put board into Simple mode\n");
#endif /* CCISS_DEBUG */
}
return 0;
- err_out_free_res:
+err_out_free_res:
+ /*
+ * Deliberately omit pci_disable_device(): it does something nasty to
+ * Smart Array controllers that pci_enable_device does not undo
+ */
pci_release_regions(pdev);
-
- err_out_disable_pdev:
- pci_disable_device(pdev);
return err;
}
}
cciss_read_capacity(cntl_num, i, 0, &total_size, &block_size);
- /* total_size = last LBA + 1 */
- if(total_size == (__u32) 0) {
+ /* If read_capacity returns all F's the logical is >2TB */
+ /* so we switch to 16-byte CDBs for all read/write ops */
+ if(total_size == 0xFFFFFFFFULL) {
cciss_read_capacity_16(cntl_num, i, 0,
&total_size, &block_size);
hba[cntl_num]->cciss_read = CCISS_READ_16;
/* Returns -1 if no free entries are left. */
static int alloc_cciss_hba(void)
{
- struct gendisk *disk[NWD];
- int i, n;
- for (n = 0; n < NWD; n++) {
- disk[n] = alloc_disk(1 << NWD_SHIFT);
- if (!disk[n])
- goto out;
- }
+ int i;
for (i = 0; i < MAX_CTLR; i++) {
if (!hba[i]) {
p = kzalloc(sizeof(ctlr_info_t), GFP_KERNEL);
if (!p)
goto Enomem;
- for (n = 0; n < NWD; n++)
- p->gendisk[n] = disk[n];
+ p->gendisk[0] = alloc_disk(1 << NWD_SHIFT);
+ if (!p->gendisk[0])
+ goto Enomem;
hba[i] = p;
return i;
}
}
printk(KERN_WARNING "cciss: This driver supports a maximum"
" of %d controllers.\n", MAX_CTLR);
- goto out;
- Enomem:
+ return -1;
+Enomem:
printk(KERN_ERR "cciss: out of memory.\n");
- out:
- while (n--)
- put_disk(disk[n]);
return -1;
}
int n;
hba[i] = NULL;
- for (n = 0; n < NWD; n++)
+ for (n = 0; n < CISS_MAX_LUN; n++)
put_disk(p->gendisk[n]);
kfree(p);
}
static int __devinit cciss_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- request_queue_t *q;
int i;
- int j;
+ int j = 0;
int rc;
int dac;
hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_ON);
cciss_procinit(i);
+
+ hba[i]->cciss_max_sectors = 2048;
+
hba[i]->busy_initializing = 0;
- for (j = 0; j < NWD; j++) { /* mfm */
+ do {
drive_info_struct *drv = &(hba[i]->drv[j]);
struct gendisk *disk = hba[i]->gendisk[j];
+ request_queue_t *q;
+
+ /* Check if the disk was allocated already */
+ if (!disk){
+ hba[i]->gendisk[j] = alloc_disk(1 << NWD_SHIFT);
+ disk = hba[i]->gendisk[j];
+ }
+
+ /* Check that the disk was able to be allocated */
+ if (!disk) {
+ printk(KERN_ERR "cciss: unable to allocate memory for disk %d\n", j);
+ goto clean4;
+ }
q = blk_init_queue(do_cciss_request, &hba[i]->lock);
if (!q) {
printk(KERN_ERR
"cciss: unable to allocate queue for disk %d\n",
j);
- break;
+ goto clean4;
}
drv->queue = q;
/* This is a limit in the driver and could be eliminated. */
blk_queue_max_phys_segments(q, MAXSGENTRIES);
- blk_queue_max_sectors(q, 512);
+ blk_queue_max_sectors(q, hba[i]->cciss_max_sectors);
blk_queue_softirq_done(q, cciss_softirq_done);
blk_queue_hardsect_size(q, drv->block_size);
set_capacity(disk, drv->nr_blocks);
add_disk(disk);
- }
+ j++;
+ } while (j <= hba[i]->highest_lun);
return 1;
unregister_blkdev(hba[i]->major, hba[i]->devname);
clean1:
hba[i]->busy_initializing = 0;
+ /* cleanup any queues that may have been initialized */
+ for (j=0; j <= hba[i]->highest_lun; j++){
+ drive_info_struct *drv = &(hba[i]->drv[j]);
+ if (drv->queue)
+ blk_cleanup_queue(drv->queue);
+ }
+ /*
+ * Deliberately omit pci_disable_device(): it does something nasty to
+ * Smart Array controllers that pci_enable_device does not undo
+ */
+ pci_release_regions(pdev);
+ pci_set_drvdata(pdev, NULL);
free_hba(i);
return -1;
}
-static void __devexit cciss_remove_one(struct pci_dev *pdev)
+static void cciss_shutdown(struct pci_dev *pdev)
{
ctlr_info_t *tmp_ptr;
- int i, j;
+ int i;
char flush_buf[4];
int return_code;
- if (pci_get_drvdata(pdev) == NULL) {
- printk(KERN_ERR "cciss: Unable to remove device \n");
- return;
- }
tmp_ptr = pci_get_drvdata(pdev);
+ if (tmp_ptr == NULL)
+ return;
i = tmp_ptr->ctlr;
- if (hba[i] == NULL) {
- printk(KERN_ERR "cciss: device appears to "
- "already be removed \n");
+ if (hba[i] == NULL)
return;
- }
+
/* Turn board interrupts off and send the flush cache command */
/* sendcmd will turn off interrupt, and send the flush...
* To write all data in the battery backed cache to disks */
memset(flush_buf, 0, 4);
return_code = sendcmd(CCISS_CACHE_FLUSH, i, flush_buf, 4, 0, 0, 0, NULL,
TYPE_CMD);
- if (return_code != IO_OK) {
- printk(KERN_WARNING "Error Flushing cache on controller %d\n",
- i);
+ if (return_code == IO_OK) {
+ printk(KERN_INFO "Completed flushing cache on controller %d\n", i);
+ } else {
+ printk(KERN_WARNING "Error flushing cache on controller %d\n", i);
}
free_irq(hba[i]->intr[2], hba[i]);
+}
-#ifdef CONFIG_PCI_MSI
- if (hba[i]->msix_vector)
- pci_disable_msix(hba[i]->pdev);
- else if (hba[i]->msi_vector)
- pci_disable_msi(hba[i]->pdev);
-#endif /* CONFIG_PCI_MSI */
+static void __devexit cciss_remove_one(struct pci_dev *pdev)
+{
+ ctlr_info_t *tmp_ptr;
+ int i, j;
+
+ if (pci_get_drvdata(pdev) == NULL) {
+ printk(KERN_ERR "cciss: Unable to remove device \n");
+ return;
+ }
+ tmp_ptr = pci_get_drvdata(pdev);
+ i = tmp_ptr->ctlr;
+ if (hba[i] == NULL) {
+ printk(KERN_ERR "cciss: device appears to "
+ "already be removed \n");
+ return;
+ }
- iounmap(hba[i]->vaddr);
- cciss_unregister_scsi(i); /* unhook from SCSI subsystem */
- unregister_blkdev(hba[i]->major, hba[i]->devname);
remove_proc_entry(hba[i]->devname, proc_cciss);
+ unregister_blkdev(hba[i]->major, hba[i]->devname);
/* remove it from the disk list */
- for (j = 0; j < NWD; j++) {
+ for (j = 0; j < CISS_MAX_LUN; j++) {
struct gendisk *disk = hba[i]->gendisk[j];
if (disk) {
request_queue_t *q = disk->queue;
}
}
+ cciss_unregister_scsi(i); /* unhook from SCSI subsystem */
+
+ cciss_shutdown(pdev);
+
+#ifdef CONFIG_PCI_MSI
+ if (hba[i]->msix_vector)
+ pci_disable_msix(hba[i]->pdev);
+ else if (hba[i]->msi_vector)
+ pci_disable_msi(hba[i]->pdev);
+#endif /* CONFIG_PCI_MSI */
+
+ iounmap(hba[i]->vaddr);
+
pci_free_consistent(hba[i]->pdev, hba[i]->nr_cmds * sizeof(CommandList_struct),
hba[i]->cmd_pool, hba[i]->cmd_pool_dhandle);
pci_free_consistent(hba[i]->pdev, hba[i]->nr_cmds * sizeof(ErrorInfo_struct),
#ifdef CONFIG_CISS_SCSI_TAPE
kfree(hba[i]->scsi_rejects.complete);
#endif
+ /*
+ * Deliberately omit pci_disable_device(): it does something nasty to
+ * Smart Array controllers that pci_enable_device does not undo
+ */
pci_release_regions(pdev);
- pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
free_hba(i);
}
.probe = cciss_init_one,
.remove = __devexit_p(cciss_remove_one),
.id_table = cciss_pci_device_id, /* id_table */
+ .shutdown = cciss_shutdown,
};
/*