Merge ../linux-2.6
[powerpc.git] / drivers / scsi / scsi_lib.c
index 4362dcd..bdce9d1 100644 (file)
@@ -30,7 +30,7 @@
 #include "scsi_logging.h"
 
 
-#define SG_MEMPOOL_NR          (sizeof(scsi_sg_pools)/sizeof(struct scsi_host_sg_pool))
+#define SG_MEMPOOL_NR          ARRAY_SIZE(scsi_sg_pools)
 #define SG_MEMPOOL_SIZE                32
 
 struct scsi_host_sg_pool {
@@ -286,13 +286,12 @@ int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
        int result;
        
        if (sshdr) {
-               sense = kmalloc(SCSI_SENSE_BUFFERSIZE, GFP_NOIO);
+               sense = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_NOIO);
                if (!sense)
                        return DRIVER_ERROR << 24;
-               memset(sense, 0, SCSI_SENSE_BUFFERSIZE);
        }
        result = scsi_execute(sdev, cmd, data_direction, buffer, bufflen,
-                                 sense, timeout, retries, 0);
+                             sense, timeout, retries, 0);
        if (sshdr)
                scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, sshdr);
 
@@ -368,7 +367,7 @@ static int scsi_req_map_sg(struct request *rq, struct scatterlist *sgl,
                           int nsegs, unsigned bufflen, gfp_t gfp)
 {
        struct request_queue *q = rq->q;
-       int nr_pages = (bufflen + PAGE_SIZE - 1) >> PAGE_SHIFT;
+       int nr_pages = (bufflen + sgl[0].offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
        unsigned int data_len = 0, len, bytes, off;
        struct page *page;
        struct bio *bio = NULL;
@@ -1068,16 +1067,29 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes,
                        break;
                case NOT_READY:
                        /*
-                        * If the device is in the process of becoming ready,
-                        * retry.
+                        * If the device is in the process of becoming
+                        * ready, or has a temporary blockage, retry.
                         */
-                       if (sshdr.asc == 0x04 && sshdr.ascq == 0x01) {
-                               scsi_requeue_command(q, cmd);
-                               return;
+                       if (sshdr.asc == 0x04) {
+                               switch (sshdr.ascq) {
+                               case 0x01: /* becoming ready */
+                               case 0x04: /* format in progress */
+                               case 0x05: /* rebuild in progress */
+                               case 0x06: /* recalculation in progress */
+                               case 0x07: /* operation in progress */
+                               case 0x08: /* Long write in progress */
+                               case 0x09: /* self test in progress */
+                                       scsi_requeue_command(q, cmd);
+                                       return;
+                               default:
+                                       break;
+                               }
                        }
-                       if (!(req->flags & REQ_QUIET))
+                       if (!(req->flags & REQ_QUIET)) {
                                scmd_printk(KERN_INFO, cmd,
-                                          "Device not ready.\n");
+                                          "Device not ready: ");
+                               scsi_print_sense_hdr("", &sshdr);
+                       }
                        scsi_end_request(cmd, 0, this_count, 1);
                        return;
                case VOLUME_OVERFLOW:
@@ -1480,6 +1492,8 @@ static inline int scsi_host_queue_ready(struct request_queue *q,
 static void scsi_kill_request(struct request *req, request_queue_t *q)
 {
        struct scsi_cmnd *cmd = req->special;
+       struct scsi_device *sdev = cmd->device;
+       struct Scsi_Host *shost = sdev->host;
 
        blkdev_dequeue_request(req);
 
@@ -1492,13 +1506,26 @@ static void scsi_kill_request(struct request *req, request_queue_t *q)
        scsi_init_cmd_errh(cmd);
        cmd->result = DID_NO_CONNECT << 16;
        atomic_inc(&cmd->device->iorequest_cnt);
+
+       /*
+        * SCSI request completion path will do scsi_device_unbusy(),
+        * bump busy counts.  To bump the counters, we need to dance
+        * with the locks as normal issue path does.
+        */
+       sdev->device_busy++;
+       spin_unlock(sdev->request_queue->queue_lock);
+       spin_lock(shost->host_lock);
+       shost->host_busy++;
+       spin_unlock(shost->host_lock);
+       spin_lock(sdev->request_queue->queue_lock);
+
        __scsi_done(cmd);
 }
 
 static void scsi_softirq_done(struct request *rq)
 {
        struct scsi_cmnd *cmd = rq->completion_data;
-       unsigned long wait_for = cmd->allowed * cmd->timeout_per_command;
+       unsigned long wait_for = (cmd->allowed + 1) * cmd->timeout_per_command;
        int disposition;
 
        INIT_LIST_HEAD(&cmd->eh_entry);
@@ -1788,9 +1815,8 @@ int __init scsi_init_queue(void)
                                        sgp->name);
                }
 
-               sgp->pool = mempool_create(SG_MEMPOOL_SIZE,
-                               mempool_alloc_slab, mempool_free_slab,
-                               sgp->slab);
+               sgp->pool = mempool_create_slab_pool(SG_MEMPOOL_SIZE,
+                                                    sgp->slab);
                if (!sgp->pool) {
                        printk(KERN_ERR "SCSI: can't init sg mempool %s\n",
                                        sgp->name);
@@ -1812,6 +1838,84 @@ void scsi_exit_queue(void)
                kmem_cache_destroy(sgp->slab);
        }
 }
+
+/**
+ *     scsi_mode_select - issue a mode select
+ *     @sdev:  SCSI device to be queried
+ *     @pf:    Page format bit (1 == standard, 0 == vendor specific)
+ *     @sp:    Save page bit (0 == don't save, 1 == save)
+ *     @modepage: mode page being requested
+ *     @buffer: request buffer (may not be smaller than eight bytes)
+ *     @len:   length of request buffer.
+ *     @timeout: command timeout
+ *     @retries: number of retries before failing
+ *     @data: returns a structure abstracting the mode header data
+ *     @sense: place to put sense data (or NULL if no sense to be collected).
+ *             must be SCSI_SENSE_BUFFERSIZE big.
+ *
+ *     Returns zero if successful; negative error number or scsi
+ *     status on error
+ *
+ */
+int
+scsi_mode_select(struct scsi_device *sdev, int pf, int sp, int modepage,
+                unsigned char *buffer, int len, int timeout, int retries,
+                struct scsi_mode_data *data, struct scsi_sense_hdr *sshdr)
+{
+       unsigned char cmd[10];
+       unsigned char *real_buffer;
+       int ret;
+
+       memset(cmd, 0, sizeof(cmd));
+       cmd[1] = (pf ? 0x10 : 0) | (sp ? 0x01 : 0);
+
+       if (sdev->use_10_for_ms) {
+               if (len > 65535)
+                       return -EINVAL;
+               real_buffer = kmalloc(8 + len, GFP_KERNEL);
+               if (!real_buffer)
+                       return -ENOMEM;
+               memcpy(real_buffer + 8, buffer, len);
+               len += 8;
+               real_buffer[0] = 0;
+               real_buffer[1] = 0;
+               real_buffer[2] = data->medium_type;
+               real_buffer[3] = data->device_specific;
+               real_buffer[4] = data->longlba ? 0x01 : 0;
+               real_buffer[5] = 0;
+               real_buffer[6] = data->block_descriptor_length >> 8;
+               real_buffer[7] = data->block_descriptor_length;
+
+               cmd[0] = MODE_SELECT_10;
+               cmd[7] = len >> 8;
+               cmd[8] = len;
+       } else {
+               if (len > 255 || data->block_descriptor_length > 255 ||
+                   data->longlba)
+                       return -EINVAL;
+
+               real_buffer = kmalloc(4 + len, GFP_KERNEL);
+               if (!real_buffer)
+                       return -ENOMEM;
+               memcpy(real_buffer + 4, buffer, len);
+               len += 4;
+               real_buffer[0] = 0;
+               real_buffer[1] = data->medium_type;
+               real_buffer[2] = data->device_specific;
+               real_buffer[3] = data->block_descriptor_length;
+               
+
+               cmd[0] = MODE_SELECT;
+               cmd[4] = len;
+       }
+
+       ret = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, real_buffer, len,
+                              sshdr, timeout, retries);
+       kfree(real_buffer);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(scsi_mode_select);
+
 /**
  *     scsi_mode_sense - issue a mode sense, falling back from 10 to 
  *             six bytes if necessary.
@@ -1833,7 +1937,8 @@ void scsi_exit_queue(void)
 int
 scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
                  unsigned char *buffer, int len, int timeout, int retries,
-                 struct scsi_mode_data *data, struct scsi_sense_hdr *sshdr) {
+                 struct scsi_mode_data *data, struct scsi_sense_hdr *sshdr)
+{
        unsigned char cmd[12];
        int use_10_for_ms;
        int header_length;
@@ -1893,8 +1998,16 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
        }
 
        if(scsi_status_is_good(result)) {
-               data->header_length = header_length;
-               if(use_10_for_ms) {
+               if (unlikely(buffer[0] == 0x86 && buffer[1] == 0x0b &&
+                            (modepage == 6 || modepage == 8))) {
+                       /* Initio breakage? */
+                       header_length = 0;
+                       data->length = 13;
+                       data->medium_type = 0;
+                       data->device_specific = 0;
+                       data->longlba = 0;
+                       data->block_descriptor_length = 0;
+               } else if(use_10_for_ms) {
                        data->length = buffer[0]*256 + buffer[1] + 2;
                        data->medium_type = buffer[2];
                        data->device_specific = buffer[3];
@@ -1907,6 +2020,7 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
                        data->device_specific = buffer[2];
                        data->block_descriptor_length = buffer[3];
                }
