On 6/15/20 8:02 AM, Sascha Hauer wrote:
> This changes the way ramfs stores its data. So far we used equally sized
> chunks, this patch changes it to use chunks in a size that fits our
> needs. The chunks are always allocated in the size they are needed for
> the current truncation. Only if we fail to allocate all desired memory
> at once we fall back to allocating smaller chunks. Together with using
> the generic list implementation this results in smaller code and has
> the advantage that many image files end up being contiguously in memory
> and thus we can provide a memmap for them. Files will end up
> contiguously in memory when they are first created, then truncated to
> the final size and then filled up with data. This is something which
> is normally easily achievable when desired.
> 
> Signed-off-by: Sascha Hauer <[email protected]>
> ---
>  fs/ramfs.c | 307 +++++++++++++++++++++++++++--------------------------
>  1 file changed, 159 insertions(+), 148 deletions(-)
> 
> diff --git a/fs/ramfs.c b/fs/ramfs.c
> index 2b6df07996..ebe03de736 100644
> --- a/fs/ramfs.c
> +++ b/fs/ramfs.c
> @@ -23,12 +23,15 @@
>  #include <errno.h>
>  #include <linux/stat.h>
>  #include <xfuncs.h>
> +#include <linux/sizes.h>
>  
>  #define CHUNK_SIZE   (4096 * 2)
>  
>  struct ramfs_chunk {
>       char *data;
> -     struct ramfs_chunk *next;
> +     unsigned long ofs;
> +     int size;
> +     struct list_head list;
>  };
>  
>  struct ramfs_inode {
> @@ -37,12 +40,14 @@ struct ramfs_inode {
>       char *symlink;
>       ulong mode;
>  
> -     ulong size;
> -     struct ramfs_chunk *data;
> +     /* bytes used in this inode */
> +     unsigned long size;
> +     /* bytes currently allocated for this inode */
> +     unsigned long alloc_size;
> +
> +     struct list_head data;
>  
> -     /* Points to recently used chunk */
> -     int recent_chunk;
> -     struct ramfs_chunk *recent_chunkp;
> +     struct ramfs_chunk *current_chunk;
>  };
>  
>  static inline struct ramfs_inode *to_ramfs_inode(struct inode *inode)
> @@ -89,18 +94,25 @@ static struct inode *ramfs_get_inode(struct super_block 
> *sb, const struct inode
>       return inode;
>  }
>  
> -static struct ramfs_chunk *ramfs_get_chunk(void)
> +#define MIN_SIZE SZ_8K
> +
> +static struct ramfs_chunk *ramfs_get_chunk(unsigned long size)
>  {
>       struct ramfs_chunk *data = malloc(sizeof(struct ramfs_chunk));
> +
>       if (!data)
>               return NULL;
>  
> -     data->data = calloc(CHUNK_SIZE, 1);
> +     if (size < MIN_SIZE)
> +             size = MIN_SIZE;
> +
> +     data->data = calloc(size, 1);
>       if (!data->data) {
>               free(data);
>               return NULL;
>       }
> -     data->next = NULL;
> +
> +     data->size = size;
>  
>       return data;
>  }
> @@ -160,23 +172,6 @@ static int ramfs_symlink(struct inode *dir, struct 
> dentry *dentry,
>  
>  static int ramfs_unlink(struct inode *dir, struct dentry *dentry)
>  {
> -     struct inode *inode = d_inode(dentry);
> -
> -     if (inode) {
> -             struct ramfs_inode *node = to_ramfs_inode(inode);
> -             struct ramfs_chunk *chunk = node->data;
> -
> -             node->data = NULL;
> -
> -             while (chunk) {
> -                     struct ramfs_chunk *tmp = chunk;
> -
> -                     chunk = chunk->next;
> -
> -                     ramfs_put_chunk(tmp);
> -             }
> -     }
> -
>       return simple_unlink(dir, dentry);
>  }
>  
> @@ -200,80 +195,57 @@ static const struct inode_operations 
> ramfs_dir_inode_operations =
>       .create = ramfs_create,
>  };
>  
> -static struct ramfs_chunk *ramfs_find_chunk(struct ramfs_inode *node, int 
> chunk)
> +static struct ramfs_chunk *ramfs_find_chunk(struct ramfs_inode *node,
> +                                         unsigned long pos, int *ofs, int 
> *len)
>  {
> -     struct ramfs_chunk *data;
> -     int left = chunk;
> +     struct ramfs_chunk *data, *cur = node->current_chunk;
>  
> -     if (chunk == 0)
> -             return node->data;
> +     if (cur && pos >= cur->ofs)
> +             data = cur;
> +     else
> +             data = list_first_entry(&node->data, struct ramfs_chunk, list);
>  
> -     if (node->recent_chunk == chunk)
> -             return node->recent_chunkp;
> +     list_for_each_entry_from(data, &node->data, list) {
> +             if (data->ofs + data->size > pos) {
> +                     *ofs = pos - data->ofs;
> +                     *len = data->ofs + data->size - pos;
>  
> -     if (node->recent_chunk < chunk && node->recent_chunk != 0) {
> -             /* Start at last known chunk */
> -             data = node->recent_chunkp;
> -             left -= node->recent_chunk;
> -     } else {
> -             /* Start at first chunk */
> -             data = node->data;
> -     }
> +                     node->current_chunk = data;
>  
> -     while (left--)
> -             data = data->next;
> +                     return data;
> +             }
> +     }
>  
> -     node->recent_chunkp = data;
> -     node->recent_chunk = chunk;
> +     pr_err("%s: no chunk for pos %ld found\n", __func__, pos);
>  
> -     return data;
> +     return NULL;
>  }
>  
>  static int ramfs_read(struct device_d *_dev, FILE *f, void *buf, size_t 
> insize)
>  {
>       struct inode *inode = f->f_inode;
>       struct ramfs_inode *node = to_ramfs_inode(inode);
> -     int chunk;
>       struct ramfs_chunk *data;
> -     int ofs;
> -     int now;
> -     int pos = f->pos;
> +     int ofs, len, now;
> +     unsigned long pos = f->pos;
>       int size = insize;
>  
> -     chunk = pos / CHUNK_SIZE;
> -     debug("%s: reading from chunk %d\n", __FUNCTION__, chunk);
> +     debug("%s: %p %d @ %lld\n", __func__, node, insize, f->pos);
> +
> +     while (size) {
> +             data = ramfs_find_chunk(node, pos, &ofs, &len);
> +             if (!data)
> +                     return -EINVAL;
>  
> -     /* Position ourself in stream */
> -     data = ramfs_find_chunk(node, chunk);
> -     ofs = pos % CHUNK_SIZE;
> +             debug("%s: pos: %ld ofs: %d len: %d\n", __func__, pos, ofs, 
> len);
> +
> +             now = min(size, len);
>  
> -     /* Read till end of current chunk */
> -     if (ofs) {
> -             now = min(size, CHUNK_SIZE - ofs);
> -             debug("Reading till end of node. size: %d\n", size);
>               memcpy(buf, data->data + ofs, now);
> +
>               size -= now;
> -             pos += now;
>               buf += now;
> -             if (pos > node->size)
> -                     node->size = now;
> -             data = data->next;
> -     }
> -
> -     /* Do full chunks */
> -     while (size >= CHUNK_SIZE) {
> -             debug("do full chunk. size: %d\n", size);
> -             memcpy(buf, data->data, CHUNK_SIZE);
> -             data = data->next;
> -             size -= CHUNK_SIZE;
> -             pos += CHUNK_SIZE;
> -             buf += CHUNK_SIZE;
> -     }
> -
> -     /* And the rest */
> -     if (size) {
> -             debug("do rest. size: %d\n", size);
> -             memcpy(buf, data->data, size);
> +             pos += now;
>       }
>  
>       return insize;
> @@ -283,100 +255,135 @@ static int ramfs_write(struct device_d *_dev, FILE 
> *f, const void *buf, size_t i
>  {
>       struct inode *inode = f->f_inode;
>       struct ramfs_inode *node = to_ramfs_inode(inode);
> -     int chunk;
>       struct ramfs_chunk *data;
> -     int ofs;
> -     int now;
> -     int pos = f->pos;
> +     int ofs, len, now;
> +     unsigned long pos = f->pos;

On 32-bit systems, you are truncating a 64-bit pos to 32-bit here. Is this 
intended?

>       int size = insize;
>  
> -     chunk = f->pos / CHUNK_SIZE;
> -     debug("%s: writing to chunk %d\n", __FUNCTION__, chunk);
> +     debug("%s: %p %d @ %lld\n", __func__, node, insize, f->pos);
> +
> +     while (size) {
> +             data = ramfs_find_chunk(node, pos, &ofs, &len);
> +             if (!data)
> +                     return -EINVAL;
> +
> +             debug("%s: pos: %ld ofs: %d len: %d\n", __func__, pos, ofs, 
> len);
>  
> -     /* Position ourself in stream */
> -     data = ramfs_find_chunk(node, chunk);
> -     ofs = f->pos % CHUNK_SIZE;
> +             now = min(size, len);
>  
> -     /* Write till end of current chunk */
> -     if (ofs) {
> -             now = min(size, CHUNK_SIZE - ofs);
> -             debug("writing till end of node. size: %d\n", size);
>               memcpy(data->data + ofs, buf, now);
> +
>               size -= now;
> -             pos += now;
>               buf += now;
> -             if (pos > node->size)
> -                     node->size = now;
> -             data = data->next;
> +             pos += now;
> +     }
> +
> +     return insize;
> +}
> +
> +static void ramfs_truncate_down(struct ramfs_inode *node, unsigned long size)
> +{
> +     struct ramfs_chunk *data, *tmp;
> +
> +     list_for_each_entry_safe(data, tmp, &node->data, list) {
> +             if (data->ofs >= size) {
> +                     list_del(&data->list);
> +                     node->alloc_size -= data->size;
> +                     ramfs_put_chunk(data);
> +             }
>       }
>  
> -     /* Do full chunks */
> -     while (size >= CHUNK_SIZE) {
> -             debug("do full chunk. size: %d\n", size);
> -             memcpy(data->data, buf, CHUNK_SIZE);
> -             data = data->next;
> -             size -= CHUNK_SIZE;
> -             pos += CHUNK_SIZE;
> -             buf += CHUNK_SIZE;
> +     node->current_chunk = NULL;
> +}
> +
> +static int ramfs_truncate_up(struct ramfs_inode *node, unsigned long size)
> +{
> +     struct ramfs_chunk *data, *tmp;
> +     LIST_HEAD(list);
> +     unsigned long add = size - node->alloc_size;
> +     unsigned long chunksize = add;
> +     unsigned long alloc_size = 0;
> +
> +     if (node->alloc_size >= size)
> +             return 0;
> +
> +     /*
> +      * We first try to allocate all space we need in a single chunk.
> +      * This may fail because of fragmented memory, so in case we cannot
> +      * allocate memory we successively decrease the chunk size until
> +      * we have enough allocations made.
> +      */
> +     while (1) {
> +             unsigned long now = min(chunksize, add);
> +
> +             data = ramfs_get_chunk(now);
> +             if (!data) {
> +                     /* No luck, try with smaller chunk size */
> +                     chunksize >>= 1;
> +
> +                     /* If we do not have even 128KiB then go out */
> +                     if (chunksize < SZ_128K)
> +                             goto out;
> +
> +                     continue;
> +             }
> +
> +             data->ofs = node->alloc_size + alloc_size;
> +
> +             alloc_size += data->size;
> +
> +             list_add_tail(&data->list, &list);
> +
> +             if (add <= data->size)
> +                     break;
> +
> +             add -= data->size;
>       }
>  
> -     /* And the rest */
> -     if (size) {
> -             debug("do rest. size: %d\n", size);
> -             memcpy(data->data, buf, size);
> +     list_splice_tail(&list, &node->data);
> +
> +     node->alloc_size += alloc_size;
> +
> +     return 0;
> +
> +out:
> +     list_for_each_entry_safe(data, tmp, &list, list) {
> +             list_del(&data->list);
> +             ramfs_put_chunk(data);
>       }
>  
> -     return insize;
> +     return -ENOSPC;
>  }
>  
>  static int ramfs_truncate(struct device_d *dev, FILE *f, loff_t size)
>  {
>       struct inode *inode = f->f_inode;
>       struct ramfs_inode *node = to_ramfs_inode(inode);
> -     int oldchunks, newchunks;
> -     struct ramfs_chunk *data = node->data;
> -
> -     newchunks = (size + CHUNK_SIZE - 1) / CHUNK_SIZE;
> -     oldchunks = (node->size + CHUNK_SIZE - 1) / CHUNK_SIZE;
> -
> -     if (newchunks < oldchunks) {
> -             if (!newchunks)
> -                     node->data = NULL;
> -             while (newchunks--)
> -                     data = data->next;
> -             while (data) {
> -                     struct ramfs_chunk *tmp;
> -                     tmp = data->next;
> -                     ramfs_put_chunk(data);
> -                     data = tmp;
> -             }
> -             if (node->recent_chunk > newchunks)
> -                     node->recent_chunk = 0;
> -     }
> +     int ret;
>  
> -     if (newchunks > oldchunks) {
> -             if (data) {
> -                     data = ramfs_find_chunk(node, oldchunks - 1);
> -             } else {
> -                     node->data = ramfs_get_chunk();
> -                     if (!node->data)
> -                             return -ENOSPC;
> -                     data = node->data;
> -                     oldchunks = 1;
> -             }
> +     /*
> +      * This is a malloc based filesystem, no need to support more
> +      * memory than fits into unsigned long.
> +      */
> +     if (size > ULONG_MAX)
> +             return -ENOSPC;
>  
> -             while (data->next)
> -                     data = data->next;
> +     debug("%s: %p cur: %ld new: %lld alloc: %ld\n", __func__, node,
> +            node->size, size, node->alloc_size);
>  
> -             while (newchunks > oldchunks) {
> -                     data->next = ramfs_get_chunk();
> -                     if (!data->next)
> -                             return -ENOSPC;
> -                     data = data->next;
> -                     oldchunks++;
> -             }
> +     if (size == node->size)
> +             return 0;
> +
> +     if (size < node->size) {
> +             ramfs_truncate_down(node, size);
> +     } else {
> +             ret = ramfs_truncate_up(node, size);
> +             if (ret)
> +                     return ret;
>       }
> +
>       node->size = size;
> +
>       return 0;
>  }
>  
> @@ -386,6 +393,8 @@ static struct inode *ramfs_alloc_inode(struct super_block 
> *sb)
>  
>       node = xzalloc(sizeof(*node));
>  
> +     INIT_LIST_HEAD(&node->data);
> +
>       return &node->inode;
>  }
>  
> @@ -393,6 +402,8 @@ static void ramfs_destroy_inode(struct inode *inode)
>  {
>       struct ramfs_inode *node = to_ramfs_inode(inode);
>  
> +     ramfs_truncate_down(node, 0);
> +
>       free(node);
>  }
>  
> 

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/barebox

Reply via email to