Merge ../linux-2.6
[powerpc.git] / drivers / scsi / scsi_lib.c
index 7b0f9a3..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 {
@@ -367,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;
@@ -1067,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:
@@ -2350,3 +2363,61 @@ scsi_target_unblock(struct device *dev)
                device_for_each_child(dev, NULL, target_unblock);
 }
 EXPORT_SYMBOL_GPL(scsi_target_unblock);
+
+/**
+ * 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
+ *
+ * Returns virtual address of the start of the mapped page
+ */
+void *scsi_kmap_atomic_sg(struct scatterlist *sg, int sg_count,
+                         size_t *offset, size_t *len)
+{
+       int i;
+       size_t sg_len = 0, len_complete = 0;
+       struct page *page;
+
+       for (i = 0; i < sg_count; i++) {
+               len_complete = sg_len; /* Complete sg-entries */
+               sg_len += sg[i].length;
+               if (sg_len > *offset)
+                       break;
+       }
+
+       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 NULL;
+       }
+
+       /* Offset starting from the beginning of first page in this sg-entry */
+       *offset = *offset - len_complete + sg[i].offset;
+
+       /* 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(scsi_kunmap_atomic_sg);