mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:44:45 +01:00
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>
This commit is contained in:
parent
6c790212c5
commit
94bd01253c
4 changed files with 89 additions and 15 deletions
|
|
@ -112,7 +112,10 @@ void fsnotify_sb_delete(struct super_block *sb)
|
|||
|
||||
void fsnotify_sb_free(struct super_block *sb)
|
||||
{
|
||||
kfree(sb->s_fsnotify_info);
|
||||
if (sb->s_fsnotify_info) {
|
||||
WARN_ON_ONCE(!list_empty(&sb->s_fsnotify_info->inode_conn_list));
|
||||
kfree(sb->s_fsnotify_info);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -777,8 +780,7 @@ static __init int fsnotify_init(void)
|
|||
if (ret)
|
||||
panic("initializing fsnotify_mark_srcu");
|
||||
|
||||
fsnotify_mark_connector_cachep = KMEM_CACHE(fsnotify_mark_connector,
|
||||
SLAB_PANIC);
|
||||
fsnotify_init_connector_caches();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,6 +106,6 @@ static inline void fsnotify_clear_marks_by_mntns(struct mnt_namespace *mntns)
|
|||
*/
|
||||
extern void fsnotify_set_children_dentry_flags(struct inode *inode);
|
||||
|
||||
extern struct kmem_cache *fsnotify_mark_connector_cachep;
|
||||
void fsnotify_init_connector_caches(void);
|
||||
|
||||
#endif /* __FS_NOTIFY_FSNOTIFY_H_ */
|
||||
|
|
|
|||
|
|
@ -79,7 +79,8 @@
|
|||
#define FSNOTIFY_REAPER_DELAY (1) /* 1 jiffy */
|
||||
|
||||
struct srcu_struct fsnotify_mark_srcu;
|
||||
struct kmem_cache *fsnotify_mark_connector_cachep;
|
||||
static struct kmem_cache *fsnotify_mark_connector_cachep;
|
||||
static struct kmem_cache *fsnotify_inode_mark_connector_cachep;
|
||||
|
||||
static DEFINE_SPINLOCK(destroy_lock);
|
||||
static LIST_HEAD(destroy_list);
|
||||
|
|
@ -323,10 +324,12 @@ static void fsnotify_connector_destroy_workfn(struct work_struct *work)
|
|||
while (conn) {
|
||||
free = conn;
|
||||
conn = conn->destroy_next;
|
||||
kmem_cache_free(fsnotify_mark_connector_cachep, free);
|
||||
kfree(free);
|
||||
}
|
||||
}
|
||||
|
||||
static void fsnotify_untrack_connector(struct fsnotify_mark_connector *conn);
|
||||
|
||||
static void *fsnotify_detach_connector_from_object(
|
||||
struct fsnotify_mark_connector *conn,
|
||||
unsigned int *type)
|
||||
|
|
@ -342,6 +345,7 @@ static void *fsnotify_detach_connector_from_object(
|
|||
if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) {
|
||||
inode = fsnotify_conn_inode(conn);
|
||||
inode->i_fsnotify_mask = 0;
|
||||
fsnotify_untrack_connector(conn);
|
||||
|
||||
/* Unpin inode when detaching from connector */
|
||||
if (!(conn->flags & FSNOTIFY_CONN_FLAG_HAS_IREF))
|
||||
|
|
@ -644,6 +648,8 @@ static int fsnotify_attach_info_to_sb(struct super_block *sb)
|
|||
if (!sbinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&sbinfo->inode_conn_list);
|
||||
spin_lock_init(&sbinfo->list_lock);
|
||||
/*
|
||||
* cmpxchg() provides the barrier so that callers of fsnotify_sb_info()
|
||||
* will observe an initialized structure
|
||||
|
|
@ -655,20 +661,73 @@ static int fsnotify_attach_info_to_sb(struct super_block *sb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
|
||||
void *obj, unsigned int obj_type)
|
||||
{
|
||||
struct fsnotify_mark_connector *conn;
|
||||
struct fsnotify_inode_mark_connector {
|
||||
struct fsnotify_mark_connector common;
|
||||
struct list_head conns_list;
|
||||
};
|
||||
|
||||
conn = kmem_cache_alloc(fsnotify_mark_connector_cachep, GFP_KERNEL);
|
||||
if (!conn)
|
||||
return -ENOMEM;
|
||||
static void fsnotify_init_connector(struct fsnotify_mark_connector *conn,
|
||||
void *obj, unsigned int obj_type)
|
||||
{
|
||||
spin_lock_init(&conn->lock);
|
||||
INIT_HLIST_HEAD(&conn->list);
|
||||
conn->flags = 0;
|
||||
conn->prio = 0;
|
||||
conn->type = obj_type;
|
||||
conn->obj = obj;
|
||||
}
|
||||
|
||||
static struct fsnotify_mark_connector *
|
||||
fsnotify_alloc_inode_connector(struct inode *inode)
|
||||
{
|
||||
struct fsnotify_inode_mark_connector *iconn;
|
||||
struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(inode->i_sb);
|
||||
|
||||
iconn = kmem_cache_alloc(fsnotify_inode_mark_connector_cachep,
|
||||
GFP_KERNEL);
|
||||
if (!iconn)
|
||||
return NULL;
|
||||
|
||||
fsnotify_init_connector(&iconn->common, inode, FSNOTIFY_OBJ_TYPE_INODE);
|
||||
spin_lock(&sbinfo->list_lock);
|
||||
list_add(&iconn->conns_list, &sbinfo->inode_conn_list);
|
||||
spin_unlock(&sbinfo->list_lock);
|
||||
|
||||
return &iconn->common;
|
||||
}
|
||||
|
||||
static void fsnotify_untrack_connector(struct fsnotify_mark_connector *conn)
|
||||
{
|
||||
struct fsnotify_inode_mark_connector *iconn;
|
||||
struct fsnotify_sb_info *sbinfo;
|
||||
|
||||
if (conn->type != FSNOTIFY_OBJ_TYPE_INODE)
|
||||
return;
|
||||
|
||||
iconn = container_of(conn, struct fsnotify_inode_mark_connector, common);
|
||||
sbinfo = fsnotify_sb_info(fsnotify_conn_inode(conn)->i_sb);
|
||||
spin_lock(&sbinfo->list_lock);
|
||||
list_del(&iconn->conns_list);
|
||||
spin_unlock(&sbinfo->list_lock);
|
||||
}
|
||||
|
||||
static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
|
||||
void *obj, unsigned int obj_type)
|
||||
{
|
||||
struct fsnotify_mark_connector *conn;
|
||||
|
||||
if (obj_type == FSNOTIFY_OBJ_TYPE_INODE) {
|
||||
struct inode *inode = obj;
|
||||
|
||||
conn = fsnotify_alloc_inode_connector(inode);
|
||||
} else {
|
||||
conn = kmem_cache_alloc(fsnotify_mark_connector_cachep,
|
||||
GFP_KERNEL);
|
||||
if (conn)
|
||||
fsnotify_init_connector(conn, obj, obj_type);
|
||||
}
|
||||
if (!conn)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* cmpxchg() provides the barrier so that readers of *connp can see
|
||||
|
|
@ -676,7 +735,8 @@ static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
|
|||
*/
|
||||
if (cmpxchg(connp, NULL, conn)) {
|
||||
/* Someone else created list structure for us */
|
||||
kmem_cache_free(fsnotify_mark_connector_cachep, conn);
|
||||
fsnotify_untrack_connector(conn);
|
||||
kfree(conn);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1007,3 +1067,12 @@ void fsnotify_wait_marks_destroyed(void)
|
|||
flush_delayed_work(&reaper_work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsnotify_wait_marks_destroyed);
|
||||
|
||||
__init void fsnotify_init_connector_caches(void)
|
||||
{
|
||||
fsnotify_mark_connector_cachep = KMEM_CACHE(fsnotify_mark_connector,
|
||||
SLAB_PANIC);
|
||||
fsnotify_inode_mark_connector_cachep = KMEM_CACHE(
|
||||
fsnotify_inode_mark_connector,
|
||||
SLAB_PANIC);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -553,7 +553,7 @@ struct fsnotify_mark_connector {
|
|||
/* Used listing heads to free after srcu period expires */
|
||||
struct fsnotify_mark_connector *destroy_next;
|
||||
};
|
||||
struct hlist_head list;
|
||||
struct hlist_head list; /* List of marks */
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -562,6 +562,9 @@ struct fsnotify_mark_connector {
|
|||
*/
|
||||
struct fsnotify_sb_info {
|
||||
struct fsnotify_mark_connector __rcu *sb_marks;
|
||||
/* List of connectors for inode marks */
|
||||
struct list_head inode_conn_list;
|
||||
spinlock_t list_lock; /* Lock protecting inode_conn_list */
|
||||
/*
|
||||
* Number of inode/mount/sb objects that are being watched in this sb.
|
||||
* Note that inodes objects are currently double-accounted.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue