Skip to content

Commit a8ed228

Browse files
committed
Merge tag 'fsnotify_for_v6.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs
Pull fsnotify updates from Jan Kara: "A set of fixes to shutdown fsnotify subsystem before invalidating dcache thus addressing some nasty possible races" * tag 'fsnotify_for_v6.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs: fsnotify: Shutdown fsnotify before destroying sb's dcache fsnotify: Use connector list for destroying inode marks fsnotify: Track inode connectors for a superblock
2 parents 997f964 + 74bd284 commit a8ed228

5 files changed

Lines changed: 144 additions & 76 deletions

File tree

fs/notify/fsnotify.c

Lines changed: 6 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -33,65 +33,6 @@ void __fsnotify_mntns_delete(struct mnt_namespace *mntns)
3333
fsnotify_clear_marks_by_mntns(mntns);
3434
}
3535

36-
/**
37-
* fsnotify_unmount_inodes - an sb is unmounting. handle any watched inodes.
38-
* @sb: superblock being unmounted.
39-
*
40-
* Called during unmount with no locks held, so needs to be safe against
41-
* concurrent modifiers. We temporarily drop sb->s_inode_list_lock and CAN block.
42-
*/
43-
static void fsnotify_unmount_inodes(struct super_block *sb)
44-
{
45-
struct inode *inode, *iput_inode = NULL;
46-
47-
spin_lock(&sb->s_inode_list_lock);
48-
list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
49-
/*
50-
* We cannot __iget() an inode in state I_FREEING,
51-
* I_WILL_FREE, or I_NEW which is fine because by that point
52-
* the inode cannot have any associated watches.
53-
*/
54-
spin_lock(&inode->i_lock);
55-
if (inode_state_read(inode) & (I_FREEING | I_WILL_FREE | I_NEW)) {
56-
spin_unlock(&inode->i_lock);
57-
continue;
58-
}
59-
60-
/*
61-
* If i_count is zero, the inode cannot have any watches and
62-
* doing an __iget/iput with SB_ACTIVE clear would actually
63-
* evict all inodes with zero i_count from icache which is
64-
* unnecessarily violent and may in fact be illegal to do.
65-
* However, we should have been called /after/ evict_inodes
66-
* removed all zero refcount inodes, in any case. Test to
67-
* be sure.
68-
*/
69-
if (!icount_read(inode)) {
70-
spin_unlock(&inode->i_lock);
71-
continue;
72-
}
73-
74-
__iget(inode);
75-
spin_unlock(&inode->i_lock);
76-
spin_unlock(&sb->s_inode_list_lock);
77-
78-
iput(iput_inode);
79-
80-
/* for each watch, send FS_UNMOUNT and then remove it */
81-
fsnotify_inode(inode, FS_UNMOUNT);
82-
83-
fsnotify_inode_delete(inode);
84-
85-
iput_inode = inode;
86-
87-
cond_resched();
88-
spin_lock(&sb->s_inode_list_lock);
89-
}
90-
spin_unlock(&sb->s_inode_list_lock);
91-
92-
iput(iput_inode);
93-
}
94-
9536
void fsnotify_sb_delete(struct super_block *sb)
9637
{
9738
struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(sb);
@@ -100,7 +41,7 @@ void fsnotify_sb_delete(struct super_block *sb)
10041
if (!sbinfo)
10142
return;
10243

103-
fsnotify_unmount_inodes(sb);
44+
fsnotify_unmount_inodes(sbinfo);
10445
fsnotify_clear_marks_by_sb(sb);
10546
/* Wait for outstanding object references from connectors */
10647
wait_var_event(fsnotify_sb_watched_objects(sb),
@@ -112,7 +53,10 @@ void fsnotify_sb_delete(struct super_block *sb)
11253

11354
void fsnotify_sb_free(struct super_block *sb)
11455
{
115-
kfree(sb->s_fsnotify_info);
56+
if (sb->s_fsnotify_info) {
57+
WARN_ON_ONCE(!list_empty(&sb->s_fsnotify_info->inode_conn_list));
58+
kfree(sb->s_fsnotify_info);
59+
}
11660
}
11761

11862
/*
@@ -777,8 +721,7 @@ static __init int fsnotify_init(void)
777721
if (ret)
778722
panic("initializing fsnotify_mark_srcu");
779723

780-
fsnotify_mark_connector_cachep = KMEM_CACHE(fsnotify_mark_connector,
781-
SLAB_PANIC);
724+
fsnotify_init_connector_caches();
782725

783726
return 0;
784727
}

fs/notify/fsnotify.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ extern struct srcu_struct fsnotify_mark_srcu;
7777
extern int fsnotify_compare_groups(struct fsnotify_group *a,
7878
struct fsnotify_group *b);
7979

80+
/* Destroy all inode marks for given superblock */
81+
void fsnotify_unmount_inodes(struct fsnotify_sb_info *sbinfo);
82+
8083
/* Destroy all marks attached to an object via connector */
8184
extern void fsnotify_destroy_marks(fsnotify_connp_t *connp);
8285
/* run the list of all marks associated with inode and destroy them */
@@ -106,6 +109,6 @@ static inline void fsnotify_clear_marks_by_mntns(struct mnt_namespace *mntns)
106109
*/
107110
extern void fsnotify_set_children_dentry_flags(struct inode *inode);
108111

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

111114
#endif /* __FS_NOTIFY_FSNOTIFY_H_ */

fs/notify/mark.c

Lines changed: 128 additions & 9 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,132 @@ 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)
664+
struct fsnotify_inode_mark_connector {
665+
struct fsnotify_mark_connector common;
666+
struct list_head conns_list;
667+
};
668+
669+
static struct inode *fsnotify_get_living_inode(struct fsnotify_sb_info *sbinfo)
660670
{
661-
struct fsnotify_mark_connector *conn;
671+
struct fsnotify_inode_mark_connector *iconn;
672+
struct inode *inode;
673+
674+
spin_lock(&sbinfo->list_lock);
675+
/* Find the first non-evicting inode */
676+
list_for_each_entry(iconn, &sbinfo->inode_conn_list, conns_list) {
677+
/* All connectors on the list are still attached to an inode */
678+
inode = iconn->common.obj;
679+
/*
680+
* For connectors without FSNOTIFY_CONN_FLAG_HAS_IREF
681+
* (evictable marks) corresponding inode may well have 0
682+
* refcount and can be undergoing eviction. OTOH list_lock
683+
* protects us from the connector getting detached and inode
684+
* freed. So we can poke around the inode safely.
685+
*/
686+
spin_lock(&inode->i_lock);
687+
if (likely(
688+
!(inode_state_read(inode) & (I_FREEING | I_WILL_FREE)))) {
689+
__iget(inode);
690+
spin_unlock(&inode->i_lock);
691+
spin_unlock(&sbinfo->list_lock);
692+
return inode;
693+
}
694+
spin_unlock(&inode->i_lock);
695+
}
696+
spin_unlock(&sbinfo->list_lock);
662697

663-
conn = kmem_cache_alloc(fsnotify_mark_connector_cachep, GFP_KERNEL);
664-
if (!conn)
665-
return -ENOMEM;
698+
return NULL;
699+
}
700+
701+
/**
702+
* fsnotify_unmount_inodes - an sb is unmounting. Handle any watched inodes.
703+
* @sbinfo: fsnotify info for superblock being unmounted.
704+
*
705+
* Walk all inode connectors for the superblock and free all associated marks.
706+
*/
707+
void fsnotify_unmount_inodes(struct fsnotify_sb_info *sbinfo)
708+
{
709+
struct inode *inode;
710+
711+
while ((inode = fsnotify_get_living_inode(sbinfo))) {
712+
fsnotify_inode(inode, FS_UNMOUNT);
713+
fsnotify_clear_marks_by_inode(inode);
714+
iput(inode);
715+
cond_resched();
716+
}
717+
}
718+
719+
static void fsnotify_init_connector(struct fsnotify_mark_connector *conn,
720+
void *obj, unsigned int obj_type)
721+
{
666722
spin_lock_init(&conn->lock);
667723
INIT_HLIST_HEAD(&conn->list);
668724
conn->flags = 0;
669725
conn->prio = 0;
670726
conn->type = obj_type;
671727
conn->obj = obj;
728+
}
729+
730+
static struct fsnotify_mark_connector *
731+
fsnotify_alloc_inode_connector(struct inode *inode)
732+
{
733+
struct fsnotify_inode_mark_connector *iconn;
734+
struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(inode->i_sb);
735+
736+
iconn = kmem_cache_alloc(fsnotify_inode_mark_connector_cachep,
737+
GFP_KERNEL);
738+
if (!iconn)
739+
return NULL;
740+
741+
fsnotify_init_connector(&iconn->common, inode, FSNOTIFY_OBJ_TYPE_INODE);
742+
spin_lock(&sbinfo->list_lock);
743+
list_add(&iconn->conns_list, &sbinfo->inode_conn_list);
744+
spin_unlock(&sbinfo->list_lock);
745+
746+
return &iconn->common;
747+
}
748+
749+
static void fsnotify_untrack_connector(struct fsnotify_mark_connector *conn)
750+
{
751+
struct fsnotify_inode_mark_connector *iconn;
752+
struct fsnotify_sb_info *sbinfo;
753+
754+
if (conn->type != FSNOTIFY_OBJ_TYPE_INODE)
755+
return;
756+
757+
iconn = container_of(conn, struct fsnotify_inode_mark_connector, common);
758+
sbinfo = fsnotify_sb_info(fsnotify_conn_inode(conn)->i_sb);
759+
spin_lock(&sbinfo->list_lock);
760+
list_del(&iconn->conns_list);
761+
spin_unlock(&sbinfo->list_lock);
762+
}
763+
764+
static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
765+
void *obj, unsigned int obj_type)
766+
{
767+
struct fsnotify_mark_connector *conn;
768+
769+
if (obj_type == FSNOTIFY_OBJ_TYPE_INODE) {
770+
struct inode *inode = obj;
771+
772+
conn = fsnotify_alloc_inode_connector(inode);
773+
} else {
774+
conn = kmem_cache_alloc(fsnotify_mark_connector_cachep,
775+
GFP_KERNEL);
776+
if (conn)
777+
fsnotify_init_connector(conn, obj, obj_type);
778+
}
779+
if (!conn)
780+
return -ENOMEM;
672781

673782
/*
674783
* cmpxchg() provides the barrier so that readers of *connp can see
675784
* only initialized structure
676785
*/
677786
if (cmpxchg(connp, NULL, conn)) {
678787
/* Someone else created list structure for us */
679-
kmem_cache_free(fsnotify_mark_connector_cachep, conn);
788+
fsnotify_untrack_connector(conn);
789+
kfree(conn);
680790
}
681791
return 0;
682792
}
@@ -1007,3 +1117,12 @@ void fsnotify_wait_marks_destroyed(void)
10071117
flush_delayed_work(&reaper_work);
10081118
}
10091119
EXPORT_SYMBOL_GPL(fsnotify_wait_marks_destroyed);
1120+
1121+
__init void fsnotify_init_connector_caches(void)
1122+
{
1123+
fsnotify_mark_connector_cachep = KMEM_CACHE(fsnotify_mark_connector,
1124+
SLAB_PANIC);
1125+
fsnotify_inode_mark_connector_cachep = KMEM_CACHE(
1126+
fsnotify_inode_mark_connector,
1127+
SLAB_PANIC);
1128+
}

fs/super.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,7 @@ void generic_shutdown_super(struct super_block *sb)
620620
const struct super_operations *sop = sb->s_op;
621621

622622
if (sb->s_root) {
623+
fsnotify_sb_delete(sb);
623624
shrink_dcache_for_umount(sb);
624625
sync_filesystem(sb);
625626
sb->s_flags &= ~SB_ACTIVE;
@@ -632,9 +633,8 @@ void generic_shutdown_super(struct super_block *sb)
632633

633634
/*
634635
* Clean up and evict any inodes that still have references due
635-
* to fsnotify or the security policy.
636+
* to the security policy.
636637
*/
637-
fsnotify_sb_delete(sb);
638638
security_sb_delete(sb);
639639

640640
if (sb->s_dio_done_wq) {

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)