Skip to content

Commit

Permalink
Merge branch 'vfs.file' of gitolite.kernel.org:pub/scm/linux/kernel/g…
Browse files Browse the repository at this point in the history
…it/vfs/vfs into slab/for-6.12/kmem_cache_args

Merge prerequisities from the vfs git tree for the following series that
introduces kmem_cache_args. The vfs.file branch includes the addition of
kmem_cache_create_rcu() which was needed in vfs for the filp cache
optimization. The following series refactors this code.
  • Loading branch information
tehcaster committed Sep 10, 2024
2 parents 5be63fc + 0f389ad commit e16f4f7
Show file tree
Hide file tree
Showing 15 changed files with 344 additions and 133 deletions.
6 changes: 6 additions & 0 deletions drivers/net/tun.c
Original file line number Diff line number Diff line change
Expand Up @@ -3451,6 +3451,12 @@ static int tun_chr_fasync(int fd, struct file *file, int on)
struct tun_file *tfile = file->private_data;
int ret;

if (on) {
ret = file_f_owner_allocate(file);
if (ret)
goto out;
}

if ((ret = fasync_helper(fd, file, on, &tfile->fasync)) < 0)
goto out;

Expand Down
6 changes: 6 additions & 0 deletions drivers/tty/tty_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -2225,6 +2225,12 @@ static int __tty_fasync(int fd, struct file *filp, int on)
if (tty_paranoia_check(tty, file_inode(filp), "tty_fasync"))
goto out;

if (on) {
retval = file_f_owner_allocate(filp);
if (retval)
goto out;
}

retval = fasync_helper(fd, filp, on, &tty->fasync);
if (retval <= 0)
goto out;
Expand Down
166 changes: 132 additions & 34 deletions fs/fcntl.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
#include <asm/siginfo.h>
#include <linux/uaccess.h>

#include "internal.h"

#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME)

static int setfl(int fd, struct file * filp, unsigned int arg)
Expand Down Expand Up @@ -87,22 +89,64 @@ static int setfl(int fd, struct file * filp, unsigned int arg)
return error;
}

/*
* Allocate an file->f_owner struct if it doesn't exist, handling racing
* allocations correctly.
*/
int file_f_owner_allocate(struct file *file)
{
struct fown_struct *f_owner;

f_owner = file_f_owner(file);
if (f_owner)
return 0;

f_owner = kzalloc(sizeof(struct fown_struct), GFP_KERNEL);
if (!f_owner)
return -ENOMEM;

rwlock_init(&f_owner->lock);
f_owner->file = file;
/* If someone else raced us, drop our allocation. */
if (unlikely(cmpxchg(&file->f_owner, NULL, f_owner)))
kfree(f_owner);
return 0;
}
EXPORT_SYMBOL(file_f_owner_allocate);

void file_f_owner_release(struct file *file)
{
struct fown_struct *f_owner;

f_owner = file_f_owner(file);
if (f_owner) {
put_pid(f_owner->pid);
kfree(f_owner);
}
}

