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

Reply via email to