diff options
Diffstat (limited to 'drivers/scsi/sg.c')
-rw-r--r-- | drivers/scsi/sg.c | 50 |
1 files changed, 38 insertions, 12 deletions
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index ffc87851f2e8..e1716f14cd47 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -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 |