Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 972248e9 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Martin K. Petersen
Browse files

scsi: bsg-lib: handle bidi requests without block layer help



We can just stash away the second request in struct bsg_job instead of
using the block layer req->next_rq field, allowing for the eventual removal
of the latter.

Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Acked-by: default avatarJens Axboe <axboe@kernel.dk>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent ccf3209f
Loading
Loading
Loading
Loading
+38 −6
Original line number Original line Diff line number Diff line
@@ -51,11 +51,40 @@ static int bsg_transport_fill_hdr(struct request *rq, struct sg_io_v4 *hdr,
		fmode_t mode)
		fmode_t mode)
{
{
	struct bsg_job *job = blk_mq_rq_to_pdu(rq);
	struct bsg_job *job = blk_mq_rq_to_pdu(rq);
	int ret;


	job->request_len = hdr->request_len;
	job->request_len = hdr->request_len;
	job->request = memdup_user(uptr64(hdr->request), hdr->request_len);
	job->request = memdup_user(uptr64(hdr->request), hdr->request_len);
	if (IS_ERR(job->request))
		return PTR_ERR(job->request);

	if (hdr->dout_xfer_len && hdr->din_xfer_len) {
		job->bidi_rq = blk_get_request(rq->q, REQ_OP_SCSI_IN, 0);
		if (IS_ERR(job->bidi_rq)) {
			ret = PTR_ERR(job->bidi_rq);
			goto out;
		}

		ret = blk_rq_map_user(rq->q, job->bidi_rq, NULL,
				uptr64(hdr->din_xferp), hdr->din_xfer_len,
				GFP_KERNEL);
		if (ret)
			goto out_free_bidi_rq;


	return PTR_ERR_OR_ZERO(job->request);
		job->bidi_bio = job->bidi_rq->bio;
	} else {
		job->bidi_rq = NULL;
		job->bidi_bio = NULL;
	}

	return 0;

out_free_bidi_rq:
	if (job->bidi_rq)
		blk_put_request(job->bidi_rq);
out:
	kfree(job->request);
	return ret;
}
}


static int bsg_transport_complete_rq(struct request *rq, struct sg_io_v4 *hdr)
static int bsg_transport_complete_rq(struct request *rq, struct sg_io_v4 *hdr)
@@ -93,7 +122,7 @@ static int bsg_transport_complete_rq(struct request *rq, struct sg_io_v4 *hdr)
	/* we assume all request payload was transferred, residual == 0 */
	/* we assume all request payload was transferred, residual == 0 */
	hdr->dout_resid = 0;
	hdr->dout_resid = 0;


	if (rq->next_rq) {
	if (job->bidi_rq) {
		unsigned int rsp_len = job->reply_payload.payload_len;
		unsigned int rsp_len = job->reply_payload.payload_len;


		if (WARN_ON(job->reply_payload_rcv_len > rsp_len))
		if (WARN_ON(job->reply_payload_rcv_len > rsp_len))
@@ -111,6 +140,11 @@ static void bsg_transport_free_rq(struct request *rq)
{
{
	struct bsg_job *job = blk_mq_rq_to_pdu(rq);
	struct bsg_job *job = blk_mq_rq_to_pdu(rq);


	if (job->bidi_rq) {
		blk_rq_unmap_user(job->bidi_bio);
		blk_put_request(job->bidi_rq);
	}

	kfree(job->request);
	kfree(job->request);
}
}


@@ -200,7 +234,6 @@ static int bsg_map_buffer(struct bsg_buffer *buf, struct request *req)
 */
 */
static bool 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 bsg_job *job = blk_mq_rq_to_pdu(req);
	struct bsg_job *job = blk_mq_rq_to_pdu(req);
	int ret;
	int ret;


@@ -211,8 +244,8 @@ static bool bsg_prepare_job(struct device *dev, struct request *req)
		if (ret)
		if (ret)
			goto failjob_rls_job;
			goto failjob_rls_job;
	}
	}
	if (rsp && rsp->bio) {
	if (job->bidi_rq) {
		ret = bsg_map_buffer(&job->reply_payload, rsp);
		ret = bsg_map_buffer(&job->reply_payload, job->bidi_rq);
		if (ret)
		if (ret)
			goto failjob_rls_rqst_payload;
			goto failjob_rls_rqst_payload;
	}
	}
