From: Tomas Henzl <thenzl@redhat.com> Date: Tue, 26 Aug 2008 17:18:14 +0200 Subject: [scsi] cciss: support for sg_ioctl Message-id: 48B41EB6.2020905@redhat.com O-Subject: RHEL5.3 PATCH 2/2] support for sg_ioctl Bugzilla: 250483 The patch comes from HP and adds c It was backported from following upstream commits - 03bbfee58d440f5dc2e880944ab75fc644534794 add SG_IO ioctl to cciss 1a614f505193fcfc1b298643268a5db5b48e297f fix error reporting for SG_IO Resolves 250483 - sg_io ioctl Regards Tomas diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 8148f47..884d20f 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -46,6 +46,9 @@ #include <linux/blkdev.h> #include <linux/genhd.h> #include <linux/completion.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.20-RH2)" @@ -1222,6 +1225,32 @@ static int cciss_ioctl(struct inode *inode, struct file *filep, 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; } @@ -1235,7 +1264,7 @@ static inline void complete_buffers(struct bio *bio, int status) bio->bi_next = NULL; blk_finished_io(len); - bio_endio(bio, nr_sectors << 9, status ? 0 : -EIO); + bio_endio(bio, nr_sectors << 9, status); bio = xbh; } } @@ -2425,6 +2454,69 @@ static inline void resend_cciss_cmd(ctlr_info_t *h, CommandList_struct *c) start_io(h); } +static inline unsigned int make_status_bytes(unsigned int scsi_status_byte, + unsigned int msg_byte, unsigned int host_byte, + unsigned int driver_byte) +{ + /* inverse of macros in scsi.h */ + return (scsi_status_byte & 0xff) | + ((msg_byte & 0xff) << 8) | + ((host_byte & 0xff) << 16) | + ((driver_byte & 0xff) << 24); +} + +static inline int evaluate_target_status(CommandList_struct *cmd) +{ + unsigned char sense_key; + unsigned char status_byte, msg_byte, host_byte, driver_byte; + int error_value; + + /* If we get in here, it means we got "target status", that is, scsi status */ + status_byte = cmd->err_info->ScsiStatus; + driver_byte = DRIVER_OK; + msg_byte = cmd->err_info->CommandStatus; /* correct? seems too device specific */ + + if (blk_pc_request(cmd->rq)) + host_byte = DID_PASSTHROUGH; + else + host_byte = DID_OK; + + error_value = make_status_bytes(status_byte, msg_byte, + host_byte, driver_byte); + + if (cmd->err_info->ScsiStatus != SAM_STAT_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_value; + } + + /* check the sense key */ + sense_key = 0xf & cmd->err_info->SenseInfo[2]; + /* no status or recovered error */ + if (((sense_key == 0x0) || (sense_key == 0x1)) && !blk_pc_request(cmd->rq)) + error_value = 0; + + if (!blk_pc_request(cmd->rq)) { /* Not SG_IO or similar? */ + if (error_value != 0) + printk(KERN_WARNING "cciss: cmd %p has CHECK CONDITION" + " sense key = 0x%x\n", cmd, sense_key); + return error_value; + } + + /* 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_value; +} + /* 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. @@ -2432,109 +2524,122 @@ static inline void resend_cciss_cmd(ctlr_info_t *h, CommandList_struct *c) 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; + int ctlr = h->ctlr; + rq->errors = 0; if (timeout) - status = 0; + rq->errors = make_status_bytes(0, 0, 0, DRIVER_TIMEOUT); - 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); - } + switch (cmd->err_info->CommandStatus) { + case CMD_TARGET_STATUS: + rq->errors = evaluate_target_status(cmd); break; case CMD_DATA_UNDERRUN: - printk(KERN_WARNING "cciss: cmd %p has" - " completed with data underrun " - "reported\n", cmd); + 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: - printk(KERN_WARNING "cciss: cmd %p has" - " completed with data overrun " - "reported\n", cmd); + 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; + rq->errors = make_status_bytes(SAM_STAT_GOOD, + cmd->err_info->CommandStatus, DRIVER_OK, + blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : + DID_ERROR); break; case CMD_PROTOCOL_ERR: printk(KERN_WARNING "cciss: cmd %p has " "protocol error \n", cmd); - status = 0; + rq->errors = make_status_bytes(SAM_STAT_GOOD, + cmd->err_info->CommandStatus, DRIVER_OK, + blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : + DID_ERROR); break; case CMD_HARDWARE_ERR: - printk(KERN_WARNING "cciss: cmd %p had " - " hardware error\n", cmd); - status = 0; + printk(KERN_WARNING "cciss%d: cmd had " + " hardware error\n", ctlr); + rq->errors = make_status_bytes(SAM_STAT_GOOD, + cmd->err_info->CommandStatus, DRIVER_OK, + blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : + DID_ERROR); break; case CMD_CONNECTION_LOST: - printk(KERN_WARNING "cciss: cmd %p had " - "connection lost\n", cmd); - status = 0; + printk(KERN_WARNING "cciss%d: cmd had " + "connection lost\n", ctlr); + rq->errors = make_status_bytes(SAM_STAT_GOOD, + cmd->err_info->CommandStatus, DRIVER_OK, + blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : + DID_ERROR); break; case CMD_ABORTED: - printk(KERN_WARNING "cciss: cmd %p was " - "aborted\n", cmd); - status = 0; + printk(KERN_WARNING "cciss%d: cmd was " + "aborted\n", ctlr); + rq->errors = make_status_bytes(SAM_STAT_GOOD, + cmd->err_info->CommandStatus, DRIVER_OK, + blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : + DID_ABORT); break; case CMD_ABORT_FAILED: - printk(KERN_WARNING "cciss: cmd %p reports " - "abort failed\n", cmd); - status = 0; + printk(KERN_WARNING "cciss%d: cmd reports " + "abort failed\n", ctlr); + rq->errors = make_status_bytes(SAM_STAT_GOOD, + cmd->err_info->CommandStatus, DRIVER_OK, + blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : + DID_ERROR); break; case CMD_UNSOLICITED_ABORT: printk(KERN_WARNING "cciss%d: unsolicited " - "abort %p\n", h->ctlr, cmd); + "abort\n", ctlr); if (cmd->retry_count < MAX_CMD_RETRIES) { retry_cmd = 1; printk(KERN_WARNING - "cciss%d: retrying %p\n", h->ctlr, cmd); + "cciss%d: retrying cmd\n", ctlr); cmd->retry_count++; } else printk(KERN_WARNING - "cciss%d: %p retried too " - "many times\n", h->ctlr, cmd); - status = 0; + "cciss%d: cmd retried too " + "many times\n", ctlr); + rq->errors = make_status_bytes(SAM_STAT_GOOD, + cmd->err_info->CommandStatus, DRIVER_OK, + blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ABORT); break; case CMD_TIMEOUT: - printk(KERN_WARNING "cciss: cmd %p timedout\n", cmd); - status = 0; + printk(KERN_WARNING "cciss%d: cmd timedout\n", ctlr); + rq->errors = make_status_bytes(SAM_STAT_GOOD, + cmd->err_info->CommandStatus, DRIVER_OK, + blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ERROR); break; default: - printk(KERN_WARNING "cciss: cmd %p returned " - "unknown status %x\n", cmd, + printk(KERN_WARNING "cciss%d: cmd returned " + "unknown status %x\n", ctlr, cmd->err_info->CommandStatus); - status = 0; - } + rq->errors = make_status_bytes(SAM_STAT_GOOD, + cmd->err_info->CommandStatus, DRIVER_OK, + blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ERROR); } - /* We need to return this command */ + +after_error_processing: + + /* We need to retry 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); } @@ -2629,34 +2734,42 @@ static void do_cciss_request(request_queue_t *q) 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 %ld\n", + h->ctlr, creq->flags); + BUG(); } - spin_lock_irq(q->queue_lock); addQ(&(h->reqQ), c); @@ -2665,9 +2778,9 @@ static void do_cciss_request(request_queue_t *q) h->maxQsinceinit = h->Qdepth; goto queue; - full: +full: blk_stop_queue(q); - startio: +startio: /* We will already have the driver lock here so not need * to lock it. */