Skip to content

Commit c3638b5

Browse files
amir73iljankara
authored andcommitted
fsnotify: allow adding an inode mark without pinning inode
fsnotify_add_mark() and variants implicitly take a reference on inode when attaching a mark to an inode. Make that behavior opt-out with the mark flag FSNOTIFY_MARK_FLAG_NO_IREF. Instead of taking the inode reference when attaching connector to inode and dropping the inode reference when detaching connector from inode, take the inode reference on attach of the first mark that wants to hold an inode reference and drop the inode reference on detach of the last mark that wants to hold an inode reference. Backends can "upgrade" an existing mark to take an inode reference, but cannot "downgrade" a mark with inode reference to release the refernce. This leaves the choice to the backend whether or not to pin the inode when adding an inode mark. This is intended to be used when adding a mark with ignored mask that is used for optimization in cases where group can afford getting unneeded events and reinstate the mark with ignored mask when inode is accessed again after being evicted. Link: https://lore.kernel.org/r/20220422120327.3459282-12-amir73il@gmail.com Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Jan Kara <jack@suse.cz>
1 parent aabb45f commit c3638b5

2 files changed

Lines changed: 58 additions & 20 deletions

File tree

fs/notify/mark.c

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -116,20 +116,64 @@ __u32 fsnotify_conn_mask(struct fsnotify_mark_connector *conn)
116116
return *fsnotify_conn_mask_p(conn);
117117
}
118118

119-
static void __fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
119+
static void fsnotify_get_inode_ref(struct inode *inode)
120+
{
121+
ihold(inode);
122+
atomic_long_inc(&inode->i_sb->s_fsnotify_connectors);
123+
}
124+
125+
/*
126+
* Grab or drop inode reference for the connector if needed.
127+
*
128+
* When it's time to drop the reference, we only clear the HAS_IREF flag and
129+
* return the inode object. fsnotify_drop_object() will be resonsible for doing
130+
* iput() outside of spinlocks. This happens when last mark that wanted iref is
131+
* detached.
132+
*/
133+
static struct inode *fsnotify_update_iref(struct fsnotify_mark_connector *conn,
134+
bool want_iref)
135+
{
136+
bool has_iref = conn->flags & FSNOTIFY_CONN_FLAG_HAS_IREF;
137+
struct inode *inode = NULL;
138+
139+
if (conn->type != FSNOTIFY_OBJ_TYPE_INODE ||
140+
want_iref == has_iref)
141+
return NULL;
142+
143+
if (want_iref) {
144+
/* Pin inode if any mark wants inode refcount held */
145+
fsnotify_get_inode_ref(fsnotify_conn_inode(conn));
146+
conn->flags |= FSNOTIFY_CONN_FLAG_HAS_IREF;
147+
} else {
148+
/* Unpin inode after detach of last mark that wanted iref */
149+
inode = fsnotify_conn_inode(conn);
150+
conn->flags &= ~FSNOTIFY_CONN_FLAG_HAS_IREF;
151+
}
152+
153+
return inode;
154+
}
155+
156+
static void *__fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
120157
{
121158
u32 new_mask = 0;
159+
bool want_iref = false;
122160
struct fsnotify_mark *mark;
123161

124162
assert_spin_locked(&conn->lock);
125163
/* We can get detached connector here when inode is getting unlinked. */
126164
if (!fsnotify_valid_obj_type(conn->type))
127-
return;
165+
return NULL;
128166
hlist_for_each_entry(mark, &conn->list, obj_list) {
129-
if (mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED)
130-
new_mask |= fsnotify_calc_mask(mark);
167+
if (!(mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED))
168+
continue;
169+
new_mask |= fsnotify_calc_mask(mark);
170+
if (conn->type == FSNOTIFY_OBJ_TYPE_INODE &&
171+
!(mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF))
172+
want_iref = true;
131173
}
132174
*fsnotify_conn_mask_p(conn) = new_mask;
175+
176+
return fsnotify_update_iref(conn, want_iref);
133177
}
134178

