Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Amir Goldstein and committed by
Jan Kara
c3638b5b aabb45fd

+58 -20
+56 -20
fs/notify/mark.c
··· 116 116 return *fsnotify_conn_mask_p(conn); 117 117 } 118 118 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) 120 157 { 121 158 u32 new_mask = 0; 159 + bool want_iref = false; 122 160 struct fsnotify_mark *mark; 123 161 124 162 assert_spin_locked(&conn->lock); 125 163 /* We can get detached connector here when inode is getting unlinked. */ 126 164 if (!fsnotify_valid_obj_type(conn->type)) 127 - return; 165 + return NULL; 128 166 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; 131 173 } 132 174 *fsnotify_conn_mask_p(conn) = new_mask; 175 + 176 + return fsnotify_update_iref(conn, want_iref); 133 177 } 134 178 135 179 /* ··· 211 167 conn = conn->destroy_next; 212 168 kmem_cache_free(fsnotify_mark_connector_cachep, free); 213 169 } 214 - } 215 - 216 - static void fsnotify_get_inode_ref(struct inode *inode) 217 - { 218 - ihold(inode); 219 - atomic_long_inc(&inode->i_sb->s_fsnotify_connectors); 220 170 } 221 171 222 172 static void fsnotify_put_inode_ref(struct inode *inode) ··· 251 213 if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) { 252 214 inode = fsnotify_conn_inode(conn); 253 215 inode->i_fsnotify_mask = 0; 216 + 217 + /* Unpin inode when detaching from connector */ 218 + if (!(conn->flags & FSNOTIFY_CONN_FLAG_HAS_IREF)) 219 + inode = NULL; 254 220 } else if (conn->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) { 255 221 fsnotify_conn_mount(conn)->mnt_fsnotify_mask = 0; 256 222 } else if (conn->type == FSNOTIFY_OBJ_TYPE_SB) { ··· 316 274 objp = fsnotify_detach_connector_from_object(conn, &type); 317 275 free_conn = true; 318 276 } else { 319 - __fsnotify_recalc_mask(conn); 277 + objp = __fsnotify_recalc_mask(conn); 278 + type = conn->type; 320 279 } 321 280 WRITE_ONCE(mark->connector, NULL); 322 281 spin_unlock(&conn->lock); ··· 540 497 unsigned int obj_type, 541 498 __kernel_fsid_t *fsid) 542 499 { 543 - struct inode *inode = NULL; 544 500 struct fsnotify_mark_connector *conn; 545 501 546 502 conn = kmem_cache_alloc(fsnotify_mark_connector_cachep, GFP_KERNEL); ··· 547 505 return -ENOMEM; 548 506 spin_lock_init(&conn->lock); 549 507 INIT_HLIST_HEAD(&conn->list); 508 + conn->flags = 0; 550 509 conn->type = obj_type; 551 510 conn->obj = connp; 552 511 /* Cache fsid of filesystem containing the object */ ··· 558 515 conn->fsid.val[0] = conn->fsid.val[1] = 0; 559 516 conn->flags = 0; 560 517 } 561 - if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) { 562 - inode = fsnotify_conn_inode(conn); 563 - fsnotify_get_inode_ref(inode); 564 - } 565 518 fsnotify_get_sb_connectors(conn); 566 519 567 520 /* ··· 566 527 */ 567 528 if (cmpxchg(connp, NULL, conn)) { 568 529 /* Someone else created list structure for us */ 569 - if (inode) 570 - fsnotify_put_inode_ref(inode); 571 530 fsnotify_put_sb_connectors(conn); 572 531 kmem_cache_free(fsnotify_mark_connector_cachep, conn); 573 532 } ··· 727 690 if (ret) 728 691 goto err; 729 692 730 - if (mark->mask || mark->ignored_mask) 731 - fsnotify_recalc_mask(mark->connector); 693 + fsnotify_recalc_mask(mark->connector); 732 694 733 695 return ret; 734 696 err:
+2
include/linux/fsnotify_backend.h
··· 456 456 spinlock_t lock; 457 457 unsigned short type; /* Type of object [lock] */ 458 458 #define FSNOTIFY_CONN_FLAG_HAS_FSID 0x01 459 + #define FSNOTIFY_CONN_FLAG_HAS_IREF 0x02 459 460 unsigned short flags; /* flags [lock] */ 460 461 __kernel_fsid_t fsid; /* fsid of filesystem containing object */ 461 462 union { ··· 511 510 #define FSNOTIFY_MARK_FLAG_IN_ONESHOT 0x0020 512 511 /* fanotify mark flags */ 513 512 #define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x0100 513 + #define FSNOTIFY_MARK_FLAG_NO_IREF 0x0200 514 514 unsigned int flags; /* flags [mark->lock] */ 515 515 }; 516 516