git: 1265516c5c61 - main - struct file: add DFLAG_FORK, indicate copying on fork
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 18 Oct 2025 05:14:08 UTC
The branch main has been updated by kib:
URL: https://cgit.FreeBSD.org/src/commit/?id=1265516c5c610fbc0d65451140e3f17b358cd015
commit 1265516c5c610fbc0d65451140e3f17b358cd015
Author: Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2025-08-20 19:07:43 +0000
Commit: Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2025-10-18 05:11:40 +0000
struct file: add DFLAG_FORK, indicate copying on fork
instead of referencing or ignoring.
The pass to handle copying is performed after all passable files are
installed into the new file descriptor's table, since one of the future
consumers of this interface (kqueue) needs all passable file descriptors
already valid. After all fds are installed, the additional call for each
copyable file is done to copy the underlying object.
[MFC note: use fo_spares for fo_fork]
Reviewed by: markj
Tested by: pho
Sponsored by: The FreeBSD Foundation
MFC after: 2 weeks
Differential revision: https://reviews.freebsd.org/D52045
---
sys/kern/kern_descrip.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++--
sys/sys/file.h | 4 ++++
2 files changed, 53 insertions(+), 2 deletions(-)
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index 19118eb7f275..373542d16285 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -2519,10 +2519,13 @@ fdcopy(struct filedesc *fdp)
{
struct filedesc *newfdp;
struct filedescent *nfde, *ofde;
+ struct file *fp;
int i, lastfile;
+ bool fork_pass;
MPASS(fdp != NULL);
+ fork_pass = false;
newfdp = fdinit();
FILEDESC_SLOCK(fdp);
for (;;) {
@@ -2533,10 +2536,35 @@ fdcopy(struct filedesc *fdp)
fdgrowtable(newfdp, lastfile + 1);
FILEDESC_SLOCK(fdp);
}
- /* copy all passable descriptors (i.e. not kqueue) */
+
+ /*
+ * Copy all passable descriptors (i.e. not kqueue), and
+ * prepare to handle copyable but not passable descriptors
+ * (kqueues).
+ *
+ * The pass to handle copying is performed after all passable
+ * files are installed into the new file descriptor's table,
+ * since kqueues need all referenced file descriptors already
+ * valid, including other kqueues. For the same reason the
+ * copying is done in two passes by itself, first installing
+ * not fully initialized ('empty') copyable files into the new
+ * fd table, and then giving the subsystems a second chance to
+ * really fill the copied file backing structure with the
+ * content.
+ */
newfdp->fd_freefile = fdp->fd_freefile;
FILEDESC_FOREACH_FDE(fdp, i, ofde) {
- if ((ofde->fde_file->f_ops->fo_flags & DFLAG_PASSABLE) == 0 ||
+ const struct fileops *ops;
+
+ ops = ofde->fde_file->f_ops;
+ fp = NULL;
+ if ((ops->fo_flags & DFLAG_FORK) != 0 &&
+ (ofde->fde_flags & UF_FOCLOSE) == 0) {
+ if (ops->fo_fork(newfdp, ofde->fde_file, &fp, p1,
+ curthread) != 0)
+ continue;
+ fork_pass = true;
+ } else if ((ops->fo_flags & DFLAG_PASSABLE) == 0 ||
(ofde->fde_flags & UF_FOCLOSE) != 0 ||
!fhold(ofde->fde_file)) {
if (newfdp->fd_freefile == fdp->fd_freefile)
@@ -2545,11 +2573,30 @@ fdcopy(struct filedesc *fdp)
}
nfde = &newfdp->fd_ofiles[i];
*nfde = *ofde;
+ if (fp != NULL)
+ nfde->fde_file = fp;
filecaps_copy(&ofde->fde_caps, &nfde->fde_caps, true);
fdused_init(newfdp, i);
}
MPASS(newfdp->fd_freefile != -1);
FILEDESC_SUNLOCK(fdp);
+
+ /*
+ * Now handle copying kqueues, since all fds, including
+ * kqueues, are in place.
+ */
+ if (__predict_false(fork_pass)) {
+ FILEDESC_FOREACH_FDE(newfdp, i, nfde) {
+ const struct fileops *ops;
+
+ ops = nfde->fde_file->f_ops;
+ if ((ops->fo_flags & DFLAG_FORK) == 0 ||
+ nfde->fde_file == NULL)
+ continue;
+ ops->fo_fork(newfdp, NULL, &nfde->fde_file, p1,
+ curthread);
+ }
+ }
return (newfdp);
}
diff --git a/sys/sys/file.h b/sys/sys/file.h
index c44fd0f28929..e0195c7c6c2a 100644
--- a/sys/sys/file.h
+++ b/sys/sys/file.h
@@ -139,6 +139,8 @@ typedef int fo_fspacectl_t(struct file *fp, int cmd,
off_t *offset, off_t *length, int flags,
struct ucred *active_cred, struct thread *td);
typedef int fo_cmp_t(struct file *fp, struct file *fp1, struct thread *td);
+typedef int fo_fork_t(struct filedesc *fdp, struct file *fp, struct file **fp1,
+ struct proc *p1, struct thread *td);
typedef int fo_spare_t(struct file *fp);
typedef int fo_flags_t;
@@ -163,12 +165,14 @@ struct fileops {
fo_fallocate_t *fo_fallocate;
fo_fspacectl_t *fo_fspacectl;
fo_cmp_t *fo_cmp;
+ fo_fork_t *fo_fork;
fo_spare_t *fo_spares[8]; /* Spare slots */
fo_flags_t fo_flags; /* DFLAG_* below */
};
#define DFLAG_PASSABLE 0x01 /* may be passed via unix sockets. */
#define DFLAG_SEEKABLE 0x02 /* seekable / nonsequential */
+#define DFLAG_FORK 0x04 /* copy on fork */
#endif /* _KERNEL */
#if defined(_KERNEL) || defined(_WANT_FILE)