Gentoo Archives: gentoo-commits

From: Georgy Yakovlev <gyakovlev@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] repo/gentoo:master commit in: sys-fs/zfs/, sys-fs/zfs/files/
Date: Sun, 07 Nov 2021 23:59:26
Message-Id: 1636329460.7c97da7804003db7be32a3c696f930ca5555ba24.gyakovlev@gentoo
1 commit: 7c97da7804003db7be32a3c696f930ca5555ba24
2 Author: Georgy Yakovlev <gyakovlev <AT> gentoo <DOT> org>
3 AuthorDate: Sun Nov 7 23:01:06 2021 +0000
4 Commit: Georgy Yakovlev <gyakovlev <AT> gentoo <DOT> org>
5 CommitDate: Sun Nov 7 23:57:40 2021 +0000
6 URL: https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=7c97da78
7
8 sys-fs/zfs: revbump 2.1.1, add patch and dist-kernel version limit
9
10 We don't really need patch in userspace package, but
11 patch also installs tests for test-suite and what's why we include it.
12
13 also remove coreutils blocker
14
15 Bug: https://bugs.gentoo.org/815469
16 Signed-off-by: Georgy Yakovlev <gyakovlev <AT> gentoo.org>
17
18 sys-fs/zfs/files/2.1.1-fix-lseek-mmap.patch | 594 ++++++++++++++++++++++++++++
19 sys-fs/zfs/zfs-2.1.1-r3.ebuild | 298 ++++++++++++++
20 2 files changed, 892 insertions(+)
21
22 diff --git a/sys-fs/zfs/files/2.1.1-fix-lseek-mmap.patch b/sys-fs/zfs/files/2.1.1-fix-lseek-mmap.patch
23 new file mode 100644
24 index 00000000000..2c3a66a97bf
25 --- /dev/null
26 +++ b/sys-fs/zfs/files/2.1.1-fix-lseek-mmap.patch
27 @@ -0,0 +1,594 @@
28 +From de198f2d9507b6dcf3d0d8f037ba33940208733e Mon Sep 17 00:00:00 2001
29 +From: Brian Behlendorf <behlendorf1@××××.gov>
30 +Date: Sun, 7 Nov 2021 13:27:44 -0800
31 +Subject: [PATCH] Fix lseek(SEEK_DATA/SEEK_HOLE) mmap consistency
32 +
33 +When using lseek(2) to report data/holes memory mapped regions of
34 +the file were ignored. This could result in incorrect results.
35 +To handle this zfs_holey_common() was updated to asynchronously
36 +writeback any dirty mmap(2) regions prior to reporting holes.
37 +
38 +Additionally, while not strictly required, the dn_struct_rwlock is
39 +now held over the dirty check to prevent the dnode structure from
40 +changing. This ensures that a clean dnode can't be dirtied before
41 +the data/hole is located. The range lock is now also taken to
42 +ensure the call cannot race with zfs_write().
43 +
44 +Furthermore, the code was refactored to provide a dnode_is_dirty()
45 +helper function which checks the dnode for any dirty records to
46 +determine its dirtiness.
47 +
48 +Reviewed-by: Matthew Ahrens <mahrens@×××××××.com>
49 +Reviewed-by: Tony Hutter <hutter2@××××.gov>
50 +Reviewed-by: Rich Ercolani <rincebrain@×××××.com>
51 +Signed-off-by: Brian Behlendorf <behlendorf1@××××.gov>
52 +Issue #11900
53 +Closes #12724
54 +---
55 + configure.ac | 1 +
56 + include/os/freebsd/spl/sys/vnode.h | 18 +++
57 + include/os/freebsd/zfs/sys/zfs_znode_impl.h | 3 +-
58 + include/os/linux/zfs/sys/zfs_znode_impl.h | 1 +
59 + include/sys/dnode.h | 1 +
60 + man/man4/zfs.4 | 2 +-
61 + module/zfs/dmu.c | 53 ++++---
62 + module/zfs/dnode.c | 20 +++
63 + module/zfs/zfs_vnops.c | 9 +-
64 + tests/runfiles/common.run | 2 +-
65 + tests/zfs-tests/cmd/Makefile.am | 1 +
66 + tests/zfs-tests/cmd/mmap_seek/.gitignore | 1 +
67 + tests/zfs-tests/cmd/mmap_seek/Makefile.am | 6 +
68 + tests/zfs-tests/cmd/mmap_seek/mmap_seek.c | 147 ++++++++++++++++++
69 + tests/zfs-tests/include/commands.cfg | 1 +
70 + tests/zfs-tests/include/tunables.cfg | 1 +
71 + .../tests/functional/mmap/Makefile.am | 3 +-
72 + .../functional/mmap/mmap_seek_001_pos.ksh | 67 ++++++++
73 + 18 files changed, 305 insertions(+), 32 deletions(-)
74 + create mode 100644 tests/zfs-tests/cmd/mmap_seek/.gitignore
75 + create mode 100644 tests/zfs-tests/cmd/mmap_seek/Makefile.am
76 + create mode 100644 tests/zfs-tests/cmd/mmap_seek/mmap_seek.c
77 + create mode 100755 tests/zfs-tests/tests/functional/mmap/mmap_seek_001_pos.ksh
78 +
79 +diff --git a/configure.ac b/configure.ac
80 +index 6f34b210d2b..ebc7b276a64 100644
81 +--- a/configure.ac
82 ++++ b/configure.ac
83 +@@ -221,6 +221,7 @@ AC_CONFIG_FILES([
84 + tests/zfs-tests/cmd/mktree/Makefile
85 + tests/zfs-tests/cmd/mmap_exec/Makefile
86 + tests/zfs-tests/cmd/mmap_libaio/Makefile
87 ++ tests/zfs-tests/cmd/mmap_seek/Makefile
88 + tests/zfs-tests/cmd/mmapwrite/Makefile
89 + tests/zfs-tests/cmd/nvlist_to_lua/Makefile
90 + tests/zfs-tests/cmd/randfree_file/Makefile
91 +diff --git a/include/os/freebsd/spl/sys/vnode.h b/include/os/freebsd/spl/sys/vnode.h
92 +index 3670712a045..3bc8a18eeb7 100644
93 +--- a/include/os/freebsd/spl/sys/vnode.h
94 ++++ b/include/os/freebsd/spl/sys/vnode.h
95 +@@ -59,6 +59,8 @@ enum symfollow { NO_FOLLOW = NOFOLLOW };
96 + #include <sys/file.h>
97 + #include <sys/filedesc.h>
98 + #include <sys/syscallsubr.h>
99 ++#include <sys/vm.h>
100 ++#include <vm/vm_object.h>
101 +
102 + typedef struct vop_vector vnodeops_t;
103 + #define VOP_FID VOP_VPTOFH
104 +@@ -83,6 +85,22 @@ vn_is_readonly(vnode_t *vp)
105 + #define vn_has_cached_data(vp) \
106 + ((vp)->v_object != NULL && \
107 + (vp)->v_object->resident_page_count > 0)
108 ++
109 ++static __inline void
110 ++vn_flush_cached_data(vnode_t *vp, boolean_t sync)
111 ++{
112 ++#if __FreeBSD_version > 1300054
113 ++ if (vm_object_mightbedirty(vp->v_object)) {
114 ++#else
115 ++ if (vp->v_object->flags & OBJ_MIGHTBEDIRTY) {
116 ++#endif
117 ++ int flags = sync ? OBJPC_SYNC : 0;
118 ++ zfs_vmobject_wlock(vp->v_object);
119 ++ vm_object_page_clean(vp->v_object, 0, 0, flags);
120 ++ zfs_vmobject_wunlock(vp->v_object);
121 ++ }
122 ++}
123 ++
124 + #define vn_exists(vp) do { } while (0)
125 + #define vn_invalid(vp) do { } while (0)
126 + #define vn_renamepath(tdvp, svp, tnm, lentnm) do { } while (0)
127 +diff --git a/include/os/freebsd/zfs/sys/zfs_znode_impl.h b/include/os/freebsd/zfs/sys/zfs_znode_impl.h
128 +index 7d28bddbf51..4456046e6e4 100644
129 +--- a/include/os/freebsd/zfs/sys/zfs_znode_impl.h
130 ++++ b/include/os/freebsd/zfs/sys/zfs_znode_impl.h
131 +@@ -116,7 +116,8 @@ typedef struct zfs_soft_state {
132 + #define Z_ISLNK(type) ((type) == VLNK)
133 + #define Z_ISDIR(type) ((type) == VDIR)
134 +
135 +-#define zn_has_cached_data(zp) vn_has_cached_data(ZTOV(zp))
136 ++#define zn_has_cached_data(zp) vn_has_cached_data(ZTOV(zp))
137 ++#define zn_flush_cached_data(zp, sync) vn_flush_cached_data(ZTOV(zp), sync)
138 + #define zn_rlimit_fsize(zp, uio) \
139 + vn_rlimit_fsize(ZTOV(zp), GET_UIO_STRUCT(uio), zfs_uio_td(uio))
140 +
141 +diff --git a/include/os/linux/zfs/sys/zfs_znode_impl.h b/include/os/linux/zfs/sys/zfs_znode_impl.h
142 +index 0a6273442b7..de46fc8f2bd 100644
143 +--- a/include/os/linux/zfs/sys/zfs_znode_impl.h
144 ++++ b/include/os/linux/zfs/sys/zfs_znode_impl.h
145 +@@ -71,6 +71,7 @@ extern "C" {
146 + #define Z_ISDIR(type) S_ISDIR(type)
147 +
148 + #define zn_has_cached_data(zp) ((zp)->z_is_mapped)
149 ++#define zn_flush_cached_data(zp, sync) write_inode_now(ZTOI(zp), sync)
150 + #define zn_rlimit_fsize(zp, uio) (0)
151 +
152 + /*
153 +diff --git a/include/sys/dnode.h b/include/sys/dnode.h
154 +index e7cccd044ab..3f5fcc958c3 100644
155 +--- a/include/sys/dnode.h
156 ++++ b/include/sys/dnode.h
157 +@@ -425,6 +425,7 @@ boolean_t dnode_add_ref(dnode_t *dn, void *ref);
158 + void dnode_rele(dnode_t *dn, void *ref);
159 + void dnode_rele_and_unlock(dnode_t *dn, void *tag, boolean_t evicting);
160 + int dnode_try_claim(objset_t *os, uint64_t object, int slots);
161 ++boolean_t dnode_is_dirty(dnode_t *dn);
162 + void dnode_setdirty(dnode_t *dn, dmu_tx_t *tx);
163 + void dnode_set_dirtyctx(dnode_t *dn, dmu_tx_t *tx, void *tag);
164 + void dnode_sync(dnode_t *dn, dmu_tx_t *tx);
165 +diff --git a/man/man4/zfs.4 b/man/man4/zfs.4
166 +index d7fc31bfde1..a136690c76e 100644
167 +--- a/man/man4/zfs.4
168 ++++ b/man/man4/zfs.4
169 +@@ -1586,7 +1586,7 @@ Allow no-operation writes.
170 + The occurrence of nopwrites will further depend on other pool properties
171 + .Pq i.a. the checksumming and compression algorithms .
172 + .
173 +-.It Sy zfs_dmu_offset_next_sync Ns = Ns Sy 0 Ns | ns 1 Pq int
174 ++.It Sy zfs_dmu_offset_next_sync Ns = Ns Sy 0 Ns | Ns 1 Pq int
175 + Enable forcing TXG sync to find holes.
176 + When enabled forces ZFS to act like prior versions when
177 + .Sy SEEK_HOLE No or Sy SEEK_DATA
178 +diff --git a/module/zfs/dmu.c b/module/zfs/dmu.c
179 +index b29d82fd793..f12c5eda8b5 100644
180 +--- a/module/zfs/dmu.c
181 ++++ b/module/zfs/dmu.c
182 +@@ -2093,42 +2093,41 @@ int
183 + dmu_offset_next(objset_t *os, uint64_t object, boolean_t hole, uint64_t *off)
184 + {
185 + dnode_t *dn;
186 +- int i, err;
187 +- boolean_t clean = B_TRUE;
188 ++ int err;
189 +
190 ++restart:
191 + err = dnode_hold(os, object, FTAG, &dn);
192 + if (err)
193 + return (err);
194 +
195 +- /*
196 +- * Check if dnode is dirty
197 +- */
198 +- for (i = 0; i < TXG_SIZE; i++) {
199 +- if (multilist_link_active(&dn->dn_dirty_link[i])) {
200 +- clean = B_FALSE;
201 +- break;
202 +- }
203 +- }
204 ++ rw_enter(&dn->dn_struct_rwlock, RW_READER);
205 +
206 +- /*
207 +- * If compatibility option is on, sync any current changes before
208 +- * we go trundling through the block pointers.
209 +- */
210 +- if (!clean && zfs_dmu_offset_next_sync) {
211 +- clean = B_TRUE;
212 +- dnode_rele(dn, FTAG);
213 +- txg_wait_synced(dmu_objset_pool(os), 0);
214 +- err = dnode_hold(os, object, FTAG, &dn);
215 +- if (err)
216 +- return (err);
217 +- }
218 ++ if (dnode_is_dirty(dn)) {
219 ++ /*
220 ++ * If the zfs_dmu_offset_next_sync module option is enabled
221 ++ * then strict hole reporting has been requested. Dirty
222 ++ * dnodes must be synced to disk to accurately report all
223 ++ * holes. When disabled (the default) dirty dnodes are
224 ++ * reported to not have any holes which is always safe.
225 ++ *
226 ++ * When called by zfs_holey_common() the zp->z_rangelock
227 ++ * is held to prevent zfs_write() and mmap writeback from
228 ++ * re-dirtying the dnode after txg_wait_synced().
229 ++ */
230 ++ if (zfs_dmu_offset_next_sync) {
231 ++ rw_exit(&dn->dn_struct_rwlock);
232 ++ dnode_rele(dn, FTAG);
233 ++ txg_wait_synced(dmu_objset_pool(os), 0);
234 ++ goto restart;
235 ++ }
236 +
237 +- if (clean)
238 +- err = dnode_next_offset(dn,
239 +- (hole ? DNODE_FIND_HOLE : 0), off, 1, 1, 0);
240 +- else
241 + err = SET_ERROR(EBUSY);
242 ++ } else {
243 ++ err = dnode_next_offset(dn, DNODE_FIND_HAVELOCK |
244 ++ (hole ? DNODE_FIND_HOLE : 0), off, 1, 1, 0);
245 ++ }
246 +
247 ++ rw_exit(&dn->dn_struct_rwlock);
248 + dnode_rele(dn, FTAG);
249 +
250 + return (err);
251 +diff --git a/module/zfs/dnode.c b/module/zfs/dnode.c
252 +index 900240479c7..6f87f49f89f 100644
253 +--- a/module/zfs/dnode.c
254 ++++ b/module/zfs/dnode.c
255 +@@ -1648,6 +1648,26 @@ dnode_try_claim(objset_t *os, uint64_t object, int slots)
256 + slots, NULL, NULL));
257 + }
258 +
259 ++/*
260 ++ * Checks if the dnode contains any uncommitted dirty records.
261 ++ */
262 ++boolean_t
263 ++dnode_is_dirty(dnode_t *dn)
264 ++{
265 ++ mutex_enter(&dn->dn_mtx);
266 ++
267 ++ for (int i = 0; i < TXG_SIZE; i++) {
268 ++ if (list_head(&dn->dn_dirty_records[i]) != NULL) {
269 ++ mutex_exit(&dn->dn_mtx);
270 ++ return (B_TRUE);
271 ++ }
272 ++ }
273 ++
274 ++ mutex_exit(&dn->dn_mtx);
275 ++
276 ++ return (B_FALSE);
277 ++}
278 ++
279 + void
280 + dnode_setdirty(dnode_t *dn, dmu_tx_t *tx)
281 + {
282 +diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c
283 +index a83f0b02ab5..7cbb70f499a 100644
284 +--- a/module/zfs/zfs_vnops.c
285 ++++ b/module/zfs/zfs_vnops.c
286 +@@ -85,6 +85,7 @@ zfs_fsync(znode_t *zp, int syncflag, cred_t *cr)
287 + static int
288 + zfs_holey_common(znode_t *zp, ulong_t cmd, loff_t *off)
289 + {
290 ++ zfs_locked_range_t *lr;
291 + uint64_t noff = (uint64_t)*off; /* new offset */
292 + uint64_t file_sz;
293 + int error;
294 +@@ -100,12 +101,18 @@ zfs_holey_common(znode_t *zp, ulong_t cmd, loff_t *off)
295 + else
296 + hole = B_FALSE;
297 +
298 ++ /* Flush any mmap()'d data to disk */
299 ++ if (zn_has_cached_data(zp))
300 ++ zn_flush_cached_data(zp, B_FALSE);
301 ++
302 ++ lr = zfs_rangelock_enter(&zp->z_rangelock, 0, file_sz, RL_READER);
303 + error = dmu_offset_next(ZTOZSB(zp)->z_os, zp->z_id, hole, &noff);
304 ++ zfs_rangelock_exit(lr);
305 +
306 + if (error == ESRCH)
307 + return (SET_ERROR(ENXIO));
308 +
309 +- /* file was dirty, so fall back to using generic logic */
310 ++ /* File was dirty, so fall back to using generic logic */
311 + if (error == EBUSY) {
312 + if (hole)
313 + *off = file_sz;
314 +diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run
315 +index 7f7d161be35..9f181b53e15 100644
316 +--- a/tests/runfiles/common.run
317 ++++ b/tests/runfiles/common.run
318 +@@ -675,7 +675,7 @@ tests = ['migration_001_pos', 'migration_002_pos', 'migration_003_pos',
319 + tags = ['functional', 'migration']
320 +
321 + [tests/functional/mmap]
322 +-tests = ['mmap_write_001_pos', 'mmap_read_001_pos']
323 ++tests = ['mmap_write_001_pos', 'mmap_read_001_pos', 'mmap_seek_001_pos']
324 + tags = ['functional', 'mmap']
325 +
326 + [tests/functional/mount]
327 +diff --git a/tests/zfs-tests/cmd/Makefile.am b/tests/zfs-tests/cmd/Makefile.am
328 +index 2b965ca7000..d1c29fcd1c6 100644
329 +--- a/tests/zfs-tests/cmd/Makefile.am
330 ++++ b/tests/zfs-tests/cmd/Makefile.am
331 +@@ -19,6 +19,7 @@ SUBDIRS = \
332 + mktree \
333 + mmap_exec \
334 + mmap_libaio \
335 ++ mmap_seek \
336 + mmapwrite \
337 + nvlist_to_lua \
338 + randwritecomp \
339 +diff --git a/tests/zfs-tests/cmd/mmap_seek/.gitignore b/tests/zfs-tests/cmd/mmap_seek/.gitignore
340 +new file mode 100644
341 +index 00000000000..6b05a791750
342 +--- /dev/null
343 ++++ b/tests/zfs-tests/cmd/mmap_seek/.gitignore
344 +@@ -0,0 +1 @@
345 ++/mmap_seek
346 +diff --git a/tests/zfs-tests/cmd/mmap_seek/Makefile.am b/tests/zfs-tests/cmd/mmap_seek/Makefile.am
347 +new file mode 100644
348 +index 00000000000..b938931125f
349 +--- /dev/null
350 ++++ b/tests/zfs-tests/cmd/mmap_seek/Makefile.am
351 +@@ -0,0 +1,6 @@
352 ++include $(top_srcdir)/config/Rules.am
353 ++
354 ++pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
355 ++
356 ++pkgexec_PROGRAMS = mmap_seek
357 ++mmap_seek_SOURCES = mmap_seek.c
358 +diff --git a/tests/zfs-tests/cmd/mmap_seek/mmap_seek.c b/tests/zfs-tests/cmd/mmap_seek/mmap_seek.c
359 +new file mode 100644
360 +index 00000000000..f476e1dba9a
361 +--- /dev/null
362 ++++ b/tests/zfs-tests/cmd/mmap_seek/mmap_seek.c
363 +@@ -0,0 +1,147 @@
364 ++/*
365 ++ * CDDL HEADER START
366 ++ *
367 ++ * The contents of this file are subject to the terms of the
368 ++ * Common Development and Distribution License (the "License").
369 ++ * You may not use this file except in compliance with the License.
370 ++ *
371 ++ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
372 ++ * or http://www.opensolaris.org/os/licensing.
373 ++ * See the License for the specific language governing permissions
374 ++ * and limitations under the License.
375 ++ *
376 ++ * When distributing Covered Code, include this CDDL HEADER in each
377 ++ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
378 ++ * If applicable, add the following below this CDDL HEADER, with the
379 ++ * fields enclosed by brackets "[]" replaced with your own identifying
380 ++ * information: Portions Copyright [yyyy] [name of copyright owner]
381 ++ *
382 ++ * CDDL HEADER END
383 ++ */
384 ++
385 ++/*
386 ++ * Copyright (c) 2021 by Lawrence Livermore National Security, LLC.
387 ++ */
388 ++
389 ++#include <unistd.h>
390 ++#include <fcntl.h>
391 ++#include <stdio.h>
392 ++#include <stdlib.h>
393 ++#include <string.h>
394 ++#include <sys/mman.h>
395 ++#include <errno.h>
396 ++
397 ++static void
398 ++seek_data(int fd, off_t offset, off_t expected)
399 ++{
400 ++ off_t data_offset = lseek(fd, offset, SEEK_DATA);
401 ++ if (data_offset != expected) {
402 ++ fprintf(stderr, "lseek(fd, %d, SEEK_DATA) = %d (expected %d)\n",
403 ++ (int)offset, (int)data_offset, (int)expected);
404 ++ exit(2);
405 ++ }
406 ++}
407 ++
408 ++static void
409 ++seek_hole(int fd, off_t offset, off_t expected)
410 ++{
411 ++ off_t hole_offset = lseek(fd, offset, SEEK_HOLE);
412 ++ if (hole_offset != expected) {
413 ++ fprintf(stderr, "lseek(fd, %d, SEEK_HOLE) = %d (expected %d)\n",
414 ++ (int)offset, (int)hole_offset, (int)expected);
415 ++ exit(2);
416 ++ }
417 ++}
418 ++
419 ++int
420 ++main(int argc, char **argv)
421 ++{
422 ++ char *execname = argv[0];
423 ++ char *file_path = argv[1];
424 ++ char *buf = NULL;
425 ++ int err;
426 ++
427 ++ if (argc != 4) {
428 ++ (void) printf("usage: %s <file name> <file size> "
429 ++ "<block size>\n", argv[0]);
430 ++ exit(1);
431 ++ }
432 ++
433 ++ int fd = open(file_path, O_RDWR | O_CREAT, 0666);
434 ++ if (fd == -1) {
435 ++ (void) fprintf(stderr, "%s: %s: ", execname, file_path);
436 ++ perror("open");
437 ++ exit(2);
438 ++ }
439 ++
440 ++ off_t file_size = atoi(argv[2]);
441 ++ off_t block_size = atoi(argv[3]);
442 ++
443 ++ if (block_size * 2 > file_size) {
444 ++ (void) fprintf(stderr, "file size must be at least "
445 ++ "double the block size\n");
446 ++ exit(2);
447 ++ }
448 ++
449 ++ err = ftruncate(fd, file_size);
450 ++ if (err == -1) {
451 ++ perror("ftruncate");
452 ++ exit(2);
453 ++ }
454 ++
455 ++ if ((buf = mmap(NULL, file_size, PROT_READ | PROT_WRITE,
456 ++ MAP_SHARED, fd, 0)) == MAP_FAILED) {
457 ++ perror("mmap");
458 ++ exit(2);
459 ++ }
460 ++
461 ++ /* Verify the file is sparse and reports no data. */
462 ++ seek_data(fd, 0, -1);
463 ++
464 ++ /* Verify the file is reported as a hole. */
465 ++ seek_hole(fd, 0, 0);
466 ++
467 ++ /* Verify search beyond end of file is an error. */
468 ++ seek_data(fd, 2 * file_size, -1);
469 ++ seek_hole(fd, 2 * file_size, -1);
470 ++
471 ++ /* Dirty the first byte. */
472 ++ memset(buf, 'a', 1);
473 ++ seek_data(fd, 0, 0);
474 ++ seek_data(fd, block_size, -1);
475 ++ seek_hole(fd, 0, block_size);
476 ++ seek_hole(fd, block_size, block_size);
477 ++
478 ++ /* Dirty the first half of the file. */
479 ++ memset(buf, 'b', file_size / 2);
480 ++ seek_data(fd, 0, 0);
481 ++ seek_data(fd, block_size, block_size);
482 ++ seek_hole(fd, 0, P2ROUNDUP(file_size / 2, block_size));
483 ++ seek_hole(fd, block_size, P2ROUNDUP(file_size / 2, block_size));
484 ++
485 ++ /* Dirty the whole file. */
486 ++ memset(buf, 'c', file_size);
487 ++ seek_data(fd, 0, 0);
488 ++ seek_data(fd, file_size * 3 / 4,
489 ++ P2ROUNDUP(file_size * 3 / 4, block_size));
490 ++ seek_hole(fd, 0, file_size);
491 ++ seek_hole(fd, file_size / 2, file_size);
492 ++
493 ++ /* Punch a hole (required compression be enabled). */
494 ++ memset(buf + block_size, 0, block_size);
495 ++ seek_data(fd, 0, 0);
496 ++ seek_data(fd, block_size, 2 * block_size);
497 ++ seek_hole(fd, 0, block_size);
498 ++ seek_hole(fd, block_size, block_size);
499 ++ seek_hole(fd, 2 * block_size, file_size);
500 ++
501 ++ err = munmap(buf, file_size);
502 ++ if (err == -1) {
503 ++ perror("munmap");
504 ++ exit(2);
505 ++ }
506 ++
507 ++ close(fd);
508 ++
509 ++ return (0);
510 ++}
511 +diff --git a/tests/zfs-tests/include/commands.cfg b/tests/zfs-tests/include/commands.cfg
512 +index 1ec73f25bae..4497a6248b4 100644
513 +--- a/tests/zfs-tests/include/commands.cfg
514 ++++ b/tests/zfs-tests/include/commands.cfg
515 +@@ -209,6 +209,7 @@ export ZFSTEST_FILES='badsend
516 + mktree
517 + mmap_exec
518 + mmap_libaio
519 ++ mmap_seek
520 + mmapwrite
521 + nvlist_to_lua
522 + randfree_file
523 +diff --git a/tests/zfs-tests/include/tunables.cfg b/tests/zfs-tests/include/tunables.cfg
524 +index 56d430a3987..fff43e46916 100644
525 +--- a/tests/zfs-tests/include/tunables.cfg
526 ++++ b/tests/zfs-tests/include/tunables.cfg
527 +@@ -33,6 +33,7 @@ DEADMAN_FAILMODE deadman.failmode zfs_deadman_failmode
528 + DEADMAN_SYNCTIME_MS deadman.synctime_ms zfs_deadman_synctime_ms
529 + DEADMAN_ZIOTIME_MS deadman.ziotime_ms zfs_deadman_ziotime_ms
530 + DISABLE_IVSET_GUID_CHECK disable_ivset_guid_check zfs_disable_ivset_guid_check
531 ++DMU_OFFSET_NEXT_SYNC dmu_offset_next_sync zfs_dmu_offset_next_sync
532 + INITIALIZE_CHUNK_SIZE initialize_chunk_size zfs_initialize_chunk_size
533 + INITIALIZE_VALUE initialize_value zfs_initialize_value
534 + KEEP_LOG_SPACEMAPS_AT_EXPORT keep_log_spacemaps_at_export zfs_keep_log_spacemaps_at_export
535 +diff --git a/tests/zfs-tests/tests/functional/mmap/Makefile.am b/tests/zfs-tests/tests/functional/mmap/Makefile.am
536 +index 2adc398b8c0..b26791ee7ce 100644
537 +--- a/tests/zfs-tests/tests/functional/mmap/Makefile.am
538 ++++ b/tests/zfs-tests/tests/functional/mmap/Makefile.am
539 +@@ -4,7 +4,8 @@ dist_pkgdata_SCRIPTS = \
540 + cleanup.ksh \
541 + mmap_read_001_pos.ksh \
542 + mmap_write_001_pos.ksh \
543 +- mmap_libaio_001_pos.ksh
544 ++ mmap_libaio_001_pos.ksh \
545 ++ mmap_seek_001_pos.ksh
546 +
547 + dist_pkgdata_DATA = \
548 + mmap.cfg
549 +diff --git a/tests/zfs-tests/tests/functional/mmap/mmap_seek_001_pos.ksh b/tests/zfs-tests/tests/functional/mmap/mmap_seek_001_pos.ksh
550 +new file mode 100755
551 +index 00000000000..6188549ad8d
552 +--- /dev/null
553 ++++ b/tests/zfs-tests/tests/functional/mmap/mmap_seek_001_pos.ksh
554 +@@ -0,0 +1,67 @@
555 ++#!/bin/ksh -p
556 ++#
557 ++# CDDL HEADER START
558 ++#
559 ++# The contents of this file are subject to the terms of the
560 ++# Common Development and Distribution License (the "License").
561 ++# You may not use this file except in compliance with the License.
562 ++#
563 ++# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
564 ++# or http://www.opensolaris.org/os/licensing.
565 ++# See the License for the specific language governing permissions
566 ++# and limitations under the License.
567 ++#
568 ++# When distributing Covered Code, include this CDDL HEADER in each
569 ++# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
570 ++# If applicable, add the following below this CDDL HEADER, with the
571 ++# fields enclosed by brackets "[]" replaced with your own identifying
572 ++# information: Portions Copyright [yyyy] [name of copyright owner]
573 ++#
574 ++# CDDL HEADER END
575 ++#
576 ++
577 ++#
578 ++# Copyright (c) 2021 by Lawrence Livermore National Security, LLC.
579 ++#
580 ++
581 ++. $STF_SUITE/include/libtest.shlib
582 ++. $STF_SUITE/tests/functional/mmap/mmap.cfg
583 ++
584 ++#
585 ++# DESCRIPTION:
586 ++# lseek() data/holes for an mmap()'d file.
587 ++#
588 ++# STRATEGY:
589 ++# 1. Enable compression and hole reporting for dirty files.
590 ++# 2. Call mmap_seek binary test case for various record sizes.
591 ++#
592 ++
593 ++verify_runnable "global"
594 ++
595 ++function cleanup
596 ++{
597 ++ log_must zfs set compression=off $TESTPOOL/$TESTFS
598 ++ log_must zfs set recordsize=128k $TESTPOOL/$TESTFS
599 ++ log_must rm -f $TESTDIR/test-mmap-file
600 ++ log_must set_tunable64 DMU_OFFSET_NEXT_SYNC $dmu_offset_next_sync
601 ++}
602 ++
603 ++log_assert "lseek() data/holes for an mmap()'d file."
604 ++
605 ++log_onexit cleanup
606 ++
607 ++# Enable hole reporting for dirty files.
608 ++typeset dmu_offset_next_sync=$(get_tunable DMU_OFFSET_NEXT_SYNC)
609 ++log_must set_tunable64 DMU_OFFSET_NEXT_SYNC 1
610 ++
611 ++# Compression must be enabled to convert zero'd blocks to holes.
612 ++# This behavior is checked by the mmap_seek test.
613 ++log_must zfs set compression=on $TESTPOOL/$TESTFS
614 ++
615 ++for bs in 4096 8192 16384 32768 65536 131072; do
616 ++ log_must zfs set recordsize=$bs $TESTPOOL/$TESTFS
617 ++ log_must mmap_seek $TESTDIR/test-mmap-file $((1024*1024)) $bs
618 ++ log_must rm $TESTDIR/test-mmap-file
619 ++done
620 ++
621 ++log_pass "lseek() data/holes for an mmap()'d file succeeded."
622
623 diff --git a/sys-fs/zfs/zfs-2.1.1-r3.ebuild b/sys-fs/zfs/zfs-2.1.1-r3.ebuild
624 new file mode 100644
625 index 00000000000..9b78e0fc383
626 --- /dev/null
627 +++ b/sys-fs/zfs/zfs-2.1.1-r3.ebuild
628 @@ -0,0 +1,298 @@
629 +# Copyright 1999-2021 Gentoo Authors
630 +# Distributed under the terms of the GNU General Public License v2
631 +
632 +EAPI=7
633 +
634 +DISTUTILS_OPTIONAL=1
635 +DISTUTILS_USE_SETUPTOOLS=manual
636 +PYTHON_COMPAT=( python3_{8,9,10} )
637 +
638 +inherit autotools bash-completion-r1 dist-kernel-utils distutils-r1 flag-o-matic linux-info pam systemd udev usr-ldscript
639 +
640 +DESCRIPTION="Userland utilities for ZFS Linux kernel module"
641 +HOMEPAGE="https://github.com/openzfs/zfs"
642 +
643 +if [[ ${PV} == "9999" ]]; then
644 + inherit git-r3 linux-mod
645 + EGIT_REPO_URI="https://github.com/openzfs/zfs.git"
646 +else
647 + VERIFY_SIG_OPENPGP_KEY_PATH=${BROOT}/usr/share/openpgp-keys/openzfs.asc
648 + inherit verify-sig
649 +
650 + MY_P="${P/_rc/-rc}"
651 + SRC_URI="https://github.com/openzfs/${PN}/releases/download/${MY_P}/${MY_P}.tar.gz"
652 + SRC_URI+=" verify-sig? ( https://github.com/openzfs/${PN}/releases/download/${MY_P}/${MY_P}.tar.gz.asc )"
653 + S="${WORKDIR}/${P%_rc?}"
654 +
655 + if [[ ${PV} != *_rc* ]]; then
656 + KEYWORDS="~amd64 ~arm64 ~ppc64 ~riscv"
657 + fi
658 +fi
659 +
660 +LICENSE="BSD-2 CDDL MIT"
661 +# just libzfs soname major for now.
662 +# possible candidates: libuutil, libzpool, libnvpair. Those do not provide stable abi, but are considered.
663 +# see libsoversion_check() below as well
664 +SLOT="0/5"
665 +IUSE="custom-cflags debug dist-kernel kernel-builtin minimal nls pam python +rootfs test-suite"
666 +
667 +DEPEND="
668 + net-libs/libtirpc
669 + sys-apps/util-linux
670 + sys-libs/zlib
671 + virtual/libudev:=
672 + dev-libs/openssl:0=
673 + !minimal? ( ${PYTHON_DEPS} )
674 + pam? ( sys-libs/pam )
675 + python? (
676 + virtual/python-cffi[${PYTHON_USEDEP}]
677 + )
678 +"
679 +
680 +BDEPEND="virtual/awk
681 + virtual/pkgconfig
682 + nls? ( sys-devel/gettext )
683 + python? (
684 + dev-python/setuptools[${PYTHON_USEDEP}]
685 + )
686 +"
687 +
688 +if [[ ${PV} != "9999" ]] ; then
689 + BDEPEND+=" verify-sig? ( app-crypt/openpgp-keys-openzfs )"
690 +fi
691 +
692 +# awk is used for some scripts, completions, and the Dracut module
693 +RDEPEND="${DEPEND}
694 + !kernel-builtin? ( ~sys-fs/zfs-kmod-${PV}:= )
695 + !prefix? ( virtual/udev )
696 + sys-fs/udev-init-scripts
697 + virtual/awk
698 + dist-kernel? ( virtual/dist-kernel:= )
699 + rootfs? (
700 + app-arch/cpio
701 + app-misc/pax-utils
702 + !<sys-kernel/genkernel-3.5.1.1
703 + )
704 + test-suite? (
705 + sys-apps/kmod[tools]
706 + sys-apps/util-linux
707 + sys-devel/bc
708 + sys-block/parted
709 + sys-fs/lsscsi
710 + sys-fs/mdadm
711 + sys-process/procps
712 + )
713 +"
714 +
715 +# PDEPEND in this form is needed to trick portage suggest
716 +# enabling dist-kernel if only 1 package have it set, without suggesting to disable
717 +PDEPEND="dist-kernel? ( ~sys-fs/zfs-kmod-${PV}[dist-kernel] )"
718 +
719 +REQUIRED_USE="
720 + !minimal? ( ${PYTHON_REQUIRED_USE} )
721 + python? ( !minimal )
722 + test-suite? ( !minimal )
723 +"
724 +
725 +RESTRICT="test"
726 +
727 +PATCHES=(
728 + "${FILESDIR}/2.0.4-scrub-timers.patch"
729 + "${FILESDIR}/2.1.1-fix-lseek-mmap.patch"
730 +)
731 +
732 +pkg_pretend() {
733 + use rootfs || return 0
734 +
735 + if has_version virtual/dist-kernel && ! use dist-kernel; then
736 + ewarn "You have virtual/dist-kernel installed, but"
737 + ewarn "USE=\"dist-kernel\" is not enabled for ${CATEGORY}/${PN}"
738 + ewarn "It's recommended to globally enable dist-kernel USE flag"
739 + ewarn "to auto-trigger initrd rebuilds with kernel updates"
740 + fi
741 +}
742 +
743 +pkg_setup() {
744 + if use kernel_linux; then
745 + linux-info_pkg_setup
746 +
747 + if ! linux_config_exists; then
748 + ewarn "Cannot check the linux kernel configuration."
749 + else
750 + if use test-suite; then
751 + if linux_chkconfig_present BLK_DEV_LOOP; then
752 + eerror "The ZFS test suite requires loop device support enabled."
753 + eerror "Please enable it:"
754 + eerror " CONFIG_BLK_DEV_LOOP=y"
755 + eerror "in /usr/src/linux/.config or"
756 + eerror " Device Drivers --->"
757 + eerror " Block devices --->"
758 + eerror " [X] Loopback device support"
759 + fi
760 + fi
761 + fi
762 + fi
763 +}
764 +
765 +libsoversion_check() {
766 + local bugurl libzfs_sover
767 + bugurl="https://bugs.gentoo.org/enter_bug.cgi?form_name=enter_bug&product=Gentoo+Linux&component=Current+packages"
768 +
769 + libzfs_sover="$(grep 'libzfs_la_LDFLAGS += -version-info' lib/libzfs/Makefile.am \
770 + | grep -Eo '[0-9]+:[0-9]+:[0-9]+')"
771 + libzfs_sover="${libzfs_sover%%:*}"
772 +
773 + if [[ ${libzfs_sover} -ne $(ver_cut 2 ${SLOT}) ]]; then
774 + echo
775 + eerror "BUG BUG BUG BUG BUG BUG BUG BUG"
776 + eerror "ebuild subslot does not match libzfs soversion!"
777 + eerror "libzfs soversion: ${libzfs_sover}"
778 + eerror "ebuild value: $(ver_cut 2 ${SLOT})"
779 + eerror "This is a bug in the ebuild, please use the following URL to report it"
780 + eerror "${bugurl}&short_desc=${CATEGORY}%2F${P}+update+subslot"
781 + echo
782 + # we want to abort for releases, but just print a warning for live ebuild
783 + # to keep package installable
784 + [[ ${PV} == "9999" ]] || die
785 + fi
786 +}
787 +
788 +src_prepare() {
789 + default
790 + libsoversion_check
791 +
792 + # Run unconditionally (bug #792627)
793 + eautoreconf
794 +
795 + if [[ ${PV} != "9999" ]]; then
796 + # Set revision number
797 + sed -i "s/\(Release:\)\(.*\)1/\1\2${PR}-gentoo/" META || die "Could not set Gentoo release"
798 + fi
799 +
800 + if use python; then
801 + pushd contrib/pyzfs >/dev/null || die
802 + distutils-r1_src_prepare
803 + popd >/dev/null || die
804 + fi
805 +
806 + # prevent errors showing up on zfs-mount stop, #647688
807 + # openrc will unmount all filesystems anyway.
808 + sed -i "/^ZFS_UNMOUNT=/ s/yes/no/" "etc/default/zfs.in" || die
809 +}
810 +
811 +src_configure() {
812 + use custom-cflags || strip-flags
813 + use minimal || python_setup
814 +
815 + local myconf=(
816 + --bindir="${EPREFIX}/bin"
817 + --enable-shared
818 + --enable-sysvinit
819 + --localstatedir="${EPREFIX}/var"
820 + --sbindir="${EPREFIX}/sbin"
821 + --with-config=user
822 + --with-dracutdir="${EPREFIX}/usr/lib/dracut"
823 + --with-linux="${KV_DIR}"
824 + --with-linux-obj="${KV_OUT_DIR}"
825 + --with-udevdir="$(get_udevdir)"
826 + --with-pamconfigsdir="${EPREFIX}/unwanted_files"
827 + --with-pammoduledir="$(getpam_mod_dir)"
828 + --with-systemdunitdir="$(systemd_get_systemunitdir)"
829 + --with-systemdpresetdir="${EPREFIX}/lib/systemd/system-preset"
830 + --with-vendor=gentoo
831 + # Building zfs-mount-generator.c on musl breaks as strndupa
832 + # isn't available. But systemd doesn't support musl anyway, so
833 + # just disable building it.
834 + $(use_enable !elibc_musl systemd)
835 + $(use_enable debug)
836 + $(use_enable nls)
837 + $(use_enable pam)
838 + $(use_enable python pyzfs)
839 + --disable-static
840 + $(usex minimal --without-python --with-python="${EPYTHON}")
841 + )
842 +
843 + econf "${myconf[@]}"
844 +}
845 +
846 +src_compile() {
847 + default
848 + if use python; then
849 + pushd contrib/pyzfs >/dev/null || die
850 + distutils-r1_src_compile
851 + popd >/dev/null || die
852 + fi
853 +}
854 +
855 +src_install() {
856 + default
857 +
858 + gen_usr_ldscript -a nvpair uutil zfsbootenv zfs zfs_core zpool
859 +
860 + use pam && { rm -rv "${ED}/unwanted_files" || die ; }
861 +
862 + use test-suite || { rm -r "${ED}"/usr/share/zfs/{test-runner,zfs-tests,runfiles,*sh} || die ; }
863 +
864 + find "${ED}" -name '*.la' -delete || die
865 +
866 + dobashcomp contrib/bash_completion.d/zfs
867 + bashcomp_alias zfs zpool
868 +
869 + # strip executable bit from conf.d file
870 + fperms 0644 /etc/conf.d/zfs
871 +
872 + if use python; then
873 + pushd contrib/pyzfs >/dev/null || die
874 + distutils-r1_src_install
875 + popd >/dev/null || die
876 + fi
877 +
878 + # enforce best available python implementation
879 + use minimal || python_fix_shebang "${ED}/bin"
880 +}
881 +
882 +pkg_postinst() {
883 + # we always need userspace utils in sync with zfs-kmod
884 + # so force initrd update for userspace as well, to avoid
885 + # situation when zfs-kmod trigger initrd rebuild before
886 + # userspace component is rebuilt
887 + # KV_* variables are provided by linux-info.eclass
888 + if [[ -z ${ROOT} ]] && use dist-kernel; then
889 + dist-kernel_reinstall_initramfs "${KV_DIR}" "${KV_FULL}"
890 + fi
891 +
892 + if use rootfs; then
893 + if ! has_version sys-kernel/genkernel && ! has_version sys-kernel/dracut; then
894 + elog "Root on zfs requires an initramfs to boot"
895 + elog "The following packages provide one and are tested on a regular basis:"
896 + elog " sys-kernel/dracut"
897 + elog " sys-kernel/genkernel"
898 + fi
899 + fi
900 +
901 + if ! use kernel-builtin && [[ ${PV} == "9999" ]]; then
902 + einfo "Adding ${P} to the module database to ensure that the"
903 + einfo "kernel modules and userland utilities stay in sync."
904 + update_moduledb
905 + fi
906 +
907 + if systemd_is_booted || has_version sys-apps/systemd; then
908 + einfo "Please refer to ${EROOT}/lib/systemd/system-preset/50-zfs.preset"
909 + einfo "for default zfs systemd service configuration"
910 + else
911 + [[ -e "${EROOT}/etc/runlevels/boot/zfs-import" ]] || \
912 + einfo "You should add zfs-import to the boot runlevel."
913 + [[ -e "${EROOT}/etc/runlevels/boot/zfs-mount" ]]|| \
914 + einfo "You should add zfs-mount to the boot runlevel."
915 + [[ -e "${EROOT}/etc/runlevels/default/zfs-share" ]] || \
916 + einfo "You should add zfs-share to the default runlevel."
917 + [[ -e "${EROOT}/etc/runlevels/default/zfs-zed" ]] || \
918 + einfo "You should add zfs-zed to the default runlevel."
919 + fi
920 +}
921 +
922 +pkg_postrm() {
923 + if ! use kernel-builtin && [[ ${PV} == "9999" ]]; then
924 + remove_moduledb
925 + fi
926 +}