Skip to content

Commit 0512bf9

Browse files
committed
Merge patch series "file: FD_{ADD,PREPARE}()"
Christian Brauner <brauner@kernel.org> says: This now removes roughly double the code that it adds. I've been playing with this to allow for moderately flexible usage of the get_unused_fd_flags() + create file + fd_install() pattern that's used quite extensively and requires cumbersome cleanup paths. How callers allocate files is really heterogenous so it's not really convenient to fold them into a single class. It's possibe to split them into subclasses like for anon inodes. I think that's not necessarily nice as well. This adds two primitives: (1) FD_ADD() the simple cases a file is installed: fd = FD_ADD(O_CLOEXEC, vfio_device_open_file(device)); if (fd < 0) vfio_device_put_registration(device); return fd; (2) FD_PREPARE() that captures all the cases where access to fd or file or additional work before publishing the fd is needed: FD_PREPARE(fdf, O_CLOEXEC, sync_file->file); if (fdf.err) { fput(sync_file->file); return fdf.err; } data.fence = fd_prepare_fd(fdf); if (copy_to_user((void __user *)arg, &data, sizeof(data))) return -EFAULT; return fd_publish(fdf); I've converted all of the easy cases over to it and it gets rid of an aweful lot of convoluted cleanup logic. There are a bunch of other cases that can also be converted after a bit of massaging. It's centered around a simple struct. FD_PREPARE() encapsulates all of allocation and cleanup logic and must be followed by a call to fd_publish() which associates the fd with the file and installs it into the callers fdtable. If fd_publish() isn't called both are deallocated. FD_ADD() is a shorthand that does the fd_publish() and never exposes the struct to the caller. That's often the case when they don't need access to anything after installing the fd. It mandates a specific order namely that first we allocate the fd and then instantiate the file. But that shouldn't be a problem. Nearly everyone I've converted used this order anyway. There's a bunch of additional cases where it would be easy to convert them to this pattern. For example, the whole sync file stuff in dma currently returns the containing structure of the file instead of the file itself even though it's only used to allocate files. Changing that would make it fall into the FD_PREPARE() pattern easily. I've not done that work yet. There's room for extending this in a way that wed'd have subclasses for some particularly often use patterns but as I said I'm not even sure that's worth it. * patches from https://patch.msgid.link/20251123-work-fd-prepare-v4-0-b6efa1706cfd@kernel.org: (47 commits) kvm: convert kvm_vcpu_ioctl_get_stats_fd() to FD_PREPARE() kvm: convert kvm_arch_supports_gmem_init_shared() to FD_PREPARE() io_uring: convert io_create_mock_file() to FD_PREPARE() file: convert replace_fd() to FD_PREPARE() vfio: convert vfio_group_ioctl_get_device_fd() to FD_PREPARE() tty: convert ptm_open_peer() to FD_PREPARE() ntsync: convert ntsync_obj_get_fd() to FD_PREPARE() media: convert media_request_alloc() to FD_PREPARE() hv: convert mshv_ioctl_create_partition() to FD_PREPARE() gpio: convert linehandle_create() to FD_PREPARE() dma: port sw_sync_ioctl_create_fence() to FD_PREPARE() pseries: port papr_rtas_setup_file_interface() to FD_PREPARE() pseries: convert papr_platform_dump_create_handle() to FD_PREPARE() spufs: convert spufs_gang_open() to FD_PREPARE() papr-hvpipe: convert papr_hvpipe_dev_create_handle() to FD_PREPARE() spufs: convert spufs_context_open() to FD_PREPARE() net/socket: convert __sys_accept4_file() to FD_PREPARE() net/socket: convert sock_map_fd() to FD_PREPARE() net/sctp: convert sctp_getsockopt_peeloff_common() to FD_PREPARE() net/kcm: convert kcm_ioctl() to FD_PREPARE() ... Link: https://patch.msgid.link/20251123-work-fd-prepare-v4-0-b6efa1706cfd@kernel.org Signed-off-by: Christian Brauner <brauner@kernel.org>
2 parents 3a86608 + 6fb1022 commit 0512bf9

38 files changed

Lines changed: 508 additions & 874 deletions