@@ -369,7 +402,6 @@ struct request_queue *bsg_setup_queue(struct device *dev, const char *name,
	}
	}


	q->queuedata = dev;
	q->queuedata = dev;
	blk_queue_flag_set(QUEUE_FLAG_BIDI, q);
	blk_queue_rq_timeout(q, BLK_DEFAULT_SG_TIMEOUT);
	blk_queue_rq_timeout(q, BLK_DEFAULT_SG_TIMEOUT);


	ret = bsg_register_queue(q, dev, name, &bsg_transport_ops);
	ret = bsg_register_queue(q, dev, name, &bsg_transport_ops);
+14 −54
Original line number Original line Diff line number Diff line
@@ -74,6 +74,11 @@ static int bsg_scsi_fill_hdr(struct request *rq, struct sg_io_v4 *hdr,
{
{
	struct scsi_request *sreq = scsi_req(rq);
	struct scsi_request *sreq = scsi_req(rq);


	if (hdr->dout_xfer_len && hdr->din_xfer_len) {
		pr_warn_once("BIDI support in bsg has been removed.\n");
		return -EOPNOTSUPP;
	}

	sreq->cmd_len = hdr->request_len;
	sreq->cmd_len = hdr->request_len;
	if (sreq->cmd_len > BLK_MAX_CDB) {
	if (sreq->cmd_len > BLK_MAX_CDB) {
		sreq->cmd = kzalloc(sreq->cmd_len, GFP_KERNEL);
		sreq->cmd = kzalloc(sreq->cmd_len, GFP_KERNEL);
@@ -114,14 +119,10 @@ static int bsg_scsi_complete_rq(struct request *rq, struct sg_io_v4 *hdr)
			hdr->response_len = len;
			hdr->response_len = len;
	}
	}


	if (rq->next_rq) {
	if (rq_data_dir(rq) == READ)
		hdr->dout_resid = sreq->resid_len;
		hdr->din_resid = scsi_req(rq->next_rq)->resid_len;
	} else if (rq_data_dir(rq) == READ) {
		hdr->din_resid = sreq->resid_len;
		hdr->din_resid = sreq->resid_len;
	} else {
	else
		hdr->dout_resid = sreq->resid_len;
		hdr->dout_resid = sreq->resid_len;
	}


	return ret;
	return ret;
}
}
@@ -140,8 +141,8 @@ static const struct bsg_ops bsg_scsi_ops = {


static int bsg_sg_io(struct request_queue *q, fmode_t mode, void __user *uarg)
static int bsg_sg_io(struct request_queue *q, fmode_t mode, void __user *uarg)
{
{
	struct request *rq, *next_rq = NULL;
	struct request *rq;
	struct bio *bio, *bidi_bio = NULL;
	struct bio *bio;
	struct sg_io_v4 hdr;
	struct sg_io_v4 hdr;
	int ret;
	int ret;


@@ -164,7 +165,7 @@ static int bsg_sg_io(struct request_queue *q, fmode_t mode, void __user *uarg)


	ret = q->bsg_dev.ops->fill_hdr(rq, &hdr, mode);
	ret = q->bsg_dev.ops->fill_hdr(rq, &hdr, mode);
	if (ret)
	if (ret)
		goto out;
		return ret;


	rq->timeout = msecs_to_jiffies(hdr.timeout);
	rq->timeout = msecs_to_jiffies(hdr.timeout);
	if (!rq->timeout)
	if (!rq->timeout)
@@ -174,29 +175,6 @@ static int bsg_sg_io(struct request_queue *q, fmode_t mode, void __user *uarg)
	if (rq->timeout < BLK_MIN_SG_TIMEOUT)
	if (rq->timeout < BLK_MIN_SG_TIMEOUT)
		rq->timeout = BLK_MIN_SG_TIMEOUT;
		rq->timeout = BLK_MIN_SG_TIMEOUT;


	if (hdr.dout_xfer_len && hdr.din_xfer_len) {
		if (!test_bit(QUEUE_FLAG_BIDI, &q->queue_flags)) {
			ret = -EOPNOTSUPP;
			goto out;
		}

		pr_warn_once(
			"BIDI support in bsg has been deprecated and might be removed. "
			"Please report your use case to linux-scsi@vger.kernel.org\n");

		next_rq = blk_get_request(q, REQ_OP_SCSI_IN, 0);
		if (IS_ERR(next_rq)) {
			ret = PTR_ERR(next_rq);
			goto out;
		}

		rq->next_rq = next_rq;
		ret = blk_rq_map_user(q, next_rq, NULL, uptr64(hdr.din_xferp),
				       hdr.din_xfer_len, GFP_KERNEL);
		if (ret)
			goto out_free_nextrq;
	}

	if (hdr.dout_xfer_len) {
	if (hdr.dout_xfer_len) {
		ret = blk_rq_map_user(q, rq, NULL, uptr64(hdr.dout_xferp),
		ret = blk_rq_map_user(q, rq, NULL, uptr64(hdr.dout_xferp),
				hdr.dout_xfer_len, GFP_KERNEL);
				hdr.dout_xfer_len, GFP_KERNEL);
@@ -206,38 +184,20 @@ static int bsg_sg_io(struct request_queue *q, fmode_t mode, void __user *uarg)
	}
	}


	if (ret)
	if (ret)
		goto out_unmap_nextrq;
		goto out_free_rq;


	bio = rq->bio;
	bio = rq->bio;
	if (rq->next_rq)
		bidi_bio = rq->next_rq->bio;


	blk_execute_rq(q, NULL, rq, !(hdr.flags & BSG_FLAG_Q_AT_TAIL));
	blk_execute_rq(q, NULL, rq, !(hdr.flags & BSG_FLAG_Q_AT_TAIL));
	ret = rq->q->bsg_dev.ops->complete_rq(rq, &hdr);
	ret = rq->q->bsg_dev.ops->complete_rq(rq, &hdr);

	if (rq->next_rq) {
		blk_rq_unmap_user(bidi_bio);
		blk_put_request(rq->next_rq);
	}

	blk_rq_unmap_user(bio);
	blk_rq_unmap_user(bio);

out_free_rq:
	rq->q->bsg_dev.ops->free_rq(rq);
	rq->q->bsg_dev.ops->free_rq(rq);
	blk_put_request(rq);
	blk_put_request(rq);

	if (!ret && copy_to_user(uarg, &hdr, sizeof(hdr)))
	if (copy_to_user(uarg, &hdr, sizeof(hdr)))
		return -EFAULT;
		return -EFAULT;
	return ret;
	return ret;

out_unmap_nextrq:
	if (rq->next_rq)
		blk_rq_unmap_user(rq->next_rq->bio);
out_free_nextrq:
	if (rq->next_rq)
		blk_put_request(rq->next_rq);
out:
	q->bsg_dev.ops->free_rq(rq);
	blk_put_request(rq);
	return ret;
}
}


static struct bsg_device *bsg_alloc_device(void)
static struct bsg_device *bsg_alloc_device(void)
+0 −1
Original line number Original line Diff line number Diff line
@@ -213,7 +213,6 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy)
		to_sas_host_attrs(shost)->q = q;
		to_sas_host_attrs(shost)->q = q;
	}
	}


	blk_queue_flag_set(QUEUE_FLAG_BIDI, q);
	return 0;
	return 0;
}
}


+4 −0
Original line number Original line Diff line number Diff line
@@ -69,6 +69,10 @@ struct bsg_job {
	int result;
	int result;
	unsigned int reply_payload_rcv_len;
	unsigned int reply_payload_rcv_len;


	/* BIDI support */
	struct request *bidi_rq;
	struct bio *bidi_bio;

	void *dd_data;		/* Used for driver-specific storage */
	void *dd_data;		/* Used for driver-specific storage */
};
};