diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2020-07-27 21:58:45 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2020-07-27 21:58:45 +0200 |
commit | 8c96ab8178ff33e9111faa0ee60dcdcb16047720 (patch) | |
tree | d1c8dc8524043095b29c72b8e075a494f51231ef /fs | |
parent | 154bc2aa3cb51104bdf213463da4fef9866c5a7b (diff) | |
parent | a5f73a6dcd5296abb76c50615cb7c1ddfb227708 (diff) | |
download | barebox-8c96ab8178ff33e9111faa0ee60dcdcb16047720.tar.gz barebox-8c96ab8178ff33e9111faa0ee60dcdcb16047720.tar.xz |
Merge branch 'for-next/ramfs'
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cramfs/cramfs.c | 10 | ||||
-rw-r--r-- | fs/devfs.c | 8 | ||||
-rw-r--r-- | fs/fs.c | 7 | ||||
-rw-r--r-- | fs/nfs.c | 8 | ||||
-rw-r--r-- | fs/ramfs.c | 349 | ||||
-rw-r--r-- | fs/squashfs/squashfs.c | 10 |
6 files changed, 228 insertions, 164 deletions
diff --git a/fs/cramfs/cramfs.c b/fs/cramfs/cramfs.c index 99cbdb920c..3ea6bd437e 100644 --- a/fs/cramfs/cramfs.c +++ b/fs/cramfs/cramfs.c @@ -333,6 +333,15 @@ static struct inode *cramfs_alloc_inode(struct super_block *sb) return &info->i_inode; } +static void cramfs_destroy_inode(struct inode *inode) +{ + struct cramfs_inode_info *info; + + info = to_cramfs_inode_info(inode); + + free(info); +} + static int cramfs_iterate(struct file *file, struct dir_context *ctx) { struct dentry *dentry = file->f_path.dentry; @@ -427,6 +436,7 @@ static const struct inode_operations cramfs_symlink_inode_operations = static const struct super_operations cramfs_ops = { .alloc_inode = cramfs_alloc_inode, + .destroy_inode = cramfs_destroy_inode, }; static int cramfs_probe(struct device_d *dev) diff --git a/fs/devfs.c b/fs/devfs.c index b503f277ac..df229cca48 100644 --- a/fs/devfs.c +++ b/fs/devfs.c @@ -200,6 +200,13 @@ static struct inode *devfs_alloc_inode(struct super_block *sb) return &node->inode; } +static void devfs_destroy_inode(struct inode *inode) +{ + struct devfs_inode *node = container_of(inode, struct devfs_inode, inode); + + free(node); +} + static int devfs_iterate(struct file *file, struct dir_context *ctx) { struct cdev *cdev; @@ -314,6 +321,7 @@ static const struct inode_operations devfs_dir_inode_operations = static const struct super_operations devfs_ops = { .alloc_inode = devfs_alloc_inode, + .destroy_inode = devfs_destroy_inode, }; static int devfs_probe(struct device_d *dev) @@ -1087,10 +1087,15 @@ void iget_failed(struct inode *inode) void iput(struct inode *inode) { - if (!inode->i_count) + if (!inode) return; inode->i_count--; + + if (!inode->i_count) { + list_del(&inode->i_sb_list); + destroy_inode(inode); + } } struct inode *iget(struct inode *inode) @@ -1202,6 +1202,13 @@ static struct inode *nfs_alloc_inode(struct super_block *sb) return &node->inode; } +static void nfs_destroy_inode(struct inode *inode) +{ + struct nfs_inode *node = nfsi(inode); + + free(node); +} + static const struct inode_operations nfs_file_inode_operations; static const struct file_operations nfs_dir_operations; static const struct inode_operations nfs_dir_inode_operations; @@ -1273,6 +1280,7 @@ static const struct inode_operations nfs_dir_inode_operations = static const struct super_operations nfs_ops = { .alloc_inode = nfs_alloc_inode, + .destroy_inode = nfs_destroy_inode, }; static char *rootnfsopts; diff --git a/fs/ramfs.c b/fs/ramfs.c index 5328775ee0..14ba877660 100644 --- a/fs/ramfs.c +++ b/fs/ramfs.c @@ -23,29 +23,31 @@ #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 { struct inode inode; char *name; - struct ramfs_inode *parent; - struct ramfs_inode *next; - struct ramfs_inode *child; 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) @@ -53,10 +55,6 @@ static inline struct ramfs_inode *to_ramfs_inode(struct inode *inode) return container_of(inode, struct ramfs_inode, inode); } -struct ramfs_priv { - struct ramfs_inode root; -}; - /* ---------------------------------------------------------------*/ static const struct super_operations ramfs_ops; @@ -96,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; } @@ -167,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); } @@ -207,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 %zu @ %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: %lu 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; @@ -290,100 +255,154 @@ 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; int size = insize; - chunk = f->pos / CHUNK_SIZE; - debug("%s: writing to chunk %d\n", __FUNCTION__, chunk); + debug("%s: %p %zu @ %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 = f->pos % CHUNK_SIZE; + debug("%s: pos: %lu ofs: %d len: %d\n", __func__, pos, ofs, len); + + 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; } - /* 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; + 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); + } } - /* And the rest */ - if (size) { - debug("do rest. size: %d\n", size); - memcpy(data->data, buf, 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; } - return insize; + 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 -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 -ENOMEM; - 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 -ENOMEM; - 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; +} + +static int ramfs_memmap(struct device_d *_dev, FILE *f, void **map, int flags) +{ + struct inode *inode = f->f_inode; + struct ramfs_inode *node = to_ramfs_inode(inode); + struct ramfs_chunk *data; + + if (list_empty(&node->data)) + return -EINVAL; + + if (!list_is_singular(&node->data)) + return -EINVAL; + + data = list_first_entry(&node->data, struct ramfs_chunk, list); + + *map = data->data; + return 0; } @@ -393,26 +412,31 @@ static struct inode *ramfs_alloc_inode(struct super_block *sb) node = xzalloc(sizeof(*node)); + INIT_LIST_HEAD(&node->data); + return &node->inode; } +static void ramfs_destroy_inode(struct inode *inode) +{ + struct ramfs_inode *node = to_ramfs_inode(inode); + + ramfs_truncate_down(node, 0); + + free(node); +} + static const struct super_operations ramfs_ops = { .alloc_inode = ramfs_alloc_inode, + .destroy_inode = ramfs_destroy_inode, }; static int ramfs_probe(struct device_d *dev) { struct inode *inode; - struct ramfs_priv *priv = xzalloc(sizeof(struct ramfs_priv)); struct fs_device_d *fsdev = dev_to_fs_device(dev); struct super_block *sb = &fsdev->sb; - dev->priv = priv; - - priv->root.name = "/"; - priv->root.mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO; - priv->root.parent = &priv->root; - sb->s_op = &ramfs_ops; inode = ramfs_get_inode(sb, NULL, S_IFDIR); @@ -429,6 +453,7 @@ static void ramfs_remove(struct device_d *dev) static struct fs_driver_d ramfs_driver = { .read = ramfs_read, .write = ramfs_write, + .memmap = ramfs_memmap, .truncate = ramfs_truncate, .flags = FS_DRIVER_NO_DEV, .drv = { diff --git a/fs/squashfs/squashfs.c b/fs/squashfs/squashfs.c index 38aff6d5b8..69451f7b84 100644 --- a/fs/squashfs/squashfs.c +++ b/fs/squashfs/squashfs.c @@ -76,8 +76,16 @@ static struct inode *squashfs_alloc_inode(struct super_block *sb) return &node->vfs_inode; } +static void squashfs_destroy_inode(struct inode *inode) +{ + struct squashfs_inode_info *node = squashfs_i(inode); + + free(node); +} + static const struct super_operations squashfs_super_ops = { - .alloc_inode = squashfs_alloc_inode, + .alloc_inode = squashfs_alloc_inode, + .destroy_inode = squashfs_destroy_inode, }; static int squashfs_probe(struct device_d *dev) |