Skip to content

Commit 1a0516f

Browse files
robnbehlendorf
authored andcommitted
FreeBSD: zfs_putpages: don't undirty pages until after write completes
zfs_putpages() would put the entire range of pages onto the ZIL, then return VM_PAGER_OK for each page to the kernel. However, an associated zil_commit() or txg sync had not happened at this point, so the write may not actually be on disk. So, we rework it to use a ZIL commit callback, and do the post-write work of undirtying the page and signaling completion there. We return VM_PAGER_PEND to the kernel instead so it knows that we will take care of it. Sponsored-by: Klara, Inc. Sponsored-by: Wasabi Technology, Inc. Reviewed-by: Mark Johnston <[email protected]> Reviewed-by: Alexander Motin <[email protected]> Signed-off-by: Rob Norris <[email protected]> Closes #17445
1 parent 988e89a commit 1a0516f

File tree

3 files changed

+47
-15
lines changed

3 files changed

+47
-15
lines changed

include/os/freebsd/spl/sys/vm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
extern const int zfs_vm_pagerret_bad;
3636
extern const int zfs_vm_pagerret_error;
3737
extern const int zfs_vm_pagerret_ok;
38+
extern const int zfs_vm_pagerret_pend;
3839
extern const int zfs_vm_pagerput_sync;
3940
extern const int zfs_vm_pagerput_inval;
4041

module/os/freebsd/spl/spl_vm.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
const int zfs_vm_pagerret_bad = VM_PAGER_BAD;
4444
const int zfs_vm_pagerret_error = VM_PAGER_ERROR;
4545
const int zfs_vm_pagerret_ok = VM_PAGER_OK;
46+
const int zfs_vm_pagerret_pend = VM_PAGER_PEND;
4647
const int zfs_vm_pagerput_sync = VM_PAGER_PUT_SYNC;
4748
const int zfs_vm_pagerput_inval = VM_PAGER_PUT_INVAL;
4849

module/os/freebsd/zfs/zfs_vnops_os.c

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
* Copyright (c) 2012, 2015 by Delphix. All rights reserved.
2626
* Copyright (c) 2014 Integros [integros.com]
2727
* Copyright 2017 Nexenta Systems, Inc.
28+
* Copyright (c) 2025, Klara, Inc.
2829
*/
2930

3031
/* Portions Copyright 2007 Jeremy Teo */
@@ -4072,6 +4073,33 @@ zfs_freebsd_getpages(struct vop_getpages_args *ap)
40724073
ap->a_rahead));
40734074
}
40744075

4076+
typedef struct {
4077+
uint_t pca_npages;
4078+
vm_page_t pca_pages[];
4079+
} putpage_commit_arg_t;
4080+
4081+
static void
4082+
zfs_putpage_commit_cb(void *arg)
4083+
{
4084+
putpage_commit_arg_t *pca = arg;
4085+
vm_object_t object = pca->pca_pages[0]->object;
4086+
4087+
zfs_vmobject_wlock(object);
4088+
4089+
for (uint_t i = 0; i < pca->pca_npages; i++) {
4090+
vm_page_t pp = pca->pca_pages[i];
4091+
vm_page_undirty(pp);
4092+
vm_page_sunbusy(pp);
4093+
}
4094+
4095+
vm_object_pip_wakeupn(object, pca->pca_npages);
4096+
4097+
zfs_vmobject_wunlock(object);
4098+
4099+
kmem_free(pca,
4100+
offsetof(putpage_commit_arg_t, pca_pages[pca->pca_npages]));
4101+
}
4102+
40754103
static int
40764104
zfs_putpages(struct vnode *vp, vm_page_t *ma, size_t len, int flags,
40774105
int *rtvals)
@@ -4173,10 +4201,12 @@ zfs_putpages(struct vnode *vp, vm_page_t *ma, size_t len, int flags,
41734201
}
41744202

41754203
if (zp->z_blksz < PAGE_SIZE) {
4176-
for (i = 0; len > 0; off += tocopy, len -= tocopy, i++) {
4177-
tocopy = len > PAGE_SIZE ? PAGE_SIZE : len;
4204+
vm_ooffset_t woff = off;
4205+
size_t wlen = len;
4206+
for (i = 0; wlen > 0; woff += tocopy, wlen -= tocopy, i++) {
4207+
tocopy = MIN(PAGE_SIZE, wlen);
41784208
va = zfs_map_page(ma[i], &sf);
4179-
dmu_write(zfsvfs->z_os, zp->z_id, off, tocopy, va, tx);
4209+
dmu_write(zfsvfs->z_os, zp->z_id, woff, tocopy, va, tx);
41804210
zfs_unmap_page(sf);
41814211
}
41824212
} else {
@@ -4197,19 +4227,19 @@ zfs_putpages(struct vnode *vp, vm_page_t *ma, size_t len, int flags,
41974227
zfs_tstamp_update_setup(zp, CONTENT_MODIFIED, mtime, ctime);
41984228
err = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx);
41994229
ASSERT0(err);
4200-
/*
4201-
* XXX we should be passing a callback to undirty
4202-
* but that would make the locking messier
4203-
*/
4204-
zfs_log_write(zfsvfs->z_log, tx, TX_WRITE, zp, off,
4205-
len, commit, B_FALSE, NULL, NULL);
42064230

4207-
zfs_vmobject_wlock(object);
4208-
for (i = 0; i < ncount; i++) {
4209-
rtvals[i] = zfs_vm_pagerret_ok;
4210-
vm_page_undirty(ma[i]);
4211-
}
4212-
zfs_vmobject_wunlock(object);
4231+
putpage_commit_arg_t *pca = kmem_alloc(
4232+
offsetof(putpage_commit_arg_t, pca_pages[ncount]),
4233+
KM_SLEEP);
4234+
pca->pca_npages = ncount;
4235+
memcpy(pca->pca_pages, ma, sizeof (vm_page_t) * ncount);
4236+
4237+
zfs_log_write(zfsvfs->z_log, tx, TX_WRITE, zp,
4238+
off, len, commit, B_FALSE, zfs_putpage_commit_cb, pca);
4239+
4240+
for (i = 0; i < ncount; i++)
4241+
rtvals[i] = zfs_vm_pagerret_pend;
4242+
42134243
VM_CNT_INC(v_vnodeout);
42144244
VM_CNT_ADD(v_vnodepgsout, ncount);
42154245
}

0 commit comments

Comments
 (0)