Commit 637b48c8 authored by Jens Axboe's avatar Jens Axboe Committed by Linus Torvalds

[PATCH] user data -> request mapping

This patch allows you to map a request with user data for io, similarly
to what you can do with bio_map_user() already to a bio. However, this
goes one step further and populates the request so the user only has to
fill in the cdb (almost) and put it on the queue for execution. Patch
converts sg_io() to use it, next patch I'll send adapts cdrom layer to
use it for zero copy cdda dma extraction.
parent 95902cd8
......@@ -28,6 +28,11 @@
#include <linux/slab.h>
#include <linux/swap.h>
/*
* for max sense size
*/
#include <scsi/scsi_cmnd.h>
static void blk_unplug_work(void *data);
static void blk_unplug_timeout(unsigned long data);
......@@ -1625,6 +1630,7 @@ static struct request *get_request(request_queue_t *q, int rw, int gfp_mask)
rq->rl = rl;
rq->waiting = NULL;
rq->special = NULL;
rq->data_len = 0;
rq->data = NULL;
rq->sense = NULL;
......@@ -1770,6 +1776,144 @@ void blk_insert_request(request_queue_t *q, struct request *rq,
EXPORT_SYMBOL(blk_insert_request);
/**
* blk_rq_map_user - map user data to a request, for REQ_BLOCK_PC usage
* @q: request queue where request should be inserted
* @rw: READ or WRITE data
* @ubuf: the user buffer
* @len: length of user data
*
* Description:
* Data will be mapped directly for zero copy io, if possible. Otherwise
* a kernel bounce buffer is used.
*
* A matching blk_rq_unmap_user() must be issued at the end of io, while
* still in process context.
*/
struct request *blk_rq_map_user(request_queue_t *q, int rw, void __user *ubuf,
unsigned int len)
{
struct request *rq = NULL;
char *buf = NULL;
struct bio *bio;
int ret;
rq = blk_get_request(q, rw, __GFP_WAIT);
if (!rq)
return ERR_PTR(-ENOMEM);
bio = bio_map_user(q, NULL, (unsigned long) ubuf, len, rw == READ);
if (!bio) {
int bytes = (len + 511) & ~511;
buf = kmalloc(bytes, q->bounce_gfp | GFP_USER);
if (!buf) {
ret = -ENOMEM;
goto fault;
}
if (rw == WRITE) {
if (copy_from_user(buf, ubuf, len)) {
ret = -EFAULT;
goto fault;
}
} else
memset(buf, 0, len);
}
rq->bio = rq->biotail = bio;
if (rq->bio)
blk_rq_bio_prep(q, rq, bio);
rq->buffer = rq->data = buf;
rq->data_len = len;
return rq;
fault:
if (buf)
kfree(buf);
if (bio)
bio_unmap_user(bio, 1);
if (rq)
blk_put_request(rq);
return ERR_PTR(ret);
}
EXPORT_SYMBOL(blk_rq_map_user);
/**
* blk_rq_unmap_user - unmap a request with user data
* @rq: request to be unmapped
* @ubuf: user buffer
* @ulen: length of user buffer
*
* Description:
* Unmap a request previously mapped by blk_rq_map_user().
*/
int blk_rq_unmap_user(struct request *rq, void __user *ubuf, unsigned int ulen)
{
const int read = rq_data_dir(rq) == READ;
int ret = 0;
if (rq->biotail)
bio_unmap_user(rq->biotail, read);
if (rq->buffer) {
if (read && copy_to_user(ubuf, rq->buffer, ulen))
ret = -EFAULT;
kfree(rq->buffer);
}
blk_put_request(rq);
return ret;
}
EXPORT_SYMBOL(blk_rq_unmap_user);
/**
* blk_execute_rq - insert a request into queue for execution
* @q: queue to insert the request in
* @bd_disk: matching gendisk
* @rq: request to insert
*
* Description:
* Insert a fully prepared request at the back of the io scheduler queue
* for execution.
*/
int blk_execute_rq(request_queue_t *q, struct gendisk *bd_disk,
struct request *rq)
{
DECLARE_COMPLETION(wait);
char sense[SCSI_SENSE_BUFFERSIZE];
int err = 0;
rq->rq_disk = bd_disk;
/*
* we need an extra reference to the request, so we can look at
* it after io completion
*/
rq->ref_count++;
if (!rq->sense) {
memset(sense, 0, sizeof(sense));
rq->sense = sense;
rq->sense_len = 0;
}
rq->flags |= REQ_NOMERGE;
rq->waiting = &wait;
elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 1);
generic_unplug_device(q);
wait_for_completion(&wait);
if (rq->errors)
err = -EIO;
return err;
}
EXPORT_SYMBOL(blk_execute_rq);
void drive_stat_acct(struct request *rq, int nr_sectors, int new_io)
{
int rw = rq_data_dir(rq);
......
......@@ -24,13 +24,12 @@
#include <linux/completion.h>
#include <linux/cdrom.h>
#include <linux/slab.h>
#include <linux/bio.h>
#include <linux/times.h>
#include <asm/uaccess.h>
#include <scsi/scsi.h>
#include <scsi/scsi_ioctl.h>
#include <scsi/scsi_cmnd.h>
/* Command group 3 is reserved and should never be used. */
const unsigned char scsi_command_size[8] =
......@@ -39,45 +38,9 @@ const unsigned char scsi_command_size[8] =
16, 12, 10, 10
};
#define BLK_DEFAULT_TIMEOUT (60 * HZ)
/* defined in ../scsi/scsi.h ... should it be included? */
#ifndef SCSI_SENSE_BUFFERSIZE
#define SCSI_SENSE_BUFFERSIZE 64
#endif
static int blk_do_rq(request_queue_t *q, struct gendisk *bd_disk,
struct request *rq)
{
char sense[SCSI_SENSE_BUFFERSIZE];
DECLARE_COMPLETION(wait);
int err = 0;
rq->rq_disk = bd_disk;
/*
* we need an extra reference to the request, so we can look at
* it after io completion
*/
rq->ref_count++;
if (!rq->sense) {
memset(sense, 0, sizeof(sense));
rq->sense = sense;
rq->sense_len = 0;
}
rq->flags |= REQ_NOMERGE;
rq->waiting = &wait;
elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 1);
generic_unplug_device(q);
wait_for_completion(&wait);
if (rq->errors)
err = -EIO;
EXPORT_SYMBOL(scsi_command_size);
return err;
}
#define BLK_DEFAULT_TIMEOUT (60 * HZ)
#include <scsi/sg.h>
......@@ -148,9 +111,7 @@ static int sg_io(request_queue_t *q, struct gendisk *bd_disk,
unsigned long start_time;
int reading, writing;
struct request *rq;
struct bio *bio;
char sense[SCSI_SENSE_BUFFERSIZE];
void *buffer;
if (hdr->interface_id != 'S')
return -EINVAL;
......@@ -167,11 +128,7 @@ static int sg_io(request_queue_t *q, struct gendisk *bd_disk,
return -EIO;
reading = writing = 0;
buffer = NULL;
bio = NULL;
if (hdr->dxfer_len) {
unsigned int bytes = (hdr->dxfer_len + 511) & ~511;
switch (hdr->dxfer_direction) {
default:
return -EINVAL;
......@@ -186,31 +143,13 @@ static int sg_io(request_queue_t *q, struct gendisk *bd_disk,
break;
}
/*
* first try to map it into a bio. reading from device will
* be a write to vm.
*/
bio = bio_map_user(q, NULL, (unsigned long) hdr->dxferp,
hdr->dxfer_len, reading);
/*
* if bio setup failed, fall back to slow approach
*/
if (!bio) {
buffer = kmalloc(bytes, q->bounce_gfp | GFP_USER);
if (!buffer)
return -ENOMEM;
rq = blk_rq_map_user(q, writing ? WRITE : READ, hdr->dxferp,
hdr->dxfer_len);
if (writing) {
if (copy_from_user(buffer, hdr->dxferp,
hdr->dxfer_len))
goto out_buffer;
if (IS_ERR(rq))
return PTR_ERR(rq);
} else
memset(buffer, 0, hdr->dxfer_len);
}
}
rq = blk_get_request(q, writing ? WRITE : READ, __GFP_WAIT);
rq = blk_get_request(q, READ, __GFP_WAIT);
/*
* fill in request structure
......@@ -226,14 +165,6 @@ static int sg_io(request_queue_t *q, struct gendisk *bd_disk,
rq->flags |= REQ_BLOCK_PC;
rq->bio = rq->biotail = NULL;
if (bio)
blk_rq_bio_prep(q, rq, bio);
rq->data = buffer;
rq->data_len = hdr->dxfer_len;
rq->timeout = (hdr->timeout * HZ) / 1000;
if (!rq->timeout)
rq->timeout = q->sg_timeout;
......@@ -246,10 +177,7 @@ static int sg_io(request_queue_t *q, struct gendisk *bd_disk,
* (if he doesn't check that is his problem).
* N.B. a non-zero SCSI status is _not_ necessarily an error.
*/
blk_do_rq(q, bd_disk, rq);
if (bio)
bio_unmap_user(bio, reading);
blk_execute_rq(q, bd_disk, rq);
/* write to all output members */
hdr->status = rq->errors;
......@@ -271,22 +199,12 @@ static int sg_io(request_queue_t *q, struct gendisk *bd_disk,
hdr->sb_len_wr = len;
}
blk_put_request(rq);
if (buffer) {
if (reading)
if (copy_to_user(hdr->dxferp, buffer, hdr->dxfer_len))
goto out_buffer;
kfree(buffer);
}
if (blk_rq_unmap_user(rq, hdr->dxferp, hdr->dxfer_len))
return -EFAULT;
/* may not have succeeded, but output values written to control
* structure (struct sg_io_hdr). */
return 0;
out_buffer:
kfree(buffer);
return -EFAULT;
}
#define FORMAT_UNIT_TIMEOUT (2 * 60 * 60 * HZ)
......@@ -369,7 +287,7 @@ static int sg_scsi_ioctl(request_queue_t *q, struct gendisk *bd_disk,
rq->data_len = bytes;
rq->flags |= REQ_BLOCK_PC;
blk_do_rq(q, bd_disk, rq);
blk_execute_rq(q, bd_disk, rq);
err = rq->errors & 0xff; /* only 8 bit SCSI status */
if (err) {
if (rq->sense_len && rq->sense) {
......@@ -447,6 +365,8 @@ int scsi_cmd_ioctl(struct gendisk *bd_disk, unsigned int cmd, unsigned long arg)
old_cdb = hdr.cmdp;
hdr.cmdp = cdb;
err = sg_io(q, bd_disk, &hdr);
if (err == -EFAULT)
break;
hdr.cmdp = old_cdb;
if (copy_to_user((struct sg_io_hdr *) arg, &hdr, sizeof(hdr)))
......@@ -457,10 +377,9 @@ int scsi_cmd_ioctl(struct gendisk *bd_disk, unsigned int cmd, unsigned long arg)
struct cdrom_generic_command cgc;
struct sg_io_hdr hdr;
if (copy_from_user(&cgc, (struct cdrom_generic_command *) arg, sizeof(cgc))) {
err = -EFAULT;
if (copy_from_user(&cgc, (struct cdrom_generic_command *) arg, sizeof(cgc)))
break;
}
cgc.timeout = clock_t_to_jiffies(cgc.timeout);
memset(&hdr, 0, sizeof(hdr));
hdr.interface_id = 'S';
......@@ -493,7 +412,10 @@ int scsi_cmd_ioctl(struct gendisk *bd_disk, unsigned int cmd, unsigned long arg)
hdr.timeout = cgc.timeout;
hdr.cmdp = cgc.cmd;
hdr.cmd_len = sizeof(cgc.cmd);
err = sg_io(q, bd_disk, &hdr);
if (err == -EFAULT)
break;
if (hdr.status)
err = -EIO;
......@@ -529,7 +451,7 @@ int scsi_cmd_ioctl(struct gendisk *bd_disk, unsigned int cmd, unsigned long arg)
rq->cmd[0] = GPCMD_START_STOP_UNIT;
rq->cmd[4] = 0x02 + (close != 0);
rq->cmd_len = 6;
err = blk_do_rq(q, bd_disk, rq);
err = blk_execute_rq(q, bd_disk, rq);
blk_put_request(rq);
break;
default:
......@@ -541,4 +463,3 @@ int scsi_cmd_ioctl(struct gendisk *bd_disk, unsigned int cmd, unsigned long arg)
}
EXPORT_SYMBOL(scsi_cmd_ioctl);
EXPORT_SYMBOL(scsi_command_size);
......@@ -517,6 +517,9 @@ extern void blk_stop_queue(request_queue_t *q);
extern void __blk_stop_queue(request_queue_t *q);
extern void blk_run_queue(request_queue_t *q);
extern void blk_queue_activity_fn(request_queue_t *, activity_fn *, void *);
extern struct request *blk_rq_map_user(request_queue_t *, int, void __user *, unsigned int);
extern int blk_rq_unmap_user(struct request *, void __user *, unsigned int);
extern int blk_execute_rq(request_queue_t *, struct gendisk *, struct request *);
static inline request_queue_t *bdev_get_queue(struct block_device *bdev)
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment