mirror of https://gitee.com/openkylin/linux.git
VFS/namei: make the use of touch_atime() in get_link() RCU-safe.
touch_atime is not RCU-safe, and so cannot be called on an RCU walk. However, in situations where RCU-walk makes a difference, the symlink will likely to accessed much more often than it is useful to update the atime. So split out the test of "Does the atime actually need to be updated" into atime_needs_update(), and have get_link() unlazy if it finds that it will need to do that update. Signed-off-by: NeilBrown <neilb@suse.de> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
bc40aee053
commit
8fa9dd2466
50
fs/inode.c
50
fs/inode.c
|
@ -1585,36 +1585,47 @@ static int update_time(struct inode *inode, struct timespec *time, int flags)
|
||||||
* This function automatically handles read only file systems and media,
|
* This function automatically handles read only file systems and media,
|
||||||
* as well as the "noatime" flag and inode specific "noatime" markers.
|
* as well as the "noatime" flag and inode specific "noatime" markers.
|
||||||
*/
|
*/
|
||||||
|
bool atime_needs_update(const struct path *path, struct inode *inode)
|
||||||
|
{
|
||||||
|
struct vfsmount *mnt = path->mnt;
|
||||||
|
struct timespec now;
|
||||||
|
|
||||||
|
if (inode->i_flags & S_NOATIME)
|
||||||
|
return false;
|
||||||
|
if (IS_NOATIME(inode))
|
||||||
|
return false;
|
||||||
|
if ((inode->i_sb->s_flags & MS_NODIRATIME) && S_ISDIR(inode->i_mode))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (mnt->mnt_flags & MNT_NOATIME)
|
||||||
|
return false;
|
||||||
|
if ((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
now = current_fs_time(inode->i_sb);
|
||||||
|
|
||||||
|
if (!relatime_need_update(mnt, inode, now))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (timespec_equal(&inode->i_atime, &now))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void touch_atime(const struct path *path)
|
void touch_atime(const struct path *path)
|
||||||
{
|
{
|
||||||
struct vfsmount *mnt = path->mnt;
|
struct vfsmount *mnt = path->mnt;
|
||||||
struct inode *inode = d_inode(path->dentry);
|
struct inode *inode = d_inode(path->dentry);
|
||||||
struct timespec now;
|
struct timespec now;
|
||||||
|
|
||||||
if (inode->i_flags & S_NOATIME)
|
if (!atime_needs_update(path, inode))
|
||||||
return;
|
|
||||||
if (IS_NOATIME(inode))
|
|
||||||
return;
|
|
||||||
if ((inode->i_sb->s_flags & MS_NODIRATIME) && S_ISDIR(inode->i_mode))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (mnt->mnt_flags & MNT_NOATIME)
|
|
||||||
return;
|
|
||||||
if ((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode))
|
|
||||||
return;
|
|
||||||
|
|
||||||
now = current_fs_time(inode->i_sb);
|
|
||||||
|
|
||||||
if (!relatime_need_update(mnt, inode, now))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (timespec_equal(&inode->i_atime, &now))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!sb_start_write_trylock(inode->i_sb))
|
if (!sb_start_write_trylock(inode->i_sb))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (__mnt_want_write(mnt))
|
if (__mnt_want_write(mnt) != 0)
|
||||||
goto skip_update;
|
goto skip_update;
|
||||||
/*
|
/*
|
||||||
* File systems can error out when updating inodes if they need to
|
* File systems can error out when updating inodes if they need to
|
||||||
|
@ -1625,6 +1636,7 @@ void touch_atime(const struct path *path)
|
||||||
* We may also fail on filesystems that have the ability to make parts
|
* We may also fail on filesystems that have the ability to make parts
|
||||||
* of the fs read only, e.g. subvolumes in Btrfs.
|
* of the fs read only, e.g. subvolumes in Btrfs.
|
||||||
*/
|
*/
|
||||||
|
now = current_fs_time(inode->i_sb);
|
||||||
update_time(inode, &now, S_ATIME);
|
update_time(inode, &now, S_ATIME);
|
||||||
__mnt_drop_write(mnt);
|
__mnt_drop_write(mnt);
|
||||||
skip_update:
|
skip_update:
|
||||||
|
|
12
fs/namei.c
12
fs/namei.c
|
@ -966,13 +966,19 @@ const char *get_link(struct nameidata *nd)
|
||||||
int error;
|
int error;
|
||||||
const char *res;
|
const char *res;
|
||||||
|
|
||||||
|
if (!(nd->flags & LOOKUP_RCU)) {
|
||||||
|
touch_atime(&last->link);
|
||||||
|
cond_resched();
|
||||||
|
} else if (atime_needs_update(&last->link, inode)) {
|
||||||
|
if (unlikely(unlazy_walk(nd, NULL, 0)))
|
||||||
|
return ERR_PTR(-ECHILD);
|
||||||
|
touch_atime(&last->link);
|
||||||
|
}
|
||||||
|
|
||||||
if (nd->flags & LOOKUP_RCU) {
|
if (nd->flags & LOOKUP_RCU) {
|
||||||
if (unlikely(unlazy_walk(nd, NULL, 0)))
|
if (unlikely(unlazy_walk(nd, NULL, 0)))
|
||||||
return ERR_PTR(-ECHILD);
|
return ERR_PTR(-ECHILD);
|
||||||
}
|
}
|
||||||
cond_resched();
|
|
||||||
|
|
||||||
touch_atime(&last->link);
|
|
||||||
|
|
||||||
error = security_inode_follow_link(dentry, inode,
|
error = security_inode_follow_link(dentry, inode,
|
||||||
nd->flags & LOOKUP_RCU);
|
nd->flags & LOOKUP_RCU);
|
||||||
|
|
|
@ -1880,6 +1880,7 @@ enum file_time_flags {
|
||||||
S_VERSION = 8,
|
S_VERSION = 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern bool atime_needs_update(const struct path *, struct inode *);
|
||||||
extern void touch_atime(const struct path *);
|
extern void touch_atime(const struct path *);
|
||||||
static inline void file_accessed(struct file *file)
|
static inline void file_accessed(struct file *file)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue