aboutsummaryrefslogtreecommitdiffstats
path: root/fs/kernfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/kernfs')
-rw-r--r--fs/kernfs/file.c69
-rw-r--r--fs/kernfs/mount.c30
2 files changed, 85 insertions, 14 deletions
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c
index e3d37f607f97..d895b4b7b661 100644
--- a/fs/kernfs/file.c
+++ b/fs/kernfs/file.c
@@ -39,6 +39,19 @@ struct kernfs_open_node {
39 struct list_head files; /* goes through kernfs_open_file.list */ 39 struct list_head files; /* goes through kernfs_open_file.list */
40}; 40};
41 41
42/*
43 * kernfs_notify() may be called from any context and bounces notifications
44 * through a work item. To minimize space overhead in kernfs_node, the
45 * pending queue is implemented as a singly linked list of kernfs_nodes.
46 * The list is terminated with the self pointer so that whether a
47 * kernfs_node is on the list or not can be determined by testing the next
48 * pointer for NULL.
49 */
50#define KERNFS_NOTIFY_EOL ((void *)&kernfs_notify_list)
51
52static DEFINE_SPINLOCK(kernfs_notify_lock);
53static struct kernfs_node *kernfs_notify_list = KERNFS_NOTIFY_EOL;
54
42static struct kernfs_open_file *kernfs_of(struct file *file) 55static struct kernfs_open_file *kernfs_of(struct file *file)
43{ 56{
44 return ((struct seq_file *)file->private_data)->private; 57 return ((struct seq_file *)file->private_data)->private;
@@ -783,24 +796,25 @@ static unsigned int kernfs_fop_poll(struct file *filp, poll_table *wait)
783 return DEFAULT_POLLMASK|POLLERR|POLLPRI; 796 return DEFAULT_POLLMASK|POLLERR|POLLPRI;
784} 797}
785 798
786/** 799static void kernfs_notify_workfn(struct work_struct *work)
787 * kernfs_notify - notify a kernfs file
788 * @kn: file to notify
789 *
790 * Notify @kn such that poll(2) on @kn wakes up.
791 */
792void kernfs_notify(struct kernfs_node *kn)
793{ 800{
794 struct kernfs_root *root = kernfs_root(kn); 801 struct kernfs_node *kn;
795 struct kernfs_open_node *on; 802 struct kernfs_open_node *on;
796 struct kernfs_super_info *info; 803 struct kernfs_super_info *info;
797 unsigned long flags; 804repeat:
798 805 /* pop one off the notify_list */
799 if (WARN_ON(kernfs_type(kn) != KERNFS_FILE)) 806 spin_lock_irq(&kernfs_notify_lock);
807 kn = kernfs_notify_list;
808 if (kn == KERNFS_NOTIFY_EOL) {
809 spin_unlock_irq(&kernfs_notify_lock);
800 return; 810 return;
811 }
812 kernfs_notify_list = kn->attr.notify_next;
813 kn->attr.notify_next = NULL;
814 spin_unlock_irq(&kernfs_notify_lock);
801 815
802 /* kick poll */ 816 /* kick poll */
803 spin_lock_irqsave(&kernfs_open_node_lock, flags); 817 spin_lock_irq(&kernfs_open_node_lock);
804 818
805 on = kn->attr.open; 819 on = kn->attr.open;
806 if (on) { 820 if (on) {
@@ -808,12 +822,12 @@ void kernfs_notify(struct kernfs_node *kn)
808 wake_up_interruptible(&on->poll); 822 wake_up_interruptible(&on->poll);
809 } 823 }
810 824
811 spin_unlock_irqrestore(&kernfs_open_node_lock, flags); 825 spin_unlock_irq(&kernfs_open_node_lock);
812 826
813 /* kick fsnotify */ 827 /* kick fsnotify */
814 mutex_lock(&kernfs_mutex); 828 mutex_lock(&kernfs_mutex);
815 829
816 list_for_each_entry(info, &root->supers, node) { 830 list_for_each_entry(info, &kernfs_root(kn)->supers, node) {
817 struct inode *inode; 831 struct inode *inode;
818 struct dentry *dentry; 832 struct dentry *dentry;
819 833
@@ -833,6 +847,33 @@ void kernfs_notify(struct kernfs_node *kn)
833 } 847 }
834 848
835 mutex_unlock(&kernfs_mutex); 849 mutex_unlock(&kernfs_mutex);
850 kernfs_put(kn);
851 goto repeat;
852}
853
854/**
855 * kernfs_notify - notify a kernfs file
856 * @kn: file to notify
857 *
858 * Notify @kn such that poll(2) on @kn wakes up. Maybe be called from any
859 * context.
860 */
861void kernfs_notify(struct kernfs_node *kn)
862{
863 static DECLARE_WORK(kernfs_notify_work, kernfs_notify_workfn);
864 unsigned long flags;
865
866 if (WARN_ON(kernfs_type(kn) != KERNFS_FILE))
867 return;
868
869 spin_lock_irqsave(&kernfs_notify_lock, flags);
870 if (!kn->attr.notify_next) {
871 kernfs_get(kn);
872 kn->attr.notify_next = kernfs_notify_list;
873 kernfs_notify_list = kn;
874 schedule_work(&kernfs_notify_work);
875 }
876 spin_unlock_irqrestore(&kernfs_notify_lock, flags);
836} 877}
837EXPORT_SYMBOL_GPL(kernfs_notify); 878EXPORT_SYMBOL_GPL(kernfs_notify);
838 879
diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c
index d171b98a6cdd..f973ae9b05f1 100644
--- a/fs/kernfs/mount.c
+++ b/fs/kernfs/mount.c
@@ -211,6 +211,36 @@ void kernfs_kill_sb(struct super_block *sb)
211 kernfs_put(root_kn); 211 kernfs_put(root_kn);
212} 212}
213 213
214/**
215 * kernfs_pin_sb: try to pin the superblock associated with a kernfs_root
216 * @kernfs_root: the kernfs_root in question
217 * @ns: the namespace tag
218 *
219 * Pin the superblock so the superblock won't be destroyed in subsequent
220 * operations. This can be used to block ->kill_sb() which may be useful
221 * for kernfs users which dynamically manage superblocks.
222 *
223 * Returns NULL if there's no superblock associated to this kernfs_root, or
224 * -EINVAL if the superblock is being freed.
225 */
226struct super_block *kernfs_pin_sb(struct kernfs_root *root, const void *ns)
227{
228 struct kernfs_super_info *info;
229 struct super_block *sb = NULL;
230
231 mutex_lock(&kernfs_mutex);
232 list_for_each_entry(info, &root->supers, node) {
233 if (info->ns == ns) {
234 sb = info->sb;
235 if (!atomic_inc_not_zero(&info->sb->s_active))
236 sb = ERR_PTR(-EINVAL);
237 break;
238 }
239 }
240 mutex_unlock(&kernfs_mutex);
241 return sb;
242}
243
214void __init kernfs_init(void) 244void __init kernfs_init(void)
215{ 245{
216 kernfs_node_cache = kmem_cache_create("kernfs_node_cache", 246 kernfs_node_cache = kmem_cache_create("kernfs_node_cache",