135179
/*
@@ -169,12 +213,6 @@ static void fsnotify_connector_destroy_workfn(struct work_struct *work)
169213
}
170214
}
171215

172-
static void fsnotify_get_inode_ref(struct inode *inode)
173-
{
174-
ihold(inode);
175-
atomic_long_inc(&inode->i_sb->s_fsnotify_connectors);
176-
}
177-
178216
static void fsnotify_put_inode_ref(struct inode *inode)
179217
{
180218
struct super_block *sb = inode->i_sb;
@@ -213,6 +251,10 @@ static void *fsnotify_detach_connector_from_object(
213251
if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) {
214252
inode = fsnotify_conn_inode(conn);
215253
inode->i_fsnotify_mask = 0;
254+
255+
/* Unpin inode when detaching from connector */
256+
if (!(conn->flags & FSNOTIFY_CONN_FLAG_HAS_IREF))
257+
inode = NULL;
216258
} else if (conn->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) {
217259
fsnotify_conn_mount(conn)->mnt_fsnotify_mask = 0;
218260
} else if (conn->type == FSNOTIFY_OBJ_TYPE_SB) {
@@ -274,7 +316,8 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
274316
objp = fsnotify_detach_connector_from_object(conn, &type);
275317
free_conn = true;
276318
} else {
277-
__fsnotify_recalc_mask(conn);
319+
objp = __fsnotify_recalc_mask(conn);
320+
type = conn->type;
278321
}
279322
WRITE_ONCE(mark->connector, NULL);
280323
spin_unlock(&conn->lock);
@@ -497,14 +540,14 @@ static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
497540
unsigned int obj_type,
498541
__kernel_fsid_t *fsid)
499542
{
500-
struct inode *inode = NULL;
501543
struct fsnotify_mark_connector *conn;
502544

503545
conn = kmem_cache_alloc(fsnotify_mark_connector_cachep, GFP_KERNEL);
504546
if (!conn)
505547
return -ENOMEM;
506548
spin_lock_init(&conn->lock);
507549
INIT_HLIST_HEAD(&conn->list);
550+
conn->flags = 0;
508551
conn->type = obj_type;
509552
conn->obj = connp;
510553
/* Cache fsid of filesystem containing the object */
@@ -515,10 +558,6 @@ static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
515558
conn->fsid.val[0] = conn->fsid.val[1] = 0;
516559
conn->flags = 0;
517560
}
518-
if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) {
519-
inode = fsnotify_conn_inode(conn);
520-
fsnotify_get_inode_ref(inode);
521-
}
522561
fsnotify_get_sb_connectors(conn);
523562

524563
/*
@@ -527,8 +566,6 @@ static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
527566
*/
528567
if (cmpxchg(connp, NULL, conn)) {
529568
/* Someone else created list structure for us */
530-
if (inode)
531-
fsnotify_put_inode_ref(inode);
532569
fsnotify_put_sb_connectors(conn);
533570
kmem_cache_free(fsnotify_mark_connector_cachep, conn);
534571
}
@@ -690,8 +727,7 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
690727
if (ret)
691728
goto err;
692729

693-
if (mark->mask || mark->ignored_mask)
694-
fsnotify_recalc_mask(mark->connector);
730+
fsnotify_recalc_mask(mark->connector);
695731

696732
return ret;
697733
err:

include/linux/fsnotify_backend.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,7 @@ struct fsnotify_mark_connector {
456456
spinlock_t lock;
457457
unsigned short type; /* Type of object [lock] */
458458
#define FSNOTIFY_CONN_FLAG_HAS_FSID 0x01
459+
#define FSNOTIFY_CONN_FLAG_HAS_IREF 0x02
459460
unsigned short flags; /* flags [lock] */
460461
__kernel_fsid_t fsid; /* fsid of filesystem containing object */
461462
union {
@@ -510,6 +511,7 @@ struct fsnotify_mark {
510511
#define FSNOTIFY_MARK_FLAG_IN_ONESHOT 0x0020
511512
/* fanotify mark flags */
512513
#define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x0100
514+
#define FSNOTIFY_MARK_FLAG_NO_IREF 0x0200
513515
unsigned int flags; /* flags [mark->lock] */
514516
};
515517

0 commit comments

Comments
 (0)