Skip to content

Commit 1934b21

Browse files
committed
file: reclaim 24 bytes from f_owner
We do embedd struct fown_struct into struct file letting it take up 32 bytes in total. We could tweak struct fown_struct to be more compact but really it shouldn't even be embedded in struct file in the first place. Instead, actual users of struct fown_struct should allocate the struct on demand. This frees up 24 bytes in struct file. That will have some potentially user-visible changes for the ownership fcntl()s. Some of them can now fail due to allocation failures. Practically, that probably will almost never happen as the allocations are small and they only happen once per file. The fown_struct is used during kill_fasync() which is used by e.g., pipes to generate a SIGIO signal. Sending of such signals is conditional on userspace having set an owner for the file using one of the F_OWNER fcntl()s. Such users will be unaffected if struct fown_struct is allocated during the fcntl() call. There are a few subsystems that call __f_setown() expecting file->f_owner to be allocated: (1) tun devices file->f_op->fasync::tun_chr_fasync() -> __f_setown() There are no callers of tun_chr_fasync(). (2) tty devices file->f_op->fasync::tty_fasync() -> __tty_fasync() -> __f_setown() tty_fasync() has no additional callers but __tty_fasync() has. Note that __tty_fasync() only calls __f_setown() if the @on argument is true. It's called from: file->f_op->release::tty_release() -> tty_release() -> __tty_fasync() -> __f_setown() tty_release() calls __tty_fasync() with @on false => __f_setown() is never called from tty_release(). => All callers of tty_release() are safe as well. file->f_op->release::tty_open() -> tty_release() -> __tty_fasync() -> __f_setown() __tty_hangup() calls __tty_fasync() with @on false => __f_setown() is never called from tty_release(). => All callers of __tty_hangup() are safe as well. From the callchains it's obvious that (1) and (2) end up getting called via file->f_op->fasync(). That can happen either through the F_SETFL fcntl() with the FASYNC flag raised or via the FIOASYNC ioctl(). If FASYNC is requested and the file isn't already FASYNC then file->f_op->fasync() is called with @on true which ends up causing both (1) and (2) to call __f_setown(). (1) and (2) are the only subsystems that call __f_setown() from the file->f_op->fasync() handler. So both (1) and (2) have been updated to allocate a struct fown_struct prior to calling fasync_helper() to register with the fasync infrastructure. That's safe as they both call fasync_helper() which also does allocations if @on is true. The other interesting case are file leases: (3) file leases lease_manager_ops->lm_setup::lease_setup() -> __f_setown() Which in turn is called from: generic_add_lease() -> lease_manager_ops->lm_setup::lease_setup() -> __f_setown() So here again we can simply make generic_add_lease() allocate struct fown_struct prior to the lease_manager_ops->lm_setup::lease_setup() which happens under a spinlock. With that the two remaining subsystems that call __f_setown() are: (4) dnotify (5) sockets Both have their own custom ioctls to set struct fown_struct and both have been converted to allocate a struct fown_struct on demand from their respective ioctls. Interactions with O_PATH are fine as well e.g., when opening a /dev/tty as O_PATH then no file->f_op->open() happens thus no file->f_owner is allocated. That's fine as no file operation will be set for those and the device has never been opened. fcntl()s called on such things will just allocate a ->f_owner on demand. Although I have zero idea why'd you care about f_owner on an O_PATH fd. Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: Christian Brauner <[email protected]>
1 parent 47ac09b commit 1934b21

File tree

11 files changed

+168
-43
lines changed

11 files changed

+168
-43
lines changed

drivers/net/tun.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3451,6 +3451,12 @@ static int tun_chr_fasync(int fd, struct file *file, int on)
34513451
struct tun_file *tfile = file->private_data;
34523452
int ret;
34533453

3454+
if (on) {
3455+
ret = file_f_owner_allocate(file);
3456+
if (ret)
3457+
goto out;
3458+
}
3459+
34543460
if ((ret = fasync_helper(fd, file, on, &tfile->fasync)) < 0)
34553461
goto out;
34563462

drivers/tty/tty_io.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2225,6 +2225,12 @@ static int __tty_fasync(int fd, struct file *filp, int on)
22252225
if (tty_paranoia_check(tty, file_inode(filp), "tty_fasync"))
22262226
goto out;
22272227

