Re: [RFC PATCH 1/1] Dual Squashfs: multicore implementation
From: Phillip Lougher
Date: Wed Jul 17 2013 - 00:28:17 EST
On 16 July 2013 19:48, mani <manishrma@xxxxxxxxx> wrote:
>
> From 19c055d73cee8e65f8c24393450014b3560a8c6a Mon Sep 17 00:00:00 2001
> From: Manish Sharma <manishrma@xxxxxxxxx>
> Date: Mon, 1 Apr 2013 12:52:35 +0530
> Subject: [RFC PATCH 1/1] Dual Squashfs: multicore implementation
Mani,
Please don't send me the same patch twice in two days.... I received
the patch the first time. I do have a day job (and Squashfs isn't
it), and so you shouldn't expect a response in one day. The patch is
queued for review as time permits.
Cheers
Phillip
>
> The basic idea includes getting big requests by using readpages and
> then decompressing two blocks on each core.
> This implementation gives 50% improvement for the sequential file reads.
> 1.Split the two chunks based on the squashfs block size in readpages
> 2.Removed the locks of the decompressor(zlib/lzo) for percpu.
> 3.Increase the number of the data cache to per cpu.
> Points to consider:-
> 1. Need a lot of memory for the mutiple cache & multiple workspaces.
> 2. All the cpu will be too busy to process all the requests. cpu %usage
> increase.
> 3. Own queue method is implemented can be replaced with workqueues.
> 4. percpu data strucutures can be used.
>
> Signed-off-by: Manish Sharma <manishrma@xxxxxxxxx>
> ---
> fs/squashfs/Kconfig | 23 +++
> fs/squashfs/Makefile | 1 +
> fs/squashfs/file.c | 250 ++++++++++++++++++++++++++++
> fs/squashfs/lzo_wrapper.c | 113 ++++++++++++-
> fs/squashfs/squashfs_fs_sb.h | 6 +-
> fs/squashfs/super.c | 59 ++++++-
> fs/squashfs/tegra_mp.c | 368
> ++++++++++++++++++++++++++++++++++++++++++
> fs/squashfs/tegra_mp.h | 58 +++++++
> fs/squashfs/zlib_wrapper.c | 160 +++++++++++++++++-
> 9 files changed, 1030 insertions(+), 8 deletions(-)
> create mode 100644 fs/squashfs/tegra_mp.c
> create mode 100644 fs/squashfs/tegra_mp.h
>
> diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig
> index c70111e..ffcf730 100644
> --- a/fs/squashfs/Kconfig
> +++ b/fs/squashfs/Kconfig
> @@ -121,3 +121,26 @@ config SQUASHFS_FRAGMENT_CACHE_SIZE
>
> Note there must be at least one cached fragment. Anything
> much more than three will probably not make much difference.
> +
> +config SQUASHFS_READPAGES_ENABLE
> + bool "Enable Readpages for Squashfs"
> + depends on SQUASHFS
> + default n
> + help
> + Saying Y here enables readpages functionality.
> + If unsure, say N.
> +
> +config SQUASHFS_MPCORE
> + bool "Include Multi Core support in SquashFS file systems"
> + depends on SQUASHFS && SQUASHFS_READPAGES_ENABLE
> + default n
> + select SQUASHFS_4K_DEVBLK_SIZE
> + select TEGRA_MPCORE
> + help
> + Saying Y here includes support for Multi Core in SquashFS file
> systems
> + Multi Core supports creates the different kernel threads to improve
> the
> + SquashFS boot time performance.
> + This implementation is independent of the TEGRA board anyway as of
> now.
> + If unsure, say N.
> +
> +
> diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile
> index 110b047..0b99517 100644
> --- a/fs/squashfs/Makefile
> +++ b/fs/squashfs/Makefile
> @@ -9,3 +9,4 @@ squashfs-$(CONFIG_SQUASHFS_XATTR) += xattr.o xattr_id.o
> squashfs-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o
> squashfs-$(CONFIG_SQUASHFS_XZ) += xz_wrapper.o
> squashfs-$(CONFIG_SQUASHFS_ZLIB) += zlib_wrapper.o
> +squashfs-$(CONFIG_SQUASHFS_MPCORE) += tegra_mp.o
> diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c
> index 8ca62c2..c134e13 100644
> --- a/fs/squashfs/file.c
> +++ b/fs/squashfs/file.c
> @@ -38,6 +38,11 @@
> * Larger files use multiple slots, with 1.75 TiB files using all 8 slots.
> * The index cache is designed to be memory efficient, and by default uses
> * 16 KiB.
> + *
> + * manish.s2@xxxxxxxxxxx
> + * Added support for readpages for getting the bigger requests.
> + * Added Multithread support for the bigger chunks > squashfs block size
> + *
> */
>
> #include <linux/fs.h>
> @@ -53,6 +58,22 @@
> #include "squashfs_fs_i.h"
> #include "squashfs.h"
>
> +#ifdef CONFIG_SQUASHFS_MPCORE
> +#include "tegra_mp.h"
> +
> +
> +extern struct squashfs_queue *to_reader_1;
> +#endif /* CONFIG_SQUASHFS_MPCORE*/
> +
> +
> +
> +#ifdef CONFIG_SQUASHFS_READPAGES_ENABLE
> +#define list_to_page(head) (list_entry((head)->prev, struct page, lru))
> +#define list_to_page_index(pos, head, index) \
> + for (pos = list_entry((head)->prev, struct page, lru);
> pos->index != index;\
> + pos = list_entry((pos)->prev, struct page, lru))
> +#endif
> +
> /*
> * Locate cache slot in range [offset, index] for specified inode. If
> * there's more than one return the slot closest to index.
> @@ -494,8 +515,237 @@ out:
>
> return 0;
> }
> +#ifdef CONFIG_SQUASHFS_READPAGES_ENABLE
> +
> +/*
> + * copy of squashfs_readpage function for
> + * supports
> + * readpages & Multicore implementation
> + */
> +int read_this_page(struct file *file, struct page *page)
> +{
> + struct inode *inode = page->mapping->host;
> + struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
> + int bytes, i, offset = 0, sparse = 0;
> + struct squashfs_cache_entry *buffer = NULL;
> + void *pageaddr;
> +
> + int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
> + int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT);
> + int start_index = page->index & ~mask;
> + int end_index = start_index | mask;
> + int file_end = i_size_read(inode) >> msblk->block_log;
> +
> + TRACE("Entered read_this_page, page index %lx, start block %llx\n",
> + page->index, squashfs_i(inode)->start);
> +
> +
> + if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
> + PAGE_CACHE_SHIFT)){
> + goto out;
> + }
> +
> + if (index < file_end || squashfs_i(inode)->fragment_block ==
> + SQUASHFS_INVALID_BLK) {
> + /*
> + * Reading a datablock from disk. Need to read block list
> + * to get location and block size.
> + */
> + u64 block = 0;
> + int bsize = read_blocklist(inode, index, &block);
> + if (bsize < 0)
> + goto error_out;
> +
> +
> + if (bsize == 0) { /* hole */
> + bytes = index == file_end ?
> + (i_size_read(inode) & (msblk->block_size - 1)) :
> + msblk->block_size;
> + sparse = 1;
> + } else {
> + /*
> + * Read and decompress datablock.
> + */
> + buffer = squashfs_get_datablock(inode->i_sb,
> + block, bsize);
> + if (buffer->error) {
> + ERROR("Unable to read page, block %llx, size %x"
> + "\n", block, bsize);
> + squashfs_cache_put(buffer);
> + goto error_out;
> + }
> + bytes = buffer->length;
> + }
> + } else {
> + /*
> + * Datablock is stored inside a fragment (tail-end packed
> + * block).
> + */
> + buffer = squashfs_get_fragment(inode->i_sb,
> + squashfs_i(inode)->fragment_block,
> + squashfs_i(inode)->fragment_size);
> +
> + if (buffer->error) {
> + ERROR("Unable to read page, block %llx, size %x\n",
> + squashfs_i(inode)->fragment_block,
> + squashfs_i(inode)->fragment_size);
> + squashfs_cache_put(buffer);
> + goto error_out;
> + }
> + bytes = i_size_read(inode) & (msblk->block_size - 1);
> + offset = squashfs_i(inode)->fragment_offset;
> + }
> +
> + /*
> + * Loop copying datablock into pages. As the datablock likely covers
> + * many PAGE_CACHE_SIZE pages (default block size is 128 KiB)
> explicitly
> + * grab the pages from the page cache, except for the page that we've
> + * been called to fill.
> + */
> + for (i = start_index; i <= end_index && bytes > 0; i++,
> + bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) {
> + struct page *push_page;
> + int avail = sparse ? 0 : min_t(int, bytes, PAGE_CACHE_SIZE);
> +
> + TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail);
> +
> + push_page = (i == page->index) ? page :
> + grab_cache_page_nowait(page->mapping, i);
> +
> + if (!push_page)
> + continue;
> +
> + if (PageUptodate(push_page))
> + goto skip_page;
> +
> + pageaddr = kmap_atomic(push_page);
> + squashfs_copy_data(pageaddr, buffer, offset, avail);
> + memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail);
> + kunmap_atomic(pageaddr);
> + flush_dcache_page(push_page);
> + SetPageUptodate(push_page);
> +skip_page:
> + unlock_page(push_page);
> + if (i != page->index)
> + page_cache_release(push_page);
> + }
> +
> + if (!sparse)
> + squashfs_cache_put(buffer);
> +#ifdef CONFIG_SQUASHFS_MPCORE
> + page_cache_release(page);
> +#endif
> +
> + return 0;
> +
> +error_out:
> + SetPageError(page);
> +out:
> + pageaddr = kmap_atomic(page);
> + memset(pageaddr, 0, PAGE_CACHE_SIZE);
> + kunmap_atomic(pageaddr);
> + flush_dcache_page(page);
> + if (!PageError(page))
> + SetPageUptodate(page);
> + unlock_page(page);
> +
> + return 0;
> +}
> +
> +/*
> + * readpages implementation and Multi Core implementation
> + * for squashfs
> + *
> + */
> +static int squashfs_readpages(struct file *filp, struct address_space
> *mapping,
> + struct list_head *pages, unsigned nr_pages)
> +{
> + unsigned page_idx;
> +
> +
> +#ifdef CONFIG_SQUASHFS_MPCORE
> + unsigned first_page_idx;
> + int err;
> + unsigned long index = 0;
> + struct squashfs_sb_info *msblk =
> filp->f_path.dentry->d_inode->i_sb->s_fs_info;
> + unsigned int pages_per_block;
> +
> + pages_per_block = (msblk->block_size/(PAGE_CACHE_SIZE));
> +
> +#ifdef DEBUG
> + printk(KERN_EMERG"[%d]%s %d %d Ino %lu \n", current->pid, __FUNCTION__,
> nr_pages, pages_per_block, filp->f_path.dentry->d_inode->i_ino);
> +#endif
> +
> + if (nr_pages > pages_per_block) {
> +
> + /*Here we will grab the page and put into queue */
> + for (first_page_idx = 0, page_idx = 0; page_idx < nr_pages; ) {
> +
> + struct page *page = NULL;
> +
> + if (first_page_idx == page_idx) {
> + page = list_to_page(pages);
> + prefetchw(&page->flags);
> + list_del(&page->lru);
> + /* Add this page to page-cache */
> + /*err = add_to_page_cache_lru(page, mapping,
> page->index, GFP_KERNEL);*/
> + err = add_to_page_cache(page, mapping, page->index,
> GFP_KERNEL);
> + if (unlikely(err)) {
> + /*printk(KERN_EMERG "releasing page cache \n");*/
> + page_cache_release(page);
> + page_idx += 1;
> + first_page_idx = page_idx;
> + continue;
> + }
> + page_idx += pages_per_block;
> + index = page->index;
> + if (queue_put(to_reader_1, filp, page))
> + break;
> + } else {
> +
> + page = grab_cache_page_nowait(mapping, (index +
> page_idx));
> + if (unlikely(!page)) {
> + /*Need to do error checking here*/
> + page_idx += 1;
> + continue;
> + /*return -ENOMEM;*/
> + } else {
> + page_idx += pages_per_block;
> + queue_put(to_reader_1, filp, page);
> + }
> +
> + }
> +
> + }
> +
> + work_on_queue(to_reader_1);
> + } else
> +
> +#endif /* CONFIG_SQUASHFS_MPCORE */
> + {
> + /* readpages Implementation */
> + for (page_idx = 0; page_idx < nr_pages; page_idx++) {
> + struct page *page = list_to_page(pages);
> + prefetchw(&page->flags);
> + list_del(&page->lru);
> + /*if (!add_to_page_cache_lru(page, mapping, page->index,
> GFP_KERNEL)) {*/
> + if (!add_to_page_cache(page, mapping, page->index,
> GFP_KERNEL)) {
> + squashfs_readpage(filp, page);
> + }
> + page_cache_release(page);
> + }
> + }
> +
> +
> + /*always return 0 as readpages either writes to a page or release it*/
> + return 0;
> +}
> +#endif
>
>
> const struct address_space_operations squashfs_aops = {
> +#ifdef CONFIG_SQUASHFS_READPAGES_ENABLE
> + .readpages = squashfs_readpages,
> +#endif
> .readpage = squashfs_readpage
> };
> diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c
> index 00f4dfc..4bcdf64 100644
> --- a/fs/squashfs/lzo_wrapper.c
> +++ b/fs/squashfs/lzo_wrapper.c
> @@ -37,7 +37,114 @@ struct squashfs_lzo {
> void *output;
> };
>
> -static void *lzo_init(struct squashfs_sb_info *msblk, void *buff, int len)
> +#ifdef CONFIG_SQUASHFS_MPCORE
> +static void *lzo_init(struct squashfs_sb_info *msblk)
> +{
> + unsigned int i = 0;
> + int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE);
> + unsigned int processors = num_online_cpus();
> +
> + /* Initialization of the lzo streams */
> + struct squashfs_lzo *stream = kmalloc(processors * sizeof(stream),
> GFP_KERNEL);
> + if (NULL == stream) {
> + ERROR("Failed to allocate zlib stream\n");
> + goto failed;
> + }
> + for_each_online_cpu(i) {
> + stream[i].input = vmalloc(block_size);
> + if (stream[i].input == NULL)
> + goto failed;
> + stream[i].output = vmalloc(block_size);
> + if (stream[i].output == NULL)
> + goto failed;
> + }
> + return stream;
> +
> +failed:
> + ERROR("Failed to allocate lzo workspace\n");
> + i = 0;
> + for_each_online_cpu(i) {
> + if (stream[i].input)
> + vfree(stream[i].input);
> + }
> + if (stream)
> + kfree(stream);
> + return NULL;
> +}
> +
> +
> +static void lzo_free(void *strm)
> +{
> + unsigned int i = 0;
> + struct squashfs_lzo *stream = strm;
> +
> + if (stream) {
> + for_each_online_cpu(i) {
> + if (stream[i].input)
> + vfree(stream[i].input);
> + if (stream[i].output)
> + vfree(stream[i].output);
> + }
> + kfree(stream);
> + }
> + strm = NULL;
> +}
> +
> +static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer,
> + struct buffer_head **bh, int b, int offset, int length, int srclength,
> + int pages)
> +{
> + unsigned int pid = smp_processor_id();
> + struct squashfs_lzo *stream = msblk->stream;
> + void *buff = stream[pid].input;
> + int avail, i, bytes = length, res;
> + size_t out_len = srclength;
> +
> + mutex_lock(&msblk->read_data_mutex[pid]);
> +
> + for (i = 0; i < b; i++) {
> + wait_on_buffer(bh[i]);
> + if (!buffer_uptodate(bh[i]))
> + goto block_release;
> +
> + avail = min(bytes, msblk->devblksize - offset);
> + memcpy(buff, bh[i]->b_data + offset, avail);
> + buff += avail;
> + bytes -= avail;
> + offset = 0;
> + put_bh(bh[i]);
> + }
> +
> + res = lzo1x_decompress_safe(stream[pid].input, (size_t)length,
> + stream[pid].output, &out_len);
> + if (res != LZO_E_OK)
> + goto failed;
> +
> + res = bytes = (int)out_len;
> + for (i = 0, buff = stream[pid].output; bytes && i < pages; i++) {
> + avail = min_t(int, bytes, PAGE_CACHE_SIZE);
> + memcpy(buffer[i], buff, avail);
> + buff += avail;
> + bytes -= avail;
> + }
> +
> + mutex_unlock(&msblk->read_data_mutex[pid]);
> + return res;
> +
> +block_release:
> + for (; i < b; i++)
> + put_bh(bh[i]);
> +
> +failed:
> + mutex_unlock(&msblk->read_data_mutex[pid]);
> +
> + ERROR("lzo decompression failed, data probably corrupt\n");
> + return -EIO;
> +}
> +
> +#else /* MPCORE*/
> +
> +static void *lzo_init(struct squashfs_sb_info *msblk)
> {
> int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE);
>
> @@ -58,7 +165,7 @@ failed2:
> failed:
> ERROR("Failed to allocate lzo workspace\n");
> kfree(stream);
> - return ERR_PTR(-ENOMEM);
> + return NULL;
> }
>
>
> @@ -125,6 +232,8 @@ failed:
> return -EIO;
> }
>
> +#endif /*MPCORE*/
> +
> const struct squashfs_decompressor squashfs_lzo_comp_ops = {
> .init = lzo_init,
> .free = lzo_free,
> diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h
> index 52934a2..bd39cd5 100644
> --- a/fs/squashfs/squashfs_fs_sb.h
> +++ b/fs/squashfs/squashfs_fs_sb.h
> @@ -63,7 +63,11 @@ struct squashfs_sb_info {
> __le64 *id_table;
> __le64 *fragment_index;
> __le64 *xattr_id_table;
> - struct mutex read_data_mutex;
> +#ifdef CONFIG_SQUASHFS_MPCORE
> + struct mutex *read_data_mutex;
> +#else
> + struct mutex read_data_mutex;
> +#endif /*MPCORE*/
> struct mutex meta_index_mutex;
> struct meta_index *meta_index;
> void *stream;
> diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
> index 260e392..2484a67 100644
> --- a/fs/squashfs/super.c
> +++ b/fs/squashfs/super.c
> @@ -25,6 +25,8 @@
> * This file implements code to read the superblock, read and initialise
> * in-memory structures at mount time, and all the VFS glue code to
> register
> * the filesystem.
> + * manish.s2 : added support for multicore
> + * : Added generic decompression selection with multicore
> */
>
> #include <linux/fs.h>
> @@ -43,6 +45,9 @@
> #include "squashfs.h"
> #include "decompressor.h"
> #include "xattr.h"
> +#ifdef CONFIG_SQUASHFS_MPCORE
> +#include "tegra_mp.h"
> +#endif
>
> static struct file_system_type squashfs_fs_type;
> static const struct super_operations squashfs_super_ops;
> @@ -85,7 +90,10 @@ static int squashfs_fill_super(struct super_block *sb,
> void *data, int silent)
> unsigned int fragments;
> u64 lookup_table_start, xattr_id_table_start, next_table;
> int err;
> -
> +#ifdef CONFIG_SQUASHFS_MPCORE
> + unsigned int i = 0;
> + unsigned int processors = num_online_cpus();
> +#endif
> TRACE("Entered squashfs_fill_superblock\n");
>
> sb->s_fs_info = kzalloc(sizeof(*msblk), GFP_KERNEL);
> @@ -98,7 +106,20 @@ static int squashfs_fill_super(struct super_block *sb,
> void *data, int silent)
> msblk->devblksize = sb_min_blocksize(sb, SQUASHFS_DEVBLK_SIZE);
> msblk->devblksize_log2 = ffz(~msblk->devblksize);
>
> +#ifdef CONFIG_SQUASHFS_MPCORE
> + /* Initialization of mutex for each core */
> + i = 0;
> + msblk->read_data_mutex = kmalloc((processors)*sizeof(struct mutex),
> GFP_KERNEL);
> + if (NULL == msblk->read_data_mutex) {
> + ERROR("unable to allocate Mutex Mem \n");
> + goto failed_mount;
> + }
> + for_each_online_cpu(i) {
> + mutex_init(&msblk->read_data_mutex[i]);
> + }
> +#else /*MPCORE */
> mutex_init(&msblk->read_data_mutex);
> +#endif /*MPCORE */
> mutex_init(&msblk->meta_index_mutex);
>
> /*
> @@ -205,13 +226,21 @@ static int squashfs_fill_super(struct super_block *sb,
> void *data, int silent)
> if (msblk->block_cache == NULL)
> goto failed_mount;
>
> +#ifdef CONFIG_SQUASHFS_MPCORE
> + /* Allocate read_page block */
> + msblk->read_page = squashfs_cache_init("data", processors,
> (msblk->block_size));
> + if (msblk->read_page == NULL) {
> + ERROR("Failed to allocate read_page block\n");
> + goto failed_mount;
> + }
> +#else
> /* Allocate read_page block */
> msblk->read_page = squashfs_cache_init("data", 1, msblk->block_size);
> if (msblk->read_page == NULL) {
> ERROR("Failed to allocate read_page block\n");
> goto failed_mount;
> }
> -
> +#endif
> msblk->stream = squashfs_decompressor_init(sb, flags);
> if (IS_ERR(msblk->stream)) {
> err = PTR_ERR(msblk->stream);
> @@ -446,7 +475,26 @@ static int __init init_squashfs_fs(void)
> destroy_inodecache();
> return err;
> }
> -
> +#ifdef CONFIG_SQUASHFS_MPCORE
> +/*M.S the size of different cache */
> +/*fragment_buffer_size = msblk->block_size;
> +data_buffer_size = msblk->block_size;
> +metadata_buffer_size = SQUASHFS_METADATA_SIZE;
> +queue_buffer_size = data_buffer_size;
> +pages_per_block = (msblk->block_size/(PAGE_CACHE_SIZE));*/
> +/*
> +* queue_buffer_size = fragment_buffer_size + data_buffer_size +
> metadata_buffer_size;
> +* M.S :- As of now we don't need that much big size of queue
> +* 1. we are currently working on offsets equal to number of pages in the
> block size
> +* so we will take the size of the queue equal to data_buffer_size only
> +* 2. The metadata requests are same as previous no threading.
> +* 3. We reduced the queue size further to 64
> +* As of now max queue request will not be more than 64.
> +*/
> +/* M.S Adding Threads here */
> +initialise_threads(SQFS_QBUFFER_SIZE);
> +
> +#endif
> printk(KERN_INFO "squashfs: version 4.0 (2009/01/31) "
> "Phillip Lougher\n");
>
> @@ -456,6 +504,11 @@ static int __init init_squashfs_fs(void)
>
> static void __exit exit_squashfs_fs(void)
> {
> +#ifdef CONFIG_SQUASHFS_MPCORE
> + printk(KERN_INFO"%s \n", __FUNCTION__);
> + /*MS Adding the exiting code */
> + exit_threads();
> +#endif
> unregister_filesystem(&squashfs_fs_type);
> destroy_inodecache();
> }
> diff --git a/fs/squashfs/tegra_mp.c b/fs/squashfs/tegra_mp.c
> new file mode 100644
> index 0000000..1d7e03f
> --- /dev/null
> +++ b/fs/squashfs/tegra_mp.c
> @@ -0,0 +1,368 @@
> +/**
> +* @file: tegra_mp.c
> +* @brief: Multi Core support for squashFS
> +* Copyright: Copyright(C) Samsung India Pvt. Ltd 2011. All Rights
> Reserved.
> +* @author: SISC: manish.s2
> +* @date: 2011/03/10
> +* @History:
> +* v1.1a is stable & support dual core.
> +* v1.2 added multi core support.
> +* v1.8 Fix the bug for the queue fill ptr overrun
> +*/
> +
> +#include <linux/delay.h>
> +#include <linux/fs.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/kthread.h>
> +#include <linux/cpumask.h>
> +#include <linux/cpu.h>
> +#include <linux/slab.h>
> +
> +#include "squashfs_fs_i.h"
> +#include "squashfs.h"
> +#include "tegra_mp.h"
> +
> +
> +struct squashfs_queue *to_reader_1;
> +static struct task_struct **thread;
> +
> +extern int read_this_page(struct file *file, struct page *page);
> +
> +int queue_fini(struct squashfs_queue *queue)
> +{
> +
> + if (queue == NULL) {
> + printk(KERN_INFO "ERROR: Wrong queue ptr\n");
> + return -EFAULT;
> + }
> +
> +
> + if (NULL != queue->data) {
> + kfree(queue->data);
> + queue->data = NULL;
> + }
> +
> + if (NULL != queue->cpu) {
> + kfree(queue->cpu);
> + queue = NULL;
> + }
> + if (NULL != queue) {
> + kfree(queue);
> + queue = NULL;
> + }
> +
> + return 0;
> +}
> +
> +struct squashfs_queue *queue_init(int size)
> +{
> +
> + unsigned int i = 0;
> + int processors;
> + struct squashfs_queue *queue = NULL;
> +
> +#ifdef DEBUG
> + printk(KERN_INFO "[%s] size %d \n", __FUNCTION__, size + 1);
> +#endif
> +
> + processors = num_online_cpus();
> +
> + queue = (struct squashfs_queue *)kmalloc(sizeof(struct squashfs_queue),
> GFP_KERNEL);
> + if (queue == NULL) {
> + printk(KERN_INFO "Out of memory in queue_init\n");
> + return NULL;
> + }
> +
> + queue->data = (struct squashfs_qdata *)kmalloc((sizeof(struct
> squashfs_qdata) * (size + 1)), GFP_KERNEL);
> + if (NULL == queue->data) {
> + printk(KERN_INFO "unable to get the memory \n");
> + queue_fini(queue);
> + return NULL;
> + }
> +
> + queue->cpu = kmalloc(processors * (sizeof(int)), GFP_KERNEL);
> + if (NULL == queue->cpu) {
> + printk(KERN_INFO "unable to get the memory for cpu \n");
> + queue_fini(queue);
> + return NULL;
> + }
> +
> +
> + for_each_online_cpu(i) {
> + queue->cpu[i] = 0;
> + }
> +
> + queue->size = size + 1;
> + queue->readp = queue->writep = 0;
> + queue->empty = 1;
> + queue->full = 0;
> + queue->stop = 0;
> + init_waitqueue_head(&queue->wait_queue);
> + spin_lock_init(&queue->lock);
> +
> + return queue;
> +}
> +
> +
> +int queue_put(struct squashfs_queue *queue, void *filp, void *page)
> +{
> + int processor_id = 0;
> + unsigned int i = 0;
> + spin_lock(&queue->lock);
> +
> + processor_id = raw_smp_processor_id();
> +
> + if (((queue->writep + 1) % queue->size) == queue->readp) {
> +#ifdef DEBUG
> + printk(KERN_INFO "[%d] Queue is full: page %lu \n", current->pid,
> ((struct page *)page)->index);
> +#endif
> + queue->full = 1;
> + spin_unlock(&queue->lock);
> +
> + for_each_online_cpu(i) {
> + if (i != processor_id) {
> + queue->cpu[i] = 1;
> + }
> +
> + }
> + wake_up(&queue->wait_queue);
> + wait_event_timeout(queue->wait_queue, !queue->full,
> msecs_to_jiffies(100));
> + spin_lock(&queue->lock);
> + if (((queue->writep + 1) % queue->size) == queue->readp) {
> +#ifdef DEBUG
> + printk(KERN_EMERG "[%d] Queue is still full: page %lu \n",
> current->pid, ((struct page *)page)->index);
> + printk(KERN_EMERG "[%d] Check threads \n", current->pid);
> +#endif
> + spin_unlock(&queue->lock);
> + return -1;
> + }
> + processor_id = raw_smp_processor_id();
> + }
> +
> + queue->data[queue->writep].filp = filp;
> + queue->data[queue->writep].page = page;
> + queue->writep = ((queue->writep + 1) % queue->size);
> + queue->empty = 0;
> +
> +#ifdef DEBUG
> + printk(KERN_EMERG "[%d]queue put w%d:r%d page %lu \n", current->pid,
> queue->writep, queue->readp, ((struct page *)page)->index);
> +#endif
> +
> + for_each_online_cpu(i) {
> + if (i != processor_id) {
> + /*printk(KERN_INFO"waking up %d processor \n",i);*/
> + queue->cpu[i] = 1;
> + }
> +
> + }
> + spin_unlock(&queue->lock);
> + wake_up(&queue->wait_queue);
> + return 0;
> +}
> +
> +
> +int queue_get(struct squashfs_queue *queue, int id, struct squashfs_qdata
> *data)
> +{
> + /*struct squashfs_qdata *data;*/
> + int processor_id = 0;
> +#ifdef DEBUG
> + printk(KERN_INFO "queue get %d \n", raw_smp_processor_id());
> +#endif
> + spin_lock(&queue->lock);
> + processor_id = raw_smp_processor_id();
> +
> + /* wait here if queue is empty */
> + if (queue->readp == queue->writep) {
> +
> + if (1 == id) {
> + queue->empty = 1;
> + queue->full = 0;
> + spin_unlock(&queue->lock);
> + wake_up(&queue->wait_queue);
> + return -1;
> + }
> +
> +#ifdef DEBUG
> + printk(KERN_EMERG "[%d] Need to wait here as queue is empty
> \n", current->pid);
> +#endif
> + queue->empty = 1;
> + queue->full = 0;
> + queue->cpu[processor_id] = 0;
> + wake_up(&queue->wait_queue);
> + spin_unlock(&queue->lock);
> + wait_event_interruptible(queue->wait_queue,
> queue->cpu[processor_id]);
> +
> + /* After the thread gets out from wait queue */
> + spin_lock(&queue->lock);
> + if (queue->stop || (queue->readp == queue->writep)) {
> + queue->empty = 1;
> + queue->full = 0;
> + wake_up(&queue->wait_queue);
> + spin_unlock(&queue->lock);
> +#ifdef DEBUG
> + printk(KERN_INFO " Thread%ld %s \n", current->cpus_allowed,
> (queue->stop ? "should stop" : "queue is empty"));
> +#endif
> + return -1;
> + }
> + }
> +
> +
> + data->filp = queue->data[queue->readp].filp;
> + data->page = queue->data[queue->readp].page;
> + queue->data[queue->readp].filp = NULL;
> + queue->data[queue->readp].page = NULL;
> + queue->readp = (queue->readp + 1) % queue->size;
> + queue->full = 0;
> +#ifdef DEBUG
> + printk(KERN_EMERG "[%d]queue get w%d:r%d page %lu \n", \
> + current->pid, queue->writep, queue->readp, ((struct page
> *)data->page)->index);
> +#endif
> + spin_unlock(&queue->lock);
> + wake_up(&queue->wait_queue);
> +
> +
> + return 0;
> +}
> +
> +
> +
> +void squashfs_thread(void *arg)
> +{
> +
> + struct squashfs_qdata data;
> + int ret = 0;
> +
> + set_user_nice(current, -20);
> + printk(KERN_INFO "### Started squashfs thread_%d \n",
> raw_smp_processor_id());
> + while (!kthread_should_stop()) {
> +
> +
> + ret = queue_get(to_reader_1, 0, &data);
> + if (unlikely(0 > ret)) {
> + if (to_reader_1->stop) {
> + printk(KERN_INFO"ERROR : We are seeing the stop being
> set\n");
> + break;
> + } else {
> + continue;
> + }
> + } else {
> +#ifdef DEBUG
> + /* Can remove this as its for error checking */
> + if ((NULL != data.filp) && (NULL != data.page)) {
> + printk(KERN_INFO "here it is page index %ld \n",
> data.page->index);
> + read_this_page(data.filp, data.page);
> + } else {
> + printk(KERN_INFO"Ptr is NULL \n");
> + }
> +#else
> + read_this_page(data.filp, data.page);
> +#endif
> +
> +
> + }
> +
> + }
> + printk(KERN_INFO"SquashFS Thread : I am dying!\n");
> +
> +}
> +
> +void squashfs_process_data(void)
> +{
> +
> + struct squashfs_qdata data;
> + int ret = 0;
> +
> + while (1) {
> +
> +
> + ret = queue_get(to_reader_1, 1, &data);
> + if (unlikely(0 > ret)) {
> +#ifdef DEBUG
> + printk(KERN_INFO "[%s][%d] Q is empty so we are exiting \n",
> __FUNCTION__, current->pid);
> +#endif
> + break;
> + } else {
> + read_this_page(data.filp, data.page);
> + }
> +
> + }
> +
> +}
> +
> +void work_on_queue(struct squashfs_queue *queue)
> +{
> + squashfs_process_data();
> +}
> +
> +int initialise_threads(int queue_buffer_size)
> +{
> + unsigned int i = 0;
> + int processors;
> +
> + processors = num_online_cpus();
> +
> +#ifdef DEBUG
> + printk(KERN_INFO "no of active cores %d \n", processors);
> +#endif
> +
> + /* Initialize the Queue */
> + to_reader_1 = queue_init(queue_buffer_size);
> +
> +
> + if ((thread = kmalloc((NOTHR_THREADS + processors) * sizeof(struct
> task_struct *), GFP_KERNEL)) == NULL) {
> + printk(KERN_INFO "Out of memory allocating thread descriptors\n");
> + return -ENOMEM;
> + }
> +
> +
> + /* Create Number n Number of Deflator threads same as core.*/
> + for_each_online_cpu(i) {
> + printk(KERN_INFO "Created %d thread \n", i);
> + thread[NOTHR_THREADS + i] = kthread_create((void *)squashfs_thread,
> NULL, MODULE_NAME);
> + if (IS_ERR(thread[NOTHR_THREADS + i])) {
> + printk(KERN_ERR ": unable to start deflator kernel thread\n");
> + return -ENOMEM;
> + } else {
> + printk(KERN_INFO" ################## \n");
> + printk(KERN_INFO"Binding cpu %d \n", i);
> + kthread_bind(thread[NOTHR_THREADS + i], i);
> + wake_up_process(thread[NOTHR_THREADS + i]);
> + }
> + }
> +
> +
> + return 0;
> +
> +}
> +
> +void exit_threads()
> +{
> + int i = 0;
> +
> + /* wake up both threads */
> + to_reader_1->empty = 0;
> + to_reader_1->stop = 1;
> + for_each_online_cpu(i) {
> + to_reader_1->cpu[i] = 1;
> + }
> + wake_up_all(&to_reader_1->wait_queue);
> +
> +#if 0
> + for (i = NOTHR_THREADS; i < (NOTHR_THREADS + NR_CPUS); i++) {
> +
> + if (NULL != thread[i])
> + kthread_stop(thread[i]);
> +
> + }
> + if (thread)
> + kfree(thread);
> +
> +#endif
> + /* We have only one queue as of now */
> + if (queue_fini(to_reader_1))
> + printk(KERN_INFO"ERROR: In queue deallocation \n");
> +
> +
> +}
> +
> diff --git a/fs/squashfs/tegra_mp.h b/fs/squashfs/tegra_mp.h
> new file mode 100644
> index 0000000..ca60c56
> --- /dev/null
> +++ b/fs/squashfs/tegra_mp.h
> @@ -0,0 +1,58 @@
> +/**
> +* @file tegra_mp.h
> +* @brief Multi Core support for squashFS
> +* Copyright: Copyright(C) Samsung India Pvt. Ltd 2011. All Rights
> Reserved.
> +* @author SISC: manish.s2
> +* @date 2011/03/10
> +* @desc Added Multi core support in squashfs
> +*/
> +#ifndef __MP_TEGRA__
> +#define __MP_TEGRA__
> +
> +
> +#include <linux/fs.h>
> +#include <linux/vfs.h>
> +#include <linux/wait.h>
> +
> +/* Total number of other threads except if needed */
> +/*#define NOTHR_THREADS 3 // To be used if we additional threads or
> so.*/
> +#define NOTHR_THREADS 0
> +#define MODULE_NAME "tegra_mpcore"
> +
> +/* Max page pool size 64 and min squashfs block size 4k */
> +#define SQFS_QBUFFER_SIZE (64)
> +
> +/*#define DEBUG*/
> +
> +struct squashfs_qdata{
> + struct file *filp;
> + struct page *page;
> + int index;
> +};
> +
> +
> +/* struct describing queues used to pass data between threads */
> +struct squashfs_queue {
> + int size;
> + int readp;
> + int writep;
> + wait_queue_head_t wait_queue;
> + spinlock_t lock;
> +
> + int empty;
> + int full;
> + int *cpu;
> + int stop;
> + struct squashfs_qdata *data;
> +};
> +
> +
> +/* Functions */
> +int initialise_threads(int queue_buffer_size);
> +void exit_threads(void);
> +int queue_put(struct squashfs_queue *queue, void *filp, void *page);
> +int queue_get(struct squashfs_queue *queue, int id, struct squashfs_qdata
> *data);
> +struct squashfs_queue *queue_init(int size);
> +void work_on_queue(struct squashfs_queue *queue);
> +
> +#endif /*__MP_TEGRA__*/
> diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c
> index 55d918f..5e8b0a1 100644
> --- a/fs/squashfs/zlib_wrapper.c
> +++ b/fs/squashfs/zlib_wrapper.c
> @@ -19,7 +19,13 @@
> * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> *
> * zlib_wrapper.c
> - */
> + * manish.s2: added the dual core support for squashfs.
> + * : Seperate mutex & z_stream for each core.
> + * : generalized for multicores.
> + * : Added seperate mutex and zlib stream for Multicore.
> + * : Replace zlib_init with zlib_reset for performance.
> + *
> +*/
>
>
> #include <linux/mutex.h>
> @@ -33,6 +39,156 @@
> #include "squashfs.h"
> #include "decompressor.h"
>
> +#ifdef CONFIG_SQUASHFS_MPCORE
> +static void *zlib_init(struct squashfs_sb_info *dummy, void *buff, int len)
> +{
> + unsigned int processors = num_online_cpus();
> + unsigned int i = 0;
> + int zlib_err = 0;
> +
> + z_stream *stream = kmalloc((processors * sizeof(z_stream)),
> GFP_KERNEL);
> + if (stream == NULL)
> + goto failed;
> +
> + for_each_online_cpu(i) {
> + stream[i].workspace = kmalloc(zlib_inflate_workspacesize(),
> + GFP_KERNEL);
> + if (stream[i].workspace == NULL)
> + goto failed;
> + zlib_err = zlib_inflateInit(&(stream[i]));
> + if (zlib_err != Z_OK) {
> + ERROR("zlib_inflateInit returned unexpected "
> + "result 0x%x\n",
> + zlib_err);
> + goto failed;
> + }
> + }
> + return stream;
> +
> +failed:
> + ERROR("Failed to allocate zlib workspace\n");
> + i = 0;
> + for_each_online_cpu(i) {
> + if (stream[i].workspace)
> + kfree(stream[i].workspace);
> + }
> + if (stream)
> + kfree(stream);
> + return NULL;
> +}
> +
> +
> +static void zlib_free(void *strm)
> +{
> + z_stream *stream = strm;
> + unsigned int i = 0;
> +
> + for_each_online_cpu(i) {
> + if (stream[i].workspace)
> + kfree(stream[i].workspace);
> + }
> + if (stream)
> + kfree(stream);
> + strm = NULL;
> +}
> +
> +
> +static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer,
> + struct buffer_head **bh, int b, int offset, int length, int srclength,
> + int pages)
> +{
> + int zlib_err = 0;
> + int avail, bytes, k = 0, page = 0;
> + unsigned int pid = smp_processor_id();
> + z_stream *stream = msblk->stream;
> +
> + mutex_lock(&msblk->read_data_mutex[pid]);
> + /*printk(KERN_INFO "[%s] pid %d \n",__FUNCTION__,pid);*/
> + /*
> + * We are resetting zlib stream here so that it avoids the
> + * overhead of zlib_init again and again for each
> + * request.
> + */
> + zlib_err = zlib_inflateReset(&(stream[pid]));
> + if (zlib_err != Z_OK) {
> + ERROR("zlib_Reset returned %d \n", zlib_err);
> + printk(KERN_EMERG"zlib_Reset returned %d \n", zlib_err);
> + goto release_mutex;
> + }
> +
> + stream[pid].avail_out = 0;
> + stream[pid].avail_in = 0;
> +
> + bytes = length;
> + do {
> + if (stream[pid].avail_in == 0 && k < b) {
> + avail = min(bytes, msblk->devblksize - offset);
> + bytes -= avail;
> + wait_on_buffer(bh[k]);
> + if (!buffer_uptodate(bh[k]))
> + goto release_mutex;
> +
> + if (avail == 0) {
> + offset = 0;
> + put_bh(bh[k++]);
> + continue;
> + }
> +
> + stream[pid].next_in = bh[k]->b_data + offset;
> + stream[pid].avail_in = avail;
> + offset = 0;
> + }
> +
> + if (stream[pid].avail_out == 0 && page < pages) {
> + stream[pid].next_out = buffer[page++];
> + stream[pid].avail_out = PAGE_CACHE_SIZE;
> + }
> +#if 0
> + if (!zlib_init) {
> + zlib_err = zlib_inflateInit(&(stream[pid]));
> + if (zlib_err != Z_OK) {
> + ERROR("zlib_inflateInit returned unexpected "
> + "result 0x%x, srclength %d\n",
> + zlib_err, srclength);
> + goto release_mutex;
> + }
> + zlib_init = 1;
> + }
> +#endif
> +
> + zlib_err = zlib_inflate(&(stream[pid]), Z_SYNC_FLUSH);
> +
> + if (stream[pid].avail_in == 0 && k < b)
> + put_bh(bh[k++]);
> + } while (zlib_err == Z_OK);
> +
> + if (zlib_err != Z_STREAM_END) {
> + ERROR("zlib_inflate error, data probably corrupt %d \n", zlib_err);
> + printk(KERN_INFO"avail in %d avail out %d \n",
> stream[pid].avail_in, stream[pid].avail_out);
> + goto release_mutex;
> + }
> +#if 0
> + zlib_err = zlib_inflateEnd(&(stream[pid]));
> + if (zlib_err != Z_OK) {
> + ERROR("zlib_inflate error, data probably corrupt\n");
> + goto release_mutex;
> + }
> +#endif
> + length = stream[pid].total_out;
> + mutex_unlock(&msblk->read_data_mutex[pid]);
> + return length;
> +
> +release_mutex:
> + mutex_unlock(&msblk->read_data_mutex[pid]);
> +
> + for (; k < b; k++)
> + put_bh(bh[k]);
> +
> + return -EIO;
> +}
> +
> +#else /* MPCORE*/
> +
> static void *zlib_init(struct squashfs_sb_info *dummy, void *buff, int len)
> {
> z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL);
> @@ -137,7 +293,7 @@ release_mutex:
>
> return -EIO;
> }
> -
> +#endif /* MPCORE*/
> const struct squashfs_decompressor squashfs_zlib_comp_ops = {
> .init = zlib_init,
> .free = zlib_free,
> --
> 1.7.9.5
>
>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/