linux_dsm_epyc7002/fs/aufs/hfsnotify.c

289 lines
6.8 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*
* This program, aufs is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* fsnotify for the lower directories
*/
#include "aufs.h"
/* FS_IN_IGNORED is unnecessary */
static const __u32 AuHfsnMask = (FS_MOVED_TO | FS_MOVED_FROM | FS_DELETE
| FS_CREATE | FS_EVENT_ON_CHILD);
static DECLARE_WAIT_QUEUE_HEAD(au_hfsn_wq);
static __cacheline_aligned_in_smp atomic64_t au_hfsn_ifree = ATOMIC64_INIT(0);
static void au_hfsn_free_mark(struct fsnotify_mark *mark)
{
struct au_hnotify *hn = container_of(mark, struct au_hnotify,
hn_mark);
/* AuDbg("here\n"); */
au_cache_free_hnotify(hn);
smp_mb__before_atomic(); /* for atomic64_dec */
if (atomic64_dec_and_test(&au_hfsn_ifree))
wake_up(&au_hfsn_wq);
}
static int au_hfsn_alloc(struct au_hinode *hinode)
{
int err;
struct au_hnotify *hn;
struct super_block *sb;
struct au_branch *br;
struct fsnotify_mark *mark;
aufs_bindex_t bindex;
hn = hinode->hi_notify;
sb = hn->hn_aufs_inode->i_sb;
bindex = au_br_index(sb, hinode->hi_id);
br = au_sbr(sb, bindex);
AuDebugOn(!br->br_hfsn);
mark = &hn->hn_mark;
fsnotify_init_mark(mark, br->br_hfsn->hfsn_group);
mark->mask = AuHfsnMask;
/*
* by udba rename or rmdir, aufs assign a new inode to the known
* h_inode, so specify 1 to allow dups.
*/
lockdep_off();
err = fsnotify_add_inode_mark(mark, hinode->hi_inode, /*allow_dups*/1);
lockdep_on();
return err;
}
static int au_hfsn_free(struct au_hinode *hinode, struct au_hnotify *hn)
{
struct fsnotify_mark *mark;
unsigned long long ull;
struct fsnotify_group *group;
ull = atomic64_inc_return(&au_hfsn_ifree);
BUG_ON(!ull);
mark = &hn->hn_mark;
spin_lock(&mark->lock);
group = mark->group;
fsnotify_get_group(group);
spin_unlock(&mark->lock);
lockdep_off();
fsnotify_destroy_mark(mark, group);
fsnotify_put_mark(mark);
fsnotify_put_group(group);
lockdep_on();
/* free hn by myself */
return 0;
}
/* ---------------------------------------------------------------------- */
static void au_hfsn_ctl(struct au_hinode *hinode, int do_set)
{
struct fsnotify_mark *mark;
mark = &hinode->hi_notify->hn_mark;
spin_lock(&mark->lock);
if (do_set) {
AuDebugOn(mark->mask & AuHfsnMask);
mark->mask |= AuHfsnMask;
} else {
AuDebugOn(!(mark->mask & AuHfsnMask));
mark->mask &= ~AuHfsnMask;
}
spin_unlock(&mark->lock);
/* fsnotify_recalc_inode_mask(hinode->hi_inode); */
}
/* ---------------------------------------------------------------------- */
/* #define AuDbgHnotify */
#ifdef AuDbgHnotify
static char *au_hfsn_name(u32 mask)
{
#ifdef CONFIG_AUFS_DEBUG
#define test_ret(flag) \
do { \
if (mask & flag) \
return #flag; \
} while (0)
test_ret(FS_ACCESS);
test_ret(FS_MODIFY);
test_ret(FS_ATTRIB);
test_ret(FS_CLOSE_WRITE);
test_ret(FS_CLOSE_NOWRITE);
test_ret(FS_OPEN);
test_ret(FS_MOVED_FROM);
test_ret(FS_MOVED_TO);
test_ret(FS_CREATE);
test_ret(FS_DELETE);
test_ret(FS_DELETE_SELF);
test_ret(FS_MOVE_SELF);
test_ret(FS_UNMOUNT);
test_ret(FS_Q_OVERFLOW);
test_ret(FS_IN_IGNORED);
test_ret(FS_ISDIR);
test_ret(FS_IN_ONESHOT);
test_ret(FS_EVENT_ON_CHILD);
return "";
#undef test_ret
#else
return "??";
#endif
}
#endif
/* ---------------------------------------------------------------------- */
static void au_hfsn_free_group(struct fsnotify_group *group)
{
struct au_br_hfsnotify *hfsn = group->private;
/* AuDbg("here\n"); */
au_kfree_try_rcu(hfsn);
}
static int au_hfsn_handle_event(struct fsnotify_group *group,
u32 mask, const void *data, int data_type,
struct inode *dir,
const struct qstr *file_name, u32 cookie,
struct fsnotify_iter_info *iter_info)
{
int err;
struct au_hnotify *hnotify;
struct inode *h_dir, *h_inode;
struct fsnotify_mark *inode_mark;
AuDebugOn(data_type != FSNOTIFY_EVENT_INODE);
err = 0;
/* if FS_UNMOUNT happens, there must be another bug */
AuDebugOn(mask & FS_UNMOUNT);
if (mask & (FS_IN_IGNORED | FS_UNMOUNT))
goto out;
h_dir = dir;
h_inode = NULL;
#ifdef AuDbgHnotify
au_debug_on();
if (1 || h_child_qstr.len != sizeof(AUFS_XINO_FNAME) - 1
|| strncmp(h_child_qstr.name, AUFS_XINO_FNAME, h_child_qstr.len)) {
AuDbg("i%lu, mask 0x%x %s, hcname %.*s, hi%lu\n",
h_dir->i_ino, mask, au_hfsn_name(mask),
AuLNPair(&h_child_qstr), h_inode ? h_inode->i_ino : 0);
/* WARN_ON(1); */
}
au_debug_off();
#endif
inode_mark = fsnotify_iter_inode_mark(iter_info);
AuDebugOn(!inode_mark);
hnotify = container_of(inode_mark, struct au_hnotify, hn_mark);
err = au_hnotify(h_dir, hnotify, mask, file_name, h_inode);
out:
return err;
}
static struct fsnotify_ops au_hfsn_ops = {
.handle_event = au_hfsn_handle_event,
.free_group_priv = au_hfsn_free_group,
.free_mark = au_hfsn_free_mark
};
/* ---------------------------------------------------------------------- */
static void au_hfsn_fin_br(struct au_branch *br)
{
struct au_br_hfsnotify *hfsn;
hfsn = br->br_hfsn;
if (hfsn) {
lockdep_off();
fsnotify_put_group(hfsn->hfsn_group);
lockdep_on();
}
}
static int au_hfsn_init_br(struct au_branch *br, int perm)
{
int err;
struct fsnotify_group *group;
struct au_br_hfsnotify *hfsn;
err = 0;
br->br_hfsn = NULL;
if (!au_br_hnotifyable(perm))
goto out;
err = -ENOMEM;
hfsn = kmalloc(sizeof(*hfsn), GFP_NOFS);
if (unlikely(!hfsn))
goto out;
err = 0;
group = fsnotify_alloc_group(&au_hfsn_ops);
if (IS_ERR(group)) {
err = PTR_ERR(group);
pr_err("fsnotify_alloc_group() failed, %d\n", err);
goto out_hfsn;
}
group->private = hfsn;
hfsn->hfsn_group = group;
br->br_hfsn = hfsn;
goto out; /* success */
out_hfsn:
au_kfree_try_rcu(hfsn);
out:
return err;
}
static int au_hfsn_reset_br(unsigned int udba, struct au_branch *br, int perm)
{
int err;
err = 0;
if (!br->br_hfsn)
err = au_hfsn_init_br(br, perm);
return err;
}
/* ---------------------------------------------------------------------- */
static void au_hfsn_fin(void)
{
AuDbg("au_hfsn_ifree %lld\n", (long long)atomic64_read(&au_hfsn_ifree));
wait_event(au_hfsn_wq, !atomic64_read(&au_hfsn_ifree));
}
const struct au_hnotify_op au_hnotify_op = {
.ctl = au_hfsn_ctl,
.alloc = au_hfsn_alloc,
.free = au_hfsn_free,
.fin = au_hfsn_fin,
.reset_br = au_hfsn_reset_br,
.fin_br = au_hfsn_fin_br,
.init_br = au_hfsn_init_br
};