Skip to content

Commit 94bd012

Browse files
committed
fsnotify: Track inode connectors for a superblock
Introduce a linked list tracking all inode connectors for a superblock. We will use this list when the superblock is getting shutdown to properly clean up all the inode marks instead of relying on scanning all inodes in the superblock which can get rather slow. Suggested-by: Amir Goldstein <amir73il@gmail.com> Reviewed-by: Amir Goldstein <amir73il@gmail.com> Reviewed-by: Christian Brauner <brauner@kernel.org> Signed-off-by: Jan Kara <jack@suse.cz>
1 parent 6c79021 commit 94bd012

File tree

4 files changed

+89
-15
lines changed

4 files changed

+89
-15
lines changed

fs/notify/fsnotify.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,10 @@ void fsnotify_sb_delete(struct super_block *sb)
112112

113113
void fsnotify_sb_free(struct super_block *sb)
114114
{
115-
kfree(sb->s_fsnotify_info);
115+
if (sb->s_fsnotify_info) {
116+
WARN_ON_ONCE(!list_empty(&sb->s_fsnotify_info->inode_conn_list));
117+
kfree(sb->s_fsnotify_info);
118+
}
116119
}
117120

118121
/*
@@ -777,8 +780,7 @@ static __init int fsnotify_init(void)
777780
if (ret)
778781
panic("initializing fsnotify_mark_srcu");
779782

780-
fsnotify_mark_connector_cachep = KMEM_CACHE(fsnotify_mark_connector,
781-
SLAB_PANIC);
783+
fsnotify_init_connector_caches();
782784

783785
return 0;
784786
}

fs/notify/fsnotify.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,6 @@ static inline void fsnotify_clear_marks_by_mntns(struct mnt_namespace *mntns)
106106
*/
107107
extern void fsnotify_set_children_dentry_flags(struct inode *inode);
108108

109-
extern struct kmem_cache *fsnotify_mark_connector_cachep;
109+
void fsnotify_init_connector_caches(void);
110110

111111
#endif /* __FS_NOTIFY_FSNOTIFY_H_ */

fs/notify/mark.c

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@
7979
#define FSNOTIFY_REAPER_DELAY (1) /* 1 jiffy */
8080

8181
struct srcu_struct fsnotify_mark_srcu;
82-
struct kmem_cache *fsnotify_mark_connector_cachep;
82+
static struct kmem_cache *fsnotify_mark_connector_cachep;
83+
static struct kmem_cache *fsnotify_inode_mark_connector_cachep;
8384

8485
static DEFINE_SPINLOCK(destroy_lock);
8586
static LIST_HEAD(destroy_list);
@@ -323,10 +324,12 @@ static void fsnotify_connector_destroy_workfn(struct work_struct *work)
323324
while (conn) {
324325
free = conn;
325326
conn = conn->destroy_next;
326-
kmem_cache_free(fsnotify_mark_connector_cachep, free);
327+
kfree(free);
327328
}
328329
}
329330

