forked from grate-driver/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This moves the FC classes bsg code to the block layer and makes it a lib so that other classes like iscsi and SAS can use it. It is helpful because working with the request queue, bios, creating scatterlists, etc are a pain that the LLD does not have to worry about with normal IOs and should not have to worry about for bsg requests. Signed-off-by: Mike Christie <michaelc@cs.wisc.edu> Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
- Loading branch information
Mike Christie
authored and
Jens Axboe
committed
Jul 31, 2011
1 parent
24c3047
commit aa387cc
Showing
5 changed files
with
385 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,297 @@ | ||
/* | ||
* 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 <linux/slab.h> | ||
#include <linux/blkdev.h> | ||
#include <linux/delay.h> | ||
#include <linux/scatterlist.h> | ||
#include <linux/bsg-lib.h> | ||
#include <scsi/scsi_cmnd.h> | ||
|
||
/** | ||
* bsg_destroy_job - routine to teardown/delete a bsg job | ||
* @job: bsg_job that is to be torn down | ||
*/ | ||
static void bsg_destroy_job(struct bsg_job *job) | ||
{ | ||
put_device(job->dev); /* release reference for the request */ | ||
|
||
kfree(job->request_payload.sg_list); | ||
kfree(job->reply_payload.sg_list); | ||
kfree(job); | ||
} | ||
|
||
/** | ||
* 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 *req = job->req; | ||
struct request *rsp = req->next_rq; | ||
int err; | ||
|
||
err = job->req->errors = result; | ||
if (err < 0) | ||
/* we're only returning the result field in the reply */ | ||
job->req->sense_len = sizeof(u32); | ||
else | ||
job->req->sense_len = job->reply_len; | ||
/* we assume all request payload was transferred, residual == 0 */ | ||
req->resid_len = 0; | ||
|
||
if (rsp) { | ||
WARN_ON(reply_payload_rcv_len > rsp->resid_len); | ||
|
||
/* set reply (bidi) residual */ | ||
rsp->resid_len -= min(reply_payload_rcv_len, rsp->resid_len); | ||
} | ||
blk_complete_request(req); | ||
} | ||
EXPORT_SYMBOL_GPL(bsg_job_done); | ||
|
||
/** | ||
* bsg_softirq_done - softirq done routine for destroying the bsg requests | ||
* @rq: BSG request that holds the job to be destroyed | ||
*/ | ||
static void bsg_softirq_done(struct request *rq) | ||
{ | ||
struct bsg_job *job = rq->special; | ||
|
||
blk_end_request_all(rq, rq->errors); | ||
bsg_destroy_job(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 = kzalloc(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->q, req, buf->sg_list); | ||
buf->payload_len = blk_rq_bytes(req); | ||
return 0; | ||
} | ||
|
||
/** | ||
* bsg_create_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 int bsg_create_job(struct device *dev, struct request *req) | ||
{ | ||
struct request *rsp = req->next_rq; | ||
struct request_queue *q = req->q; | ||
struct bsg_job *job; | ||
int ret; | ||
|
||
BUG_ON(req->special); | ||
|
||
job = kzalloc(sizeof(struct bsg_job) + q->bsg_job_size, GFP_KERNEL); | ||
if (!job) | ||
return -ENOMEM; | ||
|
||
req->special = job; | ||
job->req = req; | ||
if (q->bsg_job_size) | ||
job->dd_data = (void *)&job[1]; | ||
job->request = req->cmd; | ||
job->request_len = req->cmd_len; | ||
job->reply = req->sense; | ||
job->reply_len = SCSI_SENSE_BUFFERSIZE; /* Size of sense buffer | ||
* allocated */ | ||
if (req->bio) { | ||
ret = bsg_map_buffer(&job->request_payload, req); | ||
if (ret) | ||
goto failjob_rls_job; | ||
} | ||
if (rsp && rsp->bio) { | ||
ret = bsg_map_buffer(&job->reply_payload, rsp); | ||
if (ret) | ||
goto failjob_rls_rqst_payload; | ||
} | ||
job->dev = dev; | ||
/* take a reference for the request */ | ||
get_device(job->dev); | ||
return 0; | ||
|
||
failjob_rls_rqst_payload: | ||
kfree(job->request_payload.sg_list); | ||
failjob_rls_job: | ||
kfree(job); | ||
return -ENOMEM; | ||
} | ||
|
||
/* | ||
* bsg_goose_queue - restart queue in case it was stopped | ||
* @q: request q to be restarted | ||
*/ | ||
void bsg_goose_queue(struct request_queue *q) | ||
{ | ||
if (!q) | ||
return; | ||
|
||
blk_run_queue_async(q); | ||
} | ||
EXPORT_SYMBOL_GPL(bsg_goose_queue); | ||
|
||
/** | ||
* bsg_request_fn - generic handler for bsg requests | ||
* @q: request queue to manage | ||
* | ||
* On error the create_bsg_job function should return a -Exyz error value | ||
* that will be set to the req->errors. | ||
* | ||
* Drivers/subsys should pass this to the queue init function. | ||
*/ | ||
void bsg_request_fn(struct request_queue *q) | ||
{ | ||
struct device *dev = q->queuedata; | ||
struct request *req; | ||
struct bsg_job *job; | ||
int ret; | ||
|
||
if (!get_device(dev)) | ||
return; | ||
|
||
while (1) { | ||
req = blk_fetch_request(q); | ||
if (!req) | ||
break; | ||
spin_unlock_irq(q->queue_lock); | ||
|
||
ret = bsg_create_job(dev, req); | ||
if (ret) { | ||
req->errors = ret; | ||
blk_end_request_all(req, ret); | ||
spin_lock_irq(q->queue_lock); | ||
continue; | ||
} | ||
|
||
job = req->special; | ||
ret = q->bsg_job_fn(job); | ||
spin_lock_irq(q->queue_lock); | ||
if (ret) | ||
break; | ||
} | ||
|
||
spin_unlock_irq(q->queue_lock); | ||
put_device(dev); | ||
spin_lock_irq(q->queue_lock); | ||
} | ||
EXPORT_SYMBOL_GPL(bsg_request_fn); | ||
|
||
/** | ||
* bsg_setup_queue - Create and add the bsg hooks so we can receive requests | ||
* @dev: device to attach bsg device to | ||
* @q: request queue setup by caller | ||
* @name: device to give bsg device | ||
* @job_fn: bsg job handler | ||
* @dd_job_size: size of LLD data needed for each job | ||
* | ||
* The caller should have setup the reuqest queue with bsg_request_fn | ||
* as the request_fn. | ||
*/ | ||
int bsg_setup_queue(struct device *dev, struct request_queue *q, | ||
char *name, bsg_job_fn *job_fn, int dd_job_size) | ||
{ | ||
int ret; | ||
|
||
q->queuedata = dev; | ||
q->bsg_job_size = dd_job_size; | ||
q->bsg_job_fn = job_fn; | ||
queue_flag_set_unlocked(QUEUE_FLAG_BIDI, 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, NULL); | ||
if (ret) { | ||
printk(KERN_ERR "%s: bsg interface failed to " | ||
"initialize - register queue\n", dev->kobj.name); | ||
return ret; | ||
} | ||
|
||
return 0; | ||
} | ||
EXPORT_SYMBOL_GPL(bsg_setup_queue); | ||
|
||
/** | ||
* bsg_remove_queue - Deletes the bsg dev from the q | ||
* @q: the request_queue that is to be torn down. | ||
* | ||
* Notes: | ||
* Before unregistering the queue empty any requests that are blocked | ||
*/ | ||
void bsg_remove_queue(struct request_queue *q) | ||
{ | ||
struct request *req; /* block request */ | ||
int counts; /* totals for request_list count and starved */ | ||
|
||
if (!q) | ||
return; | ||
|
||
/* Stop taking in new requests */ | ||
spin_lock_irq(q->queue_lock); | ||
blk_stop_queue(q); | ||
|
||
/* drain all requests in the queue */ | ||
while (1) { | ||
/* need the lock to fetch a request | ||
* this may fetch the same reqeust as the previous pass | ||
*/ | ||
req = blk_fetch_request(q); | ||
/* save requests in use and starved */ | ||
counts = q->rq.count[0] + q->rq.count[1] + | ||
q->rq.starved[0] + q->rq.starved[1]; | ||
spin_unlock_irq(q->queue_lock); | ||
/* any requests still outstanding? */ | ||
if (counts == 0) | ||
break; | ||
|
||
/* This may be the same req as the previous iteration, | ||
* always send the blk_end_request_all after a prefetch. | ||
* It is not okay to not end the request because the | ||
* prefetch started the request. | ||
*/ | ||
if (req) { | ||
/* return -ENXIO to indicate that this queue is | ||
* going away | ||
*/ | ||
req->errors = -ENXIO; | ||
blk_end_request_all(req, -ENXIO); | ||
} | ||
|
||
msleep(200); /* allow bsg to possibly finish */ | ||
spin_lock_irq(q->queue_lock); | ||
} | ||
bsg_unregister_queue(q); | ||
} | ||
EXPORT_SYMBOL_GPL(bsg_remove_queue); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.