/* * BSG helper library * * Copyright (C) 2008 James Smart, Emulex Corporation * Copyright (C) 2011 Red Hat, Inc. All rights reserved. * Copyright (C) 2011 Mike Christie * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #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 * @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_end_request_all(rq, BLK_STS_OK); } void bsg_job_put(struct bsg_job *job) { k