arch/powerpc/platforms/cell/spufs/inode.c

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -267,22 +267,11 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags,
267267

268268
static int spufs_context_open(const struct path *path)
269269
{
270-
int ret;
271-
struct file *filp;
272-
273-
ret = get_unused_fd_flags(0);
274-
if (ret < 0)
275-
return ret;
276-
277-
filp = dentry_open(path, O_RDONLY, current_cred());
278-
if (IS_ERR(filp)) {
279-
put_unused_fd(ret);
280-
return PTR_ERR(filp);
281-
}
282-
283-
filp->f_op = &spufs_context_fops;
284-
fd_install(ret, filp);
285-
return ret;
270+
FD_PREPARE(fdf, 0, dentry_open(path, O_RDONLY, current_cred()));
271+
if (fdf.err)
272+
return fdf.err;
273+
fd_prepare_file(fdf)->f_op = &spufs_context_fops;
274+
return fd_publish(fdf);
286275
}
287276

288277
static struct spu_context *
@@ -508,26 +497,15 @@ static const struct file_operations spufs_gang_fops = {
508497

509498
static int spufs_gang_open(const struct path *path)
510499
{
511-
int ret;
512-
struct file *filp;
513-
514-
ret = get_unused_fd_flags(0);
515-
if (ret < 0)
516-
return ret;
517-
518500
/*
519501
* get references for dget and mntget, will be released
520502
* in error path of *_open().
521503
*/
522-
filp = dentry_open(path, O_RDONLY, current_cred());
523-
if (IS_ERR(filp)) {
524-
put_unused_fd(ret);
525-
return PTR_ERR(filp);
526-
}
527-
528-
filp->f_op = &spufs_gang_fops;
529-
fd_install(ret, filp);
530-
return ret;
504+
FD_PREPARE(fdf, 0, dentry_open(path, O_RDONLY, current_cred()));
505+
if (fdf.err)
506+
return fdf.err;
507+
fd_prepare_file(fdf)->f_op = &spufs_gang_fops;
508+
return fd_publish(fdf);
531509
}
532510

533511
static int spufs_create_gang(struct inode *inode,

arch/powerpc/platforms/pseries/papr-hvpipe.c

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -479,10 +479,7 @@ static const struct file_operations papr_hvpipe_handle_ops = {
479479

480480
static int papr_hvpipe_dev_create_handle(u32 srcID)
481481
{
482-
struct hvpipe_source_info *src_info;
483-
struct file *file;
484-
long err;
485-
int fd;
482+
struct hvpipe_source_info *src_info __free(kfree) = NULL;
486483

487484
spin_lock(&hvpipe_src_list_lock);
488485
/*
@@ -506,20 +503,13 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
506503
src_info->tsk = current;
507504
init_waitqueue_head(&src_info->recv_wqh);
508505

509-
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
510-
if (fd < 0) {
511-
err = fd;
512-
goto free_buf;
513-
}
514-
515-
file = anon_inode_getfile("[papr-hvpipe]",
516-
&papr_hvpipe_handle_ops, (void *)src_info,
517-
O_RDWR);
518-
if (IS_ERR(file)) {
519-
err = PTR_ERR(file);
520-
goto free_fd;
521-
}
506+
FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
507+
anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
508+
(void *)src_info, O_RDWR));
509+
if (fdf.err)
510+
return fdf.err;
522511

512+
retain_and_null_ptr(src_info);
523513
spin_lock(&hvpipe_src_list_lock);
524514
/*
525515
* If two processes are executing ioctl() for the same
@@ -528,22 +518,11 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
528518
*/
529519
if (hvpipe_find_source(srcID)) {
530520
spin_unlock(&hvpipe_src_list_lock);
531-
err = -EALREADY;
532-
goto free_file;
521+
return -EALREADY;
533522
}
534523
list_add(&src_info->list, &hvpipe_src_list);
535524
spin_unlock(&hvpipe_src_list_lock);
536-
537-
fd_install(fd, file);
538-
return fd;
539-
540-
free_file:
541-
fput(file);
542-
free_fd:
543-
put_unused_fd(fd);
544-
free_buf:
545-
kfree(src_info);
546-
return err;
525+
return fd_publish(fdf);
547526
}
548527

549528
/*

arch/powerpc/platforms/pseries/papr-platform-dump.c

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,6 @@ static long papr_platform_dump_create_handle(u64 dump_tag)
303303
{
304304
struct ibm_platform_dump_params *params;
305305
u64 param_dump_tag;
306-
struct file *file;
307-
long err;
308306
int fd;
309307

310308
/*
@@ -334,34 +332,22 @@ static long papr_platform_dump_create_handle(u64 dump_tag)
334332
params->dump_tag_lo = (u32)(dump_tag & 0x00000000ffffffffULL);
335333
params->status = RTAS_IBM_PLATFORM_DUMP_START;
336334

337-
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
335+
fd = FD_ADD(O_RDONLY | O_CLOEXEC,
336+
anon_inode_getfile_fmode("[papr-platform-dump]",
337+
&papr_platform_dump_handle_ops,
338+
(void *)params, O_RDONLY,
339+
FMODE_LSEEK | FMODE_PREAD));
338340
if (fd < 0) {
339-
err = fd;
340-
goto free_area;
341-
}
342-
343-
file = anon_inode_getfile_fmode("[papr-platform-dump]",
344-
&papr_platform_dump_handle_ops,
345-
(void *)params, O_RDONLY,
346-
FMODE_LSEEK | FMODE_PREAD);
347-
if (IS_ERR(file)) {
348-
err = PTR_ERR(file);
349-
goto put_fd;
341+
rtas_work_area_free(params->work_area);
342+
kfree(params);
343+
return fd;
350344
}
351345

352-
fd_install(fd, file);
353-
354346
list_add(&params->list, &platform_dump_list);
355347

356348
pr_info("%s (%d) initiated platform dump for dump tag %llu\n",
357349
current->comm, current->pid, dump_tag);
358350
return fd;
359-
put_fd:
360-
put_unused_fd(fd);
361-
free_area:
362-
rtas_work_area_free(params->work_area);
363-
kfree(params);
364-
return err;
365351
}
366352

367353
/*

arch/powerpc/platforms/pseries/papr-rtas-common.c

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -205,35 +205,18 @@ long papr_rtas_setup_file_interface(struct papr_rtas_sequence *seq,
205205
char *name)
206206
{
207207
const struct papr_rtas_blob *blob;
208-
struct file *file;
209-
long ret;
210208
int fd;
211209

212210
blob = papr_rtas_retrieve(seq);
213211
if (IS_ERR(blob))
214212
return PTR_ERR(blob);
215213

216-
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
217-
if (fd < 0) {
218-
ret = fd;
219-
goto free_blob;
220-
}
221-
222-
file = anon_inode_getfile_fmode(name, fops, (void *)blob,
223-
O_RDONLY, FMODE_LSEEK | FMODE_PREAD);
224-
if (IS_ERR(file)) {
225-
ret = PTR_ERR(file);
226-
goto put_fd;
227-
}
228-
229-
fd_install(fd, file);
214+
fd = FD_ADD(O_RDONLY | O_CLOEXEC,
215+
anon_inode_getfile_fmode(name, fops, (void *)blob, O_RDONLY,
216+
FMODE_LSEEK | FMODE_PREAD));
217+
if (fd < 0)
218+
papr_rtas_blob_free(blob);
230219
return fd;
231-
232-
put_fd:
233-
put_unused_fd(fd);
234-
free_blob:
235-
papr_rtas_blob_free(blob);
236-
return ret;
237220
}
238221

239222
/*

drivers/dma-buf/dma-buf.c

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -768,18 +768,10 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_export, "DMA_BUF");
768768
*/
769769
int dma_buf_fd(struct dma_buf *dmabuf, int flags)
770770
{
771-
int fd;
772-
773771
if (!dmabuf || !dmabuf->file)
774772
return -EINVAL;
775773

776-
fd = get_unused_fd_flags(flags);
777-
if (fd < 0)
778-
return fd;
779-
780-
fd_install(fd, dmabuf->file);
781-
782-
return fd;
774+
return FD_ADD(flags, dmabuf->file);
783775
}
784776
EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF");
785777

drivers/gpio/gpiolib-cdev.c

Lines changed: 21 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -298,12 +298,13 @@ static const struct file_operations linehandle_fileops = {
298298
#endif
299299
};
300300

301+
DEFINE_FREE(linehandle_free, struct linehandle_state *, if (!IS_ERR_OR_NULL(_T)) linehandle_free(_T))
302+
301303
static int linehandle_create(struct gpio_device *gdev, void __user *ip)
302304
{
303305
struct gpiohandle_request handlereq;
304-
struct linehandle_state *lh;
305-
struct file *file;
306-
int fd, i, ret;
306+
struct linehandle_state *lh __free(linehandle_free) = NULL;
307+
int i, ret;
307308
u32 lflags;
308309

309310
if (copy_from_user(&handlereq, ip, sizeof(handlereq)))
@@ -327,10 +328,8 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
327328
lh->label = kstrndup(handlereq.consumer_label,
328329
sizeof(handlereq.consumer_label) - 1,
329330
GFP_KERNEL);
330-
if (!lh->label) {
331-
ret = -ENOMEM;
332-
goto out_free_lh;
333-
}
331+
if (!lh->label)
332+
return -ENOMEM;
334333
}
335334

336335
lh->num_descs = handlereq.lines;
@@ -340,20 +339,18 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
340339
u32 offset = handlereq.lineoffsets[i];
341340
struct gpio_desc *desc = gpio_device_get_desc(gdev, offset);
342341

343-
if (IS_ERR(desc)) {
344-
ret = PTR_ERR(desc);
345-
goto out_free_lh;
346-
}
342+
if (IS_ERR(desc))
343+
return PTR_ERR(desc);
347344

348345
ret = gpiod_request_user(desc, lh->label);
349346
if (ret)
350-
goto out_free_lh;
347+
return ret;
351348
lh->descs[i] = desc;
352349
linehandle_flags_to_desc_flags(handlereq.flags, &desc->flags);
353350

354351
ret = gpiod_set_transitory(desc, false);
355352
if (ret < 0)
356-
goto out_free_lh;
353+
return ret;
357354

358355
/*
359356
* Lines have to be requested explicitly for input
@@ -364,11 +361,11 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
364361

365362
ret = gpiod_direction_output_nonotify(desc, val);
366363
if (ret)
367-
goto out_free_lh;
364+
return ret;
368365
} else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
369366
ret = gpiod_direction_input_nonotify(desc);
370367
if (ret)
371-
goto out_free_lh;
368+
return ret;
372369
}
373370

374371
gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED);
@@ -377,44 +374,23 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
377374
offset);
378375
}
379376

380-
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
381-
if (fd < 0) {
382-
ret = fd;
383-
goto out_free_lh;
384-
}
377+
FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
378+
anon_inode_getfile("gpio-linehandle", &linehandle_fileops,
379+
lh, O_RDONLY | O_CLOEXEC));
380+
if (fdf.err)
381+
return fdf.err;
382+
retain_and_null_ptr(lh);
385383

386-
file = anon_inode_getfile("gpio-linehandle",
387-
&linehandle_fileops,
388-
lh,
389-
O_RDONLY | O_CLOEXEC);
390-
if (IS_ERR(file)) {
391-
ret = PTR_ERR(file);
392-
goto out_put_unused_fd;
393-
}
394-
395-
handlereq.fd = fd;
396-
if (copy_to_user(ip, &handlereq, sizeof(handlereq))) {
397-
/*
398-
* fput() will trigger the release() callback, so do not go onto
399-
* the regular error cleanup path here.
400-
*/
401-
fput(file);
402-
put_unused_fd(fd);
384+
handlereq.fd = fd_prepare_fd(fdf);
385+
if (copy_to_user(ip, &handlereq, sizeof(handlereq)))
403386
return -EFAULT;
404-
}
405387

406-
fd_install(fd, file);
388+
fd_publish(fdf);
407389

408390
dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
409391
lh->num_descs);
410392

411393
return 0;
412-
413-
out_put_unused_fd:
414-
put_unused_fd(fd);
415-
out_free_lh:
416-
linehandle_free(lh);
417-
return ret;
418394
}
419395
#endif /* CONFIG_GPIO_CDEV_V1 */
420396

0 commit comments

Comments
 (0)