diff options
| author | Konstantin Belousov <kib@FreeBSD.org> | 2026-01-04 00:19:36 +0000 |
|---|---|---|
| committer | Konstantin Belousov <kib@FreeBSD.org> | 2026-01-13 14:03:14 +0000 |
| commit | de770681234d001a1f4cdb8121179331dc3a2def (patch) | |
| tree | 3db8040c7b6d87dab8575bfd8150b49581b794b2 | |
| parent | 7361727d4584b5e303183c836497ae2754ce0e53 (diff) | |
rfork(2): fix swap accounting in vmspace_unshare()
When an attempt to increase the swap charge for the ucred failed, we
must forcibly increase the charge to allow the vmspace_destroy()
operation to correctly un-charge the accumulated objects.
Add a swap_reserve_force_by_cred() helper and use it in
vmspace_unshare(), same as it is done in normal fork operations.
Reviewed by: markj
Tested by: pho
Sponsored by: The FreeBSD Foundation
MFC after: 1 week
Differential revision: https://reviews.freebsd.org/D54572
| -rw-r--r-- | sys/vm/swap_pager.c | 10 | ||||
| -rw-r--r-- | sys/vm/vm.h | 1 | ||||
| -rw-r--r-- | sys/vm/vm_map.c | 7 |
3 files changed, 16 insertions, 2 deletions
diff --git a/sys/vm/swap_pager.c b/sys/vm/swap_pager.c index e0bdca07ff0f..3f077289ac30 100644 --- a/sys/vm/swap_pager.c +++ b/sys/vm/swap_pager.c @@ -330,7 +330,7 @@ out_error: } void -swap_reserve_force(vm_ooffset_t incr) +swap_reserve_force_by_cred(vm_ooffset_t incr, struct ucred *cred) { u_long pincr; @@ -346,7 +346,13 @@ swap_reserve_force(vm_ooffset_t incr) #endif pincr = atop(incr); atomic_add_long(&swap_reserved, pincr); - swap_reserve_force_rlimit(pincr, curthread->td_ucred); + swap_reserve_force_rlimit(pincr, cred); +} + +void +swap_reserve_force(vm_ooffset_t incr) +{ + swap_reserve_force_by_cred(incr, curthread->td_ucred); } void diff --git a/sys/vm/vm.h b/sys/vm/vm.h index d28c84dd1c95..0da1891dfcc7 100644 --- a/sys/vm/vm.h +++ b/sys/vm/vm.h @@ -168,6 +168,7 @@ void vm_ksubmap_init(struct kva_md_info *); bool swap_reserve(vm_ooffset_t incr); bool swap_reserve_by_cred(vm_ooffset_t incr, struct ucred *cred); void swap_reserve_force(vm_ooffset_t incr); +void swap_reserve_force_by_cred(vm_ooffset_t incr, struct ucred *cred); void swap_release(vm_ooffset_t decr); void swap_release_by_cred(vm_ooffset_t decr, struct ucred *cred); diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c index 68dcadd2b2f1..4c33b786eaa0 100644 --- a/sys/vm/vm_map.c +++ b/sys/vm/vm_map.c @@ -4957,6 +4957,13 @@ vmspace_unshare(struct proc *p) if (newvmspace == NULL) return (ENOMEM); if (!swap_reserve_by_cred(fork_charge, p->p_ucred)) { + /* + * The swap reservation failed. The accounting from + * the entries of the copied newvmspace will be + * subtracted in vmspace_free(), so force the + * reservation there. + */ + swap_reserve_force_by_cred(fork_charge, p->p_ucred); vmspace_free(newvmspace); return (ENOMEM); } |
