162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/* 762306a36Sopenharmony_ci * fsnotify inode mark locking/lifetime/and refcnting 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * REFCNT: 1062306a36Sopenharmony_ci * The group->recnt and mark->refcnt tell how many "things" in the kernel 1162306a36Sopenharmony_ci * currently are referencing the objects. Both kind of objects typically will 1262306a36Sopenharmony_ci * live inside the kernel with a refcnt of 2, one for its creation and one for 1362306a36Sopenharmony_ci * the reference a group and a mark hold to each other. 1462306a36Sopenharmony_ci * If you are holding the appropriate locks, you can take a reference and the 1562306a36Sopenharmony_ci * object itself is guaranteed to survive until the reference is dropped. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * LOCKING: 1862306a36Sopenharmony_ci * There are 3 locks involved with fsnotify inode marks and they MUST be taken 1962306a36Sopenharmony_ci * in order as follows: 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * group->mark_mutex 2262306a36Sopenharmony_ci * mark->lock 2362306a36Sopenharmony_ci * mark->connector->lock 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * group->mark_mutex protects the marks_list anchored inside a given group and 2662306a36Sopenharmony_ci * each mark is hooked via the g_list. It also protects the groups private 2762306a36Sopenharmony_ci * data (i.e group limits). 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci * mark->lock protects the marks attributes like its masks and flags. 3062306a36Sopenharmony_ci * Furthermore it protects the access to a reference of the group that the mark 3162306a36Sopenharmony_ci * is assigned to as well as the access to a reference of the inode/vfsmount 3262306a36Sopenharmony_ci * that is being watched by the mark. 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * mark->connector->lock protects the list of marks anchored inside an 3562306a36Sopenharmony_ci * inode / vfsmount and each mark is hooked via the i_list. 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * A list of notification marks relating to inode / mnt is contained in 3862306a36Sopenharmony_ci * fsnotify_mark_connector. That structure is alive as long as there are any 3962306a36Sopenharmony_ci * marks in the list and is also protected by fsnotify_mark_srcu. A mark gets 4062306a36Sopenharmony_ci * detached from fsnotify_mark_connector when last reference to the mark is 4162306a36Sopenharmony_ci * dropped. Thus having mark reference is enough to protect mark->connector 4262306a36Sopenharmony_ci * pointer and to make sure fsnotify_mark_connector cannot disappear. Also 4362306a36Sopenharmony_ci * because we remove mark from g_list before dropping mark reference associated 4462306a36Sopenharmony_ci * with that, any mark found through g_list is guaranteed to have 4562306a36Sopenharmony_ci * mark->connector set until we drop group->mark_mutex. 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * LIFETIME: 4862306a36Sopenharmony_ci * Inode marks survive between when they are added to an inode and when their 4962306a36Sopenharmony_ci * refcnt==0. Marks are also protected by fsnotify_mark_srcu. 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * The inode mark can be cleared for a number of different reasons including: 5262306a36Sopenharmony_ci * - The inode is unlinked for the last time. (fsnotify_inode_remove) 5362306a36Sopenharmony_ci * - The inode is being evicted from cache. (fsnotify_inode_delete) 5462306a36Sopenharmony_ci * - The fs the inode is on is unmounted. (fsnotify_inode_delete/fsnotify_unmount_inodes) 5562306a36Sopenharmony_ci * - Something explicitly requests that it be removed. (fsnotify_destroy_mark) 5662306a36Sopenharmony_ci * - The fsnotify_group associated with the mark is going away and all such marks 5762306a36Sopenharmony_ci * need to be cleaned up. (fsnotify_clear_marks_by_group) 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * This has the very interesting property of being able to run concurrently with 6062306a36Sopenharmony_ci * any (or all) other directions. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#include <linux/fs.h> 6462306a36Sopenharmony_ci#include <linux/init.h> 6562306a36Sopenharmony_ci#include <linux/kernel.h> 6662306a36Sopenharmony_ci#include <linux/kthread.h> 6762306a36Sopenharmony_ci#include <linux/module.h> 6862306a36Sopenharmony_ci#include <linux/mutex.h> 6962306a36Sopenharmony_ci#include <linux/slab.h> 7062306a36Sopenharmony_ci#include <linux/spinlock.h> 7162306a36Sopenharmony_ci#include <linux/srcu.h> 7262306a36Sopenharmony_ci#include <linux/ratelimit.h> 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#include <linux/atomic.h> 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#include <linux/fsnotify_backend.h> 7762306a36Sopenharmony_ci#include "fsnotify.h" 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define FSNOTIFY_REAPER_DELAY (1) /* 1 jiffy */ 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistruct srcu_struct fsnotify_mark_srcu; 8262306a36Sopenharmony_cistruct kmem_cache *fsnotify_mark_connector_cachep; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic DEFINE_SPINLOCK(destroy_lock); 8562306a36Sopenharmony_cistatic LIST_HEAD(destroy_list); 8662306a36Sopenharmony_cistatic struct fsnotify_mark_connector *connector_destroy_list; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void fsnotify_mark_destroy_workfn(struct work_struct *work); 8962306a36Sopenharmony_cistatic DECLARE_DELAYED_WORK(reaper_work, fsnotify_mark_destroy_workfn); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic void fsnotify_connector_destroy_workfn(struct work_struct *work); 9262306a36Sopenharmony_cistatic DECLARE_WORK(connector_reaper_work, fsnotify_connector_destroy_workfn); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_civoid fsnotify_get_mark(struct fsnotify_mark *mark) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci WARN_ON_ONCE(!refcount_read(&mark->refcnt)); 9762306a36Sopenharmony_ci refcount_inc(&mark->refcnt); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic __u32 *fsnotify_conn_mask_p(struct fsnotify_mark_connector *conn) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) 10362306a36Sopenharmony_ci return &fsnotify_conn_inode(conn)->i_fsnotify_mask; 10462306a36Sopenharmony_ci else if (conn->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) 10562306a36Sopenharmony_ci return &fsnotify_conn_mount(conn)->mnt_fsnotify_mask; 10662306a36Sopenharmony_ci else if (conn->type == FSNOTIFY_OBJ_TYPE_SB) 10762306a36Sopenharmony_ci return &fsnotify_conn_sb(conn)->s_fsnotify_mask; 10862306a36Sopenharmony_ci return NULL; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci__u32 fsnotify_conn_mask(struct fsnotify_mark_connector *conn) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci if (WARN_ON(!fsnotify_valid_obj_type(conn->type))) 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return *fsnotify_conn_mask_p(conn); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void fsnotify_get_inode_ref(struct inode *inode) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci ihold(inode); 12262306a36Sopenharmony_ci atomic_long_inc(&inode->i_sb->s_fsnotify_connectors); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* 12662306a36Sopenharmony_ci * Grab or drop inode reference for the connector if needed. 12762306a36Sopenharmony_ci * 12862306a36Sopenharmony_ci * When it's time to drop the reference, we only clear the HAS_IREF flag and 12962306a36Sopenharmony_ci * return the inode object. fsnotify_drop_object() will be resonsible for doing 13062306a36Sopenharmony_ci * iput() outside of spinlocks. This happens when last mark that wanted iref is 13162306a36Sopenharmony_ci * detached. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_cistatic struct inode *fsnotify_update_iref(struct fsnotify_mark_connector *conn, 13462306a36Sopenharmony_ci bool want_iref) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci bool has_iref = conn->flags & FSNOTIFY_CONN_FLAG_HAS_IREF; 13762306a36Sopenharmony_ci struct inode *inode = NULL; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (conn->type != FSNOTIFY_OBJ_TYPE_INODE || 14062306a36Sopenharmony_ci want_iref == has_iref) 14162306a36Sopenharmony_ci return NULL; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (want_iref) { 14462306a36Sopenharmony_ci /* Pin inode if any mark wants inode refcount held */ 14562306a36Sopenharmony_ci fsnotify_get_inode_ref(fsnotify_conn_inode(conn)); 14662306a36Sopenharmony_ci conn->flags |= FSNOTIFY_CONN_FLAG_HAS_IREF; 14762306a36Sopenharmony_ci } else { 14862306a36Sopenharmony_ci /* Unpin inode after detach of last mark that wanted iref */ 14962306a36Sopenharmony_ci inode = fsnotify_conn_inode(conn); 15062306a36Sopenharmony_ci conn->flags &= ~FSNOTIFY_CONN_FLAG_HAS_IREF; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return inode; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic void *__fsnotify_recalc_mask(struct fsnotify_mark_connector *conn) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci u32 new_mask = 0; 15962306a36Sopenharmony_ci bool want_iref = false; 16062306a36Sopenharmony_ci struct fsnotify_mark *mark; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci assert_spin_locked(&conn->lock); 16362306a36Sopenharmony_ci /* We can get detached connector here when inode is getting unlinked. */ 16462306a36Sopenharmony_ci if (!fsnotify_valid_obj_type(conn->type)) 16562306a36Sopenharmony_ci return NULL; 16662306a36Sopenharmony_ci hlist_for_each_entry(mark, &conn->list, obj_list) { 16762306a36Sopenharmony_ci if (!(mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED)) 16862306a36Sopenharmony_ci continue; 16962306a36Sopenharmony_ci new_mask |= fsnotify_calc_mask(mark); 17062306a36Sopenharmony_ci if (conn->type == FSNOTIFY_OBJ_TYPE_INODE && 17162306a36Sopenharmony_ci !(mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF)) 17262306a36Sopenharmony_ci want_iref = true; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci *fsnotify_conn_mask_p(conn) = new_mask; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return fsnotify_update_iref(conn, want_iref); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* 18062306a36Sopenharmony_ci * Calculate mask of events for a list of marks. The caller must make sure 18162306a36Sopenharmony_ci * connector and connector->obj cannot disappear under us. Callers achieve 18262306a36Sopenharmony_ci * this by holding a mark->lock or mark->group->mark_mutex for a mark on this 18362306a36Sopenharmony_ci * list. 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_civoid fsnotify_recalc_mask(struct fsnotify_mark_connector *conn) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci if (!conn) 18862306a36Sopenharmony_ci return; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci spin_lock(&conn->lock); 19162306a36Sopenharmony_ci __fsnotify_recalc_mask(conn); 19262306a36Sopenharmony_ci spin_unlock(&conn->lock); 19362306a36Sopenharmony_ci if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) 19462306a36Sopenharmony_ci __fsnotify_update_child_dentry_flags( 19562306a36Sopenharmony_ci fsnotify_conn_inode(conn)); 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci/* Free all connectors queued for freeing once SRCU period ends */ 19962306a36Sopenharmony_cistatic void fsnotify_connector_destroy_workfn(struct work_struct *work) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct fsnotify_mark_connector *conn, *free; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci spin_lock(&destroy_lock); 20462306a36Sopenharmony_ci conn = connector_destroy_list; 20562306a36Sopenharmony_ci connector_destroy_list = NULL; 20662306a36Sopenharmony_ci spin_unlock(&destroy_lock); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci synchronize_srcu(&fsnotify_mark_srcu); 20962306a36Sopenharmony_ci while (conn) { 21062306a36Sopenharmony_ci free = conn; 21162306a36Sopenharmony_ci conn = conn->destroy_next; 21262306a36Sopenharmony_ci kmem_cache_free(fsnotify_mark_connector_cachep, free); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic void fsnotify_put_inode_ref(struct inode *inode) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci iput(inode); 22162306a36Sopenharmony_ci if (atomic_long_dec_and_test(&sb->s_fsnotify_connectors)) 22262306a36Sopenharmony_ci wake_up_var(&sb->s_fsnotify_connectors); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void fsnotify_get_sb_connectors(struct fsnotify_mark_connector *conn) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct super_block *sb = fsnotify_connector_sb(conn); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (sb) 23062306a36Sopenharmony_ci atomic_long_inc(&sb->s_fsnotify_connectors); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic void fsnotify_put_sb_connectors(struct fsnotify_mark_connector *conn) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct super_block *sb = fsnotify_connector_sb(conn); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (sb && atomic_long_dec_and_test(&sb->s_fsnotify_connectors)) 23862306a36Sopenharmony_ci wake_up_var(&sb->s_fsnotify_connectors); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic void *fsnotify_detach_connector_from_object( 24262306a36Sopenharmony_ci struct fsnotify_mark_connector *conn, 24362306a36Sopenharmony_ci unsigned int *type) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct inode *inode = NULL; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci *type = conn->type; 24862306a36Sopenharmony_ci if (conn->type == FSNOTIFY_OBJ_TYPE_DETACHED) 24962306a36Sopenharmony_ci return NULL; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) { 25262306a36Sopenharmony_ci inode = fsnotify_conn_inode(conn); 25362306a36Sopenharmony_ci inode->i_fsnotify_mask = 0; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* Unpin inode when detaching from connector */ 25662306a36Sopenharmony_ci if (!(conn->flags & FSNOTIFY_CONN_FLAG_HAS_IREF)) 25762306a36Sopenharmony_ci inode = NULL; 25862306a36Sopenharmony_ci } else if (conn->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) { 25962306a36Sopenharmony_ci fsnotify_conn_mount(conn)->mnt_fsnotify_mask = 0; 26062306a36Sopenharmony_ci } else if (conn->type == FSNOTIFY_OBJ_TYPE_SB) { 26162306a36Sopenharmony_ci fsnotify_conn_sb(conn)->s_fsnotify_mask = 0; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci fsnotify_put_sb_connectors(conn); 26562306a36Sopenharmony_ci rcu_assign_pointer(*(conn->obj), NULL); 26662306a36Sopenharmony_ci conn->obj = NULL; 26762306a36Sopenharmony_ci conn->type = FSNOTIFY_OBJ_TYPE_DETACHED; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return inode; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void fsnotify_final_mark_destroy(struct fsnotify_mark *mark) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct fsnotify_group *group = mark->group; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (WARN_ON_ONCE(!group)) 27762306a36Sopenharmony_ci return; 27862306a36Sopenharmony_ci group->ops->free_mark(mark); 27962306a36Sopenharmony_ci fsnotify_put_group(group); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci/* Drop object reference originally held by a connector */ 28362306a36Sopenharmony_cistatic void fsnotify_drop_object(unsigned int type, void *objp) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci if (!objp) 28662306a36Sopenharmony_ci return; 28762306a36Sopenharmony_ci /* Currently only inode references are passed to be dropped */ 28862306a36Sopenharmony_ci if (WARN_ON_ONCE(type != FSNOTIFY_OBJ_TYPE_INODE)) 28962306a36Sopenharmony_ci return; 29062306a36Sopenharmony_ci fsnotify_put_inode_ref(objp); 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_civoid fsnotify_put_mark(struct fsnotify_mark *mark) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct fsnotify_mark_connector *conn = READ_ONCE(mark->connector); 29662306a36Sopenharmony_ci void *objp = NULL; 29762306a36Sopenharmony_ci unsigned int type = FSNOTIFY_OBJ_TYPE_DETACHED; 29862306a36Sopenharmony_ci bool free_conn = false; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* Catch marks that were actually never attached to object */ 30162306a36Sopenharmony_ci if (!conn) { 30262306a36Sopenharmony_ci if (refcount_dec_and_test(&mark->refcnt)) 30362306a36Sopenharmony_ci fsnotify_final_mark_destroy(mark); 30462306a36Sopenharmony_ci return; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* 30862306a36Sopenharmony_ci * We have to be careful so that traversals of obj_list under lock can 30962306a36Sopenharmony_ci * safely grab mark reference. 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ci if (!refcount_dec_and_lock(&mark->refcnt, &conn->lock)) 31262306a36Sopenharmony_ci return; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci hlist_del_init_rcu(&mark->obj_list); 31562306a36Sopenharmony_ci if (hlist_empty(&conn->list)) { 31662306a36Sopenharmony_ci objp = fsnotify_detach_connector_from_object(conn, &type); 31762306a36Sopenharmony_ci free_conn = true; 31862306a36Sopenharmony_ci } else { 31962306a36Sopenharmony_ci objp = __fsnotify_recalc_mask(conn); 32062306a36Sopenharmony_ci type = conn->type; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci WRITE_ONCE(mark->connector, NULL); 32362306a36Sopenharmony_ci spin_unlock(&conn->lock); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci fsnotify_drop_object(type, objp); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (free_conn) { 32862306a36Sopenharmony_ci spin_lock(&destroy_lock); 32962306a36Sopenharmony_ci conn->destroy_next = connector_destroy_list; 33062306a36Sopenharmony_ci connector_destroy_list = conn; 33162306a36Sopenharmony_ci spin_unlock(&destroy_lock); 33262306a36Sopenharmony_ci queue_work(system_unbound_wq, &connector_reaper_work); 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci /* 33562306a36Sopenharmony_ci * Note that we didn't update flags telling whether inode cares about 33662306a36Sopenharmony_ci * what's happening with children. We update these flags from 33762306a36Sopenharmony_ci * __fsnotify_parent() lazily when next event happens on one of our 33862306a36Sopenharmony_ci * children. 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ci spin_lock(&destroy_lock); 34162306a36Sopenharmony_ci list_add(&mark->g_list, &destroy_list); 34262306a36Sopenharmony_ci spin_unlock(&destroy_lock); 34362306a36Sopenharmony_ci queue_delayed_work(system_unbound_wq, &reaper_work, 34462306a36Sopenharmony_ci FSNOTIFY_REAPER_DELAY); 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsnotify_put_mark); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci/* 34962306a36Sopenharmony_ci * Get mark reference when we found the mark via lockless traversal of object 35062306a36Sopenharmony_ci * list. Mark can be already removed from the list by now and on its way to be 35162306a36Sopenharmony_ci * destroyed once SRCU period ends. 35262306a36Sopenharmony_ci * 35362306a36Sopenharmony_ci * Also pin the group so it doesn't disappear under us. 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_cistatic bool fsnotify_get_mark_safe(struct fsnotify_mark *mark) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci if (!mark) 35862306a36Sopenharmony_ci return true; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (refcount_inc_not_zero(&mark->refcnt)) { 36162306a36Sopenharmony_ci spin_lock(&mark->lock); 36262306a36Sopenharmony_ci if (mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED) { 36362306a36Sopenharmony_ci /* mark is attached, group is still alive then */ 36462306a36Sopenharmony_ci atomic_inc(&mark->group->user_waits); 36562306a36Sopenharmony_ci spin_unlock(&mark->lock); 36662306a36Sopenharmony_ci return true; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci spin_unlock(&mark->lock); 36962306a36Sopenharmony_ci fsnotify_put_mark(mark); 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci return false; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci/* 37562306a36Sopenharmony_ci * Puts marks and wakes up group destruction if necessary. 37662306a36Sopenharmony_ci * 37762306a36Sopenharmony_ci * Pairs with fsnotify_get_mark_safe() 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_cistatic void fsnotify_put_mark_wake(struct fsnotify_mark *mark) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci if (mark) { 38262306a36Sopenharmony_ci struct fsnotify_group *group = mark->group; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci fsnotify_put_mark(mark); 38562306a36Sopenharmony_ci /* 38662306a36Sopenharmony_ci * We abuse notification_waitq on group shutdown for waiting for 38762306a36Sopenharmony_ci * all marks pinned when waiting for userspace. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_ci if (atomic_dec_and_test(&group->user_waits) && group->shutdown) 39062306a36Sopenharmony_ci wake_up(&group->notification_waitq); 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cibool fsnotify_prepare_user_wait(struct fsnotify_iter_info *iter_info) 39562306a36Sopenharmony_ci __releases(&fsnotify_mark_srcu) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci int type; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci fsnotify_foreach_iter_type(type) { 40062306a36Sopenharmony_ci /* This can fail if mark is being removed */ 40162306a36Sopenharmony_ci if (!fsnotify_get_mark_safe(iter_info->marks[type])) { 40262306a36Sopenharmony_ci __release(&fsnotify_mark_srcu); 40362306a36Sopenharmony_ci goto fail; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* 40862306a36Sopenharmony_ci * Now that both marks are pinned by refcount in the inode / vfsmount 40962306a36Sopenharmony_ci * lists, we can drop SRCU lock, and safely resume the list iteration 41062306a36Sopenharmony_ci * once userspace returns. 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_ci srcu_read_unlock(&fsnotify_mark_srcu, iter_info->srcu_idx); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return true; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cifail: 41762306a36Sopenharmony_ci for (type--; type >= 0; type--) 41862306a36Sopenharmony_ci fsnotify_put_mark_wake(iter_info->marks[type]); 41962306a36Sopenharmony_ci return false; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_civoid fsnotify_finish_user_wait(struct fsnotify_iter_info *iter_info) 42362306a36Sopenharmony_ci __acquires(&fsnotify_mark_srcu) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci int type; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci iter_info->srcu_idx = srcu_read_lock(&fsnotify_mark_srcu); 42862306a36Sopenharmony_ci fsnotify_foreach_iter_type(type) 42962306a36Sopenharmony_ci fsnotify_put_mark_wake(iter_info->marks[type]); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci/* 43362306a36Sopenharmony_ci * Mark mark as detached, remove it from group list. Mark still stays in object 43462306a36Sopenharmony_ci * list until its last reference is dropped. Note that we rely on mark being 43562306a36Sopenharmony_ci * removed from group list before corresponding reference to it is dropped. In 43662306a36Sopenharmony_ci * particular we rely on mark->connector being valid while we hold 43762306a36Sopenharmony_ci * group->mark_mutex if we found the mark through g_list. 43862306a36Sopenharmony_ci * 43962306a36Sopenharmony_ci * Must be called with group->mark_mutex held. The caller must either hold 44062306a36Sopenharmony_ci * reference to the mark or be protected by fsnotify_mark_srcu. 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_civoid fsnotify_detach_mark(struct fsnotify_mark *mark) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci fsnotify_group_assert_locked(mark->group); 44562306a36Sopenharmony_ci WARN_ON_ONCE(!srcu_read_lock_held(&fsnotify_mark_srcu) && 44662306a36Sopenharmony_ci refcount_read(&mark->refcnt) < 1 + 44762306a36Sopenharmony_ci !!(mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED)); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci spin_lock(&mark->lock); 45062306a36Sopenharmony_ci /* something else already called this function on this mark */ 45162306a36Sopenharmony_ci if (!(mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED)) { 45262306a36Sopenharmony_ci spin_unlock(&mark->lock); 45362306a36Sopenharmony_ci return; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci mark->flags &= ~FSNOTIFY_MARK_FLAG_ATTACHED; 45662306a36Sopenharmony_ci list_del_init(&mark->g_list); 45762306a36Sopenharmony_ci spin_unlock(&mark->lock); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* Drop mark reference acquired in fsnotify_add_mark_locked() */ 46062306a36Sopenharmony_ci fsnotify_put_mark(mark); 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci/* 46462306a36Sopenharmony_ci * Free fsnotify mark. The mark is actually only marked as being freed. The 46562306a36Sopenharmony_ci * freeing is actually happening only once last reference to the mark is 46662306a36Sopenharmony_ci * dropped from a workqueue which first waits for srcu period end. 46762306a36Sopenharmony_ci * 46862306a36Sopenharmony_ci * Caller must have a reference to the mark or be protected by 46962306a36Sopenharmony_ci * fsnotify_mark_srcu. 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_civoid fsnotify_free_mark(struct fsnotify_mark *mark) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct fsnotify_group *group = mark->group; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci spin_lock(&mark->lock); 47662306a36Sopenharmony_ci /* something else already called this function on this mark */ 47762306a36Sopenharmony_ci if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE)) { 47862306a36Sopenharmony_ci spin_unlock(&mark->lock); 47962306a36Sopenharmony_ci return; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; 48262306a36Sopenharmony_ci spin_unlock(&mark->lock); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci /* 48562306a36Sopenharmony_ci * Some groups like to know that marks are being freed. This is a 48662306a36Sopenharmony_ci * callback to the group function to let it know that this mark 48762306a36Sopenharmony_ci * is being freed. 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_ci if (group->ops->freeing_mark) 49062306a36Sopenharmony_ci group->ops->freeing_mark(mark, group); 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_civoid fsnotify_destroy_mark(struct fsnotify_mark *mark, 49462306a36Sopenharmony_ci struct fsnotify_group *group) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci fsnotify_group_lock(group); 49762306a36Sopenharmony_ci fsnotify_detach_mark(mark); 49862306a36Sopenharmony_ci fsnotify_group_unlock(group); 49962306a36Sopenharmony_ci fsnotify_free_mark(mark); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsnotify_destroy_mark); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci/* 50462306a36Sopenharmony_ci * Sorting function for lists of fsnotify marks. 50562306a36Sopenharmony_ci * 50662306a36Sopenharmony_ci * Fanotify supports different notification classes (reflected as priority of 50762306a36Sopenharmony_ci * notification group). Events shall be passed to notification groups in 50862306a36Sopenharmony_ci * decreasing priority order. To achieve this marks in notification lists for 50962306a36Sopenharmony_ci * inodes and vfsmounts are sorted so that priorities of corresponding groups 51062306a36Sopenharmony_ci * are descending. 51162306a36Sopenharmony_ci * 51262306a36Sopenharmony_ci * Furthermore correct handling of the ignore mask requires processing inode 51362306a36Sopenharmony_ci * and vfsmount marks of each group together. Using the group address as 51462306a36Sopenharmony_ci * further sort criterion provides a unique sorting order and thus we can 51562306a36Sopenharmony_ci * merge inode and vfsmount lists of marks in linear time and find groups 51662306a36Sopenharmony_ci * present in both lists. 51762306a36Sopenharmony_ci * 51862306a36Sopenharmony_ci * A return value of 1 signifies that b has priority over a. 51962306a36Sopenharmony_ci * A return value of 0 signifies that the two marks have to be handled together. 52062306a36Sopenharmony_ci * A return value of -1 signifies that a has priority over b. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_ciint fsnotify_compare_groups(struct fsnotify_group *a, struct fsnotify_group *b) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci if (a == b) 52562306a36Sopenharmony_ci return 0; 52662306a36Sopenharmony_ci if (!a) 52762306a36Sopenharmony_ci return 1; 52862306a36Sopenharmony_ci if (!b) 52962306a36Sopenharmony_ci return -1; 53062306a36Sopenharmony_ci if (a->priority < b->priority) 53162306a36Sopenharmony_ci return 1; 53262306a36Sopenharmony_ci if (a->priority > b->priority) 53362306a36Sopenharmony_ci return -1; 53462306a36Sopenharmony_ci if (a < b) 53562306a36Sopenharmony_ci return 1; 53662306a36Sopenharmony_ci return -1; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp, 54062306a36Sopenharmony_ci unsigned int obj_type, 54162306a36Sopenharmony_ci __kernel_fsid_t *fsid) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci struct fsnotify_mark_connector *conn; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci conn = kmem_cache_alloc(fsnotify_mark_connector_cachep, GFP_KERNEL); 54662306a36Sopenharmony_ci if (!conn) 54762306a36Sopenharmony_ci return -ENOMEM; 54862306a36Sopenharmony_ci spin_lock_init(&conn->lock); 54962306a36Sopenharmony_ci INIT_HLIST_HEAD(&conn->list); 55062306a36Sopenharmony_ci conn->flags = 0; 55162306a36Sopenharmony_ci conn->type = obj_type; 55262306a36Sopenharmony_ci conn->obj = connp; 55362306a36Sopenharmony_ci /* Cache fsid of filesystem containing the object */ 55462306a36Sopenharmony_ci if (fsid) { 55562306a36Sopenharmony_ci conn->fsid = *fsid; 55662306a36Sopenharmony_ci conn->flags = FSNOTIFY_CONN_FLAG_HAS_FSID; 55762306a36Sopenharmony_ci } else { 55862306a36Sopenharmony_ci conn->fsid.val[0] = conn->fsid.val[1] = 0; 55962306a36Sopenharmony_ci conn->flags = 0; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci fsnotify_get_sb_connectors(conn); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* 56462306a36Sopenharmony_ci * cmpxchg() provides the barrier so that readers of *connp can see 56562306a36Sopenharmony_ci * only initialized structure 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci if (cmpxchg(connp, NULL, conn)) { 56862306a36Sopenharmony_ci /* Someone else created list structure for us */ 56962306a36Sopenharmony_ci fsnotify_put_sb_connectors(conn); 57062306a36Sopenharmony_ci kmem_cache_free(fsnotify_mark_connector_cachep, conn); 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci return 0; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci/* 57762306a36Sopenharmony_ci * Get mark connector, make sure it is alive and return with its lock held. 57862306a36Sopenharmony_ci * This is for users that get connector pointer from inode or mount. Users that 57962306a36Sopenharmony_ci * hold reference to a mark on the list may directly lock connector->lock as 58062306a36Sopenharmony_ci * they are sure list cannot go away under them. 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_cistatic struct fsnotify_mark_connector *fsnotify_grab_connector( 58362306a36Sopenharmony_ci fsnotify_connp_t *connp) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci struct fsnotify_mark_connector *conn; 58662306a36Sopenharmony_ci int idx; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci idx = srcu_read_lock(&fsnotify_mark_srcu); 58962306a36Sopenharmony_ci conn = srcu_dereference(*connp, &fsnotify_mark_srcu); 59062306a36Sopenharmony_ci if (!conn) 59162306a36Sopenharmony_ci goto out; 59262306a36Sopenharmony_ci spin_lock(&conn->lock); 59362306a36Sopenharmony_ci if (conn->type == FSNOTIFY_OBJ_TYPE_DETACHED) { 59462306a36Sopenharmony_ci spin_unlock(&conn->lock); 59562306a36Sopenharmony_ci srcu_read_unlock(&fsnotify_mark_srcu, idx); 59662306a36Sopenharmony_ci return NULL; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ciout: 59962306a36Sopenharmony_ci srcu_read_unlock(&fsnotify_mark_srcu, idx); 60062306a36Sopenharmony_ci return conn; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci/* 60462306a36Sopenharmony_ci * Add mark into proper place in given list of marks. These marks may be used 60562306a36Sopenharmony_ci * for the fsnotify backend to determine which event types should be delivered 60662306a36Sopenharmony_ci * to which group and for which inodes. These marks are ordered according to 60762306a36Sopenharmony_ci * priority, highest number first, and then by the group's location in memory. 60862306a36Sopenharmony_ci */ 60962306a36Sopenharmony_cistatic int fsnotify_add_mark_list(struct fsnotify_mark *mark, 61062306a36Sopenharmony_ci fsnotify_connp_t *connp, 61162306a36Sopenharmony_ci unsigned int obj_type, 61262306a36Sopenharmony_ci int add_flags, __kernel_fsid_t *fsid) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci struct fsnotify_mark *lmark, *last = NULL; 61562306a36Sopenharmony_ci struct fsnotify_mark_connector *conn; 61662306a36Sopenharmony_ci int cmp; 61762306a36Sopenharmony_ci int err = 0; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (WARN_ON(!fsnotify_valid_obj_type(obj_type))) 62062306a36Sopenharmony_ci return -EINVAL; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci /* Backend is expected to check for zero fsid (e.g. tmpfs) */ 62362306a36Sopenharmony_ci if (fsid && WARN_ON_ONCE(!fsid->val[0] && !fsid->val[1])) 62462306a36Sopenharmony_ci return -ENODEV; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cirestart: 62762306a36Sopenharmony_ci spin_lock(&mark->lock); 62862306a36Sopenharmony_ci conn = fsnotify_grab_connector(connp); 62962306a36Sopenharmony_ci if (!conn) { 63062306a36Sopenharmony_ci spin_unlock(&mark->lock); 63162306a36Sopenharmony_ci err = fsnotify_attach_connector_to_object(connp, obj_type, 63262306a36Sopenharmony_ci fsid); 63362306a36Sopenharmony_ci if (err) 63462306a36Sopenharmony_ci return err; 63562306a36Sopenharmony_ci goto restart; 63662306a36Sopenharmony_ci } else if (fsid && !(conn->flags & FSNOTIFY_CONN_FLAG_HAS_FSID)) { 63762306a36Sopenharmony_ci conn->fsid = *fsid; 63862306a36Sopenharmony_ci /* Pairs with smp_rmb() in fanotify_get_fsid() */ 63962306a36Sopenharmony_ci smp_wmb(); 64062306a36Sopenharmony_ci conn->flags |= FSNOTIFY_CONN_FLAG_HAS_FSID; 64162306a36Sopenharmony_ci } else if (fsid && (conn->flags & FSNOTIFY_CONN_FLAG_HAS_FSID) && 64262306a36Sopenharmony_ci (fsid->val[0] != conn->fsid.val[0] || 64362306a36Sopenharmony_ci fsid->val[1] != conn->fsid.val[1])) { 64462306a36Sopenharmony_ci /* 64562306a36Sopenharmony_ci * Backend is expected to check for non uniform fsid 64662306a36Sopenharmony_ci * (e.g. btrfs), but maybe we missed something? 64762306a36Sopenharmony_ci * Only allow setting conn->fsid once to non zero fsid. 64862306a36Sopenharmony_ci * inotify and non-fid fanotify groups do not set nor test 64962306a36Sopenharmony_ci * conn->fsid. 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci pr_warn_ratelimited("%s: fsid mismatch on object of type %u: " 65262306a36Sopenharmony_ci "%x.%x != %x.%x\n", __func__, conn->type, 65362306a36Sopenharmony_ci fsid->val[0], fsid->val[1], 65462306a36Sopenharmony_ci conn->fsid.val[0], conn->fsid.val[1]); 65562306a36Sopenharmony_ci err = -EXDEV; 65662306a36Sopenharmony_ci goto out_err; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* is mark the first mark? */ 66062306a36Sopenharmony_ci if (hlist_empty(&conn->list)) { 66162306a36Sopenharmony_ci hlist_add_head_rcu(&mark->obj_list, &conn->list); 66262306a36Sopenharmony_ci goto added; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* should mark be in the middle of the current list? */ 66662306a36Sopenharmony_ci hlist_for_each_entry(lmark, &conn->list, obj_list) { 66762306a36Sopenharmony_ci last = lmark; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if ((lmark->group == mark->group) && 67062306a36Sopenharmony_ci (lmark->flags & FSNOTIFY_MARK_FLAG_ATTACHED) && 67162306a36Sopenharmony_ci !(mark->group->flags & FSNOTIFY_GROUP_DUPS)) { 67262306a36Sopenharmony_ci err = -EEXIST; 67362306a36Sopenharmony_ci goto out_err; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci cmp = fsnotify_compare_groups(lmark->group, mark->group); 67762306a36Sopenharmony_ci if (cmp >= 0) { 67862306a36Sopenharmony_ci hlist_add_before_rcu(&mark->obj_list, &lmark->obj_list); 67962306a36Sopenharmony_ci goto added; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci BUG_ON(last == NULL); 68462306a36Sopenharmony_ci /* mark should be the last entry. last is the current last entry */ 68562306a36Sopenharmony_ci hlist_add_behind_rcu(&mark->obj_list, &last->obj_list); 68662306a36Sopenharmony_ciadded: 68762306a36Sopenharmony_ci /* 68862306a36Sopenharmony_ci * Since connector is attached to object using cmpxchg() we are 68962306a36Sopenharmony_ci * guaranteed that connector initialization is fully visible by anyone 69062306a36Sopenharmony_ci * seeing mark->connector set. 69162306a36Sopenharmony_ci */ 69262306a36Sopenharmony_ci WRITE_ONCE(mark->connector, conn); 69362306a36Sopenharmony_ciout_err: 69462306a36Sopenharmony_ci spin_unlock(&conn->lock); 69562306a36Sopenharmony_ci spin_unlock(&mark->lock); 69662306a36Sopenharmony_ci return err; 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci/* 70062306a36Sopenharmony_ci * Attach an initialized mark to a given group and fs object. 70162306a36Sopenharmony_ci * These marks may be used for the fsnotify backend to determine which 70262306a36Sopenharmony_ci * event types should be delivered to which group. 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_ciint fsnotify_add_mark_locked(struct fsnotify_mark *mark, 70562306a36Sopenharmony_ci fsnotify_connp_t *connp, unsigned int obj_type, 70662306a36Sopenharmony_ci int add_flags, __kernel_fsid_t *fsid) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct fsnotify_group *group = mark->group; 70962306a36Sopenharmony_ci int ret = 0; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci fsnotify_group_assert_locked(group); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci /* 71462306a36Sopenharmony_ci * LOCKING ORDER!!!! 71562306a36Sopenharmony_ci * group->mark_mutex 71662306a36Sopenharmony_ci * mark->lock 71762306a36Sopenharmony_ci * mark->connector->lock 71862306a36Sopenharmony_ci */ 71962306a36Sopenharmony_ci spin_lock(&mark->lock); 72062306a36Sopenharmony_ci mark->flags |= FSNOTIFY_MARK_FLAG_ALIVE | FSNOTIFY_MARK_FLAG_ATTACHED; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci list_add(&mark->g_list, &group->marks_list); 72362306a36Sopenharmony_ci fsnotify_get_mark(mark); /* for g_list */ 72462306a36Sopenharmony_ci spin_unlock(&mark->lock); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci ret = fsnotify_add_mark_list(mark, connp, obj_type, add_flags, fsid); 72762306a36Sopenharmony_ci if (ret) 72862306a36Sopenharmony_ci goto err; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci fsnotify_recalc_mask(mark->connector); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci return ret; 73362306a36Sopenharmony_cierr: 73462306a36Sopenharmony_ci spin_lock(&mark->lock); 73562306a36Sopenharmony_ci mark->flags &= ~(FSNOTIFY_MARK_FLAG_ALIVE | 73662306a36Sopenharmony_ci FSNOTIFY_MARK_FLAG_ATTACHED); 73762306a36Sopenharmony_ci list_del_init(&mark->g_list); 73862306a36Sopenharmony_ci spin_unlock(&mark->lock); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci fsnotify_put_mark(mark); 74162306a36Sopenharmony_ci return ret; 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ciint fsnotify_add_mark(struct fsnotify_mark *mark, fsnotify_connp_t *connp, 74562306a36Sopenharmony_ci unsigned int obj_type, int add_flags, 74662306a36Sopenharmony_ci __kernel_fsid_t *fsid) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci int ret; 74962306a36Sopenharmony_ci struct fsnotify_group *group = mark->group; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci fsnotify_group_lock(group); 75262306a36Sopenharmony_ci ret = fsnotify_add_mark_locked(mark, connp, obj_type, add_flags, fsid); 75362306a36Sopenharmony_ci fsnotify_group_unlock(group); 75462306a36Sopenharmony_ci return ret; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsnotify_add_mark); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci/* 75962306a36Sopenharmony_ci * Given a list of marks, find the mark associated with given group. If found 76062306a36Sopenharmony_ci * take a reference to that mark and return it, else return NULL. 76162306a36Sopenharmony_ci */ 76262306a36Sopenharmony_cistruct fsnotify_mark *fsnotify_find_mark(fsnotify_connp_t *connp, 76362306a36Sopenharmony_ci struct fsnotify_group *group) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci struct fsnotify_mark_connector *conn; 76662306a36Sopenharmony_ci struct fsnotify_mark *mark; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci conn = fsnotify_grab_connector(connp); 76962306a36Sopenharmony_ci if (!conn) 77062306a36Sopenharmony_ci return NULL; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci hlist_for_each_entry(mark, &conn->list, obj_list) { 77362306a36Sopenharmony_ci if (mark->group == group && 77462306a36Sopenharmony_ci (mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED)) { 77562306a36Sopenharmony_ci fsnotify_get_mark(mark); 77662306a36Sopenharmony_ci spin_unlock(&conn->lock); 77762306a36Sopenharmony_ci return mark; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci spin_unlock(&conn->lock); 78162306a36Sopenharmony_ci return NULL; 78262306a36Sopenharmony_ci} 78362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsnotify_find_mark); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci/* Clear any marks in a group with given type mask */ 78662306a36Sopenharmony_civoid fsnotify_clear_marks_by_group(struct fsnotify_group *group, 78762306a36Sopenharmony_ci unsigned int obj_type) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct fsnotify_mark *lmark, *mark; 79062306a36Sopenharmony_ci LIST_HEAD(to_free); 79162306a36Sopenharmony_ci struct list_head *head = &to_free; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* Skip selection step if we want to clear all marks. */ 79462306a36Sopenharmony_ci if (obj_type == FSNOTIFY_OBJ_TYPE_ANY) { 79562306a36Sopenharmony_ci head = &group->marks_list; 79662306a36Sopenharmony_ci goto clear; 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci /* 79962306a36Sopenharmony_ci * We have to be really careful here. Anytime we drop mark_mutex, e.g. 80062306a36Sopenharmony_ci * fsnotify_clear_marks_by_inode() can come and free marks. Even in our 80162306a36Sopenharmony_ci * to_free list so we have to use mark_mutex even when accessing that 80262306a36Sopenharmony_ci * list. And freeing mark requires us to drop mark_mutex. So we can 80362306a36Sopenharmony_ci * reliably free only the first mark in the list. That's why we first 80462306a36Sopenharmony_ci * move marks to free to to_free list in one go and then free marks in 80562306a36Sopenharmony_ci * to_free list one by one. 80662306a36Sopenharmony_ci */ 80762306a36Sopenharmony_ci fsnotify_group_lock(group); 80862306a36Sopenharmony_ci list_for_each_entry_safe(mark, lmark, &group->marks_list, g_list) { 80962306a36Sopenharmony_ci if (mark->connector->type == obj_type) 81062306a36Sopenharmony_ci list_move(&mark->g_list, &to_free); 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci fsnotify_group_unlock(group); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ciclear: 81562306a36Sopenharmony_ci while (1) { 81662306a36Sopenharmony_ci fsnotify_group_lock(group); 81762306a36Sopenharmony_ci if (list_empty(head)) { 81862306a36Sopenharmony_ci fsnotify_group_unlock(group); 81962306a36Sopenharmony_ci break; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci mark = list_first_entry(head, struct fsnotify_mark, g_list); 82262306a36Sopenharmony_ci fsnotify_get_mark(mark); 82362306a36Sopenharmony_ci fsnotify_detach_mark(mark); 82462306a36Sopenharmony_ci fsnotify_group_unlock(group); 82562306a36Sopenharmony_ci fsnotify_free_mark(mark); 82662306a36Sopenharmony_ci fsnotify_put_mark(mark); 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci/* Destroy all marks attached to an object via connector */ 83162306a36Sopenharmony_civoid fsnotify_destroy_marks(fsnotify_connp_t *connp) 83262306a36Sopenharmony_ci{ 83362306a36Sopenharmony_ci struct fsnotify_mark_connector *conn; 83462306a36Sopenharmony_ci struct fsnotify_mark *mark, *old_mark = NULL; 83562306a36Sopenharmony_ci void *objp; 83662306a36Sopenharmony_ci unsigned int type; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci conn = fsnotify_grab_connector(connp); 83962306a36Sopenharmony_ci if (!conn) 84062306a36Sopenharmony_ci return; 84162306a36Sopenharmony_ci /* 84262306a36Sopenharmony_ci * We have to be careful since we can race with e.g. 84362306a36Sopenharmony_ci * fsnotify_clear_marks_by_group() and once we drop the conn->lock, the 84462306a36Sopenharmony_ci * list can get modified. However we are holding mark reference and 84562306a36Sopenharmony_ci * thus our mark cannot be removed from obj_list so we can continue 84662306a36Sopenharmony_ci * iteration after regaining conn->lock. 84762306a36Sopenharmony_ci */ 84862306a36Sopenharmony_ci hlist_for_each_entry(mark, &conn->list, obj_list) { 84962306a36Sopenharmony_ci fsnotify_get_mark(mark); 85062306a36Sopenharmony_ci spin_unlock(&conn->lock); 85162306a36Sopenharmony_ci if (old_mark) 85262306a36Sopenharmony_ci fsnotify_put_mark(old_mark); 85362306a36Sopenharmony_ci old_mark = mark; 85462306a36Sopenharmony_ci fsnotify_destroy_mark(mark, mark->group); 85562306a36Sopenharmony_ci spin_lock(&conn->lock); 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci /* 85862306a36Sopenharmony_ci * Detach list from object now so that we don't pin inode until all 85962306a36Sopenharmony_ci * mark references get dropped. It would lead to strange results such 86062306a36Sopenharmony_ci * as delaying inode deletion or blocking unmount. 86162306a36Sopenharmony_ci */ 86262306a36Sopenharmony_ci objp = fsnotify_detach_connector_from_object(conn, &type); 86362306a36Sopenharmony_ci spin_unlock(&conn->lock); 86462306a36Sopenharmony_ci if (old_mark) 86562306a36Sopenharmony_ci fsnotify_put_mark(old_mark); 86662306a36Sopenharmony_ci fsnotify_drop_object(type, objp); 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci/* 87062306a36Sopenharmony_ci * Nothing fancy, just initialize lists and locks and counters. 87162306a36Sopenharmony_ci */ 87262306a36Sopenharmony_civoid fsnotify_init_mark(struct fsnotify_mark *mark, 87362306a36Sopenharmony_ci struct fsnotify_group *group) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci memset(mark, 0, sizeof(*mark)); 87662306a36Sopenharmony_ci spin_lock_init(&mark->lock); 87762306a36Sopenharmony_ci refcount_set(&mark->refcnt, 1); 87862306a36Sopenharmony_ci fsnotify_get_group(group); 87962306a36Sopenharmony_ci mark->group = group; 88062306a36Sopenharmony_ci WRITE_ONCE(mark->connector, NULL); 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsnotify_init_mark); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci/* 88562306a36Sopenharmony_ci * Destroy all marks in destroy_list, waits for SRCU period to finish before 88662306a36Sopenharmony_ci * actually freeing marks. 88762306a36Sopenharmony_ci */ 88862306a36Sopenharmony_cistatic void fsnotify_mark_destroy_workfn(struct work_struct *work) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci struct fsnotify_mark *mark, *next; 89162306a36Sopenharmony_ci struct list_head private_destroy_list; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci spin_lock(&destroy_lock); 89462306a36Sopenharmony_ci /* exchange the list head */ 89562306a36Sopenharmony_ci list_replace_init(&destroy_list, &private_destroy_list); 89662306a36Sopenharmony_ci spin_unlock(&destroy_lock); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci synchronize_srcu(&fsnotify_mark_srcu); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci list_for_each_entry_safe(mark, next, &private_destroy_list, g_list) { 90162306a36Sopenharmony_ci list_del_init(&mark->g_list); 90262306a36Sopenharmony_ci fsnotify_final_mark_destroy(mark); 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci} 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci/* Wait for all marks queued for destruction to be actually destroyed */ 90762306a36Sopenharmony_civoid fsnotify_wait_marks_destroyed(void) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci flush_delayed_work(&reaper_work); 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsnotify_wait_marks_destroyed); 912