Skip to content

Commit

Permalink
Squashfs: refactor page_actor
Browse files Browse the repository at this point in the history
This patch essentially does 3 things:
  1/ Always use an array of page to store the data instead of a mix of
     buffers and pages.
  2/ It is now possible to have 'holes' in a page actor, i.e. NULL
     pages in the array.
     When reading a block (default 128K), squashfs tries to grab all
     the pages covering this block. If a single page is up-to-date or
     locked, it falls back to using an intermediate buffer to do the
     read and then copy the pages in the actor. Allowing holes in the
     page actor remove the need for this intermediate buffer.
  3/ Refactor the wrappers to share code that deals with page actors.

Signed-off-by: Adrien Schildknecht <adriens@google.com>
Change-Id: I98128bed5d518cf31b67e788a85b275e9a323bec
  • Loading branch information
schischi authored and Amit Pundir committed Apr 10, 2017
1 parent 3de0af4 commit 4bc7d97
Show file tree
Hide file tree
Showing 10 changed files with 252 additions and 210 deletions.
73 changes: 29 additions & 44 deletions fs/squashfs/cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,17 +209,14 @@ void squashfs_cache_put(struct squashfs_cache_entry *entry)
*/
void squashfs_cache_delete(struct squashfs_cache *cache)
{
int i, j;
int i;

if (cache == NULL)
return;

for (i = 0; i < cache->entries; i++) {
if (cache->entry[i].data) {
for (j = 0; j < cache->pages; j++)
kfree(cache->entry[i].data[j]);
kfree(cache->entry[i].data);
}
if (cache->entry[i].page)
free_page_array(cache->entry[i].page, cache->pages);
kfree(cache->entry[i].actor);
}

Expand All @@ -236,7 +233,7 @@ void squashfs_cache_delete(struct squashfs_cache *cache)
struct squashfs_cache *squashfs_cache_init(char *name, int entries,
int block_size)
{
int i, j;
int i;
struct squashfs_cache *cache = kzalloc(sizeof(*cache), GFP_KERNEL);

if (cache == NULL) {
Expand Down Expand Up @@ -268,22 +265,13 @@ struct squashfs_cache *squashfs_cache_init(char *name, int entries,
init_waitqueue_head(&cache->entry[i].wait_queue);
entry->cache = cache;
entry->block = SQUASHFS_INVALID_BLK;
entry->data = kcalloc(cache->pages, sizeof(void *), GFP_KERNEL);
if (entry->data == NULL) {
entry->page = alloc_page_array(cache->pages, GFP_KERNEL);
if (!entry->page) {
ERROR("Failed to allocate %s cache entry\n", name);
goto cleanup;
}

for (j = 0; j < cache->pages; j++) {
entry->data[j] = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
if (entry->data[j] == NULL) {
ERROR("Failed to allocate %s buffer\n", name);
goto cleanup;
}
}

entry->actor = squashfs_page_actor_init(entry->data,
cache->pages, 0);
entry->actor = squashfs_page_actor_init(entry->page,
cache->pages, 0, NULL);
if (entry->actor == NULL) {
ERROR("Failed to allocate %s cache entry\n", name);
goto cleanup;
Expand Down Expand Up @@ -314,18 +302,20 @@ int squashfs_copy_data(void *buffer, struct squashfs_cache_entry *entry,
return min(length, entry->length - offset);

while (offset < entry->length) {
void *buff = entry->data[offset / PAGE_CACHE_SIZE]
+ (offset % PAGE_CACHE_SIZE);
void *buff = kmap_atomic(entry->page[offset / PAGE_CACHE_SIZE])
+ (offset % PAGE_CACHE_SIZE);
int bytes = min_t(int, entry->length - offset,
PAGE_CACHE_SIZE - (offset % PAGE_CACHE_SIZE));

if (bytes >= remaining) {
memcpy(buffer, buff, remaining);
kunmap_atomic(buff);
remaining = 0;
break;
}

memcpy(buffer, buff, bytes);
kunmap_atomic(buff);
buffer += bytes;
remaining -= bytes;
offset += bytes;
Expand Down Expand Up @@ -416,43 +406,38 @@ struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *sb,
void *squashfs_read_table(struct super_block *sb, u64 block, int length)
{
int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
int i, res;
void *table, *buffer, **data;
struct page **page;
void *buff;
int res;
struct squashfs_page_actor *actor;

table = buffer = kmalloc(length, GFP_KERNEL);
if (table == NULL)
page = alloc_page_array(pages, GFP_KERNEL);
if (!page)
return ERR_PTR(-ENOMEM);

data = kcalloc(pages, sizeof(void *), GFP_KERNEL);
if (data == NULL) {
res = -ENOMEM;
goto failed;
}

actor = squashfs_page_actor_init(data, pages, length);
actor = squashfs_page_actor_init(page, pages, length, NULL);
if (actor == NULL) {
res = -ENOMEM;
goto failed2;
goto failed;
}

for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE)
data[i] = buffer;

res = squashfs_read_data(sb, block, length |
SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor);

kfree(data);
kfree(actor);

if (res < 0)
goto failed;
goto failed2;

return table;
buff = kmalloc(length, GFP_KERNEL);
if (!buff)
goto failed2;
squashfs_actor_to_buf(actor, buff, length);
squashfs_page_actor_free(actor, 0);
free_page_array(page, pages);
return buff;

failed2:
kfree(data);
squashfs_page_actor_free(actor, 0);
failed:
kfree(table);
free_page_array(page, pages);
return ERR_PTR(res);
}
55 changes: 30 additions & 25 deletions fs/squashfs/decompressor.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/buffer_head.h>
#include <linux/highmem.h>
#include <linux/fs.h>

#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
Expand Down Expand Up @@ -94,40 +95,44 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
static void *get_comp_opts(struct super_block *sb, unsigned short flags)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
void *buffer = NULL, *comp_opts;
void *comp_opts, *buffer = NULL;
struct page *page;
struct squashfs_page_actor *actor = NULL;
int length = 0;

if (!SQUASHFS_COMP_OPTS(flags))
return squashfs_comp_opts(msblk, buffer, length);

/*
* Read decompressor specific options from file system if present
*/
if (SQUASHFS_COMP_OPTS(flags)) {
buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
if (buffer == NULL) {
comp_opts = ERR_PTR(-ENOMEM);
goto out;
}

actor = squashfs_page_actor_init(&buffer, 1, 0);
if (actor == NULL) {
comp_opts = ERR_PTR(-ENOMEM);
goto out;
}

length = squashfs_read_data(sb,
sizeof(struct squashfs_super_block), 0, NULL, actor);

if (length < 0) {
comp_opts = ERR_PTR(length);
goto out;
}

page = alloc_page(GFP_KERNEL);
if (!page)
return ERR_PTR(-ENOMEM);

actor = squashfs_page_actor_init(&page, 1, 0, NULL);
if (actor == NULL) {
comp_opts = ERR_PTR(-ENOMEM);
goto actor_error;
}

length = squashfs_read_data(sb,
sizeof(struct squashfs_super_block), 0, NULL, actor);

if (length < 0) {
comp_opts = ERR_PTR(length);
goto read_error;
}

buffer = kmap_atomic(page);
comp_opts = squashfs_comp_opts(msblk, buffer, length);
kunmap_atomic(buffer);

out:
kfree(actor);
kfree(buffer);
read_error:
squashfs_page_actor_free(actor, 0);
actor_error:
__free_page(page);
return comp_opts;
}

Expand Down
4 changes: 2 additions & 2 deletions fs/squashfs/file_direct.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize)
* Create a "page actor" which will kmap and kunmap the
* page cache pages appropriately within the decompressor
*/
actor = squashfs_page_actor_init_special(page, pages, 0);
actor = squashfs_page_actor_init(page, pages, 0, NULL);
if (actor == NULL)
goto out;

Expand Down Expand Up @@ -131,7 +131,7 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize)
}

out:
kfree(actor);
squashfs_page_actor_free(actor, 0);
kfree(page);
return res;
}
Expand Down
32 changes: 5 additions & 27 deletions fs/squashfs/lz4_wrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,39 +94,17 @@ static int lz4_uncompress(struct squashfs_sb_info *msblk, void *strm,
struct buffer_head **bh, int b, int offset, int length,
struct squashfs_page_actor *output)
{
struct squashfs_lz4 *stream = strm;
void *buff = stream->input, *data;
int avail, i, bytes = length, res;
int res;
size_t dest_len = output->length;
struct squashfs_lz4 *stream = strm;

for (i = 0; i < b; i++) {
avail = min(bytes, msblk->devblksize - offset);
memcpy(buff, bh[i]->b_data + offset, avail);
buff += avail;
bytes -= avail;
offset = 0;
put_bh(bh[i]);
}

squashfs_bh_to_buf(bh, b, stream->input, offset, length,
msblk->devblksize);
res = lz4_decompress_unknownoutputsize(stream->input, length,
stream->output, &dest_len);
if (res)
return -EIO;

bytes = dest_len;
data = squashfs_first_page(output);
buff = stream->output;
while (data) {
if (bytes <= PAGE_CACHE_SIZE) {
memcpy(data, buff, bytes);
break;
}
memcpy(data, buff, PAGE_CACHE_SIZE);
buff += PAGE_CACHE_SIZE;
bytes -= PAGE_CACHE_SIZE;
data = squashfs_next_page(output);
}
squashfs_finish_page(output);
squashfs_buf_to_actor(stream->output, output, dest_len);

return dest_len;
}
Expand Down
40 changes: 7 additions & 33 deletions fs/squashfs/lzo_wrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,45 +79,19 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm,
struct buffer_head **bh, int b, int offset, int length,
struct squashfs_page_actor *output)
{
struct squashfs_lzo *stream = strm;
void *buff = stream->input, *data;
int avail, i, bytes = length, res;
int res;
size_t out_len = output->length;
struct squashfs_lzo *stream = strm;

for (i = 0; i < b; i++) {
avail = min(bytes, msblk->devblksize - offset);
memcpy(buff, bh[i]->b_data + offset, avail);
buff += avail;
bytes -= avail;
offset = 0;
put_bh(bh[i]);
}

squashfs_bh_to_buf(bh, b, stream->input, offset, length,
msblk->devblksize);
res = lzo1x_decompress_safe(stream->input, (size_t)length,
stream->output, &out_len);
if (res != LZO_E_OK)
goto failed;
return -EIO;
squashfs_buf_to_actor(stream->output, output, out_len);

res = bytes = (int)out_len;
data = squashfs_first_page(output);
buff = stream->output;
while (data) {
if (bytes <= PAGE_CACHE_SIZE) {
memcpy(data, buff, bytes);
break;
} else {
memcpy(data, buff, PAGE_CACHE_SIZE);
buff += PAGE_CACHE_SIZE;
bytes -= PAGE_CACHE_SIZE;
data = squashfs_next_page(output);
}
}
squashfs_finish_page(output);

return res;

failed:
return -EIO;
return out_len;
}

const struct squashfs_decompressor squashfs_lzo_comp_ops = {
Expand Down
Loading

0 comments on commit 4bc7d97

Please sign in to comment.