ANDROID: ABI break fix caused by kernfs_root and kernfs_elem_dir size increase

Adding kernfs_rwsem in kernfs_root breaks the ABI unfortunately
since it changes the size of kernfs_root structure.
To fix the issue, this patch introduces new data structure
kernfs_root_ext which includes kernfs_root with kernfs_rwsem to
avoid increasing kernfs_root'size. It also introduces kernfs_rwsem
wrapper function to reach kernfs_rwsem from kernfs_root to minimize
change.

Bug: 320903885
Bug: 219424218
Bug: 206126556
Signed-off-by: Minchan Kim <minchan@google.com>
Signed-off-by: Suren Baghdasaryan <surenb@google.com>
Change-Id: Iabaad9623e9a101210073db3106c93f06847a8b3
This commit is contained in:
Minchan Kim
2021-12-30 14:34:20 -08:00
committed by Suren Baghdasaryan
parent 1de2411a19
commit 0a649455af
7 changed files with 78 additions and 65 deletions

View File

@@ -33,7 +33,7 @@ static DEFINE_SPINLOCK(kernfs_idr_lock); /* root->ino_idr */
static bool kernfs_active(struct kernfs_node *kn)
{
lockdep_assert_held(&kernfs_root(kn)->kernfs_rwsem);
lockdep_assert_held(kernfs_rwsem(kernfs_root(kn)));
return atomic_read(&kn->active) >= 0;
}
@@ -464,15 +464,15 @@ void kernfs_put_active(struct kernfs_node *kn)
* return after draining is complete.
*/
static void kernfs_drain(struct kernfs_node *kn)
__releases(&kernfs_root(kn)->kernfs_rwsem)
__acquires(&kernfs_root(kn)->kernfs_rwsem)
__releases(kernfs_rwsem(kernfs_root(kn)))
__acquires(kernfs_rwsem(kernfs_root(kn)))
{
struct kernfs_root *root = kernfs_root(kn);
lockdep_assert_held_write(&root->kernfs_rwsem);
lockdep_assert_held_write(kernfs_rwsem(root));
WARN_ON_ONCE(kernfs_active(kn));
up_write(&root->kernfs_rwsem);
up_write(kernfs_rwsem(root));
if (kernfs_lockdep(kn)) {
rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_);
@@ -491,7 +491,7 @@ static void kernfs_drain(struct kernfs_node *kn)
kernfs_drain_open_files(kn);
down_write(&root->kernfs_rwsem);
down_write(kernfs_rwsem(root));
}
/**
@@ -733,7 +733,7 @@ int kernfs_add_one(struct kernfs_node *kn)
bool has_ns;
int ret;
down_write(&root->kernfs_rwsem);
down_write(kernfs_rwsem(root));
ret = -EINVAL;
has_ns = kernfs_ns_enabled(parent);
@@ -764,7 +764,7 @@ int kernfs_add_one(struct kernfs_node *kn)
ps_iattr->ia_mtime = ps_iattr->ia_ctime;
}
up_write(&root->kernfs_rwsem);
up_write(kernfs_rwsem(root));
/*
* Activate the new node unless CREATE_DEACTIVATED is requested.
@@ -778,7 +778,7 @@ int kernfs_add_one(struct kernfs_node *kn)
return 0;
out_unlock:
up_write(&root->kernfs_rwsem);
up_write(kernfs_rwsem(root));
return ret;
}
@@ -799,7 +799,7 @@ static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent,
bool has_ns = kernfs_ns_enabled(parent);
unsigned int hash;
lockdep_assert_held(&kernfs_root(parent)->kernfs_rwsem);
lockdep_assert_held(kernfs_rwsem(kernfs_root(parent)));
if (has_ns != (bool)ns) {
WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n",
@@ -831,7 +831,7 @@ static struct kernfs_node *kernfs_walk_ns(struct kernfs_node *parent,
size_t len;
char *p, *name;
lockdep_assert_held_read(&kernfs_root(parent)->kernfs_rwsem);
lockdep_assert_held_read(kernfs_rwsem(kernfs_root(parent)));
spin_lock_irq(&kernfs_pr_cont_lock);
@@ -871,10 +871,10 @@ struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent,
struct kernfs_node *kn;
struct kernfs_root *root = kernfs_root(parent);
down_read(&root->kernfs_rwsem);
down_read(kernfs_rwsem(root));
kn = kernfs_find_ns(parent, name, ns);
kernfs_get(kn);
up_read(&root->kernfs_rwsem);
up_read(kernfs_rwsem(root));
return kn;
}
@@ -896,10 +896,10 @@ struct kernfs_node *kernfs_walk_and_get_ns(struct kernfs_node *parent,
struct kernfs_node *kn;
struct kernfs_root *root = kernfs_root(parent);
down_read(&root->kernfs_rwsem);
down_read(kernfs_rwsem(root));
kn = kernfs_walk_ns(parent, path, ns);
kernfs_get(kn);
up_read(&root->kernfs_rwsem);
up_read(kernfs_rwsem(root));
return kn;
}
@@ -916,15 +916,17 @@ struct kernfs_node *kernfs_walk_and_get_ns(struct kernfs_node *parent,
struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops,
unsigned int flags, void *priv)
{
struct kernfs_root_ext *root_ext;
struct kernfs_root *root;
struct kernfs_node *kn;
root = kzalloc(sizeof(*root), GFP_KERNEL);
if (!root)
root_ext = kzalloc(sizeof(*root_ext), GFP_KERNEL);
if (!root_ext)
return ERR_PTR(-ENOMEM);
init_rwsem(&root_ext->kernfs_rwsem);
root = &root_ext->root;
idr_init(&root->ino_idr);
init_rwsem(&root->kernfs_rwsem);
INIT_LIST_HEAD(&root->supers);
/*
@@ -1071,12 +1073,12 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
if (parent) {
spin_unlock(&dentry->d_lock);
root = kernfs_root(parent);
down_read(&root->kernfs_rwsem);
down_read(kernfs_rwsem(root));
if (kernfs_dir_changed(parent, dentry)) {
up_read(&root->kernfs_rwsem);
up_read(kernfs_rwsem(root));
return 0;
}
up_read(&root->kernfs_rwsem);
up_read(kernfs_rwsem(root));
} else
spin_unlock(&dentry->d_lock);
@@ -1088,7 +1090,7 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
kn = kernfs_dentry_node(dentry);
root = kernfs_root(kn);
down_read(&root->kernfs_rwsem);
down_read(kernfs_rwsem(root));
/* The kernfs node has been deactivated */
if (!kernfs_active(kn))
@@ -1107,10 +1109,10 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
kernfs_info(dentry->d_sb)->ns != kn->ns)
goto out_bad;
up_read(&root->kernfs_rwsem);
up_read(kernfs_rwsem(root));
return 1;
out_bad:
up_read(&root->kernfs_rwsem);
up_read(kernfs_rwsem(root));
return 0;
}
@@ -1129,7 +1131,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir,
const void *ns = NULL;
root = kernfs_root(parent);
down_read(&root->kernfs_rwsem);
down_read(kernfs_rwsem(root));
if (kernfs_ns_enabled(parent))
ns = kernfs_info(dir->i_sb)->ns;
@@ -1140,7 +1142,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir,
* create a negative.
*/
if (!kernfs_active(kn)) {
up_read(&root->kernfs_rwsem);
up_read(kernfs_rwsem(root));
return NULL;
}
inode = kernfs_get_inode(dir->i_sb, kn);
@@ -1155,7 +1157,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir,
*/
if (!IS_ERR(inode))
kernfs_set_rev(parent, dentry);
up_read(&root->kernfs_rwsem);
up_read(kernfs_rwsem(root));
/* instantiate and hash (possibly negative) dentry */
return d_splice_alias(inode, dentry);
@@ -1278,7 +1280,7 @@ static struct kernfs_node *kernfs_next_descendant_post(struct kernfs_node *pos,
{
struct rb_node *rbn;
lockdep_assert_held_write(&kernfs_root(root)->kernfs_rwsem);
lockdep_assert_held_write(kernfs_rwsem(kernfs_root(root)));
/* if first iteration, visit leftmost descendant which may be root */
if (!pos)
@@ -1315,7 +1317,7 @@ void kernfs_activate(struct kernfs_node *kn)
struct kernfs_node *pos;
struct kernfs_root *root = kernfs_root(kn);
down_write(&root->kernfs_rwsem);
down_write(kernfs_rwsem(root));
pos = NULL;
while ((pos = kernfs_next_descendant_post(pos, kn))) {
@@ -1329,14 +1331,14 @@ void kernfs_activate(struct kernfs_node *kn)
pos->flags |= KERNFS_ACTIVATED;
}
up_write(&root->kernfs_rwsem);
up_write(kernfs_rwsem(root));
}
static void __kernfs_remove(struct kernfs_node *kn)
{
struct kernfs_node *pos;
lockdep_assert_held_write(&kernfs_root(kn)->kernfs_rwsem);
lockdep_assert_held_write(kernfs_rwsem(kernfs_root(kn)));
/*
* Short-circuit if non-root @kn has already finished removal.
@@ -1413,9 +1415,9 @@ void kernfs_remove(struct kernfs_node *kn)
root = kernfs_root(kn);
down_write(&root->kernfs_rwsem);
down_write(kernfs_rwsem(root));
__kernfs_remove(kn);
up_write(&root->kernfs_rwsem);
up_write(kernfs_rwsem(root));
}
/**
@@ -1503,7 +1505,7 @@ bool kernfs_remove_self(struct kernfs_node *kn)
bool ret;
struct kernfs_root *root = kernfs_root(kn);
down_write(&root->kernfs_rwsem);
down_write(kernfs_rwsem(root));
kernfs_break_active_protection(kn);
/*
@@ -1531,9 +1533,9 @@ bool kernfs_remove_self(struct kernfs_node *kn)
atomic_read(&kn->active) == KN_DEACTIVATED_BIAS)
break;
up_write(&root->kernfs_rwsem);
up_write(kernfs_rwsem(root));
schedule();
down_write(&root->kernfs_rwsem);
down_write(kernfs_rwsem(root));
}
finish_wait(waitq, &wait);
WARN_ON_ONCE(!RB_EMPTY_NODE(&kn->rb));
@@ -1546,7 +1548,7 @@ bool kernfs_remove_self(struct kernfs_node *kn)
*/
kernfs_unbreak_active_protection(kn);
up_write(&root->kernfs_rwsem);
up_write(kernfs_rwsem(root));
return ret;
}
@@ -1572,7 +1574,7 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
}
root = kernfs_root(parent);
down_write(&root->kernfs_rwsem);
down_write(kernfs_rwsem(root));
kn = kernfs_find_ns(parent, name, ns);
if (kn) {
@@ -1581,7 +1583,7 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
kernfs_put(kn);
}
up_write(&root->kernfs_rwsem);
up_write(kernfs_rwsem(root));
if (kn)
return 0;
@@ -1609,7 +1611,7 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
return -EINVAL;
root = kernfs_root(kn);
down_write(&root->kernfs_rwsem);
down_write(kernfs_rwsem(root));
error = -ENOENT;
if (!kernfs_active(kn) || !kernfs_active(new_parent) ||
@@ -1663,7 +1665,7 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
error = 0;
out:
up_write(&root->kernfs_rwsem);
up_write(kernfs_rwsem(root));
return error;
}
@@ -1741,7 +1743,7 @@ static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx)
return 0;
root = kernfs_root(parent);
down_read(&root->kernfs_rwsem);
down_read(kernfs_rwsem(root));
if (kernfs_ns_enabled(parent))
ns = kernfs_info(dentry->d_sb)->ns;
@@ -1758,12 +1760,12 @@ static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx)
file->private_data = pos;
kernfs_get(pos);
up_read(&root->kernfs_rwsem);
up_read(kernfs_rwsem(root));
if (!dir_emit(ctx, name, len, ino, type))
return 0;
down_read(&root->kernfs_rwsem);
down_read(kernfs_rwsem(root));
}
up_read(&root->kernfs_rwsem);
up_read(kernfs_rwsem(root));
file->private_data = NULL;
ctx->pos = INT_MAX;
return 0;

