]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/scsi/sg.c
sg: convert the direct IO path to use the block layer
[linux-2.6-omap-h63xx.git] / drivers / scsi / sg.c
index 3d36270a8b4d0a720a31f49b62dd3e3531ef6d97..cb6de0752ee1318ba9e4b708f2e39256aa9e1008 100644 (file)
@@ -137,6 +137,8 @@ typedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */
        char orphan;            /* 1 -> drop on sight, 0 -> normal */
        char sg_io_owned;       /* 1 -> packet belongs to SG_IO */
        volatile char done;     /* 0->before bh, 1->before read, 2->read */
+       struct request *rq;
+       struct bio *bio;
 } Sg_request;
 
 typedef struct sg_fd {         /* holds the state of a file descriptor */
@@ -176,7 +178,7 @@ typedef struct sg_device { /* holds the state of each scsi generic device */
 static int sg_fasync(int fd, struct file *filp, int mode);
 /* tasklet or soft irq callback */
 static void sg_cmd_done(void *data, char *sense, int result, int resid);
-static int sg_start_req(Sg_request * srp);
+static int sg_start_req(Sg_request *srp, unsigned char *cmd);
 static void sg_finish_rem_req(Sg_request * srp);
 static int sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size);
 static int sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp,
@@ -217,6 +219,23 @@ static int sg_last_dev(void);
 #define SZ_SG_IOVEC sizeof(sg_iovec_t)
 #define SZ_SG_REQ_INFO sizeof(sg_req_info_t)
 
+static int sg_allow_access(struct file *filp, unsigned char *cmd)
+{
+       struct sg_fd *sfp = (struct sg_fd *)filp->private_data;
+       struct request_queue *q = sfp->parentdp->device->request_queue;
+
+       if (sfp->parentdp->device->type == TYPE_SCANNER)
+               return 0;
+
+       return blk_verify_command(&q->cmd_filter,
+                                 cmd, filp->f_mode & FMODE_WRITE);
+}
+
+static void sg_rq_end_io(struct request *rq, int uptodate)
+{
+       sg_cmd_done(rq->end_io_data, rq->sense, rq->errors, rq->data_len);
+}
+
 static int
 sg_open(struct inode *inode, struct file *filp)
 {
@@ -689,7 +708,7 @@ sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf,
                sg_remove_request(sfp, srp);
                return -EFAULT;
        }
-       if (read_only && !blk_verify_command(file, cmnd)) {
+       if (read_only && sg_allow_access(file, cmnd)) {
                sg_remove_request(sfp, srp);
                return -EPERM;
        }
@@ -720,7 +739,8 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp,
        SCSI_LOG_TIMEOUT(4, printk("sg_common_write:  scsi opcode=0x%02x, cmd_size=%d\n",
                          (int) cmnd[0], (int) hp->cmd_len));
 
-       if ((k = sg_start_req(srp))) {
+       k = sg_start_req(srp, cmnd);
+       if (k) {
                SCSI_LOG_TIMEOUT(1, printk("sg_common_write: start_req err=%d\n", k));
                sg_finish_rem_req(srp);
                return k;       /* probably out of space --> ENOMEM */
@@ -753,6 +773,12 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp,
        hp->duration = jiffies_to_msecs(jiffies);
 /* Now send everything of to mid-level. The next time we hear about this
    packet is when sg_cmd_done() is called (i.e. a callback). */
+       if (srp->rq) {
+               srp->rq->timeout = timeout;
+               blk_execute_rq_nowait(sdp->device->request_queue, sdp->disk,
+                                     srp->rq, 1, sg_rq_end_io);
+               return 0;
+       }
        if (scsi_execute_async(sdp->device, cmnd, hp->cmd_len, data_dir, srp->data.buffer,
                                hp->dxfer_len, srp->data.k_use_sg, timeout,
                                SG_DEFAULT_RETRIES, srp, sg_cmd_done,
@@ -793,6 +819,7 @@ sg_ioctl(struct inode *inode, struct file *filp,
 
        if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
                return -ENXIO;
+
        SCSI_LOG_TIMEOUT(3, printk("sg_ioctl: %s, cmd=0x%x\n",
                                   sdp->disk->disk_name, (int) cmd_in));
        read_only = (O_RDWR != (filp->f_flags & O_ACCMODE));
@@ -1061,7 +1088,7 @@ sg_ioctl(struct inode *inode, struct file *filp,
 
                        if (copy_from_user(&opcode, siocp->data, 1))
                                return -EFAULT;
-                       if (!blk_verify_command(filp, &opcode))
+                       if (sg_allow_access(filp, &opcode))
                                return -EPERM;
                }
                return sg_scsi_ioctl(filp, sdp->device->request_queue, NULL, p);
@@ -1621,27 +1648,61 @@ exit_sg(void)
        idr_destroy(&sg_index_idr);
 }
 
-static int
-sg_start_req(Sg_request * srp)
+static int __sg_start_req(struct sg_request *srp, struct sg_io_hdr *hp,
+                         unsigned char *cmd)
+{
+       struct sg_fd *sfp = srp->parentfp;
+       struct request_queue *q = sfp->parentdp->device->request_queue;
+       struct request *rq;
+       int rw = hp->dxfer_direction == SG_DXFER_TO_DEV ? WRITE : READ;
+
+       rq = blk_get_request(q, rw, GFP_ATOMIC);
+       if (!rq)
+               return -ENOMEM;
+
+       memcpy(rq->cmd, cmd, hp->cmd_len);
+
+       rq->cmd_len = hp->cmd_len;
+       rq->cmd_type = REQ_TYPE_BLOCK_PC;
+
+       srp->rq = rq;
+       rq->end_io_data = srp;
+       rq->sense = srp->sense_b;
+       rq->retries = SG_DEFAULT_RETRIES;
+
+       return 0;
+}
+
+static int sg_start_req(Sg_request *srp, unsigned char *cmd)
 {
        int res;
        Sg_fd *sfp = srp->parentfp;
        sg_io_hdr_t *hp = &srp->header;
        int dxfer_len = (int) hp->dxfer_len;
        int dxfer_dir = hp->dxfer_direction;
+       unsigned long uaddr = (unsigned long)hp->dxferp;
        Sg_scatter_hold *req_schp = &srp->data;
        Sg_scatter_hold *rsv_schp = &sfp->reserve;
+       struct request_queue *q = sfp->parentdp->device->request_queue;
+       unsigned long alignment = queue_dma_alignment(q) | q->dma_pad_mask;
 
        SCSI_LOG_TIMEOUT(4, printk("sg_start_req: dxfer_len=%d\n", dxfer_len));
+
        if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE))
-               return 0;
+               return __sg_start_req(srp, hp, cmd);
+
+#ifdef SG_ALLOW_DIO_CODE
        if (sg_allow_dio && (hp->flags & SG_FLAG_DIRECT_IO) &&
            (dxfer_dir != SG_DXFER_UNKNOWN) && (0 == hp->iovec_count) &&
-           (!sfp->parentdp->device->host->unchecked_isa_dma)) {
-               res = sg_build_direct(srp, sfp, dxfer_len);
-               if (res <= 0)   /* -ve -> error, 0 -> done, 1 -> try indirect */
-                       return res;
+           (!sfp->parentdp->device->host->unchecked_isa_dma) &&
+           !(uaddr & alignment) && !(dxfer_len & alignment)) {
+               res = __sg_start_req(srp, hp, cmd);
+               if (!res)
+                       res = sg_build_direct(srp, sfp, dxfer_len);
+
+               return res;
        }
+#endif
        if ((!sg_res_in_use(sfp)) && (dxfer_len <= rsv_schp->bufflen))
                sg_link_reserve(sfp, srp, dxfer_len);
        else {
@@ -1665,6 +1726,13 @@ sg_finish_rem_req(Sg_request * srp)
                sg_unlink_reserve(sfp, srp);
        else
                sg_remove_scat(req_schp);
+
+       if (srp->rq) {
+               if (srp->bio)
+                       blk_rq_unmap_user(srp->bio);
+               blk_put_request(srp->rq);
+       }
+
        sg_remove_request(sfp, srp);
 }
 
@@ -1690,151 +1758,23 @@ sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp, int tablesize)
        return tablesize;       /* number of scat_gath elements allocated */
 }
 
-#ifdef SG_ALLOW_DIO_CODE
-/* vvvvvvvv  following code borrowed from st driver's direct IO vvvvvvvvv */
-       /* TODO: hopefully we can use the generic block layer code */
-
-/* Pin down user pages and put them into a scatter gather list. Returns <= 0 if
-   - mapping of all pages not successful
-   (i.e., either completely successful or fails)
-*/
-static int 
-st_map_user_pages(struct scatterlist *sgl, const unsigned int max_pages, 
-                 unsigned long uaddr, size_t count, int rw)
-{
-       unsigned long end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT;
-       unsigned long start = uaddr >> PAGE_SHIFT;
-       const int nr_pages = end - start;
-       int res, i, j;
-       struct page **pages;
-
-       /* User attempted Overflow! */
-       if ((uaddr + count) < uaddr)
-               return -EINVAL;
-
-       /* Too big */
-        if (nr_pages > max_pages)
-               return -ENOMEM;
-
-       /* Hmm? */
-       if (count == 0)
-               return 0;
-
-       if ((pages = kmalloc(max_pages * sizeof(*pages), GFP_ATOMIC)) == NULL)
-               return -ENOMEM;
-
-        /* Try to fault in all of the necessary pages */
-       down_read(&current->mm->mmap_sem);
-        /* rw==READ means read from drive, write into memory area */
-       res = get_user_pages(
-               current,
-               current->mm,
-               uaddr,
-               nr_pages,
-               rw == READ,
-               0, /* don't force */
-               pages,
-               NULL);
-       up_read(&current->mm->mmap_sem);
-
-       /* Errors and no page mapped should return here */
-       if (res < nr_pages)
-               goto out_unmap;
-
-        for (i=0; i < nr_pages; i++) {
-                /* FIXME: flush superflous for rw==READ,
-                 * probably wrong function for rw==WRITE
-                 */
-               flush_dcache_page(pages[i]);
-               /* ?? Is locking needed? I don't think so */
-               /* if (!trylock_page(pages[i]))
-                  goto out_unlock; */
-        }
-
-       sg_set_page(sgl, pages[0], 0, uaddr & ~PAGE_MASK);
-       if (nr_pages > 1) {
-               sgl[0].length = PAGE_SIZE - sgl[0].offset;
-               count -= sgl[0].length;
-               for (i=1; i < nr_pages ; i++)
-                       sg_set_page(&sgl[i], pages[i], count < PAGE_SIZE ? count : PAGE_SIZE, 0);
-       }
-       else {
-               sgl[0].length = count;
-       }
-
-       kfree(pages);
-       return nr_pages;
-
- out_unmap:
-       if (res > 0) {
-               for (j=0; j < res; j++)
-                       page_cache_release(pages[j]);
-               res = 0;
-       }
-       kfree(pages);
-       return res;
-}
-
-
-/* And unmap them... */
-static int 
-st_unmap_user_pages(struct scatterlist *sgl, const unsigned int nr_pages,
-                   int dirtied)
-{
-       int i;
-
-       for (i=0; i < nr_pages; i++) {
-               struct page *page = sg_page(&sgl[i]);
-
-               if (dirtied)
-                       SetPageDirty(page);
-               /* unlock_page(page); */
-               /* FIXME: cache flush missing for rw==READ
-                * FIXME: call the correct reference counting function
-                */
-               page_cache_release(page);
-       }
-
-       return 0;
-}
-
-/* ^^^^^^^^  above code borrowed from st driver's direct IO ^^^^^^^^^ */
-#endif
-
-
 /* Returns: -ve -> error, 0 -> done, 1 -> try indirect */
 static int
 sg_build_direct(Sg_request * srp, Sg_fd * sfp, int dxfer_len)
 {
-#ifdef SG_ALLOW_DIO_CODE
        sg_io_hdr_t *hp = &srp->header;
        Sg_scatter_hold *schp = &srp->data;
-       int sg_tablesize = sfp->parentdp->sg_tablesize;
-       int mx_sc_elems, res;
-       struct scsi_device *sdev = sfp->parentdp->device;
-
-       if (((unsigned long)hp->dxferp &
-                       queue_dma_alignment(sdev->request_queue)) != 0)
-               return 1;
+       int res;
+       struct request *rq = srp->rq;
+       struct request_queue *q = sfp->parentdp->device->request_queue;
 
-       mx_sc_elems = sg_build_sgat(schp, sfp, sg_tablesize);
-        if (mx_sc_elems <= 0) {
-                return 1;
-        }
-       res = st_map_user_pages(schp->buffer, mx_sc_elems,
-                               (unsigned long)hp->dxferp, dxfer_len, 
-                               (SG_DXFER_TO_DEV == hp->dxfer_direction) ? 1 : 0);
-       if (res <= 0) {
-               sg_remove_scat(schp);
-               return 1;
-       }
-       schp->k_use_sg = res;
+       res = blk_rq_map_user(q, rq, NULL, hp->dxferp, dxfer_len, GFP_ATOMIC);
+       if (res)
+               return res;
+       srp->bio = rq->bio;
        schp->dio_in_use = 1;
        hp->info |= SG_INFO_DIRECT_IO;
        return 0;
-#else
-       return 1;
-#endif
 }
 
 static int
@@ -2013,11 +1953,7 @@ sg_remove_scat(Sg_scatter_hold * schp)
        if (schp->buffer && (schp->sglist_len > 0)) {
                struct scatterlist *sg = schp->buffer;
 
-               if (schp->dio_in_use) {
-#ifdef SG_ALLOW_DIO_CODE
-                       st_unmap_user_pages(sg, schp->k_use_sg, TRUE);
-#endif
-               } else {
+               if (!schp->dio_in_use) {
                        int k;
 
                        for (k = 0; (k < schp->k_use_sg) && sg_page(sg);
@@ -2027,8 +1963,9 @@ sg_remove_scat(Sg_scatter_hold * schp)
                                    k, sg_page(sg), sg->length));
                                sg_page_free(sg_page(sg), sg->length);
                        }
+
+                       kfree(schp->buffer);
                }
-               kfree(schp->buffer);
        }
        memset(schp, 0, sizeof (*schp));
 }