summaryrefslogtreecommitdiffstats
path: root/block/bsg-lib.c
diff options
context:
space:
mode:
Diffstat (limited to 'block/bsg-lib.c')
-rw-r--r--block/bsg-lib.c158
1 files changed, 105 insertions, 53 deletions
diff --git a/block/bsg-lib.c b/block/bsg-lib.c
index f2c2d54a61b4..fc2e5ff2c4b9 100644
--- a/block/bsg-lib.c
+++ b/block/bsg-lib.c
@@ -27,6 +27,94 @@
#include <linux/bsg-lib.h>
#include <linux/export.h>
#include <scsi/scsi_cmnd.h>
+#include <scsi/sg.h>
+
+#define uptr64(val) ((void __user *)(uintptr_t)(val))
+
+static int bsg_transport_check_proto(struct sg_io_v4 *hdr)
+{
+ if (hdr->protocol != BSG_PROTOCOL_SCSI ||
+ hdr->subprotocol != BSG_SUB_PROTOCOL_SCSI_TRANSPORT)
+ return -EINVAL;
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+ return 0;
+}
+
+static int bsg_transport_fill_hdr(struct request *rq, struct sg_io_v4 *hdr,
+ fmode_t mode)
+{
+ struct bsg_job *job = blk_mq_rq_to_pdu(rq);
+
+ job->request_len = hdr->request_len;
+ job->request = memdup_user(uptr64(hdr->request), hdr->request_len);
+ if (IS_ERR(job->request))
+ return PTR_ERR(job->request);
+ return 0;
+}
+
+static int bsg_transport_complete_rq(struct request *rq, struct sg_io_v4 *hdr)
+{
+ struct bsg_job *job = blk_mq_rq_to_pdu(rq);
+ int ret = 0;
+
+ /*
+ * The assignments below don't make much sense, but are kept for
+ * bug by bug backwards compatibility:
+ */
+ hdr->device_status = job->result & 0xff;
+ hdr->transport_status = host_byte(job->result);
+ hdr->driver_status = driver_byte(job->result);
+ hdr->info = 0;
+ if (hdr->device_status || hdr->transport_status || hdr->driver_status)
+ hdr->info |= SG_INFO_CHECK;
+ hdr->response_len = 0;
+
+ if (job->result < 0) {
+ /* we're only returning the result field in the reply */
+ job->reply_len = sizeof(u32);
+ ret = job->result;
+ }
+
+ if (job->reply_len && hdr->response) {
+ int len = min(hdr->max_response_len, job->reply_len);
+
+ if (copy_to_user(uptr64(hdr->response), job->reply, len))
+ ret = -EFAULT;
+ else
+ hdr->response_len = len;
+ }
+
+ /* we assume all request payload was transferred, residual == 0 */
+ hdr->dout_resid = 0;
+
+ if (rq->next_rq) {
+ unsigned int rsp_len = job->reply_payload.payload_len;
+
+ if (WARN_ON(job->reply_payload_rcv_len > rsp_len))
+ hdr->din_resid = 0;
+ else
+ hdr->din_resid = rsp_len - job->reply_payload_rcv_len;
+ } else {
+ hdr->din_resid = 0;
+ }
+
+ return ret;
+}
+
+static void bsg_transport_free_rq(struct request *rq)
+{
+ struct bsg_job *job = blk_mq_rq_to_pdu(rq);
+
+ kfree(job->request);
+}
+
+static const struct bsg_ops bsg_transport_ops = {
+ .check_proto = bsg_transport_check_proto,
+ .fill_hdr = bsg_transport_fill_hdr,
+ .complete_rq = bsg_transport_complete_rq,
+ .free_rq = bsg_transport_free_rq,
+};
/**
* bsg_teardown_job - routine to teardown a bsg job
@@ -68,27 +156,9 @@ EXPORT_SYMBOL_GPL(bsg_job_get);
void bsg_job_done(struct bsg_job *job, int result,
unsigned int reply_payload_rcv_len)
{
- struct request *req = blk_mq_rq_from_pdu(job);
- struct request *rsp = req->next_rq;
- int err;
-
- err = job->sreq.result = result;
- if (err < 0)
- /* we're only returning the result field in the reply */
- job->sreq.sense_len = sizeof(u32);
- else
- job->sreq.sense_len = job->reply_len;
- /* we assume all request payload was transferred, residual == 0 */
- job->sreq.resid_len = 0;
-
- if (rsp) {
- WARN_ON(reply_payload_rcv_len > scsi_req(rsp)->resid_len);
-
- /* set reply (bidi) residual */
- scsi_req(rsp)->resid_len -=
- min(reply_payload_rcv_len, scsi_req(rsp)->resid_len);
- }
- blk_complete_request(req);
+ job->result = result;
+ job->reply_payload_rcv_len = reply_payload_rcv_len;
+ blk_complete_request(blk_mq_rq_from_pdu(job));
}
EXPORT_SYMBOL_GPL(bsg_job_done);
@@ -113,7 +183,6 @@ static int bsg_map_buffer(struct bsg_buffer *buf, struct request *req)
if (!buf->sg_list)
return -ENOMEM;
sg_init_table(buf->sg_list, req->nr_phys_segments);
- scsi_req(req)->resid_len = blk_rq_bytes(req);
buf->sg_cnt = blk_rq_map_sg(req->q, req, buf->sg_list);
buf->payload_len = blk_rq_bytes(req);
return 0;
@@ -124,16 +193,13 @@ static int bsg_map_buffer(struct bsg_buffer *buf, struct request *req)
* @dev: device that is being sent the bsg request
* @req: BSG request that needs a job structure
*/
-static int bsg_prepare_job(struct device *dev, struct request *req)
+static bool bsg_prepare_job(struct device *dev, struct request *req)
{
struct request *rsp = req->next_rq;
- struct scsi_request *rq = scsi_req(req);
struct bsg_job *job = blk_mq_rq_to_pdu(req);
int ret;
job->timeout = req->timeout;
- job->request = rq->cmd;
- job->request_len = rq->cmd_len;
if (req->bio) {
ret = bsg_map_buffer(&job->request_payload, req);
@@ -149,12 +215,13 @@ static int bsg_prepare_job(struct device *dev, struct request *req)
/* take a reference for the request */
get_device(job->dev);
kref_init(&job->kref);
- return 0;
+ return true;
failjob_rls_rqst_payload:
kfree(job->request_payload.sg_list);
failjob_rls_job:
- return -ENOMEM;
+ job->result = -ENOMEM;
+ return false;
}
/**
@@ -183,9 +250,7 @@ static void bsg_request_fn(struct request_queue *q)
break;
spin_unlock_irq(q->queue_lock);
- ret = bsg_prepare_job(dev, req);
- if (ret) {
- scsi_req(req)->result = ret;
+ if (!bsg_prepare_job(dev, req)) {
blk_end_request_all(req, BLK_STS_OK);
spin_lock_irq(q->queue_lock);
continue;
@@ -202,46 +267,34 @@ static void bsg_request_fn(struct request_queue *q)
spin_lock_irq(q->queue_lock);
}
+/* called right after the request is allocated for the request_queue */
static int bsg_init_rq(struct request_queue *q, struct request *req, gfp_t gfp)
{
struct bsg_job *job = blk_mq_rq_to_pdu(req);
- struct scsi_request *sreq = &job->sreq;
-
- /* called right after the request is allocated for the request_queue */
- sreq->sense = kzalloc(SCSI_SENSE_BUFFERSIZE, gfp);
- if (!sreq->sense)
+ job->reply = kzalloc(SCSI_SENSE_BUFFERSIZE, gfp);
+ if (!job->reply)
return -ENOMEM;
-
return 0;
}
+/* called right before the request is given to the request_queue user */
static void bsg_initialize_rq(struct request *req)
{
struct bsg_job *job = blk_mq_rq_to_pdu(req);
- struct scsi_request *sreq = &job->sreq;
- void *sense = sreq->sense;
-
- /* called right before the request is given to the request_queue user */
+ void *reply = job->reply;
memset(job, 0, sizeof(*job));
-
- scsi_req_init(sreq);
-
- sreq->sense = sense;
- sreq->sense_len = SCSI_SENSE_BUFFERSIZE;
-
- job->reply = sense;
- job->reply_len = sreq->sense_len;
+ job->reply = reply;
+ job->reply_len = SCSI_SENSE_BUFFERSIZE;
job->dd_data = job + 1;
}
static void bsg_exit_rq(struct request_queue *q, struct request *req)
{
struct bsg_job *job = blk_mq_rq_to_pdu(req);
- struct scsi_request *sreq = &job->sreq;
- kfree(sreq->sense);
+ kfree(job->reply);
}
/**
@@ -275,11 +328,10 @@ struct request_queue *bsg_setup_queue(struct device *dev, const char *name,
q->queuedata = dev;
q->bsg_job_fn = job_fn;
blk_queue_flag_set(QUEUE_FLAG_BIDI, q);
- blk_queue_flag_set(QUEUE_FLAG_SCSI_PASSTHROUGH, q);
blk_queue_softirq_done(q, bsg_softirq_done);
blk_queue_rq_timeout(q, BLK_DEFAULT_SG_TIMEOUT);
- ret = bsg_register_queue(q, dev, name, release);
+ ret = bsg_register_queue(q, dev, name, &bsg_transport_ops, release);
if (ret) {
printk(KERN_ERR "%s: bsg interface failed to "
"initialize - register queue\n", dev->kobj.name);