View File

@@ -862,7 +862,7 @@ repeat:
root = kernfs_root(kn);
/* kick fsnotify */
down_write(&root->kernfs_rwsem);
down_write(kernfs_rwsem(root));
list_for_each_entry(info, &kernfs_root(kn)->supers, node) {
struct kernfs_node *parent;
@@ -900,7 +900,7 @@ repeat:
iput(inode);
}
up_write(&root->kernfs_rwsem);
up_write(kernfs_rwsem(root));
kernfs_put(kn);
goto repeat;
}

View File

@@ -101,9 +101,9 @@ int kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr)
int ret;
struct kernfs_root *root = kernfs_root(kn);
down_write(&root->kernfs_rwsem);
down_write(kernfs_rwsem(root));
ret = __kernfs_setattr(kn, iattr);
up_write(&root->kernfs_rwsem);
up_write(kernfs_rwsem(root));
return ret;
}
@@ -119,7 +119,7 @@ int kernfs_iop_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
return -EINVAL;
root = kernfs_root(kn);
down_write(&root->kernfs_rwsem);
down_write(kernfs_rwsem(root));
error = setattr_prepare(&init_user_ns, dentry, iattr);
if (error)
goto out;
@@ -132,7 +132,7 @@ int kernfs_iop_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
setattr_copy(&init_user_ns, inode, iattr);
out:
up_write(&root->kernfs_rwsem);
up_write(kernfs_rwsem(root));
return error;
}
@@ -189,12 +189,12 @@ int kernfs_iop_getattr(struct user_namespace *mnt_userns,
struct kernfs_node *kn = inode->i_private;
struct kernfs_root *root = kernfs_root(kn);
down_read(&root->kernfs_rwsem);
down_read(kernfs_rwsem(root));
spin_lock(&inode->i_lock);
kernfs_refresh_inode(kn, inode);
generic_fillattr(&init_user_ns, inode, stat);
spin_unlock(&inode->i_lock);
up_read(&root->kernfs_rwsem);
up_read(kernfs_rwsem(root));
return 0;
}
@@ -287,12 +287,12 @@ int kernfs_iop_permission(struct user_namespace *mnt_userns,
kn = inode->i_private;
root = kernfs_root(kn);
down_read(&root->kernfs_rwsem);
down_read(kernfs_rwsem(root));
spin_lock(&inode->i_lock);
kernfs_refresh_inode(kn, inode);
ret = generic_permission(&init_user_ns, inode, mask);
spin_unlock(&inode->i_lock);
up_read(&root->kernfs_rwsem);
up_read(kernfs_rwsem(root));
return ret;
}

