Fix for problem with 2.6.31 kernel, where blk_rq_append_bio() was un-exported. So, from now scst_exec_req_fifo is again required for pass-through handlers.

git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@1207 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2009-10-13 16:01:33 +00:00
parent 8d1f6d1528
commit 647638ebac
5 changed files with 46 additions and 423 deletions
+10 -13
View File
@@ -63,19 +63,16 @@ them are optional, so, if you don't need the corresponding
functionality, you may not apply them.
1. scst_exec_req_fifo-2.6.X.patch. This patch is necessary for
pass-through dev handlers with kernels <2.6.26, because in the them
scsi_do_req()/scsi_execute_async() work in LIFO order, instead of
expected and required FIFO. So SCST needs new functions
scsi_do_req_fifo() or scsi_execute_async_fifo() to be added in the
kernel. This patch does that. You may not patch the kernel if you don't
need pass-through support. Alternatively, you can define
CONFIG_SCST_STRICT_SERIALIZING compile option during the compilation
(see description below). This patch is optional for kernels starting
from 2.6.26. On those kernels pass-through will well work without it.
The patch for them created only for the mainline kernel inclusion.
(Actually, implementation on scst_scsi_exec_async(), which you can find
in scst_lib.c for kernels >=2.6.26, can work on the earlier kernels as
well, so you're welcome to backport it.)
pass-through dev handlers (scst_disk, scst_tape, etc.). Kernels <2.6.30
need it, because in them scsi_do_req()/scsi_execute_async() work in LIFO
order, instead of expected and required FIFO. So, SCST needs new
functions scsi_do_req_fifo() or scsi_execute_async_fifo() to be added in
the kernel. Kernels >=2.6.30 need it, because in them there is no
functionality to execute commands with data in scatter-gather buffers.
This patch adds the necessary functionality to the kernel. You may not
patch your kernel if you don't need pass-through support. Alternatively,
on kernels <2.6.30 you can define CONFIG_SCST_STRICT_SERIALIZING compile
option during the compilation (see description below).
2. io_context-2.6.X.patch. This patch exports some IO context management
functions from the kernel. For performance reasons SCST queues commands
+5 -399
View File
@@ -35,7 +35,7 @@
#include "scst_cdbprobe.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
struct scsi_io_context {
unsigned int full_cdb_used:1;
void *data;
@@ -2958,401 +2958,7 @@ out:
#endif /* !((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)) */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) && !((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED))
#include <linux/pfn.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
static inline int object_is_on_stack(void *obj)
{
void *stack = task_stack_page(current);
return (obj >= stack) && (obj < (stack + THREAD_SIZE));
}
#endif
struct blk_kern_sg_work {
atomic_t bios_inflight;
struct sg_table sg_table;
struct scatterlist *src_sgl;
};
static void blk_rq_unmap_kern_sg(struct request *rq, int err);
static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
{
TRACE_DBG("Freeing bw %p", bw);
sg_free_table(&bw->sg_table);
kfree(bw);
return;
}
static void blk_bio_map_kern_endio(struct bio *bio, int err)
{
struct blk_kern_sg_work *bw = bio->bi_private;
TRACE_DBG("bio %p finished", bio);
if (bw != NULL) {
/* Decrement the bios in processing and, if zero, free */
BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
if (atomic_dec_and_test(&bw->bios_inflight)) {
TRACE_DBG("sgl %p, new_sgl %p, new_sgl_nents %d",
bw->src_sgl, bw->sg_table.sgl,
bw->sg_table.nents);
if ((bio_data_dir(bio) == READ) && (err == 0)) {
unsigned long flags;
TRACE_DBG("Copying sgl %p (nents %d) to "
"orig_sgl %p", bw->sg_table.sgl,
bw->sg_table.nents, bw->src_sgl);
local_irq_save(flags); /* to protect KMs */
sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
local_irq_restore(flags);
}
blk_free_kern_sg_work(bw);
}
}
bio_put(bio);
return;
}
static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
int nents, struct blk_kern_sg_work **pbw,
gfp_t gfp, gfp_t page_gfp)
{
int res = 0, i;
struct scatterlist *sg;
struct scatterlist *new_sgl;
int new_sgl_nents;
size_t len = 0, to_copy;
struct blk_kern_sg_work *bw;
bw = kzalloc(sizeof(*bw), gfp);
if (bw == NULL) {
PRINT_ERROR("%s", "Unable to alloc blk_kern_sg_work");
goto out;
}
bw->src_sgl = sgl;
for_each_sg(sgl, sg, nents, i)
len += sg->length;
to_copy = len;
new_sgl_nents = PFN_UP(len);
res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
if (res != 0) {
PRINT_ERROR("Unable to alloc copy sg table (nents %d)",
new_sgl_nents);
goto out_free_bw;
}
new_sgl = bw->sg_table.sgl;
TRACE_DBG("sgl %p, nents %d, to_copy %lld, new_sgl %p, new_sgl_nents %d",
sgl, nents, (long long)to_copy, new_sgl, new_sgl_nents);
for_each_sg(new_sgl, sg, new_sgl_nents, i) {
struct page *pg;
pg = alloc_page(page_gfp);
if (pg == NULL) {
PRINT_ERROR("Unable to alloc copy page (left %lld)",
(long long)len);
goto err_free_new_sgl;
}
sg_assign_page(sg, pg);
sg->length = min_t(size_t, PAGE_SIZE, len);
len -= PAGE_SIZE;
}
if (rq_data_dir(rq) == WRITE) {
/*
* We need to limit amount of copied data to to_copy, because
* sgl might have the last element in sgl not marked as last in
* SG chaining.
*/
TRACE_DBG("Copying sgl %p (nents %d) to new_sgl %p "
"(new_sgl_nents %d), to_copy %lld", sgl, nents,
new_sgl, new_sgl_nents, (long long)to_copy);
sg_copy(new_sgl, sgl, 0, to_copy,
KM_USER0, KM_USER1);
}
*pbw = bw;
/*
* REQ_COPY_USER name is misleading. It should be something like
* REQ_HAS_TAIL_SPACE_FOR_PADDING.
*/
rq->cmd_flags |= REQ_COPY_USER;
out:
return res;
err_free_new_sgl:
for_each_sg(new_sgl, sg, new_sgl_nents, i) {
struct page *pg = sg_page(sg);
if (pg == NULL)
break;
__free_page(pg);
}
sg_free_table(&bw->sg_table);
out_free_bw:
kfree(bw);
res = -ENOMEM;
goto out;
}
static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
{
int res = 0;
struct request_queue *q = rq->q;
int rw = rq_data_dir(rq);
int max_nr_vecs, i;
size_t tot_len;
bool need_new_bio;
struct scatterlist *sg, *prev_sg = NULL;
struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
int bios;
if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
WARN_ON(1);
res = -EINVAL;
goto out;
}
/*
* Let's keep each bio allocation inside a single page to decrease
* probability of failure.
*/
max_nr_vecs = min_t(size_t,
((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
BIO_MAX_PAGES);
TRACE_DBG("sgl %p, nents %d, bw %p, max_nr_vecs %d", sgl, nents, bw,
max_nr_vecs);
need_new_bio = true;
tot_len = 0;
bios = 0;
for_each_sg(sgl, sg, nents, i) {
struct page *page = sg_page(sg);
void *page_addr = page_address(page);
size_t len = sg->length, l;
size_t offset = sg->offset;
tot_len += len;
prev_sg = sg;
/*
* Each segment must be aligned on DMA boundary and
* not on stack. The last one may have unaligned
* length as long as the total length is aligned to
* DMA padding alignment.
*/
if (i == nents - 1)
l = 0;
else
l = len;
if (((sg->offset | l) & queue_dma_alignment(q)) ||
(page_addr && object_is_on_stack(page_addr + sg->offset))) {
TRACE_DBG("%s", "DMA alignment or offset don't match");
res = -EINVAL;
goto out_free_bios;
}
while (len > 0) {
size_t bytes;
int rc;
if (need_new_bio) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
bio = bio_kmalloc(gfp, max_nr_vecs);
#else
bio = bio_alloc(gfp, max_nr_vecs);
#endif
if (bio == NULL) {
PRINT_ERROR("%s", "Can't to alloc bio");
res = -ENOMEM;
goto out_free_bios;
}
TRACE_DBG("bio %p alloced", bio);
if (rw == WRITE)
bio->bi_rw |= 1 << BIO_RW;
bios++;
bio->bi_private = bw;
bio->bi_end_io = blk_bio_map_kern_endio;
if (hbio == NULL)
hbio = tbio = bio;
else
tbio = tbio->bi_next = bio;
}
bytes = min_t(size_t, len, PAGE_SIZE - offset);
rc = bio_add_pc_page(q, bio, page, bytes, offset);
if (rc < bytes) {
if (unlikely(need_new_bio || (rc < 0))) {
if (rc < 0)
res = rc;
else
res = -EIO;
PRINT_ERROR("bio_add_pc_page() failed: "
"%d", rc);
goto out_free_bios;
} else {
need_new_bio = true;
len -= rc;
offset += rc;
continue;
}
}
need_new_bio = false;
offset = 0;
len -= bytes;
page = nth_page(page, 1);
}
}
if (hbio == NULL) {
res = -EINVAL;
goto out_free_bios;
}
/* Total length must be aligned on DMA padding alignment */
if ((tot_len & q->dma_pad_mask) &&
!(rq->cmd_flags & REQ_COPY_USER)) {
TRACE_DBG("Total len %lld doesn't match DMA pad mask %x",
(long long)tot_len, q->dma_pad_mask);
res = -EINVAL;
goto out_free_bios;
}
if (bw != NULL)
atomic_set(&bw->bios_inflight, bios);
while (hbio != NULL) {
bio = hbio;
hbio = hbio->bi_next;
bio->bi_next = NULL;
blk_queue_bounce(q, &bio);
res = blk_rq_append_bio(q, rq, bio);
if (unlikely(res != 0)) {
PRINT_ERROR("blk_rq_append_bio() failed: %d", res);
bio->bi_next = hbio;
hbio = bio;
/* We can have one or more bios bounced */
goto out_unmap_bios;
}
}
rq->buffer = NULL;
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30)
rq->data = NULL;
#endif
out:
return res;
out_free_bios:
while (hbio != NULL) {
bio = hbio;
hbio = hbio->bi_next;
bio_put(bio);
}
goto out;
out_unmap_bios:
blk_rq_unmap_kern_sg(rq, res);
goto out;
}
/**
* blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
* @rq: request to fill
* @sgl: area to map
* @nents: number of elements in @sgl
* @gfp: memory allocation flags
*
* Description:
* Data will be mapped directly if possible. Otherwise a bounce
* buffer will be used.
*/
static int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
int nents, gfp_t gfp)
{
int res;
res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
if (unlikely(res != 0)) {
struct blk_kern_sg_work *bw = NULL;
TRACE_DBG("__blk_rq_map_kern_sg() failed: %d", res);
res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
gfp, rq->q->bounce_gfp | gfp);
if (unlikely(res != 0))
goto out;
res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
bw->sg_table.nents, bw, gfp);
if (res != 0) {
TRACE_DBG("Copied __blk_rq_map_kern_sg() failed: %d",
res);
blk_free_kern_sg_work(bw);
goto out;
}
}
rq->buffer = NULL;
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30)
rq->data = NULL;
#endif
out:
return res;
}
/**
* blk_rq_unmap_kern_sg - unmap a request with kernel sg
* @rq: request to unmap
* @err: non-zero error code
*
* Description:
* Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
* only in case of an error!
*/
static void blk_rq_unmap_kern_sg(struct request *rq, int err)
{
struct bio *bio = rq->bio;
while (bio) {
struct bio *b = bio;
bio = bio->bi_next;
b->bi_end_io(b, err);
}
rq->bio = NULL;
return;
}
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) && !(LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)) */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)
static void scsi_end_async(struct request *req, int error)
{
struct scsi_io_context *sioc = req->end_io_data;
@@ -3496,7 +3102,7 @@ out_free_sioc:
goto out;
}
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) */
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) && defined(SCSI_EXEC_REQ_FIFO_DEFINED) */
void scst_copy_sg(struct scst_cmd *cmd, enum scst_sg_copy_dir copy_dir)
{
@@ -5361,7 +4967,7 @@ int __init scst_lib_init(void)
scst_scsi_op_list_init();
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
scsi_io_context_cache = kmem_cache_create("scst_scsi_io_context",
sizeof(struct scsi_io_context),
0, 0, NULL);
@@ -5379,7 +4985,7 @@ out:
void scst_lib_exit(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
BUILD_BUG_ON(SCST_MAX_CDB_SIZE != BLK_MAX_CDB);
BUILD_BUG_ON(SCST_SENSE_BUFFERSIZE < SCSI_SENSE_BUFFERSIZE);
+18 -7
View File
@@ -40,13 +40,18 @@
details."
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) && !defined(SCSI_EXEC_REQ_FIFO_DEFINED)
#if !defined(SCSI_EXEC_REQ_FIFO_DEFINED)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
#if !defined(CONFIG_SCST_STRICT_SERIALIZING)
#warning "Patch scst_exec_req_fifo-<kernel-version> was not applied on\
your kernel and CONFIG_SCST_STRICT_SERIALIZING isn't defined.\
Pass-through dev handlers will not work."
#endif
#endif
#endif /* !defined(CONFIG_SCST_STRICT_SERIALIZING) */
#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) */
#warning "Patch scst_exec_req_fifo-<kernel-version> was not applied on\
your kernel. Pass-through dev handlers will not work."
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) */
#endif /* !defined(SCSI_EXEC_REQ_FIFO_DEFINED) */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
#if !defined(SCST_IO_CONTEXT)
@@ -914,18 +919,24 @@ int __scst_register_dev_driver(struct scst_dev_type *dev_type,
if (res != 0)
goto out_error;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) && !defined(SCSI_EXEC_REQ_FIFO_DEFINED)
#if !defined(CONFIG_SCST_STRICT_SERIALIZING)
#if !defined(SCSI_EXEC_REQ_FIFO_DEFINED)
if (dev_type->exec == NULL) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
#if !defined(CONFIG_SCST_STRICT_SERIALIZING)
PRINT_ERROR("Pass-through dev handlers (handler \"%s\") not "
"supported. Consider applying on your kernel patch "
"scst_exec_req_fifo-<kernel-version> or define "
"CONFIG_SCST_STRICT_SERIALIZING", dev_type->name);
#endif /* !defined(CONFIG_SCST_STRICT_SERIALIZING) */
#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) */
PRINT_ERROR("Pass-through dev handlers (handler \"%s\") not "
"supported. Consider applying on your kernel patch "
"scst_exec_req_fifo-<kernel-version>", dev_type->name);
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) */
res = -EINVAL;
goto out_error;
}
#endif
#endif
#endif /* !defined(SCSI_EXEC_REQ_FIFO_DEFINED) */
res = scst_suspend_activity(true);
if (res != 0)
+12 -3
View File
@@ -349,7 +349,7 @@ static inline void scst_do_req(struct scsi_request *sreq,
scsi_do_req_fifo(sreq, cmnd, buffer, bufflen, done, timeout, retries);
#endif
}
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
static inline int scst_exec_req(struct scsi_device *sdev,
const unsigned char *cmd, int cmd_len, int data_direction,
struct scatterlist *sgl, unsigned bufflen, unsigned nents,
@@ -360,16 +360,25 @@ static inline int scst_exec_req(struct scsi_device *sdev,
return scsi_execute_async(sdev, cmd, cmd_len, data_direction, (void *)sgl,
bufflen, nents, timeout, retries, privdata, done, gfp);
#elif !defined(SCSI_EXEC_REQ_FIFO_DEFINED)
WARN_ON(1);
WARN_ON_ONCE(1);
return -1;
#else
return scsi_execute_async_fifo(sdev, cmd, cmd_len, data_direction,
(void *)sgl, bufflen, nents, timeout, retries, privdata, done, gfp);
#endif
}
#else /* i.e. LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) */
#else /* i.e. LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) */
#if defined(SCSI_EXEC_REQ_FIFO_DEFINED)
int scst_scsi_exec_async(struct scst_cmd *cmd,
void (*done)(void *, char *, int, int));
#else
static inline int scst_scsi_exec_async(struct scst_cmd *cmd,
void (*done)(void *, char *, int, int))
{
WARN_ON_ONCE(1);
return -1;
}
#endif
#endif
int scst_alloc_space(struct scst_cmd *cmd);
+1 -1
View File
@@ -2032,7 +2032,7 @@ static int scst_do_real_exec(struct scst_cmd *cmd)
cmd->scsi_req->sr_bufflen, scst_cmd_done, cmd->timeout,
cmd->retries);
#else
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
rc = scst_exec_req(dev->scsi_dev, cmd->cdb, cmd->cdb_len,
cmd->data_direction, cmd->sg, cmd->bufflen, cmd->sg_cnt,
cmd->timeout, cmd->retries, cmd, scst_cmd_done,