This patch tries to partially implement what Al Viro suggested in
https://lkml.org/lkml/2013/4/5/15

Code also mostly copied from the above link

Signed-off-by: Li Zhong <zh...@linux.vnet.ibm.com>
---
 fs/Makefile            |    2 +-
 fs/revoke.c            |  133 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/fs.h     |    2 +
 include/linux/revoke.h |   50 ++++++++++++++++++
 4 files changed, 186 insertions(+), 1 deletion(-)
 create mode 100644 fs/revoke.c
 create mode 100644 include/linux/revoke.h

diff --git a/fs/Makefile b/fs/Makefile
index 4fe6df3..af0a622 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -11,7 +11,7 @@ obj-y :=      open.o read_write.o file_table.o super.o \
                attr.o bad_inode.o file.o filesystems.o namespace.o \
                seq_file.o xattr.o libfs.o fs-writeback.o \
                pnode.o splice.o sync.o utimes.o \
-               stack.o fs_struct.o statfs.o
+               stack.o fs_struct.o statfs.o revoke.o
 
 ifeq ($(CONFIG_BLOCK),y)
 obj-y +=       buffer.o bio.o block_dev.o direct-io.o mpage.o ioprio.o
diff --git a/fs/revoke.c b/fs/revoke.c
new file mode 100644
index 0000000..bcda9ba
--- /dev/null
+++ b/fs/revoke.c
@@ -0,0 +1,133 @@
+#include <linux/revoke.h>
+#include <linux/rcupdate.h>
+#include <linux/slab.h>
+
+bool __start_using(struct revoke *revoke)
+{
+       struct revokable *r;
+       rcu_read_lock();
+       r = rcu_dereference(revoke->revokable);
+       if (unlikely(!r)) {
+               rcu_read_unlock();
+               return false;   /* revoked */
+       }
+
+       if (likely(atomic_inc_unless_negative(&r->in_use))) {
+               rcu_read_unlock();
+               return true;    /* we are using it now */
+       }
+
+       rcu_read_unlock();
+       return false;           /* it's being revoked right now */
+}
+
+#define BIAS (-1U<<31)
+
+void __stop_using(struct revoke *revoke)
+{
+       struct revokable *r;
+       r = rcu_dereference_protected(revoke->revokable, 1);
+       BUG_ON(!r);
+       if (atomic_dec_return(&r->in_use) == BIAS)
+               complete(r->c);
+}
+
+/* called with r->lock held by caller, unlocks it */
+static void __release_revoke(struct revokable *r, struct revoke *revoke)
+{
+       if (revoke->closing) {
+               DECLARE_COMPLETION_ONSTACK(c);
+               revoke->c = &c;
+               spin_unlock(&r->lock);
+               wait_for_completion(&c);
+       } else {
+               struct file *file;
+               revoke->closing = 1;
+               spin_unlock(&r->lock);
+               file = revoke->file;
+               if (file->f_op->release)
+                       file->f_op->release(file_inode(file), file);
+               spin_lock(&r->lock);
+               hlist_del_init(&revoke->node);
+               rcu_assign_pointer(revoke->revokable, NULL);
+               rcu_read_lock();        /* prevent freeing of r */
+               if (revoke->c)
+                       complete(revoke->c);
+               spin_unlock(&r->lock);
+               rcu_read_unlock();
+       }
+}
+
+void release_revoke(struct revoke *revoke)
+{
+       struct revokable *r;
+       rcu_read_lock();
+       r = rcu_dereference(revoke->revokable);
+       if (!r) {
+               /* already closed by revokation */
+               rcu_read_unlock();
+               goto out;
+       }
+
+       spin_lock(&r->lock);
+       if (unlikely(hlist_unhashed(&revoke->node))) {
+               /* just been revoked */
+               spin_unlock(&r->lock);
+               rcu_read_unlock();
+               goto out;
+       }
+
+       /*
+        * Ok, revoke_it() couldn't have been finished yet
+        * it'll have to get r->lock before it's through, so
+        * we can drop rcu_read_lock
+        */
+       rcu_read_unlock();
+       __release_revoke(r, revoke);
+out:
+       kfree(revoke);
+}
+
+void revoke_it(struct revokable *r)
+{
+       DECLARE_COMPLETION_ONSTACK(c);
+       r->c = &c;
+       if (atomic_add_return(BIAS, &r->in_use) != BIAS) {
+               if (r->kick)
+                       r->kick(r);
+               wait_for_completion(&c);
+       }
+
+       while (1) {
+               struct hlist_node *p;
+               spin_lock(&r->lock);
+               p = r->list.first;
+               if (!p)
+                       break;
+               __release_revoke(r, hlist_entry(p, struct revoke, node));
+       }
+       spin_unlock(&r->lock);
+}
+
+int make_revokable(struct file *f, struct revokable *r)
+{
+       struct revoke *revoke = kzalloc(sizeof(struct revoke), GFP_KERNEL);
+       if (!revoke)
+               return -ENOMEM;
+
+       if (!atomic_inc_unless_negative(&r->in_use)) {
+               kfree(revoke);
+               return -ENOENT;
+       }
+
+       revoke->file = f;
+       revoke->revokable = r;
+       f->f_revoke = revoke;
+
+       spin_lock(&r->lock);
+       hlist_add_head(&revoke->node, &r->list);
+       spin_unlock(&r->lock);
+
+       __stop_using(revoke);
+       return 0;
+}
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 99be011..4ec9437 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -45,6 +45,7 @@ struct vfsmount;
 struct cred;
 struct swap_info_struct;
 struct seq_file;
+struct revoke;
 
 extern void __init inode_init(void);
 extern void __init inode_init_early(void);
@@ -807,6 +808,7 @@ struct file {
 #ifdef CONFIG_DEBUG_WRITECOUNT
        unsigned long f_mnt_write_state;
 #endif
+       struct revoke *f_revoke;
 };
 
 struct file_handle {
diff --git a/include/linux/revoke.h b/include/linux/revoke.h
new file mode 100644
index 0000000..263569b
--- /dev/null
+++ b/include/linux/revoke.h
@@ -0,0 +1,50 @@
+#ifndef _LINUX_REVOKE_H
+#define _LINUX_REVOKE_H
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/atomic.h>
+#include <linux/completion.h>
+#include <linux/fs.h>
+
+struct revokable {
+       atomic_t in_use;        /* number of threads in methods,*/
+                               /* negative => going away */
+       spinlock_t lock;
+       struct hlist_head list; /* protected by ->lock, goes through */
+                               /* struct revoke->node */
+       struct completion *c;
+       void (*kick)(struct revokable *);
+};
+
+struct revoke {
+       struct file *file;
+       struct revokable *revokable;
+       struct hlist_node node;
+       bool closing;
+       struct completion *c;
+};
+
+bool __start_using(struct revoke *revoke);
+void __stop_using(struct revoke *revoke);
+
+static inline bool start_using(struct file *f)
+{
+       struct revoke *revoke = f->f_revoke;
+       if (likely(!revoke))
+               return true;    /* non-revokable file */
+       return __start_using(revoke);
+}
+
+static inline void stop_using(struct file *f)
+{
+       struct revoke *revoke = f->f_revoke;
+       if (unlikely(revoke))
+               __stop_using(revoke);
+}
+
+void release_revoke(struct revoke *revoke);
+void revoke_it(struct revokable *r);
+int make_revokable(struct file *f, struct revokable *r);
+
+#endif /* __LINUX_REVOKE_H */
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to