mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-03-30 10:55:03 +00:00
lib/scatterlist: Fix chaining support in sgl_alloc_order()
This patch avoids that workloads with large block sizes (megabytes)
can trigger the following call stack with the ib_srpt driver (that
driver is the only driver that chains scatterlists allocated by
sgl_alloc_order()):
BUG: Bad page state in process kworker/0:1H pfn:2423a78
page:fffffb03d08e9e00 count:-3 mapcount:0 mapping: (null) index:0x0
flags: 0x57ffffc0000000()
raw: 0057ffffc0000000 0000000000000000 0000000000000000 fffffffdffffffff
raw: dead000000000100 dead000000000200 0000000000000000 0000000000000000
page dumped because: nonzero _count
CPU: 0 PID: 733 Comm: kworker/0:1H Tainted: G I 4.15.0-rc7.bart+ #1
Hardware name: HP ProLiant DL380 G7, BIOS P67 08/16/2015
Workqueue: ib-comp-wq ib_cq_poll_work [ib_core]
Call Trace:
dump_stack+0x5c/0x83
bad_page+0xf5/0x10f
get_page_from_freelist+0xa46/0x11b0
__alloc_pages_nodemask+0x103/0x290
sgl_alloc_order+0x101/0x180
target_alloc_sgl+0x2c/0x40 [target_core_mod]
srpt_alloc_rw_ctxs+0x173/0x2d0 [ib_srpt]
srpt_handle_new_iu+0x61e/0x7f0 [ib_srpt]
__ib_process_cq+0x55/0xa0 [ib_core]
ib_cq_poll_work+0x1b/0x60 [ib_core]
process_one_work+0x141/0x340
worker_thread+0x47/0x3e0
kthread+0xf5/0x130
ret_from_fork+0x1f/0x30
Fixes: e80a0af475
("lib/scatterlist: Introduce sgl_alloc() and sgl_free()")
Reported-by: Laurence Oberman <loberman@redhat.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
Cc: Nicholas A. Bellinger <nab@linux-iscsi.org>
Cc: Laurence Oberman <loberman@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
9b9c63f71b
commit
8c7a8d1c4b
3 changed files with 35 additions and 12 deletions
|
@ -2300,7 +2300,7 @@ queue_full:
|
||||||
|
|
||||||
void target_free_sgl(struct scatterlist *sgl, int nents)
|
void target_free_sgl(struct scatterlist *sgl, int nents)
|
||||||
{
|
{
|
||||||
sgl_free(sgl);
|
sgl_free_n_order(sgl, nents, 0);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(target_free_sgl);
|
EXPORT_SYMBOL(target_free_sgl);
|
||||||
|
|
||||||
|
|
|
@ -282,6 +282,7 @@ struct scatterlist *sgl_alloc_order(unsigned long long length,
|
||||||
gfp_t gfp, unsigned int *nent_p);
|
gfp_t gfp, unsigned int *nent_p);
|
||||||
struct scatterlist *sgl_alloc(unsigned long long length, gfp_t gfp,
|
struct scatterlist *sgl_alloc(unsigned long long length, gfp_t gfp,
|
||||||
unsigned int *nent_p);
|
unsigned int *nent_p);
|
||||||
|
void sgl_free_n_order(struct scatterlist *sgl, int nents, int order);
|
||||||
void sgl_free_order(struct scatterlist *sgl, int order);
|
void sgl_free_order(struct scatterlist *sgl, int order);
|
||||||
void sgl_free(struct scatterlist *sgl);
|
void sgl_free(struct scatterlist *sgl);
|
||||||
#endif /* CONFIG_SGL_ALLOC */
|
#endif /* CONFIG_SGL_ALLOC */
|
||||||
|
|
|
@ -512,7 +512,7 @@ struct scatterlist *sgl_alloc_order(unsigned long long length,
|
||||||
if (!sgl)
|
if (!sgl)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
sg_init_table(sgl, nent);
|
sg_init_table(sgl, nalloc);
|
||||||
sg = sgl;
|
sg = sgl;
|
||||||
while (length) {
|
while (length) {
|
||||||
elem_len = min_t(u64, length, PAGE_SIZE << order);
|
elem_len = min_t(u64, length, PAGE_SIZE << order);
|
||||||
|
@ -526,7 +526,7 @@ struct scatterlist *sgl_alloc_order(unsigned long long length,
|
||||||
length -= elem_len;
|
length -= elem_len;
|
||||||
sg = sg_next(sg);
|
sg = sg_next(sg);
|
||||||
}
|
}
|
||||||
WARN_ON_ONCE(sg);
|
WARN_ONCE(length, "length = %lld\n", length);
|
||||||
if (nent_p)
|
if (nent_p)
|
||||||
*nent_p = nent;
|
*nent_p = nent;
|
||||||
return sgl;
|
return sgl;
|
||||||
|
@ -548,6 +548,36 @@ struct scatterlist *sgl_alloc(unsigned long long length, gfp_t gfp,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sgl_alloc);
|
EXPORT_SYMBOL(sgl_alloc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sgl_free_n_order - free a scatterlist and its pages
|
||||||
|
* @sgl: Scatterlist with one or more elements
|
||||||
|
* @nents: Maximum number of elements to free
|
||||||
|
* @order: Second argument for __free_pages()
|
||||||
|
*
|
||||||
|
* Notes:
|
||||||
|
* - If several scatterlists have been chained and each chain element is
|
||||||
|
* freed separately then it's essential to set nents correctly to avoid that a
|
||||||
|
* page would get freed twice.
|
||||||
|
* - All pages in a chained scatterlist can be freed at once by setting @nents
|
||||||
|
* to a high number.
|
||||||
|
*/
|
||||||
|
void sgl_free_n_order(struct scatterlist *sgl, int nents, int order)
|
||||||
|
{
|
||||||
|
struct scatterlist *sg;
|
||||||
|
struct page *page;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for_each_sg(sgl, sg, nents, i) {
|
||||||
|
if (!sg)
|
||||||
|
break;
|
||||||
|
page = sg_page(sg);
|
||||||
|
if (page)
|
||||||
|
__free_pages(page, order);
|
||||||
|
}
|
||||||
|
kfree(sgl);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(sgl_free_n_order);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sgl_free_order - free a scatterlist and its pages
|
* sgl_free_order - free a scatterlist and its pages
|
||||||
* @sgl: Scatterlist with one or more elements
|
* @sgl: Scatterlist with one or more elements
|
||||||
|
@ -555,15 +585,7 @@ EXPORT_SYMBOL(sgl_alloc);
|
||||||
*/
|
*/
|
||||||
void sgl_free_order(struct scatterlist *sgl, int order)
|
void sgl_free_order(struct scatterlist *sgl, int order)
|
||||||
{
|
{
|
||||||
struct scatterlist *sg;
|
sgl_free_n_order(sgl, INT_MAX, order);
|
||||||
struct page *page;
|
|
||||||
|
|
||||||
for (sg = sgl; sg; sg = sg_next(sg)) {
|
|
||||||
page = sg_page(sg);
|
|
||||||
if (page)
|
|
||||||
__free_pages(page, order);
|
|
||||||
}
|
|
||||||
kfree(sgl);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sgl_free_order);
|
EXPORT_SYMBOL(sgl_free_order);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue