mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-03-03 18:28:01 +01:00
This was done entirely with mindless brute force, using
git grep -l '\<k[vmz]*alloc_objs*(.*, GFP_KERNEL)' |
xargs sed -i 's/\(alloc_objs*(.*\), GFP_KERNEL)/\1)/'
to convert the new alloc_obj() users that had a simple GFP_KERNEL
argument to just drop that argument.
Note that due to the extreme simplicity of the scripting, any slightly
more complex cases spread over multiple lines would not be triggered:
they definitely exist, but this covers the vast bulk of the cases, and
the resulting diff is also then easier to check automatically.
For the same reason the 'flex' versions will be done as a separate
conversion.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
413 lines
9.9 KiB
C
413 lines
9.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* BSG helper library
|
|
*
|
|
* Copyright (C) 2008 James Smart, Emulex Corporation
|
|
* Copyright (C) 2011 Red Hat, Inc. All rights reserved.
|
|
* Copyright (C) 2011 Mike Christie
|
|
*/
|
|
#include <linux/bsg.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/blk-mq.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/scatterlist.h>
|
|
#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))
|
|
|
|
struct bsg_set {
|
|
struct blk_mq_tag_set tag_set;
|
|
struct bsg_device *bd;
|
|
bsg_job_fn *job_fn;
|
|
bsg_timeout_fn *timeout_fn;
|
|
};
|
|
|
|
static int bsg_transport_sg_io_fn(struct request_queue *q, struct sg_io_v4 *hdr,
|
|
bool open_for_write, unsigned int timeout)
|
|
{
|
|
struct bsg_job *job;
|
|
struct request *rq;
|
|
struct bio *bio;
|
|
void *reply;
|
|
int ret;
|
|
|
|
if (hdr->protocol != BSG_PROTOCOL_SCSI ||
|
|
hdr->subprotocol != BSG_SUB_PROTOCOL_SCSI_TRANSPORT)
|
|
return -EINVAL;
|
|
if (!capable(CAP_SYS_RAWIO))
|
|
return -EPERM;
|
|
|
|
rq = blk_mq_alloc_request(q, hdr->dout_xfer_len ?
|
|
REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0);
|
|
if (IS_ERR(rq))
|
|
return PTR_ERR(rq);
|
|
rq->timeout = timeout;
|
|
|
|
job = blk_mq_rq_to_pdu(rq);
|
|
reply = job->reply;
|
|
memset(job, 0, sizeof(*job));
|
|
job->reply = reply;
|
|
job->reply_len = SCSI_SENSE_BUFFERSIZE;
|
|
job->dd_data = job + 1;
|
|
|
|
job->request_len = hdr->request_len;
|
|
job->request = memdup_user(uptr64(hdr->request), hdr->request_len);
|
|
if (IS_ERR(job->request)) {
|
|
ret = PTR_ERR(job->request);
|
|
goto out_free_rq;
|
|
}
|
|
|
|
if (hdr->dout_xfer_len && hdr->din_xfer_len) {
|
|
job->bidi_rq = blk_mq_alloc_request(rq->q, REQ_OP_DRV_IN, 0);
|
|
if (IS_ERR(job->bidi_rq)) {
|
|
ret = PTR_ERR(job->bidi_rq);
|
|
goto out_free_job_request;
|
|
}
|
|
|
|
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;
|
|
|
|
job->bidi_bio = job->bidi_rq->bio;
|
|
} else {
|
|
job->bidi_rq = NULL;
|
|
job->bidi_bio = NULL;
|
|
}
|
|
|
|
ret = 0;
|
|
if (hdr->dout_xfer_len) {
|
|
ret = blk_rq_map_user(rq->q, rq, NULL, uptr64(hdr->dout_xferp),
|
|
hdr->dout_xfer_len, GFP_KERNEL);
|
|
} else if (hdr->din_xfer_len) {
|
|
ret = blk_rq_map_user(rq->q, rq, NULL, uptr64(hdr->din_xferp),
|
|
hdr->din_xfer_len, GFP_KERNEL);
|
|
}
|
|
|
|
if (ret)
|
|
goto out_unmap_bidi_rq;
|
|
|
|
bio = rq->bio;
|
|
blk_execute_rq(rq, !(hdr->flags & BSG_FLAG_Q_AT_TAIL));
|
|
|
|
/*
|
|
* 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 = 0;
|
|
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 (job->bidi_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;
|
|
}
|
|
|
|
blk_rq_unmap_user(bio);
|
|
out_unmap_bidi_rq:
|
|
if (job->bidi_rq)
|
|
blk_rq_unmap_user(job->bidi_bio);
|
|
out_free_bidi_rq:
|
|
if (job->bidi_rq)
|
|
blk_mq_free_request(job->bidi_rq);
|
|
out_free_job_request:
|
|
kfree(job->request);
|
|
out_free_rq:
|
|
blk_mq_free_request(rq);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* bsg_teardown_job - routine to teardown a bsg job
|
|
* @kref: kref inside bsg_job that is to be torn down
|
|
*/
|
|
static void bsg_teardown_job(struct kref *kref)
|
|
{
|
|
struct bsg_job *job = container_of(kref, struct bsg_job, kref);
|
|
struct request *rq = blk_mq_rq_from_pdu(job);
|
|
|
|
put_device(job->dev); /* release reference for the request */
|
|
|
|
kfree(job->request_payload.sg_list);
|
|
kfree(job->reply_payload.sg_list);
|
|
|
|
blk_mq_end_request(rq, BLK_STS_OK);
|
|
}
|
|
|
|
void bsg_job_put(struct bsg_job *job)
|
|
{
|
|
kref_put(&job->kref, bsg_teardown_job);
|
|
}
|
|
EXPORT_SYMBOL_GPL(bsg_job_put);
|
|
|
|
int bsg_job_get(struct bsg_job *job)
|
|
{
|
|
return kref_get_unless_zero(&job->kref);
|
|
}
|
|
EXPORT_SYMBOL_GPL(bsg_job_get);
|
|
|
|
/**
|
|
* bsg_job_done - completion routine for bsg requests
|
|
* @job: bsg_job that is complete
|
|
* @result: job reply result
|
|
* @reply_payload_rcv_len: length of payload recvd
|
|
*
|
|
* The LLD should call this when the bsg job has completed.
|
|
*/
|
|
void bsg_job_done(struct bsg_job *job, int result,
|
|
unsigned int reply_payload_rcv_len)
|
|
{
|
|
struct request *rq = blk_mq_rq_from_pdu(job);
|
|
|
|
job->result = result;
|
|
job->reply_payload_rcv_len = reply_payload_rcv_len;
|
|
if (likely(!blk_should_fake_timeout(rq->q)))
|
|
blk_mq_complete_request(rq);
|
|
}
|
|
EXPORT_SYMBOL_GPL(bsg_job_done);
|
|
|
|
/**
|
|
* bsg_complete - softirq done routine for destroying the bsg requests
|
|
* @rq: BSG request that holds the job to be destroyed
|
|
*/
|
|
static void bsg_complete(struct request *rq)
|
|
{
|
|
struct bsg_job *job = blk_mq_rq_to_pdu(rq);
|
|
|
|
bsg_job_put(job);
|
|
}
|
|
|
|
static int bsg_map_buffer(struct bsg_buffer *buf, struct request *req)
|
|
{
|
|
size_t sz = (sizeof(struct scatterlist) * req->nr_phys_segments);
|
|
|
|
BUG_ON(!req->nr_phys_segments);
|
|
|
|
buf->sg_list = kmalloc(sz, GFP_KERNEL);
|
|
if (!buf->sg_list)
|
|
return -ENOMEM;
|
|
sg_init_table(buf->sg_list, req->nr_phys_segments);
|
|
buf->sg_cnt = blk_rq_map_sg(req, buf->sg_list);
|
|
buf->payload_len = blk_rq_bytes(req);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* bsg_prepare_job - create the bsg_job structure for the bsg request
|
|
* @dev: device that is being sent the bsg request
|
|
* @req: BSG request that needs a job structure
|
|
*/
|
|
static bool bsg_prepare_job(struct device *dev, struct request *req)
|
|
{
|
|
struct bsg_job *job = blk_mq_rq_to_pdu(req);
|
|
int ret;
|
|
|
|
job->timeout = req->timeout;
|
|
|
|
if (req->bio) {
|
|
ret = bsg_map_buffer(&job->request_payload, req);
|
|
if (ret)
|
|
goto failjob_rls_job;
|
|
}
|
|
if (job->bidi_rq) {
|
|
ret = bsg_map_buffer(&job->reply_payload, job->bidi_rq);
|
|
if (ret)
|
|
goto failjob_rls_rqst_payload;
|
|
}
|
|
job->dev = dev;
|
|
/* take a reference for the request */
|
|
get_device(job->dev);
|
|
kref_init(&job->kref);
|
|
return true;
|
|
|
|
failjob_rls_rqst_payload:
|
|
kfree(job->request_payload.sg_list);
|
|
failjob_rls_job:
|
|
job->result = -ENOMEM;
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* bsg_queue_rq - generic handler for bsg requests
|
|
* @hctx: hardware queue
|
|
* @bd: queue data
|
|
*
|
|
* On error the create_bsg_job function should return a -Exyz error value
|
|
* that will be set to ->result.
|
|
*
|
|
* Drivers/subsys should pass this to the queue init function.
|
|
*/
|
|
static blk_status_t bsg_queue_rq(struct blk_mq_hw_ctx *hctx,
|
|
const struct blk_mq_queue_data *bd)
|
|
{
|
|
struct request_queue *q = hctx->queue;
|
|
struct device *dev = q->queuedata;
|
|
struct request *req = bd->rq;
|
|
struct bsg_set *bset =
|
|
container_of(q->tag_set, struct bsg_set, tag_set);
|
|
blk_status_t sts = BLK_STS_IOERR;
|
|
int ret;
|
|
|
|
blk_mq_start_request(req);
|
|
|
|
if (!get_device(dev))
|
|
return BLK_STS_IOERR;
|
|
|
|
if (!bsg_prepare_job(dev, req))
|
|
goto out;
|
|
|
|
ret = bset->job_fn(blk_mq_rq_to_pdu(req));
|
|
if (!ret)
|
|
sts = BLK_STS_OK;
|
|
|
|
out:
|
|
put_device(dev);
|
|
return sts;
|
|
}
|
|
|
|
/* called right after the request is allocated for the request_queue */
|
|
static int bsg_init_rq(struct blk_mq_tag_set *set, struct request *req,
|
|
unsigned int hctx_idx, unsigned int numa_node)
|
|
{
|
|
struct bsg_job *job = blk_mq_rq_to_pdu(req);
|
|
|
|
job->reply = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL);
|
|
if (!job->reply)
|
|
return -ENOMEM;
|
|
return 0;
|
|
}
|
|
|
|
static void bsg_exit_rq(struct blk_mq_tag_set *set, struct request *req,
|
|
unsigned int hctx_idx)
|
|
{
|
|
struct bsg_job *job = blk_mq_rq_to_pdu(req);
|
|
|
|
kfree(job->reply);
|
|
}
|
|
|
|
void bsg_remove_queue(struct request_queue *q)
|
|
{
|
|
if (q) {
|
|
struct bsg_set *bset =
|
|
container_of(q->tag_set, struct bsg_set, tag_set);
|
|
|
|
bsg_unregister_queue(bset->bd);
|
|
blk_mq_destroy_queue(q);
|
|
blk_put_queue(q);
|
|
blk_mq_free_tag_set(&bset->tag_set);
|
|
kfree(bset);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(bsg_remove_queue);
|
|
|
|
static enum blk_eh_timer_return bsg_timeout(struct request *rq)
|
|
{
|
|
struct bsg_set *bset =
|
|
container_of(rq->q->tag_set, struct bsg_set, tag_set);
|
|
|
|
if (!bset->timeout_fn)
|
|
return BLK_EH_DONE;
|
|
return bset->timeout_fn(rq);
|
|
}
|
|
|
|
static const struct blk_mq_ops bsg_mq_ops = {
|
|
.queue_rq = bsg_queue_rq,
|
|
.init_request = bsg_init_rq,
|
|
.exit_request = bsg_exit_rq,
|
|
.complete = bsg_complete,
|
|
.timeout = bsg_timeout,
|
|
};
|
|
|
|
/**
|
|
* bsg_setup_queue - Create and add the bsg hooks so we can receive requests
|
|
* @dev: device to attach bsg device to
|
|
* @name: device to give bsg device
|
|
* @lim: queue limits for the bsg queue
|
|
* @job_fn: bsg job handler
|
|
* @timeout: timeout handler function pointer
|
|
* @dd_job_size: size of LLD data needed for each job
|
|
*/
|
|
struct request_queue *bsg_setup_queue(struct device *dev, const char *name,
|
|
struct queue_limits *lim, bsg_job_fn *job_fn,
|
|
bsg_timeout_fn *timeout, int dd_job_size)
|
|
{
|
|
struct bsg_set *bset;
|
|
struct blk_mq_tag_set *set;
|
|
struct request_queue *q;
|
|
int ret = -ENOMEM;
|
|
|
|
bset = kzalloc_obj(*bset);
|
|
if (!bset)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
bset->job_fn = job_fn;
|
|
bset->timeout_fn = timeout;
|
|
|
|
set = &bset->tag_set;
|
|
set->ops = &bsg_mq_ops;
|
|
set->nr_hw_queues = 1;
|
|
set->queue_depth = 128;
|
|
set->numa_node = NUMA_NO_NODE;
|
|
set->cmd_size = sizeof(struct bsg_job) + dd_job_size;
|
|
set->flags = BLK_MQ_F_BLOCKING;
|
|
if (blk_mq_alloc_tag_set(set))
|
|
goto out_tag_set;
|
|
|
|
q = blk_mq_alloc_queue(set, lim, dev);
|
|
if (IS_ERR(q)) {
|
|
ret = PTR_ERR(q);
|
|
goto out_queue;
|
|
}
|
|
|
|
blk_queue_rq_timeout(q, BLK_DEFAULT_SG_TIMEOUT);
|
|
|
|
bset->bd = bsg_register_queue(q, dev, name, bsg_transport_sg_io_fn);
|
|
if (IS_ERR(bset->bd)) {
|
|
ret = PTR_ERR(bset->bd);
|
|
goto out_cleanup_queue;
|
|
}
|
|
|
|
return q;
|
|
out_cleanup_queue:
|
|
blk_mq_destroy_queue(q);
|
|
blk_put_queue(q);
|
|
out_queue:
|
|
blk_mq_free_tag_set(set);
|
|
out_tag_set:
|
|
kfree(bset);
|
|
return ERR_PTR(ret);
|
|
}
|
|
EXPORT_SYMBOL_GPL(bsg_setup_queue);
|