View File

@@ -50,6 +50,14 @@ static inline struct kernfs_root *kernfs_root(struct kernfs_node *kn)
return kn->dir.root;
}
static inline struct rw_semaphore *kernfs_rwsem(struct kernfs_root *root)
{
struct kernfs_root_ext *root_ext;
root_ext = container_of(root, struct kernfs_root_ext, root);
return &root_ext->kernfs_rwsem;
}
/*
* mount.c
*/
@@ -122,7 +130,6 @@ int __kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr);
/*
* dir.c
*/
extern struct rw_semaphore kernfs_rwsem;
extern const struct dentry_operations kernfs_dops;
extern const struct file_operations kernfs_dir_fops;
extern const struct inode_operations kernfs_dir_iops;

View File

@@ -256,9 +256,9 @@ static int kernfs_fill_super(struct super_block *sb, struct kernfs_fs_context *k
sb->s_shrink.seeks = 0;
/* get root inode, initialize and unlock it */
down_read(&kf_root->kernfs_rwsem);
down_read(kernfs_rwsem(kf_root));
inode = kernfs_get_inode(sb, info->root->kn);
up_read(&kf_root->kernfs_rwsem);
up_read(kernfs_rwsem(kf_root));
if (!inode) {
pr_debug("kernfs: could not get root inode\n");
return -ENOMEM;
@@ -346,9 +346,9 @@ int kernfs_get_tree(struct fs_context *fc)
}
sb->s_flags |= SB_ACTIVE;
down_write(&root->kernfs_rwsem);
down_write(kernfs_rwsem(root));
list_add(&info->node, &info->root->supers);
up_write(&root->kernfs_rwsem);
up_write(kernfs_rwsem(root));
}
fc->root = dget(sb->s_root);
@@ -375,9 +375,9 @@ void kernfs_kill_sb(struct super_block *sb)
struct kernfs_super_info *info = kernfs_info(sb);
struct kernfs_root *root = info->root;
down_write(&root->kernfs_rwsem);
down_write(kernfs_rwsem(root));
list_del(&info->node);
up_write(&root->kernfs_rwsem);
up_write(kernfs_rwsem(root));
/*
* Remove the superblock from fs_supers/s_instances

View File

@@ -117,9 +117,9 @@ static int kernfs_getlink(struct inode *inode, char *path)
struct kernfs_root *root = kernfs_root(parent);
int error;
down_read(&root->kernfs_rwsem);
down_read(kernfs_rwsem(root));
error = kernfs_get_target_path(parent, target, path);
up_read(&root->kernfs_rwsem);
up_read(kernfs_rwsem(root));
return error;
}

View File

@@ -206,11 +206,15 @@ struct kernfs_root {
struct list_head supers;
wait_queue_head_t deactivate_waitq;
struct rw_semaphore kernfs_rwsem;
ANDROID_KABI_RESERVE(1);
};
struct kernfs_root_ext {
struct kernfs_root root;
struct rw_semaphore kernfs_rwsem;
};
struct kernfs_open_file {
/* published fields */
struct kernfs_node *kn;