+               data->header_length = header_length;
        }
 
        return result;
@@ -2250,60 +2364,60 @@ scsi_target_unblock(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(scsi_target_unblock);
 
-
-struct work_queue_work {
-       struct work_struct      work;
-       void                    (*fn)(void *);
-       void                    *data;
-};
-
-static void execute_in_process_context_work(void *data)
-{
-       void (*fn)(void *data);
-       struct work_queue_work *wqw = data;
-
-       fn = wqw->fn;
-       data = wqw->data;
-
-       kfree(wqw);
-
-       fn(data);
-}
-
 /**
- * scsi_execute_in_process_context - reliably execute the routine with user context
- * @fn:                the function to execute
- * @data:      data to pass to the function
+ * scsi_kmap_atomic_sg - find and atomically map an sg-elemnt
+ * @sg:                scatter-gather list
+ * @sg_count:  number of segments in sg
+ * @offset:    offset in bytes into sg, on return offset into the mapped area
+ * @len:       bytes to map, on return number of bytes mapped
  *
- * Executes the function immediately if process context is available,
- * otherwise schedules the function for delayed execution.
- *
- * Returns:    0 - function was executed
- *             1 - function was scheduled for execution
- *             <0 - error
+ * Returns virtual address of the start of the mapped page
  */
-int scsi_execute_in_process_context(void (*fn)(void *data), void *data)
+void *scsi_kmap_atomic_sg(struct scatterlist *sg, int sg_count,
+                         size_t *offset, size_t *len)
 {
-       struct work_queue_work *wqw;
+       int i;
+       size_t sg_len = 0, len_complete = 0;
+       struct page *page;
 
-       if (!in_interrupt()) {
-               fn(data);
-               return 0;
+       for (i = 0; i < sg_count; i++) {
+               len_complete = sg_len; /* Complete sg-entries */
+               sg_len += sg[i].length;
+               if (sg_len > *offset)
+                       break;
        }
 
-       wqw = kmalloc(sizeof(struct work_queue_work), GFP_ATOMIC);
-
-       if (unlikely(!wqw)) {
-               printk(KERN_ERR "Failed to allocate memory\n");
+       if (unlikely(i == sg_count)) {
+               printk(KERN_ERR "%s: Bytes in sg: %zu, requested offset %zu, "
+                       "elements %d\n",
+                      __FUNCTION__, sg_len, *offset, sg_count);
                WARN_ON(1);
-               return -ENOMEM;
+               return NULL;
        }
 
-       INIT_WORK(&wqw->work, execute_in_process_context_work, wqw);
-       wqw->fn = fn;
-       wqw->data = data;
-       schedule_work(&wqw->work);
+       /* Offset starting from the beginning of first page in this sg-entry */
+       *offset = *offset - len_complete + sg[i].offset;
 
-       return 1;
+       /* Assumption: contiguous pages can be accessed as "page + i" */
+       page = nth_page(sg[i].page, (*offset >> PAGE_SHIFT));
+       *offset &= ~PAGE_MASK;
+
+       /* Bytes in this sg-entry from *offset to the end of the page */
+       sg_len = PAGE_SIZE - *offset;
+       if (*len > sg_len)
+               *len = sg_len;
+
+       return kmap_atomic(page, KM_BIO_SRC_IRQ);
+}
+EXPORT_SYMBOL(scsi_kmap_atomic_sg);
+
+/**
+ * scsi_kunmap_atomic_sg - atomically unmap a virtual address, previously
+ *                        mapped with scsi_kmap_atomic_sg
+ * @virt:      virtual address to be unmapped
+ */
+void scsi_kunmap_atomic_sg(void *virt)
+{
+       kunmap_atomic(virt, KM_BIO_SRC_IRQ);
 }
-EXPORT_SYMBOL_GPL(scsi_execute_in_process_context);
+EXPORT_SYMBOL(scsi_kunmap_atomic_sg);