commit 5578625143f381cb4ee5c68b23c639f7d4daad0f
Author: Yiannis Pericleous <[EMAIL PROTECTED]>
Date: Mon Apr 2 16:24:33 2007 -0400
non-recursive cleanup, and stop cleaning if threshold is reached
diff --git a/fs/unionfs/dirhelper.c b/fs/unionfs/dirhelper.c
index af7bd82..ba37ec9 100644
--- a/fs/unionfs/dirhelper.c
+++ b/fs/unionfs/dirhelper.c
@@ -26,6 +26,29 @@
#define RM_ALL 0
#define RM_LEAF 1
+#define STACK_SIZE 64 /* initial size of the dentry stack */
+
+/* a stack for dentries */
+struct dentry_stack {
+ struct dentry **item;
+ int n;
+ int size;
+};
+
+/* reallocs at double the size */
+int __resize_stack(struct dentry_stack *stack)
+{
+ struct dentry **new_stack;
+ int new_sz = stack->size * 2;
+ new_stack = kmalloc(new_sz * sizeof(struct dentry *), GFP_KERNEL);
+ if (!new_stack)
+ return -ENOMEM;
+ memcpy(new_stack, stack->item, stack->size * sizeof(struct dentry *));
+ kfree(stack->item);
+ stack->size = new_sz;
+ stack->item = new_stack;
+ return 0;
+}
/* The callback structure for check_empty. */
struct unionfs_rdutil_callback {
int err;
@@ -35,6 +58,7 @@ struct unionfs_rdutil_callback {
struct unionfs_dir_state *rdstate;
int mode;
};
+
struct unionfs_rmdir_callback {
int err;
int filldir_called;
@@ -43,6 +67,19 @@ struct unionfs_rmdir_callback {
int isleaf;
int mode;
};
+
+/* callback structure for odf_cleanup*/
+struct unionfs_cleanup_callback {
+ int err;
+ int filldir_called;
+ int reclaim;
+ struct dentry *dir;
+ struct vfsmount *mnt;
+ struct dentry_stack *stack;
+ u64 blocks;
+ u64 inodes;
+};
+
static int rmdir_util_callback(void *dirent, const char *name, int namelen,
loff_t offset, u64 ino, unsigned int d_type)
{
@@ -76,6 +113,67 @@ out:
buf->err = err;
return err;
}
+
+/* odf_cleanup callback */
+static int cleanup_util_callback(void *dirent, const char *name, int namelen,
+ loff_t offset, u64 ino, unsigned int d_type)
+{
+ int err = 0;
+ struct unionfs_cleanup_callback *buf = dirent;
+ struct dentry *dentry;
+ loff_t bytes, size;
+ int blocks;
+
+ buf->filldir_called++;
+
+ if (name[0] == '.' &&
+ (namelen == 1 || (name[1] == '.' && namelen == 2)))
+ goto out;
+
+ /* remove dir cache files or all if files if cleaning /reclaim */
+ if ((d_type == DT_REG && buf->reclaim) ||
+ (namelen == ODF_CONTENT_LEN &&
+ !strncmp(name, ODF_CONTENT, namelen))) {
+
+ dentry = lookup_one_len(name, buf->dir, namelen);
+
+ /* calculate the blocks to be freed */
+ blocks = dentry->d_inode->i_blocks;
+ bytes = blocks * dentry->d_sb->s_blocksize;
+ size = i_size_read(dentry->d_inode);
+ while (bytes > size) {
+ bytes -= dentry->d_sb->s_blocksize;
+ blocks--;
+ }
+ buf->blocks += blocks + 1;
+ buf->inodes++;
+
+ if (IS_ERR(dentry)) {
+ err = PTR_ERR(dentry);
+ goto out;
+ }
+ BUG_ON(!dentry->d_inode);
+ BUG_ON(d_type != DT_REG);
+ err = vfs_unlink(buf->dir->d_inode, dentry);
+ dput(dentry);
+ }
+ else if (d_type == DT_DIR) {
+ /* add all dir dentries to the dentry stack */
+ dentry = lookup_one_len(name, buf->dir, namelen);
+ if (IS_ERR(dentry)) {
+ err = PTR_ERR(dentry);
+ goto out;
+ }
+ BUG_ON(!dentry->d_inode);
+ buf->stack->item[buf->stack->n++] = dentry;
+ if (buf->stack->n == buf->stack->size)
+ err = __resize_stack(buf->stack);
+ }
+out:
+ buf->err = err;
+ return err;
+}
+
/* This filldir function makes sure only whiteouts exist within a directory. */
static int readdir_util_callback(void *dirent, const char *name, int namelen,
loff_t offset, u64 ino, unsigned int d_type)
@@ -541,3 +639,147 @@ out:
kfree(buf);
return err;
}
+
+/*
+ * Cleanup function for the odf cleanup thread.
+ * First cleans up the dir caches in odf/ic and then everything
+ * in odf/reclaim. It stops once the requested blocks/inodes
+ * were freed.
+ * size contains the requested amount of inodes/blocks to be freed
+ * MULTIPLIED BY 100
+ * Modes :
+ * ODF_CLEAN_ALL: cleans all the dir caches and odf/reclaim
+ * ODF_CLEAN_CACHE: cleans all the dir caches
+ * ODF_CLEAN_BLOCKS: clean until size blocks are freed
+ * ODF_CLEAN_INODES: clean until size inodes are freed
+ */
+int odf_cleanup(struct odf_sb_info *odf, int mode, u64 size)
+{
+ int err = 0;
+ struct file *file;
+ struct unionfs_cleanup_callback *buf = NULL;
+ struct dentry *dentry;
+ struct dentry_stack stack;
+ int success = 0, reclaim = 0;
+ loff_t bytes, isize;
+ int blocks;
+
+ stack.size = STACK_SIZE;
+ stack.n = 0;
+ stack.item = kmalloc(stack.size * sizeof(struct dentry *), GFP_KERNEL);
+ if (!stack.item) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ buf = kmalloc(sizeof(struct unionfs_cleanup_callback), GFP_KERNEL);
+ if (!buf) {
+ err = -ENOMEM;
+ goto out;
+ }
+ buf->err = 0;
+ buf->mnt = odf->mnt;
+ buf->reclaim = reclaim;
+ buf->blocks = 0;
+ buf->inodes = 0;
+ buf->stack = &stack;
+
+ /* first cleanup odf/ic */
+ dget(odf->odi_ic->dentry);
+ stack.item[stack.n++] = odf->odi_ic->dentry;
+
+
+cleanup_loop:
+ /* The cleanup loop pops a dentry off the stack, reads all
+ * its entries, unlinking dir cache files and files in
+ * odf/reclaim and pushing to the stack all directories
+ */
+ while (err >= 0 && stack.n > 0 && !success) {
+ dentry = stack.item[--stack.n]; /* pop */
+
+ /* we need to dget /reclaim dirs again since we need
+ * them after we close the file
+ */
+ if (reclaim)
+ dget(dentry);
+ mntget(odf->mnt);
+
+ file = dentry_open(dentry, odf->mnt, O_RDONLY);
+ if (IS_ERR(file)) {
+ err = PTR_ERR(file);
+ dput(dentry);
+ goto out;
+ }
+
+ buf->dir = dentry;
+ do {
+ buf->filldir_called = 0;
+ err = vfs_readdir(file, cleanup_util_callback, buf);
+ if (buf->err)
+ err = buf->err;
+ } while ((err >= 0) && buf->filldir_called);
+ fput(file);
+
+ if (!err && reclaim) {
+ /* remove all directories in odf/reclaim
+ * This assumes that the odf/reclaim dir structure
+ * is entirely flat, ie only odf/reclaim contains
+ * subdirectories
+ */
+ if (dentry != odf->odi_rc->dentry) {
+ blocks = dentry->d_inode->i_blocks;
+ bytes = blocks * dentry->d_sb->s_blocksize;
+ isize = i_size_read(dentry->d_inode);
+ while (bytes > isize) {
+ bytes -= dentry->d_sb->s_blocksize;
+ blocks--;
+ }
+ buf->blocks += blocks + 1;
+ buf->inodes++;
+ err = vfs_rmdir(dentry->d_parent->d_inode,
dentry);
+ BUG_ON(err == -ENOTEMPTY);
+ }
+ dput(dentry);
+ }
+
+ /* check if we have reached the threshold */
+ if (mode == ODF_CLEAN_BLOCKS && buf->blocks * 100 >= size)
+ success = 1;
+ else if (mode == ODF_CLEAN_INODES && buf->inodes * 100 >= size)
+ success = 1;
+ }
+
+ if (err < 0)
+ goto out;
+
+ if (mode == ODF_CLEAN_CACHE)
+ success = 1;
+
+ if (!success) {
+ BUG_ON(stack.n);
+
+ /* if we did not succeed, clean up odf/reclaim as well */
+ if (!reclaim) {
+ reclaim = 1;
+ buf->reclaim = 1;
+ dget(odf->odi_rc->dentry);
+ stack.item[stack.n++] = odf->odi_rc->dentry;
+ goto cleanup_loop;
+ }
+ else if (mode == ODF_CLEAN_BLOCKS)
+ printk("unionfs: Failed to bring free odf data blocks
below threshold\n");
+ else if (mode == ODF_CLEAN_INODES)
+ printk("unionfs: Failed to bring free odf inodes below
threshold\n");
+ }
+
+
+out:
+ BUG_ON(stack.n < 0);
+ while(stack.n)
+ dput(stack.item[--stack.n]);
+
+ kfree(buf);
+ kfree(stack.item);
+ return err;
+}
+
diff --git a/fs/unionfs/odf.c b/fs/unionfs/odf.c
index 38e1490..b49872e 100644
--- a/fs/unionfs/odf.c
+++ b/fs/unionfs/odf.c
@@ -497,7 +497,7 @@ retry:
if (err) {
if (err == -ENOSPC && !cleaned) {
- /* let the cleanup thread do its work and retry
once */
+ /* let the cleanup thread do its work and retry
once */
if (flags & ODF_LOOKUP_LOCKED)
odf_unlock(osi->odi_ic);
cleaned = 1;
@@ -1401,37 +1401,40 @@ void __odf_cleanup(void *args)
{
struct sioa_args *sioa_args = (struct sioa_args *)args;
struct cleanup_args *cl = &sioa_args->cleanup;
- struct odf_dentry_info *odi_rc, *odi_ic;
struct kstatfs stat;
int cleanup = 0;
+ u64 size = 0;
int err = 0;
/* update timeout */
sioa_args->timeout = msecs_to_jiffies(cl->attr->timeout->val * 1000);
- odi_ic = cl->odf->odi_ic;
- odf_lock(odi_ic);
+ odf_lock(cl->odf->odi_ic);
+ odf_lock(cl->odf->odi_rc);
+
vfs_statfs(cl->odf->odi_sb->dentry, &stat);
if (cl->force) {
cl->force = 0;
+ cleanup = ODF_CLEAN_CACHE;
printk("unionfs cleanup thread: forced cleanup\n");
- cleanup = 1;
}
else if (stat.f_bavail * 100 < stat.f_blocks * (100 -
cl->attr->thresh_b->val)) {
- cleanup = 1;
+ cleanup = ODF_CLEAN_BLOCKS;
+ size = stat.f_blocks * (100 - cl->attr->thresh_b->val) -
+ (stat.f_bavail * 100);
printk("unionfs cleanup thread: free blocks below critical
size\n");
}
- else if (stat.f_ffree * 100 < stat.f_ffree * (100 -
cl->attr->thresh_i->val)) {
- cleanup = 1;
+ else if (stat.f_ffree * 100 < stat.f_files * (100 -
cl->attr->thresh_i->val)) {
+ cleanup = ODF_CLEAN_INODES;
+ size = stat.f_files * (100 - cl->attr->thresh_i->val) -
+ (stat.f_ffree * 100);
printk("unionfs cleanup thread: free inodes below critical
size\n");
}
if (cleanup)
- err = unionfs_force_rmdir(cl->odf->mnt, odi_ic->dentry,
FRMV_NOTPARENT|FRMV_LEAF);
+ err = odf_cleanup(cl->odf, cleanup, size);
+ if (err)
+ printk("unionfs cleanup thread: error %d\n", err);
- odf_unlock(odi_ic);
-
- odi_rc = cl->odf->odi_rc;
- odf_lock(odi_rc);
- unionfs_force_rmdir(cl->odf->mnt, odi_rc->dentry, FRMV_NOTPARENT);
- odf_unlock(odi_rc);
+ odf_unlock(cl->odf->odi_ic);
+ odf_unlock(cl->odf->odi_rc);
}
diff --git a/fs/unionfs/odf.h b/fs/unionfs/odf.h
index 80546d9..27018a2 100644
--- a/fs/unionfs/odf.h
+++ b/fs/unionfs/odf.h
@@ -41,6 +41,11 @@
#define ODF_CONTENT "content"
#define ODF_CONTENT_LEN 7
+#define ODF_CLEAN_ALL 1
+#define ODF_CLEAN_CACHE 2
+#define ODF_CLEAN_INODES 3
+#define ODF_CLEAN_BLOCKS 4
+
/* super */
struct odf_sb_info* odf_read_super(char *options);
void odf_put_super(struct odf_sb_info *osi);
@@ -99,6 +104,7 @@ int __odf_set_opaque(struct inode *i, int branch);
/* cleanup thread functions */
extern void __odf_cleanup(void *args);
+int odf_cleanup(struct odf_sb_info *odf, int mode, u64 size);
/* Macros for locking an odf dentry info. */
static inline void odf_lock(struct odf_dentry_info *odi)
_______________________________________________
unionfs-cvs mailing list: http://unionfs.filesystems.org/
[email protected]
http://www.fsl.cs.sunysb.edu/mailman/listinfo/unionfs-cvs