331+
static void fsnotify_untrack_connector(struct fsnotify_mark_connector *conn);
332+
330333
static void *fsnotify_detach_connector_from_object(
331334
struct fsnotify_mark_connector *conn,
332335
unsigned int *type)
@@ -342,6 +345,7 @@ static void *fsnotify_detach_connector_from_object(
342345
if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) {
343346
inode = fsnotify_conn_inode(conn);
344347
inode->i_fsnotify_mask = 0;
348+
fsnotify_untrack_connector(conn);
345349

346350
/* Unpin inode when detaching from connector */
347351
if (!(conn->flags & FSNOTIFY_CONN_FLAG_HAS_IREF))
@@ -644,6 +648,8 @@ static int fsnotify_attach_info_to_sb(struct super_block *sb)
644648
if (!sbinfo)
645649
return -ENOMEM;
646650

651+
INIT_LIST_HEAD(&sbinfo->inode_conn_list);
652+
spin_lock_init(&sbinfo->list_lock);
647653
/*
648654
* cmpxchg() provides the barrier so that callers of fsnotify_sb_info()
649655
* will observe an initialized structure
@@ -655,28 +661,82 @@ static int fsnotify_attach_info_to_sb(struct super_block *sb)
655661
return 0;
656662
}
657663

658-
static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
659-
void *obj, unsigned int obj_type)
660-
{
661-
struct fsnotify_mark_connector *conn;
664+
struct fsnotify_inode_mark_connector {
665+
struct fsnotify_mark_connector common;
666+
struct list_head conns_list;
667+
};
662668

663-
conn = kmem_cache_alloc(fsnotify_mark_connector_cachep, GFP_KERNEL);
664-
if (!conn)
665-
return -ENOMEM;
669+
static void fsnotify_init_connector(struct fsnotify_mark_connector *conn,
670+
void *obj, unsigned int obj_type)
671+
{
666672
spin_lock_init(&conn->lock);
667673
INIT_HLIST_HEAD(&conn->list);
668674
conn->flags = 0;
669675
conn->prio = 0;
670676
conn->type = obj_type;
671677
conn->obj = obj;
678+
}
679+
680+
static struct fsnotify_mark_connector *
681+
fsnotify_alloc_inode_connector(struct inode *inode)
682+
{
683+
struct fsnotify_inode_mark_connector *iconn;
684+
struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(inode->i_sb);
685+
686+
iconn = kmem_cache_alloc(fsnotify_inode_mark_connector_cachep,
687+
GFP_KERNEL);
688+
if (!iconn)
689+
return NULL;
690+
691+
fsnotify_init_connector(&iconn->common, inode, FSNOTIFY_OBJ_TYPE_INODE);
692+
spin_lock(&sbinfo->list_lock);
693+
list_add(&iconn->conns_list, &sbinfo->inode_conn_list);
694+
spin_unlock(&sbinfo->list_lock);
695+
696+
return &iconn->common;
697+
}
698+
699+
static void fsnotify_untrack_connector(struct fsnotify_mark_connector *conn)
700+
{
701+
struct fsnotify_inode_mark_connector *iconn;
702+
struct fsnotify_sb_info *sbinfo;
703+
704+
if (conn->type != FSNOTIFY_OBJ_TYPE_INODE)
705+
return;
706+
707+
iconn = container_of(conn, struct fsnotify_inode_mark_connector, common);
708+
sbinfo = fsnotify_sb_info(fsnotify_conn_inode(conn)->i_sb);
709+
spin_lock(&sbinfo->list_lock);
710+
list_del(&iconn->conns_list);
711+
spin_unlock(&sbinfo->list_lock);
712+
}
713+
714+
static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
715+
void *obj, unsigned int obj_type)
716+
{
717+
struct fsnotify_mark_connector *conn;
718+
719+
if (obj_type == FSNOTIFY_OBJ_TYPE_INODE) {
720+
struct inode *inode = obj;
721+
722+
conn = fsnotify_alloc_inode_connector(inode);
723+
} else {
724+
conn = kmem_cache_alloc(fsnotify_mark_connector_cachep,
725+
GFP_KERNEL);
726+
if (conn)
727+
fsnotify_init_connector(conn, obj, obj_type);
728+
}
729+
if (!conn)
730+
return -ENOMEM;
672731

673732
/*
674733
* cmpxchg() provides the barrier so that readers of *connp can see
675734
* only initialized structure
676735
*/
677736
if (cmpxchg(connp, NULL, conn)) {
678737
/* Someone else created list structure for us */
679-
kmem_cache_free(fsnotify_mark_connector_cachep, conn);
738+
fsnotify_untrack_connector(conn);
739+
kfree(conn);
680740
}
681741
return 0;
682742
}
@@ -1007,3 +1067,12 @@ void fsnotify_wait_marks_destroyed(void)
10071067
flush_delayed_work(&reaper_work);
10081068
}
10091069
EXPORT_SYMBOL_GPL(fsnotify_wait_marks_destroyed);
1070+
1071+
__init void fsnotify_init_connector_caches(void)
1072+
{
1073+
fsnotify_mark_connector_cachep = KMEM_CACHE(fsnotify_mark_connector,
1074+
SLAB_PANIC);
1075+
fsnotify_inode_mark_connector_cachep = KMEM_CACHE(
1076+
fsnotify_inode_mark_connector,
1077+
SLAB_PANIC);
1078+
}

include/linux/fsnotify_backend.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ struct fsnotify_mark_connector {
553553
/* Used listing heads to free after srcu period expires */
554554
struct fsnotify_mark_connector *destroy_next;
555555
};
556-
struct hlist_head list;
556+
struct hlist_head list; /* List of marks */
557557
};
558558

559559
/*
@@ -562,6 +562,9 @@ struct fsnotify_mark_connector {
562562
*/
563563
struct fsnotify_sb_info {
564564
struct fsnotify_mark_connector __rcu *sb_marks;
565+
/* List of connectors for inode marks */
566+
struct list_head inode_conn_list;
567+
spinlock_t list_lock; /* Lock protecting inode_conn_list */
565568
/*
566569
* Number of inode/mount/sb objects that are being watched in this sb.
567570
* Note that inodes objects are currently double-accounted.

0 commit comments

Comments
 (0)