Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc
[firefly-linux-kernel-4.4.55.git] / drivers / scsi / sg.c
index ffc87851f2e86e866616da6a6c0e39ca48dd1910..e1716f14cd4710cca0363eab985cdf370d9b9879 100644 (file)
@@ -179,7 +179,7 @@ typedef struct sg_device { /* holds the state of each scsi generic device */
 /* tasklet or soft irq callback */
 static void sg_rq_end_io(struct request *rq, int uptodate);
 static int sg_start_req(Sg_request *srp, unsigned char *cmd);
-static void sg_finish_rem_req(Sg_request * srp);
+static int sg_finish_rem_req(Sg_request * srp);
 static int sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size);
 static ssize_t sg_new_read(Sg_fd * sfp, char __user *buf, size_t count,
                           Sg_request * srp);
@@ -518,7 +518,7 @@ sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp)
                goto err_out;
        }
 err_out:
-       sg_finish_rem_req(srp);
+       err = sg_finish_rem_req(srp);
        return (0 == err) ? count : err;
 }
 
@@ -1312,8 +1312,10 @@ static void sg_rq_end_io(struct request *rq, int uptodate)
                wake_up_interruptible(&sfp->read_wait);
                kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN);
                kref_put(&sfp->f_ref, sg_remove_sfp);
-       } else
-               execute_in_process_context(sg_rq_end_io_usercontext, &srp->ew);
+       } else {
+               INIT_WORK(&srp->ew.work, sg_rq_end_io_usercontext);
+               schedule_work(&srp->ew.work);
+       }
 }
 
 static struct file_operations sg_fops = {
@@ -1656,10 +1658,30 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd)
                md->null_mapped = hp->dxferp ? 0 : 1;
        }
 
-       if (iov_count)
-               res = blk_rq_map_user_iov(q, rq, md, hp->dxferp, iov_count,
-                                         hp->dxfer_len, GFP_ATOMIC);
-       else
+       if (iov_count) {
+               int len, size = sizeof(struct sg_iovec) * iov_count;
+               struct iovec *iov;
+
+               iov = kmalloc(size, GFP_ATOMIC);
+               if (!iov)
+                       return -ENOMEM;
+
+               if (copy_from_user(iov, hp->dxferp, size)) {
+                       kfree(iov);
+                       return -EFAULT;
+               }
+
+               len = iov_length(iov, iov_count);
+               if (hp->dxfer_len < len) {
+                       iov_count = iov_shorten(iov, iov_count, hp->dxfer_len);
+                       len = hp->dxfer_len;
+               }
+
+               res = blk_rq_map_user_iov(q, rq, md, (struct sg_iovec *)iov,
+                                         iov_count,
+                                         len, GFP_ATOMIC);
+               kfree(iov);
+       } else
                res = blk_rq_map_user(q, rq, md, hp->dxferp,
                                      hp->dxfer_len, GFP_ATOMIC);
 
@@ -1674,9 +1696,10 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd)
        return res;
 }
 
-static void
-sg_finish_rem_req(Sg_request * srp)
+static int sg_finish_rem_req(Sg_request * srp)
 {
+       int ret = 0;
+
        Sg_fd *sfp = srp->parentfp;
        Sg_scatter_hold *req_schp = &srp->data;
 
@@ -1688,12 +1711,14 @@ sg_finish_rem_req(Sg_request * srp)
 
        if (srp->rq) {
                if (srp->bio)
-                       blk_rq_unmap_user(srp->bio);
+                       ret = blk_rq_unmap_user(srp->bio);
 
                blk_put_request(srp->rq);
        }
 
        sg_remove_request(sfp, srp);
+
+       return ret;
 }
 
 static int
@@ -2079,7 +2104,8 @@ static void sg_remove_sfp(struct kref *kref)
        write_unlock_irqrestore(&sg_index_lock, iflags);
        wake_up_interruptible(&sdp->o_excl_wait);
 
-       execute_in_process_context(sg_remove_sfp_usercontext, &sfp->ew);
+       INIT_WORK(&sfp->ew.work, sg_remove_sfp_usercontext);
+       schedule_work(&sfp->ew.work);
 }
 
 static int