Commit 1f13dff0 authored by Phillip Lougher's avatar Phillip Lougher Committed by Andrew Morton

squashfs: don't call kmalloc in decompressors

The decompressors may be called while in an atomic section.  So move the
kmalloc() out of this path, and into the "page actor" init function.

This fixes a regression introduced by commit
f268eedd ("squashfs: extend "page actor" to handle missing pages")

Link: https://lkml.kernel.org/r/20220822215430.15933-1-phillip@squashfs.org.uk
Fixes: f268eedd ("squashfs: extend "page actor" to handle missing pages")
Reported-by: default avatarChris Murphy <lists@colorremedies.com>
Signed-off-by: default avatarPhillip Lougher <phillip@squashfs.org.uk>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent d26f6070
...@@ -593,7 +593,7 @@ static void squashfs_readahead(struct readahead_control *ractl) ...@@ -593,7 +593,7 @@ static void squashfs_readahead(struct readahead_control *ractl)
res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor); res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor);
kfree(actor); squashfs_page_actor_free(actor);
if (res == expected) { if (res == expected) {
int bytes; int bytes;
......
...@@ -74,7 +74,7 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize, ...@@ -74,7 +74,7 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize,
/* Decompress directly into the page cache buffers */ /* Decompress directly into the page cache buffers */
res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor); res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor);
kfree(actor); squashfs_page_actor_free(actor);
if (res < 0) if (res < 0)
goto mark_errored; goto mark_errored;
......
...@@ -52,6 +52,7 @@ struct squashfs_page_actor *squashfs_page_actor_init(void **buffer, ...@@ -52,6 +52,7 @@ struct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
actor->buffer = buffer; actor->buffer = buffer;
actor->pages = pages; actor->pages = pages;
actor->next_page = 0; actor->next_page = 0;
actor->tmp_buffer = NULL;
actor->squashfs_first_page = cache_first_page; actor->squashfs_first_page = cache_first_page;
actor->squashfs_next_page = cache_next_page; actor->squashfs_next_page = cache_next_page;
actor->squashfs_finish_page = cache_finish_page; actor->squashfs_finish_page = cache_finish_page;
...@@ -68,20 +69,9 @@ static void *handle_next_page(struct squashfs_page_actor *actor) ...@@ -68,20 +69,9 @@ static void *handle_next_page(struct squashfs_page_actor *actor)
if ((actor->next_page == actor->pages) || if ((actor->next_page == actor->pages) ||
(actor->next_index != actor->page[actor->next_page]->index)) { (actor->next_index != actor->page[actor->next_page]->index)) {
if (actor->alloc_buffer) {
void *tmp_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (tmp_buffer) {
actor->tmp_buffer = tmp_buffer;
actor->next_index++; actor->next_index++;
actor->returned_pages++; actor->returned_pages++;
return tmp_buffer; return actor->alloc_buffer ? actor->tmp_buffer : ERR_PTR(-ENOMEM);
}
}
actor->next_index++;
actor->returned_pages++;
return ERR_PTR(-ENOMEM);
} }
actor->next_index++; actor->next_index++;
...@@ -96,11 +86,10 @@ static void *direct_first_page(struct squashfs_page_actor *actor) ...@@ -96,11 +86,10 @@ static void *direct_first_page(struct squashfs_page_actor *actor)
static void *direct_next_page(struct squashfs_page_actor *actor) static void *direct_next_page(struct squashfs_page_actor *actor)
{ {
if (actor->pageaddr) if (actor->pageaddr) {
kunmap_local(actor->pageaddr); kunmap_local(actor->pageaddr);
actor->pageaddr = NULL;
kfree(actor->tmp_buffer); }
actor->pageaddr = actor->tmp_buffer = NULL;
return handle_next_page(actor); return handle_next_page(actor);
} }
...@@ -109,8 +98,6 @@ static void direct_finish_page(struct squashfs_page_actor *actor) ...@@ -109,8 +98,6 @@ static void direct_finish_page(struct squashfs_page_actor *actor)
{ {
if (actor->pageaddr) if (actor->pageaddr)
kunmap_local(actor->pageaddr); kunmap_local(actor->pageaddr);
kfree(actor->tmp_buffer);
} }
struct squashfs_page_actor *squashfs_page_actor_init_special(struct squashfs_sb_info *msblk, struct squashfs_page_actor *squashfs_page_actor_init_special(struct squashfs_sb_info *msblk,
...@@ -121,6 +108,16 @@ struct squashfs_page_actor *squashfs_page_actor_init_special(struct squashfs_sb_ ...@@ -121,6 +108,16 @@ struct squashfs_page_actor *squashfs_page_actor_init_special(struct squashfs_sb_
if (actor == NULL) if (actor == NULL)
return NULL; return NULL;
if (msblk->decompressor->alloc_buffer) {
actor->tmp_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (actor->tmp_buffer == NULL) {
kfree(actor);
return NULL;
}
} else
actor->tmp_buffer = NULL;
actor->length = length ? : pages * PAGE_SIZE; actor->length = length ? : pages * PAGE_SIZE;
actor->page = page; actor->page = page;
actor->pages = pages; actor->pages = pages;
...@@ -128,7 +125,6 @@ struct squashfs_page_actor *squashfs_page_actor_init_special(struct squashfs_sb_ ...@@ -128,7 +125,6 @@ struct squashfs_page_actor *squashfs_page_actor_init_special(struct squashfs_sb_
actor->returned_pages = 0; actor->returned_pages = 0;
actor->next_index = page[0]->index & ~((1 << (msblk->block_log - PAGE_SHIFT)) - 1); actor->next_index = page[0]->index & ~((1 << (msblk->block_log - PAGE_SHIFT)) - 1);
actor->pageaddr = NULL; actor->pageaddr = NULL;
actor->tmp_buffer = NULL;
actor->alloc_buffer = msblk->decompressor->alloc_buffer; actor->alloc_buffer = msblk->decompressor->alloc_buffer;
actor->squashfs_first_page = direct_first_page; actor->squashfs_first_page = direct_first_page;
actor->squashfs_next_page = direct_next_page; actor->squashfs_next_page = direct_next_page;
......
...@@ -29,6 +29,11 @@ extern struct squashfs_page_actor *squashfs_page_actor_init(void **buffer, ...@@ -29,6 +29,11 @@ extern struct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
extern struct squashfs_page_actor *squashfs_page_actor_init_special( extern struct squashfs_page_actor *squashfs_page_actor_init_special(
struct squashfs_sb_info *msblk, struct squashfs_sb_info *msblk,
struct page **page, int pages, int length); struct page **page, int pages, int length);
static inline void squashfs_page_actor_free(struct squashfs_page_actor *actor)
{
kfree(actor->tmp_buffer);
kfree(actor);
}
static inline void *squashfs_first_page(struct squashfs_page_actor *actor) static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
{ {
return actor->squashfs_first_page(actor); return actor->squashfs_first_page(actor);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment