Skip to content

Commit 238eab7

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 aa964ce commit 238eab7

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 */
@@ -4084,6 +4085,33 @@ zfs_freebsd_getpages(struct vop_getpages_args *ap)
40844085
ap->a_rahead));
40854086
}
40864087

4088+
typedef struct {
4089+
uint_t pca_npages;
4090+
vm_page_t pca_pages[];
4091+
} putpage_commit_arg_t;
4092+
4093+
static void
4094+
zfs_putpage_commit_cb(void *arg)
4095+
{
4096+
putpage_commit_arg_t *pca = arg;
4097+
vm_object_t object = pca->pca_pages[0]->object;
4098+
4099+
zfs_vmobject_wlock(object);
4100+
4101+
for (uint_t i = 0; i < pca->pca_npages; i++) {
4102+
vm_page_t pp = pca->pca_pages[i];
4103+
vm_page_undirty(pp);
4104+
vm_page_sunbusy(pp);
4105+
}
4106+
4107+
vm_object_pip_wakeupn(object, pca->pca_npages);
4108+
4109+
zfs_vmobject_wunlock(object);
4110+
4111+
kmem_free(pca,
4112+
offsetof(putpage_commit_arg_t, pca_pages[pca->pca_npages]));
4113+
}
4114+
40874115
static int
40884116
zfs_putpages(struct vnode *vp, vm_page_t *ma, size_t len, int flags,
40894117
int *rtvals)
@@ -4185,10 +4213,12 @@ zfs_putpages(struct vnode *vp, vm_page_t *ma, size_t len, int flags,
41854213
}
41864214

41874215
if (zp->z_blksz < PAGE_SIZE) {
4188-
for (i = 0; len > 0; off += tocopy, len -= tocopy, i++) {
4189-
tocopy = len > PAGE_SIZE ? PAGE_SIZE : len;
4216+
vm_ooffset_t woff = off;
4217+
size_t wlen = len;
4218+
for (i = 0; wlen > 0; woff += tocopy, wlen -= tocopy, i++) {
4219+
tocopy = MIN(PAGE_SIZE, wlen);
41904220
va = zfs_map_page(ma[i], &sf);
4191-
dmu_write(zfsvfs->z_os, zp->z_id, off, tocopy, va, tx);
4221+
dmu_write(zfsvfs->z_os, zp->z_id, woff, tocopy, va, tx);
41924222
zfs_unmap_page(sf);
41934223
}
41944224
} else {
@@ -4209,19 +4239,19 @@ zfs_putpages(struct vnode *vp, vm_page_t *ma, size_t len, int flags,
42094239
zfs_tstamp_update_setup(zp, CONTENT_MODIFIED, mtime, ctime);
42104240
err = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx);
42114241
ASSERT0(err);
4212-
/*
4213-
* XXX we should be passing a callback to undirty
4214-
* but that would make the locking messier
4215-
*/
4216-
zfs_log_write(zfsvfs->z_log, tx, TX_WRITE, zp, off,
4217-
len, commit, B_FALSE, NULL, NULL);
42184242

4219-
zfs_vmobject_wlock(object);
4220-
for (i = 0; i < ncount; i++) {
4221-
rtvals[i] = zfs_vm_pagerret_ok;
4222-
vm_page_undirty(ma[i]);
4223-
}
4224-
zfs_vmobject_wunlock(object);
4243+
putpage_commit_arg_t *pca = kmem_alloc(
4244+
offsetof(putpage_commit_arg_t, pca_pages[ncount]),
4245+
KM_SLEEP);
4246+
pca->pca_npages = ncount;
4247+
memcpy(pca->pca_pages, ma, sizeof (vm_page_t) * ncount);
4248+
4249+
zfs_log_write(zfsvfs->z_log, tx, TX_WRITE, zp,
4250+
off, len, commit, B_FALSE, zfs_putpage_commit_cb, pca);
4251+
4252+
for (i = 0; i < ncount; i++)
4253+
rtvals[i] = zfs_vm_pagerret_pend;
4254+
42254255
VM_CNT_INC(v_vnodeout);
42264256
VM_CNT_ADD(v_vnodepgsout, ncount);
42274257
}

0 commit comments

Comments
 (0)