2228+
if (on) {
2229+
retval = file_f_owner_allocate(filp);
2230+
if (retval)
2231+
goto out;
2232+
}
2233+
22282234
retval = fasync_helper(fd, filp, on, &tty->fasync);
22292235
if (retval <= 0)
22302236
goto out;

fs/fcntl.c

Lines changed: 132 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
#include <asm/siginfo.h>
3434
#include <linux/uaccess.h>
3535

36+
#include "internal.h"
37+
3638
#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME)
3739

3840
static int setfl(int fd, struct file * filp, unsigned int arg)
@@ -87,22 +89,64 @@ static int setfl(int fd, struct file * filp, unsigned int arg)
8789
return error;
8890
}
8991

92+
/*
93+
* Allocate an file->f_owner struct if it doesn't exist, handling racing
94+
* allocations correctly.
95+
*/
96+
int file_f_owner_allocate(struct file *file)
97+
{
98+
struct fown_struct *f_owner;
99+
100+
f_owner = file_f_owner(file);
101+
if (f_owner)
102+
return 0;
103+
104+
f_owner = kzalloc(sizeof(struct fown_struct), GFP_KERNEL);
105+
if (!f_owner)
106+
return -ENOMEM;
107+
108+
rwlock_init(&f_owner->lock);
109+
f_owner->file = file;
110+
/* If someone else raced us, drop our allocation. */
111+
if (unlikely(cmpxchg(&file->f_owner, NULL, f_owner)))
112+
kfree(f_owner);
113+
return 0;
114+
}
115+
EXPORT_SYMBOL(file_f_owner_allocate);
116+
117+
void file_f_owner_release(struct file *file)
118+
{
119+
struct fown_struct *f_owner;
120+
121+
f_owner = file_f_owner(file);
122+
if (f_owner) {
123+
put_pid(f_owner->pid);
124+
kfree(f_owner);
125+
}
126+
}
127+
90128
static void f_modown(struct file *filp, struct pid *pid, enum pid_type type,
91129
int force)
92130
{
93-
write_lock_irq(&filp->f_owner.lock);
94-
if (force || !filp->f_owner.pid) {
95-
put_pid(filp->f_owner.pid);
96-
filp->f_owner.pid = get_pid(pid);
97-
filp->f_owner.pid_type = type;
131+
struct fown_struct *f_owner;
132+
133+
f_owner = file_f_owner(filp);
134+
if (WARN_ON_ONCE(!f_owner))
135+
return;
136+
137+
write_lock_irq(&f_owner->lock);
138+
if (force || !f_owner->pid) {
139+
put_pid(f_owner->pid);
140+
f_owner->pid = get_pid(pid);
141+
f_owner->pid_type = type;
98142

99143
if (pid) {
100144
const struct cred *cred = current_cred();
101-
filp->f_owner.uid = cred->uid;
102-
filp->f_owner.euid = cred->euid;
145+
f_owner->uid = cred->uid;
146+
f_owner->euid = cred->euid;
103147
}
104148
}
105-
write_unlock_irq(&filp->f_owner.lock);
149+
write_unlock_irq(&f_owner->lock);
106150
}
107151

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

166+
might_sleep();
167+
122168
type = PIDTYPE_TGID;
123169
if (who < 0) {
124170
/* avoid overflow below */
@@ -129,6 +175,10 @@ int f_setown(struct file *filp, int who, int force)
129175
who = -who;
130176
}
131177

178+
ret = file_f_owner_allocate(filp);
179+
if (ret)
180+
return ret;
181+
132182
rcu_read_lock();
133183
if (who) {
134184
pid = find_vpid(who);
@@ -152,16 +202,21 @@ void f_delown(struct file *filp)
152202
pid_t f_getown(struct file *filp)
153203
{
154204
pid_t pid = 0;
205+
struct fown_struct *f_owner;
206+
207+
f_owner = file_f_owner(filp);
208+
if (!f_owner)
209+
return pid;
155210

156-
read_lock_irq(&filp->f_owner.lock);
211+
read_lock_irq(&f_owner->lock);
157212
rcu_read_lock();
158-
if (pid_task(filp->f_owner.pid, filp->f_owner.pid_type)) {
159-
pid = pid_vnr(filp->f_owner.pid);
160-
if (filp->f_owner.pid_type == PIDTYPE_PGID)
213+
if (pid_task(f_owner->pid, f_owner->pid_type)) {
214+
pid = pid_vnr(f_owner->pid);
215+
if (f_owner->pid_type == PIDTYPE_PGID)
161216
pid = -pid;
162217
}
163218
rcu_read_unlock();
164-
read_unlock_irq(&filp->f_owner.lock);
219+
read_unlock_irq(&f_owner->lock);
165220
return pid;
166221
}
167222

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

252+
ret = file_f_owner_allocate(filp);
253+
if (ret)
254+
return ret;
255+
197256
rcu_read_lock();
198257
pid = find_vpid(owner.pid);
199258
if (owner.pid && !pid)
@@ -210,13 +269,20 @@ static int f_getown_ex(struct file *filp, unsigned long arg)
210269
struct f_owner_ex __user *owner_p = (void __user *)arg;
211270
struct f_owner_ex owner = {};
212271
int ret = 0;
272+
struct fown_struct *f_owner;
273+
enum pid_type pid_type = PIDTYPE_PID;
213274

214-
read_lock_irq(&filp->f_owner.lock);
215-
rcu_read_lock();
216-
if (pid_task(filp->f_owner.pid, filp->f_owner.pid_type))
217-
owner.pid = pid_vnr(filp->f_owner.pid);
218-
rcu_read_unlock();
219-
switch (filp->f_owner.pid_type) {
275+
f_owner = file_f_owner(filp);
276+
if (f_owner) {
277+
read_lock_irq(&f_owner->lock);
278+
rcu_read_lock();
279+
if (pid_task(f_owner->pid, f_owner->pid_type))
280+
owner.pid = pid_vnr(f_owner->pid);
281+
rcu_read_unlock();
282+
pid_type = f_owner->pid_type;
283+
}
284+
285+
switch (pid_type) {
220286
case PIDTYPE_PID:
221287
owner.type = F_OWNER_TID;
222288
break;
@@ -234,7 +300,8 @@ static int f_getown_ex(struct file *filp, unsigned long arg)
234300
ret = -EINVAL;
235301
break;
236302
}
237-
read_unlock_irq(&filp->f_owner.lock);
303+
if (f_owner)
304+
read_unlock_irq(&f_owner->lock);
238305

239306
if (!ret) {
240307
ret = copy_to_user(owner_p, &owner, sizeof(owner));
@@ -248,14 +315,18 @@ static int f_getown_ex(struct file *filp, unsigned long arg)
248315
static int f_getowner_uids(struct file *filp, unsigned long arg)
249316
{
250317
struct user_namespace *user_ns = current_user_ns();
318+
struct fown_struct *f_owner;
251319
uid_t __user *dst = (void __user *)arg;
252-
uid_t src[2];
320+
uid_t src[2] = {0, 0};
253321
int err;
254322

255-
read_lock_irq(&filp->f_owner.lock);
256-
src[0] = from_kuid(user_ns, filp->f_owner.uid);
257-
src[1] = from_kuid(user_ns, filp->f_owner.euid);
258-
read_unlock_irq(&filp->f_owner.lock);
323+
f_owner = file_f_owner(filp);
324+
if (f_owner) {
325+
read_lock_irq(&f_owner->lock);
326+
src[0] = from_kuid(user_ns, f_owner->uid);
327+
src[1] = from_kuid(user_ns, f_owner->euid);
328+
read_unlock_irq(&f_owner->lock);
329+
}
259330

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

417+
static int f_owner_sig(struct file *filp, int signum, bool setsig)
418+
{
419+
int ret = 0;
420+
struct fown_struct *f_owner;
421+
422+
might_sleep();
423+
424+
if (setsig) {
425+
if (!valid_signal(signum))
426+
return -EINVAL;
427+
428+
ret = file_f_owner_allocate(filp);
429+
if (ret)
430+
return ret;
431+
}
432+
433+
f_owner = file_f_owner(filp);
434+
if (setsig)
435+
f_owner->signum = signum;
436+
else if (f_owner)
437+
ret = f_owner->signum;
438+
return ret;
439+
}
440+
346441
static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
347442
struct file *filp)
348443
{
@@ -421,15 +516,10 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
421516
err = f_getowner_uids(filp, arg);
422517
break;
423518
case F_GETSIG:
424-
err = filp->f_owner.signum;
519+
err = f_owner_sig(filp, 0, false);
425520
break;
426521
case F_SETSIG:
427-
/* arg == 0 restores default behaviour. */
428-
if (!valid_signal(argi)) {
429-
break;
430-
}
431-
err = 0;
432-
filp->f_owner.signum = argi;
522+
err = f_owner_sig(filp, argi, true);
433523
break;
434524
case F_GETLEASE:
435525
err = fcntl_getlease(filp);
@@ -844,14 +934,19 @@ static void send_sigurg_to_task(struct task_struct *p,
844934
do_send_sig_info(SIGURG, SEND_SIG_PRIV, p, type);
845935
}
846936

847-
int send_sigurg(struct fown_struct *fown)
937+
int send_sigurg(struct file *file)
848938
{
939+
struct fown_struct *fown;
849940
struct task_struct *p;
850941
enum pid_type type;
851942
struct pid *pid;
852943
unsigned long flags;
853944
int ret = 0;
854945

946+
fown = file_f_owner(file);
947+
if (!fown)
948+
return 0;
949+
855950
read_lock_irqsave(&fown->lock, flags);
856951

857952
type = fown->pid_type;
@@ -1027,13 +1122,16 @@ static void kill_fasync_rcu(struct fasync_struct *fa, int sig, int band)
10271122
}
10281123
read_lock_irqsave(&fa->fa_lock, flags);
10291124
if (fa->fa_file) {
1030-
fown = &fa->fa_file->f_owner;
1125+
fown = file_f_owner(fa->fa_file);
1126+
if (!fown)
1127+
goto next;
10311128
/* Don't send SIGURG to processes which have not set a
10321129
queued signum: SIGURG has its own default signalling
10331130
mechanism. */
10341131
if (!(sig == SIGURG && fown->signum == 0))
10351132
send_sigio(fown, fa->fa_fd, band);
10361133
}
1134+
next:
10371135
read_unlock_irqrestore(&fa->fa_lock, flags);
10381136
fa = rcu_dereference(fa->fa_next);
10391137
}

fs/file_table.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ static int init_file(struct file *f, int flags, const struct cred *cred)
155155
return error;
156156
}
157157

158-
rwlock_init(&f->f_owner.lock);
159158
spin_lock_init(&f->f_lock);
160159
mutex_init(&f->f_pos_lock);
161160
f->f_flags = flags;
@@ -425,7 +424,7 @@ static void __fput(struct file *file)
425424
cdev_put(inode->i_cdev);
426425
}
427426
fops_put(file->f_op);
428-
put_pid(file->f_owner.pid);
427+
file_f_owner_release(file);
429428
put_file_access(file);
430429
dput(dentry);
431430
if (unlikely(mode & FMODE_NEED_UNMOUNT))

fs/internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,3 +337,4 @@ static inline bool path_mounted(const struct path *path)
337337
{
338338
return path->mnt->mnt_root == path->dentry;
339339
}
340+
void file_f_owner_release(struct file *file);

fs/locks.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1451,7 +1451,7 @@ int lease_modify(struct file_lease *fl, int arg, struct list_head *dispose)
14511451
struct file *filp = fl->c.flc_file;
14521452

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

1786+
error = file_f_owner_allocate(filp);
1787+
if (error)
1788+
return error;
1789+
17861790
/* Note that arg is never F_UNLCK here */
17871791
ctx = locks_get_lock_context(inode, arg);
17881792
if (!ctx)

fs/notify/dnotify/dnotify.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ static int dnotify_handle_event(struct fsnotify_mark *inode_mark, u32 mask,
110110
prev = &dn->dn_next;
111111
continue;
112112
}
113-
fown = &dn->dn_filp->f_owner;
113+
fown = file_f_owner(dn->dn_filp);
114114
send_sigio(fown, dn->dn_fd, POLL_MSG);
115115
if (dn->dn_mask & FS_DN_MULTISHOT)
116116
prev = &dn->dn_next;
@@ -316,6 +316,10 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned int arg)
316316
goto out_err;
317317
}
318318

319+
error = file_f_owner_allocate(filp);
320+
if (error)
321+
goto out_err;
322+
319323
/* set up the new_fsn_mark and new_dn_mark */
320324
new_fsn_mark = &new_dn_mark->fsn_mark;
321325
fsnotify_init_mark(new_fsn_mark, dnotify_group);

0 commit comments

Comments
 (0)