static void f_modown(struct file *filp, struct pid *pid, enum pid_type type,
int force)
{
write_lock_irq(&filp->f_owner.lock);
if (force || !filp->f_owner.pid) {
put_pid(filp->f_owner.pid);
filp->f_owner.pid = get_pid(pid);
filp->f_owner.pid_type = type;
struct fown_struct *f_owner;

f_owner = file_f_owner(filp);
if (WARN_ON_ONCE(!f_owner))
return;

write_lock_irq(&f_owner->lock);
if (force || !f_owner->pid) {
put_pid(f_owner->pid);
f_owner->pid = get_pid(pid);
f_owner->pid_type = type;

if (pid) {
const struct cred *cred = current_cred();
filp->f_owner.uid = cred->uid;
filp->f_owner.euid = cred->euid;
f_owner->uid = cred->uid;
f_owner->euid = cred->euid;
}
}
write_unlock_irq(&filp->f_owner.lock);
write_unlock_irq(&f_owner->lock);
}

void __f_setown(struct file *filp, struct pid *pid, enum pid_type type,
Expand All @@ -119,6 +163,8 @@ int f_setown(struct file *filp, int who, int force)
struct pid *pid = NULL;
int ret = 0;

might_sleep();

type = PIDTYPE_TGID;
if (who < 0) {
/* avoid overflow below */
Expand All @@ -129,6 +175,10 @@ int f_setown(struct file *filp, int who, int force)
who = -who;
}

ret = file_f_owner_allocate(filp);
if (ret)
return ret;

rcu_read_lock();
if (who) {
pid = find_vpid(who);
Expand All @@ -152,16 +202,21 @@ void f_delown(struct file *filp)
pid_t f_getown(struct file *filp)
{
pid_t pid = 0;
struct fown_struct *f_owner;

f_owner = file_f_owner(filp);
if (!f_owner)
return pid;

read_lock_irq(&filp->f_owner.lock);
read_lock_irq(&f_owner->lock);
rcu_read_lock();
if (pid_task(filp->f_owner.pid, filp->f_owner.pid_type)) {
pid = pid_vnr(filp->f_owner.pid);
if (filp->f_owner.pid_type == PIDTYPE_PGID)
if (pid_task(f_owner->pid, f_owner->pid_type)) {
pid = pid_vnr(f_owner->pid);
if (f_owner->pid_type == PIDTYPE_PGID)
pid = -pid;
}
rcu_read_unlock();
read_unlock_irq(&filp->f_owner.lock);
read_unlock_irq(&f_owner->lock);
return pid;
}

Expand Down Expand Up @@ -194,6 +249,10 @@ static int f_setown_ex(struct file *filp, unsigned long arg)
return -EINVAL;
}

ret = file_f_owner_allocate(filp);
if (ret)
return ret;

rcu_read_lock();
pid = find_vpid(owner.pid);
if (owner.pid && !pid)
Expand All @@ -210,13 +269,20 @@ static int f_getown_ex(struct file *filp, unsigned long arg)
struct f_owner_ex __user *owner_p = (void __user *)arg;
struct f_owner_ex owner = {};
int ret = 0;
struct fown_struct *f_owner;
enum pid_type pid_type = PIDTYPE_PID;

read_lock_irq(&filp->f_owner.lock);
rcu_read_lock();
if (pid_task(filp->f_owner.pid, filp->f_owner.pid_type))
owner.pid = pid_vnr(filp->f_owner.pid);
rcu_read_unlock();
switch (filp->f_owner.pid_type) {
f_owner = file_f_owner(filp);
if (f_owner) {
read_lock_irq(&f_owner->lock);
rcu_read_lock();
if (pid_task(f_owner->pid, f_owner->pid_type))
owner.pid = pid_vnr(f_owner->pid);
rcu_read_unlock();
pid_type = f_owner->pid_type;
}

switch (pid_type) {
case PIDTYPE_PID:
owner.type = F_OWNER_TID;
break;
Expand All @@ -234,7 +300,8 @@ static int f_getown_ex(struct file *filp, unsigned long arg)
ret = -EINVAL;
break;
}
read_unlock_irq(&filp->f_owner.lock);
if (f_owner)
read_unlock_irq(&f_owner->lock);

if (!ret) {
ret = copy_to_user(owner_p, &owner, sizeof(owner));
Expand All @@ -248,14 +315,18 @@ static int f_getown_ex(struct file *filp, unsigned long arg)
static int f_getowner_uids(struct file *filp, unsigned long arg)
{
struct user_namespace *user_ns = current_user_ns();
struct fown_struct *f_owner;
uid_t __user *dst = (void __user *)arg;
uid_t src[2];
uid_t src[2] = {0, 0};
int err;

read_lock_irq(&filp->f_owner.lock);
src[0] = from_kuid(user_ns, filp->f_owner.uid);
src[1] = from_kuid(user_ns, filp->f_owner.euid);
read_unlock_irq(&filp->f_owner.lock);
f_owner = file_f_owner(filp);
if (f_owner) {
read_lock_irq(&f_owner->lock);
src[0] = from_kuid(user_ns, f_owner->uid);
src[1] = from_kuid(user_ns, f_owner->euid);
read_unlock_irq(&f_owner->lock);
}

err = put_user(src[0], &dst[0]);
err |= put_user(src[1], &dst[1]);
Expand Down Expand Up @@ -343,6 +414,30 @@ static long f_dupfd_query(int fd, struct file *filp)
return f.file == filp;
}

static int f_owner_sig(struct file *filp, int signum, bool setsig)
{
int ret = 0;
struct fown_struct *f_owner;

might_sleep();

if (setsig) {
if (!valid_signal(signum))
return -EINVAL;

ret = file_f_owner_allocate(filp);
if (ret)
return ret;
}

f_owner = file_f_owner(filp);
if (setsig)
f_owner->signum = signum;
else if (f_owner)
ret = f_owner->signum;
return ret;
}

static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
struct file *filp)
{
Expand Down Expand Up @@ -421,15 +516,10 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
err = f_getowner_uids(filp, arg);
break;
case F_GETSIG:
err = filp->f_owner.signum;
err = f_owner_sig(filp, 0, false);
break;
case F_SETSIG:
/* arg == 0 restores default behaviour. */
if (!valid_signal(argi)) {
break;
}
err = 0;
filp->f_owner.signum = argi;
err = f_owner_sig(filp, argi, true);
break;
case F_GETLEASE:
err = fcntl_getlease(filp);
Expand Down Expand Up @@ -844,14 +934,19 @@ static void send_sigurg_to_task(struct task_struct *p,
do_send_sig_info(SIGURG, SEND_SIG_PRIV, p, type);
}

int send_sigurg(struct fown_struct *fown)
int send_sigurg(struct file *file)
{
struct fown_struct *fown;
struct task_struct *p;
enum pid_type type;
struct pid *pid;
unsigned long flags;
int ret = 0;

fown = file_f_owner(file);
if (!fown)
return 0;

read_lock_irqsave(&fown->lock, flags);

type = fown->pid_type;
Expand Down Expand Up @@ -1027,13 +1122,16 @@ static void kill_fasync_rcu(struct fasync_struct *fa, int sig, int band)
}
read_lock_irqsave(&fa->fa_lock, flags);
if (fa->fa_file) {
fown = &fa->fa_file->f_owner;
fown = file_f_owner(fa->fa_file);
if (!fown)
goto next;
/* Don't send SIGURG to processes which have not set a
queued signum: SIGURG has its own default signalling
mechanism. */
if (!(sig == SIGURG && fown->signum == 0))
send_sigio(fown, fa->fa_fd, band);
}
next:
read_unlock_irqrestore(&fa->fa_lock, flags);
fa = rcu_dereference(fa->fa_next);
}
Expand Down
9 changes: 4 additions & 5 deletions fs/file_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,6 @@ static int init_file(struct file *f, int flags, const struct cred *cred)
return error;
}

rwlock_init(&f->f_owner.lock);
spin_lock_init(&f->f_lock);
mutex_init(&f->f_pos_lock);
f->f_flags = flags;
Expand Down Expand Up @@ -425,7 +424,7 @@ static void __fput(struct file *file)
cdev_put(inode->i_cdev);
}
fops_put(file->f_op);
put_pid(file->f_owner.pid);
file_f_owner_release(file);
put_file_access(file);
dput(dentry);
if (unlikely(mode & FMODE_NEED_UNMOUNT))
Expand Down Expand Up @@ -512,9 +511,9 @@ EXPORT_SYMBOL(__fput_sync);

void __init files_init(void)
{
filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0,
SLAB_TYPESAFE_BY_RCU | SLAB_HWCACHE_ALIGN |
SLAB_PANIC | SLAB_ACCOUNT, NULL);
filp_cachep = kmem_cache_create_rcu("filp", sizeof(struct file),
offsetof(struct file, f_freeptr),
SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_ACCOUNT);
percpu_counter_init(&nr_files, 0, GFP_KERNEL);
}

Expand Down
1 change: 1 addition & 0 deletions fs/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -337,3 +337,4 @@ static inline bool path_mounted(const struct path *path)
{
return path->mnt->mnt_root == path->dentry;
}
void file_f_owner_release(struct file *file);
6 changes: 5 additions & 1 deletion fs/locks.c
Original file line number Diff line number Diff line change
Expand Up @@ -1451,7 +1451,7 @@ int lease_modify(struct file_lease *fl, int arg, struct list_head *dispose)
struct file *filp = fl->c.flc_file;

f_delown(filp);
filp->f_owner.signum = 0;
file_f_owner(filp)->signum = 0;
fasync_helper(0, fl->c.flc_file, 0, &fl->fl_fasync);
if (fl->fl_fasync != NULL) {
printk(KERN_ERR "locks_delete_lock: fasync == %p\n", fl->fl_fasync);
Expand Down Expand Up @@ -1783,6 +1783,10 @@ generic_add_lease(struct file *filp, int arg, struct file_lease **flp, void **pr
lease = *flp;
trace_generic_add_lease(inode, lease);

error = file_f_owner_allocate(filp);
if (error)
return error;

/* Note that arg is never F_UNLCK here */
ctx = locks_get_lock_context(inode, arg);
if (!ctx)
Expand Down
6 changes: 5 additions & 1 deletion fs/notify/dnotify/dnotify.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ static int dnotify_handle_event(struct fsnotify_mark *inode_mark, u32 mask,
prev = &dn->dn_next;
continue;
}
fown = &dn->dn_filp->f_owner;
fown = file_f_owner(dn->dn_filp);
send_sigio(fown, dn->dn_fd, POLL_MSG);
if (dn->dn_mask & FS_DN_MULTISHOT)
prev = &dn->dn_next;
Expand Down Expand Up @@ -316,6 +316,10 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned int arg)
goto out_err;
}

error = file_f_owner_allocate(filp);
if (error)
goto out_err;

/* set up the new_fsn_mark and new_dn_mark */
new_fsn_mark = &new_dn_mark->fsn_mark;
fsnotify_init_mark(new_fsn_mark, dnotify_group);
Expand Down
Loading

0 comments on commit e16f4f7

Please sign in to comment.