From 617d6230458b4de0610120137eba19420ccb3133 Mon Sep 17 00:00:00 2001 From: Jorgen Lundman Date: Mon, 15 Jun 2020 09:07:01 +0900 Subject: [PATCH] macOS: Add macOS support Add all files required for the macOS port. Add new cmd/os/ for tools which are only expected to be used on macOS. This has support for all macOS version up to Catalina. (Not BigSur). Signed-off-by: Jorgen Lundman macOS: big uio change over. Make uio be internal (ZFS) struct, possibly referring to supplied (XNU) uio from kernel. This means zio_crypto.c can now be identical to upstream. Update for draid, and other changes macOS: Use SET_ERROR with uiomove. [squash] macOS: they went and added vdev_draid macOS: compile fixes from rebase macOS: oh cstyle, how you vex me so macOS: They added new methods - squash macOS: arc_register_hotplug for userland too Upstream: avoid warning zio_crypt.c:1302:3: warning: passing 'const struct iovec *' to parameter of type 'void *' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers] kmem_free(uio->uio_iov, uio->uio_iovcnt * sizeof (iovec_t)); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ macOS: Update zfs_acl.c to latest This includes commits like: 65c7cc49bfcf49d38fc84552a17d7e8a3268e58e 1b376d176ead7651ffde83d319edcc1bcc65da55 cfdc432e644912bbd7e026110ee57cc0f22ec321 716b53d0a14c72bda16c0872565dd1909757e73f a741b386d3ce195fd3eb2e98066f9abffbbace89 485b50bb9e6772240af32fba434ddb8ebfa8cede macOS: struct vdev changes macOS: cstyle, how you vex me [squash] Upstream: booo Werror booo Upstream: squash baby Not defined gives warnings. Upstream: Include all Makefiles Signed-off-by: Jorgen Lundman double draid! macOS: large commit macOS: Use APPLE approved kmem_alloc() macOS: large commit WIP: remove reliance on zfs.exports The memory-pressure has been nerfed, and will not run well until we can find other solutions. The kext symbol lookup we can live without, used only for debug and panic. Use lldb to lookup symbols. leaner! leanerr! remove zfs.export dependency cont. export reduction cont. cont. Corrective tweaks for building Correct vnode_iocount() Cleanup pipe wrap code, use pthreads, handle multiple streams latest pipe send with threads sort of works, but bad timing can be deadlock macOS: work out corner case starvation issue in cv_wait_sig() Fix -C in zfs send/recv cv_wait_sig squash Also wrap zfs send resume Implement VOP_LOOKUP for snowflake Finder Don't change date when setting size. Seems to be a weird required with linux, so model after freebsd version macOS: correct xattr checks for uio Fix a noisy source of misleading-indentation warnings Fix "make install" ln -s failures Fix a noisy source of misleading-indentation warnings Fix "make install" ln -s failures fix ASSERT: don't try to peer into opaque vp structure Import non-panicking ASSERT from old spl/include/sys/debug.h Guard with MACOS_ASSERT_SHOULD_PANIC which will do what Linux and FreeBSD do: redefine ASSERTs as VERIFYs. The panic report line will say VERIFY obscuring the problem, and a system panic is harsher (and more dangerous) on MacOS than a zfs-module panic on Linux. ASSERTions: declare assfail in debug.h Build and link spl-debug.c Eliminate spurious "off" variable, use position+offset range Make sure we hold the correct range to avoid panic in dmu_tx_dirty_buf (macro DMU_TX_DIRTY_BUF defined with --enable-debug). zvol_log_write the range we have written, not the future range silence very noisy and dubious ASSERT macOS: M1 fixes for arm64. sysctl needs to use OID2 Allocs needs to be IOMalloc_aligned Initial spl-vmem memory area needs to be aligned to 16KB No cpu_number() for arm64. macOS: change zvol locking, add zvol symlinks macOS: Return error on UF_COMPRESSED This means bsdtar will be rather noisy, but we prefer noise over corrupt files (all files would be 0-sized). usr/bin/zprint: Failed to set file flags~ -rwxr-xr-x 1 root wheel 47024 Mar 17 2020 /Volumes/BOOM/usr/bin/zprint usr/bin/zprint: Failed to set file flags -rwxr-xr-x 1 root wheel 47024 Mar 17 2020 /Volumes/BOOM/usr/bin/zprint Actually include zedlet for zvols macOS: Fix Finder crash on quickview, SMB error codes xattr=sa would return negative returncode, hangover from ZOL code. Only set size if passed a ptr. Convert negative errors codes back to normal. Add LIBTOOLFLAGS for macports toolchain This will replace PR#23 macOS zpool import fixes The new codebase uses a mixture of thread pools and lio_listio async io, and on macOS there are low aio limits, and when those are reached lio_listio() returns EAGAIN when probing several prospective leaf vdevs concurrently, looking for labels. We should not abandon probing a vdev in this case, and can usually recover by trying again after a short delay. (We continue to treat other errnos as unrecoverable for that vdev, and only try to recover from EAGAIN a few times). Additionally, take logic from old o3x and don't probe a variety of devices commonly found in /dev/XXX as they either produce side-effects or are simply wasted effort. Finally, add a trailing / that FreeBSD and Linux both have. listxattr may not expose com.apple.system xattr=sa We need to ask IOMallocAligned for the enclosing POW2 vmem_create() arenas want at least natural alignment for the spans they import, and will panic if they don't get it. For sub-PAGESIZE calls to osif_malloc, align on PAGESIZE. Otherwise align on the enclosing power of two for any osif_malloc allocation up to 2^32. Anything that asks osif_malloc() for more than that is almost certainly a bug, but we can try aligning on PAGESIZE anyway, rather than extend the enclosing-power-of-two device to handle 64-bit allocations. Simplify the creation of bucket arenas, and adjust their quanta. This results in handing back considerably more (and smaller) chunks of memory to osif_free if there is pressure, and reduces waits in xnu_alloc_throttled(), so is a performance win for a busy memory-constrained system. Finally, uncomment some valid code that might be used by future callers of vmem_xcreate(). use vmem_xalloc to match the vmem_xfree of initial dynamic alloc vmem_alloc() breaks the initial large vmem_add() allocation into smaller chunks in an effort to have a large number vmem segments in the arena. This arena does not benefit from that. Additionaly, in vmem_fini() we call vmem_xfree() to return the initial allocation because it is done after almost everything has been pulled down. Unfortunately vmem_xfree() returns the entire initial allocation as a single span. IOFree() checks a variable maintained by the IOMalloc* allocators which tracks the largest allocation made so far, and will panic when (as it almost always is the case) the initial large span is handed to it. This usually manifests as a panic or hang on kext unload, or a hang at reboot. Consequently, we will now use vmem_xalloc() for this initial allocation; vmem_xalloc() also lets us explicitly specify the natural alignement we want for it. zfs_rename SA_ADDTIME may grow SA Avoid: zfs`dmu_tx_dirty_buf(tx=0xffffff8890a56e40, db=0xffffff8890ae8cd0) at dmu_tx.c:674:2 -> 674 panic("dirtying dbuf obj=%llx lvl=%u blkid=%llx but not tx_held\n", 675 (u_longlong_t)db->db.db_object, db->db_level, 676 (u_longlong_t)db->db_blkid); zfs diff also needs to be wrapped. Replace call to pipe() with a couple of open(mkfifo) instead. Upstream: cstyle zfs_fm.c macOS: cstyle baby IOMallocAligned() should call IOFreeAligned() macOS: zpool_disable_volumes v1 When exporting, also kick mounted zvols offline macOS: zpool_disable_volumes v2 When exporting zvols, check IOReg for the BSDName, instead of using readlink on the ZVOL symlinks. Also check if apfs has made any synthesized disks, and ask them to unmount first. ./scripts/cmd-macos.sh zpool export BOOM Exporting 'BOOM/volume' ... asking apfs to eject 'disk5' Unmount of all volumes on disk5 was successful ... asking apfs to eject 'disk5s1' Unmount of all volumes on disk5 was successful ... asking ZVOL to export 'disk4' Unmount of all volumes on disk4 was successful zpool_disable_volume: exit macOS: Add libdiskmgt and call inuse checks macOS: compile fixes from rebase macOS: oh cstyle, how you vex me so macOS: They added new methods - squash macOS: arc_register_hotplug for userland too macOS: minor tweaks for libdiskmgt macOS: getxattr size==0 is to lookup size Also skip the ENOENT return for "zero" finderinfo, as we do not skip over them in listxattr. macOS: 10.9 compile fixes macOS: go to rc2 macOS: kstat string handling should copyin. cstyle baby macOS: Initialise ALL quota types projectid, userobj, groupobj and projectobj, quotas were missed. macOS: error check sysctl for older macOS Wooo cstyle, \o/ Make arc sysctl tunables work (#27) * use an IOMemAligned for a PAGE_SIZE allocation * we should call arc_kstat_update_osx() Changing kstat.zfs.darwin.tunable.zfs_arc_min doesn't do anything becasue arc_kstat_update_osx() was removed at the same time the (obsoleted by upstream) arc_kstat_update() was removed from zfs_kstat_osx.c. Put it back. * when we sysctl arc tunables, call arc_tuning_update() * rely on upstream's sanity checking Simplification which also avoids spurious CMN_WARN messages caused by setting the arcstat variables here, when upstream's arc_tuning_update() checks that they differ from the tunable variables. * add tunable zfs_arc_sys_free and fix zfs_arc_lotsfree_percent both are in upstream's arc_tuning_update() zfs_arc_sys_free controls the amount of memory that ARC will leave free, which is roughly what lundman wants for putting some sort of cap on memory use. * cstyle macOS: set UIO direction, to receive xattr from XNU macOS: ensure uio is zeroed in case XNU uio is NULL. Fix zfs_vnop_getxattr (#28) "xattr -l " would return inconsistent garbage, especially from non-com.apple.FinderInfo xattrs. The UIO_WRITE was a 0 (UIO_READ) in the previous commit, change it. Also turn kmem_alloc -> kmem_zalloc in zfs_vnops_osx.c, for cheap extra safety. launch `zpool import` through launchd in the startup script (#26) Signed-off-by: Guillaume Lessard cstyle macOS: correct dataset_kstat_ logic and kstat leak. dataset_kstat_create() will allocate a string and set it before calling kstat_create() - so we can not set strings to NULL. Likewise, we can not bulk free strings on unload, we have to rely on the caller of kstat to do so. (Which is proper). Add calls to dataset_kstat for datasets and zvol. kstat.zfs/BOOM.dataset.objset-0x36.dataset_name: BOOM kstat.zfs/BOOM.dataset.objset-0x36.writes: 0 kstat.zfs/BOOM.dataset.objset-0x36.nwritten: 0 kstat.zfs/BOOM.dataset.objset-0x36.reads: 11 kstat.zfs/BOOM.dataset.objset-0x36.nread: 10810 kstat.zfs/BOOM.dataset.objset-0x36.nunlinks: 0 kstat.zfs/BOOM.dataset.objset-0x36.nunlinked: 0 macOS: remove no previous prototype for function macOS: correct openat wrapper build fixes re TargetConditionals.h (#30) AvailabilityMacros.h needs TargetConditionals.h defintions in picky modern compilers. Add them to sysmacros.h, and fix a missing sysmacros.h include. Memory fixes on macOS_pure (#31) * Improve memory handling on macOS * remove obsolete/unused zfs_file_data/zfs_metadata caching * In the new code base, we use upstream's zio.c without modification, and so the special zio caching code became entirely vestigial, and likely counterproductive. * and make busy ABD better behaved on busy macOS box Post-ABD we no longer gained much benefit in the old code base from the complicated special handling for the caches created in zio.c. As there's only really one size of ABD allocation, we do not need a qcache layer as in 1.9. Instead use an arena with VMC_NO_QCACHE set to ask for for 256k chunks. * don't reap extra caches in arc_kmem_reap_now() KMF_LITE in DEBUG build is OK * build fixes re TargetConditionals.h AvailabilityMacros.h needs TargetConditionals.h defintions in picky modern compilers. Add them to sysmacros.h, and fix a missing sysmacros.h include. Use barrier synchronization and IO Priority in ldi_iokit.cpp (#33) * other minor changes in vdev_disk Thread and taskq fixing (#32) Highlights: * thread names for spindump * some taskq_d is safe and useful * reduce thread priorities * use througput & latency QOS * TIMESHARE scheduling * passivate some IO * Pull in relevant changes from old taskq_fixing branch 1.9 experimentation pulled into 2.x * add throttle_set_thread_io_policy to zfs.exports * selectively re-enable TASKQ_DYNAMIC also drop wr_iss zio taskqs even further in priority (cf freebsd) * reduce zvol taskq priority * make system_taskq dynamic * experimentally allow three more taskq_d * lower thread prorities overall on an M1 with no zfs whatsoever, the highest priority threads are in the mid 90s, with most kernel threads at priority 81 (basepri). with so many maxclsyspri threads in zfs, we owuld starve out important things like vm_pageout_scan (pri 91), sched_maintenance_thread (pri 95), and numerous others. moreover, ifnet_start_{interfaces} are all priority 82. we should drop minclsyspri below 81, have defclsyspri at no more than 81, and make sure we have few threads above 89. * some tidying up of lowering of priority Thread and taskq fixing * fix old code pulled into spa.c, and further lower priorities * Thread and taskq fixing drop xnu priorities by one update a comment block set USER_INITIATED throughput QOS on TIMESHARE taskq threads don't boost taskq threads accidentally don't let taskq threads be pri==81 don't let o3x threads have importance > 0 apply xnu thread policies to taskq_d threads too assuming this works, it calls out for DRY refactoring with the other two flavours, that operate on current_thread(). simplify in spa.c make practically all the taskqs TIMESHARE Revert "apply xnu thread policies to taskq_d threads too" Panic in VM This reverts commit 39f93be4332a53627d0b68554a1d20020544b69c. Revert "Revert "apply xnu thread policies to taskq_d threads too"" I see what happened now. This reverts commit 75619f0dcd2eedd5d453fc64475e8f16d707b47c. adjust thread not the magic number refactor setting thread qos make DRY refactor rebuild this includes userland TASKQ_REALLY_DYNAMIC fixes fix typo set thread names for spindump visibility cstyle Upstream: Add --enable-macos-impure to autoconf Controls -DMACOS_IMPURE Signed-off-by: Jorgen lundman macOS: Add --enable-macos-impure switch to missing calls. Call the wrapped spl_throttle_set_thread_io_policy Add spl_throttle_set_thread_io_policy to headers macOS: vdev_file should use file_taskq Also cleanup spl-taskq to have taskq_wait_outstanding() in preparation for one day implementing it. Change alloc to zalloc in zfs_ctldir.c Call wrap_zstd_init() and wrap_zstd_fini() (#34) macOS: change both alloc to zalloc macOS: mutex_tryenter can be used while holding zstd uses mutex_tryenter() to check if it already is holding the mutex. Can't find any implementations that object to it, so changing our spl-mutex.c Tag zfs-2.0.0rc4 macOS: return error from uiomove instead of panic macOS: Skip known /dev entry which hangs macOS: Give better error msg when features are needed for crypto Using 1.9.4 crypto dataset now require userobj and projectquota. Alert the user to activate said features to mount crypt dataset. There is no going back to 1.9.4 after features are enabled. macOS: Revert to pread() over AIO due to platform issues. We see waves of EAGAIN errors from lio_listio() on BigSur (but not Catalina) which could stem from recent changes to AIO in XNU. For now, we will go with the classic read label. Re-introduce a purified memory pressure handling mechanism (#35) * Introduce pure pressure-detecting-and-reacting system * "pure" -- no zfs.exports requirement * plumb in mach_vm_pressure_level_monitor() and mach_vm_pressure_monitor() calls to maintain reduced set of inputs into previous signalling into (increasingly shared with upstream) arc growth or shrinking policy * introduce mach_vm_pressure kstats which can be compared with userland-only sysctls: kstat.spl.misc.spl_misc.spl_vm_pages_reclaimed: 0 kstat.spl.misc.spl_misc.spl_vm_pages_wanted: 0 kstat.spl.misc.spl_misc.spl_vm_pressure_level: 0 vm.page_free_wanted: 0 vm.page_free_count: 25,545 vm.page_speculative_count: 148,572 * and a start on tidying and obsolete code elimination * make arc_default_max() much bigger Optional: can be squashed into main pressure commit, or omitted. Users can use zsysctl.conf or manual setting of kstat.zfs.darwin.tunable.zfs_arc_max to override whichever default is chosen (this one, or the one it replaces). Allmem is already deflated during initialization, so this patch raises the un-sysctled ARC maximum from 1/6 to 1/2 of physmem. * handle (vmem) abd_cache fragmentation after arc shrink When arc shrinks due to a significant pressure event, the abd_chunk kmem cache will free slabs back to the vmem abd_cache, and this memory can be several gigabytes. Unfortunately multi-threaded concurrent kmem_cache allocation in the first place, and a priori unpredicatble arc object lifetimes means that abds held by arc objects may be scattered across multiple slabs, with different objects interleaved within slabs. Thus after a moderate free, the vmem cache can be fragmented and this is seen by (sysctl) kstat.vmem.vmem.abd_cache.mem_inuse being much smaller than (sysctl) kstat.vmem.vmem.abd_cache.mem_import, the latter of which may even be stuck at approximately the same value as before the arc free and kmem_cache reap. When there is a large difference between import and inuse, we set arc_no_grow in hopes that ongoing arc activity will defragment organically. This works better with more arc read/write activity after the free, and almost not at all if after the free there is almost no activity. We also add BESTFIT policy to abd_arena experimentally BESTFIT: look harder to place an abd chunk in a slab rather than place in the first slot that is definitely large enough which breaks the vmem constant-time allocation guarantee, although that is less important for this particular vmem arena because of the strong modality of allocations from the abd_chunk cache (its only client). Additionally reduce the abd_cache arena import size to 128k from 256k; the increase in allocation and free traffic between it and the heap is small compared to the gain under this new anti-fragmentation scheme. * some additional tidying in arc_os.c Tag macos-2.0.0-rc5 abd_cache fragmentation mitigation (#36) * printf->dprintf HFS_GET_BOOT_INFO periodically there will be huge numbers of these printfs, and they are not really useful except when debugging vnops. * Mitigate fragmentation in vmem.abd_cache In macOS_pure the abd_chunk kmem cache is parented to the abd_cache vmem arena to avoid sometimes-heavy ARC allocation and free stress on the main kmem cache, and because abd_chunk has such a strongly modal page-sized allocation size. Additionally, abd_chunk allocations and frees come in gangs, often with high multi-thread concurrency. It is that latter property which is the primary source of arena fragmentation, and it will affect any vmem arena directly underneath the abd_chunk kmem cache. Because we have a vmeme parent solely for abd_chunk, we can monitor that parent for various patterns and react to them. This patch monitors the difference between the variables exported as kstat.vmem.vmem.abd_cache.mem_inuse and kstat.vmem.vmem.abd_cache.mem_import, watching for a large gap between the two, which can arise after an ARC shrink returns many slabs from the arc_chunk kmem cache to the abd_cache arena, as vmem segments still contain slabs which hold still-alive abds. When there is a significant gap, we turn on arc_no_grow and hope that organic ARC activity reduces the gap. If after several minutes this is not the case, a small arc_reduce_target_size() is applied. In comparison with previous behaviour, ARC equilibrium sizes will tend slightly -- but not neormously -- lower because the arc target size reduction is made fairly frequently. However, this is offset by the benefit of less *long-term* abd_cache fragmentation, and less complete collapses of ARC in the face of system memory pressure (since less is "stuck" in vmem). ARC consequently will stay at its equilibrium more often than near its minimum. This is demonstrated by a generally lower overall total held memory (kstat.spl.misc.spl_misc.os_mem_alloc) except on systems with essentially no memory pressure, or systems which have been sysctl-tuned for different behaviour. macOS: Additional 10.9 fixes that missed the boat Tidying nvram zfs_boot=pool (#37) If zfs_boot is set we run a long-lived zfs_boot_import_thread, which can stay running until the kernel module is running _fini() functions at unload or shutdown. This patch dispatches it on a zfs_boot() taskq, to avoid causing a hang at the taskq_wait_outstanding(system_taskq, 0) in zvol.c's zvol_create_minors_recursive(), which would prevent pool imports finishing if the pool contained zvols. (Symptoms: "zpool import" does not exit for any pool, system does not see any zvols). This exposed a long-term race condition in our zfs_boot.cpp: the notifier can cause the mutex_enter(&pools->lock) in zfs_boot_probe_media to be reached before the mutex_enter() after the notifier was created. The use of the system_taskq was masking that, by quietly imposing a serialization choke. Moving the mutex and cv initialization earlier -- in particular before the notifier is created -- eliminates the race. Further tidying in zfs_boot.cpp, including some cstyling, switching to _Atomic instead of volatile. Volatile is for effectively random reads; _Atomic is for when we want many readers to have a consistent view after the variable is written. Finally, we need TargetConditionals.h in front of AvailabilityMacros.h in order to build. Add includes to build on Big Sur with macports-clang-11 (#38) * TargetConditionals.h before all AvailabilityMacros.h * add several TargetConditionals.h and AvaialbilityMacros.h Satisfy picky macports-clang-11 toolchain on Big Sur. macOS: clean up large build, indicate errors. Fix debug macOS: Retire MNTTYPE_ZFS_SUBTYPE lookup zfs in iokit macOS: rename net.lundman. -> org.openzfsonosx. macOS: Tag va_mode for upstream ASSERTS XNU sets va_type = VDIR, but does not bother with va_mode. However ZFS checks to confirm S_ISDIR is set in mkdir. macOS: Fix zfs_ioc_osx_proxy_dataset for datasets It was defined as a _pool() ioctl. While we are here changing things change it into a new-style ioctl instead. This should fix non-root datasets mounting as a proxy (devdisk=on). cstyle macOS: setxattr debug prints left in macOS: don't create DYNAMIC with _ent taskq macOS: Also uninstall new /usr/local/zfs before install macos-2.0.0-rc6 macOS: strcmp deprecated after macOS 11 macOS: pkg needs to notarize at the end macOS: strdup strings in getmntent mirrored on FreeBSD. macOS: remove debug print macOS: unload zfs, not openzfs macOS: actually include the volume icon file as well also update to PR macOS: prefer disk over rdisk macOS: devdisk=off mimic=on needs to check for dataset Datasets with devdisks=on will be in ioreg, with it off and mimic=on then it needs to handle: BOOM/fs1 /Volumes/BOOM/fs1 by testing if "BOOM/fs1" is a valid dataset. fixifx macOS: doubled up "int rc" losing returncode Causing misleading messages macOS: zfsctl was sending from IDs macOS: let zfs mount as user succeed If the "mkdir" can succeed (home dir etc, as opposed to /Volumes) then let the mount be able to happen. macOS: Attempt to implement taskq_dispatch_delay() frequently used with taskq_cancel_id() to stop taskq from calling `func()` before the timeout expires. Currently implemented by the taskq sleeping in cv_timedwait() until timeout expires, or it is signalled by taskq_cancel_id(). Seems a little undesirable, could we build an ordered list of delayed taskqs, and only place them to run once timeout has expired, leaving the taskq available to work instead of delaying. macOS: Separate unmount and proxy_remove When proxy_remove is called at the tail end of unmount, we get the alert about "ejecting before disconnecting device". To mirror the proxy create, we make it a separate ioctl, and issue it after unmount completes. macOS: explicitly call setsize with O_TRUNC It appears O_TRUNC does nothing, like the goggles. macOS: Add O_APPEND to zfs_file_t It is currently not used, but since it was written for a test case, we might as well keep it. macOS: Pass fd_offset between kernel and userland. macOS: Missing return in non-void function macOS: finally fix taskq_dispatch_delay() you find a bug, you own the bug. macOS: add missing kstats macOS: restore the default system_delay_taskq macOS: dont call taskq_wait in taskq_cancel macOS: fix taskq_cancel_id() We need to make sure the taskq has finished before returning in taskq_cancel_id(), so that the taskq doesn't get a chance to run after. macOS: correct 'hz' to 100. sysctl kern.clockrate: 100 sleeping for 1 second. bolt: 681571 sleep() 35 bolt: 681672: diff 101 'hz' is definitely 100. macOS: implement taskq_delay_dispatch() Implement delayed taskq by adding them to a list, sorted by wake-up time, and a dispatcher thread which sleeps until the soonest taskq is due. taskq_cancel_id() will remove task from list if present. macOS: ensure to use 1024 version of struct statfs and avoid coredump if passed zhp == NULL. macOS: fix memory leak in xattr_list macOS: must define D__DARWIN_64_BIT_INO_T for working getfsstat getmntany: don't set _DARWIN_FEATURE_64_BIT_INODE This is automatically set by default in userland if the deployment target is > 10.5 macOS: Fix watchdog unload and delay() macOS: improve handling of invariant disks Don't prepend /dev to all paths not starting with /dev as InvariantDisks places its symlinks in /var/run/disk/by-* not /dev/disk/by-*. Also, merge in some tweaks from Linux's zpool_vdev_os.c such as only using O_EXCL with spares. macOS: remove zfs_unmount_006_pos from large. Results in KILLED. Tag macos-2.0.0rc7 macOS: If we don't set SOURCES it makes up zfs.c from nowhere macOS: remove warning macOS: compile fixes after rebase macOS: connect SEEK_HOLE SEEK_DATA to ioctl macOS: Only call vnode_specrdev() when valid macOS: Use VNODE_RELOAD in iterate in the hopes of avoiding ZFS call back in VNOP_INACTIVE macOS: zfs_kmod_fini() calls taskq_cancel_id() so we must unload system_taskq_fini() after the call to zfs_kmod_fini() macOS: shellcheck error macOS: Setting landmines cause panic on M1 "panicString" : "panic(cpu 1 caller 0xfffffe001db72dc8): Break 0xC470 instruction exception from kernel. Ptrauth failure with IA key resulted in 0x2000000000000001 at pc 0xfffffe001c630880, lr 0x8afcfe001c630864 (saved state: 0xfffffe309386b180) macOS: vget should only lookup direct IDs macOS: rootzp left z_projid uninitialised Causing z_projid to have "0xBADDCAFEBADDCAFE" initially, and zfs_link() to return EXDEV due to differenting z_projid, presenting the user with "Cross-device link". Would only happen after loading kext, on the root znode. macOS: Update installer rtf macOS: update and correct the kext_version macOS: Update copyright, fix url and versions macOS ARC memory improvements and old code removal macOS_pure "purification" in spl-[kv]mem coupled with the new dynamics of trying to contain the split between inuse and allocated in the ABD vmem arena produce less memory-greed, so we don't have to do as much policing of memory consumption, and lets us rely on some more common/cross-platform code for a number of commonplace calculation and adjustment of ARC variables. Additionally: * Greater niceness in spl_free_thread : when we see pages are wanted (but no xnu pressure), react more strongly. Notably if we are within 64MB of zfs's memory ceiling, clamp spl_free to a maximum of 32MB. * following recent fixes to abd_os.c, revert to KMC_NOTOUCH at abd_chunk kmem cache creation time, to turn off BUFTAG|CONTENTS|LITE, thus avoiding allocations of many many extra 4k chunks in DEBUG builds. * Double prepopulation of kmem_taskq entries: kmem_cache_applyall() makes this busy, and we want at least as many entries as we have kmem caches at kmem_reqp() time. --- appveryor.yml | 12 + cmd/os/Makefile.am | 4 + cmd/os/macos/InvariantDisks/.gitignore | 3 + cmd/os/macos/InvariantDisks/BSD.LICENSE.md | 27 + .../InvariantDisks.xcodeproj/project.pbxproj | 375 + .../InvariantDisks/IDBaseLinker.cpp | 71 + .../InvariantDisks/IDBaseLinker.hpp | 44 + .../InvariantDisks/InvariantDisks/IDCLI.cpp | 171 + .../InvariantDisks/InvariantDisks/IDCLI.hpp | 44 + .../InvariantDisks/IDDAHandlerIdle.cpp | 49 + .../InvariantDisks/IDDAHandlerIdle.hpp | 44 + .../IDDiskArbitrationDispatcher.cpp | 106 + .../IDDiskArbitrationDispatcher.hpp | 55 + .../IDDiskArbitrationHandler.hpp | 42 + .../InvariantDisks/IDDiskArbitrationUtils.cpp | 272 + .../InvariantDisks/IDDiskArbitrationUtils.hpp | 61 + .../InvariantDisks/IDDiskInfoLogger.cpp | 47 + .../InvariantDisks/IDDiskInfoLogger.hpp | 39 + .../InvariantDisks/IDDispatchUtils.cpp | 44 + .../InvariantDisks/IDDispatchUtils.hpp | 41 + .../InvariantDisks/IDException.cpp | 17 + .../InvariantDisks/IDException.hpp | 83 + .../InvariantDisks/IDFileUtils.hpp | 27 + .../InvariantDisks/IDFileUtils.mm | 92 + .../InvariantDisks/IDImagePathLinker.cpp | 39 + .../InvariantDisks/IDImagePathLinker.hpp | 30 + .../InvariantDisks/IDLogUtils.cpp | 181 + .../InvariantDisks/IDLogUtils.hpp | 78 + .../InvariantDisks/IDMediaPathLinker.cpp | 43 + .../InvariantDisks/IDMediaPathLinker.hpp | 30 + .../InvariantDisks/IDSerialLinker.cpp | 80 + .../InvariantDisks/IDSerialLinker.hpp | 33 + .../InvariantDisks/IDSymlinkHandle.cpp | 75 + .../InvariantDisks/IDSymlinkHandle.hpp | 53 + .../InvariantDisks/IDUUIDLinker.cpp | 46 + .../InvariantDisks/IDUUIDLinker.hpp | 30 + .../InvariantDisks/InvariantDisks/Makefile.am | 48 + .../InvariantDisks/git-version.h | 3 + .../InvariantDisks/InvariantDisks/main.cpp | 40 + cmd/os/macos/InvariantDisks/Makefile.am | 1 + .../InvariantDisks/OPENSOLARIS.LICENSE.txt | 384 + cmd/os/macos/InvariantDisks/README.md | 36 + .../net.the-color-black.InvariantDisks.plist | 16 + cmd/os/macos/Makefile.am | 2 + cmd/os/macos/zconfigd/.gitignore | 1 + cmd/os/macos/zconfigd/Makefile.am | 15 + cmd/os/macos/zconfigd/zconfigd.c | 218 + cmd/os/macos/zconfigd/zconfigd.h | 43 + cmd/os/macos/zfs_util/.gitignore | 1 + .../zfs_util/English.lproj/InfoPlist.strings | 14 + cmd/os/macos/zfs_util/Info.plist | 131 + cmd/os/macos/zfs_util/Makefile.am | 68 + cmd/os/macos/zfs_util/PkgInfo | 1 + cmd/os/macos/zfs_util/zfs_util.c | 1065 +++ cmd/zed/zed.d/Makefile.am | 12 +- cmd/zed/zed.d/snapshot_mount.sh | 29 + cmd/zed/zed.d/snapshot_unmount.sh | 1 + cmd/zed/zed.d/zvol.create.sh | 27 + cmd/zed/zed.d/zvol.remove.sh | 22 + cmd/zfs/zfs_main.c | 12 +- cmd/zpool/Makefile.am | 2 +- cmd/zpool/os/macos/zpool_vdev_os.c | 194 + config/macos.m4 | 11 + configure.ac | 12 +- contrib/macOS/VolumeIcon.icns | Bin 0 -> 1753264 bytes contrib/macOS/pkg-scripts/postinstall | 51 + .../pkg-scripts/postinstall_actions/kextspost | 44 + .../postinstall_actions/loadplists | 29 + .../pkg-scripts/postinstall_actions/startzed | 20 + contrib/macOS/pkg-scripts/preinstall | 51 + .../pkg-scripts/preinstall_actions/0uninstall | 236 + .../preinstall_actions/lookforzpool | 34 + .../pkg-scripts/preinstall_actions/unloadzfs | 42 + contrib/macOS/product-scripts/poolcheck.sh | 11 + contrib/macOS/product-scripts/zevocheck.sh | 18 + .../resources/English.lproj/Conclusion.rtf | 70 + .../macOS/resources/English.lproj/License.txt | 828 +++ .../English.lproj/Localizable.strings | Bin 0 -> 1044 bytes .../macOS/resources/English.lproj/ReadMe.rtf | 54 + .../macOS/resources/English.lproj/Welcome.rtf | 48 + contrib/macOS/resources/background.png | Bin 0 -> 20428 bytes contrib/macOS/resources/javascript.js | 200 + etc/launchd/Makefile.am | 1 + etc/launchd/daemons/.gitignore | 5 + etc/launchd/daemons/Makefile.am | 38 + .../org.openzfsonosx.InvariantDisks.plist.in | 16 + .../org.openzfsonosx.zconfigd.plist.in | 20 + .../daemons/org.openzfsonosx.zed.plist.in | 21 + ...org.openzfsonosx.zpool-import-all.plist.in | 18 + .../org.openzfsonosx.zpool-import.plist.in | 24 + etc/launchd/launchd.d/.gitignore | 1 + etc/launchd/launchd.d/Makefile.am | 23 + etc/launchd/launchd.d/zpool-import-all.sh.in | 47 + etc/paths.d/Makefile.am | 19 + etc/paths.d/zfs.in | 2 + include/os/macos/Makefile.am | 1 + include/os/macos/spl/Makefile.am | 1 + include/os/macos/spl/ia32/sys/asm_linkage.h | 297 + include/os/macos/spl/libkern/libkern.h | 43 + include/os/macos/spl/linux/init.h | 26 + include/os/macos/spl/linux/kernel.h | 25 + include/os/macos/spl/linux/module.h | 28 + include/os/macos/spl/rpc/Makefile.am | 3 + include/os/macos/spl/rpc/types.h | 32 + include/os/macos/spl/rpc/xdr.h | 175 + include/os/macos/spl/stddef.h | 38 + include/os/macos/spl/string.h | 54 + include/os/macos/spl/sys/Makefile.am | 49 + include/os/macos/spl/sys/acl.h | 127 + include/os/macos/spl/sys/atomic.h | 288 + include/os/macos/spl/sys/byteorder.h | 70 + include/os/macos/spl/sys/callb.h | 66 + include/os/macos/spl/sys/cmn_err.h | 54 + include/os/macos/spl/sys/condvar.h | 105 + include/os/macos/spl/sys/console.h | 41 + include/os/macos/spl/sys/cred.h | 71 + include/os/macos/spl/sys/ctype.h | 27 + include/os/macos/spl/sys/debug.h | 270 + include/os/macos/spl/sys/disp.h | 25 + include/os/macos/spl/sys/dkio.h | 527 ++ include/os/macos/spl/sys/errno.h | 29 + include/os/macos/spl/sys/fcntl.h | 46 + include/os/macos/spl/sys/file.h | 65 + include/os/macos/spl/sys/inttypes.h | 31 + include/os/macos/spl/sys/isa_defs.h | 690 ++ include/os/macos/spl/sys/kmem.h | 155 + include/os/macos/spl/sys/kmem_cache.h | 25 + include/os/macos/spl/sys/kmem_impl.h | 494 ++ include/os/macos/spl/sys/kstat.h | 223 + include/os/macos/spl/sys/list.h | 145 + include/os/macos/spl/sys/mod_os.h | 90 + include/os/macos/spl/sys/mutex.h | 156 + include/os/macos/spl/sys/param.h | 43 + include/os/macos/spl/sys/policy.h | 91 + include/os/macos/spl/sys/priv.h | 531 ++ include/os/macos/spl/sys/proc.h | 47 + include/os/macos/spl/sys/processor.h | 37 + include/os/macos/spl/sys/procfs_list.h | 64 + include/os/macos/spl/sys/random.h | 48 + include/os/macos/spl/sys/rwlock.h | 82 + include/os/macos/spl/sys/seg_kmem.h | 86 + include/os/macos/spl/sys/sha2.h | 155 + include/os/macos/spl/sys/sid.h | 104 + include/os/macos/spl/sys/signal.h | 59 + include/os/macos/spl/sys/simd.h | 736 ++ include/os/macos/spl/sys/strings.h | 27 + include/os/macos/spl/sys/stropts.h | 247 + include/os/macos/spl/sys/sunddi.h | 204 + include/os/macos/spl/sys/sysmacros.h | 270 + include/os/macos/spl/sys/systeminfo.h | 40 + include/os/macos/spl/sys/systm.h | 36 + include/os/macos/spl/sys/taskq.h | 123 + include/os/macos/spl/sys/taskq_impl.h | 181 + include/os/macos/spl/sys/thread.h | 149 + include/os/macos/spl/sys/time.h | 90 + include/os/macos/spl/sys/timer.h | 88 + include/os/macos/spl/sys/trace.h | 26 + include/os/macos/spl/sys/tsd.h | 54 + include/os/macos/spl/sys/types.h | 128 + include/os/macos/spl/sys/types32.h | 30 + include/os/macos/spl/sys/uio.h | 177 + include/os/macos/spl/sys/utsname.h | 48 + include/os/macos/spl/sys/varargs.h | 32 + include/os/macos/spl/sys/vfs.h | 84 + include/os/macos/spl/sys/vmem.h | 178 + include/os/macos/spl/sys/vmem_impl.h | 155 + include/os/macos/spl/sys/vmsystm.h | 35 + include/os/macos/spl/sys/vnode.h | 266 + include/os/macos/spl/sys/zmod.h | 122 + include/os/macos/spl/sys/zone.h | 38 + include/os/macos/zfs/Makefile.am | 1 + include/os/macos/zfs/sys/Makefile.am | 10 + include/os/macos/zfs/sys/ZFSDataset.h | 142 + include/os/macos/zfs/sys/ZFSDatasetProxy.h | 82 + include/os/macos/zfs/sys/ZFSDatasetScheme.h | 126 + include/os/macos/zfs/sys/ZFSPool.h | 127 + include/os/macos/zfs/sys/finderinfo.h | 36 + include/os/macos/zfs/sys/hfs_internal.h | 191 + include/os/macos/zfs/sys/kstat_osx.h | 385 + include/os/macos/zfs/sys/ldi_buf.h | 77 + include/os/macos/zfs/sys/ldi_impl_osx.h | 226 + include/os/macos/zfs/sys/ldi_osx.h | 153 + include/os/macos/zfs/sys/trace_zfs.h | 68 + include/os/macos/zfs/sys/vdev_disk_os.h | 44 + include/os/macos/zfs/sys/zfs_boot.h | 53 + include/os/macos/zfs/sys/zfs_bootenv_os.h | 25 + include/os/macos/zfs/sys/zfs_context_os.h | 193 + include/os/macos/zfs/sys/zfs_ctldir.h | 124 + include/os/macos/zfs/sys/zfs_dir.h | 82 + include/os/macos/zfs/sys/zfs_ioctl_compat.h | 215 + include/os/macos/zfs/sys/zfs_mount.h | 59 + include/os/macos/zfs/sys/zfs_vfsops_os.h | 295 + include/os/macos/zfs/sys/zfs_vnops.h | 250 + include/os/macos/zfs/sys/zfs_znode_impl.h | 234 + include/os/macos/zfs/sys/zpl.h | 27 + include/os/macos/zfs/sys/zvolIO.h | 142 + include/os/macos/zfs/sys/zvol_os.h | 83 + include/sys/abd_impl.h | 6 +- include/sys/fs/zfs.h | 3 +- include/sys/mntent.h | 4 +- include/sys/sysevent/dev.h | 2 +- include/sys/zfs_sa.h | 9 +- lib/Makefile.am | 2 +- lib/libefi/rdwr_efi_macos.c | 247 +- lib/libshare/os/macos/nfs.c | 431 ++ lib/libshare/os/macos/smb.c | 128 + lib/libspl/include/os/Makefile.am | 4 + lib/libspl/include/os/macos/Makefile.am | 1 + lib/libspl/include/os/macos/dirent.h | 37 + .../include/os/macos/ia32/sys/asm_linkage.h | 297 + lib/libspl/include/os/macos/libdiskmgt.h | 84 + lib/libspl/include/os/macos/mach/Makefile.am | 3 + lib/libspl/include/os/macos/mach/boolean.h | 26 + lib/libspl/include/os/macos/mach/task.h | 29 + lib/libspl/include/os/macos/mntent.h | 144 + lib/libspl/include/os/macos/poll.h | 31 + lib/libspl/include/os/macos/rpc/Makefile.am | 3 + lib/libspl/include/os/macos/rpc/xdr.h | 38 + lib/libspl/include/os/macos/stdio.h | 34 + lib/libspl/include/os/macos/stdlib.h | 29 + lib/libspl/include/os/macos/string.h | 40 + lib/libspl/include/os/macos/synch.h | 81 + lib/libspl/include/os/macos/sys/Makefile.am | 17 + lib/libspl/include/os/macos/sys/byteorder.h | 288 + lib/libspl/include/os/macos/sys/errno.h | 31 + lib/libspl/include/os/macos/sys/fcntl.h | 42 + lib/libspl/include/os/macos/sys/file.h | 46 + .../include/os/macos/sys/kernel_types.h | 43 + lib/libspl/include/os/macos/sys/mnttab.h | 86 + lib/libspl/include/os/macos/sys/mount.h | 87 + lib/libspl/include/os/macos/sys/param.h | 62 + lib/libspl/include/os/macos/sys/stat.h | 77 + lib/libspl/include/os/macos/sys/sysmacros.h | 105 + lib/libspl/include/os/macos/sys/time.h | 79 + lib/libspl/include/os/macos/sys/uio.h | 175 + lib/libspl/include/os/macos/sys/vfs.h | 26 + lib/libspl/include/os/macos/sys/xattr.h | 35 + .../include/os/macos/sys/zfs_context_os.h | 45 + lib/libspl/include/os/macos/time.h | 74 + lib/libspl/include/os/macos/unistd.h | 79 + lib/libspl/include/sys/isa_defs.h | 6 + lib/libspl/os/macos/getexecname.c | 31 + lib/libspl/os/macos/gethostid.c | 38 + lib/libspl/os/macos/getmntany.c | 487 ++ lib/libspl/os/macos/zone.c | 28 + lib/libzfs/Makefile.am | 2 + lib/libzfs/libzfs_dataset.c | 23 +- lib/libzfs/libzfs_diff.c | 5 + lib/libzfs/libzfs_sendrecv.c | 27 + lib/libzfs/os/macos/libzfs_mount_os.c | 732 ++ lib/libzfs/os/macos/libzfs_pool_os.c | 345 + lib/libzfs/os/macos/libzfs_util_os.c | 725 ++ lib/libzutil/os/macos/zutil_compat.c | 94 + lib/libzutil/os/macos/zutil_device_path_os.c | 207 + lib/libzutil/os/macos/zutil_import_os.c | 614 ++ lib/os/Makefile.am | 4 + lib/os/macos/Makefile.am | 2 + lib/os/macos/libdiskmgt/Makefile.am | 24 + lib/os/macos/libdiskmgt/disks_private.h | 82 + lib/os/macos/libdiskmgt/diskutil.c | 379 + lib/os/macos/libdiskmgt/dm.c | 39 + lib/os/macos/libdiskmgt/entry.c | 332 + lib/os/macos/libdiskmgt/inuse_corestorage.c | 98 + lib/os/macos/libdiskmgt/inuse_fs.c | 77 + lib/os/macos/libdiskmgt/inuse_macswap.c | 75 + lib/os/macos/libdiskmgt/inuse_mnt.c | 54 + lib/os/macos/libdiskmgt/inuse_partition.c | 74 + lib/os/macos/libdiskmgt/inuse_zpool.c | 162 + lib/os/macos/libdiskmgt/libdiskmgt.c | 39 + lib/os/macos/libdiskmgt/slice.c | 105 + .../icp/asm-x86_64/os/macos/aes/aes_aesni.S | 855 +++ .../icp/asm-x86_64/os/macos/aes/aes_amd64.S | 900 +++ .../os/macos/modes/aesni-gcm-x86_64.S | 1274 ++++ .../asm-x86_64/os/macos/modes/gcm_pclmulqdq.S | 334 + .../asm-x86_64/os/macos/modes/ghash-x86_64.S | 724 ++ .../asm-x86_64/os/macos/sha1/sha1-x86_64.S | 1353 ++++ .../asm-x86_64/os/macos/sha2/sha256_impl.S | 2058 ++++++ .../asm-x86_64/os/macos/sha2/sha512_impl.S | 2082 ++++++ module/lua/setjmp/setjmp_aarch64.S | 5 +- module/os/linux/spl/spl-taskq.c | 3 +- module/os/macos/.gitignore | 1 + module/os/macos/Makefile.am | 6 + module/os/macos/README.md | 8 + module/os/macos/spl/Makefile.am | 69 + module/os/macos/spl/README.md | 14 + module/os/macos/spl/spl-atomic.c | 50 + module/os/macos/spl/spl-condvar.c | 252 + module/os/macos/spl/spl-cred.c | 153 + module/os/macos/spl/spl-ddi.c | 408 ++ module/os/macos/spl/spl-debug.c | 10 + module/os/macos/spl/spl-err.c | 83 + module/os/macos/spl/spl-kmem.c | 6524 +++++++++++++++++ module/os/macos/spl/spl-kstat.c | 1252 ++++ module/os/macos/spl/spl-list.c | 197 + module/os/macos/spl/spl-mutex.c | 433 ++ module/os/macos/spl/spl-osx.c | 518 ++ module/os/macos/spl/spl-policy.c | 185 + module/os/macos/spl/spl-proc.c | 30 + module/os/macos/spl/spl-proc_list.c | 157 + module/os/macos/spl/spl-processor.c | 88 + module/os/macos/spl/spl-qsort.c | 178 + module/os/macos/spl/spl-rwlock.c | 402 + module/os/macos/spl/spl-seg_kmem.c | 282 + module/os/macos/spl/spl-taskq.c | 2902 ++++++++ module/os/macos/spl/spl-thread.c | 292 + module/os/macos/spl/spl-time.c | 138 + module/os/macos/spl/spl-tsd.c | 389 + module/os/macos/spl/spl-uio.c | 139 + module/os/macos/spl/spl-vmem.c | 3923 ++++++++++ module/os/macos/spl/spl-vnode.c | 442 ++ module/os/macos/spl/spl-xdr.c | 524 ++ module/os/macos/spl/spl-zlib.c | 199 + module/os/macos/zfs/.gitignore | 2 + module/os/macos/zfs/Info.plist | 113 + module/os/macos/zfs/InfoPlist.strings | 5 + module/os/macos/zfs/Makefile.am | 364 + module/os/macos/zfs/ZFSDataset.cpp | 854 +++ module/os/macos/zfs/ZFSDatasetProxy.cpp | 466 ++ module/os/macos/zfs/ZFSDatasetScheme.cpp | 1108 +++ module/os/macos/zfs/ZFSPool.cpp | 868 +++ module/os/macos/zfs/abd_os.c | 473 ++ module/os/macos/zfs/arc_os.c | 777 ++ module/os/macos/zfs/ldi_iokit.cpp | 2015 +++++ module/os/macos/zfs/ldi_osx.c | 2432 ++++++ module/os/macos/zfs/ldi_vnode.c | 1022 +++ module/os/macos/zfs/policy.c | 354 + module/os/macos/zfs/qat.c | 105 + module/os/macos/zfs/qat_compress.c | 569 ++ module/os/macos/zfs/qat_crypt.c | 630 ++ module/os/macos/zfs/spa_misc_os.c | 116 + module/os/macos/zfs/trace.c | 50 + module/os/macos/zfs/vdev_disk.c | 797 ++ module/os/macos/zfs/vdev_file.c | 338 + module/os/macos/zfs/zfs_acl.c | 2826 +++++++ module/os/macos/zfs/zfs_boot.cpp | 2979 ++++++++ module/os/macos/zfs/zfs_ctldir.c | 1517 ++++ module/os/macos/zfs/zfs_debug.c | 264 + module/os/macos/zfs/zfs_dir.c | 1218 +++ module/os/macos/zfs/zfs_file_os.c | 434 ++ module/os/macos/zfs/zfs_fuid_os.c | 52 + module/os/macos/zfs/zfs_ioctl_os.c | 379 + module/os/macos/zfs/zfs_kstat_osx.c | 899 +++ module/os/macos/zfs/zfs_osx.cpp | 309 + module/os/macos/zfs/zfs_racct.c | 32 + module/os/macos/zfs/zfs_vfsops.c | 3001 ++++++++ module/os/macos/zfs/zfs_vnops.c | 3685 ++++++++++ module/os/macos/zfs/zfs_vnops_osx.c | 5397 ++++++++++++++ module/os/macos/zfs/zfs_vnops_osx_lib.c | 2235 ++++++ module/os/macos/zfs/zfs_znode.c | 2350 ++++++ module/os/macos/zfs/zio_crypt.c | 2050 ++++++ module/os/macos/zfs/zvolIO.cpp | 1174 +++ module/os/macos/zfs/zvol_os.c | 1149 +++ module/zcommon/zfs_fletcher.c | 2 +- module/zcommon/zfs_prop.c | 7 +- module/zcommon/zprop_common.c | 3 +- module/zfs/arc.c | 10 + module/zfs/dmu.c | 9 +- module/zfs/metaslab.c | 10 +- module/zfs/spa.c | 14 +- module/zfs/vdev_raidz_math.c | 2 +- module/zfs/zfs_fm.c | 3 +- module/zfs/zfs_log.c | 10 +- module/zfs/zfs_replay.c | 2 +- module/zfs/zfs_sa.c | 6 +- module/zfs/zio.c | 2 + module/zstd/lib/zstd.c | 17 +- scripts/cmd-macos.sh | 67 + scripts/debug-macos.sh | 67 + scripts/load_macos.sh | 17 + scripts/pkg_macos.sh | 456 ++ 370 files changed, 103599 insertions(+), 190 deletions(-) create mode 100644 appveryor.yml create mode 100644 cmd/os/Makefile.am create mode 100644 cmd/os/macos/InvariantDisks/.gitignore create mode 100644 cmd/os/macos/InvariantDisks/BSD.LICENSE.md create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks.xcodeproj/project.pbxproj create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDBaseLinker.cpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDBaseLinker.hpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDCLI.cpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDCLI.hpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDDAHandlerIdle.cpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDDAHandlerIdle.hpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationDispatcher.cpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationDispatcher.hpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationHandler.hpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationUtils.cpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationUtils.hpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskInfoLogger.cpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskInfoLogger.hpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDDispatchUtils.cpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDDispatchUtils.hpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDException.cpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDException.hpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDFileUtils.hpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDFileUtils.mm create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDImagePathLinker.cpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDImagePathLinker.hpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDLogUtils.cpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDLogUtils.hpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDMediaPathLinker.cpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDMediaPathLinker.hpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDSerialLinker.cpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDSerialLinker.hpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDSymlinkHandle.cpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDSymlinkHandle.hpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDUUIDLinker.cpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/IDUUIDLinker.hpp create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/Makefile.am create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/git-version.h create mode 100644 cmd/os/macos/InvariantDisks/InvariantDisks/main.cpp create mode 100644 cmd/os/macos/InvariantDisks/Makefile.am create mode 100644 cmd/os/macos/InvariantDisks/OPENSOLARIS.LICENSE.txt create mode 100644 cmd/os/macos/InvariantDisks/README.md create mode 100644 cmd/os/macos/InvariantDisks/launchd/net.the-color-black.InvariantDisks.plist create mode 100644 cmd/os/macos/Makefile.am create mode 100644 cmd/os/macos/zconfigd/.gitignore create mode 100644 cmd/os/macos/zconfigd/Makefile.am create mode 100644 cmd/os/macos/zconfigd/zconfigd.c create mode 100644 cmd/os/macos/zconfigd/zconfigd.h create mode 100644 cmd/os/macos/zfs_util/.gitignore create mode 100644 cmd/os/macos/zfs_util/English.lproj/InfoPlist.strings create mode 100644 cmd/os/macos/zfs_util/Info.plist create mode 100644 cmd/os/macos/zfs_util/Makefile.am create mode 100644 cmd/os/macos/zfs_util/PkgInfo create mode 100644 cmd/os/macos/zfs_util/zfs_util.c create mode 100644 cmd/zed/zed.d/snapshot_mount.sh create mode 120000 cmd/zed/zed.d/snapshot_unmount.sh create mode 100755 cmd/zed/zed.d/zvol.create.sh create mode 100755 cmd/zed/zed.d/zvol.remove.sh create mode 100644 cmd/zpool/os/macos/zpool_vdev_os.c create mode 100644 config/macos.m4 create mode 100644 contrib/macOS/VolumeIcon.icns create mode 100755 contrib/macOS/pkg-scripts/postinstall create mode 100755 contrib/macOS/pkg-scripts/postinstall_actions/kextspost create mode 100755 contrib/macOS/pkg-scripts/postinstall_actions/loadplists create mode 100755 contrib/macOS/pkg-scripts/postinstall_actions/startzed create mode 100755 contrib/macOS/pkg-scripts/preinstall create mode 100755 contrib/macOS/pkg-scripts/preinstall_actions/0uninstall create mode 100755 contrib/macOS/pkg-scripts/preinstall_actions/lookforzpool create mode 100755 contrib/macOS/pkg-scripts/preinstall_actions/unloadzfs create mode 100755 contrib/macOS/product-scripts/poolcheck.sh create mode 100755 contrib/macOS/product-scripts/zevocheck.sh create mode 100644 contrib/macOS/resources/English.lproj/Conclusion.rtf create mode 100644 contrib/macOS/resources/English.lproj/License.txt create mode 100644 contrib/macOS/resources/English.lproj/Localizable.strings create mode 100644 contrib/macOS/resources/English.lproj/ReadMe.rtf create mode 100644 contrib/macOS/resources/English.lproj/Welcome.rtf create mode 100644 contrib/macOS/resources/background.png create mode 100644 contrib/macOS/resources/javascript.js create mode 100644 etc/launchd/Makefile.am create mode 100644 etc/launchd/daemons/.gitignore create mode 100644 etc/launchd/daemons/Makefile.am create mode 100644 etc/launchd/daemons/org.openzfsonosx.InvariantDisks.plist.in create mode 100644 etc/launchd/daemons/org.openzfsonosx.zconfigd.plist.in create mode 100644 etc/launchd/daemons/org.openzfsonosx.zed.plist.in create mode 100644 etc/launchd/daemons/org.openzfsonosx.zpool-import-all.plist.in create mode 100644 etc/launchd/daemons/org.openzfsonosx.zpool-import.plist.in create mode 100644 etc/launchd/launchd.d/.gitignore create mode 100644 etc/launchd/launchd.d/Makefile.am create mode 100755 etc/launchd/launchd.d/zpool-import-all.sh.in create mode 100644 etc/paths.d/Makefile.am create mode 100644 etc/paths.d/zfs.in create mode 100644 include/os/macos/Makefile.am create mode 100644 include/os/macos/spl/Makefile.am create mode 100644 include/os/macos/spl/ia32/sys/asm_linkage.h create mode 100644 include/os/macos/spl/libkern/libkern.h create mode 100644 include/os/macos/spl/linux/init.h create mode 100644 include/os/macos/spl/linux/kernel.h create mode 100644 include/os/macos/spl/linux/module.h create mode 100644 include/os/macos/spl/rpc/Makefile.am create mode 100644 include/os/macos/spl/rpc/types.h create mode 100644 include/os/macos/spl/rpc/xdr.h create mode 100644 include/os/macos/spl/stddef.h create mode 100644 include/os/macos/spl/string.h create mode 100644 include/os/macos/spl/sys/Makefile.am create mode 100644 include/os/macos/spl/sys/acl.h create mode 100644 include/os/macos/spl/sys/atomic.h create mode 100644 include/os/macos/spl/sys/byteorder.h create mode 100644 include/os/macos/spl/sys/callb.h create mode 100644 include/os/macos/spl/sys/cmn_err.h create mode 100644 include/os/macos/spl/sys/condvar.h create mode 100644 include/os/macos/spl/sys/console.h create mode 100644 include/os/macos/spl/sys/cred.h create mode 100644 include/os/macos/spl/sys/ctype.h create mode 100644 include/os/macos/spl/sys/debug.h create mode 100644 include/os/macos/spl/sys/disp.h create mode 100644 include/os/macos/spl/sys/dkio.h create mode 100644 include/os/macos/spl/sys/errno.h create mode 100644 include/os/macos/spl/sys/fcntl.h create mode 100644 include/os/macos/spl/sys/file.h create mode 100644 include/os/macos/spl/sys/inttypes.h create mode 100644 include/os/macos/spl/sys/isa_defs.h create mode 100644 include/os/macos/spl/sys/kmem.h create mode 100644 include/os/macos/spl/sys/kmem_cache.h create mode 100644 include/os/macos/spl/sys/kmem_impl.h create mode 100644 include/os/macos/spl/sys/kstat.h create mode 100644 include/os/macos/spl/sys/list.h create mode 100644 include/os/macos/spl/sys/mod_os.h create mode 100644 include/os/macos/spl/sys/mutex.h create mode 100644 include/os/macos/spl/sys/param.h create mode 100644 include/os/macos/spl/sys/policy.h create mode 100644 include/os/macos/spl/sys/priv.h create mode 100644 include/os/macos/spl/sys/proc.h create mode 100644 include/os/macos/spl/sys/processor.h create mode 100644 include/os/macos/spl/sys/procfs_list.h create mode 100644 include/os/macos/spl/sys/random.h create mode 100644 include/os/macos/spl/sys/rwlock.h create mode 100644 include/os/macos/spl/sys/seg_kmem.h create mode 100644 include/os/macos/spl/sys/sha2.h create mode 100644 include/os/macos/spl/sys/sid.h create mode 100644 include/os/macos/spl/sys/signal.h create mode 100644 include/os/macos/spl/sys/simd.h create mode 100644 include/os/macos/spl/sys/strings.h create mode 100644 include/os/macos/spl/sys/stropts.h create mode 100644 include/os/macos/spl/sys/sunddi.h create mode 100644 include/os/macos/spl/sys/sysmacros.h create mode 100644 include/os/macos/spl/sys/systeminfo.h create mode 100644 include/os/macos/spl/sys/systm.h create mode 100644 include/os/macos/spl/sys/taskq.h create mode 100644 include/os/macos/spl/sys/taskq_impl.h create mode 100644 include/os/macos/spl/sys/thread.h create mode 100644 include/os/macos/spl/sys/time.h create mode 100644 include/os/macos/spl/sys/timer.h create mode 100644 include/os/macos/spl/sys/trace.h create mode 100644 include/os/macos/spl/sys/tsd.h create mode 100644 include/os/macos/spl/sys/types.h create mode 100644 include/os/macos/spl/sys/types32.h create mode 100644 include/os/macos/spl/sys/uio.h create mode 100644 include/os/macos/spl/sys/utsname.h create mode 100644 include/os/macos/spl/sys/varargs.h create mode 100644 include/os/macos/spl/sys/vfs.h create mode 100644 include/os/macos/spl/sys/vmem.h create mode 100644 include/os/macos/spl/sys/vmem_impl.h create mode 100644 include/os/macos/spl/sys/vmsystm.h create mode 100644 include/os/macos/spl/sys/vnode.h create mode 100644 include/os/macos/spl/sys/zmod.h create mode 100644 include/os/macos/spl/sys/zone.h create mode 100644 include/os/macos/zfs/Makefile.am create mode 100644 include/os/macos/zfs/sys/Makefile.am create mode 100644 include/os/macos/zfs/sys/ZFSDataset.h create mode 100644 include/os/macos/zfs/sys/ZFSDatasetProxy.h create mode 100644 include/os/macos/zfs/sys/ZFSDatasetScheme.h create mode 100644 include/os/macos/zfs/sys/ZFSPool.h create mode 100644 include/os/macos/zfs/sys/finderinfo.h create mode 100644 include/os/macos/zfs/sys/hfs_internal.h create mode 100644 include/os/macos/zfs/sys/kstat_osx.h create mode 100644 include/os/macos/zfs/sys/ldi_buf.h create mode 100644 include/os/macos/zfs/sys/ldi_impl_osx.h create mode 100644 include/os/macos/zfs/sys/ldi_osx.h create mode 100644 include/os/macos/zfs/sys/trace_zfs.h create mode 100644 include/os/macos/zfs/sys/vdev_disk_os.h create mode 100644 include/os/macos/zfs/sys/zfs_boot.h create mode 100644 include/os/macos/zfs/sys/zfs_bootenv_os.h create mode 100644 include/os/macos/zfs/sys/zfs_context_os.h create mode 100644 include/os/macos/zfs/sys/zfs_ctldir.h create mode 100644 include/os/macos/zfs/sys/zfs_dir.h create mode 100644 include/os/macos/zfs/sys/zfs_ioctl_compat.h create mode 100644 include/os/macos/zfs/sys/zfs_mount.h create mode 100644 include/os/macos/zfs/sys/zfs_vfsops_os.h create mode 100644 include/os/macos/zfs/sys/zfs_vnops.h create mode 100644 include/os/macos/zfs/sys/zfs_znode_impl.h create mode 100644 include/os/macos/zfs/sys/zpl.h create mode 100644 include/os/macos/zfs/sys/zvolIO.h create mode 100644 include/os/macos/zfs/sys/zvol_os.h create mode 100644 lib/libshare/os/macos/nfs.c create mode 100644 lib/libshare/os/macos/smb.c create mode 100644 lib/libspl/include/os/macos/Makefile.am create mode 100644 lib/libspl/include/os/macos/dirent.h create mode 100644 lib/libspl/include/os/macos/ia32/sys/asm_linkage.h create mode 100644 lib/libspl/include/os/macos/libdiskmgt.h create mode 100644 lib/libspl/include/os/macos/mach/Makefile.am create mode 100644 lib/libspl/include/os/macos/mach/boolean.h create mode 100644 lib/libspl/include/os/macos/mach/task.h create mode 100644 lib/libspl/include/os/macos/mntent.h create mode 100644 lib/libspl/include/os/macos/poll.h create mode 100644 lib/libspl/include/os/macos/rpc/Makefile.am create mode 100644 lib/libspl/include/os/macos/rpc/xdr.h create mode 100644 lib/libspl/include/os/macos/stdio.h create mode 100644 lib/libspl/include/os/macos/stdlib.h create mode 100644 lib/libspl/include/os/macos/string.h create mode 100644 lib/libspl/include/os/macos/synch.h create mode 100644 lib/libspl/include/os/macos/sys/Makefile.am create mode 100644 lib/libspl/include/os/macos/sys/byteorder.h create mode 100644 lib/libspl/include/os/macos/sys/errno.h create mode 100644 lib/libspl/include/os/macos/sys/fcntl.h create mode 100644 lib/libspl/include/os/macos/sys/file.h create mode 100644 lib/libspl/include/os/macos/sys/kernel_types.h create mode 100644 lib/libspl/include/os/macos/sys/mnttab.h create mode 100644 lib/libspl/include/os/macos/sys/mount.h create mode 100644 lib/libspl/include/os/macos/sys/param.h create mode 100644 lib/libspl/include/os/macos/sys/stat.h create mode 100644 lib/libspl/include/os/macos/sys/sysmacros.h create mode 100644 lib/libspl/include/os/macos/sys/time.h create mode 100644 lib/libspl/include/os/macos/sys/uio.h create mode 100644 lib/libspl/include/os/macos/sys/vfs.h create mode 100644 lib/libspl/include/os/macos/sys/xattr.h create mode 100644 lib/libspl/include/os/macos/sys/zfs_context_os.h create mode 100644 lib/libspl/include/os/macos/time.h create mode 100644 lib/libspl/include/os/macos/unistd.h create mode 100644 lib/libspl/os/macos/getexecname.c create mode 100644 lib/libspl/os/macos/gethostid.c create mode 100644 lib/libspl/os/macos/getmntany.c create mode 100644 lib/libspl/os/macos/zone.c create mode 100644 lib/libzfs/os/macos/libzfs_mount_os.c create mode 100644 lib/libzfs/os/macos/libzfs_pool_os.c create mode 100644 lib/libzfs/os/macos/libzfs_util_os.c create mode 100644 lib/libzutil/os/macos/zutil_compat.c create mode 100644 lib/libzutil/os/macos/zutil_device_path_os.c create mode 100644 lib/libzutil/os/macos/zutil_import_os.c create mode 100644 lib/os/Makefile.am create mode 100644 lib/os/macos/Makefile.am create mode 100644 lib/os/macos/libdiskmgt/Makefile.am create mode 100644 lib/os/macos/libdiskmgt/disks_private.h create mode 100644 lib/os/macos/libdiskmgt/diskutil.c create mode 100644 lib/os/macos/libdiskmgt/dm.c create mode 100644 lib/os/macos/libdiskmgt/entry.c create mode 100644 lib/os/macos/libdiskmgt/inuse_corestorage.c create mode 100644 lib/os/macos/libdiskmgt/inuse_fs.c create mode 100644 lib/os/macos/libdiskmgt/inuse_macswap.c create mode 100644 lib/os/macos/libdiskmgt/inuse_mnt.c create mode 100644 lib/os/macos/libdiskmgt/inuse_partition.c create mode 100644 lib/os/macos/libdiskmgt/inuse_zpool.c create mode 100644 lib/os/macos/libdiskmgt/libdiskmgt.c create mode 100644 lib/os/macos/libdiskmgt/slice.c create mode 100644 module/icp/asm-x86_64/os/macos/aes/aes_aesni.S create mode 100644 module/icp/asm-x86_64/os/macos/aes/aes_amd64.S create mode 100644 module/icp/asm-x86_64/os/macos/modes/aesni-gcm-x86_64.S create mode 100644 module/icp/asm-x86_64/os/macos/modes/gcm_pclmulqdq.S create mode 100644 module/icp/asm-x86_64/os/macos/modes/ghash-x86_64.S create mode 100644 module/icp/asm-x86_64/os/macos/sha1/sha1-x86_64.S create mode 100644 module/icp/asm-x86_64/os/macos/sha2/sha256_impl.S create mode 100644 module/icp/asm-x86_64/os/macos/sha2/sha512_impl.S create mode 100644 module/os/macos/.gitignore create mode 100644 module/os/macos/Makefile.am create mode 100644 module/os/macos/README.md create mode 100644 module/os/macos/spl/Makefile.am create mode 100644 module/os/macos/spl/README.md create mode 100644 module/os/macos/spl/spl-atomic.c create mode 100644 module/os/macos/spl/spl-condvar.c create mode 100644 module/os/macos/spl/spl-cred.c create mode 100644 module/os/macos/spl/spl-ddi.c create mode 100644 module/os/macos/spl/spl-debug.c create mode 100644 module/os/macos/spl/spl-err.c create mode 100644 module/os/macos/spl/spl-kmem.c create mode 100644 module/os/macos/spl/spl-kstat.c create mode 100644 module/os/macos/spl/spl-list.c create mode 100644 module/os/macos/spl/spl-mutex.c create mode 100644 module/os/macos/spl/spl-osx.c create mode 100644 module/os/macos/spl/spl-policy.c create mode 100644 module/os/macos/spl/spl-proc.c create mode 100644 module/os/macos/spl/spl-proc_list.c create mode 100644 module/os/macos/spl/spl-processor.c create mode 100644 module/os/macos/spl/spl-qsort.c create mode 100644 module/os/macos/spl/spl-rwlock.c create mode 100644 module/os/macos/spl/spl-seg_kmem.c create mode 100644 module/os/macos/spl/spl-taskq.c create mode 100644 module/os/macos/spl/spl-thread.c create mode 100644 module/os/macos/spl/spl-time.c create mode 100644 module/os/macos/spl/spl-tsd.c create mode 100644 module/os/macos/spl/spl-uio.c create mode 100644 module/os/macos/spl/spl-vmem.c create mode 100644 module/os/macos/spl/spl-vnode.c create mode 100644 module/os/macos/spl/spl-xdr.c create mode 100644 module/os/macos/spl/spl-zlib.c create mode 100644 module/os/macos/zfs/.gitignore create mode 100644 module/os/macos/zfs/Info.plist create mode 100644 module/os/macos/zfs/InfoPlist.strings create mode 100644 module/os/macos/zfs/Makefile.am create mode 100644 module/os/macos/zfs/ZFSDataset.cpp create mode 100644 module/os/macos/zfs/ZFSDatasetProxy.cpp create mode 100644 module/os/macos/zfs/ZFSDatasetScheme.cpp create mode 100644 module/os/macos/zfs/ZFSPool.cpp create mode 100644 module/os/macos/zfs/abd_os.c create mode 100644 module/os/macos/zfs/arc_os.c create mode 100644 module/os/macos/zfs/ldi_iokit.cpp create mode 100644 module/os/macos/zfs/ldi_osx.c create mode 100644 module/os/macos/zfs/ldi_vnode.c create mode 100644 module/os/macos/zfs/policy.c create mode 100644 module/os/macos/zfs/qat.c create mode 100644 module/os/macos/zfs/qat_compress.c create mode 100644 module/os/macos/zfs/qat_crypt.c create mode 100644 module/os/macos/zfs/spa_misc_os.c create mode 100644 module/os/macos/zfs/trace.c create mode 100644 module/os/macos/zfs/vdev_disk.c create mode 100644 module/os/macos/zfs/vdev_file.c create mode 100644 module/os/macos/zfs/zfs_acl.c create mode 100644 module/os/macos/zfs/zfs_boot.cpp create mode 100644 module/os/macos/zfs/zfs_ctldir.c create mode 100644 module/os/macos/zfs/zfs_debug.c create mode 100644 module/os/macos/zfs/zfs_dir.c create mode 100644 module/os/macos/zfs/zfs_file_os.c create mode 100644 module/os/macos/zfs/zfs_fuid_os.c create mode 100644 module/os/macos/zfs/zfs_ioctl_os.c create mode 100644 module/os/macos/zfs/zfs_kstat_osx.c create mode 100644 module/os/macos/zfs/zfs_osx.cpp create mode 100644 module/os/macos/zfs/zfs_racct.c create mode 100644 module/os/macos/zfs/zfs_vfsops.c create mode 100644 module/os/macos/zfs/zfs_vnops.c create mode 100644 module/os/macos/zfs/zfs_vnops_osx.c create mode 100644 module/os/macos/zfs/zfs_vnops_osx_lib.c create mode 100644 module/os/macos/zfs/zfs_znode.c create mode 100644 module/os/macos/zfs/zio_crypt.c create mode 100644 module/os/macos/zfs/zvolIO.cpp create mode 100644 module/os/macos/zfs/zvol_os.c create mode 100755 scripts/cmd-macos.sh create mode 100755 scripts/debug-macos.sh create mode 100755 scripts/load_macos.sh create mode 100755 scripts/pkg_macos.sh diff --git a/appveryor.yml b/appveryor.yml new file mode 100644 index 000000000000..725f8fd87e7c --- /dev/null +++ b/appveryor.yml @@ -0,0 +1,12 @@ +version: 1.0.{build} +branches: + only: + - macos +image: macOS +build_script: +- sh: >- + pwd + ls -l + sh autoconf.sh + ./configure CPPFLAGS="-I/usr/local/opt/gettext/include -I/usr/local/opt/openssl@1.1/include" LDFLAGS="-L/usr/local/opt/gettext/lib/ -L/usr/local/opt/openssl@1.1/lib" + make diff --git a/cmd/os/Makefile.am b/cmd/os/Makefile.am new file mode 100644 index 000000000000..a6cb6105b286 --- /dev/null +++ b/cmd/os/Makefile.am @@ -0,0 +1,4 @@ + +if BUILD_MACOS +SUBDIRS = macos +endif diff --git a/cmd/os/macos/InvariantDisks/.gitignore b/cmd/os/macos/InvariantDisks/.gitignore new file mode 100644 index 000000000000..da11ea5ebaf7 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/.gitignore @@ -0,0 +1,3 @@ +/DerivedData/ +*.xcworkspace/ +xcuserdata/ diff --git a/cmd/os/macos/InvariantDisks/BSD.LICENSE.md b/cmd/os/macos/InvariantDisks/BSD.LICENSE.md new file mode 100644 index 000000000000..37443bfcc54b --- /dev/null +++ b/cmd/os/macos/InvariantDisks/BSD.LICENSE.md @@ -0,0 +1,27 @@ +Copyright (c) 2014, Gerhard Röthlin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the Project nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks.xcodeproj/project.pbxproj b/cmd/os/macos/InvariantDisks/InvariantDisks.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..fa0ffd5589ba --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks.xcodeproj/project.pbxproj @@ -0,0 +1,375 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 700AE3C91915139C00929DAE /* IDUUIDLinker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 700AE3C71915139C00929DAE /* IDUUIDLinker.cpp */; }; + 700AE3CC191530A000929DAE /* IDSerialLinker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 700AE3CA191530A000929DAE /* IDSerialLinker.cpp */; }; + 704C39201A3C777F00929DAE /* IDDispatchUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 704C391E1A3C777F00929DAE /* IDDispatchUtils.cpp */; }; + 704C39231A3C7E9600929DAE /* IDDAHandlerIdle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 704C39211A3C7E9600929DAE /* IDDAHandlerIdle.cpp */; }; + 707E3F0E1B6CDC2400929DAE /* IDLogUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 707E3F0C1B6CDC2400929DAE /* IDLogUtils.cpp */; }; + 70C63F28190CF5E800929DAE /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70C63F27190CF5E800929DAE /* main.cpp */; }; + 70C63F32190CF78800929DAE /* IDCLI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70C63F30190CF78800929DAE /* IDCLI.cpp */; }; + 70C63F35190CF87400929DAE /* IDException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70C63F33190CF87400929DAE /* IDException.cpp */; }; + 70C63F44190CFFA500929DAE /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70C63F43190CFFA500929DAE /* CoreFoundation.framework */; }; + 70C63F46190CFFAC00929DAE /* DiskArbitration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70C63F45190CFFAC00929DAE /* DiskArbitration.framework */; }; + 70C63F4B190D1AEF00929DAE /* IDDiskArbitrationDispatcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70C63F49190D1AEF00929DAE /* IDDiskArbitrationDispatcher.cpp */; }; + 70C63F4F190D2AA000929DAE /* IDDiskInfoLogger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70C63F4D190D2AA000929DAE /* IDDiskInfoLogger.cpp */; }; + 70C63F52190D2CC900929DAE /* IDDiskArbitrationUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70C63F50190D2CC900929DAE /* IDDiskArbitrationUtils.cpp */; }; + 70C63F55190D4A4300929DAE /* IDMediaPathLinker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70C63F53190D4A4300929DAE /* IDMediaPathLinker.cpp */; }; + 70C63F58190D574000929DAE /* IDFileUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 70C63F56190D574000929DAE /* IDFileUtils.mm */; }; + 70C63F5A190D713B00929DAE /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70C63F59190D713B00929DAE /* IOKit.framework */; }; + 70C9002E1C6780EF001A40C8 /* IDImagePathLinker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70C9002C1C6780EF001A40C8 /* IDImagePathLinker.cpp */; }; + 70D4742E1BDD0E4200929DAE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70D4742D1BDD0E4200929DAE /* Foundation.framework */; }; + 70D474311BDD17E800929DAE /* IDSymlinkHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70D4742F1BDD17E800929DAE /* IDSymlinkHandle.cpp */; }; + 70D474371BDD21E100929DAE /* IDBaseLinker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70D474351BDD21E100929DAE /* IDBaseLinker.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 70C63F22190CF5E800929DAE /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 700AE3C71915139C00929DAE /* IDUUIDLinker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IDUUIDLinker.cpp; sourceTree = ""; }; + 700AE3C81915139C00929DAE /* IDUUIDLinker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IDUUIDLinker.hpp; sourceTree = ""; }; + 700AE3CA191530A000929DAE /* IDSerialLinker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IDSerialLinker.cpp; sourceTree = ""; }; + 700AE3CB191530A000929DAE /* IDSerialLinker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IDSerialLinker.hpp; sourceTree = ""; }; + 704C391E1A3C777F00929DAE /* IDDispatchUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IDDispatchUtils.cpp; sourceTree = ""; }; + 704C391F1A3C777F00929DAE /* IDDispatchUtils.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IDDispatchUtils.hpp; sourceTree = ""; }; + 704C39211A3C7E9600929DAE /* IDDAHandlerIdle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IDDAHandlerIdle.cpp; sourceTree = ""; }; + 704C39221A3C7E9600929DAE /* IDDAHandlerIdle.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IDDAHandlerIdle.hpp; sourceTree = ""; }; + 707E3F0C1B6CDC2400929DAE /* IDLogUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IDLogUtils.cpp; sourceTree = ""; }; + 707E3F0D1B6CDC2400929DAE /* IDLogUtils.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IDLogUtils.hpp; sourceTree = ""; }; + 70C63F24190CF5E800929DAE /* InvariantDisks */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = InvariantDisks; sourceTree = BUILT_PRODUCTS_DIR; }; + 70C63F27190CF5E800929DAE /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = main.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 70C63F30190CF78800929DAE /* IDCLI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = IDCLI.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 70C63F31190CF78800929DAE /* IDCLI.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; lineEnding = 0; path = IDCLI.hpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 70C63F33190CF87400929DAE /* IDException.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = IDException.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 70C63F34190CF87400929DAE /* IDException.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; lineEnding = 0; path = IDException.hpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 70C63F43190CFFA500929DAE /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 70C63F45190CFFAC00929DAE /* DiskArbitration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DiskArbitration.framework; path = System/Library/Frameworks/DiskArbitration.framework; sourceTree = SDKROOT; }; + 70C63F49190D1AEF00929DAE /* IDDiskArbitrationDispatcher.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = IDDiskArbitrationDispatcher.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 70C63F4A190D1AEF00929DAE /* IDDiskArbitrationDispatcher.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; lineEnding = 0; path = IDDiskArbitrationDispatcher.hpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 70C63F4C190D1B4200929DAE /* IDDiskArbitrationHandler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; lineEnding = 0; path = IDDiskArbitrationHandler.hpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 70C63F4D190D2AA000929DAE /* IDDiskInfoLogger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = IDDiskInfoLogger.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 70C63F4E190D2AA000929DAE /* IDDiskInfoLogger.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; lineEnding = 0; path = IDDiskInfoLogger.hpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 70C63F50190D2CC900929DAE /* IDDiskArbitrationUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = IDDiskArbitrationUtils.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 70C63F51190D2CC900929DAE /* IDDiskArbitrationUtils.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; lineEnding = 0; path = IDDiskArbitrationUtils.hpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 70C63F53190D4A4300929DAE /* IDMediaPathLinker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IDMediaPathLinker.cpp; sourceTree = ""; }; + 70C63F54190D4A4300929DAE /* IDMediaPathLinker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IDMediaPathLinker.hpp; sourceTree = ""; }; + 70C63F56190D574000929DAE /* IDFileUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = IDFileUtils.mm; sourceTree = ""; }; + 70C63F57190D574000929DAE /* IDFileUtils.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IDFileUtils.hpp; sourceTree = ""; }; + 70C63F59190D713B00929DAE /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; + 70C9002C1C6780EF001A40C8 /* IDImagePathLinker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IDImagePathLinker.cpp; sourceTree = ""; }; + 70C9002D1C6780EF001A40C8 /* IDImagePathLinker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IDImagePathLinker.hpp; sourceTree = ""; }; + 70D4742D1BDD0E4200929DAE /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 70D4742F1BDD17E800929DAE /* IDSymlinkHandle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IDSymlinkHandle.cpp; sourceTree = ""; }; + 70D474301BDD17E800929DAE /* IDSymlinkHandle.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IDSymlinkHandle.hpp; sourceTree = ""; }; + 70D474351BDD21E100929DAE /* IDBaseLinker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IDBaseLinker.cpp; sourceTree = ""; }; + 70D474361BDD21E100929DAE /* IDBaseLinker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IDBaseLinker.hpp; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 70C63F21190CF5E800929DAE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 70C63F5A190D713B00929DAE /* IOKit.framework in Frameworks */, + 70C63F46190CFFAC00929DAE /* DiskArbitration.framework in Frameworks */, + 70C63F44190CFFA500929DAE /* CoreFoundation.framework in Frameworks */, + 70D4742E1BDD0E4200929DAE /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 70C63F1B190CF5E800929DAE = { + isa = PBXGroup; + children = ( + 70C63F26190CF5E800929DAE /* InvariantDisks */, + 70C63F47190D15FE00929DAE /* Frameworks */, + 70C63F25190CF5E800929DAE /* Products */, + ); + sourceTree = ""; + }; + 70C63F25190CF5E800929DAE /* Products */ = { + isa = PBXGroup; + children = ( + 70C63F24190CF5E800929DAE /* InvariantDisks */, + ); + name = Products; + sourceTree = ""; + }; + 70C63F26190CF5E800929DAE /* InvariantDisks */ = { + isa = PBXGroup; + children = ( + 70C63F27190CF5E800929DAE /* main.cpp */, + 70C63F30190CF78800929DAE /* IDCLI.cpp */, + 70C63F31190CF78800929DAE /* IDCLI.hpp */, + 70C63F33190CF87400929DAE /* IDException.cpp */, + 70C63F34190CF87400929DAE /* IDException.hpp */, + 70C63F50190D2CC900929DAE /* IDDiskArbitrationUtils.cpp */, + 70C63F51190D2CC900929DAE /* IDDiskArbitrationUtils.hpp */, + 70C63F4C190D1B4200929DAE /* IDDiskArbitrationHandler.hpp */, + 70C63F4D190D2AA000929DAE /* IDDiskInfoLogger.cpp */, + 70C63F4E190D2AA000929DAE /* IDDiskInfoLogger.hpp */, + 704C39211A3C7E9600929DAE /* IDDAHandlerIdle.cpp */, + 704C39221A3C7E9600929DAE /* IDDAHandlerIdle.hpp */, + 70D474351BDD21E100929DAE /* IDBaseLinker.cpp */, + 70D474361BDD21E100929DAE /* IDBaseLinker.hpp */, + 70C63F53190D4A4300929DAE /* IDMediaPathLinker.cpp */, + 70C63F54190D4A4300929DAE /* IDMediaPathLinker.hpp */, + 700AE3C71915139C00929DAE /* IDUUIDLinker.cpp */, + 700AE3C81915139C00929DAE /* IDUUIDLinker.hpp */, + 700AE3CA191530A000929DAE /* IDSerialLinker.cpp */, + 700AE3CB191530A000929DAE /* IDSerialLinker.hpp */, + 70C9002C1C6780EF001A40C8 /* IDImagePathLinker.cpp */, + 70C9002D1C6780EF001A40C8 /* IDImagePathLinker.hpp */, + 70C63F49190D1AEF00929DAE /* IDDiskArbitrationDispatcher.cpp */, + 70C63F4A190D1AEF00929DAE /* IDDiskArbitrationDispatcher.hpp */, + 70C63F56190D574000929DAE /* IDFileUtils.mm */, + 70C63F57190D574000929DAE /* IDFileUtils.hpp */, + 70D4742F1BDD17E800929DAE /* IDSymlinkHandle.cpp */, + 70D474301BDD17E800929DAE /* IDSymlinkHandle.hpp */, + 704C391E1A3C777F00929DAE /* IDDispatchUtils.cpp */, + 704C391F1A3C777F00929DAE /* IDDispatchUtils.hpp */, + 707E3F0C1B6CDC2400929DAE /* IDLogUtils.cpp */, + 707E3F0D1B6CDC2400929DAE /* IDLogUtils.hpp */, + ); + path = InvariantDisks; + sourceTree = ""; + }; + 70C63F47190D15FE00929DAE /* Frameworks */ = { + isa = PBXGroup; + children = ( + 70D4742D1BDD0E4200929DAE /* Foundation.framework */, + 70C63F59190D713B00929DAE /* IOKit.framework */, + 70C63F45190CFFAC00929DAE /* DiskArbitration.framework */, + 70C63F43190CFFA500929DAE /* CoreFoundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 70C63F23190CF5E800929DAE /* InvariantDisks */ = { + isa = PBXNativeTarget; + buildConfigurationList = 70C63F2D190CF5E800929DAE /* Build configuration list for PBXNativeTarget "InvariantDisks" */; + buildPhases = ( + 70C63F42190CFB6800929DAE /* Git Version */, + 70C63F20190CF5E800929DAE /* Sources */, + 70C63F21190CF5E800929DAE /* Frameworks */, + 70C63F22190CF5E800929DAE /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = InvariantDisks; + productName = InvariantDisks; + productReference = 70C63F24190CF5E800929DAE /* InvariantDisks */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 70C63F1C190CF5E800929DAE /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0800; + ORGANIZATIONNAME = "the-color-black.net"; + }; + buildConfigurationList = 70C63F1F190CF5E800929DAE /* Build configuration list for PBXProject "InvariantDisks" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 70C63F1B190CF5E800929DAE; + productRefGroup = 70C63F25190CF5E800929DAE /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 70C63F23190CF5E800929DAE /* InvariantDisks */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + 70C63F42190CFB6800929DAE /* Git Version */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/.git", + ); + name = "Git Version"; + outputPaths = ( + "$(DERIVED_SOURCES_DIR)/git-version.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "GIT_VERSION=$(git describe --tags --long --abbrev=6 --dirty=*)\necho \"#define GIT_VERSION \\\"${GIT_VERSION}\\\"\" > ${DERIVED_FILE_DIR}/git-version.h\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 70C63F20190CF5E800929DAE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 700AE3C91915139C00929DAE /* IDUUIDLinker.cpp in Sources */, + 700AE3CC191530A000929DAE /* IDSerialLinker.cpp in Sources */, + 70C63F32190CF78800929DAE /* IDCLI.cpp in Sources */, + 70C63F52190D2CC900929DAE /* IDDiskArbitrationUtils.cpp in Sources */, + 70C63F55190D4A4300929DAE /* IDMediaPathLinker.cpp in Sources */, + 704C39201A3C777F00929DAE /* IDDispatchUtils.cpp in Sources */, + 704C39231A3C7E9600929DAE /* IDDAHandlerIdle.cpp in Sources */, + 70C63F35190CF87400929DAE /* IDException.cpp in Sources */, + 70C9002E1C6780EF001A40C8 /* IDImagePathLinker.cpp in Sources */, + 70D474311BDD17E800929DAE /* IDSymlinkHandle.cpp in Sources */, + 707E3F0E1B6CDC2400929DAE /* IDLogUtils.cpp in Sources */, + 70D474371BDD21E100929DAE /* IDBaseLinker.cpp in Sources */, + 70C63F4B190D1AEF00929DAE /* IDDiskArbitrationDispatcher.cpp in Sources */, + 70C63F4F190D2AA000929DAE /* IDDiskInfoLogger.cpp in Sources */, + 70C63F58190D574000929DAE /* IDFileUtils.mm in Sources */, + 70C63F28190CF5E800929DAE /* main.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 70C63F2B190CF5E800929DAE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + ONLY_ACTIVE_ARCH = YES; + }; + name = Debug; + }; + 70C63F2C190CF5E800929DAE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + }; + name = Release; + }; + 70C63F2E190CF5E800929DAE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 70C63F2F190CF5E800929DAE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 70C63F1F190CF5E800929DAE /* Build configuration list for PBXProject "InvariantDisks" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 70C63F2B190CF5E800929DAE /* Debug */, + 70C63F2C190CF5E800929DAE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 70C63F2D190CF5E800929DAE /* Build configuration list for PBXNativeTarget "InvariantDisks" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 70C63F2E190CF5E800929DAE /* Debug */, + 70C63F2F190CF5E800929DAE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 70C63F1C190CF5E800929DAE /* Project object */; +} diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDBaseLinker.cpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDBaseLinker.cpp new file mode 100644 index 000000000000..6769f649c15f --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDBaseLinker.cpp @@ -0,0 +1,71 @@ +// +// IDBaseLinker.cpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.05.03. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#include "IDBaseLinker.hpp" + +#include "IDDiskArbitrationUtils.hpp" +#include "IDFileUtils.hpp" + +namespace ID +{ + BaseLinker::BaseLinker(std::string base, LogClient const & logger) : + DiskArbitrationHandler(logger), + m_base(std::move(base)) + { + createCleanPath(m_base); + } + + void BaseLinker::diskDisappeared(DADiskRef disk, DiskInformation const & di) + { + removeLinksForDisk(di); + } + + void BaseLinker::addLinkForDisk(std::string const & link, DiskInformation const & di) + { + try + { + if (link.empty()) + return; + std::string devicePath = "/dev/" + di.mediaBSDName; + logger().logDefault("Creating symlink: ", link, " -> ", devicePath); + m_links.emplace(devicePath, SymlinkHandle(link, devicePath)); + } + catch (std::exception const & e) + { + logger().logError("Could not create symlink: ", e.what()); + } + } + + void BaseLinker::removeLinksForDisk(DiskInformation const & di) + { + try + { + std::string devicePath = "/dev/" + di.mediaBSDName; + auto found = m_links.equal_range(devicePath); + for (auto it = found.first; it != found.second; ++it) + { + logger().logDefault("Removing symlink: ", it->second.link()); + it->second.reset(); + } + m_links.erase(found.first, found.second); + } + catch (std::exception const & e) + { + logger().logError("Could not remove symlink: ", e.what()); + } + } + + std::string const & BaseLinker::base() const + { + return m_base; + } +} diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDBaseLinker.hpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDBaseLinker.hpp new file mode 100644 index 000000000000..26fd3c9544c9 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDBaseLinker.hpp @@ -0,0 +1,44 @@ +// +// IDBaseLinker.hpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.05.03. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#ifndef ID_BASE_LINKER_HPP +#define ID_BASE_LINKER_HPP + +#include "IDDiskArbitrationHandler.hpp" + +#include "IDSymlinkHandle.hpp" + +#include +#include + +namespace ID +{ + class BaseLinker : public DiskArbitrationHandler + { + public: + explicit BaseLinker(std::string base, LogClient const & logger); + + public: + virtual void diskDisappeared(DADiskRef disk, DiskInformation const & info) override; + + protected: + void addLinkForDisk(std::string const & link, DiskInformation const & di); + void removeLinksForDisk(DiskInformation const & di); + std::string const & base() const; + + private: + std::string m_base; + std::multimap m_links; + }; +} + +#endif diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDCLI.cpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDCLI.cpp new file mode 100644 index 000000000000..fd4ca8205940 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDCLI.cpp @@ -0,0 +1,171 @@ +// +// IDCLI.cpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#include "IDCLI.hpp" + +#include "IDException.hpp" +#include "IDDiskArbitrationDispatcher.hpp" +#include "IDDiskInfoLogger.hpp" +#include "IDDAHandlerIdle.hpp" +#include "IDMediaPathLinker.hpp" +#include "IDUUIDLinker.hpp" +#include "IDSerialLinker.hpp" +#include "IDImagePathLinker.hpp" +#include "IDDispatchUtils.hpp" +#include "IDLogUtils.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "git-version.h" + +namespace ID +{ + struct CLI::Impl + { + std::mutex mutex; + DispatchSource signalSourceINT; + DispatchSource signalSourceTERM; + bool showHelp = false; + bool verbose = false; + std::string basePath = "/var/run/disk"; + std::string logPath; + int64_t idleTimeoutNS = 4000000000; + CFRunLoopRef runloop = nullptr; + LogClient * logger = nullptr; + }; + + CLI::CLI(int & argc, char ** argv, LogClient & logger) : + m_impl(new Impl) + { + m_impl->logger = &logger; + // Setup + dispatch_function_t stopHandler = [](void * ctx){ static_cast(ctx)->stop();}; + m_impl->signalSourceINT = createSourceSignal(SIGINT, this, stopHandler); + m_impl->signalSourceTERM = createSourceSignal(SIGTERM, this, stopHandler); + // UI + showVersion(); + parse(argc, argv); + } + + CLI::~CLI() + { + } + + int CLI::exec() + { + // Print help and terminate + if (m_impl->showHelp) + { + showHelp(); + return 0; + } + // Start runloop + { + std::lock_guard lock(m_impl->mutex); + if (m_impl->runloop) + throw Exception("CLI already running"); + m_impl->runloop = CFRunLoopGetCurrent(); + } + auto & logger = *m_impl->logger; + if (!m_impl->logPath.empty()) + logger.addLogFile(m_impl->logPath.c_str()); + DiskArbitrationDispatcher dispatcher; + dispatcher.addHandler(std::make_shared(m_impl->basePath, m_impl->idleTimeoutNS, logger)); + dispatcher.addHandler(std::make_shared(m_impl->verbose, logger)); + dispatcher.addHandler(std::make_shared(m_impl->basePath + "/by-path", logger)); + dispatcher.addHandler(std::make_shared(m_impl->basePath + "/by-id", logger)); + dispatcher.addHandler(std::make_shared(m_impl->basePath + "/by-serial", logger)); + dispatcher.addHandler(std::make_shared(m_impl->basePath + "/by-image-path", logger)); + dispatcher.start(); + CFRunLoopRun(); + { + std::lock_guard lock(m_impl->mutex); + m_impl->runloop = nullptr; + } + return 0; + } + + void CLI::stop() + { + std::lock_guard lock(m_impl->mutex); + if (m_impl->runloop) + CFRunLoopStop(m_impl->runloop); + } + + struct CLIFlagHandler + { + size_t argCount; + std::function func; + }; + + void CLI::showVersion() const + { + std::cout << "InvariantDisk " << GIT_VERSION << std::endl; + } + + void CLI::showHelp() const + { + std::cout << "Usage: InvariantDisks [-hv] [-p ] [-t ]\n"; + std::cout << "\t-h:\tprint help and exit\n"; + std::cout << "\t-v:\tverbose logging\n"; + std::cout << "\t-p :\tset base path for symlinks (" << m_impl->basePath << ")\n"; + std::cout << "\t-l :\tset optional path for logging (" << m_impl->logPath << ")\n"; + std::cout << "\t-t :\tset idle timeout (" << m_impl->idleTimeoutNS/1000000 << " ms)\n"; + } + + void CLI::parse(int & argc, char ** argv) + { + // Command Line Parsing + std::map cliFlags = + { + {"-h", { 0, [&](char **){ m_impl->showHelp = true; }}}, + {"-v", { 0, [&](char **){ m_impl->verbose = true; }}}, + {"-p", { 1, [&](char ** a){ m_impl->basePath = a[1]; }}}, + {"-l", { 1, [&](char ** a){ m_impl->logPath = a[1]; }}}, + {"-t", { 1, [&](char ** a){ + try + { + int64_t timeoutInNS = std::stol(a[1])*1000000ll; + if (timeoutInNS < 0) + throw std::out_of_range("negative"); + m_impl->idleTimeoutNS = timeoutInNS; + } + catch (std::exception const & e) + { + Throw() << "Idle Timeout " << a[1] << " is not a valid timeout: " << e.what(); + } + }}} + }; + for (int argIdx = 0; argIdx < argc; ++argIdx) + { + auto flagIt = cliFlags.find(argv[argIdx]); + if (flagIt != cliFlags.end()) + { + CLIFlagHandler const & f = flagIt->second; + if (argIdx + f.argCount >= argc) + Throw() << "Flag " << argv[argIdx] << " requires " << f.argCount << " arguments"; + f.func(&argv[argIdx]); + argIdx += f.argCount; + } + } + } +} diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDCLI.hpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDCLI.hpp new file mode 100644 index 000000000000..6ca417b96ff1 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDCLI.hpp @@ -0,0 +1,44 @@ +// +// IDCLI.hpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#ifndef ID_CLI_HPP +#define ID_CLI_HPP + +#include +#include + +namespace ID +{ + class LogClient; + + class CLI + { + public: + CLI(int & argc, char ** argv, LogClient & logger); + ~CLI(); + + public: + int exec(); + void stop(); + + private: + void showVersion() const; + void showHelp() const; + void parse(int & argc, char ** argv); + + private: + struct Impl; + std::unique_ptr m_impl; + }; +} + +#endif diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDDAHandlerIdle.cpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDAHandlerIdle.cpp new file mode 100644 index 000000000000..3a68986148f2 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDAHandlerIdle.cpp @@ -0,0 +1,49 @@ +// +// IDDAHandlerIdle.cpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#include "IDDAHandlerIdle.hpp" + +#include "IDFileUtils.hpp" + +namespace ID +{ + DAHandlerIdle::DAHandlerIdle(std::string base, int64_t idleTimeoutNS, + LogClient const & logger) : + DiskArbitrationHandler(logger), + m_base(std::move(base)), m_idleTimeout(idleTimeoutNS), + m_idleTimer(createSourceTimer(this, [](void * ctx){ static_cast(ctx)->idle(); })) + { + createPath(m_base); + busy(); + } + + void DAHandlerIdle::diskAppeared(DADiskRef /*disk*/, DiskInformation const & /*info*/) + { + busy(); + } + + void DAHandlerIdle::diskDisappeared(DADiskRef /*disk*/, DiskInformation const & /*info*/) + { + busy(); + } + + void DAHandlerIdle::idle() + { + createFile(m_base + "/invariant.idle"); + } + + void DAHandlerIdle::busy() + { + removeFSObject(m_base + "/invariant.idle"); + scheduleSingleshot(m_idleTimer, m_idleTimeout); + } +} diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDDAHandlerIdle.hpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDAHandlerIdle.hpp new file mode 100644 index 000000000000..41087c829dd1 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDAHandlerIdle.hpp @@ -0,0 +1,44 @@ +// +// IDDAHandlerIdle.hpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#ifndef ID_DAHANDLERIDLE_HPP +#define ID_DAHANDLERIDLE_HPP + +#include "IDDiskArbitrationHandler.hpp" +#include "IDDispatchUtils.hpp" + +#include + +namespace ID +{ + class DAHandlerIdle : public DiskArbitrationHandler + { + public: + explicit DAHandlerIdle(std::string base, int64_t idleTimeoutNS, + LogClient const & logger); + + public: + virtual void diskAppeared(DADiskRef disk, DiskInformation const & info) override; + virtual void diskDisappeared(DADiskRef disk, DiskInformation const & info) override; + + private: + void idle(); + void busy(); + + private: + std::string m_base; + int64_t m_idleTimeout; + DispatchSource m_idleTimer; + }; +} + +#endif diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationDispatcher.cpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationDispatcher.cpp new file mode 100644 index 000000000000..92022158fc4d --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationDispatcher.cpp @@ -0,0 +1,106 @@ +// +// IDDiskArbitrationDispatcher.cpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#include "IDDiskArbitrationDispatcher.hpp" + +#include "IDDiskArbitrationHandler.hpp" +#include "IDDiskArbitrationUtils.hpp" + +#include + +#include +#include +#include + +namespace ID +{ + struct DiskArbitrationDispatcher::Impl + { + std::mutex mutex; + std::vector handler; + DASessionRef session = nullptr; + bool scheduled = false; + }; + + DiskArbitrationDispatcher::DiskArbitrationDispatcher() : + m_impl(new Impl) + { + m_impl->session = DASessionCreate(kCFAllocatorDefault); + DARegisterDiskAppearedCallback(m_impl->session, nullptr, [](DADiskRef disk, void * ctx) + { static_cast(ctx)->diskAppeared(disk); }, this); + DARegisterDiskDisappearedCallback(m_impl->session, nullptr, [](DADiskRef disk, void * ctx) + { static_cast(ctx)->diskDisappeared(disk); }, this); + } + + DiskArbitrationDispatcher::~DiskArbitrationDispatcher() + { + stop(); + CFRelease(m_impl->session); + } + + void DiskArbitrationDispatcher::addHandler(Handler handler) + { + std::lock_guard lock(m_impl->mutex); + m_impl->handler.push_back(std::move(handler)); + } + + void DiskArbitrationDispatcher::removeHandler(Handler const & handler) + { + std::lock_guard lock(m_impl->mutex); + m_impl->handler.erase(std::find(m_impl->handler.begin(), m_impl->handler.end(), handler), + m_impl->handler.end()); + } + + void DiskArbitrationDispatcher::clearHandler() + { + std::lock_guard lock(m_impl->mutex); + m_impl->handler.clear(); + } + + void DiskArbitrationDispatcher::start() + { + std::lock_guard lock(m_impl->mutex); + if (!m_impl->scheduled) + { + DASessionScheduleWithRunLoop(m_impl->session, + CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + m_impl->scheduled = true; + } + } + + void DiskArbitrationDispatcher::stop() + { + std::lock_guard lock(m_impl->mutex); + if (m_impl->scheduled) + { + DASessionUnscheduleFromRunLoop(m_impl->session, + CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + m_impl->scheduled = false; + } + } + + void DiskArbitrationDispatcher::diskAppeared(DADiskRef disk) const + { + DiskInformation info = getDiskInformation(disk); + std::lock_guard lock(m_impl->mutex); + for (auto const & handler: m_impl->handler) + handler->diskAppeared(disk, info); + } + + void DiskArbitrationDispatcher::diskDisappeared(DADiskRef disk) const + { + DiskInformation info = getDiskInformation(disk); + std::lock_guard lock(m_impl->mutex); + for (auto const & handler: m_impl->handler) + handler->diskDisappeared(disk, info); + } +} diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationDispatcher.hpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationDispatcher.hpp new file mode 100644 index 000000000000..9ee646fae932 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationDispatcher.hpp @@ -0,0 +1,55 @@ +// +// IDDiskArbitrationDispatcher.hpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#ifndef ID_DISKARBITRATIONDISPATCHER_HPP +#define ID_DISKARBITRATIONDISPATCHER_HPP + +#include + +#include + +namespace ID +{ + class DiskArbitrationHandler; + + /*! + \brief Dispatches DiskArbitration events, wrapper around the DiskArbitration framework + */ + class DiskArbitrationDispatcher + { + public: + typedef std::shared_ptr Handler; + + public: + DiskArbitrationDispatcher(); + ~DiskArbitrationDispatcher(); + + public: + void addHandler(Handler handler); + void removeHandler(Handler const & handler); + void clearHandler(); + + public: + void start(); + void stop(); + + private: + void diskAppeared(DADiskRef disk) const; + void diskDisappeared(DADiskRef disk) const; + + private: + struct Impl; + std::unique_ptr m_impl; + }; +} + +#endif diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationHandler.hpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationHandler.hpp new file mode 100644 index 000000000000..80d891a21631 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationHandler.hpp @@ -0,0 +1,42 @@ +// +// IDDiskArbitrationHandler.hpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#ifndef ID_DISKARBITRATIONHANDLER_HPP +#define ID_DISKARBITRATIONHANDLER_HPP + +#include + +#include "IDLogUtils.hpp" + +namespace ID +{ + class DiskInformation; + + class DiskArbitrationHandler + { + public: + explicit DiskArbitrationHandler(LogClient const & logger) : m_logger(logger) {} + virtual ~DiskArbitrationHandler() = default; + + public: + virtual void diskAppeared(DADiskRef disk, DiskInformation const & info) = 0; + virtual void diskDisappeared(DADiskRef disk, DiskInformation const & info) = 0; + + protected: + LogClient const & logger() const { return m_logger; } + + private: + LogClient m_logger; + }; +} + +#endif diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationUtils.cpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationUtils.cpp new file mode 100644 index 000000000000..1180b10b68cb --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationUtils.cpp @@ -0,0 +1,272 @@ +// +// IDDiskArbitrationUtils.cpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#include "IDDiskArbitrationUtils.hpp" + +#include + +#include +#include + +namespace ID +{ + std::ostream & operator<<(std::ostream & os, DADiskRef disk) + { + return os << getDiskInformation(disk); + } + + std::ostream & operator<<(std::ostream & os, DiskInformation const & disk) + { + return os << "Disk: (\n" + << "\tVolumeKind=\"" << disk.volumeKind << "\"\n" + << "\tVolumeUUID=\"" << disk.volumeUUID << "\"\n" + << "\tVolumeName=\"" << disk.volumeName << "\"\n" + << "\tVolumePath=\"" << disk.volumePath << "\"\n" + << "\tMediaKind=\"" << disk.mediaKind << "\"\n" + << "\tMediaType=\"" << disk.mediaType << "\"\n" + << "\tMediaUUID=\"" << disk.mediaUUID << "\"\n" + << "\tMediaBSDName=\"" << disk.mediaBSDName << "\"\n" + << "\tMediaName=\"" << disk.mediaName << "\"\n" + << "\tMediaPath=\"" << disk.mediaPath << "\"\n" + << "\tMediaContent=\"" << disk.mediaContent << "\"\n" + << "\tMedia(Whole,Leaf,Writable)=(" << disk.mediaWhole << ", " + << disk.mediaLeaf << ", " << disk.mediaWritable << ")\n" + << "\tDeviceGUID=\"" << disk.deviceGUID << "\"\n" + << "\tDevicePath=\"" << disk.devicePath << "\"\n" + << "\tDeviceProtocol=\"" << disk.deviceProtocol << "\"\n" + << "\tDeviceModel=\"" << disk.deviceModel << "\"\n" + << "\tBusName=\"" << disk.busName << "\"\n" + << "\tBusPath=\"" << disk.busPath << "\"\n" + << "\tIOSerial=\"" << disk.ioSerial << "\"\n" + << "\tImagePath=\"" << disk.imagePath << "\"\n" + << ")"; + } + + std::string to_string(CFStringRef str) + { + std::string result; + CFRange strRange = CFRangeMake(0, CFStringGetLength(str)); + CFIndex strBytes = 0; + CFStringGetBytes(str, strRange, kCFStringEncodingUTF8, 0, false, nullptr, 0, &strBytes); + if (strBytes > 0) + { + result.resize(static_cast(strBytes), '\0'); + CFStringGetBytes(str, strRange, kCFStringEncodingUTF8, 0, false, + reinterpret_cast(&result[0]), strBytes, nullptr); + } + return result; + } + + std::string to_string(CFURLRef url) + { + CFStringRef str = CFURLCopyPath(url); + std::string result = to_string(str); + CFRelease(str); + return result; + } + + std::string to_string(CFDataRef data) + { + char const * bytesBegin = reinterpret_cast(CFDataGetBytePtr(data)); + char const * bytesEnd = bytesBegin + CFDataGetLength(data); + std::stringstream ss; + ss << std::hex; + for (char const * byteIt = bytesBegin; byteIt != bytesEnd; ++byteIt) + ss << static_cast(*byteIt); + return ss.str(); + } + + std::string to_string(CFUUIDRef uuid) + { + CFStringRef str = CFUUIDCreateString(kCFAllocatorDefault, uuid); + std::string result = to_string(str); + CFRelease(str); + return result; + } + + std::string to_string(CFTypeRef variant) + { + if (!variant) + return std::string(); + CFTypeID typeID = CFGetTypeID(variant); + if (typeID == CFStringGetTypeID()) + return to_string(CFStringRef(variant)); + else if (typeID == CFURLGetTypeID()) + return to_string(CFURLRef(variant)); + else if (typeID == CFDataGetTypeID()) + return to_string(CFDataRef(variant)); + else if (typeID == CFUUIDGetTypeID()) + return to_string(CFUUIDRef(variant)); + return std::string(); + } + + std::string interpret_as_string(CFDataRef data) + { + char const * bytesBegin = reinterpret_cast(CFDataGetBytePtr(data)); + char const * bytesEnd = bytesBegin + CFDataGetLength(data); + return std::string(bytesBegin, bytesEnd); + } + + template + std::string stringFromDictionary(CFDictionaryRef dict, CFStringRef key) + { + if (T value = static_cast(CFDictionaryGetValue(dict, key))) + return to_string(value); + return std::string(); + } + + int64_t numberFromDictionary(CFDictionaryRef dict, CFStringRef key) + { + if (CFNumberRef value = static_cast(CFDictionaryGetValue(dict, key))) + { + int64_t number = 0; + CFNumberGetValue(value, kCFNumberSInt64Type, &number); + return number; + } + return 0; + } + + bool boolFromDictionary(CFDictionaryRef dict, CFStringRef key) + { + if (CFBooleanRef value = static_cast(CFDictionaryGetValue(dict, key))) + { + return CFBooleanGetValue(value); + } + return false; + } + + std::string stringFromIOObjectWithParents(io_object_t ioObject, CFStringRef key) + { + std::string result; + CFTypeRef resultRef = IORegistryEntrySearchCFProperty(ioObject, kIOServicePlane, key, + kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents); + if (resultRef) + { + result = to_string(resultRef); + CFRelease(resultRef); + } + return result; + } + + std::string serialNumberFromIOObject(io_object_t ioObject) + { + static CFStringRef const serialStrings[] = { + CFSTR("Serial Number"), + CFSTR("INQUIRY Unit Serial Number"), + CFSTR("USB Serial Number") + }; + for (CFStringRef serialString: serialStrings) + { + std::string serial = stringFromIOObjectWithParents(ioObject, serialString); + if (!serial.empty()) + return serial; + } + return std::string(); + } + + std::string imagePathFromIOObject(io_object_t ioObject) + { + std::string path; + CFStringRef key = CFSTR("image-path"); + CFTypeRef resultRef = IORegistryEntrySearchCFProperty(ioObject, kIOServicePlane, key, + kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents); + if (resultRef) + { + if (CFGetTypeID(resultRef) == CFDataGetTypeID()) + { + CFDataRef resultDataRef = CFDataRef(resultRef); + path = interpret_as_string(resultDataRef); + } + CFRelease(resultRef); + } + return path; + } + + static std::string coreStorageMark = "/CoreStoragePhysical/"; + + DiskInformation getDiskInformation(DADiskRef disk) + { + DiskInformation info; + // DiskArbitration + CFDictionaryRef descDict = DADiskCopyDescription(disk); + info.volumeKind = stringFromDictionary(descDict, kDADiskDescriptionVolumeKindKey); + info.volumeUUID = stringFromDictionary(descDict, kDADiskDescriptionVolumeUUIDKey); + info.volumeName = stringFromDictionary(descDict, kDADiskDescriptionVolumeNameKey); + info.volumePath = stringFromDictionary(descDict, kDADiskDescriptionVolumePathKey); + info.mediaKind = stringFromDictionary(descDict, kDADiskDescriptionMediaKindKey); + info.mediaType = stringFromDictionary(descDict, kDADiskDescriptionMediaTypeKey); + info.mediaUUID = stringFromDictionary(descDict, kDADiskDescriptionMediaUUIDKey); + info.mediaBSDName = stringFromDictionary(descDict, kDADiskDescriptionMediaBSDNameKey); + info.mediaName = stringFromDictionary(descDict, kDADiskDescriptionMediaNameKey); + info.mediaPath = stringFromDictionary(descDict, kDADiskDescriptionMediaPathKey); + info.mediaContent = stringFromDictionary(descDict, kDADiskDescriptionMediaContentKey); + info.mediaWhole = boolFromDictionary(descDict, kDADiskDescriptionMediaWholeKey); + info.mediaLeaf = boolFromDictionary(descDict, kDADiskDescriptionMediaLeafKey); + info.mediaWritable = boolFromDictionary(descDict, kDADiskDescriptionMediaWritableKey); + info.deviceGUID = stringFromDictionary(descDict, kDADiskDescriptionDeviceGUIDKey); + info.devicePath = stringFromDictionary(descDict, kDADiskDescriptionDevicePathKey); + info.deviceProtocol = stringFromDictionary(descDict, kDADiskDescriptionDeviceProtocolKey); + info.deviceModel = stringFromDictionary(descDict, kDADiskDescriptionDeviceModelKey); + info.busName = stringFromDictionary(descDict, kDADiskDescriptionBusNameKey); + info.busPath = stringFromDictionary(descDict, kDADiskDescriptionBusPathKey); + CFRelease(descDict); + // IOKit + io_service_t io = DADiskCopyIOMedia(disk); + info.ioSerial = serialNumberFromIOObject(io); + info.imagePath = imagePathFromIOObject(io); + CFMutableDictionaryRef ioDict = nullptr; + if (IORegistryEntryCreateCFProperties(io, &ioDict, kCFAllocatorDefault, 0) == kIOReturnSuccess) + { + // TODO: Pick out useful IOKit properties + CFRelease(ioDict); + } + IOObjectRelease(io); + // Guess wether this is an actual device + bool isCoreStorage = info.mediaPath.find(coreStorageMark) != std::string::npos; + bool isVirtual = info.deviceProtocol == kIOPropertyPhysicalInterconnectTypeVirtual; + info.isDevice = !isCoreStorage && !isVirtual; + return info; + } + + bool isDevice(DiskInformation const & di) + { + return di.isDevice; + } + + bool isWhole(DiskInformation const & di) + { + return di.mediaWhole; + } + + bool isRealDevice(DiskInformation const & di) + { + // Check if the MediaPath is in 'IODeviceTree:/', since this seems to + // work to reject APFS container. + static std::regex const devicePattern("^IODeviceTree:/.*$"); + return std::regex_match(di.mediaPath, devicePattern); + } + + std::string partitionSuffix(DiskInformation const & di) + { + if (!isWhole(di)) + { + size_t suffixStart = di.mediaBSDName.find_last_not_of("0123456789"); + if (suffixStart != std::string::npos && + suffixStart+1 < di.mediaBSDName.size() && + di.mediaBSDName[suffixStart] == 's') + { + return ':' + di.mediaBSDName.substr(suffixStart+1); + } + } + return std::string(); + } +} diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationUtils.hpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationUtils.hpp new file mode 100644 index 000000000000..3908824ec659 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskArbitrationUtils.hpp @@ -0,0 +1,61 @@ +// +// IDDiskArbitrationUtils.hpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#ifndef ID_DISKARBITRATIONUTILS_HPP +#define ID_DISKARBITRATIONUTILS_HPP + +#include + +#include +#include + +namespace ID +{ + struct DiskInformation + { + std::string volumeKind; + std::string volumeUUID; + std::string volumeName; + std::string volumePath; + std::string mediaKind; + std::string mediaType; + std::string mediaUUID; + std::string mediaBSDName; + std::string mediaName; + std::string mediaPath; + std::string mediaContent; + bool isDevice; + bool mediaWhole; + bool mediaLeaf; + bool mediaWritable; + std::string deviceGUID; + std::string devicePath; + std::string deviceProtocol; + std::string deviceModel; + std::string busName; + std::string busPath; + std::string ioSerial; + std::string imagePath; + }; + + DiskInformation getDiskInformation(DADiskRef disk); + + bool isDevice(DiskInformation const & di); + bool isWhole(DiskInformation const & di); + bool isRealDevice(DiskInformation const & di); + std::string partitionSuffix(DiskInformation const & di); + + std::ostream & operator<<(std::ostream & os, DADiskRef disk); + std::ostream & operator<<(std::ostream & os, DiskInformation const & disk); +} + +#endif diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskInfoLogger.cpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskInfoLogger.cpp new file mode 100644 index 000000000000..38aebcbfae4d --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskInfoLogger.cpp @@ -0,0 +1,47 @@ +// +// IDDiskInfoLogger.cpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#include "IDDiskInfoLogger.hpp" + +#include "IDDiskArbitrationUtils.hpp" + +#include + +#include + +namespace ID +{ + DiskInfoLogger::DiskInfoLogger(bool verbose, LogClient const & logger) : + DiskArbitrationHandler(logger), + m_verbose(verbose) + { + } + + void DiskInfoLogger::diskAppeared(DADiskRef /*disk*/, DiskInformation const & info) + { + if (m_verbose) + logger().logInfo("Disk Appeared: ", formatDisk(info)); + } + + void DiskInfoLogger::diskDisappeared(DADiskRef /*disk*/, DiskInformation const & info) + { + if (m_verbose) + logger().logInfo("Disk Disappeared: ", formatDisk(info)); + } + + std::string DiskInfoLogger::formatDisk(DiskInformation const & info) const + { + std::stringstream ss; + ss << info; + return ss.str(); + } +} diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskInfoLogger.hpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskInfoLogger.hpp new file mode 100644 index 000000000000..e1fe4655f020 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDiskInfoLogger.hpp @@ -0,0 +1,39 @@ +// +// IDDiskInfoLogger.hpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#ifndef ID_DISKINFOLOGGER_HPP +#define ID_DISKINFOLOGGER_HPP + +#include "IDDiskArbitrationHandler.hpp" + +#include + +namespace ID +{ + class DiskInfoLogger : public DiskArbitrationHandler + { + public: + explicit DiskInfoLogger(bool verbose, LogClient const & logger); + + public: + virtual void diskAppeared(DADiskRef disk, DiskInformation const & info) override; + virtual void diskDisappeared(DADiskRef disk, DiskInformation const & info) override; + + private: + std::string formatDisk(DiskInformation const & info) const; + + private: + bool m_verbose; + }; +} + +#endif diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDDispatchUtils.cpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDispatchUtils.cpp new file mode 100644 index 000000000000..f8d7314edbd0 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDispatchUtils.cpp @@ -0,0 +1,44 @@ +// +// IDDispatchUtils.cpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.12.13. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#include "IDDispatchUtils.hpp" + +namespace ID +{ + DispatchSource createSourceSignal(int sig, void * ctx, dispatch_function_t handler) + { + signal(sig, SIG_IGN); + dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, sig, 0, + DISPATCH_TARGET_QUEUE_DEFAULT); + dispatch_set_context(source, ctx); + dispatch_source_set_event_handler_f(source, handler); + dispatch_resume(source); + return DispatchSource(source); + } + + DispatchSource createSourceTimer(void * ctx, dispatch_function_t handler) + { + dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, + DISPATCH_TARGET_QUEUE_DEFAULT); + dispatch_set_context(source, ctx); + dispatch_source_set_event_handler_f(source, handler); + dispatch_resume(source); + return DispatchSource(source); + } + + void scheduleSingleshot(DispatchSource & timerSource, int64_t delayInNS) + { + dispatch_time_t start = dispatch_time(0, delayInNS); + // Schedule a single shot timer with a tolerance of 256ms + dispatch_source_set_timer(timerSource.get(), start, DISPATCH_TIME_FOREVER, 256000000); + } +} diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDDispatchUtils.hpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDispatchUtils.hpp new file mode 100644 index 000000000000..94f58bec0e36 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDDispatchUtils.hpp @@ -0,0 +1,41 @@ +// +// IDDispatchUtils.h +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.12.13. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#ifndef ID_DISPATCHUTILS_HPP +#define ID_DISPATCHUTILS_HPP + +#include + +#include + +namespace ID +{ + struct DispatchDelete + { + void operator()(dispatch_source_s * source) + { + dispatch_source_set_event_handler_f(source, nullptr); + dispatch_release(source); + } + }; + + typedef std::unique_ptr DispatchSource; + + // Signal Source + DispatchSource createSourceSignal(int sig, void * ctx, dispatch_function_t handler); + + // Timer Source + DispatchSource createSourceTimer(void * ctx, dispatch_function_t handler); + void scheduleSingleshot(DispatchSource & timerSource, int64_t delayInNS); +} + +#endif diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDException.cpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDException.cpp new file mode 100644 index 000000000000..e271282df5ec --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDException.cpp @@ -0,0 +1,17 @@ +// +// IDException.cpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#include "IDException.hpp" + +namespace ID +{ +} diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDException.hpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDException.hpp new file mode 100644 index 000000000000..70f269e00e7c --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDException.hpp @@ -0,0 +1,83 @@ +// +// IDException.hpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#ifndef ID_EXCEPTION_HPP +#define ID_EXCEPTION_HPP + +#include +#include + +#include + +namespace ID +{ + /*! + \brief Base class for all Seal exceptions + */ + class Exception : public std::runtime_error + { + public: + using std::runtime_error::runtime_error; + }; + + /*! + \brief Helper class for scope bounded exception throwing + */ + template + class Throw + { + public: + ~Throw() noexcept(false) + { + throw E(m_ss.str()); + } + + public: + /*! + Forward everything to the embedded stringstream + */ + template + Throw & operator<<(T && t) + { + m_ss << std::forward(t); + return *this; + } + + /*! + For IO Manipulators + */ + Throw & operator<<(std::ostream & (*m)(std::ostream&)) + { + m_ss << m; + return *this; + } + + public: + /*! + Format Core Foundation errors + */ + Throw & operator<<(CFErrorRef error) + { + CFStringRef es = CFErrorCopyDescription(error); + char const * esp = CFStringGetCStringPtr(es, kCFStringEncodingUTF8); + if (esp) + *this << esp; + CFRelease(es); + return *this; + } + + private: + std::stringstream m_ss; + }; +} + +#endif diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDFileUtils.hpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDFileUtils.hpp new file mode 100644 index 000000000000..1670b2f5d64c --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDFileUtils.hpp @@ -0,0 +1,27 @@ +// +// IDFileUtils.hpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#ifndef ID_FILEUTILS_HPP +#define ID_FILEUTILS_HPP + +#include + +namespace ID +{ + void createPath(std::string const & path); + void createCleanPath(std::string const & path); + void createFile(std::string const & path); + void createSymlink(std::string const & link, std::string const & target); + void removeFSObject(std::string const & path); +} + +#endif diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDFileUtils.mm b/cmd/os/macos/InvariantDisks/InvariantDisks/IDFileUtils.mm new file mode 100644 index 000000000000..410555fed6e3 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDFileUtils.mm @@ -0,0 +1,92 @@ +// +// IDFileUtils.mm +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#include "IDFileUtils.hpp" + +#include "IDException.hpp" + +#import +#import +#import + +namespace ID +{ + void createPath(std::string const & path) + { + NSError * error = nullptr; + NSFileManager * manager = [NSFileManager defaultManager]; + BOOL success = [manager createDirectoryAtPath:[NSString stringWithUTF8String:path.c_str()] + withIntermediateDirectories:YES attributes:nullptr error:&error]; + if (!success) + { + Throw e; + e << "Error creating directory " << path << ": " << [[error description] UTF8String]; + } + } + + void createCleanPath(std::string const & path) + { + removeFSObject(path); + createPath(path); + } + + void createFile(std::string const & path) + { + if (path.empty()) + throw Exception("Can not create file with empty path"); + removeFSObject(path); + NSError * error = nullptr; + NSData * empty = [NSData data]; + BOOL success = [empty writeToFile:[NSString stringWithUTF8String:path.c_str()] + options:0 error:&error]; + if (!success) + { + Throw e; + e << "Error creating file " << path << ": " << [[error description] UTF8String]; + } + } + + void createSymlink(std::string const & link, std::string const & target) + { + if (link.empty() || target.empty()) + throw Exception("Can not create symlink with empty path"); + removeFSObject(link); + NSError * error = nullptr; + NSFileManager * manager = [NSFileManager defaultManager]; + BOOL success = [manager createSymbolicLinkAtPath:[NSString stringWithUTF8String:link.c_str()] + withDestinationPath:[NSString stringWithUTF8String:target.c_str()] + error:&error]; + if (!success) + { + Throw e; + e << "Error creating symlink " << link << " pointing to " << target << ": " + << [[error description] UTF8String]; + } + } + + void removeFSObject(std::string const & path) + { + if (path.empty()) + throw Exception("Can not remove file system object with empty path"); + NSError * error = nullptr; + NSFileManager * manager = [NSFileManager defaultManager]; + BOOL success = [manager removeItemAtPath:[NSString stringWithUTF8String:path.c_str()] error:&error]; + if (!success && error.domain != NSCocoaErrorDomain && error.code != 4) + { + NSError * underlying = error.userInfo[NSUnderlyingErrorKey]; + if (underlying && underlying.domain == NSPOSIXErrorDomain && underlying.code == ENOENT) + return; // Ignore non-existing files + Throw e; + e << "Error removing file system object " << path << ": " << [[error description] UTF8String]; + } + } +} diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDImagePathLinker.cpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDImagePathLinker.cpp new file mode 100644 index 000000000000..16938e42976c --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDImagePathLinker.cpp @@ -0,0 +1,39 @@ +// +// IDImagePathLinker.cpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#include "IDImagePathLinker.hpp" + +#include "IDDiskArbitrationUtils.hpp" + +namespace ID +{ + ImagePathLinker::ImagePathLinker(std::string const & base, LogClient const & logger) : + BaseLinker(base, logger) + { + } + + static std::string formatImagePath(DiskInformation const & di) + { + std::string filteredPath = di.imagePath + partitionSuffix(di); + std::replace(filteredPath.begin(), filteredPath.end(), '/', '-'); + return filteredPath; + } + + void ImagePathLinker::diskAppeared(DADiskRef disk, DiskInformation const & di) + { + if (!di.imagePath.empty() && !di.mediaBSDName.empty()) + { + std::string imagePath = formatImagePath(di); + addLinkForDisk(base() + "/" + imagePath, di); + } + } +} diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDImagePathLinker.hpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDImagePathLinker.hpp new file mode 100644 index 000000000000..ff81b63d79a1 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDImagePathLinker.hpp @@ -0,0 +1,30 @@ +// +// IDImagePathLinker.hpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#ifndef ID_IMAGEPATHLINKER_HPP +#define ID_IMAGEPATHLINKER_HPP + +#include "IDBaseLinker.hpp" + +namespace ID +{ + class ImagePathLinker : public BaseLinker + { + public: + explicit ImagePathLinker(std::string const & base, LogClient const & logger); + + public: + virtual void diskAppeared(DADiskRef disk, DiskInformation const & info) override; + }; +} + +#endif diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDLogUtils.cpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDLogUtils.cpp new file mode 100644 index 000000000000..bbdc56af9b05 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDLogUtils.cpp @@ -0,0 +1,181 @@ +// +// IDASLUtils.cpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2015.08.01. +// Copyright (c) 2015 the-color-black.net. All rights reserved. +// + +#include "IDLogUtils.hpp" + +#include +#include + +#include +#include + +// Auto Detect the use of ASL or OS Log if it is not already enforced by the +// build environment +#if !(defined(ID_USE_ASL) || defined(ID_USE_OS_LOG)) + #include + #include + #if defined (MAC_OS_X_VERSION_10_12) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) + #define ID_USE_OS_LOG + #else + #define ID_USE_ASL + #endif +#endif + +#if defined(ID_USE_ASL) +// Uses ASL: +// https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/asl.3.html +#include +#elif defined(ID_USE_OS_LOG) +// Uses the new OS Log facilities: +// https://developer.apple.com/reference/os/logging +#include +#else +#error "Neither ID_USE_ASL nor ID_USE_OS_LOG have been defined" +#endif + +static char const * const logFacility = "net.the-color-black"; +static char const * const logCategory = "InvariantDisks"; + +namespace ID +{ +#if defined(ID_USE_ASL) + + class LogClient::Impl + { + public: + explicit Impl() : + client(asl_open(logCategory, logFacility, ASL_OPT_STDERR)) + { + } + + ~Impl() + { + asl_close(client); + for (auto fd: fds) + close(fd); + } + + public: + Impl(Impl const &) = delete; + Impl & operator=(Impl const &) = delete; + + public: + int addLogFile(char const * logFile) + { + int fd = open(logFile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) + { + asl_log(client, 0, ASL_LEVEL_ERR, "Error opening log file \"%s\" (%m)", logFile); + return -1; + } + int r = asl_add_log_file(client, fd); + if (r != 0) + { + asl_log(client, 0, ASL_LEVEL_ERR, "Error registering file \"%s\" (%d)", logFile, r); + close(fd); + return -1; + } + fds.push_back(fd); + return fd; + } + + void removeLogFile(int fd) + { + auto found = std::find(fds.begin(), fds.end(), fd); + if (found != fds.end()) + { + std::swap(*found, fds.back()); + fds.pop_back(); + asl_remove_log_file(client, fd); + close(fd); + } + } + + public: + aslclient client; + std::vector fds; + }; + + void LogClient::logInfo(std::string const & msg) const + { + asl_log(m_impl->client, 0, ASL_LEVEL_INFO, "%s", msg.c_str()); + } + + void LogClient::logDefault(std::string const & msg) const + { + asl_log(m_impl->client, 0, ASL_LEVEL_NOTICE, "%s", msg.c_str()); + } + + void LogClient::logError(std::string const & msg) const + { + asl_log(m_impl->client, 0, ASL_LEVEL_ERR, "%s", msg.c_str()); + } + +#elif defined(ID_USE_OS_LOG) + + class LogClient::Impl + { + public: + Impl() : + client(os_log_create(logFacility, logCategory)) + { + } + + ~Impl() + { + // Should release client from os_log_create, but no function for + // this is documented or defined. + } + + public: + Impl(Impl const &) = delete; + Impl & operator=(Impl const &) = delete; + + public: + int addLogFile(char const *) + { + os_log_with_type(client, OS_LOG_TYPE_DEFAULT, + "Log Files are no longer supported with os_log, use the logging subsystem instead"); + return 0; + } + + void removeLogFile(int) + { + } + + public: + os_log_t client; + }; + + void LogClient::logInfo(std::string const & msg) const + { + os_log_with_type(m_impl->client, OS_LOG_TYPE_INFO, "%{public}s", msg.c_str()); + } + + void LogClient::logDefault(std::string const & msg) const + { + os_log_with_type(m_impl->client, OS_LOG_TYPE_DEFAULT, "%{public}s", msg.c_str()); + } + + void LogClient::logError(std::string const & msg) const + { + os_log_with_type(m_impl->client, OS_LOG_TYPE_ERROR, "%{public}s", msg.c_str()); + } + +#endif + + LogClient::LogClient() : + m_impl(std::make_shared()) + { + } + + int LogClient::addLogFile(char const * logFile) + { + return m_impl->addLogFile(logFile); + } +} diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDLogUtils.hpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDLogUtils.hpp new file mode 100644 index 000000000000..6caa386d59db --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDLogUtils.hpp @@ -0,0 +1,78 @@ +// +// IDASLUtils.h +// InvariantDisks +// +// Created by Gerhard Röthlin on 2015.08.01. +// Copyright (c) 2015 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#ifndef ID_LOGUTILS_HPP +#define ID_LOGUTILS_HPP + +#include +#include + +namespace ID +{ + class LogClient + { + public: + explicit LogClient(); + + public: + int addLogFile(char const * logFile); + + public: + template + void logInfo(ARGS const & ... args) const; + + template + void logDefault(ARGS const & ... args) const; + + template + void logError(ARGS const & ... args) const; + + public: + void logInfo(std::string const & msg) const; + void logDefault(std::string const & msg) const; + void logError(std::string const & msg) const; + + private: + class Impl; + std::shared_ptr m_impl; + }; + + // Private Implementation + template + void LogClient::logInfo(ARGS const & ... args) const + { + std::stringstream ss; + int ignored[] = {(ss << args, 0)...}; + (void)ignored; + logInfo(ss.str()); + } + + template + void LogClient::logDefault(ARGS const & ... args) const + { + std::stringstream ss; + int ignored[] = {(ss << args, 0)...}; + (void)ignored; + logDefault(ss.str()); + } + + template + void LogClient::logError(ARGS const & ... args) const + { + std::stringstream ss; + int ignored[] = {(ss << args, 0)...}; + (void)ignored; + logError(ss.str()); + } +} + +#endif diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDMediaPathLinker.cpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDMediaPathLinker.cpp new file mode 100644 index 000000000000..e58dc6b9aea3 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDMediaPathLinker.cpp @@ -0,0 +1,43 @@ +// +// IDMediaPathLinker.cpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#include "IDMediaPathLinker.hpp" + +#include "IDDiskArbitrationUtils.hpp" + +namespace ID +{ + MediaPathLinker::MediaPathLinker(std::string const & base, LogClient const & logger) : + BaseLinker(base, logger) + { + } + + static std::string prefixDevice = "IODeviceTree:/"; + + static std::string filterMediaPath(std::string const & mediaPath) + { + if (mediaPath.compare(0, prefixDevice.size(), prefixDevice) != 0) + return std::string(); + std::string filteredPath = mediaPath.substr(prefixDevice.size()); + std::replace(filteredPath.begin(), filteredPath.end(), '/', '-'); + return filteredPath; + } + + void MediaPathLinker::diskAppeared(DADiskRef disk, DiskInformation const & di) + { + std::string mediaPath = filterMediaPath(di.mediaPath); + if (!mediaPath.empty() && !di.mediaBSDName.empty()) + { + addLinkForDisk(base() + "/" + mediaPath, di); + } + } +} diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDMediaPathLinker.hpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDMediaPathLinker.hpp new file mode 100644 index 000000000000..b7bf2f06a716 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDMediaPathLinker.hpp @@ -0,0 +1,30 @@ +// +// IDMediaPathLinker.hpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#ifndef ID_MEDIAPATHLINKER_HPP +#define ID_MEDIAPATHLINKER_HPP + +#include "IDBaseLinker.hpp" + +namespace ID +{ + class MediaPathLinker : public BaseLinker + { + public: + explicit MediaPathLinker(std::string const & base, LogClient const & logger); + + public: + virtual void diskAppeared(DADiskRef disk, DiskInformation const & info) override; + }; +} + +#endif diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDSerialLinker.cpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDSerialLinker.cpp new file mode 100644 index 000000000000..ec3a98e67c01 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDSerialLinker.cpp @@ -0,0 +1,80 @@ +// +// IDSerialLinker.cpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.05.03. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#include "IDSerialLinker.hpp" + +#include "IDDiskArbitrationUtils.hpp" + +#include +#include + +namespace ID +{ + SerialLinker::SerialLinker(std::string base, LogClient const & logger) : + BaseLinker(std::move(base), logger) + { + } + + static bool isInvalidSerialChar(char c) + { + if (isalnum(c)) + return false; + if (c == '-' || c == '_') + return false; + return true; + } + + std::string trim(std::string const & s) + { + size_t first = s.find_first_not_of(' '); + size_t last = s.find_last_not_of(' '); + if (first != std::string::npos) + return s.substr(first, last - first + 1); + return s; + } + + std::string formatSerial(DiskInformation const & di) + { + std::string model = trim(di.deviceModel); + std::string serial = trim(di.ioSerial); + std::string formated; + if (!serial.empty()) + { + if (model.empty()) + formated = serial; + else + formated = model + "-" + serial; + } + std::replace(formated.begin(), formated.end(), ' ', '_'); + formated.erase(std::remove_if(formated.begin(), formated.end(), isInvalidSerialChar), formated.end()); + if (!formated.empty()) + formated += partitionSuffix(di); + return formated; + } + + std::string SerialLinker::formatSerialPath(DiskInformation const & di) const + { + std::string serial = formatSerial(di); + if (!serial.empty()) + serial = base() + "/" + serial; + return serial; + } + + void SerialLinker::diskAppeared(DADiskRef disk, DiskInformation const & di) + { + if (isDevice(di) && isRealDevice(di)) + { + addLinkForDisk(formatSerialPath(di), di); + } + } + +} diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDSerialLinker.hpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDSerialLinker.hpp new file mode 100644 index 000000000000..f4d1a5f3554a --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDSerialLinker.hpp @@ -0,0 +1,33 @@ +// +// IDSerialLinker.hpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.05.03. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#ifndef ID_SERIALLINKER_HPP +#define ID_SERIALLINKER_HPP + +#include "IDBaseLinker.hpp" + +namespace ID +{ + class SerialLinker : public BaseLinker + { + public: + explicit SerialLinker(std::string base, LogClient const & logger); + + public: + virtual void diskAppeared(DADiskRef disk, DiskInformation const & info) override; + + private: + std::string formatSerialPath(DiskInformation const & di) const; + }; +} + +#endif diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDSymlinkHandle.cpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDSymlinkHandle.cpp new file mode 100644 index 000000000000..7e7d04253a05 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDSymlinkHandle.cpp @@ -0,0 +1,75 @@ +// +// IDSymlinkHandle.cpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2015.10.25. +// Copyright (c) 2015 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#include "IDSymlinkHandle.hpp" + +#include "IDFileUtils.hpp" +#include "IDLogUtils.hpp" + +namespace ID +{ + SymlinkHandle::SymlinkHandle() + { + } + + SymlinkHandle::SymlinkHandle(std::string const & link, std::string const & target) : + m_link(link), m_target(target) + { + createSymlink(link, target); + } + + SymlinkHandle::~SymlinkHandle() + { + try + { + reset(); + } + catch (std::exception const & e) + { + // Swallow exceptions during destruction + } + } + + SymlinkHandle::SymlinkHandle(SymlinkHandle && other) noexcept : + m_link(std::move(other.m_link)), m_target(std::move(other.m_target)) + { + other.m_link.clear(); + other.m_target.clear(); + } + + SymlinkHandle & SymlinkHandle::operator=(SymlinkHandle && other) + { + swap(m_link, other.m_link); + other.reset(); + return *this; + } + + void SymlinkHandle::reset() + { + if (!m_link.empty()) + { + removeFSObject(m_link); + m_link.clear(); + m_target.clear(); + } + } + + std::string const & SymlinkHandle::link() const + { + return m_link; + } + + std::string const & SymlinkHandle::target() const + { + return m_target; + } +} diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDSymlinkHandle.hpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDSymlinkHandle.hpp new file mode 100644 index 000000000000..9909d700f467 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDSymlinkHandle.hpp @@ -0,0 +1,53 @@ +// +// IDSymlinkHandle.hpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2015.10.25. +// Copyright (c) 2015 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#ifndef ID_SYMLINK_HANDLE_HPP +#define ID_SYMLINK_HANDLE_HPP + +#include + +namespace ID +{ + /*! + \brief A class representing a symlink in the file system + + This class represents a symlink in the filesystem that only exists as long as its corresponding + instance exists. + */ + class SymlinkHandle + { + public: + SymlinkHandle(); + explicit SymlinkHandle(std::string const & link, std::string const & target); + ~SymlinkHandle(); + + public: + SymlinkHandle(SymlinkHandle && other) noexcept; + SymlinkHandle & operator=(SymlinkHandle && other); + + public: + /*! + Resets the instance so that it represents no symlink. + */ + void reset(); + + public: + std::string const & link() const; + std::string const & target() const; + + private: + std::string m_link; + std::string m_target; + }; +} + +#endif diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDUUIDLinker.cpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDUUIDLinker.cpp new file mode 100644 index 000000000000..33f37758496f --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDUUIDLinker.cpp @@ -0,0 +1,46 @@ +// +// IDUUIDLinker.cpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.05.03. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#include "IDUUIDLinker.hpp" + +#include "IDDiskArbitrationUtils.hpp" + +#include + +namespace ID +{ + UUIDLinker::UUIDLinker(std::string const & base, LogClient const & logger) : + BaseLinker(base, logger) + { + } + + static std::vector getUUIDs(DiskInformation const & diskInfo) + { + std::vector uuids; + if (!diskInfo.volumeUUID.empty()) + uuids.push_back("volume-" + diskInfo.volumeUUID); + if (!diskInfo.mediaUUID.empty()) + uuids.push_back("media-" + diskInfo.mediaUUID); + if (!diskInfo.deviceGUID.empty()) + uuids.push_back("device-" + diskInfo.deviceGUID); + return uuids; + } + + void UUIDLinker::diskAppeared(DADiskRef disk, DiskInformation const & di) + { + auto mediaUUIDs = getUUIDs(di); + for (auto mediaID: mediaUUIDs) + { + addLinkForDisk(base() + "/" + mediaID, di); + } + } +} diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/IDUUIDLinker.hpp b/cmd/os/macos/InvariantDisks/InvariantDisks/IDUUIDLinker.hpp new file mode 100644 index 000000000000..16b65f02e4ed --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/IDUUIDLinker.hpp @@ -0,0 +1,30 @@ +// +// IDUUIDLinker.hpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.05.03. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#ifndef ID_UUIDLINKER_HPP +#define ID_UUIDLINKER_HPP + +#include "IDBaseLinker.hpp" + +namespace ID +{ + class UUIDLinker : public BaseLinker + { + public: + explicit UUIDLinker(std::string const & base, LogClient const & logger); + + public: + virtual void diskAppeared(DADiskRef disk, DiskInformation const & info) override; + }; +} + +#endif diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/Makefile.am b/cmd/os/macos/InvariantDisks/InvariantDisks/Makefile.am new file mode 100644 index 000000000000..b6849ba07f20 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/Makefile.am @@ -0,0 +1,48 @@ +include $(top_srcdir)/config/Rules.am + +AUTOMAKE_OPTIONS = subdir-objects +AM_LIBTOOLFLAGS += --tag=CXX + +DEFAULT_INCLUDES += \ + -I$(top_srcdir)/include + +sbin_PROGRAMS = InvariantDisks + +InvariantDisks_SOURCES = \ + IDLogUtils.cpp \ + IDLogUtils.hpp \ + IDBaseLinker.cpp \ + IDBaseLinker.hpp \ + IDCLI.cpp \ + IDCLI.hpp \ + IDDAHandlerIdle.cpp \ + IDDAHandlerIdle.hpp \ + IDDiskArbitrationDispatcher.cpp \ + IDDiskArbitrationDispatcher.hpp \ + IDDiskArbitrationHandler.hpp \ + IDDiskArbitrationUtils.cpp \ + IDDiskArbitrationUtils.hpp \ + IDDiskInfoLogger.cpp \ + IDDiskInfoLogger.hpp \ + IDDispatchUtils.cpp \ + IDDispatchUtils.hpp \ + IDException.cpp \ + IDException.hpp \ + IDFileUtils.mm \ + IDFileUtils.hpp \ + IDImagePathLinker.cpp \ + IDImagePathLinker.hpp \ + IDMediaPathLinker.cpp \ + IDMediaPathLinker.hpp \ + IDSerialLinker.cpp \ + IDSerialLinker.hpp \ + IDSymlinkHandle.cpp \ + IDSymlinkHandle.hpp \ + IDUUIDLinker.cpp \ + IDUUIDLinker.hpp \ + git-version.h \ + main.cpp + +InvariantDisks_CXXFLAGS = -std=c++11 -stdlib=libc++ +InvariantDisks_OBJCXXFLAGS = -std=c++11 -stdlib=libc++ +InvariantDisks_LDFLAGS = -static -framework IOKit -framework DiskArbitration -framework CoreFoundation -framework Foundation diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/git-version.h b/cmd/os/macos/InvariantDisks/InvariantDisks/git-version.h new file mode 100644 index 000000000000..f82f7d375d3b --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/git-version.h @@ -0,0 +1,3 @@ + +#include +#define GIT_VERSION ZFS_META_GITREV diff --git a/cmd/os/macos/InvariantDisks/InvariantDisks/main.cpp b/cmd/os/macos/InvariantDisks/InvariantDisks/main.cpp new file mode 100644 index 000000000000..447a9bf3ec8c --- /dev/null +++ b/cmd/os/macos/InvariantDisks/InvariantDisks/main.cpp @@ -0,0 +1,40 @@ +// +// main.cpp +// InvariantDisks +// +// Created by Gerhard Röthlin on 2014.04.27. +// Copyright (c) 2014 the-color-black.net. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the conditions of the "3-Clause BSD" license described in the BSD.LICENSE file are met. +// Additional licensing options are described in the README file. +// + +#include "IDCLI.hpp" +#include "IDException.hpp" +#include "IDLogUtils.hpp" + +#include + +int main(int argc, char ** argv) +{ + ID::LogClient logger; + try + { + ID::CLI idCommandLine(argc, argv, logger); + return idCommandLine.exec(); + } + catch (ID::Exception const & e) + { + logger.logError(e.what()); + } + catch (std::exception const & e) + { + logger.logError("Terminated by exception: ", e.what()); + } + catch (...) + { + logger.logError("Terminated by unknown exception"); + } + return -1; +} diff --git a/cmd/os/macos/InvariantDisks/Makefile.am b/cmd/os/macos/InvariantDisks/Makefile.am new file mode 100644 index 000000000000..cbe7d07bcdc0 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = InvariantDisks diff --git a/cmd/os/macos/InvariantDisks/OPENSOLARIS.LICENSE.txt b/cmd/os/macos/InvariantDisks/OPENSOLARIS.LICENSE.txt new file mode 100644 index 000000000000..da23621dc843 --- /dev/null +++ b/cmd/os/macos/InvariantDisks/OPENSOLARIS.LICENSE.txt @@ -0,0 +1,384 @@ +Unless otherwise noted, all files in this distribution are released +under the Common Development and Distribution License (CDDL). +Exceptions are noted within the associated source files. + +-------------------------------------------------------------------- + + +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE Version 1.0 + +1. Definitions. + + 1.1. "Contributor" means each individual or entity that creates + or contributes to the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Software, prior Modifications used by a Contributor (if any), + and the Modifications made by that particular Contributor. + + 1.3. "Covered Software" means (a) the Original Software, or (b) + Modifications, or (c) the combination of files containing + Original Software with files containing Modifications, in + each case including portions thereof. + + 1.4. "Executable" means the Covered Software in any form other + than Source Code. + + 1.5. "Initial Developer" means the individual or entity that first + makes Original Software available under this License. + + 1.6. "Larger Work" means a work which combines Covered Software or + portions thereof with code not governed by the terms of this + License. + + 1.7. "License" means this document. + + 1.8. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed + herein. + + 1.9. "Modifications" means the Source Code and Executable form of + any of the following: + + A. Any file that results from an addition to, deletion from or + modification of the contents of a file containing Original + Software or previous Modifications; + + B. Any new file that contains any part of the Original + Software or previous Modifications; or + + C. Any new file that is contributed or otherwise made + available under the terms of this License. + + 1.10. "Original Software" means the Source Code and Executable + form of computer software code that is originally released + under this License. + + 1.11. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, + process, and apparatus claims, in any patent Licensable by + grantor. + + 1.12. "Source Code" means (a) the common form of computer software + code in which modifications are made and (b) associated + documentation included in or with such code. + + 1.13. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms + of, this License. For legal entities, "You" includes any + entity which controls, is controlled by, or is under common + control with You. For purposes of this definition, + "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty + percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants. + + 2.1. The Initial Developer Grant. + + Conditioned upon Your compliance with Section 3.1 below and + subject to third party intellectual property claims, the Initial + Developer hereby grants You a world-wide, royalty-free, + non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer, to use, + reproduce, modify, display, perform, sublicense and + distribute the Original Software (or portions thereof), + with or without Modifications, and/or as part of a Larger + Work; and + + (b) under Patent Claims infringed by the making, using or + selling of Original Software, to make, have made, use, + practice, sell, and offer for sale, and/or otherwise + dispose of the Original Software (or portions thereof). + + (c) The licenses granted in Sections 2.1(a) and (b) are + effective on the date Initial Developer first distributes + or otherwise makes the Original Software available to a + third party under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: (1) for code that You delete from the Original + Software, or (2) for infringements caused by: (i) the + modification of the Original Software, or (ii) the + combination of the Original Software with other software + or devices. + + 2.2. Contributor Grant. + + Conditioned upon Your compliance with Section 3.1 below and + subject to third party intellectual property claims, each + Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor to use, reproduce, + modify, display, perform, sublicense and distribute the + Modifications created by such Contributor (or portions + thereof), either on an unmodified basis, with other + Modifications, as Covered Software and/or as part of a + Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either + alone and/or in combination with its Contributor Version + (or portions of such combination), to make, use, sell, + offer for sale, have made, and/or otherwise dispose of: + (1) Modifications made by that Contributor (or portions + thereof); and (2) the combination of Modifications made by + that Contributor with its Contributor Version (or portions + of such combination). + + (c) The licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first distributes or + otherwise makes the Modifications available to a third + party. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: (1) for any code that Contributor has deleted + from the Contributor Version; (2) for infringements caused + by: (i) third party modifications of Contributor Version, + or (ii) the combination of Modifications made by that + Contributor with other software (except as part of the + Contributor Version) or other devices; or (3) under Patent + Claims infringed by Covered Software in the absence of + Modifications made by that Contributor. + +3. Distribution Obligations. + + 3.1. Availability of Source Code. + + Any Covered Software that You distribute or otherwise make + available in Executable form must also be made available in Source + Code form and that Source Code form must be distributed only under + the terms of this License. You must include a copy of this + License with every copy of the Source Code form of the Covered + Software You distribute or otherwise make available. You must + inform recipients of any such Covered Software in Executable form + as to how they can obtain such Covered Software in Source Code + form in a reasonable manner on or through a medium customarily + used for software exchange. + + 3.2. Modifications. + + The Modifications that You create or to which You contribute are + governed by the terms of this License. You represent that You + believe Your Modifications are Your original creation(s) and/or + You have sufficient rights to grant the rights conveyed by this + License. + + 3.3. Required Notices. + + You must include a notice in each of Your Modifications that + identifies You as the Contributor of the Modification. You may + not remove or alter any copyright, patent or trademark notices + contained within the Covered Software, or any notices of licensing + or any descriptive text giving attribution to any Contributor or + the Initial Developer. + + 3.4. Application of Additional Terms. + + You may not offer or impose any terms on any Covered Software in + Source Code form that alters or restricts the applicable version + of this License or the recipients' rights hereunder. You may + choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of + Covered Software. However, you may do so only on Your own behalf, + and not on behalf of the Initial Developer or any Contributor. + You must make it absolutely clear that any such warranty, support, + indemnity or liability obligation is offered by You alone, and You + hereby agree to indemnify the Initial Developer and every + Contributor for any liability incurred by the Initial Developer or + such Contributor as a result of warranty, support, indemnity or + liability terms You offer. + + 3.5. Distribution of Executable Versions. + + You may distribute the Executable form of the Covered Software + under the terms of this License or under the terms of a license of + Your choice, which may contain terms different from this License, + provided that You are in compliance with the terms of this License + and that the license for the Executable form does not attempt to + limit or alter the recipient's rights in the Source Code form from + the rights set forth in this License. If You distribute the + Covered Software in Executable form under a different license, You + must make it absolutely clear that any terms which differ from + this License are offered by You alone, not by the Initial + Developer or Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred + by the Initial Developer or such Contributor as a result of any + such terms You offer. + + 3.6. Larger Works. + + You may create a Larger Work by combining Covered Software with + other code not governed by the terms of this License and + distribute the Larger Work as a single product. In such a case, + You must make sure the requirements of this License are fulfilled + for the Covered Software. + +4. Versions of the License. + + 4.1. New Versions. + + Sun Microsystems, Inc. is the initial license steward and may + publish revised and/or new versions of this License from time to + time. Each version will be given a distinguishing version number. + Except as provided in Section 4.3, no one other than the license + steward has the right to modify this License. + + 4.2. Effect of New Versions. + + You may always continue to use, distribute or otherwise make the + Covered Software available under the terms of the version of the + License under which You originally received the Covered Software. + If the Initial Developer includes a notice in the Original + Software prohibiting it from being distributed or otherwise made + available under any subsequent version of the License, You must + distribute and make the Covered Software available under the terms + of the version of the License under which You originally received + the Covered Software. Otherwise, You may also choose to use, + distribute or otherwise make the Covered Software available under + the terms of any subsequent version of the License published by + the license steward. + + 4.3. Modified Versions. + + When You are an Initial Developer and You want to create a new + license for Your Original Software, You may create and use a + modified version of this License if You: (a) rename the license + and remove any references to the name of the license steward + (except to note that the license differs from this License); and + (b) otherwise make it clear that the license contains terms which + differ from this License. + +5. DISCLAIMER OF WARRANTY. + + COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" + BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED + SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR + PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND + PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY + COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE + INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY + NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF + WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS + DISCLAIMER. + +6. TERMINATION. + + 6.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to + cure such breach within 30 days of becoming aware of the breach. + Provisions which, by their nature, must remain in effect beyond + the termination of this License shall survive. + + 6.2. If You assert a patent infringement claim (excluding + declaratory judgment actions) against Initial Developer or a + Contributor (the Initial Developer or Contributor against whom You + assert such claim is referred to as "Participant") alleging that + the Participant Software (meaning the Contributor Version where + the Participant is a Contributor or the Original Software where + the Participant is the Initial Developer) directly or indirectly + infringes any patent, then any and all rights granted directly or + indirectly to You by such Participant, the Initial Developer (if + the Initial Developer is not the Participant) and all Contributors + under Sections 2.1 and/or 2.2 of this License shall, upon 60 days + notice from Participant terminate prospectively and automatically + at the expiration of such 60 day notice period, unless if within + such 60 day period You withdraw Your claim with respect to the + Participant Software against such Participant either unilaterally + or pursuant to a written agreement with Participant. + + 6.3. In the event of termination under Sections 6.1 or 6.2 above, + all end user licenses that have been validly granted by You or any + distributor hereunder prior to termination (excluding licenses + granted to You by any distributor) shall survive termination. + +7. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE + INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF + COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE + LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR + CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT + LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK + STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL + INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT + APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO + NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR + CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT + APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + + The Covered Software is a "commercial item," as that term is + defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial + computer software" (as that term is defined at 48 + C.F.R. 252.227-7014(a)(1)) and "commercial computer software + documentation" as such terms are used in 48 C.F.R. 12.212 + (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 + C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all + U.S. Government End Users acquire Covered Software with only those + rights set forth herein. This U.S. Government Rights clause is in + lieu of, and supersedes, any other FAR, DFAR, or other clause or + provision that addresses Government rights in computer software + under this License. + +9. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed + by the law of the jurisdiction specified in a notice contained + within the Original Software (except to the extent applicable law, + if any, provides otherwise), excluding such jurisdiction's + conflict-of-law provisions. Any litigation relating to this + License shall be subject to the jurisdiction of the courts located + in the jurisdiction and venue specified in a notice contained + within the Original Software, with the losing party responsible + for costs, including, without limitation, court costs and + reasonable attorneys' fees and expenses. The application of the + United Nations Convention on Contracts for the International Sale + of Goods is expressly excluded. Any law or regulation which + provides that the language of a contract shall be construed + against the drafter shall not apply to this License. You agree + that You alone are responsible for compliance with the United + States export administration regulations (and the export control + laws and regulation of any other countries) when You use, + distribute or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or + indirectly, out of its utilization of rights under this License + and You agree to work with Initial Developer and Contributors to + distribute such responsibility on an equitable basis. Nothing + herein is intended or shall be deemed to constitute any admission + of liability. + +-------------------------------------------------------------------- + +NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND +DISTRIBUTION LICENSE (CDDL) + +For Covered Software in this distribution, this License shall +be governed by the laws of the State of California (excluding +conflict-of-law provisions). + +Any litigation relating to this License shall be subject to the +jurisdiction of the Federal Courts of the Northern District of +California and the state courts of the State of California, with +venue lying in Santa Clara County, California. diff --git a/cmd/os/macos/InvariantDisks/README.md b/cmd/os/macos/InvariantDisks/README.md new file mode 100644 index 000000000000..7a3c1b59f10f --- /dev/null +++ b/cmd/os/macos/InvariantDisks/README.md @@ -0,0 +1,36 @@ +InvariantDisks +============== + +InvariantDisks is a small program maintaining a mapping from invariant labels to the potentially +varying /dev/diskXsY entries. It is started like: + +`InvariantDisks -p $PREFIX` + +At the moment, three linker modules are available. The media path module creates links in +$prefix/by-path based on the reported media path, based on the physical location of the +device. Example: + + * `./by-path/PCI0@0-SATA@1F,2-PRT0@0-PMP@0-@0:0` + +The UUID module creates links in $prefix/by-id based on the volume and media UUID. Some volumes +have both a volume and a media UUID, others only one of the two. This depends on the partitioning +scheme and the filesystem. Example: + + * `./by-id/volume-AAFA3521-C1B4-3154-B16E-143D133FA21A` + * `./by-id/media-9722675C-36BB-499D-8609-120D6BDF8609` + +The Serial number module creates links to whole drives based on the product type and product +serial number. + + * `./by-serial/WDC_WD30EZRX-00MMMB-WD-WCAWZ12345` + +The Problem and some solutions on Linux are described on +http://zfsonlinux.org/faq.html#WhatDevNamesShouldIUseWhenCreatingMyPool + +License +======= + +This program is copyrighted by me, because I wrote it. +This program is licensed under the "3-clause BSD" License. See the BSD.LICENSE.md file for details. +If desired, it is also licensed under the CDDL OpenSolaris license, see OPENSOLARIS.LICENSE.txt +for details. Other licenses are available on request. diff --git a/cmd/os/macos/InvariantDisks/launchd/net.the-color-black.InvariantDisks.plist b/cmd/os/macos/InvariantDisks/launchd/net.the-color-black.InvariantDisks.plist new file mode 100644 index 000000000000..71ffccc855fd --- /dev/null +++ b/cmd/os/macos/InvariantDisks/launchd/net.the-color-black.InvariantDisks.plist @@ -0,0 +1,16 @@ + + + + + Label + net.the-color-black.InvariantDisks + ProgramArguments + + /usr/local/bin/InvariantDisks + -p + /var/run/disk + + RunAtLoad + + + diff --git a/cmd/os/macos/Makefile.am b/cmd/os/macos/Makefile.am new file mode 100644 index 000000000000..f7a8e2d7b6f6 --- /dev/null +++ b/cmd/os/macos/Makefile.am @@ -0,0 +1,2 @@ + +SUBDIRS = InvariantDisks zconfigd zfs_util diff --git a/cmd/os/macos/zconfigd/.gitignore b/cmd/os/macos/zconfigd/.gitignore new file mode 100644 index 000000000000..3339f06b4fa7 --- /dev/null +++ b/cmd/os/macos/zconfigd/.gitignore @@ -0,0 +1 @@ +/zconfigd diff --git a/cmd/os/macos/zconfigd/Makefile.am b/cmd/os/macos/zconfigd/Makefile.am new file mode 100644 index 000000000000..43d12f28981e --- /dev/null +++ b/cmd/os/macos/zconfigd/Makefile.am @@ -0,0 +1,15 @@ +include $(top_srcdir)/config/Rules.am + +AUTOMAKE_OPTIONS = subdir-objects + +#DEFAULT_INCLUDES += \ +# -I$(top_srcdir)/include \ +# -I$(top_srcdir)/lib/libspl/include + +sbin_PROGRAMS = zconfigd + +zconfigd_SOURCES = \ + zconfigd.c \ + zconfigd.h + +zconfigd_LDFLAGS = -framework IOKit -framework Foundation diff --git a/cmd/os/macos/zconfigd/zconfigd.c b/cmd/os/macos/zconfigd/zconfigd.c new file mode 100644 index 000000000000..cbf17be88cfd --- /dev/null +++ b/cmd/os/macos/zconfigd/zconfigd.c @@ -0,0 +1,218 @@ +/* + * Copyright © 2003-2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* + * © Copyright 2001-2002 Apple Inc. All rights reserved. + * + * IMPORTANT: + * This Apple software is supplied to you by Apple Computer, Inc. (“Apple”) + * in consideration of your agreement to the following terms, and your use, + * installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these + * terms, please do not use, install, modify or redistribute this + * Apple software. + * + * In consideration of your agreement to abide by the following terms, + * and subject to these terms, Apple grants you a personal, non exclusive + * license, under Apple’s copyrights in this original Apple software + * (the “Apple Software”), to use, reproduce, modify and redistribute + * the Apple Software, with or without modifications, in source and/or + * binary forms; provided that if you redistribute the Apple Software + * in its entirety and without modifications, you must retain this notice + * and the following text and disclaimers in all such redistributions + * of the Apple Software. Neither the name, trademarks, service marks + * or logos of Apple Computer, Inc. may be used to endorse or promote + * products derived from the Apple Software without specific prior written + * permission from Apple. Except as expressly stated in this notice, no other + * rights or licenses, express or implied, are granted by Apple herein, + * including but not limited to any patent rights that may be infringed + * by your derivative works or by other works in which the Apple Software + * may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. + * APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT + * LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR + * ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, + * REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, + * HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING + * NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Much of this file is a modified version of Apple's USB Notification Example: + * http://www.opensource.apple.com/source/IOUSBFamily/IOUSB + * Family-630.4.5/Examples/USBNotification%20Example/main.c + */ + +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2015 OpenZFS on OS X. All rights reserved. + */ + +#include +#include +#include +#include +#include + +#include "zconfigd.h" + +// globals +static IONotificationPortRef gNotifyPort; +static io_iterator_t gKextLoadedIter; + +static void +SignalHandler(int sigraised) +{ + fprintf(stderr, "\nInterrupted\n"); + + // Clean up here + IONotificationPortDestroy(gNotifyPort); + + if (gKextLoadedIter) { + IOObjectRelease(gKextLoadedIter); + gKextLoadedIter = 0; + } + + fflush(stdout); + fflush(stderr); + + // exit(0) should not be called from a signal handler. Use _exit(0) + // instead. + // + _exit(0); +} + +static void +ZFSKextLoaded(void *refCon, io_iterator_t iterator) +{ + io_service_t myservice; + Boolean doAction = FALSE; + struct stat sbuf; + + while ((myservice = IOIteratorNext(iterator))) { + fprintf(stderr, "Found match\n"); + doAction = TRUE; + IOObjectRelease(myservice); + } + if (doAction && stat(ZSYSCTL_CONF_FILE, &sbuf) == 0) { + fprintf(stderr, "Running "ZSYSCTL_CMD_WITH_ARGS"\n"); + system(ZSYSCTL_CMD_WITH_ARGS); + } + + fflush(stdout); + fflush(stderr); +} + +int +main(int argc, const char *argv[]) +{ + mach_port_t masterPort; + CFMutableDictionaryRef matchingDict; + CFRunLoopSourceRef runLoopSource; + kern_return_t kr; + sig_t oldHandler; + + // Set up a signal handler so we can clean up when we're interrupted + // from the command line. Otherwise we stay in our run loop forever. + oldHandler = signal(SIGINT, SignalHandler); + if (oldHandler == SIG_ERR) + fprintf(stderr, "Could not establish new signal handler"); + + // first create a master_port for my task + kr = IOMasterPort(MACH_PORT_NULL, &masterPort); + if (kr || !masterPort) { + fprintf(stderr, "ERR: Couldn't create a master IOKit " + "Port(%08x)\n", kr); + return (-1); + } + + fprintf(stderr, "Looking for service matching %s\n", + kNetLundmanZfsZvol); + + // Set up the matching criteria for the service we're interested in + matchingDict = IOServiceNameMatching(kNetLundmanZfsZvol); + if (!matchingDict) { + fprintf(stderr, "Can't create a %s matching dictionary\n", + kNetLundmanZfsZvol); + mach_port_deallocate(mach_task_self(), masterPort); + return (-1); + } + + // Create a notification port and add its run loop event source to our + // run loop. This is how async notifications get set up. + gNotifyPort = IONotificationPortCreate(masterPort); + runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, + kCFRunLoopDefaultMode); + + // Now set up a notification to be called when zfs.kext loads + kr = IOServiceAddMatchingNotification(gNotifyPort, + kIOFirstMatchNotification, matchingDict, ZFSKextLoaded, NULL, + &gKextLoadedIter); + + // Iterate once to get already-present services and arm the notification + ZFSKextLoaded(NULL, gKextLoadedIter); + + // Now done with the master_port + mach_port_deallocate(mach_task_self(), masterPort); + masterPort = 0; + + fprintf(stderr, "Starting the run loop\n"); + + fflush(stdout); + fflush(stderr); + + // Start the run loop. Now we'll receive notifications. + CFRunLoopRun(); + + // We should never get here + return (0); +} diff --git a/cmd/os/macos/zconfigd/zconfigd.h b/cmd/os/macos/zconfigd/zconfigd.h new file mode 100644 index 000000000000..d382674a247f --- /dev/null +++ b/cmd/os/macos/zconfigd/zconfigd.h @@ -0,0 +1,43 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2015 OpenZFS on OS X. All rights reserved. + */ + +#ifndef ZCONFIGD_H +#define ZCONFIGD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZSYSCTL_CMD_PATH SBINDIR "/zsysctl" +#define ZSYSCTL_CONF_FILE SYSCONFDIR "/zfs/zsysctl.conf" +#define ZSYSCTL_CMD_WITH_ARGS ZSYSCTL_CMD_PATH" -f "ZSYSCTL_CONF_FILE + +#define kNetLundmanZfsZvol "osx_openzfsonosx_zfs_zvol" + +#ifdef __cplusplus +} +#endif + +#endif /* ZCONFIGD_H */ diff --git a/cmd/os/macos/zfs_util/.gitignore b/cmd/os/macos/zfs_util/.gitignore new file mode 100644 index 000000000000..e16cf3097713 --- /dev/null +++ b/cmd/os/macos/zfs_util/.gitignore @@ -0,0 +1 @@ +/zfs.util diff --git a/cmd/os/macos/zfs_util/English.lproj/InfoPlist.strings b/cmd/os/macos/zfs_util/English.lproj/InfoPlist.strings new file mode 100644 index 000000000000..5a07a98365e1 --- /dev/null +++ b/cmd/os/macos/zfs_util/English.lproj/InfoPlist.strings @@ -0,0 +1,14 @@ + + + + + FSPersonalities + + ZFS + + FSName + ZFS Dataset + + + + diff --git a/cmd/os/macos/zfs_util/Info.plist b/cmd/os/macos/zfs_util/Info.plist new file mode 100644 index 000000000000..50740b498960 --- /dev/null +++ b/cmd/os/macos/zfs_util/Info.plist @@ -0,0 +1,131 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + org.openzfsonosx.filesystems.zfs + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + zfs + CFBundlePackageType + fs + CFBundleShortVersionString + 1.5.2 + CFBundleSignature + ???? + CFBundleVersion + 1.5.2 + FSMediaTypes + + ZFS_Dataset + + FSMediaProperties + + Content Hint + ZFS_Dataset + Leaf + + + FSProbeArguments + -p + FSProbeExecutable + zfs_util + FSProbeOrder + 1000 + autodiskmount + + + 6A898CC3-1DD2-11B2-99A6-080020736631 + + FSMediaProperties + + Content Hint + 6A898CC3-1DD2-11B2-99A6-080020736631 + Leaf + + + FSProbeArguments + -p + FSProbeExecutable + zfs_util + FSProbeOrder + 850 + autodiskmount + + + Whole + + FSMediaProperties + + Leaf + + Whole + + + FSProbeArguments + -p + FSProbeExecutable + zfs_util + FSProbeOrder + 20000 + autodiskmount + + + + FSPersonalities + + ZFS + + FSName + ZFS + + FSFormatArguments + + FSFormatContentMask + ZFS_Dataset + FSFormatExecutable + zfs_util + FSMountExecutable + zfs_util + FSMountArguments + + FSVerificationExecutable + fsck_zfs + FSVerificationArguments + + FSLiveVerificationArguments + + FSXMLOutputArgument + -x + FSRepairExecutable + fsck_zfs + FSRepairArguments + + FSFormatMinimumSize + 268435456 + FSFormatMaximumSize + 9223372034707292160 + autodiskmount + + + ZFS Pool + + FSName + ZFS Pool + FSSubType + 0 + + ZFS Dataset + + FSName + ZFS Dataset + FSSubType + 1 + + + + diff --git a/cmd/os/macos/zfs_util/Makefile.am b/cmd/os/macos/zfs_util/Makefile.am new file mode 100644 index 000000000000..88b56fab890a --- /dev/null +++ b/cmd/os/macos/zfs_util/Makefile.am @@ -0,0 +1,68 @@ +include $(top_srcdir)/config/Rules.am + +AUTOMAKE_OPTIONS = subdir-objects + +DEFAULT_INCLUDES += \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/lib/libspl/include + +sbin_PROGRAMS = zfs_util + +zfs_util_SOURCES = \ + zfs_util.c + +zfs_util_LDADD = \ + $(top_builddir)/lib/libzfs/libzfs.la + +zfs_util_LDFLAGS = -static -framework IOKit + +# build the /Library/Filesystems/zfs.fs/ bundle + +FS_BUNDLEPREFIX = /Library/Filesystems +FS_BUNDLEDIR = $(DESTDIR)$(FS_BUNDLEPREFIX)/zfs.fs +PKGINFO = PkgInfo +INFO_PLIST = Info.plist +INFOPLIST_STRINGS = InfoPlist.strings + +zfs_fs_SOURCES = + +bin_PROGRAMS = zfs.fs + +dist_noinst_DATA = $(INFO_PLIST) English.lproj/$(INFOPLIST_STRINGS) $(PKGINFO) + +zfs.fs/Contents/$(PKGINFO): + +zfs.fs/Contents/$(INFO_PLIST): + +zfs.fs/Contents/Resources/English.lproj/$(INFOPLIST_STRINGS): + +zfs.fs$(EXEEXT): zfs.fs/Contents/$(PKGINFO) zfs.fs/Contents/$(INFO_PLIST) zfs.fs/Contents/Resources/English.lproj/$(INFOPLIST_STRINGS) $(INFO_PLIST) English.lproj/$(INFOPLIST_STRINGS) $(PKGINFO) zfs_util + rm -rf zfs.fs + mkdir -p zfs.fs/Contents/Resources/English.lproj + cp -f $(PKGINFO) zfs.fs/Contents/ + cp -f $(INFO_PLIST) zfs.fs/Contents/ + cp -f zfs_util zfs.fs/Contents/Resources/ + cp -f English.lproj/$(INFOPLIST_STRINGS) zfs.fs/Contents/Resources/English.lproj/ + /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $(ZFS_META_VERSION)" zfs.fs/Contents/Info.plist + /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $(ZFS_META_VERSION)" zfs.fs/Contents/Info.plist + plutil -convert binary1 zfs.fs/Contents/Resources/English.lproj/InfoPlist.strings + +install-exec-local:: zfs.fs + rm -f $(FS_BUNDLEDIR)/Contents/$(PKGINFO) + rm -f $(FS_BUNDLEDIR)/Contents/$(INFO_PLIST) + rm -f $(FS_BUNDLEDIR)/Contents/Resources/English.lproj/$(INFOPLIST_STRINGS) + mkdir -p $(FS_BUNDLEDIR) + rsync -aH zfs.fs/ $(FS_BUNDLEDIR)/ + cp -f $(DESTDIR)/$(sbindir)/zfs $(FS_BUNDLEDIR)/Contents/Resources/mount_zfs + ln -fs /usr/bin/true $(FS_BUNDLEDIR)/Contents/Resources/fsck_zfs + ln -fs /usr/bin/true $(FS_BUNDLEDIR)/Contents/Resources/newfs_zfs + chown -R root:wheel $(FS_BUNDLEDIR) || echo "Unable to chown root:wheel in $(FS_BUNDLEDIR)" + +clean-binPROGRAMS: + rm -rf zfs.fs/ + +clean: + rm -rf zfs.fs + +uninstall-hook: + rm -rf "$(FS_BUNDLEDIR)" diff --git a/cmd/os/macos/zfs_util/PkgInfo b/cmd/os/macos/zfs_util/PkgInfo new file mode 100644 index 000000000000..b2c4b86a164e --- /dev/null +++ b/cmd/os/macos/zfs_util/PkgInfo @@ -0,0 +1 @@ +fs ???? \ No newline at end of file diff --git a/cmd/os/macos/zfs_util/zfs_util.c b/cmd/os/macos/zfs_util/zfs_util.c new file mode 100644 index 000000000000..38ab87443520 --- /dev/null +++ b/cmd/os/macos/zfs_util/zfs_util.c @@ -0,0 +1,1065 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.2 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef FSUC_GETUUID +#define FSUC_GETUUID 'k' +#endif + +#ifndef FSUC_SETUUID +#define FSUC_SETUUID 's' +#endif + +#define ZPOOL_IMPORT_ALL_COOKIE \ + "/var/run/org.openzfsonosx.zpool-import-all.didRun" +#define INVARIANT_DISKS_IDLE_FILE \ + "/var/run/disk/invariant.idle" +#define IS_INVARIANT_DISKS_LOADED_CMD \ + "/bin/launchctl list -x org.openzfsonosx.InvariantDisks &>/dev/null" +#define INVARIANT_DISKS_TIMEOUT_SECONDS 60 + +#ifdef DEBUG +int zfs_util_debug = 1; +#else +int zfs_util_debug = 0; +#endif + +#define printf zfs_util_log + +// #define ZFS_AUTOIMPORT_ZPOOL_CACHE_ONLY + +const char *progname; +libzfs_handle_t *g_zfs; + +static void +zfs_util_log(const char *format, ...) +{ + va_list args; + char buf[1024]; + + if (zfs_util_debug == 0) + return; + + setlogmask(LOG_UPTO(LOG_NOTICE)); + + va_start(args, format); + (void) vsnprintf(buf, sizeof (buf), format, args); + fputs(buf, stderr); + va_end(args); + + if (*(&buf[strlen(buf) - 1]) == '\n') + *(&buf[strlen(buf) - 1]) = '\0'; + va_start(args, format); + vsyslog(LOG_NOTICE, format, args); + va_end(args); +} + +static void +usage(void) +{ + fprintf(stderr, "usage: %s action_arg device_arg [Flags] \n", progname); + fprintf(stderr, "action_arg:\n"); + fprintf(stderr, " -%c (Probe for mounting)\n", FSUC_PROBE); + fprintf(stderr, "device_arg:\n"); + fprintf(stderr, " device we are acting upon (for example, " + "'disk0s1')\n"); + fprintf(stderr, "Flags:\n"); + fprintf(stderr, " required for Probe\n"); + fprintf(stderr, " indicates removable or fixed (for example " + "'fixed')\n"); + fprintf(stderr, " indicates readonly or writable (for example " + "'readonly')\n"); + fprintf(stderr, "Examples:\n"); + fprintf(stderr, " %s -p disk0s1 removable readonly\n", progname); +} + +/* + * Given disk2s1, look up "disk2" is IOKit and attempt to determine if + * it is an optical device. + */ +static int +is_optical_media(const char *bsdname) +{ + CFMutableDictionaryRef matchingDict; + int ret = 0; + io_service_t service, start; + kern_return_t kernResult; + io_iterator_t iter; + + if ((matchingDict = IOBSDNameMatching(kIOMasterPortDefault, + 0, bsdname)) == NULL) + return (0); + + start = IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict); + if (IO_OBJECT_NULL == start) + return (0); + + service = start; + + /* + * Create an iterator across all parents of the service object + * passed in. since only disk2 would match with ConfirmsTo, + * and not disk2s1, so we search the parents until we find "Whole", + * ie, disk2. + */ + kernResult = IORegistryEntryCreateIterator(service, + kIOServicePlane, + kIORegistryIterateRecursively | kIORegistryIterateParents, + &iter); + + if (KERN_SUCCESS == kernResult) { + Boolean isWholeMedia = false; + IOObjectRetain(service); + do { + // Lookup "Whole" if we can + if (IOObjectConformsTo(service, kIOMediaClass)) { + CFTypeRef wholeMedia; + wholeMedia = + IORegistryEntryCreateCFProperty(service, + CFSTR(kIOMediaWholeKey), + kCFAllocatorDefault, + 0); + if (wholeMedia) { + isWholeMedia = + CFBooleanGetValue(wholeMedia); + CFRelease(wholeMedia); + } + } + + // If we found "Whole", check the service type. + if (isWholeMedia && + ((IOObjectConformsTo(service, kIOCDMediaClass)) || + (IOObjectConformsTo(service, kIODVDMediaClass)))) { + ret = 1; // Is optical, skip + } + + IOObjectRelease(service); + } while ((service = IOIteratorNext(iter)) && !isWholeMedia); + IOObjectRelease(iter); + } + + IOObjectRelease(start); + return (ret); +} + + +#ifdef ZFS_AUTOIMPORT_ZPOOL_CACHE_ONLY + +#define PRIV_SYS_CONFIG 0 +static __inline int +priv_ineffect(int priv) +{ + assert(priv == PRIV_SYS_CONFIG); + return (geteuid() == 0); +} + +/* + * Perform the import for the given configuration. This passes the heavy + * lifting off to zpool_import_props(), and then mounts the datasets contained + * within the pool. + */ +static int +do_import(nvlist_t *config, const char *newname, const char *mntopts, + nvlist_t *props, int flags) +{ + zpool_handle_t *zhp; + char *name; + uint64_t state; + uint64_t version; + + verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, + &name) == 0); + + verify(nvlist_lookup_uint64(config, + ZPOOL_CONFIG_POOL_STATE, &state) == 0); + verify(nvlist_lookup_uint64(config, + ZPOOL_CONFIG_VERSION, &version) == 0); + if (!SPA_VERSION_IS_SUPPORTED(version)) { + printf("cannot import '%s': pool is formatted using an " + "unsupported ZFS version\n", name); + return (1); + } else if (state != POOL_STATE_EXPORTED && + !(flags & ZFS_IMPORT_ANY_HOST)) { + uint64_t hostid; + + if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_HOSTID, + &hostid) == 0) { + unsigned long system_hostid = gethostid() & 0xffffffff; + + if ((unsigned long)hostid != system_hostid) { + char *hostname; + uint64_t timestamp; + time_t t; + + verify(nvlist_lookup_string(config, + ZPOOL_CONFIG_HOSTNAME, &hostname) == 0); + verify(nvlist_lookup_uint64(config, + ZPOOL_CONFIG_TIMESTAMP, ×tamp) == 0); + t = timestamp; + printf("cannot import " "'%s': pool may be in " + "use from other system, it was last " + "accessed by %s (hostid: 0x%lx) on %s\n", + name, hostname, (unsigned long)hostid, + asctime(localtime(&t))); + printf("use '-f' to import anyway\n"); + return (1); + } + } else { + printf("cannot import '%s': pool may be in use from " + "other system\n", name); + printf("use '-f' to import anyway\n"); + return (1); + } + } + + if (zpool_import_props(g_zfs, config, newname, props, flags) != 0) + return (1); + + if (newname != NULL) + name = (char *)newname; + + if ((zhp = zpool_open_canfail(g_zfs, name)) == NULL) + return (1); + + if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL && + !(flags & ZFS_IMPORT_ONLY) && + zpool_enable_datasets(zhp, mntopts, 0) != 0) { + zpool_close(zhp); + return (1); + } + + zpool_close(zhp); + return (0); +} + +static int +zpool_import_by_guid(uint64_t searchguid) +{ + int err = 0; + nvlist_t *pools = NULL; + nvpair_t *elem; + nvlist_t *config; + nvlist_t *found_config = NULL; + nvlist_t *policy = NULL; + boolean_t first; + int flags = ZFS_IMPORT_NORMAL; + uint32_t rewind_policy = ZPOOL_NO_REWIND; + uint64_t pool_state, txg = -1ULL; + importargs_t idata = { 0 }; +#ifdef ZFS_AUTOIMPORT_ZPOOL_STATUS_OK_ONLY + char *msgid; + zpool_status_t reason; + zpool_errata_t errata; +#endif + + if ((g_zfs = libzfs_init()) == NULL) + return (1); + + /* In the future, we can capture further policy and include it here */ + if (nvlist_alloc(&policy, NV_UNIQUE_NAME, 0) != 0 || + nvlist_add_uint64(policy, ZPOOL_LOAD_REQUEST_TXG, txg) != 0 || + nvlist_add_uint32(policy, ZPOOL_LOAD_REWIND_POLICY, + rewind_policy) != 0) + goto error; + + if (!priv_ineffect(PRIV_SYS_CONFIG)) { + printf("cannot discover pools: permission denied\n"); + nvlist_free(policy); + return (1); + } + + idata.guid = searchguid; + + pools = zpool_search_import(g_zfs, &idata); + + if (pools == NULL && idata.exists) { + printf("cannot import '%llu': a pool with that guid is already " + "created/imported\n", searchguid); + err = 1; + } else if (pools == NULL) { + printf("cannot import '%llu': no such pool available\n", + searchguid); + err = 1; + } + + if (err == 1) { + nvlist_free(policy); + return (1); + } + + /* + * At this point we have a list of import candidate configs. Even though + * we were searching by guid, we still need to post-process the list to + * deal with pool state. + */ + err = 0; + elem = NULL; + first = B_TRUE; + while ((elem = nvlist_next_nvpair(pools, elem)) != NULL) { + + verify(nvpair_value_nvlist(elem, &config) == 0); + + verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE, + &pool_state) == 0); + if (pool_state == POOL_STATE_DESTROYED) + continue; + + verify(nvlist_add_nvlist(config, ZPOOL_LOAD_POLICY, + policy) == 0); + + uint64_t guid; + + /* + * Search for a pool by guid. + */ + verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, + &guid) == 0); + + if (guid == searchguid) + found_config = config; + } + + /* + * If we were searching for a specific pool, verify that we found a + * pool, and then do the import. + */ + if (err == 0) { + if (found_config == NULL) { + printf("zfs.util: FATAL cannot import '%llu': " + "no such pool available\n", searchguid); + err = B_TRUE; + } else { +#ifdef ZFS_AUTOIMPORT_ZPOOL_STATUS_OK_ONLY + reason = zpool_import_status(config, &msgid, &errata); + if (reason == ZPOOL_STATUS_OK) + err |= do_import(found_config, NULL, NULL, NULL, + flags); + else + err = 1; +#else + err |= do_import(found_config, NULL, NULL, NULL, flags); +#endif + } + } + +error: + nvlist_free(pools); + nvlist_free(policy); + libzfs_fini(g_zfs); + + return (err ? 1 : 0); +} +#endif // ZFS_AUTOIMPORT_ZPOOL_CACHE_ONLY + +struct probe_args { + char *pool_name; + int name_len; + uint64_t pool_guid; + uint64_t vdev_guid; +}; +typedef struct probe_args probe_args_t; + +char *UNKNOWN_STRING = "Unknown"; + +static int +zfs_probe(const char *devpath, probe_args_t *args) +{ + nvlist_t *config = NULL; + int ret = FSUR_UNRECOGNIZED; + int fd; + uint64_t guid; + int i, again = 0; + struct stat sbuf; + + // printf("+zfs_probe : devpath %s\n", devpath); + + if (system(IS_INVARIANT_DISKS_LOADED_CMD) == 0) { + /* InvariantDisks is loaded */ + i = 0; + while (i != INVARIANT_DISKS_TIMEOUT_SECONDS) { + if (stat(INVARIANT_DISKS_IDLE_FILE, &sbuf) == 0) { + // printf("Found %s after %d iterations of " + // "sleeping 1 second\n", + // INVARIANT_DISKS_IDLE_FILE, i); + break; + } + sleep(1); + i++; + } + if (i == INVARIANT_DISKS_TIMEOUT_SECONDS) { + printf("zfs.util: FATAL: File %s not found within " + "%d seconds\n", + INVARIANT_DISKS_IDLE_FILE, + INVARIANT_DISKS_TIMEOUT_SECONDS); + } + } + +retry: + + if ((fd = open(devpath, O_RDONLY)) < 0) { + printf("zfs.util: FATAL: Could not open devpath %s: %d\n", + devpath, errno); + goto out; + } + + if (zpool_read_label(fd, &config, NULL) != 0) { + printf("zfs.util: FATAL: Could not read label devpath %s: %d\n", + devpath, errno); + (void) close(fd); + goto out; + } + + (void) close(fd); + + if (config != NULL) { + char *name; + ret = FSUR_RECOGNIZED; + args->pool_guid = (nvlist_lookup_uint64(config, + ZPOOL_CONFIG_POOL_GUID, &guid) == 0) ? guid : 0; + args->vdev_guid = (nvlist_lookup_uint64(config, + ZPOOL_CONFIG_GUID, &guid) == 0) ? guid : 0; + if (args->pool_name && + nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, + &name) == 0) + strlcpy(args->pool_name, name, MAXPATHLEN); + nvlist_free(config); + } else { + if (again++ < 5) { + // printf("zfs.util: read_label config is NULL\n"); + sleep(1); + goto retry; + } + // printf("zfs.util: FATAL: read_label config is NULL\n"); + } +out: + printf("-zfs_probe : ret %s\n", + ret == FSUR_RECOGNIZED ? "FSUR_RECOGNIZED" : "FSUR_UNRECOGNIZED"); + return (ret); +} + +/* Look up "/dev/rdisk5" in ioreg to see if it is a pseudodisk */ +static int +zfs_probe_iokit(const char *devpath, probe_args_t *args) +{ + const char *name; + CFMutableDictionaryRef matchingDict; + io_service_t service = IO_OBJECT_NULL; + CFStringRef cfstr = NULL; + int result = FSUR_UNRECOGNIZED; + + // Make sure it is "disk5s1" not "/dev/" and not "rdisk" + if (strncmp("/dev/disk", devpath, 9) == 0) + name = &devpath[5]; + else if (strncmp("/dev/rdisk", devpath, 10) == 0) + name = &devpath[6]; + else if (strncmp("rdisk", devpath, 5) == 0) + name = &devpath[1]; + else + name = devpath; + + printf("%s: looking for '%s' in ioreg\n", __func__, name); + + matchingDict = IOBSDNameMatching(kIOMasterPortDefault, 0, name); + if (NULL == matchingDict) { + printf("%s: IOBSDNameMatching returned NULL dictionary\n", + __func__); + goto fail; + } + + /* + * Fetch the object with the matching BSD node name. + * Note that there should only be one match, so + * IOServiceGetMatchingService is used instead of + * IOServiceGetMatchingServices to simplify the code. + */ + service = IOServiceGetMatchingService(kIOMasterPortDefault, + matchingDict); + + if (IO_OBJECT_NULL == service) { + printf("%s: IOServiceGetMatchingService returned NULL.\n", + __func__); + goto fail; + } + + if (IOObjectConformsTo(service, kIOMediaClass)) { + cfstr = IORegistryEntryCreateCFProperty(service, + CFSTR("ZFS Dataset"), kCFAllocatorDefault, 0); + if (cfstr != NULL) { + const char *str; + + str = CFStringGetCStringPtr(cfstr, + kCFStringEncodingUTF8); + + if (str != NULL) + (void) strlcpy(args->pool_name, str, + sizeof (io_name_t)); + + result = FSUR_RECOGNIZED; + } + } + +fail: + if (service != IO_OBJECT_NULL) + IOObjectRelease(service); + if (cfstr != NULL) + CFRelease(cfstr); + + printf("%s: result %s name '%s'\n", __func__, + result == FSUR_RECOGNIZED ? "FSUR_RECOGNIZED" : + result == FSUR_UNRECOGNIZED ? "FSUR_UNRECOGNIZED" : + "UNKNOWN", + result == FSUR_RECOGNIZED ? args->pool_name : ""); + + return (result); +} + +#ifdef ZFS_AUTOIMPORT_ZPOOL_CACHE_ONLY +void +zpool_read_cachefile(void) +{ + int fd; + struct stat stbf; + void *buf = NULL; + nvlist_t *nvlist, *child; + nvpair_t *nvpair; + uint64_t guid; + int importrc = 0; + + // printf("reading cachefile\n"); + + fd = open(ZPOOL_CACHE, O_RDONLY); + if (fd < 0) + return; + + if (fstat(fd, &stbf) || !stbf.st_size) + goto out; + + buf = kmem_alloc(stbf.st_size, 0); + if (!buf) + goto out; + + if (read(fd, buf, stbf.st_size) != stbf.st_size) + goto out; + + if (nvlist_unpack(buf, stbf.st_size, &nvlist, KM_PUSHPAGE) != 0) + goto out; + + nvpair = NULL; + while ((nvpair = nvlist_next_nvpair(nvlist, nvpair)) != NULL) { + if (nvpair_type(nvpair) != DATA_TYPE_NVLIST) + continue; + + VERIFY(nvpair_value_nvlist(nvpair, &child) == 0); + + printf("Cachefile has pool '%s'\n", nvpair_name(nvpair)); + + if (nvlist_lookup_uint64(child, ZPOOL_CONFIG_POOL_GUID, + &guid) == 0) { + printf("Cachefile has pool '%s' guid %llu\n", + nvpair_name(nvpair), guid); + + importrc = zpool_import_by_guid(guid); + printf("zpool import error %d\n", importrc); + } + + } + nvlist_free(nvlist); + +out: + close(fd); + if (buf) + kmem_free(buf, stbf.st_size); + +} +#endif + + +/* + * Each vdev in a pool should each have unique uuid? + */ +static int +zfs_util_uuid_gen(probe_args_t *probe, char *uuid_str) +{ + unsigned char uuid[CC_MD5_DIGEST_LENGTH]; + // MD5_CTX md5c; + CC_MD5_CTX md5c; + /* namespace (generated by uuidgen) */ + /* 50670853-FBD2-4EC3-9802-73D847BF7E62 */ + char namespace[16] = {0x50, 0x67, 0x08, 0x53, /* - */ + 0xfb, 0xd2, /* - */ 0x4e, 0xc3, /* - */ + 0x98, 0x02, /* - */ + 0x73, 0xd8, 0x47, 0xbf, 0x7e, 0x62}; + + /* Validate arguments */ + if (!probe->vdev_guid) { + printf("zfs.util: FATAL: %s missing argument\n", __func__); + return (EINVAL); + } + + /* + * UUID version 3 (MD5) namespace variant: + * hash namespace (uuid) together with name + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + + CC_MD5_Init(&md5c); + CC_MD5_Update(&md5c, &namespace, sizeof (namespace)); + CC_MD5_Update(&md5c, &probe->vdev_guid, sizeof (probe->vdev_guid)); + CC_MD5_Final(uuid, &md5c); + +#pragma GCC diagnostic pop + + /* + * To make UUID version 3, twiddle a few bits: + * xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx + * [uint32]-[uin-t32]-[uin-t32][uint32] + * M should be 0x3 to indicate uuid v3 + * N should be 0x8, 0x9, 0xa, or 0xb + */ + uuid[6] = (uuid[6] & 0x0F) | 0x30; + uuid[8] = (uuid[8] & 0x3F) | 0x80; + + // Convert binary to ascii + uuid_unparse_upper(uuid, uuid_str); + + return (0); +} + +struct attrNameBuf { + uint32_t length; + attrreference_t nameRef; + char name[MAXPATHLEN]; +} __attribute__((aligned(4), packed)); + +int +main(int argc, char **argv) +{ + struct statfs *statfs; + char blockdevice[MAXPATHLEN]; + char rawdevice[MAXPATHLEN]; + char what; + char *cp; + char *devname; + probe_args_t probe_args; + struct stat sb; + int ret = FSUR_INVAL; + int i, num, len, is_mounted = 0; + struct attrlist attr; + struct attrNameBuf nameBuf; + char volname[MAXPATHLEN]; + char *pool_name = NULL; + + /* save & strip off program name */ + progname = argv[0]; + argc--; + argv++; + + if (argc < 2 || argv[0][0] != '-') { + usage(); + goto out; + } + + what = argv[0][1]; + printf("zfs.util called with option %c: pid %d\n", what, getpid()); + + devname = argv[1]; + cp = strrchr(devname, '/'); + if (cp != 0) + devname = cp + 1; + if (*devname == 'r') + devname++; + +/* XXX Only checking ZFS pseudo devices, so this can be skipped */ +/* We have to check all probe devices to get rid of the popup */ + if (is_optical_media(devname)) { + printf("zfs.util: is_optical_media(%s)\n", devname); + goto out; + } + + (void) snprintf(rawdevice, sizeof (rawdevice), "/dev/r%s", devname); + (void) snprintf(blockdevice, sizeof (blockdevice), "/dev/%s", devname); + // printf("blockdevice is %s\n", blockdevice); + + + /* Sometimes this is a bit of a race, so we will retry a few times */ + for (i = 0; i < 5; i++) { + + if (stat(blockdevice, &sb) == 0) break; + + // printf("%s: %d stat %s failed, %s\n", progname, i, + // blockdevice, strerror(errno)); + sleep(1); + } + if (i >= 5) { + printf("%s: FATAL: stat %s failed, %s\n", progname, blockdevice, + strerror(errno)); + goto out; + } + + /* + * XXX Should check vfs_typenum is ZFS, and also must check + * for com.apple.mimic_hfs mounts (somehow) + * Check if the blockdevice refers to a mounted filesystem + */ + do { + num = getmntinfo(&statfs, MNT_NOWAIT); + if (num <= 0) { + printf("%s: FATAL: getmntinfo error %d\n", + __func__, num); + break; + } + + len = strlen(blockdevice); + for (i = 0; i < num; i++) { + if (strlen(statfs[i].f_mntfromname) == len && + strcmp(statfs[i].f_mntfromname, + blockdevice) == 0) { + // printf("matched mountpoint %s\n", + // statfs[i].f_mntonname); + is_mounted = B_TRUE; + break; + } + /* Skip this mountpoint */ + } + + if (!is_mounted) { + printf("%s no match - not mounted\n", __func__); + break; + } + } while (0); + + + bzero(&probe_args, sizeof (probe_args_t)); + len = MAXNAMELEN; + pool_name = kmem_alloc(len, KM_SLEEP); + if (!pool_name) { + printf("FATAL: alloc failed\n"); + ret = FSUR_UNRECOGNIZED; + goto out; + } + + probe_args.pool_name = pool_name; + probe_args.name_len = len; + + /* Check the request type */ + switch (what) { + case FSUC_PROBE: + + /* XXX For now only checks mounted fs (root fs) */ + if (!is_mounted) { + printf("FSUR_PROBE : unmounted fs: %s\n", + rawdevice); + + /* rawdevice might be pseudo for devdisk mounts */ + ret = zfs_probe_iokit(rawdevice, &probe_args); + + /* otherwise, read disk */ + if (ret == FSUR_UNRECOGNIZED) + ret = zfs_probe(rawdevice, &probe_args); + + /* + * Validate guid and name, valid vdev + * must have a vdev_guid, but not + * necessarily a pool_guid + */ + if (ret == FSUR_RECOGNIZED && + (probe_args.vdev_guid == 0)) { + ret = FSUR_UNRECOGNIZED; + } + + if (ret == FSUR_RECOGNIZED) { + printf("FSUC_PROBE %s : FSUR_RECOGNIZED :" + " %s : pool guid 0x%016LLx vdev guid " + "0x%016LLx\n", + blockdevice, probe_args.pool_name, + probe_args.pool_guid, + probe_args.vdev_guid); + /* Output pool name for DiskArbitration */ + write(1, probe_args.pool_name, + strlen(probe_args.pool_name)); + } else { + printf("FSUC_PROBE %s : FSUR_UNRECOGNIZED :" + " %d\n", blockdevice, ret); + ret = FSUR_UNRECOGNIZED; + } + + break; + + } else { /* is_mounted == true */ + + bzero(&attr, sizeof (attr)); + bzero(&nameBuf, sizeof (nameBuf)); + bzero(&volname, sizeof (volname)); + attr.bitmapcount = 5; + attr.volattr = ATTR_VOL_INFO | ATTR_VOL_NAME; + + ret = getattrlist(statfs[i].f_mntonname, &attr, + &nameBuf, sizeof (nameBuf), 0); + if (ret != 0) { + printf("%s FATAL: couldn't stat mount [%s]\n", + __func__, statfs[i].f_mntonname); + ret = FSUR_UNRECOGNIZED; + break; + } + if (nameBuf.length < offsetof(struct attrNameBuf, + name)) { + printf("PROBE: FATAL: short attrlist return\n"); + ret = FSUR_UNRECOGNIZED; + break; + } + if (nameBuf.length > sizeof (nameBuf)) { + printf("PROBE: FATAL: overflow attrlist\n"); + ret = FSUR_UNRECOGNIZED; + break; + } + + snprintf(volname, nameBuf.nameRef.attr_length, "%s", + ((char *)&nameBuf.nameRef) + + nameBuf.nameRef.attr_dataoffset); + + printf("volname [%s]\n", volname); + write(1, volname, strlen(volname)); + ret = FSUR_RECOGNIZED; + break; + + } // ismounted + + break; + + /* Done */ + + case FSUC_GETUUID: + { + uint32_t buf[5]; + + /* Try to get a UUID either way */ + /* First, zpool vdev disks */ + if (!is_mounted) { + char uuid[40]; + + bzero(&probe_args, sizeof (probe_args_t)); + + ret = zfs_probe(rawdevice, &probe_args); + + /* Validate vdev guid */ + if (ret == FSUR_RECOGNIZED && + probe_args.vdev_guid == 0) { + ret = FSUR_UNRECOGNIZED; + } + + if (ret != FSUR_RECOGNIZED) { + printf("FSUC_GET_UUID %s : " + "FSUR_UNRECOGNIZED %d\n", + blockdevice, ret); + ret = FSUR_IO_FAIL; + break; + } + + /* Generate valid UUID from guids */ + if (zfs_util_uuid_gen(&probe_args, uuid) != 0) { + printf("FSUC_GET_UUID %s : " + "uuid_gen error %d\n", + blockdevice, ret); + ret = FSUR_IO_FAIL; + break; + } + + printf("FSUC_GET_UUID %s : FSUR_RECOGNIZED :" + " pool guid 0x%016llx :" + " vdev guid 0x%016llx : UUID %s\n", + blockdevice, probe_args.pool_guid, + probe_args.vdev_guid, uuid); + + /* Output the vdev guid for DiskArbitration */ + write(1, uuid, sizeof (uuid)); + ret = FSUR_IO_SUCCESS; + + break; + + } else { /* is_mounted == true */ + + /* Otherwise, ZFS filesystem pseudo device */ + + // struct attrlist attr; + bzero(&buf, sizeof (buf)); + bzero(&attr, sizeof (attr)); + attr.bitmapcount = 5; + attr.volattr = ATTR_VOL_INFO | ATTR_VOL_UUID; + + /* Retrieve UUID from mp */ + ret = getattrlist(statfs[i].f_mntonname, &attr, + &buf, sizeof (buf), 0); + if (ret != 0) { + printf("%s FATAL: couldn't stat " + "mount [%s]\n", + __func__, statfs[i].f_mntonname); + ret = FSUR_IO_FAIL; + break; + } + + /* + * buf[0] is count of uint32_t values returned, + * including itself + */ + if (buf[0] < (5 * sizeof (uint32_t))) { + printf("FATAL: getattrlist result " + "len %d != %d\n", + buf[0], (5 * sizeof (uint32_t))); + ret = FSUR_IO_FAIL; + break; + } + + /* + * getattr results are big-endian uint32_t + * and need to be swapped to host. + * Verified by reading UUID from mounted HFS + * via getattrlist and validating result. + */ + buf[1] = OSSwapBigToHostInt32(buf[1]); + buf[2] = OSSwapBigToHostInt32(buf[2]); + buf[3] = OSSwapBigToHostInt32(buf[3]); + buf[4] = OSSwapBigToHostInt32(buf[4]); + + /* + * Validate UUID version 3 + * (namespace variant w/MD5) + * We need to check a few bits: + * xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx + * [uint32]-[ uint32]-[ uint32][uint32] + * M should be 0x3xxx to indicate version 3 + * N should be 0x8xxx, 0x9xxx, 0xaxxx, or 0xbxxx + */ + if (buf[2] != + ((buf[2] & 0xFFFF0FFF) | 0x00003000)) { + printf("FATAL: missing v3 in UUID\n"); + ret = FSUR_IO_FAIL; + } + if (buf[3] != + ((buf[3] & 0x3FFFFFFF) | 0x80000000)) { + printf("FATAL: missing variant bits\n"); + ret = FSUR_IO_FAIL; + } + if (ret == FSUR_IO_FAIL) + break; + + /* + * As char (reverse) + * result_uuid[6]=(result_uuid[6]&0x0F) | 0x30; + * result_uuid[8]=(result_uuid[8]&0x3F) | 0x80; + */ + printf("uuid: %08X-%04X-%04X-%04X-%04X%08X\n", + buf[1], (buf[2]&0xffff0000)>>16, + buf[2]&0x0000ffff, + (buf[3]&0xffff0000)>>16, buf[3]&0x0000ffff, + buf[4]); + /* Print all caps to please DiskArbitration */ + + /* Print UUID string (no newline) to stdout */ + fprintf(stdout, "%08X-%04X-%04X-%04X-%04X%08X", + buf[1], (buf[2]&0xffff0000)>>16, + buf[2]&0x0000ffff, + (buf[3]&0xffff0000)>>16, buf[3]&0x0000ffff, + buf[4]); + ret = FSUR_IO_SUCCESS; + + break; + } + break; + } + + case FSUC_SETUUID: + /* Set a UUID */ + printf("FSUC_SETUUID\n"); + ret = FSUR_INVAL; + break; + case FSUC_MOUNT: + /* Reject automount */ + printf("FSUC_MOUNT\n"); + ret = FSUR_IO_FAIL; + break; + case FSUC_UNMOUNT: + /* Reject unmount */ + printf("FSUC_UNMOUNT\n"); + ret = FSUR_IO_FAIL; + break; + default: + printf("unrecognized command %c\n", what); + ret = FSUR_INVAL; + usage(); + } +out: + if (pool_name) + kmem_free(pool_name, len); + printf("Clean exit: %d (%d)\n", getpid(), ret); + closelog(); + exit(ret); + + return (ret); /* ...and make main fit the ANSI spec. */ +} diff --git a/cmd/zed/zed.d/Makefile.am b/cmd/zed/zed.d/Makefile.am index 8b2d0c200286..bb3e1e73ea7a 100644 --- a/cmd/zed/zed.d/Makefile.am +++ b/cmd/zed/zed.d/Makefile.am @@ -20,11 +20,15 @@ dist_zedexec_SCRIPTS = \ scrub_finish-notify.sh \ statechange-led.sh \ statechange-notify.sh \ + snapshot_mount.sh \ + snapshot_unmount.sh \ vdev_clear-led.sh \ vdev_attach-led.sh \ pool_import-led.sh \ resilver_finish-start-scrub.sh \ - trim_finish-notify.sh + trim_finish-notify.sh \ + zvol.create.sh \ + zvol.remove.sh nodist_zedexec_SCRIPTS = history_event-zfs-list-cacher.sh @@ -38,10 +42,14 @@ zedconfdefaults = \ scrub_finish-notify.sh \ statechange-led.sh \ statechange-notify.sh \ + snapshot_mount.sh \ + snapshot_unmount.sh \ vdev_clear-led.sh \ vdev_attach-led.sh \ pool_import-led.sh \ - resilver_finish-start-scrub.sh + resilver_finish-start-scrub.sh \ + zvol.create.sh \ + zvol.remove.sh install-data-hook: $(MKDIR_P) "$(DESTDIR)$(zedconfdir)" diff --git a/cmd/zed/zed.d/snapshot_mount.sh b/cmd/zed/zed.d/snapshot_mount.sh new file mode 100644 index 000000000000..5cf807aa99e2 --- /dev/null +++ b/cmd/zed/zed.d/snapshot_mount.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# +# Helper to mount and unmount snapshots when asked to by kernel. +# +# Mostly used in macOS. +# +set -ef + +[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc" +. "${ZED_ZEDLET_DIR}/zed-functions.sh" + +[ -n "${ZEVENT_SNAPSHOT_NAME}" ] || exit 1 +[ -n "${ZEVENT_SUBCLASS}" ] || exit 2 + +if [ "${ZEVENT_SUBCLASS}" = "snapshot_mount" ]; then + action="mount" +elif [ "${ZEVENT_SUBCLASS}" = "snapshot_unmount" ]; then + action="unmount" +else + zed_log_err "unsupported event class \"${ZEVENT_SUBCLASS}\"" + exit 3 +fi + +zed_exit_if_ignoring_this_event +zed_check_cmd "${ZFS}" || exit 4 + +"${ZFS}" "${action}" "${ZEVENT_SNAPSHOT_NAME}" + +finished diff --git a/cmd/zed/zed.d/snapshot_unmount.sh b/cmd/zed/zed.d/snapshot_unmount.sh new file mode 120000 index 000000000000..9f74a29e61f4 --- /dev/null +++ b/cmd/zed/zed.d/snapshot_unmount.sh @@ -0,0 +1 @@ +snapshot_mount.sh \ No newline at end of file diff --git a/cmd/zed/zed.d/zvol.create.sh b/cmd/zed/zed.d/zvol.create.sh new file mode 100755 index 000000000000..f61b1f30fb3e --- /dev/null +++ b/cmd/zed/zed.d/zvol.create.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# +# Log the zevent via syslog. +# + +# Given POOL and DATASET name for ZVOL +# BSD_disk for /dev/disk* +# BSD_rdisk for /dev/rdisk* +# Create symlink in +# /var/run/zfs/zvol/dsk/POOL/DATASET -> /dev/disk* +# /var/run/zfs/zvol/rdsk/POOL/DATASET -> /dev/rdisk* + +ZVOL_ROOT="/var/run/zfs/zvol" + +mkdir -p "$(dirname "${ZVOL_ROOT}/{r,}dsk/${ZEVENT_POOL}/${ZEVENT_DATASET}")" + +# Remove them if they already exist. (ln -f is not portable) +rm -f "${ZVOL_ROOT}/"{r,}"dsk/${ZEVENT_POOL}/${ZEVENT_DATASET}" + +ln -s "/dev/${ZEVENT_BSD_DISK}" "${ZVOL_ROOT}/dsk/${ZEVENT_POOL}/${ZEVENT_DATASET}" +ln -s "/dev/${ZEVENT_BSD_RDISK}" "${ZVOL_ROOT}/rdsk/${ZEVENT_POOL}/${ZEVENT_DATASET}" + +logger -t "${ZED_SYSLOG_TAG:=zed}" -p "${ZED_SYSLOG_PRIORITY:=daemon.notice}" \ + eid="${ZEVENT_EID}" class="${ZEVENT_SUBCLASS}" \ + "${ZEVENT_POOL:+pool=$ZEVENT_POOL}/${ZEVENT_DATASET} symlinked ${ZEVENT_BSD_DISK}" + +echo 0 diff --git a/cmd/zed/zed.d/zvol.remove.sh b/cmd/zed/zed.d/zvol.remove.sh new file mode 100755 index 000000000000..5a5e8d5e73b8 --- /dev/null +++ b/cmd/zed/zed.d/zvol.remove.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# +# Log the zevent via syslog. +# + +# Given POOL and DATASET name for ZVOL +# BSD_disk for /dev/disk* +# BSD_rdisk for /dev/rdisk* +# Create symlink in +# /var/run/zfs/zvol/dsk/POOL/DATASET -> /dev/disk* +# /var/run/zfs/zvol/rdsk/POOL/DATASET -> /dev/rdisk* + +ZVOL_ROOT="/var/run/zfs/zvol" + +rm -f "${ZVOL_ROOT}/"{r,}"dsk/${ZEVENT_POOL}/${ZEVENT_DATASET}" +rmdir -p "$(dirname "${ZVOL_ROOT}/{r,}dsk/${ZEVENT_POOL}/${ZEVENT_DATASET}")" + +logger -t "${ZED_SYSLOG_TAG:=zed}" -p "${ZED_SYSLOG_PRIORITY:=daemon.notice}" \ + eid="${ZEVENT_EID}" class="${ZEVENT_SUBCLASS}" \ + "${ZEVENT_POOL:+pool=$ZEVENT_POOL}/${ZEVENT_DATASET} removed symlink" + +echo 0 diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index 941dc9d166c4..7d03eec7de87 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -764,11 +764,11 @@ zfs_mount_and_share(libzfs_handle_t *hdl, const char *dataset, zfs_type_t type) } else if (zfs_mount(zhp, NULL, 0) != 0) { (void) fprintf(stderr, gettext("filesystem " "successfully created, but not mounted\n")); - ret = 1; + ret = 0; } else if (zfs_share(zhp) != 0) { (void) fprintf(stderr, gettext("filesystem " "successfully created, but not shared\n")); - ret = 1; + ret = 0; } zfs_commit_all_shares(); } @@ -7129,7 +7129,7 @@ share_mount(int op, int argc, char **argv) (void) fclose(mnttab); } else { -#if defined (__APPLE__) +#if defined(__APPLE__) /* * OsX can not mount from kernel, users are expected to mount * by hand using "zfs mount dataset@snapshot". @@ -7153,8 +7153,8 @@ share_mount(int op, int argc, char **argv) } else { - ret = share_mount_one(zhp, op, flags, NULL, B_TRUE, - options); + ret = share_mount_one(zhp, op, flags, NULL, + B_TRUE, options); } zfs_close(zhp); @@ -7551,7 +7551,7 @@ unshare_unmount(int op, int argc, char **argv) return (unshare_unmount_path(op, argv[0], flags, B_FALSE)); -#if defined (__APPLE__) +#if defined(__APPLE__) /* Temporarily, allow mounting snapshots on OS X */ if ((zhp = zfs_open(g_zfs, argv[0], diff --git a/cmd/zpool/Makefile.am b/cmd/zpool/Makefile.am index 741d6ef4c3a5..d5468e4fea1b 100644 --- a/cmd/zpool/Makefile.am +++ b/cmd/zpool/Makefile.am @@ -184,7 +184,7 @@ install-data-hook: for f in $(zpoolconfdefaults); do \ test -f "$(DESTDIR)$(zpoolconfdir)/$${f}" -o \ -L "$(DESTDIR)$(zpoolconfdir)/$${f}" || \ - ln -s "$(zpoolexecdir)/$${f}" "$(DESTDIR)$(zpoolconfdir)"; \ + ln -sf "$(zpoolexecdir)/$${f}" "$(DESTDIR)$(zpoolconfdir)"; \ done for l in $(zpoolcompatlinks); do \ (cd "$(DESTDIR)$(zpoolcompatdir)"; ln -sf $${l} ); \ diff --git a/cmd/zpool/os/macos/zpool_vdev_os.c b/cmd/zpool/os/macos/zpool_vdev_os.c new file mode 100644 index 000000000000..bce0de89565d --- /dev/null +++ b/cmd/zpool/os/macos/zpool_vdev_os.c @@ -0,0 +1,194 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "zpool_util.h" +#include + +#include +#include +#include +#include +#include +#include + +boolean_t +check_sector_size_database(char *path, int *sector_size) +{ + return (B_FALSE); +} + +static void +check_error(int err) +{ + /* + * ENXIO/ENODEV is a valid error message if the device doesn't live in + * /dev/dsk. Don't bother printing an error message in this case. + */ + if (err == ENXIO || err == ENODEV) + return; + + (void) fprintf(stderr, gettext("warning: device in use checking " + "failed: %s\n"), strerror(err)); +} + +static int +check_slice(const char *path, int force, boolean_t isspare) +{ + char *msg; + int error = 0; + dm_who_type_t who; + + if (force) + who = DM_WHO_ZPOOL_FORCE; + else if (isspare) + who = DM_WHO_ZPOOL_SPARE; + else + who = DM_WHO_ZPOOL; + + if (dm_inuse((char *)path, &msg, who, &error) || error) { + if (error != 0) { + check_error(error); + return (0); + } else { + vdev_error("%s", msg); + free(msg); + return (-1); + } + } + return (0); +} + +/* + * Validate that a disk including all partitions are safe to use. + * + * For EFI labeled disks this can done relatively easily with the libefi + * library. The partition numbers are extracted from the label and used + * to generate the expected /dev/ paths. Each partition can then be + * checked for conflicts. + * + * For non-EFI labeled disks (MBR/EBR/etc) the same process is possible + * but due to the lack of a readily available libraries this scanning is + * not implemented. Instead only the device path as given is checked. + */ +static int +check_disk(const char *path, int force, + boolean_t isspare, boolean_t iswholedisk) +{ + struct dk_gpt *vtoc; + char slice_path[MAXPATHLEN]; + int err = 0; + int fd, i; + int flags = O_RDONLY|O_DIRECT; + + if (!iswholedisk) + return (check_slice(path, force, isspare)); + + /* only spares can be shared, other devices require exclusive access */ + if (!isspare) + flags |= O_EXCL; + + if ((fd = open(path, flags)) < 0) { + return (-1); + } + + /* + * Expected to fail for non-EFI labeled disks. Just check the device + * as given and do not attempt to detect and scan partitions. + */ + err = efi_alloc_and_read(fd, &vtoc); + if (err) { + (void) close(fd); + return (check_slice(path, force, isspare)); + } + + /* + * The primary efi partition label is damaged however the secondary + * label at the end of the device is intact. Rather than use this + * label we should play it safe and treat this as a non efi device. + */ + if (vtoc->efi_flags & EFI_GPT_PRIMARY_CORRUPT) { + efi_free(vtoc); + (void) close(fd); + + if (force) { + /* Partitions will now be created using the backup */ + return (0); + } else { + vdev_error(gettext("%s contains a corrupt primary " + "EFI label.\n"), path); + return (-1); + } + } + + for (i = 0; i < vtoc->efi_nparts; i++) { + if (vtoc->efi_parts[i].p_tag == V_UNASSIGNED || + uuid_is_null((uchar_t *)&vtoc->efi_parts[i].p_guid)) + continue; + (void) snprintf(slice_path, sizeof (slice_path), + "%ss%d", path, i+1); + + err = check_slice(slice_path, force, isspare); + if (err) + break; + } + + efi_free(vtoc); + (void) close(fd); + + return (err); +} + +int +check_device(const char *path, boolean_t force, + boolean_t isspare, boolean_t iswholedisk) +{ + int error; + + error = check_disk(path, force, isspare, iswholedisk); + if (error != 0) + return (error); + + return (check_file(path, force, isspare)); +} + +int +check_file(const char *file, boolean_t force, boolean_t isspare) +{ + if (dm_in_swap_dir(file)) { + vdev_error(gettext( + "%s is located within the swapfile directory.\n"), file); + return (-1); + } + + return (check_file_generic(file, force, isspare)); +} diff --git a/config/macos.m4 b/config/macos.m4 new file mode 100644 index 000000000000..30e8ebf2ec46 --- /dev/null +++ b/config/macos.m4 @@ -0,0 +1,11 @@ + + +AC_DEFUN([ZFS_AC_MACOS_IMPURE_ENABLE], [ + AC_ARG_ENABLE(macos_impure, + AS_HELP_STRING([--enable-macos-impure], + [Use XNU Private.exports [[default: no]]]), + [CPPFLAGS="$CPPFLAGS -DMACOS_IMPURE"], + []) +]) + + diff --git a/configure.ac b/configure.ac index 9744bedf2026..3795e661a8ab 100644 --- a/configure.ac +++ b/configure.ac @@ -141,6 +141,8 @@ AC_CONFIG_FILES([ include/os/macos/spl/Makefile include/os/macos/spl/rpc/Makefile include/os/macos/spl/sys/Makefile + include/os/macos/zfs/Makefile + include/os/macos/zfs/sys/Makefile include/sys/Makefile include/sys/crypto/Makefile include/sys/fm/Makefile @@ -164,6 +166,12 @@ AC_CONFIG_FILES([ lib/libspl/include/os/freebsd/sys/Makefile lib/libspl/include/os/linux/Makefile lib/libspl/include/os/linux/sys/Makefile + lib/libspl/include/os/macos/Makefile + lib/libspl/include/os/macos/ia32/Makefile + lib/libspl/include/os/macos/ia32/sys/Makefile + lib/libspl/include/os/macos/mach/Makefile + lib/libspl/include/os/macos/rpc/Makefile + lib/libspl/include/os/macos/sys/Makefile lib/libspl/include/rpc/Makefile lib/libspl/include/sys/Makefile lib/libspl/include/sys/dktp/Makefile @@ -180,6 +188,9 @@ AC_CONFIG_FILES([ lib/libzpool/Makefile lib/libzstd/Makefile lib/libzutil/Makefile + lib/os/Makefile + lib/os/macos/Makefile + lib/os/macos/libdiskmgt/Makefile man/Makefile man/man1/Makefile man/man5/Makefile @@ -193,7 +204,6 @@ AC_CONFIG_FILES([ module/os/linux/spl/Makefile module/os/linux/zfs/Makefile module/os/macos/Makefile - module/os/macos/kernel/Makefile module/os/macos/spl/Makefile module/os/macos/zfs/Makefile module/spl/Makefile diff --git a/contrib/macOS/VolumeIcon.icns b/contrib/macOS/VolumeIcon.icns new file mode 100644 index 0000000000000000000000000000000000000000..ec23398ef3f02681203e5c44d6fd392b30da8f70 GIT binary patch literal 1753264 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%0D0Rk98ESF>9rIQ|9;PKTKe!9N&4VYw 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 z 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 zem⪚*&l=-*yY{CTEOW*EuKUcxjznF{zH@C*~{MWG*sNmpl!@Tq7XhOJR^OV2ztq zjB!5;=uGnxGyaRKOWAk7x{f!H!M%=BTi=5Xs|mxEx5*?u{g#*TmJy;@1{9+dBbdWM z{JEb=HbYWQMaN)e5O1t<8a)i)cE5-F3A09Z3}%>v45S&U*r$&B{9%QhDC_(k=%0T0x7Fp@3 zK&Sou)n|ZI3W{DD;z|O`ieLq-`OUM*p@cd{oZYY{&bEkW$`Ar|H|;$>`BL=Lfx?|a z3z`QW&H!_ZreulIIb)>oYRsrz@qC^az2{m+omaQI>SzMZxD99I1~un#R81 z0XyQK{;oHLGVBled=Egp*YQ&0)G-0RjLFO%&{u`A90L^`d*QL9zzk<|{NT*-=EdU& zGc)dr5c`X&C67cnqk#`1dkwSB?yDqoN7;XC@L%f|iU zxH84&SE_bKYCRk0KV^+E8df;diJ+9oln!<|5R`BA^Q$;jwi6e?A-l$W3}4F4JD{k$ zS&H>YNx>Ca-9Bel7ra2Y^Ww=^N6F>1vzGT#at(BV=KR9zyQ-|!LiPM(hyaK3%y1%?P$e(4&D4h5nDcidyj36+xL{e8;pCL z0_q{q%FO1^M1=UNvjEeMAuj4!zV~X%=+cSdF>PR~>Q_da&Ni zXLV`%2DBWXoaU9|1^o+Q=(DUTOy9wZ zuR*pp#~!0Wn2Fz{_n;9^>DM6JWHvg?0p{$$E^&Z@#5d_(rbS^eP+@O3<7^S=Z;ir1 z7)FfJFg_6ID+Z)}I@mu<8GJGdw8UqaSyq*=F=o6+SYD*JkvGjKe`R_K1{3#DW%D?D zBu-_#VKf3mhSkZGS_uGSLAV$cgpsatRnkQ~P#29a%h4#!vVPGtJ;EyAVihh7Y!d_2 za|?t&OkDQMCtYnVF#iZYm;3u!K8-~MEgP1Y`RBktmh(oi5MOx6Y$=QP)?s*LFo%Kl z!NphCOuxjHrQj6kiVN$eauRQB@TYKshNE@s<6{vKE5Ne?3YZ-9$NQRTr;QbO)W!ON zB8mn-{)mI07gKXk?3+e|#;6E5`jXz-dmhLN4t`!Hhv|{R9OkF62o~#RTZP`11pVWz z0B3_RGYbx!A)GCiDa<%==%3P}3lY`|2g@OTXvD7u&<{dGS|5;u1GN;-+4MwM7XWPL zM18*&p|hdr=6elr`)X%^1B^Iw^bOM<01TQ6U??HZ63S4-DB2`KDzY+x_O6)`>PsTqJa^(?^TE|y&0oFqNtS=_XKh4boTcXf%^$qb{K08f zP|}a1uoh~;Z-l<~1fOg=#qUQ?ooxP}U!QM&b>6QD*!%V`s9^s7)5n^B^~&>^^-oC@ z8NSCuKT7`;Gw)q1)$SU3` zALY?rO{t;fK><}$0qBlnoHEoSG@fD@$_z;E{1ja!MaLm7UwtA%4YD7@(x4$IPC49; zD6?q%XGu6vEjR6wX+S>HNHbyrNvDny*f{kC_0Q-+yjHfm{lkBT(mO=fcHli@ z*mH}z&m9IQ%2My$LT^o5nUT0b&cPC{b+!-4KjmHBjJ4%Q_u^_F%0vuloMD%q!5>^K zQ`(xBOM=f9VtG4(*Fmz2%C%A^4RE3haoyTtvW4`cteje7HLkP&66Jn-^T>qNU35p= zA**eJ1r%ihM!MPtjt_Oo?X984!vROor_yrJyw8KPq1Bc;I(-HbP`5Jq2Jg(}jG8-4 zB$;P~6}Tfz#7HOa>F7C&z=@F1Hf7Tvfv3YqUK)VX4cLwwX<400+S5OnNHGvUnVa^H zHjZ`-Iw`x&;s)9MI`@7ueRg)%PLQth4-)hu_JjgG2f&_?@u7g&v1K5+?uG#4k(DrR z`j2m5sLSMBX1R`M(9`f01TywPPIZ>y_+F-_iKbyFPm*yDY{GCD$k+IOsA*Q2?Qofu z19O8tsP6mT(g5571sF{Z7KE#KlBfoiMq|H<6N7Xcr$dB+uz?wY2ObI%N2yHU-X@>F zgAsVZ-5~g8FMaTI*}r?|VG;;1mn2=}(}OaRQHzvT0CCw~JA*^5qLU5I)5$Lpy?dtM*ujak$tUI1)m7Z=7 zm{3w-%1VEgp3qFi$96E(Y6BGzaTIrw@=v%5oouVH3~1pAx&;z=wxvMGr@`&52UuDM zof0~H#8qk$MRrvg6$bjSjt6n#mu)VzFUiP{(4}vxn7Fiaz-4umlUWw>mcT>d(kVtB z>tQu)rw!UxO4&;8-?Km2?u#sE?S;pS0=C&dJvGz(%RhWB3XEgV{^rt0XGAi-=sWfw z@8!GSfAUy!i2dpR>)*fM%(?YvKLX_(f;auifBt*TnFCYlt6ADfFSBn&S*6bfKb{@$ z2bqEYlOKJT&x;Om@Zm*bt=--S%lzjroofF04_<~Z($6!1iG}$}r_3gTq%6zv=1kHb zU}ztk9&i55-@eOU{{8t3qcxr8z^?z{KmC65)RCDQ>%@T)^x+S9bnO^^Pt*)682;NI zz0CH7;pVS@{fQuY;W46sU&&BjmLXsL-1bbzw|~>W)DqNZzZwAWHQ~ROpbf&u3ixQd1GDU(4cVRHArn2-;0GfW_MLjhT%jFh^`oEKZaN%-X~o}7hUQN zV0dR=qJIEeJ<>j)(VVk~cpu%Kcy(Xtk}an65ra$pHTgM|fCl0jI8K^mWhV7@+wZoH z0(Ac2tazkv1G^?EfJHv3wE=Ckx0m_))G#-9=7*y*siQa%HnzeSt;lCju>USIVK~P2 zDS3RqIyrqsA4vK-skS=P1wy7TK#mM0A<&+#cs0<5V_#+LCsF%{aYxl%o6`%Af&z}l zUibzSc#S0}Uy=Q*-kELp``z~ei6{0jqd2pWE^lI7#$Jh%ni*sVAx0MlhRj)^Dbi!wmfbfqc;=?*S!qf$``m zR|slYsZfe*38eDMnpQ@tz}C1gEF=#9NgqCr!DjGTuX-)#)$muadvdtT_`OF-4MRrc zXkZd#)_Tz`!#(rj6c0N>aEYbd)-8oWI#@czD%in21v~RS$S18vnh_i{ciC>C@vn1Z zjagt1ICM+HntO_&t3pA1oHh2GhA1n*iD2ZtLd^Fuf_Wyjh4Nn}Tf)t9vp>u@$pziK zQD`d2vILB0X=?C*Mj_Z&0*rTVZ%}~c0I&!TL=I&W)7DGF*I91_1nCUEQ}JjYLXrVy z?=zzh9?~(w89WW%n^*kYBM;Jv6Gxo|I-Gnbp23AYwpZFk+^igBI_qxzG|CN?2Fcde z$1NHv5(66l!1I@W1s;CJP&lrrA(HmM6AyZs)$a^{(u<4U>3FCq)_Q9gMm(FJdOKqv z3}IX6o#m?H33&0g?Q~B0nXq1XyeRPdPaSLi>wofHdX(+S%!chn+dE5aa9r6}((7nL z`zbBnoIEhu{HyOj6aJ8dd;G_<2mbYsUW)P0K(NPyS2;5sc`A2V1;f4g8CPSYY5pr_ zT>K7%zzjC+V6oz0GI-CYyi#Psj&P+K*sc z{W0*LX34)4DIOv(kb6ACP4$+CS6gb=V< zj&6UaztPBvvk$u3%l3}^H8H>NUE(rcjN_)0Jw7n3BHxsuf-;HlHC)Y=aUW2W7ui$& zHnIf$#4#9IY-ZkDeu7;p=}qH<`D?6bR5>P;SML3vmWAW9^tww z!Linjcgrkig1uh2mf(F0kW027@J@r#ScA9>v%Ry!Lq|8#7pw|)WSWk5XidpG<(p3Z zMO-qI4=#ZTe%8)$<|L1KO7C#&6>hu*PRVTRw1Xp9s9RW%;13)_B1bz%=cw=Na zu$)k`-NeyU8o27b&QMDqafzN`E#W)%mC&%|LE>FBW%3ODgNHczYb%IIKuHp%Sx8F} zJQyz=BMgC3w>y93*}DVjEt~%3iZkm0a-r!ohl(CQe3%pem$M2}ez}P;e;0kwT18Kx zu;}UVGIcT!2ehxc;vsZHk5=FD>s|NhE4`R314jCad=RAKIAB%r5}yI|Jg~X>c~+&e zmDMi~;L!FULeEk7MZl0YzNW<7Mw6}<=8H9V@) zDLVdkhXgn;M zkg?0SWAGsK^d7<&@c^n2B9IuYB`_SERh-7C#_q+dS9h3P4iyP|u@r}N87q$H&Lim? z8MsU{m^k1m&pqYcz}INf^Dd(;mDyb7oz(j$Ltf$Pw@w#XGQSLi`OSwP=CdT75RuV| zLskoLwbJ)kouG$Q!Qn~-R}&cghAM0{Y0RTwGhGyO7+jP*<3*Vuu|&DoGWqdU-|8_KcH)vc;vt$7`6lLZ5f0)kq1wskcTbwIMzzKqvOQ zA)K8FH*~85tcM*;7&347#swaDE9{~m5FdP`m%>84h`Wn$v3|1tZqdvIF4oc2G2Okf1{FEs#K58-*R}GLVnO{VD+fgCB%vO{<_~eU0ZNOw@+{7s z!e%)-ag1NZ@#7(Yz3})@z=Qt&#Y?Btbaaw9zS3Vnu~X)4OWQO*6(O44{^!5%vhV%* z6NmPpLM*T6EQ04x9t^Kn$yN!BF_H2eTRH{pyVdtaqKt9l`q<8J8lGP~U@sBWDf*wi ze1`F$!UtU}7y3ZH;H0u`TzDh#rS8GwTFd)I922j+c#;_@$JgG5=Qz8;&&FB4p);c7 zKgLNzixsT|?`cQh%THf@=}ZoOY~S`Sj{yaAw!7C^9mm#E-Z&POGqz*+uy43mS8xKt zugW-neRjOs7jr{8`)v#wJFNNqwH?B%}lCWBRWBw@aeT@}Hrr zpI2&@2a#2R90S&~jK!*(buhEKX6X*f|AJ^*`HwYKa+c9vWr?I=~m^+Uy2^;l+sm_SNn)3I#H_+NJnAR#0j*I!^E_$ z4#Cw)svWqN2X4xEsiq@9xRzn9;y@scH20T+b3>enwt&19@!(&#dA9TVF0g?qg>A2V zb&0FG+5}&UnPH{Z{K9;6#hl9p9?_Y|X@2WfPA6#tXxhLDteW^E0r$~$xV4|l*XU8$ z22NnXgQ2DEX|To{=p*XWPE5HqRfm8Rbjz%6y>{~^lU55Y9C`!!%-i9SHirgjpNib+ z2T4a6;$mOViVl>_BS(%*rSSWI3YmKDK7)S$ZSU@pP+)f>Jdy(cu@?d(`dT#CDZqhk zW3rtvZ4JNHc-mwZSz(bq`V7R}%dlV)dc(umVFV6!1=b0^9lF4PzIy#S%uRs`Q-R4D zG+Z@g6qsQgq<8tN_x@@rEX+9!)~g39@gq%nV5tsyV-$)Duw8=f%yt?WgT;bn^8)5GV%^29gf@acL13n4> zw6l!LYkw=@=v91UIFU!sw5W7#C0y%Z5dR1T96CYQx@#a4Y)9KF#&lu|H`8}c47WdY zOYl2mJ}X6R5NMG40nZtI@5L>SiMF2$)-qJiyv8U=Rp~*go@J6Ib*(z6^lbpIw$%U? zJPn?h9GrunrB%qmnAD5^g{yU>oz_t})^!y0!ec{$mrfmy!Dsu}y7itK9gGD1YkBieP<7Ar33PZ99|d48E{oH!>_&4r(IGX9NTz1pWM)X8)F>o zo;q|OGvRxPRzRIWhkN$;fzZQ3yYT?G0MVj~7sr3(%MOl-3=jBcoC~n)n43LA(E}Va zb}f@1SY+&V1=z+NzQ&nEypxu9hxzp2D`$>%$$QtwfC5W6PM7>FC7r5d0c{IjG*@Ic z9lqK)*J}5^IGfVAVe#rl$`#vLV?=osy+J)fxPeDcAl|Iesff)#(o`N&@jnJRH;z9; z_yI697Srb`LtQ2Y7x9symKPb7EbTB6SPdETiii2tCk&ZQrHtqZt;`9gXQ|N+zZ=y|4GB>HIyG+)Xd;)T-w8S0QKkt<_mMK_clW6ExpKQ`PCM! zBc`pg@bb?#4qq%9RZ$&&YFrlG!V;d(mKQiB>ru=?S6|9si~d38M8xcIs>y!q53wY}cBem}-t4KGHn*^fS%)_;@ordpm}CG%RNHrKmc6>IrV@r|Rp` z&m|N=JxAK+FYTdTu72)bep7%ZeY^B*`>cA6ZM}{IV3ifQE3CBfY$|_Ouia?g`|vCd ziJ8z^a%gXK_JTx@BSN|WCzEr;*~hHgPMqbE*;N42_|&OWJD&pdQ|G)kR0ZnZdUx?{ zOz*Pa6!=^N>~~@R2;hGiJ-;kol05j%XI2Ug4l;u{q(?FyOAIv`kV|nkU?iP0AOIYt zEx+1eu;!pU;`DxMtdFxl^w~2{XSvU{>o?3&j4tr-62dT?J(h9EZ1hUHEPtGp2LsF` zjqyn=;dq+4r;W;lGeb@qnStKyozQYP4KVjrukih5J)7@+@KLrfO;1h|@7Dql9HcFV zN}Uk)!j)O+)%5dle!ale4X$8F;AQ7k)axq?FE-9w-YL)&PO+ z9F=Wn_pM8eeePxL156ber|{6>aTh%Auy@rj6ol@;w8~ah=I)_@kf9l8yT#Nf!WN^^ z!xql`@Wspwetdy8Mc7H;v ze31c|);WqJ_u`nX38bu<7VF|a4y(aHSNV$KMJDT4iU6xZ*dHex>m{DjA%{7ssPJ6Q zgu>772p8?@JPY4RBR?g#k;Vr`FMR71u-%?Lb^ttzKiKE&OK}F!KOLX;wL!+Ae8cf# zpS6Fc50KOGcZ7ZI&UEc%I6O7l9G#f}HAXQwS*HA1wUtg8{+IH^$Fl901n{zRJ{jZ(QiZy>(KTOdIiFIqEz$o820lytF(gHB2a7sea(3B5l1=_vkoW;q% z$9`oy7T#_@?Pta4Xfrc2)ja?7GdKoDo0~UpHA_s`xn@+@pp z2dGj!Z3(r1-QN7Z9dhsF#_geA}VMxm`3|@ygIdy~ss7Cm7-7p@+F@7?g zF^t<$n2e{k>h+7_%ZPWstcS~#FenDm36%{zigOtMSFhe|COA0j$bmzgdOAW{5Dx=^ znNhy8>>9Z-z;u0zON+b0MF&V2ALR*KdUUhm-gnH}d=b7fP4b!7q6|cZk$LCgY|_d! zpb@(2=@$YhH?A~_2jz~#h$qC9I*u^szKiF&$h}rUI|LqY+vd8ZO^YTL4eP-9vA5gharqq(Bpem+2KI zsY^=_jayi8m~Qy2^repW0qZE83<|g8rG61`z;P*9!4z)&tgJ%1+ERKeJRtyhO1Bb- z<&k!^hxF=jR|&C1|7Qk8rws3PN(d);dFK-OC}iMJ+bk5){@4zHaUtUASJDGon6?w% zc`rrNCsYXfUMeu$re5~Z65_4E0x|1wKOhuof76)9C3wP9xe}MaB)M=^QurS_ z$H`&5ppsAFjsirz$QBrJf_d;JuzKO~pujNv^AM-Sr~fl*GNVEtu@TdEf+lUvIHOt9 z54v$%{jtX2kbRzhwwK`md~%q+*R5(&v$RadRunqk4Z(jgZNqz8u9AM66bm_bnue+3`;XVFRuK{>~XSb?Wwz*v8xpu$*Rcwu!H^5_r;OXUUc_9Emnf7Clp zUa_Fy@VIthrj0VT3)d>M=;XcF$}c=fQ5n|@j|T;8!xbk6Y$F=gzT`MimPH29<~gC9 z7aAJ|+tRaxb^`AJL#$Wi1WS3+H|!*YlBe=@4Qa+YeP+i=K!jy>GyTQ)Yo;?Y3>Y!y z>|3N1V({cyM?p7cEhjRbLE_hh4B0Lt-oc~fYj_KAl+hxcb8)DPd`MUt(O%7iWH0${ ze&9rxVcaJWr|jxzl5s&?O@e5kyCUEd-YR|Sh7^$l|{r+A+9{B_e8GeO_go^U1_L3U_Hbo zdJ%clF9c}&(gM(+18%zrzdr^k339LF!<51>W%UbbiO~wpeU4s4`k>$iKrNLEZ{V7@ z;MCRU-63)YSfLwT(|TrQB{b~rC&xJp;KZ@!Qpm5H64DP?ZBjuE}VW#ErkOfsZ$Hjx(0SjC;M4VvakZ6)q5r-ki$ltNA^sS~MECeP5vC03?0PC*IB z%g^Ob5=Ukx^R|4?RB$DQ%c-opC5};V1+Vx=$by(N`}3Ukucy?n5^$8SQX+IpgHfDj zgIgLV#_+&`;|8<-D>43|0iXW!^DZt8SD08Y_upHN0ojLgjESw4o z4)GC&eN4tLL2Vo1=}fVY_+CXnWT!(Y{5_yi_=RJ|#nVsSZ3n;6>kNwY7LH5n^C?El zB#m1H?9V)MYAuc^Btt0?{@oJ>&H(b1h?rzdN^#6ucO zaa@Q)K4UAbG9F59JB~~vZUvKPa0}fjKa&~cudxM9C`6}%?IC{2<2dCjL77w_PW@nu z1Mkcec~6{Uw+wK`a}X6~=^DZ@Qo5vmP7)ZLNa3PhC@h;AtSg_69rD?>2I-zjmG(S> ztOy`3U$X*VuT@h^Cr>W^f#%^aT-#`44S@^{TV*GJM?!loxguZ2`2t_GZJhM*3l3xG zqhoxDL>>Lu@#D=8UU{W?@`)!r&VpI_%joG>b3mV(hZAP5*0p}>EYA23qKgkOF=ZdH zJ-p_Je(Tm;^Eh!OMxdFXZne%~fD3%F>+1De!Tp72&oob>r;nlwlPP^L-HW%|Q2i5sPZvop*>U`3ZNA*W1NepanIG!8+;P|mBUFhxq?w>tLoA! zm3I#@iWL|U2t%eUFo^MOz4V-y(9-+v46^lR3AuuT>JG}diSRQo1|2>+YO>#6SiUn( zI0)EsXqXQA5J%xDSz|lKits7d3M=RZ;F9@rniR1lM;SI|r;T`4U+DJit4v8Zm8n4_ zrh-vpyy#yFhV86_!C8h5mhIj-6O2p0me#=XpN@oXnLZ^$3k8=bsy-CnarV&pQE3|9 zMtSnHK;{cA$d_#(yt7|~)~wEu>b|$%ID6oi0tWa5rg%sV$B2TGBw2AHC2X4rGx|wp zII4o7@dhwbXK@!lJC&KBm;Fdbg!PrKD+tmAlRO_sPCDhbv}VmgcwT{5t+CRp z7aku9EN~E@GBE}w?UlB)KN0Mcx#jB2eb=XK(>RrBcY0{#YgU0UY&D%UuJhb=!2%c9 zSw#a_;n`zi+%wpPY2Swx0MFqs4EsEHAmYK6V32!Zcc-_DldMCQ5gAKfWsMRoMz!zT zvchA4X_t&1z>R%2GAQ!3ts~{6@}#SkLVh9-9Uy&N94Jiq!X?gu5NCjO;4$!|DSX7S zV1dfMOeXP(*=&q12Dn#^5cjTc!nNGT>|o8H=e^6G zJ=BBIo9~1UZsIxABgpefmKw{(hYhrY%mz9qC~1G9j*=7ha=HR%Ur6 z^npbdJ+BHB^Lf62IbHgnC20sG9W6^M8_gX1{$uz7Cyis-K=db_t?L9!iARaJ%QA1j zo4N{fU1QeTy-#{yT^{d&gYj~2>m5~?@!ejXG~l53x@N^`Ytoy*AqEap)k>t6rbSYxmSqr_cZcK1=DSli?nC3Bz+F6s}PK zz)4&!v&to5>H#gpnRf7>fwFf`J^19T^4yZ4hnc)OSGWW}S5*w6Sd`H%O?ko8yJc8@W=I1Ag(QZ4IZ$+Hd6t0m zcQ!ZePgWHm%eP?be|M|A*!II1gN!}v(Us#pRVKakA&-We3I+iDt6|X#j}HY@kS|`J z(MVFzfJhp7*xaDb}S$=ZB^Ew@%-UEpL%2{D~$6S7N zO!YU%euDX-bMYNB6rP#gX8c=bR`iiRxKh46)iQ;dPl5&?<8&67r<2sT( zHU@%+n# zS*l7}`l+}}du4+T$Iev=>;R$g=M(ID`qKBWz4lr^KRZD${EigpIRJh~ zg6(B;7`&a@Ujl>V{{8UK@eNMSckqQdk-`&dH9GaGx%5W9;x?xUG`hbGK?c(g*zT15 zQ4A;#pbJxDQ0pn|$A~(`QXd(HgWgYo#F%IHX$_-mi{+Fu8<$^Ow($F0{$ArAF3^ry z7`9=Yl>-VVR~ER6V1|SKCiuSTp_xN?EXPm~UEai&0+^15Z82>H5{wq+?M#e@sa*z*3zNkm|a#sw@{dl#- zLW57?Uxz0SHp}cg^w7TuOx^`gtyRi#X}+Fo>Ea=Y;}oU=xSlKG+h@Q({TQg?-WgA>u86|}dKl7D;HrTT0Y~S`>I(ax^#%St zKIQ?H=m)Mgp}m1unQbHbG&wU<%lk!UihJSlp@973H&^GHAHA@V)jRe@`?H-d>s$LgCGE;hFp8TW0M+$-#EFRnDNpS#lh3qA!XpKuJi3Jgw?@zlwf@MIOm zmT$EKIse|+0MP~oD#MVGmoiMJ#HdwwatqQ6oM0czN&x!2TArp-jA7zU zVrpfqSJTRW)CYJJNX8d`)0f3A#=rG%WubXYD-QnC=oYZc+oi`2j{uy!OcPeQW|_Xv zXFOTziFT7X>!c3U&Dc70Geb}O7BuC;swCGrmTs^GFOt9L)Dsmd0Gz_ZG7PTJv@NWc zyg;h*fPUtr6}kjz71s zcXf_D&MLpEkChT9brs2a7k-sBq{m;guH{^!!PAg$RFJ9__Pa z@efVJwI(?F&?(f-#A^V3Vu+WnU>fJ#t!>YVq71iJ^$+-|=B7EwAJ5@BFgeA92%i(? z?5;IF)#vKq4Rkw>25UCa)&1g>JXyzeij9trLq!@v8#-UMxsFUvH3w#9nxlsfHK&>J zKYZ{&bL_~GW(u9%dTRU|>}Tr5>7S{8IgHXLL${PdmTGsXKy1;)I%M3?pIR!YFMCSs zPSDoTL4Xd*JXztq^Rm71)9JIbvvwvsh1Xu#V+vGj?=jFGxZg?vJMF@KcyJ3W5xi&kfJkH5n7088{o zFk;>Fv&`q~?!p9w?`i^-I2lpI3;7MDBzu)Dz*A&4)I{Uey-02waM{l^Gyan}1I9Ug z(A5JnJFkWjaLebcVBm&=L@D4SpNzMh@-%FnId^HB`Mae^;*h;*VXQFHmU%?LkT1)T zpqcbgXgcd{8P?Y?Fp8(acSFwp1KUIY6c`%DGUGWEU82clGr{~#PlUojib&gWXg0yB zfz8eO+}{c_6&n9CX^mvLcYo^$*m4G0fWm>ns&-1zI6D+N)WJMCD2sYY2XVFz5vt&o znS^!)M}!^k6>M2f7=b5r=tmmsnokSzK87t zpQr#uLQ{-;1dxn=gcSxglV%y^@UTz%&v(`|hIi@(?OJ16c=iSBS1S}uSDtv#Nq><3 z(q_w9POf5UsQ#A*%5#)CF_jMV8K3J@MLHz(rqc&(4}Q`?MbCD(u6oJS4#Y=*D!jno zF=J`gL&MAayLiwQ+8X=yxheW0LTm?)eUW_S5ifYF(8;d=^upsq0eQ_GcE9}H`D@L; z{O)NxRK`;J2t(fXuN6p+6%7FTjp*Bl=?F`!o6W!f^@ZBrv)9!E{`*&MHa~s)Uh_|X z??q7qH^w!6HOe`Cd@D;j>A&L581>rCE}pwF+x+;~pAom$h!wn_eDGQG+|h&0>0{GG zh=YnEGKz5&=OhI?Zl#s{$+2p1Z2t0-tIe-3)>mi}xrcwG&0oIrN%KGc(P$1C3_ZZ3 zGlaxLURDZvxUWxXdy)NrfAdLm6TaUIj~4}W#;gB~>%?!zA?gmY!q%g2;q9@lBg^b| zG_74O{U149I*en4zCdKsGL|%obr4iPQSQ{UokF>mc=D73u2zLfne?3W>GYlutJ8!Z^{LtDqcGs@~Q zrg7}W!LLjr#OQ>62H~q~NLTG!pIzdw?k5a+o=)uOBfRr7a9M#-2-FI_3jBXte=leCZ%WUE2SQO~fBlrAw6#H2!}_e7T>DrK0If4-wNlhF8X`-kN~ z$kg4pLX#Ug2?R!Y-b5D@ude^Zi31NUJyR!zby9Dtl@7{*Ef()?K`#yBYz-liCB@)P zjc1j#!xdkHPLJ@@~m#6j!GWS&06L> zDi8d-H>DE+yl6k$RUJe$luhX;7TVoYfS-=-M{n_K15;B|%_N7gYV=RCC0J*`1lxW^ z#0fUr!f|G>uF>%U?xaWbq8b6bwSG|7KF1yC__yMK<>8#-vf?Ua0!IGWE@R&UI;GOr zRiJkS`VUoRHmMi(odP`vz&9ZAYb-taitIb@^km759|$||iQj#nWE^sKq>ntZO^siT z7H2jXZy9gOOrzujh1Mjc^zXa9pu4f>9m8g#a?G_d7xeW|&~{-1KV<%jF2B3-94R!kGG4 zFHz?<-fs{@xLIwPnFpnih6F>Dsn=8nsWu-BY#BX|2jLK!^q9U^f)y;^M;vj;w9fr3{-UVhm%GiSsf@GK( zdrH|^4xd>^=wz8X25e{hvI0^9Yy2z7yw^x~MTc$!1@t}WlNoZ96WhI3C*W-tH%sS_ z@dcOmw2r=O328g1*a=TBwwT2jvn;U`0RPS7>|)-jMe!WGz{h3&($&U^l4t|kkAZ7u zm;DRhexc&xSc?z^udE)S9@1UMlFLpFI>%Br<-`Fc9)z6Bz-u6BFExdqswSSaTQX_$Q`!+r0*1~G@r*EBWuFo&+Q+1dD zVwUHxKEByZ9X{H;^5oI<@hqiuVEQA7Tk&SPGhNv+aE?_8|LzwbHY+%t_AB^Vw*T$# z-)sKlpM9r!;rNj_LDu2j@+^>I46-De=Nc=j>or#2xOlbsi#I>c*xE1LuHIQ_{>|UL z-TZey`d)LK&uYc#={SdGkrCX;LK)E+>(Wf({>^XBH$OXjdB4!=!TkmlSV2bWSRX-V z>$JDUG~SBmmmIR|$-84@(ZK$5c2U`52ET#s4c(LWm#@?6Jk)Y@`hqaw)iHi~>8yC< zi+G#Xe_j>b^89ou${73qZ?G`K0hU;;TxHlf!Xm{dB^KO*t;XF^&OvnfUWZxh$>RwcV{;kl#hdl13Uz zXXUB&arEX}@4+m!qztuE@^E$bwrG!|o%|>Ug)jHcJ{u)LicZ!_-Aczf^mf6kv=Dd8 zBVO%6dRa&HCvhaVP|z=|S>X_v*2Rh$2b>tJwmDJ8`itDQgIG*(wk@q!U9D?isqc}& z`b5{(u`16epJhZRiR0DMq{=@G!jvvv4W=pd+BWo=pH8VkbayArTtPXi-A%%5P31iV zXYvF(_D|#xy#XdMHaGe>pKA#zoFmQH=-Y!Kc(28s@`s+bn7#=p{jR@% zXmoURka6jL%FY<70+RDf|CpwLe?G~V#eY6k@98TkKo9AKho!))ufF;qALyRF`b!7r zGkl(OnK-UG1B}m)d)ol}b7;_a{CpmG^4VwqC%k8)8oI^!8E$Y`SM(@M9JJXho)bX1 z*LtOd2pT+zF}xHAG0<9O4YL!L%wA?Ia}o)aH9dW!Fc^(t2fhYg8SNU*D#bbxxQD@N zBn@!y4DZkgON;yj9!vfw@GMTU1;9O_8dhU696Kq9!vt5DIvb#K2y-lc9DtHadWGXR_= zS2W&1xnI)@>#6|@B5iOM2VjbaX9Y~N?_bk6ifQ$Z_TBiL}O%OtIjaIA|p z3q2{PJjn=Z;)UOZ(1wR7(J3Mauk(>>gt^jhs}nS{3Nn$f7X zv@W&0SY@b~xN)u;Fug{(-e(?C4{lO7XSOwRrFoY3b5#IqK$O4Xh~T5&L`affSgD)B zRjtH#lvB+~ziV-_e#Y@K3XAnoL9snIaUR@<(e_u8s+gEBiV=6x**=^8!~G!bX;Ie0 zvaLra2ve4?_!(3f@;-R-%s?tpruioO%n+(m-c}{YV7t+kQsz z4ocDA$QRpmD0f$QtXwZ#J*^!CLXS9QO7cdPm7*^AAe{_1SA(9XE* z4^{kOSj+&4RjmFp{Ua6xPbFOH;13Il3rJ!?Z1EbeDjxYpRW}R1!7SQcJfS>U1$9N{Ef5C-+pj$U-0Px{pJ*qAOGRACs}b< zvMJl^1Z$HrBw~CXE3@&tLvu$*S>db>0sz)?}cXwPcS?A zcV?x5rvWJrU9b$YCpc>^EGJ1cgoMri$ZZpdhq%dS95?DFHGYJ_U;KQb#?opdaDzC zo{!|203I+nicaF8fd>!F@Oi;0&gMFZ;g1slM}Vt!T@CD3V0C(di@vl^<)n@Els-h+ z@1#rGAt}kibKzBaO(+b>kLXrg z+tdeDz27w3tE;PmdhjQ7LyGa{$3On@&b`mQ@bxI5#?uRrhyrwOhSBH37e2$>nE2fu zbMWOLBS*a+Ne89_k*@BHvdh^ud^R_@M?f-IGLUnK9(jg_VRD`az>Lt~7WbSHu*r<1 zM(-MmVvI>Ww%afOnC3Fe*bmH1HtQI;e!^^x)9m#!I?fb&VZPiusN$^#jk911{?lsD^F!s={v+RDSAEM&`1n)CsySaOmGkm@#;T z3j!_5HXx!%XyCE*pH&lsJiDD@g)Z*%D9GkRfS7v znHY3LF?&l8a8Z}+>b5aXJ<=`<0bCEMqG&eAjLtD<0t(*WQK z1QO(AWaD*QiHU+KX@FrL&B^`XZ@oR8-&GLOE&V=NLQCqV3rB%ncmjzsP2&tfY6yIV zp9+`fWORZ@VKnHhfwZR{(!?wZFk#)J4b5lUS)cwor!#_%QAXPID01Lfp#Qq+#`f{+ z1ky@cl+jNS)Wc_SRjx64Lf>=+K>ftD7amUvymRqpbMD4m^BwlxKXY`tIRO7t*|Tpg z!Y6OcuQtEFdaJpHgOJ8e{Jz)3{>|@i-@gz?!0&M=;IqdMHwXAuy^5jyVxE;pmuHun zw=Z0O2UvUh~tBE;qmb^s(jy3h*?`Uu6YrI8^31ljAI_CEmMqJMAma?uEyb0vqs9 zS5WOVk?h|0`3k(+XPtRU5`ntUJ$e9TI==ajoG86P1Beddb_X zy_`f7Z*?3?&OzD0w-R!CDxb+BrMobrTTlaul|%icZ5yiidd}arE~$}uQ%P`8cd!V` zQO>wZPzS>ZI=ppoWvFL!c}CI%xURCgbc5}@F8^1Danhx~e>|n$#ThHS3hLC>5B(Qd zItbKj{IZu@a^3PeIWd`&{w;TmRlnBLGI#1HUf}6J>74N(B|tcB4L;%^?!K!D8QWI9 z*&L1=R(^2rM3-3|SL)iwjvYJs*=L_!A&IMInS&EwiD{?lg>QobJqN(vQQeOGMd8ch zccrpi3BKONyJPZW3g;$55#ZUGQ?PxAF$1Q+j0m?Io{ zx5d^V(UxJmwLo|gpb$pJp2OjAF1XStv#`(rUM9QB(tT+f;~!i!-dqW=va!V{|M*rs zLM0x3;CQ}Ezu!Y7$RM4?AII}LPQ5i8J@J2qrMh*BD^3lFnVJ1CM}?fi2D-rjYYBhh zCoT%U&<5Q6POA6PH9{-pQHDl1_qI(0HSc3cr&CxP(;!HsGj&i%SKAOzTvlx;0fC~B zS3z)PhT8-z&)6XZsLWdU5v0&?n=cgzM~6wg`5+;z?!defn3iFmv6fXHBnzB4G!zWH zw||KNy#NJND{OJTlK@xJovR`f}Q^|EXdm_HHoz`z*qbo4>`-g zL6LZ&+OGIL)Uv>->f?;0hge0GAVn`ct`u;u@f#S@Z}QW?c6P+^<>7GEi*4a=C%7%? zZ$G}${N&@SILSPBLnqx9xK*Ei7_98m43>-|^E*x-riM2bR+_(Ic3%jiIDX~-?k$!t zJ`6m^I0G*TbqallojBJ>6XV|=F3ry8iwcg#31pJv-7{XqEfxOlAbKgjWeO;R=9!r2 zYi8)6)uXaB+k1XHeT@6gxW8}c*Zat#_I+T`5A0j{SwD=x@OV24L8G%fTSG>!Y*fZ7 zLmee?j&r9RSKf6aM4SjQ%IN6BOdx8FKQsns_ba&XsNjOj(R`o3wF? zbo2s-L0HyJ$Aq#?W76%QMbk2@#aBn5<%zFlIpN^Uw-YmQu#(0)50JOpYcBj+dM+-~ z+LaKr4+@#OhWF|*(G!^<(eSics*SC$JWU+E@LHKU~KA4HEpmv9y9);6+<&w8i-jiUplfdLPkY`3Lcb!iX$&beF| z{^Jh}_WnC?qGd#${R_N1Q-sDG3zdC@2X+W$O_LBlV2f%((_X}WicpBqBo%>4% z!NG$E5Ba%F2U2-@0Tj-m@y|eHa4Bf=c{v7ecVM0jOM#;eBC~;4u(!Zv+nXq7qs>NN z?fYKE2xMCVd+XL=Qp`NVP&A_459AVWjVu}D#MmTXRKd7iX9kDCpKs9vSC9Gt%H9ae zjAWb&;H?6CV{qZZa$H!Ep*v_n#|-a_T8*cpXMmR~ZMDDtIFleL65>H7Eh|!V zuw5x9xp1Wt$0;YSa&n=sf^U-R}9Z7b~NTNuEqIeJq5(F^kJqJww zzyGt=o&zu>2G}!F25-;2d#%-nr=Nbh`#E*5Uik!K#9dBi#l49c%roUUT*465IZ8ty z%>&GkC4ZAQ><#`74y;gCBjo2#J@5p3KbM&Ju1KU)fvS>{x@;LkSKH0Ct4^BaQOMSCJ&=}r?sT}A!2LcjMS*4FqCa|T-R|O`3Mj6TqU<7Fj zRM3HcD^7)EOS#lLjZdr)S{tNEqws)7kRN+0A6sTO6w>+Jdaf+t*=YJA| z`8#jEyZLFpGV)Nt*v&}YzZsqt8Srt!xzi^%pMCb!=J`{$hdym6*HQK6h0DS1?aj|P zLmwgxb#8a1_G-`eeu%<%Wn~mSlU-_`fAz)9b5Ebh=LR{X;kKXGE-r1pogD>Eg&(eU zSHYiit8D@Y&p}w?4xYC+e`P6PY+WK}BRlZl8k^y*bb`tz8Pdt*UAxtQNvP$JGUUp- z{XRgN#+gr7ArB6Wp3HeNYhLpv!vig#$cI7(7w?atbNkOZO`c|xQ`pBZ9xvZp>W zSTZ;h$c@P6#HvGHMmnc=^_|?S&um|d)Y$Y#8`YOvhOjnw(SfW1V_ID}r9~>Nai>6F zj?Ogz;8j3MEwE%GpWy`d`*tpXl5}(v$oaM*&qMt<^n*t^Rbc3=*;T>mK!+gTr`-p} zG(bg9eUZs|Ku3oX-pKRNLMHSbyfhPjD9U-@ECz9aYTJyhwDHD`)Rhgp^ylhMi%yip(1#uqh`BGlN)9yWkO};0lD}vnPebT& zGz08r7TqkQIMV=KB5^vN;Ix~?v?E8dTPb+b)fBy&U8c6_UrHJ0d@&AWi?Ck%khZkDqJQ<;$UA>NDDF<`_U@r5 zuyp`D6uBNr<_>;aVoZ49nRDk}z2+dNOh8^f#|L1ZCV3fm7zKmF0V0C?3bPcz@XL=9 z@6`ZLgal&_v#=t6!X(dcyrsT1KS~wjOX1zT*Y67hIU%lf9?fw%jcT%*1gaDCeL-!blq&fQQaUbG04s-F!lYlcigjn2vxHuYgSb4Ef{X zc1|JQ2}8S@PyJoWcU7-td)9up%4-MFGPn{j3}o#Zn6tvJ1819ft`IA2LZ4jg?M=P5 z^>#PFiZ^DRczvs1y|0D7;G#XcjI(9NjR7;O2ca*FmbMrJjM`B`+>c;}tG;3A!gqFS zz)o!X3obQsiDJ;Di=}PdZ4m*`-3q~R?Neo^eomuBfMhpCUoQx(yaLvAxI3iK zD4AMQDmXjfuwylFz=m5LUl~{W#Onfr_GuGkF7G3x2h=L$nz-q|z@q6 zPR7{(?bl!3{IyS=-#nY``o?a@n6a1nD!g;)+U5^`@K#pze~_*653@af%70X9o(-@5 zn_ql&^VQd0+B}jWb>Z86#b`$ zwe(^Ty)1C-s7uAI`$l#ZGP*jif+t;tYzB_RTOAMT>MqUTtW9mp0%P#h?!~ZPa3z<7 zaYmEfdCv4LuPIK!EAzSs8wLmbr|m|Q+;!zR=`EDMbzax-o%U!r{BC~G&V}R`(1!jX zR1PLW`{-Bi?J(7~Gx^#_(h`^ac`TlA_m2)U(9#bnzm{y4v4=-gVP2G&t#4mYQU0j~`;&eT-7ab({)nHH2 zuHuK^D>GD;a}DkWeq-dvW7B5w)nnBS_xz-FY-9}V*rgFY=eV>il*Me$YJR@Z6?*_} z1)rN{8~d`?T9ZCBey#j#gEU`RIu;$$c>wsRx<;oP-B7-MO7;%oGaph1x@7bscf!mC zpMlN*715wSIBoRNGFwuP!)@Y@m=6i`S|Pg|Ug}N$guV^|n1noMtOpF5ZNuZFz}5lq zn5b=le~7B&**SUo^h^5@0))+YEVGgxqjk1+KE&~GnBo z5k#084-!eI&*5^7x!<835Wma55TY^a6aP$~yNor6>^DA#% zAy+#Njzv)VB%zwS>tIpzIAuEbLZ=ihJzK9aQak}JwQ}L~fC&TUXx}lOTDO0nWD4m0 z^Us~!{HI_0{N}ai&SnJRc^Tgw^GWP|#&YJ-**FG%=Zl}(eE#KUH~;x0b|=bnJH`~A-7%N6 zRMB8cB%$N55gCu*TTmfD$=-G1nCtLZ>jg`BvLsTY_w*{`xM%l}8_7V@wjO*U_ca8A zo3US=p@~bT6sW9TaaADc>NrA3=eh85%dmq(E6IenbD^))Mb74_(VW-<4gePh>bi4* z?kD*`w@R50+POgQP_B9vxcN7S34+DEn0ZY9Qy<;u$nt6>jjRSE{WSP?;Rx~bpRO=8 z9i0W9vj{YBxEO3t?<D_hOsvv->cc;96$8GZuZpKOVX!9VrsDp0D z9+-tGeWBgKC!y`i^NcSG8nYOs3|vhLZBfR^unQ9n12(_tjH&OK;jv;qG8=5CVIVa4ADi~K)enq{&{U-@D{ zl+n5h7%?1YhXJEFj5&3nX?JiWUzMc9p)01G%379ygG`w&c=>UH_z}QxQdT(v0qwjm zSVztxAk@v996)WKvE^13K*|e7w_Ju+fz-N{tB@n;LI=f^g^T*eJ4`Vmkfl+|3b6gC zUm4l@uTF+k*h3w`Z5w5tzT-GB9t28__O%}phaMeoNs|L!g87n^;)q5ZOKn@yi~)|6 z)nOyQcJ@HQ6@=H+KjKQj6l>#sh0X7gYD&Q~|Dri~%Zx!XY3hk=h|`~JFlTzs0GUE|B$INfrQ=i{ z`cW>$)f}Xo!AqJ|>gsCQvv?xjvDqGtR3r^w=c4-Ed zxhs7PO1RHrLfeRnY3S%7W02~z_FIrE&TCWQl{n4>&WFKGpQ1_XSa7Q|A-E`3xY12l zyt|UW;B5gb8Kysy+4H(EU45!GjIK4~2F&(!@o4h;XHaW>6O{;I#W}a7RXbIJc5BRx zASdC zq$isb`of{f+H|l}`Pi}NwEDg<18gGizHf|AsNa?U?)$rT7AYS zuV(9~XN(LZhJSIvlk&s3=AHEsHg+A9CZ$Up0AY^%c?av=%lkFLl`T6@y<}baaq13R z>nj63Cv_`$IV0hER{ovPs$M%4>Rr|)dG1aP(l?=ou4}qf3?MZ|Hd-gmSkix0VnWxI(HBQ~^>g4Eixuc8!?Bw@*n-` z=Qht~r_ieCJA|?hx9}RTADO%6!#ZEebuxC&rp({{>gSeuaQI~Mm6reHZ@j)acRE}C ziv&a{e8YRyxbm5x({WydCh2D)6aM30`$Fgc;bX=jjBa1poQ)HiqAQ>uJm6>k!8H@I zX`RzabneEP(CzR2`Y&&GwP_okXbN1;ryc98GBz@oyn5EJ`oztP|Bk%jd>w-yU$1jG zV?%xhU1bRAVLXYm$Y^-Q**}rt@5u+AXNTpQw2W*rq|q%r1CIQvkyE*a=Qn%mQm1l} zH|izZGmgm!IFM0vg%$4_@yaI6aIapJn;KWiSEoU)ExdB?$@0Q^b_rTna*ju zCbUmKq6s~X4Cm~t^JI~?-MJ8e)u94wa6)Uk&*(euTc&-J2RMb+g`fL6SHLTc^sD|$ z+fp7p^cP0w5I4MHa>yZQ+gnelqX(JI$YgPk)!MpXg2+y`J7~ zkuN}(3UY(4i3%DmM?Z#l_2<;Nw#r!Tj?^U9ffL$U)G%YTp&;vVNkpF3MICgRpZ*@* zkN%W;?(rtCHc!SMP`@Zlr~RT_+w_N`z?P^FMXp21{6Lz=urLuakB+`{GZV(7VK8;; zlhyM~&Q&>+=5%w1f?#;I3ONYQM67|F2|CP{u*pOZ6Hwkn#|*p55>BuiW>Uk+bk58^ znbZG!pj^^0G777*H6)!406<}kGRs+j`Pw1i01^gMv*eF5Wpb1|OeauE}Fp0q-#4&Qt?p<@9L36R6aCyRfBGjG{Y!G{nlWVcTg8#H7T84e7%qkrfpZ~jq2z%NZgdws+?(bU3BJ)yBaa-wlz1rvQ^ z%m-XT`XG3J+ymPRYWwG}GzEV3wdXgl=G}E2PpQp7>6qvzk}^(=#SF8IAtQsLL?O#K z%lMJ@`YX?EzVOlqEslKrW`Fh77qTlMxTW6pb*`2F28GP+;2Iu~sdNyEZ=TJW&t<#) zS3Y&F3G7+3%9ah z=44@|d=1UyYmh&%yBm=M!FUZ!^8_qEkh&E*_vA(2kB3|G!O7fbL{=M0@N@-2L=1g;pEIGf)dG!l{i@E! zhnDC@Z^@tBn;YuXmSsW4Fv>IiZ*`gse)`B6Q_;VQXXt?-!LgrP4m@~SOj9lh1~&YY zPLEo~Y6`RuEbIxv;rLy~K4k9>_dKT$?s>jDyJ4j{MkOX~BAURX_+&C$hR+1VV&;TWj5ZTF#@Z-z4DVq{tHF&i_{TuU zb4*l5FXMH#2Mtp_mY4rAWe4X~Ovs$T2{8C@2Eb()Zy9OsHKJRq7)wdFI_o{~Zp8`{ zuYnw#olW3S_b_MpwNPmgfsr`hIAvsViiBi6 ztFG;+pe+@|DL>6i`;u4R+MeZJzQQ8tQozBFoFf&gV5aP>4lqn9 z1h(nlh@n6C?RV`7%rfWTjwutMl%CZYR0QE0#!qOBcN`r?NT^f$;ewF*Cs-ukC>eUC zeXuCqw&TN}vQh;l{hFH@pDC|?_`vxl1KDdK3!a<+=qURrCHgx;I>H9d-BFOdqo_o& zLXW_MYgZ%S6hW076+~!YT&&8~#7B6Ami=--#+PPI+0+HEMc^u%n6%Zu0A6sa&`dc8 z6N2sE8VE)0r)a24-f=_>Z11ji=gL}Odwt?4V6J@Wm2;i{_*FwL6jp<+bJ3j42+1(Y zxQaTNxjd^#o<-f>d^zU<7@LO~oT~rJpFN*>461o%enG<2rVN@3Mm7F|SD5E^1_a+s zfz>JcE3ZD^wzS|O{`t}?=SzozOug`{LJ1?t&e6z6-NGy4l>4~ zxY22pF~$}k)uWU*2FX2T)$3}!uJDZ8b^MYeQs`{tO3Q(mGOIzX2KnhME1cGwf*tQk zXMB|CG^ zFz_wTRBufsm6N4y@X3FCP+Z%m8zGT+aorJ=GITifqz7rY6>WV*-@^wOuXI?iL6-i> zT_ZZJ>I%O`NB-*z_0-tKNYoP@Nf(yiE#+49j3ssUpX|Yf4u#enK&3e#3DYl$OIU_P zacRUt_g;*nlr0a-wQUN0?5I^xzmMZ-#x7k>Rq)m}IRdg~Ss#c%xM6I+ia1@YWW3uEO2-6Z8JR56bhP`1eED#(W?Jwgmh@vK&g}FQo12 z4O_RuthL4%JasHyJ`7;Cv}bB&68qendB_M7dY^bRZ5YeKFc~vp7-1Wk>`4yest1{1 z%FM(VSK&h7VXT;-(?S{U6-vs6ISmdDmYa9i)rCnAoX3OXF!3}sjKKF*=l%Hzq%a`) zs_cr+4-TFQ%kCTqF4{ssV2T*>5H0Nu#|X%7U)O}aY<=>KvoI>Xw?Ch8gHN0pacD5+ zscE!nJcdsi+@a25RMk4D;O+iitex(0Kl08|JDc5>C3fvMx0R|A`K#N#RewNt(_ z^B@$|dXFZ`F*ga1$-_~QZCR;Tnydbn*X|JgDyUzibTlX6(6Pd$_>GfApS5rSAb3JR z0;7F%HUMK`c3cp)?T?fLOEnchDQ3)etccGu!FeNx?ZE{e;@lU9G}4(29XH9ZKCs}T z-c>O(ZW7twN+Zv^*)ecDuY1&?k!MBUb6`{Yl7O0b(=vuL7S>l4z+U^t*z}#-(k}Es zUvPc0Qs3|9wHaGats;~lDGLps%>-i(It;xtg5e5}t5I6lgEdoV8=gQ4Jbhw!b3TXD z;h_dy`9KB+2VVXfB5^3q7{=r9i*SwbAer+&apu6eQx0zmj6)2kbF%)6In$*EQ0nd6 zo@!G|=1A`S;e2E;P}m_%d6x!P^YHmIC&Ld97dNX`&*l|_8uN&hipM}O_bIWIOgM5( zhXI9Z&@GS6y}2tqb80W23&az);Yp={9c5P{Z}C}UvfrG~bp|kAjlV}H$^bE5g<}~% z#)>h-*)4I#*UUHk*?g2n>!|ZO`DZ8^cZ{!;$@nwY>Reu6lTqZCL*WGVka;z%B5wrw zS3~qbH%MKDn{-$C>j0lx7agt5NP1B(TPuZz!zD zCi%jR03QP}aJ8v$&~RXqUmthp0$x^~iF^!3@S8Jp;5_wcyWNID`?jd0eW7Ql69p|3^?q7^;4`N{jFICv!{`_<1O=nRWX2>IVD2e%m?t5|0LFskXEaJP zF-?5TtzObF0CCD8Vi1G7CFjMkO>cUGqvdto4$M9Mcm82NeU2)qJF0_F)H zC+w=X%zR;(oCbtqg=q@Zi#Z%4a2c{A49tGf@DR{|5;zQTaYV=ze5~v;biJeReJRE~ zp-tG^?J!VuEO3fzV6p3z<&by7Oyt)ZYX3DS(g>vpUJ z2D&El9{j~jB@LZ9Ths@>AfNHTAQuB3Z}yarVi3nd#~R#| zLA>#<{6Ls-bR!NfG&7efe1n?;p6ys^-uw}J;GD>PcPnVWIo4ea1U1uc@@k{Iv}np@ z3|_sl^i@)};mM@H$?RzIiBLOS3{~S!m=(CWat4@_#=DbWX2f~U7^Sd>0dW+XM~4~a z^QCt_SymonKBto6x)}a=MR{!!I8Mt4at|N*IL-F#)H=k#0pM4@qurT*_<%XrU1jQu z;^@wbRFF6t@uh z&*1sZd|gU*8jp-qPCCc8>nc-wkCwruvbH9osW@}7j!C7*lcE#Aq$;zJ*w zvQtl>9Rzrx0lL7MqowzWM|<~;lc)F(VDRYpt4*Nv#L+{iuM00Y$tRpYfBx0p^%@MO7M;AG5&4+ z58a&d5C1CYX`*(bp`ufMoF}_61ia#arYJ&)Ak8>rmfabNZ%6_>v{^ZDC?s9YPdf`g z8MVdv2>M27-LA=Bh7XrKmx(8@;C_CCqc{psnRoDZcuWoY!Wd`6>NIGZl9#cF^Mo zFQL;$$;nL`JqE+Hmy%_?z^4w9;KUKMDo$Xe+SWZd-=Q1)h(AK|K)@xxS8C*~Lb9~k zoZ<|a`C}f%`4+(Q?%OD7eJ3^LFGOkGh9{o_b+A%aJ60)Ycg9+IO(N(+RoZ4acPu+& zW9C!N;>ft_xIIL2QGCIzq0zxV{MIf0A(+=adB#Ju^97rE9^DLTsWbS`9jx#RdC0BEy}i#x8-jn$?FGj=S&}yG4wg(y){*%o8Zcb1*>@EP8@i0NTZ491NF?m-rhr3 z4;%!@J!5LhHzMT)WtW>=CVkM5GSBpk+w>z}?(n={@lbglXv zT2(h+_$a**9np_Y)7AXcpGCz z^x3oL^4|H5$z;-E0=_$+IalUwLYedwvQG5OIfhRUDG3u`w0IxtNEn6qUgN1gneEX& zzehe1Ec;RF7+_tM3O5@@4epH?&wEw6lr_0A5))f}J{{;DgSP_H?V^|tCYJn^V7PNy zs7!r~LEDTRY>4BD1G1~GF*22v6gm`l494yS@Znbmopy~97-K7ptjsajWBE+q#Lu?B zP$dk=Y5y^tIWO|F75mCn2lu57eyP4Fv{?gR%x>_s`j39su3pW%|5u~@5*jeD;y#|? zGTGq8*sUkGOg93&j*GODS2RVmNoCrnLeI8C^}xd<0V_^_q8x3R;iK;#aN=rZGcbxgobAQ*d~WAHQ&v`rtZ?{Tjyk^`IKhm6zD z-mwed)ug*ZLmy?m|pn*$l%7=HhoV{^wp4_%M#9?abkEF!yP8M8aLHkGG4}bYK}!7Wk+37XHmlr8S*;MG3vo~ zh?zh}DZ9>>%0~6Kk{FM58mOlc&2N0B>cTOHq!LkNO!c))HjZXR0=crnon3w+YgncN z{KM;pct&L(`6oxx#%Yl}(vp*YHu4W1V#s1U7s#gB!BY%_NQz|cBvPZ~F3^$xXyTtj zHLN|MOLRTKy#n}`Tpw!Z0zQ(qjp`AHdN5Uob9ulThr9YCZx@F>x~vY3^3>E-eG{@p zJ#<3{giV}iI<3b#7E+nr4q&!Li5K*@+o%+&cWG0e@y0q{mOLB_cG~J+aIc3ise?XE zM*;`sTVDu@E}nA}2zNrC?uwh+#5TXnj6wQqakKLvK=l#&S4V0KrX7h${cs2kF-VQc zZ|D#lsvt#o4jr{Y`I~_^&Vs>{;}ou5C6H%@VVuFC2Ue#XJA#hMDbSq~@W~kTuVWmZ z(RX0RS&OEl8;aAP779_ocnQ3Y@6G1SnKQ36!<|2dhOl1ykbduT#_&DyO}sDf61Tm4 z1PXi*aX$i>ZIO>ifxGG7`}5B|_o=X*PH^wNn|z%{nM48sg;D$E!Tf6+7W7_Ddu9AE zCJDu(jIm5Yaf~ys6*y7ZM13l&^A5HHyW2i-f;@C3b79W6YT$w|7e>Me_AVad#{qPs zcrhX~{l`!X5X`Bk`v;`@Fi;T2*c%)%ztwqQ>RYXUs;?6yXYeH?<*}lU`MP$6t%7jG z-mNGAj5@IUCvXT_pRlVK41)=%YqL9iuE%95K*GVBqS6U%-T6hLHzmmF|E--ZJ~ZC~hgQ zK0+Xc+WvaJLOO=*iLo^|v{Q;M^E#uI^pEM57?s<@SL|~Qj1@#+{L6c=YNbKj@I+GJQViq2c3s#->Y|BSPY(e+gjT9JW_$Jg2q$T)_6@n7QUVx9{(WvdUQrgD?O&3}`*&!knK#U(}?y8se=8I01z!MVnL z+Lu~oPFfNXD&eqqE?|tSV;?@MDjicjx^M(&AHK8s$!p-<0J=Zp&!8=a=l zDlY6F$Gxm9_y3Hz>62p%L8&rAycF*}mqPxXzWN=e8w`pH!1DIFF-j za07SJm${HUGo)rNt*_){_ri-H2=||T7MddAHatcOYzh7ti2V=)dnYUBpTdN#EK-b| zd|rb-6M8(#nK)UcF7wPJ+s*wLTGxAGyUD*AthXcV$`~>`M;Xne%s&&|=WiKV0TF{Q z2eZX9cq|643C{D$u(1k1p2T4szCX%KtI%$~J!-;(F*^ker2xYT0t7QY5`|9i*XQK~ z1}A2{pOrI)YnfkiT#JAwI2ro3sCf+m9Q>w-o#eis9l?!~AAIj(6UaV~;#;ZG8EnV0 zUEF68Q`6=`R=@}ZdL_|3tj}L?ussR4Vvhd@m8IgKpGPPjO@HtJ4-X%~cI?>D#F*0t^?5C!#&w9+L+=vT zReiWJkkdciGMD}rj8E-$IxSpyd#`5odz74MFZ}&@);W8O)6!o4cR>JUyFesbaJ=3oEWUmPY%2e7{VqqjHT{_)$LFEiKCY349g-~>nac$nv+m`K+f zx&GdFejIpt*oG&X0(fctX9mQ==Vu&e%w)W-=f709$ygCbMpQ0F78o$(wsB^j(HmBy zk;6d#*ka9t|C31>eVGec0BC)gn<;N@&@+^;d;n{aJvxB%K>UGj)QKboG`a!#n>L6y zh!fnB!e9!noZdaOZZZ}wedJC%I1JLR1?NIay@P9>0vq@=rEPM~i20+yy};NA{d z{a>f6_~fO-RCkNcy1s5Tc)~HXS80A7VnK+`Jh)B2a0b-q4SvZR!s16c+i=N)x8Cz)!XORUKdW*3fIm}=6XzBbuZb711Y zae%oHy_^Fe>ud)vo;CiyKN`FZkAVVPLLV-X-$&0(s>Wp!+@H2Suh(Mo)QJqa{+15y)UC5W0F2M#mf818%JH7t+FhrDbOF2AZqKVi*&gWo}_GotT~B znZ1!OM`I9HIHo>~xCUE{%^F-4o(#vN1t;$!^-#9pi>dom8spGGYglsYS+HT?)=-37 zs$%F90%1L6+9PyxkXrI|fd; zt}A9}1tm~j<-y|6!@)Her0rD=>dq|z2%qi-P%e#8o}jTJuPg!8*Ej;tA&mf(Kx@Bf z?<86@!FT&=MaF}Cln%5*lnO4Ds4~bZ=aYdLG^Y{=0gVUN=y^Rm2K0eOxt2~DYaA*! z;^Y{oMPSeW(3CLBI1KDM4}x?4&v?v@J^@2;t!F)P@B|Co3e=<-+kK}O@ZerymFoz& zRJqg%8fS@rgo?He9*lSj)5s$9lHN9^jFIT?j>G%RlhEI*E}?GQYNp&u8~YkSXesc- zt53-qCuyf~1wZvmD2==Vp-ZEBdVAL?cPZotM&u?A4viT?9_+IAijJv~b-8F^fOs zrbbZaBR*)1;{|YUvK$%ec+3Cz2jAXYTFxJl?J$G6{6GKxw>K98KeY!Zb)s3PSF4(x zIad7as<~4;T>tlPesA;LpJW%oHazhZxDr`hXLJW^#z&04zKflf(~+tt(I*Tdc``oa zsjNVqC9d)e}hLXCa*=@G2@GH9-+UixV zT3G7vkB(57@X53CtxwwWyz&pP?g@pF^5l`ln5KKK;mpWR$RF)yNa8n~4CGu;t{&1A z_oO|}oeNz(4gx`INT0t$$Fwz(WToj$r=eS#|MUT`cGLkvPA8ontZn#`XLP9pCh66& z0);*V=ID;IP-ejZZ9{^}Z}jSJM-hS4*ZTsniv&TWdCL#Qr8gE{qwex<`d?_+_x2Nq z8M)9OfutuMghaI84x>tS3K&zgd z_+#c`VJ%~|d1I#ln2@$UJ9qc|y^DBv6LRw0_bK(e^WFpGjW^zSVA?$yw+{yt*b?l3 z6p!yFljk0~-@O&)$nBjMP4;q^Oft7C)9*n^L37>8WZJtg@%3M0$Pd0{2S zXY0UW{LM~+5h~u*x7Dqm<4aK|>I?D`7$!Cb_>`4mOewHybfzxxJ$X0z#nl5D=D8ZS zyo)`AFSszAv6=qMGhtZcE%jk0m>|a0?d9qTJOSU3UZWcVxo>OvQ%+OBDYBQ(H+J94?H@CzKci`&g-vy2b_^aEI47nv|PcguU=Gu&+hR)^of z9bwZJrY@d+0X}OejnWB)pXiWd+OM4%vm8(LGOHfklr1fDts^2xt)B>jn*#~83;xEH z8~+G5VU!DZqhKUk`da!Z0h|iyso;}e@U255V_DtJv(PP7v>f>QoRU~YLtmI{3taV} z4Mk#TW30p}=BpIRdo6i=c5)j&0Tjp>{6GHW`w&OV3(F_@fl^SExD_yy-6Bta7~VOgd*x8dd!bZ>XcN+8tVq-b=SD zP11HWzV|h#I1HjM4SsM74iVR?N8akrDJO=5r(-7gaYE503R_F`slp51ZiUea64}0L zMC0cVx-4;UkDdXKR1}EQ*YNGRPud;+G`gXQ2i)yJ7$+QcDyyO9TX`C+0;gF@7&8XJ#URGZDk5#>lpx9_&FuV z8dlpUh*&Q{Nd|a8w$V7 zIv(CItFDCG?GQ#)A=Wlvnkg5U7{8TXC)?YRpshEwKrHPApAj=DS^=!= zi5$AOr%dQ`{ra_T^C#SUK0@-r4Nc0>Gg}Dm)tSPBi*a@ZJh*FT+ft^Q-dGsAvhpNx zm~&{R4t;C&Si8VUqW_!g&SuCnc!bo=8w-uo7TfccQP%iyup;WNL7jjY7NyzJKhj4S zB%aVn&`0VHHmz6NDr^(ydu5afEApPX02i>+ z{QM%Gl23n&Z_5BjlSF_yZJ(VJ6ae%F&}s{KoKC(HVWqwO61ebIR_NCxlAo~7rTtK} zLYqD(&Q&|Wa#-NZnZc(@N2$x%Gd}{1aIB(tbov&Iwj=TAD0p=RE*wbv3qDQQ{&}J) z;9QRXCJN|9i%#nl!lud=996ppRw_cAN*wVPrvzt&Aa(D=_3sp z_~gdd|MZ8OpT^k!J750H=5sGTJ98rQ&G?0rv28BeiSYksTmJw0hu_`&*-tNIo(%7K zlsNv`TkmZC>;Lor-u#DO{_N%}`AqbQ;Ian%%WW77en)rr)%M*$^ThGJcJn&5l>sjCb*L52eGu z@%cWG4T*H0|K>!joI2?P;b8o+G4c9YXdy2HB>Ahy(0w9|$q2GGsr{D{g-ez&+BhXR z)zx9UHY2NgQL^naU2wG za1W@`4mG3F6c=N+POUtmOZB(lY<#0fUk@;Tf^S3Wkw-JMoTI}0x;xI|;=EF$sXKd& zee*;1{6-n|G|zLFzmZK*=a0@rKZxDl93};}4uFq9&0nz9C(tCuG%&OSV`aG)H=o|f6g80(Cco!Q@AwuN*0Vw!rWd#)^kOV| zpACZuQ|YOm0OSXzJiOlW_>7xv+Tk@7SeWN9`%xl269Vuh5XRUX#-Kios4LgOt->+S z1i|Xz#XXdhw?-E$J>=nu40;vx6*MUm1cQUE{)Fr>DlqFTN&J;43-3fx#I!K$_}Fug ziNh@g)Rl%B<47cmo#}hyAk0pN!pohLh6Lksbw$gH?r+flo0pH>n zBnSz48PxLlX&YEcD#MhQaX)oT+oh}5Rr$z-((=9J1B=6hx0>I6}&RD(w(zmFS|?Z#z0R8T!LZFIc>PFycko7oBrSF z`~S8kZo?;s0#=58?>j%;{K-!)Y+iryna!uqp4mMA^vTWfj3wjZ&5KtzKYZsxwyj^> zeDCcGA&kNK_`;vQ`OfBhKfAbj{e@>YpMBw(&5Ob9w1ZE>GcV?p_3yv=)6MrTUf+D{ zM?Z^G?D3A>cXQUjKmPh3ZN7f~%;t+PJ-2!J?CH(9Q^&E5e1YIrofqGI>%!)bfAm&x zUB9mJchJ-!oM*kILU8_sCcG>?j5h8DO5x3g#FgoY09HzK=&oe;`xH z!VWGnL1oEQqh99b$c5s;7*1Ziy}Jk~K*8FmiBv9x`g_oE- z(&6A$xz}3YGVP}qz>zbY4hLCf25Jz>vvSHaD#wveev*%ID!{=u#0E&{lUX?EH9A($ z3sQgZVyuQjnx?nLsh?u*IV7|l9r_9bnBXTcQY~)XfKRUN=j82cA>m)ff}jrCnfwz1 zxoAz9^)R{`w^56qhb)4-Px;h& z&e<{nR^!NU_(egU4BGdF2gScH<2K^`6xccd-cOUmi1~#BIujz(#cJiTdz=K}$EW%} zP^Jq5?MNm+W2!5@W>88IMu}>edqx~jCJBU5#YhR$ukn|eh{=^vPmz|#3_6`*QMjoq zx2yNB9>h%ky?7;S6h=|RJRLpfHGyt(&wcBy;7H*BV8k|mCcir2K7uFAAb4RUm_eC% znNi9?Aaz-}g%`Yqp5~wD8&Lq!V$MCdS)4c@A`xrQ9-MZX!~Zz@FT8uHtm3_Z=~k`a zg&D5Qmod)kZ1vweOV{_WZLZ~K<^4(=0lb?S;>~QI?O}rT_Kq<0&w!gQ)u8PY~+ESrv(x z3QpJaDNE_T)4Y>YEje#=f;RB-R}s*bw09g-p|jI36E%nE7{4Pt*Kv$a6fFaYs8lCG zUmXnH5<13%ld|&9csAZ=uBqTs#!?luDYE_h388>@Brjgt-~9fiA8&r|$d7wqXAe@% zIAp{ajuV!_v_3wjtDOq}&kujvAKo{gg<>Sk>qZCYJtlnCYSioZ{hwUgeD9|h%j+C= zTAr85w;%kr;gd@NvhGr3a}AM<+w$ZL7_!c|VaV2a%(SX)OTN%2#7F-2@HVn3M6N2I+JJr>lbTL{*twueA)(SuMCI1r`o5n!uk~e|gmu&gNnIadwW5_1G zI{|ag7=|OcjHa%~mhl58?W|r3R?>mdU-EfXR-YLYoJvDWv|8(hQ+p&a2@>Gh+XbLV zc>qR*BuW>KdUX{pp+oh(^c{U3zI4FC$^EVDP=K4Zq4CraSQ-$x;?lbOGOk-bu#Npc{x!&{k`r6@F?u0Uw5(t_jjzav7KNj=$BHaDhy4@jEeETR-L+ zrcmYte12sLIRg=&8s>Rxb3E^#U&;1-UUI>g2E2>piva|TQ&7FDN;$QaAM^6n0|9KF zcWv5>(Ww@~#y94Vg@MX&=~A}wXEn=VExS+Ue1ixWdBGFCb4ek?DSv`^&dCTa(=I|OEuLKjRbKLx@`PFc zm@C0cd%JpJT!ki9FD4$${7-?^xpV7C1e*N83x0OSPy~Pk(|E7q6gt?3tpCA+hkn$~ zi`m7n4WCR3m=bGn4#fV!LdP2W;jgANUiIm7XEvXH_RQu~9I@szgZO7T;PkuMZSdxW z>QWyb=#rBm@^M%CGt@V)JbQZcx#!Pr&YalG>r|OvnXs3`!~Z;===;Ij7wcU3u*h+s z2q|ek4>TL_~Pz zEMFbQRG`Z3AZIlfY>1LY8cIfbR)3w%uJjUlA`5F!w}Gujk-*bXGAMUsR&KQ^8fBg% zyQBR)ik%BLg9n`<3f1Pl$otZ>^jdZURF*FB@UIpYT?x*b3Ge(D&42JdWPlmK#&qW+Srg^YuF2<~d+z0A|Fit2^K!G?Yw$Ka zMhfV+ZTNT;xO)))Tu7Oi_vgL)dFRZTGkfup#R;2L$S^Mx%mmttfot+$I5osGiA+H6 z)i^~p%h{bt^}vP+kP&_VIzkX5y?!&F*o&9D0ue(9mdv>uiQnBlUPBh*-l?{_$q>f~ z4}_jc9Ru;0ZFbiJXK@%XcyB~#mhmt7%P4XWPizB2v-%2_m0!Zwmk2NmOwaq>SlZE> z<9+2h?5hSvGB|2Wm|3^0tAo=aR|4Pn-O-BI8e_f|h4HOyvArF*6{v9QJ6#xNu*na> z(cyRvbgOdLHy18l?h1BsP9C)Ot`p{L`}vK0-2lA_0AF3OvP}^0o!HIpf$X%1^CI1p zALDrN>DIKsf*)M3<@ExN3Us9$=-2V!CQtq)Yx_*Rz9&~8yc)9=l!>cyfHq*qOAy%= zkh6M+#^MQzGF^&I=rTewuXu#H_f>(kqfVP-=9I|C9@_^TB*sv8o+PcxMyi5?eysB; z_}L0i;H(1783k4{y2^pb{hu=J7h^5I;I6$@YUB|YxE1z!rub7-j1hHp$4Bt>9=)$8 zfKkOQD+_2Ze5fe-rCgrC@aN@EKLNVB~)-_o&|+O zCcl4a_YQ(5u46y*Z>2L@X|J&*-{nVia(a>5{`o{uz_F!Y{lZI|-}?M3o0m?VEH9~( z(U8nPGw@=@*|%~~-#`D>_w#x|c0N6>ka7N6jQ+p%#aB086AQ>)(AQ?c^kOP&` z$zx%}&NFc2?+$_7D?_*F`_=A*$eC=4^`6Y@vkOY8o)pTub>j&4C^(T77QvKvtCUquybceJ> z3FFAZTbh!GfsvbJrMIMlgpP>-t8<6bC2{(IE=gCUPjwN6;?)Hc<8t7mmmLQOuW&?f zadww+lS_5O8S7&nYL;i+YKKMJmuI>y+`C8=WHqPyZg8b<^;_e)v>l0~SA+BP(@%do zkiMSZG$uC>3dyf}&D(}YKmo+vhQ~mGco4Aw;U1dX0yEK@&DnG3&c^|;LoisSq~PHQ zH!_1vqTI&d@J^L^vlyqzXXarJea|N;KF4R?+rDm<+8i?RcC#Yfe3`i3>)(s<4=_W# zhD?~t2t**43Inezo)%`L_JcyIPN6ME8eLm^v}V6S^W=9(Ypas)~0 zO9lJz_b+AqE8T*tK83@;NDV2c%+N1%3DX=Lv~}>6sy+R@N~#XXGGJdT=%@SgjFy~4 z{Za#H>AN~_g3Imn6MD~c;%hvo-UwrYrUEnc^clP~uk}|@1*W>tD#Y&cp!Nmcj zo*Lbug)Hz86hS{iN_h^6IYVN$_E$j(@Q(2mtT#T-aM{ZW=U&FCa+40%WD2bao{pR3 z69+$eh~aUZ+=Vl`Ei|lvFCvRmBjt=6eLbHzoIcif1edFRfR8cWv5~m7eY4|Z@T%dT zb{m7!*t9p#7q4x10CX^H|9m70Sn2Dg_S0dHQd{LnZ(6`uLm zKlA+NcmMX+Hh=xK7d}#5AEnT*|I*8w-~C&EZS&Pny|6jsz{AWb3m>Lnb#2RRDS!eOa`pq6RcCQ{dxn3!u)(pr8DjjwU&-!H%e^+KufL>& zDXwrs13F2jZ_Ct~(t0Mp=h?BT?#Q~x|7teD4Sb6^^3(ws%vaq-8SMdE8yWo!R&Aq) zg!=gnEg=kMF-RRvYv0g|CasQzHCJ>BMn0#5t>tx$2F5J>&^;`e+36WWxB7DG4BhUW z50SQmmAYKvfuACH$TA`M?K-GqCAm6&N{ew)6y4^XHiz2nkM_?BqWEf;x|XZ7-ivLO z#ap`ms19N*3xRKp(v{bD{gv$)OMB6keghIzlC_}ed~LUSIvy5Ur+svr8S7bSjP5q@ zqeG#3s$J!a>N|&ucBqtUs#hl;T=h#|2hP2^Rn=Vpp?CE&k>dUG$0x;wlP6EU(wjSf zG(cFdeFz?XPTTK^Z{mGcaAt~@dgndX5+w32a0(aBC_fMZW`%2#rTLg$%V}jeJ zjuTkPwCZmHGw5#SzEdxgOFYB*Nb-k`v>c|Ye5PfYFhBi#>i2(m1E4+_Av%N1Kgy>2+GiTSu^po7+!{SJFxriQ5*v@^042EV*-6LMju3`2Arff8IW=d@3L4A9nl z%oHQNn)epbR9`GUh`#brq6Zekk)f<@63~5gB_Js%IC}7)H-96BLj*}^U zrBA15#!LEBTNw2nyTR{T579F|LY3fZ%otC*gpcRcBQ3>>vvB}tJ1(?SJKB=Ka)mb> z^sQYB-4)YuoI0!23|^~4r8G;q*=;g)dF`VL0vbj+yBQqOrLS6~K3DB6t@3P6nHSn> z-mSPozK)S#*0~V8>|*fU>TTFk;G zWR&64c}?o?|Bcr-zw)W`AEnBND)Z}~dwKKs|DD%2$20fggYUzom<3mHa1NqoP2+#< zvoFRG@Wss67)RT%rNBp^z{Q>cK=v+UKjVwhRz9EMwmR&MJqAdP{M17pcCI9jLFb-M zAU(w*N5kDKoHK?K;fKG=XQ4x$8i9$ZVUu`utTIzRd0L>OebUK4um!T3JT+J3BP&`_ zQ#mIh%tW~+m_yQQOee4OmPuM>*CI#9Ng|AJDa<6^GAv9>a zBD1ZALusHldFq2x{ske4E!#y3am9;aN}TFP{xG)oQp-j?pSL>f}ApKjN7>}UYndU^SZ_8n_%SK z{uf;{297ub&HWkz_nzl9etUg16xb5`(a`k(>O&0Zof9Wcyrv+g+-+)^n3y|ez^Gy< zjRBsU{I@bm06G!=z(bh)W7KDyatS#1<4VbIf&=&7* z5lY^Ai7}TL|EmyUc&?$PlMWty%D6C<W zbj9`}j27d9GrFt-nCEg1>+`vkTUq_TScc;em7vkKFs_49nG;5@{0S@NNoVvri@^Kt zJ}=bI%C)Z06HcKir-*b6OdL88l*7W&DhxI8w$WGNfMam&CuDM!jwX!cGUU*NGozm7 zplI7wg&;yoTZAYY+v!1}(Z_^F4gS!l?{F1%jMm^womPiKc$Qu)9rOWw;ed6w9mx+5 zpGPciDTBsv7BF>V3@&g0vmW9}tWYq9z}062js79v(LtSzV+J{as(%Qcb_W_R!XY;! zV3f^0SJ%T&nOaG2<`^ z-rAJ38Z*JO{hI=HG8jL=CYIn6;3`xIMmQP~Dv;lPgzKTrH7h>bu%*C9pum6d<=1*r ze;u)zn|KS}>1Q}+h|bu{4TVlHAMnk;_p7gO&YjH8u16GJId^*VcfaSVQCJKV)$el7AcsA8zMD zopZ(W!|z;RXbbRbdl#BCGJnRPhIDusq(bi-(P`Z3C-IfZxmMR%;5AM8#E;&9)?=iC zshpp5uc4MO%|B^$Pr5A{js`SFzB1s1s=3BVahraZg|S7p&u6Sd!{qQ~2C{vO=Hyoh z%~fsjLdOJtG~8U1mPRG1!L|46O|HrVt@r`ss^7ulgUjnoJi||9)QHwP1Jmj=yB$n`qZ#yj!8RQtugq<-K&`dDDBpoR%J@%E)(O zfAePI5qg~3ltzXRCbqtVgYh`rPF^#9wzZ8j0JG?-bug-jv0N(*C*{iuQf5^;!KqON zKTLwhFm*G6FgiHFSv^?e%J9m_FrdWSN}mihzzD12mKD^#Lmx2mz{6+QPUes)1jA&y zE5I&<7>XD2eQT@#{VqHyn|k{yLU3>xU>g}`dVF`UI{+xw`#C|Lp?b|qcZ3Clb7hRM zbq^P~SE&sX6Q{k}g6WBuZfg@q%fzK_I456~hvfC25n0-#-Ri0kD`V8YDbv31NJzVr zFJKxt9nh>>@APHbQpH=#0=Mt71=c!tT-WV*@P%7{aWJ5#;3^NeVj7$F5*+o|2d;53 z+qQ=BQ#gpD(jhpvZ|eLQC4g{G!*qR>6L|SK=n+m5Diq`^U0laATmhVPFpVYnT+28p zz5p&ieFIjiepRp%nW&EC&Yviz*WwJ=hAjmimI9xC=FH~TJ{SGSIL5j%9`Td%%#LyiMxi8* zfn`3HAMNy&fZuxE8B6jVZUFJOKL2VzYY4LWfft%N%m3ok`^*`5`ze>lM8CKB%`d#X zIsf#db5Ou*8=g!G_=*O-#h4QqKITCFcl^zD8FPul?>WhZ+|GQd>`6qd=<@Ht!n=E* zZRXoR9>brUAP1CPgZjE_Eg7kG<^O>>iz@}2tYpQ&WgrJkjaY&xqHSX({k*Kn)^Q_gXwPm|X>?z2XPoyB2=Hr8QE@^e%zAo1Cxusyd(K8+S2qeR82RAZy^0><+aJNN z3n?5^%Uq03dbX9_`{(Ye?@h^*PpU7GuIv5#^1boK8xKr-U*>JZg9*8%&7+|}n9w}~ z)E1Z+DV>pFq%Y1&GXv9Ut07+@k_lcj2{RcU68PED1)tz|Y$xwqr5vM6I+(jL z-rdXNTH`lY_+hNJt`BpKQ66Jl(01h*`8BdDEUosA@vM!uocZ0zVSN`a=Ce^@m=(fl zi=7KdVx=G5M(E_-BZmeC4_nhNU%IlnoOk~*(h7#)BRKT0_%Ae8KP6&}{ooy;lPV7A zsy_sbPy4!~B6tzzT`^9adu?0W4&Knm>Yml~08GTxl|yS{RKo^rap>b$13a|l)Y*$O zLs@92inamXenMCI8Q)9)ipS{Nk12;)W75+v%aAM@Ql>r)Rlj`3jo^9kfFVa!96T8n z`f8;UnwLoOqd&osHmzaW!s~uVbhVz~t1tSBN5C7KL$kCn&+_;Qls`@l`PDJB(D%wm zL@)8K`UZg67B^#0n;McnbeJ~lm)2FKY_fRn7)l~|@CS=LT%UmI4n;0bhl<5`7`!%b@R& zstn1Pt0B0IL;T-sU9$+1v6uffdNW45ixO`(VaVOwkPpnDk)(~PenQUGhyge9qN6x* zl#BKLcJz-b8|Go;nKBM0q{qoK25RO;bQkgh+BK4wc~d8I%8)_pTr#Q68ThMLSen~= zaF^n#I%S(htk(Er@g>h`ta7WYQ`d-Gxt2EM7CxNd_wQWL9*l+x0Y;y}pQ;K&U(A~C z;D|3NW|dt$J^_Zh8REjadobC`$J7m|wpP9_jsyGYMsFP*0!_$XaG#3t3!E!`(G~9- zJ!RUbEM&%A!U+(%mM&lnHtPvL^aBHMP0E$z=-t5;W#3+~CQ-4LRm zxb36A{Xck%Lk^KHaG5xnavwrl&}zRY<4O-SXubo z9i-}-MJ6=Tu4&uQ2~r#-{=sb*-4ceLMYzu>27mbIvGy9gTaU6Mr?jg05l-SdVWX2~ z{0q_BPW5K_v%UrpeHS^l;W1J`A8y0rp};z4*4pl;edf&3mwdw%v+cXjnLMW7yiYyM z&19;FE%{dFwNAJ)!IZ_!X3}9yVGuO}GZk?U6Ff$)xZUg~=oac4I}E3w)UuiCnbd?C z1HVRo%Gs9gQ-FkG1p;o6_&H6K5F0~K9sxtq0`YwxEO8@b7*H|ZQU_R|1nq=jCho3f z2LuR=JmB6-KFkDOoFWc)vEqNR+xQ9FS>;A6v?Flpr3?(h-|mRr>>>!}HI7FxTcKu% zhQ?_tI+nSGxoX;YE-4Q~^RCoGG;GSdR$WzJoD>vvA{FyQWo0P80z*D%5A)QwDT5|x zk3Mi9xRk;4o#&vf<)48`$b+Fwuc=!ZOjbGd`qx(xLYsMAV4NpC8zPF6zJ&uA1aNvK z_l#7Il{>JcYp2x{2lmV=y1Mmau4(>^O>JbLN3f*9OZNqz+#ylgj+=XJM17nC%CGss z&|X1(oFHh}^uPdz6R2==ANjS^F^UmqmzdHx6&V-Wcr+^)oB)I&f$ap~8UNWTh~7mn zW4(2QJAx7LQQlhP^j9L$%%O++R7!VWq%YQSeJ~#pMlU4)<-Eq?;LdH>Qs7}I;7p89 zpFi7irkoLETvN61h4OMdI#FZ*zShwykGSM9f6gA?+nhUf_^a$M=QD&Sa@K&k2`j<9 zxdhKu=9nA)nK4%B+_xPjU+!NC97^ssY$@{*${(^b zqc7RX7jmfbYmqndk#Chb9y&hVwek_6j06+jqL_t&;+Zo@BydphKxS5(W z2N`aHyks){LxviS`Qw?asZ(Of+c~Q(iWbk{3R!b`!KF3jnXGSuzvkQWOdd6D$q&A? zk+(%5B?L|h#-aPcEy$!S*og|9rolOtGB`cc8GTQF>K{6$hSCQdyBB6bW9EOGt%}e; zJ(YCEwh}R#!wWpq->UcJxs`;T)kRd8oHaCcRDPoy4ezV7BXM2PUiyF$94u)w!5g)N z=>vLj`=Paq4UQ#t`dx|8qq=h9dd5}KkLK$I#?wI5cElMp12sBEbfV}M+5ue{8;#Jd z2G}-C0cj1(jWX>Y<>U*#U;=_i)>)c(I|1NHpMi^hqNA;#+sr&TdkFsB36ZwYeFmFc zg7-=e*Upn7GxcdddqMl!#$=L5m|ZVPFAQUHc)T=t*143tZG*U^&q9+N@+@(|Ukles z*#)trMNaJ9W$X`u6`XL-bJ~CR^WFDr{_XW+P+&{Yha}z`Z@lsIXeO=ccWqhu&^2Ul zz*)@J%(XIZviZLgo46*i zc*cr*oNWwUjNXapR@W(ZW5D8G!~>_IHw=;KYbP8Run`g)Q}H735l)%CX;P? zwz%06$SFj4`Az>Z=;s}*m9f`(60)MTano}N(ryY4=MXQl6$>;Wuuhyf-g~GdYGLT< zyX5OGl+YR-Al@;h>q46`2tuhAng-O6NqP-~G)f&6Zf$Fd1ty%(wp$&Or~gv7D&Pe_ zhCS_yQm9@bkL?Q?wS9G3Xu~Ra;nn_2T>CIe2-=!U(gex`+INd(;>{0DQomuv(ITLg zScV%|>JT^p7EpcO{@GIC!%$!^rW!-5M)f#=n#vLFc!%LC5>CKX1*=Fzi~6qd_kw@aCGcOPGwg? z>uc#{UV@c6oqf}rWawf*$`C9Z4`VO|1$AbgPMsrT$+n1_rh(IGl&H2PX(L0MoU*fm z8iCY|-E}B$GW!*j_N9*M2|!m@NmAnABO4jI?p%o4(EM}>_W10ahehaMX zNx84%yk*j!(0cNQK7rAGLA%r?XkKZZavDf)@PiHAwm7B%;}jT!cKT#sl_)6(-l!6R z;W+|dzTBoxI1IhD)%JLyX|DP(SGc9}L`an$`5p|lOAVtrk@wLfqe~fcUJK}sKJfD# zC;>6`R##AeN`q1Osbj5c?poFSS<^ttJ^V(u2ra@_8Rbwsnw?TY`wJTmI_0!A~2J zuY5j;l2Z_tF5bBuYn--`U*h0kcPvg9^vAaV1(JsFf(BsQo`uKWWpt5(8cgzbR}*5G+PzLOg4wT z-DF(n-)ws`DXo@z_U_JXhi0-6J{YGv29+|$qMQlfF=k!s7$7uqj{#y-luSDUk~79= z6hq7}|MqGe71KHkFf&10tOl3K2M$lGQ{L-cPMd7cV%%f60Xc@OYj8f6`m}{3gYmkX z0|Haw=0e{4zd^vI4Rw0JEft~}=E@XSzfa5c#QD^*fBjl%bUjJ|{N?Luz!-Xd1k0Qr zJci#KOv4xty!;re*yHZ8d{QyJL|_sEK__9DDxrqn-dBVSg(Hu35V#W1;gW?%_>E!_ z7!p%IC0pO%t#z8@1Ve2v!w0!|%s1#m7NxrL;98U;{m_62`5ky>bC1@X8pZ(Ugudk5 z5mIg9=U_qo*tlHKf``8=4U3>31D()Fzt)LCaY$Q3%Mog6Q;-J}J_Mm&M}Y%wmvIPa86nDK#V*V{*`5`TOzk%OM!=@KnFR6 z4iCv#>zL~3G6^!?X8r(bTvt-?e05CQ5}NeP4GQ03hnqph>Q|Nb+><@#@~S+V6y`!M z{bh0$m*TPTG>W3snL&q*;TGPLOaVjZg0Jdi^o$`tV|?TY(Jjbp^fn_->#G=`6<)$uA{>rI`FOL0aTkP(Gsv$|KjT4(YHOyiD3 zhD%%XR8FdG_z zzzLuvp+)qkw5@QHwmNLu*65MytgeKJ$_0jnB=9x3pHfUCnlJS;`pIr^?UbAly5%1o z1MggoQ>~zH(yPFlv?(LMA;6hBTki)~dQ-WZb|jD82@LYmI&qu{i0a>j+%5E=mlkgc z%YC17-?rt_9kIc`_`pdU{fmJfETij0S6e_^JnCyE@J}kd`l=&%#zL;f_KqD5Xc1Vm zYcxF#MxGCLq=athqRq}WLVtRr@s-LOU$Ds#G8c!AWw+^@p5xUq7 zi>WtXgc-gw>?AjNC=-DgrV@O5;9PJ?(OG3p5(bLF)T@iRr(P_5Kc@j-x^y`Tu=KnR z0A$h#u&}=T!*Ii#LYKW`@QyLJzu)cuS90~4!u?zc3Wj}|RZ0!Kz=!~|E5Y;hLzD)D z%d;(P=-jRP$;YWNbT8TC#NhFCCIWn|5Hqqz7+|1iPw17Nx$3JUiN|ya9UW9>L^4~w zukj4Wl!F1rF7I`pno9(DXbfL{&56=eB}%{G7W_srCnVy$Sd4x+5#HKPK=}=0r(6W5 zlwVn_(@6~13?bFQcWiRb8UTr0VnM>p3oL9twWytyTx;y zd{*L>>CgB~ntbB*-%2YzKMF%|Po?Trzd6!X7&r%Z9Vxj-mwvtx`uSCUrxXKUOt%N7 zth)9y|0p;dC+H8pLdGVT4aQhrO|S!C8@3d9SPD4g?47IOO&PP93JK<9{$_m5c$;xn zg;^eRqW?0MlNX;bhWEqU%HxdDLkvHWCbS;PWW{??I=7qTs@i%gy<8Lt}7yi8tQS+!yv6({EE69zG zPXJ^zO!LQh4fqCz(YotTgmIq?SbwiB9%|T=O}Qug;WgvlRo38{dS>3V)Bz5K31ITk zUwS41gp11}rvOP1ZM(erexMJCgHx_G#v|kL3-!T^GrKq{lYexC(J`ehI~TwDLobc5(T8d(`HlLqI14d3KhSGmE`7W#|~v&+;X#^?&=lflcvm>)d=epcVLRiC$H z%FjFW!6PxVI~OX^lk;ok@JN~b4?5F+g%5Os#19@aP6@D!`@uE*-MzfgCw8K1!Ige4 zZb@w&&6c>;_%FTGk3PXy+k`qgQZlU>T#`1sCKaCZkV?1KOh=u?JQc!Sd(gs z6%LOT6PBg0!xy&i9ASMUd>Vx=9KMr6(uJhZHX4&7QZqKG8H(!w1c4?N5=-~#RsY}r zsXToe8_@(B!%@H4{hq4I%6zuWe3s0tD&G_}^WK0q_|F9y^#x~V3afBJOwv&%Bm{1L zl+mve{71)x2@?^s)z7wg_d;c~mtZnVFETmLaBT9tkqHXP>ih(P6SRehDmvw~ftg`S z6&)12ofQ-z1xYb?Ru!hOD+py&J8POUO5q=WlCq5EC_ns^!ot_S+6u{x(c0z*-nj&v z3}z=F3MUgQ$?qDMIz(q^MTsq12>b0xA?y5)1<8#r5 z_D;lT!#ED;(_Rd6or{#6G%}#rQzSG+evz5}Bd<hZ?aA@wc+nI3?$6$QclV38GGRQ!=dTksJEAG67UNx&=LD#gph-Wsr)l+(L4H%x8*p zM})}+ZQ><>(?)}vZi)a}(0T1=>6>61Xkr%N^A~Wnp(NYy>r=F+WriHqT!_|w)V+k=3UYk#A~P{?J~M z4owd8VnZ~iKjyye_$rApl=DeHRF~%*=l{qb9)3~#MqpXoA@wcXEA!i#fqbnJQZ z?98|oAI#2M>C&Z3U&?`~RyuvY@_G#YAA0Yeg8Pg6mcZ=)z3*!K3il&{Q(IZ?>BzRe z%r^Sn?y0Aqx_o>iTV6774-;5NgJl#6vTpZ{?;W_grXVJ`x}DE4ozH-t0WL_jy}fTl zO;F^sHk79c0|7gw(3w0iQ|HEk;4Z^819}E&R-hToT)R*4318h~NWrL)Kv$FiYe1C0 zQuRUZ&g@|FQjn4{nQ$#ZwpDVpQLt5#S(3(sfQ@$Xd=Z$G9jDc*wdeHVM*G=Bu@nqA zo=d_D-WkVh$F~y1UMu{gqYL4Y3`UtZs10%54G_h4@w}B{Te`3B-pvaG-^s*=V9+B4 zH#}2?n!qM&!Tu=E$RUc#R&x^wJ@hEwei7W~yjV3j z7)|Y9tG?}UHOPUDR>Xm$uaR>f#eh5YJK;Wh}^F{{^c?F%{Qjin66A&J5a6XEozOo>LBp zT9zftaR9LynRT~H&O_J2g`Qw9A7vnaw2?BMh|2YaJQ~w7=*vB(vQEZEQwW@P$h|-O z%H`ePeD2B6RSH`V@2>EVlAeW!p7$~kQ&)dbXgNMpe)wtWz)?*VpS-&J!{7gMbRoTCJQ)+|5Bf&NLI+PW=vjS7 zKhXaL*SBK>=#@YD(aY5hpEiRW`)~g6<=y}J2Va}+8{Bk?KHH(6E}3pvXR-a$#YrDe zPWTYOefOnbeA=Y#;XNc07_B$<49jexJ~lRo{}+uoyf3DbH6> z&ix#c^xll6*h9(q>b(p(cusJImW9`&q;2C4ypW0=#Kd+Et?cIc*sPoJaWFS{_()@;TL-GV zIL@=>=mv&uvj9LIA+N{!8^L?l1j&ZV!V~X=om@$PH#_9`IsBSg$*!u3gJ3a(A0XH+ z-KkiAB+Mh9T-i*tw%rIaCxdhJT;KVVvhIV+XTehg@YHV#t+l1OURl~xJ5)^i!MLG; zkv@}u+Supw#_sY((jN83LRZRX^LiGif-qa^BjVHu{y^gc%x;G11MN$B+yl`YHA9mN z$fZWpj(Xr^*9T1v^k`%9ITN3`zG@JzAb<7Y3Lr4an=eNea0AGXuC_z!IY{k%y&G*Qz2 zF?#Ao1EvmHOwg)!4M}(m+bNgHik#rJ^#fn|&90poI=c(HJ0kaet)PyFcB-sVHBN`^ z`xBU=SzwU%jFCXf1i7=d3*DpmM~Ta1Qi|+%sV@u+q=68{$sdIMwp##f|jmqwwsdj%U*@YlVN_ z%iz5OYXSsza$F<^VEgAX{B})LKIJ7_%u*M@D5zmh=OHKcjVlv6Qd?5F$k9K}nmq72 z0g=9RCvwC$nmftSirQOC=a+uaKs)g9sc+gQ_&d)$KJrRk?a&FDlr`=Iad6ep2cCZJ zrIbZdy5wD$Ub6#&L-Z#1n~dMH@@6Hw485!*f^j(Y!lTlICbq``e-=u`|i)K?Eb$OuRIXG zjG^!R^p)Lz`~2s2zniZWaMpvm<5B;sPE47Q+J2`m8hGjUIqIXk-M{+YPj*+bi(sGj z61ZOp=-*#|_UTrujZEiC7i=FKdmK$}EX4L7X5w4l)JJJN3mdgXx}-8XW0fPQlV>a$ zZ&oN(W^s~$O4_&^t z(}{}Q!Y4n2FQ6_PO(x)mq{uIA7V6_tHlFUOGBD zdd@L+EzLOiTuY%X-R5rH$=&~b5E8go@(%*TLkv?#{s{I@-#ff;@#2>Z%GK@r(!3}j z3jS<@a0AFzqz3LMgM)Itt40{bbNRI2noQ#$9w)e@`V3aa@ch{XUQsRra{;@dZ&3R6 zlWl<~a8l=DR#V+KL6D5VFnb#%&X9!G^&4`A9gb=r9SA<~8IKe!{dhZn?x-*X&GX1W zRL`aWY~iO^`zF%RO$nHx6v$5yIB&{->+N@=0rLXC!1Y{_XQsT6r?auabQI&nNE`)s z4N-#6>j^RiZdQQFQZQJfu7SeaQE<$s=@^XRm4YWqz#k<_j@nDrzDOqu6tCn^MVlLo_a!5A`I&je|CFwxEF83xoJSzG z$%x3;Du6bEkRb;R{TK#c0It2ds?hdKIm9ZGR9e%HWZbNj=?uU6r5z{310(F63XJ0H zE|3ovS>4H(1TLmz&dHB~$t|@z83SjM-7xxYO?Bj32PH@Hn7(E_%uj83ofvBW(I4?L z^pK1Gg%)@P_3jP-CZ6;!{XCF@-g9gm2k%E1I>)f82Umlw6l73Ob`<2=q!nCfQLkI- za-NLcBNr|dHaWXTu-(BUc+(D%t@@o%Q9_XsMNTGwm%#zV_iU@o%u-U{`;k6ycMD?- zb>wui^CV?vq9*MZxxRhv=I)OYSR!qoev2iL2KraorSZL={i5SQf737Yxp`(Y=f+U- zj6vOnK6mz`U%s{bC*S$uJvQOf=JYtbNB+@wez1GxSMMO8wCT$nb$#?iA8ih^$twLG z{eSfRpYHzX2QRzar@aL3TLMq!)0V&c>=On<`kFvON1Fb{w=qsmY;n%VexywPH{eP? z6$Gyi2!`5z^#wYvqSNCWWsntKZUtTNtBfe+l!AkEm0?oUt*X7f(|#9qbbtnoiA4Lr2619t1cefL@Z4Q}Lw6Fl10=)T3A z+LPc)rNI^2_yu_G{~YWtz&O4U?rc8(!)>dFWm?EN9(x^~Zb ze!fbft;iWave|!aV0Q*ct(G$^(!G53Cc=Yg{uK21;QF@GZid1XV@;&v1pdDAWKzO$ ze5xs5o6`o-pO89rOtj4;00=lnOX~C&A2I%p6yNo74#9R$I(_Z@^z}Uj_ZOdz1oi@c zKyr){u56?H-YesiYg=Cy`UG!ZoB=$F+)7`@Z=Lv=0k#c2!MqWk)Mt2dE?_ZP(CyNj zlA0n>X8C8<&P+4KVbtHuppIEckeuK?peRj2&QVrtsW1lAEMJv+jx>sif%S|Khjff> z6lUn42)sOnVKoa$5j0@SJ+LJT)=mR3yQMp2WQm2Qr4*>=r&P8;~*PPW3X=Tu4dbj=ic)uMkhKbP4eh$Wa>%`Dn)3* zgwa4cn#qc8^P5%I?d5-|!)Qd^2cPmYkut`FL5XomzUw;3*>zxoqQB%@@z((GD&q$R zS(uD)PdhR^Bv>cl{CgBAS+P&=@Wn3ed{P}DS zoD_WePU?c`r%sJ_MHRRaPD+LzXj~620jJ!S&Dp}K+HC#G-O?JVhW;8cHS%A3;i-;7 zu4_c)Z@UHt=D_Ywk-$+_8|o_^BLiyMqqORKoC1M*`k#MrguiJQlR#GGlblxF3&;nQAiko(_a|dtWmCp20^@Ab#CZjl-6X-WDb&{Pl z<4}KBzT+W$FxYlR=rbH8`RW@60)M<1yB$Y?;h#w?y5!&g@aK8_#pFs6K(bH2r4sNt z(EsOK|9SVvKloYN*hCneJCld{k`{E_x_EWjxBv2$-9P*{-_1(|?ErhQj0yh#!N2?e zci(&YjgFrIvsGpZ;?9&%XEK*8BE-@WHmwf1S{O|MO4o9=|wW zDWYf7wW|McOV{aK^tJxi--(SGov%1M5o@Jj%d$gK`)mLhI&7co+NT`ZH$G2CUi)^j zrEL6}4NmRsR-YYDc5U7GK@H?PMY)(*4(TQHr<=*;cHAp`U>VKFzg zL*&$8I(d~_?k^kUn(S2eetp0X8}gqzQIRrs1WJNVGFv>xgrz>42UOZAxGU$> zXaC44l1yg!O+9!zIgaooegqM-kuzz5h2f=b5V)bf^yE>WO>rA(^uG(2%SWbUXF;o_^T%Qb%B98^Ij;Ng z*)eQHr6m>VSXqx$#=pu;-uHao!C)E0Ybr3{FZbgU~=UP_5ij)`npfREC$}szFc&Xy0nefJG_L4`u)b z_n9=UqBx(x-wb90i`?rA1A~{+jQW@WF%*dt<(S#FJ-=JZQht(I$HCzmtWk9nln7F4 zhy!QMjMEzU>uPA|Y|tD&)IG`$gTn;x6u#iDy8uFbF1G+KG6)S;pFF=Db=UkBh+#92 zV#b@2_d0!#VBwiU^Mbuwf!UeN)T6|_xQ)*1L_jQyfO|RGKJGUw0NHtvXY&PVf^2d% z8FAf<3pf7oZZ-SXjkHCaT*aB7>-NIo3t99q9^ z8X2R@yAk>(-`I4|*)g^aHbbkID~7H7*8($BZPh^1erEYyftzYo!n)k;;GCeF0f6&* zA3va=@AkD+V4?L;2kkW$xU|-g)gXXAW!i@6vvsqA&^6jyHug@ZB%N?w5Ep2>;`A~cb&JYz;Xd3*xgjt}Dn(MkP6LU$L;V;z|;D9_67 z@-B?N930fu0`McqyPA$-Cw-?=I(EQ^|{^eJbh{R==teqY1s6O!@g|t=Wo2V`__+s-e5ev zAvZo74&Qk9+U_6z`|s`k#-($+Km5k;?4Ez->D|R6J6+Pp)A#A>tC@)W!K<(BzV+i* zc0axH)@MfBXCr|M1zW*oM0alT+J%%*jG0DxPG7@aO^8QedH5g z@USnnouy)YL`){t|8bfh-0W}P`xF~n+sDR)hVkE8rF^Jf8HA>g5MF`Ezk-d7gKGI! z8es9tA5$c31OPwJ`*zse=j+JNb)4kfxf*l(Hp}s5eor)tk z`3WkwOpZK}S)Ajckbmfi&%z(H;O0?-*-e1v$O>)oqY5YG`9&36Zba;z5NIC6!Pj;T z%2rC} z>t`=hV3Gkq*(yXq5Q_>^BkKA4mTqjJ+TMd~Lpn#B{P zM;AC}9N-&cVZ2ciMK?w)FenL1^;W)axMrDKEcBWvs8J3;^mkP`^__b(;p(m9-8r;} zG4p~9WM8hAV~I#vpKL=9<05OVZ zG8r&^Lc8b-k#BGdCi{GQ47C83feQ4>!nQ1LxR*N18=2xM(-3tcR?y#LWJks=ji;1l zRAiuyAuisOAzRk{EOSvoAPHK8J{* z0bhTF_j*<(w3qT|su3sOlvl1!Li(*27rG|Pkk&p2wtkKWFB|IpEze3vS7%a|OuFO7 zQ8{#yZSZu$0zGMm887K4cnzIs(g&1Ds}@EorCM<2V0_?R|2GM8JG*b@F^6$Z2E&X! zvgwR@h;N@M4I?|_d&$Gf$Z|@JojQ&=>MOoDs^!Qe8R4PSBtZ01IMUmvzitxv{;O~7 z{_~YLc27Nead-KNi@V2@AikWi`{m8}*hc#OR!w<<$MxO!-n_m0 z{x^THd-B3hcb|LWk=3muRflfN!_4`{vF#;e93^@F0Sh1)T!+Xer4^J+{|k2BAqTDAM}5R3x4GH0_&k8 zIBL_X+~>yK3F+9S`hqj*F*Qau&RaipkU zQ<*FzB4O`hgW-a|z6A{NeaLc69LN%p`l3<&n80#h{Zbf~8{qO2YOm3fIu?@jiP-iK zQ!qIxaccsBZ6v9+bELAuHv|Q|l#z3?f6xmy-CztG_$9s;^z^lOZ7f-SUT3 zQesjakxt_E4|5bK~e*e9F&V5Q? zKLKzb0(;@3x0bAqyt7rsV7Q-@v2>}ya0c*AirCOiFxA=P9Isuo0-fj4Gq|n`B(idB z(A`K-Uu9(AQ(zgH-Rzt}K7rFHciY7&3-u|24)&C3&>noaiKxQTUVzf!C<#j5248&6u9cN|cT*pn5kiSfjwucAWPBP=FK` zC0_a1A<$V%!Fu46JDrp|j^#6X;0JUSaNcGYf&=r~wDCND72LVch#+hkF2UjATd>Bs zz;6SSi=+9HJsWVe6f+nRL87 zGA-A-ntC;z{Aa;le_w1<)6HHjx2pYvMH%s&|{S##`az|uzL8zZT(Cb{5OPrp&eB!G5MiVW3d9LtaV)K^}T zU#BGb%AuvsOK5XF69tU9F_7!DX?Y5aK2D%70u*GOXwo0jMx7)|wtD0znZQU!UI?UZ z4|YHP#jkeX`}rFY%3jr1Kz|MY=!&nueYL+&hx|NdlnK%&q9^J5beOEn+}geQ&b3)- z`nX8azxo2L^x-R6y}6Qrf1maecn}i!+GX4S$JW*72olEzMA5&LBYRn$Oy5V>y2sA2 zo9U!A^5y!?yay#`1C4g?LI0{;jyu+r5|ZY0^CaijSR?1V3G| zFP6!xJxbl)4-J`yt}bV1Cl-oskG}6S=czZrJxci=!GWG!cu^tW1YfwI>%rg^ZCn+< z`fM9H*d-vL8K?&G{IhLRSy>p^xR&eH@Zny4klaFF>#|+3m)-R?H7nYgY!4-y9ER@T z4;{|YTAxPSL~hQ}CqIX#xzAS(&BJ_-!=uZ#Yy3nz0xJ8!^^T|7%icE$!ls7z&}&s4 zop^7`p?KAcZz{Ws8<7!Lp$lE%NgMN>3p+Oi`xYN+$5YmWvtI1$g~#PVS4$h5i8Bb( zLRXxp|AdM5olQo4&Ofm6NY?sE-*FY3^8(8p)7;u-{FK3%dR|6uOwE`x&Ze!z7Krn& zd6jKw3;oji}=yMwwE00=B?YrYo|6TYHfsC17iO6>$bJBJZS}`T}*cdOqb>HNijxmKDqfq^Zm4 z6Qt7wr?;bz;h>D7Yod6J__Jm+gTtj6=PH~A+F>#U?-L|J}%o>8?j^lpRHP?qHt1XSjSBV9`vK+VbYp zJY6l<2sCVXl&be~%3&7NSKBC)|9!k5^h&Y>vf!HiCm)|5ypYEX)KdU%^-T0oswI7^ z?Ct#OFz#UFw8lqUd4b_F4r##l6-JGcO;72Of^GY!VikMfhxOj`z6foI4%-~hN9nJZ z#H81?pXdYnmcFB{`f{7d5?@}(NaoGM!1SD)jiC(c6S?DYW`^}={V;Vi8IYDPtTAE; zi=0AFoj08X*!<~s#_*i~F}lMG2LersG(e7Q$SRpN;sJ8eG?$ zrb*hVUqxW}3N?SjKN)nghAcwcj1x23Gj_<^V0SOR=64ALKh zGjuwV6FSs2skSBxly~oD9}h8cFcW@Ydw+$o06x04*XT6`rPho&pne}1@nFt&Yp4oS|%BOaOKV2pT787&NFeE z{C(O>;ICQ&Y`zHq=D2#3P2-m&pKh*A3Xw~*cZ+iLYxOp}lXG^UdVATg=;8*@fkA(( z(}`kwI(52hGUpSp$dr9of3w@{1f33#sN9qS`fkU?V=IQXu^$sKyBA!k>;5j~)QR;g zd>$LTbo`m9TZLfP3^4P5c7wVv)k6 z{Eh53Jcz$7kGv|2*l)6#>u_opL@3su(5Y_LwR-I{tTN^MjrMdl#FzJ6=_`xgbx#G^ zKtx!P?JU-4cV);f9WHB67E=W0U<7~iwL6)#T>g@~Hslw0*)e`fL42chrxMr6iVByn zGcR9Oj?BaW{3_o&*G@?|IWaswp`I@W$ls?2Ac6e^z^5lCYY=*2AS2n; z%z&f}I)j?GFfgHk z9{dTkLJK(D0R{`tKA^Y1v%+>^a+^?mbvXs)zFXNk#Q3YY%G z&I z)b-27bLEjc$?m=BCECX1#n6S0PGrPkDBQ@36(AF{=-{;-a_$I|wpY(T&&)S)j#acu zz!2#_`W%{E`*jj6+x5DWh5W-4gH2xg8s5mK7i?`eTTL~PF=`SZr)iHFLniKO z&2!HrNCWzmuRciiPTu55wk85i4yI^%^9)@+pi?|&>0y@uo%lb0 znh@4o%YI~@xTyX^M- z|5ZxhVE4JlFYLbf)MM3aU4e`qrTf_|KEwpoY=Q~Y=;Zv|XHV!{HX+KmX{^iW=E6u> zW1zY|I+%X|ZYNp=&XhA4`ByC-eO=pFcqVWKLp-8EAil~_o0Ma;aqH33)&K6~RQNu$ z3~A2Ul368JbZmwNAqIl&D-8i&9H}pl@-LgI!cXgjc!E?isBKC0JY zW{>R-yogL7$X^`!#?C}O!a{|W`n{W<7PHEH&ei0!KxoTl-ukq$cnk=1O5ltCvlI?EjFOD8VFA}F&vfWm?w z?Mj_V5SWO*LA2HKZY_!efuAuGEKx=&nco=wqwow3#`DpG+~@?O$Y?A0DdILc?s68abM6_k8xqJ4Qde*cnhqgo+H0__a!W zoc9u#I1#i~(N`%4V|c0@!V?9`T%b|g37)kgWeb0U+|Y;iUI#b1kTV(t*^>5#yR+TF z&uN%9{)fLPVsKKTb;%|tkU<^UM>aWt3tZ*J@R3e3Y?)I!BsA3_K|^3PxXroM0LBMj zXmC%P$!|Z};H&Gt6$aouvxg3Ha6Qi4^rueL1phKS@OQ#X-{rgn4tV%350M8)U><`B zo=vve+pPV-hdYlGq>rpVYfP97(--U(frEgx9a=kPf~Re7Vkdl(3&)5|&75D4F=o8G zA|Kv3MI+NOr0x+eDmvcq6&m2`wnelChcY_0_SL8Kt$?d6J24TOMxOeAcFY9tALo;6 z`o=yz+!8SP_kaBlU*G+2|IJq#034Hyzr*@A&a#S5E*sBi`9ano5vIHZ10nhK(hNsI`tJ8e~_UL2%oei0t zk!(0!9<{1S{%5DqGaQlE1nKhBBO3|5_!IPZVS=nvrgG1Ha&I3e zyp~rxjj*|eE4FaAk00xY<;28Ee4t#{pA5a|WMew^0;Bhm*Turrdg6&EzLWz1E1f=H zc|AtHAA0Yeg8Pg6lE7Z5_a(0TFKi3TdumnYw!EzTOHV!ZY@VCS&ht?$48X-B+j3^* zk@678ar!9}CgSvgrgkZ&0kcFzapH zgL}ctJ+j5PGs(Hvpe+HP!Rx%g;OJmZotb47oFr%uY=(7$Y|mchA{izSe1@=!AC*Cd zH!I*~csZkty5O(@V(7gQTCU~U|E|ghuOKuP=NH}ylZvAQ!RQQl==BM<~XlzYuX_!SKHJqUOW9)>9Zqr4fc=?k5S2Z!;_ zJwLLIbmQV56ARpP|Q3Btye)B>0$KC(R<88%=E9DN4eFSwBgk^N@R}e>kD0d#~=34L3V*R z5{vBhV{&Obr$seg>NoHTTryD@4m0m2ZODt`B~M>A`DP+5|4SFScXA-@aV|K?N1ndX zz8`=L{GaF1x>v7i(`m1exK9tM1pfEG^}DH_RVM8*8rW3Z$EfMK& zfA?!&*!`WaTBO;hy#)ShBoGz2`+7eA*Jr)cZ)a6Anl?IB-_@V16XRphfy(RO$!veF zPG)oT^&Hs;I+}f@(kWJx_4KVV(G+`v;#}A*t&cJBl7NxJ*6!vWV2SKS2 zD7Ju|2+kZrSK#NoHa6um7|Vl^2xjTcdF@!rS@6K4U0oq?{DEL}AEh}o)gMR>R^XeR z3p0^{Cp+oto8U1tkb}UFENYk33pwnJJoVKpJ%9$+&^CE&Q7UF)`$i`eAXdM8MI^}5 z9%r)vRDA3jeEG=-4hXz)B|P3KFUTvSu6ifSOReSG{nz_QNyWmi%P{cljzur~OA2hG zV@L!%bhJhm6q1n_Gj~xca_~t(V()dJ@@>#p-oej5azan(kMF9j^)YmGcY`hvy1fXR ze}TQWP3rQgQnKX>Gv|`L`Qr!{T%i~ba~YV)4nMOsUmu9ylRlM#r{MCXR1e17;_G9X ze-ATX3%yeDgPrpF9e+(<)hEjzeDJ5{^e-|C_`u|2I{FG20{P6h(>4wE1BaaSqueb1 z;Wg*lx0DQToWS~7fib&ycDu(Od+ZBEls_JcUgtTUK8L^88CUn@Z{K`9J>nh zV8Ao{DH_3um%phqMi55EcNBLGQgW;eS1AP?+5~^+?RdSIme&Mu=i@ks->~Sh6zhwBI8)znHj~I|Y zdMWEV29Z@|ngFe^L$93#OEG$xiaHW}@DgbQ>)>Em8)!#@H6nqn{7L;y7UV&Oe%cFh z%Czmn4@XIpwH^`>!tA7M+zF99#WGO9@v#_XOtTpv=J3 zPiL}-(^X>@xaB1k(V-3+O*qvk;U#52AYWyyoO_Q1nYb~#V8TamFE^@2a2Ze)PKf`=>s!xtLi)GnMjc)|4SWY6Zh`K(|4b!tIbW7|{>dk3z1n@YrW5z+p0fawPFnRR0At^HF!`7MtAEqs zwM(IzFJmWI1K*VE1a8Wh+*V#$FjBoRYh!9F28B1yFIzzfaQUUuobw|BkN#JKK;JO~ z002M$NklkxDf38`u8}XlDY|6dbHgpC4vOl%&v4t3Szkx108mA6Szj@~q8>8I79$0(~c0Mp; z;wAvkG5%`XDY(@Uh&y({0lE1RT`KzD3wXQ2T~{Od+9Wzla&7_z{FV;@bi;#>jl;+& z`GKSS@aH;pAuLj;inxJ2T{ynolq<3#9Ec_&w39mgjqdV;4c#E7QT)@llo`)X3r`wv zW|KQq>lYY9$MmDcXWJ@e>Q4rr37PJ6C5xgBo%zSd&SjJC22W#y_zQ5`5mElbI|aWc z(B_>G1ibaVOfIO}b^GJc@MwMVndYiscUt<^rLj4APXh?->gE0`4P7PTUiTqhHrHrCD0uSvB zGNmm5I!e$fIIB@5gBWHz0B%PYP`-6iAqp_KsM+YD^5Xg^u*?|W$`*S;Cz(1n2;S(Y zXen_s-e@9YlN1u;qb%8g?wY~MX`PQ?s|-%Q{~j8_=ys|q-jUNN;xW8!Pi+%Eq$O*3 z%0co4wzSG@_A)AphFY64pIS8Bfqc!o+{+Y8%PIjrLw z$5lS0K$}h7F>;;dZf^+f;XyeG{XT;vwJped zl?z>U4A7E)>Uk^%Z|(O9z@7Uyj|{|E242f1uai{jmA~{Y{Rp1$Q>UQ(=R7>)5o~P& z26%%te}n%hlQZODLg3_chbgD739_w4x)P!Mw|@BY?)7)Jh{?EfG0ebX*`r7mD zANr|ex0AlybA2(V^u0WNkCUzFDsv%QuK)J$J=gO4KYIy$))M$eURLQ7zxwj(n{*uA zERW9Iy7tMj69etr>GkQ?%2aQsZ+GWeDhMQ100TIw!K*RAhlr2DW_8?b}ZR&@LLAm}P>$*El~KWd?GcK(4UXfqK^-=oWs4A>Yv zIQK)>Ng!JOHd?pw&8Dz@;G+)iRS*B6suRmgkjj&vGW@~CpFDKImmxQeU(k8IZAYqi&l63j?-t2cz)keSP};q`>~<)04nnz@J=}?~`E^Lh~{Z-h1!l`t+8UGw04f$G8Yu44kO? zDm>##@?tLvrba7+$n3FmN||D0AdEl3Glgrw8*G$+d^1z`~ z1fuGDY`_co!0b!LQlJ4!f=q+E0T(46;|E8LRF0IutjdnUSc*B|OH%{hoOkP7S8!*w z82y68LvI+(#KCa_oX&tEGXE>UC`NBX^#Zm0sZyyi3{Ibg^ZtackVd(;NaP}TrJ(VK zmt0mxb^erQMY~dFy@X zYCp*xlLX3k@@SKb7Y3q>l26sr?Fd#Un<7K9R8PAL_VI~E_2hg0PM>z)^~gy67*QOQ zr``#gz=vL+=vK{SbOK6l#GpW*jmvQipwfejp@Iuf_yKoSD`4fwkE4*JebEV*RJvEc z$hCV7@JqWUK!8M!ne5_wAS7EG*RS)59x)Cof*uefjCfJHcwj zD4n8{ulk=$!9qG(`c5p4zLxWrN&k&{h_1CW?9A?~&pwsO)p7D4UTN+*|Ewk8i$1ph zM+H`&E&l@@Y)T)UOP}rz#q?D=vp!79JD+zK-KAQ;XQ$XNcHYXrBe)GpGbr4%_1kgz z6l@B3L6sjpJTPaG#6aN}m=H$lvj1a$==7=UPV8OA+)P>rSkij04nLsR+c=}smw)EY z-TN7pxmUOl;yXvz5}WLN`B(MFwuuRN1;6ea>)1Gf#dioCeB(md^-de*&~36DF9cBf za>KDcPs;T1OmzbbT_|EhJ5i8xHdkUh$5(`DU|XyLOAfn+Mn`tB6Ymx7@K_oOC%D(d zLdv3}|8nf4e~t}wLjzUUfPI6T>P24qYg>nwwpGZle~^53*ABxL5Bf9lq1k)%DI3ry zxGEF4$tQiHL2?i~lE?bdtIk_K_gY>K_6Ak)_fc5>VY2%;!Ui|UQ)l^EVQi}d4gNja zRr!U6qD~$;0o1n1k$iHyRrQJWWdvo;WYSl~?%>FA`pW8a#@6^H=x?(SLe7^D1(!v> z=52Jk)+hP)eNRPrO8F;pxUb|B>stlKb^bmWFTC)=hu(W%;fFY5FQm_?@iQRbC+jGX z4&5mC=WJUS#87w?1Z7rbj=qeq{I62!NypnLnFg~IoWaKBL99oXItV*VTWtRCFUp zW$*%*t<*I@QDk`au7k>&aZ576;Tj!N&dR~O(2bHqPURNHM%hQE$y8Q8Cj%Xz3I60a z@DHvAh-oi$xDP+NIw2Ey9(QP8pj4C8ZP1u|j;_fL9hCt*!HFicFtLme{PUv0>@G-q z_r-PLw=@P{Ws@=tJ>I%np9vrM8Mc0kF*q0jZ9-{JTW%8tfkDFP*8YzAF51walfHMN za!OhBa>xYS;CrzmS{Qc*$W8$?qpN&GJ|k;}r*TT_Fh=On?;RtFQsO(jQqZ;yCP<&U z97Gc<4YG5N4>S2Q*~7rk%l>p=?b3dtpW`2W=;T=WSJn*Wz)8NO99r<^$LT@OOboP@ zS`IAjO>ReN6Zp$_=yA@dpR{fGFedP+ZSl3nP$yfGp^o#Bu^IKVyKnyF)!o(ZxY0iQ z^e{`{*-IDG&&KIacRlI9`k~&zQ5;7#{Vsh?VjtYor|27#1CQi=G?(%cwtd=5;Iov# z7oU7=_k}zHP`ww#eJ@b-V1xDOQ2LQwWzRRoT>WV`AbT|WmVO?4N$;-J-|mjg&Of#j zoTYd5clx$Lf9z3hP=<#4Et3hcsN~wsdA>Y^88At7G#fVODh;h&49K{uJqdg|A1t^V zlxG}{o@Z~zp4TT}PX}7zsL>CY0)xLbzRHYeaK|Q~h5ZQJl&QT0gYOZX8SD-2 zpdbTwcRYi^+7}Y1Tz961?&YH>H?)V=&|BXsGAJ#fn08^8_yzN9Jip8Y&H6*wMWjfTdNwRoKnkLkHO^Tv#dHGQp=G zIoCgWKlijzWgmY>wUkc%;Ei3Emj8iQcu4HFZ}E|N?vqDOXY(M^lnMNnoj=Lg=yW^h z7O7e$<;It8M&(|@z}8+#cp3jZeI{*c(g00Mr`Oln+Pi%q@TU(4X41STC?`X5>yZ<< z_1`ve^7|?`1-E{pAL;X*7{YnzvPhVW;!vmZ3_Ny(T;=I6^|7*Z)%&&90#NE+EK%E^ zwI~`ohS*a#!yn4_=~I@#UZ|h4KtBK+8K0K(>2t+1PR=}QoEcb@1Etx3!`RDEFfgm= zGTU1i_kbxU&;4wXjH z3t}lCdeVydauN|gN^#x<+!aIXO0pTD0WQ%Qe1$Q1D zptu;LD*fOS03Qd=xhgZfoym*)G+nSnCL>F-X2fT5W95&6i*gKn^gFJ5%B$;`?6!54 zI0I`Hq3x2b-f*QLI<*;1A8FLsy7t%vIOx`P=(a@IW6{am_l^qTJ z3PFpeJyI8~>+uTx5FqVE@-@84tL!45<&foewrO(0zzEJ%x@`wRj3J&P`R2zObRaMq zzG3f~daFF>x_+F`=xs)0@CF8h650eR%rY7nFm34C=kz!~;Pw~3@lLMf0`AZT-#ljG zUT3F4&doY6;akfxaGt*(dO0`KzMQU=r#wDQl;k}}EmH#=y!x;{B<|s@4lMP{<+wv zhh72~G9grblKzmJR+EJO&i-P6oK19*WNzVQFFb!amKQB3+AgOjCeZ9{@2beN_r&78< zU#A*3b>_aw-{dhO+x;~`uIze;K<#nrsI8z0aEl7S@p;%YW%zD*;2(5cu@z{EZD;_W zat)}0Z}|&r!np4#2y_$q@HS|}yRymWgNzKu=cs)0Cq+w>GH`m_(S&~hq#msh3O3r^ z$0y6P=;|U)cT$osz68eX&FGKuIbxG?Z*~n7L~ZPR=^%J>Z}9so#V+QwGGQwNkIt!_ zlnv~uk&+vK$}*JNl|4j@qxJ)}f7Qzce!7rS|6us$7ipJS%~y_HCVO-QR<6f)+S)8W zg))U3-;3{K!XF*Y+y3est8RM$`Q;~dLZ3j<6h!iSZ@WJk@xe3QXBm^1*jgrKFtp&Ae*{UfzXs`7}|3)6`BeUo+5VM|o*oorB62l?nRd5EO`t zxju1TYM2Dq>IRN>2#o77w1?##&Ez+-XTa;U1TT4Z^*v?bb?zSnY63$*iZB7YenbFP zxSPkIL{9H|!CcC4PUMgCpX;{cI1DF`281^BkqEx62$)#lP_$npUu$g=t6@BdqtSj` zK65jP0jV4SpFm*0lh^*8JZ03wlSzrSO2D|9pusc#81_ySFi5H6cP8bv;ar-{9vbyU zD+=nI%TALzFY+R0WqmIf-L^TKDC??7ZaOkrRvpsQ`fE9TC$O~>n)D3@4c(qZsWDDo z`)J@x9Kh}hNMG<`LNe0_^)dMYnELr4C}-fA%!3zBx%~yFdUGFrxtANpmvL*%e)9(} zrQaX~Mf>!yOTZ^6>B8=|NtZB21T@AvCz(UM^r~~c#`tZ1#;xSOZ;@Zigw{UoCGc5E z;6n8E*FN`Dbs+ztUr0%Ru7AVkZi+G8xn)d@Qj}~xqYLD25$t|xrZGcFxrex~`2-^y`iGogOZ?KJ`31Ineo3wHwAgkR1C4{hhY)P@l zaJZ(+Elx=6Bg<2JUkXzu2=QhWw&OBz8eFwz_zS$^Ntxn~lhorzlR9`c@s70en>w|1 z;ivWmzbVBAqNlW`3@hg!{NRw$^~k!mGx_Wl5yi~ahwv4HzPNe_s7-AB)I%e_+2{UnA`brim6O`p&t?T^qvB#-pdZ2vSZRaB~viWH30;MqYR%&-oh}1b^`cMi;Vr z%CcSWSMC}dtdQVwS&m>-Y?_&U1 zTIn$GPG7J54?EsZ*$;#9Q04Ch_JEo@gXev+%^>U8TU%Wg>I5bi6|O?e5D|Q~IjZN|x)bbi-LIDZp z?nHD~uO-Myc>!d>z?}Q#Lw<~cP+o9*nCR>9%uFiW%nJZJD;WiBAa#A3(Rov7^LZ4C@_IgXh(GYv zu|<(2NB~UyYEy0L2E{q|bHpq;gzq&YuI$uSA1oD;3HNr-rGQc2ahsy1^sRmyBXO;Okq?wJErq18va! zRP2m$t{dcXNJ2yD3U1G23YHnEwn56n%?+8)OfudHZqx`|Z#wSonN4N~!8@z`X|Kqm zG;kg{e4$-kJirZyi2-A%+YBQ!{m!I;FLR*%diqo+1#-_UwtGtXq$7BeavIW@^Jt3p zsneS4a62nX!Am9$wo3uFTHhR z_k*|6=R>Qq`?Qz9FW$PEzLpz+iS8Pmk$dTP%9tS3dpb#wenc0%n-_%s z>gxWbk`>zi&u1cmFFg6k?(*Z0R(G@GdXd$mg`r~e8X+1{xzHrH16 z`aJFHsgrtq8Rgl!+Q#G=2mF8rjxk)t&xQvtO#ZPM=kj=spu4;3z{)=pZeXFe0WH|M z*T9M$j9qp=x%tgN?1V(GQn!jd`6-hTIrcAnuw&RB|70eKXS~M7rVdDJapFd3l5jR( zu1T#_X2Uw>YdKq_<9!0=Tmv#BojPT6YwgEYR_3@~r`mJr0jxq@;Geji0R#ECAT<`36uM>W57!zwWDF z{|~T~Ig_tv(bp%7CHf9Qpq-DAWJd@Iny9F6knAqJDHFP(>0%)vOqJGaKg@M^lBN{g ze0@dQ-XV}Jut=dFOZu?1TU@`l+{ zpGF@e|0(tD&i?R8C2)_RKPj+%{q*w>Pw&0w`p~36l+=;IVz^oX?aXk-ivcER zF_TIW93`53_l#`&)5IFn(DBJ(fG3Ot{fqrn+W-(zJ^1G2QD zL-2)8j}C3@HtykJ2d2@)!dh81nJbE%uQWr9v(gAcf5!L_!nTL6|&7oiUu5 z_RyESFYFTgE!CJk1M9%3UCveiBzv;9wyuAU^2X!|E``en&!A0$U~wPebZ zwTt4^u7OJi+Q(L8eM6t_3jqV0Qw2X>lu_9y79H_p=Lw1SYZoYtyglm=4!9?dTIy;O zvRVebG7L@R3y8;8%)sMcxtTba-4`b2$UrZxgP}_Jz&ocD5YlOT2IllH*?^CnroV)) zz-K_uW^9;Ha@TmmgK-Pb$RvF|bnCz18&}F&`NG#9|LElgZOPiFy#y+OH}gWsmtX&7 z$6Wg}{Tqk%LZ0bgeQE;!anAd)*YrUXPYr(3!`Px9UioDvfXA8Ni*_%8&q4xUz5G=4 z9i3`t*4SN-T(Jv&f)zTF-d%wsT|8DH{Wf}cwmQ?Bp_m@deTyi9&IT>)LW~lX%BIk_ z>eXM$J@$i{q0^0n+Wx?Ra}@RHboIgK7g7K8bRVTjHn{AG_b-8Af}nOR=WGr;pgJBF zea>tD0+0ST7L^)1o9mP}ZU+u~H#;o_+_iOht=xhyeE7l9)%w`T(v&<*c#$FhzyePL z-N+U%a5#qO+AsEuEkuW%MBsxdSq?xW-E!9#@Caxo$OEE}vxaYYO?KoJPJsnO@CQy4 zS%bw!=EvW#U{D*Hd*r?RM(>{TsNIweMFBN_TVxn`ZNoHrccK>0pa5SR@@PzOR<@}e z(1p$4Bxzi(zJOME;Al!=w2>+Ku#XHP;6 zdgNJbshqY#UDQdRaM^yVmcD_w4ZkLv;^UjT&?h<`N>AE}Jd>2IUzZd2)#SsV3tT+v z`#Ei1CkV(R{Xo)R)Z%~W8$2S5XeK};yHHyq^_2~fMO+tAlzHZvXMCmx^Gm|AKQM*H zUt8t_EABHtR04Z}eyC_Z^2W!O+tz7Mmp^^Jd*bP*AIrdwO)_9-Kv4^nU01EQ(hP_) zwi-M4X&ee`l+g@e=VtpFTx1XmoG1_q7#Q$fQ&`>Ny$$RtfelZxI!HN#Gz3`ltatBJ zfzmftLEVRsfuXH|O!XaMo zQb4ErU^$Wjh>Tz5m}~XWv|uxQlw%D{5NX@)3ds3c3N!cng2qA`z~GNU)i7uz0|Btd z7v|0W@K`NJ4O%<~LK&0Mx;I<51lp8yD9t_P8O+)G9v*Yu7bk`e2~CVmWuR9yRdI~dC&|z}PwL2zSy7NGpjuo;4hg=Y$-8g^=lp;s4$Ob-nV4P`vQ*?Iv(2Q68&bUvXGSQqq)n{XK z-&pV&*6;lI)dg~Y+)LnrOWBNF zG~=J-9=eUM2`Gm~ws%TQ8I{r5U5%j+KlL5hlfWqtfzdLdg>7alShY@M$F5aaxd$J- z>gWGPgRSo2GyeqWW0%M8VZXfWufbYy;D6}dU`ePT0K6NGVzDrZe8B^`Y zR(-IRJQD-cKlrVoA?eXaAN^8K?*7>t(mI0rEXJk_T`CCe> z@S?y5ZqORAQ7Fqm2xC3J&Eex5IjXqukrAbt%*wt7DD@Abf6%Jj$a#}dD4}?@ zmwL;Pb#+RZgxf{5%5^V;TBA%THlqtnLGx-CD?6o~3HbxyA+8n--y_E)rcBG-- zs7n^^bDpHGh~sCIOL@q(zGUTG`v?65$l4(P*0!z`>*S0!?`{LKNh(e^lWLS1#vrXc zbENm%H^V=pfDbYvYclaB8t1_}2y}d_;IAc_jDI zf!@25GPQ-djv{Zz#v!?}`cdds58v?n886@b=@lUMX)l3$C6NC0 z=U3j|{fD2t((#%<9q*j%PDrH-&3H>6Y&T85KAAqHce(d(|Kg?Hi*M{Fm+lqp{^~(W z;Q74$|Jg?$iB61$OCMw(s{c2g4>$eZV{~J6Xf8)LZ@M&kR2@HE+wTthzU8&9I)O8f z3ygmQhR)wWIQNW!36Q4jjE%7`qqFJuz-=HMyAc~OKGE1y+oqYu@xulNpAJE6rL=4W zdtm&TNEp9l$~Z5~@PuCQN*7yd=myqF5SKE2`EoMrE4m4XKjF*7I~Z_QhQn_0_Bti` z4Qv`5xt+>nIOXx-^S*S*3Yt%RJoYK~**)(=>dJp`6)`x028S{Ys<&%-Y^NXJ6R$Ud zW7*oVsUxf9UyUsdt$c;RQ3qZW563|#FY-q#S=bFozUnCJm*SNLn#cqVBP+D#Hen|f zcJ7fRzqFFkHzFsge5spCBS*QUmVW^V_VkOnPIfYjT)|1nQ>bAtmR`QOiS>wCsV zQnLC-UA&=Xc1bETG?JS~ZropEs=RG=`KqojUc7kn2*7(Djh+Tq{@17bDf{c8Jwzq; zf_ngsosRu+5ry&VVpj3{o$g9lmTT# zQ8Ac|24ydZa-{egs3hOHSoAo9&^d!asRd|p8njjZt&{v*H!rv;Q#dG}(jjkCgcOxQ zjUUQ^0+T3kpwB1R6m&BV>QFe6?OayGO#)bLui_8VsLWAHDJRHZ#sTl)xPNU6MBB*1 zEcYm()(+ghT@2-Q0`|_9Z*u9xL&{W9#&Aexh~ew(Y2*M7 z-U*OA$*2lBlOT9!>;`6JR;8KXUw+Dl!OF7RvbJ+|X98&OEMsj2TwCI|b%W7P4RmOe zF)*BlX$MA1dze%b(0jy!p~HWby}ppT=khE&-M$n*xhtn`Ct`Bml!(+qyUG*8*lNDx55@CIn}@ghNL|k_y_hF-SmRMR@Vdv zx&pdKyD2zCWLxK{u%>Objo;!A4aSHuV4@))Ph<>64Px?Uk_at9lC?Lro z1oZQN`e#4hegBm=svo8w8k6aDaX|IGj;HC?t9y5R{K=1hzWdkTf9YO;`>VYK9-svD z$8UW8$@b6DmHO#vy~(DrE9^#oe2Mh&nQR`ysNNN9^)Z1>7lXI-zH_>@E7_ZUYoKLZ zM8~shbh|QR+o|T*!|2|iKhgR8n882r*ip8-J`ot3y@)9~(c8t7k;jIhZ|U;t`v8!S zK55a(W}yrH<3nup1Tb7+j6EH{ul3NzzEoC%$-oGE_+T&DTZwI*x{mG(?&CAWCd!xS zh~@_mTcmFN$=qkpl;Ss-3<2LS9Jd|NoQ#~OZs77`2ClqW99eR*%R}I)9Q~vn0ryhi z`c2>?pM{gmB~n}pmW?XD>=ZMCj(O*M2pT~)NQhk)bllX%z+^hLx zoU}Y51JiggPENy^yx=3_{Oo#^;_r0=Anij0_oUz-q&m_*JrD`(1^X$BHOjj(J=J;N zi{Z4)4>JSyWpjv>jV(C_2c=Y{sb&T=h39^aRqp4117=E+@?VLKa*N zG-h(38Bwy7tKfCUIfbAq@?tCiO@`yjcMlH6mJt&;&lYP11Fs5bDtcMl*(k3uIuy+W zUf@v<-UrZCQh|SzN|jOY+WvntutNAT2q%(^oY6qkVV`u06mdI%mk zyZXJ+3!fLejpCa!W+IpItl|$`LBHUYEc@s|>ie`{gYM8y24K%|jPJ>Iuo|}*hzJ$D z?ubZvvi1|`H(-zAKEF-!)Sw0~m}G{}sL9T}niv^cru{3&?HMcd)ffb>q?}(h|(S3Y|glMFb8&XT(z&3~fqgWCUD!OHKppG~l(%bx5z==N&S0&{4wEnX zR-KTRPMwfPF3S+XU3sL>gQ-0ft-;1i^4ws|*7JgBQK#b}^(!;2nz92cG@0-qyY$ia z>D+|Vm{Gq5N`)l6uW^K4smI*Edi822>L+((g}`8MJ+Mq3G%s+#H>Q>o*D*8JJY{C$ z2mjg;t?+^YpQC#@B!Ak`n3BN5lY9=8w!9kNzWtZ4_5S|PUIHH`fp@Z+_J9Asf4=*- zKYn@l?zL;vw^O+NuVYRB(%_xXDXeA*ItH1DVQ z`g2dWKhlSMjAgIsX|_^7l{~UST{l3F9qJCVT(>W$2pvgh>gQ7R?sh&lfzBO&q_%kr zRMlfEe2s=)pPg%|pFHCOT@o9wY$wxl?dX1OORmQU8yw5t(c|j((MR^Ewg3zeCxKZU zK?9X*U+nQ{1D`=2`lsB~0k=VUj>WObs1w+!zx=c1GlI|7q8E>V4u5bDpYF|%+-KE% z(N@nQb?URkMrj900FFNXYT?QYb8E7pEB9%`nKVfKSWA59at20Q ztoB*?os2?1nu3IHhE6!z-nposn7kz){cPYMK^y0$`jM@^HP_8Ts3L)fKF9f~86o6) z{idh__%U{+jL&D4?8V(br>{?+-z{_hLr@X=pxy9?nVJ~M*$={J|ahjzXX0C`gO z^j4R%XOEt{bNk)x`4}sXV?+$*&bCD%jRK`e3_?n7d#Q};4ycT}48$4%$}s0<+zdW% zX*Y1`A2XlI)aWRbBp_VdmMJ_lum^ebiE>d9ffYDW{*+>sKxYSs#u^1mA%nS!J%OeG zcLLpQc02TXYipHkU<-~WU!C90LZK^3P%y;x+$>b<&W)reFJQG z;v`UXw-}A!pv1dXI_F@M*%*_}xYam?yEq54>dhzzrE=vv@V9>x7#{2{P{b+Zt+%DW zcqq#7LGeernf7u%wKMsu-)_#M8MLS~6WP8d8=NmQn4C5tFBlZYD@! zK(w6+1+>&yYZtY2`j$0G7Q>M`;B`_abb@)|aCVpIH?vCM`TwbJA|qd&Z5>Svrpc=b z*f|?y%GhZ;a>~w#w5{DL#t&y=`VqRkBrw5vo-xfxYyWTEO$PeO&>#9XWE05G_Pf5E zC}p%OiFhv2^V&fk*(W1MF!Y=1fG{+2CjyS1x(B;&{rHvL8}IJl&K`;F)31@hyIE=c zXW#ze?)xvkw)=yxT;6^0(xba4E*^CUk^bhBfv^Aao!wu&`o``%FJ9SQdE=e*=6%{r z;Iov#!S45-eQNjAV;4Gce;EC2hhGDZkU@ub(IH3rog>6g8N0_GrcC{ls?h4s`WCT^ z%IVMSQTug`3O|X(p@Zpub|dyE6USYpWM@;JuI-{sYyeu=#+w!_Q$CuSa&BF89LCY| zj}@tXNL@aGU1=uA`AG_UQ$PJ~r)OXO7Z@qpMGd&wGxhMyw{dqJB-n6U*hS-l` zDl{R3O!7Z~*bA~?m+&-lL1%1}!f@qHJ2vF8$7)$*z6*>}4) zy@*$yx@?n)dU98fk2n)0TQFarEBwa~oXLp5jJ(Mi4hsqb+fWjS_yCzb9{J!0-(3ju z^`~6(Mev-{=9lZhnTdhi*TT7fAn3*yzbeT~cX5yIHlQ-~=TalI0O=mz3d~%D{|TAA z91ur(m9>xIKV^Cb99+2sGY8o{I&VVP@J41m>z^sV?&*s`JE<}c)?8a$0aTSrD}@g8 z^{jYcBNH_W7k@5R+aXF{ArnQWuXqQ93~fU6ZL`R!R95zB+gufE{^(P9?AQyan|HLQ z{-_VNRDJNaaj0B*r8s5Q!czLjVFnYJcXFx~6QEH)IK9DJVXLAgy03(;>H2A`SPa_H&t z0XiG_)d*3n$xj^ybv*__z18RkR0Gk_+Q&Nr%MOd%F@T;AX8;?p2LRbrGlo_HZWVm{ z(-@Mjc9U6*wl-DIi}fh-%B6-mu){;Q)m_hEJ#YJLOX@FvsANbAG9> z1S1*{g{cj-izYsbQ)E)bXowpypTY}2$DzqY0?Ft*4BmWuS)U+dSQ)ic%)Rpr7nsdQ zJI6<`Y>eFQM25E)n_O6qhW6`r!0>V5>`^{<{#cmAz6v?&8X!@PLXWL^VGd$Ry29qu1oZU-)%JHk} zZ|xF5#&=+2>tp38=%xSHg!%Ybg5l`=ly$GZ1R6sO+kY6J&seN1a#Ut4JRgEjQtP{g zeF#blG3>fPyuJat1J93K)Enc%M__dWE4sak$Xh3gLa)z%6fXrk!aNq8S)V6hfL9z&4t8anZvT%@H>eyAomxaGHdfi@C;9u6-&_Jb)P4Fj z5_sW-7e0xcPS-ts4r@l{ozEK_26Bf<1}7zCu*sJ^>(*J@E){|34lP}k$IbyhP2 zw>l%oL(}F-1}Q`7C4O|2!Rc+D%H8aX{RA5dQYbkFo|ms!(WijmWFXw9Q_3*haH^-K?MJf<0qB9X40V;XtR(RJxxO507y{oc z{utpHvIM4ZGIWef^jwWQxxtAJEA(y1tuejbmUPiKhHxAm*QFuwybX99iPW9qw6K&^C-ngZ=O~iaPRf-uh{ulIPe{>$s$Yf$JWl(=x%K zuD)R6LEe}xvedTo7yz6BkD~HdgY7cH!H;{w+G!>E!7F>K10&of1ju9BCdMiMn zWD}5`h=S`+e)f94@t(1~PkRac1`_aCz>BZHwfl>_8^HSke)i_u=}#uwGH!IVecDUl zvy?#k+!K$S-~H~hPd3=D{}a6_v19!vI=!81*?l>(-C53K`{`xpg4o(W`jp;HUB{Ui zh`x-v?asPFj#Y`WGcizcTO(bldT!{EQ_& z6B?G^13r8cjHOWMc7J#XtuYoj?QXB&T|txk#S{Mvt?Y5fE3`q@U6`fwt~?fR;}4`v zXcbJuiFh!b3gEzNxyTUykx5|Hr{J>*CFPSpOOE!Djnn)ed4lgEf8eXX6#ACDI;oQD zR4onxA6b+i<-&vC+V<{IzJbkFhsF?4-zG8uQ{c+~!Uy`nMTj~k{Cg+YZp^>jPF=pn zWXn|r?wsYnHrD1+x6$JzAnYE1ligR>(E>@>%yr-tIRMQFJm9u}DYveAEUJ-%dg~v$ zbE|*RKVQ1agwEz`@$u>-ryyTo{N=50gh>C$m@L4O>Ywn*(cIklEa6e&cVb39yvCCw zxjMmP`{xD>K4mMf(9H+d8|upt%9347@q3sW0C^?l+)g*aV`{q*W>bo|36Yks6=|Ek%-_2?O=y zb<1_~4ECKCeR>8#O&^kvUIBLL37_3U2vK171&N`l{R=MtQrGHFQ{zAaFP7 zE77SI6KY9fm%2(RvpN38P6j4@-xsw-mv-V+@SH?fo4CJCKW{do`ZTu%;8r>7)1;jG zx#%aDYm3#7zAv2C-E870^V-+i=@dY7Cn!=59QL%9Aa)Ua1^_L5xPB%r<;T9xd!Ck! zsZUUZ>hJ;3HIoqT^)Uf7<-9>x;7EO*JGcToaBHDDu?=<6=U(lUBYTvion{k2rhE#q zS9n0H`+TbUAhmg#Ci&$f2y?95S}*Mq|4!cc0_@!;j~x4HxeY#IEN0bL3J-Tei=A>| zv-ruA4QwzOzQJpo<1zH1GbP*RLD&1T@D}kBOMQc0=e!wn%kA3lcy#5{5iYwc3p0T1 zKEy{ST)+$feT&t<5nhTTG?K#rC*MF010@Y@e!1APrvZAC@1x6LC}Xl@#z|-oJ^T_$%a6a5m?7}$mz%IieRc7o-IW<%2D|!v`q|cI zXeW0J;9EcApUOiW-^-)|UMyJPeI_XIlf3-=({~!@q)yuP%rnnC^As9Sr6GDU4g>G> z^{Ha}(@!dayCoVw9c%I0`hSQp2_xTfv+Q zJgbNSkd-_@y-<(xq+$ejjDb($8PIRP{Z5tent|&Z(lcm%Be%2IfSf3SnHUh@fNQ{e zgS;IFH9(=Q!EcmB2n}4%R0nHt`qiMN9@sS?DP!O(<3~A-0!f+B*je?IKkT#Qg3er2 zi9()Vl6uY0^)l2$OJGD9GS;477u?>6fh8X^=9HL6EAU|ZIHhR^gSvR4h~zP7=q?{* zAElKdC&q=wr?BMF^AcTroRfS(;tTo1ybaedNu0Fn3o9Z zhihn>oG{T7IKj>7)L-go)F4mWcC4bE6B&8f!q+y;%ZcV)9&tLe)f%qUo-!c)7xwPO z+mhqD@B767m;peLAVrA+MOluP9Xo#}|GTV}ILmfoXXW8!$&O`NmMBvaNni#bkniVL zr!NK|z=ufL%fr+6p3~j+*tPers{N|!KG9&l*ky3>7L6bEaz=TFKS$YuJ6=@Jp{;-R zCw~!b=YEOzuR90s9Jq7fzhMp#_y6>Jzn6SYK6brFu+mf4V{GyhgwsbZ=?Ze215a^r zQWK{;=u+a6K03Ocp5ow~x6hNg9($wv=^8S1;x!m-CoAuqYcRDN6DFZ(TC|c=Fckr9- zqpi~I3}xrAfl$RB-E;2v6OgsDvRy0Rc+lC}-6M&$dFVNf4xjQB*rHb~)85jn1CNkB z_Z2&kYWuYrKHBM+tm3 z$0~b`d-IU}eC=p>zhgx7_0mtIU&h$7zT_v>uPXQRufc)OG5ObkbFXTKfA#UjJois* zeMyezMK{rlIQwv12svpcvzs{wIgZO{5)w2M_Dx0;op7}B+o?dni<~7t`(~?pFRy#n z&#EP4Im9NMm+qO|gs*yJDFOS|ThAs$1HF}b>HRN=a0y|`5q=``AN4kU0^x%XdrNxd z2+QZaVR}DR7HrPf1hTx^rzkE*Yg@LGY7_jTn@PNd9BT9^?U+;Am{d8>+hK_iyt0%B zp$I;DJx@f4i*VD_R`%gfBu+?0r|0wWkHC6faKuYo0JRD*C5F$GzT}&rB+!nKrBEou z@WB5LE4X+iEp(QS4@V4EZ-A7$Im_W>`=CCtQ-i^92}kuHGuY6qjrD1`&Kd0ftT+RD zR7)t%IKay(>duth-Vu=Sbr9}eu15`SC1t5#)J7+LNhrhFaUT8fg1e`+pW?}JCall4 z!((_@MKNBctiyH6q;?iHhEwon08r#KS!LE|G)V`ZUI!!QQ5lskntUAkej+AV>zfn@ ze(c4Dolk(LT`%?3U%z4XSlleYZUmy5Y@gBu8+?yhJ!C$ zXIMb~cKnCG{qW)c{)_wXvctc!x&Qz`07*naRA2nKpYI&FbKt*i4v@3oNOynt>(3&D zGVO7$xP|l=}c#G9~{@tbQ*rB zN3UOROV^$>pT`x{Djb?m)^YH_t9}O?Y_P+5bflMnpby$7-;U&^yF4u`5=+GDw(pj8?g*jVV4HhMzj(0=;S zLR0_4|Fvu=2R0~k__w5k!yt|`r)>p27r6?-h<4-C;?W2m|MYvsN}J6)Z1kVNllH<# zfxz_L*b3iuCmVxRd~iAMLgJtsxA_<4TyseCr@a-v^@VJ2~Ea@4Y|jWI&!lDe)_g`27pZd_~oJ@|SSn&de`i$}dRzrFnK6 ztS^^;`8ucjt#7<|{yb5~5lS)SAee+E@}76zEW88>teQ!s9pyv`=HfN8x3XQj$;Nq8 ze-3z3Xi@4WGbfj`$myA*IDt#R2pklN<)~<)FM^FwG?~u?KEj0{d8@beQ>f3Lz73Tr zjN=?vhj2(b90en~>yZSG*>Pk~R3Fq6*>|<*AjheC`y?ffP(8v++7PMi!jXhj8;o8{ zS)G+xK@TgnaHO!$wtUq*Hz>#;-KxLf2m1G-z0$Tij@Izm&WU)$NnAjn)ENyK1lqTX z9A_rPs#zcO9)YLPl|Vf^9?tf20@%(2hko$(dVH&~QWBK%ZvAofBOWs>@YPv;f9tzy zfi}D|JocvF@4OXZ=^8`X5v}rx_Km6dW=z2RD7T#S@X(I3c>8g|8p`&J z!+29W3=VZDi z`kTUPPQl?AN;jTW*0?br@CmNka-i+-=gbe|Svg2=ZYhISF#Ofl45!tHYcLt4EQ+xF zAOGJ!+t};oe%v{5=fIr)lFxSvpYJm6{c^~NvG`5^K|uOusZ1{Fr?9!+Bmual=NMChW^lQKuYN^ z*z_));rf#QWI4SmcnqfValViKl~1;_$sF3*v)5f8+l>Q7|52eVmGo7(3qq{@%4yHB za-HGkpsRhf;lMp#beap*4lYM;$);NmzYgB`?tV_V{6NET^heip9Rz}V_;eih*`<{$ zF*{I@;pjF+(JC0ura>czadySv<9*dS+0ltOdB?sxc~|>*tdF+MJOpZDx*Q%5(vSN1 zw0&$Bd5`u{`O1uM>nFBjb>gzKdV|xqYrK$x=8Nvr#>uDKuZ4#jOH>cu)j?J6z)$&C z@-D;kq<5#gI!V{g?2YWu*{S#e$7tt_Uv;3WZTvv9Ady%N7!Lj6j&1Ik4OKb3!(WSY z7B$xRt)Bh?AAfu{R_jx^gD*|~+N@@!W(S!Av#Au6wtkQX9xU|d>Hi8meC=yr`=0Yl z#~H9MCAh4;r8oAzEdR^Wz5wBV`$Z1inf67F{D$toWXsBU{n8efhxfkqt@jf~uW=Ae z9?!&^?54;iV0La|b3jdSE954g2`i6tO<0MDpUFrP^elRg-+m=v4&M>*&r^Qt5?XWG zo6IKf5q6(VNG#Z7uAGSY(;R1xjpw2X3}XbB_`09MnGiYYlm(&9VfR+k zN%vYfb&kVsdmZ6GEfH)t$Txvu@*lyh#PyOMco#sa1|cQNO&A?G&R6stTu!y;+X)_y z=ZSj8=_8R+>bL0k1o2VEgcxN@*a{$^TN)fF5g$L>2TAT5#fER5@}QhOy5Z;I(Cl#s ze9#=@rDDVR^oMfCQAc-0b}U#>QEi@I7vO~2P4Vf&htbV3JgfEz*ZAkfbl{2#qfh@Z zR`8UwJAMRQ*`q`|8xJ>{p5L~hZw!W`@e^S)Q1C!NXY`i_?;XJ#jW?r0DqL-iJb2sT zaf%vu3nau-#{vVvqG(QF;^6j1{QGIITizJdyWygrjF*kS>V|pn&%8mjF|geUwQ*pd zpI!3Ei(ju05W6^Qi{JXEvvz|%SjGof8%sCiX7{D(TkY#-{rHG)Jf2e@f8_g}U}J_+ zbBw$5vyq#R!N7yVZ#*jJ#tHC?QoYkl=Xs)fC4 z7LMR>RQb_y+G00MKXSl3W-FdT!94J1>qOht_Hoc@tVqUOXLQEo*>hbhd-a1pIyM+9 z?&!$la36XP?rbIc5ghHFF<@M_oNrb?j#Cv%jXZ+at=x zzyo~Wb#*PWTw`r?+}hVBdusN|ZT!LW(GG@beb2sK{cwPH2>B0+2@ingmH}H2Y1VG!f<6VJw zqe&Up5Sc$x<{4}1o^tFfeAWk>Ctd6XmGv9GSQOJo-qYoH=bd+ce;0jy;SuTk^ZyrP z{+`aK*Z!V>|2Becfd4W)U72^k@r~~hbM1Q!_Kok`9~d%8BXDQ(IsDA(%02`1lXZ#34K{nUSu14Q&xL^s&MJqP6ZZ_NAQ6|z*FK5G+#^%x8@fi#iy>@0$ECL01Qcn%$qPX$O$_4ly-OyKW zKYLccoNeg!#Oh7)bWK6dm-YYgdAIBQe)J_~nk4&__hd)xhuPPiC16yJWc zI2`cg7(B;Vg11kNVjLzd`g}RS6|d{cqh$M_VfBsap2fIUJn;F=+QMHd0)K~h0N{b% zgDLnv@Hht0^BiyO9%sICb)xO{fGD%^Rz6yl`AA;vEtnK+%Kxntt+w$wYirNC=`Z|) zuK<<J4#Ev@_cFib5B>4udfpTd;lEuM)%R13kF?gV(a(Un_%S{P-+cMW z^A8{Xmw)@0lJ3Wy19uMGIq*twfV%kRyKg`I;kVvRu6|}`(2bmLdYZg-5Pd$RHQhoM z&PI`%yeGqtd}JY{AIN$(6ak%dr3VD;wIlCpj|>(lo}Exhf#6S4yi^t^$2)|S;Y@08 zZ3g!|vPm}tPaD)(f6`qE$5_&Oe2&V8sRO;h;?DbbaXa3qN3<05{#@`skpu zJ1`KHmG-0shONeygiB~(U(jKAp*2n_T3q9i_N!W8{LqCS_wbOX-WHVT=k5jV=hROR zSFW-P?z1Bbt}D+**e?#0#yDGAMEM0B0h8IX>PIA7VYWoDLw9_deH38$`%})fU++QR zXoTmD3gs)dHU&M_Ctz7Ph{rzK21CCaLv$-1gE@OQ+hZ>!){b#jCfj>yQI1>M1KW5r zF7&Nn{@@04c47HDkKQ1J$2ktScU{_OL*H#YNo{PJeJ*3;P)Bws$HuvM zgGMy0FO|i^%52=k*E#y9W_DowR2Q87MrWssraADX;~C!0-l@E@X8|z#E!&4JY))J> z7_WFIXmi2$v&QV1lLjkeL*H4DJN8ro4gr8C&9OuB@cfxCllXr8hBTi&tISJ!u zlkltf`f~b{H=jQJ9>;gPo_+MN21Um3BBGwiG ztR&fvXMTF|(4ajW>8R|f7aeXZ>x|~{2&{0~%W){q?imAcAW$89yr42$?Jo@vmGILr z*FiXY;mz8l1y0qI-8BUWTkj|MsIvR1!*V(H#ueV3$`xMSqW}qaJ0U3WDURA-Af@wD zt1@`d*wrq@p{lpSQmmt8earAV{8v`6LP6xcIb-5D=@bp89gpXbtBbi6oIba3_M zTi(eKpjsP`OCHZCJF6Au6=kVy7*n_LS zbPnI)!4gXg)Jd!29NhBZ51#t_RA@MLrhh_vZ660Se3xDcutx)!hj;9|^CHCH<8=@P zEF54SyWl)_K^NoE)n_YP6@i0e_Z_^Z`zwp9!xMgVX)x)?vs*O$OR%s2uPvNZ27erE znAuDv;InZfC?FpnK5Rh+4eTqNh$Z26U`j(foE)xKuC~#?0Fk^t>)+1$P=MdU#3!Bi zqCA!H+WGd4W`Azi@3%aq;Cp(-wG3^ri_Q+*x@9~){g`bX9e9q`jR*HtkiWjG{LbbK zTmw2A_duS_QS$QG)a)(g9mes>mXEh^W;?|~S1ZTHGbd*2#27Y}a_rCbSLM}3mAddX zuC=9|EgIEU{qwtP^z&cHy^fc!ue6uXUzz{??|=U*^Iit<+j@Ox&fmS~e-++6mVJ4P z%d@xMdjG@cR)|F^n;cCLPK_n+#IK&x;gTvj42n0 zgoB1R$-6c=KTJ5u1Y@hH#L$Gp5tIb<*?vV>a0Cf-6M8d$0(g#5x04k$Kmd4CC}+p^ zJVIj6vri##0L#A#2$ej>;2HMU2#oHJu&h#BvTknc=@mo(HiT1{umKq;uIh7fRk;NmxJwaPe~Kw0xXnSIp;LsglOQy03TpNxt`1^y6&eK!gDay#WRYl zYl5FJ)knsU`cn$!PoRf`F|nU#Dc8039Gri_i8F?daHmiyS*rv3Oxcae>QM;C2q?XO zN~Nw}`$pHc{=@HppL47|G%^j~shoNoV&mM{nGsPLeX;z28*ht5>k#$37xaLOM=A^p z{fLi`4tKE5$S+O1wdqs!@s2^bM`lVy%ky>t>?aD#-!uIcr06L>W86*%wG9b)&L99c z8Y$%xYX8s!2Yj2Ni?7<$o;ep~!S{&AEg&Mb0&#h{=GmLQyTh&o_*10%3yyI3+Yg>U z{PTbRg8+hcKkgj3bKuT_SAzqb$A9vB?~NFec1~M&sjUPzT<=m?S!!jRN|hNzShR6Iww)~u*`Jd zHE@Dug8Qu6??NKV(xVuD>eBSs+z#`6)~mAk^e54TAUW8#aYo7bima*4x~M|$Wg4VJjr0DCfi$+9Apij566xJ?<^rZ}AD*hS;E5KARP$!8ln38|r8*qZq3 z@oyJh*~kd_lTdd5(AJhCsTH(@g5GmCn~SqsRt;a>RX4VFQnq;32+f%v-@NtGm5Zne zPg-(t_dB$o9+lDQsq+0*|07oXJzssx-I-645lC`wVi`H4%@87z|09Ti1vhZXCba8u z?laGD{pSTP|9sj^(l@7+bgN`);SsqiH!pDT82`W?$P%hg3w{$s>_lp~)7}2WVInQ0 ztCR;nXxZF_eUmq8BN*iu42Ua?Q{H=wLl*pkJJ!UVhYhZe{&=}Y8+zqt+Dst%>?4hy zc`H?sTcEKe`=(cC;#Z&&o&FVQ2W?_aZQTIDCDV1%{A)~gOLw@TGEhuVfx^+yIO@c; z!6o!f!3Sw-O>eOPR?0oT(1-XlB?JI&1`lxHrC%tE3L}TDzU-ScI*Crg@ zEW8^dKmXqHMlH^G?~V{k2oXa+#q2&NKxUhplZa2j+!Ss1e&{2^bySVtU=^P|IMCYe zK{!gd55K#CJw~y+(f!Nfr#z^y*Fe$Tjbq)FhEUevoRt@7@ogmg+nqkCx_V`*MV$*KqL40UVmrF zK@bSc5X`H-iTB%jE*Ee)N^a@#RO+VV)pE0qeUdRl$;7+uV!|#&1mGLMAGG+l=lX&&Zy#wo*wa=*pq+`I^r6f-m|@+i!MeR-WIz zNj?U%nypkH)29y^vF)45j5D;7_-Ra=nuAEkDmkp!cg<04cyosFZfogF3l)exo^>78 zkQ&)|%De=e?|1f?o3Se`9jTa<5ShV>mP@1AS=lXe#Ix>hx-m8h(5OflupB@yUkcc4{0Rsd zcO*|mOdp9`%to+_jd?JZL!JQ|f%14QIU0;|B2lhY_?3}nQbSb{6~`V6Og<|miqSPb zF$GE+qw{V;#LAC;3MjKY9ORC#+Foj?;j42R8%o^AnAp;jK;NAlAN{$;tax9DF}7IW z?Eo7kP8iX9jILZ)8aF|*cN;F{es^<+)IICwC+}bMv})T`P;U`r#Qyv8vsv$PU`A`~ z%WpJ;CPuo$x{u@OK}EDOx^3!HQ80Vaccyx0GBJE^zJ@+9V?2T<^~U<*s-ka9G8>F? zE%3UgB;4lmv}R_Nh61wljZ{_@+K~n)G)X4PE8jZ)J)mI%nyiMC_PU7pp&3 zhf?viA_$}Ie{zHyU!0GAVBM^x6(X*ts*^J5pIe*FXYpqzZ_*FGFZ1)pKogGBTh&Fg@Lmmjvu%Xd>ECJv~+-v~9@ed0?+z;dbNn9K%~Y zt*rtZ-yY@xty~o1t-)6h-|qen!5Yu;hdllAq6lcUpQ~MA%gIDwYo!4yM?oRG3RKz< z`sT_O#CdA=81$V(aOlfKD)?YDl;=_zxA{d2b4cO$pIR%jj=aD$XULWPC$G(agQUQ4 zdSy|V(Ba|l(Bm=0-8siCOV_pMe^Oh2K+6*#r}W*{edvIm@)1Xd;}29kZ{REDpKe~( zPcAf%cl^LwDdBzvPs5*5B>u=QoBbg;Y+aO1cZyxn%jY2KN_T0T-$z+{_*-Rom6iy+ zKk?h4M%i335>J+`{(?Hf(lX&S`(_M)?!oy__|O4a%tgo7lT)fDc;!csY|Yk&<3xAp zN-y^yY?q$WK5QxsUzsae!9pKd%fVdo_f`lt z2V!R4Ykp_4W#Rk!og!a`Q;VKL*F~6o z-!wP;lRK@b=JpX(zkUbHgxO z(%@-oN$#+7h&9}bwpB;?MnI3KX)6`23tNW%Waw_Ph_#08CM`|m3=_BZ2HHw8=31-Q ztVTxdg0CbG&wIE=Mm<0UBT|Z9Jo@P2FUH^cBC2 zytkf~dZ#m>m(Mf$I@Ld!fy+ON(~g-$MC9(RGNnxP0#9+7;;;(NOR&kP$7pGDuj9q$ zQ8dsa-hm=#i=+*gQV2HCNNU&1^Qwto=UE^bG+&XpH4wQS$C#pe0p$pmiS`y0>xcg3 zt^CIW2%$iRRZxJbEJh z5>9kvVsy6e`b(7!4o9wvf#@>5nrN#FBg~i9*f*mPvh8xb)X5R&RZG-yMzxJT1pqTJrq9O zqC}K|AdW?fADUX*;i)V`abj19e4k6~XJzbw6%IJA;K~0JDs_;pDF@v7=WX_R!*WZIl#}&M+5^h1ZzV{jD<*xrpH_EbTMAS)BbyBDUUU9utDxe5DIaiMw}@DlFh0UhSw zx>8;A>!L2upr%0VSE~eF`5ES8Q7k!Mvu()K89V)}-^%1tsg_SYyq&Ir=U@k}m$8Jk~m_ zCrP%7eteUD_ql_Zit+XK<(CY@1ONx>Kn=3muv0K5-hw&2Aa9a0G?9P5K_3Fg34OjR1=n6dhcf+@-iIwRz?F)1FQ{#V9* zz^Ld)_iQu46)>HysP86k-_2y_V$Z^fj)1pgTRG_m4vHz8MA9LiGYeFaDe526I%5ll z-hQrcR@^N=&AOgGKvTvC8d({`R!ODt>>-6xGNxU5A4Q_W`Na93H#0jp*9akg`QseO z{epSPidi%-eR7dT?8?Hk3fTdw>0ik`KvRMf0|!mk85`XmFOm=@V@k3Mp$88Rq~q!# zS)-UEVh)NhNx_%6kAdfY*qz--_||^4*-Q3e&teirUlAj70?uwjBlE@01^vX=`=exh zD}6G#uv6u>Of}Xzm>xtedL4zZm{pbDe{S+cQrn|CchR+zE3y zzTM<0hstcAE#Zn~L`K{i_zblKSPmLyFqq1OLh z16iQM&7h~$K5b&DP~E`8~<@_`Y8`dg>rlUe(xz4Rz`Mf%>a zHFS5`z@czs9d?^f*0J^92QYxR?PN)wYWccnzHhXqNnS8DtMciOP&s8dlC)5tEft_p zeJi7w>iUw!H2(G-mF4+h;#{q@DShTr>7Z|=ax&Khf2n-Z7vL^_L)Re;v zp5f>e=OZF?Rm8rnAY)|YmOqnBdu>~}m``AaAryapeOuCXXW)_x&IQtvKg zdLe=W_qMnda;ocT7M`+fJ=BvSh___OJkst2Dr((@1x`pftRKEe) z>}*p{EO&|h{<-$Evg7T7rj;RlyZq;b76dw{8ab1S=Z_>;xL>_)uK=*Q6+=@wq(w_| ztZh8^=6K8QkRxQ_v9rUkE@llnQ>3%FG5w%LhvpCR^Y3~OJi!DPk0{vM!g}&kF_JJn zHU7(p3)9h^)5QAH1nc{bnMFYfD`BzQiFi!cSzfuxyb5y|UOmeHiO8XH3Xx`yH; zkC0Js9o@{E_Azy~?{)ZtFCGQrMc|o0L3|d#3Ppf;gz8s*8C@SFZzJhXMWo`2$4*W5d8U;Ur>MVZ&qr-o?G{r z&bn^wYt!U(PC6iS*@G@-{6Tllo2PtPc9u3NUr4|Pb)lFN)^C!GQ-j{%WMtv4q11l3`%1EP5X}$RKh!oy#@eP5G`9~#baTk1NQ9va^tWPt zIIyY_er38hv&u?iAXiy=eqjl0*j}NXuAsKPOP1xQ1gTC(d)g&ONLX0$q8g$qw_prj zOoieKfnSf8*54E&l5}51mvT%RtDx*Z^Ug9N9GWKdVu}&(Hc}`(Z{B2Nu0(}i?W`bu zEmVmEK;y+qnBbOZ{cdE2P}RGQ$Tjb+=MEPQ^t*GWMCHfmue#?f8TqHXig>5WjidDK z*2~LZSpwBOANp*Vyb86eYj(Y`Wb)78V2vd3thxmj zb9|lR?>F>^#(*IME&JmAi^Eb#m#&sX_;H_QDib>GOu#09`F*3{kd zA6iI_Y!3drp~QzIjBWuHf^nx4d$m8B{KiXE45jXYP>`!ceHJL#+++ArK@kCnxpM{p zzyy=Xu>;ON$s$MjKVV&9g@s(B~Y+k@@)46~v!~`AA>hIHe4DNxvR4*`_XO@$7Q(WcB;5KQ|w*Bp?Dhp0<$YA?CeMdRQLk)H0#JK!Ko{M zq#ijbYRes5CtKVog+?|r-Fz5j?nSRri(hx6P2_&$!28He4tO2PXK`*JUAJjU#u6_}4gb1LWTfn5hi$Grw{Se<}36 z1k>wP8ax~RPQO4|IyvLT$67E;c zckzqtiv`Tob({f(j}H!q24H!O+d=iWird-r_LmVZT2k8-xScPN)>4 z|D}g@-%?75t?zkw2J)J~c?S2M=MpF#22s_w)qdroa!(!V2l(iYh=MqPpP9w;BO7Qe z@9B?{(nmKlpG=h@Ay$~6J*2IeWT>Wjnn?m%;k(o5S;+OZZ+Qjc=ZHW_R5gEjQOrQT zZ5ii6h$Y8GNYBU~AYeM58DqL!a<2JVwD~8RdHP>CY(gLZNoB(g$X*PdO3C}m%e7VL zwVN%gn1{G~EL2Hy2RZj-$^t*LQ2M5JJ9G^c(C?cNKRm66r(8G45Bk5jQKV(?dEw7B zxkJ7dzvWA;1?EZntMl)E%2*&En3*>f|zjxQ7NWhy}}wUg7bt zv}DhMhOcOyGML2Q4Tk!^;7is!P05i5Og~jk=#cO1b$y5;^+}rJ2|CJSbbpX;|K1w* zOL5@QuUNe;w#~y1UJE_~-D4Buces37=Ol}?V@9SJSwql^#dqh++gE5o4itE(CEm>D-p5JeeNV`PJy zbS5^#?(x$ac?}U`ehHmU9~avq*K~uGe=R2D`3Slo6oVL zupnIA8rXbY^AP{=vr*jeP*HWf=jjCWZhQH513fl~y+`SXBjky#cmellL;E?p)qlAy z(P#bfSQ-eXz;nHAf1JAbFr@iXX0&fy%jrdGvKWZdSkchr!PI$RwOnF5Lc21c-pGz2 z{9w;Uxb?euR;wy;T}dB%UIL~B(0EFcsUiQJqG5RM^&u+Yg8~~t*k`;6s)l3q4>nph z&S5@svJi0jL8NZBd-v1kb=Op-rg!Ah+gfOk4=XdL>#)X^M7>Yn*GQJo6sg;Tr_^i+ z21F|Vw$=1wXAZgXv+f`_&;s3eVuu8UypUv*n;HCr(h`b}N^_0ie_ay^b4txTga}Jp z6f1VUQV5#ycdbfsOTF>e@77jUt_7l2vbBy&1bbHpBU7WRf|2V%RuF z_x-z-55{B*JMm%-^{6mHiqQ*A+u^jo7xU2rRzKbR`XGY1_;)$4;|GsxS3vCb4ZrQg zPS9&YA1t=1nx7o`4NC{&5pqKX_yt z4w@B&_fiVKk}!(N2OR99Eor|IL~RMen_{QyS^G7XsBuW+_%@bM&Mqmj8Nm-9kP?^H z(g8fLYUzVVIwQ5OM5@gL-b2u{s-jU$3mvbf)Y}QFHZ$GY|EWi~us&G$rj@TctM43H z*{Gg38T5?&7Ntj$IVGf)_kd@Gg@b^**5X&n*AcFb=Z3fSrfY2ckI4A$4VRNv1mAG* zMpVHV6r&@0@x)VlDe`0my7WdaCK}&tMKz5bYP@(oHV z#;H>48d0RA`qROFVo4tfk{@_>mD8cFA3q}Cjq|Lv+Go+C00ZNn~sDL6hvaLsX%s=;E-GB=4}hw*UD>9|LZV8kWt!rYPlJ#J}X7NQwlK zJV{mh5pQ40BVEjojS{j{`DXS&MxKtppl<>8q)Dn$nhiEsi0`cSV;?66=Q&0%)Ns@@ zscpjCYviC0=1Aros2k9far3+DZ^k!Q?u`U1{ExTkqK|BwTqJ?l9b`&o^%Gnyz?$f4 zOB4|mF4<>+8tNAxOYW;3rA?7w6h!$L{Y=A0NU-^CMI734vpJ$%=I-}OoUYShiqZEm1l#1j+ZI) z>>J6DK^>tsWJOxFsfX8305p{AZgZT90@uG-`_*q4FNE5c^8 zIC$GtFV}iHyBPc=lMOnRntMhVjH`v?GIGOqIa}UXPo3|bpmOoQ4EI203vT!Nj^W1o zYyC1ydv+0X{&5Msw*0SU@65;A3Dr>rLsT^v=$&*V+3rJfue~f`9Q66T2RV*HtR|{$ zr}53X@zuqtbt1lh%V@HI{gS!I3NkRki)$VrDZSgZpe!_2xqFT8nZQfP(3CS?WTy)9it^2T2UR?6Ov;JY zbLz_nOTK3SeG;Yu{%2WQZcxF2OWQY>~_s*Bwyp`6$GbRmB z_A61SmF15=l|pm%bJhhMo;9t9xPOuYJDP>@K1xxmId{!{ChrP_Etlv8G)^aM<)1pl z6%4A!!2>Di990E_+zt*US%i7n_}A*hlEBpPP=lDa!o5+V~BFP2C+J~{VMSbsrSS~j-CBQQfG>f z+0{2&E7mvH9DsA|?td=N10Z-(2K@-S-F|&O&0j-mxtcVwr(oG3m-bSzNs-iIn8yCP zlKda2>Zy#axo)vE&1p!z_pV76W1~&zB%xJbu-wZKy=``_0;)&}bDo(AoekPPtzm!a zt!bf6jee7rY?3xp=mPsAOnB(14RLd!NPPb1;XHL%{KF^)%*2&N^XAtvE=!7kRxC1U zTJ0%EZ?9N~zry5_P3Ni@t8uGmn@Fhu%JcuWj#muiYl90NSRKuMTZ;Kdik)+N&QYA5 zUgt^lmi{)WdWY3@er3ViM=~{aCjqYSA|hDX`u|+id}2W(sl$#|d+v_wp7Q_sUUcH& z#2Y2_s81os|fsM z;@Y;B@Dc9WGd!)7S$R_qRIV2otmM-o71K927lh|Wh|IkKS5>p@{=8-Ydb#<`mTxuY zN(Cpdfon9}7ppim(PvzNr^`D#BVhNJt~XO@w=A8Rdw|8fG0pFufefQo3$8pqRlg>U zrvNG18b5oL+hg2%b8Gxur07Goyb$ncJ;oikMn=|shtlm^9;h*?}QotRzy!#6pb6UR$-iK+-Q{vfdbIQeLM zFjjP$ubX(uWWwVTwef>-3y{z++J3qlxF*jzhsHyuE9@VvGKuYcs$Y9LB>%AP`0}Vl z%lTezr7&|@*T@EOYj`E3Pgo(V#j>D!&sGO(j6*co^(`F}?B7X^05y^i+e*e`LGuMqbfXlYf zRw8WU1zejeKe&9(2x-4oYw%m?oto?Sw%aSR;a)AR{T$Yx#^`K< z3+m*c|B?XN6J8fjAfEj!k&_4ZH)*=oKj!A}U}hfO%S+&B`0)%naB13uYzz+sRKA3;Aajixq5J4Xj+O@48fLy* zWVHM6A!7eT@;1ZB*5L!w2(kUA_KvtFq>Wn5md__^Tm=~X^&fVs#yRV+vEy;e|3=?3 zj}vF9DTeHCB$(C$mBx_>$C;a*8w8b=!p(^?HbX$BMPuOqEj=&V?}l14pk)irp2w;9 z7n*?%^JN}hrvz~c2a28PbjAGN${E#qvA0{6n6qB+IYM&GXKdfa4QQF;70CBP2|8tY zEH7S@*zyWouf6RZQ}axPwyaPX^|btPp{I{I>3xY3PiI*ns+cg`8pO#;QZcP+-J2c1 zr|?3~`Yc}Q1MzF_^zG+T`KPiI_&klC?AFa&z?Obe!1sOGf{<(0I+gur{GiO?6CD8zxk}tB&de$*1BqfGE(a)zGEPpU5N%~*-N`4vn*9Peico%9n{ycJ;K0g(m4lh3kVh1WKX(Ps~up{IzROBPVPySu#Fd23} z;HlQj$ zw>Q*yw3>Hz)=!aoJaQ$#_sPcBT{#8m`4n)P(tBEb$(>FytR)CwUOUfZ+w3_Z`lq6{7Y)2AY_-xUKNpOIiX2 z^!FuZ{e0`LTLBk&);QWmZ0X%40@{9yF54>G{%@j7=84*0&)L88rb`NP<-kB{WD94W z287MxpKffx1i-U$Z=SUU9+J)O>a$IU=!Q=Cj*Gr2te6^z2WA5F(r_9RYT1BI_fNo`3Vhi0@$lnW?{VKO^ZwBtjmW&5&a${xim-i#Y?H zK4u@2y$4mXPaLisA1TP4tB^}Jc!6>y#t;?WMM#66EmFV=`1vKgt`VUc)7;SkJY|h9 zeNZOrW9*=;uo_*wF;?n*lb-^ZW(4N}ib(2Rt`&s9TQ(P7b6*nA>?+UR{*1|)a0n?E zReJ9IrbDG4ojmsiNlQo?zh7d_;t?`*)V{H{0R9^g^0bBO^;s=d@lmDRaQsYT7A zILE+UFx>tSke5*?-#oe;U&A7HJ(_d*tBQI|cN6nrt#NnXzC2qrc)|L7ZsQexbTLlV z5xV!8+T;}AHrtX`ywb>oV_J{H`zV%C0`r+6K*vbD@p66UQn0r+n(Oa(06)@?=&#KD zAW+fipIPvBo4wF(;v^EMBLn*+J(d{p|D!}E%gaAbcYW_JFQOd`l!&WH-wpCdVc=(} z3H-lX2dKGtWCm&9>q>Pd(s~Js&>qqV0F9I4(ACkw@mlw#HmTb&cAcN&<(7BRAn&sG z%Sg9XA^*IXh!XbLoWHR49on`i9H?*NWQAB^Y1Z@nO_TsXgwP%MNUTqdShZ0KvSA@C zy?G&n;Hr6|P7+P?bj6oIU0dhTyVtt^>jF?)b6Js!pofM>ZmGBCQCY5kGMR!=QB`hl*s{L} zJ;RCro7jd&t4zqRcSPH*ES4lI_7fqN=#I8}ZPKCAzVE*tIl^UIBoQ2{ojF%!Zq| zv64ka>bZe|AN9}n?4Xtu`2BJAcW(R;&BD&5kTt0QBVa=hF?CI>w1>vK<9>{{{czQd zBSR#`q;x4pc)|jb+AZ35_&02Q&!sOUR|E-Et8-9DX76qT#c^i?!^76(WIfY|! zmAPz4VHDz`=a~})kcf&!%fcH87|cQ=`R>%e*VzjJE+ zp5ei_OVZ0r=V5`dalWJGKrOH23jaI5&z$S>AdGC6><D@!8 zMBW5TC8bK-$sJ}gv}*G-K0y#x#2fvyVCATxtkwqkFNUJZ8)Dz(ohrvGhM`iRH+e*RaJPAvxz zZPS<>Hs(>WJx@sV!h*H!@BZzK|H|Jp760>6Tx5^A(b}XRrD^eP!13Z?=!HKKYM>oM zkJitQ;s9N&Nq0qj8C!l(wj3x<+HU+?40~%CZJMHTxN(a{U<{d^PB@_lPfeox2X# zvMsc~7QAD|;&5Nfas_~RM)f{eh2>h_h3D$4Z(PuDS*m?DpGQ;&%;v9@mp2nIwHn2W3CptFiwex#<#ebPfM4UZm+xUlXNH9j! ze@_G<$N;T90jpN7CQ|c*S2GFYBPB>En(-I}8{YT}#oXO8DHZ%~ z;v*CqcEu5jMyp1A{V$gL4fuQsJ5hf}+w1e$raZC&7LpWvs+Bd@rgZeY{=2?kwHQIx z{VQci^yejn81_43NoLr+DSmj%+vjvvgp*1CV`S!6E2T^Cmj%8SevzCvV_c_F`wvG$ zYnsO=4jP^uR(&^_)yd<|T-_oL&83NulmmHELKX z$KO2os|G*$ab!f8PXiSc-C^kxIilAD!8@J6SpaeTwwGYPCL9UK^WkVlJZ3QjHCEN* zQDQLqFO2JjjouTpnzGr=l3pGXPMc<1WAXWG$X&5~VE#%0!?NFnc!5GS3n5MH0 z_EZF2)_fTYY9&U^FW;W~>nt6O>lY=2m}0Ou85W5L_&g=f?hz3E*-EA_|ZgiCL99yPNIhvRDmad)q<@9l1Qk-zSmAZ-UnSjR<| zI}&ex0osDI?!~RCA>0e#>vv)l)sUbl_tFEw9@2G?Vn3gTsU^p2ln9|jRw{%M)CwxP zLDTgS)Yf0n`WekVmUgST5LGYs+RB|#EG9&1{Qi>aI5Sm_8hmaT>Z?NvS5r-YdQ7XufX2c zV+v0OA*2)XbG)$zZ?U9Nk0+dba*mX-TanN^?8vQsi`dnFkv<^tQpnW#-EzpG!SJm8 zU?S`xb=~PHjP!-$@l~(wlW%%~OQOH}hAaAB+x3kNa23q&GtK|rFCgyS+}=cu2_lJb zC-k&%W1O>#idcI+p#|FpJrn!j@@$oPJ;*_^u>1riggnRuTg8d|?AD_S$vig6(9u7T z=oLje;jYycJwVQWl9d0FALhdxox4=a(IrHpzcH#fUnuF<&mc9fX5;km*wlo5#*eB^ zN{gLp?Z1f*n>vL}x3e?C^rNzI%NECm1xyhhteH@K z{et>=Be+J3dzW8c^5}F&ANM>0kX`fK2TyVckke zC_<-@?#s>RPhByFVf4mUw_bXNGAp?n?ZS+~wOLhQka|M50>=X^-5Pv1jyQ)qn1`~dA7crf4(=`Yz*4O%t=74MH}Z7kE8v`d)NJt&&fMhTUF{dL;wY1oJYy zQ}5DvSsG2WAFTO&-|Jt=+b6MV1`m#Tb>Gxo6vr7w*J!&A%#Z!Z@8*ikNXTY@N0Ro! zW3j~QfTw|_ujOIR%r>(TH~_f5({{$1Bav_xJ1;$~-)jF=a7P~F;T}XF2-@N@kn-ZV z(17MMSd%sqk7~qIt+LwtyH1xH8Mzg~4tV(~F)h&fLqxRAmzYZk`={VX!ei&bP6YL} zb&wmwk+c_FQSE-CWec-Hhts$UOF4#Dng)$cXThtm-alGMa-YDL#*^XG}%JfU>mnSzA<6N*@ z^{&olH=f3t6gKCI(d198oFc|dx061I{L!6)=!gaps8~+>K#`2wAJ@)YqFTYi zJF^0=QTT1%XMG`0m4^I`G@C*1Z3R-RCQSo{uSst9z1AI}_*hbCzHq##HCk3dSPqOw zFJSDEmIBx%te}5Se6}AxGB#FdGinRKD4v6t`n)#+ih%s1GC^f5p+~5__wu!Rs+~so zV^DvqrZN$9ZSF|$mRaS+z>9opw=Epu#og{t8{SHbzZu=n7R>E#%arCIh5O+^3|x&d zZIbF-|61ERm}pvGvePJoeLx|mh>-AXHs&%P)*YUYn5?V$X1O{FkW<>Bo^H(-NdKZN zH3(sm&cIm&({A~Rv72p$!16{?v<5(5<+|J(Sej_2wsN%hywP7nD(GZ#W}F=cs~(cA z6NKSX?@x=tLhF@fW(d-o5eQK^G&|LdKPuN!HUd)Int}h@wp05v`}Ad*<&AikL!_@5 z78S5t>?{b~J8I}W9%^lkYCcP%9ZdAoX_%}ItbsOT=BC@uspMLNIpLm}{ARcSy#NC7c0t6O6N9P7VBeG`r`26ziQ9H~E{D(n-Yt+@DJ^8OervrJr zLNiIn_^DtRg!HK=aFSH#9trX>Wz(RwApn-p(Ghbm?D%c(=~yt1y+- z1{`jf)I5J5ebPR$%;Qe)5^`JA-MPGn{@W}m5{uwZT5t(H$w4Qp>lA5*sp-dh9kv_B zzry7jAi~7>iKwy3vokA##K1-qFcxG|t^euhe(*FjUDY2w?yyoLlH~wY3i|hBI5NLV z(@D@JuGGV|lU7OI^!u8-#_9_R2EF9;W3`~uXvX5;MrOpy$BHpVg&x=~F0F^)>3-a}_cCLMZ{XY1LC~aT1NYo~3g>UUev(>Jfo<{^p58JP zUpC2+VRzHY0hdYbm_)OSwf|{(^2&Zq3%*1y2YBdRow`>iD=erk@p1Hx5Sk?I#?Jz` z=@V$aZNjeZ_kjy1*vg!VL)F>hDVb7T>zlNdN=I;In-X6tVa)#Qv+Uj`%gQPGJbj$W z#TLiw)2)<`FZ<4jU_*JQ73LT{xxH)8Xh(IbwON%wB#dFZU#I-ROkLGzyTnh|L-F`$ zkld%KNrpMW;Yeow5>5iF5L-b-F3z`!%W?XQm;HOc`hh{5GKmnNoMAd)LQU!K(EcS^ zY@T902QU&ob~sXD&}0~-Gr$KEY5bQGS%c)-3XxhI%GzRS+p#*tCF`^?brL?AI7*2a zfIM=HrCQTDYxtZ~ZO)ly`Ix69XiwSp6QXn#y%x#wlBF%CeN5)5D2X%%hZI++6mZE#9B;p5+0_tc_l__86RXYCmw4mn`uI zV#n=&OFcPL^PSaqEi>|ul5EREeJ29Q$Cv-o7n%CW8roj$rZp|S0y-GDTfl0!hTS#Y z-=1gF8>sSIFpuCzwreQuOJEe=iRXf{+;A}MJA)&-S?ai~H9oYU*qF)KIQN=v zQ7e->B#ZNuVmu(AKWW&Wn@hU;wIwOL1gAa8fc^F`%B$;liZ(o4aJ65=nI$fLX5ILZ zMuniv1#jURe+gW(d{$05Kgvh&qM^>AQy8Z{E>X$`_S2>i@uz^4%vZ3%n>~HN{D-3- zc-W1s;yudH8SGC;Lm+le(-^)=iyN)qZ&(DhfqyOkRLUVryD(?cc0;C2b|MI81%^E4 zWgJdcp5uBBavG3&;$zfhQ*uKeVTaQ}wK(m3+vNyiy!>_8noPxk z3S!nL_en-_b;|}5F28~IFteb}6Zs$^D<$Q^TOh#)GN|_Ip1At59r?8{O&x5!0X;?6 zN4%lEuiX{QYM`6Ip!hnB4YSc=oGEDZUy4>0D)0K6X=uwg23PPk1&3)JY0vBn=HM7> zjTq&1Oqu3pt95P_PQ5_u<9L6JHH%&MHnDZ~_HP$L?c3z(eb<+=0&+4G30gNOOZx!g z208&4VVh7XfW6d~|Nj8YKr_F`KmXIes$9h0k2?qM9Jq7f73IJ;-+ub=-LJoM?cwuW0Q*l-fR+bQXY9OI4_@lLk~KR+)e)!WS+f3yjEUa=N$|ehrIJetvu=< zFx^i+YcpNKHgRx}TrXICgSY$SJRG#Kcj=J(RiUTmtgbqFAKvMsIY-$jT45JV-*tz! zy(1|es7I$=&NtfViaBZEJJ1z8)GK%BL8p|%D>%UAQ01blTUod`hUdc5+MtJSys8%a z<0^M80*ps!s}9njw&23rO`nyzfIw{t`g2m1q5DoBMc-&%wGC%KKR1@~&fcKsWnkfMsU0 z-U1m58ihK$KtHYTt2;bzZNQ0bG=2qpDqVjB>oj?AS$ic6i<%GQXP;o69=@&W+`++2zJ1e&L_O`00L+8I*_)ywjFrINbVg zyb6)^dwHX5beyu_X@9o&u1=qUciB$nvN8^jNjmaRe!tU`#^^zKsXy~|YvgCr?+Jeg z4&0gft26Cqc$TnRT9f8y@^0Wim!6~f4<eh>8gm^%6>s zD>^OjICrU;Df1g|rZ9sCrU)glwZ~!GTiAm^*m>K%gVMA5z7^?cJBspb?MClWIL9%E zQLQXui8qUU*A5{%x^m>;>p(lcP!QSQ3=&RXp+Sg!I{>=BlrDvFoc>DVwZ6~~ zm05qKMKr|_7%(_Q=W+T)Vt?B2DL-4^GbWBe&UtN{B_3{^gzup*2*Fk#yhee9MgE6> zEpX0{GKX{gD*qDljh5gXL5?0#Zv2?zyxu=fvOd8dW3}uBUbO24j+BXUz_}i7mD?D< z;e}=DPU)!Zh#ejokgh){4g*Rq+^!eP+)69HwluC?WuzU4zjp;h*UFYCB0iep_udYz z&HBl>UB9-YLAx8{mFKtzay09oDc0`yctCxn&gcpb8t_72dDnyS3zi@UBg64;{^Cau z|LzAr8K0JL|GIPF&Vf4zUO^80!{7bJ!?(Wn_GItT`^P3AoF^+2tP2h&Hz(c6+0r^q zJ|0@IFsQ34%cs;f?J(X;BKN(yZFf0%#cUtKp`Rt$*99JOR zc{r7=zV^uQZHEsA+#K4b&)H&YXZk#S-yfUf=!9hdt-Jt&x^wJJ3%x(mUmAyYi|o@DLvAI`%6Bw>FnRcZJh9U4;t=^*}s9fJ4(>Y=?sK;4vGL z9`wf!^O=PQa*bDQiyF~^$MV?-uFuPtbHJfged@!03#0H)x)H1@;kSN#*f3Xk7t zDFJ`jZ(zc$uL>!MPM71D-2QGkUFg?O`m#U(u3WWF&2tSFyOzE230pAuqc@x#>$k4q zh)-xypPhzoH2e57XEx8)*0EvPddlPNezG#Wl)uLM+5&HI4`%Guip+l0Cfwy1>${NY z!pSk>4(+neBbtY%*;f^5k?zZW1pxQ10_ekT8_`2FvH|10yZGQW-IJ5&Ci zdc8@5w~zdNc}q(HfZv}$+3YaM6DGV^6T3eX(c8~$!>6RI8vAYX3A714g1Dc`dDffo zt78JH?+xzT&R?2msyHmpCfK%6CCs$p#c<}M)#?eB+82dWW=;)BSW(-WuaE{|^1NrG zq;qtwsJkHqPY{7(BaDtC`b`b;iO54Qz+fKx3WLs((Q4A zJ15Wx5L@PyrBwFs{dvRd(DIWau8}cDJxURLyIYf(l;HZJi5U$p}_eBI0bjJfqIg^f{g=-j_R!)whKFz|84Q0J}ar}*E9E@MzS zezILS{Fjn6RzL08!tGv(7Kgysj7&{MqqYOEK8*px-M!A=dda)6Bc$G`V(x_~ZDkJ9@c90~`=iM$q^J^Ey#ba_ef z$=;(A*f>_zkJC?9PZmGQP|l-#j%3yQa+=N2dG&rKb*yoQlml8 zgS~Zv58%^n(!H+=EPCrI!hUyPUV_E~=@ACO}?3Jin;I0JY3b#PCy`s0j4bi91L3}s`u z{A1srQm6o;jrqShG1Rt2~~uQKc($`r+8E7D*iS$2+^YeO*3NV-PET(SgPH z<5%RsooT-!!+we4U&+%1w1I58zfb8AG|zel-$dCYB5Vnu4in4U{&!b75$CPQ-X_;2 zqyT*CrEJpsSP{2%TY!N;C&0HFJE6cOOi)o6{XKiu)~W=$wxsQs2)YKF@EWocm|fol z$=)Peo0K9akq{svIX7qWAE$lJRTJIse4=$qG`N$3r3dy1#0eqS8dadYn;<>cpvynb zDmb!ZV@juI&BBY)aKM@1qYTJy?69%D@R5?(bMfkc9bDz$`!R>UL=Hb*`M^tC<6ZUr62Q37*Qu`! zG#>@1pN}z+Ag!@C=g@9NtuGJI-ltLsdUXx$jA z|2W6`6^}jF=1~t$I70=zD~PbZt!$vCKp08p1q?hxC_rNu$R~mi{h2(Jy}K#5a9_YI zyyE_uGx7EW7tSsV{cr+B;RQ-y1dIIA0Q`-G8K7Ni%NT*XzHrRR?`*e8*pCKyc#LCd zcoUp|`R{)4@RuJHP`e*@4%|6#=fEq>fp359t%pDQov%+PZ3kF7(s5Scub_ZMi}Q$_ zopBC2hpwU%Uy#|!QBqbKy+GeuD4D1)jc)YWTguZr>VMSz)Nl3PtljB8I)k3-@3S3) z2d15L%Sm@$PNlOK&e213+i|WqSwW~A+vw=10~2tnf=#8pEp%wG#5w1|TY6`!;^0}V zaeadNY&i}U+vzw*%J9xEM~`jC)|lQ6?rgEjobzdGTheQE?MG{xZTKF#(r)4AvzO)# z%wu}Iif%kp1F(`_(y%r_BgBk`hsrP792VarkZ#?+&hD( zkF+;i)9~6jI}Y&{E5ad4*PijVi@w!6$8oM}VfZ)7maI+m-~dF1j>N-~TPmyJ#*(fM z?>dKnNDY$wh>0(!|M2nkCtg5({@`)(+VvIm01FuW99->mZ)EH~JlJeod@23dgU8Xw z_t|V!V7v2I1PkoQ^q!4lZ$b=A^jdT|0gCmRhC2M3k}JOeg6C{QK7yliZ-wtEhbfBE{>+Na;Xl!=w(Yw~d>L{vF- zXY%FD?CZ2iuW^J0y z1m5LLQ%HWZd=6AN2uu(!a3I4??&DyauOwKlcIoHy9P%kB*u0TIAl&;}UEqm#NHnw{Lj}pvt&ceG2bEfxe3RQZNBMV;&jUwENKDgzf+u5N_ ze5cGfke|*tuo_|8_MS_MZem|DMQXi1YwXrSfH)mfxjyt-~dkuO4;9G(Y*mzTd;m0r#K&sdj*?D;ne8&<5 z*Y~$*e1(*o@~QvJl7jF;LpHW_=tv%>&iz|(i@!}9#i zQ3tQ4W+0R*e-z1 ztctT!F?*w(iPP=rXL52o17ixgxz*-mYeI8ua#Ds9;tShO>=CEU{!7_{)2IJ9pukX{T-a9hZF=6VO>wp zrEBS{>zF*i~MDC*0XyN4Hw=LVww85J>n0kNu#Z_83TcRVb*0 zZZ;ZNSr6&)1vr9z)vroTo%S~l)X<()hz|H&L5HFo-kV!yh(Mg6K1ywy@?h~Q;)ND1jH$4a+|<}>nQ>L&5!&wH)K>+TkF2TYfC%_mB!>P96!SSSQpTGDpN(nab4&Lh6Ru8@a z%HC6e@6jsQa)cJlh7K({6GDj63(cir~ zQ!3%J2z78Mp1t6&w2xW^$AhpPLpa+P9s)-CW}j9E&UmP8^nyJBtS*D6;6nUh7;&@% zUGU@i`duGis|*V)QJDKX0vaDeH>0edU5pOHXTPWr4E&@_?KY{&87E*Lc!!P%-I!q9 zsR0hYfT7>;6dbES;GduPX&iUY*qOl>aAODkpB8|b9>r##@;oyL_C9s)qRQoQ!u?8v zm3oTHJQ2V-IHLuvhxc@iYjc4?G4P$09i^S|!MKC(49e1sojsZme0{Y?5yqF`tI)Cj zsIT<(RYtjg(To57_QSKXvx4`pI|u$NaKJcSAi48x$o^Nr;@;$|%mL%;AN}4prc>!( zverSCld#i=$s2Xw8hEY%Y>s99(3`G*)Cm|2S@F(IiR4<={3Bfo9l{swOSb<;igT;8Bnu(R+)k+mIuv#qoF1X|dgNKw}Ao6o12Kk=-*YtgWB zPrlPJg8F*-m(y-({UZ3MPqLFQW?e<^=f5Kd&Mf^qR=!vJi~|V{((UEri}%Z0P~g)~ zw;!7lG&y|sEZGDkXM~_`0_Ajgj-7+dDVzdoBAIXwQA^51z6p3%Lpdyjk1bf>RbZY)3VrIYA5fffZTSD8JVUl}V9RmylM&|vq#DRM>- zc>NhK=eK~X+n->91}QzmZey{!A9>6GY(`w=^j~Z_R2UtEa!v zsw`tjvbnhYrCqxiK7IJhAOGaxU;oFyQSpAWU}lc%!>lH2q*d7S*FhbM?9%ahB5aR)m_aGA_imM)u3 zQ_FMMOTXZd&FalzyXhcy2Ki0CUpA2-#N{Z`4Qr<_b?v0&j1BMVNt=xjUUbFqtxdXk zbgKtI`H-aRhU4vAO66KaK|t5Tr2zg_WkK)&fOl{|f%Rj5#m{ykdVFs0H>f_g9GXth zf|Ggz(XgnsmvE$q(TBy3;nB_Y-X%8Me(@lDW>W#)aWK8vUTjS1eGVV3IOFNDt0|V5CqZQ^eNw_fy80FEWUP3k@z}9j-06D#GyW-4!4CF@J{FLt|F;_hef{}I7@KK* zG=ImdR(HtOs(OqVma#Jk8vIvg9|OKKRm`4kl?jTJ}fA# zKIxzQW6YK-`0hm@@KYI?w`&L9zWWJ$^gjmPDm!mM+!S){yl>Gs_-GmcIoqB%7x zeRi29n6$-^F#?RAmzq()o=5pE`aEX9x_TNVtYb>ng*Yo3=4EbvjGYP3TJ_FFoox+q zoXDNLUjU+*us)lPp1xQnZPYmg85|s$?avZt{eC7eRMX4U7#~{M=g_0%HK>7M15NyV zKb;{hVZjx$TGw4vbTHIw@>!VB(|>^-qnP${9&}?YLeso}?^cafpbnQcHnY0YyZD4w z&Z5_8i&M7{AdjI53D{U;KaLCQ86(@B;&_pedh_YTT%seqC{I#P|JL|)CtUC^=fvUT z1w0%w^Qvx*CZ1*77*~Do%B=n-;ng@#tI8(-vv|=qjYGywUATgB78Fv?B7xO$bsKm< z7MvIIoR!o-BmI_+3mKuW0Z=Bv#g{Vla|ix2$SX*YuRj)iEM#bBEvn4Ke;!3ar{~;t z2fr_Hf@p0l+e?~uF^YYt}RTJw&_Cp zlWN6aY`gFqy=)V` zXaZwQ|7-64&(_1$!hYO2@MJmg`g2boe*Lu<@*=k94lh1^H9ARhW0NA6b5}4Eto$SE z5%uhzxqtZn`?nAOJ1?pH-{1Yg;ho#@6$E@wI|n`?2fp^wvxlqkf%PqHCfk~Y{Vuk; z?k`AlVRpUtjM=X%k$EH~;L@$>WE*YYm?0je2xuCW-tb8!x8eE0pt*v(t?abltwDOzKUI&F_p4 zO3R_mV`{F!iU<0rT(0}VztpoZQkwY_hyvGzi1;S$N{;K?L|SNQQs+*5;|~2D(E4?| z>m)Z7(7--O9izr?X(sL2g1=%t)ffRd)@f{_^|1nIu0yHx25?bG|0YBtYK9EzWDZ5eDsg zOqG8I+qf)a)-ehVBNxHB(MN8`B3N;tv9&-l;~RMky$I|W1V+9%_8}Q)DuIQ;Bi#vx zfXz6}(oVB=vwfir_>KO?PdV=mobaXZfe2jtk5A;v7$FMUE(t|N<-q{ z(O2z&!9%?C0`l&F%sHMk9!h^3=LS#X)YPd?s23Rck$`7*%W4guDo)L_ATaX)dh4-V zaryJlKmVnG{-^v#>2mV86x<|rI{G-EJ^v#)urC07Bzrztp{*&KP;Mr)^-s?aFTecq zb7yz&K1KC7Jjzj3m%&oIb54O&-Nf5D3%{lm@OVzfr!sc~if;rM7Up zv&l%&e}-R*o<{An^*wjFXY!rOTRFpfF-jQdQQ#C<>88XpS+#XOlhSS5k-N0WzM2=} zdB&aLu&Q_M>O6nX2)UE2J;p+(ZMC~_k%1XQGY*)cm`QrbEjG zM{tIA#usDDuMiCnoLz;^7}n9$NhD|qO`JkMg%`XtDUMUFlS5-shkprTQtri!`q?`e zDSW``1IqD2HkvTPeTP6+RBBXHUE3YwlzCxNC%EVf4dh8*I3a;V1EuyjqC+m^8=thB zhB}TyA#~d2-?}l<fUu2h*m=Dczodcpa1tQ*X2u;Hvpw*?HV)|{PjTMHVkZ~Rl& zBLj7EY1VmVFMV?q48hOEp%;J9Tc@3JeNktea`+UQ^uf5QgJ!U6mxB($ONa2Q+dz}4 zM)Q-GZkLBd=s~Wf1_pui@|&RR7?26=$k2v9SeThiyu5Ju_B*!@fAaRNI&h$S+Bxtf zIq=+#%ZK0k%BzRpc;n?Plt!PTe|GX*v`AcGjoo8sLqz4cd57BXFU3y2@xsl+Km5uU z4qyMX?;ZZjpS=}3w#Qmea=d=oaK{uM{@NFwZ_Hql#?FgX_;0?1jdc_^@KN_K@DaT? zlIwvZlE;Zf?RV@nL!M-}oCW2R-vYtQZX){a3qYoRTA+N}ZRN4$PL3AH*UNR%}K&R zX&rkJT3si^>nGTkBD8FS{b1fI+m9z4c!a4Z`TOao!-4Ym^jVf?o_p>!+R{nb=J-}q zmojmVW_R*HJ*?bB$THaz|-xSz_QG zdf^O(i-N1~6D08Ybew0X+}TfdpP@FTxtj0HcWRS7Mh86%iaLyjpO?*eVI8B>mk7#E z+rCE7mFrxmRu!GO(jR{%lj7#3(V;Ij!G}i-k`+ki@xHD5t`XOKMmh!sJ#`}5&=(E` z#+hI;@(d;i!YH|ekOzsz2-We7qbz@t&xm(o9=aIMPUzF-OuEGk_=C1=R}T-Ha?Sx+ zZT0e)E)e8sMYF510TO82#@ChOv=Nf!hMtT={-F=f^=^o6bq|c5)TyREUh11kuE{!9 z7<(KNoXlhwx5}czF30H01s)6F;JRoMyj@TU|KKDquGQ`@`0-?L&KL)<{WrnZAF_f7 zi%rI^@NL}=i8$1>*PtwIoKN;M7Ua`71-83D((lR``AjArjl&<>&k$a6-B!RO z>glfqGB4$c>;4CR4PjE~{4HA?*_HVC zU`bhGmBbfKo6qP%K@2lH&K}R?^?Xt`u|jaA62Az>4Gjn_7CV7EhPy|2`bb`EIh)_@ z(Sfi3;d4TN?S1exE`d1+(CWv>(Y4|f$ZSbO*G29e!8xA=h-8F6bd_K4}v&F41u z*z80&U4GXum$T>%>LhU|99>ul-tr@b^@|~P;!Csms;2G$rZ@{$(GPjVLI26I z`W?ekLPPvEI#Wl_wM%Ysvz;jJ{7Dk$8Y?nlOvvNa+`Q=cXABy528+>19`0>*Kc-+-FS5e*Wd=4nloVBGG@{LQTA4JNFm`ejfYWo^*!tXAl%+4t zpl>h)>ug7l;fwP5{%T*=W}*}r@lh5StazG$`6P=8 zGbKh71KI8KX}69#w6u-jxs{jg^||x3$FZ9Hxn^uQmO3;XV2;vGSAZQEgU~$;Nxihi zV0H2xBN341f#W#zg@f!uL{|qut8Vf-&a~Up)44jk)WMT+5^w}M6IV2#v(LnLkz@>C z1B1MD(2C|b2{2=lU3(Wpzj2%TjIOq_1UfW_cXe*zvk4=+cOiEht|YKwKK=RGV}cMjitJCD!oY3INb=D=@#_0_|_{*A95Uc8YvuSeFYD1Bl> zCtw~s!G2Ue*>#o<=UV85E?|$>w4qtxZnStB??i~0i4n*F*`ogn^ zYvK+5B)+HdNo1>*HUe2+D^4)cs{e?X zFPH-HRqB&t_=BlBAOk#nbVBNz+k?l|=>l1|JJX4+Z0(3tAN0w*zP?GHG}(OJ$?skr zr%e8dH=Er!Vmkk{DaMM6$vOTc9t@uDh6q0zPo*y=ee_r)F_Wu_XKVqV$@bu#iQdGN z#kaM)VyN;+8)?&oZFrmbC+BeS;YgNdhblYZ@qygnz>5@$8QknZTc|IrKjmLa+poZO zQq8u6fAYkF79AR!wYRCXF|k9D4tA25{)3d2>Xs3e(^G1#eI=Dg087_oX8=>Id5JSv_OoT@K)@9qYV21IuuRR=Hdc+CUI;c-zRwBie% zR;P3)v|Q3wXkH6fSpe`301ZAaDf_7M$JN{OKZFB2BR_;GACdJF{EA~~UWETA$~HWY z8j_SnUz?DoLKQgeI5J4l?XHQ>zg zQdSW4#Y!kc)X8Ti!|G_c{Y+!Zp|_o_?WLSSNOtiZ?;_(?%CxPG6qYY_W~nPsWeo9@ zA-c>!2WpUyLGuQ7hO&k+MPsz%AQGg&=WiWJCYc&L(i6V4oy^F)=X$NCdYWuRR+h@aeepB{lm5FB6%v4 zer6WGZ=c;>WTfAmV2}pKef`VWqmYbn)Zj10F*I06Nw=M)(1d~v2ztP9s?Z25I9q)( zR`M)D34km#xE@${Dd&y1;1W4&M=NXrc=ve;m~W1Nh+OQ{)DJ94W&r zGLR9@QcyASb`0z^o+OYnuH&Lf-2_Yef*IN&2WUS_U;g`_znv|vp!T$L;K^~|pZ@Br zhu{8NZ^WiWGDlP^qvWuDhW*HS7j$#v3p$b7VCj-zuKFtvk|?2I^V#cH4*%xwfA#Ry zmu^0}4(ynJDL8N?a`tOqcy{dQcnHE&88kuY<5TeiCdW*-olz$8Y;Y$aF_p{DubU*D z&<9Inr9W(H zh}!}lnc#Z~pdC#5u}$1tN3Ef!iwedco?yd+_AAGcv*d<8gAHGYIQYaWctS?>bA0;5 znWZTmfKzg*aJB?d5nAH zGB2Z&JGCMkVjVb>sjp}sas~oo^flJWm}PX1xak_2@gf{--$)I<;kAbD=0;8@`PIX% zjiAOeU(G-ZzG=h4&)hf}S(~^F)kE*?+*pyK3(xiB(Ld9{NAr$XCtqR%_gL6B-+c42 zWk+P6vy+`6Phh~KbdLz)=~kAfp1Sg?Rpe2=QAh^eL@4v)%~{N3{Zf^4&P{F^F#%h2 zYK&}-wt_9^#o18K-hGVrUCjiL!dv~WaY~-8^c<~g#)A`@RX&DpCc?{scmryUL-3BF z$aSpAl}rxdMTn!-yL&Mhmt)8o9S%hTqOY5~DUPUrjYi8E;Tj%S=p0(qV?EGgdr>V3 zYm1YE(EtEI07*naRKgo3jZwt|MPsN;cIQPb^HMxcA6)Oy2L|7)$O?k>A%DYjMhA6_ zCu6Nn@W+5Lnt~E5ovtmgtQVmOP#Acvd-E>Gyqrj~x&j)2;7BVbnBarAPKaSOmdV3+ ztIZNT2w~v$$tt#84d@a;jB``Y$frC`X&h<-D9@Ug7iq(mtk68$?qJ5aaqd0~RO1}R zb?bXd)-H|qC!l!7sP~RZuJs?SaQN(t34NV)juX@fI5@*0?`C1@E}7!v^irpPR(u_W%=Ja>l26La1BIiq`voJ=;t4O z_eY2C{qRAT=dqLja3Ckz7q964g$(KilEfUjf z!bxw)>*!{DL+pV1?vBz8bu0q>tH1Zv!^?S@;GT94JdOje<#CZ$pUn=G{HwoY&o|r5 zzl?EZLro~Q$!z>g7jh$K5w^cS|kHC*6ZD%1pG^$rWx5aQ45VU8}dnWp@1{b{PCmswv!4W+5!5J5= zo80PK;S-PGpF8_ySRvASh^RdYE}SUeVh>Vqtj8zM2qZgvM>Bt3eg|h49RjaPC)1c| zJW@{_)CKzFX+M+?P03HUidWx@qw&Wqu7g9r#R%nYf;j3ATKezEcazp78vx+bHmIQw zan8{q7R6`0TQVfRSTBmk1Nqw!Hts6Bf_jd_o1r9s1MfEpckqG?Vxy~JXHG-I()9(kTep=j_ah%Tz zPJr*-4|WrH#9;cMoo=hbrlhXgYAlGZWrv8qz;o^D_3{alV2yCYe7v0Ih04f za!qxNYnn_S^e1#AvjIr(j6=_aC;0n}VCtH@R0mRb`Yi}Szkn=vqq1nC9d{ncSr^$l zmJ={!sHV;E(K(jZVdX?W?BsxJ$yxgp7`sDmXX5pH-}<3=V^2E=o)`yw+V8hB`JYFO z#tzd30hUBQ>6OKS+T`R##7AE?*`!CE$OP8Jg^lyC&wCaNJ90QYd+qY!U;dph2VZdQ zY3IO)aNt+J@JuHYVjaFR*yh0TEpBqC-?(U# zXZ941vAH9_XL--fWeecwNfsQ^NOA0a{D3lksL$Nj?@Zitmte=Z_vS|`-*Yn}?fapz z@lNXXPH#yc-O`3P_g~o-g?TD^q!GHuXR5y@_|vb@p8hQSXq;FlB#6^er#ETzHF(;e z_G8NVsoqrBc!ZzjPw3#grB1^2KlQcAccO(KFF(RSc=>Yp>f2K`{WNA6Z{3yO0%TBhQ zoMJhaC-d~YFn1q3eA$H1_AHZ2Z{z3u{Ic2={fR?Z z;w%_E&QXR5xJe0x?}9?$izB3f^Kz5EO}*Ma6H@eO8(f{zX3RNj`BqpN|FbMstq!c{E_yJr$=KAVG^UZc-M3N3{Qii4inv-R_~$cDh9|I7*kov@4<FXzR7)eourITjz&L$Blo$XyIDS>AlzEc)|u{t?UBDPY1{7((iQu<7KSzk+{W+H0~n6YuVb-FpYt`5TCDX?YdD%f3<~|&iO3F;n`Z`12=J#qKPT%pMtZCe`()! zd8Y5dQ+iV`&9!W<(ZL_iQCZs$Uj{O9WBm8DVd5X0f$gFGabl&JbPnG3a2o^hX6Y#2 z#SCy2&S(5rVLFJgXAJfbueOI{OWE4@l8-qyfb|h!|5Y_bR)={g%_^p zd*%2Rb!X)QTADxyOKBaU%!6-MrMlS*|M%`(?beMQ5ogfBD)7V=?jcI$m5*G z8G`Yqe{FIw3<9Gw-%7vKNk5~C4!a60Ak6mknb^+rZ85|#Al`~^Vl^*e3GJ~Q`o$B5~Y8fY6w#N`(%y(!R;06PXO?bl4mHsfn_fyB`#<=9XPB%^@2s=?-v~?)c_wq`e z{H%7W3wIfsbLEnLX}8adr+@HC0tt82^;O=Q?9W0%+K++1bC$dM;ngz4>dn^hl-Efc zLo2~nTP+}%IC=)2^T6Yo^fSEpQ=@C_LsJ^n>*m2P>2sZOaDn%;n4<3-0~);?vJNnH z!CKK?PQkdS)2;vE!7LKZWSV1L#$J8$QYX_OV|F2+8GGQ6I8LdP@Z=lkyNjOSsQiTv zsl3-_Xy;_?&M>B&10HfJ0<_W>ymi>*A@#d$IdFKFtk%IplM;QG)`WZ(pDcnTg;@E< zw>0Ov{8N?$w(aUq&eefW+s4GA!5a7Sc+*-#YxCzxYu; zAJ``MKRXAW2nW9U;xmU||KjtN@6m}!bN;ipo}Z;S9)%(A{7ok!1fC9UI)yOyvM-Q5 z+U3;a6V&HR0?RKrA^*Smp>a*w^@9wv|s==drW( zbg|QS&hpaK%}4jkI}x|~`oNofvaxOeG%kqT^65*~`4RbKJ7Dz@j&79jyX2bxFrk(| zzGjT_yo8o2h`T2Cfp1=3i#~A-zqcmO(W&t;$0MJ>pEq97$I!wx^?CoaONZb=qa!}> zOUn88>SgPsG<}0d|CDRH@e4krjqWf^-R{r{oY-ag*YbApBJlcAUZzR$7N3unNmI7^ z4TpF%4Gc_a93JC6ICaHlXv80EsLxD3|F2)hioNg;F6X5?_4=iN)IHvSO1V5d8T&Le zqq}(~;Gs+EP7=J}qth33G_Uq8bTlr3FLeVqhtW``87fT$8ab41})kj5=p$#{sUXXyKIHZ^=L=jqD^pWh<=`af9r1At*)$ z3Zp|wmH*@$D>AL4_-72tkHCN|bZ{7pzF=Fc>ZEUIQ{G()uG*%)OkAg3u=+8(j!n*I ztjXcR-(8Y{>w=Wr)H@$%{nm+rA3UL(-Y35@5!&^Q(t zegS9dw>XtCCO<}<*`Ryqnz+b!@5FmGR`8oT;V%vn@Zk^LG6@GkBAj zBrr9g$-|@O#-Vw}+~XPDfe`uyGc{zTXLER%mHJlU$7r4#X_)4vb!jE|>Ri*p1R_?C zIWUeq=NTXM;m=9#PGqM;cqny&JI$b!Ea0bcYv0gPx`#KM*P1Bfy@02IOZgj~L@r8a z%jP+~S*=d}{Oc;YmVz^vX{#gz$1DKJ(}uzZC^=$5}g|NHMA-rH8T2l@VY=fJ1S z0ebUyGx=}ON=74(?Ael8`dwL1vyR}`;-70aqOh?M>~t3g0z3AMg;a0Tn;e_A;|qBo zz~6Y~*~#1g?i|=S@U@qoi5y1G5+8JtG{%tJRSqL=OIDqAVJUW;-E15{))@Ny_jB%) zA9lAqBHjt)s;5-Uq>41Mp4?=JjarsE%6)x?T=S7+oqd=1V*b_@c{h>aqj;3=0IUDr z?kIOsKKCw{i zz&PZeT~qAr#9A|6nh6r6;bUlXInYWfx8=yNp?T*7=2Cv@OAhFfhgP|byYw@CZk(9B ziRVT)(2CcMe{v*0#=CeT)$Lz!br;3f#w=<@mIB-NVU^dz2aUtk_b!Ei#?bf?x*9WW zI0b^rnwJh5R}^Pj-#jurk3>!$-c|p?WAsn_DTeg!vRR~g>7|#x7!svO?=hX8A2aup zpN|7OQ-3=D9>=4{<)30ag}E3fcPT5evDYK`wr!hu_s#HCT9d!~eJp^Id%OGGSq#cJ zo~9f)Lx~ZiKwX6kPLC7Z4bCcm%A<=>SkJG|8~N2?R2d8PF4;1gf5Fg+v@(-D#%s@VJxIQ5GtNC*9@IG) zhdP^#X=nz>c%qM!gYRPA<_)HIEy7cQ)pA(+YOw$h8!T+cKqN062tIgM-|Eot5nJ;M zm+HV9?BrT|f(N{-LkQnE6)WCD-%K7(pK(KrpaepXBH$N;Ux$VH!4#r;8+{B3xgK4q9O75#M7N~OIN`s?Ph6t~Z#}>H-S7Y4@CSc+Yk>E^ zI|rT+2VQ#mTDIfgte&u))xF3eT_>uQ*~lZ=oR^l3{a68dzjGeA>P>2ljK?mL<>43RQ^hwUY_?7s!x*icOR%|-8)=Ts~F zv~mqPqHOtG9n$vrcRrIp3BDW~C%~1ypiTYy$t*CVrzn$G{}%Xl)2Gd*9$Hxhezdzb zgJ)Cm`o*AV+*J7DNgp+m1UFjQh|-d}UK}dlA3nYB8w$DbgYASr4S0F7{^+AN1{*qa z;X0WeC$U)gn+6(};p38>!Um_l6$d(?%5kW$nBqM=iB31|T>&AzNT~x4AoCSo`G3hdq>2|HwVH`}J=B=Z) zl&Qd} zmE9g32(Z3jP+8g~dl{VMrvSzr(?n%}Y*`R7PKr4O3nBQh77F0kfBeLcPLgwmMq_3{ z!}#S{fQ~KrI_)&2FUCiE`pBWzk!lPn+RG2W$w9K3@Nq3%sY{0haO4@G9(9?(aC8m* zBR_(LfCrF+qz@~2b&gMH9bObwWlmqag^>^1Xv1c5^q>FuTX_NCtZ0|G{m;&UC%}O> zUbvAr^XCz+1Xo_-`BZ{X_K&_>m}4Jil0wHLm`Rb^%DYK{iA__A=pB8iT@P&aBRXCC z6J1lAt*tEyeme1L79d{QzX&iq-2Wba<@vn*|K{}u{$hkg4V@tJMU|cS8u_)|@h@Ir z+zC+ZCRt^(<5nZb@s%g?#%HdD{l*)S?U@Y6e*}k}f9iXG3%@V1+jiKI)0q0+^)5`P zq5*|tuOsthTnxoObfLJ5z>#&noi5}bjHN4mpeNwZV&gsfRzH`#EBOR&eN%m7>ZC6G zl0HkFoYAMi@DUFA&K$9%Lv0r9u9^JlZ;=8oFcC%9GkNJR6x) z&NepoO*`mV3kc{^XBL^xZKXN}sbBa(J^Dhgx?(5Ab_lSE#@Eq19E?x9n`@l$x2e}; zAy;?_{dsBRHv_Wuan*rm=tw^zYm)2P{_kcYEt_$xOjX|SIgg1ZE)#2kFpjO4b8W&! z7EJo}ub*sAT{nD?*>^*2t&vl@qh9F^nPN1Q*gYby)DGTS5Ry-Jv_WQ<>~|4?zHE7L zku!Z@3#9AUufNul{Q1Bm)M@oRMGkr$oR8#h5FeIzUU@$&2X-bsIYB6IPoH6V=DFuy zXFO{_qEJ@$7&8hYj}waCRMAWpm6=qrlWRi4IxsSnZ3S%VRB0|DG$a@y)r%1NvRzOp^PI zm(CO+`eyr@?Saiwq&yQn<6$wtgt2XeCN$eY0T>@R8fNgaiF5j$QE0vLIxt-fh_z(M z>u3Utr}{W|0%Gj6m`uhI9{spbxwzCf^lNKvOPsCioUs=x&LxJJ`*NMP^Wv*^I7 zSfQOsu<@O(Yx}>L5!J)Fc(RkH@2IHT$(`v`X z_<`dH@VtSO()3ZXn1s`4;iS65p#05aByDTsd1%6Cch*->_Eg#vSQr;_kLb!i;^81e z`6u6k0q5%4LO~aYa^&3loGFr1hfb(xTPR%kTc@02pSbbdAUYeEC_9N0STm z%{YQ*tD1sWS#pX;9vb``8>eviAUamhc0s0oz!M2^$rc=Vtv?op*>CY_ zWh9XPiJA~SH{fy|SiYk4)0JSMAM^`t%E__?uv~@L+4*WrQ)W!*nSPKF_#5{JvTfl> z3cK4gdH7kGG6unG;YzY2T1sr*1%UJs8IPpUGGg&a?tbyJa$slX&&s*2-p3S3e7<&`CP&8DQ#guZMVoNpoR2TuCk ztwFg4%os76j#u*;I7Z`AXpp#qPIy&{qB5pc$|~+S5(dYeU*+Jfp1U(^m5^~@NUs$C zC_DP#tigpZM!f;0Hs&n=F(NS<2-+M{X-PY6AvjY|L&MlG6s0lzLos?BXF_OV%<0a8 z03P)%?`an=>*#aN2%!OOxAWcriyO;v+R}wz%9+1_7PnSrOW7Ez8e4dC2fRBCz@?ws z0PER0lh-=gT(igchCj+PEA6-q{l+Sv-^0W9XB+48J;6oBNFN0>%F&IM?(PUo-y2b! z(+2*KgT7fft<{;PKVHsAhFqgnvRHu6=rY!`;4ltz@xQXR8FmX1>d)58fh(_qZ}8@Q z6dnKEi3tzjy_kg%fe1WZoGGuu#NNKQUPm=(FFI zsR&JP97N9eGjiBTHd*2$!alO(7(wl1gY0@lWfq@P68Y=1;spx+2z_NTRn}eq-fT#@ z7|Vp$HF;*^RjZH5Q9LsKHRR>GFwvd*@f`Wvfj^%(@!uvpd6qNwJrf^_kpKKbY1b#d zDRud%Ty_Vg{#wl7gXNi!p;!M(Ulu-;bpc)d(290>vw)(WK8)9Kv`fM^cJ-&d;hlE6 zTVjI`VI>gly*aVf^?5dZ)(;)POMS~#@cHYw#atPm#;J)tW~V~N(C&laRu679)GoT< zL%+!-o8l)f=_H)9EG)Vv&gymgjMTQA6Ev7`>hbJyP)?pKlAQB_JyNfeVTb}F!}y^A zo~b`|faq6cPQ~y$6|^#Wp;elRw36UzY5JF#1K!F_X`KG8*qNQ2SY&YEPg*!n$yQ>_ zj%np3qcKvBEls{-`PrjIpWrM1wzw($MZZ3RtBeG;@1xLroydb!`v+<>|Rd`I!q;s8}#VZSD@{B{rEA$0L13MExKC^y;S22psi-G%K*}7iGwi3bq;#nr1gxv&& zfuu;Bg-MAm=8UClcb(U{%8bLcY+*LhWrR3JliR)kCgqH|iDwN|6qli>(zj*$$iUUK zbfwLyH>`t~PZ^mjhR-zzYAcl>uyisONy^+!Ru7#Vro28fP{%cf+=9lKyZntIcOSTI zK@x-1x0A9Hbv!Z7jD6}eQWv_zz=V5@SdEHFUfLXb<3Pi&I{DCDUZ&o(DX86?cIXu6H)8Pcwv z=A1D!u9ClKfKQv!yyz%A%z1;6l;Q0p;}heXIyJ5t8^J~IaD?t~xHvg$^!ni!nK_rG zafImR9C}AU`@ti1JTuna36XwrjQXWMcx|qYKX*Itbvr-Z;heg-l(sC0j64pB^r3aONxlBzYxxdW z&IP6N(4B^JHVI#(t64!;rfB3m^>RTc^iN>poGwcL=g+@$__KG@~UO^BwQo$ptR$iYdr&C;NO$>hclw_c;gWeFW_G_|f>8z-grm zz-?=6{L+O^yf(Gq&*1WSr1r!D6wTIeH!IL7AI^Js+%+9~0#v%hYHPAhsnClb@Rjc9 z)zU3HAc{Y2qq}?yZk?KtEcM&(@mJa&o{sJoFzuq3&RS4t-BeyWFP}Y_7;$RwrINh% zH^#hk*0W@wPchockLK^+{uQVw5e)`cu0=x5vR5vFN##be<@3=q{hS*&feJl%>BbNlnR;Nxp6Wy6n$#16 zh-CnkS7~#NUl*@hTe~c9#sac1(Lz8^^M_RH#9#ZNex11FLcU zho|G1zDr```_aS6M`K&e!EyQwM!vl6o~W)DdF7`WL9qm@aYEGlIpncA8H6!xMHwJL`QkiR(Y{n`KWv$zB!@l|T9S%{SjvzNd`?J2O6IQHWqa z!qd}dSYnJ`GbQqje+>k6%74yjDMYiP43fP5qHInvid7zd8VUEvzs)Kg~STjdN6`Rdicg_at;w9C*>niYO`wA+^&-n6M+ z@6Lyg&<3x&t)1W@Fu&Rjl*SM_zq@^}K8Vtn;K+i3?LjH3L5JT2)wLk_%H^viLEq8i zj<-Jh7Y+^&Q|ChZ*cE*Ar<}p9BTH>;>twN*X2nvU_486be^^Jt2ya1&$toJ8P}#)@ zxaFJdU(EJD_*Z~pv0@o~@U!sK(FuZ?{5DX}c~^+j4{hsD?+9cv+wEAPp}_;%)1ST^ z$$8+khbMK~Ib!Cu1n2VYOweu_j4!QY7srFc^(-veY=vj~Y2_S4@NV7hM!#Q#pmcym zZv&{nqRnJ_p0P`Ra*dyr^OVncgn~_6iYslB{b9cLEP%vGwG)V4V>ixDKfs_}peA_^ zPdoaDr;9GS0v@l*`<%~$3a8E)!ENH6m2vbZ5B$iucJxWWGda&o>81)L(n$naUj>xZ;GI)YkG7 zeOa-|RPApnki-1vXZbOYdYKF#Q)HNryO%{{lWV@Bi}4Xcb;!HjX7GXYC6>lg@aM<& zcQO>=<%j46|Ly%b&iMuYSsA;p?JL3gTyG>3jRe0-HJ=7KU|B$ z)tA03kA95KiGOARpH|Y*ZNaYWu-Fk;|MLUaNvrN}l+dqY!ys~rkUT2aTyP5xP=FhQNi4?<&LDoc4OUui&BV2o?W3eCpTFY$=> z8`rrBkbl}cH&=S+H%Z^Yt~{}fmK*5YP=W(pcHJ1dqPsZDYmYxgu_ZJN%k4$^u};QVlYli_VbU!ft`6Dhf5z1=CQntkP=+h@l`L= zpl!3=hB7i#6pT|7Xayiv!Fe4(lue$A3RsG2qQWql@Kk}LNHre$%TOxk*k+PGI#Xwu z^1i*3F$~^L!lJy4BE#IeXh<@-UC;9@gU2{X6o0mm=REKpPq12uhASq^p#kkCTxh?a z$$Kiayap|O)qeieAk|6aTK}|LqnUH=M%$c6DSy~O@32n z=B_QId`=z%U9H?oWlxH%#`TPw$C*d$5sH~w>6 zGr(}-ue#`Vu0H&g!_efvywVyv(9*9Pz+65F_UH}S&q{d~o$zq@FwXZIKe%;x>t6IP zeV*Ry@1AxJJRuHH?FeplJ2H9YYCd}w+rU1I?58}s&DOBZ)rP>)A97B&BG8aj4@_q0 zB7KoRlPNkEyObfckoMq~&M5pp|T2&WH@7zfzg3*uaQ?9veZ~QMUXb4><*?lyz zx|N!Nqf25uJbI;>cl+g@zd9n*5nz#pi+)U7u^)L-?Ui;;=wY?s;`{MQf62kT+b4Bm$EKdg4*B6Oyga^f$wkjhnTY9HXF*Z&_oGV{I5XdOk1@ zAHL|h=9y?mLF24OIVd)z%#pRIR?pp;uAD_lV~J>Pm5>5)3N^-2w3%ex>z!Oi5w0p( z)R7T35%1kuTY;i~jJwGjnBpiE9K>0SvMtY1)ZpzTHQpm*0p#2!!RRG_w%t!>8Ef_3<;BN3 z-V?g#E`&aE3?_wWm@#skaZO+loaoZ0Zn5h)XN*$M!pT`24W|T0-{22eDh|$75HK>+ z$!K`$Wqj)Dcb~h{AJy<6!Hva<;|g!u>ziTmC@>aJOa|c6W}gR4V_qDC3$x;jKhDVu zj)xb{A;^Vu^~tP7nmVNvbrC3x^U|2+;cD7g&ZPK)gS=Hf@di9*M*zALAPuBDt>+69 z+9P+yd#%)yFL-Zd*N6ZMePq;_%3u642FhL27v$$MFqf>nCemt5vZVg>QcOYhp5 zd_=G41A73r0bt6>aRJqeyt73C%Q>lM)8?)ln9*}O6TJD8L*Z^^k#J9+Zw`Fv`KJ%h zUGtTkn#kDw$Vcrk8O*LSlc?BD=Oo8xZ}@{gbmL{un{Q!a?oRhcLF-~$Y(ZgT2hvS3goPVhwS#h7xCok{ z>RsdDn%FDlskpwJ947p`*~RVQ#9k_3BTIq)qk2J1Gn-`1E9c$*WBlLnU85Kd7|XaVk74BX{U0i;b(y zJMxcyx>3}df{Al8ip|$=I_Yr)aFWHe4(fK@1sFLA%3gl?<)^`%B&4jAk8C}<(|Q#= z=6G87F(9AM?42>6eB6I3&!PlTo%5|L$0iUPI+OXPbcLc5t8*-r2ptFyoW4XIwKe&7Fn5;K!I|;yl~=I1%*{ScS(X zWt6B!%`M^ZY|e>ei##U2jRf`RezNhQ~ProQcmzZ`k~&6wgZd7%qQ z9Ve6adBj1Vx`>dDHt;;sp+AC4&TSqU&{rMi%msFa#h*UH5j+||FTE6y`1Gb^H_q+I zlOE_hdj7|se&_J^?UAnn?SFO-JYf!qf!=!Wz3LeytFE)7`7IkmhMf~_b_EQ%?;~Ge zBiEFW&VZ>tFkngQk^kP!NvUg#U0~Qy`X3C3_uM&~+O4<$`TTLKndAf6 zdN9?4kkQF$@QFFJhduzlJGV}kTuRs7Hn z{+&*Q^?fFeu*SO`>Dcaihaa}=H(xuOjiBplEFS2BM+o&%nf5zrZ`WqrwWV+PE;d7R zV^7^n>SBA!j5C@C-Xc4%n?A>8Y0uY})JrdcH4UD$oHp{mZ8#qQi%cU^=#_ zF_nUU^2J_K-{oNJ!bf}tFNVY8)i^4=v&&7B%2K@6w{=={cPg z_L;Z2(<*n2iFVavQ0B#ER#MUCu4(XxV1^!y#SAO>3{Ugp;21m4s$I@`9c>Ec4*5)u zlxzEeiCHIhEZsPjoalMW;H%Mz(O_Uq+Tbx6HTk`I%# z{FR|o(*lR~BQzL0W9YhpT4Ec27@KoemIXrX{{nL%%KU|!epo%QL z^ZuQ~?|=XO@L<0a+Cks{{6aXu*8Cu{S9?yrqdSi*xJ8C%QEts(ym9#Q3peXq z*~Oe}vO*^EiyV=i`mf3q{~Q|{yDG7@z$A8>SL^@YCht8;fawm-{fg?Mp=JlMO4zSXh-XuR-3Op4boCok%& z3fq*vZ?yN*y6sPT9lnYkn%XVx`=^1)o^i>!e&b>LhMsAsH0P3?l8Y`s{PnN9VrS5Y zcPVR|>9a9YN7G`Ib-@PC_)UB>6Zl%Z**pf5cB&4fQp35>DribBf8D0pIrhp|?s?~ZUehZ1cvx+`;X6jnCXZ)_eo z=N-4ewodZ&pX?7k<7d!MSLhM^Xrxun-AFXz9gFZsmq(w5E<7t6N)jCq&stE*;$GT) z>7|!`B~_$N3g%+}`{{CEXYNPy^KtxnT>dGxQ<#g_u3fv7JH`3(9)~gpmNQ_`B+BX; z>`{OymKAN2nJ5ScA)g_(iq>bjC|?XOC2~iS(m0w_!{aV+Uucs$H4v1s$~MN)ftk!!mM^IL&!D_dbCx2ryYAkh-5dhCgE7w02&vHk5o~bhF&BWB5qf(nA zLme1=2$`_79>c@1&cseZaK$+=OdNGeJ2AJr;4D9MK{){gBWU$KMk_zYe4M@d`2}|u zGD6E3NqiV%=@~x%7^|!91Vfr+8)M%juQXZ3lYCBJo24lQ^1VG(8(pLbU45xh@EOC_ zO??KkTgK8JzSZ$#S8!q$=W>&QWCpwlx6X0~50*lNBM}b$4r|@_d&BUDy!8Oi< zVV&*s@ZRw?3b+Fswib>P<1FTRx}gP+dUqxeXkp%sCmiH&O$0P-;fYL?z74K@NxN6C zU+-=bv=Dg?*v~>$$0j(hUb_~Yvxp>M(x3T!jXJ|;I|ZCueYYFJq#NJ7L%=Z{C|}VH zP98pyD`SD4mDbn*u&9M@bzJMSa?XvAlC@q3FFul!I^;nAuOHk#+D`j)_Gr*+{E1$`Gr&f%$#)ZT^;Z8S8sRanD>m03$ryC; zAm?3N$hF5*(IEz?9Hi~JPW&xiOWBM`#${_CrR8<8rp>m#%rZ{B<(mxx>G z^nB&@n6@8V_6Xp9@e>@_8TS(``Q#b4qHIF=2tQA@t{k3!{`r^Vs8Y^I&jis#jq*^i z<=`9{!5Y`?oMKT{O7{NkOngnoljj@dUG>gH&qQ@5ZdTu{OlDGKLX+;8oVw;*I8lo2 z+WA=waGwneK0#fbQ|br?8Een-&}3WiC3%#(20YgmCDifkn8_{2!sxq0EFryg#T|2^ z44G|X`pI~3D(xFXlH`srBZdcEAPL8uLsY|``W77EM@O1&QoBYxaDgs7Jd$=fd#mZ% z?X!N#@8mLuZ%sP!uwfh zYw%VIN^(mXr2B<&LRV3X99z?kH#6U>IV+I_3nFzKltuDg_~CQzdHwhaU6K--S-dQ zdv6x==m^{DNH!zpwRdE_DYl!QMWmZgn1#r;ey#;x`on&+|NIC$(8&)y*c8iSU!(tr z!ykPAo#@XmuD^Rrf3-NkhyBghUy2l&NRA)sBs`M@S0*NGv&nQl2J<~`x&AMi^>=)G zWh(Xg zAwG0#DsYj1{yVzTm?`B;_5qFG4pD(Cb)gr$SV(G{Dd3~{4t<21KW<#0YPpd-1!ztl zeZxbfs-RD3Z96@817Q|E(8HfizpJHfWAOoDaQijZ3Q7R_1pPd_Z!#bM8$9ASt`#2^ zyd(Cp9}he#n!dO$?}MW}2_1GBb)u|!fr9`5KmbWZK~$VFxQ$bB1h_F+U}hp3er;`N zFQo|-#*oiA*bdH7Td|jFuTmxeMKeoLqaM88b#OFrfP=V`dj>mj zjA1-PS7y6?J?D63_48gPtzM`DkYn-8ULf0ES99~|LZ5pJmQF^4v(79im!YdeiZSB2 z7Vk`gJIN0&ju}pGA$^dJ^zIk~oi-URPMG86aPeuK+c|;fUf$OLB5iY`odm<1Ydq^j zGwsjTD?D;%V;l#^)t~?`gG;htQsWCp=-@mXbTH7tk7XUJ;6Q7iqf2f^FC{ZBc$(2q z-;7W2q>_NAvXsg2yrd|!q;L48Zw)|i2zcQIN^oFmx(whG>;o(Tv8VteptBS?B*FsTQmhnK>_bJKJKE- z&WXrSaBcz?kO>-%hxYGh%q);;pDy{qBl(<^Q}2z?t}&*UUSQJ~3>gDF2+;3+=iNHy zR^0#W9QXxu;9m6We}4Pzh~fl>Bf=5h$TR(*eZJzsBn=i|j2?qKs5 z{DC*Oqt6{%FF@{%n)GdbRl}ow+6ng7<+IR=E@@40`(j#pnR4w^w?kX1~)lYb5BAcdD2G8tn3=i-=FJcaBqQboQ#A5RH^fceSztW6h#OeEgKEZ- zT_1ca9v90Ors#)Ha{~(=i#Ki*8dr-&;{&uU25O95>e@i@cn?SC0T`2C7pX#l@qmlW zpa-YKf{lI2SahQ@lbEmZS76AIl-T?Wj^XzZ?_x{AHw;)A`|)Syz|P2@nRR6SV+-QN zr=NcMjT#Teo}NSjx_TFb?3$9AP*M&GbbK*gtb%{bFhv7XHqV_Q-HNUSgDMiGin>rZ z2BQWx8Owlp&c-=d%4cT)W8^xjSR+#Bm--Zv!gs=#b~vBA?k;IF@Fvx0@hs1B@SLQT zM-x&sP8~}vM^v^_;^X+6lxb#I3%~`=aPBX?0 z@iLP7ykW)*mnas;V61xAKDfN(gcH_aC&inSQkOADOYfG%fD0OK-Fjcatkwr7nlKc7 z3^5!QXgb!(!w!5R50~>?e|I({7cRKFu)*ntWX{ld=9!c(gtR}#Tz@#W{MUES*w@ji zllJlaa_-9a#fxdHaN$WtspoYNfi>wky}LipNk(q+!b4-!?>eVGI?&ut)`T$s>MnxA zAmW#PqR$r(jIphu7Bd>Kg`fs!l?}Wi)7!X3rz)}A`1xKrxj<+GzmUw)$!d1~!fA{f zBz7FoX3;`WGGlK+h&+AshwmM}@t5yGxTl>1pBV@K=zH%T{^FfmwTYd8km=2ib-Qb> z1-@0O=o(vJMGnOcl9N}TL1$wFY9QEv=oTMB=c|`h_nZZWJ^7#Ce&_J+wlnNA>-^sE zXUKtvhp)VN^KiZIkg1T=53t{{qNOXMRohzWi_G-Jxcqhg-OpWRD@~mQo5=BR$*+Hl zy=99{qRFlaiX=YjBqg}&_X5ve6KN9`c@Og`x<1abEdpqp{g%i(pTpn#n#|e-)>tfM z(%iW>`N>2i{sb=md409XV#WGYAfGRR6_@n=JvoYPEc8qDQviZr-`3r5Is|VrTfcD( zz9deY_XJIx(AXexRpYtfrQ3}qb3T0=ybmp^r#>CNn$I$hA4@;NKQU1!UL0R&apNeU zv+xqWTXb)Myug~g^~Oi|-PJ%H*^=@VCzXQGFAm8ebO%r9T9a+QnJMA#)oC1+KC?II z^xh*jtG~d`WIHh~c|c(5Hg?iHKX)rMCoObX$#o+_Ut?byZ zExz3N(JxE{1iZBBi=ohl7WnW|f3-`7y5M0fQ*&at&=oq!UFZL4SId*oHD^-;=y_>N zaw)I0r)}|+c#=M;jK64We{yI%nYQJ1qTT=}?FRxZy3|AGoR3^q=Tg)Kmy~yhXyn$o z;g@TzxS0rV$1HH&nUZ{RNLD%@2)*Vm_|%)3T1x%tn!boHrwzQxS;y$2Pb>zOocf%$ zl)L^G06JpLm_Mi2&QGsT!1mLR%YmJNACFT%!KXNy=0)K?ShlX$u`NVFeq~lk$5}DX zz0*l4V^HU_X=BVN;~2dtVg4;6L`!BdaogIH3<^h~;bMsDU^uKO7lrNWZw(iRncOHW zM(1&_*Mg>JTm8EY@CG#O9tLu!n+y9ha70gbU%1ZTOp;n8n& z_5Bh|f9iH<0Y!HFGZq!{zbK z{C5Xr;OND~Vv+y&5Y@#26BD?Rz*l;ioz{XmZB!?6E+;)0n$wqwZ{oE=l(vY}=#eQ%ZmyVOd{<1U9P7AQX0hUdH6D+j zpN(H{f;Fy5+W{DU1hzaGKRYp(#UZo>C^W0*T%VVXQclL&X8AMl6TgJA;52TA(b2?I z|HzYi(gZw6?T3|IUf5sCaxo9>pWuZs;dF*an^DpJ4JHX&2eClz#UU@F3 z)j6rG)%K2j6km$er+b5EkN^{#mM`IZ<6Cl*M%C;1LLRtT3`38x1r*Uqe>8MkeF%m(-+c42Wgi0mOP#ed;!_;n2<{{NJbiZM!lg^EEk!bE z;CLt$17Xr)do#nvIMpacff%T!z%gWuZSPc5Kn5p@x+c{ozpWdX`HuS-D+Y%{G2h zHOAV6UB|nfG z^Zd5)Z=cQsn#5^=b7vfW=V)u+a+Hq7FtmCPz)b#3wB4;39J4AK9yjP1THvs-fvzbx zh|qIVX)}{%2C<*N<2=&`?K80b;zDo_1)Ng?_`q}&GWObL&})c;f8=0Zj>H+Q{L&aC z+AOvlI|ga1K|rqYxaT^PYj-WWQz0}m%El5-Dde{Q9s!vZ=g43>$Z>RX5c)ZG?W3vf zrkt#rWTTmrw9^G04H|-U-HiaJ4tyr`m7e z-v067cmMBq>q97NcR|(Gj?Ok9v;Zc!i;hqrI!GnzXmTwOrt@IPejiy%Io(rVC3gE( z{MGOL(RcSpUq1U`|4b=(>Bi;57higz3qt(e*lu@5BS*2BCIxo(k+FFc&H}mxXJ*(W zDvL?E=2Pb`Y6>`+yd>a$CI$EI--#cv`)cl1!$U6P8b)?whu30)2@Km`ITi9S*7?bs z38QO1Hf=_Jecsq31L}c8t5~M(7c~AazMy^qTyy|j{WRYayfh+SA%EqMZn#7zUuPjf zIT|}T42(MUca7~v|M-0_w*EhU7oCj->%-Dc%9g(ur-3F6Se7|uLbMf2wID8X7@yGfRB#yaAGp*Z&OEQ#%pN&s* zNiI+VxV-M-$czU%wSk6>O5mwz<&~+CQ)8>G ziM{n9-3mX%qo{O_*T>i@`RJIwi}A&1)1P)Cddyb@Ab7PsW6{TawbTBf0?#J?&qc50 z@-M3DN#Dhgyx0>fe#f-tQW^Vp=U9-FjPoMqG6QMfXP1)7Af{yMwM@BWjI+yf`Gcj3(8@*?F zNug)0Ydbb)dQpG3)j)=*&jcbixV>Q&tgY5gT(ebOp-rB4^o7wIoG~uR>$9%Ge->vE zG))a=+utlkpdsgQtp$_f44bqt2A(8);8f(5)%?^d8O z4T41n<67sOJhD}%m29#?E-E8iUxBxu=nd?ecpL9J>Ktd~eell?f`u#Nllqn8;D#SQ zospl6F1EByZ3lnpOu4c3xB)VZm!zGUl#k5ePj_Qvtl*tVet48hJr55$zUqcIkgZ9* zcG6_ahn6X;P9}eitu_|bGX`sy#N---+gZH%y>I|WcaPs} zF6{p2bH;(l%2!^vk(&y+J05@4yM=XIbsU>7iws3xM#jdL^WEYLi`*>g*zx$=%)~8q5diLwa0%rc0!hNx?(7Y`!=C+~PL+{JJO+UP#kdTj#}z9*-N|fyYZwK~r3I2k@h~CT22577Qi4 zPV*fj*JwOFqP@72jTf%<)wuOKc=%BC__xlD&w}Nzo!8gqM4K|u0f+tuL1Viv;^$oa zVlhE^C%QQXqB~E6YW#X&luz8X#SFn+TGEe@HS(C6-MN}PM{!)THd?{B=`k^ZzE{@5 zFZ~Jn!U1UC;FX#B`X&Ba30;jfa!e7BV^U^Pt?Dst7huy$;ng<;;!$HpzV)wi5I}sz zFR@-H{F^+Y4Fa+y(U~I5p*saqZd<>xRuA7=Zu}!3cqVV=1gVsKayerkPNclC=!O?> z@T3USYdWKIjju|9%NajllcxOGMhZOYAf@dq(M^y9)9*BW@cQGHox<(s;lM{R@^im3ZCwDUKv%!{B%1Q% zrMO1eM3bWUF_@i*=h#X9W^g$L6(}x6b@YSB3(;yQ0^<&@l{f~7(}GJo%OLy2S)bcK z8jVf4bLd1ViYh0B6(<7;fr)sXTH3VYZ#(KTME8@&fIY}g2SyEsG+QG-xLh!3F}R(` za|Q#4wx=#JMuLW+S(!Gp>n<(>kM=@$IB^@o4? zn^ss%Wo`y zynIP%h}`!Df5E|DG_EObct-B)<8v|ejn79$>*u&8`}&sLPSW_c`oj9J_>RyaO<&@# zlInZ;t|Wd?d(Bffu>o{=r9XV*u`luU<)aE)0;6^9OKvXUiNy|dl$IPPHk^Lq$K1JJ z{GWJHOsLPk__SgjIQ6%DPaD(9R09*y<% z53I>09N@s{=jq};KI{4Lrc@NBd<^`mx0=K<_2I4`51sJhzq{-0ORlB9*CD6+BL{6A z2S}vAHNH-MkTu5ZLV{R2#VreUxZ^JS8ZCWT2^?CtCKp?J4Q%oeA%@ICQux?Kkm!uZ z9Lkdo2Y9#}v*l>4#WcfP{GWv~{B0~4dtbQ=dT^!J;!1w{j8BcDgUez-Z^+?w+Jm!m zhSZbbvna6uUqHTSuq>9aI^4a%)jnPgFFA$4uFi&Q?I=8%Cl|c{Ld)UTwe^{ z)9Z7n4?p`Pupf@$moRr{$|oDtpUSTo-)ea#Q;)z#X>KsA42`$OGtkV7yZ7#vdxqV% zaKV_#s%K$l(qi@3YBu8-!*1dp#q4Tml&^VF@ThVPC}&mpv;!~YGnr?Mv~6XXs!@7L zIo#uDLlGyGHvH-o7v8|$N!}Tp`KO#QWq>7)j=>MI;&!LVgnBvXzT=N!Vjx0?dJi7V zmk8_vsDaqZ7**yaV4;hV8Ry{namI-uWWYMf4BQoyy8xF*co7=)h@vbq$KaSDe z<&at4<~qR5>ndp4u@dh2F#MI&!BaSDSYv$gzeX)Mwbh*rg^Qtc%s+ka_BVzVqI8JE zLT9%FCW}+;qDBC0y8_41qq*BCJFX)mrI*1}U(gaBfcMgxPEK=WLBxxI^bwpF0pcWw z7sfRo|GX%VQ!8D;;fOYJ)92v>D@|Wg)i_2rttMCQQdWnDFZqQ98UH*MkamqXr_f{g zGqO4k2hZ?Ed+tEq&4j|j3qI5Vr=GrX45cn*7B#dXP}C1jY#jJ(RqWRS!Y8=#_+E5u zf`hcP;hpWM?xlVbgs)zKzfnP=r zc;V^)`M>?+yZ_5S|HCHH=*0OYy2)=gm(41%w+8$21)>tYp#yZBPAotzpHKR?KYVic zfBLWf;O_U|Vf!8i|6|y%>Ys$>8?QgUd;2S2n$7jHSA!B>ByT&p@iuhwwoiv8Q{*a< zB7}r*E8D!mCVsoeU$D2EK&XgoK5By#cDl0j6JT{kHkEOHf5#qp{hthTG<=7j31#W% zK|9g92Jd^s{-Ls$ADGV%7(Dp4ZM_F$xEwmQC5Bs!P+h+JS_K|%>Dl51eoNzF zEMIaoi6JgLabMi4%*rd!x$F7-VjM95;`nfWaF7BCsyY@uT*(I)E_8XY=kMcp^{)Eh zqm|zuAG*eG?Wm8(Xyuy^&Ntj#)aYd&MmzZ6T_w9#Odnk|J*Vr2?`3b`0nf!G!QH}6 z4XC?`$?T81ICgl5FK7;jcyYqc(r7AWIkG)q&`&GCGAUaCtctx+)n(ge0bvZ4d;NU5DC#x6Yuu8uoV6;eCu1^`t@D(=iVpP z@3WHbXz?NaB{}d3X8t8%{Hj%Z&a)KY`T30rpLw0~z3}Y4XRmUUmjOG07}-r3QVMUs zG%%$6n-I7`Vo>+Ye#aRZic>j`?hGDZxO=s?+3qEHDJ_U|SR24o%rY1Q2m-&UfD|gH z^KLVX6m&+P!l$S{vj@Ji!?8RDnbMmzKF<%qdP;g`k}GfZ$x6HlhW9F~&WucX422aV zhINBzcCPZFbu(_a_B@9quR*ND?aYvN^#3IT+6ciDIGZQ|^G8pf9Am`LFhrH*thY+t zx$9%_BP(2MZSAa$4B}#!3eCV8aNha}&YT>_0LJPD{tV#t$C)hA1S5xOmvL9YB!%M@ z*tj>GklQ(0{a`e-@mTQfei;jLa?U1j_N{tItg-%#m*+D?a|COiyE+C}TL#$r6wkF{ zaL>Ug#ew0SYH-*LbLD)oK!*Cqa38rK3*N*mSW(}bh|jZo!LF?5H80dSN;2px3);5o zt|0-*oJQsG1YhT9yVoN!$*#r@U)t=atR&%`LIbSa<&&ah1a9UwsLAK z4Ej%e$yXW3U4L#;C;0NzMYsMLUm`G5bzzqtE9{=fd!-M{$L@6T?fa?@ujvH&i$uSal9tQrJJ(=U^p=_9}N zq=~=(>%aKpyZ^)g^IzWm!S{Z=fOpEg(tiVg*wNY2zX8|}Apd$ekTCsLi*v7J@_t?%q`_LkGO;_oXqbGx(3#YA-!7C>pWP*ee8%c$uUEfZ z_M5T4{3U-c@A#PfOMEl?(LZv1+L&(RuZhyNJ;qUylau?-O&Q3x7_3N5%SS zyEsNwJgWR~M`YmmUp!x4M}s>%V7DSJziaRp2S&p_u~<8!*+5j8l9*Rnc&@4PuEFE= z;$wX-jXn&o?qOx^?s~UB{<9+FFdlYa96~15x!O7lK?i1C(fuO_Lkn$tH^t!*+1uny z)vcC~!vhvLX!i$bz)wt7yN3nKJ$ImeaIdAw$*wzpuR$?BnCN=NM9IpfYsDms6Co&- zuix-z_;#-U(TJ}J^ZG=537;3pq<$;uD?0!d<906p#HtlufkVsb5Aoa4uOS+=6E9W} z@Y+*%c!bO0hkMSMQj~BBRJtI2^{Zd~&Q7X!JAUpVI6t$@=T`m|zHuzY zuP_6C3h93pfx2J!#V>#P%a3}QiCuF!mt)u&Pb=yle0YrP3n?n)-8W5BgJbZ{Kx%`V z6yx#ZS1bSi-IJc-f0Xi?wSDx;GA4EP?s4s14uf?>4gpj=gvAe^E!i70sjo- z&MJHA_tH_uvJVc`vde%m@_YY1j$k?-E|m}G<6Ijc)h^>@HF(?WUTlWG`yWd}NmchY;S3?P!U2d!8zOzPFbha<(7bz53|Y@n?N; z!HHk!!za9!xF|F9R^0?RcqYJ^VICjknW=51Vf?%8RNzqJ+Z0l0`t#)` zoi69?{tLloM;!$xXdTWs zxxm#qkYwJyO~k-^4kH|bWrZK!<3O@u@<9)f5u73aTv9Ll-Rc5|Mla|U+V(h3IFr{+ z$Z0#qz~lQ*-@E&7{+-o-&>zwR2fjE59;I8aw@vqrS9=+2@;RHD48D_`{^05R^ABv| zUp0~0fBn55-Tm~JHe1ggShY3ypc-V~1l)rE@cBV@ z;Q!N$F8}!-egE#?{^+mwWtW23SJTt4y!vv-Y#195$?s&(f7s;Ov-J64itVo|0e?gF zlPe1nw*MP^pBL6H#GgOVDqmorZGa^(;e*(26Ha@Uf2e&tNc9L}9{{+;8J*Wyi${)Cpi)_JYSYT^8sx7=C zA7Joe0Q@N5#{OGi;VpdrkAGj006qs;)(Bmn=UeQ^u= z7NgZ3oM}A%n4EjR_<|1xZ@%r7VDX{!Hah`-aSj~U4qjE~g&vdiS-tHN#gX9F)QTYl0#%ppAvSbJ!_0`4O;Ak6v>e>Up+GI=>@-B0eQTi!#>B3^1@W4a-JpFv? zlPTBI>8*3F0k;2n>bfS@mPrz5qX`$pLSUl>c4*uSro-n%P2@ocU5v+=SJwbs%z|Ie z%L@Nwc4y&+#)z5>cJH8q?(n+x=S#`GlH~p1cKDOH-1rLme{y{*rS>O63^8{_59Q?22Dor@y$2i{B{V0%|f^iJ9qpO)ra(R za^P6upR@2ov7h9CEhskuB!thCEi2bnuqFV$@j;{88K4B7@%OBYXZ|S=p(oo@&MqkD zoJ)BdC{n-v`%-}X4S1`=xiJ!(CC(@;KS&&&!6dlKzs%9isbriod}h5F=c!Z1?=l!O z6D)ZOKVwp`$eA!IlvBCQu-7Id{&Gss05OicZQ#YGb$@v0NM3k+w7aThQ~pfYW%$A) z6S5WmV-T$DGAh;Yh!5v~{3xJ!-w_)i0d? zwkX!NFo)FATc(jbpg*WVMU9M|;*^Fm)a^(Z7Npyum{*!FI_mb@SmTy!^{>>$P{D z{5YfEt_3jraYXPwFGLDrywr0a1@Ccnt>kkm@IU=15HkTFxVt&IaC_;+<7~-1{OA$f z$eMt&#@3EzwQI6v4hAIfYxg|sACJ~`l}|}z*(g4y!q(vKYH`^yWjlUm+xM! z{WpB8yc_hGZTO(U#E*M%><`{~diO`~ynpvE+r99|-)~0{qx+D4@f=_)>&tIFt}l~g z{_Ta21n}A2&M#TX@+Q|^z0W3doyQ+Dt}i!v`<{3FtW161$p82bzUSN{ENncVvmIC$ zW0sw^r850K*Ist_bdD2OF7*B=dsJFe0TU{^c_FX_v;UBDpNl+vu02~e9AY0 zU}9o1mScWx@nh|P7y^e@JZ@laVnknnDOj;mzt1Xm-glc%`_|@E+AK zoi5JWBY($D59S83d~&hT2F`Hk9OL{uF{;Igm{>B|WMRVBpAMbTtKaK0yEgSTAHLW$ zSlfZQSSVN#urMz0;|E?8bnF$~%B)T){`UszR9gBJ#I2p z95h*or`|md>XF6Tj<59Ms)sG)Zj(;&Ok8i`X$wL1*O}a@j`*;47h~ad*H=4qhK`IL zJX!zIx7wwfBlEo2Z<8lsH2i9ud~c`9jVbGQtO~xd(U)F*_3pLTUi%h)NFJ%KFRtM0 z2*kPdv;Ha@#~#vWae!5ONPoQ?xG$VLHMgElIAMmmtongt_cC{(bM^d2F>ARqGM$W_%>@@IR@(G zh=E%k>EM{rVE;j9h!4WdixxT7&LLVE1N1qy>2{7xnd(@%$J;r$D%b+Z2p(Rbchv`a zd}+r6kXE=kLOiUIIfmmTUHiX3t#Y5OhUm09fmY`)T3N3TIgOW^u}8<5+1GD}PB`fE z;ZgyQVQdiF#DX9RJ)92%xR*eN7zgz7i;uST{@N{ZoK=pq9Tq2ez_-&!o3OEhQC;#h z{zfyy&wheIKJa0K=y1A{zNyyA>CZFaVWQ&fGSLA7jhE{?K@n#$zUjjfK7JSvZ10Tx zIdC$Cujse}Dg$qU~# zDbK+RVhE(Z!UuT!cR3|a^jUpDZuBKQB!Q+C7{ST+pMG%n`+t7ENxhp7e;zpSQ#tTL zgY1{?e(&u!@BTsCrQd$zHR_X&B%l*7GE3HvMSA`5EBS_(?!Nw|$9Mn9H@<_|Sgs2=@b&O;VRA-0 z=-UKOJU?~yTMUiMFTj8z$ z20b`ymwd&iGiVMr-UR2o@8tC3B?DWGc^8~wfq12?msO)y46wQe(&~rD7T6#m&R?1IbYVzWXg8qpM7mp`H@Y@7ZG@I-hrjUD0K&P@_>|m@{?vY`z-1tA zM(4WwaQt~W@Gt@J^K$A_GbH$#{LQOK@st-dA) zDAWd^L8WYzTitZ0fu{j(Go1!+)!}H}2lx4^=JF|Z1KZmmhT?&x9A|8G+O_m@)6&;H zMh*Nae{FJ#8?;qVT?46^jawTW!;D6T;BkZfC!UcGf0GDST&=Deh}A&KpX2YGA>))N zHWY#yM#m7a7Wl#Fx`_|NvU+HkwdvgU?#*Hbe}=I}$Cn$Pcyl?+kNG~9Y2pmTN55da zHiFGS`3E+h!f*W`AKb{e2B{ZMAUA{Dk16c<9me#_q!3>WABy~I^q7Srb zfNOC3^uuRopsxQznvra8{mMbc^h$W^-p=)*#!7Q|dD7#sh(S*NM$2}{=-2V$V_DWdUZ4e>l% z5_)ZaCTEuqJwB>OER!)ZD0Z;L-=w$l2EQbA69MU`LH-7g`3rt*gVhS`x%sBp>G@)x z__Wwz{yHC?KbtMM`J>?$Y*s?@7~o|G7EfrqvWmlH_9Gj?FG*n0pEF62zsgVDpP1J+ zI9vUVf=z1hsZB=6N9*PL3^ds#b*=?h*Zk)t+(x837kuXfRdit#YI#EzRRIs`6e(8j@wpFX^@=u)j+zi%*qCZag^+R}()5 z!2QFs9dA6OO{1ni=jdRzmUP8N z&X?7Fiz1`5LLY56N#(=)!*F=fWI(vq(}P8K(C>N^QPmxdlb3QUs}1;xsXU@;C&Fl6 zo*kUxFL_aV^GDvNA+_rNR)^C$%h?~-m*aB&(~QvP-v91*zx!gppH}zd7XJ>PeVVy{ zhe`cQllBul%0MhHgY@Zp*Y$N=qmN#F_1gxK5+UDUv2%)W8DGYY(Q$p9&rzf>3_GL5 zSe|r(B-e}(6?gwATj343s&60;_VR0sv7*IS-UZun+u=Z^ybMQaO3Q#%=NLJ< zehj8M*P!=l+xr-QaGySD`{t%IPhfW$kEjkWd@!iZP_z~Aw8M~~;|wxA^92^;WJdtU z{a!PaGk)sfU-!|(NnL>@e8FJY;Vn5{G~gHfBs56ZU>9xbo(Y!*o#BJ#!++OsJ$1li z08bm}IJ~-etgj6+wFTYxI*458Ru9?C3EExL)mToZ95W-U^Mr{z*^m!r9rwp9cIa&$l$G;Bv?Frai+ zz0Xnn2Qa_AIH5A)ii-vVUv(B|XaikcAN*w%U%;J7^PuWvIo1o}zj{!XEonPWPb@z3xWbLYJH>lDQ8cj-}&p8@EKKxz7 zr|b9;vwTc2OgMopBsOL^OX7%y!6b< z1uivtd%;YGi?uk$=&UePLWY$woI>0`=^DI**DbAF8x_k@>xbg%~olbeIGJ!S2 zUZxdKtG@!w88&s+j%T_u7|K-MeY0=-9&d)jxo4#Zr#9H7ZQ0BKnFTjMpF`~iKg5{I zN7JRm%owBKpKVzKGDn-uF#v5xKm?TEAriHruIJJ}>iB*W3N=*zS;4i6JDMx|vYA<2 zMtAQAVC*;Otv#j*EKGq9e!Q0~!C$ z-*xBUz-Kd=yMGKRqX)M7@VJbtnQpw6w&K;bcMNcLXvAOifbYjSz)f(%37;+R^k)J@`AtaRL$LNgFz8-e!Nec^_$)df(gO#68V4RFbN`FK z|Lb@ESzFg9Hw3C5`ALHph!IG#N{-nD1L0{$MJsm7S62CHkV2*3`n50L{g?mfH}8I} z=lEA~xxbu${(BAj|2Mz;t)B7sxD>y1EC70Dzaw;$ms7d!Woz7HM;!5HlL4-FFI(90 z>#x3c_g^##@Q=RrwO>xcAF%qR;lR7aJWsOueC*;GGI;Y{iPiinJ>s4Aa^Etq^UL4U z+f8;PkMg9+C7A@P-zLn<=QG3@e6k61dcSzCYG=|wU9x@P@arZGTN~+FRQv zb}?DbZ^40XxK>sY&pCDF{uVGgS7t9C>^>h0&$Ie|TXeX=2g5yj#{fB=7!x;6Eaui? zM=|1P&KFh|ebU;G{>ul8r$Xk~y~-WXpyB7?DLfDVF29YZmAK(GA0Ln42EbXc($>#6SKVZt8FQzeY>qfD?m78NYtZTfc?UIpQ;X4=#Z=_*_|7j}hHFlbBtK1!@bv zFW&~i*15)~WW$IUETpf#g12Fe%k==r^4_W(KkSApD}*j4~*)3wRGbEjVv@LG6VdhbQ=!b^z@CJS}=o&sj zGRJb85zGlPP>*gePn^T;rECJny`&{tXN*Rk$&MyIsTdNv!^p}qkYFieAbx_7Gua`3P9XUEelC0}4;Pa=hlk-@-!2%BHZOaTIJr~z z;NitDk9+xwd^G09>fcu0>kn<>&u|Ggp4T*zYM0D(9{yHSI4Zw6^U7R-&zw=b1cTgw zd;0Rm-(c!rZIb2TR{bj|AZK7jNcXYyqXk>^NWcT3@v(^^=K>MeaiiTLux(PHK6|e| za?d1!AWWOm9Me|%1t}(mJmVTagZJZy?f>J^!=KOQfI#qn{`bFm_YdFx%F%D~MDCKG z*|x-w1a5Mve0OJOZ`&-VrUX}i=Vc+yw%JXCAm$CtDe=&MzjHRhR%DZW?rrBy6}>ZOwaaG**!oP+wnG1}fA>54 zs=+TS4<7LP#dE+8fG7EdqdiCNHXup#uECxa%=xwC@AxfSlFv@bgmJRV{+~ftx=C&+ z<}?zGM>FN{w_=G6n!wGsk@xxP&KIMt3OFK?tOGyjd^X^~n9m>9#aR4PdSKv5{^24S z(7T4A9I#W9>52FDz}>2GxyR3jm;3y>xMi^1rF(Ov0SVVV5)eMl0rqvF`QXma0+Luw zd{F_k4fo1-AzqN+qYb+=pO-CDDc0>wUVIFP&g1ztxHkBd;3`&gWE;g_b~bE~-?exe z9*bdXOS~{&Uko!k;{#r*i_U98!HT+T3pUHUO{Rdg!EpJDT@VsnGJ;mH>`Y*Tu0?}@ zuYcjBzx@gXRjev%JC8ne%F}nRjw6c8r^&DV~hs zI2&T(=Dja0UR-^Q7uv%Icy+P5>R&r803loOr5jsZsz0~LqRQ~t~-A%p<4(B4q+hTIR`1AW^-alWNl;dlpn2|Er*jsE9_Gf)NouV-4 zRX5l4Ja8A&frOJNv5E5h0*1lJa_N8vzHsNQ0y7)y<~@hxEb9H*}MCm zs35DlDJkb9@R}j-Zp; zVN`vFseh+UoG8`qCI&M8T*XP%H%Mp9J4$BHXJh&^BgR<=+lzX%^{C@!H>3M;hRJRR zpB{?Bei^jtd*Kk{1P6@U06W8qZZKvvalCUhvH1)cp4)m?_WI&ctK@EOl{p+AHQ8}y zLh%{soOwRLV-Na>(SN0PFfe@j3tWA<0dtPXtS217AMey<)GP0e_gGji- zXD7g^iHG#SfuG>O|EOo(f49LealBrfME@5U-fRh7CWB;uGMpZ;b@GU`NmN4Yo?2LQ7A#bvB)5 zJK2T#q6qUTHZPk~#txoMf_I+fiNzn@{geOXn|I&piv|zrfdik#0lM^LUu?08Y@o(( zq<_gHUnOneS!`fogSPP#eAMEE?$bYd=Y_WhNZywN7T8biPV64JrS$Xn)qmWV3~##y;wF8Ha9V5zydBsggF#(g*kcsQZI)A>TU zEY{R&I4QSZCGFf4iz4E}0Sy+13}{augloW%qX|6RKs)&2U)OkWU|KBDKebi)@e3lA zu~38dK&*~02Pv}wWqrCy7I!PZShqgeu8XoPmMl(yhXVdr`HEdntSROu8R9lLRj4$( zE*9BiwED_o#<5*z%l|MsaI1Z*wz?V`FAu02Onj-G_;hO_+)K&qyTx1A%UQk83y$#J z&av7&3marhzhUT+_3d;NzZL(8v%tn{@wD!MPJa##+-K&`QT$iB$j{_eLQL`-^nJYj-*u}QuiF+jMWOH3O7~efxDYNT$8Pm%67AWH&AOrNw zItL>ow%NSO4~NQckd@+|RU7b+vEc+??QQUzs0g-63dRIc%#VO>z;8y(Cw=UH-~<^3 zkY?Cx10RN12S#qrGaT(MumgY*+h^-C=pk8o^%)G42nN>itiiKf4(<$4kMlqujL<*y zI<*U+W@c13n;I{=9bYq!wQ)W4Em4^QGK(=IBvYaM`e|f ze?H@vp$=abRc(%f<7JpTKzexDF|wTknv5osCk7xe@lUx0E+$UM50LQbCxF-tZ*7nh zj#+{mk!k--!2Nwe*^cVn; zVZ2*^2TxxD@=}9^-4Cy=d{jpn4o!eyBFam9$SY^P38`@7bT&h-j36V}cEa4}?+4*; zwcwrGu8DQx!=DEZ+~dH1{A;iG!oD}BBS(c2u1W2sJ9N90tR`L^$YJ+p7hR_><;h>5 zPjMIWQp)2u$mLla>1!Fbw3EyHE1BdUUn>^!wVSQ<@-I_pkIGrG zJ$}VewXN`Lf3W+jZ0&0AOls4QvuMHws3X1Piv#`RqxRzD%ENyH^utt5+)ri-16R}8qW@{7r$T@q2*VWkkj2y?>G zKyf%0w{Eg?@DDBr&LHOR?KC_SfwO_)sBEI02YkJ1aN5AUD(6uNJU#CO@)GRX(yqZp z4Zg$4J56d3=b=(R zsw=*Lj|G;28Ez1AiU0T0t;oqbs1N2Te9=?P*^b36QmjIMz&k_^zQ-N(r#?ZmWKl>G z8!nd7XYqA?UY^fCv$X4*Fgty6Wcz}LX=mY1%(reG4<|oOt}G!wT(qsP`g>^ZsQ>-S z7(eXxh4=a?!*2|9;<557cXaQt;MxID8!x=|)?06%#(ED^pH{z}*8v|VpJM;`>f!8* zaNrY+{2~>9Maw?tSBB;J`HdH!d2Lc4NAfs%pQ2F2&Biez3{pn}MV|*`Qp_ORo;-uW z;5+Hh*_vkM-Rxb4reC(P{If!BOB6$=u9-q+p7z4m z>ikyt8)PxG+A?1rm_wrq@aj0HIc5L?PKGHg#+!^FLvsxDne3QRLIcBHS!Nink23Oe z*44%z`38U5h9DljU-|JR8m)#OM}FutBMTS25g;&t-j#4UO=IwY4Gu?UH^63}E4#i3 z{v7flWBAwX__OB~^*ua0!bKaCna*vCouQAH@(<3TuzuFZhlgNejt?{D(b=FJ zo_n5uG&Jdw(=d>?y>2U)Xu|RCuMcB39^vogmQ3pZ+So)@Tj!=cC$f*XY;GB^8uHy zoS!3?DK5WV1T|R%kxzABJitd7?32Af<$+E^n*h&$KIZK$U`c#m3oSA9NN9>5|4Ix03?Tr&7^%s?IQk6)e`%nl(o(We*}y~LZ*1<&x+4wxoUW>cdX{6l-0 zmBBXyQt_T^{9UEd3ZGzguIS^jV6p*g^osQnyaR6>b zOjKEPvY-Oi`ntk)NQ(cDkI|++Ghx)ge17Q2#TGr#I>wRP;;d-E&&BOLabAGGM6Lfi&7 z+m@9gQJXz4m4eMFCHmVUngTJP3?-E@81xO&1w^z}F73;RS&6>VHBdBbXvWO|(S!jd zJ1d@sQz-d z1bc(z@EZ-I!*nnP>L3_9~-o_3g|!MmKtzRKPny#3psR&<}oa(Qj~k^>JGcwbeCT z)$^R4BbbEI1Pr(cv})l8Z|z-u48|O@M(bBRtH|1yvv$jaw@(t)I4-Siu)seKq+JiS zJ6fxUhw$ZWrHgLO;2|6*dtEQEsLeBT9nY#ODZ4-;yve4tI-P544(?>v2g!>2{nO@I zjfgj0AALBp{h`@t4akz0W4k)L#hZjspqt;yQ-D1D*}2f)#%qgR5hk7ayi)9N>S^yznd+1@WY z(MLOc{_)!e;x{{x!bah23)}X7gCW5zothn*{it}A@oRpqJd=a7BbXV@?EM-1yFT88 zpTS!}r`4)|+}8o#e0*Dx`b9J2q5WSf4!o0mkgvrAK@A-B< zx3;c2K*8a0yA9xe#c=%CY{ktPqVHS>v-&0~E}H^VcEliz&WQgE?nXm@PCWy+69dZU z|Kyt(2k+=4s>YSBO~M_&ElxG@?>#)@ExyeM#}_o`H?@b~hqo}RQvbIo6ke|Jc8@OB zHlCnS>U3q<0&&Fn-TC^mTgsiXgF0O8Bz3icDZ3F6G|Zk>M}O*WFo4&9GFza?z8pH@ zo4S?TL`7|y=!B4%>6~gqyl`TL!DF*7Kd|C3@$X7%9@yaT{JIXRau=TN`V!Q}2y2j> z9ePAY@YEwGofr~KG11~wJ8C;$d}$%9f>(S*?#a_&R0b!M(+@YXswsU8`pu73?p!4! zi{0!#z|YeL-2C*Bl5kX4J0^ur>{prUEU(I^F8_99id?C0$1z%7fBp6E49v%W4%m;c zcUIZu*T!ev|E#=ELHO0Yd|=F9q2KRqJ^2J5?`>7N`|?-5^37)PUL^dlG+3vw26P5% z6w*uc%kaQ`i@yQjH7H{!{NO`r8GS3-*MJC&)8^$QkI}t?Shy`1<5)7r2HuS6`JK1NU&{S{ ztLHT~dh8xhh5;SzwZm!7NX?G)C)k_>GY2m>!8$abRV2o;hG%$eD>?(h2C19e=w8X& z-);A2;JhyZKR(9!be-!N#p)e1#}S%6J_BpGhVc3TuLPenmhwvs8or+i2@dSwgy(Z0 z?)Q`60iIx1IsIy|dayl{FD8sQfBhz{74+5D`b2#LcJ1MhvS2|69nze5_}h`84hG(* zd%GjLzZc`_zv^;gp0iUPyi2yU3;1iV_1ta8D}VCSHMu@*kdNb_wGT(QUc6ep9E(|I z=YoFauKQLc79@#&ZfW-`13uoNq?StsXsi6b`@86fVE<_1Evd^QBj(o9q_*VL_9o zQoYFB&pF&Z;)8bA^jM%NJ$rU+9i5X_H#lC}36{Z#iN)D~^4aFEy!Oi7?|ApZxV&l) ze;zn64m?S23~UX&?Jy*f61(o4u93Ap8kvsKFXi|EamR^^PK^#iJ=HtNw$0S0x7r!V)KjxE?bpzW9gja*7+L%AArUe7=*bcGM|(2#e>;$^?>Q9J@L6CIUxuA=^ox_ zFaE47TVI|1UG4syK9K7-+?>0VVgxxr!{o5*BU>HUM*IkOK43V8&zXqOD(Hd22lWCv z+2~w9!d-t~uKjxtm}V;tR@1QHhEfI!9u(NXSpJl-dj|T9%H!ISW@LiPNK;IXbGSx}LF0^j zOzht~`JFbtjyoZ}c+Iq3{K z&qTro&fSNrNf9`0(quvSH9#~=i=T|rF`k177=sjjXEhGZ9NMAx;uAdGS2*^KANYOI zzZu~h4E&q1X4LwB;VpQz5)aD7SNLe-Oy(S;AK1Y+nWDe#@K6_=^#g;MoNUYf`nZ0r zq3c;%@}nJa;30+D>T)#~-DG3~-ssh*`YXns!qKVRHS>K2>P;e4_I#5zr*IlrU*H#4 zLyiuPw=KUlZ?a3-1vVYW8**41a4ln75#gd8_e~mXI~&0$UGhf{l%L$(9FOZ$%wK(X zWCccZI34B9Sw{0pg)f|+&sF+Nh(Wl>j^Yud;+ysneM&@`ioT5*F7;DBCM%1e2J{c=>Wh=vexWuCo-g)=D z{3_P$+>!3`C9}U>|JK*Pl$|e7iip z4jvu@IQX9U?Zg-K-P)N?tU-CRNz=XKf5ie%_P(S1 zAbcycfnepqymS+;=aD-xpqL|g{liBwh2&g)Fo&nXV7SkhcVC?|c-2Qof935|uyrdV zZ@V+&`Pofy?0`N{7|*n|y5VqeJTxC#2Wt6)#n4m_ZfCLU^y#U8U>}&rp0m|<3a*D& zRDBaQ@t_zDz24(;Qt?DMjD}^X;2wNrBZrf{r|=KjCu}yZiUD zA5Omj2Ob#r1(b;Ft0+!nQe0aT0QQjN;yAnqmqJIUCAN zDWtuvH05Imtm-o~6qXX2CDRzy;3O!Yv{mDq@(C9A2toCIZM&$24NK2C*(YrwIH=4n)asq$_V~uh#I^yste>=efHcuWxpPG*lc{b?!|^T zyg32b$unURF~i{iw?Ih07zKREh@m%LgyP;IVCI>L!?(v?xL2ECd%j45E4nU=cN}(Q zf~7r1y~B9PxuBiFLi;sPM(5CpRzdmY2oEm@dGN=t?xP!@U~sKQBWy3!!Q1-d1ovgu zINZ;7h`Y|H?peSZd?yFI&n|{PdAN*m?sAh5`gQUXt4pO=ToqDOOLz{E5 z^|U?#Qy;lIN6>LNW6CYz3BH*u;*UK;7=`NxD;DTcUt8B>EFL={*Jm~21Te!py2$Nh z4!Qo_jvLejXR_OI1BnGC`YYmUTX}recOK0$*C$K#ojj3HGUQa?eS)6!E*)dH z7QnF)O3qGHo(v|`euSI}pB*Rf2^~EEa4V1L!8Q2Mh6&rZ-|!~0`?e6Wb zyc!pNaeNqJ4}X5CIN&pYdjyj4PKNj>gK_cGNk?A92=h0$d_I{h(`AbiT~5CFgw7_v zw3VJJqda-#r^F4j0qL@Hwn3u5^z`Lk>~~Fu%RexOUVjLGz9$^ni{s-f)A_Lt2Vds>82?A38zzQwGh8 z_sZC0MZPsm7B4LRTYm9EI(*(7kGvB*z!^1oq@A@DF0*}W@l}NI++>F~7HfjRwlb{vd9YPRd*nqs=;{BtKf=%{uX5Fe-z7UI zemwCf1i-A=+7)9SypJ!`jxhlr;GCS2Y3*KdV04)P_;k7ySpNOwzvH`j!>m86(O=E3-BX@3hK!M&_B%`=zw< z4bDF7vt=8+?#(5FJUV!-hZt=r~Zh2c3^2S)~S z2E00g0Zva{NN^@j*hbFLR|Yol#+&HHLcBXBa)mNl8QD%5Fkl!vMvN2wz#|6?o9BKR zTXa{3{kYm%`%6}D}w{B0J%Qz-`Q?;f(m49aL{oExK*-R4{~kIpvQpj z>61-bgxgjhOk~s!+Bh;e!V7KEp1p!o4Aq9d)$Zu(-1fsuo*Y-dvYV9Zc)eih(WABm zgM%?8FWq76=lE~!a|WX?91P?`efJk=kn#Fw{DP}~-6U4PD(7wBaC|TRUU-m~)4m=! zX#@NS;Zzm~1?L1E!Ke`Ylu>>GoBrwo-IK!#*#Yp79yoBH1K#!VwcF}HLAvCPHXoTf zlc1BMDw1{YWqBTCQLgGu{>fUh>z~A)koN=g`~`D*U7Zs=rlc>1_5Y2Y z!Dm}GC`rey*vFpjUfRSh%AyhNl1WZ93T|V-(X|^53rH)26R~8rcYffau_HLMOE;dq zR`C04J%aF%9yoBH176(v&Ta8!6T#g)@1{9=c4;mD(f{cYU7CMP#`tBvmF`{sV7@8M zbge8%?w^T<6Q@ld-N@(hJJLQin7B{g)7!-%>fg>+X!5N6vyj39D90hu_0yWaXXn7` zmdm!tJ-+T5$RE7LS?WHIrIZXeahy75ksv(98#O^Mwa3Qb&+Zq$cvb|Rkg@kh6d4e(e<1CMOQ;xc9X!A)GE zEL>+_YXE5FTx%o(q3b*J2kz%wuKI`e<=)DiLMML(_R_`9wr~db;a#g>q8p!GkdteW?yFggM{DH5 z0bcqZ9cQtoIH)+wSVTU2*3TW$huN;}=5WMY{jX2;h1lxqn^mrg0YALtk6-a!9sD=W z7^?2|&tx~adE zuR#tH{7>+akfsn5(2U&MQ?5!-pre${OmAxs14kh#K#HwHQchVg`yv4o82yQSK%WFlI@j9PRIfn239HNN?0h>t$iSa*%JVnkJnyqE1 zZEt$78E|k7TiJ-sh^C>h)b_jWNZ2Ra$_Eb<(C|^Cvl+!M%uK%gat3^p0^!aW%s6<@ zz?lr#vu#-sY%p+UP`n@qj4Z(3gnDgd<6Jmw+pWCil+oe%3``jv#s~fze?G%Usp3SL z@E;#)XNFtB;CX{9yw4*W!E3UIA=Iu}U-;uS+|EQv{aODro)JHziZ$2!AT|IF-smw{ zWkI{|k;GcFR4r%`- z8%R`V@?V+hNpMU!tg!wF4rglcFn!bpS)?ml;fHO5mR_y~4{h)9lFlDK0XWt?{JF=0 z*B`xn_tho9vj9k$GH1Iwl1chW-r0?np$p|NsG>K{RqLOCa(Z448<`8IrH&5-u~1USr(V^i}P8xThk1}+kf}S z_7UZiH(LO3$@qEqz=PLlpdY6O_(~jGkj~JZ^pJl8f{ux?uIKULec2ZE#Gq9ee%GT0 zbPRNQ1-HSUU(er)Tgu_%>GopC+kg@dCmuD(ELyA#r;oQ+5@&8VLvZCAl&*z%<&(f!lZ)!W1K*R1?FuH(lYv9{Y_s_K z#Ajtz-lE!(VV8J!-gO1vj|s!SWi0lZoYhf|a9eWdx5_{ z)&7lx%lh0u^)q#RNMC>h4-ESP%=(O?_;;U&_soQ*%y~_HtsuVGJm?Nrh{0I_0mNYsU7PNi}n5>-hT(^3W*Y z)gEV~>`S-V-ZLPJc7yfc%+5xyiGmq#oYZ#42~B5P-U*)UE^TyPstJCzJPPxj3j)*t&+U*+)N%oMhleuIJ-?!fC)ED@i+Fk52tsU0D4Fd9JtSc zSChHN1$`!3r$5~!TSSM1Q}@fRKJh!05r3Gk7v6L1kaB$wpjsvSH`_HaC0L zHO{ajOA7R##kvMN{3qLK;2{4?*^$}BRe1RGz=0!cCT};UAcWI5@etk2SLWLdlF2Xm zGYs*mT}pg>$ZchrYpFbW4ajseXe;9=(OZiquIal+)C|@n3oWw)wZoU%VL9JdzGb3! z7yqRp1qtvWCD z-oN2lK78Q%{L`SppD$k>ygYFL8@c?-(N$QLf>Z!FO6y$b4*lXu@9kjtWD0b@hq_BDx zCZ0WcQk`VT?PyePaSZ5{A){;Y22VTq!ET~)eW&U27K4%huEpoFM?2p269b8VocE{t z&!y+|RaL=Q+$GN2@5JY1s{f;H{TpsK(s99^-P`1n8>etMkb_Eu*MV{F;q|kp-cLra zUVP=%S6}4_Aao(+mh|cClY9Pt-o4MuzhC!P=KO&fb@g|@``w?$2lsaW-qwX% zc$2mX;AZ?2c8a{2u%Ixk22{esoiiqtp)6+SDD#ZyRMtSh!AOeEXn6*H1E1hfqP_h! zh1ydCDd>zwCgpKPPdWzS)b&y`a33|OV~81j_%TqF*np0Kq{;oCfo8B6@;QOvGZMLq z)jI|$LMwa?2(7-`%FgOAHU`5Oxz*m7ss(xsOmar1Pw^LSY0o3(f(Gls_yykId5>H zn^6s!{)eMFgHTpSe6)I>#apBMdIM_M)WJ%um6tBt5@V)`4ne{my!5LtE zq3_j$hkNK9oa%e$!!i89)}J_4nY{<0a_Dir0YiOjrXT%aoqL>A1Nr!h&qx_RyMO+{ zqBi5_ocHa%g5-dl;-h*C%5E??PeIx6?w)}ZoX7$ACZx#SnV33T9<3rh%^5$W2M*ll zfZbUyo2*KB*f7Fo;F}or>kk{<1Vf$!cJ`-^6Wr+)Ukl^#0b)w=!94th}xN@S>Mz9uH^G!9E-tzDc&O`5Jj1{qgme zjLP`&oOi)dW^gxoneEJu25ffEmjT!+x(ntjHHa?{>45_m2U3@IZzU&X>@mhN-p$XD zlkGT3t_&Q>99`vm9=+_PyGO^oW9b@5Uxg{W#;Xp!{C=?m@k|Ar6p4(;Mm{9Hygk{vN{@E0RjUo3>?;jF}vyCJAvaF^w% z(Gngkp|f27*v<7}x5$|X44bd)31$ z@sTp?TjN?@W9zE{4ICZ-=+l^l3v{&Zx&9v$#uz+=Ktpi(I z7j0b*hZC?IKH(c$mlthEPMc*;*3MQnZ+7i-mtfZ?+a_3fJd%&W+BU(peh}z%uk2v< z7p}KHxM+1ejz2jM7QCiU2`2b>yT?naiNBk0DafH2>%a2tP$3^=?YZ2A;}sYh{69

?>djV@@646tqdgRqjWhU=O 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ΠtB>WRJkE5t>B7%|MCvg%Xa<068QEz-WlZ>PznU5EO1pM;yyU9s` zgJAWq-g|KOKm5(ZyC3$fG$;Stmj0@N-9P=$@3s2v(^e+Oj=ka^*eW7FyPkf&nrsrn z?Vza~jc|YVq7ycj-2SYU$$!>_$iII#oglQoW$7pPKfe3#fB0U!&8~+(6UYvd7xs^> zxAIp;_u2epp&x&Mfat}C@`=}g7l}=(ev61wf8Og8j{oCNKiN*2 zhfN6R{{|Uo(D`g}GBG>MR{4i^<G>m&{B*(-9mfmGMd{qTmYj(_z=oab}CWtWcLZjh90 z%R7V0<1ZJV1pgXHZxC7)3l#E9TrWPVo#R{ko8NVhtQ>z$uTFWb`GHH9Sl$cfb|g%W zDpOr}YX^)oK;?VoAKx38^-rcgug&trW{Y1cO9u75&?8-KCuOj%!JQcFK|2~Pik;6$ zTl9LVxBR#A|MpvF<-hu31Gqv6owR3gir!#}chJ_3M`lp}z_Du*P6qsb%>XQJu~Sfg zOd7cFF(fO|54yGk>!-hX2a^t6f_5fY4o!U-fWB!Py+_~r;p+^NJai)u=)~uZhGpu* zeA*4~(Y`$N;Rj~-I$mk$?{#>wdbge} z`-^`<>UsJi1=70T{q+6sfB#9r_~NYAgR4BV}Ix?w|be z|LxuPKlr~JFcW&hOQ6rWEmN_g+y zCNl)lCHxvJcx{ID@LMoopq?cS4EYPucz^&L!X|m(?pGcsGx!KpCI@uxITi?c2SACO z9oaarw11qHl>q#yQ0+NC<*Vm(ZQ;XZei(lfGlG%I2dDq*GPzlW4xE8W4Fkl{IYry$D{lh8n{zos|{Y~57Uy8qPH34;P?Z#R> zq~mAc#tt4nYyvbMYBWMb+qwJHi~gMlAK$$d zP2PMpdL$%lFq_S1T=ro$qnq-sGP=x0dsoL_1?Qjt&3n=KtbF|zl4B^>mAk)ftG-7i zvY2<@=-cNerrG=r4(sm>TgSx@bZIv3$j5b?UH?(?_Ah_*-rfK4v)(W8+e(n9jQ4B# zv;XUN-kJ}eOx|SE1ovn?{Ag~e407u6GX$USJlQ9IGA;l3#toAAs5mW_xa!|P?ts_g zAHJO6lmFSz@YsUT^7z(U3>g^m!mkZ(_gC(Q8%)`oE8cJo=GyPP)9Ke)0Y35Jy^ZTB7?1w6v|=DpYMzWc3j^#bPe z^&=$N+w{RHjc(b`8?^`eF^x4+Vh{Akq<-j4H^cG<1&(-pU$*mnR&M}z6b zXtl8ZM-TDcnL9XjN2eiS;pZ&U3|?)xhu@KBNj&Ht9FG2Uom{}sj#SU}x!jKB;oTi& z!HE{+7^p5!zsVH-Oh2-Z-8l68?C#rt_=k6Iz4->bKmPMS|MUN6)wa^#^%SYTI&Q_k zHnuxJUguxj0ia1p`bpRE?)%^W{tLg$y8z<;?`=Af*%RHr4f+Yx7w=uy*KxI8d+DV= z^+`FxY6h90pH&^(@s9$Slp^2^=uvjU&X_0c(;9k6}xkGW=lEy%~ba+Xb-gueCBws~?xau&RG%j!#hMMRkWh z8IkG=ip*qk9t@4W`Dk&B3c5Gh&^rmPLA9?eSXE&_@xr!Rjp@stneRxu&v!)e<-qv# z{r7tmpj`m{?!Wh5h!%LO+i!hs*TbhTgj03Z&ox)Kvv|%Ru{6gpb@D{Q2Sd#9hh4(^ z$qY#68bqHmo!I#ltveaL@tGv$MD-JVd}PGO{{~R%nPoMYQgbW!mD?69^pYFjBI97_ zFkIlR9kcJ8CzomvzAp)cpTV~Ko}Ys=Qb3;($3r;ajdKY;pT_ee)B1MG!kY||Gk9uG z88gO@K7M#}CEr#|yD|pq`JaCf1?cvBExP=RAAfxJAK!T8?!UeF(cK@t@$%ib-e_k?Bj4G& zxDmH*vWx!Yy_cCLaKCK(#b5sQPw#&GC^;H$4qyI3NIYwW>%yZ}b^pAV_5Iba9^L)f z+b`e!*;~=%wRZSqS7!@udc4)DCg0d8pT7E__g}P7e)nG@s!0UTj2}&V);jqQ$H&V; z|8tLZ{OE&sqsMD^|Lr$ky8DB+z?%>_gNxqY+%vfz3%C;d!hF5cO6t!Ze0KLAfBB%v zI)3hEH?M-fjYo#4^yB#alYZZO^TFMJ_pR4^Tq0R{U4Tz#&P4lUpyKmEqsb>tl-)&( zM|Xeui${0=xt%j--fV?_8>#WsasDtra6g~(7T;71^ZBkc6I937@ks`5m+ukJd}Q}v zK8CC+Xcrp$Hvb%t#ckd>v$&~zWv}{t7GL;DEAS_7h|?Oo@tR%OU}u@#DArK-f<3>N zj4UpgPVl#4xRj;@l*A5;2`6&>0Yf@?ScDM=w$-{=#NvW;16a5Yc75CP|3RGJ9z3zw znK)Np8+^O+`n<=$sxQXcOYG_sdcD=)|66ary~P&w#0+3Q>=9aYI&r4hnBVVr>#zLr zMH~Ga4a9-sy!xyAc9}|c8rUtl36gLZtHKGs@Au|-bT#>4VQlZ-s;ss?#lzC*B5c^X zc;PHA9p0Wy{S==#F2S=C*H_F6$4$bBJxUwwhlBhIKIX&gul4w8arg@_oqp+iHwS;b zKqvohyza&=?o!62(V_b$A=Z-EF}(VHQe1rc@uVk#&uL5XE)=qAJYX3$08h&kty%w3CEOB?|lke}m^A2UfI>+D3^!q#k@b_YP z0!lz0C&Kmk;-#0q=LJs!oC#oqqOc2P1~{<-t+7%LsH?& zz0#^S<-82SF&wvNwJ1i4VjI7$P*bXmfe8Z2G(*m)^)t9Gw*hcQXGZJHrg@$kuHL4a zTdU23>n0NznfkRsRDJM*tHA+3+!;A(hO_8neF*nW! zekD)3oa~iT({)Fm_jkr_10-~;?@JlC{~vpI(w$e5q>1?>NDKt0AY;hN%1ZS@!$u4J z#rTQZX`yqZUTD$P>deTDAPIn@-siRJ02uSBYMoO_Tn9hi$=%${+}$Sk>o@v4*ZC%{ zNNH%jx}w0|SD1R&)t4MZ)U}H~l19q&^pz+ycrYgF&?hfi(O&wKe`pLJHafby{+7=Y z|1OtLT@v_~-dU0*;8Xn7xAlB|#FzJ_KvP<#!NWIVZ6=NJf4q8nT*T5L{f z+3e*%^o%o({6|Rl4u8o$3V+FR?}ynW@XLp94o?zD;@lml4<|lV`FI(An<2O3_IIy8 zWR^R9Ew3egBzMwIpIh<{{QvRS_lN)UZ*LD@o*g9!kN|H2Bagxxli)V_dmkZa)x+z^ zuD@n>>Tmh>p?Bd|B4a+MWHrG5_4mue|NZ?TDnYVEUY*ZY+3y+YUeatl;6Jal@L?ua$n%mjH_|U% zY}x}zSybyu8*jET+nb80y>(*lI#sxzK%a0JI}IY3Dv{Ao)Zp?F6!7Wp-|@Q1Y>_v0 zdfv5+bNsbN?qlg{%JpZ%_-b5mrm@Q(0dra>=$k~yi}a9F=p&_@+Zup;4m6@+x$@cG zjaxrQ^X~5gU@2Jp+TU81?)+eYyAd1W#S^>X+^}{PLYy`KN6qxkPTtS8>4=6$+6mj% z6(W_S%@>EGGO2^`6{#eS{`e)TIrz8yP=1u8(nNTt^ri zu56WJDI6i!nri?Bz=t$S_Xz$^CigYbPy|^7+EZ$r932FTj#3C#RefO%h)5&Fkg+Oh znsU^-;GlPBmhcVBeXHp8b0&|W&~*5%L?TG4`DNWkbWHf4CZMSUzMd%sl*CQjWC4!_g{m7H%pJvN$%b#ln7tK5gHAizVF z<3WFTZoho^BPTC|mh45j68!J$t~nv=OHzR%@hbbr3gMUqsosq%XPg%bzEwXX^y3t5o)k%p_)ccwpOC z&$(UCivXclZg_mTyR)THwO$rb3&+-jf$U(D!yLrp5SD#&*!!;|c14>pz%{kHb`5^; zYr=Kz73GQiV-!cI5jm!JYW(y5RqK6Y=^HCKRNv?lmA`P>uDO=JkfxT?`I?W{7QtQ4 zuELk0ql4RPi6@6)>^9GINM!5xv|l~JOn8>_-a$Fp*IqsMIA9$_oPFd9cQzYXtc(;u^!n3`iNNUZIa$(*iu}OSZ3HMQ-ugmK-|q znTUeO`GGg-viiBDLpl{$qeX2aeL^|_6j7?9W8E6iQsnA@Dwk8QCwPj8*sy;(Drs2` ze6f{p0jw%=ifcr0LZFBf0ukjv=KIMWY#>emgd*0L52%C@Rlgi^ zZgbHo-HHN=j;BDCA|zkS5xZwC)q|^-Mh3M~ct69uYN?XVB!)3rEI4+R+;`OA76XdmZCpgQ&eD*9vzI&c1Y(y%BP6 zQ$_wyC#}8GJ2zYk68_;+t9SOfyf%PIm;PAxBMV>BeSuP`C2xzmQlJM_rW@$4gGQkQ zRTQx~feVoX!dEy1P(jvx>`CB8{5Tmaz94Iw<0I|qz8p~FvfxmSZ_^`Kt&A8sb)aaE zVFvvTf0p;74ws^c1eFx;5Zy9^(TmLOCmerlqmrl7cCZ{umr~6yCivj!)?L~(w0GA* z>9+ZDfz&^KbKGeWLM#}m>tyQ2YZK->=N&9kuTYTItUC1<2z;jS$L+BK0 zfts^;g|GhEkPCEz-bF7Ni?c5vn6P-P8hlf&n|U|9bH$!Dgcb}iF~%)R6h)%?*p5>& zdlOKzezd-J>=oUb%ofvtB-`RU3Zv8swUD(ga6o=wbTot@wPtNxuv5DFnLLezKB_AJYG7lTg9FNAoPqGx<)XqZ6>r(E& z&j6<_ihZ#!4tY+3Ww?QDME!ayzlJ|iCA$xi;^>aFU1eY7CcZeI9&8QIEY?sK+~yHpTx zy8{2DeGw-8dMv_H3DJf%nmXg?yJHdY*yZSAXjpsChzp0rz1b}8_VM|0PTeVLsUTJm z5EG=8C62s{?14?e(_7k2?v`9-hloysJWV0@y-yfOR#%_n1RLCzRj%s`ogE)99~uX= zAvqT2=2>U@J8Yp#mF-m&c{#`rlAS}pl8cU|d|LzXO396O5m&2(U*#_62H`-WeF%%T z)Ld^_0WDbq5v3XykjcOCtos!uW@ph7CfW0`7y8!mNQ@{1ns9%@^)1tM?Ig& znc7)Ot3Y&;k6GP`E)UDn_h|!KfslBC3p8i@2)ZWtS|jdWv>2MM1sU*cuL*ZC>vJ0C z{RfDgr%rpnmO}p+_rq7pP)lgzfSpLW(AwzYu0t+PR-ezXdTmb=kAZD2T08@j)o+yd z@8|hpA-slH$+#?{^3&X=Op!%dNA+jZIllRyjVEkO)mJX|GaY)oHh$*5)fzdCF@}HJ zg}~YqJ)B-^N;h}K1&jriLnlFATxFC;P1>Z)9j778*0iQ6bv%x)wuo=lcTUa>KmHuR z+FTZd?QuWwqXi`J1j~s`TW$)LIP!%OM(&+b9GA=Z-yK=GunR_F(uA-eQjGK2YApxL zMUu)U5o}pO@7BG!RvC`g8Du9X^MVM-yETjoTzdIXYHHCud&@do$esZbPzB?Dh6r-R znviAnLA+DrFLEWU@`KS0;*$>;GsT-Z@C@&Vc#NpwdbJC2`(V)gka$(zzL?Th(ZWA! zcU61<|Cq6A7AeqJ96jER><;G7q8`EL7l#0w(2M5_uY+=9rJTqavm{;wM$jP+7g6sZ zEogD^4-UdFY1A0w_Bg%OL@-$~_oyZu4LUpe&uxN8K8OvPJ8emTlrk+W)H2!rJin@g z>HBHx)ovLZ2eYo;lEMdYx1SjBg609LHt^wOI**9NOL@t8EtzKSLkXnVl6@LJmLi~XBlN`xg{>=ezvhr>{sjb%g1;ZsILp0NO2L$AR-I4A{@WQQQJh( zq15}Wf$kgrj$}02IbAHs*+pfKa0GxAjnnw{gdTWqt|Exyj(tqtwnLsq1V<;%sqh|@ zLh(hAdC?W!Pi10y`gQ-3J3;(T{)@1mLWt9^152I>IT0^dvH;{zN-KFfojg4#_viD( zal_SD*02E{5}9n_l&A90^&;%YG-a%|vVq+(w=EchFpdKn#dsw$k=VxhKJrX^%>#)cFVY{fWaVp~?Z$re!*Z2KBIsYaTCV9F#HpdxsYDWy|{No`>z_t%seV<(q&k zFUR-4wN;M#=lj-whme}&rY2haqT+FOtmewFT&#SU(pg3H&Y#CoA-@`;vpnnWYUA^L zJ2XcWfTF&ay=)NY%W=D1rm=%;fhd!3tTQJ#%d>-f@6`m`<9USo1>DD-jcvw|aopp5 zzGAede{TIw4vI}|DB5ReoT-J@QsDa!Vr}N{(o``p8=KCy=LC3< zIshuESf8oNh=n}8DEge8Zh~2Kh{V!xwE(FxtKdfFQUvA)RDii?-oG+6;!f1&860Fs zFXSp(Sk|p(oXqXVKo_qRGQ#vAKT+{y#svUT@UXiBU@@~bwaEK_lj&2F4e$k2B@DoYanZ-TG(ax2JC@KwWO`a{l((Fs@iq}il zA3a7+OTog?dAB2sDjX3wyOaYO%4zoO25B9y=il?F4WC1&D%Tqs51RbPuVeoXPd21kc z{^HutP`}~j(LV++(b*!q)erIY*6w`$IcpTI4cvZSjgGUIq{>)LD+A@N~;1PS|5` z^?wfgo~gL%6sxp^<{$lM`0wHORWO{MQA5Upt-ChM-yS(`o^1C!%$}4^1v?bltpea6 zwuxkjW@H~qb$n(_TLjyk(0&z_?x>E(foSyt| zejrAQbvSAkzY66F_A>U^b1Fi?GzN_IM^j!#jxyAxsA#*!xqmIl@>`A@pD$8o_c~|V z6@{HHT{zKA0my5NGic(7?DtL|yYD`2X61@WhN%H8QS|?9k3D_d;H{4j7Ji)bI{u9; z)SQ3maTG376Lnz%{1f-Ew{2DNUtFWHxb-yMjd!F_EB5m(4|i`ZTKW!IcEeufDf$CC zR7sfBpe5}e`o91{sf2;W(a1TI_7rb4c{l6AFTWO<>BJ;8)Y}XNq51{6d_>}ByvZ7G z52k0apGomuof_l!Adspbj#aY}4N?YH`Wt^>+&48)0L-=aB4SW?{4XKu&C zLjR@drC2q1%$<)*Scb=u!luFMr>%Y$pEs&&OQeXt9TgCA8VU)DOb}x>^uj4I2RsQ) zmMV|r56%mq@G=*CDOM9`KRwKB2T9lq9acVmy`z&gSkGhkotL(XKNfG*oIiT$%|`s6 z(U(c?nZ0b&B2MvVg(6)f5(_*SPp;!KPpkH|xzO2&U7)ox_3^az=YEsZc-iGd3IYhd zG@!jTcs1qko!`kJGmp}>pDi;1vx`V9Gfj+<`2u2=H13y+~we*0Of zf(^+P5f+*#`V6u_5dHjp?7GR8s`0ZaQbZ5U zb_j>@N?keF+MbZ;n*_|eW9M4l3O-jTrTi+6^Mhz%RwY#{Le}ezDboekE_oS-?O($$d zH(w4(ry_uB%O0G<7-@9~hjlB9zV5!Jz+k8Atgp-2oBY86Dj43{W=Hh7=dgvDUjxKO zz8CX1<-__pwf) zyT4HZ`-G0>0%0RGRGT1jkddx01gy(GAF`R0F%O`bI45Ncl~+H=5=yDT`<~3(2eI%N z#hr<1t6;n7>1k8U-1QH}_msTAkNIZ08aM^2ppj2?@8eZTHO{mQ{q#q;f1x`?etiIl zP9N{Q{+tsoQigG;zNFHC4;5me ztz{5D@~pZmL05s5)>Pm9bm?`F^wxm<69yk|BTngdvJ)-7$a%0*l3Yx76U*9E>btTn zM8nMp3>Ng%UcZ#Pp_V;U^V`?W$)Nuh+w1 zJG24UySR#nWr{d?ieh?l7-okmYSk2VhmK4@ne$Y5}lpa zcB;D=^p|o7fpjn78wvY3-w*tRxjn1w?q@v|v69R7Nqk@kQ)t#>FWgSO}oK$Fu`Px%e*+6c5O6K0rQDHP+% zXxD!=r5TIh7rPGaUe^#CpVqC*ijLgJ{7!i8hjI`>p8G}(DTCKQwAf-BA7r5Wd_n#Y zmdG=->lg%ij!r~N+j7Ee0Vs}AbEBx-K?@+0uf8F1LgYT|T6N;*W$ZO)v}~^tM*GN; z298N8tKKa=fcTNl0LE_*Y;FGAt;IPiHQGHdX*?_G=be0xnJU5o*SjO4WS<1;gYqFB zxSDNFw_NAWo87eJ_LDM^x0%J{qq}<~naR09ToNmSYQIR=6OSJ!+bdj>I_wF9bmm^i zplRmIp)t=X%Dgk$-$W-pw*YjsaL6NhuQ!4%yZ!{|r#dWD#;PkH&p)?d3AuG>XCNTr zNS8&Qhj=8*p7N65Wp$D>E`}mlUS1x4bi<09L*fl0aQhOEgrh9FwIS;eYn7-S7}&Vk zb%~S{D1~H|BdOZXn@_;(!%)`Sc<9C0X49@onQZva(H^1`OM++DLbw;oGaj_I*sKkq z4b8^XYu7@vzu#-c@5ytbm5&B%vHcNA!>-+RG7wjgp><$XCOS~?DlnkT&ZeLgorJkM z3r+q&^8KLyL}^HFFMrB8+DzT98^!6-12cB@N8BM`GYE3MJo*Rg1QW^^37-&5iG?`)CWbVrWI6g0 z8DGx!KAjeDK7Pd~F@Eq4?O8aZ+i^PQ@P{5zW}j009sy<%AjeyZD5U3UUiOC}`1=qc z(tSF!hJpX1wEOYx&N9Gbu8C_=sr4=zLUd1Q2#tWl^2h59=QRNrpr6cgn>N`8uhhb( zQH|o9iRizO?Vqe~t!Uh2qoeRV+S-Dl ztbCcjf8Y_N4L~vo%N6R1pAQ@7k_48inqSmg)9qTApHiDmn&Dm-l`$xe)5jG3pN~8O zstq(j2HEAkOiGBgJX3>$Jp4QRYH+dAWpBJ9sXa3>xDo%Ew&>K2wAnQ5uAG?GP zj>t=T;=REvef6;yscc_B+m@R+YZPk5X4 z8E|yo>}$LRIWH*U1DW?VJ4smd^GfwBcUYurn_K z(}%wS&C8C;x#>?r=WN*?fT^rFFwye%Cxug=%}mqhzZ2%6v%7a|Gu{f#i!=X9c4gly zc&zbCvfx?$>bqw>JCrf8toxA{V{$|n)&OV`Us}GO197Hh76&zoPTru_3P2ryP+i9g zKL^WRgkM414OvJ!z}r)Je8n6>^}lmm7Ib!#j;g#j!k9+;(B9^hhFuntxu9r-LGT^u zqLq2A-u$=Z#&zdtah3#$%n1RFc7$zxDJTX+SUlCOO*(@>M&>i%h&nJ-9}e}8v${_>tL-JrrlA}UTR6yk-ntOc$6 zj2w}?b^o|L{)!77+4`mh$Xagw5XKVF$O}GNBwjM=+wTY}xO7e`tza2wol^?<-m6I5 zWNq|4P_qsgz3Q<21?c5Ri?k`d?6O^ImmK4ihhsqm_RG8|_M2b~bP^R`I`yyWFY2G* zItgXV@j`;qC+MTzxdmpqSx`eZOJC^GMqtEerL}>6qMnaxVmRuhf#Do*wN26oUa1Yb zzb%wCdFuJKRKuMlw+ui2pFIx7??`mEIp}nZ;vTWXcRww+IEiB5x{9}x7DpWs#AHM< zurIuL$wsf)@0T7Fazex+c#c) z=?!>$+S=XH;l(E>!{|P4*rLd(Uj#du{ZteV0SW2*{3AdjEzW!g-lDBt)F<*YgixCE z7aI0`ZK@}8dFqIrDOM@U)iDroLy|d}QWpjop39OE`Wx4U-K5Uv?q)<)?XKmNzuQn> ze|)|{nfWXIXId2KBsb=-C>$#Bvi_LKjt9n z)g$KjUpIt%+r0-F;f<=3SD9kyX84`T`%e_jcQY3O&$b4&rZz$%{3GarPJA6dEjsS; zYXD{NS5(STk>!!;c+hp4vr>=rSst-$vZPDbh%5Re57Ig5$gV6kg=(&Cu6=bke{5ts zIt>@)G+LL*R091E8PoF~&f9>fe%+Rv!^?TpS+F{k2JUD|=0Rs0F#xf5%$`2ktWXuz z7(qidXq63DmO+ZBU0v6c^dXyg7eMDIHdsJJf?3VZ``h@(y|5+NF$ z>J2SLf(k*+iQ`E9+Y-bGo2^KpS%>hpN*l}klam~aiVg*y? z0WsCc&^H%h%&1Mnv>mV|xxwHRs{cUgRN7=rYR|`SJ7Q;~TJYU55~zj-Teu|q-l?xY zEyy05lq3oGlH68#UO=dytl82GGftOh|1;9oys*2X3_E{NW-Y!gIiL}`-0m0AbCSKK zdzMSeIsIkL3}elY;1|9Y5|&hPia%Bvh$SY>8t(-gs>!oNAoMoGkrbhi7MXO2VTiIXSYt zF;~FWV&B)0?fPLeN#%3OH_-{2-?LS+z9}zJ#|d*fE|UTH!dS{T0y#xJ=3M%MeqOP) zVKV8@q;Xe-(H+DChf-t&F)l8~c5-#K0AJ~}fx^BMR|SC`=PfCA)UhW)FBCaVxx z+@G;mq++63O16q1=oDOPie2cQ*N_oyJM^$RN12==SVcG3B7I&#QE%<}L*#)qbhI!3 zkZ|0W&wi=;X4=`D8(bVqK4>3&bLr}I7 zqq0`a7tS<_DReP(CygwdLYYg#wF1~GY%anHI~SlNUAmb_dG^wC?oJCXPGWF{8B@J z&*~`JjNFl2zXaNtrgTL=aF6wXX~9RKd+AUx)Pm~qW8cBn)(B~Rf@nw`Q~}nB+F9cZ zT@C{ghH{ECoLdZPtvjChXsb@Y ziu)W_%{L5$=~4h9pcr_pZoK-R%(#OfM|f3-@Qq}YbfWoRhdBCyVe8LyUXLr1_m@1A zSIOBj>k9t;l#qG$Sl_@8J-y^v$^L73nXu5ymW&W$=7k%D-1$kxRl|E&-+)drnE7+NB`N*?0 z`YNaZyw7~I7=(dK%O44y6OZ<{{V`hLKt?00mP?#efNVoB)eQ}XNP7~jqKe$Q1b6Cq z@su|m%eqzgOivfzkC>MGg}e_MP8faF&hGB6!g5T)N>pjseH+=T^gXQ2e932W{9;H! zE{sXajxKnUbo#7KHg+ZCB+^?Sv_`fuxf&}>qI~8A@%%|O{37$DE&mRjl3%>!(ler>PT!u{= zyYfZ^G~a6bZH?&yjG^WSXxO%S$~3gO{*JRUh`e42)y!)Ej`IajiA$#=0+mC5O z*Mxc(BnB@36*us?e7tm(uQ>}OfqjfQipe-&k!3g;kX19zIflrFc)`hp_kK>6B9tOX zp*?T5q1IkHG8oFrmv7?c9YOAb3Sa^}PHHrz472oEAbs{C3R+AGAkwqTgXPuX(JH`kPTBkBft#7 z>pvTnrkq3jdWVnT;wW=0R06N2KFL!mQ8!&91YQNiUmtnB5(M4ZvOJxIJZp=$84Mfw zPHR6q7;|Ijs)ud9O#LI#PCpb5%+mimQ@JhLyyMvz(3@<9i`ug|Z}~mVMDV9?=1-F; zb%&l2?QyrmKlpF6NjSylBv~Te$DGL8qO-5B5S_CMWIwtSVFbz(p0002s>25kk5bUQ zdTGTFZh~vZ<7mvTe0CanREMUY6cKSqAJmEWei=^ploO!ps{MGa+hD>@x(76h$!9;9 z!Zr6Ex!C{zUGKjnNQLm&bfSnVWjX!++S@f6*e%e(kG<-i82i1aj8Yk0cwhl8#7-hL z!xr^^-_|?sFX>RvA!A33awRp1?EFKfzrKJnMOeO=e@Mu(8P_la^%8}F{!8lHv>0T% z{Fw1Jno5u?qEGT|!3n_HzEx)EO{936(wzukwjH)*%d1=xv8aNW82KhFzY<%#_+{{Q zv{vdCmMXDNFJ60qVi6>Tk89Sx8~04Q9qyDB*x1r_?Xj~~XI=IsBr+z+Xm|aG1po}z z$3TWs>n#bsxi7ux8G4zito|l_*$)*z`AM#XjM;0dSIVw`m=|IGhruzM5XaY8l_uB5 z_g`_NXJ2zmj$E34qtk+|o1Lby^Cqw@I~0-a**Id{BEMK#R~7z{)qy7oI^3!c_$|odzVGNz0kS#*L5a%>4askny>3WoJY(1y!GAcS)Y;V$0l}IgXIuugwp}NdBv$D$6GW19Jpv zHsh`VY;Gt<<-Q6Oo+&>sp&y?NpEXn23k<6xhA=Y_oMqOA4kiXTLYG94J_(kJJiX+A@#*@o}R&s%2!=QPZ9vIjs?MVtMZw{GIEbkkt z2iZQ+c#b?#)y*UIknR*(a+GK`qvMg6D2=T2buRe=2G%i1%nCR z2f8Mqj{tO5`^je(vI*P zc(B`u2uMe5Uw*2jpXd^fhXzu~S|IeNGRoTJ{Ai|)=owV1sf3#uV$i4sG`-#upbNeT z`T1r&FB66bma;dmKLO0%t44DC_Dn~;jxS|%N7FG@FmQ{f?%0TkNJ6Cz!Va9m zk*XYAc}ZK}A80U1)L;dFxnshmKz3nZeZssJdNBV-E&d;`^O!uZg^kb6X}sdCP;Xh+ z)yycLpewFMM<7ijNoM|E-`<8!%ka2&XX)^g-Sr&34t6f6ursMSPK-;YcLc)Wnlz{V zlJ@a1?a17cM#WZ)=f@hqP5O#cUv`Zv>%_xS>9vqvV?gEPE*>1m$hUVbLyGrl552?6 zZ(3KMciaiKExllGl`yU=hmmeM>UTzVedNndd*b_nL{Db63Xzyf*Swt?CPtp%60t(5 z#o6^cuDPeOKA_a%O`J2HA?U6h@O{{O$lhIa5JM%z&Tc8R4^>ZIu7;b zl~Q{?iyPCLX*dr8Yx1w|+#4}5eWz(+@*tHZ!I7~^u!Rz`G)PPxp3=iv#c!qTIUXXV zZCe5k!`aAlTKOIrh#2toFyb%*B9DNeOM!(&6N;mR{+Dh;?!F8nrs`J>`g%zQPzTsu zdz9CDHB<)M9H3G0xH>8NS)p_a?fC2(K2(Y%!S*$kVtp99%e8xEMEpWxa;mVBnzzkl zo~|DbxF~po&_u33d=YRVbVQHr=bh&BJKCA%-SoXGIQM_;Rb25G8Wq9HJF(t@vp+k~ z=BrCdUNd}U^Kr-I1F=W2kG)L!5d{CK#4(;lw<5nJ(y71(g(Ck*ndw z9#fy)H{GOr1zMF^4WU1u-3HZCY=RIYT^D#isZoLO{;uTzpx;BSXEa6y^=TRx_KQFa zdsJ3HX06A~C7@yx8Zz!FS-X$Yl?^2BQrgjDQ1 zb4=)pYklYz1}gq7kWhOv6wT~$HQ>7uM)mdE{^ouckTL-eq(y~dz^{-{ru6mt-Y^P{ zsP{-+rK(RY+9stA?swPmFm%KDwX?N}t8u8FOKlbau31iu-Hi;V?I^h;dJ`^$W<@(6 zY2CmZ_aw#22Uc;>so?2=4tbiP#Orj9FX|$n+=}7v`#SA#RSW*O!wup#MFnbrt>$x* zO2CAcK>J|)Uqq<)er%52v5nkHxhHQY-dIYXpX)n|v?r5@0b51PX#{JL^3g965e?;2Cz^fA?|vV& z=AR!o__HN)gVG;FMSZWfby8kM$0pwzXW|C5Ek#{JbT*2yedGC(4{gvX>2ifI$bJ+i zrt~dN?dxQpy$b|BUpyL;2N=DiFCrKsL3kN_a-wjWCPpQd3()X+Jn8UE8VUXF;;Y`B zpnttd;|uOvI-bHy+SQcc&<9cD<#F2EKC_3sbL=09lsNqvyC2wUyW z?N&zVy4#vq|ZkK^0SwoKOs}pF@2JpQA1cnyis;Oj2Gusc2 ziO>&r1zLmXE{i2H3)+W*K*xtmFz=)}Ou~B%a>vI*K3GN}21khM26`T9ZmcyVLpncD zr3`a@#VQnz1o>Oo2k~@K*OmBzs!tyr4$|h*m%mW;E&>j1VY+pNAtJ8%g+W1i z;Ddn(qR-Tj&x6I-s=rwdZw_)7~T>D$aivj!jh6Wz5vJc@9BSYP%--ZWG^8!cYu)wU|mt9S90 zN3CpxI!>zoY9*$7JP4(3`4{XMj?{I&C6=lbR^?JsKH7eBZUyQyL7>_09kOP?8ppC< zqtiEs#5|XcP4Uj(FslRM_J*t@3PhIgE;MAcL7fxuTt9He6{YK^x#qNQY*Y&5O>pKOqSqyeJA`Y^8%hWL3l+*qu^}@P z%`y7|kW14yxg1#U;YK}LVfj^7;wWq5AymLfnSXn9F(^){>|>qHmEDb?WntN7ZHsK+ zm)3?^9`d;u%G*q+u=kS4K9BW|-x-F&8`8o+Vx!`%5aiceZO+rU(QTl-;p4lW<1}1# z?<_VDsa=*>?a6$~_n--E^i$^0`uT#VkfWjV$m*aTCMIlzWz?J26=&sR$%I7@|yNWfN?Bki|{O(GtkjiJfUZ|G7H=K*@4~Kktkp{5E@MrXi5{`Uv+k$RZ1^Klro7+G^%W^%F@xQw79#|~kwOX^$%g8g+PRKf z!2Z%#_Eng=%8AwO%{|_s&Q5VH{t-sI_WR&q5rdWay=C0_mO6a1VgTGLEe!YkX`ayY zUs6RI-j@z%=U@8#x|8hLQsCcNL*`$cg@1zc5QR|U(JXAX^KR$-Hqq=LkHDHpjYrxd zFnTAy5q3r7M9$w+;3MFQloFJC)oQSP#fPx2Isx7!vWqDnM5p zMX$pCjkzGE8!T?~lSjM%FPe-&6Jac}{h;F(StQrhs7}YfcMouxdHZl!_J?6_!7)9F z=m_ZF332+2aTPATYf(-#C2A10bDS^{H%V%6BjaUB@9(&xCIo|U`IyDeNNJrAMafca zV*`$s9sgp+_5aOPAH{QI;N7NoS5igUwPq1IO9cF zq~x{nyIlm!Fv&hlE7WsybJfa->@6tBVUYo{$0X6d3EG3~mIbbHs_>sAKU)t+jzT3! zT~a`!PtXgg5_l6K)KPtXeY!Nf&u=_^bU$=l`L4ff$Ty;cU?;VE=8vk5Gn?ZpT-fxn zam(UB>_;^T=DrV$4-=MfTU;mpzc_ma^Lz`pIOqYj0I!Puejm37rBzFz?raX@Ft9f_ z^hFEkuS?6$gf){pBl10*_r4JlsL4Ny9sBrcrQS%SR*b}**mLT+j&}FR83N zGVO7d3$R6pNA)$iNy>?NJH}ul7+=7U=X*Qd8yi>!3r##-91GN;dui;i&#^rq7TD!c zOyM&hLY>!|%O#EpA${??v(or$%e2Snd?P3U&&fvXR&yB~QxWq9xDMCM|6B^4Y)QJ8;>K|A-El)R8FJqygYlE{h`HV#COS$Jly zVaXoL=PP6KOUety%HUh9W_I?3F1eg6&1 zT=u_LbT@Zu9!C@jDxNMjawA(8v!$E*-Cn6ACsIWg@4Q1t$6xidYK`#N!O`bm5nE&3 z>`8|J`?@?Q?R~d&XNGu1qw{Jl$Nbps=6A@+@Ty3C`DmEI15i6EDpQ24(|aJMFv`!6 z6GPq@c)bh`%U8lC>lp7JwCZs#$bYo(A1XguHOps=gICJ3xIdZ; z;YTAwt!qXkXBLeFGOH%i{%H4}5ZUpAb{3NrZagsKhXN}S;5i@7#S#fzg$pM0&G7ik z(!8@4!4}HDqQSOqI!i$~)q)b?%DZz2{Iiv>S)+R|IWenz9oA{y(u44s2D9p=q)mMZVYpBVmHQ z0mFodoWeMP2V9wOlf$a-uF;mZaPRf`W3fH2IkBp{8fw;Sl@eq|>zD?$$jK~EjHq$e z|B>0zFzYZxA%}U$i;Tm6Nyz6t@O|)D85)wwe7M7GT1O`Tk-{2xy3QOLMX@vGKIGo{ zB>08p>?TXJltTHH6e4o|On&2<_#ViYLVhzf<`v3dz?5%Cr#on&&%>A*)ICLnvA0}O ziI`}|eQSCerp4D2n|-tybhON@1@@J!dk1~9S&>Q zx3OCDp5XTN+B@PR)p%KY^^HgCZxRIn`K-B`z@b-cSlt?I@8GEGVRMU6%*Q0T)m^Oz zUOS$h;@}xC9G?O5pyp&M?zZgVF)1hMSL!rO-{4@l^4W~i zGusS{-)2*qQ|{Mh(EBm)v8=qGA%`O?+px%y1tF=`E|PCYy&>{(bPj5RQX8oQyEW~_ z$IdF<9P| zySH-F4Tf(8F=F#&mYcKCa2S9gM!L^#r-n$1jv(K2oEQXB<6)*caT4?ntIkUeo$&-= zMS9;R8qsf!1<8H^jL#w>QQL65kWO2cyQXdrm?51&b&hLjPiB!l*VX;!Z~PUJ7w6Wa zZa`%{Ea&qd{Yr*wa|>p^` zxCrw7Z#Yk~&w0ly2HM7^{Bv#@v;G$M_42t#_E8-=VO$z}E~u~e!+9@k!UZs>YI zS~5OPuwJg6>fOM9?2t7j?5T#*>`18Z)Wzy=+3O!K!P1Zz0;$bu3Oe($GJ&>ldKCHl zs_vn%V_s|mAKT>8Ga*x=IvIM9^2aAIWANVBSY>m0#VK;;jF{?tA>S<>K*bCFuvpBC z_)sHg8Hg1M!}CGLmTw5)J4#&^Uzh)q%hNLqKwh0G>4Q0)=I$}cKpI!y8lT5spU`nS zV`4y?;2(a!z=19|STCl6eTBCh7C+}7%*UKqLwbwW$R_n-9~PgE0cgzbTzbmJ+Y!$j zVXT$d9Vn_+NGJhIb>|hcOHZ~*^(^N_&;7kk0}b8N{qM>ovmTd#2JcB;lUk;B_G=vx zYh`aIyDH#SjDS;G$55e+sGs_c>2ndA!{(ht_AAWu_P6GTScaTnryCT$3YWGb!R{vqSUF?$ly({|b*O`%b#lK3gclXJi zp?5c{+YQwus!o(GoBiCW6?XIDBr(ksO)6Fh=&b5T!%jEUg!$WI$GO+83;qSptY zf0>n9BAm7I8jpZWpo_N(8H&4wZL}=tncHy9-~AeTe{F<`9ei$(!b|{XQQ)>K5QsM^ z(1P_kBSYQvSD0QC&)DE`Q%9k2z&N{aDJa1PF`nAa1o$^Gz&+;v^1Bo?S4?X7cJ5-z<{_;WQt9+I`JoeZL z+95Kx`qbpZ;Q&g=d&Os}>tjZheGjg9RLmxiePnwi!`MsE6AxGe`*XWTMl!-BKoM|c z`!#Y>p#4X&y*RQDA>s6M9NC^jmguMa&v1zLM(fRCe${wzqvlbQ7aqGq4l1OI_-gTYGEkTDXBeGnZ+*?w#OOxvt<-Z z6y2KTn~Cdv3iHMPCT~{4hK3u;+Pg#Y&T?z#3iL15o8yPKXFW@PlI=&beQ#78cb|k# zz3UbYC-ld~wNTTCM+~9nHIg`g%+k`Ay1uVs<}5i@NA7}Z&v@uzX!BA~MmnOw?ffdM zo>~?RH+|Nh2dzb2yFo!#n`A)0lVlH5YM+xOa8w{$qdsHkse0d*D)6L3lW?9yG&}>^ z`00vLUBetWyE+#YF8sgVNcLZ_^_M>Dlm_h(>1nA|%~W-%=_>7XXCEc9q>UkFi1qXS}prDDGU9lP+S8ujIm2fr~fc)tM=D_`? zUCkw7_nE4UEKY=%$$dBW@id>4L<*XvHxcZ6bGmOEU2;9UUe5k0njx@=+T32gF3%Ld zP!j_5+b8|z4*rt))AGt&=~&0)rI3AB(c|*<19E?RG)Gr;8uPyxaO@eODukcbE>c+j zq_jN0v9TFZh?G=mqFxJ<1c|uIpXy!6zBQtKw`s7^*vjx4ByQUB(tan#D(5qN=7dO+ zA48!o=qXz^u}bP9l&fW>I0Spog3 zS`wV&zdNt}i$)v^x5*BuRp-H5pD6u^S zwFUJ|Qi)+$pt$G%$JAN=MH#hSo9+}5q?JaxW0(OEkVaHOQc~$|7#fsPNhJmaL_oT` zq+y2c?q(Qbh>4f`{XO;N`VaQC_j#_hj#bxjo{%_dm}NzDG?smqueYPe9#(gi?IB4Q z+Vobwzk|^qad1!hWCR=N4HxMg-%M+?cQeQ`2YHv_yfnou8R5i;31~57_l#mew|Gl> zxfR)~_eoh1plJYxHW|YGs$-l6^iz(t6*(~#wObt&C!-&^je}$BhX1_((qHW^FV~y>Mt6p( zAlyuOKR0pgv(aadVAbrqo7O38dYDEuQJr@C{=t-z>l+z$iN$}~d>W6s1)=u3&z4xS z{8+?Kd)2)OXHC?&K-YU4jdj{^BBy}?)u^|GdP7nZanxOrc@2_gK!TpdS)6RT`k#XS zUh=#&;SisT<(E1iBA#8lcY;6vbWiDbB)=A*BVcZF<^o)#LpwS147~kQ0lqne8SeRf zByX?(+Bt;e;81%(3?|glTTPixZ)LT1|GH=HIUe9D(7gL(W1tM(A4S_b!V6&i^On_G z=HGUo+{u^SxyqhWyWt&j&5VAGc%(-+ik;XE@+=TkJ}9N3EvP`4sDwv`hCi_k@lW|4 zwjl0rJ*^g}rFF>~yuGy@Y%~_TiwB5cu{dS?xym+$ks?Yo=;Fo|WqxE>8 z7dzxbsBCq2^yN+S#W-qNPgG^X?UqSLbfTZpB6T#VD;hwTDSC2&Pd2<0*((*Y5rXHt za6GtqP9#a@j&=KQr{;+uajVCbXlcwo1&At1Nh0Xw*mti-ZrCx%Z0V}be4irxGQq18McY5qQyHcnHIJ^|App{93_zmL!c?|RF`;ppO! z`u2P-->wu=|E4HOM{ASfAHxRin5F2n!_GI(-eIt)%Zyl1=*?Rr+@;}=E3TIG(Mgk| zrJk(s%8>0_nm-{lW=`DBzV4~PhkHRc>zu&BMI|LT{6=XI+wXA_`4XRQ1?kXm<2epS zpSX>$_E#}$*H;n~$a&`Gzv-8wk*&8PPoL}f$fn1Jvg55(LiHuZQ_?FG_!_`dYmV6h zncW8|uY8OKh>VAKK*6Giltfv!JQ}u8m5VL*MQX@hpuKCBtjVXh1nF0V9aJuddTsor zkgku;-i6;x&3|gO9Zp+c*1Aga8V&QDj^!oBWJG@f;`2mSL9ABb7If}I)$?7EIah|c zSelp7^CHrPs~=Qqr*&g_*|y)clTzgg&F&|c&u`UHs#K7^Y%Fh&U^|w1Xe?#r(>aG| z9R7Vc^mWM#%Ki>lb}Kj+3dZwegph0@$k&F30Sl1_ib-=GUoA1 z2wcrU^P+^6KXN}=f|G^UkvWbib1xn0LgU#-Nq+ifzcK74yZ5`!$Twn+$Z#5pBmR`G zX-(4XRARk(wFys)!b(aCvOIjD?KWOJxciFym{;=T$2CeNDpP_@X`DN@S8H!by0{{> zfY&3AxZ7%(4|BT*CkhRRwvp0457gkVe}7f)ekS<_{i!Gm|3W6r9a0ETLDT6pb9pkuAh9_JZ2`4#{ZHgeFb@y%Ooq~s6M@=}BF>VK* z$-g=r^ZLON=Ib^ke=Su=_OWH?6Q+_SNft8;1t>iwqYf$o#O{N*y94y;y~9FAfm*j}zf`#V{qXrc$_H&eU2D|87|EBh&_`5)IQHR(@u%LRjVir%(F9hy$onY>oi$ z(s|`OfUi*=!*1hea`~AZWIKa(o3EX-@8xkBdy#=pQ6wLuTyR{qT-x?Z!TN|JkPQou zLchvBZ>?9|)0f5$Il=4RFM@8CeUtGIMYKtOu{(7j-}aknTBSy?%;joEY!0jPXuu1S zToKc{i*TE}Fy7Nns~W1)seWlTiacI|TE#QOxI2Pv4j%2Rvy44<^^05Ft=_p^nN5^0>|nxl<3(w*LMg)h>_sQ{DWxO-jRcXa zWtwrlQ?+U~;sw&26sgEqMnTq(d@`G@1NL7lF?^#INF$Lm~TAcSh=Q7plezXQ;6By?8CE8ZJ2 zy0zzfEgfI8o}So|zTRu|&q0o|+vB%05}h&b~ zqTP`jKu-P3^a4QI`wbk>5Jdegbiw^YYKAB{(+@TBCVLCnGkiyCyhZ-#=dr-)HeF?r z^3Jc?zMs0G=G(GaB%ynP=omYHV>`NbDcLaqDI$s&S!2*M6tSqOq41hx<=laT6wiGs z*&w6+r%=gM52w`LCP!Cq3-iJ^U!fH*@(q134F7`ab8D+@41*Z|pKR0En zBikB|eFXq(t=JQI|8JC*K@PrgR8ZmIE9dzziSu5Rd!xt_@&Yn$!oxWyc=Yp*RL$$3 zW8{mD0hq`4>BLROtUTOE;q8Z$NEVTRc~UeUbx4K-;*U(pUuk9WI=KTJ)pdULrpvVF zw<6nfEOk17_=wGd8l64U<}~~jE7gl%v(H;JJgf>!1aDS-Z(yF_6vLu$Ty>^5aP}4I zxo6`&hIkWgN0W{3i{oq3rwJ-VK1-1QuBT{*E3pPx#ELX_(zR=*&zLE=h3JYG=8p^; zi5#L9Er-QJQE-Wyo~y{$H{Wkr<291}BV-1b>OG*jcKPc*kqm82yHe3rrQ3CW$F%T> zO#)sb>5+X1up13O(q0ne1;D3}|cTYN0O$X8wtg181Bkr3{NR zA5}WXW|Xvb7eqW;F7TM-x6>->bzI9n2_-9t^^?My+DjnaTH(}&8ZlH?Of;u$C0Xqh z4F0~+8i>ou1n1T@{xf22|4|F;9`>6{Dc#`Vbn2nsdJcnM_r$37&bJ1CHiQsi!q(FKf;*C zWBWyrr|+TQ4+rh&t3}KF(HbCjT!Et_{x`{m1JUQ7A4r1Zo`3Q7f6o022m(vM0bVC) zQSp8bldo@vOGuokn;mfcacwxf>m^t_bQ_clMeV+UHY3N3pq=F$e5&CNFL;wZvp?#7 z!7Tzfq<#5gOwvdO9N;eN;)bB2R0vJiCYE|2FaFebG6#G;MQ?O4nr z91eJ73+UTSy^-{I_$-RypM?2x`UdL*)_y_gs38GBSs`Wf`-GDAxI5s9Ea`hZRZm?U z!w<0{Yin|BYwV1_)(OZSrANwbsQPX9_OI!8wUfSR<;hgNBQ)=<_ts^WZs2$>CKCUJIcKx$VF5ECvW6^zX~Xsc)0g4;QmGBwdzZ6hwxLd@!l+?J*p?xUaYc znwO2V8Si@@mI?qX55r@~^$@%`s>ME1N_SyZPq48)HO^oBn?ojE-Td!txx9(9e70@k z9J`mlz|~~mQaLH>r!zf$6hA(Hp%80mLPy3=69V)lXZ>+BeR=`N>Z81oGaLj^P5bKQ zP(FS5C~jQe+tizv^-wqGWNpjB@_eLi{MZu~!1}RS{wkg{CO&>v;yhQ;mjHQud4Aii z29*e~P3aJMfa8C)H@+??fBhcs+}qK|Pq8wQi2fmjK$UL;WkcN-AFBFe9`!)(k6!MW znx2=VQ-w<-!y6Yo-{ZEw(g_mAa6<#z39eEy?2kgMxhkzW=(0ETWm~pVeNNEAP!(*N zYW03mGyisY-Uz~>6-ix8P2pmI>2r11tdgtMa``~`jYyc{p7Nm)GXtabX%EIJrC2M} zMP=Sgz0hdDL3vpQC(HcbUJ=o-OVi39GNNO&zsNb|eiTWw1S7m`h7RbCTF&@C`yEWX zUA|ATzRkE@%$Q(vzYpER1Z=v?8yj5k=vuS=>fmZJkQdm)(i+xhuV)lHx}5Y+Y^h8E zmjS+smi}W(YF6vaK*7LTIkUAVa(-^&m8K5WZVQ7^udbQq7);7qwn4Z5X&^dK#k8Qe z>yKAn4Y4iXPAm=uIwTj2=XQsCPe9;LbLvAT5!glTA+$Q@mipfHn`~D|`wE|7ppODZ zR)JHJLe1PXb^7bXT1B``B_oGV82+a(j%3+V?xcJYli81v%>gghXs$S|)J(k7TjBiQ znRoeApKm&siv>KjU3;9FFRI4J_}5^}Ws53l==Vl32xk7{GP)-Ct(C_BA5~Ir_~>I6 zGtwFgL-&ZFoHNm787Tt5)N@Dt^0BAmLysa;941f0Svtg%?@%&2m3V7P9W4cR8C3e z0b29KCKgnB!G(gt;yoC7t%ambZ%aZ@)@kua-x=1>&8l~x{SW&5Z0GDtMnC3HE+Pr& z3O>adaHVo0GoE(vmpL!ymExwxxl=Ch&L1Ot&qZ#VI?w+aDsg5Xe-Fd@51pO<2GGu2 ztI-C$RRRn2;V$VB6ru%#=-it%^Eea8o=F8$P-&guFH*$l#MZVs3Dhf7ND$J(-_G#= zN~(3YB;61C96pX?`nio9tN%xMFm57wZ0>v7QUie=7;zhM4>*5eHTPGe&7|Ms`d}*t zLRgD!_>cGHKTB4>7<})O$;rFe3aFgfE6Q=o&ahE0+dfvLM9S6J_ z;f?4ywz|u1o%*$i-cbG{mKFnH9vog>xh~(pG@!$?s`qnoLrJ60Pq*iH66!+Tzx9W- z+-9XV#!c$ES?;`j;XCk_#i`n7evx2`le4MXZd{jpI^q*L=p-7^0%<1{}j*j-g5dTcd zR6=@GDw_fm#Dsi-H?3Hd)be1e2Q(Xj88p7rsP&)g@KL=C8(b*d+uJId{6D}LR#U5esRXN-Eq;jZRXB1k>`N+~=Zwp}(*iU`-z zASsG0zR%qq|7|t#ia-k1+!5s0b8TsA<$48f^UjeRBY*A-()vkCKVV;))&OiQYD(Ol z3gErV8O%R%k|WwCxg)}@fBvh*z;DzqLZ-NefA<@{Xe0!f%Ia2Aiyh$wSMzqB6X4|n zLRpx8yFBL1_R&IJfXKcez_2F03Gmr~Li7SIJBX_@{NWIL-*&9@XIf8~mrJ3J%&s|m z)vKpK6J2BR*AHA-f^CLND450G3F$w5o^ht~ijg`y_!I8tq}oBqFwI0Vet*;LQiZst z$>s$F!KB2t)ll?5mkU>9hbDZn@%WrsSw-z14aHSj_ zO_*?1H@*9NIZFnq@O>O*x}1#}v)zcFmp?Yzkz$#?zGPAXLvHUa@;4e7H z2?Fjjl&`NSX`D=3YJ`~6f38lwl%SG-6lhaUoEu6#%WrBt7;lfBaT0gerw)R)C4EiR zD4y8+9#}V!KpL`VPmOa42taJX*c7m34~;vXt-5vHsD}bqra|I_9n{Fx5#quthuM@wT}{9Jo$O;0xq$k%bDZ%LlQNFP*?ni1v>-45Y2{+v}f$u|D< zoKxu*YJs4KO(~Sy`_8U z8ewrZ#lvn+oU3!Jt2bJkj2~jn9o3#{6}}5Hzhx(~C?XmB3Y&ARxtsWT+NI*z{vgz) zVb{>k8Sy#p;Tt%t$*(5qJa=_c7K^9A`>Gq%8vI=+LRgEzVinx^=}Hwh1c27KD`dU! zs(o`7(@&Q@fBm(MhmnU8ui$ul`t%G(&$+oE-WUUJi+E~AgK}iv(&=FvKC~SH4Rw7^ zl(d6AmfG(vgc!))@|7f?l1>z`5>g$VcDl=w^{iQCFC`x};H|w{xJ;WRy{i46i}5qQ zdetBXHI*wlzCUi$&eoIrc+uuaT$-f+Q)sbb-G0>p<^!{@66-Jb@@<1$Hs9o991C6^ z?Yv4Ggeh*&$nPeFyYF;RXtiQ9ldu}Wod-Wc{zC|*Ted{t>rh|r#evU?w<#8ObfbS! zv8^ZR9N_d@5X>ITqZLd~@}^Y$KI*HkaEwfYrXEvxxEFOe{Fcp(*8Oz+ulzZugQ>e^ z-gA8m(QwWoDw1L8b}Aij5$ljEr1DV$kr>`-2l}$5=dIO3L8G)gq98q$eYaLRL)snW zGc_|H*Fw-*MXPwOvHMXg_#Zy0+wZbId%+cD1Jmmu=Ec;IIHPi0Rik0T&_>gReqmV6 znm~o`*-5aALHIYgLmOUc+g*vn3H>TZ#XZmJ1K51 zwNyjg$-W-)EKQ}!q8~_xDW6P0<1Hmqb^==nH)|BpQT!00w2qWYNPtWMstGMT_#X_6 z4}sIwi$Uiar2T}Lk3Oq@KMxD3A62ZK4w0n8Sb`t1A3@h*HZ$NsS5H;>n&v~jUy{wV z7TGy!N8$srLQhM4etEid(!>$j<~z2@oQ}#+?}}g#YW>^==%ODw#I5nSpSU4qp&>BE zS$9sxt+x)Z5e7u8*FRxirAz_?2xp=*Tk~4NHy?dxmC{L=x&x$xhtXqWVX9V=2P~QC zkk_@+i{JNb_LB88N4?cD1!e6kxm%4a&o~W+S4hlZc+3#9y!V9swXxjmFWemkSs*4{ z`fw7~?Ai5x63>Xg7sb!O*L)@+YiLh+tqd-a{h~~SjslJGUO>p1IBMYAb^a#QIRBW_ zE~lebJJiY86+q7BRNT?bCHd0*311O6{~&GlAAYKz=D93=^SdaS!LJ6d@KTA_13UwR z{VzmUaYGRo@Qrwf*#6`PG$bHE&<(=p)+HU;BOw_?h%+KE%JP^X>V|KW;2{CoxA(X6 zd`wUU3$JU3OLh3lwamp|U>H#I$#(Z^_cPZzl`$y#Z!T@+b4yIYetZve>%X8N`Z>S% z+++Rgh{~)7HFA55fsre}kf;)M&yHq6 zq)t)-l(Oj0I9I9izRGO2y1T64^znuBNhEibLLbkk%?HNN+1=|o*Q4a|6z@EfnYrZg z@M#~@khL1_J#&~)WKzyTUYm$s8x*5x51ki*{p!bFOvm?}7bR2Qb49ank3(wr3L{~^ zkfv@Ic9wqHY;0b4A~c z7^fl9&`OUf^3db9GMdmkjT{0}3xj=~6}I!>(Gp(2<5nU+BCGciViKr#)0PL_mRZlL zFQs#ZvZ#_?pH8*8M-Hw1Q_KD&<6W~#6DG4a9-A$);%6-YTdv4?wDRoNN>>cT>hkZP zjATrP9F?WL_Cp{u(C$K^)j$T+Ydnx>t6>>;ywXY4YCwFw@Zx4&`8vZ%y5rumWEVG0 zd+T}S5)rgcXnp(rZJxyM&+pIz!C^Lw9PYR8G}#=|`q(``4Bb#P9?RPSn^s&`_j{zD zpGZ=Nikuw-92^|52yy#t}v8z$h|NEipY+ z7j2%RXpyple&E)po(I%(*iyKd>Pa2nmie^D5E{N-rd>ud5zl}C=GL?34q z$+wO9);&5JV|~<)DxpY3i~B|y zH%S81HuzhHWsH3V43-@&#c%h}qE%b?ZC>jjYtHA&z8kmlEpSYW(Qs@B z7`)7tFwOr~Bf8_qz>TJ-0igY%et0g>FO~aizx3d+10MK#eS7`wZOaLCjL~LOr-RG= zYhkpnXX5^3`0|rL^sqOqCP5Z|VifqRAztRJqim$Y zyB=2%z>4&ZmJ;Uu(eHA^8#NA)^kT#a@I7xAF}p1M)xW~&k{7=E$aj$>@{zA)!yHHW zm(6`L`t#EJ_FO*B+dIFg3~y|0mPKvfvHLsserlV2*!iA1CM|YvQrbVrxsLtEdBR;I zk8o%0?IQwJg;$UKN6eZ#M4x9=Ujr-liDX_BR3pUzWEF+WR8)iHdDU&un)=k{$Yh)s zIdiwEHmd`EE?N;etu`s#l{AYl*Phca7~TDqpL-hdR7Dm;30&JJMtCO8CuWwq{k+FD zw>N*795*nO!w`YP+Ne%L9|T6y=Sv(%w*rj(pm&a{C)YN-;ZEK52QVMZOhKLq$)8ON zL;v*O<=mv^>*P%KX(IBxFW>GeifVZs*kFm3^zGdAET@HUjDD;EZ7QeC7Ef<)>U7xV zj`P=|_^%pd^=SA*j{Gyemr6q&^7Y#B%-5`Hyx2U1?;VHju^$WRbD`O52FDVeK+g=d&{a_^AG2 ztZk~lCiW)eL7Iaj!_(qzk!$!Z($oD( z9P9YY>oA>rT#2Ig!EdhsH3|)@PGSr}F+DzzK7u#>C#|RfYmX>lvKj}0MO5m#Ue~)J zy=#)^VL}UjlnO`^w~HOiGiA;nFM{i0cDL0u7WVYr5FFKjLE7@Dbk5t>ARseA3=a;% z3w*$YyTbiytw0wQOMpw_n?J}ONfg)f-Q9)D@UCB<<6YbCPFmbE=9Av{(aAtxP!QW2 zje-MTCcYP>{=)PY2Oegh@^U4QmjYk+nctkaDUbVvmA+JzKFAFqZjp|MXbS?eURe9 zW}biTe@&4~^i-&I)D|3Mdjncl?fHuXRCJSK&-&b^(o#Kp5ZAo(S2d&Ak{4pF**wCg z$?{ZU`vI%EW!2X`59r#D!BZ#K++E<}AE;*eyx+yU!$M~KEOXpPA$^Y|lvUIx+R{(R zqwXAGZ!z+&MMELKiRYm3_-r;BbpHHO+9T0 zMaQtrH6;aM`lJZ;(ik}~ICVBQObFsBU2(?)FK{a~pU>fNydQemH(e*jep8L6#g^$c z>HgX_yd91gIzR3a^*C7qp60NJke=|u81mdY&{qtQG642NKP)M4^+OnBjJaz(X#Wc+ z)Hfs~h<^~hxe0#vxl6l9+3^c3FY}eHsK~C~u363D@_Ku|GO>{_oi5QZ1^H%KPADn+ zHktw<#X*Tv{QTKd<*hPJUi|L7%CDc(a6&@VS5TbUd||H5kKU8wgk`lT9I~a+66rmZ`6#@lrp)Xlc@V~3xiTNYrrnSSC z?^$WbK6t`kg8)++Yu+l2Q#SM}|3Yr4)go!TEV+VIPoHIu(=o-a5}e3ZEf6HLR3++9 zq$e7hb|H*1RzSEf>x0 zkl^f7BKrlN)X-Gd2%H?WNG*!tp&?Fp+4?eH_`129Yj7$jtbSIR;MK}*h|Eo=F&X_s zBpgR%2?9`$jrI5f0cp{2goo|wb;xEzaxz@AC>=9qE2>-ErCAVm_%+lK_u?Y{XFZ&V z$2?KKT20UO4!^J8L;0tsC=%7;QYcZ~y;0@C1Y4^KfEjMQQ>nKKNnYg2u#}V7Q1M zOd)*ZmREhn`KrS&^v2(MKNKlimC*Y=xyNmn|GuQZMBNK0{nUZm*V-Ex2AzMwawD&r zCH!MTmA|s@&(oud90{(3} zVQUjH?cu$y#jGv!oQ9muCkjhX%tYG*zGc)5_WfOcu;dcBMEY$B5~k4d*@D`9+sL0W zx5|Nx)5j#oYGN&Rju`uc5^JDrHS&TS2!4qhtR`ogR<_YMG0wb&AlI%pI*zwZNmJ~p zr}H)*R1BzleK;oFWJoPPZ%L61)QbW92*T}T`l0jb93bjLclmt;Pbo`2_n$D)qJ+zC zYYHW9y*d3Xj$rb|&jpKOW=iSPNqd#m&TwktVCPWi_0(H}*SL+tG5@OTO^2EJ z08{ms7^H5REr)=57#Z1s-n>UO$+u6o@qdeo$R*% z6NFH*5%DcB#4EWHcI+B9x$PRRlj#%L1^;2?LLgV0{v0*v)u|@?EJq8*c}?<;Y>Za% zL@(?~cf1mTgyg&8_W_mx?7P<%E0*r)CrD=Rm&~ilv>sUHJ!;)jRsuo3@EY}9%17?@ zb@)u^ctzu&d*x1$?rR$j2k(U0M59DG`$CzFUr94_e z?CBLNn#fHhpMT7qOsVNP9Ioj>nlvL1#7`N2aq6P);xATf$730ZY{UwL&A!f%02YY&7L)gq%Fvz1pQM6w9|B8{j{oz5e*zKFabxXF%L+6y%Z;a@- zyI)7YSsKdh{&1BcXIoaS;2D>~JMhNN`DH@@`5EU4I$|jda`Bj^41}{(fI>>{_tCVu zr_K~t_(H`%Un0V{ccQR^g5NlciTO6?AEoit2epG89ACfqvUag`&l9@$?ev?RV7Ztv zKHFSNdqEd39IBmX1wny_OWR_1y4ElYt~ktCE>3ZZjv8!pa*Rzerxlr>y!sEb85Lc- z5y5*TULU_}zc3$d$XA(#!60IL4!1)5LoFI}z8X4Q+xqaHkhXTxQ}EglZ>z}?u-}G^ z)^3)Pm9vh(o6~JH-uFjgncJdMPzMC$)bj-&YbztW{t@IFiO!cEfzXjYAnByL>Yr!5bH?ZGQuI~P6zFzG2dq}q%q{S;W>AFt& zBaTkNH?JR|DY}1ELg$EL65lv4&DdSh-V#+n3WbRB5k$GTM7CimK9M;+kO|lcg74qq8gSFjJs#%gY?i?d7apV*d_LN#SHRrmSmMVi@IlxkqOY)x_45?C~Uq z?T;sytfhPzfR`6Obk53EHS==s9PL#Jb6;?CvlD8DCs^STFB?)6JQm2#PgJpt(y&vZ z(F*{4btTVvBZDGwZlv9Npu9MGJyr2{cl+{h_E`IDck6+mOB&+gV%x||FZoehzn;#{ zH&UG85ByVJ0`Zl{@QxW6?hCfwpH1_U%ze!7d;@l3YhAA=aB)S>%X?x9w^7Ka*ziIN z7rleBY5otfnMyb-4X^^%*54kfupLqhu}odd9B&(?Q`9Clww*{cCT^lA)p0MHH}Fog zpGk+bFXrv50$LEMbs3DSJMn>ZZpqSIrtSp~&i={nicPdzMx;0ggs2 z8w09ZhO65qtT&zO3DdN7??eOU)R{$lHG)z`(-6G;zZYscf=k-`ZAJKWdrhYbg zV4@DgmLurRIM3_7Lig=$K`?=gjJ7T51sI7Q?ombL?n*@QSlz1r?L2Tu!jIR%O&zTK zurr4{Y)pdqNSvZ(|CdlP=|QEDOlXb6na=C+rEgbsFRg5T(LuB4FW>QF0+ZW<=htLm z9}$(Rb*4}1Slik=T{G8|_tq=>bfPYHkb?P`leY&(qibp{v*^ zn9jY#H|UeNJcxDgrVB=z;HX!qyV(U{UiLD!@lh>M;lZlhf2K)0^AZ4Fkp>4>ASrE$T+_OhZQbIy2@%!QMO|MeJW@m<3mcNp@U&12*i_R;U@PvDc&>TNU~w)oitbd5Ih z%^vJ?W5?|s%+YKESS6E37nh6nS9q_VPI-eVi#iZq^b5F7VXK7%m9Jv1GdfVK=;I3T zIq(i{M-WEjw$DV`a2n}w@&%gQSMtPElB<#9&7U;{4sm9n#D&xiSAP&_`ed~beU zkF?}a)x)>%afY0rqFQh6S88@NNBl2wmL4&DtF!l-7IKljWctD|Rq8``Vs`OE2c*Ne zbzhxsqc+iVDfh=L5(Z{}OUH*8!^HBxJ+JV596JdoD;nnbBjI03iWvmm|GTV3wbvfs zR(SC+%ClUuv$#;7G5bq!Fc&0h0^TZY#qFw=%j!S1pT z0n@t)w(hZdNHzT6%L^$^a|OUglx)Uh*$uxmuM+0PA-vi{KZW|;mrEIz-q~$e3%|6Q z-*2oV;6%D)KLar2hO_W?MBA2|-Euy$CB;DbAp@9bQ=p@wVkb71fhv?|tUD=mb)S|n z__d|m*3hrMM>^>rm+roh`m8a+5)<3Y^u3@6B^-RvS#C{pbGC%h0I8dUYTF-oNiPMy zl(wFrqz^W}XCGju7Gqt#GQmC<6s3Mo)*+6O%jyPuqGYGga$ieMF z^+(1B1#)qCW(ZddG2=mH7Bpw{ChfeGU#x%A@DE9%3dQ^bxj#-GvXrC= z&hGaMF0aJ?BBt6XKA`+Q{i&h%DRWI}uB??gxX@> z{U4_o=5Z;aTUqdl5D$5K6`;eqVAP!Gq_-3?!f_CFuZ~j1dD41E(U(^15G$b)cns`~I$G`pM*;I)(i@NVyMUTag zqOx{ufv~6lM)9vxz>O>MkFSAd5y83y>dejF?$3{7RTNXH`9(qR@qy~E2s|d>N^Zy=0uNOkCK z1PN%>@Y}HIc8LHRjwL;H70AhRi!hNWO?v&~;cV1va~D>I)q?isBuX7y=B~v|F&+oc z)zABO+Whi8R`fT^ZN^ZqsmIIiH3nod$BI3a4rJV7+$`DAgiHkGtj<3CPXZrD=?J&j z$4Ps%q%tShPY!*#P`}}(b4GQ?;t%iRp=8`k&Iv7sM9c-B*r>X04t+fw; zp~n2c6a(1}YV{p&9P&s*)NA%~ice>ev2>KC#{6#NQc``K#*0s8D?T0n;bXVdSsCvG zFQ@F)ywrESaNLw&x(cc}mHrsw2Q|GZKei|KJH?ON*@aw6gwGfIDHtRZ>8?^;`;{ee zl0XwsH2cHorhRhi;QRizio>O;GBK1~`3FezjpIPmLR8Vqx;c2XxQ)H%A_(LXlSPLE+`gAn3X zmjL>bM6P!WKlz7aU+!cRXeuPyDD!3mKCcgHJU$Ye+2aA-dOT1<2zP?-&i&YdqV~f9 z=d1s9a_^5XM4s6AqQ6%Qdm^$5zV1z-2*FHmEqJfBCdhWkgTG>1HXtmATI|h@e2^~c zb7md~>ynU&;lj_v*Lk0J?u1;=m$N_lKOgBzZU;UwMd|aPrp{jG(0N#kCD+c|?QFKA z4g#3*EZZ--U<=3C{n%4bFk&*9Ep%Kf5PX%A{JFH7KfA+mLnvlJW15JHEvt4WHnU%k zs5=s?NKR`V;k02EqJDSdca}Rh5cAp7?Og+7S3~n|29nkCXe58xRu{wG9o~Un1O=~{ zUMY6(&IWGi0Tza%*56@x{z?%LV{hMea|VP7Ta@a-b#5t!q-APpXV)z(tw}Iu6FcRv zFA6n$goH^RF8xT2px4`&KD*|X_Dlr(;|l{&kDqy5OV37`9hChoT@!X?Yj=b&77A#^ zOOEgE)||*J)H|erV`P6+@z!aKpxrxYIVA1VbW!O)aIntZLn}Qi$&l0&rHj>lc6V1QpB%^UAX7u_uU%b%$r_5?-q20tbW z{YsFac%@d;oL!!Dna}3nmry~N>L2THiTooL`tY=~lNnn2qd$DwRdPdJ@k#6)NvRx7OFmw@iCDqhnG@8S9-Jb97tJ$6`)4W*KHn!ou7h&}wNvPW* z_?*6g7r&>1$1r-_E~B^cF++3sZr_O@*B9Po?u(>|iI&-X4!n-R?Fe}h`M|^VBI;ED z3KojF>cO7NpeK}*iCQ(>WZ^OR|NjGCU=bBTS>Ef!>#L|O)C|D!`blVHg{)YDx`^J9 z$DJ3ZH{U+EPCmZQ^Nkrg_$?-8bt4d&?bEJqhSU|I%>-@q-)=hgvM?POjdT~4a{d+# zZX*+l2lF;k1dhEIM%&3c=(CRs7x{(@yPZuW*+pRSdE z{4Qgetu}R)Byo0LtKh9&->r4CjI>s&PRrInjQM|zTh=N}2i_&$lABa0As@P+{aD4rknu6j&`y>oYoY9C zo9VQVmoVG7*J1e4XCrBY5@nL(5v8mFeV+RV_*6Y1Lq_(bWgpZP8#h&6x)@lr4_=8q zdI)ArD9;pI>e`}R?{>|sEXxBj{;Af>g*NXD1hccvwMkW-ACftUDMY(8^j`P%3i+?d ziLb@38-I^HN^fM>8Sw43d%kv|#T^{$R;Z-%v90QOfY-B~k0%hW#2$TwBU!?I-f2VD z@WE(;lx52lY|TS`iGjDhzd9G#4F8UPwCmyo8dbOs%f~d)vULFWwpAb~)aL!W^=6Qm z{gIMCBNjd?LV&_ZDBumMg%ke}nh9Qh-lZGK75_w*1+bW0zRorrF@stg5RPh)f#Sz}}*fY?wp6z^@Z6nQ(ly zzo=30|2d;fEdJS8j2mYAjvBiSgFqYLQcW>8gg`rtpuxQ;f7OoADM3(I|Kmd?e%eI& zV}+mtu&`b%pTyu+!)lzqgoSb zS!f1zpVU=REQT81!nQ5yYLs1{m_l~+#qCM{Q^{CAdnfM&oK^5^KjlmjQ3_NdiF@<5%Lb>XiLsK^T|i(3Nq%8;1Z=!!W5W&p=M-ke#b zks5NVP+6ghC)@oVZQlH%fe4#WH~MeT-seXV+7SnX0T-R!KigWUGuXE*d$IZ}tFhO- zxn3d-=uatUSjF42Z{I3rCkgUMhdGCM(WqSzmJ9#K688nErAXi^6g0l{LS}u3&!A%2 z^s?YpYOn2?cB+IVsYH0u%Ws39d?`xIT{QLIlhH3}c}&fJy*V%q_+8;XCm`Ni49VfU z#j>Ye%x4M=MUq!3D5`sc5 zC~a-iEH0z_?zX!9-|dSTs~}|ynFKZ)nQGs~X7v7Wmj)nM?-mywlrwh4C~V#=vZj@? z7u@`elWV?~-!wD@NX0c&RfpARxS)?Rzo&T+&0jZcoH9OT;h~Z_y=Z}(r8B346J8XE>Cq|C>lq;ay< zYpNUWczk)-+TMUlS z2o!6@2xKee8gg?OaKG|mAq^6dmak&#NP(>P?+D1j7Kshh!Rs0Scv}8^yC&8SwP$9H z^wYINSfWmNly5+2|Jsz*4I|`B>(DgzY6;XzieFHD2P>93*M|(Q0h?fOxO||ra@vC8 zkUe)0V$7^{p=|?rL?`~Nxn8zyVFlCqhQvogH~nq~gU_U3fHPW@6ZOeP_anN3^BAz* zx@jthMP-~-*IMaX=sbVasxMw;Y+2C!YD(7zST8&N{{fyrVZX=4S0_kYe|R>&#{&~q z@v7b56DQ`&`TXjPw|$FFG}mTvoWa;Q6n|83kEcqm{p10@yn+CdUGritV^uo5D|dMM zt^@5W`=%3sJLMr(SGhXqLGP=HYCUMHM*qY*HZ{aB@3v@jCc@%uvH+&o=FBI=cn7RF z$6TTll9g$v-slaNGs!>w+q5H-a2d?X*H`WCd$AQ97h5^GT3s7i9;3a`T)*T%n}dV@ zZ8(x^8zyHb{w|#CpPRAs@HPIC-#++;zQvHyKbZ~(@}~_~<;@L^j z1gG#tqjz9PczDJq*(;W=$DLHgi!CU$k)sofTiylGd~J^aM2ETCTYcXhptm7}>6?TD zZx#Uld2&EjAD%pW_Ny=c@Yz;7-$~$14lNFN>nX!PoXIR9rwF&9V9-otDebhNuk$?e zndnjeO_EX^u7(kp2mYC`n!xw`)z#mb#8BQ8wf+e7*N!Xqt=}p2`Z_SwV-&zs=3}CH z98-91QflGitaN{oB2mn5oDfKHtvXKY>q{Mmgz?%-)WVrDG+9FveCuEf3?^t4A8*kA z&g1iQZ|AxGcJLX(3m*#=;Fz%AM$+%VAH!jC8AGaTGEJXv;~6?j)(3EpabUn0B=8s% zE8CiLyu+iuW<)Bt3wac^&5#>;4tp^TUf8@mFPe9{CBCBPI9kW~8)J?z-XRC?w%cD|wS|`!;l6IS#o|JMdZwp7 z8n?+!b@kyVoG=fpk9s8HfAx!Z9{yjSfA{d;{*JRL^G$lgfj1m@!+|#(_)nVy+3Nr7 zC+|J{+h0C=__zu5nTVZLvN~c3u*EmLnw@Zd@Wq#YCeO*q7kksX_{iyk$L~FBvj0wt z{~z>IwM~qJYr{ePZG2RJedWjOsKZzMrTjjcZ!tkUuy`}SXu+Fzo1ZOq{o?r-n;eQa zv_G*@J~jf*dxuU8iVjIU;~fE`z4~Z!!tW1lQv9Kq$7x0<6K>k0q_9CeiwP!}mCZS$ zKiGZ5q2MnbG%nF8p7GZCF8ATTy*oe*$FE27`q-(>c>d;e#n=(+*Vf zhn8*R)VlMpm0dAh-|CQCao{Gz!8L9Q&?|-)ccDRogL|KoP8@^&d%*;b;KI>0oHk$R zPV6Y5L40j;t+H|x`AXLA#F}RV^DRM9bgXXez_ZA-n6$=Vs=repqcwd2dvqml$?$%v zFzEQC{ZiMrZf@*k*40_OI%B4sq%~@g-on|n0N`za7ob7&pZl9Ju3x`b{-aDr_0KMS z`st@+`%Zs+?S7w*iabg{_e%gN%`FuBPU!KbdH(m=ytmbP=aawqi~r^ipMU=EC_E=Z zxMz@nn;u-slrU04#(^@KV9(&DL<}*7I*NBNac|PIolldk6ghAik&IP+SRA0_lvuLb z>VA{p;KKtv6K?sO0t49B`jR&#WK_z7(+pVlI|Di;j(to&&YmSocJsMd^H zz~COV;b4^!zVb|Z4;_rutGBhY7vKU12ehd_1F{~JqhTx!7ucAjFWxo(6j~2^@~W!5_0~{mF4_ z0*|gu-ox>XTd>f_2nGjSFpTAnn}wt2^6+p5FsFLBb6_9Gd{%_3u(sa9?_C96uBYFP zW$?goE$Ktr0?Ke4x;gt6IQCba{@?@%c`o~98m}s<%i(2w&|!5O4FV87Xma3nby&Ad ziwBQe1Tfk5GRo7<;YoldpW1=Rs6%(-$q59L({uE0!2)>zpby6ZSAU1bBZG%G@la&2 zfjO|@c=N*1Nb>D;3zH~Zsf_9r|<|A#UeAWWO=i&dme4|}| z?a{jl@uOmy_$g)pUlNNgRc~=i-}rsUw)(`!^>Z#quW`fg#UNc{hQ1{JS`24WySOwS zm>8Z|w=1zj_2F=O>wd6D&^8S&;?})-#@(dVx6Jbw|HJ>BmJoN^byuvEJfQK&170Ca zdoc%?o4oez1ciJt^69IXG2GENc|7Rt`oxLJVSjk3!~I1g@^v#gbk#rCcr~26CWBxc zJg#xL+D)Dp>(q2H6$8!P<>bP50NQ9E)m8k;BKr%sJ8`hNL~Up##9{xhw<`J2D_oBwm)t^Yg< z)rbtWc)f3VKGC*c0qFYs_j_EW_hBf1)$u{8tfA`r;nPn)ee~CVM!y8`V>$tFEYODW z{C7?I3C|ywy}WmX48B~FBnv-zUc*2f*K`nna|8HvP)U^19nY3x|IRbM;X zFEWx-MvBpgqB_E50(ifug_=3e|4jnD<*KeJz}cBSlSs~iOftX>h|f$=%4k)Yf*hr1 zBr5OoZ7)9avn%S&VFiJ4aK!(W@i`OV6oN)i?6uuQwfZJAlxR*>(bMRU05hq*~LzIDB~xX0I8HBmgq zaKAmuDRw`;gabHVA5SnGt+Fd)e6|{&F~TeOq5I_ouu*@v$?{h{Gj8(FSlXdBjus%m z-Rgg^IUq9e_19lcF78P-xHlbkxjE#mc8*Wy`8URICj?@c`bNkL@gBX^$r3vS%R6Ho z1M1@s!Tk2&Nk9E#jJ#zZ-ddu|Q8+MsHc_uVt9A=<)A;U67*I8LH;?yi+?Pzuugku^ zZ>;v~3J@;6RrpBFGVVd6x~7hib!6|xV z=BXDQ#wRl47ZS)gION2lKU@8WJ8qGvF*9zQ_mlT} z7PJK**XVd>FB>{eZO*AY!1G*tL0|r}%GVM6 zHZH2iw>ir1QAoas|9kiSC;eK%I}bnUojQ;6-Fx({c3*Xh&P4jFkeY{&knN;)FLpr~ zpA}-~)M9N!SK9bBp7)y=IeIO=*#wUN40mnM0>az-l>sq`MFlbK9vi)Q;u(Lmib+iV z7SFa3^_zaN==&BHJ~a8SFB83(-sC%vZL?-nU;GX3;@HX;*AzQ>+(Ntxrr4DKT?`f- z5fEL*tFo72iAVQ3MlUXsS-b5JFAg&ncu-#D-v`TL47hl^_bKSRfI%!k25iinSmNwd z!(}ocpaZ`TK8g3m3g7o`0}BIU5^1han0 z$$3=ZoPL$J7)71I5!ak}8oi4L<0xGGZQ%nSs_P|q7FTwvpkAuA1rV~Xp8DuqOj`Xd zZj5#)p^r=-Iu1XE=47F2wf4b-GpA_h^w80mwnZ90;EZSbZq$8;4}Rhy-fx}~Z@_W7 zxas%$wdFV-@B7eIcjGY;z=|`6*T<1-eUYw-o4h_>^nR(+?$P_Zxc)PT+t~LK_Y-5k zD$PD5DsLOwA7%P6Edczp1~@@tvwn~gk|ZyIeEaQR+SNTqCF9xT%c|;`U^MAXP&YZG zbbah#ZYMJh+HPhJDHG8(qwDx~3YlS|*qeN&gnRZrBWALB_eGngxWQyZtm5vz_7r5w z-PJce|G!=MaGViiP*OfdM8&N9IzUzMc+fI5$m$PCBt1`OO zm9!iWauo2kU~%YStkku_8sF<<2B|jqZ5+V_hgp(LAT1;qFOCM!^oid)!q>Q15Rl*u z{!-t2maZE{U44D%&mxP39)|ntFPpF@2Xnm1jyl6VSioiOtmls9nb84?_Cf*OD4@AFF)SmcWJv57xM7BC;#V~J-i81 z`#AG0X1rE%D~d7J4$L#&9KAkr0Oc{%PowooKUL(h0EYSxzyIvv%jCr4GkE^2dS3L$ zA#YJB09xX4Y7r6f%uYpo^|oN+<@(#`Jo1qPt}a}oCRpBoab$cvMr-XXEj+4h#}DI^ z6D9an_qA!fgZ!?*Fc#1pr;yd^&Z{IgjIllcw1)GE^GnvfLpHpV}pgE>|Uj9B%yMGyzved74AwBEGeos4HY4ZOlKkwrhyv-r+0Qkz${uugQ&Wi676L4`~ zdL*C=e$Tsf#0Y4=0}6ERNIWN3-(MDkyw?O@xprc{=b!foo|u&1t)5ut#9}8_8O-X7 z0WID?&xilMg@w0@IpD^D>hi7XaD?|yJZtBEu~)d`p+yPV`du7Z+dUU<0!v_^Sp|42 zS6O|rwaKZC8GAZs<3wEAxLo`g&hf$IU+g6!Fs5kY&+BrJc^ZTLd|h?Ko0|Yfvk9~f zq|p%~{M<{i=?dB`cGdsl*2E z-7ALLNK?+fUsqhtNjZh2D4WOxBT#c@jFlUXLw5QxML0X8!Ieo#R_`nlfHlRfjIpGk zTTz5)c^*wr$7(CX>_d6seov@@rRe@lik?J2gTgq@$mWa>zSlAHDSSpzAEnV%{Ta90 zz=R9>&f>zEBp-ulsU#Yz$8pctX4JNFA8%#?Lg*~UL_+oaH^U1yql8};C~C(rdr1ff zYxR2g)Puh8&ywwTIpnwY^F0}|>c5C57I^l&-5fx?z!!jPgRD7r#t#n!8MTub#~Hb< z$9tK{hj$Fjz%$t0b4fc>&v^b^yL_I zrs`EmVs~3vG=Zb(f8{X0GtT;?NQ()Js@DJXvK#)Z?{jr(W{W>jIo6BAw{%fK3H7=Fi6R_K!X+;kub(gwO@3b!Gk5D|- zUn{?wsdr%geO&*z?1uS@f|Hsk=3m=Bh|5*KhsQh(&{-N?e%7fletV8E9KPb-lNoV_i*7NZn_juOFU9;WB zl+V3=xO)>}@rn4$wRmhh;rU@c>fK@-got;f69XOJzz-Sl#c=sFyY=D$$e2vtK7OYX zE4LwJLg+=t78>|2agl2iD5n<1BU{w3KQW1kpcngn*bDyN4G(Yn7boszeLLAG&TALH z3Aa-WSa;f;_wyKsCp{j(=b`V*`tcm}*}XSNZ6x$4-X>J>VKhg>cvav0o`ca_><+g0 zM0hmX@|kFWzxZUEBKgbTdBJaVynkM}7EaM()1kh|<~CV^s}H^tPsP291EO1>dD!Bh zzH79YZE=Z-+}V5~-zPQ$9n7;RZ`T|j!E!3$Y`TbzjP(_p8OV)k^!Lxp579lnX}E31 ztV($416{@$pYdy-+U8gSqLBp%{gNYmu6ScVi*UzXU;|UV$$aq1$2KD? z)9CvGB{C1rV$hu-Jqtok6M&5_i!4K_|2iq(XCpJ6+Nl~k0-<1z?(QdJ;Yn_`;BeuI z#+bkP$7m#XmBG;oV0yVYJQ(#pc;YiM^i403epD=X+x5*?f;YLqi)g%00K}4a`YFK2 z&!0bUuo{q)>ilJDnzoJJyXqNp@PZPnapr53@L@3@vAOHH0hZ^ z9a~Oc@TGvm6OFZj<2@1Iu5dK$F^K9?L=Nu$c^M9bq2XW{6q8xWYKpK#p*N#CGZ2)PqFEO3Bv{;Xm?wmToL#$?X2ax_2)57g6-GOz^>aJFD!yyQEgsMFCBoX6J= zzUKiY$Fx8i{oQ|E`z<1Q_P_d`;rr%mPOQmkytCZ&yf^PZ=@|XqDe+y;_|;e3U7s&4 zSU76WP~YD}yuQx2-ccao@VVM;{~2?6q~65+Q8M=m4yR+h<4511;IqK!(f1tM+2J=X zUPvT(@oNEokC>yy!!MkYh<8lx+eLg&-s+tPoKm!)k-ThNqd^}QsK@{($n$-_nR-H8 zU*7c)&;W&OU&dE3hG)iB0QPl{dEj*;H(B8nt1!wfuo%CuUVJ(E*;vz?i~58|uyJ9(!Z zgp*A!Z~CsFIQzL$RY&E7sPP2o_>XyIuWxw=O=^Xt&<@yDaZK-dFtY2#K60wbuxZ%hJ zdqd{Q`k4S8Ik=j_0}dBH!|R~5N$=xy{Bgg^;#qjRzrEPM`$Jz4V7$NioQNA=%F!QR zPL^(3X4}}i@PPSnM@x0=RI-=S=w+As2X`<{j=_W~U7Kz-Q9J%W&!oS6$KYN`WU~jen(4V3n*DLPPY!y zg&ViGnZ82FEGj9kl`o>3oA$THz`v>m$id&Ayi^{+jCz5q#^2OT{+k5OeJ_SyD zf6=;#*m%pYks+F&4u*LJKA9(Jcc;a{jp3=6#PGfRbA5W8fV@tB@w_~74W3UQ*k_%T z?nSVhw`)T;s_&;WrFih@SHJqzU;O)j|L;F@@0Z+Wub~@h@}y+_k(m3(oc`<<0RCeK zGeta-^;*M(HF5Gx*1JDgBxthpjM1R{w*p%krF0Z%KT9{Kmy9K}93W+#g7xia^Le-6 zQ7C-?CxMsfPd6EQiIY6Pf6hR*!qp^+zy@k34NNL;g|*@ zr3}*sN6;9{=|=rhUbM_ubkC{zi5f-;U@st)7?0yb57y@>+i?Pn#c|@dVL#4MyRF<# zv5zx7urfBia}l5RCiq?+!+{(_+VIpDBYYMeT0u8)6BuVKMq4;ChIsfr!_+q;&bXk% zV$B`I850S=ZXV4URfkbo@P8P54AN}~Ys1*!!Fjf;K?pvB1aC4Rc+lUBTR5!l)gJ?R z2O$d*YrG($T7jv3r!DGB+AhOz4xc7v-?x}-vBu>F{ zY(D|?WwbG#f`j+qj1i?1D4*+P{3Cg*OUO%C%j-_YmD7aLn7`9A{(cs5lgMiMYt&C) zYklswOOorrBNOa*3g8~s_VIfkwg~W~#~6H`@1;U^?v2?NalSU8=*31ks<+8zeDpI% zR^qp-V|>EN;{hDAF+%cpJYOEj*uK-Ljw8=UHUgbP3(G^|#-R%9^HHi#MmQ=H8H5{8 zWE*E=h>ylXo?jXG_K_9y`stkmPbw#W>i(?VDSAMMmiqRmuUC_Vvq*IA1)>N3-T!Qh zFgs?W?Ks#L5-0oTg@u#jMCa|dA14z|jXWOiN4|B3(D0o;JGCU3IXs{jb_No&^3oa1 zi>Eu{SFqOzPY8q)-c;=^#^5#jQBvI!egDQukNW+(Q!L)rT(nw0^y28@^sj6Df(wjr z1s-UG+X6L^?G}=kaRx-d>Li>X71zG5f zpW(G2Hhd2KhoASjf73Ik#$5n+;FASGC`=9wXMDS7A0`+0d}Q5Ei+K^JI&bw}5wdy2 z@!%z2Uy?TNUw+iBU$V`f-1_Doe-96D`r}3a4xA^O^vsThcJ7W%Bqs+~?a!l4>cZjpA$I1-+!jsZ!)=S`?A_lzS$o9;^fp?-IX>Z%i~2u{ ze+@AlViCG`tz#^!-S|v!mf`iX=7keom0OyU6Tp^@)do@Ce24*c*EnsIPL9X^KE&f#WnA}(_}KgLdPtc z8Y}I$pm5=8(!hV4^g7A)Y5^b|;vqXKfepvW6IFXHa_aeGuyN$toXnk&SS@ZAH+u7W%;p3yNay!71@-rEUuiYQsuBd!E zZ1Q;IR6Y7U9#roT;mu;b z!9HW_-n08(H7P&xet3#+j^sc8=J|GvkIqF)ZF`UPen|*^-sI1s&<8ZISz@lqYJB@T zyA$CXhtGOk@A;R#zbE1?G(4@`xHHVO?D$7=!$+J+89xZo#x`mt=cI&|grheSR2VL4r>gr$IrhM5IPX4P$mg)L@ zdvzDj)vn9#dP^O_#0p{tbg)q%<)s7qhY0o4teNAQ?-B^?7<5s zx(L4`n`HFJrkF&Yqz?X#rs|Sq@^$MOorBL#GSmhSj(;!KNk65-C$;LUzY{aj&4b^u zO^06B+UGroYgJz2hbPmi#&Ud!Zr|femq)K!7~qex5z`_33h%wQVElp;{(0;m8En_z zqXFvR%i&Ej3h~;~d$g(R&Sun|e)hAU{p+s(-+dImN&g53L@GbV^iK)JAD6ke5b!9+ z_1-;kB;Ql61lQ!mq?E9FHrq-c0cB(be1zW-W-EmZBc*HUamHZ=pti>#RGy$G)ph9^ zvV{9GX1%a7CvcwYQ714}du52gol3~>>KL&UBp9AS=eQ}*QPL`e;0YFwVolMh)}?F= z*zl~L<2l-G(!EJleN=v+P(s)0grh*c$(Tiq1y!6&*Zd=c!C(pI*~=N;%J9ce$-wVS z?ge;pH+ZP=KkDwZz=Dy?7{ME@aJ4Ipb@#-Fq1j6G>TxoR8lwst`WVCl`|j>>8UDdD zxn@|D=?{(aj`2MhDT6P#n=E!eBBK@Uci+YUDTf{o;qdc@W$aBBJkK?G319S}i=#cy zNS%eG6L1+%>D73OqM{c6SO)ydR)|=>4ZX77*TF z_VF%-_14kL7a4bU-l>6i;>~y#JVW^?dD4wQ1J9a~uN=R9OmMvjNjW}E50l?B9v5xN zk>jPxBoilN2FE-26u53TDR|S3^xe3_1vdoAXE7~YLQHoYRuvB&b#SKa<1H_%f@E1Mq&j<)h{27<4OHlFwz;mm_*Yj zxNUs8hLedrnvDCSt#O{Qy^mblp;+)#JFkQpUJIPHXPb>ZnY!#@I31oI-XFRzS*o7} z!~&r(Iq6*C-J^K8XCfd>3zpe7c}E5>+f_v{$kCB+KH%_odU9>=aSR2cnZ#uXY@$; zU}8MmS{|5RiVJGThoNEP5^YXQ!U+st=QwiZ(WKo#maoqJu}FPn@Zhw1Xm6a)V_~Ph zw(OF$wtcG$=yocj`F_t2K0-jfufvs$fJg4d3*Klo{QeJn4*=cN4;weU>cXi&@gE*~eqU_qGn=gc`huS^^-ci#{7y&zcVe}E-tI9s z$;$+t3;>Ia@B!@cV$JB{kL-#&;xQPFV!rQH@`qvz&_Z@19hW<+)95#*1Z zLig$-+=s5A}v7ziB80y0R<_{dhVENiJB51gIVFBfv z;dW6)=P%CUSU*Skm5xvP=%bJRWmg&s z93tkkeQ}C0laM}#q9Dev2Ljkq7;u8qRwirFm03Y0&6nQx$hh*|RW0}o+{ygWM19x(R z!88T}i%?j?4~s*0Iv5`+6;lUh#d1TL69mnYY=-gJTYk2r{9Ml;{d~NKq1#mSs z5`N!uJTxo2CnuA+>U?9DsBy;^GQ;pp&TD{sX~uAb=Sy6xXR>O9;I@fbu<=7%yF=v0 zwEztyI#-qL3{6MMlk7sHLEpQtABo*K^-Q|(RW6zZIr=#|Kf8rrvoorkG1i6+SfE!n z8=;-?xNY5c(cp-w7c1%h1a0wM5N08@v<1%#$4$hmhf8;GZ~_98u!5y)Q4F4jcXq2W zk=XZ``jW^`~S-;L>Ttk2yW_wdz&Ngw`$p%2&6UUpgr9Jc5&9BLhH zX981M@P=dcCij&cT}w}s+ap`#3qMbNHtQa5zH0IsK8L^j16s*tkReq%0WcOHeI!Tv zsk=x}@A$pCkkOxuL&xB444PC4dX*rwf3rnbAM}g8mc|2mfG?GAT!;6ORd@iPjlO;R z=-Z;XZ}%tot*koguivhm24r@RuR13d_}SZ31 z@g8o~Cui(BvSAVxFY%r%mnRlbYE09IZ-%ZrYKNB8iBTmBR8 zj%_>R0MFS~eBHR1e1w;YeRbz!D|^ub^;bQ%#{YWH&=(#3|FUa!oWAj^Gau#G{G{Jc zdI!#jJv(nvb4Q=SYCOE)&*F#2`p6UdAf^xT2fHxYeN8^d0X>#p>iXp4oRYH;5Cy|a zotuZgH$Jt0H=pYi-)vg=(myerM<;&syWc(hUGEv%j0iBf(zmw8b3UZ}ErP0LAS<}X z=GdwuH@D1bf8>-;JMo+{a)Boj9RW*weIK7dp6J9ifnRKJ;#P8c$CgmW1MqG?$ac;5 z2All&tWP4}^;!5hZH~?#dpJG^kR8-F**|gq(erIy>8uj`K$mZ0z6Fok%qFd0I&kpS zVmypKxEs40HoC`Sees*42k(z=92k5p89MqFxakL&=qA5x-TjAFeFWsl8NSxO3K*~p zu`Rxc-)BpzPu@@7!4V8G#dP=@vymSlWAvg^E61LCFUAuK087d3QId1}`gO22`G?;R z@~@`Xk>f)#SkElm~OYtnCeBLFEMl-9BjaA}5a2ON<^D zbbs~i>z?a+^6>e!h(Q;;Eoe!1)^;(IvFYg!5SdeB1jb z!ursPujxoSmuYCTKYH-AdqIvme!3Hnwrdl;9z)q;Pipd3@q-<<=_vj57k}9#R@%#% z-b!8rGg$gKJc3tklQXvOdpbhq`o{PBmqGAt6Y$349`_g7C8rqPY60s^_yyNPzhPUhuRDBVsK@6i^6DVTqU-D zKfBsB$%n8&o*d2wl|QQ&PG|(efK zN2t=j`*@+x1?cg{%lAC9-eH>Q8k@y7o)Zt@J{uj}zVWd4 zFCBXr(7gqmKdsFs1@KK@-$@SHX{Qe-t@Vk8cu7`#lNS?edNAKr|1V!4h)O(we`CR) zPRXO|wzJ=UdnVQR<9Tkfz%TK^;LTQ}C40h$dA#je&-mN%7bDH?z_HI)wX2gR{k_~R zy%KA(U-#niiABzCFy7XrB!+6ixEmWxxBqQeUdj~}lg_dAMl5_J+34EIRQ@#^6Y_iS z6Mk8|c=3nN<^y&0Jo$MyU;I`wXpwk3nID%MV&BhzH?z&?GP zM=Ib-r{R-UXt4I;q~SeTG>*w>_?&0$Eqb2C;&l_;oou-oKHOmy@8gqi3IEXx{n=*r zqQ?e@L;c<3eQX)KlpHrMi=)DCk6|5^J+#GDI3g|X}Ve+{oi?X4(;fi4j8ojy3M?2og^&={7Y@CA6qAd zZkOUotU0*dc|X?gA9ChL#sz}#&mehqo__e@e@5seiZz|@dxA=W6Gnn6Q96zwUD@@H z4rg!~6N&6`APkfMl&%qa?I^Yi6p&%ihLaCc!h9K!+ST1F!;vbedj!3L>RL3Qa1>?} ziwsJ-SBYZAf!%1jBwt=h-)Pg3uVVxYiZ!^m)jt zpbLfrUj#RP$zgn}k$}m|4DI+Zx*Q%DjJ5#4_yCTt_y^_*+8U4Y@ehyvxA6}@M_

    |1J8QaNE?WFPkuy;nS%O*pP{I%QPDm};W-h2JtZuuOqWE_mOZsEEhq`WUa z@0q>lpZBKy=lkUXeEy#G{9e_l3)XkFM7G*Y)zBP^%-2Up&cH> zA0F%-%^5zG*L4dA0wO_ydI#>tzC!lkOePPVRiAuL4wBP^pf<;b?KU`DfY3IZbjn&q zq~>+5@4eu!>pl0^LWCZJH2V5FHo0#Ti8G--7U9@Mv}Y@tbZj940Rg8w)4T5AW-_|( zbZsP6?MzZzpejJu>fj+bWtX!N*^8Y)?R9E`&S8SQ#uH5R_k|G^@grtIr@z_l?v3#RJ?dGr6q0Z=L{eU#>L~v@oHm9*nZ8Dc#Q>9F?a0;M zw^W|Cvv;wNoEhiYL=4eokb#`9z}9y`WQKP?z+a$Os+wxCHD^I0BA zKr^3-_2lzuKGgz=69;4fO`rK$;o#6YFG>DgCm4PoZcjR`U<2u##1h}0I0OIg_=B{P zuko>K-*Ba`_xmsOK`{%9O2RJzNQzDbW z@)y5F<2e$a?l-3Jl-PUYYy8D!^vY1*<@cELr^cK-kt>TmN5|oLd?a3LDQ?}wHmvwv z^|YT22nM+r!gYiX#>@9DkFM#Pc4!jIj6U`AcmC#AtLJYqM0Cu4#=F^KH1)@ahSG4p z;}-aOJajt0zD_)|xkvnz@KK*|Qh&SqE9ox&p3@%jBiQ=gf|qf?JUEp%|I@wr1`_0S zG8s)^inq!bei1R6&kYE{IYGwhHF&Se6zN0z)jT-FV=_s0Kp>OnB-P~elQJRldV2ld z_3QVq*Za|2pV9v_BKOlzKP4u2`s3?csvfmMg!WEhefM^XD*qM;KM|qqoxYRlp1$wx zDSz>ofBC=uyTAL*|H3LMIVH>-gO4-^yh%_-j-*ankFrppO#=I-5Xx4kc7*me1aoW& z_=G&6o&l$zl(u_y86(Dk3A`uy5aKvy)KUt%R44m4C9FN=gc1X@{<;cGt(@BO_dKZO)D^7-u&&qh2>?F%+Y{ zYcSEV@n+OBxEQ(@B9(t|*6v*U9bA0En>iWd6J0ea!HSXPERvUaw0!+BW_{1`ovQU! zUTyKlF-m>i0o8u)t~P?EIhv~tm|ynNNP)J=HG{ifYRD0oG~+kC@DDEVgv9*^#~F9o zMS|cAGy+ST@OORZpUnM#CMBu|Q$I*@tqWRqw^e)X^>M+qyJ+lBJEv26_Rw?x@!wmL z?e3%P>(RyG<$U8K;5*J%;A2cTe(Rtv;R$Ac`rm}4%QGo}1U&}IC``>Jg6XbcYq0e4k59aq46I;R8%f#>ddE@uw!xT3`3r%RhEc09gV*ivU4#V z(xlE8JdW=tNIbZ{qLsYV;R50z81mZruq{p`*aai^OI6n?9?_X2SHX)O_F?o@#o~fG z^A*t{?bLyG!9MlsgAKq>Rp2qer~MwEkv*Gh0Zi~{dU))TF~LXo#zT8w(sbM8zYIP} zpOv0^(O?3A?}AokcOpaQ2=|e@@vpqq8LQ5u$f=XbT>b<6jVE5>8yO8C%tjBLBP&Fp z@-z0QNBU_8918RhFQhZZNV;Y3EQI0bCOQUEoBH0w8f}BaRz=hPP9Ke^@&3FjszR`y+lR9|aK9;SydEgrc_`_fMDO zku5O0d#42r9qALVCmYeGKd_87mKuZof>hV;_kVdu zpN?G6Y2&2hd$F=AlYfoiZ@eb2)zgQuS{!oAH<-!qfpPR#ePd#b@6#@8leFN2z9xV5 z0XJ}`AJI2BzT#6IuWx#|#kRiD4UOSaYw|H2ho7z3QZemB4)M14B+IIpt zWndhn33>k2oig{g5|=Tfu;Z>lnDNji*e1*`6{$S%*Ovggzdf(T*mMmqif%RE(ILiA zUpl3@GZXY1w^YG84yRo)NVm!gBuF;;+y5QjR;Ys7cSc7L* z?c$qp?w@MIpS(Qj&ACrHmS^G*2k^ZZ501ughP!&`e%IT-`?H(isP<=$_l2k6?DI|t ze9_T*e4Rrb)%|xRJ0~Xo_vC*&GVX`XsU+Ax5`4GvT~7J`P&%*#uJK}Z4*azOtB*4B zVk|6>Skc}tN6zZQ51zKO^1;K$AAR)j$;Th}Q$!zb{J(FK+2hsKJtsoEBjEXVX?mVA z`s~i&A>1~m#>}|KFZzmC)7$t$mSMkfOs|zm_xFPC+6#Up@-R6Fz?y4q%#2Nc`ZI>I z$sp((HyH=?aI5CzWjJlzkLdwplLBqwlE)=0@L$l=w*&|9M#}|wFZU^+0A)a$zZ0lV zPDis71J#SioGsa~2mtni1*p14!~W|&dQT9-R>aF_vJ0==2^wjRI~>^*j9Bn8%Cx@- zEg<8_*@gvJ>#OSF75B!MC>d^B)XPRxefVOkw!R&WN00m26S7l@|F)=Z8F`~n(sTEu{}DxV~`gOf^~WiHaWmcGQxJ57%XrN$i^2RYKH`;6I=<u>C@0VCIo`r^y1o1(H^aAzba(s>#`LOh=$$uu^G_{Cpn;qY-{dGb__u!RXQywF zfIkVO@mTPTCw=DM8qc#RduY(sq-=3UeA;e%dHTX-yz3^Y!EMagBAB7^#1z%5T1~dd zfq8JmAB&`iiZx$eh-~+td{h|S2Y;u=v@yBU9eA^!HNE=+Wq4H&kH{ptHnMATeD`2i zZqWsO?(sr@c!OvB^{wOJs%HW~hE%jDg8nTqg!6E%jRg%j@AOS@;mH4jg-eU-zHoGSAd*=su*@P$Usz2#*yZ7_UANKvM ziMPrB+uAm%W^4Y?FZKNHx4)xooenyuOxRs?OSit@<$f(n|KYB!@pqyEK9Dw+;z*NQ zJNX_9lGq#ZoynDx31FdTJH%_bpG12pGoQDh@p+pBU$m+9S#Ul}Pn;|;w!_O~SosJ2 zIo^$5M~;WZ;az#tJ9J#KN`DN_#;tOA3`V(N*PyYfM*nF`R;qT!pth6e#%c6ckG*4u zz@5FXE&#@jtoZ|BW7z$9bnS{g^HZ|}!JBV2*7eEO`vZs0II(qTzGO2vXqB{wA9=C} zQmff2eq*+y_LFBgRH!bv((yA%?Im~W8PC~reDN25&w_(y!4CjhwV_Y9Eg!FdUHRp+ z#=kQj=(o@`UX%|}8*#>kuCcwwVjE9rli%N2xNudf-qBzEBtOx3$C=7EX0-(e|MwS; za9tnOhd+JWqI|S!Gk;zI9JOWd=rT0)qu-Yso(GNbbPHfAtxompi(CvB^)S3U{E}~j zR;tgZySVzuo=zw9$&-BpfH+2pCPF~#$&Ln&NeVyN2hY!-{eH#6MRYpt_05$KpzLa zj>_K&fZ!{qf8Yq6w40-k#?hL?#0N$nejKzmb{81N8I9;*{1^zv#>8oh5%uF#0w<>} zV}(Bl?~GVmSK4V4riV}A0siW4e9o%5AnP1Qtlc4c^^Dm!If(6C)Ycn!??nbgF@*T$ zI3Zj{CuhhgnuLVcaSHLaJ_HANFhgBE(lOk+-dHuB4C8iPgUxuZkJ`+rh5C-d>YHq# zw{Hd*KfRN{7;b#VH{)|?#wWDz8R3G1?+OY$J}{)~=S4pcx1GxH^o+g9&yFvC?d`r# z)<+qk^_)(dD<8=|zv6Em$M3 zcy^BCr*-|h1_|1WD&y%KddkX4k}vwRiD1`~@mdhmwKC7DPae|XDfz~fTpd|BlfcF) z4h&xWZ=ecLbG#JMzZNjakx5;^c1;fNy@1EB zIM6RV^a_y9Pfaf^5?DAmrv@yRfmL~*8{cJ=J^aF93k-N34)EQJ3d5B>oX)A!J)UP( zXFtKChx(CN=B&+;%^}-ZH?ER?!L>L<=SUfF$0o<4cnQnBoDh`klf@GDj2(XayV_hh zuw!M%w*#APH+IK1p~?8fyLf09@J!qxVDehSY!LbmJ<*zN{ci7HV8e%LHo9HC#@nP| zvg92S?8>EgY=SYh*n|GTstbBGJ6k*Nn%VQWvtQLIkIs@BKl?Sh^gWw%_%lBh-tuO* zvU%Gbt}ly~u(9Z3A})<5WURm0iWWLHe&ILSjW?39*$#I&WSqlSy|~mM99cXIvyNNe z6F{6$KD$sGaP#$8+Q7#Z$elQ(=c5w3Zu|N3Wg7)sNt^X?&b_3*-|_-En47^|z1 zpYHPQ8GCgDJwAa4ALE0+8-oqA0%H<>)rHfUkOwQ+;+J()**zgD94_j^XEG84M0F-h^Sk9oK;?a6=%h%U z^1g0D!KXM`!?xf({;{ck67NZqc<0dRd?>u?>@P2hP-lA?WEN(oy?Csm_g^6w1G_e$V$KQ>A@)>i% zXWLj)bU7tq97qG)*dh`3jhk<=9XoKIBj=k;rs<2v0P!mtsZMo_726LUU8Zl}<-gIZ zd{PHLkNs2^4$3B*T^QHn3y=O8&n@sq3pqOWG#Lud+05!ME(tb1vTgIz;dc6SfBX=A z)erx9`aNUo)TOpQ$`;S#Egg{W4+)oj0S~4D9-J*^pfld9L#Gi+4$*tYpsdEw`+fM4 zSh#-1$Kqr>9$xVbUp*EDSIp6^cjD|?yJ)u?W$<24M`ETjF9%)Tqb5D-HXr-m zPyD&Y3*R0AUi$(mk$u)K{1}b<=cwv*Fu>E-CWz5W`S9f2a}cu_Czu+qs#6=2 zSgFhCeq{(3sAUwk$_Nj2`tQ!iQ%cSt`JS;EO~>Kf?c~pq)D}+qxoK4fc{~_>5Mel~ zv;eYOJYU}$mlhmmI}WU4g5=}PjLyb9n!;rAwQJ3KL7<5ySiwD;8~H*_>H#U zR>8C6#wGM7KY}N?3~znwXGld$GDAkl&2f4;fT(b9vVb>?<;{N>_?b%m;qT-job6I@ zC`H03SRWkC98uZiKsq#RM0P(s&eg%G>*J`ZgMe|=7oLr~r(V31xAyph9_hYTtWWuh z%iptb;f>$qhtqJ3TX6lj38u*jXTJWcMl!u*i9FtmI%h2CU9jO4?DMP}+|oDiSty@_ z#8*2%_>ZUX=cMfRO8P-Y5?>$2^6csygw>!6Me%V@5+Lu-K6m5QDw&G$g^wh&&SxGz_b3Fn0#}+v=3H$93;H?MKWV2g#6yNX8}a) z5Gd$-bjQQVO5eA9-|{11qp*)#Y#Gf3uO>&}?41nZWvHD-*yP$+8pFzWnxH~Fr-!$L zUr-i)7A@emXZMq1@DCr3O)+Zd_F_U(X)Kb~!51JdNQ&>i$IlvQk7sx7Q@d_>EVWZ4{hk zhm51du5R(9wiX2C2h-E`(6P9=C zvANwI9t_uWkLHa_7l&Rxifv3sH-0cI4-Vk=4L^18NZH{l8%$@9pQ`BazrNsdCOVB% z{kdoR7h`pOM)u-&yq9mxx3J&0z90{V=cT{w-6$qsPNMC!pt0*u?DM#o?nx8opLG)L zX+GoKI($t2(>uSwgRbdO_&x8W9ox1A2+WOM{^*u>{3vty-af=cUjkzQV@{i8r=AjE*$r$qO z$4@Iea*r2?B%|yBTMnniGeJ@Bq(lE5i@@Q=T{&JJzTx2+%k^hW<0FK?9FK>cyyMp< z+u#KU9b|8MTVDS*AK?pJpSgzdyWnMS2fTX;FK@msUPVivEy6}9JBB7azG)r5Yx~Nk z=!=~`;}RCNIrQ;a%JhlXcf5M~KCu5@UT_e1`*(6qXYlKT_dj^}uZAO6Gt zrAKbQ>~cr{;{mF^4Zrr11TGKXH`Y7;U!FSO_Xxo9mA&@qa_p41^w)nTj{qD`a#Qsu z{{88vpOW6!(|zyTXWIMm#~+*6FR$-=0YI5+JlSchLv_`YCYLFJpNx?=L)irwlh)jl|l8P&Zf4(BJU?6{slO#L~OK|ge%RdP&k26INY z9Sjr7!9B(ce(E0FYj9myAzz@EaX$gVampt+R_*FPd?heA2DluUM|{E6@sg+P zz&Qp2uho&Z>K@e!WI+_XL85zOIv+8zgR@EK6; zYCrj@Z+t$1%@4;IJowoq(i!^gMljqru6PFy+;)UF$8^SvQ@G)&o}3JpMFBg%t*)2% z)^>>dDa7qg3LK5=A^z;nckMRkFgmX5i&fQC%;D%O!uGEq;I#^;OihJ-R^}Vr`qKqm zuX~FPlK)prd7pdyHreUkxY>oVtK{()tJgd7=(hvauTfew*(JZ|KEHh}@HjjZ1TJW( z-kecwP{ZNH{qb!1@TqJ8efp(u>0n)FT1CSxf9k?lKzNUXF;?HI_+3vhaR>Fv^#!)} zY{L3DtQr&agoT4OUY_80ixqIH{h16Nx);1B&u|O7^~?K3MRRM(2XSqf6>4l7A*q+D6Y646RXcZ#jd*@Dh9+v%KJ2Kww`x}D3np7O%qwI2@Y*86Ex#sH{Esx5s++O(!lLcb}l=cV42F9bfySdv|~8-F6Lsx(&f~cTKzxukf1v zIqZT9-LC#s8DGJI!x>MoE}pCmoW`xcBY(#xjy~hLze5+g;bLJGEE9{N6+C#r0qmFK zTHbwca;CC&Y?Lt>-eul$bMQQqGjPvohxk&TP9U&@XY5Ua*u5tEHx7*zf9R#Cd#PVM zu~<$=>vcNz^x6AOFrGbp&gKI)6<~y~>&t$Gba&vV5=!F`1qWzIU_6=|8;Nh`+{&R9zUH4#Mxbd6a z#{5!6gLhC}z55|Q(r_qWwb#dZY?ar=@dGE88DDQ6Ru10yb;;xFyual^9sSW|dF1m~ z|6)G?@aXry|NZ~%Z~yjhe;uA3h<&kV{X^@1=HH;y{5y*P%d7oAwE%#m!{9F7EC67~ ze`4v=Pd_Dduc!Oo_x)*y{;6I5y#P>t#+Vh`r+m`;R)%0N0leuggc73UH z`jaQf8&Ty^9!dxYioAmE2B#Z}Mp+p3JIxvA4CE;z-v@`|aMTkFQm}K<;0^+OA3W?@ zply@taAt!xY0QvQdiA8c{*Avp0l7Z0;SRv?NMbbMbQ_*sujHaBL|@iLpR)3d4JUH* zk3nUaeV_32ptmlJ@T-F5xHl&8ueK<^nrb3 zF}}5Eb!=Za!3&?Fe}%w-G(cEN!Es$EpWVV0RIoq4&JI+ zXgh&$xG(5V@7c5Jnh@v@yx|$VByhM0u;tm^+q2~Bva$;&_%0Bu|FQqVNnh;977&b0 z*IT5ioUH*DJ%f)HeD4qVQ0kLh^hX}-aH7XJTbz*2sR91!*rg+jAV8zBw15$?$vXJM zS2;MfxoR47>5Rp}39jtc>>_+E&Rx6Eqg(y(nLJl?g5ZJKf3ttJyZd&+WcDu{wpbC8 zulK9b>;vNQukuZd@V380+i7$7eHMQv!xuk{xwh~+DGIyvfyeC^Z#h2n9)64!t9xh~ z3f=P`+TIK1cAepEAzJbVYjvi7<%4-2orjOHGuDSJZI8Z!e|W@}2QL^V%r~9dxQ4TO zgOO~*<=|if)!47iIm*r+fV&fd;YOO^g?|`cJkDR^Xddo+9GNN#^bJXyyBi@Ic-tPlLl`mnm+9b^IGjG-m2BiD+Dm5OMP<*)0px zqZ_}%$t2#-@1a4y$-h37jd#H~i&Si)n59oTiE=zR@^*BaESl6iaf;^>9K}%U2%vAPmgF{mk~->2`aiap{v91Wcpca$Ze*wL81{g>Z6o=)b@cdd z`b!VAMUB5(s*>yar1Ecdf{`&5$Y*l3MD+i zHaKhn7%pXyCwSa^*?0BM7##lr53uB+8EJ=&hdwY*dpP!4T|FOJgOls|Jvv_F@f^$jp8U3l1)hFY6M zisb^@B{v5NPfI!quy}z-Fhk$&o@oCJ;EocfSa#}W;9XUu+BdklKU**C}0n#P-mx3E!? zuy4aR$+6(EK_xf|ok056v5aTf+U*GDw~~HawUTHpr+e`mJg}ZEy0#Iv?P*K)=LJuf(7GkovaFWHF3O-5@Us@V#A*BSl*RoGv(qpPFc zmd_8wy4uX1$UcAAacyjP=2LgINm(92EvRnR@wY_$0!>)IQ8~yB| zZG7Kh03=91@V;kQfyV3T?tgUR`=O7_@$Wf!GFDGdmsG$|TggiLH0hpCCx`V1hfdMM ze%dCR#5Gx(EI;yVzu)l+Rx$~$z2td(_7B%4KFX^ZJBW@Ep#=Cz0_%QX z95|lEt1UJdSAsx>=oUMSfibx<3rx=@{_~spRq()|li60W^-Qq}D8}DSs-|19H(lXE zJob08q$f5+Z}8SY{|TRMfE6C`b7iPOsde0h zzWV1&GS6g#DEJ##(_njLdsOE|;}lXG;I*4fB7*0Ic4N|f%E<`GCekN3HuOA;2lzGa z?hX7KvrZH>-}b~&G~s)(Ft{5RD}v7E#)zLT8)uI{4)4(}_WYh$5vx7NF>z~V3x z#W#0BdBeW<2o;3Go8hwyA&{ccax3b z;*2|ezV011y^AMUbYyHEf5D+w3()%fU_0M#QV+hi-XmdLfOB^)#s|LLp-KnpyxA?dodS(;45GJ4uaythE56e6 zQ@=bSUHF+lzW5l8{JuK0Y+LZVu4gNBaI!mjqBZK!TOTq+Mxq*B(@W+0*zCxQET0^2 zy47C;_#MCYiqNYPujsX%KVMuLA8e+6#_cDT=&`bN`{19Afb`;1J-f8`W9#sAq|V9P zO*{2;UTlvqa1{Rc|Mb6Is}{ok<-ht@|LXq}yijccK=~E|ly$r>0VJ1(P`Kay?r(Vn zK;M@l{_`CGgz=x;_St8j{Z$M3fkNeP|9*D?pa5lbm-V_cfF&2}-rMH@JAbc1;J^If zqmTa&-2w21u`@P~okcFe;g^h)!!c@x`K(CJuyT08gTqK#?oID!&~tFa)rkRb_3WW^ zPUeKWog+9ygJ5@)0VTEI*uj3b(N`6xhZ6+t1!HvDToqBT@9s2+M#pG*=-Jz6b1)7$ z_|?0b%uyErv<+6p8%*^(QNy6@MHdF1KZo)9By%7bh+S0|$iZ0RQoAPrj-FT0AHwRE zI0VNX7JGgW+|WN)R3^1xp&J~O`G-gNCC|^GqDs3Z)96XY&uZlojM`6kl^ZZ^g78Db z&ZA!CQ`VQKQPx=D;(I>t?^n7MA zaTVs1_%_LDC7^Z|dMpBX2K1iOV>j@ySHAP`%>L>4Hu^cgh0XJz$UqN04+4gstz(0$ zO+WVH?-}GCJgqRUKc6Q5#Sk*-$M5?4H(`SLI*3PlH2}Jm^htoo&9k2D8+Ly0>eg(b zW9@*Tvkj_n^f%j4TIFP4p=8*V`{_V=KPGG^CrNnmqaWMjS9|BlO7xG$S6qP?qO}Xw z4}*85Y$duSmFkby66IF{z1UfAJTcn|;|`XPv6<_Kx3rTE#SmpSb~J0#NByfDoQH36 zUmJD(>Q8nM>h|aZZFGK#)00SFB0?T}zBHdg!^VW*)Oc{bFF=qY3$_C{c(Wh7e$Ga& z+|@k9@aFemeM zc*&u1a(SzZ&MkoPBmN`)Tj({3bmsutT{-`>7x{hK+x%Ve|F|*alX$}cn#8i7|HaSe zOBT-21D>&9&*cYWSKz$|ik$P?h^RLS+~$R~as+asUoyepJ`q?SvUIMmy{_H?f`I3p zPv^^uqKiDjVY2_VJN-KU<*$D|cs}j-RlI$d?SB$q%PFF9SNy}R@6mAM6FX#U(~;O` z%dWtm+@p`5u(jJxC;WtcZ9<8re$c*0FDhpP{!T|1Gf89%kK_|Lw6OtxDJGz;-|c5N zojme@YyKCWd&icx#+&-SXe4vIu{AOr?AmPnBG+Jy&TM418a=aBu>=pbMI-%z>l}Z} z;?U&kM(%Pgp(H; z{15;1pAJU3!~gs*|K-2@pMtl~{Y9eyWC1`uMLeGZ%#Gj81bT1OmtS6xinQweO?2&T zM`Qy^efHVQ{8s+E$Ikcn;{|{Q1QGb{Uq)at;N60F zvEUW~{;Qw;?7#1p;s4#QKmW}F1OxOZSS+CC6qksXJoLl3O=JYAt%_#E5HAsFzwKqz zNMev{P=zFj^D%lsX*N6Odz|xZQXh`xPJ;!n5D2b<6EiiyfO)rj{s0plTw1`k&GEve z6I$=6vw$1i?#@2boIM`U;9=X!*Xv5V= zJvig3b9&uCjZa|XaRVNCxWYkR!JI8s4=&r-0+aLL&t7_Qnn}(*+5l1$|6N76w+nkC z^;i3(5bA>YdRY*fHUJ-Ss9fLMpPUtJAe;;+?*V6+;D5J{|4#mKJ1xQqCBYwYi;X2^yf!sFBg+G_=|J#aIr!1lV9_v z0L^FO6HhaH@|HY?`*=SZI^nd)!RCBvtX4t}e9Dz{emz?Z_8tWx_xi%Wm^(iX2)_9X zxdxAnFCJeKWTEKpfZ^AYN`76^yE|Hwc5&sNUI0vvs4Bi|va1daH+ zcQ|O9JS3~ybgZwJbMDiV1iZJ~)8y&%!#?9_V$1i~=iq5pqJ$TIEAt{NHn>G5nxdm( zn7Avf_Vo zV1pAJI3Wh6p6$`zIA}6eAv?2Sr6$KydofHs8}r^9^lq}ut8ynE) z*}&%q8%yfHn7;|g$>@YpIa>+|9LAybV`FK-IEa9U|GgM>K5^|gUcK!TW_Y53ZpfUy z>I=puBbAv1*ziS5WQGy_p4fp_-OiRT2Hr)Gcp&QCu5c%Sk60G?>`30*nF=UwstZJ*BFV@mOf z{>QHOZm7yk(Afu;T$$f{b>hW_H+r6J)^-aq$pZg$V3B&cjo2_g&K@>~6~}DTZ?Ki& z9U{Bk#ZW9D2amV?Dq1Z9e^uOjzr|sXF-(`W_wrXl+69B)h%sPI=GCh_ot%6v0I7p# z!riWR{VJNi>3J~HZ9IZ9czijSz(!7ej^@4m?gfyJ$#LX0yjjG{11b<;9)L12ozO&%q@#K6{V8ynF)Bu)Nwl zzCGKij&GpJHXR|h4J`N(#vT@n19Rg$s!!%o_`Zai>=_p__T`5~%ba-F=B;nQdKJ6vzx`6vI)KiUFmWxx3IKmYUpXM0!p zLpKZDO8^6|>|5nvCx^WZ@UK|_K*$mH!~XYX0pOwv7XO}Y8N&}F<=@P^Ht+U^>womC zo}=PGj8A;vI0lzJk0{`A>}*+3aSOEx21om@XTg#yEHv>)1I70_msMbRx%Uh|82i?|0MMXztGhR8H%QqeqwN0Mq7}#GIFi?*VV`)3 z3=SMW{`?b&JqPV=;S!iP4QA-}wH5w(30LsGas^6&yFh6#Anx0rzGu_%dksFE`&LW~ z=moOD4j#F|wZIll_!|BZQeYQE$%{+`rkqs3@UsBywL-2 zt4q7l9LUMRZPTn`Bna~IIU6HDN zu)g)&z66yGl3|74e(RY-GBx?E5u{^)`+Je4#e}mP^2UZh{_Bzm$$_iOpSSq+?KdUU zVWC&IA>!A6?!jo`Y0KCw}rtK zb>AfK57U9<``-9Wt|fS*=WKFAg+YJ=H3C27&y14b`iyMmFe zJ%c#gz!#kKYvVt&mCjvt;yb7N{65?GE+I;&v6OPX zKbdn!`%Ro-JvvNQm#naLbiLw<7HGof;)&qUwn=9=B@?x{^Zd#&)f5$?t%H8a5{sKG zW`Bt$0Agy~udR6a=(dJ6`JmfZ+2)eP*-CQVR}SJg9kNX{Yymc&^}i$}`6MeA_$I-y z37HT{J^(Z3&{84iUkxpj~5&L@6)2$xVzfOcJ{$m zlJOP_(#5Xo^Ofv;?>&fy^MznUnAr4glM=j{w9wi2bQ_(@Hhv_Sjbq7-DSTu~etTS_y@|=UT@ZUdi+8NXam_q8K3byPgmy5e zqX5?@7O%!9KJo=|Y=eNBq#b(4oEcg55g_I`_^AM~vMryuv;80XQ) zpS~}q=u3Xy2LPXDmmf5#zq<~!r*%5+SHS7&3-2)SjDNg;o(?|w_$TjtT03#u z_yWyM0tJ%%Ga0`Z!(V^d_!GXW=RC~NvHZeAK!>u-r)o%H8kS*E{e>N&-ozF*O7FKzG5w%zsU*ZM)Y>3T~+ZM)aryrUZ zU-Tad#j~JTpzuk@rhUT&Kl&e^RH)y{=#Ir}cZI$ep7~8OiSYFSWfzww29!oqZN>M| z9KUFlOYh3{5i{A}Bo`mY51Eac=%wGiv!pmi_5;s8cGUmzZ(L{pa=MEzs+aF17jc@c z9B)yUT<>##$iMNX%6y`}^bp>})p-Z;!Jm9tX+Ds?DOh{~HzdjXat!@9mL(T@i}~3p zymjV<R{N=CyM{iIyhzJ~>(Q?Ir6D=@gns>`fgI>;Y ziQqZKoMDzLxEmn)oD)lJdz<_@4lYBsY0kMgBKTI7JDM|WWpP2ER_pR|%vYHN+e)-6 zonttsL-Piz1~-GI!0;rY3jhY9@xnP906Fjro=NW|CssCjY=KVVJ1T`b)Y{(co4DFK zzKK~JB#$|AWg9GB1H!JjSB5sY*M5$C&MYDc=w9d+DYcP&7vT4`1bmQ3pL4v7-sF6I(cS5QG$gvadm;L@ z^CtHVEcG*Btba1Z<$`&A1ZYI>N>MyNL2gOWC}tWdJ9==KhLTI)1=p4g>Te=P7hpQt zExN(P^9A-}bwRi`1`qn&0+l|&w~}lU_H~nB1B}GXt%VYVufMLm1uZ-w)<9zKj2-X5 zzuD387O-oB-*CYF@VchenmQ>bXA^jXsLjL-O>BUUI_z9c2ka233hHB!Sz=Jl(|tT1@DKhask{BWX zmN3qS$m*_6qlNx0A`L)5&_sTC#U7q^TVv((k~s@){AVv*AoL~E$TZ}P*CvE;iZ^nBX{Ts?YXH!!D#l^FgvXco4aeZuNxXXG=sX?Rfq!`R{Iq&QGs=>iSL|^CtbY^ZYS-AXECg{kAI2tesAolPxOJ;@5-4So8Wu1v-Gl9INQ*-zqZke zhJK7ycQul&=Wp@wOiR5 z4LNY~>7&7X;&P@fHm-kwkls%J`3m?U;7`gdlFZK10sGU&=AwZyY@cbaTrT37f4509 zVLtz1lXB1ZcPD69`tO!SNp&0@1x!UFq;kj@}A(LZ5J`dPqI*F z0@P+)_Bp`OIeIQWxw}lAzUbv!31fZ8-#Gio*BuHT1uzNo27l7P``r=LB&UhKeAKvX zq2M>4f8N&vzIf-$V16APpZ3K9{Ee<)qeV>PTeIiv<9s5UZ=(5D;NkhOI6`0Oq%afU z(b}oS4f*3NFyQF5kq8^S9O{fxe1>;%qW$uy*J)iH++X7?{MgWIziw4<|b9npR-obe9oG-9b@ab%0dbYtH zkzDV*J^j{X3t1_Su_1`#pFJ z;3C#B_n-e~SC0^KwztPw+<$FQ$yPIq3-(rZ4JI5xK(lgh)tw_r1it)j&)jm5Itd~S zQ{4h+j?7gUwI9aI+Bla)Q9@Di4F=V5RD;JQdT0;AbXy-pPi3J3B);=fO3Phf%&x>8Q)K`^QMtf;w})KGfTuT5E;Z)&QZY` ze+}3T{?7oZ?N<54x7w();ZLHs>6BnlnB}4*uXM$fplT~tjuk-cQDx9v)J2#jKO5_8=zTF)k z@DJl;QAKAaoCYu}n(rETt2_L`sj>ET46dC$LUe`T^v=Ev1{RVgYol7U&|3f-6xkft zZeKr>2LsHzZ~8>udv{f1G;6WEDcYMcNMdz+=fAX;x z6E%6R{F{=jkr>SB<2Vl{{(|i%SbyDSLPh5iSDWJ-z<%U{B`iVy!FGuy9oy*Qz3|vv z>5x#`ECleI`A_xTE3#yV{k_|jS@!l(36vKGew{qO`sSl|zWl^XlfG=y)fJ%Z=4;3;E8wEZ-x4~eT;0Jxy5COF;Hl3OKRcYA@kfiI*ul#Kdr--?etijf zZQs<^!~niD^RipdG{?X0YGV1l_p{r4$;ss6s#dg$We@Lo^7nl{XuRQ)8V}R;bUVa zhBt0n3<9kTFg!^BvMY&qaY*w2TFhwN!}}yvJ2D*n%1nfGeflJ^^p~H}HNLC6$$mD` z$t^J70^j^j* zli#{S3+!#l$ej%Oj0O1coP0M1b|*m`MpydDo<>$3?Z3(<*$Y|!c79k{ljFJ`{u-hE zqYppXqZRHTGIoAiY$TVTHhy`TAHF~DQ8)VB!ez8?u_%Hp_~H$HUp3jjE1ivtT>!$H zT+cZ8Rr@D{G;u$hkn33dt#fTXD$rD9`5FJQAQ!tEbN2$o@Ur!v1n<`^MCc}W5m!5` zojrYK!~Ol+fA{aw-M8hQ*-48FTil2@V;}zRafFLmi`^lcK9U(e@k^3yatn;6f;n1WV9VoDXx5e9JuZ^(pxH)WXgL{^%`4_ab6s9^(o^_ekBW(BH>R+Q;IR2< zmk-<>XU0Ie*2Nn%$|tnt_oFc#$b-ZH=UMW68^11eY@QIF8>^32dfDP#r}IxbA+zEd zJ6IkU4RN7(u}FNd0Og!ddBkQm9bPiK#UU|tY$OLXXIGd9DIEB9#|<^Iv3K1)JZnjZ zlpO6_D9o`Y4s-z?oaOix@&}tS7Y<*>uqF3TckudWPA9jmUuI*>`(aBqh1b_D%|8_X z-XGEE-}i0Sm|>n({zw`b1q&1(w3*yf+KoVwg(qOc!lLhNn1lwLP0FzU3b^)8y>~@+^ zpu;(r*=<9DdhGZugOee;-P(s=^qb6^ zAl7a5n;eJ6gl+?5eXq?06R;ni^;vQyVD~3sa^(jwd<%Y)Qw0_Q;CEHkEqO~bYx7md zXmj-ryo&aJ6RL1I7g%u`l3*B^Pj1?n2Oo}00yki^@OulgPYZRYfM@IXjNuk( zhy!hBhwwfD+VS7E&9^AF%@uN!}Chq_2(L{uqw-TUe<4+jsjb zQF^Z|*cDP&kLjHKd0`+qf7-2MKl!@bHnS~PUig_SEt_bCX9;+8z0cYmT+4en0GZAxDVf9>~)MzVK3}{-nLrHMTJJe#_PE z0#T!3ROJ+iCRnbWkOz__j$i{i-Ex&@)L_*&ZkaYjXwKgW$duVC%ZeVK76V=aRy2K% zw91rz!Y>*}5+VZwWfBTBW^LkgbbC58t)3VY=cO4eZ=p+p-LdtDFaHXZ)Fmh^&$MHf zlyX9^?>c!ZsNHMC)HOIn>?@-iFU#&GHmdpQs zWWw?W72LgSy5}bkm>zy;?fyhVupkmijnlr+oVC1t-1!$c_0Jy?tCM0WxID^y{eYPe z6oR=wsW|>Vged%poM3`|<3XYo0rS0>kP@!^vn?g?+s}L~2rwq2vaA{?VelX+s;f9k zf((ClJpW1}!7}3q`Y~GUDERiTy{wA=9Wb_HgfXi^%1-IKMQ)&>Pvb*ClwQ9h~2z;%7_&tU6`xocwJDQTJ0Cv#)%WwlkC1 zm=U(y4J6jR>5|X_TTWUfBV!IqW*kc~2zKbBX=l{RX_^fbvJbM*s*ZM3 z@mLHyCw-NNDZd_w1Wf-cA6ft9wk*XTt2>si(OJPS40DpeX+PFkBM7jSFJuZ$4z2n) zz!4+{)tuEy))N=9r3KW-`@Xlwt7eqlGh|DMi5wTaw|s%J~Jx;Whd1fF?NLe{fBv?2{ibh z%{JSUdbI}Hw?E3U$Krw%+l*jU8DM_1rAU9%K?j?d$z=iIqM_bTrPdq_?_g;@gRe92 zHwCl0%b>Nyw6dRNg16(Cy>1a~Lc(qr7WM35<>a3N^z$%K2H8}B;d|D&A;R8njiw#6 zcI{-Fd`*C|>vfb5JI()gyu71<1Md_du}AHExK+COR%ED(ICBt!rgu$nXm0x$=0nAs zQC6svd^D6$+D{1?3AKApf_Y_RLsOOGndQyQSIp1z)kU9JRKwvYX1ZN@)G)y;pcFDJ%GL!9G8+DPOq0-?j3^(0@R#5Zi3P zzDh7(Y7Iz89|x-1=^HH)5`j$|ig8k&qNlh?jI{?(rGNN#wRI-n5WO|M0Sg?yl0Ned z=?rNT8}gsIc6x8}msaVM#(lf{uG|5h8pwK4sv=GID$~@08Spc|BdZ*5rK`PHSttjG zT1g*HYKO4-X;8^|VqEUJv`C$v2zcn*Jt`U}1I=cRAxz87LGp2M`t_*yiVmM6kB8`z z_-h;#Df!B>!w08%jg4EO=R)V=_W@sQ!~67sOx8k)&J};)Tx377wqqHq2~R$%s56oNw=ZWMvD) z4(Ryd^1a(%FVSbBVxx8z1wW}w?{opD$AAA4RIk>t2ws>&xsL393mM(x$!nXdYyO^T z#CI^%v)F-nngKC&-`PTOK;iapDO;S;26$lloapKs9BZf4(h2p4YPU ziHh2&t)%#SuJiilPKd+}i)?E>m8WXc;djMdSaCF66I16ki%2u@^egia7ccTHVxlj3 zaVKggb;K8SHQ4@U6aSolyJP!#uVe6-H1NVL=}IysZhx|3Rg%-~hG`n^u(Es)WE*~%}V>0oDJ zxyAowagwXa@0HL($MX97ElfOHH-O$X2plXi7+!Gk6w%X@70vlG(U->kEsM4!7M!V4 zlc5CztYgP+1jfz!QA71fylxgrX758Y=LXcDeei+6~gajKOVaUg)Wm3dY4Xxg%%zD6<>az_q=;>e<*&G&As!Tne zr_4|-_zIyji++obi)prvX#R`2-|N>h|SboWltHZAt5~<8wk$ar((zs%up$0X15G45?eU@nY zsvq%Uv-X9o7k_vqMVsSJozjgDRFygBk~}Atx4^XGagI6G()%o&PRv7P_i1@{{9hf5 z+6~d1s^p+Jw`&Zrtm1XZ@=Gh|3Omgn&QcTikT3whjSQjN02iuy-C9K?kik;$`b4>X zwb3u7I;I|b2LE^M;S{>wQ)ZW>Ikgt{FknWZvhz&RB8D*ScB2Tp`^$eLCFGOKwPU5w zb8eAG((hLIiV_LY;G~Wn4Z1skK=1ReX3cvIp-?)sf<&% zhxIQKtSoR0WntAW{Ssunki>Z!W21Ik!aT(fZbyS5@5(G9UKbt#H{wk1PrhRE47Q#3 znh`diq4vp52{cwGMEpWf#GuG4#fd^gOq1vho-Cc94G>7H42xc?MVo4En_%tuujg{A zl@B*WJ9=y5`XbAq{=A@=tiDAHfV;IqD2|_grCwCTEZL+7h(uowkfOXl;{>{x&%oar zN;FkDsktS+(O_lb>}Q@g;Q;L$I+_{31m3KTikc@f<=c4^4@ z1Jk1M!j(mHW4nf>DpTp!bJyJ7Eu?PkvQ}PHU=>LrX??Ttl=H+(zu0#FHQ^pCro9 zxX5Tfz2EAu}~aRF7-*HlD`!#66fyHF;5Q7 zA?En z!Zc?}rBZH7kMBxfjk3Pp`|_@(JPfHyc$ZIFJ24_q<@SCW>Nb2MEdASS@lpsacL;i3 zS6l56NnTX#er*N_ok#_s_kg@Bz1R0W;-j;d7v)sSIBVJ|j>5N*T;wGCsPvjm{Ca z`K8gO2 z@6ps5UYfR_Um?^0+J%Hp7~R>B)|r&#spO28E0r7|_qB)|M4P=I^Wtz4w0i5AZ%qnS z6UY#}*GgojE>QOmvZzhYX?YsmslpnP5n)T>k7GO|NtYjoB|GRHLXh&;Wcjx^9lg7E zET6ZtxRp0OaHroWD$Vv49x`+~MBh({_kVk{^)pIz=+pCdNcoaN*yXy8^ISdECc!Vc zK7-Rczl8cALvGAf(;whkK|4(Hv+HBfqG@7qeUDAY3!2+VoaK^SL!Ln^u;ajkzE<}^ zZcfP0YV9|4g@#wPL}jPP#@wi;ah%}6GNzDaWq+pot4S{Oa4puqHchADij^C_YDrfW z*Y)WqEfg~@FKSJ(Is(p#RUcn1G7FKlc&@QZ|Dqp~@YjclDU(_)X5Iiaz%EJU?%9x9 zYm~vLx4Al*|ACkhUb8YdY-@LY*l7p)?P@lA@V<|R#id<%Pp>r~DHwSD`fZj-NW7+q zcu(nL`3;<^B>hjE?8y4~)0PEcc4F~|!)M}65&(B)V#_gp%9vPt<@rqGE&uR6S9PTa zp8);4OJ(o#DY@)KlI`lb>m!^v=ZOBV>3eyNwa5d9t2ssXJkO&4Dc+uE-(4K(iAze|pK4GqJoX7Cckq0X=Xq1DGi_h3tA# zsS?!900^H!&HyG?npp}0J6qOmoTk-M+WK47=W?C8N@s5xgIE)7fC6P%_s^rMw3NIT z-B><{95q&k>w5~ic}*WAEyR95jf-*?30Y)tD$jEnzqU=X4Ow$Suh1t#SY+l~7^4#P zD@&z#r$^_zYB6>E`)$_r_cJWl6`MAm>~ofi8nP+pytywjYm#X)J)FktXALOt25BUX za#yPgDlO=mv-~)9jv5VMPKrIaQJe{z4@j>foi5zIG36#FPF4!u9&*>WeqnGLIA)Yt zAP8~K;gepd+8|Y1WoJ22y9~)ab3VoLec-1I4%M?N@XTCYf8H0>C4`l?_9|=4ZZ}_+ zK?v`6l9V-Ecn}ATyM4nj@`YBw`W$mj+6)U$d2{atp%=VE7h^Wl{dJJExLt%26E#Y)QSLNQ=0VC>Gue%;XpBk6{cGY<>4?6)+9@#&bzgxD!oE3c0#eBDb zcfPGTn|rts&c+9Ob}cq&Q2RO_6(QdsPdQ^j=<&~(93p_ew-|IOhbx*_y)|Zjb7G_6 zNiKMeK>f>#y=79tt0xya!%tgF1W8U-cN1lf@)Q72WpJFXKazOqG$sVEji2HO}B*3hbbhdn2?2tT>LH#eC;P*+aA z;br(>-cDfVj#i7r-uoAD=1V2Q4RYSzmi<41nR))bLTH&l5Hb1|x|;g|{y$9qy$SM- ztgB%^AxFk0daOGC*arEsQdaxC$1Zq%wnOc4UFFo{EVBIoafo^bqHe*?sbW<&M1~vS zM#1b#;Ta2>Vg2)YPiC5U+q#_-TiONvlc>D;taBHJa|Hh>vQLqWRl{j6QEd^bP-@N% z2O92g8tQilRi+#4dndQz2GcSwJ+{_&vRm-4by{-|qmz&~#Mv0#l9bQ$2#U1`5d%oS z7#m{6OrMZ5Sw6G;aCieiZ-~2z@rx6~KzPfH1!?Pd-I$sXp|`T}eGtqDCR(W0$!oN$XY`=FWwCKg0 zevuaU(JHgQfrTv4wd$&UY|A&R<^)yh1L`lgug z$9TapEv277mZzcn{_^0k-mapR`F-LbN2&%fQrANIH8eVJ~vk5GIJO#s5X!n9c} z2R;h0Q73vadB6$Ndsf3y_!?S*_YQdb11cG;$>ct?=VakN3L-!L_x+~xolbF9?|0?ePoJ&mEoBI*Nizh?135KALB0ww-zRakNP8fX%r3!QS0;i36c;5 zhIxuDO9G#Gzn9l04hrVyoq4rb1_insof?Sw8}+Fi#)BBp4p1lu%oD;pdXf`;6({Ap;A(=?g^FS*(L)as_&oy4b}?o?R= zky)*;^fQUJF4rsneB;+J>`Eqde0gYdvHF6uT&sYf)YEGA-pr652KoNv3|{}#gVP@= zvI(wXwr0D@46V1=7Bf%`Zgip z1Znq+i!5~;QQ%h-?l*LdD!eD}3X68VlI1n4vOU}uc>VC}y^57NNqY@D+=L(M?+cGT z#kJHg5ToX$!5Xh30{T=YD?iF=tjxIB(zst7Y!h8@Sg+ifQ{(Z84rP-uyU}CM>2H0( z2fs5M*O)59(oGdB)vYn4SKsL5kA$I0hYmC5I)&cZA$aLxdf==_un9?6;ZFna1sth? zjsM=%olNDqugWz?e|0OqJR}ToOwniTK{gks7`{XC*V5gfNU&fK@l5`%!=6o+@Wm|+ zsB$jt*fZ*%HJ2+ZwEbv4FiwQ*LASHP6C0YL)nt6QD`NY-^b&xId!ejE^T^jBG?Jtn zTyX^PXQ~xHnKb7aoDL7Ef2suZLmNR)ttXyJUE24nz}|j%%=6*#x;R$`Se#zPEm60W zvcm0r^|qhTyxq8+e|5zR!m!u5HJEKeYvi5OlO2+IR>v0bZ+?nZ|BW*{*`j?FdaP|4 zC}5nLRJyjeC4(lGr>tJ*j3KO}F6ltcGWVhLl4ti@s~2=ax6QQtFIFAkZ6|^UxEqij z%je*5Rz1XV-Jto-l|h6>Iq{E(A+f2EhmzgH8`H7b%>3#6gt5?X?(qJrVCb~}y|v2K zMNW}(JY)~4I#Y55v9YnVy{L%Fd6h{pX$h;ZLG;`(ZsiqN_X9aT)_*MSa<4HpW%I9K zz!$yljh-HsrqM&A7EUWNeucIhT-xj6b}5GS71$?O_s8*D`*Q1w0(w*- z!Z812vG-c<+5XC=8j9acTPFVPw?{p^nbq(xh#Zu4d*sb|H<#&iL0M^CLUU4((!{aU zRlR?_*mWv~i^pVUBc1bx`|=|Q1{R=RKT7Fz_y09yU2Viu8!yFI=l`&F6R6FEBQEnS zHEa227fQQo&!U9^4xdGpR&WW_jI9jIt?fm&fbp&>u#xe{rc`!J@<)ltc9(uyiCTq&56-n2^d}y7{}8lA?#Lx^TlwCp!3~)LIh87Rv}5SfwchW+ z3T?8`a=O>?f22JVS?yG94yBw$BlCp8b?ji(x7n)MEDvZdi!ZEjxjWmp;oOU)?>t7e z-(L50UR}_{(GAs~_7+$t+A;BOV@v_(%gQQBI(@n;0emz>&Z}^lu-^4|TZ7?VhTov< zSJrQQK39@JyB#IBlntpVrc)=W5lIEw87FFATwz~R`w9cyiu9cH1vsd#{Ub3}#{Sn`# zBGxtTuxUu5j&_6=#4U#o;VzsZu{hXqv*5xs*&r4%Qj~@1PiksnCkixwc0l{ngFM0R z%`+uQjPJsi&uM}_hrP58baX*}W`ynFUntgR&?Q+!SukWH++%@iajp3RB%%>9{Ke`f z<;fv*QA1U@_H;oz-#H7+=@qirEFWL}Lrc9v`EKYVN$8qb1aE7v!L5%E?hjWzE7WLI zicS!nPrh*&uCBhDDx~mw&~aG*_#kMpchBg1U;M{U4EAunX>%z|CP=Tghz(H^7+atl zT^1|it@ARUWJ*&cpmjtxXX9t{|EN7R!90{0Z@?_7m+N-)PS7vB4QH@F$cuphDa$~7 zoQQBTR~ppuY@}#FJlfehYsEHk7E%U|20gKJ4HCP8VMP1v zWU+K@_{e2Hgy+@9=g{&m8E8md74hAFyC1EcG}^T>P5<@#=?LdrRq>lBp5Jl%EKpR} z>;m2bk~rjWqeXsBO@;txp5X>dkHYSlW|_j-QvouvAGNXie>RF+?3mM-CF}M1gvf!A zk%`VPv*Nfb;NUx;hiBxUx3!!{*jp3tD`>KEAFR3`IyT@Lfc4uEHkcPv@{zIM*RB7? zr4V)L&j*~sg6ItYNCzfrtFQYVmV!)_ei0fey|FuPw6;)M=bJh9_6vl^95pv5PvK*S z5i5%&+JgMK)BR>`kfu5HYYN~|{WIhPU2j2Bd`(d+Qo+0F4Lrr4ZPU9OF#PGW3C%*) zSf+OWTus@mgD0G?CIf;HKgxNE~-Q)pFoYK~NE81{3+d$pahG=IX`_K~-@{SYTqMWj+f)_8J? zseboa-~ug-Ib&~ zv(iO7FTp=}!9~zD3oQ>tO7hS`#D8y8rtG|X#J^O96A?)C4IWmOJo!LcdEVY@TDvj~ zp2tp&)KYwH_Uq=9Z&n&)S4eYIo2VeiGS2Ot29-1fBMg=;?FYRg{sKFF9AMaC692>94HOF`5jk2Tn21(*v;1x99TU zExA=9TPI7`5J@4G3-$*vax~Hf?y6wmQjX>?P2r42c83clZCw$jX((DEsH$YXxd*+j z9Izz%GUGVO@X(ZbWYJ(5UP2XSuGbpW`pa4IY6~&n5h5tw zUQf+FZQc)um4jBNVUeiEGHSuVD^LuiTBi~_ zh-vy*nJJPFM5Ti7IRS})tZsZ*w$R@Q{&Kuyf&q0jf!N+Nq}6hl=^RLHq`L0A^b;=z z%B@5`>I?F&7YxnTK1Z8v?H-`T_+`GCHM>h062DJwt}9sS^TnUJGq=6xx5w=rw_aR# zViGfV{16#?dp)c)CSORKGp#X?9PkBS>_(*N$RhwMfg0a@?gl&PD%)c(W4t^gHXG`& zArFrJXnOmxgEHUCqCWzvm-G1U@DAVVdhpYD`04JGKQlPK4vp7G-6ggl0dDME0-n!Bv!S4l)NRZ>@>%7OKLN4I3 z5nbo560{`wXc$Ec4O^532}{>)`97untu~qtF^~HuZr0a52fHc*(Dp zw&2yFap4L^$vQ;QFIZvOpeTh+VmX}Ji@Bj7_?!J%@Rf7xU!8oKNgDw#J?HNoxwGpS zs;Dxh4C(F>KCZLo`6|-S&|Y3pPb>POg{sH!wchzsN58QcUhKNS{Bv`mB7wsZ>Bop@ z?w}JY&QP^VfIaC%IPDMV5B_p{7;;+vxVDi|UtW^kZ@=2cagjyX=n9<{wc7=TfJ7UD zLu15yhAXw*hXZ&xJh~3fMljU;vXq#xA^=H4Y4!3h?>(14-L8UBc@Wgka%iJZvr-?v z=@>mLTGcGMZe#MR>z+lI(S4aVp_WQDh7C1Bqq=L*jqibGfc_n zC(PcgXRNGRpWCe^k4xmRC&{9=+1Z;Xb)DI^;y-)$$>C+>E2WWZMHu;vB&TxDy`0KV zvPjLx4gc23U>5&6`=-{8N6-b06c~2reEXgi9l=|zmUa$7d&QL8G_+v01}H?(RomJiiKLA@)TtCRTnUbA{1cnu z1n}N@*3gwbee(e;)Bmvm&Ko2tnFMMVh_b2<+Hc-<$0ui+y(5-8-w}_l9}Piy!w2?n z{(W-d8EK=Q0qC0W8d>iE(@9HA9{!apj8N+F-tUw}+0*HR)=SXa+2P3pgIh+m>Y1^Q zk|PT9g3@+BNMl7NXPSxJ+hrhDuRE8PY;tviWGDBQ+-oe?#6K%D=>)P83=T{NYt%FG z$^f?QeQUWJr@fDng&?Ak_ZzI8bK1BCGzk-j} z@u%IwMZ|~u?QFtKcl18(QMaA6&p_v!6RoEp>SjvF_y1^<&K=U2rSfX_tr}HDB%s{C z#oh|!)As5fL)?p7L!ST!|I-|O_wd~hmV^fvu^DMHH8mFYB$eYE4L!*ym@JtuAoQmjhK-w9}j z75wa7AQ4$7gEE2s{sDB`Eh$!QQ9Zw~m>a2RH1&dZafK~onGu$LzEC26B_Yj{Vb{6h z0|KhTI2c52W7j=Q6rFh8mfHWZ+2YqlZBdB9CWEqmdiZvE{X;zX!LPEChVfuS+WM0` z>#$1NYBfAYP==lww_(%Cxqd9xQj?&Qmt;~JJVF?Rm~6AU-^6sKl*jUn?K9Os&k zef7gz;1hZ>`x&6RezQ(FdSd87JuRTgzqN>r5ltUr;ANxl^ge8da|(XQgTZg2bN`sE z-NKjt{rg{!c*~S9tv9oTDt5soH%%i0 z_JD=*26ou5EQ>W2Sk0oW?eFewRV=s0LB7<#)G5&!O|{bVsX04FGJ0&U z-i1MPuuF+Ws;gy8Lak6j`F-#Q74^t)NL+ZDaMuY)eaD5o`9l873u;ka63TQf1wCaY za4q(`>%^ETUyxFr?vGM^p3F>tXp^<2*tzooHMx2Kn%8qInhCb6DA~tu0)hpDgEVev z&o8Tq=WGa;Wg@jncjyspVINAqXhjz`GqOFu0>nRLa8hL!SK4J0f)6xWUD((Uy6c$M^{aiEm;4H>K zl>^6dd^-?pItkyLd6kIl4gZi1;Q>FbN&~3|@}$gK`n;k~!~6YJ(J4g@=rnJn)?5h&Pbe0qWB8t-K(wmM0kkk-?&vN_V9Eb3+khInx|w7&MPOI4tQzluFmlI?7jps3C13BK?Cyjgpx+}B@a4)As;ALBEe8!6P) zJ%|ntlFRpm=0f2BjDxD_j!b5Tu%>&ae{B&3Zf+)}eti;qn=?&Ci<9a30_nG2WzYAi zY1pU{O&1*HMj>faFHn=m;->{#j$7PK5k-9{+4z3SFGd=Nhg(C(+w-K0M$8=93dB&y zw$FbSLZ;qU);RX8OMZWe`V4DC1N*Nj`wU^c#Q%fVfyIKOGPzZ}rM^E*CNKS_1-4&+ zzsZ3j{*wKQ>LAlm{Pq)A5R+uUNz4!D$~OG39=75{r_3-$do6g-R1OW#_MRVdVoaN* z`Z)dHjjxk}-w?-QN{o-Hr`nGA?9uSrz7czmPqz`rCMzH`v+x-3E=@L+^=Ft-=Te9) zh;}oLL9moDF-cgp-Os!?%&>H z?A$a}<{oxomJkpI?tU$Q9>Q>^O00!_C6m*#jV;gjbP0F6fpa2JQ}~|2{0PHPbJYXx zd1>Dv}g9N$p1ZwcKGjWZN(Y99uROtzqtbI7u=2JAaie zaNR2Ri*{v;PV<2)C#v}<|KEuaH95rl3Y$RscJ#hadb;BU#byEANLb{K$e6fnWw!Jh zu4&vYj>$O3UaJE-n(e{75Ddk;;Wp6ugP&7jMJh})K%;wmn%Fq#Q8D7)C})L!6-%zeDMU)=g{RL6Qvg<$W2WMLEBY+L-q11r`<=WA zyD)cd4m*4k$<>4^V_|<*qm!rd`PTjK!DhUKdbg+f8PZJuQ0uY+7QeUfN-@<5B#w?% zEITbbK+WXy;=NFhgd0d+v)zQR2O$+jX-8Qx)(FUeZ|zm}G zWf46%<@p6YKp$29VoK{;s`XTSv_SoyCId;kDW%`h4`Z`zxii^Q%C9%SWO{*D^DtcA z=yr(G>Ost?=IN1bB@I9B9OcbVvB++i1n~s3-r}{#b~!%c3<$B1s<^Z0Z~x9`rXwq4 z4~_40oi*7_#>!8PjYWdcq&vx0QmClCkrzKr#jP(Xr!u903DL?)>MGaf{XLT;8uS~9 zMoM6Q2ydQrn`w8fDmhxSzjXDour?1~?2zjLpIfEbX(GMu1KA7H)e(l~wqHxzn<9%k z0R)jV>BX9{9)#IrJgXRm(;uYOzc~3bUIZoVXM7q_qDY$>yjh4>nIo9J zrYtmfB_58dsvkmc_?cp&e8*ZDY5aI{rXUp845|{f{LX>kaXhwe8ZqbA+G3~~l@4I= zGQYIZvLgVnY5+;~yRL`Eb)m~zj}$+Vn^9zZh{x~K6YX<{z&DNC@IB~@^OLgF=wskT z2HUjEC0A7VQchjvnt>{eDcDd=bxs+J(`-MEx+UtLd2)kZ3@|Xu*uUc>$&6xi>h6=} z85^+eRd4dtmmtnBI#A~Kve81Q$O?M&8sY!0_^vC1!7K{?Y{^#0|@075xLzLNBTq@JsjYoW+(Pd#oRCD+deYUoJ_%Ru+?u)wf zmgI@)%R0utzFs1ptv#t4%*V3`Pz(ot|1+TWBYi|x!*e;M7+v8`BSQ*hq^!d!wP$^y z)-9HR`xdl?Fd;Y*!VWx+(jHXSGVNAA9+W7*a47}*>!_Z7aaj)WcRX0GN#z~j`to<= zzYJns>a+Qj^5r}w0tC#RrC(%#y5HRs*VZMN8i#p3v$4&%B``5geZ{mZ|0}gaYHfl# zLx!NF~739LUNft*SaduO*!!8&fbOy z%+D-m)>6x|SWp&~t^2XWL>l^QlHQE66Mss9<+4-4d^9aC2^d{WwmWTsFXIiM`I2RT z1CVXI%YxnAEUfJrIMIcE3l(FJrV8 z2JBDSbHlzKbJId0PUa|x@(V3cJB$+v+RfyMvyXCUP$~bMkuF1%v#3}A z|BmAbZ>*HObJ@34<7X5et*3uz`oz@?#`L^tG2D?i-bMM;I_R+GTU~>Ui19C@Q|n+u z5yl7mZ>THLLz>*QN+N>6rdnnX#hc8Ted}U>Gl4@-5_M> zBWSEBeci5Ct6@wJdQd%%zLj*`t}nEI7``6nR@{MXzYm|;N(Z+zuQ8(nFv^&n7UqyW zQ`w69caIbA8Kzpu72s#~EYCN8Oo;1=zp#F(*Vi0C+BxyNHdB!9OUJ(qkXR14{oqPP@hKxv%lSQj##Xurmv3P|tcjjHIT&}$-vDW`g) zUB;9l*RAYl6}G-t-IR8WIYvs@?*>d9Xg zLYu%yq1~0bf^Tfs#8OD_aVwpsda;?l!h^BsMZF+KpWo@*fB#JRod1g$yjA0ofAW6s zdkAFeszAS)y0*n-gB|?RIJClHxtxMwixq$AKrp0i{ugNd`NY<|G}%JqxGULWd!sbe zIxfHe8c7f_*K`mNkQ69;8he<+vFWDT(SBIfHQ*r7yKz|^6n(ABaW8MhCFreS!#y5@ z3vX)pd=q{t9AaH@3f|xg3{)mbx+Vo>yoxWQi3k6Fc)RkoJ zW%-SXkV&3(r=oB1>nrPHel3@F&(~a(d4U$pFa|b5^4zU2Ea$6^OP2e8@Ebub)>$T{ zXtsRMs6Q^fjHXSJW!7^`!LTD?pddg<@E}|e+ucYqKL@^U-Y!R7ky2)lL&Z&%q<$$A zGzxQ)`pr&~vgS~p`CU@?3Y!EN9*dLYYyLMDfO^aU`tCNJyE-b|MENm8*lTrVb^CcF zKH6$vF-|!Ni}%oMURX&d<-wIs%){1@WLik_2g#>Njh!Qo*^J29 zs$4PQB&W#J5FU2(x{buxRC19;m^G+}w{w`{ink=eYPUC)tr$Iy4vMUOMOnVRMC%M1 zy(3*y%c~8`TYf*GPRfA2+y2_UuF&?qy#2(}t8xXfLR@AK;i}Jd+Nb`ja`XKjp{bXY z!{ak_+(zD2ul36;o(x@KGfP~pf`ANqIhMvUXaTU(Bf&9=AY*DhW(2*yRWJmej&s zfHM}{qZOUcgZ7h*cVu#Hu(x%c;7Jg?{tMs0mLCv3^>eT%W$8e?3)9d=uk_&8%DyGn zEA60urUcTCm)>L>$Wya1S|*A3Gp6m@TCT&<@>m~o3zN!n7(a~M@ZV^2_b~3>md{S~uZtFGk zw~SPDuyk0X*6VI^^(;nzjkT0Tnx;N^G?WwD#fK3F)v*)aD^GCPaTuJjZ)y;N@3Am8 z;Yfz_EJYJKuT8tCx96YXDp>p&%ekT>8d>f*pYr(S$#eP>0t%w)-iF(uBINGF5$nYY zs`*8B82Q)zC`mtqF?q_s?!88bEKYu}C6gxSN40#HrzgJ>*-+d#3Scc^MD3PdW*6~ufiC|er8b4ZqDIa9hqZ$A zaL!+M7cTG_N)3jOj)oi-6FNkra%|X@Q^8vE^A7oGtCyz6yOrEQ748)%FW>t=dIUuP zGgn`1#H*mMoOFuvIC!o^nUy{mG@7o8h|OA=&1zK9`WX6Sk96;=zZ)%P=@%xlcB#i3 z#pDZ*iU7FH;w*Cjl(IaoViWJ{2;RK-cuQV+f-F^MvID55CV?E|3wlu0s?JVktV}$7 z#pCxL%8xi5E^@WW4f3&4n0nRIr|ouAP}94@U@b9A?LG3_O${25lTjw{9>Xl zL}PZ(-h=jsjxle^cBCO)Z{y>#MW=n$mfQdQt9@mNV^MQ6BAO#v{H3m{S127J%Yx~r zt!8TNfFW zp67A7AA?i4%eHmcE8qba_&VQoH-n5HK+K&SKdS70NnJC+-o=-pNXS0w$D-ETPPn%) zgD>aEUh=5+`6w0km5gk-NA;Ko0Lsj|k9$DM&U>$&44*S?T+YXsNQeolZn$u3+c4&q zwV+O4((Ft^elm?{I(-iho`Fk0&_ys@l18?@jtg(zma*0%Pu|j_f2M0V=AqAnvGKu_ce9v8K4gH*4sg^Xktq%4UbTNDC z&0S0hVw988;pyw^y*e{i(RZE|5!a1itj3HgNd|{<;^qNav~DcCaa*0i=~cI*uU==b zW9pPhc)Nhm(l}E;of@5DYo3Gqw5bJL+7BxnNWOe7x@+`2X`Shzv2)mi)I3_~__C@e zj~Zsem7q9M8uG0)u*;_mbSISb#l|8u2I;r|0=K$^dk+47Or8im2MKFQCvM>{HGJBtI+;A)L6=`n#%XUh_4gQ>eF$k~h2fVU7<`WJl3P6k%#;3OYDMX0WT`@|U@ELh@5 zUyCZv;o7?}>f=^dve_r|IyZ@xp!2KSZgK#~yDMY2@CQHo7lX3h`Alt|1mo$geo0yY z@UiGrrMM_YY;f;K4F>O$VoqZ-XuB%WGYLnH#m4e==^}bC1w^*YRXSdUgey0 zrB2`PUvIX@9~u`YOO98cr|cQX> zx!wO+v{>B|&}7w^36(|d_q;6ALiFspc(KQ3vKNUau}8}$5XN4FB#)M?MytA$orRU| zq-(KY6{CTzjP(4BY#IaUXp579DRu>XZQPBJn63^Ft;SvgOs92D2J2fN_P0kv?CU=o zvX=phEB3|D>DRy6bBAcT_QTaNS_q7u_zZn6HYFhPMgvHX zUy_sfpuZU7oSgLE-5QuZ)F8}P(DzL0*_mzQwd444hmW!J;6{fZ^q?f$U;}%eSV?`v zO_xx>H(q*%-30f;;?oDo(Opeqbj`EnXsFMZeJ=5fFM5-IeZ?g>zw{oWY;0nID_+TG zcO%F{V#K2ePb|N7A?}x-e$YES84c`RMf-8E+$oR**SIoY5WA8TtT9)WSb?tE!#p!AmX= zP0LrRNB-s0XM)y=>0$t7^FDVr^;;5B!&-o>t+bmqD>W z*2(NgyL`t*47Bg}g#$hF*Tej7xAgzvzxg-+=0Du&{xgzid%pg5LGLO3d$MP;f9JQ) zKKtxtWp7z|&h7n8;TghS{--QMdpGA-zQ2yi;@uq1%m4Nwz|P>f4 zNCd7*bkAww=zO>|;)=o5;pB9D2dJ zj%R)G+Ot6h?&_X);7O(>5KExLBhWv|(1JxcCId;M_yKl5+aoc$kh_3a5E_po9n9lJ zQn_RgEcp9_Ckc%f^?1IO{g<4FOL+k~{^)zM2*zX`PD$1+g1~bDJJ6HW>AguncT3>M zwpH|!XpHP?eFLy0Mq3kZNsJ&Fifslr@Zi8B{{7oT0gv&Arb(c46EI~4Is94l*{9wD z^1NkwleGB3FS_(in&ByFV!HGZ{6DkP_Nq;cpy8H&}O7tg3!4IkS(}V%h(X zygyCaB+1V7%y18nF$aKE(gavVuA+Za1WUt?xCCMoCB24bq0tyJGiDFo*K=O;h(u+w zie!tlBvQY@%+yqmo>`9$W`;KPAAK?40E0Xq`J9cRxw7=3pH4eJ9%-I#!yli=v)wye zSzFaRxEELrcj%+1dmH!!6v^XvbZbL`Hyd(Knl8mxcd*cX{!TmUC?{W_il1b~UvF2# z?ngoSw!=>z(SsIs#?%0y5oBo-3c;8>v^g9P=5_(`%fq3aOD2obD}!Ztg~bh+JNK;d zXj>l=-Q$&Z4JIlYoM+I#{;2!#obHV#h0ec&jn;iZCglg0jm&?nF)1kS4ZGD6E}iUy-4MA!Iq(X^w2Jm=|lHjj~0qL)&vjj zUEo7^S9WcpnOz9Cb(=i%~usY`tlo$$yBKfzY`(Y@28 z_Q^;dcF|e7%6FN}^&-D7z0j|1^YKz%{?R~|`ZE(8H=th&*@4F}P)liE3CKiHwgpPHVIJif#Lkv;f*d?eXI9CPn!RdV*Jbfp+3W`}L=% z?{EcYe$A0yr3ay~e4^MO5!4!=J%LYG!W*plhQVM{Mc^JKV#MJS4o;fHod^&~GU;e&SQ*-|RydwB74#o>T9PJi)1`;|roj3=w#{w$;I17%MBijm5&$Mn=K$KfK?nd~Z zjtj6ne~VU*YYzRz3#&>$w8FK{Qh?_x`EdTuxX?4r2}^zVNg7;*khxtMB1jF~3C6v7cQ>S9qCS)sd}$e+tI` zALq1{eRV)p7SQ7F(alMI`OT#$E!JDjun8? z(G-Ig-rN>Ox`Myhu4DVO$4GRp0%0AwuN+LZTX{s+XP?naa_XgAwe@Tje*dGF#n~ai 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

    VSWuZD7t{ zKmYm9|G8n$R;cT!U@ber3wd^1wl)3;SmZeRqo~q`Msd#C zPa7X;32ARTzIMLT7r<@{0qWM|avonA5jlKQO2zP>np`6vHcXdRH>>K;=0*Xo{BIxZ zHju{F9~J_i3||!>{Y`yz9gsBb0@f$!6UUzYa7e*B_VH>PnRgj0OK8ytLnnT0*@Lwu zha}ow{kqxeA|NUtLgL#Jr8*WCQMyo(2tFMDMAyK>XL*?%VpqFqXvs%E^I!5IDcqE0 zEa9f34wKjK-9R0{4r8^v>Kmo2J~KYFS?n?N9RTTjXs&S}pKN|;*mc2va$J3rK3VzN z3&@O>O4Tvx7;8HEs1VY%Zc@h7^6eNP{k7VqYc_h&I<|@hb~>@AQf}?V3D}|j>^Km< zHc34wkuT%&oS#QOJ4_i4V={STm2&~u+4z-ifHQx|qxLx3QJwUsQmq`Z@uE>)1@MUh z7;LFFR{vejW52|vOU}Z8FTdNE)b~ATUQ?C;5Uu25p*nJf9rCn9G(*a0Y&Fk;kr=|dSrzg1^hyG_i?yekaz z_xNOmlITkEGHrA__YXw|8CdbWMs7WXk@tAEXz(m1T{m;0P(+k}ix3(lobB0-+XNG z3Kf~b+BU<7{V4rbOOsD3X;?g>;|54G^tQfSB6Q3_6S^|cPrbT6kx^n3Hpuu{dZ%4G zMLy{+Vq7AReE1hh0INLGR&a{0|Ax;@2^419Z6$F_|kTe zE$jPbo3h8Z5P1H)W9Uk5;{y~tyz`&sD7ZOX$e^sfveqW+4HuK^WDlsHL!VE4{3lPp z_=}%8eeCCc;dJ-b-FP=I?ytOTYi$plkMfUTHi??*pwq z?sm3LIDWDgLoJ-pJoj~^bFS&VU3|YD0Gzj~9WXArwXF_2OiAfn%a1tixyY%Kc+)g$qym)%yW1l_!!cYG6>E~Ym z>fNzIl3s&A%_9c>DIfypZB8?%MnK;62_v=^|0b)|vHvPp5l|2CcXj^*ooIK`ns3 z?_jXm;ED&k=1kfKPwQsOJA<`Gpv^(G2KJy1Oa`EB2U-=xpi(&&vknIanDdEp+lpISm1^?6mUpQw zdNoez)7&7M4mgu^I6PH5QzTFm{V~@Em zy2?4hTgl~>x_t9hyGd8MQ%zeH?y54{qs7Us_2^uX=`c zi?{VFRVE9S<*Xd#XY1ZyJD+2H{X|*HZQ90HA{TZUJE=<~vhP-X?iA{yvdyLU%ct3@?V2QdI$TBCcA!{W^!e00FihyHmf|gZOLq^k!JZ@+xD^{c@DS+ zZ#az%Q*U{vvqRY)Ib8@*9hT?qA6bt0rFx`7tD0@>rd(UovP@-tY%qMcvACgbnT>`u zvGHJTAY0}r%Tz`e`JJ&Jtw-A)WR^$zKjU)7$WU8$I`s1{Uv-ZRi}rbW#uv`l*Wypw z&HBdpqg7M8*?IWy5+2*nbK1*$?)$;phN(7N?Oei_23`0l`PtHWe>k?5_Sk!;Onv&G zHT6pzL_vN~I>E4wBMyMg6uf1IyNbLbTC>?U(ZKF`ILz3yxd^E|;v-VVd)qF!!D1>BM} zN^|1$l()V<$Qj_nSN+)vYzY0_l#$P7^R@*=VW{87z%GB=`K~xM+Fk$++w!jo9SBbH z2>7wj0g1TbpByL)FBhqR2Vz+recl>?7Wl?~Ewg&`hb-;+E^t+ zJ_NuRM#`cbUUl=On|gUz0$MtT0Jt z29FksLnHmX@KO)t9uJf~fjRggDDRRxdksfI4u5j&G0-DEKtfUOZ$>An7n`fEqf| zl}Bj-(c?9%w^Z16zd^x`>(p6pU}``V$#PCD7lpz|yD)pZxD5i4F+?p(-*7NIt4H)q zoq==edS{?G-Bhn8U?RGSozInv8!ug<0lP458TeXNml}+nx;#>ws`kQJ z&SDqOLjxx`+x<86Jo!v*B4+Dt59YxZJw?T?fM1~h4Tkxf*5DOBWH_8CKQ~a7o#2Vj zY1i^E+A6C+EG^&5fxxM# zRs3B&x+8=7v}v&77)9QA9_re{_n{8Bk;8s|%z?$w)g9vF;#n}sFnpDUH2DI@(^_44 z@ppdug{`g0jTzk$7=Nm99P0r}las9PEb+I0fEL_uXf0xtCKl{t^5~@iOwu{l7#QsH z*0X-juw!)Nm`+^36}Ja^%DCGjx{cUNhR{9_ z6Z(~19BK9CZR`C3;L6YXNf~|8G){*0YFjoEPPuzW1u$gwv2dY`t(J2DdDP1`HHDrq zXRV}s;+M~b3)~L?#z*3n&9=#B%7Rw>1U|79Ws`iT9KS%ui~6$v4s8P)B)$e`%D#7J zo8RJ#od@J;iW#&kz9yWst)wcl_V>1xW#lbpJ!t$9BzW(%Gp_<7ouRq;1_b24)2qcI zPl%6q)`r4LZy##{4NMT$m+-3|TV*Z1Y&E>rFh-`*b53{c?PIMxC8(_%i`78{J_~R7 z6t6hSSG^p&F*Y(6cej(2^#$wAPZ}ES3v18fSnl|VO(jp7MZN6K(v)6fh;*iH^c>ph zXBx$~O}Ms&{HojOSgT!*8+T2<9j3sxbR-01%^=by!^k4<%tsha96x#LP7W~+G*%jU zEgtpnLEP9bY`_I3c-HSUQf0B=DYkCGovTkR@LBi4G^2IMr?d-^Kv$h@JhEVOQ?%w!P$NXEe)ppcUjv4uKqd2w@b~?kBSqlv> zWsqOj)Rr8t4rIn5HAX^lksR`lrZW(eB)DYU!j$ehySE4 zwn0*Lw`DW5yc!p6F=b;5&7nPWfwVV!0GBxjxn@mYnO%DrYtS)*$-QY?S1M8LE=+X_ z5V9%W*u(tbhJuAvnXLJw>ZMMxk@M6cxt<=n)31GjG4mXtuHoe5)6r$1O&f<&kBt6= zS|Z|I=3Q6*Ir3}QdB@uI=T28|yl}ew+>58T9$i1(ethM0o1Zn`@%=2=^&U9>++Ul< z7hrYa&KvjdfAb&ww{Lvs&*snl-Nhg7!_$}i-ABfIjEno`Nxu2tw?gC!jE@}n0>B48 z0YIDiN3XyBy7fA!$?JSlvC7H+^Pd2WNuKT14p`p(0~z_}9CGL1$35tH4xaY+0Fajs ze!&o9G(LLp;PzL3_iz1YpZJx(_8(vT@lX7Rk9hl9hj|CKj4vB7c(s>*I_}yBuAHvl zxWVtrB@#+xaDECI#E|d2+(71rKi-bV%erL;#loj`ONkPA8z@RC`7YAu;Gh9Cfk}R8 zCjdY!FY|E{h)o!=&~l2f5Cql2aIc+6>=yT~* zM(t6*7}Au-31npoulk$C^jT>7rL?jrla%DWY6rHq9{oaO(H(wLsSFvy>N6E3U47QX zuV01lvcu>Yoq^F-=Cx7ugNFIMgwWq)gI$@(LPHjryoEd1hFDV?*{rl}Ccoea%fB4B zgcP;VQ@BoILg{n$V+v{OrS-xlw&3fcZhaydMD60w@j5x9#CjYZ6TOQgC`ZWQ;2!WMSK@`wSi6cLt(OU1=+ZwKCxYBinRJ`$<>2W z7aPjw|Blu2cbvi#G{^ARF7gP=r;nk+M`V&PX(wNuLpF8&TJ^NsU#9qnCGR_HR9tg&The3x0rF6GEJQ=f^I6n~}kH~kRW)Z5fPQ5IiHS7670Fm_Aj01oEaMA?txCe}GFw1@5D zXg_tC*sAa{w?%N4}fG&~GE~zM-A{7 z#I(>OUbA7#JQ5uHiiK+W7C}jX#g~jP$~`vnrHzl5PbG!{yNwC);}_z{U){jXxL$Ul zJY&R0^;_W-LhaWaY$Cg3w075@e90-lDJO8Jj|^1M;(z?ZO#ZyHY=ASR|7l2nQ&#_s zhqR+J{V9D@#zb=BuD;u72~et4iN&j!7t>)g22WEae+II4*EF0FV;cROIHm(tyi{8 z42&Wj-`~z$@sWiki$ltn$R}7BR0sxxJIoT`)Cs;@TG6vZTdDQ|C%j31TOMN3g*q4Y zogkce40gb-AnsjNZII0b3n?;-)^K-%D%aApYs*i-ilv*BwQRBS-AaP3Y7&Xleqc@%z7+DexnCm``3xwyfzVTu?}^=ikn=Ck<~U`B!C^c z#nsRTLgn}%9Qv~E=z~nuPA(*3G33fqPnVc7b)uRW_b>EaTA)-nI4Ey}TSNJrJ3G04;r@jl@gQO6X&I<#OXl**00p|C+F5A}e*m+6Q}o zRaa#bE}O=!z5ON+X{BG0A%4I-V2rVzmR!5=l)f=*^pSp~;;Idl!E*VkOMj-(a`_A{ zU`h4gX&XA0^CXY9hz}^^@H4L+C@+IpZhvSXqqTC7@G}?A?W0u0KBA_CfAG3rHDBe@ z$=$N}xB{$x$$Sy4z}nyXjN`zKWyi+A%D?hz`~3ADg^Vtp$ITa}xh!b27QpuW<&$iT zBd9Voo;vQjDQlmKA2X;i{eOjab=3!?PAN7RK2BT4KxOhU!ey#b+HJ9?@-4YqW>K9! z)h^2ABqi1QYSr}tgXz|d0y6NU3D*wv2U?E29pO)`P5N=ANhSW70Tf!Uq!N}W3>lZ z`l&L9Kk`W@atF`;M5e?n|4f@>*Z5WY=v(I7uPUWZWt2mi)F8W+&~{ClaiLwHtZa}B z{&FX3sHa^rty5@pf`)&dP1LZ}Y7}bc%J2A;uJdZ}0qo&Ub+WJ82ez7Gkg5w0+f4c6 zyA%v@8M zv{mUNl3BjuV)0$q+V-n2V(uCRfb^@R!bU!@nZA;bhfSJ5f6(_W%%(c=5qUGdBVJ_` zUs0U5)h+ZrRLFb;3B`?`@K5JbCW(D9c3ZKLnyeRCzh8;Z7A7`Wvdl(Q1IvY{eQ$b|Mh;j`>)LV{^q;}@BQ*} z)lE>#ri;fH0n+)_J35Xj9qg$O0WEnuo&ji{n3PDO1K_5PxyO&*ym|AxU;2~3^$&jQ z*MI%reEC;@_4JufeWHWT=T2~$1PVE1bmNRvX5hN$!kG>1CNDYhghj}^P=U?Dh@0!= ziqHTO;u-8ZzJt>s>^OE)okyY&+u@Rdk@(&~1mgr0uhL|UlNeygbAd77U{iJ%hFfC9 z&WT}k5FqkY!q%A&s?=z=JnaVCOo^$%h6$!#g(FmD4ITnOTw>7LrfiXicGw;0Q9#+) zf&-5Pm1Uo7_F3@6e$&Gdr;}P1F9b$y)hVT-wp)=0+joJZE(VqcD2=Q=)ueiPUBNQq z$Ws|<4NmE%b6{pLmgiAEgBv7)uWZqI(SR|O42l4Fr4J&rK{@#XM(!{kTGr2}ioGYS=-1)=zUF7HayDI*3!6 z4d3eQL58wUP#8K=#BP9JxpJ+Gl+Z3e<#7g(ruLF-7gy3ZCajoD&h{>{Knj2DMOHZ@ zEAoTpILZ{0IDu@yNIyQm`cDzmp?-h>vO_oRf9xmmCLYLV?wGq+%3pAy&i;sx65A<- zR~Dq{x7BwsL*v=l$bMA@ZIgvI*dV7)-F3b3m2BdHGtMh#uHhwNH?!EuyS~x_QTbXlL6-n*>AZ>NQN;K4m z>Ngbe!?A;ppDIb_Bsg^Y?67K-jj`kN#*oZG=cT?k)24E@UqqPx;QbT6z1+gtj0_44@ znVZCJa@sz&Qig2Uq(qbQ$ftf`45lJP(qI$R^eG8n-}&>dmL^a_IUO5{zZ~5XInu z&=>(Bb&;oiBTbX?vrp8%P8BgE~2*jZnRyz0Y0C3y&<{TkcI#1{LDwvH)e z)@Nv%b*BDjyv~y3z{2jy-0Z-9nc96>a~rK(h?Xx6MN<+N@St`9FNMF@eF?8 z@sH@tonY^H&l(GoTk4<7t{&1?uJ~;@D}(;Z7@^77jU<7w!r@nu>&x09KIl3q@@Q|z zdGWR9_=s?kIXuHP{b??p1N};)R#fR6{I?G>j#jL`!lT2|iS2sV zg&z8iJMGvHSbfSksjZXe+J4ohDr$S_RTk^qR~SQ-i8dV9dBC19*=2LcIO`P|*hiYN zDlsNpZ)>xi6gV^V z@0LH-+eSLJwdQ^FZNz)uq?ccQ880}KrhL4bzwm(%e85ij!yIzy0s|}cw$Y*Z41cBg zP+g^B002M$Nkl;e(!gF^Yll5_z$_0 ze&O`ehd*?>#`^&b^w}7&s4(a<#6ZzETr|Kmlg)@pdj#Ag93| zTo*}(3JZ~S@o>!YAK46S($KKI+A^o9(lAwy(iu52{8erZ_&I3lL<4`Agtt~#e`zT{ zSNZ$pWo+21HC94bxOBO7;7LS13b6WOmAL3rvELGU%3(>Jfoga}AHIZ%{0Y({i!>#; z0yVOnfhP#2*{hH=PP+~2J!#s4KxJi^RImW~Hc`(7W92%)1a#m)85Mnl?a zliER;bhX{REl$nb-pRcB0V$~Ui;kE09u2}~Vp_aAfwI6_bi~bAp$~=Yc&W{$ubvUa z3#qicR!`gY&Cyqw#;7GtWT=fIZwUgm`{C#zZfpapb>&~ZTBcck^u_d5P{azm+E*5~ z3p{kpyEyMpj0CRVG>&yL7iaXF{47fB-pV7#o$gUr>xZsali!6YzVTMk!pFV=K)R;L zj4dr89bf24-o>v|gK*keU!2qpN1$mZ{u*DBDx-^&p+CA&v-Mvl}qu@r#dgQguJ6#95WB=UM@%@xrfB4pr5H9-6ZqOnJ%^5M${5^Cg{8jvww7EKj04(Qk zhsUnK49{M*wVS}bHnISx1mfru+2;ZySVbv6zlDkH%A&oE3Ce;#;_mSBke@M?jY?7| zP`zxU|Djs^B{p!0uNDx>`o z>zIL8p6m%veaQZXB25#6w0p;-_UncxG%^sCMBA`Np`5h~jkdellvWRl*4jhb@ej3C zA4RDg+5D5&-{hJ8j#1;K(UJ4a&1R05iA_jde%b>Z%Uwq)U6Z`Do%^&I59*&38I(hy ziX?w!Q@2`NJ%LufNN;m>-RFuzc3mK?#zOgp1yv>wu}rP2y?1@BuP=;$lz!o1on&A2 z9Yo5b=rkakIL%9=d@Re$FKKP`)C5`llg=RLzr-Y!>+-MCm%N8~^%H^ixmVKCs$YX0 z{1TMjl4JE}Y%9M1v?=;lugWfeiy(veovXuRQV#E%Z)I5tc+- zY@0kFf~o7~4#GRm+iy_fpSA;#T&5@G2jKy0rzB~}i{j>cDLX^$>zT+0U1NdtgCuno zCgsLU1QFR9`Aoi&NW&`IZA;8^P0#;^-?l6B*kTYuYw6{Ig-T~VKLnALTB@CkrH7ot z@VUn!8qYG)j;(yk8l1bg-#UHcEC1y5)j#-+(|5l7N2iDP`E%*^w>r@N{Phd}D^L2} zgrqlfb+m_G`MBY??C<&?a+e=gJZ?=peLS$a@((uC6mc0odCR7$OW_M& z_`=ku{9gU)=ywc3aAHSi}a@_OAjT_m(({VbYzxx-5V!X|}{OW#z z4mO_y0PAdDW#Ju`egezybbj(Xx8D9`Ps!Z$e)-E^IsNu;{?_S>U-|0k4u9MHxf@qb zH?CjfV4%N4&&CGiE4;l@5((%!rzE|y;3|2=$FY|W!5^7BFtY$*&?vQ{can8cVi~N^ z108H6=UZ5Lm}-vHIdD_OH<3cR!6Eoeu$Ojd9AL}G0SS)$;VYL-9^`DW3-u+}@K;BJ zkVz~OvZTDIq?cpyU@otD2l-)E~R5dhMtza$Y*=3*p^*S%*KF zOdz3DIsmRVAS7rhx|Lyg#Fx52)5e*c1q8$(S-eg%v5jq{^rhFa12R?*VO{v(bI|K= z*5nJp=oDT6Bbu41AF(ZS!E^e=aVq^Ha@)sf|WgYEqX;Ok(1KCvW{@ z_*pM*e44B6(#;)7-H3mk)H~j!WV`s)zt`ePcj%R;^i9&YZFqN5l#sM?s8~QPvgC0* zd6%*HOP|e_dwqx$DzYi{B;}NsJa*aSS%_T+=?|9e3I-)`NA~(a?NA==hb*E&mPci& ztWpzaY@P8K7Ba5@jd#t5r8E+w#4D~c)^4_W2wM59Z$BuwpW05?(FJLv|Fq{y<6uM_ zO48}ZzWv$ye!H>oPP;VQH`a?&st$vE0$yFF-GI>XQu*Z5{wQtZoBb>;)7VRTn#h9( z=Ms6(;<^4<4O*ui^r0EEp%ff#DDL=}Hp`&}S2v2ae{~uDUaJs7Fy0KO+Ff0gy_-Av zE3dRW-mMuw%M%7!j0Y@ys`vPY{$6o0vDRu!HTEMsnOmE4{g943hK3|A5W2bPn zuL2jQ_S7#cqfpsIN`!)WNl8>&hJ5DcJucd+lG8Wo7jU;j3;S3QzGIJomsS_A+T7OC zkevL2106U^G8=q(7|YYI&cAVt%2JGo4ZIlIM_S$dh9LE=V+@E(KbJ2+p(V7~&h1>6 zf0!RXPaUmuch08>Ws?IKt|mBx>v(KA6xVuT_fKUJpmL^Od8kqp39YA9@ z=Ae@AoT%-v%krtR)yA%w#Mg(p{Ssd41y}0)#9zf-2P5_4wLa4J3sz0?G(p)?-~L31 zz;vuAl5$40s9&BPpW0!J;-lJ`=ix04Q|^+(u=D>eqivN}`?ROMnv{=9HG^;K>{I!w zi+57J_Gt-y9oP;5@?-=fXGU8YZvrDt=*@=F`i#-EuKtl0_;QaJ+FIq4DvM(-{+$jL zHjvwzM5x-hSc9LjN+ZJ#96r$XsvPP*vMOW7Y0)F23OYt&7hn_9q%knmNq=~>FIy*6 zYzk~#HaO5yY*OuN!Mf_7H6FxEQ`w|p8_*h?HhJ5geoMj1$}U~$$#?p&<9zx;n}#!S zz_DqPzLqat#WBf`>ewfI(;t=9c6o?Z-D`vBE1^IE9>>33T2O2l7U;H0w zdmH{f>hI=0ANcv=ztkbIKNEK&=F9)=usI~i!2r1a<$#B_-M)QWP3I%5$+Bs#DZF0} z0OpuDLjQ<-*)iYmX8_Z2Uh^IRox!}PLIfy1UFYynfLLxM;_wNYS1w=pE?2x0r^dss zUE}c+KCj)pb^5K}{k_w#{r2C@BIYlB@W$zd7oI7_?nM@kg9G+1PUJV+hW4*L00;lOrMj7Z3ble!$#q`_3-N3EDcH}cAu`a<~gheiG79#{K zUz?sS@vV+_tdqC$wkjF$)!Mt}_M!a5SKmJ2Q604XCX1w2&~;N_pjThT=;FXYtgT(B z+V|+~!UCA|A#M7)6el@}o2+rszD>~bwK9hpF5Zwfbh;3W6WWYzwDqJpkQhFBSZDcV zm`F6VS=#z&xr@D+T7J@6eW=}>WRS_bIZw3&~q#g5p4bs$|s=lE4SyM*t+H$HSMC{M;Z0@+XcinId1cy*jf&-(TmTKsIR!Nd*+AW$f7LA3xJot2n zI}vZb`L=)yR+;NZJ)~Ow3U1OGA3TF5Rp>f?8F#4YLT38@0Y;KHoUqlU(@QV?Sl^PY zP*KjlME`bvNGqfMV6&U12M_L_UU}svvO>#xSk4_%Y0EQ8^4)PMzPQQ)=g82w6FaC^ z`VX6jVre{gPa23K zeMltZn7OMHQhAU5edU$!0_&Y*pO6qzqgC(5H;SYO{Pmyhrw0)0JO+-7VB=8LD-Yzk zdd(*Zq{G{;mGfHH2v>O&IM#rMhn3;UnnS-(|GuDFUvw^baQgOlzJ+}GMQs&{G>uW| zZ+JKc^b=_~|2qC0mmPcUqs$}FyoOx%pE1-$xVm5ArFhy)Ikj(-@&Q9T(rk5Rw55IF zpFzHP^A+;tB@NruOJS#<^>6#ct1|jtg6&BV%&k2xgL{RiE$wSzyee}H%QrviYq1hE z8#lJ-5BQ~v zof1SI?V>M9)MQ^bjonkLa^a;M?#EsecPwkW2eF|tJv`kdX577h zpV;w$>qF+(JEu3_;?JskhabT&xu!=Z|3ZLH&kuQXja@XMZI{{1KYaK-8f<*xCjhH{ z_I%5u`n&&Wquz4+zs=>42DbR$%?I8$cOdb(rFMPKH? zOZ|w4KHHbge*PMOjs6CN;GE4r?RhZ-4l1qr==nO!s~H^Q2M$gvNCxBQZa6ue_?wgu zPH*44b^7jWw`gIc5pa~!Y0{LK;$}X6>Ckc*Hc%&EQ}VxQTJdV|ED7C|g{^oCSh@)? zK_6~~H5mBaMkh-|!+X9OXf&@YGu`d*Zkk)qr@`%mWLtd(A@O!`s;C)`C^!D5LDBnGO=z}kY zk1}p4-arlX>=Q#{>@z?LY<{kTc)mQnl9U)a`b z+fo+&Y&zhDH?0Y(v8BJQe#~zs?_q2t9^_wk z5I+ZCYBc=>)mUFruwN3S)~}QmiA)!W)kAp{$wjC6iCbf{99~EOTN(>YqN;(}1#U;pN} zcna({Pp`l3lZMYWefF1r`Sj^ef9mwX7heqjaT%?b?jQCD?9_CddFY#e_O;Wm{o4PI zj!XD~d6%!+m-mqGt6%-f>F<2u|9F?B zd6L{!HeWygOaId87e4uOrx#y%A&YCFb{{eJd2pzJwp?QIdH3|CFMj#-8-M3-Tk*`2 zHu7hLpZxS^PrvZ#pX25M^0}!-j)_g0Z@KK4@pSjjgVUG3^6cfaZ&^Q`KJ}SjIDHEH zeeikh*H|;c%($+Q?c_Vk_oXkMe)BiJ@JwxIe7SZ_`&GUd@xN}e>EHNV<%tk0t398! zeEPX-5aX)y{p`itAE4IVUf_g<@i6f+t|>x-F0@QW|`s_i==u%3eEf z9oEZdn^QW*-EJvV9UJg*F7uGrL(sb%L_J_pb^jg*-&iO&{TO%pvFoU*;CR+0A<$uXbg|sZ7T>_s<-z%5Qp?#I#*O&L*0l?=uUE#BP z*=~M2=vcp%aR3hj>M?Yn9jikCuWLQv*E1 z!NoeuZnRALRl zqfGpP#9W2T->)|CDI3>^d7%s8jFPFG#vyI{PM6G$KJd3@E4uKHX(-M>O`e01gX z9Cs-0-o1Rf!K8XV(YMj_cs&pE{mNs@xwc2ZIE<8w{M7}MdJLS`V^XKFz4hnI1N&sX zmX}s~VCB#9YCn3w=}V;alHy!helvVm4Bh-D^T!$@ZEK(IqMy}48=7?Ayyqbs&{SEQ z%)ktd@e;gy05ic%a6coPPN7iCR{|&&zLCu&4KB(}+IMAB9zMZ6G`QZ<_p7=8%bA=~F1I$IrFG9M;8$ub>b1ckUZI_0K4kP{yfp zEZT#L8cVaz(xV7_McZQH>l~Ut>(qX_a(W5*>@PR7{`>~FJ(G05%d5^f+0Jt+d-qAF6y$v)@sM^l#+juSPIlpH-UJ?%3uhm8TEC_JM0@P9f0ggqj1OhQY>ZK!4&d-f`CfYI z1+I)=KE}GSm3A|AoHYM~%lnug{J}g=XJ~mHSYakjUFN??VUF5$LUC43<_7+W^CX|C zeOg=Vjyy*Oc}deWvXu5a*Gp~3AF91}eYDrv7p*(r*Sowsw7IkO?aLWwZIfPU`efCP zam4hdzc6m*=gChu{lbXBW`h@tdDDwAgR}SqlkXL?oSO*Dfmb*v@-Pq*>x$mVR~`?N zlrIrK{$}3PvC%vwU^*^K8y2C{>);IZ*{iKtw;3B{iy5zkC;JBpkRpSFYZ+kZwiSTX zKP?vQyt8M3?jn(EXgiQhD>iR55k`ubIqBi7Cn;UKe*OOG{+nXtgMGJy0P8HTm;Z^I zecysrmq*?(w!u7m!o}H|q#slsxcB^ZS@u0I=l@#)(?Oov*m-v1bgEZ~<>O}GypG^U zr=a|R0hs(M54^t-d5cGo35x{p@w#zSp|&~)ISbD08kf`K=NTX22rMaM$ARSFWI^Jh zH+OF-&tv~3IfEoVxe!aE-`$8JZvu&RdF+s~VG^AK=CvQ{w<8`mulDB$uJ+s!~K zLZM%dGedI+Bv&lr5_;ggV}8*C1OG4H8V9SzT0$n7(LC9$LCy)sRl3KI+v*JdQQNO& z%OF4f%iw7`Ud>PW=~#R19Hh*3Z)r{&+cukjYd3w!7tb@?W9ngmoBgJ_f}7yhLy)$0 zNN%91+~F62-8@slwS8s9=7{7CsI_~ex9!?zx6#f^KJy7w?Dpj5-?p=9XdLyPu3ude zgxv;q>mmGTL;we8v)~}_1MZBy9aA0Bm1o=hFY!W_d^e{fomX4hbM3RX^;~cv=Th4< zLQ;y~Pcw_VY(gnV2K{zFG+t2UrCgcd23H&gl0IMlZOA5->}>uYac)`O_M`Ud1YT%0 zfbiG`d6IaF=4mO9QdUjm?Lk}*phh>X?w`(c^FL$FM}euIet__dX|W{d2EnACcwo~o znyS>98#~PtAX$u5dT_eH(f0G?fqx#fskF6=pLz1ct9(-Lx-P;HW46bWm2*WU+G5wS zkJt4PE&BM@cZw@xw}q7xcGs*d>nn>6(u7dgw$9qxcUq zMV1-FFxEDjbPmOopLw)WVeTJhntqh$nQmS}%=%Fo$9^1TKT{D$rX2cXxB4P>Lk49W z`z61+^5}1S%zt)b8asQqM6?;94(#HSl#LP7^@(Ye=cj20@OL#Ec^1X4XIfB9Kc=i# zW`g2o!epI0=28AazWd*abHvlc3H#R!$v(8NXJ)o;OgsHM^4m6b$9(aZj~oplP*P)o zxVt?{b;?Knoo9-f{9>vO!nfGGP~x7IYcG|{(CW1~jahq*xzZdSL*=YIJI1j-{tu!! z#nT6!hk#n2a3F6o_Uy^JgQQQjArRE>>w?l58qP5u3Y)w-2j)uIsYteN{#{Sc9J*ZK zjP1ntX;{~n)ue~&L{7@`7s4+;jiKr?(htqyvD6jmiPS@Ut9Kh6ZI<{9L1i&tp9R+a z355NOwWmCCL@5e|8K@0>*Lg#r&GkQ@%>Af47S44FfSES5bM>}ea{e~a#uJ8j$QitG z5hFnJ=p%`g{VQ;j_lnFL9A)*lJn5LX?tD3L-k=_Y_rtZ(==~t+JIi?9;OEQ1Jane* z$IiC>(a{+WPLjg_+Xccw9$vkCnH937COzo} z9B@K!1F!kv!7xR-r*~rp;W?6$br{(e8q>Z;R`|#fAsy!qs#)OA-PrtAsmFCCH#97n zU$iX9V{cx%EO9ejeRM$U0<_KnD6BxZ?9@AhR40|1f$q3*n${n5odD@|^6!LmzFp44 z)4ZFfDbKYqrD*$Xcx~4#Q|`6+E=cr=X%pLkZoPP3O`(xaTNSO&w|!-DvF4o(X$5ra zjIC1&C~Ru>6^(8$~-ZYRI+bTdnu zzDa?vIRxo=f*ww`El9C>{!Z_~wenacc=Nb^&C-pDG{?63YH3$zC7tAke>S1EO+ z`U8QXxoo%*2Cjd{xH3yuH_hRN2Kb?+>No6Wp_@fIYK-l7k!Ddj7|-rHG3Y#pE^6`R zqnR*X51Gc`=dqqmIkH4hW=@~(!BEF5JzB<&1G10v+udg)i&Pz>isRVO5}C8c@k6N;?IFKGes+l%p8hdr_WcPl&>4pH|BjK`)Mjh-jrfQ8 zd^+otCJxgHq(A7R%=}@HIa@}29EVNh z%REJ4|j1*ZX2}X*RP@-3Tcl?$Nbbi?J9?$i8{m=pl$gm(puXE#5rJ< z1y$dXIufAh#JiEGnh4!A2edx^Z=bMqNP_HJ=$*U%*fKBjqah}>Yv z-MS3Awd5U@i2-ZJD@U$tGg3glogG)5GS22dclZo62%N{K#a}dn>z5U4C@WXA*V6*% zFhz$^(hb5DA*N!-HHeMO)|x`P+3h=<-7)M|`($Q<*<6K~yw~%2Wk03~LctT4&k-VW zth%%Hbr0(_siGduZGT#qs6^(t=?7({Blk{Pv)KvmYIgJAL#89VBs%#GHLnxr)l(XK zva?KX(C0vbej;wpMsGQL!H~SNTikKQH;oy)a7k5pj}eZ>qS-@$k*!qk3LTc zc%M{(?i!0edRSRYSHiK4SKw3TklZsA!-XMGI`T2^W?}llLxda#sP-YA=r7syBTx?} z&^(X+53SO(GzS5YhlsYb_Gt;51kISjV$A$}7Pus~x}h^Lx$+gn0o2&LO?@vxo1GSZ zIB65d1swnKJ5UU5Ke|wgePj3W9_31>{160e{GPGX&EdX)Qvl=sq#Tm5am;V-r}7zI z^GI#jPiODlJ9hOt^5pQ=!?9bMLNdXp>MqF0mYI##;m>C$Bq?s$;u*V z4|uoGvrDdrEe*W%+?pTIjt@o|0(12kudWSvc1!*`Jp%ULvzZd zlNl%L6ziQ=25)E@6HR_MR|*5yuGnE_jL*0VuC^_pMH29Jo?J$!;Sf;RbUt33n3MN) z68!;??ejAL=gOqu4SCiE8OtjM2(ZRcsrpo2xej`0@KyP3#U@;0pS6AvM*jAlNjcPE zskH1^4*HJC@q0iIPN}y2iEkZ@6e{`B=d?jTX^d~fn+1?-jK_RT`pTAQiwUSm>rv5T zUWCnvOXx+!`-yN>(buUGYd-2d`E9>9?bv17LJ6B%ey4P7JLcaB{ykfN)_U(*F@K<7 z=R3jjR)_P?9e;oS=ihXk_Tz6q&Nu})9Y@f?akjTeGDz;kXi%6zbXGdh;?oQ`V0t3e z;APpMQ>X7{zyUeK$2F}fzo&0@4G#mgpF>iEl^pcvlXNFdev_9OU0RkW7$8l8M{x2e z^U*a9L1`n3F67tPPp$VI9ef*~)Gn!cA==qZBSo5cFE0O(HOfO#y+-szq)uwB#6Ywi z1GYia3DVCyr-QMc0;CNlozG8st^+sMY>-xAs2s26{TSHh+FUpNitVZNo;o@2<>scZ zroP?Vpl;ovyNQisz_s~VNYa?i=FsU?$em}pt z2ixVx9Nryi43DWh(we#~jw1A`VZq?bfI0@OW8p~Gy6N}&Fn9kqZQ<)E>jrd=3uD_A zK$5o`NUA=cnqn;yu+b9>oucsVVT1kaV~EA+_2+zlW~_-9l9NGTpTg}fo

    (c27!hhk4S-)la?P35~lCALj~%Fmzc+X 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ⅇ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�G!k}A0sH*Z#sY8 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

    ^}_!Bf5 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∨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� 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

    ^f5Wf20;qGeT;E0s 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≥L&=|P)u@;RmhBk9XFDLElKl)Ej6 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�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 B99aMW literal 0 HcmV?d00001 diff --git a/contrib/macOS/pkg-scripts/postinstall b/contrib/macOS/pkg-scripts/postinstall new file mode 100755 index 000000000000..647dae71b7c1 --- /dev/null +++ b/contrib/macOS/pkg-scripts/postinstall @@ -0,0 +1,51 @@ +#!/usr/bin/perl + +$0 =~ m/.*\/(.*)$/; +my $stage = $1; +my $action = $ARGV[3]; +my $wd = `pwd`; +chomp($wd); +my $actionsDir = $wd . "/" . 'postinstall' . "_actions/"; +my $upgradeDir = $wd . "/" . 'postupgrade' . "_actions/"; +my $upgrade = 0; +my $debug = 1; + +if ( $0 =~ /postupgrade/ || $0 =~ /preupgrade/ || $action =~ /upgrade/) { + system("logger -p install.info 'Upgrade scripts will be run.'") if ( $debug ); + $upgrade = 1; +} + +if ( $stage =~ /pre/ ) +{ + $actionsDir = $wd . "/" . 'preinstall' . "_actions/"; + $upgradeDir = $wd . "/" . 'preupgrade' . "_actions/"; +} + +if ( $upgrade == 1 && -e $upgradeDir ) { + system("logger -p install.info 'Running Upgrade Scripts . . .'") if ( $debug ); + $count = 0; + foreach my $action (`/bin/ls \"$upgradeDir\"`) { + chomp($action); + system("logger -p install.info 'Begin script: $action'") if ( $debug ); + system($upgradeDir . $action, $ARGV[0], $ARGV[1], $ARGV[2], $ARGV[3]) == 0 or die; + system("logger -p install.info 'End script: $action'") if ( $debug ); + $count++; + } + system("logger -p install.info '$count Upgrade Scripts run.'") if ( $debug ); +} + +if ( -e $actionsDir ) { + system("logger -p install.info 'Running Install Scripts . . .'") if ( $debug ); + $count = 0; + foreach my $action (`/bin/ls \"$actionsDir\"`) { + chomp($action); + system("logger -p install.info 'Begin script: $action'") if ( $debug ); + system($actionsDir . $action, $ARGV[0], $ARGV[1], $ARGV[2], $ARGV[3],$upgrade) == 0 or die; + system("logger -p install.info 'End script: $action'") if ( $debug ); + $count++; + } + system("logger -p install.info '$count Install Scripts run.'") if ( $debug ); +} + +exit(0); + diff --git a/contrib/macOS/pkg-scripts/postinstall_actions/kextspost b/contrib/macOS/pkg-scripts/postinstall_actions/kextspost new file mode 100755 index 000000000000..20c633252be7 --- /dev/null +++ b/contrib/macOS/pkg-scripts/postinstall_actions/kextspost @@ -0,0 +1,44 @@ +#!/bin/sh +date '+%s' >> /tmp/o3x.log +echo $0 >> /tmp/o3x.log + +if [ "$(uname -r | awk -F '.' '{print $1;}')" -lt 13 ] ; then + kernelextensionsdir=$3System/Library/Extensions +else + kernelextensionsdir=$3Library/Extensions + if [ ! -d $3Library/Extensions/zfs.kext/ ] ; then + kernelextensionsdir=$3System/Library/Extensions + fi +fi + +if [ $(/usr/sbin/kextstat -b org.openzfsonosx.zfs | wc -l) -gt 1 ] ; then + printf "\nUnloading zfs.kext...\n" + /sbin/kextunload -b org.openzfsonosx.zfs +fi +if [ $(/usr/sbin/kextstat -b net.lundman.zfs | wc -l) -gt 1 ] ; then + printf "\nUnloading zfs.kext...\n" + /sbin/kextunload -b net.lundman.zfs +fi +if [ $(/usr/sbin/kextstat -b net.lundman.spl | wc -l) -gt 1 ] ; then + printf "\nUnloading spl.kext...\n" + /sbin/kextunload -b net.lundman.spl +fi + +printf "\nTouching %s\n" "${kernelextensionsdir}" +/usr/bin/touch "${kernelextensionsdir}" + +# HUP kextd +/usr/bin/killall -HUP kextd + +# Load kext +printf "\nLoading spl.kext...\n" +/usr/bin/kextutil "${kernelextensionsdir}/zfs.kext" 2>&1 | grep -q "rejected due to system policy" + +if [[ $? -eq 0 ]]; then + open /System/Library/PreferencePanes/Security.prefPane +else + printf "\nLoading zfs.kext...\n" + /sbin/kextload "${kernelextensionsdir}/zfs.kext" +fi + +exit 0 diff --git a/contrib/macOS/pkg-scripts/postinstall_actions/loadplists b/contrib/macOS/pkg-scripts/postinstall_actions/loadplists new file mode 100755 index 000000000000..85cd15f19080 --- /dev/null +++ b/contrib/macOS/pkg-scripts/postinstall_actions/loadplists @@ -0,0 +1,29 @@ +#!/bin/bash +date '+%s' >> /tmp/o3x.log +echo $0 >> /tmp/o3x.log + +launchctl list | grep org.openzfsonosx.zconfigd 1>/dev/null +[ $? -eq 0 ] && /bin/launchctl remove org.openzfsonosx.zconfigd + +/bin/launchctl load -w /Library/LaunchDaemons/org.openzfsonosx.zconfigd.plist + +launchctl list | grep org.openzfsonosx.zed.service 1>/dev/null +[ $? -eq 0 ] && /bin/launchctl remove org.openzfsonosx.zed.service + +launchctl list | grep org.openzfsonosx.zed 1>/dev/null +[ $? -eq 0 ] && /bin/launchctl remove org.openzfsonosx.zed + +/bin/launchctl load -w /Library/LaunchDaemons/org.openzfsonosx.zed.plist + +launchctl list | grep org.openzfsonosx.InvariantDisks 1>/dev/null +[ $? -eq 0 ] && /bin/launchctl remove org.openzfsonosx.InvariantDisks + +/bin/launchctl load -w /Library/LaunchDaemons/org.openzfsonosx.InvariantDisks.plist + +launchctl list | grep org.openzfsonosx.zpool-autoimport 1>/dev/null +[ $? -eq 0 ] && /bin/launchctl remove org.openzfsonosx.zpool-autoimport + +launchctl list | grep org.openzfsonosx.zpool-import-all 1>/dev/null +[ $? -eq 0 ] && /bin/launchctl remove org.openzfsonosx.zpool-import-all + +/bin/launchctl load -w /Library/LaunchDaemons/org.openzfsonosx.zpool-import-all.plist diff --git a/contrib/macOS/pkg-scripts/postinstall_actions/startzed b/contrib/macOS/pkg-scripts/postinstall_actions/startzed new file mode 100755 index 000000000000..a17445241389 --- /dev/null +++ b/contrib/macOS/pkg-scripts/postinstall_actions/startzed @@ -0,0 +1,20 @@ +#!/bin/bash + +set +e +pgrep zed 1>/dev/null +pgrepret=$? +set -e + +if [ $pgrepret -ne 0 ] ; then + echo "zed not started" + if [ -f /usr/sbin/zed ] ; then + echo "Starting zed ..." + /usr/sbin/zed + else + echo "zed binary /usr/sbin/zed not found" + fi +else + echo "zed already running" +fi + +exit 0 diff --git a/contrib/macOS/pkg-scripts/preinstall b/contrib/macOS/pkg-scripts/preinstall new file mode 100755 index 000000000000..647dae71b7c1 --- /dev/null +++ b/contrib/macOS/pkg-scripts/preinstall @@ -0,0 +1,51 @@ +#!/usr/bin/perl + +$0 =~ m/.*\/(.*)$/; +my $stage = $1; +my $action = $ARGV[3]; +my $wd = `pwd`; +chomp($wd); +my $actionsDir = $wd . "/" . 'postinstall' . "_actions/"; +my $upgradeDir = $wd . "/" . 'postupgrade' . "_actions/"; +my $upgrade = 0; +my $debug = 1; + +if ( $0 =~ /postupgrade/ || $0 =~ /preupgrade/ || $action =~ /upgrade/) { + system("logger -p install.info 'Upgrade scripts will be run.'") if ( $debug ); + $upgrade = 1; +} + +if ( $stage =~ /pre/ ) +{ + $actionsDir = $wd . "/" . 'preinstall' . "_actions/"; + $upgradeDir = $wd . "/" . 'preupgrade' . "_actions/"; +} + +if ( $upgrade == 1 && -e $upgradeDir ) { + system("logger -p install.info 'Running Upgrade Scripts . . .'") if ( $debug ); + $count = 0; + foreach my $action (`/bin/ls \"$upgradeDir\"`) { + chomp($action); + system("logger -p install.info 'Begin script: $action'") if ( $debug ); + system($upgradeDir . $action, $ARGV[0], $ARGV[1], $ARGV[2], $ARGV[3]) == 0 or die; + system("logger -p install.info 'End script: $action'") if ( $debug ); + $count++; + } + system("logger -p install.info '$count Upgrade Scripts run.'") if ( $debug ); +} + +if ( -e $actionsDir ) { + system("logger -p install.info 'Running Install Scripts . . .'") if ( $debug ); + $count = 0; + foreach my $action (`/bin/ls \"$actionsDir\"`) { + chomp($action); + system("logger -p install.info 'Begin script: $action'") if ( $debug ); + system($actionsDir . $action, $ARGV[0], $ARGV[1], $ARGV[2], $ARGV[3],$upgrade) == 0 or die; + system("logger -p install.info 'End script: $action'") if ( $debug ); + $count++; + } + system("logger -p install.info '$count Install Scripts run.'") if ( $debug ); +} + +exit(0); + diff --git a/contrib/macOS/pkg-scripts/preinstall_actions/0uninstall b/contrib/macOS/pkg-scripts/preinstall_actions/0uninstall new file mode 100755 index 000000000000..4019ab91e0e9 --- /dev/null +++ b/contrib/macOS/pkg-scripts/preinstall_actions/0uninstall @@ -0,0 +1,236 @@ +#!/bin/bash +set +e +date '+%s' >> /tmp/o3x.log +echo $0 >> /tmp/o3x.log +pkgutil --pkgs | egrep 'net.lundman.openzfs|org.openzfsonosx.zfs' |\ +while read apkg +do + for i in {1..2} + do + pkgutil --lsbom "$apkg" | sed -E 's/^\.//' |\ + while read e + do + if [ -f "$e" ] ; then + rm "$e" + elif [ -d "$e" ] ; then + rmdir -p "$e" + elif [ -L "$e" ] ; then + rm "$e" + fi + done + done + pkgutil --forget "$apkg" +done + +rm -rfv /Library/OpenZFSonOSX +rm -rfv /Library/Extensions/spl.kext +rm -rfv /System/Library/Extensions/spl.kext +rm -rfv /usr/src/spl-* +rm -rfv /Library/Extensions/zfs.kext +rm -rfv /System/Library/Extensions/zfs.kext +rm -rfv /usr/src/zfs-* +rm -rfv /usr/lib/modules-load.d +rm -rfv /usr/lib/systemd +rm -rfv /usr/local/etc/init.d +rm -rfv /etc/init.d +rm -rfv /usr/local/etc/zfs +rm -rfv /usr/local/include/libspl +rm -rfv /usr/local/include/libzfs +rm -rfv /usr/include/libspl +rm -rfv /usr/include/libzfs +rm -rfv /usr/local/lib/dracut +rm -rfv /usr/lib/dracut +rm -rfv /usr/local/lib/udev +rm -rfv /usr/lib/udev +rm -rfv /usr/local/share/zfs +rm -rfv /usr/share/zfs +rm -rfv /usr/local/libexec/zfs +rm -rfv /usr/libexec/zfs + +rm -rfv /usr/local/zfs + +rm -fv /usr/local/share/man/man1/zhack.1 +rm -fv /usr/local/share/man/man1/zpios.1 +rm -fv /usr/local/share/man/man1/ztest.1 +rm -fv /usr/local/share/man/man5/zfs-events.5 +rm -fv /usr/local/share/man/man5/vdev_id.conf.5 +rm -fv /usr/local/share/man/man5/zfs-module-parameters.5 +rm -fv /usr/local/share/man/man5/zpool-features.5 +rm -fv /usr/local/share/man/man8/fsck.zfs.8 +rm -fv /usr/local/share/man/man8/mount.zfs.8 +rm -fv /usr/local/share/man/man8/vdev_id.8 +rm -fv /usr/local/share/man/man8/zdb.8 +rm -fv /usr/local/share/man/man8/zed.8 +rm -fv /usr/local/share/man/man8/zfs.8 +rm -fv /usr/local/share/man/man8/zinject.8 +rm -fv /usr/local/share/man/man8/zpool.8 +rm -fv /usr/local/share/man/man8/zstreamdump.8 +rm -fv /usr/share/man/man1/zhack.1 +rm -fv /usr/share/man/man1/zpios.1 +rm -fv /usr/share/man/man1/ztest.1 +rm -fv /usr/share/man/man5/vdev_id.conf.5 +rm -fv /usr/share/man/man5/zfs-events.5 +rm -fv /usr/share/man/man5/zfs-module-parameters.5 +rm -fv /usr/share/man/man5/zpool-features.5 +rm -fv /usr/share/man/man8/fsck.zfs.8 +rm -fv /usr/share/man/man8/mount.zfs.8 +rm -fv /usr/share/man/man8/vdev_id.8 +rm -fv /usr/share/man/man8/zdb.8 +rm -fv /usr/share/man/man8/zed.8 +rm -fv /usr/share/man/man8/zfs.8 +rm -fv /usr/share/man/man8/zinject.8 +rm -fv /usr/share/man/man8/zpool.8 +rm -fv /usr/share/man/man8/zstreamdump.8 + +rm -fv /usr/local/lib/libdiskmgt.1.dylib +rm -fv /usr/local/lib/libdiskmgt.a +rm -fv /usr/local/lib/libdiskmgt.dylib +rm -fv /usr/local/lib/libdiskmgt.la +rm -fv /usr/local/lib/libnvpair.1.dylib +rm -fv /usr/local/lib/libnvpair.a +rm -fv /usr/local/lib/libnvpair.dylib +rm -fv /usr/local/lib/libnvpair.la +rm -fv /usr/local/lib/libuutil.1.dylib +rm -fv /usr/local/lib/libuutil.a +rm -fv /usr/local/lib/libuutil.dylib +rm -fv /usr/local/lib/libuutil.la +rm -fv /usr/local/lib/libzfs.2.dylib +rm -fv /usr/local/lib/libzfs.a +rm -fv /usr/local/lib/libzfs.dylib +rm -fv /usr/local/lib/libzfs.la +rm -fv /usr/local/lib/libzfs_core.1.dylib +rm -fv /usr/local/lib/libzfs_core.a +rm -fv /usr/local/lib/libzfs_core.dylib +rm -fv /usr/local/lib/libzfs_core.la +rm -fv /usr/local/lib/libzpool.1.dylib +rm -fv /usr/local/lib/libzpool.a +rm -fv /usr/local/lib/libzpool.dylib +rm -fv /usr/local/lib/libzpool.la +rm -fv /usr/lib/libdiskmgt.1.dylib +rm -fv /usr/lib/libdiskmgt.a +rm -fv /usr/lib/libdiskmgt.dylib +rm -fv /usr/lib/libdiskmgt.la +rm -fv /usr/lib/libnvpair.1.dylib +rm -fv /usr/lib/libnvpair.a +rm -fv /usr/lib/libnvpair.dylib +rm -fv /usr/lib/libnvpair.la +rm -fv /usr/lib/libuutil.1.dylib +rm -fv /usr/lib/libuutil.a +rm -fv /usr/lib/libuutil.dylib +rm -fv /usr/lib/libuutil.la +rm -fv /usr/lib/libzfs.2.dylib +rm -fv /usr/lib/libzfs.a +rm -fv /usr/lib/libzfs.dylib +rm -fv /usr/lib/libzfs.la +rm -fv /usr/lib/libzfs_core.1.dylib +rm -fv /usr/lib/libzfs_core.a +rm -fv /usr/lib/libzfs_core.dylib +rm -fv /usr/lib/libzfs_core.la +rm -fv /usr/lib/libzpool.1.dylib +rm -fv /usr/lib/libzpool.a +rm -fv /usr/lib/libzpool.dylib +rm -fv /usr/lib/libzpool.la + +rm -fv /usr/local/bin/arcstat.pl +rm -fv /usr/local/bin/InvariantDisks +rm -fv /usr/local/bin/zconfigd +rm -fv /usr/local/bin/zdb +rm -fv /usr/local/bin/zdb_static +rm -fv /usr/local/bin/zed +rm -fv /usr/local/bin/zfs +rm -fv /usr/local/bin/zhack +rm -fv /usr/local/bin/zinject +rm -fv /usr/local/bin/zpios +rm -fv /usr/local/bin/zpool +rm -fv /usr/local/bin/zstreamdump +rm -fv /usr/local/bin/zsysctl +rm -fv /usr/local/bin/ztest +rm -fv /usr/local/bin/ztest_static +rm -fv /usr/local/sbin/InvariantDisks +rm -fv /usr/local/sbin/zdb +rm -fv /usr/local/sbin/zdb_static +rm -fv /usr/local/sbin/zed +rm -fv /usr/local/sbin/zfs +rm -fv /usr/local/sbin/zhack +rm -fv /usr/local/sbin/zinject +rm -fv /usr/local/sbin/zpios +rm -fv /usr/local/sbin/zpool +rm -fv /usr/local/sbin/zstreamdump +rm -fv /usr/local/sbin/ztest +rm -fv /usr/local/sbin/ztest_static +rm -fv /usr/bin/arcstat.pl +rm -fv /usr/sbin/InvariantDisks +rm -fv /usr/sbin/zconfigd +rm -fv /usr/sbin/zdb +rm -fv /usr/sbin/zdb_static +rm -fv /usr/sbin/zed +rm -fv /usr/sbin/zfs +rm -fv /usr/sbin/zhack +rm -fv /usr/sbin/zinject +rm -fv /usr/sbin/zpios +rm -fv /usr/sbin/zpool +rm -fv /usr/sbin/zstreamdump +rm -fv /usr/sbin/zsysctl +rm -fv /usr/sbin/ztest +rm -fv /usr/sbin/ztest_static + +rm -fv /sbin/mount.zfs +rm -fv /sbin/mount_zfs +rm -fv /sbin/umount_zfs +rm -fv /usr/local/bin/mount.zfs +rm -fv /usr/local/bin/mount_zfs +rm -fv /usr/local/bin/umount_zfs +rm -fv /usr/local/sbin/mount.zfs +rm -fv /usr/local/sbin/mount_zfs +rm -fv /usr/local/sbin/umount_zfs + +rm -fv /etc/zfs/zpool.cache +rm -fv /etc/zfs/zpool.cache.tmp + +rm -fv /etc/zfs/vdev_id.conf.alias.example +rm -fv /etc/zfs/vdev_id.conf.multipath.example +rm -fv /etc/zfs/vdev_id.conf.sas_direct.example +rm -fv /etc/zfs/vdev_id.conf.sas_switch.example +rm -fv /etc/zfs/zsysctl.conf.example + +rm -fv /etc/zfs/zed.d/checksum-email.sh +rm -fv /etc/zfs/zed.d/data-email.sh +rm -fv /etc/zfs/zed.d/generic-email.sh +rm -fv /etc/zfs/zed.d/io-email.sh +rm -fv /etc/zfs/zed.d/resilver.finish-email.sh +rm -fv /etc/zfs/zed.d/scrub.finish-email.sh + +rm -fv /etc/zfs/zed.d/all-debug.sh +rm -fv /etc/zfs/zed.d/all-syslog.sh +rm -fv /etc/zfs/zed.d/checksum-notify.sh +rm -fv /etc/zfs/zed.d/checksum-spare.sh +rm -fv /etc/zfs/zed.d/config.remove.sh +rm -fv /etc/zfs/zed.d/config.sync.sh +rm -fv /etc/zfs/zed.d/config.rename.sh +rm -fv /etc/zfs/zed.d/data-notify.sh +rm -fv /etc/zfs/zed.d/generic-notify.sh +rm -fv /etc/zfs/zed.d/io-notify.sh +rm -fv /etc/zfs/zed.d/io-spare.sh +rm -fv /etc/zfs/zed.d/resilver.finish-notify.sh +rm -fv /etc/zfs/zed.d/scrub.finish-notify.sh +rm -fv /etc/zfs/zed.d/snapshot.mount.sh +rm -fv /etc/zfs/zed.d/zed.rc +rm -fv /etc/zfs/zed.d/zpool.destroy.sh +rm -fv /etc/zfs/zed.d/zpool.import.sh +rm -fv /etc/zfs/zed.d/zvol.create.sh +rm -fv /etc/zfs/zed.d/zvol.remove.sh + +rm -fv /Library/LaunchDaemons/org.openzfsonosx.InvariantDisks.plist +rm -fv /Library/LaunchDaemons/org.openzfsonosx.zconfigd.plist +rm -fv /Library/LaunchDaemons/org.openzfsonosx.zed.plist +rm -fv /Library/LaunchDaemons/org.openzfsonosx.zed.service.plist +rm -fv /Library/LaunchDaemons/org.openzfsonosx.zpool-autoimport.plist +rm -fv /Library/LaunchDaemons/org.openzfsonosx.zpool-import-all.plist + +rm -rfv /Library/Filesystems/zfs.fs +rm -rfv /System/Library/Filesystems/zfs.fs + +[ -d /etc/zfs/zed.d ] && [ $(ls -A /etc/zfs/zed.d | wc -l) -eq 0 ] && rmdir /etc/zfs/zed.d +[ -d /etc/zfs ] && [ $(ls -A /etc/zfs | wc -l) -eq 0 ] && rmdir /etc/zfs + +exit 0 diff --git a/contrib/macOS/pkg-scripts/preinstall_actions/lookforzpool b/contrib/macOS/pkg-scripts/preinstall_actions/lookforzpool new file mode 100755 index 000000000000..3b2ed7f01c58 --- /dev/null +++ b/contrib/macOS/pkg-scripts/preinstall_actions/lookforzpool @@ -0,0 +1,34 @@ +#!/bin/bash +date '+%s' >> /tmp/o3x.log +echo $0 >> /tmp/o3x.log +set -e + +TMPF=`mktemp /private/tmp/zfsinstaller-lookforzpool.XXXXXX` + +echo "Checking for /dev/zfs" +if [ -c /dev/zfs ] ; then +echo "Found /dev/zfs" +for zpoolcommand in /usr/local/zfs/bin/zpool /Library/OpenZFSonOSX/ZFSUserland/usr/local/sbin/zpool /Library/OpenZFSonOSX/ZFSCommandLineTools/usr/sbin/zpool /usr/sbin/zpool /usr/local/sbin/zpool /sbin /usr/local/bin/zpool /usr/bin/zpool /bin/zpool zpool ; do + printf "Checking for zpool command at %s\n" "$zpoolcommand" + if [ -f "$zpoolcommand" -o -L "$zpoolcommand" ] ; then + echo "Found zpool command $zpoolcommand" + set +e + echo "Checking output of zpool status" + 2>"$TMPF" 1>"$TMPF" sudo "$zpoolcommand" status + err=$? + set -e + count=$(cat "$TMPF" | wc -l | tr -d ' ') + if [ "$count" -gt 1 ] ; then + cat "$TMPF" + if [ $err -eq 0 ] ; then + printf "First export all pools\n" + fi + exit 1 + fi + if [ "$err" -ne 0 -a "$err" -ne 1 ] ; then + echo "$err" + exit "$err" + fi + fi +done +fi diff --git a/contrib/macOS/pkg-scripts/preinstall_actions/unloadzfs b/contrib/macOS/pkg-scripts/preinstall_actions/unloadzfs new file mode 100755 index 000000000000..baca13e37d99 --- /dev/null +++ b/contrib/macOS/pkg-scripts/preinstall_actions/unloadzfs @@ -0,0 +1,42 @@ +#!/bin/bash +date '+%s' >> /tmp/o3x.log +echo $0 >> /tmp/o3x.log +set -e + +TMPF=`mktemp /private/tmp/zfsinstaller-unloadzfs.XXXXXX` + +if [ $(/usr/sbin/kextstat -b net.lundman.zfs | wc -l) -gt 1 ] ; then + echo "Unloading zfs.kext" + /sbin/kextunload -b net.lundman.zfs + if [ $(/usr/sbin/kextstat -b net.lundman.zfs | wc -l) -gt 1 ] ; then + echo "zfs.kext could not be unloaded" + exit 2 + fi +fi + +if [ $(/usr/sbin/kextstat -b org.openzfsonosx.zfs | wc -l) -gt 1 ] ; then + echo "Unloading zfs.kext" + /sbin/kextunload -b org.openzfsonosx.zfs + if [ $(/usr/sbin/kextstat -b org.openzfsonosx.zfs | wc -l) -gt 1 ] ; then + echo "zfs.kext could not be unloaded" + exit 2 + fi +fi + +if [ -c /dev/zfs ] ; then + echo "/dev/zfs still exists. zfs kernel extension likely still loaded. Exiting." + exit 3 +fi + +if [ $(/usr/sbin/kextstat -b net.lundman.spl | wc -l) -gt 1 ] ; then + /sbin/kextunload -b net.lundman.spl + if [ $(/usr/sbin/kextstat -b net.lundman.spl | wc -l) -gt 1 ] ; then + echo "spl.kext could not be unloaded" + exit 4 + fi +fi + +set +e +killall zed + +exit 0 diff --git a/contrib/macOS/product-scripts/poolcheck.sh b/contrib/macOS/product-scripts/poolcheck.sh new file mode 100755 index 000000000000..c7a989fbe87d --- /dev/null +++ b/contrib/macOS/product-scripts/poolcheck.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +#exit code 1 means no zfs file systems mounted + +echo "Mounted ZFS file system(s) check" +myvar="$(2>/dev/null /usr/bin/lsvfs zfs | /usr/bin/tail -1 | /usr/bin/awk '{print $2}')" +[ ! -z "${myvar##*[!0-9]*}" ] || exit 1 +[ $myvar -ne 0 ] && exit 0 +sysctl -n kstat.zfs.darwin.ldi.handle_count || exit 1 +if [ x"$(sysctl -n kstat.zfs.darwin.ldi.handle_count)" != x"0" ]; then exit 0; fi +exit 1 diff --git a/contrib/macOS/product-scripts/zevocheck.sh b/contrib/macOS/product-scripts/zevocheck.sh new file mode 100755 index 000000000000..78326bdb3e07 --- /dev/null +++ b/contrib/macOS/product-scripts/zevocheck.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +#exit code 1 means ZEVO is neither installed nor just uninstalled without reboot + +echo "ZEVO files check" +&>/dev/null ls /System/Library/Extensions/ZFSDriver.kext/ && exit 0 +&>/dev/null ls /System/Library/Extensions/ZFSFilesystem.kext/ && exit 0 +&>/dev/null ls /Library/LaunchDaemons/com.getgreenbytes* && exit 0 + +echo "ZEVO launchctl check" +/bin/launchctl list | grep greenbytes &>/dev/null +[ $? -eq 0 ] && exit 0 + +echo "ZEVO kextstat check" +/usr/sbin/kextstat | grep greenbytes &>/dev/null +[ $? -eq 0 ] && exit 0 + +exit 1 diff --git a/contrib/macOS/resources/English.lproj/Conclusion.rtf b/contrib/macOS/resources/English.lproj/Conclusion.rtf new file mode 100644 index 000000000000..97d834fd7120 --- /dev/null +++ b/contrib/macOS/resources/English.lproj/Conclusion.rtf @@ -0,0 +1,70 @@ +{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +{\fonttbl\f0\fswiss\fcharset0 ArialMT;} +{\colortbl;\red255\green255\blue255;} +{\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid1\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid1} +{\list\listtemplateid2\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid101\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid2} +{\list\listtemplateid3\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid201\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid3}} +{\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}{\listoverride\listid2\listoverridecount0\ls2}{\listoverride\listid3\listoverridecount0\ls3}} +\margl1440\margr1440\vieww18180\viewh10020\viewkind0 +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural + +\f0\fs24 \cf0 \CocoaLigature0 \ +J\'f6rgen Lundman is the principal developer of the OpenZFS on OS X port. He works full time as a Unix Engineer at GMO Internet, Tokyo, Japan. This port would not have been possible without the help of many others who have contributed their time, effort, and insight.\ +\ +\pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\pardirnatural +\ls1\ilvl0\cf0 \CocoaLigature1 {\listtext \'95 }\CocoaLigature0 J\'f6rgen Lundman \ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural +\cf0 \ +First and foremost the hard working ZFS developers at Sun/Oracle. They are responsible for the bulk of the code in this project and without their efforts there never would have been a ZFS filesystem.\ +\ +\pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\pardirnatural +\ls2\ilvl0\cf0 \CocoaLigature1 {\listtext \'95 }\CocoaLigature0 The ZFS Development Team at Sun/Oracle\ +\pard\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural +\cf0 \ +The OpenZFS project community brings together developers from the illumos, FreeBSD, Linux, and OS X platforms, and a wide range of companies that build products on top of OpenZFS.\ +\ +\pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\pardirnatural +\cf0 \CocoaLigature1 \'95 \CocoaLigature0 OpenZFS http://open-zfs.org/\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural +\cf0 \ +The OpenZFS on OS X port was made easier by the great work of ZFS on Linux, and more information on that project can be found below.\ +\ +\pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\pardirnatural +\ls3\ilvl0\cf0 \CocoaLigature1 {\listtext \'95 }\CocoaLigature0 ZFS on Linux by Lawrence Livermore National Laboratory\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural +\cf0 \ +Large parts come from the original work in MacZFS, and the excellent work of those early developers.\ +\ +\pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\pardirnatural +\cf0 \CocoaLigature1 \'95 \CocoaLigature0 MacZFS http://maczfs.org/\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural +\cf0 \ +VFS layer and glue comes from the FreeBSD project, who has done a excellent job connecting the BSD and SYSV kernels together.\ +\ +\pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\pardirnatural +\cf0 \CocoaLigature1 \'95 \CocoaLigature0 FreeBSD http://www.freebsd.org/\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural +\cf0 \ +Additional assistance has been given by people online, reluctant and sometimes\ +willingly,\ +\ +\pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\pardirnatural +\cf0 \CocoaLigature1 \'95 \CocoaLigature0 Richard Yao\ +\CocoaLigature1 \'95 \CocoaLigature0 Bj\'f6rn Kahl\ +\CocoaLigature1 \'95 \CocoaLigature0 Will Andrews\ +\CocoaLigature1 \'95 \CocoaLigature0 Evan Lojewski\ +\CocoaLigature1 \'95 \CocoaLigature0 Daniel Bethe\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural +\cf0 \ +The flexible and welcoming attitude toward Open Source by GMO Internet, Tokyo, Japan has been an integral part of this project.\ +\ +\pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\pardirnatural +\cf0 \CocoaLigature1 \'95 \CocoaLigature0 GMO Internet, Inc. http://www.gmo.jp/\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural +\cf0 \ +The book [OS X and iOS Kernel Programming] has been useful when working out the internals of the newer IOKit section of the Apple OS X kernel. Available on Amazon.\ +\ +\pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\pardirnatural +\cf0 \CocoaLigature1 \'95 \CocoaLigature0 Ole Henry Halvorsen (Author) http://osxkernel.com/\ +\CocoaLigature1 \'95 \CocoaLigature0 Douglas Clarke (Author)\ +} \ No newline at end of file diff --git a/contrib/macOS/resources/English.lproj/License.txt b/contrib/macOS/resources/English.lproj/License.txt new file mode 100644 index 000000000000..b45fa15769e5 --- /dev/null +++ b/contrib/macOS/resources/English.lproj/License.txt @@ -0,0 +1,828 @@ + +OpenZFS on OS X consists of software that is covered under the +Common Development and Distribution License 1.0 and the Apple +Public Source License 2.0. + +Each file at https://github.com/openzfsonosx records which license it is +released under. + +Much of OpenZFS on OS X is a port of ZFS on Linux. + +-------------------------------------------------------------------- +ZFS on Linux COPYRIGHT + +The majority of the code in the ZFS on Linux port comes from +OpenSolaris which has been released under the terms of the CDDL +open source license. This includes the core ZFS code, libavl, +libnvpair, libefi, libunicode, and libutil. The original OpenSolaris +source can be downloaded from: + +http://dlc.sun.com/osol/on/downloads/b121/on-src.tar.bz2 + +Files which do not originate from OpenSolaris are noted in the file +header and attributed properly. These exceptions include, but are not +limited to, the vdev_disk.c and zvol.c implementation which are +licensed under the CDDL. + +The zpios test code is originally derived from the Lustre pios test +code which is licensed under the GPLv2. As such the heavily modified +zpios kernel test code also remains licensed under the GPLv2. + +The latest stable and development versions of this port can be +downloaded from the official ZFS on Linux site located at: + +http://zfsonlinux.org/ + +This ZFS on Linux port was produced at the Lawrence Livermore National +Laboratory (LLNL) under Contract No. DE-AC52-07NA27344 (Contract 44) +between the U.S. Department of Energy (DOE) and Lawrence Livermore +National Security, LLC (LLNS) for the operation of LLNL. It has been +approved for release under LLNL-CODE-403049. + +Unless otherwise noted, all files in this distribution are released +under the Common Development and Distribution License (CDDL). +Exceptions are noted within the associated source files. + +-------------------------------------------------------------------- +ZFS on Linux Disclaimer + +This work was produced at the Lawrence Livermore National Laboratory +(LLNL) under Contract No. DE-AC52-07NA27344 (Contract 44) between +the U.S. Department of Energy (DOE) and Lawrence Livermore National +Security, LLC (LLNS) for the operation of LLNL. + +This work was prepared as an account of work sponsored by an agency of +the United States Government. Neither the United States Government nor +Lawrence Livermore National Security, LLC nor any of their employees, +makes any warranty, express or implied, or assumes any liability or +responsibility for the accuracy, completeness, or usefulness of any +information, apparatus, product, or process disclosed, or represents +that its use would not infringe privately-owned rights. + +Reference herein to any specific commercial products, process, or +services by trade name, trademark, manufacturer or otherwise does +not necessarily constitute or imply its endorsement, recommendation, +or favoring by the United States Government or Lawrence Livermore +National Security, LLC. The views and opinions of authors expressed +herein do not necessarily state or reflect those of the United States +Government or Lawrence Livermore National Security, LLC, and shall +not be used for advertising or product endorsement purposes. + +The precise terms and conditions for copying, distribution, and +modification are specified in the file OPENSOLARIS.LICENSE.txt + +-------------------------------------------------------------------- +Contents of OPENSOLARIS.LICENSE: + +Unless otherwise noted, all files in this distribution are released +under the Common Development and Distribution License (CDDL). +Exceptions are noted within the associated source files. + +-------------------------------------------------------------------- + +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE Version 1.0 + +1. Definitions. + + 1.1. "Contributor" means each individual or entity that creates + or contributes to the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Software, prior Modifications used by a Contributor (if any), + and the Modifications made by that particular Contributor. + + 1.3. "Covered Software" means (a) the Original Software, or (b) + Modifications, or (c) the combination of files containing + Original Software with files containing Modifications, in + each case including portions thereof. + + 1.4. "Executable" means the Covered Software in any form other + than Source Code. + + 1.5. "Initial Developer" means the individual or entity that first + makes Original Software available under this License. + + 1.6. "Larger Work" means a work which combines Covered Software or + portions thereof with code not governed by the terms of this + License. + + 1.7. "License" means this document. + + 1.8. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed + herein. + + 1.9. "Modifications" means the Source Code and Executable form of + any of the following: + + A. Any file that results from an addition to, deletion from or + modification of the contents of a file containing Original + Software or previous Modifications; + + B. Any new file that contains any part of the Original + Software or previous Modifications; or + + C. Any new file that is contributed or otherwise made + available under the terms of this License. + + 1.10. "Original Software" means the Source Code and Executable + form of computer software code that is originally released + under this License. + + 1.11. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, + process, and apparatus claims, in any patent Licensable by + grantor. + + 1.12. "Source Code" means (a) the common form of computer software + code in which modifications are made and (b) associated + documentation included in or with such code. + + 1.13. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms + of, this License. For legal entities, "You" includes any + entity which controls, is controlled by, or is under common + control with You. For purposes of this definition, + "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty + percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants. + + 2.1. The Initial Developer Grant. + + Conditioned upon Your compliance with Section 3.1 below and + subject to third party intellectual property claims, the Initial + Developer hereby grants You a world-wide, royalty-free, + non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer, to use, + reproduce, modify, display, perform, sublicense and + distribute the Original Software (or portions thereof), + with or without Modifications, and/or as part of a Larger + Work; and + + (b) under Patent Claims infringed by the making, using or + selling of Original Software, to make, have made, use, + practice, sell, and offer for sale, and/or otherwise + dispose of the Original Software (or portions thereof). + + (c) The licenses granted in Sections 2.1(a) and (b) are + effective on the date Initial Developer first distributes + or otherwise makes the Original Software available to a + third party under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: (1) for code that You delete from the Original + Software, or (2) for infringements caused by: (i) the + modification of the Original Software, or (ii) the + combination of the Original Software with other software + or devices. + + 2.2. Contributor Grant. + + Conditioned upon Your compliance with Section 3.1 below and + subject to third party intellectual property claims, each + Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor to use, reproduce, + modify, display, perform, sublicense and distribute the + Modifications created by such Contributor (or portions + thereof), either on an unmodified basis, with other + Modifications, as Covered Software and/or as part of a + Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either + alone and/or in combination with its Contributor Version + (or portions of such combination), to make, use, sell, + offer for sale, have made, and/or otherwise dispose of: + (1) Modifications made by that Contributor (or portions + thereof); and (2) the combination of Modifications made by + that Contributor with its Contributor Version (or portions + of such combination). + + (c) The licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first distributes or + otherwise makes the Modifications available to a third + party. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: (1) for any code that Contributor has deleted + from the Contributor Version; (2) for infringements caused + by: (i) third party modifications of Contributor Version, + or (ii) the combination of Modifications made by that + Contributor with other software (except as part of the + Contributor Version) or other devices; or (3) under Patent + Claims infringed by Covered Software in the absence of + Modifications made by that Contributor. + +3. Distribution Obligations. + + 3.1. Availability of Source Code. + + Any Covered Software that You distribute or otherwise make + available in Executable form must also be made available in Source + Code form and that Source Code form must be distributed only under + the terms of this License. You must include a copy of this + License with every copy of the Source Code form of the Covered + Software You distribute or otherwise make available. You must + inform recipients of any such Covered Software in Executable form + as to how they can obtain such Covered Software in Source Code + form in a reasonable manner on or through a medium customarily + used for software exchange. + + 3.2. Modifications. + + The Modifications that You create or to which You contribute are + governed by the terms of this License. You represent that You + believe Your Modifications are Your original creation(s) and/or + You have sufficient rights to grant the rights conveyed by this + License. + + 3.3. Required Notices. + + You must include a notice in each of Your Modifications that + identifies You as the Contributor of the Modification. You may + not remove or alter any copyright, patent or trademark notices + contained within the Covered Software, or any notices of licensing + or any descriptive text giving attribution to any Contributor or + the Initial Developer. + + 3.4. Application of Additional Terms. + + You may not offer or impose any terms on any Covered Software in + Source Code form that alters or restricts the applicable version + of this License or the recipients' rights hereunder. You may + choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of + Covered Software. However, you may do so only on Your own behalf, + and not on behalf of the Initial Developer or any Contributor. + You must make it absolutely clear that any such warranty, support, + indemnity or liability obligation is offered by You alone, and You + hereby agree to indemnify the Initial Developer and every + Contributor for any liability incurred by the Initial Developer or + such Contributor as a result of warranty, support, indemnity or + liability terms You offer. + + 3.5. Distribution of Executable Versions. + + You may distribute the Executable form of the Covered Software + under the terms of this License or under the terms of a license of + Your choice, which may contain terms different from this License, + provided that You are in compliance with the terms of this License + and that the license for the Executable form does not attempt to + limit or alter the recipient's rights in the Source Code form from + the rights set forth in this License. If You distribute the + Covered Software in Executable form under a different license, You + must make it absolutely clear that any terms which differ from + this License are offered by You alone, not by the Initial + Developer or Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred + by the Initial Developer or such Contributor as a result of any + such terms You offer. + + 3.6. Larger Works. + + You may create a Larger Work by combining Covered Software with + other code not governed by the terms of this License and + distribute the Larger Work as a single product. In such a case, + You must make sure the requirements of this License are fulfilled + for the Covered Software. + +4. Versions of the License. + + 4.1. New Versions. + + Sun Microsystems, Inc. is the initial license steward and may + publish revised and/or new versions of this License from time to + time. Each version will be given a distinguishing version number. + Except as provided in Section 4.3, no one other than the license + steward has the right to modify this License. + + 4.2. Effect of New Versions. + + You may always continue to use, distribute or otherwise make the + Covered Software available under the terms of the version of the + License under which You originally received the Covered Software. + If the Initial Developer includes a notice in the Original + Software prohibiting it from being distributed or otherwise made + available under any subsequent version of the License, You must + distribute and make the Covered Software available under the terms + of the version of the License under which You originally received + the Covered Software. Otherwise, You may also choose to use, + distribute or otherwise make the Covered Software available under + the terms of any subsequent version of the License published by + the license steward. + + 4.3. Modified Versions. + + When You are an Initial Developer and You want to create a new + license for Your Original Software, You may create and use a + modified version of this License if You: (a) rename the license + and remove any references to the name of the license steward + (except to note that the license differs from this License); and + (b) otherwise make it clear that the license contains terms which + differ from this License. + +5. DISCLAIMER OF WARRANTY. + + COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" + BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED + SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR + PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND + PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY + COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE + INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY + NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF + WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS + DISCLAIMER. + +6. TERMINATION. + + 6.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to + cure such breach within 30 days of becoming aware of the breach. + Provisions which, by their nature, must remain in effect beyond + the termination of this License shall survive. + + 6.2. If You assert a patent infringement claim (excluding + declaratory judgment actions) against Initial Developer or a + Contributor (the Initial Developer or Contributor against whom You + assert such claim is referred to as "Participant") alleging that + the Participant Software (meaning the Contributor Version where + the Participant is a Contributor or the Original Software where + the Participant is the Initial Developer) directly or indirectly + infringes any patent, then any and all rights granted directly or + indirectly to You by such Participant, the Initial Developer (if + the Initial Developer is not the Participant) and all Contributors + under Sections 2.1 and/or 2.2 of this License shall, upon 60 days + notice from Participant terminate prospectively and automatically + at the expiration of such 60 day notice period, unless if within + such 60 day period You withdraw Your claim with respect to the + Participant Software against such Participant either unilaterally + or pursuant to a written agreement with Participant. + + 6.3. In the event of termination under Sections 6.1 or 6.2 above, + all end user licenses that have been validly granted by You or any + distributor hereunder prior to termination (excluding licenses + granted to You by any distributor) shall survive termination. + +7. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE + INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF + COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE + LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR + CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT + LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK + STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL + INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT + APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO + NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR + CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT + APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + + The Covered Software is a "commercial item," as that term is + defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial + computer software" (as that term is defined at 48 + C.F.R. 252.227-7014(a)(1)) and "commercial computer software + documentation" as such terms are used in 48 C.F.R. 12.212 + (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 + C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all + U.S. Government End Users acquire Covered Software with only those + rights set forth herein. This U.S. Government Rights clause is in + lieu of, and supersedes, any other FAR, DFAR, or other clause or + provision that addresses Government rights in computer software + under this License. + +9. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed + by the law of the jurisdiction specified in a notice contained + within the Original Software (except to the extent applicable law, + if any, provides otherwise), excluding such jurisdiction's + conflict-of-law provisions. Any litigation relating to this + License shall be subject to the jurisdiction of the courts located + in the jurisdiction and venue specified in a notice contained + within the Original Software, with the losing party responsible + for costs, including, without limitation, court costs and + reasonable attorneys' fees and expenses. The application of the + United Nations Convention on Contracts for the International Sale + of Goods is expressly excluded. Any law or regulation which + provides that the language of a contract shall be construed + against the drafter shall not apply to this License. You agree + that You alone are responsible for compliance with the United + States export administration regulations (and the export control + laws and regulation of any other countries) when You use, + distribute or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or + indirectly, out of its utilization of rights under this License + and You agree to work with Initial Developer and Contributors to + distribute such responsibility on an equitable basis. Nothing + herein is intended or shall be deemed to constitute any admission + of liability. + +-------------------------------------------------------------------- + +NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND +DISTRIBUTION LICENSE (CDDL) + +For Covered Software in this distribution, this License shall +be governed by the laws of the State of California (excluding +conflict-of-law provisions). + +Any litigation relating to this License shall be subject to the +jurisdiction of the Federal Courts of the Northern District of +California and the state courts of the State of California, with +venue lying in Santa Clara County, California. + +-------------------------------------------------------------------- +APPLE PUBLIC SOURCE LICENSE +Version 2.0 - August 6, 2003 + +Please read this License carefully before downloading this software. +By downloading or using this software, you are agreeing to be bound by +the terms of this License. If you do not or cannot agree to the terms +of this License, please do not download or use the software. + +1. General; Definitions. This License applies to any program or other +work which Apple Computer, Inc. ("Apple") makes publicly available and +which contains a notice placed by Apple identifying such program or +work as "Original Code" and stating that it is subject to the terms of +this Apple Public Source License version 2.0 ("License"). As used in +this License: + +1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is +the grantor of rights, (i) claims of patents that are now or hereafter +acquired, owned by or assigned to Apple and (ii) that cover subject +matter contained in the Original Code, but only to the extent +necessary to use, reproduce and/or distribute the Original Code +without infringement; and (b) in the case where You are the grantor of +rights, (i) claims of patents that are now or hereafter acquired, +owned by or assigned to You and (ii) that cover subject matter in Your +Modifications, taken alone or in combination with Original Code. + +1.2 "Contributor" means any person or entity that creates or +contributes to the creation of Modifications. + +1.3 "Covered Code" means the Original Code, Modifications, the +combination of Original Code and any Modifications, and/or any +respective portions thereof. + +1.4 "Externally Deploy" means: (a) to sublicense, distribute or +otherwise make Covered Code available, directly or indirectly, to +anyone other than You; and/or (b) to use Covered Code, alone or as +part of a Larger Work, in any way to provide a service, including but +not limited to delivery of content, through electronic communication +with a client other than You. + +1.5 "Larger Work" means a work which combines Covered Code or portions +thereof with code not governed by the terms of this License. + +1.6 "Modifications" mean any addition to, deletion from, and/or change +to, the substance and/or structure of the Original Code, any previous +Modifications, the combination of Original Code and any previous +Modifications, and/or any respective portions thereof. When code is +released as a series of files, a Modification is: (a) any addition to +or deletion from the contents of a file containing Covered Code; +and/or (b) any new file or other representation of computer program +statements that contains any part of Covered Code. + +1.7 "Original Code" means (a) the Source Code of a program or other +work as originally made available by Apple under this License, +including the Source Code of any updates or upgrades to such programs +or works made available by Apple under this License, and that has been +expressly identified by Apple as such in the header file(s) of such +work; and (b) the object code compiled from such Source Code and +originally made available by Apple under this License. + +1.8 "Source Code" means the human readable form of a program or other +work that is suitable for making modifications to it, including all +modules it contains, plus any associated interface definition files, +scripts used to control compilation and installation of an executable +(object code). + +1.9 "You" or "Your" means an individual or a legal entity exercising +rights under this License. For legal entities, "You" or "Your" +includes any entity which controls, is controlled by, or is under +common control with, You, where "control" means (a) the power, direct +or indirect, to cause the direction or management of such entity, +whether by contract or otherwise, or (b) ownership of fifty percent +(50%) or more of the outstanding shares or beneficial ownership of +such entity. + +2. Permitted Uses; Conditions & Restrictions. Subject to the terms +and conditions of this License, Apple hereby grants You, effective on +the date You accept this License and download the Original Code, a +world-wide, royalty-free, non-exclusive license, to the extent of +Apple's Applicable Patent Rights and copyrights covering the Original +Code, to do the following: + +2.1 Unmodified Code. You may use, reproduce, display, perform, +internally distribute within Your organization, and Externally Deploy +verbatim, unmodified copies of the Original Code, for commercial or +non-commercial purposes, provided that in each instance: + +(a) You must retain and reproduce in all copies of Original Code the +copyright and other proprietary notices and disclaimers of Apple as +they appear in the Original Code, and keep intact all notices in the +Original Code that refer to this License; and + +(b) You must include a copy of this License with every copy of Source +Code of Covered Code and documentation You distribute or Externally +Deploy, and You may not offer or impose any terms on such Source Code +that alter or restrict this License or the recipients' rights +hereunder, except as permitted under Section 6. + +2.2 Modified Code. You may modify Covered Code and use, reproduce, +display, perform, internally distribute within Your organization, and +Externally Deploy Your Modifications and Covered Code, for commercial +or non-commercial purposes, provided that in each instance You also +meet all of these conditions: + +(a) You must satisfy all the conditions of Section 2.1 with respect to +the Source Code of the Covered Code; + +(b) You must duplicate, to the extent it does not already exist, the +notice in Exhibit A in each file of the Source Code of all Your +Modifications, and cause the modified files to carry prominent notices +stating that You changed the files and the date of any change; and + +(c) If You Externally Deploy Your Modifications, You must make +Source Code of all Your Externally Deployed Modifications either +available to those to whom You have Externally Deployed Your +Modifications, or publicly available. Source Code of Your Externally +Deployed Modifications must be released under the terms set forth in +this License, including the license grants set forth in Section 3 +below, for as long as you Externally Deploy the Covered Code or twelve +(12) months from the date of initial External Deployment, whichever is +longer. You should preferably distribute the Source Code of Your +Externally Deployed Modifications electronically (e.g. download from a +web site). + +2.3 Distribution of Executable Versions. In addition, if You +Externally Deploy Covered Code (Original Code and/or Modifications) in +object code, executable form only, You must include a prominent +notice, in the code itself as well as in related documentation, +stating that Source Code of the Covered Code is available under the +terms of this License with information on how and where to obtain such +Source Code. + +2.4 Third Party Rights. You expressly acknowledge and agree that +although Apple and each Contributor grants the licenses to their +respective portions of the Covered Code set forth herein, no +assurances are provided by Apple or any Contributor that the Covered +Code does not infringe the patent or other intellectual property +rights of any other entity. Apple and each Contributor disclaim any +liability to You for claims brought by any other entity based on +infringement of intellectual property rights or otherwise. As a +condition to exercising the rights and licenses granted hereunder, You +hereby assume sole responsibility to secure any other intellectual +property rights needed, if any. For example, if a third party patent +license is required to allow You to distribute the Covered Code, it is +Your responsibility to acquire that license before distributing the +Covered Code. + +3. Your Grants. In consideration of, and as a condition to, the +licenses granted to You under this License, You hereby grant to any +person or entity receiving or distributing Covered Code under this +License a non-exclusive, royalty-free, perpetual, irrevocable license, +under Your Applicable Patent Rights and other intellectual property +rights (other than patent) owned or controlled by You, to use, +reproduce, display, perform, modify, sublicense, distribute and +Externally Deploy Your Modifications of the same scope and extent as +Apple's licenses under Sections 2.1 and 2.2 above. + +4. Larger Works. You may create a Larger Work by combining Covered +Code with other code not governed by the terms of this License and +distribute the Larger Work as a single product. In each such instance, +You must make sure the requirements of this License are fulfilled for +the Covered Code or any portion thereof. + +5. Limitations on Patent License. Except as expressly stated in +Section 2, no other patent rights, express or implied, are granted by +Apple herein. Modifications and/or Larger Works may require additional +patent licenses from Apple which Apple may grant in its sole +discretion. + +6. Additional Terms. You may choose to offer, and to charge a fee for, +warranty, support, indemnity or liability obligations and/or other +rights consistent with the scope of the license granted herein +("Additional Terms") to one or more recipients of Covered Code. +However, You may do so only on Your own behalf and as Your sole +responsibility, and not on behalf of Apple or any Contributor. You +must obtain the recipient's agreement that any such Additional Terms +are offered by You alone, and You hereby agree to indemnify, defend +and hold Apple and every Contributor harmless for any liability +incurred by or claims asserted against Apple or such Contributor by +reason of any such Additional Terms. + +7. Versions of the License. Apple may publish revised and/or new +versions of this License from time to time. Each version will be given +a distinguishing version number. Once Original Code has been published +under a particular version of this License, You may continue to use it +under the terms of that version. You may also choose to use such +Original Code under the terms of any subsequent version of this +License published by Apple. No one other than Apple has the right to +modify the terms applicable to Covered Code created under this +License. + +8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in +part pre-release, untested, or not fully tested works. The Covered +Code may contain errors that could cause failures or loss of data, and +may be incomplete or contain inaccuracies. You expressly acknowledge +and agree that use of the Covered Code, or any portion thereof, is at +Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND +WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND +APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE +PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM +ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF +MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR +PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD +PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST +INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE +FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, +THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR +ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO +ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE +AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. +You acknowledge that the Covered Code is not intended for use in the +operation of nuclear facilities, aircraft navigation, communication +systems, or air traffic control machines in which case the failure of +the Covered Code could lead to death, personal injury, or severe +physical or environmental damage. + +9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO +EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING +TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR +ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, +TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF +APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY +REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF +INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY +TO YOU. In no event shall Apple's total liability to You for all +damages (other than as may be required by applicable law) under this +License exceed the amount of fifty dollars ($50.00). + +10. Trademarks. This License does not grant any rights to use the +trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS", +"QuickTime", "QuickTime Streaming Server" or any other trademarks, +service marks, logos or trade names belonging to Apple (collectively +"Apple Marks") or to any trademark, service mark, logo or trade name +belonging to any Contributor. You agree not to use any Apple Marks in +or as part of the name of products derived from the Original Code or +to endorse or promote products derived from the Original Code other +than as expressly permitted by and in strict compliance at all times +with Apple's third party trademark usage guidelines which are posted +at http://www.apple.com/legal/guidelinesfor3rdparties.html. + +11. Ownership. Subject to the licenses granted under this License, +each Contributor retains all rights, title and interest in and to any +Modifications made by such Contributor. Apple retains all rights, +title and interest in and to the Original Code and any Modifications +made by or on behalf of Apple ("Apple Modifications"), and such Apple +Modifications will not be automatically subject to this License. Apple +may, at its sole discretion, choose to license such Apple +Modifications under this License, or on different terms from those +contained in this License or may choose not to license them at all. + +12. Termination. + +12.1 Termination. This License and the rights granted hereunder will +terminate: + +(a) automatically without notice from Apple if You fail to comply with +any term(s) of this License and fail to cure such breach within 30 +days of becoming aware of such breach; + +(b) immediately in the event of the circumstances described in Section +13.5(b); or + +(c) automatically without notice from Apple if You, at any time during +the term of this License, commence an action for patent infringement +against Apple; provided that Apple did not first commence +an action for patent infringement against You in that instance. + +12.2 Effect of Termination. Upon termination, You agree to immediately +stop any further use, reproduction, modification, sublicensing and +distribution of the Covered Code. All sublicenses to the Covered Code +which have been properly granted prior to termination shall survive +any termination of this License. Provisions which, by their nature, +should remain in effect beyond the termination of this License shall +survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, +12.2 and 13. No party will be liable to any other for compensation, +indemnity or damages of any sort solely as a result of terminating +this License in accordance with its terms, and termination of this +License will be without prejudice to any other right or remedy of +any party. + +13. Miscellaneous. + +13.1 Government End Users. The Covered Code is a "commercial item" as +defined in FAR 2.101. Government software and technical data rights in +the Covered Code include only those rights customarily provided to the +public as defined in this License. This customary commercial license +in technical data and software is provided in accordance with FAR +12.211 (Technical Data) and 12.212 (Computer Software) and, for +Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- +Commercial Items) and 227.7202-3 (Rights in Commercial Computer +Software or Computer Software Documentation). Accordingly, all U.S. +Government End Users acquire Covered Code with only those rights set +forth herein. + +13.2 Relationship of Parties. This License will not be construed as +creating an agency, partnership, joint venture or any other form of +legal association between or among You, Apple or any Contributor, and +You will not represent to the contrary, whether expressly, by +implication, appearance or otherwise. + +13.3 Independent Development. Nothing in this License will impair +Apple's right to acquire, license, develop, have others develop for +it, market and/or distribute technology or products that perform the +same or similar functions as, or otherwise compete with, +Modifications, Larger Works, technology or products that You may +develop, produce, market or distribute. + +13.4 Waiver; Construction. Failure by Apple or any Contributor to +enforce any provision of this License will not be deemed a waiver of +future enforcement of that or any other provision. Any law or +regulation which provides that the language of a contract shall be +construed against the drafter will not apply to this License. + +13.5 Severability. (a) If for any reason a court of competent +jurisdiction finds any provision of this License, or portion thereof, +to be unenforceable, that provision of the License will be enforced to +the maximum extent permissible so as to effect the economic benefits +and intent of the parties, and the remainder of this License will +continue in full force and effect. (b) Notwithstanding the foregoing, +if applicable law prohibits or restricts You from fully and/or +specifically complying with Sections 2 and/or 3 or prevents the +enforceability of either of those Sections, this License will +immediately terminate and You must immediately discontinue any use of +the Covered Code and destroy all copies of it that are in your +possession or control. + +13.6 Dispute Resolution. Any litigation or other dispute resolution +between You and Apple relating to this License shall take place in the +Northern District of California, and You and Apple hereby consent to +the personal jurisdiction of, and venue in, the state and federal +courts within that District with respect to this License. The +application of the United Nations Convention on Contracts for the +International Sale of Goods is expressly excluded. + +13.7 Entire Agreement; Governing Law. This License constitutes the +entire agreement between the parties with respect to the subject +matter hereof. This License shall be governed by the laws of the +United States and the State of California, except that body of +California law concerning conflicts of law. + +Where You are located in the province of Quebec, Canada, the following +clause applies: The parties hereby confirm that they have requested +that this License and all related documents be drafted in English. Les +parties ont exige que le present contrat et tous les documents +connexes soient rediges en anglais. + +EXHIBIT A. + +"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights +Reserved. + +This file contains Original Code and/or Modifications of Original Code +as defined in and that are subject to the Apple Public Source License +Version 2.0 (the 'License'). You may not use this file except in +compliance with the License. Please obtain a copy of the License at +http://www.opensource.apple.com/apsl/ and read it before using this +file. + +The Original Code and all software distributed under the License are +distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +Please see the License for the specific language governing rights and +limitations under the License." diff --git a/contrib/macOS/resources/English.lproj/Localizable.strings b/contrib/macOS/resources/English.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..9b778679528d01507b9befadd8422271176076c0 GIT binary patch literal 1044 zcmcJOUr)kN48_m0pJL(Jkm#%N0l_~+1QayN3jrO1(T&JZ@x!aXb{QMQ7>O~>?rraF zZ%^B^&ySKWbf|M(s_xnuR#RQ|b)*Vs4eT10op+Fmj?wzuG_kta4WxorSBta0sye}= zs#Ek`l@w^7uSAAO6TX=e4fKcv>fw1sT);`BJN9RV8Y)H-V)nF!{1#))F;=Ogu66;@ z=2+XH3&AvF4K)7|`5Z5+>mJWUV~@z|ntDYePOY*?w|+nMdP8k5W1_X%%h+{|SQqqS z&i=q^z55PN=)KFb+N(pn&38Y?jWatigGKL=N4MZ`Mvd+D` z9#i3LV=RATViD5~cx?Jl%jt(JD6>Clf=7luovK+WdSgQfm#i}5vvD$ZCYoo;-F%8) Du-2v7 literal 0 HcmV?d00001 diff --git a/contrib/macOS/resources/English.lproj/ReadMe.rtf b/contrib/macOS/resources/English.lproj/ReadMe.rtf new file mode 100644 index 000000000000..c0c327ae7aed --- /dev/null +++ b/contrib/macOS/resources/English.lproj/ReadMe.rtf @@ -0,0 +1,54 @@ +{\rtf1\ansi\ansicpg1252\cocoartf2512 +\cocoascreenfonts1\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset0 LucidaGrande-Bold;\f2\fnil\fcharset0 LucidaGrande; +\f3\fswiss\fcharset0 Helvetica-Bold;} +{\colortbl;\red255\green255\blue255;} +{\*\expandedcolortbl;;} +{\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid1\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{hyphen\}}{\leveltext\leveltemplateid2\'01\uc0\u8259 ;}{\levelnumbers;}\fi-360\li1440\lin1440 }{\listname ;}\listid1}} +{\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}} +\margl1440\margr1440\vieww17820\viewh11820\viewkind0 +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 + +\f0\fs24 \cf0 \ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li360\slleading40\pardirnatural\partightenfactor0 + +\f1\b \cf0 WARNING: +\f2\b0 ZFS is not in and of itself a backup. You should have INDEPENDENT backups of all of your data, whether it is on ZFS or not. You only have a backup of a pool if every disk in the pool can simultaneously end up in a swimming pool, and yet you still have your data. +\f0 \ +\ +\pard\tx220\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li720\fi-720\slleading40\pardirnatural\partightenfactor0 +\ls1\ilvl0\cf0 {\listtext \uc0\u8226 } +\f3\b Forum +\f0\b0 and +\f3\b wiki:\ +\pard\tx940\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li1440\fi-1440\slleading40\pardirnatural\partightenfactor0 +\ls1\ilvl1 +\f0\b0 \cf0 {\field{\*\fldinst{HYPERLINK "https://openzfsonosx.org"}}{\fldrslt https://openzfsonosx.org}} ({\field{\*\fldinst{HYPERLINK "http://o3x.org"}}{\fldrslt http://o3x.org}})\ +\pard\tx220\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li720\fi-720\slleading40\pardirnatural\partightenfactor0 +\ls1\ilvl0\cf0 {\listtext \uc0\u8226 }If you want to help with development:\ +\pard\tx940\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li1440\fi-1440\slleading40\pardirnatural\partightenfactor0 +\ls1\ilvl1\cf0 {\field{\*\fldinst{HYPERLINK "https://github.com/openzfsonosx"}}{\fldrslt https://github.com/openzfsonosx}}\ +\pard\tx220\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li720\fi-720\slleading40\pardirnatural\partightenfactor0 +\ls1\ilvl0\cf0 {\listtext \uc0\u8226 }Bug reports and enhancement suggestions:\ +\pard\tx940\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li1440\fi-1440\slleading40\pardirnatural\partightenfactor0 +\ls1\ilvl1\cf0 {\field{\*\fldinst{HYPERLINK "https://github.com/openzfsonosx/openzfs/issues"}}{\fldrslt https://github.com/openzfsonosx/openzfs/issues}}\ +\ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li360\slleading40\pardirnatural\partightenfactor0 + +\f1\b \cf0 About OpenZFS on OS X +\f0\b0 \ +\ +OpenZFS on OS X brings OpenZFS to Mac OS X. It is a free software project released under the CDDL and the Apple Public Source License.\ +\ + +\f1\b Backups +\f0\b0 \ +\ +Please note that all software is known to contain bugs. While care has been taken to ensure that under normal operations things don't go wrong, there's no guarantee of that fact. Using kernel extensions introduces a degree of instability into a system that userland processes don't encounter; the software has been known to cause kernel panics in the past. In addition, any file system has the possibility of causing damage to files. While ZFS creates checksums of all blocks (and so can detect failure earlier than in other systems), there is no guarantee that your data will be accessible in the event of problems. You should therefore take full responsibility for backups of data in the event that a restoration is needed. Any single filing system, including ZFS, is not a substitute for backups.\ +\ + +\f1\b Disclaimer +\f0\b0 \ +\ +The Original Code and all software distributed under the License are distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.\ +\ +Please see the License for the specific language governing rights and limitations under the License.} \ No newline at end of file diff --git a/contrib/macOS/resources/English.lproj/Welcome.rtf b/contrib/macOS/resources/English.lproj/Welcome.rtf new file mode 100644 index 000000000000..a67536e470cc --- /dev/null +++ b/contrib/macOS/resources/English.lproj/Welcome.rtf @@ -0,0 +1,48 @@ +{\rtf1\ansi\ansicpg1252\cocoartf2512 +\cocoascreenfonts1\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 LucidaGrande;\f1\fnil\fcharset0 LucidaGrande-Bold;\f2\fnil\fcharset0 AppleSymbols; +\f3\fswiss\fcharset0 Helvetica;\f4\fswiss\fcharset0 ArialMT;\f5\fswiss\fcharset0 Arial-BoldMT; +\f6\fswiss\fcharset0 Arial-ItalicMT;} +{\colortbl;\red255\green255\blue255;\red255\green128\blue0;\red52\green52\blue52;\red93\green93\blue93; +\red76\green76\blue76;} +{\*\expandedcolortbl;;\csgenericrgb\c100000\c50196\c0;\csgenericrgb\c20392\c20392\c20392;\csgenericrgb\c36471\c36471\c36471; +\csgenericrgb\c29804\c29804\c29804;} +\paperw11900\paperh16840\margl1440\margr1440\vieww17860\viewh11000\viewkind0 +\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\li360\pardirnatural\partightenfactor0 + +\f0\fs24 \cf0 \ +You will be guided through the steps necessary to install +\f1\b OpenZFS on OS X +\f0\b0 on your system.\ +\ +\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\li360\pardirnatural\partightenfactor0 + +\f2\fs36 \cf2 {{}{\*\glid2449 ⚠}\uc0\u9888 } +\f1\b\fs24 \cf3 IMPORTANT +\f0\b0 \cf0 \ +- You must "sudo zpool export" any imported pools before\ + running this installer.\ +- You must uninstall any other ZFS implementation before\ + installing OpenZFS on OS X.\ +- If this installer fails, please check install.log in Console.app\ + (or /var/log/install.log) for errors. +\f3 \ +\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardeftab720\li360\sl420\partightenfactor0 + +\f4\fs18 \cf4 \'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\'97\ + +\f5\b\fs24 \cf5 System Requirements: +\f4\b0\fs26 \ + +\f6\i\fs22 Mac OS X operating system: +\f4\i0 \ + +\f5\b BigSur +\f4\b0 (v11) down to +\f5\b Yosemite +\f4\b0 (v10.10) +\f6\i \ +2 GB of memory +\f0\i0 \uc0\u8232 +\f6\i 25 MB of available disk space +\f0\i0 \uc0\u8232 +\f6\i Additional internal/external hard disks (reformatting required to add ZFS)} \ No newline at end of file diff --git a/contrib/macOS/resources/background.png b/contrib/macOS/resources/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f8aa84d282cc3d2f3be53b1bea4946a7ff918b12 GIT binary patch literal 20428 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 Hu0mjfd~aUc literal 0 HcmV?d00001 diff --git a/contrib/macOS/resources/javascript.js b/contrib/macOS/resources/javascript.js new file mode 100644 index 000000000000..01d45f30b349 --- /dev/null +++ b/contrib/macOS/resources/javascript.js @@ -0,0 +1,200 @@ +const __IC_FLAT_DISTRIBUTION__=false; +const IC_OS_DISTRIBUTION_TYPE_ANY=0; +const IC_OS_DISTRIBUTION_TYPE_CLIENT=1; +const IC_DISK_TYPE_DESTINATION=0; +const IC_OS_DISTRIBUTION_TYPE_SERVER=2; +const IC_DISK_TYPE_STARTUP_DISK=1; + +const IC_COMPARATOR_IS_EQUAL=0; +const IC_COMPARATOR_IS_GREATER=1; +const IC_COMPARATOR_IS_NOT_EQUAL=2; +const IC_COMPARATOR_IS_LESS=-1; + +function IC_CheckOS(inDiskType,inMustBeInstalled,inMinimumVersion,inMaximumVersion,inDistributionType) +{ + var tOSVersion=undefined; + + /* Check Version Constraints */ + + if (inDiskType==IC_DISK_TYPE_DESTINATION) + { + if (my.target.systemVersion!=undefined) + { + tOSVersion=my.target.systemVersion.ProductVersion; + } + + /* Check if no OS is installed on the potential target */ + + if (tOSVersion==undefined) + { + return (inMustBeInstalled==false); + } + + if (inMustBeInstalled==false) + { + return false; + } + } + else + { + tOSVersion=system.version.ProductVersion; + } + + if (system.compareVersions(tOSVersion,inMinimumVersion)==-1) + return false; + + if (inMaximumVersion!=undefined && + system.compareVersions(tOSVersion,inMaximumVersion)==1) + return false; + + /* Check Distribution Type */ + + if (inDistributionType!=IC_OS_DISTRIBUTION_TYPE_ANY) + { + var tIsServer; + + if (system.compareVersions(tOSVersion,'10.8.0')==-1) + { + if (inDiskType==IC_DISK_TYPE_DESTINATION) + { + tIsServer=system.files.fileExistsAtPath(my.target.mountpoint+'/System/Library/CoreServices/ServerVersion.plist'); + } + else + { + tIsServer=system.files.fileExistsAtPath('/System/Library/CoreServices/ServerVersion.plist'); + } + } + else + { + if (inDiskType==IC_DISK_TYPE_DESTINATION) + { + tIsServer=system.files.fileExistsAtPath(my.target.mountpoint+'/Applications/Server.app'); + } + else + { + tIsServer=system.files.fileExistsAtPath('/Applications/Server.app'); + } + } + if (inDistributionType==IC_OS_DISTRIBUTION_TYPE_CLIENT && tIsServer==true) + { + return false; + } + + if (inDistributionType==IC_OS_DISTRIBUTION_TYPE_SERVER && tIsServer==false) + { + return false; + } + } + + return true; +} + +function IC_CheckScriptReturnValue(inScriptPath,inArguments,inComparator,inReturnValue) +{ + var tReturnValue; + + if (inScriptPath.charAt(0)=='/') + { + /* Check Absolute Path Existence */ + + if (system.files.fileExistsAtPath(inScriptPath)==false) + { + return false; + } + } + else + { + if (__IC_FLAT_DISTRIBUTION__==true && system.compareVersions(system.version.ProductVersion, '10.6.0')<0) + { + system.log("[WARNING] Embedded scripts are not supported in Flat distribution format on Mac OS X 10.5"); + + return true; + } + } + if (inArguments.length>0) + { + var tMethodCall; + var tStringArguments=[]; + + for(var i=0;i<inArguments.length;i++) + { + tStringArguments[i]='inArguments['+i+']'; + } + + tMethodCall='system.run(inScriptPath,'+tStringArguments.join(',')+');'; + + tReturnValue=eval(tMethodCall); + } + else + { + tReturnValue=system.run(inScriptPath); + } + + if (tReturnValue==undefined) + { + return false; + } + + if (inComparator==IC_COMPARATOR_IS_EQUAL) + { + return (tReturnValue==inReturnValue); + } + else if (inComparator==IC_COMPARATOR_IS_GREATER) + { + return (tReturnValue>inReturnValue); + } + else if (inComparator==IC_COMPARATOR_IS_LESS) + { + return (tReturnValue<inReturnValue); + } + else if (inComparator==IC_COMPARATOR_IS_NOT_EQUAL) + { + return (tReturnValue!=inReturnValue); + } + + return false; +} + +function installation_check() +{ + var tResult; + + tResult=IC_CheckOS(IC_DISK_TYPE_STARTUP_DISK,true,'10.5',undefined,IC_OS_DISTRIBUTION_TYPE_ANY); + + if (tResult==false) + { + my.result.title = system.localizedStandardStringWithFormat('InstallationCheckError', system.localizedString('DISTRIBUTION_TITLE')); + my.result.message = ' '; + my.result.type = 'Fatal'; + } + + if (tResult==true) + { + var tScriptArguments1=new Array(); + + tResult=IC_CheckScriptReturnValue('poolcheck.sh',tScriptArguments1,IC_COMPARATOR_IS_EQUAL,1); + + if (tResult==false) + { + my.result.title = system.localizedString('REQUIREMENT_FAILED_MESSAGE_INSTALLATION_CHECK_1'); + my.result.message = system.localizedString('REQUIREMENT_FAILED_DESCRIPTION_INSTALLATION_CHECK_1'); + my.result.type = 'Fatal'; + } + + if (tResult==true) + { + var tScriptArguments2=new Array(); + + tResult=IC_CheckScriptReturnValue('zevocheck.sh',tScriptArguments2,IC_COMPARATOR_IS_EQUAL,1); + + if (tResult==false) + { + my.result.title = system.localizedString('REQUIREMENT_FAILED_MESSAGE_INSTALLATION_CHECK_2'); + my.result.message = system.localizedString('REQUIREMENT_FAILED_DESCRIPTION_INSTALLATION_CHECK_2'); + my.result.type = 'Fatal'; + } + } + } + + return tResult; +} diff --git a/etc/launchd/Makefile.am b/etc/launchd/Makefile.am new file mode 100644 index 000000000000..79d067e8195b --- /dev/null +++ b/etc/launchd/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = daemons launchd.d diff --git a/etc/launchd/daemons/.gitignore b/etc/launchd/daemons/.gitignore new file mode 100644 index 000000000000..8b43b0f2a1fd --- /dev/null +++ b/etc/launchd/daemons/.gitignore @@ -0,0 +1,5 @@ +org.openzfsonosx.zconfigd.plist +org.openzfsonosx.zed.plist +org.openzfsonosx.zpool-import.plist +org.openzfsonosx.zpool-import-all.plist +org.openzfsonosx.InvariantDisks.plist diff --git a/etc/launchd/daemons/Makefile.am b/etc/launchd/daemons/Makefile.am new file mode 100644 index 000000000000..ff1d1b683ae1 --- /dev/null +++ b/etc/launchd/daemons/Makefile.am @@ -0,0 +1,38 @@ +AUTOMAKE_OPTIONS = subdir-objects + +launchddaemondir = /Library/LaunchDaemons + +launchddaemon_DATA = \ + $(top_srcdir)/etc/launchd/daemons/org.openzfsonosx.zconfigd.plist \ + $(top_srcdir)/etc/launchd/daemons/org.openzfsonosx.zed.plist \ + $(top_srcdir)/etc/launchd/daemons/org.openzfsonosx.zpool-import.plist \ + $(top_srcdir)/etc/launchd/daemons/org.openzfsonosx.zpool-import-all.plist \ + $(top_srcdir)/etc/launchd/daemons/org.openzfsonosx.InvariantDisks.plist + +DAEMON_DIR = $(top_srcdir)/etc/launchd/daemons +launchdscriptdir=${libexecdir}/zfs/launchd.d + +EXTRA_DIST = \ + $(DAEMON_DIR)/org.openzfsonosx.zconfigd.plist.in \ + $(DAEMON_DIR)/org.openzfsonosx.zed.plist.in \ + $(DAEMON_DIR)/org.openzfsonosx.zpool-import.plist.in \ + $(DAEMON_DIR)/org.openzfsonosx.zpool-import-all.plist.in \ + $(DAEMON_DIR)/org.openzfsonosx.InvariantDisks.plist.in + +$(DAEMON_DIR)/org.openzfsonosx.zconfigd.plist: $(DAEMON_DIR)/org.openzfsonosx.zconfigd.plist.in +$(DAEMON_DIR)/org.openzfsonosx.zed.plist: $(DAEMON_DIR)/org.openzfsonosx.zed.plist.in +$(DAEMON_DIR)/org.openzfsonosx.zpool-import.plist: $(DAEMON_DIR)/org.openzfsonosx.zpool-import.plist.in +$(DAEMON_DIR)/org.openzfsonosx.zpool-import-all.plist: $(DAEMON_DIR)/org.openzfsonosx.zpool-import-all.plist.in +$(DAEMON_DIR)/org.openzfsonosx.InvariantDisks.plist: $(DAEMON_DIR)/org.openzfsonosx.InvariantDisks.plist.in + +$(launchddaemon_DATA): + -$(SED) -e 's,@bindir\@,$(bindir),g' \ + -e 's,@runstatedir\@,$(runstatedir),g' \ + -e 's,@sbindir\@,$(sbindir),g' \ + -e 's,@sysconfdir\@,$(sysconfdir),g' \ + -e 's,@launchddaemondir\@,$(launchddaemondir),g' \ + -e 's,@launchdscriptdir\@,$(launchdscriptdir),g' \ + '$@.in' >'$@' + +clean-local:: + -$(RM) $(launchddaemon_DATA) diff --git a/etc/launchd/daemons/org.openzfsonosx.InvariantDisks.plist.in b/etc/launchd/daemons/org.openzfsonosx.InvariantDisks.plist.in new file mode 100644 index 000000000000..4e49548aedb7 --- /dev/null +++ b/etc/launchd/daemons/org.openzfsonosx.InvariantDisks.plist.in @@ -0,0 +1,16 @@ + + + + + Label + org.openzfsonosx.InvariantDisks + ProgramArguments + + @sbindir@/InvariantDisks + + RunAtLoad + + KeepAlive + + + diff --git a/etc/launchd/daemons/org.openzfsonosx.zconfigd.plist.in b/etc/launchd/daemons/org.openzfsonosx.zconfigd.plist.in new file mode 100644 index 000000000000..51154e84661a --- /dev/null +++ b/etc/launchd/daemons/org.openzfsonosx.zconfigd.plist.in @@ -0,0 +1,20 @@ + + + + + Label + org.openzfsonosx.zconfigd + ProgramArguments + + @sbindir@/zconfigd + + RunAtLoad + + KeepAlive + + StandardErrorPath + /private/var/log/org.openzfsonosx.zconfigd.err + StandardOutPath + /private/var/log/org.openzfsonosx.zconfigd.log + + diff --git a/etc/launchd/daemons/org.openzfsonosx.zed.plist.in b/etc/launchd/daemons/org.openzfsonosx.zed.plist.in new file mode 100644 index 000000000000..1f62d4e2a9d2 --- /dev/null +++ b/etc/launchd/daemons/org.openzfsonosx.zed.plist.in @@ -0,0 +1,21 @@ + + + + + Label + org.openzfsonosx.zed + ProgramArguments + + @sbindir@/zed + -vfF + + RunAtLoad + + KeepAlive + + StandardErrorPath + /private/var/log/org.openzfsonosx.zed.err + StandardOutPath + /private/var/log/org.openzfsonosx.zed.log + + diff --git a/etc/launchd/daemons/org.openzfsonosx.zpool-import-all.plist.in b/etc/launchd/daemons/org.openzfsonosx.zpool-import-all.plist.in new file mode 100644 index 000000000000..3687eb418e33 --- /dev/null +++ b/etc/launchd/daemons/org.openzfsonosx.zpool-import-all.plist.in @@ -0,0 +1,18 @@ + + + + + Label + org.openzfsonosx.zpool-import-all + ProgramArguments + + @launchdscriptdir@/zpool-import-all.sh + + RunAtLoad + + StandardErrorPath + /private/var/log/org.openzfsonosx.zpool-import-all.err + StandardOutPath + /private/var/log/org.openzfsonosx.zpool-import-all.log + + diff --git a/etc/launchd/daemons/org.openzfsonosx.zpool-import.plist.in b/etc/launchd/daemons/org.openzfsonosx.zpool-import.plist.in new file mode 100644 index 000000000000..6bdb2b3bb4c1 --- /dev/null +++ b/etc/launchd/daemons/org.openzfsonosx.zpool-import.plist.in @@ -0,0 +1,24 @@ + + + + + Label + org.openzfsonosx.zpool-import + ProgramArguments + + @sbindir@/zpool + import + -a + -d + /var/run/disk/by-id + + RunAtLoad + + LaunchOnlyOnce + + StandardErrorPath + /private/var/log/org.openzfsonosx.zpool-import-all.log + StandardOutPath + /private/var/log/org.openzfsonosx.zpool-import-all.log + + diff --git a/etc/launchd/launchd.d/.gitignore b/etc/launchd/launchd.d/.gitignore new file mode 100644 index 000000000000..f11cc9cf5e4f --- /dev/null +++ b/etc/launchd/launchd.d/.gitignore @@ -0,0 +1 @@ +zpool-import-all.sh diff --git a/etc/launchd/launchd.d/Makefile.am b/etc/launchd/launchd.d/Makefile.am new file mode 100644 index 000000000000..8a3c9d13627a --- /dev/null +++ b/etc/launchd/launchd.d/Makefile.am @@ -0,0 +1,23 @@ +AUTOMAKE_OPTIONS = subdir-objects + +launchdscriptdir = ${libexecdir}/zfs/launchd.d +launchdscript_SCRIPTS = \ + zpool-import-all.sh + +CLEANFILES = $(launchdscript_SCRIPTS) + +EXTRA_DIST = \ + $(top_srcdir)/etc/launchd/launchd.d/zpool-import-all.sh.in + +do_subst = -$(SED) -e 's,@bindir\@,$(bindir),g' \ + -e 's,@runstatedir\@,$(runstatedir),g' \ + -e 's,@sbindir\@,$(sbindir),g' \ + -e 's,@sysconfdir\@,$(sysconfdir),g' \ + -e 's,@launchdscriptdir\@,$(launchdscriptdir),g' + +zpool-import-all.sh: zpool-import-all.sh.in + $(do_subst) < $(top_srcdir)/etc/launchd/launchd.d/zpool-import-all.sh.in > zpool-import-all.sh + chmod +x zpool-import-all.sh + +clean-local:: + -$(RM) $(launchdscript_SCRIPTS) diff --git a/etc/launchd/launchd.d/zpool-import-all.sh.in b/etc/launchd/launchd.d/zpool-import-all.sh.in new file mode 100755 index 000000000000..d3f27affac89 --- /dev/null +++ b/etc/launchd/launchd.d/zpool-import-all.sh.in @@ -0,0 +1,47 @@ +#!/bin/bash + +echo "+zpool-import-all.sh" +date +export ZPOOL_IMPORT_ALL_COOKIE=/var/run/org.openzfsonosx.zpool-import-all.didRun +export INVARIANT_DISKS_IDLE_FILE=/var/run/disk/invariant.idle +export TIMEOUT_SECONDS=60 +export MAXIMUM_SLEEP_ITERATIONS=$((${TIMEOUT_SECONDS} * 10)) + +/usr/bin/time /usr/sbin/system_profiler SPParallelATADataType SPCardReaderDataType SPFibreChannelDataType SPFireWireDataType SPHardwareRAIDDataType SPNetworkDataType SPPCIDataType SPParallelSCSIDataType SPSASDataType SPSerialATADataType SPStorageDataType SPThunderboltDataType SPUSBDataType SPNetworkVolumeDataType 1>/dev/null 2>&1 + +/bin/sync + +echo "Waiting up to ${TIMEOUT_SECONDS} seconds for the InvariantDisks idle file ${INVARIANT_DISKS_IDLE_FILE} to exist" + +i=0 +while [ "${i}" -lt "${MAXIMUM_SLEEP_ITERATIONS}" -a ! -e "${INVARIANT_DISKS_IDLE_FILE}" ] +do + i=$((i+1)) + sleep .1 +done + +if [ -e "${INVARIANT_DISKS_IDLE_FILE}" ] +then + echo "Found ${INVARIANT_DISKS_IDLE_FILE} after ${i} iterations of sleeping 0.1 seconds" +else + echo "File ${INVARIANT_DISKS_IDLE_FILE} not found within ${TIMEOUT_SECONDS} seconds" +fi +date + +sleep 10 +echo "Running zpool import -a" +date + +/bin/launchctl kickstart system/org.openzfsonosx.zpool-import +ret=$? + +date +echo "Launched zpool import -a : ${ret}" + +echo "Touching the file ${ZPOOL_IMPORT_ALL_COOKIE}" +touch "${ZPOOL_IMPORT_ALL_COOKIE}" + +date +echo "-zpool-import-all.sh" + +exit 0 diff --git a/etc/paths.d/Makefile.am b/etc/paths.d/Makefile.am new file mode 100644 index 000000000000..d5d4f4ce5d47 --- /dev/null +++ b/etc/paths.d/Makefile.am @@ -0,0 +1,19 @@ +pathsddir = $(sysconfdir)/paths.d +pathsd_DATA = zfs + +EXTRA_DIST = \ + zfs + +zfs: zfs.in + +$(pathsd_DATA): + -$(SED) -e 's,@bindir\@,$(bindir),g' \ + -e 's,@runstatedir\@,$(runstatedir),g' \ + -e 's,@sbindir\@,$(sbindir),g' \ + -e 's,@sysconfdir\@,$(sysconfdir),g' \ + -e 's,@launchddaemondir\@,$(launchddaemondir),g' \ + -e 's,@launchdscriptdir\@,$(launchdscriptdir),g' \ + '$@.in' >'$@' + +distclean-local:: + -$(RM) $(pathsd_DATA) diff --git a/etc/paths.d/zfs.in b/etc/paths.d/zfs.in new file mode 100644 index 000000000000..5532f40754b3 --- /dev/null +++ b/etc/paths.d/zfs.in @@ -0,0 +1,2 @@ +@bindir@ +@sbindir@ diff --git a/include/os/macos/Makefile.am b/include/os/macos/Makefile.am new file mode 100644 index 000000000000..a9564c3e3cba --- /dev/null +++ b/include/os/macos/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = spl zfs \ No newline at end of file diff --git a/include/os/macos/spl/Makefile.am b/include/os/macos/spl/Makefile.am new file mode 100644 index 000000000000..75cad0836e61 --- /dev/null +++ b/include/os/macos/spl/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = sys rpc diff --git a/include/os/macos/spl/ia32/sys/asm_linkage.h b/include/os/macos/spl/ia32/sys/asm_linkage.h new file mode 100644 index 000000000000..0009705ad61b --- /dev/null +++ b/include/os/macos/spl/ia32/sys/asm_linkage.h @@ -0,0 +1,297 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _IA32_SYS_ASM_LINKAGE_H +#define _IA32_SYS_ASM_LINKAGE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _ASM /* The remainder of this file is only for assembly files */ + +/* + * make annoying differences in assembler syntax go away + */ + +/* + * D16 and A16 are used to insert instructions prefixes; the + * macros help the assembler code be slightly more portable. + */ +#if !defined(__GNUC_AS__) +/* + * /usr/ccs/bin/as prefixes are parsed as separate instructions + */ +#define D16 data16; +#define A16 addr16; + +/* + * (There are some weird constructs in constant expressions) + */ +#define _CONST(const) [const] +#define _BITNOT(const) -1!_CONST(const) +#define _MUL(a, b) _CONST(a \* b) + +#else +/* + * Why not use the 'data16' and 'addr16' prefixes .. well, the + * assembler doesn't quite believe in real mode, and thus argues with + * us about what we're trying to do. + */ +#define D16 .byte 0x66; +#define A16 .byte 0x67; + +#define _CONST(const) (const) +#define _BITNOT(const) ~_CONST(const) +#define _MUL(a, b) _CONST(a * b) + +#endif + +/* + * C pointers are different sizes between i386 and amd64. + * These constants can be used to compute offsets into pointer arrays. + */ +#if defined(__amd64) +#define CLONGSHIFT 3 +#define CLONGSIZE 8 +#define CLONGMASK 7 +#elif defined(__i386) +#define CLONGSHIFT 2 +#define CLONGSIZE 4 +#define CLONGMASK 3 +#endif + +/* + * Since we know we're either ILP32 or LP64 .. + */ +#define CPTRSHIFT CLONGSHIFT +#define CPTRSIZE CLONGSIZE +#define CPTRMASK CLONGMASK + +#if CPTRSIZE != (1 << CPTRSHIFT) || CLONGSIZE != (1 << CLONGSHIFT) +#error "inconsistent shift constants" +#endif + +#if CPTRMASK != (CPTRSIZE - 1) || CLONGMASK != (CLONGSIZE - 1) +#error "inconsistent mask constants" +#endif + +#define ASM_ENTRY_ALIGN 4, 0x90 + +/* + * SSE register alignment and save areas + */ + +#define XMM_SIZE 16 +#define XMM_ALIGN 16 +#define XMM_ALIGN_LOG 4, 0x90 + +#if defined(__amd64) + +#define SAVE_XMM_PROLOG(sreg, nreg) \ + subq $_CONST(_MUL(XMM_SIZE, nreg)), %rsp; \ + movq %rsp, sreg + +#define RSTOR_XMM_EPILOG(sreg, nreg) \ + addq $_CONST(_MUL(XMM_SIZE, nreg)), %rsp + +#elif defined(__i386) + +#define SAVE_XMM_PROLOG(sreg, nreg) \ + subl $_CONST(_MUL(XMM_SIZE, nreg) + XMM_ALIGN), %esp; \ + movl %esp, sreg; \ + addl $XMM_ALIGN, sreg; \ + andl $_BITNOT(XMM_ALIGN-1), sreg + +#define RSTOR_XMM_EPILOG(sreg, nreg) \ + addl $_CONST(_MUL(XMM_SIZE, nreg) + XMM_ALIGN), %esp; + +#endif /* __i386 */ + +/* + * profiling causes definitions of the MCOUNT and RTMCOUNT + * particular to the type + */ +#ifdef GPROF + +#define MCOUNT(x) \ + pushl %ebp; \ + movl %esp, %ebp; \ + call _mcount; \ + popl %ebp + +#endif /* GPROF */ + +#ifdef PROF + +#define MCOUNT(x) \ +/* CSTYLED */ \ + .lcomm .L_/**/x/**/1, 4, 4; \ + pushl %ebp; \ + movl %esp, %ebp; \ +/* CSTYLED */ \ + movl $.L_/**/x/**/1, %edx; \ + call _mcount; \ + popl %ebp + +#endif /* PROF */ + +/* + * if we are not profiling, MCOUNT should be defined to nothing + */ +#if !defined(PROF) && !defined(GPROF) +#define MCOUNT(x) +#endif /* !defined(PROF) && !defined(GPROF) */ + +#define RTMCOUNT(x) MCOUNT(x) + +/* + * Macro to define weak symbol aliases. These are similar to the ANSI-C + * #pragma weak name = _name + * except a compiler can determine type. The assembler must be told. Hence, + * the second parameter must be the type of the symbol (i.e.: function,...) + */ +#define ANSI_PRAGMA_WEAK(sym, stype) \ + .weak sym; \ +/* CSTYLED */ \ +sym = _/**/sym + +/* + * Like ANSI_PRAGMA_WEAK(), but for unrelated names, as in: + * #pragma weak sym1 = sym2 + */ +#define ANSI_PRAGMA_WEAK2(sym1, sym2, stype) \ + .weak sym1; \ +sym1 = sym2 + +/* + * ENTRY provides the standard procedure entry code and an easy way to + * insert the calls to mcount for profiling. ENTRY_NP is identical, but + * never calls mcount. + */ +#define ENTRY(x) \ + .text; \ + .align ASM_ENTRY_ALIGN; \ + .globl _##x; \ + .globl _##x; \ + .globl x; \ +_##x:; \ +x: MCOUNT(x) + +#define ENTRY_NP(x) \ + .text; \ + .align ASM_ENTRY_ALIGN; \ + .globl _##x; \ + .globl x; \ +_##x:; \ +x: + +#define RTENTRY(x) \ + .text; \ + .align ASM_ENTRY_ALIGN; \ + .globl _##x; \ + .globl x; \ +_##x:; \ +x: RTMCOUNT(x) + +/* + * ENTRY2 is identical to ENTRY but provides two labels for the entry point. + */ +#define ENTRY2(x, y) \ + .text; \ + .align ASM_ENTRY_ALIGN; \ + .globl x, y; \ +/* CSTYLED */ \ +x:; \ +y: MCOUNT(x) + +#define ENTRY_NP2(x, y) \ + .text; \ + .align ASM_ENTRY_ALIGN; \ + .globl x, y; \ +/* CSTYLED */ \ +x:; \ +y: + + +/* + * ALTENTRY provides for additional entry points. + */ +#define ALTENTRY(x) \ + .globl _##x; \ + .globl x; \ +_##x:; \ +x: + +/* + * DGDEF and DGDEF2 provide global data declarations. + * + * DGDEF provides a word aligned word of storage. + * + * DGDEF2 allocates "sz" bytes of storage with **NO** alignment. This + * implies this macro is best used for byte arrays. + * + * DGDEF3 allocates "sz" bytes of storage with "algn" alignment. + */ +#define DGDEF2(name, sz) \ + .data; \ + .globl name; \ +name: + +#define DGDEF3(name, sz, algn) \ + .data; \ + .align algn; \ + .globl name; \ +name: + +#define DGDEF(name) DGDEF3(name, 4, 4) + +/* + * SET_SIZE trails a function and set the size for the ELF symbol table. + */ +#define SET_SIZE(x) + +/* + * NWORD provides native word value. + */ +#if defined(__amd64) + +/*CSTYLED*/ +#define NWORD quad + +#elif defined(__i386) + +#define NWORD long + +#endif /* __i386 */ + +#endif /* _ASM */ + +#ifdef __cplusplus +} +#endif + +#endif /* _IA32_SYS_ASM_LINKAGE_H */ diff --git a/include/os/macos/spl/libkern/libkern.h b/include/os/macos/spl/libkern/libkern.h new file mode 100644 index 000000000000..b1fd44555ab3 --- /dev/null +++ b/include/os/macos/spl/libkern/libkern.h @@ -0,0 +1,43 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2020 Jorgen Lundman + * + */ + +#ifndef _SPL_LIBKERN_H +#define _SPL_LIBKERN_H + +/* + * We wrap this header to handle that copyinstr()'s final argument is + * mandatory on OSX. Wrap it to call our ddi_copyinstr to make it optional. + */ + +#include +#include + +#include_next +#undef copyinstr +#define copyinstr(U, K, L, D) ddi_copyinstr((U), (K), (L), (D)) + +#endif diff --git a/include/os/macos/spl/linux/init.h b/include/os/macos/spl/linux/init.h new file mode 100644 index 000000000000..4ab1523c16ba --- /dev/null +++ b/include/os/macos/spl/linux/init.h @@ -0,0 +1,26 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _LINUX_INIT_H +#define _LINUX_INIT_H + + +#endif diff --git a/include/os/macos/spl/linux/kernel.h b/include/os/macos/spl/linux/kernel.h new file mode 100644 index 000000000000..73a2b2eaad2e --- /dev/null +++ b/include/os/macos/spl/linux/kernel.h @@ -0,0 +1,25 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _LINUX_KERNEL_H +#define _LINUX_KERNEL_H + +#endif diff --git a/include/os/macos/spl/linux/module.h b/include/os/macos/spl/linux/module.h new file mode 100644 index 000000000000..264d6c058dd9 --- /dev/null +++ b/include/os/macos/spl/linux/module.h @@ -0,0 +1,28 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _LINUX_MODULE_H +#define _LINUX_MODULE_H + +#include +#include + +#endif diff --git a/include/os/macos/spl/rpc/Makefile.am b/include/os/macos/spl/rpc/Makefile.am new file mode 100644 index 000000000000..770d26812ea6 --- /dev/null +++ b/include/os/macos/spl/rpc/Makefile.am @@ -0,0 +1,3 @@ +KERNEL_H = \ + $(top_srcdir)/include/os/macos/spl/rpc/types.h \ + $(top_srcdir)/include/os/macos/spl/rpc/xdr.h diff --git a/include/os/macos/spl/rpc/types.h b/include/os/macos/spl/rpc/types.h new file mode 100644 index 000000000000..e089e0ed8c27 --- /dev/null +++ b/include/os/macos/spl/rpc/types.h @@ -0,0 +1,32 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * + */ +#ifndef _SPL_RPC_TYPES_H +#define _SPL_RPC_TYPES_H + +typedef int bool_t; + +#endif /* SPL_RPC_TYPES_H */ diff --git a/include/os/macos/spl/rpc/xdr.h b/include/os/macos/spl/rpc/xdr.h new file mode 100644 index 000000000000..7b8074b05c78 --- /dev/null +++ b/include/os/macos/spl/rpc/xdr.h @@ -0,0 +1,175 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * Copyright (c) 1989, 2011, Oracle and/or its affiliates. All rights reserved. + */ +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ +/* + * Portions of this source code were derived from Berkeley + * 4.3 BSD under license from the Regents of the University of + * California. + */ + +/* + * xdr.h, External Data Representation Serialization Routines. + * + */ + +#ifndef _SPL_RPC_XDR_H +#define _SPL_RPC_XDR_H + + +#include +#include + +/* + * XDR enums and types. + */ +enum xdr_op { + XDR_ENCODE, + XDR_DECODE +}; + +struct xdr_ops; + +typedef struct { + struct xdr_ops *x_ops; /* Also used to let caller know if */ + /* xdrmem_create() succeeds (sigh..) */ + caddr_t x_addr; /* Current buffer addr */ + caddr_t x_addr_end; /* End of the buffer */ + enum xdr_op x_op; /* Stream direction */ +} XDR; + +typedef bool_t (*xdrproc_t)(XDR *xdrs, void *ptr); + +struct xdr_ops { + bool_t (*xdr_control)(XDR *, int, void *); + + bool_t (*xdr_char)(XDR *, char *); + bool_t (*xdr_u_short)(XDR *, unsigned short *); + bool_t (*xdr_u_int)(XDR *, unsigned *); + bool_t (*xdr_u_longlong_t)(XDR *, u_longlong_t *); + + bool_t (*xdr_opaque)(XDR *, caddr_t, const uint_t); + bool_t (*xdr_string)(XDR *, char **, const uint_t); + bool_t (*xdr_array)(XDR *, caddr_t *, uint_t *, const uint_t, + const uint_t, const xdrproc_t); +}; + +/* + * XDR control operator. + */ +#define XDR_GET_BYTES_AVAIL 1 + +struct xdr_bytesrec { + bool_t xc_is_last_record; + size_t xc_num_avail; +}; + +/* + * XDR functions. + */ +void xdrmem_create(XDR *xdrs, const caddr_t addr, const uint_t size, + const enum xdr_op op); +#define xdr_destroy(xdrs) ((void) 0) + +#define xdr_control(xdrs, req, info) \ + (xdrs)->x_ops->xdr_control((xdrs), (req), (info)) + +/* + * For precaution, the following are defined as static inlines instead of macros + * to get some amount of type safety. + * + * Also, macros wouldn't work in the case where typecasting is done, because it + * must be possible to reference the functions' addresses by these names. + */ +static inline bool_t +xdr_char(XDR *xdrs, char *cp) +{ + return (xdrs->x_ops->xdr_char(xdrs, cp)); +} + +static inline bool_t +xdr_u_short(XDR *xdrs, unsigned short *usp) +{ + return (xdrs->x_ops->xdr_u_short(xdrs, usp)); +} + +static inline bool_t +xdr_short(XDR *xdrs, short *sp) +{ + return (xdrs->x_ops->xdr_u_short(xdrs, (unsigned short *) sp)); +} + +static inline bool_t +xdr_u_int(XDR *xdrs, unsigned *up) +{ + return (xdrs->x_ops->xdr_u_int(xdrs, up)); +} + +static inline bool_t +xdr_int(XDR *xdrs, int *ip) +{ + return (xdrs->x_ops->xdr_u_int(xdrs, (unsigned *)ip)); +} + +static inline bool_t +xdr_u_longlong_t(XDR *xdrs, u_longlong_t *ullp) +{ + return (xdrs->x_ops->xdr_u_longlong_t(xdrs, ullp)); +} + +static inline bool_t +xdr_longlong_t(XDR *xdrs, longlong_t *llp) +{ + return (xdrs->x_ops->xdr_u_longlong_t(xdrs, (u_longlong_t *)llp)); +} + +/* + * Fixed-length opaque data. + */ +static inline bool_t +xdr_opaque(XDR *xdrs, caddr_t cp, const uint_t cnt) +{ + return (xdrs->x_ops->xdr_opaque(xdrs, cp, cnt)); +} + +/* + * Variable-length string. + * The *sp buffer must have (maxsize + 1) bytes. + */ +static inline bool_t +xdr_string(XDR *xdrs, char **sp, const uint_t maxsize) +{ + return (xdrs->x_ops->xdr_string(xdrs, sp, maxsize)); +} + +/* + * Variable-length arrays. + */ +static inline bool_t xdr_array(XDR *xdrs, caddr_t *arrp, uint_t *sizep, + const uint_t maxsize, const uint_t elsize, const xdrproc_t elproc) +{ + return (xdrs->x_ops->xdr_array(xdrs, arrp, sizep, maxsize, elsize, + elproc)); +} + +#endif /* SPL_RPC_XDR_H */ diff --git a/include/os/macos/spl/stddef.h b/include/os/macos/spl/stddef.h new file mode 100644 index 000000000000..16caf0178c19 --- /dev/null +++ b/include/os/macos/spl/stddef.h @@ -0,0 +1,38 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2021 Jorgen Lundman + * + */ + +#ifndef _SPL_STDDEF_H +#define _SPL_STDDEF_H + +#include +#include +#if defined(MAC_OS_X_VERSION_10_12) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) +#include_next +#endif + +#endif diff --git a/include/os/macos/spl/string.h b/include/os/macos/spl/string.h new file mode 100644 index 000000000000..b93b35832c02 --- /dev/null +++ b/include/os/macos/spl/string.h @@ -0,0 +1,54 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _SPL_STRING_H +#define _SPL_STRING_H + +/* + * strcmp() has been deprecated in macOS 11, but case is needed to change + * to strncmp(). For now, we just create a simple spl_strcmp() until + * upstream can be changed. + */ +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#if defined(MAC_OS_VERSION_11_0) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0) +#define strcmp XNU_strcmp +#endif /* MAC_OS */ + +#include_next + +#if defined(MAC_OS_VERSION_11_0) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0) +extern int spl_strcmp(const char *, const char *); +#undef strcmp +#define strcmp spl_strcmp +#endif /* MAC_OS */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/os/macos/spl/sys/Makefile.am b/include/os/macos/spl/sys/Makefile.am new file mode 100644 index 000000000000..570377456952 --- /dev/null +++ b/include/os/macos/spl/sys/Makefile.am @@ -0,0 +1,49 @@ +KERNEL_H = \ + $(top_srcdir)/include/os/macos/spl/sys/atomic.h \ + $(top_srcdir)/include/os/macos/spl/sys/byteorder.h \ + $(top_srcdir)/include/os/macos/spl/sys/callb.h \ + $(top_srcdir)/include/os/macos/spl/sys/cmn_err.h \ + $(top_srcdir)/include/os/macos/spl/sys/condvar.h \ + $(top_srcdir)/include/os/macos/spl/sys/console.h \ + $(top_srcdir)/include/os/macos/spl/sys/cred.h \ + $(top_srcdir)/include/os/macos/spl/sys/debug.h \ + $(top_srcdir)/include/os/macos/spl/sys/errno.h \ + $(top_srcdir)/include/os/macos/spl/sys/fcntl.h \ + $(top_srcdir)/include/os/macos/spl/sys/file.h \ + $(top_srcdir)/include/os/macos/spl/sys/inttypes.h \ + $(top_srcdir)/include/os/macos/spl/sys/isa_defs.h \ + $(top_srcdir)/include/os/macos/spl/sys/kmem.h \ + $(top_srcdir)/include/os/macos/spl/sys/kmem_impl.h \ + $(top_srcdir)/include/os/macos/spl/sys/kstat.h \ + $(top_srcdir)/include/os/macos/spl/sys/list.h \ + $(top_srcdir)/include/os/macos/spl/sys/mod_os.h \ + $(top_srcdir)/include/os/macos/spl/sys/mutex.h \ + $(top_srcdir)/include/os/macos/spl/sys/param.h \ + $(top_srcdir)/include/os/macos/spl/sys/policy.h \ + $(top_srcdir)/include/os/macos/spl/sys/priv.h \ + $(top_srcdir)/include/os/macos/spl/sys/proc.h \ + $(top_srcdir)/include/os/macos/spl/sys/processor.h \ + $(top_srcdir)/include/os/macos/spl/sys/random.h \ + $(top_srcdir)/include/os/macos/spl/sys/rwlock.h \ + $(top_srcdir)/include/os/macos/spl/sys/seg_kmem.h \ + $(top_srcdir)/include/os/macos/spl/sys/signal.h \ + $(top_srcdir)/include/os/macos/spl/sys/stropts.h \ + $(top_srcdir)/include/os/macos/spl/sys/sunddi.h \ + $(top_srcdir)/include/os/macos/spl/sys/sysmacros.h \ + $(top_srcdir)/include/os/macos/spl/sys/systeminfo.h \ + $(top_srcdir)/include/os/macos/spl/sys/systm.h \ + $(top_srcdir)/include/os/macos/spl/sys/taskq.h \ + $(top_srcdir)/include/os/macos/spl/sys/taskq_impl.h \ + $(top_srcdir)/include/os/macos/spl/sys/thread.h \ + $(top_srcdir)/include/os/macos/spl/sys/time.h \ + $(top_srcdir)/include/os/macos/spl/sys/timer.h \ + $(top_srcdir)/include/os/macos/spl/sys/tsd.h \ + $(top_srcdir)/include/os/macos/spl/sys/types.h \ + $(top_srcdir)/include/os/macos/spl/sys/utsname.h \ + $(top_srcdir)/include/os/macos/spl/sys/varargs.h \ + $(top_srcdir)/include/os/macos/spl/sys/vfs.h \ + $(top_srcdir)/include/os/macos/spl/sys/vmem.h \ + $(top_srcdir)/include/os/macos/spl/sys/vmem_impl.h \ + $(top_srcdir)/include/os/macos/spl/sys/vmsystm.h \ + $(top_srcdir)/include/os/macos/spl/sys/vnode.h \ + $(top_srcdir)/include/os/macos/spl/sys/zone.h diff --git a/include/os/macos/spl/sys/acl.h b/include/os/macos/spl/sys/acl.h new file mode 100644 index 000000000000..840ba7f43cb8 --- /dev/null +++ b/include/os/macos/spl/sys/acl.h @@ -0,0 +1,127 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SPL_ACL_H +#define _SPL_ACL_H + +#include + +typedef struct ace { + uid_t a_who; + uint32_t a_access_mask; + uint16_t a_flags; + uint16_t a_type; +} ace_t; + +typedef struct ace_object { + uid_t a_who; /* uid or gid */ + uint32_t a_access_mask; /* read,write,... */ + uint16_t a_flags; /* see below */ + uint16_t a_type; /* allow or deny */ + uint8_t a_obj_type[16]; /* obj type */ + uint8_t a_inherit_obj_type[16]; /* inherit obj */ +} ace_object_t; + +#define MAX_ACL_ENTRIES 1024 + +#define ACE_READ_DATA 0x00000001 +#define ACE_LIST_DIRECTORY 0x00000001 +#define ACE_WRITE_DATA 0x00000002 +#define ACE_ADD_FILE 0x00000002 +#define ACE_APPEND_DATA 0x00000004 +#define ACE_ADD_SUBDIRECTORY 0x00000004 +#define ACE_READ_NAMED_ATTRS 0x00000008 +#define ACE_WRITE_NAMED_ATTRS 0x00000010 +#define ACE_EXECUTE 0x00000020 +#define ACE_DELETE_CHILD 0x00000040 +#define ACE_READ_ATTRIBUTES 0x00000080 +#define ACE_WRITE_ATTRIBUTES 0x00000100 +#define ACE_DELETE 0x00010000 +#define ACE_READ_ACL 0x00020000 +#define ACE_WRITE_ACL 0x00040000 +#define ACE_WRITE_OWNER 0x00080000 +#define ACE_SYNCHRONIZE 0x00100000 + +#define ACE_FILE_INHERIT_ACE 0x0001 +#define ACE_DIRECTORY_INHERIT_ACE 0x0002 +#define ACE_NO_PROPAGATE_INHERIT_ACE 0x0004 +#define ACE_INHERIT_ONLY_ACE 0x0008 +#define ACE_SUCCESSFUL_ACCESS_ACE_FLAG 0x0010 +#define ACE_FAILED_ACCESS_ACE_FLAG 0x0020 +#define ACE_IDENTIFIER_GROUP 0x0040 +#define ACE_INHERITED_ACE 0x0080 +#define ACE_OWNER 0x1000 +#define ACE_GROUP 0x2000 +#define ACE_EVERYONE 0x4000 + +#define ACE_ACCESS_ALLOWED_ACE_TYPE 0x0000 +#define ACE_ACCESS_DENIED_ACE_TYPE 0x0001 +#define ACE_SYSTEM_AUDIT_ACE_TYPE 0x0002 +#define ACE_SYSTEM_ALARM_ACE_TYPE 0x0003 + +#define ACL_AUTO_INHERIT 0x0001 +#define ACL_PROTECTED 0x0002 +#define ACL_DEFAULTED 0x0004 +#define ACL_FLAGS_ALL (ACL_AUTO_INHERIT|ACL_PROTECTED|ACL_DEFAULTED) + +#define ACE_ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04 +#define ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 +#define ACE_ACCESS_DENIED_OBJECT_ACE_TYPE 0x06 +#define ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07 +#define ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08 +#define ACE_ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09 +#define ACE_ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A +#define ACE_ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B +#define ACE_ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C +#define ACE_SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D +#define ACE_SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E +#define ACE_SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F +#define ACE_SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 + +#define ACE_ALL_TYPES 0x001F + +#define ACE_TYPE_FLAGS (ACE_OWNER|ACE_GROUP|ACE_EVERYONE|ACE_IDENTIFIER_GROUP) + +#define ACE_ALL_PERMS (ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \ + ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_READ_NAMED_ATTRS|\ + ACE_WRITE_NAMED_ATTRS|ACE_EXECUTE|ACE_DELETE_CHILD|ACE_READ_ATTRIBUTES|\ + ACE_WRITE_ATTRIBUTES|ACE_DELETE|ACE_READ_ACL|ACE_WRITE_ACL| \ + ACE_WRITE_OWNER|ACE_SYNCHRONIZE) + +#define VSA_ACE 0x0010 +#define VSA_ACECNT 0x0020 +#define VSA_ACE_ALLTYPES 0x0040 +#define VSA_ACE_ACLFLAGS 0x0080 + +typedef struct trivial_acl { + uint32_t allow0; /* allow mask for bits only in owner */ + uint32_t deny1; /* deny mask for bits not in owner */ + uint32_t deny2; /* deny mask for bits not in group */ + uint32_t owner; /* allow mask matching mode */ + uint32_t group; /* allow mask matching mode */ + uint32_t everyone; /* allow mask matching mode */ +} trivial_acl_t; + +#endif /* _SPL_ACL_H */ diff --git a/include/os/macos/spl/sys/atomic.h b/include/os/macos/spl/sys/atomic.h new file mode 100644 index 000000000000..0fcc072680fa --- /dev/null +++ b/include/os/macos/spl/sys/atomic.h @@ -0,0 +1,288 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * OSX Atomic functions using clang builtins. + * + * Jorgen Lundman + * + */ + +#ifndef _SPL_ATOMIC_H +#define _SPL_ATOMIC_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Increment target + */ +static inline void +atomic_inc_8(volatile uint8_t *target) +{ + __sync_fetch_and_add(target, 1); +} + +static inline void +atomic_inc_16(volatile uint16_t *target) +{ + __sync_fetch_and_add(target, 1); +} + +static inline void +atomic_inc_32(volatile uint32_t *target) +{ + __sync_fetch_and_add(target, 1); +} + +static inline void +atomic_inc_64(volatile uint64_t *target) +{ + __sync_fetch_and_add(target, 1); +} + +static inline int32_t +atomic_inc_32_nv(volatile uint32_t *target) +{ + return (__sync_add_and_fetch(target, 1)); +} + +static inline int64_t +atomic_inc_64_nv(volatile uint64_t *target) +{ + return (__sync_add_and_fetch(target, 1)); +} + + + +/* + * Decrement target + */ +static inline void +atomic_dec_8(volatile uint8_t *target) +{ + __sync_fetch_and_sub(target, 1); +} + +static inline void +atomic_dec_16(volatile uint16_t *target) +{ + __sync_fetch_and_sub(target, 1); +} + +static inline void +atomic_dec_32(volatile uint32_t *target) +{ + __sync_fetch_and_sub(target, 1); +} + +static inline void +atomic_dec_64(volatile uint64_t *target) +{ + __sync_fetch_and_sub(target, 1); +} + +static inline int32_t +atomic_dec_32_nv(volatile uint32_t *target) +{ + return (__sync_sub_and_fetch(target, 1)); +} + +static inline int64_t +atomic_dec_64_nv(volatile uint64_t *target) +{ + return (__sync_sub_and_fetch(target, 1)); +} + +/* + * Add delta to target + */ +static inline void +atomic_add_8(volatile uint8_t *target, int8_t delta) +{ + __sync_add_and_fetch(target, delta); +} + +static inline void +atomic_add_16(volatile uint16_t *target, int16_t delta) +{ + __sync_add_and_fetch(target, delta); +} + +static inline void +atomic_add_32(volatile uint32_t *target, int32_t delta) +{ + __sync_add_and_fetch(target, delta); +} + +static inline uint32_t +atomic_add_32_nv(volatile uint32_t *target, int32_t delta) +{ + return (__sync_add_and_fetch(target, delta)); +} + +static inline void +atomic_add_64(volatile uint64_t *target, int64_t delta) +{ + __sync_add_and_fetch(target, delta); +} + +static inline uint64_t +atomic_add_64_nv(volatile uint64_t *target, int64_t delta) +{ + return (__sync_add_and_fetch(target, delta)); +} + + +/* + * Subtract delta to target + */ +static inline void +atomic_sub_8(volatile uint8_t *target, int8_t delta) +{ + __sync_sub_and_fetch(target, delta); +} + +static inline void +atomic_sub_16(volatile uint16_t *target, int16_t delta) +{ + __sync_sub_and_fetch(target, delta); +} + +static inline void +atomic_sub_32(volatile uint32_t *target, int32_t delta) +{ + __sync_sub_and_fetch(target, delta); +} + +static inline void +atomic_sub_64(volatile uint64_t *target, int64_t delta) +{ + __sync_sub_and_fetch(target, delta); +} + +static inline uint64_t +atomic_sub_64_nv(volatile uint64_t *target, int64_t delta) +{ + return (__sync_sub_and_fetch(target, delta)); +} + +/* + * logical OR bits with target + */ +static inline void +atomic_or_8(volatile uint8_t *target, uint8_t mask) +{ + __sync_or_and_fetch(target, mask); +} + +static inline void +atomic_or_16(volatile uint16_t *target, uint16_t mask) +{ + __sync_or_and_fetch(target, mask); +} + +static inline void +atomic_or_32(volatile uint32_t *target, uint32_t mask) +{ + __sync_or_and_fetch(target, mask); +} + +/* + * logical AND bits with target + */ +static inline void +atomic_and_8(volatile uint8_t *target, uint8_t mask) +{ + __sync_and_and_fetch(target, mask); +} + +static inline void +atomic_and_16(volatile uint16_t *target, uint16_t mask) +{ + __sync_and_and_fetch(target, mask); +} + +static inline void +atomic_and_32(volatile uint32_t *target, uint32_t mask) +{ + __sync_and_and_fetch(target, mask); +} + +/* + * Compare And Set + * if *arg1 == arg2, then set *arg1 = arg3; return old value. + */ +static inline uint8_t +atomic_cas_8(volatile uint8_t *_target, uint8_t _cmp, uint8_t _new) +{ + return (__sync_val_compare_and_swap(_target, _cmp, _new)); +} + +static inline uint16_t +atomic_cas_16(volatile uint16_t *_target, uint16_t _cmp, uint16_t _new) +{ + return (__sync_val_compare_and_swap(_target, _cmp, _new)); +} + +static inline uint32_t +atomic_cas_32(volatile uint32_t *_target, uint32_t _cmp, uint32_t _new) +{ + return (__sync_val_compare_and_swap(_target, _cmp, _new)); +} + +static inline uint64_t +atomic_cas_64(volatile uint64_t *_target, uint64_t _cmp, uint64_t _new) +{ + return (__sync_val_compare_and_swap(_target, _cmp, _new)); +} + +static inline uint32_t +atomic_swap_32(volatile uint32_t *_target, uint32_t _new) +{ + return (__sync_lock_test_and_set(_target, _new)); +} + +static inline uint64_t +atomic_swap_64(volatile uint64_t *_target, uint64_t _new) +{ + return (__sync_lock_test_and_set(_target, _new)); +} + +extern void *atomic_cas_ptr(volatile void *_target, void *_cmp, void *_new); + +static inline void +membar_producer(void) +{ + __c11_atomic_thread_fence(__ATOMIC_SEQ_CST); +} + +#ifdef __cplusplus +} +#endif + +#endif /* _SPL_ATOMIC_H */ diff --git a/include/os/macos/spl/sys/byteorder.h b/include/os/macos/spl/sys/byteorder.h new file mode 100644 index 000000000000..498e467f2718 --- /dev/null +++ b/include/os/macos/spl/sys/byteorder.h @@ -0,0 +1,70 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_BYTEORDER_H +#define _SPL_BYTEORDER_H + +#include +#include + +#include +#include + +#define LE_16(x) OSSwapHostToLittleInt16(x) +#define LE_32(x) OSSwapHostToLittleInt32(x) +#define LE_64(x) OSSwapHostToLittleInt64(x) +#define BE_16(x) OSSwapHostToBigInt16(x) +#define BE_32(x) OSSwapHostToBigInt32(x) +#define BE_64(x) OSSwapHostToBigInt64(x) + +#define BE_IN8(xa) \ + *((uint8_t *)(xa)) + +#define BE_IN16(xa) \ + (((uint16_t)BE_IN8(xa) << 8) | BE_IN8((uint8_t *)(xa)+1)) + +#define BE_IN32(xa) \ + (((uint32_t)BE_IN16(xa) << 16) | BE_IN16((uint8_t *)(xa)+2)) + + +/* 10.8 is lacking in htonll */ +#if !defined(htonll) +#define htonll(x) __DARWIN_OSSwapInt64(x) +#endif +#if !defined(ntohll) +#define ntohll(x) __DARWIN_OSSwapInt64(x) +#endif + +#ifdef __LITTLE_ENDIAN__ +#define _ZFS_LITTLE_ENDIAN +#endif + +#ifdef __BIG_ENDIAN__ +#define _ZFS_BIG_ENDIAN +#endif + +#endif /* SPL_BYTEORDER_H */ diff --git a/include/os/macos/spl/sys/callb.h b/include/os/macos/spl/sys/callb.h new file mode 100644 index 000000000000..3d86b9d41c05 --- /dev/null +++ b/include/os/macos/spl/sys/callb.h @@ -0,0 +1,66 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SPL_CALLB_H +#define _SPL_CALLB_H + +#include + +#define CALLB_CPR_ASSERT(cp) ASSERT(MUTEX_HELD((cp)->cc_lockp)); + +typedef struct callb_cpr { + kmutex_t *cc_lockp; +} callb_cpr_t; + +#define CALLB_CPR_INIT(cp, lockp, func, name) { \ + (cp)->cc_lockp = lockp; \ +} + +#define CALLB_CPR_SAFE_BEGIN(cp) { \ + CALLB_CPR_ASSERT(cp); \ +} + +#define CALLB_CPR_SAFE_END(cp, lockp) { \ + CALLB_CPR_ASSERT(cp); \ +} + +#define CALLB_CPR_EXIT(cp) { \ + ASSERT(MUTEX_HELD((cp)->cc_lockp)); \ + mutex_exit((cp)->cc_lockp); \ +} + + +#define CALLOUT_FLAG_ROUNDUP 0x1 +#define CALLOUT_FLAG_ABSOLUTE 0x2 +#define CALLOUT_FLAG_HRESTIME 0x4 +#define CALLOUT_FLAG_32BIT 0x8 + +/* Move me to more correct "sys/callo.h" file when convenient. */ +#define CALLOUT_NORMAL 1 +typedef uint64_t callout_id_t; +callout_id_t timeout_generic(int, void (*)(void *), void *, hrtime_t, + hrtime_t, int); + +#endif /* _SPL_CALLB_H */ diff --git a/include/os/macos/spl/sys/cmn_err.h b/include/os/macos/spl/sys/cmn_err.h new file mode 100644 index 000000000000..e4343a97a763 --- /dev/null +++ b/include/os/macos/spl/sys/cmn_err.h @@ -0,0 +1,54 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + + +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + */ + +#ifndef _SPL_CMN_ERR_H +#define _SPL_CMN_ERR_H + +#include +#include + +#define CE_CONT 0 /* continuation */ +#define CE_NOTE 1 /* notice */ +#define CE_WARN 2 /* warning */ +#define CE_PANIC 3 /* panic */ +#define CE_IGNORE 4 /* print nothing */ + +#ifdef _KERNEL + +extern void vcmn_err(int, const char *, __va_list); +extern void cmn_err(int, const char *, ...); + +#endif /* _KERNEL */ + +#define fm_panic panic + +#endif /* SPL_CMN_ERR_H */ diff --git a/include/os/macos/spl/sys/condvar.h b/include/os/macos/spl/sys/condvar.h new file mode 100644 index 000000000000..8bf89b1be748 --- /dev/null +++ b/include/os/macos/spl/sys/condvar.h @@ -0,0 +1,105 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#ifndef OSX_CONDVAR_H +#define OSX_CONDVAR_H + +#include +#include + +#define hz 100 /* sysctl kern.clockrate */ + +typedef enum { + CV_DEFAULT, + CV_DRIVER +} kcv_type_t; + + +struct cv { + uint64_t pad; +}; + +typedef struct cv kcondvar_t; + +void spl_cv_init(kcondvar_t *cvp, char *name, kcv_type_t type, void *arg); +void spl_cv_destroy(kcondvar_t *cvp); +void spl_cv_signal(kcondvar_t *cvp); +void spl_cv_broadcast(kcondvar_t *cvp); +int spl_cv_wait(kcondvar_t *cvp, kmutex_t *mp, int flags, const char *msg); +int spl_cv_timedwait(kcondvar_t *, kmutex_t *, clock_t, int, const char *msg); +int cv_timedwait_hires(kcondvar_t *cvp, kmutex_t *mp, + hrtime_t tim, hrtime_t res, int flag); + +/* + * Use these wrapper macros to obtain the CV variable + * name to make ZFS more gdb debugging friendly! + * This name shows up as a thread's wait_event string. + */ +#define cv_wait(cvp, mp) \ + (void) spl_cv_wait((cvp), (mp), PRIBIO, #cvp) + +#define cv_wait_io(cvp, mp) \ + (void) spl_cv_wait((cvp), (mp), PRIBIO, #cvp) + +#define cv_wait_idle(cvp, mp) \ + (void) spl_cv_wait((cvp), (mp), PRIBIO, #cvp) + +#define cv_timedwait(cvp, mp, tim) \ + spl_cv_timedwait((cvp), (mp), (tim), PRIBIO, #cvp) + +#define cv_timedwait_io(cvp, mp, tim) \ + spl_cv_timedwait((cvp), (mp), (tim), PRIBIO, #cvp) + +#define cv_timedwait_idle(cvp, mp, tim) \ + spl_cv_timedwait((cvp), (mp), (tim), PRIBIO, #cvp) + +#define cv_wait_interruptible(cvp, mp) \ + (void) spl_cv_wait((cvp), (mp), PRIBIO|PCATCH, #cvp) + +#define cv_timedwait_interruptible(cvp, mp, tim) \ + spl_cv_timedwait((cvp), (mp), (tim), PRIBIO|PCATCH, #cvp) + +/* cv_wait_sig is the correct name for cv_wait_interruptible */ +#define cv_wait_sig(cvp, mp) \ + spl_cv_wait((cvp), (mp), PRIBIO|PCATCH, #cvp) + +#define cv_wait_io_sig(cvp, mp) \ + spl_cv_wait((cvp), (mp), PRIBIO|PCATCH, #cvp) + +#define cv_timedwait_sig(cvp, mp, tim) \ + spl_cv_timedwait((cvp), (mp), (tim), PRIBIO|PCATCH, #cvp) + +#define TICK_TO_NSEC(tick) ((hrtime_t)(tick) * 1000000000 / hz) +#define cv_reltimedwait(cvp, mp, tim, type) \ + cv_timedwait_hires((cvp), (mp), TICK_TO_NSEC((tim)), 0, 0) + +#define cv_timedwait_sig_hires(cvp, mp, tim, res, flag) \ + cv_timedwait_hires(cvp, mp, tim, res, (flag)|PCATCH) + +#define cv_timedwait_idle_hires(cvp, mp, tim, res, flag) \ + cv_timedwait_hires(cvp, mp, tim, res, (flag)|PCATCH) + +#define cv_init spl_cv_init +#define cv_destroy spl_cv_destroy +#define cv_broadcast spl_cv_broadcast +#define cv_signal spl_cv_signal + +#endif diff --git a/include/os/macos/spl/sys/console.h b/include/os/macos/spl/sys/console.h new file mode 100644 index 000000000000..57c962210512 --- /dev/null +++ b/include/os/macos/spl/sys/console.h @@ -0,0 +1,41 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#ifndef _SPL_SYS_CONSOLE_H +#define _SPL_SYS_CONSOLE_H + +static inline void +console_vprintf(const char *fmt, va_list args) +{ + vprintf(fmt, args); +} + +static inline void +console_printf(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + console_vprintf(fmt, args); + va_end(args); +} + +#endif /* _SPL_SYS_CONSOLE_H */ diff --git a/include/os/macos/spl/sys/cred.h b/include/os/macos/spl/sys/cred.h new file mode 100644 index 000000000000..f8f3f3b5dd79 --- /dev/null +++ b/include/os/macos/spl/sys/cred.h @@ -0,0 +1,71 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_CRED_H +#define _SPL_CRED_H + +#include +#include +#include + +typedef struct ucred cred_t; + +#define kcred (cred_t *)NOCRED +#define CRED() (cred_t *)kauth_cred_get() +#define KUID_TO_SUID(x) (x) +#define KGID_TO_SGID(x) (x) + +#include +#include + +// Older OSX API +#if !(MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) +#define kauth_cred_getruid(x) (x)->cr_ruid +#define kauth_cred_getrgid(x) (x)->cr_rgid +#define kauth_cred_getsvuid(x) (x)->cr_svuid +#define kauth_cred_getsvgid(x) (x)->cr_svgid +#endif + + +extern void crhold(cred_t *cr); +extern void crfree(cred_t *cr); +extern uid_t crgetuid(const cred_t *cr); +extern uid_t crgetruid(const cred_t *cr); +extern uid_t crgetsuid(const cred_t *cr); +extern uid_t crgetfsuid(const cred_t *cr); +extern gid_t crgetgid(const cred_t *cr); +extern gid_t crgetrgid(const cred_t *cr); +extern gid_t crgetsgid(const cred_t *cr); +extern gid_t crgetfsgid(const cred_t *cr); +extern int crgetngroups(const cred_t *cr); +extern gid_t *crgetgroups(const cred_t *cr); +extern void crgetgroupsfree(gid_t *gids); +extern int spl_cred_ismember_gid(cred_t *cr, gid_t gid); + +#define crgetsid(cred, i) (NULL) + +#endif /* _SPL_CRED_H */ diff --git a/include/os/macos/spl/sys/ctype.h b/include/os/macos/spl/sys/ctype.h new file mode 100644 index 000000000000..74554873305c --- /dev/null +++ b/include/os/macos/spl/sys/ctype.h @@ -0,0 +1,27 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _SPL_CTYPE_H +#define _SPL_CTYPE_H + +#define iscntrl(C) (uchar(C) <= 0x1f || uchar(C) == 0x7f) + +#endif diff --git a/include/os/macos/spl/sys/debug.h b/include/os/macos/spl/sys/debug.h new file mode 100644 index 000000000000..b9ff220b6e21 --- /dev/null +++ b/include/os/macos/spl/sys/debug.h @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2020 iXsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Available Solaris debug functions. All of the ASSERT() macros will be + * compiled out when NDEBUG is defined, this is the default behavior for + * the SPL. To enable assertions use the --enable-debug with configure. + * The VERIFY() functions are never compiled out and cannot be disabled. + * + * PANIC() - Panic the node and print message. + * ASSERT() - Assert X is true, if not panic. + * ASSERT3B() - Assert boolean X OP Y is true, if not panic. + * ASSERT3S() - Assert signed X OP Y is true, if not panic. + * ASSERT3U() - Assert unsigned X OP Y is true, if not panic. + * ASSERT3P() - Assert pointer X OP Y is true, if not panic. + * ASSERT0() - Assert value is zero, if not panic. + * VERIFY() - Verify X is true, if not panic. + * VERIFY3B() - Verify boolean X OP Y is true, if not panic. + * VERIFY3S() - Verify signed X OP Y is true, if not panic. + * VERIFY3U() - Verify unsigned X OP Y is true, if not panic. + * VERIFY3P() - Verify pointer X OP Y is true, if not panic. + * VERIFY0() - Verify value is zero, if not panic. + */ + +#ifndef _SPL_DEBUG_H +#define _SPL_DEBUG_H + +#include + +/* SPL has own 'dprintf' as zfs_debug.c version uses mutex */ +#ifdef __cplusplus +extern "C" { +#endif + +extern int zfs_flags; + +/* Simple dprintf for SPL only */ +#ifndef dprintf +#define dprintf(...) \ + if (zfs_flags & 1) \ + printf(__VA_ARGS__) +#endif + +#ifndef __printflike +#define __printflike(a, b) __attribute__((__format__(__printf__, a, b))) +#endif + +/* + * Common DEBUG functionality. + */ +int spl_panic(const char *file, const char *func, int line, + const char *fmt, ...); +void spl_dumpstack(void); + +void spl_backtrace(char *thesignal); +int getpcstack(uintptr_t *pcstack, int pcstack_limit); +void print_symbol(uintptr_t symbol); + +#ifndef expect +#define expect(expr, value) (__builtin_expect((expr), (value))) +#endif +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +#ifndef __maybe_unused +#define __maybe_unused __attribute__((unused)) +#endif + +/* BEGIN CSTYLED */ +#define PANIC(fmt, a...) \ + spl_panic(__FILE__, __FUNCTION__, __LINE__, fmt, ## a) + +#define VERIFY(cond) \ + (void) (unlikely(!(cond)) && \ + spl_panic(__FILE__, __FUNCTION__, __LINE__, \ + "%s", "VERIFY(" #cond ") failed\n")) + +#define VERIFY3B(LEFT, OP, RIGHT) do { \ + boolean_t _verify3_left = (boolean_t)(LEFT); \ + boolean_t _verify3_right = (boolean_t)(RIGHT); \ + if (!(_verify3_left OP _verify3_right)) \ + spl_panic(__FILE__, __FUNCTION__, __LINE__, \ + "VERIFY3(" #LEFT " " #OP " " #RIGHT ") " \ + "failed (%d " #OP " %d)\n", \ + (boolean_t) (_verify3_left), \ + (boolean_t) (_verify3_right)); \ + } while (0) + +#define VERIFY3S(LEFT, OP, RIGHT) do { \ + int64_t _verify3_left = (int64_t)(LEFT); \ + int64_t _verify3_right = (int64_t)(RIGHT); \ + if (!(_verify3_left OP _verify3_right)) \ + spl_panic(__FILE__, __FUNCTION__, __LINE__, \ + "VERIFY3(" #LEFT " " #OP " " #RIGHT ") " \ + "failed (%lld " #OP " %lld)\n", \ + (long long) (_verify3_left), \ + (long long) (_verify3_right)); \ + } while (0) + +#define VERIFY3U(LEFT, OP, RIGHT) do { \ + uint64_t _verify3_left = (uint64_t)(LEFT); \ + uint64_t _verify3_right = (uint64_t)(RIGHT); \ + if (!(_verify3_left OP _verify3_right)) \ + spl_panic(__FILE__, __FUNCTION__, __LINE__, \ + "VERIFY3(" #LEFT " " #OP " " #RIGHT ") " \ + "failed (%llu " #OP " %llu)\n", \ + (unsigned long long) (_verify3_left), \ + (unsigned long long) (_verify3_right)); \ + } while (0) + +#define VERIFY3P(LEFT, OP, RIGHT) do { \ + uintptr_t _verify3_left = (uintptr_t)(LEFT); \ + uintptr_t _verify3_right = (uintptr_t)(RIGHT); \ + if (!(_verify3_left OP _verify3_right)) \ + spl_panic(__FILE__, __FUNCTION__, __LINE__, \ + "VERIFY3(" #LEFT " " #OP " " #RIGHT ") " \ + "failed (%px " #OP " %px)\n", \ + (void *) (_verify3_left), \ + (void *) (_verify3_right)); \ + } while (0) + +#define VERIFY0(RIGHT) do { \ + int64_t _verify3_left = (int64_t)(0); \ + int64_t _verify3_right = (int64_t)(RIGHT); \ + if (!(_verify3_left == _verify3_right)) \ + spl_panic(__FILE__, __FUNCTION__, __LINE__, \ + "VERIFY3(0 == " #RIGHT ") " \ + "failed (0 == %lld)\n", \ + (long long) (_verify3_right)); \ + } while (0) + +#define CTASSERT_GLOBAL(x) _CTASSERT(x, __LINE__) +#define CTASSERT(x) { _CTASSERT(x, __LINE__); } +#define _CTASSERT(x, y) __CTASSERT(x, y) +#define __CTASSERT(x, y) \ + typedef char __attribute__ ((unused)) \ + __compile_time_assertion__ ## y[(x) ? 1 : -1] + + + +/* + * Debugging disabled (--disable-debug) + */ +#ifdef NDEBUG + +#define ASSERT(x) ((void)0) +#define ASSERT3B(x,y,z) ((void)0) +#define ASSERT3S(x,y,z) ((void)0) +#define ASSERT3U(x,y,z) ((void)0) +#define ASSERT3P(x,y,z) ((void)0) +#define ASSERT0(x) ((void)0) +#define ASSERTV(x) ((void)0) +#define IMPLY(A, B) ((void)0) +#define EQUIV(A, B) ((void)0) + +/* + * Debugging enabled (--enable-debug) + */ +#else + +#ifdef MACOS_ASSERT_SHOULD_PANIC +#define ASSERT3B VERIFY3B +#define ASSERT3S VERIFY3S +#define ASSERT3U VERIFY3U +#define ASSERT3P VERIFY3P +#define ASSERT0 VERIFY0 +#define ASSERT VERIFY +#define ASSERTV(X) X +#define IMPLY(A, B) \ + ((void)(((!(A)) || (B)) || \ + spl_panic(__FILE__, __FUNCTION__, __LINE__, \ + "(" #A ") implies (" #B ")"))) +#define EQUIV(A, B) \ + ((void)((!!(A) == !!(B)) || \ + spl_panic(__FILE__, __FUNCTION__, __LINE__, \ + "(" #A ") is equivalent to (" #B ")"))) +/* END CSTYLED */ +#else /* MACOS_ASSERT_SHOULD_PANIC */ + +#define PRINT printf + +__attribute__((noinline)) int assfail(const char *str, const char *file, + unsigned int line) __attribute__((optnone)); + +#define ASSERT(cond) \ + (void) (unlikely(!(cond)) && assfail(#cond, __FILE__, __LINE__) && \ + PRINT("ZFS: %s %s %d : %s\n", __FILE__, __FUNCTION__, __LINE__, \ + "ASSERTION(" #cond ") failed\n")) + +#define ASSERT3_IMPL(LEFT, OP, RIGHT, TYPE, FMT, CAST) \ + do { \ + if (!((TYPE)(LEFT) OP(TYPE)(RIGHT)) && \ + assfail(#LEFT #OP #RIGHT, __FILE__, __LINE__)) \ + PRINT("ZFS: %s %s %d : ASSERT3( %s " #OP " %s) " \ + "failed (" FMT " " #OP " " FMT ")\n", \ + __FILE__, __FUNCTION__, \ + __LINE__, #LEFT, #RIGHT, \ + CAST(LEFT), CAST(RIGHT)); \ + } while (0) + + +#define ASSERTF(cond, fmt, a...) \ + do { \ + if (unlikely(!(cond))) \ + panic("ZFS: ASSERTION(" #cond ") failed: " fmt, ## a); \ + } while (0) + + +#define ASSERT3B(x, y, z) ASSERT3_IMPL(x, y, z, int64_t, "%u", \ + (boolean_t)) +#define ASSERT3S(x, y, z) ASSERT3_IMPL(x, y, z, int64_t, "%lld", \ + (long long)) +#define ASSERT3U(x, y, z) ASSERT3_IMPL(x, y, z, uint64_t, "%llu", \ + (unsigned long long)) + +#define ASSERT3P(x, y, z) ASSERT3_IMPL(x, y, z, uintptr_t, "%p", (void *)) +#define ASSERT0(x) ASSERT3_IMPL(0, ==, x, int64_t, "%lld", (long long)) +#define ASSERTV(x) x + + +/* + * IMPLY and EQUIV are assertions of the form: + * + * if (a) then (b) + * and + * if (a) then (b) *AND* if (b) then (a) + */ +#define IMPLY(A, B) \ + ((void)(((!(A)) || (B)) || \ + printf("%s:%d (" #A ") implies (" #B "): failed\n", \ + __FILE__, __LINE__))) + +#define EQUIV(A, B) \ + ((void)((!!(A) == !!(B)) || \ + printf("%s:%d (" #A ") is equivalent to (" #B "): failed\n", \ + __FILE__, __LINE__))) + +#endif /* MACOS_ASSERT_SHOULD_PANIC */ +#endif /* NDEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* SPL_DEBUG_H */ diff --git a/include/os/macos/spl/sys/disp.h b/include/os/macos/spl/sys/disp.h new file mode 100644 index 000000000000..3b1bcbb25cc9 --- /dev/null +++ b/include/os/macos/spl/sys/disp.h @@ -0,0 +1,25 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _SPL_DISP_H +#define _SPL_DISP_H + +#endif diff --git a/include/os/macos/spl/sys/dkio.h b/include/os/macos/spl/sys/dkio.h new file mode 100644 index 000000000000..d10314b3e427 --- /dev/null +++ b/include/os/macos/spl/sys/dkio.h @@ -0,0 +1,527 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * $FreeBSD$ + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _OPENSOLARIS_SYS_DKIO_H_ +#define _OPENSOLARIS_SYS_DKIO_H_ + +#include /* Needed for NDKMAP define */ +#include /* Needed for NDKMAP define */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_SUNOS_VTOC_16) +#define NDKMAP 16 /* # of logical partitions */ +#define DK_LABEL_LOC 1 /* location of disk label */ +#elif defined(_SUNOS_VTOC_8) +#define NDKMAP 8 /* # of logical partitions */ +#define DK_LABEL_LOC 0 /* location of disk label */ +#else +#error "No VTOC format defined." +#endif + +/* + * Structures and definitions for disk io control commands + */ + +/* + * Structures used as data by ioctl calls. + */ + +#define DK_DEVLEN 16 /* device name max length, including */ + /* unit # & NULL (ie - "xyc1") */ + +/* + * Used for controller info + */ +struct dk_cinfo { + char dki_cname[DK_DEVLEN]; /* controller name (no unit #) */ + ushort_t dki_ctype; /* controller type */ + ushort_t dki_flags; /* flags */ + ushort_t dki_cnum; /* controller number */ + uint_t dki_addr; /* controller address */ + uint_t dki_space; /* controller bus type */ + uint_t dki_prio; /* interrupt priority */ + uint_t dki_vec; /* interrupt vector */ + char dki_dname[DK_DEVLEN]; /* drive name (no unit #) */ + uint_t dki_unit; /* unit number */ + uint_t dki_slave; /* slave number */ + ushort_t dki_partition; /* partition number */ + ushort_t dki_maxtransfer; /* max. transfer size in DEV_BSIZE */ +}; + +/* + * Controller types + */ +#define DKC_UNKNOWN 0 +#define DKC_CDROM 1 /* CD-ROM, SCSI or otherwise */ +#define DKC_WDC2880 2 +#define DKC_XXX_0 3 /* unassigned */ +#define DKC_XXX_1 4 /* unassigned */ +#define DKC_DSD5215 5 +#define DKC_ACB4000 7 +#define DKC_MD21 8 +#define DKC_XXX_2 9 /* unassigned */ +#define DKC_NCRFLOPPY 10 +#define DKC_SMSFLOPPY 12 +#define DKC_SCSI_CCS 13 /* SCSI CCS compatible */ +#define DKC_INTEL82072 14 /* native floppy chip */ +#define DKC_MD 16 /* meta-disk (virtual-disk) driver */ +#define DKC_INTEL82077 19 /* 82077 floppy disk controller */ +#define DKC_DIRECT 20 /* Intel direct attached device i.e. IDE */ +#define DKC_PCMCIA_MEM 21 /* PCMCIA memory disk-like type */ +#define DKC_PCMCIA_ATA 22 /* PCMCIA AT Attached type */ +#define DKC_VBD 23 /* virtual block device */ + +/* + * Sun reserves up through 1023 + */ + +#define DKC_CUSTOMER_BASE 1024 + +/* + * Flags + */ +#define DKI_BAD144 0x01 /* use DEC std 144 bad sector fwding */ +#define DKI_MAPTRK 0x02 /* controller does track mapping */ +#define DKI_FMTTRK 0x04 /* formats only full track at a time */ +#define DKI_FMTVOL 0x08 /* formats only full volume at a time */ +#define DKI_FMTCYL 0x10 /* formats only full cylinders at a time */ +#define DKI_HEXUNIT 0x20 /* unit number is printed as 3 hex digits */ +#define DKI_PCMCIA_PFD 0x40 /* PCMCIA pseudo-floppy memory card */ + +/* + * partition headers: section 1 + * Returned in struct dk_allmap by ioctl DKIOC[SG]APART (dkio(7I)) + */ +struct dk_map { + uint64_t dkl_cylno; /* starting cylinder */ + uint64_t dkl_nblk; /* number of blocks; if == 0, */ + /* partition is undefined */ +}; + +/* + * Used for all partitions + */ +struct dk_allmap { + struct dk_map dka_map[NDKMAP]; +}; + +#if defined(_SYSCALL32) +struct dk_allmap32 { + struct dk_map32 dka_map[NDKMAP]; +}; +#endif /* _SYSCALL32 */ + +/* + * Definition of a disk's geometry + */ +struct dk_geom { + unsigned short dkg_ncyl; /* # of data cylinders */ + unsigned short dkg_acyl; /* # of alternate cylinders */ + unsigned short dkg_bcyl; /* cyl offset (for fixed head area) */ + unsigned short dkg_nhead; /* # of heads */ + unsigned short dkg_obs1; /* obsolete */ + unsigned short dkg_nsect; /* # of data sectors per track */ + unsigned short dkg_intrlv; /* interleave factor */ + unsigned short dkg_obs2; /* obsolete */ + unsigned short dkg_obs3; /* obsolete */ + unsigned short dkg_apc; /* alternates per cyl (SCSI only) */ + unsigned short dkg_rpm; /* revolutions per minute */ + unsigned short dkg_pcyl; /* # of physical cylinders */ + unsigned short dkg_write_reinstruct; /* # sectors to skip, writes */ + unsigned short dkg_read_reinstruct; /* # sectors to skip, reads */ + unsigned short dkg_extra[7]; /* for compatible expansion */ +}; + +/* + * These defines are for historic compatibility with old drivers. + */ +#define dkg_bhead dkg_obs1 /* used to be head offset */ +#define dkg_gap1 dkg_obs2 /* used to be gap1 */ +#define dkg_gap2 dkg_obs3 /* used to be gap2 */ + +/* + * Disk io control commands + * Warning: some other ioctls with the DIOC prefix exist elsewhere. + * The Generic DKIOC numbers are from 0 - 50. + * The Floppy Driver uses 51 - 100. + * The Hard Disk (except SCSI) 101 - 106. (these are obsolete) + * The CDROM Driver 151 - 200. + * The USCSI ioctl 201 - 250. + */ +#define DKIOC (0x04 << 8) + +/* + * The following ioctls are generic in nature and need to be + * supported as appropriate by all disk drivers + */ +#define DKIOCGGEOM (DKIOC|1) /* Get geometry */ +#define DKIOCINFO (DKIOC|3) /* Get info */ +#define DKIOCGVTOC (DKIOC|11) /* Get VTOC */ +#define DKIOCSVTOC (DKIOC|12) /* Set VTOC & Write to Disk */ + +/* + * Disk Cache Controls. These ioctls should be supported by + * all disk drivers. + * + * DKIOCFLUSHWRITECACHE when used from user-mode ignores the ioctl + * argument, but it should be passed as NULL to allow for future + * reinterpretation. From user-mode, this ioctl request is synchronous. + * + * When invoked from within the kernel, the arg can be NULL to indicate + * a synchronous request or can be the address of a struct dk_callback + * to request an asynchronous callback when the flush request is complete. + * In this case, the flag to the ioctl must include FKIOCTL and the + * dkc_callback field of the pointed to struct must be non-null or the + * request is made synchronously. + * + * In the callback case: if the ioctl returns 0, a callback WILL be performed. + * If the ioctl returns non-zero, a callback will NOT be performed. + * NOTE: In some cases, the callback may be done BEFORE the ioctl call + * returns. The caller's locking strategy should be prepared for this case. + */ +#define DKIOCFLUSHWRITECACHE (DKIOC|34) /* flush cache to phys medium */ + +struct dk_callback { + void (*dkc_callback)(void *dkc_cookie, int error); + void *dkc_cookie; + int dkc_flag; +}; + +/* bit flag definitions for dkc_flag */ +#define FLUSH_VOLATILE 0x1 /* Bit 0: if set, only flush */ + /* volatile cache; otherwise, flush */ + /* volatile and non-volatile cache */ + +#define DKIOCGETWCE (DKIOC|36) /* Get current write cache */ + /* enablement status */ +#define DKIOCSETWCE (DKIOC|37) /* Enable/Disable write cache */ + +/* + * The following ioctls are used by Sun drivers to communicate + * with their associated format routines. Support of these ioctls + * is not required of foreign drivers + */ +#define DKIOCSGEOM (DKIOC|2) /* Set geometry */ +#define DKIOCSAPART (DKIOC|4) /* Set all partitions */ +#define DKIOCGAPART (DKIOC|5) /* Get all partitions */ +#define DKIOCG_PHYGEOM (DKIOC|32) /* get physical geometry */ +#define DKIOCG_VIRTGEOM (DKIOC|33) /* get virtual geometry */ + +/* + * The following ioctl's are removable media support + */ +#define DKIOCLOCK (DKIOC|7) /* Generic 'lock' */ +#define DKIOCUNLOCK (DKIOC|8) /* Generic 'unlock' */ +#define DKIOCSTATE (DKIOC|13) /* Inquire insert/eject state */ +#define DKIOCREMOVABLE (DKIOC|16) /* is media removable */ + + +/* + * ioctl for hotpluggable devices + */ +#define DKIOCHOTPLUGGABLE (DKIOC|35) /* is hotpluggable */ + +/* + * Ioctl to force driver to re-read the alternate partition and rebuild + * the internal defect map. + */ +#define DKIOCADDBAD (DKIOC|20) /* Re-read the alternate map (IDE) */ +#define DKIOCGETDEF (DKIOC|21) /* read defect list (IDE) */ + +/* + * Used by applications to get disk defect information from IDE + * drives. + */ +#ifdef _SYSCALL32 +struct defect_header32 { + int head; + caddr32_t buffer; +}; +#endif /* _SYSCALL32 */ + +struct defect_header { + int head; + caddr_t buffer; +}; + +#define DKIOCPARTINFO (DKIOC|22) /* Get partition or slice parameters */ + +/* + * Used by applications to get partition or slice information + */ +#ifdef _SYSCALL32 +struct part_info32 { + uint32_t p_start; + int p_length; +}; +#endif /* _SYSCALL32 */ + +struct part_info { + uint64_t p_start; + int p_length; +}; + +/* The following ioctls are for Optical Memory Device */ +#define DKIOC_EBP_ENABLE (DKIOC|40) /* enable by pass erase on write */ +#define DKIOC_EBP_DISABLE (DKIOC|41) /* disable by pass erase on write */ + +/* + * This state enum is the argument passed to the DKIOCSTATE ioctl. + */ +enum dkio_state { DKIO_NONE, DKIO_EJECTED, DKIO_INSERTED, DKIO_DEV_GONE }; + +#define DKIOCGMEDIAINFO (DKIOC|42) /* get information about the media */ + +/* + * ioctls to read/write mboot info. + */ +#define DKIOCGMBOOT (DKIOC|43) /* get mboot info */ +#define DKIOCSMBOOT (DKIOC|44) /* set mboot info */ + +/* + * ioctl to get the device temperature. + */ +#define DKIOCGTEMPERATURE (DKIOC|45) /* get temperature */ + +/* + * Used for providing the temperature. + */ + +struct dk_temperature { + uint_t dkt_flags; /* Flags */ + short dkt_cur_temp; /* Current disk temperature */ + short dkt_ref_temp; /* reference disk temperature */ +}; + +#define DKT_BYPASS_PM 0x1 +#define DKT_INVALID_TEMP 0xFFFF + + +/* + * Media types or profiles known + */ +#define DK_UNKNOWN 0x00 /* Media inserted - type unknown */ + + +/* + * SFF 8090 Specification Version 3, media types 0x01 - 0xfffe are retained to + * maintain compatibility with SFF8090. The following define the + * optical media type. + */ +#define DK_REMOVABLE_DISK 0x02 /* Removable Disk */ +#define DK_MO_ERASABLE 0x03 /* MO Erasable */ +#define DK_MO_WRITEONCE 0x04 /* MO Write once */ +#define DK_AS_MO 0x05 /* AS MO */ +#define DK_CDROM 0x08 /* CDROM */ +#define DK_CDR 0x09 /* CD-R */ +#define DK_CDRW 0x0A /* CD-RW */ +#define DK_DVDROM 0x10 /* DVD-ROM */ +#define DK_DVDR 0x11 /* DVD-R */ +#define DK_DVDRAM 0x12 /* DVD_RAM or DVD-RW */ + +/* + * Media types for other rewritable magnetic media + */ +#define DK_FIXED_DISK 0x10001 /* Fixed disk SCSI or otherwise */ +#define DK_FLOPPY 0x10002 /* Floppy media */ +#define DK_ZIP 0x10003 /* IOMEGA ZIP media */ +#define DK_JAZ 0x10004 /* IOMEGA JAZ media */ + +#define DKIOCSETEFI (DKIOC|17) /* Set EFI info */ +#define DKIOCGETEFI (DKIOC|18) /* Get EFI info */ + +#define DKIOCPARTITION (DKIOC|9) /* Get partition info */ + +/* + * Ioctls to get/set volume capabilities related to Logical Volume Managers. + * They include the ability to get/set capabilities and to issue a read to a + * specific underlying device of a replicated device. + */ + +#define DKIOCGETVOLCAP (DKIOC | 25) /* Get volume capabilities */ +#define DKIOCSETVOLCAP (DKIOC | 26) /* Set volume capabilities */ +#define DKIOCDMR (DKIOC | 27) /* Issue a directed read */ + +typedef uint_t volcapinfo_t; + +typedef uint_t volcapset_t; + +#define DKV_ABR_CAP 0x00000001 /* Support Appl.Based Recovery */ +#define DKV_DMR_CAP 0x00000002 /* Support Directed Mirror Read */ + +typedef struct volcap { + volcapinfo_t vc_info; /* Capabilities available */ + volcapset_t vc_set; /* Capabilities set */ +} volcap_t; + +#define VOL_SIDENAME 256 + +typedef struct vol_directed_rd { + int vdr_flags; + offset_t vdr_offset; + size_t vdr_nbytes; + size_t vdr_bytesread; + void *vdr_data; + int vdr_side; + char vdr_side_name[VOL_SIDENAME]; +} vol_directed_rd_t; + +#define DKV_SIDE_INIT (-1) +#define DKV_DMR_NEXT_SIDE 0x00000001 +#define DKV_DMR_DONE 0x00000002 +#define DKV_DMR_ERROR 0x00000004 +#define DKV_DMR_SUCCESS 0x00000008 +#define DKV_DMR_SHORT 0x00000010 + +#ifdef _MULTI_DATAMODEL +#if _LONG_LONG_ALIGNMENT == 8 && _LONG_LONG_ALIGNMENT_32 == 4 +#pragma pack(4) +#endif +typedef struct vol_directed_rd32 { + int32_t vdr_flags; + offset_t vdr_offset; /* 64-bit element on 32-bit alignment */ + size32_t vdr_nbytes; + size32_t vdr_bytesread; + caddr32_t vdr_data; + int32_t vdr_side; + char vdr_side_name[VOL_SIDENAME]; +} vol_directed_rd32_t; +#if _LONG_LONG_ALIGNMENT == 8 && _LONG_LONG_ALIGNMENT_32 == 4 +#pragma pack() +#endif +#endif /* _MULTI_DATAMODEL */ + +/* + * The ioctl is used to fetch disk's device type, vendor ID, + * model number/product ID, firmware revision and serial number together. + * + * Currently there are two device types - DKD_ATA_TYPE which means the + * disk is driven by cmdk/ata or dad/uata driver, and DKD_SCSI_TYPE + * which means the disk is driven by sd/scsi hba driver. + */ +#define DKIOC_GETDISKID (DKIOC|46) + +/* These two labels are for dkd_dtype of dk_disk_id_t */ +#define DKD_ATA_TYPE 0x01 /* ATA disk or legacy mode SATA disk */ +#define DKD_SCSI_TYPE 0x02 /* SCSI disk or native mode SATA disk */ + +#define DKD_ATA_MODEL 40 /* model number length */ +#define DKD_ATA_FWVER 8 /* firmware revision length */ +#define DKD_ATA_SERIAL 20 /* serial number length */ + +#define DKD_SCSI_VENDOR 8 /* vendor ID length */ +#define DKD_SCSI_PRODUCT 16 /* product ID length */ +#define DKD_SCSI_REVLEVEL 4 /* revision level length */ +#define DKD_SCSI_SERIAL 12 /* serial number length */ + +/* + * The argument type for DKIOC_GETDISKID ioctl. + */ +typedef struct dk_disk_id { + uint_t dkd_dtype; + union { + struct { + char dkd_amodel[DKD_ATA_MODEL]; /* 40 bytes */ + char dkd_afwver[DKD_ATA_FWVER]; /* 8 bytes */ + char dkd_aserial[DKD_ATA_SERIAL]; /* 20 bytes */ + } ata_disk_id; + struct { + char dkd_svendor[DKD_SCSI_VENDOR]; /* 8 bytes */ + char dkd_sproduct[DKD_SCSI_PRODUCT]; /* 16 bytes */ + char dkd_sfwver[DKD_SCSI_REVLEVEL]; /* 4 bytes */ + char dkd_sserial[DKD_SCSI_SERIAL]; /* 12 bytes */ + } scsi_disk_id; + } disk_id; +} dk_disk_id_t; + +/* + * The ioctl is used to update the firmware of device. + */ +#define DKIOC_UPDATEFW (DKIOC|47) + +/* The argument type for DKIOC_UPDATEFW ioctl */ +typedef struct dk_updatefw { + caddr_t dku_ptrbuf; /* pointer to firmware buf */ + uint_t dku_size; /* firmware buf length */ + uint8_t dku_type; /* firmware update type */ +} dk_updatefw_t; + +#ifdef _SYSCALL32 +typedef struct dk_updatefw_32 { + caddr32_t dku_ptrbuf; /* pointer to firmware buf */ + uint_t dku_size; /* firmware buf length */ + uint8_t dku_type; /* firmware update type */ +} dk_updatefw_32_t; +#endif /* _SYSCALL32 */ + +/* + * firmware update type - temporary or permanent use + */ +#define FW_TYPE_TEMP 0x0 /* temporary use */ +#define FW_TYPE_PERM 0x1 /* permanent use */ + +#define DKIOC (0x04 << 8) +#define DKIOCTRIM (DKIOC | 35) + +/* + * ioctl to free space (e.g. SCSI UNMAP) off a disk. + * Pass a dkioc_free_list_t containing a list of extents to be freed. + */ +#define DKIOCFREE (DKIOC|50) + +#define DF_WAIT_SYNC 0x00000001 /* Wait for full write-out of free. */ + +typedef struct dkioc_free_list_ext_s { + uint64_t dfle_start; + uint64_t dfle_length; +} dkioc_free_list_ext_t; + +typedef struct dkioc_free_list_s { + uint64_t dfl_flags; + uint64_t dfl_num_exts; + uint64_t dfl_offset; + dkioc_free_list_ext_t dfl_exts[1]; +} dkioc_free_list_t; +#define DFL_SZ(num_exts) \ + (sizeof (dkioc_free_list_t) + \ + (num_exts - 1) * sizeof (dkioc_free_list_ext_t)) + +/* Frees a variable-length dkioc_free_list_t structure. */ +static inline void +dfl_free(dkioc_free_list_t *dfl) +{ + kmem_free(dfl, DFL_SZ(dfl->dfl_num_exts)); +} + +#ifdef __cplusplus +} +#endif + +#endif /* _OPENSOLARIS_SYS_DKIO_H_ */ diff --git a/include/os/macos/spl/sys/errno.h b/include/os/macos/spl/sys/errno.h new file mode 100644 index 000000000000..67574721cc5c --- /dev/null +++ b/include/os/macos/spl/sys/errno.h @@ -0,0 +1,29 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#include_next + +#define EBADE EBADMACHO +#define ECKSUM EBADE +#define EFRAGS EIDRM +#define EREMOTEIO ENOLINK +#define ENOTACTIVE ENOPOLICY +#define ECHRNG EMULTIHOP diff --git a/include/os/macos/spl/sys/fcntl.h b/include/os/macos/spl/sys/fcntl.h new file mode 100644 index 000000000000..1a4d8fb5474c --- /dev/null +++ b/include/os/macos/spl/sys/fcntl.h @@ -0,0 +1,46 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_FCNTL_H +#define _SPL_FCNTL_H + +#include +#include +#if !defined(MAC_OS_X_VERSION_10_9) || \ + (MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_9) +#include +#endif + +#include_next + +#define F_FREESP 11 + +#define O_LARGEFILE 0 +#define O_RSYNC 0 +#define O_DIRECT 0 + +#endif /* _SPL_FCNTL_H */ diff --git a/include/os/macos/spl/sys/file.h b/include/os/macos/spl/sys/file.h new file mode 100644 index 000000000000..9a2667cefc98 --- /dev/null +++ b/include/os/macos/spl/sys/file.h @@ -0,0 +1,65 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_FILE_H +#define _SPL_FILE_H + +#define FIGNORECASE 0x00080000 +#define FKIOCTL 0x80000000 +#define ED_CASE_CONFLICT 0x10 + +#include + +/* + * XNU has all the proc structs as opaque and with no functions we + * are allowed to call, so we implement file IO from within the kernel + * as vnode operations. + * The second mode is when we are given a "fd" from userland, which we + * map in here, using getf()/releasef(). + * When it comes to IO, if "fd" is set, we use it (fo_rdwr()) as it + * can handle both files, and pipes. + * In kernel space file ops, we use vn_rdwr on the vnode. + */ +struct spl_fileproc { + void *f_vnode; /* underlying vnode */ + list_node_t f_next; /* * next getf() link for releasef() */ + int f_fd; /* * userland file descriptor */ + off_t f_offset; /* offset for stateful IO */ + void *f_proc; /* opaque */ + void *f_fp; /* opaque */ + int f_writes; /* did write? for close sync */ + int f_ioflags; /* IO_APPEND */ + minor_t f_file; /* minor of the file */ + void *f_private; /* zfsdev_state_t */ +}; +/* Members with '*' are not used when 'fd' is not given */ + +void *getf(int fd); +void releasef(int fd); +struct vnode *getf_vnode(void *fp); + +#endif /* SPL_FILE_H */ diff --git a/include/os/macos/spl/sys/inttypes.h b/include/os/macos/spl/sys/inttypes.h new file mode 100644 index 000000000000..c9f6a316aa8b --- /dev/null +++ b/include/os/macos/spl/sys/inttypes.h @@ -0,0 +1,31 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_INTTYPES_H +#define _SPL_INTTYPES_H + +#endif /* SPL_INTTYPES_H */ diff --git a/include/os/macos/spl/sys/isa_defs.h b/include/os/macos/spl/sys/isa_defs.h new file mode 100644 index 000000000000..f702dc51e10c --- /dev/null +++ b/include/os/macos/spl/sys/isa_defs.h @@ -0,0 +1,690 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_ISA_DEFS_H +#define _SYS_ISA_DEFS_H + +/* + * This header file serves to group a set of well known defines and to + * set these for each instruction set architecture. These defines may + * be divided into two groups; characteristics of the processor and + * implementation choices for Solaris on a processor. + * + * Processor Characteristics: + * + * _LITTLE_ENDIAN / _BIG_ENDIAN: + * The natural byte order of the processor. A pointer to an int points + * to the least/most significant byte of that int. + * + * _STACK_GROWS_UPWARD / _STACK_GROWS_DOWNWARD: + * The processor specific direction of stack growth. A push onto the + * stack increases/decreases the stack pointer, so it stores data at + * successively higher/lower addresses. (Stackless machines ignored + * without regrets). + * + * _LONG_LONG_HTOL / _LONG_LONG_LTOH: + * A pointer to a long long points to the most/least significant long + * within that long long. + * + * _BIT_FIELDS_HTOL / _BIT_FIELDS_LTOH: + * The C compiler assigns bit fields from the high/low to the low/high end + * of an int (most to least significant vs. least to most significant). + * + * _IEEE_754: + * The processor (or supported implementations of the processor) + * supports the ieee-754 floating point standard. No other floating + * point standards are supported (or significant). Any other supported + * floating point formats are expected to be cased on the ISA processor + * symbol. + * + * _CHAR_IS_UNSIGNED / _CHAR_IS_SIGNED: + * The C Compiler implements objects of type `char' as `unsigned' or + * `signed' respectively. This is really an implementation choice of + * the compiler writer, but it is specified in the ABI and tends to + * be uniform across compilers for an instruction set architecture. + * Hence, it has the properties of a processor characteristic. + * + * _CHAR_ALIGNMENT / _SHORT_ALIGNMENT / _INT_ALIGNMENT / _LONG_ALIGNMENT / + * _LONG_LONG_ALIGNMENT / _DOUBLE_ALIGNMENT / _LONG_DOUBLE_ALIGNMENT / + * _POINTER_ALIGNMENT / _FLOAT_ALIGNMENT: + * The ABI defines alignment requirements of each of the primitive + * object types. Some, if not all, may be hardware requirements as + * well. The values are expressed in "byte-alignment" units. + * + * _MAX_ALIGNMENT: + * The most stringent alignment requirement as specified by the ABI. + * Equal to the maximum of all the above _XXX_ALIGNMENT values. + * + * _ALIGNMENT_REQUIRED: + * True or false (1 or 0) whether or not the hardware requires the ABI + * alignment. + * + * _LONG_LONG_ALIGNMENT_32 + * The 32-bit ABI supported by a 64-bit kernel may have different + * alignment requirements for primitive object types. The value of this + * identifier is expressed in "byte-alignment" units. + * + * _HAVE_CPUID_INSN + * This indicates that the architecture supports the 'cpuid' + * instruction as defined by Intel. (Intel allows other vendors + * to extend the instruction for their own purposes.) + * + * + * Implementation Choices: + * + * _ILP32 / _LP64: + * This specifies the compiler data type implementation as specified in + * the relevant ABI. The choice between these is strongly influenced + * by the underlying hardware, but is not absolutely tied to it. + * Currently only two data type models are supported: + * + * _ILP32: + * Int/Long/Pointer are 32 bits. This is the historical UNIX + * and Solaris implementation. Due to its historical standing, + * this is the default case. + * + * _LP64: + * Long/Pointer are 64 bits, Int is 32 bits. This is the chosen + * implementation for 64-bit ABIs such as SPARC V9. + * + * _I32LPx: + * A compilation environment where 'int' is 32-bit, and + * longs and pointers are simply the same size. + * + * In all cases, Char is 8 bits and Short is 16 bits. + * + * _SUNOS_VTOC_8 / _SUNOS_VTOC_16 / _SVR4_VTOC_16: + * This specifies the form of the disk VTOC (or label): + * + * _SUNOS_VTOC_8: + * This is a VTOC form which is upwardly compatible with the + * SunOS 4.x disk label and allows 8 partitions per disk. + * + * _SUNOS_VTOC_16: + * In this format the incore vtoc image matches the ondisk + * version. It allows 16 slices per disk, and is not + * compatible with the SunOS 4.x disk label. + * + * Note that these are not the only two VTOC forms possible and + * additional forms may be added. One possible form would be the + * SVr4 VTOC form. The symbol for that is reserved now, although + * it is not implemented. + * + * _SVR4_VTOC_16: + * This VTOC form is compatible with the System V Release 4 + * VTOC (as implemented on the SVr4 Intel and 3b ports) with + * 16 partitions per disk. + * + * + * _DMA_USES_PHYSADDR / _DMA_USES_VIRTADDR + * This describes the type of addresses used by system DMA: + * + * _DMA_USES_PHYSADDR: + * This type of DMA, used in the x86 implementation, + * requires physical addresses for DMA buffers. The 24-bit + * addresses used by some legacy boards is the source of the + * "low-memory" (<16MB) requirement for some devices using DMA. + * + * _DMA_USES_VIRTADDR: + * This method of DMA allows the use of virtual addresses for + * DMA transfers. + * + * _FIRMWARE_NEEDS_FDISK / _NO_FDISK_PRESENT + * This indicates the presence/absence of an fdisk table. + * + * _FIRMWARE_NEEDS_FDISK + * The fdisk table is required by system firmware. If present, + * it allows a disk to be subdivided into multiple fdisk + * partitions, each of which is equivalent to a separate, + * virtual disk. This enables the co-existence of multiple + * operating systems on a shared hard disk. + * + * _NO_FDISK_PRESENT + * If the fdisk table is absent, it is assumed that the entire + * media is allocated for a single operating system. + * + * _HAVE_TEM_FIRMWARE + * Defined if this architecture has the (fallback) option of + * using prom_* calls for doing I/O if a suitable kernel driver + * is not available to do it. + * + * _DONT_USE_1275_GENERIC_NAMES + * Controls whether or not device tree node names should + * comply with the IEEE 1275 "Generic Names" Recommended + * Practice. With _DONT_USE_GENERIC_NAMES, device-specific + * names identifying the particular device will be used. + * + * __i386_COMPAT + * This indicates whether the i386 ABI is supported as a *non-native* + * mode for the platform. When this symbol is defined: + * - 32-bit xstat-style system calls are enabled + * - 32-bit xmknod-style system calls are enabled + * - 32-bit system calls use i386 sizes -and- alignments + * + * Note that this is NOT defined for the i386 native environment! + * + * __x86 + * This is ONLY a synonym for defined(__i386) || defined(__amd64) + * which is useful only insofar as these two architectures share + * common attributes. Analogous to __sparc. + * + * _PSM_MODULES + * This indicates whether or not the implementation uses PSM + * modules for processor support, reading /etc/mach from inside + * the kernel to extract a list. + * + * _RTC_CONFIG + * This indicates whether or not the implementation uses /etc/rtc_config + * to configure the real-time clock in the kernel. + * + * _UNIX_KRTLD + * This indicates that the implementation uses a dynamically + * linked unix + krtld to form the core kernel image at boot + * time, or (in the absence of this symbol) a prelinked kernel image. + * + * _OBP + * This indicates the firmware interface is OBP. + * + * _SOFT_HOSTID + * This indicates that the implementation obtains the hostid + * from the file /etc/hostid, rather than from hardware. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The following set of definitions characterize Solaris on AMD's + * 64-bit systems. + */ +#if defined(__x86_64) || defined(__amd64) + +#if !defined(__amd64) +#define __amd64 /* preferred guard */ +#endif + +#if !defined(__x86) +#define __x86 +#endif + +/* + * Define the appropriate "processor characteristics" + */ +#define _STACK_GROWS_DOWNWARD +#define _LONG_LONG_LTOH +#define _BIT_FIELDS_LTOH +#define _IEEE_754 +#define _CHAR_IS_SIGNED +#define _BOOL_ALIGNMENT 1 +#define _CHAR_ALIGNMENT 1 +#define _SHORT_ALIGNMENT 2 +#define _INT_ALIGNMENT 4 +#define _FLOAT_ALIGNMENT 4 +#define _FLOAT_COMPLEX_ALIGNMENT 4 +#define _LONG_ALIGNMENT 8 +#define _LONG_LONG_ALIGNMENT 8 +#define _DOUBLE_ALIGNMENT 8 +#define _DOUBLE_COMPLEX_ALIGNMENT 8 +#define _LONG_DOUBLE_ALIGNMENT 16 +#define _LONG_DOUBLE_COMPLEX_ALIGNMENT 16 +#define _POINTER_ALIGNMENT 8 +#define _MAX_ALIGNMENT 16 +#define _ALIGNMENT_REQUIRED 1 + +/* + * Different alignment constraints for the i386 ABI in compatibility mode + */ +#define _LONG_LONG_ALIGNMENT_32 4 + +/* + * Define the appropriate "implementation choices". + */ +#if !defined(_LP64) +#error "_LP64 not defined" +#endif +#if !defined(_I32LPx) +#define _I32LPx +#endif +#define _MULTI_DATAMODEL +#define _SUNOS_VTOC_16 +#define _DMA_USES_PHYSADDR +#define _FIRMWARE_NEEDS_FDISK +#define __i386_COMPAT +#define _PSM_MODULES +#define _RTC_CONFIG +#define _SOFT_HOSTID +#define _DONT_USE_1275_GENERIC_NAMES +#define _HAVE_CPUID_INSN + +/* + * The feature test macro __i386 is generic for all processors implementing + * the Intel 386 instruction set or a superset of it. Specifically, this + * includes all members of the 386, 486, and Pentium family of processors. + */ +#elif defined(__i386) || defined(__i386__) + +#if !defined(__i386) +#define __i386 +#endif + +#if !defined(__x86) +#define __x86 +#endif + +/* + * Define the appropriate "processor characteristics" + */ +#define _STACK_GROWS_DOWNWARD +#define _LONG_LONG_LTOH +#define _BIT_FIELDS_LTOH +#define _IEEE_754 +#define _CHAR_IS_SIGNED +#define _BOOL_ALIGNMENT 1 +#define _CHAR_ALIGNMENT 1 +#define _SHORT_ALIGNMENT 2 +#define _INT_ALIGNMENT 4 +#define _FLOAT_ALIGNMENT 4 +#define _FLOAT_COMPLEX_ALIGNMENT 4 +#define _LONG_ALIGNMENT 4 +#define _LONG_LONG_ALIGNMENT 4 +#define _DOUBLE_ALIGNMENT 4 +#define _DOUBLE_COMPLEX_ALIGNMENT 4 +#define _LONG_DOUBLE_ALIGNMENT 4 +#define _LONG_DOUBLE_COMPLEX_ALIGNMENT 4 +#define _POINTER_ALIGNMENT 4 +#define _MAX_ALIGNMENT 4 +#define _ALIGNMENT_REQUIRED 0 + +#define _LONG_LONG_ALIGNMENT_32 _LONG_LONG_ALIGNMENT + +/* + * Define the appropriate "implementation choices". + */ +#if !defined(_ILP32) +#define _ILP32 +#endif +#if !defined(_I32LPx) +#define _I32LPx +#endif +#define _SUNOS_VTOC_16 +#define _DMA_USES_PHYSADDR +#define _FIRMWARE_NEEDS_FDISK +#define _PSM_MODULES +#define _RTC_CONFIG +#define _SOFT_HOSTID +#define _DONT_USE_1275_GENERIC_NAMES +#define _HAVE_CPUID_INSN + +#elif defined(__aarch64__) + +/* + * Define the appropriate "processor characteristics" + */ +#define _STACK_GROWS_DOWNWARD +#define _LONG_LONG_LTOH +#define _BIT_FIELDS_LTOH +#define _IEEE_754 +#define _CHAR_IS_UNSIGNED +#define _BOOL_ALIGNMENT 1 +#define _CHAR_ALIGNMENT 1 +#define _SHORT_ALIGNMENT 2 +#define _INT_ALIGNMENT 4 +#define _FLOAT_ALIGNMENT 4 +#define _FLOAT_COMPLEX_ALIGNMENT 4 +#define _LONG_ALIGNMENT 8 +#define _LONG_LONG_ALIGNMENT 8 +#define _DOUBLE_ALIGNMENT 8 +#define _DOUBLE_COMPLEX_ALIGNMENT 8 +#define _LONG_DOUBLE_ALIGNMENT 16 +#define _LONG_DOUBLE_COMPLEX_ALIGNMENT 16 +#define _POINTER_ALIGNMENT 8 +#define _MAX_ALIGNMENT 16 +#define _ALIGNMENT_REQUIRED 1 + +#define _LONG_LONG_ALIGNMENT_32 _LONG_LONG_ALIGNMENT + +/* + * Define the appropriate "implementation choices" + */ +#if !defined(_LP64) +#error "_LP64 not defined" +#endif +#define _SUNOS_VTOC_16 +#define _DMA_USES_PHYSADDR +#define _FIRMWARE_NEEDS_FDISK +#define _PSM_MODULES +#define _RTC_CONFIG +#define _DONT_USE_1275_GENERIC_NAMES +#define _HAVE_CPUID_INSN + +#elif defined(__riscv) + +/* + * Define the appropriate "processor characteristics" + */ +#define _STACK_GROWS_DOWNWARD +#define _LONG_LONG_LTOH +#define _BIT_FIELDS_LTOH +#define _IEEE_754 +#define _CHAR_IS_UNSIGNED +#define _BOOL_ALIGNMENT 1 +#define _CHAR_ALIGNMENT 1 +#define _SHORT_ALIGNMENT 2 +#define _INT_ALIGNMENT 4 +#define _FLOAT_ALIGNMENT 4 +#define _FLOAT_COMPLEX_ALIGNMENT 4 +#define _LONG_ALIGNMENT 8 +#define _LONG_LONG_ALIGNMENT 8 +#define _DOUBLE_ALIGNMENT 8 +#define _DOUBLE_COMPLEX_ALIGNMENT 8 +#define _LONG_DOUBLE_ALIGNMENT 16 +#define _LONG_DOUBLE_COMPLEX_ALIGNMENT 16 +#define _POINTER_ALIGNMENT 8 +#define _MAX_ALIGNMENT 16 +#define _ALIGNMENT_REQUIRED 1 + +#define _LONG_LONG_ALIGNMENT_32 _LONG_LONG_ALIGNMENT + +/* + * Define the appropriate "implementation choices" + */ +#if !defined(_LP64) +#define _LP64 +#endif +#define _SUNOS_VTOC_16 +#define _DMA_USES_PHYSADDR +#define _FIRMWARE_NEEDS_FDISK +#define _PSM_MODULES +#define _RTC_CONFIG +#define _DONT_USE_1275_GENERIC_NAMES +#define _HAVE_CPUID_INSN + +#elif defined(__arm__) + +/* + * Define the appropriate "processor characteristics" + */ +#define _STACK_GROWS_DOWNWARD +#define _LONG_LONG_LTOH +#define _BIT_FIELDS_LTOH +#define _IEEE_754 +#define _CHAR_IS_SIGNED +#define _BOOL_ALIGNMENT 1 +#define _CHAR_ALIGNMENT 1 +#define _SHORT_ALIGNMENT 2 +#define _INT_ALIGNMENT 4 +#define _FLOAT_ALIGNMENT 4 +#define _FLOAT_COMPLEX_ALIGNMENT 4 +#define _LONG_ALIGNMENT 4 +#define _LONG_LONG_ALIGNMENT 4 +#define _DOUBLE_ALIGNMENT 4 +#define _DOUBLE_COMPLEX_ALIGNMENT 4 +#define _LONG_DOUBLE_ALIGNMENT 4 +#define _LONG_DOUBLE_COMPLEX_ALIGNMENT 4 +#define _POINTER_ALIGNMENT 4 +#define _MAX_ALIGNMENT 4 +#define _ALIGNMENT_REQUIRED 0 + +#define _LONG_LONG_ALIGNMENT_32 _LONG_LONG_ALIGNMENT + +/* + * Define the appropriate "implementation choices". + */ +#if !defined(_ILP32) +#define _ILP32 +#endif +#if !defined(_I32LPx) +#define _I32LPx +#endif +#define _SUNOS_VTOC_16 +#define _DMA_USES_PHYSADDR +#define _FIRMWARE_NEEDS_FDISK +#define _PSM_MODULES +#define _RTC_CONFIG +#define _DONT_USE_1275_GENERIC_NAMES +#define _HAVE_CPUID_INSN + +#elif defined(__mips__) + +/* + * Define the appropriate "processor characteristics" + */ +#define _STACK_GROWS_DOWNWARD +#define _LONG_LONG_LTOH +#define _BIT_FIELDS_LTOH +#define _IEEE_754 +#define _CHAR_IS_SIGNED +#define _BOOL_ALIGNMENT 1 +#define _CHAR_ALIGNMENT 1 +#define _SHORT_ALIGNMENT 2 +#define _INT_ALIGNMENT 4 +#define _FLOAT_ALIGNMENT 4 +#define _FLOAT_COMPLEX_ALIGNMENT 4 +#if defined(__mips_n64) +#define _LONG_ALIGNMENT 8 +#define _LONG_LONG_ALIGNMENT 8 +#define _DOUBLE_ALIGNMENT 8 +#define _DOUBLE_COMPLEX_ALIGNMENT 8 +#define _LONG_DOUBLE_ALIGNMENT 8 +#define _LONG_DOUBLE_COMPLEX_ALIGNMENT 8 +#define _POINTER_ALIGNMENT 8 +#define _MAX_ALIGNMENT 8 +#define _ALIGNMENT_REQUIRED 0 + +#define _LONG_LONG_ALIGNMENT_32 _INT_ALIGNMENT +/* + * Define the appropriate "implementation choices". + */ +#if !defined(_LP64) +#error "_LP64 not defined" +#endif +#else +#define _LONG_ALIGNMENT 4 +#define _LONG_LONG_ALIGNMENT 4 +#define _DOUBLE_ALIGNMENT 4 +#define _DOUBLE_COMPLEX_ALIGNMENT 4 +#define _LONG_DOUBLE_ALIGNMENT 4 +#define _LONG_DOUBLE_COMPLEX_ALIGNMENT 4 +#define _POINTER_ALIGNMENT 4 +#define _MAX_ALIGNMENT 4 +#define _ALIGNMENT_REQUIRED 0 + +#define _LONG_LONG_ALIGNMENT_32 _LONG_LONG_ALIGNMENT + +/* + * Define the appropriate "implementation choices". + */ +#if !defined(_ILP32) +#define _ILP32 +#endif +#if !defined(_I32LPx) +#define _I32LPx +#endif +#endif +#define _SUNOS_VTOC_16 +#define _DMA_USES_PHYSADDR +#define _FIRMWARE_NEEDS_FDISK +#define _PSM_MODULES +#define _RTC_CONFIG +#define _DONT_USE_1275_GENERIC_NAMES +#define _HAVE_CPUID_INSN + +#elif defined(__powerpc__) + +#if defined(__BIG_ENDIAN__) +#define _BIT_FIELDS_HTOL +#else +#define _BIT_FIELDS_LTOH +#endif + +/* + * The following set of definitions characterize the Solaris on SPARC systems. + * + * The symbol __sparc indicates any of the SPARC family of processor + * architectures. This includes SPARC V7, SPARC V8 and SPARC V9. + * + * The symbol __sparcv8 indicates the 32-bit SPARC V8 architecture as defined + * by Version 8 of the SPARC Architecture Manual. (SPARC V7 is close enough + * to SPARC V8 for the former to be subsumed into the latter definition.) + * + * The symbol __sparcv9 indicates the 64-bit SPARC V9 architecture as defined + * by Version 9 of the SPARC Architecture Manual. + * + * The symbols __sparcv8 and __sparcv9 are mutually exclusive, and are only + * relevant when the symbol __sparc is defined. + */ +/* + * XXX Due to the existence of 5110166, "defined(__sparcv9)" needs to be added + * to support backwards builds. This workaround should be removed in s10_71. + */ +#elif defined(__sparc) || defined(__sparcv9) || defined(__sparc__) +#if !defined(__sparc) +#define __sparc +#endif + +/* + * You can be 32-bit or 64-bit, but not both at the same time. + */ +#if defined(__sparcv8) && defined(__sparcv9) +#error "SPARC Versions 8 and 9 are mutually exclusive choices" +#endif + +/* + * Existing compilers do not set __sparcv8. Years will transpire before + * the compilers can be depended on to set the feature test macro. In + * the interim, we'll set it here on the basis of historical behaviour; + * if you haven't asked for SPARC V9, then you must've meant SPARC V8. + */ +#if !defined(__sparcv9) && !defined(__sparcv8) +#define __sparcv8 +#endif + +/* + * Define the appropriate "processor characteristics" shared between + * all Solaris on SPARC systems. + */ +#define _STACK_GROWS_DOWNWARD +#define _LONG_LONG_HTOL +#define _BIT_FIELDS_HTOL +#define _IEEE_754 +#define _CHAR_IS_SIGNED +#define _BOOL_ALIGNMENT 1 +#define _CHAR_ALIGNMENT 1 +#define _SHORT_ALIGNMENT 2 +#define _INT_ALIGNMENT 4 +#define _FLOAT_ALIGNMENT 4 +#define _FLOAT_COMPLEX_ALIGNMENT 4 +#define _LONG_LONG_ALIGNMENT 8 +#define _DOUBLE_ALIGNMENT 8 +#define _DOUBLE_COMPLEX_ALIGNMENT 8 +#define _ALIGNMENT_REQUIRED 1 + +/* + * Define the appropriate "implementation choices" shared between versions. + */ +#define _SUNOS_VTOC_8 +#define _DMA_USES_VIRTADDR +#define _NO_FDISK_PRESENT +#define _HAVE_TEM_FIRMWARE +#define _OBP + +/* + * The following set of definitions characterize the implementation of + * 32-bit Solaris on SPARC V8 systems. + */ +#if defined(__sparcv8) + +/* + * Define the appropriate "processor characteristics" + */ +#define _LONG_ALIGNMENT 4 +#define _LONG_DOUBLE_ALIGNMENT 8 +#define _LONG_DOUBLE_COMPLEX_ALIGNMENT 8 +#define _POINTER_ALIGNMENT 4 +#define _MAX_ALIGNMENT 8 + +#define _LONG_LONG_ALIGNMENT_32 _LONG_LONG_ALIGNMENT + +/* + * Define the appropriate "implementation choices" + */ +#define _ILP32 +#if !defined(_I32LPx) +#define _I32LPx +#endif + +/* + * The following set of definitions characterize the implementation of + * 64-bit Solaris on SPARC V9 systems. + */ +#elif defined(__sparcv9) + +/* + * Define the appropriate "processor characteristics" + */ +#define _LONG_ALIGNMENT 8 +#define _LONG_DOUBLE_ALIGNMENT 16 +#define _LONG_DOUBLE_COMPLEX_ALIGNMENT 16 +#define _POINTER_ALIGNMENT 8 +#define _MAX_ALIGNMENT 16 + +#define _LONG_LONG_ALIGNMENT_32 _LONG_LONG_ALIGNMENT + +/* + * Define the appropriate "implementation choices" + */ +#if !defined(_LP64) +#error "_LP64 not defined" +#endif +#if !defined(_I32LPx) +#define _I32LPx +#endif +#define _MULTI_DATAMODEL + +#else +#error "unknown SPARC version" +#endif + +/* + * #error is strictly ansi-C, but works as well as anything for K&R systems. + */ +#else +#error "ISA not supported" +#endif + +#if defined(_ILP32) && defined(_LP64) +#error "Both _ILP32 and _LP64 are defined" +#endif + +#define ____cacheline_aligned __attribute__((aligned(64))) + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_ISA_DEFS_H */ diff --git a/include/os/macos/spl/sys/kmem.h b/include/os/macos/spl/sys/kmem.h new file mode 100644 index 000000000000..86287060cc78 --- /dev/null +++ b/include/os/macos/spl/sys/kmem.h @@ -0,0 +1,155 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2008 MacZFS Project + * Copyright (C) 2013 Jorgen Lundman + * Copyright (C) 2017 Sean Doran + * + */ + +#ifndef _SPL_KMEM_H +#define _SPL_KMEM_H + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// XNU total amount of memory +extern uint64_t physmem; + +#define KM_SLEEP 0x0000 /* can block for memory; success guaranteed */ +#define KM_NOSLEEP 0x0001 /* cannot block for memory; may fail */ +#define KM_PANIC 0x0002 /* if memory cannot be allocated, panic */ +#define KM_PUSHPAGE 0x0004 /* can block for memory; may use reserve */ +#define KM_NORMALPRI 0x0008 /* with KM_NOSLEEP, lower priority allocation */ +#define KM_NODEBUG 0x0010 /* NOT IMPLEMENTED ON OSX */ +#define KM_NO_VBA 0x0020 /* OSX: don't descend to the bucket layer */ +#define KM_VMFLAGS 0x00ff /* flags that must match VM_* flags */ + +#define KM_FLAGS 0xffff /* all settable kmem flags */ + +/* + * Kernel memory allocator: DDI interfaces. + * See kmem_alloc(9F) for details. + */ + +// Work around symbol collisions in XNU +#define kmem_alloc(size, kmflags) zfs_kmem_alloc((size), (kmflags)) +#define kmem_zalloc(size, kmflags) zfs_kmem_zalloc((size), (kmflags)) +#define kmem_free(buf, size) zfs_kmem_free((buf), (size)) + +void *zfs_kmem_alloc(size_t size, int kmflags); +void *zfs_kmem_zalloc(size_t size, int kmflags); +void zfs_kmem_free(void *buf, size_t size); + +void spl_kmem_init(uint64_t); +void spl_kmem_thread_init(void); +void spl_kmem_mp_init(void); +void spl_kmem_thread_fini(void); +void spl_kmem_fini(void); + +size_t kmem_size(void); +size_t kmem_used(void); +int64_t kmem_avail(void); +size_t kmem_num_pages_wanted(void); +int spl_vm_pool_low(void); +int32_t spl_minimal_physmem_p(void); +int64_t spl_adjust_pressure(int64_t); +int64_t spl_free_wrapper(void); +int64_t spl_free_manual_pressure_wrapper(void); +boolean_t spl_free_fast_pressure_wrapper(void); +void spl_free_set_pressure(int64_t); +void spl_free_set_fast_pressure(boolean_t); +uint64_t spl_free_last_pressure_wrapper(void); + +#define KMC_NOTOUCH 0x00010000 +#define KMC_NODEBUG 0x00020000 +#define KMC_NOMAGAZINE 0x00040000 +#define KMC_NOHASH 0x00080000 +#define KMC_QCACHE 0x00100000 +#define KMC_KMEM_ALLOC 0x00200000 /* internal use only */ +#define KMC_IDENTIFIER 0x00400000 /* internal use only */ +#define KMC_PREFILL 0x00800000 +#define KMC_ARENA_SLAB 0x01000000 /* use a bigger kmem cache */ + +struct kmem_cache; + +typedef struct kmem_cache kmem_cache_t; + +/* Client response to kmem move callback */ +typedef enum kmem_cbrc { + KMEM_CBRC_YES, + KMEM_CBRC_NO, + KMEM_CBRC_LATER, + KMEM_CBRC_DONT_NEED, + KMEM_CBRC_DONT_KNOW +} kmem_cbrc_t; + +#define POINTER_IS_VALID(p) (!((uintptr_t)(p) & 0x3)) +#define POINTER_INVALIDATE(pp) (*(pp) = (void *)((uintptr_t)(*(pp)) | 0x1)) + +kmem_cache_t *kmem_cache_create(char *name, size_t bufsize, size_t align, + int (*constructor)(void *, void *, int), + void (*destructor)(void *, void *), + void (*reclaim)(void *), + void *_private, vmem_t *vmp, int cflags); +void kmem_cache_destroy(kmem_cache_t *cache); +void *kmem_cache_alloc(kmem_cache_t *cache, int flags); +void kmem_cache_free(kmem_cache_t *cache, void *buf); +void kmem_cache_free_to_slab(kmem_cache_t *cache, void *buf); +extern boolean_t kmem_cache_reap_active(void); +void kmem_cache_reap_now(kmem_cache_t *cache); +void kmem_depot_ws_zero(kmem_cache_t *cache); +void kmem_reap(void); +void kmem_reap_idspace(void); +kmem_cache_t *kmem_cache_buf_in_cache(kmem_cache_t *, void *); + +int kmem_debugging(void); +void kmem_cache_set_move(kmem_cache_t *, + kmem_cbrc_t (*)(void *, void *, size_t, void *)); + +char *kmem_asprintf(const char *fmt, ...); +extern char *kmem_strdup(const char *str); +extern void kmem_strfree(char *str); +char *kmem_vasprintf(const char *fmt, va_list ap); +char *kmem_strstr(const char *in, const char *str); +void strident_canon(char *s, size_t n); + +boolean_t spl_arc_no_grow(size_t, boolean_t, kmem_cache_t **); + +extern uint64_t spl_kmem_cache_inuse(kmem_cache_t *cache); +extern uint64_t spl_kmem_cache_entry_size(kmem_cache_t *cache); + +#ifdef __cplusplus +} +#endif + +#endif /* _SPL_KMEM_H */ diff --git a/include/os/macos/spl/sys/kmem_cache.h b/include/os/macos/spl/sys/kmem_cache.h new file mode 100644 index 000000000000..2dc08b171266 --- /dev/null +++ b/include/os/macos/spl/sys/kmem_cache.h @@ -0,0 +1,25 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _SPL_KMEM_CACHE_H +#define _SPL_KMEM_CACHE_H + +#endif diff --git a/include/os/macos/spl/sys/kmem_impl.h b/include/os/macos/spl/sys/kmem_impl.h new file mode 100644 index 000000000000..80af077eda2c --- /dev/null +++ b/include/os/macos/spl/sys/kmem_impl.h @@ -0,0 +1,494 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#ifndef _SYS_KMEM_IMPL_H +#define _SYS_KMEM_IMPL_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * kernel memory allocator: implementation-private data structures + * + * Lock order: + * 1. cache_lock + * 2. cc_lock in order by CPU ID + * 3. cache_depot_lock + * + * Do not call kmem_cache_alloc() or taskq_dispatch() while holding any of the + * above locks. + */ + +#define KMF_AUDIT 0x00000001 /* transaction auditing */ +#define KMF_DEADBEEF 0x00000002 /* deadbeef checking */ +#define KMF_REDZONE 0x00000004 /* redzone checking */ +#define KMF_CONTENTS 0x00000008 /* freed-buffer content logging */ +#define KMF_STICKY 0x00000010 /* if set, override /etc/system */ +#define KMF_NOMAGAZINE 0x00000020 /* disable per-cpu magazines */ +#define KMF_FIREWALL 0x00000040 /* put all bufs before unmapped pages */ +#define KMF_LITE 0x00000100 /* lightweight debugging */ + +#define KMF_HASH 0x00000200 /* cache has hash table */ +#define KMF_RANDOMIZE 0x00000400 /* randomize other kmem_flags */ + +#define KMF_DUMPDIVERT 0x00001000 /* use alternate memory at dump time */ +#define KMF_DUMPUNSAFE 0x00002000 /* flag caches used at dump time */ +#define KMF_PREFILL 0x00004000 /* Prefill the slab when created. */ + +#define KMF_BUFTAG (KMF_DEADBEEF | KMF_REDZONE) +#define KMF_TOUCH (KMF_BUFTAG | KMF_LITE | KMF_CONTENTS) +#define KMF_RANDOM (KMF_TOUCH | KMF_AUDIT | KMF_NOMAGAZINE) +#define KMF_DEBUG (KMF_RANDOM | KMF_FIREWALL) + +#define KMEM_STACK_DEPTH 15 + +#define KMEM_FREE_PATTERN 0xdeadbeefdeadbeefULL +#define KMEM_UNINITIALIZED_PATTERN 0xbaddcafebaddcafeULL +#define KMEM_REDZONE_PATTERN 0xfeedfacefeedfaceULL +#define KMEM_REDZONE_BYTE 0xbb + +/* + * Upstream platforms handle size == 0 as valid alloc, we + * can not return NULL, as that invalidates KM_SLEEP. So + * we return a valid hardcoded address, instead of actually taking up + * memory by fudging size to 1 byte. If read/writes are + * attempted, we will get page fault (which is correct, they + * asked for zero bytes after all) + */ +#define KMEM_ZERO_SIZE_PTR ((void *)16) + +/* + * Redzone size encodings for kmem_alloc() / kmem_free(). We encode the + * allocation size, rather than storing it directly, so that kmem_free() + * can distinguish frees of the wrong size from redzone violations. + * + * A size of zero is never valid. + */ +#define KMEM_SIZE_ENCODE(x) (251 * (x) + 1) +#define KMEM_SIZE_DECODE(x) ((x) / 251) +#define KMEM_SIZE_VALID(x) ((x) % 251 == 1 && (x) != 1) + + +#define KMEM_ALIGN 8 /* min guaranteed alignment */ +#define KMEM_ALIGN_SHIFT 3 /* log2(KMEM_ALIGN) */ +#define KMEM_VOID_FRACTION 8 /* never waste more than 1/8 of slab */ + +#define KMEM_SLAB_IS_PARTIAL(sp) \ + ((sp)->slab_refcnt > 0 && (sp)->slab_refcnt < (sp)->slab_chunks) +#define KMEM_SLAB_IS_ALL_USED(sp) \ + ((sp)->slab_refcnt == (sp)->slab_chunks) + +/* + * The bufctl (buffer control) structure keeps some minimal information + * about each buffer: its address, its slab, and its current linkage, + * which is either on the slab's freelist (if the buffer is free), or + * on the cache's buf-to-bufctl hash table (if the buffer is allocated). + * In the case of non-hashed, or "raw", caches (the common case), only + * the freelist linkage is necessary: the buffer address is at a fixed + * offset from the bufctl address, and the slab is at the end of the page. + * + * NOTE: bc_next must be the first field; raw buffers have linkage only. + */ +typedef struct kmem_bufctl { + struct kmem_bufctl *bc_next; /* next bufctl struct */ + void *bc_addr; /* address of buffer */ + struct kmem_slab *bc_slab; /* controlling slab */ +} kmem_bufctl_t; + +/* + * The KMF_AUDIT version of the bufctl structure. The beginning of this + * structure must be identical to the normal bufctl structure so that + * pointers are interchangeable. + */ +typedef struct kmem_bufctl_audit { + struct kmem_bufctl *bc_next; /* next bufctl struct */ + void *bc_addr; /* address of buffer */ + struct kmem_slab *bc_slab; /* controlling slab */ + kmem_cache_t *bc_cache; /* controlling cache */ + hrtime_t bc_timestamp; /* transaction time */ + kthread_t *bc_thread; /* thread doing transaction */ + struct kmem_bufctl *bc_lastlog; /* last log entry */ + void *bc_contents; /* contents at last free */ + int bc_depth; /* stack depth */ + pc_t bc_stack[KMEM_STACK_DEPTH]; /* pc stack */ +} kmem_bufctl_audit_t; + +/* + * A kmem_buftag structure is appended to each buffer whenever any of the + * KMF_BUFTAG flags (KMF_DEADBEEF, KMF_REDZONE, KMF_VERIFY) are set. + */ +typedef struct kmem_buftag { + uint64_t bt_redzone; /* 64-bit redzone pattern */ + kmem_bufctl_t *bt_bufctl; /* bufctl */ + intptr_t bt_bxstat; /* bufctl ^ (alloc/free) */ +} kmem_buftag_t; + +/* + * A variant of the kmem_buftag structure used for KMF_LITE caches. + * Previous callers are stored in reverse chronological order. (i.e. most + * recent first) + */ +typedef struct kmem_buftag_lite { + kmem_buftag_t bt_buftag; /* a normal buftag */ + pc_t bt_history[1]; /* zero or more callers */ +} kmem_buftag_lite_t; + +#define KMEM_BUFTAG_LITE_SIZE(f) \ + (offsetof(kmem_buftag_lite_t, bt_history[f])) + +#define KMEM_BUFTAG(cp, buf) \ + ((kmem_buftag_t *)((char *)(buf) + (cp)->cache_buftag)) + +#define KMEM_BUFCTL(cp, buf) \ + ((kmem_bufctl_t *)((char *)(buf) + (cp)->cache_bufctl)) + +#define KMEM_BUF(cp, bcp) \ + ((void *)((char *)(bcp) - (cp)->cache_bufctl)) + +#define KMEM_SLAB(cp, buf) \ + ((kmem_slab_t *)P2END((uintptr_t)(buf), (cp)->cache_slabsize) - 1) + +/* + * Test for using alternate memory at dump time. + */ +#define KMEM_DUMP(cp) ((cp)->cache_flags & KMF_DUMPDIVERT) +#define KMEM_DUMPCC(ccp) ((ccp)->cc_flags & KMF_DUMPDIVERT) + +/* + * The "CPU" macro loads a cpu_t that refers to the cpu that the current + * thread is running on at the time the macro is executed. A context switch + * may occur immediately after loading this data structure, leaving this + * thread pointing at the cpu_t for the previous cpu. This is not a problem; + * we'd just end up checking the previous cpu's per-cpu cache, and then check + * the other layers of the kmem cache if need be. + * + * It's not even a problem if the old cpu gets DR'ed out during the context + * switch. The cpu-remove DR operation bzero()s the cpu_t, but doesn't free + * it. So the cpu_t's cpu_cache_offset would read as 0, causing us to use + * cpu 0's per-cpu cache. + * + * So, there is no need to disable kernel preemption while using the CPU macro + * below since if we have been context switched, there will not be any + * correctness problem, just a momentary use of a different per-cpu cache. + */ + +#define KMEM_CPU_CACHE(cp) \ + (&cp->cache_cpu[CPU_SEQID]) + +#define KMOM_MAGAZINE_VALID(cp, mp) \ + (((kmem_slab_t *)P2END((uintptr_t)(mp), PAGESIZE) - 1)->slab_cache == \ + (cp)->cache_magtype->mt_cache) + +#define KMEM_MAGAZINE_VALID(cp, mp) \ + (((kmem_slab_t *)P2END((uintptr_t)(mp), PAGESIZE) - 1)->slab_cache == \ + (cp)->cache_magtype->mt_cache) + +#define KMEM_SLAB_OFFSET(sp, buf) \ + ((size_t)((uintptr_t)(buf) - (uintptr_t)((sp)->slab_base))) + +#define KMEM_SLAB_MEMBER(sp, buf) \ + (KMEM_SLAB_OFFSET(sp, buf) < (sp)->slab_cache->cache_slabsize) + +#define KMEM_BUFTAG_ALLOC 0xa110c8edUL +#define KMEM_BUFTAG_FREE 0xf4eef4eeUL + +/* slab_later_count thresholds */ +#define KMEM_DISBELIEF 3 + +/* slab_flags */ +#define KMEM_SLAB_NOMOVE 0x1 +#define KMEM_SLAB_MOVE_PENDING 0x2 + +typedef struct kmem_slab { + struct kmem_cache *slab_cache; /* controlling cache */ + void *slab_base; /* base of allocated memory */ + avl_node_t slab_link; /* slab linkage */ + struct kmem_bufctl *slab_head; /* first free buffer */ + long slab_refcnt; /* outstanding allocations */ + long slab_chunks; /* chunks (bufs) in this slab */ + uint32_t slab_stuck_offset; /* unmoved buffer offset */ + uint16_t slab_later_count; /* cf KMEM_CBRC_LATER */ + uint16_t slab_flags; /* bits to mark the slab */ + hrtime_t slab_create_time; /* when was slab created? */ +} kmem_slab_t; + +#define KMEM_HASH_INITIAL 64 + +#define KMEM_HASH(cp, buf) \ + ((cp)->cache_hash_table + \ + (((uintptr_t)(buf) >> (cp)->cache_hash_shift) & (cp)->cache_hash_mask)) + +#define KMEM_CACHE_NAMELEN 31 + +typedef struct kmem_magazine { + void *mag_next; + void *mag_round[1]; /* one or more rounds */ +} kmem_magazine_t; + +/* + * The magazine types for fast per-cpu allocation + */ +typedef struct kmem_magtype { + short mt_magsize; /* magazine size (number of rounds) */ + int mt_align; /* magazine alignment */ + size_t mt_minbuf; /* all smaller buffers qualify */ + size_t mt_maxbuf; /* no larger buffers qualify */ + kmem_cache_t *mt_cache; /* magazine cache */ +} kmem_magtype_t; + +#define KMEM_CPU_CACHE_SIZE 128 /* must be power of 2 */ +#define KMEM_CPU_PAD (KMEM_CPU_CACHE_SIZE - sizeof (kmutex_t) - \ + 2 * sizeof (uint64_t) - 2 * sizeof (void *) - sizeof (int) - \ + 5 * sizeof (short)) +#define KMEM_CACHE_SIZE(ncpus) \ + __builtin_offsetof(kmem_cache_t, cache_cpu[ncpus]) + + /* Offset from kmem_cache->cache_cpu for per cpu caches */ +#define KMEM_CPU_CACHE_OFFSET(cpuid) \ + __builtin_offsetof(kmem_cache_t, cache_cpu[cpuid]) - \ + __builtin_offsetof(kmem_cache_t, cache_cpu) + +/* + * Per CPU cache data + */ +typedef struct kmem_cpu_cache { + kmutex_t cc_lock; /* protects this cpu's local cache */ + uint64_t cc_alloc; /* allocations from this cpu */ + uint64_t cc_free; /* frees to this cpu */ + kmem_magazine_t *cc_loaded; /* the currently loaded magazine */ + kmem_magazine_t *cc_ploaded; /* the previously loaded magazine */ + int cc_flags; /* CPU-local copy of cache_flags */ + short cc_rounds; /* number of objects in loaded mag */ + short cc_prounds; /* number of objects in previous mag */ + short cc_magsize; /* number of rounds in a full mag */ + short cc_dump_rounds; /* dump time copy of cc_rounds */ + short cc_dump_prounds; /* dump time copy of cc_prounds */ + char cc_pad[KMEM_CPU_PAD]; /* for nice alignment */ +} kmem_cpu_cache_t; + +/* + * The magazine lists used in the depot. + */ +typedef struct kmem_maglist { + kmem_magazine_t *ml_list; /* magazine list */ + long ml_total; /* number of magazines */ + long ml_min; /* min since last update */ + long ml_reaplimit; /* max reapable magazines */ + uint64_t ml_alloc; /* allocations from this list */ +} kmem_maglist_t; + +typedef struct kmem_defrag { + /* + * Statistics + */ + uint64_t kmd_callbacks; /* move callbacks */ + uint64_t kmd_yes; /* KMEM_CBRC_YES responses */ + uint64_t kmd_no; /* NO responses */ + uint64_t kmd_later; /* LATER responses */ + uint64_t kmd_dont_need; /* DONT_NEED responses */ + uint64_t kmd_dont_know; /* DONT_KNOW responses */ + uint64_t kmd_slabs_freed; /* slabs freed by moves */ + uint64_t kmd_defrags; /* kmem_cache_defrag() */ + uint64_t kmd_scans; /* kmem_cache_scan() */ + + /* + * Consolidator fields + */ + avl_tree_t kmd_moves_pending; /* buffer moves pending */ + list_t kmd_deadlist; /* deferred slab frees */ + size_t kmd_deadcount; /* # of slabs in kmd_deadlist */ + uint8_t kmd_reclaim_numer; /* slab usage threshold */ + uint8_t kmd_pad1; /* compiler padding */ + uint16_t kmd_consolidate; /* triggers consolidator */ + uint32_t kmd_pad2; /* compiler padding */ + size_t kmd_slabs_sought; /* reclaimable slabs sought */ + size_t kmd_slabs_found; /* reclaimable slabs found */ + size_t kmd_tries; /* nth scan interval counter */ + /* + * Fields used to ASSERT that the client does not kmem_cache_free() + * objects passed to the move callback. + */ + void *kmd_from_buf; /* object to move */ + void *kmd_to_buf; /* move destination */ + kthread_t *kmd_thread; /* thread calling move */ +} kmem_defrag_t; + +/* + * Cache callback function types + */ +typedef int (*constructor_fn_t)(void*, void*, int); +typedef void (*destructor_fn_t)(void*, void*); +typedef void (*reclaim_fn_t)(void*); + +/* + * Cache + */ +struct kmem_cache { + +/* + * Statistics + */ + uint64_t cache_slab_create; /* slab creates */ + uint64_t cache_slab_destroy; /* slab destroys */ + uint64_t cache_slab_alloc; /* slab layer allocations */ + uint64_t cache_slab_free; /* slab layer frees */ + uint64_t cache_alloc_fail; /* total failed allocations */ + uint64_t cache_buftotal; /* total buffers */ + uint64_t cache_bufmax; /* max buffers ever */ + uint64_t cache_bufslab; /* buffers free in slab layer */ + uint64_t cache_reap; /* cache reaps */ + uint64_t cache_rescale; /* hash table rescales */ + uint64_t cache_lookup_depth; /* hash lookup depth */ + uint64_t cache_depot_contention; /* mutex contention count */ + uint64_t cache_depot_contention_prev; /* previous snapshot */ + uint64_t cache_alloc_count; /* Number of allocations in cache */ + /* successful calls with KM_NO_VBA flag set */ + uint64_t no_vba_success; + uint64_t no_vba_fail; + /* number of times we set arc growth suppression time */ + uint64_t arc_no_grow_set; + /* number of times spl_zio_is_suppressed returned true for this cache */ + uint64_t arc_no_grow; + + /* + * Cache properties + */ + char cache_name[KMEM_CACHE_NAMELEN + 1]; + size_t cache_bufsize; /* object size */ + size_t cache_align; /* object alignment */ + int (*cache_constructor)(void *, void *, int); + void (*cache_destructor)(void *, void *); + void (*cache_reclaim)(void *); + kmem_cbrc_t (*cache_move)(void *, void *, size_t, void *); + void *cache_private; /* opaque arg to callbacks */ + vmem_t *cache_arena; /* vmem source for slabs */ + int cache_cflags; /* cache creation flags */ + int cache_flags; /* various cache state info */ + uint32_t cache_mtbf; /* induced alloc failure rate */ + uint32_t cache_pad1; /* compiler padding */ + kstat_t *cache_kstat; /* exported statistics */ + list_node_t cache_link; /* cache linkage */ + + /* + * Slab layer + */ + kmutex_t cache_lock; /* protects slab layer */ + + size_t cache_chunksize; /* buf + alignment [+ debug] */ + size_t cache_slabsize; /* size of a slab */ + size_t cache_maxchunks; /* max buffers per slab */ + size_t cache_bufctl; /* buf-to-bufctl distance */ + size_t cache_buftag; /* buf-to-buftag distance */ + size_t cache_verify; /* bytes to verify */ + size_t cache_contents; /* bytes of saved content */ + size_t cache_color; /* next slab color */ + size_t cache_mincolor; /* maximum slab color */ + size_t cache_maxcolor; /* maximum slab color */ + size_t cache_hash_shift; /* get to interesting bits */ + size_t cache_hash_mask; /* hash table mask */ + list_t cache_complete_slabs; /* completely allocated slabs */ + size_t cache_complete_slab_count; + avl_tree_t cache_partial_slabs; /* partial slab freelist */ + size_t cache_partial_binshift; /* for AVL sort bins */ + kmem_cache_t *cache_bufctl_cache; /* source of bufctls */ + kmem_bufctl_t **cache_hash_table; /* hash table base */ + kmem_defrag_t *cache_defrag; /* slab consolidator fields */ + + /* + * Depot layer + */ + kmutex_t cache_depot_lock; /* protects depot */ + kmem_magtype_t *cache_magtype; /* magazine type */ + kmem_maglist_t cache_full; /* full magazines */ + kmem_maglist_t cache_empty; /* empty magazines */ + void *cache_dumpfreelist; /* heap during crash dump */ + void *cache_dumplog; /* log entry during dump */ + + /* + * Per CPU structures + */ + // XNU adjust to suit __builtin_offsetof + kmem_cpu_cache_t cache_cpu[1]; /* per-cpu data */ + +}; + +typedef struct kmem_cpu_log_header { + kmutex_t clh_lock; + char *clh_current; + size_t clh_avail; + int clh_chunk; + int clh_hits; +#if defined(SPL_DEBUG_MUTEX) + char clh_pad[128 - sizeof (kmutex_t) - sizeof (char *) - + sizeof (size_t) - 2 * sizeof (int)]; +#else + char clh_pad[64 - sizeof (kmutex_t) - sizeof (char *) - + sizeof (size_t) - 2 * sizeof (int)]; +#endif +} kmem_cpu_log_header_t; + +typedef struct kmem_log_header { + kmutex_t lh_lock; + char *lh_base; + int *lh_free; + size_t lh_chunksize; + int lh_nchunks; + int lh_head; + int lh_tail; + int lh_hits; + kmem_cpu_log_header_t lh_cpu[1]; /* ncpus actually allocated */ +} kmem_log_header_t; + +/* kmem_move kmm_flags */ +#define KMM_DESPERATE 0x1 +#define KMM_NOTIFY 0x2 +#define KMM_DEBUG 0x4 + +typedef struct kmem_move { + kmem_slab_t *kmm_from_slab; + void *kmm_from_buf; + void *kmm_to_buf; + avl_node_t kmm_entry; + int kmm_flags; +} kmem_move_t; + +/* + * In order to consolidate partial slabs, it must be possible for the cache to + * have partial slabs. + */ +#define KMEM_IS_MOVABLE(cp) \ + (((cp)->cache_chunksize * 2) <= (cp)->cache_slabsize) + +#endif diff --git a/include/os/macos/spl/sys/kstat.h b/include/os/macos/spl/sys/kstat.h new file mode 100644 index 000000000000..7f16ab77913f --- /dev/null +++ b/include/os/macos/spl/sys/kstat.h @@ -0,0 +1,223 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SPL_KSTAT_H +#define _SPL_KSTAT_H + +#include +#include +#include +#include +#include + +#define KSTAT_STRLEN 255 +#define KSTAT_RAW_MAX (128*1024) + +/* + * For reference valid classes are: + * disk, tape, net, controller, vm, kvm, hat, streams, kstat, misc + */ + +#define KSTAT_TYPE_RAW 0 /* can be anything; ks_ndata >= 1 */ +#define KSTAT_TYPE_NAMED 1 /* name/value pair; ks_ndata >= 1 */ +#define KSTAT_TYPE_INTR 2 /* interrupt stats; ks_ndata == 1 */ +#define KSTAT_TYPE_IO 3 /* I/O stats; ks_ndata == 1 */ +#define KSTAT_TYPE_TIMER 4 /* event timer; ks_ndata >= 1 */ +#define KSTAT_TYPE_TXG 5 /* txg sync; ks_ndata >= 1 */ +#define KSTAT_NUM_TYPES 6 + +#define KSTAT_DATA_CHAR 0 +#define KSTAT_DATA_INT32 1 +#define KSTAT_DATA_UINT32 2 +#define KSTAT_DATA_INT64 3 +#define KSTAT_DATA_UINT64 4 +#define KSTAT_DATA_LONG 5 +#define KSTAT_DATA_ULONG 6 +#define KSTAT_DATA_STRING 7 +#define KSTAT_NUM_DATAS 8 + +#define KSTAT_INTR_HARD 0 +#define KSTAT_INTR_SOFT 1 +#define KSTAT_INTR_WATCHDOG 2 +#define KSTAT_INTR_SPURIOUS 3 +#define KSTAT_INTR_MULTSVC 4 +#define KSTAT_NUM_INTRS 5 + +#define KSTAT_FLAG_VIRTUAL 0x01 +#define KSTAT_FLAG_VAR_SIZE 0x02 +#define KSTAT_FLAG_WRITABLE 0x04 +#define KSTAT_FLAG_PERSISTENT 0x08 +#define KSTAT_FLAG_DORMANT 0x10 +#define KSTAT_FLAG_UNSUPPORTED (KSTAT_FLAG_VAR_SIZE | KSTAT_FLAG_WRITABLE | \ + KSTAT_FLAG_PERSISTENT | KSTAT_FLAG_DORMANT) +#define KSTAT_FLAG_INVALID 0x20 +#define KSTAT_FLAG_LONGSTRINGS 0x40 +#define KSTAT_FLAG_NO_HEADERS 0x80 + +#define KS_MAGIC 0x9d9d9d9d + +/* Dynamic updates */ +#define KSTAT_READ 0 +#define KSTAT_WRITE 1 + +struct kstat_s; + +typedef int kid_t; /* unique kstat id */ +typedef int kstat_update_t(struct kstat_s *, int); /* dynamic update cb */ + +struct seq_file { + char *sf_buf; + size_t sf_size; +}; + +void seq_printf(struct seq_file *m, const char *fmt, ...); + +typedef struct kstat_raw_ops { + int (*headers)(char *buf, size_t size); + int (*seq_headers)(struct seq_file *); + int (*data)(char *buf, size_t size, void *data); + void *(*addr)(struct kstat_s *ksp, loff_t index); +} kstat_raw_ops_t; + +typedef struct kstat_s { + int ks_magic; /* magic value */ + kid_t ks_kid; /* unique kstat ID */ + hrtime_t ks_crtime; /* creation time */ + hrtime_t ks_snaptime; /* last access time */ + char ks_module[KSTAT_STRLEN+1]; /* provider module name */ + int ks_instance; /* provider module instance */ + char ks_name[KSTAT_STRLEN+1]; /* kstat name */ + char ks_class[KSTAT_STRLEN+1]; /* kstat class */ + uchar_t ks_type; /* kstat data type */ + uchar_t ks_flags; /* kstat flags */ + void *ks_data; /* kstat type-specific data */ + uint_t ks_ndata; /* # of type-specific data records */ + size_t ks_data_size; /* size of kstat data section */ + struct proc_dir_entry *ks_proc; /* proc linkage */ + kstat_update_t *ks_update; /* dynamic updates */ + void *ks_private; /* private data */ + void *ks_private1; /* private data */ + kmutex_t ks_private_lock; /* kstat private data lock */ + kmutex_t *ks_lock; /* kstat data lock */ + kstat_raw_ops_t ks_raw_ops; /* ops table for raw type */ + char *ks_raw_buf; /* buf used for raw ops */ + size_t ks_raw_bufsize; /* size of raw ops buffer */ +} kstat_t; + +typedef struct kstat_named_s { + char name[KSTAT_STRLEN]; /* name of counter */ + uchar_t data_type; /* data type */ + union { + char c[16]; /* 128-bit int */ + int32_t i32; /* 32-bit signed int */ + uint32_t ui32; /* 32-bit unsigned int */ + int64_t i64; /* 64-bit signed int */ + uint64_t ui64; /* 64-bit unsigned int */ + long l; /* native signed long */ + ulong_t ul; /* native unsigned long */ + struct { + union { + char *ptr; /* NULL-term string */ + char __pad[8]; /* 64-bit padding */ + } addr; + uint32_t len; /* # bytes for strlen + '\0' */ + } string; + } value; +} kstat_named_t; + +#define KSTAT_NAMED_STR_PTR(knptr) ((knptr)->value.string.addr.ptr) +#define KSTAT_NAMED_STR_BUFLEN(knptr) ((knptr)->value.string.len) + +typedef struct kstat_intr { + uint_t intrs[KSTAT_NUM_INTRS]; +} kstat_intr_t; + +typedef struct kstat_io { + u_longlong_t nread; /* number of bytes read */ + u_longlong_t nwritten; /* number of bytes written */ + uint_t reads; /* number of read operations */ + uint_t writes; /* number of write operations */ + hrtime_t wtime; /* cumulative wait (pre-service) time */ + hrtime_t wlentime; /* cumulative wait len * time product */ + hrtime_t wlastupdate; /* last time wait queue changed */ + hrtime_t rtime; /* cumulative run (service) time */ + hrtime_t rlentime; /* cumulative run length*time product */ + hrtime_t rlastupdate; /* last time run queue changed */ + uint_t wcnt; /* count of elements in wait state */ + uint_t rcnt; /* count of elements in run state */ +} kstat_io_t; + +typedef struct kstat_timer { + char name[KSTAT_STRLEN+1]; /* event name */ + u_longlong_t num_events; /* number of events */ + hrtime_t elapsed_time; /* cumulative elapsed time */ + hrtime_t min_time; /* shortest event duration */ + hrtime_t max_time; /* longest event duration */ + hrtime_t start_time; /* previous event start time */ + hrtime_t stop_time; /* previous event stop time */ +} kstat_timer_t; + +void spl_kstat_init(void); +void spl_kstat_fini(void); + +extern void __kstat_set_raw_ops(kstat_t *ksp, + int (*headers)(char *buf, size_t size), + int (*data)(char *buf, size_t size, void *data), + void* (*addr)(kstat_t *ksp, loff_t index)); + +extern void __kstat_set_seq_raw_ops(kstat_t *ksp, + int (*headers)(struct seq_file *), + int (*data)(char *buf, size_t size, void *data), + void* (*addr)(kstat_t *ksp, loff_t index)); + +extern kstat_t *__kstat_create(const char *ks_module, int ks_instance, + const char *ks_name, const char *ks_class, + uchar_t ks_type, ulong_t ks_ndata, + uchar_t ks_flags); +extern void __kstat_install(kstat_t *ksp); +extern void __kstat_delete(kstat_t *ksp); + +#define kstat_create(m, i, n, c, t, s, f) \ + __kstat_create(m, i, n, c, t, s, f) +#define kstat_install(k) __kstat_install(k) +#define kstat_delete(k) __kstat_delete(k) + +extern void kstat_waitq_enter(kstat_io_t *); +extern void kstat_waitq_exit(kstat_io_t *); +extern void kstat_runq_enter(kstat_io_t *); +extern void kstat_runq_exit(kstat_io_t *); +extern void kstat_named_init(kstat_named_t *, const char *, uchar_t); + +#define kstat_set_seq_raw_ops(k, h, d, a) __kstat_set_seq_raw_ops(k, h, d, a) +#define kstat_set_raw_ops(k, h, d, a) __kstat_set_raw_ops(k, h, d, a) +void kstat_named_setstr(kstat_named_t *knp, const char *src); + +struct sbuf; +struct sysctl_req; +extern void sbuf_finish(struct sbuf *s); +extern struct sbuf *sbuf_new_for_sysctl(struct sbuf *s, char *buf, + int length, struct sysctl_req *req); + +#endif /* _SPL_KSTAT_H */ diff --git a/include/os/macos/spl/sys/list.h b/include/os/macos/spl/sys/list.h new file mode 100644 index 000000000000..c9a72a53a6ad --- /dev/null +++ b/include/os/macos/spl/sys/list.h @@ -0,0 +1,145 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#ifndef _SPL_LIST_H +#define _SPL_LIST_H + +#include +#include + +/* + * NOTE: I have implemented the Solaris list API in terms of the native + * linux API. This has certain advantages in terms of leveraging the linux + * list debugging infrastructure, but it also means that the internals of a + * list differ slightly than on Solaris. This is not a problem as long as + * all callers stick to the published API. The two major differences are: + * + * 1) A list_node_t is mapped to a linux list_head struct which changes + * the name of the list_next/list_prev pointers to next/prev respectively. + * + * 2) A list_node_t which is not attached to a list on Solaris is denoted + * by having its list_next/list_prev pointers set to NULL. Under linux + * the next/prev pointers are set to LIST_POISON1 and LIST_POISON2 + * respectively. At this moment this only impacts the implementation + * of the list_link_init() and list_link_active() functions. + */ + +typedef struct list_node { + struct list_node *list_next; + struct list_node *list_prev; +} list_node_t; + + + +typedef struct list { + size_t list_size; + size_t list_offset; + list_node_t list_head; +} list_t; + +void list_create(list_t *, size_t, size_t); +void list_destroy(list_t *); + +void list_insert_after(list_t *, void *, void *); +void list_insert_before(list_t *, void *, void *); +void list_insert_head(list_t *, void *); +void list_insert_tail(list_t *, void *); +void list_remove(list_t *, void *); +void list_move_tail(list_t *, list_t *); + +void *list_head(list_t *); +void *list_tail(list_t *); +void *list_next(list_t *, void *); +void *list_prev(list_t *, void *); + +int list_link_active(list_node_t *); +int list_is_empty(list_t *); + +#define LIST_POISON1 NULL +#define LIST_POISON2 NULL + +#define list_d2l(a, obj) ((list_node_t *)(((char *)obj) + (a)->list_offset)) +#define list_object(a, node) ((void *)(((char *)node) - (a)->list_offset)) +#define list_empty(a) ((a)->list_head.list_next == &(a)->list_head) + + +static inline void +list_link_init(list_node_t *node) +{ + node->list_next = LIST_POISON1; + node->list_prev = LIST_POISON2; +} + +static inline void +__list_del(list_node_t *prev, list_node_t *next) +{ + next->list_prev = prev; + prev->list_next = next; +} + +static inline void list_del(list_node_t *entry) +{ + __list_del(entry->list_prev, entry->list_next); + entry->list_next = LIST_POISON1; + entry->list_prev = LIST_POISON2; +} + +static inline void * +list_remove_head(list_t *list) +{ + list_node_t *head = list->list_head.list_next; + if (head == &list->list_head) + return (NULL); + + list_del(head); + return (list_object(list, head)); +} + +static inline void * +list_remove_tail(list_t *list) +{ + list_node_t *tail = list->list_head.list_prev; + if (tail == &list->list_head) + return (NULL); + + list_del(tail); + return (list_object(list, tail)); +} + +static inline void +list_link_replace(list_node_t *old_node, list_node_t *new_node) +{ + ASSERT(list_link_active(old_node)); + ASSERT(!list_link_active(new_node)); + + new_node->list_next = old_node->list_next; + new_node->list_prev = old_node->list_prev; + old_node->list_prev->list_next = new_node; + old_node->list_next->list_prev = new_node; + list_link_init(old_node); +} + +#endif /* SPL_LIST_H */ diff --git a/include/os/macos/spl/sys/mod_os.h b/include/os/macos/spl/sys/mod_os.h new file mode 100644 index 000000000000..98d32877caa4 --- /dev/null +++ b/include/os/macos/spl/sys/mod_os.h @@ -0,0 +1,90 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#ifndef _SPL_MOD_H +#define _SPL_MOD_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include + +#define MODULE_INIT(s) +#define MODULE_AUTHOR(s) +#define MODULE_LICENSE(s) +#define MODULE_VERSION(s) +#define ZFS_MODULE_DESCRIPTION(s) +#define ZFS_MODULE_AUTHOR(s) +#define ZFS_MODULE_LICENSE(s) +#define ZFS_MODULE_VERSION(s) + +#define ZFS_MODULE_PARAM_CALL(scope_prefix, name_prefix, name, setfunc, \ + getfunc, perm, desc) + +#define __init __attribute__((unused)) +#define __exit __attribute__((unused)) + +/* + * The init/fini functions need to be called, but they are all static + */ +#define module_init(fn) \ + int wrap_ ## fn(void) \ + { \ + return (fn()); \ + } + +#define module_exit(fn) \ + void wrap_ ## fn(void) \ + { \ + fn(); \ + } + +// XNU defines SYSCTL_HANDLER_ARGS with "()" so it no worky. +// #define ZFS_MODULE_PARAM_ARGS SYSCTL_HANDLER_ARGS +#define ZFS_MODULE_PARAM_ARGS \ + struct sysctl_oid *oidp, void *arg1, int arg2, struct sysctl_req *req + +#define ZFS_MODULE_PARAM(A, B, C, D, E, F) +#define module_param_call(a, b, c, d, e) +#define module_param_named(a, b, c, d) + +#define ZFS_MODULE_VIRTUAL_PARAM_CALL ZFS_MODULE_PARAM_CALL +#define module_init_early(fn) \ +void \ +wrap_ ## fn(void *dummy __unused) \ +{ \ + fn(); \ +} + +kern_return_t spl_start(kmod_info_t *ki, void *d); +kern_return_t spl_stop(kmod_info_t *ki, void *d); + +struct zfs_kernel_param_s; +typedef struct zfs_kernel_param_s zfs_kernel_param_t; + +extern int param_set_uint(char *v, zfs_kernel_param_t *kp); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SPL_MOD_H */ diff --git a/include/os/macos/spl/sys/mutex.h b/include/os/macos/spl/sys/mutex.h new file mode 100644 index 000000000000..1afdae5740cd --- /dev/null +++ b/include/os/macos/spl/sys/mutex.h @@ -0,0 +1,156 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * OSX mutex functions + * + * Jorgen Lundman + * + */ + +#ifndef OSX_MUTEX_H +#define OSX_MUTEX_H + +#include +#include + +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + MUTEX_ADAPTIVE = 0, /* spin if owner is running, otherwise block */ + MUTEX_SPIN = 1, /* block interrupts and spin */ + MUTEX_DRIVER = 4, /* driver (DDI) mutex */ + MUTEX_DEFAULT = 6 /* kernel default mutex */ +} kmutex_type_t; + +#define MUTEX_NOLOCKDEP 0 + +/* + * Alas lck_mtx_t; is opaque and not available at compile time, and we + * really want to embed them. Luckily, mutex size has not changed in + * many versions of OSX. We should possibly to a startup check of + * the size though. + */ +typedef struct { + uint32_t opaque[4]; +} wrapper_mutex_t; + +/* + * To enable watchdog to keep an eye on mutex being held for too long + * define this debug variable. + */ + +#ifdef DEBUG +#define SPL_DEBUG_MUTEX +#endif + +#ifdef SPL_DEBUG_MUTEX +#define SPL_MUTEX_WATCHDOG_SLEEP 10 /* How long to sleep between checking */ +#define SPL_MUTEX_WATCHDOG_TIMEOUT 60 /* When is a mutex held too long? */ +#endif + +/* + * Solaris kmutex defined. + * + * and is embedded into ZFS structures (see dbuf) so we need to match the + * size carefully. It appears to be 32 bytes. Or rather, it needs to be + * aligned. + */ + +typedef struct kmutex { + void *m_owner; + wrapper_mutex_t m_lock; + + uint64_t m_waiters; + uint64_t m_sleepers; + +#ifdef SPL_DEBUG_MUTEX + void *leak; + uint64_t m_initialised; +#define MUTEX_INIT 0x123456789abcdef0ULL +#define MUTEX_DESTROYED 0xaabbccddaabbccddULL +#endif + +} kmutex_t; + +#include + +#define MUTEX_HELD(x) (mutex_owned(x)) +#define MUTEX_NOT_HELD(x) (!mutex_owned(x)) + +/* + * On OS X, CoreStorage provides these symbols, so we have to redefine them, + * preferably without having to modify SPL users. + */ +#ifdef SPL_DEBUG_MUTEX + +#define mutex_init(A, B, C, D) \ + spl_mutex_init(A, B, C, D, __FILE__, __FUNCTION__, __LINE__) +void spl_mutex_init(kmutex_t *mp, char *name, kmutex_type_t type, + void *ibc, const char *f, const char *fn, int l); + +#else + +#define mutex_init spl_mutex_init +void spl_mutex_init(kmutex_t *mp, char *name, kmutex_type_t type, void *ibc); + +#endif + +#ifdef SPL_DEBUG_MUTEX +#define mutex_enter(X) spl_mutex_enter((X), __FILE__, __LINE__) +void spl_mutex_enter(kmutex_t *mp, char *file, int line); +#else +#define mutex_enter spl_mutex_enter +void spl_mutex_enter(kmutex_t *mp); +#endif + +#define mutex_enter_nested(A, B) mutex_enter(A) + +#define mutex_destroy spl_mutex_destroy +#define mutex_exit spl_mutex_exit +#define mutex_tryenter spl_mutex_tryenter +#define mutex_owned spl_mutex_owned +#define mutex_owner spl_mutex_owner + +void spl_mutex_destroy(kmutex_t *mp); +void spl_mutex_exit(kmutex_t *mp); +int spl_mutex_tryenter(kmutex_t *mp); +int spl_mutex_owned(kmutex_t *mp); + +struct thread *spl_mutex_owner(kmutex_t *mp); + +int spl_mutex_subsystem_init(void); +void spl_mutex_subsystem_fini(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/os/macos/spl/sys/param.h b/include/os/macos/spl/sys/param.h new file mode 100644 index 000000000000..1d01fa47b4a9 --- /dev/null +++ b/include/os/macos/spl/sys/param.h @@ -0,0 +1,43 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_PARAM_H +#define _SPL_PARAM_H + +#include +#include + +#include_next +#include + +/* Pages to bytes and back */ +#define ptob(pages) (pages << PAGE_SHIFT) +#define btop(bytes) (bytes >> PAGE_SHIFT) + +#define MAXUID UINT32_MAX + +#endif /* SPL_PARAM_H */ diff --git a/include/os/macos/spl/sys/policy.h b/include/os/macos/spl/sys/policy.h new file mode 100644 index 000000000000..2e786ea291e1 --- /dev/null +++ b/include/os/macos/spl/sys/policy.h @@ -0,0 +1,91 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2008 MacZFS + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_POLICY_H +#define _SPL_POLICY_H + +#ifdef _KERNEL + +#include +#include + +struct vattr; + +int secpolicy_fs_unmount(cred_t *, struct mount *); +int secpolicy_nfs(const cred_t *); +int secpolicy_sys_config(const cred_t *, boolean_t); +int secpolicy_zfs(const cred_t *); +int secpolicy_zinject(const cred_t *); + +/* + * This function to be called from xxfs_setattr(). + * Must be called with the node's attributes read-write locked. + * + * cred_t * - acting credentials + * struct vnode * - vnode we're operating on + * struct vattr *va - new attributes, va_mask may be + * changed on return from a call + * struct vattr *oldva - old attributes, need include owner + * and mode only + * int flags - setattr flags + * int iaccess(void *node, int mode, cred_t *cr) + * - non-locking internal access function + * mode be checked + * w/ VREAD|VWRITE|VEXEC, not fs + * internal mode encoding. + * + * void *node - internal node (inode, tmpnode) to + * pass as arg to iaccess + */ +int secpolicy_vnode_setattr(cred_t *, struct vnode *, vattr_t *, + const vattr_t *, int, int (void *, int, cred_t *), void *); + +int secpolicy_vnode_stky_modify(const cred_t *); +int secpolicy_setid_setsticky_clear(struct vnode *vp, vattr_t *vap, + const vattr_t *ovap, cred_t *cr); + +int secpolicy_vnode_remove(struct vnode *, const cred_t *); +int secpolicy_vnode_create_gid(const cred_t *); +int secpolicy_vnode_setids_setgids(struct vnode *, const cred_t *, gid_t); +int secpolicy_vnode_setdac(struct vnode *, const cred_t *, uid_t); +int secpolicy_vnode_chown(struct vnode *, const cred_t *, uid_t); +struct znode; +int secpolicy_vnode_setid_retain(struct znode *, const cred_t *, boolean_t); +int secpolicy_xvattr(vattr_t *, uid_t, const cred_t *, mode_t); +int secpolicy_setid_clear(vattr_t *, const cred_t *); +int secpolicy_basic_link(const cred_t *); +int secpolicy_fs_mount_clearopts(const cred_t *, struct mount *); +int secpolicy_fs_mount(const cred_t *, struct vnode *, struct mount *); +int secpolicy_zfs_proc(const cred_t *, proc_t *); +int secpolicy_vnode_any_access(const cred_t *, struct vnode *, uid_t); +int secpolicy_vnode_access2(const cred_t *, struct vnode *, + uid_t, mode_t, mode_t); + +#endif /* _KERNEL */ + +#endif /* SPL_POLICY_H */ diff --git a/include/os/macos/spl/sys/priv.h b/include/os/macos/spl/sys/priv.h new file mode 100644 index 000000000000..a8da8f101ec5 --- /dev/null +++ b/include/os/macos/spl/sys/priv.h @@ -0,0 +1,531 @@ +/* + * Copyright (c) 2006 nCircle Network Security, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson for the TrustedBSD + * Project under contract to nCircle Network Security, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR, NCIRCLE NETWORK SECURITY, + * INC., OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Privilege checking interface for BSD kernel. + */ +#ifndef _SPL_PRIV_H +#define _SPL_PRIV_H + +/* + * Privilege list, sorted loosely by kernel subsystem. + * + * Think carefully before adding or reusing one of these privileges -- are + * there existing instances referring to the same privilege? Third party + * vendors may request the assignment of privileges to be used in loadable + * modules. Particular numeric privilege assignments are part of the + * loadable kernel module ABI, and should not be changed across minor + * releases. + * + * When adding a new privilege, remember to determine if it's appropriate for + * use in jail, and update the privilege switch in kern_jail.c as necessary. + */ + +/* + * Track beginning of privilege list. + */ +#define _PRIV_LOWEST 1 + +/* + * The remaining privileges typically correspond to one or a small + * number of specific privilege checks, and have (relatively) precise + * meanings. They are loosely sorted into a set of base system + * privileges, such as the ability to reboot, and then loosely by + * subsystem, indicated by a subsystem name. + */ +#define _PRIV_ROOT 1 /* Removed. */ +#define PRIV_ACCT 2 /* Manage process accounting. */ +#define PRIV_MAXFILES 3 /* Exceed system open files limit. */ +#define PRIV_MAXPROC 4 /* Exceed system processes limit. */ +#define PRIV_KTRACE 5 /* Set/clear KTRFAC_ROOT on ktrace. */ +#define PRIV_SETDUMPER 6 /* Configure dump device. */ +#define PRIV_REBOOT 8 /* Can reboot system. */ +#define PRIV_SWAPON 9 /* Can swapon(). */ +#define PRIV_SWAPOFF 10 /* Can swapoff(). */ +#define PRIV_MSGBUF 11 /* Can read kernel message buffer. */ +#define PRIV_IO 12 /* Can perform low-level I/O. */ +#define PRIV_KEYBOARD 13 /* Reprogram keyboard. */ +#define PRIV_DRIVER 14 /* Low-level driver privilege. */ +#define PRIV_ADJTIME 15 /* Set time adjustment. */ +#define PRIV_NTP_ADJTIME 16 /* Set NTP time adjustment. */ +#define PRIV_CLOCK_SETTIME 17 /* Can call clock_settime. */ +#define PRIV_SETTIMEOFDAY 18 /* Can call settimeofday. */ +#define _PRIV_SETHOSTID 19 /* Removed. */ +#define _PRIV_SETDOMAINNAME 20 /* Removed. */ + +/* + * Audit subsystem privileges. + */ +#define PRIV_AUDIT_CONTROL 40 /* Can configure audit. */ +#define PRIV_AUDIT_FAILSTOP 41 /* Can run during audit fail stop. */ +#define PRIV_AUDIT_GETAUDIT 42 /* Can get proc audit properties. */ +#define PRIV_AUDIT_SETAUDIT 43 /* Can set proc audit properties. */ +#define PRIV_AUDIT_SUBMIT 44 /* Can submit an audit record. */ + +/* + * Credential management privileges. + */ +#define PRIV_CRED_SETUID 50 /* setuid. */ +#define PRIV_CRED_SETEUID 51 /* seteuid to !ruid and !svuid. */ +#define PRIV_CRED_SETGID 52 /* setgid. */ +#define PRIV_CRED_SETEGID 53 /* setgid to !rgid and !svgid. */ +#define PRIV_CRED_SETGROUPS 54 /* Set process additional groups. */ +#define PRIV_CRED_SETREUID 55 /* setreuid. */ +#define PRIV_CRED_SETREGID 56 /* setregid. */ +#define PRIV_CRED_SETRESUID 57 /* setresuid. */ +#define PRIV_CRED_SETRESGID 58 /* setresgid. */ +#define PRIV_SEEOTHERGIDS 59 /* Exempt bsd.seeothergids. */ +#define PRIV_SEEOTHERUIDS 60 /* Exempt bsd.seeotheruids. */ + +/* + * Debugging privileges. + */ +#define PRIV_DEBUG_DIFFCRED 80 /* Exempt debugging other users. */ +#define PRIV_DEBUG_SUGID 81 /* Exempt debugging setuid proc. */ +#define PRIV_DEBUG_UNPRIV 82 /* Exempt unprivileged debug limit. */ +#define PRIV_DEBUG_DENIED 83 /* Exempt P2_NOTRACE. */ + +/* + * Dtrace privileges. + */ +#define PRIV_DTRACE_KERNEL 90 /* Allow use of DTrace on the kernel. */ +#define PRIV_DTRACE_PROC 91 /* Allow attaching DTrace to process. */ +#define PRIV_DTRACE_USER 92 /* Process may submit DTrace events. */ + +/* + * Firmware privilegs. + */ +#define PRIV_FIRMWARE_LOAD 100 /* Can load firmware. */ + +/* + * Jail privileges. + */ +#define PRIV_JAIL_ATTACH 110 /* Attach to a jail. */ +#define PRIV_JAIL_SET 111 /* Set jail parameters. */ +#define PRIV_JAIL_REMOVE 112 /* Remove a jail. */ + +/* + * Kernel environment priveleges. + */ +#define PRIV_KENV_SET 120 /* Set kernel env. variables. */ +#define PRIV_KENV_UNSET 121 /* Unset kernel env. variables. */ + +/* + * Loadable kernel module privileges. + */ +#define PRIV_KLD_LOAD 130 /* Load a kernel module. */ +#define PRIV_KLD_UNLOAD 131 /* Unload a kernel module. */ + +/* + * Privileges associated with the MAC Framework and specific MAC policy + * modules. + */ +#define PRIV_MAC_PARTITION 140 /* Privilege in mac_partition policy. */ +#define PRIV_MAC_PRIVS 141 /* Privilege in the mac_privs policy. */ + +/* + * Process-related privileges. + */ +#define PRIV_PROC_LIMIT 160 /* Exceed user process limit. */ +#define PRIV_PROC_SETLOGIN 161 /* Can call setlogin. */ +#define PRIV_PROC_SETRLIMIT 162 /* Can raise resources limits. */ +#define PRIV_PROC_SETLOGINCLASS 163 /* Can call setloginclass(2). */ + +/* + * System V IPC privileges. + */ +#define PRIV_IPC_READ 170 /* Can override IPC read perm. */ +#define PRIV_IPC_WRITE 171 /* Can override IPC write perm. */ +#define PRIV_IPC_ADMIN 172 /* Can override IPC owner-only perm. */ +#define PRIV_IPC_MSGSIZE 173 /* Exempt IPC message queue limit. */ + +/* + * POSIX message queue privileges. + */ +#define PRIV_MQ_ADMIN 180 /* Can override msgq owner-only perm. */ + +/* + * Performance monitoring counter privileges. + */ +#define PRIV_PMC_MANAGE 190 /* Can administer PMC. */ +#define PRIV_PMC_SYSTEM 191 /* Can allocate a system-wide PMC. */ + +/* + * Scheduling privileges. + */ +#define PRIV_SCHED_DIFFCRED 200 /* Exempt scheduling other users. */ +#define PRIV_SCHED_SETPRIORITY 201 /* Can set lower nice value for proc. */ +#define PRIV_SCHED_RTPRIO 202 /* Can set real time scheduling. */ +#define PRIV_SCHED_SETPOLICY 203 /* Can set scheduler policy. */ +#define PRIV_SCHED_SET 204 /* Can set thread scheduler. */ +#define PRIV_SCHED_SETPARAM 205 /* Can set thread scheduler params. */ +#define PRIV_SCHED_CPUSET 206 /* Can manipulate cpusets. */ +#define PRIV_SCHED_CPUSET_INTR 207 /* Can adjust IRQ to CPU binding. */ + +/* + * POSIX semaphore privileges. + */ +#define PRIV_SEM_WRITE 220 /* Can override sem write perm. */ + +/* + * Signal privileges. + */ +#define PRIV_SIGNAL_DIFFCRED 230 /* Exempt signalling other users. */ +#define PRIV_SIGNAL_SUGID 231 /* Non-conserv signal setuid proc. */ + +/* + * Sysctl privileges. + */ +#define PRIV_SYSCTL_DEBUG 240 /* Can invoke sysctl.debug. */ +#define PRIV_SYSCTL_WRITE 241 /* Can write sysctls. */ +#define PRIV_SYSCTL_WRITEJAIL 242 /* Can write sysctls, jail permitted. */ + +/* + * TTY privileges. + */ +#define PRIV_TTY_CONSOLE 250 /* Set console to tty. */ +#define PRIV_TTY_DRAINWAIT 251 /* Set tty drain wait time. */ +#define PRIV_TTY_DTRWAIT 252 /* Set DTR wait on tty. */ +#define PRIV_TTY_EXCLUSIVE 253 /* Override tty exclusive flag. */ +#define _PRIV_TTY_PRISON 254 /* Removed. */ +#define PRIV_TTY_STI 255 /* Simulate input on another tty. */ +#define PRIV_TTY_SETA 256 /* Set tty termios structure. */ + +/* + * UFS-specific privileges. + */ +#define PRIV_UFS_EXTATTRCTL 270 /* Can configure EAs on UFS1. */ +#define PRIV_UFS_QUOTAOFF 271 /* quotaoff(). */ +#define PRIV_UFS_QUOTAON 272 /* quotaon(). */ +#define PRIV_UFS_SETUSE 273 /* setuse(). */ + +/* + * ZFS-specific privileges. + */ +#define PRIV_ZFS_POOL_CONFIG 280 /* Can configure ZFS pools. */ + +/* Can inject faults in the ZFS fault injection framework. */ +#define PRIV_ZFS_INJECT 281 + +/* Can attach/detach ZFS file systems to/from jails. */ +#define PRIV_ZFS_JAIL 282 + +/* + * NFS-specific privileges. + */ +#define PRIV_NFS_DAEMON 290 /* Can become the NFS daemon. */ +#define PRIV_NFS_LOCKD 291 /* Can become NFS lock daemon. */ + +/* + * VFS privileges. + */ +#define PRIV_VFS_READ 310 /* Override vnode DAC read perm. */ +#define PRIV_VFS_WRITE 311 /* Override vnode DAC write perm. */ +#define PRIV_VFS_ADMIN 312 /* Override vnode DAC admin perm. */ +#define PRIV_VFS_EXEC 313 /* Override vnode DAC exec perm. */ +#define PRIV_VFS_LOOKUP 314 /* Override vnode DAC lookup perm. */ +#define PRIV_VFS_BLOCKRESERVE 315 /* Can use free block reserve. */ +#define PRIV_VFS_CHFLAGS_DEV 316 /* Can chflags() a device node. */ +#define PRIV_VFS_CHOWN 317 /* Can set user; group to non-member. */ +#define PRIV_VFS_CHROOT 318 /* chroot(). */ +#define PRIV_VFS_RETAINSUGID 319 /* Can retain sugid bits on change. */ +#define PRIV_VFS_EXCEEDQUOTA 320 /* Exempt from quota restrictions. */ +#define PRIV_VFS_EXTATTR_SYSTEM 321 /* Operate on system EA namespace. */ +#define PRIV_VFS_FCHROOT 322 /* fchroot(). */ +#define PRIV_VFS_FHOPEN 323 /* Can fhopen(). */ +#define PRIV_VFS_FHSTAT 324 /* Can fhstat(). */ +#define PRIV_VFS_FHSTATFS 325 /* Can fhstatfs(). */ +#define PRIV_VFS_GENERATION 326 /* stat() returns generation number. */ +#define PRIV_VFS_GETFH 327 /* Can retrieve file handles. */ +#define PRIV_VFS_GETQUOTA 328 /* getquota(). */ +#define PRIV_VFS_LINK 329 /* bsd.hardlink_check_uid */ +#define PRIV_VFS_MKNOD_BAD 330 /* Can mknod() to mark bad inodes. */ +#define PRIV_VFS_MKNOD_DEV 331 /* Can mknod() to create dev nodes. */ +#define PRIV_VFS_MKNOD_WHT 332 /* Can mknod() to create whiteout. */ +#define PRIV_VFS_MOUNT 333 /* Can mount(). */ +#define PRIV_VFS_MOUNT_OWNER 334 /* Can manage other users' fsystems. */ +#define PRIV_VFS_MOUNT_EXPORTED 335 /* Can set MNT_EXPORTED on mount. */ +#define PRIV_VFS_MOUNT_PERM 336 /* Override dev node perms at mount. */ +#define PRIV_VFS_MOUNT_SUIDDIR 337 /* Can set MNT_SUIDDIR on mount. */ +#define PRIV_VFS_MOUNT_NONUSER 338 /* Can perform a non-user mount. */ +#define PRIV_VFS_SETGID 339 /* Can setgid if not in group. */ +#define PRIV_VFS_SETQUOTA 340 /* setquota(). */ +#define PRIV_VFS_STICKYFILE 341 /* Can set sticky bit on file. */ +#define PRIV_VFS_SYSFLAGS 342 /* Can modify system flags. */ +#define PRIV_VFS_UNMOUNT 343 /* Can unmount(). */ +#define PRIV_VFS_STAT 344 /* Override vnode MAC stat perm. */ + +/* + * Virtual memory privileges. + */ +#define PRIV_VM_MADV_PROTECT 360 /* Can set MADV_PROTECT. */ +#define PRIV_VM_MLOCK 361 /* Can mlock(), mlockall(). */ +#define PRIV_VM_MUNLOCK 362 /* Can munlock(), munlockall(). */ +/* Can override the global swap reservation limits. */ +#define PRIV_VM_SWAP_NOQUOTA 363 +/* Can override the per-uid swap reservation limits. */ +#define PRIV_VM_SWAP_NORLIMIT 364 + +/* + * Device file system privileges. + */ +#define PRIV_DEVFS_RULE 370 /* Can manage devfs rules. */ +#define PRIV_DEVFS_SYMLINK 371 /* Can create symlinks in devfs. */ + +/* + * Random number generator privileges. + */ +#define PRIV_RANDOM_RESEED 380 /* Closing /dev/random reseeds. */ + +/* + * Network stack privileges. + */ +#define PRIV_NET_BRIDGE 390 /* Administer bridge. */ +#define PRIV_NET_GRE 391 /* Administer GRE. */ +#define _PRIV_NET_PPP 392 /* Removed. */ +#define _PRIV_NET_SLIP 393 /* Removed. */ +#define PRIV_NET_BPF 394 /* Monitor BPF. */ +#define PRIV_NET_RAW 395 /* Open raw socket. */ +#define PRIV_NET_ROUTE 396 /* Administer routing. */ +#define PRIV_NET_TAP 397 /* Can open tap device. */ +#define PRIV_NET_SETIFMTU 398 /* Set interface MTU. */ +#define PRIV_NET_SETIFFLAGS 399 /* Set interface flags. */ +#define PRIV_NET_SETIFCAP 400 /* Set interface capabilities. */ +#define PRIV_NET_SETIFNAME 401 /* Set interface name. */ +#define PRIV_NET_SETIFMETRIC 402 /* Set interface metrics. */ +#define PRIV_NET_SETIFPHYS 403 /* Set interface physical layer prop. */ +#define PRIV_NET_SETIFMAC 404 /* Set interface MAC label. */ +#define PRIV_NET_ADDMULTI 405 /* Add multicast addr. to ifnet. */ +#define PRIV_NET_DELMULTI 406 /* Delete multicast addr. from ifnet. */ +#define PRIV_NET_HWIOCTL 407 /* Issue hardware ioctl on ifnet. */ +#define PRIV_NET_SETLLADDR 408 /* Set interface link-level address. */ +#define PRIV_NET_ADDIFGROUP 409 /* Add new interface group. */ +#define PRIV_NET_DELIFGROUP 410 /* Delete interface group. */ +#define PRIV_NET_IFCREATE 411 /* Create cloned interface. */ +#define PRIV_NET_IFDESTROY 412 /* Destroy cloned interface. */ +#define PRIV_NET_ADDIFADDR 413 /* Add protocol addr to interface. */ +#define PRIV_NET_DELIFADDR 414 /* Delete protocol addr on interface. */ +#define PRIV_NET_LAGG 415 /* Administer lagg interface. */ +#define PRIV_NET_GIF 416 /* Administer gif interface. */ +#define PRIV_NET_SETIFVNET 417 /* Move interface to vnet. */ +#define PRIV_NET_SETIFDESCR 418 /* Set interface description. */ +#define PRIV_NET_SETIFFIB 419 /* Set interface fib. */ +#define PRIV_NET_VXLAN 420 /* Administer vxlan. */ + +/* + * 802.11-related privileges. + */ +#define PRIV_NET80211_GETKEY 440 /* Query 802.11 keys. */ +#define PRIV_NET80211_MANAGE 441 /* Administer 802.11. */ + +/* + * Placeholder for AppleTalk privileges, not supported anymore. + */ +#define _PRIV_NETATALK_RESERVEDPORT 450 /* Bind low port number. */ + +/* + * ATM privileges. + */ +#define PRIV_NETATM_CFG 460 +#define PRIV_NETATM_ADD 461 +#define PRIV_NETATM_DEL 462 +#define PRIV_NETATM_SET 463 + +/* + * Bluetooth privileges. + */ +#define PRIV_NETBLUETOOTH_RAW 470 /* Open raw bluetooth socket. */ + +/* + * Netgraph and netgraph module privileges. + */ +#define PRIV_NETGRAPH_CONTROL 480 /* Open netgraph control socket. */ +#define PRIV_NETGRAPH_TTY 481 /* Configure tty for netgraph. */ + +/* + * IPv4 and IPv6 privileges. + */ +#define PRIV_NETINET_RESERVEDPORT 490 /* Bind low port number. */ +#define PRIV_NETINET_IPFW 491 /* Administer IPFW firewall. */ +#define PRIV_NETINET_DIVERT 492 /* Open IP divert socket. */ +#define PRIV_NETINET_PF 493 /* Administer pf firewall. */ +#define PRIV_NETINET_DUMMYNET 494 /* Administer DUMMYNET. */ +#define PRIV_NETINET_CARP 495 /* Administer CARP. */ +#define PRIV_NETINET_MROUTE 496 /* Administer multicast routing. */ +#define PRIV_NETINET_RAW 497 /* Open netinet raw socket. */ +#define PRIV_NETINET_GETCRED 498 /* Query netinet pcb credentials. */ +#define PRIV_NETINET_ADDRCTRL6 499 /* Administer IPv6 address scopes. */ +#define PRIV_NETINET_ND6 500 /* Administer IPv6 neighbor disc. */ +#define PRIV_NETINET_SCOPE6 501 /* Administer IPv6 address scopes. */ +#define PRIV_NETINET_ALIFETIME6 502 /* Administer IPv6 address lifetimes. */ +#define PRIV_NETINET_IPSEC 503 /* Administer IPSEC. */ +#define PRIV_NETINET_REUSEPORT 504 /* Allow [rapid] port/address reuse. */ +#define PRIV_NETINET_SETHDROPTS 505 /* Set certain IPv4/6 header options. */ +#define PRIV_NETINET_BINDANY 506 /* Allow bind to any address. */ +#define PRIV_NETINET_HASHKEY 507 /* Get and set hash keys for IPv4/6. */ + +/* + * Placeholders for IPX/SPX privileges, not supported any more. + */ +#define _PRIV_NETIPX_RESERVEDPORT 520 /* Bind low port number. */ +#define _PRIV_NETIPX_RAW 521 /* Open netipx raw socket. */ + +/* + * NCP privileges. + */ +#define PRIV_NETNCP 530 /* Use another user's connection. */ + +/* + * SMB privileges. + */ +#define PRIV_NETSMB 540 /* Use another user's connection. */ + +/* + * VM86 privileges. + */ +#define PRIV_VM86_INTCALL 550 /* Allow invoking vm86 int handlers. */ + +/* + * Set of reserved privilege values, which will be allocated to code as + * needed, in order to avoid renumbering later privileges due to insertion. + */ +#define _PRIV_RESERVED0 560 +#define _PRIV_RESERVED1 561 +#define _PRIV_RESERVED2 562 +#define _PRIV_RESERVED3 563 +#define _PRIV_RESERVED4 564 +#define _PRIV_RESERVED5 565 +#define _PRIV_RESERVED6 566 +#define _PRIV_RESERVED7 567 +#define _PRIV_RESERVED8 568 +#define _PRIV_RESERVED9 569 +#define _PRIV_RESERVED10 570 +#define _PRIV_RESERVED11 571 +#define _PRIV_RESERVED12 572 +#define _PRIV_RESERVED13 573 +#define _PRIV_RESERVED14 574 +#define _PRIV_RESERVED15 575 + +/* + * Define a set of valid privilege numbers that can be used by loadable + * modules that don't yet have privilege reservations. Ideally, these should + * not be used, since their meaning is opaque to any policies that are aware + * of specific privileges, such as jail, and as such may be arbitrarily + * denied. + */ +#define PRIV_MODULE0 600 +#define PRIV_MODULE1 601 +#define PRIV_MODULE2 602 +#define PRIV_MODULE3 603 +#define PRIV_MODULE4 604 +#define PRIV_MODULE5 605 +#define PRIV_MODULE6 606 +#define PRIV_MODULE7 607 +#define PRIV_MODULE8 608 +#define PRIV_MODULE9 609 +#define PRIV_MODULE10 610 +#define PRIV_MODULE11 611 +#define PRIV_MODULE12 612 +#define PRIV_MODULE13 613 +#define PRIV_MODULE14 614 +#define PRIV_MODULE15 615 + +/* + * DDB(4) privileges. + */ +#define PRIV_DDB_CAPTURE 620 /* Allow reading of DDB capture log. */ + +/* + * Arla/nnpfs privileges. + */ +#define PRIV_NNPFS_DEBUG 630 /* Perforn ARLA_VIOC_NNPFSDEBUG. */ + +/* + * cpuctl(4) privileges. + */ +#define PRIV_CPUCTL_WRMSR 640 /* Write model-specific register. */ +#define PRIV_CPUCTL_UPDATE 641 /* Update cpu microcode. */ + +/* + * Capi4BSD privileges. + */ +#define PRIV_C4B_RESET_CTLR 650 /* Load firmware, reset controller. */ +#define PRIV_C4B_TRACE 651 /* Unrestricted CAPI message tracing. */ + +/* + * OpenAFS privileges. + */ +#define PRIV_AFS_ADMIN 660 /* Can change AFS client settings. */ +#define PRIV_AFS_DAEMON 661 /* Can become the AFS daemon. */ + +/* + * Resource Limits privileges. + */ +#define PRIV_RCTL_GET_RACCT 670 +#define PRIV_RCTL_GET_RULES 671 +#define PRIV_RCTL_GET_LIMITS 672 +#define PRIV_RCTL_ADD_RULE 673 +#define PRIV_RCTL_REMOVE_RULE 674 + +/* + * mem(4) privileges. + */ +#define PRIV_KMEM_READ 680 /* Open mem/kmem for reading. */ +#define PRIV_KMEM_WRITE 681 /* Open mem/kmem for writing. */ + +/* + * Track end of privilege list. + */ +#define _PRIV_HIGHEST 682 + +/* + * Validate that a named privilege is known by the privilege system. Invalid + * privileges presented to the privilege system by a priv_check interface + * will result in a panic. This is only approximate due to sparse allocation + * of the privilege space. + */ +#define PRIV_VALID(x) ((x) > _PRIV_LOWEST && (x) < _PRIV_HIGHEST) + +#ifdef _KERNEL +/* + * Privilege check interfaces, modeled after historic suser() interfaces, but + * with the addition of a specific privilege name. No flags are currently + * defined for the API. Historically, flags specified using the real uid + * instead of the effective uid, and whether or not the check should be + * allowed in jail. + */ +struct thread; +struct ucred; +int priv_check(struct thread *td, int priv); +int priv_check_cred(struct ucred *cred, int priv, int flags); +#endif + +#endif /* _SPL_PRIV_H */ diff --git a/include/os/macos/spl/sys/proc.h b/include/os/macos/spl/sys/proc.h new file mode 100644 index 000000000000..132964d9c1c1 --- /dev/null +++ b/include/os/macos/spl/sys/proc.h @@ -0,0 +1,47 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013, 2020 Jorgen Lundman + * + */ + +#ifndef _SPL_PROC_H +#define _SPL_PROC_H + +#include +#include +#include_next +#include +#include + +#define proc_t struct proc + +extern proc_t p0; /* process 0 */ + +static inline boolean_t +zfs_proc_is_caller(proc_t *p) +{ + return (p == curproc); +} + +#endif /* SPL_PROC_H */ diff --git a/include/os/macos/spl/sys/processor.h b/include/os/macos/spl/sys/processor.h new file mode 100644 index 000000000000..d077817e94ea --- /dev/null +++ b/include/os/macos/spl/sys/processor.h @@ -0,0 +1,37 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_PROCESSOR_H +#define _SPL_PROCESSOR_H + +#include + +extern uint32_t getcpuid(void); + +typedef int processorid_t; + +#endif /* _SPL_PROCESSOR_H */ diff --git a/include/os/macos/spl/sys/procfs_list.h b/include/os/macos/spl/sys/procfs_list.h new file mode 100644 index 000000000000..d77fb72c3066 --- /dev/null +++ b/include/os/macos/spl/sys/procfs_list.h @@ -0,0 +1,64 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _SPL_PROCFS_LIST_H +#define _SPL_PROCFS_LIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct procfs_list procfs_list_t; +struct procfs_list { + void *pl_private; + void *pl_next_data; + kmutex_t pl_lock; + list_t pl_list; + uint64_t pl_next_id; + int (*pl_show)(struct seq_file *f, void *p); + int (*pl_show_header)(struct seq_file *f); + int (*pl_clear)(procfs_list_t *procfs_list); + size_t pl_node_offset; +}; + +typedef struct procfs_list_node { + list_node_t pln_link; + uint64_t pln_id; +} procfs_list_node_t; + +void procfs_list_install(const char *module, + const char *submodule, + const char *name, + mode_t mode, + procfs_list_t *procfs_list, + int (*show)(struct seq_file *f, void *p), + int (*show_header)(struct seq_file *f), + int (*clear)(procfs_list_t *procfs_list), + size_t procfs_list_node_off); +void procfs_list_uninstall(procfs_list_t *procfs_list); +void procfs_list_destroy(procfs_list_t *procfs_list); +void procfs_list_add(procfs_list_t *procfs_list, void *p); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/os/macos/spl/sys/random.h b/include/os/macos/spl/sys/random.h new file mode 100644 index 000000000000..c69184cc84e7 --- /dev/null +++ b/include/os/macos/spl/sys/random.h @@ -0,0 +1,48 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_RANDOM_H +#define _SPL_RANDOM_H + +#include_next + + +static inline int +random_get_bytes(uint8_t *ptr, size_t len) +{ + read_random(ptr, len); + return (0); +} + +static inline int +random_get_pseudo_bytes(uint8_t *ptr, size_t len) +{ + read_random(ptr, len); + return (0); +} + +#endif /* _SPL_RANDOM_H */ diff --git a/include/os/macos/spl/sys/rwlock.h b/include/os/macos/spl/sys/rwlock.h new file mode 100644 index 000000000000..99eedbfa490f --- /dev/null +++ b/include/os/macos/spl/sys/rwlock.h @@ -0,0 +1,82 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SPL_RWLOCK_H +#define _SPL_RWLOCK_H + +#include +#include + +typedef enum { + RW_DRIVER = 2, + RW_DEFAULT = 4 +} krw_type_t; + +typedef enum { + RW_NONE = 0, + RW_WRITER = 1, + RW_READER = 2 +} krw_t; + +#define RW_NOLOCKDEP 0 + +struct krwlock { + uint32_t rw_lock[4]; /* opaque lck_rw_t data */ + void *rw_owner; /* writer (exclusive) lock only */ + int rw_readers; /* reader lock only */ + int rw_pad; /* */ +#ifdef SPL_DEBUG_RWLOCK + void *leak; +#endif +}; +typedef struct krwlock krwlock_t; + +#define RW_WRITE_HELD(x) (rw_write_held((x))) +#define RW_LOCK_HELD(x) (rw_lock_held((x))) +#define RW_READ_HELD(x) (rw_read_held((x))) + +#ifdef SPL_DEBUG_RWLOCK +#define rw_init(A, B, C, D) \ + rw_initx(A, B, C, D, __FILE__, __FUNCTION__, __LINE__) +extern void rw_initx(krwlock_t *, char *, krw_type_t, void *, + const char *, const char *, int); +#else +extern void rw_init(krwlock_t *, char *, krw_type_t, void *); +#endif +extern void rw_destroy(krwlock_t *); +extern void rw_enter(krwlock_t *, krw_t); +extern int rw_tryenter(krwlock_t *, krw_t); +extern void rw_exit(krwlock_t *); +extern void rw_downgrade(krwlock_t *); +extern int rw_tryupgrade(krwlock_t *); +extern int rw_write_held(krwlock_t *); +extern int rw_read_held(krwlock_t *); +extern int rw_lock_held(krwlock_t *); +extern int rw_isinit(krwlock_t *); + +int spl_rwlock_init(void); +void spl_rwlock_fini(void); + +#endif /* _SPL_RWLOCK_H */ diff --git a/include/os/macos/spl/sys/seg_kmem.h b/include/os/macos/spl/sys/seg_kmem.h new file mode 100644 index 000000000000..5c30d8d54f7d --- /dev/null +++ b/include/os/macos/spl/sys/seg_kmem.h @@ -0,0 +1,86 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _VM_SEG_KMEM_H +#define _VM_SEG_KMEM_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + * VM - Kernel Segment Driver + */ + +#if defined(_KERNEL) + +extern uint64_t segkmem_total_allocated; + +/* qcaching for abd */ +extern vmem_t *abd_arena; + +/* + * segkmem page vnodes + */ +#define kvp (kvps[KV_KVP]) +#define zvp (kvps[KV_ZVP]) +#if defined(__sparc) +#define mpvp (kvps[KV_MPVP]) +#define promvp (kvps[KV_PROMVP]) +#endif /* __sparc */ + +void *segkmem_alloc(vmem_t *, size_t, int); +extern void segkmem_free(vmem_t *, void *, size_t); +extern void kernelheap_init(void); +extern void kernelheap_fini(void); + +extern void segkmem_abd_init(void); +extern void segkmem_abd_fini(void); + +/* + * Flags for segkmem_xalloc(). + * + * SEGKMEM_SHARELOCKED requests pages which are locked SE_SHARED to be + * returned rather than unlocked which is now the default. Note that + * memory returned by SEGKMEM_SHARELOCKED cannot be freed by segkmem_free(). + * This is a hack for seg_dev that should be cleaned up in the future. + */ +#define SEGKMEM_SHARELOCKED 0x20000 + +#define SEGKMEM_USE_LARGEPAGES (segkmem_lpsize > PAGESIZE) + +#define IS_KMEM_VA_LARGEPAGE(vaddr) \ + (((vaddr) >= heap_lp_base) && ((vaddr) < heap_lp_end)) + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _VM_SEG_KMEM_H */ diff --git a/include/os/macos/spl/sys/sha2.h b/include/os/macos/spl/sys/sha2.h new file mode 100644 index 000000000000..9039835f18ff --- /dev/null +++ b/include/os/macos/spl/sys/sha2.h @@ -0,0 +1,155 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* Copyright 2013 Saso Kiselkov. All rights reserved. */ + +#ifndef _SYS_SHA2_H +#define _SYS_SHA2_H + +#ifdef _KERNEL +#include /* for uint_* */ +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define SHA2_HMAC_MIN_KEY_LEN 1 /* SHA2-HMAC min key length in bytes */ +#define SHA2_HMAC_MAX_KEY_LEN INT_MAX /* SHA2-HMAC max key length in bytes */ + +#define SHA256_DIGEST_LENGTH 32 /* SHA256 digest length in bytes */ +#define SHA384_DIGEST_LENGTH 48 /* SHA384 digest length in bytes */ +#define SHA512_DIGEST_LENGTH 64 /* SHA512 digest length in bytes */ + +/* Truncated versions of SHA-512 according to FIPS-180-4, section 5.3.6 */ +#define SHA512_224_DIGEST_LENGTH 28 /* SHA512/224 digest length */ +#define SHA512_256_DIGEST_LENGTH 32 /* SHA512/256 digest length */ + +#define SHA256_HMAC_BLOCK_SIZE 64 /* SHA256-HMAC block size */ +#define SHA512_HMAC_BLOCK_SIZE 128 /* SHA512-HMAC block size */ + +#define SHA256 0 +#define SHA256_HMAC 1 +#define SHA256_HMAC_GEN 2 +#define SHA384 3 +#define SHA384_HMAC 4 +#define SHA384_HMAC_GEN 5 +#define SHA512 6 +#define SHA512_HMAC 7 +#define SHA512_HMAC_GEN 8 +#define SHA512_224 9 +#define SHA512_256 10 + +/* + * SHA2 context. + * The contents of this structure are a private interface between the + * Init/Update/Final calls of the functions defined below. + * Callers must never attempt to read or write any of the fields + * in this structure directly. + */ +typedef struct { + uint32_t algotype; /* Algorithm Type */ + + /* state (ABCDEFGH) */ + union { + uint32_t s32[8]; /* for SHA256 */ + uint64_t s64[8]; /* for SHA384/512 */ + } state; + /* number of bits */ + union { + uint32_t c32[2]; /* for SHA256 , modulo 2^64 */ + uint64_t c64[2]; /* for SHA384/512, modulo 2^128 */ + } count; + union { + uint8_t buf8[128]; /* undigested input */ + uint32_t buf32[32]; /* realigned input */ + uint64_t buf64[16]; /* realigned input */ + } buf_un; +} SHA2_CTX; + +typedef SHA2_CTX SHA256_CTX; +typedef SHA2_CTX SHA384_CTX; +typedef SHA2_CTX SHA512_CTX; + +extern void SHA2Init(uint64_t mech, SHA2_CTX *); + +extern void SHA2Update(SHA2_CTX *, const void *, size_t); + +extern void SHA2Final(void *, SHA2_CTX *); + +extern void SHA256Init(SHA256_CTX *); + +extern void SHA256Update(SHA256_CTX *, const void *, size_t); + +extern void SHA256Final(void *, SHA256_CTX *); + +extern void SHA384Init(SHA384_CTX *); + +extern void SHA384Update(SHA384_CTX *, const void *, size_t); + +extern void SHA384Final(void *, SHA384_CTX *); + +extern void SHA512Init(SHA512_CTX *); + +extern void SHA512Update(SHA512_CTX *, const void *, size_t); + +extern void SHA512Final(void *, SHA512_CTX *); + +#ifdef _SHA2_IMPL +/* + * The following types/functions are all private to the implementation + * of the SHA2 functions and must not be used by consumers of the interface + */ + +/* + * List of support mechanisms in this module. + * + * It is important to note that in the module, division or modulus calculations + * are used on the enumerated type to determine which mechanism is being used; + * therefore, changing the order or additional mechanisms should be done + * carefully + */ +typedef enum sha2_mech_type { + SHA256_MECH_INFO_TYPE, /* SUN_CKM_SHA256 */ + SHA256_HMAC_MECH_INFO_TYPE, /* SUN_CKM_SHA256_HMAC */ + SHA256_HMAC_GEN_MECH_INFO_TYPE, /* SUN_CKM_SHA256_HMAC_GENERAL */ + SHA384_MECH_INFO_TYPE, /* SUN_CKM_SHA384 */ + SHA384_HMAC_MECH_INFO_TYPE, /* SUN_CKM_SHA384_HMAC */ + SHA384_HMAC_GEN_MECH_INFO_TYPE, /* SUN_CKM_SHA384_HMAC_GENERAL */ + SHA512_MECH_INFO_TYPE, /* SUN_CKM_SHA512 */ + SHA512_HMAC_MECH_INFO_TYPE, /* SUN_CKM_SHA512_HMAC */ + SHA512_HMAC_GEN_MECH_INFO_TYPE, /* SUN_CKM_SHA512_HMAC_GENERAL */ + SHA512_224_MECH_INFO_TYPE, /* SUN_CKM_SHA512_224 */ + SHA512_256_MECH_INFO_TYPE /* SUN_CKM_SHA512_256 */ +} sha2_mech_type_t; + +#endif /* _SHA2_IMPL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_SHA2_H */ diff --git a/include/os/macos/spl/sys/sid.h b/include/os/macos/spl/sys/sid.h new file mode 100644 index 000000000000..ac8c58b88593 --- /dev/null +++ b/include/os/macos/spl/sys/sid.h @@ -0,0 +1,104 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#ifndef _SPL_SID_H +#define _SPL_SID_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define crgetzoneid(x) (GLOBAL_ZONEID) + +typedef struct ksiddomain { + char *kd_name; +} ksiddomain_t; + +typedef enum ksid_index { + KSID_USER, + KSID_GROUP, + KSID_OWNER, + KSID_COUNT +} ksid_index_t; + +typedef int ksid_t; + +/* Should be in kidmap.h */ +typedef int32_t idmap_stat; + +static inline ksiddomain_t * +ksid_lookupdomain(const char *dom) +{ + ksiddomain_t *kd; + int len = strlen(dom); + + kd = (ksiddomain_t *)kmem_zalloc(sizeof (ksiddomain_t), KM_SLEEP); + kd->kd_name = (char *)kmem_zalloc(len + 1, KM_SLEEP); + memcpy(kd->kd_name, dom, len); + + return (kd); +} + +static inline void +ksiddomain_rele(ksiddomain_t *ksid) +{ + kmem_free(ksid->kd_name, strlen(ksid->kd_name) + 1); + kmem_free(ksid, sizeof (ksiddomain_t)); +} + +#define UID_NOBODY 65534 +#define GID_NOBODY 65534 + +static __inline uint_t +ksid_getid(ksid_t *ks) +{ + panic("%s has been unexpectedly called", __func__); + return (0); +} + +static __inline const char * +ksid_getdomain(ksid_t *ks) +{ + panic("%s has been unexpectedly called", __func__); + return (0); +} + +static __inline uint_t +ksid_getrid(ksid_t *ks) +{ + panic("%s has been unexpectedly called", __func__); + return (0); +} + +#define kidmap_getsidbyuid(zone, uid, sid_prefix, rid) (1) +#define kidmap_getsidbygid(zone, gid, sid_prefix, rid) (1) + +#ifdef __cplusplus +} +#endif + +#endif /* _SPL_SID_H */ diff --git a/include/os/macos/spl/sys/signal.h b/include/os/macos/spl/sys/signal.h new file mode 100644 index 000000000000..f4bf1845e92d --- /dev/null +++ b/include/os/macos/spl/sys/signal.h @@ -0,0 +1,59 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2020 Jorgen Lundman + * + */ + +#ifndef _SPL_SYS_SIGNAL_H +#define _SPL_SYS_SIGNAL_H + +#include +#include_next +#include + +#define FORREAL 0 /* Usual side-effects */ +#define JUSTLOOKING 1 /* Don't stop the process */ + +struct proc; + +extern int thread_issignal(struct proc *, thread_t, sigset_t); + +#define THREADMASK (sigmask(SIGILL)|sigmask(SIGTRAP)|\ + sigmask(SIGIOT)|sigmask(SIGEMT)|\ + sigmask(SIGFPE)|sigmask(SIGBUS)|\ + sigmask(SIGSEGV)|sigmask(SIGSYS)|\ + sigmask(SIGPIPE)|sigmask(SIGKILL)|\ + sigmask(SIGTERM)|sigmask(SIGINT)) + +static __inline__ int +issig(int why) +{ + return (thread_issignal(current_proc(), current_thread(), + THREADMASK)); +} + +/* Always called with curthread */ +#define signal_pending(p) issig(0) + +#endif /* SPL_SYS_SIGNAL_H */ diff --git a/include/os/macos/spl/sys/simd.h b/include/os/macos/spl/sys/simd.h new file mode 100644 index 000000000000..44a142505b38 --- /dev/null +++ b/include/os/macos/spl/sys/simd.h @@ -0,0 +1,736 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (C) 2016 Gvozden Neskovic . + */ + +/* + * USER API: + * + * Kernel fpu methods: + * kfpu_begin() + * kfpu_end() + * + * SIMD support: + * + * Following functions should be called to determine whether CPU feature + * is supported. All functions are usable in kernel and user space. + * If a SIMD algorithm is using more than one instruction set + * all relevant feature test functions should be called. + * + * Supported features: + * zfs_sse_available() + * zfs_sse2_available() + * zfs_sse3_available() + * zfs_ssse3_available() + * zfs_sse4_1_available() + * zfs_sse4_2_available() + * + * zfs_avx_available() + * zfs_avx2_available() + * + * zfs_bmi1_available() + * zfs_bmi2_available() + * + * zfs_avx512f_available() + * zfs_avx512cd_available() + * zfs_avx512er_available() + * zfs_avx512pf_available() + * zfs_avx512bw_available() + * zfs_avx512dq_available() + * zfs_avx512vl_available() + * zfs_avx512ifma_available() + * zfs_avx512vbmi_available() + * + * NOTE(AVX-512VL): If using AVX-512 instructions with 128Bit registers + * also add zfs_avx512vl_available() to feature check. + */ + +#ifndef _SIMD_X86_H +#define _SIMD_X86_H + +#include + +#define kfpu_init() (0) +#define kfpu_fini() do {} while (0) + +#define kfpu_begin() ((void)0) +#define kfpu_end() ((void)0) +#define kfpu_allowed() 1 + + +/* only for __x86 */ +#if defined(__x86) + +#include + +#if defined(_KERNEL) +#include +#include + +#ifdef __APPLE__ +// XNU fpu.h +static inline uint64_t +xgetbv(uint32_t c) +{ + uint32_t mask_hi, mask_lo; + __asm__ __volatile__("xgetbv" : "=a"(mask_lo), "=d"(mask_hi) : "c" (c)); + return (((uint64_t)mask_hi<<32) + (uint64_t)mask_lo); +} + +#endif + +extern uint64_t spl_cpuid_features(void); +extern uint64_t spl_cpuid_leaf7_features(void); + +#define ZFS_ASM_BUG() { ASSERT(0); } break + +#endif + +/* + * CPUID feature tests for user-space. Linux kernel provides an interface for + * CPU feature testing. + */ +#if !defined(_KERNEL) + +#include + +#define ZFS_ASM_BUG() { assert(0); } break + +/* + * x86 registers used implicitly by CPUID + */ +typedef enum cpuid_regs { + EAX = 0, + EBX, + ECX, + EDX, + CPUID_REG_CNT = 4 +} cpuid_regs_t; + +/* + * List of instruction sets identified by CPUID + */ +typedef enum cpuid_inst_sets { + SSE = 0, + SSE2, + SSE3, + SSSE3, + SSE4_1, + SSE4_2, + OSXSAVE, + AVX, + AVX2, + BMI1, + BMI2, + AVX512F, + AVX512CD, + AVX512DQ, + AVX512BW, + AVX512IFMA, + AVX512VBMI, + AVX512PF, + AVX512ER, + AVX512VL, + AES, + PCLMULQDQ +} cpuid_inst_sets_t; + +/* + * Instruction set descriptor. + */ +typedef struct cpuid_feature_desc { + uint32_t leaf; /* CPUID leaf */ + uint32_t subleaf; /* CPUID sub-leaf */ + uint32_t flag; /* bit mask of the feature */ + cpuid_regs_t reg; /* which CPUID return register to test */ +} cpuid_feature_desc_t; + +#define _AVX512F_BIT (1U << 16) +#define _AVX512CD_BIT (_AVX512F_BIT | (1U << 28)) +#define _AVX512DQ_BIT (_AVX512F_BIT | (1U << 17)) +#define _AVX512BW_BIT (_AVX512F_BIT | (1U << 30)) +#define _AVX512IFMA_BIT (_AVX512F_BIT | (1U << 21)) +#define _AVX512VBMI_BIT (1U << 1) /* AVX512F_BIT is on another leaf */ +#define _AVX512PF_BIT (_AVX512F_BIT | (1U << 26)) +#define _AVX512ER_BIT (_AVX512F_BIT | (1U << 27)) +#define _AVX512VL_BIT (1U << 31) /* if used also check other levels */ +#define _AES_BIT (1U << 25) +#define _PCLMULQDQ_BIT (1U << 1) + +/* + * Descriptions of supported instruction sets + */ +static const cpuid_feature_desc_t spl_cpuid_features[] = { + [SSE] = {1U, 0U, 1U << 25, EDX }, + [SSE2] = {1U, 0U, 1U << 26, EDX }, + [SSE3] = {1U, 0U, 1U << 0, ECX }, + [SSSE3] = {1U, 0U, 1U << 9, ECX }, + [SSE4_1] = {1U, 0U, 1U << 19, ECX }, + [SSE4_2] = {1U, 0U, 1U << 20, ECX }, + [OSXSAVE] = {1U, 0U, 1U << 27, ECX }, + [AVX] = {1U, 0U, 1U << 28, ECX }, + [AVX2] = {7U, 0U, 1U << 5, EBX }, + [BMI1] = {7U, 0U, 1U << 3, EBX }, + [BMI2] = {7U, 0U, 1U << 8, EBX }, + [AVX512F] = {7U, 0U, _AVX512F_BIT, EBX }, + [AVX512CD] = {7U, 0U, _AVX512CD_BIT, EBX }, + [AVX512DQ] = {7U, 0U, _AVX512DQ_BIT, EBX }, + [AVX512BW] = {7U, 0U, _AVX512BW_BIT, EBX }, + [AVX512IFMA] = {7U, 0U, _AVX512IFMA_BIT, EBX }, + [AVX512VBMI] = {7U, 0U, _AVX512VBMI_BIT, ECX }, + [AVX512PF] = {7U, 0U, _AVX512PF_BIT, EBX }, + [AVX512ER] = {7U, 0U, _AVX512ER_BIT, EBX }, + [AVX512VL] = {7U, 0U, _AVX512ER_BIT, EBX }, + [AES] = {1U, 0U, _AES_BIT, ECX }, + [PCLMULQDQ] = {1U, 0U, _PCLMULQDQ_BIT, ECX }, +}; + +/* + * Check if OS supports AVX and AVX2 by checking XCR0 + * Only call this function if CPUID indicates that AVX feature is + * supported by the CPU, otherwise it might be an illegal instruction. + */ +static inline uint64_t +xgetbv(uint32_t index) +{ + uint32_t eax, edx; + /* xgetbv - instruction byte code */ + __asm__ __volatile__(".byte 0x0f; .byte 0x01; .byte 0xd0" + : "=a" (eax), "=d" (edx) + : "c" (index)); + + return ((((uint64_t)edx)<<32) | (uint64_t)eax); +} + +/* + * Check if CPU supports a feature + */ +static inline boolean_t +__cpuid_check_feature(const cpuid_feature_desc_t *desc) +{ + uint32_t r[CPUID_REG_CNT]; + + if (__get_cpuid_max(0, NULL) >= desc->leaf) { + /* + * __cpuid_count is needed to properly check + * for AVX2. It is a macro, so return parameters + * are passed by value. + */ + __cpuid_count(desc->leaf, desc->subleaf, + r[EAX], r[EBX], r[ECX], r[EDX]); + return ((r[desc->reg] & desc->flag) == desc->flag); + } + return (B_FALSE); +} + +#define CPUID_FEATURE_CHECK(name, id) \ +static inline boolean_t \ +__cpuid_has_ ## name(void) \ +{ \ + return (__cpuid_check_feature(&spl_cpuid_features[id])); \ +} + +/* + * Define functions for user-space CPUID features testing + */ +CPUID_FEATURE_CHECK(sse, SSE); +CPUID_FEATURE_CHECK(sse2, SSE2); +CPUID_FEATURE_CHECK(sse3, SSE3); +CPUID_FEATURE_CHECK(ssse3, SSSE3); +CPUID_FEATURE_CHECK(sse4_1, SSE4_1); +CPUID_FEATURE_CHECK(sse4_2, SSE4_2); +CPUID_FEATURE_CHECK(avx, AVX); +CPUID_FEATURE_CHECK(avx2, AVX2); +CPUID_FEATURE_CHECK(osxsave, OSXSAVE); +CPUID_FEATURE_CHECK(bmi1, BMI1); +CPUID_FEATURE_CHECK(bmi2, BMI2); +CPUID_FEATURE_CHECK(avx512f, AVX512F); +CPUID_FEATURE_CHECK(avx512cd, AVX512CD); +CPUID_FEATURE_CHECK(avx512dq, AVX512DQ); +CPUID_FEATURE_CHECK(avx512bw, AVX512BW); +CPUID_FEATURE_CHECK(avx512ifma, AVX512IFMA); +CPUID_FEATURE_CHECK(avx512vbmi, AVX512VBMI); +CPUID_FEATURE_CHECK(avx512pf, AVX512PF); +CPUID_FEATURE_CHECK(avx512er, AVX512ER); +CPUID_FEATURE_CHECK(avx512vl, AVX512VL); +CPUID_FEATURE_CHECK(aes, AES); +CPUID_FEATURE_CHECK(pclmulqdq, PCLMULQDQ); + +#endif /* !defined(_KERNEL) */ + + +/* + * Detect register set support + */ +static inline boolean_t +__simd_state_enabled(const uint64_t state) +{ + boolean_t has_osxsave; + uint64_t xcr0; + +#if defined(_KERNEL) + has_osxsave = !!(spl_cpuid_features() & CPUID_FEATURE_OSXSAVE); +#elif !defined(_KERNEL) + has_osxsave = __cpuid_has_osxsave(); +#endif + if (!has_osxsave) + return (B_FALSE); + + xcr0 = xgetbv(0); + return ((xcr0 & state) == state); +} + +#define _XSTATE_SSE_AVX (0x2 | 0x4) +#define _XSTATE_AVX512 (0xE0 | _XSTATE_SSE_AVX) + +#define __ymm_enabled() __simd_state_enabled(_XSTATE_SSE_AVX) +#define __zmm_enabled() __simd_state_enabled(_XSTATE_AVX512) + + +/* + * Check if SSE instruction set is available + */ +static inline boolean_t +zfs_sse_available(void) +{ +#if defined(_KERNEL) + return (!!(spl_cpuid_features() & CPUID_FEATURE_SSE)); +#elif !defined(_KERNEL) + return (__cpuid_has_sse()); +#endif +} + +/* + * Check if SSE2 instruction set is available + */ +static inline boolean_t +zfs_sse2_available(void) +{ +#if defined(_KERNEL) + return (!!(spl_cpuid_features() & CPUID_FEATURE_SSE2)); +#elif !defined(_KERNEL) + return (__cpuid_has_sse2()); +#endif +} + +/* + * Check if SSE3 instruction set is available + */ +static inline boolean_t +zfs_sse3_available(void) +{ +#if defined(_KERNEL) + return (!!(spl_cpuid_features() & CPUID_FEATURE_SSE3)); +#elif !defined(_KERNEL) + return (__cpuid_has_sse3()); +#endif +} + +/* + * Check if SSSE3 instruction set is available + */ +static inline boolean_t +zfs_ssse3_available(void) +{ +#if defined(_KERNEL) + return (!!(spl_cpuid_features() & CPUID_FEATURE_SSSE3)); +#elif !defined(_KERNEL) + return (__cpuid_has_ssse3()); +#endif +} + +/* + * Check if SSE4.1 instruction set is available + */ +static inline boolean_t +zfs_sse4_1_available(void) +{ +#if defined(_KERNEL) + return (!!(spl_cpuid_features() & CPUID_FEATURE_SSE4_1)); +#elif !defined(_KERNEL) + return (__cpuid_has_sse4_1()); +#endif +} + +/* + * Check if SSE4.2 instruction set is available + */ +static inline boolean_t +zfs_sse4_2_available(void) +{ +#if defined(_KERNEL) + return (!!(spl_cpuid_features() & CPUID_FEATURE_SSE4_2)); +#elif !defined(_KERNEL) + return (__cpuid_has_sse4_2()); +#endif +} + +/* + * Check if AVX instruction set is available + */ +static inline boolean_t +zfs_avx_available(void) +{ + boolean_t has_avx; + return (FALSE); // Currently broken on macOS +#if defined(_KERNEL) + return (!!(spl_cpuid_features() & CPUID_FEATURE_AVX1_0)); +#elif !defined(_KERNEL) + has_avx = __cpuid_has_avx(); +#endif + + return (has_avx && __ymm_enabled()); +} + +/* + * Check if AVX2 instruction set is available + */ +static inline boolean_t +zfs_avx2_available(void) +{ + boolean_t has_avx2; + return (FALSE); // Currently broken on macOS +#if defined(_KERNEL) +#if defined(HAVE_AVX2) + has_avx2 = (!!(spl_cpuid_leaf7_features() & CPUID_LEAF7_FEATURE_AVX2)); +#else + has_avx2 = B_FALSE; +#endif +#elif !defined(_KERNEL) + has_avx2 = __cpuid_has_avx2(); +#endif + + return (has_avx2 && __ymm_enabled()); +} + +/* + * Check if BMI1 instruction set is available + */ +static inline boolean_t +zfs_bmi1_available(void) +{ +#if defined(_KERNEL) +#if defined(CPUID_LEAF7_FEATURE_BMI1) + return (!!(spl_cpuid_leaf7_features() & CPUID_LEAF7_FEATURE_BMI1)); +#else + return (B_FALSE); +#endif +#elif !defined(_KERNEL) + return (__cpuid_has_bmi1()); +#endif +} + +/* + * Check if BMI2 instruction set is available + */ +static inline boolean_t +zfs_bmi2_available(void) +{ +#if defined(_KERNEL) +#if defined(CPUID_LEAF7_FEATURE_BMI2) + return (!!(spl_cpuid_leaf7_features() & CPUID_LEAF7_FEATURE_BMI2)); +#else + return (B_FALSE); +#endif +#elif !defined(_KERNEL) + return (__cpuid_has_bmi2()); +#endif +} + +/* + * Check if AES instruction set is available + */ +static inline boolean_t +zfs_aes_available(void) +{ +#if defined(_KERNEL) +#if defined(HAVE_AES) + return (!!(spl_cpuid_features() & CPUID_FEATURE_AES)); +#else + return (B_FALSE); +#endif +#elif !defined(_KERNEL) + return (__cpuid_has_aes()); +#endif +} + +/* + * Check if PCLMULQDQ instruction set is available + */ +static inline boolean_t +zfs_pclmulqdq_available(void) +{ +#if defined(_KERNEL) +#if defined(HAVE_PCLMULQDQ) + return (!!(spl_cpuid_features() & CPUID_FEATURE_PCLMULQDQ)); +#else + return (B_FALSE); +#endif +#elif !defined(_KERNEL) + return (__cpuid_has_pclmulqdq()); +#endif +} + +/* + * Check if MOVBE instruction is available + */ +static inline boolean_t +zfs_movbe_available(void) +{ +#if defined(X86_FEATURE_MOVBE) + return (!!(spl_cpuid_features() & CPUID_FEATURE_MOVBE)); +#else + return (B_FALSE); +#endif +} + +/* + * AVX-512 family of instruction sets: + * + * AVX512F Foundation + * AVX512CD Conflict Detection Instructions + * AVX512ER Exponential and Reciprocal Instructions + * AVX512PF Prefetch Instructions + * + * AVX512BW Byte and Word Instructions + * AVX512DQ Double-word and Quadword Instructions + * AVX512VL Vector Length Extensions + * + * AVX512IFMA Integer Fused Multiply Add (Not supported by kernel 4.4) + * AVX512VBMI Vector Byte Manipulation Instructions + */ + + +/* Check if AVX512F instruction set is available */ +static inline boolean_t +zfs_avx512f_available(void) +{ + boolean_t has_avx512 = B_FALSE; + + return (FALSE); // Currently broken on macOS +#if defined(_KERNEL) +#if defined(HAVE_AVX512F) && defined(CPUID_LEAF7_FEATURE_AVX512F) + return (!!(spl_cpuid_leaf7_features() & CPUID_LEAF7_FEATURE_AVX512F)); +#else + has_avx512 = B_FALSE; +#endif +#elif !defined(_KERNEL) + has_avx512 = __cpuid_has_avx512f(); +#endif + + return (has_avx512 && __zmm_enabled()); +} + +/* Check if AVX512CD instruction set is available */ +static inline boolean_t +zfs_avx512cd_available(void) +{ + boolean_t has_avx512 = B_FALSE; + + return (FALSE); // Currently broken on macOS +#if defined(_KERNEL) +#if defined(HAVE_AVX512F) && defined(HAVE_AVX512CD) && \ + defined(CPUID_LEAF7_FEATURE_AVX512F) && \ + defined(CPUID_LEAF7_FEATURE_AVX512CD) + has_avx512 = (spl_cpuid_leaf7_features() & + (CPUID_LEAF7_FEATURE_AVX512F | CPUID_LEAF7_FEATURE_AVX512CD)) == + (CPUID_LEAF7_FEATURE_AVX512F | CPUID_LEAF7_FEATURE_AVX512CD); +#else + has_avx512 = B_FALSE; +#endif +#elif !defined(_KERNEL) + has_avx512 = __cpuid_has_avx512cd(); +#endif + + return (has_avx512 && __zmm_enabled()); +} + +/* Check if AVX512ER instruction set is available */ +static inline boolean_t +zfs_avx512er_available(void) +{ + boolean_t has_avx512 = B_FALSE; + + return (FALSE); // Currently broken on macOS +#if defined(_KERNEL) +#if defined(HAVE_AVX512F) && defined(HAVE_AVX512ER) && \ + defined(CPUID_LEAF7_FEATURE_AVX512ER) + has_avx512 = (spl_cpuid_leaf7_features() & + (CPUID_LEAF7_FEATURE_AVX512F | CPUID_LEAF7_FEATURE_AVX512ER)) == + (CPUID_LEAF7_FEATURE_AVX512F | CPUID_LEAF7_FEATURE_AVX512ER); +#else + has_avx512 = B_FALSE; +#endif +#elif !defined(_KERNEL) + has_avx512 = __cpuid_has_avx512er(); +#endif + + return (has_avx512 && __zmm_enabled()); +} + +/* Check if AVX512PF instruction set is available */ +static inline boolean_t +zfs_avx512pf_available(void) +{ + boolean_t has_avx512 = B_FALSE; + + return (FALSE); // Currently broken on macOS +#if defined(_KERNEL) +#if defined(HAVE_AVX512PF) && defined(HAVE_AVX512F) && \ + defined(CPUID_LEAF7_FEATURE_AVX512PF) + has_avx512 = (spl_cpuid_leaf7_features() & + (CPUID_LEAF7_FEATURE_AVX512F | CPUID_LEAF7_FEATURE_AVX512PF)) == + (CPUID_LEAF7_FEATURE_AVX512F | CPUID_LEAF7_FEATURE_AVX512PF); +#else + has_avx512 = B_FALSE; +#endif +#elif !defined(_KERNEL) + has_avx512 = __cpuid_has_avx512pf(); +#endif + + return (has_avx512 && __zmm_enabled()); +} + +/* Check if AVX512BW instruction set is available */ +static inline boolean_t +zfs_avx512bw_available(void) +{ + boolean_t has_avx512 = B_FALSE; + + return (FALSE); // Currently broken on macOS +#if defined(_KERNEL) +#if defined(HAVE_AVX512BW) && defined(HAVE_AVX512F) && \ + defined(CPUID_LEAF7_FEATURE_AVX512F) && \ + defined(CPUID_LEAF7_FEATURE_AVX512BW) + has_avx512 = (spl_cpuid_leaf7_features() & + (CPUID_LEAF7_FEATURE_AVX512F | CPUID_LEAF7_FEATURE_AVX512BW)) == + (CPUID_LEAF7_FEATURE_AVX512F | CPUID_LEAF7_FEATURE_AVX512BW); +#else + has_avx512 = B_FALSE; +#endif +#elif !defined(_KERNEL) + has_avx512 = __cpuid_has_avx512bw(); +#endif + + return (has_avx512 && __zmm_enabled()); +} + +/* Check if AVX512DQ instruction set is available */ +static inline boolean_t +zfs_avx512dq_available(void) +{ + boolean_t has_avx512 = B_FALSE; + + return (FALSE); // Currently broken on macOS +#if defined(_KERNEL) +#if defined(HAVE_AVX512DQ) && defined(HAVE_AVX512F) && \ + defined(CPUID_LEAF7_FEATURE_AVX512F) && \ + defined(CPUID_LEAF7_FEATURE_AVX512DQ) + has_avx512 = (spl_cpuid_leaf7_features() & + (CPUID_LEAF7_FEATURE_AVX512F|CPUID_LEAF7_FEATURE_AVX512DQ)) == + (CPUID_LEAF7_FEATURE_AVX512F|CPUID_LEAF7_FEATURE_AVX512DQ); +#else + has_avx512 = B_FALSE; +#endif +#elif !defined(_KERNEL) + has_avx512 = __cpuid_has_avx512dq(); +#endif + + return (has_avx512 && __zmm_enabled()); +} + +/* Check if AVX512VL instruction set is available */ +static inline boolean_t +zfs_avx512vl_available(void) +{ + boolean_t has_avx512 = B_FALSE; + + return (FALSE); // Currently broken on macOS +#if defined(_KERNEL) +#if defined(HAVE_AVX512VL) && defined(HAVE_AVX512F) && \ + defined(CPUID_LEAF7_FEATURE_AVX512F) && \ + defined(CPUID_LEAF7_FEATURE_AVX512VL) + has_avx512 = (spl_cpuid_leaf7_features() & + (CPUID_LEAF7_FEATURE_AVX512F|CPUID_LEAF7_FEATURE_AVX512VL)) == + (CPUID_LEAF7_FEATURE_AVX512F|CPUID_LEAF7_FEATURE_AVX512VL); +#else + has_avx512 = B_FALSE; +#endif +#elif !defined(_KERNEL) + has_avx512 = __cpuid_has_avx512vl(); +#endif + + return (has_avx512 && __zmm_enabled()); +} + +/* Check if AVX512IFMA instruction set is available */ +static inline boolean_t +zfs_avx512ifma_available(void) +{ + boolean_t has_avx512 = B_FALSE; + + return (FALSE); // Currently broken on macOS +#if defined(_KERNEL) +#if defined(HAVE_AVX512IFMA) && defined(HAVE_AVX512F) && \ + defined(CPUID_LEAF7_FEATURE_AVX512F) && \ + defined(CPUID_LEAF7_FEATURE_AVX512IFMA) + has_avx512 = (spl_cpuid_leaf7_features() & + (CPUID_LEAF7_FEATURE_AVX512F|CPUID_LEAF7_FEATURE_AVX512IFMA)) == + (CPUID_LEAF7_FEATURE_AVX512F|CPUID_LEAF7_FEATURE_AVX512IFMA); +#else + has_avx512 = B_FALSE; +#endif +#elif !defined(_KERNEL) + has_avx512 = __cpuid_has_avx512ifma(); +#endif + + return (has_avx512 && __zmm_enabled()); +} + +/* Check if AVX512VBMI instruction set is available */ +static inline boolean_t +zfs_avx512vbmi_available(void) +{ + boolean_t has_avx512 = B_FALSE; + + return (FALSE); // Currently broken on macOS +#if defined(_KERNEL) +#if defined(HAVE_AVX512VBMI) && defined(HAVE_AVX512F) && \ + defined(CPUID_LEAF7_FEATURE_AVX512F) && \ + defined(CPUID_LEAF7_FEATURE_AVX512VBMI) + has_avx512 = (spl_cpuid_leaf7_features() & + (CPUID_LEAF7_FEATURE_AVX512F|CPUID_LEAF7_FEATURE_AVX512VBMI)) == + (CPUID_LEAF7_FEATURE_AVX512F|CPUID_LEAF7_FEATURE_AVX512VBMI); +#else + has_avx512 = B_FALSE; +#endif +#elif !defined(_KERNEL) + has_avx512 = __cpuid_has_avx512f() && + __cpuid_has_avx512vbmi(); +#endif + + return (has_avx512 && __zmm_enabled()); +} + +#endif /* defined(__x86) */ + +#endif /* _SIMD_X86_H */ diff --git a/include/os/macos/spl/sys/strings.h b/include/os/macos/spl/sys/strings.h new file mode 100644 index 000000000000..ce5260910d27 --- /dev/null +++ b/include/os/macos/spl/sys/strings.h @@ -0,0 +1,27 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _SPL_STRINGS_H +#define _SPL_STRINGS_H + +int kstrtoul(const char *s, unsigned int base, unsigned long *res); + +#endif diff --git a/include/os/macos/spl/sys/stropts.h b/include/os/macos/spl/sys/stropts.h new file mode 100644 index 000000000000..bdce60f32751 --- /dev/null +++ b/include/os/macos/spl/sys/stropts.h @@ -0,0 +1,247 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * + */ + + +#ifndef _SPL_STROPTS_H +#define _SPL_STROPTS_H + +#define LOCORE +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define isprint(c) ((c) >= ' ' && (c) <= '~') + +/* + * Find highest one bit set. + * Returns bit number + 1 of highest bit that is set, otherwise returns 0. + * High order bit is 31 (or 63 in _LP64 kernel). + */ +static inline int +highbit64(unsigned long long i) +{ + register int h = 1; + if (i == 0) + return (0); + if (i & 0xffffffff00000000ull) { + h += 32; i >>= 32; + } + if (i & 0xffff0000) { + h += 16; i >>= 16; + } + if (i & 0xff00) { + h += 8; i >>= 8; + } + if (i & 0xf0) { + h += 4; i >>= 4; + } + if (i & 0xc) { + h += 2; i >>= 2; + } + if (i & 0x2) { + h += 1; + } + return (h); +} + +static inline int +highbit(unsigned long long i) +{ + register int h = 1; + if (i == 0) + return (0); + if (i & 0xffffffff00000000ull) { + h += 32; i >>= 32; + } + if (i & 0xffff0000) { + h += 16; i >>= 16; + } + if (i & 0xff00) { + h += 8; i >>= 8; + } + if (i & 0xf0) { + h += 4; i >>= 4; + } + if (i & 0xc) { + h += 2; i >>= 2; + } + if (i & 0x2) { + h += 1; + } + return (h); +} + +/* + * Find lowest one bit set. + * Returns bit number + 1 of lowest bit that is set, otherwise returns 0. + * Low order bit is 0. + */ +static inline int +lowbit(unsigned long long i) +{ + register int h = 1; + + if (i == 0) + return (0); + + if (!(i & 0xffffffff)) { + h += 32; i >>= 32; + } + if (!(i & 0xffff)) { + h += 16; i >>= 16; + } + if (!(i & 0xff)) { + h += 8; i >>= 8; + } + if (!(i & 0xf)) { + h += 4; i >>= 4; + } + if (!(i & 0x3)) { + h += 2; i >>= 2; + } + if (!(i & 0x1)) { + h += 1; + } + return (h); +} + +static inline int +isdigit(char c) +{ + return (c >= ' ' && c <= '9'); +} + + +static inline char * +strpbrk(const char *s, const char *b) +{ + const char *p; + do { + for (p = b; *p != '\0' && *p != *s; ++p) + ; + if (*p != '\0') + return ((char *)s); + } while (*s++); + return (NULL); +} + + +static inline char * +strrchr(const char *p, int ch) +{ + union { + const char *cp; + char *p; + } u; + char *save; + + u.cp = p; + for (save = NULL; /* empty */; ++u.p) { + if (*u.p == ch) + save = u.p; + if (*u.p == '\0') + return (save); + } + /* NOTREACHED */ +} + +static inline int +is_ascii_str(const char *str) +{ + unsigned char ch; + + while ((ch = (unsigned char)*str++) != '\0') { + if (ch >= 0x80) + return (0); + } + return (1); +} + + +static inline void * +kmemchr(const void *s, int c, size_t n) +{ + if (n != 0) { + const unsigned char *p = (const unsigned char *)s; + do { + if (*p++ == (unsigned char)c) + return ((void *)(uintptr_t)(p - 1)); + } while (--n != 0); + } + return (NULL); +} + +#ifndef memchr +#define memchr kmemchr +#endif + +#define IDX(c) ((unsigned char)(c) / LONG_BIT) +#define BIT(c) ((unsigned long)1 << ((unsigned char)(c) % LONG_BIT)) + +static inline size_t +strcspn(const char *__restrict s, const char *__restrict charset) +{ + /* + * NB: idx and bit are temporaries whose use causes gcc 3.4.2 to + * generate better code. Without them, gcc gets a little confused. + */ + const char *s1; + unsigned long bit; + unsigned long tbl[(UCHAR_MAX + 1) / LONG_BIT]; + int idx; + + if (*s == '\0') + return (0); + + tbl[0] = 1; + tbl[3] = tbl[2] = tbl[1] = 0; + + for (; *charset != '\0'; charset++) { + idx = IDX(*charset); + bit = BIT(*charset); + tbl[idx] |= bit; + } + + for (s1 = s; ; s1++) { + idx = IDX(*s1); + bit = BIT(*s1); + if ((tbl[idx] & bit) != 0) + break; + } + return (s1 - s); +} + +#ifdef __cplusplus +} +#endif + +#endif /* SPL_STROPTS_H */ diff --git a/include/os/macos/spl/sys/sunddi.h b/include/os/macos/spl/sys/sunddi.h new file mode 100644 index 000000000000..04d07d0385a9 --- /dev/null +++ b/include/os/macos/spl/sys/sunddi.h @@ -0,0 +1,204 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Garrett D'Amore . All rights reserved. + * Copyright (c) 2012 by Delphix. All rights reserved. + */ + + + +#ifndef _SPL_SUNDDI_H +#define _SPL_SUNDDI_H + +#include +#include +#include +#include +#include +#include +#include + +typedef int ddi_devid_t; + +#define DDI_DEV_T_NONE ((dev_t)-1) +#define DDI_DEV_T_ANY ((dev_t)-2) +#define DI_MAJOR_T_UNKNOWN ((major_t)0) + +#define DDI_PROP_DONTPASS 0x0001 +#define DDI_PROP_CANSLEEP 0x0002 + +#define DDI_SUCCESS 0 +#define DDI_FAILURE -1 + +#define ddi_prop_lookup_string(x1, x2, x3, x4, x5) (*x5 = NULL) +#define ddi_prop_free(x) (void)0 +#define ddi_root_node() (void)0 + +#define isalnum(ch) (isalpha(ch) || isdigit(ch)) +#define isalpha(ch) (isupper(ch) || islower(ch)) +#define isdigit(ch) ((ch) >= '0' && (ch) <= '9') +#define islower(ch) ((ch) >= 'a' && (ch) <= 'z') +#define isspace(ch) (((ch) == ' ') || ((ch) == '\r') || ((ch) == '\n') || \ + ((ch) == '\t') || ((ch) == '\f')) +#define isupper(ch) ((ch) >= 'A' && (ch) <= 'Z') +#define isxdigit(ch) (isdigit(ch) || ((ch) >= 'a' && (ch) <= 'f') || \ + ((ch) >= 'A' && (ch) <= 'F')) +#define tolower(C) (((C) >= 'A' && (C) <= 'Z') ? (C) - 'A' + 'a' : (C)) +#define toupper(C) (((C) >= 'a' && (C) <= 'z') ? (C) - 'a' + 'A': (C)) +#define isgraph(C) ((C) >= 0x21 && (C) <= 0x7E) +#define ispunct(C) (((C) >= 0x21 && (C) <= 0x2F) || \ + ((C) >= 0x3A && (C) <= 0x40) || \ + ((C) >= 0x5B && (C) <= 0x60) || \ + ((C) >= 0x7B && (C) <= 0x7E)) + +// Define proper Solaris API calls, and clean ZFS up to use +int ddi_copyin(const void *from, void *to, size_t len, int flags); +int ddi_copyout(const void *from, void *to, size_t len, int flags); +int ddi_copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done); + +static inline int +ddi_strtol(const char *str, char **nptr, int base, long *result) +{ + *result = strtol(str, nptr, base); + if (*result == 0) + return (EINVAL); + else if (*result == LONG_MIN || *result == LONG_MAX) + return (ERANGE); + return (0); +} + +static inline int +ddi_strtoul(const char *str, char **nptr, int base, unsigned long *result) +{ + *result = strtoul(str, nptr, base); + if (*result == 0) + return (EINVAL); + else if (*result == ULONG_MAX) + return (ERANGE); + return (0); +} + +static inline int +ddi_strtoull(const char *str, char **nptr, int base, + unsigned long long *result) +{ + *result = (unsigned long long)strtouq(str, nptr, base); + if (*result == 0) + return (EINVAL); + else if (*result == ULLONG_MAX) + return (ERANGE); + return (0); +} + +static inline int +ddi_strtoll(const char *str, char **nptr, int base, long long *result) +{ + *result = (unsigned long long)strtoq(str, nptr, base); + if (*result == 0) + return (EINVAL); + else if (*result == ULLONG_MAX) + return (ERANGE); + return (0); +} + +#ifndef OTYPCNT +#define OTYPCNT 5 +#define OTYP_BLK 0 +#define OTYP_MNT 1 +#define OTYP_CHR 2 +#define OTYP_SWP 3 +#define OTYP_LYR 4 +#endif + +#define P2END(x, align) (-(~(x) & -(align))) + +#define ddi_name_to_major(name) devsw_name2blk(name, NULL, 0) + +struct dev_info { + dev_t dev; // Major / Minor + void *devc; + void *devb; +}; +typedef struct dev_info dev_info_t; + + +int ddi_strtoul(const char *, char **, int, unsigned long *); +int ddi_strtol(const char *, char **, int, long *); +int ddi_soft_state_init(void **, size_t, size_t); +int ddi_soft_state_zalloc(void *, int); +void *ddi_get_soft_state(void *, int); +void ddi_soft_state_free(void *, int); +void ddi_soft_state_fini(void **); +int ddi_create_minor_node(dev_info_t *, char *, int, + minor_t, char *, int); +void ddi_remove_minor_node(dev_info_t *, char *); + +int ddi_driver_major(dev_info_t *); + +typedef void *ldi_ident_t; + +#define DDI_SUCCESS 0 +#define DDI_FAILURE -1 + +#define DDI_PSEUDO "" + +#define ddi_prop_update_int64(a, b, c, d) DDI_SUCCESS +#define ddi_prop_update_string(a, b, c, d) DDI_SUCCESS + +#define bioerror(bp, er) (buf_seterror((bp), (er))) +#define biodone(bp) buf_biodone(bp) + +#define ddi_ffs ffs +static inline long ddi_fls(long mask) { \ + /* Algorithm courtesy of Steve Chessin. */ \ + while (mask) { \ + long nx; \ + if ((nx = (mask & (mask - 1))) == 0) { \ + break; \ + } \ + mask = nx; \ + } \ + return (ffs(mask)); \ +} + +#define getminor(X) minor((X)) + + + +/* + * This data structure is entirely private to the soft state allocator. + */ +struct i_ddi_soft_state { + void **array; /* the array of pointers */ + kmutex_t lock; /* serialize access to this struct */ + size_t size; /* how many bytes per state struct */ + size_t n_items; /* how many structs herein */ + struct i_ddi_soft_state *next; /* 'dirty' elements */ +}; + +#define MIN_N_ITEMS 8 /* 8 void *'s == 32 bytes */ + +extern int strspn(const char *string, register char *charset); + + +#endif /* SPL_SUNDDI_H */ diff --git a/include/os/macos/spl/sys/sysmacros.h b/include/os/macos/spl/sys/sysmacros.h new file mode 100644 index 000000000000..75e97125fcf6 --- /dev/null +++ b/include/os/macos/spl/sys/sysmacros.h @@ -0,0 +1,270 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_SYSMACROS_H +#define _SPL_SYSMACROS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _KERNEL +#define _KERNEL __KERNEL__ +#endif + +#define FALSE 0 +#define TRUE 1 + +#if 0 +#define INT8_MAX (127) +#define INT8_MIN (-128) +#define UINT8_MAX (255) +#define UINT8_MIN (0) + +#define INT16_MAX (32767) +#define INT16_MIN (-32768) +#define UINT16_MAX (65535) +#define UINT16_MIN (0) + +#define INT32_MAX INT_MAX +#define INT32_MIN INT_MIN +#define UINT32_MAX UINT_MAX +#define UINT32_MIN UINT_MIN + +#define INT64_MAX LLONG_MAX +#define INT64_MIN LLONG_MIN +#define UINT64_MAX ULLONG_MAX +#define UINT64_MIN ULLONG_MIN + +#define NBBY 8 +#define MAXBSIZE 8192 +#endif + +#define MAXMSGLEN 256 +#define MAXNAMELEN 256 +#define MAXPATHLEN PATH_MAX +#define MAXOFFSET_T LLONG_MAX +#define DEV_BSIZE 512 +#define DEV_BSHIFT 9 /* log2(DEV_BSIZE) */ + +#define proc_pageout NULL +#define curproc (struct proc *)current_proc() + +#ifndef __arm64__ +extern int cpu_number(void); +#define CPU_SEQID (cpu_number()) +#define CPU_SEQID_UNSTABLE (cpu_number()) +#else +#define CPU_SEQID (0) // Find solution +#define CPU_SEQID_UNSTABLE (0) +#endif +#define is_system_labeled() 0 + +extern unsigned int max_ncpus; +#define boot_ncpus max_ncpus + +#ifndef RLIM64_INFINITY +#define RLIM64_INFINITY (~0ULL) +#endif + +/* + * 0..MAX_PRIO-1: Process priority + * 0..MAX_RT_PRIO-1: RT priority tasks + * MAX_RT_PRIO..MAX_PRIO-1: SCHED_NORMAL tasks + * + * Treat shim tasks as SCHED_NORMAL tasks + */ + +/* + * In OSX, the kernel thread priorities start at 81 and goes to + * 95 MAXPRI_KERNEL. BASEPRI_REALTIME starts from 96. Since + * swap priority is at 92. ZFS priorities should have a base below + * 81 in general. Xnu will dynamically adjust priorities of + * some taskq threads around maxclsyspri. + */ +#define minclsyspri 70 /* well below the render server and other graphics */ +#define defclsyspri 75 /* five below the xnu kernel services */ +#define maxclsyspri 80 /* 1 less than base, 2 less than networking */ + +/* + * Missing macros + */ +#define PAGESIZE PAGE_SIZE + +/* from Solaris sys/byteorder.h */ +#define BSWAP_8(x) ((x) & 0xff) +#define BSWAP_16(x) ((BSWAP_8(x) << 8) | BSWAP_8((x) >> 8)) +#define BSWAP_32(x) ((BSWAP_16(x) << 16) | BSWAP_16((x) >> 16)) +#define BSWAP_64(x) ((BSWAP_32(x) << 32) | BSWAP_32((x) >> 32)) + + +/* Dtrace probes do not exist in the linux kernel */ +#ifdef DTRACE_PROBE +#undef DTRACE_PROBE +#endif /* DTRACE_PROBE */ +#define DTRACE_PROBE(a) ((void)0) + +#ifdef DTRACE_PROBE1 +#undef DTRACE_PROBE1 +#endif /* DTRACE_PROBE1 */ +#define DTRACE_PROBE1(a, b, c) ((void)0) + +#ifdef DTRACE_PROBE2 +#undef DTRACE_PROBE2 +#endif /* DTRACE_PROBE2 */ +#define DTRACE_PROBE2(a, b, c, d, e) ((void)0) + +#ifdef DTRACE_PROBE3 +#undef DTRACE_PROBE3 +#endif /* DTRACE_PROBE3 */ +#define DTRACE_PROBE3(a, b, c, d, e, f, g) ((void)0) + +#ifdef DTRACE_PROBE4 +#undef DTRACE_PROBE4 +#endif /* DTRACE_PROBE4 */ +#define DTRACE_PROBE4(a, b, c, d, e, f, g, h, i) ((void)0) + +/* Missing globals */ +extern char spl_version[32]; +extern unsigned long spl_hostid; +extern char hw_serial[11]; + +/* Missing misc functions */ +extern uint32_t zone_get_hostid(void *zone); +extern void spl_setup(void); +extern void spl_cleanup(void); + +#define makedevice(maj, min) makedev(maj, min) + +/* common macros */ +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#endif +#ifndef ABS +#define ABS(a) ((a) < 0 ? -(a) : (a)) +#endif +#ifndef DIV_ROUND_UP +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof (a) / sizeof (a[0])) +#endif + +/* + * Compatibility macros/typedefs needed for Solaris -> Linux port + */ +#define P2ALIGN(x, align) ((x) & -(align)) +#define P2CROSS(x, y, align) (((x) ^ (y)) > (align) - 1) +#define P2ROUNDUP(x, align) (-(-(x) & -(align))) +#define P2PHASE(x, align) ((x) & ((align) - 1)) +#define P2NPHASE(x, align) (-(x) & ((align) - 1)) +#define ISP2(x) (((x) & ((x) - 1)) == 0) +#define IS_P2ALIGNED(v, a) ((((uintptr_t)(v)) & ((uintptr_t)(a) - 1)) == 0) +#define P2BOUNDARY(off, len, align) \ + (((off) ^ ((off) + (len) - 1)) > (align) - 1) + +/* + * Typed version of the P2* macros. These macros should be used to ensure + * that the result is correctly calculated based on the data type of (x), + * which is passed in as the last argument, regardless of the data + * type of the alignment. For example, if (x) is of type uint64_t, + * and we want to round it up to a page boundary using "PAGESIZE" as + * the alignment, we can do either + * + * P2ROUNDUP(x, (uint64_t)PAGESIZE) + * or + * P2ROUNDUP_TYPED(x, PAGESIZE, uint64_t) + */ +#define P2ALIGN_TYPED(x, align, type) \ + ((type)(x) & -(type)(align)) +#define P2PHASE_TYPED(x, align, type) \ + ((type)(x) & ((type)(align) - 1)) +#define P2NPHASE_TYPED(x, align, type) \ + (-(type)(x) & ((type)(align) - 1)) +#define P2ROUNDUP_TYPED(x, align, type) \ + (-(-(type)(x) & -(type)(align))) +#define P2END_TYPED(x, align, type) \ + (-(~(type)(x) & -(type)(align))) +#define P2PHASEUP_TYPED(x, align, phase, type) \ + ((type)(phase) - (((type)(phase) - (type)(x)) & -(type)(align))) +#define P2CROSS_TYPED(x, y, align, type) \ + (((type)(x) ^ (type)(y)) > (type)(align) - 1) +#define P2SAMEHIGHBIT_TYPED(x, y, type) \ + (((type)(x) ^ (type)(y)) < ((type)(x) & (type)(y))) + +/* + * P2* Macros from Illumos + */ + +/* + * return x rounded up to the next phase (offset) within align. + * phase should be < align. + * eg, P2PHASEUP(0x1234, 0x100, 0x10) == 0x1310 (0x13*align + phase) + * eg, P2PHASEUP(0x5600, 0x100, 0x10) == 0x5610 (0x56*align + phase) + */ +#define P2PHASEUP(x, align, phase) ((phase) - (((phase) - (x)) & -(align))) + +/* + * Return TRUE if they have the same highest bit set. + * eg, P2SAMEHIGHBIT(0x1234, 0x1001) == TRUE (the high bit is 0x1000) + * eg, P2SAMEHIGHBIT(0x1234, 0x3010) == FALSE (high bit of 0x3010 is 0x2000) + */ +#define P2SAMEHIGHBIT(x, y) (((x) ^ (y)) < ((x) & (y))) + +/* + * End Illumos copy-fest + */ + +/* avoid any possibility of clashing with version */ +#if defined(_KERNEL) && !defined(_KMEMUSER) && !defined(offsetof) +/* + * Use the correct builtin mechanism. The Traditional macro is + * not safe on this platform. + */ +#define offsetof(s, m) __builtin_offsetof(s, m) +#endif + +#define SET_ERROR(X) (X) + +#ifdef __cplusplus +} +#endif + +#endif /* _SPL_SYSMACROS_H */ diff --git a/include/os/macos/spl/sys/systeminfo.h b/include/os/macos/spl/sys/systeminfo.h new file mode 100644 index 000000000000..d1c15744ec5d --- /dev/null +++ b/include/os/macos/spl/sys/systeminfo.h @@ -0,0 +1,40 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_SYSTEMINFO_H +#define _SPL_SYSTEMINFO_H + +#define HW_INVALID_HOSTID 0xFFFFFFFF /* an invalid hostid */ +#define HW_HOSTID_LEN 11 /* minimum buffer size needed */ + /* to hold a decimal or hex */ + /* hostid string */ + +const char *spl_panicstr(void); +int spl_system_inshutdown(void); + + +#endif /* SPL_SYSTEMINFO_H */ diff --git a/include/os/macos/spl/sys/systm.h b/include/os/macos/spl/sys/systm.h new file mode 100644 index 000000000000..54b75e29b53e --- /dev/null +++ b/include/os/macos/spl/sys/systm.h @@ -0,0 +1,36 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_SYSTM_H +#define _SPL_SYSTM_H + +#include_next +#include + +typedef uintptr_t pc_t; + +#endif /* SPL_SYSTM_H */ diff --git a/include/os/macos/spl/sys/taskq.h b/include/os/macos/spl/sys/taskq.h new file mode 100644 index 000000000000..998dcad17e91 --- /dev/null +++ b/include/os/macos/spl/sys/taskq.h @@ -0,0 +1,123 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (C) 2015 Jorgen Lundman + */ + +#ifndef _SYS_TASKQ_H +#define _SYS_TASKQ_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define TASKQ_NAMELEN 31 + +typedef struct taskq taskq_t; +typedef uintptr_t taskqid_t; +typedef void (task_func_t)(void *); + +struct proc; +struct taskq_ent; + +/* New ZFS expects to find taskq_ent_t as well */ +#include + +/* + * Public flags for taskq_create(): bit range 0-15 + */ +#define TASKQ_PREPOPULATE 0x0001 /* Prepopulate with threads and data */ +#define TASKQ_CPR_SAFE 0x0002 /* Use CPR safe protocol */ +#define TASKQ_DYNAMIC 0x0004 /* Use dynamic thread scheduling */ +#define TASKQ_THREADS_CPU_PCT 0x0008 /* number of threads as % of ncpu */ +#define TASKQ_DC_BATCH 0x0010 /* Taskq uses SDC in batch mode */ + +#ifdef __APPLE__ +#define TASKQ_TIMESHARE 0x0020 /* macOS dynamic thread priority */ +#define TASKQ_REALLY_DYNAMIC 0x0040 /* don't filter out TASKQ_DYNAMIC */ +#endif + +/* + * Flags for taskq_dispatch. TQ_SLEEP/TQ_NOSLEEP should be same as + * KM_SLEEP/KM_NOSLEEP. + */ +#define TQ_SLEEP 0x00 /* Can block for memory */ +#define TQ_NOSLEEP 0x01 /* cannot block for memory; may fail */ +#define TQ_NOQUEUE 0x02 /* Do not enqueue if can't dispatch */ +#define TQ_NOALLOC 0x04 /* cannot allocate memory; may fail */ +#define TQ_FRONT 0x08 /* Put task at the front of the queue */ + +#define TASKQID_INVALID ((taskqid_t)0) + +#ifdef _KERNEL + +extern taskq_t *system_taskq; +/* Global dynamic task queue for long delay */ +extern taskq_t *system_delay_taskq; + +extern int spl_taskq_init(void); +extern void spl_taskq_fini(void); +extern void taskq_mp_init(void); + +extern taskq_t *taskq_create(const char *, int, pri_t, int, int, uint_t); +extern taskq_t *taskq_create_instance(const char *, int, int, pri_t, int, + int, uint_t); +extern taskq_t *taskq_create_proc(const char *, int, pri_t, int, int, + proc_t *, uint_t); +extern taskq_t *taskq_create_sysdc(const char *, int, int, int, + proc_t *, uint_t, uint_t); +extern taskqid_t taskq_dispatch(taskq_t *, task_func_t, void *, uint_t); +extern void nulltask(void *); +extern void taskq_destroy(taskq_t *); +extern void taskq_wait(taskq_t *); +#define HAVE_TASKQ_WAIT_ID +extern void taskq_wait_id(taskq_t *, taskqid_t); +extern void taskq_suspend(taskq_t *); +extern int taskq_suspended(taskq_t *); +extern void taskq_resume(taskq_t *); +extern int taskq_member(taskq_t *, kthread_t *); +extern boolean_t taskq_empty(taskq_t *tq); +extern int taskq_cancel_id(taskq_t *, taskqid_t); +extern taskq_t *taskq_of_curthread(void); +extern int taskq_empty_ent(struct taskq_ent *); + +extern void taskq_wait_outstanding(taskq_t *, taskqid_t); + +extern void system_taskq_init(void); +extern void system_taskq_fini(void); + +#endif /* _KERNEL */ + +extern int EMPTY_TASKQ(taskq_t *tq); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_TASKQ_H */ diff --git a/include/os/macos/spl/sys/taskq_impl.h b/include/os/macos/spl/sys/taskq_impl.h new file mode 100644 index 000000000000..60e86b367350 --- /dev/null +++ b/include/os/macos/spl/sys/taskq_impl.h @@ -0,0 +1,181 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + */ +/* + * Copyright (C) 2015 Jorgen Lundman + */ + + +#ifndef _SYS_TASKQ_IMPL_H +#define _SYS_TASKQ_IMPL_H + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct taskq_bucket taskq_bucket_t; + +typedef struct taskq_ent { + struct taskq_ent *tqent_next; + struct taskq_ent *tqent_prev; + task_func_t *tqent_func; + void *tqent_arg; + union { + taskq_bucket_t *tqent_bucket; + uintptr_t tqent_flags; + } tqent_un; + kthread_t *tqent_thread; + kcondvar_t tqent_cv; +#ifdef __APPLE__ + /* Used to simulate TS_STOPPED */ + kmutex_t tqent_thread_lock; + kcondvar_t tqent_thread_cv; +#endif +} taskq_ent_t; + +#define TQENT_FLAG_PREALLOC 0x1 + +/* + * Taskq Statistics fields are not protected by any locks. + */ +typedef struct tqstat { + uint_t tqs_hits; + uint_t tqs_misses; + uint_t tqs_overflow; /* no threads to allocate */ + uint_t tqs_tcreates; /* threads created */ + uint_t tqs_tdeaths; /* threads died */ + uint_t tqs_maxthreads; /* max # of alive threads */ + uint_t tqs_nomem; /* # of times there were no memory */ + uint_t tqs_disptcreates; +} tqstat_t; + +/* + * Per-CPU hash bucket manages taskq_bent_t structures using freelist. + */ +struct taskq_bucket { + kmutex_t tqbucket_lock; + taskq_t *tqbucket_taskq; /* Enclosing taskq */ + taskq_ent_t tqbucket_freelist; + uint_t tqbucket_nalloc; /* # of allocated entries */ + uint_t tqbucket_nfree; /* # of free entries */ + kcondvar_t tqbucket_cv; + ushort_t tqbucket_flags; + hrtime_t tqbucket_totaltime; + tqstat_t tqbucket_stat; +}; + +/* + * Bucket flags. + */ +#define TQBUCKET_CLOSE 0x01 +#define TQBUCKET_SUSPEND 0x02 + +#define TASKQ_INTERFACE_FLAGS 0x0000ffff /* defined in */ + +/* + * taskq implementation flags: bit range 16-31 + */ +#define TASKQ_CHANGING 0x00010000 /* nthreads != target */ +#define TASKQ_SUSPENDED 0x00020000 /* taskq is suspended */ +#define TASKQ_NOINSTANCE 0x00040000 /* no instance number */ +#define TASKQ_THREAD_CREATED 0x00080000 /* a thread has been created */ +#define TASKQ_DUTY_CYCLE 0x00100000 /* using the SDC class */ + +struct taskq { + char tq_name[TASKQ_NAMELEN + 1]; + kmutex_t tq_lock; + krwlock_t tq_threadlock; + kcondvar_t tq_dispatch_cv; + kcondvar_t tq_wait_cv; + kcondvar_t tq_exit_cv; + pri_t tq_pri; /* Scheduling priority */ + uint_t tq_flags; + int tq_active; + int tq_nthreads; + int tq_nthreads_target; + int tq_nthreads_max; + int tq_threads_ncpus_pct; + int tq_nalloc; + int tq_minalloc; + int tq_maxalloc; + kcondvar_t tq_maxalloc_cv; + int tq_maxalloc_wait; + taskq_ent_t *tq_freelist; + taskq_ent_t tq_task; + int tq_maxsize; + taskq_bucket_t *tq_buckets; /* Per-cpu array of buckets */ + int tq_instance; + uint_t tq_nbuckets; /* # of buckets (2^n) */ + union { + kthread_t *_tq_thread; + kthread_t **_tq_threadlist; + } tq_thr; + + list_node_t tq_cpupct_link; /* linkage for taskq_cpupct_list */ + proc_t *tq_proc; /* process for taskq threads */ + int tq_cpupart; /* cpupart id bound to */ + uint_t tq_DC; /* duty cycle for SDC */ + + /* + * Statistics. + */ + kstat_t *tq_kstat; /* Exported statistics */ + hrtime_t tq_totaltime; /* Time spent processing tasks */ + uint64_t tq_tasks; /* Total # of tasks posted */ + uint64_t tq_executed; /* Total # of tasks executed */ + int tq_maxtasks; /* Max number of tasks in the queue */ + int tq_tcreates; + int tq_tdeaths; +}; + +/* Special form of taskq dispatch that uses preallocated entries. */ +void taskq_dispatch_ent(taskq_t *, task_func_t, void *, uint_t, taskq_ent_t *); + + +#define tq_thread tq_thr._tq_thread +#define tq_threadlist tq_thr._tq_threadlist + +/* The MAX guarantees we have at least one thread */ +#define TASKQ_THREADS_PCT(ncpus, pct) MAX(((ncpus) * (pct)) / 100, 1) + +/* Extra ZOL / Apple */ +extern void taskq_init_ent(taskq_ent_t *t); +extern taskqid_t taskq_dispatch_delay(taskq_t *tq, task_func_t func, void *arg, + uint_t flags, clock_t expire_time); + + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_TASKQ_IMPL_H */ diff --git a/include/os/macos/spl/sys/thread.h b/include/os/macos/spl/sys/thread.h new file mode 100644 index 000000000000..e28eb6720b52 --- /dev/null +++ b/include/os/macos/spl/sys/thread.h @@ -0,0 +1,149 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2008 MacZFS + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_THREAD_H +#define _SPL_THREAD_H + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * OsX thread type is + * typedef struct thread *thread_t; + * + * Map that to the ZFS thread type: kthread_t + */ +#define kthread thread +#define kthread_t struct kthread + +/* + * Thread interfaces + */ +#define TP_MAGIC 0x53535353 + +#define TS_FREE 0x00 /* Thread at loose ends */ +#define TS_SLEEP 0x01 /* Awaiting an event */ +#define TS_RUN 0x02 /* Runnable, but not yet on a processor */ +#define TS_ONPROC 0x04 /* Thread is being run on a processor */ +#define TS_ZOMB 0x08 /* Thread has died but hasn't been reaped */ +#define TS_STOPPED 0x10 /* Stopped, initial state */ +#define TS_WAIT 0x20 /* Waiting to become runnable */ + + +typedef void (*thread_func_t)(void *); + + +#define curthread ((kthread_t *)current_thread()) /* current thread pointer */ +#define curproj (ttoproj(curthread)) /* current project pointer */ + +#define thread_join(t) VERIFY(0) + +// Drop the p0 argument, not used. + +#ifdef SPL_DEBUG_THREAD + +#define thread_create(A, B, C, D, E, F, G, H) \ + spl_thread_create_named(__FILE__, A, B, C, D, E, G, __FILE__, __LINE__, H) +#define thread_create_named(name, A, B, C, D, E, F, G, H) \ + spl_thread_create_named(name, A, B, C, D, E, G, __FILE__, __LINE__, H) + +extern kthread_t *spl_thread_create_named(char *name, + caddr_t stk, size_t stksize, + void (*proc)(void *), void *arg, size_t len, /* proc_t *pp, */ int state, + char *, int, pri_t pri); + +#else + +#define thread_create(A, B, C, D, E, F, G, H) \ + spl_thread_create_named(__FILE__, A, B, C, D, E, G, H) +#define thread_create_named(name, A, B, C, D, E, F, G, H) \ + spl_thread_create_named(name, A, B, C, D, E, G, H) +extern kthread_t *spl_thread_create_named(char *name, + caddr_t stk, size_t stksize, + void (*proc)(void *), void *arg, size_t len, /* proc_t *pp, */ int state, + pri_t pri); + +#endif + +#define thread_exit spl_thread_exit +extern void spl_thread_exit(void); + +extern kthread_t *spl_current_thread(void); + +extern void set_thread_importance_named(thread_t, pri_t, char *); +extern void set_thread_importance(thread_t, pri_t); + +extern void set_thread_throughput_named(thread_t, + thread_throughput_qos_t, char *); +extern void set_thread_throughput(thread_t, + thread_throughput_qos_t); + +extern void set_thread_latency_named(thread_t, + thread_latency_qos_t, char *); +extern void set_thread_latency(thread_t, + thread_latency_qos_t); + +extern void set_thread_timeshare_named(thread_t, + char *); +extern void set_thread_timeshare(thread_t); + +extern void spl_throttle_set_thread_io_policy(int); + +#define delay osx_delay +extern void osx_delay(int); + +#define KPREEMPT_SYNC 0 +static inline void kpreempt(int flags) +{ + (void) thread_block(THREAD_CONTINUE_NULL); +} + +static inline char * +getcomm(void) +{ + static char name[MAXCOMLEN + 1]; + proc_selfname(name, sizeof (name)); + /* Not thread safe */ + return (name); +} + +#define getpid() proc_selfpid() + +#ifdef __cplusplus +} +#endif + +#endif /* _SPL_THREAD_H */ diff --git a/include/os/macos/spl/sys/time.h b/include/os/macos/spl/sys/time.h new file mode 100644 index 000000000000..d02711280208 --- /dev/null +++ b/include/os/macos/spl/sys/time.h @@ -0,0 +1,90 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2008 MacZFS + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_TIME_H +#define _SPL_TIME_H + +#include +#include_next +#include +#include + +#if defined(CONFIG_64BIT) +#define TIME_MAX INT64_MAX +#define TIME_MIN INT64_MIN +#else +#define TIME_MAX INT32_MAX +#define TIME_MIN INT32_MIN +#endif + +#define SEC 1 +#define MILLISEC 1000 +#define MICROSEC 1000000 +#define NANOSEC 1000000000 + +/* Already defined in include/linux/time.h */ +#undef CLOCK_THREAD_CPUTIME_ID +#undef CLOCK_REALTIME +#undef CLOCK_MONOTONIC +#undef CLOCK_PROCESS_CPUTIME_ID + +typedef enum clock_type { + __CLOCK_REALTIME0 = 0, /* obsolete; same as CLOCK_REALTIME */ + CLOCK_VIRTUAL = 1, /* thread's user-level CPU clock */ + CLOCK_THREAD_CPUTIME_ID = 2, /* thread's user+system CPU clock */ + CLOCK_REALTIME = 3, /* wall clock */ + CLOCK_MONOTONIC = 4, /* high resolution monotonic clock */ + CLOCK_PROCESS_CPUTIME_ID = 5, /* process's user+system CPU clock */ + CLOCK_HIGHRES = CLOCK_MONOTONIC, /* alternate name */ + CLOCK_PROF = CLOCK_THREAD_CPUTIME_ID, /* alternate name */ +} clock_type_t; + +#define TIMESPEC_OVERFLOW(ts) \ + ((ts)->tv_sec < TIME_MIN || (ts)->tv_sec > TIME_MAX) + +typedef long long hrtime_t; + +extern hrtime_t gethrtime(void); +extern void gethrestime(struct timespec *); +extern time_t gethrestime_sec(void); +extern void hrt2ts(hrtime_t hrt, struct timespec *tsp); + +#define SEC_TO_TICK(sec) ((sec) * hz) +#define NSEC_TO_TICK(nsec) ((nsec) / (NANOSEC / hz)) + +#define MSEC2NSEC(m) ((hrtime_t)(m) * (NANOSEC / MILLISEC)) +#define NSEC2MSEC(n) ((n) / (NANOSEC / MILLISEC)) + +#define USEC2NSEC(m) ((hrtime_t)(m) * (NANOSEC / MICROSEC)) +#define NSEC2USEC(n) ((n) / (NANOSEC / MICROSEC)) + +#define NSEC2SEC(n) ((n) / (NANOSEC / SEC)) +#define SEC2NSEC(m) ((hrtime_t)(m) * (NANOSEC / SEC)) + + +#endif /* _SPL_TIME_H */ diff --git a/include/os/macos/spl/sys/timer.h b/include/os/macos/spl/sys/timer.h new file mode 100644 index 000000000000..ccdf64f5df12 --- /dev/null +++ b/include/os/macos/spl/sys/timer.h @@ -0,0 +1,88 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2008 MacZFS + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_TIMER_H +#define _SPL_TIMER_H + +#include + +/* Open Solaris lbolt is in hz */ +static inline uint64_t +zfs_lbolt(void) +{ + struct timeval tv; + uint64_t lbolt_hz; + microuptime(&tv); + lbolt_hz = ((uint64_t)tv.tv_sec * USEC_PER_SEC + tv.tv_usec) / 10000; + return (lbolt_hz); +} + + +#define lbolt zfs_lbolt() +#define lbolt64 zfs_lbolt() + +#define ddi_get_lbolt() (zfs_lbolt()) +#define ddi_get_lbolt64() (zfs_lbolt()) + +#define typecheck(type, x) \ + ( \ + { type __dummy; \ + typeof(x) __dummy2; \ + (void) (&__dummy == &__dummy2); \ + 1; \ + }) + + + +#define ddi_time_before(a, b) (typecheck(clock_t, a) && \ + typecheck(clock_t, b) && \ + ((a) - (b) < 0)) +#define ddi_time_after(a, b) ddi_time_before(b, a) + +#define ddi_time_before64(a, b) (typecheck(int64_t, a) && \ + typecheck(int64_t, b) && \ + ((a) - (b) < 0)) +#define ddi_time_after64(a, b) ddi_time_before64(b, a) + + + +extern void delay(clock_t ticks); + +#define usleep_range(wakeup, whocares) \ + do { \ + hrtime_t delta = wakeup - gethrtime(); \ + if (delta > 0) { \ + struct timespec ts; \ + ts.tv_sec = delta / NANOSEC; \ + ts.tv_nsec = delta % NANOSEC; \ + (void) msleep(NULL, NULL, PWAIT, "usleep_range", &ts); \ + } \ + } while (0) + + +#endif /* _SPL_TIMER_H */ diff --git a/include/os/macos/spl/sys/trace.h b/include/os/macos/spl/sys/trace.h new file mode 100644 index 000000000000..7b72d3a98d49 --- /dev/null +++ b/include/os/macos/spl/sys/trace.h @@ -0,0 +1,26 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _SPL_TRACE_H +#define _SPL_TRACE_H + + +#endif diff --git a/include/os/macos/spl/sys/tsd.h b/include/os/macos/spl/sys/tsd.h new file mode 100644 index 000000000000..cfc48000a5dd --- /dev/null +++ b/include/os/macos/spl/sys/tsd.h @@ -0,0 +1,54 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2008 MacZFS + * Copyright (C) 2013, 2020 Jorgen Lundman + * + */ + + +#ifndef _SPL_TSD_H +#define _SPL_TSD_H + +#include + +#define TSD_HASH_TABLE_BITS_DEFAULT 9 +#define TSD_KEYS_MAX 32768 +#define DTOR_PID (PID_MAX_LIMIT+1) +#define PID_KEY (TSD_KEYS_MAX+1) + +typedef void (*dtor_func_t)(void *); + +extern int tsd_set(uint_t, void *); +extern void *tsd_get(uint_t); +extern void *tsd_get_by_thread(uint_t, thread_t); +extern void tsd_create(uint_t *, dtor_func_t); +extern void tsd_destroy(uint_t *); +extern void tsd_exit(void); + +uint64_t spl_tsd_size(void); +void tsd_thread_exit(void); +int spl_tsd_init(void); +void spl_tsd_fini(void); + +#endif /* _SPL_TSD_H */ diff --git a/include/os/macos/spl/sys/types.h b/include/os/macos/spl/sys/types.h new file mode 100644 index 000000000000..656c4fd905b6 --- /dev/null +++ b/include/os/macos/spl/sys/types.h @@ -0,0 +1,128 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2008 MacZFS + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_TYPES_H +#define _SPL_TYPES_H + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +#include +#include +#include_next +#include +#include +#include + +#if !defined(MAC_OS_X_VERSION_10_12) || \ + (MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_12) +#include +#include +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Avoid kcdata.h header error */ +extern unsigned long strnlen(const char *, unsigned long); + +#ifdef __cplusplus +} +#endif + +#include + +#include + +#ifndef ULLONG_MAX +#define ULLONG_MAX (~0ULL) +#endif + +#ifndef LLONG_MAX +#define LLONG_MAX ((long long)(~0ULL>>1)) +#endif + +enum { B_FALSE = 0, B_TRUE = 1 }; +typedef short pri_t; +typedef unsigned long ulong_t; +typedef unsigned long long u_longlong_t; +typedef unsigned long long rlim64_t; +typedef unsigned long long loff_t; +typedef long long longlong_t; +typedef unsigned char uchar_t; +typedef unsigned int uint_t; +typedef unsigned short ushort_t; +typedef void *spinlock_t; +typedef long long offset_t; +typedef struct timespec timestruc_t; /* definition per SVr4 */ +typedef struct timespec timespec_t; +typedef ulong_t pgcnt_t; +typedef unsigned int umode_t; +#define NODEV32 (dev32_t)(-1) +typedef uint32_t dev32_t; +typedef uint_t minor_t; +typedef short index_t; + +#include +#define FCREAT O_CREAT +#define FTRUNC O_TRUNC +#define FEXCL O_EXCL +#define FNOCTTY O_NOCTTY +#define FNOFOLLOW O_NOFOLLOW + +#ifdef __APPLE__ +#define FSYNC O_SYNC /* file (data+inode) integrity while writing */ +#define FDSYNC O_DSYNC /* file data only integrity while writing */ +#define FOFFMAX 0x0000 /* not used */ +#define FRSYNC 0x0000 /* not used */ +#else +#define FRSYNC 0x8000 /* sync read operations at same level of */ + /* integrity as specified for writes by */ + /* FSYNC and FDSYNC flags */ +#define FOFFMAX 0x2000 /* large file */ +#endif + +#define EXPORT_SYMBOL(X) +#define module_param(X, Y, Z) +#define MODULE_PARM_DESC(X, Y) + +#ifdef __GNUC__ +#define member_type(type, member) __typeof__(((type *)0)->member) +#else +#define member_type(type, member) void +#endif + +#define container_of(ptr, type, member) ((type *) \ + ((char *)(member_type(type, member) *) \ + { ptr } - offsetof(type, member))) + +typedef struct timespec inode_timespec_t; + +#endif /* _SPL_TYPES_H */ diff --git a/include/os/macos/spl/sys/types32.h b/include/os/macos/spl/sys/types32.h new file mode 100644 index 000000000000..799a956b5e27 --- /dev/null +++ b/include/os/macos/spl/sys/types32.h @@ -0,0 +1,30 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#ifndef _SPL_TYPES32_H +#define _SPL_TYPES32_H + +typedef uint32_t caddr32_t; +typedef int32_t daddr32_t; +typedef int32_t time32_t; +typedef uint32_t size32_t; + +#endif /* SPL_TYPE32_H */ diff --git a/include/os/macos/spl/sys/uio.h b/include/os/macos/spl/sys/uio.h new file mode 100644 index 000000000000..eb4add4309cc --- /dev/null +++ b/include/os/macos/spl/sys/uio.h @@ -0,0 +1,177 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * University Copyright- Copyright (c) 1982, 1986, 1988 + * The Regents of the University of California + * All Rights Reserved + * + * University Acknowledgment- Portions of this document are derived from + * software developed by the University of California, Berkeley, and its + * contributors. + */ + + +#ifndef _SPL_UIO_H +#define _SPL_UIO_H + +#include_next +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct iovec iovec_t; + +typedef enum uio_seg zfs_uio_seg_t; +typedef enum uio_rw zfs_uio_rw_t; + +/* + * Hybrid uio, use OS uio for IO and communicating with XNU + * and internal uio for ZFS / crypto. The default mode is + * ZFS style, as zio_crypt.c creates uios on the stack, and + * they are uninitialised. However, all XNU entries will use + * ZFS_UIO_INIT_XNU(), so we can set uio_iov = NULL, to signify + * that it is a XNU uio. ZFS uio will always set uio_iov before + * it can use them. + */ +typedef struct zfs_uio { + /* Type A: XNU uio. */ + struct uio *uio_xnu; + /* Type B: Internal uio */ + const struct iovec *uio_iov; + int uio_iovcnt; + off_t uio_loffset; + zfs_uio_seg_t uio_segflg; + boolean_t uio_fault_disable; + uint16_t uio_fmode; + uint16_t uio_extflg; + ssize_t uio_resid; + size_t uio_skip; +} zfs_uio_t; + +#define ZFS_UIO_INIT_XNU(U, X) \ + zfs_uio_t _U = { 0 }; \ + zfs_uio_t *U = &_U; \ + (U)->uio_iov = NULL; \ + (U)->uio_xnu = X; + +static inline zfs_uio_seg_t +zfs_uio_segflg(zfs_uio_t *uio) +{ + if (uio->uio_iov == NULL) + return (uio_isuserspace(uio->uio_xnu) ? + UIO_USERSPACE : UIO_SYSSPACE); + return (uio->uio_segflg); +} + +static inline void +zfs_uio_setrw(zfs_uio_t *uio, zfs_uio_rw_t inout) +{ + if (uio->uio_iov == NULL) + uio_setrw(uio->uio_xnu, inout); +} + +static inline int +zfs_uio_iovcnt(zfs_uio_t *uio) +{ + if (uio->uio_iov == NULL) + return (uio_iovcnt(uio->uio_xnu)); + return (uio->uio_iovcnt); +} + +static inline off_t +zfs_uio_offset(zfs_uio_t *uio) +{ + if (uio->uio_iov == NULL) + return (uio_offset(uio->uio_xnu)); + return (uio->uio_loffset); +} + +static inline size_t +zfs_uio_resid(zfs_uio_t *uio) +{ + if (uio->uio_iov == NULL) + return (uio_resid(uio->uio_xnu)); + return (uio->uio_resid); +} + +static inline void +zfs_uio_setoffset(zfs_uio_t *uio, off_t off) +{ + if (uio->uio_iov == NULL) { + uio_setoffset(uio->uio_xnu, off); + return; + } + uio->uio_loffset = off; +} + +static inline void +zfs_uio_advance(zfs_uio_t *uio, size_t size) +{ + if (uio->uio_iov == NULL) { + uio_update(uio->uio_xnu, size); + } else { + uio->uio_resid -= size; + uio->uio_loffset += size; + } +} + +/* zfs_uio_iovlen(uio, 0) = uio_curriovlen() */ +static inline uint64_t +zfs_uio_iovlen(zfs_uio_t *uio, unsigned int idx) +{ + if (uio->uio_iov == NULL) { + user_size_t iov_len; + uio_getiov(uio->uio_xnu, idx, NULL, &iov_len); + return (iov_len); + } + return (uio->uio_iov[idx].iov_len); +} + +static inline void * +zfs_uio_iovbase(zfs_uio_t *uio, unsigned int idx) +{ + if (uio->uio_iov == NULL) { + user_addr_t iov_base; + uio_getiov(uio->uio_xnu, idx, &iov_base, NULL); + return ((void *)iov_base); + } + return (uio->uio_iov[(idx)].iov_base); +} + +extern int zfs_uio_prefaultpages(ssize_t, zfs_uio_t *); +#define zfs_uio_fault_disable(uio, set) +#define zfs_uio_fault_move(p, n, rw, u) zfs_uiomove((p), (n), (rw), (u)) + +#ifdef __cplusplus +} +#endif +#endif /* SPL_UIO_H */ diff --git a/include/os/macos/spl/sys/utsname.h b/include/os/macos/spl/sys/utsname.h new file mode 100644 index 000000000000..b6bcab77bb74 --- /dev/null +++ b/include/os/macos/spl/sys/utsname.h @@ -0,0 +1,48 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + + +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#ifndef _SPL_UTSNAME_H +#define _SPL_UTSNAME_H + +#define _SYS_NMLN 257 +struct opensolaris_utsname { + char sysname[_SYS_NMLN]; + char nodename[_SYS_NMLN]; + char release[_SYS_NMLN]; + char version[_SYS_NMLN]; + char machine[_SYS_NMLN]; +}; + +typedef struct opensolaris_utsname utsname_t; + +extern utsname_t *utsname(void); + +#endif /* SPL_UTSNAME_H */ diff --git a/include/os/macos/spl/sys/varargs.h b/include/os/macos/spl/sys/varargs.h new file mode 100644 index 000000000000..b7371a1f2a8a --- /dev/null +++ b/include/os/macos/spl/sys/varargs.h @@ -0,0 +1,32 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * + */ +#ifndef _SPL_VARARGS_H +#define _SPL_VARARGS_H + +#define __va_list va_list + +#endif /* SPL_VARARGS_H */ diff --git a/include/os/macos/spl/sys/vfs.h b/include/os/macos/spl/sys/vfs.h new file mode 100644 index 000000000000..aa78dc434793 --- /dev/null +++ b/include/os/macos/spl/sys/vfs.h @@ -0,0 +1,84 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Portions of this source code were derived from Berkeley 4.3 BSD + * under license from the Regents of the University of California. + */ + +#ifndef _SPL_ZFS_H +#define _SPL_ZFS_H + +#include +#include + +#define MAXFIDSZ 64 + +typedef struct mount vfs_t; + +#define vn_vfswlock(vp) (0) +#define vn_vfsunlock(vp) +#define VFS_HOLD(vfsp) +#define VFS_RELE(vfsp) + + + +/* + * File identifier. Should be unique per filesystem on a single + * machine. This is typically called by a stateless file server + * in order to generate "file handles". + * + * Do not change the definition of struct fid ... fid_t without + * letting the CacheFS group know about it! They will have to do at + * least two things, in the same change that changes this structure: + * 1. change CFSVERSION in usr/src/uts/common/sys/fs/cachefs_fs.h + * 2. put the old version # in the canupgrade array + * in cachfs_upgrade() in usr/src/cmd/fs.d/cachefs/fsck/fsck.c + * This is necessary because CacheFS stores FIDs on disk. + * + * Many underlying file systems cast a struct fid into other + * file system dependent structures which may require 4 byte alignment. + * Because a fid starts with a short it may not be 4 byte aligned, the + * fid_pad will force the alignment. + */ +#define MAXFIDSZ 64 +#define OLD_MAXFIDSZ 16 + +typedef struct fid { + union { + long fid_pad; + struct { + ushort_t len; /* length of data in bytes */ + char data[MAXFIDSZ]; /* data (variable len) */ + } _fid; + } un; +} fid_t; + + +extern void (*mountroot_post_hook)(void); + +#endif /* SPL_ZFS_H */ diff --git a/include/os/macos/spl/sys/vmem.h b/include/os/macos/spl/sys/vmem.h new file mode 100644 index 000000000000..d54df9a9f55c --- /dev/null +++ b/include/os/macos/spl/sys/vmem.h @@ -0,0 +1,178 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012 by Delphix. All rights reserved. + */ + +#ifndef _SYS_VMEM_H +#define _SYS_VMEM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +// Make sure IOMalloc uses kernel_map, and not kalloc.zones +// This is from XNU kalloc_max and kalloc_kernmap_size +// #define KMEM_QUANTUM (PAGESIZE << 2) * 16 + PAGESIZE; +#define KMEM_QUANTUM PAGESIZE + + /* + * Per-allocation flags + */ +#define VM_SLEEP 0x00000000 /* same as KM_SLEEP */ +#define VM_NOSLEEP 0x00000001 /* same as KM_NOSLEEP */ +#define VM_PANIC 0x00000002 /* same as KM_PANIC */ +#define VM_PUSHPAGE 0x00000004 /* same as KM_PUSHPAGE */ +#define VM_NORMALPRI 0x00000008 /* same as KM_NORMALPRI */ +#define VM_NODEBUG 0x00000010 /* matches KM_NODE~BUG, */ + /* not implemented on OSX */ +#define VM_NO_VBA 0x00000020 /* OSX: do not descend to the bucket layer */ +#define VM_KMFLAGS 0x000000ff /* flags that must match KM_* flags */ + +#define VM_BESTFIT 0x00000100 +#define VM_FIRSTFIT 0x00000200 +#define VM_NEXTFIT 0x00000400 + +/* + * The following flags are restricted for use only within the kernel. + * VM_MEMLOAD is for use by the HAT to avoid infinite recursion. + * VM_NORELOC is used by the kernel when static VA->PA mappings are required. + */ +#define VM_MEMLOAD 0x00000800 +#define VM_NORELOC 0x00001000 + +/* + * VM_ABORT requests that vmem_alloc() *ignore* the VM_SLEEP/VM_NOSLEEP flags + * and forgo reaping if the allocation or attempted import, fails. This + * flag is a segkmem-specific flag, and should not be used by anyone else. + */ +#define VM_ABORT 0x00002000 + +/* + * VM_ENDALLOC requests that large addresses be preferred in allocations. + * Has no effect if VM_NEXTFIT is active. + */ +#define VM_ENDALLOC 0x00004000 + +#define VM_FLAGS 0x0000FFFF + +/* + * Arena creation flags + */ +#define VMC_POPULATOR 0x00010000 +#define VMC_NO_QCACHE 0x00020000 /* cannot use quantum caches */ +#define VMC_IDENTIFIER 0x00040000 /* not backed by memory */ +// VMC_XALLOC 0x00080000 below +// VMC_XALIGN 0x00100000 below +#define VMC_DUMPSAFE 0x00200000 /* can use alternate dump memory */ +// KMC_IDENTIFIER == 0x00400000 +// KMC_PREFILL == 0x00800000 +#define VMC_TIMEFREE 0x01000000 /* keep span creation time, */ + /* newest spans to front */ +#define VMC_OLDFIRST 0x02000000 /* must accompany VMC_TIMEFREE, */ + /* oldest spans to front */ + +/* + * internal use only; the import function uses the vmem_ximport_t interface + * and may increase the request size if it so desires. + * VMC_XALIGN, for use with vmem_xcreate, specifies that + * the address returned by the import function will be + * aligned according to the alignment argument. + */ +#define VMC_XALLOC 0x00080000 +#define VMC_XALIGN 0x00100000 +#define VMC_FLAGS 0xFFFF0000 + +/* + * Public segment types + */ +#define VMEM_ALLOC 0x01 +#define VMEM_FREE 0x02 + +/* + * Implementation-private segment types + */ +#define VMEM_SPAN 0x10 +#define VMEM_ROTOR 0x20 +#define VMEM_WALKER 0x40 + +/* + * VMEM_REENTRANT indicates to vmem_walk() that the callback routine may + * call back into the arena being walked, so vmem_walk() must drop the + * arena lock before each callback. The caveat is that since the arena + * isn't locked, its state can change. Therefore it is up to the callback + * routine to handle cases where the segment isn't of the expected type. + * For example, we use this to walk heap_arena when generating a crash dump; + * see segkmem_dump() for sample usage. + */ +#define VMEM_REENTRANT 0x80000000 + +struct vmem; + +typedef struct vmem vmem_t; +typedef void *(vmem_alloc_t)(vmem_t *, size_t, int); +typedef void (vmem_free_t)(vmem_t *, void *, size_t); + +/* + * Alternate import style; the requested size is passed in a pointer, + * which can be increased by the import function if desired. + */ +typedef void *(vmem_ximport_t)(vmem_t *, size_t *, size_t, int); + +#ifdef _KERNEL +extern vmem_t *vmem_init(const char *, void *, size_t, size_t, + vmem_alloc_t *, vmem_free_t *); +extern void vmem_fini(vmem_t *); +extern void vmem_update(void *); +extern int vmem_is_populator(void); +extern size_t vmem_seg_size; +#endif + +extern vmem_t *vmem_create(const char *, void *, size_t, size_t, + vmem_alloc_t *, vmem_free_t *, vmem_t *, size_t, int); +extern vmem_t *vmem_xcreate(const char *, void *, size_t, size_t, + vmem_ximport_t *, vmem_free_t *, vmem_t *, size_t, int); +extern void vmem_destroy(vmem_t *); +extern void *vmem_alloc(vmem_t *, size_t, int); +extern void *vmem_xalloc(vmem_t *, size_t, size_t, size_t, size_t, + void *, void *, int); +extern void vmem_free(vmem_t *, void *, size_t); +extern void vmem_xfree(vmem_t *, void *, size_t); +extern void *vmem_add(vmem_t *, void *, size_t, int); +extern int vmem_contains(vmem_t *, void *, size_t); +extern void vmem_walk(vmem_t *, int, void (*)(void *, void *, size_t), void *); +extern size_t vmem_size(vmem_t *, int); +extern size_t vmem_size_locked(vmem_t *, int); +extern size_t vmem_size_semi_atomic(vmem_t *, int); +extern void vmem_qcache_reap(vmem_t *vmp); +extern int64_t vmem_buckets_size(int); +extern int64_t abd_arena_empty_space(void); +extern int64_t abd_arena_total_size(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_VMEM_H */ diff --git a/include/os/macos/spl/sys/vmem_impl.h b/include/os/macos/spl/sys/vmem_impl.h new file mode 100644 index 000000000000..233a6b33ae46 --- /dev/null +++ b/include/os/macos/spl/sys/vmem_impl.h @@ -0,0 +1,155 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1999-2001, 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_VMEM_IMPL_H +#define _SYS_VMEM_IMPL_H + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct vmem_seg vmem_seg_t; + +#define VMEM_STACK_DEPTH 20 + +struct vmem_seg { + /* + * The first four fields must match vmem_freelist_t exactly. + */ + uintptr_t vs_start; /* start of segment (inclusive) */ + uintptr_t vs_end; /* end of segment (exclusive) */ + vmem_seg_t *vs_knext; /* next of kin (alloc, free, span) */ + vmem_seg_t *vs_kprev; /* prev of kin */ + + vmem_seg_t *vs_anext; /* next in arena */ + vmem_seg_t *vs_aprev; /* prev in arena */ + uint8_t vs_type; /* alloc, free, span */ + uint8_t vs_import; /* non-zero if segment was imported */ + uint8_t vs_depth; /* stack depth if KMF_AUDIT active */ + /* + * if VM_FREESORT is set on the arena, then + * this field is set at span creation time. + */ + hrtime_t vs_span_createtime; + /* + * The following fields are present only when KMF_AUDIT is set. + */ + kthread_t *vs_thread; + hrtime_t vs_timestamp; + pc_t vs_stack[VMEM_STACK_DEPTH]; +}; + +typedef struct vmem_freelist { + uintptr_t vs_start; /* always zero */ + uintptr_t vs_end; /* segment size */ + vmem_seg_t *vs_knext; /* next of kin */ + vmem_seg_t *vs_kprev; /* prev of kin */ +} vmem_freelist_t; + +#define VS_SIZE(vsp) ((vsp)->vs_end - (vsp)->vs_start) + +/* + * Segment hashing + */ +#define VMEM_HASH_INDEX(a, s, q, m) \ + ((((a) + ((a) >> (s)) + ((a) >> ((s) << 1))) >> (q)) & (m)) + +#define VMEM_HASH(vmp, addr) \ + (&(vmp)->vm_hash_table[VMEM_HASH_INDEX(addr, \ + (vmp)->vm_hash_shift, (vmp)->vm_qshift, (vmp)->vm_hash_mask)]) + +#define VMEM_QCACHE_SLABSIZE(max) \ + MAX(1 << highbit(3 * (max)), 64) + +#define VMEM_NAMELEN 30 +#define VMEM_HASH_INITIAL 16 +#define VMEM_NQCACHE_MAX 16 +#define VMEM_FREELISTS (sizeof (void *) * 8) + +typedef struct vmem_kstat { + kstat_named_t vk_mem_inuse; /* memory in use */ + kstat_named_t vk_mem_import; /* memory imported */ + kstat_named_t vk_mem_total; /* total memory in arena */ + kstat_named_t vk_source_id; /* vmem id of vmem source */ + kstat_named_t vk_alloc; /* number of allocations */ + kstat_named_t vk_free; /* number of frees */ + kstat_named_t vk_wait; /* number of allocations that waited */ + kstat_named_t vk_fail; /* number of allocations that failed */ + kstat_named_t vk_lookup; /* hash lookup count */ + kstat_named_t vk_search; /* freelist search count */ + kstat_named_t vk_populate_fail; /* populates that failed */ + kstat_named_t vk_contains; /* vmem_contains() calls */ + kstat_named_t vk_contains_search; /* vmem_contains() search cnt */ + kstat_named_t vk_parent_alloc; /* called the source allocator */ + kstat_named_t vk_parent_free; /* called the source free function */ + kstat_named_t vk_threads_waiting; /* threads in cv_wait in vmem */ + /* allocator function */ + kstat_named_t vk_excess; /* count of retained excess imports */ +} vmem_kstat_t; + +struct vmem { + char vm_name[VMEM_NAMELEN]; /* arena name */ + kcondvar_t vm_cv; /* cv for blocking allocations */ + kmutex_t vm_lock; /* arena lock */ + uint32_t vm_id; /* vmem id */ + hrtime_t vm_createtime; + uint32_t vm_mtbf; /* induced alloc failure rate */ + int vm_cflags; /* arena creation flags */ + int vm_qshift; /* log2(vm_quantum) */ + size_t vm_quantum; /* vmem quantum */ + size_t vm_qcache_max; /* maximum size to front by kmem */ + size_t vm_min_import; /* smallest amount to import */ + void *(*vm_source_alloc)(vmem_t *, size_t, int); + void (*vm_source_free)(vmem_t *, void *, size_t); + vmem_t *vm_source; /* vmem source for imported memory */ + vmem_t *vm_next; /* next in vmem_list */ + kstat_t *vm_ksp; /* kstat */ + ssize_t vm_nsegfree; /* number of free vmem_seg_t's */ + vmem_seg_t *vm_segfree; /* free vmem_seg_t list */ + vmem_seg_t **vm_hash_table; /* allocated-segment hash table */ + size_t vm_hash_mask; /* hash_size - 1 */ + size_t vm_hash_shift; /* log2(vm_hash_mask + 1) */ + ulong_t vm_freemap; /* bitmap of non-empty freelists */ + vmem_seg_t vm_seg0; /* anchor segment */ + vmem_seg_t vm_rotor; /* rotor for VM_NEXTFIT allocations */ + vmem_seg_t *vm_hash0[VMEM_HASH_INITIAL]; /* initial hash table */ + void *vm_qcache[VMEM_NQCACHE_MAX]; /* quantum caches */ + vmem_freelist_t vm_freelist[VMEM_FREELISTS + 1]; /* power-of-2 flists */ + vmem_kstat_t vm_kstat; /* kstat data */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_VMEM_IMPL_H */ diff --git a/include/os/macos/spl/sys/vmsystm.h b/include/os/macos/spl/sys/vmsystm.h new file mode 100644 index 000000000000..421e26364c62 --- /dev/null +++ b/include/os/macos/spl/sys/vmsystm.h @@ -0,0 +1,35 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_VMSYSTM_H +#define _SPL_VMSYSTM_H + +#include + +#define xcopyout copyout + +#endif /* SPL_VMSYSTM_H */ diff --git a/include/os/macos/spl/sys/vnode.h b/include/os/macos/spl/sys/vnode.h new file mode 100644 index 000000000000..06f57918b1d2 --- /dev/null +++ b/include/os/macos/spl/sys/vnode.h @@ -0,0 +1,266 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2008 MacZFS + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_VNODE_H +#define _SPL_VNODE_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +// Be aware that Apple defines "typedef struct vnode *vnode_t" and +// ZFS uses "typedef struct vnode vnode_t". +// uio and vnode wrappers can be removed now. +// uio_t -> zfs_uio_t +// vnode_t -> struct vnode (as it is only used in os/macos/ +// proc_t is to work around vn_rdwr( ..., proc_t p) +#undef uio_t +#undef vnode_t +#undef proc_t +#define proc_t struct proc * +#include_next +#undef proc_t +#define proc_t struct proc +#define vnode_t struct vnode +#define uio_t struct uio + + +struct caller_context; +typedef struct caller_context caller_context_t; +typedef int vcexcl_t; + +enum vcexcl { NONEXCL, EXCL }; + +#define B_INVAL 0x01 +#define B_TRUNC 0x02 + +#define CREATE_XATTR_DIR 0x04 /* Create extended attr dir */ +#define ATTR_NOACLCHECK 0x20 + +#define IS_DEVVP(vp) \ + (vnode_ischr(vp) || vnode_isblk(vp) || vnode_isfifo(vp)) + +enum rm { RMFILE, RMDIRECTORY }; /* rm or rmdir (remove) */ +enum create { CRCREAT, CRMKNOD, CRMKDIR }; /* reason for create */ + +#define va_mask va_active +#define va_nodeid va_fileid +#define va_nblocks va_filerev + +/* + * vnode attr translations + */ +#define ATTR_TYPE VNODE_ATTR_va_type +#define ATTR_MODE VNODE_ATTR_va_mode +#define ATTR_ACL VNODE_ATTR_va_acl +#define ATTR_UID VNODE_ATTR_va_uid +#define ATTR_GID VNODE_ATTR_va_gid +#define ATTR_ATIME VNODE_ATTR_va_access_time +#define ATTR_MTIME VNODE_ATTR_va_modify_time +#define ATTR_CTIME VNODE_ATTR_va_change_time +#define ATTR_CRTIME VNODE_ATTR_va_create_time +#define ATTR_SIZE VNODE_ATTR_va_data_size +#define ATTR_NOSET 0 +/* + * OSX uses separate vnop getxattr and setxattr to deal with XATTRs, so + * we never get vop&XVATTR set from VFS. All internal checks for it in + * ZFS is not required. + */ +#define ATTR_XVATTR 0 +#define AT_XVATTR ATTR_XVATTR + +#define va_size va_data_size +#define va_atime va_access_time +#define va_mtime va_modify_time +#define va_ctime va_change_time +#define va_crtime va_create_time +#define va_bytes va_data_size + +typedef struct vnode_attr vattr; +typedef struct vnode_attr vattr_t; + +/* vsa_mask values */ +#define VSA_ACL 0x0001 +#define VSA_ACLCNT 0x0002 +#define VSA_DFACL 0x0004 +#define VSA_DFACLCNT 0x0008 +#define VSA_ACE 0x0010 +#define VSA_ACECNT 0x0020 +#define VSA_ACE_ALLTYPES 0x0040 +#define VSA_ACE_ACLFLAGS 0x0080 /* get/set ACE ACL flags */ + + +extern struct vnode *vn_alloc(int flag); + +extern int vn_open(char *pnamep, enum uio_seg seg, int filemode, + int createmode, struct vnode **vpp, enum create crwhy, mode_t umask); +extern int vn_openat(char *pnamep, enum uio_seg seg, int filemode, + int createmode, struct vnode **vpp, enum create crwhy, + mode_t umask, struct vnode *startvp); + +#define vn_renamepath(tdvp, svp, tnm, lentnm) do { } while (0) +#define vn_free(vp) do { } while (0) +#define vn_pages_remove(vp, fl, op) do { } while (0) + +/* XNU is a vn_rdwr, so we work around it to match arguments */ +/* This should be deprecated, if not now, soon. */ +extern int zfs_vn_rdwr(enum uio_rw rw, struct vnode *vp, caddr_t base, + ssize_t len, offset_t offset, enum uio_seg seg, int ioflag, + rlim64_t ulimit, cred_t *cr, ssize_t *residp); + +#define vn_rdwr(rw, vp, b, l, o, s, flg, li, cr, resid) \ + zfs_vn_rdwr((rw), (vp), (b), (l), (o), (s), (flg), (li), (cr), (resid)) + +/* Other vn_rdwr for zfs_file_t ops */ +struct spl_fileproc; +extern int spl_vn_rdwr(enum uio_rw rw, struct spl_fileproc *, caddr_t base, + ssize_t len, offset_t offset, enum uio_seg seg, int ioflag, + rlim64_t ulimit, cred_t *cr, ssize_t *residp); + +extern int vn_remove(char *fnamep, enum uio_seg seg, enum rm dirflag); +extern int vn_rename(char *from, char *to, enum uio_seg seg); + +#define LK_RETRY 0 +#define LK_SHARED 0 +#define VN_UNLOCK(vp) +static inline int +vn_lock(struct vnode *vp, int fl) +{ + return (0); +} + +/* + * XNU reserves fileID 1-15, so we remap them high. + * 2 is root-of-the-mount. + * If ID is same as root, return 2. Otherwise, if it is 0-15, return + * adjusted, otherwise, return as-is. + * See hfs_format.h: kHFSRootFolderID, kHFSExtentsFileID, ... + */ +#define INO_ROOT 2ULL +#define INO_RESERVED 16ULL /* [0-15] reserved. */ +#define INO_ISRESERVED(ID) ((ID) < (INO_RESERVED)) +/* 0xFFFFFFFFFFFFFFF0 */ +#define INO_MAP ((uint64_t)-INO_RESERVED) /* -16, -15, .., -1 */ + +#define INO_ZFSTOXNU(ID, ROOT) \ + ((ID) == (ROOT)?INO_ROOT:(INO_ISRESERVED(ID)?INO_MAP+(ID):(ID))) + +/* + * This macro relies on *unsigned*. + * If asking for 2, return rootID. If in special range, adjust to + * normal, otherwise, return as-is. + */ +#define INO_XNUTOZFS(ID, ROOT) \ + ((ID) == INO_ROOT)?(ROOT): \ + (INO_ISRESERVED((ID)-INO_MAP))?((ID)-INO_MAP):(ID) + +#define VN_HOLD(vp) vnode_getwithref(vp) +#define VN_RELE(vp) vnode_put(vp) + +void spl_rele_async(void *arg); +void vn_rele_async(struct vnode *vp, void *taskq); + +extern int vnode_iocount(struct vnode *); + +#define F_SEEK_HOLE SEEK_HOLE + +#define VN_RELE_ASYNC(vp, tq) vn_rele_async((vp), (tq)) + +#define vn_exists(vp) +#define vn_is_readonly(vp) vnode_vfsisrdonly(vp) + +#define vnode_pager_setsize(vp, sz) ubc_setsize((vp), (sz)) + +#define VATTR_NULL(v) do { } while (0) + +extern int VOP_CLOSE(struct vnode *vp, int flag, int count, + offset_t off, void *cr, void *); +extern int VOP_FSYNC(struct vnode *vp, int flags, void* unused, void *); +extern int VOP_SPACE(struct vnode *vp, int cmd, struct flock *fl, + int flags, offset_t off, cred_t *cr, void *ctx); + +extern int VOP_GETATTR(struct vnode *vp, vattr_t *vap, int flags, + void *x3, void *x4); + +#define VOP_UNLOCK(vp, fl) do { } while (0) + +void vfs_mountedfrom(struct mount *vfsp, char *osname); + +#define build_path(A, B, C, D, E, F) spl_build_path(A, B, C, D, E, F) +extern int spl_build_path(struct vnode *vp, char *buff, int buflen, + int *outlen, int flags, vfs_context_t ctx); + +extern struct vnode *rootdir; + +static inline int chklock(struct vnode *vp, int iomode, + unsigned long long offset, ssize_t len, int fmode, void *ct) +{ + return (0); +} + +#define vn_ismntpt(vp) (vnode_mountedhere(vp) != NULL) + +extern errno_t VOP_LOOKUP (struct vnode *, struct vnode **, + struct componentname *, vfs_context_t); +extern errno_t VOP_MKDIR (struct vnode *, struct vnode **, + struct componentname *, struct vnode_attr *, + vfs_context_t); +extern errno_t VOP_REMOVE (struct vnode *, struct vnode *, + struct componentname *, int, vfs_context_t); +extern errno_t VOP_SYMLINK (struct vnode *, struct vnode **, + struct componentname *, struct vnode_attr *, + char *, vfs_context_t); + +void spl_vnode_fini(void); +int spl_vnode_init(void); + +extern void spl_cache_purgevfs(struct mount *mp); +#define cache_purgevfs spl_cache_purgevfs + +vfs_context_t vfs_context_kernel(void); +vfs_context_t spl_vfs_context_kernel(void); +extern int spl_vnode_notify(struct vnode *vp, uint32_t type, + struct vnode_attr *vap); +extern int spl_vfs_get_notify_attributes(struct vnode_attr *vap); +extern void spl_hijack_mountroot(void *func); +extern void spl_setrootvnode(struct vnode *vp); + +struct vnode *getrootdir(void); +void spl_vfs_start(void); + +#endif /* SPL_VNODE_H */ diff --git a/include/os/macos/spl/sys/zmod.h b/include/os/macos/spl/sys/zmod.h new file mode 100644 index 000000000000..6965c91f3d6c --- /dev/null +++ b/include/os/macos/spl/sys/zmod.h @@ -0,0 +1,122 @@ +/* + * zlib.h -- interface of the 'zlib' general purpose compression library + * version 1.2.5, April 19th, 2010 + * + * Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Jean-loup Gailly + * Mark Adler + */ + +#ifndef _SPL_ZMOD_H +#define _SPL_ZMOD_H + + +#include +#include +#include + +struct _zmemheader { + uint64_t length; + char data[0]; +}; + +static inline void * +zfs_zalloc(void* opaque, uInt items, uInt size) +{ + struct _zmemheader *hdr; + size_t alloc_size = (items * size) + sizeof (uint64_t); + hdr = kmem_zalloc(alloc_size, KM_SLEEP); + hdr->length = alloc_size; + return (&hdr->data); +} + +static inline void +zfs_zfree(void *opaque, void *addr) +{ + struct _zmemheader *hdr; + hdr = addr; + hdr--; + kmem_free(hdr, hdr->length); +} + +/* + * Uncompress the buffer 'src' into the buffer 'dst'. The caller must store + * the expected decompressed data size externally so it can be passed in. + * The resulting decompressed size is then returned through dstlen. This + * function return Z_OK on success, or another error code on failure. + */ +static inline int + z_uncompress(void *dst, size_t *dstlen, const void *src, size_t srclen) +{ + z_stream zs; + int err; + + bzero(&zs, sizeof (zs)); + zs.next_in = (uchar_t *)src; + zs.avail_in = srclen; + zs.next_out = dst; + zs.avail_out = *dstlen; + zs.zalloc = zfs_zalloc; + zs.zfree = zfs_zfree; + if ((err = inflateInit(&zs)) != Z_OK) + return (err); + if ((err = inflate(&zs, Z_FINISH)) != Z_STREAM_END) { + (void) inflateEnd(&zs); + return (err == Z_OK ? Z_BUF_ERROR : err); + } + *dstlen = zs.total_out; + return (inflateEnd(&zs)); +} + +static inline int +z_compress_level(void *dst, size_t *dstlen, const void *src, size_t srclen, + int level) +{ + z_stream zs; + int err; + bzero(&zs, sizeof (zs)); + zs.next_in = (uchar_t *)src; + zs.avail_in = srclen; + zs.next_out = dst; + zs.avail_out = *dstlen; + zs.zalloc = zfs_zalloc; + zs.zfree = zfs_zfree; + if ((err = deflateInit(&zs, level)) != Z_OK) + return (err); + if ((err = deflate(&zs, Z_FINISH)) != Z_STREAM_END) { + (void) deflateEnd(&zs); + return (err == Z_OK ? Z_BUF_ERROR : err); + } + *dstlen = zs.total_out; + return (deflateEnd(&zs)); +} + +static inline int +z_compress(void *dst, size_t *dstlen, const void *src, size_t srclen) +{ + return (z_compress_level(dst, dstlen, src, srclen, + Z_DEFAULT_COMPRESSION)); +} + + +int spl_zlib_init(void); +void spl_zlib_fini(void); + +#endif /* SPL_ZMOD_H */ diff --git a/include/os/macos/spl/sys/zone.h b/include/os/macos/spl/sys/zone.h new file mode 100644 index 000000000000..deefad54a1d9 --- /dev/null +++ b/include/os/macos/spl/sys/zone.h @@ -0,0 +1,38 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#ifndef _SPL_ZONE_H +#define _SPL_ZONE_H + +#include + +#define GLOBAL_ZONEID 0 + +#define zone_dataset_visible(x, y) (1) +#define INGLOBALZONE(z) (1) + +#endif /* SPL_ZONE_H */ diff --git a/include/os/macos/zfs/Makefile.am b/include/os/macos/zfs/Makefile.am new file mode 100644 index 000000000000..081839c48c8f --- /dev/null +++ b/include/os/macos/zfs/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = sys diff --git a/include/os/macos/zfs/sys/Makefile.am b/include/os/macos/zfs/sys/Makefile.am new file mode 100644 index 000000000000..9779de08e0b3 --- /dev/null +++ b/include/os/macos/zfs/sys/Makefile.am @@ -0,0 +1,10 @@ +KERNEL_H = \ + $(top_srcdir)/include/os/macos/zfs/sys/kstat_osx.h \ + $(top_srcdir)/include/os/macos/spl/sys/ldi_buf.h \ + $(top_srcdir)/include/os/macos/spl/sys/ldi_impl_osx.h \ + $(top_srcdir)/include/os/macos/spl/sys/ldi_osx.h \ + $(top_srcdir)/include/os/macos/spl/sys/trace_zfs.h \ + $(top_srcdir)/include/os/macos/spl/sys/vdev_disk_os.h \ + $(top_srcdir)/include/os/macos/spl/sys/zfs_ioctl_compat.h \ + $(top_srcdir)/include/os/macos/spl/sys/zfs_vfsops.h \ + $(top_srcdir)/include/os/macos/spl/sys/zfs_znode_impl.h diff --git a/include/os/macos/zfs/sys/ZFSDataset.h b/include/os/macos/zfs/sys/ZFSDataset.h new file mode 100644 index 000000000000..948e64432a8f --- /dev/null +++ b/include/os/macos/zfs/sys/ZFSDataset.h @@ -0,0 +1,142 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2016, Evan Susarret. All rights reserved. + */ + +#ifndef ZFSDATASET_H_INCLUDED +#define ZFSDATASET_H_INCLUDED + +#ifdef __cplusplus + +#include +#include +#include + +#ifdef super +#undef super +#endif +#define super IOMedia + +// #define kZFSContentHint "6A898CC3-1DD2-11B2-99A6-080020736631" +#define kZFSContentHint "ZFS_Dataset" + +#define kZFSIOMediaPrefix "ZFS " +#define kZFSIOMediaSuffix " Media" +#define kZFSDatasetNameKey "ZFS Dataset" +#define kZFSDatasetClassKey "ZFSDataset" + +class ZFSDataset : public IOMedia +{ + OSDeclareDefaultStructors(ZFSDataset) +public: +#if 0 + /* XXX Only for debug tracing */ + virtual bool open(IOService *client, + IOOptionBits options, IOStorageAccess access = 0); + virtual bool isOpen(const IOService *forClient = 0) const; + virtual void close(IOService *client, + IOOptionBits options); + + virtual bool handleOpen(IOService *client, + IOOptionBits options, void *access); + virtual bool handleIsOpen(const IOService *client) const; + virtual void handleClose(IOService *client, + IOOptionBits options); + + virtual bool attach(IOService *provider); + virtual void detach(IOService *provider); + + virtual bool start(IOService *provider); + virtual void stop(IOService *provider); +#endif + + virtual bool init(UInt64 base, UInt64 size, + UInt64 preferredBlockSize, + IOMediaAttributeMask attributes, + bool isWhole, bool isWritable, + const char *contentHint = 0, + OSDictionary *properties = 0); + virtual void free(); + + static ZFSDataset * withDatasetNameAndSize(const char *name, + uint64_t size); + + virtual void read(IOService *client, + UInt64 byteStart, IOMemoryDescriptor *buffer, + IOStorageAttributes *attributes, + IOStorageCompletion *completion); + virtual void write(IOService *client, + UInt64 byteStart, IOMemoryDescriptor *buffer, + IOStorageAttributes *attributes, + IOStorageCompletion *completion); + +#if defined(MAC_OS_X_VERSION_10_11) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11) + virtual IOReturn synchronize(IOService *client, + UInt64 byteStart, UInt64 byteCount, + IOStorageSynchronizeOptions options = 0); +#else + virtual IOReturn synchronizeCache(IOService *client); +#endif + + virtual IOReturn unmap(IOService *client, + IOStorageExtent *extents, UInt32 extentsCount, +#if defined(MAC_OS_X_VERSION_10_11) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11) + IOStorageUnmapOptions options = 0); +#else + UInt32 options = 0); +#endif + + virtual bool lockPhysicalExtents(IOService *client); + virtual IOStorage *copyPhysicalExtent(IOService *client, + UInt64 *byteStart, UInt64 *byteCount); + virtual void unlockPhysicalExtents(IOService *client); + +#if defined(MAC_OS_X_VERSION_10_10) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10) + virtual IOReturn setPriority(IOService *client, + IOStorageExtent *extents, UInt32 extentsCount, + IOStoragePriority priority); +#endif + + virtual UInt64 getPreferredBlockSize() const; + virtual UInt64 getSize() const; + virtual UInt64 getBase() const; + + virtual bool isEjectable() const; + virtual bool isFormatted() const; + virtual bool isWhole() const; + virtual bool isWritable() const; + + virtual const char *getContent() const; + virtual const char *getContentHint() const; + virtual IOMediaAttributeMask getAttributes() const; + +protected: +private: + bool setDatasetName(const char *); +}; + +#endif /* __cplusplus */ + +#endif /* ZFSDATASET_H_INCLUDED */ diff --git a/include/os/macos/zfs/sys/ZFSDatasetProxy.h b/include/os/macos/zfs/sys/ZFSDatasetProxy.h new file mode 100644 index 000000000000..e220cdcf9aaf --- /dev/null +++ b/include/os/macos/zfs/sys/ZFSDatasetProxy.h @@ -0,0 +1,82 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2016, Evan Susarret. All rights reserved. + */ + +#ifndef ZFSDATASETPROXY_H_INCLUDED +#define ZFSDATASETPROXY_H_INCLUDED + +#include + +class ZFSDatasetProxy : public IOBlockStorageDevice +{ + OSDeclareDefaultStructors(ZFSDatasetProxy); +public: + + virtual void free(void); + virtual bool init(OSDictionary *properties); + virtual bool start(IOService *provider); + + /* IOBlockStorageDevice */ + virtual IOReturn doSynchronizeCache(void); + virtual IOReturn doAsyncReadWrite(IOMemoryDescriptor *, + UInt64, UInt64, IOStorageAttributes *, + IOStorageCompletion *); + virtual UInt32 doGetFormatCapacities(UInt64 *, + UInt32) const; + virtual IOReturn doFormatMedia(UInt64 byteCapacity); + virtual IOReturn doEjectMedia(); + virtual char *getVendorString(); + virtual char *getProductString(); + virtual char *getRevisionString(); + virtual char *getAdditionalDeviceInfoString(); + virtual IOReturn reportWriteProtection(bool *); + virtual IOReturn reportRemovability(bool *); + virtual IOReturn reportMediaState(bool *, bool *); + virtual IOReturn reportBlockSize(UInt64 *); + virtual IOReturn reportEjectability(bool *); + virtual IOReturn reportMaxValidBlock(UInt64 *); + + virtual IOReturn setWriteCacheState(bool enabled); + virtual IOReturn getWriteCacheState(bool *enabled); +#if 0 + virtual void read(IOService *client, UInt64 byteStart, + IOMemoryDescriptor *buffer, IOStorageAttributes *attr, + IOStorageCompletion *completion); + virtual void write(IOService *client, UInt64 byteStart, + IOMemoryDescriptor *buffer, IOStorageAttributes *attr, + IOStorageCompletion *completion); +#endif + +protected: +private: + /* These are declared class static to share across instances */ + const char *vendorString; + const char *revisionString; + const char *infoString; + /* These are per-instance */ + const char *productString; + uint64_t _pool_bcount; + bool isReadOnly; +}; + +#endif /* ZFSDATASETPROXY_H_INCLUDED */ diff --git a/include/os/macos/zfs/sys/ZFSDatasetScheme.h b/include/os/macos/zfs/sys/ZFSDatasetScheme.h new file mode 100644 index 000000000000..eaa8bb368d2a --- /dev/null +++ b/include/os/macos/zfs/sys/ZFSDatasetScheme.h @@ -0,0 +1,126 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2016, Evan Susarret. All rights reserved. + */ + +#ifndef ZFSDATASETSCHEME_H_INCLUDED +#define ZFSDATASETSCHEME_H_INCLUDED + +#define kZFSDatasetSchemeClass "ZFSDatasetScheme" + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +int zfs_osx_proxy_get_osname(const char *bsdname, + char *osname, int len); +int zfs_osx_proxy_get_bsdname(const char *osname, + char *bsdname, int len); + + +void zfs_osx_proxy_remove(const char *osname); +int zfs_osx_proxy_create(const char *osname); + +#ifdef __cplusplus +} /* extern "C" */ + +/* Not C external */ +ZFSDataset * zfs_osx_proxy_get(const char *osname); + +class ZFSDatasetScheme : public IOPartitionScheme +{ + OSDeclareDefaultStructors(ZFSDatasetScheme); +public: + + virtual void free(void); + virtual bool init(OSDictionary *properties); + virtual bool start(IOService *provider); + virtual IOService *probe(IOService *provider, SInt32 *score); + + bool addDataset(const char *osname); + bool removeDataset(const char *osname, bool force); + + /* Compatibility shims */ + virtual void read(IOService *client, + UInt64 byteStart, + IOMemoryDescriptor *buffer, + IOStorageAttributes *attributes, + IOStorageCompletion *completion); + + virtual void write(IOService *client, + UInt64 byteStart, + IOMemoryDescriptor *buffer, + IOStorageAttributes *attributes, + IOStorageCompletion *completion); + +#if defined(MAC_OS_X_VERSION_10_11) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11) + virtual IOReturn synchronize(IOService *client, + UInt64 byteStart, + UInt64 byteCount, + IOStorageSynchronizeOptions options = 0); +#else + virtual IOReturn synchronizeCache(IOService *client); +#endif + + virtual IOReturn unmap(IOService *client, + IOStorageExtent *extents, + UInt32 extentsCount, +#if defined(MAC_OS_X_VERSION_10_11) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11) + IOStorageUnmapOptions options = 0); +#else + UInt32 options = 0); +#endif + + virtual bool lockPhysicalExtents(IOService *client); + + virtual IOStorage *copyPhysicalExtent(IOService *client, + UInt64 * byteStart, + UInt64 * byteCount); + + virtual void unlockPhysicalExtents(IOService *client); + +#if defined(MAC_OS_X_VERSION_10_10) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10) + virtual IOReturn setPriority(IOService *client, + IOStorageExtent *extents, + UInt32 extentsCount, + IOStoragePriority priority); +#endif + +protected: +private: + OSSet *_datasets; + OSOrderedSet *_holes; + uint64_t _max_id; + + uint32_t getNextPartitionID(); + void returnPartitionID(uint32_t part_id); +}; + +#endif /* __cplusplus */ +#endif /* ZFSDATASETSCHEME_H_INCLUDED */ diff --git a/include/os/macos/zfs/sys/ZFSPool.h b/include/os/macos/zfs/sys/ZFSPool.h new file mode 100644 index 000000000000..56a190a0c65a --- /dev/null +++ b/include/os/macos/zfs/sys/ZFSPool.h @@ -0,0 +1,127 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2016, Evan Susarret. All rights reserved. + */ + +#ifndef ZFSPOOL_H_INCLUDED +#define ZFSPOOL_H_INCLUDED + +#ifdef __cplusplus +#include + +#pragma mark - ZFSPool + +#define kZFSPoolNameKey "ZFS Pool Name" +#define kZFSPoolSizeKey "ZFS Pool Size" +#define kZFSPoolGUIDKey "ZFS Pool GUID" +#define kZFSPoolReadOnlyKey "ZFS Pool Read-Only" + +typedef struct spa spa_t; + +class ZFSPool : public IOService { + OSDeclareDefaultStructors(ZFSPool); + +protected: +#if 0 + /* XXX Only for debug tracing */ + virtual bool open(IOService *client, + IOOptionBits options, void *arg = 0); + virtual bool isOpen(const IOService *forClient = 0) const; + virtual void close(IOService *client, + IOOptionBits options); +#endif + + bool setPoolName(const char *name); + + virtual bool handleOpen(IOService *client, + IOOptionBits options, void *arg); + virtual bool handleIsOpen(const IOService *client) const; + virtual void handleClose(IOService *client, + IOOptionBits options); + + virtual bool init(OSDictionary *properties, spa_t *spa); + virtual void free(); + +#if 0 + /* IOBlockStorageDevice */ + virtual IOReturn doSynchronizeCache(void); + virtual IOReturn doAsyncReadWrite(IOMemoryDescriptor *, + UInt64, UInt64, IOStorageAttributes *, + IOStorageCompletion *); + virtual UInt32 doGetFormatCapacities(UInt64 *, + UInt32) const; + virtual IOReturn doFormatMedia(UInt64 byteCapacity); + virtual IOReturn doEjectMedia(); + virtual char *getVendorString(); + virtual char *getProductString(); + virtual char *getRevisionString(); + virtual char *getAdditionalDeviceInfoString(); + virtual IOReturn reportWriteProtection(bool *); + virtual IOReturn reportRemovability(bool *); + virtual IOReturn reportMediaState(bool *, bool *); + virtual IOReturn reportBlockSize(UInt64 *); + virtual IOReturn reportEjectability(bool *); + virtual IOReturn reportMaxValidBlock(UInt64 *); + +public: + virtual void read(IOService *client, UInt64 byteStart, + IOMemoryDescriptor *buffer, IOStorageAttributes *attr, + IOStorageCompletion *completion); + virtual void write(IOService *client, UInt64 byteStart, + IOMemoryDescriptor *buffer, IOStorageAttributes *attr, + IOStorageCompletion *completion); +#endif +public: + static ZFSPool * withProviderAndPool(IOService *, spa_t *); + +private: + OSSet *_openClients; + spa_t *_spa; + +#if 0 + /* These are declared class static to share across instances */ + static const char *vendorString; + static const char *revisionString; + static const char *infoString; + /* These are per-instance */ + const char *productString; + bool isReadOnly; +#endif +}; + +/* C++ wrapper, C uses opaque pointer reference */ +typedef struct spa_iokit { + ZFSPool *proxy; +} spa_iokit_t; + +extern "C" { +#endif /* __cplusplus */ + +/* C functions */ +void spa_iokit_pool_proxy_destroy(spa_t *spa); +int spa_iokit_pool_proxy_create(spa_t *spa); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ZFSPOOL_H_INCLUDED */ diff --git a/include/os/macos/zfs/sys/finderinfo.h b/include/os/macos/zfs/sys/finderinfo.h new file mode 100644 index 000000000000..ee3b48017bf5 --- /dev/null +++ b/include/os/macos/zfs/sys/finderinfo.h @@ -0,0 +1,36 @@ +#ifndef FINDERINFO_H +#define FINDERINFO_H + + +struct FndrExtendedDirInfo { + u_int32_t document_id; + u_int32_t date_added; + u_int16_t extended_flags; + u_int16_t reserved3; + u_int32_t write_gen_counter; +} __attribute__((aligned(2), packed)); + +struct FndrExtendedFileInfo { + u_int32_t document_id; + u_int32_t date_added; + u_int16_t extended_flags; + u_int16_t reserved2; + u_int32_t write_gen_counter; +} __attribute__((aligned(2), packed)); + +/* Finder information */ +struct FndrFileInfo { + u_int32_t fdType; + u_int32_t fdCreator; + u_int16_t fdFlags; + struct { + int16_t v; + int16_t h; + } fdLocation; + int16_t opaque; +} __attribute__((aligned(2), packed)); +typedef struct FndrFileInfo FndrFileInfo; + + + +#endif diff --git a/include/os/macos/zfs/sys/hfs_internal.h b/include/os/macos/zfs/sys/hfs_internal.h new file mode 100644 index 000000000000..ea2a8ad9bafa --- /dev/null +++ b/include/os/macos/zfs/sys/hfs_internal.h @@ -0,0 +1,191 @@ + +#ifndef HFS_INTERNAL_H +#define HFS_INTERNAL_H + +// BGH - Definitions of HFS vnops that we will need to emulate +// including supporting structures. + +struct hfs_journal_info { + off_t jstart; + off_t jsize; +}; + +struct user32_access_t { + uid_t uid; + short flags; + short num_groups; + int num_files; + user32_addr_t file_ids; + user32_addr_t groups; + user32_addr_t access; +}; + +struct user64_access_t { + uid_t uid; + short flags; + short num_groups; + int num_files; + user64_addr_t file_ids; + user64_addr_t groups; + user64_addr_t access; +}; + +struct user32_ext_access_t { + uint32_t flags; + uint32_t num_files; + uint32_t map_size; + user32_addr_t file_ids; + user32_addr_t bitmap; + user32_addr_t access; + uint32_t num_parents; + user32_addr_t parents; +}; + +struct user64_ext_access_t { + uint32_t flags; + uint32_t num_files; + uint32_t map_size; + user64_addr_t file_ids; + user64_addr_t bitmap; + user64_addr_t access; + uint32_t num_parents; + user64_addr_t parents; +}; + +/* + * HFS specific fcntl()'s + */ +#define HFS_BULKACCESS (FCNTL_FS_SPECIFIC_BASE + 0x00001) +#define HFS_GET_MOUNT_TIME (FCNTL_FS_SPECIFIC_BASE + 0x00002) +#define HFS_GET_LAST_MTIME (FCNTL_FS_SPECIFIC_BASE + 0x00003) +#define HFS_GET_BOOT_INFO (FCNTL_FS_SPECIFIC_BASE + 0x00004) +#define HFS_SET_BOOT_INFO (FCNTL_FS_SPECIFIC_BASE + 0x00005) + +/* HFS FS CONTROL COMMANDS */ + +#define HFSIOC_RESIZE_PROGRESS _IOR('h', 1, u_int32_t) +#define HFS_RESIZE_PROGRESS IOCBASECMD(HFSIOC_RESIZE_PROGRESS) + +#define HFSIOC_RESIZE_VOLUME _IOW('h', 2, u_int64_t) +#define HFS_RESIZE_VOLUME IOCBASECMD(HFSIOC_RESIZE_VOLUME) + +#define HFSIOC_CHANGE_NEXT_ALLOCATION _IOWR('h', 3, u_int32_t) +#define HFS_CHANGE_NEXT_ALLOCATION IOCBASECMD(HFSIOC_CHANGE_NEXT_ALLOCATION) +/* + * Magic value for next allocation to use with fcntl to set next allocation + * to zero and never update it again on new block allocation. + */ +#define HFS_NO_UPDATE_NEXT_ALLOCATION 0xffffFFFF + +#define HFSIOC_GETCREATETIME _IOR('h', 4, time_t) +#define HFS_GETCREATETIME IOCBASECMD(HFSIOC_GETCREATETIME) + +#define HFSIOC_SETBACKINGSTOREINFO _IOW('h', 7, struct hfs_backingstoreinfo) +#define HFS_SETBACKINGSTOREINFO IOCBASECMD(HFSIOC_SETBACKINGSTOREINFO) + +#define HFSIOC_CLRBACKINGSTOREINFO _IO('h', 8) +#define HFS_CLRBACKINGSTOREINFO IOCBASECMD(HFSIOC_CLRBACKINGSTOREINFO) + +#define HFSIOC_BULKACCESS _IOW('h', 9, struct user32_access_t) +#define HFS_BULKACCESS_FSCTL IOCBASECMD(HFSIOC_BULKACCESS) + +#define HFSIOC_SETACLSTATE _IOW('h', 10, int32_t) +#define HFS_SETACLSTATE IOCBASECMD(HFSIOC_SETACLSTATE) + +#define HFSIOC_PREV_LINK _IOWR('h', 11, u_int32_t) +#define HFS_PREV_LINK IOCBASECMD(HFSIOC_PREV_LINK) + +#define HFSIOC_NEXT_LINK _IOWR('h', 12, u_int32_t) +#define HFS_NEXT_LINK IOCBASECMD(HFSIOC_NEXT_LINK) + +#define HFSIOC_GETPATH _IOWR('h', 13, pathname_t) +#define HFS_GETPATH IOCBASECMD(HFSIOC_GETPATH) +#define HFS_GETPATH_VOLUME_RELATIVE 0x1 + +/* This define is deemed secret by Apple */ +#define BUILDPATH_VOLUME_RELATIVE 0x8 + +/* Enable/disable extent-based extended attributes */ +#define HFSIOC_SET_XATTREXTENTS_STATE _IOW('h', 14, u_int32_t) +#define HFS_SET_XATTREXTENTS_STATE IOCBASECMD(HFSIOC_SET_XATTREXTENTS_STATE) + +#define HFSIOC_EXT_BULKACCESS _IOW('h', 15, struct user32_ext_access_t) +#define HFS_EXT_BULKACCESS_FSCTL IOCBASECMD(HFSIOC_EXT_BULKACCESS) + +#define HFSIOC_MARK_BOOT_CORRUPT _IO('h', 16) +#define HFS_MARK_BOOT_CORRUPT IOCBASECMD(HFSIOC_MARK_BOOT_CORRUPT) + +#define HFSIOC_GET_JOURNAL_INFO _IOR('h', 17, struct hfs_journal_info) +#define HFS_FSCTL_GET_JOURNAL_INFO IOCBASECMD(HFSIOC_GET_JOURNAL_INFO) + +#define HFSIOC_SET_VERY_LOW_DISK _IOW('h', 20, u_int32_t) +#define HFS_FSCTL_SET_VERY_LOW_DISK IOCBASECMD(HFSIOC_SET_VERY_LOW_DISK) + +#define HFSIOC_SET_LOW_DISK _IOW('h', 21, u_int32_t) +#define HFS_FSCTL_SET_LOW_DISK IOCBASECMD(HFSIOC_SET_LOW_DISK) + +#define HFSIOC_SET_DESIRED_DISK _IOW('h', 22, u_int32_t) +#define HFS_FSCTL_SET_DESIRED_DISK IOCBASECMD(HFSIOC_SET_DESIRED_DISK) + +#define HFSIOC_SET_ALWAYS_ZEROFILL _IOW('h', 23, int32_t) +#define HFS_SET_ALWAYS_ZEROFILL IOCBASECMD(HFSIOC_SET_ALWAYS_ZEROFILL) + +#define HFSIOC_VOLUME_STATUS _IOR('h', 24, u_int32_t) +#define HFS_VOLUME_STATUS IOCBASECMD(HFSIOC_VOLUME_STATUS) + +/* Disable metadata zone for given volume */ +#define HFSIOC_DISABLE_METAZONE _IO('h', 25) +#define HFS_DISABLE_METAZONE IOCBASECMD(HFSIOC_DISABLE_METAZONE) + +/* Change the next CNID value */ +#define HFSIOC_CHANGE_NEXTCNID _IOWR('h', 26, u_int32_t) +#define HFS_CHANGE_NEXTCNID IOCBASECMD(HFSIOC_CHANGE_NEXTCNID) + +/* Get the low disk space values */ +#define HFSIOC_GET_VERY_LOW_DISK _IOR('h', 27, u_int32_t) +#define HFS_FSCTL_GET_VERY_LOW_DISK IOCBASECMD(HFSIOC_GET_VERY_LOW_DISK) + +#define HFSIOC_GET_LOW_DISK _IOR('h', 28, u_int32_t) +#define HFS_FSCTL_GET_LOW_DISK IOCBASECMD(HFSIOC_GET_LOW_DISK) + +#define HFSIOC_GET_DESIRED_DISK _IOR('h', 29, u_int32_t) +#define HFS_FSCTL_GET_DESIRED_DISK IOCBASECMD(HFSIOC_GET_DESIRED_DISK) + +/* + * revisiond only uses this when something transforms in a way + * the kernel can't track such as "foo.rtf" -> "foo.rtfd" + */ +#define HFSIOC_TRANSFER_DOCUMENT_ID _IOW('h', 32, u_int32_t) +#define HFS_TRANSFER_DOCUMENT_ID IOCBASECMD(HFSIOC_TRANSFER_DOCUMENT_ID) + + +/* fcntl.h */ +#define F_MAKECOMPRESSED 80 + +/* Get file system information for the given volume */ +// #define HFSIOC_GET_FSINFO _IOWR('h', 45, hfs_fsinfo) +// #define HFS_GET_FSINFO IOCBASECMD(HFSIOC_GET_FSINFO) + +/* Re-pin hotfile data; argument controls what state gets repinned */ +#define HFSIOC_REPIN_HOTFILE_STATE _IOWR('h', 46, u_int32_t) +#define HFS_REPIN_HOTFILE_STATE IOCBASECMD(HFSIOC_REPIN_HOTFILE_STATE) + +/* Mark a directory or file as worth caching on any underlying "fast" device */ +#define HFSIOC_SET_HOTFILE_STATE _IOWR('h', 47, u_int32_t) +#define HFS_SET_HOTFILE_STATE IOCBASECMD(HFSIOC_SET_HOTFILE_STATE) + +#define APFSIOC_SET_NEAR_LOW_DISK _IOW('J', 17, u_int32_t) +#define APFSIOC_GET_NEAR_LOW_DISK _IOR('J', 18, u_int32_t) + +#ifndef FSIOC_FIOSEEKHOLE +#define FSIOC_FIOSEEKHOLE _IOWR('A', 16, off_t) +#define FSCTL_FIOSEEKHOLE IOCBASECMD(FSIOC_FIOSEEKHOLE) +#endif +#ifndef FSIOC_FIOSEEKDATA +#define FSIOC_FIOSEEKDATA _IOWR('A', 17, off_t) +#define FSCTL_FIOSEEKDATA IOCBASECMD(FSIOC_FIOSEEKDATA) +#endif + +// END of definitions + +#endif diff --git a/include/os/macos/zfs/sys/kstat_osx.h b/include/os/macos/zfs/sys/kstat_osx.h new file mode 100644 index 000000000000..4683280a2e84 --- /dev/null +++ b/include/os/macos/zfs/sys/kstat_osx.h @@ -0,0 +1,385 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2014, 2016 Jorgen Lundman + */ + +#ifndef KSTAT_OSX_INCLUDED +#define KSTAT_OSX_INCLUDED + +typedef struct osx_kstat { + kstat_named_t spa_version; + kstat_named_t zpl_version; + + kstat_named_t darwin_active_vnodes; + kstat_named_t darwin_debug; + kstat_named_t darwin_reclaim_nodes; + kstat_named_t darwin_ignore_negatives; + kstat_named_t darwin_ignore_positives; + kstat_named_t darwin_create_negatives; + kstat_named_t darwin_force_formd_normalized; + kstat_named_t darwin_skip_unlinked_drain; + kstat_named_t darwin_use_system_sync; + + kstat_named_t arc_zfs_arc_max; + kstat_named_t arc_zfs_arc_min; + kstat_named_t arc_zfs_arc_meta_limit; + kstat_named_t arc_zfs_arc_meta_min; + kstat_named_t arc_zfs_arc_grow_retry; + kstat_named_t arc_zfs_arc_shrink_shift; + kstat_named_t arc_zfs_arc_p_min_shift; + kstat_named_t arc_zfs_arc_average_blocksize; + + kstat_named_t l2arc_write_max; + kstat_named_t l2arc_write_boost; + kstat_named_t l2arc_headroom; + kstat_named_t l2arc_headroom_boost; + kstat_named_t l2arc_feed_secs; + kstat_named_t l2arc_feed_min_ms; + + kstat_named_t zfs_vdev_max_active; + kstat_named_t zfs_vdev_sync_read_min_active; + kstat_named_t zfs_vdev_sync_read_max_active; + kstat_named_t zfs_vdev_sync_write_min_active; + kstat_named_t zfs_vdev_sync_write_max_active; + kstat_named_t zfs_vdev_async_read_min_active; + kstat_named_t zfs_vdev_async_read_max_active; + kstat_named_t zfs_vdev_async_write_min_active; + kstat_named_t zfs_vdev_async_write_max_active; + kstat_named_t zfs_vdev_scrub_min_active; + kstat_named_t zfs_vdev_scrub_max_active; + kstat_named_t zfs_vdev_async_write_active_min_dirty_percent; + kstat_named_t zfs_vdev_async_write_active_max_dirty_percent; + kstat_named_t zfs_vdev_aggregation_limit; + kstat_named_t zfs_vdev_read_gap_limit; + kstat_named_t zfs_vdev_write_gap_limit; + + kstat_named_t zfs_arc_lotsfree_percent; + kstat_named_t zfs_arc_sys_free; + kstat_named_t zfs_dirty_data_max; + kstat_named_t zfs_delay_max_ns; + kstat_named_t zfs_delay_min_dirty_percent; + kstat_named_t zfs_delay_scale; + kstat_named_t spa_asize_inflation; + kstat_named_t zfs_prefetch_disable; + kstat_named_t zfetch_max_streams; + kstat_named_t zfetch_min_sec_reap; + kstat_named_t zfetch_array_rd_sz; + kstat_named_t zfs_default_bs; + kstat_named_t zfs_default_ibs; + kstat_named_t metaslab_aliquot; + kstat_named_t spa_max_replication_override; + kstat_named_t spa_mode_global; + kstat_named_t zfs_flags; + kstat_named_t zfs_txg_timeout; + kstat_named_t zfs_vdev_cache_max; + kstat_named_t zfs_vdev_cache_size; + kstat_named_t zfs_vdev_cache_bshift; + kstat_named_t vdev_mirror_shift; + kstat_named_t zfs_scrub_limit; + kstat_named_t zfs_no_scrub_io; + kstat_named_t zfs_no_scrub_prefetch; + kstat_named_t fzap_default_block_shift; + kstat_named_t zfs_immediate_write_sz; +// kstat_named_t zfs_read_chunk_size; + kstat_named_t zfs_nocacheflush; + kstat_named_t zil_replay_disable; + kstat_named_t metaslab_df_alloc_threshold; + kstat_named_t metaslab_df_free_pct; + kstat_named_t zio_injection_enabled; + kstat_named_t zvol_immediate_write_sz; + + kstat_named_t l2arc_noprefetch; + kstat_named_t l2arc_feed_again; + kstat_named_t l2arc_norw; + + kstat_named_t zfs_recover; + + kstat_named_t zfs_free_bpobj_enabled; + + kstat_named_t zfs_send_corrupt_data; + kstat_named_t zfs_send_queue_length; + kstat_named_t zfs_recv_queue_length; + + kstat_named_t zvol_inhibit_dev; + kstat_named_t zfs_send_set_freerecords_bit; + + kstat_named_t zfs_write_implies_delete_child; + kstat_named_t zfs_send_holes_without_birth_time; + + kstat_named_t dbuf_cache_max_bytes; + + kstat_named_t zfs_vdev_queue_depth_pct; + kstat_named_t zio_dva_throttle_enabled; + + kstat_named_t zfs_lua_max_instrlimit; + kstat_named_t zfs_lua_max_memlimit; + + kstat_named_t zfs_trim_extent_bytes_max; + kstat_named_t zfs_trim_extent_bytes_min; + kstat_named_t zfs_trim_metaslab_skip; + kstat_named_t zfs_trim_txg_batch; + kstat_named_t zfs_trim_queue_limit; + + kstat_named_t zfs_send_unmodified_spill_blocks; + kstat_named_t zfs_special_class_metadata_reserve_pct; + + kstat_named_t zfs_vdev_raidz_impl; + kstat_named_t icp_gcm_impl; + kstat_named_t icp_aes_impl; + kstat_named_t zfs_fletcher_4_impl; + + kstat_named_t zfs_expire_snapshot; + kstat_named_t zfs_admin_snapshot; + kstat_named_t zfs_auto_snapshot; + + kstat_named_t zfs_spa_discard_memory_limit; + kstat_named_t zfs_async_block_max_blocks; + kstat_named_t zfs_initialize_chunk_size; + kstat_named_t zfs_scan_suspend_progress; + kstat_named_t zfs_removal_suspend_progress; + kstat_named_t zfs_livelist_max_entries; + + kstat_named_t zfs_allow_redacted_dataset_mount; + kstat_named_t zfs_checksum_events_per_second; + kstat_named_t zfs_commit_timeout_pct; + kstat_named_t zfs_compressed_arc_enabled; + kstat_named_t zfs_condense_indirect_commit_entry_delay_ms; + kstat_named_t zfs_condense_min_mapping_bytes; + kstat_named_t zfs_deadman_checktime_ms; + kstat_named_t zfs_deadman_failmode; + kstat_named_t zfs_deadman_synctime_ms; + kstat_named_t zfs_deadman_ziotime_ms; + kstat_named_t zfs_disable_ivset_guid_check; + kstat_named_t zfs_initialize_value; + kstat_named_t zfs_keep_log_spacemaps_at_export; + kstat_named_t l2arc_rebuild_blocks_min_l2size; + kstat_named_t l2arc_rebuild_enabled; + kstat_named_t l2arc_trim_ahead; + kstat_named_t zfs_livelist_condense_new_alloc; + kstat_named_t zfs_livelist_condense_sync_cancel; + kstat_named_t zfs_livelist_condense_sync_pause; + kstat_named_t zfs_livelist_condense_zthr_cancel; + kstat_named_t zfs_livelist_condense_zthr_pause; + kstat_named_t zfs_livelist_min_percent_shared; + kstat_named_t zfs_max_dataset_nesting; + kstat_named_t zfs_max_missing_tvds; + kstat_named_t metaslab_debug_load; + kstat_named_t metaslab_force_ganging; + kstat_named_t zfs_multihost_fail_intervals; + kstat_named_t zfs_multihost_import_intervals; + kstat_named_t zfs_multihost_interval; + kstat_named_t zfs_override_estimate_recordsize; + kstat_named_t zfs_remove_max_segment; + kstat_named_t zfs_resilver_min_time_ms; + kstat_named_t zfs_scan_legacy; + kstat_named_t zfs_scan_vdev_limit; + kstat_named_t zfs_slow_io_events_per_second; + kstat_named_t spa_load_verify_data; + kstat_named_t spa_load_verify_metadata; + kstat_named_t zfs_unlink_suspend_progress; + kstat_named_t zfs_vdev_min_ms_count; + kstat_named_t vdev_validate_skip; + kstat_named_t zfs_zevent_len_max; + kstat_named_t zio_slow_io_ms; + kstat_named_t l2arc_mfuonly; + kstat_named_t zfs_multihost_history; + kstat_named_t zfs_rebuild_scrub_enabled; + kstat_named_t zfs_txg_history; + kstat_named_t vdev_file_physical_ashift; + kstat_named_t zvol_volmode; + kstat_named_t zfs_zevent_retain_max; +} osx_kstat_t; + +extern unsigned int zfs_vnop_ignore_negatives; +extern unsigned int zfs_vnop_ignore_positives; +extern unsigned int zfs_vnop_create_negatives; +extern unsigned int zfs_vnop_skip_unlinked_drain; +extern uint64_t zfs_vfs_sync_paranoia; +extern uint64_t vnop_num_vnodes; +extern uint64_t vnop_num_reclaims; + +extern unsigned long zfs_arc_max; +extern unsigned long zfs_arc_min; +extern unsigned long zfs_arc_meta_limit; +extern uint64_t zfs_arc_meta_min; +extern int zfs_arc_grow_retry; +extern int zfs_arc_shrink_shift; +extern int zfs_arc_p_min_shift; +extern int zfs_arc_average_blocksize; + +extern uint64_t l2arc_write_max; +extern uint64_t l2arc_write_boost; +extern uint64_t l2arc_headroom; +extern uint64_t l2arc_headroom_boost; +extern uint64_t l2arc_feed_secs; +extern uint64_t l2arc_feed_min_ms; + +extern uint32_t zfs_vdev_max_active; +extern uint32_t zfs_vdev_sync_read_min_active; +extern uint32_t zfs_vdev_sync_read_max_active; +extern uint32_t zfs_vdev_sync_write_min_active; +extern uint32_t zfs_vdev_sync_write_max_active; +extern uint32_t zfs_vdev_async_read_min_active; +extern uint32_t zfs_vdev_async_read_max_active; +extern uint32_t zfs_vdev_async_write_min_active; +extern uint32_t zfs_vdev_async_write_max_active; +extern uint32_t zfs_vdev_scrub_min_active; +extern uint32_t zfs_vdev_scrub_max_active; +extern int zfs_vdev_async_write_active_min_dirty_percent; +extern int zfs_vdev_async_write_active_max_dirty_percent; +extern int zfs_vdev_aggregation_limit; +extern int zfs_vdev_read_gap_limit; +extern int zfs_vdev_write_gap_limit; + +extern uint_t arc_reduce_dnlc_percent; +extern int zfs_arc_lotsfree_percent; +extern uint64_t zfs_arc_sys_free; +extern hrtime_t zfs_delay_max_ns; +extern int spa_asize_inflation; +extern unsigned int zfetch_max_streams; +extern unsigned int zfetch_min_sec_reap; +extern int zfs_default_bs; +extern int zfs_default_ibs; +extern uint64_t metaslab_aliquot; +extern int zfs_vdev_cache_max; +extern int spa_max_replication_override; +extern int zfs_no_scrub_io; +extern int zfs_no_scrub_prefetch; +extern ssize_t zfs_immediate_write_sz; +extern offset_t zfs_read_chunk_size; +extern uint64_t metaslab_df_alloc_threshold; +extern int metaslab_df_free_pct; +extern ssize_t zvol_immediate_write_sz; + +extern boolean_t l2arc_noprefetch; +extern boolean_t l2arc_feed_again; +extern boolean_t l2arc_norw; + +extern int zfs_top_maxinflight; +extern int zfs_resilver_delay; +extern int zfs_scrub_delay; +extern int zfs_scan_idle; + +extern int64_t zfs_free_bpobj_enabled; + +extern int zfs_send_corrupt_data; +extern int zfs_send_queue_length; +extern int zfs_recv_queue_length; + +extern uint64_t zvol_inhibit_dev; +extern int zfs_send_set_freerecords_bit; + +extern uint64_t zfs_write_implies_delete_child; +extern uint32_t send_holes_without_birth_time; +extern uint64_t zfs_send_holes_without_birth_time; + +extern uint64_t dbuf_cache_max_bytes; + +extern int zfs_vdev_queue_depth_pct; +extern boolean_t zio_dva_throttle_enabled; + +extern uint64_t zfs_lua_max_instrlimit; +extern uint64_t zfs_lua_max_memlimit; + + +extern uint64_t zfs_trim_extent_bytes_max; +extern uint64_t zfs_trim_extent_bytes_min; +extern unsigned int zfs_trim_metaslab_skip; +extern uint64_t zfs_trim_txg_batch; +extern uint64_t zfs_trim_queue_limit; + +extern int zfs_send_unmodified_spill_blocks; +extern int zfs_special_class_metadata_reserve_pct; + +extern int zfs_vnop_force_formd_normalized_output; + +extern int zfs_arc_min_prefetch_ms; +extern int zfs_arc_min_prescient_prefetch_ms; + +extern int zfs_expire_snapshot; +extern int zfs_admin_snapshot; +extern int zfs_auto_snapshot; + +extern unsigned long zfs_spa_discard_memory_limit; +extern unsigned long zfs_async_block_max_blocks; +extern unsigned long zfs_initialize_chunk_size; +extern int zfs_scan_suspend_progress; +extern int zfs_removal_suspend_progress; +extern unsigned long zfs_livelist_max_entries; + +extern int zfs_allow_redacted_dataset_mount; +extern unsigned int zfs_checksum_events_per_second; +extern int zfs_commit_timeout_pct; +extern int zfs_compressed_arc_enabled; +extern int zfs_condense_indirect_commit_entry_delay_ms; +extern unsigned long zfs_condense_min_mapping_bytes; +extern unsigned long zfs_deadman_checktime_ms; +extern char *zfs_deadman_failmode; +extern unsigned long zfs_deadman_synctime_ms; +extern unsigned long zfs_deadman_ziotime_ms; +extern int zfs_disable_ivset_guid_check; +extern unsigned long zfs_initialize_value; +extern int zfs_keep_log_spacemaps_at_export; +extern unsigned long l2arc_rebuild_blocks_min_l2size; +extern int l2arc_rebuild_enabled; +extern unsigned long l2arc_trim_ahead; +extern int zfs_livelist_condense_new_alloc; +extern int zfs_livelist_condense_sync_cancel; +extern int zfs_livelist_condense_sync_pause; +extern int zfs_livelist_condense_zthr_cancel; +extern int zfs_livelist_condense_zthr_pause; +extern int zfs_livelist_min_percent_shared; +extern int zfs_max_dataset_nesting; +extern unsigned long zfs_max_missing_tvds; +extern int metaslab_debug_load; +extern unsigned long metaslab_force_ganging; +extern unsigned int zfs_multihost_fail_intervals; +extern unsigned int zfs_multihost_import_intervals; +extern unsigned long zfs_multihost_interval; +extern int zfs_override_estimate_recordsize; +extern int zfs_remove_max_segment; +extern int zfs_resilver_min_time_ms; +extern int zfs_scan_legacy; +extern unsigned long zfs_scan_vdev_limit; +extern unsigned int zfs_slow_io_events_per_second; +extern int spa_load_verify_data; +extern int spa_load_verify_metadata; +extern int zfs_unlink_suspend_progress; +extern int zfs_vdev_min_ms_count; +extern int vdev_validate_skip; +extern int zfs_zevent_len_max; +extern int zio_slow_io_ms; +extern int l2arc_mfuonly; +extern int zfs_multihost_history; +extern int zfs_rebuild_scrub_enabled; +extern int zfs_txg_history; +extern unsigned long vdev_file_physical_ashift; +extern unsigned int zvol_volmode; +extern unsigned int zfs_zevent_retain_max; + +int kstat_osx_init(void); +void kstat_osx_fini(void); + +int arc_kstat_update(kstat_t *ksp, int rw); +int arc_kstat_update_osx(kstat_t *ksp, int rw); + +#endif diff --git a/include/os/macos/zfs/sys/ldi_buf.h b/include/os/macos/zfs/sys/ldi_buf.h new file mode 100644 index 000000000000..9b69b0610a92 --- /dev/null +++ b/include/os/macos/zfs/sys/ldi_buf.h @@ -0,0 +1,77 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2015, Evan Susarret. All rights reserved. + * + * OS X implementation of ldi_ named functions for ZFS written by + * Evan Susarret in 2015. + */ + +#ifndef _SYS_LDI_BUF_H +#define _SYS_LDI_BUF_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Buffer context for LDI strategy + */ +typedef struct ldi_buf { + /* For client use */ + int (*b_iodone)(struct ldi_buf *); /* Callback */ + union { + void *b_addr; /* Passed buffer address */ + } b_un; /* Union to match illumos */ + uint64_t b_bcount; /* Size of IO */ + uint64_t b_bufsize; /* Size of buffer */ + uint64_t b_lblkno; /* logical block number */ + uint64_t b_resid; /* Remaining IO size */ + int b_flags; /* Read or write, options */ + int b_error; /* IO error code */ + uint64_t pad; /* Pad to 64 bytes */ +} ldi_buf_t; /* XXX Currently 64b */ + +ldi_buf_t *ldi_getrbuf(int); +void ldi_freerbuf(ldi_buf_t *); +void ldi_bioinit(ldi_buf_t *); + +/* Define macros to get and release a buffer */ +#define getrbuf(flags) ldi_getrbuf(flags) +#define freerbuf(lbp) ldi_freerbuf(lbp) +#define bioinit(lbp) ldi_bioinit(lbp) +#define geterror(lbp) (lbp->b_error) +#define biowait(lbp) (0) + +#define lbtodb(bytes) \ + (bytes >> DEV_BSHIFT) +#define dbtolb(blkno) \ + (blkno << DEV_BSHIFT) +#define ldbtob(blkno) dbtolb(blkno) + +/* Redefine B_BUSY */ +#define B_BUSY B_PHYS + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _SYS_LDI_BUF_H */ diff --git a/include/os/macos/zfs/sys/ldi_impl_osx.h b/include/os/macos/zfs/sys/ldi_impl_osx.h new file mode 100644 index 000000000000..68c8d121ab15 --- /dev/null +++ b/include/os/macos/zfs/sys/ldi_impl_osx.h @@ -0,0 +1,226 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ +/* + * Copyright (c) 2015, Evan Susarret. All rights reserved. + */ +/* + * Portions of this document are copyright Oracle and Joyent. + * OS X implementation of ldi_ named functions for ZFS written by + * Evan Susarret in 2015. + */ + +#ifndef _SYS_LDI_IMPL_OSX_H +#define _SYS_LDI_IMPL_OSX_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * OS X + */ +#define LDI_TYPE_INVALID 0x0 /* uninitialized */ +#define LDI_TYPE_IOKIT 0x1 /* IOMedia device */ +#define LDI_TYPE_VNODE 0x2 /* vnode (bdev) device */ + +/* + * OS X + */ +#define LDI_STATUS_OFFLINE 0x0 /* device offline (dead-end) */ +#define LDI_STATUS_CLOSED 0x1 /* just initialized or closed */ +#define LDI_STATUS_CLOSING 0x2 /* close in-progress */ +#define LDI_STATUS_OPENING 0x3 /* open in-progress */ +#define LDI_STATUS_ONLINE 0x4 /* device is open and active */ +typedef uint_t ldi_status_t; + +/* + * LDI hash definitions + */ +#define LH_HASH_SZ 32 /* number of hash lists */ + +/* + * Flag for LDI handle's lh_flags field + */ +#define LH_FLAGS_NOTIFY 0x0001 /* invoked in context of a notify */ + + +/* + * LDI handle (OS X) + */ +typedef struct _handle_iokit *handle_iokit_t; +typedef struct _handle_vnode *handle_vnode_t; +typedef struct _handle_notifier *handle_notifier_t; + +struct ldi_handle { + /* protected by ldi_handle_hash_lock */ + list_node_t lh_node; /* list membership */ + uint_t lh_ref; /* active references */ + uint_t lh_flags; /* for notify event */ + + /* protected by handle lh_lock */ + kmutex_t lh_lock; /* internal lock */ + kcondvar_t lh_cv; /* for concurrent open */ + ldi_status_t lh_status; /* Closed, Offline, Online */ + uint_t lh_openref; /* open client count */ + + /* unique/static fields in the handle */ + union ldi_handle_tsd { + handle_iokit_t iokit_tsd; + handle_vnode_t vnode_tsd; + } lh_tsd; /* union */ + handle_notifier_t lh_notifier; /* pointer */ + uint_t lh_type; /* IOKit or vnode */ + uint_t lh_fmode; /* FREAD | FWRITE */ + dev_t lh_dev; /* device number */ + uint_t pad; /* pad to 96 bytes */ +}; /* XXX Currently 96b */ + +/* Shared functions */ +struct ldi_handle *handle_alloc_common(uint_t, dev_t, int); +struct ldi_handle *handle_find(dev_t, int, boolean_t); +struct ldi_handle *handle_add(struct ldi_handle *); +int handle_status_change(struct ldi_handle *, int); +void handle_hold(struct ldi_handle *); +void handle_release(struct ldi_handle *); +ldi_status_t handle_open_start(struct ldi_handle *); +void handle_open_done(struct ldi_handle *, ldi_status_t); + +/* Handle IOKit functions */ +void handle_free_iokit(struct ldi_handle *); +struct ldi_handle *handle_alloc_iokit(dev_t, int); +int handle_register_notifier(struct ldi_handle *); +int handle_close_iokit(struct ldi_handle *); +int handle_free_ioservice(struct ldi_handle *); +int handle_alloc_ioservice(struct ldi_handle *); +int handle_remove_notifier(struct ldi_handle *); +int handle_set_wce_iokit(struct ldi_handle *, int *); +int handle_get_size_iokit(struct ldi_handle *, uint64_t *); +int handle_get_dev_path_iokit(struct ldi_handle *lh, + char *path, int len); +int handle_get_media_info_iokit(struct ldi_handle *, + struct dk_minfo *); +int handle_get_media_info_ext_iokit(struct ldi_handle *, + struct dk_minfo_ext *); +int handle_check_media_iokit(struct ldi_handle *, int *); +int handle_is_solidstate_iokit(struct ldi_handle *, int *); +int handle_sync_iokit(struct ldi_handle *); +int buf_strategy_iokit(ldi_buf_t *, struct ldi_handle *); +int ldi_open_media_by_dev(dev_t, int, ldi_handle_t *); +int ldi_open_media_by_path(char *, int, ldi_handle_t *); +int handle_get_bootinfo_iokit(struct ldi_handle *, + struct io_bootinfo *); +int handle_features_iokit(struct ldi_handle *, + uint32_t *); +int handle_unmap_iokit(struct ldi_handle *, + dkioc_free_list_ext_t *); + +/* Handle vnode functions */ +dev_t dev_from_path(char *); +void handle_free_vnode(struct ldi_handle *); +struct ldi_handle *handle_alloc_vnode(dev_t, int); +int handle_close_vnode(struct ldi_handle *); +int handle_get_size_vnode(struct ldi_handle *, uint64_t *); +int handle_get_dev_path_vnode(struct ldi_handle *lh, + char *path, int len); +int handle_get_media_info_vnode(struct ldi_handle *, + struct dk_minfo *); +int handle_get_media_info_ext_vnode(struct ldi_handle *, + struct dk_minfo_ext *); +int handle_check_media_vnode(struct ldi_handle *, int *); +int handle_is_solidstate_vnode(struct ldi_handle *, int *); +int handle_sync_vnode(struct ldi_handle *); +int buf_strategy_vnode(ldi_buf_t *, struct ldi_handle *); +int ldi_open_vnode_by_path(char *, dev_t, int, ldi_handle_t *); +int handle_get_bootinfo_vnode(struct ldi_handle *, + struct io_bootinfo *); +int handle_features_vnode(struct ldi_handle *, + uint32_t *); +int handle_unmap_vnode(struct ldi_handle *, + dkioc_free_list_ext_t *); + +/* + * LDI event information + */ +typedef struct ldi_ev_callback_impl { + struct ldi_handle *lec_lhp; +#ifdef illumos + dev_info_t *lec_dip; +#endif + dev_t lec_dev; + int lec_spec; + int (*lec_notify)(ldi_handle_t, ldi_ev_cookie_t, void *, void *); + void (*lec_finalize)(ldi_handle_t, ldi_ev_cookie_t, int, + void *, void *); + void *lec_arg; + void *lec_cookie; + void *lec_id; + list_node_t lec_list; +} ldi_ev_callback_impl_t; /* XXX Currently 72b */ + +/* + * Members of "struct ldi_ev_callback_list" are protected by their le_lock + * member. The struct is currently only used once, as a file-level global, + * and the locking protocol is currently implemented in ldi_ev_lock() and + * ldi_ev_unlock(). + * + * When delivering events to subscribers, ldi_invoke_notify() and + * ldi_invoke_finalize() will walk the list of callbacks: le_head. It is + * possible that an invoked callback function will need to unregister an + * arbitrary number of callbacks from this list. + * + * To enable ldi_ev_remove_callbacks() to remove elements from the list + * without breaking the walk-in-progress, we store the next element in the + * walk direction on the struct as le_walker_next and le_walker_prev. + */ +struct ldi_ev_callback_list { + kmutex_t le_lock; + kcondvar_t le_cv; + uint64_t le_busy; + void *le_thread; + list_t le_head; + ldi_ev_callback_impl_t *le_walker_next; + ldi_ev_callback_impl_t *le_walker_prev; +}; /* XXX Currently 96b, but only used once */ + +int ldi_invoke_notify(dev_info_t *, dev_t, int, char *, void *); +void ldi_invoke_finalize(dev_info_t *, dev_t, int, char *, int, void *); +int e_ddi_offline_notify(dev_info_t *); +void e_ddi_offline_finalize(dev_info_t *, int); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _SYS_LDI_IMPL_OSX_H */ diff --git a/include/os/macos/zfs/sys/ldi_osx.h b/include/os/macos/zfs/sys/ldi_osx.h new file mode 100644 index 000000000000..2d78017c4245 --- /dev/null +++ b/include/os/macos/zfs/sys/ldi_osx.h @@ -0,0 +1,153 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ +/* + * Copyright (c) 2015, Evan Susarret. All rights reserved. + */ +/* + * Portions of this document are copyright Oracle and Joyent. + * OS X implementation of ldi_ named functions for ZFS written by + * Evan Susarret in 2015. + */ + +#ifndef _SYS_LDI_OSX_H +#define _SYS_LDI_OSX_H + +#include + +/* + * OS X - The initialization/destructor functions are available + * for zfs-osx.cpp to call during zfs_init/zfs_fini. + */ +#ifdef __cplusplus +extern "C" { + +int ldi_init(void *); /* passes IOService provider */ +void ldi_fini(); /* teardown */ +#endif /* __cplusplus */ + +/* + * Opaque layered driver data structures. + * vdev_disk and other C callers may use these LDI interfaces + * ldi_ident_t is already defined as typedef void* by spl sunddi.h + */ +typedef struct __ldi_handle *ldi_handle_t; +typedef struct __ldi_callback_id *ldi_callback_id_t; +typedef struct __ldi_ev_cookie *ldi_ev_cookie_t; + +/* + * LDI event interface related + */ +#define LDI_EV_SUCCESS 0 +#define LDI_EV_FAILURE (-1) +#define LDI_EV_NONE (-2) /* no matching callbacks registered */ +#define LDI_EV_OFFLINE "LDI:EVENT:OFFLINE" +#define LDI_EV_DEGRADE "LDI:EVENT:DEGRADE" +#define LDI_EV_DEVICE_REMOVE "LDI:EVENT:DEVICE_REMOVE" + +#define LDI_EV_CB_VERS_1 1 +#define LDI_EV_CB_VERS LDI_EV_CB_VERS_1 + +typedef struct ldi_ev_callback { + uint_t cb_vers; + int (*cb_notify)(ldi_handle_t, ldi_ev_cookie_t, void *, void *); + void (*cb_finalize)(ldi_handle_t, ldi_ev_cookie_t, int, + void *, void *); +} ldi_ev_callback_t; + +/* Structs passed to media_get_info */ +struct dk_minfo { + uint32_t dki_capacity; /* Logical block count */ + uint32_t dki_lbsize; /* Logical block size */ +}; /* (8b) */ + +struct dk_minfo_ext { + uint64_t dki_capacity; /* Logical block count */ + uint32_t dki_lbsize; /* Logical block size */ + uint32_t dki_pbsize; /* Physical block size */ +}; /* (16b) */ + +struct io_bootinfo { + char dev_path[MAXPATHLEN]; /* IODeviceTree path */ + uint64_t dev_size; /* IOMedia device size */ +}; + +/* + * XXX This struct is defined in spl but was unused until now. + * There is a reference in zvol.c zvol_ioctl, commented out. + */ +#if 0 +struct dk_callback { + void (*dkc_callback)(void *dkc_cookie, int error); + void *dkc_cookie; + int dkc_flag; +}; /* XXX Currently 20b */ +#endif + +/* XXX Already defined in spl dkio.h (used elsewhere) */ +#if 0 +#define DKIOCFLUSHWRITECACHE (DKIOC | 34) +#endif + +#define FLUSH_VOLATILE 0x1 +#define DKIOCGMEDIAINFOEXT (DKIOC | 48) + +/* XXX Created this additional ioctl */ +#define DKIOCGETBOOTINFO (DKIOC | 99) + +/* + * LDI Handle manipulation functions + */ +int ldi_open_by_dev(dev_t, int, int, cred_t *, + ldi_handle_t *, __unused ldi_ident_t); +int ldi_open_by_name(char *, int, cred_t *, + ldi_handle_t *, __unused ldi_ident_t); + +int ldi_close(ldi_handle_t, int, cred_t *); + +int ldi_sync(ldi_handle_t); +int ldi_get_size(ldi_handle_t, uint64_t *); +int ldi_ioctl(ldi_handle_t, int, intptr_t, int, cred_t *, int *); +int ldi_strategy(ldi_handle_t, ldi_buf_t *); + +/* + * LDI events related declarations + */ +extern int ldi_ev_get_cookie(ldi_handle_t, char *, ldi_ev_cookie_t *); +extern char *ldi_ev_get_type(ldi_ev_cookie_t); +extern int ldi_ev_register_callbacks(ldi_handle_t, ldi_ev_cookie_t, + ldi_ev_callback_t *, void *, ldi_callback_id_t *); +extern int ldi_ev_remove_callbacks(ldi_callback_id_t); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _SYS_LDI_OSX_H */ diff --git a/include/os/macos/zfs/sys/trace_zfs.h b/include/os/macos/zfs/sys/trace_zfs.h new file mode 100644 index 000000000000..f32ba529ecd1 --- /dev/null +++ b/include/os/macos/zfs/sys/trace_zfs.h @@ -0,0 +1,68 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#if defined(_KERNEL) && defined(HAVE_DECLARE_EVENT_CLASS) + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM zfs + +#if !defined(_TRACE_ZFS_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_ZFS_H + +#include +#include + +/* + * The sys/trace_dbgmsg.h header defines tracepoint events for + * dprintf(), dbgmsg(), and SET_ERROR(). + */ +#define _SYS_TRACE_DBGMSG_INDIRECT +#include +#undef _SYS_TRACE_DBGMSG_INDIRECT + +/* + * Redefine the DTRACE_PROBE* functions to use Linux tracepoints + */ +#undef DTRACE_PROBE1 +#define DTRACE_PROBE1(name, t1, arg1) \ + trace_zfs_##name((arg1)) + +#undef DTRACE_PROBE2 +#define DTRACE_PROBE2(name, t1, arg1, t2, arg2) \ + trace_zfs_##name((arg1), (arg2)) + +#undef DTRACE_PROBE3 +#define DTRACE_PROBE3(name, t1, arg1, t2, arg2, t3, arg3) \ + trace_zfs_##name((arg1), (arg2), (arg3)) + +#undef DTRACE_PROBE4 +#define DTRACE_PROBE4(name, t1, arg1, t2, arg2, t3, arg3, t4, arg4) \ + trace_zfs_##name((arg1), (arg2), (arg3), (arg4)) + +#endif /* _TRACE_ZFS_H */ + +#undef TRACE_INCLUDE_PATH +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_PATH sys +#define TRACE_INCLUDE_FILE trace +#include + +#endif /* _KERNEL && HAVE_DECLARE_EVENT_CLASS */ diff --git a/include/os/macos/zfs/sys/vdev_disk_os.h b/include/os/macos/zfs/sys/vdev_disk_os.h new file mode 100644 index 000000000000..79b68c7ee6ba --- /dev/null +++ b/include/os/macos/zfs/sys/vdev_disk_os.h @@ -0,0 +1,44 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _ZFS_VDEV_DISK_OS_H +#define _ZFS_VDEV_DISK_OS_H + +#include + +typedef struct vdev_disk { + ldi_handle_t vd_lh; + list_t vd_ldi_cbs; + boolean_t vd_ldi_offline; +} vdev_disk_t; + +/* + * The vdev_buf_t is used to translate between zio_t and buf_t, and back again. + */ +typedef struct vdev_buf { + ldi_buf_t vb_buf; /* buffer that describes the io */ + zio_t *vb_io; /* pointer back to the original zio_t */ +} vdev_buf_t; + + +extern int vdev_disk_ldi_physio(ldi_handle_t, caddr_t, size_t, uint64_t, int); + +#endif diff --git a/include/os/macos/zfs/sys/zfs_boot.h b/include/os/macos/zfs/sys/zfs_boot.h new file mode 100644 index 000000000000..cad5c0bdfd90 --- /dev/null +++ b/include/os/macos/zfs/sys/zfs_boot.h @@ -0,0 +1,53 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2016, Evan Susarret. All rights reserved. + */ + +#ifndef ZFS_BOOT_H_INCLUDED +#define ZFS_BOOT_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Link data vdevs to virtual devices */ +int zfs_boot_update_bootinfo(spa_t *spa); + +int zfs_attach_devicedisk(zfsvfs_t *zfsvfs); +int zfs_detach_devicedisk(zfsvfs_t *zfsvfs); +int zfs_devdisk_get_path(void *, char *, int); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + + + +#ifdef __cplusplus +#include +bool zfs_boot_init(IOService *); +void zfs_boot_fini(); +#endif /* __cplusplus */ + + +#endif /* ZFS_BOOT_H_INCLUDED */ diff --git a/include/os/macos/zfs/sys/zfs_bootenv_os.h b/include/os/macos/zfs/sys/zfs_bootenv_os.h new file mode 100644 index 000000000000..77b8b8ecd9ea --- /dev/null +++ b/include/os/macos/zfs/sys/zfs_bootenv_os.h @@ -0,0 +1,25 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +#ifndef _ZFS_BOOTENV_OS_H +#define _ZFS_BOOTENV_OS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define BOOTENV_OS BE_MACOS_VENDOR + +#ifdef __cplusplus +} +#endif + +#endif /* _ZFS_BOOTENV_OS_H */ diff --git a/include/os/macos/zfs/sys/zfs_context_os.h b/include/os/macos/zfs/sys/zfs_context_os.h new file mode 100644 index 000000000000..a88f5dae9e89 --- /dev/null +++ b/include/os/macos/zfs/sys/zfs_context_os.h @@ -0,0 +1,193 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _SPL_ZFS_CONTEXT_OS_H +#define _SPL_ZFS_CONTEXT_OS_H + +#include +#include +#include + +#define MSEC_TO_TICK(msec) ((msec) / (MILLISEC / hz)) + +#define KMALLOC_MAX_SIZE MAXPHYS + +#ifndef MAX_UPL_TRANSFER +#define MAX_UPL_TRANSFER 256 +#endif + +#define flock64_t struct flock + +struct spa_iokit; +typedef struct spa_iokit spa_iokit_t; + +#define noinline __attribute__((noinline)) + +/* really? */ +#define kpreempt_disable() ((void)0) +#define kpreempt_enable() ((void)0) +#define cond_resched() (void)thread_block(THREAD_CONTINUE_NULL); +#define schedule() (void)thread_block(THREAD_CONTINUE_NULL); + +#define current curthread + +extern boolean_t ml_set_interrupts_enabled(boolean_t); + +/* Make sure kmem and vmem are already included */ +#include +#include + +/* + * We could add another field to zfs_cmd_t, but since we should be + * moving to the new-style ioctls, send and recv still hang on to old, + * we will just (ab)use a field not used on macOS. + * We use this field to keep userland's file offset pointer, and kernel + * fp_offset in sync, as we have no means to access "fp_offset" in XNU. + */ +#define zc_fd_offset zc_zoneid + +/* Since Linux code uses vmem_free() and we already have one: */ +#define vmem_free(A, B) zfs_kmem_free((A), (B)) +#define vmem_alloc(A, B) zfs_kmem_alloc((A), (B)) +#define vmem_zalloc(A, B) zfs_kmem_zalloc((A), (B)) + +typedef int fstrans_cookie_t; +#define spl_fstrans_mark() (0) +#define spl_fstrans_unmark(x) (x = 0) + +#ifdef _KERNEL + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +struct hlist_head { + struct hlist_node *first; +}; + +typedef struct { + volatile int counter; +} atomic_t; + +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) + +#define barrier() __asm__ __volatile__("": : :"memory") +#define smp_rmb() barrier() + +#define READ_ONCE(x) ( \ +{ \ + __typeof(x) __var = ( \ + { \ + barrier(); \ + ACCESS_ONCE(x); \ + }); \ + barrier(); \ + __var; \ + }) + +#define WRITE_ONCE(x, v) do { \ + barrier(); \ + ACCESS_ONCE(x) = (v); \ + barrier(); \ + } while (0) + +/* BEGIN CSTYLED */ +#define hlist_for_each(p, head) \ + for (p = (head)->first; p; p = (p)->next) + +#define hlist_entry(ptr, type, field) container_of(ptr, type, field) +/* END CSTYLED */ + +static inline void +hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + n->next = h->first; + if (h->first != NULL) + h->first->pprev = &n->next; + WRITE_ONCE(h->first, n); + n->pprev = &h->first; +} + +static inline void +hlist_del(struct hlist_node *n) +{ + WRITE_ONCE(*(n->pprev), n->next); + if (n->next != NULL) + n->next->pprev = n->pprev; +} + + +#define HLIST_HEAD_INIT { } +#define HLIST_HEAD(name) struct hlist_head name = HLIST_HEAD_INIT +#define INIT_HLIST_HEAD(head) (head)->first = NULL + +/* BEGIN CSTYLED */ +#define INIT_HLIST_NODE(node) \ + do { \ + (node)->next = NULL; \ + (node)->pprev = NULL; \ + } while (0) + +/* END CSTYLED */ + +static inline int +atomic_read(const atomic_t *v) +{ + return (READ_ONCE(v->counter)); +} + +static inline int +atomic_inc(atomic_t *v) +{ + return (__sync_fetch_and_add(&v->counter, 1) + 1); +} + +static inline int +atomic_dec(atomic_t *v) +{ + return (__sync_fetch_and_add(&v->counter, -1) - 1); +} + +extern void spl_qsort(void *array, size_t nm, size_t member_size, + int (*cmpf)(const void *, const void *)); +#define qsort spl_qsort + +#define strstr kmem_strstr + +void spa_create_os(void *spa); +void spa_export_os(void *spa); +void spa_activate_os(void *spa); +void spa_deactivate_os(void *spa); + +#define task_io_account_read(n) +#define task_io_account_write(n) + +#ifndef SEEK_HOLE +#define SEEK_HOLE 3 +#endif + +#ifndef SEEK_DATA +#define SEEK_DATA 4 +#endif + +#endif // _KERNEL + +#endif diff --git a/include/os/macos/zfs/sys/zfs_ctldir.h b/include/os/macos/zfs/sys/zfs_ctldir.h new file mode 100644 index 000000000000..4cacf1aefe02 --- /dev/null +++ b/include/os/macos/zfs/sys/zfs_ctldir.h @@ -0,0 +1,124 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (C) 2011 Lawrence Livermore National Security, LLC. + * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). + * LLNL-CODE-403049. + * Rewritten for Linux by: + * Rohan Puri + * Brian Behlendorf + */ + +#ifndef _ZFS_CTLDIR_H +#define _ZFS_CTLDIR_H + +#include +#include +#include +#include + +#define ZFS_CTLDIR_NAME ".zfs" +#define ZFS_SNAPDIR_NAME "snapshot" +#define ZFS_SHAREDIR_NAME "shares" + +#define zfs_has_ctldir(zdp) \ + ((zdp)->z_id == ZTOZSB(zdp)->z_root && \ + (ZTOZSB(zdp)->z_ctldir != NULL)) +#define zfs_show_ctldir(zdp) \ + (zfs_has_ctldir(zdp) && \ + (ZTOZSB(zdp)->z_show_ctldir)) + +struct path; + +extern int zfs_expire_snapshot; + +/* zfsctl generic functions */ +extern int zfsctl_create(zfsvfs_t *); +extern void zfsctl_destroy(zfsvfs_t *); +extern struct vnode *zfsctl_root(znode_t *); +extern void zfsctl_init(void); +extern void zfsctl_fini(void); +extern boolean_t zfsctl_is_node(struct vnode *ip); +extern boolean_t zfsctl_is_snapdir(struct vnode *ip); +extern int zfsctl_fid(struct vnode *ip, fid_t *fidp); + +/* zfsctl '.zfs' functions */ +extern int zfsctl_root_lookup(struct vnode *dip, char *name, + struct vnode **ipp, int flags, cred_t *cr, int *direntflags, + struct componentname *realpnp); + +/* zfsctl '.zfs/snapshot' functions */ +extern int zfsctl_snapdir_lookup(struct vnode *dip, char *name, + struct vnode **ipp, int flags, cred_t *cr, int *direntflags, + struct componentname *realpnp); +extern int zfsctl_snapdir_rename(struct vnode *sdip, char *sname, + struct vnode *tdip, char *tname, cred_t *cr, int flags); +extern int zfsctl_snapdir_remove(struct vnode *dip, char *name, cred_t *cr, + int flags); +extern int zfsctl_snapdir_mkdir(struct vnode *dip, char *dirname, vattr_t *vap, + struct vnode **ipp, cred_t *cr, int flags); +extern int zfsctl_snapshot_mount(struct vnode *, int flags); +extern int zfsctl_snapshot_unmount(const char *, int flags); +extern int zfsctl_snapshot_unmount_node(struct vnode *, const char *, + int flags); +extern int zfsctl_snapshot_unmount_delay(spa_t *spa, uint64_t objsetid, + int delay); +extern int zfsctl_snapdir_vget(struct mount *sb, uint64_t objsetid, + int gen, struct vnode **ipp); + +/* zfsctl '.zfs/shares' functions */ +extern int zfsctl_shares_lookup(struct vnode *dip, char *name, + struct vnode **ipp, int flags, cred_t *cr, int *direntflags, + struct componentname *realpnp); + +extern int zfsctl_vnop_lookup(struct vnop_lookup_args *); +extern int zfsctl_vnop_getattr(struct vnop_getattr_args *); +extern int zfsctl_vnop_readdir(struct vnop_readdir_args *); +extern int zfsctl_vnop_mkdir(struct vnop_mkdir_args *); +extern int zfsctl_vnop_rmdir(struct vnop_rmdir_args *); +extern int zfsctl_vnop_access(struct vnop_access_args *); +extern int zfsctl_vnop_open(struct vnop_open_args *); +extern int zfsctl_vnop_close(struct vnop_close_args *); +extern int zfsctl_vnop_inactive(struct vnop_inactive_args *); +extern int zfsctl_vnop_reclaim(struct vnop_reclaim_args *); + +extern void zfs_ereport_snapshot_post(const char *subclass, spa_t *spa, + const char *name); + +extern void zfsctl_mount_signal(char *, boolean_t); + + +/* + * These vnodes numbers are reserved for the .zfs control directory. + * It is important that they be no larger that 48-bits because only + * 6 bytes are reserved in the NFS file handle for the object number. + * However, they should be as large as possible to avoid conflicts + * with the objects which are assigned monotonically by the dmu. + */ +#define ZFSCTL_INO_ROOT 0x0000FFFFFFFFFFFFULL +#define ZFSCTL_INO_SHARES 0x0000FFFFFFFFFFFEULL +#define ZFSCTL_INO_SNAPDIR 0x0000FFFFFFFFFFFDULL +#define ZFSCTL_INO_SNAPDIRS 0x0000FFFFFFFFFFFCULL + +#define ZFSCTL_EXPIRE_SNAPSHOT 300 + +#endif /* _ZFS_CTLDIR_H */ diff --git a/include/os/macos/zfs/sys/zfs_dir.h b/include/os/macos/zfs/sys/zfs_dir.h new file mode 100644 index 000000000000..cfee82308a7d --- /dev/null +++ b/include/os/macos/zfs/sys/zfs_dir.h @@ -0,0 +1,82 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_FS_ZFS_DIR_H +#define _SYS_FS_ZFS_DIR_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* zfs_dirent_lock() flags */ +#define ZNEW 0x0001 /* entry should not exist */ +#define ZEXISTS 0x0002 /* entry should exist */ +#define ZSHARED 0x0004 /* shared access (zfs_dirlook()) */ +#define ZXATTR 0x0008 /* we want the xattr dir */ +#define ZRENAMING 0x0010 /* znode is being renamed */ +#define ZCILOOK 0x0020 /* case-insensitive lookup requested */ +#define ZCIEXACT 0x0040 /* c-i requires c-s match (rename) */ +#define ZHAVELOCK 0x0080 /* z_name_lock is already held */ + +/* mknode flags */ +#define IS_ROOT_NODE 0x01 /* create a root node */ +#define IS_XATTR 0x02 /* create an extended attribute node */ +#define IS_REPLAY 0x04 /* we are replaying intent log */ + +extern int zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, + znode_t **zpp, int flag, int *direntflags, + struct componentname *realpnp); + +extern void zfs_dirent_unlock(zfs_dirlock_t *); +extern int zfs_link_create(zfs_dirlock_t *, znode_t *, dmu_tx_t *, int); +extern int zfs_link_destroy(zfs_dirlock_t *, znode_t *, dmu_tx_t *, int, + boolean_t *); + +extern int zfs_dirlook(znode_t *, char *name, znode_t **, int, + int *deflg, struct componentname *rpnp); + +extern void zfs_mknode(znode_t *dzp, vattr_t *vap, dmu_tx_t *tx, cred_t *cr, + uint_t flag, znode_t **zpp, zfs_acl_ids_t *acl_ids); + +extern void zfs_rmnode(znode_t *); +extern void zfs_dl_name_switch(zfs_dirlock_t *dl, char *new, char **old); +extern boolean_t zfs_dirempty(znode_t *); +extern void zfs_unlinked_add(znode_t *, dmu_tx_t *); +extern void zfs_unlinked_drain(zfsvfs_t *zfsvfs); +extern void zfs_unlinked_drain_stop_wait(zfsvfs_t *zfsvfs); +extern int zfs_sticky_remove_access(znode_t *, znode_t *, cred_t *cr); + +extern int zfs_get_xattrdir(znode_t *, znode_t **, cred_t *, int); +extern int zfs_make_xattrdir(znode_t *, vattr_t *, znode_t **, cred_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_FS_ZFS_DIR_H */ diff --git a/include/os/macos/zfs/sys/zfs_ioctl_compat.h b/include/os/macos/zfs/sys/zfs_ioctl_compat.h new file mode 100644 index 000000000000..ae482e131299 --- /dev/null +++ b/include/os/macos/zfs/sys/zfs_ioctl_compat.h @@ -0,0 +1,215 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2013 Jorgen Lundan . All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_ZFS_IOCTL_COMPAT_H +#define _SYS_ZFS_IOCTL_COMPAT_H + +#include +#include +#include +#include +#include +#include + +#ifdef _KERNEL +#include +#endif /* _KERNEL */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Backwards ioctl compatibility + */ + +/* ioctl versions for vfs.zfs.version.ioctl */ +#define ZFS_IOCVER_UNDEF -1 +#define ZFS_IOCVER_NONE 0 +#define ZFS_IOCVER_1_9_4 1 +#define ZFS_IOCVER_ZOF 15 + +/* compatibility conversion flag */ +#define ZFS_CMD_COMPAT_NONE 0 +#define ZFS_CMD_COMPAT_V15 1 +#define ZFS_CMD_COMPAT_V28 2 + +#define ZFS_IOC_COMPAT_PASS 254 +#define ZFS_IOC_COMPAT_FAIL 255 + +#define ZFS_IOCREQ(ioreq) ((ioreq) & 0xff) + +typedef struct zfs_iocparm { + uint32_t zfs_ioctl_version; + uint64_t zfs_cmd; + uint64_t zfs_cmd_size; + + /* + * ioctl() return codes can not be used to communicate - + * as XNU will skip copyout() if there is an error, so it + * is passed along in this wrapping structure. + */ + int zfs_ioc_error; /* ioctl error value */ +} zfs_iocparm_t; + +typedef struct zfs_cmd_1_9_4 +{ + char zc_name[MAXPATHLEN]; /* name of pool or dataset */ + uint64_t zc_nvlist_src; /* really (char *) */ + uint64_t zc_nvlist_src_size; + uint64_t zc_nvlist_dst; /* really (char *) */ + uint64_t zc_nvlist_dst_size; + boolean_t zc_nvlist_dst_filled; /* put an nvlist in dst? */ + int zc_pad2; + + /* + * The following members are for legacy ioctls which haven't been + * converted to the new method. + */ + uint64_t zc_history; /* really (char *) */ + char zc_value[MAXPATHLEN * 2]; + char zc_string[MAXNAMELEN]; + uint64_t zc_guid; + uint64_t zc_nvlist_conf; /* really (char *) */ + uint64_t zc_nvlist_conf_size; + uint64_t zc_cookie; + uint64_t zc_objset_type; + uint64_t zc_perm_action; + uint64_t zc_history_len; + uint64_t zc_history_offset; + uint64_t zc_obj; + uint64_t zc_iflags; /* internal to zfs(7fs) */ + zfs_share_t zc_share; + dmu_objset_stats_t zc_objset_stats; + struct drr_begin zc_begin_record; + zinject_record_t zc_inject_record; + uint32_t zc_defer_destroy; + uint32_t zc_flags; + uint64_t zc_action_handle; + int zc_cleanup_fd; + uint8_t zc_simple; + uint8_t zc_pad3[3]; + boolean_t zc_resumable; + uint32_t zc_pad4; + uint64_t zc_sendobj; + uint64_t zc_fromobj; + uint64_t zc_createtxg; + zfs_stat_t zc_stat; + int zc_ioc_error; /* ioctl error value */ + uint64_t zc_dev; /* OSX doesn't have ddi_driver_major */ +} zfs_cmd_1_9_4_t; + +// Figure this out +unsigned static long zfs_ioctl_1_9_4[] = +{ + // ZFS_IOC_POOL_CREATE = _IOWR('Z', 0, struct zfs_cmd), + + 0, /* 0 ZFS_IOC_POOL_CREATE */ + 1, /* 1 ZFS_IOC_POOL_DESTROY */ + 2, /* 2 ZFS_IOC_POOL_IMPORT */ + 3, /* 3 ZFS_IOC_POOL_EXPORT */ + 4, /* 4 ZFS_IOC_POOL_CONFIGS */ + 5, /* 5 ZFS_IOC_POOL_STATS */ + 6, /* 6 ZFS_IOC_POOL_TRYIMPORT */ + 7, /* 7 ZFS_IOC_POOL_SCRUB */ + 8, /* 8 ZFS_IOC_POOL_FREEZE */ + 9, /* 9 ZFS_IOC_POOL_UPGRADE */ + 10, /* 10 ZFS_IOC_POOL_GET_HISTORY */ + 11, /* 11 ZFS_IOC_VDEV_ADD */ + 12, /* 12 ZFS_IOC_VDEV_REMOVE */ + 13, /* 13 ZFS_IOC_VDEV_SET_STATE */ + 14, /* 14 ZFS_IOC_VDEV_ATTACH */ + 15, /* 15 ZFS_IOC_VDEV_DETACH */ + 16, /* 16 ZFS_IOC_VDEV_SETPATH */ + 18, /* 17 ZFS_IOC_OBJSET_STATS */ + 19, /* 18 ZFS_IOC_OBJSET_ZPLPROPS */ + 20, /* 19 ZFS_IOC_DATASET_LIST_NEXT */ + 21, /* 20 ZFS_IOC_SNAPSHOT_LIST_NEXT */ + 22, /* 21 ZFS_IOC_SET_PROP */ + ZFS_IOC_COMPAT_PASS, /* 22 ZFS_IOC_CREATE_MINOR */ + ZFS_IOC_COMPAT_PASS, /* 23 ZFS_IOC_REMOVE_MINOR */ + 23, /* 24 ZFS_IOC_CREATE */ + 24, /* 25 ZFS_IOC_DESTROY */ + 25, /* 26 ZFS_IOC_ROLLBACK */ + 26, /* 27 ZFS_IOC_RENAME */ + 27, /* 28 ZFS_IOC_RECV */ + 28, /* 29 ZFS_IOC_SEND */ + 29, /* 30 ZFS_IOC_INJECT_FAULT */ + 30, /* 31 ZFS_IOC_CLEAR_FAULT */ + 31, /* 32 ZFS_IOC_INJECT_LIST_NEXT */ + 32, /* 33 ZFS_IOC_ERROR_LOG */ + 33, /* 34 ZFS_IOC_CLEAR */ + 34, /* 35 ZFS_IOC_PROMOTE */ + 35, /* 36 ZFS_IOC_DESTROY_SNAPS */ + 36, /* 37 ZFS_IOC_SNAPSHOT */ + 37, /* 38 ZFS_IOC_DSOBJ_TO_DSNAME */ + 38, /* 39 ZFS_IOC_OBJ_TO_PATH */ + 39, /* 40 ZFS_IOC_POOL_SET_PROPS */ + 40, /* 41 ZFS_IOC_POOL_GET_PROPS */ + 41, /* 42 ZFS_IOC_SET_FSACL */ + 42, /* 43 ZFS_IOC_GET_FSACL */ + ZFS_IOC_COMPAT_PASS, /* 44 ZFS_IOC_ISCSI_PERM_CHECK */ + 43, /* 45 ZFS_IOC_SHARE */ + 44, /* 46 ZFS_IOC_IHNERIT_PROP */ + 58, /* 47 ZFS_IOC_JAIL */ + 59, /* 48 ZFS_IOC_UNJAIL */ + 45, /* 49 ZFS_IOC_SMB_ACL */ + 46, /* 50 ZFS_IOC_USERSPACE_ONE */ + 47, /* 51 ZFS_IOC_USERSPACE_MANY */ + 48, /* 52 ZFS_IOC_USERSPACE_UPGRADE */ + 17, /* 53 ZFS_IOC_SETFRU */ +}; + +#ifdef _KERNEL +int zfs_ioctl_compat_pre(zfs_cmd_t *, int *, const int); +void zfs_ioctl_compat_post(zfs_cmd_t *, const int, const int); +nvlist_t *zfs_ioctl_compat_innvl(zfs_cmd_t *, nvlist_t *, const int, + const int); +nvlist_t *zfs_ioctl_compat_outnvl(zfs_cmd_t *, nvlist_t *, const int, + const int); +#endif /* _KERNEL */ +void zfs_cmd_compat_get(zfs_cmd_t *, caddr_t, const int); +void zfs_cmd_compat_put(zfs_cmd_t *, caddr_t, const int, const int); + +int wrap_avl_init(void); +int wrap_unicode_init(void); +int wrap_nvpair_init(void); +int wrap_zcommon_init(void); +int wrap_icp_init(void); +int wrap_lua_init(void); +int wrap_zstd_init(void); +void wrap_avl_fini(void); +void wrap_unicode_fini(void); +void wrap_nvpair_fini(void); +void wrap_zcommon_fini(void); +void wrap_icp_fini(void); +void wrap_lua_fini(void); +void wrap_zstd_fini(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_ZFS_IOCTL_COMPAT_H */ diff --git a/include/os/macos/zfs/sys/zfs_mount.h b/include/os/macos/zfs/sys/zfs_mount.h new file mode 100644 index 000000000000..7a3abc93e906 --- /dev/null +++ b/include/os/macos/zfs/sys/zfs_mount.h @@ -0,0 +1,59 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#ifndef _SYS_ZFS_MOUNT_H_ +#define _SYS_ZFS_MOUNT_H_ + +#include + +struct zfs_mount_args { + const char *fspec; + int mflag; + char *optptr; + int optlen; + int struct_size; +}; + + +/* + * Maximum option string length accepted or returned by mount(2). + */ +#define MAX_MNTOPT_STR 1024 /* max length of mount options string */ + +#ifdef _KERNEL +#define MS_RDONLY MNT_RDONLY +#define MS_NOEXEC MNT_NOEXEC +#define MS_NOSUID MNT_NOSUID +#define MS_NODEV MNT_NODEV +#define MS_BIND 0 +#define MS_REMOUNT MNT_UPDATE +#define MS_SYNCHRONOUS MNT_SYNCHRONOUS +#define MS_USERS (MS_NOEXEC|MS_NOSUID|MS_NODEV) +#define MS_OWNER (MS_NOSUID|MS_NODEV) +#define MS_GROUP (MS_NOSUID|MS_NODEV) +#define MS_COMMENT 0 +#define MS_FORCE MNT_FORCE +#define MS_DETACH MNT_DETACH +#define MS_OVERLAY MNT_UNION +#define MS_CRYPT MNT_CPROTECT +#endif + +#endif /* _SYS_ZFS_IOCTL_H */ diff --git a/include/os/macos/zfs/sys/zfs_vfsops_os.h b/include/os/macos/zfs/sys/zfs_vfsops_os.h new file mode 100644 index 000000000000..8084495f3624 --- /dev/null +++ b/include/os/macos/zfs/sys/zfs_vfsops_os.h @@ -0,0 +1,295 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#ifndef _SYS_FS_ZFS_VFSOPS_H +#define _SYS_FS_ZFS_VFSOPS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct zfs_sb; +struct znode; + +#ifdef __APPLE__ +#define APPLE_SA_RECOVER +/* #define WITH_SEARCHFS */ +/* #define WITH_READDIRATTR */ +#define HAVE_PAGEOUT_V2 1 +#define HIDE_TRIVIAL_ACL 1 +#ifndef __arm64__ +#define HAVE_NAMED_STREAMS 1 +#endif +#endif + +/* + * Status of the zfs_unlinked_drain thread. + */ +typedef enum drain_state { + ZFS_DRAIN_SHUTDOWN = 0, + ZFS_DRAIN_RUNNING, + ZFS_DRAIN_SHUTDOWN_REQ +} drain_state_t; + + +typedef struct zfsvfs zfsvfs_t; + +struct zfsvfs { + vfs_t *z_vfs; /* generic fs struct */ + zfsvfs_t *z_parent; /* parent fs */ + objset_t *z_os; /* objset reference */ + uint64_t z_root; /* id of root znode */ + uint64_t z_unlinkedobj; /* id of unlinked zapobj */ + uint64_t z_max_blksz; /* maximum block size for files */ + uint64_t z_fuid_obj; /* fuid table object number */ + uint64_t z_fuid_size; /* fuid table size */ + avl_tree_t z_fuid_idx; /* fuid tree keyed by index */ + avl_tree_t z_fuid_domain; /* fuid tree keyed by domain */ + krwlock_t z_fuid_lock; /* fuid lock */ + boolean_t z_fuid_loaded; /* fuid tables are loaded */ + boolean_t z_fuid_dirty; /* need to sync fuid table ? */ + struct zfs_fuid_info *z_fuid_replay; /* fuid info for replay */ + uint64_t z_assign; /* TXG_NOWAIT or set by zil_replay() */ + zilog_t *z_log; /* intent log pointer */ + uint_t z_acl_mode; /* acl chmod/mode behavior */ + uint_t z_acl_inherit; /* acl inheritance behavior */ + zfs_case_t z_case; /* case-sense */ + boolean_t z_utf8; /* utf8-only */ + int z_norm; /* normalization flags */ + boolean_t z_atime; /* enable atimes mount option */ + boolean_t z_unmounted; /* unmounted */ + rrmlock_t z_teardown_lock; + krwlock_t z_teardown_inactive_lock; + list_t z_all_znodes; /* all vnodes in the fs */ + kmutex_t z_znodes_lock; /* lock for z_all_znodes */ + struct vnode *z_ctldir; /* .zfs directory pointer */ + uint64_t z_ctldir_startid; /* Start of snapdir range */ + boolean_t z_show_ctldir; /* expose .zfs in the root dir */ + boolean_t z_issnap; /* true if this is a snapshot */ + boolean_t z_vscan; /* virus scan on/off */ + boolean_t z_use_fuids; /* version allows fuids */ + boolean_t z_replay; /* set during ZIL replay */ + boolean_t z_use_sa; /* version allow system attributes */ + boolean_t z_xattr_sa; /* allow xattrs to be stores as SA */ + uint64_t z_version; + uint64_t z_shares_dir; /* hidden shares dir */ + dataset_kstats_t z_kstat; /* fs kstats */ + kmutex_t z_lock; + + /* for controlling async zfs_unlinked_drain */ + kmutex_t z_drain_lock; + kcondvar_t z_drain_cv; + drain_state_t z_drain_state; + + uint64_t z_userquota_obj; + uint64_t z_groupquota_obj; + uint64_t z_userobjquota_obj; + uint64_t z_groupobjquota_obj; + uint64_t z_projectquota_obj; + uint64_t z_projectobjquota_obj; + +#ifdef __APPLE__ + dev_t z_rdev; /* proxy device for mount */ + boolean_t z_rdonly; /* is mount read-only? */ + time_t z_mount_time; /* mount timestamp (for Spotlight) */ + time_t z_last_unmount_time; /* unmount timestamp (for Spotlight) */ + boolean_t z_xattr; /* enable atimes mount option */ + + avl_tree_t z_hardlinks; /* linkid hash avl tree for vget */ + avl_tree_t z_hardlinks_linkid; /* sorted on linkid */ + krwlock_t z_hardlinks_lock; /* lock to access z_hardlinks */ + + uint64_t z_notification_conditions; /* HFSIOC_VOLUME_STATUS */ + uint64_t z_freespace_notify_warninglimit; + uint64_t z_freespace_notify_dangerlimit; + uint64_t z_freespace_notify_desiredlevel; + + void *z_devdisk; /* Hold fake disk if prop devdisk is on */ + + uint64_t z_findernotify_space; + +#endif + uint64_t z_replay_eof; /* New end of file - replay only */ + sa_attr_type_t *z_attr_table; /* SA attr mapping->id */ + + uint64_t z_hold_size; /* znode hold array size */ + avl_tree_t *z_hold_trees; /* znode hold trees */ + kmutex_t *z_hold_locks; /* znode hold locks */ + taskqid_t z_drain_task; /* task id for the unlink drain task */ +}; +#define ZFS_OBJ_MTX_SZ 64 + +#ifdef __APPLE__ +struct hardlinks_struct { + avl_node_t hl_node; + avl_node_t hl_node_linkid; + uint64_t hl_parent; // parentid of entry + uint64_t hl_fileid; // the fileid (z_id) for vget + uint32_t hl_linkid; // the linkid, persistent over renames + char hl_name[PATH_MAX]; // cached name for vget +}; +typedef struct hardlinks_struct hardlinks_t; + +int zfs_vfs_uuid_unparse(uuid_t uuid, char *dst); +int zfs_vfs_uuid_gen(const char *osname, uuid_t uuid); +#endif + + +#define ZFS_SUPER_MAGIC 0x2fc12fc1 + +#define ZSB_XATTR 0x0001 /* Enable user xattrs */ + +/* + * Normal filesystems (those not under .zfs/snapshot) have a total + * file ID size limited to 12 bytes (including the length field) due to + * NFSv2 protocol's limitation of 32 bytes for a filehandle. For historical + * reasons, this same limit is being imposed by the Solaris NFSv3 implementation + * (although the NFSv3 protocol actually permits a maximum of 64 bytes). It + * is not possible to expand beyond 12 bytes without abandoning support + * of NFSv2. + * + * For normal filesystems, we partition up the available space as follows: + * 2 bytes fid length (required) + * 6 bytes object number (48 bits) + * 4 bytes generation number (32 bits) + * + * We reserve only 48 bits for the object number, as this is the limit + * currently defined and imposed by the DMU. + */ +typedef struct zfid_short { + uint16_t zf_len; + uint8_t zf_object[6]; /* obj[i] = obj >> (8 * i) */ + uint8_t zf_gen[4]; /* gen[i] = gen >> (8 * i) */ +} zfid_short_t; + +/* + * Filesystems under .zfs/snapshot have a total file ID size of 22 bytes + * (including the length field). This makes files under .zfs/snapshot + * accessible by NFSv3 and NFSv4, but not NFSv2. + * + * For files under .zfs/snapshot, we partition up the available space + * as follows: + * 2 bytes fid length (required) + * 6 bytes object number (48 bits) + * 4 bytes generation number (32 bits) + * 6 bytes objset id (48 bits) + * 4 bytes currently just zero (32 bits) + * + * We reserve only 48 bits for the object number and objset id, as these are + * the limits currently defined and imposed by the DMU. + */ +typedef struct zfid_long { + zfid_short_t z_fid; + uint8_t zf_setid[6]; /* obj[i] = obj >> (8 * i) */ + uint8_t zf_setgen[4]; /* gen[i] = gen >> (8 * i) */ +} zfid_long_t; + +#define SHORT_FID_LEN (sizeof (zfid_short_t) - sizeof (uint16_t)) +#define LONG_FID_LEN (sizeof (zfid_long_t) - sizeof (uint16_t)) + +extern uint_t zfs_fsyncer_key; + +extern int zfs_suspend_fs(zfsvfs_t *zfsvfs); +extern int zfs_resume_fs(zfsvfs_t *zfsvfs, struct dsl_dataset *ds); +extern int zfs_userspace_one(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type, + const char *domain, uint64_t rid, uint64_t *valuep); +extern int zfs_userspace_many(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type, + uint64_t *cookiep, void *vbuf, uint64_t *bufsizep); +extern int zfs_set_userquota(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type, + const char *domain, uint64_t rid, uint64_t quota); +extern boolean_t zfs_owner_overquota(zfsvfs_t *zfsvfs, struct znode *, + boolean_t isgroup); +extern boolean_t zfs_fuid_overquota(zfsvfs_t *zfsvfs, boolean_t isgroup, + uint64_t fuid); +extern int zfs_set_version(zfsvfs_t *zfsvfs, uint64_t newvers); +extern int zfsvfs_create_impl(zfsvfs_t **zfvp, zfsvfs_t *zfsvfs, objset_t *os); + +extern int zfs_get_zplprop(objset_t *os, zfs_prop_t prop, + uint64_t *value); + +extern int zfs_sb_create(const char *name, zfsvfs_t **zfsvfsp); +extern int zfs_sb_setup(zfsvfs_t *zfsvfs, boolean_t mounting); +extern void zfs_sb_free(zfsvfs_t *zfsvfs); +extern int zfs_check_global_label(const char *dsname, const char *hexsl); +extern boolean_t zfs_is_readonly(zfsvfs_t *zfsvfs); + + + + +extern int zfs_vfs_init(struct vfsconf *vfsp); +extern int zfs_vfs_start(struct mount *mp, int flags, vfs_context_t context); +extern int zfs_vfs_mount(struct mount *mp, vnode_t *devvp, + user_addr_t data, vfs_context_t context); +extern int zfs_vfs_unmount(struct mount *mp, int mntflags, + vfs_context_t context); +extern int zfs_vfs_root(struct mount *mp, vnode_t **vpp, + vfs_context_t context); +extern int zfs_vfs_vget(struct mount *mp, ino64_t ino, vnode_t **vpp, + vfs_context_t context); +extern int zfs_vfs_getattr(struct mount *mp, struct vfs_attr *fsap, + vfs_context_t context); +extern int zfs_vfs_setattr(struct mount *mp, struct vfs_attr *fsap, + vfs_context_t context); +extern int zfs_vfs_sync(struct mount *mp, int waitfor, vfs_context_t context); +extern int zfs_vfs_fhtovp(struct mount *mp, int fhlen, unsigned char *fhp, + vnode_t **vpp, vfs_context_t context); +extern int zfs_vfs_vptofh(vnode_t *vp, int *fhlenp, unsigned char *fhp, + vfs_context_t context); +extern int zfs_vfs_sysctl(int *name, uint_t namelen, user_addr_t oldp, + size_t *oldlenp, user_addr_t newp, size_t newlen, vfs_context_t context); +extern int zfs_vfs_quotactl(struct mount *mp, int cmds, uid_t uid, + caddr_t datap, vfs_context_t context); +extern int zfs_vfs_mountroot(struct mount *mp, struct vnode *vp, + vfs_context_t context); + +extern void zfs_init(void); +extern void zfs_fini(void); + +extern int zfs_vnode_lock(vnode_t *vp, int flags); +extern void zfs_freevfs(struct mount *vfsp); + +extern int zfsvfs_create(const char *name, boolean_t rd, zfsvfs_t **zfvp); +extern void zfsvfs_free(zfsvfs_t *zfsvfs); + +extern int zfs_get_temporary_prop(dsl_dataset_t *ds, zfs_prop_t zfs_prop, + uint64_t *val, char *setpoint); + +extern int zfs_end_fs(zfsvfs_t *zfsvfs, struct dsl_dataset *ds); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_FS_ZFS_VFSOPS_H */ diff --git a/include/os/macos/zfs/sys/zfs_vnops.h b/include/os/macos/zfs/sys/zfs_vnops.h new file mode 100644 index 000000000000..09562a9a9c1f --- /dev/null +++ b/include/os/macos/zfs/sys/zfs_vnops.h @@ -0,0 +1,250 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#ifndef _SYS_FS_ZFS_VNOPS_OS_H +#define _SYS_FS_ZFS_VNOPS_OS_H + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Spotlight specific fcntl()'s + */ + +// Older defines +#define SPOTLIGHT_GET_MOUNT_TIME (FCNTL_FS_SPECIFIC_BASE + 0x00002) +#define SPOTLIGHT_GET_UNMOUNT_TIME (FCNTL_FS_SPECIFIC_BASE + 0x00003) + +// Newer defines, will these need a OSX version test to compile on older? +#define SPOTLIGHT_IOC_GET_MOUNT_TIME _IOR('h', 18, u_int32_t) +#define SPOTLIGHT_FSCTL_GET_MOUNT_TIME \ + IOCBASECMD(SPOTLIGHT_IOC_GET_MOUNT_TIME) +#define SPOTLIGHT_IOC_GET_LAST_MTIME _IOR('h', 19, u_int32_t) +#define SPOTLIGHT_FSCTL_GET_LAST_MTIME \ + IOCBASECMD(SPOTLIGHT_IOC_GET_LAST_MTIME) + +/* + * Account for user timespec structure differences + */ +#ifdef ZFS_LEOPARD_ONLY +typedef struct timespec timespec_user32_t; +typedef struct user_timespec timespec_user64_t; +#else +typedef struct user32_timespec timespec_user32_t; +typedef struct user64_timespec timespec_user64_t; +#endif + +#define UNKNOWNUID ((uid_t)99) +#define UNKNOWNGID ((gid_t)99) + +#define DTTOVT(dtype) (iftovt_tab[(dtype)]) +#define kTextEncodingMacUnicode 0x7e +#define ZAP_AVENAMELEN (ZAP_MAXNAMELEN / 4) + +/* Finder information */ +struct finderinfo { + u_int32_t fi_type; /* files only */ + u_int32_t fi_creator; /* files only */ + u_int16_t fi_flags; + struct { + int16_t v; + int16_t h; + } fi_location; + int8_t fi_opaque[18]; +} __attribute__((aligned(2), packed)); +typedef struct finderinfo finderinfo_t; + +enum { + /* Finder Flags */ + kHasBeenInited = 0x0100, + kHasCustomIcon = 0x0400, + kIsStationery = 0x0800, + kNameLocked = 0x1000, + kHasBundle = 0x2000, + kIsInvisible = 0x4000, + kIsAlias = 0x8000 +}; + +/* Attribute packing information */ +typedef struct attrinfo { + struct attrlist *ai_attrlist; + void **ai_attrbufpp; + void **ai_varbufpp; + void *ai_varbufend; + vfs_context_t ai_context; +} attrinfo_t; + +/* + * Attributes that we can get for free from the zap (ie without a znode) + */ +#define ZFS_DIR_ENT_ATTRS ( \ + ATTR_CMN_NAME | ATTR_CMN_DEVID | ATTR_CMN_FSID | \ + ATTR_CMN_OBJTYPE | ATTR_CMN_OBJTAG | ATTR_CMN_OBJID | \ + ATTR_CMN_OBJPERMANENTID | ATTR_CMN_SCRIPT | \ + ATTR_CMN_FILEID) + +/* + * Attributes that we support + */ +#define ZFS_ATTR_BIT_MAP_COUNT 5 + +#define ZFS_ATTR_CMN_VALID ( \ + ATTR_CMN_NAME | ATTR_CMN_DEVID | ATTR_CMN_FSID | \ + ATTR_CMN_OBJTYPE | ATTR_CMN_OBJTAG | ATTR_CMN_OBJID | \ + ATTR_CMN_OBJPERMANENTID | ATTR_CMN_PAROBJID | \ + ATTR_CMN_SCRIPT | ATTR_CMN_CRTIME | ATTR_CMN_MODTIME | \ + ATTR_CMN_CHGTIME | ATTR_CMN_ACCTIME | \ + ATTR_CMN_BKUPTIME | ATTR_CMN_FNDRINFO | \ + ATTR_CMN_OWNERID | ATTR_CMN_GRPID | \ + ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS | \ + ATTR_CMN_USERACCESS | ATTR_CMN_FILEID | \ + ATTR_CMN_PARENTID) + +#define ZFS_ATTR_DIR_VALID ( \ + ATTR_DIR_LINKCOUNT | ATTR_DIR_ENTRYCOUNT | \ + ATTR_DIR_MOUNTSTATUS) + +#define ZFS_ATTR_FILE_VALID ( \ + ATTR_FILE_LINKCOUNT |ATTR_FILE_TOTALSIZE | \ + ATTR_FILE_ALLOCSIZE | ATTR_FILE_IOBLOCKSIZE | \ + ATTR_FILE_DEVTYPE | ATTR_FILE_DATALENGTH | \ + ATTR_FILE_DATAALLOCSIZE | ATTR_FILE_RSRCLENGTH | \ + ATTR_FILE_RSRCALLOCSIZE) + +extern int zfs_remove(znode_t *dzp, char *name, cred_t *cr, int flags); +extern int zfs_mkdir(znode_t *dzp, char *dirname, vattr_t *vap, + znode_t **zpp, cred_t *cr, int flags, vsecattr_t *vsecp); +extern int zfs_rmdir(znode_t *dzp, char *name, znode_t *cwd, + cred_t *cr, int flags); +extern int zfs_setattr(znode_t *zp, vattr_t *vap, int flag, cred_t *cr); +extern int zfs_rename(znode_t *sdzp, char *snm, znode_t *tdzp, + char *tnm, cred_t *cr, int flags); +extern int zfs_symlink(znode_t *dzp, char *name, vattr_t *vap, + char *link, znode_t **zpp, cred_t *cr, int flags); +extern int zfs_link(znode_t *tdzp, znode_t *sp, + char *name, cred_t *cr, int flags); +extern int zfs_space(znode_t *zp, int cmd, struct flock *bfp, int flag, + offset_t offset, cred_t *cr); +extern int zfs_create(znode_t *dzp, char *name, vattr_t *vap, int excl, + int mode, znode_t **zpp, cred_t *cr, int flag, vsecattr_t *vsecp); +extern int zfs_write_simple(znode_t *zp, const void *data, size_t len, + loff_t pos, size_t *resid); + +extern int zfs_open(struct vnode *ip, int mode, int flag, cred_t *cr); +extern int zfs_close(struct vnode *ip, int flag, cred_t *cr); +extern int zfs_lookup(znode_t *dzp, char *nm, znode_t **zpp, + int flags, cred_t *cr, int *direntflags, struct componentname *realpnp); +extern int zfs_ioctl(vnode_t *vp, ulong_t com, intptr_t data, int flag, + cred_t *cred, int *rvalp, caller_context_t *ct); +extern int zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, int *eofp, + int flags, int *a_numdirent); +extern int zfs_fsync(znode_t *zp, int syncflag, cred_t *cr); +extern int zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, + cred_t *cr, caller_context_t *ct); +extern int zfs_readlink(vnode_t *vp, zfs_uio_t *uio, cred_t *cr); + +extern void zfs_inactive(vnode_t *vp); + +/* zfs_vops_osx.c calls */ +extern int zfs_znode_getvnode(znode_t *zp, zfsvfs_t *zfsvfs); + +extern void getnewvnode_reserve(int num); +extern void getnewvnode_drop_reserve(void); +extern int zfs_vfsops_init(void); +extern int zfs_vfsops_fini(void); +extern int zfs_znode_asyncgetvnode(znode_t *zp, zfsvfs_t *zfsvfs); +extern void zfs_znode_asyncput(znode_t *zp); +extern int zfs_znode_asyncwait(zfsvfs_t *, znode_t *zp); + +/* zfs_vnops_osx_lib calls */ +extern int zfs_ioflags(int ap_ioflag); +extern int zfs_getattr_znode_unlocked(struct vnode *vp, vattr_t *vap); +extern int ace_trivial_common(void *acep, int aclcnt, + uint64_t (*walk)(void *, uint64_t, int aclcnt, + uint16_t *, uint16_t *, uint32_t *)); +extern void acl_trivial_access_masks(mode_t mode, boolean_t isdir, + trivial_acl_t *masks); +extern int zpl_obtain_xattr(struct znode *, const char *name, mode_t mode, + cred_t *cr, struct vnode **vpp, int flag); + +extern void commonattrpack(attrinfo_t *aip, zfsvfs_t *zfsvfs, znode_t *zp, + const char *name, ino64_t objnum, enum vtype vtype, + boolean_t user64); +extern void dirattrpack(attrinfo_t *aip, znode_t *zp); +extern void fileattrpack(attrinfo_t *aip, zfsvfs_t *zfsvfs, znode_t *zp); +extern void nameattrpack(attrinfo_t *aip, const char *name, int namelen); +extern int getpackedsize(struct attrlist *alp, boolean_t user64); +extern void getfinderinfo(znode_t *zp, cred_t *cr, finderinfo_t *fip); +extern uint32_t getuseraccess(znode_t *zp, vfs_context_t ctx); +extern void finderinfo_update(uint8_t *finderinfo, znode_t *zp); +extern int zpl_xattr_set_sa(struct vnode *vp, const char *name, + const void *value, size_t size, int flags, cred_t *cr); +extern int zpl_xattr_get_sa(struct vnode *vp, const char *name, void *value, + size_t size); +extern void zfs_zrele_async(znode_t *zp); + +/* + * OSX ACL Helper funcions + * + * OSX uses 'guids' for the 'who' part of ACLs, and uses a 'well known' + * binary sequence to signify the special rules of "owner", "group" and + * "everybody". We translate between this "well-known" guid and ZFS' + * flags ACE_OWNER, ACE_GROUP and ACE_EVERYBODY. + * + */ +#define KAUTH_WKG_NOT 0 /* not a well-known GUID */ +#define KAUTH_WKG_OWNER 1 +#define KAUTH_WKG_GROUP 2 +#define KAUTH_WKG_NOBODY 3 +#define KAUTH_WKG_EVERYBODY 4 + +extern int kauth_wellknown_guid(guid_t *guid); +extern void aces_from_acl(ace_t *aces, int *nentries, struct kauth_acl *k_acl, + int *seen_type); +extern void nfsacl_set_wellknown(int wkg, guid_t *guid); +extern int zfs_addacl_trivial(znode_t *zp, ace_t *aces, int *nentries, + int seen_type); + +extern struct vnodeopv_desc zfs_dvnodeop_opv_desc; +extern struct vnodeopv_desc zfs_fvnodeop_opv_desc; +extern struct vnodeopv_desc zfs_symvnodeop_opv_desc; +extern struct vnodeopv_desc zfs_xdvnodeop_opv_desc; +extern struct vnodeopv_desc zfs_evnodeop_opv_desc; +extern struct vnodeopv_desc zfs_fifonodeop_opv_desc; +extern struct vnodeopv_desc zfs_ctldir_opv_desc; +extern int (**zfs_ctldirops)(void *); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_FS_ZFS_VNOPS_H */ diff --git a/include/os/macos/zfs/sys/zfs_znode_impl.h b/include/os/macos/zfs/sys/zfs_znode_impl.h new file mode 100644 index 000000000000..49a3b9af28e5 --- /dev/null +++ b/include/os/macos/zfs/sys/zfs_znode_impl.h @@ -0,0 +1,234 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2015 by Delphix. All rights reserved. + * Copyright (c) 2014 Integros [integros.com] + * Copyright 2016 Nexenta Systems, Inc. All rights reserved. + */ + +#ifndef _MACOS_ZFS_SYS_ZNODE_IMPL_H +#define _MACOS_ZFS_SYS_ZNODE_IMPL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZFS_UIMMUTABLE 0x0000001000000000ull // OSX +#define ZFS_UAPPENDONLY 0x0000004000000000ull // OSX + +// #define ZFS_IMMUTABLE (ZFS_UIMMUTABLE | ZFS_SIMMUTABLE) +// #define ZFS_APPENDONLY (ZFS_UAPPENDONLY | ZFS_SAPPENDONLY) + +#define ZFS_TRACKED 0x0010000000000000ull +#define ZFS_COMPRESSED 0x0020000000000000ull + +#define ZFS_SIMMUTABLE 0x0040000000000000ull +#define ZFS_SAPPENDONLY 0x0080000000000000ull + +#define SA_ZPL_ADDTIME(z) z->z_attr_table[ZPL_ADDTIME] +#define SA_ZPL_DOCUMENTID(z) z->z_attr_table[ZPL_DOCUMENTID] + +#define ZGET_FLAG_UNLINKED (1<<0) /* Also lookup unlinked */ +#define ZGET_FLAG_ASYNC (1<<3) /* taskq the vnode_create call */ + +extern int zfs_zget_ext(zfsvfs_t *zfsvfs, uint64_t obj_num, + struct znode **zpp, int flags); + + +/* + * Directory entry locks control access to directory entries. + * They are used to protect creates, deletes, and renames. + * Each directory znode has a mutex and a list of locked names. + */ +#define ZNODE_OS_FIELDS \ + struct zfsvfs *z_zfsvfs; \ + struct vnode *z_vnode; \ + uint64_t z_uid; \ + uint64_t z_gid; \ + uint64_t z_gen; \ + uint64_t z_atime[2]; \ + uint64_t z_links; \ + uint32_t z_vid; \ + uint32_t z_document_id; \ + uint64_t z_finder_parentid; \ + boolean_t z_finder_hardlink; \ + uint64_t z_write_gencount; \ + char z_name_cache[MAXPATHLEN]; \ + boolean_t z_skip_truncate_undo_decmpfs; \ + taskq_ent_t z_attach_taskq; \ + kcondvar_t z_attach_cv; \ + kmutex_t z_attach_lock; \ + hrtime_t z_snap_mount_time; \ + krwlock_t z_map_lock; + +#define ZFS_LINK_MAX UINT64_MAX + +/* + * ZFS minor numbers can refer to either a control device instance or + * a zvol. Depending on the value of zss_type, zss_data points to either + * a zvol_state_t or a zfs_onexit_t. + */ +enum zfs_soft_state_type { + ZSST_ZVOL, + ZSST_CTLDEV +}; + +typedef struct zfs_soft_state { + enum zfs_soft_state_type zss_type; + void *zss_data; +} zfs_soft_state_t; + +extern minor_t zfsdev_minor_alloc(void); + +/* + * Convert between znode pointers and vnode pointers + */ +#define ZTOV(ZP) ((ZP)->z_vnode) +#define ZTOI(ZP) ((ZP)->z_vnode) +#define VTOZ(VP) ((znode_t *)vnode_fsnode((VP))) +#define ITOZ(VP) ((znode_t *)vnode_fsnode((VP))) + +#define VTOM(VP) ((mount_t *)vnode_mount((VP))) + +/* These are not used so far, VN_HOLD returncode must be checked. */ +#define zhold(zp) VN_HOLD(ZTOV(zp)) +#define zrele(zp) VN_RELE(ZTOV(zp)) + +#define ZTOZSB(zp) ((zp)->z_zfsvfs) +#define ITOZSB(vp) ((zfsvfs_t *)vfs_fsprivate(vnode_mount(vp))) +#define ZTOTYPE(zp) (vnode_vtype(ZTOV(zp))) +#define ZTOGID(zp) ((zp)->z_gid) +#define ZTOUID(zp) ((zp)->z_uid) +#define ZTONLNK(zp) ((zp)->z_links) +#define Z_ISBLK(type) ((type) == VBLK) +#define Z_ISCHR(type) ((type) == VCHR) +#define Z_ISLNK(type) ((type) == VLNK) +#define Z_ISDIR(type) ((type) == VDIR) + +#define zn_has_cached_data(zp) ((zp)->z_is_mapped) +#define zn_rlimit_fsize(zp, uio) (0) + +/* Called on entry to each ZFS inode and vfs operation. */ +#define ZFS_ENTER_IFERROR(zfsvfs) \ + rrm_enter_read(&(zfsvfs)->z_teardown_lock, FTAG); \ + if ((zfsvfs)->z_unmounted) + +#define ZFS_ENTER_ERROR(zfsvfs, error) \ + do { \ + rrm_enter_read(&(zfsvfs)->z_teardown_lock, FTAG); \ + if ((zfsvfs)->z_unmounted) { \ + ZFS_EXIT(zfsvfs); \ + return (error); \ + } \ + } while (0) + +#define ZFS_ENTER(zfsvfs) ZFS_ENTER_ERROR(zfsvfs, EIO) +#define ZPL_ENTER(zfsvfs) ZFS_ENTER_ERROR(zfsvfs, EIO) + +/* Must be called before exiting the operation. */ +#define ZFS_EXIT(zfsvfs) \ + do { \ + rrm_exit(&(zfsvfs)->z_teardown_lock, FTAG); \ + } while (0) +#define ZPL_EXIT(zfsvfs) ZFS_EXIT(zfsvfs) + +/* Verifies the znode is valid. */ +#define ZFS_VERIFY_ZP_ERROR(zp, error) \ + do { \ + if ((zp)->z_sa_hdl == NULL) { \ + ZFS_EXIT(ZTOZSB(zp)); \ + return (error); \ + } \ + } while (0) + +#define ZFS_VERIFY_ZP(zp) ZFS_VERIFY_ZP_ERROR(zp, EIO) +#define ZPL_VERIFY_ZP(zp) ZFS_VERIFY_ZP_ERROR(zp, EIO) + +/* + * Macros for dealing with dmu_buf_hold + */ +#define ZFS_OBJ_MTX_SZ 64 +#define ZFS_OBJ_MTX_MAX (1024 * 1024) +#define ZFS_OBJ_HASH(zfsvfs, obj) ((obj) & ((zfsvfs->z_hold_size) - 1)) + +extern unsigned int zfs_object_mutex_size; + +/* Encode ZFS stored time values from a struct timespec */ +#define ZFS_TIME_ENCODE(tp, stmp) \ + { \ + (stmp)[0] = (uint64_t)(tp)->tv_sec; \ + (stmp)[1] = (uint64_t)(tp)->tv_nsec; \ + } + +/* Decode ZFS stored time values to a struct timespec */ +#define ZFS_TIME_DECODE(tp, stmp) \ + { \ + (tp)->tv_sec = (time_t)(stmp)[0]; \ + (tp)->tv_nsec = (long)(stmp)[1]; \ +} + +#define ZFS_ACCESSTIME_STAMP(zfsvfs, zp) \ + if ((zfsvfs)->z_atime && !vfs_isrdonly(zfsvfs->z_vfs)) \ + zfs_tstamp_update_setup_ext(zp, ACCESSED, NULL, NULL, B_FALSE); + +extern void zfs_tstamp_update_setup_ext(struct znode *, + uint_t, uint64_t [2], uint64_t [2], boolean_t); +extern void zfs_tstamp_update_setup(struct znode *, + uint_t, uint64_t [2], uint64_t [2]); +extern void zfs_znode_free(struct znode *); + +extern zil_get_data_t zfs_get_data; +extern zil_replay_func_t *zfs_replay_vector[TX_MAX_TYPE]; +extern int zfsfstype; + +extern int zfs_znode_parent_and_name(struct znode *zp, struct znode **dzpp, + char *buf); +extern uint32_t zfs_getbsdflags(struct znode *zp); +extern void zfs_setattr_generate_id(struct znode *, uint64_t, char *name); + +extern int zfs_setattr_set_documentid(struct znode *zp, + boolean_t update_flags); + +/* Legacy macOS uses fnv_32a hash for hostid. */ +#define FNV1_32A_INIT ((uint32_t)0x811c9dc5) +uint32_t fnv_32a_str(const char *str, uint32_t hval); + +void zfs_setbsdflags(struct znode *, uint32_t bsdflags); +uint32_t zfs_getbsdflags(struct znode *zp); + +#ifdef __cplusplus +} +#endif + +#endif /* _MACOS_SYS_FS_ZFS_ZNODE_H */ diff --git a/include/os/macos/zfs/sys/zpl.h b/include/os/macos/zfs/sys/zpl.h new file mode 100644 index 000000000000..5d391c6a960c --- /dev/null +++ b/include/os/macos/zfs/sys/zpl.h @@ -0,0 +1,27 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _SYS_ZPL_H +#define _SYS_ZPL_H + + + + +#endif // _SYS_ZPL_H diff --git a/include/os/macos/zfs/sys/zvolIO.h b/include/os/macos/zfs/sys/zvolIO.h new file mode 100644 index 000000000000..81544dfa4b22 --- /dev/null +++ b/include/os/macos/zfs/sys/zvolIO.h @@ -0,0 +1,142 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2013, 2016 Jorgen Lundman + */ + +#ifndef ZVOLIO_H_INCLUDED +#define ZVOLIO_H_INCLUDED + +/* Linux polutes 'current' */ +#undef current + +#ifdef __cplusplus +#include + +extern "C" { +#endif /* __cplusplus */ + +#include +#include + +struct iomem { + IOMemoryDescriptor *buf; +}; + +uint64_t zvolIO_kit_read(struct iomem *iomem, uint64_t offset, + char *address, uint64_t len); +uint64_t zvolIO_kit_write(struct iomem *iomem, uint64_t offset, + char *address, uint64_t len); + +#ifdef __cplusplus +} /* extern "C" */ + +class org_openzfsonosx_zfs_zvol : public IOService +{ + OSDeclareDefaultStructors(org_openzfsonosx_zfs_zvol) + +private: + +public: + virtual bool init(OSDictionary* dictionary = NULL); + virtual void free(void); + virtual IOService* probe(IOService* provider, SInt32* score); + virtual bool start(IOService* provider); + virtual void stop(IOService* provider); + + virtual bool handleOpen(IOService *client, + IOOptionBits options, void *arg); + virtual bool handleIsOpen(const IOService *client) const; + virtual void handleClose(IOService *client, + IOOptionBits options); + virtual bool isOpen(const IOService *forClient = 0) const; + +private: + OSSet *_openClients; +}; + +#include + +class org_openzfsonosx_zfs_zvol_device : public IOBlockStorageDevice +{ + OSDeclareDefaultStructors(org_openzfsonosx_zfs_zvol_device) + +private: + // IOService *m_provider; + zvol_state_t *zv; + +public: + virtual bool init(zvol_state_t *c_zv, + OSDictionary* properties = 0); + + virtual bool attach(IOService* provider); + virtual void detach(IOService* provider); + virtual IOReturn doEjectMedia(void); + virtual IOReturn doFormatMedia(UInt64 byteCapacity); + virtual UInt32 doGetFormatCapacities(UInt64 * capacities, + UInt32 capacitiesMaxCount) const; + + virtual IOReturn doLockUnlockMedia(bool doLock); + virtual IOReturn doSynchronizeCache(void); + virtual char *getVendorString(void); + virtual char *getProductString(void); + virtual char *getRevisionString(void); + virtual char *getAdditionalDeviceInfoString(void); + virtual IOReturn reportBlockSize(UInt64 *blockSize); + virtual IOReturn reportEjectability(bool *isEjectable); + virtual IOReturn reportLockability(bool *isLockable); + virtual IOReturn reportMaxValidBlock(UInt64 *maxBlock); + virtual IOReturn reportMediaState(bool *mediaPresent, + bool *changedState); + + virtual IOReturn reportPollRequirements(bool *pollRequired, + bool *pollIsExpensive); + + virtual IOReturn reportRemovability(bool *isRemovable); + virtual IOReturn reportWriteProtection(bool *isWriteProtected); + virtual IOReturn getWriteCacheState(bool *enabled); + virtual IOReturn setWriteCacheState(bool enabled); + virtual IOReturn doAsyncReadWrite(IOMemoryDescriptor *buffer, + UInt64 block, UInt64 nblks, + IOStorageAttributes *attributes, + IOStorageCompletion *completion); + + virtual IOReturn doDiscard(UInt64 block, UInt64 nblks); + virtual IOReturn doUnmap(IOBlockStorageDeviceExtent *extents, + UInt32 extentsCount, UInt32 options); + + virtual bool handleOpen(IOService *client, + IOOptionBits options, void *access); + + virtual void handleClose(IOService *client, + IOOptionBits options); + + virtual int getBSDName(void); + virtual int renameDevice(void); + virtual int offlineDevice(void); + virtual int onlineDevice(void); + virtual int refreshDevice(void); + + virtual void clearState(void); +}; +#endif /* __cplusplus */ + +#endif /* ZVOLIO_H_INCLUDED */ diff --git a/include/os/macos/zfs/sys/zvol_os.h b/include/os/macos/zfs/sys/zvol_os.h new file mode 100644 index 000000000000..1d26ee335c83 --- /dev/null +++ b/include/os/macos/zfs/sys/zvol_os.h @@ -0,0 +1,83 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _SYS_ZVOL_OS_h +#define _SYS_ZVOL_OS_h + +#ifdef __cplusplus +extern "C" { +#endif + +/* struct wrapper for IOKit class */ +typedef struct zvol_iokit zvol_iokit_t; +typedef struct zvol_state zvol_state_t; +struct iomem; + +struct zvol_state_os { + dev_t zvo_dev; /* device id */ + + zvol_iokit_t *zvo_iokitdev; /* IOKit device */ + uint64_t zvo_openflags; /* Remember flags used at open */ + char zvo_bsdname[MAXPATHLEN]; /* /dev/diskX */ +}; + +extern int zvol_os_ioctl(dev_t, unsigned long, caddr_t, + int isblk, cred_t *, int *rvalp); +extern int zvol_os_open_zv(zvol_state_t *, int, int, struct proc *p); +extern int zvol_os_open(dev_t dev, int flag, int otyp, struct proc *p); +extern int zvol_os_close_zv(zvol_state_t *, int, int, struct proc *p); +extern int zvol_os_close(dev_t dev, int flag, int otyp, struct proc *p); +extern int zvol_os_read(dev_t dev, struct uio *uio, int p); +extern int zvol_os_write(dev_t dev, struct uio *uio, int p); + +extern int zvol_os_read_zv(zvol_state_t *zv, uint64_t position, + uint64_t count, struct iomem *iomem); +extern int zvol_os_write_zv(zvol_state_t *zv, uint64_t position, + uint64_t count, struct iomem *iomem); +extern int zvol_os_unmap(zvol_state_t *zv, uint64_t off, uint64_t bytes); + +extern void zvol_os_strategy(struct buf *bp); +extern int zvol_os_get_volume_blocksize(dev_t dev); + +extern void zvol_os_lock_zv(zvol_state_t *zv); +extern void zvol_os_unlock_zv(zvol_state_t *zv); + +extern void *zvolRemoveDevice(zvol_iokit_t *iokitdev); +extern int zvolRemoveDeviceTerminate(void *iokitdev); +extern int zvolCreateNewDevice(zvol_state_t *zv); +extern int zvolRegisterDevice(zvol_state_t *zv); + +extern int zvolRenameDevice(zvol_state_t *zv); +extern int zvolSetVolsize(zvol_state_t *zv); + +extern void zvol_add_symlink(zvol_state_t *zv, const char *bsd_disk, + const char *bsd_rdisk); + +extern void zvol_remove_symlink(zvol_state_t *zv); + +extern void zfs_ereport_zvol_post(const char *subclass, const char *name, + const char *bsd, const char *rbsd); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/sys/abd_impl.h b/include/sys/abd_impl.h index 8c6883b66668..b719899d9bc3 100644 --- a/include/sys/abd_impl.h +++ b/include/sys/abd_impl.h @@ -96,9 +96,9 @@ void abd_iter_unmap(struct abd_iter *); #if defined(__FreeBSD__) #define abd_enter_critical(flags) critical_enter() #define abd_exit_critical(flags) critical_exit() -#elif defined (__APPLE__) -#define abd_enter_critical(flags) (flags) = ml_set_interrupts_enabled(FALSE) -#define abd_exit_critical(flags) ml_set_interrupts_enabled((flags)) +#elif defined(__APPLE__) +#define abd_enter_critical(flags) (flags) = ml_set_interrupts_enabled(FALSE) +#define abd_exit_critical(flags) ml_set_interrupts_enabled((flags)) #else #define abd_enter_critical(flags) local_irq_save(flags) #define abd_exit_critical(flags) local_irq_restore(flags) diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index 10e024022c28..890bf2ea37fd 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -1280,7 +1280,7 @@ typedef enum zfs_ioc { /* * Core features - 81/128 numbers reserved. */ -#if defined (__FreeBSD__) || defined(__APPLE__) +#if defined(__FreeBSD__) || defined(__APPLE__) ZFS_IOC_FIRST = 0, #else ZFS_IOC_FIRST = ('Z' << 8), @@ -1385,6 +1385,7 @@ typedef enum zfs_ioc { ZFS_IOC_SET_BOOTENV, /* 0x87 */ ZFS_IOC_GET_BOOTENV, /* 0x88 */ ZFS_IOC_PROXY_DATASET, /* 0x89 (macOS) */ + ZFS_IOC_PROXY_REMOVE, /* 0x8a (macOS) */ ZFS_IOC_LAST } zfs_ioc_t; diff --git a/include/sys/mntent.h b/include/sys/mntent.h index cb463ce29268..be62c53976b3 100644 --- a/include/sys/mntent.h +++ b/include/sys/mntent.h @@ -84,8 +84,8 @@ #define MNTOPT_NOSETUID "nosetuid" /* Set uid not allowed */ #define MNTOPT_BROWSE "browse" /* browsable autofs mount */ #define MNTOPT_NOBROWSE "nobrowse" /* non-browsable autofs mount */ -#define MNTOPT_OWNERS "owners" /* VFS will not ignore ownership information on filesystem objects */ -#define MNTOPT_NOOWNERS "noowners" /* VFS will ignore ownership information on filesystem objects */ +#define MNTOPT_OWNERS "owners" /* use ownership */ +#define MNTOPT_NOOWNERS "noowners" /* ignore ownership */ #else #error "unknown OS" #endif diff --git a/include/sys/sysevent/dev.h b/include/sys/sysevent/dev.h index 1255a176d36d..a41f0c0fe860 100644 --- a/include/sys/sysevent/dev.h +++ b/include/sys/sysevent/dev.h @@ -239,7 +239,7 @@ extern "C" { #define DEV_INSTANCE "instance" #define DEV_PROP_PREFIX "prop-" -#if defined (__linux__) || defined (__APPLE__) +#if defined(__linux__) || defined(__APPLE__) #define DEV_IDENTIFIER "devid" #define DEV_PATH "path" #define DEV_IS_PART "is_slice" diff --git a/include/sys/zfs_sa.h b/include/sys/zfs_sa.h index daf6113f8523..1ef781553fd1 100644 --- a/include/sys/zfs_sa.h +++ b/include/sys/zfs_sa.h @@ -76,10 +76,11 @@ typedef enum zpl_attr { ZPL_DXATTR, ZPL_PROJID, - /* Apple defines a ADDEDTIME, which is the time the entry was placed in - * the containing directory. Ie, CRTIME and updated when moved into - * a different directory. This can be retrieved with getxattr "FinderInfo" - * or the getattrlist() syscall. + /* + * Apple defines a ADDEDTIME, which is the time the entry was placed + * in the containing directory. Ie, CRTIME and updated when moved + * into a different directory. This can be retrieved with getxattr + * "FinderInfo" or the getattrlist() syscall. */ ZPL_ADDTIME, ZPL_DOCUMENTID, diff --git a/lib/Makefile.am b/lib/Makefile.am index 0d76c9d2f46d..a9f25555c19f 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,6 +1,6 @@ # NB: GNU Automake Manual, Chapter 8.3.5: Libtool Convenience Libraries # These nine libraries are intermediary build components. -SUBDIRS = libavl libicp libshare libspl libtpool libzstd +SUBDIRS = libavl libicp libshare libspl libtpool libzstd os CPPCHECKDIRS = libavl libicp libnvpair libshare libspl libtpool libunicode CPPCHECKDIRS += libuutil libzfs libzfs_core libzfsbootenv libzpool libzutil diff --git a/lib/libefi/rdwr_efi_macos.c b/lib/libefi/rdwr_efi_macos.c index 9a1a64ebe460..aafc575fa593 100644 --- a/lib/libefi/rdwr_efi_macos.c +++ b/lib/libefi/rdwr_efi_macos.c @@ -53,8 +53,6 @@ #include #include -int osx_device_isvirtual(char *pathbuf); - static struct uuid_to_ptag { struct uuid uuid; } conversion_array[] = { @@ -190,6 +188,127 @@ int efi_debug = 0; #endif static int efi_read(int, struct dk_gpt *); +/* Additional macOS support functions */ + +#include +#include +#include +#include + +static const CFStringRef CoreStorageLogicalVolumeMediaPathSubstring = + CFSTR("/CoreStoragePhysical/"); +static const CFStringRef VirtualInterfaceDeviceProtocolSubstring = + CFSTR(kIOPropertyPhysicalInterconnectTypeVirtual); + +typedef struct { + DASessionRef session; + DADiskRef disk; +} DADiskSession; + +static Boolean +CFDictionaryValueIfPresentMatchesSubstring(CFDictionaryRef dict, + CFStringRef key, CFStringRef substr) +{ + Boolean ret = false; + CFStringRef existing; + if (dict && + CFDictionaryGetValueIfPresent(dict, key, + (const void **)&existing)) { + CFRange range = CFStringFind(existing, substr, + kCFCompareCaseInsensitive); + if (range.location != kCFNotFound) + ret = true; + } + return (ret); +} + +static int +setupDADiskSession(DADiskSession *ds, const char *bsdName) +{ + int err = 0; + + ds->session = DASessionCreate(NULL); + if (ds->session == NULL) { + err = EINVAL; + } + + if (err == 0) { + ds->disk = DADiskCreateFromBSDName(NULL, ds->session, bsdName); + if (ds->disk == NULL) + err = EINVAL; + } + return (err); +} + +static void +teardownDADiskSession(DADiskSession *ds) +{ + if (ds->session != NULL) + CFRelease(ds->session); + if (ds->disk != NULL) + CFRelease(ds->disk); +} + +static int +isDeviceMatchForKeyAndSubstr(char *device, CFStringRef key, CFStringRef substr, + Boolean *isMatch) +{ + int error; + DADiskSession ds = { 0 }; + + if (!isMatch) + return (-1); + + if ((error = setupDADiskSession(&ds, device)) == 0) { + CFDictionaryRef descDict = NULL; + if ((descDict = DADiskCopyDescription(ds.disk)) != NULL) { + *isMatch = + CFDictionaryValueIfPresentMatchesSubstring(descDict, + key, substr); + } else { + error = -1; + (void) fprintf(stderr, + "no DADiskCopyDescription for device %s\n", + device); + *isMatch = false; + } + } + + teardownDADiskSession(&ds); + return (error); +} + +/* + * Caller is responsible for supplying a /dev/disk* block device path + * or the BSD name (disk*). + */ +static int +osx_device_isvirtual(char *device) +{ + Boolean isCoreStorageLV = false; + Boolean isVirtualInterface = false; + + if (efi_debug) + (void) fprintf(stderr, "Checking if '%s' is virtual\n", device); + + isDeviceMatchForKeyAndSubstr(device, + kDADiskDescriptionMediaPathKey, + CoreStorageLogicalVolumeMediaPathSubstring, + &isCoreStorageLV); + + isDeviceMatchForKeyAndSubstr(device, + kDADiskDescriptionDeviceProtocolKey, + VirtualInterfaceDeviceProtocolSubstring, + &isVirtualInterface); + + if (efi_debug) + (void) fprintf(stderr, + "Is CoreStorage LV %d : is virtual interface %d\n", + isCoreStorageLV, + isVirtualInterface); + + return (isCoreStorageLV /* || isVirtualInterface*/); +} /* * Return a 32-bit CRC of the contents of the buffer. Pre-and-post @@ -248,8 +367,8 @@ efi_get_info(int fd, struct dk_cinfo *dki_info) dki_info->dki_partition = 0; } strlcpy(dki_info->dki_dname, - &pathbuf[5], - sizeof(dki_info->dki_dname)); + &pathbuf[5], + sizeof (dki_info->dki_dname)); } /* @@ -1591,123 +1710,3 @@ efi_auto_sense(int fd, struct dk_gpt **vtoc) (*vtoc)->efi_parts[8].p_tag = V_RESERVED; return (0); } - -/* Additional macOS support functions */ - -#include -#include - -static const CFStringRef CoreStorageLogicalVolumeMediaPathSubstring = - CFSTR("/CoreStoragePhysical/"); -static const CFStringRef VirtualInterfaceDeviceProtocolSubstring = - CFSTR(kIOPropertyPhysicalInterconnectTypeVirtual); - -typedef struct { - DASessionRef session; - DADiskRef disk; -} DADiskSession; - -Boolean -CFDictionaryValueIfPresentMatchesSubstring(CFDictionaryRef dict, - CFStringRef key, CFStringRef substr) -{ - Boolean ret = false; - CFStringRef existing; - if (dict && - CFDictionaryGetValueIfPresent(dict, key, - (const void **)&existing)) { - CFRange range = CFStringFind(existing, substr, - kCFCompareCaseInsensitive); - if (range.location != kCFNotFound) - ret = true; - } - return (ret); -} - -int -setupDADiskSession(DADiskSession *ds, const char *bsdName) -{ - int err = 0; - - ds->session = DASessionCreate(NULL); - if (ds->session == NULL) { - err = EINVAL; - } - - if (err == 0) { - ds->disk = DADiskCreateFromBSDName(NULL, ds->session, bsdName); - if (ds->disk == NULL) - err = EINVAL; - } - return (err); -} - -void -teardownDADiskSession(DADiskSession *ds) -{ - if (ds->session != NULL) - CFRelease(ds->session); - if (ds->disk != NULL) - CFRelease(ds->disk); -} - -int -isDeviceMatchForKeyAndSubstr(char *device, CFStringRef key, CFStringRef substr, - Boolean *isMatch) -{ - int error; - DADiskSession ds = { 0 }; - - if (!isMatch) - return (-1); - - if ((error = setupDADiskSession(&ds, device)) == 0) { - CFDictionaryRef descDict = NULL; - if((descDict = DADiskCopyDescription(ds.disk)) != NULL) { - *isMatch = - CFDictionaryValueIfPresentMatchesSubstring(descDict, - key, substr); - } else { - error = -1; - (void) fprintf(stderr, - "no DADiskCopyDescription for device %s\n", - device); - *isMatch = false; - } - } - - teardownDADiskSession(&ds); - return (error); -} - -/* - * Caller is responsible for supplying a /dev/disk* block device path - * or the BSD name (disk*). - */ -int -osx_device_isvirtual(char *device) -{ - Boolean isCoreStorageLV = false; - Boolean isVirtualInterface = false; - - if (efi_debug) - (void) fprintf(stderr, "Checking if '%s' is virtual\n", device); - - isDeviceMatchForKeyAndSubstr(device, - kDADiskDescriptionMediaPathKey, - CoreStorageLogicalVolumeMediaPathSubstring, - &isCoreStorageLV); - - isDeviceMatchForKeyAndSubstr(device, - kDADiskDescriptionDeviceProtocolKey, - VirtualInterfaceDeviceProtocolSubstring, - &isVirtualInterface); - - if (efi_debug) - (void) fprintf(stderr, - "Is CoreStorage LV %d : is virtual interface %d\n", - isCoreStorageLV, - isVirtualInterface); - - return (isCoreStorageLV || isVirtualInterface); -} diff --git a/lib/libshare/os/macos/nfs.c b/lib/libshare/os/macos/nfs.c new file mode 100644 index 000000000000..9b63f99ee628 --- /dev/null +++ b/lib/libshare/os/macos/nfs.c @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2007 Pawel Jakub Dawidek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Copyright (c) 2020 by Delphix. All rights reserved. + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libzfs_impl.h" +#include "libshare_impl.h" +#include "nfs.h" + +#define _PATH_MOUNTDPID "/var/run/mountd.pid" +#define FILE_HEADER "# !!! DO NOT EDIT THIS FILE MANUALLY !!!\n\n" +#define OPTSSIZE 1024 +#define MAXLINESIZE (PATH_MAX + OPTSSIZE) +#define ZFS_EXPORTS_FILE "/etc/zfs/exports" +#define ZFS_EXPORTS_LOCK ZFS_EXPORTS_FILE".lock" + +static sa_fstype_t *nfs_fstype; + +static int nfs_lock_fd = -1; + +/* + * The nfs_exports_[lock|unlock] is used to guard against conconcurrent + * updates to the exports file. Each protocol is responsible for + * providing the necessary locking to ensure consistency. + */ +static int +nfs_exports_lock(void) +{ + nfs_lock_fd = open(ZFS_EXPORTS_LOCK, + O_RDWR | O_CREAT, 0600); + if (nfs_lock_fd == -1) { + fprintf(stderr, "failed to lock %s: %s\n", + ZFS_EXPORTS_LOCK, strerror(errno)); + return (errno); + } + if (flock(nfs_lock_fd, LOCK_EX) != 0) { + fprintf(stderr, "failed to lock %s: %s\n", + ZFS_EXPORTS_LOCK, strerror(errno)); + return (errno); + } + return (0); +} + +static void +nfs_exports_unlock(void) +{ + verify(nfs_lock_fd > 0); + + if (flock(nfs_lock_fd, LOCK_UN) != 0) { + fprintf(stderr, "failed to unlock %s: %s\n", + ZFS_EXPORTS_LOCK, strerror(errno)); + } + close(nfs_lock_fd); + nfs_lock_fd = -1; +} + +/* + * Read one line from a file. Skip comments, empty lines and a line with a + * mountpoint specified in the 'skip' argument. + * + * NOTE: This function returns a static buffer and thus is not thread-safe. + */ +static char * +zgetline(FILE *fd, const char *skip) +{ + static char line[MAXLINESIZE]; + size_t len, skiplen = 0; + char *s, last; + + if (skip != NULL) + skiplen = strlen(skip); + for (;;) { + s = fgets(line, sizeof (line), fd); + if (s == NULL) + return (NULL); + /* Skip empty lines and comments. */ + if (line[0] == '\n' || line[0] == '#') + continue; + len = strlen(line); + if (line[len - 1] == '\n') + line[len - 1] = '\0'; + last = line[skiplen]; + /* Skip the given mountpoint. */ + if (skip != NULL && strncmp(skip, line, skiplen) == 0 && + (last == '\t' || last == ' ' || last == '\0')) { + continue; + } + break; + } + return (line); +} + +/* + * This function translate options to a format acceptable by exports(5), eg. + * + * -ro -network=192.168.0.0 -mask=255.255.255.0 -maproot=0 \ + * zfs.freebsd.org 69.147.83.54 + * + * Accepted input formats: + * + * ro,network=192.168.0.0,mask=255.255.255.0,maproot=0,zfs.freebsd.org + * ro network=192.168.0.0 mask=255.255.255.0 maproot=0 zfs.freebsd.org + * -ro,-network=192.168.0.0,-mask=255.255.255.0,-maproot=0,zfs.freebsd.org + * -ro -network=192.168.0.0 -mask=255.255.255.0 -maproot=0 \ + * zfs.freebsd.org + * + * Recognized keywords: + * + * ro, maproot, mapall, mask, network, sec, alldirs, public, webnfs, + * index, quiet + * + * NOTE: This function returns a static buffer and thus is not thread-safe. + */ +static char * +translate_opts(const char *shareopts) +{ + static const char *known_opts[] = { "ro", "maproot", "mapall", "mask", + "network", "sec", "alldirs", "public", "webnfs", "index", "quiet", + NULL }; + static char newopts[OPTSSIZE]; + char oldopts[OPTSSIZE]; + char *o, *s = NULL; + unsigned int i; + size_t len; + + strlcpy(oldopts, shareopts, sizeof (oldopts)); + newopts[0] = '\0'; + s = oldopts; + while ((o = strsep(&s, "-, ")) != NULL) { + if (o[0] == '\0') + continue; + for (i = 0; known_opts[i] != NULL; i++) { + len = strlen(known_opts[i]); + if (strncmp(known_opts[i], o, len) == 0 && + (o[len] == '\0' || o[len] == '=')) { + strlcat(newopts, "-", sizeof (newopts)); + break; + } + } + strlcat(newopts, o, sizeof (newopts)); + strlcat(newopts, " ", sizeof (newopts)); + } + return (newopts); +} + +static char * +nfs_init_tmpfile(void) +{ + char *tmpfile = NULL; + + if (asprintf(&tmpfile, "%s%s", ZFS_EXPORTS_FILE, ".XXXXXXXX") == -1) { + fprintf(stderr, "Unable to allocate buffer for temporary " + "file name\n"); + return (NULL); + } + + int fd = mkstemp(tmpfile); + if (fd == -1) { + fprintf(stderr, "Unable to create temporary file: %s", + strerror(errno)); + free(tmpfile); + return (NULL); + } + close(fd); + return (tmpfile); +} + +static int +nfs_fini_tmpfile(char *tmpfile) +{ + if (rename(tmpfile, ZFS_EXPORTS_FILE) == -1) { + fprintf(stderr, "Unable to rename %s: %s\n", tmpfile, + strerror(errno)); + unlink(tmpfile); + free(tmpfile); + return (SA_SYSTEM_ERR); + } + free(tmpfile); + return (SA_OK); +} + +/* + * This function copies all entries from the exports file to "filename", + * omitting any entries for the specified mountpoint. + */ +int +nfs_copy_entries(char *filename, const char *mountpoint) +{ + int error = SA_OK; + char *line; + + /* + * If the file doesn't exist then there is nothing more + * we need to do. + */ + FILE *oldfp = fopen(ZFS_EXPORTS_FILE, "r"); + if (oldfp == NULL) + return (SA_OK); + + FILE *newfp = fopen(filename, "w+"); + fputs(FILE_HEADER, newfp); + while ((line = zgetline(oldfp, mountpoint)) != NULL) + fprintf(newfp, "%s\n", line); + if (ferror(oldfp) != 0) { + error = ferror(oldfp); + } + if (error == 0 && ferror(newfp) != 0) { + error = ferror(newfp); + } + + if (fclose(newfp) != 0) { + fprintf(stderr, "Unable to close file %s: %s\n", + filename, strerror(errno)); + error = error != 0 ? error : SA_SYSTEM_ERR; + } + fclose(oldfp); + + return (error); +} + +static int +nfs_enable_share(sa_share_impl_t impl_share) +{ + char *filename = NULL; + int error; + + if ((filename = nfs_init_tmpfile()) == NULL) + return (SA_SYSTEM_ERR); + + error = nfs_exports_lock(); + if (error != 0) { + unlink(filename); + free(filename); + return (error); + } + + error = nfs_copy_entries(filename, impl_share->sa_mountpoint); + if (error != SA_OK) { + unlink(filename); + free(filename); + nfs_exports_unlock(); + return (error); + } + + FILE *fp = fopen(filename, "a+"); + if (fp == NULL) { + fprintf(stderr, "failed to open %s file: %s", filename, + strerror(errno)); + unlink(filename); + free(filename); + nfs_exports_unlock(); + return (SA_SYSTEM_ERR); + } + char *shareopts = FSINFO(impl_share, nfs_fstype)->shareopts; + if (strcmp(shareopts, "on") == 0) + shareopts = ""; + + if (fprintf(fp, "%s\t%s\n", impl_share->sa_mountpoint, + translate_opts(shareopts)) < 0) { + fprintf(stderr, "failed to write to %s\n", filename); + fclose(fp); + unlink(filename); + free(filename); + nfs_exports_unlock(); + return (SA_SYSTEM_ERR); + } + + if (fclose(fp) != 0) { + fprintf(stderr, "Unable to close file %s: %s\n", + filename, strerror(errno)); + unlink(filename); + free(filename); + nfs_exports_unlock(); + return (SA_SYSTEM_ERR); + } + error = nfs_fini_tmpfile(filename); + nfs_exports_unlock(); + return (error); +} + +static int +nfs_disable_share(sa_share_impl_t impl_share) +{ + int error; + char *filename = NULL; + + if ((filename = nfs_init_tmpfile()) == NULL) + return (SA_SYSTEM_ERR); + + error = nfs_exports_lock(); + if (error != 0) { + unlink(filename); + free(filename); + return (error); + } + + error = nfs_copy_entries(filename, impl_share->sa_mountpoint); + if (error != SA_OK) { + unlink(filename); + free(filename); + nfs_exports_unlock(); + return (error); + } + + error = nfs_fini_tmpfile(filename); + nfs_exports_unlock(); + return (error); +} + +/* + * NOTE: This function returns a static buffer and thus is not thread-safe. + */ +static boolean_t +nfs_is_shared(sa_share_impl_t impl_share) +{ + static char line[MAXLINESIZE]; + char *s, last; + size_t len; + char *mntpoint = impl_share->sa_mountpoint; + size_t mntlen = strlen(mntpoint); + + FILE *fp = fopen(ZFS_EXPORTS_FILE, "r"); + if (fp == NULL) + return (B_FALSE); + + for (;;) { + s = fgets(line, sizeof (line), fp); + if (s == NULL) + return (B_FALSE); + /* Skip empty lines and comments. */ + if (line[0] == '\n' || line[0] == '#') + continue; + len = strlen(line); + if (line[len - 1] == '\n') + line[len - 1] = '\0'; + last = line[mntlen]; + /* Skip the given mountpoint. */ + if (strncmp(mntpoint, line, mntlen) == 0 && + (last == '\t' || last == ' ' || last == '\0')) { + fclose(fp); + return (B_TRUE); + } + } + fclose(fp); + return (B_FALSE); +} + +static int +nfs_validate_shareopts(const char *shareopts) +{ + return (SA_OK); +} + +static int +nfs_update_shareopts(sa_share_impl_t impl_share, const char *shareopts) +{ + FSINFO(impl_share, nfs_fstype)->shareopts = (char *)shareopts; + return (SA_OK); +} + +static void +nfs_clear_shareopts(sa_share_impl_t impl_share) +{ + FSINFO(impl_share, nfs_fstype)->shareopts = NULL; +} + +/* + * Commit the shares by restarting mountd. + */ +static int +nfs_commit_shares(void) +{ + return (SA_OK); +} + +static const sa_share_ops_t nfs_shareops = { + .enable_share = nfs_enable_share, + .disable_share = nfs_disable_share, + .is_shared = nfs_is_shared, + + .validate_shareopts = nfs_validate_shareopts, + .update_shareopts = nfs_update_shareopts, + .clear_shareopts = nfs_clear_shareopts, + .commit_shares = nfs_commit_shares, +}; + +/* + * Initializes the NFS functionality of libshare. + */ +void +libshare_nfs_init(void) +{ + nfs_fstype = register_fstype("nfs", &nfs_shareops); +} diff --git a/lib/libshare/os/macos/smb.c b/lib/libshare/os/macos/smb.c new file mode 100644 index 000000000000..5b606ab96955 --- /dev/null +++ b/lib/libshare/os/macos/smb.c @@ -0,0 +1,128 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2020 by Delphix. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libshare_impl.h" +#include "smb.h" + +static sa_fstype_t *smb_fstype; + +/* + * Enables SMB sharing for the specified share. + */ +static int +smb_enable_share(sa_share_impl_t impl_share) +{ + fprintf(stderr, "No SMB support in FreeBSD yet.\n"); + return (SA_NOT_SUPPORTED); +} +/* + * Disables SMB sharing for the specified share. + */ +static int +smb_disable_share(sa_share_impl_t impl_share) +{ + fprintf(stderr, "No SMB support in FreeBSD yet.\n"); + return (SA_NOT_SUPPORTED); +} + +/* + * Checks whether the specified SMB share options are syntactically correct. + */ +static int +smb_validate_shareopts(const char *shareopts) +{ + fprintf(stderr, "No SMB support in FreeBSD yet.\n"); + return (SA_NOT_SUPPORTED); +} + +/* + * Checks whether a share is currently active. + */ +static boolean_t +smb_is_share_active(sa_share_impl_t impl_share) +{ + return (B_FALSE); +} + +/* + * Called to update a share's options. A share's options might be out of + * date if the share was loaded from disk and the "sharesmb" dataset + * property has changed in the meantime. This function also takes care + * of re-enabling the share if necessary. + */ +static int +smb_update_shareopts(sa_share_impl_t impl_share, const char *shareopts) +{ + return (SA_OK); +} + +static int +smb_update_shares(void) +{ + /* Not implemented */ + return (0); +} +/* + * Clears a share's SMB options. Used by libshare to + * clean up shares that are about to be free()'d. + */ +static void +smb_clear_shareopts(sa_share_impl_t impl_share) +{ + FSINFO(impl_share, smb_fstype)->shareopts = NULL; +} + +static const sa_share_ops_t smb_shareops = { + .enable_share = smb_enable_share, + .disable_share = smb_disable_share, + .is_shared = smb_is_share_active, + + .validate_shareopts = smb_validate_shareopts, + .update_shareopts = smb_update_shareopts, + .clear_shareopts = smb_clear_shareopts, + .commit_shares = smb_update_shares, +}; + +/* + * Initializes the SMB functionality of libshare. + */ +void +libshare_smb_init(void) +{ + smb_fstype = register_fstype("smb", &smb_shareops); +} diff --git a/lib/libspl/include/os/Makefile.am b/lib/libspl/include/os/Makefile.am index 7b362e02ad59..22495a05b72f 100644 --- a/lib/libspl/include/os/Makefile.am +++ b/lib/libspl/include/os/Makefile.am @@ -5,3 +5,7 @@ endif if BUILD_LINUX SUBDIRS = linux endif + +if BUILD_MACOS +SUBDIRS = macos +endif diff --git a/lib/libspl/include/os/macos/Makefile.am b/lib/libspl/include/os/macos/Makefile.am new file mode 100644 index 000000000000..1d3c559bed89 --- /dev/null +++ b/lib/libspl/include/os/macos/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = mach rpc sys diff --git a/lib/libspl/include/os/macos/dirent.h b/lib/libspl/include/os/macos/dirent.h new file mode 100644 index 000000000000..b7ffe3d89cf1 --- /dev/null +++ b/lib/libspl/include/os/macos/dirent.h @@ -0,0 +1,37 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#ifndef _LIBSPL_DIRENT_H +#define _LIBSPL_DIRENT_H + +#include_next + + +/* Handle Linux use of 64 names */ + +#define readdir64 readdir +#define dirent64 dirent + +#endif diff --git a/lib/libspl/include/os/macos/ia32/sys/asm_linkage.h b/lib/libspl/include/os/macos/ia32/sys/asm_linkage.h new file mode 100644 index 000000000000..0009705ad61b --- /dev/null +++ b/lib/libspl/include/os/macos/ia32/sys/asm_linkage.h @@ -0,0 +1,297 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _IA32_SYS_ASM_LINKAGE_H +#define _IA32_SYS_ASM_LINKAGE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _ASM /* The remainder of this file is only for assembly files */ + +/* + * make annoying differences in assembler syntax go away + */ + +/* + * D16 and A16 are used to insert instructions prefixes; the + * macros help the assembler code be slightly more portable. + */ +#if !defined(__GNUC_AS__) +/* + * /usr/ccs/bin/as prefixes are parsed as separate instructions + */ +#define D16 data16; +#define A16 addr16; + +/* + * (There are some weird constructs in constant expressions) + */ +#define _CONST(const) [const] +#define _BITNOT(const) -1!_CONST(const) +#define _MUL(a, b) _CONST(a \* b) + +#else +/* + * Why not use the 'data16' and 'addr16' prefixes .. well, the + * assembler doesn't quite believe in real mode, and thus argues with + * us about what we're trying to do. + */ +#define D16 .byte 0x66; +#define A16 .byte 0x67; + +#define _CONST(const) (const) +#define _BITNOT(const) ~_CONST(const) +#define _MUL(a, b) _CONST(a * b) + +#endif + +/* + * C pointers are different sizes between i386 and amd64. + * These constants can be used to compute offsets into pointer arrays. + */ +#if defined(__amd64) +#define CLONGSHIFT 3 +#define CLONGSIZE 8 +#define CLONGMASK 7 +#elif defined(__i386) +#define CLONGSHIFT 2 +#define CLONGSIZE 4 +#define CLONGMASK 3 +#endif + +/* + * Since we know we're either ILP32 or LP64 .. + */ +#define CPTRSHIFT CLONGSHIFT +#define CPTRSIZE CLONGSIZE +#define CPTRMASK CLONGMASK + +#if CPTRSIZE != (1 << CPTRSHIFT) || CLONGSIZE != (1 << CLONGSHIFT) +#error "inconsistent shift constants" +#endif + +#if CPTRMASK != (CPTRSIZE - 1) || CLONGMASK != (CLONGSIZE - 1) +#error "inconsistent mask constants" +#endif + +#define ASM_ENTRY_ALIGN 4, 0x90 + +/* + * SSE register alignment and save areas + */ + +#define XMM_SIZE 16 +#define XMM_ALIGN 16 +#define XMM_ALIGN_LOG 4, 0x90 + +#if defined(__amd64) + +#define SAVE_XMM_PROLOG(sreg, nreg) \ + subq $_CONST(_MUL(XMM_SIZE, nreg)), %rsp; \ + movq %rsp, sreg + +#define RSTOR_XMM_EPILOG(sreg, nreg) \ + addq $_CONST(_MUL(XMM_SIZE, nreg)), %rsp + +#elif defined(__i386) + +#define SAVE_XMM_PROLOG(sreg, nreg) \ + subl $_CONST(_MUL(XMM_SIZE, nreg) + XMM_ALIGN), %esp; \ + movl %esp, sreg; \ + addl $XMM_ALIGN, sreg; \ + andl $_BITNOT(XMM_ALIGN-1), sreg + +#define RSTOR_XMM_EPILOG(sreg, nreg) \ + addl $_CONST(_MUL(XMM_SIZE, nreg) + XMM_ALIGN), %esp; + +#endif /* __i386 */ + +/* + * profiling causes definitions of the MCOUNT and RTMCOUNT + * particular to the type + */ +#ifdef GPROF + +#define MCOUNT(x) \ + pushl %ebp; \ + movl %esp, %ebp; \ + call _mcount; \ + popl %ebp + +#endif /* GPROF */ + +#ifdef PROF + +#define MCOUNT(x) \ +/* CSTYLED */ \ + .lcomm .L_/**/x/**/1, 4, 4; \ + pushl %ebp; \ + movl %esp, %ebp; \ +/* CSTYLED */ \ + movl $.L_/**/x/**/1, %edx; \ + call _mcount; \ + popl %ebp + +#endif /* PROF */ + +/* + * if we are not profiling, MCOUNT should be defined to nothing + */ +#if !defined(PROF) && !defined(GPROF) +#define MCOUNT(x) +#endif /* !defined(PROF) && !defined(GPROF) */ + +#define RTMCOUNT(x) MCOUNT(x) + +/* + * Macro to define weak symbol aliases. These are similar to the ANSI-C + * #pragma weak name = _name + * except a compiler can determine type. The assembler must be told. Hence, + * the second parameter must be the type of the symbol (i.e.: function,...) + */ +#define ANSI_PRAGMA_WEAK(sym, stype) \ + .weak sym; \ +/* CSTYLED */ \ +sym = _/**/sym + +/* + * Like ANSI_PRAGMA_WEAK(), but for unrelated names, as in: + * #pragma weak sym1 = sym2 + */ +#define ANSI_PRAGMA_WEAK2(sym1, sym2, stype) \ + .weak sym1; \ +sym1 = sym2 + +/* + * ENTRY provides the standard procedure entry code and an easy way to + * insert the calls to mcount for profiling. ENTRY_NP is identical, but + * never calls mcount. + */ +#define ENTRY(x) \ + .text; \ + .align ASM_ENTRY_ALIGN; \ + .globl _##x; \ + .globl _##x; \ + .globl x; \ +_##x:; \ +x: MCOUNT(x) + +#define ENTRY_NP(x) \ + .text; \ + .align ASM_ENTRY_ALIGN; \ + .globl _##x; \ + .globl x; \ +_##x:; \ +x: + +#define RTENTRY(x) \ + .text; \ + .align ASM_ENTRY_ALIGN; \ + .globl _##x; \ + .globl x; \ +_##x:; \ +x: RTMCOUNT(x) + +/* + * ENTRY2 is identical to ENTRY but provides two labels for the entry point. + */ +#define ENTRY2(x, y) \ + .text; \ + .align ASM_ENTRY_ALIGN; \ + .globl x, y; \ +/* CSTYLED */ \ +x:; \ +y: MCOUNT(x) + +#define ENTRY_NP2(x, y) \ + .text; \ + .align ASM_ENTRY_ALIGN; \ + .globl x, y; \ +/* CSTYLED */ \ +x:; \ +y: + + +/* + * ALTENTRY provides for additional entry points. + */ +#define ALTENTRY(x) \ + .globl _##x; \ + .globl x; \ +_##x:; \ +x: + +/* + * DGDEF and DGDEF2 provide global data declarations. + * + * DGDEF provides a word aligned word of storage. + * + * DGDEF2 allocates "sz" bytes of storage with **NO** alignment. This + * implies this macro is best used for byte arrays. + * + * DGDEF3 allocates "sz" bytes of storage with "algn" alignment. + */ +#define DGDEF2(name, sz) \ + .data; \ + .globl name; \ +name: + +#define DGDEF3(name, sz, algn) \ + .data; \ + .align algn; \ + .globl name; \ +name: + +#define DGDEF(name) DGDEF3(name, 4, 4) + +/* + * SET_SIZE trails a function and set the size for the ELF symbol table. + */ +#define SET_SIZE(x) + +/* + * NWORD provides native word value. + */ +#if defined(__amd64) + +/*CSTYLED*/ +#define NWORD quad + +#elif defined(__i386) + +#define NWORD long + +#endif /* __i386 */ + +#endif /* _ASM */ + +#ifdef __cplusplus +} +#endif + +#endif /* _IA32_SYS_ASM_LINKAGE_H */ diff --git a/lib/libspl/include/os/macos/libdiskmgt.h b/lib/libspl/include/os/macos/libdiskmgt.h new file mode 100644 index 000000000000..b1a3b54e6e7f --- /dev/null +++ b/lib/libspl/include/os/macos/libdiskmgt.h @@ -0,0 +1,84 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016, Brendon Humphrey (brendon.humphrey@mac.com). + */ + +#ifndef _LIBDISKMGT_H +#define _LIBDISKMGT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* attribute definitions */ + +#define DM_USED_BY "used_by" +#define DM_USED_NAME "used_name" +#define DM_USE_MOUNT "mount" +#define DM_USE_FS "fs" +#define DM_USE_FS_NO_FORCE "fs_nf" +#define DM_USE_EXPORTED_ZPOOL "exported_zpool" +#define DM_USE_ACTIVE_ZPOOL "active_zpool" +#define DM_USE_SPARE_ZPOOL "spare_zpool" +#define DM_USE_L2CACHE_ZPOOL "l2cache_zpool" +#define DM_USE_CORESTORAGE_PV "corestorage_pv" +#define DM_USE_CORESTORAGE_LOCKED_LV "corestorage_locked_lv" +#define DM_USE_CORESTORAGE_CONVERTING_LV "corestorage_converting_lv" +#define DM_USE_CORESTORAGE_OFFLINE_LV "corestorage_offline_lv" +#define DM_USE_OS_PARTITION "reserved_os_partititon" +#define DM_USE_OS_PARTITION_NO_FORCE "reserved_os_partititon_nf" + +#define NOINUSE_SET getenv("NOINUSE_CHECK") != NULL + +typedef enum { + DM_WHO_ZPOOL = 0, + DM_WHO_ZPOOL_FORCE, + DM_WHO_ZPOOL_SPARE +} dm_who_type_t; + +/* slice stat name */ +typedef enum { + DM_SLICE_STAT_USE = 0 +} dm_slice_stat_t; + +/* + * Unlike the Solaris implementation, libdiskmgt must be initialised, + * and torn down when no longer used. + */ +void libdiskmgt_init(void); +void libdiskmgt_fini(void); + +/* + * This is a partial implementation of (or similar to) libdiskmgt, + * adapted for OSX use. + */ +int dm_in_swap_dir(const char *dev_name); +int dm_inuse(char *dev_name, char **msg, dm_who_type_t who, int *errp); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/libspl/include/os/macos/mach/Makefile.am b/lib/libspl/include/os/macos/mach/Makefile.am new file mode 100644 index 000000000000..733185033dbd --- /dev/null +++ b/lib/libspl/include/os/macos/mach/Makefile.am @@ -0,0 +1,3 @@ +libspldir = $(includedir)/libspl/mach +libspl_HEADERS = \ + $(top_srcdir)/lib/libspl/include/os/macos/mach/boolean.h diff --git a/lib/libspl/include/os/macos/mach/boolean.h b/lib/libspl/include/os/macos/mach/boolean.h new file mode 100644 index 000000000000..47c93a3151b7 --- /dev/null +++ b/lib/libspl/include/os/macos/mach/boolean.h @@ -0,0 +1,26 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* Deal with XNU's own boolean_t version */ + +#define boolean_t xnu_boolean_t +#include_next +#undef boolean_t diff --git a/lib/libspl/include/os/macos/mach/task.h b/lib/libspl/include/os/macos/mach/task.h new file mode 100644 index 000000000000..eb4f9f877f7a --- /dev/null +++ b/lib/libspl/include/os/macos/mach/task.h @@ -0,0 +1,29 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#ifndef _SPL_MACH_TASK_H +#define _SPL_MACH_TASK_H + +#define thread_create xnu_thread_create +#include_next +#undef thread_create + +#endif diff --git a/lib/libspl/include/os/macos/mntent.h b/lib/libspl/include/os/macos/mntent.h new file mode 100644 index 000000000000..8183dda00b7f --- /dev/null +++ b/lib/libspl/include/os/macos/mntent.h @@ -0,0 +1,144 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T + * All Rights Reserved + */ + +#ifndef _SYS_MNTENT_H +#define _SYS_MNTENT_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#define MNTTAB "/etc/mnttab" +#define VFSTAB "/etc/vfstab" +#define MNTMAXSTR 128 + +#define MNTTYPE_ZFS "zfs" /* ZFS file system */ +#define MNTTYPE_UFS "ufs" /* Unix file system */ +#define MNTTYPE_NFS "nfs" /* NFS file system */ +#define MNTTYPE_NFS3 "nfs3" /* NFS Version 3 file system */ +#define MNTTYPE_NFS4 "nfs4" /* NFS Version 4 file system */ +#define MNTTYPE_CACHEFS "cachefs" /* Cache File System */ +#define MNTTYPE_PCFS "pcfs" /* PC (MSDOS) file system */ +#define MNTTYPE_PC MNTTYPE_PCFS /* Deprecated name; use MNTTYPE_PCFS */ +#define MNTTYPE_LOFS "lofs" /* Loop back file system */ +#define MNTTYPE_LO MNTTYPE_LOFS /* Deprecated name; use MNTTYPE_LOFS */ +#define MNTTYPE_HSFS "hsfs" /* High Sierra (9660) file system */ +#define MNTTYPE_SWAP "swap" /* Swap file system */ +#define MNTTYPE_TMPFS "tmpfs" /* Tmp volatile file system */ +#define MNTTYPE_AUTOFS "autofs" /* Automounter ``file'' system */ +#define MNTTYPE_MNTFS "mntfs" /* In-kernel mnttab */ +#define MNTTYPE_DEV "dev" /* /dev file system */ +#define MNTTYPE_CTFS "ctfs" /* Contract file system */ +#define MNTTYPE_OBJFS "objfs" /* Kernel object file system */ +#define MNTTYPE_SHAREFS "sharefs" /* Kernel sharetab file system */ + + +#define MNTOPT_RO "ro" /* Read only */ +#define MNTOPT_RW "rw" /* Read/write */ +#define MNTOPT_RQ "rq" /* Read/write with quotas */ +#define MNTOPT_QUOTA "quota" /* Check quotas */ +#define MNTOPT_NOQUOTA "noquota" /* Don't check quotas */ +#define MNTOPT_ONERROR "onerror" /* action to taken on error */ +#define MNTOPT_SOFT "soft" /* Soft mount */ +#define MNTOPT_SEMISOFT "semisoft" /* partial soft, uncommited interface */ +#define MNTOPT_HARD "hard" /* Hard mount */ +#define MNTOPT_SUID "suid" /* Both setuid and devices allowed */ +#define MNTOPT_NOSUID "nosuid" /* Neither setuid nor devices allowed */ +#define MNTOPT_DEVICES "devices" /* Device-special allowed */ +#define MNTOPT_NODEVICES "nodevices" /* Device-special disallowed */ +#define MNTOPT_SETUID "setuid" /* Set uid allowed */ +#define MNTOPT_NOSETUID "nosetuid" /* Set uid not allowed */ +#define MNTOPT_GRPID "grpid" /* SysV-compatible gid on create */ +#define MNTOPT_REMOUNT "remount" /* Change mount options */ +#define MNTOPT_NOSUB "nosub" /* Disallow mounts on subdirs */ +#define MNTOPT_MULTI "multi" /* Do multi-component lookup */ +#define MNTOPT_INTR "intr" /* Allow NFS ops to be interrupted */ +#define MNTOPT_NOINTR "nointr" /* Don't allow interrupted ops */ +#define MNTOPT_PORT "port" /* NFS server IP port number */ +#define MNTOPT_SECURE "secure" /* Secure (AUTH_DES) mounting */ +#define MNTOPT_RSIZE "rsize" /* Max NFS read size (bytes) */ +#define MNTOPT_WSIZE "wsize" /* Max NFS write size (bytes) */ +#define MNTOPT_TIMEO "timeo" /* NFS timeout (1/10 sec) */ +#define MNTOPT_RETRANS "retrans" /* Max retransmissions (soft mnts) */ +#define MNTOPT_ACTIMEO "actimeo" /* Attr cache timeout (sec) */ +#define MNTOPT_ACREGMIN "acregmin" /* Min attr cache timeout (files) */ +#define MNTOPT_ACREGMAX "acregmax" /* Max attr cache timeout (files) */ +#define MNTOPT_ACDIRMIN "acdirmin" /* Min attr cache timeout (dirs) */ +#define MNTOPT_ACDIRMAX "acdirmax" /* Max attr cache timeout (dirs) */ +#define MNTOPT_NOAC "noac" /* Don't cache attributes at all */ +#define MNTOPT_NOCTO "nocto" /* No close-to-open consistency */ +#define MNTOPT_BG "bg" /* Do mount retries in background */ +#define MNTOPT_FG "fg" /* Do mount retries in foreground */ +#define MNTOPT_RETRY "retry" /* Number of mount retries */ +#define MNTOPT_DEV "dev" /* Device id of mounted fs */ +#define MNTOPT_POSIX "posix" /* Get static pathconf for mount */ +#define MNTOPT_MAP "map" /* Automount map */ +#define MNTOPT_DIRECT "direct" /* Automount direct map mount */ +#define MNTOPT_INDIRECT "indirect" /* Automount indirect map mount */ +#define MNTOPT_LLOCK "llock" /* Local locking (no lock manager) */ +#define MNTOPT_IGNORE "ignore" /* Ignore this entry */ +#define MNTOPT_VERS "vers" /* protocol version number indicator */ +#define MNTOPT_PROTO "proto" /* protocol network_id indicator */ +#define MNTOPT_SEC "sec" /* Security flavor indicator */ +#define MNTOPT_SYNCDIR "syncdir" /* Synchronous local directory ops */ +#define MNTOPT_NOSETSEC "nosec" /* Do no allow setting sec attrs */ +#define MNTOPT_NOPRINT "noprint" /* Do not print messages */ +#define MNTOPT_LARGEFILES "largefiles" /* allow large files */ +#define MNTOPT_NOLARGEFILES "nolargefiles" /* don't allow large files */ +#define MNTOPT_FORCEDIRECTIO "forcedirectio" /* Force DirectIO on all files */ +#define MNTOPT_NOFORCEDIRECTIO "noforcedirectio" /* No Force DirectIO */ +#define MNTOPT_DISABLEDIRECTIO "disabledirectio" /* Disable DirectIO ioctls */ +#define MNTOPT_PUBLIC "public" /* Use NFS public file handlee */ +#define MNTOPT_LOGGING "logging" /* enable logging */ +#define MNTOPT_NOLOGGING "nologging" /* disable logging */ +#define MNTOPT_ATIME "atime" /* update atime for files */ +#define MNTOPT_NOATIME "noatime" /* do not update atime for files */ +#define MNTOPT_GLOBAL "global" /* Cluster-wide global mount */ +#define MNTOPT_NOGLOBAL "noglobal" /* Mount local to single node */ +#define MNTOPT_DFRATIME "dfratime" /* Deferred access time updates */ +#define MNTOPT_NODFRATIME "nodfratime" /* No Deferred access time updates */ +#define MNTOPT_NBMAND "nbmand" /* allow non-blocking mandatory locks */ +#define MNTOPT_NONBMAND "nonbmand" /* deny non-blocking mandatory locks */ +#define MNTOPT_XATTR "xattr" /* enable extended attributes */ +#define MNTOPT_NOXATTR "noxattr" /* disable extended attributes */ +#define MNTOPT_EXEC "exec" /* enable executables */ +#define MNTOPT_NOEXEC "noexec" /* disable executables */ +#define MNTOPT_RESTRICT "restrict" /* restricted autofs mount */ +#define MNTOPT_BROWSE "browse" /* browsable autofs mount */ +#define MNTOPT_NOBROWSE "nobrowse" /* non-browsable autofs mount */ +/* VFS will not ignore ownership information on filesystem objects */ +#define MNTOPT_OWNERS "owners" +/* VFS will ignore ownership information on filesystem objects */ +#define MNTOPT_NOOWNERS "noowners" + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_MNTENT_H */ diff --git a/lib/libspl/include/os/macos/poll.h b/lib/libspl/include/os/macos/poll.h new file mode 100644 index 000000000000..2bb5203d00c4 --- /dev/null +++ b/lib/libspl/include/os/macos/poll.h @@ -0,0 +1,31 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _LIBSPL_POLL_H +#define _LIBSPL_POLL_H + +#include_next + +#ifndef O_DIRECT +#define O_DIRECT 0 +#endif + +#endif diff --git a/lib/libspl/include/os/macos/rpc/Makefile.am b/lib/libspl/include/os/macos/rpc/Makefile.am new file mode 100644 index 000000000000..645ec772f92e --- /dev/null +++ b/lib/libspl/include/os/macos/rpc/Makefile.am @@ -0,0 +1,3 @@ +libspldir = $(includedir)/libspl/sys +libspl_HEADERS = \ + $(top_srcdir)/lib/libspl/include/os/macos/rpc/xdr.h diff --git a/lib/libspl/include/os/macos/rpc/xdr.h b/lib/libspl/include/os/macos/rpc/xdr.h new file mode 100644 index 000000000000..9fc13fefaf41 --- /dev/null +++ b/lib/libspl/include/os/macos/rpc/xdr.h @@ -0,0 +1,38 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T + * All Rights Reserved + * + * Portions of this source code were derived from Berkeley 4.3 BSD + * under license from the Regents of the University of California. + */ + +#ifndef LIBSPL_MACOS_RPC_XDR_H +#define LIBSPL_MACOS_RPC_XDR_H + +#include +#include_next + +#endif /* LIBSPL_MACOS_RPC_XDR_H */ diff --git a/lib/libspl/include/os/macos/stdio.h b/lib/libspl/include/os/macos/stdio.h new file mode 100644 index 000000000000..50294f658ed5 --- /dev/null +++ b/lib/libspl/include/os/macos/stdio.h @@ -0,0 +1,34 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBSPL_OSX_STDIO_H +#define _LIBSPL_OSX_STDIO_H + +#define dprintf xnu_dprintf +#include_next +#undef dprintf +#define dprintf printf +#endif diff --git a/lib/libspl/include/os/macos/stdlib.h b/lib/libspl/include/os/macos/stdlib.h new file mode 100644 index 000000000000..e95d4f65deb9 --- /dev/null +++ b/lib/libspl/include/os/macos/stdlib.h @@ -0,0 +1,29 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _LIBSPL_OSX_STDLIB_H +#define _LIBSPL_OSX_STDLIB_H + +#include_next +#include +#include + +#endif diff --git a/lib/libspl/include/os/macos/string.h b/lib/libspl/include/os/macos/string.h new file mode 100644 index 000000000000..c094d836f377 --- /dev/null +++ b/lib/libspl/include/os/macos/string.h @@ -0,0 +1,40 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#ifndef _LIBSPL_OSX_STRING_H +#define _LIBSPL_OSX_STRING_H + +#include_next + +/* OsX will assert if src == dst */ +static inline size_t +spl_strlcpy(char *__dst, const char *__source, size_t __size) +{ + if (__dst == __source) + return (0); + return (strlcpy(__dst, __source, __size)); +} + +#undef strlcpy +#define strlcpy spl_strlcpy + +#endif /* _LIBSPL_OSX_STRING_H */ diff --git a/lib/libspl/include/os/macos/synch.h b/lib/libspl/include/os/macos/synch.h new file mode 100644 index 000000000000..d75b8b15ec8f --- /dev/null +++ b/lib/libspl/include/os/macos/synch.h @@ -0,0 +1,81 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2014 Zettabyte Software, LLC. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBSPL_SYNCH_H +#define _LIBSPL_SYNCH_H + +#ifndef __sun__ + +#include +#include + +/* + * Definitions of synchronization types. + */ +#define USYNC_THREAD 0x00 /* private to a process */ +#define USYNC_PROCESS 0x01 /* shared by processes */ + +typedef pthread_rwlock_t rwlock_t; + +#define DEFAULTRWLOCK PTHREAD_RWLOCK_INITIALIZER + +static inline int +rwlock_init(rwlock_t *rwlp, int type, void *arg) +{ + pthread_rwlockattr_t attr; + int err = 0; + + VERIFY0(pthread_rwlockattr_init(&attr)); + switch (type) { + case USYNC_THREAD: + VERIFY0(pthread_rwlockattr_setpshared(&attr, + PTHREAD_PROCESS_PRIVATE)); + break; + case USYNC_PROCESS: + VERIFY0(pthread_rwlockattr_setpshared(&attr, + PTHREAD_PROCESS_SHARED)); + break; + default: + VERIFY0(1); + } + + err = pthread_rwlock_init(rwlp, &attr); + VERIFY0(pthread_rwlockattr_destroy(&attr)); + + return (err); +} + +#define rwlock_destroy(x) pthread_rwlock_destroy((x)) +#define rw_rdlock(x) pthread_rwlock_rdlock((x)) +#define rw_wrlock(x) pthread_rwlock_wrlock((x)) +#define rw_unlock(x) pthread_rwlock_unlock((x)) +#define rw_tryrdlock(x) pthread_rwlock_tryrdlock((x)) +#define rw_trywrlock(x) pthread_rwlock_trywrlock((x)) + +#endif /* __sun__ */ + +#endif diff --git a/lib/libspl/include/os/macos/sys/Makefile.am b/lib/libspl/include/os/macos/sys/Makefile.am new file mode 100644 index 000000000000..fb063b875c35 --- /dev/null +++ b/lib/libspl/include/os/macos/sys/Makefile.am @@ -0,0 +1,17 @@ +libspldir = $(includedir)/libspl/sys +libspl_HEADERS = \ + $(top_srcdir)/lib/libspl/include/os/macos/mach/boolean.h \ + $(top_srcdir)/lib/libspl/include/os/macos/rpc/xdr.h \ + $(top_srcdir)/lib/libspl/include/os/macos/sys/byteorder.h \ + $(top_srcdir)/lib/libspl/include/os/macos/sys/errno.h \ + $(top_srcdir)/lib/libspl/include/os/macos/sys/file.h \ + $(top_srcdir)/lib/libspl/include/os/macos/sys/kernel_types.h \ + $(top_srcdir)/lib/libspl/include/os/macos/sys/mnttab.h \ + $(top_srcdir)/lib/libspl/include/os/macos/sys/mount.h \ + $(top_srcdir)/lib/libspl/include/os/macos/sys/param.h \ + $(top_srcdir)/lib/libspl/include/os/macos/sys/stat.h \ + $(top_srcdir)/lib/libspl/include/os/macos/sys/sysmacros.h \ + $(top_srcdir)/lib/libspl/include/os/macos/sys/uio.h \ + $(top_srcdir)/lib/libspl/include/os/macos/sys/vfs.h \ + $(top_srcdir)/lib/libspl/include/os/macos/sys/xattr.h \ + $(top_srcdir)/lib/libspl/include/os/macos/sys/zfs_context_os.h diff --git a/lib/libspl/include/os/macos/sys/byteorder.h b/lib/libspl/include/os/macos/sys/byteorder.h new file mode 100644 index 000000000000..287dc900d17a --- /dev/null +++ b/lib/libspl/include/os/macos/sys/byteorder.h @@ -0,0 +1,288 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * University Copyright- Copyright (c) 1982, 1986, 1988 + * The Regents of the University of California + * All Rights Reserved + * + * University Acknowledgment- Portions of this document are derived from + * software developed by the University of California, Berkeley, and its + * contributors. + */ + +#ifndef _SYS_BYTEORDER_H +#define _SYS_BYTEORDER_H + +#include +#include <_types.h> + +/* + * Define the order of 32-bit words in 64-bit words. + */ +#define _QUAD_HIGHWORD 1 +#define _QUAD_LOWWORD 0 + +/* + * Definitions for byte order, according to byte significance from low + * address to high. + */ +#undef _LITTLE_ENDIAN +/* LSB first: i386, vax */ +#define _LITTLE_ENDIAN 1234 +/* LSB first in word, MSW first in long */ +#define _PDP_ENDIAN 3412 + +#define _BYTE_ORDER _LITTLE_ENDIAN + +/* + * Deprecated variants that don't have enough underscores to be useful in more + * strict namespaces. + */ +#if __BSD_VISIBLE +#define LITTLE_ENDIAN _LITTLE_ENDIAN +#define PDP_ENDIAN _PDP_ENDIAN +#define BYTE_ORDER _BYTE_ORDER +#endif + +#define __bswap16_gen(x) (__uint16_t)((x) << 8 | (x) >> 8) +#define __bswap32_gen(x) \ + (((__uint32_t)__bswap16((x) & 0xffff) << 16) | __bswap16((x) >> 16)) +#define __bswap64_gen(x) \ + (((__uint64_t)__bswap32((x) & 0xffffffff) << 32) | __bswap32((x) >> 32)) + +#ifdef __GNUCLIKE_BUILTIN_CONSTANT_P +#define __bswap16(x) \ + ((__uint16_t)(__builtin_constant_p(x) ? \ + __bswap16_gen((__uint16_t)(x)) : __bswap16_var(x))) +#define __bswap32(x) \ + (__builtin_constant_p(x) ? \ + __bswap32_gen((__uint32_t)(x)) : __bswap32_var(x)) +#define __bswap64(x) \ + (__builtin_constant_p(x) ? \ + __bswap64_gen((__uint64_t)(x)) : __bswap64_var(x)) +#else +/* XXX these are broken for use in static initializers. */ +#define __bswap16(x) __bswap16_var(x) +#define __bswap32(x) __bswap32_var(x) +#define __bswap64(x) __bswap64_var(x) +#endif + +/* These are defined as functions to avoid multiple evaluation of x. */ + +static __inline __uint16_t +__bswap16_var(__uint16_t _x) +{ + + return (__bswap16_gen(_x)); +} + +static __inline __uint32_t +__bswap32_var(__uint32_t _x) +{ + +#ifdef __GNUCLIKE_ASM + __asm("bswap %0" : "+r" (_x)); + return (_x); +#else + return (__bswap32_gen(_x)); +#endif +} +#define __htonl(x) __bswap32(x) +#define __htons(x) __bswap16(x) +#define __ntohl(x) __bswap32(x) +#define __ntohs(x) __bswap16(x) + +#include +#include + +#if defined(__GNUC__) && defined(_ASM_INLINES) && \ + (defined(__i386) || defined(__amd64)) +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * macros for conversion between host and (internet) network byte order + */ + +#if defined(_BIG_ENDIAN) && !defined(ntohl) && !defined(__lint) +/* big-endian */ +#if defined(_BIG_ENDIAN) && (defined(__amd64__) || defined(__amd64)) +#error "incompatible ENDIAN / ARCH combination" +#endif +#define ntohl(x) (x) +#define ntohs(x) (x) +#define htonl(x) (x) +#define htons(x) (x) + +#elif !defined(ntohl) /* little-endian */ + +#ifndef _IN_PORT_T +#define _IN_PORT_T +typedef uint16_t in_port_t; +#endif + +#ifndef _IN_ADDR_T +#define _IN_ADDR_T +typedef uint32_t in_addr_t; +#endif + +#if !defined(_XPG4_2) || defined(__EXTENSIONS__) || defined(_XPG5) +extern uint32_t htonl(uint32_t); +extern uint16_t htons(uint16_t); +extern uint32_t ntohl(uint32_t); +extern uint16_t ntohs(uint16_t); +#else +extern in_addr_t htonl(in_addr_t); +extern in_port_t htons(in_port_t); +extern in_addr_t ntohl(in_addr_t); +extern in_port_t ntohs(in_port_t); +#endif /* !defined(_XPG4_2) || defined(__EXTENSIONS__) || defined(_XPG5) */ +#endif + +/* 10.8 is lacking in htonll */ +#if !defined(htonll) +#define htonll(x) __DARWIN_OSSwapInt64(x) +#endif + +#if !defined(ntohll) +#define ntohll(x) __DARWIN_OSSwapInt64(x) +#endif + +#if !defined(_XPG4_2) || defined(__EXTENSIONS__) + +/* + * Macros to reverse byte order + */ +#define BSWAP_8(x) ((x) & 0xff) +#define BSWAP_16(x) ((BSWAP_8(x) << 8) | BSWAP_8((x) >> 8)) +#define BSWAP_32(x) ((BSWAP_16(x) << 16) | BSWAP_16((x) >> 16)) +#define BSWAP_64(x) ((BSWAP_32(x) << 32) | BSWAP_32((x) >> 32)) + +#define BMASK_8(x) ((x) & 0xff) +#define BMASK_16(x) ((x) & 0xffff) +#define BMASK_32(x) ((x) & 0xffffffff) +#define BMASK_64(x) (x) + +/* + * Macros to convert from a specific byte order to/from native byte order + */ +#ifdef _BIG_ENDIAN +#define BE_8(x) BMASK_8(x) +#define BE_16(x) BMASK_16(x) +#define BE_32(x) BMASK_32(x) +#define BE_64(x) BMASK_64(x) +#define LE_8(x) BSWAP_8(x) +#define LE_16(x) BSWAP_16(x) +#define LE_32(x) BSWAP_32(x) +#define LE_64(x) BSWAP_64(x) +#else +#define LE_8(x) BMASK_8(x) +#define LE_16(x) BMASK_16(x) +#define LE_32(x) BMASK_32(x) +#define LE_64(x) BMASK_64(x) +#define BE_8(x) BSWAP_8(x) +#define BE_16(x) BSWAP_16(x) +#define BE_32(x) BSWAP_32(x) +#define BE_64(x) BSWAP_64(x) +#endif + +/* + * Macros to read unaligned values from a specific byte order to + * native byte order + */ + +#define BE_IN8(xa) \ + *((uint8_t *)(xa)) + +#define BE_IN16(xa) \ + (((uint16_t)BE_IN8(xa) << 8) | BE_IN8((uint8_t *)(xa)+1)) + +#define BE_IN32(xa) \ + (((uint32_t)BE_IN16(xa) << 16) | BE_IN16((uint8_t *)(xa)+2)) + +#define BE_IN64(xa) \ + (((uint64_t)BE_IN32(xa) << 32) | BE_IN32((uint8_t *)(xa)+4)) + +#define LE_IN8(xa) \ + *((uint8_t *)(xa)) + +#define LE_IN16(xa) \ + (((uint16_t)LE_IN8((uint8_t *)(xa) + 1) << 8) | LE_IN8(xa)) + +#define LE_IN32(xa) \ + (((uint32_t)LE_IN16((uint8_t *)(xa) + 2) << 16) | LE_IN16(xa)) + +#define LE_IN64(xa) \ + (((uint64_t)LE_IN32((uint8_t *)(xa) + 4) << 32) | LE_IN32(xa)) + +/* + * Macros to write unaligned values from native byte order to a specific byte + * order. + */ + +#define BE_OUT8(xa, yv) *((uint8_t *)(xa)) = (uint8_t)(yv); + +#define BE_OUT16(xa, yv) \ + BE_OUT8((uint8_t *)(xa) + 1, yv); \ + BE_OUT8((uint8_t *)(xa), (yv) >> 8); + +#define BE_OUT32(xa, yv) \ + BE_OUT16((uint8_t *)(xa) + 2, yv); \ + BE_OUT16((uint8_t *)(xa), (yv) >> 16); + +#define BE_OUT64(xa, yv) \ + BE_OUT32((uint8_t *)(xa) + 4, yv); \ + BE_OUT32((uint8_t *)(xa), (yv) >> 32); + +#define LE_OUT8(xa, yv) *((uint8_t *)(xa)) = (uint8_t)(yv); + +#define LE_OUT16(xa, yv) \ + LE_OUT8((uint8_t *)(xa), yv); \ + LE_OUT8((uint8_t *)(xa) + 1, (yv) >> 8); + +#define LE_OUT32(xa, yv) \ + LE_OUT16((uint8_t *)(xa), yv); \ + LE_OUT16((uint8_t *)(xa) + 2, (yv) >> 16); + +#define LE_OUT64(xa, yv) \ + LE_OUT32((uint8_t *)(xa), yv); \ + LE_OUT32((uint8_t *)(xa) + 4, (yv) >> 32); + +#endif /* !defined(_XPG4_2) || defined(__EXTENSIONS__) */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_BYTEORDER_H */ diff --git a/lib/libspl/include/os/macos/sys/errno.h b/lib/libspl/include/os/macos/sys/errno.h new file mode 100644 index 000000000000..af4846ebb903 --- /dev/null +++ b/lib/libspl/include/os/macos/sys/errno.h @@ -0,0 +1,31 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#include_next + +#define EBADE EBADMACHO +#define ECKSUM EBADE +#define EFRAGS EIDRM +#define EREMOTEIO ENOLINK +#define ENOTACTIVE ENOPOLICY +#define ECHRNG EMULTIHOP + +#define ERESTART (-1) /* restart syscall */ diff --git a/lib/libspl/include/os/macos/sys/fcntl.h b/lib/libspl/include/os/macos/sys/fcntl.h new file mode 100644 index 000000000000..f99dca4804f2 --- /dev/null +++ b/lib/libspl/include/os/macos/sys/fcntl.h @@ -0,0 +1,42 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#ifndef _LIBSPL_SYS_FCNTL_H +#define _LIBSPL_SYS_FCNTL_H + +#include_next + +#define O_LARGEFILE 0 +#define O_RSYNC 0 + +#ifndef O_DIRECT +#define O_DIRECT 0 +#endif + +#if !defined(MAC_OS_X_VERSION_10_10) || \ + (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10) +#define AT_FDCWD -2 +#define openat(fd, path, oflag, ...) \ + (fd == AT_FDCWD ? open((path), (oflag), __VA_ARGS__) : -1) +#endif + +#endif diff --git a/lib/libspl/include/os/macos/sys/file.h b/lib/libspl/include/os/macos/sys/file.h new file mode 100644 index 000000000000..94a33cbb336e --- /dev/null +++ b/lib/libspl/include/os/macos/sys/file.h @@ -0,0 +1,46 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBSPL_SYS_FILE_H +#define _LIBSPL_SYS_FILE_H + +#include_next + +#define FCREAT O_CREAT +#define FTRUNC O_TRUNC +#define FOFFMAX 0 +#define FSYNC O_SYNC +#define FDSYNC O_DSYNC +#define FRSYNC O_RSYNC +#define FEXCL O_EXCL + +#define IO_DIRECT 0 + +#define FNODSYNC 0x10000 /* fsync pseudo flag */ +#define FNOFOLLOW 0x20000 /* don't follow symlinks */ +#define FIGNORECASE 0x80000 /* request case-insensitive lookups */ + +#endif diff --git a/lib/libspl/include/os/macos/sys/kernel_types.h b/lib/libspl/include/os/macos/sys/kernel_types.h new file mode 100644 index 000000000000..5796351a2024 --- /dev/null +++ b/lib/libspl/include/os/macos/sys/kernel_types.h @@ -0,0 +1,43 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#ifndef LIBSPL_SYS_KERNEL_TYPES_H +#define LIBSPL_SYS_KERNEL_TYPES_H + +/* + * Unfortunately, XNU defines uio_t, proc_t and vnode_t differently to + * ZFS, so we need to hack around it. + */ + +#undef vnode_t +#undef uio_t +#define proc_t kernel_proc_t +#include_next +#define vnode_t struct vnode +#define uio_t struct uio +#undef proc_t + + +/* Other missing Linux types */ +typedef off_t loff_t; + +#endif diff --git a/lib/libspl/include/os/macos/sys/mnttab.h b/lib/libspl/include/os/macos/sys/mnttab.h new file mode 100644 index 000000000000..9ba5d08b219b --- /dev/null +++ b/lib/libspl/include/os/macos/sys/mnttab.h @@ -0,0 +1,86 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* Copyright 2006 Ricardo Correia */ + +#ifndef _SYS_MNTTAB_H +#define _SYS_MNTTAB_H + +#include +#include +#include +#include + +#ifdef MNTTAB +#undef MNTTAB +#endif /* MNTTAB */ + +#include +#include +#define MNTTAB _PATH_DEVNULL +#define MS_NOMNTTAB 0x0 +#define MS_RDONLY 0x1 +#define umount2(p, f) unmount(p, f) +#define MNT_LINE_MAX 4096 + +#define MNT_TOOLONG 1 /* entry exceeds MNT_LINE_MAX */ +#define MNT_TOOMANY 2 /* too many fields in line */ +#define MNT_TOOFEW 3 /* too few fields in line */ + +struct mnttab { + char *mnt_special; + char *mnt_mountp; + char *mnt_fstype; + char *mnt_mntopts; + uint_t mnt_major; + uint_t mnt_minor; + uint32_t mnt_fssubtype; +}; + +#define extmnttab mnttab + +struct stat64; +struct statfs; + +extern DIR *fdopendir(int fd); +extern int openat64(int, const char *, int, ...); + +extern int getmntany(FILE *fd, struct mnttab *mgetp, struct mnttab *mrefp); +extern int getmntent(FILE *fp, struct mnttab *mp); +extern char *hasmntopt(struct mnttab *mnt, char *opt); +extern int getextmntent(const char *path, struct extmnttab *entry, + struct stat64 *statbuf); + +extern void statfs2mnttab(struct statfs *sfs, struct mnttab *mp); + +#ifndef AT_SYMLINK_NOFOLLOW +#define AT_SYMLINK_NOFOLLOW 0x100 +#endif + +extern int fstatat64(int, const char *, struct stat *, int); + +#endif diff --git a/lib/libspl/include/os/macos/sys/mount.h b/lib/libspl/include/os/macos/sys/mount.h new file mode 100644 index 000000000000..0153b7f53583 --- /dev/null +++ b/lib/libspl/include/os/macos/sys/mount.h @@ -0,0 +1,87 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#ifndef _LIBSPL_SYS_MOUNT_H +#define _LIBSPL_SYS_MOUNT_H + +#undef _SYS_MOUNT_H_ + +#include +#include +#include +#include +#include + +/* Unfortunately, XNU has a different meaning for "vnode_t". */ +#undef vnode_t +#include_next +#define vnode_t struct vnode + +/* + * Some old glibc headers don't define BLKGETSIZE64 + * and we don't want to require the kernel headers + */ +#if !defined(BLKGETSIZE64) +#define BLKGETSIZE64 _IOR(0x12, 114, size_t) +#endif + +/* + * Some old glibc headers don't correctly define MS_DIRSYNC and + * instead use the enum name S_WRITE. When using these older + * headers define MS_DIRSYNC to be S_WRITE. + */ +#if !defined(MS_DIRSYNC) +#define MS_DIRSYNC S_WRITE +#endif + +/* + * Some old glibc headers don't correctly define MS_POSIXACL and + * instead leave it undefined. When using these older headers define + * MS_POSIXACL to the reserved value of (1<<16). + */ +#if !defined(MS_POSIXACL) +#define MS_POSIXACL (1<<16) +#endif + +#define MS_NOSUID MNT_NOSUID +#define MS_NOEXEC MNT_NOEXEC +#define MS_NODEV MNT_NODEV +#define S_WRITE 0 +#define MS_BIND 0 +#define MS_REMOUNT MNT_UPDATE +#define MS_SYNCHRONOUS MNT_SYNCHRONOUS + +#define MS_USERS (MS_NOEXEC|MS_NOSUID|MS_NODEV) +#define MS_OWNER (MS_NOSUID|MS_NODEV) +#define MS_GROUP (MS_NOSUID|MS_NODEV) +#define MS_COMMENT 0 +#define MS_FORCE MNT_FORCE +#define MS_DETACH MNT_DETACH +#define MS_OVERLAY MNT_UNION +#define MS_CRYPT MNT_CPROTECT + +#endif /* _LIBSPL_SYS_MOUNT_H */ diff --git a/lib/libspl/include/os/macos/sys/param.h b/lib/libspl/include/os/macos/sys/param.h new file mode 100644 index 000000000000..ca5bb154e3a0 --- /dev/null +++ b/lib/libspl/include/os/macos/sys/param.h @@ -0,0 +1,62 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBSPL_SYS_PARAM_H +#define _LIBSPL_SYS_PARAM_H + +#include_next +#include + +/* + * File system parameters and macros. + * + * The file system is made out of blocks of at most MAXBSIZE units, + * with smaller units (fragments) only in the last direct block. + * MAXBSIZE primarily determines the size of buffers in the buffer + * pool. It may be made larger without any effect on existing + * file systems; however making it smaller may make some file + * systems unmountable. + * + * Note that the blocked devices are assumed to have DEV_BSIZE + * "sectors" and that fragments must be some multiple of this size. + */ +#define MAXNAMELEN 256 + +#define UID_NOBODY 60001 /* user ID no body */ +#define GID_NOBODY UID_NOBODY +#define UID_NOACCESS 60002 /* user ID no access */ + +#define MAXUID UINT32_MAX /* max user id */ +#define MAXPROJID MAXUID /* max project id */ + +extern size_t spl_pagesize(void); +#define PAGESIZE (spl_pagesize()) + +extern int execvpe(const char *name, char * const argv[], char * const envp[]); + +struct zfs_handle; + +#endif diff --git a/lib/libspl/include/os/macos/sys/stat.h b/lib/libspl/include/os/macos/sys/stat.h new file mode 100644 index 000000000000..1c7858194db6 --- /dev/null +++ b/lib/libspl/include/os/macos/sys/stat.h @@ -0,0 +1,77 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#ifndef _LIBSPL_SYS_STAT_H +#define _LIBSPL_SYS_STAT_H + +#include_next + +#include +#include /* for BLKGETSIZE64 */ + +#define MAXOFFSET_T OFF_MAX + +#ifndef _KERNEL +#include +#endif + +static inline int +fstat_blk(int fd, struct stat *st) +{ + if (fstat(fd, st) == -1) + return (-1); + + /* In OS X we need to use ioctl to get the size of a block dev */ + if (st->st_mode & (S_IFBLK | S_IFCHR)) { + uint32_t blksize; + uint64_t blkcnt; + + if (ioctl(fd, DKIOCGETBLOCKSIZE, &blksize) < 0) { + return (-1); + } + if (ioctl(fd, DKIOCGETBLOCKCOUNT, &blkcnt) < 0) { + return (-1); + } + + st->st_size = (off_t)((uint64_t)blksize * blkcnt); + } + + return (0); +} + + +/* + * Deal with Linux use of 64 for everything. + * OsX has moved past it, dropped all 32 versions, and + * standard form is 64 bit. + */ + +#define stat64 stat +#define lstat64 lstat +#define fstat64 fstat +#define fstat64_blk fstat_blk +#define statfs64 statfs + +#endif /* _LIBSPL_SYS_STAT_H */ diff --git a/lib/libspl/include/os/macos/sys/sysmacros.h b/lib/libspl/include/os/macos/sys/sysmacros.h new file mode 100644 index 000000000000..7480eb85a559 --- /dev/null +++ b/lib/libspl/include/os/macos/sys/sysmacros.h @@ -0,0 +1,105 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBSPL_SYS_SYSMACROS_H +#define _LIBSPL_SYS_SYSMACROS_H + +/* common macros */ +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#endif +#ifndef ABS +#define ABS(a) ((a) < 0 ? -(a) : (a)) +#endif +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof (a) / sizeof (a[0])) +#endif +#ifndef DIV_ROUND_UP +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) +#endif + +#define makedevice(maj, min) makedev(maj, min) +#define _sysconf(a) sysconf(a) + +/* + * Compatibility macros/typedefs needed for Solaris -> Linux port + */ +#define P2ALIGN(x, align) ((x) & -(align)) +#define P2CROSS(x, y, align) (((x) ^ (y)) > (align) - 1) +#define P2ROUNDUP(x, align) (-(-(x) & -(align))) +#define P2ROUNDUP_TYPED(x, align, type) \ + (-(-(type)(x) & -(type)(align))) +#define P2BOUNDARY(off, len, align) \ + (((off) ^ ((off) + (len) - 1)) > (align) - 1) +#define P2PHASE(x, align) ((x) & ((align) - 1)) +#define P2NPHASE(x, align) (-(x) & ((align) - 1)) +#define P2NPHASE_TYPED(x, align, type) \ + (-(type)(x) & ((type)(align) - 1)) +#define ISP2(x) (((x) & ((x) - 1)) == 0) +#define IS_P2ALIGNED(v, a) ((((uintptr_t)(v)) & ((uintptr_t)(a) - 1)) == 0) + +/* + * Typed version of the P2* macros. These macros should be used to ensure + * that the result is correctly calculated based on the data type of (x), + * which is passed in as the last argument, regardless of the data + * type of the alignment. For example, if (x) is of type uint64_t, + * and we want to round it up to a page boundary using "PAGESIZE" as + * the alignment, we can do either + * P2ROUNDUP(x, (uint64_t)PAGESIZE) + * or + * P2ROUNDUP_TYPED(x, PAGESIZE, uint64_t) + */ +#define P2ALIGN_TYPED(x, align, type) \ + ((type)(x) & -(type)(align)) +#define P2PHASE_TYPED(x, align, type) \ + ((type)(x) & ((type)(align) - 1)) +#define P2NPHASE_TYPED(x, align, type) \ + (-(type)(x) & ((type)(align) - 1)) +#define P2ROUNDUP_TYPED(x, align, type) \ + (-(-(type)(x) & -(type)(align))) +#define P2END_TYPED(x, align, type) \ + (-(~(type)(x) & -(type)(align))) +#define P2PHASEUP_TYPED(x, align, phase, type) \ + ((type)(phase) - (((type)(phase) - (type)(x)) & -(type)(align))) +#define P2CROSS_TYPED(x, y, align, type) \ + (((type)(x) ^ (type)(y)) > (type)(align) - 1) +#define P2SAMEHIGHBIT_TYPED(x, y, type) \ + (((type)(x) ^ (type)(y)) < ((type)(x) & (type)(y))) + + +/* avoid any possibility of clashing with version */ +#if defined(_KERNEL) && !defined(_KMEMUSER) && !defined(offsetof) +#define offsetof(s, m) ((size_t)(&(((s *)0)->m))) +#endif + +#ifndef RLIM64_INFINITY +#define RLIM64_INFINITY (~0ULL) +#endif + +#endif /* _LIBSPL_SYS_SYSMACROS_H */ diff --git a/lib/libspl/include/os/macos/sys/time.h b/lib/libspl/include/os/macos/sys/time.h new file mode 100644 index 000000000000..98b2ec9ee9bd --- /dev/null +++ b/lib/libspl/include/os/macos/sys/time.h @@ -0,0 +1,79 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _LIBSPL_SYS_OSX_TIME_H +#define _LIBSPL_SYS_OSX_TIME_H + +#include_next +#include +#include + +/* + * clock_gettime() is defined from 10.12 (High Sierra) onwards. + * For older platforms, we define in here. + */ + +#if !defined(MAC_OS_X_VERSION_10_12) || \ + (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12) + +#include +#include +#include +#include +#include +#include + + +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC_RAW 4 +#define CLOCK_MONOTONIC 6 + +static inline int +clock_gettime(clockid_t clk_id, struct timespec *tp) +{ + int retval = 0; + struct timeval now; + clock_serv_t cclock; + mach_timespec_t mts; + + switch (clk_id) { + case CLOCK_MONOTONIC_RAW: + case CLOCK_MONOTONIC: + + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, + &cclock); + retval = clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + + tp->tv_sec = mts.tv_sec; + tp->tv_nsec = mts.tv_nsec; + break; + case CLOCK_REALTIME: + gettimeofday(&now, NULL); + tp->tv_sec = now.tv_sec; + tp->tv_nsec = now.tv_usec * 1000; + break; + } + return (0); +} +#endif + +#endif diff --git a/lib/libspl/include/os/macos/sys/uio.h b/lib/libspl/include/os/macos/sys/uio.h new file mode 100644 index 000000000000..f646b3e5d5e0 --- /dev/null +++ b/lib/libspl/include/os/macos/sys/uio.h @@ -0,0 +1,175 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * University Copyright- Copyright (c) 1982, 1986, 1988 + * The Regents of the University of California + * All Rights Reserved + * + * University Acknowledgment- Portions of this document are derived from + * software developed by the University of California, Berkeley, and its + * contributors. + */ + +#ifndef _LIBSPL_SYS_UIO_H +#define _LIBSPL_SYS_UIO_H + +#include +#include_next + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* + * I/O parameter information. A uio structure describes the I/O which + * is to be performed by an operation. Typically the data movement will + * be performed by a routine such as uiomove(), which updates the uio + * structure to reflect what was done. + */ + +typedef struct iovec iovec_t; + + +/* + * I/O direction. + */ +// typedef enum uio_rw { UIO_READ, UIO_WRITE } uio_rw_t; + +/* + * Segment flag values. + */ +typedef enum uio_seg { UIO_USERSPACE, UIO_SYSSPACE, UIO_USERISPACE } uio_seg_t; + + +struct uio { + struct iovec *uio_iov; /* pointer to array of iovecs */ + int uio_iovcnt; /* number of iovecs */ + off_t uio_offset; /* file offset */ + uio_seg_t uio_segflg; /* address space (kernel or user) */ + off_t uio_limit; /* u-limit (maximum byte offset) */ + ssize_t uio_resid; /* residual count */ + enum uio_rw uio_rw; + int uio_max_iovs; /* max iovecs this uio_t can hold */ + uint32_t uio_index; /* Current index */ +}; + + +uio_t *uio_create(int iovcount, off_t offset, int spacetype, int iodirection); +void uio_free(uio_t *uio); +int uio_addiov(uio_t *uio, user_addr_t baseaddr, user_size_t length); +int uio_isuserspace(uio_t *uio); +int uio_getiov(uio_t *uio, int index, user_addr_t *baseaddr, + user_size_t *length); +int uio_iovcnt(uio_t *uio); +off_t uio_offset(uio_t *uio); +void uio_update(uio_t *uio, user_size_t count); +uint64_t uio_resid(uio_t *uio); +user_addr_t uio_curriovbase(uio_t *uio); +user_size_t uio_curriovlen(uio_t *uio); +void uio_setoffset(uio_t *uio, off_t a_offset); +uio_t *uio_duplicate(uio_t *uio); +int uio_rw(uio_t *a_uio); +void uio_setrw(uio_t *a_uio, int a_value); + +int uiomove(void *, uint32_t, enum uio_rw, struct uio *); +int spllib_uiomove(const uint8_t *, uint32_t, struct uio *); +void uioskip(struct uio *, uint32_t); +int uiodup(struct uio *, struct uio *, iovec_t *, int); + +// xuio struct is not used in this platform, but we define it +// to allow compilation and easier patching +typedef enum xuio_type { + UIOTYPE_ASYNCIO, + UIOTYPE_ZEROCOPY, +} xuio_type_t; + + +#define UIOA_IOV_MAX 16 + +typedef struct uioa_page_s { + int uioa_pfncnt; + void **uioa_ppp; + caddr_t uioa_base; + size_t uioa_len; +} uioa_page_t; + +typedef struct xuio { + uio_t *xu_uio; + enum xuio_type xu_type; + union { + struct { + uint32_t xu_a_state; + ssize_t xu_a_mbytes; + uioa_page_t *xu_a_lcur; + void **xu_a_lppp; + void *xu_a_hwst[4]; + uioa_page_t xu_a_locked[UIOA_IOV_MAX]; + } xu_aio; + struct { + int xu_zc_rw; + void *xu_zc_priv; + } xu_zc; + } xu_ext; +} xuio_t; + +#define XUIO_XUZC_PRIV(xuio) xuio->xu_ext.xu_zc.xu_zc_priv +#define XUIO_XUZC_RW(xuio) xuio->xu_ext.xu_zc.xu_zc_rw + +/* + * same as uiomove() but doesn't modify uio structure. + * return in cbytes how many bytes were copied. + */ +static inline int uiocopy(const unsigned char *p, uint32_t n, + enum uio_rw rw, struct uio *uio, uint64_t *cbytes) +{ + int result; + struct uio *nuio = uio_duplicate(uio); + unsigned long long x = uio_resid(uio); + if (!nuio) + return (ENOMEM); + uio_setrw(nuio, rw); + result = spllib_uiomove(p, n, nuio); + *cbytes = (x - uio_resid(nuio)); + uio_free(nuio); + return (result); +} + +// Apple's uiomove puts the uio_rw in uio_create +#define uiomove(A, B, C, D) spllib_uiomove((A), (B), (D)) +#define uioskip(A, B) uio_update((A), (B)) + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_UIO_H */ diff --git a/lib/libspl/include/os/macos/sys/vfs.h b/lib/libspl/include/os/macos/sys/vfs.h new file mode 100644 index 000000000000..a2ffcc08d877 --- /dev/null +++ b/lib/libspl/include/os/macos/sys/vfs.h @@ -0,0 +1,26 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#ifndef ZFS_SYS_VFS_H_ +#define ZFS_SYS_VFS_H_ + +#endif /* !ZFS_SYS_VFS_H_ */ diff --git a/lib/libspl/include/os/macos/sys/xattr.h b/lib/libspl/include/os/macos/sys/xattr.h new file mode 100644 index 000000000000..045f681b1e23 --- /dev/null +++ b/lib/libspl/include/os/macos/sys/xattr.h @@ -0,0 +1,35 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#ifndef _LIBSPL_SYS_XATTR_H +#define _LIBSPL_SYS_XATTR_H + +#include_next + +/* macOS has one more argument */ +#define setxattr(A, B, C, D, E) setxattr(A, B, C, D, E, 0) +#define getxattr(A, B, C, D, E) getxattr(A, B, C, D, E, 0) + +#endif diff --git a/lib/libspl/include/os/macos/sys/zfs_context_os.h b/lib/libspl/include/os/macos/sys/zfs_context_os.h new file mode 100644 index 000000000000..524f7a61d213 --- /dev/null +++ b/lib/libspl/include/os/macos/sys/zfs_context_os.h @@ -0,0 +1,45 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#ifndef ZFS_CONTEXT_OS_H_ +#define ZFS_CONTEXT_OS_H_ + +#include + +#define ZFS_EXPORTS_PATH "/etc/exports" +#define MNTTYPE_ZFS_SUBTYPE ('Z'<<24|'F'<<16|'S'<<8) + +struct spa_iokit; +typedef struct spa_iokit spa_iokit_t; + +typedef off_t loff_t; + +#define zc_fd_offset zc_zoneid + +struct zfs_handle; + +extern void zfs_rollback_os(struct zfs_handle *zhp); +extern void libzfs_macos_wrapfd(int *srcfd, boolean_t send); +extern void libzfs_macos_wrapclose(void); +extern int libzfs_macos_pipefd(int *read_fd, int *write_fd); + +#endif diff --git a/lib/libspl/include/os/macos/time.h b/lib/libspl/include/os/macos/time.h new file mode 100644 index 000000000000..e48e8e59e326 --- /dev/null +++ b/lib/libspl/include/os/macos/time.h @@ -0,0 +1,74 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _LIBSPL_TIME_H +#define _LIBSPL_TIME_H + +#include_next +#include +#include + +/* Linux also has a timer_create() API we need to emulate. */ + +/* + * OsX version can probably be implemented by using: + * dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); + * dispatch_source_set_event_handler(timer1, ^{vector1(timer1);}); + * dispatch_source_set_cancel_handler(timer1 + * dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC); + * dispatch_source_set_timer(timer1, start, NSEC_PER_SEC / 5, 0); + */ + +typedef void *timer_t; + +#if !defined(MAC_OS_X_VERSION_10_12) || \ + (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12) +typedef int clockid_t; +#endif + +struct itimerspec { + struct timespec it_interval; /* timer period */ + struct timespec it_value; /* timer expiration */ +}; + +struct sigevent; + +static inline int +timer_create(clockid_t clockid, struct sigevent *sevp, + timer_t *timerid) +{ + return (0); +} + +static inline int +timer_settime(timer_t id, int flags, + const struct itimerspec *its, struct itimerspec *remainvalue) +{ + return (0); +} + +static inline int +timer_delete(timer_t id) +{ + return (0); +} + +#endif diff --git a/lib/libspl/include/os/macos/unistd.h b/lib/libspl/include/os/macos/unistd.h new file mode 100644 index 000000000000..72f991c0d907 --- /dev/null +++ b/lib/libspl/include/os/macos/unistd.h @@ -0,0 +1,79 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#ifndef _LIBSPL_UNISTD_H +#define _LIBSPL_UNISTD_H + +#include_next +#include +#include + +/* Handle Linux use of 64 names */ + +#define open64 open +#define pread64 pread +#define pwrite64 pwrite +#define ftruncate64 ftruncate +#define lseek64 lseek + + +static inline int +fdatasync(int fd) +{ + if (fcntl(fd, F_FULLFSYNC) == -1) + return (-1); + return (0); +} + +#ifndef _SC_PHYS_PAGES +#define _SC_PHYS_PAGES 200 +#endif + +static inline int +pipe2(int fildes[2], int flags) +{ + int rv; + int old; + + if ((rv = pipe(fildes)) != 0) + return (rv); + + if (flags & O_NONBLOCK) { + old = fcntl(fildes[0], F_GETFL); + if (old >= 0) + fcntl(fildes[0], F_SETFL, old | O_NONBLOCK); + old = fcntl(fildes[1], F_GETFL); + if (old >= 0) + fcntl(fildes[1], F_SETFL, old | O_NONBLOCK); + } + if (flags & O_CLOEXEC) { + old = fcntl(fildes[0], F_GETFD); + if (old >= 0) + fcntl(fildes[0], F_SETFD, old | FD_CLOEXEC); + old = fcntl(fildes[1], F_GETFD); + if (old >= 0) + fcntl(fildes[1], F_SETFD, old | FD_CLOEXEC); + } + return (0); +} + + +#endif diff --git a/lib/libspl/include/sys/isa_defs.h b/lib/libspl/include/sys/isa_defs.h index 8c0932f57654..cd739166449d 100644 --- a/lib/libspl/include/sys/isa_defs.h +++ b/lib/libspl/include/sys/isa_defs.h @@ -128,15 +128,21 @@ extern "C" { /* arm arch specific defines */ #elif defined(__arm) || defined(__arm__) || defined(__aarch64__) +/* We can NOT define __arm / __arm__ on macOS, it is only for 32bit */ #if !defined(__arm) #define __arm #endif +#ifndef __APPLE__ #if !defined(__arm__) #define __arm__ #endif +#endif #if defined(__aarch64__) +#if !defined(__arm64) +#define __arm64 +#endif #if !defined(_LP64) #define _LP64 #endif diff --git a/lib/libspl/os/macos/getexecname.c b/lib/libspl/os/macos/getexecname.c new file mode 100644 index 000000000000..f6cac8a259dd --- /dev/null +++ b/lib/libspl/os/macos/getexecname.c @@ -0,0 +1,31 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#include +#include +#include + +const char * +getexecname_impl(char *execname) +{ + return (getprogname()); +} diff --git a/lib/libspl/os/macos/gethostid.c b/lib/libspl/os/macos/gethostid.c new file mode 100644 index 000000000000..434d37004a9c --- /dev/null +++ b/lib/libspl/os/macos/gethostid.c @@ -0,0 +1,38 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2020, Jorgen Lundman + */ + +#include +#include +#include +#include + +unsigned long +get_system_hostid(void) +{ + size_t len; + uint32_t myhostid = 0; + len = sizeof (myhostid); + sysctlbyname("kern.hostid", &myhostid, &len, NULL, 0); + return (myhostid); +} diff --git a/lib/libspl/os/macos/getmntany.c b/lib/libspl/os/macos/getmntany.c new file mode 100644 index 000000000000..0a7c26c2b1fc --- /dev/null +++ b/lib/libspl/os/macos/getmntany.c @@ -0,0 +1,487 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Ricardo Correia. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1988 AT&T */ +/* All Rights Reserved */ + +#include +#include +#include +#include /* for isspace() */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DIFF(xx) ((mrefp->xx != NULL) && \ + (mgetp->xx == NULL || strcmp(mrefp->xx, mgetp->xx) != 0)) + +static struct statfs *gsfs = NULL; +static int allfs = 0; +/* + * We will also query the extended filesystem capabilities API, to lookup + * other mount options, for example, XATTR. We can not use the MNTNOUSERXATTR + * option due to VFS rejecting with EACCESS. + */ + +#include +typedef struct attrlist attrlist_t; + +struct attrBufS { + u_int32_t length; + vol_capabilities_set_t caps; +} __attribute__((aligned(4), packed)); + + +DIR * +fdopendir(int fd) +{ + char fullpath[MAXPATHLEN]; + + if (fcntl(fd, F_GETPATH, fullpath) < 0) { + perror("fcntl"); + return (NULL); + } + if (close(fd) < 0) { + return (NULL); + } + + return (opendir(fullpath)); +} + +static int +chdir_block_begin(int newroot_fd) +{ + int cwdfd, error; + + cwdfd = open(".", O_RDONLY | O_DIRECTORY); + if (cwdfd == -1) + return (-1); + + if (fchdir(newroot_fd) == -1) { + error = errno; + (void) close(cwdfd); + errno = error; + return (-1); + } + return (cwdfd); +} + +static void +chdir_block_end(int cwdfd) +{ + int error = errno; + (void) fchdir(cwdfd); + (void) close(cwdfd); + errno = error; +} + +int +openat64(int dirfd, const char *path, int flags, ...) +{ + int cwdfd, filefd; + + if ((cwdfd = chdir_block_begin(dirfd)) == -1) + return (-1); + + if ((flags & O_CREAT) != 0) { + va_list ap; + int mode; + + va_start(ap, flags); + mode = va_arg(ap, int); + va_end(ap); + + filefd = open(path, flags, mode); + } else + filefd = open(path, flags); + + chdir_block_end(cwdfd); + return (filefd); +} + +int +fstatat64(int dirfd, const char *path, struct stat *statbuf, int flag) +{ + int cwdfd, error; + + if ((cwdfd = chdir_block_begin(dirfd)) == -1) + return (-1); + + if (flag == AT_SYMLINK_NOFOLLOW) + error = lstat(path, statbuf); + else + error = stat(path, statbuf); + + chdir_block_end(cwdfd); + return (error); +} + + +static char * +mntopt(char **p) +{ + char *cp = *p; + char *retstr; + + while (*cp && isspace(*cp)) + cp++; + + retstr = cp; + while (*cp && *cp != ',') + cp++; + + if (*cp) { + *cp = '\0'; + cp++; + } + + *p = cp; + return (retstr); +} + +char * +hasmntopt(struct mnttab *mnt, char *opt) +{ + char tmpopts[256]; + char *f, *opts = tmpopts; + + if (mnt->mnt_mntopts == NULL) + return (NULL); + (void) strlcpy(opts, mnt->mnt_mntopts, 256); + f = mntopt(&opts); + for (; *f; f = mntopt(&opts)) { + if (strncmp(opt, f, strlen(opt)) == 0) + return (f - tmpopts + mnt->mnt_mntopts); + } + return (NULL); +} + +static void +optadd(char *mntopts, size_t size, const char *opt) +{ + + if (mntopts[0] != '\0') + strlcat(mntopts, ",", size); + strlcat(mntopts, opt, size); +} + + +#include +#include +#include +#include +#include + + +static char * +MYCFStringCopyUTF8String(CFStringRef aString) +{ + if (aString == NULL) + return (NULL); + + CFIndex length = CFStringGetLength(aString); + CFIndex maxSize = + CFStringGetMaximumSizeForEncoding(length, + kCFStringEncodingUTF8); + char *buffer = (char *)malloc(maxSize); + if (CFStringGetCString(aString, buffer, maxSize, + kCFStringEncodingUTF8)) { + return (buffer); + } + return (NULL); +} + +/* + * Given "/dev/disk6" connect to IOkit and fetch the dataset + * name "BOOM/lower", and use it instead. + * Return 0 for no match (not ZFS) + * Return 1 for ZFS (path expanded) + */ +static int +expand_disk_to_zfs(char *devname, int len) +{ + char *result = NULL; + CFMutableDictionaryRef matchingDict; + io_service_t service; + CFStringRef cfstr; + char *device; + + if (strncmp(devname, "/dev/disk", 9) != 0) + return (0); + + device = &devname[5]; + + matchingDict = IOBSDNameMatching(kIOMasterPortDefault, 0, device); + if (NULL == matchingDict) + return (0); + + /* + * Fetch the object with the matching BSD node name. + * Note that there should only be one match, so + * IOServiceGetMatchingService is used instead of + * IOServiceGetMatchingServices to simplify the code. + */ + service = IOServiceGetMatchingService(kIOMasterPortDefault, + matchingDict); + + if (IO_OBJECT_NULL == service) + return (0); + + cfstr = IORegistryEntryCreateCFProperty(service, + CFSTR("ZFS Dataset"), kCFAllocatorDefault, 0); + if (cfstr) { + result = MYCFStringCopyUTF8String(cfstr); + CFRelease(cfstr); + } + + IOObjectRelease(service); + + if (result) { + strlcpy(devname, result, len); + free(result); + return (1); + } + return (0); +} + +void +statfs2mnttab(struct statfs *sfs, struct mnttab *mp) +{ + static char mntopts[MNTMAXSTR]; + long flags; + + mntopts[0] = '\0'; + + flags = sfs->f_flags; +#define OPTADD(opt) optadd(mntopts, sizeof (mntopts), (opt)) + if (flags & MNT_RDONLY) + OPTADD(MNTOPT_RO); + else + OPTADD(MNTOPT_RW); + if (flags & MNT_NOSUID) +#ifdef __FreeBSD__ + OPTADD(MNTOPT_NOSUID); +#elif defined(__APPLE__) + OPTADD(MNTOPT_NOSETUID); +#endif + else + OPTADD(MNTOPT_SETUID); + if (flags & MNT_UPDATE) + OPTADD(MNTOPT_REMOUNT); + if (flags & MNT_NOATIME) + OPTADD(MNTOPT_NOATIME); + else + OPTADD(MNTOPT_ATIME); + { + struct attrBufS attrBuf; + attrlist_t attrList; + + memset(&attrList, 0, sizeof (attrList)); + attrList.bitmapcount = ATTR_BIT_MAP_COUNT; + attrList.volattr = ATTR_VOL_INFO|ATTR_VOL_CAPABILITIES; + + if (getattrlist(sfs->f_mntonname, &attrList, &attrBuf, + sizeof (attrBuf), 0) == 0) { + + if (attrBuf.caps[VOL_CAPABILITIES_INTERFACES] & + VOL_CAP_INT_EXTENDED_ATTR) { + OPTADD(MNTOPT_XATTR); + } else { + OPTADD(MNTOPT_NOXATTR); + } // If EXTENDED + } // if getattrlist + } + if (flags & MNT_NOEXEC) + OPTADD(MNTOPT_NOEXEC); + else + OPTADD(MNTOPT_EXEC); + if (flags & MNT_NODEV) + OPTADD(MNTOPT_NODEVICES); + else + OPTADD(MNTOPT_DEVICES); + if (flags & MNT_DONTBROWSE) + OPTADD(MNTOPT_NOBROWSE); + else + OPTADD(MNTOPT_BROWSE); + if (flags & MNT_IGNORE_OWNERSHIP) + OPTADD(MNTOPT_NOOWNERS); + else + OPTADD(MNTOPT_OWNERS); + +#undef OPTADD + + // If a disk is /dev/diskX, lets see if it has "zfs_dataset_name" + // set, and if so, use it instead, for mount matching - also update + // fstypename, as libzfs_mnttab_find() checks for it. + int is_actually_zfs = 0; + + // This is rather unattractive, is there a better way. + libzfs_handle_t *g_zfs = NULL; + if (g_zfs == NULL) + g_zfs = libzfs_init(); + + if (expand_disk_to_zfs(sfs->f_mntfromname, sizeof (sfs->f_mntfromname))) + is_actually_zfs = 1; + // check if it is a valid dataset (fastpast, first char isn't '/' + // in case mimic is enabled + else if (sfs->f_mntfromname[0] != '/' && + g_zfs != NULL && + zfs_dataset_exists(g_zfs, sfs->f_mntfromname, ZFS_TYPE_DATASET)) + is_actually_zfs = 1; + + if (is_actually_zfs) + mp->mnt_fstype = strdup(MNTTYPE_ZFS); + else + mp->mnt_fstype = strdup(sfs->f_fstypename); + + mp->mnt_special = strdup(sfs->f_mntfromname); + mp->mnt_mountp = strdup(sfs->f_mntonname); + mp->mnt_mntopts = strdup(mntopts); + + // Apple addition + mp->mnt_fssubtype = sfs->f_fssubtype; + +} + +static int +statfs_init(void) +{ + struct statfs *sfs; + int error; + + if (gsfs != NULL) { + free(gsfs); + gsfs = NULL; + } + allfs = getfsstat(NULL, 0, MNT_NOWAIT); + if (allfs == -1) + goto fail; + gsfs = malloc(sizeof (gsfs[0]) * allfs * 2); + if (gsfs == NULL) + goto fail; + allfs = getfsstat(gsfs, (long)(sizeof (gsfs[0]) * allfs * 2), + MNT_NOWAIT); + if (allfs == -1) + goto fail; + sfs = realloc(gsfs, allfs * sizeof (gsfs[0])); + if (sfs != NULL) + gsfs = sfs; + return (0); +fail: + error = errno; + if (gsfs != NULL) + free(gsfs); + gsfs = NULL; + allfs = 0; + return (error); +} + +int +getmntany(FILE *fd __unused, struct mnttab *mgetp, struct mnttab *mrefp) +{ + int i, error; + + error = statfs_init(); + if (error != 0) + return (error); + + for (i = 0; i < allfs; i++) { + statfs2mnttab(&gsfs[i], mgetp); + if (mrefp->mnt_special != NULL && mgetp->mnt_special != NULL && + strcmp(mrefp->mnt_special, mgetp->mnt_special) != 0) { + continue; + } + if (mrefp->mnt_mountp != NULL && mgetp->mnt_mountp != NULL && + strcmp(mrefp->mnt_mountp, mgetp->mnt_mountp) != 0) { + continue; + } + if (mrefp->mnt_fstype != NULL && mgetp->mnt_fstype != NULL && + strcmp(mrefp->mnt_fstype, mgetp->mnt_fstype) != 0) { + continue; + } + return (0); + } + return (-1); +} + +int +getmntent(FILE *fp, struct mnttab *mp) +{ + static int index = -1; + int error = 0; + + if (index < 0) { + error = statfs_init(); + } + + if (error != 0) + return (error); + + index++; + + // If we have finished "reading" the mnttab, reset it to + // start from the beginning, and return EOF. + if (index >= allfs) { + index = -1; + return (-1); + } + + statfs2mnttab(&gsfs[index], mp); + return (0); +} + +int +getextmntent(const char *path, struct extmnttab *entry, struct stat64 *statbuf) +{ + struct statfs sfs; + + if (strlen(path) >= MAXPATHLEN) { + (void) fprintf(stderr, "invalid object; pathname too long\n"); + return (-1); + } + + if (stat64(path, statbuf) != 0) { + (void) fprintf(stderr, "cannot open '%s': %s\n", + path, strerror(errno)); + return (-1); + } + + if (statfs(path, &sfs) != 0) { + (void) fprintf(stderr, "%s: %s\n", path, + strerror(errno)); + return (-1); + } + statfs2mnttab(&sfs, (struct mnttab *)entry); + return (0); +} diff --git a/lib/libspl/os/macos/zone.c b/lib/libspl/os/macos/zone.c new file mode 100644 index 000000000000..a1e4f524094c --- /dev/null +++ b/lib/libspl/os/macos/zone.c @@ -0,0 +1,28 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#include + +zoneid_t +getzoneid() +{ + return (GLOBAL_ZONEID); +} diff --git a/lib/libzfs/Makefile.am b/lib/libzfs/Makefile.am index fb7533bdd943..1f7f3a9ab9a4 100644 --- a/lib/libzfs/Makefile.am +++ b/lib/libzfs/Makefile.am @@ -95,6 +95,8 @@ libzfs_la_LIBADD += -lutil -lgeom endif if BUILD_MACOS +libzfs_la_LIBADD += \ + $(abs_top_builddir)/lib/os/macos/libdiskmgt/libdiskmgt.la libzfs_la_LDFLAGS += -framework IOKit -lssl endif diff --git a/lib/libzfs/libzfs_dataset.c b/lib/libzfs/libzfs_dataset.c index 5aea297c37f8..82c8a3a8655f 100644 --- a/lib/libzfs/libzfs_dataset.c +++ b/lib/libzfs/libzfs_dataset.c @@ -2718,15 +2718,19 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, #ifdef __APPLE__ /* - * On OSX by default we mount pools under /Volumes unless - * the dataset property mountpoint specifies otherwise. - * In addition to this, there is an undocumented environment - * variable __ZFS_MAIN_MOUNTPOINT_DIR, used mainly by the - * testing environment, as it expects "/" by default. + * On OSX by default we mount pools under /Volumes + * unless the dataset property mountpoint specifies + * otherwise. + * In addition to this, there is an undocumented + * environment variable __ZFS_MAIN_MOUNTPOINT_DIR, + * used mainly by the testing environment, as it + * expects "/" by default. */ char *default_mountpoint; - default_mountpoint = getenv("__ZFS_MAIN_MOUNTPOINT_DIR"); - if (!default_mountpoint) default_mountpoint = "/Volumes/"; + default_mountpoint = + getenv("__ZFS_MAIN_MOUNTPOINT_DIR"); + if (!default_mountpoint) + default_mountpoint = "/Volumes/"; if (relpath[0] == '\0') (void) snprintf(propbuf, proplen, "%s%s", @@ -2734,8 +2738,8 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, else (void) snprintf(propbuf, proplen, "%s%s%s%s", root, str, source == NULL || - source[0] == '\0' ? default_mountpoint : "/", - relpath); + source[0] == '\0' ? default_mountpoint : + "/", relpath); #else @@ -3855,7 +3859,6 @@ zfs_destroy(zfs_handle_t *zhp, boolean_t defer) int retry = 0; do { if ((retry++) != 1) { - printf("retry\n"); sleep(1); } #endif diff --git a/lib/libzfs/libzfs_diff.c b/lib/libzfs/libzfs_diff.c index 12e079b0eeb7..dbf7aefdcbaa 100644 --- a/lib/libzfs/libzfs_diff.c +++ b/lib/libzfs/libzfs_diff.c @@ -731,7 +731,12 @@ zfs_show_diffs(zfs_handle_t *zhp, int outfd, const char *fromsnap, return (-1); } +#if defined(__APPLE__) + /* Can't do IO on pipes, open fds mkfifo */ + if (libzfs_macos_pipefd(&pipefd[0], &pipefd[1])) { +#else if (pipe2(pipefd, O_CLOEXEC)) { +#endif zfs_error_aux(zhp->zfs_hdl, strerror(errno)); teardown_differ_info(&di); return (zfs_error(zhp->zfs_hdl, EZFS_PIPEFAILED, errbuf)); diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c index 511895d18658..5c884b0fd8e0 100644 --- a/lib/libzfs/libzfs_sendrecv.c +++ b/lib/libzfs/libzfs_sendrecv.c @@ -1109,6 +1109,12 @@ dump_snapshot(zfs_handle_t *zhp, void *arg) } if (!sdd->dryrun) { + +#if defined(__APPLE__) + /* Can't do IO on pipes, possibly wrap fd in domain socket */ + libzfs_macos_wrapfd(&sdd->outfd, B_TRUE); +#endif + /* * If progress reporting is requested, spawn a new thread to * poll ZFS_IOC_SEND_PROGRESS at a regular interval. @@ -1770,6 +1776,11 @@ zfs_send_resume_impl(libzfs_handle_t *hdl, sendflags_t *flags, int outfd, } } +#if defined(__APPLE__) + /* Can't do IO on pipes, possibly wrap fd in domain socket */ + libzfs_macos_wrapfd(&outfd, B_TRUE); +#endif + error = lzc_send_resume_redacted(zhp->zfs_name, fromname, outfd, lzc_flags, resumeobj, resumeoff, redact_book); if (redact_book != NULL) @@ -2493,6 +2504,11 @@ zfs_send_one(zfs_handle_t *zhp, const char *from, int fd, sendflags_t *flags, if (flags->dryrun) return (0); +#if defined(__APPLE__) + /* Can't do IO on pipes, possibly wrap fd in domain socket */ + libzfs_macos_wrapfd(&fd, B_TRUE); +#endif + /* * If progress reporting is requested, spawn a new thread to poll * ZFS_IOC_SEND_PROGRESS at a regular interval. @@ -2583,6 +2599,7 @@ zfs_send_one(zfs_handle_t *zhp, const char *from, int fd, sendflags_t *flags, return (zfs_standard_error(hdl, errno, errbuf)); } } + return (err != 0); } @@ -4660,6 +4677,11 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, goto out; } +#if defined(__APPLE__) + /* Can't do IO on pipes, possibly wrap fd in domain socket */ + libzfs_macos_wrapfd(&infd, B_FALSE); +#endif + err = ioctl_err = lzc_receive_with_cmdprops(destsnap, rcvprops, oxprops, wkeydata, wkeylen, origin, flags->force, flags->resumable, raw, infd, drr_noswap, -1, &read_bytes, &errflags, @@ -5032,6 +5054,11 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, return (zfs_error(hdl, EZFS_NOENT, errbuf)); } +#if defined(__APPLE__) + /* Can't do IO on pipes, possibly wrap fd in domain socket */ + libzfs_macos_wrapfd(&infd, B_FALSE); +#endif + /* read in the BEGIN record */ if (0 != (err = recv_read(hdl, infd, &drr, sizeof (drr), B_FALSE, &zcksum))) diff --git a/lib/libzfs/os/macos/libzfs_mount_os.c b/lib/libzfs/os/macos/libzfs_mount_os.c new file mode 100644 index 000000000000..6c19e97563b6 --- /dev/null +++ b/lib/libzfs/os/macos/libzfs_mount_os.c @@ -0,0 +1,732 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2019 by Delphix. All rights reserved. + * Copyright 2016 Igor Kozhukhov + * Copyright 2017 RackTop Systems. + * Copyright (c) 2018 Datto Inc. + * Copyright 2018 OmniOS Community Edition (OmniOSce) Association. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libzfs_impl.h" +#include +#include +#include +#include + +#include +#include +#include + +/* + * The default OpenZFS icon. Compare against known values to see if it needs + * updating. Allowing users to set own. + * No file: copy icon + * correct size: do nothing + * other size: user custom icon, do nothing + */ + +/* icon name on root of a mount */ +#define MOUNT_POINT_CUSTOM_ICON ".VolumeIcon.icns" + +/* source icon name from inside zfs.kext bundle */ +#define CUSTOM_ICON_PATH \ + KERNEL_MODPREFIX "/zfs.kext/Contents/Resources/VolumeIcon.icns" + +#include + + +/* + * On OSX we can set the icon to an Open ZFS specific one, just to be extra + * shiny + */ +static void +zfs_mount_seticon(const char *mountpoint) +{ + /* For a root file system, add a volume icon. */ + ssize_t attrsize; + uint16_t finderinfo[16]; + struct stat sbuf; + char *path = NULL; + FILE *dstfp = NULL, *srcfp = NULL; + unsigned char buf[1024]; + unsigned int red; + + if (asprintf(&path, "%s/%s", mountpoint, MOUNT_POINT_CUSTOM_ICON) == -1) + return; + + /* If we can stat it, and it has a size, leave it be. */ + if ((stat(path, &sbuf) == 0 && sbuf.st_size > 0)) + goto out; + + /* Looks like we should copy the icon over */ + + /* check if we can read in the default ZFS icon */ + srcfp = fopen(CUSTOM_ICON_PATH, "r"); + + /* No source icon */ + if (!srcfp) + goto out; + + /* Open the output icon for writing */ + dstfp = fopen(path, "w"); + if (!dstfp) + goto out; + + /* Copy icon */ + while ((red = fread(buf, 1, sizeof (buf), srcfp)) > 0) + (void) fwrite(buf, 1, red, dstfp); + + /* We have copied it, set icon */ + attrsize = getxattr(mountpoint, XATTR_FINDERINFO_NAME, &finderinfo, + sizeof (finderinfo), 0); + if (attrsize != sizeof (finderinfo)) + (void) memset(&finderinfo, 0, sizeof (finderinfo)); + if ((finderinfo[4] & BE_16(0x0400)) == 0) { + finderinfo[4] |= BE_16(0x0400); + (void) setxattr(mountpoint, XATTR_FINDERINFO_NAME, &finderinfo, + sizeof (finderinfo), 0); + } + + /* Now tell Finder to update */ +#if 0 + int fd = -1; + strlcpy(template, mountpoint, sizeof (template)); + strlcat(template, "/tempXXXXXX", sizeof (template)); + if ((fd = mkstemp(template)) != -1) { + unlink(template); // Just delete it right away + close(fd); + } +#endif + +out: + if (dstfp != NULL) + fclose(dstfp); + if (srcfp != NULL) + fclose(srcfp); + if (path != NULL) + free(path); +} + +static void +check_special(zfs_handle_t *zhp) +{ + zpool_handle_t *zph = zhp->zpool_hdl; + uint64_t feat_refcount; + nvlist_t *features; + + /* check that features can be enabled */ + if (zpool_get_prop_int(zph, ZPOOL_PROP_VERSION, NULL) + < SPA_VERSION_FEATURES) + return; + + /* SPA_FEATURE_PROJECT_QUOTA SPA_FEATURE_USEROBJ_ACCOUNTING */ + features = zpool_get_features(zph); + if (!features) + return; + + if (nvlist_lookup_uint64(features, + spa_feature_table[SPA_FEATURE_PROJECT_QUOTA].fi_guid, + &feat_refcount) != 0 && + nvlist_lookup_uint64(features, + spa_feature_table[SPA_FEATURE_USEROBJ_ACCOUNTING].fi_guid, + &feat_refcount) != 0) + return; + + printf(gettext("If importing from zfs-1.9.4 (or earlier), " + "then possibly enable features: \n" + " project_quota & userobj_accounting\n")); + +} + +/* + * if (zmount(zhp, zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags, + * MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { + */ +int +do_mount(zfs_handle_t *zhp, const char *dir, char *optptr, int mflag) +{ + int rv; + const char *spec = zfs_get_name(zhp); + const char *fstype = MNTTYPE_ZFS; + struct zfs_mount_args mnt_args; + char *rpath = NULL; + zfs_cmd_t zc = { "\0" }; + int devdisk = ZFS_DEVDISK_POOLONLY; + int ispool = 0; // the pool dataset, that is + int optlen = 0; + char *value = NULL; + nvlist_t *args = NULL; + + assert(spec != NULL); + assert(dir != NULL); + assert(fstype != NULL); + assert(mflag >= 0); + + if (optptr != NULL) + optlen = strlen(optptr); + + /* + * Figure out if we want this mount as a /dev/diskX mount, if so + * ask kernel to create one for us, then use it to mount. + */ + + // Use dataset name by default + mnt_args.fspec = spec; + + /* + * Lookup the dataset property devdisk, and depending on its + * setting, we need to create a /dev/diskX for the mount + */ + if (zhp) { + + /* If we are in zfs-tests, no devdisks */ + if (getenv("__ZFS_MAIN_MOUNTPOINT_DIR") != NULL) + devdisk = ZFS_DEVDISK_OFF; + else + devdisk = zfs_prop_get_int(zhp, ZFS_PROP_DEVDISK); + + if (zhp && zhp->zpool_hdl && + strcmp(zpool_get_name(zhp->zpool_hdl), + zfs_get_name(zhp)) == 0) + ispool = 1; + + if ((devdisk == ZFS_DEVDISK_ON) || + ((devdisk == ZFS_DEVDISK_POOLONLY) && + ispool)) { + + strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); + + zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0); + + args = fnvlist_alloc(); + fnvlist_add_string(args, ZPOOL_CONFIG_POOL_NAME, + zhp->zfs_name); + rv = zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, args); + + if (rv == 0) + rv = zfs_ioctl(zhp->zfs_hdl, + ZFS_IOC_PROXY_DATASET, &zc); + + /* Free innvl */ + nvlist_free(args); + args = NULL; + + /* args = outnvl */ + + if (rv == 0 && + zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &args) == 0) + if (nvlist_exists(args, ZPOOL_CONFIG_PATH)) + value = fnvlist_lookup_string(args, + ZPOOL_CONFIG_PATH); + + zcmd_free_nvlists(&zc); + +#ifdef DEBUG + if (rv) + fprintf(stderr, + "proxy dataset returns %d '%s'\n", + rv, value ? value : ""); +#endif + + // Mount using /dev/diskX, use temporary buffer to + // give it full name + if (rv == 0 && value != NULL) { + snprintf(zc.zc_name, sizeof (zc.zc_name), + "/dev/%s", value); + mnt_args.fspec = zc.zc_name; + } + + /* free outnvl */ + if (args != NULL) + nvlist_free(args); + } + } + + // We don't pass flags to XNU, we use optstr + mflag = 0; + + // Some arguments need to be told to XNU + if (strstr(optptr, "remount") != NULL) + mflag |= MNT_UPDATE; + + mnt_args.mflag = mflag; + mnt_args.optptr = optptr; + mnt_args.optlen = optlen; + mnt_args.struct_size = sizeof (mnt_args); + + /* + * There is a bug in XNU where /var/tmp is resolved as + * "private/var/tmp" without the leading "/", and both mount(2) and + * diskutil mount avoid this by calling realpath() first. So we will + * do the same. + */ + rpath = realpath(dir, NULL); + +#ifdef ZFS_DEBUG + printf("%s calling mount with fstype %s, %s %s, fspec %s, mflag %d," + " optptr %s, optlen %d, devdisk %d, ispool %d\n", + __func__, fstype, (rpath ? "rpath" : "dir"), + (rpath ? rpath : dir), mnt_args.fspec, mflag, optptr, optlen, + devdisk, ispool); +#endif + rv = mount(fstype, rpath ? rpath : dir, mflag, &mnt_args); + + /* Check if we need to create/update icon */ + if (rv == 0) + zfs_mount_seticon(dir); + else + rv = errno; + + /* 1.9.4 did not have projectquotas, check if user should upgrade */ + if (rv == EIO) + check_special(zhp); + + + if (rpath) free(rpath); + + return (rv); +} + +static int +do_unmount_impl(const char *mntpt, int flags) +{ + char force_opt[] = "force"; + char *argv[7] = { + "/usr/sbin/diskutil", + "unmount", + NULL, NULL, NULL, NULL }; + int rc, count = 2; + + if (flags & MS_FORCE) { + argv[count] = force_opt; + count++; + } + + argv[count] = (char *)mntpt; + rc = libzfs_run_process(argv[0], argv, STDOUT_VERBOSE|STDERR_VERBOSE); + + /* + * There is a bug, where we can not unmount, with the error + * already unmounted, even though it wasn't. But it is easy + * to work around by calling 'umount'. Until a real fix is done... + * re-test this: 202004/lundman + */ + if (rc != 0) { + char *argv[7] = { + "/sbin/umount", + NULL, NULL, NULL, NULL }; + int count = 1; + + if (flags & MS_FORCE) { + argv[count] = "-f"; + count++; + } + argv[count] = (char *)mntpt; + rc = libzfs_run_process(argv[0], argv, + STDOUT_VERBOSE|STDERR_VERBOSE); + } + + return (rc ? EINVAL : 0); +} + + +void unmount_snapshots(zfs_handle_t *zhp, const char *mntpt, int flags); + +int +do_unmount(zfs_handle_t *zhp, const char *mntpt, int flags) +{ + int rv = 0; + /* + * On OSX, the kernel can not unmount all snapshots for us, as XNU + * rejects the unmount before it reaches ZFS. But we can easily handle + * unmounting snapshots from userland. + */ + unmount_snapshots(zhp, mntpt, flags); + + rv = do_unmount_impl(mntpt, flags); + + /* We might need to remove the proxy as well */ + if (rv == 0 && zhp != NULL) { + zfs_cmd_t zc = { "\0" }; + nvlist_t *args = NULL; + + strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); + + zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0); + + args = fnvlist_alloc(); + fnvlist_add_string(args, ZPOOL_CONFIG_POOL_NAME, + zhp->zfs_name); + rv = zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, args); + + if (rv == 0) + rv = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_PROXY_REMOVE, &zc); + + /* Free innvl */ + nvlist_free(args); + args = NULL; + + /* args = outnvl */ + + zcmd_free_nvlists(&zc); + + // We don't care about proxy failing + rv = 0; + } + + return (rv); +} + +/* + * Given "/Volumes/BOOM" look for any lower mounts with ".zfs/snapshot/" + * in them - issue unmount. + */ +void +unmount_snapshots(zfs_handle_t *zhp, const char *mntpt, int flags) +{ + struct mnttab entry; + int len = strlen(mntpt); + + if (zhp == NULL) + return; + + while (getmntent(NULL, &entry) == 0) { + /* Starts with our mountpoint ? */ + if (strncmp(mntpt, entry.mnt_mountp, len) == 0) { + /* The next part is "/.zfs/snapshot/" ? */ + if (strncmp("/.zfs/snapshot/", &entry.mnt_mountp[len], + 15) == 0) { + /* Unmount it */ + do_unmount_impl(entry.mnt_mountp, MS_FORCE); + } + } + } +} + +int +zfs_mount_delegation_check(void) +{ + return ((geteuid() != 0) ? EACCES : 0); +} + +static char * +zfs_snapshot_mountpoint(zfs_handle_t *zhp) +{ + char *dataset_name, *snapshot_mountpoint, *parent_mountpoint; + libzfs_handle_t *hdl = zhp->zfs_hdl; + zfs_handle_t *parent; + char *r; + + dataset_name = zfs_strdup(hdl, zhp->zfs_name); + if (dataset_name == NULL) { + (void) fprintf(stderr, gettext("not enough memory")); + return (NULL); + } + + r = strrchr(dataset_name, '@'); + + if (r == NULL) { + (void) fprintf(stderr, gettext("snapshot '%s' " + "has no '@'\n"), zhp->zfs_name); + free(dataset_name); + return (NULL); + } + + r[0] = 0; + + /* Open the dataset */ + if ((parent = zfs_open(hdl, dataset_name, + ZFS_TYPE_FILESYSTEM)) == NULL) { + (void) fprintf(stderr, + gettext("unable to open parent dataset '%s'\n"), + dataset_name); + free(dataset_name); + return (NULL); + } + + if (!zfs_is_mounted(parent, &parent_mountpoint)) { + (void) fprintf(stderr, + gettext("parent dataset '%s' must be mounted\n"), + dataset_name); + free(dataset_name); + zfs_close(parent); + return (NULL); + } + + zfs_close(parent); + + snapshot_mountpoint = + zfs_asprintf(hdl, "%s/.zfs/snapshot/%s/", + parent_mountpoint, &r[1]); + + free(dataset_name); + free(parent_mountpoint); + + return (snapshot_mountpoint); +} + +/* + * Mount a snapshot; called from "zfs mount dataset@snapshot". + * Given "dataset@snapshot" construct mountpoint path of the + * style "/mountpoint/dataset/.zfs/snapshot/$name/". Ensure + * parent "dataset" is mounted, then issue mount for snapshot. + */ +int +zfs_snapshot_mount(zfs_handle_t *zhp, const char *options, + int flags) +{ + int ret = 0; + char *mountpoint; + + /* + * The automounting will kick in, and zed mounts it - so + * we temporarily disable it + */ + uint64_t automount = 0; + uint64_t saved_automount = 0; + size_t len = sizeof (automount); + size_t slen = sizeof (saved_automount); + + /* Remember what the user has it set to */ + sysctlbyname("kstat.zfs.darwin.tunable.zfs_auto_snapshot", + &saved_automount, &slen, NULL, 0); + + /* Disable automounting */ + sysctlbyname("kstat.zfs.darwin.tunable.zfs_auto_snapshot", + NULL, NULL, &automount, len); + + if (zfs_is_mounted(zhp, NULL)) { + return (EBUSY); + } + + mountpoint = zfs_snapshot_mountpoint(zhp); + if (mountpoint == NULL) + return (EINVAL); + + ret = zfs_mount_at(zhp, options, MS_RDONLY | flags, + mountpoint); + + /* If zed is running, it can mount it before us */ + if (ret == -1 && errno == EINVAL) + ret = 0; + + if (ret == 0) { + (void) fprintf(stderr, + gettext("ZFS: snapshot mountpoint '%s'\n"), + mountpoint); + } + + free(mountpoint); + + /* Restore automount setting */ + sysctlbyname("kstat.zfs.darwin.tunable.zfs_auto_snapshot", + NULL, NULL, &saved_automount, len); + + return (ret); +} + +int +zfs_snapshot_unmount(zfs_handle_t *zhp, int flags) +{ + int ret = 0; + char *mountpoint; + + if (!zfs_is_mounted(zhp, NULL)) { + return (ENOENT); + } + + mountpoint = zfs_snapshot_mountpoint(zhp); + if (mountpoint == NULL) + return (EINVAL); + + ret = zfs_unmount(zhp, mountpoint, flags); + + free(mountpoint); + + return (ret); +} + +static int +do_unmount_volume(const char *mntpt, int flags) +{ + char force_opt[] = "force"; + char *argv[7] = { + "/usr/sbin/diskutil", + NULL, NULL, NULL, NULL }; + int rc, count = 1; + + // Check if ends with "s1" partition + int idx = strlen(mntpt); + while (idx > 0 && + isdigit(mntpt[idx])) + idx--; + if (mntpt[idx] == 's') + argv[count++] = "unmount"; + else + argv[count++] = "unmountDisk"; + + if (flags & MS_FORCE) { + argv[count] = force_opt; + count++; + } + + argv[count] = (char *)mntpt; + rc = libzfs_run_process(argv[0], argv, STDOUT_VERBOSE|STDERR_VERBOSE); + + return (rc ? EINVAL : 0); +} + +static void +zpool_disable_volume(const char *name) +{ + CFMutableDictionaryRef matching = 0; + char *fullname = NULL; + int result; + io_service_t service = 0; + io_service_t child; + char bsdstr[MAXPATHLEN]; + char bsdstr2[MAXPATHLEN]; + CFStringRef bsdname; + CFStringRef bsdname2; + io_iterator_t iter; + + printf("Exporting '%s'\n", name); + + if (asprintf(&fullname, "ZVOL %s Media", name) < 0) + return; + + matching = IOServiceNameMatching(fullname); + if (matching == 0) + goto out; + service = IOServiceGetMatchingService(kIOMasterPortDefault, matching); + if (service == 0) + goto out; + // printf("GetMatching said %p\n", service); + + // Get BSDName? + bsdname = IORegistryEntryCreateCFProperty(service, + CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0); + if (bsdname && + CFStringGetCString(bsdname, bsdstr, sizeof (bsdstr), + kCFStringEncodingUTF8)) { + // printf("BSDName '%s'\n", bsdstr); + + // Now loop through and check if apfs has any synthesized + // garbage attached, as they didnt make "diskutil unmountdisk" + // handle it, we have to do it manually. (minus 1 apple!) + + result = IORegistryEntryCreateIterator(service, + kIOServicePlane, + kIORegistryIterateRecursively, + &iter); + + // printf("iterating ret %d \n", result); + if (result == 0) { + while ((child = IOIteratorNext(iter)) != 0) { + + bsdname2 = IORegistryEntryCreateCFProperty( + child, CFSTR(kIOBSDNameKey), + kCFAllocatorDefault, 0); + + if (bsdname2 && + CFStringGetCString(bsdname2, bsdstr2, + sizeof (bsdstr2), kCFStringEncodingUTF8)) { + CFRelease(bsdname2); + printf( + "... asking apfs to eject '%s'\n", + bsdstr2); + do_unmount_volume(bsdstr2, 0); + + } // Has BSDName? + + IOObjectRelease(child); + } + IOObjectRelease(iter); + } // iterate + + CFRelease(bsdname); + printf("... asking ZVOL to export '%s'\n", bsdstr); + do_unmount_volume(bsdstr, 0); + } + +out: + if (service != 0) + IOObjectRelease(service); + if (fullname != NULL) + free(fullname); + +} + +static int +zpool_disable_volumes(zfs_handle_t *nzhp, void *data) +{ + // Same pool? + if (nzhp && nzhp->zpool_hdl && zpool_get_name(nzhp->zpool_hdl) && + data && + strcmp(zpool_get_name(nzhp->zpool_hdl), (char *)data) == 0) { + if (zfs_get_type(nzhp) == ZFS_TYPE_VOLUME) { + /* + * /var/run/zfs/zvol/dsk/$POOL/$volume + */ + + zpool_disable_volume(zfs_get_name(nzhp)); + + } + } + (void) zfs_iter_children(nzhp, zpool_disable_volumes, data); + zfs_close(nzhp); + return (0); +} + +/* + * Since volumes can be mounted, we need to ask diskutil to unmountdisk + * to make sure Spotlight and all that, let go of the mount. + */ +void +zpool_disable_datasets_os(zpool_handle_t *zhp, boolean_t force) +{ + libzfs_handle_t *hdl = zhp->zpool_hdl; + + zfs_iter_root(hdl, zpool_disable_volumes, (void *)zpool_get_name(zhp)); +} diff --git a/lib/libzfs/os/macos/libzfs_pool_os.c b/lib/libzfs/os/macos/libzfs_pool_os.c new file mode 100644 index 000000000000..f7685e2f01fb --- /dev/null +++ b/lib/libzfs/os/macos/libzfs_pool_os.c @@ -0,0 +1,345 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2018 by Delphix. All rights reserved. + * Copyright 2016 Igor Kozhukhov + * Copyright (c) 2018 Datto Inc. + * Copyright (c) 2017 Open-E, Inc. All Rights Reserved. + * Copyright (c) 2017, Intel Corporation. + * Copyright (c) 2018, loli10K + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zfs_namecheck.h" +#include "zfs_prop.h" +#include "libzfs_impl.h" +#include "zfs_comutil.h" +#include "zfeature_common.h" + +/* + * If the device has being dynamically expanded then we need to relabel + * the disk to use the new unallocated space. + */ +int +zpool_relabel_disk(libzfs_handle_t *hdl, const char *path, const char *msg) +{ + int fd, error; + + if ((fd = open(path, O_RDWR|O_DIRECT)) < 0) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot " + "relabel '%s': unable to open device: %d"), path, errno); + return (zfs_error(hdl, EZFS_OPENFAILED, msg)); + } + + /* + * It's possible that we might encounter an error if the device + * does not have any unallocated space left. If so, we simply + * ignore that error and continue on. + * + * Also, we don't call efi_rescan() - that would just return EBUSY. + * The module will do it for us in vdev_disk_open(). + */ + error = efi_use_whole_disk(fd); + + /* Flush the buffers to disk and invalidate the page cache. */ + (void) fsync(fd); +// (void) ioctl(fd, BLKFLSBUF); + + (void) close(fd); + if (error && error != VT_ENOSPC) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot " + "relabel '%s': unable to read disk capacity"), path); + return (zfs_error(hdl, EZFS_NOCAP, msg)); + } + return (0); +} + +/* + * Read the EFI label from the config, if a label does not exist then + * pass back the error to the caller. If the caller has passed a non-NULL + * diskaddr argument then we set it to the starting address of the EFI + * partition. + */ +static int +read_efi_label(nvlist_t *config, diskaddr_t *sb) +{ + char *path; + int fd; + char diskname[MAXPATHLEN]; + int err = -1; + + if (nvlist_lookup_string(config, ZPOOL_CONFIG_PATH, &path) != 0) + return (err); + + (void) snprintf(diskname, sizeof (diskname), "%s%s", DISK_ROOT, + strrchr(path, '/')); + if ((fd = open(diskname, O_RDONLY|O_DIRECT)) >= 0) { + struct dk_gpt *vtoc; + + if ((err = efi_alloc_and_read(fd, &vtoc)) >= 0) { + if (sb != NULL) + *sb = vtoc->efi_parts[0].p_start; + efi_free(vtoc); + } + (void) close(fd); + } + return (err); +} + +/* + * determine where a partition starts on a disk in the current + * configuration + */ +static diskaddr_t +find_start_block(nvlist_t *config) +{ + nvlist_t **child; + uint_t c, children; + diskaddr_t sb = MAXOFFSET_T; + uint64_t wholedisk; + + if (nvlist_lookup_nvlist_array(config, + ZPOOL_CONFIG_CHILDREN, &child, &children) != 0) { + if (nvlist_lookup_uint64(config, + ZPOOL_CONFIG_WHOLE_DISK, + &wholedisk) != 0 || !wholedisk) { + return (MAXOFFSET_T); + } + if (read_efi_label(config, &sb) < 0) + sb = MAXOFFSET_T; + return (sb); + } + + for (c = 0; c < children; c++) { + sb = find_start_block(child[c]); + if (sb != MAXOFFSET_T) { + return (sb); + } + } + return (MAXOFFSET_T); +} + +static int +zpool_label_disk_check(char *path) +{ + struct dk_gpt *vtoc; + int fd, err; + + if ((fd = open(path, O_RDONLY|O_DIRECT)) < 0) + return (errno); + + if ((err = efi_alloc_and_read(fd, &vtoc)) != 0) { + (void) close(fd); + return (err); + } + + if (vtoc->efi_flags & EFI_GPT_PRIMARY_CORRUPT) { + efi_free(vtoc); + (void) close(fd); + return (EIDRM); + } + + efi_free(vtoc); + (void) close(fd); + return (0); +} + +/* + * Generate a unique partition name for the ZFS member. Partitions must + * have unique names to ensure udev will be able to create symlinks under + * /dev/disk/by-partlabel/ for all pool members. The partition names are + * of the form -. + */ +static void +zpool_label_name(char *label_name, int label_size) +{ + uint64_t id = 0; + int fd; + + fd = open("/dev/urandom", O_RDONLY); + if (fd >= 0) { + if (read(fd, &id, sizeof (id)) != sizeof (id)) + id = 0; + + close(fd); + } + + if (id == 0) + id = (((uint64_t)rand()) << 32) | (uint64_t)rand(); + + snprintf(label_name, label_size, "zfs-%016llx", (u_longlong_t)id); +} + +/* + * Label an individual disk. The name provided is the short name, + * stripped of any leading /dev path. + */ +int +zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, const char *name) +{ + char path[MAXPATHLEN]; + struct dk_gpt *vtoc; + int rval, fd; + size_t resv = EFI_MIN_RESV_SIZE; + uint64_t slice_size; + diskaddr_t start_block; + char errbuf[1024]; + + /* prepare an error message just in case */ + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot label '%s'"), name); + + if (zhp) { + nvlist_t *nvroot; + + verify(nvlist_lookup_nvlist(zhp->zpool_config, + ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0); + + if (zhp->zpool_start_block == 0) + start_block = find_start_block(nvroot); + else + start_block = zhp->zpool_start_block; + zhp->zpool_start_block = start_block; + } else { + /* new pool */ + start_block = NEW_START_BLOCK; + } + + (void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name); + + if ((fd = open(path, O_RDWR|O_DIRECT|O_EXCL)) < 0) { + /* + * This shouldn't happen. We've long since verified that this + * is a valid device. + */ + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot " + "label '%s': unable to open device: %d"), path, errno); + return (zfs_error(hdl, EZFS_OPENFAILED, errbuf)); + } + + if (efi_alloc_and_init(fd, EFI_NUMPAR, &vtoc) != 0) { + /* + * The only way this can fail is if we run out of memory, or we + * were unable to read the disk's capacity + */ + if (errno == ENOMEM) + (void) no_memory(hdl); + + (void) close(fd); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot " + "label '%s': unable to read disk capacity"), path); + + return (zfs_error(hdl, EZFS_NOCAP, errbuf)); + } + + slice_size = vtoc->efi_last_u_lba + 1; + slice_size -= EFI_MIN_RESV_SIZE; + if (start_block == MAXOFFSET_T) + start_block = NEW_START_BLOCK; + slice_size -= start_block; + slice_size = P2ALIGN(slice_size, PARTITION_END_ALIGNMENT); + + vtoc->efi_parts[0].p_start = start_block; + vtoc->efi_parts[0].p_size = slice_size; + + /* + * Why we use V_USR: V_BACKUP confuses users, and is considered + * disposable by some EFI utilities (since EFI doesn't have a backup + * slice). V_UNASSIGNED is supposed to be used only for zero size + * partitions, and efi_write() will fail if we use it. V_ROOT, V_BOOT, + * etc. were all pretty specific. V_USR is as close to reality as we + * can get, in the absence of V_OTHER. + */ + vtoc->efi_parts[0].p_tag = V_USR; + zpool_label_name(vtoc->efi_parts[0].p_name, EFI_PART_NAME_LEN); + + vtoc->efi_parts[8].p_start = slice_size + start_block; + vtoc->efi_parts[8].p_size = resv; + vtoc->efi_parts[8].p_tag = V_RESERVED; + + rval = efi_write(fd, vtoc); + + /* Flush the buffers to disk and invalidate the page cache. */ + (void) fsync(fd); +// (void) ioctl(fd, BLKFLSBUF); + + if (rval == 0) + rval = efi_rescan(fd); + + /* + * Some block drivers (like pcata) may not support EFI GPT labels. + * Print out a helpful error message directing the user to manually + * label the disk and give a specific slice. + */ + if (rval != 0) { + (void) close(fd); + efi_free(vtoc); + + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "try using " + "parted(8) and then provide a specific slice: %d"), rval); + return (zfs_error(hdl, EZFS_LABELFAILED, errbuf)); + } + + (void) close(fd); + efi_free(vtoc); + + (void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name); + (void) zfs_append_partition(path, MAXPATHLEN); + + /* Wait to udev to signal use the device has settled. */ + rval = zpool_label_disk_wait(path, DISK_LABEL_WAIT); + if (rval) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "failed to " + "detect device partitions on '%s': %d"), path, rval); + return (zfs_error(hdl, EZFS_LABELFAILED, errbuf)); + } + + /* We can't be to paranoid. Read the label back and verify it. */ + (void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name); + rval = zpool_label_disk_check(path); + if (rval) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "freshly written " + "EFI label on '%s' is damaged. Ensure\nthis device " + "is not in use, and is functioning properly: %d"), + path, rval); + return (zfs_error(hdl, EZFS_LABELFAILED, errbuf)); + } + return (0); +} diff --git a/lib/libzfs/os/macos/libzfs_util_os.c b/lib/libzfs/os/macos/libzfs_util_os.c new file mode 100644 index 000000000000..56446953f112 --- /dev/null +++ b/lib/libzfs/os/macos/libzfs_util_os.c @@ -0,0 +1,725 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libzfs_impl.h" +#include "zfs_prop.h" +#include +#include +#include + +#define ZDIFF_SHARESDIR "/.zfs/shares/" + + +int +zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc) +{ + return (zfs_ioctl_fd(hdl->libzfs_fd, request, zc)); +} + +const char * +libzfs_error_init(int error) +{ + switch (error) { + case ENXIO: + return (dgettext(TEXT_DOMAIN, "The ZFS modules are not " + "loaded.\nTry running '/sbin/kextload zfs.kext' as root " + "to load them.")); + case ENOENT: + return (dgettext(TEXT_DOMAIN, "/dev/zfs and /proc/self/mounts " + "are required.\nTry running 'udevadm trigger' and 'mount " + "-t proc proc /proc' as root.")); + case ENOEXEC: + return (dgettext(TEXT_DOMAIN, "The ZFS modules cannot be " + "auto-loaded.\nTry running '/sbin/kextload zfs.kext' as " + "root to manually load them.")); + case EACCES: + return (dgettext(TEXT_DOMAIN, "Permission denied the " + "ZFS utilities must be run as root.")); + default: + return (dgettext(TEXT_DOMAIN, "Failed to initialize the " + "libzfs library.")); + } +} + +static int +libzfs_module_loaded(const char *module) +{ + const char path_prefix[] = "/dev/"; + char path[256]; + + memcpy(path, path_prefix, sizeof (path_prefix) - 1); + strcpy(path + sizeof (path_prefix) - 1, module); + + return (access(path, F_OK) == 0); +} + +/* + * Verify the required ZFS_DEV device is available and optionally attempt + * to load the ZFS modules. Under normal circumstances the modules + * should already have been loaded by some external mechanism. + * + * Environment variables: + * - ZFS_MODULE_LOADING="YES|yes|ON|on" - Attempt to load modules. + * - ZFS_MODULE_TIMEOUT="" - Seconds to wait for ZFS_DEV + */ +static int +libzfs_load_module_impl(const char *module) +{ + char *argv[4] = {"/sbin/kextload", (char *)module, (char *)0}; + char *load_str, *timeout_str; + long timeout = 10; /* seconds */ + long busy_timeout = 10; /* milliseconds */ + int load = 0, fd; + hrtime_t start; + + /* Optionally request module loading */ + if (!libzfs_module_loaded(module)) { + load_str = getenv("ZFS_MODULE_LOADING"); + if (load_str) { + if (!strncasecmp(load_str, "YES", strlen("YES")) || + !strncasecmp(load_str, "ON", strlen("ON"))) + load = 1; + else + load = 0; + } + + if (load) { + if (libzfs_run_process("/sbin/kextload", argv, 0)) + return (ENOEXEC); + } + + if (!libzfs_module_loaded(module)) + return (ENXIO); + } + + /* + * Device creation by udev is asynchronous and waiting may be + * required. Busy wait for 10ms and then fall back to polling every + * 10ms for the allowed timeout (default 10s, max 10m). This is + * done to optimize for the common case where the device is + * immediately available and to avoid penalizing the possible + * case where udev is slow or unable to create the device. + */ + timeout_str = getenv("ZFS_MODULE_TIMEOUT"); + if (timeout_str) { + timeout = strtol(timeout_str, NULL, 0); + timeout = MAX(MIN(timeout, (10 * 60)), 0); /* 0 <= N <= 600 */ + } + + start = gethrtime(); + do { + fd = open(ZFS_DEV, O_RDWR); + if (fd >= 0) { + (void) close(fd); + return (0); + } else if (errno != ENOENT) { + return (errno); + } else if (NSEC2MSEC(gethrtime() - start) < busy_timeout) { + sched_yield(); + } else { + usleep(10 * MILLISEC); + } + } while (NSEC2MSEC(gethrtime() - start) < (timeout * MILLISEC)); + + return (ENOENT); +} + +int +libzfs_load_module(void) +{ + + // Using this as a libzfs_init_os() - we should probably do it properly + libdiskmgt_init(); + + return (libzfs_load_module_impl(ZFS_DRIVER)); +} + +int +find_shares_object(differ_info_t *di) +{ + return (0); +} + +/* + * Fill given version buffer with zfs kernel version read from ZFS_SYSFS_DIR + * Returns 0 on success, and -1 on error (with errno set) + */ +int +zfs_version_kernel(char *version, int len) +{ + size_t rlen = len; + + if (sysctlbyname("zfs.kext_version", + version, &rlen, NULL, 0) == -1) + return (-1); + + return (0); +} + +static int +execvPe(const char *name, const char *path, char * const *argv, + char * const *envp) +{ + const char **memp; + size_t cnt, lp, ln; + int eacces, save_errno; + char *cur, buf[MAXPATHLEN]; + const char *p, *bp; + struct stat sb; + + eacces = 0; + + /* If it's an absolute or relative path name, it's easy. */ + if (strchr(name, '/')) { + bp = name; + cur = NULL; + goto retry; + } + bp = buf; + + /* If it's an empty path name, fail in the usual POSIX way. */ + if (*name == '\0') { + errno = ENOENT; + return (-1); + } + + cur = alloca(strlen(path) + 1); + if (cur == NULL) { + errno = ENOMEM; + return (-1); + } + strcpy(cur, path); + while ((p = strsep(&cur, ":")) != NULL) { + /* + * It's a SHELL path -- double, leading and trailing colons + * mean the current directory. + */ + if (*p == '\0') { + p = "."; + lp = 1; + } else + lp = strlen(p); + ln = strlen(name); + + /* + * If the path is too long complain. This is a possible + * security issue; given a way to make the path too long + * the user may execute the wrong program. + */ + if (lp + ln + 2 > sizeof (buf)) { + (void) write(STDERR_FILENO, "execvP: ", 8); + (void) write(STDERR_FILENO, p, lp); + (void) write(STDERR_FILENO, ": path too long\n", + 16); + continue; + } + bcopy(p, buf, lp); + buf[lp] = '/'; + bcopy(name, buf + lp + 1, ln); + buf[lp + ln + 1] = '\0'; + +retry: + (void) execve(bp, argv, envp); + switch (errno) { + case E2BIG: + goto done; + case ELOOP: + case ENAMETOOLONG: + case ENOENT: + break; + case ENOEXEC: + for (cnt = 0; argv[cnt]; ++cnt) + ; + memp = alloca((cnt + 2) * sizeof (char *)); + if (memp == NULL) { + goto done; + } + memp[0] = "sh"; + memp[1] = bp; + bcopy(argv + 1, memp + 2, cnt * sizeof (char *)); + execve(_PATH_BSHELL, __DECONST(char **, memp), + envp); + goto done; + case ENOMEM: + goto done; + case ENOTDIR: + break; + case ETXTBSY: + /* + * We used to retry here, but sh(1) doesn't. + */ + goto done; + default: + /* + * EACCES may be for an inaccessible directory or + * a non-executable file. Call stat() to decide + * which. This also handles ambiguities for EFAULT + * and EIO, and undocumented errors like ESTALE. + * We hope that the race for a stat() is unimportant. + */ + save_errno = errno; + if (stat(bp, &sb) != 0) + break; + if (save_errno == EACCES) { + eacces = 1; + continue; + } + errno = save_errno; + goto done; + } + } + if (eacces) + errno = EACCES; + else + errno = ENOENT; +done: + return (-1); +} + +int +execvpe(const char *name, char * const argv[], char * const envp[]) +{ + const char *path; + + /* Get the path we're searching. */ + if ((path = getenv("PATH")) == NULL) + path = _PATH_DEFPATH; + + return (execvPe(name, path, argv, envp)); +} + +#include +#include +#include + +extern void libzfs_refresh_finder(char *); + +/* + * To tell Finder to refresh is relatively easy from Obj-C, but as this + * would be the only function to use Obj-C (and only .m), the following code: + * void libzfs_refresh_finder(char *mountpoint) + * { + * [[NSWorkspace sharedWorkspace] noteFileSystemChanged:[NSString + * stringWithUTF8String:mountpoint]]; + * } + * Has been converted to C to keep autoconf simpler. If in future we have + * more Obj-C source files, then we should re-address this. + */ +void +libzfs_refresh_finder(char *path) +{ + Class NSWorkspace = objc_getClass("NSWorkspace"); + Class NSString = objc_getClass("NSString"); + SEL stringWithUTF8String = sel_registerName("stringWithUTF8String:"); + SEL sharedWorkspace = sel_registerName("sharedWorkspace"); + SEL noteFileSystemChanged = sel_registerName("noteFileSystemChanged:"); + id ns_path = ((id(*)(Class, SEL, char *))objc_msgSend)(NSString, + stringWithUTF8String, path); + id workspace = ((id(*)(Class, SEL))objc_msgSend)(NSWorkspace, + sharedWorkspace); + ((id(*)(id, SEL, id))objc_msgSend)(workspace, noteFileSystemChanged, + ns_path); +} + +void +zfs_rollback_os(zfs_handle_t *zhp) +{ + char sourceloc[ZFS_MAX_DATASET_NAME_LEN]; + char mountpoint[ZFS_MAXPROPLEN]; + zprop_source_t sourcetype; + + if (zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type, + B_FALSE)) { + if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, + mountpoint, sizeof (mountpoint), + &sourcetype, sourceloc, sizeof (sourceloc), B_FALSE) == 0) + libzfs_refresh_finder(mountpoint); + } +} + +struct pipe2file { + int from; + int to; +}; +typedef struct pipe2file pipe2file_t; + +// #define VERBOSE_WRAPFD +static int pipe_relay_readfd = -1; +static int pipe_relay_writefd = -1; +static int pipe_relay_send; +static volatile int signal_received = 0; +static int pipe_relay_pid = 0; + +static void pipe_io_relay_intr(int signum) +{ + signal_received = 1; +} + +static void * +pipe_io_relay(void *arg) +{ + pipe2file_t *p2f = (pipe2file_t *)arg; + int readfd, writefd; + unsigned char *buffer; + unsigned char space[1024]; + int size = 1024 * 1024; + int red, sent; + uint64_t total = 0; + + readfd = p2f->from; + writefd = p2f->to; + free(p2f); + p2f = NULL; + + buffer = malloc(size); + if (buffer == NULL) { + buffer = space; + size = sizeof (space); + } + +#ifdef VERBOSE_WRAPFD + fprintf(stderr, "%s: thread up: read(%d) write(%d)\r\n", __func__, + readfd, writefd); +#endif + + /* + * If ^C is hit, we must close the fds in the correct order, or + * we deadlock. So we need to install a signal handler, let's be + * nice and check if one is installed, and chain them in. + */ + struct sigaction sa; + sigset_t blocked; + + /* Process: Ignore SIGINT */ + + sigemptyset(&blocked); + sigaddset(&blocked, SIGINT); + sigaddset(&blocked, SIGPIPE); + sigprocmask(SIG_SETMASK, &blocked, NULL); + + sa.sa_handler = pipe_io_relay_intr; + sa.sa_mask = blocked; + sa.sa_flags = 0; + + sigaction(SIGINT, &sa, NULL); + + errno = 0; + + for (;;) { + + red = read(readfd, buffer, size); +#ifdef VERBOSE_WRAPFD + fprintf(stderr, "%s: read(%d): %d (errno %d)\r\n", __func__, + readfd, red, errno); +#endif + if (red == 0) + break; + if (red < 0 && errno != EWOULDBLOCK) + break; + + sent = write(writefd, buffer, red); +#ifdef VERBOSE_WRAPFD + fprintf(stderr, "%s: write(%d): %d (errno %d)\r\n", __func__, + writefd, sent, errno); +#endif + if (sent < 0) + break; + + if (signal_received) { +#ifdef VERBOSE_WRAPFD + fprintf(stderr, "sigint handler - exit\r\n"); +#endif + break; + } + + total += red; + } + + +#ifdef VERBOSE_WRAPFD + fprintf(stderr, "loop exit (closing)\r\n"); +#endif + + close(readfd); + close(writefd); + + if (buffer != space) + free(buffer); + +#ifdef VERBOSE_WRAPFD + fprintf(stderr, "%s: thread done: %llu bytes\r\n", __func__, total); +#endif + + return (NULL); +} + +/* + * XNU only lets us do IO on vnodes, not pipes, so create a Unix + * Domain socket, open it to get a vnode for the kernel, and spawn + * thread to relay IO. As used by sendrecv, we are given a FD it wants + * to send to the kernel, and we'll replace it with the pipe FD instead. + * If pipe/fork already exists, use same descriptors. (multiple send/recv) + * + * In addition to this, upstream will do their "zfs send" by having the kernel + * look in fd->f_offset for the userland file-position, then update it + * again after IO completes, so userland is kept in-sync. + * + * In XNU, we have no access to "f_offset". For "zfs send", it is possible + * to change the "fd" to have O_APPEND, then have kernel use IO_APPEND + * when writing to it. Once back in userland, any write()s will SEEK_END + * due to O_APPEND. This was tested, but it feels "questionable" to + * add O_APPEND to a file descriptor opened by the shell (zfs send > file). + * Even though this would work for "zfs send", we also need "zfs recv" to + * work. + * + * So now when zfs adds the "fd" to either "zc", or the "innvl", to pass it + * to the kernel via ioctl() - annoyingly we still have OLD and NEW ioctl + * for send and recv - we will also pass the file offset, either in + * zc.zoneid (not used in XNU) or innvl "input_fd_offset". + * Since the kernel might do writes, we need to SEEK_END once we return. + */ +void +libzfs_macos_wrapfd(int *srcfd, boolean_t send) +{ + char template[100]; + int error; + struct stat sb; + pipe2file_t *p2f = NULL; + + pipe_relay_send = send; + +#ifdef VERBOSE_WRAPFD + fprintf(stderr, "%s: checking if we need pipe wrap\r\n", __func__); +#endif + + // Check if it is a pipe + error = fstat(*srcfd, &sb); + + if (error != 0) + return; + + if (!S_ISFIFO(sb.st_mode)) + return; + + if (pipe_relay_pid != 0) { +#ifdef VERBOSE_WRAPFD + fprintf(stderr, "%s: pipe relay already started ... \r\n", + __func__); +#endif + if (send) { + *srcfd = pipe_relay_writefd; + } else { + *srcfd = pipe_relay_readfd; + } + return; + } + + p2f = (pipe2file_t *)malloc(sizeof (pipe2file_t)); + if (p2f == NULL) + return; + +#ifdef VERBOSE_WRAPFD + fprintf(stderr, "%s: is pipe: work on fd %d\r\n", __func__, *srcfd); +#endif + snprintf(template, sizeof (template), "/tmp/.zfs.pipe.XXXXXX"); + + mktemp(template); + + mkfifo(template, 0600); + + pipe_relay_readfd = open(template, O_RDONLY | O_NONBLOCK); + +#ifdef VERBOSE_WRAPFD + fprintf(stderr, "%s: pipe_relay_readfd %d (%d)\r\n", __func__, + pipe_relay_readfd, error); +#endif + + pipe_relay_writefd = open(template, O_WRONLY | O_NONBLOCK); + +#ifdef VERBOSE_WRAPFD + fprintf(stderr, "%s: pipe_relay_writefd %d (%d)\r\n", __func__, + pipe_relay_writefd, error); +#endif + + // set it to delete + unlink(template); + + // Check delayed so unlink() is always called. + if (pipe_relay_readfd < 0) + goto out; + if (pipe_relay_writefd < 0) + goto out; + + /* Open needs NONBLOCK, so switch back to BLOCK */ + int flags; + flags = fcntl(pipe_relay_readfd, F_GETFL); + flags &= ~O_NONBLOCK; + fcntl(pipe_relay_readfd, F_SETFL, flags); + flags = fcntl(pipe_relay_writefd, F_GETFL); + flags &= ~O_NONBLOCK; + fcntl(pipe_relay_writefd, F_SETFL, flags); + + // create IO thread + // Send, kernel was to be given *srcfd - to write to. + // Instead we give it pipe_relay_writefd. + // thread then uses read(pipe_relay_readfd) -> write(*srcfd) + if (send) { + p2f->from = pipe_relay_readfd; + p2f->to = *srcfd; + } else { + p2f->from = *srcfd; + p2f->to = pipe_relay_writefd; + } +#ifdef VERBOSE_WRAPFD + fprintf(stderr, "%s: forking\r\n", __func__); +#endif + + error = fork(); + if (error == 0) { + + // Close the fd we don't need + if (send) + close(pipe_relay_writefd); + else + close(pipe_relay_readfd); + + setsid(); + pipe_io_relay(p2f); + _exit(0); + } + + if (error < 0) + goto out; + + pipe_relay_pid = error; + + // Return open(file) fd to kernel only after all error cases + if (send) { + *srcfd = pipe_relay_writefd; + close(pipe_relay_readfd); + } else { + *srcfd = pipe_relay_readfd; + close(pipe_relay_writefd); + } + return; + +out: + if (p2f != NULL) + free(p2f); + + if (pipe_relay_readfd >= 0) + close(pipe_relay_readfd); + + if (pipe_relay_writefd >= 0) + close(pipe_relay_writefd); +} + +/* + * libzfs_diff uses pipe() to make 2 connected FDs, + * one is passed to kernel, and the other end it creates + * a thread to relay IO (to STDOUT). + * We can not do IO on anything by vnode opened FDs, so + * we'll use mkfifo, and open it twice, the WRONLY side + * is passed to kernel (now it is a vnode), and other other + * is used in the "differ" thread. + */ +int +libzfs_macos_pipefd(int *read_fd, int *write_fd) +{ + char template[100]; + + snprintf(template, sizeof (template), "/tmp/.zfs.diff.XXXXXX"); + mktemp(template); + + if (mkfifo(template, 0600)) + return (-1); + + *read_fd = open(template, O_RDONLY | O_NONBLOCK); + +#ifdef VERBOSE_WRAPFD + fprintf(stderr, "%s: readfd %d\r\n", __func__, + *read_fd); +#endif + if (*read_fd < 0) { + unlink(template); + return (-1); + } + + *write_fd = open(template, O_WRONLY | O_NONBLOCK); + +#ifdef VERBOSE_WRAPFD + fprintf(stderr, "%s: writefd %d\r\n", __func__, + *write_fd); +#endif + + // set it to delete + unlink(template); + + if (*write_fd < 0) { + close(*read_fd); + return (-1); + } + + /* Open needs NONBLOCK, so switch back to BLOCK */ + int flags; + flags = fcntl(*read_fd, F_GETFL); + flags &= ~O_NONBLOCK; + fcntl(*read_fd, F_SETFL, flags); + flags = fcntl(*write_fd, F_GETFL); + flags &= ~O_NONBLOCK; + fcntl(*write_fd, F_SETFL, flags); + + + return (0); + +} + + +void +libzfs_macos_wrapclose(void) +{ +} + +void +libzfs_set_pipe_max(int infd) +{ + /* macOS automatically resizes */ +} diff --git a/lib/libzutil/os/macos/zutil_compat.c b/lib/libzutil/os/macos/zutil_compat.c new file mode 100644 index 000000000000..b46263ac27cc --- /dev/null +++ b/lib/libzutil/os/macos/zutil_compat.c @@ -0,0 +1,94 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#include +#include +#include +#include +#include +#include + +static int +zcmd_ioctl_compat(int fd, int request, zfs_cmd_t *zc, const int cflag) +{ + int ret; + void *zc_c; + unsigned long ncmd; + zfs_iocparm_t zp; + + switch (cflag) { + case ZFS_CMD_COMPAT_NONE: + ncmd = _IOWR('Z', request, zfs_iocparm_t); + zp.zfs_cmd = (uint64_t)zc; + zp.zfs_cmd_size = sizeof (zfs_cmd_t); + zp.zfs_ioctl_version = ZFS_IOCVER_ZOF; + zp.zfs_ioc_error = 0; + + ret = ioctl(fd, ncmd, &zp); + + /* + * If ioctl worked, get actual rc from kernel, which goes + * into errno, and return -1 if not-zero. + */ + if (ret == 0) { + errno = zp.zfs_ioc_error; + if (zp.zfs_ioc_error != 0) + ret = -1; + } + return (ret); + + default: + abort(); + return (EINVAL); + } + + /* Pass-through ioctl, rarely used if at all */ + + ret = ioctl(fd, ncmd, zc_c); + ASSERT0(ret); + + zfs_cmd_compat_get(zc, (caddr_t)zc_c, cflag); + free(zc_c); + + return (ret); +} + +/* + * This is the macOS version of ioctl(). Because the XNU kernel + * handles copyin() and copyout(), we must return success from the + * ioctl() handler (or it will not copyout() for userland), + * and instead embed the error return value in the zc structure. + */ +int +zfs_ioctl_fd(int fd, unsigned long request, zfs_cmd_t *zc) +{ + size_t oldsize; + int ret, cflag = ZFS_CMD_COMPAT_NONE; + + oldsize = zc->zc_nvlist_dst_size; + ret = zcmd_ioctl_compat(fd, request, zc, cflag); + + if (ret == 0 && oldsize < zc->zc_nvlist_dst_size) { + ret = -1; + errno = ENOMEM; + } + + return (ret); +} diff --git a/lib/libzutil/os/macos/zutil_device_path_os.c b/lib/libzutil/os/macos/zutil_device_path_os.c new file mode 100644 index 000000000000..000e576f376a --- /dev/null +++ b/lib/libzutil/os/macos/zutil_device_path_os.c @@ -0,0 +1,207 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* + * Note: The caller must free the returned string. + */ +char * +zfs_strip_partition(char *dev) +{ + unsigned int disk, slice; + char *partless; + char whole_disk[MAXPATHLEN]; + + partless = strdup(dev); + + /* Ends with "diskNsP" - where 'N' and 'P' are integers - strip sP */ + if (sscanf(partless, "disk%us%u", &disk, &slice) == 2) { + char *r; + r = strrchr(partless, 's'); + if (r != NULL) + *r = 0; + } else if ((sscanf(partless, "%[^:]:%u", whole_disk, &slice)) == 2) { + char *r; + r = strrchr(partless, ':'); + if (r != NULL) { + if (strchr(partless, '@')) { // by-path + if (slice == 1) + r[1] = '0'; + } else // by-serial + *r = 0; + } + } + + return (partless); +} + +int +zfs_append_partition(char *path, size_t max_len) +{ + int len = strlen(path); + char dpath[max_len]; + if (strncmp(path, "/var/", 5) == 0) { + (void) strlcpy(dpath, "/private", max_len); + (void) strlcat(dpath, path, max_len); + } else + strlcpy(dpath, path, max_len); + + + if (strncmp(dpath, "/private/var/run/disk/by-id", 27) == 0) { + return (len); + } else if (strncmp(dpath, "/private/var/run/disk/by-path", 29) == 0) { + if (path[len - 1] == '0' && + path[len - 2] == ':') + path[len - 1] = '1'; + + } else if (strncmp(dpath, "/private/var/run/disk/by-serial", 31) == 0) { + if (len + 2 >= max_len) + return (-1); + + if (strchr(path, ':') == NULL) { + (void) strcat(path, ":1"); + len += 2; + } + } else { + + if (len + 2 >= max_len) + return (-1); + + if (isdigit(path[len-1])) { + (void) strcat(path, "s1"); + len += 2; + } else { + (void) strcat(path, "1"); + len += 1; + } + } + + return (len); +} + +/* + * Strip the path from a device name. + * On FreeBSD we only want to remove "/dev/" from the beginning of + * paths if present. + */ +char * +zfs_strip_path(char *path) +{ + char *r; + r = strrchr(path, '/'); + if (r == NULL) + return (r); + return (&r[1]); +} + +char * +zfs_get_underlying_path(const char *dev_name) +{ + + if (dev_name == NULL) + return (NULL); + + return (realpath(dev_name, NULL)); +} + +boolean_t +zfs_dev_is_whole_disk(const char *dev_name) +{ + struct dk_gpt *label; + int fd; + + if ((fd = open(dev_name, O_RDONLY | O_DIRECT)) < 0) + return (B_FALSE); + + if (efi_alloc_and_init(fd, EFI_NUMPAR, &label) != 0) { + (void) close(fd); + return (B_FALSE); + } + + efi_free(label); + (void) close(fd); + + return (B_TRUE); +} + +/* + * Wait up to timeout_ms for udev to set up the device node. The device is + * considered ready when libudev determines it has been initialized, all of + * the device links have been verified to exist, and it has been allowed to + * settle. At this point the device the device can be accessed reliably. + * Depending on the complexity of the udev rules this process could take + * several seconds. + */ +int +zpool_label_disk_wait(const char *path, int timeout_ms) +{ + int settle_ms = 50; + long sleep_ms = 10; + hrtime_t start, settle; + struct stat64 statbuf; + + start = gethrtime(); + settle = 0; + + do { + errno = 0; + if ((stat64(path, &statbuf) == 0) && (errno == 0)) { + if (settle == 0) + settle = gethrtime(); + else if (NSEC2MSEC(gethrtime() - settle) >= settle_ms) + return (0); + } else if (errno != ENOENT) { + return (errno); + } + + usleep(sleep_ms * MILLISEC); + } while (NSEC2MSEC(gethrtime() - start) < timeout_ms); + + return (ENODEV); +} + +/* ARGSUSED */ +boolean_t +is_mpath_whole_disk(const char *path) +{ + return (B_FALSE); +} + +/* + * Return B_TRUE if device is a device mapper or multipath device. + * Return B_FALSE if not. + */ +boolean_t +zfs_dev_is_dm(const char *dev_name) +{ + return (B_FALSE); +} diff --git a/lib/libzutil/os/macos/zutil_import_os.c b/lib/libzutil/os/macos/zutil_import_os.c new file mode 100644 index 000000000000..8956308dec0b --- /dev/null +++ b/lib/libzutil/os/macos/zutil_import_os.c @@ -0,0 +1,614 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018 by Delphix. All rights reserved. + * Copyright 2015 RackTop Systems. + * Copyright (c) 2016, Intel Corporation. + */ + +/* + * Pool import support functions. + * + * Used by zpool, ztest, zdb, and zhack to locate importable configs. Since + * these commands are expected to run in the global zone, we can assume + * that the devices are all readable when called. + * + * To import a pool, we rely on reading the configuration information from the + * ZFS label of each device. If we successfully read the label, then we + * organize the configuration information in the following hierarchy: + * + * pool guid -> toplevel vdev guid -> label txg + * + * Duplicate entries matching this same tuple will be discarded. Once we have + * examined every device, we pick the best label txg config for each toplevel + * vdev. We then arrange these toplevel vdevs into a complete pool config, and + * update any paths that have changed. Finally, we attempt to import the pool + * using our derived config, and record the results. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "zutil_import.h" + +#ifdef HAVE_LIBUDEV +#include +#include +#endif + +/* + * We allow /dev/ to be search in DEBUG build + * DEFAULT_IMPORT_PATH_SIZE is decremented by one to remove /dev! + * See below in zpool_find_import_blkid() to skip. + */ +#define DEFAULT_IMPORT_PATH_SIZE 4 + +#define DEV_BYID_PATH "/private/var/run/disk/by-id/" + +static char * +zpool_default_import_path[DEFAULT_IMPORT_PATH_SIZE] = { + "/private/var/run/disk/by-id", + "/private/var/run/disk/by-path", + "/private/var/run/disk/by-serial", + "/dev" /* Only with DEBUG build */ +}; + +static boolean_t +is_watchdog_dev(char *dev) +{ + /* For 'watchdog' dev */ + if (strcmp(dev, "watchdog") == 0) + return (B_TRUE); + + /* For 'watchdog */ + if (strstr(dev, "watchdog") == dev && isdigit(dev[8])) + return (B_TRUE); + + return (B_FALSE); +} + +int +zfs_dev_flush(int fd) +{ +// return (ioctl(fd, BLKFLSBUF)); + return (0); +} + +static uint64_t +label_offset(uint64_t size, int l) +{ + ASSERT(P2PHASE_TYPED(size, sizeof (vdev_label_t), uint64_t) == 0); + return (l * sizeof (vdev_label_t) + (l < VDEV_LABELS / 2 ? + 0 : size - VDEV_LABELS * sizeof (vdev_label_t))); +} + +/* + * We have had issues with lio_listio() and AIO on BigSur, where + * we receive waves of EAGAIN, and have to loop, often up to + * 100 times before labels are read. Until this problem can be + * understood better, we use the old serial style here. + */ +static int +zpool_read_label_os(int fd, nvlist_t **config, int *num_labels) +{ + struct stat64 statbuf; + int l, count = 0; + vdev_phys_t *label; + nvlist_t *expected_config = NULL; + uint64_t expected_guid = 0, size; + int error; + + *config = NULL; + + if (fstat64_blk(fd, &statbuf) == -1) + return (0); + size = P2ALIGN_TYPED(statbuf.st_size, sizeof (vdev_label_t), uint64_t); + + error = posix_memalign((void **)&label, PAGESIZE, sizeof (*label)); + if (error) + return (-1); + + for (l = 0; l < VDEV_LABELS; l++) { + uint64_t state, guid, txg; + off_t offset = label_offset(size, l) + VDEV_SKIP_SIZE; + + if (pread64(fd, label, sizeof (vdev_phys_t), + offset) != sizeof (vdev_phys_t)) + continue; + + if (nvlist_unpack(label->vp_nvlist, + sizeof (label->vp_nvlist), config, 0) != 0) + continue; + + if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_GUID, + &guid) != 0 || guid == 0) { + nvlist_free(*config); + continue; + } + if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_STATE, + &state) != 0 || state > POOL_STATE_L2CACHE) { + nvlist_free(*config); + continue; + } + + if (state != POOL_STATE_SPARE && state != POOL_STATE_L2CACHE && + (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_TXG, + &txg) != 0 || txg == 0)) { + nvlist_free(*config); + continue; + } + + if (expected_guid) { + if (expected_guid == guid) + count++; + + nvlist_free(*config); + } else { + expected_config = *config; + expected_guid = guid; + count++; + } + } + + if (num_labels != NULL) + *num_labels = count; + + free(label); + *config = expected_config; + + return (0); +} + +void +zpool_open_func(void *arg) +{ + rdsk_node_t *rn = arg; + libpc_handle_t *hdl = rn->rn_hdl; + struct stat64 statbuf; + nvlist_t *config; + char *bname, *dupname; + uint64_t vdev_guid = 0; + int error; + int num_labels = 0; + int fd; + + /* + * Skip devices with well known prefixes there can be side effects + * when opening devices which need to be avoided. + * + * hpet - High Precision Event Timer + * watchdog - Watchdog must be closed in a special way. + */ + dupname = zutil_strdup(hdl, rn->rn_name); + bname = basename(dupname); + error = ((strcmp(bname, "hpet") == 0) || is_watchdog_dev(bname)); + if ((strncmp(bname, "core", 4) == 0) || + (strncmp(bname, "fd", 2) == 0) || + (strncmp(bname, "fuse", 4) == 0) || + (strncmp(bname, "hpet", 4) == 0) || + (strncmp(bname, "lp", 2) == 0) || + (strncmp(bname, "parport", 7) == 0) || + (strncmp(bname, "ppp", 3) == 0) || + (strncmp(bname, "random", 6) == 0) || + (strncmp(bname, "rtc", 3) == 0) || + (strncmp(bname, "tty", 3) == 0) || + (strncmp(bname, "urandom", 7) == 0) || + (strncmp(bname, "usbmon", 6) == 0) || + (strncmp(bname, "vcs", 3) == 0) || + (strncmp(bname, "pty", 3) == 0) || // lots, skip for speed + (strncmp(bname, "bpf", 3) == 0) || + (strncmp(bname, "audit", 5) == 0) || + (strncmp(bname, "autofs", 6) == 0) || + (strncmp(bname, "console", 7) == 0) || + (strncmp(bname, "zfs", 3) == 0) || + (strncmp(bname, "oslog_stream", 12) == 0) || + (strncmp(bname, "com", 3) == 0)) // /dev/com_digidesign_semiface + error = 1; + + free(dupname); + if (error) + return; + + /* + * Ignore failed stats. We only want regular files and block devices. + */ + if (stat(rn->rn_name, &statbuf) != 0 || + (!S_ISREG(statbuf.st_mode) && + !S_ISBLK(statbuf.st_mode) && + !S_ISCHR(statbuf.st_mode))) + return; + + fd = open(rn->rn_name, O_RDONLY); + if ((fd < 0) && (errno == EINVAL)) + fd = open(rn->rn_name, O_RDONLY); + if ((fd < 0) && (errno == EACCES)) + hdl->lpc_open_access_error = B_TRUE; + if (fd < 0) + return; + + /* + * This file is too small to hold a zpool + */ + if (S_ISREG(statbuf.st_mode) && statbuf.st_size < SPA_MINDEVSIZE) { + (void) close(fd); + return; + } + + error = zpool_read_label_os(fd, &config, &num_labels); + + if (error != 0) { + (void) close(fd); +#ifdef DEBUG + printf("%s: zpool_read_label returned error %d " + "(errno: %d name: %s)\n", + __func__, error, errno, rn->rn_name); +#endif + return; + } + + if (num_labels == 0) { + (void) close(fd); + nvlist_free(config); + return; + } + + /* + * Check that the vdev is for the expected guid. Additional entries + * are speculatively added based on the paths stored in the labels. + * Entries with valid paths but incorrect guids must be removed. + */ + error = nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, &vdev_guid); + if (error || (rn->rn_vdev_guid && rn->rn_vdev_guid != vdev_guid)) { + (void) close(fd); + nvlist_free(config); + return; + } + + (void) close(fd); + + rn->rn_config = config; + rn->rn_num_labels = num_labels; + + /* + * Add additional entries for paths described by this label. + */ + if (rn->rn_labelpaths) { + char *path = NULL; + char *devid = NULL; + char *env = NULL; + rdsk_node_t *slice; + avl_index_t where; + int timeout; + int error; + + if (label_paths(rn->rn_hdl, rn->rn_config, &path, &devid)) + return; + + env = getenv("ZPOOL_IMPORT_UDEV_TIMEOUT_MS"); + if ((env == NULL) || sscanf(env, "%d", &timeout) != 1 || + timeout < 0) { + timeout = DISK_LABEL_WAIT; + } + + /* + * Allow devlinks to stabilize so all paths are available. + */ + zpool_label_disk_wait(rn->rn_name, timeout); + + if (path != NULL) { + slice = zutil_alloc(hdl, sizeof (rdsk_node_t)); + slice->rn_name = zutil_strdup(hdl, path); + slice->rn_vdev_guid = vdev_guid; + slice->rn_avl = rn->rn_avl; + slice->rn_hdl = hdl; + slice->rn_order = IMPORT_ORDER_PREFERRED_1; + slice->rn_labelpaths = B_FALSE; + pthread_mutex_lock(rn->rn_lock); + if (avl_find(rn->rn_avl, slice, &where)) { + pthread_mutex_unlock(rn->rn_lock); + free(slice->rn_name); + free(slice); + } else { + avl_insert(rn->rn_avl, slice, where); + pthread_mutex_unlock(rn->rn_lock); + zpool_open_func(slice); + } + } + + if (devid != NULL) { + slice = zutil_alloc(hdl, sizeof (rdsk_node_t)); + error = asprintf(&slice->rn_name, "%s%s", + DEV_BYID_PATH, devid); + if (error == -1) { + free(slice); + return; + } + + slice->rn_vdev_guid = vdev_guid; + slice->rn_avl = rn->rn_avl; + slice->rn_hdl = hdl; + slice->rn_order = IMPORT_ORDER_PREFERRED_2; + slice->rn_labelpaths = B_FALSE; + pthread_mutex_lock(rn->rn_lock); + if (avl_find(rn->rn_avl, slice, &where)) { + pthread_mutex_unlock(rn->rn_lock); + free(slice->rn_name); + free(slice); + } else { + avl_insert(rn->rn_avl, slice, where); + pthread_mutex_unlock(rn->rn_lock); + zpool_open_func(slice); + } + } + } +} + +const char * const * +zpool_default_search_paths(size_t *count) +{ + *count = DEFAULT_IMPORT_PATH_SIZE; + return ((const char * const *)zpool_default_import_path); +} + +int +zpool_find_import_blkid(libpc_handle_t *hdl, pthread_mutex_t *lock, + avl_tree_t **slice_cache) +{ + int i, dirs; + struct dirent *dp; + char path[MAXPATHLEN]; + char *end, **dir; + size_t pathleft; + avl_index_t where; + rdsk_node_t *slice; + int error = 0; + + dir = zpool_default_import_path; + dirs = DEFAULT_IMPORT_PATH_SIZE; + + /* + * Go through and read the label configuration information from every + * possible device, organizing the information according to pool GUID + * and toplevel GUID. + */ + *slice_cache = zutil_alloc(hdl, sizeof (avl_tree_t)); + avl_create(*slice_cache, slice_cache_compare, + sizeof (rdsk_node_t), offsetof(rdsk_node_t, rn_node)); + + for (i = 0; i < dirs; i++) { + char rdsk[MAXPATHLEN]; + int dfd; + DIR *dirp; + +#ifndef DEBUG + /* + * We skip imports in /dev/ in release builds, due to the + * danger of cache/log devices and drive renumbering. + * We have it in zpool_default_import_path to allow + * zfs_resolve_shortname() to still work, ie + * "zpool create disk3" to resolve to /dev/disk3. + */ + if (strncmp("/dev", dir[i], 4) == 0) + continue; +#endif + + /* use realpath to normalize the path */ + if (realpath(dir[i], path) == 0) { + + /* it is safe to skip missing search paths */ + if (errno == ENOENT) + continue; + + return (EPERM); + } + end = &path[strlen(path)]; + *end++ = '/'; + *end = 0; + pathleft = &path[sizeof (path)] - end; + + (void) strlcpy(rdsk, path, sizeof (rdsk)); + + if ((dfd = open(rdsk, O_RDONLY)) < 0 || + (dirp = fdopendir(dfd)) == NULL) { + if (dfd >= 0) + (void) close(dfd); + return (ENOENT); + } + + while ((dp = readdir(dirp)) != NULL) { + const char *name = dp->d_name; + if (name[0] == '.' && + (name[1] == 0 || (name[1] == '.' && name[2] == 0))) + continue; + + slice = zutil_alloc(hdl, sizeof (rdsk_node_t)); + + error = asprintf(&slice->rn_name, "%s%s", + path, name); + if (error == -1) { + free(slice); + return (ENOMEM); + } + + slice->rn_vdev_guid = 0; + slice->rn_lock = lock; + slice->rn_avl = *slice_cache; + slice->rn_hdl = hdl; + slice->rn_labelpaths = B_FALSE; + + // Make rdisk have a lower priority than disk + if (name[0] == 'r') + slice->rn_order = IMPORT_ORDER_DEFAULT + i; + else + slice->rn_order = IMPORT_ORDER_SCAN_OFFSET + i; + + pthread_mutex_lock(lock); + if (avl_find(*slice_cache, slice, &where)) { + free(slice->rn_name); + free(slice); + } else { + avl_insert(*slice_cache, slice, where); + } + pthread_mutex_unlock(lock); + } + + (void) closedir(dirp); + } + + return (0); +} + +/* + * Linux persistent device strings for vdev labels + * + * based on libudev for consistency with libudev disk add/remove events + */ + +typedef struct vdev_dev_strs { + char vds_devid[128]; + char vds_devphys[128]; +} vdev_dev_strs_t; + +/* ARGSUSED */ +int +zfs_device_get_devid(struct udev_device *dev, char *bufptr, size_t buflen) +{ + return (ENODATA); +} + +/* ARGSUSED */ +int +zfs_device_get_physical(struct udev_device *dev, char *bufptr, size_t buflen) +{ + return (ENODATA); +} + +/* + * Encode the persistent devices strings + * used for the vdev disk label + */ +static int +encode_device_strings(const char *path, vdev_dev_strs_t *ds, + boolean_t wholedisk) +{ + return (ENOENT); +} + +/* + * Update a leaf vdev's persistent device strings + * + * - only applies for a dedicated leaf vdev (aka whole disk) + * - updated during pool create|add|attach|import + * - used for matching device matching during auto-{online,expand,replace} + * - stored in a leaf disk config label (i.e. alongside 'path' NVP) + * - these strings are currently not used in kernel (i.e. for vdev_disk_open) + * + * single device node example: + * devid: 'scsi-MG03SCA300_350000494a8cb3d67-part1' + * phys_path: 'pci-0000:04:00.0-sas-0x50000394a8cb3d67-lun-0' + * + * multipath device node example: + * devid: 'dm-uuid-mpath-35000c5006304de3f' + * + * We also store the enclosure sysfs path for turning on enclosure LEDs + * (if applicable): + * vdev_enc_sysfs_path: '/sys/class/enclosure/11:0:1:0/SLOT 4' + */ +void +update_vdev_config_dev_strs(nvlist_t *nv) +{ + vdev_dev_strs_t vds; + char *env, *type, *path; + uint64_t wholedisk = 0; + + /* + * For the benefit of legacy ZFS implementations, allow + * for opting out of devid strings in the vdev label. + * + * example use: + * env ZFS_VDEV_DEVID_OPT_OUT=YES zpool import dozer + * + * explanation: + * Older ZFS on Linux implementations had issues when attempting to + * display pool config VDEV names if a "devid" NVP value is present + * in the pool's config. + * + * For example, a pool that originated on illumos platform would + * have a devid value in the config and "zpool status" would fail + * when listing the config. + * + * A pool can be stripped of any "devid" values on import or + * prevented from adding them on zpool create|add by setting + * ZFS_VDEV_DEVID_OPT_OUT. + */ + env = getenv("ZFS_VDEV_DEVID_OPT_OUT"); + if (env && (strtoul(env, NULL, 0) > 0 || + !strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2))) { + (void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID); + (void) nvlist_remove_all(nv, ZPOOL_CONFIG_PHYS_PATH); + return; + } + + if (nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) != 0 || + strcmp(type, VDEV_TYPE_DISK) != 0) { + return; + } + if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0) + return; + (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK, &wholedisk); + + /* + * Update device string values in the config nvlist. + */ + if (encode_device_strings(path, &vds, (boolean_t)wholedisk) == 0) { + (void) nvlist_add_string(nv, ZPOOL_CONFIG_DEVID, vds.vds_devid); + if (vds.vds_devphys[0] != '\0') { + (void) nvlist_add_string(nv, ZPOOL_CONFIG_PHYS_PATH, + vds.vds_devphys); + } + + } else { + /* Clear out any stale entries. */ + (void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID); + (void) nvlist_remove_all(nv, ZPOOL_CONFIG_PHYS_PATH); + (void) nvlist_remove_all(nv, ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH); + } +} diff --git a/lib/os/Makefile.am b/lib/os/Makefile.am new file mode 100644 index 000000000000..a6cb6105b286 --- /dev/null +++ b/lib/os/Makefile.am @@ -0,0 +1,4 @@ + +if BUILD_MACOS +SUBDIRS = macos +endif diff --git a/lib/os/macos/Makefile.am b/lib/os/macos/Makefile.am new file mode 100644 index 000000000000..a0eb6a6cbc36 --- /dev/null +++ b/lib/os/macos/Makefile.am @@ -0,0 +1,2 @@ + +SUBDIRS = libdiskmgt diff --git a/lib/os/macos/libdiskmgt/Makefile.am b/lib/os/macos/libdiskmgt/Makefile.am new file mode 100644 index 000000000000..4be3bf22db09 --- /dev/null +++ b/lib/os/macos/libdiskmgt/Makefile.am @@ -0,0 +1,24 @@ +include $(top_srcdir)/config/Rules.am + +DEFAULT_INCLUDES += \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/lib/libspl/include + +lib_LTLIBRARIES = libdiskmgt.la + +libdiskmgt_la_SOURCES = \ + dm.c \ + libdiskmgt.c \ + diskutil.c \ + entry.c \ + inuse_corestorage.c \ + inuse_fs.c \ + inuse_macswap.c \ + inuse_mnt.c \ + inuse_partition.c \ + inuse_zpool.c \ + slice.c + +libdiskmgt_la_LDFLAGS = -version-info 1:0:0 -framework DiskArbitration -framework CoreServices + +EXTRA_DIST = $(USER_C) diff --git a/lib/os/macos/libdiskmgt/disks_private.h b/lib/os/macos/libdiskmgt/disks_private.h new file mode 100644 index 000000000000..c1ebc8db8e69 --- /dev/null +++ b/lib/os/macos/libdiskmgt/disks_private.h @@ -0,0 +1,82 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016, Brendon Humphrey (brendon.humphrey@mac.com). + */ + +#ifndef DISKS_PRIVATE_H +#define DISKS_PRIVATE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NVATTRS NV_UNIQUE_NAME | NV_UNIQUE_NAME_TYPE +#define NVATTRS_STAT 0x0 + + typedef void* DU_Info; + + void diskutil_init(void); + void diskutil_fini(void); + + void init_diskutil_info(DU_Info *info); + int diskutil_info_valid(DU_Info info); + void get_diskutil_cs_info(char *slice, DU_Info *info); + void get_diskutil_info(char *slice, DU_Info *info); + int is_cs_disk(DU_Info *info); + int is_cs_converted(DU_Info *info); + int is_cs_locked(DU_Info *info); + int is_cs_logical_volume(DU_Info *info); + int is_cs_physical_volume(DU_Info *info); + int is_cs_online(DU_Info *info); + CFStringRef get_cs_LV_status(DU_Info *info); + + int is_whole_disk(DU_Info info); + int is_efi_partition(DU_Info info); + int is_recovery_partition(DU_Info info); + int is_APFS_partition(DU_Info info); + int is_HFS_partition(DU_Info info); + int is_MSDOS_partition(DU_Info info); + int has_filesystem_type(DU_Info info); + CFStringRef get_filesystem_type(DU_Info info); + + int inuse_corestorage(char *slice, nvlist_t *attrs, int *errp); + int inuse_fs(char *slice, nvlist_t *attrs, int *errp); + int inuse_macswap(const char *dev_name); + int inuse_mnt(char *slice, nvlist_t *attrs, int *errp); + int inuse_partition(char *slice, nvlist_t *attrs, int *errp); + int inuse_active_zpool(char *slice, nvlist_t *attrs, int *errp); + int inuse_exported_zpool(char *slice, nvlist_t *attrs, int *errp); + + void libdiskmgt_add_str(nvlist_t *attrs, char *name, char *val, + int *errp); + + nvlist_t *slice_get_stats(char *slice, int stat_type, int *errp); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/os/macos/libdiskmgt/diskutil.c b/lib/os/macos/libdiskmgt/diskutil.c new file mode 100644 index 000000000000..5cb5f1c03cf4 --- /dev/null +++ b/lib/os/macos/libdiskmgt/diskutil.c @@ -0,0 +1,379 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016, Brendon Humphrey (brendon.humphrey@mac.com). + */ + +#include +#include +#include +#include +#include +#include +#include +#include "disks_private.h" +#include + +static int out_pipe[2], err_pipe[2]; +static CFMutableDictionaryRef diskutil_cs_info_cache = NULL; +static CFMutableDictionaryRef diskutil_info_cache = NULL; + +static Boolean +CFDictionaryValueIfPresentMatchesSubstring(CFDictionaryRef dict, + CFStringRef key, CFStringRef substr) +{ + Boolean ret = false; + CFStringRef existing; + if (dict && + CFDictionaryGetValueIfPresent(dict, key, + (const void **)&existing)) { + CFRange range = CFStringFind(existing, substr, + kCFCompareCaseInsensitive); + if (range.location != kCFNotFound) + ret = true; + } + return (ret); +} + +static int +run_command(char *argv[], int *out_length) +{ + pid_t pid; + int status = 0; + struct stat out_stat; + + pipe(out_pipe); // create a pipe + pipe(err_pipe); + pid = fork(); + + if (pid == 0) { + close(out_pipe[0]); + close(err_pipe[0]); + dup2(out_pipe[1], STDOUT_FILENO); + dup2(err_pipe[1], STDERR_FILENO); + + execv(argv[0], argv); + } + + // Parent + close(out_pipe[1]); + close(err_pipe[1]); + waitpid(pid, &status, 0); + + fstat(out_pipe[0], &out_stat); + + *out_length = (int)out_stat.st_size; + + return (status); +} + +static void +read_buffers(char *out_buffer, int out_length) +{ + out_buffer[read(out_pipe[0], out_buffer, out_length)] = 0; +} + +void +diskutil_init() +{ + diskutil_info_cache = CFDictionaryCreateMutable(NULL, 0, + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + diskutil_cs_info_cache = CFDictionaryCreateMutable(NULL, 0, + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); +} + +void +diskutil_fini() +{ + CFRelease(diskutil_cs_info_cache); + CFRelease(diskutil_info_cache); +} + +void +init_diskutil_info(DU_Info *info) +{ + info = NULL; +} + +int +diskutil_info_valid(DU_Info info) +{ + return (info != NULL); +} + +void +get_diskutil_cs_info(char *slice, DU_Info *info) +{ + int status = 0; + int out_length = 0; + char *cc[] = { "/usr/sbin/diskutil", "cs", "info", "-plist", + slice, NULL}; + char *output = NULL; + CFPropertyListRef plist = NULL; + CFStringRef slice_str = CFStringCreateWithCString(NULL, + slice, kCFStringEncodingUTF8); + + if (CFDictionaryGetValueIfPresent(diskutil_cs_info_cache, slice_str, + (const void **)&plist)) { + *info = (DU_Info)plist; + } else { + *info = NULL; + status = run_command(cc, &out_length); + + if (WIFEXITED(status) && (WEXITSTATUS(status) == 0)) { + output = (char *)malloc(out_length); + + if (output) { + read_buffers(output, out_length); + + CFErrorRef err; + CFDataRef bytes = CFDataCreate(NULL, + (const unsigned char *)(output), + strlen(output)); + + if (bytes) { + plist = + CFPropertyListCreateWithData(NULL, + bytes, kCFPropertyListImmutable, + NULL, &err); + + if (plist) + CFDictionaryAddValue( + diskutil_cs_info_cache, + slice_str, plist); + + *info = (DU_Info)plist; + } + + free(output); + } + } + } + + CFRelease(slice_str); +} + +void +get_diskutil_info(char *slice, DU_Info *info) +{ + int status = 0; + int out_length = 0; + char *cc[] = {"/usr/sbin/diskutil", "info", "-plist", slice, NULL}; + char *output = NULL; + CFPropertyListRef plist = NULL; + CFStringRef slice_str = CFStringCreateWithCString(NULL, slice, + kCFStringEncodingUTF8); + + if (CFDictionaryGetValueIfPresent(diskutil_info_cache, slice_str, + (const void **)&plist)) { + *info = (DU_Info)plist; + } else { + *info = NULL; + status = run_command(cc, &out_length); + + if (WIFEXITED(status) && (WEXITSTATUS(status) == 0)) { + output = (char *)malloc(out_length); + + if (output) { + read_buffers(output, out_length); + + CFErrorRef err; + CFDataRef bytes = CFDataCreate(NULL, + (const unsigned char *)(output), + strlen(output)); + + if (bytes) { + plist = + CFPropertyListCreateWithData(NULL, + bytes, kCFPropertyListImmutable, + NULL, &err); + + if (plist) + CFDictionaryAddValue( + diskutil_info_cache, + slice_str, plist); + + *info = (DU_Info)plist; + } + + free(output); + } + } else { + *info = NULL; + } + + } + + CFRelease(slice_str); +} + +int +is_cs_converted(DU_Info *info) +{ + return CFDictionaryValueIfPresentMatchesSubstring( + (CFDictionaryRef)info, + CFSTR("CoreStorageLogicalVolumeConversionState"), + CFSTR("Complete")); +} + +int +is_cs_locked(DU_Info *info) +{ + return CFDictionaryValueIfPresentMatchesSubstring( + (CFDictionaryRef)info, + CFSTR("CoreStorageLogicalVolumeStatus"), + CFSTR("Locked")); +} + +int +is_cs_online(DU_Info *info) +{ + return CFDictionaryValueIfPresentMatchesSubstring( + (CFDictionaryRef)info, + CFSTR("CoreStorageLogicalVolumeStatus"), + CFSTR("Online")); +} + +CFStringRef +get_cs_LV_status(DU_Info *info) +{ + CFStringRef existing = NULL; + + if (info && + CFDictionaryGetValueIfPresent((CFDictionaryRef)info, + CFSTR("CoreStorageLogicalVolumeStatus"), + (const void **)&existing)) { + return (existing); + } + + return (NULL); +} + +int +is_cs_logical_volume(DU_Info *info) +{ + return CFDictionaryValueIfPresentMatchesSubstring( + (CFDictionaryRef)info, + CFSTR("CoreStorageRole"), + CFSTR("LV")); +} + +int +is_cs_physical_volume(DU_Info *info) +{ + return CFDictionaryValueIfPresentMatchesSubstring( + (CFDictionaryRef)info, + CFSTR("CoreStorageRole"), + CFSTR("PV")); +} + +int +is_cs_disk(DU_Info *info) +{ + return (is_cs_logical_volume(info) || is_cs_physical_volume(info)); +} + +int +is_efi_partition(DU_Info info) +{ + return CFDictionaryValueIfPresentMatchesSubstring( + (CFDictionaryRef)info, + CFSTR("Content"), + CFSTR("EFI")); +} + +int +is_recovery_partition(DU_Info info) +{ + return CFDictionaryValueIfPresentMatchesSubstring( + (CFDictionaryRef)info, + CFSTR("Content"), + CFSTR("Apple_Boot")); +} + +int +is_APFS_partition(DU_Info info) +{ + return CFDictionaryValueIfPresentMatchesSubstring( + (CFDictionaryRef)info, + CFSTR("Content"), + CFSTR("Apple_APFS")); +} + +int +is_HFS_partition(DU_Info info) +{ + return CFDictionaryValueIfPresentMatchesSubstring( + (CFDictionaryRef)info, + CFSTR("Content"), + CFSTR("Apple_HFS")); +} + +int +is_MSDOS_partition(DU_Info info) +{ + return CFDictionaryValueIfPresentMatchesSubstring( + (CFDictionaryRef)info, + CFSTR("Content"), + CFSTR("Microsoft Basic Data")); +} + +int +is_whole_disk(DU_Info info) +{ + int ret = 0; + Boolean is_whole = false; + if (info && + CFDictionaryGetValueIfPresent((CFDictionaryRef)info, + CFSTR("WholeDisk"), + (const void **)&is_whole)) { + ret = is_whole; + } + + return (ret); +} + +int +has_filesystem_type(DU_Info info) +{ + return (info && + CFDictionaryContainsKey((CFDictionaryRef)info, + CFSTR("FilesystemType"))); +} + +CFStringRef +get_filesystem_type(DU_Info info) +{ + CFStringRef existing = NULL; + + if (info && + CFDictionaryGetValueIfPresent((CFDictionaryRef)info, + CFSTR("FilesystemType"), + (const void **)&existing)) { + return (existing); + } + + return (NULL); +} diff --git a/lib/os/macos/libdiskmgt/dm.c b/lib/os/macos/libdiskmgt/dm.c new file mode 100644 index 000000000000..d661ac74bf7a --- /dev/null +++ b/lib/os/macos/libdiskmgt/dm.c @@ -0,0 +1,39 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016, Brendon Humphrey (brendon.humphrey@mac.com). + */ + +#include "disks_private.h" +#include + +void +libdiskmgt_init() +{ + diskutil_init(); +} + +void +libdiskmgt_fini() +{ + diskutil_fini(); +} diff --git a/lib/os/macos/libdiskmgt/entry.c b/lib/os/macos/libdiskmgt/entry.c new file mode 100644 index 000000000000..19b1d6eb62a4 --- /dev/null +++ b/lib/os/macos/libdiskmgt/entry.c @@ -0,0 +1,332 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016, Brendon Humphrey (brendon.humphrey@mac.com). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "disks_private.h" + +#define ANY_ZPOOL_USE(who) \ + (((who) == DM_WHO_ZPOOL_FORCE) || \ + ((who) == DM_WHO_ZPOOL) || \ + ((who) == DM_WHO_ZPOOL_SPARE)) + +void dm_get_slice_stats(char *slice, nvlist_t **dev_stats, int *errp); +nvlist_t *dm_get_stats(char *slice, int stat_type, int *errp); +static int build_usage_string(char *dname, char *by, char *data, char **msg, +int *found, int *errp); +void dm_get_usage_string(char *what, char *how, char **usage_string); + + +void +libdiskmgt_add_str(nvlist_t *attrs, char *name, char *val, int *errp) +{ + if (*errp == 0) { + *errp = nvlist_add_string(attrs, name, val); + } +} + +/* + * Returns 'in use' details, if found, about a specific dev_name, + * based on the caller(who). It is important to note that it is possible + * for there to be more than one 'in use' statistic regarding a dev_name. + * The **msg parameter returns a list of 'in use' details. This message + * is formatted via gettext(). + */ +int +dm_inuse(char *dev_name, char **msg, dm_who_type_t who, int *errp) +{ + nvlist_t *dev_stats = NULL; + char *by, *data; + nvpair_t *nvwhat = NULL; + nvpair_t *nvdesc = NULL; + int found = 0; + + *errp = 0; + *msg = NULL; + + /* + * If the user doesn't want to do in use checking, return. + */ + + if (NOINUSE_SET) + return (0); + + dm_get_slice_stats(dev_name, &dev_stats, errp); + if (dev_stats == NULL) { + /* + * If there is an error, but it isn't a no device found error + * return the error as recorded. Otherwise, with a full + * block name, we might not be able to get the slice + * associated, and will get an ENODEV error. For example, + * an SVM metadevice will return a value from getfullblkname() + * but libdiskmgt won't be able to find this device for + * statistics gathering. This is expected and we should not + * report errnoneous errors. + */ + if (*errp) { + if (*errp == ENODEV) { + *errp = 0; + } + } + // free(dname); + return (found); + } + + for (;;) { + + nvwhat = nvlist_next_nvpair(dev_stats, nvdesc); + nvdesc = nvlist_next_nvpair(dev_stats, nvwhat); + + /* + * End of the list found. + */ + if (nvwhat == NULL || nvdesc == NULL) { + break; + } + /* + * Otherwise, we check to see if this client(who) cares + * about this in use scenario + */ + + ASSERT(strcmp(nvpair_name(nvwhat), DM_USED_BY) == 0); + ASSERT(strcmp(nvpair_name(nvdesc), DM_USED_NAME) == 0); + /* + * If we error getting the string value continue on + * to the next pair(if there is one) + */ + if (nvpair_value_string(nvwhat, &by)) { + continue; + } + if (nvpair_value_string(nvdesc, &data)) { + continue; + } + + switch (who) { + + case DM_WHO_ZPOOL_FORCE: + if (strcmp(by, DM_USE_FS) == 0 || + strcmp(by, DM_USE_EXPORTED_ZPOOL) == 0 || + strcmp(by, DM_USE_OS_PARTITION) == 0) + break; + /* FALLTHROUGH */ + case DM_WHO_ZPOOL: + if (build_usage_string(dev_name, + by, data, msg, &found, errp) != 0) { + if (*errp) + goto out; + } + break; + + case DM_WHO_ZPOOL_SPARE: + if (strcmp(by, DM_USE_SPARE_ZPOOL) != 0) { + if (build_usage_string(dev_name, by, + data, msg, &found, errp) != 0) { + if (*errp) + goto out; + } + } + break; + + default: + /* + * nothing found in use for this client + * of libdiskmgt. Default is 'not in use'. + */ + break; + } + } +out: + nvlist_free(dev_stats); + + return (found); +} + +nvlist_t * +dm_get_stats(char *slice, int stat_type, int *errp) +{ + nvlist_t *stats = NULL; + + /* BGH - removed everything except ability to check a slice */ + + if (stat_type == DM_SLICE_STAT_USE) { + /* + * If NOINUSE_CHECK is set, we do not perform + * the in use checking if the user has set stat_type + * DM_SLICE_STAT_USE + */ + if (NOINUSE_SET) { + stats = NULL; + return (stats); + } + } + stats = slice_get_stats(slice, stat_type, errp); + + return (stats); +} + + +/* + * Convenience function to get slice stats. This is where we are going to + * depart from the illumos implementation - libdiskmgt on that + * platform has a lot more tricks that are not applicable + * to O3X. + */ +void +dm_get_slice_stats(char *slice, nvlist_t **dev_stats, int *errp) +{ + *dev_stats = NULL; + *errp = 0; + + if (slice == NULL) { + return; + } + + *dev_stats = dm_get_stats(slice, DM_SLICE_STAT_USE, errp); +} + +void +dm_get_usage_string(char *what, char *how, char **usage_string) +{ + if (usage_string == NULL || what == NULL) { + return; + } + *usage_string = NULL; + + if (strcmp(what, DM_USE_MOUNT) == 0) { + if (strcmp(how, "swap") == 0) { + *usage_string = dgettext(TEXT_DOMAIN, + "%s is currently used by swap. Please see swap(1M)." + "\n"); + } else { + *usage_string = dgettext(TEXT_DOMAIN, + "%s is currently mounted on %s." + " Please see umount(1M).\n"); + } + } else if (strcmp(what, DM_USE_FS) == 0 || + strcmp(what, DM_USE_FS_NO_FORCE) == 0) { + *usage_string = dgettext(TEXT_DOMAIN, + "%s contains a %s filesystem.\n"); + } else if (strcmp(what, DM_USE_EXPORTED_ZPOOL) == 0) { + *usage_string = dgettext(TEXT_DOMAIN, + "%s is part of exported or potentially active ZFS pool %s. " + "Please see zpool(1M).\n"); + } else if (strcmp(what, DM_USE_ACTIVE_ZPOOL) == 0) { + *usage_string = dgettext(TEXT_DOMAIN, + "%s is part of active ZFS pool %s. Please see zpool(1M)." + "\n"); + } else if (strcmp(what, DM_USE_SPARE_ZPOOL) == 0) { + *usage_string = dgettext(TEXT_DOMAIN, + "%s is reserved as a hot spare for ZFS pool %s. Please " + "see zpool(1M).\n"); + } else if (strcmp(what, DM_USE_L2CACHE_ZPOOL) == 0) { + *usage_string = dgettext(TEXT_DOMAIN, + "%s is in use as a cache device for ZFS pool %s. " + "Please see zpool(1M).\n"); + } else if (strcmp(what, DM_USE_CORESTORAGE_PV) == 0) { + *usage_string = dgettext(TEXT_DOMAIN, + "%s is in use as a corestorage physical volume. " + "Please see diskutil(8).\n"); + } else if (strcmp(what, DM_USE_CORESTORAGE_LOCKED_LV) == 0) { + *usage_string = dgettext(TEXT_DOMAIN, + "%s is a corestorage logical volume, " + "but cannot be used as it is locked. " + "Please see diskutil(8).\n"); + } else if (strcmp(what, DM_USE_CORESTORAGE_CONVERTING_LV) == 0) { + *usage_string = dgettext(TEXT_DOMAIN, + "%s is a corestorage physical volume, but is still " + "converting (%s).\n" + "Creating a zpool while converting will result in " + "data corruption.\n" + "Please see diskutil(8).\n"); + } else if (strcmp(what, DM_USE_CORESTORAGE_OFFLINE_LV) == 0) { + *usage_string = dgettext(TEXT_DOMAIN, + "%s is a corestorage physical volume, but is not " + "online (%s). Please see diskutil(8).\n"); + } else if (strcmp(what, DM_USE_OS_PARTITION) == 0 || + strcmp(what, DM_USE_OS_PARTITION_NO_FORCE) == 0) { + *usage_string = dgettext(TEXT_DOMAIN, + "%s is a %s partition. " + "Please see diskutil(8).\n"); + } +} + +/* + * Build the usage string for the in use data. Return the build string in + * the msg parameter. This function takes care of reallocing all the memory + * for this usage string. Usage string is returned already formatted for + * localization. + */ +static int +build_usage_string(char *dname, char *by, char *data, char **msg, + int *found, int *errp) +{ + int len0; + int len1; + char *use; + char *p; + + *errp = 0; + + dm_get_usage_string(by, data, &use); + if (!use) { + return (-1); + } + + if (*msg) + len0 = strlen(*msg); + else + len0 = 0; + /* LINTED */ + len1 = snprintf(NULL, 0, use, dname, data); + + /* + * If multiple in use details they + * are listed 1 per line for ease of + * reading. dm_find_usage_string + * formats these appropriately. + */ + if ((p = realloc(*msg, len0 + len1 + 1)) == NULL) { + *errp = errno; + free(*msg); + return (-1); + } + *msg = p; + + /* LINTED */ + (void) snprintf(*msg + len0, len1 + 1, use, dname, data); + (*found)++; + return (0); +} diff --git a/lib/os/macos/libdiskmgt/inuse_corestorage.c b/lib/os/macos/libdiskmgt/inuse_corestorage.c new file mode 100644 index 000000000000..3897a1902f83 --- /dev/null +++ b/lib/os/macos/libdiskmgt/inuse_corestorage.c @@ -0,0 +1,98 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016, Brendon Humphrey (brendon.humphrey@mac.com). + */ + +#include +#include +#include "disks_private.h" + +int +inuse_corestorage(char *slice, nvlist_t *attrs, int *errp) +{ + DU_Info info; + int in_use = 0; + + init_diskutil_info(&info); + + get_diskutil_cs_info(slice, &info); + + if (diskutil_info_valid(info)) { + if (is_cs_physical_volume(info)) { + libdiskmgt_add_str(attrs, DM_USED_BY, + DM_USE_CORESTORAGE_PV, errp); + libdiskmgt_add_str(attrs, DM_USED_NAME, + slice, errp); + in_use = 1; + } else if (is_cs_logical_volume(info)) { + + if (is_cs_locked(info)) { + libdiskmgt_add_str(attrs, DM_USED_BY, + DM_USE_CORESTORAGE_LOCKED_LV, errp); + libdiskmgt_add_str(attrs, DM_USED_NAME, + slice, errp); + in_use = 1; + } else if (!is_cs_converted(info)) { + CFStringRef lv_status = get_cs_LV_status(info); + char lv_status_str[128] = { 0 }; + Boolean success = + CFStringGetCString(lv_status, + lv_status_str, 128, + kCFStringEncodingMacRoman); + + libdiskmgt_add_str(attrs, DM_USED_BY, + DM_USE_CORESTORAGE_CONVERTING_LV, errp); + + if (success) { + libdiskmgt_add_str(attrs, DM_USED_NAME, + lv_status_str, errp); + } else { + libdiskmgt_add_str(attrs, DM_USED_NAME, + "Unknown", errp); + } + in_use = 1; + } else if (!is_cs_online(info)) { + CFStringRef lv_status = get_cs_LV_status(info); + char lv_status_str[128] = { 0 }; + Boolean success = + CFStringGetCString(lv_status, + lv_status_str, 128, + kCFStringEncodingMacRoman); + + libdiskmgt_add_str(attrs, DM_USED_BY, + DM_USE_CORESTORAGE_OFFLINE_LV, errp); + + if (success) { + libdiskmgt_add_str(attrs, DM_USED_NAME, + lv_status_str, errp); + } else { + libdiskmgt_add_str(attrs, DM_USED_NAME, + "Unknown", errp); + } + in_use = 1; + } + } + } + + return (in_use); +} diff --git a/lib/os/macos/libdiskmgt/inuse_fs.c b/lib/os/macos/libdiskmgt/inuse_fs.c new file mode 100644 index 000000000000..ebaaece7a855 --- /dev/null +++ b/lib/os/macos/libdiskmgt/inuse_fs.c @@ -0,0 +1,77 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016, Brendon Humphrey (brendon.humphrey@mac.com). + */ + +#include +#include +#include +#include "disks_private.h" + + +/* + * Use the heuristics to check for a filesystem on the slice. + */ +int +inuse_fs(char *slice, nvlist_t *attrs, int *errp) +{ + int in_use = 0; + DU_Info info; + + init_diskutil_info(&info); + get_diskutil_info(slice, &info); + + if (diskutil_info_valid(info) && has_filesystem_type(info)) { + CFStringRef filesystem_type = get_filesystem_type(info); + char filesystem_type_str[128] = { 0 }; + Boolean success = + CFStringGetCString(filesystem_type, + filesystem_type_str, 128, + kCFStringEncodingUTF8); + + if (filesystem_type && + (CFStringCompare(filesystem_type, CFSTR("zfs"), + kCFCompareCaseInsensitive) != kCFCompareEqualTo)) { + + if (CFStringCompare(filesystem_type, CFSTR("apfs"), + kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + libdiskmgt_add_str(attrs, DM_USED_BY, + DM_USE_FS_NO_FORCE, errp); + } else { + libdiskmgt_add_str(attrs, DM_USED_BY, + DM_USE_FS, errp); + } + + if (success) { + libdiskmgt_add_str(attrs, DM_USED_NAME, + filesystem_type_str, errp); + } else { + libdiskmgt_add_str(attrs, DM_USED_NAME, + "Unknown", errp); + } + in_use = 1; + } + } + + return (in_use); +} diff --git a/lib/os/macos/libdiskmgt/inuse_macswap.c b/lib/os/macos/libdiskmgt/inuse_macswap.c new file mode 100644 index 000000000000..0cf0fa0803d8 --- /dev/null +++ b/lib/os/macos/libdiskmgt/inuse_macswap.c @@ -0,0 +1,75 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016, Brendon Humphrey (brendon.humphrey@mac.com). + */ + +#include +#include +#include +#include +#include +#include +#include "disks_private.h" + +extern char *dirname(char *path); + +static const char *SWAP_SYSCTL_NAME = "vm.swapfileprefix"; + +int +inuse_macswap(const char *dev_name) +{ + size_t oldlen = 0; + char *tmp; + char *tmp2; + char *swap_filename; + char real_swap_path[MAXPATHLEN]; + char real_dev_path[MAXPATHLEN]; + + /* Obtain the swap file prefix (path + prototype basename) */ + if (sysctlbyname(SWAP_SYSCTL_NAME, NULL, &oldlen, NULL, 0) != 0) + return (0); + + swap_filename = (char *)malloc(oldlen); + if (sysctlbyname(SWAP_SYSCTL_NAME, swap_filename, &oldlen, NULL, + 0) != 0) + return (0); + + /* + * Get the directory portion of the vm.swapfileprefix sysctl + * once links etc have been resolved. + */ + tmp = realpath(swap_filename, NULL); + tmp2 = dirname(swap_filename); + (void) strlcpy(real_swap_path, tmp2, MAXPATHLEN); + free(swap_filename); + free(tmp); + + /* Get the (resolved) directory portion of dev_name */ + tmp = realpath(dev_name, NULL); + tmp2 = dirname(tmp); + (void) strlcpy(real_dev_path, tmp2, MAXPATHLEN); + free(tmp); + + /* If the strings are equal, the file is in the swap dir */ + return (strcmp(real_dev_path, real_swap_path) == 0); +} diff --git a/lib/os/macos/libdiskmgt/inuse_mnt.c b/lib/os/macos/libdiskmgt/inuse_mnt.c new file mode 100644 index 000000000000..7bc9e73b224a --- /dev/null +++ b/lib/os/macos/libdiskmgt/inuse_mnt.c @@ -0,0 +1,54 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016, Brendon Humphrey (brendon.humphrey@mac.com). + */ + +#include +#include +#include +#include +#include +#include "disks_private.h" + +int +inuse_mnt(char *slice, nvlist_t *attrs, int *errp) +{ + struct statfs *mounts; + + /* Read the current set of mounts */ + int num_mounts = getmntinfo(&mounts, MNT_WAIT); + + /* Check whether slice is presently in use */ + for (int i = 0; i < num_mounts; i++) { + int slice_found = (strcmp(mounts[i].f_mntfromname, slice) == 0); + + if (slice_found) { + libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_MOUNT, + errp); + libdiskmgt_add_str(attrs, DM_USED_NAME, + mounts[i].f_mntonname, errp); + return (1); + } + } + return (0); +} diff --git a/lib/os/macos/libdiskmgt/inuse_partition.c b/lib/os/macos/libdiskmgt/inuse_partition.c new file mode 100644 index 000000000000..f4fb607d882a --- /dev/null +++ b/lib/os/macos/libdiskmgt/inuse_partition.c @@ -0,0 +1,74 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016, Brendon Humphrey (brendon.humphrey@mac.com). + */ + +#include +#include +#include "disks_private.h" + +int +inuse_partition(char *slice, nvlist_t *attrs, int *errp) +{ + int in_use = 0; + DU_Info info; + + init_diskutil_info(&info); + get_diskutil_info(slice, &info); + + if (diskutil_info_valid(info)) { + if (is_efi_partition(info)) { + libdiskmgt_add_str(attrs, DM_USED_BY, + DM_USE_OS_PARTITION, errp); + libdiskmgt_add_str(attrs, DM_USED_NAME, + "EFI", errp); + in_use = 1; + } else if (is_recovery_partition(info)) { + libdiskmgt_add_str(attrs, DM_USED_BY, + DM_USE_OS_PARTITION_NO_FORCE, errp); + libdiskmgt_add_str(attrs, DM_USED_NAME, + "Recovery", errp); + in_use = 1; + } else if (is_APFS_partition(info)) { + libdiskmgt_add_str(attrs, DM_USED_BY, + DM_USE_OS_PARTITION_NO_FORCE, errp); + libdiskmgt_add_str(attrs, DM_USED_NAME, + "APFS", errp); + in_use = 1; + } else if (is_HFS_partition(info)) { + libdiskmgt_add_str(attrs, DM_USED_BY, + DM_USE_OS_PARTITION, errp); + libdiskmgt_add_str(attrs, DM_USED_NAME, + "HFS", errp); + in_use = 1; + } else if (is_MSDOS_partition(info)) { + libdiskmgt_add_str(attrs, DM_USED_BY, + DM_USE_OS_PARTITION, errp); + libdiskmgt_add_str(attrs, DM_USED_NAME, + "MSDOS", errp); + in_use = 1; + } + } + + return (in_use); +} diff --git a/lib/os/macos/libdiskmgt/inuse_zpool.c b/lib/os/macos/libdiskmgt/inuse_zpool.c new file mode 100644 index 000000000000..aa6417fd7f0c --- /dev/null +++ b/lib/os/macos/libdiskmgt/inuse_zpool.c @@ -0,0 +1,162 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Attempt to dynamically link in the ZFS libzfs.so.1 so that we can + * see if there are any ZFS zpools on any of the slices. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "libdiskmgt.h" +#include "disks_private.h" + +/* + * Pointers to libzfs.so functions that we dynamically resolve. + */ +static int (*zfsdl_zpool_in_use)(libzfs_handle_t *hdl, int fd, +pool_state_t *state, char **name, boolean_t *); +static libzfs_handle_t *(*zfsdl_libzfs_init)(boolean_t); + +static boolean_t initialized = false; +static libzfs_handle_t *zfs_hdl; + +static void *init_zpool(void); + +static int +inuse_zpool_common(char *slice, nvlist_t *attrs, int *errp, char *type) +{ + int found = 0; + char *name; + int fd; + pool_state_t state; + boolean_t used; + + *errp = 0; + if (slice == NULL) { + return (found); + } + + /* + * Dynamically load libzfs + */ + if (!initialized) { + if (!init_zpool()) { + return (found); + } + initialized = B_TRUE; + } + + if ((fd = open(slice, O_RDONLY)) > 0) { + name = NULL; + if (zfsdl_zpool_in_use(zfs_hdl, fd, &state, + &name, &used) == 0 && used) { + if (strcmp(type, DM_USE_ACTIVE_ZPOOL) == 0) { + if (state == POOL_STATE_ACTIVE) { + found = 1; + } else if (state == POOL_STATE_SPARE) { + found = 1; + type = DM_USE_SPARE_ZPOOL; + } else if (state == POOL_STATE_L2CACHE) { + found = 1; + type = DM_USE_L2CACHE_ZPOOL; + } + } else { + found = 1; + } + + if (found) { + libdiskmgt_add_str(attrs, DM_USED_BY, + type, errp); + libdiskmgt_add_str(attrs, DM_USED_NAME, + name, errp); + } + } + if (name) + free(name); + (void) close(fd); + } + + return (found); +} + +int +inuse_active_zpool(char *slice, nvlist_t *attrs, int *errp) +{ + return (inuse_zpool_common(slice, attrs, errp, DM_USE_ACTIVE_ZPOOL)); +} + +int +inuse_exported_zpool(char *slice, nvlist_t *attrs, int *errp) +{ + return (inuse_zpool_common(slice, attrs, errp, DM_USE_EXPORTED_ZPOOL)); +} + +/* + * Try to dynamically link the zfs functions we need. + */ +static void* +init_zpool(void) +{ + void *lh = NULL; + + if ((lh = dlopen("libzfs.dylib", RTLD_NOW)) == NULL) { + return (lh); + } + + /* + * Instantiate the functions needed to get zpool configuration + * data + */ + if ((zfsdl_libzfs_init = (libzfs_handle_t *(*)(boolean_t)) + dlsym(lh, "libzfs_init")) == NULL || + (zfsdl_zpool_in_use = (int (*)(libzfs_handle_t *, int, + pool_state_t *, char **, boolean_t *)) + dlsym(lh, "zpool_in_use")) == NULL) { + (void) dlclose(lh); + return (NULL); + } + + if ((zfs_hdl = (*zfsdl_libzfs_init)(B_FALSE)) == NULL) { + (void) dlclose(lh); + return (NULL); + } + + return (lh); +} diff --git a/lib/os/macos/libdiskmgt/libdiskmgt.c b/lib/os/macos/libdiskmgt/libdiskmgt.c new file mode 100644 index 000000000000..2439d0bca046 --- /dev/null +++ b/lib/os/macos/libdiskmgt/libdiskmgt.c @@ -0,0 +1,39 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016, Brendon Humphrey (brendon.humphrey@mac.com). + */ + +#include +#include +#include +#include +#include +#include +#include "disks_private.h" +#include "libdiskmgt.h" + +int +dm_in_swap_dir(const char *dev_name) +{ + return (inuse_macswap(dev_name)); +} diff --git a/lib/os/macos/libdiskmgt/slice.c b/lib/os/macos/libdiskmgt/slice.c new file mode 100644 index 000000000000..c3b016607b96 --- /dev/null +++ b/lib/os/macos/libdiskmgt/slice.c @@ -0,0 +1,105 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libdiskmgt.h" +#include "disks_private.h" +#include + +#ifndef VT_ENOTSUP +#define VT_ENOTSUP (-5) +#endif + +#define FMT_UNKNOWN 0 +#define FMT_VTOC 1 +#define FMT_EFI 2 + +typedef int (*detectorp)(char *, nvlist_t *, int *); + +static detectorp detectors[] = { + inuse_mnt, + inuse_corestorage, + inuse_partition, + inuse_active_zpool, + inuse_exported_zpool, + inuse_fs, /* fs should always be last */ + NULL +}; + +static int add_inuse(char *name, nvlist_t *attrs); + +nvlist_t * +slice_get_stats(char *slice, int stat_type, int *errp) +{ + nvlist_t *stats; + + if (stat_type != DM_SLICE_STAT_USE) { + *errp = EINVAL; + return (NULL); + } + + *errp = 0; + + if (nvlist_alloc(&stats, NVATTRS_STAT, 0) != 0) { + *errp = ENOMEM; + return (NULL); + } + + if ((*errp = add_inuse(slice, stats)) != 0) { + return (NULL); + } + + return (stats); +} + +/* + * Check if/how the slice is used. + */ +static int +add_inuse(char *name, nvlist_t *attrs) +{ + int i = 0; + int error = 0; + + for (i = 0; detectors[i] != NULL; i ++) { + if (detectors[i](name, attrs, &error) || error != 0) { + if (error != 0) { + return (error); + } + break; + } + } + + return (0); +} diff --git a/module/icp/asm-x86_64/os/macos/aes/aes_aesni.S b/module/icp/asm-x86_64/os/macos/aes/aes_aesni.S new file mode 100644 index 000000000000..9be7f631510e --- /dev/null +++ b/module/icp/asm-x86_64/os/macos/aes/aes_aesni.S @@ -0,0 +1,855 @@ +/* + * ==================================================================== + * Written by Intel Corporation for the OpenSSL project to add support + * for Intel AES-NI instructions. Rights for redistribution and usage + * in source and binary forms are granted according to the OpenSSL + * license. + * + * Author: Huang Ying + * Vinodh Gopal + * Kahraman Akdemir + * + * Intel AES-NI is a new set of Single Instruction Multiple Data (SIMD) + * instructions that are going to be introduced in the next generation + * of Intel processor, as of 2009. These instructions enable fast and + * secure data encryption and decryption, using the Advanced Encryption + * Standard (AES), defined by FIPS Publication number 197. The + * architecture introduces six instructions that offer full hardware + * support for AES. Four of them support high performance data + * encryption and decryption, and the other two instructions support + * the AES key expansion procedure. + * ==================================================================== + */ + +/* + * ==================================================================== + * Copyright (c) 1998-2008 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +/* + * ==================================================================== + * OpenSolaris OS modifications + * + * This source originates as files aes-intel.S and eng_aesni_asm.pl, in + * patches sent sent Dec. 9, 2008 and Dec. 24, 2008, respectively, by + * Huang Ying of Intel to the openssl-dev mailing list under the subject + * of "Add support to Intel AES-NI instruction set for x86_64 platform". + * + * This OpenSolaris version has these major changes from the original source: + * + * 1. Added OpenSolaris ENTRY_NP/SET_SIZE macros from + * /usr/include/sys/asm_linkage.h, lint(1B) guards, and dummy C function + * definitions for lint. + * + * 2. Formatted code, added comments, and added #includes and #defines. + * + * 3. If bit CR0.TS is set, clear and set the TS bit, after and before + * calling kpreempt_disable() and kpreempt_enable(). + * If the TS bit is not set, Save and restore %xmm registers at the beginning + * and end of function calls (%xmm* registers are not saved and restored by + * during kernel thread preemption). + * + * 4. Renamed functions, reordered parameters, and changed return value + * to match OpenSolaris: + * + * OpenSSL interface: + * int intel_AES_set_encrypt_key(const unsigned char *userKey, + * const int bits, AES_KEY *key); + * int intel_AES_set_decrypt_key(const unsigned char *userKey, + * const int bits, AES_KEY *key); + * Return values for above are non-zero on error, 0 on success. + * + * void intel_AES_encrypt(const unsigned char *in, unsigned char *out, + * const AES_KEY *key); + * void intel_AES_decrypt(const unsigned char *in, unsigned char *out, + * const AES_KEY *key); + * typedef struct aes_key_st { + * unsigned int rd_key[4 *(AES_MAXNR + 1)]; + * int rounds; + * unsigned int pad[3]; + * } AES_KEY; + * Note: AES_LONG is undefined (that is, Intel uses 32-bit key schedules + * (ks32) instead of 64-bit (ks64). + * Number of rounds (aka round count) is at offset 240 of AES_KEY. + * + * OpenSolaris OS interface (#ifdefs removed for readability): + * int rijndael_key_setup_dec_intel(uint32_t rk[], + * const uint32_t cipherKey[], uint64_t keyBits); + * int rijndael_key_setup_enc_intel(uint32_t rk[], + * const uint32_t cipherKey[], uint64_t keyBits); + * Return values for above are 0 on error, number of rounds on success. + * + * void aes_encrypt_intel(const aes_ks_t *ks, int Nr, + * const uint32_t pt[4], uint32_t ct[4]); + * void aes_decrypt_intel(const aes_ks_t *ks, int Nr, + * const uint32_t pt[4], uint32_t ct[4]); + * typedef union {uint64_t ks64[(MAX_AES_NR + 1) * 4]; + * uint32_t ks32[(MAX_AES_NR + 1) * 4]; } aes_ks_t; + * + * typedef union { + * uint32_t ks32[((MAX_AES_NR) + 1) * (MAX_AES_NB)]; + * } aes_ks_t; + * typedef struct aes_key { + * aes_ks_t encr_ks, decr_ks; + * long double align128; + * int flags, nr, type; + * } aes_key_t; + * + * Note: ks is the AES key schedule, Nr is number of rounds, pt is plain text, + * ct is crypto text, and MAX_AES_NR is 14. + * For the x86 64-bit architecture, OpenSolaris OS uses ks32 instead of ks64. + * + * Note2: aes_ks_t must be aligned on a 0 mod 128 byte boundary. + * ==================================================================== + * Mac OS X modifications + * 1. Removed CR0.TS / STTS / CLTS since the XNU kernel can apparently use floating point + * registers without this. + * + * ==================================================================== + */ + +#if defined(lint) || defined(__lint) + +#include + +/* ARGSUSED */ +void +aes_encrypt_intel(const uint32_t rk[], int Nr, const uint32_t pt[4], + uint32_t ct[4]) { +} +/* ARGSUSED */ +void +aes_decrypt_intel(const uint32_t rk[], int Nr, const uint32_t ct[4], + uint32_t pt[4]) { +} +/* ARGSUSED */ +int +rijndael_key_setup_enc_intel(uint32_t rk[], const uint32_t cipherKey[], + uint64_t keyBits) { + return (0); +} +/* ARGSUSED */ +int +rijndael_key_setup_dec_intel(uint32_t rk[], const uint32_t cipherKey[], + uint64_t keyBits) { + return (0); +} + + +#else /* lint */ + +#define _ASM +#include + +#if defined(_KERNEL) && !defined(__APPLE__) + /* + * Note: the CLTS macro clobbers P2 (%rsi) under i86xpv. That is, + * it calls HYPERVISOR_fpu_taskswitch() which modifies %rsi when it + * uses it to pass P2 to syscall. + * This also occurs with the STTS macro, but we dont care if + * P2 (%rsi) is modified just before function exit. + * The CLTS and STTS macros push and pop P1 (%rdi) already. + */ +#ifdef __xpv +#define PROTECTED_CLTS \ + push %rsi; \ + CLTS; \ + pop %rsi +#else +#define PROTECTED_CLTS \ + CLTS +#endif /* __xpv */ + +#define CLEAR_TS_OR_PUSH_XMM0_XMM1(tmpreg) \ + push %rbp; \ + mov %rsp, %rbp; \ + movq %cr0, tmpreg; \ + testq $CR0_TS, tmpreg; \ + jnz 1f; \ + and $-XMM_ALIGN, %rsp; \ + sub $(XMM_SIZE * 2), %rsp; \ + movaps %xmm0, 16(%rsp); \ + movaps %xmm1, (%rsp); \ + jmp 2f; \ +1: \ + PROTECTED_CLTS; \ +2: + + /* + * If CR0_TS was not set above, pop %xmm0 and %xmm1 off stack, + * otherwise set CR0_TS. + */ +#define SET_TS_OR_POP_XMM0_XMM1(tmpreg) \ + testq $CR0_TS, tmpreg; \ + jnz 1f; \ + movaps (%rsp), %xmm1; \ + movaps 16(%rsp), %xmm0; \ + jmp 2f; \ +1: \ + STTS(tmpreg); \ +2: \ + mov %rbp, %rsp; \ + pop %rbp + + /* + * If CR0_TS is not set, align stack (with push %rbp) and push + * %xmm0 - %xmm6 on stack, otherwise clear CR0_TS + */ +#define CLEAR_TS_OR_PUSH_XMM0_TO_XMM6(tmpreg) \ + push %rbp; \ + mov %rsp, %rbp; \ + movq %cr0, tmpreg; \ + testq $CR0_TS, tmpreg; \ + jnz 1f; \ + and $-XMM_ALIGN, %rsp; \ + sub $(XMM_SIZE * 7), %rsp; \ + movaps %xmm0, 96(%rsp); \ + movaps %xmm1, 80(%rsp); \ + movaps %xmm2, 64(%rsp); \ + movaps %xmm3, 48(%rsp); \ + movaps %xmm4, 32(%rsp); \ + movaps %xmm5, 16(%rsp); \ + movaps %xmm6, (%rsp); \ + jmp 2f; \ +1: \ + PROTECTED_CLTS; \ +2: + + + /* + * If CR0_TS was not set above, pop %xmm0 - %xmm6 off stack, + * otherwise set CR0_TS. + */ +#define SET_TS_OR_POP_XMM0_TO_XMM6(tmpreg) \ + testq $CR0_TS, tmpreg; \ + jnz 1f; \ + movaps (%rsp), %xmm6; \ + movaps 16(%rsp), %xmm5; \ + movaps 32(%rsp), %xmm4; \ + movaps 48(%rsp), %xmm3; \ + movaps 64(%rsp), %xmm2; \ + movaps 80(%rsp), %xmm1; \ + movaps 96(%rsp), %xmm0; \ + jmp 2f; \ +1: \ + STTS(tmpreg); \ +2: \ + mov %rbp, %rsp; \ + pop %rbp + + +#else +#define PROTECTED_CLTS +#define CLEAR_TS_OR_PUSH_XMM0_XMM1(tmpreg) +#define SET_TS_OR_POP_XMM0_XMM1(tmpreg) +#define CLEAR_TS_OR_PUSH_XMM0_TO_XMM6(tmpreg) +#define SET_TS_OR_POP_XMM0_TO_XMM6(tmpreg) +#endif /* _KERNEL */ + + +/* + * _key_expansion_128(), * _key_expansion_192a(), _key_expansion_192b(), + * _key_expansion_256a(), _key_expansion_256b() + * + * Helper functions called by rijndael_key_setup_inc_intel(). + * Also used indirectly by rijndael_key_setup_dec_intel(). + * + * Input: + * %xmm0 User-provided cipher key + * %xmm1 Round constant + * Output: + * (%rcx) AES key + */ + +.align 4, 0x90 +_key_expansion_128: +_key_expansion_256a: + pshufd $0b11111111, %xmm1, %xmm1 + shufps $0b00010000, %xmm0, %xmm4 + pxor %xmm4, %xmm0 + shufps $0b10001100, %xmm0, %xmm4 + pxor %xmm4, %xmm0 + pxor %xmm1, %xmm0 + movups %xmm0, (%rcx) + add $0x10, %rcx + ret + SET_SIZE(_key_expansion_128) + SET_SIZE(_key_expansion_256a) + +.align 4, 0x90 +_key_expansion_192a: + pshufd $0b01010101, %xmm1, %xmm1 + shufps $0b00010000, %xmm0, %xmm4 + pxor %xmm4, %xmm0 + shufps $0b10001100, %xmm0, %xmm4 + pxor %xmm4, %xmm0 + pxor %xmm1, %xmm0 + + movups %xmm2, %xmm5 + movups %xmm2, %xmm6 + pslldq $4, %xmm5 + pshufd $0b11111111, %xmm0, %xmm3 + pxor %xmm3, %xmm2 + pxor %xmm5, %xmm2 + + movups %xmm0, %xmm1 + shufps $0b01000100, %xmm0, %xmm6 + movups %xmm6, (%rcx) + shufps $0b01001110, %xmm2, %xmm1 + movups %xmm1, 0x10(%rcx) + add $0x20, %rcx + ret + SET_SIZE(_key_expansion_192a) + +.align 4, 0x90 +_key_expansion_192b: + pshufd $0b01010101, %xmm1, %xmm1 + shufps $0b00010000, %xmm0, %xmm4 + pxor %xmm4, %xmm0 + shufps $0b10001100, %xmm0, %xmm4 + pxor %xmm4, %xmm0 + pxor %xmm1, %xmm0 + + movups %xmm2, %xmm5 + pslldq $4, %xmm5 + pshufd $0b11111111, %xmm0, %xmm3 + pxor %xmm3, %xmm2 + pxor %xmm5, %xmm2 + + movups %xmm0, (%rcx) + add $0x10, %rcx + ret + SET_SIZE(_key_expansion_192b) + +.align 4, 0x90 +_key_expansion_256b: + pshufd $0b10101010, %xmm1, %xmm1 + shufps $0b00010000, %xmm2, %xmm4 + pxor %xmm4, %xmm2 + shufps $0b10001100, %xmm2, %xmm4 + pxor %xmm4, %xmm2 + pxor %xmm1, %xmm2 + movups %xmm2, (%rcx) + add $0x10, %rcx + ret + SET_SIZE(_key_expansion_256b) + + +/* + * rijndael_key_setup_enc_intel() + * Expand the cipher key into the encryption key schedule. + * + * For kernel code, caller is responsible for ensuring kpreempt_disable() + * has been called. This is because %xmm registers are not saved/restored. + * Clear and set the CR0.TS bit on entry and exit, respectively, if TS is set + * on entry. Otherwise, if TS is not set, save and restore %xmm registers + * on the stack. + * + * OpenSolaris interface: + * int rijndael_key_setup_enc_intel(uint32_t rk[], const uint32_t cipherKey[], + * uint64_t keyBits); + * Return value is 0 on error, number of rounds on success. + * + * Original Intel OpenSSL interface: + * int intel_AES_set_encrypt_key(const unsigned char *userKey, + * const int bits, AES_KEY *key); + * Return value is non-zero on error, 0 on success. + */ + +#ifdef OPENSSL_INTERFACE +#define rijndael_key_setup_enc_intel intel_AES_set_encrypt_key +#define rijndael_key_setup_dec_intel intel_AES_set_decrypt_key + +#define USERCIPHERKEY rdi /* P1, 64 bits */ +#define KEYSIZE32 esi /* P2, 32 bits */ +#define KEYSIZE64 rsi /* P2, 64 bits */ +#define AESKEY rdx /* P3, 64 bits */ + +#else /* OpenSolaris Interface */ +#define AESKEY rdi /* P1, 64 bits */ +#define USERCIPHERKEY rsi /* P2, 64 bits */ +#define KEYSIZE32 edx /* P3, 32 bits */ +#define KEYSIZE64 rdx /* P3, 64 bits */ +#endif /* OPENSSL_INTERFACE */ + +#define ROUNDS32 KEYSIZE32 /* temp */ +#define ROUNDS64 KEYSIZE64 /* temp */ +#define ENDAESKEY USERCIPHERKEY /* temp */ + +ENTRY_NP(rijndael_key_setup_enc_intel) +rijndael_key_setup_enc_intel_local: + CLEAR_TS_OR_PUSH_XMM0_TO_XMM6(%r10) + + // NULL pointer sanity check + test %USERCIPHERKEY, %USERCIPHERKEY + jz .Lenc_key_invalid_param + test %AESKEY, %AESKEY + jz .Lenc_key_invalid_param + + movups (%USERCIPHERKEY), %xmm0 // user key (first 16 bytes) + movups %xmm0, (%AESKEY) + lea 0x10(%AESKEY), %rcx // key addr + pxor %xmm4, %xmm4 // xmm4 is assumed 0 in _key_expansion_x + + cmp $256, %KEYSIZE32 + jnz .Lenc_key192 + + // AES 256: 14 rounds in encryption key schedule +#ifdef OPENSSL_INTERFACE + mov $14, %ROUNDS32 + movl %ROUNDS32, 240(%AESKEY) // key.rounds = 14 +#endif /* OPENSSL_INTERFACE */ + + movups 0x10(%USERCIPHERKEY), %xmm2 // other user key (2nd 16 bytes) + movups %xmm2, (%rcx) + add $0x10, %rcx + + aeskeygenassist $0x1, %xmm2, %xmm1 // expand the key + call _key_expansion_256a + aeskeygenassist $0x1, %xmm0, %xmm1 + call _key_expansion_256b + aeskeygenassist $0x2, %xmm2, %xmm1 // expand the key + call _key_expansion_256a + aeskeygenassist $0x2, %xmm0, %xmm1 + call _key_expansion_256b + aeskeygenassist $0x4, %xmm2, %xmm1 // expand the key + call _key_expansion_256a + aeskeygenassist $0x4, %xmm0, %xmm1 + call _key_expansion_256b + aeskeygenassist $0x8, %xmm2, %xmm1 // expand the key + call _key_expansion_256a + aeskeygenassist $0x8, %xmm0, %xmm1 + call _key_expansion_256b + aeskeygenassist $0x10, %xmm2, %xmm1 // expand the key + call _key_expansion_256a + aeskeygenassist $0x10, %xmm0, %xmm1 + call _key_expansion_256b + aeskeygenassist $0x20, %xmm2, %xmm1 // expand the key + call _key_expansion_256a + aeskeygenassist $0x20, %xmm0, %xmm1 + call _key_expansion_256b + aeskeygenassist $0x40, %xmm2, %xmm1 // expand the key + call _key_expansion_256a + + SET_TS_OR_POP_XMM0_TO_XMM6(%r10) +#ifdef OPENSSL_INTERFACE + xor %rax, %rax // return 0 (OK) +#else /* Open Solaris Interface */ + mov $14, %rax // return # rounds = 14 +#endif + ret + +.align 4 +.Lenc_key192: + cmp $192, %KEYSIZE32 + jnz .Lenc_key128 + + // AES 192: 12 rounds in encryption key schedule +#ifdef OPENSSL_INTERFACE + mov $12, %ROUNDS32 + movl %ROUNDS32, 240(%AESKEY) // key.rounds = 12 +#endif /* OPENSSL_INTERFACE */ + + movq 0x10(%USERCIPHERKEY), %xmm2 // other user key + aeskeygenassist $0x1, %xmm2, %xmm1 // expand the key + call _key_expansion_192a + aeskeygenassist $0x2, %xmm2, %xmm1 // expand the key + call _key_expansion_192b + aeskeygenassist $0x4, %xmm2, %xmm1 // expand the key + call _key_expansion_192a + aeskeygenassist $0x8, %xmm2, %xmm1 // expand the key + call _key_expansion_192b + aeskeygenassist $0x10, %xmm2, %xmm1 // expand the key + call _key_expansion_192a + aeskeygenassist $0x20, %xmm2, %xmm1 // expand the key + call _key_expansion_192b + aeskeygenassist $0x40, %xmm2, %xmm1 // expand the key + call _key_expansion_192a + aeskeygenassist $0x80, %xmm2, %xmm1 // expand the key + call _key_expansion_192b + + SET_TS_OR_POP_XMM0_TO_XMM6(%r10) +#ifdef OPENSSL_INTERFACE + xor %rax, %rax // return 0 (OK) +#else /* OpenSolaris Interface */ + mov $12, %rax // return # rounds = 12 +#endif + ret + +.align 4 +.Lenc_key128: + cmp $128, %KEYSIZE32 + jnz .Lenc_key_invalid_key_bits + + // AES 128: 10 rounds in encryption key schedule +#ifdef OPENSSL_INTERFACE + mov $10, %ROUNDS32 + movl %ROUNDS32, 240(%AESKEY) // key.rounds = 10 +#endif /* OPENSSL_INTERFACE */ + + aeskeygenassist $0x1, %xmm0, %xmm1 // expand the key + call _key_expansion_128 + aeskeygenassist $0x2, %xmm0, %xmm1 // expand the key + call _key_expansion_128 + aeskeygenassist $0x4, %xmm0, %xmm1 // expand the key + call _key_expansion_128 + aeskeygenassist $0x8, %xmm0, %xmm1 // expand the key + call _key_expansion_128 + aeskeygenassist $0x10, %xmm0, %xmm1 // expand the key + call _key_expansion_128 + aeskeygenassist $0x20, %xmm0, %xmm1 // expand the key + call _key_expansion_128 + aeskeygenassist $0x40, %xmm0, %xmm1 // expand the key + call _key_expansion_128 + aeskeygenassist $0x80, %xmm0, %xmm1 // expand the key + call _key_expansion_128 + aeskeygenassist $0x1b, %xmm0, %xmm1 // expand the key + call _key_expansion_128 + aeskeygenassist $0x36, %xmm0, %xmm1 // expand the key + call _key_expansion_128 + + SET_TS_OR_POP_XMM0_TO_XMM6(%r10) +#ifdef OPENSSL_INTERFACE + xor %rax, %rax // return 0 (OK) +#else /* OpenSolaris Interface */ + mov $10, %rax // return # rounds = 10 +#endif + ret + +.Lenc_key_invalid_param: +#ifdef OPENSSL_INTERFACE + SET_TS_OR_POP_XMM0_TO_XMM6(%r10) + mov $-1, %rax // user key or AES key pointer is NULL + ret +#else + /* FALLTHROUGH */ +#endif /* OPENSSL_INTERFACE */ + +.Lenc_key_invalid_key_bits: + SET_TS_OR_POP_XMM0_TO_XMM6(%r10) +#ifdef OPENSSL_INTERFACE + mov $-2, %rax // keysize is invalid +#else /* Open Solaris Interface */ + xor %rax, %rax // a key pointer is NULL or invalid keysize +#endif /* OPENSSL_INTERFACE */ + + ret + SET_SIZE(rijndael_key_setup_enc_intel) + + +/* + * rijndael_key_setup_dec_intel() + * Expand the cipher key into the decryption key schedule. + * + * For kernel code, caller is responsible for ensuring kpreempt_disable() + * has been called. This is because %xmm registers are not saved/restored. + * Clear and set the CR0.TS bit on entry and exit, respectively, if TS is set + * on entry. Otherwise, if TS is not set, save and restore %xmm registers + * on the stack. + * + * OpenSolaris interface: + * int rijndael_key_setup_dec_intel(uint32_t rk[], const uint32_t cipherKey[], + * uint64_t keyBits); + * Return value is 0 on error, number of rounds on success. + * P1->P2, P2->P3, P3->P1 + * + * Original Intel OpenSSL interface: + * int intel_AES_set_decrypt_key(const unsigned char *userKey, + * const int bits, AES_KEY *key); + * Return value is non-zero on error, 0 on success. + */ +ENTRY_NP(rijndael_key_setup_dec_intel) + // Generate round keys used for encryption + call rijndael_key_setup_enc_intel_local + test %rax, %rax +#ifdef OPENSSL_INTERFACE + jnz .Ldec_key_exit // Failed if returned non-0 +#else /* OpenSolaris Interface */ + jz .Ldec_key_exit // Failed if returned 0 +#endif /* OPENSSL_INTERFACE */ + + CLEAR_TS_OR_PUSH_XMM0_XMM1(%r10) + + /* + * Convert round keys used for encryption + * to a form usable for decryption + */ +#ifndef OPENSSL_INTERFACE /* OpenSolaris Interface */ + mov %rax, %ROUNDS64 // set # rounds (10, 12, or 14) + // (already set for OpenSSL) +#endif + + lea 0x10(%AESKEY), %rcx // key addr + shl $4, %ROUNDS32 + add %AESKEY, %ROUNDS64 + mov %ROUNDS64, %ENDAESKEY + +.align 4 +.Ldec_key_reorder_loop: + movups (%AESKEY), %xmm0 + movups (%ROUNDS64), %xmm1 + movups %xmm0, (%ROUNDS64) + movups %xmm1, (%AESKEY) + lea 0x10(%AESKEY), %AESKEY + lea -0x10(%ROUNDS64), %ROUNDS64 + cmp %AESKEY, %ROUNDS64 + ja .Ldec_key_reorder_loop + +.align 4 +.Ldec_key_inv_loop: + movups (%rcx), %xmm0 + // Convert an encryption round key to a form usable for decryption + // with the "AES Inverse Mix Columns" instruction + aesimc %xmm0, %xmm1 + movups %xmm1, (%rcx) + lea 0x10(%rcx), %rcx + cmp %ENDAESKEY, %rcx + jnz .Ldec_key_inv_loop + + SET_TS_OR_POP_XMM0_XMM1(%r10) + +.Ldec_key_exit: + // OpenSolaris: rax = # rounds (10, 12, or 14) or 0 for error + // OpenSSL: rax = 0 for OK, or non-zero for error + ret + SET_SIZE(rijndael_key_setup_dec_intel) + + +/* + * aes_encrypt_intel() + * Encrypt a single block (in and out can overlap). + * + * For kernel code, caller is responsible for ensuring kpreempt_disable() + * has been called. This is because %xmm registers are not saved/restored. + * Clear and set the CR0.TS bit on entry and exit, respectively, if TS is set + * on entry. Otherwise, if TS is not set, save and restore %xmm registers + * on the stack. + * + * Temporary register usage: + * %xmm0 State + * %xmm1 Key + * + * Original OpenSolaris Interface: + * void aes_encrypt_intel(const aes_ks_t *ks, int Nr, + * const uint32_t pt[4], uint32_t ct[4]) + * + * Original Intel OpenSSL Interface: + * void intel_AES_encrypt(const unsigned char *in, unsigned char *out, + * const AES_KEY *key) + */ + +#ifdef OPENSSL_INTERFACE +#define aes_encrypt_intel intel_AES_encrypt +#define aes_decrypt_intel intel_AES_decrypt + +#define INP rdi /* P1, 64 bits */ +#define OUTP rsi /* P2, 64 bits */ +#define KEYP rdx /* P3, 64 bits */ + +/* No NROUNDS parameter--offset 240 from KEYP saved in %ecx: */ +#define NROUNDS32 ecx /* temporary, 32 bits */ +#define NROUNDS cl /* temporary, 8 bits */ + +#else /* OpenSolaris Interface */ +#define KEYP rdi /* P1, 64 bits */ +#define NROUNDS esi /* P2, 32 bits */ +#define INP rdx /* P3, 64 bits */ +#define OUTP rcx /* P4, 64 bits */ +#endif /* OPENSSL_INTERFACE */ + +#define STATE xmm0 /* temporary, 128 bits */ +#define KEY xmm1 /* temporary, 128 bits */ + +ENTRY_NP(aes_encrypt_intel) + CLEAR_TS_OR_PUSH_XMM0_XMM1(%r10) + + movups (%INP), %STATE // input + movups (%KEYP), %KEY // key +#ifdef OPENSSL_INTERFACE + mov 240(%KEYP), %NROUNDS32 // round count +#else /* OpenSolaris Interface */ + /* Round count is already present as P2 in %rsi/%esi */ +#endif /* OPENSSL_INTERFACE */ + + pxor %KEY, %STATE // round 0 + lea 0x30(%KEYP), %KEYP + cmp $12, %NROUNDS + jb .Lenc128 + lea 0x20(%KEYP), %KEYP + je .Lenc192 + + // AES 256 + lea 0x20(%KEYP), %KEYP + movups -0x60(%KEYP), %KEY + aesenc %KEY, %STATE + movups -0x50(%KEYP), %KEY + aesenc %KEY, %STATE + +.align 4 +.Lenc192: + // AES 192 and 256 + movups -0x40(%KEYP), %KEY + aesenc %KEY, %STATE + movups -0x30(%KEYP), %KEY + aesenc %KEY, %STATE + +.align 4 +.Lenc128: + // AES 128, 192, and 256 + movups -0x20(%KEYP), %KEY + aesenc %KEY, %STATE + movups -0x10(%KEYP), %KEY + aesenc %KEY, %STATE + movups (%KEYP), %KEY + aesenc %KEY, %STATE + movups 0x10(%KEYP), %KEY + aesenc %KEY, %STATE + movups 0x20(%KEYP), %KEY + aesenc %KEY, %STATE + movups 0x30(%KEYP), %KEY + aesenc %KEY, %STATE + movups 0x40(%KEYP), %KEY + aesenc %KEY, %STATE + movups 0x50(%KEYP), %KEY + aesenc %KEY, %STATE + movups 0x60(%KEYP), %KEY + aesenc %KEY, %STATE + movups 0x70(%KEYP), %KEY + aesenclast %KEY, %STATE // last round + movups %STATE, (%OUTP) // output + + SET_TS_OR_POP_XMM0_XMM1(%r10) + ret + SET_SIZE(aes_encrypt_intel) + + +/* + * aes_decrypt_intel() + * Decrypt a single block (in and out can overlap). + * + * For kernel code, caller is responsible for ensuring kpreempt_disable() + * has been called. This is because %xmm registers are not saved/restored. + * Clear and set the CR0.TS bit on entry and exit, respectively, if TS is set + * on entry. Otherwise, if TS is not set, save and restore %xmm registers + * on the stack. + * + * Temporary register usage: + * %xmm0 State + * %xmm1 Key + * + * Original OpenSolaris Interface: + * void aes_decrypt_intel(const aes_ks_t *ks, int Nr, + * const uint32_t pt[4], uint32_t ct[4])/ + * + * Original Intel OpenSSL Interface: + * void intel_AES_decrypt(const unsigned char *in, unsigned char *out, + * const AES_KEY *key); + */ +ENTRY_NP(aes_decrypt_intel) + CLEAR_TS_OR_PUSH_XMM0_XMM1(%r10) + + movups (%INP), %STATE // input + movups (%KEYP), %KEY // key +#ifdef OPENSSL_INTERFACE + mov 240(%KEYP), %NROUNDS32 // round count +#else /* OpenSolaris Interface */ + /* Round count is already present as P2 in %rsi/%esi */ +#endif /* OPENSSL_INTERFACE */ + + pxor %KEY, %STATE // round 0 + lea 0x30(%KEYP), %KEYP + cmp $12, %NROUNDS + jb .Ldec128 + lea 0x20(%KEYP), %KEYP + je .Ldec192 + + // AES 256 + lea 0x20(%KEYP), %KEYP + movups -0x60(%KEYP), %KEY + aesdec %KEY, %STATE + movups -0x50(%KEYP), %KEY + aesdec %KEY, %STATE + +.align 4 +.Ldec192: + // AES 192 and 256 + movups -0x40(%KEYP), %KEY + aesdec %KEY, %STATE + movups -0x30(%KEYP), %KEY + aesdec %KEY, %STATE + +.align 4 +.Ldec128: + // AES 128, 192, and 256 + movups -0x20(%KEYP), %KEY + aesdec %KEY, %STATE + movups -0x10(%KEYP), %KEY + aesdec %KEY, %STATE + movups (%KEYP), %KEY + aesdec %KEY, %STATE + movups 0x10(%KEYP), %KEY + aesdec %KEY, %STATE + movups 0x20(%KEYP), %KEY + aesdec %KEY, %STATE + movups 0x30(%KEYP), %KEY + aesdec %KEY, %STATE + movups 0x40(%KEYP), %KEY + aesdec %KEY, %STATE + movups 0x50(%KEYP), %KEY + aesdec %KEY, %STATE + movups 0x60(%KEYP), %KEY + aesdec %KEY, %STATE + movups 0x70(%KEYP), %KEY + aesdeclast %KEY, %STATE // last round + movups %STATE, (%OUTP) // output + + SET_TS_OR_POP_XMM0_XMM1(%r10) + ret + SET_SIZE(aes_decrypt_intel) + +#endif /* lint || __lint */ diff --git a/module/icp/asm-x86_64/os/macos/aes/aes_amd64.S b/module/icp/asm-x86_64/os/macos/aes/aes_amd64.S new file mode 100644 index 000000000000..cdd9a861be71 --- /dev/null +++ b/module/icp/asm-x86_64/os/macos/aes/aes_amd64.S @@ -0,0 +1,900 @@ +/* + * --------------------------------------------------------------------------- + * Copyright (c) 1998-2007, Brian Gladman, Worcester, UK. All rights reserved. + * + * LICENSE TERMS + * + * The free distribution and use of this software is allowed (with or without + * changes) provided that: + * + * 1. source code distributions include the above copyright notice, this + * list of conditions and the following disclaimer; + * + * 2. binary distributions include the above copyright notice, this list + * of conditions and the following disclaimer in their documentation; + * + * 3. the name of the copyright holder is not used to endorse products + * built using this software without specific written permission. + * + * DISCLAIMER + * + * This software is provided 'as is' with no explicit or implied warranties + * in respect of its properties, including, but not limited to, correctness + * and/or fitness for purpose. + * --------------------------------------------------------------------------- + * Issue 20/12/2007 + * + * I am grateful to Dag Arne Osvik for many discussions of the techniques that + * can be used to optimise AES assembler code on AMD64/EM64T architectures. + * Some of the techniques used in this implementation are the result of + * suggestions made by him for which I am most grateful. + * + * An AES implementation for AMD64 processors using the YASM assembler. This + * implementation provides only encryption, decryption and hence requires key + * scheduling support in C. It uses 8k bytes of tables but its encryption and + * decryption performance is very close to that obtained using large tables. + * It can use either MS Windows or Gnu/Linux/OpenSolaris OS calling conventions, + * which are as follows: + * ms windows gnu/linux/opensolaris os + * + * in_blk rcx rdi + * out_blk rdx rsi + * context (cx) r8 rdx + * + * preserved rsi - + rbx, rbp, rsp, r12, r13, r14 & r15 + * registers rdi - on both + * + * destroyed - rsi + rax, rcx, rdx, r8, r9, r10 & r11 + * registers - rdi on both + * + * The convention used here is that for gnu/linux/opensolaris os. + * + * This code provides the standard AES block size (128 bits, 16 bytes) and the + * three standard AES key sizes (128, 192 and 256 bits). It has the same call + * interface as my C implementation. It uses the Microsoft C AMD64 calling + * conventions in which the three parameters are placed in rcx, rdx and r8 + * respectively. The rbx, rsi, rdi, rbp and r12..r15 registers are preserved. + * + * OpenSolaris Note: + * Modified to use GNU/Linux/Solaris calling conventions. + * That is parameters are placed in rdi, rsi, rdx, and rcx, respectively. + * + * AES_RETURN aes_encrypt(const unsigned char in_blk[], + * unsigned char out_blk[], const aes_encrypt_ctx cx[1])/ + * + * AES_RETURN aes_decrypt(const unsigned char in_blk[], + * unsigned char out_blk[], const aes_decrypt_ctx cx[1])/ + * + * AES_RETURN aes_encrypt_key(const unsigned char key[], + * const aes_encrypt_ctx cx[1])/ + * + * AES_RETURN aes_decrypt_key(const unsigned char key[], + * const aes_decrypt_ctx cx[1])/ + * + * AES_RETURN aes_encrypt_key(const unsigned char key[], + * unsigned int len, const aes_decrypt_ctx cx[1])/ + * + * AES_RETURN aes_decrypt_key(const unsigned char key[], + * unsigned int len, const aes_decrypt_ctx cx[1])/ + * + * where is 128, 102 or 256. In the last two calls the length can be in + * either bits or bytes. + * + * Comment in/out the following lines to obtain the desired subroutines. These + * selections MUST match those in the C header file aesopt.h + */ +#define AES_REV_DKS /* define if key decryption schedule is reversed */ + +#define LAST_ROUND_TABLES /* define for the faster version using extra tables */ + +/* + * The encryption key schedule has the following in memory layout where N is the + * number of rounds (10, 12 or 14): + * + * lo: | input key (round 0) | / each round is four 32-bit words + * | encryption round 1 | + * | encryption round 2 | + * .... + * | encryption round N-1 | + * hi: | encryption round N | + * + * The decryption key schedule is normally set up so that it has the same + * layout as above by actually reversing the order of the encryption key + * schedule in memory (this happens when AES_REV_DKS is set): + * + * lo: | decryption round 0 | = | encryption round N | + * | decryption round 1 | = INV_MIX_COL[ | encryption round N-1 | ] + * | decryption round 2 | = INV_MIX_COL[ | encryption round N-2 | ] + * .... .... + * | decryption round N-1 | = INV_MIX_COL[ | encryption round 1 | ] + * hi: | decryption round N | = | input key (round 0) | + * + * with rounds except the first and last modified using inv_mix_column() + * But if AES_REV_DKS is NOT set the order of keys is left as it is for + * encryption so that it has to be accessed in reverse when used for + * decryption (although the inverse mix column modifications are done) + * + * lo: | decryption round 0 | = | input key (round 0) | + * | decryption round 1 | = INV_MIX_COL[ | encryption round 1 | ] + * | decryption round 2 | = INV_MIX_COL[ | encryption round 2 | ] + * .... .... + * | decryption round N-1 | = INV_MIX_COL[ | encryption round N-1 | ] + * hi: | decryption round N | = | encryption round N | + * + * This layout is faster when the assembler key scheduling provided here + * is used. + * + * End of user defines + */ + +/* + * --------------------------------------------------------------------------- + * OpenSolaris OS modifications + * + * This source originates from Brian Gladman file aes_amd64.asm + * in http://fp.gladman.plus.com/AES/aes-src-04-03-08.zip + * with these changes: + * + * 1. Removed MS Windows-specific code within DLL_EXPORT, _SEH_, and + * !__GNUC__ ifdefs. Also removed ENCRYPTION, DECRYPTION, + * AES_128, AES_192, AES_256, AES_VAR ifdefs. + * + * 2. Translate yasm/nasm %define and .macro definitions to cpp(1) #define + * + * 3. Translate yasm/nasm %ifdef/%ifndef to cpp(1) #ifdef + * + * 4. Translate Intel/yasm/nasm syntax to ATT/OpenSolaris as(1) syntax + * (operands reversed, literals prefixed with "$", registers prefixed with "%", + * and "[register+offset]", addressing changed to "offset(register)", + * parenthesis in constant expressions "()" changed to square brackets "[]", + * "." removed from local (numeric) labels, and other changes. + * Examples: + * Intel/yasm/nasm Syntax ATT/OpenSolaris Syntax + * mov rax,(4*20h) mov $[4*0x20],%rax + * mov rax,[ebx+20h] mov 0x20(%ebx),%rax + * lea rax,[ebx+ecx] lea (%ebx,%ecx),%rax + * sub rax,[ebx+ecx*4-20h] sub -0x20(%ebx,%ecx,4),%rax + * + * 5. Added OpenSolaris ENTRY_NP/SET_SIZE macros from + * /usr/include/sys/asm_linkage.h, lint(1B) guards, and dummy C function + * definitions for lint. + * + * 6. Renamed functions and reordered parameters to match OpenSolaris: + * Original Gladman interface: + * int aes_encrypt(const unsigned char *in, + * unsigned char *out, const aes_encrypt_ctx cx[1])/ + * int aes_decrypt(const unsigned char *in, + * unsigned char *out, const aes_encrypt_ctx cx[1])/ + * Note: aes_encrypt_ctx contains ks, a 60 element array of uint32_t, + * and a union type, inf., containing inf.l, a uint32_t and + * inf.b, a 4-element array of uint32_t. Only b[0] in the array (aka "l") is + * used and contains the key schedule length * 16 where key schedule length is + * 10, 12, or 14 bytes. + * + * OpenSolaris OS interface: + * void aes_encrypt_amd64(const aes_ks_t *ks, int Nr, + * const uint32_t pt[4], uint32_t ct[4])/ + * void aes_decrypt_amd64(const aes_ks_t *ks, int Nr, + * const uint32_t pt[4], uint32_t ct[4])/ + * typedef union {uint64_t ks64[(MAX_AES_NR + 1) * 4]/ + * uint32_t ks32[(MAX_AES_NR + 1) * 4]/ } aes_ks_t/ + * Note: ks is the AES key schedule, Nr is number of rounds, pt is plain text, + * ct is crypto text, and MAX_AES_NR is 14. + * For the x86 64-bit architecture, OpenSolaris OS uses ks32 instead of ks64. + */ + +#if defined(lint) || defined(__lint) + +#include +/* ARGSUSED */ +void +aes_encrypt_amd64(const uint32_t rk[], int Nr, const uint32_t pt[4], + uint32_t ct[4]) { +} +/* ARGSUSED */ +void +aes_decrypt_amd64(const uint32_t rk[], int Nr, const uint32_t ct[4], + uint32_t pt[4]) { +} + + +#else + +#define _ASM +#include + +#define KS_LENGTH 60 + +#define raxd eax +#define rdxd edx +#define rcxd ecx +#define rbxd ebx +#define rsid esi +#define rdid edi + +#define raxb al +#define rdxb dl +#define rcxb cl +#define rbxb bl +#define rsib sil +#define rdib dil + +// finite field multiplies by {02}, {04} and {08} + +#define f2(x) ((x<<1)^(((x>>7)&1)*0x11b)) +#define f4(x) ((x<<2)^(((x>>6)&1)*0x11b)^(((x>>6)&2)*0x11b)) +#define f8(x) ((x<<3)^(((x>>5)&1)*0x11b)^(((x>>5)&2)*0x11b)^(((x>>5)&4)*0x11b)) + +// finite field multiplies required in table generation + +#define f3(x) ((f2(x)) ^ (x)) +#define f9(x) ((f8(x)) ^ (x)) +#define fb(x) ((f8(x)) ^ (f2(x)) ^ (x)) +#define fd(x) ((f8(x)) ^ (f4(x)) ^ (x)) +#define fe(x) ((f8(x)) ^ (f4(x)) ^ (f2(x))) + +// macros for expanding S-box data + +#define u8(x) (f2(x)), (x), (x), (f3(x)), (f2(x)), (x), (x), (f3(x)) +#define v8(x) (fe(x)), (f9(x)), (fd(x)), (fb(x)), (fe(x)), (f9(x)), (fd(x)), (x) +#define w8(x) (x), 0, 0, 0, (x), 0, 0, 0 + +#define enc_vals(x) \ + .byte x(0x63),x(0x7c),x(0x77),x(0x7b),x(0xf2),x(0x6b),x(0x6f),x(0xc5); \ + .byte x(0x30),x(0x01),x(0x67),x(0x2b),x(0xfe),x(0xd7),x(0xab),x(0x76); \ + .byte x(0xca),x(0x82),x(0xc9),x(0x7d),x(0xfa),x(0x59),x(0x47),x(0xf0); \ + .byte x(0xad),x(0xd4),x(0xa2),x(0xaf),x(0x9c),x(0xa4),x(0x72),x(0xc0); \ + .byte x(0xb7),x(0xfd),x(0x93),x(0x26),x(0x36),x(0x3f),x(0xf7),x(0xcc); \ + .byte x(0x34),x(0xa5),x(0xe5),x(0xf1),x(0x71),x(0xd8),x(0x31),x(0x15); \ + .byte x(0x04),x(0xc7),x(0x23),x(0xc3),x(0x18),x(0x96),x(0x05),x(0x9a); \ + .byte x(0x07),x(0x12),x(0x80),x(0xe2),x(0xeb),x(0x27),x(0xb2),x(0x75); \ + .byte x(0x09),x(0x83),x(0x2c),x(0x1a),x(0x1b),x(0x6e),x(0x5a),x(0xa0); \ + .byte x(0x52),x(0x3b),x(0xd6),x(0xb3),x(0x29),x(0xe3),x(0x2f),x(0x84); \ + .byte x(0x53),x(0xd1),x(0x00),x(0xed),x(0x20),x(0xfc),x(0xb1),x(0x5b); \ + .byte x(0x6a),x(0xcb),x(0xbe),x(0x39),x(0x4a),x(0x4c),x(0x58),x(0xcf); \ + .byte x(0xd0),x(0xef),x(0xaa),x(0xfb),x(0x43),x(0x4d),x(0x33),x(0x85); \ + .byte x(0x45),x(0xf9),x(0x02),x(0x7f),x(0x50),x(0x3c),x(0x9f),x(0xa8); \ + .byte x(0x51),x(0xa3),x(0x40),x(0x8f),x(0x92),x(0x9d),x(0x38),x(0xf5); \ + .byte x(0xbc),x(0xb6),x(0xda),x(0x21),x(0x10),x(0xff),x(0xf3),x(0xd2); \ + .byte x(0xcd),x(0x0c),x(0x13),x(0xec),x(0x5f),x(0x97),x(0x44),x(0x17); \ + .byte x(0xc4),x(0xa7),x(0x7e),x(0x3d),x(0x64),x(0x5d),x(0x19),x(0x73); \ + .byte x(0x60),x(0x81),x(0x4f),x(0xdc),x(0x22),x(0x2a),x(0x90),x(0x88); \ + .byte x(0x46),x(0xee),x(0xb8),x(0x14),x(0xde),x(0x5e),x(0x0b),x(0xdb); \ + .byte x(0xe0),x(0x32),x(0x3a),x(0x0a),x(0x49),x(0x06),x(0x24),x(0x5c); \ + .byte x(0xc2),x(0xd3),x(0xac),x(0x62),x(0x91),x(0x95),x(0xe4),x(0x79); \ + .byte x(0xe7),x(0xc8),x(0x37),x(0x6d),x(0x8d),x(0xd5),x(0x4e),x(0xa9); \ + .byte x(0x6c),x(0x56),x(0xf4),x(0xea),x(0x65),x(0x7a),x(0xae),x(0x08); \ + .byte x(0xba),x(0x78),x(0x25),x(0x2e),x(0x1c),x(0xa6),x(0xb4),x(0xc6); \ + .byte x(0xe8),x(0xdd),x(0x74),x(0x1f),x(0x4b),x(0xbd),x(0x8b),x(0x8a); \ + .byte x(0x70),x(0x3e),x(0xb5),x(0x66),x(0x48),x(0x03),x(0xf6),x(0x0e); \ + .byte x(0x61),x(0x35),x(0x57),x(0xb9),x(0x86),x(0xc1),x(0x1d),x(0x9e); \ + .byte x(0xe1),x(0xf8),x(0x98),x(0x11),x(0x69),x(0xd9),x(0x8e),x(0x94); \ + .byte x(0x9b),x(0x1e),x(0x87),x(0xe9),x(0xce),x(0x55),x(0x28),x(0xdf); \ + .byte x(0x8c),x(0xa1),x(0x89),x(0x0d),x(0xbf),x(0xe6),x(0x42),x(0x68); \ + .byte x(0x41),x(0x99),x(0x2d),x(0x0f),x(0xb0),x(0x54),x(0xbb),x(0x16) + +#define dec_vals(x) \ + .byte x(0x52),x(0x09),x(0x6a),x(0xd5),x(0x30),x(0x36),x(0xa5),x(0x38); \ + .byte x(0xbf),x(0x40),x(0xa3),x(0x9e),x(0x81),x(0xf3),x(0xd7),x(0xfb); \ + .byte x(0x7c),x(0xe3),x(0x39),x(0x82),x(0x9b),x(0x2f),x(0xff),x(0x87); \ + .byte x(0x34),x(0x8e),x(0x43),x(0x44),x(0xc4),x(0xde),x(0xe9),x(0xcb); \ + .byte x(0x54),x(0x7b),x(0x94),x(0x32),x(0xa6),x(0xc2),x(0x23),x(0x3d); \ + .byte x(0xee),x(0x4c),x(0x95),x(0x0b),x(0x42),x(0xfa),x(0xc3),x(0x4e); \ + .byte x(0x08),x(0x2e),x(0xa1),x(0x66),x(0x28),x(0xd9),x(0x24),x(0xb2); \ + .byte x(0x76),x(0x5b),x(0xa2),x(0x49),x(0x6d),x(0x8b),x(0xd1),x(0x25); \ + .byte x(0x72),x(0xf8),x(0xf6),x(0x64),x(0x86),x(0x68),x(0x98),x(0x16); \ + .byte x(0xd4),x(0xa4),x(0x5c),x(0xcc),x(0x5d),x(0x65),x(0xb6),x(0x92); \ + .byte x(0x6c),x(0x70),x(0x48),x(0x50),x(0xfd),x(0xed),x(0xb9),x(0xda); \ + .byte x(0x5e),x(0x15),x(0x46),x(0x57),x(0xa7),x(0x8d),x(0x9d),x(0x84); \ + .byte x(0x90),x(0xd8),x(0xab),x(0x00),x(0x8c),x(0xbc),x(0xd3),x(0x0a); \ + .byte x(0xf7),x(0xe4),x(0x58),x(0x05),x(0xb8),x(0xb3),x(0x45),x(0x06); \ + .byte x(0xd0),x(0x2c),x(0x1e),x(0x8f),x(0xca),x(0x3f),x(0x0f),x(0x02); \ + .byte x(0xc1),x(0xaf),x(0xbd),x(0x03),x(0x01),x(0x13),x(0x8a),x(0x6b); \ + .byte x(0x3a),x(0x91),x(0x11),x(0x41),x(0x4f),x(0x67),x(0xdc),x(0xea); \ + .byte x(0x97),x(0xf2),x(0xcf),x(0xce),x(0xf0),x(0xb4),x(0xe6),x(0x73); \ + .byte x(0x96),x(0xac),x(0x74),x(0x22),x(0xe7),x(0xad),x(0x35),x(0x85); \ + .byte x(0xe2),x(0xf9),x(0x37),x(0xe8),x(0x1c),x(0x75),x(0xdf),x(0x6e); \ + .byte x(0x47),x(0xf1),x(0x1a),x(0x71),x(0x1d),x(0x29),x(0xc5),x(0x89); \ + .byte x(0x6f),x(0xb7),x(0x62),x(0x0e),x(0xaa),x(0x18),x(0xbe),x(0x1b); \ + .byte x(0xfc),x(0x56),x(0x3e),x(0x4b),x(0xc6),x(0xd2),x(0x79),x(0x20); \ + .byte x(0x9a),x(0xdb),x(0xc0),x(0xfe),x(0x78),x(0xcd),x(0x5a),x(0xf4); \ + .byte x(0x1f),x(0xdd),x(0xa8),x(0x33),x(0x88),x(0x07),x(0xc7),x(0x31); \ + .byte x(0xb1),x(0x12),x(0x10),x(0x59),x(0x27),x(0x80),x(0xec),x(0x5f); \ + .byte x(0x60),x(0x51),x(0x7f),x(0xa9),x(0x19),x(0xb5),x(0x4a),x(0x0d); \ + .byte x(0x2d),x(0xe5),x(0x7a),x(0x9f),x(0x93),x(0xc9),x(0x9c),x(0xef); \ + .byte x(0xa0),x(0xe0),x(0x3b),x(0x4d),x(0xae),x(0x2a),x(0xf5),x(0xb0); \ + .byte x(0xc8),x(0xeb),x(0xbb),x(0x3c),x(0x83),x(0x53),x(0x99),x(0x61); \ + .byte x(0x17),x(0x2b),x(0x04),x(0x7e),x(0xba),x(0x77),x(0xd6),x(0x26); \ + .byte x(0xe1),x(0x69),x(0x14),x(0x63),x(0x55),x(0x21),x(0x0c),x(0x7d) + +#define tptr %rbp /* table pointer */ +#define kptr %r8 /* key schedule pointer */ +#define fofs 128 /* adjust offset in key schedule to keep |disp| < 128 */ +#define fk_ref(x, y) -16*x+fofs+4*y(kptr) + +#ifdef AES_REV_DKS +#define rofs 128 +#define ik_ref(x, y) -16*x+rofs+4*y(kptr) + +#else +#define rofs -128 +#define ik_ref(x, y) 16*x+rofs+4*y(kptr) +#endif /* AES_REV_DKS */ + +#define tab_0(x) (tptr,x,8) +#define tab_1(x) 3(tptr,x,8) +#define tab_2(x) 2(tptr,x,8) +#define tab_3(x) 1(tptr,x,8) +#define tab_f(x) 1(tptr,x,8) +#define tab_i(x) 7(tptr,x,8) + +#define ff_rnd(p1, p2, p3, p4, round) /* normal forward round */ \ + mov fk_ref(round,0), p1; \ + mov fk_ref(round,1), p2; \ + mov fk_ref(round,2), p3; \ + mov fk_ref(round,3), p4; \ + \ + movzx %al, %esi; \ + movzx %ah, %edi; \ + shr $16, %eax; \ + xor tab_0(%rsi), p1; \ + xor tab_1(%rdi), p4; \ + movzx %al, %esi; \ + movzx %ah, %edi; \ + xor tab_2(%rsi), p3; \ + xor tab_3(%rdi), p2; \ + \ + movzx %bl, %esi; \ + movzx %bh, %edi; \ + shr $16, %ebx; \ + xor tab_0(%rsi), p2; \ + xor tab_1(%rdi), p1; \ + movzx %bl, %esi; \ + movzx %bh, %edi; \ + xor tab_2(%rsi), p4; \ + xor tab_3(%rdi), p3; \ + \ + movzx %cl, %esi; \ + movzx %ch, %edi; \ + shr $16, %ecx; \ + xor tab_0(%rsi), p3; \ + xor tab_1(%rdi), p2; \ + movzx %cl, %esi; \ + movzx %ch, %edi; \ + xor tab_2(%rsi), p1; \ + xor tab_3(%rdi), p4; \ + \ + movzx %dl, %esi; \ + movzx %dh, %edi; \ + shr $16, %edx; \ + xor tab_0(%rsi), p4; \ + xor tab_1(%rdi), p3; \ + movzx %dl, %esi; \ + movzx %dh, %edi; \ + xor tab_2(%rsi), p2; \ + xor tab_3(%rdi), p1; \ + \ + mov p1, %eax; \ + mov p2, %ebx; \ + mov p3, %ecx; \ + mov p4, %edx + +#ifdef LAST_ROUND_TABLES + +#define fl_rnd(p1, p2, p3, p4, round) /* last forward round */ \ + add $2048, tptr; \ + mov fk_ref(round,0), p1; \ + mov fk_ref(round,1), p2; \ + mov fk_ref(round,2), p3; \ + mov fk_ref(round,3), p4; \ + \ + movzx %al, %esi; \ + movzx %ah, %edi; \ + shr $16, %eax; \ + xor tab_0(%rsi), p1; \ + xor tab_1(%rdi), p4; \ + movzx %al, %esi; \ + movzx %ah, %edi; \ + xor tab_2(%rsi), p3; \ + xor tab_3(%rdi), p2; \ + \ + movzx %bl, %esi; \ + movzx %bh, %edi; \ + shr $16, %ebx; \ + xor tab_0(%rsi), p2; \ + xor tab_1(%rdi), p1; \ + movzx %bl, %esi; \ + movzx %bh, %edi; \ + xor tab_2(%rsi), p4; \ + xor tab_3(%rdi), p3; \ + \ + movzx %cl, %esi; \ + movzx %ch, %edi; \ + shr $16, %ecx; \ + xor tab_0(%rsi), p3; \ + xor tab_1(%rdi), p2; \ + movzx %cl, %esi; \ + movzx %ch, %edi; \ + xor tab_2(%rsi), p1; \ + xor tab_3(%rdi), p4; \ + \ + movzx %dl, %esi; \ + movzx %dh, %edi; \ + shr $16, %edx; \ + xor tab_0(%rsi), p4; \ + xor tab_1(%rdi), p3; \ + movzx %dl, %esi; \ + movzx %dh, %edi; \ + xor tab_2(%rsi), p2; \ + xor tab_3(%rdi), p1 + +#else + +#define fl_rnd(p1, p2, p3, p4, round) /* last forward round */ \ + mov fk_ref(round,0), p1; \ + mov fk_ref(round,1), p2; \ + mov fk_ref(round,2), p3; \ + mov fk_ref(round,3), p4; \ + \ + movzx %al, %esi; \ + movzx %ah, %edi; \ + shr $16, %eax; \ + movzx tab_f(%rsi), %esi; \ + movzx tab_f(%rdi), %edi; \ + xor %esi, p1; \ + rol $8, %edi; \ + xor %edi, p4; \ + movzx %al, %esi; \ + movzx %ah, %edi; \ + movzx tab_f(%rsi), %esi; \ + movzx tab_f(%rdi), %edi; \ + rol $16, %esi; \ + rol $24, %edi; \ + xor %esi, p3; \ + xor %edi, p2; \ + \ + movzx %bl, %esi; \ + movzx %bh, %edi; \ + shr $16, %ebx; \ + movzx tab_f(%rsi), %esi; \ + movzx tab_f(%rdi), %edi; \ + xor %esi, p2; \ + rol $8, %edi; \ + xor %edi, p1; \ + movzx %bl, %esi; \ + movzx %bh, %edi; \ + movzx tab_f(%rsi), %esi; \ + movzx tab_f(%rdi), %edi; \ + rol $16, %esi; \ + rol $24, %edi; \ + xor %esi, p4; \ + xor %edi, p3; \ + \ + movzx %cl, %esi; \ + movzx %ch, %edi; \ + movzx tab_f(%rsi), %esi; \ + movzx tab_f(%rdi), %edi; \ + shr $16, %ecx; \ + xor %esi, p3; \ + rol $8, %edi; \ + xor %edi, p2; \ + movzx %cl, %esi; \ + movzx %ch, %edi; \ + movzx tab_f(%rsi), %esi; \ + movzx tab_f(%rdi), %edi; \ + rol $16, %esi; \ + rol $24, %edi; \ + xor %esi, p1; \ + xor %edi, p4; \ + \ + movzx %dl, %esi; \ + movzx %dh, %edi; \ + movzx tab_f(%rsi), %esi; \ + movzx tab_f(%rdi), %edi; \ + shr $16, %edx; \ + xor %esi, p4; \ + rol $8, %edi; \ + xor %edi, p3; \ + movzx %dl, %esi; \ + movzx %dh, %edi; \ + movzx tab_f(%rsi), %esi; \ + movzx tab_f(%rdi), %edi; \ + rol $16, %esi; \ + rol $24, %edi; \ + xor %esi, p2; \ + xor %edi, p1 + +#endif /* LAST_ROUND_TABLES */ + +#define ii_rnd(p1, p2, p3, p4, round) /* normal inverse round */ \ + mov ik_ref(round,0), p1; \ + mov ik_ref(round,1), p2; \ + mov ik_ref(round,2), p3; \ + mov ik_ref(round,3), p4; \ + \ + movzx %al, %esi; \ + movzx %ah, %edi; \ + shr $16, %eax; \ + xor tab_0(%rsi), p1; \ + xor tab_1(%rdi), p2; \ + movzx %al, %esi; \ + movzx %ah, %edi; \ + xor tab_2(%rsi), p3; \ + xor tab_3(%rdi), p4; \ + \ + movzx %bl, %esi; \ + movzx %bh, %edi; \ + shr $16, %ebx; \ + xor tab_0(%rsi), p2; \ + xor tab_1(%rdi), p3; \ + movzx %bl, %esi; \ + movzx %bh, %edi; \ + xor tab_2(%rsi), p4; \ + xor tab_3(%rdi), p1; \ + \ + movzx %cl, %esi; \ + movzx %ch, %edi; \ + shr $16, %ecx; \ + xor tab_0(%rsi), p3; \ + xor tab_1(%rdi), p4; \ + movzx %cl, %esi; \ + movzx %ch, %edi; \ + xor tab_2(%rsi), p1; \ + xor tab_3(%rdi), p2; \ + \ + movzx %dl, %esi; \ + movzx %dh, %edi; \ + shr $16, %edx; \ + xor tab_0(%rsi), p4; \ + xor tab_1(%rdi), p1; \ + movzx %dl, %esi; \ + movzx %dh, %edi; \ + xor tab_2(%rsi), p2; \ + xor tab_3(%rdi), p3; \ + \ + mov p1, %eax; \ + mov p2, %ebx; \ + mov p3, %ecx; \ + mov p4, %edx + +#ifdef LAST_ROUND_TABLES + +#define il_rnd(p1, p2, p3, p4, round) /* last inverse round */ \ + add $2048, tptr; \ + mov ik_ref(round,0), p1; \ + mov ik_ref(round,1), p2; \ + mov ik_ref(round,2), p3; \ + mov ik_ref(round,3), p4; \ + \ + movzx %al, %esi; \ + movzx %ah, %edi; \ + shr $16, %eax; \ + xor tab_0(%rsi), p1; \ + xor tab_1(%rdi), p2; \ + movzx %al, %esi; \ + movzx %ah, %edi; \ + xor tab_2(%rsi), p3; \ + xor tab_3(%rdi), p4; \ + \ + movzx %bl, %esi; \ + movzx %bh, %edi; \ + shr $16, %ebx; \ + xor tab_0(%rsi), p2; \ + xor tab_1(%rdi), p3; \ + movzx %bl, %esi; \ + movzx %bh, %edi; \ + xor tab_2(%rsi), p4; \ + xor tab_3(%rdi), p1; \ + \ + movzx %cl, %esi; \ + movzx %ch, %edi; \ + shr $16, %ecx; \ + xor tab_0(%rsi), p3; \ + xor tab_1(%rdi), p4; \ + movzx %cl, %esi; \ + movzx %ch, %edi; \ + xor tab_2(%rsi), p1; \ + xor tab_3(%rdi), p2; \ + \ + movzx %dl, %esi; \ + movzx %dh, %edi; \ + shr $16, %edx; \ + xor tab_0(%rsi), p4; \ + xor tab_1(%rdi), p1; \ + movzx %dl, %esi; \ + movzx %dh, %edi; \ + xor tab_2(%rsi), p2; \ + xor tab_3(%rdi), p3 + +#else + +#define il_rnd(p1, p2, p3, p4, round) /* last inverse round */ \ + mov ik_ref(round,0), p1; \ + mov ik_ref(round,1), p2; \ + mov ik_ref(round,2), p3; \ + mov ik_ref(round,3), p4; \ + \ + movzx %al, %esi; \ + movzx %ah, %edi; \ + movzx tab_i(%rsi), %esi; \ + movzx tab_i(%rdi), %edi; \ + shr $16, %eax; \ + xor %esi, p1; \ + rol $8, %edi; \ + xor %edi, p2; \ + movzx %al, %esi; \ + movzx %ah, %edi; \ + movzx tab_i(%rsi), %esi; \ + movzx tab_i(%rdi), %edi; \ + rol $16, %esi; \ + rol $24, %edi; \ + xor %esi, p3; \ + xor %edi, p4; \ + \ + movzx %bl, %esi; \ + movzx %bh, %edi; \ + movzx tab_i(%rsi), %esi; \ + movzx tab_i(%rdi), %edi; \ + shr $16, %ebx; \ + xor %esi, p2; \ + rol $8, %edi; \ + xor %edi, p3; \ + movzx %bl, %esi; \ + movzx %bh, %edi; \ + movzx tab_i(%rsi), %esi; \ + movzx tab_i(%rdi), %edi; \ + rol $16, %esi; \ + rol $24, %edi; \ + xor %esi, p4; \ + xor %edi, p1; \ + \ + movzx %cl, %esi; \ + movzx %ch, %edi; \ + movzx tab_i(%rsi), %esi; \ + movzx tab_i(%rdi), %edi; \ + shr $16, %ecx; \ + xor %esi, p3; \ + rol $8, %edi; \ + xor %edi, p4; \ + movzx %cl, %esi; \ + movzx %ch, %edi; \ + movzx tab_i(%rsi), %esi; \ + movzx tab_i(%rdi), %edi; \ + rol $16, %esi; \ + rol $24, %edi; \ + xor %esi, p1; \ + xor %edi, p2; \ + \ + movzx %dl, %esi; \ + movzx %dh, %edi; \ + movzx tab_i(%rsi), %esi; \ + movzx tab_i(%rdi), %edi; \ + shr $16, %edx; \ + xor %esi, p4; \ + rol $8, %edi; \ + xor %edi, p1; \ + movzx %dl, %esi; \ + movzx %dh, %edi; \ + movzx tab_i(%rsi), %esi; \ + movzx tab_i(%rdi), %edi; \ + rol $16, %esi; \ + rol $24, %edi; \ + xor %esi, p2; \ + xor %edi, p3 + +#endif /* LAST_ROUND_TABLES */ + +/* + * OpenSolaris OS: + * void aes_encrypt_amd64(const aes_ks_t *ks, int Nr, + * const uint32_t pt[4], uint32_t ct[4])/ + * + * Original interface: + * int aes_encrypt(const unsigned char *in, + * unsigned char *out, const aes_encrypt_ctx cx[1])/ + */ + .align 6, 0x90 +enc_tab: + enc_vals(u8) +#ifdef LAST_ROUND_TABLES + // Last Round Tables: + enc_vals(w8) +#endif + + + ENTRY_NP(aes_encrypt_amd64) +#ifdef GLADMAN_INTERFACE + // Original interface + sub $[4*8], %rsp // gnu/linux/opensolaris binary interface + mov %rsi, (%rsp) // output pointer (P2) + mov %rdx, %r8 // context (P3) + + mov %rbx, 1*8(%rsp) // P1: input pointer in rdi + mov %rbp, 2*8(%rsp) // P2: output pointer in (rsp) + mov %r12, 3*8(%rsp) // P3: context in r8 + movzx 4*KS_LENGTH(kptr), %esi // Get byte key length * 16 + +#else + // OpenSolaris OS interface + sub $(4*8), %rsp // Make room on stack to save registers + mov %rcx, (%rsp) // Save output pointer (P4) on stack + mov %rdi, %r8 // context (P1) + mov %rdx, %rdi // P3: save input pointer + shl $4, %esi // P2: esi byte key length * 16 + + mov %rbx, 1*8(%rsp) // Save registers + mov %rbp, 2*8(%rsp) + mov %r12, 3*8(%rsp) + // P1: context in r8 + // P2: byte key length * 16 in esi + // P3: input pointer in rdi + // P4: output pointer in (rsp) +#endif /* GLADMAN_INTERFACE */ + + lea enc_tab(%rip), tptr + sub $fofs, kptr + + // Load input block into registers + mov (%rdi), %eax + mov 1*4(%rdi), %ebx + mov 2*4(%rdi), %ecx + mov 3*4(%rdi), %edx + + xor fofs(kptr), %eax + xor fofs+4(kptr), %ebx + xor fofs+8(kptr), %ecx + xor fofs+12(kptr), %edx + + lea (kptr,%rsi), kptr + // Jump based on byte key length * 16: + cmp $(10*16), %esi + je 3f + cmp $(12*16), %esi + je 2f + cmp $(14*16), %esi + je 1f + mov $-1, %rax // error + jmp 4f + + // Perform normal forward rounds +1: ff_rnd(%r9d, %r10d, %r11d, %r12d, 13) + ff_rnd(%r9d, %r10d, %r11d, %r12d, 12) +2: ff_rnd(%r9d, %r10d, %r11d, %r12d, 11) + ff_rnd(%r9d, %r10d, %r11d, %r12d, 10) +3: ff_rnd(%r9d, %r10d, %r11d, %r12d, 9) + ff_rnd(%r9d, %r10d, %r11d, %r12d, 8) + ff_rnd(%r9d, %r10d, %r11d, %r12d, 7) + ff_rnd(%r9d, %r10d, %r11d, %r12d, 6) + ff_rnd(%r9d, %r10d, %r11d, %r12d, 5) + ff_rnd(%r9d, %r10d, %r11d, %r12d, 4) + ff_rnd(%r9d, %r10d, %r11d, %r12d, 3) + ff_rnd(%r9d, %r10d, %r11d, %r12d, 2) + ff_rnd(%r9d, %r10d, %r11d, %r12d, 1) + fl_rnd(%r9d, %r10d, %r11d, %r12d, 0) + + // Copy results + mov (%rsp), %rbx + mov %r9d, (%rbx) + mov %r10d, 4(%rbx) + mov %r11d, 8(%rbx) + mov %r12d, 12(%rbx) + xor %rax, %rax +4: // Restore registers + mov 1*8(%rsp), %rbx + mov 2*8(%rsp), %rbp + mov 3*8(%rsp), %r12 + add $(4*8), %rsp + ret + + SET_SIZE(aes_encrypt_amd64) + +/* + * OpenSolaris OS: + * void aes_decrypt_amd64(const aes_ks_t *ks, int Nr, + * const uint32_t pt[4], uint32_t ct[4])/ + * + * Original interface: + * int aes_decrypt(const unsigned char *in, + * unsigned char *out, const aes_encrypt_ctx cx[1])/ + */ + .align 6, 0x90 +dec_tab: + dec_vals(v8) +#ifdef LAST_ROUND_TABLES + // Last Round Tables: + dec_vals(w8) +#endif + + + ENTRY_NP(aes_decrypt_amd64) +#ifdef GLADMAN_INTERFACE + // Original interface + sub $[4*8], %rsp // gnu/linux/opensolaris binary interface + mov %rsi, (%rsp) // output pointer (P2) + mov %rdx, %r8 // context (P3) + + mov %rbx, 1*8(%rsp) // P1: input pointer in rdi + mov %rbp, 2*8(%rsp) // P2: output pointer in (rsp) + mov %r12, 3*8(%rsp) // P3: context in r8 + movzx 4*KS_LENGTH(kptr), %esi // Get byte key length * 16 + +#else + // OpenSolaris OS interface + sub $(4*8), %rsp // Make room on stack to save registers + mov %rcx, (%rsp) // Save output pointer (P4) on stack + mov %rdi, %r8 // context (P1) + mov %rdx, %rdi // P3: save input pointer + shl $4, %esi // P2: esi byte key length * 16 + + mov %rbx, 1*8(%rsp) // Save registers + mov %rbp, 2*8(%rsp) + mov %r12, 3*8(%rsp) + // P1: context in r8 + // P2: byte key length * 16 in esi + // P3: input pointer in rdi + // P4: output pointer in (rsp) +#endif /* GLADMAN_INTERFACE */ + + lea dec_tab(%rip), tptr + sub $rofs, kptr + + // Load input block into registers + mov (%rdi), %eax + mov 1*4(%rdi), %ebx + mov 2*4(%rdi), %ecx + mov 3*4(%rdi), %edx + +#ifdef AES_REV_DKS + mov kptr, %rdi + lea (kptr,%rsi), kptr +#else + lea (kptr,%rsi), %rdi +#endif + + xor rofs(%rdi), %eax + xor rofs+4(%rdi), %ebx + xor rofs+8(%rdi), %ecx + xor rofs+12(%rdi), %edx + + // Jump based on byte key length * 16: + cmp $(10*16), %esi + je 3f + cmp $(12*16), %esi + je 2f + cmp $(14*16), %esi + je 1f + mov $-1, %rax // error + jmp 4f + + // Perform normal inverse rounds +1: ii_rnd(%r9d, %r10d, %r11d, %r12d, 13) + ii_rnd(%r9d, %r10d, %r11d, %r12d, 12) +2: ii_rnd(%r9d, %r10d, %r11d, %r12d, 11) + ii_rnd(%r9d, %r10d, %r11d, %r12d, 10) +3: ii_rnd(%r9d, %r10d, %r11d, %r12d, 9) + ii_rnd(%r9d, %r10d, %r11d, %r12d, 8) + ii_rnd(%r9d, %r10d, %r11d, %r12d, 7) + ii_rnd(%r9d, %r10d, %r11d, %r12d, 6) + ii_rnd(%r9d, %r10d, %r11d, %r12d, 5) + ii_rnd(%r9d, %r10d, %r11d, %r12d, 4) + ii_rnd(%r9d, %r10d, %r11d, %r12d, 3) + ii_rnd(%r9d, %r10d, %r11d, %r12d, 2) + ii_rnd(%r9d, %r10d, %r11d, %r12d, 1) + il_rnd(%r9d, %r10d, %r11d, %r12d, 0) + + // Copy results + mov (%rsp), %rbx + mov %r9d, (%rbx) + mov %r10d, 4(%rbx) + mov %r11d, 8(%rbx) + mov %r12d, 12(%rbx) + xor %rax, %rax +4: // Restore registers + mov 1*8(%rsp), %rbx + mov 2*8(%rsp), %rbp + mov 3*8(%rsp), %r12 + add $(4*8), %rsp + ret + + SET_SIZE(aes_decrypt_amd64) +#endif /* lint || __lint */ diff --git a/module/icp/asm-x86_64/os/macos/modes/aesni-gcm-x86_64.S b/module/icp/asm-x86_64/os/macos/modes/aesni-gcm-x86_64.S new file mode 100644 index 000000000000..998d91e0fa35 --- /dev/null +++ b/module/icp/asm-x86_64/os/macos/modes/aesni-gcm-x86_64.S @@ -0,0 +1,1274 @@ +# Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +# +# ==================================================================== +# Written by Andy Polyakov for the OpenSSL +# project. The module is, however, dual licensed under OpenSSL and +# CRYPTOGAMS licenses depending on where you obtain it. For further +# details see http://www.openssl.org/~appro/cryptogams/. +# ==================================================================== +# +# +# AES-NI-CTR+GHASH stitch. +# +# February 2013 +# +# OpenSSL GCM implementation is organized in such way that its +# performance is rather close to the sum of its streamed components, +# in the context parallelized AES-NI CTR and modulo-scheduled +# PCLMULQDQ-enabled GHASH. Unfortunately, as no stitch implementation +# was observed to perform significantly better than the sum of the +# components on contemporary CPUs, the effort was deemed impossible to +# justify. This module is based on combination of Intel submissions, +# [1] and [2], with MOVBE twist suggested by Ilya Albrekht and Max +# Locktyukhin of Intel Corp. who verified that it reduces shuffles +# pressure with notable relative improvement, achieving 1.0 cycle per +# byte processed with 128-bit key on Haswell processor, 0.74 - on +# Broadwell, 0.63 - on Skylake... [Mentioned results are raw profiled +# measurements for favourable packet size, one divisible by 96. +# Applications using the EVP interface will observe a few percent +# worse performance.] +# +# Knights Landing processes 1 byte in 1.25 cycles (measured with EVP). +# +# [1] http://rt.openssl.org/Ticket/Display.html?id=2900&user=guest&pass=guest +# [2] http://www.intel.com/content/dam/www/public/us/en/documents/software-support/enabling-high-performance-gcm.pdf + +# Generated once from +# https://github.com/openssl/openssl/blob/5ffc3324/crypto/modes/asm/aesni-gcm-x86_64.pl +# and modified for ICP. Modification are kept at a bare minimum to ease later +# upstream merges. + +#if defined(__x86_64__) && defined(HAVE_AVX) && \ + defined(HAVE_AES) && defined(HAVE_PCLMULQDQ) + +#define _ASM +#include + +.extern _gcm_avx_can_use_movbe + +.text + +#ifdef HAVE_MOVBE +ENTRY_NP(aesni_ctr32_ghash_6x) +#.type _aesni_ctr32_ghash_6x,@function +# .align 32 +.align 5, 0x90 +#_aesni_ctr32_ghash_6x: +.cfi_startproc + vmovdqu 32(%r11),%xmm2 + subq $6,%rdx + vpxor %xmm4,%xmm4,%xmm4 + vmovdqu 0-128(%rcx),%xmm15 + vpaddb %xmm2,%xmm1,%xmm10 + vpaddb %xmm2,%xmm10,%xmm11 + vpaddb %xmm2,%xmm11,%xmm12 + vpaddb %xmm2,%xmm12,%xmm13 + vpaddb %xmm2,%xmm13,%xmm14 + vpxor %xmm15,%xmm1,%xmm9 + vmovdqu %xmm4,16+8(%rsp) + jmp .Loop6x + +.align 5, 0x90 +.Loop6x: + addl $100663296,%ebx + jc .Lhandle_ctr32 + vmovdqu 0-32(%r9),%xmm3 + vpaddb %xmm2,%xmm14,%xmm1 + vpxor %xmm15,%xmm10,%xmm10 + vpxor %xmm15,%xmm11,%xmm11 + +.Lresume_ctr32: + vmovdqu %xmm1,(%r8) + vpclmulqdq $0x10,%xmm3,%xmm7,%xmm5 + vpxor %xmm15,%xmm12,%xmm12 + vmovups 16-128(%rcx),%xmm2 + vpclmulqdq $0x01,%xmm3,%xmm7,%xmm6 + xorq %r12,%r12 + cmpq %r14,%r15 + + vaesenc %xmm2,%xmm9,%xmm9 + vmovdqu 48+8(%rsp),%xmm0 + vpxor %xmm15,%xmm13,%xmm13 + vpclmulqdq $0x00,%xmm3,%xmm7,%xmm1 + vaesenc %xmm2,%xmm10,%xmm10 + vpxor %xmm15,%xmm14,%xmm14 + setnc %r12b + vpclmulqdq $0x11,%xmm3,%xmm7,%xmm7 + vaesenc %xmm2,%xmm11,%xmm11 + vmovdqu 16-32(%r9),%xmm3 + negq %r12 + vaesenc %xmm2,%xmm12,%xmm12 + vpxor %xmm5,%xmm6,%xmm6 + vpclmulqdq $0x00,%xmm3,%xmm0,%xmm5 + vpxor %xmm4,%xmm8,%xmm8 + vaesenc %xmm2,%xmm13,%xmm13 + vpxor %xmm5,%xmm1,%xmm4 + andq $0x60,%r12 + vmovups 32-128(%rcx),%xmm15 + vpclmulqdq $0x10,%xmm3,%xmm0,%xmm1 + vaesenc %xmm2,%xmm14,%xmm14 + + vpclmulqdq $0x01,%xmm3,%xmm0,%xmm2 + leaq (%r14,%r12,1),%r14 + vaesenc %xmm15,%xmm9,%xmm9 + vpxor 16+8(%rsp),%xmm8,%xmm8 + vpclmulqdq $0x11,%xmm3,%xmm0,%xmm3 + vmovdqu 64+8(%rsp),%xmm0 + vaesenc %xmm15,%xmm10,%xmm10 + movbeq 88(%r14),%r13 + vaesenc %xmm15,%xmm11,%xmm11 + movbeq 80(%r14),%r12 + vaesenc %xmm15,%xmm12,%xmm12 + movq %r13,32+8(%rsp) + vaesenc %xmm15,%xmm13,%xmm13 + movq %r12,40+8(%rsp) + vmovdqu 48-32(%r9),%xmm5 + vaesenc %xmm15,%xmm14,%xmm14 + + vmovups 48-128(%rcx),%xmm15 + vpxor %xmm1,%xmm6,%xmm6 + vpclmulqdq $0x00,%xmm5,%xmm0,%xmm1 + vaesenc %xmm15,%xmm9,%xmm9 + vpxor %xmm2,%xmm6,%xmm6 + vpclmulqdq $0x10,%xmm5,%xmm0,%xmm2 + vaesenc %xmm15,%xmm10,%xmm10 + vpxor %xmm3,%xmm7,%xmm7 + vpclmulqdq $0x01,%xmm5,%xmm0,%xmm3 + vaesenc %xmm15,%xmm11,%xmm11 + vpclmulqdq $0x11,%xmm5,%xmm0,%xmm5 + vmovdqu 80+8(%rsp),%xmm0 + vaesenc %xmm15,%xmm12,%xmm12 + vaesenc %xmm15,%xmm13,%xmm13 + vpxor %xmm1,%xmm4,%xmm4 + vmovdqu 64-32(%r9),%xmm1 + vaesenc %xmm15,%xmm14,%xmm14 + + vmovups 64-128(%rcx),%xmm15 + vpxor %xmm2,%xmm6,%xmm6 + vpclmulqdq $0x00,%xmm1,%xmm0,%xmm2 + vaesenc %xmm15,%xmm9,%xmm9 + vpxor %xmm3,%xmm6,%xmm6 + vpclmulqdq $0x10,%xmm1,%xmm0,%xmm3 + vaesenc %xmm15,%xmm10,%xmm10 + movbeq 72(%r14),%r13 + vpxor %xmm5,%xmm7,%xmm7 + vpclmulqdq $0x01,%xmm1,%xmm0,%xmm5 + vaesenc %xmm15,%xmm11,%xmm11 + movbeq 64(%r14),%r12 + vpclmulqdq $0x11,%xmm1,%xmm0,%xmm1 + vmovdqu 96+8(%rsp),%xmm0 + vaesenc %xmm15,%xmm12,%xmm12 + movq %r13,48+8(%rsp) + vaesenc %xmm15,%xmm13,%xmm13 + movq %r12,56+8(%rsp) + vpxor %xmm2,%xmm4,%xmm4 + vmovdqu 96-32(%r9),%xmm2 + vaesenc %xmm15,%xmm14,%xmm14 + + vmovups 80-128(%rcx),%xmm15 + vpxor %xmm3,%xmm6,%xmm6 + vpclmulqdq $0x00,%xmm2,%xmm0,%xmm3 + vaesenc %xmm15,%xmm9,%xmm9 + vpxor %xmm5,%xmm6,%xmm6 + vpclmulqdq $0x10,%xmm2,%xmm0,%xmm5 + vaesenc %xmm15,%xmm10,%xmm10 + movbeq 56(%r14),%r13 + vpxor %xmm1,%xmm7,%xmm7 + vpclmulqdq $0x01,%xmm2,%xmm0,%xmm1 + vpxor 112+8(%rsp),%xmm8,%xmm8 + vaesenc %xmm15,%xmm11,%xmm11 + movbeq 48(%r14),%r12 + vpclmulqdq $0x11,%xmm2,%xmm0,%xmm2 + vaesenc %xmm15,%xmm12,%xmm12 + movq %r13,64+8(%rsp) + vaesenc %xmm15,%xmm13,%xmm13 + movq %r12,72+8(%rsp) + vpxor %xmm3,%xmm4,%xmm4 + vmovdqu 112-32(%r9),%xmm3 + vaesenc %xmm15,%xmm14,%xmm14 + + vmovups 96-128(%rcx),%xmm15 + vpxor %xmm5,%xmm6,%xmm6 + vpclmulqdq $0x10,%xmm3,%xmm8,%xmm5 + vaesenc %xmm15,%xmm9,%xmm9 + vpxor %xmm1,%xmm6,%xmm6 + vpclmulqdq $0x01,%xmm3,%xmm8,%xmm1 + vaesenc %xmm15,%xmm10,%xmm10 + movbeq 40(%r14),%r13 + vpxor %xmm2,%xmm7,%xmm7 + vpclmulqdq $0x00,%xmm3,%xmm8,%xmm2 + vaesenc %xmm15,%xmm11,%xmm11 + movbeq 32(%r14),%r12 + vpclmulqdq $0x11,%xmm3,%xmm8,%xmm8 + vaesenc %xmm15,%xmm12,%xmm12 + movq %r13,80+8(%rsp) + vaesenc %xmm15,%xmm13,%xmm13 + movq %r12,88+8(%rsp) + vpxor %xmm5,%xmm6,%xmm6 + vaesenc %xmm15,%xmm14,%xmm14 + vpxor %xmm1,%xmm6,%xmm6 + + vmovups 112-128(%rcx),%xmm15 + vpslldq $8,%xmm6,%xmm5 + vpxor %xmm2,%xmm4,%xmm4 + vmovdqu 16(%r11),%xmm3 + + vaesenc %xmm15,%xmm9,%xmm9 + vpxor %xmm8,%xmm7,%xmm7 + vaesenc %xmm15,%xmm10,%xmm10 + vpxor %xmm5,%xmm4,%xmm4 + movbeq 24(%r14),%r13 + vaesenc %xmm15,%xmm11,%xmm11 + movbeq 16(%r14),%r12 + vpalignr $8,%xmm4,%xmm4,%xmm0 + vpclmulqdq $0x10,%xmm3,%xmm4,%xmm4 + movq %r13,96+8(%rsp) + vaesenc %xmm15,%xmm12,%xmm12 + movq %r12,104+8(%rsp) + vaesenc %xmm15,%xmm13,%xmm13 + vmovups 128-128(%rcx),%xmm1 + vaesenc %xmm15,%xmm14,%xmm14 + + vaesenc %xmm1,%xmm9,%xmm9 + vmovups 144-128(%rcx),%xmm15 + vaesenc %xmm1,%xmm10,%xmm10 + vpsrldq $8,%xmm6,%xmm6 + vaesenc %xmm1,%xmm11,%xmm11 + vpxor %xmm6,%xmm7,%xmm7 + vaesenc %xmm1,%xmm12,%xmm12 + vpxor %xmm0,%xmm4,%xmm4 + movbeq 8(%r14),%r13 + vaesenc %xmm1,%xmm13,%xmm13 + movbeq 0(%r14),%r12 + vaesenc %xmm1,%xmm14,%xmm14 + vmovups 160-128(%rcx),%xmm1 + cmpl $12,%ebp // ICP uses 10,12,14 not 9,11,13 for rounds. + jb .Lenc_tail + + vaesenc %xmm15,%xmm9,%xmm9 + vaesenc %xmm15,%xmm10,%xmm10 + vaesenc %xmm15,%xmm11,%xmm11 + vaesenc %xmm15,%xmm12,%xmm12 + vaesenc %xmm15,%xmm13,%xmm13 + vaesenc %xmm15,%xmm14,%xmm14 + + vaesenc %xmm1,%xmm9,%xmm9 + vaesenc %xmm1,%xmm10,%xmm10 + vaesenc %xmm1,%xmm11,%xmm11 + vaesenc %xmm1,%xmm12,%xmm12 + vaesenc %xmm1,%xmm13,%xmm13 + vmovups 176-128(%rcx),%xmm15 + vaesenc %xmm1,%xmm14,%xmm14 + vmovups 192-128(%rcx),%xmm1 + cmpl $14,%ebp // ICP does not zero key schedule. + jb .Lenc_tail + + vaesenc %xmm15,%xmm9,%xmm9 + vaesenc %xmm15,%xmm10,%xmm10 + vaesenc %xmm15,%xmm11,%xmm11 + vaesenc %xmm15,%xmm12,%xmm12 + vaesenc %xmm15,%xmm13,%xmm13 + vaesenc %xmm15,%xmm14,%xmm14 + + vaesenc %xmm1,%xmm9,%xmm9 + vaesenc %xmm1,%xmm10,%xmm10 + vaesenc %xmm1,%xmm11,%xmm11 + vaesenc %xmm1,%xmm12,%xmm12 + vaesenc %xmm1,%xmm13,%xmm13 + vmovups 208-128(%rcx),%xmm15 + vaesenc %xmm1,%xmm14,%xmm14 + vmovups 224-128(%rcx),%xmm1 + jmp .Lenc_tail + +.align 5, 0x90 +.Lhandle_ctr32: + vmovdqu (%r11),%xmm0 + vpshufb %xmm0,%xmm1,%xmm6 + vmovdqu 48(%r11),%xmm5 + vpaddd 64(%r11),%xmm6,%xmm10 + vpaddd %xmm5,%xmm6,%xmm11 + vmovdqu 0-32(%r9),%xmm3 + vpaddd %xmm5,%xmm10,%xmm12 + vpshufb %xmm0,%xmm10,%xmm10 + vpaddd %xmm5,%xmm11,%xmm13 + vpshufb %xmm0,%xmm11,%xmm11 + vpxor %xmm15,%xmm10,%xmm10 + vpaddd %xmm5,%xmm12,%xmm14 + vpshufb %xmm0,%xmm12,%xmm12 + vpxor %xmm15,%xmm11,%xmm11 + vpaddd %xmm5,%xmm13,%xmm1 + vpshufb %xmm0,%xmm13,%xmm13 + vpshufb %xmm0,%xmm14,%xmm14 + vpshufb %xmm0,%xmm1,%xmm1 + jmp .Lresume_ctr32 + +.align 5, 0x90 +.Lenc_tail: + vaesenc %xmm15,%xmm9,%xmm9 + vmovdqu %xmm7,16+8(%rsp) + vpalignr $8,%xmm4,%xmm4,%xmm8 + vaesenc %xmm15,%xmm10,%xmm10 + vpclmulqdq $0x10,%xmm3,%xmm4,%xmm4 + vpxor 0(%rdi),%xmm1,%xmm2 + vaesenc %xmm15,%xmm11,%xmm11 + vpxor 16(%rdi),%xmm1,%xmm0 + vaesenc %xmm15,%xmm12,%xmm12 + vpxor 32(%rdi),%xmm1,%xmm5 + vaesenc %xmm15,%xmm13,%xmm13 + vpxor 48(%rdi),%xmm1,%xmm6 + vaesenc %xmm15,%xmm14,%xmm14 + vpxor 64(%rdi),%xmm1,%xmm7 + vpxor 80(%rdi),%xmm1,%xmm3 + vmovdqu (%r8),%xmm1 + + vaesenclast %xmm2,%xmm9,%xmm9 + vmovdqu 32(%r11),%xmm2 + vaesenclast %xmm0,%xmm10,%xmm10 + vpaddb %xmm2,%xmm1,%xmm0 + movq %r13,112+8(%rsp) + leaq 96(%rdi),%rdi + vaesenclast %xmm5,%xmm11,%xmm11 + vpaddb %xmm2,%xmm0,%xmm5 + movq %r12,120+8(%rsp) + leaq 96(%rsi),%rsi + vmovdqu 0-128(%rcx),%xmm15 + vaesenclast %xmm6,%xmm12,%xmm12 + vpaddb %xmm2,%xmm5,%xmm6 + vaesenclast %xmm7,%xmm13,%xmm13 + vpaddb %xmm2,%xmm6,%xmm7 + vaesenclast %xmm3,%xmm14,%xmm14 + vpaddb %xmm2,%xmm7,%xmm3 + + addq $0x60,%r10 + subq $0x6,%rdx + jc .L6x_done + + vmovups %xmm9,-96(%rsi) + vpxor %xmm15,%xmm1,%xmm9 + vmovups %xmm10,-80(%rsi) + vmovdqa %xmm0,%xmm10 + vmovups %xmm11,-64(%rsi) + vmovdqa %xmm5,%xmm11 + vmovups %xmm12,-48(%rsi) + vmovdqa %xmm6,%xmm12 + vmovups %xmm13,-32(%rsi) + vmovdqa %xmm7,%xmm13 + vmovups %xmm14,-16(%rsi) + vmovdqa %xmm3,%xmm14 + vmovdqu 32+8(%rsp),%xmm7 + jmp .Loop6x + +.L6x_done: + vpxor 16+8(%rsp),%xmm8,%xmm8 + vpxor %xmm4,%xmm8,%xmm8 + + .byte 0xf3,0xc3 +.cfi_endproc +#.size _aesni_ctr32_ghash_6x,.-_aesni_ctr32_ghash_6x +#endif /* ifdef HAVE_MOVBE */ + +#.type _aesni_ctr32_ghash_no_movbe_6x,@function +ENTRY_NP(aesni_ctr32_ghash_no_movbe_6x) +.align 5, 0x90 +#_aesni_ctr32_ghash_no_movbe_6x: +.cfi_startproc + vmovdqu 32(%r11),%xmm2 + subq $6,%rdx + vpxor %xmm4,%xmm4,%xmm4 + vmovdqu 0-128(%rcx),%xmm15 + vpaddb %xmm2,%xmm1,%xmm10 + vpaddb %xmm2,%xmm10,%xmm11 + vpaddb %xmm2,%xmm11,%xmm12 + vpaddb %xmm2,%xmm12,%xmm13 + vpaddb %xmm2,%xmm13,%xmm14 + vpxor %xmm15,%xmm1,%xmm9 + vmovdqu %xmm4,16+8(%rsp) + jmp .Loop6x_nmb + +.align 5, 0x90 +.Loop6x_nmb: + addl $100663296,%ebx + jc .Lhandle_ctr32_nmb + vmovdqu 0-32(%r9),%xmm3 + vpaddb %xmm2,%xmm14,%xmm1 + vpxor %xmm15,%xmm10,%xmm10 + vpxor %xmm15,%xmm11,%xmm11 + +.Lresume_ctr32_nmb: + vmovdqu %xmm1,(%r8) + vpclmulqdq $0x10,%xmm3,%xmm7,%xmm5 + vpxor %xmm15,%xmm12,%xmm12 + vmovups 16-128(%rcx),%xmm2 + vpclmulqdq $0x01,%xmm3,%xmm7,%xmm6 + xorq %r12,%r12 + cmpq %r14,%r15 + + vaesenc %xmm2,%xmm9,%xmm9 + vmovdqu 48+8(%rsp),%xmm0 + vpxor %xmm15,%xmm13,%xmm13 + vpclmulqdq $0x00,%xmm3,%xmm7,%xmm1 + vaesenc %xmm2,%xmm10,%xmm10 + vpxor %xmm15,%xmm14,%xmm14 + setnc %r12b + vpclmulqdq $0x11,%xmm3,%xmm7,%xmm7 + vaesenc %xmm2,%xmm11,%xmm11 + vmovdqu 16-32(%r9),%xmm3 + negq %r12 + vaesenc %xmm2,%xmm12,%xmm12 + vpxor %xmm5,%xmm6,%xmm6 + vpclmulqdq $0x00,%xmm3,%xmm0,%xmm5 + vpxor %xmm4,%xmm8,%xmm8 + vaesenc %xmm2,%xmm13,%xmm13 + vpxor %xmm5,%xmm1,%xmm4 + andq $0x60,%r12 + vmovups 32-128(%rcx),%xmm15 + vpclmulqdq $0x10,%xmm3,%xmm0,%xmm1 + vaesenc %xmm2,%xmm14,%xmm14 + + vpclmulqdq $0x01,%xmm3,%xmm0,%xmm2 + leaq (%r14,%r12,1),%r14 + vaesenc %xmm15,%xmm9,%xmm9 + vpxor 16+8(%rsp),%xmm8,%xmm8 + vpclmulqdq $0x11,%xmm3,%xmm0,%xmm3 + vmovdqu 64+8(%rsp),%xmm0 + vaesenc %xmm15,%xmm10,%xmm10 + movq 88(%r14),%r13 + bswapq %r13 + vaesenc %xmm15,%xmm11,%xmm11 + movq 80(%r14),%r12 + bswapq %r12 + vaesenc %xmm15,%xmm12,%xmm12 + movq %r13,32+8(%rsp) + vaesenc %xmm15,%xmm13,%xmm13 + movq %r12,40+8(%rsp) + vmovdqu 48-32(%r9),%xmm5 + vaesenc %xmm15,%xmm14,%xmm14 + + vmovups 48-128(%rcx),%xmm15 + vpxor %xmm1,%xmm6,%xmm6 + vpclmulqdq $0x00,%xmm5,%xmm0,%xmm1 + vaesenc %xmm15,%xmm9,%xmm9 + vpxor %xmm2,%xmm6,%xmm6 + vpclmulqdq $0x10,%xmm5,%xmm0,%xmm2 + vaesenc %xmm15,%xmm10,%xmm10 + vpxor %xmm3,%xmm7,%xmm7 + vpclmulqdq $0x01,%xmm5,%xmm0,%xmm3 + vaesenc %xmm15,%xmm11,%xmm11 + vpclmulqdq $0x11,%xmm5,%xmm0,%xmm5 + vmovdqu 80+8(%rsp),%xmm0 + vaesenc %xmm15,%xmm12,%xmm12 + vaesenc %xmm15,%xmm13,%xmm13 + vpxor %xmm1,%xmm4,%xmm4 + vmovdqu 64-32(%r9),%xmm1 + vaesenc %xmm15,%xmm14,%xmm14 + + vmovups 64-128(%rcx),%xmm15 + vpxor %xmm2,%xmm6,%xmm6 + vpclmulqdq $0x00,%xmm1,%xmm0,%xmm2 + vaesenc %xmm15,%xmm9,%xmm9 + vpxor %xmm3,%xmm6,%xmm6 + vpclmulqdq $0x10,%xmm1,%xmm0,%xmm3 + vaesenc %xmm15,%xmm10,%xmm10 + movq 72(%r14),%r13 + bswapq %r13 + vpxor %xmm5,%xmm7,%xmm7 + vpclmulqdq $0x01,%xmm1,%xmm0,%xmm5 + vaesenc %xmm15,%xmm11,%xmm11 + movq 64(%r14),%r12 + bswapq %r12 + vpclmulqdq $0x11,%xmm1,%xmm0,%xmm1 + vmovdqu 96+8(%rsp),%xmm0 + vaesenc %xmm15,%xmm12,%xmm12 + movq %r13,48+8(%rsp) + vaesenc %xmm15,%xmm13,%xmm13 + movq %r12,56+8(%rsp) + vpxor %xmm2,%xmm4,%xmm4 + vmovdqu 96-32(%r9),%xmm2 + vaesenc %xmm15,%xmm14,%xmm14 + + vmovups 80-128(%rcx),%xmm15 + vpxor %xmm3,%xmm6,%xmm6 + vpclmulqdq $0x00,%xmm2,%xmm0,%xmm3 + vaesenc %xmm15,%xmm9,%xmm9 + vpxor %xmm5,%xmm6,%xmm6 + vpclmulqdq $0x10,%xmm2,%xmm0,%xmm5 + vaesenc %xmm15,%xmm10,%xmm10 + movq 56(%r14),%r13 + bswapq %r13 + vpxor %xmm1,%xmm7,%xmm7 + vpclmulqdq $0x01,%xmm2,%xmm0,%xmm1 + vpxor 112+8(%rsp),%xmm8,%xmm8 + vaesenc %xmm15,%xmm11,%xmm11 + movq 48(%r14),%r12 + bswapq %r12 + vpclmulqdq $0x11,%xmm2,%xmm0,%xmm2 + vaesenc %xmm15,%xmm12,%xmm12 + movq %r13,64+8(%rsp) + vaesenc %xmm15,%xmm13,%xmm13 + movq %r12,72+8(%rsp) + vpxor %xmm3,%xmm4,%xmm4 + vmovdqu 112-32(%r9),%xmm3 + vaesenc %xmm15,%xmm14,%xmm14 + + vmovups 96-128(%rcx),%xmm15 + vpxor %xmm5,%xmm6,%xmm6 + vpclmulqdq $0x10,%xmm3,%xmm8,%xmm5 + vaesenc %xmm15,%xmm9,%xmm9 + vpxor %xmm1,%xmm6,%xmm6 + vpclmulqdq $0x01,%xmm3,%xmm8,%xmm1 + vaesenc %xmm15,%xmm10,%xmm10 + movq 40(%r14),%r13 + bswapq %r13 + vpxor %xmm2,%xmm7,%xmm7 + vpclmulqdq $0x00,%xmm3,%xmm8,%xmm2 + vaesenc %xmm15,%xmm11,%xmm11 + movq 32(%r14),%r12 + bswapq %r12 + vpclmulqdq $0x11,%xmm3,%xmm8,%xmm8 + vaesenc %xmm15,%xmm12,%xmm12 + movq %r13,80+8(%rsp) + vaesenc %xmm15,%xmm13,%xmm13 + movq %r12,88+8(%rsp) + vpxor %xmm5,%xmm6,%xmm6 + vaesenc %xmm15,%xmm14,%xmm14 + vpxor %xmm1,%xmm6,%xmm6 + + vmovups 112-128(%rcx),%xmm15 + vpslldq $8,%xmm6,%xmm5 + vpxor %xmm2,%xmm4,%xmm4 + vmovdqu 16(%r11),%xmm3 + + vaesenc %xmm15,%xmm9,%xmm9 + vpxor %xmm8,%xmm7,%xmm7 + vaesenc %xmm15,%xmm10,%xmm10 + vpxor %xmm5,%xmm4,%xmm4 + movq 24(%r14),%r13 + bswapq %r13 + vaesenc %xmm15,%xmm11,%xmm11 + movq 16(%r14),%r12 + bswapq %r12 + vpalignr $8,%xmm4,%xmm4,%xmm0 + vpclmulqdq $0x10,%xmm3,%xmm4,%xmm4 + movq %r13,96+8(%rsp) + vaesenc %xmm15,%xmm12,%xmm12 + movq %r12,104+8(%rsp) + vaesenc %xmm15,%xmm13,%xmm13 + vmovups 128-128(%rcx),%xmm1 + vaesenc %xmm15,%xmm14,%xmm14 + + vaesenc %xmm1,%xmm9,%xmm9 + vmovups 144-128(%rcx),%xmm15 + vaesenc %xmm1,%xmm10,%xmm10 + vpsrldq $8,%xmm6,%xmm6 + vaesenc %xmm1,%xmm11,%xmm11 + vpxor %xmm6,%xmm7,%xmm7 + vaesenc %xmm1,%xmm12,%xmm12 + vpxor %xmm0,%xmm4,%xmm4 + movq 8(%r14),%r13 + bswapq %r13 + vaesenc %xmm1,%xmm13,%xmm13 + movq 0(%r14),%r12 + bswapq %r12 + vaesenc %xmm1,%xmm14,%xmm14 + vmovups 160-128(%rcx),%xmm1 + cmpl $12,%ebp // ICP uses 10,12,14 not 9,11,13 for rounds. + jb .Lenc_tail_nmb + + vaesenc %xmm15,%xmm9,%xmm9 + vaesenc %xmm15,%xmm10,%xmm10 + vaesenc %xmm15,%xmm11,%xmm11 + vaesenc %xmm15,%xmm12,%xmm12 + vaesenc %xmm15,%xmm13,%xmm13 + vaesenc %xmm15,%xmm14,%xmm14 + + vaesenc %xmm1,%xmm9,%xmm9 + vaesenc %xmm1,%xmm10,%xmm10 + vaesenc %xmm1,%xmm11,%xmm11 + vaesenc %xmm1,%xmm12,%xmm12 + vaesenc %xmm1,%xmm13,%xmm13 + vmovups 176-128(%rcx),%xmm15 + vaesenc %xmm1,%xmm14,%xmm14 + vmovups 192-128(%rcx),%xmm1 + cmpl $14,%ebp // ICP does not zero key schedule. + jb .Lenc_tail_nmb + + vaesenc %xmm15,%xmm9,%xmm9 + vaesenc %xmm15,%xmm10,%xmm10 + vaesenc %xmm15,%xmm11,%xmm11 + vaesenc %xmm15,%xmm12,%xmm12 + vaesenc %xmm15,%xmm13,%xmm13 + vaesenc %xmm15,%xmm14,%xmm14 + + vaesenc %xmm1,%xmm9,%xmm9 + vaesenc %xmm1,%xmm10,%xmm10 + vaesenc %xmm1,%xmm11,%xmm11 + vaesenc %xmm1,%xmm12,%xmm12 + vaesenc %xmm1,%xmm13,%xmm13 + vmovups 208-128(%rcx),%xmm15 + vaesenc %xmm1,%xmm14,%xmm14 + vmovups 224-128(%rcx),%xmm1 + jmp .Lenc_tail_nmb + +.align 5, 0x90 +.Lhandle_ctr32_nmb: + vmovdqu (%r11),%xmm0 + vpshufb %xmm0,%xmm1,%xmm6 + vmovdqu 48(%r11),%xmm5 + vpaddd 64(%r11),%xmm6,%xmm10 + vpaddd %xmm5,%xmm6,%xmm11 + vmovdqu 0-32(%r9),%xmm3 + vpaddd %xmm5,%xmm10,%xmm12 + vpshufb %xmm0,%xmm10,%xmm10 + vpaddd %xmm5,%xmm11,%xmm13 + vpshufb %xmm0,%xmm11,%xmm11 + vpxor %xmm15,%xmm10,%xmm10 + vpaddd %xmm5,%xmm12,%xmm14 + vpshufb %xmm0,%xmm12,%xmm12 + vpxor %xmm15,%xmm11,%xmm11 + vpaddd %xmm5,%xmm13,%xmm1 + vpshufb %xmm0,%xmm13,%xmm13 + vpshufb %xmm0,%xmm14,%xmm14 + vpshufb %xmm0,%xmm1,%xmm1 + jmp .Lresume_ctr32_nmb + +.align 5, 0x90 +.Lenc_tail_nmb: + vaesenc %xmm15,%xmm9,%xmm9 + vmovdqu %xmm7,16+8(%rsp) + vpalignr $8,%xmm4,%xmm4,%xmm8 + vaesenc %xmm15,%xmm10,%xmm10 + vpclmulqdq $0x10,%xmm3,%xmm4,%xmm4 + vpxor 0(%rdi),%xmm1,%xmm2 + vaesenc %xmm15,%xmm11,%xmm11 + vpxor 16(%rdi),%xmm1,%xmm0 + vaesenc %xmm15,%xmm12,%xmm12 + vpxor 32(%rdi),%xmm1,%xmm5 + vaesenc %xmm15,%xmm13,%xmm13 + vpxor 48(%rdi),%xmm1,%xmm6 + vaesenc %xmm15,%xmm14,%xmm14 + vpxor 64(%rdi),%xmm1,%xmm7 + vpxor 80(%rdi),%xmm1,%xmm3 + vmovdqu (%r8),%xmm1 + + vaesenclast %xmm2,%xmm9,%xmm9 + vmovdqu 32(%r11),%xmm2 + vaesenclast %xmm0,%xmm10,%xmm10 + vpaddb %xmm2,%xmm1,%xmm0 + movq %r13,112+8(%rsp) + leaq 96(%rdi),%rdi + vaesenclast %xmm5,%xmm11,%xmm11 + vpaddb %xmm2,%xmm0,%xmm5 + movq %r12,120+8(%rsp) + leaq 96(%rsi),%rsi + vmovdqu 0-128(%rcx),%xmm15 + vaesenclast %xmm6,%xmm12,%xmm12 + vpaddb %xmm2,%xmm5,%xmm6 + vaesenclast %xmm7,%xmm13,%xmm13 + vpaddb %xmm2,%xmm6,%xmm7 + vaesenclast %xmm3,%xmm14,%xmm14 + vpaddb %xmm2,%xmm7,%xmm3 + + addq $0x60,%r10 + subq $0x6,%rdx + jc .L6x_done_nmb + + vmovups %xmm9,-96(%rsi) + vpxor %xmm15,%xmm1,%xmm9 + vmovups %xmm10,-80(%rsi) + vmovdqa %xmm0,%xmm10 + vmovups %xmm11,-64(%rsi) + vmovdqa %xmm5,%xmm11 + vmovups %xmm12,-48(%rsi) + vmovdqa %xmm6,%xmm12 + vmovups %xmm13,-32(%rsi) + vmovdqa %xmm7,%xmm13 + vmovups %xmm14,-16(%rsi) + vmovdqa %xmm3,%xmm14 + vmovdqu 32+8(%rsp),%xmm7 + jmp .Loop6x_nmb + +.L6x_done_nmb: + vpxor 16+8(%rsp),%xmm8,%xmm8 + vpxor %xmm4,%xmm8,%xmm8 + + .byte 0xf3,0xc3 +.cfi_endproc +#.size _aesni_ctr32_ghash_no_movbe_6x,.-_aesni_ctr32_ghash_no_movbe_6x + +ENTRY_NP(aesni_gcm_decrypt) +#.globl aesni_gcm_decrypt +#.type aesni_gcm_decrypt,@function +.align 5, 0x90 +#aesni_gcm_decrypt: +.cfi_startproc + xorq %r10,%r10 + cmpq $0x60,%rdx + jb .Lgcm_dec_abort + + leaq (%rsp),%rax +.cfi_def_cfa_register %rax + pushq %rbx +.cfi_offset %rbx,-16 + pushq %rbp +.cfi_offset %rbp,-24 + pushq %r12 +.cfi_offset %r12,-32 + pushq %r13 +.cfi_offset %r13,-40 + pushq %r14 +.cfi_offset %r14,-48 + pushq %r15 +.cfi_offset %r15,-56 + pushq %r9 +.cfi_offset %r9,-64 + vzeroupper + + vmovdqu (%r8),%xmm1 + addq $-128,%rsp + movl 12(%r8),%ebx + leaq .Lbswap_mask(%rip),%r11 + leaq -128(%rcx),%r14 + movq $0xf80,%r15 + vmovdqu (%r9),%xmm8 + andq $-128,%rsp + vmovdqu (%r11),%xmm0 + leaq 128(%rcx),%rcx + movq 32(%r9),%r9 + leaq 32(%r9),%r9 + movl 504-128(%rcx),%ebp // ICP has a larger offset for rounds. + vpshufb %xmm0,%xmm8,%xmm8 + + andq %r15,%r14 + andq %rsp,%r15 + subq %r14,%r15 + jc .Ldec_no_key_aliasing + cmpq $768,%r15 + jnc .Ldec_no_key_aliasing + subq %r15,%rsp +.Ldec_no_key_aliasing: + + vmovdqu 80(%rdi),%xmm7 + leaq (%rdi),%r14 + vmovdqu 64(%rdi),%xmm4 + leaq -192(%rdi,%rdx,1),%r15 + vmovdqu 48(%rdi),%xmm5 + shrq $4,%rdx + xorq %r10,%r10 + vmovdqu 32(%rdi),%xmm6 + vpshufb %xmm0,%xmm7,%xmm7 + vmovdqu 16(%rdi),%xmm2 + vpshufb %xmm0,%xmm4,%xmm4 + vmovdqu (%rdi),%xmm3 + vpshufb %xmm0,%xmm5,%xmm5 + vmovdqu %xmm4,48(%rsp) + vpshufb %xmm0,%xmm6,%xmm6 + vmovdqu %xmm5,64(%rsp) + vpshufb %xmm0,%xmm2,%xmm2 + vmovdqu %xmm6,80(%rsp) + vpshufb %xmm0,%xmm3,%xmm3 + vmovdqu %xmm2,96(%rsp) + vmovdqu %xmm3,112(%rsp) + +#ifdef HAVE_MOVBE +#ifdef _KERNEL + testl $1,_gcm_avx_can_use_movbe(%rip) +#else + testl $1,_gcm_avx_can_use_movbe@GOTPCREL(%rip) +#endif + jz 1f + call _aesni_ctr32_ghash_6x + jmp 2f +1: +#endif + call _aesni_ctr32_ghash_no_movbe_6x +2: + vmovups %xmm9,-96(%rsi) + vmovups %xmm10,-80(%rsi) + vmovups %xmm11,-64(%rsi) + vmovups %xmm12,-48(%rsi) + vmovups %xmm13,-32(%rsi) + vmovups %xmm14,-16(%rsi) + + vpshufb (%r11),%xmm8,%xmm8 + movq -56(%rax),%r9 +.cfi_restore %r9 + vmovdqu %xmm8,(%r9) + + vzeroupper + movq -48(%rax),%r15 +.cfi_restore %r15 + movq -40(%rax),%r14 +.cfi_restore %r14 + movq -32(%rax),%r13 +.cfi_restore %r13 + movq -24(%rax),%r12 +.cfi_restore %r12 + movq -16(%rax),%rbp +.cfi_restore %rbp + movq -8(%rax),%rbx +.cfi_restore %rbx + leaq (%rax),%rsp +.cfi_def_cfa_register %rsp +.Lgcm_dec_abort: + movq %r10,%rax + .byte 0xf3,0xc3 +.cfi_endproc +#.size aesni_gcm_decrypt,.-aesni_gcm_decrypt + +ENTRY_NP(aesni_ctr32_6x) +#.type _aesni_ctr32_6x,@function +.align 5, 0x90 +#_aesni_ctr32_6x: +.cfi_startproc + vmovdqu 0-128(%rcx),%xmm4 + vmovdqu 32(%r11),%xmm2 + leaq -2(%rbp),%r13 // ICP uses 10,12,14 not 9,11,13 for rounds. + vmovups 16-128(%rcx),%xmm15 + leaq 32-128(%rcx),%r12 + vpxor %xmm4,%xmm1,%xmm9 + addl $100663296,%ebx + jc .Lhandle_ctr32_2 + vpaddb %xmm2,%xmm1,%xmm10 + vpaddb %xmm2,%xmm10,%xmm11 + vpxor %xmm4,%xmm10,%xmm10 + vpaddb %xmm2,%xmm11,%xmm12 + vpxor %xmm4,%xmm11,%xmm11 + vpaddb %xmm2,%xmm12,%xmm13 + vpxor %xmm4,%xmm12,%xmm12 + vpaddb %xmm2,%xmm13,%xmm14 + vpxor %xmm4,%xmm13,%xmm13 + vpaddb %xmm2,%xmm14,%xmm1 + vpxor %xmm4,%xmm14,%xmm14 + jmp .Loop_ctr32 + +.align 4, 0x90 +.Loop_ctr32: + vaesenc %xmm15,%xmm9,%xmm9 + vaesenc %xmm15,%xmm10,%xmm10 + vaesenc %xmm15,%xmm11,%xmm11 + vaesenc %xmm15,%xmm12,%xmm12 + vaesenc %xmm15,%xmm13,%xmm13 + vaesenc %xmm15,%xmm14,%xmm14 + vmovups (%r12),%xmm15 + leaq 16(%r12),%r12 + decl %r13d + jnz .Loop_ctr32 + + vmovdqu (%r12),%xmm3 + vaesenc %xmm15,%xmm9,%xmm9 + vpxor 0(%rdi),%xmm3,%xmm4 + vaesenc %xmm15,%xmm10,%xmm10 + vpxor 16(%rdi),%xmm3,%xmm5 + vaesenc %xmm15,%xmm11,%xmm11 + vpxor 32(%rdi),%xmm3,%xmm6 + vaesenc %xmm15,%xmm12,%xmm12 + vpxor 48(%rdi),%xmm3,%xmm8 + vaesenc %xmm15,%xmm13,%xmm13 + vpxor 64(%rdi),%xmm3,%xmm2 + vaesenc %xmm15,%xmm14,%xmm14 + vpxor 80(%rdi),%xmm3,%xmm3 + leaq 96(%rdi),%rdi + + vaesenclast %xmm4,%xmm9,%xmm9 + vaesenclast %xmm5,%xmm10,%xmm10 + vaesenclast %xmm6,%xmm11,%xmm11 + vaesenclast %xmm8,%xmm12,%xmm12 + vaesenclast %xmm2,%xmm13,%xmm13 + vaesenclast %xmm3,%xmm14,%xmm14 + vmovups %xmm9,0(%rsi) + vmovups %xmm10,16(%rsi) + vmovups %xmm11,32(%rsi) + vmovups %xmm12,48(%rsi) + vmovups %xmm13,64(%rsi) + vmovups %xmm14,80(%rsi) + leaq 96(%rsi),%rsi + + .byte 0xf3,0xc3 +.align 5, 0x90 +.Lhandle_ctr32_2: + vpshufb %xmm0,%xmm1,%xmm6 + vmovdqu 48(%r11),%xmm5 + vpaddd 64(%r11),%xmm6,%xmm10 + vpaddd %xmm5,%xmm6,%xmm11 + vpaddd %xmm5,%xmm10,%xmm12 + vpshufb %xmm0,%xmm10,%xmm10 + vpaddd %xmm5,%xmm11,%xmm13 + vpshufb %xmm0,%xmm11,%xmm11 + vpxor %xmm4,%xmm10,%xmm10 + vpaddd %xmm5,%xmm12,%xmm14 + vpshufb %xmm0,%xmm12,%xmm12 + vpxor %xmm4,%xmm11,%xmm11 + vpaddd %xmm5,%xmm13,%xmm1 + vpshufb %xmm0,%xmm13,%xmm13 + vpxor %xmm4,%xmm12,%xmm12 + vpshufb %xmm0,%xmm14,%xmm14 + vpxor %xmm4,%xmm13,%xmm13 + vpshufb %xmm0,%xmm1,%xmm1 + vpxor %xmm4,%xmm14,%xmm14 + jmp .Loop_ctr32 +.cfi_endproc +#.size _aesni_ctr32_6x,.-_aesni_ctr32_6x + +ENTRY_NP(aesni_gcm_encrypt) +#.globl aesni_gcm_encrypt +#.type aesni_gcm_encrypt,@function +.align 5, 0x90 +#aesni_gcm_encrypt: +.cfi_startproc + xorq %r10,%r10 + cmpq $288,%rdx + jb .Lgcm_enc_abort + + leaq (%rsp),%rax +.cfi_def_cfa_register %rax + pushq %rbx +.cfi_offset %rbx,-16 + pushq %rbp +.cfi_offset %rbp,-24 + pushq %r12 +.cfi_offset %r12,-32 + pushq %r13 +.cfi_offset %r13,-40 + pushq %r14 +.cfi_offset %r14,-48 + pushq %r15 +.cfi_offset %r15,-56 + pushq %r9 +.cfi_offset %r9,-64 + vzeroupper + + vmovdqu (%r8),%xmm1 + addq $-128,%rsp + movl 12(%r8),%ebx + leaq .Lbswap_mask(%rip),%r11 + leaq -128(%rcx),%r14 + movq $0xf80,%r15 + leaq 128(%rcx),%rcx + vmovdqu (%r11),%xmm0 + andq $-128,%rsp + movl 504-128(%rcx),%ebp // ICP has an larger offset for rounds. + + andq %r15,%r14 + andq %rsp,%r15 + subq %r14,%r15 + jc .Lenc_no_key_aliasing + cmpq $768,%r15 + jnc .Lenc_no_key_aliasing + subq %r15,%rsp +.Lenc_no_key_aliasing: + + leaq (%rsi),%r14 + leaq -192(%rsi,%rdx,1),%r15 + shrq $4,%rdx + + call _aesni_ctr32_6x + vpshufb %xmm0,%xmm9,%xmm8 + vpshufb %xmm0,%xmm10,%xmm2 + vmovdqu %xmm8,112(%rsp) + vpshufb %xmm0,%xmm11,%xmm4 + vmovdqu %xmm2,96(%rsp) + vpshufb %xmm0,%xmm12,%xmm5 + vmovdqu %xmm4,80(%rsp) + vpshufb %xmm0,%xmm13,%xmm6 + vmovdqu %xmm5,64(%rsp) + vpshufb %xmm0,%xmm14,%xmm7 + vmovdqu %xmm6,48(%rsp) + + call _aesni_ctr32_6x + + vmovdqu (%r9),%xmm8 + movq 32(%r9),%r9 + leaq 32(%r9),%r9 + subq $12,%rdx + movq $192,%r10 + vpshufb %xmm0,%xmm8,%xmm8 + +#ifdef HAVE_MOVBE +#ifdef _KERNEL + testl $1,_gcm_avx_can_use_movbe(%rip) +#else + testl $1,_gcm_avx_can_use_movbe@GOTPCREL(%rip) +#endif + jz 1f + call _aesni_ctr32_ghash_6x + jmp 2f +1: +#endif + call _aesni_ctr32_ghash_no_movbe_6x +2: + vmovdqu 32(%rsp),%xmm7 + vmovdqu (%r11),%xmm0 + vmovdqu 0-32(%r9),%xmm3 + vpunpckhqdq %xmm7,%xmm7,%xmm1 + vmovdqu 32-32(%r9),%xmm15 + vmovups %xmm9,-96(%rsi) + vpshufb %xmm0,%xmm9,%xmm9 + vpxor %xmm7,%xmm1,%xmm1 + vmovups %xmm10,-80(%rsi) + vpshufb %xmm0,%xmm10,%xmm10 + vmovups %xmm11,-64(%rsi) + vpshufb %xmm0,%xmm11,%xmm11 + vmovups %xmm12,-48(%rsi) + vpshufb %xmm0,%xmm12,%xmm12 + vmovups %xmm13,-32(%rsi) + vpshufb %xmm0,%xmm13,%xmm13 + vmovups %xmm14,-16(%rsi) + vpshufb %xmm0,%xmm14,%xmm14 + vmovdqu %xmm9,16(%rsp) + vmovdqu 48(%rsp),%xmm6 + vmovdqu 16-32(%r9),%xmm0 + vpunpckhqdq %xmm6,%xmm6,%xmm2 + vpclmulqdq $0x00,%xmm3,%xmm7,%xmm5 + vpxor %xmm6,%xmm2,%xmm2 + vpclmulqdq $0x11,%xmm3,%xmm7,%xmm7 + vpclmulqdq $0x00,%xmm15,%xmm1,%xmm1 + + vmovdqu 64(%rsp),%xmm9 + vpclmulqdq $0x00,%xmm0,%xmm6,%xmm4 + vmovdqu 48-32(%r9),%xmm3 + vpxor %xmm5,%xmm4,%xmm4 + vpunpckhqdq %xmm9,%xmm9,%xmm5 + vpclmulqdq $0x11,%xmm0,%xmm6,%xmm6 + vpxor %xmm9,%xmm5,%xmm5 + vpxor %xmm7,%xmm6,%xmm6 + vpclmulqdq $0x10,%xmm15,%xmm2,%xmm2 + vmovdqu 80-32(%r9),%xmm15 + vpxor %xmm1,%xmm2,%xmm2 + + vmovdqu 80(%rsp),%xmm1 + vpclmulqdq $0x00,%xmm3,%xmm9,%xmm7 + vmovdqu 64-32(%r9),%xmm0 + vpxor %xmm4,%xmm7,%xmm7 + vpunpckhqdq %xmm1,%xmm1,%xmm4 + vpclmulqdq $0x11,%xmm3,%xmm9,%xmm9 + vpxor %xmm1,%xmm4,%xmm4 + vpxor %xmm6,%xmm9,%xmm9 + vpclmulqdq $0x00,%xmm15,%xmm5,%xmm5 + vpxor %xmm2,%xmm5,%xmm5 + + vmovdqu 96(%rsp),%xmm2 + vpclmulqdq $0x00,%xmm0,%xmm1,%xmm6 + vmovdqu 96-32(%r9),%xmm3 + vpxor %xmm7,%xmm6,%xmm6 + vpunpckhqdq %xmm2,%xmm2,%xmm7 + vpclmulqdq $0x11,%xmm0,%xmm1,%xmm1 + vpxor %xmm2,%xmm7,%xmm7 + vpxor %xmm9,%xmm1,%xmm1 + vpclmulqdq $0x10,%xmm15,%xmm4,%xmm4 + vmovdqu 128-32(%r9),%xmm15 + vpxor %xmm5,%xmm4,%xmm4 + + vpxor 112(%rsp),%xmm8,%xmm8 + vpclmulqdq $0x00,%xmm3,%xmm2,%xmm5 + vmovdqu 112-32(%r9),%xmm0 + vpunpckhqdq %xmm8,%xmm8,%xmm9 + vpxor %xmm6,%xmm5,%xmm5 + vpclmulqdq $0x11,%xmm3,%xmm2,%xmm2 + vpxor %xmm8,%xmm9,%xmm9 + vpxor %xmm1,%xmm2,%xmm2 + vpclmulqdq $0x00,%xmm15,%xmm7,%xmm7 + vpxor %xmm4,%xmm7,%xmm4 + + vpclmulqdq $0x00,%xmm0,%xmm8,%xmm6 + vmovdqu 0-32(%r9),%xmm3 + vpunpckhqdq %xmm14,%xmm14,%xmm1 + vpclmulqdq $0x11,%xmm0,%xmm8,%xmm8 + vpxor %xmm14,%xmm1,%xmm1 + vpxor %xmm5,%xmm6,%xmm5 + vpclmulqdq $0x10,%xmm15,%xmm9,%xmm9 + vmovdqu 32-32(%r9),%xmm15 + vpxor %xmm2,%xmm8,%xmm7 + vpxor %xmm4,%xmm9,%xmm6 + + vmovdqu 16-32(%r9),%xmm0 + vpxor %xmm5,%xmm7,%xmm9 + vpclmulqdq $0x00,%xmm3,%xmm14,%xmm4 + vpxor %xmm9,%xmm6,%xmm6 + vpunpckhqdq %xmm13,%xmm13,%xmm2 + vpclmulqdq $0x11,%xmm3,%xmm14,%xmm14 + vpxor %xmm13,%xmm2,%xmm2 + vpslldq $8,%xmm6,%xmm9 + vpclmulqdq $0x00,%xmm15,%xmm1,%xmm1 + vpxor %xmm9,%xmm5,%xmm8 + vpsrldq $8,%xmm6,%xmm6 + vpxor %xmm6,%xmm7,%xmm7 + + vpclmulqdq $0x00,%xmm0,%xmm13,%xmm5 + vmovdqu 48-32(%r9),%xmm3 + vpxor %xmm4,%xmm5,%xmm5 + vpunpckhqdq %xmm12,%xmm12,%xmm9 + vpclmulqdq $0x11,%xmm0,%xmm13,%xmm13 + vpxor %xmm12,%xmm9,%xmm9 + vpxor %xmm14,%xmm13,%xmm13 + vpalignr $8,%xmm8,%xmm8,%xmm14 + vpclmulqdq $0x10,%xmm15,%xmm2,%xmm2 + vmovdqu 80-32(%r9),%xmm15 + vpxor %xmm1,%xmm2,%xmm2 + + vpclmulqdq $0x00,%xmm3,%xmm12,%xmm4 + vmovdqu 64-32(%r9),%xmm0 + vpxor %xmm5,%xmm4,%xmm4 + vpunpckhqdq %xmm11,%xmm11,%xmm1 + vpclmulqdq $0x11,%xmm3,%xmm12,%xmm12 + vpxor %xmm11,%xmm1,%xmm1 + vpxor %xmm13,%xmm12,%xmm12 + vxorps 16(%rsp),%xmm7,%xmm7 + vpclmulqdq $0x00,%xmm15,%xmm9,%xmm9 + vpxor %xmm2,%xmm9,%xmm9 + + vpclmulqdq $0x10,16(%r11),%xmm8,%xmm8 + vxorps %xmm14,%xmm8,%xmm8 + + vpclmulqdq $0x00,%xmm0,%xmm11,%xmm5 + vmovdqu 96-32(%r9),%xmm3 + vpxor %xmm4,%xmm5,%xmm5 + vpunpckhqdq %xmm10,%xmm10,%xmm2 + vpclmulqdq $0x11,%xmm0,%xmm11,%xmm11 + vpxor %xmm10,%xmm2,%xmm2 + vpalignr $8,%xmm8,%xmm8,%xmm14 + vpxor %xmm12,%xmm11,%xmm11 + vpclmulqdq $0x10,%xmm15,%xmm1,%xmm1 + vmovdqu 128-32(%r9),%xmm15 + vpxor %xmm9,%xmm1,%xmm1 + + vxorps %xmm7,%xmm14,%xmm14 + vpclmulqdq $0x10,16(%r11),%xmm8,%xmm8 + vxorps %xmm14,%xmm8,%xmm8 + + vpclmulqdq $0x00,%xmm3,%xmm10,%xmm4 + vmovdqu 112-32(%r9),%xmm0 + vpxor %xmm5,%xmm4,%xmm4 + vpunpckhqdq %xmm8,%xmm8,%xmm9 + vpclmulqdq $0x11,%xmm3,%xmm10,%xmm10 + vpxor %xmm8,%xmm9,%xmm9 + vpxor %xmm11,%xmm10,%xmm10 + vpclmulqdq $0x00,%xmm15,%xmm2,%xmm2 + vpxor %xmm1,%xmm2,%xmm2 + + vpclmulqdq $0x00,%xmm0,%xmm8,%xmm5 + vpclmulqdq $0x11,%xmm0,%xmm8,%xmm7 + vpxor %xmm4,%xmm5,%xmm5 + vpclmulqdq $0x10,%xmm15,%xmm9,%xmm6 + vpxor %xmm10,%xmm7,%xmm7 + vpxor %xmm2,%xmm6,%xmm6 + + vpxor %xmm5,%xmm7,%xmm4 + vpxor %xmm4,%xmm6,%xmm6 + vpslldq $8,%xmm6,%xmm1 + vmovdqu 16(%r11),%xmm3 + vpsrldq $8,%xmm6,%xmm6 + vpxor %xmm1,%xmm5,%xmm8 + vpxor %xmm6,%xmm7,%xmm7 + + vpalignr $8,%xmm8,%xmm8,%xmm2 + vpclmulqdq $0x10,%xmm3,%xmm8,%xmm8 + vpxor %xmm2,%xmm8,%xmm8 + + vpalignr $8,%xmm8,%xmm8,%xmm2 + vpclmulqdq $0x10,%xmm3,%xmm8,%xmm8 + vpxor %xmm7,%xmm2,%xmm2 + vpxor %xmm2,%xmm8,%xmm8 + vpshufb (%r11),%xmm8,%xmm8 + movq -56(%rax),%r9 +.cfi_restore %r9 + vmovdqu %xmm8,(%r9) + + vzeroupper + movq -48(%rax),%r15 +.cfi_restore %r15 + movq -40(%rax),%r14 +.cfi_restore %r14 + movq -32(%rax),%r13 +.cfi_restore %r13 + movq -24(%rax),%r12 +.cfi_restore %r12 + movq -16(%rax),%rbp +.cfi_restore %rbp + movq -8(%rax),%rbx +.cfi_restore %rbx + leaq (%rax),%rsp +.cfi_def_cfa_register %rsp +.Lgcm_enc_abort: + movq %r10,%rax + .byte 0xf3,0xc3 +.cfi_endproc +#.size aesni_gcm_encrypt,.-aesni_gcm_encrypt + +/* Some utility routines */ + +/* + * clear all fpu registers + * void clear_fpu_regs_avx(void); + */ +ENTRY_NP(clear_fpu_regs_avx) +#.globl clear_fpu_regs_avx +#.type clear_fpu_regs_avx,@function +.align 5, 0x90 +#clear_fpu_regs_avx: + vzeroall + ret +#.size clear_fpu_regs_avx,.-clear_fpu_regs_avx + +/* + * void gcm_xor_avx(const uint8_t *src, uint8_t *dst); + * + * XORs one pair of unaligned 128-bit blocks from `src' and `dst' and + * stores the result at `dst'. The XOR is performed using FPU registers, + * so make sure FPU state is saved when running this in the kernel. + */ +ENTRY_NP(gcm_xor_avx) +#.globl gcm_xor_avx +#.type gcm_xor_avx,@function +.align 5, 0x90 +#gcm_xor_avx: + movdqu (%rdi), %xmm0 + movdqu (%rsi), %xmm1 + pxor %xmm1, %xmm0 + movdqu %xmm0, (%rsi) + ret +#.size gcm_xor_avx,.-gcm_xor_avx + +/* + * Toggle a boolean_t value atomically and return the new value. + * boolean_t atomic_toggle_boolean_nv(volatile boolean_t *); + */ +ENTRY_NP(atomic_toggle_boolean_nv) +#.globl atomic_toggle_boolean_nv +#.type atomic_toggle_boolean_nv,@function +.align 5, 0x90 +#atomic_toggle_boolean_nv: + xorl %eax, %eax + lock + xorl $1, (%rdi) + jz 1f + movl $1, %eax +1: + ret +#.size atomic_toggle_boolean_nv,.-atomic_toggle_boolean_nv + +.align 6, 0x90 +.Lbswap_mask: +.byte 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0 +.Lpoly: +.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xc2 +.Lone_msb: +.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 +.Ltwo_lsb: +.byte 2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.Lone_lsb: +.byte 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.byte 65,69,83,45,78,73,32,71,67,77,32,109,111,100,117,108,101,32,102,111,114,32,120,56,54,95,54,52,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0 +.align 5, 0x90 + +/* Mark the stack non-executable. */ +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif + +#endif /* defined(__x86_64__) && defined(HAVE_AVX) && defined(HAVE_AES) ... */ diff --git a/module/icp/asm-x86_64/os/macos/modes/gcm_pclmulqdq.S b/module/icp/asm-x86_64/os/macos/modes/gcm_pclmulqdq.S new file mode 100644 index 000000000000..20f4d14c7836 --- /dev/null +++ b/module/icp/asm-x86_64/os/macos/modes/gcm_pclmulqdq.S @@ -0,0 +1,334 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2009 Intel Corporation + * All Rights Reserved. + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Accelerated GHASH implementation with Intel PCLMULQDQ-NI + * instructions. This file contains an accelerated + * Galois Field Multiplication implementation. + * + * PCLMULQDQ is used to accelerate the most time-consuming part of GHASH, + * carry-less multiplication. More information about PCLMULQDQ can be + * found at: + * http://software.intel.com/en-us/articles/ + * carry-less-multiplication-and-its-usage-for-computing-the-gcm-mode/ + * + */ + +/* + * ==================================================================== + * OpenSolaris OS modifications + * + * This source originates as file galois_hash_asm.c from + * Intel Corporation dated September 21, 2009. + * + * This OpenSolaris version has these major changes from the original source: + * + * 1. Added OpenSolaris ENTRY_NP/SET_SIZE macros from + * /usr/include/sys/asm_linkage.h, lint(1B) guards, and a dummy C function + * definition for lint. + * + * 2. Formatted code, added comments, and added #includes and #defines. + * + * 3. If bit CR0.TS is set, clear and set the TS bit, after and before + * calling kpreempt_disable() and kpreempt_enable(). + * If the TS bit is not set, Save and restore %xmm registers at the beginning + * and end of function calls (%xmm* registers are not saved and restored by + * during kernel thread preemption). + * + * 4. Removed code to perform hashing. This is already done with C macro + * GHASH in gcm.c. For better performance, this removed code should be + * reintegrated in the future to replace the C GHASH macro. + * + * 5. Added code to byte swap 16-byte input and output. + * + * 6. Folded in comments from the original C source with embedded assembly + * (SB_w_shift_xor.c) + * + * 7. Renamed function and reordered parameters to match OpenSolaris: + * Intel interface: + * void galois_hash_asm(unsigned char *hk, unsigned char *s, + * unsigned char *d, int length) + * OpenSolaris OS interface: + * void gcm_mul_pclmulqdq(uint64_t *x_in, uint64_t *y, uint64_t *res); + * ==================================================================== + */ + + +#if defined(lint) || defined(__lint) + +#include + +/* ARGSUSED */ +void +gcm_mul_pclmulqdq(uint64_t *x_in, uint64_t *y, uint64_t *res) { +} + +#else /* lint */ + +#define _ASM +#include + +#if defined(_KERNEL) && !defined(__APPLE__) + /* + * Note: the CLTS macro clobbers P2 (%rsi) under i86xpv. That is, + * it calls HYPERVISOR_fpu_taskswitch() which modifies %rsi when it + * uses it to pass P2 to syscall. + * This also occurs with the STTS macro, but we dont care if + * P2 (%rsi) is modified just before function exit. + * The CLTS and STTS macros push and pop P1 (%rdi) already. + */ +#ifdef __xpv +#define PROTECTED_CLTS \ + push %rsi; \ + CLTS; \ + pop %rsi +#else +#define PROTECTED_CLTS \ + CLTS +#endif /* __xpv */ + + /* + * If CR0_TS is not set, align stack (with push %rbp) and push + * %xmm0 - %xmm10 on stack, otherwise clear CR0_TS + */ +#define CLEAR_TS_OR_PUSH_XMM_REGISTERS(tmpreg) \ + push %rbp; \ + mov %rsp, %rbp; \ + movq %cr0, tmpreg; \ + testq $CR0_TS, tmpreg; \ + jnz 1f; \ + and $-XMM_ALIGN, %rsp; \ + sub $(XMM_SIZE * 11), %rsp; \ + movaps %xmm0, 160(%rsp); \ + movaps %xmm1, 144(%rsp); \ + movaps %xmm2, 128(%rsp); \ + movaps %xmm3, 112(%rsp); \ + movaps %xmm4, 96(%rsp); \ + movaps %xmm5, 80(%rsp); \ + movaps %xmm6, 64(%rsp); \ + movaps %xmm7, 48(%rsp); \ + movaps %xmm8, 32(%rsp); \ + movaps %xmm9, 16(%rsp); \ + movaps %xmm10, (%rsp); \ + jmp 2f; \ +1: \ + PROTECTED_CLTS; \ +2: + + + /* + * If CR0_TS was not set above, pop %xmm0 - %xmm10 off stack, + * otherwise set CR0_TS. + */ +#define SET_TS_OR_POP_XMM_REGISTERS(tmpreg) \ + testq $CR0_TS, tmpreg; \ + jnz 1f; \ + movaps (%rsp), %xmm10; \ + movaps 16(%rsp), %xmm9; \ + movaps 32(%rsp), %xmm8; \ + movaps 48(%rsp), %xmm7; \ + movaps 64(%rsp), %xmm6; \ + movaps 80(%rsp), %xmm5; \ + movaps 96(%rsp), %xmm4; \ + movaps 112(%rsp), %xmm3; \ + movaps 128(%rsp), %xmm2; \ + movaps 144(%rsp), %xmm1; \ + movaps 160(%rsp), %xmm0; \ + jmp 2f; \ +1: \ + STTS(tmpreg); \ +2: \ + mov %rbp, %rsp; \ + pop %rbp + + +#else +#define PROTECTED_CLTS +#define CLEAR_TS_OR_PUSH_XMM_REGISTERS(tmpreg) +#define SET_TS_OR_POP_XMM_REGISTERS(tmpreg) +#endif /* _KERNEL */ + +/* + * Use this mask to byte-swap a 16-byte integer with the pshufb instruction + */ + +// static uint8_t byte_swap16_mask[] = { +// 15, 14, 13, 12, 11, 10, 9, 8, 7, 6 ,5, 4, 3, 2, 1, 0 }; +.text +.align XMM_ALIGN_LOG +.Lbyte_swap16_mask: + .byte 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + + + +/* + * void gcm_mul_pclmulqdq(uint64_t *x_in, uint64_t *y, uint64_t *res); + * + * Perform a carry-less multiplication (that is, use XOR instead of the + * multiply operator) on P1 and P2 and place the result in P3. + * + * Byte swap the input and the output. + * + * Note: x_in, y, and res all point to a block of 20-byte numbers + * (an array of two 64-bit integers). + * + * Note2: For kernel code, caller is responsible for ensuring + * kpreempt_disable() has been called. This is because %xmm registers are + * not saved/restored. Clear and set the CR0.TS bit on entry and exit, + * respectively, if TS is set on entry. Otherwise, if TS is not set, + * save and restore %xmm registers on the stack. + * + * Note3: Original Intel definition: + * void galois_hash_asm(unsigned char *hk, unsigned char *s, + * unsigned char *d, int length) + * + * Note4: Register/parameter mapping: + * Intel: + * Parameter 1: %rcx (copied to %xmm0) hk or x_in + * Parameter 2: %rdx (copied to %xmm1) s or y + * Parameter 3: %rdi (result) d or res + * OpenSolaris: + * Parameter 1: %rdi (copied to %xmm0) x_in + * Parameter 2: %rsi (copied to %xmm1) y + * Parameter 3: %rdx (result) res + */ + +ENTRY_NP(gcm_mul_pclmulqdq) + CLEAR_TS_OR_PUSH_XMM_REGISTERS(%r10) + + // + // Copy Parameters + // + movdqu (%rdi), %xmm0 // P1 + movdqu (%rsi), %xmm1 // P2 + + // + // Byte swap 16-byte input + // + lea .Lbyte_swap16_mask(%rip), %rax + movups (%rax), %xmm10 + pshufb %xmm10, %xmm0 + pshufb %xmm10, %xmm1 + + + // + // Multiply with the hash key + // + movdqu %xmm0, %xmm3 + pclmulqdq $0, %xmm1, %xmm3 // xmm3 holds a0*b0 + + movdqu %xmm0, %xmm4 + pclmulqdq $16, %xmm1, %xmm4 // xmm4 holds a0*b1 + + movdqu %xmm0, %xmm5 + pclmulqdq $1, %xmm1, %xmm5 // xmm5 holds a1*b0 + movdqu %xmm0, %xmm6 + pclmulqdq $17, %xmm1, %xmm6 // xmm6 holds a1*b1 + + pxor %xmm5, %xmm4 // xmm4 holds a0*b1 + a1*b0 + + movdqu %xmm4, %xmm5 // move the contents of xmm4 to xmm5 + psrldq $8, %xmm4 // shift by xmm4 64 bits to the right + pslldq $8, %xmm5 // shift by xmm5 64 bits to the left + pxor %xmm5, %xmm3 + pxor %xmm4, %xmm6 // Register pair holds the result + // of the carry-less multiplication of + // xmm0 by xmm1. + + // We shift the result of the multiplication by one bit position + // to the left to cope for the fact that the bits are reversed. + movdqu %xmm3, %xmm7 + movdqu %xmm6, %xmm8 + pslld $1, %xmm3 + pslld $1, %xmm6 + psrld $31, %xmm7 + psrld $31, %xmm8 + movdqu %xmm7, %xmm9 + pslldq $4, %xmm8 + pslldq $4, %xmm7 + psrldq $12, %xmm9 + por %xmm7, %xmm3 + por %xmm8, %xmm6 + por %xmm9, %xmm6 + + // + // First phase of the reduction + // + // Move xmm3 into xmm7, xmm8, xmm9 in order to perform the shifts + // independently. + movdqu %xmm3, %xmm7 + movdqu %xmm3, %xmm8 + movdqu %xmm3, %xmm9 + pslld $31, %xmm7 // packed right shift shifting << 31 + pslld $30, %xmm8 // packed right shift shifting << 30 + pslld $25, %xmm9 // packed right shift shifting << 25 + pxor %xmm8, %xmm7 // xor the shifted versions + pxor %xmm9, %xmm7 + movdqu %xmm7, %xmm8 + pslldq $12, %xmm7 + psrldq $4, %xmm8 + pxor %xmm7, %xmm3 // first phase of the reduction complete + + // + // Second phase of the reduction + // + // Make 3 copies of xmm3 in xmm2, xmm4, xmm5 for doing these + // shift operations. + movdqu %xmm3, %xmm2 + movdqu %xmm3, %xmm4 // packed left shifting >> 1 + movdqu %xmm3, %xmm5 + psrld $1, %xmm2 + psrld $2, %xmm4 // packed left shifting >> 2 + psrld $7, %xmm5 // packed left shifting >> 7 + pxor %xmm4, %xmm2 // xor the shifted versions + pxor %xmm5, %xmm2 + pxor %xmm8, %xmm2 + pxor %xmm2, %xmm3 + pxor %xmm3, %xmm6 // the result is in xmm6 + + // + // Byte swap 16-byte result + // + pshufb %xmm10, %xmm6 // %xmm10 has the swap mask + + // + // Store the result + // + movdqu %xmm6, (%rdx) // P3 + + + // + // Cleanup and Return + // + SET_TS_OR_POP_XMM_REGISTERS(%r10) + ret + SET_SIZE(gcm_mul_pclmulqdq) + +#endif /* lint || __lint */ diff --git a/module/icp/asm-x86_64/os/macos/modes/ghash-x86_64.S b/module/icp/asm-x86_64/os/macos/modes/ghash-x86_64.S new file mode 100644 index 000000000000..9e084bf1d05d --- /dev/null +++ b/module/icp/asm-x86_64/os/macos/modes/ghash-x86_64.S @@ -0,0 +1,724 @@ +# Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +# +# ==================================================================== +# Written by Andy Polyakov for the OpenSSL +# project. The module is, however, dual licensed under OpenSSL and +# CRYPTOGAMS licenses depending on where you obtain it. For further +# details see http://www.openssl.org/~appro/cryptogams/. +# ==================================================================== +# +# March, June 2010 +# +# The module implements "4-bit" GCM GHASH function and underlying +# single multiplication operation in GF(2^128). "4-bit" means that +# it uses 256 bytes per-key table [+128 bytes shared table]. GHASH +# function features so called "528B" variant utilizing additional +# 256+16 bytes of per-key storage [+512 bytes shared table]. +# Performance results are for this streamed GHASH subroutine and are +# expressed in cycles per processed byte, less is better: +# +# gcc 3.4.x(*) assembler +# +# P4 28.6 14.0 +100% +# Opteron 19.3 7.7 +150% +# Core2 17.8 8.1(**) +120% +# Atom 31.6 16.8 +88% +# VIA Nano 21.8 10.1 +115% +# +# (*) comparison is not completely fair, because C results are +# for vanilla "256B" implementation, while assembler results +# are for "528B";-) +# (**) it's mystery [to me] why Core2 result is not same as for +# Opteron; + +# May 2010 +# +# Add PCLMULQDQ version performing at 2.02 cycles per processed byte. +# See ghash-x86.pl for background information and details about coding +# techniques. +# +# Special thanks to David Woodhouse for providing access to a +# Westmere-based system on behalf of Intel Open Source Technology Centre. + +# December 2012 +# +# Overhaul: aggregate Karatsuba post-processing, improve ILP in +# reduction_alg9, increase reduction aggregate factor to 4x. As for +# the latter. ghash-x86.pl discusses that it makes lesser sense to +# increase aggregate factor. Then why increase here? Critical path +# consists of 3 independent pclmulqdq instructions, Karatsuba post- +# processing and reduction. "On top" of this we lay down aggregated +# multiplication operations, triplets of independent pclmulqdq's. As +# issue rate for pclmulqdq is limited, it makes lesser sense to +# aggregate more multiplications than it takes to perform remaining +# non-multiplication operations. 2x is near-optimal coefficient for +# contemporary Intel CPUs (therefore modest improvement coefficient), +# but not for Bulldozer. Latter is because logical SIMD operations +# are twice as slow in comparison to Intel, so that critical path is +# longer. A CPU with higher pclmulqdq issue rate would also benefit +# from higher aggregate factor... +# +# Westmere 1.78(+13%) +# Sandy Bridge 1.80(+8%) +# Ivy Bridge 1.80(+7%) +# Haswell 0.55(+93%) (if system doesn't support AVX) +# Broadwell 0.45(+110%)(if system doesn't support AVX) +# Skylake 0.44(+110%)(if system doesn't support AVX) +# Bulldozer 1.49(+27%) +# Silvermont 2.88(+13%) +# Knights L 2.12(-) (if system doesn't support AVX) +# Goldmont 1.08(+24%) + +# March 2013 +# +# ... 8x aggregate factor AVX code path is using reduction algorithm +# suggested by Shay Gueron[1]. Even though contemporary AVX-capable +# CPUs such as Sandy and Ivy Bridge can execute it, the code performs +# sub-optimally in comparison to above mentioned version. But thanks +# to Ilya Albrekht and Max Locktyukhin of Intel Corp. we knew that +# it performs in 0.41 cycles per byte on Haswell processor, in +# 0.29 on Broadwell, and in 0.36 on Skylake. +# +# Knights Landing achieves 1.09 cpb. +# +# [1] http://rt.openssl.org/Ticket/Display.html?id=2900&user=guest&pass=guest + +# Generated once from +# https://github.com/openssl/openssl/blob/5ffc3324/crypto/modes/asm/ghash-x86_64.pl +# and modified for ICP. Modification are kept at a bare minimum to ease later +# upstream merges. + + +#if defined(__x86_64__) && defined(HAVE_AVX) && \ + defined(HAVE_AES) && defined(HAVE_PCLMULQDQ) + +#define _ASM +#include + +.text + +# .globl gcm_gmult_clmul +#.type gcm_gmult_clmul,@function +#.align 16 +ENTRY_NP(gcm_gmult_clmul) +.align 4, 0x90 +#gcm_gmult_clmul: +.cfi_startproc +.L_gmult_clmul: + movdqu (%rdi),%xmm0 + movdqa .Lbswap_mask(%rip),%xmm5 + movdqu (%rsi),%xmm2 + movdqu 32(%rsi),%xmm4 +.byte 102,15,56,0,197 + movdqa %xmm0,%xmm1 + pshufd $78,%xmm0,%xmm3 + pxor %xmm0,%xmm3 +.byte 102,15,58,68,194,0 +.byte 102,15,58,68,202,17 +.byte 102,15,58,68,220,0 + pxor %xmm0,%xmm3 + pxor %xmm1,%xmm3 + + movdqa %xmm3,%xmm4 + psrldq $8,%xmm3 + pslldq $8,%xmm4 + pxor %xmm3,%xmm1 + pxor %xmm4,%xmm0 + + movdqa %xmm0,%xmm4 + movdqa %xmm0,%xmm3 + psllq $5,%xmm0 + pxor %xmm0,%xmm3 + psllq $1,%xmm0 + pxor %xmm3,%xmm0 + psllq $57,%xmm0 + movdqa %xmm0,%xmm3 + pslldq $8,%xmm0 + psrldq $8,%xmm3 + pxor %xmm4,%xmm0 + pxor %xmm3,%xmm1 + + + movdqa %xmm0,%xmm4 + psrlq $1,%xmm0 + pxor %xmm4,%xmm1 + pxor %xmm0,%xmm4 + psrlq $5,%xmm0 + pxor %xmm4,%xmm0 + psrlq $1,%xmm0 + pxor %xmm1,%xmm0 +.byte 102,15,56,0,197 + movdqu %xmm0,(%rdi) + .byte 0xf3,0xc3 +.cfi_endproc +#.size gcm_gmult_clmul,.-gcm_gmult_clmul + +ENTRY_NP(gcm_init_htab_avx) +#.globl gcm_init_htab_avx +#.type gcm_init_htab_avx,@function +.align 5, 0x90 +#gcm_init_htab_avx: +.cfi_startproc + vzeroupper + + vmovdqu (%rsi),%xmm2 + // KCF/ICP stores H in network byte order with the hi qword first + // so we need to swap all bytes, not the 2 qwords. + vmovdqu .Lbswap_mask(%rip),%xmm4 + vpshufb %xmm4,%xmm2,%xmm2 + + + vpshufd $255,%xmm2,%xmm4 + vpsrlq $63,%xmm2,%xmm3 + vpsllq $1,%xmm2,%xmm2 + vpxor %xmm5,%xmm5,%xmm5 + vpcmpgtd %xmm4,%xmm5,%xmm5 + vpslldq $8,%xmm3,%xmm3 + vpor %xmm3,%xmm2,%xmm2 + + + vpand .L0x1c2_polynomial(%rip),%xmm5,%xmm5 + vpxor %xmm5,%xmm2,%xmm2 + + vpunpckhqdq %xmm2,%xmm2,%xmm6 + vmovdqa %xmm2,%xmm0 + vpxor %xmm2,%xmm6,%xmm6 + movq $4,%r10 + jmp .Linit_start_avx +.align 4, 0x90 +.Linit_loop_avx: + vpalignr $8,%xmm3,%xmm4,%xmm5 + vmovdqu %xmm5,-16(%rdi) + vpunpckhqdq %xmm0,%xmm0,%xmm3 + vpxor %xmm0,%xmm3,%xmm3 + vpclmulqdq $0x11,%xmm2,%xmm0,%xmm1 + vpclmulqdq $0x00,%xmm2,%xmm0,%xmm0 + vpclmulqdq $0x00,%xmm6,%xmm3,%xmm3 + vpxor %xmm0,%xmm1,%xmm4 + vpxor %xmm4,%xmm3,%xmm3 + + vpslldq $8,%xmm3,%xmm4 + vpsrldq $8,%xmm3,%xmm3 + vpxor %xmm4,%xmm0,%xmm0 + vpxor %xmm3,%xmm1,%xmm1 + vpsllq $57,%xmm0,%xmm3 + vpsllq $62,%xmm0,%xmm4 + vpxor %xmm3,%xmm4,%xmm4 + vpsllq $63,%xmm0,%xmm3 + vpxor %xmm3,%xmm4,%xmm4 + vpslldq $8,%xmm4,%xmm3 + vpsrldq $8,%xmm4,%xmm4 + vpxor %xmm3,%xmm0,%xmm0 + vpxor %xmm4,%xmm1,%xmm1 + + vpsrlq $1,%xmm0,%xmm4 + vpxor %xmm0,%xmm1,%xmm1 + vpxor %xmm4,%xmm0,%xmm0 + vpsrlq $5,%xmm4,%xmm4 + vpxor %xmm4,%xmm0,%xmm0 + vpsrlq $1,%xmm0,%xmm0 + vpxor %xmm1,%xmm0,%xmm0 +.Linit_start_avx: + vmovdqa %xmm0,%xmm5 + vpunpckhqdq %xmm0,%xmm0,%xmm3 + vpxor %xmm0,%xmm3,%xmm3 + vpclmulqdq $0x11,%xmm2,%xmm0,%xmm1 + vpclmulqdq $0x00,%xmm2,%xmm0,%xmm0 + vpclmulqdq $0x00,%xmm6,%xmm3,%xmm3 + vpxor %xmm0,%xmm1,%xmm4 + vpxor %xmm4,%xmm3,%xmm3 + + vpslldq $8,%xmm3,%xmm4 + vpsrldq $8,%xmm3,%xmm3 + vpxor %xmm4,%xmm0,%xmm0 + vpxor %xmm3,%xmm1,%xmm1 + vpsllq $57,%xmm0,%xmm3 + vpsllq $62,%xmm0,%xmm4 + vpxor %xmm3,%xmm4,%xmm4 + vpsllq $63,%xmm0,%xmm3 + vpxor %xmm3,%xmm4,%xmm4 + vpslldq $8,%xmm4,%xmm3 + vpsrldq $8,%xmm4,%xmm4 + vpxor %xmm3,%xmm0,%xmm0 + vpxor %xmm4,%xmm1,%xmm1 + + vpsrlq $1,%xmm0,%xmm4 + vpxor %xmm0,%xmm1,%xmm1 + vpxor %xmm4,%xmm0,%xmm0 + vpsrlq $5,%xmm4,%xmm4 + vpxor %xmm4,%xmm0,%xmm0 + vpsrlq $1,%xmm0,%xmm0 + vpxor %xmm1,%xmm0,%xmm0 + vpshufd $78,%xmm5,%xmm3 + vpshufd $78,%xmm0,%xmm4 + vpxor %xmm5,%xmm3,%xmm3 + vmovdqu %xmm5,0(%rdi) + vpxor %xmm0,%xmm4,%xmm4 + vmovdqu %xmm0,16(%rdi) + leaq 48(%rdi),%rdi + subq $1,%r10 + jnz .Linit_loop_avx + + vpalignr $8,%xmm4,%xmm3,%xmm5 + vmovdqu %xmm5,-16(%rdi) + + vzeroupper + .byte 0xf3,0xc3 +.cfi_endproc +#.size gcm_init_htab_avx,.-gcm_init_htab_avx + +ENTRY_NP(gcm_gmult_avx) +#.globl gcm_gmult_avx +#.type gcm_gmult_avx,@function +.align 5, 0x90 +#gcm_gmult_avx: +.cfi_startproc + jmp .L_gmult_clmul +.cfi_endproc +#.size gcm_gmult_avx,.-gcm_gmult_avx + +ENTRY_NP(gcm_ghash_avx) +#.globl gcm_ghash_avx +#.type gcm_ghash_avx,@function +.align 5, 0x90 +#gcm_ghash_avx: +.cfi_startproc + vzeroupper + + vmovdqu (%rdi),%xmm10 + leaq .L0x1c2_polynomial(%rip),%r10 + leaq 64(%rsi),%rsi + vmovdqu .Lbswap_mask(%rip),%xmm13 + vpshufb %xmm13,%xmm10,%xmm10 + cmpq $0x80,%rcx + jb .Lshort_avx + subq $0x80,%rcx + + vmovdqu 112(%rdx),%xmm14 + vmovdqu 0-64(%rsi),%xmm6 + vpshufb %xmm13,%xmm14,%xmm14 + vmovdqu 32-64(%rsi),%xmm7 + + vpunpckhqdq %xmm14,%xmm14,%xmm9 + vmovdqu 96(%rdx),%xmm15 + vpclmulqdq $0x00,%xmm6,%xmm14,%xmm0 + vpxor %xmm14,%xmm9,%xmm9 + vpshufb %xmm13,%xmm15,%xmm15 + vpclmulqdq $0x11,%xmm6,%xmm14,%xmm1 + vmovdqu 16-64(%rsi),%xmm6 + vpunpckhqdq %xmm15,%xmm15,%xmm8 + vmovdqu 80(%rdx),%xmm14 + vpclmulqdq $0x00,%xmm7,%xmm9,%xmm2 + vpxor %xmm15,%xmm8,%xmm8 + + vpshufb %xmm13,%xmm14,%xmm14 + vpclmulqdq $0x00,%xmm6,%xmm15,%xmm3 + vpunpckhqdq %xmm14,%xmm14,%xmm9 + vpclmulqdq $0x11,%xmm6,%xmm15,%xmm4 + vmovdqu 48-64(%rsi),%xmm6 + vpxor %xmm14,%xmm9,%xmm9 + vmovdqu 64(%rdx),%xmm15 + vpclmulqdq $0x10,%xmm7,%xmm8,%xmm5 + vmovdqu 80-64(%rsi),%xmm7 + + vpshufb %xmm13,%xmm15,%xmm15 + vpxor %xmm0,%xmm3,%xmm3 + vpclmulqdq $0x00,%xmm6,%xmm14,%xmm0 + vpxor %xmm1,%xmm4,%xmm4 + vpunpckhqdq %xmm15,%xmm15,%xmm8 + vpclmulqdq $0x11,%xmm6,%xmm14,%xmm1 + vmovdqu 64-64(%rsi),%xmm6 + vpxor %xmm2,%xmm5,%xmm5 + vpclmulqdq $0x00,%xmm7,%xmm9,%xmm2 + vpxor %xmm15,%xmm8,%xmm8 + + vmovdqu 48(%rdx),%xmm14 + vpxor %xmm3,%xmm0,%xmm0 + vpclmulqdq $0x00,%xmm6,%xmm15,%xmm3 + vpxor %xmm4,%xmm1,%xmm1 + vpshufb %xmm13,%xmm14,%xmm14 + vpclmulqdq $0x11,%xmm6,%xmm15,%xmm4 + vmovdqu 96-64(%rsi),%xmm6 + vpxor %xmm5,%xmm2,%xmm2 + vpunpckhqdq %xmm14,%xmm14,%xmm9 + vpclmulqdq $0x10,%xmm7,%xmm8,%xmm5 + vmovdqu 128-64(%rsi),%xmm7 + vpxor %xmm14,%xmm9,%xmm9 + + vmovdqu 32(%rdx),%xmm15 + vpxor %xmm0,%xmm3,%xmm3 + vpclmulqdq $0x00,%xmm6,%xmm14,%xmm0 + vpxor %xmm1,%xmm4,%xmm4 + vpshufb %xmm13,%xmm15,%xmm15 + vpclmulqdq $0x11,%xmm6,%xmm14,%xmm1 + vmovdqu 112-64(%rsi),%xmm6 + vpxor %xmm2,%xmm5,%xmm5 + vpunpckhqdq %xmm15,%xmm15,%xmm8 + vpclmulqdq $0x00,%xmm7,%xmm9,%xmm2 + vpxor %xmm15,%xmm8,%xmm8 + + vmovdqu 16(%rdx),%xmm14 + vpxor %xmm3,%xmm0,%xmm0 + vpclmulqdq $0x00,%xmm6,%xmm15,%xmm3 + vpxor %xmm4,%xmm1,%xmm1 + vpshufb %xmm13,%xmm14,%xmm14 + vpclmulqdq $0x11,%xmm6,%xmm15,%xmm4 + vmovdqu 144-64(%rsi),%xmm6 + vpxor %xmm5,%xmm2,%xmm2 + vpunpckhqdq %xmm14,%xmm14,%xmm9 + vpclmulqdq $0x10,%xmm7,%xmm8,%xmm5 + vmovdqu 176-64(%rsi),%xmm7 + vpxor %xmm14,%xmm9,%xmm9 + + vmovdqu (%rdx),%xmm15 + vpxor %xmm0,%xmm3,%xmm3 + vpclmulqdq $0x00,%xmm6,%xmm14,%xmm0 + vpxor %xmm1,%xmm4,%xmm4 + vpshufb %xmm13,%xmm15,%xmm15 + vpclmulqdq $0x11,%xmm6,%xmm14,%xmm1 + vmovdqu 160-64(%rsi),%xmm6 + vpxor %xmm2,%xmm5,%xmm5 + vpclmulqdq $0x10,%xmm7,%xmm9,%xmm2 + + leaq 128(%rdx),%rdx + cmpq $0x80,%rcx + jb .Ltail_avx + + vpxor %xmm10,%xmm15,%xmm15 + subq $0x80,%rcx + jmp .Loop8x_avx + +.align 5, 0x90 +.Loop8x_avx: + vpunpckhqdq %xmm15,%xmm15,%xmm8 + vmovdqu 112(%rdx),%xmm14 + vpxor %xmm0,%xmm3,%xmm3 + vpxor %xmm15,%xmm8,%xmm8 + vpclmulqdq $0x00,%xmm6,%xmm15,%xmm10 + vpshufb %xmm13,%xmm14,%xmm14 + vpxor %xmm1,%xmm4,%xmm4 + vpclmulqdq $0x11,%xmm6,%xmm15,%xmm11 + vmovdqu 0-64(%rsi),%xmm6 + vpunpckhqdq %xmm14,%xmm14,%xmm9 + vpxor %xmm2,%xmm5,%xmm5 + vpclmulqdq $0x00,%xmm7,%xmm8,%xmm12 + vmovdqu 32-64(%rsi),%xmm7 + vpxor %xmm14,%xmm9,%xmm9 + + vmovdqu 96(%rdx),%xmm15 + vpclmulqdq $0x00,%xmm6,%xmm14,%xmm0 + vpxor %xmm3,%xmm10,%xmm10 + vpshufb %xmm13,%xmm15,%xmm15 + vpclmulqdq $0x11,%xmm6,%xmm14,%xmm1 + vxorps %xmm4,%xmm11,%xmm11 + vmovdqu 16-64(%rsi),%xmm6 + vpunpckhqdq %xmm15,%xmm15,%xmm8 + vpclmulqdq $0x00,%xmm7,%xmm9,%xmm2 + vpxor %xmm5,%xmm12,%xmm12 + vxorps %xmm15,%xmm8,%xmm8 + + vmovdqu 80(%rdx),%xmm14 + vpxor %xmm10,%xmm12,%xmm12 + vpclmulqdq $0x00,%xmm6,%xmm15,%xmm3 + vpxor %xmm11,%xmm12,%xmm12 + vpslldq $8,%xmm12,%xmm9 + vpxor %xmm0,%xmm3,%xmm3 + vpclmulqdq $0x11,%xmm6,%xmm15,%xmm4 + vpsrldq $8,%xmm12,%xmm12 + vpxor %xmm9,%xmm10,%xmm10 + vmovdqu 48-64(%rsi),%xmm6 + vpshufb %xmm13,%xmm14,%xmm14 + vxorps %xmm12,%xmm11,%xmm11 + vpxor %xmm1,%xmm4,%xmm4 + vpunpckhqdq %xmm14,%xmm14,%xmm9 + vpclmulqdq $0x10,%xmm7,%xmm8,%xmm5 + vmovdqu 80-64(%rsi),%xmm7 + vpxor %xmm14,%xmm9,%xmm9 + vpxor %xmm2,%xmm5,%xmm5 + + vmovdqu 64(%rdx),%xmm15 + vpalignr $8,%xmm10,%xmm10,%xmm12 + vpclmulqdq $0x00,%xmm6,%xmm14,%xmm0 + vpshufb %xmm13,%xmm15,%xmm15 + vpxor %xmm3,%xmm0,%xmm0 + vpclmulqdq $0x11,%xmm6,%xmm14,%xmm1 + vmovdqu 64-64(%rsi),%xmm6 + vpunpckhqdq %xmm15,%xmm15,%xmm8 + vpxor %xmm4,%xmm1,%xmm1 + vpclmulqdq $0x00,%xmm7,%xmm9,%xmm2 + vxorps %xmm15,%xmm8,%xmm8 + vpxor %xmm5,%xmm2,%xmm2 + + vmovdqu 48(%rdx),%xmm14 + vpclmulqdq $0x10,(%r10),%xmm10,%xmm10 + vpclmulqdq $0x00,%xmm6,%xmm15,%xmm3 + vpshufb %xmm13,%xmm14,%xmm14 + vpxor %xmm0,%xmm3,%xmm3 + vpclmulqdq $0x11,%xmm6,%xmm15,%xmm4 + vmovdqu 96-64(%rsi),%xmm6 + vpunpckhqdq %xmm14,%xmm14,%xmm9 + vpxor %xmm1,%xmm4,%xmm4 + vpclmulqdq $0x10,%xmm7,%xmm8,%xmm5 + vmovdqu 128-64(%rsi),%xmm7 + vpxor %xmm14,%xmm9,%xmm9 + vpxor %xmm2,%xmm5,%xmm5 + + vmovdqu 32(%rdx),%xmm15 + vpclmulqdq $0x00,%xmm6,%xmm14,%xmm0 + vpshufb %xmm13,%xmm15,%xmm15 + vpxor %xmm3,%xmm0,%xmm0 + vpclmulqdq $0x11,%xmm6,%xmm14,%xmm1 + vmovdqu 112-64(%rsi),%xmm6 + vpunpckhqdq %xmm15,%xmm15,%xmm8 + vpxor %xmm4,%xmm1,%xmm1 + vpclmulqdq $0x00,%xmm7,%xmm9,%xmm2 + vpxor %xmm15,%xmm8,%xmm8 + vpxor %xmm5,%xmm2,%xmm2 + vxorps %xmm12,%xmm10,%xmm10 + + vmovdqu 16(%rdx),%xmm14 + vpalignr $8,%xmm10,%xmm10,%xmm12 + vpclmulqdq $0x00,%xmm6,%xmm15,%xmm3 + vpshufb %xmm13,%xmm14,%xmm14 + vpxor %xmm0,%xmm3,%xmm3 + vpclmulqdq $0x11,%xmm6,%xmm15,%xmm4 + vmovdqu 144-64(%rsi),%xmm6 + vpclmulqdq $0x10,(%r10),%xmm10,%xmm10 + vxorps %xmm11,%xmm12,%xmm12 + vpunpckhqdq %xmm14,%xmm14,%xmm9 + vpxor %xmm1,%xmm4,%xmm4 + vpclmulqdq $0x10,%xmm7,%xmm8,%xmm5 + vmovdqu 176-64(%rsi),%xmm7 + vpxor %xmm14,%xmm9,%xmm9 + vpxor %xmm2,%xmm5,%xmm5 + + vmovdqu (%rdx),%xmm15 + vpclmulqdq $0x00,%xmm6,%xmm14,%xmm0 + vpshufb %xmm13,%xmm15,%xmm15 + vpclmulqdq $0x11,%xmm6,%xmm14,%xmm1 + vmovdqu 160-64(%rsi),%xmm6 + vpxor %xmm12,%xmm15,%xmm15 + vpclmulqdq $0x10,%xmm7,%xmm9,%xmm2 + vpxor %xmm10,%xmm15,%xmm15 + + leaq 128(%rdx),%rdx + subq $0x80,%rcx + jnc .Loop8x_avx + + addq $0x80,%rcx + jmp .Ltail_no_xor_avx + +.align 5, 0x90 +.Lshort_avx: + vmovdqu -16(%rdx,%rcx,1),%xmm14 + leaq (%rdx,%rcx,1),%rdx + vmovdqu 0-64(%rsi),%xmm6 + vmovdqu 32-64(%rsi),%xmm7 + vpshufb %xmm13,%xmm14,%xmm15 + + vmovdqa %xmm0,%xmm3 + vmovdqa %xmm1,%xmm4 + vmovdqa %xmm2,%xmm5 + subq $0x10,%rcx + jz .Ltail_avx + + vpunpckhqdq %xmm15,%xmm15,%xmm8 + vpxor %xmm0,%xmm3,%xmm3 + vpclmulqdq $0x00,%xmm6,%xmm15,%xmm0 + vpxor %xmm15,%xmm8,%xmm8 + vmovdqu -32(%rdx),%xmm14 + vpxor %xmm1,%xmm4,%xmm4 + vpclmulqdq $0x11,%xmm6,%xmm15,%xmm1 + vmovdqu 16-64(%rsi),%xmm6 + vpshufb %xmm13,%xmm14,%xmm15 + vpxor %xmm2,%xmm5,%xmm5 + vpclmulqdq $0x00,%xmm7,%xmm8,%xmm2 + vpsrldq $8,%xmm7,%xmm7 + subq $0x10,%rcx + jz .Ltail_avx + + vpunpckhqdq %xmm15,%xmm15,%xmm8 + vpxor %xmm0,%xmm3,%xmm3 + vpclmulqdq $0x00,%xmm6,%xmm15,%xmm0 + vpxor %xmm15,%xmm8,%xmm8 + vmovdqu -48(%rdx),%xmm14 + vpxor %xmm1,%xmm4,%xmm4 + vpclmulqdq $0x11,%xmm6,%xmm15,%xmm1 + vmovdqu 48-64(%rsi),%xmm6 + vpshufb %xmm13,%xmm14,%xmm15 + vpxor %xmm2,%xmm5,%xmm5 + vpclmulqdq $0x00,%xmm7,%xmm8,%xmm2 + vmovdqu 80-64(%rsi),%xmm7 + subq $0x10,%rcx + jz .Ltail_avx + + vpunpckhqdq %xmm15,%xmm15,%xmm8 + vpxor %xmm0,%xmm3,%xmm3 + vpclmulqdq $0x00,%xmm6,%xmm15,%xmm0 + vpxor %xmm15,%xmm8,%xmm8 + vmovdqu -64(%rdx),%xmm14 + vpxor %xmm1,%xmm4,%xmm4 + vpclmulqdq $0x11,%xmm6,%xmm15,%xmm1 + vmovdqu 64-64(%rsi),%xmm6 + vpshufb %xmm13,%xmm14,%xmm15 + vpxor %xmm2,%xmm5,%xmm5 + vpclmulqdq $0x00,%xmm7,%xmm8,%xmm2 + vpsrldq $8,%xmm7,%xmm7 + subq $0x10,%rcx + jz .Ltail_avx + + vpunpckhqdq %xmm15,%xmm15,%xmm8 + vpxor %xmm0,%xmm3,%xmm3 + vpclmulqdq $0x00,%xmm6,%xmm15,%xmm0 + vpxor %xmm15,%xmm8,%xmm8 + vmovdqu -80(%rdx),%xmm14 + vpxor %xmm1,%xmm4,%xmm4 + vpclmulqdq $0x11,%xmm6,%xmm15,%xmm1 + vmovdqu 96-64(%rsi),%xmm6 + vpshufb %xmm13,%xmm14,%xmm15 + vpxor %xmm2,%xmm5,%xmm5 + vpclmulqdq $0x00,%xmm7,%xmm8,%xmm2 + vmovdqu 128-64(%rsi),%xmm7 + subq $0x10,%rcx + jz .Ltail_avx + + vpunpckhqdq %xmm15,%xmm15,%xmm8 + vpxor %xmm0,%xmm3,%xmm3 + vpclmulqdq $0x00,%xmm6,%xmm15,%xmm0 + vpxor %xmm15,%xmm8,%xmm8 + vmovdqu -96(%rdx),%xmm14 + vpxor %xmm1,%xmm4,%xmm4 + vpclmulqdq $0x11,%xmm6,%xmm15,%xmm1 + vmovdqu 112-64(%rsi),%xmm6 + vpshufb %xmm13,%xmm14,%xmm15 + vpxor %xmm2,%xmm5,%xmm5 + vpclmulqdq $0x00,%xmm7,%xmm8,%xmm2 + vpsrldq $8,%xmm7,%xmm7 + subq $0x10,%rcx + jz .Ltail_avx + + vpunpckhqdq %xmm15,%xmm15,%xmm8 + vpxor %xmm0,%xmm3,%xmm3 + vpclmulqdq $0x00,%xmm6,%xmm15,%xmm0 + vpxor %xmm15,%xmm8,%xmm8 + vmovdqu -112(%rdx),%xmm14 + vpxor %xmm1,%xmm4,%xmm4 + vpclmulqdq $0x11,%xmm6,%xmm15,%xmm1 + vmovdqu 144-64(%rsi),%xmm6 + vpshufb %xmm13,%xmm14,%xmm15 + vpxor %xmm2,%xmm5,%xmm5 + vpclmulqdq $0x00,%xmm7,%xmm8,%xmm2 + vmovq 184-64(%rsi),%xmm7 + subq $0x10,%rcx + jmp .Ltail_avx + +.align 5, 0x90 +.Ltail_avx: + vpxor %xmm10,%xmm15,%xmm15 +.Ltail_no_xor_avx: + vpunpckhqdq %xmm15,%xmm15,%xmm8 + vpxor %xmm0,%xmm3,%xmm3 + vpclmulqdq $0x00,%xmm6,%xmm15,%xmm0 + vpxor %xmm15,%xmm8,%xmm8 + vpxor %xmm1,%xmm4,%xmm4 + vpclmulqdq $0x11,%xmm6,%xmm15,%xmm1 + vpxor %xmm2,%xmm5,%xmm5 + vpclmulqdq $0x00,%xmm7,%xmm8,%xmm2 + + vmovdqu (%r10),%xmm12 + + vpxor %xmm0,%xmm3,%xmm10 + vpxor %xmm1,%xmm4,%xmm11 + vpxor %xmm2,%xmm5,%xmm5 + + vpxor %xmm10,%xmm5,%xmm5 + vpxor %xmm11,%xmm5,%xmm5 + vpslldq $8,%xmm5,%xmm9 + vpsrldq $8,%xmm5,%xmm5 + vpxor %xmm9,%xmm10,%xmm10 + vpxor %xmm5,%xmm11,%xmm11 + + vpclmulqdq $0x10,%xmm12,%xmm10,%xmm9 + vpalignr $8,%xmm10,%xmm10,%xmm10 + vpxor %xmm9,%xmm10,%xmm10 + + vpclmulqdq $0x10,%xmm12,%xmm10,%xmm9 + vpalignr $8,%xmm10,%xmm10,%xmm10 + vpxor %xmm11,%xmm10,%xmm10 + vpxor %xmm9,%xmm10,%xmm10 + + cmpq $0,%rcx + jne .Lshort_avx + + vpshufb %xmm13,%xmm10,%xmm10 + vmovdqu %xmm10,(%rdi) + vzeroupper + .byte 0xf3,0xc3 +.cfi_endproc +#.size gcm_ghash_avx,.-gcm_ghash_avx +.align 6, 0x90 +.Lbswap_mask: +.byte 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0 +.L0x1c2_polynomial: +.byte 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xc2 +.L7_mask: +.long 7,0,7,0 +.L7_mask_poly: +.long 7,0,450,0 +.align 6, 0x90 +#.type .Lrem_4bit,@object +.Lrem_4bit: +.long 0,0,0,471859200,0,943718400,0,610271232 +.long 0,1887436800,0,1822425088,0,1220542464,0,1423966208 +.long 0,3774873600,0,4246732800,0,3644850176,0,3311403008 +.long 0,2441084928,0,2376073216,0,2847932416,0,3051356160 +#.type .Lrem_8bit,@object +.Lrem_8bit: +.value 0x0000,0x01C2,0x0384,0x0246,0x0708,0x06CA,0x048C,0x054E +.value 0x0E10,0x0FD2,0x0D94,0x0C56,0x0918,0x08DA,0x0A9C,0x0B5E +.value 0x1C20,0x1DE2,0x1FA4,0x1E66,0x1B28,0x1AEA,0x18AC,0x196E +.value 0x1230,0x13F2,0x11B4,0x1076,0x1538,0x14FA,0x16BC,0x177E +.value 0x3840,0x3982,0x3BC4,0x3A06,0x3F48,0x3E8A,0x3CCC,0x3D0E +.value 0x3650,0x3792,0x35D4,0x3416,0x3158,0x309A,0x32DC,0x331E +.value 0x2460,0x25A2,0x27E4,0x2626,0x2368,0x22AA,0x20EC,0x212E +.value 0x2A70,0x2BB2,0x29F4,0x2836,0x2D78,0x2CBA,0x2EFC,0x2F3E +.value 0x7080,0x7142,0x7304,0x72C6,0x7788,0x764A,0x740C,0x75CE +.value 0x7E90,0x7F52,0x7D14,0x7CD6,0x7998,0x785A,0x7A1C,0x7BDE +.value 0x6CA0,0x6D62,0x6F24,0x6EE6,0x6BA8,0x6A6A,0x682C,0x69EE +.value 0x62B0,0x6372,0x6134,0x60F6,0x65B8,0x647A,0x663C,0x67FE +.value 0x48C0,0x4902,0x4B44,0x4A86,0x4FC8,0x4E0A,0x4C4C,0x4D8E +.value 0x46D0,0x4712,0x4554,0x4496,0x41D8,0x401A,0x425C,0x439E +.value 0x54E0,0x5522,0x5764,0x56A6,0x53E8,0x522A,0x506C,0x51AE +.value 0x5AF0,0x5B32,0x5974,0x58B6,0x5DF8,0x5C3A,0x5E7C,0x5FBE +.value 0xE100,0xE0C2,0xE284,0xE346,0xE608,0xE7CA,0xE58C,0xE44E +.value 0xEF10,0xEED2,0xEC94,0xED56,0xE818,0xE9DA,0xEB9C,0xEA5E +.value 0xFD20,0xFCE2,0xFEA4,0xFF66,0xFA28,0xFBEA,0xF9AC,0xF86E +.value 0xF330,0xF2F2,0xF0B4,0xF176,0xF438,0xF5FA,0xF7BC,0xF67E +.value 0xD940,0xD882,0xDAC4,0xDB06,0xDE48,0xDF8A,0xDDCC,0xDC0E +.value 0xD750,0xD692,0xD4D4,0xD516,0xD058,0xD19A,0xD3DC,0xD21E +.value 0xC560,0xC4A2,0xC6E4,0xC726,0xC268,0xC3AA,0xC1EC,0xC02E +.value 0xCB70,0xCAB2,0xC8F4,0xC936,0xCC78,0xCDBA,0xCFFC,0xCE3E +.value 0x9180,0x9042,0x9204,0x93C6,0x9688,0x974A,0x950C,0x94CE +.value 0x9F90,0x9E52,0x9C14,0x9DD6,0x9898,0x995A,0x9B1C,0x9ADE +.value 0x8DA0,0x8C62,0x8E24,0x8FE6,0x8AA8,0x8B6A,0x892C,0x88EE +.value 0x83B0,0x8272,0x8034,0x81F6,0x84B8,0x857A,0x873C,0x86FE +.value 0xA9C0,0xA802,0xAA44,0xAB86,0xAEC8,0xAF0A,0xAD4C,0xAC8E +.value 0xA7D0,0xA612,0xA454,0xA596,0xA0D8,0xA11A,0xA35C,0xA29E +.value 0xB5E0,0xB422,0xB664,0xB7A6,0xB2E8,0xB32A,0xB16C,0xB0AE +.value 0xBBF0,0xBA32,0xB874,0xB9B6,0xBCF8,0xBD3A,0xBF7C,0xBEBE + +.byte 71,72,65,83,72,32,102,111,114,32,120,56,54,95,54,52,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0 +.align 6, 0x90 + +/* Mark the stack non-executable. */ +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif + +#endif /* defined(__x86_64__) && defined(HAVE_AVX) && defined(HAVE_AES) ... */ diff --git a/module/icp/asm-x86_64/os/macos/sha1/sha1-x86_64.S b/module/icp/asm-x86_64/os/macos/sha1/sha1-x86_64.S new file mode 100644 index 000000000000..cb923784a730 --- /dev/null +++ b/module/icp/asm-x86_64/os/macos/sha1/sha1-x86_64.S @@ -0,0 +1,1353 @@ +/* + * !/usr/bin/env perl + * + * ==================================================================== + * Written by Andy Polyakov for the OpenSSL + * project. The module is, however, dual licensed under OpenSSL and + * CRYPTOGAMS licenses depending on where you obtain it. For further + * details see http://www.openssl.org/~appro/cryptogams/. + * ==================================================================== + * + * sha1_block procedure for x86_64. + * + * It was brought to my attention that on EM64T compiler-generated code + * was far behind 32-bit assembler implementation. This is unlike on + * Opteron where compiler-generated code was only 15% behind 32-bit + * assembler, which originally made it hard to motivate the effort. + * There was suggestion to mechanically translate 32-bit code, but I + * dismissed it, reasoning that x86_64 offers enough register bank + * capacity to fully utilize SHA-1 parallelism. Therefore this fresh + * implementation:-) However! While 64-bit code does performs better + * on Opteron, I failed to beat 32-bit assembler on EM64T core. Well, + * x86_64 does offer larger *addressable* bank, but out-of-order core + * reaches for even more registers through dynamic aliasing, and EM64T + * core must have managed to run-time optimize even 32-bit code just as + * good as 64-bit one. Performance improvement is summarized in the + * following table: + * + * gcc 3.4 32-bit asm cycles/byte + * Opteron +45% +20% 6.8 + * Xeon P4 +65% +0% 9.9 + * Core2 +60% +10% 7.0 + * + * + * OpenSolaris OS modifications + * + * Sun elects to use this software under the BSD license. + * + * This source originates from OpenSSL file sha1-x86_64.pl at + * ftp://ftp.openssl.org/snapshot/openssl-0.9.8-stable-SNAP-20080131.tar.gz + * (presumably for future OpenSSL release 0.9.8h), with these changes: + * + * 1. Added perl "use strict" and declared variables. + * + * 2. Added OpenSolaris ENTRY_NP/SET_SIZE macros from + * /usr/include/sys/asm_linkage.h, .ident keywords, and lint(1B) guards. + * + * 3. Removed x86_64-xlate.pl script (not needed for as(1) or gas(1) + * assemblers). + * + */ + +/* + * This file was generated by a perl script (sha1-x86_64.pl). The comments from + * the original file have been pasted above. + */ + +#if defined(lint) || defined(__lint) +#include +#include + + +/* ARGSUSED */ +void +sha1_block_data_order(SHA1_CTX *ctx, const void *inpp, size_t blocks) +{ +} + +#else +#define _ASM +#include +ENTRY_NP(sha1_block_data_order) + push %rbx + push %rbp + push %r12 + mov %rsp,%rax + mov %rdi,%r8 # reassigned argument + sub $72,%rsp + mov %rsi,%r9 # reassigned argument + and $-64,%rsp + mov %rdx,%r10 # reassigned argument + mov %rax,64(%rsp) + + mov 0(%r8),%edx + mov 4(%r8),%esi + mov 8(%r8),%edi + mov 12(%r8),%ebp + mov 16(%r8),%r11d +.align 4 +.Lloop: + mov 0(%r9),%eax + bswap %eax + mov %eax,0(%rsp) + lea 0x5a827999(%eax,%r11d),%r12d + mov %edi,%ebx + mov 4(%r9),%eax + mov %edx,%r11d + xor %ebp,%ebx + bswap %eax + rol $5,%r11d + and %esi,%ebx + mov %eax,4(%rsp) + add %r11d,%r12d + xor %ebp,%ebx + rol $30,%esi + add %ebx,%r12d + lea 0x5a827999(%eax,%ebp),%r11d + mov %esi,%ebx + mov 8(%r9),%eax + mov %r12d,%ebp + xor %edi,%ebx + bswap %eax + rol $5,%ebp + and %edx,%ebx + mov %eax,8(%rsp) + add %ebp,%r11d + xor %edi,%ebx + rol $30,%edx + add %ebx,%r11d + lea 0x5a827999(%eax,%edi),%ebp + mov %edx,%ebx + mov 12(%r9),%eax + mov %r11d,%edi + xor %esi,%ebx + bswap %eax + rol $5,%edi + and %r12d,%ebx + mov %eax,12(%rsp) + add %edi,%ebp + xor %esi,%ebx + rol $30,%r12d + add %ebx,%ebp + lea 0x5a827999(%eax,%esi),%edi + mov %r12d,%ebx + mov 16(%r9),%eax + mov %ebp,%esi + xor %edx,%ebx + bswap %eax + rol $5,%esi + and %r11d,%ebx + mov %eax,16(%rsp) + add %esi,%edi + xor %edx,%ebx + rol $30,%r11d + add %ebx,%edi + lea 0x5a827999(%eax,%edx),%esi + mov %r11d,%ebx + mov 20(%r9),%eax + mov %edi,%edx + xor %r12d,%ebx + bswap %eax + rol $5,%edx + and %ebp,%ebx + mov %eax,20(%rsp) + add %edx,%esi + xor %r12d,%ebx + rol $30,%ebp + add %ebx,%esi + lea 0x5a827999(%eax,%r12d),%edx + mov %ebp,%ebx + mov 24(%r9),%eax + mov %esi,%r12d + xor %r11d,%ebx + bswap %eax + rol $5,%r12d + and %edi,%ebx + mov %eax,24(%rsp) + add %r12d,%edx + xor %r11d,%ebx + rol $30,%edi + add %ebx,%edx + lea 0x5a827999(%eax,%r11d),%r12d + mov %edi,%ebx + mov 28(%r9),%eax + mov %edx,%r11d + xor %ebp,%ebx + bswap %eax + rol $5,%r11d + and %esi,%ebx + mov %eax,28(%rsp) + add %r11d,%r12d + xor %ebp,%ebx + rol $30,%esi + add %ebx,%r12d + lea 0x5a827999(%eax,%ebp),%r11d + mov %esi,%ebx + mov 32(%r9),%eax + mov %r12d,%ebp + xor %edi,%ebx + bswap %eax + rol $5,%ebp + and %edx,%ebx + mov %eax,32(%rsp) + add %ebp,%r11d + xor %edi,%ebx + rol $30,%edx + add %ebx,%r11d + lea 0x5a827999(%eax,%edi),%ebp + mov %edx,%ebx + mov 36(%r9),%eax + mov %r11d,%edi + xor %esi,%ebx + bswap %eax + rol $5,%edi + and %r12d,%ebx + mov %eax,36(%rsp) + add %edi,%ebp + xor %esi,%ebx + rol $30,%r12d + add %ebx,%ebp + lea 0x5a827999(%eax,%esi),%edi + mov %r12d,%ebx + mov 40(%r9),%eax + mov %ebp,%esi + xor %edx,%ebx + bswap %eax + rol $5,%esi + and %r11d,%ebx + mov %eax,40(%rsp) + add %esi,%edi + xor %edx,%ebx + rol $30,%r11d + add %ebx,%edi + lea 0x5a827999(%eax,%edx),%esi + mov %r11d,%ebx + mov 44(%r9),%eax + mov %edi,%edx + xor %r12d,%ebx + bswap %eax + rol $5,%edx + and %ebp,%ebx + mov %eax,44(%rsp) + add %edx,%esi + xor %r12d,%ebx + rol $30,%ebp + add %ebx,%esi + lea 0x5a827999(%eax,%r12d),%edx + mov %ebp,%ebx + mov 48(%r9),%eax + mov %esi,%r12d + xor %r11d,%ebx + bswap %eax + rol $5,%r12d + and %edi,%ebx + mov %eax,48(%rsp) + add %r12d,%edx + xor %r11d,%ebx + rol $30,%edi + add %ebx,%edx + lea 0x5a827999(%eax,%r11d),%r12d + mov %edi,%ebx + mov 52(%r9),%eax + mov %edx,%r11d + xor %ebp,%ebx + bswap %eax + rol $5,%r11d + and %esi,%ebx + mov %eax,52(%rsp) + add %r11d,%r12d + xor %ebp,%ebx + rol $30,%esi + add %ebx,%r12d + lea 0x5a827999(%eax,%ebp),%r11d + mov %esi,%ebx + mov 56(%r9),%eax + mov %r12d,%ebp + xor %edi,%ebx + bswap %eax + rol $5,%ebp + and %edx,%ebx + mov %eax,56(%rsp) + add %ebp,%r11d + xor %edi,%ebx + rol $30,%edx + add %ebx,%r11d + lea 0x5a827999(%eax,%edi),%ebp + mov %edx,%ebx + mov 60(%r9),%eax + mov %r11d,%edi + xor %esi,%ebx + bswap %eax + rol $5,%edi + and %r12d,%ebx + mov %eax,60(%rsp) + add %edi,%ebp + xor %esi,%ebx + rol $30,%r12d + add %ebx,%ebp + lea 0x5a827999(%eax,%esi),%edi + mov 0(%rsp),%eax + mov %r12d,%ebx + mov %ebp,%esi + xor 8(%rsp),%eax + xor %edx,%ebx + rol $5,%esi + xor 32(%rsp),%eax + and %r11d,%ebx + add %esi,%edi + xor 52(%rsp),%eax + xor %edx,%ebx + rol $30,%r11d + add %ebx,%edi + rol $1,%eax + mov %eax,0(%rsp) + lea 0x5a827999(%eax,%edx),%esi + mov 4(%rsp),%eax + mov %r11d,%ebx + mov %edi,%edx + xor 12(%rsp),%eax + xor %r12d,%ebx + rol $5,%edx + xor 36(%rsp),%eax + and %ebp,%ebx + add %edx,%esi + xor 56(%rsp),%eax + xor %r12d,%ebx + rol $30,%ebp + add %ebx,%esi + rol $1,%eax + mov %eax,4(%rsp) + lea 0x5a827999(%eax,%r12d),%edx + mov 8(%rsp),%eax + mov %ebp,%ebx + mov %esi,%r12d + xor 16(%rsp),%eax + xor %r11d,%ebx + rol $5,%r12d + xor 40(%rsp),%eax + and %edi,%ebx + add %r12d,%edx + xor 60(%rsp),%eax + xor %r11d,%ebx + rol $30,%edi + add %ebx,%edx + rol $1,%eax + mov %eax,8(%rsp) + lea 0x5a827999(%eax,%r11d),%r12d + mov 12(%rsp),%eax + mov %edi,%ebx + mov %edx,%r11d + xor 20(%rsp),%eax + xor %ebp,%ebx + rol $5,%r11d + xor 44(%rsp),%eax + and %esi,%ebx + add %r11d,%r12d + xor 0(%rsp),%eax + xor %ebp,%ebx + rol $30,%esi + add %ebx,%r12d + rol $1,%eax + mov %eax,12(%rsp) + lea 0x5a827999(%eax,%ebp),%r11d + mov 16(%rsp),%eax + mov %esi,%ebx + mov %r12d,%ebp + xor 24(%rsp),%eax + xor %edi,%ebx + rol $5,%ebp + xor 48(%rsp),%eax + and %edx,%ebx + add %ebp,%r11d + xor 4(%rsp),%eax + xor %edi,%ebx + rol $30,%edx + add %ebx,%r11d + rol $1,%eax + mov %eax,16(%rsp) + lea 0x6ed9eba1(%eax,%edi),%ebp + mov 20(%rsp),%eax + mov %edx,%ebx + mov %r11d,%edi + xor 28(%rsp),%eax + xor %r12d,%ebx + rol $5,%edi + xor 52(%rsp),%eax + xor %esi,%ebx + add %edi,%ebp + xor 8(%rsp),%eax + rol $30,%r12d + add %ebx,%ebp + rol $1,%eax + mov %eax,20(%rsp) + lea 0x6ed9eba1(%eax,%esi),%edi + mov 24(%rsp),%eax + mov %r12d,%ebx + mov %ebp,%esi + xor 32(%rsp),%eax + xor %r11d,%ebx + rol $5,%esi + xor 56(%rsp),%eax + xor %edx,%ebx + add %esi,%edi + xor 12(%rsp),%eax + rol $30,%r11d + add %ebx,%edi + rol $1,%eax + mov %eax,24(%rsp) + lea 0x6ed9eba1(%eax,%edx),%esi + mov 28(%rsp),%eax + mov %r11d,%ebx + mov %edi,%edx + xor 36(%rsp),%eax + xor %ebp,%ebx + rol $5,%edx + xor 60(%rsp),%eax + xor %r12d,%ebx + add %edx,%esi + xor 16(%rsp),%eax + rol $30,%ebp + add %ebx,%esi + rol $1,%eax + mov %eax,28(%rsp) + lea 0x6ed9eba1(%eax,%r12d),%edx + mov 32(%rsp),%eax + mov %ebp,%ebx + mov %esi,%r12d + xor 40(%rsp),%eax + xor %edi,%ebx + rol $5,%r12d + xor 0(%rsp),%eax + xor %r11d,%ebx + add %r12d,%edx + xor 20(%rsp),%eax + rol $30,%edi + add %ebx,%edx + rol $1,%eax + mov %eax,32(%rsp) + lea 0x6ed9eba1(%eax,%r11d),%r12d + mov 36(%rsp),%eax + mov %edi,%ebx + mov %edx,%r11d + xor 44(%rsp),%eax + xor %esi,%ebx + rol $5,%r11d + xor 4(%rsp),%eax + xor %ebp,%ebx + add %r11d,%r12d + xor 24(%rsp),%eax + rol $30,%esi + add %ebx,%r12d + rol $1,%eax + mov %eax,36(%rsp) + lea 0x6ed9eba1(%eax,%ebp),%r11d + mov 40(%rsp),%eax + mov %esi,%ebx + mov %r12d,%ebp + xor 48(%rsp),%eax + xor %edx,%ebx + rol $5,%ebp + xor 8(%rsp),%eax + xor %edi,%ebx + add %ebp,%r11d + xor 28(%rsp),%eax + rol $30,%edx + add %ebx,%r11d + rol $1,%eax + mov %eax,40(%rsp) + lea 0x6ed9eba1(%eax,%edi),%ebp + mov 44(%rsp),%eax + mov %edx,%ebx + mov %r11d,%edi + xor 52(%rsp),%eax + xor %r12d,%ebx + rol $5,%edi + xor 12(%rsp),%eax + xor %esi,%ebx + add %edi,%ebp + xor 32(%rsp),%eax + rol $30,%r12d + add %ebx,%ebp + rol $1,%eax + mov %eax,44(%rsp) + lea 0x6ed9eba1(%eax,%esi),%edi + mov 48(%rsp),%eax + mov %r12d,%ebx + mov %ebp,%esi + xor 56(%rsp),%eax + xor %r11d,%ebx + rol $5,%esi + xor 16(%rsp),%eax + xor %edx,%ebx + add %esi,%edi + xor 36(%rsp),%eax + rol $30,%r11d + add %ebx,%edi + rol $1,%eax + mov %eax,48(%rsp) + lea 0x6ed9eba1(%eax,%edx),%esi + mov 52(%rsp),%eax + mov %r11d,%ebx + mov %edi,%edx + xor 60(%rsp),%eax + xor %ebp,%ebx + rol $5,%edx + xor 20(%rsp),%eax + xor %r12d,%ebx + add %edx,%esi + xor 40(%rsp),%eax + rol $30,%ebp + add %ebx,%esi + rol $1,%eax + mov %eax,52(%rsp) + lea 0x6ed9eba1(%eax,%r12d),%edx + mov 56(%rsp),%eax + mov %ebp,%ebx + mov %esi,%r12d + xor 0(%rsp),%eax + xor %edi,%ebx + rol $5,%r12d + xor 24(%rsp),%eax + xor %r11d,%ebx + add %r12d,%edx + xor 44(%rsp),%eax + rol $30,%edi + add %ebx,%edx + rol $1,%eax + mov %eax,56(%rsp) + lea 0x6ed9eba1(%eax,%r11d),%r12d + mov 60(%rsp),%eax + mov %edi,%ebx + mov %edx,%r11d + xor 4(%rsp),%eax + xor %esi,%ebx + rol $5,%r11d + xor 28(%rsp),%eax + xor %ebp,%ebx + add %r11d,%r12d + xor 48(%rsp),%eax + rol $30,%esi + add %ebx,%r12d + rol $1,%eax + mov %eax,60(%rsp) + lea 0x6ed9eba1(%eax,%ebp),%r11d + mov 0(%rsp),%eax + mov %esi,%ebx + mov %r12d,%ebp + xor 8(%rsp),%eax + xor %edx,%ebx + rol $5,%ebp + xor 32(%rsp),%eax + xor %edi,%ebx + add %ebp,%r11d + xor 52(%rsp),%eax + rol $30,%edx + add %ebx,%r11d + rol $1,%eax + mov %eax,0(%rsp) + lea 0x6ed9eba1(%eax,%edi),%ebp + mov 4(%rsp),%eax + mov %edx,%ebx + mov %r11d,%edi + xor 12(%rsp),%eax + xor %r12d,%ebx + rol $5,%edi + xor 36(%rsp),%eax + xor %esi,%ebx + add %edi,%ebp + xor 56(%rsp),%eax + rol $30,%r12d + add %ebx,%ebp + rol $1,%eax + mov %eax,4(%rsp) + lea 0x6ed9eba1(%eax,%esi),%edi + mov 8(%rsp),%eax + mov %r12d,%ebx + mov %ebp,%esi + xor 16(%rsp),%eax + xor %r11d,%ebx + rol $5,%esi + xor 40(%rsp),%eax + xor %edx,%ebx + add %esi,%edi + xor 60(%rsp),%eax + rol $30,%r11d + add %ebx,%edi + rol $1,%eax + mov %eax,8(%rsp) + lea 0x6ed9eba1(%eax,%edx),%esi + mov 12(%rsp),%eax + mov %r11d,%ebx + mov %edi,%edx + xor 20(%rsp),%eax + xor %ebp,%ebx + rol $5,%edx + xor 44(%rsp),%eax + xor %r12d,%ebx + add %edx,%esi + xor 0(%rsp),%eax + rol $30,%ebp + add %ebx,%esi + rol $1,%eax + mov %eax,12(%rsp) + lea 0x6ed9eba1(%eax,%r12d),%edx + mov 16(%rsp),%eax + mov %ebp,%ebx + mov %esi,%r12d + xor 24(%rsp),%eax + xor %edi,%ebx + rol $5,%r12d + xor 48(%rsp),%eax + xor %r11d,%ebx + add %r12d,%edx + xor 4(%rsp),%eax + rol $30,%edi + add %ebx,%edx + rol $1,%eax + mov %eax,16(%rsp) + lea 0x6ed9eba1(%eax,%r11d),%r12d + mov 20(%rsp),%eax + mov %edi,%ebx + mov %edx,%r11d + xor 28(%rsp),%eax + xor %esi,%ebx + rol $5,%r11d + xor 52(%rsp),%eax + xor %ebp,%ebx + add %r11d,%r12d + xor 8(%rsp),%eax + rol $30,%esi + add %ebx,%r12d + rol $1,%eax + mov %eax,20(%rsp) + lea 0x6ed9eba1(%eax,%ebp),%r11d + mov 24(%rsp),%eax + mov %esi,%ebx + mov %r12d,%ebp + xor 32(%rsp),%eax + xor %edx,%ebx + rol $5,%ebp + xor 56(%rsp),%eax + xor %edi,%ebx + add %ebp,%r11d + xor 12(%rsp),%eax + rol $30,%edx + add %ebx,%r11d + rol $1,%eax + mov %eax,24(%rsp) + lea 0x6ed9eba1(%eax,%edi),%ebp + mov 28(%rsp),%eax + mov %edx,%ebx + mov %r11d,%edi + xor 36(%rsp),%eax + xor %r12d,%ebx + rol $5,%edi + xor 60(%rsp),%eax + xor %esi,%ebx + add %edi,%ebp + xor 16(%rsp),%eax + rol $30,%r12d + add %ebx,%ebp + rol $1,%eax + mov %eax,28(%rsp) + lea 0x6ed9eba1(%eax,%esi),%edi + mov 32(%rsp),%eax + mov %r12d,%ebx + mov %ebp,%esi + xor 40(%rsp),%eax + xor %r11d,%ebx + rol $5,%esi + xor 0(%rsp),%eax + xor %edx,%ebx + add %esi,%edi + xor 20(%rsp),%eax + rol $30,%r11d + add %ebx,%edi + rol $1,%eax + mov %eax,32(%rsp) + lea -0x70e44324(%eax,%edx),%esi + mov 36(%rsp),%eax + mov %ebp,%ebx + mov %ebp,%ecx + xor 44(%rsp),%eax + mov %edi,%edx + and %r11d,%ebx + xor 4(%rsp),%eax + or %r11d,%ecx + rol $5,%edx + xor 24(%rsp),%eax + and %r12d,%ecx + add %edx,%esi + rol $1,%eax + or %ecx,%ebx + rol $30,%ebp + mov %eax,36(%rsp) + add %ebx,%esi + lea -0x70e44324(%eax,%r12d),%edx + mov 40(%rsp),%eax + mov %edi,%ebx + mov %edi,%ecx + xor 48(%rsp),%eax + mov %esi,%r12d + and %ebp,%ebx + xor 8(%rsp),%eax + or %ebp,%ecx + rol $5,%r12d + xor 28(%rsp),%eax + and %r11d,%ecx + add %r12d,%edx + rol $1,%eax + or %ecx,%ebx + rol $30,%edi + mov %eax,40(%rsp) + add %ebx,%edx + lea -0x70e44324(%eax,%r11d),%r12d + mov 44(%rsp),%eax + mov %esi,%ebx + mov %esi,%ecx + xor 52(%rsp),%eax + mov %edx,%r11d + and %edi,%ebx + xor 12(%rsp),%eax + or %edi,%ecx + rol $5,%r11d + xor 32(%rsp),%eax + and %ebp,%ecx + add %r11d,%r12d + rol $1,%eax + or %ecx,%ebx + rol $30,%esi + mov %eax,44(%rsp) + add %ebx,%r12d + lea -0x70e44324(%eax,%ebp),%r11d + mov 48(%rsp),%eax + mov %edx,%ebx + mov %edx,%ecx + xor 56(%rsp),%eax + mov %r12d,%ebp + and %esi,%ebx + xor 16(%rsp),%eax + or %esi,%ecx + rol $5,%ebp + xor 36(%rsp),%eax + and %edi,%ecx + add %ebp,%r11d + rol $1,%eax + or %ecx,%ebx + rol $30,%edx + mov %eax,48(%rsp) + add %ebx,%r11d + lea -0x70e44324(%eax,%edi),%ebp + mov 52(%rsp),%eax + mov %r12d,%ebx + mov %r12d,%ecx + xor 60(%rsp),%eax + mov %r11d,%edi + and %edx,%ebx + xor 20(%rsp),%eax + or %edx,%ecx + rol $5,%edi + xor 40(%rsp),%eax + and %esi,%ecx + add %edi,%ebp + rol $1,%eax + or %ecx,%ebx + rol $30,%r12d + mov %eax,52(%rsp) + add %ebx,%ebp + lea -0x70e44324(%eax,%esi),%edi + mov 56(%rsp),%eax + mov %r11d,%ebx + mov %r11d,%ecx + xor 0(%rsp),%eax + mov %ebp,%esi + and %r12d,%ebx + xor 24(%rsp),%eax + or %r12d,%ecx + rol $5,%esi + xor 44(%rsp),%eax + and %edx,%ecx + add %esi,%edi + rol $1,%eax + or %ecx,%ebx + rol $30,%r11d + mov %eax,56(%rsp) + add %ebx,%edi + lea -0x70e44324(%eax,%edx),%esi + mov 60(%rsp),%eax + mov %ebp,%ebx + mov %ebp,%ecx + xor 4(%rsp),%eax + mov %edi,%edx + and %r11d,%ebx + xor 28(%rsp),%eax + or %r11d,%ecx + rol $5,%edx + xor 48(%rsp),%eax + and %r12d,%ecx + add %edx,%esi + rol $1,%eax + or %ecx,%ebx + rol $30,%ebp + mov %eax,60(%rsp) + add %ebx,%esi + lea -0x70e44324(%eax,%r12d),%edx + mov 0(%rsp),%eax + mov %edi,%ebx + mov %edi,%ecx + xor 8(%rsp),%eax + mov %esi,%r12d + and %ebp,%ebx + xor 32(%rsp),%eax + or %ebp,%ecx + rol $5,%r12d + xor 52(%rsp),%eax + and %r11d,%ecx + add %r12d,%edx + rol $1,%eax + or %ecx,%ebx + rol $30,%edi + mov %eax,0(%rsp) + add %ebx,%edx + lea -0x70e44324(%eax,%r11d),%r12d + mov 4(%rsp),%eax + mov %esi,%ebx + mov %esi,%ecx + xor 12(%rsp),%eax + mov %edx,%r11d + and %edi,%ebx + xor 36(%rsp),%eax + or %edi,%ecx + rol $5,%r11d + xor 56(%rsp),%eax + and %ebp,%ecx + add %r11d,%r12d + rol $1,%eax + or %ecx,%ebx + rol $30,%esi + mov %eax,4(%rsp) + add %ebx,%r12d + lea -0x70e44324(%eax,%ebp),%r11d + mov 8(%rsp),%eax + mov %edx,%ebx + mov %edx,%ecx + xor 16(%rsp),%eax + mov %r12d,%ebp + and %esi,%ebx + xor 40(%rsp),%eax + or %esi,%ecx + rol $5,%ebp + xor 60(%rsp),%eax + and %edi,%ecx + add %ebp,%r11d + rol $1,%eax + or %ecx,%ebx + rol $30,%edx + mov %eax,8(%rsp) + add %ebx,%r11d + lea -0x70e44324(%eax,%edi),%ebp + mov 12(%rsp),%eax + mov %r12d,%ebx + mov %r12d,%ecx + xor 20(%rsp),%eax + mov %r11d,%edi + and %edx,%ebx + xor 44(%rsp),%eax + or %edx,%ecx + rol $5,%edi + xor 0(%rsp),%eax + and %esi,%ecx + add %edi,%ebp + rol $1,%eax + or %ecx,%ebx + rol $30,%r12d + mov %eax,12(%rsp) + add %ebx,%ebp + lea -0x70e44324(%eax,%esi),%edi + mov 16(%rsp),%eax + mov %r11d,%ebx + mov %r11d,%ecx + xor 24(%rsp),%eax + mov %ebp,%esi + and %r12d,%ebx + xor 48(%rsp),%eax + or %r12d,%ecx + rol $5,%esi + xor 4(%rsp),%eax + and %edx,%ecx + add %esi,%edi + rol $1,%eax + or %ecx,%ebx + rol $30,%r11d + mov %eax,16(%rsp) + add %ebx,%edi + lea -0x70e44324(%eax,%edx),%esi + mov 20(%rsp),%eax + mov %ebp,%ebx + mov %ebp,%ecx + xor 28(%rsp),%eax + mov %edi,%edx + and %r11d,%ebx + xor 52(%rsp),%eax + or %r11d,%ecx + rol $5,%edx + xor 8(%rsp),%eax + and %r12d,%ecx + add %edx,%esi + rol $1,%eax + or %ecx,%ebx + rol $30,%ebp + mov %eax,20(%rsp) + add %ebx,%esi + lea -0x70e44324(%eax,%r12d),%edx + mov 24(%rsp),%eax + mov %edi,%ebx + mov %edi,%ecx + xor 32(%rsp),%eax + mov %esi,%r12d + and %ebp,%ebx + xor 56(%rsp),%eax + or %ebp,%ecx + rol $5,%r12d + xor 12(%rsp),%eax + and %r11d,%ecx + add %r12d,%edx + rol $1,%eax + or %ecx,%ebx + rol $30,%edi + mov %eax,24(%rsp) + add %ebx,%edx + lea -0x70e44324(%eax,%r11d),%r12d + mov 28(%rsp),%eax + mov %esi,%ebx + mov %esi,%ecx + xor 36(%rsp),%eax + mov %edx,%r11d + and %edi,%ebx + xor 60(%rsp),%eax + or %edi,%ecx + rol $5,%r11d + xor 16(%rsp),%eax + and %ebp,%ecx + add %r11d,%r12d + rol $1,%eax + or %ecx,%ebx + rol $30,%esi + mov %eax,28(%rsp) + add %ebx,%r12d + lea -0x70e44324(%eax,%ebp),%r11d + mov 32(%rsp),%eax + mov %edx,%ebx + mov %edx,%ecx + xor 40(%rsp),%eax + mov %r12d,%ebp + and %esi,%ebx + xor 0(%rsp),%eax + or %esi,%ecx + rol $5,%ebp + xor 20(%rsp),%eax + and %edi,%ecx + add %ebp,%r11d + rol $1,%eax + or %ecx,%ebx + rol $30,%edx + mov %eax,32(%rsp) + add %ebx,%r11d + lea -0x70e44324(%eax,%edi),%ebp + mov 36(%rsp),%eax + mov %r12d,%ebx + mov %r12d,%ecx + xor 44(%rsp),%eax + mov %r11d,%edi + and %edx,%ebx + xor 4(%rsp),%eax + or %edx,%ecx + rol $5,%edi + xor 24(%rsp),%eax + and %esi,%ecx + add %edi,%ebp + rol $1,%eax + or %ecx,%ebx + rol $30,%r12d + mov %eax,36(%rsp) + add %ebx,%ebp + lea -0x70e44324(%eax,%esi),%edi + mov 40(%rsp),%eax + mov %r11d,%ebx + mov %r11d,%ecx + xor 48(%rsp),%eax + mov %ebp,%esi + and %r12d,%ebx + xor 8(%rsp),%eax + or %r12d,%ecx + rol $5,%esi + xor 28(%rsp),%eax + and %edx,%ecx + add %esi,%edi + rol $1,%eax + or %ecx,%ebx + rol $30,%r11d + mov %eax,40(%rsp) + add %ebx,%edi + lea -0x70e44324(%eax,%edx),%esi + mov 44(%rsp),%eax + mov %ebp,%ebx + mov %ebp,%ecx + xor 52(%rsp),%eax + mov %edi,%edx + and %r11d,%ebx + xor 12(%rsp),%eax + or %r11d,%ecx + rol $5,%edx + xor 32(%rsp),%eax + and %r12d,%ecx + add %edx,%esi + rol $1,%eax + or %ecx,%ebx + rol $30,%ebp + mov %eax,44(%rsp) + add %ebx,%esi + lea -0x70e44324(%eax,%r12d),%edx + mov 48(%rsp),%eax + mov %edi,%ebx + mov %edi,%ecx + xor 56(%rsp),%eax + mov %esi,%r12d + and %ebp,%ebx + xor 16(%rsp),%eax + or %ebp,%ecx + rol $5,%r12d + xor 36(%rsp),%eax + and %r11d,%ecx + add %r12d,%edx + rol $1,%eax + or %ecx,%ebx + rol $30,%edi + mov %eax,48(%rsp) + add %ebx,%edx + lea -0x359d3e2a(%eax,%r11d),%r12d + mov 52(%rsp),%eax + mov %edi,%ebx + mov %edx,%r11d + xor 60(%rsp),%eax + xor %esi,%ebx + rol $5,%r11d + xor 20(%rsp),%eax + xor %ebp,%ebx + add %r11d,%r12d + xor 40(%rsp),%eax + rol $30,%esi + add %ebx,%r12d + rol $1,%eax + mov %eax,52(%rsp) + lea -0x359d3e2a(%eax,%ebp),%r11d + mov 56(%rsp),%eax + mov %esi,%ebx + mov %r12d,%ebp + xor 0(%rsp),%eax + xor %edx,%ebx + rol $5,%ebp + xor 24(%rsp),%eax + xor %edi,%ebx + add %ebp,%r11d + xor 44(%rsp),%eax + rol $30,%edx + add %ebx,%r11d + rol $1,%eax + mov %eax,56(%rsp) + lea -0x359d3e2a(%eax,%edi),%ebp + mov 60(%rsp),%eax + mov %edx,%ebx + mov %r11d,%edi + xor 4(%rsp),%eax + xor %r12d,%ebx + rol $5,%edi + xor 28(%rsp),%eax + xor %esi,%ebx + add %edi,%ebp + xor 48(%rsp),%eax + rol $30,%r12d + add %ebx,%ebp + rol $1,%eax + mov %eax,60(%rsp) + lea -0x359d3e2a(%eax,%esi),%edi + mov 0(%rsp),%eax + mov %r12d,%ebx + mov %ebp,%esi + xor 8(%rsp),%eax + xor %r11d,%ebx + rol $5,%esi + xor 32(%rsp),%eax + xor %edx,%ebx + add %esi,%edi + xor 52(%rsp),%eax + rol $30,%r11d + add %ebx,%edi + rol $1,%eax + mov %eax,0(%rsp) + lea -0x359d3e2a(%eax,%edx),%esi + mov 4(%rsp),%eax + mov %r11d,%ebx + mov %edi,%edx + xor 12(%rsp),%eax + xor %ebp,%ebx + rol $5,%edx + xor 36(%rsp),%eax + xor %r12d,%ebx + add %edx,%esi + xor 56(%rsp),%eax + rol $30,%ebp + add %ebx,%esi + rol $1,%eax + mov %eax,4(%rsp) + lea -0x359d3e2a(%eax,%r12d),%edx + mov 8(%rsp),%eax + mov %ebp,%ebx + mov %esi,%r12d + xor 16(%rsp),%eax + xor %edi,%ebx + rol $5,%r12d + xor 40(%rsp),%eax + xor %r11d,%ebx + add %r12d,%edx + xor 60(%rsp),%eax + rol $30,%edi + add %ebx,%edx + rol $1,%eax + mov %eax,8(%rsp) + lea -0x359d3e2a(%eax,%r11d),%r12d + mov 12(%rsp),%eax + mov %edi,%ebx + mov %edx,%r11d + xor 20(%rsp),%eax + xor %esi,%ebx + rol $5,%r11d + xor 44(%rsp),%eax + xor %ebp,%ebx + add %r11d,%r12d + xor 0(%rsp),%eax + rol $30,%esi + add %ebx,%r12d + rol $1,%eax + mov %eax,12(%rsp) + lea -0x359d3e2a(%eax,%ebp),%r11d + mov 16(%rsp),%eax + mov %esi,%ebx + mov %r12d,%ebp + xor 24(%rsp),%eax + xor %edx,%ebx + rol $5,%ebp + xor 48(%rsp),%eax + xor %edi,%ebx + add %ebp,%r11d + xor 4(%rsp),%eax + rol $30,%edx + add %ebx,%r11d + rol $1,%eax + mov %eax,16(%rsp) + lea -0x359d3e2a(%eax,%edi),%ebp + mov 20(%rsp),%eax + mov %edx,%ebx + mov %r11d,%edi + xor 28(%rsp),%eax + xor %r12d,%ebx + rol $5,%edi + xor 52(%rsp),%eax + xor %esi,%ebx + add %edi,%ebp + xor 8(%rsp),%eax + rol $30,%r12d + add %ebx,%ebp + rol $1,%eax + mov %eax,20(%rsp) + lea -0x359d3e2a(%eax,%esi),%edi + mov 24(%rsp),%eax + mov %r12d,%ebx + mov %ebp,%esi + xor 32(%rsp),%eax + xor %r11d,%ebx + rol $5,%esi + xor 56(%rsp),%eax + xor %edx,%ebx + add %esi,%edi + xor 12(%rsp),%eax + rol $30,%r11d + add %ebx,%edi + rol $1,%eax + mov %eax,24(%rsp) + lea -0x359d3e2a(%eax,%edx),%esi + mov 28(%rsp),%eax + mov %r11d,%ebx + mov %edi,%edx + xor 36(%rsp),%eax + xor %ebp,%ebx + rol $5,%edx + xor 60(%rsp),%eax + xor %r12d,%ebx + add %edx,%esi + xor 16(%rsp),%eax + rol $30,%ebp + add %ebx,%esi + rol $1,%eax + mov %eax,28(%rsp) + lea -0x359d3e2a(%eax,%r12d),%edx + mov 32(%rsp),%eax + mov %ebp,%ebx + mov %esi,%r12d + xor 40(%rsp),%eax + xor %edi,%ebx + rol $5,%r12d + xor 0(%rsp),%eax + xor %r11d,%ebx + add %r12d,%edx + xor 20(%rsp),%eax + rol $30,%edi + add %ebx,%edx + rol $1,%eax + mov %eax,32(%rsp) + lea -0x359d3e2a(%eax,%r11d),%r12d + mov 36(%rsp),%eax + mov %edi,%ebx + mov %edx,%r11d + xor 44(%rsp),%eax + xor %esi,%ebx + rol $5,%r11d + xor 4(%rsp),%eax + xor %ebp,%ebx + add %r11d,%r12d + xor 24(%rsp),%eax + rol $30,%esi + add %ebx,%r12d + rol $1,%eax + mov %eax,36(%rsp) + lea -0x359d3e2a(%eax,%ebp),%r11d + mov 40(%rsp),%eax + mov %esi,%ebx + mov %r12d,%ebp + xor 48(%rsp),%eax + xor %edx,%ebx + rol $5,%ebp + xor 8(%rsp),%eax + xor %edi,%ebx + add %ebp,%r11d + xor 28(%rsp),%eax + rol $30,%edx + add %ebx,%r11d + rol $1,%eax + mov %eax,40(%rsp) + lea -0x359d3e2a(%eax,%edi),%ebp + mov 44(%rsp),%eax + mov %edx,%ebx + mov %r11d,%edi + xor 52(%rsp),%eax + xor %r12d,%ebx + rol $5,%edi + xor 12(%rsp),%eax + xor %esi,%ebx + add %edi,%ebp + xor 32(%rsp),%eax + rol $30,%r12d + add %ebx,%ebp + rol $1,%eax + mov %eax,44(%rsp) + lea -0x359d3e2a(%eax,%esi),%edi + mov 48(%rsp),%eax + mov %r12d,%ebx + mov %ebp,%esi + xor 56(%rsp),%eax + xor %r11d,%ebx + rol $5,%esi + xor 16(%rsp),%eax + xor %edx,%ebx + add %esi,%edi + xor 36(%rsp),%eax + rol $30,%r11d + add %ebx,%edi + rol $1,%eax + mov %eax,48(%rsp) + lea -0x359d3e2a(%eax,%edx),%esi + mov 52(%rsp),%eax + mov %r11d,%ebx + mov %edi,%edx + xor 60(%rsp),%eax + xor %ebp,%ebx + rol $5,%edx + xor 20(%rsp),%eax + xor %r12d,%ebx + add %edx,%esi + xor 40(%rsp),%eax + rol $30,%ebp + add %ebx,%esi + rol $1,%eax + lea -0x359d3e2a(%eax,%r12d),%edx + mov 56(%rsp),%eax + mov %ebp,%ebx + mov %esi,%r12d + xor 0(%rsp),%eax + xor %edi,%ebx + rol $5,%r12d + xor 24(%rsp),%eax + xor %r11d,%ebx + add %r12d,%edx + xor 44(%rsp),%eax + rol $30,%edi + add %ebx,%edx + rol $1,%eax + lea -0x359d3e2a(%eax,%r11d),%r12d + mov 60(%rsp),%eax + mov %edi,%ebx + mov %edx,%r11d + xor 4(%rsp),%eax + xor %esi,%ebx + rol $5,%r11d + xor 28(%rsp),%eax + xor %ebp,%ebx + add %r11d,%r12d + xor 48(%rsp),%eax + rol $30,%esi + add %ebx,%r12d + rol $1,%eax + lea -0x359d3e2a(%eax,%ebp),%r11d + mov %esi,%ebx + mov %r12d,%ebp + xor %edx,%ebx + rol $5,%ebp + xor %edi,%ebx + add %ebp,%r11d + rol $30,%edx + add %ebx,%r11d + // Update and save state information in SHA-1 context + add 0(%r8),%r11d + add 4(%r8),%r12d + add 8(%r8),%edx + add 12(%r8),%esi + add 16(%r8),%edi + mov %r11d,0(%r8) + mov %r12d,4(%r8) + mov %edx,8(%r8) + mov %esi,12(%r8) + mov %edi,16(%r8) + + xchg %r11d,%edx # mov %r11d,%edx + xchg %r12d,%esi # mov %r12d,%esi + xchg %r11d,%edi # mov %edx,%edi + xchg %r12d,%ebp # mov %esi,%ebp + # mov %edi,%r11d + lea 64(%r9),%r9 + sub $1,%r10 + jnz .Lloop + mov 64(%rsp),%rsp + pop %r12 + pop %rbp + pop %rbx + ret +SET_SIZE(sha1_block_data_order) + +.data +.asciz "SHA1 block transform for x86_64, CRYPTOGAMS by " + +#endif /* lint || __lint */ + +#ifdef __ELF__ +.section .note.GNU-stack,"",%progbits +#endif diff --git a/module/icp/asm-x86_64/os/macos/sha2/sha256_impl.S b/module/icp/asm-x86_64/os/macos/sha2/sha256_impl.S new file mode 100644 index 000000000000..0b0f3444fada --- /dev/null +++ b/module/icp/asm-x86_64/os/macos/sha2/sha256_impl.S @@ -0,0 +1,2058 @@ + +/* + * ==================================================================== + * Written by Andy Polyakov for the OpenSSL + * project. Rights for redistribution and usage in source and binary + * forms are granted according to the OpenSSL license. + * ==================================================================== + * + * sha256/512_block procedure for x86_64. + * + * 40% improvement over compiler-generated code on Opteron. On EM64T + * sha256 was observed to run >80% faster and sha512 - >40%. No magical + * tricks, just straight implementation... I really wonder why gcc + * [being armed with inline assembler] fails to generate as fast code. + * The only thing which is cool about this module is that it's very + * same instruction sequence used for both SHA-256 and SHA-512. In + * former case the instructions operate on 32-bit operands, while in + * latter - on 64-bit ones. All I had to do is to get one flavor right, + * the other one passed the test right away:-) + * + * sha256_block runs in ~1005 cycles on Opteron, which gives you + * asymptotic performance of 64*1000/1005=63.7MBps times CPU clock + * frequency in GHz. sha512_block runs in ~1275 cycles, which results + * in 128*1000/1275=100MBps per GHz. Is there room for improvement? + * Well, if you compare it to IA-64 implementation, which maintains + * X[16] in register bank[!], tends to 4 instructions per CPU clock + * cycle and runs in 1003 cycles, 1275 is very good result for 3-way + * issue Opteron pipeline and X[16] maintained in memory. So that *if* + * there is a way to improve it, *then* the only way would be to try to + * offload X[16] updates to SSE unit, but that would require "deeper" + * loop unroll, which in turn would naturally cause size blow-up, not + * to mention increased complexity! And once again, only *if* it's + * actually possible to noticeably improve overall ILP, instruction + * level parallelism, on a given CPU implementation in this case. + * + * Special note on Intel EM64T. While Opteron CPU exhibits perfect + * perfromance ratio of 1.5 between 64- and 32-bit flavors [see above], + * [currently available] EM64T CPUs apparently are far from it. On the + * contrary, 64-bit version, sha512_block, is ~30% *slower* than 32-bit + * sha256_block:-( This is presumably because 64-bit shifts/rotates + * apparently are not atomic instructions, but implemented in microcode. + */ + +/* + * OpenSolaris OS modifications + * + * Sun elects to use this software under the BSD license. + * + * This source originates from OpenSSL file sha512-x86_64.pl at + * ftp://ftp.openssl.org/snapshot/openssl-0.9.8-stable-SNAP-20080131.tar.gz + * (presumably for future OpenSSL release 0.9.8h), with these changes: + * + * 1. Added perl "use strict" and declared variables. + * + * 2. Added OpenSolaris ENTRY_NP/SET_SIZE macros from + * /usr/include/sys/asm_linkage.h, .ident keywords, and lint(1B) guards. + * + * 3. Removed x86_64-xlate.pl script (not needed for as(1) or gas(1) + * assemblers). Replaced the .picmeup macro with assembler code. + * + * 4. Added 8 to $ctx, as OpenSolaris OS has an extra 4-byte field, "algotype", + * at the beginning of SHA2_CTX (the next field is 8-byte aligned). + */ + +/* + * This file was generated by a perl script (sha512-x86_64.pl) that were + * used to generate sha256 and sha512 variants from the same code base. + * The comments from the original file have been pasted above. + */ + +#if defined(lint) || defined(__lint) +#include +#include + +/* ARGSUSED */ +void +SHA256TransformBlocks(SHA2_CTX *ctx, const void *in, size_t num) +{ +} + + +#else +#define _ASM +#include + +ENTRY_NP(SHA256TransformBlocks) + push %rbx + push %rbp + push %r12 + push %r13 + push %r14 + push %r15 + mov %rsp,%rbp # copy %rsp + shl $4,%rdx # num*16 + sub $16*4+4*8,%rsp + lea (%rsi,%rdx,4),%rdx # inp+num*16*4 + and $-64,%rsp # align stack frame + add $8,%rdi # Skip OpenSolaris field, "algotype" + mov %rdi,16*4+0*8(%rsp) # save ctx, 1st arg + mov %rsi,16*4+1*8(%rsp) # save inp, 2nd arg + mov %rdx,16*4+2*8(%rsp) # save end pointer, "3rd" arg + mov %rbp,16*4+3*8(%rsp) # save copy of %rsp + + //.picmeup %rbp + // The .picmeup pseudo-directive, from perlasm/x86_64_xlate.pl, puts + // the address of the "next" instruction into the target register + // (%rbp). This generates these 2 instructions: + lea .Llea(%rip),%rbp + //nop // .picmeup generates a nop for mod 8 alignment--not needed here + +.Llea: + lea K256-.(%rbp),%rbp + + mov 4*0(%rdi),%eax + mov 4*1(%rdi),%ebx + mov 4*2(%rdi),%ecx + mov 4*3(%rdi),%edx + mov 4*4(%rdi),%r8d + mov 4*5(%rdi),%r9d + mov 4*6(%rdi),%r10d + mov 4*7(%rdi),%r11d + jmp .Lloop + +.align 4, 0x90 +.Lloop: + xor %rdi,%rdi + mov 4*0(%rsi),%r12d + bswap %r12d + mov %r8d,%r13d + mov %r8d,%r14d + mov %r9d,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %r10d,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %r8d,%r15d # (f^g)&e + mov %r12d,0(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %r10d,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %r11d,%r12d # T1+=h + + mov %eax,%r11d + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %eax,%r13d + mov %eax,%r14d + + ror $2,%r11d + ror $13,%r13d + mov %eax,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%r11d + ror $9,%r13d + or %ecx,%r14d # a|c + + xor %r13d,%r11d # h=Sigma0(a) + and %ecx,%r15d # a&c + add %r12d,%edx # d+=T1 + + and %ebx,%r14d # (a|c)&b + add %r12d,%r11d # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%r11d # h+=Maj(a,b,c) + mov 4*1(%rsi),%r12d + bswap %r12d + mov %edx,%r13d + mov %edx,%r14d + mov %r8d,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %r9d,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %edx,%r15d # (f^g)&e + mov %r12d,4(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %r9d,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %r10d,%r12d # T1+=h + + mov %r11d,%r10d + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %r11d,%r13d + mov %r11d,%r14d + + ror $2,%r10d + ror $13,%r13d + mov %r11d,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%r10d + ror $9,%r13d + or %ebx,%r14d # a|c + + xor %r13d,%r10d # h=Sigma0(a) + and %ebx,%r15d # a&c + add %r12d,%ecx # d+=T1 + + and %eax,%r14d # (a|c)&b + add %r12d,%r10d # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%r10d # h+=Maj(a,b,c) + mov 4*2(%rsi),%r12d + bswap %r12d + mov %ecx,%r13d + mov %ecx,%r14d + mov %edx,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %r8d,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %ecx,%r15d # (f^g)&e + mov %r12d,8(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %r8d,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %r9d,%r12d # T1+=h + + mov %r10d,%r9d + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %r10d,%r13d + mov %r10d,%r14d + + ror $2,%r9d + ror $13,%r13d + mov %r10d,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%r9d + ror $9,%r13d + or %eax,%r14d # a|c + + xor %r13d,%r9d # h=Sigma0(a) + and %eax,%r15d # a&c + add %r12d,%ebx # d+=T1 + + and %r11d,%r14d # (a|c)&b + add %r12d,%r9d # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%r9d # h+=Maj(a,b,c) + mov 4*3(%rsi),%r12d + bswap %r12d + mov %ebx,%r13d + mov %ebx,%r14d + mov %ecx,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %edx,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %ebx,%r15d # (f^g)&e + mov %r12d,12(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %edx,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %r8d,%r12d # T1+=h + + mov %r9d,%r8d + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %r9d,%r13d + mov %r9d,%r14d + + ror $2,%r8d + ror $13,%r13d + mov %r9d,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%r8d + ror $9,%r13d + or %r11d,%r14d # a|c + + xor %r13d,%r8d # h=Sigma0(a) + and %r11d,%r15d # a&c + add %r12d,%eax # d+=T1 + + and %r10d,%r14d # (a|c)&b + add %r12d,%r8d # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%r8d # h+=Maj(a,b,c) + mov 4*4(%rsi),%r12d + bswap %r12d + mov %eax,%r13d + mov %eax,%r14d + mov %ebx,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %ecx,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %eax,%r15d # (f^g)&e + mov %r12d,16(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %ecx,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %edx,%r12d # T1+=h + + mov %r8d,%edx + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %r8d,%r13d + mov %r8d,%r14d + + ror $2,%edx + ror $13,%r13d + mov %r8d,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%edx + ror $9,%r13d + or %r10d,%r14d # a|c + + xor %r13d,%edx # h=Sigma0(a) + and %r10d,%r15d # a&c + add %r12d,%r11d # d+=T1 + + and %r9d,%r14d # (a|c)&b + add %r12d,%edx # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%edx # h+=Maj(a,b,c) + mov 4*5(%rsi),%r12d + bswap %r12d + mov %r11d,%r13d + mov %r11d,%r14d + mov %eax,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %ebx,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %r11d,%r15d # (f^g)&e + mov %r12d,20(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %ebx,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %ecx,%r12d # T1+=h + + mov %edx,%ecx + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %edx,%r13d + mov %edx,%r14d + + ror $2,%ecx + ror $13,%r13d + mov %edx,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%ecx + ror $9,%r13d + or %r9d,%r14d # a|c + + xor %r13d,%ecx # h=Sigma0(a) + and %r9d,%r15d # a&c + add %r12d,%r10d # d+=T1 + + and %r8d,%r14d # (a|c)&b + add %r12d,%ecx # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%ecx # h+=Maj(a,b,c) + mov 4*6(%rsi),%r12d + bswap %r12d + mov %r10d,%r13d + mov %r10d,%r14d + mov %r11d,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %eax,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %r10d,%r15d # (f^g)&e + mov %r12d,24(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %eax,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %ebx,%r12d # T1+=h + + mov %ecx,%ebx + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %ecx,%r13d + mov %ecx,%r14d + + ror $2,%ebx + ror $13,%r13d + mov %ecx,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%ebx + ror $9,%r13d + or %r8d,%r14d # a|c + + xor %r13d,%ebx # h=Sigma0(a) + and %r8d,%r15d # a&c + add %r12d,%r9d # d+=T1 + + and %edx,%r14d # (a|c)&b + add %r12d,%ebx # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%ebx # h+=Maj(a,b,c) + mov 4*7(%rsi),%r12d + bswap %r12d + mov %r9d,%r13d + mov %r9d,%r14d + mov %r10d,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %r11d,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %r9d,%r15d # (f^g)&e + mov %r12d,28(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %r11d,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %eax,%r12d # T1+=h + + mov %ebx,%eax + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %ebx,%r13d + mov %ebx,%r14d + + ror $2,%eax + ror $13,%r13d + mov %ebx,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%eax + ror $9,%r13d + or %edx,%r14d # a|c + + xor %r13d,%eax # h=Sigma0(a) + and %edx,%r15d # a&c + add %r12d,%r8d # d+=T1 + + and %ecx,%r14d # (a|c)&b + add %r12d,%eax # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%eax # h+=Maj(a,b,c) + mov 4*8(%rsi),%r12d + bswap %r12d + mov %r8d,%r13d + mov %r8d,%r14d + mov %r9d,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %r10d,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %r8d,%r15d # (f^g)&e + mov %r12d,32(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %r10d,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %r11d,%r12d # T1+=h + + mov %eax,%r11d + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %eax,%r13d + mov %eax,%r14d + + ror $2,%r11d + ror $13,%r13d + mov %eax,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%r11d + ror $9,%r13d + or %ecx,%r14d # a|c + + xor %r13d,%r11d # h=Sigma0(a) + and %ecx,%r15d # a&c + add %r12d,%edx # d+=T1 + + and %ebx,%r14d # (a|c)&b + add %r12d,%r11d # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%r11d # h+=Maj(a,b,c) + mov 4*9(%rsi),%r12d + bswap %r12d + mov %edx,%r13d + mov %edx,%r14d + mov %r8d,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %r9d,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %edx,%r15d # (f^g)&e + mov %r12d,36(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %r9d,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %r10d,%r12d # T1+=h + + mov %r11d,%r10d + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %r11d,%r13d + mov %r11d,%r14d + + ror $2,%r10d + ror $13,%r13d + mov %r11d,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%r10d + ror $9,%r13d + or %ebx,%r14d # a|c + + xor %r13d,%r10d # h=Sigma0(a) + and %ebx,%r15d # a&c + add %r12d,%ecx # d+=T1 + + and %eax,%r14d # (a|c)&b + add %r12d,%r10d # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%r10d # h+=Maj(a,b,c) + mov 4*10(%rsi),%r12d + bswap %r12d + mov %ecx,%r13d + mov %ecx,%r14d + mov %edx,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %r8d,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %ecx,%r15d # (f^g)&e + mov %r12d,40(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %r8d,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %r9d,%r12d # T1+=h + + mov %r10d,%r9d + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %r10d,%r13d + mov %r10d,%r14d + + ror $2,%r9d + ror $13,%r13d + mov %r10d,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%r9d + ror $9,%r13d + or %eax,%r14d # a|c + + xor %r13d,%r9d # h=Sigma0(a) + and %eax,%r15d # a&c + add %r12d,%ebx # d+=T1 + + and %r11d,%r14d # (a|c)&b + add %r12d,%r9d # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%r9d # h+=Maj(a,b,c) + mov 4*11(%rsi),%r12d + bswap %r12d + mov %ebx,%r13d + mov %ebx,%r14d + mov %ecx,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %edx,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %ebx,%r15d # (f^g)&e + mov %r12d,44(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %edx,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %r8d,%r12d # T1+=h + + mov %r9d,%r8d + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %r9d,%r13d + mov %r9d,%r14d + + ror $2,%r8d + ror $13,%r13d + mov %r9d,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%r8d + ror $9,%r13d + or %r11d,%r14d # a|c + + xor %r13d,%r8d # h=Sigma0(a) + and %r11d,%r15d # a&c + add %r12d,%eax # d+=T1 + + and %r10d,%r14d # (a|c)&b + add %r12d,%r8d # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%r8d # h+=Maj(a,b,c) + mov 4*12(%rsi),%r12d + bswap %r12d + mov %eax,%r13d + mov %eax,%r14d + mov %ebx,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %ecx,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %eax,%r15d # (f^g)&e + mov %r12d,48(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %ecx,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %edx,%r12d # T1+=h + + mov %r8d,%edx + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %r8d,%r13d + mov %r8d,%r14d + + ror $2,%edx + ror $13,%r13d + mov %r8d,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%edx + ror $9,%r13d + or %r10d,%r14d # a|c + + xor %r13d,%edx # h=Sigma0(a) + and %r10d,%r15d # a&c + add %r12d,%r11d # d+=T1 + + and %r9d,%r14d # (a|c)&b + add %r12d,%edx # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%edx # h+=Maj(a,b,c) + mov 4*13(%rsi),%r12d + bswap %r12d + mov %r11d,%r13d + mov %r11d,%r14d + mov %eax,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %ebx,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %r11d,%r15d # (f^g)&e + mov %r12d,52(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %ebx,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %ecx,%r12d # T1+=h + + mov %edx,%ecx + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %edx,%r13d + mov %edx,%r14d + + ror $2,%ecx + ror $13,%r13d + mov %edx,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%ecx + ror $9,%r13d + or %r9d,%r14d # a|c + + xor %r13d,%ecx # h=Sigma0(a) + and %r9d,%r15d # a&c + add %r12d,%r10d # d+=T1 + + and %r8d,%r14d # (a|c)&b + add %r12d,%ecx # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%ecx # h+=Maj(a,b,c) + mov 4*14(%rsi),%r12d + bswap %r12d + mov %r10d,%r13d + mov %r10d,%r14d + mov %r11d,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %eax,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %r10d,%r15d # (f^g)&e + mov %r12d,56(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %eax,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %ebx,%r12d # T1+=h + + mov %ecx,%ebx + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %ecx,%r13d + mov %ecx,%r14d + + ror $2,%ebx + ror $13,%r13d + mov %ecx,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%ebx + ror $9,%r13d + or %r8d,%r14d # a|c + + xor %r13d,%ebx # h=Sigma0(a) + and %r8d,%r15d # a&c + add %r12d,%r9d # d+=T1 + + and %edx,%r14d # (a|c)&b + add %r12d,%ebx # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%ebx # h+=Maj(a,b,c) + mov 4*15(%rsi),%r12d + bswap %r12d + mov %r9d,%r13d + mov %r9d,%r14d + mov %r10d,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %r11d,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %r9d,%r15d # (f^g)&e + mov %r12d,60(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %r11d,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %eax,%r12d # T1+=h + + mov %ebx,%eax + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %ebx,%r13d + mov %ebx,%r14d + + ror $2,%eax + ror $13,%r13d + mov %ebx,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%eax + ror $9,%r13d + or %edx,%r14d # a|c + + xor %r13d,%eax # h=Sigma0(a) + and %edx,%r15d # a&c + add %r12d,%r8d # d+=T1 + + and %ecx,%r14d # (a|c)&b + add %r12d,%eax # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%eax # h+=Maj(a,b,c) + jmp .Lrounds_16_xx +.align 4, 0x90 +.Lrounds_16_xx: + mov 4(%rsp),%r13d + mov 56(%rsp),%r12d + + mov %r13d,%r15d + + shr $3,%r13d + ror $7,%r15d + + xor %r15d,%r13d + ror $11,%r15d + + xor %r15d,%r13d # sigma0(X[(i+1)&0xf]) + mov %r12d,%r14d + + shr $10,%r12d + ror $17,%r14d + + xor %r14d,%r12d + ror $2,%r14d + + xor %r14d,%r12d # sigma1(X[(i+14)&0xf]) + + add %r13d,%r12d + + add 36(%rsp),%r12d + + add 0(%rsp),%r12d + mov %r8d,%r13d + mov %r8d,%r14d + mov %r9d,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %r10d,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %r8d,%r15d # (f^g)&e + mov %r12d,0(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %r10d,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %r11d,%r12d # T1+=h + + mov %eax,%r11d + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %eax,%r13d + mov %eax,%r14d + + ror $2,%r11d + ror $13,%r13d + mov %eax,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%r11d + ror $9,%r13d + or %ecx,%r14d # a|c + + xor %r13d,%r11d # h=Sigma0(a) + and %ecx,%r15d # a&c + add %r12d,%edx # d+=T1 + + and %ebx,%r14d # (a|c)&b + add %r12d,%r11d # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%r11d # h+=Maj(a,b,c) + mov 8(%rsp),%r13d + mov 60(%rsp),%r12d + + mov %r13d,%r15d + + shr $3,%r13d + ror $7,%r15d + + xor %r15d,%r13d + ror $11,%r15d + + xor %r15d,%r13d # sigma0(X[(i+1)&0xf]) + mov %r12d,%r14d + + shr $10,%r12d + ror $17,%r14d + + xor %r14d,%r12d + ror $2,%r14d + + xor %r14d,%r12d # sigma1(X[(i+14)&0xf]) + + add %r13d,%r12d + + add 40(%rsp),%r12d + + add 4(%rsp),%r12d + mov %edx,%r13d + mov %edx,%r14d + mov %r8d,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %r9d,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %edx,%r15d # (f^g)&e + mov %r12d,4(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %r9d,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %r10d,%r12d # T1+=h + + mov %r11d,%r10d + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %r11d,%r13d + mov %r11d,%r14d + + ror $2,%r10d + ror $13,%r13d + mov %r11d,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%r10d + ror $9,%r13d + or %ebx,%r14d # a|c + + xor %r13d,%r10d # h=Sigma0(a) + and %ebx,%r15d # a&c + add %r12d,%ecx # d+=T1 + + and %eax,%r14d # (a|c)&b + add %r12d,%r10d # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%r10d # h+=Maj(a,b,c) + mov 12(%rsp),%r13d + mov 0(%rsp),%r12d + + mov %r13d,%r15d + + shr $3,%r13d + ror $7,%r15d + + xor %r15d,%r13d + ror $11,%r15d + + xor %r15d,%r13d # sigma0(X[(i+1)&0xf]) + mov %r12d,%r14d + + shr $10,%r12d + ror $17,%r14d + + xor %r14d,%r12d + ror $2,%r14d + + xor %r14d,%r12d # sigma1(X[(i+14)&0xf]) + + add %r13d,%r12d + + add 44(%rsp),%r12d + + add 8(%rsp),%r12d + mov %ecx,%r13d + mov %ecx,%r14d + mov %edx,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %r8d,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %ecx,%r15d # (f^g)&e + mov %r12d,8(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %r8d,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %r9d,%r12d # T1+=h + + mov %r10d,%r9d + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %r10d,%r13d + mov %r10d,%r14d + + ror $2,%r9d + ror $13,%r13d + mov %r10d,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%r9d + ror $9,%r13d + or %eax,%r14d # a|c + + xor %r13d,%r9d # h=Sigma0(a) + and %eax,%r15d # a&c + add %r12d,%ebx # d+=T1 + + and %r11d,%r14d # (a|c)&b + add %r12d,%r9d # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%r9d # h+=Maj(a,b,c) + mov 16(%rsp),%r13d + mov 4(%rsp),%r12d + + mov %r13d,%r15d + + shr $3,%r13d + ror $7,%r15d + + xor %r15d,%r13d + ror $11,%r15d + + xor %r15d,%r13d # sigma0(X[(i+1)&0xf]) + mov %r12d,%r14d + + shr $10,%r12d + ror $17,%r14d + + xor %r14d,%r12d + ror $2,%r14d + + xor %r14d,%r12d # sigma1(X[(i+14)&0xf]) + + add %r13d,%r12d + + add 48(%rsp),%r12d + + add 12(%rsp),%r12d + mov %ebx,%r13d + mov %ebx,%r14d + mov %ecx,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %edx,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %ebx,%r15d # (f^g)&e + mov %r12d,12(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %edx,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %r8d,%r12d # T1+=h + + mov %r9d,%r8d + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %r9d,%r13d + mov %r9d,%r14d + + ror $2,%r8d + ror $13,%r13d + mov %r9d,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%r8d + ror $9,%r13d + or %r11d,%r14d # a|c + + xor %r13d,%r8d # h=Sigma0(a) + and %r11d,%r15d # a&c + add %r12d,%eax # d+=T1 + + and %r10d,%r14d # (a|c)&b + add %r12d,%r8d # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%r8d # h+=Maj(a,b,c) + mov 20(%rsp),%r13d + mov 8(%rsp),%r12d + + mov %r13d,%r15d + + shr $3,%r13d + ror $7,%r15d + + xor %r15d,%r13d + ror $11,%r15d + + xor %r15d,%r13d # sigma0(X[(i+1)&0xf]) + mov %r12d,%r14d + + shr $10,%r12d + ror $17,%r14d + + xor %r14d,%r12d + ror $2,%r14d + + xor %r14d,%r12d # sigma1(X[(i+14)&0xf]) + + add %r13d,%r12d + + add 52(%rsp),%r12d + + add 16(%rsp),%r12d + mov %eax,%r13d + mov %eax,%r14d + mov %ebx,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %ecx,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %eax,%r15d # (f^g)&e + mov %r12d,16(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %ecx,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %edx,%r12d # T1+=h + + mov %r8d,%edx + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %r8d,%r13d + mov %r8d,%r14d + + ror $2,%edx + ror $13,%r13d + mov %r8d,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%edx + ror $9,%r13d + or %r10d,%r14d # a|c + + xor %r13d,%edx # h=Sigma0(a) + and %r10d,%r15d # a&c + add %r12d,%r11d # d+=T1 + + and %r9d,%r14d # (a|c)&b + add %r12d,%edx # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%edx # h+=Maj(a,b,c) + mov 24(%rsp),%r13d + mov 12(%rsp),%r12d + + mov %r13d,%r15d + + shr $3,%r13d + ror $7,%r15d + + xor %r15d,%r13d + ror $11,%r15d + + xor %r15d,%r13d # sigma0(X[(i+1)&0xf]) + mov %r12d,%r14d + + shr $10,%r12d + ror $17,%r14d + + xor %r14d,%r12d + ror $2,%r14d + + xor %r14d,%r12d # sigma1(X[(i+14)&0xf]) + + add %r13d,%r12d + + add 56(%rsp),%r12d + + add 20(%rsp),%r12d + mov %r11d,%r13d + mov %r11d,%r14d + mov %eax,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %ebx,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %r11d,%r15d # (f^g)&e + mov %r12d,20(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %ebx,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %ecx,%r12d # T1+=h + + mov %edx,%ecx + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %edx,%r13d + mov %edx,%r14d + + ror $2,%ecx + ror $13,%r13d + mov %edx,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%ecx + ror $9,%r13d + or %r9d,%r14d # a|c + + xor %r13d,%ecx # h=Sigma0(a) + and %r9d,%r15d # a&c + add %r12d,%r10d # d+=T1 + + and %r8d,%r14d # (a|c)&b + add %r12d,%ecx # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%ecx # h+=Maj(a,b,c) + mov 28(%rsp),%r13d + mov 16(%rsp),%r12d + + mov %r13d,%r15d + + shr $3,%r13d + ror $7,%r15d + + xor %r15d,%r13d + ror $11,%r15d + + xor %r15d,%r13d # sigma0(X[(i+1)&0xf]) + mov %r12d,%r14d + + shr $10,%r12d + ror $17,%r14d + + xor %r14d,%r12d + ror $2,%r14d + + xor %r14d,%r12d # sigma1(X[(i+14)&0xf]) + + add %r13d,%r12d + + add 60(%rsp),%r12d + + add 24(%rsp),%r12d + mov %r10d,%r13d + mov %r10d,%r14d + mov %r11d,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %eax,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %r10d,%r15d # (f^g)&e + mov %r12d,24(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %eax,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %ebx,%r12d # T1+=h + + mov %ecx,%ebx + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %ecx,%r13d + mov %ecx,%r14d + + ror $2,%ebx + ror $13,%r13d + mov %ecx,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%ebx + ror $9,%r13d + or %r8d,%r14d # a|c + + xor %r13d,%ebx # h=Sigma0(a) + and %r8d,%r15d # a&c + add %r12d,%r9d # d+=T1 + + and %edx,%r14d # (a|c)&b + add %r12d,%ebx # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%ebx # h+=Maj(a,b,c) + mov 32(%rsp),%r13d + mov 20(%rsp),%r12d + + mov %r13d,%r15d + + shr $3,%r13d + ror $7,%r15d + + xor %r15d,%r13d + ror $11,%r15d + + xor %r15d,%r13d # sigma0(X[(i+1)&0xf]) + mov %r12d,%r14d + + shr $10,%r12d + ror $17,%r14d + + xor %r14d,%r12d + ror $2,%r14d + + xor %r14d,%r12d # sigma1(X[(i+14)&0xf]) + + add %r13d,%r12d + + add 0(%rsp),%r12d + + add 28(%rsp),%r12d + mov %r9d,%r13d + mov %r9d,%r14d + mov %r10d,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %r11d,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %r9d,%r15d # (f^g)&e + mov %r12d,28(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %r11d,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %eax,%r12d # T1+=h + + mov %ebx,%eax + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %ebx,%r13d + mov %ebx,%r14d + + ror $2,%eax + ror $13,%r13d + mov %ebx,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%eax + ror $9,%r13d + or %edx,%r14d # a|c + + xor %r13d,%eax # h=Sigma0(a) + and %edx,%r15d # a&c + add %r12d,%r8d # d+=T1 + + and %ecx,%r14d # (a|c)&b + add %r12d,%eax # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%eax # h+=Maj(a,b,c) + mov 36(%rsp),%r13d + mov 24(%rsp),%r12d + + mov %r13d,%r15d + + shr $3,%r13d + ror $7,%r15d + + xor %r15d,%r13d + ror $11,%r15d + + xor %r15d,%r13d # sigma0(X[(i+1)&0xf]) + mov %r12d,%r14d + + shr $10,%r12d + ror $17,%r14d + + xor %r14d,%r12d + ror $2,%r14d + + xor %r14d,%r12d # sigma1(X[(i+14)&0xf]) + + add %r13d,%r12d + + add 4(%rsp),%r12d + + add 32(%rsp),%r12d + mov %r8d,%r13d + mov %r8d,%r14d + mov %r9d,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %r10d,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %r8d,%r15d # (f^g)&e + mov %r12d,32(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %r10d,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %r11d,%r12d # T1+=h + + mov %eax,%r11d + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %eax,%r13d + mov %eax,%r14d + + ror $2,%r11d + ror $13,%r13d + mov %eax,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%r11d + ror $9,%r13d + or %ecx,%r14d # a|c + + xor %r13d,%r11d # h=Sigma0(a) + and %ecx,%r15d # a&c + add %r12d,%edx # d+=T1 + + and %ebx,%r14d # (a|c)&b + add %r12d,%r11d # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%r11d # h+=Maj(a,b,c) + mov 40(%rsp),%r13d + mov 28(%rsp),%r12d + + mov %r13d,%r15d + + shr $3,%r13d + ror $7,%r15d + + xor %r15d,%r13d + ror $11,%r15d + + xor %r15d,%r13d # sigma0(X[(i+1)&0xf]) + mov %r12d,%r14d + + shr $10,%r12d + ror $17,%r14d + + xor %r14d,%r12d + ror $2,%r14d + + xor %r14d,%r12d # sigma1(X[(i+14)&0xf]) + + add %r13d,%r12d + + add 8(%rsp),%r12d + + add 36(%rsp),%r12d + mov %edx,%r13d + mov %edx,%r14d + mov %r8d,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %r9d,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %edx,%r15d # (f^g)&e + mov %r12d,36(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %r9d,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %r10d,%r12d # T1+=h + + mov %r11d,%r10d + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %r11d,%r13d + mov %r11d,%r14d + + ror $2,%r10d + ror $13,%r13d + mov %r11d,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%r10d + ror $9,%r13d + or %ebx,%r14d # a|c + + xor %r13d,%r10d # h=Sigma0(a) + and %ebx,%r15d # a&c + add %r12d,%ecx # d+=T1 + + and %eax,%r14d # (a|c)&b + add %r12d,%r10d # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%r10d # h+=Maj(a,b,c) + mov 44(%rsp),%r13d + mov 32(%rsp),%r12d + + mov %r13d,%r15d + + shr $3,%r13d + ror $7,%r15d + + xor %r15d,%r13d + ror $11,%r15d + + xor %r15d,%r13d # sigma0(X[(i+1)&0xf]) + mov %r12d,%r14d + + shr $10,%r12d + ror $17,%r14d + + xor %r14d,%r12d + ror $2,%r14d + + xor %r14d,%r12d # sigma1(X[(i+14)&0xf]) + + add %r13d,%r12d + + add 12(%rsp),%r12d + + add 40(%rsp),%r12d + mov %ecx,%r13d + mov %ecx,%r14d + mov %edx,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %r8d,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %ecx,%r15d # (f^g)&e + mov %r12d,40(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %r8d,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %r9d,%r12d # T1+=h + + mov %r10d,%r9d + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %r10d,%r13d + mov %r10d,%r14d + + ror $2,%r9d + ror $13,%r13d + mov %r10d,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%r9d + ror $9,%r13d + or %eax,%r14d # a|c + + xor %r13d,%r9d # h=Sigma0(a) + and %eax,%r15d # a&c + add %r12d,%ebx # d+=T1 + + and %r11d,%r14d # (a|c)&b + add %r12d,%r9d # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%r9d # h+=Maj(a,b,c) + mov 48(%rsp),%r13d + mov 36(%rsp),%r12d + + mov %r13d,%r15d + + shr $3,%r13d + ror $7,%r15d + + xor %r15d,%r13d + ror $11,%r15d + + xor %r15d,%r13d # sigma0(X[(i+1)&0xf]) + mov %r12d,%r14d + + shr $10,%r12d + ror $17,%r14d + + xor %r14d,%r12d + ror $2,%r14d + + xor %r14d,%r12d # sigma1(X[(i+14)&0xf]) + + add %r13d,%r12d + + add 16(%rsp),%r12d + + add 44(%rsp),%r12d + mov %ebx,%r13d + mov %ebx,%r14d + mov %ecx,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %edx,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %ebx,%r15d # (f^g)&e + mov %r12d,44(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %edx,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %r8d,%r12d # T1+=h + + mov %r9d,%r8d + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %r9d,%r13d + mov %r9d,%r14d + + ror $2,%r8d + ror $13,%r13d + mov %r9d,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%r8d + ror $9,%r13d + or %r11d,%r14d # a|c + + xor %r13d,%r8d # h=Sigma0(a) + and %r11d,%r15d # a&c + add %r12d,%eax # d+=T1 + + and %r10d,%r14d # (a|c)&b + add %r12d,%r8d # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%r8d # h+=Maj(a,b,c) + mov 52(%rsp),%r13d + mov 40(%rsp),%r12d + + mov %r13d,%r15d + + shr $3,%r13d + ror $7,%r15d + + xor %r15d,%r13d + ror $11,%r15d + + xor %r15d,%r13d # sigma0(X[(i+1)&0xf]) + mov %r12d,%r14d + + shr $10,%r12d + ror $17,%r14d + + xor %r14d,%r12d + ror $2,%r14d + + xor %r14d,%r12d # sigma1(X[(i+14)&0xf]) + + add %r13d,%r12d + + add 20(%rsp),%r12d + + add 48(%rsp),%r12d + mov %eax,%r13d + mov %eax,%r14d + mov %ebx,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %ecx,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %eax,%r15d # (f^g)&e + mov %r12d,48(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %ecx,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %edx,%r12d # T1+=h + + mov %r8d,%edx + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %r8d,%r13d + mov %r8d,%r14d + + ror $2,%edx + ror $13,%r13d + mov %r8d,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%edx + ror $9,%r13d + or %r10d,%r14d # a|c + + xor %r13d,%edx # h=Sigma0(a) + and %r10d,%r15d # a&c + add %r12d,%r11d # d+=T1 + + and %r9d,%r14d # (a|c)&b + add %r12d,%edx # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%edx # h+=Maj(a,b,c) + mov 56(%rsp),%r13d + mov 44(%rsp),%r12d + + mov %r13d,%r15d + + shr $3,%r13d + ror $7,%r15d + + xor %r15d,%r13d + ror $11,%r15d + + xor %r15d,%r13d # sigma0(X[(i+1)&0xf]) + mov %r12d,%r14d + + shr $10,%r12d + ror $17,%r14d + + xor %r14d,%r12d + ror $2,%r14d + + xor %r14d,%r12d # sigma1(X[(i+14)&0xf]) + + add %r13d,%r12d + + add 24(%rsp),%r12d + + add 52(%rsp),%r12d + mov %r11d,%r13d + mov %r11d,%r14d + mov %eax,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %ebx,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %r11d,%r15d # (f^g)&e + mov %r12d,52(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %ebx,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %ecx,%r12d # T1+=h + + mov %edx,%ecx + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %edx,%r13d + mov %edx,%r14d + + ror $2,%ecx + ror $13,%r13d + mov %edx,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%ecx + ror $9,%r13d + or %r9d,%r14d # a|c + + xor %r13d,%ecx # h=Sigma0(a) + and %r9d,%r15d # a&c + add %r12d,%r10d # d+=T1 + + and %r8d,%r14d # (a|c)&b + add %r12d,%ecx # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%ecx # h+=Maj(a,b,c) + mov 60(%rsp),%r13d + mov 48(%rsp),%r12d + + mov %r13d,%r15d + + shr $3,%r13d + ror $7,%r15d + + xor %r15d,%r13d + ror $11,%r15d + + xor %r15d,%r13d # sigma0(X[(i+1)&0xf]) + mov %r12d,%r14d + + shr $10,%r12d + ror $17,%r14d + + xor %r14d,%r12d + ror $2,%r14d + + xor %r14d,%r12d # sigma1(X[(i+14)&0xf]) + + add %r13d,%r12d + + add 28(%rsp),%r12d + + add 56(%rsp),%r12d + mov %r10d,%r13d + mov %r10d,%r14d + mov %r11d,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %eax,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %r10d,%r15d # (f^g)&e + mov %r12d,56(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %eax,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %ebx,%r12d # T1+=h + + mov %ecx,%ebx + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %ecx,%r13d + mov %ecx,%r14d + + ror $2,%ebx + ror $13,%r13d + mov %ecx,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%ebx + ror $9,%r13d + or %r8d,%r14d # a|c + + xor %r13d,%ebx # h=Sigma0(a) + and %r8d,%r15d # a&c + add %r12d,%r9d # d+=T1 + + and %edx,%r14d # (a|c)&b + add %r12d,%ebx # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%ebx # h+=Maj(a,b,c) + mov 0(%rsp),%r13d + mov 52(%rsp),%r12d + + mov %r13d,%r15d + + shr $3,%r13d + ror $7,%r15d + + xor %r15d,%r13d + ror $11,%r15d + + xor %r15d,%r13d # sigma0(X[(i+1)&0xf]) + mov %r12d,%r14d + + shr $10,%r12d + ror $17,%r14d + + xor %r14d,%r12d + ror $2,%r14d + + xor %r14d,%r12d # sigma1(X[(i+14)&0xf]) + + add %r13d,%r12d + + add 32(%rsp),%r12d + + add 60(%rsp),%r12d + mov %r9d,%r13d + mov %r9d,%r14d + mov %r10d,%r15d + + ror $6,%r13d + ror $11,%r14d + xor %r11d,%r15d # f^g + + xor %r14d,%r13d + ror $14,%r14d + and %r9d,%r15d # (f^g)&e + mov %r12d,60(%rsp) + + xor %r14d,%r13d # Sigma1(e) + xor %r11d,%r15d # Ch(e,f,g)=((f^g)&e)^g + add %eax,%r12d # T1+=h + + mov %ebx,%eax + add %r13d,%r12d # T1+=Sigma1(e) + + add %r15d,%r12d # T1+=Ch(e,f,g) + mov %ebx,%r13d + mov %ebx,%r14d + + ror $2,%eax + ror $13,%r13d + mov %ebx,%r15d + add (%rbp,%rdi,4),%r12d # T1+=K[round] + + xor %r13d,%eax + ror $9,%r13d + or %edx,%r14d # a|c + + xor %r13d,%eax # h=Sigma0(a) + and %edx,%r15d # a&c + add %r12d,%r8d # d+=T1 + + and %ecx,%r14d # (a|c)&b + add %r12d,%eax # h+=T1 + + or %r15d,%r14d # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14d,%eax # h+=Maj(a,b,c) + cmp $64,%rdi + jb .Lrounds_16_xx + + mov 16*4+0*8(%rsp),%rdi + lea 16*4(%rsi),%rsi + + add 4*0(%rdi),%eax + add 4*1(%rdi),%ebx + add 4*2(%rdi),%ecx + add 4*3(%rdi),%edx + add 4*4(%rdi),%r8d + add 4*5(%rdi),%r9d + add 4*6(%rdi),%r10d + add 4*7(%rdi),%r11d + + cmp 16*4+2*8(%rsp),%rsi + + mov %eax,4*0(%rdi) + mov %ebx,4*1(%rdi) + mov %ecx,4*2(%rdi) + mov %edx,4*3(%rdi) + mov %r8d,4*4(%rdi) + mov %r9d,4*5(%rdi) + mov %r10d,4*6(%rdi) + mov %r11d,4*7(%rdi) + jb .Lloop + + mov 16*4+3*8(%rsp),%rsp + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %rbp + pop %rbx + + ret +SET_SIZE(SHA256TransformBlocks) + +.align 6, 0x90 +K256: + .long 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5 + .long 0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5 + .long 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3 + .long 0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174 + .long 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc + .long 0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da + .long 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7 + .long 0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967 + .long 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13 + .long 0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85 + .long 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3 + .long 0xd192e819,0xd6990624,0xf40e3585,0x106aa070 + .long 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5 + .long 0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3 + .long 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208 + .long 0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 +#endif /* !lint && !__lint */ diff --git a/module/icp/asm-x86_64/os/macos/sha2/sha512_impl.S b/module/icp/asm-x86_64/os/macos/sha2/sha512_impl.S new file mode 100644 index 000000000000..1b51f9d5b495 --- /dev/null +++ b/module/icp/asm-x86_64/os/macos/sha2/sha512_impl.S @@ -0,0 +1,2082 @@ +/* + * ==================================================================== + * Written by Andy Polyakov for the OpenSSL + * project. Rights for redistribution and usage in source and binary + * forms are granted according to the OpenSSL license. + * ==================================================================== + * + * sha256/512_block procedure for x86_64. + * + * 40% improvement over compiler-generated code on Opteron. On EM64T + * sha256 was observed to run >80% faster and sha512 - >40%. No magical + * tricks, just straight implementation... I really wonder why gcc + * [being armed with inline assembler] fails to generate as fast code. + * The only thing which is cool about this module is that it's very + * same instruction sequence used for both SHA-256 and SHA-512. In + * former case the instructions operate on 32-bit operands, while in + * latter - on 64-bit ones. All I had to do is to get one flavor right, + * the other one passed the test right away:-) + * + * sha256_block runs in ~1005 cycles on Opteron, which gives you + * asymptotic performance of 64*1000/1005=63.7MBps times CPU clock + * frequency in GHz. sha512_block runs in ~1275 cycles, which results + * in 128*1000/1275=100MBps per GHz. Is there room for improvement? + * Well, if you compare it to IA-64 implementation, which maintains + * X[16] in register bank[!], tends to 4 instructions per CPU clock + * cycle and runs in 1003 cycles, 1275 is very good result for 3-way + * issue Opteron pipeline and X[16] maintained in memory. So that *if* + * there is a way to improve it, *then* the only way would be to try to + * offload X[16] updates to SSE unit, but that would require "deeper" + * loop unroll, which in turn would naturally cause size blow-up, not + * to mention increased complexity! And once again, only *if* it's + * actually possible to noticeably improve overall ILP, instruction + * level parallelism, on a given CPU implementation in this case. + * + * Special note on Intel EM64T. While Opteron CPU exhibits perfect + * perfromance ratio of 1.5 between 64- and 32-bit flavors [see above], + * [currently available] EM64T CPUs apparently are far from it. On the + * contrary, 64-bit version, sha512_block, is ~30% *slower* than 32-bit + * sha256_block:-( This is presumably because 64-bit shifts/rotates + * apparently are not atomic instructions, but implemented in microcode. + */ + +/* + * OpenSolaris OS modifications + * + * Sun elects to use this software under the BSD license. + * + * This source originates from OpenSSL file sha512-x86_64.pl at + * ftp://ftp.openssl.org/snapshot/openssl-0.9.8-stable-SNAP-20080131.tar.gz + * (presumably for future OpenSSL release 0.9.8h), with these changes: + * + * 1. Added perl "use strict" and declared variables. + * + * 2. Added OpenSolaris ENTRY_NP/SET_SIZE macros from + * /usr/include/sys/asm_linkage.h, .ident keywords, and lint(1B) guards. + * + * 3. Removed x86_64-xlate.pl script (not needed for as(1) or gas(1) + * assemblers). Replaced the .picmeup macro with assembler code. + * + * 4. Added 8 to $ctx, as OpenSolaris OS has an extra 4-byte field, "algotype", + * at the beginning of SHA2_CTX (the next field is 8-byte aligned). + */ + +/* + * This file was generated by a perl script (sha512-x86_64.pl) that were + * used to generate sha256 and sha512 variants from the same code base. + * The comments from the original file have been pasted above. + */ + + +#if defined(lint) || defined(__lint) +#include +#include + +/* ARGSUSED */ +void +SHA512TransformBlocks(SHA2_CTX *ctx, const void *in, size_t num) +{ +} + + +#else +#define _ASM +#include + +ENTRY_NP(SHA512TransformBlocks) + push %rbx + push %rbp + push %r12 + push %r13 + push %r14 + push %r15 + mov %rsp,%rbp # copy %rsp + shl $4,%rdx # num*16 + sub $16*8+4*8,%rsp + lea (%rsi,%rdx,8),%rdx # inp+num*16*8 + and $-64,%rsp # align stack frame + add $8,%rdi # Skip OpenSolaris field, "algotype" + mov %rdi,16*8+0*8(%rsp) # save ctx, 1st arg + mov %rsi,16*8+1*8(%rsp) # save inp, 2nd arg + mov %rdx,16*8+2*8(%rsp) # save end pointer, "3rd" arg + mov %rbp,16*8+3*8(%rsp) # save copy of %rsp + + //.picmeup %rbp + // The .picmeup pseudo-directive, from perlasm/x86_64_xlate.pl, puts + // the address of the "next" instruction into the target register + // (%rbp). This generates these 2 instructions: + lea .Llea(%rip),%rbp + //nop // .picmeup generates a nop for mod 8 alignment--not needed here + +.Llea: + lea K512-.(%rbp),%rbp + + mov 8*0(%rdi),%rax + mov 8*1(%rdi),%rbx + mov 8*2(%rdi),%rcx + mov 8*3(%rdi),%rdx + mov 8*4(%rdi),%r8 + mov 8*5(%rdi),%r9 + mov 8*6(%rdi),%r10 + mov 8*7(%rdi),%r11 + jmp .Lloop + +.align 4, 0x90 +.Lloop: + xor %rdi,%rdi + mov 8*0(%rsi),%r12 + bswap %r12 + mov %r8,%r13 + mov %r8,%r14 + mov %r9,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %r10,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %r8,%r15 # (f^g)&e + mov %r12,0(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %r10,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %r11,%r12 # T1+=h + + mov %rax,%r11 + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %rax,%r13 + mov %rax,%r14 + + ror $28,%r11 + ror $34,%r13 + mov %rax,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%r11 + ror $5,%r13 + or %rcx,%r14 # a|c + + xor %r13,%r11 # h=Sigma0(a) + and %rcx,%r15 # a&c + add %r12,%rdx # d+=T1 + + and %rbx,%r14 # (a|c)&b + add %r12,%r11 # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%r11 # h+=Maj(a,b,c) + mov 8*1(%rsi),%r12 + bswap %r12 + mov %rdx,%r13 + mov %rdx,%r14 + mov %r8,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %r9,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %rdx,%r15 # (f^g)&e + mov %r12,8(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %r9,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %r10,%r12 # T1+=h + + mov %r11,%r10 + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %r11,%r13 + mov %r11,%r14 + + ror $28,%r10 + ror $34,%r13 + mov %r11,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%r10 + ror $5,%r13 + or %rbx,%r14 # a|c + + xor %r13,%r10 # h=Sigma0(a) + and %rbx,%r15 # a&c + add %r12,%rcx # d+=T1 + + and %rax,%r14 # (a|c)&b + add %r12,%r10 # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%r10 # h+=Maj(a,b,c) + mov 8*2(%rsi),%r12 + bswap %r12 + mov %rcx,%r13 + mov %rcx,%r14 + mov %rdx,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %r8,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %rcx,%r15 # (f^g)&e + mov %r12,16(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %r8,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %r9,%r12 # T1+=h + + mov %r10,%r9 + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %r10,%r13 + mov %r10,%r14 + + ror $28,%r9 + ror $34,%r13 + mov %r10,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%r9 + ror $5,%r13 + or %rax,%r14 # a|c + + xor %r13,%r9 # h=Sigma0(a) + and %rax,%r15 # a&c + add %r12,%rbx # d+=T1 + + and %r11,%r14 # (a|c)&b + add %r12,%r9 # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%r9 # h+=Maj(a,b,c) + mov 8*3(%rsi),%r12 + bswap %r12 + mov %rbx,%r13 + mov %rbx,%r14 + mov %rcx,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %rdx,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %rbx,%r15 # (f^g)&e + mov %r12,24(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %rdx,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %r8,%r12 # T1+=h + + mov %r9,%r8 + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %r9,%r13 + mov %r9,%r14 + + ror $28,%r8 + ror $34,%r13 + mov %r9,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%r8 + ror $5,%r13 + or %r11,%r14 # a|c + + xor %r13,%r8 # h=Sigma0(a) + and %r11,%r15 # a&c + add %r12,%rax # d+=T1 + + and %r10,%r14 # (a|c)&b + add %r12,%r8 # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%r8 # h+=Maj(a,b,c) + mov 8*4(%rsi),%r12 + bswap %r12 + mov %rax,%r13 + mov %rax,%r14 + mov %rbx,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %rcx,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %rax,%r15 # (f^g)&e + mov %r12,32(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %rcx,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %rdx,%r12 # T1+=h + + mov %r8,%rdx + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %r8,%r13 + mov %r8,%r14 + + ror $28,%rdx + ror $34,%r13 + mov %r8,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%rdx + ror $5,%r13 + or %r10,%r14 # a|c + + xor %r13,%rdx # h=Sigma0(a) + and %r10,%r15 # a&c + add %r12,%r11 # d+=T1 + + and %r9,%r14 # (a|c)&b + add %r12,%rdx # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%rdx # h+=Maj(a,b,c) + mov 8*5(%rsi),%r12 + bswap %r12 + mov %r11,%r13 + mov %r11,%r14 + mov %rax,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %rbx,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %r11,%r15 # (f^g)&e + mov %r12,40(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %rbx,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %rcx,%r12 # T1+=h + + mov %rdx,%rcx + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %rdx,%r13 + mov %rdx,%r14 + + ror $28,%rcx + ror $34,%r13 + mov %rdx,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%rcx + ror $5,%r13 + or %r9,%r14 # a|c + + xor %r13,%rcx # h=Sigma0(a) + and %r9,%r15 # a&c + add %r12,%r10 # d+=T1 + + and %r8,%r14 # (a|c)&b + add %r12,%rcx # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%rcx # h+=Maj(a,b,c) + mov 8*6(%rsi),%r12 + bswap %r12 + mov %r10,%r13 + mov %r10,%r14 + mov %r11,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %rax,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %r10,%r15 # (f^g)&e + mov %r12,48(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %rax,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %rbx,%r12 # T1+=h + + mov %rcx,%rbx + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %rcx,%r13 + mov %rcx,%r14 + + ror $28,%rbx + ror $34,%r13 + mov %rcx,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%rbx + ror $5,%r13 + or %r8,%r14 # a|c + + xor %r13,%rbx # h=Sigma0(a) + and %r8,%r15 # a&c + add %r12,%r9 # d+=T1 + + and %rdx,%r14 # (a|c)&b + add %r12,%rbx # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%rbx # h+=Maj(a,b,c) + mov 8*7(%rsi),%r12 + bswap %r12 + mov %r9,%r13 + mov %r9,%r14 + mov %r10,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %r11,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %r9,%r15 # (f^g)&e + mov %r12,56(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %r11,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %rax,%r12 # T1+=h + + mov %rbx,%rax + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %rbx,%r13 + mov %rbx,%r14 + + ror $28,%rax + ror $34,%r13 + mov %rbx,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%rax + ror $5,%r13 + or %rdx,%r14 # a|c + + xor %r13,%rax # h=Sigma0(a) + and %rdx,%r15 # a&c + add %r12,%r8 # d+=T1 + + and %rcx,%r14 # (a|c)&b + add %r12,%rax # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%rax # h+=Maj(a,b,c) + mov 8*8(%rsi),%r12 + bswap %r12 + mov %r8,%r13 + mov %r8,%r14 + mov %r9,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %r10,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %r8,%r15 # (f^g)&e + mov %r12,64(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %r10,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %r11,%r12 # T1+=h + + mov %rax,%r11 + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %rax,%r13 + mov %rax,%r14 + + ror $28,%r11 + ror $34,%r13 + mov %rax,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%r11 + ror $5,%r13 + or %rcx,%r14 # a|c + + xor %r13,%r11 # h=Sigma0(a) + and %rcx,%r15 # a&c + add %r12,%rdx # d+=T1 + + and %rbx,%r14 # (a|c)&b + add %r12,%r11 # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%r11 # h+=Maj(a,b,c) + mov 8*9(%rsi),%r12 + bswap %r12 + mov %rdx,%r13 + mov %rdx,%r14 + mov %r8,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %r9,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %rdx,%r15 # (f^g)&e + mov %r12,72(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %r9,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %r10,%r12 # T1+=h + + mov %r11,%r10 + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %r11,%r13 + mov %r11,%r14 + + ror $28,%r10 + ror $34,%r13 + mov %r11,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%r10 + ror $5,%r13 + or %rbx,%r14 # a|c + + xor %r13,%r10 # h=Sigma0(a) + and %rbx,%r15 # a&c + add %r12,%rcx # d+=T1 + + and %rax,%r14 # (a|c)&b + add %r12,%r10 # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%r10 # h+=Maj(a,b,c) + mov 8*10(%rsi),%r12 + bswap %r12 + mov %rcx,%r13 + mov %rcx,%r14 + mov %rdx,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %r8,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %rcx,%r15 # (f^g)&e + mov %r12,80(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %r8,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %r9,%r12 # T1+=h + + mov %r10,%r9 + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %r10,%r13 + mov %r10,%r14 + + ror $28,%r9 + ror $34,%r13 + mov %r10,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%r9 + ror $5,%r13 + or %rax,%r14 # a|c + + xor %r13,%r9 # h=Sigma0(a) + and %rax,%r15 # a&c + add %r12,%rbx # d+=T1 + + and %r11,%r14 # (a|c)&b + add %r12,%r9 # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%r9 # h+=Maj(a,b,c) + mov 8*11(%rsi),%r12 + bswap %r12 + mov %rbx,%r13 + mov %rbx,%r14 + mov %rcx,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %rdx,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %rbx,%r15 # (f^g)&e + mov %r12,88(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %rdx,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %r8,%r12 # T1+=h + + mov %r9,%r8 + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %r9,%r13 + mov %r9,%r14 + + ror $28,%r8 + ror $34,%r13 + mov %r9,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%r8 + ror $5,%r13 + or %r11,%r14 # a|c + + xor %r13,%r8 # h=Sigma0(a) + and %r11,%r15 # a&c + add %r12,%rax # d+=T1 + + and %r10,%r14 # (a|c)&b + add %r12,%r8 # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%r8 # h+=Maj(a,b,c) + mov 8*12(%rsi),%r12 + bswap %r12 + mov %rax,%r13 + mov %rax,%r14 + mov %rbx,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %rcx,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %rax,%r15 # (f^g)&e + mov %r12,96(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %rcx,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %rdx,%r12 # T1+=h + + mov %r8,%rdx + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %r8,%r13 + mov %r8,%r14 + + ror $28,%rdx + ror $34,%r13 + mov %r8,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%rdx + ror $5,%r13 + or %r10,%r14 # a|c + + xor %r13,%rdx # h=Sigma0(a) + and %r10,%r15 # a&c + add %r12,%r11 # d+=T1 + + and %r9,%r14 # (a|c)&b + add %r12,%rdx # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%rdx # h+=Maj(a,b,c) + mov 8*13(%rsi),%r12 + bswap %r12 + mov %r11,%r13 + mov %r11,%r14 + mov %rax,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %rbx,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %r11,%r15 # (f^g)&e + mov %r12,104(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %rbx,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %rcx,%r12 # T1+=h + + mov %rdx,%rcx + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %rdx,%r13 + mov %rdx,%r14 + + ror $28,%rcx + ror $34,%r13 + mov %rdx,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%rcx + ror $5,%r13 + or %r9,%r14 # a|c + + xor %r13,%rcx # h=Sigma0(a) + and %r9,%r15 # a&c + add %r12,%r10 # d+=T1 + + and %r8,%r14 # (a|c)&b + add %r12,%rcx # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%rcx # h+=Maj(a,b,c) + mov 8*14(%rsi),%r12 + bswap %r12 + mov %r10,%r13 + mov %r10,%r14 + mov %r11,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %rax,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %r10,%r15 # (f^g)&e + mov %r12,112(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %rax,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %rbx,%r12 # T1+=h + + mov %rcx,%rbx + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %rcx,%r13 + mov %rcx,%r14 + + ror $28,%rbx + ror $34,%r13 + mov %rcx,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%rbx + ror $5,%r13 + or %r8,%r14 # a|c + + xor %r13,%rbx # h=Sigma0(a) + and %r8,%r15 # a&c + add %r12,%r9 # d+=T1 + + and %rdx,%r14 # (a|c)&b + add %r12,%rbx # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%rbx # h+=Maj(a,b,c) + mov 8*15(%rsi),%r12 + bswap %r12 + mov %r9,%r13 + mov %r9,%r14 + mov %r10,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %r11,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %r9,%r15 # (f^g)&e + mov %r12,120(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %r11,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %rax,%r12 # T1+=h + + mov %rbx,%rax + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %rbx,%r13 + mov %rbx,%r14 + + ror $28,%rax + ror $34,%r13 + mov %rbx,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%rax + ror $5,%r13 + or %rdx,%r14 # a|c + + xor %r13,%rax # h=Sigma0(a) + and %rdx,%r15 # a&c + add %r12,%r8 # d+=T1 + + and %rcx,%r14 # (a|c)&b + add %r12,%rax # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%rax # h+=Maj(a,b,c) + jmp .Lrounds_16_xx +.align 4, 0x90 +.Lrounds_16_xx: + mov 8(%rsp),%r13 + mov 112(%rsp),%r12 + + mov %r13,%r15 + + shr $7,%r13 + ror $1,%r15 + + xor %r15,%r13 + ror $7,%r15 + + xor %r15,%r13 # sigma0(X[(i+1)&0xf]) + mov %r12,%r14 + + shr $6,%r12 + ror $19,%r14 + + xor %r14,%r12 + ror $42,%r14 + + xor %r14,%r12 # sigma1(X[(i+14)&0xf]) + + add %r13,%r12 + + add 72(%rsp),%r12 + + add 0(%rsp),%r12 + mov %r8,%r13 + mov %r8,%r14 + mov %r9,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %r10,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %r8,%r15 # (f^g)&e + mov %r12,0(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %r10,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %r11,%r12 # T1+=h + + mov %rax,%r11 + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %rax,%r13 + mov %rax,%r14 + + ror $28,%r11 + ror $34,%r13 + mov %rax,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%r11 + ror $5,%r13 + or %rcx,%r14 # a|c + + xor %r13,%r11 # h=Sigma0(a) + and %rcx,%r15 # a&c + add %r12,%rdx # d+=T1 + + and %rbx,%r14 # (a|c)&b + add %r12,%r11 # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%r11 # h+=Maj(a,b,c) + mov 16(%rsp),%r13 + mov 120(%rsp),%r12 + + mov %r13,%r15 + + shr $7,%r13 + ror $1,%r15 + + xor %r15,%r13 + ror $7,%r15 + + xor %r15,%r13 # sigma0(X[(i+1)&0xf]) + mov %r12,%r14 + + shr $6,%r12 + ror $19,%r14 + + xor %r14,%r12 + ror $42,%r14 + + xor %r14,%r12 # sigma1(X[(i+14)&0xf]) + + add %r13,%r12 + + add 80(%rsp),%r12 + + add 8(%rsp),%r12 + mov %rdx,%r13 + mov %rdx,%r14 + mov %r8,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %r9,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %rdx,%r15 # (f^g)&e + mov %r12,8(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %r9,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %r10,%r12 # T1+=h + + mov %r11,%r10 + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %r11,%r13 + mov %r11,%r14 + + ror $28,%r10 + ror $34,%r13 + mov %r11,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%r10 + ror $5,%r13 + or %rbx,%r14 # a|c + + xor %r13,%r10 # h=Sigma0(a) + and %rbx,%r15 # a&c + add %r12,%rcx # d+=T1 + + and %rax,%r14 # (a|c)&b + add %r12,%r10 # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%r10 # h+=Maj(a,b,c) + mov 24(%rsp),%r13 + mov 0(%rsp),%r12 + + mov %r13,%r15 + + shr $7,%r13 + ror $1,%r15 + + xor %r15,%r13 + ror $7,%r15 + + xor %r15,%r13 # sigma0(X[(i+1)&0xf]) + mov %r12,%r14 + + shr $6,%r12 + ror $19,%r14 + + xor %r14,%r12 + ror $42,%r14 + + xor %r14,%r12 # sigma1(X[(i+14)&0xf]) + + add %r13,%r12 + + add 88(%rsp),%r12 + + add 16(%rsp),%r12 + mov %rcx,%r13 + mov %rcx,%r14 + mov %rdx,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %r8,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %rcx,%r15 # (f^g)&e + mov %r12,16(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %r8,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %r9,%r12 # T1+=h + + mov %r10,%r9 + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %r10,%r13 + mov %r10,%r14 + + ror $28,%r9 + ror $34,%r13 + mov %r10,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%r9 + ror $5,%r13 + or %rax,%r14 # a|c + + xor %r13,%r9 # h=Sigma0(a) + and %rax,%r15 # a&c + add %r12,%rbx # d+=T1 + + and %r11,%r14 # (a|c)&b + add %r12,%r9 # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%r9 # h+=Maj(a,b,c) + mov 32(%rsp),%r13 + mov 8(%rsp),%r12 + + mov %r13,%r15 + + shr $7,%r13 + ror $1,%r15 + + xor %r15,%r13 + ror $7,%r15 + + xor %r15,%r13 # sigma0(X[(i+1)&0xf]) + mov %r12,%r14 + + shr $6,%r12 + ror $19,%r14 + + xor %r14,%r12 + ror $42,%r14 + + xor %r14,%r12 # sigma1(X[(i+14)&0xf]) + + add %r13,%r12 + + add 96(%rsp),%r12 + + add 24(%rsp),%r12 + mov %rbx,%r13 + mov %rbx,%r14 + mov %rcx,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %rdx,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %rbx,%r15 # (f^g)&e + mov %r12,24(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %rdx,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %r8,%r12 # T1+=h + + mov %r9,%r8 + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %r9,%r13 + mov %r9,%r14 + + ror $28,%r8 + ror $34,%r13 + mov %r9,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%r8 + ror $5,%r13 + or %r11,%r14 # a|c + + xor %r13,%r8 # h=Sigma0(a) + and %r11,%r15 # a&c + add %r12,%rax # d+=T1 + + and %r10,%r14 # (a|c)&b + add %r12,%r8 # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%r8 # h+=Maj(a,b,c) + mov 40(%rsp),%r13 + mov 16(%rsp),%r12 + + mov %r13,%r15 + + shr $7,%r13 + ror $1,%r15 + + xor %r15,%r13 + ror $7,%r15 + + xor %r15,%r13 # sigma0(X[(i+1)&0xf]) + mov %r12,%r14 + + shr $6,%r12 + ror $19,%r14 + + xor %r14,%r12 + ror $42,%r14 + + xor %r14,%r12 # sigma1(X[(i+14)&0xf]) + + add %r13,%r12 + + add 104(%rsp),%r12 + + add 32(%rsp),%r12 + mov %rax,%r13 + mov %rax,%r14 + mov %rbx,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %rcx,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %rax,%r15 # (f^g)&e + mov %r12,32(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %rcx,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %rdx,%r12 # T1+=h + + mov %r8,%rdx + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %r8,%r13 + mov %r8,%r14 + + ror $28,%rdx + ror $34,%r13 + mov %r8,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%rdx + ror $5,%r13 + or %r10,%r14 # a|c + + xor %r13,%rdx # h=Sigma0(a) + and %r10,%r15 # a&c + add %r12,%r11 # d+=T1 + + and %r9,%r14 # (a|c)&b + add %r12,%rdx # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%rdx # h+=Maj(a,b,c) + mov 48(%rsp),%r13 + mov 24(%rsp),%r12 + + mov %r13,%r15 + + shr $7,%r13 + ror $1,%r15 + + xor %r15,%r13 + ror $7,%r15 + + xor %r15,%r13 # sigma0(X[(i+1)&0xf]) + mov %r12,%r14 + + shr $6,%r12 + ror $19,%r14 + + xor %r14,%r12 + ror $42,%r14 + + xor %r14,%r12 # sigma1(X[(i+14)&0xf]) + + add %r13,%r12 + + add 112(%rsp),%r12 + + add 40(%rsp),%r12 + mov %r11,%r13 + mov %r11,%r14 + mov %rax,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %rbx,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %r11,%r15 # (f^g)&e + mov %r12,40(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %rbx,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %rcx,%r12 # T1+=h + + mov %rdx,%rcx + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %rdx,%r13 + mov %rdx,%r14 + + ror $28,%rcx + ror $34,%r13 + mov %rdx,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%rcx + ror $5,%r13 + or %r9,%r14 # a|c + + xor %r13,%rcx # h=Sigma0(a) + and %r9,%r15 # a&c + add %r12,%r10 # d+=T1 + + and %r8,%r14 # (a|c)&b + add %r12,%rcx # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%rcx # h+=Maj(a,b,c) + mov 56(%rsp),%r13 + mov 32(%rsp),%r12 + + mov %r13,%r15 + + shr $7,%r13 + ror $1,%r15 + + xor %r15,%r13 + ror $7,%r15 + + xor %r15,%r13 # sigma0(X[(i+1)&0xf]) + mov %r12,%r14 + + shr $6,%r12 + ror $19,%r14 + + xor %r14,%r12 + ror $42,%r14 + + xor %r14,%r12 # sigma1(X[(i+14)&0xf]) + + add %r13,%r12 + + add 120(%rsp),%r12 + + add 48(%rsp),%r12 + mov %r10,%r13 + mov %r10,%r14 + mov %r11,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %rax,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %r10,%r15 # (f^g)&e + mov %r12,48(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %rax,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %rbx,%r12 # T1+=h + + mov %rcx,%rbx + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %rcx,%r13 + mov %rcx,%r14 + + ror $28,%rbx + ror $34,%r13 + mov %rcx,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%rbx + ror $5,%r13 + or %r8,%r14 # a|c + + xor %r13,%rbx # h=Sigma0(a) + and %r8,%r15 # a&c + add %r12,%r9 # d+=T1 + + and %rdx,%r14 # (a|c)&b + add %r12,%rbx # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%rbx # h+=Maj(a,b,c) + mov 64(%rsp),%r13 + mov 40(%rsp),%r12 + + mov %r13,%r15 + + shr $7,%r13 + ror $1,%r15 + + xor %r15,%r13 + ror $7,%r15 + + xor %r15,%r13 # sigma0(X[(i+1)&0xf]) + mov %r12,%r14 + + shr $6,%r12 + ror $19,%r14 + + xor %r14,%r12 + ror $42,%r14 + + xor %r14,%r12 # sigma1(X[(i+14)&0xf]) + + add %r13,%r12 + + add 0(%rsp),%r12 + + add 56(%rsp),%r12 + mov %r9,%r13 + mov %r9,%r14 + mov %r10,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %r11,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %r9,%r15 # (f^g)&e + mov %r12,56(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %r11,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %rax,%r12 # T1+=h + + mov %rbx,%rax + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %rbx,%r13 + mov %rbx,%r14 + + ror $28,%rax + ror $34,%r13 + mov %rbx,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%rax + ror $5,%r13 + or %rdx,%r14 # a|c + + xor %r13,%rax # h=Sigma0(a) + and %rdx,%r15 # a&c + add %r12,%r8 # d+=T1 + + and %rcx,%r14 # (a|c)&b + add %r12,%rax # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%rax # h+=Maj(a,b,c) + mov 72(%rsp),%r13 + mov 48(%rsp),%r12 + + mov %r13,%r15 + + shr $7,%r13 + ror $1,%r15 + + xor %r15,%r13 + ror $7,%r15 + + xor %r15,%r13 # sigma0(X[(i+1)&0xf]) + mov %r12,%r14 + + shr $6,%r12 + ror $19,%r14 + + xor %r14,%r12 + ror $42,%r14 + + xor %r14,%r12 # sigma1(X[(i+14)&0xf]) + + add %r13,%r12 + + add 8(%rsp),%r12 + + add 64(%rsp),%r12 + mov %r8,%r13 + mov %r8,%r14 + mov %r9,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %r10,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %r8,%r15 # (f^g)&e + mov %r12,64(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %r10,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %r11,%r12 # T1+=h + + mov %rax,%r11 + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %rax,%r13 + mov %rax,%r14 + + ror $28,%r11 + ror $34,%r13 + mov %rax,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%r11 + ror $5,%r13 + or %rcx,%r14 # a|c + + xor %r13,%r11 # h=Sigma0(a) + and %rcx,%r15 # a&c + add %r12,%rdx # d+=T1 + + and %rbx,%r14 # (a|c)&b + add %r12,%r11 # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%r11 # h+=Maj(a,b,c) + mov 80(%rsp),%r13 + mov 56(%rsp),%r12 + + mov %r13,%r15 + + shr $7,%r13 + ror $1,%r15 + + xor %r15,%r13 + ror $7,%r15 + + xor %r15,%r13 # sigma0(X[(i+1)&0xf]) + mov %r12,%r14 + + shr $6,%r12 + ror $19,%r14 + + xor %r14,%r12 + ror $42,%r14 + + xor %r14,%r12 # sigma1(X[(i+14)&0xf]) + + add %r13,%r12 + + add 16(%rsp),%r12 + + add 72(%rsp),%r12 + mov %rdx,%r13 + mov %rdx,%r14 + mov %r8,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %r9,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %rdx,%r15 # (f^g)&e + mov %r12,72(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %r9,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %r10,%r12 # T1+=h + + mov %r11,%r10 + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %r11,%r13 + mov %r11,%r14 + + ror $28,%r10 + ror $34,%r13 + mov %r11,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%r10 + ror $5,%r13 + or %rbx,%r14 # a|c + + xor %r13,%r10 # h=Sigma0(a) + and %rbx,%r15 # a&c + add %r12,%rcx # d+=T1 + + and %rax,%r14 # (a|c)&b + add %r12,%r10 # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%r10 # h+=Maj(a,b,c) + mov 88(%rsp),%r13 + mov 64(%rsp),%r12 + + mov %r13,%r15 + + shr $7,%r13 + ror $1,%r15 + + xor %r15,%r13 + ror $7,%r15 + + xor %r15,%r13 # sigma0(X[(i+1)&0xf]) + mov %r12,%r14 + + shr $6,%r12 + ror $19,%r14 + + xor %r14,%r12 + ror $42,%r14 + + xor %r14,%r12 # sigma1(X[(i+14)&0xf]) + + add %r13,%r12 + + add 24(%rsp),%r12 + + add 80(%rsp),%r12 + mov %rcx,%r13 + mov %rcx,%r14 + mov %rdx,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %r8,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %rcx,%r15 # (f^g)&e + mov %r12,80(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %r8,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %r9,%r12 # T1+=h + + mov %r10,%r9 + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %r10,%r13 + mov %r10,%r14 + + ror $28,%r9 + ror $34,%r13 + mov %r10,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%r9 + ror $5,%r13 + or %rax,%r14 # a|c + + xor %r13,%r9 # h=Sigma0(a) + and %rax,%r15 # a&c + add %r12,%rbx # d+=T1 + + and %r11,%r14 # (a|c)&b + add %r12,%r9 # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%r9 # h+=Maj(a,b,c) + mov 96(%rsp),%r13 + mov 72(%rsp),%r12 + + mov %r13,%r15 + + shr $7,%r13 + ror $1,%r15 + + xor %r15,%r13 + ror $7,%r15 + + xor %r15,%r13 # sigma0(X[(i+1)&0xf]) + mov %r12,%r14 + + shr $6,%r12 + ror $19,%r14 + + xor %r14,%r12 + ror $42,%r14 + + xor %r14,%r12 # sigma1(X[(i+14)&0xf]) + + add %r13,%r12 + + add 32(%rsp),%r12 + + add 88(%rsp),%r12 + mov %rbx,%r13 + mov %rbx,%r14 + mov %rcx,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %rdx,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %rbx,%r15 # (f^g)&e + mov %r12,88(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %rdx,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %r8,%r12 # T1+=h + + mov %r9,%r8 + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %r9,%r13 + mov %r9,%r14 + + ror $28,%r8 + ror $34,%r13 + mov %r9,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%r8 + ror $5,%r13 + or %r11,%r14 # a|c + + xor %r13,%r8 # h=Sigma0(a) + and %r11,%r15 # a&c + add %r12,%rax # d+=T1 + + and %r10,%r14 # (a|c)&b + add %r12,%r8 # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%r8 # h+=Maj(a,b,c) + mov 104(%rsp),%r13 + mov 80(%rsp),%r12 + + mov %r13,%r15 + + shr $7,%r13 + ror $1,%r15 + + xor %r15,%r13 + ror $7,%r15 + + xor %r15,%r13 # sigma0(X[(i+1)&0xf]) + mov %r12,%r14 + + shr $6,%r12 + ror $19,%r14 + + xor %r14,%r12 + ror $42,%r14 + + xor %r14,%r12 # sigma1(X[(i+14)&0xf]) + + add %r13,%r12 + + add 40(%rsp),%r12 + + add 96(%rsp),%r12 + mov %rax,%r13 + mov %rax,%r14 + mov %rbx,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %rcx,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %rax,%r15 # (f^g)&e + mov %r12,96(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %rcx,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %rdx,%r12 # T1+=h + + mov %r8,%rdx + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %r8,%r13 + mov %r8,%r14 + + ror $28,%rdx + ror $34,%r13 + mov %r8,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%rdx + ror $5,%r13 + or %r10,%r14 # a|c + + xor %r13,%rdx # h=Sigma0(a) + and %r10,%r15 # a&c + add %r12,%r11 # d+=T1 + + and %r9,%r14 # (a|c)&b + add %r12,%rdx # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%rdx # h+=Maj(a,b,c) + mov 112(%rsp),%r13 + mov 88(%rsp),%r12 + + mov %r13,%r15 + + shr $7,%r13 + ror $1,%r15 + + xor %r15,%r13 + ror $7,%r15 + + xor %r15,%r13 # sigma0(X[(i+1)&0xf]) + mov %r12,%r14 + + shr $6,%r12 + ror $19,%r14 + + xor %r14,%r12 + ror $42,%r14 + + xor %r14,%r12 # sigma1(X[(i+14)&0xf]) + + add %r13,%r12 + + add 48(%rsp),%r12 + + add 104(%rsp),%r12 + mov %r11,%r13 + mov %r11,%r14 + mov %rax,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %rbx,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %r11,%r15 # (f^g)&e + mov %r12,104(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %rbx,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %rcx,%r12 # T1+=h + + mov %rdx,%rcx + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %rdx,%r13 + mov %rdx,%r14 + + ror $28,%rcx + ror $34,%r13 + mov %rdx,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%rcx + ror $5,%r13 + or %r9,%r14 # a|c + + xor %r13,%rcx # h=Sigma0(a) + and %r9,%r15 # a&c + add %r12,%r10 # d+=T1 + + and %r8,%r14 # (a|c)&b + add %r12,%rcx # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%rcx # h+=Maj(a,b,c) + mov 120(%rsp),%r13 + mov 96(%rsp),%r12 + + mov %r13,%r15 + + shr $7,%r13 + ror $1,%r15 + + xor %r15,%r13 + ror $7,%r15 + + xor %r15,%r13 # sigma0(X[(i+1)&0xf]) + mov %r12,%r14 + + shr $6,%r12 + ror $19,%r14 + + xor %r14,%r12 + ror $42,%r14 + + xor %r14,%r12 # sigma1(X[(i+14)&0xf]) + + add %r13,%r12 + + add 56(%rsp),%r12 + + add 112(%rsp),%r12 + mov %r10,%r13 + mov %r10,%r14 + mov %r11,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %rax,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %r10,%r15 # (f^g)&e + mov %r12,112(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %rax,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %rbx,%r12 # T1+=h + + mov %rcx,%rbx + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %rcx,%r13 + mov %rcx,%r14 + + ror $28,%rbx + ror $34,%r13 + mov %rcx,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%rbx + ror $5,%r13 + or %r8,%r14 # a|c + + xor %r13,%rbx # h=Sigma0(a) + and %r8,%r15 # a&c + add %r12,%r9 # d+=T1 + + and %rdx,%r14 # (a|c)&b + add %r12,%rbx # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%rbx # h+=Maj(a,b,c) + mov 0(%rsp),%r13 + mov 104(%rsp),%r12 + + mov %r13,%r15 + + shr $7,%r13 + ror $1,%r15 + + xor %r15,%r13 + ror $7,%r15 + + xor %r15,%r13 # sigma0(X[(i+1)&0xf]) + mov %r12,%r14 + + shr $6,%r12 + ror $19,%r14 + + xor %r14,%r12 + ror $42,%r14 + + xor %r14,%r12 # sigma1(X[(i+14)&0xf]) + + add %r13,%r12 + + add 64(%rsp),%r12 + + add 120(%rsp),%r12 + mov %r9,%r13 + mov %r9,%r14 + mov %r10,%r15 + + ror $14,%r13 + ror $18,%r14 + xor %r11,%r15 # f^g + + xor %r14,%r13 + ror $23,%r14 + and %r9,%r15 # (f^g)&e + mov %r12,120(%rsp) + + xor %r14,%r13 # Sigma1(e) + xor %r11,%r15 # Ch(e,f,g)=((f^g)&e)^g + add %rax,%r12 # T1+=h + + mov %rbx,%rax + add %r13,%r12 # T1+=Sigma1(e) + + add %r15,%r12 # T1+=Ch(e,f,g) + mov %rbx,%r13 + mov %rbx,%r14 + + ror $28,%rax + ror $34,%r13 + mov %rbx,%r15 + add (%rbp,%rdi,8),%r12 # T1+=K[round] + + xor %r13,%rax + ror $5,%r13 + or %rdx,%r14 # a|c + + xor %r13,%rax # h=Sigma0(a) + and %rdx,%r15 # a&c + add %r12,%r8 # d+=T1 + + and %rcx,%r14 # (a|c)&b + add %r12,%rax # h+=T1 + + or %r15,%r14 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1(%rdi),%rdi # round++ + + add %r14,%rax # h+=Maj(a,b,c) + cmp $80,%rdi + jb .Lrounds_16_xx + + mov 16*8+0*8(%rsp),%rdi + lea 16*8(%rsi),%rsi + + add 8*0(%rdi),%rax + add 8*1(%rdi),%rbx + add 8*2(%rdi),%rcx + add 8*3(%rdi),%rdx + add 8*4(%rdi),%r8 + add 8*5(%rdi),%r9 + add 8*6(%rdi),%r10 + add 8*7(%rdi),%r11 + + cmp 16*8+2*8(%rsp),%rsi + + mov %rax,8*0(%rdi) + mov %rbx,8*1(%rdi) + mov %rcx,8*2(%rdi) + mov %rdx,8*3(%rdi) + mov %r8,8*4(%rdi) + mov %r9,8*5(%rdi) + mov %r10,8*6(%rdi) + mov %r11,8*7(%rdi) + jb .Lloop + + mov 16*8+3*8(%rsp),%rsp + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %rbp + pop %rbx + + ret +SET_SIZE(SHA512TransformBlocks) + +.align 6, 0x90 +K512: + .quad 0x428a2f98d728ae22,0x7137449123ef65cd + .quad 0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc + .quad 0x3956c25bf348b538,0x59f111f1b605d019 + .quad 0x923f82a4af194f9b,0xab1c5ed5da6d8118 + .quad 0xd807aa98a3030242,0x12835b0145706fbe + .quad 0x243185be4ee4b28c,0x550c7dc3d5ffb4e2 + .quad 0x72be5d74f27b896f,0x80deb1fe3b1696b1 + .quad 0x9bdc06a725c71235,0xc19bf174cf692694 + .quad 0xe49b69c19ef14ad2,0xefbe4786384f25e3 + .quad 0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65 + .quad 0x2de92c6f592b0275,0x4a7484aa6ea6e483 + .quad 0x5cb0a9dcbd41fbd4,0x76f988da831153b5 + .quad 0x983e5152ee66dfab,0xa831c66d2db43210 + .quad 0xb00327c898fb213f,0xbf597fc7beef0ee4 + .quad 0xc6e00bf33da88fc2,0xd5a79147930aa725 + .quad 0x06ca6351e003826f,0x142929670a0e6e70 + .quad 0x27b70a8546d22ffc,0x2e1b21385c26c926 + .quad 0x4d2c6dfc5ac42aed,0x53380d139d95b3df + .quad 0x650a73548baf63de,0x766a0abb3c77b2a8 + .quad 0x81c2c92e47edaee6,0x92722c851482353b + .quad 0xa2bfe8a14cf10364,0xa81a664bbc423001 + .quad 0xc24b8b70d0f89791,0xc76c51a30654be30 + .quad 0xd192e819d6ef5218,0xd69906245565a910 + .quad 0xf40e35855771202a,0x106aa07032bbd1b8 + .quad 0x19a4c116b8d2d0c8,0x1e376c085141ab53 + .quad 0x2748774cdf8eeb99,0x34b0bcb5e19b48a8 + .quad 0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb + .quad 0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3 + .quad 0x748f82ee5defb2fc,0x78a5636f43172f60 + .quad 0x84c87814a1f0ab72,0x8cc702081a6439ec + .quad 0x90befffa23631e28,0xa4506cebde82bde9 + .quad 0xbef9a3f7b2c67915,0xc67178f2e372532b + .quad 0xca273eceea26619c,0xd186b8c721c0c207 + .quad 0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178 + .quad 0x06f067aa72176fba,0x0a637dc5a2c898a6 + .quad 0x113f9804bef90dae,0x1b710b35131c471b + .quad 0x28db77f523047d84,0x32caab7b40c72493 + .quad 0x3c9ebe0a15c9bebc,0x431d67c49c100d4c + .quad 0x4cc5d4becb3e42b6,0x597f299cfc657e2a + .quad 0x5fcb6fab3ad6faec,0x6c44198c4a475817 +#endif /* !lint && !__lint */ diff --git a/module/lua/setjmp/setjmp_aarch64.S b/module/lua/setjmp/setjmp_aarch64.S index a5a9a85fd57e..5a9dfe188af1 100644 --- a/module/lua/setjmp/setjmp_aarch64.S +++ b/module/lua/setjmp/setjmp_aarch64.S @@ -39,9 +39,12 @@ .type sym,#function; \ sym: +#ifdef __APPLE__ +#define END(sym) +#else #define END(sym) \ .size sym, . - sym - +#endif ENTRY(setjmp) /* Store the stack pointer */ diff --git a/module/os/linux/spl/spl-taskq.c b/module/os/linux/spl/spl-taskq.c index 57e2458b7e94..23935ef8caa3 100644 --- a/module/os/linux/spl/spl-taskq.c +++ b/module/os/linux/spl/spl-taskq.c @@ -1225,7 +1225,8 @@ taskq_destroy(taskq_t *tq) } EXPORT_SYMBOL(taskq_destroy); -int EMPTY_TASKQ(taskq_t *tq) +int +EMPTY_TASKQ(taskq_t *tq) { return (tq->tq_lowest_id == tq->tq_next_id); } diff --git a/module/os/macos/.gitignore b/module/os/macos/.gitignore new file mode 100644 index 000000000000..14140f47af8b --- /dev/null +++ b/module/os/macos/.gitignore @@ -0,0 +1 @@ +*.in diff --git a/module/os/macos/Makefile.am b/module/os/macos/Makefile.am new file mode 100644 index 000000000000..933094981fc8 --- /dev/null +++ b/module/os/macos/Makefile.am @@ -0,0 +1,6 @@ +# Makefile used only by macOS. Should define no dependencies for +# other platforms. + +if BUILD_MACOS +SUBDIRS=spl zfs +endif diff --git a/module/os/macos/README.md b/module/os/macos/README.md new file mode 100644 index 000000000000..45c21cf4a065 --- /dev/null +++ b/module/os/macos/README.md @@ -0,0 +1,8 @@ + +OpenZFS on OS X, the [macOS](https://openzfsonosx.org) port of [Open ZFS](https://openzfs.org) + +Please use the [OpenZFSOnOsX](https://github.com/openzfsonosx/openzfs) +repository for support, troubleshooting, and using GitHub issues. + +For more compiling information please visit the +[wiki](https://openzfsonosx.org/wiki/Install#Initial_installation_from_source) diff --git a/module/os/macos/spl/Makefile.am b/module/os/macos/spl/Makefile.am new file mode 100644 index 000000000000..fa709d204b46 --- /dev/null +++ b/module/os/macos/spl/Makefile.am @@ -0,0 +1,69 @@ + +# Anyone remember why we made this a library? +libspl_la_CPPFLAGS= \ + -Wall \ + -nostdinc \ + -mkernel \ + -fno-builtin-printf \ + -D_KERNEL \ + -DKERNEL \ + -DKERNEL_PRIVATE \ + -DDRIVER_PRIVATE \ + -D__DARWIN_64_BIT_INO_T=1 \ + -DAPPLE \ + -DNeXT \ + -include $(top_builddir)/zfs_config.h \ + -I$(top_srcdir)/include/os/macos/spl \ + -I$(top_srcdir)/include \ + -I@KERNEL_HEADERS@/Headers \ + -I@KERNEL_HEADERS@/PrivateHeaders + +libspl_la_CPPFLAGS += @KERNEL_DEBUG_CPPFLAGS@ + +libspl_la_LDFLAGS= \ + -Xlinker \ + -kext \ + -nostdlib \ + -lkmodc++ \ + -lkmod \ + -lcc_kext + +if TARGET_CPU_AARCH64 +libspl_la_CPPFLAGS+=-arch arm64e +libspl_la_LDFLAGS+=-arch arm64e +endif + +libspl_la_LIBS = -lnone + +# If we don't set this to nothing, it adds "-lz -liconv" +LIBS = + +noinst_LTLIBRARIES = libspl.la + +libspl_la_SOURCES = \ + spl-atomic.c \ + spl-condvar.c \ + spl-cred.c \ + spl-debug.c \ + spl-ddi.c \ + spl-err.c \ + spl-kmem.c \ + spl-kstat.c \ + spl-list.c \ + spl-mutex.c \ + spl-osx.c \ + spl-policy.c \ + spl-proc.c \ + spl-processor.c \ + spl-proc_list.c \ + spl-qsort.c \ + spl-rwlock.c \ + spl-seg_kmem.c \ + spl-taskq.c \ + spl-thread.c \ + spl-time.c \ + spl-tsd.c \ + spl-uio.c \ + spl-vmem.c \ + spl-vnode.c \ + spl-xdr.c diff --git a/module/os/macos/spl/README.md b/module/os/macos/spl/README.md new file mode 100644 index 000000000000..cc8dbf288e46 --- /dev/null +++ b/module/os/macos/spl/README.md @@ -0,0 +1,14 @@ +The Solaris Porting Layer, SPL, is a macOS kernel module which provides a +compatibility layer used by the macOS port of Open ZFS. + +# Installation + +The latest version of the SPL is maintained as part of this repository. +Only when building ZFS version 1.9.4 or earlier must an external SPL release +be used. These releases can be found at: + + * Version 1.9.4: https://github.com/openzfsonosx/spl/tree/spl-1.9.4-release + +# Release + +The SPL is released under a CDDL license. diff --git a/module/os/macos/spl/spl-atomic.c b/module/os/macos/spl/spl-atomic.c new file mode 100644 index 000000000000..973f462fdfa2 --- /dev/null +++ b/module/os/macos/spl/spl-atomic.c @@ -0,0 +1,50 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * Solaris Porting Layer (SPL) Atomic Implementation. + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#include +#include +#include + + +#include +#include +#include +#include + +void * +atomic_cas_ptr(volatile void *target, void *cmp, void *new) +{ +#ifdef __LP64__ + return (void *)__sync_val_compare_and_swap((uint64_t *)target, + (uint64_t)cmp, (uint64_t)new); +#else + return (void *)__sync_val_compare_and_swap((uint32_t *)target, cmp, + new); +#endif +} diff --git a/module/os/macos/spl/spl-condvar.c b/module/os/macos/spl/spl-condvar.c new file mode 100644 index 000000000000..f95c1b9ade39 --- /dev/null +++ b/module/os/macos/spl/spl-condvar.c @@ -0,0 +1,252 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013, 2020 Jorgen Lundman + * + */ + +#include +#include +#include + +extern wait_result_t thread_block(thread_continue_t continuation); + +/* + * cv_timedwait() is similar to cv_wait() except that it additionally expects + * a timeout value specified in ticks. When woken by cv_signal() or + * cv_broadcast() it returns 1, otherwise when the timeout is reached -1 is + * returned. + * + * cv_timedwait_sig() behaves the same as cv_timedwait() but blocks + * interruptibly and can be woken by a signal (EINTR, ERESTART). When + * this occurs 0 is returned. + * + * cv_timedwait_io() and cv_timedwait_sig_io() are variants of cv_timedwait() + * and cv_timedwait_sig() which should be used when waiting for outstanding + * IO to complete. They are responsible for updating the iowait accounting + * when this is supported by the platform. + * + * cv_timedwait_hires() and cv_timedwait_sig_hires() are high resolution + * versions of cv_timedwait() and cv_timedwait_sig(). They expect the timeout + * to be specified as a hrtime_t allowing for timeouts of less than a tick. + * + * N.B. The return values differ slightly from the illumos implementation + * which returns the time remaining, instead of 1, when woken. They both + * return -1 on timeout. Consumers which need to know the time remaining + * are responsible for tracking it themselves. + */ + +#ifdef SPL_DEBUG_MUTEX +void spl_wdlist_settime(void *mpleak, uint64_t value); +#endif + +void +spl_cv_init(kcondvar_t *cvp, char *name, kcv_type_t type, void *arg) +{ +} + +void +spl_cv_destroy(kcondvar_t *cvp) +{ +} + +void +spl_cv_signal(kcondvar_t *cvp) +{ + wakeup_one((caddr_t)cvp); +} + +void +spl_cv_broadcast(kcondvar_t *cvp) +{ + wakeup((caddr_t)cvp); +} + + +/* + * Block on the indicated condition variable and + * release the associated mutex while blocked. + */ +int +spl_cv_wait(kcondvar_t *cvp, kmutex_t *mp, int flags, const char *msg) +{ + int result; + + if (msg != NULL && msg[0] == '&') + ++msg; /* skip over '&' prefixes */ + +#ifdef SPL_DEBUG_MUTEX + spl_wdlist_settime(mp->leak, 0); +#endif + mp->m_owner = NULL; + atomic_inc_64(&mp->m_sleepers); + result = msleep(cvp, (lck_mtx_t *)&mp->m_lock, flags, msg, 0); + atomic_dec_64(&mp->m_sleepers); + mp->m_owner = current_thread(); +#ifdef SPL_DEBUG_MUTEX + spl_wdlist_settime(mp->leak, gethrestime_sec()); +#endif + + /* + * If already signalled, XNU never releases mutex, so + * do so manually if we know there are threads waiting. + * Avoids a starvation in bqueue_dequeue(). + * Does timedwait() versions need the same? + */ + if (result == EINTR && + (mp->m_waiters > 0 || mp->m_sleepers > 0)) { + mutex_exit(mp); + (void) thread_block(THREAD_CONTINUE_NULL); + mutex_enter(mp); + } + + /* + * 1 - condvar got cv_signal()/cv_broadcast() + * 0 - received signal (kill -signal) + */ + return (result == EINTR ? 0 : 1); +} + +/* + * Same as cv_wait except the thread will unblock at 'tim' + * (an absolute time) if it hasn't already unblocked. + * + * Returns the amount of time left from the original 'tim' value + * when it was unblocked. + */ +int +spl_cv_timedwait(kcondvar_t *cvp, kmutex_t *mp, clock_t tim, int flags, + const char *msg) +{ + struct timespec ts; + int result; + + if (msg != NULL && msg[0] == '&') + ++msg; /* skip over '&' prefixes */ + + clock_t timenow = zfs_lbolt(); + + /* Already expired? */ + if (timenow >= tim) + return (-1); + + tim -= timenow; + + ts.tv_sec = (tim / hz); + ts.tv_nsec = (tim % hz) * NSEC_PER_SEC / hz; + + /* Both sec and nsec zero is a blocking call in XNU. (Not poll) */ + if (ts.tv_sec == 0 && ts.tv_nsec == 0) + ts.tv_nsec = 1000; + +#ifdef SPL_DEBUG_MUTEX + spl_wdlist_settime(mp->leak, 0); +#endif + + mp->m_owner = NULL; + atomic_inc_64(&mp->m_sleepers); + result = msleep(cvp, (lck_mtx_t *)&mp->m_lock, flags, msg, &ts); + atomic_dec_64(&mp->m_sleepers); + + mp->m_owner = current_thread(); + +#ifdef SPL_DEBUG_MUTEX + spl_wdlist_settime(mp->leak, gethrestime_sec()); +#endif + + switch (result) { + + case EINTR: /* Signal */ + case ERESTART: + return (0); + + case EWOULDBLOCK: /* Timeout: EAGAIN */ + return (-1); + } + + return (1); +} + + +/* + * Compatibility wrapper for the cv_timedwait_hires() Illumos interface. + */ +int +cv_timedwait_hires(kcondvar_t *cvp, kmutex_t *mp, hrtime_t tim, + hrtime_t res, int flag) +{ + struct timespec ts; + int result; + + if (res > 1) { + /* + * Align expiration to the specified resolution. + */ + if (flag & CALLOUT_FLAG_ROUNDUP) + tim += res - 1; + tim = (tim / res) * res; + } + + if ((flag & CALLOUT_FLAG_ABSOLUTE)) { + hrtime_t timenow = gethrtime(); + + /* Already expired? */ + if (timenow >= tim) + return (-1); + + tim -= timenow; + } + + ts.tv_sec = NSEC2SEC(tim); + ts.tv_nsec = tim - SEC2NSEC(ts.tv_sec); + + /* Both sec and nsec set to zero is a blocking call in XNU. */ + if (ts.tv_sec == 0 && ts.tv_nsec == 0) + ts.tv_nsec = 1000; + +#ifdef SPL_DEBUG_MUTEX + spl_wdlist_settime(mp->leak, 0); +#endif + + mp->m_owner = NULL; + atomic_inc_64(&mp->m_sleepers); + result = msleep(cvp, (lck_mtx_t *)&mp->m_lock, + flag, "cv_timedwait_hires", &ts); + atomic_dec_64(&mp->m_sleepers); + mp->m_owner = current_thread(); +#ifdef SPL_DEBUG_MUTEX + spl_wdlist_settime(mp->leak, gethrestime_sec()); +#endif + + switch (result) { + + case EINTR: /* Signal */ + case ERESTART: + return (0); + + case EWOULDBLOCK: /* Timeout */ + return (-1); + } + + return (1); +} diff --git a/module/os/macos/spl/spl-cred.c b/module/os/macos/spl/spl-cred.c new file mode 100644 index 000000000000..282588564078 --- /dev/null +++ b/module/os/macos/spl/spl-cred.c @@ -0,0 +1,153 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2008 MacZFS + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#include +#include +#include + +/* Return the effective user id */ +uid_t +crgetuid(const cred_t *cr) +{ + if (!cr) + return (0); + return (kauth_cred_getuid((kauth_cred_t)cr)); +} + +/* Return the real user id */ +uid_t +crgetruid(const cred_t *cr) +{ + if (!cr) + return (0); + return (kauth_cred_getruid((kauth_cred_t)cr)); +} + +/* Return the saved user id */ +uid_t +crgetsuid(const cred_t *cr) +{ + if (!cr) + return (0); + return (kauth_cred_getsvuid((kauth_cred_t)cr)); +} + +/* Return the filesystem user id */ +uid_t +crgetfsuid(const cred_t *cr) +{ + if (!cr) + return (0); + return (-1); +} + +/* Return the effective group id */ +gid_t +crgetgid(const cred_t *cr) +{ + if (!cr) + return (0); + return (kauth_cred_getgid((kauth_cred_t)cr)); +} + +/* Return the real group id */ +gid_t +crgetrgid(const cred_t *cr) +{ + if (!cr) + return (0); + return (kauth_cred_getrgid((kauth_cred_t)cr)); +} + +/* Return the saved group id */ +gid_t +crgetsgid(const cred_t *cr) +{ + if (!cr) + return (0); + return (kauth_cred_getsvgid((kauth_cred_t)cr)); +} + +/* Return the filesystem group id */ +gid_t +crgetfsgid(const cred_t *cr) +{ + return (-1); +} + + +extern int kauth_cred_getgroups(kauth_cred_t _cred, gid_t *_groups, + int *_groupcount); +/* + * Unfortunately, to get the count of groups, we have to call XNU which + * memcpy's them over. No real clean way to get around that, but at least + * these calls are done sparingly. + * dsl_deleg.c: dsl_check_user_access() loops the gid the user is in + * to call dsl_check_access(gid) to see if "zfs allow" matches. + * If we can iterate the gids saved in mos, and test with + * kauth_cred_ismember_gid() the equivalent can be achieved. + * However, "zfs allow" does not yet work of macOS. + */ +int +crgetngroups(const cred_t *cr) +{ + return (0); +} + + +/* + * We always allocate NGROUPs here, since we don't know how many there will + * be until after the call. Unlike IllumOS, the ptr returned is allocated + * and must be returned by a call to crgetgroupsfree(). + */ +gid_t * +crgetgroups(const cred_t *cr) +{ + return (NULL); +} + +void +crgetgroupsfree(gid_t *gids) +{ + if (!gids) + return; + kmem_free(gids, sizeof (gid_t) * NGROUPS); +} + +/* + * Return true if "cr" belongs in group "gid". + */ +int +spl_cred_ismember_gid(cred_t *cr, gid_t gid) +{ + int ret = 0; // Is not member. + kauth_cred_ismember_gid((kauth_cred_t)cr, gid, &ret); + if (ret == 1) + return (TRUE); + return (FALSE); +} diff --git a/module/os/macos/spl/spl-ddi.c b/module/os/macos/spl/spl-ddi.c new file mode 100644 index 000000000000..a64b21186c43 --- /dev/null +++ b/module/os/macos/spl/spl-ddi.c @@ -0,0 +1,408 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#include +#include +#include +#include +#include + + +/* + * Allocate a set of pointers to 'n_items' objects of size 'size' + * bytes. Each pointer is initialized to nil. + * + * The 'size' and 'n_items' values are stashed in the opaque + * handle returned to the caller. + * + * This implementation interprets 'set of pointers' to mean 'array + * of pointers' but note that nothing in the interface definition + * precludes an implementation that uses, for example, a linked list. + * However there should be a small efficiency gain from using an array + * at lookup time. + * + * NOTE As an optimization, we make our growable array allocations in + * powers of two (bytes), since that's how much kmem_alloc (currently) + * gives us anyway. It should save us some free/realloc's .. + * + * As a further optimization, we make the growable array start out + * with MIN_N_ITEMS in it. + */ + + +int +ddi_soft_state_init(void **state_p, size_t size, size_t n_items) +{ + struct i_ddi_soft_state *ss; + + if (state_p == NULL || *state_p != NULL || size == 0) + return (EINVAL); + + ss = kmem_zalloc(sizeof (*ss), KM_SLEEP); + mutex_init(&ss->lock, NULL, MUTEX_DRIVER, NULL); + ss->size = size; + + if (n_items < MIN_N_ITEMS) + ss->n_items = MIN_N_ITEMS; + else { + int bitlog; + + if ((bitlog = ddi_fls(n_items)) == ddi_ffs(n_items)) + bitlog--; + ss->n_items = 1 << bitlog; + } + + ASSERT(ss->n_items >= n_items); + + ss->array = kmem_zalloc(ss->n_items * sizeof (void *), KM_SLEEP); + + *state_p = ss; + + return (0); +} + + +/* + * Allocate a state structure of size 'size' to be associated + * with item 'item'. + * + * In this implementation, the array is extended to + * allow the requested offset, if needed. + */ +int +ddi_soft_state_zalloc(void *state, int item) +{ + struct i_ddi_soft_state *ss; + void **array; + void *new_element; + + if ((ss = state) == NULL || item < 0) + return (DDI_FAILURE); + + mutex_enter(&ss->lock); + if (ss->size == 0) { + mutex_exit(&ss->lock); + cmn_err(CE_WARN, "ddi_soft_state_zalloc: bad handle"); + return (DDI_FAILURE); + } + + array = ss->array; /* NULL if ss->n_items == 0 */ + ASSERT(ss->n_items != 0 && array != NULL); + + /* + * refuse to tread on an existing element + */ + if (item < ss->n_items && array[item] != NULL) { + mutex_exit(&ss->lock); + return (DDI_FAILURE); + } + + /* + * Allocate a new element to plug in + */ + new_element = kmem_zalloc(ss->size, KM_SLEEP); + + /* + * Check if the array is big enough, if not, grow it. + */ + if (item >= ss->n_items) { + void **new_array; + size_t new_n_items; + struct i_ddi_soft_state *dirty; + + /* + * Allocate a new array of the right length, copy + * all the old pointers to the new array, then + * if it exists at all, put the old array on the + * dirty list. + * + * Note that we can't kmem_free() the old array. + * + * Why -- well the 'get' operation is 'mutex-free', so we + * can't easily catch a suspended thread that is just about + * to dereference the array we just grew out of. So we + * cons up a header and put it on a list of 'dirty' + * pointer arrays. (Dirty in the sense that there may + * be suspended threads somewhere that are in the middle + * of referencing them). Fortunately, we -can- garbage + * collect it all at ddi_soft_state_fini time. + */ + new_n_items = ss->n_items; + while (new_n_items < (1 + item)) + new_n_items <<= 1; /* double array size .. */ + + ASSERT(new_n_items >= (1 + item)); /* sanity check! */ + + new_array = kmem_zalloc(new_n_items * sizeof (void *), + KM_SLEEP); + /* + * Copy the pointers into the new array + */ + bcopy(array, new_array, ss->n_items * sizeof (void *)); + + /* + * Save the old array on the dirty list + */ + dirty = kmem_zalloc(sizeof (*dirty), KM_SLEEP); + dirty->array = ss->array; + dirty->n_items = ss->n_items; + dirty->next = ss->next; + ss->next = dirty; + + ss->array = (array = new_array); + ss->n_items = new_n_items; + } + + ASSERT(array != NULL && item < ss->n_items && array[item] == NULL); + + array[item] = new_element; + + mutex_exit(&ss->lock); + return (DDI_SUCCESS); +} + + +/* + * Fetch a pointer to the allocated soft state structure. + * + * This is designed to be cheap. + * + * There's an argument that there should be more checking for + * nil pointers and out of bounds on the array.. but we do a lot + * of that in the alloc/free routines. + * + * An array has the convenience that we don't need to lock read-access + * to it c.f. a linked list. However our "expanding array" strategy + * means that we should hold a readers lock on the i_ddi_soft_state + * structure. + * + * However, from a performance viewpoint, we need to do it without + * any locks at all -- this also makes it a leaf routine. The algorithm + * is 'lock-free' because we only discard the pointer arrays at + * ddi_soft_state_fini() time. + */ +void * +ddi_get_soft_state(void *state, int item) +{ + struct i_ddi_soft_state *ss = state; + + ASSERT(ss != NULL && item >= 0); + + if (item < ss->n_items && ss->array != NULL) + return (ss->array[item]); + return (NULL); +} + +/* + * Free the state structure corresponding to 'item.' Freeing an + * element that has either gone or was never allocated is not + * considered an error. Note that we free the state structure, but + * we don't shrink our pointer array, or discard 'dirty' arrays, + * since even a few pointers don't really waste too much memory. + * + * Passing an item number that is out of bounds, or a null pointer will + * provoke an error message. + */ +void +ddi_soft_state_free(void *state, int item) +{ + struct i_ddi_soft_state *ss; + void **array; + void *element; + static char msg[] = "ddi_soft_state_free:"; + + if ((ss = state) == NULL) { + cmn_err(CE_WARN, "%s null handle", + msg); + return; + } + + element = NULL; + + mutex_enter(&ss->lock); + + if ((array = ss->array) == NULL || ss->size == 0) { + cmn_err(CE_WARN, "%s bad handle", + msg); + } else if (item < 0 || item >= ss->n_items) { + cmn_err(CE_WARN, "%s item %d not in range [0..%lu]", + msg, item, ss->n_items - 1); + } else if (array[item] != NULL) { + element = array[item]; + array[item] = NULL; + } + + mutex_exit(&ss->lock); + + if (element) + kmem_free(element, ss->size); +} + + +/* + * Free the entire set of pointers, and any + * soft state structures contained therein. + * + * Note that we don't grab the ss->lock mutex, even though + * we're inspecting the various fields of the data structure. + * + * There is an implicit assumption that this routine will + * never run concurrently with any of the above on this + * particular state structure i.e. by the time the driver + * calls this routine, there should be no other threads + * running in the driver. + */ +void +ddi_soft_state_fini(void **state_p) +{ + struct i_ddi_soft_state *ss, *dirty; + int item; + static char msg[] = "ddi_soft_state_fini:"; + + if (state_p == NULL || (ss = *state_p) == NULL) + return; + + if (ss->size == 0) { + cmn_err(CE_WARN, "%s bad handle", + msg); + return; + } + + if (ss->n_items > 0) { + for (item = 0; item < ss->n_items; item++) + ddi_soft_state_free(ss, item); + kmem_free(ss->array, ss->n_items * sizeof (void *)); + } + + /* + * Now delete any dirty arrays from previous 'grow' operations + */ + for (dirty = ss->next; dirty; dirty = ss->next) { + ss->next = dirty->next; + kmem_free(dirty->array, dirty->n_items * sizeof (void *)); + kmem_free(dirty, sizeof (*dirty)); + } + + mutex_destroy(&ss->lock); + kmem_free(ss, sizeof (*ss)); + + *state_p = NULL; +} + +int +ddi_create_minor_node(dev_info_t *dip, char *name, int spec_type, + minor_t minor_num, char *node_type, int flag) +{ + dev_t dev; + int error = 0; + char *r, *dup; + + dev = makedev(flag, minor_num); + dip->dev = dev; + + /* + * http://lists.apple.com/archives/darwin-kernel/2007/Nov/msg00038.html + * + * devfs_make_name() has an off-by-one error when using directories + * and it appears Apple does not want to fix it. + * + * We then change "/" to "_" and create more Apple-like /dev names + * + */ + MALLOC(dup, char *, strlen(name)+1, M_TEMP, M_WAITOK); + if (dup == NULL) + return (ENOMEM); + bcopy(name, dup, strlen(name)); + dup[strlen(name)] = '\0'; + + for (r = dup; + (r = strchr(r, '/')); + *r = '_') + /* empty */; + + dip->devc = NULL; + dip->devb = NULL; + + if (spec_type == S_IFCHR) + dip->devc = devfs_make_node(dev, DEVFS_CHAR, + UID_ROOT, GID_OPERATOR, + 0600, "rdisk_%s", dup); + else + dip->devb = devfs_make_node(dev, DEVFS_BLOCK, + UID_ROOT, GID_OPERATOR, + 0600, "disk_%s", dup); + FREE(dup, M_TEMP); + + return (error); +} + +void +ddi_remove_minor_node(dev_info_t *dip, char *name) +{ + if (dip->devc) { + devfs_remove(dip->devc); + dip->devc = NULL; + } + if (dip->devb) { + devfs_remove(dip->devb); + dip->devb = NULL; + } +} + +int +strspn(const char *string, + register char *charset) +{ + register const char *p, *q; + + for (q = string; *q != '\0'; ++q) { + for (p = charset; *p != '\0' && *p != *q; ++p) + ; + if (*p == '\0') + break; + } + return (q-string); +} + +#undef strcmp +int +spl_strcmp(const char *s1, const char *s2) +{ + char c1, c2; + + while (1) { + c1 = *s1++; + c2 = *s2++; + + if ((c1 == 0) && + (c2 == 0)) + break; + + if ((c1 == 0) || + (c1 < c2)) + return (-1); + if ((c2 == 0) || + (c1 > c2)) + return (1); + } // while + + return (0); +} diff --git a/module/os/macos/spl/spl-debug.c b/module/os/macos/spl/spl-debug.c new file mode 100644 index 000000000000..28ec1612d432 --- /dev/null +++ b/module/os/macos/spl/spl-debug.c @@ -0,0 +1,10 @@ +#include + + + +/* Debug log support enabled */ +__attribute__((noinline)) int assfail(const char *str, const char *file, + unsigned int line) __attribute__((optnone)) +{ + return (1); /* Must return true for ASSERT macro */ +} diff --git a/module/os/macos/spl/spl-err.c b/module/os/macos/spl/spl-err.c new file mode 100644 index 000000000000..455bf2c8b9d6 --- /dev/null +++ b/module/os/macos/spl/spl-err.c @@ -0,0 +1,83 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013, 2020 Jorgen Lundman + * + */ + +#include +#include +#include + +void +vcmn_err(int ce, const char *fmt, va_list ap) +{ + char msg[MAXMSGLEN]; + + vsnprintf(msg, MAXMSGLEN - 1, fmt, ap); + + switch (ce) { + case CE_IGNORE: + break; + case CE_CONT: + printf("%s", msg); + break; + case CE_NOTE: + printf("SPL: Notice: %s\n", msg); + break; + case CE_WARN: + printf("SPL: Warning: %s\n", msg); + break; + case CE_PANIC: + PANIC("%s", msg); + break; + } +} /* vcmn_err() */ + +void +cmn_err(int ce, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vcmn_err(ce, fmt, ap); + va_end(ap); +} /* cmn_err() */ + + +int +spl_panic(const char *file, const char *func, int line, const char *fmt, ...) +{ + char msg[MAXMSGLEN]; + va_list ap; + + va_start(ap, fmt); + (void) vsnprintf(msg, sizeof (msg), fmt, ap); + va_end(ap); + + printf("%s", msg); + panic("%s", msg); + + /* Unreachable */ + return (1); +} diff --git a/module/os/macos/spl/spl-kmem.c b/module/os/macos/spl/spl-kmem.c new file mode 100644 index 000000000000..4fb186b0455d --- /dev/null +++ b/module/os/macos/spl/spl-kmem.c @@ -0,0 +1,6524 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2008 MacZFS + * Copyright (C) 2013, 2020 Jorgen Lundman + * Copyright (C) 2014 Brendon Humphrey + * Copyright (C) 2017 Sean Doran + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// =============================================================== +// Options +// =============================================================== +// #define PRINT_CACHE_STATS 1 + +// =============================================================== +// OS Interface +// =============================================================== + +// 3500 kern.spl_vm_page_free_min, rarely changes +const unsigned int spl_vm_page_free_min = 3500; + +#define SMALL_PRESSURE_INCURSION_PAGES (spl_vm_page_free_min >> 5) + +static kcondvar_t spl_free_thread_cv; +static kmutex_t spl_free_thread_lock; +static boolean_t spl_free_thread_exit; +static volatile _Atomic int64_t spl_free; +int64_t spl_free_delta_ema; + +static volatile _Atomic int64_t spl_free_manual_pressure = 0; +static volatile _Atomic boolean_t spl_free_fast_pressure = FALSE; +static _Atomic bool spl_free_maybe_reap_flag = false; +static _Atomic uint64_t spl_free_last_pressure = 0; + +/* + * variables informed by "pure" mach_vm_pressure interface + * + * osfmk/vm/vm_pageout.c: "We don't need fully + * accurate monitoring anyway..." + * + * but in macOS_pure we do want modifications of these + * variables to be seen by all the other threads + * consistently, and asap (there may be hundreds + * of simultaneous readers, even if few writers!) + */ +_Atomic uint32_t spl_vm_pages_reclaimed = 0; +_Atomic uint32_t spl_vm_pages_wanted = 0; +_Atomic uint32_t spl_vm_pressure_level = 0; + +/* From osfmk/vm/vm_pageout.h */ +extern kern_return_t mach_vm_pressure_level_monitor( + boolean_t wait_for_pressure, unsigned int *pressure_level); +extern kern_return_t mach_vm_pressure_monitor( + boolean_t wait_for_pressure, + unsigned int nsecs_monitored, + unsigned int *pages_reclaimed_p, + unsigned int *pages_wanted_p); + +/* + * the spl_pressure_level enum only goes to four, + * but we want to watch kstat for whether + * mach's pressure is unavailable + */ +#define MAGIC_PRESSURE_UNAVAILABLE 1001001 + +// Start and end address of kernel memory +extern vm_offset_t virtual_space_start; +extern vm_offset_t virtual_space_end; + +// Can be polled to determine if the VM is experiecing +// a shortage of free pages. +extern int vm_pool_low(void); + +// Which CPU are we executing on? +extern int cpu_number(void); + +// Invoke the kernel debugger +extern void Debugger(const char *message); + +// Read from /dev/random +void read_random(void *buffer, uint_t numbytes); + +// =============================================================== +// Non Illumos Variables +// =============================================================== + +// Flag to cause tasks and threads to terminate as +// the kmem module is preparing to unload. +static int shutting_down = 0; + +// Amount of RAM in machine +uint64_t physmem = 0; + +// Size in bytes of the memory allocated in seg_kmem +extern uint64_t segkmem_total_mem_allocated; + +// Number of active threads +extern uint64_t zfs_threads; +extern uint64_t zfs_active_mutex; +extern uint64_t zfs_active_rwlock; + +extern uint64_t total_memory; +extern uint64_t real_total_memory; + +#define MULT 1 + +static const char *KMEM_VA_PREFIX = "kmem_va"; +static const char *KMEM_MAGAZINE_PREFIX = "kmem_magazine_"; + +// =============================================================== +// Illumos Variables +// =============================================================== + +struct kmem_cache_kstat { + kstat_named_t kmc_buf_size; + kstat_named_t kmc_align; + kstat_named_t kmc_chunk_size; + kstat_named_t kmc_slab_size; + kstat_named_t kmc_alloc; + kstat_named_t kmc_alloc_fail; + kstat_named_t kmc_free; + kstat_named_t kmc_depot_alloc; + kstat_named_t kmc_depot_free; + kstat_named_t kmc_depot_contention; + kstat_named_t kmc_slab_alloc; + kstat_named_t kmc_slab_free; + kstat_named_t kmc_buf_constructed; + kstat_named_t kmc_buf_avail; + kstat_named_t kmc_buf_inuse; + kstat_named_t kmc_buf_total; + kstat_named_t kmc_buf_max; + kstat_named_t kmc_slab_create; + kstat_named_t kmc_slab_destroy; + kstat_named_t kmc_vmem_source; + kstat_named_t kmc_hash_size; + kstat_named_t kmc_hash_lookup_depth; + kstat_named_t kmc_hash_rescale; + kstat_named_t kmc_full_magazines; + kstat_named_t kmc_empty_magazines; + kstat_named_t kmc_magazine_size; + kstat_named_t kmc_reap; /* number of kmem_cache_reap() calls */ + kstat_named_t kmc_defrag; /* attempts to defrag all partial slabs */ + kstat_named_t kmc_scan; /* attempts to defrag one partial slab */ + kstat_named_t kmc_move_callbacks; /* sum of yes, no, later, dn, dk */ + kstat_named_t kmc_move_yes; + kstat_named_t kmc_move_no; + kstat_named_t kmc_move_later; + kstat_named_t kmc_move_dont_need; + kstat_named_t kmc_move_dont_know; /* obj unrecognized by client ... */ + kstat_named_t kmc_move_hunt_found; /* ... but found in mag layer */ + kstat_named_t kmc_move_slabs_freed; /* slabs freed by consolidator */ + kstat_named_t kmc_move_reclaimable; /* buffers, if consolidator ran */ + kstat_named_t kmc_no_vba_success; + kstat_named_t kmc_no_vba_fail; + kstat_named_t kmc_arc_no_grow_set; + kstat_named_t kmc_arc_no_grow; +} kmem_cache_kstat = { + { "buf_size", KSTAT_DATA_UINT64 }, + { "align", KSTAT_DATA_UINT64 }, + { "chunk_size", KSTAT_DATA_UINT64 }, + { "slab_size", KSTAT_DATA_UINT64 }, + { "alloc", KSTAT_DATA_UINT64 }, + { "alloc_fail", KSTAT_DATA_UINT64 }, + { "free", KSTAT_DATA_UINT64 }, + { "depot_alloc", KSTAT_DATA_UINT64 }, + { "depot_free", KSTAT_DATA_UINT64 }, + { "depot_contention", KSTAT_DATA_UINT64 }, + { "slab_alloc", KSTAT_DATA_UINT64 }, + { "slab_free", KSTAT_DATA_UINT64 }, + { "buf_constructed", KSTAT_DATA_UINT64 }, + { "buf_avail", KSTAT_DATA_UINT64 }, + { "buf_inuse", KSTAT_DATA_UINT64 }, + { "buf_total", KSTAT_DATA_UINT64 }, + { "buf_max", KSTAT_DATA_UINT64 }, + { "slab_create", KSTAT_DATA_UINT64 }, + { "slab_destroy", KSTAT_DATA_UINT64 }, + { "vmem_source", KSTAT_DATA_UINT64 }, + { "hash_size", KSTAT_DATA_UINT64 }, + { "hash_lookup_depth", KSTAT_DATA_UINT64 }, + { "hash_rescale", KSTAT_DATA_UINT64 }, + { "full_magazines", KSTAT_DATA_UINT64 }, + { "empty_magazines", KSTAT_DATA_UINT64 }, + { "magazine_size", KSTAT_DATA_UINT64 }, + { "reap", KSTAT_DATA_UINT64 }, + { "defrag", KSTAT_DATA_UINT64 }, + { "scan", KSTAT_DATA_UINT64 }, + { "move_callbacks", KSTAT_DATA_UINT64 }, + { "move_yes", KSTAT_DATA_UINT64 }, + { "move_no", KSTAT_DATA_UINT64 }, + { "move_later", KSTAT_DATA_UINT64 }, + { "move_dont_need", KSTAT_DATA_UINT64 }, + { "move_dont_know", KSTAT_DATA_UINT64 }, + { "move_hunt_found", KSTAT_DATA_UINT64 }, + { "move_slabs_freed", KSTAT_DATA_UINT64 }, + { "move_reclaimable", KSTAT_DATA_UINT64 }, + { "no_vba_success", KSTAT_DATA_UINT64 }, + { "no_vba_fail", KSTAT_DATA_UINT64 }, + { "arc_no_grow_set", KSTAT_DATA_UINT64 }, + { "arc_no_grow", KSTAT_DATA_UINT64 }, +}; + +static kmutex_t kmem_cache_kstat_lock; + +/* + * The default set of caches to back kmem_alloc(). + * These sizes should be reevaluated periodically. + * + * We want allocations that are multiples of the coherency granularity + * (64 bytes) to be satisfied from a cache which is a multiple of 64 + * bytes, so that it will be 64-byte aligned. For all multiples of 64, + * the next 1 greater than or equal to it must be a + * multiple of 64. + * + * We split the table into two sections: size <= 4k and size > 4k. This + * saves a lot of space and cache footprint in our cache tables. + */ +static const int kmem_alloc_sizes[] = { + 1 * 8, + 2 * 8, + 3 * 8, + 4 * 8, 5 * 8, 6 * 8, 7 * 8, + 4 * 16, 5 * 16, 6 * 16, 7 * 16, + 4 * 32, 5 * 32, 6 * 32, 7 * 32, + 4 * 64, 5 * 64, 6 * 64, 7 * 64, + 4 * 128, 9*64, 5 * 128, 6 * 128, 13*64, 7 * 128, + P2ALIGN(8192 / 8, 64), + P2ALIGN(8192 / 7, 64), + P2ALIGN(8192 / 6, 64), + P2ALIGN(8192 / 5, 64), + P2ALIGN(8192 / 4, 64), + P2ALIGN(8192 / 3, 64), + P2ALIGN(8192 / 2, 64), +}; + +static const int kmem_big_alloc_sizes[] = { + 2 * 4096, 3 * 4096, + 2 * 8192, 3 * 8192, + 4 * 8192, 5 * 8192, 6 * 8192, 7 * 8192, + 8 * 8192, 9 * 8192, 10 * 8192, 11 * 8192, + 12 * 8192, 13 * 8192, 14 * 8192, 15 * 8192, + 16 * 8192 +}; + +#define KMEM_MAXBUF 4096 +#define KMEM_BIG_MAXBUF_32BIT 32768 +#define KMEM_BIG_MAXBUF 131072 + +#define KMEM_BIG_MULTIPLE 4096 /* big_alloc_sizes must be a multiple */ +#define KMEM_BIG_SHIFT 12 /* lg(KMEM_BIG_MULTIPLE) */ + +static kmem_cache_t *kmem_alloc_table[KMEM_MAXBUF >> KMEM_ALIGN_SHIFT]; +static kmem_cache_t *kmem_big_alloc_table[KMEM_BIG_MAXBUF >> KMEM_BIG_SHIFT]; + +#define KMEM_ALLOC_TABLE_MAX (KMEM_MAXBUF >> KMEM_ALIGN_SHIFT) +static size_t kmem_big_alloc_table_max = 0; /* # of filled elements */ + +static kmem_magtype_t kmem_magtype[] = { + { 1, 8, 3200, 65536 }, + { 3, 16, 256, 32768 }, + { 7, 32, 64, 16384 }, + { 15, 64, 0, 8192 }, + { 31, 64, 0, 4096 }, + { 47, 64, 0, 2048 }, + { 63, 64, 0, 1024 }, + { 95, 64, 0, 512 }, + { 143, 64, 0, 0 }, +}; + +static uint32_t kmem_reaping; +static uint32_t kmem_reaping_idspace; + +/* + * kmem tunables + */ +static struct timespec kmem_reap_interval = {15, 0}; +int kmem_depot_contention = 3; /* max failed tryenters per real interval */ +pgcnt_t kmem_reapahead = 0; /* start reaping N pages before pageout */ +int kmem_panic = 1; /* whether to panic on error */ +int kmem_logging = 0; /* kmem_log_enter() override */ +uint32_t kmem_mtbf = 0; /* mean time between failures [default: off] */ +size_t kmem_transaction_log_size; /* transaction log size [2% of memory] */ +size_t kmem_content_log_size; /* content log size [2% of memory] */ +size_t kmem_failure_log_size; /* failure log [4 pages per CPU] */ +size_t kmem_slab_log_size; /* slab create log [4 pages per CPU] */ +size_t kmem_content_maxsave = 256; /* KMF_CONTENTS max bytes to log */ +size_t kmem_lite_minsize = 0; /* minimum buffer size for KMF_LITE */ +size_t kmem_lite_maxalign = 8192; /* maximum buffer alignment for KMF_LITE */ +int kmem_lite_pcs = 4; /* number of PCs to store in KMF_LITE mode */ +size_t kmem_maxverify; /* maximum bytes to inspect in debug routines */ +size_t kmem_minfirewall; /* hardware-enforced redzone threshold */ + +size_t kmem_max_cached = KMEM_BIG_MAXBUF; /* maximum kmem_alloc cache */ + +/* + * Be aware that KMF_AUDIT does not release memory, and you will eventually + * grind to a halt. But it is useful to enable if you can trigger a memory + * fault, and wish to see the calling stack. + */ +#ifdef DEBUG +// can be 0 or KMF_LITE +// or KMF_DEADBEEF | KMF_REDZONE | KMF_CONTENTS +// with or without KMF_AUDIT +int kmem_flags = KMF_LITE; +#else +int kmem_flags = 0; +#endif +int kmem_ready; + +static kmem_cache_t *kmem_slab_cache; +static kmem_cache_t *kmem_bufctl_cache; +static kmem_cache_t *kmem_bufctl_audit_cache; + +static kmutex_t kmem_cache_lock; /* inter-cache linkage only */ +static list_t kmem_caches; +extern vmem_t *heap_arena; +static taskq_t *kmem_taskq; +static kmutex_t kmem_flags_lock; +static vmem_t *kmem_metadata_arena; +static vmem_t *kmem_msb_arena; /* arena for metadata caches */ +static vmem_t *kmem_cache_arena; +static vmem_t *kmem_hash_arena; +static vmem_t *kmem_log_arena; +static vmem_t *kmem_oversize_arena; +static vmem_t *kmem_va_arena; +static vmem_t *kmem_default_arena; +static vmem_t *kmem_firewall_arena; + +/* + * kmem slab consolidator thresholds (tunables) + */ +size_t kmem_frag_minslabs = 101; /* minimum total slabs */ +size_t kmem_frag_numer = 1; /* free buffers (numerator) */ +size_t kmem_frag_denom = KMEM_VOID_FRACTION; /* buffers (denominator) */ +/* + * Maximum number of slabs from which to move buffers during a single + * maintenance interval while the system is not low on memory. + */ +size_t kmem_reclaim_max_slabs = 4; // smd 1 +/* + * Number of slabs to scan backwards from the end of the partial slab list + * when searching for buffers to relocate. + */ +size_t kmem_reclaim_scan_range = 48; // smd 12 + +/* consolidator knobs */ +static boolean_t kmem_move_noreap; +static boolean_t kmem_move_blocked; +static boolean_t kmem_move_fulltilt; +static boolean_t kmem_move_any_partial; + +#ifdef DEBUG +/* + * kmem consolidator debug tunables: + * Ensure code coverage by occasionally running the consolidator even when the + * caches are not fragmented (they may never be). These intervals are mean time + * in cache maintenance intervals (kmem_cache_update). + */ +uint32_t kmem_mtb_move = 20; /* defrag 1 slab (~5min) */ +uint32_t kmem_mtb_reap = 240; /* defrag all slabs (~1hrs) */ +uint32_t kmem_mtb_reap_count = 0; +#endif /* DEBUG */ + +static kmem_cache_t *kmem_defrag_cache; +static kmem_cache_t *kmem_move_cache; +static taskq_t *kmem_move_taskq; + +static void kmem_cache_scan(kmem_cache_t *); +static void kmem_cache_defrag(kmem_cache_t *); +static void kmem_slab_prefill(kmem_cache_t *, kmem_slab_t *); + + +kmem_log_header_t *kmem_transaction_log; +kmem_log_header_t *kmem_content_log; +kmem_log_header_t *kmem_failure_log; +kmem_log_header_t *kmem_slab_log; + +static int kmem_lite_count; /* # of PCs in kmem_buftag_lite_t */ + +#define KMEM_BUFTAG_LITE_ENTER(bt, count, caller) \ +if ((count) > 0) { \ +pc_t *_s = ((kmem_buftag_lite_t *)(bt))->bt_history; \ +pc_t *_e; \ +/* memmove() the old entries down one notch */ \ +for (_e = &_s[(count) - 1]; _e > _s; _e--) \ +*_e = *(_e - 1); \ +*_s = (uintptr_t)(caller); \ +} + +#define KMERR_MODIFIED 0 /* buffer modified while on freelist */ +#define KMERR_REDZONE 1 /* redzone violation (write past end of buf) */ +#define KMERR_DUPFREE 2 /* freed a buffer twice */ +#define KMERR_BADADDR 3 /* freed a bad (unallocated) address */ +#define KMERR_BADBUFTAG 4 /* buftag corrupted */ +#define KMERR_BADBUFCTL 5 /* bufctl corrupted */ +#define KMERR_BADCACHE 6 /* freed a buffer to the wrong cache */ +#define KMERR_BADSIZE 7 /* alloc size != free size */ +#define KMERR_BADBASE 8 /* buffer base address wrong */ + +struct { + hrtime_t kmp_timestamp; /* timestamp of panic */ + int kmp_error; /* type of kmem error */ + void *kmp_buffer; /* buffer that induced panic */ + void *kmp_realbuf; /* real start address for buffer */ + kmem_cache_t *kmp_cache; /* buffer's cache according to client */ + kmem_cache_t *kmp_realcache; /* actual cache containing buffer */ + kmem_slab_t *kmp_slab; /* slab accoring to kmem_findslab() */ + kmem_bufctl_t *kmp_bufctl; /* bufctl */ +} kmem_panic_info; + +extern uint64_t stat_osif_malloc_success; +extern uint64_t stat_osif_malloc_bytes; +extern uint64_t stat_osif_free; +extern uint64_t stat_osif_free_bytes; + +extern uint64_t spl_bucket_non_pow2_allocs; + +// stats for spl_root_allocator(); +extern uint64_t spl_root_allocator_calls; +extern uint64_t spl_root_allocator_large_bytes_asked; +extern uint64_t spl_root_allocator_small_bytes_asked; +extern uint64_t spl_root_allocator_minalloc_bytes_asked; +extern uint64_t spl_root_allocator_extra_pass; +extern uint64_t spl_root_allocator_recovered; +extern uint64_t spl_root_allocator_recovered_bytes; + +extern uint64_t spl_vmem_unconditional_allocs; +extern uint64_t spl_vmem_unconditional_alloc_bytes; +extern uint64_t spl_vmem_conditional_allocs; +extern uint64_t spl_vmem_conditional_alloc_bytes; +extern uint64_t spl_vmem_conditional_alloc_deny; +extern uint64_t spl_vmem_conditional_alloc_deny_bytes; + +extern uint64_t spl_xat_success; +extern uint64_t spl_xat_late_success; +extern uint64_t spl_xat_late_success_nosleep; +extern uint64_t spl_xat_pressured; +extern uint64_t spl_xat_bailed; +extern uint64_t spl_xat_bailed_contended; +extern uint64_t spl_xat_lastalloc; +extern uint64_t spl_xat_lastfree; +extern uint64_t spl_xat_forced; +extern uint64_t spl_xat_sleep; +extern uint64_t spl_xat_late_deny; +extern uint64_t spl_xat_no_waiters; +extern uint64_t spl_xft_wait; + +extern uint64_t spl_vba_parent_memory_appeared; +extern uint64_t spl_vba_parent_memory_blocked; +extern uint64_t spl_vba_hiprio_blocked; +extern uint64_t spl_vba_cv_timeout; +extern uint64_t spl_vba_loop_timeout; +extern uint64_t spl_vba_cv_timeout_blocked; +extern uint64_t spl_vba_loop_timeout_blocked; +extern uint64_t spl_vba_sleep; +extern uint64_t spl_vba_loop_entries; + +extern uint64_t spl_bucket_tunable_large_span; +extern uint64_t spl_bucket_tunable_small_span; +extern void spl_set_bucket_tunable_large_span(uint64_t); +extern void spl_set_bucket_tunable_small_span(uint64_t); + +extern _Atomic uint64_t spl_arc_no_grow_bits; +extern uint64_t spl_arc_no_grow_count; + +extern uint64_t spl_frag_max_walk; +extern uint64_t spl_frag_walked_out; +extern uint64_t spl_frag_walk_cnt; + +uint64_t spl_buckets_mem_free = 0; +uint64_t spl_arc_reclaim_avoided = 0; + +uint64_t kmem_free_to_slab_when_fragmented = 0; + +typedef struct spl_stats { + kstat_named_t spl_os_alloc; + kstat_named_t spl_active_threads; + kstat_named_t spl_active_mutex; + kstat_named_t spl_active_rwlock; + kstat_named_t spl_active_tsd; + kstat_named_t spl_free_wake_count; + kstat_named_t spl_spl_free; + kstat_named_t spl_spl_free_manual_pressure; + kstat_named_t spl_spl_free_fast_pressure; + kstat_named_t spl_spl_free_delta_ema; + kstat_named_t spl_spl_free_negative_count; + kstat_named_t spl_osif_malloc_success; + kstat_named_t spl_osif_malloc_bytes; + kstat_named_t spl_osif_free; + kstat_named_t spl_osif_free_bytes; + kstat_named_t spl_bucket_non_pow2_allocs; + + kstat_named_t spl_vmem_unconditional_allocs; + kstat_named_t spl_vmem_unconditional_alloc_bytes; + kstat_named_t spl_vmem_conditional_allocs; + kstat_named_t spl_vmem_conditional_alloc_bytes; + kstat_named_t spl_vmem_conditional_alloc_deny; + kstat_named_t spl_vmem_conditional_alloc_deny_bytes; + + kstat_named_t spl_xat_success; + kstat_named_t spl_xat_late_success; + kstat_named_t spl_xat_late_success_nosleep; + kstat_named_t spl_xat_pressured; + kstat_named_t spl_xat_bailed; + kstat_named_t spl_xat_bailed_contended; + kstat_named_t spl_xat_lastalloc; + kstat_named_t spl_xat_lastfree; + kstat_named_t spl_xat_forced; + kstat_named_t spl_xat_sleep; + kstat_named_t spl_xat_late_deny; + kstat_named_t spl_xat_no_waiters; + kstat_named_t spl_xft_wait; + + kstat_named_t spl_vba_parent_memory_appeared; + kstat_named_t spl_vba_parent_memory_blocked; + kstat_named_t spl_vba_hiprio_blocked; + kstat_named_t spl_vba_cv_timeout; + kstat_named_t spl_vba_loop_timeout; + kstat_named_t spl_vba_cv_timeout_blocked; + kstat_named_t spl_vba_loop_timeout_blocked; + kstat_named_t spl_vba_sleep; + kstat_named_t spl_vba_loop_entries; + + kstat_named_t spl_bucket_tunable_large_span; + kstat_named_t spl_bucket_tunable_small_span; + + kstat_named_t spl_buckets_mem_free; + kstat_named_t spl_arc_no_grow_bits; + kstat_named_t spl_arc_no_grow_count; + kstat_named_t spl_frag_max_walk; + kstat_named_t spl_frag_walked_out; + kstat_named_t spl_frag_walk_cnt; + kstat_named_t spl_arc_reclaim_avoided; + + kstat_named_t kmem_free_to_slab_when_fragmented; + + kstat_named_t spl_vm_pages_reclaimed; + kstat_named_t spl_vm_pages_wanted; + kstat_named_t spl_vm_pressure_level; +} spl_stats_t; + +static spl_stats_t spl_stats = { + {"os_mem_alloc", KSTAT_DATA_UINT64}, + {"active_threads", KSTAT_DATA_UINT64}, + {"active_mutex", KSTAT_DATA_UINT64}, + {"active_rwlock", KSTAT_DATA_UINT64}, + {"active_tsd", KSTAT_DATA_UINT64}, + {"spl_free_wake_count", KSTAT_DATA_UINT64}, + {"spl_spl_free", KSTAT_DATA_INT64}, + {"spl_spl_free_manual_pressure", KSTAT_DATA_UINT64}, + {"spl_spl_free_fast_pressure", KSTAT_DATA_UINT64}, + {"spl_spl_free_delta_ema", KSTAT_DATA_UINT64}, + {"spl_spl_free_negative_count", KSTAT_DATA_UINT64}, + {"spl_osif_malloc_success", KSTAT_DATA_UINT64}, + {"spl_osif_malloc_bytes", KSTAT_DATA_UINT64}, + {"spl_osif_free", KSTAT_DATA_UINT64}, + {"spl_osif_free_bytes", KSTAT_DATA_UINT64}, + {"spl_bucket_non_pow2_allocs", KSTAT_DATA_UINT64}, + + {"vmem_unconditional_allocs", KSTAT_DATA_UINT64}, + {"vmem_unconditional_alloc_bytes", KSTAT_DATA_UINT64}, + {"vmem_conditional_allocs", KSTAT_DATA_UINT64}, + {"vmem_conditional_alloc_bytes", KSTAT_DATA_UINT64}, + {"vmem_conditional_alloc_deny", KSTAT_DATA_UINT64}, + {"vmem_conditional_alloc_deny_bytes", KSTAT_DATA_UINT64}, + + {"spl_xat_success", KSTAT_DATA_UINT64}, + {"spl_xat_late_success", KSTAT_DATA_UINT64}, + {"spl_xat_late_success_nosleep", KSTAT_DATA_UINT64}, + {"spl_xat_pressured", KSTAT_DATA_UINT64}, + {"spl_xat_bailed", KSTAT_DATA_UINT64}, + {"spl_xat_bailed_contended", KSTAT_DATA_UINT64}, + {"spl_xat_lastalloc", KSTAT_DATA_UINT64}, + {"spl_xat_lastfree", KSTAT_DATA_UINT64}, + {"spl_xat_forced", KSTAT_DATA_UINT64}, + {"spl_xat_sleep", KSTAT_DATA_UINT64}, + {"spl_xat_late_deny", KSTAT_DATA_UINT64}, + {"spl_xat_no_waiters", KSTAT_DATA_UINT64}, + {"spl_xft_wait", KSTAT_DATA_UINT64}, + + {"spl_vba_parent_memory_appeared", KSTAT_DATA_UINT64}, + {"spl_vba_parent_memory_blocked", KSTAT_DATA_UINT64}, + {"spl_vba_hiprio_blocked", KSTAT_DATA_UINT64}, + {"spl_vba_cv_timeout", KSTAT_DATA_UINT64}, + {"spl_vba_loop_timeout", KSTAT_DATA_UINT64}, + {"spl_vba_cv_timeout_blocked", KSTAT_DATA_UINT64}, + {"spl_vba_loop_timeout_blocked", KSTAT_DATA_UINT64}, + {"spl_vba_sleep", KSTAT_DATA_UINT64}, + {"spl_vba_loop_entries", KSTAT_DATA_UINT64}, + + {"spl_tunable_large_span", KSTAT_DATA_UINT64}, + {"spl_tunable_small_span", KSTAT_DATA_UINT64}, + + {"spl_buckets_mem_free", KSTAT_DATA_UINT64}, + {"spl_arc_no_grow_bits", KSTAT_DATA_UINT64}, + {"spl_arc_no_grow_count", KSTAT_DATA_UINT64}, + + {"spl_vmem_frag_max_walk", KSTAT_DATA_UINT64}, + {"spl_vmem_frag_walked_out", KSTAT_DATA_UINT64}, + {"spl_vmem_frag_walk_cnt", KSTAT_DATA_UINT64}, + {"spl_arc_reclaim_avoided", KSTAT_DATA_UINT64}, + + {"kmem_free_to_slab_when_fragmented", KSTAT_DATA_UINT64}, + {"spl_vm_pages_reclaimed", KSTAT_DATA_UINT64}, + {"spl_vm_pages_wanted", KSTAT_DATA_UINT64}, + {"spl_vm_pressure_level", KSTAT_DATA_UINT64}, +}; + +static kstat_t *spl_ksp = 0; + +// Stub out caller() +caddr_t +caller() +{ + return ((caddr_t)(0)); +} + +void * +calloc(size_t n, size_t s) +{ + return (zfs_kmem_zalloc(n * s, KM_NOSLEEP)); +} + +#define IS_DIGIT(c) ((c) >= '0' && (c) <= '9') + +#define IS_ALPHA(c) \ +(((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) + +/* + * Get bytes from the /dev/random generator. Returns 0 + * on success. Returns EAGAIN if there is insufficient entropy. + */ +int +random_get_bytes(uint8_t *ptr, size_t len) +{ + read_random(ptr, len); + return (0); +} + +/* + * BGH - Missing from OSX? + * + * Convert a string into a valid C identifier by replacing invalid + * characters with '_'. Also makes sure the string is nul-terminated + * and takes up at most n bytes. + */ +void +strident_canon(char *s, size_t n) +{ + char c; + char *end = s + n - 1; + + if ((c = *s) == 0) + return; + + if (!IS_ALPHA(c) && c != '_') + *s = '_'; + + while (s < end && ((c = *(++s)) != 0)) { + if (!IS_ALPHA(c) && !IS_DIGIT(c) && c != '_') + *s = '_'; + } + *s = 0; +} + +int +strident_valid(const char *id) +{ + int c = *id++; + + if (!IS_ALPHA(c) && c != '_') + return (0); + while ((c = *id++) != 0) { + if (!IS_ALPHA(c) && !IS_DIGIT(c) && c != '_') + return (0); + } + return (1); +} + +static void +copy_pattern(uint64_t pattern, void *buf_arg, size_t size) +{ + uint64_t *bufend = (uint64_t *)((char *)buf_arg + size); + uint64_t *buf = buf_arg; + + while (buf < bufend) + *buf++ = pattern; +} + +static void * +verify_pattern(uint64_t pattern, void *buf_arg, size_t size) +{ + uint64_t *bufend = (uint64_t *)((char *)buf_arg + size); + uint64_t *buf; + + for (buf = buf_arg; buf < bufend; buf++) + if (*buf != pattern) + return (buf); + return (NULL); +} + +static void * +verify_and_copy_pattern(uint64_t old, uint64_t new, void *buf_arg, size_t size) +{ + uint64_t *bufend = (uint64_t *)((char *)buf_arg + size); + uint64_t *buf; + + for (buf = buf_arg; buf < bufend; buf++) { + if (*buf != old) { + copy_pattern(old, buf_arg, + (char *)buf - (char *)buf_arg); + return (buf); + } + *buf = new; + } + + return (NULL); +} + +static void +kmem_cache_applyall(void (*func)(kmem_cache_t *), taskq_t *tq, int tqflag) +{ + kmem_cache_t *cp; + + mutex_enter(&kmem_cache_lock); + for (cp = list_head(&kmem_caches); cp != NULL; + cp = list_next(&kmem_caches, cp)) + if (tq != NULL) + (void) taskq_dispatch(tq, (task_func_t *)func, cp, + tqflag); + else + func(cp); + mutex_exit(&kmem_cache_lock); +} + +static void +kmem_cache_applyall_id(void (*func)(kmem_cache_t *), taskq_t *tq, int tqflag) +{ + kmem_cache_t *cp; + + mutex_enter(&kmem_cache_lock); + for (cp = list_head(&kmem_caches); cp != NULL; + cp = list_next(&kmem_caches, cp)) { + if (!(cp->cache_cflags & KMC_IDENTIFIER)) + continue; + if (tq != NULL) + (void) taskq_dispatch(tq, (task_func_t *)func, cp, + tqflag); + else + func(cp); + } + mutex_exit(&kmem_cache_lock); +} + +/* + * Debugging support. Given a buffer address, find its slab. + */ +static kmem_slab_t * +kmem_findslab(kmem_cache_t *cp, void *buf) +{ + kmem_slab_t *sp; + + mutex_enter(&cp->cache_lock); + for (sp = list_head(&cp->cache_complete_slabs); sp != NULL; + sp = list_next(&cp->cache_complete_slabs, sp)) { + if (KMEM_SLAB_MEMBER(sp, buf)) { + mutex_exit(&cp->cache_lock); + return (sp); + } + } + for (sp = avl_first(&cp->cache_partial_slabs); sp != NULL; + sp = AVL_NEXT(&cp->cache_partial_slabs, sp)) { + if (KMEM_SLAB_MEMBER(sp, buf)) { + mutex_exit(&cp->cache_lock); + return (sp); + } + } + mutex_exit(&cp->cache_lock); + + return (NULL); +} + +static void +kmem_error(int error, kmem_cache_t *cparg, void *bufarg) +{ + kmem_buftag_t *btp = NULL; + kmem_bufctl_t *bcp = NULL; + kmem_cache_t *cp = cparg; + kmem_slab_t *sp; + uint64_t *off; + void *buf = bufarg; + + kmem_logging = 0; /* stop logging when a bad thing happens */ + + kmem_panic_info.kmp_timestamp = gethrtime(); + + sp = kmem_findslab(cp, buf); + if (sp == NULL) { + for (cp = list_tail(&kmem_caches); cp != NULL; + cp = list_prev(&kmem_caches, cp)) { + if ((sp = kmem_findslab(cp, buf)) != NULL) + break; + } + } + + if (sp == NULL) { + cp = NULL; + error = KMERR_BADADDR; + } else { + if (cp != cparg) + error = KMERR_BADCACHE; + else + buf = (char *)bufarg - + ((uintptr_t)bufarg - + (uintptr_t)sp->slab_base) % cp->cache_chunksize; + if (buf != bufarg) + error = KMERR_BADBASE; + if (cp->cache_flags & KMF_BUFTAG) + btp = KMEM_BUFTAG(cp, buf); + if (cp->cache_flags & KMF_HASH) { + mutex_enter(&cp->cache_lock); + for (bcp = *KMEM_HASH(cp, buf); bcp; bcp = bcp->bc_next) + if (bcp->bc_addr == buf) + break; + mutex_exit(&cp->cache_lock); + if (bcp == NULL && btp != NULL) + bcp = btp->bt_bufctl; + if (kmem_findslab(cp->cache_bufctl_cache, bcp) == + NULL || P2PHASE((uintptr_t)bcp, KMEM_ALIGN) || + bcp->bc_addr != buf) { + error = KMERR_BADBUFCTL; + bcp = NULL; + } + } + } + + kmem_panic_info.kmp_error = error; + kmem_panic_info.kmp_buffer = bufarg; + kmem_panic_info.kmp_realbuf = buf; + kmem_panic_info.kmp_cache = cparg; + kmem_panic_info.kmp_realcache = cp; + kmem_panic_info.kmp_slab = sp; + kmem_panic_info.kmp_bufctl = bcp; + + printf("SPL: kernel memory allocator: "); + + switch (error) { + + case KMERR_MODIFIED: + printf("buffer modified after being freed\n"); + off = verify_pattern(KMEM_FREE_PATTERN, buf, + cp->cache_verify); + if (off == NULL) /* shouldn't happen */ + off = buf; + printf("SPL: modification occurred at offset 0x%lx " + "(0x%llx replaced by 0x%llx)\n", + (uintptr_t)off - (uintptr_t)buf, + (longlong_t)KMEM_FREE_PATTERN, (longlong_t)*off); + break; + + case KMERR_REDZONE: + printf("redzone violation: write past end of buffer\n"); + break; + + case KMERR_BADADDR: + printf("invalid free: buffer not in cache\n"); + break; + + case KMERR_DUPFREE: + printf("duplicate free: buffer freed twice\n"); + break; + + case KMERR_BADBUFTAG: + printf("boundary tag corrupted\n"); + printf("SPL: bcp ^ bxstat = %lx, should be %lx\n", + (intptr_t)btp->bt_bufctl ^ btp->bt_bxstat, + KMEM_BUFTAG_FREE); + break; + + case KMERR_BADBUFCTL: + printf("bufctl corrupted\n"); + break; + + case KMERR_BADCACHE: + printf("buffer freed to wrong cache\n"); + printf("SPL: buffer was allocated from %s,\n", + cp->cache_name); + printf("SPL: caller attempting free to %s.\n", + cparg->cache_name); + break; + + case KMERR_BADSIZE: + printf("bad free: free size (%u) != alloc size (%u)\n", + KMEM_SIZE_DECODE(((uint32_t *)btp)[0]), + KMEM_SIZE_DECODE(((uint32_t *)btp)[1])); + break; + + case KMERR_BADBASE: + printf("bad free: free address (%p) != alloc address" + " (%p)\n", bufarg, buf); + break; + } + + printf("SPL: buffer=%p bufctl=%p cache: %s\n", + bufarg, (void *)bcp, cparg->cache_name); + + if (bcp != NULL && (cp->cache_flags & KMF_AUDIT) && + error != KMERR_BADBUFCTL) { + int d; + timestruc_t ts = {0, 0}; + kmem_bufctl_audit_t *bcap = (kmem_bufctl_audit_t *)bcp; + + hrt2ts(kmem_panic_info.kmp_timestamp - bcap->bc_timestamp, &ts); + printf("SPL: previous transaction on buffer %p:\n", buf); + printf("SPL: thread=%p time=T-%ld.%09ld slab=%p cache: %s\n", + (void *)bcap->bc_thread, ts.tv_sec, ts.tv_nsec, + (void *)sp, cp->cache_name); + for (d = 0; d < MIN(bcap->bc_depth, KMEM_STACK_DEPTH); d++) { + print_symbol(bcap->bc_stack[d]); + } + } + + if (kmem_panic > 0) { + extern void IODelay(unsigned microseconds); // lh_cpu[max_ncpus]; + int i; + + /* + * Make sure that lhp->lh_cpu[] is nicely aligned + * to prevent false sharing of cache lines. + */ + lhsize = P2ROUNDUP(lhsize, KMEM_ALIGN); + lhp = vmem_xalloc(kmem_log_arena, lhsize, 64, P2NPHASE(lhsize, 64), 0, + NULL, NULL, VM_SLEEP); + bzero(lhp, lhsize); + + mutex_init(&lhp->lh_lock, NULL, MUTEX_DEFAULT, NULL); + lhp->lh_nchunks = nchunks; + lhp->lh_chunksize = P2ROUNDUP(logsize / nchunks + 1, PAGESIZE); + lhp->lh_base = vmem_alloc(kmem_log_arena, + lhp->lh_chunksize * nchunks, VM_SLEEP); + lhp->lh_free = vmem_alloc(kmem_log_arena, + nchunks * sizeof (int), VM_SLEEP); + bzero(lhp->lh_base, lhp->lh_chunksize * nchunks); + + for (i = 0; i < max_ncpus; i++) { + kmem_cpu_log_header_t *clhp = &lhp->lh_cpu[i]; + mutex_init(&clhp->clh_lock, NULL, MUTEX_DEFAULT, NULL); + clhp->clh_chunk = i; + } + + for (i = max_ncpus; i < nchunks; i++) + lhp->lh_free[i] = i; + + lhp->lh_head = max_ncpus; + lhp->lh_tail = 0; + + return (lhp); +} + + +static void +kmem_log_fini(kmem_log_header_t *lhp) +{ + int nchunks = 4 * max_ncpus; + size_t lhsize = (size_t)&((kmem_log_header_t *)0)->lh_cpu[max_ncpus]; + int i; + + + + for (i = 0; i < max_ncpus; i++) { + kmem_cpu_log_header_t *clhp = &lhp->lh_cpu[i]; + mutex_destroy(&clhp->clh_lock); + } + + vmem_free(kmem_log_arena, lhp->lh_free, nchunks * sizeof (int)); + + vmem_free(kmem_log_arena, lhp->lh_base, lhp->lh_chunksize * nchunks); + + mutex_destroy(&lhp->lh_lock); + + lhsize = P2ROUNDUP(lhsize, KMEM_ALIGN); + vmem_xfree(kmem_log_arena, lhp, lhsize); +} + + +static void * +kmem_log_enter(kmem_log_header_t *lhp, void *data, size_t size) +{ + void *logspace; + + kmem_cpu_log_header_t *clhp = &lhp->lh_cpu[CPU_SEQID]; + + // if (lhp == NULL || kmem_logging == 0 || panicstr) + if (lhp == NULL || kmem_logging == 0) + return (NULL); + + mutex_enter(&clhp->clh_lock); + clhp->clh_hits++; + if (size > clhp->clh_avail) { + mutex_enter(&lhp->lh_lock); + lhp->lh_hits++; + lhp->lh_free[lhp->lh_tail] = clhp->clh_chunk; + lhp->lh_tail = (lhp->lh_tail + 1) % lhp->lh_nchunks; + clhp->clh_chunk = lhp->lh_free[lhp->lh_head]; + lhp->lh_head = (lhp->lh_head + 1) % lhp->lh_nchunks; + clhp->clh_current = lhp->lh_base + + clhp->clh_chunk * lhp->lh_chunksize; + clhp->clh_avail = lhp->lh_chunksize; + if (size > lhp->lh_chunksize) + size = lhp->lh_chunksize; + mutex_exit(&lhp->lh_lock); + } + logspace = clhp->clh_current; + clhp->clh_current += size; + clhp->clh_avail -= size; + bcopy(data, logspace, size); + mutex_exit(&clhp->clh_lock); + return (logspace); +} + +#define KMEM_AUDIT(lp, cp, bcp) \ +{ \ +kmem_bufctl_audit_t *_bcp = (kmem_bufctl_audit_t *)(bcp); \ +_bcp->bc_timestamp = gethrtime(); \ +_bcp->bc_thread = spl_current_thread(); \ +_bcp->bc_depth = getpcstack(_bcp->bc_stack, KMEM_STACK_DEPTH); \ +_bcp->bc_lastlog = kmem_log_enter((lp), _bcp, sizeof (*_bcp)); \ +} + +static void +kmem_log_event(kmem_log_header_t *lp, kmem_cache_t *cp, + kmem_slab_t *sp, void *addr) +{ + kmem_bufctl_audit_t bca; + + bzero(&bca, sizeof (kmem_bufctl_audit_t)); + bca.bc_addr = addr; + bca.bc_slab = sp; + KMEM_AUDIT(lp, cp, &bca); +} + +/* + * Create a new slab for cache cp. + */ +static kmem_slab_t * +kmem_slab_create(kmem_cache_t *cp, int kmflag) +{ + size_t slabsize = cp->cache_slabsize; + size_t chunksize = cp->cache_chunksize; + int cache_flags = cp->cache_flags; + size_t color, chunks; + char *buf, *slab; + kmem_slab_t *sp; + kmem_bufctl_t *bcp; + vmem_t *vmp = cp->cache_arena; + + ASSERT(MUTEX_NOT_HELD(&cp->cache_lock)); + + color = cp->cache_color + cp->cache_align; + if (color > cp->cache_maxcolor) + color = cp->cache_mincolor; + cp->cache_color = color; + + slab = vmem_alloc(vmp, slabsize, kmflag & KM_VMFLAGS); + + if (slab == NULL) + goto vmem_alloc_failure; + + ASSERT(P2PHASE((uintptr_t)slab, vmp->vm_quantum) == 0); + + /* + * Reverify what was already checked in kmem_cache_set_move(), since the + * consolidator depends (for correctness) on slabs being initialized + * with the 0xbaddcafe memory pattern (setting a low order bit usable by + * clients to distinguish uninitialized memory from known objects). + */ + ASSERT((cp->cache_move == NULL) || !(cp->cache_cflags & KMC_NOTOUCH)); + if (!(cp->cache_cflags & KMC_NOTOUCH)) + copy_pattern(KMEM_UNINITIALIZED_PATTERN, slab, slabsize); + + if (cache_flags & KMF_HASH) { + if ((sp = kmem_cache_alloc(kmem_slab_cache, kmflag)) == NULL) + goto slab_alloc_failure; + chunks = (slabsize - color) / chunksize; + } else { + sp = KMEM_SLAB(cp, slab); + chunks = (slabsize - sizeof (kmem_slab_t) - color) / chunksize; + } + + sp->slab_cache = cp; + sp->slab_head = NULL; + sp->slab_refcnt = 0; + sp->slab_base = buf = slab + color; + sp->slab_chunks = chunks; + sp->slab_stuck_offset = (uint32_t)-1; + sp->slab_later_count = 0; + sp->slab_flags = 0; + sp->slab_create_time = gethrtime(); + + ASSERT(chunks > 0); + while (chunks-- != 0) { + if (cache_flags & KMF_HASH) { + bcp = kmem_cache_alloc(cp->cache_bufctl_cache, kmflag); + if (bcp == NULL) + goto bufctl_alloc_failure; + if (cache_flags & KMF_AUDIT) { + kmem_bufctl_audit_t *bcap = + (kmem_bufctl_audit_t *)bcp; + bzero(bcap, sizeof (kmem_bufctl_audit_t)); + bcap->bc_cache = cp; + } + bcp->bc_addr = buf; + bcp->bc_slab = sp; + } else { + bcp = KMEM_BUFCTL(cp, buf); + } + if (cache_flags & KMF_BUFTAG) { + kmem_buftag_t *btp = KMEM_BUFTAG(cp, buf); + btp->bt_redzone = KMEM_REDZONE_PATTERN; + btp->bt_bufctl = bcp; + btp->bt_bxstat = (intptr_t)bcp ^ KMEM_BUFTAG_FREE; + if (cache_flags & KMF_DEADBEEF) { + copy_pattern(KMEM_FREE_PATTERN, buf, + cp->cache_verify); + } + } + bcp->bc_next = sp->slab_head; + sp->slab_head = bcp; + buf += chunksize; + } + + kmem_log_event(kmem_slab_log, cp, sp, slab); + + return (sp); + +bufctl_alloc_failure: + + while ((bcp = sp->slab_head) != NULL) { + sp->slab_head = bcp->bc_next; + kmem_cache_free(cp->cache_bufctl_cache, bcp); + } + kmem_cache_free(kmem_slab_cache, sp); + +slab_alloc_failure: + + vmem_free(vmp, slab, slabsize); + +vmem_alloc_failure: + + if (0 == (kmflag & KM_NO_VBA)) { + kmem_log_event(kmem_failure_log, cp, NULL, NULL); + atomic_inc_64(&cp->cache_alloc_fail); + } + + return (NULL); +} + +/* + * Destroy a slab. + */ +static void +kmem_slab_destroy(kmem_cache_t *cp, kmem_slab_t *sp) +{ + vmem_t *vmp = cp->cache_arena; + void *slab = (void *)P2ALIGN((uintptr_t)sp->slab_base, vmp->vm_quantum); + + ASSERT(MUTEX_NOT_HELD(&cp->cache_lock)); + ASSERT(sp->slab_refcnt == 0); + + if (cp->cache_flags & KMF_HASH) { + kmem_bufctl_t *bcp; + while ((bcp = sp->slab_head) != NULL) { + sp->slab_head = bcp->bc_next; + kmem_cache_free(cp->cache_bufctl_cache, bcp); + } + kmem_cache_free(kmem_slab_cache, sp); + } + kpreempt(KPREEMPT_SYNC); + vmem_free(vmp, slab, cp->cache_slabsize); +} + +static void * +kmem_slab_alloc_impl(kmem_cache_t *cp, kmem_slab_t *sp, boolean_t prefill) +{ + kmem_bufctl_t *bcp, **hash_bucket; + void *buf; + boolean_t new_slab = (sp->slab_refcnt == 0); + + ASSERT(MUTEX_HELD(&cp->cache_lock)); + /* + * kmem_slab_alloc() drops cache_lock when it creates a new slab, so we + * can't ASSERT(avl_is_empty(&cp->cache_partial_slabs)) here when the + * slab is newly created. + */ + ASSERT(new_slab || (KMEM_SLAB_IS_PARTIAL(sp) && + (sp == avl_first(&cp->cache_partial_slabs)))); + ASSERT(sp->slab_cache == cp); + + cp->cache_slab_alloc++; + cp->cache_bufslab--; + sp->slab_refcnt++; + + bcp = sp->slab_head; + sp->slab_head = bcp->bc_next; + + if (cp->cache_flags & KMF_HASH) { + /* + * Add buffer to allocated-address hash table. + */ + buf = bcp->bc_addr; + hash_bucket = KMEM_HASH(cp, buf); + bcp->bc_next = *hash_bucket; + *hash_bucket = bcp; + if ((cp->cache_flags & (KMF_AUDIT | KMF_BUFTAG)) == KMF_AUDIT) { + KMEM_AUDIT(kmem_transaction_log, cp, bcp); + } + } else { + buf = KMEM_BUF(cp, bcp); + } + + ASSERT(KMEM_SLAB_MEMBER(sp, buf)); + + if (sp->slab_head == NULL) { + ASSERT(KMEM_SLAB_IS_ALL_USED(sp)); + if (new_slab) { + ASSERT(sp->slab_chunks == 1); + } else { + ASSERT(sp->slab_chunks > 1); /* the slab was partial */ + avl_remove(&cp->cache_partial_slabs, sp); + sp->slab_later_count = 0; /* clear history */ + sp->slab_flags &= ~KMEM_SLAB_NOMOVE; + sp->slab_stuck_offset = (uint32_t)-1; + } + list_insert_head(&cp->cache_complete_slabs, sp); + cp->cache_complete_slab_count++; + return (buf); + } + + ASSERT(KMEM_SLAB_IS_PARTIAL(sp)); + /* + * Peek to see if the magazine layer is enabled before + * we prefill. We're not holding the cpu cache lock, + * so the peek could be wrong, but there's no harm in it. + */ + if (new_slab && prefill && (cp->cache_flags & KMF_PREFILL) && + (KMEM_CPU_CACHE(cp)->cc_magsize != 0)) { + kmem_slab_prefill(cp, sp); + return (buf); + } + + if (new_slab) { + avl_add(&cp->cache_partial_slabs, sp); + return (buf); + } + + /* + * The slab is now more allocated than it was, so the + * order remains unchanged. + */ + ASSERT(!avl_update(&cp->cache_partial_slabs, sp)); + return (buf); +} + +/* + * Allocate a raw (unconstructed) buffer from cp's slab layer. + */ +static void * +kmem_slab_alloc(kmem_cache_t *cp, int kmflag) +{ + kmem_slab_t *sp; + void *buf; + boolean_t test_destructor; + + mutex_enter(&cp->cache_lock); + test_destructor = (cp->cache_slab_alloc == 0); + sp = avl_first(&cp->cache_partial_slabs); + if (sp == NULL) { + ASSERT(cp->cache_bufslab == 0); + + /* + * The freelist is empty. Create a new slab. + */ + mutex_exit(&cp->cache_lock); + if ((sp = kmem_slab_create(cp, kmflag)) == NULL) { + return (NULL); + } + mutex_enter(&cp->cache_lock); + cp->cache_slab_create++; + if ((cp->cache_buftotal += sp->slab_chunks) > cp->cache_bufmax) + cp->cache_bufmax = cp->cache_buftotal; + cp->cache_bufslab += sp->slab_chunks; + } + + buf = kmem_slab_alloc_impl(cp, sp, B_TRUE); + ASSERT((cp->cache_slab_create - cp->cache_slab_destroy) == + (cp->cache_complete_slab_count + + avl_numnodes(&cp->cache_partial_slabs) + + (cp->cache_defrag == NULL ? 0 : cp->cache_defrag->kmd_deadcount))); + mutex_exit(&cp->cache_lock); + + if (test_destructor && cp->cache_destructor != NULL) { + copy_pattern(KMEM_UNINITIALIZED_PATTERN, buf, + cp->cache_bufsize); + if (cp->cache_flags & KMF_DEADBEEF) { + copy_pattern(KMEM_FREE_PATTERN, buf, cp->cache_verify); + } + } + + return (buf); +} + +static void kmem_slab_move_yes(kmem_cache_t *, kmem_slab_t *, void *); + +/* + * Free a raw (unconstructed) buffer to cp's slab layer. + */ +static void +kmem_slab_free(kmem_cache_t *cp, void *buf) +{ + kmem_slab_t *sp; + kmem_bufctl_t *bcp, **prev_bcpp; + + ASSERT(buf != NULL); + + mutex_enter(&cp->cache_lock); + cp->cache_slab_free++; + + if (cp->cache_flags & KMF_HASH) { + /* + * Look up buffer in allocated-address hash table. + */ + prev_bcpp = KMEM_HASH(cp, buf); + while ((bcp = *prev_bcpp) != NULL) { + if (bcp->bc_addr == buf) { + *prev_bcpp = bcp->bc_next; + sp = bcp->bc_slab; + break; + } + cp->cache_lookup_depth++; + prev_bcpp = &bcp->bc_next; + } + } else { + bcp = KMEM_BUFCTL(cp, buf); + sp = KMEM_SLAB(cp, buf); + } + + if (bcp == NULL || sp->slab_cache != cp || !KMEM_SLAB_MEMBER(sp, buf)) { + mutex_exit(&cp->cache_lock); + kmem_error(KMERR_BADADDR, cp, buf); + return; + } + + if (KMEM_SLAB_OFFSET(sp, buf) == sp->slab_stuck_offset) { + /* + * If this is the buffer that prevented the consolidator from + * clearing the slab, we can reset the slab flags now that the + * buffer is freed. (It makes sense to do this in + * kmem_cache_free(), where the client gives up ownership of the + * buffer, but on the hot path the test is too expensive.) + */ + kmem_slab_move_yes(cp, sp, buf); + } + + if ((cp->cache_flags & (KMF_AUDIT | KMF_BUFTAG)) == KMF_AUDIT) { + if (cp->cache_flags & KMF_CONTENTS) + ((kmem_bufctl_audit_t *)bcp)->bc_contents = + kmem_log_enter(kmem_content_log, buf, + cp->cache_contents); + KMEM_AUDIT(kmem_transaction_log, cp, bcp); + } + + bcp->bc_next = sp->slab_head; + sp->slab_head = bcp; + + cp->cache_bufslab++; + ASSERT(sp->slab_refcnt >= 1); + + if (--sp->slab_refcnt == 0) { + /* + * There are no outstanding allocations from this slab, + * so we can reclaim the memory. + */ + if (sp->slab_chunks == 1) { + list_remove(&cp->cache_complete_slabs, sp); + cp->cache_complete_slab_count--; + } else { + avl_remove(&cp->cache_partial_slabs, sp); + } + + cp->cache_buftotal -= sp->slab_chunks; + cp->cache_bufslab -= sp->slab_chunks; + /* + * Defer releasing the slab to the virtual memory subsystem + * while there is a pending move callback, since we guarantee + * that buffers passed to the move callback have only been + * touched by kmem or by the client itself. Since the memory + * patterns baddcafe (uninitialized) and deadbeef (freed) both + * set at least one of the two lowest order bits, the client can + * test those bits in the move callback to determine whether or + * not it knows about the buffer (assuming that the client also + * sets one of those low order bits whenever it frees a buffer). + */ + if (cp->cache_defrag == NULL || + (avl_is_empty(&cp->cache_defrag->kmd_moves_pending) && + !(sp->slab_flags & KMEM_SLAB_MOVE_PENDING))) { + cp->cache_slab_destroy++; + mutex_exit(&cp->cache_lock); + kmem_slab_destroy(cp, sp); + } else { + list_t *deadlist = + &cp->cache_defrag->kmd_deadlist; + /* + * Slabs are inserted at both ends of the + * deadlist to distinguish between slabs + * freed while move callbacks are pending + * (list head) and a slab freed while the + * lock is dropped in kmem_move_buffers() + * (list tail) so that in both cases + * slab_destroy() is called from the + * right context. + */ + if (sp->slab_flags & KMEM_SLAB_MOVE_PENDING) { + list_insert_tail(deadlist, sp); + } else { + list_insert_head(deadlist, sp); + } + cp->cache_defrag->kmd_deadcount++; + mutex_exit(&cp->cache_lock); + } + return; + } + + if (bcp->bc_next == NULL) { + /* Transition the slab from completely allocated to partial. */ + ASSERT(sp->slab_refcnt == (sp->slab_chunks - 1)); + ASSERT(sp->slab_chunks > 1); + list_remove(&cp->cache_complete_slabs, sp); + cp->cache_complete_slab_count--; + avl_add(&cp->cache_partial_slabs, sp); + } else { + (void) avl_update_gt(&cp->cache_partial_slabs, sp); + } + + ASSERT((cp->cache_slab_create - cp->cache_slab_destroy) == + (cp->cache_complete_slab_count + + avl_numnodes(&cp->cache_partial_slabs) + + (cp->cache_defrag == NULL ? 0 : cp->cache_defrag->kmd_deadcount))); + mutex_exit(&cp->cache_lock); +} + +/* + * Return -1 if kmem_error, 1 if constructor fails, 0 if successful. + */ +static int +kmem_cache_alloc_debug(kmem_cache_t *cp, void *buf, int kmflag, int construct, + caddr_t caller) +{ + kmem_buftag_t *btp = KMEM_BUFTAG(cp, buf); + kmem_bufctl_audit_t *bcp = (kmem_bufctl_audit_t *)btp->bt_bufctl; + uint32_t mtbf; + + if (btp->bt_bxstat != ((intptr_t)bcp ^ KMEM_BUFTAG_FREE)) { + kmem_error(KMERR_BADBUFTAG, cp, buf); + return (-1); + } + + btp->bt_bxstat = (intptr_t)bcp ^ KMEM_BUFTAG_ALLOC; + + if ((cp->cache_flags & KMF_HASH) && bcp->bc_addr != buf) { + kmem_error(KMERR_BADBUFCTL, cp, buf); + return (-1); + } + + if (cp->cache_flags & KMF_DEADBEEF) { + if (!construct && (cp->cache_flags & KMF_LITE)) { + if (*(uint64_t *)buf != KMEM_FREE_PATTERN) { + kmem_error(KMERR_MODIFIED, cp, buf); + return (-1); + } + if (cp->cache_constructor != NULL) + *(uint64_t *)buf = btp->bt_redzone; + else + *(uint64_t *)buf = KMEM_UNINITIALIZED_PATTERN; + } else { + construct = 1; + if (verify_and_copy_pattern(KMEM_FREE_PATTERN, + KMEM_UNINITIALIZED_PATTERN, buf, + cp->cache_verify)) { + kmem_error(KMERR_MODIFIED, cp, buf); + return (-1); + } + } + } + btp->bt_redzone = KMEM_REDZONE_PATTERN; + + if ((mtbf = kmem_mtbf | cp->cache_mtbf) != 0 && + gethrtime() % mtbf == 0 && + (kmflag & (KM_NOSLEEP | KM_PANIC)) == KM_NOSLEEP) { + kmem_log_event(kmem_failure_log, cp, NULL, NULL); + if (!construct && cp->cache_destructor != NULL) + cp->cache_destructor(buf, cp->cache_private); + } else { + mtbf = 0; + } + + if (mtbf || (construct && cp->cache_constructor != NULL && + cp->cache_constructor(buf, cp->cache_private, kmflag) != 0)) { + atomic_inc_64(&cp->cache_alloc_fail); + btp->bt_bxstat = (intptr_t)bcp ^ KMEM_BUFTAG_FREE; + if (cp->cache_flags & KMF_DEADBEEF) + copy_pattern(KMEM_FREE_PATTERN, buf, cp->cache_verify); + kmem_slab_free(cp, buf); + return (1); + } + + if (cp->cache_flags & KMF_AUDIT) { + KMEM_AUDIT(kmem_transaction_log, cp, bcp); + } + + if ((cp->cache_flags & KMF_LITE) && + !(cp->cache_cflags & KMC_KMEM_ALLOC)) { + KMEM_BUFTAG_LITE_ENTER(btp, kmem_lite_count, caller); + } + + return (0); +} + +static int +kmem_cache_free_debug(kmem_cache_t *cp, void *buf, caddr_t caller) +{ + kmem_buftag_t *btp = KMEM_BUFTAG(cp, buf); + kmem_bufctl_audit_t *bcp = (kmem_bufctl_audit_t *)btp->bt_bufctl; + kmem_slab_t *sp; + + if (btp->bt_bxstat != ((intptr_t)bcp ^ KMEM_BUFTAG_ALLOC)) { + if (btp->bt_bxstat == ((intptr_t)bcp ^ KMEM_BUFTAG_FREE)) { + kmem_error(KMERR_DUPFREE, cp, buf); + return (-1); + } + sp = kmem_findslab(cp, buf); + if (sp == NULL || sp->slab_cache != cp) + kmem_error(KMERR_BADADDR, cp, buf); + else + kmem_error(KMERR_REDZONE, cp, buf); + return (-1); + } + + btp->bt_bxstat = (intptr_t)bcp ^ KMEM_BUFTAG_FREE; + + if ((cp->cache_flags & KMF_HASH) && bcp->bc_addr != buf) { + kmem_error(KMERR_BADBUFCTL, cp, buf); + return (-1); + } + + if (btp->bt_redzone != KMEM_REDZONE_PATTERN) { + kmem_error(KMERR_REDZONE, cp, buf); + return (-1); + } + + if (cp->cache_flags & KMF_AUDIT) { + if (cp->cache_flags & KMF_CONTENTS) + bcp->bc_contents = kmem_log_enter(kmem_content_log, + buf, cp->cache_contents); + KMEM_AUDIT(kmem_transaction_log, cp, bcp); + } + + if ((cp->cache_flags & KMF_LITE) && + !(cp->cache_cflags & KMC_KMEM_ALLOC)) { + KMEM_BUFTAG_LITE_ENTER(btp, kmem_lite_count, caller); + } + + if (cp->cache_flags & KMF_DEADBEEF) { + if (cp->cache_flags & KMF_LITE) + btp->bt_redzone = *(uint64_t *)buf; + else if (cp->cache_destructor != NULL) + cp->cache_destructor(buf, cp->cache_private); + + copy_pattern(KMEM_FREE_PATTERN, buf, cp->cache_verify); + } + + return (0); +} + +/* + * Free each object in magazine mp to cp's slab layer, and free mp itself. + */ +static void +kmem_magazine_destroy(kmem_cache_t *cp, kmem_magazine_t *mp, int nrounds) +{ + int round; + + ASSERT(!list_link_active(&cp->cache_link) || + taskq_member(kmem_taskq, curthread)); + + for (round = 0; round < nrounds; round++) { + void *buf = mp->mag_round[round]; + + if (cp->cache_flags & KMF_DEADBEEF) { + if (verify_pattern(KMEM_FREE_PATTERN, buf, + cp->cache_verify) != NULL) { + kmem_error(KMERR_MODIFIED, cp, buf); + continue; + } + if ((cp->cache_flags & KMF_LITE) && + cp->cache_destructor != NULL) { + kmem_buftag_t *btp = KMEM_BUFTAG(cp, buf); + *(uint64_t *)buf = btp->bt_redzone; + cp->cache_destructor(buf, cp->cache_private); + *(uint64_t *)buf = KMEM_FREE_PATTERN; + } + } else if (cp->cache_destructor != NULL) { + cp->cache_destructor(buf, cp->cache_private); + } + + kmem_slab_free(cp, buf); + kpreempt(KPREEMPT_SYNC); + } + ASSERT(KMEM_MAGAZINE_VALID(cp, mp)); + kmem_cache_free(cp->cache_magtype->mt_cache, mp); +} + +/* + * Allocate a magazine from the depot. + */ +static kmem_magazine_t * +kmem_depot_alloc(kmem_cache_t *cp, kmem_maglist_t *mlp) +{ + kmem_magazine_t *mp; + + /* + * If we can't get the depot lock without contention, + * update our contention count. We use the depot + * contention rate to determine whether we need to + * increase the magazine size for better scalability. + */ + if (!mutex_tryenter(&cp->cache_depot_lock)) { + mutex_enter(&cp->cache_depot_lock); + cp->cache_depot_contention++; + } + + if ((mp = mlp->ml_list) != NULL) { + ASSERT(KMEM_MAGAZINE_VALID(cp, mp)); + mlp->ml_list = mp->mag_next; + if (--mlp->ml_total < mlp->ml_min) + mlp->ml_min = mlp->ml_total; + mlp->ml_alloc++; + } + + mutex_exit(&cp->cache_depot_lock); + + return (mp); +} + +/* + * Free a magazine to the depot. + */ +static void +kmem_depot_free(kmem_cache_t *cp, kmem_maglist_t *mlp, kmem_magazine_t *mp) +{ + mutex_enter(&cp->cache_depot_lock); + ASSERT(KMEM_MAGAZINE_VALID(cp, mp)); + mp->mag_next = mlp->ml_list; + mlp->ml_list = mp; + mlp->ml_total++; + mutex_exit(&cp->cache_depot_lock); +} + +/* + * Update the working set statistics for cp's depot. + */ +static void +kmem_depot_ws_update(kmem_cache_t *cp) +{ + mutex_enter(&cp->cache_depot_lock); + cp->cache_full.ml_reaplimit = cp->cache_full.ml_min; + cp->cache_full.ml_min = cp->cache_full.ml_total; + cp->cache_empty.ml_reaplimit = cp->cache_empty.ml_min; + cp->cache_empty.ml_min = cp->cache_empty.ml_total; + mutex_exit(&cp->cache_depot_lock); +} + +/* + * Set the working set statistics for cp's depot to zero. (Everything is + * eligible for reaping.) + */ +void +kmem_depot_ws_zero(kmem_cache_t *cp) +{ + mutex_enter(&cp->cache_depot_lock); + cp->cache_full.ml_reaplimit = cp->cache_full.ml_total; + cp->cache_full.ml_min = cp->cache_full.ml_total; + cp->cache_empty.ml_reaplimit = cp->cache_empty.ml_total; + cp->cache_empty.ml_min = cp->cache_empty.ml_total; + mutex_exit(&cp->cache_depot_lock); +} + +/* + * The number of bytes to reap before we call kpreempt(). The default (1MB) + * causes us to preempt reaping up to hundres of times per second. Using a + * larger value (1GB) causes this to have virtually no effect. + */ +size_t kmem_reap_preempt_bytes = 64 * 1024 * 1024; + + +/* + * Reap all magazines that have fallen out of the depot's working set. + */ +static void +kmem_depot_ws_reap(kmem_cache_t *cp) +{ + size_t bytes = 0; + long reap; + kmem_magazine_t *mp; + + ASSERT(!list_link_active(&cp->cache_link) || + taskq_member(kmem_taskq, curthread)); + + reap = MIN(cp->cache_full.ml_reaplimit, cp->cache_full.ml_min); + while (reap-- && + (mp = kmem_depot_alloc(cp, &cp->cache_full)) != NULL) { + kmem_magazine_destroy(cp, mp, cp->cache_magtype->mt_magsize); + bytes += cp->cache_magtype->mt_magsize * cp->cache_bufsize; + if (bytes > kmem_reap_preempt_bytes) { + kpreempt(KPREEMPT_SYNC); + bytes = 0; + } + } + + reap = MIN(cp->cache_empty.ml_reaplimit, cp->cache_empty.ml_min); + while (reap-- && + (mp = kmem_depot_alloc(cp, &cp->cache_empty)) != NULL) { + kmem_magazine_destroy(cp, mp, 0); + bytes += cp->cache_magtype->mt_magsize * cp->cache_bufsize; + if (bytes > kmem_reap_preempt_bytes) { + kpreempt(KPREEMPT_SYNC); + bytes = 0; + } + } +} + +static void +kmem_cpu_reload(kmem_cpu_cache_t *ccp, kmem_magazine_t *mp, int rounds) +{ + ASSERT((ccp->cc_loaded == NULL && ccp->cc_rounds == -1) || + (ccp->cc_loaded && ccp->cc_rounds + rounds == ccp->cc_magsize)); + ASSERT(ccp->cc_magsize > 0); + + ccp->cc_ploaded = ccp->cc_loaded; + ccp->cc_prounds = ccp->cc_rounds; + ccp->cc_loaded = mp; + ccp->cc_rounds = rounds; +} + +/* + * Intercept kmem alloc/free calls during crash dump in order to avoid + * changing kmem state while memory is being saved to the dump device. + * Otherwise, ::kmem_verify will report "corrupt buffers". Note that + * there are no locks because only one CPU calls kmem during a crash + * dump. To enable this feature, first create the associated vmem + * arena with VMC_DUMPSAFE. + */ +static void *kmem_dump_start; /* start of pre-reserved heap */ +static void *kmem_dump_end; /* end of heap area */ +static void *kmem_dump_curr; /* current free heap pointer */ +static size_t kmem_dump_size; /* size of heap area */ + +/* append to each buf created in the pre-reserved heap */ +typedef struct kmem_dumpctl { + void *kdc_next; /* cache dump free list linkage */ +} kmem_dumpctl_t; + +#define KMEM_DUMPCTL(cp, buf) \ +((kmem_dumpctl_t *)P2ROUNDUP((uintptr_t)(buf) + (cp)->cache_bufsize, \ +sizeof (void *))) + +/* Keep some simple stats. */ +#define KMEM_DUMP_LOGS (100) + +typedef struct kmem_dump_log { + kmem_cache_t *kdl_cache; + uint_t kdl_allocs; /* # of dump allocations */ + uint_t kdl_frees; /* # of dump frees */ + uint_t kdl_alloc_fails; /* # of allocation failures */ + uint_t kdl_free_nondump; /* # of non-dump frees */ + uint_t kdl_unsafe; /* cache was used, but unsafe */ +} kmem_dump_log_t; + +static kmem_dump_log_t *kmem_dump_log; +static int kmem_dump_log_idx; + +#define KDI_LOG(cp, stat) { \ +kmem_dump_log_t *kdl; \ +if ((kdl = (kmem_dump_log_t *)((cp)->cache_dumplog)) != NULL) { \ +kdl->stat++; \ +} else if (kmem_dump_log_idx < KMEM_DUMP_LOGS) { \ +kdl = &kmem_dump_log[kmem_dump_log_idx++]; \ +kdl->stat++; \ +kdl->kdl_cache = (cp); \ +(cp)->cache_dumplog = kdl; \ +} \ +} + +/* set non zero for full report */ +uint_t kmem_dump_verbose = 0; + +/* stats for overize heap */ +uint_t kmem_dump_oversize_allocs = 0; +uint_t kmem_dump_oversize_max = 0; + +static void +kmem_dumppr(char **pp, char *e, const char *format, ...) +{ + char *p = *pp; + + if (p < e) { + int n; + va_list ap; + + va_start(ap, format); + n = vsnprintf(p, e - p, format, ap); + va_end(ap); + *pp = p + n; + } +} + +/* + * Called when dumpadm(1M) configures dump parameters. + */ +void +kmem_dump_init(size_t size) +{ + if (kmem_dump_start != NULL) + zfs_kmem_free(kmem_dump_start, kmem_dump_size); + + if (kmem_dump_log == NULL) + kmem_dump_log = + (kmem_dump_log_t *)zfs_kmem_zalloc( + KMEM_DUMP_LOGS * sizeof (kmem_dump_log_t), KM_SLEEP); + + kmem_dump_start = zfs_kmem_alloc(size, KM_SLEEP); + + if (kmem_dump_start != NULL) { + kmem_dump_size = size; + kmem_dump_curr = kmem_dump_start; + kmem_dump_end = (void *)((char *)kmem_dump_start + size); + copy_pattern(KMEM_UNINITIALIZED_PATTERN, kmem_dump_start, size); + } else { + kmem_dump_size = 0; + kmem_dump_curr = NULL; + kmem_dump_end = NULL; + } +} + +/* + * Set flag for each kmem_cache_t if is safe to use alternate dump + * memory. Called just before panic crash dump starts. Set the flag + * for the calling CPU. + */ +void +kmem_dump_begin(void) +{ + if (kmem_dump_start != NULL) { + kmem_cache_t *cp; + + for (cp = list_head(&kmem_caches); cp != NULL; + cp = list_next(&kmem_caches, cp)) { + kmem_cpu_cache_t *ccp = KMEM_CPU_CACHE(cp); + + if (cp->cache_arena->vm_cflags & VMC_DUMPSAFE) { + cp->cache_flags |= KMF_DUMPDIVERT; + ccp->cc_flags |= KMF_DUMPDIVERT; + ccp->cc_dump_rounds = ccp->cc_rounds; + ccp->cc_dump_prounds = ccp->cc_prounds; + ccp->cc_rounds = ccp->cc_prounds = -1; + } else { + cp->cache_flags |= KMF_DUMPUNSAFE; + ccp->cc_flags |= KMF_DUMPUNSAFE; + } + } + } +} + +/* + * finished dump intercept + * print any warnings on the console + * return verbose information to dumpsys() in the given buffer + */ +size_t +kmem_dump_finish(char *buf, size_t size) +{ + int kdi_idx; + int kdi_end = kmem_dump_log_idx; + int percent = 0; + int header = 0; + int warn = 0; + size_t used; + kmem_cache_t *cp; + kmem_dump_log_t *kdl; + char *e = buf + size; + char *p = buf; + + if (kmem_dump_size == 0 || kmem_dump_verbose == 0) + return (0); + + used = (char *)kmem_dump_curr - (char *)kmem_dump_start; + percent = (used * 100) / kmem_dump_size; + + kmem_dumppr(&p, e, "%% heap used,%d\n", percent); + kmem_dumppr(&p, e, "used bytes,%ld\n", used); + kmem_dumppr(&p, e, "heap size,%ld\n", kmem_dump_size); + kmem_dumppr(&p, e, "Oversize allocs,%d\n", + kmem_dump_oversize_allocs); + kmem_dumppr(&p, e, "Oversize max size,%ld\n", + kmem_dump_oversize_max); + + for (kdi_idx = 0; kdi_idx < kdi_end; kdi_idx++) { + kdl = &kmem_dump_log[kdi_idx]; + cp = kdl->kdl_cache; + if (cp == NULL) + break; + if (kdl->kdl_alloc_fails) + ++warn; + if (header == 0) { + kmem_dumppr(&p, e, + "Cache Name,Allocs,Frees,Alloc Fails," + "Nondump Frees,Unsafe Allocs/Frees\n"); + header = 1; + } + kmem_dumppr(&p, e, "%s,%d,%d,%d,%d,%d\n", + cp->cache_name, kdl->kdl_allocs, kdl->kdl_frees, + kdl->kdl_alloc_fails, kdl->kdl_free_nondump, + kdl->kdl_unsafe); + } + + /* return buffer size used */ + if (p < e) + bzero(p, e - p); + return (p - buf); +} + +/* + * Allocate a constructed object from alternate dump memory. + */ +void * +kmem_cache_alloc_dump(kmem_cache_t *cp, int kmflag) +{ + void *buf; + void *curr; + char *bufend; + + /* return a constructed object */ + if ((buf = cp->cache_dumpfreelist) != NULL) { + cp->cache_dumpfreelist = KMEM_DUMPCTL(cp, buf)->kdc_next; + KDI_LOG(cp, kdl_allocs); + return (buf); + } + + /* create a new constructed object */ + curr = kmem_dump_curr; + buf = (void *)P2ROUNDUP((uintptr_t)curr, cp->cache_align); + bufend = (char *)KMEM_DUMPCTL(cp, buf) + sizeof (kmem_dumpctl_t); + + /* hat layer objects cannot cross a page boundary */ + if (cp->cache_align < PAGESIZE) { + char *page = (char *)P2ROUNDUP((uintptr_t)buf, PAGESIZE); + if (bufend > page) { + bufend += page - (char *)buf; + buf = (void *)page; + } + } + + /* fall back to normal alloc if reserved area is used up */ + if (bufend > (char *)kmem_dump_end) { + kmem_dump_curr = kmem_dump_end; + KDI_LOG(cp, kdl_alloc_fails); + return (NULL); + } + + /* + * Must advance curr pointer before calling a constructor that + * may also allocate memory. + */ + kmem_dump_curr = bufend; + + /* run constructor */ + if (cp->cache_constructor != NULL && + cp->cache_constructor(buf, cp->cache_private, kmflag) + != 0) { +#ifdef DEBUG + printf("name='%s' cache=0x%p: kmem cache constructor failed\n", + cp->cache_name, (void *)cp); +#endif + /* reset curr pointer iff no allocs were done */ + if (kmem_dump_curr == bufend) + kmem_dump_curr = curr; + + /* fall back to normal alloc if the constructor fails */ + KDI_LOG(cp, kdl_alloc_fails); + return (NULL); + } + + KDI_LOG(cp, kdl_allocs); + return (buf); +} + +/* + * Free a constructed object in alternate dump memory. + */ +int +kmem_cache_free_dump(kmem_cache_t *cp, void *buf) +{ + /* save constructed buffers for next time */ + if ((char *)buf >= (char *)kmem_dump_start && + (char *)buf < (char *)kmem_dump_end) { + KMEM_DUMPCTL(cp, buf)->kdc_next = cp->cache_dumpfreelist; + cp->cache_dumpfreelist = buf; + KDI_LOG(cp, kdl_frees); + return (0); + } + + /* count all non-dump buf frees */ + KDI_LOG(cp, kdl_free_nondump); + + /* just drop buffers that were allocated before dump started */ + if (kmem_dump_curr < kmem_dump_end) + return (0); + + /* fall back to normal free if reserved area is used up */ + return (1); +} + +/* + * Allocate a constructed object from cache cp. + */ +void * +kmem_cache_alloc(kmem_cache_t *cp, int kmflag) +{ + kmem_cpu_cache_t *ccp = KMEM_CPU_CACHE(cp); + kmem_magazine_t *fmp; + void *buf; + mutex_enter(&ccp->cc_lock); + for (;;) { + /* + * If there's an object available in the current CPU's + * loaded magazine, just take it and return. + */ + if (ccp->cc_rounds > 0) { + buf = ccp->cc_loaded->mag_round[--ccp->cc_rounds]; + ccp->cc_alloc++; + mutex_exit(&ccp->cc_lock); + if (ccp->cc_flags & (KMF_BUFTAG | KMF_DUMPUNSAFE)) { + if (ccp->cc_flags & KMF_DUMPUNSAFE) { + ASSERT(!(ccp->cc_flags & + KMF_DUMPDIVERT)); + KDI_LOG(cp, kdl_unsafe); + } + if ((ccp->cc_flags & KMF_BUFTAG) && + kmem_cache_alloc_debug(cp, buf, kmflag, 0, + caller()) != 0) { + if (kmflag & KM_NOSLEEP) + return (NULL); + mutex_enter(&ccp->cc_lock); + continue; + } + } + return (buf); + } + + /* + * The loaded magazine is empty. If the previously loaded + * magazine was full, exchange them and try again. + */ + if (ccp->cc_prounds > 0) { + kmem_cpu_reload(ccp, ccp->cc_ploaded, ccp->cc_prounds); + continue; + } + + /* + * Return an alternate buffer at dump time to preserve + * the heap. + */ + if (ccp->cc_flags & (KMF_DUMPDIVERT | KMF_DUMPUNSAFE)) { + if (ccp->cc_flags & KMF_DUMPUNSAFE) { + ASSERT(!(ccp->cc_flags & KMF_DUMPDIVERT)); + /* log it so that we can warn about it */ + KDI_LOG(cp, kdl_unsafe); + } else { + if ((buf = kmem_cache_alloc_dump(cp, kmflag)) != + NULL) { + mutex_exit(&ccp->cc_lock); + return (buf); + } + break; /* fall back to slab layer */ + } + } + + /* + * If the magazine layer is disabled, break out now. + */ + if (ccp->cc_magsize == 0) + break; + + /* + * Try to get a full magazine from the depot. + */ + fmp = kmem_depot_alloc(cp, &cp->cache_full); + if (fmp != NULL) { + if (ccp->cc_ploaded != NULL) + kmem_depot_free(cp, &cp->cache_empty, + ccp->cc_ploaded); + kmem_cpu_reload(ccp, fmp, ccp->cc_magsize); + continue; + } + + /* + * There are no full magazines in the depot, + * so fall through to the slab layer. + */ + break; + } + mutex_exit(&ccp->cc_lock); + + /* + * We couldn't allocate a constructed object from the magazine layer, + * so get a raw buffer from the slab layer and apply its constructor. + */ + buf = kmem_slab_alloc(cp, kmflag); + + if (buf == NULL) + return (NULL); + + if (cp->cache_flags & KMF_BUFTAG) { + /* + * Make kmem_cache_alloc_debug() apply the constructor for us. + */ + int rc = kmem_cache_alloc_debug(cp, buf, kmflag, 1, caller()); + if (rc != 0) { + if (kmflag & KM_NOSLEEP) + return (NULL); + /* + * kmem_cache_alloc_debug() detected corruption + * but didn't panic (kmem_panic <= 0). We should not be + * here because the constructor failed (indicated by a + * return code of 1). Try again. + */ + ASSERT(rc == -1); + return (kmem_cache_alloc(cp, kmflag)); + } + return (buf); + } + + if (cp->cache_constructor != NULL && + cp->cache_constructor(buf, cp->cache_private, kmflag) != 0) { + atomic_inc_64(&cp->cache_alloc_fail); + kmem_slab_free(cp, buf); + return (NULL); + } + + return (buf); +} + +/* + * The freed argument tells whether or not kmem_cache_free_debug() has already + * been called so that we can avoid the duplicate free error. For example, a + * buffer on a magazine has already been freed by the client but is still + * constructed. + */ +static void +kmem_slab_free_constructed(kmem_cache_t *cp, void *buf, boolean_t freed) +{ + if (!freed && (cp->cache_flags & KMF_BUFTAG)) + if (kmem_cache_free_debug(cp, buf, caller()) == -1) + return; + + /* + * Note that if KMF_DEADBEEF is in effect and KMF_LITE is not, + * kmem_cache_free_debug() will have already applied the destructor. + */ + if ((cp->cache_flags & (KMF_DEADBEEF | KMF_LITE)) != KMF_DEADBEEF && + cp->cache_destructor != NULL) { + if (cp->cache_flags & KMF_DEADBEEF) { /* KMF_LITE implied */ + kmem_buftag_t *btp = KMEM_BUFTAG(cp, buf); + *(uint64_t *)buf = btp->bt_redzone; + cp->cache_destructor(buf, cp->cache_private); + *(uint64_t *)buf = KMEM_FREE_PATTERN; + } else { + cp->cache_destructor(buf, cp->cache_private); + } + } + + kmem_slab_free(cp, buf); +} + +/* + * Used when there's no room to free a buffer to the per-CPU cache. + * Drops and re-acquires &ccp->cc_lock, and returns non-zero if the + * caller should try freeing to the per-CPU cache again. + * Note that we don't directly install the magazine in the cpu cache, + * since its state may have changed wildly while the lock was dropped. + */ +static int +kmem_cpucache_magazine_alloc(kmem_cpu_cache_t *ccp, kmem_cache_t *cp) +{ + kmem_magazine_t *emp; + kmem_magtype_t *mtp; + + ASSERT(MUTEX_HELD(&ccp->cc_lock)); + ASSERT(((uint_t)ccp->cc_rounds == ccp->cc_magsize || + ((uint_t)ccp->cc_rounds == -1)) && + ((uint_t)ccp->cc_prounds == ccp->cc_magsize || + ((uint_t)ccp->cc_prounds == -1))); + + emp = kmem_depot_alloc(cp, &cp->cache_empty); + if (emp != NULL) { + if (ccp->cc_ploaded != NULL) + kmem_depot_free(cp, &cp->cache_full, + ccp->cc_ploaded); + kmem_cpu_reload(ccp, emp, 0); + return (1); + } + /* + * There are no empty magazines in the depot, + * so try to allocate a new one. We must drop all locks + * across kmem_cache_alloc() because lower layers may + * attempt to allocate from this cache. + */ + mtp = cp->cache_magtype; + mutex_exit(&ccp->cc_lock); + emp = kmem_cache_alloc(mtp->mt_cache, KM_NOSLEEP); + mutex_enter(&ccp->cc_lock); + + if (emp != NULL) { + /* + * We successfully allocated an empty magazine. + * However, we had to drop ccp->cc_lock to do it, + * so the cache's magazine size may have changed. + * If so, free the magazine and try again. + */ + if (ccp->cc_magsize != mtp->mt_magsize) { + mutex_exit(&ccp->cc_lock); + kmem_cache_free(mtp->mt_cache, emp); + mutex_enter(&ccp->cc_lock); + return (1); + } + + /* + * We got a magazine of the right size. Add it to + * the depot and try the whole dance again. + */ + kmem_depot_free(cp, &cp->cache_empty, emp); + return (1); + } + + /* + * We couldn't allocate an empty magazine, + * so fall through to the slab layer. + */ + return (0); +} + +/* + * If the cache's parent arena is a leaf arena (i.e., it imports all its memory) + * then we can consider it fragmented if either there is 1 GiB free in the arena + * or one eighth of the arena is free. + * + * This is useful in kmem_cache_free{_debug} to determine whether to free to the + * slab layer if the loaded magazine is full. + */ +static inline boolean_t +kmem_cache_parent_arena_fragmented(kmem_cache_t *cp) +{ + const vmem_kstat_t *kp = &cp->cache_arena->vm_kstat; + const int64_t vk_import = kp->vk_mem_import.value.ui64; + const int64_t vk_inuse = kp->vk_mem_inuse.value.ui64; + const int64_t vk_total = kp->vk_mem_total.value.ui64; + + if (vk_import == vk_total && vk_inuse < vk_total) { + const int64_t vk_free = vk_total - vk_inuse; + const int64_t highthresh = 1024LL*1024LL*1024LL; + // we are fragmented if we have 1GiB free + if (vk_free >= highthresh) + return (B_TRUE); + // we are fragmented if at least 1/8 of the + // total arena space is free + if (vk_free > 0 && vk_total > 0) { + const int64_t eighth_total = vk_total / 8; + if (vk_free >= eighth_total) + return (B_TRUE); + } + } + return (B_FALSE); +} + +/* + * Free a constructed object to cache cp. + */ +void +kmem_cache_free(kmem_cache_t *cp, void *buf) +{ + kmem_cpu_cache_t *ccp = KMEM_CPU_CACHE(cp); + + /* + * The client must not free either of the buffers passed to the move + * callback function. + */ + ASSERT(cp->cache_defrag == NULL || + cp->cache_defrag->kmd_thread != spl_current_thread() || + (buf != cp->cache_defrag->kmd_from_buf && + buf != cp->cache_defrag->kmd_to_buf)); + + if (ccp->cc_flags & (KMF_BUFTAG | KMF_DUMPDIVERT | KMF_DUMPUNSAFE)) { + if (ccp->cc_flags & KMF_DUMPUNSAFE) { + ASSERT(!(ccp->cc_flags & KMF_DUMPDIVERT)); + /* log it so that we can warn about it */ + KDI_LOG(cp, kdl_unsafe); + } else if (KMEM_DUMPCC(ccp) && !kmem_cache_free_dump(cp, buf)) { + return; + } + if (ccp->cc_flags & KMF_BUFTAG) { + if (kmem_cache_free_debug(cp, buf, caller()) == -1) + return; + } + } + + mutex_enter(&ccp->cc_lock); + /* + * Any changes to this logic should be reflected in kmem_slab_prefill() + */ + for (;;) { + /* + * If there's a slot available in the current CPU's + * loaded magazine, just put the object there and return. + */ + if ((uint_t)ccp->cc_rounds < ccp->cc_magsize) { + ccp->cc_loaded->mag_round[ccp->cc_rounds++] = buf; + ccp->cc_free++; + mutex_exit(&ccp->cc_lock); + return; + } + + /* + * If the magazine layer is disabled, break out now. + */ + if (ccp->cc_magsize == 0) { + break; + } + + /* + * The magazine layer is on, but the loaded magazine is now + * full (of allocatable constructed elements). + * + * If the cache's arena is badly fragmented, break out now; + * this frees to the slab layer. + * + * Note: this is not reflected in kmem_slab_prefill() which + * deals with a freshly allocated slab. + */ + + if (kmem_free_to_slab_when_fragmented == 1 && + kmem_cache_parent_arena_fragmented(cp)) + break; + + /* + * The loaded magazine is full. If the previously loaded + * magazine was empty, exchange them and try again. + */ + if (ccp->cc_prounds == 0) { + kmem_cpu_reload(ccp, ccp->cc_ploaded, ccp->cc_prounds); + continue; + } + + if (!kmem_cpucache_magazine_alloc(ccp, cp)) { + /* + * We couldn't free our constructed object to the + * magazine layer, so apply its destructor and free it + * to the slab layer. + */ + break; + } + } + mutex_exit(&ccp->cc_lock); + kpreempt(KPREEMPT_SYNC); + kmem_slab_free_constructed(cp, buf, B_TRUE); +} + +/* + * Free a constructed object to cache cp. + * Do not free to the magazine layer. + * This is essentially just kmem_cache_free() without + * the for(;;) loop or the ccp critical section. + */ +void +kmem_cache_free_to_slab(kmem_cache_t *cp, void *buf) +{ + kmem_cpu_cache_t *ccp = KMEM_CPU_CACHE(cp); + + /* + * The client must not free either of the buffers passed to the move + * callback function. + */ + ASSERT(cp->cache_defrag == NULL || + cp->cache_defrag->kmd_thread != spl_current_thread() || + (buf != cp->cache_defrag->kmd_from_buf && + buf != cp->cache_defrag->kmd_to_buf)); + + if (ccp->cc_flags & (KMF_BUFTAG | KMF_DUMPDIVERT | KMF_DUMPUNSAFE)) { + if (ccp->cc_flags & KMF_DUMPUNSAFE) { + ASSERT(!(ccp->cc_flags & KMF_DUMPDIVERT)); + /* log it so that we can warn about it */ + KDI_LOG(cp, kdl_unsafe); + } else if (KMEM_DUMPCC(ccp) && !kmem_cache_free_dump(cp, buf)) { + return; + } + if (ccp->cc_flags & KMF_BUFTAG) { + if (kmem_cache_free_debug(cp, buf, caller()) == -1) + return; + } + } + + /* omitted the for(;;) loop from kmem_cache_free */ + /* also do not take ccp mutex */ + + kmem_slab_free_constructed(cp, buf, B_TRUE); +} + +static void +kmem_slab_prefill(kmem_cache_t *cp, kmem_slab_t *sp) +{ + kmem_cpu_cache_t *ccp = KMEM_CPU_CACHE(cp); + + kmem_bufctl_t *next, *head; + size_t nbufs; + + /* + * Completely allocate the newly created slab and put the pre-allocated + * buffers in magazines. Any of the buffers that cannot be put in + * magazines must be returned to the slab. + */ + ASSERT(MUTEX_HELD(&cp->cache_lock)); + ASSERT(cp->cache_constructor == NULL); + ASSERT(sp->slab_cache == cp); + ASSERT(sp->slab_refcnt == 1); + ASSERT(sp->slab_head != NULL && sp->slab_chunks > sp->slab_refcnt); + ASSERT(avl_find(&cp->cache_partial_slabs, sp, NULL) == NULL); + + head = sp->slab_head; + nbufs = (sp->slab_chunks - sp->slab_refcnt); + sp->slab_head = NULL; + sp->slab_refcnt += nbufs; + cp->cache_bufslab -= nbufs; + cp->cache_slab_alloc += nbufs; + list_insert_head(&cp->cache_complete_slabs, sp); + cp->cache_complete_slab_count++; + mutex_exit(&cp->cache_lock); + mutex_enter(&ccp->cc_lock); + + while (head != NULL) { + void *buf = KMEM_BUF(cp, head); + /* + * If there's a slot available in the current CPU's + * loaded magazine, just put the object there and + * continue. + */ + if ((uint_t)ccp->cc_rounds < ccp->cc_magsize) { + ccp->cc_loaded->mag_round[ccp->cc_rounds++] = + buf; + ccp->cc_free++; + nbufs--; + head = head->bc_next; + continue; + } + + /* + * The loaded magazine is full. If the previously + * loaded magazine was empty, exchange them and try + * again. + */ + if (ccp->cc_prounds == 0) { + kmem_cpu_reload(ccp, ccp->cc_ploaded, + ccp->cc_prounds); + continue; + } + + /* + * If the magazine layer is disabled, break out now. + */ + + if (ccp->cc_magsize == 0) { + break; + } + + if (!kmem_cpucache_magazine_alloc(ccp, cp)) + break; + } + mutex_exit(&ccp->cc_lock); + if (nbufs != 0) { + ASSERT(head != NULL); + + /* + * If there was a failure, return remaining objects to + * the slab + */ + while (head != NULL) { + ASSERT(nbufs != 0); + next = head->bc_next; + head->bc_next = NULL; + kmem_slab_free(cp, KMEM_BUF(cp, head)); + head = next; + nbufs--; + } + } + ASSERT(head == NULL); + ASSERT(nbufs == 0); + mutex_enter(&cp->cache_lock); +} + +void * +zfs_kmem_zalloc(size_t size, int kmflag) +{ + size_t index; + void *buf; + + if ((index = ((size - 1) >> KMEM_ALIGN_SHIFT)) < KMEM_ALLOC_TABLE_MAX) { + kmem_cache_t *cp = kmem_alloc_table[index]; + buf = kmem_cache_alloc(cp, kmflag); + if (buf != NULL) { + if ((cp->cache_flags & KMF_BUFTAG) && !KMEM_DUMP(cp)) { + kmem_buftag_t *btp = KMEM_BUFTAG(cp, buf); + ((uint8_t *)buf)[size] = KMEM_REDZONE_BYTE; + ((uint32_t *)btp)[1] = KMEM_SIZE_ENCODE(size); + + if (cp->cache_flags & KMF_LITE) { + KMEM_BUFTAG_LITE_ENTER(btp, + kmem_lite_count, caller()); + } + } + bzero(buf, size); + } + } else { + buf = zfs_kmem_alloc(size, kmflag); + if (buf != NULL) + bzero(buf, size); + } + return (buf); +} + +void * +zfs_kmem_alloc(size_t size, int kmflag) +{ + size_t index; + kmem_cache_t *cp; + void *buf; + + if (size == 0) + return (KMEM_ZERO_SIZE_PTR); + + if ((index = ((size - 1) >> KMEM_ALIGN_SHIFT)) < KMEM_ALLOC_TABLE_MAX) { + cp = kmem_alloc_table[index]; + /* fall through to kmem_cache_alloc() */ + + } else if ((index = ((size - 1) >> KMEM_BIG_SHIFT)) < + kmem_big_alloc_table_max) { + cp = kmem_big_alloc_table[index]; + /* fall through to kmem_cache_alloc() */ + + } else { + + buf = vmem_alloc(kmem_oversize_arena, size, + kmflag & KM_VMFLAGS); + if (buf == NULL) + kmem_log_event(kmem_failure_log, NULL, NULL, + (void *)size); + else if (KMEM_DUMP(kmem_slab_cache)) { + /* stats for dump intercept */ + kmem_dump_oversize_allocs++; + if (size > kmem_dump_oversize_max) + kmem_dump_oversize_max = size; + } + return (buf); + } + + buf = kmem_cache_alloc(cp, kmflag); + if ((cp->cache_flags & KMF_BUFTAG) && !KMEM_DUMP(cp) && buf != NULL) { + kmem_buftag_t *btp = KMEM_BUFTAG(cp, buf); + ((uint8_t *)buf)[size] = KMEM_REDZONE_BYTE; + ((uint32_t *)btp)[1] = KMEM_SIZE_ENCODE(size); + + if (cp->cache_flags & KMF_LITE) { + KMEM_BUFTAG_LITE_ENTER(btp, kmem_lite_count, caller()); + } + } + return (buf); +} + +void +zfs_kmem_free(void *buf, size_t size) +{ + size_t index; + kmem_cache_t *cp; + + if (size == 0 || buf == KMEM_ZERO_SIZE_PTR || buf == NULL) + return; + + if ((index = (size - 1) >> KMEM_ALIGN_SHIFT) < KMEM_ALLOC_TABLE_MAX) { + cp = kmem_alloc_table[index]; + /* fall through to kmem_cache_free() */ + + } else if ((index = ((size - 1) >> KMEM_BIG_SHIFT)) < + kmem_big_alloc_table_max) { + cp = kmem_big_alloc_table[index]; + /* fall through to kmem_cache_free() */ + + } else { + vmem_free(kmem_oversize_arena, buf, size); + return; + } + + if ((cp->cache_flags & KMF_BUFTAG) && !KMEM_DUMP(cp)) { + kmem_buftag_t *btp = KMEM_BUFTAG(cp, buf); + uint32_t *ip = (uint32_t *)btp; + if (ip[1] != KMEM_SIZE_ENCODE(size)) { + if (*(uint64_t *)buf == KMEM_FREE_PATTERN) { + kmem_error(KMERR_DUPFREE, cp, buf); + return; + } + if (KMEM_SIZE_VALID(ip[1])) { + ip[0] = KMEM_SIZE_ENCODE(size); + kmem_error(KMERR_BADSIZE, cp, buf); + } else { + kmem_error(KMERR_REDZONE, cp, buf); + } + return; + } + if (((uint8_t *)buf)[size] != KMEM_REDZONE_BYTE) { + kmem_error(KMERR_REDZONE, cp, buf); + return; + } + btp->bt_redzone = KMEM_REDZONE_PATTERN; + if (cp->cache_flags & KMF_LITE) { + KMEM_BUFTAG_LITE_ENTER(btp, kmem_lite_count, + caller()); + } + } + kmem_cache_free(cp, buf); +} + +/* + * Try to allocate at least `size' bytes of memory without sleeping or + * panicking. Return actual allocated size in `asize'. If allocation failed, + * try final allocation with sleep or panic allowed. + */ +void * +kmem_alloc_tryhard(size_t size, size_t *asize, int kmflag) +{ + void *p; + + *asize = P2ROUNDUP(size, KMEM_ALIGN); + do { + p = kmem_alloc(*asize, (kmflag | KM_NOSLEEP) & ~KM_PANIC); + if (p != NULL) + return (p); + *asize += KMEM_ALIGN; + } while (*asize <= PAGESIZE); + + *asize = P2ROUNDUP(size, KMEM_ALIGN); + return (zfs_kmem_alloc(*asize, kmflag)); +} + +/* + * Reclaim all unused memory from a cache. + */ +static void +kmem_cache_reap(kmem_cache_t *cp) +{ + ASSERT(taskq_member(kmem_taskq, curthread)); + + cp->cache_reap++; + + /* + * Ask the cache's owner to free some memory if possible. + * The idea is to handle things like the inode cache, which + * typically sits on a bunch of memory that it doesn't truly + * *need*. Reclaim policy is entirely up to the owner; this + * callback is just an advisory plea for help. + */ + if (cp->cache_reclaim != NULL) { + long delta; + + /* + * Reclaimed memory should be reapable (not included in the + * depot's working set). + */ + delta = cp->cache_full.ml_total; + cp->cache_reclaim(cp->cache_private); + delta = cp->cache_full.ml_total - delta; + if (delta > 0) { + mutex_enter(&cp->cache_depot_lock); + cp->cache_full.ml_reaplimit += delta; + cp->cache_full.ml_min += delta; + mutex_exit(&cp->cache_depot_lock); + } + } + + kmem_depot_ws_reap(cp); + + if (cp->cache_defrag != NULL && !kmem_move_noreap) { + kmem_cache_defrag(cp); + } +} + +static void +kmem_reap_timeout(void *flag_arg) +{ + uint32_t *flag = (uint32_t *)flag_arg; + + ASSERT(flag == &kmem_reaping || flag == &kmem_reaping_idspace); + *flag = 0; +} + +static void +kmem_reap_done(void *flag) +{ + (void) bsd_timeout(kmem_reap_timeout, flag, &kmem_reap_interval); +} + +static void +kmem_reap_start(void *flag) +{ + ASSERT(flag == &kmem_reaping || flag == &kmem_reaping_idspace); + + if (flag == &kmem_reaping) { + kmem_cache_applyall(kmem_cache_reap, kmem_taskq, TQ_NOSLEEP); + /* + * if we have segkp under heap, reap segkp cache. + */ + } + else + kmem_cache_applyall_id(kmem_cache_reap, kmem_taskq, TQ_NOSLEEP); + + /* + * We use taskq_dispatch() to schedule a timeout to clear + * the flag so that kmem_reap() becomes self-throttling: + * we won't reap again until the current reap completes *and* + * at least kmem_reap_interval ticks have elapsed. + */ + if (!taskq_dispatch(kmem_taskq, kmem_reap_done, flag, TQ_NOSLEEP)) + kmem_reap_done(flag); +} + +static void +kmem_reap_common(void *flag_arg) +{ + uint32_t *flag = (uint32_t *)flag_arg; + + + if (MUTEX_HELD(&kmem_cache_lock) || kmem_taskq == NULL || + atomic_cas_32(flag, 0, 1) != 0) + return; + + /* + * It may not be kosher to do memory allocation when a reap is called + * is called (for example, if vmem_populate() is in the call chain). + * So we start the reap going with a TQ_NOALLOC dispatch. If the + * dispatch fails, we reset the flag, and the next reap will try again. + */ + if (!taskq_dispatch(kmem_taskq, kmem_reap_start, flag, TQ_NOALLOC)) + *flag = 0; +} + +/* + * Reclaim all unused memory from all caches. Called from the VM system + * when memory gets tight. + */ +void +kmem_reap(void) +{ + kmem_reap_common(&kmem_reaping); +} + +/* + * Reclaim all unused memory from identifier arenas, called when a vmem + * arena not back by memory is exhausted. Since reaping memory-backed caches + * cannot help with identifier exhaustion, we avoid both a large amount of + * work and unwanted side-effects from reclaim callbacks. + */ +void +kmem_reap_idspace(void) +{ + kmem_reap_common(&kmem_reaping_idspace); +} + +/* + * Purge all magazines from a cache and set its magazine limit to zero. + * All calls are serialized by the kmem_taskq lock, except for the final + * call from kmem_cache_destroy(). + */ +static void +kmem_cache_magazine_purge(kmem_cache_t *cp) +{ + kmem_cpu_cache_t *ccp; + kmem_magazine_t *mp, *pmp; + int rounds, prounds, cpu_seqid; + + ASSERT(!list_link_active(&cp->cache_link) || + taskq_member(kmem_taskq, curthread)); + ASSERT(MUTEX_NOT_HELD(&cp->cache_lock)); + + for (cpu_seqid = 0; cpu_seqid < max_ncpus; cpu_seqid++) { + ccp = &cp->cache_cpu[cpu_seqid]; + + mutex_enter(&ccp->cc_lock); + mp = ccp->cc_loaded; + pmp = ccp->cc_ploaded; + rounds = ccp->cc_rounds; + prounds = ccp->cc_prounds; + ccp->cc_loaded = NULL; + ccp->cc_ploaded = NULL; + ccp->cc_rounds = -1; + ccp->cc_prounds = -1; + ccp->cc_magsize = 0; + mutex_exit(&ccp->cc_lock); + + if (mp) + kmem_magazine_destroy(cp, mp, rounds); + + if (pmp) + kmem_magazine_destroy(cp, pmp, prounds); + } + + kmem_depot_ws_zero(cp); + kmem_depot_ws_reap(cp); +} + +/* + * Enable per-cpu magazines on a cache. + */ +static void +kmem_cache_magazine_enable(kmem_cache_t *cp) +{ + int cpu_seqid; + + if (cp->cache_flags & KMF_NOMAGAZINE) + return; + + for (cpu_seqid = 0; cpu_seqid < max_ncpus; cpu_seqid++) { + kmem_cpu_cache_t *ccp = &cp->cache_cpu[cpu_seqid]; + mutex_enter(&ccp->cc_lock); + ccp->cc_magsize = cp->cache_magtype->mt_magsize; + mutex_exit(&ccp->cc_lock); + } + +} + +static void +kmem_cache_magazine_disable(kmem_cache_t *cp) +{ + int cpu_seqid; + + if (cp->cache_flags & KMF_NOMAGAZINE) + return; + + for (cpu_seqid = 0; cpu_seqid < max_ncpus; cpu_seqid++) { + kmem_cpu_cache_t *ccp = &cp->cache_cpu[cpu_seqid]; + mutex_enter(&ccp->cc_lock); + ccp->cc_magsize = 0; + mutex_exit(&ccp->cc_lock); + } + +} + +/* + * Allow our caller to determine if there are running reaps. + * + * This call is very conservative and may return B_TRUE even when + * reaping activity isn't active. If it returns B_FALSE, then reaping + * activity is definitely inactive. + */ +boolean_t +kmem_cache_reap_active(void) +{ + return (!taskq_empty(kmem_taskq)); +} + +/* + * Reap (almost) everything right now. + */ +void +kmem_cache_reap_now(kmem_cache_t *cp) +{ + ASSERT(list_link_active(&cp->cache_link)); + + kmem_depot_ws_zero(cp); + + (void) taskq_dispatch(kmem_taskq, + (task_func_t *)kmem_depot_ws_reap, cp, TQ_SLEEP); +} + +/* + * Recompute a cache's magazine size. The trade-off is that larger magazines + * provide a higher transfer rate with the depot, while smaller magazines + * reduce memory consumption. Magazine resizing is an expensive operation; + * it should not be done frequently. + * + * Changes to the magazine size are serialized by the kmem_taskq lock. + * + * Note: at present this only grows the magazine size. It might be useful + * to allow shrinkage too. + */ +static void +kmem_cache_magazine_resize(kmem_cache_t *cp) +{ + kmem_magtype_t *mtp = cp->cache_magtype; + + ASSERT(taskq_member(kmem_taskq, curthread)); + + if (cp->cache_chunksize < mtp->mt_maxbuf) { + kmem_cache_magazine_purge(cp); + mutex_enter(&cp->cache_depot_lock); + cp->cache_magtype = ++mtp; + cp->cache_depot_contention_prev = + cp->cache_depot_contention + INT_MAX; + mutex_exit(&cp->cache_depot_lock); + kmem_cache_magazine_enable(cp); + } +} + +/* + * Rescale a cache's hash table, so that the table size is roughly the + * cache size. We want the average lookup time to be extremely small. + */ +static void +kmem_hash_rescale(kmem_cache_t *cp) +{ + kmem_bufctl_t **old_table, **new_table, *bcp; + size_t old_size, new_size, h; + + ASSERT(taskq_member(kmem_taskq, curthread)); + + new_size = MAX(KMEM_HASH_INITIAL, + 1 << (highbit(3 * cp->cache_buftotal + 4) - 2)); + old_size = cp->cache_hash_mask + 1; + + if ((old_size >> 1) <= new_size && new_size <= (old_size << 1)) + return; + + new_table = vmem_alloc(kmem_hash_arena, new_size * sizeof (void *), + VM_NOSLEEP); + if (new_table == NULL) + return; + bzero(new_table, new_size * sizeof (void *)); + + mutex_enter(&cp->cache_lock); + + old_size = cp->cache_hash_mask + 1; + old_table = cp->cache_hash_table; + + cp->cache_hash_mask = new_size - 1; + cp->cache_hash_table = new_table; + cp->cache_rescale++; + + for (h = 0; h < old_size; h++) { + bcp = old_table[h]; + while (bcp != NULL) { + void *addr = bcp->bc_addr; + kmem_bufctl_t *next_bcp = bcp->bc_next; + kmem_bufctl_t **hash_bucket = KMEM_HASH(cp, addr); + bcp->bc_next = *hash_bucket; + *hash_bucket = bcp; + bcp = next_bcp; + } + } + + mutex_exit(&cp->cache_lock); + + vmem_free(kmem_hash_arena, old_table, old_size * sizeof (void *)); +} + +/* + * Perform periodic maintenance on a cache: hash rescaling, depot working-set + * update, magazine resizing, and slab consolidation. + */ +static void +kmem_cache_update(kmem_cache_t *cp) +{ + int need_hash_rescale = 0; + int need_magazine_resize = 0; + + /* + * If the cache has become much larger or smaller than its hash table, + * fire off a request to rescale the hash table. + */ + mutex_enter(&cp->cache_lock); + + if ((cp->cache_flags & KMF_HASH) && + (cp->cache_buftotal > (cp->cache_hash_mask << 1) || + (cp->cache_buftotal < (cp->cache_hash_mask >> 1) && + cp->cache_hash_mask > KMEM_HASH_INITIAL))) + need_hash_rescale = 1; + + mutex_exit(&cp->cache_lock); + + /* + * Update the depot working set statistics. + */ + kmem_depot_ws_update(cp); + + /* + * If there's a lot of contention in the depot, + * increase the magazine size. + */ + mutex_enter(&cp->cache_depot_lock); + + if (cp->cache_chunksize < cp->cache_magtype->mt_maxbuf && + (int)(cp->cache_depot_contention - + cp->cache_depot_contention_prev) > kmem_depot_contention) + need_magazine_resize = 1; + + cp->cache_depot_contention_prev = cp->cache_depot_contention; + + mutex_exit(&cp->cache_depot_lock); + + if (need_hash_rescale) + (void) taskq_dispatch(kmem_taskq, + (task_func_t *)kmem_hash_rescale, cp, TQ_NOSLEEP); + + if (need_magazine_resize) + (void) taskq_dispatch(kmem_taskq, + (task_func_t *)kmem_cache_magazine_resize, + cp, TQ_NOSLEEP); + + // smd : the following if is only true for the dnode cache + if (cp->cache_defrag != NULL) + (void) taskq_dispatch(kmem_taskq, + (task_func_t *)kmem_cache_scan, cp, TQ_NOSLEEP); + +#ifdef DEBUG + else { + // for every other cache, duplicate some of the logic from + // kmem_cache_scan() below + // run reap occasionally even if there is plenty of memory + uint16_t debug_rand; + + (void) random_get_bytes((uint8_t *)&debug_rand, 2); + if (!kmem_move_noreap && + ((debug_rand % kmem_mtb_reap) == 0)) { + /* + * no mutex above, so no need to give it up as + * in kmem_cache_scan() + */ + } + } +#endif + +} + +static void kmem_update(void *); + +static void +kmem_update_timeout(void *dummy) +{ + (void) bsd_timeout(kmem_update, dummy, &kmem_reap_interval); +} + +static void +kmem_update(void *dummy) +{ + kmem_cache_applyall(kmem_cache_update, NULL, TQ_NOSLEEP); + + /* + * We use taskq_dispatch() to reschedule the timeout so that + * kmem_update() becomes self-throttling: it won't schedule + * new tasks until all previous tasks have completed. + */ + if (!taskq_dispatch(kmem_taskq, kmem_update_timeout, dummy, TQ_NOSLEEP)) + kmem_update_timeout(NULL); + +} + +static int +kmem_cache_kstat_update(kstat_t *ksp, int rw) +{ + struct kmem_cache_kstat *kmcp = &kmem_cache_kstat; + kmem_cache_t *cp = ksp->ks_private; + uint64_t cpu_buf_avail; + uint64_t buf_avail = 0; + int cpu_seqid; + long reap; + + if (rw == KSTAT_WRITE) + return (EACCES); + + mutex_enter(&cp->cache_lock); + + kmcp->kmc_alloc_fail.value.ui64 = cp->cache_alloc_fail; + kmcp->kmc_alloc.value.ui64 = cp->cache_slab_alloc; + kmcp->kmc_free.value.ui64 = cp->cache_slab_free; + kmcp->kmc_slab_alloc.value.ui64 = cp->cache_slab_alloc; + kmcp->kmc_slab_free.value.ui64 = cp->cache_slab_free; + kmcp->kmc_no_vba_success.value.ui64 = cp->no_vba_success; + kmcp->kmc_no_vba_fail.value.ui64 = cp->no_vba_fail; + kmcp->kmc_arc_no_grow_set.value.ui64 = cp->arc_no_grow_set; + kmcp->kmc_arc_no_grow.value.ui64 = cp->arc_no_grow; + + for (cpu_seqid = 0; cpu_seqid < max_ncpus; cpu_seqid++) { + kmem_cpu_cache_t *ccp = &cp->cache_cpu[cpu_seqid]; + + mutex_enter(&ccp->cc_lock); + + cpu_buf_avail = 0; + if (ccp->cc_rounds > 0) + cpu_buf_avail += ccp->cc_rounds; + if (ccp->cc_prounds > 0) + cpu_buf_avail += ccp->cc_prounds; + + kmcp->kmc_alloc.value.ui64 += ccp->cc_alloc; + kmcp->kmc_free.value.ui64 += ccp->cc_free; + buf_avail += cpu_buf_avail; + + mutex_exit(&ccp->cc_lock); + } + + mutex_enter(&cp->cache_depot_lock); + + kmcp->kmc_depot_alloc.value.ui64 = cp->cache_full.ml_alloc; + kmcp->kmc_depot_free.value.ui64 = cp->cache_empty.ml_alloc; + kmcp->kmc_depot_contention.value.ui64 = cp->cache_depot_contention; + kmcp->kmc_full_magazines.value.ui64 = cp->cache_full.ml_total; + kmcp->kmc_empty_magazines.value.ui64 = cp->cache_empty.ml_total; + kmcp->kmc_magazine_size.value.ui64 = + (cp->cache_flags & KMF_NOMAGAZINE) ? + 0 : cp->cache_magtype->mt_magsize; + + kmcp->kmc_alloc.value.ui64 += cp->cache_full.ml_alloc; + kmcp->kmc_free.value.ui64 += cp->cache_empty.ml_alloc; + buf_avail += cp->cache_full.ml_total * cp->cache_magtype->mt_magsize; + + reap = MIN(cp->cache_full.ml_reaplimit, cp->cache_full.ml_min); + reap = MIN(reap, cp->cache_full.ml_total); + + mutex_exit(&cp->cache_depot_lock); + + kmcp->kmc_buf_size.value.ui64 = cp->cache_bufsize; + kmcp->kmc_align.value.ui64 = cp->cache_align; + kmcp->kmc_chunk_size.value.ui64 = cp->cache_chunksize; + kmcp->kmc_slab_size.value.ui64 = cp->cache_slabsize; + kmcp->kmc_buf_constructed.value.ui64 = buf_avail; + buf_avail += cp->cache_bufslab; + kmcp->kmc_buf_avail.value.ui64 = buf_avail; + kmcp->kmc_buf_inuse.value.ui64 = cp->cache_buftotal - buf_avail; + kmcp->kmc_buf_total.value.ui64 = cp->cache_buftotal; + kmcp->kmc_buf_max.value.ui64 = cp->cache_bufmax; + kmcp->kmc_slab_create.value.ui64 = cp->cache_slab_create; + kmcp->kmc_slab_destroy.value.ui64 = cp->cache_slab_destroy; + kmcp->kmc_hash_size.value.ui64 = (cp->cache_flags & KMF_HASH) ? + cp->cache_hash_mask + 1 : 0; + kmcp->kmc_hash_lookup_depth.value.ui64 = cp->cache_lookup_depth; + kmcp->kmc_hash_rescale.value.ui64 = cp->cache_rescale; + kmcp->kmc_vmem_source.value.ui64 = cp->cache_arena->vm_id; + kmcp->kmc_reap.value.ui64 = cp->cache_reap; + + if (cp->cache_defrag == NULL) { + kmcp->kmc_move_callbacks.value.ui64 = 0; + kmcp->kmc_move_yes.value.ui64 = 0; + kmcp->kmc_move_no.value.ui64 = 0; + kmcp->kmc_move_later.value.ui64 = 0; + kmcp->kmc_move_dont_need.value.ui64 = 0; + kmcp->kmc_move_dont_know.value.ui64 = 0; + kmcp->kmc_move_hunt_found.value.ui64 = 0; + kmcp->kmc_move_slabs_freed.value.ui64 = 0; + kmcp->kmc_defrag.value.ui64 = 0; + kmcp->kmc_scan.value.ui64 = 0; + kmcp->kmc_move_reclaimable.value.ui64 = 0; + } else { + int64_t reclaimable; + + kmem_defrag_t *kd = cp->cache_defrag; + kmcp->kmc_move_callbacks.value.ui64 = kd->kmd_callbacks; + kmcp->kmc_move_yes.value.ui64 = kd->kmd_yes; + kmcp->kmc_move_no.value.ui64 = kd->kmd_no; + kmcp->kmc_move_later.value.ui64 = kd->kmd_later; + kmcp->kmc_move_dont_need.value.ui64 = kd->kmd_dont_need; + kmcp->kmc_move_dont_know.value.ui64 = kd->kmd_dont_know; + kmcp->kmc_move_hunt_found.value.ui64 = 0; + kmcp->kmc_move_slabs_freed.value.ui64 = kd->kmd_slabs_freed; + kmcp->kmc_defrag.value.ui64 = kd->kmd_defrags; + kmcp->kmc_scan.value.ui64 = kd->kmd_scans; + + reclaimable = cp->cache_bufslab - (cp->cache_maxchunks - 1); + reclaimable = MAX(reclaimable, 0); + reclaimable += ((uint64_t)reap * cp->cache_magtype->mt_magsize); + kmcp->kmc_move_reclaimable.value.ui64 = reclaimable; + } + + mutex_exit(&cp->cache_lock); + return (0); +} + +/* + * Return a named statistic about a particular cache. + * This shouldn't be called very often, so it's currently designed for + * simplicity (leverages existing kstat support) rather than efficiency. + */ +uint64_t +kmem_cache_stat(kmem_cache_t *cp, char *name) +{ + int i; + kstat_t *ksp = cp->cache_kstat; + kstat_named_t *knp = (kstat_named_t *)&kmem_cache_kstat; + uint64_t value = 0; + + if (ksp != NULL) { + mutex_enter(&kmem_cache_kstat_lock); + (void) kmem_cache_kstat_update(ksp, KSTAT_READ); + for (i = 0; i < ksp->ks_ndata; i++) { + if (strcmp(knp[i].name, name) == 0) { + value = knp[i].value.ui64; + break; + } + } + mutex_exit(&kmem_cache_kstat_lock); + } + return (value); +} + +// TRUE if we have more than a critical minimum of memory +// used in arc_memory_throttle; if FALSE, we throttle +static inline bool +spl_minimal_physmem_p_logic() +{ + // do we have enough memory to avoid throttling? + if (spl_vm_pages_wanted > 0 || + (spl_vm_pressure_level > 0 && + spl_vm_pressure_level != MAGIC_PRESSURE_UNAVAILABLE)) + return (false); + // XXX : check reclaiming load? + return (true); +} + +int32_t +spl_minimal_physmem_p(void) +{ + + // arc will throttle throttle if we are paging, otherwise + // we want a small bit of pressure here so that we can compete + // a little with the xnu buffer cache + + return (spl_minimal_physmem_p_logic() && spl_free > -4096LL); +} + +/* + * Return the maximum amount of memory that is (in theory) allocatable + * from the heap. This may be used as an estimate only since there + * is no guarentee this space will still be available when an allocation + * request is made, nor that the space may be allocated in one big request + * due to kernel heap fragmentation. + */ +size_t +kmem_maxavail(void) +{ +#ifndef APPLE + // spgcnt_t pmem = availrmem - tune.t_minarmem; + // spgcnt_t vmem = btop(vmem_size(heap_arena, VMEM_FREE)); + // + // return ((size_t)ptob(MAX(MIN(pmem, vmem), 0))); +#endif + return (physmem * PAGE_SIZE); +} + +/* + * Indicate whether memory-intensive kmem debugging is enabled. + */ +int +kmem_debugging(void) +{ + return (kmem_flags & (KMF_AUDIT | KMF_REDZONE)); +} + +/* binning function, sorts finely at the two extremes */ +#define KMEM_PARTIAL_SLAB_WEIGHT(sp, binshift) \ +((((sp)->slab_refcnt <= (binshift)) || \ +(((sp)->slab_chunks - (sp)->slab_refcnt) <= (binshift))) \ +? -(sp)->slab_refcnt \ +: -((binshift) + ((sp)->slab_refcnt >> (binshift)))) + +/* + * Minimizing the number of partial slabs on the freelist minimizes + * fragmentation (the ratio of unused buffers held by the slab layer). There are + * two ways to get a slab off of the freelist: 1) free all the buffers on the + * slab, and 2) allocate all the buffers on the slab. It follows that we want + * the most-used slabs at the front of the list where they have the best chance + * of being completely allocated, and the least-used slabs at a safe distance + * from the front to improve the odds that the few remaining buffers will all be + * freed before another allocation can tie up the slab. For that reason a slab + * with a higher slab_refcnt sorts less than than a slab with a lower + * slab_refcnt. + * + * However, if a slab has at least one buffer that is deemed unfreeable, we + * would rather have that slab at the front of the list regardless of + * slab_refcnt, since even one unfreeable buffer makes the entire slab + * unfreeable. If the client returns KMEM_CBRC_NO in response to a cache_move() + * callback, the slab is marked unfreeable for as long as it remains on the + * freelist. + */ +static int +kmem_partial_slab_cmp(const void *pp0, const void *pp1) +{ + const kmem_cache_t *cp; + const kmem_slab_t *s0 = pp0; + const kmem_slab_t *s1 = pp1; + int w0, w1; + size_t binshift; + + ASSERT(KMEM_SLAB_IS_PARTIAL(s0)); + ASSERT(KMEM_SLAB_IS_PARTIAL(s1)); + ASSERT(s0->slab_cache == s1->slab_cache); + cp = s1->slab_cache; + ASSERT(MUTEX_HELD((struct kmutex *)&cp->cache_lock)); + binshift = cp->cache_partial_binshift; + + /* weight of first slab */ + w0 = KMEM_PARTIAL_SLAB_WEIGHT(s0, binshift); + if (s0->slab_flags & KMEM_SLAB_NOMOVE) { + w0 -= cp->cache_maxchunks; + } + + /* weight of second slab */ + w1 = KMEM_PARTIAL_SLAB_WEIGHT(s1, binshift); + if (s1->slab_flags & KMEM_SLAB_NOMOVE) { + w1 -= cp->cache_maxchunks; + } + + if (w0 < w1) + return (-1); + if (w0 > w1) + return (1); + + // compare slab age if available + hrtime_t c0 = s0->slab_create_time, c1 = s1->slab_create_time; + if (c0 != 0 && c1 != 0 && c0 != c1) { + // higher time is newer; newer sorts before older + if (c0 < c1) // c0 is older than c1 + return (1); // so c0 sorts after c1 + if (c0 > c1) + return (-1); + } + + /* compare pointer values */ + if ((uintptr_t)s0 < (uintptr_t)s1) + return (-1); + if ((uintptr_t)s0 > (uintptr_t)s1) + return (1); + + return (0); +} + +/* + * It must be valid to call the destructor (if any) on a newly created object. + * That is, the constructor (if any) must leave the object in a valid state for + * the destructor. + */ +kmem_cache_t * +kmem_cache_create( + char *name, /* descriptive name for this cache */ + size_t bufsize, /* size of the objects it manages */ + size_t align, /* required object alignment */ + int (*constructor)(void *, void *, int), /* object constructor */ + void (*destructor)(void *, void *), /* object destructor */ + void (*reclaim)(void *), /* memory reclaim callback */ + void *private, /* pass-thru arg for constr/destr/reclaim */ + vmem_t *vmp, /* vmem source for slab allocation */ + int cflags) /* cache creation flags */ +{ + int cpu_seqid; + size_t chunksize; + kmem_cache_t *cp; + kmem_magtype_t *mtp; + size_t csize = KMEM_CACHE_SIZE(max_ncpus); + +#ifdef DEBUG + /* + * Cache names should conform to the rules for valid C identifiers + */ + if (!strident_valid(name)) { + cmn_err(CE_CONT, + "kmem_cache_create: '%s' is an invalid cache name\n" + "cache names must conform to the rules for " + "C identifiers\n", name); + } +#endif /* DEBUG */ + + if (vmp == NULL) + vmp = kmem_default_arena; + + /* + * If this kmem cache has an identifier vmem arena as its source, mark + * it such to allow kmem_reap_idspace(). + */ + ASSERT(!(cflags & KMC_IDENTIFIER)); /* consumer should not set this */ + if (vmp->vm_cflags & VMC_IDENTIFIER) + cflags |= KMC_IDENTIFIER; + + /* + * Get a kmem_cache structure. We arrange that cp->cache_cpu[] + * is aligned on a KMEM_CPU_CACHE_SIZE boundary to prevent + * false sharing of per-CPU data. + */ + cp = vmem_xalloc(kmem_cache_arena, csize, + KMEM_CPU_CACHE_SIZE, + P2NPHASE(csize, KMEM_CPU_CACHE_SIZE), + 0, NULL, NULL, VM_SLEEP); + bzero(cp, csize); + list_link_init(&cp->cache_link); + + if (align == 0) + align = KMEM_ALIGN; + + /* + * If we're not at least KMEM_ALIGN aligned, we can't use free + * memory to hold bufctl information (because we can't safely + * perform word loads and stores on it). + */ + if (align < KMEM_ALIGN) + cflags |= KMC_NOTOUCH; + + if ((align & (align - 1)) != 0 || align > vmp->vm_quantum) + panic("kmem_cache_create: bad alignment %lu", align); + + mutex_enter(&kmem_flags_lock); + if (kmem_flags & KMF_RANDOMIZE) + kmem_flags = (((kmem_flags | ~KMF_RANDOM) + 1) & KMF_RANDOM) | + KMF_RANDOMIZE; + cp->cache_flags = (kmem_flags | cflags) & KMF_DEBUG; + mutex_exit(&kmem_flags_lock); + + /* + * Make sure all the various flags are reasonable. + */ + ASSERT(!(cflags & KMC_NOHASH) || !(cflags & KMC_NOTOUCH)); + + if (cp->cache_flags & KMF_LITE) { + if (bufsize >= kmem_lite_minsize && + align <= kmem_lite_maxalign && + P2PHASE(bufsize, kmem_lite_maxalign) != 0) { + cp->cache_flags |= KMF_BUFTAG; + cp->cache_flags &= ~(KMF_AUDIT | KMF_FIREWALL); + } else { + cp->cache_flags &= ~KMF_DEBUG; + } + } + + if (cp->cache_flags & KMF_DEADBEEF) + cp->cache_flags |= KMF_REDZONE; + + if ((cflags & KMC_QCACHE) && (cp->cache_flags & KMF_AUDIT)) + cp->cache_flags |= KMF_NOMAGAZINE; + + if (cflags & KMC_NODEBUG) + cp->cache_flags &= ~KMF_DEBUG; + + if (cflags & KMC_NOTOUCH) + cp->cache_flags &= ~KMF_TOUCH; + + if (cflags & KMC_PREFILL) + cp->cache_flags |= KMF_PREFILL; + + if (cflags & KMC_NOHASH) + cp->cache_flags &= ~(KMF_AUDIT | KMF_FIREWALL); + + if (cflags & KMC_NOMAGAZINE) + cp->cache_flags |= KMF_NOMAGAZINE; + + if ((cp->cache_flags & KMF_AUDIT) && !(cflags & KMC_NOTOUCH)) + cp->cache_flags |= KMF_REDZONE; + + if (!(cp->cache_flags & KMF_AUDIT)) + cp->cache_flags &= ~KMF_CONTENTS; + + if ((cp->cache_flags & KMF_BUFTAG) && bufsize >= kmem_minfirewall && + !(cp->cache_flags & KMF_LITE) && !(cflags & KMC_NOHASH)) + cp->cache_flags |= KMF_FIREWALL; + + if (vmp != kmem_default_arena || kmem_firewall_arena == NULL) + cp->cache_flags &= ~KMF_FIREWALL; + + if (cp->cache_flags & KMF_FIREWALL) { + cp->cache_flags &= ~KMF_BUFTAG; + cp->cache_flags |= KMF_NOMAGAZINE; + ASSERT(vmp == kmem_default_arena); + vmp = kmem_firewall_arena; + } + + /* + * Set cache properties. + */ + (void) strncpy(cp->cache_name, name, KMEM_CACHE_NAMELEN); + strident_canon(cp->cache_name, KMEM_CACHE_NAMELEN + 1); + cp->cache_bufsize = bufsize; + cp->cache_align = align; + cp->cache_constructor = constructor; + cp->cache_destructor = destructor; + cp->cache_reclaim = reclaim; + cp->cache_private = private; + cp->cache_arena = vmp; + cp->cache_cflags = cflags; + + /* + * Determine the chunk size. + */ + chunksize = bufsize; + + if (align >= KMEM_ALIGN) { + chunksize = P2ROUNDUP(chunksize, KMEM_ALIGN); + cp->cache_bufctl = chunksize - KMEM_ALIGN; + } + + if (cp->cache_flags & KMF_BUFTAG) { + cp->cache_bufctl = chunksize; + cp->cache_buftag = chunksize; + if (cp->cache_flags & KMF_LITE) + chunksize += KMEM_BUFTAG_LITE_SIZE(kmem_lite_count); + else + chunksize += sizeof (kmem_buftag_t); + } + + if (cp->cache_flags & KMF_DEADBEEF) { + cp->cache_verify = MIN(cp->cache_buftag, kmem_maxverify); + if (cp->cache_flags & KMF_LITE) + cp->cache_verify = sizeof (uint64_t); + } + + cp->cache_contents = MIN(cp->cache_bufctl, kmem_content_maxsave); + + cp->cache_chunksize = chunksize = P2ROUNDUP(chunksize, align); + + /* + * Now that we know the chunk size, determine the optimal slab size. + */ + + size_t vquantum = vmp->vm_quantum; + + if ((cflags & KMC_ARENA_SLAB) == KMC_ARENA_SLAB) { + VERIFY3U((vmp->vm_cflags & VMC_NO_QCACHE), ==, VMC_NO_QCACHE); + VERIFY3U(vmp->vm_min_import, >, 0); + VERIFY3U(vmp->vm_min_import, >=, (2 * vmp->vm_quantum)); + VERIFY(ISP2(vmp->vm_min_import)); + vquantum = vmp->vm_min_import >> 1; + } + + if (vmp == kmem_firewall_arena) { + cp->cache_slabsize = P2ROUNDUP(chunksize, vquantum); + cp->cache_mincolor = cp->cache_slabsize - chunksize; + cp->cache_maxcolor = cp->cache_mincolor; + cp->cache_flags |= KMF_HASH; + ASSERT(!(cp->cache_flags & KMF_BUFTAG)); + } else if ((cflags & KMC_NOHASH) || (!(cflags & KMC_NOTOUCH) && + !(cp->cache_flags & KMF_AUDIT) && + chunksize < vquantum / + KMEM_VOID_FRACTION)) { + cp->cache_slabsize = vquantum; + cp->cache_mincolor = 0; + cp->cache_maxcolor = + (cp->cache_slabsize - sizeof (kmem_slab_t)) % chunksize; + ASSERT(chunksize + sizeof (kmem_slab_t) <= cp->cache_slabsize); + ASSERT(!(cp->cache_flags & KMF_AUDIT)); + } else { + size_t chunks, bestfit, waste, slabsize; + size_t minwaste = LONG_MAX; + + for (chunks = 1; chunks <= KMEM_VOID_FRACTION; chunks++) { + slabsize = P2ROUNDUP(chunksize * chunks, + vquantum); + chunks = slabsize / chunksize; + waste = (slabsize % chunksize) / chunks; + if (waste < minwaste) { + minwaste = waste; + bestfit = slabsize; + } + } + if (cflags & KMC_QCACHE) + bestfit = VMEM_QCACHE_SLABSIZE(vmp->vm_qcache_max); + cp->cache_slabsize = bestfit; + cp->cache_mincolor = 0; + cp->cache_maxcolor = bestfit % chunksize; + cp->cache_flags |= KMF_HASH; + } + + cp->cache_maxchunks = (cp->cache_slabsize / cp->cache_chunksize); + cp->cache_partial_binshift = highbit(cp->cache_maxchunks / 16) + 1; + + /* + * Disallowing prefill when either the DEBUG or HASH flag is set or when + * there is a constructor avoids some tricky issues with debug setup + * that may be revisited later. We cannot allow prefill in a + * metadata cache because of potential recursion. + */ + if (vmp == kmem_msb_arena || + cp->cache_flags & (KMF_HASH | KMF_BUFTAG) || + cp->cache_constructor != NULL) + cp->cache_flags &= ~KMF_PREFILL; + + if (cp->cache_flags & KMF_HASH) { + ASSERT(!(cflags & KMC_NOHASH)); + cp->cache_bufctl_cache = (cp->cache_flags & KMF_AUDIT) ? + kmem_bufctl_audit_cache : kmem_bufctl_cache; + } + + if (cp->cache_maxcolor >= vquantum) + cp->cache_maxcolor = vquantum - 1; + + cp->cache_color = cp->cache_mincolor; + + /* + * Initialize the rest of the slab layer. + */ + mutex_init(&cp->cache_lock, NULL, MUTEX_DEFAULT, NULL); + + avl_create(&cp->cache_partial_slabs, kmem_partial_slab_cmp, + sizeof (kmem_slab_t), offsetof(kmem_slab_t, slab_link)); + /* LINTED: E_TRUE_LOGICAL_EXPR */ + ASSERT(sizeof (list_node_t) <= sizeof (avl_node_t)); + /* reuse partial slab AVL linkage for complete slab list linkage */ + list_create(&cp->cache_complete_slabs, + sizeof (kmem_slab_t), offsetof(kmem_slab_t, slab_link)); + + if (cp->cache_flags & KMF_HASH) { + cp->cache_hash_table = vmem_alloc(kmem_hash_arena, + KMEM_HASH_INITIAL * sizeof (void *), + VM_SLEEP); + bzero(cp->cache_hash_table, + KMEM_HASH_INITIAL * sizeof (void *)); + cp->cache_hash_mask = KMEM_HASH_INITIAL - 1; + cp->cache_hash_shift = highbit((ulong_t)chunksize) - 1; + } + + /* + * Initialize the depot. + */ + mutex_init(&cp->cache_depot_lock, NULL, MUTEX_DEFAULT, NULL); + + for (mtp = kmem_magtype; chunksize <= mtp->mt_minbuf; mtp++) + continue; + + cp->cache_magtype = mtp; + + /* + * Initialize the CPU layer. + */ + for (cpu_seqid = 0; cpu_seqid < max_ncpus; cpu_seqid++) { + kmem_cpu_cache_t *ccp = &cp->cache_cpu[cpu_seqid]; + mutex_init(&ccp->cc_lock, NULL, MUTEX_DEFAULT, NULL); // XNU + ccp->cc_flags = cp->cache_flags; + ccp->cc_rounds = -1; + ccp->cc_prounds = -1; + } + + /* + * Create the cache's kstats. + */ + if ((cp->cache_kstat = kstat_create("unix", 0, cp->cache_name, + "kmem_cache", KSTAT_TYPE_NAMED, + sizeof (kmem_cache_kstat) / sizeof (kstat_named_t), + KSTAT_FLAG_VIRTUAL)) != NULL) { + cp->cache_kstat->ks_data = &kmem_cache_kstat; + cp->cache_kstat->ks_update = kmem_cache_kstat_update; + cp->cache_kstat->ks_private = cp; + cp->cache_kstat->ks_lock = &kmem_cache_kstat_lock; + kstat_install(cp->cache_kstat); + } + + /* + * Add the cache to the global list. This makes it visible + * to kmem_update(), so the cache must be ready for business. + */ + mutex_enter(&kmem_cache_lock); + list_insert_tail(&kmem_caches, cp); + mutex_exit(&kmem_cache_lock); + + if (kmem_ready) + kmem_cache_magazine_enable(cp); + + return (cp); +} + +static int +kmem_move_cmp(const void *buf, const void *p) +{ + const kmem_move_t *kmm = p; + uintptr_t v1 = (uintptr_t)buf; + uintptr_t v2 = (uintptr_t)kmm->kmm_from_buf; + return (v1 < v2 ? -1 : (v1 > v2 ? 1 : 0)); +} + +static void +kmem_reset_reclaim_threshold(kmem_defrag_t *kmd) +{ + kmd->kmd_reclaim_numer = 1; +} + +/* + * Initially, when choosing candidate slabs for buffers to move, we want to be + * very selective and take only slabs that are less than + * (1 / KMEM_VOID_FRACTION) allocated. If we have difficulty finding candidate + * slabs, then we raise the allocation ceiling incrementally. The reclaim + * threshold is reset to (1 / KMEM_VOID_FRACTION) as soon as the cache is no + * longer fragmented. + */ +static void +kmem_adjust_reclaim_threshold(kmem_defrag_t *kmd, int direction) +{ + if (direction > 0) { + /* make it easier to find a candidate slab */ + if (kmd->kmd_reclaim_numer < (KMEM_VOID_FRACTION - 1)) { + kmd->kmd_reclaim_numer++; + } + } else { + /* be more selective */ + if (kmd->kmd_reclaim_numer > 1) { + kmd->kmd_reclaim_numer--; + } + } +} + +uint64_t +spl_kmem_cache_inuse(kmem_cache_t *cache) +{ + return (cache->cache_buftotal); +} + +uint64_t +spl_kmem_cache_entry_size(kmem_cache_t *cache) +{ + return (cache->cache_bufsize); +} + +void +kmem_cache_set_move(kmem_cache_t *cp, + kmem_cbrc_t (*move)(void *, void *, size_t, void *)) +{ + kmem_defrag_t *defrag; + + ASSERT(move != NULL); + /* + * The consolidator does not support NOTOUCH caches because kmem cannot + * initialize their slabs with the 0xbaddcafe memory pattern, which sets + * a low order bit usable by clients to distinguish uninitialized memory + * from known objects (see kmem_slab_create). + */ + ASSERT(!(cp->cache_cflags & KMC_NOTOUCH)); + ASSERT(!(cp->cache_cflags & KMC_IDENTIFIER)); + + /* + * We should not be holding anyone's cache lock when calling + * kmem_cache_alloc(), so allocate in all cases before acquiring the + * lock. + */ + defrag = kmem_cache_alloc(kmem_defrag_cache, KM_SLEEP); + + mutex_enter(&cp->cache_lock); + + if (KMEM_IS_MOVABLE(cp)) { + if (cp->cache_move == NULL) { + ASSERT(cp->cache_slab_alloc == 0); + + cp->cache_defrag = defrag; + defrag = NULL; /* nothing to free */ + bzero(cp->cache_defrag, sizeof (kmem_defrag_t)); + avl_create(&cp->cache_defrag->kmd_moves_pending, + kmem_move_cmp, sizeof (kmem_move_t), + offsetof(kmem_move_t, kmm_entry)); + /* LINTED: E_TRUE_LOGICAL_EXPR */ + ASSERT(sizeof (list_node_t) <= sizeof (avl_node_t)); + /* reuse the slab's AVL linkage for deadlist linkage */ + list_create(&cp->cache_defrag->kmd_deadlist, + sizeof (kmem_slab_t), + offsetof(kmem_slab_t, slab_link)); + kmem_reset_reclaim_threshold(cp->cache_defrag); + } + cp->cache_move = move; + } + + mutex_exit(&cp->cache_lock); + + if (defrag != NULL) { + kmem_cache_free(kmem_defrag_cache, defrag); /* unused */ + } +} + +void +kmem_qcache_destroy() +{ + kmem_cache_t *cp; + kmem_cache_t *cache_to_destroy = NULL; + + do { + cache_to_destroy = NULL; + mutex_enter(&kmem_cache_lock); + for (cp = list_head(&kmem_caches); cp != NULL; + cp = list_next(&kmem_caches, cp)) { + if (cp->cache_cflags & KMC_QCACHE) { + cache_to_destroy = cp; + break; + } + } + mutex_exit(&kmem_cache_lock); + + if (cache_to_destroy) { + kmem_cache_destroy(cache_to_destroy); + } + } while (cache_to_destroy); +} + +void +kmem_cache_destroy(kmem_cache_t *cp) +{ + int cpu_seqid; + + /* + * Remove the cache from the global cache list so that no one else + * can schedule tasks on its behalf, wait for any pending tasks to + * complete, purge the cache, and then destroy it. + */ + mutex_enter(&kmem_cache_lock); + list_remove(&kmem_caches, cp); + mutex_exit(&kmem_cache_lock); + + if (kmem_taskq != NULL) + taskq_wait(kmem_taskq); + + if (kmem_move_taskq != NULL && cp->cache_defrag != NULL) + taskq_wait(kmem_move_taskq); + + kmem_cache_magazine_purge(cp); + + mutex_enter(&cp->cache_lock); + + if (cp->cache_buftotal != 0) + cmn_err(CE_WARN, "kmem_cache_destroy: '%s' (%p) not empty", + cp->cache_name, (void *)cp); + if (cp->cache_defrag != NULL) { + avl_destroy(&cp->cache_defrag->kmd_moves_pending); + list_destroy(&cp->cache_defrag->kmd_deadlist); + kmem_cache_free(kmem_defrag_cache, cp->cache_defrag); + cp->cache_defrag = NULL; + } + /* + * The cache is now dead. There should be no further activity. We + * enforce this by setting land mines in the constructor, destructor, + * reclaim, and move routines that induce a kernel text fault if + * invoked. + */ +#if defined(__aarch64__) + /* Setting landmines causes Break 0xC470: Ptrauth failure */ + cp->cache_constructor = (int (*)(void *, void *, int))NULL; + cp->cache_destructor = (void (*)(void *, void *))NULL; + cp->cache_reclaim = (void (*)(void *))NULL; + cp->cache_move = (kmem_cbrc_t (*)(void *, void *, size_t, void *))NULL; +#else + cp->cache_constructor = (int (*)(void *, void *, int))1; + cp->cache_destructor = (void (*)(void *, void *))2; + cp->cache_reclaim = (void (*)(void *))3; + cp->cache_move = (kmem_cbrc_t (*)(void *, void *, size_t, void *))4; +#endif + mutex_exit(&cp->cache_lock); + + kstat_delete(cp->cache_kstat); + + if (cp->cache_hash_table != NULL) + vmem_free(kmem_hash_arena, cp->cache_hash_table, + (cp->cache_hash_mask + 1) * sizeof (void *)); + + for (cpu_seqid = 0; cpu_seqid < max_ncpus; cpu_seqid++) + mutex_destroy(&cp->cache_cpu[cpu_seqid].cc_lock); + + mutex_destroy(&cp->cache_depot_lock); + mutex_destroy(&cp->cache_lock); + + vmem_free(kmem_cache_arena, cp, KMEM_CACHE_SIZE(max_ncpus)); +} + +static void +kmem_alloc_caches_create(const int *array, size_t count, + kmem_cache_t **alloc_table, size_t maxbuf, + uint_t shift) +{ + char name[KMEM_CACHE_NAMELEN + 1]; + size_t table_unit = (1 << shift); /* range of one alloc_table entry */ + size_t size = table_unit; + int i; + + for (i = 0; i < count; i++) { + size_t cache_size = array[i]; + size_t align = KMEM_ALIGN; + kmem_cache_t *cp; + + /* if the table has an entry for maxbuf, we're done */ + if (size > maxbuf) + break; + + /* cache size must be a multiple of the table unit */ + ASSERT(P2PHASE(cache_size, table_unit) == 0); + + /* + * If they allocate a multiple of the coherency granularity, + * they get a coherency-granularity-aligned address. + */ + if (IS_P2ALIGNED(cache_size, 64)) + align = 64; + if (IS_P2ALIGNED(cache_size, PAGESIZE)) + align = PAGESIZE; + (void) snprintf(name, sizeof (name), + "kmem_alloc_%lu", cache_size); + cp = kmem_cache_create(name, cache_size, align, + NULL, NULL, NULL, NULL, NULL, KMC_KMEM_ALLOC | KMF_HASH); + + while (size <= cache_size) { + alloc_table[(size - 1) >> shift] = cp; + size += table_unit; + } + } + + ASSERT(size > maxbuf); /* i.e. maxbuf <= max(cache_size) */ +} + +static void +kmem_alloc_caches_destroy() +{ + kmem_cache_t *cache_to_destroy = NULL; + kmem_cache_t *cp = NULL; + + do { + cache_to_destroy = NULL; + + // Locate the first cache that has the KMC_KMEM_ALLOC flag. + mutex_enter(&kmem_cache_lock); + + for (cp = list_head(&kmem_caches); cp != NULL; + cp = list_next(&kmem_caches, cp)) { + if (cp->cache_cflags & KMC_KMEM_ALLOC) { + cache_to_destroy = cp; + break; + } + } + + mutex_exit(&kmem_cache_lock); + + // Destroy the cache + if (cache_to_destroy) { + kmem_cache_destroy(cache_to_destroy); + } + + } while (cache_to_destroy); +} + +static void +kmem_destroy_cache_by_name(const char *substr) +{ + kmem_cache_t *cache_to_destroy = NULL; + kmem_cache_t *cp = NULL; + + do { + cache_to_destroy = NULL; + + // Locate the first cache that has the KMC_KMEM_ALLOC flag. + mutex_enter(&kmem_cache_lock); + + for (cp = list_head(&kmem_caches); cp != NULL; + cp = list_next(&kmem_caches, cp)) { + if (kmem_strstr(cp->cache_name, substr)) { + cache_to_destroy = cp; + break; + } + } + + mutex_exit(&kmem_cache_lock); + + // Destroy the cache + if (cache_to_destroy) { + kmem_cache_destroy(cache_to_destroy); + } + + } while (cache_to_destroy); +} + +static void +kmem_cache_init(int pass, int use_large_pages) +{ + int i; + size_t maxbuf; + kmem_magtype_t *mtp; + + for (i = 0; i < sizeof (kmem_magtype) / sizeof (*mtp); i++) { + char name[KMEM_CACHE_NAMELEN + 1]; + + mtp = &kmem_magtype[i]; + (void) snprintf(name, KMEM_CACHE_NAMELEN, "%s%d", + KMEM_MAGAZINE_PREFIX, + mtp->mt_magsize); + mtp->mt_cache = kmem_cache_create( + name, + (mtp->mt_magsize + 1) * sizeof (void *), + mtp->mt_align, NULL, NULL, NULL, NULL, + kmem_msb_arena, KMC_NOHASH); + } + + kmem_slab_cache = kmem_cache_create("kmem_slab_cache", + sizeof (kmem_slab_t), 0, NULL, NULL, + NULL, NULL, + kmem_msb_arena, KMC_NOHASH); + + kmem_bufctl_cache = kmem_cache_create("kmem_bufctl_cache", + sizeof (kmem_bufctl_t), 0, + NULL, NULL, NULL, NULL, + kmem_msb_arena, KMC_NOHASH); + + kmem_bufctl_audit_cache = kmem_cache_create("kmem_bufctl_audit_cache", + sizeof (kmem_bufctl_audit_t), + 0, NULL, NULL, NULL, NULL, + kmem_msb_arena, KMC_NOHASH); + + if (pass == 2) { + kmem_va_arena = vmem_create(KMEM_VA_PREFIX, + NULL, 0, PAGESIZE, + vmem_alloc, vmem_free, heap_arena, + 2 * PAGESIZE, VM_SLEEP); + + kmem_default_arena = vmem_create("kmem_default", + NULL, 0, PAGESIZE, + vmem_alloc, vmem_free, kmem_va_arena, + 0, VMC_DUMPSAFE | VM_SLEEP); + + /* Figure out what our maximum cache size is */ + maxbuf = kmem_max_cached; + if (maxbuf <= KMEM_MAXBUF) { + maxbuf = 0; + kmem_max_cached = KMEM_MAXBUF; + } else { + size_t size = 0; + size_t max = + sizeof (kmem_big_alloc_sizes) / sizeof (int); + /* + * Round maxbuf up to an existing cache size. If maxbuf + * is larger than the largest cache, we truncate it to + * the largest cache's size. + */ + for (i = 0; i < max; i++) { + size = kmem_big_alloc_sizes[i]; + if (maxbuf <= size) + break; + } + kmem_max_cached = maxbuf = size; + } + + /* + * The big alloc table may not be completely overwritten, so + * we clear out any stale cache pointers from the first pass. + */ + bzero(kmem_big_alloc_table, sizeof (kmem_big_alloc_table)); + } else { + /* + * During the first pass, the kmem_alloc_* caches + * are treated as metadata. + */ + kmem_default_arena = kmem_msb_arena; + maxbuf = KMEM_BIG_MAXBUF_32BIT; + } + + /* + * Set up the default caches to back kmem_alloc() + */ + kmem_alloc_caches_create( + kmem_alloc_sizes, sizeof (kmem_alloc_sizes) / sizeof (int), + kmem_alloc_table, KMEM_MAXBUF, KMEM_ALIGN_SHIFT); + + kmem_alloc_caches_create( + kmem_big_alloc_sizes, sizeof (kmem_big_alloc_sizes) / sizeof (int), + kmem_big_alloc_table, maxbuf, KMEM_BIG_SHIFT); + + kmem_big_alloc_table_max = maxbuf >> KMEM_BIG_SHIFT; +} + +struct free_slab { + vmem_t *vmp; + size_t slabsize; + void *slab; + list_node_t next; +}; + +static list_t freelist; + + +void +kmem_cache_build_slablist(kmem_cache_t *cp) +{ + int cpu_seqid; + + vmem_t *vmp = cp->cache_arena; + kmem_slab_t *sp; + struct free_slab *fs; + + for (sp = list_head(&cp->cache_complete_slabs); sp != NULL; + sp = list_next(&cp->cache_complete_slabs, sp)) { + + MALLOC(fs, struct free_slab *, sizeof (struct free_slab), + M_TEMP, M_WAITOK); + fs->vmp = vmp; + fs->slabsize = cp->cache_slabsize; + fs->slab = (void *)P2ALIGN((uintptr_t)sp->slab_base, + vmp->vm_quantum); + list_link_init(&fs->next); + list_insert_tail(&freelist, fs); + } + + for (sp = avl_first(&cp->cache_partial_slabs); sp != NULL; + sp = AVL_NEXT(&cp->cache_partial_slabs, sp)) { + + MALLOC(fs, struct free_slab *, sizeof (struct free_slab), + M_TEMP, M_WAITOK); + fs->vmp = vmp; + fs->slabsize = cp->cache_slabsize; + fs->slab = (void *)P2ALIGN((uintptr_t)sp->slab_base, + vmp->vm_quantum); + list_link_init(&fs->next); + list_insert_tail(&freelist, fs); + } + + + kstat_delete(cp->cache_kstat); + + if (cp->cache_hash_table != NULL) + vmem_free(kmem_hash_arena, cp->cache_hash_table, + (cp->cache_hash_mask + 1) * sizeof (void *)); + + for (cpu_seqid = 0; cpu_seqid < max_ncpus; cpu_seqid++) + mutex_destroy(&cp->cache_cpu[cpu_seqid].cc_lock); + + mutex_destroy(&cp->cache_depot_lock); + mutex_destroy(&cp->cache_lock); + + vmem_free(kmem_cache_arena, cp, KMEM_CACHE_SIZE(max_ncpus)); +} + + +static void +kmem_cache_fini() +{ + kmem_cache_t *cp; + int i; + struct free_slab *fs; + + list_create(&freelist, sizeof (struct free_slab), + offsetof(struct free_slab, next)); + + mutex_enter(&kmem_cache_lock); + + while ((cp = list_head(&kmem_caches))) { + list_remove(&kmem_caches, cp); + mutex_exit(&kmem_cache_lock); + kmem_cache_build_slablist(cp); + mutex_enter(&kmem_cache_lock); + } + + mutex_exit(&kmem_cache_lock); + + i = 0; + while ((fs = list_head(&freelist))) { + i++; + list_remove(&freelist, fs); + vmem_free(fs->vmp, fs->slab, fs->slabsize); + FREE(fs, M_TEMP); + + } + printf("SPL: Released %u slabs\n", i); + list_destroy(&freelist); +} + +// this is intended to substitute for kmem_avail() in arc.c +int64_t +spl_free_wrapper(void) +{ + return (spl_free); +} + +// this is intended to substitute for kmem_avail() in arc.c +// when arc_reclaim_thread() calls spl_free_set_pressure(0); +int64_t +spl_free_manual_pressure_wrapper(void) +{ + return (spl_free_manual_pressure); +} + +uint64_t +spl_free_last_pressure_wrapper(void) +{ + return (spl_free_last_pressure); +} + +int64_t +spl_free_set_and_wait_pressure(int64_t new_p, boolean_t fast, + clock_t check_interval) +{ + + int64_t snapshot_pressure = 0; + + if (new_p <= 0) + return (0); + + spl_free_fast_pressure = fast; + + if (spl_free_manual_pressure >= 0) + spl_free_manual_pressure += new_p; + else + spl_free_manual_pressure = new_p; + + // wait for another thread to reset pressure + const uint64_t start = zfs_lbolt(); + const uint64_t end_by = start + (hz*60); + const uint64_t double_at = start + (hz/2); + const uint64_t double_again_at = start + hz; + bool doubled = false, doubled_again = false; + uint64_t now; + + spl_free_last_pressure = start; + + for (; spl_free_manual_pressure != 0; ) { + // has another thread set spl_free_manual_pressure? + if (spl_free_manual_pressure < new_p) + spl_free_manual_pressure = new_p; + snapshot_pressure = spl_free_manual_pressure; + mutex_enter(&spl_free_thread_lock); + cv_timedwait_hires(&spl_free_thread_cv, + &spl_free_thread_lock, check_interval, 0, 0); + mutex_exit(&spl_free_thread_lock); + now = zfs_lbolt(); + if (now > end_by) { + printf("%s: ERROR: timed out after one minute!\n", + __func__); + break; + } else if (now > double_again_at && !doubled_again) { + doubled_again = true; + new_p *= 2; + } else if (now > double_at) { + doubled = true; + new_p *= 2; + } + } + return (snapshot_pressure); +} + +// routinely called by arc_reclaim_thread() with new_p == 0 +void +spl_free_set_pressure(int64_t new_p) +{ + if (new_p > spl_free_manual_pressure || new_p <= 0) + spl_free_manual_pressure = new_p; + if (new_p == 0) { + spl_free_fast_pressure = FALSE; + // wake up both spl_free_thread() to recalculate spl_free + // and any spl_free_set_and_wait_pressure() threads + cv_broadcast(&spl_free_thread_cv); + } + spl_free_last_pressure = zfs_lbolt(); +} + +void +spl_free_set_pressure_both(int64_t new_p, boolean_t fast) +{ + spl_free_fast_pressure = fast; + if (new_p > spl_free_manual_pressure || new_p <= 0) + spl_free_manual_pressure = new_p; + spl_free_last_pressure = zfs_lbolt(); +} + +void spl_free_maybe_reap(void); + +void +spl_free_set_emergency_pressure(int64_t new_p) +{ + spl_free_fast_pressure = TRUE; + if (new_p > spl_free_manual_pressure || new_p <= 0) + spl_free_manual_pressure = new_p; + spl_free_maybe_reap(); + spl_free_last_pressure = zfs_lbolt(); +} + +void +spl_free_set_emergency_pressure_additive(int64_t new_p) +{ + spl_free_fast_pressure = TRUE; + spl_free_manual_pressure += new_p; + spl_free_last_pressure = zfs_lbolt(); +} + +void +spl_free_set_pressure_additive(int64_t new_p) +{ + spl_free_manual_pressure += new_p; + spl_free_last_pressure = zfs_lbolt(); +} + +boolean_t +spl_free_fast_pressure_wrapper() +{ + return (spl_free_fast_pressure); +} + +void +spl_free_set_fast_pressure(boolean_t state) +{ + spl_free_fast_pressure = state; + spl_free_last_pressure = zfs_lbolt(); +} + +void +spl_free_reap_caches(void) +{ + // note: this may take some time + static hrtime_t last_reap = 0; + const hrtime_t reap_after = SEC2NSEC(60); + const hrtime_t curtime = gethrtime(); + + if (curtime - last_reap < reap_after) + return; + + vmem_qcache_reap(abd_arena); + kmem_reap(); + vmem_qcache_reap(kmem_va_arena); +} + +void +spl_free_maybe_reap(void) +{ + static _Atomic uint64_t last_reap = 0; + const uint64_t lockout_time = 60 * hz; + + uint64_t now = zfs_lbolt(); + if (now > last_reap + lockout_time) { + last_reap = now; + spl_free_maybe_reap_flag = true; + } +} + +boolean_t +spl_maybe_send_large_pressure(uint64_t now, uint64_t minutes, boolean_t full) +{ + static volatile _Atomic uint64_t spl_last_large_pressure = 0; + const uint64_t interval_ticks = minutes * 60ULL * (uint64_t)hz; + + if (spl_last_large_pressure + interval_ticks > now) + return (false); + + spl_last_large_pressure = now; + + const int64_t sixteenth_physmem = (int64_t)real_total_memory / 16LL; + const int64_t sixtyfourth_physmem = sixteenth_physmem / 4LL; + int64_t howmuch = sixteenth_physmem; + + if (full == false) + howmuch = sixtyfourth_physmem; + + + dprintf("SPL: %s: %lld bytes at time %llu\n", + __func__, howmuch, now); + + spl_free_set_emergency_pressure(howmuch); + + return (true); +} + +static void +spl_free_thread() +{ + callb_cpr_t cpr; + uint64_t last_update = zfs_lbolt(); + int64_t last_spl_free; + double ema_new = 0; + double ema_old = 0; + double alpha; + + CALLB_CPR_INIT(&cpr, &spl_free_thread_lock, callb_generic_cpr, FTAG); + + /* initialize with a reasonably large amount of memory */ + spl_free = MAX(4*1024*1024*1024, + total_memory * 75ULL / 100ULL); + + mutex_enter(&spl_free_thread_lock); + + dprintf("SPL: beginning spl_free_thread() loop, spl_free == %lld\n", + spl_free); + + uint64_t recent_lowmem = 0; + uint64_t last_disequilibrium = 0; + + while (!spl_free_thread_exit) { + mutex_exit(&spl_free_thread_lock); + boolean_t lowmem = false; + boolean_t emergency_lowmem = false; + int64_t base; + int64_t new_spl_free = 0LL; + + spl_stats.spl_free_wake_count.value.ui64++; + + if (spl_free_maybe_reap_flag == true) { + spl_free_maybe_reap_flag = false; + spl_free_reap_caches(); + } + + uint64_t time_now = zfs_lbolt(); + uint64_t time_now_seconds = 0; + if (time_now > hz) + time_now_seconds = time_now / hz; + + last_spl_free = spl_free; + + new_spl_free = total_memory - + segkmem_total_mem_allocated; + + /* Ask Mach about pressure */ + + /* + * Don't wait for pressure, just report back + * how much has changed while we were asleep + * (cf. the duration of cv_timedwait_hires + * at justwait: below -- 10 msec is an eternity + * on most hardware, and the osfmk/vm/vm_pageout.c + * code is linear in the nsecs_wanted parameter + * + */ + + uint32_t pages_reclaimed = 0; + uint32_t pages_wanted = 0; + kern_return_t kr_mon = + mach_vm_pressure_monitor(false, + MSEC2NSEC(10), + &pages_reclaimed, + &pages_wanted); + + if (kr_mon == KERN_SUCCESS) { + spl_vm_pages_reclaimed = + pages_reclaimed; + spl_vm_pages_wanted = + pages_wanted; + } else { + printf("%s:%d : mach_vm_pressure_monitor" + " returned error %d, keeping old" + " values reclaimed %u wanted %u\n", + __FILE__, __LINE__, + kr_mon, + spl_vm_pages_reclaimed, + spl_vm_pages_wanted); + } + + /* + * Don't wait for pressure, just report + * back the pressure level + */ + + uint32_t pressure_level = 0; + kr_mon = mach_vm_pressure_level_monitor(false, + &pressure_level); + + if (kr_mon == KERN_SUCCESS) { + spl_vm_pressure_level = + pressure_level; + } else if (kr_mon == KERN_FAILURE) { + /* optioned out of xnu, use SOS value */ + spl_vm_pressure_level = MAGIC_PRESSURE_UNAVAILABLE; + } else { + printf("%s:%d : mach_vm_pressure_level_monitor" + " returned unexpected error %d," + " keeping old level %d\n", + __FILE__, __LINE__, + kr_mon, spl_vm_pressure_level); + } + + if (spl_vm_pressure_level > 0 && + spl_vm_pressure_level != MAGIC_PRESSURE_UNAVAILABLE) { + /* there is pressure */ + lowmem = true; + new_spl_free = -(2LL * PAGE_SIZE * spl_vm_pages_wanted); + if (spl_vm_pressure_level > 1) { + emergency_lowmem = true; + if (new_spl_free > 0) + new_spl_free = -(4LL * + PAGE_SIZE * + spl_vm_pages_wanted); + spl_free_fast_pressure = TRUE; + } + spl_free_manual_pressure += PAGE_SIZE * + spl_vm_pages_wanted; + } else if (spl_vm_pages_wanted > 0) { + /* + * kVMPressureNormal but pages wanted : react more + * strongly if there was some transient pressure that + * was weakly absorbed by the virtual memory system + */ + /* XXX : additional hysteresis maintained below */ + int64_t m = 2LL; + if (spl_vm_pages_wanted * 8 > + spl_vm_pages_reclaimed) + m = 8LL; + + new_spl_free -= m * + PAGE_SIZE * spl_vm_pages_wanted; + } else { + /* + * No pressure. Xnu has freed up some memory + * which we can use. + */ + if (spl_vm_pages_reclaimed > 0) + new_spl_free += (PAGE_SIZE * + spl_vm_pages_reclaimed) >> 1LL; + else { + /* grow a little every pressure-free pass */ + new_spl_free += 1024LL*1024LL; + } + /* + * Cap, bearing in mind that we deflate + * total_memory by 50% at initialization + */ + if (new_spl_free > total_memory) + new_spl_free = total_memory; + } + + /* + * if there is pressure that has not yet reached + * arc_reclaim_thread() then start with a negative + * new_spl_free + */ + if (spl_free_manual_pressure > 0) { + int64_t old_pressure = spl_free_manual_pressure; + new_spl_free -= old_pressure * 2LL; + lowmem = true; + if (spl_free_fast_pressure) { + emergency_lowmem = true; + new_spl_free -= old_pressure * 4LL; + } + } + + /* + * can we allocate at least a 64 MiB segment + * from spl_heap_arena? this probes the reserve + * and also the largest imported spans, which + * vmem_alloc can fragment if needed. + */ + boolean_t reserve_low = false; + extern vmem_t *spl_heap_arena; + const uint64_t sixtyfour = 64ULL*1024ULL*1024ULL; + const uint64_t rvallones = (sixtyfour << 1ULL) - 1ULL; + const uint64_t rvmask = ~rvallones; + uint64_t rvfreebits = spl_heap_arena->vm_freemap; + + if ((rvfreebits & rvmask) == 0) { + reserve_low = true; + } else { + new_spl_free += (int64_t)sixtyfour; + } + + // do we have lots of memory in the spl_heap_arena ? + + boolean_t early_lots_free = false; + const uint64_t onetwentyeight = 128ULL*1024ULL*1024ULL; + const uint64_t sixteen = 16ULL*1024ULL*1024ULL; + if (!reserve_low) { + early_lots_free = true; + } else if (vmem_size_semi_atomic(spl_heap_arena, + VMEM_FREE) > onetwentyeight) { + early_lots_free = true; + new_spl_free += (int64_t)sixteen; + } + + // do we have lots of memory in the bucket_arenas ? + + extern int64_t vmem_buckets_size(int); // non-locking + int64_t buckets_free = vmem_buckets_size(VMEM_FREE); + if ((uint64_t)buckets_free != spl_buckets_mem_free) + spl_buckets_mem_free = (uint64_t)buckets_free; + + if (buckets_free >= 512LL*1024LL*1024LL) { + early_lots_free = true; + new_spl_free += (int64_t)sixteen; + } + if (buckets_free >= 1024LL*1024LL*1024LL) { + reserve_low = false; + new_spl_free += (int64_t)sixteen; + } + + /* + * if we have neither alloced or freed in + * several minutes, then we do not need to + * shrink back if there is a momentary transient + * memory spike (i.e., one that lasts less than a second) + */ + boolean_t memory_equilibrium = false; + const uint64_t five_minutes = 300ULL; + const uint64_t one_minute = 60ULL; + uint64_t last_xat_alloc_seconds = spl_xat_lastalloc; + uint64_t last_xat_free_seconds = spl_xat_lastfree; + + if (last_xat_alloc_seconds + five_minutes > time_now_seconds && + last_xat_free_seconds + five_minutes > time_now_seconds) { + if (last_disequilibrium + one_minute > + time_now_seconds) { + memory_equilibrium = true; + last_disequilibrium = 0; + } + } else { + last_disequilibrium = time_now_seconds; + } + + boolean_t just_alloced = false; + if (last_xat_alloc_seconds + 1 > time_now_seconds) + just_alloced = true; + + /* + * this is a sign of a period of time of low system + * memory, however XNU's generation of this variable + * is not very predictable, but generally it should be + * taken seriously when it's positive (it is often falsely 0) + */ + if ((spl_vm_pages_wanted > 0 && reserve_low && + !early_lots_free && !memory_equilibrium && + !just_alloced) || spl_vm_pages_wanted >= 1024) { + int64_t bminus = (int64_t)spl_vm_pages_wanted * + (int64_t)PAGESIZE * -16LL; + if (bminus > -16LL*1024LL*1024LL) + bminus = -16LL*1024LL*1024LL; + new_spl_free += bminus; + lowmem = true; + emergency_lowmem = true; + // atomic swaps to set these variables used in arc.c + int64_t previous_highest_pressure = 0; + int64_t new_p = -bminus; + previous_highest_pressure = spl_free_manual_pressure; + if (new_p > previous_highest_pressure || new_p <= 0) { + boolean_t fast = FALSE; + if (spl_vm_pages_wanted > + spl_vm_page_free_min / 8) + fast = TRUE; + spl_free_set_pressure_both(-16LL * new_spl_free, + fast); + } + last_disequilibrium = time_now_seconds; + } else if (spl_vm_pages_wanted > 0) { + int64_t bytes_wanted = + (int64_t)spl_vm_pages_wanted * + (int64_t)PAGESIZE; + new_spl_free -= bytes_wanted; + if (reserve_low && !early_lots_free) { + lowmem = true; + if (recent_lowmem == 0) { + recent_lowmem = time_now; + } + if (!memory_equilibrium) { + last_disequilibrium = time_now_seconds; + } + } + } + + /* + * If we have already detected a memory shortage + * and we have not reaped in a while (a short while + * for emergency_lowmem), then do a kmem_reap() now. + * See http://comments.gmane.org/gmane.os.illumos.devel/22552 + * (notably Richard Elling's "A kernel module can call + * kmem_reap() whenever it wishes and some modules, + * like zfs, do so." If we reap, stop processing spl_free + * on this pass, to let the reaps (and arc, if pressure + * has been set above) do their job for a few milliseconds. + */ + if (emergency_lowmem || lowmem) { + static uint64_t last_reap = 0; + uint64_t now = time_now; + uint64_t elapsed = 60*hz; + if (emergency_lowmem) + elapsed = 15*hz; // min.freq. kmem_reap_interval + if (now - last_reap > elapsed) { + last_reap = now; + /* + * spl_free_reap_caches() calls functions + * that will acquire locks and can take a while + * so set spl_free to a small positive value + * to stop arc shrinking too much during this + * period when we expect to be freeing up + * arc-usable memory, but low enough that + * arc_no_grow likely will be set. + */ + const int64_t two_spamax = 32LL * 1024LL * + 1024LL; + if (spl_free < two_spamax) + spl_free = two_spamax; // atomic! + spl_free_reap_caches(); + // we do not have any lock now, so we can jump + // to just before the thread-suspending code + goto justwait; + } + } + + /* + * a number or exceptions to reverse the lowmem + * / emergency_lowmem states if we have recently reaped. + * we also take the strong reaction sting out of + * the set pressure by turning off spl_free_fast_pressure, + * since that automatically provokes an arc shrink + * and arc reap. + */ + + if (!reserve_low || early_lots_free || memory_equilibrium || + just_alloced) { + lowmem = false; + emergency_lowmem = false; + spl_free_fast_pressure = FALSE; + } + + /* + * Stay in a low memory condition for several seconds + * after we first detect that we are in it, giving the + * system (arc, xnu and userland) time to adapt + */ + if (!lowmem && recent_lowmem > 0) { + if (recent_lowmem + 4*hz < time_now) + lowmem = true; + else + recent_lowmem = 0; + } + + base = new_spl_free; + + // adjust for available memory in spl_heap_arena + // cf arc_available_memory() + if (!emergency_lowmem) { + extern vmem_t *spl_default_arena; + int64_t heap_free = (int64_t)vmem_size_semi_atomic( + spl_heap_arena, VMEM_FREE); + // grabbed buckets_free up above; we are OK with + // change to it in the meanwhile, + // it'll get an update on the next run. + int64_t combined_free = heap_free + buckets_free; + + if (combined_free != 0) { + const int64_t mb = 1024*1024; + if (!lowmem) { + new_spl_free += combined_free / + 4; + } else { + new_spl_free -= 16LL * mb; + } + } + + // memory footprint has gotten really big, + // decrease spl_free substantially + int64_t total_mem_used = (int64_t) + segkmem_total_mem_allocated; + if ((segkmem_total_mem_allocated * 100LL / + real_total_memory) > 70) { + new_spl_free -= total_mem_used / 64; + } else if ((segkmem_total_mem_allocated * 100LL / + real_total_memory) > 75) { + new_spl_free -= total_mem_used / 32; + lowmem = true; + } + } + + // try to get 1/64 of spl_heap_arena freed up + if (emergency_lowmem && new_spl_free >= 0LL) { + extern vmem_t *spl_root_arena; + uint64_t root_size = vmem_size_semi_atomic( + spl_heap_arena, VMEM_ALLOC | VMEM_FREE); + uint64_t root_free = vmem_size_semi_atomic( + spl_heap_arena, VMEM_FREE); + int64_t difference = root_size - root_free; + int64_t target = root_size / 64; + if (difference < target) { + new_spl_free -= target; + } + // and we should definitely not be returning + // positive now + if (new_spl_free >= 0LL) + new_spl_free = -1024LL; + } + + double delta = (double)new_spl_free - (double)last_spl_free; + + boolean_t spl_free_is_negative = false; + + if (new_spl_free < 0LL) { + spl_stats.spl_spl_free_negative_count.value.ui64++; + spl_free_is_negative = true; + } + + /* + * leave a little headroom if we have hit our + * allocation maximum + */ + const int64_t spamaxblksz = 16LL * 1024LL; + if ((4LL * spamaxblksz) > + (total_memory - segkmem_total_mem_allocated)) { + if (new_spl_free > 2LL * spamaxblksz) + new_spl_free = 2LL * spamaxblksz; + } + + // NOW set spl_free from calculated new_spl_free + spl_free = new_spl_free; + // the direct equivalent of : + // __c11_atomic_store(&spl_free, new_spl_free, + // __ATOMIC_SEQ_CST); + + /* + * Because we're already negative, arc is likely to have + * been signalled already. We can rely on the _maybe_ in + * spl-vmem.c:xnu_alloc_throttled() [XAT] to try to give + * arc a kick with greater probability. However, if we've + * gone negative several times, and have not tried a full + * kick in a long time, do so now; if the full kick is + * refused because there has been a kick too few minutes + * ago, try a gentler kick. We do this outside the lock, + * as spl_maybe_send_large_pressure may need to take a + * mutex, and we forbid further mutex entry when + * spl_free_lock is held. + */ + + if (spl_free_is_negative) { + static volatile _Atomic uint32_t + negatives_since_last_kick = 0; + + if (negatives_since_last_kick++ > 8) { + if (spl_maybe_send_large_pressure(time_now, 360, + true) || + spl_maybe_send_large_pressure(time_now, 60, + false)) { + negatives_since_last_kick = 0; + } + } + } + + if (lowmem) + recent_lowmem = time_now; + + // maintain an exponential moving average for the ema kstat + if (last_update > hz) + alpha = 1.0; + else { + double td_tick = (double)(time_now - last_update); + alpha = td_tick / (double)(hz*50.0); // roughly 0.02 + } + + ema_new = (alpha * delta) + (1.0 - alpha)*ema_old; + spl_free_delta_ema = ema_new; + ema_old = ema_new; + + justwait: + mutex_enter(&spl_free_thread_lock); + CALLB_CPR_SAFE_BEGIN(&cpr); + (void) cv_timedwait_hires(&spl_free_thread_cv, + &spl_free_thread_lock, MSEC2NSEC(10), 0, 0); + CALLB_CPR_SAFE_END(&cpr, &spl_free_thread_lock); + } + spl_free_thread_exit = FALSE; + dprintf("SPL: spl_free_thread_exit set to FALSE " \ + "and exiting: cv_broadcasting\n"); + spl_free_manual_pressure = 0; + cv_broadcast(&spl_free_thread_cv); + CALLB_CPR_EXIT(&cpr); + dprintf("SPL: %s thread_exit\n", __func__); + thread_exit(); +} + + +static int +spl_kstat_update(kstat_t *ksp, int rw) +{ + spl_stats_t *ks = ksp->ks_data; + + if (rw == KSTAT_WRITE) { + + if (ks->spl_spl_free_manual_pressure.value.i64 != + spl_free_manual_pressure) { + spl_free_set_pressure( + ks->spl_spl_free_manual_pressure.value.i64 * 1024 * + 1024); + if (ks->spl_spl_free_manual_pressure.value.i64 > 0) { + spl_free_reap_caches(); + } + } + + if (ks->spl_spl_free_fast_pressure.value.i64 != + spl_free_fast_pressure) { + if (spl_free_wrapper() != 0) { + spl_free_set_fast_pressure(TRUE); + } + } + + if (ks->spl_bucket_tunable_large_span.value.ui64 != + spl_bucket_tunable_large_span) { + spl_set_bucket_tunable_large_span( + ks->spl_bucket_tunable_large_span.value.ui64); + } + + if (ks->spl_bucket_tunable_small_span.value.ui64 != + spl_bucket_tunable_small_span) { + spl_set_bucket_tunable_small_span( + ks->spl_bucket_tunable_small_span.value.ui64); + } + + if (ks->spl_frag_max_walk.value.ui64 != spl_frag_max_walk) { + spl_frag_max_walk = ks->spl_frag_max_walk.value.ui64; + } + + if (ks->kmem_free_to_slab_when_fragmented.value.ui64 != + kmem_free_to_slab_when_fragmented) { + kmem_free_to_slab_when_fragmented = + ks->kmem_free_to_slab_when_fragmented.value.ui64; + } + + } else { + ks->spl_os_alloc.value.ui64 = segkmem_total_mem_allocated; + ks->spl_active_threads.value.ui64 = zfs_threads; + ks->spl_active_mutex.value.ui64 = zfs_active_mutex; + ks->spl_active_rwlock.value.ui64 = zfs_active_rwlock; + ks->spl_active_tsd.value.ui64 = spl_tsd_size(); + ks->spl_spl_free.value.i64 = spl_free; + ks->spl_spl_free_manual_pressure.value.i64 = + spl_free_manual_pressure; + ks->spl_spl_free_fast_pressure.value.i64 = + spl_free_fast_pressure; + ks->spl_spl_free_delta_ema.value.i64 = spl_free_delta_ema; + ks->spl_osif_malloc_success.value.ui64 = + stat_osif_malloc_success; + ks->spl_osif_malloc_bytes.value.ui64 = stat_osif_malloc_bytes; + ks->spl_osif_free.value.ui64 = stat_osif_free; + ks->spl_osif_free_bytes.value.ui64 = stat_osif_free_bytes; + ks->spl_bucket_non_pow2_allocs.value.ui64 = + spl_bucket_non_pow2_allocs; + + ks->spl_vmem_unconditional_allocs.value.ui64 = + spl_vmem_unconditional_allocs; + ks->spl_vmem_unconditional_alloc_bytes.value.ui64 = + spl_vmem_unconditional_alloc_bytes; + ks->spl_vmem_conditional_allocs.value.ui64 = + spl_vmem_conditional_allocs; + ks->spl_vmem_conditional_alloc_bytes.value.ui64 = + spl_vmem_conditional_alloc_bytes; + ks->spl_vmem_conditional_alloc_deny.value.ui64 = + spl_vmem_conditional_alloc_deny; + ks->spl_vmem_conditional_alloc_deny_bytes.value.ui64 = + spl_vmem_conditional_alloc_deny_bytes; + + ks->spl_xat_success.value.ui64 = spl_xat_success; + ks->spl_xat_late_success.value.ui64 = spl_xat_late_success; + ks->spl_xat_late_success_nosleep.value.ui64 = + spl_xat_late_success_nosleep; + ks->spl_xat_pressured.value.ui64 = spl_xat_pressured; + ks->spl_xat_bailed.value.ui64 = spl_xat_bailed; + ks->spl_xat_bailed_contended.value.ui64 = + spl_xat_bailed_contended; + ks->spl_xat_lastalloc.value.ui64 = spl_xat_lastalloc; + ks->spl_xat_lastfree.value.ui64 = spl_xat_lastfree; + ks->spl_xat_forced.value.ui64 = spl_xat_forced; + ks->spl_xat_sleep.value.ui64 = spl_xat_sleep; + ks->spl_xat_late_deny.value.ui64 = spl_xat_late_deny; + ks->spl_xat_no_waiters.value.ui64 = spl_xat_no_waiters; + ks->spl_xft_wait.value.ui64 = spl_xft_wait; + + ks->spl_vba_parent_memory_appeared.value.ui64 = + spl_vba_parent_memory_appeared; + ks->spl_vba_parent_memory_blocked.value.ui64 = + spl_vba_parent_memory_blocked; + ks->spl_vba_hiprio_blocked.value.ui64 = spl_vba_hiprio_blocked; + ks->spl_vba_cv_timeout.value.ui64 = spl_vba_cv_timeout; + ks->spl_vba_loop_timeout.value.ui64 = spl_vba_loop_timeout; + ks->spl_vba_cv_timeout_blocked.value.ui64 = + spl_vba_cv_timeout_blocked; + ks->spl_vba_loop_timeout_blocked.value.ui64 = + spl_vba_loop_timeout_blocked; + ks->spl_vba_sleep.value.ui64 = spl_vba_sleep; + ks->spl_vba_loop_entries.value.ui64 = spl_vba_loop_entries; + + ks->spl_bucket_tunable_large_span.value.ui64 = + spl_bucket_tunable_large_span; + ks->spl_bucket_tunable_small_span.value.ui64 = + spl_bucket_tunable_small_span; + + ks->spl_buckets_mem_free.value.ui64 = spl_buckets_mem_free; + ks->spl_arc_no_grow_bits.value.ui64 = spl_arc_no_grow_bits; + ks->spl_arc_no_grow_count.value.ui64 = spl_arc_no_grow_count; + + ks->spl_frag_max_walk.value.ui64 = spl_frag_max_walk; + ks->spl_frag_walked_out.value.ui64 = spl_frag_walked_out; + ks->spl_frag_walk_cnt.value.ui64 = spl_frag_walk_cnt; + + ks->spl_arc_reclaim_avoided.value.ui64 = + spl_arc_reclaim_avoided; + + ks->kmem_free_to_slab_when_fragmented.value.ui64 = + kmem_free_to_slab_when_fragmented; + + ks->spl_vm_pages_reclaimed.value.ui64 = + spl_vm_pages_reclaimed; + ks->spl_vm_pages_wanted.value.ui64 = + spl_vm_pages_wanted; + ks->spl_vm_pressure_level.value.ui64 = + spl_vm_pressure_level; + + } + + return (0); +} + +void +spl_kmem_init(uint64_t xtotal_memory) +{ + int old_kmem_flags = kmem_flags; + int use_large_pages = 0; + size_t maxverify, minfirewall; + + dprintf("SPL: KMEM starting. Total memory %llu\n", xtotal_memory); + + // Initialise the kstat lock + mutex_init(&kmem_cache_lock, "kmem_cache_lock", MUTEX_DEFAULT, NULL); + mutex_init(&kmem_flags_lock, "kmem_flags_lock", MUTEX_DEFAULT, NULL); + mutex_init(&kmem_cache_kstat_lock, "kmem_kstat_lock", MUTEX_DEFAULT, + NULL); + + spl_kstat_init(); + + + /* + * Small-memory systems (< 24 MB) can't handle kmem_flags overhead. + */ + if (physmem < btop(24 << 20) && !(old_kmem_flags & KMF_STICKY)) + kmem_flags = 0; + + /* + * Don't do firewalled allocations if the heap is less than 1TB + * (i.e. on a 32-bit kernel) + * The resulting VM_NEXTFIT allocations would create too much + * fragmentation in a small heap. + */ + maxverify = minfirewall = PAGESIZE / 2; + + + /* LINTED */ + ASSERT(sizeof (kmem_cpu_cache_t) == KMEM_CPU_CACHE_SIZE); + + list_create(&kmem_caches, sizeof (kmem_cache_t), + offsetof(kmem_cache_t, cache_link)); + + kernelheap_init(); + + kmem_metadata_arena = vmem_create("kmem_metadata", NULL, 0, PAGESIZE, + vmem_alloc, vmem_free, heap_arena, 8 * PAGESIZE, + VM_SLEEP | VMC_NO_QCACHE); + + kmem_msb_arena = vmem_create("kmem_msb", NULL, 0, + PAGESIZE, vmem_alloc, vmem_free, kmem_metadata_arena, 0, + VMC_DUMPSAFE | VM_SLEEP); + + kmem_cache_arena = vmem_create("kmem_cache", NULL, 0, KMEM_ALIGN, + vmem_alloc, vmem_free, kmem_metadata_arena, 0, VM_SLEEP); + + kmem_hash_arena = vmem_create("kmem_hash", NULL, 0, KMEM_ALIGN, + vmem_alloc, vmem_free, kmem_metadata_arena, 0, VM_SLEEP); + + kmem_log_arena = vmem_create("kmem_log", NULL, 0, KMEM_ALIGN, + vmem_alloc, vmem_free, kmem_metadata_arena, 0, VM_SLEEP); + + /* temporary oversize arena for mod_read_system_file */ + kmem_oversize_arena = vmem_create("kmem_oversize", NULL, 0, PAGESIZE, + vmem_alloc, vmem_free, heap_arena, 0, VM_SLEEP); + + // statically declared above kmem_reap_interval = 15 * hz; + + /* + * Read /etc/system. This is a chicken-and-egg problem because + * kmem_flags may be set in /etc/system, but mod_read_system_file() + * needs to use the allocator. The simplest solution is to create + * all the standard kmem caches, read /etc/system, destroy all the + * caches we just created, and then create them all again in light + * of the (possibly) new kmem_flags and other kmem tunables. + */ + + if (old_kmem_flags & KMF_STICKY) + kmem_flags = old_kmem_flags; + + if (!(kmem_flags & KMF_AUDIT)) + vmem_seg_size = offsetof(vmem_seg_t, vs_thread); + + if (kmem_maxverify == 0) + kmem_maxverify = maxverify; + + if (kmem_minfirewall == 0) + kmem_minfirewall = minfirewall; + + /* + * give segkmem a chance to figure out if we are using large pages + * for the kernel heap + */ + // use_large_pages = segkmem_lpsetup(); + use_large_pages = 0; + + /* + * To protect against corruption, we keep the actual number of callers + * KMF_LITE records seperate from the tunable. We arbitrarily clamp + * to 16, since the overhead for small buffers quickly gets out of + * hand. + * + * The real limit would depend on the needs of the largest KMC_NOHASH + * cache. + */ + kmem_lite_count = MIN(MAX(0, kmem_lite_pcs), 16); + kmem_lite_pcs = kmem_lite_count; + + kmem_cache_init(2, use_large_pages); + + if (kmem_flags & (KMF_AUDIT | KMF_RANDOMIZE)) { + if (kmem_transaction_log_size == 0) + kmem_transaction_log_size = MIN(kmem_maxavail() / 50ULL, + PAGESIZE<<4); + kmem_transaction_log = kmem_log_init(kmem_transaction_log_size); + } + + if (kmem_flags & (KMF_CONTENTS | KMF_RANDOMIZE)) { + if (kmem_content_log_size == 0) + kmem_content_log_size = MIN(kmem_maxavail() / 50ULL, + PAGESIZE<<4); + kmem_content_log = kmem_log_init(kmem_content_log_size); + } + + kmem_failure_log = kmem_log_init(kmem_failure_log_size); + + kmem_slab_log = kmem_log_init(kmem_slab_log_size); + + spl_tsd_init(); + spl_rwlock_init(); + spl_taskq_init(); + + /* + * Warn about invalid or dangerous values of kmem_flags. + * Always warn about unsupported values. + */ + if (((kmem_flags & ~(KMF_AUDIT | KMF_DEADBEEF | KMF_REDZONE | + KMF_CONTENTS | KMF_LITE)) != 0) || + ((kmem_flags & KMF_LITE) && kmem_flags != KMF_LITE)) + cmn_err(CE_WARN, "kmem_flags set to unsupported value 0x%x. " + "See the Solaris Tunable Parameters Reference Manual.", + kmem_flags); + +#ifdef DEBUG + if ((kmem_flags & KMF_DEBUG) == 0) + cmn_err(CE_NOTE, "kmem debugging disabled."); +#else + /* + * For non-debug kernels, the only "normal" flags are 0, KMF_LITE, + * KMF_REDZONE, and KMF_CONTENTS (the last because it is only enabled + * if KMF_AUDIT is set). We should warn the user about the performance + * penalty of KMF_AUDIT or KMF_DEADBEEF if they are set and KMF_LITE + * isn't set (since that disables AUDIT). + */ + if (!(kmem_flags & KMF_LITE) && + (kmem_flags & (KMF_AUDIT | KMF_DEADBEEF)) != 0) + cmn_err(CE_WARN, "High-overhead kmem debugging features " + "enabled (kmem_flags = 0x%x). Performance degradation " + "and large memory overhead possible. See the Solaris " + "Tunable Parameters Reference Manual.", kmem_flags); +#endif /* not DEBUG */ + + segkmem_abd_init(); + + kmem_cache_applyall(kmem_cache_magazine_enable, NULL, TQ_SLEEP); + + kmem_ready = 1; + + // Install spl kstats + spl_ksp = kstat_create("spl", 0, "spl_misc", "misc", KSTAT_TYPE_NAMED, + sizeof (spl_stats) / sizeof (kstat_named_t), + KSTAT_FLAG_VIRTUAL|KSTAT_FLAG_WRITABLE); + + if (spl_ksp != NULL) { + spl_ksp->ks_data = &spl_stats; + spl_ksp->ks_update = spl_kstat_update; + kstat_install(spl_ksp); + } +} + +void +spl_kmem_fini(void) +{ + + kmem_cache_applyall(kmem_cache_magazine_disable, NULL, TQ_SLEEP); + + kstat_delete(spl_ksp); + + kmem_log_fini(kmem_slab_log); + kmem_log_fini(kmem_failure_log); + + if (kmem_flags & (KMF_CONTENTS | KMF_RANDOMIZE)) { + if (kmem_content_log_size == 0) + kmem_content_log_size = kmem_maxavail() / 50; + kmem_log_fini(kmem_content_log); + } + + if (kmem_flags & (KMF_AUDIT | KMF_RANDOMIZE)) { + if (kmem_transaction_log_size == 0) + kmem_transaction_log_size = kmem_maxavail() / 50; + kmem_log_fini(kmem_transaction_log); + } + + // Destroy all the "general allocation" caches + kmem_alloc_caches_destroy(); + + // Destroy the VA associated caches + kmem_destroy_cache_by_name(KMEM_VA_PREFIX); + + kmem_qcache_destroy(); + // Destroy metadata caches + kmem_cache_destroy(kmem_bufctl_cache); + kmem_cache_destroy(kmem_bufctl_audit_cache); + kmem_cache_destroy(kmem_slab_cache); // Dont think this one + + // Some caches cannot be destroyed as + // they mutually reference each other. + // So we explicitly pull them apart piece-by-piece. + kmem_cache_fini(); + + segkmem_abd_fini(); + + // Now destroy the vmem arenas used by kmem. + vmem_destroy(kmem_default_arena); + vmem_destroy(kmem_va_arena); + vmem_destroy(kmem_oversize_arena); + vmem_destroy(kmem_log_arena); + vmem_destroy(kmem_hash_arena); + vmem_destroy(kmem_cache_arena); + vmem_destroy(kmem_msb_arena); + vmem_destroy(kmem_metadata_arena); + + kernelheap_fini(); + + list_destroy(&kmem_caches); + + mutex_destroy(&kmem_cache_kstat_lock); + mutex_destroy(&kmem_flags_lock); + mutex_destroy(&kmem_cache_lock); +} + +static void +kmem_move_init(void) +{ + kmem_defrag_cache = kmem_cache_create("kmem_defrag_cache", + sizeof (kmem_defrag_t), 0, NULL, NULL, NULL, NULL, + kmem_msb_arena, KMC_NOHASH); + kmem_move_cache = kmem_cache_create("kmem_move_cache", + sizeof (kmem_move_t), 0, NULL, NULL, NULL, NULL, + kmem_msb_arena, KMC_NOHASH); + + /* + * kmem guarantees that move callbacks are sequential and that even + * across multiple caches no two moves ever execute simultaneously. + * Move callbacks are processed on a separate taskq so that client code + * does not interfere with internal maintenance tasks. + */ + kmem_move_taskq = taskq_create("kmem_move_taskq", 1, + minclsyspri, 100, INT_MAX, TASKQ_PREPOPULATE); +} + +void +kmem_move_fini(void) +{ + + taskq_wait(kmem_move_taskq); + taskq_destroy(kmem_move_taskq); + kmem_move_taskq = 0; + + kmem_cache_destroy(kmem_move_cache); + kmem_cache_destroy(kmem_defrag_cache); + +} + +void +spl_kmem_thread_init(void) +{ + kmem_move_init(); + + // Initialize the spl_free locks + mutex_init(&spl_free_thread_lock, "spl_free_thead_lock", MUTEX_DEFAULT, + NULL); + + kmem_taskq = taskq_create("kmem_taskq", 1, minclsyspri, + 600, INT_MAX, TASKQ_PREPOPULATE); + + spl_free_thread_exit = FALSE; + (void) cv_init(&spl_free_thread_cv, NULL, CV_DEFAULT, NULL); + (void) thread_create(NULL, 0, spl_free_thread, 0, 0, 0, 0, 92); +} + +void +spl_kmem_thread_fini(void) +{ + shutting_down = 1; + + mutex_enter(&spl_free_thread_lock); + spl_free_thread_exit = TRUE; + while (spl_free_thread_exit) { + cv_signal(&spl_free_thread_cv); + cv_wait(&spl_free_thread_cv, &spl_free_thread_lock); + } + mutex_exit(&spl_free_thread_lock); + cv_destroy(&spl_free_thread_cv); + mutex_destroy(&spl_free_thread_lock); + + bsd_untimeout(kmem_update, 0); + bsd_untimeout(kmem_reap_timeout, &kmem_reaping); + bsd_untimeout(kmem_reap_timeout, &kmem_reaping_idspace); + + taskq_wait(kmem_taskq); + + taskq_destroy(kmem_taskq); + kmem_taskq = 0; + + kmem_move_fini(); + +} + +void +spl_kmem_mp_init(void) +{ + kmem_update_timeout(NULL); +} + +/* + * Return the slab of the allocated buffer, or NULL if the buffer is not + * allocated. This function may be called with a known slab address to determine + * whether or not the buffer is allocated, or with a NULL slab address to obtain + * an allocated buffer's slab. + */ +static kmem_slab_t * +kmem_slab_allocated(kmem_cache_t *cp, kmem_slab_t *sp, void *buf) +{ + kmem_bufctl_t *bcp, *bufbcp; + + ASSERT(MUTEX_HELD(&cp->cache_lock)); + ASSERT(sp == NULL || KMEM_SLAB_MEMBER(sp, buf)); + + if (cp->cache_flags & KMF_HASH) { + for (bcp = *KMEM_HASH(cp, buf); + (bcp != NULL) && (bcp->bc_addr != buf); + bcp = bcp->bc_next) { + continue; + } + ASSERT(sp != NULL && bcp != NULL ? sp == bcp->bc_slab : 1); + return (bcp == NULL ? NULL : bcp->bc_slab); + } + + if (sp == NULL) { + sp = KMEM_SLAB(cp, buf); + } + bufbcp = KMEM_BUFCTL(cp, buf); + for (bcp = sp->slab_head; + (bcp != NULL) && (bcp != bufbcp); + bcp = bcp->bc_next) { + continue; + } + return (bcp == NULL ? sp : NULL); +} + +static boolean_t +kmem_slab_is_reclaimable(kmem_cache_t *cp, kmem_slab_t *sp, int flags) +{ + long refcnt = sp->slab_refcnt; + + ASSERT(cp->cache_defrag != NULL); + + /* + * For code coverage we want to be able to move an object within the + * same slab (the only partial slab) even if allocating the destination + * buffer resulted in a completely allocated slab. + */ + if (flags & KMM_DEBUG) { + return ((flags & KMM_DESPERATE) || + ((sp->slab_flags & KMEM_SLAB_NOMOVE) == 0)); + } + + /* If we're desperate, we don't care if the client said NO. */ + if (flags & KMM_DESPERATE) { + return (refcnt < sp->slab_chunks); /* any partial */ + } + + if (sp->slab_flags & KMEM_SLAB_NOMOVE) { + return (B_FALSE); + } + + if ((refcnt == 1) || kmem_move_any_partial) { + return (refcnt < sp->slab_chunks); + } + + /* + * The reclaim threshold is adjusted at each kmem_cache_scan() so that + * slabs with a progressively higher percentage of used buffers can be + * reclaimed until the cache as a whole is no longer fragmented. + * + * sp->slab_refcnt kmd_reclaim_numer + * --------------- < ------------------ + * sp->slab_chunks KMEM_VOID_FRACTION + */ + return ((refcnt * KMEM_VOID_FRACTION) < + (sp->slab_chunks * cp->cache_defrag->kmd_reclaim_numer)); +} + +/* + * May be called from the kmem_move_taskq, from kmem_cache_move_notify_task(), + * or when the buffer is freed. + */ +static void +kmem_slab_move_yes(kmem_cache_t *cp, kmem_slab_t *sp, void *from_buf) +{ + ASSERT(MUTEX_HELD(&cp->cache_lock)); + ASSERT(KMEM_SLAB_MEMBER(sp, from_buf)); + + if (!KMEM_SLAB_IS_PARTIAL(sp)) { + return; + } + + if (sp->slab_flags & KMEM_SLAB_NOMOVE) { + if (KMEM_SLAB_OFFSET(sp, from_buf) == sp->slab_stuck_offset) { + avl_remove(&cp->cache_partial_slabs, sp); + sp->slab_flags &= ~KMEM_SLAB_NOMOVE; + sp->slab_stuck_offset = (uint32_t)-1; + avl_add(&cp->cache_partial_slabs, sp); + } + } else { + sp->slab_later_count = 0; + sp->slab_stuck_offset = (uint32_t)-1; + } +} + +static void +kmem_slab_move_no(kmem_cache_t *cp, kmem_slab_t *sp, void *from_buf) +{ + ASSERT(taskq_member(kmem_move_taskq, curthread)); + ASSERT(MUTEX_HELD(&cp->cache_lock)); + ASSERT(KMEM_SLAB_MEMBER(sp, from_buf)); + + if (!KMEM_SLAB_IS_PARTIAL(sp)) { + return; + } + + avl_remove(&cp->cache_partial_slabs, sp); + sp->slab_later_count = 0; + sp->slab_flags |= KMEM_SLAB_NOMOVE; + sp->slab_stuck_offset = KMEM_SLAB_OFFSET(sp, from_buf); + avl_add(&cp->cache_partial_slabs, sp); +} + +static void kmem_move_end(kmem_cache_t *, kmem_move_t *); + +/* + * The move callback takes two buffer addresses, the buffer to be moved, and a + * newly allocated and constructed buffer selected by kmem as the destination. + * It also takes the size of the buffer and an optional user argument specified + * at cache creation time. kmem guarantees that the buffer to be moved has not + * been unmapped by the virtual memory subsystem. Beyond that, it cannot + * guarantee the present whereabouts of the buffer to be moved, so it is up to + * the client to safely determine whether or not it is still using the buffer. + * The client must not free either of the buffers passed to the move callback, + * since kmem wants to free them directly to the slab layer. The client response + * tells kmem which of the two buffers to free: + * + * YES kmem frees the old buffer (the move was successful) + * NO kmem frees the new buffer, marks the slab of the old buffer + * non-reclaimable to avoid bothering the client again + * LATER kmem frees the new buffer, increments slab_later_count + * DONT_KNOW kmem frees the new buffer + * DONT_NEED kmem frees both the old buffer and the new buffer + * + * The pending callback argument now being processed contains both of the + * buffers (old and new) passed to the move callback function, the slab of the + * old buffer, and flags related to the move request, such as whether or not the + * system was desperate for memory. + * + * Slabs are not freed while there is a pending callback, but instead are kept + * on a deadlist, which is drained after the last callback completes. This means + * that slabs are safe to access until kmem_move_end(), no matter how many of + * their buffers have been freed. Once slab_refcnt reaches zero, it stays at + * zero for as long as the slab remains on the deadlist and until the slab is + * freed. + */ +static void +kmem_move_buffer(kmem_move_t *callback) +{ + kmem_cbrc_t response; + kmem_slab_t *sp = callback->kmm_from_slab; + kmem_cache_t *cp = sp->slab_cache; + boolean_t free_on_slab; + + ASSERT(taskq_member(kmem_move_taskq, curthread)); + ASSERT(MUTEX_NOT_HELD(&cp->cache_lock)); + ASSERT(KMEM_SLAB_MEMBER(sp, callback->kmm_from_buf)); + + /* + * The number of allocated buffers on the slab may have changed since we + * last checked the slab's reclaimability (when the pending move was + * enqueued), or the client may have responded NO when asked to move + * another buffer on the same slab. + */ + if (!kmem_slab_is_reclaimable(cp, sp, callback->kmm_flags)) { + kmem_slab_free(cp, callback->kmm_to_buf); + kmem_move_end(cp, callback); + return; + } + + /* + * Checking the slab layer is easy, so we might as well do that here + * in case we can avoid bothering the client. + */ + mutex_enter(&cp->cache_lock); + free_on_slab = (kmem_slab_allocated(cp, sp, + callback->kmm_from_buf) == NULL); + mutex_exit(&cp->cache_lock); + + if (free_on_slab) { + kmem_slab_free(cp, callback->kmm_to_buf); + kmem_move_end(cp, callback); + return; + } + + if (cp->cache_flags & KMF_BUFTAG) { + /* + * Make kmem_cache_alloc_debug() apply the constructor for us. + */ + if (kmem_cache_alloc_debug(cp, callback->kmm_to_buf, + KM_NOSLEEP, 1, caller()) != 0) { + kmem_move_end(cp, callback); + return; + } + } else if (cp->cache_constructor != NULL && + cp->cache_constructor(callback->kmm_to_buf, cp->cache_private, + KM_NOSLEEP) != 0) { + atomic_inc_64(&cp->cache_alloc_fail); + kmem_slab_free(cp, callback->kmm_to_buf); + kmem_move_end(cp, callback); + return; + } + + cp->cache_defrag->kmd_callbacks++; + cp->cache_defrag->kmd_thread = spl_current_thread(); + cp->cache_defrag->kmd_from_buf = callback->kmm_from_buf; + cp->cache_defrag->kmd_to_buf = callback->kmm_to_buf; + DTRACE_PROBE2(kmem__move__start, kmem_cache_t *, cp, kmem_move_t *, + callback); + + response = cp->cache_move(callback->kmm_from_buf, + callback->kmm_to_buf, cp->cache_bufsize, cp->cache_private); + + DTRACE_PROBE3(kmem__move__end, kmem_cache_t *, cp, kmem_move_t *, + callback, kmem_cbrc_t, response); + cp->cache_defrag->kmd_thread = NULL; + cp->cache_defrag->kmd_from_buf = NULL; + cp->cache_defrag->kmd_to_buf = NULL; + + if (response == KMEM_CBRC_YES) { + cp->cache_defrag->kmd_yes++; + kmem_slab_free_constructed(cp, callback->kmm_from_buf, B_FALSE); + /* slab safe to access until kmem_move_end() */ + if (sp->slab_refcnt == 0) + cp->cache_defrag->kmd_slabs_freed++; + mutex_enter(&cp->cache_lock); + kmem_slab_move_yes(cp, sp, callback->kmm_from_buf); + mutex_exit(&cp->cache_lock); + kmem_move_end(cp, callback); + return; + } + + switch (response) { + case KMEM_CBRC_NO: + cp->cache_defrag->kmd_no++; + mutex_enter(&cp->cache_lock); + kmem_slab_move_no(cp, sp, callback->kmm_from_buf); + mutex_exit(&cp->cache_lock); + break; + case KMEM_CBRC_LATER: + cp->cache_defrag->kmd_later++; + mutex_enter(&cp->cache_lock); + if (!KMEM_SLAB_IS_PARTIAL(sp)) { + mutex_exit(&cp->cache_lock); + break; + } + + if (++sp->slab_later_count >= KMEM_DISBELIEF) { + kmem_slab_move_no(cp, sp, + callback->kmm_from_buf); + } else if (!(sp->slab_flags & KMEM_SLAB_NOMOVE)) { + sp->slab_stuck_offset = KMEM_SLAB_OFFSET(sp, + callback->kmm_from_buf); + } + mutex_exit(&cp->cache_lock); + break; + case KMEM_CBRC_DONT_NEED: + cp->cache_defrag->kmd_dont_need++; + kmem_slab_free_constructed(cp, callback->kmm_from_buf, + B_FALSE); + if (sp->slab_refcnt == 0) + cp->cache_defrag->kmd_slabs_freed++; + mutex_enter(&cp->cache_lock); + kmem_slab_move_yes(cp, sp, callback->kmm_from_buf); + mutex_exit(&cp->cache_lock); + break; + case KMEM_CBRC_DONT_KNOW: + /* + * If we don't know if we can move this buffer or not, + * we'll just assume that we can't: if the buffer is + * in fact free, then it is sitting in one of the + * per-CPU magazines or in a full magazine in the depot + * layer. Either way, because defrag is induced in the + * same logic that reaps a cache, it's likely that full + * magazines will be returned to the system soon + * (thereby accomplishing what we're trying to + * accomplish here: return those magazines to their + * slabs). Given this, any work that we might do now to + * locate a buffer in a magazine is wasted (and + * expensive!) work; we bump a counter in this case and + * otherwise assume that we can't move it. + */ + cp->cache_defrag->kmd_dont_know++; + break; + default: + panic("'%s' (%p) unexpected move callback " + "response %d\n", cp->cache_name, (void *)cp, + response); + } + + kmem_slab_free_constructed(cp, callback->kmm_to_buf, B_FALSE); + kmem_move_end(cp, callback); +} + +/* Return B_FALSE if there is insufficient memory for the move request. */ +static boolean_t +kmem_move_begin(kmem_cache_t *cp, kmem_slab_t *sp, void *buf, int flags) +{ + void *to_buf; + avl_index_t index; + kmem_move_t *callback, *pending; + ulong_t n; + + ASSERT(taskq_member(kmem_taskq, curthread)); + ASSERT(MUTEX_NOT_HELD(&cp->cache_lock)); + ASSERT(sp->slab_flags & KMEM_SLAB_MOVE_PENDING); + + callback = kmem_cache_alloc(kmem_move_cache, KM_NOSLEEP); + + if (callback == NULL) + return (B_FALSE); + + callback->kmm_from_slab = sp; + callback->kmm_from_buf = buf; + callback->kmm_flags = flags; + + mutex_enter(&cp->cache_lock); + + n = avl_numnodes(&cp->cache_partial_slabs); + if ((n == 0) || ((n == 1) && !(flags & KMM_DEBUG))) { + mutex_exit(&cp->cache_lock); + kmem_cache_free(kmem_move_cache, callback); + return (B_TRUE); /* there is no need for the move request */ + } + + pending = avl_find(&cp->cache_defrag->kmd_moves_pending, buf, &index); + if (pending != NULL) { + /* + * If the move is already pending and we're desperate now, + * update the move flags. + */ + if (flags & KMM_DESPERATE) { + pending->kmm_flags |= KMM_DESPERATE; + } + mutex_exit(&cp->cache_lock); + kmem_cache_free(kmem_move_cache, callback); + return (B_TRUE); + } + + to_buf = kmem_slab_alloc_impl(cp, avl_first(&cp->cache_partial_slabs), + B_FALSE); + callback->kmm_to_buf = to_buf; + avl_insert(&cp->cache_defrag->kmd_moves_pending, callback, index); + + mutex_exit(&cp->cache_lock); + + if (!taskq_dispatch(kmem_move_taskq, (task_func_t *)kmem_move_buffer, + callback, TQ_NOSLEEP)) { + mutex_enter(&cp->cache_lock); + avl_remove(&cp->cache_defrag->kmd_moves_pending, callback); + mutex_exit(&cp->cache_lock); + kmem_slab_free(cp, to_buf); + kmem_cache_free(kmem_move_cache, callback); + return (B_FALSE); + } + + return (B_TRUE); +} + +static void +kmem_move_end(kmem_cache_t *cp, kmem_move_t *callback) +{ + avl_index_t index; + + ASSERT(cp->cache_defrag != NULL); + ASSERT(taskq_member(kmem_move_taskq, curthread)); + ASSERT(MUTEX_NOT_HELD(&cp->cache_lock)); + + mutex_enter(&cp->cache_lock); + VERIFY(avl_find(&cp->cache_defrag->kmd_moves_pending, + callback->kmm_from_buf, &index) != NULL); + avl_remove(&cp->cache_defrag->kmd_moves_pending, callback); + if (avl_is_empty(&cp->cache_defrag->kmd_moves_pending)) { + list_t *deadlist = &cp->cache_defrag->kmd_deadlist; + kmem_slab_t *sp; + + /* + * The last pending move completed. Release all slabs + * from the front of the dead list except for any slab + * at the tail that needs to be released from the context + * of kmem_move_buffers(). kmem deferred unmapping the + * buffers on these slabs in order to guarantee that + * buffers passed to the move callback have been touched + * only by kmem or by the client itself. + */ + while ((sp = list_remove_head(deadlist)) != NULL) { + if (sp->slab_flags & KMEM_SLAB_MOVE_PENDING) { + list_insert_tail(deadlist, sp); + break; + } + cp->cache_defrag->kmd_deadcount--; + cp->cache_slab_destroy++; + mutex_exit(&cp->cache_lock); + kmem_slab_destroy(cp, sp); + mutex_enter(&cp->cache_lock); + } + } + mutex_exit(&cp->cache_lock); + kmem_cache_free(kmem_move_cache, callback); +} + +/* + * Move buffers from least used slabs first by scanning backwards from the end + * of the partial slab list. Scan at most max_scan candidate slabs and move + * buffers from at most max_slabs slabs (0 for all partial slabs in both cases). + * If desperate to reclaim memory, move buffers from any partial slab, otherwise + * skip slabs with a ratio of allocated buffers at or above the current + * threshold. Return the number of unskipped slabs (at most max_slabs, -1 if the + * scan is aborted) so that the caller can adjust the reclaimability threshold + * depending on how many reclaimable slabs it finds. + * + * kmem_move_buffers() drops and reacquires cache_lock every time it issues a + * move request, since it is not valid for kmem_move_begin() to call + * kmem_cache_alloc() or taskq_dispatch() with cache_lock held. + */ +static int +kmem_move_buffers(kmem_cache_t *cp, size_t max_scan, size_t max_slabs, + int flags) +{ + kmem_slab_t *sp; + void *buf; + int i, j; /* slab index, buffer index */ + int s; /* reclaimable slabs */ + int b; /* allocated (movable) buffers on reclaimable slab */ + boolean_t success; + int refcnt; + int nomove; + + ASSERT(taskq_member(kmem_taskq, curthread)); + ASSERT(MUTEX_HELD(&cp->cache_lock)); + ASSERT(kmem_move_cache != NULL); + ASSERT(cp->cache_move != NULL && cp->cache_defrag != NULL); + ASSERT((flags & KMM_DEBUG) ? !avl_is_empty(&cp->cache_partial_slabs) : + avl_numnodes(&cp->cache_partial_slabs) > 1); + + if (kmem_move_blocked) { + return (0); + } + + if (kmem_move_fulltilt) { + flags |= KMM_DESPERATE; + } + + if (max_scan == 0 || (flags & KMM_DESPERATE)) { + /* + * Scan as many slabs as needed to find the desired number of + * candidate slabs. + */ + max_scan = (size_t)-1; + } + + if (max_slabs == 0 || (flags & KMM_DESPERATE)) { + /* Find as many candidate slabs as possible. */ + max_slabs = (size_t)-1; + } + + sp = avl_last(&cp->cache_partial_slabs); + ASSERT(KMEM_SLAB_IS_PARTIAL(sp)); + for (i = 0, s = 0; (i < max_scan) && (s < max_slabs) && (sp != NULL) && + ((sp != avl_first(&cp->cache_partial_slabs)) || + (flags & KMM_DEBUG)); + sp = AVL_PREV(&cp->cache_partial_slabs, sp), i++) { + + if (!kmem_slab_is_reclaimable(cp, sp, flags)) { + continue; + } + s++; + + /* Look for allocated buffers to move. */ + for (j = 0, b = 0, buf = sp->slab_base; + (j < sp->slab_chunks) && (b < sp->slab_refcnt); + buf = (((char *)buf) + cp->cache_chunksize), j++) { + + if (kmem_slab_allocated(cp, sp, buf) == NULL) { + continue; + } + + b++; + + /* + * Prevent the slab from being destroyed while we drop + * cache_lock and while the pending move is not yet + * registered. Flag the pending move while + * kmd_moves_pending may still be empty, since we can't + * yet rely on a non-zero pending move count to prevent + * the slab from being destroyed. + */ + ASSERT(!(sp->slab_flags & KMEM_SLAB_MOVE_PENDING)); + sp->slab_flags |= KMEM_SLAB_MOVE_PENDING; + /* + * Recheck refcnt and nomove after reacquiring the lock, + * since these control the order of partial slabs, and + * we want to know if we can pick up the scan where we + * left off. + */ + refcnt = sp->slab_refcnt; + nomove = (sp->slab_flags & KMEM_SLAB_NOMOVE); + mutex_exit(&cp->cache_lock); + + success = kmem_move_begin(cp, sp, buf, flags); + + /* + * Now, before the lock is reacquired, kmem could + * process all pending move requests and purge the + * deadlist, so that upon reacquiring the lock, sp has + * been remapped. Or, the client may free all the + * objects on the slab while the pending moves are still + * on the taskq. Therefore, the KMEM_SLAB_MOVE_PENDING + * flag causes the slab to be put at the end of the + * deadlist and prevents it from being destroyed, since + * we plan to destroy it here after reacquiring the + * lock. + */ + mutex_enter(&cp->cache_lock); + ASSERT(sp->slab_flags & KMEM_SLAB_MOVE_PENDING); + sp->slab_flags &= ~KMEM_SLAB_MOVE_PENDING; + + if (sp->slab_refcnt == 0) { + list_t *deadlist = + &cp->cache_defrag->kmd_deadlist; + list_remove(deadlist, sp); + + if (!avl_is_empty( + &cp->cache_defrag->kmd_moves_pending)) { + /* + * A pending move makes it unsafe to + * destroy the slab, because even though + * the move is no longer needed, the + * context where that is determined + * requires the slab to exist. + * Fortunately, a pending move also + * means we don't need to destroy the + * slab here, since it will get + * destroyed along with any other slabs + * on the deadlist after the last + * pending move completes. + */ + list_insert_head(deadlist, sp); + return (-1); + } + + /* + * Destroy the slab now if it was completely + * freed while we dropped cache_lock and there + * are no pending moves. Since slab_refcnt + * cannot change once it reaches zero, no new + * pending moves from that slab are possible. + */ + cp->cache_defrag->kmd_deadcount--; + cp->cache_slab_destroy++; + mutex_exit(&cp->cache_lock); + kmem_slab_destroy(cp, sp); + mutex_enter(&cp->cache_lock); + /* + * Since we can't pick up the scan where we left + * off, abort the scan and say nothing about the + * number of reclaimable slabs. + */ + return (-1); + } + + if (!success) { + /* + * Abort the scan if there is not enough memory + * for the request and say nothing about the + * number of reclaimable slabs. + */ + return (-1); + } + + /* + * The slab's position changed while the lock was + * dropped, so we don't know where we are in the + * sequence any more. + */ + if (sp->slab_refcnt != refcnt) { + /* + * If this is a KMM_DEBUG move, the slab_refcnt + * may have changed because we allocated a + * destination buffer on the same slab. In that + * case, we're not interested in counting it. + */ + return (-1); + } + if ((sp->slab_flags & KMEM_SLAB_NOMOVE) != nomove) + return (-1); + + /* + * Generating a move request allocates a destination + * buffer from the slab layer, bumping the first partial + * slab if it is completely allocated. If the current + * slab becomes the first partial slab as a result, we + * can't continue to scan backwards. + * + * If this is a KMM_DEBUG move and we allocated the + * destination buffer from the last partial slab, then + * the buffer we're moving is on the same slab and our + * slab_refcnt has changed, causing us to return before + * reaching here if there are no partial slabs left. + */ + ASSERT(!avl_is_empty(&cp->cache_partial_slabs)); + if (sp == avl_first(&cp->cache_partial_slabs)) { + /* + * We're not interested in a second KMM_DEBUG + * move. + */ + goto end_scan; + } + } + } +end_scan: + + return (s); +} + +typedef struct kmem_move_notify_args { + kmem_cache_t *kmna_cache; + void *kmna_buf; +} kmem_move_notify_args_t; + +static void +kmem_cache_move_notify_task(void *arg) +{ + kmem_move_notify_args_t *args = arg; + kmem_cache_t *cp = args->kmna_cache; + void *buf = args->kmna_buf; + kmem_slab_t *sp; + + ASSERT(taskq_member(kmem_taskq, curthread)); + ASSERT(list_link_active(&cp->cache_link)); + + zfs_kmem_free(args, sizeof (kmem_move_notify_args_t)); + mutex_enter(&cp->cache_lock); + sp = kmem_slab_allocated(cp, NULL, buf); + + /* Ignore the notification if the buffer is no longer allocated. */ + if (sp == NULL) { + mutex_exit(&cp->cache_lock); + return; + } + + /* Ignore the notification if there's no reason to move the buffer. */ + if (avl_numnodes(&cp->cache_partial_slabs) > 1) { + /* + * So far the notification is not ignored. Ignore the + * notification if the slab is not marked by an earlier refusal + * to move a buffer. + */ + if (!(sp->slab_flags & KMEM_SLAB_NOMOVE) && + (sp->slab_later_count == 0)) { + mutex_exit(&cp->cache_lock); + return; + } + + kmem_slab_move_yes(cp, sp, buf); + ASSERT(!(sp->slab_flags & KMEM_SLAB_MOVE_PENDING)); + sp->slab_flags |= KMEM_SLAB_MOVE_PENDING; + mutex_exit(&cp->cache_lock); + /* see kmem_move_buffers() about dropping the lock */ + (void) kmem_move_begin(cp, sp, buf, KMM_NOTIFY); + mutex_enter(&cp->cache_lock); + ASSERT(sp->slab_flags & KMEM_SLAB_MOVE_PENDING); + sp->slab_flags &= ~KMEM_SLAB_MOVE_PENDING; + if (sp->slab_refcnt == 0) { + list_t *deadlist = &cp->cache_defrag->kmd_deadlist; + list_remove(deadlist, sp); + + if (!avl_is_empty( + &cp->cache_defrag->kmd_moves_pending)) { + list_insert_head(deadlist, sp); + mutex_exit(&cp->cache_lock); + return; + } + + cp->cache_defrag->kmd_deadcount--; + cp->cache_slab_destroy++; + mutex_exit(&cp->cache_lock); + kmem_slab_destroy(cp, sp); + return; + } + } else { + kmem_slab_move_yes(cp, sp, buf); + } + mutex_exit(&cp->cache_lock); +} + +void +kmem_cache_move_notify(kmem_cache_t *cp, void *buf) +{ + kmem_move_notify_args_t *args; + + args = zfs_kmem_alloc(sizeof (kmem_move_notify_args_t), KM_NOSLEEP); + if (args != NULL) { + args->kmna_cache = cp; + args->kmna_buf = buf; + if (!taskq_dispatch(kmem_taskq, + (task_func_t *)kmem_cache_move_notify_task, args, + TQ_NOSLEEP)) + zfs_kmem_free(args, sizeof (kmem_move_notify_args_t)); + } +} + +static void +kmem_cache_defrag(kmem_cache_t *cp) +{ + size_t n; + + ASSERT(cp->cache_defrag != NULL); + + mutex_enter(&cp->cache_lock); + n = avl_numnodes(&cp->cache_partial_slabs); + if (n > 1) { + /* kmem_move_buffers() drops and reacquires cache_lock */ + cp->cache_defrag->kmd_defrags++; + (void) kmem_move_buffers(cp, n, 0, KMM_DESPERATE); + } + mutex_exit(&cp->cache_lock); +} + +/* Is this cache above the fragmentation threshold? */ +static boolean_t +kmem_cache_frag_threshold(kmem_cache_t *cp, uint64_t nfree) +{ + /* + * nfree kmem_frag_numer + * ------------------ > --------------- + * cp->cache_buftotal kmem_frag_denom + */ + return ((nfree * kmem_frag_denom) > + (cp->cache_buftotal * kmem_frag_numer)); +} + +static boolean_t +kmem_cache_is_fragmented(kmem_cache_t *cp, boolean_t *doreap) +{ + boolean_t fragmented; + uint64_t nfree; + + ASSERT(MUTEX_HELD(&cp->cache_lock)); + *doreap = B_FALSE; + + if (kmem_move_fulltilt) { + if (avl_numnodes(&cp->cache_partial_slabs) > 1) { + return (B_TRUE); + } + } else { + if ((cp->cache_complete_slab_count + avl_numnodes( + &cp->cache_partial_slabs)) < kmem_frag_minslabs) { + return (B_FALSE); + } + } + + nfree = cp->cache_bufslab; + fragmented = ((avl_numnodes(&cp->cache_partial_slabs) > 1) && + kmem_cache_frag_threshold(cp, nfree)); + + /* + * Free buffers in the magazine layer appear allocated from the point of + * view of the slab layer. We want to know if the slab layer would + * appear fragmented if we included free buffers from magazines that + * have fallen out of the working set. + */ + if (!fragmented) { + long reap; + + mutex_enter(&cp->cache_depot_lock); + reap = MIN(cp->cache_full.ml_reaplimit, cp->cache_full.ml_min); + reap = MIN(reap, cp->cache_full.ml_total); + mutex_exit(&cp->cache_depot_lock); + + nfree += ((uint64_t)reap * cp->cache_magtype->mt_magsize); + if (kmem_cache_frag_threshold(cp, nfree)) { + *doreap = B_TRUE; + } + } + + return (fragmented); +} + +/* Called periodically from kmem_taskq */ +static void +kmem_cache_scan(kmem_cache_t *cp) +{ + boolean_t reap = B_FALSE; + kmem_defrag_t *kmd; + + ASSERT(taskq_member(kmem_taskq, curthread)); + + mutex_enter(&cp->cache_lock); + + kmd = cp->cache_defrag; + if (kmd->kmd_consolidate > 0) { + kmd->kmd_consolidate--; + mutex_exit(&cp->cache_lock); + kmem_cache_reap(cp); + return; + } + + if (kmem_cache_is_fragmented(cp, &reap)) { + size_t slabs_found; + + /* + * Consolidate reclaimable slabs from the end of the partial + * slab list (scan at most kmem_reclaim_scan_range slabs to find + * reclaimable slabs). Keep track of how many candidate slabs we + * looked for and how many we actually found so we can adjust + * the definition of a candidate slab if we're having trouble + * finding them. + * + * kmem_move_buffers() drops and reacquires cache_lock. + */ + kmd->kmd_scans++; + slabs_found = kmem_move_buffers(cp, kmem_reclaim_scan_range, + kmem_reclaim_max_slabs, 0); + kmd->kmd_slabs_sought += kmem_reclaim_max_slabs; + kmd->kmd_slabs_found += slabs_found; + + if (++kmd->kmd_tries >= kmem_reclaim_scan_range) { + kmd->kmd_tries = 0; + + /* + * If we had difficulty finding candidate slabs in + * previous scans, adjust the threshold so that + * candidates are easier to find. + */ + if (kmd->kmd_slabs_found == kmd->kmd_slabs_sought) { + kmem_adjust_reclaim_threshold(kmd, -1); + } else if ((kmd->kmd_slabs_found * 2) < + kmd->kmd_slabs_sought) { + kmem_adjust_reclaim_threshold(kmd, 1); + } + kmd->kmd_slabs_sought = 0; + kmd->kmd_slabs_found = 0; + } + } else { + kmem_reset_reclaim_threshold(cp->cache_defrag); +#ifdef DEBUG + if (!avl_is_empty(&cp->cache_partial_slabs)) { + /* + * In a debug kernel we want the consolidator to + * run occasionally even when there is plenty of + * memory. + */ + uint16_t debug_rand; + + /* + * smd: note that this only gets called for the + * dnode cache because only the dnode cache has + * kmem_cache_set_move() applied to it + * brendon says move is voluntary and "tricky" + * the reason this is not called is because the source + * is kmem_cache_update(), that only calls this + * function (kmem_cache_scan()) + * if there is a move/defrag (same thing) associated + * with it so hoist some of this code up to to + * kmem_cache_update + */ + + (void) random_get_bytes((uint8_t *)&debug_rand, 2); + if (!kmem_move_noreap && + ((debug_rand % kmem_mtb_reap) == 0)) { + mutex_exit(&cp->cache_lock); + kmem_mtb_reap_count++; + return; + } else if ((debug_rand % kmem_mtb_move) == 0) { + kmd->kmd_scans++; + (void) kmem_move_buffers(cp, + kmem_reclaim_scan_range, 1, KMM_DEBUG); + } + } +#endif /* DEBUG */ + } + + mutex_exit(&cp->cache_lock); + +} + +// =============================================================== +// Status +// =============================================================== + + +size_t +kmem_size(void) +{ + return (total_memory); // smd +} + +// this is used in arc_reclaim_needed. if 1, reclaim is needed. +// returning 1 has the effect of throttling ARC, so be careful. +int +spl_vm_pool_low(void) +{ + bool m = spl_minimal_physmem_p_logic(); + + if (m) + return (0); + else + return (1); +} + +// =============================================================== +// String handling +// =============================================================== + +char * +kmem_strdup(const char *str) +{ + char *buf; + int len; + len = strlen(str) + 1; + buf = kmem_alloc(len, KM_SLEEP); + strlcpy(buf, str, len); + return (buf); +} + +void +kmem_strfree(char *str) +{ + zfs_kmem_free(str, strlen(str) + 1); +} + +char * +kvasprintf(const char *fmt, va_list ap) +{ + unsigned int len; + char *p; + va_list aq; + + va_copy(aq, ap); + len = vsnprintf(NULL, 0, fmt, aq); + va_end(aq); + p = zfs_kmem_alloc(len+1, KM_SLEEP); + if (!p) + return (NULL); + + vsnprintf(p, len+1, fmt, ap); + + return (p); +} + +char * +kmem_vasprintf(const char *fmt, va_list ap) +{ + va_list aq; + char *ptr; + + do { + va_copy(aq, ap); + ptr = kvasprintf(fmt, aq); + va_end(aq); + } while (ptr == NULL); + + return (ptr); +} + +char * +kmem_asprintf(const char *fmt, ...) +{ + va_list ap; + char *ptr; + + do { + va_start(ap, fmt); + ptr = kvasprintf(fmt, ap); + va_end(ap); + } while (ptr == NULL); + + return (ptr); +} + +char * +kmem_strstr(const char *in, const char *str) +{ + char c; + size_t len; + + c = *str++; + if (!c) + return ((char *)in); // Trivial empty string case + + len = strlen(str); + do { + char sc; + + do { + sc = *in++; + if (!sc) + return ((char *)0); + } while (sc != c); + } while (strncmp(in, str, len) != 0); + + return ((char *)(in - 1)); +} + + +// suppress timer and related logic for this kmem cache can live here +// three new per-kmem-cache stats: counters: non-vba-success non-vba-fail; +// flag: arc_no_grow +// from zfs/include/sys/spa.h + +#define SPA_MINBLOCKSHIFT 9 +#define SPA_MAXBLOCKSHIFT 24 +#define SPA_MINBLOCKSIZE (1ULL << SPA_MINBLOCKSHIFT) +#define SPA_MAXBLOCKSIZE (1ULL << SPA_MAXBLOCKSHIFT) + +typedef struct { + _Atomic(kmem_cache_t *)cp_metadata; + _Atomic(kmem_cache_t *)cp_filedata; + uint16_t pointed_to; + _Atomic int64_t suppress_count; + _Atomic uint64_t last_bumped; +} ksupp_t; + +typedef struct { + ksupp_t *ks_entry; +} iksupp_t; + +ksupp_t ksvec[SPA_MAXBLOCKSIZE >> SPA_MINBLOCKSHIFT] = + { { NULL, NULL, false, 0, 0 } }; +iksupp_t iksvec[SPA_MAXBLOCKSIZE >> SPA_MINBLOCKSHIFT] = + { { NULL } }; + +/* + * return true if the reclaim thread should be awakened + * because we do not have enough memory on hand + */ +boolean_t +spl_arc_reclaim_needed(const size_t bytes, kmem_cache_t **zp) +{ + + /* + * fast path: + * if our argument is 0, then do the equivalent of + * if (arc_available_memory() < 0) return (B_TRUE); + * which is traditional arc.c appraoch + * so we can arc_reclaim_needed() -> spl_arc_reclaim_needed(0) + * if we desire. + */ + if (bytes == 0 && spl_free < 0) { + return (B_TRUE); + } + + // copy some code from zio_buf_alloc() + size_t c = (bytes - 1) >> SPA_MINBLOCKSHIFT; + VERIFY3U(c, <, SPA_MAXBLOCKSIZE >> SPA_MINBLOCKSHIFT); + + // if there is free memory in the kmem cache slab layer + // then we do not have to reclaim + + if (zp[c]->cache_bufslab > 1) { + if (spl_free < 0) + atomic_inc_64(&spl_arc_reclaim_avoided); + return (B_FALSE); + } + + extern uint64_t vmem_xnu_useful_bytes_free(void); + const uint64_t min_threshold = 64ULL*1024ULL*1024ULL; + const uint64_t pm_pct = real_total_memory >> 8; + const uint64_t high_threshold = MAX(min_threshold, (uint64_t)pm_pct); + const uint64_t low_threshold = bytes; + + const uint64_t f = vmem_xnu_useful_bytes_free(); + + if (f <= low_threshold) { + return (B_TRUE); + } else if (f > high_threshold) { + if (spl_free < 0) + atomic_inc_64(&spl_arc_reclaim_avoided); + return (B_FALSE); + } + + if (spl_free < 0) { + return (B_TRUE); + } else { + return (B_FALSE); + } +} + +/* small auxiliary function since we do not export struct kmem_cache to zfs */ +size_t +kmem_cache_bufsize(kmem_cache_t *cp) +{ + return (cp->cache_bufsize); +} + +/* + * check that we would not have KMERR_BADCACHE error in the event + * we did kmem_cache_free(cp, buf) in a DEBUG setting + * + * returns: NULL if the buf is not found in any cache + * cparg if the buf is found in cparg + * a pointer to the cache the buf is found in, if not cparg + */ + +kmem_cache_t * +kmem_cache_buf_in_cache(kmem_cache_t *cparg, void *bufarg) +{ + kmem_cache_t *cp = cparg; + kmem_slab_t *sp; + void *buf = bufarg; + + sp = kmem_findslab(cp, buf); + if (sp == NULL) { + for (cp = list_tail(&kmem_caches); cp != NULL; + cp = list_prev(&kmem_caches, cp)) { + if ((sp = kmem_findslab(cp, buf)) != NULL) + break; + } + } + + if (sp == NULL) { + printf("SPL: %s: KMERR_BADADDR orig cache = %s\n", + __func__, cparg->cache_name); + return (NULL); + } + + if (cp == NULL) { + printf("SPL: %s: ERROR cp == NULL; cparg == %s", + __func__, cparg->cache_name); + return (NULL); + } + + if (cp != cparg) { + printf("SPL: %s: KMERR_BADCACHE arg cache = %s but found " + "in %s instead\n", + __func__, cparg->cache_name, cp->cache_name); + return (cp); + } + + ASSERT(cp == cparg); + + return (cp); +} diff --git a/module/os/macos/spl/spl-kstat.c b/module/os/macos/spl/spl-kstat.c new file mode 100644 index 000000000000..52c61e4bae18 --- /dev/null +++ b/module/os/macos/spl/spl-kstat.c @@ -0,0 +1,1252 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * Copyright (C) 2014 Brendon Humphrey + * + */ + +/* + * Provides an implementation of kstat that is backed by OSX sysctls. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * We need to get dynamically allocated memory from the kernel allocator + * (Our needs are small, we wont blow the zone_map). + */ +void *IOMalloc(vm_size_t size); +void IOFree(void *address, vm_size_t size); + +void *IOMallocAligned(vm_size_t size, vm_offset_t alignment); +void IOFreeAligned(void *address, vm_size_t size); + +/* + * Statically declared toplevel OID that all kstats + * will hang off. + */ +struct sysctl_oid_list sysctl__kstat_children; +SYSCTL_DECL(_kstat); +SYSCTL_NODE(, OID_AUTO, kstat, CTLFLAG_RW, 0, "kstat tree"); + +/* + * Sysctl node tree structure. + * + * These are wired into the OSX sysctl structure + * and also stored a list/tree/whatever for easy + * location and destruction at shutdown time. + */ +typedef struct sysctl_tree_node { + char tn_kstat_name[KSTAT_STRLEN + 1]; + struct sysctl_oid_list tn_children; + struct sysctl_oid tn_oid; + struct sysctl_tree_node *tn_next; +} sysctl_tree_node_t; + +/* + * Each named kstats consists of one or more named + * fields which are implemented as OIDs parented + * off the kstat OID. + * + * To implement the kstat interface, we need to be able + * to call the update() function on the kstat to + * allow the owner to populate the kstat values from + * internal data. + * + * To do this we need the address of the kstat_named_t + * which contains the data value, and the owning kstat_t. + * + * OIDs allow a single void* user argument, so we will + * use a structure that contains both values and + * point to that. + */ +typedef struct sysctl_leaf { + kstat_t *l_ksp; + kstat_named_t *l_named; + struct sysctl_oid l_oid; /* kstats are backed w/sysctl */ + char l_name[KSTAT_STRLEN + 1]; /* Name of the related sysctl */ + int l_oid_registered; /* !0 = registered */ +} sysctl_leaf_t; + +/* + * Extended kstat structure -- for internal use only. + */ +typedef struct ekstat { + kstat_t e_ks; /* the kstat itself */ + size_t e_size; /* total allocation size */ + kthread_t *e_owner; /* thread holding this kstat */ + kcondvar_t e_cv; /* wait for owner == NULL */ + /* contains the named values from the kstat */ + struct sysctl_oid_list e_children; + struct sysctl_oid e_oid; /* the kstat is itself an OID */ + /* array of OIDs that implement the children */ + sysctl_leaf_t *e_vals; + uint64_t e_num_vals; /* size of e_vals array */ +} ekstat_t; + +struct sysctl_tree_node *tree_nodes = 0; +struct sysctl_oid *e_sysctl = 0; + +/* sbuf_new() and family does exist in XNU, but Apple wont let us call them */ +#define M_SBUF 105 /* string buffers */ +#define SBMALLOC(size) _MALLOC(size, M_SBUF, M_WAITOK) +#define SBFREE(buf) FREE(buf, M_SBUF) + +#define SBUF_SETFLAG(s, f) do { (s)->s_flags |= (f); } while (0) +#define SBUF_CLEARFLAG(s, f) do { (s)->s_flags &= ~(f); } while (0) +#define SBUF_ISDYNAMIC(s) ((s)->s_flags & SBUF_DYNAMIC) +#define SBUF_ISDYNSTRUCT(s) ((s)->s_flags & SBUF_DYNSTRUCT) +#define SBUF_HASOVERFLOWED(s) ((s)->s_flags & SBUF_OVERFLOWED) +#define SBUF_HASROOM(s) ((s)->s_len < (s)->s_size - 1) +#define SBUF_FREESPACE(s) ((s)->s_size - (s)->s_len - 1) +#define SBUF_CANEXTEND(s) ((s)->s_flags & SBUF_AUTOEXTEND) +#define SBUF_ISFINISHED(s) ((s)->s_flags & SBUF_FINISHED) + +#define SBUF_MINEXTENDSIZE 16 /* Should be power of 2. */ +#define SBUF_MAXEXTENDSIZE PAGE_SIZE +#define SBUF_MAXEXTENDINCR PAGE_SIZE + +#define SBUF_INCLUDENUL 0x00000002 /* FBSD: nulterm byte is counted in len */ +#define SBUF_NULINCLUDED(s) ((s)->s_flags & SBUF_INCLUDENUL) + +void +sbuf_finish(struct sbuf *s) +{ + s->s_buf[s->s_len] = '\0'; + if (SBUF_NULINCLUDED(s)) + s->s_len++; + + SBUF_CLEARFLAG(s, SBUF_OVERFLOWED); + SBUF_SETFLAG(s, SBUF_FINISHED); +} + +char * +sbuf_data(struct sbuf *s) +{ + return (s->s_buf); +} + +int +sbuf_len(struct sbuf *s) +{ + if (SBUF_HASOVERFLOWED(s)) { + return (-1); + } + /* If finished, nulterm is already in len, else add one. */ + if (SBUF_NULINCLUDED(s) && !SBUF_ISFINISHED(s)) + return (s->s_len + 1); + return (s->s_len); +} + +void +sbuf_delete(struct sbuf *s) +{ + int isdyn; + if (SBUF_ISDYNAMIC(s)) { + SBFREE(s->s_buf); + } + isdyn = SBUF_ISDYNSTRUCT(s); + bzero(s, sizeof (*s)); + if (isdyn) { + SBFREE(s); + } +} + +static int +sbuf_extendsize(int size) +{ + int newsize; + + newsize = SBUF_MINEXTENDSIZE; + while (newsize < size) { + if (newsize < (int)SBUF_MAXEXTENDSIZE) { + newsize *= 2; + } else { + newsize += SBUF_MAXEXTENDINCR; + } + } + + return (newsize); +} + +static int +sbuf_extend(struct sbuf *s, int addlen) +{ + char *newbuf; + int newsize; + + if (!SBUF_CANEXTEND(s)) { + return (-1); + } + + newsize = sbuf_extendsize(s->s_size + addlen); + newbuf = (char *)SBMALLOC(newsize); + if (newbuf == NULL) { + return (-1); + } + bcopy(s->s_buf, newbuf, s->s_size); + if (SBUF_ISDYNAMIC(s)) { + SBFREE(s->s_buf); + } else { + SBUF_SETFLAG(s, SBUF_DYNAMIC); + } + s->s_buf = newbuf; + s->s_size = newsize; + return (0); +} + +struct sbuf * +sbuf_new(struct sbuf *s, char *buf, int length, int flags) +{ + flags &= SBUF_USRFLAGMSK; + if (s == NULL) { + s = (struct sbuf *)SBMALLOC(sizeof (*s)); + if (s == NULL) { + return (NULL); + } + bzero(s, sizeof (*s)); + s->s_flags = flags; + SBUF_SETFLAG(s, SBUF_DYNSTRUCT); + } else { + bzero(s, sizeof (*s)); + s->s_flags = flags; + } + s->s_size = length; + if (buf) { + s->s_buf = buf; + return (s); + } + if (flags & SBUF_AUTOEXTEND) { + s->s_size = sbuf_extendsize(s->s_size); + } + s->s_buf = (char *)SBMALLOC(s->s_size); + if (s->s_buf == NULL) { + if (SBUF_ISDYNSTRUCT(s)) { + SBFREE(s); + } + return (NULL); + } + SBUF_SETFLAG(s, SBUF_DYNAMIC); + return (s); +} + +int +sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap) +{ + __builtin_va_list ap_copy; /* XXX tduffy - blame on him */ + int len; + + if (SBUF_HASOVERFLOWED(s)) { + return (-1); + } + + do { + va_copy(ap_copy, ap); + len = vsnprintf(&s->s_buf[s->s_len], SBUF_FREESPACE(s) + 1, + fmt, ap_copy); + va_end(ap_copy); + } while (len > SBUF_FREESPACE(s) && + sbuf_extend(s, len - SBUF_FREESPACE(s)) == 0); + s->s_len += min(len, SBUF_FREESPACE(s)); + if (!SBUF_HASROOM(s) && !SBUF_CANEXTEND(s)) { + SBUF_SETFLAG(s, SBUF_OVERFLOWED); + } + + if (SBUF_HASOVERFLOWED(s)) { + return (-1); + } + return (0); +} + +int +sbuf_printf(struct sbuf *s, const char *fmt, ...) +{ + va_list ap; + int result; + + va_start(ap, fmt); + result = sbuf_vprintf(s, fmt, ap); + va_end(ap); + return (result); +} + +static void +kstat_set_string(char *dst, const char *src) +{ + bzero(dst, KSTAT_STRLEN); + (void) strlcpy(dst, src, KSTAT_STRLEN); +} + +static struct sysctl_oid * +get_oid_with_name(struct sysctl_oid_list *list, char *name) +{ + struct sysctl_oid *oidp; + + SLIST_FOREACH(oidp, list, oid_link) { + if (strcmp(name, oidp->oid_name) == 0) { + return (oidp); + } + } + + return (0); +} + +static void +init_oid_tree_node(struct sysctl_oid_list *parent, char *name, + sysctl_tree_node_t *node) +{ + strlcpy(node->tn_kstat_name, name, KSTAT_STRLEN); + + node->tn_oid.oid_parent = parent; + node->tn_oid.oid_link.sle_next = 0; + node->tn_oid.oid_number = OID_AUTO; + node->tn_oid.oid_arg2 = 0; + node->tn_oid.oid_name = &node->tn_kstat_name[0]; + node->tn_oid.oid_descr = ""; + node->tn_oid.oid_version = SYSCTL_OID_VERSION; + node->tn_oid.oid_refcnt = 0; + node->tn_oid.oid_handler = 0; + node->tn_oid.oid_kind = CTLTYPE_NODE|CTLFLAG_RW|CTLFLAG_OID2; + node->tn_oid.oid_fmt = "N"; + node->tn_oid.oid_arg1 = (void*)(&node->tn_children); + + sysctl_register_oid(&node->tn_oid); + + node->tn_next = tree_nodes; + tree_nodes = node; +} + +static struct sysctl_oid_list * +get_kstat_parent(struct sysctl_oid_list *root, char *module_name, + char *class_name) +{ + struct sysctl_oid *the_module = 0; + struct sysctl_oid *the_class = 0; + sysctl_tree_node_t *new_node = 0; + struct sysctl_oid_list *container = root; + + /* + * Locate/create the module + */ + the_module = get_oid_with_name(root, module_name); + + if (!the_module) { + new_node = IOMalloc(sizeof (sysctl_tree_node_t)); + bzero(new_node, sizeof (sysctl_tree_node_t)); + init_oid_tree_node(root, module_name, new_node); + the_module = &new_node->tn_oid; + } + + /* + * Locate/create the class + */ + container = the_module->oid_arg1; + the_class = get_oid_with_name(container, class_name); + + if (!the_class) { + new_node = IOMalloc(sizeof (sysctl_tree_node_t)); + bzero(new_node, sizeof (sysctl_tree_node_t)); + init_oid_tree_node(container, class_name, new_node); + the_class = &new_node->tn_oid; + } + + container = the_class->oid_arg1; + return (container); +} + +struct sbuf * +sbuf_new_for_sysctl(struct sbuf *s, char *buf, int length, + struct sysctl_req *req) +{ + /* Supply a default buffer size if none given. */ + if (buf == NULL && length == 0) + length = 64; + s = sbuf_new(s, buf, length, SBUF_FIXEDLEN | SBUF_INCLUDENUL); + /* sbuf_set_drain(s, sbuf_sysctl_drain, req); */ + return (s); +} + +static int +kstat_default_update(kstat_t *ksp, int rw) +{ + ASSERT(ksp != NULL); + + if (rw == KSTAT_WRITE) + return (EACCES); + + return (0); +} + +static int +kstat_resize_raw(kstat_t *ksp) +{ + if (ksp->ks_raw_bufsize == KSTAT_RAW_MAX) + return (ENOMEM); + + IOFree(ksp->ks_raw_buf, ksp->ks_raw_bufsize); + ksp->ks_raw_bufsize = MIN(ksp->ks_raw_bufsize * 2, KSTAT_RAW_MAX); + ksp->ks_raw_buf = IOMalloc(ksp->ks_raw_bufsize); + + return (0); +} + +static void * +kstat_raw_default_addr(kstat_t *ksp, loff_t n) +{ + if (n == 0) + return (ksp->ks_data); + return (NULL); +} + +#define HD_COLUMN_MASK 0xff +#define HD_DELIM_MASK 0xff00 +#define HD_OMIT_COUNT (1 << 16) +#define HD_OMIT_HEX (1 << 17) +#define HD_OMIT_CHARS (1 << 18) + +void +sbuf_hexdump(struct sbuf *sb, const void *ptr, int length, const char *hdr, + int flags) +{ + int i, j, k; + int cols; + const unsigned char *cp; + char delim; + + if ((flags & HD_DELIM_MASK) != 0) + delim = (flags & HD_DELIM_MASK) >> 8; + else + delim = ' '; + + if ((flags & HD_COLUMN_MASK) != 0) + cols = flags & HD_COLUMN_MASK; + else + cols = 16; + + cp = ptr; + for (i = 0; i < length; i += cols) { + if (hdr != NULL) + sbuf_printf(sb, "%s", hdr); + + if ((flags & HD_OMIT_COUNT) == 0) + sbuf_printf(sb, "%04x ", i); + + if ((flags & HD_OMIT_HEX) == 0) { + for (j = 0; j < cols; j++) { + k = i + j; + if (k < length) + sbuf_printf(sb, "%c%02x", delim, cp[k]); + else + sbuf_printf(sb, " "); + } + } + + if ((flags & HD_OMIT_CHARS) == 0) { + sbuf_printf(sb, " |"); + for (j = 0; j < cols; j++) { + k = i + j; + if (k >= length) + sbuf_printf(sb, " "); + else if (cp[k] >= ' ' && cp[k] <= '~') + sbuf_printf(sb, "%c", cp[k]); + else + sbuf_printf(sb, "."); + } + sbuf_printf(sb, "|"); + } + sbuf_printf(sb, "\n"); + } +} + +static int +kstat_handle_raw SYSCTL_HANDLER_ARGS +{ + struct sbuf *sb; + void *data; + kstat_t *ksp = arg1; + void *(*addr_op)(kstat_t *ksp, loff_t index); + int n, has_header, rc = 0; + + sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); + if (sb == NULL) + return (ENOMEM); + + if (ksp->ks_raw_ops.addr) + addr_op = ksp->ks_raw_ops.addr; + else + addr_op = kstat_raw_default_addr; + + VERIFY3P(ksp->ks_lock, !=, NULL); + mutex_enter(ksp->ks_lock); + + /* Update the aggsums before reading */ + (void) ksp->ks_update(ksp, KSTAT_READ); + + ksp->ks_raw_bufsize = PAGE_SIZE; + ksp->ks_raw_buf = IOMallocAligned(PAGE_SIZE, PAGE_SIZE); + + n = 0; + has_header = (ksp->ks_raw_ops.headers || + ksp->ks_raw_ops.seq_headers); + +restart_headers: + if (ksp->ks_raw_ops.headers) { + rc = ksp->ks_raw_ops.headers( + ksp->ks_raw_buf, ksp->ks_raw_bufsize); + } else if (ksp->ks_raw_ops.seq_headers) { + struct seq_file f; + + f.sf_buf = ksp->ks_raw_buf; + f.sf_size = ksp->ks_raw_bufsize; + rc = ksp->ks_raw_ops.seq_headers(&f); + } + if (has_header) { + if (rc == ENOMEM && !kstat_resize_raw(ksp)) + goto restart_headers; + if (rc == 0) + sbuf_printf(sb, "\n%s", ksp->ks_raw_buf); + } + + while ((data = addr_op(ksp, n)) != NULL) { +restart: + if (ksp->ks_raw_ops.data) { + rc = ksp->ks_raw_ops.data(ksp->ks_raw_buf, + ksp->ks_raw_bufsize, data); + if (rc == ENOMEM && !kstat_resize_raw(ksp)) + goto restart; + if (rc == 0) + sbuf_printf(sb, "%s", ksp->ks_raw_buf); + + } else { + ASSERT(ksp->ks_ndata == 1); + sbuf_hexdump(sb, ksp->ks_data, + ksp->ks_data_size, NULL, 0); + } + n++; + } + IOFreeAligned(ksp->ks_raw_buf, PAGE_SIZE); + mutex_exit(ksp->ks_lock); + rc = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb)); + sbuf_delete(sb); + return (rc); +} + +static int +kstat_handle_io SYSCTL_HANDLER_ARGS +{ + struct sbuf *sb; + kstat_t *ksp = arg1; + kstat_io_t *kip = ksp->ks_data; + int rc; + + sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); + if (sb == NULL) + return (ENOMEM); + /* Update the aggsums before reading */ + (void) ksp->ks_update(ksp, KSTAT_READ); + + /* though wlentime & friends are signed, they will never be negative */ + sbuf_printf(sb, + "%-8llu %-8llu %-8u %-8u %-8llu %-8llu " + "%-8llu %-8llu %-8llu %-8llu %-8u %-8u\n", + kip->nread, kip->nwritten, + kip->reads, kip->writes, + kip->wtime, kip->wlentime, kip->wlastupdate, + kip->rtime, kip->rlentime, kip->rlastupdate, + kip->wcnt, kip->rcnt); + sbuf_finish(sb); + rc = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb)); + sbuf_delete(sb); + return (rc); +} + +static int +kstat_handle_i64 SYSCTL_HANDLER_ARGS +{ + int error = 0; + sysctl_leaf_t *params = (sysctl_leaf_t *)(arg1); + kstat_named_t *named = params->l_named; + kstat_t *ksp = params->l_ksp; + kmutex_t *lock = ksp->ks_lock; + int lock_needs_release = 0; + + if (lock && !MUTEX_NOT_HELD(lock)) { + mutex_enter(lock); + lock_needs_release = 1; + } + + if (ksp->ks_update) { + ksp->ks_update(ksp, KSTAT_READ); + } + + if (!error && req->newptr) { + /* + * Write request - first read add current values for the kstat + * (remember that is sysctl is likely only one of many + * values that make up the kstat). + */ + + /* Copy the new value from user space */ + (void) copyin(req->newptr, &named->value.i64, + sizeof (named->value.i64)); + + /* and invoke the update operation */ + if (ksp->ks_update) { + error = ksp->ks_update(ksp, KSTAT_WRITE); + } + } else { + /* + * Read request + */ + error = SYSCTL_OUT(req, &named->value.i64, sizeof (int64_t)); + } + + if (lock_needs_release) { + mutex_exit(lock); + } + + return (error); +} + +static int +kstat_handle_ui64 SYSCTL_HANDLER_ARGS +{ + int error = 0; + sysctl_leaf_t *params = (sysctl_leaf_t *)(arg1); + kstat_named_t *named = params->l_named; + kstat_t *ksp = params->l_ksp; + kmutex_t *lock = ksp->ks_lock; + int lock_needs_release = 0; + + if (lock && !MUTEX_NOT_HELD(lock)) { + mutex_enter(lock); + lock_needs_release = 1; + } + + if (ksp->ks_update) { + ksp->ks_update(ksp, KSTAT_READ); + } + + if (!error && req->newptr) { + /* + * Write request - first read add current values for the kstat + * (remember that is sysctl is likely only one of many + * values that make up the kstat). + */ + + /* Copy the new value from user space */ + (void) copyin(req->newptr, &named->value.ui64, + sizeof (named->value.ui64)); + + /* and invoke the update operation */ + if (ksp->ks_update) { + error = ksp->ks_update(ksp, KSTAT_WRITE); + } + } else { + /* + * Read request + */ + error = SYSCTL_OUT(req, &named->value.ui64, sizeof (uint64_t)); + } + + if (lock_needs_release) { + mutex_exit(lock); + } + + return (error); +} + +static int +kstat_handle_string SYSCTL_HANDLER_ARGS +{ + int error = 0; + sysctl_leaf_t *params = (sysctl_leaf_t *)(arg1); + kstat_named_t *named = params->l_named; + kstat_t *ksp = params->l_ksp; + kmutex_t *lock = ksp->ks_lock; + int lock_needs_release = 0; + + if (lock && !MUTEX_NOT_HELD(lock)) { + mutex_enter(lock); + lock_needs_release = 1; + } + + if (ksp->ks_update) { + ksp->ks_update(ksp, KSTAT_READ); + } + + if (!error && req->newptr) { + char *inbuf[256]; + + error = SYSCTL_IN(req, inbuf, req->newlen); + + if (error == 0) { + + inbuf[req->newlen] = 0; + + /* + * Copy the new value from user space + * (copyin done by XNU) + */ + kstat_named_setstr(named, (const char *)inbuf); + + /* and invoke the update operation: last call out */ + if (ksp->ks_update) + error = ksp->ks_update(ksp, KSTAT_WRITE); + } + + } else { + + error = SYSCTL_OUT(req, named->value.string.addr.ptr, + named->value.string.len); + } + + if (lock_needs_release) { + mutex_exit(lock); + } + + return (error); +} + +kstat_t * +kstat_create(const char *ks_module, int ks_instance, const char *ks_name, + const char *ks_class, uchar_t ks_type, ulong_t ks_ndata, uchar_t ks_flags) +{ + kstat_t *ksp = 0; + ekstat_t *e = 0; + size_t size = 0; + + if (ks_class == NULL) + ks_class = "misc"; + + /* + * Allocate memory for the new kstat header. + */ + size = sizeof (ekstat_t); + e = (ekstat_t *)IOMalloc(size); + bzero(e, size); + if (e == NULL) { + cmn_err(CE_NOTE, "kstat_create('%s', %d, '%s'): " + "insufficient kernel memory", + ks_module, ks_instance, ks_name); + return (NULL); + } + e->e_size = size; + + cv_init(&e->e_cv, NULL, CV_DEFAULT, NULL); + + /* + * Initialize as many fields as we can. The caller may reset + * ks_lock, ks_update, ks_private, and ks_snapshot as necessary. + * Creators of virtual kstats may also reset ks_data. It is + * also up to the caller to initialize the kstat data section, + * if necessary. All initialization must be complete before + * calling kstat_install(). + */ + ksp = &e->e_ks; + + ksp->ks_crtime = gethrtime(); + kstat_set_string(ksp->ks_module, ks_module); + ksp->ks_instance = ks_instance; + kstat_set_string(ksp->ks_name, ks_name); + ksp->ks_type = ks_type; + kstat_set_string(ksp->ks_class, ks_class); + ksp->ks_flags = ks_flags | KSTAT_FLAG_INVALID; + ksp->ks_snaptime = ksp->ks_crtime; + ksp->ks_update = kstat_default_update; + + mutex_init(&ksp->ks_private_lock, NULL, MUTEX_DEFAULT, NULL); + ksp->ks_lock = &ksp->ks_private_lock; + + switch (ksp->ks_type) { + case KSTAT_TYPE_RAW: + ksp->ks_ndata = 1; + ksp->ks_data_size = ks_ndata; + break; + case KSTAT_TYPE_NAMED: + ksp->ks_ndata = ks_ndata; + ksp->ks_data_size = ks_ndata * sizeof (kstat_named_t); + break; + case KSTAT_TYPE_INTR: + ksp->ks_ndata = ks_ndata; + ksp->ks_data_size = ks_ndata * sizeof (kstat_intr_t); + break; + case KSTAT_TYPE_IO: + ASSERT(ks_ndata == 1); + ksp->ks_ndata = ks_ndata; + ksp->ks_data_size = ks_ndata * sizeof (kstat_io_t); + break; + case KSTAT_TYPE_TIMER: + ksp->ks_ndata = ks_ndata; + ksp->ks_data_size = ks_ndata * sizeof (kstat_timer_t); + break; + default: + panic("Undefined kstat type %d\n", ksp->ks_type); + } + + + + /* + * Initialise the sysctl that represents this kstat + */ + e->e_children.slh_first = 0; + + e->e_oid.oid_parent = get_kstat_parent(&sysctl__kstat_children, + ksp->ks_module, ksp->ks_class); + e->e_oid.oid_link.sle_next = 0; + e->e_oid.oid_number = OID_AUTO; + e->e_oid.oid_arg2 = 0; + e->e_oid.oid_name = ksp->ks_name; + e->e_oid.oid_descr = ""; + e->e_oid.oid_version = SYSCTL_OID_VERSION; + e->e_oid.oid_refcnt = 0; + e->e_oid.oid_handler = 0; + e->e_oid.oid_kind = CTLTYPE_NODE|CTLFLAG_RW|CTLFLAG_OID2; + e->e_oid.oid_fmt = "N"; + e->e_oid.oid_arg1 = (void*)(&e->e_children); + + /* If VIRTUAL we allocate memory to store data */ + if (ks_flags & KSTAT_FLAG_VIRTUAL) + ksp->ks_data = NULL; + else + ksp->ks_data = (void *)kmem_zalloc( + ksp->ks_data_size, KM_SLEEP); + + sysctl_register_oid(&e->e_oid); + + return (ksp); +} + +void +kstat_install(kstat_t *ksp) +{ + ekstat_t *e = (ekstat_t *)ksp; + kstat_named_t *named_base = 0; + sysctl_leaf_t *vals_base = 0; + sysctl_leaf_t *params = 0; + int oid_permissions = CTLFLAG_RD; + + if (ksp->ks_type == KSTAT_TYPE_NAMED) { + + if (ksp->ks_flags & KSTAT_FLAG_WRITABLE) { + oid_permissions |= CTLFLAG_RW; + } + + // Create the leaf node OID objects + e->e_vals = (sysctl_leaf_t *)IOMalloc(ksp->ks_ndata * + sizeof (sysctl_leaf_t)); + bzero(e->e_vals, ksp->ks_ndata * sizeof (sysctl_leaf_t)); + e->e_num_vals = ksp->ks_ndata; + + named_base = (kstat_named_t *)(ksp->ks_data); + vals_base = e->e_vals; + + for (int i = 0; i < ksp->ks_ndata; i++) { + int oid_valid = 1; + + kstat_named_t *named = &named_base[i]; + sysctl_leaf_t *val = &vals_base[i]; + + // Perform basic initialisation of the sysctl. + // + // The sysctl: kstat.... + snprintf(val->l_name, KSTAT_STRLEN, "%s", named->name); + + val->l_oid.oid_parent = &e->e_children; + val->l_oid.oid_link.sle_next = 0; + val->l_oid.oid_number = OID_AUTO; + val->l_oid.oid_arg2 = 0; + val->l_oid.oid_name = val->l_name; + val->l_oid.oid_descr = ""; + val->l_oid.oid_version = SYSCTL_OID_VERSION; + val->l_oid.oid_refcnt = 0; + + // Based on the kstat type flags, provide location + // of data item and associated type and handler + // flags to the sysctl. + switch (named->data_type) { + case KSTAT_DATA_INT64: + params = (sysctl_leaf_t *)IOMalloc( + sizeof (sysctl_leaf_t)); + params->l_named = named; + params->l_ksp = ksp; + + val->l_oid.oid_handler = + kstat_handle_i64; + val->l_oid.oid_kind = CTLTYPE_QUAD | + oid_permissions | CTLFLAG_OID2; + val->l_oid.oid_fmt = "Q"; + val->l_oid.oid_arg1 = (void*)params; + params = 0; + break; + case KSTAT_DATA_UINT64: + params = (sysctl_leaf_t *)IOMalloc( + sizeof (sysctl_leaf_t)); + params->l_named = named; + params->l_ksp = ksp; + + val->l_oid.oid_handler = + kstat_handle_ui64; + val->l_oid.oid_kind = CTLTYPE_QUAD | + oid_permissions | CTLFLAG_OID2; + val->l_oid.oid_fmt = "Q"; + val->l_oid.oid_arg1 = (void*)params; + break; + case KSTAT_DATA_INT32: + val->l_oid.oid_handler = + sysctl_handle_int; + val->l_oid.oid_kind = CTLTYPE_INT | + oid_permissions | CTLFLAG_OID2; + val->l_oid.oid_fmt = "I"; + val->l_oid.oid_arg1 = &named->value.i32; + break; + case KSTAT_DATA_UINT32: + val->l_oid.oid_handler = + sysctl_handle_int; + val->l_oid.oid_kind = CTLTYPE_INT | + oid_permissions | CTLFLAG_OID2; + val->l_oid.oid_fmt = "IU"; + val->l_oid.oid_arg1 = + &named->value.ui32; + break; + case KSTAT_DATA_LONG: + val->l_oid.oid_handler = + sysctl_handle_long; + val->l_oid.oid_kind = CTLTYPE_INT | + oid_permissions | CTLFLAG_OID2; + val->l_oid.oid_fmt = "L"; + val->l_oid.oid_arg1 = &named->value.l; + break; + case KSTAT_DATA_ULONG: + val->l_oid.oid_handler = + sysctl_handle_long; + val->l_oid.oid_kind = CTLTYPE_INT | + oid_permissions | CTLFLAG_OID2; + val->l_oid.oid_fmt = "L"; + val->l_oid.oid_arg1 = &named->value.ul; + break; + case KSTAT_DATA_STRING: + params = (sysctl_leaf_t *)IOMalloc( + sizeof (sysctl_leaf_t)); + params->l_named = named; + params->l_ksp = ksp; + val->l_oid.oid_handler = + kstat_handle_string; + val->l_oid.oid_kind = CTLTYPE_STRING | + oid_permissions | CTLFLAG_OID2; + val->l_oid.oid_fmt = "S"; + val->l_oid.oid_arg1 = (void*)params; + break; + + case KSTAT_DATA_CHAR: + default: + oid_valid = 0; + break; + } + + /* + * Finally publish the OID, provided that there were + * no issues initialising it. + */ + if (oid_valid) { + sysctl_register_oid(&val->l_oid); + val->l_oid_registered = 1; + } else { + val->l_oid_registered = 0; + } + } + + } else if (ksp->ks_type == KSTAT_TYPE_RAW) { + + e->e_vals = (sysctl_leaf_t *)IOMalloc(sizeof (sysctl_leaf_t)); + bzero(e->e_vals, sizeof (sysctl_leaf_t)); + e->e_num_vals = 1; + sysctl_leaf_t *val = e->e_vals; + + snprintf(val->l_name, KSTAT_STRLEN, "%s", ksp->ks_name); + + val->l_oid.oid_parent = &e->e_children; + val->l_oid.oid_link.sle_next = 0; + val->l_oid.oid_number = OID_AUTO; + val->l_oid.oid_arg2 = 0; + val->l_oid.oid_name = val->l_name; + val->l_oid.oid_descr = ""; + val->l_oid.oid_version = SYSCTL_OID_VERSION; + val->l_oid.oid_refcnt = 0; + + if (ksp->ks_raw_ops.data) { + val->l_oid.oid_handler = + kstat_handle_raw; + val->l_oid.oid_kind = CTLTYPE_STRING | + CTLFLAG_RD | CTLFLAG_OID2; + val->l_oid.oid_fmt = "A"; + val->l_oid.oid_arg1 = (void *) ksp; + sysctl_register_oid(&val->l_oid); + } else { + val->l_oid.oid_handler = + kstat_handle_raw; + val->l_oid.oid_kind = CTLTYPE_OPAQUE | + CTLFLAG_RD | CTLFLAG_OID2; + val->l_oid.oid_fmt = "A"; + val->l_oid.oid_arg1 = (void *) ksp; + sysctl_register_oid(&val->l_oid); + } + + } else if (ksp->ks_type == KSTAT_TYPE_IO) { + + e->e_vals = (sysctl_leaf_t *)IOMalloc(sizeof (sysctl_leaf_t)); + bzero(e->e_vals, sizeof (sysctl_leaf_t)); + e->e_num_vals = 1; + sysctl_leaf_t *val = e->e_vals; + + snprintf(val->l_name, KSTAT_STRLEN, "%s", ksp->ks_name); + + val->l_oid.oid_parent = &e->e_children; + val->l_oid.oid_link.sle_next = 0; + val->l_oid.oid_number = OID_AUTO; + val->l_oid.oid_arg2 = 0; + val->l_oid.oid_name = val->l_name; + val->l_oid.oid_descr = ""; + val->l_oid.oid_version = SYSCTL_OID_VERSION; + val->l_oid.oid_refcnt = 0; + + val->l_oid.oid_handler = + kstat_handle_io; + val->l_oid.oid_kind = CTLTYPE_STRING | + CTLFLAG_RD | CTLFLAG_OID2; + val->l_oid.oid_fmt = "A"; + val->l_oid.oid_arg1 = (void *) ksp; + sysctl_register_oid(&val->l_oid); + } + + ksp->ks_flags &= ~KSTAT_FLAG_INVALID; +} + +static void +remove_child_sysctls(ekstat_t *e) +{ + kstat_t *ksp = &e->e_ks; + kstat_named_t *named_base = (kstat_named_t *)(ksp->ks_data); + sysctl_leaf_t *vals_base = e->e_vals; + + for (int i = 0; i < ksp->ks_ndata; i++) { + if (vals_base[i].l_oid_registered) { + sysctl_unregister_oid(&vals_base[i].l_oid); + vals_base[i].l_oid_registered = 0; + } + + if (named_base[i].data_type == KSTAT_DATA_INT64 || + named_base[i].data_type == KSTAT_DATA_UINT64 || + named_base[i].data_type == KSTAT_DATA_STRING) { + + sysctl_leaf_t *leaf = (sysctl_leaf_t *) + vals_base[i].l_oid.oid_arg1; /* params */ + IOFree(leaf, sizeof (sysctl_leaf_t)); + + if (named_base[i].data_type == KSTAT_DATA_STRING) { + void *data; + int len; + data = KSTAT_NAMED_STR_PTR(&named_base[i]); + len = KSTAT_NAMED_STR_BUFLEN(&named_base[i]); + // kstat_named_setstr(&named_base[i], NULL); + if (data != NULL) + dprintf( + "%s: unknown if %p:%d was freed.\n", + __func__, data, len); + } + } + } +} + +void +kstat_delete(kstat_t *ksp) +{ + ekstat_t *e = (ekstat_t *)ksp; + kmutex_t *lock = ksp->ks_lock; + int lock_needs_release = 0; + + // destroy the sysctl + if (ksp->ks_type == KSTAT_TYPE_NAMED) { + + if (lock && MUTEX_NOT_HELD(lock)) { + mutex_enter(lock); + lock_needs_release = 1; + } + + remove_child_sysctls(e); + + if (lock_needs_release) { + mutex_exit(lock); + } + } + + sysctl_unregister_oid(&e->e_oid); + + if (e->e_vals) { + IOFree(e->e_vals, sizeof (sysctl_leaf_t) * e->e_num_vals); + } + + if (!(ksp->ks_flags & KSTAT_FLAG_VIRTUAL)) + kmem_free(ksp->ks_data, ksp->ks_data_size); + + ksp->ks_lock = NULL; + mutex_destroy(&ksp->ks_private_lock); + + cv_destroy(&e->e_cv); + IOFree(e, e->e_size); +} + +void +kstat_named_setstr(kstat_named_t *knp, const char *src) +{ + void *data; + int len; + + if (knp->data_type != KSTAT_DATA_STRING) + panic("kstat_named_setstr('%p', '%p'): " + "named kstat is not of type KSTAT_DATA_STRING", + (void *)knp, (void *)src); + + data = KSTAT_NAMED_STR_PTR(knp); + len = KSTAT_NAMED_STR_BUFLEN(knp); + + if (data != NULL && len > 0) { + + // If strings are the same, don't bother swapping them + if (src != NULL && + strcmp(src, data) == 0) + return; + + IOFree(data, len); + KSTAT_NAMED_STR_PTR(knp) = NULL; + KSTAT_NAMED_STR_BUFLEN(knp) = 0; + } + + if (src == NULL) + return; + + len = strlen(src) + 1; + + data = IOMalloc(len); + strlcpy(data, src, len); + KSTAT_NAMED_STR_PTR(knp) = data; + KSTAT_NAMED_STR_BUFLEN(knp) = len; +} + +void +kstat_named_init(kstat_named_t *knp, const char *name, uchar_t data_type) +{ + kstat_set_string(knp->name, name); + knp->data_type = data_type; + + if (data_type == KSTAT_DATA_STRING) + kstat_named_setstr(knp, NULL); +} + + +void +kstat_waitq_enter(kstat_io_t *kiop) +{ +} + +void +kstat_waitq_exit(kstat_io_t *kiop) +{ +} + +void +kstat_runq_enter(kstat_io_t *kiop) +{ +} + +void +kstat_runq_exit(kstat_io_t *kiop) +{ +} + +void +__kstat_set_raw_ops(kstat_t *ksp, + int (*headers)(char *buf, size_t size), + int (*data)(char *buf, size_t size, void *data), + void *(*addr)(kstat_t *ksp, loff_t index)) +{ + ksp->ks_raw_ops.headers = headers; + ksp->ks_raw_ops.data = data; + ksp->ks_raw_ops.addr = addr; +} + +void +__kstat_set_seq_raw_ops(kstat_t *ksp, + int (*headers)(struct seq_file *f), + int (*data)(char *buf, size_t size, void *data), + void *(*addr)(kstat_t *ksp, loff_t index)) +{ + ksp->ks_raw_ops.seq_headers = headers; + ksp->ks_raw_ops.data = data; + ksp->ks_raw_ops.addr = addr; +} + +void +spl_kstat_init() +{ + /* + * Create the kstat root OID + */ + sysctl_register_oid(&sysctl__kstat); +} + +void +spl_kstat_fini() +{ + /* + * Destroy the kstat module/class/name tree + * + * Done in two passes, first unregisters all + * of the oids, second releases all the memory. + */ + + sysctl_tree_node_t *iter = tree_nodes; + while (iter) { + sysctl_tree_node_t *tn = iter; + iter = tn->tn_next; + sysctl_unregister_oid(&tn->tn_oid); + } + + while (tree_nodes) { + sysctl_tree_node_t *tn = tree_nodes; + tree_nodes = tn->tn_next; + IOFree(tn, sizeof (sysctl_tree_node_t)); + } + + /* + * Destroy the root oid + */ + sysctl_unregister_oid(&sysctl__kstat); +} diff --git a/module/os/macos/spl/spl-list.c b/module/os/macos/spl/spl-list.c new file mode 100644 index 000000000000..ede7a29c4285 --- /dev/null +++ b/module/os/macos/spl/spl-list.c @@ -0,0 +1,197 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Generic doubly-linked list implementation + */ + +#include +#include +#include +#include + + +#define list_insert_after_node(list, node, object) { \ + list_node_t *lnew = list_d2l(list, object); \ + lnew->list_prev = node; \ + lnew->list_next = node->list_next; \ + node->list_next->list_prev = lnew; \ + node->list_next = lnew; \ +} + +#define list_insert_before_node(list, node, object) { \ + list_node_t *lnew = list_d2l(list, object); \ + lnew->list_next = node; \ + lnew->list_prev = node->list_prev; \ + node->list_prev->list_next = lnew; \ + node->list_prev = lnew; \ +} + +void +list_create(list_t *list, size_t size, size_t offset) +{ + ASSERT(list); + ASSERT(size > 0); + ASSERT(size >= offset + sizeof (list_node_t)); + + list->list_size = size; + list->list_offset = offset; + list->list_head.list_next = list->list_head.list_prev = + &list->list_head; +} + +void +list_destroy(list_t *list) +{ + list_node_t *node = &list->list_head; + + ASSERT(list); + ASSERT(list->list_head.list_next == node); + ASSERT(list->list_head.list_prev == node); + + node->list_next = node->list_prev = NULL; +} + +void +list_insert_after(list_t *list, void *object, void *nobject) +{ + if (object == NULL) { + list_insert_head(list, nobject); + } else { + list_node_t *lold = list_d2l(list, object); + list_insert_after_node(list, lold, nobject); + } +} + +void +list_insert_before(list_t *list, void *object, void *nobject) +{ + if (object == NULL) { + list_insert_tail(list, nobject); + } else { + list_node_t *lold = list_d2l(list, object); + list_insert_before_node(list, lold, nobject); + } +} + +void +list_insert_head(list_t *list, void *object) +{ + list_node_t *lold = &list->list_head; + list_insert_after_node(list, lold, object); +} + +void +list_insert_tail(list_t *list, void *object) +{ + list_node_t *lold = &list->list_head; + list_insert_before_node(list, lold, object); +} + +void +list_remove(list_t *list, void *object) +{ + list_node_t *lold = list_d2l(list, object); + ASSERT(!list_empty(list)); + ASSERT(lold->list_next != NULL); + lold->list_prev->list_next = lold->list_next; + lold->list_next->list_prev = lold->list_prev; + lold->list_next = lold->list_prev = NULL; +} + + +void * +list_head(list_t *list) +{ + if (list_empty(list)) + return (NULL); + return (list_object(list, list->list_head.list_next)); +} + +void * +list_tail(list_t *list) +{ + if (list_empty(list)) + return (NULL); + return (list_object(list, list->list_head.list_prev)); +} + +void * +list_next(list_t *list, void *object) +{ + list_node_t *node = list_d2l(list, object); + + if (node->list_next != &list->list_head) + return (list_object(list, node->list_next)); + + return (NULL); +} + +void * +list_prev(list_t *list, void *object) +{ + list_node_t *node = list_d2l(list, object); + + if (node->list_prev != &list->list_head) + return (list_object(list, node->list_prev)); + + return (NULL); +} + +/* + * Insert src list after dst list. Empty src list thereafter. + */ +void +list_move_tail(list_t *dst, list_t *src) +{ + list_node_t *dstnode = &dst->list_head; + list_node_t *srcnode = &src->list_head; + + ASSERT(dst->list_size == src->list_size); + ASSERT(dst->list_offset == src->list_offset); + + if (list_empty(src)) + return; + + dstnode->list_prev->list_next = srcnode->list_next; + srcnode->list_next->list_prev = dstnode->list_prev; + dstnode->list_prev = srcnode->list_prev; + srcnode->list_prev->list_next = dstnode; + + /* empty src list */ + srcnode->list_next = srcnode->list_prev = srcnode; +} + +int +list_link_active(list_node_t *link) +{ + return (link->list_next != NULL); +} + +int +list_is_empty(list_t *list) +{ + return (list_empty(list)); +} diff --git a/module/os/macos/spl/spl-mutex.c b/module/os/macos/spl/spl-mutex.c new file mode 100644 index 000000000000..990557e92080 --- /dev/null +++ b/module/os/macos/spl/spl-mutex.c @@ -0,0 +1,433 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2008 MacZFS + * Copyright (C) 2013,2020 Jorgen Lundman + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Not defined in headers +extern boolean_t lck_mtx_try_lock(lck_mtx_t *lck); + + +static lck_attr_t *zfs_lock_attr = NULL; +static lck_grp_attr_t *zfs_group_attr = NULL; + +static lck_grp_t *zfs_mutex_group = NULL; + +uint64_t zfs_active_mutex = 0; + +#ifdef SPL_DEBUG_MUTEX +#include +static list_t mutex_list; +static kmutex_t mutex_list_mutex; +static kcondvar_t mutex_list_cv; + + +struct leak { + list_node_t mutex_leak_node; + +#define SPL_DEBUG_MUTEX_MAXCHAR 32 + char location_file[SPL_DEBUG_MUTEX_MAXCHAR]; + char location_function[SPL_DEBUG_MUTEX_MAXCHAR]; + uint64_t location_line; + void *mp; + + uint64_t wdlist_locktime; // time lock was taken + char wdlist_file[32]; // storing holder + uint64_t wdlist_line; +}; + +static int wdlist_exit = 0; + +void +spl_wdlist_settime(void *mpleak, uint64_t value) +{ + struct leak *leak = (struct leak *)mpleak; + if (!leak) + return; + leak->wdlist_locktime = value; +} + +inline static void +spl_wdlist_check(void *ignored) +{ + struct leak *mp; + printf("SPL: Mutex watchdog is alive\n"); + + mutex_enter(&mutex_list_mutex); + while (!wdlist_exit) { + + (void) cv_timedwait(&mutex_list_cv, + &mutex_list_mutex, ddi_get_lbolt() + + SEC_TO_TICK(SPL_MUTEX_WATCHDOG_SLEEP)); + + uint64_t noe = gethrestime_sec(); + for (mp = list_head(&mutex_list); + mp; + mp = list_next(&mutex_list, mp)) { + uint64_t locktime = mp->wdlist_locktime; + if ((locktime > 0) && (noe > locktime) && + noe - locktime >= SPL_MUTEX_WATCHDOG_TIMEOUT) { + printf("SPL: mutex (%p) held for %llus by " + "'%s':%llu\n", mp, noe - + mp->wdlist_locktime, mp->wdlist_file, + mp->wdlist_line); + } // if old + } // for all + } // while not exit + + wdlist_exit = 0; + cv_signal(&mutex_list_cv); + mutex_exit(&mutex_list_mutex); + + printf("SPL: watchdog thread exit\n"); + thread_exit(); +} +#endif + + +int +spl_mutex_subsystem_init(void) +{ + zfs_lock_attr = lck_attr_alloc_init(); + zfs_group_attr = lck_grp_attr_alloc_init(); + zfs_mutex_group = lck_grp_alloc_init("zfs-mutex", zfs_group_attr); + +#ifdef SPL_DEBUG_MUTEX + { + unsigned char mutex[128]; + int i; + + memset(mutex, 0xAF, sizeof (mutex)); + lck_mtx_init((lck_mtx_t *)&mutex[0], zfs_mutex_group, + zfs_lock_attr); + for (i = sizeof (mutex) -1; i >= 0; i--) + if (mutex[i] != 0xAF) + break; + + printf("SPL: mutex size is %u\n", i+1); + + } + + list_create(&mutex_list, sizeof (struct leak), + offsetof(struct leak, mutex_leak_node)); + /* We can not call mutex_init() as it would use "leak" */ + lck_mtx_init((lck_mtx_t *)&mutex_list_mutex.m_lock, zfs_mutex_group, + zfs_lock_attr); + mutex_list_mutex.m_initialised = MUTEX_INIT; + cv_init(&mutex_list_cv, NULL, CV_DEFAULT, NULL); + + (void) thread_create(NULL, 0, spl_wdlist_check, 0, 0, 0, 0, + maxclsyspri); +#endif + return (0); +} + + + +void +spl_mutex_subsystem_fini(void) +{ +#ifdef SPL_DEBUG_MUTEX + uint64_t total = 0; + printf("Dumping leaked mutex allocations...\n"); + + mutex_enter(&mutex_list_mutex); + while (1) { + struct leak *leak, *runner; + uint32_t found; + + leak = list_head(&mutex_list); + + if (leak) { + list_remove(&mutex_list, leak); + } + if (!leak) + break; + + // Run through list and count up how many times this leak is + // found, removing entries as we go. + for (found = 1, runner = list_head(&mutex_list); + runner; + runner = runner ? list_next(&mutex_list, runner) : + list_head(&mutex_list)) { + + if (strcmp(leak->location_file, runner->location_file) + == 0 && strcmp(leak->location_function, + runner->location_function) == 0 && + leak->location_line == runner->location_line) { + // Same place + found++; + list_remove(&mutex_list, runner); + FREE(runner, M_TEMP); + runner = NULL; + } // if same + + } // for all nodes + + printf(" mutex %p : %s %s %llu : # leaks: %u\n", + leak->mp, + leak->location_file, + leak->location_function, + leak->location_line, + found); + + FREE(leak, M_TEMP); + total += found; + + } + mutex_exit(&mutex_list_mutex); + printf("Dumped %llu leaked allocations. Wait for watchdog " + "to exit..\n", total); + + /* Asking for it to quit */ + mutex_enter(&mutex_list_mutex); + wdlist_exit = 1; + while (wdlist_exit) { + cv_signal(&mutex_list_cv); + cv_wait(&mutex_list_cv, &mutex_list_mutex); + } + mutex_exit(&mutex_list_mutex); + /* We can not call mutex_destroy() as it uses leak */ + lck_mtx_destroy((lck_mtx_t *)&mutex_list_mutex.m_lock, zfs_mutex_group); + cv_destroy(&mutex_list_cv); + list_destroy(&mutex_list); +#endif + + lck_attr_free(zfs_lock_attr); + zfs_lock_attr = NULL; + + lck_grp_attr_free(zfs_group_attr); + zfs_group_attr = NULL; + + lck_grp_free(zfs_mutex_group); + zfs_mutex_group = NULL; +} + + + +#ifdef SPL_DEBUG_MUTEX +void +spl_mutex_init(kmutex_t *mp, char *name, kmutex_type_t type, void *ibc, + const char *file, const char *fn, int line) +#else +void +spl_mutex_init(kmutex_t *mp, char *name, kmutex_type_t type, void *ibc) +#endif +{ + ASSERT(type != MUTEX_SPIN); + ASSERT(ibc == NULL); + +#ifdef SPL_DEBUG_MUTEX + VERIFY3U(mp->m_initialised, !=, MUTEX_INIT); +#endif + + lck_mtx_init((lck_mtx_t *)&mp->m_lock, zfs_mutex_group, zfs_lock_attr); + mp->m_owner = NULL; + mp->m_waiters = 0; + mp->m_sleepers = 0; + + atomic_inc_64(&zfs_active_mutex); + +#ifdef SPL_DEBUG_MUTEX + mp->m_initialised = MUTEX_INIT; + + struct leak *leak; + + MALLOC(leak, struct leak *, + sizeof (struct leak), M_TEMP, M_WAITOK); + + if (leak) { + bzero(leak, sizeof (struct leak)); + strlcpy(leak->location_file, file, SPL_DEBUG_MUTEX_MAXCHAR); + strlcpy(leak->location_function, fn, SPL_DEBUG_MUTEX_MAXCHAR); + leak->location_line = line; + leak->mp = mp; + + mutex_enter(&mutex_list_mutex); + list_link_init(&leak->mutex_leak_node); + list_insert_tail(&mutex_list, leak); + mp->leak = leak; + mutex_exit(&mutex_list_mutex); + } + leak->wdlist_locktime = 0; + leak->wdlist_file[0] = 0; + leak->wdlist_line = 0; +#endif +} + +void +spl_mutex_destroy(kmutex_t *mp) +{ + if (!mp) + return; + +#ifdef SPL_DEBUG_MUTEX + VERIFY3U(mp->m_initialised, ==, MUTEX_INIT); +#endif + + if (mp->m_owner != 0) + panic("SPL: releasing held mutex"); + + lck_mtx_destroy((lck_mtx_t *)&mp->m_lock, zfs_mutex_group); + + atomic_dec_64(&zfs_active_mutex); + +#ifdef SPL_DEBUG_MUTEX + mp->m_initialised = MUTEX_DESTROYED; + + if (mp->leak) { + struct leak *leak = (struct leak *)mp->leak; + mutex_enter(&mutex_list_mutex); + list_remove(&mutex_list, leak); + mp->leak = NULL; + mutex_exit(&mutex_list_mutex); + FREE(leak, M_TEMP); + } +#endif +} + + + +#ifdef SPL_DEBUG_MUTEX +void +spl_mutex_enter(kmutex_t *mp, char *file, int line) +#else +void +spl_mutex_enter(kmutex_t *mp) +#endif +{ +#ifdef SPL_DEBUG_MUTEX + VERIFY3U(mp->m_initialised, ==, MUTEX_INIT); +#endif + + if (mp->m_owner == current_thread()) + panic("mutex_enter: locking against myself!"); + +#ifdef DEBUG + if (*((uint64_t *)mp) == 0xdeadbeefdeadbeef) { + panic("SPL: mutex_enter"); + } +#endif + + atomic_inc_64(&mp->m_waiters); + lck_mtx_lock((lck_mtx_t *)&mp->m_lock); + atomic_dec_64(&mp->m_waiters); + mp->m_owner = current_thread(); + +#ifdef SPL_DEBUG_MUTEX + if (mp->leak) { + struct leak *leak = (struct leak *)mp->leak; + leak->wdlist_locktime = gethrestime_sec(); + strlcpy(leak->wdlist_file, file, sizeof (leak->wdlist_file)); + leak->wdlist_line = line; + } +#endif + +} + +void +spl_mutex_exit(kmutex_t *mp) +{ +#ifdef DEBUG + if (*((uint64_t *)mp) == 0xdeadbeefdeadbeef) { + panic("SPL: mutex_exit"); + } +#endif + +#ifdef SPL_DEBUG_MUTEX + VERIFY3U(mp->m_initialised, ==, MUTEX_INIT); +#endif + +#ifdef SPL_DEBUG_MUTEX + if (mp->leak) { + struct leak *leak = (struct leak *)mp->leak; + uint64_t locktime = leak->wdlist_locktime; + uint64_t noe = gethrestime_sec(); + if ((locktime > 0) && (noe > locktime) && + noe - locktime >= SPL_MUTEX_WATCHDOG_TIMEOUT) { + printf("SPL: mutex (%p) finally released after %llus " + "by '%s':%llu\n", leak, noe - leak->wdlist_locktime, + leak->wdlist_file, leak->wdlist_line); + } + leak->wdlist_locktime = 0; + leak->wdlist_file[0] = 0; + leak->wdlist_line = 0; + } +#endif + mp->m_owner = NULL; + lck_mtx_unlock((lck_mtx_t *)&mp->m_lock); +} + + +int +spl_mutex_tryenter(kmutex_t *mp) +{ + int held; + +#ifdef SPL_DEBUG_MUTEX + VERIFY3U(mp->m_initialised, ==, MUTEX_INIT); +#endif + + atomic_inc_64(&mp->m_waiters); + held = lck_mtx_try_lock((lck_mtx_t *)&mp->m_lock); + atomic_dec_64(&mp->m_waiters); + if (held) { + mp->m_owner = current_thread(); + +#ifdef SPL_DEBUG_MUTEX + if (mp->leak) { + struct leak *leak = (struct leak *)mp->leak; + leak->wdlist_locktime = gethrestime_sec(); + strlcpy(leak->wdlist_file, "tryenter", + sizeof (leak->wdlist_file)); + leak->wdlist_line = 123; + } +#endif + + } + return (held); +} + +int +spl_mutex_owned(kmutex_t *mp) +{ + return (mp->m_owner == current_thread()); +} + +struct kthread * +spl_mutex_owner(kmutex_t *mp) +{ + return (mp->m_owner); +} diff --git a/module/os/macos/spl/spl-osx.c b/module/os/macos/spl/spl-osx.c new file mode 100644 index 000000000000..91f7eb8ebbec --- /dev/null +++ b/module/os/macos/spl/spl-osx.c @@ -0,0 +1,518 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013, 2020 Jorgen Lundman + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _task_user_ +#include + +#include + +static utsname_t utsname_static = { { 0 } }; + +unsigned int max_ncpus = 0; +uint64_t total_memory = 0; +uint64_t real_total_memory = 0; + +// Size in bytes of the memory allocated in seg_kmem +extern uint64_t segkmem_total_mem_allocated; + +extern int bsd_hostname(char *, size_t, size_t *); +static char spl_hostname[MAXHOSTNAMELEN]; + +#ifdef __arm64__ + +/* Currently lua's setjmp does not work on arm64 */ + +void +longjmp(void *env, int val) +{ +} + +int +setjmp(void *env) +{ + return (0); +} +#endif + +utsname_t * +utsname(void) +{ + return (&utsname_static); +} + +/* + * Solaris delay is in ticks (hz) and Darwin uses microsecs + * 1 HZ is 10 milliseconds + */ +void +osx_delay(int ticks) +{ + if (ticks < 2) { + // IODelay spins and takes microseconds as an argument + // don't spend more than 10msec spinning. + IODelay(ticks * 10000); + return; + } + + // ticks are 10 msec units + int64_t ticks_to_go = (int64_t)ticks; + // zfs_lbolt() is in 10 mec units + int64_t start_tick = (int64_t)zfs_lbolt(); + int64_t end_tick = start_tick + (int64_t)ticks_to_go; + + do { + IOSleep(ticks_to_go); + int64_t cur_tick = (int64_t)zfs_lbolt(); + ticks_to_go = (end_tick - cur_tick); + } while (ticks_to_go > 0); + +} + + +uint32_t +zone_get_hostid(void *zone) +{ + size_t len; + uint32_t myhostid = 0; + + len = sizeof (myhostid); + sysctlbyname("kern.hostid", &myhostid, &len, NULL, 0); + return (myhostid); +} + +extern void *(*__ihook_malloc)(size_t size); +extern void (*__ihook_free)(void *); + +const char * +spl_panicstr(void) +{ + return (NULL); +} + +extern int get_system_inshutdown(void); + +int +spl_system_inshutdown(void) +{ + // return (get_system_inshutdown()); + return (1); +} + +#include +typedef struct mach_header_64 kernel_mach_header_t; +#include +typedef struct nlist_64 kernel_nlist_t; + +typedef struct segment_command_64 kernel_segment_command_t; + +typedef struct _loaded_kext_summary { + char name[KMOD_MAX_NAME]; + uuid_t uuid; + uint64_t address; + uint64_t size; + uint64_t version; + uint32_t loadTag; + uint32_t flags; + uint64_t reference_list; +} OSKextLoadedKextSummary; + +typedef struct _loaded_kext_summary_header { + uint32_t version; + uint32_t entry_size; + uint32_t numSummaries; + uint32_t reserved; /* explicit alignment for gdb */ + OSKextLoadedKextSummary summaries[0]; +} OSKextLoadedKextSummaryHeader; + +extern OSKextLoadedKextSummaryHeader *gLoadedKextSummaries; + +typedef struct _cframe_t { + struct _cframe_t *prev; + uintptr_t caller; +#if PRINT_ARGS_FROM_STACK_FRAME + unsigned args[0]; +#endif +} cframe_t; + +extern kernel_mach_header_t _mh_execute_header; + +extern kmod_info_t *kmod; /* the list of modules */ + +extern addr64_t kvtophys(vm_offset_t va); + +static int +panic_print_macho_symbol_name(kernel_mach_header_t *mh, vm_address_t search, + const char *module_name) +{ + kernel_nlist_t *sym = NULL; + struct load_command *cmd; + kernel_segment_command_t *orig_ts = NULL, *orig_le = NULL; + struct symtab_command *orig_st = NULL; + unsigned int i; + char *strings, *bestsym = NULL; + vm_address_t bestaddr = 0, diff, curdiff; + + /* + * Assume that if it's loaded and linked into the kernel, + * it's a valid Mach-O + */ + cmd = (struct load_command *)&mh[1]; + for (i = 0; i < mh->ncmds; i++) { + if (cmd->cmd == LC_SEGMENT_64) { + kernel_segment_command_t *orig_sg = + (kernel_segment_command_t *)cmd; + + if (strncmp(SEG_TEXT, orig_sg->segname, + sizeof (orig_sg->segname)) == 0) + orig_ts = orig_sg; + else if (strncmp(SEG_LINKEDIT, orig_sg->segname, + sizeof (orig_sg->segname)) == 0) + orig_le = orig_sg; + /* pre-Lion i386 kexts have a single unnamed segment */ + else if (strncmp("", orig_sg->segname, + sizeof (orig_sg->segname)) == 0) + orig_ts = orig_sg; + } else if (cmd->cmd == LC_SYMTAB) + orig_st = (struct symtab_command *)cmd; + + cmd = (struct load_command *)((uintptr_t)cmd + cmd->cmdsize); + } + + if ((orig_ts == NULL) || (orig_st == NULL) || (orig_le == NULL)) + return (0); + + if ((search < orig_ts->vmaddr) || + (search >= orig_ts->vmaddr + orig_ts->vmsize)) { + /* search out of range for this mach header */ + return (0); + } + + sym = (kernel_nlist_t *)(uintptr_t)(orig_le->vmaddr + + orig_st->symoff - orig_le->fileoff); + strings = (char *)(uintptr_t)(orig_le->vmaddr + + orig_st->stroff - orig_le->fileoff); + diff = search; + + for (i = 0; i < orig_st->nsyms; i++) { + if (sym[i].n_type & N_STAB) continue; + + if (sym[i].n_value <= search) { + curdiff = search - (vm_address_t)sym[i].n_value; + if (curdiff < diff) { + diff = curdiff; + bestaddr = sym[i].n_value; + bestsym = strings + sym[i].n_un.n_strx; + } + } + } + + if (bestsym != NULL) { + if (diff != 0) { + printf("%s : %s + 0x%lx", module_name, bestsym, + (unsigned long)diff); + } else { + printf("%s : %s", module_name, bestsym); + } + return (1); + } + return (0); +} + + +static void +panic_print_kmod_symbol_name(vm_address_t search) +{ +#if 0 // gLoadedKextSummaries is no longer available + uint_t i; + if (gLoadedKextSummaries == NULL) + return; + for (i = 0; i < gLoadedKextSummaries->numSummaries; ++i) { + OSKextLoadedKextSummary *summary = + gLoadedKextSummaries->summaries + i; + + if ((search >= summary->address) && + (search < (summary->address + summary->size))) { + kernel_mach_header_t *header = + (kernel_mach_header_t *)(uintptr_t)summary->address; + if (panic_print_macho_symbol_name(header, search, + summary->name) == 0) { + printf("%s + %llu", summary->name, + (unsigned long)search - summary->address); + } + break; + } + } +#endif +} + + +static void +panic_print_symbol_name(vm_address_t search) +{ + /* try searching in the kernel */ +#if 0 + if (panic_print_macho_symbol_name(&_mh_execute_header, + search, "mach_kernel") == 0) { + /* that failed, now try to search for the right kext */ + panic_print_kmod_symbol_name(search); + } +#endif +} + + +void +spl_backtrace(char *thesignal) +{ + void *stackptr; + + printf("SPL: backtrace \"%s\"\n", thesignal); + +#if defined(__i386__) + __asm__ volatile("movl %%ebp, %0" : "=m" (stackptr)); +#elif defined(__x86_64__) + __asm__ volatile("movq %%rbp, %0" : "=m" (stackptr)); +#endif + + int frame_index; + int nframes = 16; + cframe_t *frame = (cframe_t *)stackptr; + + for (frame_index = 0; frame_index < nframes; frame_index++) { + vm_offset_t curframep = (vm_offset_t)frame; + if (!curframep) + break; + if (curframep & 0x3) { + printf("SPL: Unaligned frame\n"); + break; + } +#if 0 + // no kvtophys() available now. Used to verify only? + // pmap_find_phys(kernel_pmap, curframep) ? + if (!kvtophys(curframep) || + !kvtophys(curframep + sizeof (cframe_t) - 1)) { + printf("SPL: No mapping exists for frame pointer\n"); + break; + } +#endif + printf("SPL: %p : 0x%lx ", frame, frame->caller); + panic_print_symbol_name((vm_address_t)frame->caller); + printf("\n"); + frame = frame->prev; + } +} + +int +getpcstack(uintptr_t *pcstack, int pcstack_limit) +{ + int depth = 0; + void *stackptr; + +#if defined(__i386__) + __asm__ volatile("movl %%ebp, %0" : "=m" (stackptr)); +#elif defined(__x86_64__) + __asm__ volatile("movq %%rbp, %0" : "=m" (stackptr)); +#endif + + int frame_index; + int nframes = pcstack_limit; + cframe_t *frame = (cframe_t *)stackptr; + + for (frame_index = 0; frame_index < nframes; frame_index++) { + vm_offset_t curframep = (vm_offset_t)frame; + if (!curframep) + break; + if (curframep & 0x3) { + break; + } +#if 0 + if (!kvtophys(curframep) || + !kvtophys(curframep + sizeof (cframe_t) - 1)) { + break; + } +#endif + pcstack[depth++] = frame->caller; + frame = frame->prev; + } + + return (depth); +} + +void +print_symbol(uintptr_t symbol) +{ + printf("SPL: "); + panic_print_symbol_name((vm_address_t)(symbol)); + printf("\n"); +} + +int +ddi_copyin(const void *from, void *to, size_t len, int flags) +{ + int ret = 0; + + /* Fake ioctl() issued by kernel, 'from' is a kernel address */ + if (flags & FKIOCTL) + bcopy(from, to, len); + else + ret = copyin((user_addr_t)from, (void *)to, len); + + return (ret); +} + +int +ddi_copyout(const void *from, void *to, size_t len, int flags) +{ + int ret = 0; + + /* Fake ioctl() issued by kernel, 'from' is a kernel address */ + if (flags & FKIOCTL) { + bcopy(from, to, len); + } else { + ret = copyout(from, (user_addr_t)to, len); + } + + return (ret); +} + +/* + * Technically, this call does not exist in illumos, but we use it for + * consistency. + */ +int +ddi_copyinstr(const void *uaddr, void *kaddr, size_t len, size_t *done) +{ + int ret; + size_t local_done; + +#undef copyinstr + ret = copyinstr((user_addr_t)uaddr, kaddr, len, &local_done); + if (done != NULL) + *done = local_done; + return (ret); +} + +kern_return_t +spl_start(kmod_info_t *ki, void *d) +{ + printf("SPL: loading\n"); + + int ncpus; + size_t len = sizeof (ncpus); + + /* + * Boot load time is excessively early, so we have to wait + * until certain subsystems are available. Surely there is + * a more elegant way to do this wait? + */ + + while (current_proc() == NULL) { + printf("SPL: waiting for kernel init...\n"); + delay(hz>>1); + } + + while (1) { + len = sizeof (total_memory); + sysctlbyname("hw.memsize", &total_memory, &len, NULL, 0); + if (total_memory != 0) break; + + printf("SPL: waiting for sysctl...\n"); + delay(hz>>1); + } + + sysctlbyname("hw.logicalcpu_max", &max_ncpus, &len, NULL, 0); + if (!max_ncpus) max_ncpus = 1; + + /* + * Setting the total memory to physmem * 50% here, since kmem is + * not in charge of all memory and we need to leave some room for + * the OS X allocator. We internally add pressure if we step over it + */ + real_total_memory = total_memory; + total_memory = total_memory * 50ULL / 100ULL; + physmem = total_memory / PAGE_SIZE; + + len = sizeof (utsname_static.sysname); + sysctlbyname("kern.ostype", &utsname_static.sysname, &len, NULL, 0); + + /* + * For some reason, (CTLFLAG_KERN is not set) looking up hostname + * returns 1. So we set it to uuid just to give it *something*. + * As it happens, ZFS sets the nodename on init. + */ + len = sizeof (utsname_static.nodename); + sysctlbyname("kern.uuid", &utsname_static.nodename, &len, NULL, 0); + + len = sizeof (utsname_static.release); + sysctlbyname("kern.osrelease", &utsname_static.release, &len, NULL, 0); + + len = sizeof (utsname_static.version); + sysctlbyname("kern.version", &utsname_static.version, &len, NULL, 0); + + strlcpy(spl_hostname, "noname", sizeof (spl_hostname)); + // Private.exports + // bsd_hostname(spl_hostname, sizeof (spl_hostname), &len); + + strlcpy(utsname_static.nodename, spl_hostname, + sizeof (utsname_static.nodename)); + + spl_mutex_subsystem_init(); + spl_kmem_init(total_memory); + spl_vnode_init(); + spl_kmem_thread_init(); + spl_kmem_mp_init(); + + return (KERN_SUCCESS); +} + +kern_return_t +spl_stop(kmod_info_t *ki, void *d) +{ + spl_kmem_thread_fini(); + spl_vnode_fini(); + spl_taskq_fini(); + spl_rwlock_fini(); + spl_tsd_fini(); + spl_kmem_fini(); + spl_kstat_fini(); + spl_mutex_subsystem_fini(); + + return (KERN_SUCCESS); +} diff --git a/module/os/macos/spl/spl-policy.c b/module/os/macos/spl/spl-policy.c new file mode 100644 index 000000000000..6a743ac794f8 --- /dev/null +++ b/module/os/macos/spl/spl-policy.c @@ -0,0 +1,185 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +#include +#include +#include + +int +spl_priv_check_cred(kauth_cred_t cred, int priv, __unused int flags) +{ + int error; + + if (kauth_cred_getuid(cred) == 0) { + error = 0; + goto out; + } + + /* + * The default is deny, so if no policies have granted it, reject + * with a privilege error here. + */ + error = EPERM; +out: + return (error); +} + +int +secpolicy_fs_unmount(cred_t *cr, struct mount *vfsp) +{ + return (spl_priv_check_cred((kauth_cred_t)cr, PRIV_VFS_UNMOUNT, 0)); +} + +int +secpolicy_nfs(const cred_t *cr) +{ + return (spl_priv_check_cred((kauth_cred_t)cr, PRIV_NFS_DAEMON, 0)); +} + +int +secpolicy_sys_config(const cred_t *cr, boolean_t checkonly) +{ + return (spl_priv_check_cred((kauth_cred_t)cr, PRIV_ZFS_POOL_CONFIG, 0)); +} + +int +secpolicy_zfs(const cred_t *cr) +{ + return (spl_priv_check_cred((kauth_cred_t)cr, PRIV_VFS_MOUNT, 0)); +} + +int +secpolicy_zinject(const cred_t *cr) +{ + return (spl_priv_check_cred((kauth_cred_t)cr, PRIV_ZFS_INJECT, 0)); +} + +int +secpolicy_vnode_any_access(const cred_t *cr, struct vnode *vp, uid_t owner) +{ + // FIXME + return (0); +} + +int +secpolicy_vnode_access2(const cred_t *cr, struct vnode *vp, uid_t owner, + mode_t curmode, mode_t wantmode) +{ + // FIXME + return (0); +} + +int +secpolicy_vnode_setattr(cred_t *cr, struct vnode *vp, vattr_t *vap, + const vattr_t *ovap, int flags, + int unlocked_access(void *, int, cred_t *), + void *node) +{ + // FIXME + return (0); +} + +int +secpolicy_vnode_stky_modify(const cred_t *cred) +{ + return (EPERM); +} + +int +secpolicy_setid_setsticky_clear(vnode_t *vp, vattr_t *vap, const vattr_t *ovap, + cred_t *cr) +{ + // FIXME + return (0); +} + +int +secpolicy_vnode_remove(struct vnode *vp, const cred_t *cr) +{ + return (0); +} + +int +secpolicy_vnode_create_gid(const cred_t *cred) +{ + return (0); +} + +int +secpolicy_vnode_setids_setgids(struct vnode *vp, const cred_t *cr, + gid_t gid) +{ + return (0); +} + +int +secpolicy_vnode_setdac(struct vnode *vp, const cred_t *cr, uid_t u) +{ + return (0); +} + +int +secpolicy_vnode_chown(struct vnode *vp, const cred_t *cr, uid_t u) +{ + return (0); +} + +int +secpolicy_vnode_setid_retain(struct znode *zp, const cred_t *cr, + boolean_t issuidroot) +{ + return (0); +} + +int +secpolicy_xvattr(vattr_t *vap, uid_t uid, const cred_t *cr, mode_t mod) +{ + return (0); +} + +int +secpolicy_setid_clear(vattr_t *vap, const cred_t *cr) +{ + return (0); +} + +int +secpolicy_basic_link(const cred_t *cr) +{ + return (0); +} + +int +secpolicy_fs_mount_clearopts(const cred_t *cr, struct mount *mp) +{ + return (0); +} + +int +secpolicy_fs_mount(const cred_t *cr, struct vnode *vp, struct mount *mp) +{ + return (spl_priv_check_cred((kauth_cred_t)cr, PRIV_VFS_MOUNT, 0)); +} + +int +secpolicy_zfs_proc(const cred_t *cr, proc_t *proc) +{ + return (spl_priv_check_cred((kauth_cred_t)cr, PRIV_VFS_MOUNT, 0)); +} diff --git a/module/os/macos/spl/spl-proc.c b/module/os/macos/spl/spl-proc.c new file mode 100644 index 000000000000..9c90559f0fba --- /dev/null +++ b/module/os/macos/spl/spl-proc.c @@ -0,0 +1,30 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#include +#include +#include + +struct proc { + void *nothing; +}; + +struct proc p0 = {0}; diff --git a/module/os/macos/spl/spl-proc_list.c b/module/os/macos/spl/spl-proc_list.c new file mode 100644 index 000000000000..9ec54e21c87a --- /dev/null +++ b/module/os/macos/spl/spl-proc_list.c @@ -0,0 +1,157 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#include +#include +#include + +void *IOMalloc(vm_size_t size); +void IOFree(void *address, vm_size_t size); + +typedef struct procfs_list_iter { + procfs_list_t *pli_pl; + void *pli_elt; +} pli_t; + +void +seq_printf(struct seq_file *f, const char *fmt, ...) +{ + va_list adx; + + va_start(adx, fmt); + (void) vsnprintf(f->sf_buf, f->sf_size, fmt, adx); + va_end(adx); +} + +static int +procfs_list_update(kstat_t *ksp, int rw) +{ + procfs_list_t *pl = ksp->ks_private; + + if (rw == KSTAT_WRITE) + pl->pl_clear(pl); + + return (0); +} + +static int +procfs_list_data(char *buf, size_t size, void *data) +{ + pli_t *p; + void *elt; + procfs_list_t *pl; + struct seq_file f; + + p = data; + pl = p->pli_pl; + elt = p->pli_elt; + IOFree(p, sizeof (*p)); + f.sf_buf = buf; + f.sf_size = size; + return (pl->pl_show(&f, elt)); +} + +static void * +procfs_list_addr(kstat_t *ksp, loff_t n) +{ + procfs_list_t *pl = ksp->ks_private; + void *elt = ksp->ks_private1; + pli_t *p = NULL; + + + if (n == 0) + ksp->ks_private1 = list_head(&pl->pl_list); + else if (elt) + ksp->ks_private1 = list_next(&pl->pl_list, elt); + + if (ksp->ks_private1) { + p = IOMalloc(sizeof (*p)); + p->pli_pl = pl; + p->pli_elt = ksp->ks_private1; + } + + return (p); +} + + +void +procfs_list_install(const char *module, + const char *submodule, + const char *name, + mode_t mode, + procfs_list_t *procfs_list, + int (*show)(struct seq_file *f, void *p), + int (*show_header)(struct seq_file *f), + int (*clear)(procfs_list_t *procfs_list), + size_t procfs_list_node_off) +{ + kstat_t *procfs_kstat; + + mutex_init(&procfs_list->pl_lock, NULL, MUTEX_DEFAULT, NULL); + list_create(&procfs_list->pl_list, + procfs_list_node_off + sizeof (procfs_list_node_t), + procfs_list_node_off + offsetof(procfs_list_node_t, pln_link)); + procfs_list->pl_show = show; + procfs_list->pl_show_header = show_header; + procfs_list->pl_clear = clear; + procfs_list->pl_next_id = 1; + procfs_list->pl_node_offset = procfs_list_node_off; + + procfs_kstat = kstat_create(module, 0, name, submodule, + KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL); + + if (procfs_kstat) { + procfs_kstat->ks_lock = &procfs_list->pl_lock; + procfs_kstat->ks_ndata = UINT32_MAX; + procfs_kstat->ks_private = procfs_list; + procfs_kstat->ks_update = procfs_list_update; + kstat_set_seq_raw_ops(procfs_kstat, show_header, + procfs_list_data, procfs_list_addr); + kstat_install(procfs_kstat); + procfs_list->pl_private = procfs_kstat; + } +} + +void +procfs_list_uninstall(procfs_list_t *procfs_list) +{ +} + +void +procfs_list_destroy(procfs_list_t *procfs_list) +{ + ASSERT(list_is_empty(&procfs_list->pl_list)); + kstat_delete(procfs_list->pl_private); + list_destroy(&procfs_list->pl_list); + mutex_destroy(&procfs_list->pl_lock); +} + +#define NODE_ID(procfs_list, obj) \ + (((procfs_list_node_t *)(((char *)obj) + \ + (procfs_list)->pl_node_offset))->pln_id) + +void +procfs_list_add(procfs_list_t *procfs_list, void *p) +{ + ASSERT(MUTEX_HELD(&procfs_list->pl_lock)); + NODE_ID(procfs_list, p) = procfs_list->pl_next_id++; + list_insert_tail(&procfs_list->pl_list, p); +} diff --git a/module/os/macos/spl/spl-processor.c b/module/os/macos/spl/spl-processor.c new file mode 100644 index 000000000000..9a05e259a7d2 --- /dev/null +++ b/module/os/macos/spl/spl-processor.c @@ -0,0 +1,88 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#include + +extern int cpu_number(void); + +#ifdef __x86_64__ +#define cpuid(func, a, b, c, d) \ + __asm__ __volatile__( \ + " pushq %%rbx \n" \ + " xorq %%rcx,%%rcx \n" \ + " cpuid \n" \ + " movq %%rbx, %%rsi \n" \ + " popq %%rbx \n" : \ + "=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (func)) +#else /* Add ARM */ +#define cpuid(func, a, b, c, d) \ + a = b = c = d = 0 +#endif + +static uint64_t cpuid_features = 0ULL; +static uint64_t cpuid_features_leaf7 = 0ULL; +static boolean_t cpuid_has_xgetbv = B_FALSE; + +uint32_t +getcpuid() +{ +#if defined(__aarch64__) + // Find arm64 solution. + return (0); +#else + return ((uint32_t)cpu_number()); +#endif +} + +uint64_t +spl_cpuid_features(void) +{ + static int first_time = 1; + uint64_t a, b, c, d; + + if (first_time == 1) { + first_time = 0; + cpuid(0, a, b, c, d); + if (a >= 1) { + cpuid(1, a, b, c, d); + cpuid_features = d; + cpuid_has_xgetbv = (c & 0x08000000); // number->#define + } + if (a >= 7) { + cpuid(7, a, b, c, d); + cpuid_features_leaf7 = b; + cpuid_features_leaf7 |= (c << 32); + } + } + return (cpuid_features); +} + +uint64_t +spl_cpuid_leaf7_features(void) +{ + return (cpuid_features_leaf7); +} diff --git a/module/os/macos/spl/spl-qsort.c b/module/os/macos/spl/spl-qsort.c new file mode 100644 index 000000000000..d647bc87de32 --- /dev/null +++ b/module/os/macos/spl/spl-qsort.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved + * + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)qsort.c 8.1 (Berkeley) 6/4/93 + */ + + +#include + +static inline +char *med3(char *, char *, char *, int (*)(const void *, const void *)); +static inline +void swapfunc(char *, char *, int, int); + +#define min(a, b) (a) < (b) ? a : b + +/* + * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". + */ +#define swapcode(TYPE, parmi, parmj, n) { \ + long i = (n) / sizeof (TYPE); \ + TYPE *pi = (TYPE *) (parmi); \ + TYPE *pj = (TYPE *) (parmj); \ + do { \ + TYPE t = *pi; \ + *pi++ = *pj; \ + *pj++ = t; \ + } while (--i > 0); \ +} + +#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof (long) || \ + es % sizeof (long) ? 2 : es == sizeof (long)? 0 : 1; + +static inline void +swapfunc(char *a, char *b, int n, int swaptype) +{ + if (swaptype <= 1) + swapcode(long, a, b, n) + else + swapcode(char, a, b, n) +} + +#define swap(a, b) \ + if (swaptype == 0) { \ + long t = *(long *)(a); \ + *(long *)(a) = *(long *)(b); \ + *(long *)(b) = t; \ + } else \ + swapfunc(a, b, es, swaptype) + +#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype) + +static inline char * +med3(char *a, char *b, char *c, int (*cmp)(const void *, const void *)) +{ + return cmp(a, b) < 0 ? + (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a)) + :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c)); +} + +static void +qsort(void *a, size_t n, size_t es, int (*cmp)(const void *, const void *)) +{ + char *pa, *pb, *pc, *pd, *pl, *pm, *pn; + int d, swaptype, swap_cnt; + int r; + +loop: + SWAPINIT(a, es); + swap_cnt = 0; + if (n < 7) { + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; pl > (char *)a && cmp(pl - es, pl) > 0; + pl -= es) + swap(pl, pl - es); + return; + } + pm = (char *)a + (n / 2) * es; + if (n > 7) { + pl = a; + pn = (char *)a + (n - 1) * es; + if (n > 40) { + d = (n / 8) * es; + pl = med3(pl, pl + d, pl + 2 * d, cmp); + pm = med3(pm - d, pm, pm + d, cmp); + pn = med3(pn - 2 * d, pn - d, pn, cmp); + } + pm = med3(pl, pm, pn, cmp); + } + swap(a, pm); + pa = pb = (char *)a + es; + + pc = pd = (char *)a + (n - 1) * es; + for (;;) { + while (pb <= pc && (r = cmp(pb, a)) <= 0) { + if (r == 0) { + swap_cnt = 1; + swap(pa, pb); + pa += es; + } + pb += es; + } + while (pb <= pc && (r = cmp(pc, a)) >= 0) { + if (r == 0) { + swap_cnt = 1; + swap(pc, pd); + pd -= es; + } + pc -= es; + } + if (pb > pc) + break; + swap(pb, pc); + swap_cnt = 1; + pb += es; + pc -= es; + } + if (swap_cnt == 0) { /* Switch to insertion sort */ + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; pl > (char *)a && cmp(pl - es, pl) > 0; + pl -= es) + swap(pl, pl - es); + return; + } + + pn = (char *)a + n * es; + r = min(pa - (char *)a, pb - pa); + vecswap(a, pb - r, r); + r = min((size_t)(pd - pc), pn - pd - es); + vecswap(pb, pn - r, r); + if ((size_t)(r = pb - pa) > es) + qsort(a, r / es, es, cmp); + if ((size_t)(r = pd - pc) > es) { + /* Iterate rather than recurse to save stack space */ + a = pn - r; + n = r / es; + goto loop; + } +/* qsort(pn - r, r / es, es, cmp); */ +} + +void +spl_qsort(void *array, size_t nm, size_t member_size, + int (*cmpf)(const void *, const void *)) +{ + qsort(array, nm, member_size, cmpf); +} diff --git a/module/os/macos/spl/spl-rwlock.c b/module/os/macos/spl/spl-rwlock.c new file mode 100644 index 000000000000..c6ed7dac5f20 --- /dev/null +++ b/module/os/macos/spl/spl-rwlock.c @@ -0,0 +1,402 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2008 MacZFS + * Copyright (C) 2013, 2020 Jorgen Lundman + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +static lck_attr_t *zfs_rwlock_attr = NULL; +static lck_grp_attr_t *zfs_rwlock_group_attr = NULL; +static lck_grp_t *zfs_rwlock_group = NULL; + +uint64_t zfs_active_rwlock = 0; + +#ifdef SPL_DEBUG_RWLOCK +#include +static list_t rwlock_list; +static kmutex_t rwlock_list_mutex; +struct leak { + list_node_t rwlock_leak_node; + +#define SPL_DEBUG_RWLOCK_MAXCHAR 32 + char location_file[SPL_DEBUG_RWLOCK_MAXCHAR]; + char location_function[SPL_DEBUG_RWLOCK_MAXCHAR]; + uint64_t location_line; + void *mp; + + uint64_t wdlist_locktime; // time lock was taken + char wdlist_file[32]; // storing holder + uint64_t wdlist_line; +}; + +#endif + +/* + * We run rwlock with DEBUG on for now, as it protects against + * uninitialised access etc, and almost no cost. + */ +#ifndef DEBUG +#define DEBUG +#endif + +#ifdef DEBUG +int +rw_isinit(krwlock_t *rwlp) +{ + if (rwlp->rw_pad != 0x012345678) + return (0); + return (1); +} +#endif + + +#ifdef SPL_DEBUG_RWLOCK +void +rw_initx(krwlock_t *rwlp, char *name, krw_type_t type, __unused void *arg, + const char *file, const char *fn, int line) +#else +void +rw_init(krwlock_t *rwlp, char *name, krw_type_t type, __unused void *arg) +#endif +{ + ASSERT(type != RW_DRIVER); + +#ifdef DEBUG + VERIFY3U(rwlp->rw_pad, !=, 0x012345678); +#endif + + lck_rw_init((lck_rw_t *)&rwlp->rw_lock[0], + zfs_rwlock_group, zfs_rwlock_attr); + rwlp->rw_owner = NULL; + rwlp->rw_readers = 0; +#ifdef DEBUG + rwlp->rw_pad = 0x012345678; +#endif + atomic_inc_64(&zfs_active_rwlock); + +#ifdef SPL_DEBUG_RWLOCK + struct leak *leak; + + MALLOC(leak, struct leak *, + sizeof (struct leak), M_TEMP, M_WAITOK); + + if (leak) { + bzero(leak, sizeof (struct leak)); + strlcpy(leak->location_file, file, SPL_DEBUG_RWLOCK_MAXCHAR); + strlcpy(leak->location_function, fn, SPL_DEBUG_RWLOCK_MAXCHAR); + leak->location_line = line; + leak->mp = rwlp; + + mutex_enter(&rwlock_list_mutex); + list_link_init(&leak->rwlock_leak_node); + list_insert_tail(&rwlock_list, leak); + rwlp->leak = leak; + mutex_exit(&rwlock_list_mutex); + } + leak->wdlist_locktime = 0; + leak->wdlist_file[0] = 0; + leak->wdlist_line = 0; +#endif +} + +void +rw_destroy(krwlock_t *rwlp) +{ +#ifdef DEBUG + VERIFY3U(rwlp->rw_pad, ==, 0x012345678); +#endif + + lck_rw_destroy((lck_rw_t *)&rwlp->rw_lock[0], zfs_rwlock_group); +#ifdef DEBUG + rwlp->rw_pad = 0x99; +#endif + atomic_dec_64(&zfs_active_rwlock); + ASSERT(rwlp->rw_owner == NULL); + ASSERT(rwlp->rw_readers == 0); + +#ifdef SPL_DEBUG_RWLOCK + if (rwlp->leak) { + struct leak *leak = (struct leak *)rwlp->leak; + mutex_enter(&rwlock_list_mutex); + list_remove(&rwlock_list, leak); + rwlp->leak = NULL; + mutex_exit(&rwlock_list_mutex); + FREE(leak, M_TEMP); + } +#endif +} + +void +rw_enter(krwlock_t *rwlp, krw_t rw) +{ +#ifdef DEBUG + if (rwlp->rw_pad != 0x012345678) + panic("rwlock %p not initialised\n", rwlp); +#endif + + if (rw == RW_READER) { + lck_rw_lock_shared((lck_rw_t *)&rwlp->rw_lock[0]); + atomic_inc_32((volatile uint32_t *)&rwlp->rw_readers); + ASSERT(rwlp->rw_owner == 0); + } else { + if (rwlp->rw_owner == current_thread()) + panic("rw_enter: locking against myself!"); + lck_rw_lock_exclusive((lck_rw_t *)&rwlp->rw_lock[0]); + ASSERT(rwlp->rw_owner == 0); + ASSERT(rwlp->rw_readers == 0); + rwlp->rw_owner = current_thread(); + } +} + +/* + * kernel private from osfmk/kern/locks.h + */ +extern boolean_t lck_rw_try_lock(lck_rw_t *lck, lck_rw_type_t lck_rw_type); + +int +rw_tryenter(krwlock_t *rwlp, krw_t rw) +{ + int held = 0; + +#ifdef DEBUG + if (rwlp->rw_pad != 0x012345678) + panic("rwlock %p not initialised\n", rwlp); +#endif + + if (rw == RW_READER) { + held = lck_rw_try_lock((lck_rw_t *)&rwlp->rw_lock[0], + LCK_RW_TYPE_SHARED); + if (held) + atomic_inc_32((volatile uint32_t *)&rwlp->rw_readers); + } else { + if (rwlp->rw_owner == current_thread()) + panic("rw_tryenter: locking against myself!"); + held = lck_rw_try_lock((lck_rw_t *)&rwlp->rw_lock[0], + LCK_RW_TYPE_EXCLUSIVE); + if (held) + rwlp->rw_owner = current_thread(); + } + + return (held); +} + +/* + * It appears a difference between Darwin's + * lck_rw_lock_shared_to_exclusive() and Solaris's rw_tryupgrade() and + * FreeBSD's sx_try_upgrade() is that on failure to upgrade, the prior + * held shared/reader lock is lost on Darwin, but retained on + * Solaris/FreeBSD. We could re-acquire the lock in this situation, + * but it enters a possibility of blocking, when tryupgrade is meant + * to be non-blocking. + * Also note that XNU's lck_rw_lock_shared_to_exclusive() is always + * blocking (when waiting on readers), which means we can not use it. + */ +int +rw_tryupgrade(krwlock_t *rwlp) +{ + int held = 0; + + if (rwlp->rw_owner == current_thread()) + panic("rw_enter: locking against myself!"); + + /* More readers than us? give up */ + if (rwlp->rw_readers != 1) + return (0); + + /* + * It is ON. We need to drop our READER lock, and try to + * grab the WRITER as quickly as possible. + */ + atomic_dec_32((volatile uint32_t *)&rwlp->rw_readers); + lck_rw_unlock_shared((lck_rw_t *)&rwlp->rw_lock[0]); + + /* Grab the WRITER lock */ + held = lck_rw_try_lock((lck_rw_t *)&rwlp->rw_lock[0], + LCK_RW_TYPE_EXCLUSIVE); + + if (held) { + /* Looks like we won */ + rwlp->rw_owner = current_thread(); + ASSERT(rwlp->rw_readers == 0); + return (1); + } + + /* + * The worst has happened, we failed to grab WRITE lock, either + * due to another WRITER lock, or, some READER came along. + * IllumOS implementation returns with the READER lock again + * so we need to grab it. + */ + rw_enter(rwlp, RW_READER); + return (0); + +} + +void +rw_exit(krwlock_t *rwlp) +{ + if (rwlp->rw_owner == current_thread()) { + rwlp->rw_owner = NULL; + ASSERT(rwlp->rw_readers == 0); + lck_rw_unlock_exclusive((lck_rw_t *)&rwlp->rw_lock[0]); + } else { + atomic_dec_32((volatile uint32_t *)&rwlp->rw_readers); + ASSERT(rwlp->rw_owner == 0); + lck_rw_unlock_shared((lck_rw_t *)&rwlp->rw_lock[0]); + } +} + +int +rw_read_held(krwlock_t *rwlp) +{ + return (rw_lock_held(rwlp) && rwlp->rw_owner == NULL); +} + +int +rw_lock_held(krwlock_t *rwlp) +{ + /* + * ### not sure about this one ### + */ + return (rwlp->rw_owner == current_thread() || rwlp->rw_readers > 0); +} + +int +rw_write_held(krwlock_t *rwlp) +{ + return (rwlp->rw_owner == current_thread()); +} + +void +rw_downgrade(krwlock_t *rwlp) +{ + if (rwlp->rw_owner != current_thread()) + panic("SPL: rw_downgrade not WRITE lock held\n"); + rwlp->rw_owner = NULL; + lck_rw_lock_exclusive_to_shared((lck_rw_t *)&rwlp->rw_lock[0]); + atomic_inc_32((volatile uint32_t *)&rwlp->rw_readers); +} + +int +spl_rwlock_init(void) +{ + zfs_rwlock_attr = lck_attr_alloc_init(); + zfs_rwlock_group_attr = lck_grp_attr_alloc_init(); + zfs_rwlock_group = lck_grp_alloc_init("zfs-rwlock", + zfs_rwlock_group_attr); + +#ifdef SPL_DEBUG_RWLOCK + list_create(&rwlock_list, sizeof (struct leak), + offsetof(struct leak, rwlock_leak_node)); + lck_mtx_init((lck_mtx_t *)&rwlock_list_mutex.m_lock, + zfs_rwlock_group, zfs_rwlock_attr); +#endif + + return (0); +} + +void +spl_rwlock_fini(void) +{ + +#ifdef SPL_DEBUG_RWLOCK + uint64_t total = 0; + printf("Dumping leaked rwlock allocations...\n"); + + mutex_enter(&rwlock_list_mutex); + while (1) { + struct leak *leak, *runner; + uint32_t found; + + leak = list_head(&rwlock_list); + + if (leak) { + list_remove(&rwlock_list, leak); + } + if (!leak) break; + + // Run through list and count up how many times this leak is + // found, removing entries as we go. + for (found = 1, runner = list_head(&rwlock_list); + runner; + runner = runner ? list_next(&rwlock_list, runner) : + list_head(&rwlock_list)) { + + if (strcmp(leak->location_file, runner->location_file) + == 0 && + strcmp(leak->location_function, + runner->location_function) == 0 && + leak->location_line == runner->location_line) { + // Same place + found++; + list_remove(&rwlock_list, runner); + FREE(runner, M_TEMP); + runner = NULL; + } // if same + + } // for all nodes + + printf(" rwlock %p : %s %s %llu : # leaks: %u\n", + leak->mp, + leak->location_file, + leak->location_function, + leak->location_line, + found); + + FREE(leak, M_TEMP); + total += found; + + } + mutex_exit(&rwlock_list_mutex); + printf("Dumped %llu leaked allocations.\n", total); + + lck_mtx_destroy((lck_mtx_t *)&rwlock_list_mutex.m_lock, + zfs_rwlock_group); + list_destroy(&rwlock_list); +#endif + + lck_grp_free(zfs_rwlock_group); + zfs_rwlock_group = NULL; + + lck_grp_attr_free(zfs_rwlock_group_attr); + zfs_rwlock_group_attr = NULL; + + lck_attr_free(zfs_rwlock_attr); + zfs_rwlock_attr = NULL; + + ASSERT3U(zfs_active_rwlock, ==, 0); +} diff --git a/module/os/macos/spl/spl-seg_kmem.c b/module/os/macos/spl/spl-seg_kmem.c new file mode 100644 index 000000000000..eb8e08749006 --- /dev/null +++ b/module/os/macos/spl/spl-seg_kmem.c @@ -0,0 +1,282 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#include + +#include +#include + +#include +#include +#include + +#include + +/* + * seg_kmem is the primary kernel memory segment driver. It + * maps the kernel heap [kernelheap, ekernelheap), module text, + * and all memory which was allocated before the VM was initialized + * into kas. + * + * Pages which belong to seg_kmem are hashed into &kvp vnode at + * an offset equal to (u_offset_t)virt_addr, and have p_lckcnt >= 1. + * They must never be paged out since segkmem_fault() is a no-op to + * prevent recursive faults. + * + * Currently, seg_kmem pages are sharelocked (p_sharelock == 1) on + * __x86 and are unlocked (p_sharelock == 0) on __sparc. Once __x86 + * supports relocation the #ifdef kludges can be removed. + * + * seg_kmem pages may be subject to relocation by page_relocate(), + * provided that the HAT supports it; if this is so, segkmem_reloc + * will be set to a nonzero value. All boot time allocated memory as + * well as static memory is considered off limits to relocation. + * Pages are "relocatable" if p_state does not have P_NORELOC set, so + * we request P_NORELOC pages for memory that isn't safe to relocate. + * + * The kernel heap is logically divided up into four pieces: + * + * heap32_arena is for allocations that require 32-bit absolute + * virtual addresses (e.g. code that uses 32-bit pointers/offsets). + * + * heap_core is for allocations that require 2GB *relative* + * offsets; in other words all memory from heap_core is within + * 2GB of all other memory from the same arena. This is a requirement + * of the addressing modes of some processors in supervisor code. + * + * heap_arena is the general heap arena. + * + * static_arena is the static memory arena. Allocations from it + * are not subject to relocation so it is safe to use the memory + * physical address as well as the virtual address (e.g. the VA to + * PA translations are static). Caches may import from static_arena; + * all other static memory allocations should use static_alloc_arena. + * + * On some platforms which have limited virtual address space, seg_kmem + * may share [kernelheap, ekernelheap) with seg_kp; if this is so, + * segkp_bitmap is non-NULL, and each bit represents a page of virtual + * address space which is actually seg_kp mapped. + */ + +/* + * Rough stubbed Port for XNU. + * + * Copyright (c) 2014 Brendon Humphrey (brendon.humphrey@mac.com) + */ + + +#ifdef _KERNEL +#define XNU_KERNEL_PRIVATE +#include +extern vm_map_t kernel_map; + +/* + * These extern prototypes has to be carefully checked against XNU source + * in case Apple changes them. They are not defined in the "allowed" parts + * of the kernel.framework + */ +typedef uint8_t vm_tag_t; + +/* + * Tag we use to identify memory we have allocated + * + * (VM_KERN_MEMORY_KEXT - mach_vm_statistics.h) + */ +#define SPL_TAG 6 + + + + +/* + * In kernel lowlevel form of malloc. + */ +void *IOMalloc(vm_size_t size); +void *IOMallocAligned(vm_size_t size, vm_offset_t alignment); + +/* + * Free memory + */ +void IOFree(void *address, vm_size_t size); +void IOFreeAligned(void * address, vm_size_t size); + +#endif /* _KERNEL */ + +typedef int page_t; + +void *segkmem_alloc(vmem_t *vmp, size_t size, int vmflag); +void segkmem_free(vmem_t *vmp, void *inaddr, size_t size); + +/* Total memory held allocated */ +uint64_t segkmem_total_mem_allocated = 0; + +/* primary kernel heap arena */ +vmem_t *heap_arena; + +/* qcaches abd */ +vmem_t *abd_arena; + +#ifdef _KERNEL +extern uint64_t total_memory; +uint64_t stat_osif_malloc_success = 0; +uint64_t stat_osif_free = 0; +uint64_t stat_osif_malloc_bytes = 0; +uint64_t stat_osif_free_bytes = 0; +#endif + +void * +osif_malloc(uint64_t size) +{ +#ifdef _KERNEL + // vm_offset_t tr = NULL; + void *tr = NULL; + kern_return_t kr = -1; + + // kern_return_t kr = kmem_alloc(kernel_map, &tr, size); + // tr = IOMalloc(size); + + /* + * align small allocations on PAGESIZE + * and larger ones on the enclosing power of two + * but drop to PAGESIZE for huge allocations + */ + uint64_t align = PAGESIZE; + if (size > PAGESIZE && !ISP2(size) && size < UINT32_MAX) { + uint64_t v = size; + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + align = v; + } else if (size > PAGESIZE && ISP2(size)) { + align = size; + } + + tr = IOMallocAligned(size, MAX(PAGESIZE, align)); + if (tr != NULL) + kr = KERN_SUCCESS; + + if (kr == KERN_SUCCESS) { + atomic_inc_64(&stat_osif_malloc_success); + atomic_add_64(&segkmem_total_mem_allocated, size); + atomic_add_64(&stat_osif_malloc_bytes, size); + return ((void *)tr); + } else { + // well, this can't really happen, kernel_memory_allocate + // would panic instead + return (NULL); + } +#else + return (malloc(size)); +#endif +} + +void +osif_free(void *buf, uint64_t size) +{ +#ifdef _KERNEL + IOFreeAligned(buf, size); + atomic_inc_64(&stat_osif_free); + atomic_sub_64(&segkmem_total_mem_allocated, size); + atomic_add_64(&stat_osif_free_bytes, size); +#else + free(buf); +#endif /* _KERNEL */ +} + +/* + * Configure vmem, such that the heap arena is fed, + * and drains to the kernel low level allocator. + */ +void +kernelheap_init() +{ + heap_arena = vmem_init("heap", NULL, 0, PAGESIZE, segkmem_alloc, + segkmem_free); +} + + +void +kernelheap_fini(void) +{ + vmem_fini(heap_arena); +} + +void * +segkmem_alloc(vmem_t *vmp, size_t size, int maybe_unmasked_vmflag) +{ + return (osif_malloc(size)); +} + +void +segkmem_free(vmem_t *vmp, void *inaddr, size_t size) +{ + osif_free(inaddr, size); + // since this is mainly called by spl_root_arena and free_arena, + // do we really want to wake up a waiter, just because we have + // transferred from one to the other? + // we already have vmem_add_a_gibibyte waking up waiters + // so specializing here seems wasteful + // (originally included in vmem_experiments) + // cv_signal(&vmp->vm_cv); +} + +/* + * OSX does not use separate heaps for the ZIO buffers, + * the ZFS code is structured such that the zio caches will + * fallback to using the kmem_default arena same + * as all the other caches. + */ +// smd: we nevertheless plumb in an arena with heap as parent, so that +// we can track stats and maintain the VM_ / qc settings differently +void +segkmem_abd_init() +{ + /* + * OpenZFS does not segregate the abd kmem cache out of the general + * heap, leading to large numbers of short-lived slabs exchanged + * between the kmem cache and it's parent. XNU absorbs this with a + * qcache, following its history of absorbing the pre-ABD zio file and + * metadata caches being qcached (which raises the exchanges with the + * general heap from PAGESIZE to 256k). + */ + + extern vmem_t *spl_heap_arena; + + abd_arena = vmem_create("abd_cache", NULL, 0, + PAGESIZE, vmem_alloc, vmem_free, spl_heap_arena, + 131072, VM_SLEEP | VMC_NO_QCACHE | VM_BESTFIT); + + ASSERT(abd_arena != NULL); +} + +void +segkmem_abd_fini(void) +{ + if (abd_arena) { + vmem_destroy(abd_arena); + } +} diff --git a/module/os/macos/spl/spl-taskq.c b/module/os/macos/spl/spl-taskq.c new file mode 100644 index 000000000000..4c1788891553 --- /dev/null +++ b/module/os/macos/spl/spl-taskq.c @@ -0,0 +1,2902 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * Copyright (C) 2015, 2020 Jorgen Lundman + */ + +/* + * Kernel task queues: general-purpose asynchronous task scheduling. + * + * A common problem in kernel programming is the need to schedule tasks + * to be performed later, by another thread. There are several reasons + * you may want or need to do this: + * + * (1) The task isn't time-critical, but your current code path is. + * + * (2) The task may require grabbing locks that you already hold. + * + * (3) The task may need to block (e.g. to wait for memory), but you + * cannot block in your current context. + * + * (4) Your code path can't complete because of some condition, but you can't + * sleep or fail, so you queue the task for later execution when condition + * disappears. + * + * (5) You just want a simple way to launch multiple tasks in parallel. + * + * Task queues provide such a facility. In its simplest form (used when + * performance is not a critical consideration) a task queue consists of a + * single list of tasks, together with one or more threads to service the + * list. There are some cases when this simple queue is not sufficient: + * + * (1) The task queues are very hot and there is a need to avoid data and lock + * contention over global resources. + * + * (2) Some tasks may depend on other tasks to complete, so they can't be put in + * the same list managed by the same thread. + * + * (3) Some tasks may block for a long time, and this should not block other + * tasks in the queue. + * + * To provide useful service in such cases we define a "dynamic task queue" + * which has an individual thread for each of the tasks. These threads are + * dynamically created as they are needed and destroyed when they are not in + * use. The API for managing task pools is the same as for managing task queues + * with the exception of a taskq creation flag TASKQ_DYNAMIC which tells that + * dynamic task pool behavior is desired. + * + * Dynamic task queues may also place tasks in the normal queue (called "backing + * queue") when task pool runs out of resources. Users of task queues may + * disallow such queued scheduling by specifying TQ_NOQUEUE in the dispatch + * flags. + * + * The backing task queue is also used for scheduling internal tasks needed for + * dynamic task queue maintenance. + * + * INTERFACES ================================================================== + * + * taskq_t *taskq_create(name, nthreads, pri, minalloc, maxall, flags); + * + * Create a taskq with specified properties. + * Possible 'flags': + * + * TASKQ_DYNAMIC: Create task pool for task management. If this flag is + * specified, 'nthreads' specifies the maximum number of threads in + * the task queue. Task execution order for dynamic task queues is + * not predictable. + * + * If this flag is not specified (default case) a + * single-list task queue is created with 'nthreads' threads + * servicing it. Entries in this queue are managed by + * taskq_ent_alloc() and taskq_ent_free() which try to keep the + * task population between 'minalloc' and 'maxalloc', but the + * latter limit is only advisory for TQ_SLEEP dispatches and the + * former limit is only advisory for TQ_NOALLOC dispatches. If + * TASKQ_PREPOPULATE is set in 'flags', the taskq will be + * prepopulated with 'minalloc' task structures. + * + * Since non-DYNAMIC taskqs are queues, tasks are guaranteed to be + * executed in the order they are scheduled if nthreads == 1. + * If nthreads > 1, task execution order is not predictable. + * + * TASKQ_PREPOPULATE: Prepopulate task queue with threads. + * Also prepopulate the task queue with 'minalloc' task structures. + * + * TASKQ_THREADS_CPU_PCT: This flag specifies that 'nthreads' should be + * interpreted as a percentage of the # of online CPUs on the + * system. The taskq subsystem will automatically adjust the + * number of threads in the taskq in response to CPU online + * and offline events, to keep the ratio. nthreads must be in + * the range [0,100]. + * + * The calculation used is: + * + * MAX((ncpus_online * percentage)/100, 1) + * + * This flag is not supported for DYNAMIC task queues. + * This flag is not compatible with TASKQ_CPR_SAFE. + * + * TASKQ_CPR_SAFE: This flag specifies that users of the task queue will + * use their own protocol for handling CPR issues. This flag is not + * supported for DYNAMIC task queues. This flag is not compatible + * with TASKQ_THREADS_CPU_PCT. + * + * The 'pri' field specifies the default priority for the threads that + * service all scheduled tasks. + * + * taskq_t *taskq_create_instance(name, instance, nthreads, pri, minalloc, + * maxall, flags); + * + * Like taskq_create(), but takes an instance number (or -1 to indicate + * no instance). + * + * taskq_t *taskq_create_proc(name, nthreads, pri, minalloc, maxall, proc, + * flags); + * + * Like taskq_create(), but creates the taskq threads in the specified + * system process. If proc != &p0, this must be called from a thread + * in that process. + * + * taskq_t *taskq_create_sysdc(name, nthreads, minalloc, maxall, proc, + * dc, flags); + * + * Like taskq_create_proc(), but the taskq threads will use the + * System Duty Cycle (SDC) scheduling class with a duty cycle of dc. + * + * void taskq_destroy(tap): + * + * Waits for any scheduled tasks to complete, then destroys the taskq. + * Caller should guarantee that no new tasks are scheduled in the closing + * taskq. + * + * taskqid_t taskq_dispatch(tq, func, arg, flags): + * + * Dispatches the task "func(arg)" to taskq. The 'flags' indicates whether + * the caller is willing to block for memory. The function returns an + * opaque value which is zero iff dispatch fails. If flags is TQ_NOSLEEP + * or TQ_NOALLOC and the task can't be dispatched, taskq_dispatch() fails + * and returns (taskqid_t)0. + * + * ASSUMES: func != NULL. + * + * Possible flags: + * TQ_NOSLEEP: Do not wait for resources; may fail. + * + * TQ_NOALLOC: Do not allocate memory; may fail. May only be used with + * non-dynamic task queues. + * + * TQ_NOQUEUE: Do not enqueue a task if it can't dispatch it due to + * lack of available resources and fail. If this flag is not + * set, and the task pool is exhausted, the task may be scheduled + * in the backing queue. This flag may ONLY be used with dynamic + * task queues. + * + * NOTE: This flag should always be used when a task queue is used + * for tasks that may depend on each other for completion. + * Enqueueing dependent tasks may create deadlocks. + * + * TQ_SLEEP: May block waiting for resources. May still fail for + * dynamic task queues if TQ_NOQUEUE is also specified, otherwise + * always succeed. + * + * TQ_FRONT: Puts the new task at the front of the queue. Be careful. + * + * NOTE: Dynamic task queues are much more likely to fail in + * taskq_dispatch() (especially if TQ_NOQUEUE was specified), so it + * is important to have backup strategies handling such failures. + * + * void taskq_dispatch_ent(tq, func, arg, flags, tqent) + * + * This is a light-weight form of taskq_dispatch(), that uses a + * preallocated taskq_ent_t structure for scheduling. As a + * result, it does not perform allocations and cannot ever fail. + * Note especially that it cannot be used with TASKQ_DYNAMIC + * taskqs. The memory for the tqent must not be modified or used + * until the function (func) is called. (However, func itself + * may safely modify or free this memory, once it is called.) + * Note that the taskq framework will NOT free this memory. + * + * void taskq_wait(tq): + * + * Waits for all previously scheduled tasks to complete. + * + * NOTE: It does not stop any new task dispatches. + * Do NOT call taskq_wait() from a task: it will cause deadlock. + * + * void taskq_suspend(tq) + * + * Suspend all task execution. Tasks already scheduled for a dynamic task + * queue will still be executed, but all new scheduled tasks will be + * suspended until taskq_resume() is called. + * + * int taskq_suspended(tq) + * + * Returns 1 if taskq is suspended and 0 otherwise. It is intended to + * ASSERT that the task queue is suspended. + * + * void taskq_resume(tq) + * + * Resume task queue execution. + * + * int taskq_member(tq, thread) + * + * Returns 1 if 'thread' belongs to taskq 'tq' and 0 otherwise. The + * intended use is to ASSERT that a given function is called in taskq + * context only. + * + * system_taskq + * + * Global system-wide dynamic task queue for common uses. It may be used by + * any subsystem that needs to schedule tasks and does not need to manage + * its own task queues. It is initialized quite early during system boot. + * + * IMPLEMENTATION ============================================================== + * + * This is schematic representation of the task queue structures. + * + * taskq: + * +-------------+ + * | tq_lock | +---< taskq_ent_free() + * +-------------+ | + * |... | | tqent: tqent: + * +-------------+ | +------------+ +------------+ + * | tq_freelist |-->| tqent_next |--> ... ->| tqent_next | + * +-------------+ +------------+ +------------+ + * |... | | ... | | ... | + * +-------------+ +------------+ +------------+ + * | tq_task | | + * | | +-------------->taskq_ent_alloc() + * +--------------------------------------------------------------------------+ + * | | | tqent tqent | + * | +---------------------+ +--> +------------+ +--> +------------+ | + * | | ... | | | func, arg | | | func, arg | | + * +>+---------------------+ <---|-+ +------------+ <---|-+ +------------+ | + * | tq_taskq.tqent_next | ----+ | | tqent_next | --->+ | | tqent_next |--+ + * +---------------------+ | +------------+ ^ | +------------+ + * +-| tq_task.tqent_prev | +--| tqent_prev | | +--| tqent_prev | ^ + * | +---------------------+ +------------+ | +------------+ | + * | |... | | ... | | | ... | | + * | +---------------------+ +------------+ | +------------+ | + * | ^ | | + * | | | | + * +--------------------------------------+--------------+ TQ_APPEND() -+ + * | | | + * |... | taskq_thread()-----+ + * +-------------+ + * | tq_buckets |--+-------> [ NULL ] (for regular task queues) + * +-------------+ | + * | DYNAMIC TASK QUEUES: + * | + * +-> taskq_bucket[nCPU] taskq_bucket_dispatch() + * +-------------------+ ^ + * +--->| tqbucket_lock | | + * | +-------------------+ +--------+ +--------+ + * | | tqbucket_freelist |-->| tqent |-->...| tqent | ^ + * | +-------------------+<--+--------+<--...+--------+ | + * | | ... | | thread | | thread | | + * | +-------------------+ +--------+ +--------+ | + * | +-------------------+ | + * taskq_dispatch()--+--->| tqbucket_lock | TQ_APPEND()------+ + * TQ_HASH() | +-------------------+ +--------+ +--------+ + * | | tqbucket_freelist |-->| tqent |-->...| tqent | + * | +-------------------+<--+--------+<--...+--------+ + * | | ... | | thread | | thread | + * | +-------------------+ +--------+ +--------+ + * +---> ... + * + * + * Task queues use tq_task field to link new entry in the queue. The queue is a + * circular doubly-linked list. Entries are put in the end of the list with + * TQ_APPEND() and processed from the front of the list by taskq_thread() in + * FIFO order. Task queue entries are cached in the free list managed by + * taskq_ent_alloc() and taskq_ent_free() functions. + * + * All threads used by task queues mark t_taskq field of the thread to + * point to the task queue. + * + * Taskq Thread Management ----------------------------------------------------- + * + * Taskq's non-dynamic threads are managed with several variables and flags: + * + * * tq_nthreads - The number of threads in taskq_thread() for the + * taskq. + * + * * tq_active - The number of threads not waiting on a CV in + * taskq_thread(); includes newly created threads + * not yet counted in tq_nthreads. + * + * * tq_nthreads_target + * - The number of threads desired for the taskq. + * + * * tq_flags & TASKQ_CHANGING + * - Indicates that tq_nthreads != tq_nthreads_target. + * + * * tq_flags & TASKQ_THREAD_CREATED + * - Indicates that a thread is being created in the taskq. + * + * During creation, tq_nthreads and tq_active are set to 0, and + * tq_nthreads_target is set to the number of threads desired. The + * TASKQ_CHANGING flag is set, and taskq_thread_create() is called to + * create the first thread. taskq_thread_create() increments tq_active, + * sets TASKQ_THREAD_CREATED, and creates the new thread. + * + * Each thread starts in taskq_thread(), clears the TASKQ_THREAD_CREATED + * flag, and increments tq_nthreads. It stores the new value of + * tq_nthreads as its "thread_id", and stores its thread pointer in the + * tq_threadlist at the (thread_id - 1). We keep the thread_id space + * densely packed by requiring that only the largest thread_id can exit during + * normal adjustment. The exception is during the destruction of the + * taskq; once tq_nthreads_target is set to zero, no new threads will be created + * for the taskq queue, so every thread can exit without any ordering being + * necessary. + * + * Threads will only process work if their thread id is <= tq_nthreads_target. + * + * When TASKQ_CHANGING is set, threads will check the current thread target + * whenever they wake up, and do whatever they can to apply its effects. + * + * TASKQ_THREAD_CPU_PCT -------------------------------------------------------- + * + * When a taskq is created with TASKQ_THREAD_CPU_PCT, we store their requested + * percentage in tq_threads_ncpus_pct, start them off with the correct thread + * target, and add them to the taskq_cpupct_list for later adjustment. + * + * We register taskq_cpu_setup() to be called whenever a CPU changes state. It + * walks the list of TASKQ_THREAD_CPU_PCT taskqs, adjusts their nthread_target + * if need be, and wakes up all of the threads to process the change. + * + * Dynamic Task Queues Implementation ------------------------------------------ + * + * For a dynamic task queues there is a 1-to-1 mapping between a thread and + * taskq_ent_structure. Each entry is serviced by its own thread and each thread + * is controlled by a single entry. + * + * Entries are distributed over a set of buckets. To avoid using modulo + * arithmetics the number of buckets is 2^n and is determined as the nearest + * power of two roundown of the number of CPUs in the system. Tunable + * variable 'taskq_maxbuckets' limits the maximum number of buckets. Each entry + * is attached to a bucket for its lifetime and can't migrate to other buckets. + * + * Entries that have scheduled tasks are not placed in any list. The dispatch + * function sets their "func" and "arg" fields and signals the corresponding + * thread to execute the task. Once the thread executes the task it clears the + * "func" field and places an entry on the bucket cache of free entries pointed + * by "tqbucket_freelist" field. ALL entries on the free list should have "func" + * field equal to NULL. The free list is a circular doubly-linked list identical + * in structure to the tq_task list above, but entries are taken from it in LIFO + * order - the last freed entry is the first to be allocated. The + * taskq_bucket_dispatch() function gets the most recently used entry from the + * free list, sets its "func" and "arg" fields and signals a worker thread. + * + * After executing each task a per-entry thread taskq_d_thread() places its + * entry on the bucket free list and goes to a timed sleep. If it wakes up + * without getting new task it removes the entry from the free list and destroys + * itself. The thread sleep time is controlled by a tunable variable + * `taskq_thread_timeout'. + * + * There are various statistics kept in the bucket which allows for later + * analysis of taskq usage patterns. Also, a global copy of taskq creation and + * death statistics is kept in the global taskq data structure. Since thread + * creation and death happen rarely, updating such global data does not present + * a performance problem. + * + * NOTE: Threads are not bound to any CPU and there is absolutely no association + * between the bucket and actual thread CPU, so buckets are used only to + * split resources and reduce resource contention. Having threads attached + * to the CPU denoted by a bucket may reduce number of times the job + * switches between CPUs. + * + * Current algorithm creates a thread whenever a bucket has no free + * entries. It would be nice to know how many threads are in the running + * state and don't create threads if all CPUs are busy with existing + * tasks, but it is unclear how such strategy can be implemented. + * + * Currently buckets are created statically as an array attached to task + * queue. On some system with nCPUs < max_ncpus it may waste system + * memory. One solution may be allocation of buckets when they are first + * touched, but it is not clear how useful it is. + * + * SUSPEND/RESUME implementation ----------------------------------------------- + * + * Before executing a task taskq_thread() (executing non-dynamic task + * queues) obtains taskq's thread lock as a reader. The taskq_suspend() + * function gets the same lock as a writer blocking all non-dynamic task + * execution. The taskq_resume() function releases the lock allowing + * taskq_thread to continue execution. + * + * For dynamic task queues, each bucket is marked as TQBUCKET_SUSPEND by + * taskq_suspend() function. After that taskq_bucket_dispatch() always + * fails, so that taskq_dispatch() will either enqueue tasks for a + * suspended backing queue or fail if TQ_NOQUEUE is specified in dispatch + * flags. + * + * NOTE: taskq_suspend() does not immediately block any tasks already + * scheduled for dynamic task queues. It only suspends new tasks + * scheduled after taskq_suspend() was called. + * + * taskq_member() function works by comparing a thread t_taskq pointer with + * the passed thread pointer. + * + * LOCKS and LOCK Hierarchy ---------------------------------------------------- + * + * There are three locks used in task queues: + * + * 1) The taskq_t's tq_lock, protecting global task queue state. + * + * 2) Each per-CPU bucket has a lock for bucket management. + * + * 3) The global taskq_cpupct_lock, which protects the list of + * TASKQ_THREADS_CPU_PCT taskqs. + * + * If both (1) and (2) are needed, tq_lock should be taken *after* the bucket + * lock. + * + * If both (1) and (3) are needed, tq_lock should be taken *after* + * taskq_cpupct_lock. + * + * DEBUG FACILITIES ------------------------------------------------------------ + * + * For DEBUG kernels it is possible to induce random failures to + * taskq_dispatch() function when it is given TQ_NOSLEEP argument. The value of + * taskq_dmtbf and taskq_smtbf tunables control the mean time between induced + * failures for dynamic and static task queues respectively. + * + * Setting TASKQ_STATISTIC to 0 will disable per-bucket statistics. + * + * TUNABLES -------------------------------------------------------------------- + * + * system_taskq_size - Size of the global system_taskq. + * This value is multiplied by nCPUs to determine + * actual size. + * Default value: 64 + * + * taskq_minimum_nthreads_max + * - Minimum size of the thread list for a taskq. + * Useful for testing different thread pool + * sizes by overwriting tq_nthreads_target. + * + * taskq_thread_timeout - Maximum idle time for taskq_d_thread() + * Default value: 5 minutes + * + * taskq_maxbuckets - Maximum number of buckets in any task queue + * Default value: 128 + * + * taskq_search_depth - Maximum # of buckets searched for a free entry + * Default value: 4 + * + * taskq_dmtbf - Mean time between induced dispatch failures + * for dynamic task queues. + * Default value: UINT_MAX (no induced failures) + * + * taskq_smtbf - Mean time between induced dispatch failures + * for static task queues. + * Default value: UINT_MAX (no induced failures) + * + * CONDITIONAL compilation ----------------------------------------------------- + * + * TASKQ_STATISTIC - If set will enable bucket statistic (default). + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* For throttlefree */ +#include +#include +#include +#ifdef __APPLE__ +#include +#include +#endif +#include + +static kmem_cache_t *taskq_ent_cache, *taskq_cache; + +static uint_t taskq_tsd; + +/* + * Pseudo instance numbers for taskqs without explicitly provided instance. + */ +static vmem_t *taskq_id_arena; + +/* Global system task queue for common use */ +taskq_t *system_taskq = NULL; +taskq_t *system_delay_taskq = NULL; + +/* + * Maximum number of entries in global system taskq is + * system_taskq_size * max_ncpus + */ +#ifdef __APPLE__ +#define SYSTEM_TASKQ_SIZE 128 +#else +#define SYSTEM_TASKQ_SIZE 64 +#endif +int system_taskq_size = SYSTEM_TASKQ_SIZE; + +/* + * Minimum size for tq_nthreads_max; useful for those who want to play around + * with increasing a taskq's tq_nthreads_target. + */ +int taskq_minimum_nthreads_max = 1; + +/* + * We want to ensure that when taskq_create() returns, there is at least + * one thread ready to handle requests. To guarantee this, we have to wait + * for the second thread, since the first one cannot process requests until + * the second thread has been created. + */ +#define TASKQ_CREATE_ACTIVE_THREADS 2 + +/* Maximum percentage allowed for TASKQ_THREADS_CPU_PCT */ +#define TASKQ_CPUPCT_MAX_PERCENT 1000 +int taskq_cpupct_max_percent = TASKQ_CPUPCT_MAX_PERCENT; + +/* + * Dynamic task queue threads that don't get any work within + * taskq_thread_timeout destroy themselves + */ +#define TASKQ_THREAD_TIMEOUT (60 * 5) +int taskq_thread_timeout = TASKQ_THREAD_TIMEOUT; + +#define TASKQ_MAXBUCKETS 128 +int taskq_maxbuckets = TASKQ_MAXBUCKETS; + +/* + * When a bucket has no available entries another buckets are tried. + * taskq_search_depth parameter limits the amount of buckets that we search + * before failing. This is mostly useful in systems with many CPUs where we may + * spend too much time scanning busy buckets. + */ +#define TASKQ_SEARCH_DEPTH 4 +int taskq_search_depth = TASKQ_SEARCH_DEPTH; + +/* + * Hashing function: mix various bits of x. May be pretty much anything. + */ +#define TQ_HASH(x) ((x) ^ ((x) >> 11) ^ ((x) >> 17) ^ ((x) ^ 27)) + +/* + * We do not create any new threads when the system is low on memory and start + * throttling memory allocations. The following macro tries to estimate such + * condition. + */ +#ifdef __APPLE__ +#define ENOUGH_MEMORY() (!spl_vm_pool_low()) +#else +#define ENOUGH_MEMORY() (freemem > throttlefree) +#endif + +/* + * Static functions. + */ +static taskq_t *taskq_create_common(const char *, int, int, pri_t, int, + int, proc_t *, uint_t, uint_t); +static void taskq_thread(void *); +static void taskq_d_thread(taskq_ent_t *); +static void taskq_bucket_extend(void *); +static int taskq_constructor(void *, void *, int); +static void taskq_destructor(void *, void *); +static int taskq_ent_constructor(void *, void *, int); +static void taskq_ent_destructor(void *, void *); +static taskq_ent_t *taskq_ent_alloc(taskq_t *, int); +static void taskq_ent_free(taskq_t *, taskq_ent_t *); +static int taskq_ent_exists(taskq_t *, task_func_t, void *); +static taskq_ent_t *taskq_bucket_dispatch(taskq_bucket_t *, task_func_t, + void *); + +/* + * Task queues kstats. + */ +struct taskq_kstat { + kstat_named_t tq_pid; + kstat_named_t tq_tasks; + kstat_named_t tq_executed; + kstat_named_t tq_maxtasks; + kstat_named_t tq_totaltime; + kstat_named_t tq_nalloc; + kstat_named_t tq_nactive; + kstat_named_t tq_pri; + kstat_named_t tq_nthreads; +} taskq_kstat = { + { "pid", KSTAT_DATA_UINT64 }, + { "tasks", KSTAT_DATA_UINT64 }, + { "executed", KSTAT_DATA_UINT64 }, + { "maxtasks", KSTAT_DATA_UINT64 }, + { "totaltime", KSTAT_DATA_UINT64 }, + { "nactive", KSTAT_DATA_UINT64 }, + { "nalloc", KSTAT_DATA_UINT64 }, + { "priority", KSTAT_DATA_UINT64 }, + { "threads", KSTAT_DATA_UINT64 }, +}; + +struct taskq_d_kstat { + kstat_named_t tqd_pri; + kstat_named_t tqd_btasks; + kstat_named_t tqd_bexecuted; + kstat_named_t tqd_bmaxtasks; + kstat_named_t tqd_bnalloc; + kstat_named_t tqd_bnactive; + kstat_named_t tqd_btotaltime; + kstat_named_t tqd_hits; + kstat_named_t tqd_misses; + kstat_named_t tqd_overflows; + kstat_named_t tqd_tcreates; + kstat_named_t tqd_tdeaths; + kstat_named_t tqd_maxthreads; + kstat_named_t tqd_nomem; + kstat_named_t tqd_disptcreates; + kstat_named_t tqd_totaltime; + kstat_named_t tqd_nalloc; + kstat_named_t tqd_nfree; +} taskq_d_kstat = { + { "priority", KSTAT_DATA_UINT64 }, + { "btasks", KSTAT_DATA_UINT64 }, + { "bexecuted", KSTAT_DATA_UINT64 }, + { "bmaxtasks", KSTAT_DATA_UINT64 }, + { "bnalloc", KSTAT_DATA_UINT64 }, + { "bnactive", KSTAT_DATA_UINT64 }, + { "btotaltime", KSTAT_DATA_UINT64 }, + { "hits", KSTAT_DATA_UINT64 }, + { "misses", KSTAT_DATA_UINT64 }, + { "overflows", KSTAT_DATA_UINT64 }, + { "tcreates", KSTAT_DATA_UINT64 }, + { "tdeaths", KSTAT_DATA_UINT64 }, + { "maxthreads", KSTAT_DATA_UINT64 }, + { "nomem", KSTAT_DATA_UINT64 }, + { "disptcreates", KSTAT_DATA_UINT64 }, + { "totaltime", KSTAT_DATA_UINT64 }, + { "nalloc", KSTAT_DATA_UINT64 }, + { "nfree", KSTAT_DATA_UINT64 }, +}; + +static kmutex_t taskq_kstat_lock; +static kmutex_t taskq_d_kstat_lock; +static int taskq_kstat_update(kstat_t *, int); +static int taskq_d_kstat_update(kstat_t *, int); + +/* + * List of all TASKQ_THREADS_CPU_PCT taskqs. + */ +static list_t taskq_cpupct_list; /* protected by cpu_lock */ + +/* + * Collect per-bucket statistic when TASKQ_STATISTIC is defined. + */ +#define TASKQ_STATISTIC 1 + +#if TASKQ_STATISTIC +#define TQ_STAT(b, x) b->tqbucket_stat.x++ +#else +#define TQ_STAT(b, x) +#endif + +/* + * Random fault injection. + */ +uint_t taskq_random; +uint_t taskq_dmtbf = UINT_MAX; /* mean time between injected failures */ +uint_t taskq_smtbf = UINT_MAX; /* mean time between injected failures */ + +/* + * TQ_NOSLEEP dispatches on dynamic task queues are always allowed to fail. + * + * TQ_NOSLEEP dispatches on static task queues can't arbitrarily fail because + * they could prepopulate the cache and make sure that they do not use more + * then minalloc entries. So, fault injection in this case insures that + * either TASKQ_PREPOPULATE is not set or there are more entries allocated + * than is specified by minalloc. TQ_NOALLOC dispatches are always allowed + * to fail, but for simplicity we treat them identically to TQ_NOSLEEP + * dispatches. + */ +#ifdef DEBUG +#define TASKQ_D_RANDOM_DISPATCH_FAILURE(tq, flag) \ + taskq_random = (taskq_random * 2416 + 374441) % 1771875;\ + if ((flag & TQ_NOSLEEP) && \ + taskq_random < 1771875 / taskq_dmtbf) { \ + return (0); \ + } + +#define TASKQ_S_RANDOM_DISPATCH_FAILURE(tq, flag) \ + taskq_random = (taskq_random * 2416 + 374441) % 1771875;\ + if ((flag & (TQ_NOSLEEP | TQ_NOALLOC)) && \ + (!(tq->tq_flags & TASKQ_PREPOPULATE) || \ + (tq->tq_nalloc > tq->tq_minalloc)) && \ + (taskq_random < (1771875 / taskq_smtbf))) { \ + mutex_exit(&tq->tq_lock); \ + return (0); \ + } +#else +#define TASKQ_S_RANDOM_DISPATCH_FAILURE(tq, flag) +#define TASKQ_D_RANDOM_DISPATCH_FAILURE(tq, flag) +#endif + +#define IS_EMPTY(l) (((l).tqent_prev == (l).tqent_next) && \ + ((l).tqent_prev == &(l))) + +/* + * Append `tqe' in the end of the doubly-linked list denoted by l. + */ +#define TQ_APPEND(l, tqe) { \ + tqe->tqent_next = &l; \ + tqe->tqent_prev = l.tqent_prev; \ + tqe->tqent_next->tqent_prev = tqe; \ + tqe->tqent_prev->tqent_next = tqe; \ +} +/* + * Prepend 'tqe' to the beginning of l + */ +#define TQ_PREPEND(l, tqe) { \ + tqe->tqent_next = l.tqent_next; \ + tqe->tqent_prev = &l; \ + tqe->tqent_next->tqent_prev = tqe; \ + tqe->tqent_prev->tqent_next = tqe; \ +} + +/* + * Schedule a task specified by func and arg into the task queue entry tqe. + */ +#define TQ_DO_ENQUEUE(tq, tqe, func, arg, front) { \ + ASSERT(MUTEX_HELD(&tq->tq_lock)); \ + _NOTE(CONSTCOND) \ + if (front) { \ + TQ_PREPEND(tq->tq_task, tqe); \ + } else { \ + TQ_APPEND(tq->tq_task, tqe); \ + } \ + tqe->tqent_func = (func); \ + tqe->tqent_arg = (arg); \ + tq->tq_tasks++; \ + if (tq->tq_tasks - tq->tq_executed > tq->tq_maxtasks) \ + tq->tq_maxtasks = tq->tq_tasks - tq->tq_executed; \ + cv_signal(&tq->tq_dispatch_cv); \ + DTRACE_PROBE2(taskq__enqueue, taskq_t *, tq, taskq_ent_t *, tqe); \ +} + +#define TQ_ENQUEUE(tq, tqe, func, arg) \ + TQ_DO_ENQUEUE(tq, tqe, func, arg, 0) + +#define TQ_ENQUEUE_FRONT(tq, tqe, func, arg) \ + TQ_DO_ENQUEUE(tq, tqe, func, arg, 1) + +/* + * Do-nothing task which may be used to prepopulate thread caches. + */ +/*ARGSUSED*/ +void +nulltask(void *unused) +{ +} + +/*ARGSUSED*/ +static int +taskq_constructor(void *buf, void *cdrarg, int kmflags) +{ + taskq_t *tq = buf; + + bzero(tq, sizeof (taskq_t)); + + mutex_init(&tq->tq_lock, NULL, MUTEX_DEFAULT, NULL); + rw_init(&tq->tq_threadlock, NULL, RW_DEFAULT, NULL); + cv_init(&tq->tq_dispatch_cv, NULL, CV_DEFAULT, NULL); + cv_init(&tq->tq_exit_cv, NULL, CV_DEFAULT, NULL); + cv_init(&tq->tq_wait_cv, NULL, CV_DEFAULT, NULL); + cv_init(&tq->tq_maxalloc_cv, NULL, CV_DEFAULT, NULL); + + tq->tq_task.tqent_next = &tq->tq_task; + tq->tq_task.tqent_prev = &tq->tq_task; + + return (0); +} + +/*ARGSUSED*/ +static void +taskq_destructor(void *buf, void *cdrarg) +{ + taskq_t *tq = buf; + + ASSERT(tq->tq_nthreads == 0); + ASSERT(tq->tq_buckets == NULL); + ASSERT(tq->tq_tcreates == 0); + ASSERT(tq->tq_tdeaths == 0); + + mutex_destroy(&tq->tq_lock); + rw_destroy(&tq->tq_threadlock); + cv_destroy(&tq->tq_dispatch_cv); + cv_destroy(&tq->tq_exit_cv); + cv_destroy(&tq->tq_wait_cv); + cv_destroy(&tq->tq_maxalloc_cv); +} + +/*ARGSUSED*/ +static int +taskq_ent_constructor(void *buf, void *cdrarg, int kmflags) +{ + taskq_ent_t *tqe = buf; + + tqe->tqent_thread = NULL; + cv_init(&tqe->tqent_cv, NULL, CV_DEFAULT, NULL); +#ifdef __APPLE__ + /* Simulate TS_STOPPED */ + mutex_init(&tqe->tqent_thread_lock, NULL, MUTEX_DEFAULT, NULL); + cv_init(&tqe->tqent_thread_cv, NULL, CV_DEFAULT, NULL); +#endif /* __APPLE__ */ + return (0); +} + +/*ARGSUSED*/ +static void +taskq_ent_destructor(void *buf, void *cdrarg) +{ + taskq_ent_t *tqe = buf; + + ASSERT(tqe->tqent_thread == NULL); + cv_destroy(&tqe->tqent_cv); +#ifdef __APPLE__ + /* See comment in taskq_d_thread(). */ + mutex_destroy(&tqe->tqent_thread_lock); + cv_destroy(&tqe->tqent_thread_cv); +#endif /* __APPLE__ */ +} + + +struct tqdelay { + // list + list_node_t tqd_listnode; + // time (list sorted on this) + clock_t tqd_time; + // func + taskq_t *tqd_taskq; + task_func_t *tqd_func; + void *tqd_arg; + uint_t tqd_tqflags; +}; + +typedef struct tqdelay tqdelay_t; + +static list_t tqd_list; +static kmutex_t tqd_delay_lock; +static kcondvar_t tqd_delay_cv; +static int tqd_do_exit = 0; + +static void +taskq_delay_dispatcher_thread(void *notused) +{ + callb_cpr_t cpr; + + dprintf("%s: starting\n", __func__); + CALLB_CPR_INIT(&cpr, &tqd_delay_lock, callb_generic_cpr, FTAG); + + mutex_enter(&tqd_delay_lock); + while (tqd_do_exit == 0) { + int didsleep = 0; + tqdelay_t *tqdnode; + CALLB_CPR_SAFE_BEGIN(&cpr); + + /* + * If list is empty, just sleep until signal, + * otherwise, sleep on list_head (lowest in the list) + */ + tqdnode = list_head(&tqd_list); + + if (tqdnode == NULL) + (void) cv_wait(&tqd_delay_cv, &tqd_delay_lock); + else + didsleep = cv_timedwait(&tqd_delay_cv, + &tqd_delay_lock, tqdnode->tqd_time); + CALLB_CPR_SAFE_END(&cpr, &tqd_delay_lock); + + if (tqd_do_exit != 0) + break; + + /* If we got a node, and we slept until expired, run it. */ + tqdnode = list_head(&tqd_list); + if (tqdnode != NULL) { + clock_t now = ddi_get_lbolt(); + /* Time has arrived */ + if (tqdnode->tqd_time <= now) { + list_remove(&tqd_list, tqdnode); + taskq_dispatch(tqdnode->tqd_taskq, + tqdnode->tqd_func, tqdnode->tqd_arg, + tqdnode->tqd_tqflags); + kmem_free(tqdnode, sizeof (tqdelay_t)); + } + } + } + + tqd_do_exit = 0; + cv_broadcast(&tqd_delay_cv); + CALLB_CPR_EXIT(&cpr); /* drops lock */ + dprintf("%s: exit\n", __func__); + thread_exit(); +} + +taskqid_t +taskq_dispatch_delay(taskq_t *tq, task_func_t func, void *arg, uint_t tqflags, + clock_t expire_time) +{ + tqdelay_t *tqdnode; + + tqdnode = kmem_alloc(sizeof (tqdelay_t), KM_SLEEP); + + /* If it has already expired, just dispatch */ + if (expire_time <= ddi_get_lbolt()) { + (void) taskq_dispatch(tq, func, arg, tqflags); + /* + * We free the node here, and still return the pointer. + * If they call taskq_cancel_id() the pointer wil not be + * in the list, so nothing happens. + * We could make this use something like KMEM_ZERO_SIZE_PTR + * but perhaps the callers expect unique ids? + */ + kmem_free(tqdnode, sizeof (tqdelay_t)); + return ((taskqid_t)tqdnode); + } + + tqdnode->tqd_time = expire_time; + tqdnode->tqd_taskq = tq; + tqdnode->tqd_func = func; + tqdnode->tqd_arg = arg; + tqdnode->tqd_tqflags = tqflags; + + mutex_enter(&tqd_delay_lock); + + /* Insert sorted on time */ + tqdelay_t *runner; + for (runner = list_head(&tqd_list); + runner != NULL; + runner = list_next(&tqd_list, runner)) + if (tqdnode->tqd_time < runner->tqd_time) { + list_insert_before(&tqd_list, runner, tqdnode); + break; + } + if (runner == NULL) { + list_insert_tail(&tqd_list, tqdnode); + } + + /* We have added to the list, wake the thread up */ + cv_broadcast(&tqd_delay_cv); + mutex_exit(&tqd_delay_lock); + + return ((taskqid_t)tqdnode); +} + +int +taskq_cancel_id(taskq_t *tq, taskqid_t id) +{ + tqdelay_t *task = (tqdelay_t *)id; + tqdelay_t *tqdnode; + + /* delay_taskq active? Linux will call with id==NULL */ + if (task != NULL) { + + /* Don't trust 'task' until it is found in the list */ + mutex_enter(&tqd_delay_lock); + + for (tqdnode = list_head(&tqd_list); + tqdnode != NULL; + tqdnode = list_next(&tqd_list, tqdnode)) { + + if (tqdnode == task) { + /* + * task exists and needs to be cancelled. + * remove it from list, and wake the thread up + * as it might be sleeping on this node. We can + * free the memory as "time" is passed in as a + * variable. + */ + list_remove(&tqd_list, tqdnode); + cv_signal(&tqd_delay_cv); + mutex_exit(&tqd_delay_lock); + + kmem_free(tqdnode, sizeof (tqdelay_t)); + + return (1); + + } // task == tqdnode + } // for + mutex_exit(&tqd_delay_lock); + } // task != NULL + return (0); +} + +void +taskq_start_delay_thread(void) +{ + list_create(&tqd_list, sizeof (tqdelay_t), + offsetof(tqdelay_t, tqd_listnode)); + mutex_init(&tqd_delay_lock, NULL, MUTEX_DEFAULT, NULL); + cv_init(&tqd_delay_cv, NULL, CV_DEFAULT, NULL); + tqd_do_exit = 0; + (void) thread_create(NULL, 0, taskq_delay_dispatcher_thread, + NULL, 0, &p0, TS_RUN, minclsyspri); +} + +void +taskq_stop_delay_thread(void) +{ + tqdelay_t *tqdnode; + + mutex_enter(&tqd_delay_lock); + tqd_do_exit = 1; + /* + * The reclaim thread will set arc_reclaim_thread_exit back to + * FALSE when it is finished exiting; we're waiting for that. + */ + while (tqd_do_exit) { + cv_signal(&tqd_delay_cv); + cv_wait(&tqd_delay_cv, &tqd_delay_lock); + } + mutex_exit(&tqd_delay_lock); + mutex_destroy(&tqd_delay_lock); + cv_destroy(&tqd_delay_cv); + + while ((tqdnode = list_head(&tqd_list)) != NULL) { + list_remove(&tqd_list, tqdnode); + kmem_free(tqdnode, sizeof (tqdelay_t)); + } + + list_destroy(&tqd_list); +} + +int +spl_taskq_init(void) +{ + tsd_create(&taskq_tsd, NULL); + + taskq_ent_cache = kmem_cache_create("taskq_ent_cache", + sizeof (taskq_ent_t), 0, taskq_ent_constructor, + taskq_ent_destructor, NULL, NULL, NULL, 0); + taskq_cache = kmem_cache_create("taskq_cache", sizeof (taskq_t), + 0, taskq_constructor, taskq_destructor, NULL, NULL, NULL, 0); + taskq_id_arena = vmem_create("taskq_id_arena", + (void *)1, INT32_MAX, 1, NULL, NULL, NULL, 0, + VM_SLEEP | VMC_IDENTIFIER); + + list_create(&taskq_cpupct_list, sizeof (taskq_t), + offsetof(taskq_t, tq_cpupct_link)); + + mutex_init(&taskq_kstat_lock, NULL, MUTEX_DEFAULT, NULL); + mutex_init(&taskq_d_kstat_lock, NULL, MUTEX_DEFAULT, NULL); + + return (0); +} + +void +spl_taskq_fini(void) +{ + mutex_destroy(&taskq_d_kstat_lock); + mutex_destroy(&taskq_kstat_lock); + + if (taskq_cache) { + kmem_cache_destroy(taskq_cache); + taskq_cache = NULL; + } + if (taskq_ent_cache) { + kmem_cache_destroy(taskq_ent_cache); + taskq_ent_cache = NULL; + } + + list_destroy(&taskq_cpupct_list); + + vmem_destroy(taskq_id_arena); + + tsd_destroy(&taskq_tsd); +} + + + + +static void +taskq_update_nthreads(taskq_t *tq, uint_t ncpus) +{ + uint_t newtarget = TASKQ_THREADS_PCT(ncpus, tq->tq_threads_ncpus_pct); + +#ifndef __APPLE__ + ASSERT(MUTEX_HELD(&cpu_lock)); +#endif + ASSERT(MUTEX_HELD(&tq->tq_lock)); + + /* We must be going from non-zero to non-zero; no exiting. */ + ASSERT3U(tq->tq_nthreads_target, !=, 0); + ASSERT3U(newtarget, !=, 0); + + ASSERT3U(newtarget, <=, tq->tq_nthreads_max); + if (newtarget != tq->tq_nthreads_target) { + tq->tq_flags |= TASKQ_CHANGING; + tq->tq_nthreads_target = newtarget; + cv_broadcast(&tq->tq_dispatch_cv); + cv_broadcast(&tq->tq_exit_cv); + } +} + +#ifndef __APPLE__ +/* No dynamic CPU add/remove in XNU, so we can just use static ncpu math */ + +/* called during task queue creation */ +static void +taskq_cpupct_install(taskq_t *tq, cpupart_t *cpup) +{ + ASSERT(tq->tq_flags & TASKQ_THREADS_CPU_PCT); + + mutex_enter(&cpu_lock); + mutex_enter(&tq->tq_lock); + tq->tq_cpupart = cpup->cp_id; + taskq_update_nthreads(tq, cpup->cp_ncpus); + mutex_exit(&tq->tq_lock); + + list_insert_tail(&taskq_cpupct_list, tq); + mutex_exit(&cpu_lock); +} + +static void +taskq_cpupct_remove(taskq_t *tq) +{ + ASSERT(tq->tq_flags & TASKQ_THREADS_CPU_PCT); + + mutex_enter(&cpu_lock); + list_remove(&taskq_cpupct_list, tq); + mutex_exit(&cpu_lock); +} + +/*ARGSUSED*/ +static int +taskq_cpu_setup(cpu_setup_t what, int id, void *arg) +{ + taskq_t *tq; + cpupart_t *cp = cpu[id]->cpu_part; + uint_t ncpus = cp->cp_ncpus; + + ASSERT(MUTEX_HELD(&cpu_lock)); + ASSERT(ncpus > 0); + + switch (what) { + case CPU_OFF: + case CPU_CPUPART_OUT: + /* offlines are called *before* the cpu is offlined. */ + if (ncpus > 1) + ncpus--; + break; + + case CPU_ON: + case CPU_CPUPART_IN: + break; + + default: + return (0); /* doesn't affect cpu count */ + } + + for (tq = list_head(&taskq_cpupct_list); tq != NULL; + tq = list_next(&taskq_cpupct_list, tq)) { + + mutex_enter(&tq->tq_lock); + /* + * If the taskq is part of the cpuset which is changing, + * update its nthreads_target. + */ + if (tq->tq_cpupart == cp->cp_id) { + taskq_update_nthreads(tq, ncpus); + } + mutex_exit(&tq->tq_lock); + } + return (0); +} + +void +taskq_mp_init(void) +{ + mutex_enter(&cpu_lock); + register_cpu_setup_func(taskq_cpu_setup, NULL); + /* + * Make sure we're up to date. At this point in boot, there is only + * one processor set, so we only have to update the current CPU. + */ + (void) taskq_cpu_setup(CPU_ON, CPU->cpu_id, NULL); + mutex_exit(&cpu_lock); +} +#endif /* __APPLE__ */ + + +/* + * Create global system dynamic task queue. + */ +void +system_taskq_init(void) +{ +#ifdef __APPLE__ + system_taskq = taskq_create_common("system_taskq", 0, + system_taskq_size * max_ncpus, minclsyspri, 4, 512, &p0, 0, + TASKQ_DYNAMIC | TASKQ_PREPOPULATE | TASKQ_REALLY_DYNAMIC); +#else + system_taskq = taskq_create_common("system_taskq", 0, + system_taskq_size * max_ncpus, minclsyspri, 4, 512, &p0, 0, + TASKQ_DYNAMIC | TASKQ_PREPOPULATE); +#endif + + system_delay_taskq = taskq_create("system_delay_taskq", max_ncpus, + minclsyspri, max_ncpus, INT_MAX, TASKQ_PREPOPULATE); + + taskq_start_delay_thread(); + +} + + +void +system_taskq_fini(void) +{ + taskq_stop_delay_thread(); + + if (system_delay_taskq) + taskq_destroy(system_delay_taskq); + if (system_taskq) + taskq_destroy(system_taskq); + system_taskq = NULL; +} + +/* + * taskq_ent_alloc() + * + * Allocates a new taskq_ent_t structure either from the free list or from the + * cache. Returns NULL if it can't be allocated. + * + * Assumes: tq->tq_lock is held. + */ +static taskq_ent_t * +taskq_ent_alloc(taskq_t *tq, int flags) +{ + int kmflags = (flags & TQ_NOSLEEP) ? KM_NOSLEEP : KM_SLEEP; + taskq_ent_t *tqe; + clock_t wait_time; + clock_t wait_rv; + + ASSERT(MUTEX_HELD(&tq->tq_lock)); + + /* + * TQ_NOALLOC allocations are allowed to use the freelist, even if + * we are below tq_minalloc. + */ +again: if ((tqe = tq->tq_freelist) != NULL && + ((flags & TQ_NOALLOC) || tq->tq_nalloc >= tq->tq_minalloc)) { + tq->tq_freelist = tqe->tqent_next; + } else { + if (flags & TQ_NOALLOC) + return (NULL); + + if (tq->tq_nalloc >= tq->tq_maxalloc) { + if (kmflags & KM_NOSLEEP) + return (NULL); + + /* + * We don't want to exceed tq_maxalloc, but we can't + * wait for other tasks to complete (and thus free up + * task structures) without risking deadlock with + * the caller. So, we just delay for one second + * to throttle the allocation rate. If we have tasks + * complete before one second timeout expires then + * taskq_ent_free will signal us and we will + * immediately retry the allocation (reap free). + */ + wait_time = ddi_get_lbolt() + hz; + while (tq->tq_freelist == NULL) { + tq->tq_maxalloc_wait++; + wait_rv = cv_timedwait(&tq->tq_maxalloc_cv, + &tq->tq_lock, wait_time); + tq->tq_maxalloc_wait--; + if (wait_rv == -1) + break; + } + if (tq->tq_freelist) + goto again; /* reap freelist */ + + } + mutex_exit(&tq->tq_lock); + + tqe = kmem_cache_alloc(taskq_ent_cache, kmflags); + + mutex_enter(&tq->tq_lock); + if (tqe != NULL) + tq->tq_nalloc++; + } + return (tqe); +} + +/* + * taskq_ent_free() + * + * Free taskq_ent_t structure by either putting it on the free list or freeing + * it to the cache. + * + * Assumes: tq->tq_lock is held. + */ +static void +taskq_ent_free(taskq_t *tq, taskq_ent_t *tqe) +{ + ASSERT(MUTEX_HELD(&tq->tq_lock)); + + if (tq->tq_nalloc <= tq->tq_minalloc) { + tqe->tqent_next = tq->tq_freelist; + tq->tq_freelist = tqe; + } else { + tq->tq_nalloc--; + mutex_exit(&tq->tq_lock); + kmem_cache_free(taskq_ent_cache, tqe); + mutex_enter(&tq->tq_lock); + } + + if (tq->tq_maxalloc_wait) + cv_signal(&tq->tq_maxalloc_cv); +} + +/* + * taskq_ent_exists() + * + * Return 1 if taskq already has entry for calling 'func(arg)'. + * + * Assumes: tq->tq_lock is held. + */ +static int +taskq_ent_exists(taskq_t *tq, task_func_t func, void *arg) +{ + taskq_ent_t *tqe; + + ASSERT(MUTEX_HELD(&tq->tq_lock)); + + for (tqe = tq->tq_task.tqent_next; tqe != &tq->tq_task; + tqe = tqe->tqent_next) + if ((tqe->tqent_func == func) && (tqe->tqent_arg == arg)) + return (1); + return (0); +} + +/* + * Dispatch a task "func(arg)" to a free entry of bucket b. + * + * Assumes: no bucket locks is held. + * + * Returns: a pointer to an entry if dispatch was successful. + * NULL if there are no free entries or if the bucket is suspended. + */ +static taskq_ent_t * +taskq_bucket_dispatch(taskq_bucket_t *b, task_func_t func, void *arg) +{ + taskq_ent_t *tqe; + + ASSERT(MUTEX_NOT_HELD(&b->tqbucket_lock)); + ASSERT(func != NULL); + + mutex_enter(&b->tqbucket_lock); + + ASSERT(b->tqbucket_nfree != 0 || IS_EMPTY(b->tqbucket_freelist)); + ASSERT(b->tqbucket_nfree == 0 || !IS_EMPTY(b->tqbucket_freelist)); + + /* + * Get en entry from the freelist if there is one. + * Schedule task into the entry. + */ + if ((b->tqbucket_nfree != 0) && + !(b->tqbucket_flags & TQBUCKET_SUSPEND)) { + tqe = b->tqbucket_freelist.tqent_prev; + + ASSERT(tqe != &b->tqbucket_freelist); + ASSERT(tqe->tqent_thread != NULL); + + tqe->tqent_prev->tqent_next = tqe->tqent_next; + tqe->tqent_next->tqent_prev = tqe->tqent_prev; + b->tqbucket_nalloc++; + b->tqbucket_nfree--; + tqe->tqent_func = func; + tqe->tqent_arg = arg; + TQ_STAT(b, tqs_hits); + cv_signal(&tqe->tqent_cv); + DTRACE_PROBE2(taskq__d__enqueue, taskq_bucket_t *, b, + taskq_ent_t *, tqe); + } else { + tqe = NULL; + TQ_STAT(b, tqs_misses); + } + mutex_exit(&b->tqbucket_lock); + return (tqe); +} + +/* + * Dispatch a task. + * + * Assumes: func != NULL + * + * Returns: NULL if dispatch failed. + * non-NULL if task dispatched successfully. + * Actual return value is the pointer to taskq entry that was used to + * dispatch a task. This is useful for debugging. + */ +taskqid_t +taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t flags) +{ + taskq_bucket_t *bucket = NULL; /* Which bucket needs extension */ + taskq_ent_t *tqe = NULL; + taskq_ent_t *tqe1; + uint_t bsize; + + ASSERT(tq != NULL); + ASSERT(func != NULL); + + if (!(tq->tq_flags & TASKQ_DYNAMIC)) { + /* + * TQ_NOQUEUE flag can't be used with non-dynamic task queues. + */ + ASSERT(!(flags & TQ_NOQUEUE)); + /* + * Enqueue the task to the underlying queue. + */ + mutex_enter(&tq->tq_lock); + + TASKQ_S_RANDOM_DISPATCH_FAILURE(tq, flags); + + if ((tqe = taskq_ent_alloc(tq, flags)) == NULL) { + mutex_exit(&tq->tq_lock); + return (0); + } + /* Make sure we start without any flags */ + tqe->tqent_un.tqent_flags = 0; + + if (flags & TQ_FRONT) { + TQ_ENQUEUE_FRONT(tq, tqe, func, arg); + } else { + TQ_ENQUEUE(tq, tqe, func, arg); + } + mutex_exit(&tq->tq_lock); + return ((taskqid_t)tqe); + } + + /* + * Dynamic taskq dispatching. + */ + ASSERT(!(flags & (TQ_NOALLOC | TQ_FRONT))); + TASKQ_D_RANDOM_DISPATCH_FAILURE(tq, flags); + + bsize = tq->tq_nbuckets; + + if (bsize == 1) { + /* + * In a single-CPU case there is only one bucket, so get + * entry directly from there. + */ + if ((tqe = taskq_bucket_dispatch(tq->tq_buckets, func, arg)) + != NULL) + return ((taskqid_t)tqe); /* Fastpath */ + bucket = tq->tq_buckets; + } else { + int loopcount; + taskq_bucket_t *b; + // uintptr_t h = ((uintptr_t)CPU + (uintptr_t)arg) >> 3; + uintptr_t h = ((uintptr_t)(CPU_SEQID<<3) + + (uintptr_t)arg) >> 3; + + h = TQ_HASH(h); + + /* + * The 'bucket' points to the original bucket that we hit. If we + * can't allocate from it, we search other buckets, but only + * extend this one. + */ + b = &tq->tq_buckets[h & (bsize - 1)]; + ASSERT(b->tqbucket_taskq == tq); /* Sanity check */ + + /* + * Do a quick check before grabbing the lock. If the bucket does + * not have free entries now, chances are very small that it + * will after we take the lock, so we just skip it. + */ + if (b->tqbucket_nfree != 0) { + if ((tqe = taskq_bucket_dispatch(b, func, arg)) != NULL) + return ((taskqid_t)tqe); /* Fastpath */ + } else { + TQ_STAT(b, tqs_misses); + } + + bucket = b; + loopcount = MIN(taskq_search_depth, bsize); + /* + * If bucket dispatch failed, search loopcount number of buckets + * before we give up and fail. + */ + do { + b = &tq->tq_buckets[++h & (bsize - 1)]; + ASSERT(b->tqbucket_taskq == tq); /* Sanity check */ + loopcount--; + + if (b->tqbucket_nfree != 0) { + tqe = taskq_bucket_dispatch(b, func, arg); + } else { + TQ_STAT(b, tqs_misses); + } + } while ((tqe == NULL) && (loopcount > 0)); + } + + /* + * At this point we either scheduled a task and (tqe != NULL) or failed + * (tqe == NULL). Try to recover from fails. + */ + + /* + * For KM_SLEEP dispatches, try to extend the bucket and retry dispatch. + */ + if ((tqe == NULL) && !(flags & TQ_NOSLEEP)) { + /* + * taskq_bucket_extend() may fail to do anything, but this is + * fine - we deal with it later. If the bucket was successfully + * extended, there is a good chance that taskq_bucket_dispatch() + * will get this new entry, unless someone is racing with us and + * stealing the new entry from under our nose. + * taskq_bucket_extend() may sleep. + */ + taskq_bucket_extend(bucket); + TQ_STAT(bucket, tqs_disptcreates); + if ((tqe = taskq_bucket_dispatch(bucket, func, arg)) != NULL) + return ((taskqid_t)tqe); + } + + ASSERT(bucket != NULL); + + /* + * Since there are not enough free entries in the bucket, add a + * taskq entry to extend it in the background using backing queue + * (unless we already have a taskq entry to perform that extension). + */ + mutex_enter(&tq->tq_lock); + if (!taskq_ent_exists(tq, taskq_bucket_extend, bucket)) { + if ((tqe1 = taskq_ent_alloc(tq, TQ_NOSLEEP)) != NULL) { + TQ_ENQUEUE_FRONT(tq, tqe1, taskq_bucket_extend, bucket); + } else { + TQ_STAT(bucket, tqs_nomem); + } + } + + /* + * Dispatch failed and we can't find an entry to schedule a task. + * Revert to the backing queue unless TQ_NOQUEUE was asked. + */ + if ((tqe == NULL) && !(flags & TQ_NOQUEUE)) { + if ((tqe = taskq_ent_alloc(tq, flags)) != NULL) { + TQ_ENQUEUE(tq, tqe, func, arg); + } else { + TQ_STAT(bucket, tqs_nomem); + } + } + mutex_exit(&tq->tq_lock); + + return ((taskqid_t)tqe); +} + +void +taskq_init_ent(taskq_ent_t *t) +{ + memset(t, 0, sizeof (*t)); +} + +void +taskq_dispatch_ent(taskq_t *tq, task_func_t func, void *arg, uint_t flags, + taskq_ent_t *tqe) +{ + ASSERT(func != NULL); + ASSERT(!(tq->tq_flags & TASKQ_DYNAMIC)); + + /* + * Mark it as a prealloc'd task. This is important + * to ensure that we don't free it later. + */ + tqe->tqent_un.tqent_flags |= TQENT_FLAG_PREALLOC; + /* + * Enqueue the task to the underlying queue. + */ + mutex_enter(&tq->tq_lock); + + if (flags & TQ_FRONT) { + TQ_ENQUEUE_FRONT(tq, tqe, func, arg); + } else { + TQ_ENQUEUE(tq, tqe, func, arg); + } + mutex_exit(&tq->tq_lock); +} + +/* + * Allow our caller to ask if there are tasks pending on the queue. + */ +boolean_t +taskq_empty(taskq_t *tq) +{ + boolean_t rv; + + mutex_enter(&tq->tq_lock); + rv = (tq->tq_task.tqent_next == &tq->tq_task) && (tq->tq_active == 0); + mutex_exit(&tq->tq_lock); + + return (rv); +} + +int +taskq_empty_ent(taskq_ent_t *t) +{ + return (IS_EMPTY(*t)); +} + +/* + * Wait for all pending tasks to complete. + * Calling taskq_wait from a task will cause deadlock. + */ +void +taskq_wait(taskq_t *tq) +{ +#ifndef __APPLE__ + ASSERT(tq != curthread->t_taskq); +#endif + + if (tq == NULL) + return; + + mutex_enter(&tq->tq_lock); + while (tq->tq_task.tqent_next != &tq->tq_task || tq->tq_active != 0) + cv_wait(&tq->tq_wait_cv, &tq->tq_lock); + mutex_exit(&tq->tq_lock); + + if (tq->tq_flags & TASKQ_DYNAMIC) { + taskq_bucket_t *b = tq->tq_buckets; + int bid = 0; + for (; (b != NULL) && (bid < tq->tq_nbuckets); b++, bid++) { + mutex_enter(&b->tqbucket_lock); + while (b->tqbucket_nalloc > 0) + cv_wait(&b->tqbucket_cv, &b->tqbucket_lock); + mutex_exit(&b->tqbucket_lock); + } + } +} + +/* + * ZOL implements taskq_wait_id() that can wait for a specific + * taskq to finish, rather than all active taskqs. Until it is + * implemented, we wait for all to complete. + */ +void +taskq_wait_id(taskq_t *tq, taskqid_t id) +{ + return (taskq_wait(tq)); +} + +void +taskq_wait_outstanding(taskq_t *tq, taskqid_t id) +{ + return (taskq_wait(tq)); +} + +/* + * Suspend execution of tasks. + * + * Tasks in the queue part will be suspended immediately upon return from this + * function. Pending tasks in the dynamic part will continue to execute, but all + * new tasks will be suspended. + */ +void +taskq_suspend(taskq_t *tq) +{ + rw_enter(&tq->tq_threadlock, RW_WRITER); + + if (tq->tq_flags & TASKQ_DYNAMIC) { + taskq_bucket_t *b = tq->tq_buckets; + int bid = 0; + for (; (b != NULL) && (bid < tq->tq_nbuckets); b++, bid++) { + mutex_enter(&b->tqbucket_lock); + b->tqbucket_flags |= TQBUCKET_SUSPEND; + mutex_exit(&b->tqbucket_lock); + } + } + /* + * Mark task queue as being suspended. Needed for taskq_suspended(). + */ + mutex_enter(&tq->tq_lock); + ASSERT(!(tq->tq_flags & TASKQ_SUSPENDED)); + tq->tq_flags |= TASKQ_SUSPENDED; + mutex_exit(&tq->tq_lock); +} + +/* + * returns: 1 if tq is suspended, 0 otherwise. + */ +int +taskq_suspended(taskq_t *tq) +{ + return ((tq->tq_flags & TASKQ_SUSPENDED) != 0); +} + +/* + * Resume taskq execution. + */ +void +taskq_resume(taskq_t *tq) +{ + ASSERT(RW_WRITE_HELD(&tq->tq_threadlock)); + + if (tq->tq_flags & TASKQ_DYNAMIC) { + taskq_bucket_t *b = tq->tq_buckets; + int bid = 0; + for (; (b != NULL) && (bid < tq->tq_nbuckets); b++, bid++) { + mutex_enter(&b->tqbucket_lock); + b->tqbucket_flags &= ~TQBUCKET_SUSPEND; + mutex_exit(&b->tqbucket_lock); + } + } + mutex_enter(&tq->tq_lock); + ASSERT(tq->tq_flags & TASKQ_SUSPENDED); + tq->tq_flags &= ~TASKQ_SUSPENDED; + mutex_exit(&tq->tq_lock); + + rw_exit(&tq->tq_threadlock); +} + +int +taskq_member(taskq_t *tq, kthread_t *thread) +{ + return (tq == (taskq_t *)tsd_get_by_thread(taskq_tsd, thread)); +} + +taskq_t * +taskq_of_curthread(void) +{ + return (tsd_get(taskq_tsd)); +} + +/* + * Creates a thread in the taskq. We only allow one outstanding create at + * a time. We drop and reacquire the tq_lock in order to avoid blocking other + * taskq activity while thread_create() or lwp_kernel_create() run. + * + * The first time we're called, we do some additional setup, and do not + * return until there are enough threads to start servicing requests. + */ +static void +taskq_thread_create(taskq_t *tq) +{ + kthread_t *t; + const boolean_t first = (tq->tq_nthreads == 0); + + ASSERT(MUTEX_HELD(&tq->tq_lock)); + ASSERT(tq->tq_flags & TASKQ_CHANGING); + ASSERT(tq->tq_nthreads < tq->tq_nthreads_target); + ASSERT(!(tq->tq_flags & TASKQ_THREAD_CREATED)); + + tq->tq_flags |= TASKQ_THREAD_CREATED; + tq->tq_active++; + mutex_exit(&tq->tq_lock); + + /* + * With TASKQ_DUTY_CYCLE the new thread must have an LWP + * as explained in ../disp/sysdc.c (for the msacct data). + * Otherwise simple kthreads are preferred. + */ + if ((tq->tq_flags & TASKQ_DUTY_CYCLE) != 0) { + /* Enforced in taskq_create_common */ + printf("SPL: taskq_thread_create(TASKQ_DUTY_CYCLE) seen\n"); +#ifndef __APPLE__ + ASSERT3P(tq->tq_proc, !=, &p0); + t = lwp_kernel_create(tq->tq_proc, taskq_thread, tq, TS_RUN, + tq->tq_pri); +#else + t = thread_create_named(tq->tq_name, + NULL, 0, taskq_thread, tq, 0, tq->tq_proc, + TS_RUN, tq->tq_pri); +#endif + } else { + t = thread_create_named(tq->tq_name, + NULL, 0, taskq_thread, tq, 0, tq->tq_proc, + TS_RUN, tq->tq_pri); + } + + if (!first) { + mutex_enter(&tq->tq_lock); + return; + } + + /* + * We know the thread cannot go away, since tq cannot be + * destroyed until creation has completed. We can therefore + * safely dereference t. + */ + if (tq->tq_flags & TASKQ_THREADS_CPU_PCT) { +#ifdef __APPLE__ + mutex_enter(&tq->tq_lock); + taskq_update_nthreads(tq, max_ncpus); + mutex_exit(&tq->tq_lock); +#else + taskq_cpupct_install(tq, t->t_cpupart); +#endif + } + mutex_enter(&tq->tq_lock); + + /* Wait until we can service requests. */ + while (tq->tq_nthreads != tq->tq_nthreads_target && + tq->tq_nthreads < TASKQ_CREATE_ACTIVE_THREADS) { + cv_wait(&tq->tq_wait_cv, &tq->tq_lock); + } +} + +/* + * Common "sleep taskq thread" function, which handles CPR stuff, as well + * as giving a nice common point for debuggers to find inactive threads. + */ +static clock_t +taskq_thread_wait(taskq_t *tq, kmutex_t *mx, kcondvar_t *cv, + callb_cpr_t *cprinfo, clock_t timeout) +{ + clock_t ret = 0; + + if (!(tq->tq_flags & TASKQ_CPR_SAFE)) { + CALLB_CPR_SAFE_BEGIN(cprinfo); + } + if ((signed long)timeout < 0) + cv_wait(cv, mx); + else + ret = cv_reltimedwait(cv, mx, timeout, TR_CLOCK_TICK); + + if (!(tq->tq_flags & TASKQ_CPR_SAFE)) { + CALLB_CPR_SAFE_END(cprinfo, mx); + } + + return (ret); +} + +#ifdef __APPLE__ +/* + * Adjust thread policies for SYSDC and BATCH task threads + */ + +/* + * from osfmk/kern/thread.[hc] and osfmk/kern/ledger.c + * + * limit [is] a percentage of CPU over an interval in nanoseconds + * + * in particular limittime = (interval_ns * percentage) / 100 + * + * when a thread has enough cpu time accumulated to hit limittime, + * ast_taken->thread_block is seen in a stackshot (e.g. spindump) + * + * thread.h 204:#define MINIMUM_CPULIMIT_INTERVAL_MS 1 + * + * Illumos's sysdc updates its stats every 20 ms + * (sysdc_update_interval_msec) + * which is the tunable we can deal with here; xnu will + * take care of the bookkeeping and the amount of "break", + * which are the other Illumos tunables. + */ +#define CPULIMIT_INTERVAL (MSEC2NSEC(100ULL)) +#define THREAD_CPULIMIT_BLOCK 0x1 + +#if defined(MACOS_IMPURE) +extern int thread_set_cpulimit(int action, uint8_t percentage, + uint64_t interval_ns); +#endif + +static void +taskq_thread_set_cpulimit(taskq_t *tq) +{ + if (tq->tq_flags & TASKQ_DUTY_CYCLE) { + ASSERT3U(tq->tq_DC, <=, 100); + ASSERT3U(tq->tq_DC, >, 0); +#if defined(MACOS_IMPURE) + const uint8_t inpercent = MIN(100, MAX(tq->tq_DC, 1)); + const uint64_t interval_ns = CPULIMIT_INTERVAL; + /* + * deflate tq->tq_DC (a percentage of cpu) by the + * ratio of max_ncpus (logical cpus) to physical_ncpu. + * + * we don't want hyperthread resources to get starved + * out by a large DUTY CYCLE, and we aren't doing + * processor set pinning of threads to CPUs of either + * type (neither does Illumos, but sysdc does take + * account of psets when calculating the duty cycle, + * and I don't know how to do that yet). + * + * do some scaled integer division to get + * decpct = percent/(maxcpus/physcpus) + */ + const uint64_t m100 = (uint64_t)max_ncpus * 100ULL; + const uint64_t r100 = m100 / (MAX(max_ncpus/2, 1)); + const uint64_t pct100 = inpercent * 100ULL; + const uint64_t decpct = pct100 / r100; + uint8_t percent = MIN(decpct, inpercent); + ASSERT3U(percent, <=, 100); + ASSERT3U(percent, >, 0); + + int ret = thread_set_cpulimit(THREAD_CPULIMIT_BLOCK, + percent, interval_ns); +#else + int ret = 45; // ENOTSUP -- XXX todo: drop priority? +#endif + + if (ret != KERN_SUCCESS) { + printf("SPL: %s:%d: WARNING" + " thread_set_cpulimit returned %d\n", + __func__, __LINE__, ret); + } + } +} + +/* + * Set up xnu thread importance, + * throughput and latency QOS. + * + * Approximate Illumos's SYSDC + * (/usr/src/uts/common/disp/sysdc.c) + * + * SYSDC tracks cpu runtime itself, and yields to + * other threads if + * onproc time / (onproc time + runnable time) + * exceeds the Duty Cycle threshold. + * + * Approximate this by + * [a] setting a thread_cpu_limit percentage, + * [b] setting the thread precedence + * slightly higher than normal, + * [c] setting the thread throughput and latency policies + * just less than USER_INTERACTIVE, and + * [d] turning on the + * TIMESHARE policy, which adjusts the thread + * priority based on cpu usage. + */ + +static void +set_taskq_thread_attributes(thread_t thread, taskq_t *tq) +{ + pri_t pri = tq->tq_pri; + + if (tq->tq_flags & TASKQ_DUTY_CYCLE) { + taskq_thread_set_cpulimit(tq); + } + + if (tq->tq_flags & TASKQ_DC_BATCH) + pri--; + + set_thread_importance_named(thread, + pri, tq->tq_name); + + /* + * TIERs: 0 is USER_INTERACTIVE, 1 is USER_INITIATED, 1 is LEGACY, + * 2 is UTILITY, 5 is BACKGROUND, 5 is MAINTENANCE + */ + const thread_throughput_qos_t std_throughput = THROUGHPUT_QOS_TIER_1; + const thread_throughput_qos_t sysdc_throughput = THROUGHPUT_QOS_TIER_1; + const thread_throughput_qos_t batch_throughput = THROUGHPUT_QOS_TIER_2; + if (tq->tq_flags & TASKQ_DC_BATCH) + set_thread_throughput_named(thread, + batch_throughput, tq->tq_name); + else if (tq->tq_flags & TASKQ_DUTY_CYCLE) + set_thread_throughput_named(thread, + sysdc_throughput, tq->tq_name); + else + set_thread_throughput_named(thread, + std_throughput, tq->tq_name); + + /* + * TIERs: 0 is USER_INTERACTIVE, 1 is USER_INITIATED, + * 1 is LEGACY, 3 is UTILITY, 3 is BACKGROUND, + * 5 is MAINTENANCE + */ + const thread_latency_qos_t batch_latency = LATENCY_QOS_TIER_3; + const thread_latency_qos_t std_latency = LATENCY_QOS_TIER_1; + + if (tq->tq_flags & TASKQ_DC_BATCH) + set_thread_latency_named(thread, + batch_latency, tq->tq_name); + else + set_thread_latency_named(thread, + std_latency, tq->tq_name); + + /* + * Passivate I/Os for this thread + * (Default is IOPOOL_IMPORTANT) + */ + spl_throttle_set_thread_io_policy(IOPOL_PASSIVE); + + set_thread_timeshare_named(thread, + tq->tq_name); +} + +#endif // __APPLE__ + +/* + * Worker thread for processing task queue. + */ +static void +taskq_thread(void *arg) +{ + int thread_id; + + taskq_t *tq = arg; + taskq_ent_t *tqe; + callb_cpr_t cprinfo; + hrtime_t start, end; + boolean_t freeit; + +#ifdef __APPLE__ + set_taskq_thread_attributes(current_thread(), tq); +#endif + + CALLB_CPR_INIT(&cprinfo, &tq->tq_lock, callb_generic_cpr, + tq->tq_name); + + tsd_set(taskq_tsd, tq); + mutex_enter(&tq->tq_lock); + thread_id = ++tq->tq_nthreads; + ASSERT(tq->tq_flags & TASKQ_THREAD_CREATED); + ASSERT(tq->tq_flags & TASKQ_CHANGING); + tq->tq_flags &= ~TASKQ_THREAD_CREATED; + + VERIFY3S(thread_id, <=, tq->tq_nthreads_max); + + if (tq->tq_nthreads_max == 1) + tq->tq_thread = (kthread_t *)curthread; + else + tq->tq_threadlist[thread_id - 1] = (kthread_t *)curthread; + + /* Allow taskq_create_common()'s taskq_thread_create() to return. */ + if (tq->tq_nthreads == TASKQ_CREATE_ACTIVE_THREADS) + cv_broadcast(&tq->tq_wait_cv); + + for (;;) { + if (tq->tq_flags & TASKQ_CHANGING) { + /* See if we're no longer needed */ + if (thread_id > tq->tq_nthreads_target) { + /* + * To preserve the one-to-one mapping between + * thread_id and thread, we must exit from + * highest thread ID to least. + * + * However, if everyone is exiting, the order + * doesn't matter, so just exit immediately. + * (this is safe, since you must wait for + * nthreads to reach 0 after setting + * tq_nthreads_target to 0) + */ + if (thread_id == tq->tq_nthreads || + tq->tq_nthreads_target == 0) { + break; + } + + /* Wait for higher thread_ids to exit */ + (void) taskq_thread_wait(tq, &tq->tq_lock, + &tq->tq_exit_cv, &cprinfo, -1); + continue; + } + + /* + * If no thread is starting taskq_thread(), we can + * do some bookkeeping. + */ + if (!(tq->tq_flags & TASKQ_THREAD_CREATED)) { + /* Check if we've reached our target */ + if (tq->tq_nthreads == tq->tq_nthreads_target) { + tq->tq_flags &= ~TASKQ_CHANGING; + cv_broadcast(&tq->tq_wait_cv); + } + /* Check if we need to create a thread */ + if (tq->tq_nthreads < tq->tq_nthreads_target) { + taskq_thread_create(tq); + continue; /* tq_lock was dropped */ + } + } + } + if ((tqe = tq->tq_task.tqent_next) == &tq->tq_task) { + if (--tq->tq_active == 0) + cv_broadcast(&tq->tq_wait_cv); + (void) taskq_thread_wait(tq, &tq->tq_lock, + &tq->tq_dispatch_cv, &cprinfo, -1); + tq->tq_active++; + continue; + } + + tqe->tqent_prev->tqent_next = tqe->tqent_next; + tqe->tqent_next->tqent_prev = tqe->tqent_prev; + mutex_exit(&tq->tq_lock); + + /* + * For prealloc'd tasks, we don't free anything. We + * have to check this now, because once we call the + * function for a prealloc'd taskq, we can't touch the + * tqent any longer (calling the function returns the + * ownershp of the tqent back to caller of + * taskq_dispatch.) + */ + if ((!(tq->tq_flags & TASKQ_DYNAMIC)) && + (tqe->tqent_un.tqent_flags & TQENT_FLAG_PREALLOC)) { + /* clear pointers to assist assertion checks */ + tqe->tqent_next = tqe->tqent_prev = NULL; + freeit = B_FALSE; + } else { + freeit = B_TRUE; + } + + rw_enter(&tq->tq_threadlock, RW_READER); + start = gethrtime(); + DTRACE_PROBE2(taskq__exec__start, taskq_t *, tq, + taskq_ent_t *, tqe); + tqe->tqent_func(tqe->tqent_arg); + DTRACE_PROBE2(taskq__exec__end, taskq_t *, tq, + taskq_ent_t *, tqe); + end = gethrtime(); + rw_exit(&tq->tq_threadlock); + + mutex_enter(&tq->tq_lock); + tq->tq_totaltime += end - start; + tq->tq_executed++; + + if (freeit) + taskq_ent_free(tq, tqe); + } + + if (tq->tq_nthreads_max == 1) + tq->tq_thread = NULL; + else + tq->tq_threadlist[thread_id - 1] = NULL; + + /* We're exiting, and therefore no longer active */ + ASSERT(tq->tq_active > 0); + tq->tq_active--; + + ASSERT(tq->tq_nthreads > 0); + tq->tq_nthreads--; + + /* Wake up anyone waiting for us to exit */ + cv_broadcast(&tq->tq_exit_cv); + if (tq->tq_nthreads == tq->tq_nthreads_target) { + if (!(tq->tq_flags & TASKQ_THREAD_CREATED)) + tq->tq_flags &= ~TASKQ_CHANGING; + + cv_broadcast(&tq->tq_wait_cv); + } + + tsd_set(taskq_tsd, NULL); + + CALLB_CPR_EXIT(&cprinfo); + thread_exit(); + +} + +/* + * Worker per-entry thread for dynamic dispatches. + */ +static void +taskq_d_thread(taskq_ent_t *tqe) +{ + taskq_bucket_t *bucket = tqe->tqent_un.tqent_bucket; + taskq_t *tq = bucket->tqbucket_taskq; + kmutex_t *lock = &bucket->tqbucket_lock; + kcondvar_t *cv = &tqe->tqent_cv; + callb_cpr_t cprinfo; + clock_t w; + + CALLB_CPR_INIT(&cprinfo, lock, callb_generic_cpr, tq->tq_name); + +#ifdef __APPLE__ + /* + * There's no way in Mac OS X KPI to create a thread + * in a suspended state (TS_STOPPED). So instead we + * use tqent_thread as a flag and wait for it to get + * initialized. + */ + mutex_enter(&tqe->tqent_thread_lock); + while (tqe->tqent_thread == (kthread_t *)0xCEDEC0DE) + cv_wait(&tqe->tqent_thread_cv, &tqe->tqent_thread_lock); + mutex_exit(&tqe->tqent_thread_lock); +#endif + + mutex_enter(lock); + + for (;;) { + /* + * If a task is scheduled (func != NULL), execute it, otherwise + * sleep, waiting for a job. + */ + if (tqe->tqent_func != NULL) { + hrtime_t start; + hrtime_t end; + + ASSERT(bucket->tqbucket_nalloc > 0); + + /* + * It is possible to free the entry right away before + * actually executing the task so that subsequent + * dispatches may immediately reuse it. But this, + * effectively, creates a two-length queue in the entry + * and may lead to a deadlock if the execution of the + * current task depends on the execution of the next + * scheduled task. So, we keep the entry busy until the + * task is processed. + */ + + mutex_exit(lock); + start = gethrtime(); + DTRACE_PROBE3(taskq__d__exec__start, taskq_t *, tq, + taskq_bucket_t *, bucket, taskq_ent_t *, tqe); + tqe->tqent_func(tqe->tqent_arg); + DTRACE_PROBE3(taskq__d__exec__end, taskq_t *, tq, + taskq_bucket_t *, bucket, taskq_ent_t *, tqe); + end = gethrtime(); + mutex_enter(lock); + bucket->tqbucket_totaltime += end - start; + + /* + * Return the entry to the bucket free list. + */ + tqe->tqent_func = NULL; + TQ_APPEND(bucket->tqbucket_freelist, tqe); + bucket->tqbucket_nalloc--; + bucket->tqbucket_nfree++; + ASSERT(!IS_EMPTY(bucket->tqbucket_freelist)); + /* + * taskq_wait() waits for nalloc to drop to zero on + * tqbucket_cv. + */ + cv_signal(&bucket->tqbucket_cv); + } + + /* + * At this point the entry must be in the bucket free list - + * either because it was there initially or because it just + * finished executing a task and put itself on the free list. + */ + ASSERT(bucket->tqbucket_nfree > 0); + /* + * Go to sleep unless we are closing. + * If a thread is sleeping too long, it dies. + */ + if (! (bucket->tqbucket_flags & TQBUCKET_CLOSE)) { + w = taskq_thread_wait(tq, lock, cv, + &cprinfo, taskq_thread_timeout * hz); + } + + /* + * At this point we may be in two different states: + * + * (1) tqent_func is set which means that a new task is + * dispatched and we need to execute it. + * + * (2) Thread is sleeping for too long or we are closing. In + * both cases destroy the thread and the entry. + */ + + /* If func is NULL we should be on the freelist. */ + ASSERT((tqe->tqent_func != NULL) || + (bucket->tqbucket_nfree > 0)); + /* If func is non-NULL we should be allocated */ + ASSERT((tqe->tqent_func == NULL) || + (bucket->tqbucket_nalloc > 0)); + + /* Check freelist consistency */ + ASSERT((bucket->tqbucket_nfree > 0) || + IS_EMPTY(bucket->tqbucket_freelist)); + ASSERT((bucket->tqbucket_nfree == 0) || + !IS_EMPTY(bucket->tqbucket_freelist)); + + if ((tqe->tqent_func == NULL) && + ((w == -1) || (bucket->tqbucket_flags & TQBUCKET_CLOSE))) { + /* + * This thread is sleeping for too long or we are + * closing - time to die. + * Thread creation/destruction happens rarely, + * so grabbing the lock is not a big performance issue. + * The bucket lock is dropped by CALLB_CPR_EXIT(). + */ + + /* Remove the entry from the free list. */ + tqe->tqent_prev->tqent_next = tqe->tqent_next; + tqe->tqent_next->tqent_prev = tqe->tqent_prev; + ASSERT(bucket->tqbucket_nfree > 0); + bucket->tqbucket_nfree--; + + TQ_STAT(bucket, tqs_tdeaths); + cv_signal(&bucket->tqbucket_cv); + tqe->tqent_thread = NULL; + mutex_enter(&tq->tq_lock); + tq->tq_tdeaths++; + mutex_exit(&tq->tq_lock); + CALLB_CPR_EXIT(&cprinfo); + kmem_cache_free(taskq_ent_cache, tqe); + thread_exit(); + } + } +} + + +/* + * Taskq creation. May sleep for memory. + * Always use automatically generated instances to avoid kstat name space + * collisions. + */ + +taskq_t * +taskq_create(const char *name, int nthreads, pri_t pri, int minalloc, + int maxalloc, uint_t flags) +{ + ASSERT((flags & ~TASKQ_INTERFACE_FLAGS) == 0); + + return (taskq_create_common(name, 0, nthreads, pri, minalloc, + maxalloc, &p0, 0, flags | TASKQ_NOINSTANCE)); +} + +/* + * Create an instance of task queue. It is legal to create task queues with the + * same name and different instances. + * + * taskq_create_instance is used by ddi_taskq_create() where it gets the + * instance from ddi_get_instance(). In some cases the instance is not + * initialized and is set to -1. This case is handled as if no instance was + * passed at all. + */ +taskq_t * +taskq_create_instance(const char *name, int instance, int nthreads, pri_t pri, + int minalloc, int maxalloc, uint_t flags) +{ + ASSERT((flags & ~TASKQ_INTERFACE_FLAGS) == 0); + ASSERT((instance >= 0) || (instance == -1)); + + if (instance < 0) { + flags |= TASKQ_NOINSTANCE; + } + + return (taskq_create_common(name, instance, nthreads, + pri, minalloc, maxalloc, &p0, 0, flags)); +} + +taskq_t * +taskq_create_proc(const char *name, int nthreads, pri_t pri, int minalloc, + int maxalloc, proc_t *proc, uint_t flags) +{ + ASSERT((flags & ~TASKQ_INTERFACE_FLAGS) == 0); +#ifndef __APPLE__ + ASSERT(proc->p_flag & SSYS); +#endif + return (taskq_create_common(name, 0, nthreads, pri, minalloc, + maxalloc, proc, 0, flags | TASKQ_NOINSTANCE)); +} + +taskq_t * +taskq_create_sysdc(const char *name, int nthreads, int minalloc, + int maxalloc, proc_t *proc, uint_t dc, uint_t flags) +{ + ASSERT((flags & ~TASKQ_INTERFACE_FLAGS) == 0); +#ifndef __APPLE__ + ASSERT(proc->p_flag & SSYS); +#else + dprintf("SPL: %s:%d: taskq_create_sysdc(%s, nthreads: %d," + " minalloc: %d, maxalloc: %d, proc, dc: %u, flags: %x)\n", + __func__, __LINE__, name, nthreads, + minalloc, maxalloc, dc, flags); +#endif + return (taskq_create_common(name, 0, nthreads, minclsyspri, minalloc, + maxalloc, proc, dc, flags | TASKQ_NOINSTANCE | TASKQ_DUTY_CYCLE)); +} + +static taskq_t * +taskq_create_common(const char *name, int instance, int nthreads, pri_t pri, + int minalloc, int maxalloc, proc_t *proc, uint_t dc, uint_t flags) +{ + taskq_t *tq = kmem_cache_alloc(taskq_cache, KM_SLEEP); +#ifdef __APPLE__ + uint_t ncpus = max_ncpus; +#else + uint_t ncpus = ((boot_max_ncpus == -1) ? max_ncpus : boot_max_ncpus); +#endif + uint_t bsize; /* # of buckets - always power of 2 */ + int max_nthreads; + + /* + * We are not allowed to use TASKQ_DYNAMIC with taskq_dispatch_ent() + * but that is done by spa.c - so we will simply mask DYNAMIC out. + */ + if (!(flags & TASKQ_REALLY_DYNAMIC)) + flags &= ~TASKQ_DYNAMIC; + + /* + * TASKQ_DYNAMIC, TASKQ_CPR_SAFE and TASKQ_THREADS_CPU_PCT are all + * mutually incompatible. + */ + IMPLY((flags & TASKQ_DYNAMIC), !(flags & TASKQ_CPR_SAFE)); + IMPLY((flags & TASKQ_DYNAMIC), !(flags & TASKQ_THREADS_CPU_PCT)); + IMPLY((flags & TASKQ_CPR_SAFE), !(flags & TASKQ_THREADS_CPU_PCT)); + + /* Cannot have DYNAMIC with DUTY_CYCLE */ + IMPLY((flags & TASKQ_DYNAMIC), !(flags & TASKQ_DUTY_CYCLE)); + + /* Cannot have DUTY_CYCLE with a p0 kernel process */ + IMPLY((flags & TASKQ_DUTY_CYCLE), proc != &p0); + + /* Cannot have DC_BATCH without DUTY_CYCLE */ + ASSERT((flags & (TASKQ_DUTY_CYCLE|TASKQ_DC_BATCH)) + != TASKQ_DC_BATCH); + +#ifdef __APPLE__ + /* Cannot have DC_BATCH or DUTY_CYCLE with TIMESHARE */ + IMPLY((flags & (TASKQ_DUTY_CYCLE|TASKQ_DC_BATCH)), + !(flags & TASKQ_TIMESHARE)); +#endif + + ASSERT(proc != NULL); + + bsize = 1 << (highbit(ncpus) - 1); + ASSERT(bsize >= 1); + bsize = MIN(bsize, taskq_maxbuckets); + + if (flags & TASKQ_DYNAMIC) { + ASSERT3S(nthreads, >=, 1); + tq->tq_maxsize = nthreads; + + /* For dynamic task queues use just one backup thread */ + nthreads = max_nthreads = 1; + + } else if (flags & TASKQ_THREADS_CPU_PCT) { + uint_t pct; + ASSERT3S(nthreads, >=, 0); + pct = nthreads; + + if (pct > taskq_cpupct_max_percent) + pct = taskq_cpupct_max_percent; + + /* + * If you're using THREADS_CPU_PCT, the process for the + * taskq threads must be curproc. This allows any pset + * binding to be inherited correctly. If proc is &p0, + * we won't be creating LWPs, so new threads will be assigned + * to the default processor set. + */ + /* ASSERT(curproc == proc || proc == &p0); */ + tq->tq_threads_ncpus_pct = pct; + nthreads = 1; /* corrected in taskq_thread_create() */ + max_nthreads = TASKQ_THREADS_PCT(max_ncpus, pct); + + } else { + ASSERT3S(nthreads, >=, 1); + max_nthreads = nthreads; + } + + if (max_nthreads < taskq_minimum_nthreads_max) + max_nthreads = taskq_minimum_nthreads_max; + + /* + * Make sure the name is 0-terminated, and conforms to the rules for + * C indentifiers + */ + (void) strncpy(tq->tq_name, name, TASKQ_NAMELEN + 1); + strident_canon(tq->tq_name, TASKQ_NAMELEN + 1); + + tq->tq_flags = flags | TASKQ_CHANGING; + tq->tq_active = 0; + tq->tq_instance = instance; + tq->tq_nthreads_target = nthreads; + tq->tq_nthreads_max = max_nthreads; + tq->tq_minalloc = minalloc; + tq->tq_maxalloc = maxalloc; + tq->tq_nbuckets = bsize; + tq->tq_proc = proc; + tq->tq_pri = pri; + tq->tq_DC = dc; + list_link_init(&tq->tq_cpupct_link); + + if (max_nthreads > 1) + tq->tq_threadlist = kmem_alloc( + sizeof (kthread_t *) * max_nthreads, KM_SLEEP); + + mutex_enter(&tq->tq_lock); + if (flags & TASKQ_PREPOPULATE) { + while (minalloc-- > 0) + taskq_ent_free(tq, taskq_ent_alloc(tq, TQ_SLEEP)); + } + + /* + * Before we start creating threads for this taskq, take a + * zone hold so the zone can't go away before taskq_destroy + * makes sure all the taskq threads are gone. This hold is + * similar in purpose to those taken by zthread_create(). + */ +#ifndef __APPLE__ + zone_hold(tq->tq_proc->p_zone); +#endif + /* + * Create the first thread, which will create any other threads + * necessary. taskq_thread_create will not return until we have + * enough threads to be able to process requests. + */ + taskq_thread_create(tq); + mutex_exit(&tq->tq_lock); + + if (flags & TASKQ_DYNAMIC) { + taskq_bucket_t *bucket = kmem_zalloc(sizeof (taskq_bucket_t) * + bsize, KM_SLEEP); + int b_id; + + tq->tq_buckets = bucket; + + /* Initialize each bucket */ + for (b_id = 0; b_id < bsize; b_id++, bucket++) { + mutex_init(&bucket->tqbucket_lock, NULL, MUTEX_DEFAULT, + NULL); + cv_init(&bucket->tqbucket_cv, NULL, CV_DEFAULT, NULL); + bucket->tqbucket_taskq = tq; + bucket->tqbucket_freelist.tqent_next = + bucket->tqbucket_freelist.tqent_prev = + &bucket->tqbucket_freelist; + if (flags & TASKQ_PREPOPULATE) + taskq_bucket_extend(bucket); + } + } + + /* + * Install kstats. + * We have two cases: + * 1) Instance is provided to taskq_create_instance(). In this case it + * should be >= 0 and we use it. + * + * 2) Instance is not provided and is automatically generated + */ + if (flags & TASKQ_NOINSTANCE) { + instance = tq->tq_instance = + (int)(uintptr_t)vmem_alloc(taskq_id_arena, 1, VM_SLEEP); + } + + if (flags & TASKQ_DYNAMIC) { + if ((tq->tq_kstat = kstat_create("unix", instance, + tq->tq_name, "taskq_d", KSTAT_TYPE_NAMED, + sizeof (taskq_d_kstat) / sizeof (kstat_named_t), + KSTAT_FLAG_VIRTUAL)) != NULL) { + tq->tq_kstat->ks_lock = &taskq_d_kstat_lock; + tq->tq_kstat->ks_data = &taskq_d_kstat; + tq->tq_kstat->ks_update = taskq_d_kstat_update; + tq->tq_kstat->ks_private = tq; + kstat_install(tq->tq_kstat); + } + } else { + if ((tq->tq_kstat = kstat_create("unix", instance, tq->tq_name, + "taskq", KSTAT_TYPE_NAMED, + sizeof (taskq_kstat) / sizeof (kstat_named_t), + KSTAT_FLAG_VIRTUAL)) != NULL) { + tq->tq_kstat->ks_lock = &taskq_kstat_lock; + tq->tq_kstat->ks_data = &taskq_kstat; + tq->tq_kstat->ks_update = taskq_kstat_update; + tq->tq_kstat->ks_private = tq; + kstat_install(tq->tq_kstat); + } + } + + return (tq); +} + +/* + * taskq_destroy(). + * + * Assumes: by the time taskq_destroy is called no one will use this task queue + * in any way and no one will try to dispatch entries in it. + */ +void +taskq_destroy(taskq_t *tq) +{ + taskq_bucket_t *b = tq->tq_buckets; + int bid = 0; + + ASSERT(! (tq->tq_flags & TASKQ_CPR_SAFE)); + + /* + * Destroy kstats. + */ + if (tq->tq_kstat != NULL) { + kstat_delete(tq->tq_kstat); + tq->tq_kstat = NULL; + } + + /* + * Destroy instance if needed. + */ + if (tq->tq_flags & TASKQ_NOINSTANCE) { + vmem_free(taskq_id_arena, (void *)(uintptr_t)(tq->tq_instance), + 1); + tq->tq_instance = 0; + } + + /* + * Unregister from the cpupct list. + */ +#ifndef __APPLE__ + if (tq->tq_flags & TASKQ_THREADS_CPU_PCT) { + taskq_cpupct_remove(tq); + } +#endif + + /* + * Wait for any pending entries to complete. + */ + taskq_wait(tq); + + mutex_enter(&tq->tq_lock); + ASSERT((tq->tq_task.tqent_next == &tq->tq_task) && + (tq->tq_active == 0)); + + /* notify all the threads that they need to exit */ + tq->tq_nthreads_target = 0; + + tq->tq_flags |= TASKQ_CHANGING; + cv_broadcast(&tq->tq_dispatch_cv); + cv_broadcast(&tq->tq_exit_cv); + + while (tq->tq_nthreads != 0) + cv_wait(&tq->tq_wait_cv, &tq->tq_lock); + + if (tq->tq_nthreads_max != 1) + kmem_free(tq->tq_threadlist, sizeof (kthread_t *) * + tq->tq_nthreads_max); + + tq->tq_minalloc = 0; + while (tq->tq_nalloc != 0) + taskq_ent_free(tq, taskq_ent_alloc(tq, TQ_SLEEP)); + + mutex_exit(&tq->tq_lock); + + /* + * Mark each bucket as closing and wakeup all sleeping threads. + */ + for (; (b != NULL) && (bid < tq->tq_nbuckets); b++, bid++) { + taskq_ent_t *tqe; + + mutex_enter(&b->tqbucket_lock); + + b->tqbucket_flags |= TQBUCKET_CLOSE; + /* Wakeup all sleeping threads */ + + for (tqe = b->tqbucket_freelist.tqent_next; + tqe != &b->tqbucket_freelist; tqe = tqe->tqent_next) + cv_signal(&tqe->tqent_cv); + + ASSERT(b->tqbucket_nalloc == 0); + + /* + * At this point we waited for all pending jobs to complete (in + * both the task queue and the bucket and no new jobs should + * arrive. Wait for all threads to die. + */ + while (b->tqbucket_nfree > 0) + cv_wait(&b->tqbucket_cv, &b->tqbucket_lock); + mutex_exit(&b->tqbucket_lock); + mutex_destroy(&b->tqbucket_lock); + cv_destroy(&b->tqbucket_cv); + } + + if (tq->tq_buckets != NULL) { + ASSERT(tq->tq_flags & TASKQ_DYNAMIC); + kmem_free(tq->tq_buckets, + sizeof (taskq_bucket_t) * tq->tq_nbuckets); + + /* Cleanup fields before returning tq to the cache */ + tq->tq_buckets = NULL; + tq->tq_tcreates = 0; + tq->tq_tdeaths = 0; + } else { + ASSERT(!(tq->tq_flags & TASKQ_DYNAMIC)); + } + + /* + * Now that all the taskq threads are gone, we can + * drop the zone hold taken in taskq_create_common + */ +#ifndef __APPLE__ + zone_rele(tq->tq_proc->p_zone); +#endif + + tq->tq_threads_ncpus_pct = 0; + tq->tq_totaltime = 0; + tq->tq_tasks = 0; + tq->tq_maxtasks = 0; + tq->tq_executed = 0; + kmem_cache_free(taskq_cache, tq); +} + +/* + * Extend a bucket with a new entry on the free list and attach a worker thread + * to it. + * + * Argument: pointer to the bucket. + * + * This function may quietly fail. It is only used by taskq_dispatch() which + * handles such failures properly. + */ +static void +taskq_bucket_extend(void *arg) +{ + taskq_ent_t *tqe; + taskq_bucket_t *b = (taskq_bucket_t *)arg; + taskq_t *tq = b->tqbucket_taskq; + int nthreads; +#ifdef __APPLE__ + kthread_t *thread; +#endif + + if (! ENOUGH_MEMORY()) { + TQ_STAT(b, tqs_nomem); + return; + } + + mutex_enter(&tq->tq_lock); + + /* + * Observe global taskq limits on the number of threads. + */ + if (tq->tq_tcreates++ - tq->tq_tdeaths > tq->tq_maxsize) { + tq->tq_tcreates--; + mutex_exit(&tq->tq_lock); + return; + } + mutex_exit(&tq->tq_lock); + + tqe = kmem_cache_alloc(taskq_ent_cache, KM_NOSLEEP); + + if (tqe == NULL) { + mutex_enter(&tq->tq_lock); + TQ_STAT(b, tqs_nomem); + tq->tq_tcreates--; + mutex_exit(&tq->tq_lock); + return; + } + + ASSERT(tqe->tqent_thread == NULL); + + tqe->tqent_un.tqent_bucket = b; + +#ifdef __APPLE__ + /* + * There's no way in Mac OS X KPI to create a thread + * in a suspended state (TS_STOPPED). So instead we + * use tqent_thread as a flag and the thread must wait + * for it to be initialized (below). + */ + tqe->tqent_thread = (kthread_t *)0xCEDEC0DE; + thread = thread_create_named(tq->tq_name, + NULL, 0, (void (*)(void *))taskq_d_thread, + tqe, 0, pp0, TS_RUN, tq->tq_pri); + + set_taskq_thread_attributes(thread, tq); +#else + + /* + * Create a thread in a TS_STOPPED state first. If it is successfully + * created, place the entry on the free list and start the thread. + */ + tqe->tqent_thread = thread_create(NULL, 0, taskq_d_thread, tqe, + 0, tq->tq_proc, TS_STOPPED, tq->tq_pri); +#endif /* __APPLE__ */ + + /* + * Once the entry is ready, link it to the the bucket free list. + */ + mutex_enter(&b->tqbucket_lock); + tqe->tqent_func = NULL; + TQ_APPEND(b->tqbucket_freelist, tqe); + b->tqbucket_nfree++; + TQ_STAT(b, tqs_tcreates); + +#if TASKQ_STATISTIC + nthreads = b->tqbucket_stat.tqs_tcreates - + b->tqbucket_stat.tqs_tdeaths; + b->tqbucket_stat.tqs_maxthreads = MAX(nthreads, + b->tqbucket_stat.tqs_maxthreads); +#endif + + mutex_exit(&b->tqbucket_lock); + /* + * Start the stopped thread. + */ +#ifdef __APPLE__ + mutex_enter(&tqe->tqent_thread_lock); + tqe->tqent_thread = thread; + cv_signal(&tqe->tqent_thread_cv); + mutex_exit(&tqe->tqent_thread_lock); +#else + thread_lock(tqe->tqent_thread); + tqe->tqent_thread->t_taskq = tq; + tqe->tqent_thread->t_schedflag |= TS_ALLSTART; + setrun_locked(tqe->tqent_thread); + thread_unlock(tqe->tqent_thread); +#endif /* __APPLE__ */ +} + +static int +taskq_kstat_update(kstat_t *ksp, int rw) +{ + struct taskq_kstat *tqsp = &taskq_kstat; + taskq_t *tq = ksp->ks_private; + + if (rw == KSTAT_WRITE) + return (EACCES); + +#ifdef __APPLE__ + tqsp->tq_pid.value.ui64 = 0; /* kernel_task'd pid is 0 */ +#else + tqsp->tq_pid.value.ui64 = proc_pid(tq->tq_proc->p_pid); +#endif + tqsp->tq_tasks.value.ui64 = tq->tq_tasks; + tqsp->tq_executed.value.ui64 = tq->tq_executed; + tqsp->tq_maxtasks.value.ui64 = tq->tq_maxtasks; + tqsp->tq_totaltime.value.ui64 = tq->tq_totaltime; + tqsp->tq_nactive.value.ui64 = tq->tq_active; + tqsp->tq_nalloc.value.ui64 = tq->tq_nalloc; + tqsp->tq_pri.value.ui64 = tq->tq_pri; + tqsp->tq_nthreads.value.ui64 = tq->tq_nthreads; + return (0); +} + +static int +taskq_d_kstat_update(kstat_t *ksp, int rw) +{ + struct taskq_d_kstat *tqsp = &taskq_d_kstat; + taskq_t *tq = ksp->ks_private; + taskq_bucket_t *b = tq->tq_buckets; + int bid = 0; + + if (rw == KSTAT_WRITE) + return (EACCES); + + ASSERT(tq->tq_flags & TASKQ_DYNAMIC); + + tqsp->tqd_btasks.value.ui64 = tq->tq_tasks; + tqsp->tqd_bexecuted.value.ui64 = tq->tq_executed; + tqsp->tqd_bmaxtasks.value.ui64 = tq->tq_maxtasks; + tqsp->tqd_bnalloc.value.ui64 = tq->tq_nalloc; + tqsp->tqd_bnactive.value.ui64 = tq->tq_active; + tqsp->tqd_btotaltime.value.ui64 = tq->tq_totaltime; + tqsp->tqd_pri.value.ui64 = tq->tq_pri; + + tqsp->tqd_hits.value.ui64 = 0; + tqsp->tqd_misses.value.ui64 = 0; + tqsp->tqd_overflows.value.ui64 = 0; + tqsp->tqd_tcreates.value.ui64 = 0; + tqsp->tqd_tdeaths.value.ui64 = 0; + tqsp->tqd_maxthreads.value.ui64 = 0; + tqsp->tqd_nomem.value.ui64 = 0; + tqsp->tqd_disptcreates.value.ui64 = 0; + tqsp->tqd_totaltime.value.ui64 = 0; + tqsp->tqd_nalloc.value.ui64 = 0; + tqsp->tqd_nfree.value.ui64 = 0; + + for (; (b != NULL) && (bid < tq->tq_nbuckets); b++, bid++) { + tqsp->tqd_hits.value.ui64 += b->tqbucket_stat.tqs_hits; + tqsp->tqd_misses.value.ui64 += b->tqbucket_stat.tqs_misses; + tqsp->tqd_overflows.value.ui64 += b->tqbucket_stat.tqs_overflow; + tqsp->tqd_tcreates.value.ui64 += b->tqbucket_stat.tqs_tcreates; + tqsp->tqd_tdeaths.value.ui64 += b->tqbucket_stat.tqs_tdeaths; + tqsp->tqd_maxthreads.value.ui64 += + b->tqbucket_stat.tqs_maxthreads; + tqsp->tqd_nomem.value.ui64 += b->tqbucket_stat.tqs_nomem; + tqsp->tqd_disptcreates.value.ui64 += + b->tqbucket_stat.tqs_disptcreates; + tqsp->tqd_totaltime.value.ui64 += b->tqbucket_totaltime; + tqsp->tqd_nalloc.value.ui64 += b->tqbucket_nalloc; + tqsp->tqd_nfree.value.ui64 += b->tqbucket_nfree; + } + return (0); +} + +int +EMPTY_TASKQ(taskq_t *tq) +{ +#ifdef _KERNEL + return ((tq)->tq_task.tqent_next == &(tq)->tq_task); +#else + return (tq->tq_task.tqent_next == &tq->tq_task || tq->tq_active == 0); +#endif +} diff --git a/module/os/macos/spl/spl-thread.c b/module/os/macos/spl/spl-thread.c new file mode 100644 index 000000000000..f5189cfac943 --- /dev/null +++ b/module/os/macos/spl/spl-thread.c @@ -0,0 +1,292 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2008 MacZFS + * Copyright (C) 2013, 2020 Jorgen Lundman + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +uint64_t zfs_threads = 0; + +kthread_t * +spl_thread_create_named( + char *name, + caddr_t stk, + size_t stksize, + void (*proc)(void *), + void *arg, + size_t len, + int state, +#ifdef SPL_DEBUG_THREAD + char *filename, + int line, +#endif + pri_t pri) +{ + kern_return_t result; + thread_t thread; + +#ifdef SPL_DEBUG_THREAD + printf("Start thread pri %d by '%s':%d\n", pri, + filename, line); +#endif + + result = kernel_thread_start((thread_continue_t)proc, arg, &thread); + + if (result != KERN_SUCCESS) + return (NULL); + + set_thread_importance_named(thread, pri, "anonymous new zfs thread"); + + if (name == NULL) + name = "unnamed zfs thread"; + +#if defined(MAC_OS_X_VERSION_10_15) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15) + thread_set_thread_name(thread, name); +#endif + + thread_deallocate(thread); + + atomic_inc_64(&zfs_threads); + + return ((kthread_t *)thread); +} + +kthread_t * +spl_current_thread(void) +{ + thread_t cur_thread = current_thread(); + return ((kthread_t *)cur_thread); +} + +void +spl_thread_exit(void) +{ + atomic_dec_64(&zfs_threads); + + tsd_thread_exit(); + (void) thread_terminate(current_thread()); +} + + +/* + * IllumOS has callout.c - place it here until we find a better place + */ +callout_id_t +timeout_generic(int type, void (*func)(void *), void *arg, + hrtime_t expiration, hrtime_t resolution, int flags) +{ + struct timespec ts; + hrt2ts(expiration, &ts); + bsd_timeout(func, arg, &ts); + /* + * bsd_untimeout() requires func and arg to cancel the timeout, so + * pass it back as the callout_id. If we one day were to implement + * untimeout_generic() they would pass it back to us + */ + return ((callout_id_t)arg); +} + +#if defined(MACOS_IMPURE) +extern void throttle_set_thread_io_policy(int priority); +#endif + +void +spl_throttle_set_thread_io_policy(int priority) +{ +#if defined(MACOS_IMPURE) + throttle_set_thread_io_policy(priority); +#endif +} + + +/* + * Set xnu kernel thread importance based on openzfs pri_t. + * + * Thread importance adjusts upwards and downards from + * BASEPRI_KERNEL (defined as 81). + * + * Many important kernel tasks run at BASEPRI_KERNEL, + * with networking and kernel graphics (Metal etc) running + * at BASEPRI_KERNEL + 1. + * + * We want maxclsyspri threads to have less xnu priority + * BASEPRI_KERNEL, so as to avoid UI stuttering, network + * disconnection and other side-effects of high zfs load with + * high thread priority. + * + * In we define maxclsyspri to 80 with + * defclsyspri and minclsyspri set below that. + */ + +void +set_thread_importance_named(thread_t thread, pri_t pri, char *name) +{ + thread_precedence_policy_data_t policy = { 0 }; + + /* + * start by finding an offset from BASEPRI_KERNEL, + * which is found in osfmk/kern/sched.h + */ + + policy.importance = pri - 81; + + /* dont let ANY of our threads run as high as networking & GPU */ + if (policy.importance > 0) + policy.importance = 0; + else if (policy.importance < (-11)) + policy.importance = -11; + + int i = policy.importance; + kern_return_t pol_prec_kret = thread_policy_set(thread, + THREAD_PRECEDENCE_POLICY, + (thread_policy_t)&policy, + THREAD_PRECEDENCE_POLICY_COUNT); + if (pol_prec_kret != KERN_SUCCESS) { + printf("SPL: %s:%d: ERROR failed to set" + " thread precedence to %d ret %d name %s\n", + __func__, __LINE__, i, pol_prec_kret, name); + } +} + +void +set_thread_importance(thread_t thread, pri_t pri) +{ + set_thread_importance_named(thread, pri, "anonymous zfs thread"); +} + +/* + * Set a kernel throughput qos for this thread, + */ + +void +set_thread_throughput_named(thread_t thread, + thread_throughput_qos_t throughput, char *name) +{ + /* + * TIERs: 0 is USER_INTERACTIVE, 1 is USER_INITIATED, 1 is LEGACY, + * 2 is UTILITY, 5 is BACKGROUND, 5 is MAINTENANCE + * + * (from xnu/osfmk/kern/thread_policy.c) + */ + + thread_throughput_qos_policy_data_t qosp = { 0 }; + qosp.thread_throughput_qos_tier = throughput; + + kern_return_t qoskret = thread_policy_set(thread, + THREAD_THROUGHPUT_QOS_POLICY, + (thread_policy_t)&qosp, + THREAD_THROUGHPUT_QOS_POLICY_COUNT); + if (qoskret != KERN_SUCCESS) { + printf("SPL: %s:%d: WARNING failed to set" + " thread throughput policy retval: %d " + " (THREAD_THROUGHPUT_QOS_POLICY %x), %s\n", + __func__, __LINE__, qoskret, + qosp.thread_throughput_qos_tier, name); + } +} + +void +set_thread_throughput(thread_t thread, + thread_throughput_qos_t throughput) +{ + set_thread_throughput_named(thread, throughput, + "anonymous zfs function"); +} + +void +set_thread_latency_named(thread_t thread, + thread_latency_qos_t latency, char *name) +{ + /* + * TIERs: 0 is USER_INTERACTIVE, 1 is USER_INITIATED, 1 is LEGACY, + * 3 is UTILITY, 3 is BACKGROUND, 5 is MAINTENANCE + * + * (from xnu/osfmk/kern/thread_policy.c) + * NB: these differ from throughput tier mapping + */ + + thread_latency_qos_policy_data_t qosp = { 0 }; + qosp.thread_latency_qos_tier = latency; + kern_return_t qoskret = thread_policy_set(thread, + THREAD_LATENCY_QOS_POLICY, + (thread_policy_t)&qosp, + THREAD_LATENCY_QOS_POLICY_COUNT); + if (qoskret != KERN_SUCCESS) { + printf("SPL: %s:%d: WARNING failed to set" + " thread latency policy retval: %d " + " (THREAD_LATENCY_QOS_POLICY %x), %s", + __func__, __LINE__, + qoskret, qosp.thread_latency_qos_tier, + name); + } +} + +void +set_thread_latency(thread_t thread, + thread_latency_qos_t latency) +{ + set_thread_latency_named(thread, latency, "anonymous zfs function"); +} + +/* + * XNU will dynamically adjust TIMESHARE + * threads around the chosen thread priority. + * The lower the importance (signed value), + * the more XNU will adjust a thread. + * Threads may be adjusted *upwards* from their + * base priority by XNU as well. + */ + +void +set_thread_timeshare_named(thread_t thread, char *name) +{ + thread_extended_policy_data_t policy = { .timeshare = TRUE }; + kern_return_t kret = thread_policy_set(thread, + THREAD_EXTENDED_POLICY, + (thread_policy_t)&policy, + THREAD_EXTENDED_POLICY_COUNT); + if (kret != KERN_SUCCESS) { + printf("SPL: %s:%d: WARNING failed to set" + " timeshare policy retval: %d, %s\n", + __func__, __LINE__, kret, name); + } +} + +void +set_thread_timeshare(thread_t thread) +{ + set_thread_timeshare_named(thread, "anonymous zfs function"); +} diff --git a/module/os/macos/spl/spl-time.c b/module/os/macos/spl/spl-time.c new file mode 100644 index 000000000000..151691d60b6c --- /dev/null +++ b/module/os/macos/spl/spl-time.c @@ -0,0 +1,138 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2008 MacZFS + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#include +#include +#include + +/* + * gethrtime() provides high-resolution timestamps with + * machine-dependent origin Hence its primary use is to specify + * intervals. + */ + +static hrtime_t +zfs_abs_to_nano(uint64_t elapsed) +{ + static mach_timebase_info_data_t sTimebaseInfo = { 0, 0 }; + + /* + * If this is the first time we've run, get the timebase. + * We can use denom == 0 to indicate that sTimebaseInfo is + * uninitialised because it makes no sense to have a zero + * denominator in a fraction. + */ + + if (sTimebaseInfo.denom == 0) { + (void) clock_timebase_info(&sTimebaseInfo); + } + + /* + * Convert to nanoseconds. + * return (elapsed * (uint64_t)sTimebaseInfo.numer) / + * (uint64_t)sTimebaseInfo.denom; + * + * Provided the final result is representable in 64 bits the + * following maneuver will deliver that result without intermediate + * overflow. + */ + if (sTimebaseInfo.denom == sTimebaseInfo.numer) + return (elapsed); + else if (sTimebaseInfo.denom == 1) + return (elapsed * (uint64_t)sTimebaseInfo.numer); + else { + /* Decompose elapsed = eta32 * 2^32 + eps32: */ + uint64_t eta32 = elapsed >> 32; + uint64_t eps32 = elapsed & 0x00000000ffffffffLL; + + uint32_t numer = sTimebaseInfo.numer; + uint32_t denom = sTimebaseInfo.denom; + + /* Form product of elapsed64 (decomposed) and numer: */ + uint64_t mu64 = numer * eta32; + uint64_t lambda64 = numer * eps32; + + /* Divide the constituents by denom: */ + uint64_t q32 = mu64/denom; + uint64_t r32 = mu64 - (q32 * denom); /* mu64 % denom */ + + return ((q32 << 32) + ((r32 << 32) + lambda64) / denom); + } +} + + +hrtime_t +gethrtime(void) +{ + static uint64_t start = 0; + if (start == 0) + start = mach_absolute_time(); + return (zfs_abs_to_nano(mach_absolute_time() - start)); +} + + +void +gethrestime(struct timespec *ts) +{ + nanotime(ts); +} + +time_t +gethrestime_sec(void) +{ + struct timeval tv; + + microtime(&tv); + return (tv.tv_sec); +} + +void +hrt2ts(hrtime_t hrt, struct timespec *tsp) +{ + uint32_t sec, nsec, tmp; + + tmp = (uint32_t)(hrt >> 30); + sec = tmp - (tmp >> 2); + sec = tmp - (sec >> 5); + sec = tmp + (sec >> 1); + sec = tmp - (sec >> 6) + 7; + sec = tmp - (sec >> 3); + sec = tmp + (sec >> 1); + sec = tmp + (sec >> 3); + sec = tmp + (sec >> 4); + tmp = (sec << 7) - sec - sec - sec; + tmp = (tmp << 7) - tmp - tmp - tmp; + tmp = (tmp << 7) - tmp - tmp - tmp; + nsec = (uint32_t)hrt - (tmp << 9); + while (nsec >= NANOSEC) { + nsec -= NANOSEC; + sec++; + } + tsp->tv_sec = (time_t)sec; + tsp->tv_nsec = nsec; +} diff --git a/module/os/macos/spl/spl-tsd.c b/module/os/macos/spl/spl-tsd.c new file mode 100644 index 000000000000..6ca970a9f96e --- /dev/null +++ b/module/os/macos/spl/spl-tsd.c @@ -0,0 +1,389 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2014 Jorgen Lundman + * + * A thread will call tsd_create(&key, dtor) to allocate a new + * "variable" placement, called a "key". In illumos, this is the index + * into an array of dtors. (If dtor is passed as NULL, TSD internally + * set it to an empty function). So if the dtor array[i] is NULL, it + * is "free" and can be allocated. (returned as *key = i). + * illumos will grow this dtor array with realloc when required. + * Then Any Thread can set a value on this "key index", and this value + * is specific to each thread by calling tsd_set(key, value). + * And can be retrieved with tsd_get(key). + * When tsd_destroy(key) is called, we need to loop through all + * threads different "values", and call the dtor on each one. + * Likewise, we need to know when a thread exists, so we can clean up + * the values (by calling dtor for each one) so we patch into the + * thread_exit() call, to also call tsd_thread_exit(). + * + * In OsX, we build an array of the dtors, and return the key index, + * this is to store the dtor, and know which "key" values are valid. + * Then we build an AVL tree, indexed by , to store + * each thread's value. This allows us to do key access quick. + * On thread_exit, we iterate the dtor array, and for each key + * remove . + * On tsd_destroy(key), we use AVL find nearest with , then + * avl_next as long as key remains the same, to remove each thread value. + * + * Note a key of "0" is considered "invalid" in IllumOS, so we return + * a "1" based index, even though internally it is 0 based. + * + */ + +#include +#include +#include +#include +#include + +/* Initial size of array, and realloc growth size */ +#define TSD_ALLOC_SIZE 10 + +/* array of dtors, allocated in init */ +static dtor_func_t *tsd_dtor_array = NULL; +static uint32_t tsd_dtor_size = 0; +static avl_tree_t tsd_tree; + +struct spl_tsd_node_s +{ + /* The index/key */ + uint_t tsd_key; + thread_t tsd_thread; + + /* The payload */ + void *tsd_value; + + /* Internal mumbo */ + avl_node_t tsd_link_node; +}; +typedef struct spl_tsd_node_s spl_tsd_node_t; + +static kmutex_t spl_tsd_mutex; + +/* + * tsd_set - set thread specific data + * @key: lookup key + * @value: value to set + * + * Caller must prevent racing tsd_create() or tsd_destroy(), protected + * from racing tsd_get() or tsd_set() because it is thread specific. + * This function has been optimized to be fast for the update case. + * When setting the tsd initially it will be slower due to additional + * required locking and potential memory allocations. + * If the value is set to NULL, we also release it. + */ +int +tsd_set(uint_t key, void *value) +{ + spl_tsd_node_t *entry = NULL; + spl_tsd_node_t search; + avl_index_t loc; + uint_t i; + + /* Invalid key values? */ + if ((key < 1) || + (key >= tsd_dtor_size)) { + return (EINVAL); + } + + i = key - 1; + + /* + * First handle the easy case, already has a node/value + * so we just need to find it, update it. + */ + + search.tsd_key = i; + search.tsd_thread = current_thread(); + + mutex_enter(&spl_tsd_mutex); + entry = avl_find(&tsd_tree, &search, &loc); + mutex_exit(&spl_tsd_mutex); + + if (entry) { + + /* If value is set to NULL, release it as well */ + if (value == NULL) { + mutex_enter(&spl_tsd_mutex); + avl_remove(&tsd_tree, entry); + mutex_exit(&spl_tsd_mutex); + kmem_free(entry, sizeof (*entry)); + return (0); + } + entry->tsd_value = value; + return (0); + } + + /* No node, we need to create a new one and insert it. */ + /* But if the value is NULL, then why create one eh? */ + if (value == NULL) + return (0); + + entry = kmem_alloc(sizeof (spl_tsd_node_t), KM_SLEEP); + + entry->tsd_key = i; + entry->tsd_thread = current_thread(); + entry->tsd_value = value; + + mutex_enter(&spl_tsd_mutex); + avl_add(&tsd_tree, entry); + mutex_exit(&spl_tsd_mutex); + + return (0); +} + +/* + * tsd_get - get thread specific data for specified thread + * @key: lookup key + * + * Caller must prevent racing tsd_create() or tsd_destroy(). This + * implementation is designed to be fast and scalable, it does not + * lock the entire table only a single hash bin. + */ +void * +tsd_get_by_thread(uint_t key, thread_t thread) +{ + spl_tsd_node_t *entry = NULL; + spl_tsd_node_t search; + avl_index_t loc; + uint_t i; + + /* Invalid key values? */ + if ((key < 1) || + (key >= tsd_dtor_size)) { + return (NULL); + } + + i = key - 1; + + search.tsd_key = i; + search.tsd_thread = thread; + + mutex_enter(&spl_tsd_mutex); + entry = avl_find(&tsd_tree, &search, &loc); + mutex_exit(&spl_tsd_mutex); + + return (entry ? entry->tsd_value : NULL); +} + +void * +tsd_get(uint_t key) +{ + return (tsd_get_by_thread(key, current_thread())); +} + +static void +tsd_internal_dtor(void *value) +{ +} + +/* + * Create TSD for a pid and fill in key with unique value, remember the dtor + * + * We cheat and create an entry with pid=0, to keep the dtor. + */ +void +tsd_create(uint_t *keyp, dtor_func_t dtor) +{ + uint_t i; + + if (*keyp) + return; + + // Iterate the dtor_array, looking for first NULL + for (i = 0; i < TSD_ALLOC_SIZE; i++) { + if (tsd_dtor_array[i] == NULL) break; + } + + /* Do we need to grow the list? */ + if (i >= tsd_dtor_size) { + printf("SPL: tsd list growing not implemented\n"); + return; + } + + if (dtor == NULL) + dtor = tsd_internal_dtor; + + tsd_dtor_array[i] = dtor; + + *keyp = i + 1; +} + +void +tsd_destroy(uint_t *keyp) +{ + spl_tsd_node_t *entry = NULL, *next = NULL; + spl_tsd_node_t search; + avl_index_t loc; + dtor_func_t dtor = NULL; + uint_t i; + + /* Invalid key values? */ + if ((*keyp < 1) || + (*keyp >= tsd_dtor_size)) { + return; + } + + i = *keyp - 1; + *keyp = 0; + + ASSERT(tsd_dtor_array[i] != NULL); + + dtor = tsd_dtor_array[i]; + tsd_dtor_array[i] = NULL; + + /* + * For each thread; + * if it has a value + * call the dtor + */ + search.tsd_key = i; + search.tsd_thread = NULL; + + mutex_enter(&spl_tsd_mutex); + entry = avl_find(&tsd_tree, &search, &loc); + + /* + * "entry" should really be NULL here, as we searched for the + * NULL thread + */ + if (entry == NULL) + entry = avl_nearest(&tsd_tree, loc, AVL_AFTER); + + /* Now, free node, and go to next, as long as the key matches */ + while (entry && (entry->tsd_key == i)) { + next = AVL_NEXT(&tsd_tree, entry); + + /* If we have a value, call the dtor for this thread */ + if (entry->tsd_value) + dtor(entry->tsd_value); + + avl_remove(&tsd_tree, entry); + + kmem_free(entry, sizeof (*entry)); + + entry = next; + } + + mutex_exit(&spl_tsd_mutex); +} + + + +/* + * A thread is exiting, clear out any tsd values it might have. + */ +void +tsd_thread_exit(void) +{ + spl_tsd_node_t *entry = NULL; + spl_tsd_node_t search; + avl_index_t loc; + int i; + + search.tsd_thread = current_thread(); + + /* For all defined dtor/values */ + for (i = 0; i < tsd_dtor_size; i++) { + + /* If not allocated, skip */ + if (tsd_dtor_array[i] == NULL) continue; + + /* Find out of this thread has a value */ + search.tsd_key = i; + + mutex_enter(&spl_tsd_mutex); + entry = avl_find(&tsd_tree, &search, &loc); + if (entry) avl_remove(&tsd_tree, entry); + mutex_exit(&spl_tsd_mutex); + + if (entry == NULL) continue; + + /* If we have a value, call dtor */ + if (entry->tsd_value) + tsd_dtor_array[i](entry->tsd_value); + + kmem_free(entry, sizeof (*entry)); + } // for all i +} + +static int +tsd_tree_cmp(const void *arg1, const void *arg2) +{ + const spl_tsd_node_t *node1 = arg1; + const spl_tsd_node_t *node2 = arg2; + if (node1->tsd_key > node2->tsd_key) + return (1); + if (node1->tsd_key < node2->tsd_key) + return (-1); + if (node1->tsd_thread > node2->tsd_thread) + return (1); + if (node1->tsd_thread < node2->tsd_thread) + return (-1); + return (0); +} + +int +spl_tsd_init(void) +{ + tsd_dtor_array = kmem_zalloc(sizeof (dtor_func_t) * TSD_ALLOC_SIZE, + KM_SLEEP); + tsd_dtor_size = TSD_ALLOC_SIZE; + + mutex_init(&spl_tsd_mutex, NULL, MUTEX_DEFAULT, NULL); + avl_create(&tsd_tree, tsd_tree_cmp, + sizeof (spl_tsd_node_t), + offsetof(spl_tsd_node_t, tsd_link_node)); + return (0); +} + + +uint64_t +spl_tsd_size(void) +{ + return (avl_numnodes(&tsd_tree)); +} + +void +spl_tsd_fini(void) +{ + spl_tsd_node_t *entry = NULL; + void *cookie = NULL; + + printf("SPL: tsd unloading %llu\n", spl_tsd_size()); + + mutex_enter(&spl_tsd_mutex); + cookie = NULL; + while ((entry = avl_destroy_nodes(&tsd_tree, &cookie))) { + kmem_free(entry, sizeof (*entry)); + } + mutex_exit(&spl_tsd_mutex); + + avl_destroy(&tsd_tree); + mutex_destroy(&spl_tsd_mutex); + + kmem_free(tsd_dtor_array, sizeof (dtor_func_t) * tsd_dtor_size); + tsd_dtor_size = 0; +} diff --git a/module/os/macos/spl/spl-uio.c b/module/os/macos/spl/spl-uio.c new file mode 100644 index 000000000000..14175e08d263 --- /dev/null +++ b/module/os/macos/spl/spl-uio.c @@ -0,0 +1,139 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (c) 2015 by Chunwei Chen. All rights reserved. + * Copyright (C) 2021 Jorgen Lundman + * + */ + +#include +#include +#include + +static int +zfs_uiomove_iov(void *p, size_t n, zfs_uio_rw_t rw, zfs_uio_t *uio) +{ + const struct iovec *iov = uio->uio_iov; + size_t skip = uio->uio_skip; + int cnt; + + while (n && uio->uio_resid) { + cnt = MIN(iov->iov_len - skip, n); + switch (uio->uio_segflg) { + case UIO_SYSSPACE: + if (rw == UIO_READ) + bcopy(p, iov->iov_base + skip, cnt); + else + bcopy(iov->iov_base + skip, (void *)p, + cnt); + break; + default: + VERIFY(0); + return (-1); + } + skip += cnt; + if (skip == iov->iov_len) { + skip = 0; + uio->uio_iov = (++iov); + uio->uio_iovcnt--; + } + uio->uio_skip = skip; + uio->uio_resid -= cnt; + uio->uio_loffset += cnt; + p = (caddr_t)p + cnt; + n -= cnt; + } + return (0); +} + +int +zfs_uiomove(const char *p, size_t n, enum uio_rw rw, zfs_uio_t *uio) +{ + int result; + + if (uio->uio_iov == NULL) { + uio_setrw(uio->uio_xnu, rw); + result = uiomove(p, n, uio->uio_xnu); + return (SET_ERROR(result)); + } + + result = zfs_uiomove_iov((void *)p, n, rw, uio); + return (SET_ERROR(result)); +} + +/* + * same as uiomove() but doesn't modify uio structure. + * return in cbytes how many bytes were copied. + */ +int +zfs_uiocopy(const char *p, size_t n, enum uio_rw rw, zfs_uio_t *uio, + size_t *cbytes) +{ + int result; + if (uio->uio_iov == NULL) { + struct uio *nuio = uio_duplicate(uio->uio_xnu); + unsigned long long x = uio_resid(uio->uio_xnu); + if (!nuio) + return (ENOMEM); + uio_setrw(nuio, rw); + result = uiomove(p, n, nuio); + *cbytes = x-uio_resid(nuio); + uio_free(nuio); + return (result); + } + + zfs_uio_t uio_copy; + + bcopy(uio, &uio_copy, sizeof (zfs_uio_t)); + result = zfs_uiomove_iov((void *)p, n, rw, &uio_copy); + + *cbytes = uio->uio_resid - uio_copy.uio_resid; + + return (result); +} + +void +zfs_uioskip(zfs_uio_t *uio, size_t n) +{ + if (uio->uio_iov == NULL) { + uio_update(uio->uio_xnu, n); + } else { + if (n > uio->uio_resid) + return; + uio->uio_skip += n; + while (uio->uio_iovcnt && + uio->uio_skip >= uio->uio_iov->iov_len) { + uio->uio_skip -= uio->uio_iov->iov_len; + uio->uio_iov++; + uio->uio_iovcnt--; + } + uio->uio_loffset += n; + uio->uio_resid -= n; + } +} + +int +zfs_uio_prefaultpages(ssize_t n, zfs_uio_t *uio) +{ + return (0); +} diff --git a/module/os/macos/spl/spl-vmem.c b/module/os/macos/spl/spl-vmem.c new file mode 100644 index 000000000000..ecaf5b23f94b --- /dev/null +++ b/module/os/macos/spl/spl-vmem.c @@ -0,0 +1,3923 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright (c) 2017 Sean Doran + */ + +/* + * Big Theory Statement for the virtual memory allocator. + * + * For a more complete description of the main ideas, see: + * + * Jeff Bonwick and Jonathan Adams, + * + * Magazines and vmem: Extending the Slab Allocator to Many CPUs and + * Arbitrary Resources. + * + * Proceedings of the 2001 Usenix Conference. + * Available as http://www.usenix.org/event/usenix01/bonwick.html + * + * + * 1. General Concepts + * ------------------- + * + * 1.1 Overview + * ------------ + * We divide the kernel address space into a number of logically distinct + * pieces, or *arenas*: text, data, heap, stack, and so on. Within these + * arenas we often subdivide further; for example, we use heap addresses + * not only for the kernel heap (kmem_alloc() space), but also for DVMA, + * bp_mapin(), /dev/kmem, and even some device mappings like the TOD chip. + * The kernel address space, therefore, is most accurately described as + * a tree of arenas in which each node of the tree *imports* some subset + * of its parent. The virtual memory allocator manages these arenas and + * supports their natural hierarchical structure. + * + * 1.2 Arenas + * ---------- + * An arena is nothing more than a set of integers. These integers most + * commonly represent virtual addresses, but in fact they can represent + * anything at all. For example, we could use an arena containing the + * integers minpid through maxpid to allocate process IDs. vmem_create() + * and vmem_destroy() create and destroy vmem arenas. In order to + * differentiate between arenas used for adresses and arenas used for + * identifiers, the VMC_IDENTIFIER flag is passed to vmem_create(). This + * prevents identifier exhaustion from being diagnosed as general memory + * failure. + * + * 1.3 Spans + * --------- + * We represent the integers in an arena as a collection of *spans*, or + * contiguous ranges of integers. For example, the kernel heap consists + * of just one span: [kernelheap, ekernelheap). Spans can be added to an + * arena in two ways: explicitly, by vmem_add(), or implicitly, by + * importing, as described in Section 1.5 below. + * + * 1.4 Segments + * ------------ + * Spans are subdivided into *segments*, each of which is either allocated + * or free. A segment, like a span, is a contiguous range of integers. + * Each allocated segment [addr, addr + size) represents exactly one + * vmem_alloc(size) that returned addr. Free segments represent the space + * between allocated segments. If two free segments are adjacent, we + * coalesce them into one larger segment; that is, if segments [a, b) and + * [b, c) are both free, we merge them into a single segment [a, c). + * The segments within a span are linked together in increasing-address order + * so we can easily determine whether coalescing is possible. + * + * Segments never cross span boundaries. When all segments within + * an imported span become free, we return the span to its source. + * + * 1.5 Imported Memory + * ------------------- + * As mentioned in the overview, some arenas are logical subsets of + * other arenas. For example, kmem_va_arena (a virtual address cache + * that satisfies most kmem_slab_create() requests) is just a subset + * of heap_arena (the kernel heap) that provides caching for the most + * common slab sizes. When kmem_va_arena runs out of virtual memory, + * it *imports* more from the heap; we say that heap_arena is the + * *vmem source* for kmem_va_arena. vmem_create() allows you to + * specify any existing vmem arena as the source for your new arena. + * Topologically, since every arena is a child of at most one source, + * the set of all arenas forms a collection of trees. + * + * 1.6 Constrained Allocations + * --------------------------- + * Some vmem clients are quite picky about the kind of address they want. + * For example, the DVMA code may need an address that is at a particular + * phase with respect to some alignment (to get good cache coloring), or + * that lies within certain limits (the addressable range of a device), + * or that doesn't cross some boundary (a DMA counter restriction) -- + * or all of the above. vmem_xalloc() allows the client to specify any + * or all of these constraints. + * + * 1.7 The Vmem Quantum + * -------------------- + * Every arena has a notion of 'quantum', specified at vmem_create() time, + * that defines the arena's minimum unit of currency. Most commonly the + * quantum is either 1 or PAGESIZE, but any power of 2 is legal. + * All vmem allocations are guaranteed to be quantum-aligned. + * + * 1.8 Quantum Caching + * ------------------- + * A vmem arena may be so hot (frequently used) that the scalability of vmem + * allocation is a significant concern. We address this by allowing the most + * common allocation sizes to be serviced by the kernel memory allocator, + * which provides low-latency per-cpu caching. The qcache_max argument to + * vmem_create() specifies the largest allocation size to cache. + * + * 1.9 Relationship to Kernel Memory Allocator + * ------------------------------------------- + * Every kmem cache has a vmem arena as its slab supplier. The kernel memory + * allocator uses vmem_alloc() and vmem_free() to create and destroy slabs. + * + * + * 2. Implementation + * ----------------- + * + * 2.1 Segment lists and markers + * ----------------------------- + * The segment structure (vmem_seg_t) contains two doubly-linked lists. + * + * The arena list (vs_anext/vs_aprev) links all segments in the arena. + * In addition to the allocated and free segments, the arena contains + * special marker segments at span boundaries. Span markers simplify + * coalescing and importing logic by making it easy to tell both when + * we're at a span boundary (so we don't coalesce across it), and when + * a span is completely free (its neighbors will both be span markers). + * + * Imported spans will have vs_import set. + * + * The next-of-kin list (vs_knext/vs_kprev) links segments of the same type: + * (1) for allocated segments, vs_knext is the hash chain linkage; + * (2) for free segments, vs_knext is the freelist linkage; + * (3) for span marker segments, vs_knext is the next span marker. + * + * 2.2 Allocation hashing + * ---------------------- + * We maintain a hash table of all allocated segments, hashed by address. + * This allows vmem_free() to discover the target segment in constant time. + * vmem_update() periodically resizes hash tables to keep hash chains short. + * + * 2.3 Freelist management + * ----------------------- + * We maintain power-of-2 freelists for free segments, i.e. free segments + * of size >= 2^n reside in vmp->vm_freelist[n]. To ensure constant-time + * allocation, vmem_xalloc() looks not in the first freelist that *might* + * satisfy the allocation, but in the first freelist that *definitely* + * satisfies the allocation (unless VM_BESTFIT is specified, or all larger + * freelists are empty). For example, a 1000-byte allocation will be + * satisfied not from the 512..1023-byte freelist, whose members *might* + * contains a 1000-byte segment, but from a 1024-byte or larger freelist, + * the first member of which will *definitely* satisfy the allocation. + * This ensures that vmem_xalloc() works in constant time. + * + * We maintain a bit map to determine quickly which freelists are non-empty. + * vmp->vm_freemap & (1 << n) is non-zero iff vmp->vm_freelist[n] is non-empty. + * + * The different freelists are linked together into one large freelist, + * with the freelist heads serving as markers. Freelist markers simplify + * the maintenance of vm_freemap by making it easy to tell when we're taking + * the last member of a freelist (both of its neighbors will be markers). + * + * 2.4 Vmem Locking + * ---------------- + * For simplicity, all arena state is protected by a per-arena lock. + * For very hot arenas, use quantum caching for scalability. + * + * 2.5 Vmem Population + * ------------------- + * Any internal vmem routine that might need to allocate new segment + * structures must prepare in advance by calling vmem_populate(), which + * will preallocate enough vmem_seg_t's to get is through the entire + * operation without dropping the arena lock. + * + * 2.6 Auditing + * ------------ + * If KMF_AUDIT is set in kmem_flags, we audit vmem allocations as well. + * Since virtual addresses cannot be scribbled on, there is no equivalent + * in vmem to redzone checking, deadbeef, or other kmem debugging features. + * Moreover, we do not audit frees because segment coalescing destroys the + * association between an address and its segment structure. Auditing is + * thus intended primarily to keep track of who's consuming the arena. + * Debugging support could certainly be extended in the future if it proves + * necessary, but we do so much live checking via the allocation hash table + * that even non-DEBUG systems get quite a bit of sanity checking already. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VMEM_INITIAL 21 /* early vmem arenas */ +#define VMEM_SEG_INITIAL 800 + +/* + * Adding a new span to an arena requires two segment structures: one to + * represent the span, and one to represent the free segment it contains. + */ +#define VMEM_SEGS_PER_SPAN_CREATE 2 + +/* + * Allocating a piece of an existing segment requires 0-2 segment structures + * depending on how much of the segment we're allocating. + * + * To allocate the entire segment, no new segment structures are needed; we + * simply move the existing segment structure from the freelist to the + * allocation hash table. + * + * To allocate a piece from the left or right end of the segment, we must + * split the segment into two pieces (allocated part and remainder), so we + * need one new segment structure to represent the remainder. + * + * To allocate from the middle of a segment, we need two new segment strucures + * to represent the remainders on either side of the allocated part. + */ +#define VMEM_SEGS_PER_EXACT_ALLOC 0 +#define VMEM_SEGS_PER_LEFT_ALLOC 1 +#define VMEM_SEGS_PER_RIGHT_ALLOC 1 +#define VMEM_SEGS_PER_MIDDLE_ALLOC 2 + +/* + * vmem_populate() preallocates segment structures for vmem to do its work. + * It must preallocate enough for the worst case, which is when we must import + * a new span and then allocate from the middle of it. + */ +#define VMEM_SEGS_PER_ALLOC_MAX \ +(VMEM_SEGS_PER_SPAN_CREATE + VMEM_SEGS_PER_MIDDLE_ALLOC) + +/* + * The segment structures themselves are allocated from vmem_seg_arena, so + * we have a recursion problem when vmem_seg_arena needs to populate itself. + * We address this by working out the maximum number of segment structures + * this act will require, and multiplying by the maximum number of threads + * that we'll allow to do it simultaneously. + * + * The worst-case segment consumption to populate vmem_seg_arena is as + * follows (depicted as a stack trace to indicate why events are occurring): + * + * (In order to lower the fragmentation in the heap_arena, we specify a + * minimum import size for the vmem_metadata_arena which is the same size + * as the kmem_va quantum cache allocations. This causes the worst-case + * allocation from the vmem_metadata_arena to be 3 segments.) + * + * vmem_alloc(vmem_seg_arena) -> 2 segs (span create + exact alloc) + * segkmem_alloc(vmem_metadata_arena) + * vmem_alloc(vmem_metadata_arena) -> 3 segs (span create + left alloc) + * vmem_alloc(heap_arena) -> 1 seg (left alloc) + * page_create() + * hat_memload() + * kmem_cache_alloc() + * kmem_slab_create() + * vmem_alloc(hat_memload_arena) -> 2 segs (span create + exact alloc) + * segkmem_alloc(heap_arena) + * vmem_alloc(heap_arena) -> 1 seg (left alloc) + * page_create() + * hat_memload() -> (hat layer won't recurse further) + * + * The worst-case consumption for each arena is 3 segment structures. + * Of course, a 3-seg reserve could easily be blown by multiple threads. + * Therefore, we serialize all allocations from vmem_seg_arena (which is OK + * because they're rare). We cannot allow a non-blocking allocation to get + * tied up behind a blocking allocation, however, so we use separate locks + * for VM_SLEEP and VM_NOSLEEP allocations. Similarly, VM_PUSHPAGE allocations + * must not block behind ordinary VM_SLEEPs. In addition, if the system is + * panicking then we must keep enough resources for panic_thread to do its + * work. Thus we have at most four threads trying to allocate from + * vmem_seg_arena, and each thread consumes at most three segment structures, + * so we must maintain a 12-seg reserve. + */ +#define VMEM_POPULATE_RESERVE 12 + +/* + * vmem_populate() ensures that each arena has VMEM_MINFREE seg structures + * so that it can satisfy the worst-case allocation *and* participate in + * worst-case allocation from vmem_seg_arena. + */ +#define VMEM_MINFREE (VMEM_POPULATE_RESERVE + VMEM_SEGS_PER_ALLOC_MAX) + +static vmem_t vmem0[VMEM_INITIAL]; +static vmem_t *vmem_populator[VMEM_INITIAL]; +static uint32_t vmem_id; +static uint32_t vmem_populators; +static vmem_seg_t vmem_seg0[VMEM_SEG_INITIAL]; +static vmem_seg_t *vmem_segfree; +static kmutex_t vmem_list_lock; +static kmutex_t vmem_segfree_lock; +static kmutex_t vmem_sleep_lock; +static kmutex_t vmem_nosleep_lock; +static kmutex_t vmem_pushpage_lock; +static kmutex_t vmem_panic_lock; +static kmutex_t vmem_xnu_alloc_lock; +static vmem_t *vmem_list; +static vmem_t *vmem_metadata_arena; +static vmem_t *vmem_seg_arena; +static vmem_t *vmem_hash_arena; +static vmem_t *vmem_vmem_arena; +vmem_t *spl_default_arena; // The bottom-most arena for SPL +static vmem_t *spl_default_arena_parent; // dummy arena as a placeholder +#define VMEM_BUCKETS 13 +#define VMEM_BUCKET_LOWBIT 12 +#define VMEM_BUCKET_HIBIT 24 +static vmem_t *vmem_bucket_arena[VMEM_BUCKETS]; +vmem_t *spl_heap_arena; +static void *spl_heap_arena_initial_alloc; +static size_t spl_heap_arena_initial_alloc_size = 0; +#define NUMBER_OF_ARENAS_IN_VMEM_INIT 21 +/* vmem_update() every 15 seconds */ +static struct timespec vmem_update_interval = {15, 0}; +uint32_t vmem_mtbf; /* mean time between failures [default: off] */ +size_t vmem_seg_size = sizeof (vmem_seg_t); + +// must match with include/sys/vmem_impl.h +static vmem_kstat_t vmem_kstat_template = { + { "mem_inuse", KSTAT_DATA_UINT64 }, + { "mem_import", KSTAT_DATA_UINT64 }, + { "mem_total", KSTAT_DATA_UINT64 }, + { "vmem_source", KSTAT_DATA_UINT32 }, + { "alloc", KSTAT_DATA_UINT64 }, + { "free", KSTAT_DATA_UINT64 }, + { "wait", KSTAT_DATA_UINT64 }, + { "fail", KSTAT_DATA_UINT64 }, + { "lookup", KSTAT_DATA_UINT64 }, + { "search", KSTAT_DATA_UINT64 }, + { "populate_fail", KSTAT_DATA_UINT64 }, + { "contains", KSTAT_DATA_UINT64 }, + { "contains_search", KSTAT_DATA_UINT64 }, + { "parent_alloc", KSTAT_DATA_UINT64 }, + { "parent_free", KSTAT_DATA_UINT64 }, + { "threads_waiting", KSTAT_DATA_UINT64 }, + { "excess", KSTAT_DATA_UINT64 }, +}; + + +/* + * Insert/delete from arena list (type 'a') or next-of-kin list (type 'k'). + */ +#define VMEM_INSERT(vprev, vsp, type) \ +{ \ +vmem_seg_t *_vnext = (vprev)->vs_##type##next; \ +(vsp)->vs_##type##next = (_vnext); \ +(vsp)->vs_##type##prev = (vprev); \ +(vprev)->vs_##type##next = (vsp); \ +(_vnext)->vs_##type##prev = (vsp); \ +} + +#define VMEM_DELETE(vsp, type) \ +{ \ +vmem_seg_t *_vprev = (vsp)->vs_##type##prev; \ +vmem_seg_t *_vnext = (vsp)->vs_##type##next; \ +(_vprev)->vs_##type##next = (_vnext); \ +(_vnext)->vs_##type##prev = (_vprev); \ +} + +// vmem thread block count +uint64_t spl_vmem_threads_waiting = 0; + +// number of allocations > minalloc +uint64_t spl_bucket_non_pow2_allocs = 0; + +// allocator kstats +uint64_t spl_vmem_unconditional_allocs = 0; +uint64_t spl_vmem_unconditional_alloc_bytes = 0; +uint64_t spl_vmem_conditional_allocs = 0; +uint64_t spl_vmem_conditional_alloc_bytes = 0; +uint64_t spl_vmem_conditional_alloc_deny = 0; +uint64_t spl_vmem_conditional_alloc_deny_bytes = 0; + +// bucket allocator kstat +uint64_t spl_xat_success = 0; +uint64_t spl_xat_late_success = 0; +uint64_t spl_xat_late_success_nosleep = 0; +uint64_t spl_xat_pressured = 0; +uint64_t spl_xat_bailed = 0; +uint64_t spl_xat_bailed_contended = 0; +uint64_t spl_xat_lastalloc = 0; +uint64_t spl_xat_lastfree = 0; +uint64_t spl_xat_forced = 0; +uint64_t spl_xat_sleep = 0; +uint64_t spl_xat_late_deny = 0; +uint64_t spl_xat_no_waiters = 0; +uint64_t spl_xft_wait = 0; + +uint64_t spl_vba_parent_memory_appeared = 0; +uint64_t spl_vba_parent_memory_blocked = 0; +uint64_t spl_vba_hiprio_blocked = 0; +uint64_t spl_vba_cv_timeout = 0; +uint64_t spl_vba_loop_timeout = 0; +uint64_t spl_vba_cv_timeout_blocked = 0; +uint64_t spl_vba_loop_timeout_blocked = 0; +uint64_t spl_vba_sleep = 0; +uint64_t spl_vba_loop_entries = 0; + +// bucket minimum span size tunables +uint64_t spl_bucket_tunable_large_span = 0; +uint64_t spl_bucket_tunable_small_span = 0; + +// for XAT & XATB visibility into VBA queue +static _Atomic uint32_t spl_vba_threads[VMEM_BUCKETS] = { 0 }; +static uint32_t + vmem_bucket_id_to_bucket_number[NUMBER_OF_ARENAS_IN_VMEM_INIT] = { 0 }; +boolean_t spl_arc_no_grow(size_t, boolean_t, kmem_cache_t **); +_Atomic uint64_t spl_arc_no_grow_bits = 0; +uint64_t spl_arc_no_grow_count = 0; + +// compare span ages this many steps from the head of the freelist +uint64_t spl_frag_max_walk = 1000; +uint64_t spl_frag_walked_out = 0; +uint64_t spl_frag_walk_cnt = 0; + +extern void spl_free_set_emergency_pressure(int64_t p); +extern uint64_t segkmem_total_mem_allocated; +extern uint64_t total_memory; + +extern void IOSleep(unsigned milliseconds); + +/* + * Get a vmem_seg_t from the global segfree list. + */ +static vmem_seg_t * +vmem_getseg_global(void) +{ + vmem_seg_t *vsp; + + mutex_enter(&vmem_segfree_lock); + if ((vsp = vmem_segfree) != NULL) + vmem_segfree = vsp->vs_knext; + mutex_exit(&vmem_segfree_lock); + + if (vsp != NULL) + vsp->vs_span_createtime = 0; + + return (vsp); +} + +/* + * Put a vmem_seg_t on the global segfree list. + */ +static void +vmem_putseg_global(vmem_seg_t *vsp) +{ + mutex_enter(&vmem_segfree_lock); + vsp->vs_knext = vmem_segfree; + vmem_segfree = vsp; + mutex_exit(&vmem_segfree_lock); +} + +/* + * Get a vmem_seg_t from vmp's segfree list. + */ +static vmem_seg_t * +vmem_getseg(vmem_t *vmp) +{ + vmem_seg_t *vsp; + + ASSERT(vmp->vm_nsegfree > 0); + + vsp = vmp->vm_segfree; + vmp->vm_segfree = vsp->vs_knext; + vmp->vm_nsegfree--; + + return (vsp); +} + +/* + * Put a vmem_seg_t on vmp's segfree list. + */ +static void +vmem_putseg(vmem_t *vmp, vmem_seg_t *vsp) +{ + vsp->vs_knext = vmp->vm_segfree; + vmp->vm_segfree = vsp; + vmp->vm_nsegfree++; +} + + +/* + * Add vsp to the appropriate freelist, at the appropriate location, + * keeping the freelist sorted by age. + */ + +/* + * return true when we continue the for loop in + * vmem_freelist_insert_sort_by_time + */ +static inline bool +flist_sort_compare(bool newfirst, + const vmem_seg_t *vhead, + const vmem_seg_t *nextlist, + vmem_seg_t *p, vmem_seg_t *to_insert) +{ + /* + * vsp is the segment we are inserting into the freelist + * p is a freelist poniter or an element inside a non-empty freelist + * if we return false, then vsp is inserted immedaitely after p, + */ + + // always enter the for loop if we're at the front of a flist + if (p == vhead) + return (true); + + const vmem_seg_t *n = p->vs_knext; + + if (n == nextlist || n == NULL) { + // if we are at the tail of the flist, then + // insert vsp between p and n + return (false); + } + + if (n->vs_import == true && to_insert->vs_import == false) { + /* + * put non-imported segments before imported segments + * no matter what their respective create times are, + * thereby making imported segments more likely "age out" + */ + return (false); // inserts to_insert between p and n + } + + if (newfirst == true) { + if (n->vs_span_createtime < to_insert->vs_span_createtime) { + // n is older than me, so insert me between p and n + return (false); + } + } else { + if (n->vs_span_createtime > to_insert->vs_span_createtime) { + // n is newer than me, so insert me between p and n + return (false); + } + } + // continue iterating + return (true); +} + +static void +vmem_freelist_insert_sort_by_time(vmem_t *vmp, vmem_seg_t *vsp) +{ + ASSERT(vmp->vm_cflags & VMC_TIMEFREE); + ASSERT(vsp->vs_span_createtime > 0); + + const bool newfirst = 0 == (vmp->vm_cflags & VMC_OLDFIRST); + + const uint64_t abs_max_walk_steps = 1ULL << 30ULL; + uint32_t max_walk_steps = (uint32_t)MIN(spl_frag_max_walk, + abs_max_walk_steps); + + vmem_seg_t *vprev; + + ASSERT(*VMEM_HASH(vmp, vsp->vs_start) != vsp); + + /* + * in vmem_create_common() the freelists are arranged: + * freelist[0].vs_kprev = NULL, freelist[VMEM_FREELISTS].vs_knext = NULL + * freelist[1].vs_kprev = freelist[0], freelist[1].vs_knext = + * freelist[2] ... + * from vmem_freelist_insert(): + * VS_SIZE is the segment size (->vs_end - ->vs_start), so say 8k-512 + * highbit is the higest bit set PLUS 1, so in this case would be the + * 16k list. so below, vprev is therefore pointing to the 8k list + * in vmem_alloc, the unconstrained allocation takes, for a 8k-512 + * block: vsp = flist[8k].vs_knext + * and calls vmem_seg_create() which sends any leftovers from vsp + * to vmem_freelist_insert + * + * vmem_freelist_insert would take the seg (as above, 8k-512 size), + * vprev points to the 16k list, and VMEM_INSERT(vprev, vsp, k) + * inserts the segment immediately after + * + * so vmem_seg_create(...8k-512...) pushes to the head of the 8k list, + * and vmem_alloc(...8-512k...) will pull from the head of the 8k list + * + * below we may want to push to the TAIL of the 8k list, which is + * just before flist[16k]. + */ + + vprev = (vmem_seg_t *)&vmp->vm_freelist[highbit(VS_SIZE(vsp)) - 1]; + + int my_listnum = highbit(VS_SIZE(vsp)) - 1; + + ASSERT(my_listnum >= 1); + ASSERT(my_listnum < VMEM_FREELISTS); + + int next_listnum = my_listnum + 1; + + const vmem_seg_t *nextlist = + (vmem_seg_t *)&vmp->vm_freelist[next_listnum]; + + ASSERT(vsp->vs_span_createtime != 0); + if (vsp->vs_span_createtime == 0) { + printf("SPL: %s: WARNING: vsp->vs_span_createtime == 0 (%s)!\n", + __func__, vmp->vm_name); + } + + // continuing our example, starts with p at flist[8k] + // and n at the following freelist entry + + const vmem_seg_t *vhead = vprev; + vmem_seg_t *p = vprev; + vmem_seg_t *n = p->vs_knext; + + // walk from the freelist head looking for + // a segment whose creation time is earlier than + // the segment to be inserted's creation time, + // then insert before that segment. + + for (uint32_t step = 0; + flist_sort_compare(newfirst, vhead, nextlist, p, vsp) == true; + step++) { + // iterating while predecessor pointer p was created + // at a later tick than funcarg vsp. + // + // below we set p to n and update n. + ASSERT(n != NULL); + if (n == nextlist) { + dprintf("SPL: %s: at marker (%s)(steps: %u) " + "p->vs_start, end == %lu, %lu\n", + __func__, vmp->vm_name, step, + (uintptr_t)p->vs_start, (uintptr_t)p->vs_end); + // IOSleep(1); + // the next entry is the next marker (e.g. 16k marker) + break; + } + if (n->vs_start == 0) { + // from vmem_freelist_delete, this is a head + dprintf("SPL: %s: n->vs_start == 0 (%s)(steps: %u) " + "p->vs_start, end == %lu, %lu\n", + __func__, vmp->vm_name, step, + (uintptr_t)p->vs_start, (uintptr_t)p->vs_end); + // IOSleep(1); + break; + } + if (step >= max_walk_steps) { + ASSERT(nextlist->vs_kprev != NULL); + // we have walked far enough. + // put this segment at the tail of the freelist. + if (nextlist->vs_kprev != NULL) { + n = (vmem_seg_t *)nextlist; + p = nextlist->vs_kprev; + } + dprintf("SPL: %s: walked out (%s)\n", __func__, + vmp->vm_name); + // IOSleep(1); + atomic_inc_64(&spl_frag_walked_out); + break; + } + if (n->vs_knext == NULL) { + dprintf("SPL: %s: n->vs_knext == NULL (my_listnum " + "== %d)\n", __func__, my_listnum); + // IOSleep(1); + break; + } + p = n; + n = n->vs_knext; + atomic_inc_64(&spl_frag_walk_cnt); + } + + ASSERT(p != NULL); + + // insert segment between p and n + + vsp->vs_type = VMEM_FREE; + vmp->vm_freemap |= VS_SIZE(vprev); + VMEM_INSERT(p, vsp, k); + + cv_broadcast(&vmp->vm_cv); +} + +/* + * Add vsp to the appropriate freelist. + */ +static void +vmem_freelist_insert(vmem_t *vmp, vmem_seg_t *vsp) +{ + + if (vmp->vm_cflags & VMC_TIMEFREE) { + vmem_freelist_insert_sort_by_time(vmp, vsp); + return; + } + + vmem_seg_t *vprev; + + ASSERT(*VMEM_HASH(vmp, vsp->vs_start) != vsp); + + vprev = (vmem_seg_t *)&vmp->vm_freelist[highbit(VS_SIZE(vsp)) - 1]; + vsp->vs_type = VMEM_FREE; + vmp->vm_freemap |= VS_SIZE(vprev); + VMEM_INSERT(vprev, vsp, k); + + cv_broadcast(&vmp->vm_cv); +} + +/* + * Take vsp from the freelist. + */ +static void +vmem_freelist_delete(vmem_t *vmp, vmem_seg_t *vsp) +{ + ASSERT(*VMEM_HASH(vmp, vsp->vs_start) != vsp); + ASSERT(vsp->vs_type == VMEM_FREE); + + if (vsp->vs_knext->vs_start == 0 && vsp->vs_kprev->vs_start == 0) { + /* + * The segments on both sides of 'vsp' are freelist heads, + * so taking vsp leaves the freelist at vsp->vs_kprev empty. + */ + ASSERT(vmp->vm_freemap & VS_SIZE(vsp->vs_kprev)); + vmp->vm_freemap ^= VS_SIZE(vsp->vs_kprev); + } + VMEM_DELETE(vsp, k); +} + +/* + * Add vsp to the allocated-segment hash table and update kstats. + */ +static void +vmem_hash_insert(vmem_t *vmp, vmem_seg_t *vsp) +{ + vmem_seg_t **bucket; + + vsp->vs_type = VMEM_ALLOC; + bucket = VMEM_HASH(vmp, vsp->vs_start); + vsp->vs_knext = *bucket; + *bucket = vsp; + + if (vmem_seg_size == sizeof (vmem_seg_t)) { + // vsp->vs_depth = (uint8_t)getpcstack(vsp->vs_stack, + // VMEM_STACK_DEPTH); + // vsp->vs_thread = curthread; + vsp->vs_depth = 0; + vsp->vs_thread = 0; + vsp->vs_timestamp = gethrtime(); + } else { + vsp->vs_depth = 0; + } + + vmp->vm_kstat.vk_alloc.value.ui64++; + vmp->vm_kstat.vk_mem_inuse.value.ui64 += VS_SIZE(vsp); +} + +/* + * Remove vsp from the allocated-segment hash table and update kstats. + */ +static vmem_seg_t * +vmem_hash_delete(vmem_t *vmp, uintptr_t addr, size_t size) +{ + vmem_seg_t *vsp, **prev_vspp; + + prev_vspp = VMEM_HASH(vmp, addr); + while ((vsp = *prev_vspp) != NULL) { + if (vsp->vs_start == addr) { + *prev_vspp = vsp->vs_knext; + break; + } + vmp->vm_kstat.vk_lookup.value.ui64++; + prev_vspp = &vsp->vs_knext; + } + + if (vsp == NULL) + panic("vmem_hash_delete(%p, %lx, %lu): bad free " + "(name: %s, addr, size)", + (void *)vmp, addr, size, vmp->vm_name); + if (VS_SIZE(vsp) != size) + panic("vmem_hash_delete(%p, %lx, %lu): (%s) wrong size" + "(expect %lu)", + (void *)vmp, addr, size, vmp->vm_name, VS_SIZE(vsp)); + + vmp->vm_kstat.vk_free.value.ui64++; + vmp->vm_kstat.vk_mem_inuse.value.ui64 -= size; + + return (vsp); +} + +/* + * Create a segment spanning the range [start, end) and add it to the arena. + */ +static vmem_seg_t * +vmem_seg_create(vmem_t *vmp, vmem_seg_t *vprev, uintptr_t start, uintptr_t end) +{ + vmem_seg_t *newseg = vmem_getseg(vmp); + + newseg->vs_start = start; + newseg->vs_end = end; + newseg->vs_type = 0; + newseg->vs_import = 0; + newseg->vs_span_createtime = 0; + + VMEM_INSERT(vprev, newseg, a); + + return (newseg); +} + +/* + * Remove segment vsp from the arena. + */ +static void +vmem_seg_destroy(vmem_t *vmp, vmem_seg_t *vsp) +{ + ASSERT(vsp->vs_type != VMEM_ROTOR); + VMEM_DELETE(vsp, a); + + vmem_putseg(vmp, vsp); +} + +/* + * Add the span [vaddr, vaddr + size) to vmp and update kstats. + */ +static vmem_seg_t * +vmem_span_create(vmem_t *vmp, void *vaddr, size_t size, uint8_t import) +{ + vmem_seg_t *newseg, *span; + uintptr_t start = (uintptr_t)vaddr; + uintptr_t end = start + size; + + ASSERT(MUTEX_HELD(&vmp->vm_lock)); + if ((start | end) & (vmp->vm_quantum - 1)) + panic("vmem_span_create(%p, %p, %lu): misaligned (%s)", + (void *)vmp, vaddr, size, vmp->vm_name); + + span = vmem_seg_create(vmp, vmp->vm_seg0.vs_aprev, start, end); + span->vs_type = VMEM_SPAN; + span->vs_import = import; + + hrtime_t t = 0; + if (vmp->vm_cflags & VMC_TIMEFREE) { + t = gethrtime(); + } + span->vs_span_createtime = t; + + VMEM_INSERT(vmp->vm_seg0.vs_kprev, span, k); + + newseg = vmem_seg_create(vmp, span, start, end); + newseg->vs_span_createtime = t; + + vmem_freelist_insert(vmp, newseg); + + if (import) + vmp->vm_kstat.vk_mem_import.value.ui64 += size; + vmp->vm_kstat.vk_mem_total.value.ui64 += size; + + return (newseg); +} + +/* + * Remove span vsp from vmp and update kstats. + */ +static void +vmem_span_destroy(vmem_t *vmp, vmem_seg_t *vsp) +{ + vmem_seg_t *span = vsp->vs_aprev; + size_t size = VS_SIZE(vsp); + + ASSERT(MUTEX_HELD(&vmp->vm_lock)); + ASSERT(span->vs_type == VMEM_SPAN); + + if (span->vs_import) + vmp->vm_kstat.vk_mem_import.value.ui64 -= size; + vmp->vm_kstat.vk_mem_total.value.ui64 -= size; + + VMEM_DELETE(span, k); + + vmem_seg_destroy(vmp, vsp); + vmem_seg_destroy(vmp, span); +} + +/* + * Allocate the subrange [addr, addr + size) from segment vsp. + * If there are leftovers on either side, place them on the freelist. + * Returns a pointer to the segment representing [addr, addr + size). + */ +static vmem_seg_t * +vmem_seg_alloc(vmem_t *vmp, vmem_seg_t *vsp, uintptr_t addr, size_t size) +{ + uintptr_t vs_start = vsp->vs_start; + uintptr_t vs_end = vsp->vs_end; + size_t vs_size = vs_end - vs_start; + size_t realsize = P2ROUNDUP(size, vmp->vm_quantum); + uintptr_t addr_end = addr + realsize; + + ASSERT(P2PHASE(vs_start, vmp->vm_quantum) == 0); + ASSERT(P2PHASE(addr, vmp->vm_quantum) == 0); + ASSERT(vsp->vs_type == VMEM_FREE); + ASSERT(addr >= vs_start && addr_end - 1 <= vs_end - 1); + ASSERT(addr - 1 <= addr_end - 1); + + hrtime_t parent_seg_span_createtime = vsp->vs_span_createtime; + + /* + * If we're allocating from the start of the segment, and the + * remainder will be on the same freelist, we can save quite + * a bit of work. + */ + if (P2SAMEHIGHBIT(vs_size, vs_size - realsize) && addr == vs_start) { + ASSERT(highbit(vs_size) == highbit(vs_size - realsize)); + vsp->vs_start = addr_end; + vsp = vmem_seg_create(vmp, vsp->vs_aprev, addr, addr + size); + vsp->vs_span_createtime = parent_seg_span_createtime; + vmem_hash_insert(vmp, vsp); + return (vsp); + } + + vmem_freelist_delete(vmp, vsp); + + if (vs_end != addr_end) { + vmem_seg_t *v = vmem_seg_create(vmp, vsp, addr_end, vs_end); + v->vs_span_createtime = parent_seg_span_createtime; + vmem_freelist_insert(vmp, v); + } + + if (vs_start != addr) { + vmem_seg_t *v = + vmem_seg_create(vmp, vsp->vs_aprev, vs_start, addr); + v->vs_span_createtime = parent_seg_span_createtime; + vmem_freelist_insert(vmp, v); + } + + vsp->vs_start = addr; + vsp->vs_end = addr + size; + + vsp->vs_span_createtime = parent_seg_span_createtime; + + vmem_hash_insert(vmp, vsp); + return (vsp); +} + +/* + * Returns 1 if we are populating, 0 otherwise. + * Call it if we want to prevent recursion from HAT. + */ +int +vmem_is_populator() +{ + return (mutex_owner(&vmem_sleep_lock) == curthread || + mutex_owner(&vmem_nosleep_lock) == curthread || + mutex_owner(&vmem_pushpage_lock) == curthread || + mutex_owner(&vmem_panic_lock) == curthread); +} + +/* + * Populate vmp's segfree list with VMEM_MINFREE vmem_seg_t structures. + */ +static int +vmem_populate(vmem_t *vmp, int vmflag) +{ + char *p; + vmem_seg_t *vsp; + ssize_t nseg; + size_t size; + kmutex_t *lp; + int i; + + while (vmp->vm_nsegfree < VMEM_MINFREE && + (vsp = vmem_getseg_global()) != NULL) + vmem_putseg(vmp, vsp); + + if (vmp->vm_nsegfree >= VMEM_MINFREE) + return (1); + + /* + * If we're already populating, tap the reserve. + */ + if (vmem_is_populator()) { + ASSERT(vmp->vm_cflags & VMC_POPULATOR); + return (1); + } + + mutex_exit(&vmp->vm_lock); + + // if (panic_thread == curthread) + // lp = &vmem_panic_lock; + // else + + if (vmflag & VM_NOSLEEP) + lp = &vmem_nosleep_lock; + else if (vmflag & VM_PUSHPAGE) + lp = &vmem_pushpage_lock; + else + lp = &vmem_sleep_lock; + + mutex_enter(lp); + + nseg = VMEM_MINFREE + vmem_populators * VMEM_POPULATE_RESERVE; + size = P2ROUNDUP(nseg * vmem_seg_size, vmem_seg_arena->vm_quantum); + nseg = size / vmem_seg_size; + + /* + * The following vmem_alloc() may need to populate vmem_seg_arena + * and all the things it imports from. When doing so, it will tap + * each arena's reserve to prevent recursion (see the block comment + * above the definition of VMEM_POPULATE_RESERVE). + */ + p = vmem_alloc(vmem_seg_arena, size, vmflag & VM_KMFLAGS); + if (p == NULL) { + mutex_exit(lp); + mutex_enter(&vmp->vm_lock); + vmp->vm_kstat.vk_populate_fail.value.ui64++; + return (0); + } + + /* + * Restock the arenas that may have been depleted during population. + */ + for (i = 0; i < vmem_populators; i++) { + mutex_enter(&vmem_populator[i]->vm_lock); + while (vmem_populator[i]->vm_nsegfree < VMEM_POPULATE_RESERVE) + vmem_putseg(vmem_populator[i], + (vmem_seg_t *)(p + --nseg * vmem_seg_size)); + mutex_exit(&vmem_populator[i]->vm_lock); + } + + mutex_exit(lp); + mutex_enter(&vmp->vm_lock); + + /* + * Now take our own segments. + */ + ASSERT(nseg >= VMEM_MINFREE); + while (vmp->vm_nsegfree < VMEM_MINFREE) + vmem_putseg(vmp, (vmem_seg_t *)(p + --nseg * vmem_seg_size)); + + /* + * Give the remainder to charity. + */ + while (nseg > 0) + vmem_putseg_global((vmem_seg_t *)(p + --nseg * vmem_seg_size)); + + return (1); +} + +/* + * Advance a walker from its previous position to 'afterme'. + * Note: may drop and reacquire vmp->vm_lock. + */ +static void +vmem_advance(vmem_t *vmp, vmem_seg_t *walker, vmem_seg_t *afterme) +{ + vmem_seg_t *vprev = walker->vs_aprev; + vmem_seg_t *vnext = walker->vs_anext; + vmem_seg_t *vsp = NULL; + + VMEM_DELETE(walker, a); + + if (afterme != NULL) + VMEM_INSERT(afterme, walker, a); + + /* + * The walker segment's presence may have prevented its neighbors + * from coalescing. If so, coalesce them now. + */ + if (vprev->vs_type == VMEM_FREE) { + if (vnext->vs_type == VMEM_FREE) { + ASSERT(vprev->vs_end == vnext->vs_start); + ASSERT(vprev->vs_span_createtime == + vnext->vs_span_createtime); + vmem_freelist_delete(vmp, vnext); + vmem_freelist_delete(vmp, vprev); + vprev->vs_end = vnext->vs_end; + vmem_freelist_insert(vmp, vprev); + vmem_seg_destroy(vmp, vnext); + } + vsp = vprev; + } else if (vnext->vs_type == VMEM_FREE) { + vsp = vnext; + } + + /* + * vsp could represent a complete imported span, + * in which case we must return it to the source. + */ + if (vsp != NULL && vsp->vs_aprev->vs_import && + vmp->vm_source_free != NULL && + vsp->vs_aprev->vs_type == VMEM_SPAN && + vsp->vs_anext->vs_type == VMEM_SPAN) { + void *vaddr = (void *)vsp->vs_start; + size_t size = VS_SIZE(vsp); + ASSERT(size == VS_SIZE(vsp->vs_aprev)); + vmem_freelist_delete(vmp, vsp); + vmem_span_destroy(vmp, vsp); + vmp->vm_kstat.vk_parent_free.value.ui64++; + mutex_exit(&vmp->vm_lock); + vmp->vm_source_free(vmp->vm_source, vaddr, size); + mutex_enter(&vmp->vm_lock); + } +} + +/* + * VM_NEXTFIT allocations deliberately cycle through all virtual addresses + * in an arena, so that we avoid reusing addresses for as long as possible. + * This helps to catch used-after-freed bugs. It's also the perfect policy + * for allocating things like process IDs, where we want to cycle through + * all values in order. + */ +static void * +vmem_nextfit_alloc(vmem_t *vmp, size_t size, int vmflag) +{ + vmem_seg_t *vsp, *rotor; + uintptr_t addr; + size_t realsize = P2ROUNDUP(size, vmp->vm_quantum); + size_t vs_size; + + mutex_enter(&vmp->vm_lock); + + if (vmp->vm_nsegfree < VMEM_MINFREE && !vmem_populate(vmp, vmflag)) { + mutex_exit(&vmp->vm_lock); + return (NULL); + } + + /* + * The common case is that the segment right after the rotor is free, + * and large enough that extracting 'size' bytes won't change which + * freelist it's on. In this case we can avoid a *lot* of work. + * Instead of the normal vmem_seg_alloc(), we just advance the start + * address of the victim segment. Instead of moving the rotor, we + * create the new segment structure *behind the rotor*, which has + * the same effect. And finally, we know we don't have to coalesce + * the rotor's neighbors because the new segment lies between them. + */ + rotor = &vmp->vm_rotor; + vsp = rotor->vs_anext; + if (vsp->vs_type == VMEM_FREE && (vs_size = VS_SIZE(vsp)) > realsize && + P2SAMEHIGHBIT(vs_size, vs_size - realsize)) { + ASSERT(highbit(vs_size) == highbit(vs_size - realsize)); + addr = vsp->vs_start; + vsp->vs_start = addr + realsize; + hrtime_t t = vsp->vs_span_createtime; + vmem_hash_insert(vmp, + vmem_seg_create(vmp, rotor->vs_aprev, addr, addr + size)); + vsp->vs_span_createtime = t; + mutex_exit(&vmp->vm_lock); + return ((void *)addr); + } + + /* + * Starting at the rotor, look for a segment large enough to + * satisfy the allocation. + */ + for (;;) { + atomic_inc_64(&vmp->vm_kstat.vk_search.value.ui64); + if (vsp->vs_type == VMEM_FREE && VS_SIZE(vsp) >= size) + break; + vsp = vsp->vs_anext; + if (vsp == rotor) { + /* + * We've come full circle. One possibility is that the + * there's actually enough space, but the rotor itself + * is preventing the allocation from succeeding because + * it's sitting between two free segments. Therefore, + * we advance the rotor and see if that liberates a + * suitable segment. + */ + vmem_advance(vmp, rotor, rotor->vs_anext); + vsp = rotor->vs_aprev; + if (vsp->vs_type == VMEM_FREE && VS_SIZE(vsp) >= size) + break; + /* + * If there's a lower arena we can import from, or it's + * a VM_NOSLEEP allocation, let vmem_xalloc() handle it. + * Otherwise, wait until another thread frees something. + */ + if (vmp->vm_source_alloc != NULL || + (vmflag & VM_NOSLEEP)) { + mutex_exit(&vmp->vm_lock); + return (vmem_xalloc(vmp, size, vmp->vm_quantum, + 0, 0, NULL, NULL, + vmflag & (VM_KMFLAGS | VM_NEXTFIT))); + } + atomic_inc_64(&vmp->vm_kstat.vk_wait.value.ui64); + atomic_inc_64( + &vmp->vm_kstat.vk_threads_waiting.value.ui64); + atomic_inc_64(&spl_vmem_threads_waiting); + if (spl_vmem_threads_waiting > 1) + dprintf("SPL: %s: waiting for %lu sized alloc " + "after full circle of %s, waiting " + "threads %llu, total threads waiting " + "= %llu.\n", + __func__, size, vmp->vm_name, + vmp->vm_kstat.vk_threads_waiting.value.ui64, + spl_vmem_threads_waiting); + cv_wait(&vmp->vm_cv, &vmp->vm_lock); + atomic_dec_64(&spl_vmem_threads_waiting); + atomic_dec_64( + &vmp->vm_kstat.vk_threads_waiting.value.ui64); + vsp = rotor->vs_anext; + } + } + + /* + * We found a segment. Extract enough space to satisfy the allocation. + */ + addr = vsp->vs_start; + vsp = vmem_seg_alloc(vmp, vsp, addr, size); + ASSERT(vsp->vs_type == VMEM_ALLOC && + vsp->vs_start == addr && vsp->vs_end == addr + size); + + /* + * Advance the rotor to right after the newly-allocated segment. + * That's where the next VM_NEXTFIT allocation will begin searching. + */ + vmem_advance(vmp, rotor, vsp); + mutex_exit(&vmp->vm_lock); + return ((void *)addr); +} + +/* + * Checks if vmp is guaranteed to have a size-byte buffer somewhere on its + * freelist. If size is not a power-of-2, it can return a false-negative. + * + * Used to decide if a newly imported span is superfluous after re-acquiring + * the arena lock. + */ +static int +vmem_canalloc(vmem_t *vmp, size_t size) +{ + int hb; + int flist = 0; + ASSERT(MUTEX_HELD(&vmp->vm_lock)); + + if ((size & (size - 1)) == 0) + flist = lowbit(P2ALIGN(vmp->vm_freemap, size)); + else if ((hb = highbit(size)) < VMEM_FREELISTS) + flist = lowbit(P2ALIGN(vmp->vm_freemap, 1ULL << hb)); + + return (flist); +} + +// Convenience functions for use when gauging +// allocation ability when not holding the lock. +// These are unreliable because vmp->vm_freemap is +// liable to change immediately after being examined. +int +vmem_canalloc_lock(vmem_t *vmp, size_t size) +{ + mutex_enter(&vmp->vm_lock); + int i = vmem_canalloc(vmp, size); + mutex_exit(&vmp->vm_lock); + return (i); +} + +int +vmem_canalloc_atomic(vmem_t *vmp, size_t size) +{ + int hb; + int flist = 0; + + ulong_t freemap = + __c11_atomic_load((_Atomic ulong_t *)&vmp->vm_freemap, + __ATOMIC_SEQ_CST); + + if (ISP2(size)) + flist = lowbit(P2ALIGN(freemap, size)); + else if ((hb = highbit(size)) < VMEM_FREELISTS) + flist = lowbit(P2ALIGN(freemap, 1ULL << hb)); + + return (flist); +} + +static inline uint64_t +spl_vmem_xnu_useful_bytes_free(void) +{ + extern _Atomic uint32_t spl_vm_pages_reclaimed; + extern _Atomic uint32_t spl_vm_pages_wanted; + extern _Atomic uint32_t spl_vm_pressure_level; + + if (spl_vm_pages_wanted > 0) + return (PAGE_SIZE * spl_vm_pages_reclaimed); + + /* + * beware of large magic guard values, + * the pressure enum only goes to 4 + */ + if (spl_vm_pressure_level > 0 && + spl_vm_pressure_level < 100) + return (0); + + return (total_memory - segkmem_total_mem_allocated); +} + +uint64_t +vmem_xnu_useful_bytes_free(void) +{ + return (spl_vmem_xnu_useful_bytes_free()); +} + + +static void * +spl_vmem_malloc_unconditionally_unlocked(size_t size) +{ + extern void *osif_malloc(uint64_t); + atomic_inc_64(&spl_vmem_unconditional_allocs); + atomic_add_64(&spl_vmem_unconditional_alloc_bytes, size); + return (osif_malloc(size)); +} + +static void * +spl_vmem_malloc_unconditionally(size_t size) +{ + mutex_enter(&vmem_xnu_alloc_lock); + void *m = spl_vmem_malloc_unconditionally_unlocked(size); + mutex_exit(&vmem_xnu_alloc_lock); + return (m); +} + +static void * +spl_vmem_malloc_if_no_pressure(size_t size) +{ + // The mutex serializes concurrent callers, providing time for + // the variables in spl_vmem_xnu_useful_bytes_free() to be updated. + mutex_enter(&vmem_xnu_alloc_lock); + if (spl_vmem_xnu_useful_bytes_free() > (MAX(size, 1024ULL*1024ULL))) { + extern void *osif_malloc(uint64_t); + void *p = osif_malloc(size); + if (p != NULL) { + spl_vmem_conditional_allocs++; + spl_vmem_conditional_alloc_bytes += size; + } + mutex_exit(&vmem_xnu_alloc_lock); + return (p); + } else { + spl_vmem_conditional_alloc_deny++; + spl_vmem_conditional_alloc_deny_bytes += size; + mutex_exit(&vmem_xnu_alloc_lock); + return (NULL); + } +} + +/* + * Allocate size bytes at offset phase from an align boundary such that the + * resulting segment [addr, addr + size) is a subset of [minaddr, maxaddr) + * that does not straddle a nocross-aligned boundary. + */ +void * +vmem_xalloc(vmem_t *vmp, size_t size, size_t align_arg, size_t phase, + size_t nocross, void *minaddr, void *maxaddr, int vmflag) +{ + vmem_seg_t *vsp; + vmem_seg_t *vbest = NULL; + uintptr_t addr, taddr, start, end; + uintptr_t align = (align_arg != 0) ? align_arg : vmp->vm_quantum; + void *vaddr, *xvaddr = NULL; + size_t xsize; + int hb, flist, resv; + uint32_t mtbf; + + if ((align | phase | nocross) & (vmp->vm_quantum - 1)) + panic("vmem_xalloc(%p, %lu, %lu, %lu, %lu, %p, %p, %x): " + "parameters not vm_quantum aligned", + (void *)vmp, size, align_arg, phase, nocross, + minaddr, maxaddr, vmflag); + + if (nocross != 0 && + (align > nocross || P2ROUNDUP(phase + size, align) > nocross)) + panic("vmem_xalloc(%p, %lu, %lu, %lu, %lu, %p, %p, %x): " + "overconstrained allocation", + (void *)vmp, size, align_arg, phase, nocross, + minaddr, maxaddr, vmflag); + + if (phase >= align || (align & (align - 1)) != 0 || + (nocross & (nocross - 1)) != 0) + panic("vmem_xalloc(%p, %lu, %lu, %lu, %lu, %p, %p, %x): " + "parameters inconsistent or invalid", + (void *)vmp, size, align_arg, phase, nocross, + minaddr, maxaddr, vmflag); + + if ((mtbf = vmem_mtbf | vmp->vm_mtbf) != 0 && gethrtime() % mtbf == 0 && + (vmflag & (VM_NOSLEEP | VM_PANIC)) == VM_NOSLEEP) + return (NULL); + + mutex_enter(&vmp->vm_lock); + for (;;) { + if (vmp->vm_nsegfree < VMEM_MINFREE && + !vmem_populate(vmp, vmflag)) + break; +do_alloc: + /* + * highbit() returns the highest bit + 1, which is exactly + * what we want: we want to search the first freelist whose + * members are *definitely* large enough to satisfy our + * allocation. However, there are certain cases in which we + * want to look at the next-smallest freelist (which *might* + * be able to satisfy the allocation): + * + * (1) The size is exactly a power of 2, in which case + * the smaller freelist is always big enough; + * + * (2) All other freelists are empty; + * + * (3) We're in the highest possible freelist, which is + * always empty (e.g. the 4GB freelist on 32-bit systems); + * + * (4) We're doing a best-fit or first-fit allocation. + */ + if ((size & (size - 1)) == 0) { + flist = lowbit(P2ALIGN(vmp->vm_freemap, size)); + } else { + hb = highbit(size); + if ((vmp->vm_freemap >> hb) == 0 || + hb == VMEM_FREELISTS || + (vmflag & (VM_BESTFIT | VM_FIRSTFIT))) + hb--; + flist = lowbit(P2ALIGN(vmp->vm_freemap, 1UL << hb)); + } + + for (vbest = NULL, vsp = (flist == 0) ? NULL : + vmp->vm_freelist[flist - 1].vs_knext; + vsp != NULL; vsp = vsp->vs_knext) { + atomic_inc_64(&vmp->vm_kstat.vk_search.value.ui64); + if (vsp->vs_start == 0) { + /* + * We're moving up to a larger freelist, + * so if we've already found a candidate, + * the fit can't possibly get any better. + */ + if (vbest != NULL) + break; + /* + * Find the next non-empty freelist. + */ + flist = lowbit(P2ALIGN(vmp->vm_freemap, + VS_SIZE(vsp))); + if (flist-- == 0) + break; + vsp = (vmem_seg_t *)&vmp->vm_freelist[flist]; + ASSERT(vsp->vs_knext->vs_type == VMEM_FREE); + continue; + } + if (vsp->vs_end - 1 < (uintptr_t)minaddr) + continue; + if (vsp->vs_start > (uintptr_t)maxaddr - 1) + continue; + start = MAX(vsp->vs_start, (uintptr_t)minaddr); + end = MIN(vsp->vs_end - 1, (uintptr_t)maxaddr - 1) + 1; + taddr = P2PHASEUP(start, align, phase); + if (P2BOUNDARY(taddr, size, nocross)) + taddr += + P2ROUNDUP(P2NPHASE(taddr, nocross), align); + if ((taddr - start) + size > end - start || + (vbest != NULL && VS_SIZE(vsp) >= VS_SIZE(vbest))) + continue; + vbest = vsp; + addr = taddr; + if (!(vmflag & VM_BESTFIT) || VS_SIZE(vbest) == size) + break; + } + if (vbest != NULL) + break; + ASSERT(xvaddr == NULL); + if (size == 0) + panic("vmem_xalloc(): size == 0"); + if (vmp->vm_source_alloc != NULL && nocross == 0 && + minaddr == NULL && maxaddr == NULL) { + size_t aneeded, asize; + size_t aquantum = MAX(vmp->vm_quantum, + vmp->vm_source->vm_quantum); + size_t aphase = phase; + if ((align > aquantum) && + !(vmp->vm_cflags & VMC_XALIGN)) { + aphase = (P2PHASE(phase, aquantum) != 0) ? + align - vmp->vm_quantum : align - aquantum; + ASSERT(aphase >= phase); + } + aneeded = MAX(size + aphase, vmp->vm_min_import); + asize = P2ROUNDUP(aneeded, aquantum); + + if (asize < size) { + /* + * The rounding induced overflow; return NULL + * if we are permitted to fail the allocation + * (and explicitly panic if we aren't). + */ + if ((vmflag & VM_NOSLEEP) && + !(vmflag & VM_PANIC)) { + mutex_exit(&vmp->vm_lock); + return (NULL); + } + + panic("vmem_xalloc(): size overflow"); + } + + /* + * Determine how many segment structures we'll consume. + * The calculation must be precise because if we're + * here on behalf of vmem_populate(), we are taking + * segments from a very limited reserve. + */ + if (size == asize && !(vmp->vm_cflags & VMC_XALLOC)) + resv = VMEM_SEGS_PER_SPAN_CREATE + + VMEM_SEGS_PER_EXACT_ALLOC; + else if (phase == 0 && + align <= vmp->vm_source->vm_quantum) + resv = VMEM_SEGS_PER_SPAN_CREATE + + VMEM_SEGS_PER_LEFT_ALLOC; + else + resv = VMEM_SEGS_PER_ALLOC_MAX; + + ASSERT(vmp->vm_nsegfree >= resv); + vmp->vm_nsegfree -= resv; /* reserve our segs */ + mutex_exit(&vmp->vm_lock); + if (vmp->vm_cflags & VMC_XALLOC) { + size_t oasize = asize; + vaddr = ((vmem_ximport_t *) + vmp->vm_source_alloc)(vmp->vm_source, + &asize, align, vmflag & VM_KMFLAGS); + ASSERT(asize >= oasize); + ASSERT(P2PHASE(asize, + vmp->vm_source->vm_quantum) == 0); + ASSERT(!(vmp->vm_cflags & VMC_XALIGN) || + IS_P2ALIGNED(vaddr, align)); + } else { + atomic_inc_64( + &vmp->vm_kstat.vk_parent_alloc.value.ui64); + vaddr = vmp->vm_source_alloc(vmp->vm_source, + asize, vmflag & (VM_KMFLAGS | VM_NEXTFIT)); + } + mutex_enter(&vmp->vm_lock); + vmp->vm_nsegfree += resv; /* claim reservation */ + aneeded = size + align - vmp->vm_quantum; + aneeded = P2ROUNDUP(aneeded, vmp->vm_quantum); + if (vaddr != NULL) { + /* + * Since we dropped the vmem lock while + * calling the import function, other + * threads could have imported space + * and made our import unnecessary. In + * order to save space, we return + * excess imports immediately. + */ + // but if there are threads waiting below, + // do not return the excess import, rather + // wake those threads up so they can use it. + if (asize > aneeded && + vmp->vm_source_free != NULL && + vmp->vm_kstat.vk_threads_waiting.value.ui64 + == 0 && vmem_canalloc(vmp, aneeded)) { + ASSERT(resv >= + VMEM_SEGS_PER_MIDDLE_ALLOC); + xvaddr = vaddr; + xsize = asize; + goto do_alloc; + } else if ( + vmp->vm_kstat.vk_threads_waiting.value.ui64 + > 0) { + vmp->vm_kstat.vk_excess.value.ui64++; + cv_broadcast(&vmp->vm_cv); + } + vbest = vmem_span_create(vmp, vaddr, asize, 1); + addr = P2PHASEUP(vbest->vs_start, align, phase); + break; + } else if (vmem_canalloc(vmp, aneeded)) { + /* + * Our import failed, but another thread + * added sufficient free memory to the arena + * to satisfy our request. Go back and + * grab it. + */ + ASSERT(resv >= VMEM_SEGS_PER_MIDDLE_ALLOC); + goto do_alloc; + } + } + + /* + * If the requestor chooses to fail the allocation attempt + * rather than reap wait and retry - get out of the loop. + */ + if (vmflag & VM_ABORT) + break; + mutex_exit(&vmp->vm_lock); + +#if 0 + if (vmp->vm_cflags & VMC_IDENTIFIER) + kmem_reap_idspace(); + else + kmem_reap(); +#endif + + mutex_enter(&vmp->vm_lock); + if (vmflag & VM_NOSLEEP) + break; + atomic_inc_64(&vmp->vm_kstat.vk_wait.value.ui64); + atomic_inc_64(&vmp->vm_kstat.vk_threads_waiting.value.ui64); + atomic_inc_64(&spl_vmem_threads_waiting); + if (spl_vmem_threads_waiting > 0) { + dprintf("SPL: %s: vmem waiting for %lu sized alloc " + "for %s, waiting threads %llu, total threads " + "waiting = %llu\n", + __func__, size, vmp->vm_name, + vmp->vm_kstat.vk_threads_waiting.value.ui64, + spl_vmem_threads_waiting); + extern int64_t spl_free_set_and_wait_pressure(int64_t, + boolean_t, clock_t); + extern int64_t spl_free_manual_pressure_wrapper(void); + mutex_exit(&vmp->vm_lock); + // release other waiting threads + spl_free_set_pressure(0); + int64_t target_pressure = size * + spl_vmem_threads_waiting; + int64_t delivered_pressure = + spl_free_set_and_wait_pressure(target_pressure, + TRUE, USEC2NSEC(500)); + dprintf("SPL: %s: pressure %lld targeted, %lld " + "delivered\n", __func__, target_pressure, + delivered_pressure); + mutex_enter(&vmp->vm_lock); + } + cv_wait(&vmp->vm_cv, &vmp->vm_lock); + atomic_dec_64(&spl_vmem_threads_waiting); + atomic_dec_64(&vmp->vm_kstat.vk_threads_waiting.value.ui64); + } + if (vbest != NULL) { + ASSERT(vbest->vs_type == VMEM_FREE); + ASSERT(vbest->vs_knext != vbest); + /* re-position to end of buffer */ + if (vmflag & VM_ENDALLOC) { + addr += ((vbest->vs_end - (addr + size)) / align) * + align; + } + (void) vmem_seg_alloc(vmp, vbest, addr, size); + mutex_exit(&vmp->vm_lock); + if (xvaddr) { + atomic_inc_64(&vmp->vm_kstat.vk_parent_free.value.ui64); + vmp->vm_source_free(vmp->vm_source, xvaddr, xsize); + } + ASSERT(P2PHASE(addr, align) == phase); + ASSERT(!P2BOUNDARY(addr, size, nocross)); + ASSERT(addr >= (uintptr_t)minaddr); + ASSERT(addr + size - 1 <= (uintptr_t)maxaddr - 1); + return ((void *)addr); + } + if (0 == (vmflag & VM_NO_VBA)) { + vmp->vm_kstat.vk_fail.value.ui64++; + } + mutex_exit(&vmp->vm_lock); + if (vmflag & VM_PANIC) + panic("vmem_xalloc(%p, %lu, %lu, %lu, %lu, %p, %p, %x): " + "cannot satisfy mandatory allocation", + (void *)vmp, size, align_arg, phase, nocross, + minaddr, maxaddr, vmflag); + ASSERT(xvaddr == NULL); + return (NULL); +} + +/* + * Free the segment [vaddr, vaddr + size), where vaddr was a constrained + * allocation. vmem_xalloc() and vmem_xfree() must always be paired because + * both routines bypass the quantum caches. + */ +void +vmem_xfree(vmem_t *vmp, void *vaddr, size_t size) +{ + vmem_seg_t *vsp, *vnext, *vprev; + + mutex_enter(&vmp->vm_lock); + + vsp = vmem_hash_delete(vmp, (uintptr_t)vaddr, size); + vsp->vs_end = P2ROUNDUP(vsp->vs_end, vmp->vm_quantum); + + /* + * Attempt to coalesce with the next segment. + */ + vnext = vsp->vs_anext; + if (vnext->vs_type == VMEM_FREE) { + ASSERT(vsp->vs_end == vnext->vs_start); + vmem_freelist_delete(vmp, vnext); + vsp->vs_end = vnext->vs_end; + vmem_seg_destroy(vmp, vnext); + } + + /* + * Attempt to coalesce with the previous segment. + */ + vprev = vsp->vs_aprev; + if (vprev->vs_type == VMEM_FREE) { + ASSERT(vprev->vs_end == vsp->vs_start); + vmem_freelist_delete(vmp, vprev); + vprev->vs_end = vsp->vs_end; + vmem_seg_destroy(vmp, vsp); + vsp = vprev; + } + + /* + * If the entire span is free, return it to the source. + */ + if (vsp->vs_aprev->vs_import && vmp->vm_source_free != NULL && + vsp->vs_aprev->vs_type == VMEM_SPAN && + vsp->vs_anext->vs_type == VMEM_SPAN) { + vaddr = (void *)vsp->vs_start; + size = VS_SIZE(vsp); + ASSERT(size == VS_SIZE(vsp->vs_aprev)); + vmem_span_destroy(vmp, vsp); + vmp->vm_kstat.vk_parent_free.value.ui64++; + mutex_exit(&vmp->vm_lock); + vmp->vm_source_free(vmp->vm_source, vaddr, size); + } else { + vmem_freelist_insert(vmp, vsp); + mutex_exit(&vmp->vm_lock); + } +} + +/* + * Allocate size bytes from arena vmp. Returns the allocated address + * on success, NULL on failure. vmflag specifies VM_SLEEP or VM_NOSLEEP, + * and may also specify best-fit, first-fit, or next-fit allocation policy + * instead of the default instant-fit policy. VM_SLEEP allocations are + * guaranteed to succeed. + */ +void * +vmem_alloc(vmem_t *vmp, size_t size, int vmflag) +{ + vmem_seg_t *vsp; + uintptr_t addr; + int hb; + int flist = 0; + uint32_t mtbf; + + if (size - 1 < vmp->vm_qcache_max) + return (kmem_cache_alloc(vmp->vm_qcache[(size - 1) >> + vmp->vm_qshift], vmflag & VM_KMFLAGS)); + + if ((mtbf = vmem_mtbf | vmp->vm_mtbf) != 0 && gethrtime() % mtbf == 0 && + (vmflag & (VM_NOSLEEP | VM_PANIC)) == VM_NOSLEEP) + return (NULL); + + if (vmflag & VM_NEXTFIT) + return (vmem_nextfit_alloc(vmp, size, vmflag)); + + if (vmflag & (VM_BESTFIT | VM_FIRSTFIT)) + return (vmem_xalloc(vmp, size, vmp->vm_quantum, 0, 0, + NULL, NULL, vmflag)); + if (vmp->vm_cflags & VM_NEXTFIT) + return (vmem_nextfit_alloc(vmp, size, vmflag)); + + /* + * Unconstrained instant-fit allocation from the segment list. + */ + mutex_enter(&vmp->vm_lock); + + if (vmp->vm_nsegfree >= VMEM_MINFREE || vmem_populate(vmp, vmflag)) { + if ((size & (size - 1)) == 0) + flist = lowbit(P2ALIGN(vmp->vm_freemap, size)); + else if ((hb = highbit(size)) < VMEM_FREELISTS) + flist = lowbit(P2ALIGN(vmp->vm_freemap, 1UL << hb)); + } + + if (flist-- == 0) { + mutex_exit(&vmp->vm_lock); + return (vmem_xalloc(vmp, size, vmp->vm_quantum, + 0, 0, NULL, NULL, vmflag)); + } + + ASSERT(size <= (1UL << flist)); + vsp = vmp->vm_freelist[flist].vs_knext; + addr = vsp->vs_start; + if (vmflag & VM_ENDALLOC) { + addr += vsp->vs_end - (addr + size); + } + (void) vmem_seg_alloc(vmp, vsp, addr, size); + mutex_exit(&vmp->vm_lock); + return ((void *)addr); +} + +/* + * Free the segment [vaddr, vaddr + size). + */ +void +vmem_free(vmem_t *vmp, void *vaddr, size_t size) +{ + if (size - 1 < vmp->vm_qcache_max) + kmem_cache_free(vmp->vm_qcache[(size - 1) >> vmp->vm_qshift], + vaddr); + else + vmem_xfree(vmp, vaddr, size); +} + +/* + * Determine whether arena vmp contains the segment [vaddr, vaddr + size). + */ +int +vmem_contains(vmem_t *vmp, void *vaddr, size_t size) +{ + uintptr_t start = (uintptr_t)vaddr; + uintptr_t end = start + size; + vmem_seg_t *vsp; + vmem_seg_t *seg0 = &vmp->vm_seg0; + + mutex_enter(&vmp->vm_lock); + vmp->vm_kstat.vk_contains.value.ui64++; + for (vsp = seg0->vs_knext; vsp != seg0; vsp = vsp->vs_knext) { + vmp->vm_kstat.vk_contains_search.value.ui64++; + ASSERT(vsp->vs_type == VMEM_SPAN); + if (start >= vsp->vs_start && end - 1 <= vsp->vs_end - 1) + break; + } + mutex_exit(&vmp->vm_lock); + return (vsp != seg0); +} + +/* + * Add the span [vaddr, vaddr + size) to arena vmp. + */ +void * +vmem_add(vmem_t *vmp, void *vaddr, size_t size, int vmflag) +{ + if (vaddr == NULL || size == 0) + panic("vmem_add(%p, %p, %lu): bad arguments", + (void *)vmp, vaddr, size); + + ASSERT(!vmem_contains(vmp, vaddr, size)); + + mutex_enter(&vmp->vm_lock); + if (vmem_populate(vmp, vmflag)) + (void) vmem_span_create(vmp, vaddr, size, 0); + else + vaddr = NULL; + mutex_exit(&vmp->vm_lock); + return (vaddr); +} + +/* + * Walk the vmp arena, applying func to each segment matching typemask. + * If VMEM_REENTRANT is specified, the arena lock is dropped across each + * call to func(); otherwise, it is held for the duration of vmem_walk() + * to ensure a consistent snapshot. Note that VMEM_REENTRANT callbacks + * are *not* necessarily consistent, so they may only be used when a hint + * is adequate. + */ +void +vmem_walk(vmem_t *vmp, int typemask, + void (*func)(void *, void *, size_t), void *arg) +{ + vmem_seg_t *vsp; + vmem_seg_t *seg0 = &vmp->vm_seg0; + vmem_seg_t walker; + + if (typemask & VMEM_WALKER) + return; + + bzero(&walker, sizeof (walker)); + walker.vs_type = VMEM_WALKER; + + mutex_enter(&vmp->vm_lock); + VMEM_INSERT(seg0, &walker, a); + for (vsp = seg0->vs_anext; vsp != seg0; vsp = vsp->vs_anext) { + if (vsp->vs_type & typemask) { + void *start = (void *)vsp->vs_start; + size_t size = VS_SIZE(vsp); + if (typemask & VMEM_REENTRANT) { + vmem_advance(vmp, &walker, vsp); + mutex_exit(&vmp->vm_lock); + func(arg, start, size); + mutex_enter(&vmp->vm_lock); + vsp = &walker; + } else { + func(arg, start, size); + } + } + } + vmem_advance(vmp, &walker, NULL); + mutex_exit(&vmp->vm_lock); +} + +/* + * Return the total amount of memory whose type matches typemask. Thus: + * + * typemask VMEM_ALLOC yields total memory allocated (in use). + * typemask VMEM_FREE yields total memory free (available). + * typemask (VMEM_ALLOC | VMEM_FREE) yields total arena size. + */ +size_t +vmem_size(vmem_t *vmp, int typemask) +{ + int64_t size = 0; + + if (typemask & VMEM_ALLOC) + size += (int64_t)vmp->vm_kstat.vk_mem_inuse.value.ui64; + if (typemask & VMEM_FREE) + size += (int64_t)vmp->vm_kstat.vk_mem_total.value.ui64 - + (int64_t)vmp->vm_kstat.vk_mem_inuse.value.ui64; + if (size < 0) + size = 0; + + return ((size_t)size); +} + +size_t +vmem_size_locked(vmem_t *vmp, int typemask) +{ + boolean_t m = (mutex_owner(&vmp->vm_lock) == curthread); + + if (!m) + mutex_enter(&vmp->vm_lock); + size_t s = vmem_size(vmp, typemask); + if (!m) + mutex_exit(&vmp->vm_lock); + return (s); +} + +size_t +vmem_size_semi_atomic(vmem_t *vmp, int typemask) +{ + int64_t size = 0; + uint64_t inuse = 0; + uint64_t total = 0; + + __sync_swap(&total, vmp->vm_kstat.vk_mem_total.value.ui64); + __sync_swap(&inuse, vmp->vm_kstat.vk_mem_inuse.value.ui64); + + int64_t inuse_signed = (int64_t)inuse; + int64_t total_signed = (int64_t)total; + + if (typemask & VMEM_ALLOC) + size += inuse_signed; + if (typemask & VMEM_FREE) + size += total_signed - inuse_signed; + + if (size < 0) + size = 0; + + return ((size_t)size); +} + +size_t +spl_vmem_size(vmem_t *vmp, int typemask) +{ + return (vmem_size_locked(vmp, typemask)); +} + +/* + * Create an arena called name whose initial span is [base, base + size). + * The arena's natural unit of currency is quantum, so vmem_alloc() + * guarantees quantum-aligned results. The arena may import new spans + * by invoking afunc() on source, and may return those spans by invoking + * ffunc() on source. To make small allocations fast and scalable, + * the arena offers high-performance caching for each integer multiple + * of quantum up to qcache_max. + */ +static vmem_t * +vmem_create_common(const char *name, void *base, size_t size, size_t quantum, + void *(*afunc)(vmem_t *, size_t, int), + void (*ffunc)(vmem_t *, void *, size_t), + vmem_t *source, size_t qcache_max, int vmflag) +{ + int i; + size_t nqcache; + vmem_t *vmp, *cur, **vmpp; + vmem_seg_t *vsp; + vmem_freelist_t *vfp; + uint32_t id = atomic_inc_32_nv(&vmem_id); + + if (vmem_vmem_arena != NULL) { + vmp = vmem_alloc(vmem_vmem_arena, sizeof (vmem_t), + vmflag & VM_KMFLAGS); + } else { + ASSERT(id <= VMEM_INITIAL); + vmp = &vmem0[id - 1]; + } + + /* An identifier arena must inherit from another identifier arena */ + ASSERT(source == NULL || ((source->vm_cflags & VMC_IDENTIFIER) == + (vmflag & VMC_IDENTIFIER))); + + if (vmp == NULL) + return (NULL); + bzero(vmp, sizeof (vmem_t)); + + (void) snprintf(vmp->vm_name, VMEM_NAMELEN, "%s", name); + mutex_init(&vmp->vm_lock, NULL, MUTEX_DEFAULT, NULL); + cv_init(&vmp->vm_cv, NULL, CV_DEFAULT, NULL); + vmp->vm_cflags = vmflag; + vmflag &= VM_KMFLAGS; + + hrtime_t hrnow = gethrtime(); + + vmp->vm_createtime = hrnow; + + vmp->vm_quantum = quantum; + vmp->vm_qshift = highbit(quantum) - 1; + nqcache = MIN(qcache_max >> vmp->vm_qshift, VMEM_NQCACHE_MAX); + + for (i = 0; i <= VMEM_FREELISTS; i++) { + vfp = &vmp->vm_freelist[i]; + vfp->vs_end = 1UL << i; + vfp->vs_knext = (vmem_seg_t *)(vfp + 1); + vfp->vs_kprev = (vmem_seg_t *)(vfp - 1); + } + + vmp->vm_freelist[0].vs_kprev = NULL; + vmp->vm_freelist[VMEM_FREELISTS].vs_knext = NULL; + vmp->vm_freelist[VMEM_FREELISTS].vs_end = 0; + vmp->vm_hash_table = vmp->vm_hash0; + vmp->vm_hash_mask = VMEM_HASH_INITIAL - 1; + vmp->vm_hash_shift = highbit(vmp->vm_hash_mask); + + vsp = &vmp->vm_seg0; + vsp->vs_anext = vsp; + vsp->vs_aprev = vsp; + vsp->vs_knext = vsp; + vsp->vs_kprev = vsp; + vsp->vs_type = VMEM_SPAN; + vsp->vs_span_createtime = hrnow; + + vsp = &vmp->vm_rotor; + vsp->vs_type = VMEM_ROTOR; + VMEM_INSERT(&vmp->vm_seg0, vsp, a); + + bcopy(&vmem_kstat_template, &vmp->vm_kstat, sizeof (vmem_kstat_t)); + + vmp->vm_id = id; + if (source != NULL) + vmp->vm_kstat.vk_source_id.value.ui32 = source->vm_id; + vmp->vm_source = source; + vmp->vm_source_alloc = afunc; + vmp->vm_source_free = ffunc; + + /* + * Some arenas (like vmem_metadata and kmem_metadata) cannot + * use quantum caching to lower fragmentation. Instead, we + * increase their imports, giving a similar effect. + */ + if (vmp->vm_cflags & VMC_NO_QCACHE) { + if (qcache_max > VMEM_NQCACHE_MAX && ISP2(qcache_max)) { + vmp->vm_min_import = qcache_max; + } else { + vmp->vm_min_import = + VMEM_QCACHE_SLABSIZE(nqcache << vmp->vm_qshift); + } + nqcache = 0; + } + + if (nqcache != 0) { + ASSERT(!(vmflag & VM_NOSLEEP)); + vmp->vm_qcache_max = nqcache << vmp->vm_qshift; + for (i = 0; i < nqcache; i++) { + char buf[VMEM_NAMELEN + 21]; + (void) snprintf(buf, VMEM_NAMELEN + 20, "%s_%lu", + vmp->vm_name, (i + 1) * quantum); + vmp->vm_qcache[i] = kmem_cache_create(buf, + (i + 1) * quantum, quantum, NULL, NULL, NULL, + NULL, vmp, KMC_QCACHE | KMC_NOTOUCH); + } + } + + if ((vmp->vm_ksp = kstat_create("vmem", vmp->vm_id, vmp->vm_name, + "vmem", KSTAT_TYPE_NAMED, sizeof (vmem_kstat_t) / + sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL)) != NULL) { + vmp->vm_ksp->ks_data = &vmp->vm_kstat; + kstat_install(vmp->vm_ksp); + } + + mutex_enter(&vmem_list_lock); + vmpp = &vmem_list; + while ((cur = *vmpp) != NULL) + vmpp = &cur->vm_next; + *vmpp = vmp; + mutex_exit(&vmem_list_lock); + + if (vmp->vm_cflags & VMC_POPULATOR) { + ASSERT(vmem_populators < VMEM_INITIAL); + vmem_populator[atomic_inc_32_nv(&vmem_populators) - 1] = vmp; + mutex_enter(&vmp->vm_lock); + (void) vmem_populate(vmp, vmflag | VM_PANIC); + mutex_exit(&vmp->vm_lock); + } + + if ((base || size) && vmem_add(vmp, base, size, vmflag) == NULL) { + vmem_destroy(vmp); + return (NULL); + } + + return (vmp); +} + +vmem_t * +vmem_xcreate(const char *name, void *base, size_t size, size_t quantum, + vmem_ximport_t *afunc, vmem_free_t *ffunc, vmem_t *source, + size_t qcache_max, int vmflag) +{ + ASSERT(!(vmflag & (VMC_POPULATOR | VMC_XALLOC))); + vmflag &= ~(VMC_POPULATOR | VMC_XALLOC); + + return (vmem_create_common(name, base, size, quantum, + (vmem_alloc_t *)afunc, ffunc, source, qcache_max, + vmflag | VMC_XALLOC)); +} + +vmem_t * +vmem_create(const char *name, void *base, size_t size, size_t quantum, + vmem_alloc_t *afunc, vmem_free_t *ffunc, vmem_t *source, + size_t qcache_max, int vmflag) +{ + ASSERT(!(vmflag & (VMC_XALLOC | VMC_XALIGN))); + vmflag &= ~(VMC_XALLOC | VMC_XALIGN); + + return (vmem_create_common(name, base, size, quantum, + afunc, ffunc, source, qcache_max, vmflag)); +} + +/* + * Destroy arena vmp. + */ +void +vmem_destroy(vmem_t *vmp) +{ + vmem_t *cur, **vmpp; + vmem_seg_t *seg0 = &vmp->vm_seg0; + vmem_seg_t *vsp, *anext; + size_t leaked; + + /* + * set vm_nsegfree to zero because vmem_free_span_list + * would have already freed vm_segfree. + */ + vmp->vm_nsegfree = 0; + mutex_enter(&vmem_list_lock); + vmpp = &vmem_list; + while ((cur = *vmpp) != vmp) + vmpp = &cur->vm_next; + *vmpp = vmp->vm_next; + mutex_exit(&vmem_list_lock); + + leaked = vmem_size(vmp, VMEM_ALLOC); + if (leaked != 0) + printf("SPL: vmem_destroy('%s'): leaked %lu %s\n", + vmp->vm_name, leaked, (vmp->vm_cflags & VMC_IDENTIFIER) ? + "identifiers" : "bytes"); + + if (vmp->vm_hash_table != vmp->vm_hash0) + if (vmem_hash_arena != NULL) + vmem_free(vmem_hash_arena, vmp->vm_hash_table, + (vmp->vm_hash_mask + 1) * sizeof (void *)); + + /* + * Give back the segment structures for anything that's left in the + * arena, e.g. the primary spans and their free segments. + */ + VMEM_DELETE(&vmp->vm_rotor, a); + for (vsp = seg0->vs_anext; vsp != seg0; vsp = anext) { + anext = vsp->vs_anext; + vmem_putseg_global(vsp); + } + + while (vmp->vm_nsegfree > 0) + vmem_putseg_global(vmem_getseg(vmp)); + + kstat_delete(vmp->vm_ksp); + + mutex_destroy(&vmp->vm_lock); + cv_destroy(&vmp->vm_cv); + vmem_free(vmem_vmem_arena, vmp, sizeof (vmem_t)); +} + + +/* + * Destroy arena vmp. + */ +void +vmem_destroy_internal(vmem_t *vmp) +{ + vmem_t *cur, **vmpp; + vmem_seg_t *seg0 = &vmp->vm_seg0; + vmem_seg_t *vsp, *anext; + size_t leaked; + + mutex_enter(&vmem_list_lock); + vmpp = &vmem_list; + while ((cur = *vmpp) != vmp) + vmpp = &cur->vm_next; + *vmpp = vmp->vm_next; + mutex_exit(&vmem_list_lock); + + leaked = vmem_size(vmp, VMEM_ALLOC); + if (leaked != 0) + printf("SPL: vmem_destroy('%s'): leaked %lu %s\n", + vmp->vm_name, leaked, (vmp->vm_cflags & VMC_IDENTIFIER) ? + "identifiers" : "bytes"); + + if (vmp->vm_hash_table != vmp->vm_hash0) + if (vmem_hash_arena != NULL) + vmem_free(vmem_hash_arena, vmp->vm_hash_table, + (vmp->vm_hash_mask + 1) * sizeof (void *)); + + /* + * Give back the segment structures for anything that's left in the + * arena, e.g. the primary spans and their free segments. + */ + VMEM_DELETE(&vmp->vm_rotor, a); + for (vsp = seg0->vs_anext; vsp != seg0; vsp = anext) { + anext = vsp->vs_anext; + vmem_putseg_global(vsp); + } + + while (vmp->vm_nsegfree > 0) + vmem_putseg_global(vmem_getseg(vmp)); + + if (!(vmp->vm_cflags & VMC_IDENTIFIER) && + vmem_size(vmp, VMEM_ALLOC) != 0) + printf("SPL: vmem_destroy('%s'): STILL %lu bytes at " + "kstat_delete() time\n", + vmp->vm_name, vmem_size(vmp, VMEM_ALLOC)); + + kstat_delete(vmp->vm_ksp); + + mutex_destroy(&vmp->vm_lock); + cv_destroy(&vmp->vm_cv); + + // Alas, to free, requires access to "vmem_vmem_arena" the very thing + // we release first. + // vmem_free(vmem_vmem_arena, vmp, sizeof (vmem_t)); +} + +/* + * Only shrink vmem hashtable if it is 1<vm_kstat.vk_alloc.value.ui64 - + vmp->vm_kstat.vk_free.value.ui64); + + new_size = MAX(VMEM_HASH_INITIAL, 1 << (highbit(3 * nseg + 4) - 2)); + old_size = vmp->vm_hash_mask + 1; + + if ((old_size >> vmem_rescale_minshift) <= new_size && + new_size <= (old_size << 1)) + return; + + new_table = vmem_alloc(vmem_hash_arena, new_size * sizeof (void *), + VM_NOSLEEP); + if (new_table == NULL) + return; + bzero(new_table, new_size * sizeof (void *)); + + mutex_enter(&vmp->vm_lock); + + old_size = vmp->vm_hash_mask + 1; + old_table = vmp->vm_hash_table; + + vmp->vm_hash_mask = new_size - 1; + vmp->vm_hash_table = new_table; + vmp->vm_hash_shift = highbit(vmp->vm_hash_mask); + + for (h = 0; h < old_size; h++) { + vsp = old_table[h]; + while (vsp != NULL) { + uintptr_t addr = vsp->vs_start; + vmem_seg_t *next_vsp = vsp->vs_knext; + vmem_seg_t **hash_bucket = VMEM_HASH(vmp, addr); + vsp->vs_knext = *hash_bucket; + *hash_bucket = vsp; + vsp = next_vsp; + } + } + + mutex_exit(&vmp->vm_lock); + + if (old_table != vmp->vm_hash0) + vmem_free(vmem_hash_arena, old_table, + old_size * sizeof (void *)); +} + +/* + * Perform periodic maintenance on all vmem arenas. + */ + +void +vmem_update(void *dummy) +{ + vmem_t *vmp; + + mutex_enter(&vmem_list_lock); + for (vmp = vmem_list; vmp != NULL; vmp = vmp->vm_next) { + /* + * If threads are waiting for resources, wake them up + * periodically so they can issue another kmem_reap() + * to reclaim resources cached by the slab allocator. + */ + cv_broadcast(&vmp->vm_cv); + + /* + * Rescale the hash table to keep the hash chains short. + */ + vmem_hash_rescale(vmp); + } + mutex_exit(&vmem_list_lock); + + (void) bsd_timeout(vmem_update, dummy, &vmem_update_interval); +} + +void +vmem_qcache_reap(vmem_t *vmp) +{ + int i; + + /* + * Reap any quantum caches that may be part of this vmem. + */ + for (i = 0; i < VMEM_NQCACHE_MAX; i++) + if (vmp->vm_qcache[i]) + kmem_cache_reap_now(vmp->vm_qcache[i]); +} + +/* given a size, return the appropriate vmem_bucket_arena[] entry */ + +static inline uint16_t +vmem_bucket_number(size_t size) +{ + // For VMEM_BUCKET_HIBIT == 12, + // vmem_bucket_arena[n] holds allocations from 2^[n+11]+1 to 2^[n+12], + // so for [n] = 0, 2049-4096, for [n]=5 65537-131072, + // for [n]=7 (256k+1)-512k + // set hb: 512k == 19, 256k+1 == 19, 256k == 18, ... + const int hb = highbit(size-1); + + int bucket = hb - VMEM_BUCKET_LOWBIT; + + // very large allocations go into the 16 MiB bucket + if (hb > VMEM_BUCKET_HIBIT) + bucket = VMEM_BUCKET_HIBIT - VMEM_BUCKET_LOWBIT; + + // very small allocations go into the 4 kiB bucket + if (bucket < 0) + bucket = 0; + + return (bucket); +} + +static inline vmem_t * +vmem_bucket_arena_by_size(size_t size) +{ + uint16_t bucket = vmem_bucket_number(size); + + return (vmem_bucket_arena[bucket]); +} + +vmem_t * +spl_vmem_bucket_arena_by_size(size_t size) +{ + return (vmem_bucket_arena_by_size(size)); +} + +static inline void +vmem_bucket_wake_all_waiters(void) +{ + for (int i = VMEM_BUCKET_LOWBIT; i < VMEM_BUCKET_HIBIT; i++) { + const int bucket = i - VMEM_BUCKET_LOWBIT; + vmem_t *bvmp = vmem_bucket_arena[bucket]; + cv_broadcast(&bvmp->vm_cv); + } + cv_broadcast(&spl_heap_arena->vm_cv); +} + +/* + * xnu_alloc_throttled_bail() : spin looking for memory + * + */ + +static inline void * +xnu_alloc_throttled_bail(uint64_t now_ticks, vmem_t *calling_vmp, + size_t size, int vmflags) +{ + // spin looking for memory + const uint64_t bigtarget = MAX(size, 16ULL*1024ULL*1024ULL); + static volatile _Atomic bool alloc_lock = false; + static volatile _Atomic uint64_t force_time = 0; + + uint64_t timeout_ticks = hz / 2; + if (vmflags & VM_PUSHPAGE) + timeout_ticks = hz / 4; + + uint64_t timeout_time = now_ticks + timeout_ticks; + + for (uint32_t suspends = 0, blocked_suspends = 0, + try_no_pressure = 0; /* empty */; /* empty */) { + if (force_time + timeout_ticks > timeout_time) { + // another thread has forced an allocation + // by timing out. push our deadline into the future. + timeout_time = force_time + timeout_ticks; + } + if (alloc_lock) { + blocked_suspends++; + IOSleep(1); + } else if (spl_vmem_xnu_useful_bytes_free() >= bigtarget) { + bool f = false; + // if alloc_lock == f then alloc_lock = true and result + // is true otherwise result is false and f = true + if (!__c11_atomic_compare_exchange_strong(&alloc_lock, + &f, true, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED)) { + /* + * avoid (highly unlikely) data race on + * alloc_lock. if alloc_lock has become true + * while we were in the else if expression + * then we effectively optimize away the + * (relaxed) load of alloc_lock (== true) + * into f and continue. + */ + continue; + } + // alloc_lock is now visible as true to all threads + try_no_pressure++; + void *m = spl_vmem_malloc_if_no_pressure(size); + if (m != NULL) { + uint64_t ticks = zfs_lbolt() - now_ticks; + dprintf("SPL: %s returning %llu bytes after " + "%llu ticks (hz=%u, seconds = %llu), " + "%u suspends, %u blocked, %u tries (%s)\n", + __func__, (uint64_t)size, + ticks, hz, ticks/hz, suspends, + blocked_suspends, try_no_pressure, + calling_vmp->vm_name); + // atomic seq cst, so is published to all + // threads + alloc_lock = false; + return (m); + } else { + alloc_lock = false; + spl_free_set_emergency_pressure(bigtarget); + suspends++; + IOSleep(1); + } + } else if (zfs_lbolt() > timeout_time) { + bool f = false; + if (!__c11_atomic_compare_exchange_strong(&alloc_lock, + &f, true, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED)) { + // avoid (highly unlikely) data race on + // alloc_lock as above + continue; + } + void *mp = spl_vmem_malloc_unconditionally(size); + uint64_t now = zfs_lbolt(); + uint64_t ticks = now - now_ticks; + force_time = now; + dprintf("SPL: %s TIMEOUT %llu bytes after " + "%llu ticks (hz=%u, seconds=%llu), " + "%u suspends, %u blocked, %u tries (%s)\n", + __func__, (uint64_t)size, + ticks, hz, ticks/hz, suspends, + blocked_suspends, try_no_pressure, + calling_vmp->vm_name); + alloc_lock = false; + atomic_inc_64(&spl_xat_forced); + return (mp); + } else { + spl_free_set_emergency_pressure(bigtarget); + suspends++; + IOSleep(1); + } + } +} + +static void * +xnu_alloc_throttled(vmem_t *bvmp, size_t size, int vmflag) +{ + // the caller is one of the bucket arenas. + // null_vmp will be spl_default_arena_parent, which is + // just a placeholder. + + uint64_t now = zfs_lbolt(); + const uint64_t entry_now = now; + + void *m = spl_vmem_malloc_if_no_pressure(size); + + if (m != NULL) { + atomic_inc_64(&spl_xat_success); + spl_xat_lastalloc = gethrtime(); + // wake up waiters on all the arena condvars + // since there is apparently no memory shortage. + vmem_bucket_wake_all_waiters(); + return (m); + } else { + spl_free_set_emergency_pressure((int64_t)size); + } + + if (vmflag & VM_PANIC) { + // force an allocation now to avoid a panic + spl_xat_lastalloc = gethrtime(); + spl_free_set_emergency_pressure(4LL * (int64_t)size); + void *p = spl_vmem_malloc_unconditionally(size); + // p cannot be NULL (unconditional kernel malloc always works + // or panics) + // therefore: success, wake all waiters on alloc|free condvar + // wake up arena waiters to let them know there is memory + // available in the arena; let waiters on other bucket arenas + // continue sleeping. + cv_broadcast(&bvmp->vm_cv); + return (p); + } + + if (vmflag & VM_NOSLEEP) { + spl_free_set_emergency_pressure(MAX(2LL * (int64_t)size, + 16LL*1024LL*1024LL)); + /* cheating a bit, but not really waiting */ + kpreempt(KPREEMPT_SYNC); + void *p = spl_vmem_malloc_if_no_pressure(size); + if (p != NULL) { + atomic_inc_64(&spl_xat_late_success_nosleep); + cv_broadcast(&bvmp->vm_cv); + spl_xat_lastalloc = gethrtime(); + } + // if p == NULL, then there will be an increment in + // the fail kstat + return (p); + } + + /* + * Loop for a while trying to satisfy VM_SLEEP allocations. + * + * If we are able to allocate memory, then return the pointer. + * + * We return NULL if some other thread's activity has caused + * sufficient memory to appear in this arena that we can satisfy + * the allocation. + * + * We call xnu_alloc_throttle_bail() after a few milliseconds of + * waiting; it will either return a pointer to newly allocated + * memory or NULL. We return the result. + * + */ + + const uint32_t bucket_number = + vmem_bucket_id_to_bucket_number[bvmp->vm_id]; + static volatile _Atomic uint32_t waiters = 0; + + waiters++; + + if (waiters == 1UL) + atomic_inc_64(&spl_xat_no_waiters); + + static _Atomic uint32_t max_waiters_seen = 0; + + if (waiters > max_waiters_seen) { + max_waiters_seen = waiters; + dprintf("SPL: %s: max_waiters_seen increased to %u\n", __func__, + max_waiters_seen); + } + + boolean_t local_xat_pressured = false; + + for (; /* empty */; /* empty */) { + clock_t wait_time = USEC2NSEC(500UL * MAX(waiters, 1UL)); + mutex_enter(&bvmp->vm_lock); + spl_xat_sleep++; + if (local_xat_pressured) { + spl_xat_pressured++; + local_xat_pressured = false; + } + (void) cv_timedwait_hires(&bvmp->vm_cv, &bvmp->vm_lock, + wait_time, 0, 0); + mutex_exit(&bvmp->vm_lock); + now = zfs_lbolt(); + // We may be here because of a broadcast to &vmp->vm_cv, + // causing xnu to schedule all the sleepers in priority-weighted + // FIFO order. Because of the mutex_exit(), the sections below + // here may be entered concurrently. + // spl_vmem_malloc_if_no_pressure does a mutex, so avoid calling + // it unless there is a chance it will succeed. + if (spl_vmem_xnu_useful_bytes_free() > (MAX(size, + 16ULL*1024ULL*1024ULL))) { + void *a = spl_vmem_malloc_if_no_pressure(size); + if (a != NULL) { + atomic_inc_64(&spl_xat_late_success); + spl_xat_lastalloc = gethrtime(); + waiters--; + // Wake up all waiters on the bucket arena + // locks, since the system apparently has + // memory again. + vmem_bucket_wake_all_waiters(); + return (a); + } else { + // Probably spl_vm_page_free_count changed while + // we were in the mutex queue in + // spl_vmem_malloc_if_no_pressure(). There is + // therefore no point in doing the bail-out + // check below, so go back to the top of the + // for loop. + atomic_inc_64(&spl_xat_late_deny); + continue; + } + } + if (now > entry_now + hz / 4 || + spl_vba_threads[bucket_number] > 1UL) { + // If there are other threads waiting for us + // in vba() then when we satisfy this allocation, + // we satisfy more than one thread, so invoke XATB(). + // Otherwise, if we have had no luck for 250 ms, then + // switch to XATB() which is much more aggressive. + if (spl_vba_threads[bucket_number] > 1UL) + atomic_inc_64(&spl_xat_bailed_contended); + atomic_inc_64(&spl_xat_bailed); + static _Atomic uint32_t bailing_threads = 0; + static _Atomic uint32_t max_bailers_seen = 0; + bailing_threads++; + if (bailing_threads > max_bailers_seen) { + max_bailers_seen = bailing_threads; + dprintf("SPL: %s: max_bailers_seen increased " + "to %u\n", __func__, max_bailers_seen); + } + void *b = + xnu_alloc_throttled_bail(now, bvmp, size, vmflag); + bailing_threads--; + spl_xat_lastalloc = gethrtime(); + // wake up waiters on the arena lock, + // since they now have memory they can use. + cv_broadcast(&bvmp->vm_cv); + // open turnstile after having bailed, rather + // than before + waiters--; + return (b); + } else if (now - entry_now > 0 && + ((now - entry_now) % (hz/10))) { + spl_free_set_emergency_pressure(MAX(size, + 16LL*1024LL*1024LL)); + local_xat_pressured = true; + } + } +} + +static void +xnu_free_throttled(vmem_t *vmp, void *vaddr, size_t size) +{ + extern void osif_free(void *, uint64_t); + + // Serialize behind a (short) spin-sleep delay, giving + // xnu time to do freelist management and + // PT teardowns + + // In the usual case there is only one thread in this function, + // so we proceed waitlessly to osif_free(). + + // When there are multiple threads here, we delay the 2nd and later. + + // Explict race: + // The osif_free() is not protected by the vmem_xnu_alloc_lock + // mutex; that is just used for implementing the delay. Consequently, + // the waiters on the same lock in spl_vmem_malloc_if_no_pressure may + // falsely see too small a value for spl_vm_page_free_count. We don't + // care in part because xnu performs poorly when doing + // free-then-allocate anwyay. + + // a_waiters gauges the loop exit checking and sleep duration; + // it is a count of the number of threads trying to do work + // in this function. + static volatile _Atomic uint32_t a_waiters = 0; + + // is_freeing protects the osif_free() call; see comment below + static volatile _Atomic bool is_freeing = false; + + a_waiters++; // generates "lock incl ..." + + static _Atomic uint32_t max_waiters_seen = 0; + + if (a_waiters > max_waiters_seen) { + max_waiters_seen = a_waiters; + dprintf("SPL: %s: max_waiters_seen increased to %u\n", + __func__, max_waiters_seen); + } + + for (uint32_t iter = 0; a_waiters > 1UL; iter++) { + // there is more than one thread here, so suspend and + // sleep for 1 ms + atomic_inc_64(&spl_xft_wait); + IOSleep(1); + // If are growing old in this loop, then see if + // anyone else is still in osif_free. If not, + // we can exit. + if (iter >= a_waiters) { + // if is_freeing == f, then set is_freeing to true with + // release semantics (i.e. "push" it to other cores) + // then break; otherwise, set f to true relaxedly (i.e., + // optimize it out) + bool f = false; + if (__c11_atomic_compare_exchange_weak(&is_freeing, + &f, true, __ATOMIC_RELEASE, __ATOMIC_RELAXED)) { + break; + } + } + } + // If there is more than one thread in this function, osif_free() is + // protected by is_freeing. Release it after the osif_free() + // call has been made and the lastfree bookkeeping has been done. + osif_free(vaddr, size); + spl_xat_lastfree = gethrtime(); + is_freeing = false; + a_waiters--; + kpreempt(KPREEMPT_SYNC); + // since we just gave back xnu enough to satisfy an allocation + // in at least the smaller buckets, let's wake up anyone in + // the cv_wait() in vmem_xalloc([bucket_#], ...) + vmem_bucket_wake_all_waiters(); +} + +// return 0 if the bit was unset before the atomic OR. +static inline bool +vba_atomic_lock_bucket(volatile _Atomic uint16_t *bbap, uint16_t bucket_bit) +{ + + // We use a test-and-set of the appropriate bit + // in buckets_busy_allocating; if it was not set, + // then break out of the loop. + // + // This compiles into an orl, cmpxchgw instruction pair. + // the return from __c11_atomic_fetch_or() is the + // previous value of buckets_busy_allocating. + + uint16_t prev = + __c11_atomic_fetch_or(bbap, bucket_bit, __ATOMIC_SEQ_CST); + if (prev & bucket_bit) + return (false); // we did not acquire the bit lock here + else + return (true); // we turned the bit from 0 to 1 +} + +static void * +vmem_bucket_alloc(vmem_t *null_vmp, size_t size, const int vmflags) +{ + + if (vmflags & VM_NO_VBA) + return (NULL); + + // caller is spl_heap_arena looking for memory. + // null_vmp will be spl_default_arena_parent, and so + // is just a placeholder. + + vmem_t *calling_arena = spl_heap_arena; + + static volatile _Atomic uint32_t hipriority_allocators = 0; + boolean_t local_hipriority_allocator = false; + + if (0 != (vmflags & (VM_PUSHPAGE | VM_NOSLEEP | VM_PANIC | VM_ABORT))) { + local_hipriority_allocator = true; + hipriority_allocators++; + } + + if (!ISP2(size)) + atomic_inc_64(&spl_bucket_non_pow2_allocs); + + vmem_t *bvmp = vmem_bucket_arena_by_size(size); + + // there are 13 buckets, so use a 16-bit scalar to hold + // a set of bits, where each bit corresponds to an in-progress + // vmem_alloc(bucket, ...) below. + + static volatile _Atomic uint16_t buckets_busy_allocating = 0; + const uint16_t bucket_number = vmem_bucket_number(size); + const uint16_t bucket_bit = (uint16_t)1 << bucket_number; + + spl_vba_threads[bucket_number]++; + + static volatile _Atomic uint32_t waiters = 0; + + // First, if we are VM_SLEEP, check for memory, try some pressure, + // and if that doesn't work, force entry into the loop below. + + bool loop_once = false; + + if ((vmflags & (VM_NOSLEEP | VM_PANIC | VM_ABORT)) == 0 && + ! vmem_canalloc_atomic(bvmp, size)) { + if (spl_vmem_xnu_useful_bytes_free() < (MAX(size, + 16ULL*1024ULL*1024ULL))) { + spl_free_set_emergency_pressure(size); + IOSleep(1); + if (!vmem_canalloc_atomic(bvmp, size) && + (spl_vmem_xnu_useful_bytes_free() < (MAX(size, + 16ULL*1024ULL*1024ULL)))) { + loop_once = true; + } + } + } + + // spin-sleep: if we would need to go to the xnu allocator. + // + // We want to avoid a burst of allocs from bucket_heap's children + // successively hitting a low-memory condition, or alternatively + // each successfully importing memory from xnu when they can share + // a single import. + // + // We also want to take advantage of any memory that becomes available + // in bucket_heap. + // + // If there is more than one thread in this function (~ few percent) + // then the subsequent threads are put into the loop below. They + // can escape the loop if they are [1]non-waiting allocations, or + // [2]if they become the only waiting thread, or + // [3]if the cv_timedwait_hires returns -1 (which represents EWOULDBLOCK + // from msleep() which gets it from _sleep()'s THREAD_TIMED_OUT) + // allocating in the bucket, or [4]if this thread has (rare condition) + // spent a quarter of a second in the loop. + + if (waiters++ > 1 || loop_once) { + atomic_inc_64(&spl_vba_loop_entries); + } + + static _Atomic uint32_t max_waiters_seen = 0; + + if (waiters > max_waiters_seen) { + max_waiters_seen = waiters; + dprintf("SPL: %s: max_waiters_seen increased to %u\n", __func__, + max_waiters_seen); + } + + // local counters, to be added atomically to global kstat variables + uint64_t local_memory_blocked = 0, local_cv_timeout = 0; + uint64_t local_loop_timeout = 0; + uint64_t local_cv_timeout_blocked = 0, local_loop_timeout_blocked = 0; + uint64_t local_sleep = 0, local_hipriority_blocked = 0; + + const uint64_t loop_ticks = 25; // a tick is 10 msec, so 250 msec + const uint64_t hiprio_loop_ticks = 4; // 40 msec + + for (uint64_t entry_time = zfs_lbolt(), + loop_timeout = entry_time + loop_ticks, + hiprio_timeout = entry_time + hiprio_loop_ticks, timedout = 0; + waiters > 1UL || loop_once; /* empty */) { + loop_once = false; + // non-waiting allocations should proceeed to vmem_alloc() + // immediately + if (vmflags & (VM_NOSLEEP | VM_PANIC | VM_ABORT)) { + break; + } + if (vmem_canalloc_atomic(bvmp, size)) { + // We can probably vmem_alloc(bvmp, size, vmflags). + // At worst case it will give us a NULL and we will + // end up on the vmp's cv_wait. + // + // We can have threads with different bvmp + // taking this exit, and will proceed concurrently. + // + // However, we should protect against a burst of + // callers hitting the same bvmp before the allocation + // results are reflected in + // vmem_canalloc_atomic(bvmp, ...) + if (local_hipriority_allocator == false && + hipriority_allocators > 0) { + // more high priority allocations are wanted, + // so this thread stays here + local_hipriority_blocked++; + } else if (vba_atomic_lock_bucket( + &buckets_busy_allocating, bucket_bit)) { + // we are not being blocked by another allocator + // to the same bucket, or any higher priority + // allocator + atomic_inc_64(&spl_vba_parent_memory_appeared); + break; + // The vmem_alloc() should return extremely + // quickly from an INSTANTFIT allocation that + // canalloc predicts will succeed. + } else { + // another thread is trying to use the free + // memory in the bucket_## arena; there might + // still be free memory there after its + // allocation is completed, and there might be + // excess in the bucket_heap arena, so stick + // around in this loop. + local_memory_blocked++; + cv_broadcast(&bvmp->vm_cv); + } + } + if (timedout > 0) { + if (local_hipriority_allocator == false && + hipriority_allocators > 0) { + local_hipriority_blocked++; + } else if (vba_atomic_lock_bucket( + &buckets_busy_allocating, bucket_bit)) { + if (timedout & 1) + local_cv_timeout++; + if (timedout & 6 || zfs_lbolt() >= loop_timeout) + local_loop_timeout++; + break; + } else { + if (timedout & 1) { + local_cv_timeout_blocked++; + } + if (timedout & 6) { + local_loop_timeout_blocked++; + } else if (zfs_lbolt() > loop_timeout) { + timedout |= 2; + } + // flush the current thread in xat() out of + // xat()'s for() loop and into xat_bail() + cv_broadcast(&bvmp->vm_cv); + } + } + // The bucket is already allocating, or the bucket needs + // more memory to satisfy vmem_allocat(bvmp, size, VM_NOSLEEP), + // or we want to give the bucket some time to acquire more + // memory. + // substitute for the vmp arena's cv_wait in vmem_xalloc() + // (vmp is the bucket_heap AKA spl_heap_arena) + mutex_enter(&calling_arena->vm_lock); + local_sleep++; + if (local_sleep >= 1000ULL) { + atomic_add_64(&spl_vba_sleep, local_sleep - 1ULL); + local_sleep = 1ULL; + atomic_add_64(&spl_vba_cv_timeout_blocked, + local_cv_timeout_blocked); + local_cv_timeout_blocked = 0; + atomic_add_64(&spl_vba_loop_timeout_blocked, + local_loop_timeout_blocked); + local_loop_timeout_blocked = 0; + atomic_add_64(&spl_vba_hiprio_blocked, + local_hipriority_blocked); + local_hipriority_blocked = 0; + if (local_memory_blocked > 1ULL) { + atomic_add_64(&spl_vba_parent_memory_blocked, + local_memory_blocked - 1ULL); + local_memory_blocked = 1ULL; + } + } + clock_t wait_time = MSEC2NSEC(30); + if (timedout > 0 || local_memory_blocked > 0) { + wait_time = MSEC2NSEC(1); + } + int ret = cv_timedwait_hires(&calling_arena->vm_cv, + &calling_arena->vm_lock, + wait_time, 0, 0); + // We almost certainly have exited because of a + // signal/broadcast, but maybe just timed out. + // Either way, recheck memory. + mutex_exit(&calling_arena->vm_lock); + if (ret == -1) { + // cv_timedwait_hires timer expired + timedout |= 1; + cv_broadcast(&bvmp->vm_cv); + } else if ((timedout & 2) == 0) { + // we were awakened; check to see if we have been + // in the for loop for a long time + uint64_t n = zfs_lbolt(); + if (n > loop_timeout) { + timedout |= 2; + extern uint64_t real_total_memory; + spl_free_set_emergency_pressure( + real_total_memory / 64LL); + // flush the current thread in xat() out of + // xat()'s for() loop and into xat_bail() + cv_broadcast(&bvmp->vm_cv); + } else if (local_hipriority_allocator && + n > hiprio_timeout && waiters > 1UL) { + timedout |= 4; + } + } + } + + /* + * Turn on the exclusion bit in buckets_busy_allocating, to + * prevent multiple threads from calling vmem_alloc() on the + * same bucket arena concurrently rather than serially. + * + * This principally reduces the liklihood of asking xnu for + * more memory when other memory is or becomes available. + * + * This exclusion only applies to VM_SLEEP allocations; + * others (VM_PANIC, VM_NOSLEEP, VM_ABORT) will go to + * vmem_alloc() concurrently with any other threads. + * + * Since we aren't doing a test-and-set operation like above, + * we can just use |= and &= below and get correct atomic + * results, instead of using: + * + * __c11_atomic_fetch_or(&buckets_busy_allocating, + * bucket_bit, __ATOMIC_SEQ_CST); + * with the &= down below being written as + * __c11_atomic_fetch_and(&buckets_busy_allocating, + * ~bucket_bit, __ATOMIC_SEQ_CST); + * + * and this makes a difference with no optimization either + * compiling the whole file or with __attribute((optnone)) + * in front of the function decl. In particular, the non- + * optimized version that uses the builtin __c11_atomic_fetch_{and,or} + * preserves the C program order in the machine language output, + * inersting cmpxchgws, while all optimized versions, and the + * non-optimized version using the plainly-written version, reorder + * the "orw regr, memory" and "andw register, memory" (these are atomic + * RMW operations in x86-64 when the memory is naturally aligned) so + * that the strong memory model x86-64 promise that later loads see the + * results of earlier stores. + * + * clang+llvm simply are good at optimizing _Atomics and + * the optimized code differs only in line numbers and + * among all three approaches (as plainly written, using + * the __c11_atomic_fetch_{or,and} with sequential consistency, + * or when compiling with at least -O optimization so an + * atomic_or_16(&buckets_busy_allocating) built with GCC intrinsics + * is actually inlined rather than a function call). + * + */ + + // in case we left the loop by being the only waiter, stop the + // next thread arriving from leaving the for loop because + // vmem_canalloc(bvmp, that_thread's_size) is true. + + buckets_busy_allocating |= bucket_bit; + + // update counters + if (local_sleep > 0) + atomic_add_64(&spl_vba_sleep, local_sleep); + if (local_memory_blocked > 0) + atomic_add_64(&spl_vba_parent_memory_blocked, + local_memory_blocked); + if (local_cv_timeout > 0) + atomic_add_64(&spl_vba_cv_timeout, local_cv_timeout); + if (local_cv_timeout_blocked > 0) + atomic_add_64(&spl_vba_cv_timeout_blocked, + local_cv_timeout_blocked); + if (local_loop_timeout > 0) + atomic_add_64(&spl_vba_loop_timeout, local_loop_timeout); + if (local_loop_timeout_blocked > 0) + atomic_add_64(&spl_vba_loop_timeout_blocked, + local_loop_timeout_blocked); + if (local_hipriority_blocked > 0) + atomic_add_64(&spl_vba_hiprio_blocked, + local_hipriority_blocked); + + // There is memory in this bucket, or there are no other waiters, + // or we aren't a VM_SLEEP allocation, or we iterated out of the + // for loop. + // vmem_alloc() and vmem_xalloc() do their own mutex serializing + // on bvmp->vm_lock, so we don't have to here. + // + // vmem_alloc may take some time to return (especially for VM_SLEEP + // allocations where we did not take the vm_canalloc(bvmp...) break out + // of the for loop). Therefore, if we didn't enter the for loop at all + // because waiters was 0 when we entered this function, + // subsequent callers will enter the for loop. + + void *m = vmem_alloc(bvmp, size, vmflags); + + // allow another vmem_canalloc() through for this bucket + // by atomically turning off the appropriate bit + + /* + * Except clang+llvm DTRT because of _Atomic, could be written as: + * __c11_atomic_fetch_and(&buckets_busy_allocating, + * ~bucket_bit, __ATOMIC_SEQ_CST); + * + * On processors with more relaxed memory models, it might be + * more efficient to do so with release semantics here, and + * in the atomic |= above, with acquire semantics in the bit tests, + * but on the other hand it may be hard to do better than clang+llvm. + */ + + buckets_busy_allocating &= ~bucket_bit; + + if (local_hipriority_allocator) + hipriority_allocators--; + + // if we got an allocation, wake up the arena cv waiters + // to let them try to exit the for(;;) loop above and + // exit the cv_wait() in vmem_xalloc(vmp, ...) + + if (m != NULL) { + cv_broadcast(&calling_arena->vm_cv); + } + + waiters--; + spl_vba_threads[bucket_number]--; + return (m); +} + +static void +vmem_bucket_free(vmem_t *null_vmp, void *vaddr, size_t size) +{ + vmem_t *calling_arena = spl_heap_arena; + + vmem_free(vmem_bucket_arena_by_size(size), vaddr, size); + + // wake up arena waiters to let them try an alloc + cv_broadcast(&calling_arena->vm_cv); +} + +static inline int64_t +vmem_bucket_arena_free(uint16_t bucket) +{ + VERIFY(bucket < VMEM_BUCKETS); + return ((int64_t)vmem_size_semi_atomic(vmem_bucket_arena[bucket], + VMEM_FREE)); +} + +static inline int64_t +vmem_bucket_arena_used(int bucket) +{ + VERIFY(bucket < VMEM_BUCKETS); + return ((int64_t)vmem_size_semi_atomic(vmem_bucket_arena[bucket], + VMEM_ALLOC)); +} + + +int64_t +vmem_buckets_size(int typemask) +{ + int64_t total_size = 0; + + for (int i = 0; i < VMEM_BUCKETS; i++) { + int64_t u = vmem_bucket_arena_used(i); + int64_t f = vmem_bucket_arena_free(i); + if (typemask & VMEM_ALLOC) + total_size += u; + if (typemask & VMEM_FREE) + total_size += f; + } + if (total_size < 0) + total_size = 0; + + return ((size_t)total_size); +} + +static uint64_t +spl_validate_bucket_span_size(uint64_t val) +{ + if (!ISP2(val)) { + printf("SPL: %s: WARNING %llu is not a power of two, " + "not changing.\n", __func__, val); + return (0); + } + if (val < 128ULL*1024ULL || val > 16ULL*1024ULL*1024ULL) { + printf("SPL: %s: WARNING %llu is out of range [128k - 16M], " + "not changing.\n", __func__, val); + return (0); + } + return (val); +} + +static inline void +spl_modify_bucket_span_size(int bucket, uint64_t size) +{ + vmem_t *bvmp = vmem_bucket_arena[bucket]; + + mutex_enter(&bvmp->vm_lock); + bvmp->vm_min_import = size; + mutex_exit(&bvmp->vm_lock); +} + +static inline void +spl_modify_bucket_array() +{ + for (int i = VMEM_BUCKET_LOWBIT; i < VMEM_BUCKET_HIBIT; i++) { + // i = 12, bucket = 0, contains allocs from 8192 to 16383 bytes, + // and should never ask xnu for < 16384 bytes, so as to avoid + // asking xnu for a non-power-of-two size. + const int bucket = i - VMEM_BUCKET_LOWBIT; + const uint32_t bucket_alloc_minimum_size = 1UL << (uint32_t)i; + const uint32_t bucket_parent_alloc_minimum_size = + bucket_alloc_minimum_size * 2UL; + + switch (i) { + // see vmem_init() below for details + case 16: + case 17: + spl_modify_bucket_span_size(bucket, + MAX(spl_bucket_tunable_small_span, + bucket_parent_alloc_minimum_size)); + break; + default: + spl_modify_bucket_span_size(bucket, + MAX(spl_bucket_tunable_large_span, + bucket_parent_alloc_minimum_size)); + break; + } + } +} + +static inline void +spl_printf_bucket_span_sizes(void) +{ + // this doesn't have to be super-exact + dprintf("SPL: %s: ", __func__); + for (int i = VMEM_BUCKET_LOWBIT; i < VMEM_BUCKET_HIBIT; i++) { + int bnum = i - VMEM_BUCKET_LOWBIT; + vmem_t *bvmp = vmem_bucket_arena[bnum]; + dprintf("%llu ", (uint64_t)bvmp->vm_min_import); + } + dprintf("\n"); +} + +static inline void +spl_set_bucket_spans(uint64_t l, uint64_t s) +{ + if (spl_validate_bucket_span_size(l) && + spl_validate_bucket_span_size(s)) { + atomic_swap_64(&spl_bucket_tunable_large_span, l); + atomic_swap_64(&spl_bucket_tunable_small_span, s); + spl_modify_bucket_array(); + } +} + +void +spl_set_bucket_tunable_large_span(uint64_t size) +{ + uint64_t s = 0; + + mutex_enter(&vmem_xnu_alloc_lock); + atomic_swap_64(&s, spl_bucket_tunable_small_span); + spl_set_bucket_spans(size, s); + mutex_exit(&vmem_xnu_alloc_lock); + + spl_printf_bucket_span_sizes(); +} + +void +spl_set_bucket_tunable_small_span(uint64_t size) +{ + uint64_t l = 0; + + mutex_enter(&vmem_xnu_alloc_lock); + atomic_swap_64(&l, spl_bucket_tunable_large_span); + spl_set_bucket_spans(l, size); + mutex_exit(&vmem_xnu_alloc_lock); + + spl_printf_bucket_span_sizes(); +} + +static void * +spl_vmem_default_alloc(vmem_t *vmp, size_t size, int vmflags) +{ + extern void *osif_malloc(uint64_t); + return (osif_malloc(size)); +} + +static void +spl_vmem_default_free(vmem_t *vmp, void *vaddr, size_t size) +{ + extern void osif_free(void *, uint64_t); + osif_free(vaddr, size); +} + +vmem_t * +vmem_init(const char *heap_name, + void *heap_start, size_t heap_size, size_t heap_quantum, + void *(*heap_alloc)(vmem_t *, size_t, int), + void (*heap_free)(vmem_t *, void *, size_t)) +{ + uint32_t id; + int nseg = VMEM_SEG_INITIAL; + vmem_t *heap; + + // XNU mutexes need initialisation + mutex_init(&vmem_list_lock, "vmem_list_lock", MUTEX_DEFAULT, + NULL); + mutex_init(&vmem_segfree_lock, "vmem_segfree_lock", MUTEX_DEFAULT, + NULL); + mutex_init(&vmem_sleep_lock, "vmem_sleep_lock", MUTEX_DEFAULT, + NULL); + mutex_init(&vmem_nosleep_lock, "vmem_nosleep_lock", MUTEX_DEFAULT, + NULL); + mutex_init(&vmem_pushpage_lock, "vmem_pushpage_lock", MUTEX_DEFAULT, + NULL); + mutex_init(&vmem_panic_lock, "vmem_panic_lock", MUTEX_DEFAULT, + NULL); + mutex_init(&vmem_xnu_alloc_lock, "vmem_xnu_alloc_lock", MUTEX_DEFAULT, + NULL); + + while (--nseg >= 0) + vmem_putseg_global(&vmem_seg0[nseg]); + + /* + * On OSX we ultimately have to use the OS allocator + * as the ource and sink of memory as it is allocated + * and freed. + * + * The spl_root_arena_parent is needed in order to provide a + * base arena with an always-NULL afunc and ffunc in order to + * end the searches done by vmem_[x]alloc and vm_xfree; it + * serves no other purpose; its stats will always be zero. + * + */ + + // id 0 + spl_default_arena_parent = vmem_create("spl_default_arena_parent", + NULL, 0, heap_quantum, NULL, NULL, NULL, 0, VM_SLEEP); + + // illumos/openzfs has a gigantic pile of memory that it can use + // for its first arena; + // o3x is not so lucky, so we start with this + // Intel can go with 4096 alignment, but arm64 needs 16384. So + // we just use the larger. + static char initial_default_block[16ULL*1024ULL*1024ULL] + __attribute__((aligned(16384))) = { 0 }; + + // The default arena is very low-bandwidth; it supplies the initial + // large allocation for the heap arena below, and it serves as the + // parent of the vmem_metadata arena. It will typically do only 2 + // or 3 parent_alloc calls (to spl_vmem_default_alloc) in total. + + spl_default_arena = vmem_create("spl_default_arena", // id 1 + initial_default_block, 16ULL*1024ULL*1024ULL, + heap_quantum, spl_vmem_default_alloc, spl_vmem_default_free, + spl_default_arena_parent, 131072, + VM_SLEEP | VMC_POPULATOR | VMC_NO_QCACHE); + + VERIFY(spl_default_arena != NULL); + + // The bucket arenas satisfy allocations & frees from the bucket heap + // that are dispatched to the bucket whose power-of-two label is the + // smallest allocation that vmem_bucket_allocate will ask for. + // + // The bucket arenas in turn exchange memory with XNU's allocator/freer + // in large spans (~ 1 MiB is stable on all systems but creates bucket + // fragmentation) + // + // Segregating by size constrains internal fragmentation within the + // bucket and provides kstat.vmem visiblity and span-size policy to + // be applied to particular buckets (notably the sources of most + // allocations, see the comments below) + // + // For VMEM_BUCKET_HIBIT == 12, + // vmem_bucket_arena[n] holds allocations from 2^[n+11]+1 to 2^[n+12], + // so for [n] = 0, 2049-4096, for [n]=5 65537-131072, + // for [n]=7 (256k+1)-512k + // + // so "kstat.vmvm.vmem.bucket_1048576" should be read as the bucket + // arena containing allocations 1 MiB and smaller, but larger + // than 512 kiB. + + // create arenas for the VMEM_BUCKETS, id 2 - id 14 + + extern uint64_t real_total_memory; + VERIFY3U(real_total_memory, >=, 1024ULL*1024ULL*1024ULL); + + // adjust minimum bucket span size for memory size + // see comments in the switch below + // large span: 1 MiB and bigger on large-memory (> 32 GiB) systems + // small span: 256 kiB and bigger on large-memory systems + const uint64_t k = 1024ULL; + const uint64_t qm = 256ULL * k; + const uint64_t m = 1024ULL* k; + const uint64_t big = MAX(real_total_memory / (k * 32ULL), m); + const uint64_t small = MAX(real_total_memory / (k * 128ULL), qm); + spl_bucket_tunable_large_span = MIN(big, 16ULL * m); + spl_bucket_tunable_small_span = small; + dprintf("SPL: %s: real_total_memory %llu, large spans %llu, small " + "spans %llu\n", __func__, real_total_memory, + spl_bucket_tunable_large_span, spl_bucket_tunable_small_span); + char *buf = vmem_alloc(spl_default_arena, VMEM_NAMELEN + 21, VM_SLEEP); + for (int32_t i = VMEM_BUCKET_LOWBIT; i <= VMEM_BUCKET_HIBIT; i++) { + const uint64_t bucket_largest_size = (1ULL << (uint64_t)i); + (void) snprintf(buf, VMEM_NAMELEN + 20, "%s_%llu", + "bucket", bucket_largest_size); + dprintf("SPL: %s creating arena %s (i == %d)\n", __func__, buf, + i); + const int bucket_number = i - VMEM_BUCKET_LOWBIT; + /* + * To reduce the number of IOMalloc/IOFree transactions with + * the kernel, we create vmem bucket arenas with a PAGESIZE or + * bigger quantum, and a minimum import that is several pages + * for small bucket sizes, and twice the bucket size. + * These will serve power-of-two sized blocks to the + * bucket_heap arena. + */ + vmem_t *b = vmem_create(buf, NULL, 0, + MAX(heap_quantum, bucket_largest_size), + xnu_alloc_throttled, xnu_free_throttled, + spl_default_arena_parent, + MAX(heap_quantum * 8, bucket_largest_size * 2), + VM_SLEEP | VMC_POPULATOR | VMC_NO_QCACHE | VMC_TIMEFREE); + VERIFY(b != NULL); + b->vm_source = b; + vmem_bucket_arena[bucket_number] = b; + vmem_bucket_id_to_bucket_number[b->vm_id] = bucket_number; + } + + vmem_free(spl_default_arena, buf, VMEM_NAMELEN + 21); + // spl_heap_arena, the bucket heap, is the primary interface + // to the vmem system + + // all arenas not rooted to vmem_metadata will be rooted to + // spl_heap arena. + + spl_heap_arena = vmem_create("bucket_heap", // id 15 + NULL, 0, heap_quantum, + vmem_bucket_alloc, vmem_bucket_free, spl_default_arena_parent, 0, + VM_SLEEP | VMC_TIMEFREE | VMC_OLDFIRST); + + VERIFY(spl_heap_arena != NULL); + + // add a fixed-sized allocation to spl_heap_arena; this reduces the + // need to talk to the bucket arenas by a substantial margin + // (kstat.vmem.vmem.bucket_heap.{alloc+free} is much greater than + // kstat.vmem.vmem.bucket_heap.parent_{alloc+free}, and improves with + // increasing initial fixed allocation size. + + const size_t mib = 1024ULL * 1024ULL; + const size_t gib = 1024ULL * mib; + size_t resv_size = 128ULL * mib; + extern uint64_t real_total_memory; + + if (real_total_memory >= 4ULL * gib) + resv_size = 256ULL * mib; + if (real_total_memory >= 8ULL * gib) + resv_size = 512ULL * mib; + if (real_total_memory >= 16ULL * gib) + resv_size = gib; + + dprintf("SPL: %s adding fixed allocation of %llu to the bucket_heap\n", + __func__, (uint64_t)resv_size); + + spl_heap_arena_initial_alloc = vmem_add(spl_heap_arena, + vmem_xalloc(spl_default_arena, resv_size, resv_size, + 0, 0, NULL, NULL, VM_SLEEP), + resv_size, VM_SLEEP); + + VERIFY(spl_heap_arena_initial_alloc != NULL); + + spl_heap_arena_initial_alloc_size = resv_size; + + // kstat.vmem.vmem.heap : kmem_cache_alloc() and similar calls + // to handle in-memory datastructures other than abd + + heap = vmem_create(heap_name, // id 16 + NULL, 0, heap_quantum, + vmem_alloc, vmem_free, spl_heap_arena, 0, + VM_SLEEP); + + VERIFY(heap != NULL); + + // Root all the low bandwidth metadata arenas to the default arena. + // The vmem_metadata allocations will all be 32 kiB or larger, + // and the total allocation will generally cap off around 24 MiB. + + vmem_metadata_arena = vmem_create("vmem_metadata", // id 17 + NULL, 0, heap_quantum, vmem_alloc, vmem_free, spl_default_arena, + 8 * PAGESIZE, VM_SLEEP | VMC_POPULATOR | VMC_NO_QCACHE); + + VERIFY(vmem_metadata_arena != NULL); + + vmem_seg_arena = vmem_create("vmem_seg", // id 18 + NULL, 0, heap_quantum, + vmem_alloc, vmem_free, vmem_metadata_arena, 0, + VM_SLEEP | VMC_POPULATOR); + + VERIFY(vmem_seg_arena != NULL); + + vmem_hash_arena = vmem_create("vmem_hash", // id 19 + NULL, 0, 8, + vmem_alloc, vmem_free, vmem_metadata_arena, 0, + VM_SLEEP); + + VERIFY(vmem_hash_arena != NULL); + + vmem_vmem_arena = vmem_create("vmem_vmem", // id 20 + vmem0, sizeof (vmem0), 1, + vmem_alloc, vmem_free, vmem_metadata_arena, 0, + VM_SLEEP); + + VERIFY(vmem_vmem_arena != NULL); + + // 21 (0-based) vmem_create before this line. - macroized + // NUMBER_OF_ARENAS_IN_VMEM_INIT + for (id = 0; id < vmem_id; id++) { + (void) vmem_xalloc(vmem_vmem_arena, sizeof (vmem_t), + 1, 0, 0, &vmem0[id], &vmem0[id + 1], + VM_NOSLEEP | VM_BESTFIT | VM_PANIC); + } + + dprintf("SPL: starting vmem_update() thread\n"); + vmem_update(NULL); + + return (heap); +} + +struct free_slab { + vmem_t *vmp; + size_t slabsize; + void *slab; + list_node_t next; +}; +static list_t freelist; + +static void vmem_fini_freelist(void *vmp, void *start, size_t size) +{ + struct free_slab *fs; + + MALLOC(fs, struct free_slab *, sizeof (struct free_slab), M_TEMP, + M_WAITOK); + fs->vmp = vmp; + fs->slabsize = size; + fs->slab = start; + list_link_init(&fs->next); + list_insert_tail(&freelist, fs); +} + +void +vmem_free_span_list(void) +{ + int total = 0; + int total_count = 0; + struct free_slab *fs; +// int release = 1; + + while ((fs = list_head(&freelist))) { + total_count++; + total += fs->slabsize; + list_remove(&freelist, fs); + /* + * Commenting out due to BSOD during uninstallation, + * will revisit later. + * + * for (int id = 0; id < VMEM_INITIAL; id++) { + * if (&vmem0[id] == fs->slab) { + * release = 0; + * break; + * } + * } + * + * if (release) + * fs->vmp->vm_source_free(fs->vmp, fs->slab, + * fs->slabsize); + * release = 1; + * + */ + FREE(fs, M_TEMP); + } +} + +static void +vmem_fini_void(void *vmp, void *start, size_t size) +{ +} + +void +vmem_fini(vmem_t *heap) +{ + struct free_slab *fs; + uint64_t total; + + bsd_untimeout(vmem_update, NULL); + + dprintf("SPL: %s: stopped vmem_update. Creating list and walking " + "arenas.\n", __func__); + + /* Create a list of slabs to free by walking the list of allocs */ + list_create(&freelist, sizeof (struct free_slab), + offsetof(struct free_slab, next)); + + /* Walk to list of allocations */ + + /* + * walking with VMEM_REENTRANT causes segment consolidation and + * freeing of spans the freelist contains a list of segments that + * are still allocated at the time of the walk; unfortunately the + * lists cannot be exact without complex multiple passes, locking, + * and a more complex vmem_fini_freelist(). + * + * Walking without VMEM_REENTRANT can produce a nearly-exact list + * of unfreed spans, which Illumos would then free directly after + * the list is complete. + * + * Unfortunately in O3X, that lack of exactness can lead to a panic + * caused by attempting to free to xnu memory that we already freed + * to xnu. Fortunately, we can get a sense of what would have been + * destroyed after the (non-reentrant) walking, and we printf that + * at the end of this function. + */ + + // Walk all still-alive arenas from leaves to the root + + vmem_walk(heap, VMEM_ALLOC | VMEM_REENTRANT, vmem_fini_void, heap); + + vmem_walk(heap, VMEM_ALLOC, vmem_fini_freelist, heap); + + vmem_free_span_list(); + dprintf("\nSPL: %s destroying heap\n", __func__); + vmem_destroy(heap); // PARENT: spl_heap_arena + + dprintf("SPL: %s: walking spl_heap_arena, aka bucket_heap (pass 1)\n", + __func__); + + vmem_walk(spl_heap_arena, VMEM_ALLOC | VMEM_REENTRANT, vmem_fini_void, + spl_heap_arena); + + dprintf("SPL: %s: calling vmem_xfree(spl_default_arena, ptr, %llu);\n", + __func__, (uint64_t)spl_heap_arena_initial_alloc_size); + + // forcibly remove the initial alloc from spl_heap_arena arena, whether + // or not it is empty. below this point, any activity on + // spl_default_arena other than a non-reentrant(!) walk and a destroy + // is unsafe (UAF or MAF). + // However, all the children of spl_heap_arena should now be destroyed. + + vmem_xfree(spl_default_arena, spl_heap_arena_initial_alloc, + spl_heap_arena_initial_alloc_size); + + printf("SPL: %s: walking spl_heap_arena, aka bucket_heap (pass 2)\n", + __func__); + + vmem_walk(spl_heap_arena, VMEM_ALLOC, vmem_fini_freelist, + spl_heap_arena); + vmem_free_span_list(); + + printf("SPL: %s: walking bucket arenas...\n", __func__); + + for (int i = VMEM_BUCKET_LOWBIT; i <= VMEM_BUCKET_HIBIT; i++) { + const int bucket = i - VMEM_BUCKET_LOWBIT; + vmem_walk(vmem_bucket_arena[bucket], + VMEM_ALLOC | VMEM_REENTRANT, vmem_fini_void, + vmem_bucket_arena[bucket]); + + vmem_walk(vmem_bucket_arena[bucket], VMEM_ALLOC, + vmem_fini_freelist, vmem_bucket_arena[bucket]); + } + vmem_free_span_list(); + + dprintf("SPL: %s destroying spl_bucket_arenas...", __func__); + for (int32_t i = VMEM_BUCKET_LOWBIT; i <= VMEM_BUCKET_HIBIT; i++) { + vmem_t *vmpt = vmem_bucket_arena[i - VMEM_BUCKET_LOWBIT]; + dprintf(" %llu", (1ULL << i)); + vmem_destroy(vmpt); // parent: spl_default_arena_parent + } + dprintf("\n"); + + printf("SPL: %s: walking vmem metadata-related arenas...\n", __func__); + + vmem_walk(vmem_vmem_arena, VMEM_ALLOC | VMEM_REENTRANT, + vmem_fini_void, vmem_vmem_arena); + + vmem_walk(vmem_vmem_arena, VMEM_ALLOC, + vmem_fini_freelist, vmem_vmem_arena); + + vmem_free_span_list(); + + // We should not do VMEM_REENTRANT on vmem_seg_arena or + // vmem_hash_arena or below to avoid causing work in + // vmem_seg_arena and vmem_hash_arena. + + vmem_walk(vmem_seg_arena, VMEM_ALLOC, + vmem_fini_freelist, vmem_seg_arena); + + vmem_free_span_list(); + + vmem_walk(vmem_hash_arena, VMEM_ALLOC, + vmem_fini_freelist, vmem_hash_arena); + vmem_free_span_list(); + + vmem_walk(vmem_metadata_arena, VMEM_ALLOC, + vmem_fini_freelist, vmem_metadata_arena); + + vmem_free_span_list(); + dprintf("SPL: %s walking the root arena (spl_default_arena)...\n", + __func__); + + vmem_walk(spl_default_arena, VMEM_ALLOC, + vmem_fini_freelist, spl_default_arena); + + vmem_free_span_list(); + + dprintf("SPL: %s destroying bucket heap\n", __func__); + // PARENT: spl_default_arena_parent (but depends on buckets) + vmem_destroy(spl_heap_arena); + + // destroying the vmem_vmem arena and any arena afterwards + // requires the use of vmem_destroy_internal(), which does + // not talk to vmem_vmem_arena like vmem_destroy() does. + // dprintf("SPL: %s destroying vmem_vmem_arena\n", __func__); + // vmem_destroy_internal(vmem_vmem_arena); + // parent: vmem_metadata_arena + + // destroying the seg arena means we must no longer + // talk to vmem_populate() + dprintf("SPL: %s destroying vmem_seg_arena\n", __func__); + vmem_destroy(vmem_seg_arena); + + // vmem_hash_arena may be freed-to in vmem_destroy_internal() + // so it should be just before the vmem_metadata_arena. + dprintf("SPL: %s destroying vmem_hash_arena\n", __func__); + vmem_destroy(vmem_hash_arena); // parent: vmem_metadata_arena + vmem_hash_arena = NULL; + + // XXX: if we panic on unload below here due to destroyed mutex, + // vmem_init() will need some reworking (e.g. have + // vmem_metadata_arena talk directly to xnu), or alternatively a + // vmem_destroy_internal_internal() function that does not touch + // vmem_hash_arena will need writing. + + dprintf("SPL: %s destroying vmem_metadata_arena\n", __func__); + vmem_destroy(vmem_metadata_arena); // parent: spl_default_arena + + dprintf("\nSPL: %s destroying spl_default_arena\n", __func__); + vmem_destroy(spl_default_arena); // parent: spl_default_arena_parent + dprintf("\nSPL: %s destroying spl_default_arena_parant\n", __func__); + vmem_destroy(spl_default_arena_parent); + + dprintf("SPL: %s destroying vmem_vmem_arena\n", __func__); + vmem_destroy_internal(vmem_vmem_arena); + + printf("SPL: arenas removed, now try destroying mutexes... "); + + printf("vmem_xnu_alloc_lock "); + mutex_destroy(&vmem_xnu_alloc_lock); + printf("vmem_panic_lock "); + mutex_destroy(&vmem_panic_lock); + printf("vmem_pushpage_lock "); + mutex_destroy(&vmem_pushpage_lock); + printf("vmem_nosleep_lock "); + mutex_destroy(&vmem_nosleep_lock); + printf("vmem_sleep_lock "); + mutex_destroy(&vmem_sleep_lock); + printf("vmem_segfree_lock "); + mutex_destroy(&vmem_segfree_lock); + printf("vmem_list_lock "); + mutex_destroy(&vmem_list_lock); + + printf("\nSPL: %s: walking list of live slabs at time of call to %s\n", + __func__, __func__); + + // annoyingly, some of these should be returned to xnu, but + // we have no idea which have already been freed to xnu, and + // freeing a second time results in a panic. + + /* Now release the list of allocs to built above */ + total = 0; + uint64_t total_count = 0; + while ((fs = list_head(&freelist))) { + total_count++; + total += fs->slabsize; + list_remove(&freelist, fs); + // extern void segkmem_free(vmem_t *, void *, size_t); + // segkmem_free(fs->vmp, fs->slab, fs->slabsize); + FREE(fs, M_TEMP); + } + printf("SPL: WOULD HAVE released %llu bytes (%llu spans) from arenas\n", + total, total_count); + list_destroy(&freelist); + printf("SPL: %s: Brief delay for readability...\n", __func__); + delay(hz); + printf("SPL: %s: done!\n", __func__); +} + +/* + * return true if inuse is much smaller than imported + */ +static inline bool +bucket_fragmented(const uint16_t bn, const uint64_t now) +{ + + // early during uptime, just let buckets grow. + + if (now < 600 * hz) + return (false); + + // if there has been no pressure in the past five minutes, + // then we will just let the bucket grow. + + const uint64_t timeout = 5ULL * 60ULL * hz; + + if (spl_free_last_pressure_wrapper() + timeout < now) + return (false); + + const vmem_t *vmp = vmem_bucket_arena[bn]; + + const int64_t imported = + (int64_t)vmp->vm_kstat.vk_mem_import.value.ui64; + const int64_t inuse = + (int64_t)vmp->vm_kstat.vk_mem_inuse.value.ui64; + const int64_t tiny = 64LL*1024LL*1024LL; + const int64_t small = tiny * 2LL; // 128 M + const int64_t medium = small * 2LL; // 256 + const int64_t large = medium * 2LL; // 512 + const int64_t huge = large * 2LL; // 1 G + const int64_t super_huge = huge * 2LL; // 2 + + const int64_t amount_free = imported - inuse; + + if (amount_free <= tiny || imported <= small) + return (false); + + const int64_t percent_free = (amount_free * 100LL) / imported; + + if (percent_free > 75LL) { + return (true); + } else if (imported <= medium) { + return (percent_free >= 50); + } else if (imported <= large) { + return (percent_free >= 33); + } else if (imported <= huge) { + return (percent_free >= 25); + } else if (imported <= super_huge) { + return (percent_free >= 15); + } else { + return (percent_free >= 10); + } +} + +/* + * Return an adjusted number of bytes free in the + * abd_cache_arena (if it exists), for arc_no_grow + * policy: if there's lots of space, don't allow + * arc growth for a while to see if the gap + * between imported and inuse drops. + */ +int64_t +abd_arena_empty_space(void) +{ + extern vmem_t *abd_arena; + + if (abd_arena == NULL) + return (0); + + const int64_t imported = + (int64_t)abd_arena->vm_kstat.vk_mem_import.value.ui64; + const int64_t inuse = + (int64_t)abd_arena->vm_kstat.vk_mem_inuse.value.ui64; + + /* Hide 10% or 1GiB fragmentation from arc_no_grow */ + int64_t headroom = + (imported * 90LL / 100LL) - inuse; + + if (headroom < 1024LL*1024LL*1024LL) + headroom = 0; + + return (headroom); +} + +int64_t +abd_arena_total_size(void) +{ + extern vmem_t *abd_arena; + + if (abd_arena != NULL) + return (abd_arena->vm_kstat.vk_mem_total.value.ui64); + return (0LL); +} + + +/* + * return true if the bucket for size is fragmented + */ +static inline bool +spl_arc_no_grow_impl(const uint16_t b, const size_t size, + const boolean_t buf_is_metadata, kmem_cache_t **kc) +{ + static _Atomic uint8_t frag_suppression_counter[VMEM_BUCKETS] = { 0 }; + + const uint64_t now = zfs_lbolt(); + + const bool fragmented = bucket_fragmented(b, now); + + if (fragmented) { + if (size < 32768) { + // Don't suppress small qcached blocks when the + // qcache size (bucket_262144) is fragmented, + // since they will push everything else towards + // the tails of ARC lists without eating up a large + // amount of space themselves. + return (false); + } + const uint32_t b_bit = (uint32_t)1 << (uint32_t)b; + spl_arc_no_grow_bits |= b_bit; + const uint32_t sup_at_least_every = MIN(b_bit, 255); + const uint32_t sup_at_most_every = MAX(b_bit, 16); + const uint32_t sup_every = MIN(sup_at_least_every, + sup_at_most_every); + if (frag_suppression_counter[b] >= sup_every) { + frag_suppression_counter[b] = 0; + return (true); + } else { + frag_suppression_counter[b]++; + return (false); + } + } else { + const uint32_t b_bit = (uint32_t)1 << (uint32_t)b; + spl_arc_no_grow_bits &= ~b_bit; + } + + return (false); +} + +static inline uint16_t +vmem_bucket_number_arc_no_grow(const size_t size) +{ + // qcaching on arc + if (size < 128*1024) + return (vmem_bucket_number(262144)); + else + return (vmem_bucket_number(size)); +} + +boolean_t +spl_arc_no_grow(size_t size, boolean_t buf_is_metadata, kmem_cache_t **zp) +{ + const uint16_t b = vmem_bucket_number_arc_no_grow(size); + + const bool rv = spl_arc_no_grow_impl(b, size, buf_is_metadata, zp); + + if (rv) { + atomic_inc_64(&spl_arc_no_grow_count); + } + + return ((boolean_t)rv); +} diff --git a/module/os/macos/spl/spl-vnode.c b/module/os/macos/spl/spl-vnode.c new file mode 100644 index 000000000000..8a0c41bb4c66 --- /dev/null +++ b/module/os/macos/spl/spl-vnode.c @@ -0,0 +1,442 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2008 MacZFS + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +int +VOP_SPACE(struct vnode *vp, int cmd, struct flock *fl, int flags, offset_t off, + cred_t *cr, void *ctx) +{ + int error = 0; +#ifdef F_PUNCHHOLE + if (cmd == F_FREESP) { + fpunchhole_t fpht; + fpht.fp_flags = 0; + fpht.fp_offset = fl->l_start; + fpht.fp_length = fl->l_len; + if (vnode_getwithref(vp) == 0) { + error = VNOP_IOCTL(vp, F_PUNCHHOLE, (caddr_t)&fpht, 0, + ctx); + (void) vnode_put(vp); + } + } +#endif + return (error); +} + +int +VOP_FSYNC(struct vnode *vp, int flags, void* unused, void *uused2) +{ + vfs_context_t vctx; + int error; + + vctx = vfs_context_create((vfs_context_t)0); + error = VNOP_FSYNC(vp, (flags == FSYNC), vctx); + (void) vfs_context_rele(vctx); + return (error); +} + +int +VOP_GETATTR(struct vnode *vp, vattr_t *vap, int flags, void *x3, void *x4) +{ + vfs_context_t vctx; + int error; + + vctx = vfs_context_create((vfs_context_t)0); + error = vnode_getattr(vp, vap, vctx); + (void) vfs_context_rele(vctx); + return (error); +} + +extern errno_t vnode_lookup(const char *path, int flags, struct vnode **vpp, + vfs_context_t ctx); + +extern errno_t vnode_lookupat(const char *path, int flags, struct vnode **vpp, + vfs_context_t ctx, struct vnode *start_dvp); + +errno_t +VOP_LOOKUP(struct vnode *dvp, struct vnode **vpp, + struct componentname *cn, vfs_context_t ct) +{ + char path[MAXPATHLEN]; + char *lookup_name = cn->cn_nameptr; + + /* + * Lookup a name, to get vnode. + * If dvp is NULL, and it uses full path, just call vnode_lookup(). + * If dvp is supplied, we need to build path (vnode_lookupat() is + * private.exports) + * However, VOP_LOOKUP() is only used by OSX calls, finder and rename. + * We could re-write that code to use /absolute/path. + */ + if (dvp != NULL) { + int result, len; + + len = MAXPATHLEN; + result = vn_getpath(dvp, path, &len); + if (result != 0) + return (result); + + strlcat(path, "/", MAXPATHLEN); + strlcat(path, cn->cn_nameptr, MAXPATHLEN); + + lookup_name = path; + } + + return (vnode_lookup(lookup_name, 0, vpp, ct)); +} + +void +vfs_mountedfrom(struct mount *vfsp, char *osname) +{ + (void) copystr(osname, vfs_statfs(vfsp)->f_mntfromname, MNAMELEN - 1, + 0); +} + +static kmutex_t spl_getf_lock; +static list_t spl_getf_list; + +int +spl_vnode_init(void) +{ + mutex_init(&spl_getf_lock, NULL, MUTEX_DEFAULT, NULL); + list_create(&spl_getf_list, sizeof (struct spl_fileproc), + offsetof(struct spl_fileproc, f_next)); + return (0); +} + +void +spl_vnode_fini(void) +{ + mutex_destroy(&spl_getf_lock); + list_destroy(&spl_getf_list); +} + +#include +struct fileproc; + +extern int fp_drop(struct proc *p, int fd, struct fileproc *fp, int locked); +extern int fp_drop_written(struct proc *p, int fd, struct fileproc *fp, + int locked); +extern int fp_lookup(struct proc *p, int fd, struct fileproc **resultfp, + int locked); +extern int fo_read(struct fileproc *fp, struct uio *uio, int flags, + vfs_context_t ctx); +extern int fo_write(struct fileproc *fp, struct uio *uio, int flags, + vfs_context_t ctx); +extern int file_vnode_withvid(int, struct vnode **, uint32_t *); +extern int file_drop(int); + +/* + * getf(int fd) - hold a lock on a file descriptor, to be released by calling + * releasef(). On OSX we will also look up the vnode of the fd for calls + * to spl_vn_rdwr(). + */ +void * +getf(int fd) +{ + struct fileproc *fp = NULL; + struct spl_fileproc *sfp = NULL; + struct vnode *vp = NULL; + uint32_t vid; + + /* + * We keep the "fp" pointer as well, both for unlocking in releasef() + * and used in vn_rdwr(). + */ + + sfp = kmem_alloc(sizeof (*sfp), KM_SLEEP); + if (!sfp) + return (NULL); + + /* We no longer use fp */ + + dprintf("current_proc %p: fd %d fp %p vp %p\n", current_proc(), + fd, fp, vp); + + sfp->f_vnode = vp; + sfp->f_fd = fd; + sfp->f_offset = 0; + sfp->f_proc = current_proc(); + sfp->f_fp = fp; + + /* Also grab vnode, so we can fish out the minor, for onexit */ + if (!file_vnode_withvid(fd, &vp, &vid)) { + sfp->f_vnode = vp; + + if (vnode_getwithref(vp) != 0) { + file_drop(fd); + return (NULL); + } + + enum vtype type; + type = vnode_vtype(vp); + if (type == VCHR || type == VBLK) { + sfp->f_file = minor(vnode_specrdev(vp)); + } + file_drop(fd); + } + + mutex_enter(&spl_getf_lock); + list_insert_tail(&spl_getf_list, sfp); + mutex_exit(&spl_getf_lock); + + return (sfp); +} + +struct vnode * +getf_vnode(void *fp) +{ + struct spl_fileproc *sfp = (struct spl_fileproc *)fp; + struct vnode *vp = NULL; + uint32_t vid; + + if (!file_vnode_withvid(sfp->f_fd, &vp, &vid)) { + file_drop(sfp->f_fd); + } + + return (vp); +} + +void +releasef(int fd) +{ + struct spl_fileproc *fp = NULL; + struct proc *p; + + p = current_proc(); + mutex_enter(&spl_getf_lock); + for (fp = list_head(&spl_getf_list); fp != NULL; + fp = list_next(&spl_getf_list, fp)) { + if ((fp->f_proc == p) && fp->f_fd == fd) break; + } + mutex_exit(&spl_getf_lock); + if (!fp) + return; // Not found + + if (fp->f_vnode != NULL) + vnode_put(fp->f_vnode); + + /* Remove node from the list */ + mutex_enter(&spl_getf_lock); + list_remove(&spl_getf_list, fp); + mutex_exit(&spl_getf_lock); + + /* Free the node */ + kmem_free(fp, sizeof (*fp)); +} + +/* + * getf()/releasef() IO handler. + */ +#undef vn_rdwr +extern int vn_rdwr(enum uio_rw rw, struct vnode *vp, caddr_t base, int len, + off_t offset, enum uio_seg segflg, int ioflg, kauth_cred_t cred, + int *aresid, struct proc *p); + +int spl_vn_rdwr(enum uio_rw rw, struct spl_fileproc *sfp, + caddr_t base, ssize_t len, offset_t offset, enum uio_seg seg, + int ioflag, rlim64_t ulimit, cred_t *cr, ssize_t *residp) +{ + int error = 0; + int aresid; + + VERIFY3P(sfp->f_vnode, !=, NULL); + + error = vn_rdwr(rw, sfp->f_vnode, base, len, offset, seg, ioflag, + cr, &aresid, sfp->f_proc); + + if (residp) { + *residp = aresid; + } + + return (error); +} + +/* Regular vnode vn_rdwr */ +int zfs_vn_rdwr(enum uio_rw rw, struct vnode *vp, caddr_t base, ssize_t len, + offset_t offset, enum uio_seg seg, int ioflag, rlim64_t ulimit, + cred_t *cr, ssize_t *residp) +{ + uio_t *auio; + int spacetype; + int error = 0; + vfs_context_t vctx; + + spacetype = UIO_SEG_IS_USER_SPACE(seg) ? UIO_USERSPACE32 : UIO_SYSSPACE; + + vctx = vfs_context_create((vfs_context_t)0); + auio = uio_create(1, 0, spacetype, rw); + uio_reset(auio, offset, spacetype, rw); + uio_addiov(auio, (uint64_t)(uintptr_t)base, len); + + if (rw == UIO_READ) { + error = VNOP_READ(vp, auio, ioflag, vctx); + } else { + error = VNOP_WRITE(vp, auio, ioflag, vctx); + } + + if (residp) { + *residp = uio_resid(auio); + } else { + if (uio_resid(auio) && error == 0) + error = EIO; + } + + uio_free(auio); + vfs_context_rele(vctx); + + return (error); +} + +void +spl_rele_async(void *arg) +{ + struct vnode *vp = (struct vnode *)arg; + if (vp) vnode_put(vp); +} + +void +vn_rele_async(struct vnode *vp, void *taskq) +{ + VERIFY(taskq_dispatch((taskq_t *)taskq, + (task_func_t *)spl_rele_async, vp, TQ_SLEEP) != 0); +} + +vfs_context_t +spl_vfs_context_kernel(void) +{ + return (NULL); +} + +#undef build_path +extern int build_path(struct vnode *vp, char *buff, int buflen, int *outlen, + int flags, vfs_context_t ctx); + +int spl_build_path(struct vnode *vp, char *buff, int buflen, int *outlen, + int flags, vfs_context_t ctx) +{ + // Private.exports + // return (build_path(vp, buff, buflen, outlen, flags, ctx)); + printf("%s: missing implementation. All will fail.\n", __func__); + + buff[0] = 0; + *outlen = 0; + return (0); +} + +/* + * vnode_notify was moved from KERNEL_PRIVATE to KERNEL in 10.11, but to be + * backward compatible, we keep the wrapper for now. + */ +extern int vnode_notify(struct vnode *, uint32_t, struct vnode_attr *); +int +spl_vnode_notify(struct vnode *vp, uint32_t type, struct vnode_attr *vap) +{ +#if defined(MAC_OS_X_VERSION_10_11) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11) + return (vnode_notify(vp, type, vap)); +#else + return (0); +#endif +} + +extern int vfs_get_notify_attributes(struct vnode_attr *vap); +int +spl_vfs_get_notify_attributes(struct vnode_attr *vap) +{ +#if defined(MAC_OS_X_VERSION_10_11) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11) + return (vfs_get_notify_attributes(vap)); +#else + return (0); +#endif +} + +/* Root directory vnode for the system a.k.a. '/' */ +/* + * Must use vfs_rootvnode() to acquire a reference, and + * vnode_put() to release it + */ + +struct vnode * +getrootdir(void) +{ + struct vnode *rvnode; + + rvnode = vfs_rootvnode(); + if (rvnode) + vnode_put(rvnode); + return (rvnode); +} + + +static inline int +spl_cache_purgevfs_impl(struct vnode *vp, void *arg) +{ + cache_purge(vp); + cache_purge_negatives(vp); + return (VNODE_RETURNED); +} + +/* + * Apple won't let us call cache_purgevfs() so let's try to get + * as close as possible + */ +void +spl_cache_purgevfs(mount_t mp) +{ + (void) vnode_iterate(mp, VNODE_RELOAD, spl_cache_purgevfs_impl, NULL); +} + +/* Gross hacks - find solutions */ + +/* + * Sorry, but this is gross. But unable to find a way around it yet.. + * Maybe one day Apple will allow it. + */ +int +vnode_iocount(struct vnode *vp) +{ + int32_t *binvp; + + binvp = (int32_t *)vp; + + return (binvp[25]); +} diff --git a/module/os/macos/spl/spl-xdr.c b/module/os/macos/spl/spl-xdr.c new file mode 100644 index 000000000000..4eb115017d07 --- /dev/null +++ b/module/os/macos/spl/spl-xdr.c @@ -0,0 +1,524 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * + * Copyright (C) 2008 MacZFS + * Copyright (C) 2013 Jorgen Lundman + * + */ + +#include +#include +#include +#include +#include +#include + + +/* + * SPL's XDR mem implementation. + * + * This is used by libnvpair to serialize/deserialize the name-value pair data + * structures into byte arrays in a well-defined and portable manner. + * + * These data structures are used by the DMU/ZFS to flexibly manipulate various + * information in memory and later serialize it/deserialize it to disk. + * Examples of usages include the pool configuration, lists of pool and dataset + * properties, etc. + * + * Reference documentation for the XDR representation and XDR operations can be + * found in RFC 1832 and xdr(3), respectively. + * + * === Implementation shortcomings === + * + * It is assumed that the following C types have the following sizes: + * + * char/unsigned char: 1 byte + * short/unsigned short: 2 bytes + * int/unsigned int: 4 bytes + * longlong_t/u_longlong_t: 8 bytes + * + * The C standard allows these types to be larger (and in the case of ints, + * shorter), so if that is the case on some compiler/architecture, the build + * will fail (on purpose). + * + * If someone wants to fix the code to work properly on such environments, then: + * + * 1) Preconditions should be added to xdrmem_enc functions to make sure the + * caller doesn't pass arguments which exceed the expected range. + * 2) Functions which take signed integers should be changed to properly do + * sign extension. + * 3) For ints with less than 32 bits, well.. I suspect you'll have bigger + * problems than this implementation. + * + * It is also assumed that: + * + * 1) Chars have 8 bits. + * 2) We can always do 32-bit-aligned int memory accesses and byte-aligned + * memcpy, memset and memcmp. + * 3) Arrays passed to xdr_array() are packed and the compiler/architecture + * supports element-sized-aligned memory accesses. + * 4) Negative integers are natively stored in two's complement binary + * representation. + * + * No checks are done for the 4 assumptions above, though. + * + * === Caller expectations === + * + * Existing documentation does not describe the semantics of XDR operations very + * well. Therefore, some assumptions about failure semantics will be made and + * will be described below: + * + * 1) If any encoding operation fails (e.g., due to lack of buffer space), the + * the stream should be considered valid only up to the encoding operation + * previous to the one that first failed. However, the stream size as returned + * by xdr_control() cannot be considered to be strictly correct (it may be + * bigger). + * + * Putting it another way, if there is an encoding failure it's undefined + * whether anything is added to the stream in that operation and therefore + * neither xdr_control() nor future encoding operations on the same stream can + * be relied upon to produce correct results. + * + * 2) If a decoding operation fails, it's undefined whether anything will be + * decoded into passed buffers/pointers during that operation, or what the + * values on those buffers will look like. + * + * Future decoding operations on the same stream will also have similar + * undefined behavior. + * + * 3) When the first decoding operation fails it is OK to trust the results of + * previous decoding operations on the same stream, as long as the caller + * expects a failure to be possible (e.g. due to end-of-stream). + * + * However, this is highly discouraged because the caller should know the + * stream size and should be coded to expect any decoding failure to be data + * corruption due to hardware, accidental or even malicious causes, which should + * be handled gracefully in all cases. + * + * In very rare situations where there are strong reasons to believe the data + * can be trusted to be valid and non-tampered with, then the caller may assume + * a decoding failure to be a bug (e.g. due to mismatched data types) and may + * fail non-gracefully. + * + * 4) Non-zero padding bytes will cause the decoding operation to fail. + * + * 5) Zero bytes on string types will also cause the decoding operation to fail. + * + * 6) It is assumed that either the pointer to the stream buffer given by the + * caller is 32-bit aligned or the architecture supports non-32-bit-aligned int + * memory accesses. + * + * 7) The stream buffer and encoding/decoding buffers/ptrs should not overlap. + * + * 8) If a caller passes pointers to non-kernel memory (e.g., pointers to user + * space or MMIO space), the computer may explode. + */ + +static struct xdr_ops xdrmem_encode_ops; +static struct xdr_ops xdrmem_decode_ops; + +void +xdrmem_create(XDR *xdrs, const caddr_t addr, const uint_t size, + const enum xdr_op op) +{ + switch (op) { + case XDR_ENCODE: + xdrs->x_ops = &xdrmem_encode_ops; + break; + case XDR_DECODE: + xdrs->x_ops = &xdrmem_decode_ops; + break; + default: + printf("SPL: Invalid op value: %d\n", op); + xdrs->x_ops = NULL; /* Let the caller know we failed */ + return; + } + + xdrs->x_op = op; + xdrs->x_addr = addr; + xdrs->x_addr_end = addr + size; + + if (xdrs->x_addr_end < xdrs->x_addr) { + printf("SPL: Overflow while creating xdrmem: %p, %u\n", addr, + size); + xdrs->x_ops = NULL; + } +} +EXPORT_SYMBOL(xdrmem_create); + +static bool_t +xdrmem_control(XDR *xdrs, int req, void *info) +{ + struct xdr_bytesrec *rec = (struct xdr_bytesrec *)info; + + if (req != XDR_GET_BYTES_AVAIL) { + printf("SPL: Called with unknown request: %d\n", req); + return (FALSE); + } + + rec->xc_is_last_record = TRUE; /* always TRUE in xdrmem streams */ + rec->xc_num_avail = xdrs->x_addr_end - xdrs->x_addr; + + return (TRUE); +} + +static bool_t +xdrmem_enc_bytes(XDR *xdrs, caddr_t cp, const uint_t cnt) +{ + uint_t size = roundup(cnt, 4); + uint_t pad; + + if (size < cnt) + return (FALSE); /* Integer overflow */ + + if (xdrs->x_addr > xdrs->x_addr_end) + return (FALSE); + + if (xdrs->x_addr_end - xdrs->x_addr < size) + return (FALSE); + + memcpy(xdrs->x_addr, cp, cnt); + + xdrs->x_addr += cnt; + + pad = size - cnt; + if (pad > 0) { + memset(xdrs->x_addr, 0, pad); + xdrs->x_addr += pad; + } + + return (TRUE); +} + +static bool_t +xdrmem_dec_bytes(XDR *xdrs, caddr_t cp, const uint_t cnt) +{ + static uint32_t zero = 0; + uint_t size = roundup(cnt, 4); + uint_t pad; + + if (size < cnt) + return (FALSE); /* Integer overflow */ + + if (xdrs->x_addr > xdrs->x_addr_end) + return (FALSE); + + if (xdrs->x_addr_end - xdrs->x_addr < size) + return (FALSE); + + memcpy(cp, xdrs->x_addr, cnt); + xdrs->x_addr += cnt; + + pad = size - cnt; + if (pad > 0) { + /* An inverted memchr() would be useful here... */ + if (memcmp(&zero, xdrs->x_addr, pad) != 0) + return (FALSE); + + xdrs->x_addr += pad; + } + + return (TRUE); +} + +static bool_t +xdrmem_enc_uint32(XDR *xdrs, uint32_t val) +{ + if (xdrs->x_addr + sizeof (uint32_t) > xdrs->x_addr_end) + return (FALSE); + + *((uint32_t *)xdrs->x_addr) = BE_32(val); + + xdrs->x_addr += sizeof (uint32_t); + + return (TRUE); +} + +static bool_t +xdrmem_dec_uint32(XDR *xdrs, uint32_t *val) +{ + if (xdrs->x_addr + sizeof (uint32_t) > xdrs->x_addr_end) + return (FALSE); + + *val = BE_32(*((uint32_t *)xdrs->x_addr)); + + xdrs->x_addr += sizeof (uint32_t); + + return (TRUE); +} + +static bool_t +xdrmem_enc_char(XDR *xdrs, char *cp) +{ + uint32_t val; + + // BUILD_BUG_ON(sizeof(char) != 1); + val = *((unsigned char *) cp); + + return (xdrmem_enc_uint32(xdrs, val)); +} + +static bool_t +xdrmem_dec_char(XDR *xdrs, char *cp) +{ + uint32_t val; + + // BUILD_BUG_ON(sizeof(char) != 1); + + if (!xdrmem_dec_uint32(xdrs, &val)) + return (FALSE); + + /* + * If any of the 3 other bytes are non-zero then val will be greater + * than 0xff and we fail because according to the RFC, this block does + * not have a char encoded in it. + */ + if (val > 0xff) + return (FALSE); + + *((unsigned char *) cp) = val; + + return (TRUE); +} + +static bool_t +xdrmem_enc_ushort(XDR *xdrs, unsigned short *usp) +{ + // BUILD_BUG_ON(sizeof(unsigned short) != 2); + + return (xdrmem_enc_uint32(xdrs, *usp)); +} + +static bool_t +xdrmem_dec_ushort(XDR *xdrs, unsigned short *usp) +{ + uint32_t val; + + // BUILD_BUG_ON(sizeof(unsigned short) != 2); + + if (!xdrmem_dec_uint32(xdrs, &val)) + return (FALSE); + + /* + * Short ints are not in the RFC, but we assume similar logic as in + * xdrmem_dec_char(). + */ + if (val > 0xffff) + return (FALSE); + + *usp = val; + + return (TRUE); +} + +static bool_t +xdrmem_enc_uint(XDR *xdrs, unsigned *up) +{ + // BUILD_BUG_ON(sizeof(unsigned) != 4); + + return (xdrmem_enc_uint32(xdrs, *up)); +} + +static bool_t +xdrmem_dec_uint(XDR *xdrs, unsigned *up) +{ + // BUILD_BUG_ON(sizeof(unsigned) != 4); + + return (xdrmem_dec_uint32(xdrs, (uint32_t *)up)); +} + +static bool_t +xdrmem_enc_ulonglong(XDR *xdrs, u_longlong_t *ullp) +{ + // BUILD_BUG_ON(sizeof(u_longlong_t) != 8); + + if (!xdrmem_enc_uint32(xdrs, *ullp >> 32)) + return (FALSE); + + return (xdrmem_enc_uint32(xdrs, *ullp & 0xffffffff)); +} + +static bool_t +xdrmem_dec_ulonglong(XDR *xdrs, u_longlong_t *ullp) +{ + uint32_t low, high; + + // BUILD_BUG_ON(sizeof(u_longlong_t) != 8); + + if (!xdrmem_dec_uint32(xdrs, &high)) + return (FALSE); + if (!xdrmem_dec_uint32(xdrs, &low)) + return (FALSE); + + *ullp = ((u_longlong_t)high << 32) | low; + + return (TRUE); +} + +static bool_t +xdr_enc_array(XDR *xdrs, caddr_t *arrp, uint_t *sizep, const uint_t maxsize, + const uint_t elsize, const xdrproc_t elproc) +{ + uint_t i; + caddr_t addr = *arrp; + + if (*sizep > maxsize || *sizep > UINT_MAX / elsize) + return (FALSE); + + if (!xdrmem_enc_uint(xdrs, sizep)) + return (FALSE); + + for (i = 0; i < *sizep; i++) { + if (!elproc(xdrs, addr)) + return (FALSE); + addr += elsize; + } + + return (TRUE); +} + +static bool_t +xdr_dec_array(XDR *xdrs, caddr_t *arrp, uint_t *sizep, const uint_t maxsize, + const uint_t elsize, const xdrproc_t elproc) +{ + uint_t i, size; + bool_t alloc = FALSE; + caddr_t addr; + + if (!xdrmem_dec_uint(xdrs, sizep)) + return (FALSE); + + size = *sizep; + + if (size > maxsize || size > UINT_MAX / elsize) + return (FALSE); + + /* + * The Solaris man page says: "If *arrp is NULL when decoding, + * xdr_array() allocates memory and *arrp points to it". + */ + if (*arrp == NULL) { + // BUILD_BUG_ON(sizeof(uint_t) > sizeof(size_t)); + + *arrp = kmem_alloc(size * elsize, KM_NOSLEEP); + if (*arrp == NULL) + return (FALSE); + + alloc = TRUE; + } + + addr = *arrp; + + for (i = 0; i < size; i++) { + if (!elproc(xdrs, addr)) { + if (alloc) + kmem_free(*arrp, size * elsize); + return (FALSE); + } + addr += elsize; + } + + return (TRUE); +} + +static bool_t +xdr_enc_string(XDR *xdrs, char **sp, const uint_t maxsize) +{ + size_t slen = strlen(*sp); + uint_t len; + + if (slen > maxsize) + return (FALSE); + + len = slen; + + if (!xdrmem_enc_uint(xdrs, &len)) + return (FALSE); + + return (xdrmem_enc_bytes(xdrs, *sp, len)); +} + +static bool_t +xdr_dec_string(XDR *xdrs, char **sp, const uint_t maxsize) +{ + uint_t size; + bool_t alloc = FALSE; + + if (!xdrmem_dec_uint(xdrs, &size)) + return (FALSE); + + if (size > maxsize || size > UINT_MAX - 1) + return (FALSE); + + /* + * Solaris man page: "If *sp is NULL when decoding, xdr_string() + * allocates memory and *sp points to it". + */ + if (*sp == NULL) { + // BUILD_BUG_ON(sizeof(uint_t) > sizeof(size_t)); + + *sp = kmem_alloc(size + 1, KM_NOSLEEP); + if (*sp == NULL) + return (FALSE); + + alloc = TRUE; + } + + if (!xdrmem_dec_bytes(xdrs, *sp, size)) + goto fail; + + if (kmemchr(*sp, 0, size) != NULL) + goto fail; + + (*sp)[size] = '\0'; + + return (TRUE); + +fail: + if (alloc) + kmem_free(*sp, size + 1); + + return (FALSE); +} + +static struct xdr_ops xdrmem_encode_ops = { + .xdr_control = xdrmem_control, + .xdr_char = xdrmem_enc_char, + .xdr_u_short = xdrmem_enc_ushort, + .xdr_u_int = xdrmem_enc_uint, + .xdr_u_longlong_t = xdrmem_enc_ulonglong, + .xdr_opaque = xdrmem_enc_bytes, + .xdr_string = xdr_enc_string, + .xdr_array = xdr_enc_array +}; + +static struct xdr_ops xdrmem_decode_ops = { + .xdr_control = xdrmem_control, + .xdr_char = xdrmem_dec_char, + .xdr_u_short = xdrmem_dec_ushort, + .xdr_u_int = xdrmem_dec_uint, + .xdr_u_longlong_t = xdrmem_dec_ulonglong, + .xdr_opaque = xdrmem_dec_bytes, + .xdr_string = xdr_dec_string, + .xdr_array = xdr_dec_array +}; diff --git a/module/os/macos/spl/spl-zlib.c b/module/os/macos/spl/spl-zlib.c new file mode 100644 index 000000000000..5aa92c324a47 --- /dev/null +++ b/module/os/macos/spl/spl-zlib.c @@ -0,0 +1,199 @@ +/* + * + * zlib.h -- interface of the 'zlib' general purpose compression library + * version 1.2.5, April 19th, 2010 + * + * Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Jean-loup Gailly + * Mark Adler + */ + +#include +#include +#include +#include + +#ifdef DEBUG_SUBSYSTEM +#undef DEBUG_SUBSYSTEM +#endif + +#define DEBUG_SUBSYSTEM SS_ZLIB + +static spl_kmem_cache_t *zlib_workspace_cache; + +/* + * A kmem_cache is used for the zlib workspaces to avoid having to vmalloc + * and vfree for every call. Using a kmem_cache also has the advantage + * that improves the odds that the memory used will be local to this cpu. + * To further improve things it might be wise to create a dedicated per-cpu + * workspace for use. This would take some additional care because we then + * must disable preemption around the critical section, and verify that + * zlib_deflate* and zlib_inflate* never internally call schedule(). + */ +static void * +zlib_workspace_alloc(int flags) +{ + return (kmem_cache_alloc(zlib_workspace_cache, flags & ~(__GFP_FS))); +} + +static void +zlib_workspace_free(void *workspace) +{ + kmem_cache_free(zlib_workspace_cache, workspace); +} + +/* + * Compresses the source buffer into the destination buffer. The level + * parameter has the same meaning as in deflateInit. sourceLen is the byte + * length of the source buffer. Upon entry, destLen is the total size of the + * destination buffer, which must be at least 0.1% larger than sourceLen plus + * 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + * + * compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + * memory, Z_BUF_ERROR if there was not enough room in the output buffer, + * Z_STREAM_ERROR if the level parameter is invalid. + */ +int +z_compress_level(void *dest, size_t *destLen, const void *source, + size_t sourceLen, int level) +{ + z_stream stream; + int err; + + stream.next_in = (Byte *)source; + stream.avail_in = (uInt)sourceLen; + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + + if ((size_t)stream.avail_out != *destLen) + return (Z_BUF_ERROR); + + stream.workspace = zlib_workspace_alloc(KM_SLEEP); + if (!stream.workspace) + return (Z_MEM_ERROR); + + err = zlib_deflateInit(&stream, level); + if (err != Z_OK) { + zlib_workspace_free(stream.workspace); + return (err); + } + + err = zlib_deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + zlib_deflateEnd(&stream); + zlib_workspace_free(stream.workspace); + return (err == Z_OK ? Z_BUF_ERROR : err); + } + *destLen = stream.total_out; + + err = zlib_deflateEnd(&stream); + zlib_workspace_free(stream.workspace); + + return (err); +} +EXPORT_SYMBOL(z_compress_level); + +/* + * Decompresses the source buffer into the destination buffer. sourceLen is + * the byte length of the source buffer. Upon entry, destLen is the total + * size of the destination buffer, which must be large enough to hold the + * entire uncompressed data. (The size of the uncompressed data must have + * been saved previously by the compressor and transmitted to the decompressor + * by some mechanism outside the scope of this compression library.) + * Upon exit, destLen is the actual size of the compressed buffer. + * This function can be used to decompress a whole file at once if the + * input file is mmap'ed. + * + * uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + * enough memory, Z_BUF_ERROR if there was not enough room in the output + * buffer, or Z_DATA_ERROR if the input data was corrupted. + */ +int +z_uncompress(void *dest, size_t *destLen, const void *source, + size_t sourceLen) +{ + z_stream stream; + int err; + + stream.next_in = (Byte *)source; + stream.avail_in = (uInt)sourceLen; + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + + if ((size_t)stream.avail_out != *destLen) + return (Z_BUF_ERROR); + + stream.workspace = zlib_workspace_alloc(KM_SLEEP); + if (!stream.workspace) + return (Z_MEM_ERROR); + + err = zlib_inflateInit(&stream); + if (err != Z_OK) { + zlib_workspace_free(stream.workspace); + return (err); + } + + err = zlib_inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + zlib_inflateEnd(&stream); + zlib_workspace_free(stream.workspace); + + if (err == Z_NEED_DICT || + (err == Z_BUF_ERROR && stream.avail_in == 0)) + return (Z_DATA_ERROR); + + return (err); + } + *destLen = stream.total_out; + + err = zlib_inflateEnd(&stream); + zlib_workspace_free(stream.workspace); + + return (err); +} +EXPORT_SYMBOL(z_uncompress); + +int +spl_zlib_init(void) +{ + int size; + SENTRY; + + size = MAX(spl_zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL), + zlib_inflate_workspacesize()); + + zlib_workspace_cache = kmem_cache_create( + "spl_zlib_workspace_cache", + size, 0, NULL, NULL, NULL, NULL, NULL, + KMC_VMEM | KMC_NOEMERGENCY); + if (!zlib_workspace_cache) + SRETURN(1); + + SRETURN(0); +} + +void +spl_zlib_fini(void) +{ + SENTRY; + kmem_cache_destroy(zlib_workspace_cache); + zlib_workspace_cache = NULL; + SEXIT; +} diff --git a/module/os/macos/zfs/.gitignore b/module/os/macos/zfs/.gitignore new file mode 100644 index 000000000000..aaec2f8ea214 --- /dev/null +++ b/module/os/macos/zfs/.gitignore @@ -0,0 +1,2 @@ +zfs +zfs.kext diff --git a/module/os/macos/zfs/Info.plist b/module/os/macos/zfs/Info.plist new file mode 100644 index 000000000000..2483aaf3b939 --- /dev/null +++ b/module/os/macos/zfs/Info.plist @@ -0,0 +1,113 @@ + + + + + BuildMachineOSBuild + 14C1514 + CFBundleDevelopmentRegion + English + CFBundleExecutable + zfs + CFBundleIdentifier + org.openzfsonosx.zfs + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + zfs + CFBundlePackageType + KEXT + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0.0 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 6C131e + DTPlatformVersion + GM + DTSDKBuild + 12F37 + DTSDKName + macosx10.8 + DTXcode + 0620 + DTXcodeBuild + 6C131e + IOKitPersonalities + + org.openzfsonosx.zfs + + CFBundleIdentifier + org.openzfsonosx.zfs + IOClass + org_openzfsonosx_zfs_zvol + IOMatchCategory + org_openzfsonosx_zfs_zvol + IOMediaIcon + + CFBundleIdentifier + org.openzfsonosx.zfs + IOBundleResourceFile + VolumeIcon.icns + + IOProviderClass + IOResources + IOResourceMatch + IOBSD + + org.openzfsonosx.zfs.ZFSDatasetProxy + + CFBundleIdentifier + org.openzfsonosx.zfs + IOClass + ZFSDatasetProxy + IOProbeScore + 1000 + IOMatchCategory + ZFSPool + IOProviderClass + ZFSPool + + org.openzfsonosx.zfs.ZFSDatasetScheme + + CFBundleIdentifier + org.openzfsonosx.zfs + IOClass + ZFSDatasetScheme + IOProbeScore + 5000 + IOMatchCategory + IOStorage + IOPropertyMatch + + Whole + + + IOProviderClass + IOMedia + + + NSHumanReadableCopyright + CDDL (ZFS), BSD (FreeBSD), Copyright © 2012-2021 OpenZFS on OS X. All rights reserved. + OSBundleCompatibleVersion + 1.0.0 + OSBundleLibraries + + com.apple.iokit.IOStorageFamily + 1.6 + com.apple.kpi.bsd + 8.0.0 + com.apple.kpi.iokit + 8.0.0 + com.apple.kpi.libkern + 10.0 + com.apple.kpi.mach + 8.0.0 + com.apple.kpi.unsupported + 8.0.0 + + + diff --git a/module/os/macos/zfs/InfoPlist.strings b/module/os/macos/zfs/InfoPlist.strings new file mode 100644 index 000000000000..0c67376ebacb --- /dev/null +++ b/module/os/macos/zfs/InfoPlist.strings @@ -0,0 +1,5 @@ + + + + + diff --git a/module/os/macos/zfs/Makefile.am b/module/os/macos/zfs/Makefile.am new file mode 100644 index 000000000000..683fc769e029 --- /dev/null +++ b/module/os/macos/zfs/Makefile.am @@ -0,0 +1,364 @@ + +INFO_PLIST = Info.plist +PLIST_STRING = InfoPlist.strings + +ZFS_META_VERSION = @ZFS_META_VERSION@ +ZFS_DEBUG_STR = @ZFS_DEBUG_STR@ + +zfs_CPPFLAGS = \ + -Wall \ + -nostdinc \ + -mkernel \ + -fno-builtin-printf \ + -D__KERNEL__ \ + -D_KERNEL \ + -DKERNEL \ + -DKERNEL_PRIVATE \ + -DDRIVER_PRIVATE \ + -DNAMEDSTREAMS=1 \ + -D__DARWIN_64_BIT_INO_T=1 \ + -DAPPLE \ + -DNeXT \ + -include $(top_builddir)/zfs_config.h \ + -I$(top_srcdir)/include/os/macos/spl \ + -I$(top_srcdir)/include/os/macos/zfs \ + -I$(top_srcdir)/module/icp/include \ + -I$(top_srcdir)/include \ + -I@KERNEL_HEADERS@/Headers \ + -I@KERNEL_HEADERS@/PrivateHeaders \ + -I$(top_srcdir)/module/zstd/include + + +zfs_CPPFLAGS += @KERNEL_DEBUG_CPPFLAGS@ + +zfs_CFLAGS = +zfs_CXXFLAGS = + +zfs_LDFLAGS = \ + -Xlinker \ + -kext \ + -nostdlib \ + -lkmodc++ \ + -lkmod \ + -lcc_kext + +if TARGET_CPU_AARCH64 +zfs_CPPFLAGS+=-arch arm64e +zfs_LDFLAGS+=-arch arm64e +endif + +zfs_LDADD = \ + $(top_builddir)/module/os/macos/spl/libspl.la + +zfs_LIBS = + +# If we don't set this to nothing, it adds "-lz -liconv" +LIBS = + +bin_PROGRAMS = zfs.kext +noinst_PROGRAMS = zfs + +zfs_kext_SOURCE = + +if TARGET_CPU_X86_64 +zfs_ASM_SOURCES_C = \ + ../../../icp/asm-x86_64/aes/aeskey.c \ + ../../../icp/algs/modes/gcm_pclmulqdq.c \ + ../../../zcommon/zfs_fletcher_intel.c \ + ../../../zcommon/zfs_fletcher_sse.c \ + ../../../zcommon/zfs_fletcher_avx512.c \ + ../../../zfs/vdev_raidz_math_sse2.c \ + ../../../zfs/vdev_raidz_math_ssse3.c \ + ../../../zfs/vdev_raidz_math_avx2.c \ + ../../../zfs/vdev_raidz_math_avx512f.c \ + ../../../zfs/vdev_raidz_math_avx512bw.c +zfs_ASM_SOURCES_AS = \ + ../../../icp/asm-x86_64/os/macos/aes/aes_amd64.S \ + ../../../icp/asm-x86_64/os/macos/aes/aes_aesni.S \ + ../../../icp/asm-x86_64/os/macos/modes/aesni-gcm-x86_64.S \ + ../../../icp/asm-x86_64/os/macos/modes/ghash-x86_64.S \ + ../../../icp/asm-x86_64/os/macos/modes/gcm_pclmulqdq.S \ + ../../../icp/asm-x86_64/os/macos/sha1/sha1-x86_64.S \ + ../../../icp/asm-x86_64/os/macos/sha2/sha256_impl.S \ + ../../../icp/asm-x86_64/os/macos/sha2/sha512_impl.S + +else +zfs_ASM_SOURCES_C = +zfs_ASM_SOURCES_AS = +endif + +zfs_SOURCES = \ + ../../../zfs/abd.c \ + abd_os.c \ + ../../../zfs/aggsum.c \ + ../../../zfs/arc.c \ + arc_os.c \ + ../../../avl/avl.c \ + ../../../zfs/blkptr.c \ + ../../../zfs/bplist.c \ + ../../../zfs/bpobj.c \ + ../../../zfs/bptree.c \ + ../../../zfs/bqueue.c \ + ../../../zfs/btree.c \ + ../../../zcommon/cityhash.c \ + ../../../zfs/dataset_kstats.c \ + ../../../zfs/dbuf.c \ + ../../../zfs/dbuf_stats.c \ + ../../../zfs/ddt.c \ + ../../../zfs/ddt_zap.c \ + ../../../zfs/dmu.c \ + ../../../zfs/dmu_diff.c \ + ../../../zfs/dmu_object.c \ + ../../../zfs/dmu_objset.c \ + ../../../zfs/dmu_recv.c \ + ../../../zfs/dmu_redact.c \ + ../../../zfs/dmu_send.c \ + ../../../zfs/dmu_traverse.c \ + ../../../zfs/dmu_tx.c \ + ../../../zfs/dmu_zfetch.c \ + ../../../zfs/dnode.c \ + ../../../zfs/dnode_sync.c \ + ../../../zfs/dsl_bookmark.c \ + ../../../zfs/dsl_crypt.c \ + ../../../zfs/dsl_dataset.c \ + ../../../zfs/dsl_deadlist.c \ + ../../../zfs/dsl_deleg.c \ + ../../../zfs/dsl_destroy.c \ + ../../../zfs/dsl_dir.c \ + ../../../zfs/dsl_pool.c \ + ../../../zfs/dsl_prop.c \ + ../../../zfs/dsl_scan.c \ + ../../../zfs/dsl_synctask.c \ + ../../../zfs/dsl_userhold.c \ + ../../../zfs/edonr_zfs.c \ + ../../../zfs/fm.c \ + ../../../zfs/gzip.c \ + ../../../zfs/hkdf.c \ + ldi_osx.c \ + ldi_vnode.c \ + ldi_iokit.cpp \ + ../../../zfs/lz4.c \ + ../../../zfs/lzjb.c \ + ../../../zfs/metaslab.c \ + ../../../zfs/mmp.c \ + ../../../zfs/multilist.c \ + ../../../zfs/objlist.c \ + ../../../zfs/pathname.c \ + ../../../zfs/range_tree.c \ + ../../../zfs/refcount.c \ + ../../../zfs/rrwlock.c \ + ../../../zfs/sa.c \ + ../../../zfs/sha256.c \ + ../../../zfs/skein_zfs.c \ + ../../../zfs/spa.c \ + ../../../zfs/spa_boot.c \ + ../../../zfs/spa_checkpoint.c \ + ../../../zfs/spa_config.c \ + ../../../zfs/spa_errlog.c \ + ../../../zfs/spa_history.c \ + ../../../zfs/spa_log_spacemap.c \ + ../../../zfs/spa_misc.c \ + spa_misc_os.c \ + ../../../zfs/spa_stats.c \ + ../../../zfs/space_map.c \ + ../../../zfs/space_reftree.c \ + ../../../zfs/txg.c \ + ../../../zfs/uberblock.c \ + ../../../zfs/unique.c \ + ../../../zfs/vdev.c \ + ../../../zfs/vdev_cache.c \ + vdev_disk.c \ + ../../../zfs/vdev_draid.c \ + ../../../zfs/vdev_draid_rand.c \ + vdev_file.c \ + ../../../zfs/vdev_indirect.c \ + ../../../zfs/vdev_indirect_births.c \ + ../../../zfs/vdev_indirect_mapping.c \ + ../../../zfs/vdev_initialize.c \ + ../../../zfs/vdev_label.c \ + ../../../zfs/vdev_mirror.c \ + ../../../zfs/vdev_missing.c \ + ../../../zfs/vdev_queue.c \ + ../../../zfs/vdev_raidz.c \ + ../../../zfs/vdev_raidz_math.c \ + ../../../zfs/vdev_raidz_math_scalar.c \ + ../../../zfs/vdev_rebuild.c \ + ../../../zfs/vdev_removal.c \ + ../../../zfs/vdev_root.c \ + ../../../zfs/vdev_trim.c \ + ../../../zfs/zap.c \ + ../../../zfs/zap_leaf.c \ + ../../../zfs/zap_micro.c \ + ../../../zfs/zcp.c \ + ../../../zfs/zcp_get.c \ + ../../../zfs/zcp_global.c \ + ../../../zfs/zcp_iter.c \ + ../../../zfs/zcp_set.c \ + ../../../zfs/zcp_synctask.c \ + ../../../zfs/zfeature.c \ + ../../../zcommon/zfeature_common.c \ + zfs_acl.c \ + zfs_boot.cpp \ + ../../../zfs/zfs_byteswap.c \ + zfs_ctldir.c \ + zfs_debug.c \ + zfs_dir.c \ + ../../../zfs/zfs_fm.c \ + zfs_file_os.c \ + ../../../zfs/zfs_fuid.c \ + zfs_fuid_os.c \ + ../../../zfs/zfs_ioctl.c \ + zfs_ioctl_os.c \ + zfs_kstat_osx.c \ + ../../../zfs/zfs_log.c \ + ../../../zfs/zfs_onexit.c \ + zfs_osx.cpp \ + ../../../zfs/zfs_quota.c \ + zfs_racct.c \ + ../../../zfs/zfs_ratelimit.c \ + ../../../zfs/zfs_replay.c \ + ../../../zfs/zfs_rlock.c \ + ../../../zfs/zfs_sa.c \ + zfs_vfsops.c \ + ../../../zfs/zfs_vnops.c \ + zfs_vnops_os.c \ + zfs_vnops_osx.c \ + zfs_vnops_osx_lib.c \ + zfs_znode.c \ + ../../../zfs/zil.c \ + ../../../zfs/zio.c \ + ../../../zfs/zio_checksum.c \ + zio_crypt.c \ + ../../../zfs/zio_compress.c \ + ../../../zfs/zio_inject.c \ + ../../../zfs/zle.c \ + ../../../zfs/zrlock.c \ + ../../../zfs/zthr.c \ + ../../../zfs/zvol.c \ + zvol_os.c \ + zvolIO.cpp \ + ZFSDatasetProxy.cpp \ + ZFSDatasetScheme.cpp \ + ZFSDataset.cpp \ + ZFSPool.cpp \ + ../../../nvpair/fnvpair.c \ + ../../../nvpair/nvpair.c \ + ../../../nvpair/nvpair_alloc_fixed.c \ + ../../../nvpair/nvpair_alloc_spl.c \ + ../../../unicode/u8_textprep.c \ + ../../../unicode/uconv.c \ + ../../../zcommon/zfs_comutil.c \ + ../../../zcommon/zfs_deleg.c \ + ../../../zcommon/zfs_fletcher.c \ + ../../../zcommon/zfs_fletcher_superscalar.c \ + ../../../zcommon/zfs_fletcher_superscalar4.c \ + ../../../zcommon/zfs_namecheck.c \ + ../../../zcommon/zfs_prop.c \ + ../../../zcommon/zpool_prop.c \ + ../../../zcommon/zprop_common.c \ + ../../../icp/api/kcf_cipher.c \ + ../../../icp/api/kcf_digest.c \ + ../../../icp/api/kcf_mac.c \ + ../../../icp/api/kcf_miscapi.c \ + ../../../icp/api/kcf_ctxops.c \ + ../../../icp/core/kcf_callprov.c \ + ../../../icp/core/kcf_prov_tabs.c \ + ../../../icp/core/kcf_sched.c \ + ../../../icp/core/kcf_mech_tabs.c \ + ../../../icp/core/kcf_prov_lib.c \ + ../../../icp/spi/kcf_spi.c \ + ../../../icp/io/aes.c \ + ../../../icp/io/edonr_mod.c \ + ../../../icp/io/sha2_mod.c \ + ../../../icp/io/sha1_mod.c \ + ../../../icp/io/skein_mod.c \ + ../../../icp/os/modhash.c \ + ../../../icp/os/modconf.c \ + ../../../icp/algs/edonr/edonr.c \ + ../../../icp/algs/modes/cbc.c \ + ../../../icp/algs/modes/ccm.c \ + ../../../icp/algs/modes/ctr.c \ + ../../../icp/algs/modes/ecb.c \ + ../../../icp/algs/modes/gcm_generic.c \ + ../../../icp/algs/modes/gcm.c \ + ../../../icp/algs/modes/modes.c \ + ../../../icp/algs/sha2/sha2.c \ + ../../../icp/algs/skein/skein.c \ + ../../../icp/algs/skein/skein_block.c \ + ../../../icp/algs/skein/skein_iv.c \ + ../../../icp/algs/aes/aes_impl_aesni.c \ + ../../../icp/algs/aes/aes_impl_generic.c \ + ../../../icp/algs/aes/aes_impl_x86-64.c \ + ../../../icp/algs/aes/aes_impl.c \ + ../../../icp/algs/aes/aes_modes.c \ + ../../../icp/illumos-crypto.c \ + ../../../lua/lapi.c \ + ../../../lua/lauxlib.c \ + ../../../lua/lbaselib.c \ + ../../../lua/lcode.c \ + ../../../lua/lcompat.c \ + ../../../lua/lcorolib.c \ + ../../../lua/lctype.c \ + ../../../lua/ldebug.c \ + ../../../lua/ldo.c \ + ../../../lua/lfunc.c \ + ../../../lua/lgc.c \ + ../../../lua/llex.c \ + ../../../lua/lmem.c \ + ../../../lua/lobject.c \ + ../../../lua/lopcodes.c \ + ../../../lua/lparser.c \ + ../../../lua/lstate.c \ + ../../../lua/lstring.c \ + ../../../lua/lstrlib.c \ + ../../../lua/ltable.c \ + ../../../lua/ltablib.c \ + ../../../lua/ltm.c \ + ../../../lua/lvm.c \ + ../../../lua/lzio.c \ + ../../../lua/setjmp/setjmp.S \ + ../../../zstd/lib/zstd.c \ + ../../../zstd/zfs_zstd.c \ + $(zfs_ASM_SOURCES_C) \ + $(zfs_ASM_SOURCES_AS) + +# Ensure these files are always built with -O2 to avoid stack overflow. +../../../zfs/zfs-dsl_scan.$(OBJEXT): CFLAGS := $(CFLAGS:-O0%=-O2) +../../../lua/zfs-lvm.$(OBJEXT): CFLAGS := $(CFLAGS:-O0%=-O2) + +# Zstd uses -O3 by default, so we should follow +../../../zstd/lib/zfs-zstd.$(OBJEXT): CFLAGS := -fno-tree-vectorize -O3 + + +KERNEL_MODDIR= $(DESTDIR)@KERNEL_MODPREFIX@/zfs.kext + +dist_noinst_DATA = $(PLIST_STRING) $(INFO_PLIST) + +zfs.kext$(EXEEXT): zfs $(PLIST_STRING) $(INFO_PLIST) + @echo "" + @mkdir -p zfs.kext/Contents/Resources/English.lproj zfs.kext/Contents/MacOS + @cp -f $(INFO_PLIST) zfs.kext/Contents/ + /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $(ZFS_META_VERSION)" zfs.kext/Contents/Info.plist + /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $(ZFS_META_VERSION)" zfs.kext/Contents/Info.plist + @cp -f $(PLIST_STRING) zfs.kext/Contents/Resources/English.lproj/ + @cp -f zfs zfs.kext/Contents/MacOS/ + @cp -f $(top_srcdir)/contrib/macOS/VolumeIcon.icns zfs.kext/Contents/Resources/VolumeIcon.icns +/usr/libexec/PlistBuddy -c "Add :OSBundleRequired string Root" zfs.kext/Contents/Info.plist + @kextlibs -unsupported -undef-symbols -xml zfs.kext/ || echo "Ignoring errors..(Most of these are expected)" + +install-exec-local: zfs.kext + rm -rf $(KERNEL_MODDIR) + mkdir -p $(KERNEL_MODDIR) + rsync -r zfs.kext/ $(KERNEL_MODDIR) + chown -R root:wheel $(KERNEL_MODDIR) || echo "Unable to chown root:wheel $(KERNEL_MODDIR)" + @echo + @echo "To load module: kextload -v $(KERNEL_MODDIR)" + @echo "To uninstall module: rm -rf $(KERNEL_MODDIR)" + @echo + +uninstall-am: + rm -rf $(KERNEL_MODDIR) + +clean: + rm -rf zfs.kext/ + rm -f *.o *.lo zfs $(zfs_OBJECTS) diff --git a/module/os/macos/zfs/ZFSDataset.cpp b/module/os/macos/zfs/ZFSDataset.cpp new file mode 100644 index 000000000000..ce598e35ac29 --- /dev/null +++ b/module/os/macos/zfs/ZFSDataset.cpp @@ -0,0 +1,854 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2015, Evan Susarret. All rights reserved. + */ +/* + * ZFSDataset - proxy disk for legacy and com.apple.devicenode mounts. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DPRINTF_FUNC() do { dprintf(""); } while (0); + +OSDefineMetaClassAndStructors(ZFSDataset, IOMedia); + +#if 0 +/* XXX Only for debug tracing */ +bool +ZFSDataset::open(IOService *client, + IOOptionBits options, IOStorageAccess access) +{ + bool ret; + DPRINTF_FUNC(); + + ret = IOMedia::open(client, options, access); + + dprintf("ZFSDataset %s ret %d", ret); + return (ret); +} + +bool +ZFSDataset::isOpen(const IOService *forClient) const +{ + DPRINTF_FUNC(); + return (false); +} + +void +ZFSDataset::close(IOService *client, + IOOptionBits options) +{ + DPRINTF_FUNC(); + IOMedia::close(client, options); +} + +bool +ZFSDataset::handleOpen(IOService *client, + IOOptionBits options, void *access) +{ + bool ret; + DPRINTF_FUNC(); + + ret = IOMedia::handleOpen(client, options, access); + + dprintf("ZFSDataset %s ret %d", ret); + return (ret); +} + +bool +ZFSDataset::handleIsOpen(const IOService *client) const +{ + bool ret; + DPRINTF_FUNC(); + + ret = IOMedia::handleIsOpen(client); + + dprintf("ZFSDataset %s ret %d", ret); + return (ret); +} + +void +ZFSDataset::handleClose(IOService *client, + IOOptionBits options) +{ + DPRINTF_FUNC(); + IOMedia::handleClose(client, options); +} + +bool +ZFSDataset::attach(IOService *provider) +{ + DPRINTF_FUNC(); + return (IOMedia::attach(provider)); +} + +void +ZFSDataset::detach(IOService *provider) +{ + DPRINTF_FUNC(); + IOMedia::detach(provider); +} + +bool +ZFSDataset::start(IOService *provider) +{ + DPRINTF_FUNC(); + return (IOMedia::start(provider)); +} + +void +ZFSDataset::stop(IOService *provider) +{ + DPRINTF_FUNC(); + IOMedia::stop(provider); +} +#endif + +/* XXX Only for debug tracing */ +void +ZFSDataset::free() +{ + DPRINTF_FUNC(); + IOMedia::free(); +} + +/* + * Override init to call IOMedia init then setup properties. + */ +bool +ZFSDataset::init(UInt64 base, UInt64 size, + UInt64 preferredBlockSize, + IOMediaAttributeMask attributes, + bool isWhole, bool isWritable, + const char *contentHint, + OSDictionary *properties) +{ + OSDictionary *newProps = NULL, *deviceDict; + OSNumber *physSize, *logSize; +#if 0 + OSDictionary *protocolDict; + const OSSymbol *virtualSymbol, *internalSymbol; +#endif + bool ret; + + DPRINTF_FUNC(); + + /* Clone or create new properties dictionary */ + if (properties) newProps = OSDictionary::withDictionary(properties); + if (!newProps) newProps = OSDictionary::withCapacity(2); + + /* Allocate dictionaries, numbers, and string symbols */ + deviceDict = OSDictionary::withCapacity(2); +#if 0 + protocolDict = OSDictionary::withCapacity(2); +#endif + + physSize = OSNumber::withNumber(4096, 32); + logSize = OSNumber::withNumber(512, 32); + +#if 0 + kIOPropertyPhysicalInterconnectTypeVirtual + kIOPropertyPhysicalInterconnectTypeKey + kIOPropertyInterconnectFileKey + kIOPropertyInternalKey + kIOPropertyPhysicalInterconnectLocationKey + + kIOPropertyProtocolCharacteristicsKey + kIOPropertyMediumTypeKey + kIOPropertyLogicalBlockSizeKey + kIOPropertyPhysicalBlockSizeKey + kIOPropertyBytesPerPhysicalSectorKey + kIOPropertyDeviceCharacteristicsKey + kIOBlockStorageDeviceTypeKey + kIOBlockStorageDeviceTypeGeneric +#endif + +#if 0 + virtualSymbol = OSSymbol::withCString( + kIOPropertyPhysicalInterconnectTypeVirtual); + internalSymbol = OSSymbol::withCString( + kIOPropertyInternalKey); +#endif + + /* Validate allocations */ + if (!newProps || !deviceDict || !physSize || !logSize +#if 0 + // || !protocolDict || !virtualSymbol || !internalSymbol +#endif + ) { + dprintf("symbol allocation failed"); + OSSafeReleaseNULL(newProps); + OSSafeReleaseNULL(deviceDict); +#if 0 + OSSafeReleaseNULL(protocolDict); +#endif + OSSafeReleaseNULL(physSize); + OSSafeReleaseNULL(logSize); +#if 0 + OSSafeReleaseNULL(virtualSymbol); + OSSafeReleaseNULL(internalSymbol); +#endif + return (false); + } + + /* Setup device characteristics */ + deviceDict->setObject(kIOPropertyPhysicalBlockSizeKey, physSize); + deviceDict->setObject(kIOPropertyLogicalBlockSizeKey, logSize); + OSSafeReleaseNULL(physSize); + OSSafeReleaseNULL(logSize); + +#if 0 + /* Setup protocol characteristics */ + protocolDict->setObject(kIOPropertyPhysicalInterconnectTypeKey, + virtualSymbol); + protocolDict->setObject(kIOPropertyPhysicalInterconnectLocationKey, + internalSymbol); + OSSafeReleaseNULL(virtualSymbol); + OSSafeReleaseNULL(internalSymbol); +#endif + + /* XXX Setup required IOMedia props */ + + /* Set new device and protocol dictionaries */ + if (newProps->setObject(kIOPropertyDeviceCharacteristicsKey, + deviceDict) == false +#if 0 + // || + // newProps->setObject(kIOPropertyProtocolCharacteristicsKey, + // protocolDict) == false +#endif + ) { + dprintf("setup properties failed"); + OSSafeReleaseNULL(newProps); + OSSafeReleaseNULL(deviceDict); +#if 0 + OSSafeReleaseNULL(protocolDict); +#endif + return (false); + } + OSSafeReleaseNULL(deviceDict); +#if 0 + OSSafeReleaseNULL(protocolDict); +#endif + + /* Call IOMedia init with size and newProps */ + ret = IOMedia::init(base, size, preferredBlockSize, + attributes, isWhole, isWritable, contentHint, + newProps); + OSSafeReleaseNULL(newProps); + + if (!ret) dprintf("IOMedia init failed"); + + return (ret); + +#if 0 + /* Get current device and protocol dictionaries */ + lockForArbitration(); + oldDeviceDict = OSDynamicCast(OSDictionary, + getProperty(kIOStorageDeviceCharacteristicsKey)); + oldProtocolDict = OSDynamicCast(OSDictionary, + getProperty(kIOStorageProtocolCharacteristicsKey)); + if (oldDeviceDict) oldDeviceDict->retain(); + if (oldProtocolDict) oldProtocolDict->retain(); + unlockForArbitration(); + + /* Clone existing dictionaries */ + if (oldDeviceDict) { + newDeviceDict = OSDictionary::withDict(oldDeviceDict); + OSSafeReleaseNULL(oldDeviceDict); + } + if (oldProtocolDict) { + newProtocolDict = OSDictionary::withDict(oldProtocolDict); + OSSafeReleaseNULL(oldDeviceDict); + } + + /* Make new if missing */ + if (!newDeviceDict) + newDeviceDict = OSDictionary::withCapacity(2); + if (!newProtocolDict) + newProtocolDict = OSDictionary::withCapacity(2); + + /* Setup device characteristics */ + newDeviceDict->setObject(kIOStoragePhysicalBlocksizeKey, physSize); + newDeviceDict->setObject(kIOStorageLogicalBlocksizeKey, logSize); + OSSafeReleaseNULL(physSize); + OSSafeReleaseNULL(logSize); + + /* Setup protocol characteristics */ + newProtocolDict->setObject(kIOStorageProtocolInterconnectTypeKey, + virtualSymbol); + newProtocolDict->setObject(kIOStorageProtocolInterconnectNameKey, + internalSymbol); + OSSafeReleaseNULL(virtualSymbol); + OSSafeReleaseNULL(internalSymbol); + + /* XXX Setup required IOMedia props */ + + /* Set new device and protocol dictionaries */ + lockForArbitration(); + setProperty(kIOStorageDeviceCharacteristicsKey, newDeviceDict); + setProperty(kIOStorageProtocolCharacteristicsKey, newProtocolDict); + unlockForArbitration(); + + /* Cleanup and return success */ + OSSafeReleaseNULL(newDeviceDict); + OSSafeReleaseNULL(newProtocolDict); + return (true); +#endif +} + +/* + * Set both the IOService name and the ZFS Dataset property. + */ +bool +ZFSDataset::setDatasetName(const char *name) +{ + OSDictionary *prevDict, *newDict = NULL; + OSString *datasetString; + const char *newname; + + if (!name || name[0] == '\0') { + dprintf("missing name"); + return (false); + } + + if ((newname = strrchr(name, '/')) == NULL) { + newname = name; + } else { + /* Advance beyond slash */ + newname++; + } + +#if 0 + size_t len; + /* Length of IOMedia name plus null terminator */ + len = (strlen(kZFSIOMediaPrefix) + strlen(name) + + strlen(kZFSIOMediaSuffix) + 1); + // len = strlen("ZFS ") + strlen(name) + strlen(" Media") + 1; + + newname = (char *)kmem_alloc(len, KM_SLEEP); +#endif + datasetString = OSString::withCString(name); + +#if 0 + nameString = OSString::withCString(newname); + if (newname == NULL || nameString == NULL) { + dprintf("couldn't make name strings"); + OSSafeReleaseNULL(nameString); + if (newname) kmem_free(newname, len); + return (false); + } +#else + if (datasetString == NULL) { + dprintf("couldn't make name strings"); + return (false); + } +#endif + +#if 0 + bzero(newname, len); + snprintf(newname, len, "%s%s%s", kZFSIOMediaPrefix, + name, kZFSIOMediaSuffix); + + ASSERT3U(strlen(newname), ==, len-1); +#endif + + /* Lock IORegistryEntry and get current prop dict */ + lockForArbitration(); + if ((prevDict = OSDynamicCast(OSDictionary, + getProperty(kIOPropertyDeviceCharacteristicsKey))) == NULL) { + /* Unlock IORegistryEntry */ + unlockForArbitration(); + dprintf("couldn't get prop dict"); + } + prevDict->retain(); + unlockForArbitration(); + + /* Clone existing dictionary */ + if (prevDict) { + if ((newDict = OSDictionary::withDictionary(prevDict)) == + NULL) { + dprintf("couldn't clone prop dict"); + } + OSSafeReleaseNULL(prevDict); + /* Non-fatal at the moment */ + } + + /* If prevDict did not exist or couldn't be copied, make new */ + if (!newDict && (newDict = OSDictionary::withCapacity(1)) == NULL) { + dprintf("couldn't make new prop dict"); + } + + /* If we have a new or copied dict at this point */ + if (newDict) { + /* Add or replace dictionary Product Name string */ + if (newDict->setObject(kIOPropertyProductNameKey, + datasetString) == false) { + dprintf("couldn't set name"); + OSSafeReleaseNULL(datasetString); + // OSSafeReleaseNULL(nameString); + // kmem_free(newname, len); + OSSafeReleaseNULL(newDict); + return (false); + } + + /* Lock IORegistryEntry and replace prop dict */ + lockForArbitration(); + if (setProperty(kIOPropertyDeviceCharacteristicsKey, + newDict) == false) { + unlockForArbitration(); + dprintf("couldn't set name"); + OSSafeReleaseNULL(datasetString); + // OSSafeReleaseNULL(nameString); + // kmem_free(newname, len); + OSSafeReleaseNULL(newDict); + return (false); + } + unlockForArbitration(); + OSSafeReleaseNULL(newDict); + } + + /* Lock IORegistryEntry to replace property and set name */ + lockForArbitration(); + /* Assign plain ZFS Dataset name */ + setProperty(kZFSDatasetNameKey, datasetString); + /* Assign IOMedia name */ + // setName(name); + setName(newname); + + /* Unlock IORegistryEntry and cleanup allocations */ + unlockForArbitration(); + // kmem_free(newname, len); + // OSSafeReleaseNULL(nameString); + return (true); +} + +#if 0 +static inline uint64_t +get_objnum(const char *name) +{ + objset_t *os = NULL; + uint64_t objnum; + int error; + + if (!name) + return (0); + + error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE, FTAG, &os); + if (error != 0) { + dprintf("couldn't open dataset %d", error); + return (0); + } + + objnum = dmu_objset_id(os); + + dmu_objset_disown(os, FTAG); + + return (objnum); +} +#endif + +/* + * Create a proxy device, name it appropriately, and return it. + */ +ZFSDataset * +ZFSDataset::withDatasetNameAndSize(const char *name, uint64_t size) +{ + ZFSDataset *dataset = NULL; + objset_t *os = NULL; + OSString *uuidStr = NULL; + OSObject *property = NULL; + char uuid_cstr[37]; + uint64_t objnum, readonly, guid; +#if 0 + // uint64_t ref_size, avail_size, obj_count, obj_free; +#endif + uuid_t uuid; + int error; + bool isWritable; + + DPRINTF_FUNC(); + + if (!name || name[0] == '\0') { + dprintf("missing name"); + /* Nothing allocated or retained yet */ + return (NULL); + } + bzero(uuid_cstr, sizeof (uuid_cstr)); + +#if 0 + OSNumber *sizeNum = NULL; + property = copyProperty(kZFSPoolSizeKey, gIOServicePlane, + kIORegistryIterateRecursively|kIORegistryIterateParents); + if (!property) { + dprintf("couldn't get pool size"); + /* Nothing allocated or retained yet */ + return (NULL); + } + if ((sizeNum = OSDynamicCast(OSNumber, property)) == NULL) { + dprintf("couldn't cast pool size"); + goto error; + } + size = sizeNum->unsigned64BitValue(); + sizeNum = NULL; + OSSafeReleaseNULL(property); +#endif + + if (zfs_vfs_uuid_gen(name, uuid) != 0) { + dprintf("UUID gen failed"); + goto error; + } + // uuid_unparse(uuid, uuid_cstr); + zfs_vfs_uuid_unparse(uuid, uuid_cstr); + // snprintf(uuid_cstr, sizeof (uuid_cstr), ""); + + uuidStr = OSString::withCString(uuid_cstr); + if (!uuidStr) { + dprintf("uuidStr alloc failed"); + goto error; + } + + dataset = new ZFSDataset; + if (!dataset) { + dprintf("allocation failed"); + goto error; + } + + /* Own the dmu objset to get properties */ + error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE, B_FALSE, FTAG, &os); + if (error != 0) { + dprintf("couldn't open dataset %d", error); + goto error; + } + + /* Get the dsl_dir to lookup object number */ + objnum = dmu_objset_id(os); + +#if 0 + dmu_objset_space(os, &ref_size, &avail_size, &obj_count, &obj_free); +#endif + + // if (os->os_dsl_dataset) + // guid = dsl_dataset_phys(os->os_dsl_dataset)->ds_guid; + guid = dmu_objset_fsid_guid(os); + // dsl_prop_get_integer(name, "guid", &guid, NULL) != 0) { + + if (dsl_prop_get_integer(name, "readonly", &readonly, NULL) != 0) { + dmu_objset_disown(os, B_FALSE, FTAG); + dprintf("get readonly property failed"); + goto error; + } + // size = (1<<30); + // isWritable = true; + dmu_objset_disown(os, B_FALSE, FTAG); + +#if 0 + size = ref_size + avail_size; +#endif + + isWritable = (readonly == 0ULL); + + if (dataset->init(/* base */ 0, size, DEV_BSIZE, + /* attributes */ 0, /* isWhole */ false, isWritable, + kZFSContentHint, /* properties */ NULL) == false) { + dprintf("init failed"); + goto error; + } + + if (dataset->setDatasetName(name) == false) { + dprintf("invalid name"); + goto error; + } + + /* Set media UUID */ + dataset->setProperty(kIOMediaUUIDKey, uuidStr); + OSSafeReleaseNULL(uuidStr); + + return (dataset); + +error: + OSSafeReleaseNULL(property); + OSSafeReleaseNULL(uuidStr); + OSSafeReleaseNULL(dataset); + return (NULL); +} + +/* + * Compatibility method simulates a read but returns all zeros. + */ +void +ZFSDataset::read(IOService *client, + UInt64 byteStart, IOMemoryDescriptor *buffer, + IOStorageAttributes *attributes, + IOStorageCompletion *completion) +{ + IOByteCount total, cur_len, done = 0; + addr64_t cur; + + DPRINTF_FUNC(); + if (!buffer) { + if (completion) complete(completion, kIOReturnInvalid, 0); + return; + } + + total = buffer->getLength(); + + /* XXX Get each physical segment of the buffer and zero it */ + while (done < total) { + cur_len = 0; + cur = buffer->getPhysicalSegment(done, &cur_len); + if (cur == 0) break; + if (cur_len != 0) bzero_phys(cur, cur_len); + done += cur_len; + ASSERT3U(done, <=, total); + } + ASSERT3U(done, ==, total); + + // if (!completion || !completion->action) { + if (!completion) { + dprintf("invalid completion"); + return; + } + +// (completion->action)(completion->target, completion->parameter, +// kIOReturnSuccess, total); + complete(completion, kIOReturnSuccess, total); +} + +/* + * Compatibility method simulates a write as a no-op. + */ +void +ZFSDataset::write(IOService *client, + UInt64 byteStart, IOMemoryDescriptor *buffer, + IOStorageAttributes *attributes, + IOStorageCompletion *completion) +{ + IOByteCount total; + DPRINTF_FUNC(); + + if (!buffer) { + if (completion) complete(completion, kIOReturnInvalid); + return; + } + + total = buffer->getLength(); + + // if (!completion || !completion->action) { + if (!completion) { + dprintf("invalid completion"); + return; + } + + /* XXX No-op, just return success */ +// (completion->action)(completion->target, completion->parameter, +// kIOReturnSuccess, total); + complete(completion, kIOReturnSuccess, total); +} + +#ifdef DEBUG +volatile SInt64 num_sync = 0; +#endif + +/* + * Compatibility method simulates a barrier sync as a no-op. + */ +#if defined(MAC_OS_X_VERSION_10_11) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11) +IOReturn +ZFSDataset::synchronize(IOService *client, + UInt64 byteStart, UInt64 byteCount, + IOStorageSynchronizeOptions options) +#else +IOReturn +ZFSDataset::synchronizeCache(IOService *client) +#endif +{ +#ifdef DEBUG + SInt64 cur_sync = 0; + DPRINTF_FUNC(); + cur_sync = OSIncrementAtomic64(&num_sync); + dprintf("sync called %lld times", cur_sync); +#endif + + /* XXX Needs to report success for mount_common() */ + return (kIOReturnSuccess); +} + +/* + * Compatibility method returns failure (unsupported). + */ +IOReturn +ZFSDataset::unmap(IOService *client, + IOStorageExtent *extents, UInt32 extentsCount, +#if defined(MAC_OS_X_VERSION_10_11) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11) + IOStorageUnmapOptions options) +#else + UInt32 options) +#endif +{ + DPRINTF_FUNC(); + return (kIOReturnUnsupported); +} + +/* + * Compatibility method returns failure (no result). + */ +IOStorage * +ZFSDataset::copyPhysicalExtent(IOService *client, + UInt64 *byteStart, UInt64 *byteCount) +{ + DPRINTF_FUNC(); + return (0); + // return (IOMedia::copyPhysicalExtent(client, byteStart, byteCount)); +} + +/* + * Compatibility method simulates lock as a no-op. + */ +bool +ZFSDataset::lockPhysicalExtents(IOService *client) +{ + DPRINTF_FUNC(); + // return (IOMedia::unlockPhysicalExtents(client)); + return (true); +} + +/* + * Compatibility method simulates unlock as a no-op. + */ +void +ZFSDataset::unlockPhysicalExtents(IOService *client) +{ + DPRINTF_FUNC(); + // IOMedia::unlockPhysicalExtents(client); +} + +/* + * Compatibility method returns failure (unsupported). + */ +#if defined(MAC_OS_X_VERSION_10_10) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10) +IOReturn +ZFSDataset::setPriority(IOService *client, + IOStorageExtent *extents, UInt32 extentsCount, + IOStoragePriority priority) +{ + DPRINTF_FUNC(); + return (kIOReturnUnsupported); + // return (IOMedia::setPriority(client, extents, + // extentsCount, priority)); +} +#endif + +/* + * Compatibility method returns default system blocksize. + */ +UInt64 +ZFSDataset::getPreferredBlockSize() const +{ + DPRINTF_FUNC(); + return (DEV_BSIZE); + // return (IOMedia::getPreferredBlockSize()); +} + +/* XXX Only for debug tracing */ +UInt64 +ZFSDataset::getSize() const +{ + DPRINTF_FUNC(); + return (IOMedia::getSize()); +} + +/* XXX Only for debug tracing */ +UInt64 +ZFSDataset::getBase() const +{ + DPRINTF_FUNC(); + return (IOMedia::getBase()); +} + +/* XXX Only for debug tracing */ +bool +ZFSDataset::isEjectable() const +{ + DPRINTF_FUNC(); + return (IOMedia::isEjectable()); +} + +/* XXX Only for debug tracing */ +bool +ZFSDataset::isFormatted() const +{ + DPRINTF_FUNC(); + return (IOMedia::isFormatted()); +} + +/* XXX Only for debug tracing */ +bool +ZFSDataset::isWhole() const +{ + DPRINTF_FUNC(); + return (IOMedia::isWhole()); +} + +/* XXX Only for debug tracing */ +bool +ZFSDataset::isWritable() const +{ + DPRINTF_FUNC(); + return (IOMedia::isWritable()); +} + +/* XXX Only for debug tracing */ +const char * +ZFSDataset::getContent() const +{ + DPRINTF_FUNC(); + return (IOMedia::getContent()); +} + +/* XXX Only for debug tracing */ +const char * +ZFSDataset::getContentHint() const +{ + DPRINTF_FUNC(); + return (IOMedia::getContentHint()); +} + +/* XXX Only for debug tracing */ +IOMediaAttributeMask +ZFSDataset::getAttributes() const +{ + DPRINTF_FUNC(); + return (IOMedia::getAttributes()); +} diff --git a/module/os/macos/zfs/ZFSDatasetProxy.cpp b/module/os/macos/zfs/ZFSDatasetProxy.cpp new file mode 100644 index 000000000000..10d3dd13e98d --- /dev/null +++ b/module/os/macos/zfs/ZFSDatasetProxy.cpp @@ -0,0 +1,466 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2015, Evan Susarret. All rights reserved. + */ + +#include +#include +#include +#include +#include + +#define DPRINTF_FUNC() do { dprintf(""); } while (0); + +/* block size is 512 B, count is 512 M blocks */ +#define ZFS_PROXY_DEV_BSIZE (UInt64)(1<<9) +#define ZFS_PROXY_DEV_BCOUNT (UInt64)(2<<29) +#define kZFSProxyGUIDKey "ZFS Pool GUID" +#define kZFSProxyReadOnlyKey "ZFS Pool Read-Only" + +OSDefineMetaClassAndStructors(ZFSDatasetProxy, IOBlockStorageDevice); + +void +ZFSDatasetProxy::free() +{ + char *str; + + /* vendor, revision, and info share a null char */ + if (vendorString) { + str = (char *)vendorString; + vendorString = 0; + if (revisionString == str) revisionString = 0; + if (infoString == str) infoString = 0; + IOFree(str, strlen(str)+1); + } + + /* Product string contains pool name */ + if (productString) { + str = (char *)productString; + productString = 0; + IOFree(str, strlen(str)+1); + } + + IOBlockStorageDevice::free(); +} + +bool +ZFSDatasetProxy::init(OSDictionary *properties) +{ + char *str = (char *)IOMalloc(1); + + if (!str) { + dprintf("string allocation failed\n"); + return (false); + } + str[0] = '\0'; + vendorString = str; + revisionString = str; + infoString = str; + + if (IOBlockStorageDevice::init(properties) == false) { + dprintf("BlockStorageDevice start failed"); + goto error; + } + + return (true); + +error: + if (str) { + vendorString = 0; + revisionString = 0; + infoString = 0; + IOFree(str, 1); + } + return (false); +} + +bool +ZFSDatasetProxy::start(IOService *provider) +{ + OSObject *property = NULL, *size = NULL; + OSString *nameString = NULL; + OSNumber *sizeNum = NULL; + OSDictionary *deviceDict = NULL, *protocolDict = NULL; + const OSSymbol *virtualSymbol = NULL, *internalSymbol = NULL; + const char *cstr = NULL; + char *pstring = NULL; + int plen = 0; + bool started = false; + + size = copyProperty(kZFSPoolSizeKey, gIOServicePlane, + (kIORegistryIterateRecursively|kIORegistryIterateParents)); + property = copyProperty(kZFSPoolNameKey, gIOServicePlane, + (kIORegistryIterateRecursively|kIORegistryIterateParents)); + + if (!size || !property) { + dprintf("couldn't get pool name or size"); + goto error; + } + + nameString = OSDynamicCast(OSString, property); + if (!nameString) { + dprintf("missing pool name"); + goto error; + } +#if 0 + /* Try hard to get the name string */ + do { + nameString = OSDynamicCast(OSString, property); + + if (nameString) nameString->retain(); + + if (!nameString) { + OSSymbol *nameSymbol; + nameSymbol = OSDynamicCast(OSSymbol, property); + if (!nameSymbol) { + dprintf("couldn't get name"); + goto error; + } + nameString = OSString::withCString( + nameSymbol->getCStringNoCopy()); + } + } while (0); +#endif + + sizeNum = OSDynamicCast(OSNumber, size); + if (!sizeNum) { + dprintf("invalid size"); + goto error; + } + _pool_bcount = sizeNum->unsigned64BitValue() / DEV_BSIZE; + sizeNum = 0; + size->release(); + size = 0; + + cstr = nameString->getCStringNoCopy(); + if (!cstr || (plen = strlen(cstr) + 1) == 1) { + goto error; + } + pstring = (char *)IOMalloc(plen); + if (!pstring) { + goto error; + } + snprintf(pstring, plen, "%s", cstr); + productString = pstring; + pstring = 0; + + if (IOBlockStorageDevice::start(provider) == false) { + dprintf("BlockStorageDevice start failed"); + goto error; + } + started = true; + + deviceDict = OSDynamicCast(OSDictionary, + getProperty(kIOPropertyDeviceCharacteristicsKey)); + if (deviceDict) { + /* Clone a new dictionary */ + deviceDict = OSDictionary::withDictionary(deviceDict); + if (!deviceDict) { + dprintf("dict clone failed"); + goto error; + } + } + + if (!deviceDict) { + dprintf("creating new device dict"); + deviceDict = OSDictionary::withCapacity(1); + } + + if (!deviceDict) { + dprintf("missing device dict"); + goto error; + } + + deviceDict->setObject(kIOPropertyProductNameKey, nameString); + OSSafeReleaseNULL(nameString); + + if (setProperty(kIOPropertyDeviceCharacteristicsKey, + deviceDict) == false) { + dprintf("device dict setProperty failed"); + goto error; + } + OSSafeReleaseNULL(deviceDict); + + protocolDict = OSDynamicCast(OSDictionary, + getProperty(kIOPropertyProtocolCharacteristicsKey)); + if (protocolDict) { + /* Clone a new dictionary */ + protocolDict = OSDictionary::withDictionary(protocolDict); + if (!protocolDict) { + dprintf("dict clone failed"); + goto error; + } + } + + if (!protocolDict) { + dprintf("creating new protocol dict"); + protocolDict = OSDictionary::withCapacity(1); + } + + if (!protocolDict) { + dprintf("missing protocol dict"); + goto error; + } + + virtualSymbol = OSSymbol::withCString( + kIOPropertyPhysicalInterconnectTypeVirtual); + internalSymbol = OSSymbol::withCString( + kIOPropertyInternalKey); + if (!virtualSymbol || !internalSymbol) { + dprintf("symbol alloc failed"); + goto error; + } + + protocolDict->setObject(kIOPropertyPhysicalInterconnectTypeKey, + virtualSymbol); + protocolDict->setObject(kIOPropertyPhysicalInterconnectLocationKey, + internalSymbol); + + OSSafeReleaseNULL(virtualSymbol); + OSSafeReleaseNULL(internalSymbol); + + if (setProperty(kIOPropertyProtocolCharacteristicsKey, + protocolDict) == false) { + dprintf("protocol dict setProperty failed"); + goto error; + } + OSSafeReleaseNULL(protocolDict); + registerService(kIOServiceAsynchronous); + + return (true); + +error: + OSSafeReleaseNULL(size); + OSSafeReleaseNULL(property); + OSSafeReleaseNULL(deviceDict); + OSSafeReleaseNULL(protocolDict); + OSSafeReleaseNULL(nameString); + OSSafeReleaseNULL(virtualSymbol); + OSSafeReleaseNULL(internalSymbol); + if (pstring) IOFree(pstring, plen); + if (started) IOBlockStorageDevice::stop(provider); + return (false); +} + +/* XXX IOBlockStorageDevice */ +IOReturn +ZFSDatasetProxy::doSynchronizeCache(void) +{ + DPRINTF_FUNC(); + return (kIOReturnSuccess); +} + +IOReturn +ZFSDatasetProxy::doAsyncReadWrite(IOMemoryDescriptor *buffer, + UInt64 block, UInt64 nblks, + IOStorageAttributes *attributes, + IOStorageCompletion *completion) +{ + char zero[ZFS_PROXY_DEV_BSIZE]; + size_t len, cur, off = 0; + + DPRINTF_FUNC(); + + if (!buffer) { + IOStorage::complete(completion, kIOReturnError, 0); + return (kIOReturnSuccess); + } + + /* Read vs. write */ + if (buffer->getDirection() == kIODirectionIn) { + /* Zero the read buffer */ + bzero(zero, ZFS_PROXY_DEV_BSIZE); + len = buffer->getLength(); + while (len > 0) { + cur = (len > ZFS_PROXY_DEV_BSIZE ? + ZFS_PROXY_DEV_BSIZE : len); + buffer->writeBytes(/* offset */ off, + /* buf */ zero, /* length */ cur); + off += cur; + len -= cur; + } + // dprintf("%s: read: %llu %llu", + // __func__, block, nblks); + IOStorage::complete(completion, kIOReturnSuccess, + buffer->getLength()); + return (kIOReturnSuccess); + } + + if (buffer->getDirection() != kIODirectionOut) { + dprintf("invalid direction %d", buffer->getDirection()); + IOStorage::complete(completion, kIOReturnError, 0); + return (kIOReturnSuccess); + } + + /* + * XXX For now this just returns error for all writes. + * If it turns out that mountroot/bdevvp try to + * verify writable status by reading a block and writing + * it back to disk, lie and say it succeeded. + */ + dprintf("write: %llu %llu", block, nblks); + IOStorage::complete(completion, kIOReturnError, 0); + return (kIOReturnSuccess); +} + +IOReturn +ZFSDatasetProxy::doEjectMedia() +{ + DPRINTF_FUNC(); + /* XXX Called at shutdown, maybe return success? */ + return (kIOReturnError); +} + +IOReturn +ZFSDatasetProxy::doFormatMedia(UInt64 byteCapacity) +{ + DPRINTF_FUNC(); + /* XXX shouldn't need it */ + return (kIOReturnError); + // return (kIOReturnSuccess); +} + +UInt32 +ZFSDatasetProxy::doGetFormatCapacities(UInt64 *capacities, + UInt32 capacitiesMaxCount) const +{ + DPRINTF_FUNC(); + if (capacities && capacitiesMaxCount > 0) { + capacities[0] = (ZFS_PROXY_DEV_BSIZE * ZFS_PROXY_DEV_BCOUNT); + dprintf("capacity %llu", capacities[0]); + } + + /* Always inform caller of capacity count */ + return (1); +} + +/* Returns full pool name from instance private var */ +char * +ZFSDatasetProxy::getProductString() +{ + if (productString) dprintf("[%s]", productString); + /* Return class private string */ + return ((char *)productString); +} + +/* Returns readonly status from instance private var */ +IOReturn +ZFSDatasetProxy::reportWriteProtection(bool *isWriteProtected) +{ + DPRINTF_FUNC(); + if (isWriteProtected) *isWriteProtected = isReadOnly; + return (kIOReturnSuccess); +} + +/* These return class static string for all instances */ +char * +ZFSDatasetProxy::getVendorString() +{ + dprintf("[%s]", vendorString); + /* Return class static string */ + return ((char *)vendorString); +} +char * +ZFSDatasetProxy::getRevisionString() +{ + dprintf("[%s]", revisionString); + /* Return class static string */ + return ((char *)revisionString); +} +char * +ZFSDatasetProxy::getAdditionalDeviceInfoString() +{ + dprintf("[%s]", infoString); + /* Return class static string */ + return ((char *)infoString); +} + +/* Always return media present and unchanged */ +IOReturn +ZFSDatasetProxy::reportMediaState(bool *mediaPresent, + bool *changedState) +{ + DPRINTF_FUNC(); + if (mediaPresent) *mediaPresent = true; + if (changedState) *changedState = false; + return (kIOReturnSuccess); +} + +/* Always report nonremovable and nonejectable */ +IOReturn +ZFSDatasetProxy::reportRemovability(bool *isRemoveable) +{ + DPRINTF_FUNC(); + if (isRemoveable) *isRemoveable = false; + return (kIOReturnSuccess); +} +IOReturn +ZFSDatasetProxy::reportEjectability(bool *isEjectable) +{ + DPRINTF_FUNC(); + if (isEjectable) *isEjectable = false; + return (kIOReturnSuccess); +} + +/* Always report 512b blocksize */ +IOReturn +ZFSDatasetProxy::reportBlockSize(UInt64 *blockSize) +{ + DPRINTF_FUNC(); + if (!blockSize) + return (kIOReturnError); + + *blockSize = ZFS_PROXY_DEV_BSIZE; + return (kIOReturnSuccess); +} + +/* XXX Calculate from dev_bcount, should get size from objset */ +/* XXX Can issue message kIOMessageMediaParametersHaveChanged to update */ +IOReturn +ZFSDatasetProxy::reportMaxValidBlock(UInt64 *maxBlock) +{ + DPRINTF_FUNC(); + if (!maxBlock) + return (kIOReturnError); + + // *maxBlock = 0; + // *maxBlock = ZFS_PROXY_DEV_BCOUNT - 1; + *maxBlock = _pool_bcount - 1; + dprintf("maxBlock %llu", *maxBlock); + + return (kIOReturnSuccess); +} + +IOReturn +ZFSDatasetProxy::getWriteCacheState(bool *enabled) +{ + dprintf("getCacheState\n"); + if (enabled) *enabled = true; + return (kIOReturnSuccess); +} + +IOReturn +ZFSDatasetProxy::setWriteCacheState(bool enabled) +{ + dprintf("setWriteCache\n"); + return (kIOReturnSuccess); +} diff --git a/module/os/macos/zfs/ZFSDatasetScheme.cpp b/module/os/macos/zfs/ZFSDatasetScheme.cpp new file mode 100644 index 000000000000..1694357b9207 --- /dev/null +++ b/module/os/macos/zfs/ZFSDatasetScheme.cpp @@ -0,0 +1,1108 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2015, Evan Susarret. All rights reserved. + * Copyright (c) 2017, Jorgen Lundman. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static ZFSDatasetScheme * +zfs_osx_proxy_scheme_by_osname(const char *osname) +{ + ZFSDatasetScheme *scheme = NULL; + OSDictionary *matching; + OSObject *object; + OSString *str; + OSIterator *iter; + char *pool_name, *slash; + size_t len; + + slash = strchr(osname, '/'); + if (slash) { + len = (slash - osname) + 1; + } else { + len = strlen(osname) + 1; + } + + pool_name = (char *)kmem_alloc(len, KM_SLEEP); + if (!pool_name) { + dprintf("string alloc failed"); + return (NULL); + } + snprintf(pool_name, len, "%s", osname); + dprintf("pool_name [%s] from %s", pool_name, osname); + + matching = IOService::serviceMatching(kZFSDatasetSchemeClass); + if (!matching) { + dprintf("couldn't get match dict"); + kmem_free(pool_name, len); + return (NULL); + } + + /* Add the pool name for exact match */ + str = OSString::withCString(pool_name); + matching->setObject(kZFSPoolNameKey, str); + OSSafeReleaseNULL(str); + + object = IOService::copyMatchingService(matching); + + if (object && (scheme = OSDynamicCast(ZFSDatasetScheme, + object)) == NULL) { + object->release(); + } + object = NULL; + + if (scheme && ((str = OSDynamicCast(OSString, + scheme->getProperty(kZFSPoolNameKey))) == NULL || + str->isEqualTo(pool_name) == false)) { + scheme->release(); + scheme = NULL; + } + + if (!scheme) { + int i; + for (i = 0; i < 12; i++) { // up to 6s + iter = IOService::getMatchingServices(matching); + if (iter) break; + IOSleep(500); + } + + if (i) dprintf("%s: tried %d times\n", __func__, i); + + if (!iter) { + dprintf("couldn't get iterator"); + kmem_free(pool_name, len); + OSSafeReleaseNULL(matching); + return (NULL); + } + + while ((object = iter->getNextObject())) { + if (iter->isValid() == false) { + iter->reset(); + continue; + } + scheme = OSDynamicCast(ZFSDatasetScheme, object); + if (!scheme) continue; + + object = scheme->getProperty(kZFSPoolNameKey, + gIOServicePlane, kIORegistryIterateParents | + kIORegistryIterateRecursively); + if (!object) continue; + + str = OSDynamicCast(OSString, object); + if (!str) continue; + + if (str->isEqualTo(pool_name)) break; + + str = NULL; + object = NULL; + scheme = NULL; + } + + if (scheme) scheme->retain(); + OSSafeReleaseNULL(iter); + } + OSSafeReleaseNULL(matching); + kmem_free(pool_name, len); + pool_name = 0; + + if (scheme == NULL) { + dprintf("no matching pool proxy"); + } + return (scheme); + +#if 0 + spa_t *spa; + ZFSPool *pool = 0; + + if (!osname || osname[0] == '\0') { + dprintf("missing dataset argument"); + return (EINVAL); + } + + /* Lookup the pool spa */ + mutex_enter(&spa_namespace_lock); + spa = spa_lookup(osname); + if (spa && spa->spa_iokit_proxy) { + pool = spa->spa_iokit_proxy->proxy; + if (pool) pool->retain(); + } + mutex_exit(&spa_namespace_lock); + + /* Need a pool proxy to attach to */ + if (!pool) { + dprintf("couldn't get pool proxy"); + return (EINVAL); + } + return (0); +#endif +} + +/* + * Get the proxy device by matching a property name and value. + * + * Inputs: + * property: const char string. + * value: const char string. + * + * Return: + * Pointer to proxy on success, NULL on error or missing. + */ +static ZFSDataset * +zfs_osx_proxy_lookup(const char *property, OSObject *value) +{ + OSIterator *iter = NULL; + OSDictionary *matching = NULL; + OSObject *next = NULL, *prop = NULL; + ZFSDataset *dataset = NULL; + + /* Validate arguments */ + if (!property || !value || property[0] == '\0') { + dprintf("invalid argument"); + return (NULL); + } + + /* + * Create the matching dictionary for class. + * Add property and value to match dict. + */ + matching = IOService::serviceMatching(kZFSDatasetClassKey); + if ((matching) == NULL || + (matching->setObject(property, value) == false)) { + dprintf("match dictionary create failed"); + OSSafeReleaseNULL(matching); + return (NULL); + } + + /* Try to copy if there is only one match */ + next = IOService::copyMatchingService(matching); + if (next != NULL && ((dataset = OSDynamicCast(ZFSDataset, + next)) != NULL) && + (prop = dataset->getProperty(property)) != NULL && + (prop->isEqualTo(value))) { + dprintf("quick matched dataset"); + OSSafeReleaseNULL(matching); + /* Leave retain taken by copyMatching */ + return (dataset); + } + /* Unretained references */ + prop = NULL; + dataset = NULL; + /* If set, it was retained by copyMatchingService */ + OSSafeReleaseNULL(next); + + iter = IOService::getMatchingServices(matching); + OSSafeReleaseNULL(matching); + if (iter == NULL) { + dprintf("iterator failed"); + return (NULL); + } + + while ((next = iter->getNextObject())) { + dataset = OSDynamicCast(ZFSDataset, next); + if (!dataset) continue; + + if ((prop = dataset->getProperty(property)) == NULL) { + dataset = NULL; + continue; + } + + if (prop->isEqualTo(value)) { + /* Take a reference on the match */ + dprintf("found match"); + dataset->retain(); + prop = NULL; + break; + } + + prop = NULL; + dataset = NULL; + } + /* Release iterator */ + OSSafeReleaseNULL(iter); + + /* Leave retain */ + return (dataset); +#if 0 + /* + * Copy (first) matching service. + * Cast service to proxy class. + */ + if ((service = IOService::copyMatchingService(matching)) == NULL || + (dataset = OSDynamicCast(ZFSDataset, service)) == NULL) { + dprintf("matching failed"); + OSSafeReleaseNULL(service); + return (NULL); + } + + /* Leave retain from copyMatchingService */ + return (dataset); +#endif +} + +/* + * Get the proxy device for a given dataset name. + * + * Input: + * osname: dataset name e.g. pool/dataset + * + * Return: + * Valid ZFSDataset service, or NULL on error or missing. + */ +ZFSDataset * +zfs_osx_proxy_get(const char *osname) +{ + ZFSDataset *dataset; + OSString *osstr; + + /* Validate arguments, osname is limited to MAXNAMELEN */ + if (!osname || osname[0] == '\0' || osname[0] == '/' || + strnlen(osname, MAXNAMELEN+1) == (MAXNAMELEN+1)) { + dprintf("invalid argument"); + return (NULL); + } + + osstr = OSString::withCString(osname); + if (!osstr) { + dprintf("string alloc failed"); + return (NULL); + } + + dataset = zfs_osx_proxy_lookup(kZFSDatasetNameKey, osstr); + OSSafeReleaseNULL(osstr); + + if (!dataset) { + dprintf("lookup failed"); + return (NULL); + } + + return (dataset); +} + +/* + * Get the proxy device for a given a device name or path. + * + * Input: + * devpath: BSD name as const char* string, e.g. "/dev/diskN" or "diskN" + * must be null-terminated + * + * Return: + * Valid ZFSDataset service, or NULL on error or missing. + */ +static ZFSDataset * +zfs_osx_proxy_from_devpath(const char *devpath) +{ + /* XXX No need to init, will be assigned */ + ZFSDataset *dataset; + OSString *bsdstr; + const char *bsdname; + + /* Validate arguments, devpath is limited to MAXPATHLEN */ + if (!devpath || devpath[0] == '\0' || + strnlen(devpath, MAXPATHLEN+1) == (MAXPATHLEN+1)) { + dprintf("invalid argument"); + return (NULL); + } + + /* If we have a path, remove prefix */ + if (strncmp(devpath, "/dev/", 5) == 0) { + bsdname = devpath + 5; + } else { + bsdname = devpath; + } + + /* Make sure we have (at least) "diskN" at this point */ + if (strncmp(bsdname, "disk", 4) != 0 || bsdname[4] == '\0') { + dprintf("invalid bsdname %s from %s", bsdname, devpath); + return (NULL); + } + + bsdstr = OSString::withCString(bsdname); + if (!bsdstr) { + dprintf("string alloc failed"); + return (NULL); + } + + dataset = zfs_osx_proxy_lookup(kIOBSDNameKey, bsdstr); + OSSafeReleaseNULL(bsdstr); + + if (!dataset) { + dprintf("lookup with %s failed", bsdname); + return (NULL); + } + + return (dataset); +} + +/* + * Given a dataset, get the desired property and write its + * value to the caller-supplied buffer. + * + * Inputs: + * dataset: valid ZFSDataset object, should be retained by + * caller. + * property: const char* of the desired property name key. + * value: char* buffer which should be at least 'len' bytes. + * len: length of value buffer. + * + * Return: + * 0 on success, positive int on error. + */ +static int +zfs_osx_proxy_get_prop_string(ZFSDataset *dataset, + const char *property, char *value, int len) +{ + OSObject *obj; + OSString *valueString; + + /* Validate arguments */ + if (!dataset || !property || !value || len == 0) { + dprintf("invalid argument"); + return (EINVAL); + } + + /* Lock proxy while getting property */ + dataset->lockForArbitration(); + obj = dataset->copyProperty(property); + dataset->unlockForArbitration(); + + if (!obj) { + dprintf("no property %s", property); + return (ENXIO); + } + + valueString = OSDynamicCast(OSString, obj); + /* Validate property value */ + if (!valueString) { + dprintf("couldn't cast value for %s", property); + OSSafeReleaseNULL(obj); + return (ENXIO); + } + + /* Write up to len bytes */ + snprintf(value, len, "%s", valueString->getCStringNoCopy()); + + /* Release string and proxy */ + valueString = 0; + OSSafeReleaseNULL(obj); + + return (0); +} + +extern "C" { + +/* + * Given a ZFS dataset name, get the proxy device and write the + * BSD Name to the caller-supplied buffer. + * + * Inputs: + * osname: dataset name as char* string, e.g. "pool/dataset" + * must be null-terminated + * bsdname: char* string buffer where bsdname will be written + * len: length of bsdname buffer + * + * Return: + * 0 on success, positive int errno on failure. + */ +int +zfs_osx_proxy_get_bsdname(const char *osname, + char *bsdname, int len) +{ + /* XXX No need to init, will be assigned */ + ZFSDataset *dataset; + int ret; + + /* Validate arguments */ + if (!osname || !bsdname || len == 0) { + dprintf("invalid argument"); + return (EINVAL); + } + + /* Get dataset proxy (takes a retain) */ + dataset = zfs_osx_proxy_get(osname); + if (!dataset) { + dprintf("no proxy matching %s", osname); + return (ENOENT); + } + + /* Get BSD name property and write to bsdname buffer */ + ret = zfs_osx_proxy_get_prop_string(dataset, + kIOBSDNameKey, bsdname, len); + OSSafeReleaseNULL(dataset); + + if (ret != 0) { + dprintf("ret %d", ret); + } + + return (ret); +} + +/* + * Given a device name or path, get the proxy device and write the + * ZFS Dataset name to the caller-supplied buffer. + * + * Inputs: + * devpath: BSD name as const char* string, e.g. "/dev/diskN" or "diskN" + * must be null-terminated + * osname: char* string buffer where osname will be written + * len: length of osname buffer + * + * Return: + * 0 on success, positive int errno on failure. + */ +int +zfs_osx_proxy_get_osname(const char *devpath, char *osname, int len) +{ + /* XXX No need to init, will be assigned */ + ZFSDataset *dataset; + int ret; + + /* Validate arguments */ + if (!devpath || !osname || len == 0) { + dprintf("invalid argument"); + return (EINVAL); + } + + /* Get dataset proxy (takes a retain) */ + dataset = zfs_osx_proxy_from_devpath(devpath); + if (!dataset) { + dprintf("no proxy matching %s", devpath); + return (ENOENT); + } + + /* Get dataset name property and write to osname buffer */ + ret = zfs_osx_proxy_get_prop_string(dataset, + kZFSDatasetNameKey, osname, len); + OSSafeReleaseNULL(dataset); + + if (ret != 0) { + dprintf("ret %d", ret); + } + + return (ret); +} + +/* + * Check if a dataset has a proxy device. + * + * Input: + * osname: dataset name e.g. pool/dataset + * + * Return: + * 1 if exists, 0 on error or missing. + */ +int +zfs_osx_proxy_exists(const char *osname) +{ + ZFSDataset *dataset; + + /* Get dataset proxy (takes a retain) */ + if ((dataset = zfs_osx_proxy_get(osname)) != NULL) { + OSSafeReleaseNULL(dataset); + return (1); + } + + return (0); +} + +/* + * Remove the proxy device for a given dataset name. + * + * Input: + * osname: dataset name e.g. pool/dataset + */ +void +zfs_osx_proxy_remove(const char *osname) +{ + ZFSDataset *dataset; + ZFSDatasetScheme *provider; + + /* Get dataset proxy (takes a retain) */ + dataset = zfs_osx_proxy_get(osname); + if (dataset == NULL) { + dprintf("couldn't get dataset"); + return; + } +#if 0 + /* Terminate and release retain */ + dataset->terminate(kIOServiceSynchronous | kIOServiceRequired); + OSSafeReleaseNULL(dataset); +#endif + provider = OSDynamicCast(ZFSDatasetScheme, + dataset->getProvider()); + if (!provider) { + dprintf("invalid provider"); + return; + } + + OSSafeReleaseNULL(dataset); + dprintf("removing %s", osname); + provider->removeDataset(osname, /* force */ true); +} + +/* + * Create a proxy device for a given dataset name, unless one exists. + * + * Input: + * osname: dataset name e.g. pool/dataset + * + * Return: + * 0 on success, or positive int on error. + */ +int +zfs_osx_proxy_create(const char *osname) +{ + ZFSDatasetScheme *provider = NULL; + + if (!osname || osname[0] == '\0') { + dprintf("missing dataset argument"); + return (EINVAL); + } + + provider = zfs_osx_proxy_scheme_by_osname(osname); + if (provider == NULL) { + dprintf("can't get pool proxy"); + return (ENOENT); + } + + if (provider->addDataset(osname) == false) { + dprintf("couldn't add dataset"); + provider->release(); + return (ENXIO); + } + + provider->release(); + return (0); +} + +} /* extern "C" */ + +static SInt32 +orderHoles(const OSMetaClassBase *obj1, const OSMetaClassBase *obj2, + __unused void *context) +{ + OSNumber *num1, *num2; + + if (obj1 == NULL || + (num1 = OSDynamicCast(OSNumber, obj1)) == NULL) { + /* Push invalid OSNumbers to end of list */ + return (-1); + } + if (obj2 == NULL || + (num2 = OSDynamicCast(OSNumber, obj2)) == NULL) { + /* If both are non-OSNumber, same ordering */ + if (num1 == NULL) + return (0); + /* If num1 is a valid OSNumber, push num2 to end */ + return (1); + } + + /* + * A comparison result of the object: + *
        + *
      • a negative value if obj2 should precede obj1,
      • + *
      • a positive value if obj1 should precede obj2,
      • + *
      • and 0 if obj1 and obj2 have an equivalent ordering.
      • + *
      + */ + if (num1->isEqualTo(num2)) + return (0); + + if (num1->unsigned32BitValue() < num2->unsigned32BitValue()) { + return (1); + } else { + return (-1); + } +} + +OSDefineMetaClassAndStructors(ZFSDatasetScheme, IOPartitionScheme); + +void +ZFSDatasetScheme::free() +{ + OSSafeReleaseNULL(_datasets); + OSSafeReleaseNULL(_holes); + _max_id = 0; + + IOPartitionScheme::free(); +} + +bool +ZFSDatasetScheme::init(OSDictionary *properties) +{ + _datasets = OSSet::withCapacity(1); + _holes = OSOrderedSet::withCapacity(1, orderHoles); + _max_id = 0; + + if (!_datasets || !_holes) { + dprintf("OSSet allocation failed"); + OSSafeReleaseNULL(_datasets); + OSSafeReleaseNULL(_holes); + return (false); + } + + OSDictionary *newProps = NULL; + if (properties) newProps = OSDictionary::withDictionary(properties); + if (!newProps) newProps = OSDictionary::withCapacity(2); + OSString *str; + str = OSString::withCString("IOGUIDPartitionScheme"); + newProps->setObject("IOClass", str); + OSSafeReleaseNULL(str); + str = OSString::withCString("GUID_partition_scheme"); + newProps->setObject("Content Mask", str); + OSSafeReleaseNULL(str); + + if (IOPartitionScheme::init(newProps) == false) { + dprintf("IOPartitionScheme init failed"); + OSSafeReleaseNULL(newProps); + OSSafeReleaseNULL(_datasets); + OSSafeReleaseNULL(_holes); + return (false); + } + OSSafeReleaseNULL(newProps); + + return (true); +} + +bool +ZFSDatasetScheme::start(IOService *provider) +{ + OSObject *pool_name; + + if (IOPartitionScheme::start(provider) == false) { + dprintf("IOPartitionScheme start failed"); + return (false); + } + + pool_name = getProperty(kZFSPoolNameKey, + gIOServicePlane, kIORegistryIterateRecursively| + kIORegistryIterateParents); + if (pool_name) { + setProperty(kZFSPoolNameKey, pool_name); + } + + // registerService(kIOServiceAsynchronous); + registerService(kIOServiceSynchronous); + + return (true); +} + +IOService * +ZFSDatasetScheme::probe(IOService *provider, SInt32 *score) +{ + OSObject *property; + IOService *parent; + + /* First ask IOPartitionScheme to probe */ + if (IOPartitionScheme::probe(provider, score) == 0) { + dprintf("IOPartitionScheme probe failed"); + return (0); + } + + /* Check for ZFS Pool Name first */ + property = getProperty(kZFSPoolNameKey, gIOServicePlane, + kIORegistryIterateRecursively|kIORegistryIterateParents); + if (!property) { + dprintf("no pool name"); + return (0); + } + + /* Make sure we have a target, and valid provider below */ + if (provider == NULL || + OSDynamicCast(IOMedia, provider) == NULL || + (parent = provider->getProvider()) == NULL) { + dprintf("invalid provider"); + return (0); + } + + /* Make sure provider is driver, and has valid provider below */ + if (OSDynamicCast(IOBlockStorageDriver, parent) == NULL || + (parent = parent->getProvider()) == NULL) { + dprintf("invalid parent"); + return (0); + } + + /* Make sure the parent provider is a proxy */ + if (OSDynamicCast(ZFSDatasetProxy, parent) == NULL) { + dprintf("invalid grandparent"); + return (0); + } + + /* Successful match */ + dprintf("Match"); + // *score = 5000; + return (this); +} + +uint32_t +ZFSDatasetScheme::getNextPartitionID() +{ + uint32_t ret_id = 0ULL; + + /* Try to lock, unless service is terminated */ + if (lockForArbitration(false) == false) { + dprintf("service is terminated"); + return (0ULL); + } + + /* If the partiton list is sparse (has holes) */ + if (_holes->getCount() != 0) { + OSNumber *id_num = OSDynamicCast(OSNumber, + _holes->getFirstObject()); + + /* Just in case the list is invalid */ +#ifdef DEBUG + if (!id_num) panic("invalid hole list"); +#endif + + if (id_num) { + id_num->retain(); + _holes->removeObject(id_num); + ret_id = id_num->unsigned32BitValue(); + OSSafeReleaseNULL(id_num); + goto out; + } + } + + /* If no holes were found, just get next id */ + ret_id = (_max_id += 1); + +out: + unlockForArbitration(); + return (ret_id); +} + +void ZFSDatasetScheme::returnPartitionID(uint32_t part_id) +{ + OSNumber *id_num = OSNumber::withNumber(part_id, 32); + + if (!id_num) dprintf("alloc failed"); + /* XXX Continue and try to decrement max_id if possible */ + + if (lockForArbitration(false) == false) { + dprintf("service is terminated"); + OSSafeReleaseNULL(id_num); + return; + } + + /* Decrementing highest part id */ + if (part_id == _max_id) { + /* First, decrement max */ + _max_id--; + /* no longer needed */ + OSSafeReleaseNULL(id_num); + + /* Now iterate down the hole list */ + while ((id_num = OSDynamicCast(OSNumber, + _holes->getLastObject()))) { + /* Only need to remove consecutive matches */ + if (id_num->unsigned32BitValue() != (_max_id)) { + break; + } + + /* Remove this num from hole list */ + id_num->retain(); + _holes->removeObject(id_num); + OSSafeReleaseNULL(id_num); + /* Decrement max */ + _max_id--; + } + /* Creating a new 'hole' in the ID namespace */ + } else { + /* Better have been able to allocate OSNum */ + if (!id_num) { + unlockForArbitration(); +#ifdef DEBUG + panic("ZFSDatasetScheme %s failed to return partID", + __func__); +#endif + return; + } + + /* + * OSOrderedSet only enforces ordering when + * using setObject(anObject) interface. + * Therefore _holes must not use setFirstObject, + * setLastObject, setObject(index, anObject) + */ + + /* Add a new OSNum to hole list */ + _holes->setObject(id_num); + OSSafeReleaseNULL(id_num); + } + + unlockForArbitration(); +} + +bool +ZFSDatasetScheme::addDataset(const char *osname) +{ + ZFSDataset *dataset; + OSObject *obj; + OSNumber *sizeNum; + char location[24]; + uint64_t size; + uint32_t part_id; + + obj = copyProperty(kZFSPoolSizeKey, gIOServicePlane, + kIORegistryIterateRecursively|kIORegistryIterateParents); + if (!obj) { + dprintf("missing pool size"); + return (false); + } + sizeNum = OSDynamicCast(OSNumber, obj); + if (!sizeNum) { + dprintf("invalid pool size"); + return (false); + } + size = sizeNum->unsigned64BitValue(); + sizeNum = 0; + OSSafeReleaseNULL(obj); + + part_id = getNextPartitionID(); + /* Only using non-zero partition ids */ + if (part_id == 0) { + dprintf("invalid partition ID"); + return (false); + } + snprintf(location, sizeof (location), "%u", part_id); + +#if 0 + OSString *locationStr; + locationStr = OSString::withCString(location); + if (!locationStr) { + dprintf("location string alloc failed"); + return (false); + } + OSSafeReleaseNULL(locationStr); +#endif + + dataset = ZFSDataset::withDatasetNameAndSize(osname, size); + if (!dataset) { + dprintf("couldn't add %s", osname); + return (false); + } + + /* Set location in plane and partiton ID property */ + dataset->setLocation(location); +#ifdef kIOMediaBaseKey + dataset->setProperty(kIOMediaBaseKey, 0ULL, 64); +#endif + dataset->setProperty(kIOMediaPartitionIDKey, part_id, 32); + + // This sets the "diskutil list -> TYPE" field + dataset->setProperty("Content", "ZFS Dataset"); + // This matches with Info.plist, so it calls zfs.util for NAME + dataset->setProperty("Content Hint", + "6A898CC3-1DD2-11B2-99A6-080020736631"); + + if (dataset->attach(this) == false) { + dprintf("attach failed"); + OSSafeReleaseNULL(dataset); + return (false); + } + + if (dataset->start(this) == false) { + dprintf("start failed"); + dataset->detach(this); + OSSafeReleaseNULL(dataset); + return (false); + } + + /* Protect the OSSet by taking IOService lock */ + lockForArbitration(); + _datasets->setObject(dataset); + unlockForArbitration(); + + // dataset->registerService(kIOServiceAsynchronous); + dataset->registerService(kIOServiceSynchronous); + + /* Adding to OSSet takes a retain */ + OSSafeReleaseNULL(dataset); + + return (true); +} + +bool +ZFSDatasetScheme::removeDataset(const char *osname, bool force) +{ + OSCollectionIterator *iter; + ZFSDataset *dataset = NULL; + OSNumber *partNum; + uint32_t part_id = 0; + bool locked; + + if ((locked = lockForArbitration(false)) == false) { + dprintf("couldn't lock terminated service"); + } + + iter = OSCollectionIterator::withCollection(_datasets); + if (!iter) { + dprintf("couldn't get dataset iterator"); + return (false); + } + + while ((dataset = OSDynamicCast(ZFSDataset, + iter->getNextObject())) != NULL) { + OSObject *property; + OSString *str; + + property = dataset->getProperty(kZFSDatasetNameKey); + if (!property) continue; + + str = OSDynamicCast(OSString, property); + if (!str) continue; + + if (str->isEqualTo(osname)) { + _datasets->removeObject(dataset); + break; + } + } + + if (!dataset) { + dprintf("couldn't get dataset"); + iter->release(); + return (false); + } + + dataset->retain(); + iter->release(); + iter = 0; + + if (locked) unlockForArbitration(); + + partNum = OSDynamicCast(OSNumber, + dataset->getProperty(kIOMediaPartitionIDKey)); + if (!partNum) { + dprintf("couldn't get partition number"); + } else { + part_id = partNum->unsigned32BitValue(); + } + + if (force) { + dataset->terminate(kIOServiceSynchronous| + kIOServiceRequired); + } else { + dataset->terminate(kIOServiceSynchronous); + } + + dataset->release(); + dataset = 0; + + /* Only return non-zero partition ids */ + if (part_id != 0) { + dprintf("terminated partition %u", part_id); + returnPartitionID(part_id); + } + + return (true); +} + +/* Compatibility shims */ +void +ZFSDatasetScheme::read(IOService *client, + UInt64 byteStart, + IOMemoryDescriptor *buffer, + IOStorageAttributes *attributes, + IOStorageCompletion *completion) +{ + IOStorage::complete(completion, kIOReturnError, 0); +} + +void +ZFSDatasetScheme::write(IOService *client, + UInt64 byteStart, + IOMemoryDescriptor *buffer, + IOStorageAttributes *attributes, + IOStorageCompletion *completion) +{ + IOStorage::complete(completion, kIOReturnError, 0); +} + +#if defined(MAC_OS_X_VERSION_10_11) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11) +IOReturn +ZFSDatasetScheme::synchronize(IOService *client, + UInt64 byteStart, + UInt64 byteCount, + IOStorageSynchronizeOptions options) +#else +IOReturn +ZFSDatasetScheme::synchronizeCache(IOService *client) +#endif +{ + return (kIOReturnUnsupported); +} + +IOReturn +ZFSDatasetScheme::unmap(IOService *client, + IOStorageExtent *extents, + UInt32 extentsCount, +#if defined(MAC_OS_X_VERSION_10_11) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11) + IOStorageUnmapOptions options) +#else + UInt32 options) +#endif +{ + return (kIOReturnUnsupported); +} + +bool +ZFSDatasetScheme::lockPhysicalExtents(IOService *client) +{ + return (false); +} + +IOStorage * +ZFSDatasetScheme::copyPhysicalExtent(IOService *client, + UInt64 * byteStart, + UInt64 * byteCount) +{ + return (NULL); +} + +void +ZFSDatasetScheme::unlockPhysicalExtents(IOService *client) +{ +} + +#if defined(MAC_OS_X_VERSION_10_10) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10) +IOReturn +ZFSDatasetScheme::setPriority(IOService *client, + IOStorageExtent *extents, + UInt32 extentsCount, + IOStoragePriority priority) +{ + return (kIOReturnUnsupported); +} +#endif diff --git a/module/os/macos/zfs/ZFSPool.cpp b/module/os/macos/zfs/ZFSPool.cpp new file mode 100644 index 000000000000..859bd1ea84d6 --- /dev/null +++ b/module/os/macos/zfs/ZFSPool.cpp @@ -0,0 +1,868 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2016, Evan Susarret. All rights reserved. + */ + +#include +#include +#include +#include +#include + +extern "C" { +#include +#include +#include +} /* extern "C" */ + +#include + +#define DPRINTF_FUNC() do { dprintf("%s\n", __func__); } while (0); + +#if 0 +/* block size is 512 B, count is 512 M blocks */ +#define ZFS_POOL_DEV_BSIZE (UInt64)(1<<9) +#define ZFS_POOL_DEV_BCOUNT (UInt64)(2<<29) +#endif + +/* + * Returns handle to ZFS IOService, with a retain count. + */ +static IOService * +copy_zfs_handle() +{ + /* Get the ZFS service handle the 'hard way' */ + OSDictionary *matching; + IOService *service = 0; + + matching = IOService::serviceMatching("org_openzfsonosx_zfs_zvol"); + if (matching) { + service = IOService::copyMatchingService(matching); + OSSafeReleaseNULL(matching); + } + + if (!service) { + dprintf("couldn't get zfs IOService"); + return (NULL); + } + + return (service); +#if 0 + /* Got service, make sure it casts */ + zfs_hl = OSDynamicCast(org_openzfsonosx_zfs_zvol, service); + if (zfs_hl == NULL) { + dprintf("couldn't get zfs_hl"); + /* Drop retain from copyMatchingService */ + OSSafeReleaseNULL(service); + return (NULL); + } + + return (zfs_hl); +#endif +} + +OSDefineMetaClassAndStructors(ZFSPool, IOService); + +#if 0 +bool +ZFSPool::open(IOService *client, IOOptionBits options, void *arg) +{ + bool ret; + + IOLog("ZFSPool %s\n", __func__); + + ret = IOService::open(client, options, arg); + + IOLog("ZFSPool %s ret %d\n", __func__, ret); + + return (ret); +} + +bool +ZFSPool::isOpen(const IOService *forClient) const +{ + IOLog("ZFSPool %s\n", __func__); + return (false); +} + +void +ZFSPool::close(IOService *client, IOOptionBits options) +{ + IOLog("ZFSPool %s\n", __func__); + IOService::close(client, options); +} +#endif + +bool +ZFSPool::handleOpen(IOService *client, + IOOptionBits options, void *arg) +{ + bool ret = true; + + dprintf(""); + // IOLog("ZFSPool %s\n", __func__); + + /* XXX IOService open() locks for arbitration around handleOpen */ + // lockForArbitration(); + _openClients->setObject(client); + ret = _openClients->containsObject(client); + // unlockForArbitration(); + + return (ret); +// return (IOService::handleOpen(client, options, NULL)); +} + +bool +ZFSPool::handleIsOpen(const IOService *client) const +{ + bool ret; + + dprintf(""); + // IOLog("ZFSPool %s\n", __func__); + + /* XXX IOService isOpen() locks for arbitration around handleIsOpen */ + // lockForArbitration(); + ret = _openClients->containsObject(client); + // unlockForArbitration(); + + return (ret); +// return (IOService::handleIsOpen(client)); +} + +void +ZFSPool::handleClose(IOService *client, + IOOptionBits options) +{ + dprintf(""); + // IOLog("ZFSPool %s\n", __func__); + + /* XXX IOService close() locks for arbitration around handleClose */ + // lockForArbitration(); + if (_openClients->containsObject(client) == false) { + dprintf("not open"); + } + /* Remove client from set */ + _openClients->removeObject(client); + // unlockForArbitration(); + +// IOService::handleClose(client, options); +} + +#if 0 +/* XXX IOBlockStorageDevice */ +void +ZFSPool::read(IOService *client, UInt64 byteStart, + IOMemoryDescriptor *buffer, IOStorageAttributes *attr, + IOStorageCompletion *completion) +{ + IOLog("ZFSPool %s\n", __func__); + IOStorage::complete(completion, kIOReturnError, 0); +} + +void +ZFSPool::write(IOService *client, UInt64 byteStart, + IOMemoryDescriptor *buffer, IOStorageAttributes *attr, + IOStorageCompletion *completion) +{ + IOLog("ZFSPool %s\n", __func__); + IOStorage::complete(completion, kIOReturnError, 0); +} +#endif + +bool +ZFSPool::setPoolName(const char *name) +{ +/* Assign dataset name from null-terminated string */ + OSString *dsstr; + // const OSSymbol *dsstr; +#if 0 + OSDictionary *dict; + char *newname, *oldname; +#else + char *newname; +#endif + size_t len; + + DPRINTF_FUNC(); + + /* Validate arguments */ + if (!name || (len = strnlen(name, + ZFS_MAX_DATASET_NAME_LEN)) == 0) { + dprintf("missing argument"); + return (false); + } + + /* Truncate too-long names (shouldn't happen) */ + if (len == ZFS_MAX_DATASET_NAME_LEN && + name[ZFS_MAX_DATASET_NAME_LEN] != '\0') { + dprintf("name too long [%s]", name); + /* XXX Just truncate the name */ + len--; + } + + /* Allocate room for name plus null char */ + newname = (char *)kmem_alloc(len+1, KM_SLEEP); + if (!newname) { + dprintf("string alloc failed"); + return (false); + } + snprintf(newname, len+1, "%s", name); + newname[len] = '\0'; /* just in case */ + + /* Save an OSString copy for IORegistry */ + dsstr = OSString::withCString(newname); + // dsstr = OSSymbol::withCString(newname); + + kmem_free(newname, len+1); + + if (!dsstr) { + dprintf("OSString failed"); + return (false); + } + +#if 0 + /* Swap into class private var */ + oldname = (char *)productString; + productString = newname; + newname = 0; + if (oldname) { + kmem_free(oldname, strlen(oldname)+1); + oldname = 0; + } + + /* Get and clone device characteristics prop dict */ + if ((dict = OSDynamicCast(OSDictionary, + getProperty(kIOPropertyDeviceCharacteristicsKey))) == NULL || + (dict = OSDictionary::withDictionary(dict)) == NULL) { + dprintf("couldn't clone prop dict"); + /* Should only happen during initialization */ + } + + if (dict) { + /* Copy string, add to dictionary, and replace prop dict */ + if (dict->setObject(kIOPropertyProductNameKey, + dsstr) == false || + setProperty(kIOPropertyDeviceCharacteristicsKey, + dict) == false) { + dprintf("couldn't set name"); + OSSafeReleaseNULL(dsstr); + OSSafeReleaseNULL(dict); + return (false); + } + OSSafeReleaseNULL(dict); + } +#endif + + /* Set Pool name IOregistry property */ + setProperty(kZFSPoolNameKey, dsstr); + + /* Finally, set the IORegistryEntry/IOService name */ + setName(dsstr->getCStringNoCopy()); + OSSafeReleaseNULL(dsstr); + + return (true); +} + +bool +ZFSPool::init(OSDictionary *properties, spa_t *spa) +{ +#if 0 + /* Allocate dictionaries and symbols */ + OSDictionary *pdict = OSDictionary::withCapacity(2); + OSDictionary *ddict = OSDictionary::withCapacity(4); + const OSSymbol *virtualSymbol = OSSymbol::withCString( + kIOPropertyPhysicalInterconnectTypeVirtual); + const OSSymbol *locationSymbol = OSSymbol::withCString( + kIOPropertyInternalExternalKey); + const OSSymbol *ssdSymbol = OSSymbol::withCString( + kIOPropertyMediumTypeSolidStateKey); + OSNumber *physSize = NULL, *logSize = NULL; + const OSSymbol *vendorSymbol = 0; + const OSSymbol *revisionSymbol = 0; + const OSSymbol *blankSymbol = 0; + OSBoolean *rdonly = 0; + UInt64 phys_bsize, log_bsize; + OSString *str = 0; + const char *cstr = 0; +#endif + uint64_t space; + bool ret = false; + + DPRINTF_FUNC(); + +#if 0 + physSize = OSNumber::withNumber((uint32_t)ZFS_POOL_DEV_BSIZE, 32); + logSize = OSNumber::withNumber((uint32_t)ZFS_POOL_DEV_BSIZE, 32); +#endif + if (!spa) { + dprintf("missing spa"); + goto error; + } + +#if 0 + /* Get physical and logical size from spa */ + phys_bsize = (1ULL<spa_max_ashift); + log_bsize = (1ULL<spa_min_ashift); +#endif + +#if 0 + /* Workaround glitchy behavior with large bsize in xnu */ + if (log_bsize > 8192) log_bsize = 8192; +#endif + +#if 0 + /* XXX Shouldn't be possible */ + if (log_bsize == 0) log_bsize = DEV_BSIZE; + + physSize = OSNumber::withNumber((uint32_t)phys_bsize, 32); + logSize = OSNumber::withNumber((uint32_t)log_bsize, 32); + + /* Validate allocations */ + if (!pdict || !ddict || !virtualSymbol || !locationSymbol || + !ssdSymbol || !physSize || !logSize) { + dprintf("allocation failed"); + goto error; + } +#endif + + /* Need an OSSet for open clients */ + _openClients = OSSet::withCapacity(1); + if (_openClients == NULL) { + dprintf("client OSSet failed"); + goto error; + } + + /* Set spa pointer and this Pool object's name to match */ + if (!spa) { + dprintf("missing spa"); + goto error; + } + _spa = spa; + // setName(spa_name(spa)); + +#if 0 + /* Init class statics every time an instance inits */ + /* Shared across instances, but doesn't hurt to reprint */ + if (vendorString == NULL) { + char *string; + int len = strlen("zpool")+1; + string = (char *)kmem_alloc(len, KM_SLEEP); + if (!string) goto error; + snprintf(string, len, "zpool"); + vendorString = string; + } + + if (revisionString == NULL) { + char *string; + int len = strlen("0.1")+1; + string = (char *)kmem_alloc(len, KM_SLEEP); + if (!string) goto error; + snprintf(string, len, "0.1"); + revisionString = string; + } + + if (revisionString == NULL) { + char *string; + int len = strlen("ZFS Pool")+1; + string = (char *)kmem_alloc(len, KM_SLEEP); + if (!string) goto error; + snprintf(string, len, "ZFS pool"); + infoString = string; + } + + /* For IORegistry keys, cache OSSymbols for class statics */ + /* Leverages OSSymbol cahce pool to reuse across instances */ + vendorSymbol = OSSymbol::withCString(vendorString); + revisionSymbol = OSSymbol::withCString(revisionString); + blankSymbol = OSSymbol::withCString(""); + if (!vendorSymbol || !revisionSymbol || !blankSymbol) { + dprintf("class symbols failed"); + goto error; + } +#endif + + /* Call super init */ + if (IOService::init(properties) == false) { + dprintf("device init failed"); + goto error; + } + +#if 0 + /* Set class private vars */ + productString = NULL; + isReadOnly = false; // XXX should really be true initially + + /* Set Protocol Characteristics */ + if (pdict->setObject(kIOPropertyPhysicalInterconnectLocationKey, + locationSymbol) == false || + pdict->setObject(kIOPropertyPhysicalInterconnectTypeKey, + virtualSymbol) == false) { + dprintf("pdict set properties failed"); + goto error; + } + setProperty(kIOPropertyProtocolCharacteristicsKey, pdict); + + /* Set Device Characteristics */ + if (ddict->setObject(kIOPropertyVendorNameKey, + vendorSymbol) == false || + ddict->setObject(kIOPropertyProductRevisionLevelKey, + revisionSymbol) == false || + ddict->setObject(kIOPropertyProductSerialNumberKey, + blankSymbol) == false || + ddict->setObject(kIOPropertyPhysicalBlockSizeKey, + physSize) == false || + ddict->setObject(kIOPropertyLogicalBlockSizeKey, + logSize) == false || + ddict->setObject(kIOPropertyMediumTypeKey, + ssdSymbol) == false) { + dprintf("ddict set properties failed"); + goto error; + } + setProperty(kIOPropertyDeviceCharacteristicsKey, ddict); + + /* Check for passed in readonly status */ + if (properties && (rdonly = OSDynamicCast(OSBoolean, + properties->getObject(kZFSPoolReadOnlyKey))) != NULL) { + /* Got the boolean */ + isReadOnly = rdonly->getValue(); + dprintf("set %s", (isReadOnly ? "readonly" : "readwrite")); + } + + /* Check for passed in pool GUID */ + if (properties && (str = OSDynamicCast(OSString, + properties->getObject(kZFSPoolGUIDKey))) != NULL) { + /* Got the string, try to set GUID */ + str->retain(); + if (ddict->setObject(kZFSPoolGUIDKey, str) == false) { + dprintf("couldn't set GUID"); + OSSafeReleaseNULL(str); + goto error; + } +#ifdef DEBUG + cstr = str->getCStringNoCopy(); + dprintf("set GUID"); + cstr = 0; +#endif + OSSafeReleaseNULL(str); + } +#endif + + if (setPoolName(spa_name(spa)) == false) { + dprintf("setPoolName failed"); + goto error; + } + + space = spa_get_dspace(spa); +dprintf("space %llu", space); + setProperty(kZFSPoolSizeKey, space, 64); + +#if 0 + /* Check for passed in pool name */ + if (properties && (str = OSDynamicCast(OSString, + properties->getObject(kZFSPoolNameKey))) != NULL && + (cstr = str->getCStringNoCopy()) != NULL) { + /* Got the string, try to set name */ + str->retain(); + if (setPoolName(cstr) == false) { + /* Unlikely */ + dprintf("couldn't setup pool" + " name property [%s]", cstr); + OSSafeReleaseNULL(str); + goto error; + } + + dprintf("set pool name [%s]", cstr); + OSSafeReleaseNULL(str); + } else { + if (setPoolName("invalid") == false) { + dprintf("setPoolName failed"); + goto error; + } + dprintf("set name [invalid]"); + } +#endif + + /* Success */ + ret = true; + +error: +#if 0 + /* All of these will be released on error */ + OSSafeReleaseNULL(pdict); + OSSafeReleaseNULL(ddict); + OSSafeReleaseNULL(virtualSymbol); + OSSafeReleaseNULL(locationSymbol); + OSSafeReleaseNULL(ssdSymbol); + OSSafeReleaseNULL(physSize); + OSSafeReleaseNULL(logSize); + OSSafeReleaseNULL(vendorSymbol); + OSSafeReleaseNULL(revisionSymbol); + OSSafeReleaseNULL(blankSymbol); + OSSafeReleaseNULL(str); +#endif + return (ret); +} + +void +ZFSPool::free() +{ + OSSet *oldSet; +#if 0 + char *pstring; +#endif + + if (_openClients) { + oldSet = _openClients; + _openClients = 0; + OSSafeReleaseNULL(oldSet); + } + _spa = 0; + +#if 0 + pstring = (char *)productString; + productString = 0; + if (pstring) kmem_free(pstring, strlen(pstring) + 1); +#endif + + IOService::free(); +} + +extern "C" { + +void +spa_iokit_pool_proxy_destroy(spa_t *spa) +{ + ZFSPool *proxy; + spa_iokit_t *wrapper; + + if (!spa) { + printf("missing spa"); + return; + } + + /* Get pool proxy */ + wrapper = spa->spa_iokit_proxy; + spa->spa_iokit_proxy = NULL; + + if (wrapper == NULL) { + printf("missing spa_iokit_proxy"); + return; + } + + proxy = wrapper->proxy; + + /* Free the struct */ + kmem_free(wrapper, sizeof (spa_iokit_t)); + if (!proxy) { + printf("missing proxy"); + return; + } + + if (proxy->terminate(kIOServiceSynchronous| + kIOServiceRequired) == false) { + dprintf("terminate failed"); + } + proxy->release(); + + /* + * IOService *provider; + * provider = proxy->getProvider(); + * + * proxy->detach(provider); + * proxy->stop(provider); + * + * proxy->release(); + */ +} + +int +spa_iokit_pool_proxy_create(spa_t *spa) +{ + IOService *zfs_hl; + ZFSPool *proxy; + spa_iokit_t *wrapper; + + if (!spa) { + dprintf("missing spa"); + return (EINVAL); + } + + /* Allocate C struct */ + if ((wrapper = (spa_iokit_t *)kmem_alloc(sizeof (spa_iokit_t), + KM_SLEEP)) == NULL) { + dprintf("couldn't allocate wrapper"); + return (ENOMEM); + } + + /* Get ZFS IOService */ + if ((zfs_hl = copy_zfs_handle()) == NULL) { + dprintf("couldn't get ZFS handle"); + kmem_free(wrapper, sizeof (spa_iokit_t)); + return (ENODEV); + } + + /* Allocate and init ZFS pool proxy */ + proxy = ZFSPool::withProviderAndPool(zfs_hl, spa); + if (!proxy) { + dprintf("Pool proxy creation failed"); + kmem_free(wrapper, sizeof (spa_iokit_t)); + OSSafeReleaseNULL(zfs_hl); + return (ENOMEM); + } + /* Drop retain from copy_zfs_handle */ + OSSafeReleaseNULL(zfs_hl); + + /* Set pool proxy */ + wrapper->proxy = proxy; + spa->spa_iokit_proxy = wrapper; + + return (0); +} + +} /* extern "C" */ + +ZFSPool * +ZFSPool::withProviderAndPool(IOService *zfs_hl, spa_t *spa) +{ + ZFSPool *proxy = new ZFSPool; + + if (!proxy) { + printf("allocation failed"); + return (0); + } + + if (proxy->init(0, spa) == false || + proxy->attach(zfs_hl) == false) { + printf("init/attach failed"); + OSSafeReleaseNULL(proxy); + return (0); + } + + if (proxy->start(zfs_hl) == false) { + printf("start failed"); + proxy->detach(zfs_hl); + OSSafeReleaseNULL(proxy); + return (0); + } + + /* Open zfs_hl, adding proxy to its open clients */ + // if (proxy->open(zfs_hl) == false) { + if (zfs_hl->open(proxy) == false) { + printf("open failed"); + proxy->stop(zfs_hl); + proxy->detach(zfs_hl); + OSSafeReleaseNULL(proxy); + return (0); + } + proxy->registerService(kIOServiceAsynchronous); + + return (proxy); +} + +#if 0 +/* XXX IOBlockStorageDevice */ +IOReturn +ZFSPool::doSynchronizeCache(void) +{ + dprintf(""); + return (kIOReturnSuccess); +} + +IOReturn +ZFSPool::doAsyncReadWrite(IOMemoryDescriptor *buffer, + UInt64 block, UInt64 nblks, + IOStorageAttributes *attributes, + IOStorageCompletion *completion) +{ + char zero[ZFS_POOL_DEV_BSIZE]; + size_t len, cur, off = 0; + + DPRINTF_FUNC(); + + if (!buffer) { + IOStorage::complete(completion, kIOReturnError, 0); + return (kIOReturnSuccess); + } + + /* Read vs. write */ + if (buffer->getDirection() == kIODirectionIn) { + /* Zero the read buffer */ + bzero(zero, ZFS_POOL_DEV_BSIZE); + len = buffer->getLength(); + while (len > 0) { + cur = (len > ZFS_POOL_DEV_BSIZE ? + ZFS_POOL_DEV_BSIZE : len); + buffer->writeBytes(/* offset */ off, + /* buf */ zero, /* length */ cur); + off += cur; + len -= cur; + } + // dprintf("read: %llu %llu", block, nblks); + IOStorage::complete(completion, kIOReturnSuccess, + buffer->getLength()); + return (kIOReturnSuccess); + } + + if (buffer->getDirection() != kIODirectionOut) { + dprintf("invalid direction %d", buffer->getDirection()); + IOStorage::complete(completion, kIOReturnError, 0); + return (kIOReturnSuccess); + } + + /* + * XXX For now this just returns error for all writes. + * If it turns out that mountroot/bdevvp try to + * verify writable status by reading a block and writing + * it back to disk, lie and say it succeeded. + */ + dprintf("write: %llu %llu", block, nblks); + IOStorage::complete(completion, kIOReturnError, 0); + return (kIOReturnSuccess); +} + +IOReturn +ZFSPool::doEjectMedia() +{ + DPRINTF_FUNC(); + /* XXX Called at shutdown, maybe return success? */ + return (kIOReturnError); +} + +IOReturn +ZFSPool::doFormatMedia(UInt64 byteCapacity) +{ + DPRINTF_FUNC(); + /* XXX shouldn't need it */ + return (kIOReturnError); + // return (kIOReturnSuccess); +} + +UInt32 +ZFSPool::doGetFormatCapacities(UInt64 *capacities, + UInt32 capacitiesMaxCount) const +{ + DPRINTF_FUNC(); + if (capacities && capacitiesMaxCount > 0) { + capacities[0] = (ZFS_POOL_DEV_BSIZE * ZFS_POOL_DEV_BCOUNT); + dprintf("capacity %llu", capacities[0]); + } + + /* Always inform caller of capacity count */ + return (1); +} + +/* Returns full pool name from instance private var */ +char * +ZFSPool::getProductString() +{ + if (productString) dprintf("[%s]", productString); + /* Return class private string */ + return ((char *)productString); +} + +/* Returns readonly status from instance private var */ +IOReturn +ZFSPool::reportWriteProtection(bool *isWriteProtected) +{ + DPRINTF_FUNC(); + if (isWriteProtected) *isWriteProtected = isReadOnly; + return (kIOReturnSuccess); +} + +/* These return class static string for all instances */ +char * +ZFSPool::getVendorString() +{ + dprintf("[%s]", vendorString); + /* Return class static string */ + return ((char *)vendorString); +} +char * +ZFSPool::getRevisionString() +{ + dprintf("[%s]", revisionString); + /* Return class static string */ + return ((char *)revisionString); +} +char * +ZFSPool::getAdditionalDeviceInfoString() +{ + dprintf("[%s]", infoString); + /* Return class static string */ + return ((char *)infoString); +} + +/* Always return media present and unchanged */ +IOReturn +ZFSPool::reportMediaState(bool *mediaPresent, + bool *changedState) +{ + DPRINTF_FUNC(); + if (mediaPresent) *mediaPresent = true; + if (changedState) *changedState = false; + return (kIOReturnSuccess); +} + +/* Always report nonremovable and nonejectable */ +IOReturn +ZFSPool::reportRemovability(bool *isRemoveable) +{ + DPRINTF_FUNC(); + if (isRemoveable) *isRemoveable = false; + return (kIOReturnSuccess); +} +IOReturn +ZFSPool::reportEjectability(bool *isEjectable) +{ + DPRINTF_FUNC(); + if (isEjectable) *isEjectable = false; + return (kIOReturnSuccess); +} + +/* Always report 512b blocksize */ +IOReturn +ZFSPool::reportBlockSize(UInt64 *blockSize) +{ + DPRINTF_FUNC(); + if (!blockSize) + return (kIOReturnError); + + *blockSize = ZFS_POOL_DEV_BSIZE; + return (kIOReturnSuccess); +} + +/* XXX Calculate from dev_bcount, should get size from objset */ +/* XXX Can issue message kIOMessageMediaParametersHaveChanged to update */ +IOReturn +ZFSPool::reportMaxValidBlock(UInt64 *maxBlock) +{ + DPRINTF_FUNC(); + if (!maxBlock) + return (kIOReturnError); + + // *maxBlock = 0; + *maxBlock = ZFS_POOL_DEV_BCOUNT - 1; + dprintf("maxBlock %llu", *maxBlock); + + return (kIOReturnSuccess); +} +#endif diff --git a/module/os/macos/zfs/abd_os.c b/module/os/macos/zfs/abd_os.c new file mode 100644 index 000000000000..3895d129e308 --- /dev/null +++ b/module/os/macos/zfs/abd_os.c @@ -0,0 +1,473 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 by Chunwei Chen. All rights reserved. + * Copyright (c) 2016 by Delphix. All rights reserved. + * Copyright (c) 2020 by Jorgen Lundman. All rights reserved. + */ + +/* + * See abd.c for a general overview of the arc buffered data (ABD). + * + * Using a large proportion of scattered ABDs decreases ARC fragmentation since + * when we are at the limit of allocatable space, using equal-size chunks will + * allow us to quickly reclaim enough space for a new large allocation (assuming + * it is also scattered). + * + * ABDs are allocated scattered by default unless the caller uses + * abd_alloc_linear() or zfs_abd_scatter_enabled is disabled. + */ + +#include +#include +#include +#include +#include + +typedef struct abd_stats { + kstat_named_t abdstat_struct_size; + kstat_named_t abdstat_scatter_cnt; + kstat_named_t abdstat_scatter_data_size; + kstat_named_t abdstat_scatter_chunk_waste; + kstat_named_t abdstat_linear_cnt; + kstat_named_t abdstat_linear_data_size; +} abd_stats_t; + +static abd_stats_t abd_stats = { + /* Amount of memory occupied by all of the abd_t struct allocations */ + { "struct_size", KSTAT_DATA_UINT64 }, + /* + * The number of scatter ABDs which are currently allocated, excluding + * ABDs which don't own their data (for instance the ones which were + * allocated through abd_get_offset()). + */ + { "scatter_cnt", KSTAT_DATA_UINT64 }, + /* Amount of data stored in all scatter ABDs tracked by scatter_cnt */ + { "scatter_data_size", KSTAT_DATA_UINT64 }, + /* + * The amount of space wasted at the end of the last chunk across all + * scatter ABDs tracked by scatter_cnt. + */ + { "scatter_chunk_waste", KSTAT_DATA_UINT64 }, + /* + * The number of linear ABDs which are currently allocated, excluding + * ABDs which don't own their data (for instance the ones which were + * allocated through abd_get_offset() and abd_get_from_buf()). If an + * ABD takes ownership of its buf then it will become tracked. + */ + { "linear_cnt", KSTAT_DATA_UINT64 }, + /* Amount of data stored in all linear ABDs tracked by linear_cnt */ + { "linear_data_size", KSTAT_DATA_UINT64 }, +}; + +/* + * The size of the chunks ABD allocates. Because the sizes allocated from the + * kmem_cache can't change, this tunable can only be modified at boot. Changing + * it at runtime would cause ABD iteration to work incorrectly for ABDs which + * were allocated with the old size, so a safeguard has been put in place which + * will cause the machine to panic if you change it and try to access the data + * within a scattered ABD. + */ +size_t zfs_abd_chunk_size = 4096; + +kmem_cache_t *abd_chunk_cache; +static kstat_t *abd_ksp; + + +/* + * We use a scattered SPA_MAXBLOCKSIZE sized ABD whose chunks are + * just a single zero'd sized zfs_abd_chunk_size buffer. This + * allows us to conserve memory by only using a single zero buffer + * for the scatter chunks. + */ +abd_t *abd_zero_scatter = NULL; +static char *abd_zero_buf = NULL; + +static void +abd_free_chunk(void *c) +{ + kmem_cache_free(abd_chunk_cache, c); +} + +static size_t +abd_chunkcnt_for_bytes(size_t size) +{ + return (P2ROUNDUP(size, zfs_abd_chunk_size) / zfs_abd_chunk_size); +} + +static inline size_t +abd_scatter_chunkcnt(abd_t *abd) +{ + ASSERT(!abd_is_linear(abd)); + return (abd_chunkcnt_for_bytes( + ABD_SCATTER(abd).abd_offset + abd->abd_size)); +} + +boolean_t +abd_size_alloc_linear(size_t size) +{ + return (size <= zfs_abd_chunk_size ? B_TRUE : B_FALSE); +} + +void +abd_update_scatter_stats(abd_t *abd, abd_stats_op_t op) +{ + size_t n = abd_scatter_chunkcnt(abd); + ASSERT(op == ABDSTAT_INCR || op == ABDSTAT_DECR); + if (op == ABDSTAT_INCR) { + ABDSTAT_BUMP(abdstat_scatter_cnt); + ABDSTAT_INCR(abdstat_scatter_data_size, abd->abd_size); + ABDSTAT_INCR(abdstat_scatter_chunk_waste, + n * zfs_abd_chunk_size - abd->abd_size); + } else { + ABDSTAT_BUMPDOWN(abdstat_scatter_cnt); + ABDSTAT_INCR(abdstat_scatter_data_size, -(int)abd->abd_size); + ABDSTAT_INCR(abdstat_scatter_chunk_waste, + abd->abd_size - n * zfs_abd_chunk_size); + } +} + +void +abd_update_linear_stats(abd_t *abd, abd_stats_op_t op) +{ + ASSERT(op == ABDSTAT_INCR || op == ABDSTAT_DECR); + if (op == ABDSTAT_INCR) { + ABDSTAT_BUMP(abdstat_linear_cnt); + ABDSTAT_INCR(abdstat_linear_data_size, abd->abd_size); + } else { + ABDSTAT_BUMPDOWN(abdstat_linear_cnt); + ABDSTAT_INCR(abdstat_linear_data_size, -(int)abd->abd_size); + } +} + +void +abd_verify_scatter(abd_t *abd) +{ + /* + * There is no scatter linear pages in FreeBSD so there is an + * if an error if the ABD has been marked as a linear page. + */ + VERIFY(!abd_is_linear_page(abd)); + ASSERT3U(ABD_SCATTER(abd).abd_offset, <, + zfs_abd_chunk_size); + size_t n = abd_scatter_chunkcnt(abd); + for (int i = 0; i < n; i++) { + ASSERT3P( + ABD_SCATTER(abd).abd_chunks[i], !=, NULL); + } +} + +void +abd_alloc_chunks(abd_t *abd, size_t size) +{ + size_t n = abd_chunkcnt_for_bytes(size); + for (int i = 0; i < n; i++) { + void *c = kmem_cache_alloc(abd_chunk_cache, KM_PUSHPAGE); + ASSERT3P(c, !=, NULL); + ABD_SCATTER(abd).abd_chunks[i] = c; + } + ABD_SCATTER(abd).abd_chunk_size = zfs_abd_chunk_size; +} + +void +abd_free_chunks(abd_t *abd) +{ + size_t n = abd_scatter_chunkcnt(abd); + for (int i = 0; i < n; i++) { + abd_free_chunk(ABD_SCATTER(abd).abd_chunks[i]); + } +} + +abd_t * +abd_alloc_struct_impl(size_t size) +{ + size_t chunkcnt = abd_chunkcnt_for_bytes(size); + /* + * In the event we are allocating a gang ABD, the size passed in + * will be 0. We must make sure to set abd_size to the size of an + * ABD struct as opposed to an ABD scatter with 0 chunks. The gang + * ABD struct allocation accounts for an additional 24 bytes over + * a scatter ABD with 0 chunks. + */ + size_t abd_size = MAX(sizeof (abd_t), + offsetof(abd_t, abd_u.abd_scatter.abd_chunks[chunkcnt])); + abd_t *abd = kmem_alloc(abd_size, KM_PUSHPAGE); + ASSERT3P(abd, !=, NULL); + ABDSTAT_INCR(abdstat_struct_size, abd_size); + + return (abd); +} + +void +abd_free_struct_impl(abd_t *abd) +{ + uint_t chunkcnt = abd_is_linear(abd) || abd_is_gang(abd) ? 0 : + abd_scatter_chunkcnt(abd); + ssize_t size = MAX(sizeof (abd_t), + offsetof(abd_t, abd_u.abd_scatter.abd_chunks[chunkcnt])); + kmem_free(abd, size); + ABDSTAT_INCR(abdstat_struct_size, -size); +} + +/* + * Allocate scatter ABD of size SPA_MAXBLOCKSIZE, where + * each chunk in the scatterlist will be set to abd_zero_buf. + */ +static void +abd_alloc_zero_scatter(void) +{ + size_t n = abd_chunkcnt_for_bytes(SPA_MAXBLOCKSIZE); + abd_zero_buf = kmem_zalloc(zfs_abd_chunk_size, KM_SLEEP); + abd_zero_scatter = abd_alloc_struct(SPA_MAXBLOCKSIZE); + + abd_zero_scatter->abd_flags |= ABD_FLAG_OWNER | ABD_FLAG_ZEROS; + abd_zero_scatter->abd_size = SPA_MAXBLOCKSIZE; + + ABD_SCATTER(abd_zero_scatter).abd_offset = 0; + ABD_SCATTER(abd_zero_scatter).abd_chunk_size = + zfs_abd_chunk_size; + + for (int i = 0; i < n; i++) { + ABD_SCATTER(abd_zero_scatter).abd_chunks[i] = + abd_zero_buf; + } + + ABDSTAT_BUMP(abdstat_scatter_cnt); + ABDSTAT_INCR(abdstat_scatter_data_size, zfs_abd_chunk_size); +} + +static void +abd_free_zero_scatter(void) +{ + ABDSTAT_BUMPDOWN(abdstat_scatter_cnt); + ABDSTAT_INCR(abdstat_scatter_data_size, -(int)zfs_abd_chunk_size); + + abd_free_struct(abd_zero_scatter); + abd_zero_scatter = NULL; + kmem_free(abd_zero_buf, zfs_abd_chunk_size); +} + +void +abd_init(void) +{ + abd_chunk_cache = kmem_cache_create("abd_chunk", zfs_abd_chunk_size, + MIN(PAGE_SIZE, 4096), + NULL, NULL, NULL, NULL, abd_arena, KMC_NOTOUCH); + + abd_ksp = kstat_create("zfs", 0, "abdstats", "misc", KSTAT_TYPE_NAMED, + sizeof (abd_stats) / sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL); + if (abd_ksp != NULL) { + abd_ksp->ks_data = &abd_stats; + kstat_install(abd_ksp); + } + + abd_alloc_zero_scatter(); +} + +void +abd_fini(void) +{ + abd_free_zero_scatter(); + + if (abd_ksp != NULL) { + kstat_delete(abd_ksp); + abd_ksp = NULL; + } + + kmem_cache_destroy(abd_chunk_cache); + abd_chunk_cache = NULL; +} + +void +abd_free_linear_page(abd_t *abd) +{ + /* + * FreeBSD does not have have scatter linear pages + * so there is an error. + */ + VERIFY(0); +} + +/* + * If we're going to use this ABD for doing I/O using the block layer, the + * consumer of the ABD data doesn't care if it's scattered or not, and we don't + * plan to store this ABD in memory for a long period of time, we should + * allocate the ABD type that requires the least data copying to do the I/O. + * + * Currently this is linear ABDs, however if ldi_strategy() can ever issue I/Os + * using a scatter/gather list we should switch to that and replace this call + * with vanilla abd_alloc(). + */ +abd_t * +abd_alloc_for_io(size_t size, boolean_t is_metadata) +{ + return (abd_alloc_linear(size, is_metadata)); +} + +abd_t * +abd_get_offset_scatter(abd_t *abd, abd_t *sabd, size_t off) +{ + abd_verify(sabd); + ASSERT3U(off, <=, sabd->abd_size); + + size_t new_offset = ABD_SCATTER(sabd).abd_offset + off; + size_t chunkcnt = abd_scatter_chunkcnt(sabd) - + (new_offset / zfs_abd_chunk_size); + + /* + * If an abd struct is provided, it is only the minimum size. If we + * need additional chunks, we need to allocate a new struct. + */ + if (abd != NULL && + offsetof(abd_t, abd_u.abd_scatter.abd_chunks[chunkcnt]) > + sizeof (abd_t)) { + abd = NULL; + } + + if (abd == NULL) + abd = abd_alloc_struct(chunkcnt * zfs_abd_chunk_size); + + /* + * Even if this buf is filesystem metadata, we only track that + * if we own the underlying data buffer, which is not true in + * this case. Therefore, we don't ever use ABD_FLAG_META here. + */ + abd->abd_flags = 0; + + ABD_SCATTER(abd).abd_offset = new_offset % zfs_abd_chunk_size; + ABD_SCATTER(abd).abd_chunk_size = zfs_abd_chunk_size; + + /* Copy the scatterlist starting at the correct offset */ + (void) memcpy(&ABD_SCATTER(abd).abd_chunks, + &ABD_SCATTER(sabd).abd_chunks[new_offset / + zfs_abd_chunk_size], + chunkcnt * sizeof (void *)); + + return (abd); +} + +static inline size_t +abd_iter_scatter_chunk_offset(struct abd_iter *aiter) +{ + ASSERT(!abd_is_linear(aiter->iter_abd)); + return ((ABD_SCATTER(aiter->iter_abd).abd_offset + + aiter->iter_pos) % zfs_abd_chunk_size); +} + +static inline size_t +abd_iter_scatter_chunk_index(struct abd_iter *aiter) +{ + ASSERT(!abd_is_linear(aiter->iter_abd)); + return ((ABD_SCATTER(aiter->iter_abd).abd_offset + + aiter->iter_pos) / zfs_abd_chunk_size); +} + +/* + * Initialize the abd_iter. + */ +void +abd_iter_init(struct abd_iter *aiter, abd_t *abd) +{ + ASSERT(!abd_is_gang(abd)); + abd_verify(abd); + aiter->iter_abd = abd; + aiter->iter_pos = 0; + aiter->iter_mapaddr = NULL; + aiter->iter_mapsize = 0; +} + +/* + * This is just a helper function to see if we have exhausted the + * abd_iter and reached the end. + */ +boolean_t +abd_iter_at_end(struct abd_iter *aiter) +{ + return (aiter->iter_pos == aiter->iter_abd->abd_size); +} + +/* + * Advance the iterator by a certain amount. Cannot be called when a chunk is + * in use. This can be safely called when the aiter has already exhausted, in + * which case this does nothing. + */ +void +abd_iter_advance(struct abd_iter *aiter, size_t amount) +{ + ASSERT3P(aiter->iter_mapaddr, ==, NULL); + ASSERT0(aiter->iter_mapsize); + + /* There's nothing left to advance to, so do nothing */ + if (abd_iter_at_end(aiter)) + return; + + aiter->iter_pos += amount; +} + +/* + * Map the current chunk into aiter. This can be safely called when the aiter + * has already exhausted, in which case this does nothing. + */ +void +abd_iter_map(struct abd_iter *aiter) +{ + void *paddr; + size_t offset = 0; + + ASSERT3P(aiter->iter_mapaddr, ==, NULL); + ASSERT0(aiter->iter_mapsize); + + /* Panic if someone has changed zfs_abd_chunk_size */ + IMPLY(!abd_is_linear(aiter->iter_abd), zfs_abd_chunk_size == + ABD_SCATTER(aiter->iter_abd).abd_chunk_size); + + /* There's nothing left to iterate over, so do nothing */ + if (abd_iter_at_end(aiter)) + return; + + if (abd_is_linear(aiter->iter_abd)) { + offset = aiter->iter_pos; + aiter->iter_mapsize = aiter->iter_abd->abd_size - offset; + paddr = ABD_LINEAR_BUF(aiter->iter_abd); + } else { + size_t index = abd_iter_scatter_chunk_index(aiter); + offset = abd_iter_scatter_chunk_offset(aiter); + aiter->iter_mapsize = MIN(zfs_abd_chunk_size - offset, + aiter->iter_abd->abd_size - aiter->iter_pos); + paddr = ABD_SCATTER(aiter->iter_abd).abd_chunks[index]; + } + aiter->iter_mapaddr = (char *)paddr + offset; +} + +/* + * Unmap the current chunk from aiter. This can be safely called when the aiter + * has already exhausted, in which case this does nothing. + */ +void +abd_iter_unmap(struct abd_iter *aiter) +{ + /* There's nothing left to unmap, so do nothing */ + if (abd_iter_at_end(aiter)) + return; + + ASSERT3P(aiter->iter_mapaddr, !=, NULL); + ASSERT3U(aiter->iter_mapsize, >, 0); + + aiter->iter_mapaddr = NULL; + aiter->iter_mapsize = 0; +} + +void +abd_cache_reap_now(void) +{ + kmem_cache_reap_now(abd_chunk_cache); +} diff --git a/module/os/macos/zfs/arc_os.c b/module/os/macos/zfs/arc_os.c new file mode 100644 index 000000000000..e853ab68f8d5 --- /dev/null +++ b/module/os/macos/zfs/arc_os.c @@ -0,0 +1,777 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, Joyent, Inc. + * Copyright (c) 2011, 2019 by Delphix. All rights reserved. + * Copyright (c) 2014 by Saso Kiselkov. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef _KERNEL +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +extern arc_stats_t arc_stats; + +static kmutex_t arc_reclaim_lock; +static kcondvar_t arc_reclaim_thread_cv; +static boolean_t arc_reclaim_thread_exit; +static kcondvar_t arc_reclaim_waiters_cv; + +/* + * log2(fraction of ARC which must be free to allow growing). + * I.e. If there is less than arc_c >> arc_no_grow_shift free memory, + * when reading a new block into the ARC, we will evict an equal-sized block + * from the ARC. + * + * This must be less than arc_shrink_shift, so that when we shrink the ARC, + * we will still not allow it to grow. + */ +extern int arc_no_grow_shift; + + +/* + * Return a default max arc size based on the amount of physical memory. + */ +uint64_t +arc_default_max(uint64_t min, uint64_t allmem) +{ + /* Default to 1/3 of all memory. */ + return (MAX(allmem, min)); +} + +#ifdef _KERNEL + +static _Atomic boolean_t arc_reclaim_in_loop = B_FALSE; + +/* + * Return maximum amount of memory that we could possibly use. Reduced + * to half of all memory in user space which is primarily used for testing. + */ +uint64_t +arc_all_memory(void) +{ + return (kmem_size()); +} + +/* + * Return the amount of memory that is considered free. In user space + * which is primarily used for testing we pretend that free memory ranges + * from 0-20% of all memory. + */ +uint64_t +arc_free_memory(void) +{ + int64_t avail; + + avail = spl_free_wrapper(); + return (avail >= 0LL ? avail : 0LL); +} + +/* + * Return the amount of memory that can be consumed before reclaim will be + * needed. Positive if there is sufficient free memory, negative indicates + * the amount of memory that needs to be freed up. + */ +int64_t +arc_available_memory(void) +{ + return (arc_free_memory() - arc_sys_free); +} + +int +arc_memory_throttle(spa_t *spa, uint64_t reserve, uint64_t txg) +{ + + /* possibly wake up arc reclaim thread */ + + if (arc_reclaim_in_loop == B_FALSE) { + if (spl_free_manual_pressure_wrapper() != 0 || + !spl_minimal_physmem_p() || + arc_reclaim_needed()) { + cv_signal(&arc_reclaim_thread_cv); + kpreempt(KPREEMPT_SYNC); + ARCSTAT_INCR(arcstat_memory_throttle_count, 1); + } + } + + return (0); +} + +/* + * arc.c has a arc_reap_zthr we should probably use, instead of + * having our own legacy arc_reclaim_thread(). + */ +static void arc_kmem_reap_now(void) +{ + arc_wait_for_eviction(0); + + /* arc.c will do the heavy lifting */ + arc_kmem_reap_soon(); +} + +/* + * Threads can block in arc_get_data_impl() waiting for this thread to evict + * enough data and signal them to proceed. When this happens, the threads in + * arc_get_data_impl() are sleeping while holding the hash lock for their + * particular arc header. Thus, we must be careful to never sleep on a + * hash lock in this thread. This is to prevent the following deadlock: + * + * - Thread A sleeps on CV in arc_get_data_impl() holding hash lock "L", + * waiting for the reclaim thread to signal it. + * + * - arc_reclaim_thread() tries to acquire hash lock "L" using mutex_enter, + * fails, and goes to sleep forever. + * + * This possible deadlock is avoided by always acquiring a hash lock + * using mutex_tryenter() from arc_reclaim_thread(). + */ +static void +arc_reclaim_thread(void *unused) +{ + hrtime_t growtime = 0; + + callb_cpr_t cpr; + + CALLB_CPR_INIT(&cpr, &arc_reclaim_lock, callb_generic_cpr, FTAG); + + mutex_enter(&arc_reclaim_lock); + while (!arc_reclaim_thread_exit) { + arc_reclaim_in_loop = B_TRUE; + + mutex_exit(&arc_reclaim_lock); + + const int64_t pre_adjust_free_memory = MIN(spl_free_wrapper(), + arc_available_memory()); + + int64_t manual_pressure = spl_free_manual_pressure_wrapper(); + spl_free_set_pressure(0); // clears both spl pressure variables + + /* + * We call arc_adjust() before (possibly) calling + * arc_kmem_reap_now(), so that we can wake up + * arc_get_data_impl() sooner. + */ + + if (manual_pressure > 0) { + arc_reduce_target_size(MIN(manual_pressure, + (arc_c >> arc_shrink_shift))); + } + + arc_wait_for_eviction(0); + + int64_t free_memory = arc_available_memory(); + + const int64_t post_adjust_manual_pressure = + spl_free_manual_pressure_wrapper(); + + /* maybe we are getting lots of pressure from spl */ + manual_pressure = MAX(manual_pressure, + post_adjust_manual_pressure); + + spl_free_set_pressure(0); + + const int64_t post_adjust_free_memory = + MIN(spl_free_wrapper(), arc_available_memory()); + + // if arc_adjust() evicted, we expect post_adjust_free_memory + // to be larger than pre_adjust_free_memory (as there should + // be more free memory). + + /* + * d_adj tracks the change of memory across the call + * to arc_wait_for_eviction(), and will count the number + * of bytes the spl_free_thread calculates has been + * made free (signed) + */ + + const int64_t d_adj = post_adjust_free_memory - + pre_adjust_free_memory; + + if (manual_pressure > 0 && post_adjust_manual_pressure == 0) { + // pressure did not get re-signalled during arc_adjust() + if (d_adj > 0) + manual_pressure -= d_adj; + } else if (manual_pressure > 0 && + post_adjust_manual_pressure > 0) { + // otherwise use the most recent pressure value + manual_pressure = post_adjust_manual_pressure; + } + + /* + * If we have successfully freed a bunch of memory, + * it is worth reaping the abd_chunk_cache + */ + if (d_adj >= 64LL*1024LL*1024LL) { + extern kmem_cache_t *abd_chunk_cache; + kmem_cache_reap_now(abd_chunk_cache); + } + + free_memory = post_adjust_free_memory; + + const hrtime_t curtime = gethrtime(); + + if (free_memory < 0 || manual_pressure > 0) { + + if (manual_pressure > 0 || free_memory <= + (arc_c >> arc_no_grow_shift) + SPA_MAXBLOCKSIZE) { + + arc_no_grow = B_TRUE; + + /* + * Absorb occasional low memory conditions, as + * they may be caused by a single sequentially + * writing thread pushing a lot of dirty data + * into the ARC. + * + * In particular, we want to quickly begin + * re-growing the ARC if we are not in chronic + * high pressure. However, if we're in + * chronic high pressure, we want to reduce + * reclaim thread work by keeping arc_no_grow + * set. + * + * If growtime is in the past, then set it to + * last half a second (which is the length of + * the cv_timedwait_hires() call below). + * + * If growtime is in the future, then make + * sure that it is no further than 60 seconds + * into the future. + * + * If growtime is less than 60 seconds in the + * future, then grow growtime by an + * exponentially increasing value starting + * with 500msec. + * + */ + const hrtime_t agr = SEC2NSEC(arc_grow_retry); + static int grow_pass = 0; + + if (growtime == 0) { + growtime = curtime + MSEC2NSEC(500); + grow_pass = 0; + } else { + // check for 500ms not being enough + ASSERT3U(growtime, >, curtime); + if (growtime <= curtime) + growtime = curtime + + MSEC2NSEC(500); + + // growtime is in the future! + const hrtime_t difference = + growtime - curtime; + + if (difference >= agr) { + // cap arc_grow_retry secs now + growtime = curtime + agr - 1LL; + grow_pass = 0; + } else { + /* + * with each pass, push + * turning off arc_no_grow + * by longer + */ + hrtime_t grow_by = + MSEC2NSEC(500) * + (1LL << grow_pass); + + if (grow_by > (agr >> 1)) + grow_by = agr >> 1; + + growtime += grow_by; + + // add 512 seconds maximum + if (grow_pass < 10) + grow_pass++; + } + } + } + + arc_warm = B_TRUE; + + arc_kmem_reap_now(); + + /* + * If we are still low on memory, shrink the ARC + * so that we have arc_shrink_min free space. + */ + free_memory = arc_available_memory(); + + int64_t to_free = + (arc_c >> arc_shrink_shift) - free_memory; + + if (to_free > 0 || manual_pressure != 0) { + + to_free = MAX(to_free, manual_pressure); + + arc_reduce_target_size(to_free); + + goto lock_and_sleep; + } + } else if (free_memory < (arc_c >> arc_no_grow_shift) && + aggsum_value(&arc_size) > + arc_c_min + SPA_MAXBLOCKSIZE) { + // relatively low memory and arc is above arc_c_min + arc_no_grow = B_TRUE; + growtime = curtime + SEC2NSEC(1); + goto lock_and_sleep; + } + + /* + * The abd vmem layer can see a large number of + * frees from the abd kmem cache layer, and unfortunately + * the abd vmem layer might end up fragmented as a result. + * + * Watch for this fragmentation and if it arises + * suppress ARC growth for ten minutes in hopes that + * abd activity driven by ARC replacement or further ARC + * shrinking lets the abd vmem layer defragment. + */ + + if (arc_no_grow != B_TRUE) { + /* + * The gap is between imported and inuse + * in the abd vmem layer + */ + + static hrtime_t when_gap_grew = 0; + static int64_t previous_gap = 0; + static int64_t previous_abd_size = 0; + + int64_t gap = abd_arena_empty_space(); + int64_t abd_size = abd_arena_total_size(); + + if (gap == 0) { + /* + * no abd vmem layer fragmentation + * so don't adjust arc_no_grow + */ + previous_gap = 0; + previous_abd_size = abd_size; + } else if (gap > 0 && gap == previous_gap && + abd_size == previous_abd_size) { + if (curtime < when_gap_grew + SEC2NSEC(600)) { + /* + * our abd arena is unchanged + * try up to ten minutes for kmem layer + * to free slabs to abd vmem layer + */ + arc_no_grow = B_TRUE; + growtime = curtime + + SEC2NSEC(arc_grow_retry); + previous_abd_size = abd_size; + } else { + /* + * ten minutes have expired with no + * good result, shrink the arc a little, + * no more than once every + * arc_grow_retry (5) seconds + */ + arc_no_grow = B_TRUE; + growtime = curtime + + SEC2NSEC(arc_grow_retry); + previous_abd_size = abd_size; + + const int64_t sb = + arc_c >> arc_shrink_shift; + if (arc_c_min + sb > arc_c) { + arc_reduce_target_size(sb); + goto lock_and_sleep; + } + } + } else if (gap > 0 && gap > previous_gap) { + /* + * kmem layer must have freed slabs + * but vmem layer is holding on because + * of fragmentation. Don't grow ARC + * for a minute. + */ + arc_no_grow = B_TRUE; + growtime = curtime + SEC2NSEC(arc_grow_retry); + previous_gap = gap; + when_gap_grew = curtime; + /* + * but if we're growing the abd + * as well as its gap, shrink + */ + if (abd_size > previous_abd_size) { + const int64_t sb = + arc_c >> arc_shrink_shift; + if (arc_c_min + sb > arc_c) + arc_reduce_target_size(sb); + } + previous_abd_size = abd_size; + } else if (gap > 0 && gap < previous_gap) { + /* + * vmem layer successfully freeing. + */ + if (curtime < when_gap_grew + SEC2NSEC(600)) { + arc_no_grow = B_TRUE; + growtime = curtime + + SEC2NSEC(arc_grow_retry); + } + previous_gap = gap; + previous_abd_size = abd_size; + } else { + previous_abd_size = abd_size; + } + } + + if (growtime > 0 && curtime >= growtime) { + if (arc_no_grow == B_TRUE) + dprintf("ZFS: arc growtime expired\n"); + growtime = 0; + arc_no_grow = B_FALSE; + } + +lock_and_sleep: + + arc_reclaim_in_loop = B_FALSE; + + mutex_enter(&arc_reclaim_lock); + + /* + * If d_adj is non-positive, we didn't evict anything, + * perhaps because nothing was evictable. Immediately + * running another pass is unlikely to be helpful. + */ + + if (aggsum_compare(&arc_size, arc_c) <= 0 || d_adj <= 0) { + /* + * We're either no longer overflowing, or we + * can't evict anything more, so we should wake + * up any threads before we go to sleep. + */ + cv_broadcast(&arc_reclaim_waiters_cv); + + /* + * Block until signaled, or after one second (we + * might need to perform arc_kmem_reap_now() + * even if we aren't being signalled) + */ + CALLB_CPR_SAFE_BEGIN(&cpr); + (void) cv_timedwait_hires(&arc_reclaim_thread_cv, + &arc_reclaim_lock, MSEC2NSEC(500), MSEC2NSEC(1), 0); + CALLB_CPR_SAFE_END(&cpr, &arc_reclaim_lock); + + } else if (d_adj >= SPA_MAXBLOCKSIZE * 3) { + // we evicted plenty of buffers, so let's wake up + // all the waiters rather than having them stall + cv_broadcast(&arc_reclaim_waiters_cv); + } else { + // we evicted some buffers but are still overflowing, + // so wake up only one waiter + cv_signal(&arc_reclaim_waiters_cv); + } + } + + arc_reclaim_thread_exit = B_FALSE; + cv_broadcast(&arc_reclaim_thread_cv); + CALLB_CPR_EXIT(&cpr); /* drops arc_reclaim_lock */ + thread_exit(); +} + +/* This is called before arc is initialized, and threads are not running */ +void +arc_lowmem_init(void) +{ + /* + * The ARC tries to keep at least this much memory available for the + * system. This gives the ARC time to shrink in response to memory + * pressure, before running completely out of memory and invoking the + * direct-reclaim ARC shrinker. + * + * arc_wait_for_eviction() waits for half of arc_sys_free. Bump this up + * to 3x to ensure we're above it. + */ + VERIFY3U(arc_all_memory(), >, 0); + arc_sys_free = arc_all_memory() / 128LL; + +} + +/* This is called after arc is initialized, and thread are running */ +void +arc_os_init(void) +{ + mutex_init(&arc_reclaim_lock, NULL, MUTEX_DEFAULT, NULL); + cv_init(&arc_reclaim_thread_cv, NULL, CV_DEFAULT, NULL); + cv_init(&arc_reclaim_waiters_cv, NULL, CV_DEFAULT, NULL); + + arc_reclaim_thread_exit = B_FALSE; + + (void) thread_create(NULL, 0, arc_reclaim_thread, NULL, 0, &p0, + TS_RUN, minclsyspri); + + arc_warm = B_FALSE; + +} + +void +arc_lowmem_fini(void) +{ +} + +void +arc_os_fini(void) +{ + mutex_enter(&arc_reclaim_lock); + arc_reclaim_thread_exit = B_TRUE; + /* + * The reclaim thread will set arc_reclaim_thread_exit back to + * B_FALSE when it is finished exiting; we're waiting for that. + */ + while (arc_reclaim_thread_exit) { + cv_signal(&arc_reclaim_thread_cv); + cv_wait(&arc_reclaim_thread_cv, &arc_reclaim_lock); + } + mutex_exit(&arc_reclaim_lock); + + mutex_destroy(&arc_reclaim_lock); + cv_destroy(&arc_reclaim_thread_cv); + cv_destroy(&arc_reclaim_waiters_cv); +} + +/* + * Uses ARC static variables in logic. + */ +#define arc_meta_limit ARCSTAT(arcstat_meta_limit) /* max size for metadata */ +/* max size for dnodes */ +#define arc_dnode_size_limit ARCSTAT(arcstat_dnode_limit) +#define arc_meta_min ARCSTAT(arcstat_meta_min) /* min size for metadata */ +#define arc_meta_max ARCSTAT(arcstat_meta_max) /* max size of metadata */ + +/* So close, they made arc_min_prefetch_ms be static, but no others */ + +int +arc_kstat_update_osx(kstat_t *ksp, int rw) +{ + osx_kstat_t *ks = ksp->ks_data; + boolean_t do_update = B_FALSE; + + if (rw == KSTAT_WRITE) { + + /* Did we change the value ? */ + if (ks->arc_zfs_arc_max.value.ui64 != zfs_arc_max) { + /* Assign new value */ + zfs_arc_max = ks->arc_zfs_arc_max.value.ui64; + do_update = B_TRUE; + } + + if (ks->arc_zfs_arc_min.value.ui64 != zfs_arc_min) { + zfs_arc_min = ks->arc_zfs_arc_min.value.ui64; + do_update = B_TRUE; + } + + if (ks->arc_zfs_arc_meta_limit.value.ui64 != + zfs_arc_meta_limit) { + zfs_arc_meta_limit = + ks->arc_zfs_arc_meta_limit.value.ui64; + do_update = B_TRUE; + } + + if (ks->arc_zfs_arc_meta_min.value.ui64 != zfs_arc_meta_min) { + zfs_arc_meta_min = ks->arc_zfs_arc_meta_min.value.ui64; + do_update = B_TRUE; + } + + if (zfs_arc_grow_retry != + ks->arc_zfs_arc_grow_retry.value.ui64) { + zfs_arc_grow_retry = + ks->arc_zfs_arc_grow_retry.value.ui64; + do_update = B_TRUE; + } + if (zfs_arc_shrink_shift != + ks->arc_zfs_arc_shrink_shift.value.ui64) { + zfs_arc_shrink_shift = + ks->arc_zfs_arc_shrink_shift.value.ui64; + do_update = B_TRUE; + } + if (zfs_arc_p_min_shift != + ks->arc_zfs_arc_p_min_shift.value.ui64) { + zfs_arc_p_min_shift = + ks->arc_zfs_arc_p_min_shift.value.ui64; + do_update = B_TRUE; + } + if (zfs_arc_average_blocksize != + ks->arc_zfs_arc_average_blocksize.value.ui64) { + zfs_arc_average_blocksize = + ks->arc_zfs_arc_average_blocksize.value.ui64; + do_update = B_TRUE; + } + if (zfs_arc_lotsfree_percent != + ks->zfs_arc_lotsfree_percent.value.i64) { + zfs_arc_lotsfree_percent = + ks->zfs_arc_lotsfree_percent.value.i64; + do_update = B_TRUE; + } + if (zfs_arc_sys_free != + ks->zfs_arc_sys_free.value.i64) { + zfs_arc_sys_free = + ks->zfs_arc_sys_free.value.i64; + do_update = B_TRUE; + } + + if (do_update) + arc_tuning_update(B_TRUE); + } else { + + ks->arc_zfs_arc_max.value.ui64 = zfs_arc_max; + ks->arc_zfs_arc_min.value.ui64 = zfs_arc_min; + + ks->arc_zfs_arc_meta_limit.value.ui64 = zfs_arc_meta_limit; + ks->arc_zfs_arc_meta_min.value.ui64 = zfs_arc_meta_min; + + ks->arc_zfs_arc_grow_retry.value.ui64 = + zfs_arc_grow_retry ? zfs_arc_grow_retry : arc_grow_retry; + ks->arc_zfs_arc_shrink_shift.value.ui64 = zfs_arc_shrink_shift; + ks->arc_zfs_arc_p_min_shift.value.ui64 = zfs_arc_p_min_shift; + ks->arc_zfs_arc_average_blocksize.value.ui64 = + zfs_arc_average_blocksize; + ks->zfs_arc_lotsfree_percent.value.i64 = + zfs_arc_lotsfree_percent; + ks->zfs_arc_sys_free.value.i64 = + zfs_arc_sys_free; + } + return (0); +} + +/* + * Helper function for arc_prune_async() it is responsible for safely + * handling the execution of a registered arc_prune_func_t. + */ +static void +arc_prune_task(void *ptr) +{ + arc_prune_t *ap = (arc_prune_t *)ptr; + arc_prune_func_t *func = ap->p_pfunc; + + if (func != NULL) + func(ap->p_adjust, ap->p_private); + + zfs_refcount_remove(&ap->p_refcnt, func); +} + +/* + * Notify registered consumers they must drop holds on a portion of the ARC + * buffered they reference. This provides a mechanism to ensure the ARC can + * honor the arc_meta_limit and reclaim otherwise pinned ARC buffers. This + * is analogous to dnlc_reduce_cache() but more generic. + * + * This operation is performed asynchronously so it may be safely called + * in the context of the arc_reclaim_thread(). A reference is taken here + * for each registered arc_prune_t and the arc_prune_task() is responsible + * for releasing it once the registered arc_prune_func_t has completed. + */ +void +arc_prune_async(int64_t adjust) +{ + arc_prune_t *ap; + + mutex_enter(&arc_prune_mtx); + for (ap = list_head(&arc_prune_list); ap != NULL; + ap = list_next(&arc_prune_list, ap)) { + + if (zfs_refcount_count(&ap->p_refcnt) >= 2) + continue; + + zfs_refcount_add(&ap->p_refcnt, ap->p_pfunc); + ap->p_adjust = adjust; + if (taskq_dispatch(arc_prune_taskq, arc_prune_task, + ap, TQ_SLEEP) == TASKQID_INVALID) { + zfs_refcount_remove(&ap->p_refcnt, ap->p_pfunc); + continue; + } + ARCSTAT_BUMP(arcstat_prune); + } + mutex_exit(&arc_prune_mtx); +} + +#else /* from above ifdef _KERNEL */ + +int64_t +arc_available_memory(void) +{ + return (arc_free_memory() - arc_sys_free); +} + +int +arc_memory_throttle(spa_t *spa, uint64_t reserve, uint64_t txg) +{ + return (0); +} + +uint64_t +arc_all_memory(void) +{ + return (ptob(physmem) / 2); +} + +uint64_t +arc_free_memory(void) +{ + int64_t avail; + + avail = spl_free_wrapper(); + return (avail >= 0LL ? avail : 0LL); +} +#endif /* KERNEL */ + +void +arc_register_hotplug(void) +{ +} + +void +arc_unregister_hotplug(void) +{ +} diff --git a/module/os/macos/zfs/ldi_iokit.cpp b/module/os/macos/zfs/ldi_iokit.cpp new file mode 100644 index 000000000000..0b51da0698a8 --- /dev/null +++ b/module/os/macos/zfs/ldi_iokit.cpp @@ -0,0 +1,2015 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ +/* + * Copyright (c) 2015, Evan Susarret. All rights reserved. + */ +/* + * Portions of this document are copyright Oracle and Joyent. + * OS X implementation of ldi_ named functions for ZFS written by + * Evan Susarret in 2015. + */ + +/* Quiet some noisy build warnings */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winconsistent-missing-override" +#pragma GCC diagnostic ignored "-Wdeprecated-register" + +/* + * Apple IOKit (c++) + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * ZFS internal + */ +#include + +/* + * LDI Includes + */ +#include + +/* Debug prints */ + +/* Attach created IOService objects to the IORegistry under ZFS. */ +// #define LDI_IOREGISTRY_ATTACH + +/* + * Globals + */ +static IOService *ldi_zfs_handle; + +/* Exposed to c callers */ +extern "C" { + +struct _handle_iokit { + IOMedia *media; + IOService *client; +}; /* 16b */ + +struct _handle_notifier { + IONotifier *obj; +}; /* 8b */ + +#define LH_MEDIA(lhp) lhp->lh_tsd.iokit_tsd->media +#define LH_CLIENT(lhp) lhp->lh_tsd.iokit_tsd->client +#define LH_NOTIFIER(lhp) lhp->lh_notifier->obj + +void +handle_free_iokit(struct ldi_handle *lhp) { + if (!lhp) { + dprintf("%s missing lhp\n", __func__); + return; + } + + if (!lhp->lh_tsd.iokit_tsd) { + dprintf("%s missing iokit_tsd\n", __func__); + return; + } + + /* Free IOService client */ + if (handle_free_ioservice(lhp) != 0) { + dprintf("%s lhp %p client %s\n", + __func__, lhp, "couldn't be removed"); + } + + kmem_free(lhp->lh_tsd.iokit_tsd, sizeof (struct _handle_iokit)); + lhp->lh_tsd.iokit_tsd = 0; +} + +/* Returns handle with lock still held */ +struct ldi_handle * +handle_alloc_iokit(dev_t device, int fmode) +{ + struct ldi_handle *lhp, *retlhp; + + /* Search for existing handle */ + if ((retlhp = handle_find(device, fmode, B_TRUE)) != NULL) { + dprintf("%s found handle before alloc\n", __func__); + return (retlhp); + } + + /* Allocate an LDI IOKit handle */ + if ((lhp = handle_alloc_common(LDI_TYPE_IOKIT, device, + fmode)) == NULL) { + dprintf("%s couldn't allocate handle\n", __func__); + return (NULL); + } + + /* Allocate and clear type-specific device data */ + lhp->lh_tsd.iokit_tsd = (struct _handle_iokit *)kmem_alloc( + sizeof (struct _handle_iokit), KM_SLEEP); + LH_MEDIA(lhp) = 0; + LH_CLIENT(lhp) = 0; + + /* Allocate an IOService client for open/close */ + if (handle_alloc_ioservice(lhp) != 0) { + dprintf("%s couldn't allocate IOService client\n", __func__); + handle_release(lhp); + return (NULL); + } + + /* Add the handle to the list, or return match */ + if ((retlhp = handle_add(lhp)) == NULL) { + dprintf("%s handle_add failed\n", __func__); + handle_release(lhp); + return (NULL); + } + + /* Check if new or found handle was returned */ + if (retlhp != lhp) { + dprintf("%s found handle after alloc\n", __func__); + handle_release(lhp); + lhp = 0; + } + + return (retlhp); +} + +int +handle_free_ioservice(struct ldi_handle *lhp) +{ + /* Validate handle pointer */ + ASSERT3U(lhp, !=, NULL); +#ifdef DEBUG + if (!lhp) { + dprintf("%s missing handle\n", __func__); + return (EINVAL); + } + if (!LH_CLIENT(lhp)) { + dprintf("%s missing client\n", __func__); + return (ENODEV); + } +#endif + +#ifdef LDI_IOREGISTRY_ATTACH + /* Detach client from ZFS in IORegistry */ + LH_CLIENT(lhp)->detach(ldi_zfs_handle); +#endif + + LH_CLIENT(lhp)->stop(ldi_zfs_handle); + LH_CLIENT(lhp)->release(); + LH_CLIENT(lhp) = 0; + + return (0); +} + +int +handle_alloc_ioservice(struct ldi_handle *lhp) +{ + IOService *client; + + /* Validate handle pointer */ + ASSERT3U(lhp, !=, NULL); + if (lhp == NULL) { + dprintf("%s missing handle\n", __func__); + return (EINVAL); + } + + /* Allocate and init an IOService client for open/close */ + if ((client = new IOService) == NULL) { + dprintf("%s couldn't allocate new IOService\n", __func__); + return (ENOMEM); + } + if (client->init(0) != true) { + dprintf("%s IOService init failed\n", __func__); + client->release(); + return (ENOMEM); + } + +#ifdef LDI_IOREGISTRY_ATTACH + /* Attach client to ZFS in IORegistry */ + if (client->attach(ldi_zfs_handle) != true) { + dprintf("%s IOService attach failed\n", __func__); + client->release(); + return (ENOMEM); + } +#endif + + /* Start service */ + if (client->start(ldi_zfs_handle) != true) { + dprintf("%s IOService attach failed\n", __func__); + /* Detach client from ZFS in IORegistry */ +#ifdef LDI_IOREGISTRY_ATTACH + client->detach(ldi_zfs_handle); +#endif + client->release(); + return (ENOMEM); + } + + LH_CLIENT(lhp) = client; + return (0); +} + +/* Set status to Offline and post event */ +static bool +handle_media_terminate_cb(void* target, void* refCon, + IOService* newService, IONotifier* notifier) +{ + struct ldi_handle *lhp = (struct ldi_handle *)refCon; + +#ifdef DEBUG + if (!lhp) { + dprintf("%s missing refCon ldi_handle\n", __func__); + return (false); + } +#endif + + /* Take hold on handle to prevent removal */ + handle_hold(lhp); + + dprintf("%s setting lhp %p to Offline status\n", __func__, lhp); + if (handle_status_change(lhp, LDI_STATUS_OFFLINE) != 0) { + dprintf("%s handle_status_change failed\n", __func__); + handle_release(lhp); + return (false); + } + + handle_release(lhp); + return (true); +} + +int +handle_close_iokit(struct ldi_handle *lhp) +{ +#ifdef DEBUG + ASSERT3U(lhp, !=, NULL); + ASSERT3U(lhp->lh_type, ==, LDI_TYPE_IOKIT); + ASSERT3U(lhp->lh_status, ==, LDI_STATUS_CLOSING); + + /* Validate IOMedia and IOService client */ + if (!OSDynamicCast(IOMedia, LH_MEDIA(lhp)) || + !OSDynamicCast(IOService, LH_CLIENT(lhp))) { + dprintf("%s invalid IOMedia or client\n", __func__); + return (ENODEV); + } +#endif /* DEBUG */ + + LH_MEDIA(lhp)->close(LH_CLIENT(lhp)); + LH_MEDIA(lhp) = 0; + return (0); +} + +static int +handle_open_iokit(struct ldi_handle *lhp, IOMedia *media) +{ +#ifdef DEBUG + ASSERT3U(lhp, !=, NULL); + ASSERT3U(media, !=, NULL); + ASSERT3U(lhp->lh_type, ==, LDI_TYPE_IOKIT); + ASSERT3U(lhp->lh_status, ==, LDI_STATUS_OPENING); + + /* Validate IOMedia and IOService client */ + if (!OSDynamicCast(IOMedia, media) || + !OSDynamicCast(IOService, LH_CLIENT(lhp))) { + dprintf("%s invalid IOMedia or client\n", __func__); + return (ENODEV); + } +#endif /* DEBUG */ + /* Retain until open or error */ + media->retain(); + + /* + * If read/write mode is requested, check that the + * device is actually writeable. + */ + if (lhp->lh_fmode & FWRITE && media->isWritable() == false) { + dprintf("%s read-write requested on %s\n", + __func__, "read-only IOMedia"); + media->release(); + return (EPERM); + } + + /* Call open with the IOService client handle */ + if (media->IOMedia::open(LH_CLIENT(lhp), 0, + (lhp->lh_fmode & FWRITE ? kIOStorageAccessReaderWriter : + kIOStorageAccessReader)) == false) { + dprintf("%s IOMedia->open failed\n", __func__); + media->release(); + return (EIO); + } + media->release(); + + /* Assign IOMedia device */ + LH_MEDIA(lhp) = media; + return (0); +} + +int +handle_get_size_iokit(struct ldi_handle *lhp, uint64_t *dev_size) +{ + if (!lhp || !dev_size) { + dprintf("%s missing lhp or dev_size\n", __func__); + return (EINVAL); + } + +#ifdef DEBUG + /* Validate IOMedia */ + if (!OSDynamicCast(IOMedia, LH_MEDIA(lhp))) { + dprintf("%s no IOMedia\n", __func__); + return (ENODEV); + } +#endif + + *dev_size = LH_MEDIA(lhp)->getSize(); + if (*dev_size == 0) { + dprintf("%s %s\n", __func__, + "IOMedia getSize returned 0"); + return (EINVAL); + } + + return (0); +} + +int +handle_get_dev_path_iokit(struct ldi_handle *lhp, + char *path, int len) +{ + int retlen = len; + + if (!lhp || !path || len == 0) { + dprintf("%s missing argument\n", __func__); + return (EINVAL); + } + +#ifdef DEBUG + /* Validate IOMedia */ + if (!OSDynamicCast(IOMedia, LH_MEDIA(lhp))) { + dprintf("%s no IOMedia\n", __func__); + return (ENODEV); + } +#endif + + if (LH_MEDIA(lhp)->getPath(path, &retlen, gIODTPlane) == false) { + dprintf("%s getPath failed\n", __func__); + return (EIO); + } + +dprintf("%s got path [%s]\n", __func__, path); + return (0); +} + +int handle_get_bootinfo_iokit(struct ldi_handle *lhp, + struct io_bootinfo *bootinfo) +{ + int error = 0; + + if (!lhp || !bootinfo) { + dprintf("%s missing argument\n", __func__); +printf("%s missing argument\n", __func__); + return (EINVAL); + } + + if ((error = handle_get_size_iokit(lhp, + &bootinfo->dev_size)) != 0 || + (error = handle_get_dev_path_iokit(lhp, bootinfo->dev_path, + sizeof (bootinfo->dev_path))) != 0) { + dprintf("%s get size or dev_path error %d\n", + __func__, error); + } + + return (error); +} + +int +handle_sync_iokit(struct ldi_handle *lhp) +{ +#ifdef DEBUG + /* Validate IOMedia and client */ + if (!OSDynamicCast(IOMedia, LH_MEDIA(lhp)) || + !OSDynamicCast(IOService, LH_CLIENT(lhp))) { + dprintf("%s invalid IOMedia or client\n", __func__); + return (ENODEV); + } +#endif + +#if defined(MAC_OS_X_VERSION_10_11) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11) + /* Issue device sync */ + if (LH_MEDIA(lhp)->synchronize(LH_CLIENT(lhp), 0, 0, kIOStorageSynchronizeOptionBarrier) != + kIOReturnSuccess) { + printf("%s %s\n", __func__, + "IOMedia synchronizeCache (with write barrier) failed\n"); + if (LH_MEDIA(lhp)->synchronize(LH_CLIENT(lhp), 0, 0, 0) != + kIOReturnSuccess) { + printf("%s %s\n", __func__, + "IOMedia synchronizeCache (standard) failed\n"); + return (ENOTSUP); + } + } +#else + /* Issue device sync */ + if (LH_MEDIA(lhp)->synchronizeCache(LH_CLIENT(lhp)) != + kIOReturnSuccess) { + printf("%s %s\n", __func__, + "IOMedia synchronizeCache failed"); + return (ENOTSUP); + } +#endif + + /* Success */ + return (0); +} + +static dev_t +dev_from_media(IOMedia *media) +{ + OSObject *property; + OSNumber *number; + uint32_t major, minor; + dev_t device = 0; + + /* Validate media */ + if (!media || !OSDynamicCast(IOMedia, media)) { + dprintf("%s no device\n", __func__); + return (0); + } + media->retain(); + + /* Get device major */ + if (NULL == (property = media->getProperty(kIOBSDMajorKey, + gIOServicePlane, kIORegistryIterateRecursively)) || + NULL == (number = OSDynamicCast(OSNumber, property))) { + dprintf("%s couldn't get BSD major\n", __func__); + media->release(); + return (0); + } + major = number->unsigned32BitValue(); + number = NULL; + property = NULL; + + /* Get device minor */ + if (NULL == (property = media->getProperty(kIOBSDMinorKey, + gIOServicePlane, kIORegistryIterateRecursively)) || + NULL == (number = OSDynamicCast(OSNumber, property))) { + dprintf("%s couldn't get BSD major\n", __func__); + media->release(); + return (0); + } + minor = number->unsigned32BitValue(); + number = NULL; + property = NULL; + + /* Cleanup */ + media->release(); + media = NULL; + + device = makedev(major, minor); + + /* Return 0 or valid dev_t */ + return (device); +} + +/* Returns NULL or dictionary with a retain count */ +static OSDictionary * +media_matchdict_from_dev(dev_t device) +{ + OSDictionary *matchDict; + OSNumber *majorNum, *minorNum; + + /* Validate dev_t */ + if (device == 0) { + dprintf("%s no dev_t provided\n", __func__); + return (NULL); + } + + /* Allocate OSNumbers for BSD major and minor (32-bit) */ + if (NULL == (majorNum = OSNumber::withNumber(major(device), 32)) || + NULL == (minorNum = OSNumber::withNumber(minor(device), 32))) { + dprintf("%s couldn't alloc major/minor as OSNumber\n", + __func__); + if (majorNum) { + majorNum->release(); + } + return (NULL); + } + + /* Match on IOMedia */ + if (NULL == (matchDict = IOService::serviceMatching("IOMedia")) || + !(matchDict->setObject(kIOBSDMajorKey, majorNum)) || + !(matchDict->setObject(kIOBSDMinorKey, minorNum))) { + dprintf("%s couldn't get matching dictionary\n", __func__); + if (matchDict) { + matchDict->release(); + } + majorNum->release(); + minorNum->release(); + return (NULL); + } + majorNum->release(); + minorNum->release(); + + /* Return NULL or valid OSDictionary with retain count */ + return (matchDict); +} + +/* Returns NULL or dictionary with a retain count */ +/* + * media_matchdict_from_path + * translate from paths of the form /dev/diskNsN + * or /private/var/run/disk/by-id/media- to a matching + * dictionary. + */ +static OSDictionary * +media_matchdict_from_path(const char *path) +{ + OSDictionary *matchDict = 0; + OSString *bsdName = NULL; + OSString *uuid = NULL; + const char *substr = 0; + bool ret; + + /* Validate path */ + if (path == 0 || strlen(path) <= 1) { + dprintf("%s no path provided\n", __func__); + return (NULL); + } + /* Translate /dev/diskN and InvariantDisks paths */ + if (strncmp(path, "/dev/", 5) != 0 && + strncmp(path, "/var/run/disk/by-id/", 20) != 0 && + strncmp(path, "/private/var/run/disk/by-id/", 28) != 0) { + dprintf("%s Unrecognized path %s\n", __func__, path); + return (NULL); + } + + /* Validate path and alloc bsdName */ + if (strncmp(path, "/dev/", 5) == 0) { + + /* substr starts after '/dev/' */ + substr = path + 5; + /* Get diskN from /dev/diskN or /dev/rdiskN */ + if (strncmp(substr, "disk", 4) == 0) { + bsdName = OSString::withCString(substr); + } else if (strncmp(substr, "rdisk", 5) == 0) { + bsdName = OSString::withCString(substr + 1); + } + } else if (strncmp(path, "/var/run/disk/by-id/", 20) == 0 || + strncmp(path, "/private/var/run/disk/by-id/", 28) == 0) { + /* InvariantDisks paths */ + + /* substr starts after '/by-id/' */ + substr = path + 20; + if (strncmp(path, "/private", 8) == 0) substr += 8; + + /* Handle media UUID, skip volume UUID or device GUID */ + if (strncmp(substr, "media-", 6) == 0) { + /* Lookup IOMedia with UUID */ + uuid = OSString::withCString(substr+strlen("media-")); + } else if (strncmp(substr, "volume-", 7) == 0) { + /* + * volume-UUID is specified by DiskArbitration + * when a Filesystem bundle is able to probe + * the media and retrieve/generate a UUID for + * it's contents. + * So while we could use this and have zfs.util + * probe for vdev GUID (and pool GUID) and + * generate a UUID, we would need to do the same + * here to find the disk, possibly probing + * devices to get the vdev GUID in the process. + */ + dprintf("%s Unsupported volume-UUID path %s\n", + __func__, path); + } else if (strncmp(substr, "device-", 7) == 0) { + /* Lookup IOMedia with device GUID */ + /* + * XXX Not sure when this is used, no devices + * seem to be presented this way. + */ + dprintf("%s Unsupported device-GUID path %s\n", + __func__, path); + } else { + dprintf("%s unrecognized path %s\n", __func__, path); + } + /* by-path and by-serial are handled separately */ + } + + if (!bsdName && !uuid) { + dprintf("%s Invalid path %s\n", __func__, path); + return (NULL); + } + + /* Match on IOMedia by BSD disk name */ + matchDict = IOService::serviceMatching("IOMedia"); + if (!matchDict) { + dprintf("%s couldn't get matching dictionary\n", __func__); + if (bsdName) bsdName->release(); + if (uuid) uuid->release(); + return (NULL); + } + if (bsdName) { + ret = matchDict->setObject(kIOBSDNameKey, bsdName); + bsdName->release(); + + if (!ret) { + dprintf("%s couldn't setup bsd name matching" + " dictionary\n", __func__); + matchDict->release(); + matchDict = 0; + } + if (uuid) uuid->release(); + } else if (uuid) { + if (matchDict->setObject(kIOMediaUUIDKey, uuid) == false) { + dprintf("%s couldn't setup UUID matching" + " dictionary\n", __func__); + uuid->release(); + matchDict->release(); + matchDict = 0; + } + } else { + dprintf("%s missing matching property\n", __func__); + matchDict->release(); + matchDict = 0; + } + + /* Return NULL or valid OSDictionary with retain count */ + return (matchDict); +} + +/* Returns NULL or matched IOMedia with a retain count */ +static IOMedia * +media_from_matchdict(OSDictionary *matchDict) +{ + OSIterator *iter = 0; + OSObject *obj = 0; + IOMedia *media = 0; + + if (!matchDict) { + dprintf("%s missing matching dictionary\n", __func__); + return (NULL); + } + + /* + * We could instead use copyMatchingService, since + * there should only be one match. + */ + iter = IOService::getMatchingServices(matchDict); + if (!iter) { + dprintf("%s No iterator from getMatchingServices\n", + __func__); + return (NULL); + } + + /* Get first object from iterator */ + while ((obj = iter->getNextObject()) != NULL) { + if ((media = OSDynamicCast(IOMedia, obj)) == NULL) { + obj = 0; + continue; + } + if (media->isFormatted() == false) { + obj = 0; + media = 0; + continue; + } + + media->retain(); + break; + } + + if (!media) { + dprintf("%s no match found\n", __func__); + iter->release(); + return (NULL); + } + +#ifdef DEBUG + /* Report if there were additional matches */ + if (iter->getNextObject() != NULL) { + dprintf("%s Had more potential matches\n", __func__); + } +#endif + iter->release(); + iter = 0; + + /* Return valid IOMedia with retain count */ + return (media); +} + +/* + * media_from_dev is intended to be called by ldi_open_by_name + * and ldi_open_by_dev with a dev_t, and returns NULL or an IOMedia + * device with a retain count that should be released on open. + */ +static IOMedia * +media_from_dev(dev_t device = 0) +{ + IOMedia *media; + OSDictionary *matchDict; + + /* Get matchDict, will need to be released */ + matchDict = media_matchdict_from_dev(device); + if (!matchDict) { + dprintf("%s couldn't get matching dictionary\n", __func__); + return (NULL); + } + + /* Get first matching IOMedia */ + media = media_from_matchdict(matchDict); + matchDict->release(); + matchDict = 0; + + if (!media) { + dprintf("%s no IOMedia found for dev_t %d\n", __func__, + device); + } + + /* Return NULL or valid media with retain count */ + return (media); +} + +/* + * media_from_device_path + * + * translate /private/var/run/disk/by-path/ to an IOMedia + * handle. The remainder of the path should be a valid + * path in the IORegistry IODTPlane device tree. + */ +static IOMedia * +media_from_device_path(const char *path = 0) +{ + IORegistryEntry *entry = 0; + IOMedia *media = 0; + OSString *osstr; + const char *string, *dash; + + /* Must be /var/run/disk/by-path/, but may have /private prefix */ + if (!path || path[0] == 0 || + (strncmp(path, "/var/run/disk/by-path/", 22) != 0 && + strncmp(path, "/private/var/run/disk/by-path/", 30) != 0)) { + dprintf("%s invalid path [%s]\n", __func__, + (path && path[0] != '\0' ? path : "")); + return (NULL); + } + + /* We need the leading slash in the string, so trim 21 or 29 */ + if (strncmp(path, "/private", 8) == 0) { + osstr = OSString::withCString(path+29); + } else { + osstr = OSString::withCString(path+21); + } + if (!osstr) { + dprintf("%s couldn't get string from path\n", __func__); + return (NULL); + } + + string = osstr->getCStringNoCopy(); + ASSERT(string); + + /* Convert dashes to slashes */ + while ((dash = strchr(string, '-')) != NULL) { + osstr->setChar('/', dash - string); + } + dprintf("%s string [%s]\n", __func__, string); + + entry = IORegistryEntry::fromPath(string, gIODTPlane); + string = 0; + osstr->release(); + osstr = 0; + + if (!entry) { + dprintf("%s IORegistryEntry::fromPath failed\n", __func__); + return (NULL); + } + + if ((media = OSDynamicCast(IOMedia, entry)) == NULL) { + entry->release(); + return (0); + } + + /* Leave a retain count on the media */ + return (media); +} + +/* + * media_from_serial + * + * translate /private/var/run/disk/by-serial/model-serial[:location] + * to an IOMedia handle. The path format is determined by + * InvariantDisks logic in IDSerialLinker.cpp. + */ +static IOMedia * +media_from_serial(const char *path = 0) +{ + IORegistryEntry *entry = 0; + IOMedia *media = 0; + OSDictionary *matching = 0; + OSDictionary *deviceCharacteristics = 0; + OSIterator *iter = 0; + OSString *osstr = 0; + OSString *model = 0; + OSString *serial = 0; + OSNumber *bsdUnit = 0; + OSObject *property = 0; + OSObject *propDict = 0; + OSObject *obj = 0; + const char *substr = 0; + const char *sep1 = 0, *sep2 = 0; + const char *string = 0, *space = 0; + const char *location = 0, *entryLocation = 0; + int newlen = 0, soff = 0; + bool matched = false; + + /* Must be /var/run/disk/by-serial/, but may have /private prefix */ + if (!path || path[0] == 0 || + (strncmp(path, "/var/run/disk/by-serial/", 24) != 0 && + strncmp(path, "/private/var/run/disk/by-serial/", 32) != 0)) { + dprintf("%s invalid path [%s]\n", __func__, + (path && path[0] != '\0' ? path : "")); + return (NULL); + } + + /* substr starts after '/by-serial/' */ + substr = path + 24; + if (strncmp(path, "/private", 8) == 0) substr += 8; + + /* + * For each whole-disk IOMedia: + * Search parents for deviceCharacteristics, or skip. + * Check for Model and Serial Number properties, or skip. + * Trim trailing space and swap underscores within string. + * If "model-serial" matches path so far: + * Match whole-disk IOMedia if no slice specified. + * Or get child IOMedia with matching Location property. + */ + + sep1 = strchr(substr, '-'); + sep2 = strrchr(substr, ':'); + if (sep1 == 0) { + dprintf("%s invalid by-serial path [%s]\n", __func__, substr); + return (NULL); + } + if (sep2 == 0) { + dprintf("%s no slice, whole disk [%s]\n", __func__, substr); + sep2 = substr + (strlen(substr)); + } + + if ((matching = IOService::serviceMatching("IOMedia")) == NULL) { + dprintf("%s couldn't get matching dictionary\n", __func__); + return (NULL); + } + + if ((matching->setObject(kIOMediaWholeKey, kOSBooleanTrue) == false) || + (iter = IOService::getMatchingServices(matching)) == NULL) { + dprintf("%s couldn't get IOMedia iterator\n", __func__); + matching->release(); + return (NULL); + } + matching->release(); + matching = 0; + + while ((obj = iter->getNextObject()) != NULL) { + if ((entry = OSDynamicCast(IORegistryEntry, obj)) == NULL || + (media = OSDynamicCast(IOMedia, entry)) == NULL || + media->isFormatted() == false) { + // media->isWhole() == false) { + continue; + } + + propDict = media->getProperty( + kIOPropertyDeviceCharacteristicsKey, gIOServicePlane, + (kIORegistryIterateRecursively | + kIORegistryIterateParents)); + if ((deviceCharacteristics = OSDynamicCast(OSDictionary, + propDict)) == NULL) { + dprintf("%s no device characteristics, skipping\n", + __func__); + continue; + } + + /* + * Get each property, cast as OSString, then copy + * to a new OSString. + */ + if ((property = deviceCharacteristics->getObject( + kIOPropertyProductNameKey)) == NULL || + (osstr = OSDynamicCast(OSString, property)) == NULL || + (model = OSString::withString(osstr)) == NULL) { + dprintf("%s no product name, skipping\n", __func__); + continue; + } + if ((property = deviceCharacteristics->getObject( + kIOPropertyProductSerialNumberKey)) == NULL || + (osstr = OSDynamicCast(OSString, property)) == NULL || + (serial = OSString::withString(osstr)) == NULL) { + dprintf("%s no serial number, skipping\n", __func__); + model->release(); + model = 0; + continue; + } + + string = model->getCStringNoCopy(); + if (!string) { + model->release(); + model = 0; + serial->release(); + serial = 0; + continue; + } + /* Trim trailing whitespace */ + for (newlen = strlen(string); newlen > 0; newlen--) { + if (string[newlen-1] != ' ') { + model->setChar('\0', newlen); + break; + } + } + + /* + * sep1 is the location of the first '-' in the path. + * even if there is a '-' in the model name, we can skip + * media with model names shorter than that. + */ + if (newlen == 0 || + (newlen < (sep1 - substr)) || + (substr[newlen] != '-')) { + model->release(); + model = 0; + serial->release(); + serial = 0; + continue; + } + + /* Convert spaces to underscores */ + while ((space = strchr(string, ' ')) != NULL) { + model->setChar('_', space - string); + } + + /* Compare the model string with the path */ + if (strncmp(substr, string, newlen) != 0) { + model->release(); + model = 0; + serial->release(); + serial = 0; + continue; + } + dprintf("%s model string matched [%s]\n", + __func__, model->getCStringNoCopy()); + model->release(); + model = 0; + + soff = newlen + 1; + + string = serial->getCStringNoCopy(); + if (!string) { + serial->release(); + serial = 0; + continue; + } + /* Trim trailing whitespace */ + for (newlen = strlen(string); newlen > 0; newlen--) { + if (string[newlen-1] != ' ') { + serial->setChar('\0', newlen); + break; + } + } + /* + * sep2 is the location of the last ':' in the path, or + * the end of the string if there is none. + * even if there is a ':' in the serial number, we can skip + * media with serial number strings shorter than that. + */ + if (newlen == 0 || + (newlen < (sep2 - sep1 - 1)) || + (substr[soff+newlen] != '\0' && + substr[soff+newlen] != ':')) { + serial->release(); + serial = 0; + continue; + } + + /* Convert spaces to underscores */ + while ((space = strchr(string, ' ')) != NULL) { + serial->setChar('_', space - string); + } + + /* Compare the serial string with the path */ + if (strncmp(substr+soff, string, newlen) != 0) { + serial->release(); + serial = 0; + continue; + } + dprintf("%s serial string matched [%s]\n", + __func__, serial->getCStringNoCopy()); + serial->release(); + serial = 0; + + /* + * Still need to get the slice - the component + * after an optional ':' at the end of the + * string, by searching for IOMedia with that + * location string below the whole-disk IOMedia. + */ + /* Set new location of ':' */ + sep2 = substr + (soff + newlen); + /* Found match */ + matched = true; + media->retain(); + break; + } + iter->release(); + iter = 0; + + if (!matched || !media) { + dprintf("%s no matching devices found\n", __func__); + return (NULL); + } + + /* Whole disk path will not end with ':' */ + if (sep2[0] != ':') { + dprintf("%s Found whole disk [%s]\n", __func__, path); + /* Leave a retain count on the media */ + return (media); + } + + /* Remainder of string is location */ + location = sep2 + 1; + dprintf("%s location string [%s]\n", __func__, location); + + if ((bsdUnit = OSDynamicCast(OSNumber, + media->getProperty(kIOBSDUnitKey))) == NULL) { + dprintf("%s couldn't get BSD unit number\n", __func__); + media->release(); + return (NULL); + } + if ((matching = IOService::serviceMatching("IOMedia")) == NULL || + (matching->setObject(kIOMediaWholeKey, kOSBooleanFalse)) == false || + (matching->setObject(kIOBSDUnitKey, bsdUnit)) == false || + (iter = IOService::getMatchingServices(matching)) == NULL) { + dprintf("%s iterator for location failed\n", + __func__); + + if (matching) matching->release(); + /* We had a candidate, but couldn't get the location */ + media->release(); + return (NULL); + } + matching->release(); + matching = 0; + + /* Iterate over children checking for matching location */ + matched = false; + entry = 0; + while ((obj = iter->getNextObject()) != NULL) { + if ((entry = OSDynamicCast(IORegistryEntry, obj)) == NULL || + (OSDynamicCast(IOMedia, entry)) == NULL) { + entry = 0; + continue; + } + + if ((entryLocation = entry->getLocation()) == NULL || + (strlen(entryLocation) != strlen(location)) || + strcmp(entryLocation, location) != 0) { + entry = 0; + continue; + } + + dprintf("%s found match\n", __func__); + matched = true; + entry->retain(); + break; + } + iter->release(); + iter = 0; + + /* Drop the whole-disk media */ + media->release(); + media = 0; + + /* Cast the new entry, if there is one */ + if (!entry || (media = OSDynamicCast(IOMedia, entry)) == NULL) { +if (entry) dprintf("%s had entry but couldn't cast\n", __func__); + dprintf("%s no media found for path %s\n", + __func__, path); + if (entry) entry->release(); + return (NULL); + } + + dprintf("%s media from serial number succeeded\n", __func__); + + /* Leave a retain count on the media */ + return (matched ? media : NULL); +} + +/* + * media_from_path is intended to be called by ldi_open_by_name + * with a char* path, and returns NULL or an IOMedia device with a + * retain count that should be released on open. + */ +static IOMedia * +media_from_path(const char *path = 0) +{ + IOMedia *media; + OSDictionary *matchDict; + + /* Validate path */ + if (path == 0 || strlen(path) <= 1) { + dprintf("%s no path provided\n", __func__); + return (NULL); + } + + if (strncmp(path, "/var/run/disk/by-path/", 22) == 0 || + strncmp(path, "/private/var/run/disk/by-path/", 30) == 0) { + media = media_from_device_path(path); + dprintf("%s media_from_device_path %s\n", __func__, + (media ? "succeeded" : "failed")); + return (media); + } + + if (strncmp(path, "/var/run/disk/by-serial/", 24) == 0 || + strncmp(path, "/private/var/run/disk/by-serial/", 32) == 0) { + media = media_from_serial(path); + dprintf("%s media_from_serial %s\n", __func__, + (media ? "succeeded" : "failed")); + return (media); + } + + /* Try to get /dev/disk or /private/var/run/disk/by-id path */ + matchDict = media_matchdict_from_path(path); + if (!matchDict) { + dprintf("%s couldn't get matching dictionary\n", __func__); + return (NULL); + } + + media = media_from_matchdict(matchDict); + matchDict->release(); + matchDict = 0; + + if (!media) { + dprintf("%s no IOMedia found for path %s\n", __func__, path); + } + + /* Return NULL or valid media with retain count */ + return (media); +} + +/* Define an IOKit buffer for buf_strategy_iokit */ +typedef struct ldi_iokit_buf { + IOMemoryDescriptor *iomem; + IOStorageCompletion iocompletion; + IOStorageAttributes ioattr; +} ldi_iokit_buf_t; /* XXX Currently 64b */ + +/* Completion handler for IOKit strategy */ +static void +ldi_iokit_io_intr(void *target, void *parameter, + IOReturn status, UInt64 actualByteCount) +{ + ldi_iokit_buf_t *iobp = (ldi_iokit_buf_t *)target; + ldi_buf_t *lbp = (ldi_buf_t *)parameter; + +#ifdef DEBUG + /* In debug builds, verify buffer pointers */ + ASSERT3U(lbp, !=, 0); + ASSERT3U(iobp, !=, 0); + + if (!iobp || !lbp) { + printf("%s missing a buffer\n", __func__); + return; + } + + ASSERT3U(iobp->iomem, !=, 0); + + if (!iobp->iomem) { + printf("%s missing iobp->iomem\n", __func__); + return; + } + + // this is very very very noisy in --enable-boot + // ASSERT3U(ldi_zfs_handle, !=, 0); + + if (actualByteCount == 0 || + actualByteCount != lbp->b_bcount || + status != kIOReturnSuccess) { + printf("%s %s %llx / %llx\n", __func__, + "actualByteCount != lbp->b_bcount", + actualByteCount, lbp->b_bcount); + if (ldi_zfs_handle) + printf("%s status %d %d %s\n", __func__, status, + ldi_zfs_handle->errnoFromReturn(status), + ldi_zfs_handle->stringFromReturn(status)); + else + printf("%s status %d ldi_zfs_handle is NULL\n", + __func__, status); + } +#endif + + /* Complete and release IOMemoryDescriptor */ + iobp->iomem->complete(); + iobp->iomem->release(); + iobp->iomem = 0; + + /* Compute resid */ + ASSERT3U(lbp->b_bcount, >=, actualByteCount); + lbp->b_resid = (lbp->b_bcount - actualByteCount); + + /* Set error status */ + if (status == kIOReturnSuccess && + actualByteCount != 0 && lbp->b_resid == 0) { + lbp->b_error = 0; + } else { + lbp->b_error = EIO; + } + + /* Free IOKit buffer */ + kmem_free(iobp, sizeof (ldi_iokit_buf_t)); + + /* Call original completion function */ + if (lbp->b_iodone) { + (void) lbp->b_iodone(lbp); + } +} + +/* Synchronous IO, called by buf_strategy_iokit */ +static int +buf_sync_strategy_iokit(ldi_buf_t *lbp, ldi_iokit_buf_t *iobp, + struct ldi_handle *lhp) +{ + UInt64 actualByteCount = 0; + IOReturn result; + + iobp->ioattr.priority = 0; + iobp->ioattr.options = 0; + + /* Read or write */ + if (lbp->b_flags & B_READ) { + result = LH_MEDIA(lhp)->IOStorage::read(LH_CLIENT(lhp), + dbtolb(lbp->b_lblkno), iobp->iomem, + &iobp->ioattr, &actualByteCount); + } else { + result = LH_MEDIA(lhp)->IOStorage::write(LH_CLIENT(lhp), + dbtolb(lbp->b_lblkno), iobp->iomem, + &iobp->ioattr, &actualByteCount); + } + + /* Call completion */ + ldi_iokit_io_intr((void *)iobp, (void *)lbp, + result, actualByteCount); + + /* Return success based on result */ + return (result == kIOReturnSuccess ? 0 : EIO); +} + +/* + * Uses IOMedia::read asynchronously or IOStorage::read synchronously. + * virtual void read(IOService * client, + * UInt64 byteStart, + * IOMemoryDescriptor * buffer, + * IOStorageAttributes * attributes, + * IOStorageCompletion * completion); + * virtual IOReturn read(IOService * client, + * UInt64 byteStart, + * IOMemoryDescriptor * buffer, + * IOStorageAttributes * attributes = 0, + * UInt64 * actualByteCount = 0); + */ +int +buf_strategy_iokit(ldi_buf_t *lbp, struct ldi_handle *lhp) +{ + ldi_iokit_buf_t *iobp = 0; + + ASSERT3U(lbp, !=, NULL); + ASSERT3U(lhp, !=, NULL); + +#ifdef DEBUG + /* Validate IOMedia */ + if (!OSDynamicCast(IOMedia, LH_MEDIA(lhp)) || + !OSDynamicCast(IOService, LH_CLIENT(lhp))) { + dprintf("%s invalid IOMedia or client\n", __func__); + return (ENODEV); + } +#endif /* DEBUG */ + + /* Allocate an IOKit buffer */ + iobp = (ldi_iokit_buf_t *)kmem_alloc(sizeof (ldi_iokit_buf_t), + KM_SLEEP); + if (!iobp) { + dprintf("%s couldn't allocate buf_iokit_t\n", __func__); + return (ENOMEM); + } +#ifdef LDI_ZERO + /* Zero the new buffer struct */ + bzero(iobp, sizeof (ldi_iokit_buf_t)); +#endif + + /* Set completion and attributes for async IO */ + if (lbp->b_iodone != NULL) { + iobp->iocompletion.target = iobp; + iobp->iocompletion.parameter = lbp; + iobp->iocompletion.action = &ldi_iokit_io_intr; + } + +/* XXX Zeroed above if LDI_ZERO, otherwise here */ +#ifndef LDI_ZERO + /* XXX Zero the ioattr struct */ + bzero(&iobp->ioattr, sizeof (IOStorageAttributes)); +#endif + + /* Priority of I/O */ + if (lbp->b_flags & B_THROTTLED_IO) { + lbp->b_flags &= ~B_THROTTLED_IO; + iobp->ioattr.priority = kIOStoragePriorityBackground; + if (lbp->b_flags & B_WRITE) + iobp->ioattr.priority--; + } + else if ((lbp->b_flags & B_ASYNC) == 0 || (lbp->b_flags & B_WRITE)) + iobp->ioattr.priority = kIOStoragePriorityDefault - 1; + else + iobp->ioattr.priority = kIOStoragePriorityDefault; + + /* Allocate a memory descriptor pointing to the data address */ + iobp->iomem = IOMemoryDescriptor::withAddress( + lbp->b_un.b_addr, lbp->b_bcount, + (lbp->b_flags & B_READ ? kIODirectionIn : kIODirectionOut)); + + /* Verify the buffer */ + if (!iobp->iomem || iobp->iomem->getLength() != lbp->b_bcount || + iobp->iomem->prepare() != kIOReturnSuccess) { + dprintf("%s couldn't allocate IO buffer\n", + __func__); + if (iobp->iomem) { + iobp->iomem->release(); + } + kmem_free(iobp, sizeof (ldi_iokit_buf_t)); + return (ENOMEM); + } + + /* Recheck instantaneous value of handle status */ + if (lhp->lh_status != LDI_STATUS_ONLINE) { + dprintf("%s device not online\n", __func__); + iobp->iomem->complete(); + iobp->iomem->release(); + kmem_free(iobp, sizeof (ldi_iokit_buf_t)); + return (ENODEV); + } + + /* Synchronous or async */ + if (lbp->b_iodone == NULL) { + return (buf_sync_strategy_iokit(lbp, iobp, lhp)); + } + + /* Read or write */ + if (lbp->b_flags & B_READ) { + LH_MEDIA(lhp)->IOMedia::read(LH_CLIENT(lhp), + dbtolb(lbp->b_lblkno), iobp->iomem, + &iobp->ioattr, &iobp->iocompletion); + } else { + LH_MEDIA(lhp)->IOMedia::write(LH_CLIENT(lhp), + dbtolb(lbp->b_lblkno), iobp->iomem, + &iobp->ioattr, &iobp->iocompletion); + } + + /* Return success, will call io_intr when done */ + return (0); +} + +/* Client interface, alloc and open IOKit handle */ +int +ldi_open_by_media(IOMedia *media = 0, dev_t device = 0, + int fmode = 0, ldi_handle_t *lhp = 0) +{ + struct ldi_handle *retlhp; + ldi_status_t status; + int error; + + /* Validate IOMedia */ + if (!media || !lhp) { + dprintf("%s invalid argument %p or %p\n", + __func__, media, lhp); + return (EINVAL); + } + + /* Retain for duration of open */ + media->retain(); + + /* Get dev_t if not supplied */ + if (device == 0 && (device = dev_from_media(media)) == 0) { + dprintf("%s dev_from_media failed: %p %d\n", __func__, + media, device); + media->release(); + return (ENODEV); + } + + /* In debug build, be loud if we potentially leak a handle */ + ASSERT3U(*(struct ldi_handle **)lhp, ==, NULL); + + /* Allocate IOKit handle */ + retlhp = handle_alloc_iokit(device, fmode); + if (retlhp == NULL) { + dprintf("%s couldn't allocate IOKit handle\n", __func__); + media->release(); + return (ENOMEM); + } + + /* Try to open device with IOMedia */ + status = handle_open_start(retlhp); + if (status == LDI_STATUS_ONLINE) { + dprintf("%s already online, refs %d, openrefs %d\n", __func__, + retlhp->lh_ref, retlhp->lh_openref); + /* Cast retlhp and assign to lhp (may be 0) */ + *lhp = (ldi_handle_t)retlhp; + media->release(); + /* Successfully incremented open ref */ + return (0); + } + if (status != LDI_STATUS_OPENING) { + dprintf("%s invalid status %d\n", __func__, status); + handle_release(retlhp); + retlhp = 0; + media->release(); + return (ENODEV); + } + + error = handle_open_iokit(retlhp, media); + media->release(); + + if (error) { + dprintf("%s Couldn't open handle\n", __func__); + handle_open_done(retlhp, LDI_STATUS_CLOSED); + handle_release(retlhp); + retlhp = 0; + return (EIO); + } + handle_open_done(retlhp, LDI_STATUS_ONLINE); + + /* Register for disk notifications */ + handle_register_notifier(retlhp); + + /* Cast retlhp and assign to lhp (may be 0) */ + *lhp = (ldi_handle_t)retlhp; + /* Pass error from open */ + return (error); +} + +/* Client interface, find IOMedia from dev_t, alloc and open handle */ +int +ldi_open_media_by_dev(dev_t device = 0, int fmode = 0, + ldi_handle_t *lhp = 0) +{ + IOMedia *media = 0; + int error = EINVAL; + + /* Validate arguments */ + if (!lhp || device == 0) { + dprintf("%s missing argument %p %d\n", + __func__, lhp, device); + return (EINVAL); + } + /* In debug build, be loud if we potentially leak a handle */ + ASSERT3U(*((struct ldi_handle **)lhp), ==, NULL); + + /* Get IOMedia from major/minor */ + if ((media = media_from_dev(device)) == NULL) { + dprintf("%s media_from_dev error %d\n", + __func__, error); + return (ENODEV); + } + + /* Try to open by media */ + error = ldi_open_by_media(media, device, fmode, lhp); + + /* Release IOMedia and clear */ + media->release(); + media = 0; + + /* Pass error from open */ + return (error); +} + +/* Client interface, find dev_t and IOMedia/vnode, alloc and open handle */ +int +ldi_open_media_by_path(char *path = 0, int fmode = 0, + ldi_handle_t *lhp = 0) +{ + IOMedia *media = 0; + dev_t device = 0; + int error = EINVAL; + + /* Validate arguments */ + if (!lhp || !path) { + dprintf("%s %s %p %s %d\n", __func__, + "missing lhp or path", lhp, path, fmode); + return (EINVAL); + } + /* In debug build, be loud if we potentially leak a handle */ + ASSERT3U(*((struct ldi_handle **)lhp), ==, NULL); + + /* For /dev/disk*, and InvariantDisk paths */ + if ((media = media_from_path(path)) == NULL) { + dprintf("%s media_from_path failed\n", __func__); + return (ENODEV); + } + + error = ldi_open_by_media(media, device, fmode, lhp); + + /* Release IOMedia and clear */ + media->release(); + media = 0; + + /* Error check open */ + if (error) { + dprintf("%s ldi_open_by_media failed %d\n", + __func__, error); + } + + return (error); +} + +int +handle_remove_notifier(struct ldi_handle *lhp) +{ + handle_notifier_t notifier; + +#ifdef DEBUG + if (!lhp) { + dprintf("%s missing handle\n", __func__); + return (EINVAL); + } +#endif + + if (lhp->lh_notifier == 0) { + dprintf("%s no notifier installed\n", __func__); + return (0); + } + + /* First clear notifier pointer */ + notifier = lhp->lh_notifier; + lhp->lh_notifier = 0; + +#ifdef DEBUG + /* Validate IONotifier object */ + if (!OSDynamicCast(IONotifier, notifier->obj)) { + dprintf("%s %p is not an IONotifier\n", __func__, + notifier->obj); + return (EINVAL); + } +#endif + + notifier->obj->remove(); + kmem_free(notifier, sizeof (handle_notifier_t)); + return (0); +} + +int +handle_register_notifier(struct ldi_handle *lhp) +{ + OSDictionary *matchDict; + handle_notifier_t notifier; + + /* Make sure we have a handle and dev_t */ + if (!lhp || lhp->lh_dev == 0) { + dprintf("%s no handle or missing dev_t\n", __func__); + return (EINVAL); + } + + notifier = (handle_notifier_t)kmem_alloc( + sizeof (struct _handle_notifier), KM_SLEEP); + if (!notifier) { + dprintf("%s couldn't alloc notifier struct\n", __func__); + return (ENOMEM); + } + + /* Get matchDict, will need to be released */ + matchDict = media_matchdict_from_dev(lhp->lh_dev); + if (!matchDict) { + dprintf("%s couldn't get matching dictionary\n", __func__); + kmem_free(notifier, sizeof (handle_notifier_t)); + return (EINVAL); + } + + /* Register IOMedia termination notification */ + notifier->obj = IOService::addMatchingNotification( + gIOTerminatedNotification, matchDict, + handle_media_terminate_cb, /* target */ 0, + /* refCon */ (void *)lhp, /* priority */ 0); + matchDict->release(); + + /* Error check notifier */ + if (!notifier->obj) { + dprintf("%s addMatchingNotification failed\n", + __func__); + kmem_free(notifier, sizeof (handle_notifier_t)); + return (ENOMEM); + } + + /* Assign notifier to handle */ + lhp->lh_notifier = notifier; + return (0); +} + +/* Supports both IOKit and vnode handles by finding IOMedia from dev_t */ +int +handle_set_wce_iokit(struct ldi_handle *lhp, int *wce) +{ + IOMedia *media; + IORegistryEntry *parent; + IOBlockStorageDevice *device; + IOReturn result; + bool value; + + if (!lhp || !wce) { + return (EINVAL); + } + + switch (lhp->lh_type) { + case LDI_TYPE_IOKIT: + if ((media = LH_MEDIA(lhp)) == NULL) { + dprintf("%s couldn't get IOMedia\n", __func__); + return (ENODEV); + } + /* Add a retain count */ + media->retain(); + break; + case LDI_TYPE_VNODE: + if (lhp->lh_dev == 0 || + (media = media_from_dev(lhp->lh_dev)) == 0) { + dprintf("%s couldn't find IOMedia for dev_t %d\n", + __func__, lhp->lh_dev); + return (ENODEV); + } + /* Returned media has a retain count */ + break; + default: + dprintf("%s invalid handle\n", __func__); + return (EINVAL); + } + + /* Walk the parents of this media */ + for (parent = media->getParentEntry(gIOServicePlane); + parent != NULL; + parent = parent->getParentEntry(gIOServicePlane)) { + /* Until a valid device is found */ + device = OSDynamicCast(IOBlockStorageDevice, parent); + if (device != NULL) { + device->retain(); + break; + } + /* Next parent */ + } + media->release(); + media = 0; + + /* If no matching device was found */ + if (!device) { + dprintf("%s no IOBlockStorageDevice found\n", __func__); + return (ENODEV); + } + + result = device->getWriteCacheState(&value); + if (result != kIOReturnSuccess) { + // dprintf("%s couldn't get current write cache state %d\n", + // __func__, ldi_zfs_handle->errnoFromReturn(result)); + return (ENXIO); + } + + /* If requested value does not match current */ + if (value != *wce) { + value = (*wce == 1); + /* Attempt to change the value */ + result = device->setWriteCacheState(value); + } + + /* Set error and wce to return */ + if (result != kIOReturnSuccess) { + // dprintf("%s couldn't set write cache %d\n", + // __func__, ldi_zfs_handle->errnoFromReturn(result)); + /* Flip wce to indicate current status */ + *wce = !(*wce); + return (ENXIO); + } + + return (0); +} + +int +handle_get_media_info_iokit(struct ldi_handle *lhp, + struct dk_minfo *dkm) +{ + uint32_t blksize; + uint64_t blkcount; + + if (!lhp || !dkm) { + return (EINVAL); + } + + /* Validate IOMedia */ + if (!OSDynamicCast(IOMedia, LH_MEDIA(lhp))) { + dprintf("%s invalid IOKit handle\n", __func__); + return (ENODEV); + } + + LH_MEDIA(lhp)->retain(); + + if ((blksize = LH_MEDIA(lhp)->getPreferredBlockSize()) == 0) { + dprintf("%s invalid blocksize\n", __func__); + LH_MEDIA(lhp)->release(); + return (ENXIO); + } + + if ((blkcount = LH_MEDIA(lhp)->getSize() / blksize) == 0) { + dprintf("%s invalid block count\n", __func__); + LH_MEDIA(lhp)->release(); + return (ENXIO); + } + + LH_MEDIA(lhp)->release(); + + /* Set the return values */ + dkm->dki_capacity = blkcount; + dkm->dki_lbsize = blksize; + + return (0); +} + +int +handle_get_media_info_ext_iokit(struct ldi_handle *lhp, + struct dk_minfo_ext *dkmext) +{ + OSObject *prop; + OSNumber *number; + uint32_t blksize, pblksize; + uint64_t blkcount; + + if (!lhp || !dkmext) { + dprintf("%s missing lhp or dkmext\n", __func__); + return (EINVAL); + } + + /* Validate IOMedia */ + if (!OSDynamicCast(IOMedia, LH_MEDIA(lhp))) { + dprintf("%s invalid IOKit handle\n", __func__); + return (ENODEV); + } + + LH_MEDIA(lhp)->retain(); + + prop = LH_MEDIA(lhp)->getProperty(kIOPropertyPhysicalBlockSizeKey, + gIOServicePlane, kIORegistryIterateRecursively | + kIORegistryIterateParents); + + number = OSDynamicCast(OSNumber, prop); + if (!prop || !number) { + dprintf("%s couldn't get physical blocksize\n", __func__); + LH_MEDIA(lhp)->release(); + return (ENXIO); + } + + pblksize = number->unsigned32BitValue(); + number = 0; + prop = 0; + + if ((blksize = LH_MEDIA(lhp)->getPreferredBlockSize()) == 0) { + dprintf("%s invalid blocksize\n", __func__); + LH_MEDIA(lhp)->release(); + return (ENXIO); + } + + if ((blkcount = LH_MEDIA(lhp)->getSize() / blksize) == 0) { + dprintf("%s invalid block count\n", __func__); + LH_MEDIA(lhp)->release(); + return (ENXIO); + } + + LH_MEDIA(lhp)->release(); + +#ifdef DEBUG + dprintf("%s phys blksize %u, logical blksize %u, blockcount %llu\n", + __func__, pblksize, blksize, blkcount); +#endif + + /* Set the return values */ + dkmext->dki_capacity = blkcount; + dkmext->dki_lbsize = blksize; + dkmext->dki_pbsize = pblksize; + + return (0); +} + +int +handle_check_media_iokit(struct ldi_handle *lhp, int *status) +{ + /* Validate arguments */ + if (!lhp || !status) { + return (EINVAL); + } + + /* Validate IOMedia */ + if (!OSDynamicCast(IOMedia, LH_MEDIA(lhp))) { + dprintf("%s invalid IOKit handle\n", __func__); + return (ENODEV); + } + + LH_MEDIA(lhp)->retain(); + + /* Validate device size */ + if (LH_MEDIA(lhp)->getSize() == 0) { + dprintf("%s media reported 0 size\n", __func__); + LH_MEDIA(lhp)->release(); + return (ENXIO); + } + + /* Validate write status if handle fmode is read-write */ + if ((lhp->lh_fmode & FWRITE) && + LH_MEDIA(lhp)->isWritable() == false) { + dprintf("%s media is not writeable\n", __func__); + LH_MEDIA(lhp)->release(); + return (EPERM); + } + + LH_MEDIA(lhp)->release(); + + /* Success */ + *status = 0; + return (0); +} + +int +handle_is_solidstate_iokit(struct ldi_handle *lhp, int *isssd) +{ + OSDictionary *propDict = 0; + OSString *property = 0; + + /* Validate arguments */ + if (!lhp || !isssd) { + return (EINVAL); + } + + /* Validate IOMedia */ + if (!OSDynamicCast(IOMedia, LH_MEDIA(lhp))) { + dprintf("%s invalid IOKit handle\n", __func__); + return (ENODEV); + } + + LH_MEDIA(lhp)->retain(); + + propDict = OSDynamicCast(OSDictionary, LH_MEDIA(lhp)->getProperty( + kIOPropertyDeviceCharacteristicsKey, gIOServicePlane)); + + if (propDict != 0) { + property = OSDynamicCast(OSString, + propDict->getObject(kIOPropertyMediumTypeKey)); + propDict = 0; + } + + if (property != 0 && + property->isEqualTo(kIOPropertyMediumTypeSolidStateKey)) { + *isssd = 1; + } + property = 0; + + LH_MEDIA(lhp)->release(); + + return (0); +} + +int +handle_features_iokit(struct ldi_handle *lhp, + uint32_t *data) +{ + if (!lhp || !data) { + return (EINVAL); + } + + /* Validate IOMedia */ + if (!OSDynamicCast(IOMedia, LH_MEDIA(lhp))) { + dprintf("%s invalid IOKit handle\n", __func__); + return (ENODEV); + } + + LH_MEDIA(lhp)->retain(); + + OSDictionary *dictionary = OSDynamicCast( + /* class */ OSDictionary, + /* object */ LH_MEDIA(lhp)->getProperty( + /* key */ kIOStorageFeaturesKey, + /* plane */ gIOServicePlane)); + + *data = 0; + + if (dictionary) { + OSBoolean *boolean; + +#ifdef DK_FEATURE_BARRIER + boolean = OSDynamicCast( + /* class */ OSBoolean, + /* object */ dictionary->getObject( + /* key */ kIOStorageFeatureBarrier)); + + if (boolean == kOSBooleanTrue) + *(uint32_t *)data |= DK_FEATURE_BARRIER; +#endif + + boolean = OSDynamicCast( + /* class */ OSBoolean, + /* object */ dictionary->getObject( + /* key */ kIOStorageFeatureForceUnitAccess)); + + if (boolean == kOSBooleanTrue) + *(uint32_t *)data |= DK_FEATURE_FORCE_UNIT_ACCESS; + +#ifdef DK_FEATURE_PRIORITY + boolean = OSDynamicCast( + /* class */ OSBoolean, + /* object */ dictionary->getObject( + /* key */ kIOStorageFeaturePriority)); + + if (boolean == kOSBooleanTrue) + *(uint32_t *)data |= DK_FEATURE_PRIORITY; +#endif + + boolean = OSDynamicCast( + /* class */ OSBoolean, + /* object */ dictionary->getObject( + /* key */ kIOStorageFeatureUnmap)); + + if (boolean == kOSBooleanTrue) + *(uint32_t *)data |= DK_FEATURE_UNMAP; + } + + LH_MEDIA(lhp)->release(); + return (0); +} + +int +handle_unmap_iokit(struct ldi_handle *lhp, + dkioc_free_list_ext_t *dkm) +{ + int error = 0; + + if (!lhp || !dkm) { + return (EINVAL); + } + + /* Validate IOMedia */ + if (!OSDynamicCast(IOMedia, LH_MEDIA(lhp))) { + dprintf("%s invalid IOKit handle\n", __func__); + return (ENODEV); + } + + LH_MEDIA(lhp)->retain(); + + /* We need to convert illumos' dkioc_free_list_t to dk_unmap_t */ + IOStorageExtent *extents; + extents = IONew(IOStorageExtent, 1); + extents[0].byteStart = dkm->dfle_start; + extents[0].byteCount = dkm->dfle_length; + + /* + * dkm->dfl_flags vs IOStorageUnmapOptions + * #define DF_WAIT_SYNC 0x00000001 + * Wait for full write-out of free. + * IOStorageUnmapOptions is only 0 + */ + + /* issue unmap */ + error = LH_MEDIA(lhp)->unmap(LH_CLIENT(lhp), + extents, 1, 0); + + if (error != 0) { + dprintf("%s unmap: 0x%x\n", __func__, error); + // Convert IOReturn to errno + error = LH_MEDIA(lhp)->errnoFromReturn(error); + } + + IODelete(extents, IOStorageExtent, 1); + LH_MEDIA(lhp)->release(); + + return (error); +} + + +} /* extern "C" */ diff --git a/module/os/macos/zfs/ldi_osx.c b/module/os/macos/zfs/ldi_osx.c new file mode 100644 index 000000000000..0c07cec85e1a --- /dev/null +++ b/module/os/macos/zfs/ldi_osx.c @@ -0,0 +1,2432 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ +/* + * Copyright (c) 2015, Evan Susarret. All rights reserved. + */ +/* + * Portions of this document are copyright Oracle and Joyent. + * OS X implementation of ldi_ named functions for ZFS written by + * Evan Susarret in 2015. + */ + +/* + * LDI Subsystem on OS X: + * + * Designed as a drop-in replacement for sunldi.h and driver_lyr.c, + * LDI abstracts away platform-specific device handling. This allows + * vdev_disk.c to more closely match 'upstream' illumos/OpenZFS. + * + * LDI handles may use IOKit or vnode ops to locate and use devices. + * - This reduces the call stack and work needed for almost all IO. + * - Allows for vdev discovery and use during early boot, before the + * root device is mounted. + * - Having both types allows use of non-standard kexts which publish + * bdevsw block devices (without IOMedia). + * + * XXX Review correct call stack using dtrace, annotate stack size. + * Previously, vnode_open and VNOP_STRATEGY were used, which required + * allocating buf_t for IO. This meant translating byte offsets to + * block numbers for every IO. Once issued, dtrace showed that a very + * large stack was required: + * VNOP_STRATEGY macro performs work then calls + * spec_strategy (vop->vop_strategy) which performs work then calls + * dkiostrategy (syscall) which passes the IO to an IOMediaBSDClient + * IOMediaBSDClient performed work and passes to its IOMedia provider + * + * Beyond that is a common path shared by vnode and IOMedia: + * IOMedia performs work, then does prepareRequest, breakUpRequest, + * deBlockRequest, and executeRequest. + * Potentially passed down the provider stack through IOPartitionMap + * then to the whole-disk IOMedia, with more work + * Passed down through IOBlockStorageDriver, with more work + * Passed down through IOBlockStorageDevice, with more work + * Finally passed to Family-specific driver (AHCI, diskimage, etc.) + * + * By directly accessing IOMedia, the stack is reduced, and byte + * offsets are passed to read()/write() via ldi_strategy. + * We still need to allocate an IOMemoryDescriptor for the data buf, + * however only an IOMemoryDescriptor::withAddress() reference is + * required, similar to buf_setdataptr. + */ + +/* + * LDI Handle hash lists: + * + * During ldi_init, LH_HASH_SZ lists and locks are allocated. New handles + * will be added to the list indexed by the hash of the dev_t number. + * + * The hash function simply performs a modulus on the dev_t number based on + * the LH_HASH_SZ, as opposed to illumos which hashes based on the vnode + * pointer. + * This has been tested by hashing disk0, disk0s1, disk0s2, disk1, disk1s1, + * etc. to verify results were distributed across hash range. + * + * OS X dev_t numbers should be unique unless a new device claims the same + * dev_t as a removed/failed device. This would only be a collision if we + * still have a handle for the failed device (notification/event handlers + * should remove these before that occurs). + * Since Offline status is a dead-end and the handle cannot be dereferenced + * or freed while iterating the hash list, it is safe to check the status + * and skip a handle if the status is Offline (without taking handle lock). + * + * XXX On illumos the hash function uses the vnode's pointer address as the + * unique key. Since vnode addresses are aligned to the size of the vnode + * struct, the hash function shifts the pointer address to the right in order + * to hash the unique bits of the address. OS X dev_t use all the bits of + * an unsigned 32-bit int. + */ + +/* + * LDI Handle locks: + * + * Handle references and list membership are protected by the hash list + * locks. + * Handle status and other fields are protected by a per-handle mutex. + * + * To prevent deadlocks and artificial delays, the hash list locks should + * be held only for handle hold/release and handle_add/remove (list + * iterate/insert/remove). Those functions avoid blocking. + * Use the handle mutex to change state, and avoid blocking there, too. + * + * XXX Right now handle_status_change does allocate for taskq_dispatch + * with the handle lock held, but uses TQ_NOSLEEP and verifies result. + * + * Non-locking ops such as ldi_strategy, ldi_get_size, and ldi_sync will + * check the instantaneous status/refs before attempting to proceed, and + * can only perform IO while the device is Online. + */ + +/* + * LDI Handle allocation: + * + * ldi_open_by_name and ldi_open_by_dev locate the device and call + * ldi_open_media_by_path, ldi_open_media_by_dev, or ldi_open_vnode_by_path. + * + * From ldi_open_by_media and _by_vnode, we call handle_alloc_{type}. Both + * call handle_alloc_common to allocate and configure the handle. + * + * A handle is allocated in the Closed state with 1 reference. The handle + * is added to the hash list on allocation, unless a duplicate handle exists + * (same dev_t as well as fmode, not in Offline status). If an existing + * handle is found, the newly allocated handle is freed. + * + * handle_open_start is called, which takes the handle lock to check current + * status. Each of these states is possible: + * Offline: device has disappeared between allocation and now (unlikely). + * Closed: new or recently closed handle, changes status to Opening. + * Closing: already in progress. Sleeps on lock and rechecks the status. + * Opening: already in progress. Sleeps on lock and rechecks the status. + * Online: no need to open device, just increment openref count. + * + * If handle_open_start changes the status to Opening, the device is opened + * by calling handle_open_iokit or handle_open_vnode. + * + * This differs from illumos driver_lyr.c where handle_alloc first opens a + * vnode for the device, allocates a handle by vnode, and finally checks for + * a duplicate handle in the list (open, alloc, find vs. alloc, open, find). + * To do so, illumos has a VOP_OPEN that is aware of layered-driver opens. + */ + +/* + * LDI Handle list membership: + * + * Allocate with one reference, to be used or released by the caller. + * Call handle_hold if additional references are needed. + * + * Call handle_release to drop reference. On last release, this calls + * handle_free (but does not remove the handle from the list, see below). + * + * Call handle_add to determine if this handle is a duplicate, inserting + * handle into list or returning an existing handle with a hold. + * Check the result and call handle_release on the new handle if another + * handle was returned (new handle is not added to list). + * + * Each call to handle_find will take optionally take a hold, which should + * be released when no longer needed (used by handle_add). + * + * Calling handle_open increments lh_openref but does not change lh_ref. + * Caller should already have called handle_hold to get a reference. + * + * If lh_ref is 1, call handle_remove_locked (with list lock) to remove the + * handle from the list, then call handle_release_locked to remove last ref + * and free. + * A handle shouldn't remain in the list in Closed status with no refs. + * + * Calling handle_close with the last openref will automatically take list + * lock, call handle_remove_locked, and then handle_release_locked. + */ + +/* + * LDI Handle device objects: + * + * Multiple read-only opens share one read-only handle. + * Multiple read-write opens share one read-write handle. + * + * IOKit handles are allocated with the dev_t number and fmode. + * handle_open_iokit is passed an IOMedia object (which should have a + * retain held). + * Once handle_open returns, the IOMedia can be released by the caller. + * + * Vnode handles are allocated with the dev_t number and fmode. + * handle_open_vnode is passed a path (null-terminated C string). + * vnode_open increments both iocount and refcount, vnode_ref increments + * usecount, vnode_put drops iocount between ops. + * vnode_getwithref takes an iocount, and vnode_rele drops usecount + * before vnode_close decrements iocount and refcount. + */ + +/* + * LDI Handle status: + * + * #define LDI_STATUS_OFFLINE 0x0 + * #define LDI_STATUS_CLOSED 0x1 + * #define LDI_STATUS_CLOSING 0x2 + * #define LDI_STATUS_OPENING 0x3 + * #define LDI_STATUS_ONLINE 0x4 + * + * The handle lock will be taken to change status. + * + * Handle state can only progress from Closed to Opening status, and must + * have a reference held to do so. The lock is dropped for open and close + * ops while the handle is in Opening or Closing status. + * + * If the open is successful, the state is set to Online (with handle lock + * held). This state is required for IO operations to be started. The state + * may have changed by the time an IO completes. + * + * For IOKit devices, and vnode devices that have an IOMedia, a callback is + * registered for IOMedia termination which changes the state to Offline and + * posts event callbacks. + * + * Closing a handle, by the user or as a result of an event, sets the state + * to Closing. Once device close is issued, the state changes from Closing + * to Closed (even if close returned failure). + * + * A handle that still has refs and openrefs will remain in the Online + * state, dropping refs and openrefs each time ldi_close is called. + * + * If there are refs but no openrefs, it remains in the Closed state, and + * drops refs each time handle_release is called. + * This allows clients to call ldi_open_by_* to reopen the handle, in the + * case where one client is opening the handle at the same time another is + * closing it. + * + * If the device has gone missing (IOMedia terminated), the handle will + * change to Offline status. This is a dead-end which issues Offline Notify + * and Finalize events, then cleans up the handle once all clients have + * called ldi_close. + * + * Once all references have been dropped, the handle is removed from the + * hash list with the hash list lock held, then freed. + */ + +/* + * LDI Events: + * + * XXX Degrade event is not implemented, doubt it will be useful. Intended + * to be set when a vdev that is backed by RAID becomes degraded. This is + * not a recommended use case for ZFS, and on OS X we only have AppleRAID + * or custom hardware or software RAID. Also per the comments, the vdev + * would be marked Degraded only to inform the user via zpool status. + * + * XXX Tested in VirtualBox by hotplugging a SATA device, have yet to + * test with USB removal, etc. + * + * ldi_register_ev_callback can be used to add a struct to the event + * callback list containing the handle pointer, a notify callback, and + * a finalize callback. + * + * Supported events are Offline Notify/Finalize, which will be + * posted when the device enters the Offline state (IOMedia terminated). + * + * The event callback functions should be non-blocking. It is recommended + * to update a flag that can be checked prior to calling ldi_strategy. + */ + +/* + * LDI client interfaces: + * + * ldi_open_by_name + * ldi_open_by_dev + * ldi_close + * + * ldi_register_ev_callback + * ldi_unregister_ev_callback + * + * ldi_get_size + * ldi_sync + * ldi_ioctl + * ldi_strategy + * + * ldi_bioinit + * ldi_biofini + */ + +/* + * LDI Buffers: + * + * ldi_strategy uses an abstract buffer for IO, so clients do not need to + * be concerned with type-specific buf_t and IOMemoryDescriptor handling. + * + * Allocate and free ldi_buf_t manually, calling ldi_bioinit after alloc + * and ldi_biofini prior to free. + * + * Synchronous IO can be performed by setting b_iodone to NULL. + * + * Allocate and use a buffer like this: + * + * ldi_buf_t *bp = (ldi_buf_t *)kmem_alloc(sizeof (ldi_buf_t), KM_SLEEP); + * // Verify allocation before proceeding + * error = ldi_bioinit(bp); + * bp->b_bcount = size; + * bp->b_bufsize = size; + * bp->b_offset = offset; + * bp->b_data = data_ptr; + * bp->b_flags = B_BUSY | B_NOCACHE | B_READ; // For example + * bp->b_iodone = &io_intr_func; // For async IO, omit for sync IO + * ldi_strategy(handle, bp); // Issue IO + * + * With an async callback function such as: + * void io_intr_func(ldi_buf_t bp, void *param) + * { + * // Check/copyout bp->b_error and bp->b_resid + * ldi_biofini(bp); + * kmem_free(bp, sizeof (ldi_buf_t)); + * } + */ + +/* + * XXX LDI TO DO + * + * LDI handle stats. In debug builds, we have IO counters - number of IOs, + * number of bytes in/out. + * kstats for handle counts and sysctls for vnode/IOKit modes also implemented. + * + * To implement events, both vnode and IOKit handles register for matching + * notifications from the IOMedia object (if found). + * Using subclassed IOService can also receive IOMessage events, which + * would be issued earlier. + * + * Vnode handles with no IOMedia could post events on (multiple) IO failures. + */ + +/* + * ZFS internal + */ +#include +#include +#include +#include +#include + +/* + * LDI Includes + */ +#include + +/* Debug prints */ +#ifdef DEBUG +#define LDI_EVDBG(args) cmn_err args +#define LDI_EVTRC(args) cmn_err args +#else +#define LDI_EVDBG(args) do {} while (0) +#define LDI_EVTRC(args) do {} while (0) +#endif + +#define ldi_log(fmt, ...) do { \ + dprintf(fmt, __VA_ARGS__); \ + /* delay(hz>>1); */ \ +_NOTE(CONSTCOND) } while (0) + +/* + * Defines + * comment out defines to alter behavior. + */ +// #define LDI_ZERO /* For debugging, zero allocations */ + +/* Find IOMedia by matching on the BSD disk name. */ +static boolean_t ldi_use_iokit_from_path = 1; + +/* Find IOMedia by matching on the BSD major/minor (dev_t) number. */ +static boolean_t ldi_use_iokit_from_dev = 1; + +/* + * Find dev_t by vnode_lookup. + * Resolves symlinks to block devices, symlinks, InvariantDisk links. + */ +static boolean_t ldi_use_dev_from_path = 1; + +/* + * Open device by vnode if all else fails. + * Not intented to be a fallback for unsuccessful IOMedia open, but rather + * for bdev devices that do not have an IOMedia (published by other KEXTs). + */ +static boolean_t ldi_use_vnode_from_path = 1; + +/* + * Sysctls + */ +#include +SYSCTL_DECL(_ldi); +SYSCTL_NODE(, OID_AUTO, ldi, CTLFLAG_RD | CTLFLAG_LOCKED, 0, ""); +SYSCTL_NODE(_ldi, OID_AUTO, debug, CTLFLAG_RD | CTLFLAG_LOCKED, 0, ""); +SYSCTL_UINT(_ldi_debug, OID_AUTO, use_iokit_from_dev, + CTLFLAG_RW | CTLFLAG_LOCKED, &ldi_use_iokit_from_dev, 0, + "ZFS LDI use iokit_from_path"); +SYSCTL_UINT(_ldi_debug, OID_AUTO, use_iokit_from_path, + CTLFLAG_RW | CTLFLAG_LOCKED, &ldi_use_iokit_from_path, 0, + "ZFS LDI use iokit_from_dev"); +SYSCTL_UINT(_ldi_debug, OID_AUTO, use_dev_from_path, + CTLFLAG_RW | CTLFLAG_LOCKED, &ldi_use_dev_from_path, 0, + "ZFS LDI use dev_from_path"); +SYSCTL_UINT(_ldi_debug, OID_AUTO, use_vnode_from_path, + CTLFLAG_RW | CTLFLAG_LOCKED, &ldi_use_vnode_from_path, 0, + "ZFS LDI use vnode_from_path"); + +/* + * Globals + */ +static volatile int64_t ldi_handle_hash_count; + +static list_t ldi_handle_hash_list[LH_HASH_SZ]; +static kmutex_t ldi_handle_hash_lock[LH_HASH_SZ]; + +/* + * Use of "ldi_ev_callback_list" must be protected by ldi_ev_lock() + * and ldi_ev_unlock(). + */ +static struct ldi_ev_callback_list ldi_ev_callback_list; + +static uint32_t ldi_ev_id_pool = 0; + +struct ldi_ev_cookie { + char *ck_evname; + uint_t ck_sync; + uint_t ck_ctype; +}; + +#define CT_DEV_EV_OFFLINE 0x1 +#define CT_DEV_EV_DEGRADED 0x2 +static struct ldi_ev_cookie ldi_ev_cookies[] = { + {LDI_EV_OFFLINE, 1, CT_DEV_EV_OFFLINE}, + {LDI_EV_DEGRADE, 0, CT_DEV_EV_DEGRADED}, + {LDI_EV_DEVICE_REMOVE, 0, 0}, + {NULL} /* must terminate list */ +}; + +/* + * kstats + */ +static kstat_t *ldi_ksp; + +typedef struct ldi_stats { + kstat_named_t handle_count; + kstat_named_t handle_count_iokit; + kstat_named_t handle_count_vnode; + kstat_named_t handle_refs; + kstat_named_t handle_open_rw; + kstat_named_t handle_open_ro; +} ldi_stats_t; + +static ldi_stats_t ldi_stats = { + { "handle_count", KSTAT_DATA_UINT64 }, + { "handle_count_iokit", KSTAT_DATA_UINT64 }, + { "handle_count_vnode", KSTAT_DATA_UINT64 }, + { "handle_refs", KSTAT_DATA_UINT64 }, + { "handle_open_rw", KSTAT_DATA_UINT64 }, + { "handle_open_ro", KSTAT_DATA_UINT64 } +}; + +#define LDISTAT(stat) (ldi_stats.stat.value.ui64) +#define LDISTAT_INCR(stat, val) \ +atomic_add_64(&ldi_stats.stat.value.ui64, (val)) +#define LDISTAT_BUMP(stat) LDISTAT_INCR(stat, 1) +#define LDISTAT_BUMPDOWN(stat) LDISTAT_INCR(stat, -1) + +/* + * Define macros for accessing layered driver hash structures + */ +#define LH_HASH(dev) handle_hash_func(dev) + +static inline uint_t +handle_hash_func(dev_t device) +{ + /* Just cast, macro does modulus to hash value */ + return ((uint_t)device % LH_HASH_SZ); +} + +typedef struct status_change_args { + struct ldi_handle *lhp; + int new_status; +} status_change_args_t; + +static void +handle_status_change_callback(void *arg) +{ + status_change_args_t *sc = (status_change_args_t *)arg; + + /* Validate arg struct */ + if (!sc || !sc->lhp) { + dprintf("%s missing callback struct %p or lh\n", + __func__, sc); + return; + } + if (sc->new_status > LDI_STATUS_ONLINE) { + dprintf("%s invalid status %d\n", + __func__, sc->new_status); + return; + } + + dprintf("%s Invoking notify for handle %p status %d\n", + __func__, sc->lhp, sc->new_status); + ldi_invoke_notify(0 /* dip */, sc->lhp->lh_dev, S_IFBLK, + LDI_EV_OFFLINE, sc->lhp); + + dprintf("%s Invoking finalize for handle %p status %d\n", + __func__, sc->lhp, sc->new_status); + ldi_invoke_finalize(0 /* dip */, sc->lhp->lh_dev, S_IFBLK, + LDI_EV_OFFLINE, LDI_EV_SUCCESS, sc->lhp); + + /* Free callback struct */ + kmem_free(sc, sizeof (status_change_args_t)); +} + +/* Protected by handle lock */ +static int +handle_status_change_locked(struct ldi_handle *lhp, int new_status) +{ + status_change_args_t *sc = 0; + + /* Validate lhp */ + if (!lhp) { + dprintf("%s missing handle\n", __func__); + return (EINVAL); + } + if (new_status > LDI_STATUS_ONLINE) { + dprintf("%s invalid status %d\n", __func__, new_status); + return (EINVAL); + } + + ASSERT3U(lhp, !=, NULL); + ASSERT3U(lhp->lh_dev, !=, 0); + ASSERT(MUTEX_HELD(&lhp->lh_lock)); + + /* Set the status first */ + lhp->lh_status = new_status; + + /* Only Offline needs an event */ + if (new_status != LDI_STATUS_OFFLINE) { + dprintf("%s skipping status %d\n", __func__, new_status); + return (0); + } + + dprintf("%s new_status is Offline %d\n", __func__, new_status); + + /* Allocate struct to pass to event callback */ + /* Allocating with lock held, use KM_NOSLEEP */ + sc = (status_change_args_t *)kmem_alloc(sizeof (status_change_args_t), + KM_NOSLEEP); + if (!sc) { + dprintf("%s couldn't allocate callback struct\n", + __func__); + return (ENOMEM); + } + sc->lhp = lhp; + sc->new_status = new_status; + + mutex_exit(&lhp->lh_lock); /* Currently needs to drop lock */ + handle_status_change_callback((void *)sc); + mutex_enter(&lhp->lh_lock); /* Retake before return */ + + return (0); +} + +/* Protected by handle lock */ +int +handle_status_change(struct ldi_handle *lhp, int new_status) +{ + int error; + + /* Validate lh and new_status */ + if (!lhp) { + dprintf("%s missing handle\n", __func__); + return (EINVAL); + } + if (new_status > LDI_STATUS_ONLINE) { + dprintf("%s invalid state %d\n", __func__, new_status); + return (EINVAL); + } + + mutex_enter(&lhp->lh_lock); + error = handle_status_change_locked(lhp, new_status); + mutex_exit(&lhp->lh_lock); + + return (error); +} + +/* Protected by hash list lock */ +void +handle_hold_locked(struct ldi_handle *lhp) +{ +#ifdef DEBUG + int index; + + ASSERT3U(lhp, !=, NULL); + index = LH_HASH(lhp->lh_dev); + ASSERT(MUTEX_HELD(&ldi_handle_hash_lock[index])); +#endif + + /* Increment ref count and kstat */ + lhp->lh_ref++; + LDISTAT_BUMP(handle_refs); +} + +/* Protected by hash list lock */ +void +handle_hold(struct ldi_handle *lhp) +{ + int index; + + ASSERT3U(lhp, !=, NULL); + ASSERT3U(lhp->lh_dev, !=, 0); + + index = LH_HASH(lhp->lh_dev); + mutex_enter(&ldi_handle_hash_lock[index]); + handle_hold_locked(lhp); + mutex_exit(&ldi_handle_hash_lock[index]); +} + +/* + * Locate existing handle in linked list, may return NULL. Optionally places a + * hold on found handle. + */ +static struct ldi_handle * +handle_find_locked(dev_t device, int fmode, boolean_t hold) +{ + struct ldi_handle *retlhp = NULL, *lhp; + int index = LH_HASH(device); + + /* Validate device */ + if (device == 0) { + dprintf("%s invalid device\n", __func__); + return (NULL); + } + /* If fmode is 0, find any handle with matching dev_t */ + + ASSERT(MUTEX_HELD(&ldi_handle_hash_lock[index])); + + /* Iterate over handle hash list */ + for (lhp = list_head(&ldi_handle_hash_list[index]); + lhp != NULL; + lhp = list_next(&ldi_handle_hash_list[index], lhp)) { + /* Check for matching dev_t and fmode (if set) */ + if (lhp->lh_dev != device) { + continue; + } + + /* Special case for find any */ + if (fmode == 0) { + /* Found a match */ + retlhp = lhp; + break; + } + + /* fmode must match write level */ + if (((lhp->lh_fmode & FWRITE) && !(fmode & FWRITE)) || + (!(lhp->lh_fmode & FWRITE) && (fmode & FWRITE))) { + continue; + } + + /* Found a match */ + retlhp = lhp; + break; + } + + /* Take hold, if requested */ + if (hold && retlhp) { + /* Caller asked for hold on found handle */ + handle_hold_locked(retlhp); + } + + return (retlhp); +} + +/* + * Call without lock held to find a handle by dev_t, + * optionally placing a hold on the found handle. + */ +struct ldi_handle * +handle_find(dev_t device, int fmode, boolean_t hold) +{ + struct ldi_handle *lhp; + int index = LH_HASH(device); + + if (device == 0) { + dprintf("%s invalid device\n", __func__); + return (NULL); + } + + /* Lock for duration of find */ + mutex_enter(&ldi_handle_hash_lock[index]); + + /* Find handle by dev_t (with hold) */ + lhp = handle_find_locked(device, fmode, hold); + + /* Unlock and return handle (could be NULL) */ + mutex_exit(&ldi_handle_hash_lock[index]); + return (lhp); +} + +static void +handle_free(struct ldi_handle *lhp) +{ + ASSERT3U(lhp, !=, NULL); + + /* Validate lhp, references, and status */ + if (lhp->lh_ref != 0 || + lhp->lh_status != LDI_STATUS_CLOSED) { + dprintf("%s ref %d status %d\n", __func__, lhp->lh_ref, + lhp->lh_status); + } + + /* Remove notification handler */ + if (handle_remove_notifier(lhp) != 0) { + dprintf("%s lhp %p notifier %s\n", + __func__, lhp, "couldn't be removed"); + } + + /* Destroy condvar and mutex */ + cv_destroy(&lhp->lh_cv); + mutex_destroy(&lhp->lh_lock); + + /* Decrement kstat handle count */ + LDISTAT_BUMPDOWN(handle_count); + /* IOKit or vnode */ + switch (lhp->lh_type) { + case LDI_TYPE_IOKIT: + /* Decrement kstat handle count and free iokit_tsd */ + LDISTAT_BUMPDOWN(handle_count_iokit); + handle_free_iokit(lhp); + break; + + case LDI_TYPE_VNODE: + /* Decrement kstat handle count and free vnode_tsd */ + LDISTAT_BUMPDOWN(handle_count_vnode); + handle_free_vnode(lhp); + break; + default: + dprintf("%s invalid handle type\n", __func__); + break; + } + + /* Deallocate handle */ + dprintf("%s freeing %p\n", __func__, lhp); + kmem_free(lhp, sizeof (struct ldi_handle)); + lhp = 0; +} + +/* + * Remove handle from list, decrementing counters + */ +static void +handle_remove_locked(struct ldi_handle *lhp) +{ + int index; + + ASSERT3U(lhp, !=, NULL); + index = LH_HASH(lhp->lh_dev); + ASSERT(MUTEX_HELD(&ldi_handle_hash_lock[index])); + + /* Remove from list, update handle count */ + list_remove(&ldi_handle_hash_list[index], lhp); + OSDecrementAtomic(&ldi_handle_hash_count); +} + +void +handle_remove(struct ldi_handle *lhp) +{ + int index = LH_HASH(lhp->lh_dev); + + mutex_enter(&ldi_handle_hash_lock[index]); + handle_remove_locked(lhp); + mutex_exit(&ldi_handle_hash_lock[index]); +} + +/* Protected by hash list lock */ +static void +handle_release_locked(struct ldi_handle *lhp) +{ + boolean_t lastrelease = B_FALSE; + +#ifdef DEBUG + ASSERT3U(lhp, !=, NULL); + int index = LH_HASH(lhp->lh_dev); + ASSERT(MUTEX_HELD(&ldi_handle_hash_lock[index])); +#endif + + if (lhp->lh_ref != 0) { + lhp->lh_ref--; + LDISTAT_BUMPDOWN(handle_refs); + } else { + dprintf("%s with 0 refs\n", __func__); + } + + dprintf("%s %x remaining holds\n", __func__, lhp->lh_ref); + + /* If last open ref was dropped */ + lastrelease = (lhp->lh_ref == 0); + + if (lastrelease) { + dprintf("%s removing handle %p from list\n", __func__, lhp); + handle_remove_locked(lhp); + dprintf("%s freeing handle %p\n", __func__, lhp); + handle_free(lhp); + } +} + +/* Protected by hash list lock */ +void +handle_release(struct ldi_handle *lhp) +{ + int index; + + ASSERT3U(lhp, !=, NULL); + index = LH_HASH(lhp->lh_dev); + + mutex_enter(&ldi_handle_hash_lock[index]); + handle_release_locked(lhp); + mutex_exit(&ldi_handle_hash_lock[index]); +} + +/* + * Add new handle to list. + */ +static struct ldi_handle * +handle_add_locked(struct ldi_handle *lhp) +{ + struct ldi_handle *retlhp; + int index = 0; + + ASSERT3U(lhp, !=, NULL); + ASSERT3U(lhp->lh_dev, !=, 0); + + /* Lock should be held */ + index = LH_HASH(lhp->lh_dev); + ASSERT(MUTEX_HELD(&ldi_handle_hash_lock[index])); + + /* Search for existing handle */ + if ((retlhp = handle_find_locked(lhp->lh_dev, lhp->lh_fmode, + B_TRUE)) != NULL) { + dprintf("%s found handle %p\n", __func__, retlhp); + return (retlhp); + } + + /* Insert into list */ + list_insert_head(&ldi_handle_hash_list[index], lhp); + + /* Update handle count */ + OSIncrementAtomic(&ldi_handle_hash_count); + + /* Return success */ + return (lhp); +} + +/* + * Caller should check if returned handle is the same and free new + * handle if an existing handle was returned + */ +struct ldi_handle * +handle_add(struct ldi_handle *lhp) +{ + struct ldi_handle *retlhp; + int index; + + ASSERT3U(lhp, !=, NULL); + index = LH_HASH(lhp->lh_dev); + + mutex_enter(&ldi_handle_hash_lock[index]); + retlhp = handle_add_locked(lhp); + mutex_exit(&ldi_handle_hash_lock[index]); + + return (retlhp); +} + +/* + * Returns a handle with 1 reference and status Closed + */ +#ifdef illumos +static struct ldi_handle * +handle_alloc(vnode_t *vp, struct ldi_ident_t *li) +#else /* illumos */ +struct ldi_handle * +handle_alloc_common(uint_t type, dev_t device, int fmode) +#endif /* !illumos */ +{ + struct ldi_handle *new_lh; + size_t len; + + /* Validate arguments */ + if ((type != LDI_TYPE_IOKIT && type != LDI_TYPE_VNODE) || + device == 0 || fmode == 0) { + dprintf("%s Invalid type %d, device %d, or fmode %d\n", + __func__, type, device, fmode); + return (NULL); + } + + /* Allocate and verify */ + len = sizeof (struct ldi_handle); + if (NULL == (new_lh = (struct ldi_handle *)kmem_alloc(len, + KM_SLEEP))) { + dprintf("%s couldn't allocate ldi_handle\n", __func__); + return (NULL); + } +#ifdef LDI_ZERO + /* Clear the struct for safety */ + bzero(new_lh, len); +#endif + + /* Create handle lock */ + mutex_init(&new_lh->lh_lock, NULL, MUTEX_DEFAULT, NULL); + /* And condvar */ + cv_init(&new_lh->lh_cv, NULL, CV_DEFAULT, NULL); + + /* + * Set the handle type, which dictates the type of device pointer + * and buffers used for the lifetime of the ldi_handle + */ + new_lh->lh_type = type; + /* Set dev_t (major/minor) device number */ + new_lh->lh_dev = device; + + /* Clear list head */ + new_lh->lh_node.list_next = NULL; + new_lh->lh_node.list_prev = NULL; + + /* Initialize with 1 handle ref and 0 open refs */ + new_lh->lh_ref = 1; + new_lh->lh_openref = 0; + + /* Clear type-specific device data */ + new_lh->lh_tsd.iokit_tsd = 0; + /* No need to clear vnode_tsd in union */ + new_lh->lh_notifier = 0; + + /* Assign fmode */ + new_lh->lh_fmode = fmode; + + /* Alloc in status Closed */ + new_lh->lh_status = LDI_STATUS_CLOSED; + + /* Increment kstats */ + LDISTAT_BUMP(handle_count); + LDISTAT_BUMP(handle_refs); + if (type == LDI_TYPE_IOKIT) { + LDISTAT_BUMP(handle_count_iokit); + } else if (type == LDI_TYPE_VNODE) { + LDISTAT_BUMP(handle_count_vnode); + } + + return (new_lh); +} + +static void +handle_set_open_locked(struct ldi_handle *lhp) +{ + ASSERT3U(lhp, !=, NULL); + ASSERT(MUTEX_HELD(&lhp->lh_lock)); + + /* Increment number of open clients */ + lhp->lh_openref++; + + /* Increment kstats */ + if (lhp->lh_fmode & FWRITE) { + LDISTAT_BUMP(handle_open_rw); + } else { + LDISTAT_BUMP(handle_open_ro); + } +} + +#if 0 +static void +handle_set_open(struct ldi_handle *lhp) +{ + ASSERT3U(lhp, !=, NULL); + + mutex_enter(&lhp->lh_lock); + handle_set_open_locked(lhp); + mutex_exit(&lhp->lh_lock); +} +#endif + +static void +handle_clear_open_locked(struct ldi_handle *lhp) +{ + ASSERT3U(lhp, !=, NULL); + ASSERT(MUTEX_HELD(&lhp->lh_lock)); + + /* Decrement number of open clients */ + if (lhp->lh_openref == 0) { + dprintf("%s with 0 open refs\n", __func__); + return; + } + + /* Decrement kstats */ + lhp->lh_openref--; + if (lhp->lh_fmode & FWRITE) { + LDISTAT_BUMPDOWN(handle_open_rw); + } else { + LDISTAT_BUMPDOWN(handle_open_ro); + } +} + +#if 0 +static inline void +handle_clear_open(struct ldi_handle *lhp) +{ + ASSERT3U(lhp, !=, NULL); + ASSERT3U(lhp->lh_dev, !=, 0); + ASSERT3U(lhp->lh_openref, !=, 0); + + mutex_enter(&lhp->lh_lock); + handle_clear_open_locked(lhp, lhp->lh_fmode); + mutex_exit(&lhp->lh_lock); +} +#endif + +static int +handle_close(struct ldi_handle *lhp) +{ +#ifdef DEBUG + int openrefs; +#endif + int error = EINVAL; + + ASSERT3U(lhp, !=, NULL); + ASSERT3U(lhp->lh_ref, !=, 0); + ASSERT3U(lhp->lh_openref, !=, 0); + ASSERT(lhp->lh_type == LDI_TYPE_IOKIT || + lhp->lh_type == LDI_TYPE_VNODE); + + /* Take lock */ + mutex_enter(&lhp->lh_lock); + + /* + * Possible statuses: + * Online with one or more openref + * Offline due to IOMedia termination, one or more openref remain + * Impossible or programming error: + * Closing and Closed should only be set with 0 openref + * Opening should have 0 openref so far, and clients should not be + * calling ldi_close + */ + switch (lhp->lh_status) { + case LDI_STATUS_ONLINE: + if (lhp->lh_openref == 0) { + /* Unlock and return error */ + mutex_exit(&lhp->lh_lock); + /* Shouldn't happen */ + dprintf("%s status Online with 0 openrefs\n", + __func__); + return (ENXIO); + } + + /* If multiple open refs are held */ + if (lhp->lh_openref > 1) { + goto drop_openref; + } + + /* Otherwise open with last open ref */ + /* change status to closing and proceed */ + handle_status_change_locked(lhp, LDI_STATUS_CLOSING); + /* Unlock and exit loop */ + mutex_exit(&lhp->lh_lock); + goto do_close; + + case LDI_STATUS_OFFLINE: + if (lhp->lh_openref == 0) { + /* Unlock and return error */ + mutex_exit(&lhp->lh_lock); + /* Shouldn't happen */ + dprintf("%s status Offline with 0 openrefs\n", + __func__); + return (ENXIO); + } + + /* + * Otherwise the device was marked missing and clients need + * to drop openrefs until it can be released. + */ + goto drop_openref; + + default: + mutex_exit(&lhp->lh_lock); + dprintf("%s invalid handle status %d\n", + __func__, lhp->lh_status); + return (ENXIO); + } + +drop_openref: + /* Just decrement open refs/stats */ + handle_clear_open_locked(lhp); +#ifdef DEBUG + /* Save openrefs to report after unlock */ + openrefs = lhp->lh_openref; +#endif + mutex_exit(&lhp->lh_lock); + +#ifdef DEBUG + dprintf("%s has %d remaining openrefs\n", __func__, openrefs); +#endif + return (0); + +do_close: + /* Remove notification handler */ + if (lhp->lh_notifier) { + error = handle_remove_notifier(lhp); + if (error) { + dprintf("%s lhp %p notifier %p error %d %s\n", + __func__, lhp, lhp->lh_notifier, error, + "couldn't be removed"); + /* Proceeds with close */ + } + } + + /* IOMedia or vnode */ + switch (lhp->lh_type) { + case LDI_TYPE_IOKIT: + error = handle_close_iokit(lhp); + /* Preserve error for return */ + break; + case LDI_TYPE_VNODE: + error = handle_close_vnode(lhp); + /* Preserve error for return */ + break; + } + +#ifdef DEBUG + if (error != 0) { + /* We will still set the handle to Closed status */ + dprintf("%s error %d from handle_close_{type}\n", + __func__, error); + } +#endif + + /* Take lock to drop openref and set status */ + mutex_enter(&lhp->lh_lock); + handle_clear_open_locked(lhp); + handle_status_change_locked(lhp, LDI_STATUS_CLOSED); + + /* Wake any waiting opens and unlock */ + cv_signal(&lhp->lh_cv); + mutex_exit(&lhp->lh_lock); + +dprintf("%s returning %d\n", __func__, error); + return (error); +} + +ldi_status_t +handle_open_start(struct ldi_handle *lhp) +{ + ASSERT3U(lhp, !=, NULL); + ASSERT3U(lhp->lh_ref, !=, 0); + + /* Take lock */ + mutex_enter(&lhp->lh_lock); + /* Loop if the handle is in opening or closing status */ + do { + /* XXX Needs sleep timeout */ + switch (lhp->lh_status) { + case LDI_STATUS_ONLINE: + /* Increment readonly / readwrite count */ + handle_set_open_locked(lhp); + mutex_exit(&lhp->lh_lock); + + /* Success */ + return (LDI_STATUS_ONLINE); + + case LDI_STATUS_CLOSED: + /* Not yet open, change status to opening and proceed */ + handle_status_change_locked(lhp, LDI_STATUS_OPENING); + + /* Unlock and exit loop */ + mutex_exit(&lhp->lh_lock); + /* Return success */ + return (LDI_STATUS_OPENING); + + case LDI_STATUS_OPENING: + case LDI_STATUS_CLOSING: + /* Open or close in progress, sleep until signaled */ + dprintf("%s sleeping on lock\n", __func__); + cv_wait(&lhp->lh_cv, &lhp->lh_lock); + continue; + default: + mutex_exit(&lhp->lh_lock); + dprintf("%s invalid handle status %d\n", + __func__, lhp->lh_status); + return (LDI_STATUS_OFFLINE); + } + } while (1); + + /* Shouldn't reach this */ + return (LDI_STATUS_CLOSED); +} + +void +handle_open_done(struct ldi_handle *lhp, ldi_status_t new_status) +{ + ASSERT3U(lhp, !=, NULL); + ASSERT3U(lhp->lh_status, ==, LDI_STATUS_OPENING); + + /* Lock to change status */ + mutex_enter(&lhp->lh_lock); + + if (new_status != LDI_STATUS_ONLINE) { + /* Set status, issues event */ + handle_status_change_locked(lhp, LDI_STATUS_CLOSED); + } else { + /* Increment open count and fmode */ + handle_set_open_locked(lhp); + /* Set status, issues event */ + handle_status_change_locked(lhp, LDI_STATUS_ONLINE); + } + + /* Wake any waiting opens and unlock */ + cv_signal(&lhp->lh_cv); + mutex_exit(&lhp->lh_lock); + + /* + * Flush out any old buffers remaining from + * a previous use, only if opening read-write. + */ + if (new_status == LDI_STATUS_ONLINE && + (lhp->lh_fmode & FWRITE) && + ldi_sync((ldi_handle_t)lhp) != 0) { + dprintf("%s ldi_sync failed\n", __func__); + } +} + +/* + * Release all remaining handles (during ldi_fini) + * Unless something went wrong, all handles should + * be closed and have zero references. + */ +static void +handle_hash_release() +{ + struct ldi_handle *lhp; + int index, refs, j; + + for (index = 0; index < LH_HASH_SZ; index++) { + mutex_enter(&ldi_handle_hash_lock[index]); + if (!list_empty(&ldi_handle_hash_list[index])) { + dprintf("%s still have LDI handle(s) in list %d\n", + __func__, index); + } + + /* Iterate over the list */ + while ((lhp = list_head(&ldi_handle_hash_list[index]))) { + /* remove from list to deallocate */ + list_remove(&ldi_handle_hash_list[index], lhp); + + /* Update handle count */ + OSDecrementAtomic(&ldi_handle_hash_count); + + dprintf("%s releasing %p with %u refs and status %d\n", + __func__, lhp, lhp->lh_ref, lhp->lh_status); + /* release holds */ + refs = lhp->lh_ref; + for (j = 0; j < refs; j++) { + handle_release_locked(lhp); + } + lhp = 0; + } + + list_destroy(&ldi_handle_hash_list[index]); + mutex_exit(&ldi_handle_hash_lock[index]); + mutex_destroy(&ldi_handle_hash_lock[index]); + } +} + +/* + * LDI Event functions + */ +char * +ldi_ev_get_type(ldi_ev_cookie_t cookie) +{ + int i; + struct ldi_ev_cookie *cookie_impl = (struct ldi_ev_cookie *)cookie; + + for (i = 0; ldi_ev_cookies[i].ck_evname != NULL; i++) { + if (&ldi_ev_cookies[i] == cookie_impl) { + LDI_EVTRC((CE_NOTE, "ldi_ev_get_type: LDI: %s", + ldi_ev_cookies[i].ck_evname)); + return (ldi_ev_cookies[i].ck_evname); + } + } + + return ("UNKNOWN EVENT"); +} + +static int +ldi_native_cookie(ldi_ev_cookie_t cookie) +{ + int i; + struct ldi_ev_cookie *cookie_impl = (struct ldi_ev_cookie *)cookie; + + for (i = 0; ldi_ev_cookies[i].ck_evname != NULL; i++) { + if (&ldi_ev_cookies[i] == cookie_impl) { + LDI_EVTRC((CE_NOTE, "ldi_native_cookie: native LDI")); + return (1); + } + } + + LDI_EVTRC((CE_NOTE, "ldi_native_cookie: is NDI")); + return (0); +} + +static ldi_ev_cookie_t +ldi_get_native_cookie(const char *evname) +{ + int i; + + for (i = 0; ldi_ev_cookies[i].ck_evname != NULL; i++) { + if (strcmp(ldi_ev_cookies[i].ck_evname, evname) == 0) { + LDI_EVTRC((CE_NOTE, "ldi_get_native_cookie: found")); + return ((ldi_ev_cookie_t)&ldi_ev_cookies[i]); + } + } + + LDI_EVTRC((CE_NOTE, "ldi_get_native_cookie: NOT found")); + return (NULL); +} + +/* + * ldi_ev_lock() needs to be recursive, since layered drivers may call + * other LDI interfaces (such as ldi_close() from within the context of + * a notify callback. Since the notify callback is called with the + * ldi_ev_lock() held and ldi_close() also grabs ldi_ev_lock, the lock needs + * to be recursive. + */ +static void +ldi_ev_lock(void) +{ + LDI_EVTRC((CE_NOTE, "ldi_ev_lock: entered")); + + mutex_enter(&ldi_ev_callback_list.le_lock); + if (ldi_ev_callback_list.le_thread == curthread) { + ASSERT(ldi_ev_callback_list.le_busy >= 1); + ldi_ev_callback_list.le_busy++; + } else { + while (ldi_ev_callback_list.le_busy) + cv_wait(&ldi_ev_callback_list.le_cv, + &ldi_ev_callback_list.le_lock); + ASSERT(ldi_ev_callback_list.le_thread == NULL); + ldi_ev_callback_list.le_busy = 1; + ldi_ev_callback_list.le_thread = curthread; + } + mutex_exit(&ldi_ev_callback_list.le_lock); + + LDI_EVTRC((CE_NOTE, "ldi_ev_lock: exit")); +} + +static void +ldi_ev_unlock(void) +{ + LDI_EVTRC((CE_NOTE, "ldi_ev_unlock: entered")); + mutex_enter(&ldi_ev_callback_list.le_lock); + ASSERT(ldi_ev_callback_list.le_thread == curthread); + ASSERT(ldi_ev_callback_list.le_busy >= 1); + + ldi_ev_callback_list.le_busy--; + if (ldi_ev_callback_list.le_busy == 0) { + ldi_ev_callback_list.le_thread = NULL; + cv_signal(&ldi_ev_callback_list.le_cv); + } + mutex_exit(&ldi_ev_callback_list.le_lock); + LDI_EVTRC((CE_NOTE, "ldi_ev_unlock: exit")); +} + +int +ldi_ev_get_cookie(ldi_handle_t lh, char *evname, ldi_ev_cookie_t *cookiep) +{ + ldi_ev_cookie_t tcookie; + + LDI_EVDBG((CE_NOTE, "ldi_ev_get_cookie: entered: evname=%s", + evname ? evname : "")); + + if (lh == NULL || evname == NULL || + strlen(evname) == 0 || cookiep == NULL) { + LDI_EVDBG((CE_NOTE, "ldi_ev_get_cookie: invalid args")); + return (LDI_EV_FAILURE); + } + + *cookiep = NULL; + + /* + * First check if it is a LDI native event + */ + tcookie = ldi_get_native_cookie(evname); + if (tcookie) { + LDI_EVDBG((CE_NOTE, "ldi_ev_get_cookie: got native cookie")); + *cookiep = tcookie; + return (LDI_EV_SUCCESS); + } + + return (LDI_EV_FAILURE); +} + +int +ldi_ev_register_callbacks(ldi_handle_t lh, ldi_ev_cookie_t cookie, + ldi_ev_callback_t *callb, void *arg, ldi_callback_id_t *id) +{ + struct ldi_handle *lhp = (struct ldi_handle *)lh; + ldi_ev_callback_impl_t *lecp; + + if (lh == NULL || cookie == NULL || callb == NULL || id == NULL) { + LDI_EVDBG((CE_NOTE, "ldi_ev_register_callbacks: Invalid args")); + return (LDI_EV_FAILURE); + } + + if (callb->cb_vers != LDI_EV_CB_VERS) { + LDI_EVDBG((CE_NOTE, "ldi_ev_register_callbacks: Invalid vers")); + return (LDI_EV_FAILURE); + } + + if (callb->cb_notify == NULL && callb->cb_finalize == NULL) { + LDI_EVDBG((CE_NOTE, "ldi_ev_register_callbacks: NULL callb")); + return (LDI_EV_FAILURE); + } + + *id = 0; + + lecp = kmem_zalloc(sizeof (ldi_ev_callback_impl_t), KM_SLEEP); + + ldi_ev_lock(); + + /* + * Add the notify/finalize callback to the LDI's list of callbacks. + */ + lecp->lec_lhp = lhp; + + lecp->lec_dev = lhp->lh_dev; + lecp->lec_spec = S_IFBLK; + + lecp->lec_notify = callb->cb_notify; + lecp->lec_finalize = callb->cb_finalize; + lecp->lec_arg = arg; + lecp->lec_cookie = cookie; + + lecp->lec_id = (void *)(uintptr_t)(++ldi_ev_id_pool); + + list_insert_tail(&ldi_ev_callback_list.le_head, lecp); + + *id = (ldi_callback_id_t)lecp->lec_id; + + ldi_ev_unlock(); + + LDI_EVDBG((CE_NOTE, "ldi_ev_register_callbacks: registered " + "notify/finalize")); + + return (LDI_EV_SUCCESS); +} + +static int +ldi_ev_device_match(ldi_ev_callback_impl_t *lecp, __unused dev_info_t *dip, + dev_t dev, int spec_type) +{ + ASSERT(lecp); + ASSERT(dev != DDI_DEV_T_NONE); + ASSERT(dev != NODEV); + ASSERT((dev == DDI_DEV_T_ANY && spec_type == 0) || + (spec_type == S_IFCHR || spec_type == S_IFBLK)); + ASSERT(lecp->lec_spec == S_IFCHR || lecp->lec_spec == S_IFBLK); + ASSERT(lecp->lec_dev != DDI_DEV_T_ANY); + ASSERT(lecp->lec_dev != DDI_DEV_T_NONE); + ASSERT(lecp->lec_dev != NODEV); + + if (dev != DDI_DEV_T_ANY) { + if (dev != lecp->lec_dev || spec_type != lecp->lec_spec) + return (0); + } + + LDI_EVTRC((CE_NOTE, "ldi_ev_device_match: MATCH dev=%d", + (uint32_t)dev)); + + return (1); +} + +/* + * LDI framework function to post a "notify" event to all layered drivers + * that have registered for that event + * + * Returns: + * LDI_EV_SUCCESS - registered callbacks allow event + * LDI_EV_FAILURE - registered callbacks block event + * LDI_EV_NONE - No matching LDI callbacks + * + * This function is *not* to be called by layered drivers. It is for I/O + * framework code in Solaris, such as the I/O retire code and DR code + * to call while servicing a device event such as offline or degraded. + */ +int +ldi_invoke_notify(__unused dev_info_t *dip, dev_t dev, int spec_type, + char *event, void *ev_data) +{ + ldi_ev_callback_impl_t *lecp; + list_t *listp; + int ret; + char *lec_event; + + ASSERT(dev != DDI_DEV_T_NONE); + ASSERT(dev != NODEV); + ASSERT((dev == DDI_DEV_T_ANY && spec_type == 0) || + (spec_type == S_IFCHR || spec_type == S_IFBLK)); + ASSERT(event); + + LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): entered: dip=%p, ev=%s", + (void *)dip, event)); + + ret = LDI_EV_NONE; + ldi_ev_lock(); + + VERIFY(ldi_ev_callback_list.le_walker_next == NULL); + listp = &ldi_ev_callback_list.le_head; + for (lecp = list_head(listp); lecp; lecp = + ldi_ev_callback_list.le_walker_next) { + ldi_ev_callback_list.le_walker_next = list_next(listp, lecp); + + /* Check if matching device */ + if (!ldi_ev_device_match(lecp, dip, dev, spec_type)) + continue; + + if (lecp->lec_lhp == NULL) { + /* + * Consumer has unregistered the handle and so + * is no longer interested in notify events. + */ + LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): No LDI " + "handle, skipping")); + continue; + } + + if (lecp->lec_notify == NULL) { + LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): No notify " + "callback. skipping")); + continue; /* not interested in notify */ + } + + /* + * Check if matching event + */ + lec_event = ldi_ev_get_type(lecp->lec_cookie); + if (strcmp(event, lec_event) != 0) { + LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): Not matching" + " event {%s,%s}. skipping", event, lec_event)); + continue; + } + + lecp->lec_lhp->lh_flags |= LH_FLAGS_NOTIFY; + if (lecp->lec_notify((ldi_handle_t)lecp->lec_lhp, + lecp->lec_cookie, lecp->lec_arg, ev_data) != + LDI_EV_SUCCESS) { + ret = LDI_EV_FAILURE; + LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): notify" + " FAILURE")); + break; + } + + /* We have a matching callback that allows the event to occur */ + ret = LDI_EV_SUCCESS; + + LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): 1 consumer success")); + } + + if (ret != LDI_EV_FAILURE) + goto out; + +#ifdef __APPLE__ + dprintf("%s offline notify failed, shouldn't happen\n", __func__); + goto out; +#endif +#ifdef illumos + LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): undoing notify")); + + /* + * Undo notifies already sent + */ + lecp = list_prev(listp, lecp); + VERIFY(ldi_ev_callback_list.le_walker_prev == NULL); + for (; lecp; lecp = ldi_ev_callback_list.le_walker_prev) { + ldi_ev_callback_list.le_walker_prev = list_prev(listp, lecp); + + /* + * Check if matching device + */ + if (!ldi_ev_device_match(lecp, dip, dev, spec_type)) + continue; + + if (lecp->lec_finalize == NULL) { + LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): no finalize, " + "skipping")); + continue; /* not interested in finalize */ + } + + /* + * it is possible that in response to a notify event a + * layered driver closed its LDI handle so it is ok + * to have a NULL LDI handle for finalize. The layered + * driver is expected to maintain state in its "arg" + * parameter to keep track of the closed device. + */ + + /* Check if matching event */ + lec_event = ldi_ev_get_type(lecp->lec_cookie); + if (strcmp(event, lec_event) != 0) { + LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): not matching " + "event: %s,%s, skipping", event, lec_event)); + continue; + } + + LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): calling finalize")); + + lecp->lec_finalize(lecp->lec_lhp, lecp->lec_cookie, + LDI_EV_FAILURE, lecp->lec_arg, ev_data); + + /* + * If LDI native event and LDI handle closed in context + * of notify, NULL out the finalize callback as we have + * already called the 1 finalize above allowed in this situation + */ + if (lecp->lec_lhp == NULL && + ldi_native_cookie(lecp->lec_cookie)) { + LDI_EVDBG((CE_NOTE, + "ldi_invoke_notify(): NULL-ing finalize after " + "calling 1 finalize following ldi_close")); + lecp->lec_finalize = NULL; + } + } +#endif /* illumos */ + +out: + ldi_ev_callback_list.le_walker_next = NULL; + ldi_ev_callback_list.le_walker_prev = NULL; + ldi_ev_unlock(); + + if (ret == LDI_EV_NONE) { + LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): no matching " + "LDI callbacks")); + } + + return (ret); +} + +/* + * LDI framework function to invoke "finalize" callbacks for all layered + * drivers that have registered callbacks for that event. + * + * This function is *not* to be called by layered drivers. It is for I/O + * framework code in Solaris, such as the I/O retire code and DR code + * to call while servicing a device event such as offline or degraded. + */ +void +ldi_invoke_finalize(__unused dev_info_t *dip, dev_t dev, int spec_type, + char *event, int ldi_result, void *ev_data) +{ + ldi_ev_callback_impl_t *lecp; + list_t *listp; + char *lec_event; + int found = 0; + + ASSERT(dev != DDI_DEV_T_NONE); + ASSERT(dev != NODEV); + ASSERT((dev == DDI_DEV_T_ANY && spec_type == 0) || + (spec_type == S_IFCHR || spec_type == S_IFBLK)); + ASSERT(event); + ASSERT(ldi_result == LDI_EV_SUCCESS || ldi_result == LDI_EV_FAILURE); + + LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): entered: dip=%p, result=%d" + " event=%s", (void *)dip, ldi_result, event)); + + ldi_ev_lock(); + VERIFY(ldi_ev_callback_list.le_walker_next == NULL); + listp = &ldi_ev_callback_list.le_head; + for (lecp = list_head(listp); lecp; lecp = + ldi_ev_callback_list.le_walker_next) { + ldi_ev_callback_list.le_walker_next = list_next(listp, lecp); + + if (lecp->lec_finalize == NULL) { + LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): No " + "finalize. Skipping")); + continue; /* Not interested in finalize */ + } + + /* + * Check if matching device + */ + if (!ldi_ev_device_match(lecp, dip, dev, spec_type)) + continue; + + /* + * It is valid for the LDI handle to be NULL during finalize. + * The layered driver may have done an LDI close in the notify + * callback. + */ + + /* + * Check if matching event + */ + lec_event = ldi_ev_get_type(lecp->lec_cookie); + if (strcmp(event, lec_event) != 0) { + LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): Not " + "matching event {%s,%s}. Skipping", + event, lec_event)); + continue; + } + + LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): calling finalize")); + + found = 1; + + lecp->lec_finalize((ldi_handle_t)lecp->lec_lhp, + lecp->lec_cookie, ldi_result, lecp->lec_arg, + ev_data); + + /* + * If LDI native event and LDI handle closed in context + * of notify, NULL out the finalize callback as we have + * already called the 1 finalize above allowed in this situation + */ + if (lecp->lec_lhp == NULL && + ldi_native_cookie(lecp->lec_cookie)) { + LDI_EVDBG((CE_NOTE, + "ldi_invoke_finalize(): NULLing finalize after " + "calling 1 finalize following ldi_close")); + lecp->lec_finalize = NULL; + } + } + ldi_ev_callback_list.le_walker_next = NULL; + ldi_ev_unlock(); + + if (found) + return; + + LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): no matching callbacks")); +} + +int +ldi_ev_remove_callbacks(ldi_callback_id_t id) +{ + ldi_ev_callback_impl_t *lecp; + ldi_ev_callback_impl_t *next; + ldi_ev_callback_impl_t *found; + list_t *listp; + + if (id == 0) { + cmn_err(CE_WARN, "ldi_ev_remove_callbacks: Invalid ID 0"); + return (LDI_EV_FAILURE); + } + + LDI_EVDBG((CE_NOTE, "ldi_ev_remove_callbacks: entered: id=%p", + (void *)id)); + + ldi_ev_lock(); + + listp = &ldi_ev_callback_list.le_head; + next = found = NULL; + for (lecp = list_head(listp); lecp; lecp = next) { + next = list_next(listp, lecp); + if (lecp->lec_id == id) { + VERIFY(found == NULL); + + /* + * If there is a walk in progress, shift that walk + * along to the next element so that we can remove + * this one. This allows us to unregister an arbitrary + * number of callbacks from within a callback. + * + * See the struct definition (in sunldi_impl.h) for + * more information. + */ + if (ldi_ev_callback_list.le_walker_next == lecp) + ldi_ev_callback_list.le_walker_next = next; + if (ldi_ev_callback_list.le_walker_prev == lecp) + ldi_ev_callback_list.le_walker_prev = list_prev( + listp, ldi_ev_callback_list.le_walker_prev); + + list_remove(listp, lecp); + found = lecp; + } + } + ldi_ev_unlock(); + + if (found == NULL) { + cmn_err(CE_WARN, "No LDI event handler for id (%p)", + (void *)id); + return (LDI_EV_SUCCESS); + } + + LDI_EVDBG((CE_NOTE, "ldi_ev_remove_callbacks: removed " + "LDI native callbacks")); + kmem_free(found, sizeof (ldi_ev_callback_impl_t)); + + return (LDI_EV_SUCCESS); +} +/* + * XXX End LDI Events + */ + +/* Client interface, find IOMedia from dev_t, alloc and open handle */ +int +ldi_open_by_dev(dev_t device, __unused int otyp, int fmode, + __unused cred_t *cred, ldi_handle_t *lhp, + __unused ldi_ident_t ident) +{ + int error = EINVAL; + + dprintf("%s dev_t %d fmode %d\n", __func__, device, fmode); + + /* Validate arguments */ + if (!lhp || device == 0) { + dprintf("%s missing argument %p %d\n", + __func__, lhp, device); + return (EINVAL); + } + /* In debug build, be loud if we potentially leak a handle */ + ASSERT3U(*((struct ldi_handle **)lhp), ==, NULL); + + /* Try to open by media */ + error = ldi_open_media_by_dev(device, fmode, lhp); + + /* Pass error from open */ + return (error); +} + +/* Client interface, find dev_t and IOMedia/vnode, alloc and open handle */ +int +ldi_open_by_name(char *path, int fmode, __unused cred_t *cred, + ldi_handle_t *lhp, __unused ldi_ident_t li) +{ + dev_t device = 0; + int error = EINVAL; + + dprintf("%s dev_t %d fmode %d\n", __func__, device, fmode); + + /* Validate arguments */ + if (!lhp || !path) { + dprintf("%s %s %p %s %d\n", __func__, + "missing lhp or path", lhp, path, fmode); + return (EINVAL); + } + /* In debug build, be loud if we potentially leak a handle */ + ASSERT3U(*((struct ldi_handle **)lhp), ==, NULL); + + /* Validate active open modes */ + if (!ldi_use_iokit_from_path && !ldi_use_dev_from_path && + !ldi_use_vnode_from_path) { + dprintf("%s no valid modes to open device\n", __func__); + return (EINVAL); + } + + /* Try to open IOMedia by path */ + if (ldi_use_iokit_from_path) { + error = ldi_open_media_by_path(path, fmode, lhp); + + /* Error check open */ + if (!error) { + return (0); + } else { + dprintf("%s ldi_open_media_by_path failed\n", + __func__); + /* Not fatal, retry by dev_t or vnode */ + } + } + + /* Get dev_t from path, try to open IOMedia by dev */ + if (ldi_use_dev_from_path) { + /* Uses vnode_lookup */ + device = dev_from_path(path); + if (device == 0) { + dprintf("%s dev_from_path failed %s\n", + __func__, path); + /* + * Both media_from_dev and vnode_from_path will fail + * if dev_from_path fails, since it uses vnode_lookup. + */ + return (ENODEV); + } + + if (ldi_use_iokit_from_dev) { + /* Searches for matching IOMedia */ + error = ldi_open_media_by_dev(device, fmode, lhp); + if (!error) { + return (0); + } else { + dprintf("%s ldi_open_media_by_dev failed %d\n", + __func__, device); + /* Not fatal, retry as vnode */ + } + } + } + + if (!ldi_use_vnode_from_path) { + return (EINVAL); + } + + /* Try to open vnode by path */ + error = ldi_open_vnode_by_path(path, device, fmode, lhp); + if (error) { + dprintf("%s ldi_open_vnode_by_path failed %d\n", __func__, + error); + } + + return (error); +} + +/* Client interface, wrapper for handle_close */ +int +ldi_close(ldi_handle_t lh, int fmode, __unused cred_t *cred) +{ + struct ldi_handle *handlep = (struct ldi_handle *)lh; + int error = EINVAL; + + ASSERT3U(handlep, !=, NULL); + ASSERT3U(handlep->lh_ref, !=, 0); + ASSERT3U(handlep->lh_fmode, ==, fmode); + + dprintf("%s dev_t %d fmode %d\n", __func__, handlep->lh_dev, fmode); + + /* Remove event callbacks */ + boolean_t notify = B_FALSE; + list_t *listp; + ldi_ev_callback_impl_t *lecp; + + /* + * Search the event callback list for callbacks with this + * handle. There are 2 cases + * 1. Called in the context of a notify. The handle consumer + * is releasing its hold on the device to allow a reconfiguration + * of the device. Simply NULL out the handle and the notify callback. + * The finalize callback is still available so that the consumer + * knows of the final disposition of the device. + * 2. Not called in the context of notify. NULL out the handle as well + * as the notify and finalize callbacks. Since the consumer has + * closed the handle, we assume it is not interested in the + * notify and finalize callbacks. + */ + ldi_ev_lock(); + + if (handlep->lh_flags & LH_FLAGS_NOTIFY) + notify = B_TRUE; + listp = &ldi_ev_callback_list.le_head; + for (lecp = list_head(listp); lecp; lecp = list_next(listp, lecp)) { + if (lecp->lec_lhp != handlep) + continue; + lecp->lec_lhp = NULL; + lecp->lec_notify = NULL; + LDI_EVDBG((CE_NOTE, "ldi_close: NULLed lh and notify")); + if (!notify) { + LDI_EVDBG((CE_NOTE, "ldi_close: NULLed finalize")); + lecp->lec_finalize = NULL; + } + } + + if (notify) + handlep->lh_flags &= ~LH_FLAGS_NOTIFY; + ldi_ev_unlock(); + + /* Close device if only one openref, or just decrement openrefs */ + if ((error = handle_close(handlep)) != 0) { + dprintf("%s error from handle_close: %d\n", + __func__, error); + } + + /* Decrement lh_ref, if last ref then remove and free */ + handle_release(handlep); + handlep = 0; + + /* XXX clear pointer arg, and return success? */ + lh = (ldi_handle_t)0; + return (0); + // return (error); +} + +/* + * Client interface, must be in LDI_STATUS_ONLINE + */ +int +ldi_get_size(ldi_handle_t lh, uint64_t *dev_size) +{ + struct ldi_handle *handlep = (struct ldi_handle *)lh; + int error; + + /* + * Ensure we have an LDI handle, and a valid dev_size and/or + * blocksize pointer. Caller must pass at least one of these. + */ + if (!handlep || !dev_size) { + dprintf("%s handle %p\n", __func__, handlep); + dprintf("%s dev_size %p\n", __func__, dev_size); + return (EINVAL); + } + + /* + * Must be in LDI_STATUS_ONLINE + * IOMedia can return getSize without being opened, but vnode + * devices must be opened first. + * Rather than have support differing behaviors, require that + * handle is open to retrieve the size. + */ + if (handlep->lh_status != LDI_STATUS_ONLINE) { + dprintf("%s device not online\n", __func__); + return (ENODEV); + } + + /* IOMedia or vnode */ + switch (handlep->lh_type) { + case LDI_TYPE_IOKIT: + error = handle_get_size_iokit(handlep, dev_size); + return (error); + + case LDI_TYPE_VNODE: + error = handle_get_size_vnode(handlep, dev_size); + return (error); + } + + /* Default case, shouldn't reach this */ + dprintf("%s invalid lh_type %d\n", __func__, + handlep->lh_type); + return (EINVAL); +} + +/* + * Must be in LDI_STATUS_ONLINE + * XXX Needs async callback + */ +int +ldi_sync(ldi_handle_t lh) +{ + struct ldi_handle *handlep = (struct ldi_handle *)lh; + int error; + + /* Ensure we have an LDI handle */ + if (!handlep) { + dprintf("%s no handle\n", __func__); + return (EINVAL); + } + + /* Must be in LDI_STATUS_ONLINE */ + if (handlep->lh_status != LDI_STATUS_ONLINE) { + dprintf("%s device not online\n", __func__); + return (ENODEV); + } + + /* IOMedia or vnode */ + switch (handlep->lh_type) { + case LDI_TYPE_IOKIT: + error = handle_sync_iokit(handlep); + return (error); + + case LDI_TYPE_VNODE: + error = handle_sync_vnode(handlep); + return (error); + } + + /* Default case, shouldn't reach this */ + dprintf("%s invalid lh_type %d\n", __func__, + handlep->lh_type); + return (EINVAL); +} + +int +ldi_ioctl(ldi_handle_t lh, int cmd, intptr_t arg, + __unused int mode, __unused cred_t *cr, __unused int *rvalp) +{ + struct ldi_handle *handlep = (struct ldi_handle *)lh; + int error = EINVAL; + struct dk_callback *dkc; + + switch (cmd) { + /* Flush write cache */ + case DKIOCFLUSHWRITECACHE: + /* IOMedia or vnode */ + switch (handlep->lh_type) { + case LDI_TYPE_IOKIT: + error = handle_sync_iokit(handlep); + break; + + case LDI_TYPE_VNODE: + error = handle_sync_vnode(handlep); + break; + + default: + error = ENOTSUP; + } + + if (!arg) { + return (error); + } + + dkc = (struct dk_callback *)arg; + /* Issue completion callback if set */ + if (dkc->dkc_callback) { + (*dkc->dkc_callback)(dkc->dkc_cookie, error); + } + + return (error); + + /* Set or clear write cache enabled */ + case DKIOCSETWCE: + /* + * There doesn't seem to be a way to do this by vnode, + * so we need to be able to locate an IOMedia and an + * IOBlockStorageDevice provider. + */ + return (handle_set_wce_iokit(handlep, (int *)arg)); + + /* Get media blocksize and block count */ + case DKIOCGMEDIAINFO: + /* IOMedia or vnode */ + switch (handlep->lh_type) { + case LDI_TYPE_IOKIT: + return (handle_get_media_info_iokit(handlep, + (struct dk_minfo *)arg)); + + case LDI_TYPE_VNODE: + return (handle_get_media_info_vnode(handlep, + (struct dk_minfo *)arg)); + + default: + return (ENOTSUP); + } + + /* Get media logical/physical blocksize and block count */ + case DKIOCGMEDIAINFOEXT: + /* IOMedia or vnode */ + switch (handlep->lh_type) { + case LDI_TYPE_IOKIT: + return (handle_get_media_info_ext_iokit(handlep, + (struct dk_minfo_ext *)arg)); + + case LDI_TYPE_VNODE: + return (handle_get_media_info_ext_vnode(handlep, + (struct dk_minfo_ext *)arg)); + + default: + return (ENOTSUP); + } + + /* Check device status */ + case DKIOCSTATE: + /* IOMedia or vnode */ + switch (handlep->lh_type) { + case LDI_TYPE_IOKIT: + return (handle_check_media_iokit(handlep, + (int *)arg)); + + case LDI_TYPE_VNODE: + return (handle_check_media_vnode(handlep, + (int *)arg)); + + default: + return (ENOTSUP); + } + + case DKIOCISSOLIDSTATE: + /* IOMedia or vnode */ + switch (handlep->lh_type) { + case LDI_TYPE_IOKIT: + return (handle_is_solidstate_iokit(handlep, + (int *)arg)); + + case LDI_TYPE_VNODE: + return (handle_is_solidstate_vnode(handlep, + (int *)arg)); + + default: + return (ENOTSUP); + } + + case DKIOCGETBOOTINFO: + /* IOMedia or vnode */ + switch (handlep->lh_type) { + case LDI_TYPE_IOKIT: + return (handle_get_bootinfo_iokit(handlep, + (struct io_bootinfo *)arg)); + + case LDI_TYPE_VNODE: + return (handle_get_bootinfo_vnode(handlep, + (struct io_bootinfo *)arg)); + + default: + return (ENOTSUP); + } + + case DKIOCGETFEATURES: /* UNMAP? */ + /* IOMedia or vnode */ + switch (handlep->lh_type) { + case LDI_TYPE_IOKIT: + return (handle_features_iokit(handlep, + (uint32_t *)arg)); + + case LDI_TYPE_VNODE: + return (handle_features_vnode(handlep, + (uint32_t *)arg)); + + default: + return (ENOTSUP); + } + + case DKIOCFREE: /* UNMAP */ + /* IOMedia or vnode */ + switch (handlep->lh_type) { + case LDI_TYPE_IOKIT: + return (handle_unmap_iokit(handlep, + (dkioc_free_list_ext_t *)arg)); + + case LDI_TYPE_VNODE: + return (handle_unmap_vnode(handlep, + (dkioc_free_list_ext_t *)arg)); + + default: + return (ENOTSUP); + } + + default: + return (ENOTSUP); + } +} + +/* + * Must already have handle_open called on lh. + */ +int +ldi_strategy(ldi_handle_t lh, ldi_buf_t *lbp) +{ + struct ldi_handle *handlep = (struct ldi_handle *)lh; + int error = EINVAL; + + /* Verify arguments */ + if (!handlep || !lbp || lbp->b_bcount == 0) { + dprintf("%s missing something...\n", __func__); + dprintf("handlep [%p]\n", handlep); + dprintf("lbp [%p]\n", lbp); + if (lbp) { + dprintf("lbp->b_bcount %llu\n", + lbp->b_bcount); + } + return (EINVAL); + } + + /* Check instantaneous value of handle status */ + if (handlep->lh_status != LDI_STATUS_ONLINE) { + dprintf("%s device not online\n", __func__); + return (ENODEV); + } + + /* IOMedia or vnode */ + /* Issue type-specific buf_strategy, preserve error */ + switch (handlep->lh_type) { + case LDI_TYPE_IOKIT: + error = buf_strategy_iokit(lbp, handlep); + break; + case LDI_TYPE_VNODE: + error = buf_strategy_vnode(lbp, handlep); + break; + default: + dprintf("%s invalid lh_type %d\n", __func__, handlep->lh_type); + return (EINVAL); + } + + return (error); +} + +/* Client interface to get an LDI buffer */ +ldi_buf_t * +ldi_getrbuf(int flags) +{ +/* Example: bp = getrbuf(KM_SLEEP); */ + ldi_buf_t *lbp; + + /* Allocate with requested flags */ + lbp = kmem_alloc(sizeof (ldi_buf_t), flags); + /* Verify allocation */ + if (!lbp) { + return (NULL); + } + + ldi_bioinit(lbp); + + return (lbp); +} + +/* Client interface to release an LDI buffer */ +void +ldi_freerbuf(ldi_buf_t *lbp) +{ + if (!lbp) { + return; + } + + /* Deallocate */ + kmem_free(lbp, sizeof (ldi_buf_t)); +} + +void +ldi_bioinit(ldi_buf_t *lbp) +{ +#ifdef LDI_ZERO + /* Zero the new buffer struct */ + bzero(lbp, sizeof (ldi_buf_t)); +#endif + + /* Initialize defaults */ + lbp->b_un.b_addr = 0; + lbp->b_flags = 0; + lbp->b_bcount = 0; + lbp->b_bufsize = 0; + lbp->b_lblkno = 0; + lbp->b_resid = 0; + lbp->b_error = 0; +} + +/* + * IOKit C++ functions + */ +int +ldi_init(void *provider) +{ + int index; + + /* Allocate kstat pointer */ + ldi_ksp = kstat_create("zfs", 0, "ldi", "darwin", KSTAT_TYPE_NAMED, + sizeof (ldi_stats) / sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL); + + if (ldi_ksp == NULL) { + dprintf("%s couldn't register kstats\n", __func__); + return (ENOMEM); + } + + /* Register kstats */ + ldi_ksp->ks_data = &ldi_stats; + kstat_install(ldi_ksp); + + /* Register sysctls */ + sysctl_register_oid(&sysctl__ldi); + sysctl_register_oid(&sysctl__ldi_debug); + sysctl_register_oid(&sysctl__ldi_debug_use_iokit_from_path); + sysctl_register_oid(&sysctl__ldi_debug_use_iokit_from_dev); + sysctl_register_oid(&sysctl__ldi_debug_use_dev_from_path); + sysctl_register_oid(&sysctl__ldi_debug_use_vnode_from_path); + + /* Create handle hash lists and locks */ + ldi_handle_hash_count = 0; + for (index = 0; index < LH_HASH_SZ; index++) { + mutex_init(&ldi_handle_hash_lock[index], NULL, + MUTEX_DEFAULT, NULL); + list_create(&ldi_handle_hash_list[index], + sizeof (struct ldi_handle), + offsetof(struct ldi_handle, lh_node)); + } + + /* + * Initialize the LDI event subsystem + */ + mutex_init(&ldi_ev_callback_list.le_lock, NULL, MUTEX_DEFAULT, NULL); + cv_init(&ldi_ev_callback_list.le_cv, NULL, CV_DEFAULT, NULL); + ldi_ev_callback_list.le_busy = 0; + ldi_ev_callback_list.le_thread = NULL; + ldi_ev_callback_list.le_walker_next = NULL; + ldi_ev_callback_list.le_walker_prev = NULL; + list_create(&ldi_ev_callback_list.le_head, + sizeof (ldi_ev_callback_impl_t), + offsetof(ldi_ev_callback_impl_t, lec_list)); + + return (0); +} + +void +ldi_fini() +{ + /* + * Teardown the LDI event subsystem + */ + ldi_ev_lock(); +#ifdef DEBUG + if (ldi_ev_callback_list.le_busy != 1 || + ldi_ev_callback_list.le_thread != curthread || + ldi_ev_callback_list.le_walker_next != NULL || + ldi_ev_callback_list.le_walker_prev != NULL) { + dprintf("%s still has %s %llu %s %p %s %p %s %p\n", __func__, + "le_busy", ldi_ev_callback_list.le_busy, + "le_thread", ldi_ev_callback_list.le_thread, + "le_walker_next", ldi_ev_callback_list.le_walker_next, + "le_walker_prev", ldi_ev_callback_list.le_walker_prev); + } +#endif + list_destroy(&ldi_ev_callback_list.le_head); + ldi_ev_unlock(); +#ifdef DEBUG + ldi_ev_callback_list.le_busy = 0; + ldi_ev_callback_list.le_thread = NULL; + ldi_ev_callback_list.le_walker_next = NULL; + ldi_ev_callback_list.le_walker_prev = NULL; +#endif + + cv_destroy(&ldi_ev_callback_list.le_cv); + mutex_destroy(&ldi_ev_callback_list.le_lock); + + if (ldi_handle_hash_count != 0) { + dprintf("%s ldi_handle_hash_count %llu\n", __func__, + ldi_handle_hash_count); + } + + /* Destroy handle hash lists and locks */ + handle_hash_release(); + + /* Unregister sysctls */ + sysctl_unregister_oid(&sysctl__ldi_debug_use_iokit_from_path); + sysctl_unregister_oid(&sysctl__ldi_debug_use_iokit_from_dev); + sysctl_unregister_oid(&sysctl__ldi_debug_use_dev_from_path); + sysctl_unregister_oid(&sysctl__ldi_debug_use_vnode_from_path); + sysctl_unregister_oid(&sysctl__ldi_debug); + sysctl_unregister_oid(&sysctl__ldi); + + /* Unregister kstats */ + if (ldi_ksp != NULL) { + kstat_delete(ldi_ksp); + ldi_ksp = NULL; + } + + if (ldi_handle_hash_count != 0) { + dprintf("%s handle_hash_count still %llu\n", __func__, + ldi_handle_hash_count); + } +} diff --git a/module/os/macos/zfs/ldi_vnode.c b/module/os/macos/zfs/ldi_vnode.c new file mode 100644 index 000000000000..04accb62338b --- /dev/null +++ b/module/os/macos/zfs/ldi_vnode.c @@ -0,0 +1,1022 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ +/* + * Copyright (c) 2015, Evan Susarret. All rights reserved. + */ +/* + * Portions of this document are copyright Oracle and Joyent. + * OS X implementation of ldi_ named functions for ZFS written by + * Evan Susarret in 2015. + */ + +/* + * ZFS internal + */ +#include + +/* + * LDI Includes + */ +#include + +/* Debug prints */ + +#define ldi_log(fmt, ...) do { \ + dprintf(fmt, __VA_ARGS__); \ + /* delay(hz>>1); */ \ +_NOTE(CONSTCOND) } while (0) + +struct _handle_vnode { + vnode_t *devvp; + char *vd_readlinkname; +}; /* 16b */ + +#define LH_VNODE(lhp) lhp->lh_tsd.vnode_tsd->devvp + +void +handle_free_vnode(struct ldi_handle *lhp) +{ + if (!lhp) { + dprintf("%s missing lhp\n", __func__); + return; + } + + if (!lhp->lh_tsd.vnode_tsd) { + dprintf("%s missing vnode_tsd\n", __func__); + return; + } + + kmem_free(lhp->lh_tsd.vnode_tsd, sizeof (struct _handle_vnode)); + lhp->lh_tsd.vnode_tsd = 0; +} + + +/* Returns handle with lock still held */ +struct ldi_handle * +handle_alloc_vnode(dev_t device, int fmode) +{ + struct ldi_handle *lhp, *retlhp; + + /* Search for existing handle */ + if ((retlhp = handle_find(device, fmode, B_TRUE)) != NULL) { + dprintf("%s found handle before alloc\n", __func__); + return (retlhp); + } + + /* Validate arguments */ + if (device == 0 || fmode == 0) { + dprintf("%s missing dev_t %d or fmode %d\n", + __func__, device, fmode); + return (NULL); + } + + /* Allocate LDI vnode handle */ + if ((lhp = handle_alloc_common(LDI_TYPE_VNODE, device, + fmode)) == NULL) { + dprintf("%s couldn't allocate lhp\n", __func__); + return (NULL); + } + + /* Allocate and clear type-specific device data */ + lhp->lh_tsd.vnode_tsd = (struct _handle_vnode *)kmem_alloc( + sizeof (struct _handle_vnode), KM_SLEEP); + LH_VNODE(lhp) = 0; + + /* Add the handle to the list, or return match */ + if ((retlhp = handle_add(lhp)) == NULL) { + dprintf("%s handle_add failed\n", __func__); + handle_release(lhp); + return (NULL); + } + + /* Check if new or found handle was returned */ + if (retlhp != lhp) { + dprintf("%s found handle after alloc\n", __func__); + handle_release(lhp); + lhp = 0; + } + + return (retlhp); +} + +int +handle_close_vnode(struct ldi_handle *lhp) +{ + vfs_context_t context; + int error = EINVAL; + + ASSERT3U(lhp, !=, NULL); + ASSERT3U(lhp->lh_type, ==, LDI_TYPE_VNODE); + ASSERT3U(LH_VNODE(lhp), !=, NULL); + ASSERT3U(lhp->lh_status, ==, LDI_STATUS_CLOSING); + +#ifdef DEBUG + /* Validate vnode and context */ + if (LH_VNODE(lhp) == NULLVP) { + dprintf("%s missing vnode\n", __func__); + return (ENODEV); + } +#endif + + context = vfs_context_create(spl_vfs_context_kernel()); + if (!context) { + dprintf("%s couldn't create VFS context\n", __func__); + return (ENOMEM); + } + + /* Take an iocount on devvp vnode. */ + error = vnode_getwithref(LH_VNODE(lhp)); + if (error) { + dprintf("%s vnode_getwithref error %d\n", + __func__, error); + /* If getwithref failed, we can't call vnode_close. */ + LH_VNODE(lhp) = NULLVP; + vfs_context_rele(context); + return (ENODEV); + } + /* All code paths from here must vnode_put. */ + + /* For read-write, clear mountedon flag and wait for writes */ + if (lhp->lh_fmode & FWRITE) { + /* Wait for writes to complete */ + error = vnode_waitforwrites(LH_VNODE(lhp), 0, 0, 0, + "ldi::handle_close_vnode"); + if (error != 0) { + dprintf("%s waitforwrites returned %d\n", + __func__, error); + } + } + + /* Drop usecount */ + vnode_rele(LH_VNODE(lhp)); + + /* Drop iocount and refcount */ + error = vnode_close(LH_VNODE(lhp), + (lhp->lh_fmode & FWRITE ? FWASWRITTEN : 0), + context); + /* Preserve error from vnode_close */ + + /* Clear handle devvp vnode pointer */ + LH_VNODE(lhp) = NULLVP; + /* Drop VFS context */ + vfs_context_rele(context); + + if (error) { + dprintf("%s vnode_close error %d\n", + __func__, error); + } + /* Return error from close */ + return (error); +} + +static int +handle_open_vnode(struct ldi_handle *lhp, char *path) +{ + vfs_context_t context; + int error = EINVAL; + + ASSERT3U(lhp, !=, NULL); + ASSERT3U(path, !=, NULL); + ASSERT3U(lhp->lh_type, ==, LDI_TYPE_VNODE); + ASSERT3U(lhp->lh_status, ==, LDI_STATUS_OPENING); + + /* Validate path string */ + if (!path || strlen(path) <= 1) { + dprintf("%s missing path\n", __func__); + return (EINVAL); + } + + /* Allocate and validate context */ + context = vfs_context_create(spl_vfs_context_kernel()); + if (!context) { + dprintf("%s couldn't create VFS context\n", __func__); + return (ENOMEM); + } + + /* Try to open the device by path (takes iocount) */ + error = vnode_open(path, lhp->lh_fmode, 0, 0, + &(LH_VNODE(lhp)), context); + + if (error) { + dprintf("%s vnode_open error %d\n", __func__, error); + /* Return error from vnode_open */ + return (error); + } + + /* Increase usecount, saving error. */ + error = vnode_ref(LH_VNODE(lhp)); + if (error != 0) { + dprintf("%s couldn't vnode_ref\n", __func__); + vnode_close(LH_VNODE(lhp), lhp->lh_fmode, context); + /* Return error from vnode_ref */ + return (error); + } + + /* Verify vnode refers to a block device */ + if (!vnode_isblk(LH_VNODE(lhp))) { + dprintf("%s %s is not a block device\n", + __func__, path); + vnode_rele(LH_VNODE(lhp)); + vnode_close(LH_VNODE(lhp), lhp->lh_fmode, context); + return (ENOTBLK); + } + + /* Drop iocount on vnode (still has usecount) */ + vnode_put(LH_VNODE(lhp)); + /* Drop VFS context */ + vfs_context_rele(context); + + return (0); +} + +int +handle_get_size_vnode(struct ldi_handle *lhp, uint64_t *dev_size) +{ + vfs_context_t context; + uint64_t blkcnt = 0; + uint32_t blksize = 0; + int error = EINVAL; + +#ifdef DEBUG + if (!lhp || !dev_size) { + dprintf("%s missing lhp or dev_size\n", __func__); + return (EINVAL); + } + + /* Validate vnode */ + if (LH_VNODE(lhp) == NULLVP) { + dprintf("%s missing vnode\n", __func__); + return (ENODEV); + } +#endif + + /* Allocate and validate context */ + context = vfs_context_create(spl_vfs_context_kernel()); + if (!context) { + dprintf("%s couldn't create VFS context\n", __func__); + return (ENOMEM); + } + + /* Take an iocount on devvp vnode. */ + error = vnode_getwithref(LH_VNODE(lhp)); + if (error) { + dprintf("%s vnode_getwithref error %d\n", + __func__, error); + vfs_context_rele(context); + return (ENODEV); + } + /* All code paths from here must vnode_put. */ + + /* Fetch the blocksize */ + error = VNOP_IOCTL(LH_VNODE(lhp), DKIOCGETBLOCKSIZE, + (caddr_t)&blksize, 0, context); + error = (blksize == 0 ? ENODEV : error); + + /* Fetch the block count */ + error = (error ? error : VNOP_IOCTL(LH_VNODE(lhp), + DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt, + 0, context)); + error = (blkcnt == 0 ? ENODEV : error); + + /* Release iocount on vnode (still has usecount) */ + vnode_put(LH_VNODE(lhp)); + /* Drop VFS context */ + vfs_context_rele(context); + + /* Cast both to 64-bit then multiply */ + *dev_size = ((uint64_t)blksize * (uint64_t)blkcnt); + if (*dev_size == 0) { + dprintf("%s invalid blksize %u or blkcnt %llu\n", + __func__, blksize, blkcnt); + return (ENODEV); + } + return (0); +} + +int +handle_get_dev_path_vnode(struct ldi_handle *lhp, char *path, int len) +{ + vfs_context_t context; + int error; + + if (!lhp || !path || len == 0) { + dprintf("%s missing argument\n", __func__); + return (EINVAL); + } + + /* Validate vnode */ + if (LH_VNODE(lhp) == NULLVP) { + dprintf("%s missing vnode\n", __func__); + return (ENODEV); + } + + /* Allocate and validate context */ + context = vfs_context_create(spl_vfs_context_kernel()); + if (!context) { + dprintf("%s couldn't create VFS context\n", __func__); + return (ENOMEM); + } + + /* Take an iocount on devvp vnode. */ + error = vnode_getwithref(LH_VNODE(lhp)); + if (error) { + dprintf("%s vnode_getwithref error %d\n", + __func__, error); + vfs_context_rele(context); + return (ENODEV); + } + /* All code paths from here must vnode_put. */ + + if ((error = VNOP_IOCTL(LH_VNODE(lhp), DKIOCGETFIRMWAREPATH, + (caddr_t)path, len, context)) != 0) { + dprintf("%s VNOP_IOCTL error %d\n", __func__, error); + /* Preserve error to return */ + } + + /* Drop iocount on vnode (still has usecount) */ + vnode_put(LH_VNODE(lhp)); + /* Drop VFS context */ + vfs_context_rele(context); + +if (error == 0) dprintf("%s got device path [%s]\n", __func__, path); + return (error); +} + +int +handle_get_bootinfo_vnode(struct ldi_handle *lhp, + struct io_bootinfo *bootinfo) +{ + int error; + + if (!lhp || !bootinfo) { + dprintf("%s missing argument\n", __func__); +printf("%s missing argument\n", __func__); + return (EINVAL); + } + + if ((error = handle_get_size_vnode(lhp, + &bootinfo->dev_size)) != 0 || + (error = handle_get_dev_path_vnode(lhp, bootinfo->dev_path, + sizeof (bootinfo->dev_path))) != 0) { + dprintf("%s get size or dev_path error %d\n", + __func__, error); + } + + return (error); +} + +int +handle_sync_vnode(struct ldi_handle *lhp) +{ + vfs_context_t context; + int error = EINVAL; + +#ifdef DEBUG + if (!lhp) { + dprintf("%s missing lhp\n", __func__); + return (EINVAL); + } + + /* Validate vnode */ + if (LH_VNODE(lhp) == NULLVP) { + dprintf("%s missing vnode\n", __func__); + return (ENODEV); + } +#endif + + /* Allocate and validate context */ + context = vfs_context_create(spl_vfs_context_kernel()); + if (!context) { + dprintf("%s couldn't create VFS context\n", __func__); + return (ENOMEM); + } + + /* Take an iocount on devvp vnode. */ + error = vnode_getwithref(LH_VNODE(lhp)); + if (error) { + dprintf("%s vnode_getwithref error %d\n", + __func__, error); + vfs_context_rele(context); + return (ENODEV); + } + /* All code paths from here must vnode_put. */ + + /* + * Flush out any old buffers remaining from a previous use. + * buf_invalidateblks flushes UPL buffers, VNOP_FSYNC informs + * the disk device to flush write buffers to disk. + */ + error = buf_invalidateblks(LH_VNODE(lhp), BUF_WRITE_DATA, 0, 0); + + error = (error ? error : VNOP_FSYNC(LH_VNODE(lhp), + MNT_WAIT, context)); + + /* Release iocount on vnode (still has usecount) */ + vnode_put(LH_VNODE(lhp)); + /* Drop VFS context */ + vfs_context_rele(context); + + if (error) { + dprintf("%s buf_invalidateblks or VNOP_FSYNC error %d\n", + __func__, error); + return (ENOTSUP); + } + return (0); +} + +/* vnode_lookup, find dev_t info */ +dev_t +dev_from_path(char *path) +{ + vfs_context_t context; + vnode_t *devvp = NULLVP; + dev_t device; + int error = EINVAL; + +#ifdef DEBUG + /* Validate path */ + if (path == 0 || strlen(path) <= 1 || path[0] != '/') { + dprintf("%s invalid path provided\n", __func__); + return (0); + } +#endif + + /* Allocate and validate context */ + context = vfs_context_create(spl_vfs_context_kernel()); + if (!context) { + dprintf("%s couldn't create VFS context\n", __func__); + return (0); + } + + /* Try to lookup the vnode by path */ + error = vnode_lookup(path, 0, &devvp, context); + if (error || devvp == NULLVP) { + dprintf("%s vnode_lookup failed %d\n", __func__, error); + vfs_context_rele(context); + return (0); + } + + /* Get the rdev of this vnode */ + device = vnode_specrdev(devvp); + + /* Drop iocount on devvp */ + vnode_put(devvp); + /* Drop vfs_context */ + vfs_context_rele(context); + +#ifdef DEBUG + /* Validate dev_t */ + if (device == 0) { + dprintf("%s invalid device\n", __func__); + } +#endif + + /* Return 0 or valid dev_t */ + return (device); +} + +/* Completion handler for vnode strategy */ +static void +ldi_vnode_io_intr(buf_t bp, void *arg) +{ + ldi_buf_t *lbp = (ldi_buf_t *)arg; + + ASSERT3U(bp, !=, NULL); + ASSERT3U(lbp, !=, NULL); + + /* Copyout error and resid */ + lbp->b_error = buf_error(bp); + lbp->b_resid = buf_resid(bp); + +#ifdef DEBUG + if (lbp->b_error || lbp->b_resid != 0) { + dprintf("%s io error %d resid %llu\n", __func__, + lbp->b_error, lbp->b_resid); + } +#endif + + /* Teardown */ + buf_free(bp); + + /* Call original completion function */ + if (lbp->b_iodone) { + lbp->b_iodone(lbp); + } +} + +int +buf_strategy_vnode(ldi_buf_t *lbp, struct ldi_handle *lhp) +{ + buf_t bp = 0; + int error = EINVAL; + + ASSERT3U(lbp, !=, NULL); + ASSERT3U(lhp, !=, NULL); + +#ifdef DEBUG + if (!lbp || !lhp) { + dprintf("%s missing lbp or lhp\n", __func__); + return (EINVAL); + } + if (lhp->lh_status != LDI_STATUS_ONLINE) { + dprintf("%s handle is not Online\n", __func__); + return (ENODEV); + } + + /* Validate vnode */ + if (LH_VNODE(lhp) == NULLVP) { + dprintf("%s missing vnode\n", __func__); + return (ENODEV); + } +#endif + + /* Allocate and verify buf_t */ + if (NULL == (bp = buf_alloc(LH_VNODE(lhp)))) { + dprintf("%s couldn't allocate buf_t\n", __func__); + return (ENOMEM); + } + + /* Setup buffer */ + buf_setflags(bp, B_NOCACHE | (lbp->b_flags & B_READ ? + B_READ : B_WRITE) | + (lbp->b_flags & (B_PASSIVE | B_PHYS | B_RAW)) | + ((lbp->b_iodone == NULL) ? 0: B_ASYNC)); + buf_setcount(bp, lbp->b_bcount); + buf_setdataptr(bp, (uintptr_t)lbp->b_un.b_addr); + buf_setblkno(bp, lbp->b_lblkno); + buf_setlblkno(bp, lbp->b_lblkno); + buf_setsize(bp, lbp->b_bufsize); + + /* For asynchronous IO */ + if (lbp->b_iodone != NULL) { + buf_setcallback(bp, &ldi_vnode_io_intr, lbp); + } + + /* Recheck instantaneous value of handle status */ + if (lhp->lh_status != LDI_STATUS_ONLINE) { + dprintf("%s device not online\n", __func__); + buf_free(bp); + return (ENODEV); + } + + /* Take an iocount on devvp vnode. */ + error = vnode_getwithref(LH_VNODE(lhp)); + if (error) { + dprintf("%s vnode_getwithref error %d\n", + __func__, error); + buf_free(bp); + return (ENODEV); + } + /* All code paths from here must vnode_put. */ + + if (!(lbp->b_flags & B_READ)) { + /* Does not return an error status */ + vnode_startwrite(LH_VNODE(lhp)); + } + + + + /* Issue the IO, preserving error */ + error = VNOP_STRATEGY(bp); + + if (error) { + dprintf("%s VNOP_STRATEGY error %d\n", + __func__, error); + /* Reclaim write count on vnode */ + if (!(lbp->b_flags & B_READ)) { + vnode_writedone(LH_VNODE(lhp)); + } + vnode_put(LH_VNODE(lhp)); + buf_free(bp); + return (EIO); + } + + /* Release iocount on vnode (still has usecount) */ + vnode_put(LH_VNODE(lhp)); + + /* For synchronous IO, call completion */ + if (lbp->b_iodone == NULL) { + ldi_vnode_io_intr(bp, (void*)lbp); + } + + /* Pass error from VNOP_STRATEGY */ + return (error); +} + +/* Client interface, alloc and open vnode handle by pathname */ +int +ldi_open_vnode_by_path(char *path, dev_t device, + int fmode, ldi_handle_t *lhp) +{ + struct ldi_handle *retlhp; + ldi_status_t status; + int error = EIO; + + /* Validate arguments */ + if (!path || strlen(path) <= 1 || device == 0 || !lhp) { + dprintf("%s invalid argument %p %d %p\n", __func__, + path, device, lhp); + if (path) { + dprintf("*path string is %s\n", path); + } + return (EINVAL); + } + /* In debug build, be loud if we potentially leak a handle */ + ASSERT3U(*(struct ldi_handle **)lhp, ==, NULL); + + /* Allocate handle with path */ + retlhp = handle_alloc_vnode(device, fmode); + if (retlhp == NULL) { + dprintf("%s couldn't allocate vnode handle\n", __func__); + return (ENOMEM); + } + + /* Mark the handle as Opening, or increment openref */ + status = handle_open_start(retlhp); + if (status == LDI_STATUS_ONLINE) { + dprintf("%s already online, refs %d, openrefs %d\n", __func__, + retlhp->lh_ref, retlhp->lh_openref); + /* Cast retlhp and assign to lhp (may be 0) */ + *lhp = (ldi_handle_t)retlhp; + /* Successfully incremented open ref in open_start */ + return (0); + } + + /* If state is now Opening, try to open device by vnode */ + if (status != LDI_STATUS_OPENING || + (error = handle_open_vnode(retlhp, path)) != 0) { + dprintf("%s Couldn't open handle\n", __func__); + handle_open_done(retlhp, LDI_STATUS_CLOSED); + handle_release(retlhp); + retlhp = 0; + return ((error == EACCES) ? EROFS:EIO); + } + handle_open_done(retlhp, LDI_STATUS_ONLINE); + + /* Register for disk notifications */ + handle_register_notifier(retlhp); + + /* Cast retlhp and assign to lhp (may be 0) */ + *lhp = (ldi_handle_t)retlhp; + /* Pass error from open */ + return (error); +} + +int +handle_get_media_info_vnode(struct ldi_handle *lhp, + struct dk_minfo *dkm) +{ + vfs_context_t context; + uint32_t blksize; + uint64_t blkcount; + int error; + +#ifdef DEBUG + if (!lhp || !dkm) { + dprintf("%s missing lhp or dkm\n", __func__); + return (EINVAL); + } + if (lhp->lh_status != LDI_STATUS_ONLINE) { + dprintf("%s handle is not Online\n", __func__); + return (ENODEV); + } + + /* Validate vnode */ + if (LH_VNODE(lhp) == NULLVP) { + dprintf("%s missing vnode\n", __func__); + return (ENODEV); + } +#endif + + /* Allocate and validate context */ + context = vfs_context_create(spl_vfs_context_kernel()); + if (!context) { + dprintf("%s couldn't create VFS context\n", __func__); + return (0); + } + + /* Take an iocount on devvp vnode. */ + error = vnode_getwithref(LH_VNODE(lhp)); + if (error) { + dprintf("%s vnode_getwithref error %d\n", + __func__, error); + vfs_context_rele(context); + return (ENODEV); + } + /* All code paths from here must vnode_put. */ + + /* Get the blocksize and block count */ + error = VNOP_IOCTL(LH_VNODE(lhp), DKIOCGETBLOCKSIZE, + (caddr_t)&blksize, 0, context); + error = (error ? error : VNOP_IOCTL(LH_VNODE(lhp), + DKIOCGETBLOCKCOUNT, (caddr_t)&blkcount, + 0, context)); + + /* Release iocount on vnode (still has usecount) */ + vnode_put(LH_VNODE(lhp)); + /* Drop vfs_context */ + vfs_context_rele(context); + + if (error) { + dkm->dki_capacity = 0; + dkm->dki_lbsize = 0; + return (error); + } + + /* If successful, set return values */ + dkm->dki_capacity = blkcount; + dkm->dki_lbsize = blksize; + return (0); +} + +int +handle_get_media_info_ext_vnode(struct ldi_handle *lhp, + struct dk_minfo_ext *dkmext) +{ + vfs_context_t context; + uint32_t blksize, pblksize; + uint64_t blkcount; + int error; + +#ifdef DEBUG + if (!lhp || !dkmext) { + dprintf("%s missing lhp or dkmext\n", __func__); + return (EINVAL); + } + if (lhp->lh_status != LDI_STATUS_ONLINE) { + dprintf("%s handle is not Online\n", __func__); + return (ENODEV); + } + + /* Validate vnode and context */ + if (LH_VNODE(lhp) == NULLVP) { + dprintf("%s missing vnode or context\n", __func__); + return (ENODEV); + } +#endif + + /* Allocate and validate context */ + context = vfs_context_create(spl_vfs_context_kernel()); + if (!context) { + dprintf("%s couldn't create VFS context\n", __func__); + return (0); + } + + /* Take an iocount on devvp vnode. */ + error = vnode_getwithref(LH_VNODE(lhp)); + if (error) { + dprintf("%s vnode_getwithref error %d\n", + __func__, error); + vfs_context_rele(context); + return (ENODEV); + } + /* All code paths from here must vnode_put. */ + + /* Get the blocksize, physical blocksize, and block count */ + error = VNOP_IOCTL(LH_VNODE(lhp), DKIOCGETBLOCKSIZE, + (caddr_t)&blksize, 0, context); + error = (error ? error : VNOP_IOCTL(LH_VNODE(lhp), + DKIOCGETPHYSICALBLOCKSIZE, (caddr_t)&pblksize, + 0, context)); + error = (error ? error : VNOP_IOCTL(LH_VNODE(lhp), + DKIOCGETBLOCKCOUNT, (caddr_t)&blkcount, + 0, context)); + + /* Release iocount on vnode (still has usecount) */ + vnode_put(LH_VNODE(lhp)); + /* Drop vfs_context */ + vfs_context_rele(context); + + if (error) { + dkmext->dki_capacity = 0; + dkmext->dki_lbsize = 0; + dkmext->dki_pbsize = 0; + return (error); + } + + /* If successful, set return values */ + dkmext->dki_capacity = blkcount; + dkmext->dki_lbsize = blksize; + dkmext->dki_pbsize = pblksize; + return (0); +} + +int +handle_check_media_vnode(struct ldi_handle *lhp, int *status) +{ + if (!lhp || !status) { + dprintf("%s missing lhp or invalid status\n", __func__); + return (EINVAL); + } + + /* Validate vnode and context */ + if (LH_VNODE(lhp) == NULLVP) { + dprintf("%s missing vnode\n", __func__); + return (ENODEV); + } + + /* XXX As yet unsupported */ + return (ENOTSUP); + + /* Check if the device is available and responding */ + return (0); +} + +int +handle_is_solidstate_vnode(struct ldi_handle *lhp, int *isssd) +{ + vfs_context_t context; + int error; + + if (!lhp || !isssd) { + dprintf("%s missing lhp or invalid status\n", __func__); + return (EINVAL); + } + + /* Validate vnode */ + if (LH_VNODE(lhp) == NULLVP) { + dprintf("%s missing vnode\n", __func__); + return (ENODEV); + } + + /* Allocate and validate context */ + context = vfs_context_create(spl_vfs_context_kernel()); + if (!context) { + dprintf("%s couldn't create VFS context\n", __func__); + return (ENOMEM); + } + + /* Take an iocount on devvp vnode. */ + error = vnode_getwithref(LH_VNODE(lhp)); + if (error) { + dprintf("%s vnode_getwithref error %d\n", + __func__, error); + vfs_context_rele(context); + return (ENODEV); + } + /* All code paths from here must vnode_put. */ + + error = VNOP_IOCTL(LH_VNODE(lhp), DKIOCISSOLIDSTATE, + (caddr_t)isssd, 0, context); + + /* Release iocount on vnode (still has usecount) */ + vnode_put(LH_VNODE(lhp)); + /* Drop vfs_context */ + vfs_context_rele(context); + + return (error); +} + +int +handle_features_vnode(struct ldi_handle *lhp, + uint32_t *features) +{ + vfs_context_t context; + int error; + +#ifdef DEBUG + if (lhp->lh_status != LDI_STATUS_ONLINE) { + dprintf("%s handle is not Online\n", __func__); + return (ENODEV); + } + + /* Validate vnode */ + if (LH_VNODE(lhp) == NULLVP) { + dprintf("%s missing vnode\n", __func__); + return (ENODEV); + } +#endif + + /* Allocate and validate context */ + context = vfs_context_create(spl_vfs_context_kernel()); + if (!context) { + dprintf("%s couldn't create VFS context\n", __func__); + return (0); + } + + /* Take an iocount on devvp vnode. */ + error = vnode_getwithref(LH_VNODE(lhp)); + if (error) { + dprintf("%s vnode_getwithref error %d\n", + __func__, error); + vfs_context_rele(context); + return (ENODEV); + } + + /* All code paths from here must vnode_put. */ + + error = VNOP_IOCTL(LH_VNODE(lhp), DKIOCGETFEATURES, + (caddr_t)features, 0, context); + + if (error) { + printf("%s: 0x%x\n", __func__, error); + } + + /* Release iocount on vnode (still has usecount) */ + vnode_put(LH_VNODE(lhp)); + /* Drop vfs_context */ + vfs_context_rele(context); + + return (error); +} + +int +handle_unmap_vnode(struct ldi_handle *lhp, + dkioc_free_list_ext_t *dkm) +{ + vfs_context_t context; + int error; + +#ifdef DEBUG + if (!lhp || !dkm) { + dprintf("%s missing lhp or dkm\n", __func__); + return (EINVAL); + } + if (lhp->lh_status != LDI_STATUS_ONLINE) { + dprintf("%s handle is not Online\n", __func__); + return (ENODEV); + } + + /* Validate vnode */ + if (LH_VNODE(lhp) == NULLVP) { + dprintf("%s missing vnode\n", __func__); + return (ENODEV); + } +#endif + + /* Allocate and validate context */ + context = vfs_context_create(spl_vfs_context_kernel()); + if (!context) { + dprintf("%s couldn't create VFS context\n", __func__); + return (0); + } + + /* Take an iocount on devvp vnode. */ + error = vnode_getwithref(LH_VNODE(lhp)); + if (error) { + dprintf("%s vnode_getwithref error %d\n", + __func__, error); + vfs_context_rele(context); + return (ENODEV); + } + /* All code paths from here must vnode_put. */ + + /* We need to convert illumos' dkioc_free_list_t to dk_unmap_t */ + /* We only support 1 entry now */ + dk_unmap_t dkun = { 0 }; + dk_extent_t ext; + dkun.extentsCount = 1; + dkun.extents = &ext; + ext.offset = dkm->dfle_start; + ext.length = dkm->dfle_length; + + /* + * dkm->dfl_flags vs dkun.options + * #define DF_WAIT_SYNC 0x00000001 Wait for full write-out of free. + * #define _DK_UNMAP_INITIALIZE 0x00000100 + */ + + /* issue unmap */ + error = VNOP_IOCTL(LH_VNODE(lhp), DKIOCUNMAP, + (caddr_t)&dkun, 0, context); + + if (error) { + dprintf("%s unmap: 0x%x for off %llx size %llx\n", __func__, + error, ext.offset, ext.length); + } + + /* Release iocount on vnode (still has usecount) */ + vnode_put(LH_VNODE(lhp)); + /* Drop vfs_context */ + vfs_context_rele(context); + + return (error); +} diff --git a/module/os/macos/zfs/policy.c b/module/os/macos/zfs/policy.c new file mode 100644 index 000000000000..5525302266c7 --- /dev/null +++ b/module/os/macos/zfs/policy.c @@ -0,0 +1,354 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013, Joyent, Inc. All rights reserved. + * Copyright (C) 2016 Lawrence Livermore National Security, LLC. + * + * For Linux the vast majority of this enforcement is already handled via + * the standard Linux VFS permission checks. However certain administrative + * commands which bypass the standard mechanisms may need to make use of + * this functionality. + */ + +#include +#include +#include + +/* + * The passed credentials cannot be directly verified because Linux only + * provides and interface to check the *current* process credentials. In + * order to handle this the capable() test is only run when the passed + * credentials match the current process credentials or the kcred. In + * all other cases this function must fail and return the passed err. + */ +static int +priv_policy_ns(const cred_t *cr, int capability, boolean_t all, int err, + struct user_namespace *ns) +{ + ASSERT3S(all, ==, B_FALSE); + + if (cr != CRED() && (cr != kcred)) + return (err); + +#if defined(CONFIG_USER_NS) + if (!(ns ? ns_capable(ns, capability) : capable(capability))) +#else + if (!capable(capability)) +#endif + return (err); + + return (0); +} + +static int +priv_policy(const cred_t *cr, int capability, boolean_t all, int err) +{ + return (priv_policy_ns(cr, capability, all, err, NULL)); +} + +static int +priv_policy_user(const cred_t *cr, int capability, boolean_t all, int err) +{ + /* + * All priv_policy_user checks are preceded by kuid/kgid_has_mapping() + * checks. If we cannot do them, we shouldn't be using ns_capable() + * since we don't know whether the affected files are valid in our + * namespace. + */ +#if defined(CONFIG_USER_NS) + return (priv_policy_ns(cr, capability, all, err, cr->user_ns)); +#else + return (priv_policy_ns(cr, capability, all, err, NULL)); +#endif +} + +/* + * Checks for operations that are either client-only or are used by + * both clients and servers. + */ +int +secpolicy_nfs(const cred_t *cr) +{ + return (priv_policy(cr, CAP_SYS_ADMIN, B_FALSE, EPERM)); +} + +/* + * Catch all system configuration. + */ +int +secpolicy_sys_config(const cred_t *cr, boolean_t checkonly) +{ + return (priv_policy(cr, CAP_SYS_ADMIN, B_FALSE, EPERM)); +} + +/* + * Like secpolicy_vnode_access() but we get the actual wanted mode and the + * current mode of the file, not the missing bits. + * + * Enforced in the Linux VFS. + */ +int +secpolicy_vnode_access2(const cred_t *cr, struct inode *ip, uid_t owner, + mode_t curmode, mode_t wantmode) +{ + return (0); +} + +/* + * This is a special routine for ZFS; it is used to determine whether + * any of the privileges in effect allow any form of access to the + * file. There's no reason to audit this or any reason to record + * this. More work is needed to do the "KPLD" stuff. + */ +int +secpolicy_vnode_any_access(const cred_t *cr, struct inode *ip, uid_t owner) +{ + if (crgetfsuid(cr) == owner) + return (0); + + if (inode_owner_or_capable(ip)) + return (0); + +#if defined(CONFIG_USER_NS) + if (!kuid_has_mapping(cr->user_ns, SUID_TO_KUID(owner))) + return (EPERM); +#endif + + if (priv_policy_user(cr, CAP_DAC_OVERRIDE, B_FALSE, EPERM) == 0) + return (0); + + if (priv_policy_user(cr, CAP_DAC_READ_SEARCH, B_FALSE, EPERM) == 0) + return (0); + + return (EPERM); +} + +/* + * Determine if subject can chown owner of a file. + */ +int +secpolicy_vnode_chown(const cred_t *cr, uid_t owner) +{ + if (crgetfsuid(cr) == owner) + return (0); + +#if defined(CONFIG_USER_NS) + if (!kuid_has_mapping(cr->user_ns, SUID_TO_KUID(owner))) + return (EPERM); +#endif + + return (priv_policy_user(cr, CAP_FOWNER, B_FALSE, EPERM)); +} + +/* + * Determine if subject can change group ownership of a file. + */ +int +secpolicy_vnode_create_gid(const cred_t *cr) +{ + return (priv_policy(cr, CAP_SETGID, B_FALSE, EPERM)); +} + +/* + * Policy determines whether we can remove an entry from a directory, + * regardless of permission bits. + */ +int +secpolicy_vnode_remove(const cred_t *cr) +{ + return (priv_policy(cr, CAP_FOWNER, B_FALSE, EPERM)); +} + +/* + * Determine that subject can modify the mode of a file. allzone privilege + * needed when modifying root owned object. + */ +int +secpolicy_vnode_setdac(const cred_t *cr, uid_t owner) +{ + if (crgetfsuid(cr) == owner) + return (0); + +#if defined(CONFIG_USER_NS) + if (!kuid_has_mapping(cr->user_ns, SUID_TO_KUID(owner))) + return (EPERM); +#endif + + return (priv_policy_user(cr, CAP_FOWNER, B_FALSE, EPERM)); +} + +/* + * Are we allowed to retain the set-uid/set-gid bits when + * changing ownership or when writing to a file? + * "issuid" should be true when set-uid; only in that case + * root ownership is checked (setgid is assumed). + * + * Enforced in the Linux VFS. + */ +int +secpolicy_vnode_setid_retain(const cred_t *cr, boolean_t issuidroot) +{ + return (priv_policy_user(cr, CAP_FSETID, B_FALSE, EPERM)); +} + +/* + * Determine that subject can set the file setgid flag. + */ +int +secpolicy_vnode_setids_setgids(const cred_t *cr, gid_t gid) +{ +#if defined(CONFIG_USER_NS) + if (!kgid_has_mapping(cr->user_ns, SGID_TO_KGID(gid))) + return (EPERM); +#endif + if (crgetfsgid(cr) != gid && !groupmember(gid, cr)) + return (priv_policy_user(cr, CAP_FSETID, B_FALSE, EPERM)); + + return (0); +} + +/* + * Determine if the subject can inject faults in the ZFS fault injection + * framework. Requires all privileges. + */ +int +secpolicy_zinject(const cred_t *cr) +{ + return (priv_policy(cr, CAP_SYS_ADMIN, B_FALSE, EACCES)); +} + +/* + * Determine if the subject has permission to manipulate ZFS datasets + * (not pools). Equivalent to the SYS_MOUNT privilege. + */ +int +secpolicy_zfs(const cred_t *cr) +{ + return (priv_policy(cr, CAP_SYS_ADMIN, B_FALSE, EACCES)); +} + +void +secpolicy_setid_clear(vattr_t *vap, cred_t *cr) +{ + if ((vap->va_mode & (S_ISUID | S_ISGID)) != 0 && + secpolicy_vnode_setid_retain(cr, + (vap->va_mode & S_ISUID) != 0 && + (vap->va_mask & AT_UID) != 0 && vap->va_uid == 0) != 0) { + vap->va_mask |= AT_MODE; + vap->va_mode &= ~(S_ISUID|S_ISGID); + } +} + +/* + * Determine that subject can set the file setid flags. + */ +static int +secpolicy_vnode_setid_modify(const cred_t *cr, uid_t owner) +{ + if (crgetfsuid(cr) == owner) + return (0); + +#if defined(CONFIG_USER_NS) + if (!kuid_has_mapping(cr->user_ns, SUID_TO_KUID(owner))) + return (EPERM); +#endif + + return (priv_policy_user(cr, CAP_FSETID, B_FALSE, EPERM)); +} + +/* + * Determine that subject can make a file a "sticky". + * + * Enforced in the Linux VFS. + */ +static int +secpolicy_vnode_stky_modify(const cred_t *cr) +{ + return (0); +} + +int +secpolicy_setid_setsticky_clear(struct inode *ip, vattr_t *vap, + const vattr_t *ovap, cred_t *cr) +{ + int error; + + if ((vap->va_mode & S_ISUID) != 0 && + (error = secpolicy_vnode_setid_modify(cr, + ovap->va_uid)) != 0) { + return (error); + } + + /* + * Check privilege if attempting to set the + * sticky bit on a non-directory. + */ + if (!S_ISDIR(ip->i_mode) && (vap->va_mode & S_ISVTX) != 0 && + secpolicy_vnode_stky_modify(cr) != 0) { + vap->va_mode &= ~S_ISVTX; + } + + /* + * Check for privilege if attempting to set the + * group-id bit. + */ + if ((vap->va_mode & S_ISGID) != 0 && + secpolicy_vnode_setids_setgids(cr, ovap->va_gid) != 0) { + vap->va_mode &= ~S_ISGID; + } + + return (0); +} + +/* + * Check privileges for setting xvattr attributes + */ +int +secpolicy_xvattr(xvattr_t *xvap, uid_t owner, cred_t *cr, mode_t type) +{ + return (secpolicy_vnode_chown(cr, owner)); +} + +/* + * Check privileges for setattr attributes. + * + * Enforced in the Linux VFS. + */ +int +secpolicy_vnode_setattr(cred_t *cr, struct inode *ip, struct vattr *vap, + const struct vattr *ovap, int flags, + int unlocked_access(void *, int, cred_t *), void *node) +{ + return (0); +} + +/* + * Check privileges for links. + * + * Enforced in the Linux VFS. + */ +int +secpolicy_basic_link(const cred_t *cr) +{ + return (0); +} diff --git a/module/os/macos/zfs/qat.c b/module/os/macos/zfs/qat.c new file mode 100644 index 000000000000..08613b3a2042 --- /dev/null +++ b/module/os/macos/zfs/qat.c @@ -0,0 +1,105 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#if defined(_KERNEL) && defined(HAVE_QAT) +#include +#include + +qat_stats_t qat_stats = { + { "comp_requests", KSTAT_DATA_UINT64 }, + { "comp_total_in_bytes", KSTAT_DATA_UINT64 }, + { "comp_total_out_bytes", KSTAT_DATA_UINT64 }, + { "decomp_requests", KSTAT_DATA_UINT64 }, + { "decomp_total_in_bytes", KSTAT_DATA_UINT64 }, + { "decomp_total_out_bytes", KSTAT_DATA_UINT64 }, + { "dc_fails", KSTAT_DATA_UINT64 }, + { "encrypt_requests", KSTAT_DATA_UINT64 }, + { "encrypt_total_in_bytes", KSTAT_DATA_UINT64 }, + { "encrypt_total_out_bytes", KSTAT_DATA_UINT64 }, + { "decrypt_requests", KSTAT_DATA_UINT64 }, + { "decrypt_total_in_bytes", KSTAT_DATA_UINT64 }, + { "decrypt_total_out_bytes", KSTAT_DATA_UINT64 }, + { "crypt_fails", KSTAT_DATA_UINT64 }, + { "cksum_requests", KSTAT_DATA_UINT64 }, + { "cksum_total_in_bytes", KSTAT_DATA_UINT64 }, + { "cksum_fails", KSTAT_DATA_UINT64 }, +}; + +static kstat_t *qat_ksp = NULL; + +CpaStatus +qat_mem_alloc_contig(void **pp_mem_addr, Cpa32U size_bytes) +{ + *pp_mem_addr = kmalloc(size_bytes, GFP_KERNEL); + if (*pp_mem_addr == NULL) + return (CPA_STATUS_RESOURCE); + return (CPA_STATUS_SUCCESS); +} + +void +qat_mem_free_contig(void **pp_mem_addr) +{ + if (*pp_mem_addr != NULL) { + kfree(*pp_mem_addr); + *pp_mem_addr = NULL; + } +} + +int +qat_init(void) +{ + qat_ksp = kstat_create("zfs", 0, "qat", "misc", + KSTAT_TYPE_NAMED, sizeof (qat_stats) / sizeof (kstat_named_t), + KSTAT_FLAG_VIRTUAL); + if (qat_ksp != NULL) { + qat_ksp->ks_data = &qat_stats; + kstat_install(qat_ksp); + } + + /* + * Just set the disable flag when qat init failed, qat can be + * turned on again in post-process after zfs module is loaded, e.g.: + * echo 0 > /sys/module/zfs/parameters/zfs_qat_compress_disable + */ + if (qat_dc_init() != 0) + zfs_qat_compress_disable = 1; + + if (qat_cy_init() != 0) { + zfs_qat_checksum_disable = 1; + zfs_qat_encrypt_disable = 1; + } + + return (0); +} + +void +qat_fini(void) +{ + if (qat_ksp != NULL) { + kstat_delete(qat_ksp); + qat_ksp = NULL; + } + + qat_cy_fini(); + qat_dc_fini(); +} + +#endif diff --git a/module/os/macos/zfs/qat_compress.c b/module/os/macos/zfs/qat_compress.c new file mode 100644 index 000000000000..ad3ead3b16e3 --- /dev/null +++ b/module/os/macos/zfs/qat_compress.c @@ -0,0 +1,569 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#if defined(_KERNEL) && defined(HAVE_QAT) +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Max instances in a QAT device, each instance is a channel to submit + * jobs to QAT hardware, this is only for pre-allocating instance and + * session arrays; the actual number of instances are defined in the + * QAT driver's configuration file. + */ +#define QAT_DC_MAX_INSTANCES 48 + +/* + * ZLIB head and foot size + */ +#define ZLIB_HEAD_SZ 2 +#define ZLIB_FOOT_SZ 4 + +static CpaInstanceHandle dc_inst_handles[QAT_DC_MAX_INSTANCES]; +static CpaDcSessionHandle session_handles[QAT_DC_MAX_INSTANCES]; +static CpaBufferList **buffer_array[QAT_DC_MAX_INSTANCES]; +static Cpa16U num_inst = 0; +static Cpa32U inst_num = 0; +static boolean_t qat_dc_init_done = B_FALSE; +int zfs_qat_compress_disable = 0; + +boolean_t +qat_dc_use_accel(size_t s_len) +{ + return (!zfs_qat_compress_disable && + qat_dc_init_done && + s_len >= QAT_MIN_BUF_SIZE && + s_len <= QAT_MAX_BUF_SIZE); +} + +static void +qat_dc_callback(void *p_callback, CpaStatus status) +{ + if (p_callback != NULL) + complete((struct completion *)p_callback); +} + +static void +qat_dc_clean(void) +{ + Cpa16U buff_num = 0; + Cpa16U num_inter_buff_lists = 0; + + for (Cpa16U i = 0; i < num_inst; i++) { + cpaDcStopInstance(dc_inst_handles[i]); + QAT_PHYS_CONTIG_FREE(session_handles[i]); + /* free intermediate buffers */ + if (buffer_array[i] != NULL) { + cpaDcGetNumIntermediateBuffers( + dc_inst_handles[i], &num_inter_buff_lists); + for (buff_num = 0; buff_num < num_inter_buff_lists; + buff_num++) { + CpaBufferList *buffer_inter = + buffer_array[i][buff_num]; + if (buffer_inter->pBuffers) { + QAT_PHYS_CONTIG_FREE( + buffer_inter->pBuffers->pData); + QAT_PHYS_CONTIG_FREE( + buffer_inter->pBuffers); + } + QAT_PHYS_CONTIG_FREE( + buffer_inter->pPrivateMetaData); + QAT_PHYS_CONTIG_FREE(buffer_inter); + } + } + } + + num_inst = 0; + qat_dc_init_done = B_FALSE; +} + +int +qat_dc_init(void) +{ + CpaStatus status = CPA_STATUS_SUCCESS; + Cpa32U sess_size = 0; + Cpa32U ctx_size = 0; + Cpa16U num_inter_buff_lists = 0; + Cpa16U buff_num = 0; + Cpa32U buff_meta_size = 0; + CpaDcSessionSetupData sd = {0}; + + if (qat_dc_init_done) + return (0); + + status = cpaDcGetNumInstances(&num_inst); + if (status != CPA_STATUS_SUCCESS) + return (-1); + + /* if the user has configured no QAT compression units just return */ + if (num_inst == 0) + return (0); + + if (num_inst > QAT_DC_MAX_INSTANCES) + num_inst = QAT_DC_MAX_INSTANCES; + + status = cpaDcGetInstances(num_inst, &dc_inst_handles[0]); + if (status != CPA_STATUS_SUCCESS) + return (-1); + + for (Cpa16U i = 0; i < num_inst; i++) { + cpaDcSetAddressTranslation(dc_inst_handles[i], + (void*)virt_to_phys); + + status = cpaDcBufferListGetMetaSize(dc_inst_handles[i], + 1, &buff_meta_size); + + if (status == CPA_STATUS_SUCCESS) + status = cpaDcGetNumIntermediateBuffers( + dc_inst_handles[i], &num_inter_buff_lists); + + if (status == CPA_STATUS_SUCCESS && num_inter_buff_lists != 0) + status = QAT_PHYS_CONTIG_ALLOC(&buffer_array[i], + num_inter_buff_lists * + sizeof (CpaBufferList *)); + + for (buff_num = 0; buff_num < num_inter_buff_lists; + buff_num++) { + if (status == CPA_STATUS_SUCCESS) + status = QAT_PHYS_CONTIG_ALLOC( + &buffer_array[i][buff_num], + sizeof (CpaBufferList)); + + if (status == CPA_STATUS_SUCCESS) + status = QAT_PHYS_CONTIG_ALLOC( + &buffer_array[i][buff_num]-> + pPrivateMetaData, + buff_meta_size); + + if (status == CPA_STATUS_SUCCESS) + status = QAT_PHYS_CONTIG_ALLOC( + &buffer_array[i][buff_num]->pBuffers, + sizeof (CpaFlatBuffer)); + + if (status == CPA_STATUS_SUCCESS) { + /* + * implementation requires an intermediate + * buffer approximately twice the size of + * output buffer, which is 2x max buffer + * size here. + */ + status = QAT_PHYS_CONTIG_ALLOC( + &buffer_array[i][buff_num]->pBuffers-> + pData, 2 * QAT_MAX_BUF_SIZE); + if (status != CPA_STATUS_SUCCESS) + goto fail; + + buffer_array[i][buff_num]->numBuffers = 1; + buffer_array[i][buff_num]->pBuffers-> + dataLenInBytes = 2 * QAT_MAX_BUF_SIZE; + } + } + + status = cpaDcStartInstance(dc_inst_handles[i], + num_inter_buff_lists, buffer_array[i]); + if (status != CPA_STATUS_SUCCESS) + goto fail; + + sd.compLevel = CPA_DC_L1; + sd.compType = CPA_DC_DEFLATE; + sd.huffType = CPA_DC_HT_FULL_DYNAMIC; + sd.sessDirection = CPA_DC_DIR_COMBINED; + sd.sessState = CPA_DC_STATELESS; + sd.deflateWindowSize = 7; + sd.checksum = CPA_DC_ADLER32; + status = cpaDcGetSessionSize(dc_inst_handles[i], + &sd, &sess_size, &ctx_size); + if (status != CPA_STATUS_SUCCESS) + goto fail; + + QAT_PHYS_CONTIG_ALLOC(&session_handles[i], sess_size); + if (session_handles[i] == NULL) + goto fail; + + status = cpaDcInitSession(dc_inst_handles[i], + session_handles[i], + &sd, NULL, qat_dc_callback); + if (status != CPA_STATUS_SUCCESS) + goto fail; + } + + qat_dc_init_done = B_TRUE; + return (0); +fail: + qat_dc_clean(); + return (-1); +} + +void +qat_dc_fini(void) +{ + if (!qat_dc_init_done) + return; + + qat_dc_clean(); +} + +/* + * The "add" parameter is an additional buffer which is passed + * to QAT as a scratch buffer alongside the destination buffer + * in case the "compressed" data ends up being larger than the + * original source data. This is necessary to prevent QAT from + * generating buffer overflow warnings for incompressible data. + */ +static int +qat_compress_impl(qat_compress_dir_t dir, char *src, int src_len, + char *dst, int dst_len, char *add, int add_len, size_t *c_len) +{ + CpaInstanceHandle dc_inst_handle; + CpaDcSessionHandle session_handle; + CpaBufferList *buf_list_src = NULL; + CpaBufferList *buf_list_dst = NULL; + CpaFlatBuffer *flat_buf_src = NULL; + CpaFlatBuffer *flat_buf_dst = NULL; + Cpa8U *buffer_meta_src = NULL; + Cpa8U *buffer_meta_dst = NULL; + Cpa32U buffer_meta_size = 0; + CpaDcRqResults dc_results; + CpaStatus status = CPA_STATUS_FAIL; + Cpa32U hdr_sz = 0; + Cpa32U compressed_sz; + Cpa32U num_src_buf = (src_len >> PAGE_SHIFT) + 2; + Cpa32U num_dst_buf = (dst_len >> PAGE_SHIFT) + 2; + Cpa32U num_add_buf = (add_len >> PAGE_SHIFT) + 2; + Cpa32U bytes_left; + Cpa32U dst_pages = 0; + Cpa32U adler32 = 0; + char *data; + struct page *page; + struct page **in_pages = NULL; + struct page **out_pages = NULL; + struct page **add_pages = NULL; + Cpa32U page_off = 0; + struct completion complete; + Cpa32U page_num = 0; + Cpa16U i; + + /* + * We increment num_src_buf and num_dst_buf by 2 to allow + * us to handle non page-aligned buffer addresses and buffers + * whose sizes are not divisible by PAGE_SIZE. + */ + Cpa32U src_buffer_list_mem_size = sizeof (CpaBufferList) + + (num_src_buf * sizeof (CpaFlatBuffer)); + Cpa32U dst_buffer_list_mem_size = sizeof (CpaBufferList) + + ((num_dst_buf + num_add_buf) * sizeof (CpaFlatBuffer)); + + status = QAT_PHYS_CONTIG_ALLOC(&in_pages, + num_src_buf * sizeof (struct page *)); + if (status != CPA_STATUS_SUCCESS) + goto fail; + + status = QAT_PHYS_CONTIG_ALLOC(&out_pages, + num_dst_buf * sizeof (struct page *)); + if (status != CPA_STATUS_SUCCESS) + goto fail; + + status = QAT_PHYS_CONTIG_ALLOC(&add_pages, + num_add_buf * sizeof (struct page *)); + if (status != CPA_STATUS_SUCCESS) + goto fail; + + i = (Cpa32U)atomic_inc_32_nv(&inst_num) % num_inst; + dc_inst_handle = dc_inst_handles[i]; + session_handle = session_handles[i]; + + cpaDcBufferListGetMetaSize(dc_inst_handle, num_src_buf, + &buffer_meta_size); + status = QAT_PHYS_CONTIG_ALLOC(&buffer_meta_src, buffer_meta_size); + if (status != CPA_STATUS_SUCCESS) + goto fail; + + cpaDcBufferListGetMetaSize(dc_inst_handle, num_dst_buf + num_add_buf, + &buffer_meta_size); + status = QAT_PHYS_CONTIG_ALLOC(&buffer_meta_dst, buffer_meta_size); + if (status != CPA_STATUS_SUCCESS) + goto fail; + + /* build source buffer list */ + status = QAT_PHYS_CONTIG_ALLOC(&buf_list_src, src_buffer_list_mem_size); + if (status != CPA_STATUS_SUCCESS) + goto fail; + + flat_buf_src = (CpaFlatBuffer *)(buf_list_src + 1); + + buf_list_src->pBuffers = flat_buf_src; /* always point to first one */ + + /* build destination buffer list */ + status = QAT_PHYS_CONTIG_ALLOC(&buf_list_dst, dst_buffer_list_mem_size); + if (status != CPA_STATUS_SUCCESS) + goto fail; + + flat_buf_dst = (CpaFlatBuffer *)(buf_list_dst + 1); + + buf_list_dst->pBuffers = flat_buf_dst; /* always point to first one */ + + buf_list_src->numBuffers = 0; + buf_list_src->pPrivateMetaData = buffer_meta_src; + bytes_left = src_len; + data = src; + page_num = 0; + while (bytes_left > 0) { + page_off = ((long)data & ~PAGE_MASK); + page = qat_mem_to_page(data); + in_pages[page_num] = page; + flat_buf_src->pData = kmap(page) + page_off; + flat_buf_src->dataLenInBytes = + min((long)PAGE_SIZE - page_off, (long)bytes_left); + + bytes_left -= flat_buf_src->dataLenInBytes; + data += flat_buf_src->dataLenInBytes; + flat_buf_src++; + buf_list_src->numBuffers++; + page_num++; + } + + buf_list_dst->numBuffers = 0; + buf_list_dst->pPrivateMetaData = buffer_meta_dst; + bytes_left = dst_len; + data = dst; + page_num = 0; + while (bytes_left > 0) { + page_off = ((long)data & ~PAGE_MASK); + page = qat_mem_to_page(data); + flat_buf_dst->pData = kmap(page) + page_off; + out_pages[page_num] = page; + flat_buf_dst->dataLenInBytes = + min((long)PAGE_SIZE - page_off, (long)bytes_left); + + bytes_left -= flat_buf_dst->dataLenInBytes; + data += flat_buf_dst->dataLenInBytes; + flat_buf_dst++; + buf_list_dst->numBuffers++; + page_num++; + dst_pages++; + } + + /* map additional scratch pages into the destination buffer list */ + bytes_left = add_len; + data = add; + page_num = 0; + while (bytes_left > 0) { + page_off = ((long)data & ~PAGE_MASK); + page = qat_mem_to_page(data); + flat_buf_dst->pData = kmap(page) + page_off; + add_pages[page_num] = page; + flat_buf_dst->dataLenInBytes = + min((long)PAGE_SIZE - page_off, (long)bytes_left); + + bytes_left -= flat_buf_dst->dataLenInBytes; + data += flat_buf_dst->dataLenInBytes; + flat_buf_dst++; + buf_list_dst->numBuffers++; + page_num++; + } + + init_completion(&complete); + + if (dir == QAT_COMPRESS) { + QAT_STAT_BUMP(comp_requests); + QAT_STAT_INCR(comp_total_in_bytes, src_len); + + cpaDcGenerateHeader(session_handle, + buf_list_dst->pBuffers, &hdr_sz); + buf_list_dst->pBuffers->pData += hdr_sz; + buf_list_dst->pBuffers->dataLenInBytes -= hdr_sz; + status = cpaDcCompressData( + dc_inst_handle, session_handle, + buf_list_src, buf_list_dst, + &dc_results, CPA_DC_FLUSH_FINAL, + &complete); + if (status != CPA_STATUS_SUCCESS) { + goto fail; + } + + /* we now wait until the completion of the operation. */ + wait_for_completion(&complete); + + if (dc_results.status != CPA_STATUS_SUCCESS) { + status = CPA_STATUS_FAIL; + goto fail; + } + + compressed_sz = dc_results.produced; + if (compressed_sz + hdr_sz + ZLIB_FOOT_SZ > dst_len) { + status = CPA_STATUS_INCOMPRESSIBLE; + goto fail; + } + + flat_buf_dst = (CpaFlatBuffer *)(buf_list_dst + 1); + /* move to the last page */ + flat_buf_dst += (compressed_sz + hdr_sz) >> PAGE_SHIFT; + + /* no space for gzip footer in the last page */ + if (((compressed_sz + hdr_sz) % PAGE_SIZE) + + ZLIB_FOOT_SZ > PAGE_SIZE) { + status = CPA_STATUS_INCOMPRESSIBLE; + goto fail; + } + + /* jump to the end of the buffer and append footer */ + flat_buf_dst->pData = + (char *)((unsigned long)flat_buf_dst->pData & PAGE_MASK) + + ((compressed_sz + hdr_sz) % PAGE_SIZE); + flat_buf_dst->dataLenInBytes = ZLIB_FOOT_SZ; + + dc_results.produced = 0; + status = cpaDcGenerateFooter(session_handle, + flat_buf_dst, &dc_results); + if (status != CPA_STATUS_SUCCESS) + goto fail; + + *c_len = compressed_sz + dc_results.produced + hdr_sz; + QAT_STAT_INCR(comp_total_out_bytes, *c_len); + } else { + ASSERT3U(dir, ==, QAT_DECOMPRESS); + QAT_STAT_BUMP(decomp_requests); + QAT_STAT_INCR(decomp_total_in_bytes, src_len); + + buf_list_src->pBuffers->pData += ZLIB_HEAD_SZ; + buf_list_src->pBuffers->dataLenInBytes -= ZLIB_HEAD_SZ; + status = cpaDcDecompressData(dc_inst_handle, session_handle, + buf_list_src, buf_list_dst, &dc_results, CPA_DC_FLUSH_FINAL, + &complete); + + if (CPA_STATUS_SUCCESS != status) { + status = CPA_STATUS_FAIL; + goto fail; + } + + /* we now wait until the completion of the operation. */ + wait_for_completion(&complete); + + if (dc_results.status != CPA_STATUS_SUCCESS) { + status = CPA_STATUS_FAIL; + goto fail; + } + + /* verify adler checksum */ + adler32 = *(Cpa32U *)(src + dc_results.consumed + ZLIB_HEAD_SZ); + if (adler32 != BSWAP_32(dc_results.checksum)) { + status = CPA_STATUS_FAIL; + goto fail; + } + *c_len = dc_results.produced; + QAT_STAT_INCR(decomp_total_out_bytes, *c_len); + } + +fail: + if (status != CPA_STATUS_SUCCESS && status != CPA_STATUS_INCOMPRESSIBLE) + QAT_STAT_BUMP(dc_fails); + + if (in_pages) { + for (page_num = 0; + page_num < buf_list_src->numBuffers; + page_num++) { + kunmap(in_pages[page_num]); + } + QAT_PHYS_CONTIG_FREE(in_pages); + } + + if (out_pages) { + for (page_num = 0; page_num < dst_pages; page_num++) { + kunmap(out_pages[page_num]); + } + QAT_PHYS_CONTIG_FREE(out_pages); + } + + if (add_pages) { + for (page_num = 0; + page_num < buf_list_dst->numBuffers - dst_pages; + page_num++) { + kunmap(add_pages[page_num]); + } + QAT_PHYS_CONTIG_FREE(add_pages); + } + + QAT_PHYS_CONTIG_FREE(buffer_meta_src); + QAT_PHYS_CONTIG_FREE(buffer_meta_dst); + QAT_PHYS_CONTIG_FREE(buf_list_src); + QAT_PHYS_CONTIG_FREE(buf_list_dst); + + return (status); +} + +/* + * Entry point for QAT accelerated compression / decompression. + */ +int +qat_compress(qat_compress_dir_t dir, char *src, int src_len, + char *dst, int dst_len, size_t *c_len) +{ + int ret; + size_t add_len = 0; + void *add = NULL; + + if (dir == QAT_COMPRESS) { + add_len = dst_len; + add = zio_data_buf_alloc(add_len); + } + + ret = qat_compress_impl(dir, src, src_len, dst, + dst_len, add, add_len, c_len); + + if (dir == QAT_COMPRESS) + zio_data_buf_free(add, add_len); + + return (ret); +} + +static int +param_set_qat_compress(const char *val, zfs_kernel_param_t *kp) +{ + int ret; + int *pvalue = kp->arg; + ret = param_set_int(val, kp); + if (ret) + return (ret); + /* + * zfs_qat_compress_disable = 0: enable qat compress + * try to initialize qat instance if it has not been done + */ + if (*pvalue == 0 && !qat_dc_init_done) { + ret = qat_dc_init(); + if (ret != 0) { + zfs_qat_compress_disable = 1; + return (ret); + } + } + return (ret); +} + +module_param_call(zfs_qat_compress_disable, param_set_qat_compress, + param_get_int, &zfs_qat_compress_disable, 0644); +MODULE_PARM_DESC(zfs_qat_compress_disable, "Enable/Disable QAT compression"); + +#endif diff --git a/module/os/macos/zfs/qat_crypt.c b/module/os/macos/zfs/qat_crypt.c new file mode 100644 index 000000000000..4771b2f3bec5 --- /dev/null +++ b/module/os/macos/zfs/qat_crypt.c @@ -0,0 +1,630 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * This file represents the QAT implementation of checksums and encryption. + * Internally, QAT shares the same cryptographic instances for both of these + * operations, so the code has been combined here. QAT data compression uses + * compression instances, so that code is separated into qat_compress.c + */ + +#if defined(_KERNEL) && defined(HAVE_QAT) +#include +#include +#include +#include +#include +#include +#include "lac/cpa_cy_im.h" +#include "lac/cpa_cy_common.h" +#include + +/* + * Max instances in a QAT device, each instance is a channel to submit + * jobs to QAT hardware, this is only for pre-allocating instances + * and session arrays; the actual number of instances are defined in + * the QAT driver's configure file. + */ +#define QAT_CRYPT_MAX_INSTANCES 48 + +#define MAX_PAGE_NUM 1024 + +static Cpa32U inst_num = 0; +static Cpa16U num_inst = 0; +static CpaInstanceHandle cy_inst_handles[QAT_CRYPT_MAX_INSTANCES]; +static boolean_t qat_cy_init_done = B_FALSE; +int zfs_qat_encrypt_disable = 0; +int zfs_qat_checksum_disable = 0; + +typedef struct cy_callback { + CpaBoolean verify_result; + struct completion complete; +} cy_callback_t; + +static void +symcallback(void *p_callback, CpaStatus status, const CpaCySymOp operation, + void *op_data, CpaBufferList *buf_list_dst, CpaBoolean verify) +{ + cy_callback_t *cb = p_callback; + + if (cb != NULL) { + /* indicate that the function has been called */ + cb->verify_result = verify; + complete(&cb->complete); + } +} + +boolean_t +qat_crypt_use_accel(size_t s_len) +{ + return (!zfs_qat_encrypt_disable && + qat_cy_init_done && + s_len >= QAT_MIN_BUF_SIZE && + s_len <= QAT_MAX_BUF_SIZE); +} + +boolean_t +qat_checksum_use_accel(size_t s_len) +{ + return (!zfs_qat_checksum_disable && + qat_cy_init_done && + s_len >= QAT_MIN_BUF_SIZE && + s_len <= QAT_MAX_BUF_SIZE); +} + +void +qat_cy_clean(void) +{ + for (Cpa16U i = 0; i < num_inst; i++) + cpaCyStopInstance(cy_inst_handles[i]); + + num_inst = 0; + qat_cy_init_done = B_FALSE; +} + +int +qat_cy_init(void) +{ + CpaStatus status = CPA_STATUS_FAIL; + + if (qat_cy_init_done) + return (0); + + status = cpaCyGetNumInstances(&num_inst); + if (status != CPA_STATUS_SUCCESS) + return (-1); + + /* if the user has configured no QAT encryption units just return */ + if (num_inst == 0) + return (0); + + if (num_inst > QAT_CRYPT_MAX_INSTANCES) + num_inst = QAT_CRYPT_MAX_INSTANCES; + + status = cpaCyGetInstances(num_inst, &cy_inst_handles[0]); + if (status != CPA_STATUS_SUCCESS) + return (-1); + + for (Cpa16U i = 0; i < num_inst; i++) { + status = cpaCySetAddressTranslation(cy_inst_handles[i], + (void *)virt_to_phys); + if (status != CPA_STATUS_SUCCESS) + goto error; + + status = cpaCyStartInstance(cy_inst_handles[i]); + if (status != CPA_STATUS_SUCCESS) + goto error; + } + + qat_cy_init_done = B_TRUE; + return (0); + +error: + qat_cy_clean(); + return (-1); +} + +void +qat_cy_fini(void) +{ + if (!qat_cy_init_done) + return; + + qat_cy_clean(); +} + +static CpaStatus +qat_init_crypt_session_ctx(qat_encrypt_dir_t dir, CpaInstanceHandle inst_handle, + CpaCySymSessionCtx **cy_session_ctx, crypto_key_t *key, + Cpa64U crypt, Cpa32U aad_len) +{ + CpaStatus status = CPA_STATUS_SUCCESS; + Cpa32U ctx_size; + Cpa32U ciper_algorithm; + Cpa32U hash_algorithm; + CpaCySymSessionSetupData sd = { 0 }; + + if (zio_crypt_table[crypt].ci_crypt_type == ZC_TYPE_CCM) { + return (CPA_STATUS_FAIL); + } else { + ciper_algorithm = CPA_CY_SYM_CIPHER_AES_GCM; + hash_algorithm = CPA_CY_SYM_HASH_AES_GCM; + } + + sd.cipherSetupData.cipherAlgorithm = ciper_algorithm; + sd.cipherSetupData.pCipherKey = key->ck_data; + sd.cipherSetupData.cipherKeyLenInBytes = key->ck_length / 8; + sd.hashSetupData.hashAlgorithm = hash_algorithm; + sd.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH; + sd.hashSetupData.digestResultLenInBytes = ZIO_DATA_MAC_LEN; + sd.hashSetupData.authModeSetupData.aadLenInBytes = aad_len; + sd.sessionPriority = CPA_CY_PRIORITY_NORMAL; + sd.symOperation = CPA_CY_SYM_OP_ALGORITHM_CHAINING; + sd.digestIsAppended = CPA_FALSE; + sd.verifyDigest = CPA_FALSE; + + if (dir == QAT_ENCRYPT) { + sd.cipherSetupData.cipherDirection = + CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT; + sd.algChainOrder = + CPA_CY_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER; + } else { + ASSERT3U(dir, ==, QAT_DECRYPT); + sd.cipherSetupData.cipherDirection = + CPA_CY_SYM_CIPHER_DIRECTION_DECRYPT; + sd.algChainOrder = + CPA_CY_SYM_ALG_CHAIN_ORDER_CIPHER_THEN_HASH; + } + + status = cpaCySymSessionCtxGetSize(inst_handle, &sd, &ctx_size); + if (status != CPA_STATUS_SUCCESS) + return (status); + + status = QAT_PHYS_CONTIG_ALLOC(cy_session_ctx, ctx_size); + if (status != CPA_STATUS_SUCCESS) + return (status); + + status = cpaCySymInitSession(inst_handle, symcallback, &sd, + *cy_session_ctx); + if (status != CPA_STATUS_SUCCESS) { + QAT_PHYS_CONTIG_FREE(*cy_session_ctx); + return (status); + } + + return (CPA_STATUS_SUCCESS); +} + +static CpaStatus +qat_init_checksum_session_ctx(CpaInstanceHandle inst_handle, + CpaCySymSessionCtx **cy_session_ctx, Cpa64U cksum) +{ + CpaStatus status = CPA_STATUS_SUCCESS; + Cpa32U ctx_size; + Cpa32U hash_algorithm; + CpaCySymSessionSetupData sd = { 0 }; + + /* + * ZFS's SHA512 checksum is actually SHA512/256, which uses + * a different IV from standard SHA512. QAT does not support + * SHA512/256, so we can only support SHA256. + */ + if (cksum == ZIO_CHECKSUM_SHA256) + hash_algorithm = CPA_CY_SYM_HASH_SHA256; + else + return (CPA_STATUS_FAIL); + + sd.sessionPriority = CPA_CY_PRIORITY_NORMAL; + sd.symOperation = CPA_CY_SYM_OP_HASH; + sd.hashSetupData.hashAlgorithm = hash_algorithm; + sd.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_PLAIN; + sd.hashSetupData.digestResultLenInBytes = sizeof (zio_cksum_t); + sd.digestIsAppended = CPA_FALSE; + sd.verifyDigest = CPA_FALSE; + + status = cpaCySymSessionCtxGetSize(inst_handle, &sd, &ctx_size); + if (status != CPA_STATUS_SUCCESS) + return (status); + + status = QAT_PHYS_CONTIG_ALLOC(cy_session_ctx, ctx_size); + if (status != CPA_STATUS_SUCCESS) + return (status); + + status = cpaCySymInitSession(inst_handle, symcallback, &sd, + *cy_session_ctx); + if (status != CPA_STATUS_SUCCESS) { + QAT_PHYS_CONTIG_FREE(*cy_session_ctx); + return (status); + } + + return (CPA_STATUS_SUCCESS); +} + +static CpaStatus +qat_init_cy_buffer_lists(CpaInstanceHandle inst_handle, uint32_t nr_bufs, + CpaBufferList *src, CpaBufferList *dst) +{ + CpaStatus status = CPA_STATUS_SUCCESS; + Cpa32U meta_size = 0; + + status = cpaCyBufferListGetMetaSize(inst_handle, nr_bufs, &meta_size); + if (status != CPA_STATUS_SUCCESS) + return (status); + + status = QAT_PHYS_CONTIG_ALLOC(&src->pPrivateMetaData, meta_size); + if (status != CPA_STATUS_SUCCESS) + goto error; + + if (src != dst) { + status = QAT_PHYS_CONTIG_ALLOC(&dst->pPrivateMetaData, + meta_size); + if (status != CPA_STATUS_SUCCESS) + goto error; + } + + return (CPA_STATUS_SUCCESS); + +error: + QAT_PHYS_CONTIG_FREE(src->pPrivateMetaData); + if (src != dst) + QAT_PHYS_CONTIG_FREE(dst->pPrivateMetaData); + + return (status); +} + +int +qat_crypt(qat_encrypt_dir_t dir, uint8_t *src_buf, uint8_t *dst_buf, + uint8_t *aad_buf, uint32_t aad_len, uint8_t *iv_buf, uint8_t *digest_buf, + crypto_key_t *key, uint64_t crypt, uint32_t enc_len) +{ + CpaStatus status = CPA_STATUS_SUCCESS; + Cpa16U i; + CpaInstanceHandle cy_inst_handle; + Cpa16U nr_bufs = (enc_len >> PAGE_SHIFT) + 2; + Cpa32U bytes_left = 0; + Cpa8S *data = NULL; + CpaCySymSessionCtx *cy_session_ctx = NULL; + cy_callback_t cb; + CpaCySymOpData op_data = { 0 }; + CpaBufferList src_buffer_list = { 0 }; + CpaBufferList dst_buffer_list = { 0 }; + CpaFlatBuffer *flat_src_buf_array = NULL; + CpaFlatBuffer *flat_src_buf = NULL; + CpaFlatBuffer *flat_dst_buf_array = NULL; + CpaFlatBuffer *flat_dst_buf = NULL; + struct page *in_pages[MAX_PAGE_NUM]; + struct page *out_pages[MAX_PAGE_NUM]; + Cpa32U in_page_num = 0; + Cpa32U out_page_num = 0; + Cpa32U in_page_off = 0; + Cpa32U out_page_off = 0; + + if (dir == QAT_ENCRYPT) { + QAT_STAT_BUMP(encrypt_requests); + QAT_STAT_INCR(encrypt_total_in_bytes, enc_len); + } else { + QAT_STAT_BUMP(decrypt_requests); + QAT_STAT_INCR(decrypt_total_in_bytes, enc_len); + } + + i = (Cpa32U)atomic_inc_32_nv(&inst_num) % num_inst; + cy_inst_handle = cy_inst_handles[i]; + + status = qat_init_crypt_session_ctx(dir, cy_inst_handle, + &cy_session_ctx, key, crypt, aad_len); + if (status != CPA_STATUS_SUCCESS) { + /* don't count CCM as a failure since it's not supported */ + if (zio_crypt_table[crypt].ci_crypt_type == ZC_TYPE_GCM) + QAT_STAT_BUMP(crypt_fails); + return (status); + } + + /* + * We increment nr_bufs by 2 to allow us to handle non + * page-aligned buffer addresses and buffers whose sizes + * are not divisible by PAGE_SIZE. + */ + status = qat_init_cy_buffer_lists(cy_inst_handle, nr_bufs, + &src_buffer_list, &dst_buffer_list); + if (status != CPA_STATUS_SUCCESS) + goto fail; + + status = QAT_PHYS_CONTIG_ALLOC(&flat_src_buf_array, + nr_bufs * sizeof (CpaFlatBuffer)); + if (status != CPA_STATUS_SUCCESS) + goto fail; + status = QAT_PHYS_CONTIG_ALLOC(&flat_dst_buf_array, + nr_bufs * sizeof (CpaFlatBuffer)); + if (status != CPA_STATUS_SUCCESS) + goto fail; + status = QAT_PHYS_CONTIG_ALLOC(&op_data.pDigestResult, + ZIO_DATA_MAC_LEN); + if (status != CPA_STATUS_SUCCESS) + goto fail; + status = QAT_PHYS_CONTIG_ALLOC(&op_data.pIv, + ZIO_DATA_IV_LEN); + if (status != CPA_STATUS_SUCCESS) + goto fail; + if (aad_len > 0) { + status = QAT_PHYS_CONTIG_ALLOC(&op_data.pAdditionalAuthData, + aad_len); + if (status != CPA_STATUS_SUCCESS) + goto fail; + bcopy(aad_buf, op_data.pAdditionalAuthData, aad_len); + } + + bytes_left = enc_len; + data = src_buf; + flat_src_buf = flat_src_buf_array; + while (bytes_left > 0) { + in_page_off = ((long)data & ~PAGE_MASK); + in_pages[in_page_num] = qat_mem_to_page(data); + flat_src_buf->pData = kmap(in_pages[in_page_num]) + in_page_off; + flat_src_buf->dataLenInBytes = + min((long)PAGE_SIZE - in_page_off, (long)bytes_left); + data += flat_src_buf->dataLenInBytes; + bytes_left -= flat_src_buf->dataLenInBytes; + flat_src_buf++; + in_page_num++; + } + src_buffer_list.pBuffers = flat_src_buf_array; + src_buffer_list.numBuffers = in_page_num; + + bytes_left = enc_len; + data = dst_buf; + flat_dst_buf = flat_dst_buf_array; + while (bytes_left > 0) { + out_page_off = ((long)data & ~PAGE_MASK); + out_pages[out_page_num] = qat_mem_to_page(data); + flat_dst_buf->pData = kmap(out_pages[out_page_num]) + + out_page_off; + flat_dst_buf->dataLenInBytes = + min((long)PAGE_SIZE - out_page_off, (long)bytes_left); + data += flat_dst_buf->dataLenInBytes; + bytes_left -= flat_dst_buf->dataLenInBytes; + flat_dst_buf++; + out_page_num++; + } + dst_buffer_list.pBuffers = flat_dst_buf_array; + dst_buffer_list.numBuffers = out_page_num; + + op_data.sessionCtx = cy_session_ctx; + op_data.packetType = CPA_CY_SYM_PACKET_TYPE_FULL; + op_data.cryptoStartSrcOffsetInBytes = 0; + op_data.messageLenToCipherInBytes = 0; + op_data.hashStartSrcOffsetInBytes = 0; + op_data.messageLenToHashInBytes = 0; + op_data.messageLenToCipherInBytes = enc_len; + op_data.ivLenInBytes = ZIO_DATA_IV_LEN; + bcopy(iv_buf, op_data.pIv, ZIO_DATA_IV_LEN); + /* if dir is QAT_DECRYPT, copy digest_buf to pDigestResult */ + if (dir == QAT_DECRYPT) + bcopy(digest_buf, op_data.pDigestResult, ZIO_DATA_MAC_LEN); + + cb.verify_result = CPA_FALSE; + init_completion(&cb.complete); + status = cpaCySymPerformOp(cy_inst_handle, &cb, &op_data, + &src_buffer_list, &dst_buffer_list, NULL); + if (status != CPA_STATUS_SUCCESS) + goto fail; + + /* we now wait until the completion of the operation. */ + wait_for_completion(&cb.complete); + + if (cb.verify_result == CPA_FALSE) { + status = CPA_STATUS_FAIL; + goto fail; + } + + if (dir == QAT_ENCRYPT) { + /* if dir is QAT_ENCRYPT, save pDigestResult to digest_buf */ + bcopy(op_data.pDigestResult, digest_buf, ZIO_DATA_MAC_LEN); + QAT_STAT_INCR(encrypt_total_out_bytes, enc_len); + } else { + QAT_STAT_INCR(decrypt_total_out_bytes, enc_len); + } + +fail: + if (status != CPA_STATUS_SUCCESS) + QAT_STAT_BUMP(crypt_fails); + + for (i = 0; i < in_page_num; i++) + kunmap(in_pages[i]); + for (i = 0; i < out_page_num; i++) + kunmap(out_pages[i]); + + cpaCySymRemoveSession(cy_inst_handle, cy_session_ctx); + if (aad_len > 0) + QAT_PHYS_CONTIG_FREE(op_data.pAdditionalAuthData); + QAT_PHYS_CONTIG_FREE(op_data.pIv); + QAT_PHYS_CONTIG_FREE(op_data.pDigestResult); + QAT_PHYS_CONTIG_FREE(src_buffer_list.pPrivateMetaData); + QAT_PHYS_CONTIG_FREE(dst_buffer_list.pPrivateMetaData); + QAT_PHYS_CONTIG_FREE(cy_session_ctx); + QAT_PHYS_CONTIG_FREE(flat_src_buf_array); + QAT_PHYS_CONTIG_FREE(flat_dst_buf_array); + + return (status); +} + +int +qat_checksum(uint64_t cksum, uint8_t *buf, uint64_t size, zio_cksum_t *zcp) +{ + CpaStatus status; + Cpa16U i; + CpaInstanceHandle cy_inst_handle; + Cpa16U nr_bufs = (size >> PAGE_SHIFT) + 2; + Cpa32U bytes_left = 0; + Cpa8S *data = NULL; + CpaCySymSessionCtx *cy_session_ctx = NULL; + cy_callback_t cb; + Cpa8U *digest_buffer = NULL; + CpaCySymOpData op_data = { 0 }; + CpaBufferList src_buffer_list = { 0 }; + CpaFlatBuffer *flat_src_buf_array = NULL; + CpaFlatBuffer *flat_src_buf = NULL; + struct page *in_pages[MAX_PAGE_NUM]; + Cpa32U page_num = 0; + Cpa32U page_off = 0; + + QAT_STAT_BUMP(cksum_requests); + QAT_STAT_INCR(cksum_total_in_bytes, size); + + i = (Cpa32U)atomic_inc_32_nv(&inst_num) % num_inst; + cy_inst_handle = cy_inst_handles[i]; + + status = qat_init_checksum_session_ctx(cy_inst_handle, + &cy_session_ctx, cksum); + if (status != CPA_STATUS_SUCCESS) { + /* don't count unsupported checksums as a failure */ + if (cksum == ZIO_CHECKSUM_SHA256 || + cksum == ZIO_CHECKSUM_SHA512) + QAT_STAT_BUMP(cksum_fails); + return (status); + } + + /* + * We increment nr_bufs by 2 to allow us to handle non + * page-aligned buffer addresses and buffers whose sizes + * are not divisible by PAGE_SIZE. + */ + status = qat_init_cy_buffer_lists(cy_inst_handle, nr_bufs, + &src_buffer_list, &src_buffer_list); + if (status != CPA_STATUS_SUCCESS) + goto fail; + + status = QAT_PHYS_CONTIG_ALLOC(&flat_src_buf_array, + nr_bufs * sizeof (CpaFlatBuffer)); + if (status != CPA_STATUS_SUCCESS) + goto fail; + status = QAT_PHYS_CONTIG_ALLOC(&digest_buffer, + sizeof (zio_cksum_t)); + if (status != CPA_STATUS_SUCCESS) + goto fail; + + bytes_left = size; + data = buf; + flat_src_buf = flat_src_buf_array; + while (bytes_left > 0) { + page_off = ((long)data & ~PAGE_MASK); + in_pages[page_num] = qat_mem_to_page(data); + flat_src_buf->pData = kmap(in_pages[page_num]) + page_off; + flat_src_buf->dataLenInBytes = + min((long)PAGE_SIZE - page_off, (long)bytes_left); + data += flat_src_buf->dataLenInBytes; + bytes_left -= flat_src_buf->dataLenInBytes; + flat_src_buf++; + page_num++; + } + src_buffer_list.pBuffers = flat_src_buf_array; + src_buffer_list.numBuffers = page_num; + + op_data.sessionCtx = cy_session_ctx; + op_data.packetType = CPA_CY_SYM_PACKET_TYPE_FULL; + op_data.hashStartSrcOffsetInBytes = 0; + op_data.messageLenToHashInBytes = size; + op_data.pDigestResult = digest_buffer; + + cb.verify_result = CPA_FALSE; + init_completion(&cb.complete); + status = cpaCySymPerformOp(cy_inst_handle, &cb, &op_data, + &src_buffer_list, &src_buffer_list, NULL); + if (status != CPA_STATUS_SUCCESS) + goto fail; + + /* we now wait until the completion of the operation. */ + wait_for_completion(&cb.complete); + + if (cb.verify_result == CPA_FALSE) { + status = CPA_STATUS_FAIL; + goto fail; + } + + bcopy(digest_buffer, zcp, sizeof (zio_cksum_t)); + +fail: + if (status != CPA_STATUS_SUCCESS) + QAT_STAT_BUMP(cksum_fails); + + for (i = 0; i < page_num; i++) + kunmap(in_pages[i]); + + cpaCySymRemoveSession(cy_inst_handle, cy_session_ctx); + QAT_PHYS_CONTIG_FREE(digest_buffer); + QAT_PHYS_CONTIG_FREE(src_buffer_list.pPrivateMetaData); + QAT_PHYS_CONTIG_FREE(cy_session_ctx); + QAT_PHYS_CONTIG_FREE(flat_src_buf_array); + + return (status); +} + +static int +param_set_qat_encrypt(const char *val, zfs_kernel_param_t *kp) +{ + int ret; + int *pvalue = kp->arg; + ret = param_set_int(val, kp); + if (ret) + return (ret); + /* + * zfs_qat_encrypt_disable = 0: enable qat encrypt + * try to initialize qat instance if it has not been done + */ + if (*pvalue == 0 && !qat_cy_init_done) { + ret = qat_cy_init(); + if (ret != 0) { + zfs_qat_encrypt_disable = 1; + return (ret); + } + } + return (ret); +} + +static int +param_set_qat_checksum(const char *val, zfs_kernel_param_t *kp) +{ + int ret; + int *pvalue = kp->arg; + ret = param_set_int(val, kp); + if (ret) + return (ret); + /* + * set_checksum_param_ops = 0: enable qat checksum + * try to initialize qat instance if it has not been done + */ + if (*pvalue == 0 && !qat_cy_init_done) { + ret = qat_cy_init(); + if (ret != 0) { + zfs_qat_checksum_disable = 1; + return (ret); + } + } + return (ret); +} + +module_param_call(zfs_qat_encrypt_disable, param_set_qat_encrypt, + param_get_int, &zfs_qat_encrypt_disable, 0644); +MODULE_PARM_DESC(zfs_qat_encrypt_disable, "Enable/Disable QAT encryption"); + +module_param_call(zfs_qat_checksum_disable, param_set_qat_checksum, + param_get_int, &zfs_qat_checksum_disable, 0644); +MODULE_PARM_DESC(zfs_qat_checksum_disable, "Enable/Disable QAT checksumming"); + +#endif diff --git a/module/os/macos/zfs/spa_misc_os.c b/module/os/macos/zfs/spa_misc_os.c new file mode 100644 index 000000000000..aa546edd1b9e --- /dev/null +++ b/module/os/macos/zfs/spa_misc_os.c @@ -0,0 +1,116 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2019 by Delphix. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved. + * Copyright 2013 Saso Kiselkov. All rights reserved. + * Copyright (c) 2017 Datto Inc. + * Copyright (c) 2017, Intel Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zfs_prop.h" + +const char * +spa_history_zone(void) +{ + return ("macos"); +} + +void +spa_create_os(void *arg) +{ + spa_t *spa = (spa_t *)arg; + int haslock = 0; + int error; + + haslock = mutex_owned(&spa_namespace_lock); + + /* Increase open refcount */ + spa_open_ref(spa, FTAG); + + if (haslock) { + mutex_exit(&spa_namespace_lock); + } + + /* Create IOKit pool proxy */ + if ((error = spa_iokit_pool_proxy_create(spa)) != 0) { + printf("%s spa_iokit_pool_proxy_create error %d\n", + __func__, error); + /* spa_create succeeded, ignore proxy error */ + } + + /* Cache vdev info, needs open ref above, and pool proxy */ + + if (error == 0 && (error = zfs_boot_update_bootinfo(spa)) != 0) { + printf("%s update_bootinfo error %d\n", __func__, error); + /* create succeeded, ignore error from bootinfo */ + } + + /* Drop open refcount */ + if (haslock) { + mutex_enter(&spa_namespace_lock); + } + + spa_close(spa, FTAG); +} + +void +spa_export_os(void *arg) +{ + spa_t *spa = (spa_t *)arg; + + /* Remove IOKit pool proxy */ + spa_iokit_pool_proxy_destroy(spa); +} + +void +spa_activate_os(void *arg) +{ + /* spa_t *spa = (spa_t *)arg; */ + /* Lock kext in kernel while mounted */ + OSKextRetainKextWithLoadTag(OSKextGetCurrentLoadTag()); +} + +void +spa_deactivate_os(void *arg) +{ + /* spa_t *spa = (spa_t *)arg; */ + OSKextReleaseKextWithLoadTag(OSKextGetCurrentLoadTag()); +} diff --git a/module/os/macos/zfs/trace.c b/module/os/macos/zfs/trace.c new file mode 100644 index 000000000000..0c9990e8547b --- /dev/null +++ b/module/os/macos/zfs/trace.c @@ -0,0 +1,50 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Each Linux tracepoints subsystem must define CREATE_TRACE_POINTS in one + * (and only one) C file, so this dummy file exists for that purpose. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CREATE_TRACE_POINTS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/module/os/macos/zfs/vdev_disk.c b/module/os/macos/zfs/vdev_disk.c new file mode 100644 index 000000000000..391ee429dd3b --- /dev/null +++ b/module/os/macos/zfs/vdev_disk.c @@ -0,0 +1,797 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Based on Apple MacZFS source code + * Copyright (c) 2014,2016 by Jorgen Lundman. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016 by Delphix. All rights reserved. + * Copyright 2016 Nexenta Systems, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Virtual device vector for disks. + */ + +/* XXX leave extern if declared elsewhere - originally was in zfs_ioctl.c */ +ldi_ident_t zfs_li; + +static void vdev_disk_close(vdev_t *); + +typedef struct vdev_disk_ldi_cb { + list_node_t lcb_next; + ldi_callback_id_t lcb_id; +} vdev_disk_ldi_cb_t; + +static void +vdev_disk_alloc(vdev_t *vd) +{ + vdev_disk_t *dvd; + + dvd = vd->vdev_tsd = kmem_zalloc(sizeof (vdev_disk_t), KM_SLEEP); + + /* + * Create the LDI event callback list. + */ + list_create(&dvd->vd_ldi_cbs, sizeof (vdev_disk_ldi_cb_t), + offsetof(vdev_disk_ldi_cb_t, lcb_next)); +} + +static void +vdev_disk_free(vdev_t *vd) +{ + vdev_disk_t *dvd = vd->vdev_tsd; + vdev_disk_ldi_cb_t *lcb; + + if (dvd == NULL) + return; + + /* + * We have already closed the LDI handle. Clean up the LDI event + * callbacks and free vd->vdev_tsd. + */ + while ((lcb = list_head(&dvd->vd_ldi_cbs)) != NULL) { + list_remove(&dvd->vd_ldi_cbs, lcb); + (void) ldi_ev_remove_callbacks(lcb->lcb_id); + kmem_free(lcb, sizeof (vdev_disk_ldi_cb_t)); + } + list_destroy(&dvd->vd_ldi_cbs); + kmem_free(dvd, sizeof (vdev_disk_t)); + vd->vdev_tsd = NULL; +} + +static int +vdev_disk_off_notify(ldi_handle_t lh, ldi_ev_cookie_t ecookie, void *arg, + void *ev_data) +{ + vdev_t *vd = (vdev_t *)arg; + vdev_disk_t *dvd = vd->vdev_tsd; + + /* + * Ignore events other than offline. + */ + if (strcmp(ldi_ev_get_type(ecookie), LDI_EV_OFFLINE) != 0) + return (LDI_EV_SUCCESS); + + /* + * All LDI handles must be closed for the state change to succeed, so + * call on vdev_disk_close() to do this. + * + * We inform vdev_disk_close that it is being called from offline + * notify context so it will defer cleanup of LDI event callbacks and + * freeing of vd->vdev_tsd to the offline finalize or a reopen. + */ + dvd->vd_ldi_offline = B_TRUE; + vdev_disk_close(vd); + + /* + * Now that the device is closed, request that the spa_async_thread + * mark the device as REMOVED and notify FMA of the removal. + */ + zfs_post_remove(vd->vdev_spa, vd); + vd->vdev_remove_wanted = B_TRUE; + spa_async_request(vd->vdev_spa, SPA_ASYNC_REMOVE); + + return (LDI_EV_SUCCESS); +} + +/* ARGSUSED */ +static void +vdev_disk_off_finalize(ldi_handle_t lh, ldi_ev_cookie_t ecookie, + int ldi_result, void *arg, void *ev_data) +{ + vdev_t *vd = (vdev_t *)arg; + + /* + * Ignore events other than offline. + */ + if (strcmp(ldi_ev_get_type(ecookie), LDI_EV_OFFLINE) != 0) + return; + + /* + * We have already closed the LDI handle in notify. + * Clean up the LDI event callbacks and free vd->vdev_tsd. + */ + vdev_disk_free(vd); + /* + * Request that the vdev be reopened if the offline state change was + * unsuccessful. + */ + if (ldi_result != LDI_EV_SUCCESS) { + vd->vdev_probe_wanted = B_TRUE; + spa_async_request(vd->vdev_spa, SPA_ASYNC_PROBE); + } +} + +static ldi_ev_callback_t vdev_disk_off_callb = { + .cb_vers = LDI_EV_CB_VERS, + .cb_notify = vdev_disk_off_notify, + .cb_finalize = vdev_disk_off_finalize +}; + +/* + * We want to be loud in DEBUG kernels when DKIOCGMEDIAINFOEXT fails, or when + * even a fallback to DKIOCGMEDIAINFO fails. + */ +#ifdef DEBUG +#define VDEV_DEBUG(...) cmn_err(CE_NOTE, __VA_ARGS__) +#else +#define VDEV_DEBUG(...) /* Nothing... */ +#endif + +static int +vdev_disk_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize, + uint64_t *logical_ashift, uint64_t *physical_ashift) +{ + spa_t *spa = vd->vdev_spa; + vdev_disk_t *dvd = vd->vdev_tsd; + ldi_ev_cookie_t ecookie; + vdev_disk_ldi_cb_t *lcb; + union { + struct dk_minfo_ext ude; + struct dk_minfo ud; + } dks; + struct dk_minfo_ext *dkmext = &dks.ude; + struct dk_minfo *dkm = &dks.ud; + int error; + uint64_t capacity = 0, blksz = 0, pbsize; + int isssd; + + /* + * We must have a pathname, and it must be absolute. + */ + if (vd->vdev_path == NULL || vd->vdev_path[0] != '/') { + vd->vdev_stat.vs_aux = VDEV_AUX_BAD_LABEL; + return (SET_ERROR(EINVAL)); + } + + /* + * Reopen the device if it's not currently open. Otherwise, + * just update the physical size of the device. + */ + if (dvd != NULL) { + if (dvd->vd_ldi_offline && dvd->vd_lh == NULL) { + /* + * If we are opening a device in its offline notify + * context, the LDI handle was just closed. Clean + * up the LDI event callbacks and free vd->vdev_tsd. + */ + vdev_disk_free(vd); + } else { + ASSERT(vd->vdev_reopening); + goto skip_open; + } + } + + /* + * Create vd->vdev_tsd. + */ + vdev_disk_alloc(vd); + dvd = vd->vdev_tsd; + + /* + * When opening a disk device, we want to preserve the user's original + * intent. We always want to open the device by the path the user gave + * us, even if it is one of multiple paths to the same device. But we + * also want to be able to survive disks being removed/recabled. + * Therefore the sequence of opening devices is: + * + * 1. Try opening the device by path. For legacy pools without the + * 'whole_disk' property, attempt to fix the path by appending 's0'. + * + * 2. If the devid of the device matches the stored value, return + * success. + * + * 3. Otherwise, the device may have moved. Try opening the device + * by the devid instead. + */ + + error = EINVAL; /* presume failure */ + + if (vd->vdev_path != NULL) { + + /* + * If we have not yet opened the device, try to open it by the + * specified path. + */ + if (error != 0) { + error = ldi_open_by_name(vd->vdev_path, spa_mode(spa), + kcred, &dvd->vd_lh, zfs_li); + } + + /* + * If we succeeded in opening the device, but 'vdev_wholedisk' + * is not yet set, then this must be a slice. + */ + if (error == 0 && vd->vdev_wholedisk == -1ULL) + vd->vdev_wholedisk = 0; + } + + /* + * If all else fails, then try opening by physical path (if available) + * or the logical path (if we failed due to the devid check). While not + * as reliable as the devid, this will give us something, and the higher + * level vdev validation will prevent us from opening the wrong device. + */ + if (error) { + + /* + * Note that we don't support the legacy auto-wholedisk support + * as above. This hasn't been used in a very long time and we + * don't need to propagate its oddities to this edge condition. + */ + if (error && vd->vdev_path != NULL) + error = ldi_open_by_name(vd->vdev_path, spa_mode(spa), + kcred, &dvd->vd_lh, zfs_li); + } + + if (error) { + vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED; + vdev_dbgmsg(vd, "vdev_disk_open: failed to open [error=%d]", + error); + return (error); + } + + /* + * Register callbacks for the LDI offline event. + */ + if (ldi_ev_get_cookie(dvd->vd_lh, LDI_EV_OFFLINE, &ecookie) == + LDI_EV_SUCCESS) { + lcb = kmem_zalloc(sizeof (vdev_disk_ldi_cb_t), KM_SLEEP); + list_insert_tail(&dvd->vd_ldi_cbs, lcb); + (void) ldi_ev_register_callbacks(dvd->vd_lh, ecookie, + &vdev_disk_off_callb, (void *) vd, &lcb->lcb_id); + } + +skip_open: + /* + * Determine the actual size of the device. + */ + if (ldi_get_size(dvd->vd_lh, psize) != 0) { + vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED; + vdev_dbgmsg(vd, "vdev_disk_open: failed to get size"); + return (SET_ERROR(EINVAL)); + } + + *max_psize = *psize; + + /* + * Determine the device's minimum transfer size. + * If the ioctl isn't supported, assume DEV_BSIZE. + */ + if ((error = ldi_ioctl(dvd->vd_lh, DKIOCGMEDIAINFOEXT, + (intptr_t)dkmext, FKIOCTL, kcred, NULL)) == 0) { + capacity = dkmext->dki_capacity - 1; + blksz = dkmext->dki_lbsize; + pbsize = dkmext->dki_pbsize; + } else if ((error = ldi_ioctl(dvd->vd_lh, DKIOCGMEDIAINFO, + (intptr_t)dkm, FKIOCTL, kcred, NULL)) == 0) { + VDEV_DEBUG( + "vdev_disk_open(\"%s\"): fallback to DKIOCGMEDIAINFO\n", + vd->vdev_path); + capacity = dkm->dki_capacity - 1; + blksz = dkm->dki_lbsize; + pbsize = blksz; + } else { + VDEV_DEBUG("vdev_disk_open(\"%s\"): " + "both DKIOCGMEDIAINFO{,EXT} calls failed, %d\n", + vd->vdev_path, error); + pbsize = DEV_BSIZE; + } + + *physical_ashift = highbit64(MAX(pbsize, + SPA_MINBLOCKSIZE)) - 1; + + *logical_ashift = highbit64(MAX(pbsize, + SPA_MINBLOCKSIZE)) - 1; + + if (vd->vdev_wholedisk == 1) { + int wce = 1; + + /* + * Since we own the whole disk, try to enable disk write + * caching. We ignore errors because it's OK if we can't do it. + */ + (void) ldi_ioctl(dvd->vd_lh, DKIOCSETWCE, (intptr_t)&wce, + FKIOCTL, kcred, NULL); + } + + /* + * Clear the nowritecache bit, so that on a vdev_reopen() we will + * try again. + */ + vd->vdev_nowritecache = B_FALSE; + + /* Inform the ZIO pipeline that we are non-rotational */ + vd->vdev_nonrot = B_FALSE; + if (ldi_ioctl(dvd->vd_lh, DKIOCISSOLIDSTATE, (intptr_t)&isssd, + FKIOCTL, kcred, NULL) == 0) { + vd->vdev_nonrot = (isssd ? B_TRUE : B_FALSE); + } + + // Assume no TRIM + vd->vdev_has_trim = B_FALSE; + uint32_t features; + if (ldi_ioctl(dvd->vd_lh, DKIOCGETFEATURES, (intptr_t)&features, + FKIOCTL, kcred, NULL) == 0) { + if (features & DK_FEATURE_UNMAP) + vd->vdev_has_trim = B_TRUE; + } + + /* Set when device reports it supports secure TRIM. */ + // No secure trim in Apple yet. + vd->vdev_has_securetrim = B_FALSE; + + return (0); +} + +static void +vdev_disk_close(vdev_t *vd) +{ + vdev_disk_t *dvd = vd->vdev_tsd; + + if (vd->vdev_reopening || dvd == NULL) + return; + + if (dvd->vd_lh != NULL) { + (void) ldi_close(dvd->vd_lh, spa_mode(vd->vdev_spa), kcred); + dvd->vd_lh = NULL; + } + + vd->vdev_delayed_close = B_FALSE; + /* + * If we closed the LDI handle due to an offline notify from LDI, + * don't free vd->vdev_tsd or unregister the callbacks here; + * the offline finalize callback or a reopen will take care of it. + */ + if (dvd->vd_ldi_offline) + return; + + vdev_disk_free(vd); +} + +int +vdev_disk_physio(vdev_t *vd, caddr_t data, + size_t size, uint64_t offset, int flags, boolean_t isdump) +{ + vdev_disk_t *dvd = vd->vdev_tsd; + + /* + * If the vdev is closed, it's likely in the REMOVED or FAULTED state. + * Nothing to be done here but return failure. + */ + if (dvd == NULL || (dvd->vd_ldi_offline && dvd->vd_lh == NULL)) + return (EIO); + + ASSERT(vd->vdev_ops == &vdev_disk_ops); + + return (vdev_disk_ldi_physio(dvd->vd_lh, data, size, offset, flags)); +} + +int +vdev_disk_ldi_physio(ldi_handle_t vd_lh, caddr_t data, + size_t size, uint64_t offset, int flags) +{ + ldi_buf_t *bp; + int error = 0; + + if (vd_lh == NULL) + return (SET_ERROR(EINVAL)); + + ASSERT(flags & B_READ || flags & B_WRITE); + + bp = getrbuf(KM_SLEEP); + bp->b_flags = flags | B_BUSY | B_NOCACHE; + bp->b_bcount = size; + bp->b_un.b_addr = (void *)data; + bp->b_lblkno = lbtodb(offset); + bp->b_bufsize = size; + + error = ldi_strategy(vd_lh, bp); + ASSERT(error == 0); + + if ((error = biowait(bp)) == 0 && bp->b_resid != 0) + error = SET_ERROR(EIO); + freerbuf(bp); + + return (error); +} + +static void +vdev_disk_io_intr(ldi_buf_t *bp) +{ + vdev_buf_t *vb = (vdev_buf_t *)bp; + zio_t *zio = vb->vb_io; + + /* + * The rest of the zio stack only deals with EIO, ECKSUM, and ENXIO. + * Rather than teach the rest of the stack about other error + * possibilities (EFAULT, etc), we normalize the error value here. + */ + zio->io_error = (geterror(bp) != 0 ? EIO : 0); + + if (zio->io_error == 0 && bp->b_resid != 0) + zio->io_error = SET_ERROR(EIO); + + if (zio->io_type == ZIO_TYPE_READ) { + abd_return_buf_copy(zio->io_abd, bp->b_un.b_addr, + zio->io_size); + } else { + abd_return_buf(zio->io_abd, bp->b_un.b_addr, + zio->io_size); + } + + kmem_free(vb, sizeof (vdev_buf_t)); + + zio_delay_interrupt(zio); +} + +static void +vdev_disk_ioctl_free(zio_t *zio) +{ + kmem_free(zio->io_vsd, sizeof (struct dk_callback)); +} + +static const zio_vsd_ops_t vdev_disk_vsd_ops = { + vdev_disk_ioctl_free, + zio_vsd_default_cksum_report +}; + +static void +vdev_disk_ioctl_done(void *zio_arg, int error) +{ + zio_t *zio = zio_arg; + + zio->io_error = error; + + zio_interrupt(zio); +} + +static void +vdev_disk_io_start(zio_t *zio) +{ + vdev_t *vd = zio->io_vd; + vdev_disk_t *dvd = vd->vdev_tsd; + vdev_buf_t *vb; + struct dk_callback *dkc; + ldi_buf_t *bp = 0; + int flags, error = 0; + + /* + * If the vdev is closed, it's likely in the REMOVED or FAULTED state. + * Nothing to be done here but return failure. + */ + if (dvd == NULL || (dvd->vd_ldi_offline && dvd->vd_lh == NULL)) { + zio->io_error = ENXIO; + zio_interrupt(zio); + return; + } + + switch (zio->io_type) { + case ZIO_TYPE_IOCTL: + + if (!vdev_readable(vd)) { + zio->io_error = SET_ERROR(ENXIO); + zio_interrupt(zio); + return; + } + + switch (zio->io_cmd) { + case DKIOCFLUSHWRITECACHE: + + if (zfs_nocacheflush) + break; + + if (vd->vdev_nowritecache) { + zio->io_error = SET_ERROR(ENOTSUP); + break; + } + + zio->io_vsd = dkc = kmem_alloc(sizeof (*dkc), KM_SLEEP); + zio->io_vsd_ops = &vdev_disk_vsd_ops; + + dkc->dkc_callback = vdev_disk_ioctl_done; + dkc->dkc_flag = FLUSH_VOLATILE; + dkc->dkc_cookie = zio; + + error = ldi_ioctl(dvd->vd_lh, zio->io_cmd, + (uintptr_t)dkc, FKIOCTL, kcred, NULL); + + if (error == 0) { + /* + * The ioctl will be done asychronously, + * and will call vdev_disk_ioctl_done() + * upon completion. + */ + return; + } + + zio->io_error = error; + + break; + + default: + zio->io_error = SET_ERROR(ENOTSUP); + } /* io_cmd */ + + zio_execute(zio); + return; + + case ZIO_TYPE_WRITE: + if (zio->io_priority == ZIO_PRIORITY_SYNC_WRITE) + flags = B_WRITE; + else + flags = B_WRITE | B_ASYNC; + break; + + case ZIO_TYPE_READ: + if (zio->io_priority == ZIO_PRIORITY_SYNC_READ) + flags = B_READ; + else + flags = B_READ | B_ASYNC; + break; + + case ZIO_TYPE_TRIM: + { + dkioc_free_list_ext_t dfle; + dfle.dfle_start = zio->io_offset; + dfle.dfle_length = zio->io_size; + zio->io_error = ldi_ioctl(dvd->vd_lh, DKIOCFREE, + (uintptr_t)&dfle, FKIOCTL, kcred, NULL); + zio_interrupt(zio); + return; + } + + default: + zio->io_error = SET_ERROR(ENOTSUP); + zio_execute(zio); + return; + } /* io_type */ + + ASSERT(zio->io_type == ZIO_TYPE_READ || zio->io_type == ZIO_TYPE_WRITE); + + /* Stop OSX from also caching our data */ + flags |= B_NOCACHE | B_PASSIVE; + + zio->io_target_timestamp = zio_handle_io_delay(zio); + + vb = kmem_alloc(sizeof (vdev_buf_t), KM_SLEEP); + + vb->vb_io = zio; + bp = &vb->vb_buf; + + ASSERT(bp != NULL); + ASSERT(zio->io_abd != NULL); + ASSERT(zio->io_size != 0); + + bioinit(bp); + bp->b_flags = B_BUSY | flags; + bp->b_bcount = zio->io_size; + + if (zio->io_type == ZIO_TYPE_READ) { + bp->b_un.b_addr = + abd_borrow_buf(zio->io_abd, zio->io_size); + } else { + bp->b_un.b_addr = + abd_borrow_buf_copy(zio->io_abd, zio->io_size); + } + + bp->b_lblkno = lbtodb(zio->io_offset); + bp->b_bufsize = zio->io_size; + bp->b_iodone = (int (*)(struct ldi_buf *))vdev_disk_io_intr; + + error = ldi_strategy(dvd->vd_lh, bp); + if (error != 0) { + dprintf("%s error from ldi_strategy %d\n", __func__, error); + zio->io_error = EIO; + kmem_free(vb, sizeof (vdev_buf_t)); + zio_execute(zio); + // zio_interrupt(zio); + } +} + +static void +vdev_disk_io_done(zio_t *zio) +{ + vdev_t *vd = zio->io_vd; + + /* + * If the device returned EIO, then attempt a DKIOCSTATE ioctl to see if + * the device has been removed. If this is the case, then we trigger an + * asynchronous removal of the device. Otherwise, probe the device and + * make sure it's still accessible. + */ + if (zio->io_error == EIO && !vd->vdev_remove_wanted) { + vdev_disk_t *dvd = vd->vdev_tsd; + int state = DKIO_NONE; + + if (ldi_ioctl(dvd->vd_lh, DKIOCSTATE, (intptr_t)&state, + FKIOCTL, kcred, NULL) == 0 && state != DKIO_INSERTED) { + /* + * We post the resource as soon as possible, instead of + * when the async removal actually happens, because the + * DE is using this information to discard previous I/O + * errors. + */ + zfs_post_remove(zio->io_spa, vd); + vd->vdev_remove_wanted = B_TRUE; + spa_async_request(zio->io_spa, SPA_ASYNC_REMOVE); + } else if (!vd->vdev_delayed_close) { + vd->vdev_delayed_close = B_TRUE; + } + } +} + +static void +vdev_disk_hold(vdev_t *vd) +{ + ASSERT(spa_config_held(vd->vdev_spa, SCL_STATE, RW_WRITER)); + + /* We must have a pathname, and it must be absolute. */ + if (vd->vdev_path == NULL || vd->vdev_path[0] != '/') + return; + + /* + * Only prefetch path and devid info if the device has + * never been opened. + */ + if (vd->vdev_tsd != NULL) + return; + +} + +static void +vdev_disk_rele(vdev_t *vd) +{ + ASSERT(spa_config_held(vd->vdev_spa, SCL_STATE, RW_WRITER)); + + /* XXX: Implement me as a vnode rele for the device */ +} + +vdev_ops_t vdev_disk_ops = { + .vdev_op_init = NULL, + .vdev_op_fini = NULL, + .vdev_op_open = vdev_disk_open, + .vdev_op_close = vdev_disk_close, + .vdev_op_asize = vdev_default_asize, + .vdev_op_min_asize = vdev_default_min_asize, + .vdev_op_min_alloc = NULL, + .vdev_op_io_start = vdev_disk_io_start, + .vdev_op_io_done = vdev_disk_io_done, + .vdev_op_state_change = NULL, + .vdev_op_need_resilver = NULL, + .vdev_op_hold = vdev_disk_hold, + .vdev_op_rele = vdev_disk_rele, + .vdev_op_remap = NULL, + .vdev_op_xlate = vdev_default_xlate, + .vdev_op_rebuild_asize = NULL, + .vdev_op_metaslab_init = NULL, + .vdev_op_config_generate = NULL, + .vdev_op_nparity = NULL, + .vdev_op_ndisks = NULL, + .vdev_op_type = VDEV_TYPE_DISK, /* name of this vdev type */ + .vdev_op_leaf = B_TRUE /* leaf vdev */ +}; + +/* + * Given the root disk device devid or pathname, read the label from + * the device, and construct a configuration nvlist. + */ +int +vdev_disk_read_rootlabel(char *devpath, char *devid, nvlist_t **config) +{ + ldi_handle_t vd_lh; + vdev_label_t *label; + uint64_t s, size; + int l; + int error = -1; + + /* + * Read the device label and build the nvlist. + */ + + /* Apple: Error will be -1 at this point, allowing open_by_name */ + error = -1; + vd_lh = 0; /* Dismiss compiler warning */ + + if (error && (error = ldi_open_by_name(devpath, FREAD, kcred, &vd_lh, + zfs_li))) + return (error); + + if (ldi_get_size(vd_lh, &s)) { + (void) ldi_close(vd_lh, FREAD, kcred); + return (SET_ERROR(EIO)); + } + + size = P2ALIGN_TYPED(s, sizeof (vdev_label_t), uint64_t); + label = kmem_alloc(sizeof (vdev_label_t), KM_SLEEP); + + *config = NULL; + for (l = 0; l < VDEV_LABELS; l++) { + uint64_t offset, state, txg = 0; + + /* read vdev label */ + offset = vdev_label_offset(size, l, 0); + if (vdev_disk_ldi_physio(vd_lh, (caddr_t)label, + VDEV_SKIP_SIZE + VDEV_PHYS_SIZE, offset, B_READ) != 0) + continue; + + if (nvlist_unpack(label->vl_vdev_phys.vp_nvlist, + sizeof (label->vl_vdev_phys.vp_nvlist), config, 0) != 0) { + *config = NULL; + continue; + } + + if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_STATE, + &state) != 0 || state >= POOL_STATE_DESTROYED) { + nvlist_free(*config); + *config = NULL; + continue; + } + + if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_TXG, + &txg) != 0 || txg == 0) { + nvlist_free(*config); + *config = NULL; + continue; + } + + break; + } + + kmem_free(label, sizeof (vdev_label_t)); + (void) ldi_close(vd_lh, FREAD, kcred); + if (*config == NULL) + error = SET_ERROR(EIDRM); + + return (error); +} diff --git a/module/os/macos/zfs/vdev_file.c b/module/os/macos/zfs/vdev_file.c new file mode 100644 index 000000000000..c685a872ebfe --- /dev/null +++ b/module/os/macos/zfs/vdev_file.c @@ -0,0 +1,338 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2016 by Delphix. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Virtual device vector for files. + */ + +static taskq_t *vdev_file_taskq; + +unsigned long vdev_file_logical_ashift = SPA_MINBLOCKSHIFT; +unsigned long vdev_file_physical_ashift = SPA_MINBLOCKSHIFT; + +static void +vdev_file_hold(vdev_t *vd) +{ + ASSERT(vd->vdev_path != NULL); +} + +static void +vdev_file_rele(vdev_t *vd) +{ + ASSERT(vd->vdev_path != NULL); +} + +static mode_t +vdev_file_open_mode(spa_mode_t spa_mode) +{ + mode_t mode = 0; + + if ((spa_mode & SPA_MODE_READ) && (spa_mode & SPA_MODE_WRITE)) { + mode = O_RDWR; + } else if (spa_mode & SPA_MODE_READ) { + mode = O_RDONLY; + } else if (spa_mode & SPA_MODE_WRITE) { + mode = O_WRONLY; + } + + return (mode | O_LARGEFILE); +} + +static int +vdev_file_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize, + uint64_t *logical_ashift, uint64_t *physical_ashift) +{ + vdev_file_t *vf; + zfs_file_t *fp; + zfs_file_attr_t zfa; + int error = 0; + + dprintf("vdev_file_open %p\n", vd->vdev_tsd); + + /* + * Rotational optimizations only make sense on block devices. + */ + vd->vdev_nonrot = B_TRUE; + + /* + * Allow TRIM on file based vdevs. This may not always be supported, + * since it depends on your kernel version and underlying filesystem + * type but it is always safe to attempt. + */ + vd->vdev_has_trim = B_TRUE; + + /* + * Disable secure TRIM on file based vdevs. There is no way to + * request this behavior from the underlying filesystem. + */ + vd->vdev_has_securetrim = B_FALSE; + + /* + * We must have a pathname, and it must be absolute. + */ + if (vd->vdev_path == NULL || vd->vdev_path[0] != '/') { + vd->vdev_stat.vs_aux = VDEV_AUX_BAD_LABEL; + return (SET_ERROR(EINVAL)); + } + + /* + * Reopen the device if it's not currently open. Otherwise, + * just update the physical size of the device. + */ +#ifdef _KERNEL + if (vd->vdev_tsd != NULL) { + ASSERT(vd->vdev_reopening); + vf = vd->vdev_tsd; + goto skip_open; + } +#endif + + vf = vd->vdev_tsd = kmem_zalloc(sizeof (vdev_file_t), KM_SLEEP); + + ASSERT(vd->vdev_path != NULL && vd->vdev_path[0] == '/'); + + error = zfs_file_open(vd->vdev_path, + vdev_file_open_mode(spa_mode(vd->vdev_spa)), 0, &fp); + + if (error) { + vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED; + return (error); + } + + vf->vf_file = fp; + + /* + * Make sure it's a regular file. + */ + if (zfs_file_getattr(fp, &zfa)) { + return (SET_ERROR(ENODEV)); + } + +skip_open: + /* + * Determine the physical size of the file. + */ + error = zfs_file_getattr(vf->vf_file, &zfa); + + if (error) { + vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED; + return (error); + } + + *max_psize = *psize = zfa.zfa_size; + *logical_ashift = vdev_file_logical_ashift; + *physical_ashift = vdev_file_physical_ashift; + + return (0); +} + +static void +vdev_file_close(vdev_t *vd) +{ + vdev_file_t *vf = vd->vdev_tsd; + + if (vd->vdev_reopening || vf == NULL) + return; + + if (vf->vf_file != NULL) { + zfs_file_close(vf->vf_file); + } + + vd->vdev_delayed_close = B_FALSE; + kmem_free(vf, sizeof (vdev_file_t)); + vd->vdev_tsd = NULL; +} + +static void +vdev_file_io_strategy(void *arg) +{ + zio_t *zio = (zio_t *)arg; + vdev_t *vd = zio->io_vd; + vdev_file_t *vf = vd->vdev_tsd; + ssize_t resid; + loff_t off; + void *data; + ssize_t size; + int err; + + off = zio->io_offset; + size = zio->io_size; + resid = 0; + + if (zio->io_type == ZIO_TYPE_READ) { + data = + abd_borrow_buf(zio->io_abd, size); + err = zfs_file_pread(vf->vf_file, data, size, off, &resid); + abd_return_buf_copy(zio->io_abd, data, size); + } else { + data = + abd_borrow_buf_copy(zio->io_abd, size); + err = zfs_file_pwrite(vf->vf_file, data, size, off, &resid); + abd_return_buf(zio->io_abd, data, size); + } + + zio->io_error = (err != 0 ? EIO : 0); + + if (zio->io_error == 0 && resid != 0) + zio->io_error = SET_ERROR(ENOSPC); + + zio_delay_interrupt(zio); +} + +static void +vdev_file_io_start(zio_t *zio) +{ + vdev_t *vd = zio->io_vd; + vdev_file_t *vf = vd->vdev_tsd; + + if (zio->io_type == ZIO_TYPE_IOCTL) { + + if (!vdev_readable(vd)) { + zio->io_error = SET_ERROR(ENXIO); + zio_interrupt(zio); + return; + } + + switch (zio->io_cmd) { + case DKIOCFLUSHWRITECACHE: + zio->io_error = zfs_file_fsync(vf->vf_file, + O_SYNC|O_DSYNC); + break; + default: + zio->io_error = SET_ERROR(ENOTSUP); + } + + zio_execute(zio); + return; + } else if (zio->io_type == ZIO_TYPE_TRIM) { + int mode = 0; + + ASSERT3U(zio->io_size, !=, 0); + + /* XXX FreeBSD has no fallocate routine in file ops */ + zio->io_error = zfs_file_fallocate(vf->vf_file, + mode, zio->io_offset, zio->io_size); + zio_execute(zio); + return; + } + + ASSERT(zio->io_type == ZIO_TYPE_READ || zio->io_type == ZIO_TYPE_WRITE); + zio->io_target_timestamp = zio_handle_io_delay(zio); + + VERIFY3U(taskq_dispatch(vdev_file_taskq, vdev_file_io_strategy, zio, + TQ_SLEEP), !=, 0); +} + + +/* ARGSUSED */ +static void +vdev_file_io_done(zio_t *zio) +{ +} + +vdev_ops_t vdev_file_ops = { + .vdev_op_init = NULL, + .vdev_op_fini = NULL, + .vdev_op_open = vdev_file_open, + .vdev_op_close = vdev_file_close, + .vdev_op_asize = vdev_default_asize, + .vdev_op_min_asize = vdev_default_min_asize, + .vdev_op_min_alloc = NULL, + .vdev_op_io_start = vdev_file_io_start, + .vdev_op_io_done = vdev_file_io_done, + .vdev_op_state_change = NULL, + .vdev_op_need_resilver = NULL, + .vdev_op_hold = vdev_file_hold, + .vdev_op_rele = vdev_file_rele, + .vdev_op_remap = NULL, + .vdev_op_xlate = vdev_default_xlate, + .vdev_op_rebuild_asize = NULL, + .vdev_op_metaslab_init = NULL, + .vdev_op_config_generate = NULL, + .vdev_op_nparity = NULL, + .vdev_op_ndisks = NULL, + .vdev_op_type = VDEV_TYPE_FILE, /* name of this vdev type */ + .vdev_op_leaf = B_TRUE /* leaf vdev */ +}; + +void +vdev_file_init(void) +{ + vdev_file_taskq = taskq_create("vdev_file_taskq", 100, minclsyspri, + max_ncpus, INT_MAX, TASKQ_PREPOPULATE | TASKQ_THREADS_CPU_PCT); + + VERIFY(vdev_file_taskq); +} + +void +vdev_file_fini(void) +{ + taskq_destroy(vdev_file_taskq); +} + +/* + * From userland we access disks just like files. + */ +#ifndef _KERNEL + +vdev_ops_t vdev_disk_ops = { + .vdev_op_init = NULL, + .vdev_op_fini = NULL, + .vdev_op_open = vdev_file_open, + .vdev_op_close = vdev_file_close, + .vdev_op_asize = vdev_default_asize, + .vdev_op_min_asize = vdev_default_min_asize, + .vdev_op_min_alloc = NULL, + .vdev_op_io_start = vdev_file_io_start, + .vdev_op_io_done = vdev_file_io_done, + .vdev_op_state_change = NULL, + .vdev_op_need_resilver = NULL, + .vdev_op_hold = vdev_file_hold, + .vdev_op_rele = vdev_file_rele, + .vdev_op_remap = NULL, + .vdev_op_xlate = vdev_default_xlate, + .vdev_op_rebuild_asize = NULL, + .vdev_op_metaslab_init = NULL, + .vdev_op_config_generate = NULL, + .vdev_op_nparity = NULL, + .vdev_op_ndisks = NULL, + .vdev_op_type = VDEV_TYPE_DISK, /* name of this vdev type */ + .vdev_op_leaf = B_TRUE /* leaf vdev */ +}; + +#endif diff --git a/module/os/macos/zfs/zfs_acl.c b/module/os/macos/zfs/zfs_acl.c new file mode 100644 index 000000000000..d43787750c4a --- /dev/null +++ b/module/os/macos/zfs/zfs_acl.c @@ -0,0 +1,2826 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum vtype vtype_t; + +#define ALLOW ACE_ACCESS_ALLOWED_ACE_TYPE +#define DENY ACE_ACCESS_DENIED_ACE_TYPE +#define MAX_ACE_TYPE ACE_SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE +#define MIN_ACE_TYPE ALLOW + +#define OWNING_GROUP (ACE_GROUP|ACE_IDENTIFIER_GROUP) +#define EVERYONE_ALLOW_MASK (ACE_READ_ACL|ACE_READ_ATTRIBUTES | \ + ACE_READ_NAMED_ATTRS|ACE_SYNCHRONIZE) +#define EVERYONE_DENY_MASK (ACE_WRITE_ACL|ACE_WRITE_OWNER | \ + ACE_WRITE_ATTRIBUTES|ACE_WRITE_NAMED_ATTRS) +#define OWNER_ALLOW_MASK (ACE_WRITE_ACL | ACE_WRITE_OWNER | \ + ACE_WRITE_ATTRIBUTES|ACE_WRITE_NAMED_ATTRS) + +#define ZFS_CHECKED_MASKS (ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_READ_DATA| \ + ACE_READ_NAMED_ATTRS|ACE_WRITE_DATA|ACE_WRITE_ATTRIBUTES| \ + ACE_WRITE_NAMED_ATTRS|ACE_APPEND_DATA|ACE_EXECUTE|ACE_WRITE_OWNER| \ + ACE_WRITE_ACL|ACE_DELETE|ACE_DELETE_CHILD|ACE_SYNCHRONIZE) + +#define WRITE_MASK_DATA (ACE_WRITE_DATA|ACE_APPEND_DATA|ACE_WRITE_NAMED_ATTRS) +#define WRITE_MASK_ATTRS (ACE_WRITE_ACL|ACE_WRITE_OWNER|ACE_WRITE_ATTRIBUTES| \ + ACE_DELETE|ACE_DELETE_CHILD) +#define WRITE_MASK (WRITE_MASK_DATA|WRITE_MASK_ATTRS) + +#define OGE_CLEAR (ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \ + ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_EXECUTE) + +#define OKAY_MASK_BITS (ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \ + ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_EXECUTE) + +#define ALL_INHERIT (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE | \ + ACE_NO_PROPAGATE_INHERIT_ACE|ACE_INHERIT_ONLY_ACE|ACE_INHERITED_ACE) + +#define RESTRICTED_CLEAR (ACE_WRITE_ACL|ACE_WRITE_OWNER) + +#define V4_ACL_WIDE_FLAGS (ZFS_ACL_AUTO_INHERIT|ZFS_ACL_DEFAULTED|\ + ZFS_ACL_PROTECTED) + +#define ZFS_ACL_WIDE_FLAGS (V4_ACL_WIDE_FLAGS|ZFS_ACL_TRIVIAL|ZFS_INHERIT_ACE|\ + ZFS_ACL_OBJ_ACE) + +#define ALL_MODE_EXECS (S_IXUSR | S_IXGRP | S_IXOTH) + +static uint16_t +zfs_ace_v0_get_type(void *acep) +{ + return (((zfs_oldace_t *)acep)->z_type); +} + +static uint16_t +zfs_ace_v0_get_flags(void *acep) +{ + return (((zfs_oldace_t *)acep)->z_flags); +} + +static uint32_t +zfs_ace_v0_get_mask(void *acep) +{ + return (((zfs_oldace_t *)acep)->z_access_mask); +} + +static uint64_t +zfs_ace_v0_get_who(void *acep) +{ + return (((zfs_oldace_t *)acep)->z_fuid); +} + +static void +zfs_ace_v0_set_type(void *acep, uint16_t type) +{ + ((zfs_oldace_t *)acep)->z_type = type; +} + +static void +zfs_ace_v0_set_flags(void *acep, uint16_t flags) +{ + ((zfs_oldace_t *)acep)->z_flags = flags; +} + +static void +zfs_ace_v0_set_mask(void *acep, uint32_t mask) +{ + ((zfs_oldace_t *)acep)->z_access_mask = mask; +} + +static void +zfs_ace_v0_set_who(void *acep, uint64_t who) +{ + ((zfs_oldace_t *)acep)->z_fuid = who; +} + +/*ARGSUSED*/ +static size_t +zfs_ace_v0_size(void *acep) +{ + return (sizeof (zfs_oldace_t)); +} + +static size_t +zfs_ace_v0_abstract_size(void) +{ + return (sizeof (zfs_oldace_t)); +} + +static int +zfs_ace_v0_mask_off(void) +{ + return (offsetof(zfs_oldace_t, z_access_mask)); +} + +/*ARGSUSED*/ +static int +zfs_ace_v0_data(void *acep, void **datap) +{ + *datap = NULL; + return (0); +} + +static acl_ops_t zfs_acl_v0_ops = { + zfs_ace_v0_get_mask, + zfs_ace_v0_set_mask, + zfs_ace_v0_get_flags, + zfs_ace_v0_set_flags, + zfs_ace_v0_get_type, + zfs_ace_v0_set_type, + zfs_ace_v0_get_who, + zfs_ace_v0_set_who, + zfs_ace_v0_size, + zfs_ace_v0_abstract_size, + zfs_ace_v0_mask_off, + zfs_ace_v0_data +}; + +static uint16_t +zfs_ace_fuid_get_type(void *acep) +{ + return (((zfs_ace_hdr_t *)acep)->z_type); +} + +static uint16_t +zfs_ace_fuid_get_flags(void *acep) +{ + return (((zfs_ace_hdr_t *)acep)->z_flags); +} + +static uint32_t +zfs_ace_fuid_get_mask(void *acep) +{ + return (((zfs_ace_hdr_t *)acep)->z_access_mask); +} + +static uint64_t +zfs_ace_fuid_get_who(void *args) +{ + uint16_t entry_type; + zfs_ace_t *acep = args; + + entry_type = acep->z_hdr.z_flags & ACE_TYPE_FLAGS; + + if (entry_type == ACE_OWNER || entry_type == OWNING_GROUP || + entry_type == ACE_EVERYONE) + return (-1); + return (((zfs_ace_t *)acep)->z_fuid); +} + +static void +zfs_ace_fuid_set_type(void *acep, uint16_t type) +{ + ((zfs_ace_hdr_t *)acep)->z_type = type; +} + +static void +zfs_ace_fuid_set_flags(void *acep, uint16_t flags) +{ + ((zfs_ace_hdr_t *)acep)->z_flags = flags; +} + +static void +zfs_ace_fuid_set_mask(void *acep, uint32_t mask) +{ + ((zfs_ace_hdr_t *)acep)->z_access_mask = mask; +} + +static void +zfs_ace_fuid_set_who(void *arg, uint64_t who) +{ + zfs_ace_t *acep = arg; + + uint16_t entry_type = acep->z_hdr.z_flags & ACE_TYPE_FLAGS; + + if (entry_type == ACE_OWNER || entry_type == OWNING_GROUP || + entry_type == ACE_EVERYONE) + return; + acep->z_fuid = who; +} + +static size_t +zfs_ace_fuid_size(void *acep) +{ + zfs_ace_hdr_t *zacep = acep; + uint16_t entry_type; + + switch (zacep->z_type) { + case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE: + case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE: + case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE: + case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE: + return (sizeof (zfs_object_ace_t)); + case ALLOW: + case DENY: + entry_type = + (((zfs_ace_hdr_t *)acep)->z_flags & ACE_TYPE_FLAGS); + if (entry_type == ACE_OWNER || + entry_type == OWNING_GROUP || + entry_type == ACE_EVERYONE) + return (sizeof (zfs_ace_hdr_t)); + /*FALLTHROUGH*/ + default: + return (sizeof (zfs_ace_t)); + } +} + +static size_t +zfs_ace_fuid_abstract_size(void) +{ + return (sizeof (zfs_ace_hdr_t)); +} + +static int +zfs_ace_fuid_mask_off(void) +{ + return (offsetof(zfs_ace_hdr_t, z_access_mask)); +} + +static int +zfs_ace_fuid_data(void *acep, void **datap) +{ + zfs_ace_t *zacep = acep; + zfs_object_ace_t *zobjp; + + switch (zacep->z_hdr.z_type) { + case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE: + case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE: + case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE: + case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE: + zobjp = acep; + *datap = (caddr_t)zobjp + sizeof (zfs_ace_t); + return (sizeof (zfs_object_ace_t) - sizeof (zfs_ace_t)); + default: + *datap = NULL; + return (0); + } +} + +static acl_ops_t zfs_acl_fuid_ops = { + zfs_ace_fuid_get_mask, + zfs_ace_fuid_set_mask, + zfs_ace_fuid_get_flags, + zfs_ace_fuid_set_flags, + zfs_ace_fuid_get_type, + zfs_ace_fuid_set_type, + zfs_ace_fuid_get_who, + zfs_ace_fuid_set_who, + zfs_ace_fuid_size, + zfs_ace_fuid_abstract_size, + zfs_ace_fuid_mask_off, + zfs_ace_fuid_data +}; + +/* + * The following three functions are provided for compatibility with + * older ZPL version in order to determine if the file use to have + * an external ACL and what version of ACL previously existed on the + * file. Would really be nice to not need this, sigh. + */ +uint64_t +zfs_external_acl(znode_t *zp) +{ + zfs_acl_phys_t acl_phys; + int error; + + if (zp->z_is_sa) + return (0); + + /* + * Need to deal with a potential + * race where zfs_sa_upgrade could cause + * z_isa_sa to change. + * + * If the lookup fails then the state of z_is_sa should have + * changed. + */ + + if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_ZNODE_ACL(zp->z_zfsvfs), + &acl_phys, sizeof (acl_phys))) == 0) + return (acl_phys.z_acl_extern_obj); + else { + /* + * after upgrade the SA_ZPL_ZNODE_ACL should have been + * removed + */ + VERIFY(zp->z_is_sa && error == ENOENT); + return (0); + } +} + +/* + * Determine size of ACL in bytes + * + * This is more complicated than it should be since we have to deal + * with old external ACLs. + */ +static int +zfs_acl_znode_info(znode_t *zp, int *aclsize, int *aclcount, + zfs_acl_phys_t *aclphys) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + uint64_t acl_count; + int size; + int error; + + ASSERT(MUTEX_HELD(&zp->z_acl_lock)); + if (zp->z_is_sa) { + if ((error = sa_size(zp->z_sa_hdl, SA_ZPL_DACL_ACES(zfsvfs), + &size)) != 0) + return (error); + *aclsize = size; + if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_DACL_COUNT(zfsvfs), + &acl_count, sizeof (acl_count))) != 0) + return (error); + *aclcount = acl_count; + } else { + if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_ZNODE_ACL(zfsvfs), + aclphys, sizeof (*aclphys))) != 0) + return (error); + + if (aclphys->z_acl_version == ZFS_ACL_VERSION_INITIAL) { + *aclsize = ZFS_ACL_SIZE(aclphys->z_acl_size); + *aclcount = aclphys->z_acl_size; + } else { + *aclsize = aclphys->z_acl_size; + *aclcount = aclphys->z_acl_count; + } + } + return (0); +} + +int +zfs_znode_acl_version(znode_t *zp) +{ + zfs_acl_phys_t acl_phys; + + if (zp->z_is_sa) + return (ZFS_ACL_VERSION_FUID); + else { + int error; + + /* + * Need to deal with a potential + * race where zfs_sa_upgrade could cause + * z_isa_sa to change. + * + * If the lookup fails then the state of z_is_sa should have + * changed. + */ + if ((error = sa_lookup(zp->z_sa_hdl, + SA_ZPL_ZNODE_ACL(zp->z_zfsvfs), + &acl_phys, sizeof (acl_phys))) == 0) + return (acl_phys.z_acl_version); + else { + /* + * After upgrade SA_ZPL_ZNODE_ACL should have + * been removed. + */ + VERIFY(zp->z_is_sa && error == ENOENT); + return (ZFS_ACL_VERSION_FUID); + } + } +} + +static int +zfs_acl_version(int version) +{ + if (version < ZPL_VERSION_FUID) + return (ZFS_ACL_VERSION_INITIAL); + else + return (ZFS_ACL_VERSION_FUID); +} + +static int +zfs_acl_version_zp(znode_t *zp) +{ + return (zfs_acl_version(zp->z_zfsvfs->z_version)); +} + +zfs_acl_t * +zfs_acl_alloc(int vers) +{ + zfs_acl_t *aclp; + + aclp = kmem_zalloc(sizeof (zfs_acl_t), KM_SLEEP); + list_create(&aclp->z_acl, sizeof (zfs_acl_node_t), + offsetof(zfs_acl_node_t, z_next)); + aclp->z_version = vers; + if (vers == ZFS_ACL_VERSION_FUID) + aclp->z_ops = &zfs_acl_fuid_ops; + else + aclp->z_ops = &zfs_acl_v0_ops; + return (aclp); +} + +zfs_acl_node_t * +zfs_acl_node_alloc(size_t bytes) +{ + zfs_acl_node_t *aclnode; + + aclnode = kmem_zalloc(sizeof (zfs_acl_node_t), KM_SLEEP); + if (bytes) { + aclnode->z_acldata = kmem_alloc(bytes, KM_SLEEP); + aclnode->z_allocdata = aclnode->z_acldata; + aclnode->z_allocsize = bytes; + aclnode->z_size = bytes; + } + + return (aclnode); +} + +static void +zfs_acl_node_free(zfs_acl_node_t *aclnode) +{ + if (aclnode->z_allocsize) + kmem_free(aclnode->z_allocdata, aclnode->z_allocsize); + kmem_free(aclnode, sizeof (zfs_acl_node_t)); +} + +static void +zfs_acl_release_nodes(zfs_acl_t *aclp) +{ + zfs_acl_node_t *aclnode; + + while ((aclnode = list_head(&aclp->z_acl))) { + list_remove(&aclp->z_acl, aclnode); + zfs_acl_node_free(aclnode); + } + aclp->z_acl_count = 0; + aclp->z_acl_bytes = 0; +} + +void +zfs_acl_free(zfs_acl_t *aclp) +{ + zfs_acl_release_nodes(aclp); + list_destroy(&aclp->z_acl); + kmem_free(aclp, sizeof (zfs_acl_t)); +} + +static boolean_t +zfs_acl_valid_ace_type(uint_t type, uint_t flags) +{ + uint16_t entry_type; + + switch (type) { + case ALLOW: + case DENY: + case ACE_SYSTEM_AUDIT_ACE_TYPE: + case ACE_SYSTEM_ALARM_ACE_TYPE: + entry_type = flags & ACE_TYPE_FLAGS; + return (entry_type == ACE_OWNER || + entry_type == OWNING_GROUP || + entry_type == ACE_EVERYONE || entry_type == 0 || + entry_type == ACE_IDENTIFIER_GROUP); + default: + if (type >= MIN_ACE_TYPE && type <= MAX_ACE_TYPE) + return (B_TRUE); + } + return (B_FALSE); +} + +static boolean_t +zfs_ace_valid(vtype_t obj_type, zfs_acl_t *aclp, uint16_t type, uint16_t iflags) +{ + /* + * first check type of entry + */ + + if (!zfs_acl_valid_ace_type(type, iflags)) + return (B_FALSE); + + switch (type) { + case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE: + case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE: + case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE: + case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE: + if (aclp->z_version < ZFS_ACL_VERSION_FUID) + return (B_FALSE); + aclp->z_hints |= ZFS_ACL_OBJ_ACE; + } + + /* + * next check inheritance level flags + */ + + if (obj_type == VDIR && + (iflags & (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE))) + aclp->z_hints |= ZFS_INHERIT_ACE; + + if (iflags & (ACE_INHERIT_ONLY_ACE|ACE_NO_PROPAGATE_INHERIT_ACE)) { + if ((iflags & (ACE_FILE_INHERIT_ACE| + ACE_DIRECTORY_INHERIT_ACE)) == 0) { + return (B_FALSE); + } + } + + return (B_TRUE); +} + +static void * +zfs_acl_next_ace(zfs_acl_t *aclp, void *start, uint64_t *who, + uint32_t *access_mask, uint16_t *iflags, uint16_t *type) +{ + zfs_acl_node_t *aclnode; + + ASSERT(aclp); + + if (start == NULL) { + aclnode = list_head(&aclp->z_acl); + if (aclnode == NULL) + return (NULL); + + aclp->z_next_ace = aclnode->z_acldata; + aclp->z_curr_node = aclnode; + aclnode->z_ace_idx = 0; + } + + aclnode = aclp->z_curr_node; + + if (aclnode == NULL) + return (NULL); + + if (aclnode->z_ace_idx >= aclnode->z_ace_count) { + aclnode = list_next(&aclp->z_acl, aclnode); + if (aclnode == NULL) + return (NULL); + else { + aclp->z_curr_node = aclnode; + aclnode->z_ace_idx = 0; + aclp->z_next_ace = aclnode->z_acldata; + } + } + + if (aclnode->z_ace_idx < aclnode->z_ace_count) { + void *acep = aclp->z_next_ace; + size_t ace_size; + + /* + * Make sure we don't overstep our bounds + */ + ace_size = aclp->z_ops->ace_size(acep); + + if (((caddr_t)acep + ace_size) > + ((caddr_t)aclnode->z_acldata + aclnode->z_size)) { + return (NULL); + } + + *iflags = aclp->z_ops->ace_flags_get(acep); + *type = aclp->z_ops->ace_type_get(acep); + *access_mask = aclp->z_ops->ace_mask_get(acep); + *who = aclp->z_ops->ace_who_get(acep); + aclp->z_next_ace = (caddr_t)aclp->z_next_ace + ace_size; + aclnode->z_ace_idx++; + + return ((void *)acep); + } + return (NULL); +} + +/*ARGSUSED*/ +static uint64_t +zfs_ace_walk(void *datap, uint64_t cookie, int aclcnt, + uint16_t *flags, uint16_t *type, uint32_t *mask) +{ + zfs_acl_t *aclp = datap; + zfs_ace_hdr_t *acep = (zfs_ace_hdr_t *)(uintptr_t)cookie; + uint64_t who; + + acep = zfs_acl_next_ace(aclp, acep, &who, mask, + flags, type); + return ((uint64_t)(uintptr_t)acep); +} + +/* + * Copy ACE to internal ZFS format. + * While processing the ACL each ACE will be validated for correctness. + * ACE FUIDs will be created later. + */ +static int +zfs_copy_ace_2_fuid(zfsvfs_t *zfsvfs, vtype_t obj_type, zfs_acl_t *aclp, + void *datap, zfs_ace_t *z_acl, uint64_t aclcnt, size_t *size, + zfs_fuid_info_t **fuidp, cred_t *cr) +{ + int i; + uint16_t entry_type; + zfs_ace_t *aceptr = z_acl; + ace_t *acep = datap; + zfs_object_ace_t *zobjacep; + ace_object_t *aceobjp; + + for (i = 0; i != aclcnt; i++) { + aceptr->z_hdr.z_access_mask = acep->a_access_mask; + aceptr->z_hdr.z_flags = acep->a_flags; + aceptr->z_hdr.z_type = acep->a_type; + entry_type = aceptr->z_hdr.z_flags & ACE_TYPE_FLAGS; + if (entry_type != ACE_OWNER && entry_type != OWNING_GROUP && + entry_type != ACE_EVERYONE) { + aceptr->z_fuid = zfs_fuid_create(zfsvfs, acep->a_who, + cr, (entry_type == 0) ? + ZFS_ACE_USER : ZFS_ACE_GROUP, fuidp); + } + + /* + * Make sure ACE is valid + */ + if (zfs_ace_valid(obj_type, aclp, aceptr->z_hdr.z_type, + aceptr->z_hdr.z_flags) != B_TRUE) + return (SET_ERROR(EINVAL)); + + switch (acep->a_type) { + case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE: + case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE: + case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE: + case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE: + zobjacep = (zfs_object_ace_t *)aceptr; + aceobjp = (ace_object_t *)acep; + + bcopy(aceobjp->a_obj_type, zobjacep->z_object_type, + sizeof (aceobjp->a_obj_type)); + bcopy(aceobjp->a_inherit_obj_type, + zobjacep->z_inherit_type, + sizeof (aceobjp->a_inherit_obj_type)); + acep = (ace_t *)((caddr_t)acep + sizeof (ace_object_t)); + break; + default: + acep = (ace_t *)((caddr_t)acep + sizeof (ace_t)); + } + + aceptr = (zfs_ace_t *)((caddr_t)aceptr + + aclp->z_ops->ace_size(aceptr)); + } + + *size = (caddr_t)aceptr - (caddr_t)z_acl; + + return (0); +} + +/* + * Copy ZFS ACEs to fixed size ace_t layout + */ +static void +zfs_copy_fuid_2_ace(zfsvfs_t *zfsvfs, zfs_acl_t *aclp, cred_t *cr, + void *datap, int filter) +{ + uint64_t who; + uint32_t access_mask; + uint16_t iflags, type; + zfs_ace_hdr_t *zacep = NULL; + ace_t *acep = datap; + ace_object_t *objacep; + zfs_object_ace_t *zobjacep; + size_t ace_size; + uint16_t entry_type; + + while ((zacep = zfs_acl_next_ace(aclp, zacep, + &who, &access_mask, &iflags, &type))) { + + switch (type) { + case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE: + case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE: + case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE: + case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE: + if (filter) { + continue; + } + zobjacep = (zfs_object_ace_t *)zacep; + objacep = (ace_object_t *)acep; + bcopy(zobjacep->z_object_type, + objacep->a_obj_type, + sizeof (zobjacep->z_object_type)); + bcopy(zobjacep->z_inherit_type, + objacep->a_inherit_obj_type, + sizeof (zobjacep->z_inherit_type)); + ace_size = sizeof (ace_object_t); + break; + default: + ace_size = sizeof (ace_t); + break; + } + + entry_type = (iflags & ACE_TYPE_FLAGS); + if ((entry_type != ACE_OWNER && + entry_type != OWNING_GROUP && + entry_type != ACE_EVERYONE)) { + acep->a_who = zfs_fuid_map_id(zfsvfs, who, + cr, (entry_type & ACE_IDENTIFIER_GROUP) ? + ZFS_ACE_GROUP : ZFS_ACE_USER); + } else { + acep->a_who = (uid_t)(int64_t)who; + } + acep->a_access_mask = access_mask; + acep->a_flags = iflags; + acep->a_type = type; + acep = (ace_t *)((caddr_t)acep + ace_size); + } +} + +static int +zfs_copy_ace_2_oldace(vtype_t obj_type, zfs_acl_t *aclp, ace_t *acep, + zfs_oldace_t *z_acl, int aclcnt, size_t *size) +{ + int i; + zfs_oldace_t *aceptr = z_acl; + + for (i = 0; i != aclcnt; i++, aceptr++) { + aceptr->z_access_mask = acep[i].a_access_mask; + aceptr->z_type = acep[i].a_type; + aceptr->z_flags = acep[i].a_flags; + aceptr->z_fuid = acep[i].a_who; + /* + * Make sure ACE is valid + */ + if (zfs_ace_valid(obj_type, aclp, aceptr->z_type, + aceptr->z_flags) != B_TRUE) + return (SET_ERROR(EINVAL)); + } + *size = (caddr_t)aceptr - (caddr_t)z_acl; + return (0); +} + +/* + * convert old ACL format to new + */ +void +zfs_acl_xform(znode_t *zp, zfs_acl_t *aclp, cred_t *cr) +{ + zfs_oldace_t *oldaclp; + int i; + uint16_t type, iflags; + uint32_t access_mask; + uint64_t who; + void *cookie = NULL; + zfs_acl_node_t *newaclnode; + + ASSERT(aclp->z_version == ZFS_ACL_VERSION_INITIAL); + /* + * First create the ACE in a contiguous piece of memory + * for zfs_copy_ace_2_fuid(). + * + * We only convert an ACL once, so this won't happen + * everytime. + */ + oldaclp = kmem_alloc(sizeof (zfs_oldace_t) * aclp->z_acl_count, + KM_SLEEP); + i = 0; + while ((cookie = zfs_acl_next_ace(aclp, cookie, &who, + &access_mask, &iflags, &type))) { + oldaclp[i].z_flags = iflags; + oldaclp[i].z_type = type; + oldaclp[i].z_fuid = who; + oldaclp[i++].z_access_mask = access_mask; + } + + newaclnode = zfs_acl_node_alloc(aclp->z_acl_count * + sizeof (zfs_object_ace_t)); + aclp->z_ops = &zfs_acl_fuid_ops; + VERIFY(zfs_copy_ace_2_fuid(zp->z_zfsvfs, vnode_vtype(ZTOV(zp)), aclp, + oldaclp, newaclnode->z_acldata, aclp->z_acl_count, + &newaclnode->z_size, NULL, cr) == 0); + newaclnode->z_ace_count = aclp->z_acl_count; + aclp->z_version = ZFS_ACL_VERSION; + kmem_free(oldaclp, aclp->z_acl_count * sizeof (zfs_oldace_t)); + + /* + * Release all previous ACL nodes + */ + + zfs_acl_release_nodes(aclp); + + list_insert_head(&aclp->z_acl, newaclnode); + + aclp->z_acl_bytes = newaclnode->z_size; + aclp->z_acl_count = newaclnode->z_ace_count; + +} + +/* + * Convert unix access mask to v4 access mask + */ +static uint32_t +zfs_unix_to_v4(uint32_t access_mask) +{ + uint32_t new_mask = 0; + + if (access_mask & S_IXOTH) + new_mask |= ACE_EXECUTE; + if (access_mask & S_IWOTH) + new_mask |= ACE_WRITE_DATA; + if (access_mask & S_IROTH) + new_mask |= ACE_READ_DATA; + return (new_mask); +} + +static void +zfs_set_ace(zfs_acl_t *aclp, void *acep, uint32_t access_mask, + uint16_t access_type, uint64_t fuid, uint16_t entry_type) +{ + uint16_t type = entry_type & ACE_TYPE_FLAGS; + + aclp->z_ops->ace_mask_set(acep, access_mask); + aclp->z_ops->ace_type_set(acep, access_type); + aclp->z_ops->ace_flags_set(acep, entry_type); + if ((type != ACE_OWNER && type != OWNING_GROUP && + type != ACE_EVERYONE)) + aclp->z_ops->ace_who_set(acep, fuid); +} + +/* + * Determine mode of file based on ACL. + */ +uint64_t +zfs_mode_compute(uint64_t fmode, zfs_acl_t *aclp, + uint64_t *pflags, uint64_t fuid, uint64_t fgid) +{ + int entry_type; + mode_t mode; + mode_t seen = 0; + zfs_ace_hdr_t *acep = NULL; + uint64_t who; + uint16_t iflags, type; + uint32_t access_mask; + boolean_t an_exec_denied = B_FALSE; + + mode = (fmode & (S_IFMT | S_ISUID | S_ISGID | S_ISVTX)); + + while ((acep = zfs_acl_next_ace(aclp, acep, &who, + &access_mask, &iflags, &type))) { + + if (!zfs_acl_valid_ace_type(type, iflags)) + continue; + + entry_type = (iflags & ACE_TYPE_FLAGS); + + /* + * Skip over any inherit_only ACEs + */ + if (iflags & ACE_INHERIT_ONLY_ACE) + continue; + + /* + * Apple has unusual expectations to emulate hfs in that the + * mode is not updated: + * -rw-r--r-- 1 root wheel 0 Nov 12 12:39 file.txt + * chmod +a "root allow execute" file.txt + * ZFS: -rwxr--r--+ 1 root wheel 0 Nov 12 12:39 file.txt + * HFS: -rw-r--r--+ 1 root wheel 0 Nov 12 12:39 file.txt + * 0: user:root allow execute + */ + + if (entry_type == ACE_OWNER || (entry_type == 0 && + who == fuid)) { + if ((access_mask & ACE_READ_DATA) && + (!(seen & S_IRUSR))) { + seen |= S_IRUSR; + if (type == ALLOW) { + mode |= S_IRUSR; + } + } + if ((access_mask & ACE_WRITE_DATA) && + (!(seen & S_IWUSR))) { + seen |= S_IWUSR; + if (type == ALLOW) { + mode |= S_IWUSR; + } + } + if ((access_mask & ACE_EXECUTE) && + (!(seen & S_IXUSR))) { + seen |= S_IXUSR; + if (type == ALLOW) { + mode |= S_IXUSR; + } + } + } else if (entry_type == OWNING_GROUP || + (entry_type == ACE_IDENTIFIER_GROUP && who == fgid)) { + if ((access_mask & ACE_READ_DATA) && + (!(seen & S_IRGRP))) { + seen |= S_IRGRP; + if (type == ALLOW) { + mode |= S_IRGRP; + } + } + if ((access_mask & ACE_WRITE_DATA) && + (!(seen & S_IWGRP))) { + seen |= S_IWGRP; + if (type == ALLOW) { + mode |= S_IWGRP; + } + } + if ((access_mask & ACE_EXECUTE) && + (!(seen & S_IXGRP))) { + seen |= S_IXGRP; + if (type == ALLOW) { + mode |= S_IXGRP; + } + } + } else if (entry_type == ACE_EVERYONE) { + if ((access_mask & ACE_READ_DATA)) { + if (!(seen & S_IRUSR)) { + seen |= S_IRUSR; + if (type == ALLOW) { + mode |= S_IRUSR; + } + } + if (!(seen & S_IRGRP)) { + seen |= S_IRGRP; + if (type == ALLOW) { + mode |= S_IRGRP; + } + } + if (!(seen & S_IROTH)) { + seen |= S_IROTH; + if (type == ALLOW) { + mode |= S_IROTH; + } + } + } + if ((access_mask & ACE_WRITE_DATA)) { + if (!(seen & S_IWUSR)) { + seen |= S_IWUSR; + if (type == ALLOW) { + mode |= S_IWUSR; + } + } + if (!(seen & S_IWGRP)) { + seen |= S_IWGRP; + if (type == ALLOW) { + mode |= S_IWGRP; + } + } + if (!(seen & S_IWOTH)) { + seen |= S_IWOTH; + if (type == ALLOW) { + mode |= S_IWOTH; + } + } + } + if ((access_mask & ACE_EXECUTE)) { + if (!(seen & S_IXUSR)) { + seen |= S_IXUSR; + if (type == ALLOW) { + mode |= S_IXUSR; + } + } + if (!(seen & S_IXGRP)) { + seen |= S_IXGRP; + if (type == ALLOW) { + mode |= S_IXGRP; + } + } + if (!(seen & S_IXOTH)) { + seen |= S_IXOTH; + if (type == ALLOW) { + mode |= S_IXOTH; + } + } + } + } else { + /* + * Only care if this IDENTIFIER_GROUP or + * USER ACE denies execute access to someone, + * mode is not affected + */ + if ((access_mask & ACE_EXECUTE) && type == DENY) + an_exec_denied = B_TRUE; + } + } + + /* + * Failure to allow is effectively a deny, so execute permission + * is denied if it was never mentioned or if we explicitly + * weren't allowed it. + */ + if (!an_exec_denied && + ((seen & ALL_MODE_EXECS) != ALL_MODE_EXECS || + (mode & ALL_MODE_EXECS) != ALL_MODE_EXECS)) + an_exec_denied = B_TRUE; + + if (an_exec_denied) + *pflags &= ~ZFS_NO_EXECS_DENIED; + else + *pflags |= ZFS_NO_EXECS_DENIED; + + return (mode); +} + +/* + * Read an external acl object. If the intent is to modify, always + * create a new acl and leave any cached acl in place. + */ +int +zfs_acl_node_read(znode_t *zp, boolean_t have_lock, zfs_acl_t **aclpp, + boolean_t will_modify) +{ + zfs_acl_t *aclp; + int aclsize; + int acl_count; + zfs_acl_node_t *aclnode; + zfs_acl_phys_t znode_acl; + int version; + int error; + + ASSERT(MUTEX_HELD(&zp->z_acl_lock)); + + if (zp->z_acl_cached && !will_modify) { + *aclpp = zp->z_acl_cached; + return (0); + } + + version = zfs_znode_acl_version(zp); + + if ((error = zfs_acl_znode_info(zp, &aclsize, + &acl_count, &znode_acl)) != 0) { + goto done; + } + + aclp = zfs_acl_alloc(version); + + aclp->z_acl_count = acl_count; + aclp->z_acl_bytes = aclsize; + + aclnode = zfs_acl_node_alloc(aclsize); + aclnode->z_ace_count = aclp->z_acl_count; + aclnode->z_size = aclsize; + + if (!zp->z_is_sa) { + if (znode_acl.z_acl_extern_obj) { + error = dmu_read(zp->z_zfsvfs->z_os, + znode_acl.z_acl_extern_obj, 0, aclnode->z_size, + aclnode->z_acldata, DMU_READ_PREFETCH); + } else { + bcopy(znode_acl.z_ace_data, aclnode->z_acldata, + aclnode->z_size); + } + } else { + error = sa_lookup(zp->z_sa_hdl, SA_ZPL_DACL_ACES(zp->z_zfsvfs), + aclnode->z_acldata, aclnode->z_size); + } + + if (error != 0) { + zfs_acl_free(aclp); + zfs_acl_node_free(aclnode); + /* convert checksum errors into IO errors */ + if (error == ECKSUM) + error = SET_ERROR(EIO); + goto done; + } + + list_insert_head(&aclp->z_acl, aclnode); + + *aclpp = aclp; + if (!will_modify) + zp->z_acl_cached = aclp; +done: + return (error); +} + +/*ARGSUSED*/ +void +zfs_acl_data_locator(void **dataptr, uint32_t *length, uint32_t buflen, + boolean_t start, void *userdata) +{ + zfs_acl_locator_cb_t *cb = (zfs_acl_locator_cb_t *)userdata; + + if (start) { + cb->cb_acl_node = list_head(&cb->cb_aclp->z_acl); + } else { + cb->cb_acl_node = list_next(&cb->cb_aclp->z_acl, + cb->cb_acl_node); + } + *dataptr = cb->cb_acl_node->z_acldata; + *length = cb->cb_acl_node->z_size; +} + +int +zfs_acl_chown_setattr(znode_t *zp) +{ + int error; + zfs_acl_t *aclp; + + ASSERT(MUTEX_HELD(&zp->z_acl_lock)); + + if ((error = zfs_acl_node_read(zp, B_TRUE, &aclp, B_FALSE)) == 0) + zp->z_mode = zfs_mode_compute(zp->z_mode, aclp, + &zp->z_pflags, zp->z_uid, zp->z_gid); + + /* + * Some ZFS implementations (ZEVO) create neither a ZNODE_ACL + * nor a DACL_ACES SA in which case ENOENT is returned from + * zfs_acl_node_read() when the SA can't be located. + * Allow chown/chgrp to succeed in these cases rather than + * returning an error that makes no sense in the context of + * the caller. + */ + if (error == ENOENT) + return (0); + + return (error); +} + +/* + * common code for setting ACLs. + * + * This function is called from zfs_mode_update, zfs_perm_init, and zfs_setacl. + * zfs_setacl passes a non-NULL inherit pointer (ihp) to indicate that it's + * already checked the acl and knows whether to inherit. + */ +int +zfs_aclset_common(znode_t *zp, zfs_acl_t *aclp, cred_t *cr, dmu_tx_t *tx) +{ + int error; + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + dmu_object_type_t otype; + zfs_acl_locator_cb_t locate = { 0 }; + uint64_t mode; + sa_bulk_attr_t bulk[5]; + uint64_t ctime[2]; + int count = 0; + zfs_acl_phys_t acl_phys; + + mode = zp->z_mode; + + mode = zfs_mode_compute(mode, aclp, &zp->z_pflags, + zp->z_uid, zp->z_gid); + + zp->z_mode = mode; + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MODE(zfsvfs), NULL, + &mode, sizeof (mode)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL, + &zp->z_pflags, sizeof (zp->z_pflags)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, + &ctime, sizeof (ctime)); + + if (zp->z_acl_cached) { + zfs_acl_free(zp->z_acl_cached); + zp->z_acl_cached = NULL; + } + + /* + * Upgrade needed? + */ + if (!zfsvfs->z_use_fuids) { + otype = DMU_OT_OLDACL; + } else { + if ((aclp->z_version == ZFS_ACL_VERSION_INITIAL) && + (zfsvfs->z_version >= ZPL_VERSION_FUID)) + zfs_acl_xform(zp, aclp, cr); + ASSERT(aclp->z_version >= ZFS_ACL_VERSION_FUID); + otype = DMU_OT_ACL; + } + + /* + * Arrgh, we have to handle old on disk format + * as well as newer (preferred) SA format. + */ + + if (zp->z_is_sa) { /* the easy case, just update the ACL attribute */ + locate.cb_aclp = aclp; + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_DACL_ACES(zfsvfs), + zfs_acl_data_locator, &locate, aclp->z_acl_bytes); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_DACL_COUNT(zfsvfs), + NULL, &aclp->z_acl_count, sizeof (uint64_t)); + } else { /* Painful legacy way */ + zfs_acl_node_t *aclnode; + uint64_t off = 0; + uint64_t aoid; + + if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_ZNODE_ACL(zfsvfs), + &acl_phys, sizeof (acl_phys))) != 0) + return (error); + + aoid = acl_phys.z_acl_extern_obj; + + if (aclp->z_acl_bytes > ZFS_ACE_SPACE) { + /* + * If ACL was previously external and we are now + * converting to new ACL format then release old + * ACL object and create a new one. + */ + if (aoid && + aclp->z_version != acl_phys.z_acl_version) { + error = dmu_object_free(zfsvfs->z_os, aoid, tx); + if (error) + return (error); + aoid = 0; + } + if (aoid == 0) { + aoid = dmu_object_alloc(zfsvfs->z_os, + otype, aclp->z_acl_bytes, + otype == DMU_OT_ACL ? + DMU_OT_SYSACL : DMU_OT_NONE, + otype == DMU_OT_ACL ? + DN_OLD_MAX_BONUSLEN : 0, tx); + } else { + (void) dmu_object_set_blocksize(zfsvfs->z_os, + aoid, aclp->z_acl_bytes, 0, tx); + } + acl_phys.z_acl_extern_obj = aoid; + for (aclnode = list_head(&aclp->z_acl); aclnode; + aclnode = list_next(&aclp->z_acl, aclnode)) { + if (aclnode->z_ace_count == 0) + continue; + dmu_write(zfsvfs->z_os, aoid, off, + aclnode->z_size, aclnode->z_acldata, tx); + off += aclnode->z_size; + } + } else { + void *start = acl_phys.z_ace_data; + /* + * Migrating back embedded? + */ + if (acl_phys.z_acl_extern_obj) { + error = dmu_object_free(zfsvfs->z_os, + acl_phys.z_acl_extern_obj, tx); + if (error) + return (error); + acl_phys.z_acl_extern_obj = 0; + } + + for (aclnode = list_head(&aclp->z_acl); aclnode; + aclnode = list_next(&aclp->z_acl, aclnode)) { + if (aclnode->z_ace_count == 0) + continue; + bcopy(aclnode->z_acldata, start, + aclnode->z_size); + start = (caddr_t)start + aclnode->z_size; + } + } + /* + * If Old version then swap count/bytes to match old + * layout of znode_acl_phys_t. + */ + if (aclp->z_version == ZFS_ACL_VERSION_INITIAL) { + acl_phys.z_acl_size = aclp->z_acl_count; + acl_phys.z_acl_count = aclp->z_acl_bytes; + } else { + acl_phys.z_acl_size = aclp->z_acl_bytes; + acl_phys.z_acl_count = aclp->z_acl_count; + } + acl_phys.z_acl_version = aclp->z_version; + + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_ZNODE_ACL(zfsvfs), NULL, + &acl_phys, sizeof (acl_phys)); + } + + /* + * Replace ACL wide bits, but first clear them. + */ + zp->z_pflags &= ~ZFS_ACL_WIDE_FLAGS; + + zp->z_pflags |= aclp->z_hints; + + if (ace_trivial_common(aclp, 0, zfs_ace_walk) == 0) + zp->z_pflags |= ZFS_ACL_TRIVIAL; + + zfs_tstamp_update_setup(zp, STATE_CHANGED, NULL, ctime); + return (sa_bulk_update(zp->z_sa_hdl, bulk, count, tx)); +} + +static void +zfs_acl_chmod(vtype_t vtype, uint64_t mode, boolean_t split, boolean_t trim, + zfs_acl_t *aclp) +{ + void *acep = NULL; + uint64_t who; + int new_count, new_bytes; + int ace_size; + int entry_type; + uint16_t iflags, type; + uint32_t access_mask; + zfs_acl_node_t *newnode; + size_t abstract_size = aclp->z_ops->ace_abstract_size(); + void *zacep; + boolean_t isdir; + trivial_acl_t masks; + + new_count = new_bytes = 0; + + isdir = (vtype == VDIR); + + acl_trivial_access_masks((mode_t)mode, isdir, &masks); + + newnode = zfs_acl_node_alloc((abstract_size * 6) + aclp->z_acl_bytes); + + zacep = newnode->z_acldata; + if (masks.allow0) { + zfs_set_ace(aclp, zacep, masks.allow0, ALLOW, -1, ACE_OWNER); + zacep = (void *)((uintptr_t)zacep + abstract_size); + new_count++; + new_bytes += abstract_size; + } + if (masks.deny1) { + zfs_set_ace(aclp, zacep, masks.deny1, DENY, -1, ACE_OWNER); + zacep = (void *)((uintptr_t)zacep + abstract_size); + new_count++; + new_bytes += abstract_size; + } + if (masks.deny2) { + zfs_set_ace(aclp, zacep, masks.deny2, DENY, -1, OWNING_GROUP); + zacep = (void *)((uintptr_t)zacep + abstract_size); + new_count++; + new_bytes += abstract_size; + } + + while ((acep = zfs_acl_next_ace(aclp, acep, &who, &access_mask, + &iflags, &type))) { + entry_type = (iflags & ACE_TYPE_FLAGS); + /* + * ACEs used to represent the file mode may be divided + * into an equivalent pair of inherit-only and regular + * ACEs, if they are inheritable. + * Skip regular ACEs, which are replaced by the new mode. + */ + if (split && (entry_type == ACE_OWNER || + entry_type == OWNING_GROUP || + entry_type == ACE_EVERYONE)) { + if (!isdir || !(iflags & + (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE))) + continue; + /* + * We preserve owner@, group@, or @everyone + * permissions, if they are inheritable, by + * copying them to inherit_only ACEs. This + * prevents inheritable permissions from being + * altered along with the file mode. + */ + iflags |= ACE_INHERIT_ONLY_ACE; + } + + /* + * If this ACL has any inheritable ACEs, mark that in + * the hints (which are later masked into the pflags) + * so create knows to do inheritance. + */ + if (isdir && (iflags & + (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE))) + aclp->z_hints |= ZFS_INHERIT_ACE; + + if ((type != ALLOW && type != DENY) || + (iflags & ACE_INHERIT_ONLY_ACE)) { + switch (type) { + case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE: + case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE: + case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE: + case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE: + aclp->z_hints |= ZFS_ACL_OBJ_ACE; + break; + } + } else { + /* + * Limit permissions granted by ACEs to be no greater + * than permissions of the requested group mode. + * Applies when the "aclmode" property is set to + * "groupmask". + */ + if ((type == ALLOW) && trim) + access_mask &= masks.group; + } + zfs_set_ace(aclp, zacep, access_mask, type, who, iflags); + ace_size = aclp->z_ops->ace_size(acep); + zacep = (void *)((uintptr_t)zacep + ace_size); + new_count++; + new_bytes += ace_size; + } + zfs_set_ace(aclp, zacep, masks.owner, ALLOW, -1, ACE_OWNER); + zacep = (void *)((uintptr_t)zacep + abstract_size); + zfs_set_ace(aclp, zacep, masks.group, ALLOW, -1, OWNING_GROUP); + zacep = (void *)((uintptr_t)zacep + abstract_size); + zfs_set_ace(aclp, zacep, masks.everyone, ALLOW, -1, ACE_EVERYONE); + + new_count += 3; + new_bytes += abstract_size * 3; + zfs_acl_release_nodes(aclp); + aclp->z_acl_count = new_count; + aclp->z_acl_bytes = new_bytes; + newnode->z_ace_count = new_count; + newnode->z_size = new_bytes; + list_insert_tail(&aclp->z_acl, newnode); +} + +int +zfs_acl_chmod_setattr(znode_t *zp, zfs_acl_t **aclp, uint64_t mode) +{ + int error = 0; + + mutex_enter(&zp->z_acl_lock); + if (zp->z_zfsvfs->z_acl_mode == ZFS_ACL_DISCARD) + *aclp = zfs_acl_alloc(zfs_acl_version_zp(zp)); + else + error = zfs_acl_node_read(zp, B_TRUE, aclp, B_TRUE); + + if (error == 0) { + (*aclp)->z_hints = zp->z_pflags & V4_ACL_WIDE_FLAGS; + zfs_acl_chmod(vnode_vtype(ZTOV(zp)), mode, B_TRUE, + (zp->z_zfsvfs->z_acl_mode == ZFS_ACL_GROUPMASK), *aclp); + } + mutex_exit(&zp->z_acl_lock); + + return (error); +} + +/* + * Should ACE be inherited? + */ +static int +zfs_ace_can_use(vtype_t vtype, uint16_t acep_flags) +{ + int iflags = (acep_flags & 0xf); + + if ((vtype == VDIR) && (iflags & ACE_DIRECTORY_INHERIT_ACE)) + return (1); + else if (iflags & ACE_FILE_INHERIT_ACE) + return (!((vtype == VDIR) && + (iflags & ACE_NO_PROPAGATE_INHERIT_ACE))); + return (0); +} + +/* + * inherit inheritable ACEs from parent + */ +static zfs_acl_t * +zfs_acl_inherit(zfsvfs_t *zfsvfs, vtype_t vtype, zfs_acl_t *paclp, + uint64_t mode, boolean_t *need_chmod) +{ + void *pacep = NULL; + void *acep; + zfs_acl_node_t *aclnode; + zfs_acl_t *aclp = NULL; + uint64_t who; + uint32_t access_mask; + uint16_t iflags, newflags, type; + size_t ace_size; + void *data1, *data2; + size_t data1sz, data2sz; + uint_t aclinherit; + boolean_t isdir = (vtype == VDIR); + boolean_t isreg = (vtype == VREG); + + *need_chmod = B_TRUE; + + aclp = zfs_acl_alloc(paclp->z_version); + aclinherit = zfsvfs->z_acl_inherit; + if (aclinherit == ZFS_ACL_DISCARD || vtype == VLNK) + return (aclp); + + while ((pacep = zfs_acl_next_ace(paclp, pacep, &who, + &access_mask, &iflags, &type))) { + + /* + * don't inherit bogus ACEs + */ + if (!zfs_acl_valid_ace_type(type, iflags)) + continue; + + /* + * Check if ACE is inheritable by this vnode + */ + if ((aclinherit == ZFS_ACL_NOALLOW && type == ALLOW) || + !zfs_ace_can_use(vtype, iflags)) + continue; + + /* + * If owner@, group@, or everyone@ inheritable + * then zfs_acl_chmod() isn't needed. + */ + if ((aclinherit == ZFS_ACL_PASSTHROUGH || + aclinherit == ZFS_ACL_PASSTHROUGH_X) && + ((iflags & (ACE_OWNER|ACE_EVERYONE)) || + ((iflags & OWNING_GROUP) == OWNING_GROUP)) && + (isreg || (isdir && (iflags & ACE_DIRECTORY_INHERIT_ACE)))) + *need_chmod = B_FALSE; + + /* + * Strip inherited execute permission from file if + * not in mode + */ + if (aclinherit == ZFS_ACL_PASSTHROUGH_X && type == ALLOW && + !isdir && ((mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0)) { + access_mask &= ~ACE_EXECUTE; + } + + /* + * Strip write_acl and write_owner from permissions + * when inheriting an ACE + */ + if (aclinherit == ZFS_ACL_RESTRICTED && type == ALLOW) { + access_mask &= ~RESTRICTED_CLEAR; + } + + ace_size = aclp->z_ops->ace_size(pacep); + aclnode = zfs_acl_node_alloc(ace_size); + list_insert_tail(&aclp->z_acl, aclnode); + acep = aclnode->z_acldata; + + zfs_set_ace(aclp, acep, access_mask, type, + who, iflags|ACE_INHERITED_ACE); + + /* + * Copy special opaque data if any + */ + if ((data1sz = paclp->z_ops->ace_data(pacep, &data1)) != 0) { + VERIFY((data2sz = aclp->z_ops->ace_data(acep, + &data2)) == data1sz); + bcopy(data1, data2, data2sz); + } + + aclp->z_acl_count++; + aclnode->z_ace_count++; + aclp->z_acl_bytes += aclnode->z_size; + newflags = aclp->z_ops->ace_flags_get(acep); + + /* + * If ACE is not to be inherited further, or if the vnode is + * not a directory, remove all inheritance flags + */ + if (!isdir || (iflags & ACE_NO_PROPAGATE_INHERIT_ACE)) { + newflags &= ~ALL_INHERIT; + aclp->z_ops->ace_flags_set(acep, + newflags|ACE_INHERITED_ACE); + continue; + } + + /* + * This directory has an inheritable ACE + */ + aclp->z_hints |= ZFS_INHERIT_ACE; + + /* + * If only FILE_INHERIT is set then turn on + * inherit_only + */ + if ((iflags & (ACE_FILE_INHERIT_ACE | + ACE_DIRECTORY_INHERIT_ACE)) == ACE_FILE_INHERIT_ACE) { + newflags |= ACE_INHERIT_ONLY_ACE; + aclp->z_ops->ace_flags_set(acep, + newflags|ACE_INHERITED_ACE); + } else { + newflags &= ~ACE_INHERIT_ONLY_ACE; + aclp->z_ops->ace_flags_set(acep, + newflags|ACE_INHERITED_ACE); + } + } + if (zfsvfs->z_acl_mode == ZFS_ACL_RESTRICTED && + aclp->z_acl_count != 0) { + *need_chmod = B_FALSE; + } + + return (aclp); +} + +/* + * Create file system object initial permissions + * including inheritable ACEs. + * Also, create FUIDs for owner and group. + */ +int +zfs_acl_ids_create(znode_t *dzp, int flag, vattr_t *vap, cred_t *cr, + vsecattr_t *vsecp, zfs_acl_ids_t *acl_ids) +{ + int error; + zfsvfs_t *zfsvfs = dzp->z_zfsvfs; + zfs_acl_t *paclp; + gid_t gid; + boolean_t need_chmod = B_TRUE; + boolean_t trim = B_FALSE; + boolean_t inherited = B_FALSE; + + bzero(acl_ids, sizeof (zfs_acl_ids_t)); + acl_ids->z_mode = MAKEIMODE(vap->va_type, vap->va_mode); + + if (vsecp) + if ((error = zfs_vsec_2_aclp(zfsvfs, vap->va_type, vsecp, cr, + &acl_ids->z_fuidp, &acl_ids->z_aclp)) != 0) + return (error); + /* + * Determine uid and gid. + */ + if ((flag & IS_ROOT_NODE) || zfsvfs->z_replay || + ((flag & IS_XATTR) && (vap->va_type == VDIR))) { + acl_ids->z_fuid = zfs_fuid_create(zfsvfs, + (uint64_t)vap->va_uid, cr, + ZFS_OWNER, &acl_ids->z_fuidp); + acl_ids->z_fgid = zfs_fuid_create(zfsvfs, + (uint64_t)vap->va_gid, cr, + ZFS_GROUP, &acl_ids->z_fuidp); + gid = vap->va_gid; + } else { + acl_ids->z_fuid = zfs_fuid_create_cred(zfsvfs, ZFS_OWNER, + cr, &acl_ids->z_fuidp); + acl_ids->z_fgid = 0; + if (vap->va_mask & ATTR_GID) { + acl_ids->z_fgid = zfs_fuid_create(zfsvfs, + (uint64_t)vap->va_gid, + cr, ZFS_GROUP, &acl_ids->z_fuidp); + gid = vap->va_gid; + if (acl_ids->z_fgid != dzp->z_gid && + !groupmember(vap->va_gid, cr) && + secpolicy_vnode_create_gid(cr) != 0) + acl_ids->z_fgid = 0; + } + if (acl_ids->z_fgid == 0) { + char *domain; + uint32_t rid; + + acl_ids->z_fgid = dzp->z_gid; + gid = zfs_fuid_map_id(zfsvfs, acl_ids->z_fgid, + cr, ZFS_GROUP); + + if (zfsvfs->z_use_fuids && + IS_EPHEMERAL(acl_ids->z_fgid)) { + domain = + zfs_fuid_idx_domain(&zfsvfs->z_fuid_idx, + FUID_INDEX(acl_ids->z_fgid)); + rid = FUID_RID(acl_ids->z_fgid); + zfs_fuid_node_add(&acl_ids->z_fuidp, + domain, rid, FUID_INDEX(acl_ids->z_fgid), + acl_ids->z_fgid, ZFS_GROUP); + } + } + } + + /* + * If we're creating a directory, and the parent directory has the + * set-GID bit set, set in on the new directory. + * Otherwise, if the user is neither privileged nor a member of the + * file's new group, clear the file's set-GID bit. + */ + + if (!(flag & IS_ROOT_NODE) && (dzp->z_mode & S_ISGID) && + (vap->va_type == VDIR)) { + acl_ids->z_mode |= S_ISGID; + } else { + if ((acl_ids->z_mode & S_ISGID) && + secpolicy_vnode_setids_setgids(ZTOV(dzp), cr, gid) != 0) + acl_ids->z_mode &= ~S_ISGID; + } + + if (acl_ids->z_aclp == NULL) { + mutex_enter(&dzp->z_acl_lock); + if (!(flag & IS_ROOT_NODE) && + (dzp->z_pflags & ZFS_INHERIT_ACE) && + !(dzp->z_pflags & ZFS_XATTR)) { + VERIFY0(zfs_acl_node_read(dzp, B_TRUE, + &paclp, B_FALSE)); + acl_ids->z_aclp = zfs_acl_inherit(zfsvfs, + vap->va_type, paclp, acl_ids->z_mode, &need_chmod); + inherited = B_TRUE; + } else { + acl_ids->z_aclp = + zfs_acl_alloc(zfs_acl_version_zp(dzp)); + acl_ids->z_aclp->z_hints |= ZFS_ACL_TRIVIAL; + } + mutex_exit(&dzp->z_acl_lock); + + if (need_chmod) { + if (vap->va_type == VDIR) + acl_ids->z_aclp->z_hints |= + ZFS_ACL_AUTO_INHERIT; + + if (zfsvfs->z_acl_mode == ZFS_ACL_GROUPMASK && + zfsvfs->z_acl_inherit != ZFS_ACL_PASSTHROUGH && + zfsvfs->z_acl_inherit != ZFS_ACL_PASSTHROUGH_X) + trim = B_TRUE; + zfs_acl_chmod(vap->va_type, acl_ids->z_mode, B_FALSE, + trim, acl_ids->z_aclp); + } + } + + if (inherited || vsecp) { + acl_ids->z_mode = zfs_mode_compute(acl_ids->z_mode, + acl_ids->z_aclp, &acl_ids->z_aclp->z_hints, + acl_ids->z_fuid, acl_ids->z_fgid); + if (ace_trivial_common(acl_ids->z_aclp, 0, zfs_ace_walk) == 0) + acl_ids->z_aclp->z_hints |= ZFS_ACL_TRIVIAL; + } + + return (0); +} + +/* + * Free ACL and fuid_infop, but not the acl_ids structure + */ +void +zfs_acl_ids_free(zfs_acl_ids_t *acl_ids) +{ + if (acl_ids->z_aclp) + zfs_acl_free(acl_ids->z_aclp); + if (acl_ids->z_fuidp) + zfs_fuid_info_free(acl_ids->z_fuidp); + acl_ids->z_aclp = NULL; + acl_ids->z_fuidp = NULL; +} + +boolean_t +zfs_acl_ids_overquota(zfsvfs_t *zv, zfs_acl_ids_t *acl_ids, uint64_t projid) +{ + return (zfs_id_overquota(zv, DMU_USERUSED_OBJECT, acl_ids->z_fuid) || + zfs_id_overquota(zv, DMU_GROUPUSED_OBJECT, acl_ids->z_fgid) || + (projid != ZFS_DEFAULT_PROJID && projid != ZFS_INVALID_PROJID && + zfs_id_overquota(zv, DMU_PROJECTUSED_OBJECT, projid))); +} + +/* + * Retrieve a file's ACL + */ +int +zfs_getacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr) +{ + struct kauth_acl **aclpp = (struct kauth_acl **)vsecp; + zfs_acl_t *aclp; + kauth_acl_t k_acl; + u_int32_t ace_flags = 0; + kauth_ace_rights_t rights = 0; + guid_t *guidp; + uint64_t who; + uint32_t access_mask; + uint16_t flags; + uint16_t type; + int i; + int error; + void *zacep = NULL; + + mutex_enter(&zp->z_acl_lock); + + error = zfs_acl_node_read(zp, B_FALSE, &aclp, B_TRUE); + if (error != 0) { + mutex_exit(&zp->z_acl_lock); + return (error); + } + if ((k_acl = kauth_acl_alloc(aclp->z_acl_count)) == NULL) { + mutex_exit(&zp->z_acl_lock); + *aclpp = (kauth_acl_t)KAUTH_FILESEC_NONE; + return (ENOMEM); + } + + dprintf("acl_count %d\n", aclp->z_acl_count); + + k_acl->acl_entrycount = aclp->z_acl_count; + k_acl->acl_flags = 0; + *aclpp = k_acl; + + /* + * Translate Open Solaris ACEs to Mac OS X ACLs + */ + i = 0; + while ((zacep = zfs_acl_next_ace(aclp, zacep, + &who, &access_mask, &flags, &type))) { + rights = 0; + ace_flags = 0; + + guidp = &k_acl->acl_ace[i].ace_applicable; + + if (flags & ACE_OWNER) { +#if HIDE_TRIVIAL_ACL + continue; +#endif + who = -1; + nfsacl_set_wellknown(KAUTH_WKG_OWNER, guidp); + } else if ((flags & OWNING_GROUP) == OWNING_GROUP) { +#if HIDE_TRIVIAL_ACL + continue; +#endif + who = -1; + nfsacl_set_wellknown(KAUTH_WKG_GROUP, guidp); + } else if (flags & ACE_EVERYONE) { +#if HIDE_TRIVIAL_ACL + continue; +#endif + who = -1; + nfsacl_set_wellknown(KAUTH_WKG_EVERYBODY, guidp); + /* Try to get a guid from our uid */ + } else { + + dprintf("ZFS: trying to map uid %d flags %x type %x\n", + who, flags, type); + + if (flags & OWNING_GROUP) { + if (kauth_cred_gid2guid(who, guidp) == 0) { + dprintf("ZFS: appears to be a group\n"); + } + } else if (kauth_cred_uid2guid(who, guidp) == 0) { + dprintf("ZFS: appears to be a user\n"); + } else { + dprintf("ZFS: Unable to map\n"); + bzero(guidp, sizeof (guid_t)); + } + } + + // access_mask = aclp->z_acl[i].a_access_mask; + if (access_mask & ACE_READ_DATA) + rights |= KAUTH_VNODE_READ_DATA; + if (access_mask & ACE_WRITE_DATA) + rights |= KAUTH_VNODE_WRITE_DATA; + if (access_mask & ACE_APPEND_DATA) + rights |= KAUTH_VNODE_APPEND_DATA; + if (access_mask & ACE_READ_NAMED_ATTRS) + rights |= KAUTH_VNODE_READ_EXTATTRIBUTES; + if (access_mask & ACE_WRITE_NAMED_ATTRS) + rights |= KAUTH_VNODE_WRITE_EXTATTRIBUTES; + if (access_mask & ACE_EXECUTE) + rights |= KAUTH_VNODE_EXECUTE; + if (access_mask & ACE_DELETE_CHILD) + rights |= KAUTH_VNODE_DELETE_CHILD; + if (access_mask & ACE_READ_ATTRIBUTES) + rights |= KAUTH_VNODE_READ_ATTRIBUTES; + if (access_mask & ACE_WRITE_ATTRIBUTES) + rights |= KAUTH_VNODE_WRITE_ATTRIBUTES; + if (access_mask & ACE_DELETE) + rights |= KAUTH_VNODE_DELETE; + if (access_mask & ACE_READ_ACL) + rights |= KAUTH_VNODE_READ_SECURITY; + if (access_mask & ACE_WRITE_ACL) + rights |= KAUTH_VNODE_WRITE_SECURITY; + if (access_mask & ACE_WRITE_OWNER) + rights |= KAUTH_VNODE_TAKE_OWNERSHIP; + if (access_mask & ACE_SYNCHRONIZE) + rights |= KAUTH_VNODE_SYNCHRONIZE; + k_acl->acl_ace[i].ace_rights = rights; + + // flags = aclp->z_acl[i].a_flags; + if (flags & ACE_FILE_INHERIT_ACE) + ace_flags |= KAUTH_ACE_FILE_INHERIT; + if (flags & ACE_DIRECTORY_INHERIT_ACE) + ace_flags |= KAUTH_ACE_DIRECTORY_INHERIT; + if (flags & ACE_NO_PROPAGATE_INHERIT_ACE) + ace_flags |= KAUTH_ACE_LIMIT_INHERIT; + if (flags & ACE_INHERIT_ONLY_ACE) + ace_flags |= KAUTH_ACE_ONLY_INHERIT; + + // type = aclp->z_acl[i].a_type; + switch (type) { + case ACE_ACCESS_ALLOWED_ACE_TYPE: + ace_flags |= KAUTH_ACE_PERMIT; + break; + case ACE_ACCESS_DENIED_ACE_TYPE: + ace_flags |= KAUTH_ACE_DENY; + break; + case ACE_SYSTEM_AUDIT_ACE_TYPE: + ace_flags |= KAUTH_ACE_AUDIT; + break; + case ACE_SYSTEM_ALARM_ACE_TYPE: + ace_flags |= KAUTH_ACE_ALARM; + break; + } + k_acl->acl_ace[i].ace_flags = ace_flags; + i++; + } + k_acl->acl_entrycount = i; + mutex_exit(&zp->z_acl_lock); + + zfs_acl_free(aclp); + + return (0); +} + +int +zfs_addacl_trivial(znode_t *zp, ace_t *aces, int *nentries, int seen_type) +{ + zfs_acl_t *aclp; + uint64_t who; + uint32_t access_mask; + uint16_t flags; + uint16_t type; + int i; + int error; + void *zacep = NULL; + + mutex_enter(&zp->z_acl_lock); + + error = zfs_acl_node_read(zp, B_FALSE, &aclp, B_TRUE); + if (error != 0) { + mutex_exit(&zp->z_acl_lock); + return (error); + } + + dprintf("ondisk acl_count %d\n", aclp->z_acl_count); + + // Start at the end + i = *nentries; + + /* + * Translate Open Solaris ACEs to Mac OS X ACLs + */ + while ((zacep = zfs_acl_next_ace(aclp, zacep, + &who, &access_mask, &flags, &type))) { + + if (flags & ACE_OWNER) { + if (seen_type & ACE_OWNER) continue; + seen_type |= ACE_OWNER; + who = -1; + } else if ((flags & OWNING_GROUP) == OWNING_GROUP) { + if (seen_type & ACE_GROUP) continue; + seen_type |= ACE_GROUP; + who = -1; + } else if (flags & ACE_EVERYONE) { + if (seen_type & ACE_EVERYONE) continue; + seen_type |= ACE_EVERYONE; + who = -1; + /* Try to get a guid from our uid */ + } else { + // Only deal with the trivials + continue; + } + + aces[i].a_who = who; + aces[i].a_access_mask = access_mask; + aces[i].a_flags = flags; + aces[i].a_type = type; + + dprintf("zfs: adding entry %d for type %x sizeof %d\n", i, type, + sizeof (aces[i])); + i++; + } + + *nentries = i; + mutex_exit(&zp->z_acl_lock); + + zfs_acl_free(aclp); + + return (0); +} + +int +zfs_vsec_2_aclp(zfsvfs_t *zfsvfs, umode_t obj_type, + vsecattr_t *vsecp, cred_t *cr, zfs_fuid_info_t **fuidp, zfs_acl_t **zaclp) +{ + zfs_acl_t *aclp; + zfs_acl_node_t *aclnode; + int aclcnt = vsecp->vsa_aclcnt; + int error; + + if (vsecp->vsa_aclcnt > MAX_ACL_ENTRIES || vsecp->vsa_aclcnt <= 0) + return (SET_ERROR(EINVAL)); + + aclp = zfs_acl_alloc(zfs_acl_version(zfsvfs->z_version)); + + aclp->z_hints = 0; + aclnode = zfs_acl_node_alloc(aclcnt * sizeof (zfs_object_ace_t)); + if (aclp->z_version == ZFS_ACL_VERSION_INITIAL) { + if ((error = zfs_copy_ace_2_oldace(obj_type, aclp, + (ace_t *)vsecp->vsa_aclentp, aclnode->z_acldata, + aclcnt, &aclnode->z_size)) != 0) { + zfs_acl_free(aclp); + zfs_acl_node_free(aclnode); + return (error); + } + } else { + if ((error = zfs_copy_ace_2_fuid(zfsvfs, obj_type, aclp, + vsecp->vsa_aclentp, aclnode->z_acldata, aclcnt, + &aclnode->z_size, fuidp, cr)) != 0) { + zfs_acl_free(aclp); + zfs_acl_node_free(aclnode); + return (error); + } + } + aclp->z_acl_bytes = aclnode->z_size; + aclnode->z_ace_count = aclcnt; + aclp->z_acl_count = aclcnt; + list_insert_head(&aclp->z_acl, aclnode); + + /* + * If flags are being set then add them to z_hints + */ + if (vsecp->vsa_mask & VSA_ACE_ACLFLAGS) { + if (vsecp->vsa_aclflags & ACL_PROTECTED) + aclp->z_hints |= ZFS_ACL_PROTECTED; + if (vsecp->vsa_aclflags & ACL_DEFAULTED) + aclp->z_hints |= ZFS_ACL_DEFAULTED; + if (vsecp->vsa_aclflags & ACL_AUTO_INHERIT) + aclp->z_hints |= ZFS_ACL_AUTO_INHERIT; + } + + *zaclp = aclp; + + return (0); +} + +/* + * Set a file's ACL + */ +int +zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + zilog_t *zilog = zfsvfs->z_log; + ulong_t mask = vsecp->vsa_mask & (VSA_ACE | VSA_ACECNT); + dmu_tx_t *tx; + int error; + zfs_acl_t *aclp; + zfs_fuid_info_t *fuidp = NULL; + boolean_t fuid_dirtied; + uint64_t acl_obj; + + if (mask == 0) + return (SET_ERROR(ENOSYS)); + + if (zp->z_pflags & ZFS_IMMUTABLE) + return (SET_ERROR(EPERM)); + + if ((error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr))) + return (error); + + error = zfs_vsec_2_aclp(zfsvfs, vnode_vtype(ZTOV(zp)), vsecp, cr, + &fuidp, &aclp); + if (error) + return (error); + + /* + * If ACL wide flags aren't being set then preserve any + * existing flags. + */ + if (!(vsecp->vsa_mask & VSA_ACE_ACLFLAGS)) { + aclp->z_hints |= + (zp->z_pflags & V4_ACL_WIDE_FLAGS); + } +top: + mutex_enter(&zp->z_acl_lock); + + tx = dmu_tx_create(zfsvfs->z_os); + + dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE); + + fuid_dirtied = zfsvfs->z_fuid_dirty; + if (fuid_dirtied) + zfs_fuid_txhold(zfsvfs, tx); + + /* + * If old version and ACL won't fit in bonus and we aren't + * upgrading then take out necessary DMU holds + */ + + if ((acl_obj = zfs_external_acl(zp)) != 0) { + if (zfsvfs->z_version >= ZPL_VERSION_FUID && + zfs_znode_acl_version(zp) <= ZFS_ACL_VERSION_INITIAL) { + dmu_tx_hold_free(tx, acl_obj, 0, + DMU_OBJECT_END); + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, + aclp->z_acl_bytes); + } else { + dmu_tx_hold_write(tx, acl_obj, 0, aclp->z_acl_bytes); + } + } else if (!zp->z_is_sa && aclp->z_acl_bytes > ZFS_ACE_SPACE) { + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, aclp->z_acl_bytes); + } + + zfs_sa_upgrade_txholds(tx, zp); + error = dmu_tx_assign(tx, TXG_NOWAIT); + if (error) { + mutex_exit(&zp->z_acl_lock); + + if (error == ERESTART) { + dmu_tx_wait(tx); + dmu_tx_abort(tx); + goto top; + } + dmu_tx_abort(tx); + zfs_acl_free(aclp); + return (error); + } + + error = zfs_aclset_common(zp, aclp, cr, tx); + ASSERT(error == 0); + ASSERT(zp->z_acl_cached == NULL); + zp->z_acl_cached = aclp; + + if (fuid_dirtied) + zfs_fuid_sync(zfsvfs, tx); + + zfs_log_acl(zilog, tx, zp, vsecp, fuidp); + + if (fuidp) + zfs_fuid_info_free(fuidp); + dmu_tx_commit(tx); + mutex_exit(&zp->z_acl_lock); + + return (error); +} + +/* + * Check accesses of interest (AoI) against attributes of the dataset + * such as read-only. Returns zero if no AoI conflict with dataset + * attributes, otherwise an appropriate errno is returned. + */ +static int +zfs_zaccess_dataset_check(znode_t *zp, uint32_t v4_mode) +{ + if ((v4_mode & WRITE_MASK) && + (vfs_isrdonly(zp->z_zfsvfs->z_vfs)) && + (!IS_DEVVP(ZTOV(zp)) || + (IS_DEVVP(ZTOV(zp)) && (v4_mode & WRITE_MASK_ATTRS)))) { + return (SET_ERROR(EROFS)); + } + + /* + * Intentionally allow ZFS_READONLY through here. + * See zfs_zaccess_common(). + */ + if ((v4_mode & WRITE_MASK_DATA) && + (zp->z_pflags & ZFS_IMMUTABLE)) { + return (SET_ERROR(EPERM)); + } + + /* + * In FreeBSD we allow to modify directory's content is ZFS_NOUNLINK + * (sunlnk) is set. We just don't allow directory removal, which is + * handled in zfs_zaccess_delete(). + */ + if ((v4_mode & ACE_DELETE) && + (zp->z_pflags & ZFS_NOUNLINK)) { + return (EPERM); + } + + if (((v4_mode & (ACE_READ_DATA|ACE_EXECUTE)) && + (zp->z_pflags & ZFS_AV_QUARANTINED))) { + return (SET_ERROR(EACCES)); + } + + return (0); +} + +/* + * The primary usage of this function is to loop through all of the + * ACEs in the znode, determining what accesses of interest (AoI) to + * the caller are allowed or denied. The AoI are expressed as bits in + * the working_mode parameter. As each ACE is processed, bits covered + * by that ACE are removed from the working_mode. This removal + * facilitates two things. The first is that when the working mode is + * empty (= 0), we know we've looked at all the AoI. The second is + * that the ACE interpretation rules don't allow a later ACE to undo + * something granted or denied by an earlier ACE. Removing the + * discovered access or denial enforces this rule. At the end of + * processing the ACEs, all AoI that were found to be denied are + * placed into the working_mode, giving the caller a mask of denied + * accesses. Returns: + * 0 if all AoI granted + * EACCESS if the denied mask is non-zero + * other error if abnormal failure (e.g., IO error) + * + * A secondary usage of the function is to determine if any of the + * AoI are granted. If an ACE grants any access in + * the working_mode, we immediately short circuit out of the function. + * This mode is chosen by setting anyaccess to B_TRUE. The + * working_mode is not a denied access mask upon exit if the function + * is used in this manner. + */ +static int +zfs_zaccess_aces_check(znode_t *zp, uint32_t *working_mode, + boolean_t anyaccess, cred_t *cr) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + zfs_acl_t *aclp; + int error; + uid_t uid = crgetuid(cr); + uint64_t who; + uint16_t type, iflags; + uint16_t entry_type; + uint32_t access_mask; + uint32_t deny_mask = 0; + zfs_ace_hdr_t *acep = NULL; + boolean_t checkit; + uid_t gowner; + uid_t fowner; + + zfs_fuid_map_ids(zp, cr, &fowner, &gowner); + + mutex_enter(&zp->z_acl_lock); + + error = zfs_acl_node_read(zp, B_TRUE, &aclp, B_FALSE); + if (error != 0) { + mutex_exit(&zp->z_acl_lock); + return (error); + } + + ASSERT(zp->z_acl_cached); + + while ((acep = zfs_acl_next_ace(aclp, acep, &who, &access_mask, + &iflags, &type))) { + uint32_t mask_matched; + + if (!zfs_acl_valid_ace_type(type, iflags)) + continue; + + if (vnode_vtype(ZTOV(zp)) == VDIR && + (iflags & ACE_INHERIT_ONLY_ACE)) + continue; + + /* Skip ACE if it does not affect any AoI */ + mask_matched = (access_mask & *working_mode); + if (!mask_matched) + continue; + + entry_type = (iflags & ACE_TYPE_FLAGS); + + checkit = B_FALSE; + + switch (entry_type) { + case ACE_OWNER: + if (uid == fowner) + checkit = B_TRUE; + break; + case OWNING_GROUP: + who = gowner; + /*FALLTHROUGH*/ + case ACE_IDENTIFIER_GROUP: + checkit = zfs_groupmember(zfsvfs, who, cr); + break; + case ACE_EVERYONE: + checkit = B_TRUE; + break; + + /* USER Entry */ + default: + if (entry_type == 0) { + uid_t newid; + + newid = zfs_fuid_map_id(zfsvfs, who, cr, + ZFS_ACE_USER); + if (newid != UID_NOBODY && + uid == newid) + checkit = B_TRUE; + break; + } else { + mutex_exit(&zp->z_acl_lock); + return (SET_ERROR(EIO)); + } + } + + if (checkit) { + if (type == DENY) { + DTRACE_PROBE3(zfs__ace__denies, + znode_t *, zp, + zfs_ace_hdr_t *, acep, + uint32_t, mask_matched); + deny_mask |= mask_matched; + } else { + DTRACE_PROBE3(zfs__ace__allows, + znode_t *, zp, + zfs_ace_hdr_t *, acep, + uint32_t, mask_matched); + if (anyaccess) { + mutex_exit(&zp->z_acl_lock); + return (0); + } + } + *working_mode &= ~mask_matched; + } + + /* Are we done? */ + if (*working_mode == 0) + break; + } + + mutex_exit(&zp->z_acl_lock); + + /* Put the found 'denies' back on the working mode */ + if (deny_mask) { + *working_mode |= deny_mask; + return (SET_ERROR(EACCES)); + } else if (*working_mode) { + return (-1); + } + + return (0); +} + +/* + * Return true if any access whatsoever granted, we don't actually + * care what access is granted. + */ +boolean_t +zfs_has_access(znode_t *zp, cred_t *cr) +{ + uint32_t have = ACE_ALL_PERMS; + + if (zfs_zaccess_aces_check(zp, &have, B_TRUE, cr) != 0) { + uid_t owner; + + owner = zfs_fuid_map_id(zp->z_zfsvfs, zp->z_uid, cr, ZFS_OWNER); + return (secpolicy_vnode_any_access(cr, ZTOV(zp), owner) == 0); + } + return (B_TRUE); +} + +static int +zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode, + boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + int err; + + *working_mode = v4_mode; + *check_privs = B_TRUE; + + /* + * Short circuit empty requests + */ + if (v4_mode == 0 || zfsvfs->z_replay) { + *working_mode = 0; + return (0); + } + + if ((err = zfs_zaccess_dataset_check(zp, v4_mode)) != 0) { + *check_privs = B_FALSE; + return (err); + } + + /* + * The caller requested that the ACL check be skipped. This + * would only happen if the caller checked VOP_ACCESS() with a + * 32 bit ACE mask and already had the appropriate permissions. + */ + if (skipaclchk) { + *working_mode = 0; + return (0); + } + + /* + * Note: ZFS_READONLY represents the "DOS R/O" attribute. + * When that flag is set, we should behave as if write access + * were not granted by anything in the ACL. In particular: + * We _must_ allow writes after opening the file r/w, then + * setting the DOS R/O attribute, and writing some more. + * (Similar to how you can write after fchmod(fd, 0444).) + * + * Therefore ZFS_READONLY is ignored in the dataset check + * above, and checked here as if part of the ACL check. + * Also note: DOS R/O is ignored for directories. + */ + if ((v4_mode & WRITE_MASK_DATA) && + (vnode_vtype(ZTOV(zp)) != VDIR) && + (zp->z_pflags & ZFS_READONLY)) { + return (SET_ERROR(EPERM)); + } + + return (zfs_zaccess_aces_check(zp, working_mode, B_FALSE, cr)); +} + +static int +zfs_zaccess_append(znode_t *zp, uint32_t *working_mode, boolean_t *check_privs, + cred_t *cr) +{ + if (*working_mode != ACE_WRITE_DATA) + return (SET_ERROR(EACCES)); + + return (zfs_zaccess_common(zp, ACE_APPEND_DATA, working_mode, + check_privs, B_FALSE, cr)); +} + +/* + * Check if VEXEC is allowed. + * + * This routine is based on zfs_fastaccesschk_execute which has slowpath + * calling zfs_zaccess. This would be incorrect on FreeBSD (see + * zfs_freebsd_access for the difference). Thus this variant let's the + * caller handle the slowpath (if necessary). + * + * On top of that we perform a lockless check for ZFS_NO_EXECS_DENIED. + * + * Safe access to znode_t is provided by the vnode lock. + */ +int +zfs_fastaccesschk_execute(znode_t *zdp, cred_t *cr) +{ + boolean_t is_attr; + + if (zdp->z_pflags & ZFS_AV_QUARANTINED) + return (1); + + is_attr = ((zdp->z_pflags & ZFS_XATTR) && + (vnode_vtype(ZTOV(zdp)) == VDIR)); + if (is_attr) + return (1); + + if (zdp->z_pflags & ZFS_NO_EXECS_DENIED) + return (0); + + return (1); +} + + +/* + * Determine whether Access should be granted/denied. + * + * The least priv subsystem is always consulted as a basic privilege + * can define any form of access. + */ +int +zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr) +{ + uint32_t working_mode; + int error; + int is_attr; + boolean_t check_privs; + znode_t *xzp = NULL; + znode_t *check_zp = zp; + mode_t needed_bits; + uid_t owner; + + is_attr = ((zp->z_pflags & ZFS_XATTR) && + (vnode_vtype(ZTOV(zp)) == VDIR)); + +#ifdef __APPLE__ + /* + * In APPLE, we don't care about permissions of individual ADS. + * Note that not checking them is not just an optimization - without + * this shortcut, EA operations may bogusly fail with EACCES. + */ + if (zp->z_pflags & ZFS_XATTR) + return (0); +#else + /* + * If attribute then validate against base file + */ + if (is_attr) { + uint64_t parent; + + if ((error = sa_lookup(zp->z_sa_hdl, + SA_ZPL_PARENT(zp->z_zfsvfs), &parent, + sizeof (parent))) != 0) + return (error); + + if ((error = zfs_zget(zp->z_zfsvfs, + parent, &xzp)) != 0) { + return (error); + } + + check_zp = xzp; + + /* + * fixup mode to map to xattr perms + */ + + if (mode & (ACE_WRITE_DATA|ACE_APPEND_DATA)) { + mode &= ~(ACE_WRITE_DATA|ACE_APPEND_DATA); + mode |= ACE_WRITE_NAMED_ATTRS; + } + + if (mode & (ACE_READ_DATA|ACE_EXECUTE)) { + mode &= ~(ACE_READ_DATA|ACE_EXECUTE); + mode |= ACE_READ_NAMED_ATTRS; + } + } +#endif + + owner = zfs_fuid_map_id(zp->z_zfsvfs, zp->z_uid, cr, ZFS_OWNER); + /* + * Map the bits required to the standard vnode flags VREAD|VWRITE|VEXEC + * in needed_bits. Map the bits mapped by working_mode (currently + * missing) in missing_bits. + * Call secpolicy_vnode_access2() with (needed_bits & ~checkmode), + * needed_bits. + */ + needed_bits = 0; + + working_mode = mode; + if ((working_mode & (ACE_READ_ACL|ACE_READ_ATTRIBUTES)) && + owner == crgetuid(cr)) + working_mode &= ~(ACE_READ_ACL|ACE_READ_ATTRIBUTES); + + if (working_mode & (ACE_READ_DATA|ACE_READ_NAMED_ATTRS| + ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_SYNCHRONIZE)) + needed_bits |= VREAD; + if (working_mode & (ACE_WRITE_DATA|ACE_WRITE_NAMED_ATTRS| + ACE_APPEND_DATA|ACE_WRITE_ATTRIBUTES|ACE_SYNCHRONIZE)) + needed_bits |= VWRITE; + if (working_mode & ACE_EXECUTE) + needed_bits |= VEXEC; + + if ((error = zfs_zaccess_common(check_zp, mode, &working_mode, + &check_privs, skipaclchk, cr)) == 0) { + if (is_attr) + VN_RELE(ZTOV(xzp)); + return (secpolicy_vnode_access2(cr, ZTOV(zp), owner, + needed_bits, needed_bits)); + } + + if (error && !check_privs) { + if (is_attr) + VN_RELE(ZTOV(xzp)); + return (error); + } + + if (error && (flags & V_APPEND)) { + error = zfs_zaccess_append(zp, &working_mode, &check_privs, cr); + } + + if (error && check_privs) { + mode_t checkmode = 0; + vnode_t *check_vp = ZTOV(check_zp); + + /* + * First check for implicit owner permission on + * read_acl/read_attributes + */ + + error = 0; + ASSERT(working_mode != 0); + + if ((working_mode & (ACE_READ_ACL|ACE_READ_ATTRIBUTES) && + owner == crgetuid(cr))) + working_mode &= ~(ACE_READ_ACL|ACE_READ_ATTRIBUTES); + + if (working_mode & (ACE_READ_DATA|ACE_READ_NAMED_ATTRS| + ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_SYNCHRONIZE)) + checkmode |= VREAD; + if (working_mode & (ACE_WRITE_DATA|ACE_WRITE_NAMED_ATTRS| + ACE_APPEND_DATA|ACE_WRITE_ATTRIBUTES|ACE_SYNCHRONIZE)) + checkmode |= VWRITE; + if (working_mode & ACE_EXECUTE) + checkmode |= VEXEC; + + error = secpolicy_vnode_access2(cr, check_vp, owner, + needed_bits & ~checkmode, needed_bits); + + if (error == 0 && (working_mode & ACE_WRITE_OWNER)) + error = secpolicy_vnode_chown(check_vp, cr, owner); + if (error == 0 && (working_mode & ACE_WRITE_ACL)) + error = secpolicy_vnode_setdac(check_vp, cr, owner); + + if (error == 0 && (working_mode & + (ACE_DELETE|ACE_DELETE_CHILD))) + error = secpolicy_vnode_remove(check_vp, cr); + + if (error == 0 && (working_mode & ACE_SYNCHRONIZE)) { + error = secpolicy_vnode_chown(check_vp, cr, owner); + } + if (error == 0) { + /* + * See if any bits other than those already checked + * for are still present. If so then return EACCES + */ + if (working_mode & ~(ZFS_CHECKED_MASKS)) { + error = SET_ERROR(EACCES); + } + } + } else if (error == 0) { + error = secpolicy_vnode_access2(cr, ZTOV(zp), owner, + needed_bits, needed_bits); + } + + + if (is_attr) + VN_RELE(ZTOV(xzp)); + + return (error); +} + +/* + * Translate traditional unix VREAD/VWRITE/VEXEC mode into + * NFSv4-style ZFS ACL format and call zfs_zaccess() + */ +int +zfs_zaccess_rwx(znode_t *zp, mode_t mode, int flags, cred_t *cr) +{ + return (zfs_zaccess(zp, zfs_unix_to_v4(mode >> 6), flags, B_FALSE, cr)); +} + +/* + * Access function for secpolicy_vnode_setattr + */ +int +zfs_zaccess_unix(znode_t *zp, mode_t mode, cred_t *cr) +{ + int v4_mode = zfs_unix_to_v4(mode >> 6); + + return (zfs_zaccess(zp, v4_mode, 0, B_FALSE, cr)); +} + +static int +zfs_delete_final_check(znode_t *zp, znode_t *dzp, + mode_t available_perms, cred_t *cr) +{ + int error; + uid_t downer; + + downer = zfs_fuid_map_id(dzp->z_zfsvfs, dzp->z_uid, cr, ZFS_OWNER); + + error = secpolicy_vnode_access2(cr, ZTOV(dzp), + downer, available_perms, VWRITE|VEXEC); + + if (error == 0) + error = zfs_sticky_remove_access(dzp, zp, cr); + + return (error); +} + +uint64_t zfs_write_implies_delete_child = 1; + +/* + * Determine whether Access should be granted/deny, without + * consulting least priv subsystem. + * + * The following chart is the recommended NFSv4 enforcement for + * ability to delete an object. + * + * ------------------------------------------------------- + * | Parent Dir | Target Object Permissions | + * | permissions | | + * ------------------------------------------------------- + * | | ACL Allows | ACL Denies| Delete | + * | | Delete | Delete | unspecified| + * ------------------------------------------------------- + * | ACL Allows | Permit | Permit | Permit | + * | DELETE_CHILD | | + * ------------------------------------------------------- + * | ACL Denies | Permit | Deny | Deny | + * | DELETE_CHILD | | | | + * ------------------------------------------------------- + * | ACL specifies | | | | + * | only allow | Permit | Permit | Permit | + * | write and | | | | + * | execute | | | | + * ------------------------------------------------------- + * | ACL denies | | | | + * | write and | Permit | Deny | Deny | + * | execute | | | | + * ------------------------------------------------------- + * ^ + * | + * No search privilege, can't even look up file? + * + */ +int +zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr) +{ + uint32_t dzp_working_mode = 0; + uint32_t zp_working_mode = 0; + int dzp_error, zp_error; + mode_t available_perms; + boolean_t dzpcheck_privs = B_TRUE; + boolean_t zpcheck_privs = B_TRUE; + + /* + * We want specific DELETE permissions to + * take precedence over WRITE/EXECUTE. We don't + * want an ACL such as this to mess us up. + * user:joe:write_data:deny,user:joe:delete:allow + * + * However, deny permissions may ultimately be overridden + * by secpolicy_vnode_access(). + * + * We will ask for all of the necessary permissions and then + * look at the working modes from the directory and target object + * to determine what was found. + */ + + if (zp->z_pflags & (ZFS_IMMUTABLE | ZFS_NOUNLINK)) + return (SET_ERROR(EPERM)); + + /* + * First row + * If the directory permissions allow the delete, we are done. + */ + if ((dzp_error = zfs_zaccess_common(dzp, ACE_DELETE_CHILD, + &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr)) == 0) + return (0); + + /* + * If target object has delete permission then we are done + */ + if ((zp_error = zfs_zaccess_common(zp, ACE_DELETE, &zp_working_mode, + &zpcheck_privs, B_FALSE, cr)) == 0) + return (0); + + ASSERT(dzp_error && zp_error); + + if (!dzpcheck_privs) + return (dzp_error); + if (!zpcheck_privs) + return (zp_error); + + /* + * Second row + * + * If directory returns EACCES then delete_child was denied + * due to deny delete_child. In this case send the request through + * secpolicy_vnode_remove(). We don't use zfs_delete_final_check() + * since that *could* allow the delete based on write/execute permission + * and we want delete permissions to override write/execute. + */ + + if (dzp_error == EACCES) { + /* XXXPJD: s/dzp/zp/ ? */ + return (secpolicy_vnode_remove(ZTOV(dzp), cr)); + } + /* + * Third Row + * only need to see if we have write/execute on directory. + */ + + dzp_error = zfs_zaccess_common(dzp, ACE_EXECUTE|ACE_WRITE_DATA, + &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr); + + if (dzp_error != 0 && !dzpcheck_privs) + return (dzp_error); + + /* + * Fourth row + */ + + available_perms = (dzp_working_mode & ACE_WRITE_DATA) ? 0 : VWRITE; + available_perms |= (dzp_working_mode & ACE_EXECUTE) ? 0 : VEXEC; + + return (zfs_delete_final_check(zp, dzp, available_perms, cr)); + +} + +int +zfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp, + znode_t *tzp, cred_t *cr) +{ + int add_perm; + int error; + + if (szp->z_pflags & ZFS_AV_QUARANTINED) + return (SET_ERROR(EACCES)); + + add_perm = (vnode_vtype(ZTOV(szp)) == VDIR) ? + ACE_ADD_SUBDIRECTORY : ACE_ADD_FILE; + + /* + * Rename permissions are combination of delete permission + + * add file/subdir permission. + * + * BSD operating systems also require write permission + * on the directory being moved from one parent directory + * to another. + */ + if (vnode_vtype(ZTOV(szp)) == VDIR && ZTOV(sdzp) != ZTOV(tdzp)) { + if ((error = zfs_zaccess(szp, ACE_WRITE_DATA, 0, B_FALSE, cr))) + return (error); + } + + /* + * first make sure we do the delete portion. + * + * If that succeeds then check for add_file/add_subdir permissions + */ + + if ((error = zfs_zaccess_delete(sdzp, szp, cr))) + return (error); + + /* + * If we have a tzp, see if we can delete it? + */ + if (tzp && (error = zfs_zaccess_delete(tdzp, tzp, cr))) + return (error); + + /* + * Now check for add permissions + */ + error = zfs_zaccess(tdzp, add_perm, 0, B_FALSE, cr); + + return (error); +} diff --git a/module/os/macos/zfs/zfs_boot.cpp b/module/os/macos/zfs/zfs_boot.cpp new file mode 100644 index 000000000000..7449c22e9afc --- /dev/null +++ b/module/os/macos/zfs/zfs_boot.cpp @@ -0,0 +1,2979 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2015, Evan Susarret. All rights reserved. + */ +/* + * ZFS boot utils + * + * While loading the kext, check if early boot and zfs-boot + * kernel flag. + * Allocate pool_list (and lock). + * Register matching notification zfs_boot_probe_disk to check + * IOMediaBSDClient devices as they are published (or matched?), + * passing pool_list (automatically calls handler for all + * existing devices). + * Dispatch zfs_boot_import_thread on zfs_boot_taskq. + * + * In notification handler zfs_boot_probe_disk: + * Check provider IOMedia for: + * 1 Leaf node and whole disk. + * 2 Leaf node and type ZFS. + * 3 Leaf node and type FreeBSD-ZFS. + * Check IOMedia meets minimum size or bail. + * Allocate char* buffer. + * Call vdev_disk_read_rootlabel. + * XXX Alternately: + * Alloc and prep IOMemoryDescriptor. + * Open IOMedia device (read-only). + * Try to read vdev label from device. + * Close IOMedia device. + * Release IOMemoryDescriptor (data is in buffer). + * XXX + * If label was read, try to generate a config from label. + * Check pool name matches zfs-boot or bail. + * Check pool status. + * Update this vdev's path and set status. + * Set other vdevs to missing status. + * Check-in config in thread-safe manner: + * Take pool_list lock. + * If config not found, insert new config, or update existing. + * Unlock pool_list. + * If found config is complete, wake import thread. + * + * In vdev_disk_read_rootlabel: + * Use vdev_disk_physio to read label. + * If label was read, try to unpack. + * Return label or failure. + * + * In vdev_disk_physio: + * Open device (read-only) using vnop/VOP. + * Try to read vdev label from device. + * Close device using vnop/VOP. + * + * In zfs_boot_import_thread: + * Loop checking for work and sleeping on lock between loops. + * Take pool_list lock and check for work. + * Attempt to import root pool using spa_import_rootpool. + * If successful, remove notification handler (waits for + * all tasks). + * Empty and deallocate pool_list (and lock). + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +int dsl_dsobj_to_dsname(char *pname, uint64_t obj, char *buf); + +} /* extern "C" */ + +static taskq_t *zfs_boot_taskq; + +#include +#include +#include +#include + +#if defined(DEBUG) || defined(ZFS_DEBUG) +#define DSTATIC +#else +#define DSTATIC static +#endif + +#ifndef verify +#define verify(EX) (void)((EX) || \ + (printf("%s, %s, %d, %s\n", #EX, __FILE__, __LINE__, __func__), 0)) +#endif /* verify */ + +/* Most of this is only built when configured with --enable-boot */ + +/* block size is 512 B, count is 512 M blocks */ +#define ZFS_BOOT_DEV_BSIZE (UInt64)(1<<9) +#define ZFS_BOOT_DEV_BCOUNT (UInt64)(2<<29) +#define ZFS_BOOT_DATASET_NAME_KEY "zfs_dataset_name" +#define ZFS_BOOT_DATASET_UUID_KEY "zfs_dataset_uuid" +#define ZFS_BOOT_DATASET_RDONLY_KEY "zfs_dataset_rdonly" +#define ZFS_MOUNTROOT_RETRIES 50 +#define ZFS_BOOTLOG_DELAY 100 + +/* + * C functions for boot-time vdev discovery + */ + +/* + * Intermediate structures used to gather configuration information. + */ +typedef struct config_entry { + uint64_t ce_txg; + nvlist_t *ce_config; + struct config_entry *ce_next; +} config_entry_t; + +typedef struct vdev_entry { + uint64_t ve_guid; + config_entry_t *ve_configs; + struct vdev_entry *ve_next; +} vdev_entry_t; + +typedef struct pool_entry { + uint64_t pe_guid; + vdev_entry_t *pe_vdevs; + struct pool_entry *pe_next; + uint64_t complete; +} pool_entry_t; + +typedef struct name_entry { + char *ne_name; + uint64_t ne_guid; + uint64_t ne_order; + uint64_t ne_num_labels; + struct name_entry *ne_next; +} name_entry_t; + +typedef struct pool_list { + pool_entry_t *pools; + name_entry_t *names; + uint64_t pool_guid; + char *pool_name; + OSSet *new_disks; + OSSet *disks; + kmutex_t lock; + kcondvar_t cv; + IOService *zfs_hl; + IONotifier *notifier; + _Atomic UInt64 terminating; +} pool_list_t; + +#define ZFS_BOOT_ACTIVE 0x1 +#define ZFS_BOOT_TERMINATING 0x2 +#define ZFS_BOOT_INVALID 0x99 + +#define ZFS_BOOT_PREALLOC_SET 5 + +#if 0 +static ZFSBootDevice *bootdev = 0; +#endif +static pool_list_t *zfs_boot_pool_list = 0; + +DSTATIC char * +zfs_boot_get_devid(const char *path) +{ + /* + * XXX Unavailable interface + * + * If we implement one in spl, it could + * simplify import when device paths + * have changed (e.g. USB pools). + * + * Could use ldi DeviceTree path, or + * IOService path if not in DTPlane. + */ + return (NULL); +} + +/* + * Go through and fix up any path and/or devid information for the given vdev + * configuration. + * + * Copied from libzfs_import.c + */ +DSTATIC int +zfs_boot_fix_paths(nvlist_t *nv, name_entry_t *names) +{ + nvlist_t **child; + uint_t c, children; + uint64_t guid; + name_entry_t *ne, *best; + char *path, *devid; + + if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, + &child, &children) == 0) { + for (c = 0; c < children; c++) + if (zfs_boot_fix_paths(child[c], names) != 0) + return (-1); + return (0); + } + + /* + * This is a leaf (file or disk) vdev. In either case, go through + * the name list and see if we find a matching guid. If so, replace + * the path and see if we can calculate a new devid. + * + * There may be multiple names associated with a particular guid, in + * which case we have overlapping partitions or multiple paths to the + * same disk. In this case we prefer to use the path name which + * matches the ZPOOL_CONFIG_PATH. If no matching entry is found we + * use the lowest order device which corresponds to the first match + * while traversing the ZPOOL_IMPORT_PATH search path. + */ + verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0); + if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0) + path = NULL; + + best = NULL; + for (ne = names; ne != NULL; ne = ne->ne_next) { + if (ne->ne_guid == guid) { + + if (path == NULL) { + best = ne; + break; + } + + if ((strlen(path) == strlen(ne->ne_name)) && + strncmp(path, ne->ne_name, strlen(path)) == 0) { + best = ne; + break; + } + + if (best == NULL) { + best = ne; + continue; + } + + /* Prefer paths with more vdev labels. */ + if (ne->ne_num_labels > best->ne_num_labels) { + best = ne; + continue; + } + + /* Prefer paths earlier in the search order. */ + if (ne->ne_num_labels == best->ne_num_labels && + ne->ne_order < best->ne_order) { + best = ne; + continue; + } + } + } + + if (best == NULL) + return (0); + + if (nvlist_add_string(nv, ZPOOL_CONFIG_PATH, best->ne_name) != 0) + return (-1); + + if ((devid = zfs_boot_get_devid(best->ne_name)) == NULL) { + (void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID); + } else { + if (nvlist_add_string(nv, ZPOOL_CONFIG_DEVID, devid) != 0) { + spa_strfree(devid); + return (-1); + } + spa_strfree(devid); + } + + return (0); +} + +/* + * Add the given configuration to the list of known devices. + * + * Copied from libzfs_import.c + * diffs: kmem_alloc, kmem_free with size + */ +DSTATIC int +zfs_boot_add_config(pool_list_t *pl, const char *path, + int order, int num_labels, nvlist_t *config) +{ + uint64_t pool_guid, vdev_guid, top_guid, txg, state; + pool_entry_t *pe; + vdev_entry_t *ve; + config_entry_t *ce; + name_entry_t *ne; + + dprintf("%s %p [%s] %d %d %p\n", __func__, + pl, path, order, num_labels, config); + + /* + * If this is a hot spare not currently in use or level 2 cache + * device, add it to the list of names to translate, but don't do + * anything else. + */ + if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE, + &state) == 0 && + (state == POOL_STATE_SPARE || state == POOL_STATE_L2CACHE) && + nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, &vdev_guid) == 0) { + if ((ne = (name_entry_t *)kmem_alloc( + sizeof (name_entry_t), KM_SLEEP)) == NULL) { + return (-1); + } + bzero(ne, sizeof (name_entry_t)); + + if ((ne->ne_name = spa_strdup(path)) == NULL) { + kmem_free(ne, sizeof (name_entry_t)); + return (-1); + } + ne->ne_guid = vdev_guid; + ne->ne_order = order; + ne->ne_num_labels = num_labels; + ne->ne_next = pl->names; + pl->names = ne; + return (0); + } + + /* + * If we have a valid config but cannot read any of these fields, then + * it means we have a half-initialized label. In vdev_label_init() + * we write a label with txg == 0 so that we can identify the device + * in case the user refers to the same disk later on. If we fail to + * create the pool, we'll be left with a label in this state + * which should not be considered part of a valid pool. + */ + if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, + &pool_guid) != 0 || + nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, + &vdev_guid) != 0 || + nvlist_lookup_uint64(config, ZPOOL_CONFIG_TOP_GUID, + &top_guid) != 0 || + nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG, + &txg) != 0 || txg == 0) { + nvlist_free(config); + return (0); + } + + /* + * First, see if we know about this pool. If not, then add it to the + * list of known pools. + */ + for (pe = pl->pools; pe != NULL; pe = pe->pe_next) { + if (pe->pe_guid == pool_guid) + break; + } + + if (pe == NULL) { + if ((pe = (pool_entry_t *)kmem_alloc( + sizeof (pool_entry_t), KM_SLEEP)) == NULL) { + nvlist_free(config); + return (-1); + } + bzero(pe, sizeof (pool_entry_t)); + pe->pe_guid = pool_guid; + pe->pe_next = pl->pools; + pl->pools = pe; + } + + /* + * Second, see if we know about this toplevel vdev. Add it if its + * missing. + */ + for (ve = pe->pe_vdevs; ve != NULL; ve = ve->ve_next) { + if (ve->ve_guid == top_guid) + break; + } + + if (ve == NULL) { + if ((ve = (vdev_entry_t *)kmem_alloc( + sizeof (vdev_entry_t), KM_SLEEP)) == NULL) { + nvlist_free(config); + return (-1); + } + bzero(ve, sizeof (vdev_entry_t)); + ve->ve_guid = top_guid; + ve->ve_next = pe->pe_vdevs; + pe->pe_vdevs = ve; + } + + /* + * Third, see if we have a config with a matching transaction group. If + * so, then we do nothing. Otherwise, add it to the list of known + * configs. + */ + for (ce = ve->ve_configs; ce != NULL; ce = ce->ce_next) { + if (ce->ce_txg == txg) + break; + } + + if (ce == NULL) { + if ((ce = (config_entry_t *)kmem_alloc( + sizeof (config_entry_t), KM_SLEEP)) == NULL) { + nvlist_free(config); + return (-1); + } + bzero(ce, sizeof (config_entry_t)); + ce->ce_txg = txg; + ce->ce_config = config; + ce->ce_next = ve->ve_configs; + ve->ve_configs = ce; + } else { + nvlist_free(config); + } + + /* + * At this point we've successfully added our config to the list of + * known configs. The last thing to do is add the vdev guid -> path + * mappings so that we can fix up the configuration as necessary before + * doing the import. + */ + if ((ne = (name_entry_t *)kmem_alloc( + sizeof (name_entry_t), KM_SLEEP)) == NULL) { + return (-1); + } + bzero(ne, sizeof (name_entry_t)); + + if ((ne->ne_name = spa_strdup(path)) == NULL) { + kmem_free(ne, sizeof (name_entry_t)); + return (-1); + } + + ne->ne_guid = vdev_guid; + ne->ne_order = order; + ne->ne_num_labels = num_labels; + ne->ne_next = pl->names; + pl->names = ne; + + return (0); +} + +/* + * libzfs_import used the libzfs handle and a zfs + * command to issue tryimport in-kernel via ioctl. + * This should leave config as-is, and return nvl. + * Since zfs_boot is already in-kernel, duplicate + * config into nvl, and call spa_tryimport on it. + */ +DSTATIC nvlist_t * +zfs_boot_refresh_config(nvlist_t *config) +{ + nvlist_t *nvl = 0; + + /* tryimport does not free config, and returns new nvl or null */ + nvl = spa_tryimport(config); + return (nvl); +} + +/* + * Determine if the vdev id is a hole in the namespace. + */ +DSTATIC boolean_t +zfs_boot_vdev_is_hole(uint64_t *hole_array, uint_t holes, uint_t id) +{ + int c; + + for (c = 0; c < holes; c++) { + /* Top-level is a hole */ + if (hole_array[c] == id) + return (B_TRUE); + } + return (B_FALSE); +} + +/* + * Convert our list of pools into the definitive set of configurations. We + * start by picking the best config for each toplevel vdev. Once that's done, + * we assemble the toplevel vdevs into a full config for the pool. We make a + * pass to fix up any incorrect paths, and then add it to the main list to + * return to the user. + */ +DSTATIC nvlist_t * +zfs_boot_get_configs(pool_list_t *pl, boolean_t active_ok) +{ + pool_entry_t *pe; + vdev_entry_t *ve; + config_entry_t *ce; + nvlist_t *ret = NULL, *config = NULL, *tmp = NULL, *nvtop, *nvroot; + nvlist_t **spares, **l2cache; + uint_t i, nspares, nl2cache; + boolean_t config_seen; + uint64_t best_txg; + char *name, *hostname = NULL; + uint64_t guid; + uint_t children = 0; + nvlist_t **child = NULL; + uint_t holes; + uint64_t *hole_array, max_id; + uint_t c; +#if 0 + boolean_t isactive; +#endif + uint64_t hostid; + nvlist_t *nvl; + boolean_t valid_top_config = B_FALSE; + + if (nvlist_alloc(&ret, 0, 0) != 0) + goto nomem; + + for (pe = pl->pools; pe != NULL; pe = pe->pe_next) { + uint64_t id, max_txg = 0; + + if (nvlist_alloc(&config, NV_UNIQUE_NAME, 0) != 0) + goto nomem; + config_seen = B_FALSE; + + /* + * Iterate over all toplevel vdevs. Grab the pool configuration + * from the first one we find, and then go through the rest and + * add them as necessary to the 'vdevs' member of the config. + */ + for (ve = pe->pe_vdevs; ve != NULL; ve = ve->ve_next) { + + /* + * Determine the best configuration for this vdev by + * selecting the config with the latest transaction + * group. + */ + best_txg = 0; + for (ce = ve->ve_configs; ce != NULL; + ce = ce->ce_next) { + + if (ce->ce_txg > best_txg) { + tmp = ce->ce_config; + best_txg = ce->ce_txg; + } + } + + /* + * We rely on the fact that the max txg for the + * pool will contain the most up-to-date information + * about the valid top-levels in the vdev namespace. + */ + if (best_txg > max_txg) { + (void) nvlist_remove(config, + ZPOOL_CONFIG_VDEV_CHILDREN, + DATA_TYPE_UINT64); + (void) nvlist_remove(config, + ZPOOL_CONFIG_HOLE_ARRAY, + DATA_TYPE_UINT64_ARRAY); + + max_txg = best_txg; + hole_array = NULL; + holes = 0; + max_id = 0; + valid_top_config = B_FALSE; + + if (nvlist_lookup_uint64(tmp, + ZPOOL_CONFIG_VDEV_CHILDREN, &max_id) == 0) { + verify(nvlist_add_uint64(config, + ZPOOL_CONFIG_VDEV_CHILDREN, + max_id) == 0); + valid_top_config = B_TRUE; + } + + if (nvlist_lookup_uint64_array(tmp, + ZPOOL_CONFIG_HOLE_ARRAY, &hole_array, + &holes) == 0) { + verify(nvlist_add_uint64_array(config, + ZPOOL_CONFIG_HOLE_ARRAY, + hole_array, holes) == 0); + } + } + + if (!config_seen) { + /* + * Copy the relevant pieces of data to the pool + * configuration: + * + * version + * pool guid + * name + * pool txg (if available) + * comment (if available) + * pool state + * hostid (if available) + * hostname (if available) + */ + uint64_t state, version, pool_txg; + char *comment = NULL; + + version = fnvlist_lookup_uint64(tmp, + ZPOOL_CONFIG_VERSION); + fnvlist_add_uint64(config, + ZPOOL_CONFIG_VERSION, version); + guid = fnvlist_lookup_uint64(tmp, + ZPOOL_CONFIG_POOL_GUID); + fnvlist_add_uint64(config, + ZPOOL_CONFIG_POOL_GUID, guid); + name = fnvlist_lookup_string(tmp, + ZPOOL_CONFIG_POOL_NAME); + fnvlist_add_string(config, + ZPOOL_CONFIG_POOL_NAME, name); + if (nvlist_lookup_uint64(tmp, + ZPOOL_CONFIG_POOL_TXG, &pool_txg) == 0) + fnvlist_add_uint64(config, + ZPOOL_CONFIG_POOL_TXG, pool_txg); + + if (nvlist_lookup_string(tmp, + ZPOOL_CONFIG_COMMENT, &comment) == 0) + fnvlist_add_string(config, + ZPOOL_CONFIG_COMMENT, comment); + + state = fnvlist_lookup_uint64(tmp, + ZPOOL_CONFIG_POOL_STATE); + fnvlist_add_uint64(config, + ZPOOL_CONFIG_POOL_STATE, state); + + hostid = 0; + if (nvlist_lookup_uint64(tmp, + ZPOOL_CONFIG_HOSTID, &hostid) == 0) { + fnvlist_add_uint64(config, + ZPOOL_CONFIG_HOSTID, hostid); + hostname = fnvlist_lookup_string(tmp, + ZPOOL_CONFIG_HOSTNAME); + fnvlist_add_string(config, + ZPOOL_CONFIG_HOSTNAME, hostname); + } + + config_seen = B_TRUE; + } + + /* + * Add this top-level vdev to the child array. + */ + verify(nvlist_lookup_nvlist(tmp, + ZPOOL_CONFIG_VDEV_TREE, &nvtop) == 0); + verify(nvlist_lookup_uint64(nvtop, ZPOOL_CONFIG_ID, + &id) == 0); + + if (id >= children) { + nvlist_t **newchild; + + newchild = (nvlist_t **)kmem_alloc((id + 1) * + sizeof (nvlist_t *), KM_SLEEP); + if (newchild == NULL) + goto nomem; + + for (c = 0; c < children; c++) + newchild[c] = child[c]; + + kmem_free(child, children * + sizeof (nvlist_t *)); + child = newchild; + children = id + 1; + } + if (nvlist_dup(nvtop, &child[id], 0) != 0) + goto nomem; + + } + + /* + * If we have information about all the top-levels then + * clean up the nvlist which we've constructed. This + * means removing any extraneous devices that are + * beyond the valid range or adding devices to the end + * of our array which appear to be missing. + */ + if (valid_top_config) { + if (max_id < children) { + for (c = max_id; c < children; c++) + nvlist_free(child[c]); + children = max_id; + } else if (max_id > children) { + nvlist_t **newchild; + + newchild = (nvlist_t **)kmem_alloc((max_id) * + sizeof (nvlist_t *), KM_SLEEP); + if (newchild == NULL) + goto nomem; + + for (c = 0; c < children; c++) + newchild[c] = child[c]; + + kmem_free(child, children * + sizeof (nvlist_t *)); + child = newchild; + children = max_id; + } + } + + verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, + &guid) == 0); + + /* + * The vdev namespace may contain holes as a result of + * device removal. We must add them back into the vdev + * tree before we process any missing devices. + */ + if (holes > 0) { + ASSERT(valid_top_config); + + for (c = 0; c < children; c++) { + nvlist_t *holey; + + if (child[c] != NULL || + !zfs_boot_vdev_is_hole(hole_array, holes, + c)) + continue; + + if (nvlist_alloc(&holey, NV_UNIQUE_NAME, + 0) != 0) + goto nomem; + + /* + * Holes in the namespace are treated as + * "hole" top-level vdevs and have a + * special flag set on them. + */ + if (nvlist_add_string(holey, + ZPOOL_CONFIG_TYPE, + VDEV_TYPE_HOLE) != 0 || + nvlist_add_uint64(holey, + ZPOOL_CONFIG_ID, c) != 0 || + nvlist_add_uint64(holey, + ZPOOL_CONFIG_GUID, 0ULL) != 0) { + nvlist_free(holey); + goto nomem; + } + child[c] = holey; + } + } + + /* + * Look for any missing top-level vdevs. If this is the case, + * create a faked up 'missing' vdev as a placeholder. We cannot + * simply compress the child array, because the kernel performs + * certain checks to make sure the vdev IDs match their location + * in the configuration. + */ + for (c = 0; c < children; c++) { + if (child[c] == NULL) { + nvlist_t *missing; + if (nvlist_alloc(&missing, NV_UNIQUE_NAME, + 0) != 0) + goto nomem; + if (nvlist_add_string(missing, + ZPOOL_CONFIG_TYPE, + VDEV_TYPE_MISSING) != 0 || + nvlist_add_uint64(missing, + ZPOOL_CONFIG_ID, c) != 0 || + nvlist_add_uint64(missing, + ZPOOL_CONFIG_GUID, 0ULL) != 0) { + nvlist_free(missing); + goto nomem; + } + child[c] = missing; + } + } + + /* + * Put all of this pool's top-level vdevs into a root vdev. + */ + if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0) + goto nomem; + if (nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, + VDEV_TYPE_ROOT) != 0 || + nvlist_add_uint64(nvroot, ZPOOL_CONFIG_ID, 0ULL) != 0 || + nvlist_add_uint64(nvroot, ZPOOL_CONFIG_GUID, guid) != 0 || + nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, + child, children) != 0) { + nvlist_free(nvroot); + goto nomem; + } + + for (c = 0; c < children; c++) + nvlist_free(child[c]); + kmem_free(child, children * sizeof (nvlist_t *)); + children = 0; + child = NULL; + + /* + * Go through and fix up any paths and/or devids based on our + * known list of vdev GUID -> path mappings. + */ + if (zfs_boot_fix_paths(nvroot, pl->names) != 0) { + nvlist_free(nvroot); + goto nomem; + } + + /* + * Add the root vdev to this pool's configuration. + */ + if (nvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, + nvroot) != 0) { + nvlist_free(nvroot); + goto nomem; + } + nvlist_free(nvroot); + + /* + * zdb uses this path to report on active pools that were + * imported or created using -R. + */ + if (active_ok) + goto add_pool; + +#if 0 +/* + * For root-pool import, no pools are active yet. + * Pool name and guid were looked up from the config and only used here. + * (Later we lookup the pool name for a separate test). + */ + /* + * Determine if this pool is currently active, in which case we + * can't actually import it. + */ + verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, + &name) == 0); + verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, + &guid) == 0); + + if (zfs_boot_pool_active(name, guid, &isactive) != 0) + goto error; + + if (isactive) { + nvlist_free(config); + config = NULL; + continue; + } +#endif + + if ((nvl = zfs_boot_refresh_config(config)) == NULL) { + nvlist_free(config); + config = NULL; + continue; + } + + nvlist_free(config); + config = nvl; + + /* + * Go through and update the paths for spares, now that we have + * them. + */ + verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, + &nvroot) == 0); + if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES, + &spares, &nspares) == 0) { + for (i = 0; i < nspares; i++) { + if (zfs_boot_fix_paths(spares[i], pl->names) != + 0) + goto nomem; + } + } + + /* + * Update the paths for l2cache devices. + */ + if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE, + &l2cache, &nl2cache) == 0) { + for (i = 0; i < nl2cache; i++) { + if (zfs_boot_fix_paths(l2cache[i], pl->names) != + 0) + goto nomem; + } + } + + /* + * Restore the original information read from the actual label. + */ + (void) nvlist_remove(config, ZPOOL_CONFIG_HOSTID, + DATA_TYPE_UINT64); + (void) nvlist_remove(config, ZPOOL_CONFIG_HOSTNAME, + DATA_TYPE_STRING); + if (hostid != 0) { + verify(nvlist_add_uint64(config, ZPOOL_CONFIG_HOSTID, + hostid) == 0); + verify(nvlist_add_string(config, ZPOOL_CONFIG_HOSTNAME, + hostname) == 0); + } + +add_pool: + /* + * Add this pool to the list of configs. + */ + verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, + &name) == 0); + if (nvlist_add_nvlist(ret, name, config) != 0) + goto nomem; + + nvlist_free(config); + config = NULL; + } + + return (ret); + +nomem: +#ifdef DEBUG + printf("zfs_boot_get_configs failed to allocate memory\n"); +#endif + if (config) nvlist_free(config); + if (ret) nvlist_free(ret); + for (c = 0; c < children; c++) + nvlist_free(child[c]); + if (children > 0) { + kmem_free(child, children * sizeof (nvlist_t *)); + } + /* + * libzfs_import simply calls free(child), we need to + * pass kmem_free the size of the array. Array is + * allocated above as (children * sizeof nvlist_t*). + */ + + return (NULL); +} + +/* + * Return the offset of the given label. + */ +DSTATIC uint64_t +zfs_boot_label_offset(uint64_t size, int l) +{ + ASSERT(P2PHASE_TYPED(size, sizeof (vdev_label_t), uint64_t) == 0); + return (l * sizeof (vdev_label_t) + (l < VDEV_LABELS / 2 ? + 0 : size - VDEV_LABELS * sizeof (vdev_label_t))); +} + +/* + * Given an IOMedia, read the label information and return an nvlist + * describing the configuration, if there is one. The number of valid + * labels found will be returned in num_labels when non-NULL. + */ +DSTATIC int +zfs_boot_read_label(IOService *zfs_hl, IOMedia *media, + nvlist_t **config, int *num_labels) +{ + IOMemoryDescriptor *buffer = NULL; + uint64_t mediaSize; + uint64_t nread = 0; + vdev_label_t *label; + nvlist_t *expected_config = NULL; + uint64_t expected_guid = 0, size, labelsize; + int l, count = 0; + IOReturn ret; + + *config = NULL; + + /* Verify IOMedia pointer and device size */ + if (!media || (mediaSize = media->getSize()) == 0) { + dprintf("%s couldn't get media or size\n", __func__); + return (-1); + } + + /* Determine vdev label size and aligned vdev size */ + labelsize = sizeof (vdev_label_t); + size = P2ALIGN_TYPED(mediaSize, labelsize, uint64_t); + + /* Allocate a buffer to read labels into */ + label = (vdev_label_t *)kmem_alloc(labelsize, KM_SLEEP); + if (!label) { + dprintf("%s couldn't allocate label for read\n", __func__); + return (-1); + } + + /* Allocate a memory descriptor with the label pointer */ + buffer = IOMemoryDescriptor::withAddress((void*)label, labelsize, + kIODirectionIn); + + /* Verify buffer was allocated */ + if (!buffer || (buffer->getLength() != labelsize)) { + dprintf("%s couldn't allocate buffer for read\n", __func__); + goto error; + } + + /* Open the device for reads */ + if (false == media->IOMedia::open(zfs_hl, 0, + kIOStorageAccessReader)) { + dprintf("%s media open failed\n", __func__); + goto error; + } + + /* Read all four vdev labels */ + for (l = 0; l < VDEV_LABELS; l++) { + uint64_t state, guid, txg; + + /* Zero the label buffer */ + bzero(label, labelsize); + + /* Prepare the buffer for IO */ + buffer->prepare(kIODirectionIn); + + /* Read a label from the specified offset */ + ret = media->IOMedia::read(zfs_hl, + zfs_boot_label_offset(size, l), + buffer, 0, &nread); + + /* Call the buffer completion */ + buffer->complete(); + + /* Skip failed reads, try next label */ + if (ret != kIOReturnSuccess) { + dprintf("%s media->read failed\n", __func__); + continue; + } + + /* Skip incomplete reads, try next label */ + if (nread < labelsize) { + dprintf("%s nread %llu / %llu\n", + __func__, nread, labelsize); + continue; + } + + /* Skip invalid labels that can't be unpacked */ + if (nvlist_unpack(label->vl_vdev_phys.vp_nvlist, + sizeof (label->vl_vdev_phys.vp_nvlist), config, 0) != 0) + continue; + + /* Verify GUID */ + if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_GUID, + &guid) != 0 || guid == 0) { + dprintf("%s nvlist_lookup guid failed %llu\n", + __func__, guid); + nvlist_free(*config); + continue; + } + + /* Verify vdev state */ + if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_STATE, + &state) != 0 || state > POOL_STATE_L2CACHE) { + dprintf("%s nvlist_lookup state failed %llu\n", + __func__, state); + nvlist_free(*config); + continue; + } + + /* Verify txg number */ + if (state != POOL_STATE_SPARE && state != POOL_STATE_L2CACHE && + (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_TXG, + &txg) != 0 || txg == 0)) { + dprintf("%s nvlist_lookup txg failed %llu\n", + __func__, txg); + nvlist_free(*config); + continue; + } + + /* Increment count for first match, or if guid matches */ + if (expected_guid) { + if (expected_guid == guid) + count++; + + nvlist_free(*config); + } else { + expected_config = *config; + expected_guid = guid; + count++; + } + } + + /* Close IOMedia */ + media->close(zfs_hl); + + /* Copy out the config and number of labels */ + if (num_labels != NULL) + *num_labels = count; + + kmem_free(label, labelsize); + buffer->release(); + *config = expected_config; + + return (0); + + +error: + /* Clean up */ + if (buffer) { + buffer->release(); + buffer = 0; + } + if (label) { + kmem_free(label, labelsize); + label = 0; + } + + return (-1); +} + +DSTATIC bool +zfs_boot_probe_media(void* target, void* refCon, + IOService* newService, __unused IONotifier* notifier) +{ + IOMedia *media = 0; + OSObject *isLeaf = 0; + OSString *ospath = 0; + uint64_t mediaSize = 0; + pool_list_t *pools = (pool_list_t *)refCon; + + /* Verify pool list can be cast */ + if (!pools) { + dprintf("%s invalid refCon\n", __func__); + return (false); + } + /* Should never happen */ + if (!newService) { + printf("%s %s\n", "zfs_boot_probe_media", + "called with null newService"); + return (false); + } + + /* Abort early */ + if (pools->terminating != ZFS_BOOT_ACTIVE) { + dprintf("%s terminating 1\n", __func__); + return (false); + } + + /* Validate pool name */ + if (!pools->pool_name || strlen(pools->pool_name) == 0) { + dprintf("%s no pool name specified\n", __func__); + return (false); + } + + /* Get the parent IOMedia device */ + media = OSDynamicCast(IOMedia, newService->getProvider()); + + if (!media) { + dprintf("%s couldn't be cast as IOMedia\n", + __func__); + return (false); + } + + isLeaf = media->getProperty(kIOMediaLeafKey); + if (!isLeaf) { + dprintf("%s skipping non-leaf\n", __func__); + goto out; + } + + mediaSize = media->getSize(); + if (mediaSize < SPA_MINDEVSIZE) { + dprintf("%s skipping device with size %llu\n", + __func__, mediaSize); + goto out; + } + + ospath = OSDynamicCast(OSString, media->getProperty( + kIOBSDNameKey, gIOServicePlane, + kIORegistryIterateRecursively)); + if (!ospath || (ospath->getLength() == 0)) { + dprintf("%s skipping device with no bsd disk node\n", + __func__); + goto out; + } + + /* Abort early */ + if (pools->terminating != ZFS_BOOT_ACTIVE) { + dprintf("%s terminating 2\n", __func__); + goto out; + } + + /* Take pool_list lock */ + mutex_enter(&pools->lock); + + /* Abort early */ + if (pools->terminating != ZFS_BOOT_ACTIVE) { + dprintf("%s terminating 3\n", __func__); + /* Unlock the pool list lock */ + mutex_exit(&pools->lock); + goto out; + } + + /* Add this IOMedia to the disk set */ + pools->disks->setObject(media); + + /* Unlock the pool list lock */ + mutex_exit(&pools->lock); + + /* Wakeup zfs_boot_import_thread */ + cv_signal(&pools->cv); + +out: + media = 0; + return (true); +} + +DSTATIC bool +zfs_boot_probe_disk(pool_list_t *pools, IOMedia *media) +{ + OSString *ospath, *uuid; + char *path = 0, *pname; + const char prefix[] = "/private/var/run/disk/by-id/media-"; + uint64_t this_guid; + int num_labels, err, len = 0; + nvlist_t *config; + boolean_t matched = B_FALSE; + + dprintf("%s: with %s media\n", __func__, + (media ? "valid" : "missing")); + ASSERT3U(media, !=, NULL); + + /* Verify pool list can be cast */ + if (!pools) { + dprintf("%s missing pool_list\n", __func__); + return (false); + } + + /* Abort early */ + if (pools->terminating != ZFS_BOOT_ACTIVE) { + dprintf("%s terminating 1\n", __func__); + return (false); + } + + /* Validate pool name */ + if (!pools->pool_name || strlen(pools->pool_name) == 0) { + dprintf("%s no pool name specified\n", __func__); + return (false); + } + + /* Try to get a UUID from the media */ + uuid = OSDynamicCast(OSString, media->getProperty(kIOMediaUUIDKey)); + if (uuid && uuid->getLength() != 0) { + /* Allocate room for prefix, UUID, and null terminator */ + len = (strlen(prefix) + uuid->getLength()) + 1; + + path = (char *)kmem_alloc(len, KM_SLEEP); + if (!path) { + dprintf("%s couldn't allocate path\n", __func__); + return (false); + } + + snprintf(path, len, "%s%s", prefix, uuid->getCStringNoCopy()); + uuid = 0; + } else { + /* Get the BSD name as a C string */ + ospath = OSDynamicCast(OSString, media->getProperty( + kIOBSDNameKey, gIOServicePlane, + kIORegistryIterateRecursively)); + if (!ospath || (ospath->getLength() == 0)) { + dprintf("%s skipping device with no bsd disk node\n", + __func__); + return (false); + } + + /* Allocate room for "/dev/" + "diskNsN" + '\0' */ + len = (strlen("/dev/") + ospath->getLength() + 1); + path = (char *)kmem_alloc(len, KM_SLEEP); + if (!path) { + dprintf("%s couldn't allocate path\n", __func__); + return (false); + } + + /* "/dev/" is 5 characters, plus null character */ + snprintf(path, len, "/dev/%s", ospath->getCStringNoCopy()); + ospath = 0; + } + dprintf("%s path [%s]\n", __func__, (path ? path : "")); + + /* Read vdev labels, if any */ + err = zfs_boot_read_label(pools->zfs_hl, media, + &config, &num_labels); + + /* Skip disks with no labels */ + if (err != 0 || num_labels == 0 || !config) { + goto out; + } + + /* Lookup pool name */ + if (pools->pool_name != NULL && + (nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, + &pname) == 0)) { + /* Compare with pool_name */ + if (strlen(pools->pool_name) == strlen(pname) && + strncmp(pools->pool_name, pname, + strlen(pname)) == 0) { + printf("%s matched pool %s\n", + __func__, pname); + matched = B_TRUE; + } + /* Compare with pool_guid */ + } else if (pools->pool_guid != 0) { + matched = nvlist_lookup_uint64(config, + ZPOOL_CONFIG_POOL_GUID, + &this_guid) == 0 && + pools->pool_guid == this_guid; + } + + /* Skip non-matches */ + if (!matched) { + nvlist_free(config); + config = NULL; + goto out; + } + + /* + * Add this config to the pool list. + * Always assigns order 1 since all disks are + * referenced by /private/var/run/disk/by-id/ paths. + */ + dprintf("%s: add_config %s\n", __func__, path); + if (zfs_boot_add_config(pools, path, 1, + num_labels, config) != 0) { + printf("%s couldn't add config to pool list\n", + __func__); + } + +out: + /* Clean up */ + if (path && len > 0) { + kmem_free(path, len); + } + return (true); +} + +DSTATIC void +zfs_boot_free() +{ + pool_entry_t *pe, *penext; + vdev_entry_t *ve, *venext; + config_entry_t *ce, *cenext; + name_entry_t *ne, *nenext; + pool_list_t *pools = zfs_boot_pool_list; + + /* Verify pool list can be cast */ + if (!pools) { + dprintf("%s: no pool_list to clear\n", __func__); + return; + } + + /* Clear global ptr */ + zfs_boot_pool_list = 0; + + pools->terminating = ZFS_BOOT_TERMINATING; + + /* Remove IONotifier (waits for tasks to complete) */ + if (pools->notifier) { + pools->notifier->remove(); + pools->notifier = 0; + } + + /* Release the lock */ + mutex_destroy(&pools->lock); + + /* Release the disk set */ + if (pools->disks) { + pools->disks->flushCollection(); + pools->disks->release(); + pools->disks = 0; + } + + /* Clear the zfs IOService handle */ + if (pools->zfs_hl) { + pools->zfs_hl = 0; + } + + /* Free the pool_name string */ + if (pools->pool_name) { + kmem_free(pools->pool_name, strlen(pools->pool_name) + 1); + pools->pool_name = 0; + } + + /* Clear the pool config list */ + for (pe = pools->pools; pe != NULL; pe = penext) { + /* Clear the vdev list */ + penext = pe->pe_next; + for (ve = pe->pe_vdevs; ve != NULL; ve = venext) { + /* Clear the vdev config list */ + venext = ve->ve_next; + for (ce = ve->ve_configs; ce != NULL; ce = cenext) { + cenext = ce->ce_next; + if (ce->ce_config) + nvlist_free(ce->ce_config); + kmem_free(ce, sizeof (config_entry_t)); + } + kmem_free(ve, sizeof (vdev_entry_t)); + } + kmem_free(pe, sizeof (pool_entry_t)); + } + pools->pools = 0; + + /* Clear the vdev name list */ + for (ne = pools->names; ne != NULL; ne = nenext) { + nenext = ne->ne_next; + if (ne->ne_name) + spa_strfree(ne->ne_name); + kmem_free(ne, sizeof (name_entry_t)); + } + pools->names = 0; + + /* Finally, free the pool list struct */ + kmem_free(pools, sizeof (pool_list_t)); + pools = 0; +} + +void +zfs_boot_fini() +{ + pool_list_t *pools = zfs_boot_pool_list; + + if (!pools) { + printf("%s no pool_list to clear\n", __func__); + return; + } + + /* Set terminating flag */ + if (false == OSCompareAndSwap64(ZFS_BOOT_ACTIVE, + ZFS_BOOT_TERMINATING, &(pools->terminating))) { + printf("%s already terminating? %llu\n", + __func__, pools->terminating); + } + + /* Wakeup zfs_boot_import_thread */ + cv_signal(&pools->cv); + + taskq_wait(zfs_boot_taskq); + taskq_destroy(zfs_boot_taskq); + + /* Clean up */ + pools = 0; +} + +#define kBootUUIDKey "boot-uuid" +#define kBootUUIDMediaKey "boot-uuid-media" + +DSTATIC int +zfs_boot_publish_bootfs(IOService *zfs_hl, pool_list_t *pools) +{ + ZFSDataset *dataset = NULL; + IOMedia *media; + IOService *resourceService = NULL; + OSDictionary *properties = NULL; + spa_t *spa = NULL; + char *zfs_bootfs = NULL; + uint64_t bootfs = 0ULL; + int error, len = ZFS_MAX_DATASET_NAME_LEN; + + dprintf("%s\n", __func__); + if (!zfs_hl || !pools) { + dprintf("%s missing argument\n", __func__); + return (EINVAL); + } + +#if 0 + ZFSPool *pool_proxy = NULL; + if (bootdev) { + dprintf("%s bootdev already set\n", __func__); + return (EBUSY); + } +#endif + + zfs_bootfs = (char *)kmem_alloc(len, KM_SLEEP); + if (!zfs_bootfs) { + printf("%s string alloc failed\n", __func__); + return (ENOMEM); + } + zfs_bootfs[0] = '\0'; + + mutex_enter(&spa_namespace_lock); + spa = spa_next(NULL); + if (spa) { + bootfs = spa_bootfs(spa); + } + if (bootfs == 0) { + mutex_exit(&spa_namespace_lock); + dprintf("%s no bootfs, nothing to do\n", __func__); + kmem_free(zfs_bootfs, len); + return (0); + } + +#if 0 + /* Get pool proxy */ + if (!spa->spa_iokit_proxy || + (pool_proxy = spa->spa_iokit_proxy->proxy) == NULL) { + mutex_exit(&spa_namespace_lock); + dprintf("%s no spa_pool_proxy\n", __func__); + kmem_free(zfs_bootfs, len); + return (0); + } +#endif + + error = dsl_dsobj_to_dsname(spa_name(spa), + spa_bootfs(spa), zfs_bootfs); + mutex_exit(&spa_namespace_lock); + + if (error != 0) { + dprintf("%s bootfs to name failed\n", __func__); + kmem_free(zfs_bootfs, len); + return (ENODEV); + } + + printf("%s: publishing bootfs [%s]\n", __func__, zfs_bootfs); + + /* Create prop dict for the proxy, with 6 or more keys */ + if ((properties = OSDictionary::withCapacity(6)) == NULL) { + dprintf("%s prop dict allocation failed\n", __func__); + kmem_free(zfs_bootfs, len); + return (ENOMEM); + } + + /* Set Content Hint and Content */ + do { + const OSSymbol *partUUID; + + /* ZFS (BF01) partition type */ + if ((partUUID = OSSymbol::withCString( + "6A898CC3-1DD2-11B2-99A6-080020736631")) == NULL) { + dprintf("%s couldn't make partUUID\n", __func__); + break; + // kmem_free(zfs_bootfs, len); + // return (ENOMEM); + } + + /* Assign ZFS partiton UUID to both */ + if (properties->setObject(kIOMediaContentKey, + partUUID) == false || + properties->setObject(kIOMediaContentHintKey, + partUUID) == false) { + dprintf("%s content hint failed\n", __func__); + // kmem_free(zfs_bootfs, len); + // return (ENOMEM); + } + partUUID->release(); + } while (0); + + /* XXX Set dataset name, rdonly, and UUID */ + do { + OSString *nameStr; + OSString *uuidStr; + char uuid_cstr[UUID_PRINTABLE_STRING_LENGTH]; + uuid_t uuid; + + bzero(uuid, sizeof (uuid_t)); + bzero(uuid_cstr, UUID_PRINTABLE_STRING_LENGTH); + + zfs_vfs_uuid_gen(zfs_bootfs, uuid); + zfs_vfs_uuid_unparse(uuid, uuid_cstr); + + nameStr = OSString::withCString(zfs_bootfs); + uuidStr = OSString::withCString(uuid_cstr); + + if (properties->setObject(ZFS_BOOT_DATASET_NAME_KEY, + nameStr) == false || + properties->setObject(ZFS_BOOT_DATASET_UUID_KEY, + uuidStr) == false || + properties->setObject(ZFS_BOOT_DATASET_RDONLY_KEY, + kOSBooleanFalse) == false) { + dprintf("ZFSBootDevice::%s couldn't setup" + "property dict\n", __func__); + nameStr->release(); + uuidStr->release(); + kmem_free(zfs_bootfs, len); + return (ENOMEM); + } + nameStr->release(); + uuidStr->release(); + } while (0); + + /* Create proxy device */ + error = zfs_osx_proxy_create(zfs_bootfs); + if (error == 0) { + dataset = zfs_osx_proxy_get(zfs_bootfs); + } + /* Done with this string */ + kmem_free(zfs_bootfs, len); + zfs_bootfs = 0; + + if (!dataset) { + printf("%s: couldn't create proxy device\n", + __func__); + return (ENXIO); + } + + media = OSDynamicCast(IOMedia, dataset); + if (!media) { + printf("%s: couldn't cast proxy media\n", + __func__); + dataset->release(); + return (ENXIO); + } + +#if 0 + bootdev = new ZFSBootDevice; + + if (!bootdev) { + printf("%s: couldn't create boot device\n", __func__); + return (ENOMEM); + } + + if (bootdev->init(properties) == false) { + printf("%s init failed\n", __func__); + properties->release(); + bootdev->release(); + bootdev = 0; + return (ENXIO); + } + properties->release(); + properties = 0; + + if (bootdev->attach(pool_proxy) == false) { + printf("%s attach failed\n", __func__); + bootdev->release(); + bootdev = 0; + return (ENXIO); + } + + /* Technically should start but this doesn't do much */ + if (bootdev->start(pool_proxy) == false) { + printf("%s start failed\n", __func__); + bootdev->detach(pool_proxy); + bootdev->release(); + bootdev = 0; + return (ENXIO); + } + + /* Get matching started */ + bootdev->registerService(kIOServiceAsynchronous); + // bootdev->registerService(kIOServiceSynchronous); + + do { + if (bootdev->getClient() != 0) { + media = OSDynamicCast(IOMedia, + bootdev->getClient()->getClient()); + if (media) { + media->retain(); + break; + } + } + + /* Sleep until media is available */ + /* + * XXX Should use waitForServiceMatching or IONotifier + */ + IOSleep(200); + } while (!media); + + if (!media) { + /* XXX currently unreachable */ + printf("%s couldn't get bootdev media\n", __func__); + return (ENXIO); + } +#endif + + resourceService = IOService::getResourceService(); + if (!resourceService) { + dprintf("%s missing resource service\n", __func__); + /* Handle error */ + media->release(); + return (ENXIO); + } + +#if 1 + /* XXX publish an IOMedia as the BootUUIDMedia resource */ + /* uses same method as AppleFileSystemDriver */ + + /* Set IOMedia UUID */ + /* XXX skip (moved get uuid below) */ + // media->setProperty(kIOMediaUUIDKey, uuid); + /* Publish this IOMedia as the boot-uuid-media */ + IOService::publishResource(kBootUUIDMediaKey, media); + + /* Drop retain from earlier */ + media->release(); + /* Remove boot-uuid key so AppleFileSystem stops matching */ + resourceService->removeProperty(kBootUUIDKey); +#else + OSString *uuid = 0; + /* Get the current boot-uuid string */ + uuid = OSDynamicCast(OSString, + resourceService->getProperty(kBootUUIDKey, gIOServicePlane)); + if (!uuid) { + dprintf("%s missing boot-uuid IOResource\n", __func__); + /* Handle error */ + return (ENXIO); + } + printf("%s: got boot-uuid %s\n", __func__, uuid->getCStringNoCopy()); + + /* XXX Or use below and let AppleFileSystemDriver match it */ + /* Leaves the Apple_Boot content hint (at least for now) */ + media->setProperty(kIOMediaContentHintKey, "Apple_Boot"); + media->setProperty(kIOMediaUUIDKey, uuid); + /* Register for notifications (not matching) */ + media->registerService(kIOServiceAsynchronous); + /* Drop retain from earlier */ + media->release(); +#endif + + printf("%s done\n", __func__); + return (0); +} + +DSTATIC void +zfs_boot_import_thread(void *arg) +{ + nvlist_t *configs, *nv, *newnv; + nvpair_t *elem; + IOService *zfs_hl = 0; + OSSet *disks, *new_set = 0; + OSCollectionIterator *iter = 0; + OSObject *next; + IOMedia *media; + pool_list_t *pools = (pool_list_t *)arg; + uint64_t pool_state; + boolean_t pool_imported = B_FALSE; + int error = EINVAL; + + /* Verify pool list coult be cast */ + ASSERT3U(pools, !=, 0); + if (!pools) { + printf("%s %p %s\n", "zfs_boot_import_thread", + arg, "couldn't be cast as pool_list_t*"); + return; + } + + /* Abort early */ + if (pools->terminating != ZFS_BOOT_ACTIVE) { + dprintf("%s terminating 1\n", __func__); + goto out_unlocked; + } + + new_set = OSSet::withCapacity(1); + /* To swap with pools->disks while locked */ + if (!new_set) { + dprintf("%s couldn't allocate new_set\n", __func__); + goto out_unlocked; + } + + /* Take pool list lock */ + mutex_enter(&pools->lock); + + zfs_hl = pools->zfs_hl; + + /* Check for work, then sleep on the lock */ + do { + /* Abort early */ + if (pools->terminating != ZFS_BOOT_ACTIVE) { + dprintf("%s terminating 2\n", __func__); + goto out_locked; + } + + /* Check for work */ + if (pools->disks->getCount() == 0) { + dprintf("%s no disks to check\n", __func__); + goto next_locked; + } + + /* Swap full set with a new empty one */ + ASSERT3U(new_set, !=, 0); + disks = pools->disks; + pools->disks = new_set; + new_set = 0; + + /* Release pool list lock */ + mutex_exit(&pools->lock); + + /* Create an iterator over the objects in the set */ + iter = OSCollectionIterator::withCollection(disks); + + /* couldn't be initialized */ + if (!iter) { + dprintf("%s %s %d %s\n", "zfs_boot_import_thread", + "couldn't get iterator from collection", + disks->getCount(), "disks skipped"); + + /* Merge disks back into pools->disks */ + mutex_enter(&pools->lock); + pools->disks->merge(disks); + mutex_exit(&pools->lock); + + /* Swap 'disks' back to new_set */ + disks->flushCollection(); + new_set = disks; + disks = 0; + + continue; + } + + /* Iterate over all disks */ + while ((next = iter->getNextObject()) != NULL) { + /* Cast each IOMedia object */ + media = OSDynamicCast(IOMedia, next); + + if (!iter->isValid()) { + /* Oh gosh, need to start over */ + iter->reset(); + continue; + } + + if (!media) { + dprintf("%s couldn't cast IOMedia\n", + __func__); + continue; + } + + /* Check this IOMedia device for a vdev label */ + if (!zfs_boot_probe_disk(pools, media)) { + dprintf("%s couldn't probe disk\n", + __func__); + continue; + } + } + + /* Clean up */ + media = 0; + iter->release(); + iter = 0; + + /* Swap 'disks' back to new_set */ + disks->flushCollection(); + new_set = disks; + disks = 0; + + /* Abort early */ + if (pools->terminating != ZFS_BOOT_ACTIVE) { + dprintf("%s terminating 3\n", __func__); + goto out_unlocked; + } + + mutex_enter(&pools->lock); + /* Check for work */ + if (pools->disks->getCount() != 0) { + dprintf("%s more disks available, looping\n", __func__); + continue; + } + /* Release pool list lock */ + mutex_exit(&pools->lock); + + /* Generate a list of pool configs to import */ + configs = zfs_boot_get_configs(pools, B_TRUE); + + /* Abort early */ + if (pools->terminating != ZFS_BOOT_ACTIVE) { + dprintf("%s terminating 4\n", __func__); + goto out_unlocked; + } + + /* Iterate over the nvlists (stored as nvpairs in nvlist) */ + elem = NULL; + while ((elem = nvlist_next_nvpair(configs, + elem)) != NULL) { + /* Cast the nvpair back to nvlist */ + nv = NULL; + verify(nvpair_value_nvlist(elem, &nv) == 0); + + /* Check vdev state */ + verify(nvlist_lookup_uint64(nv, + ZPOOL_CONFIG_POOL_STATE, + &pool_state) == 0); + if (pool_state == POOL_STATE_DESTROYED) { + dprintf("%s skipping destroyed pool\n", + __func__); + continue; + } + + /* Abort early */ + if (pools->terminating != ZFS_BOOT_ACTIVE) { + dprintf("%s terminating 5\n", __func__); + goto out_unlocked; + } + + /* Try import */ + newnv = spa_tryimport(nv); + nvlist_free(nv); + nv = 0; + if (newnv) { + dprintf("%s newnv: %p\n", __func__, newnv); + + /* Stop probing disks */ + if (pools->notifier) + pools->notifier->disable(); + + /* Do import */ + pool_imported = (spa_import(pools->pool_name, + newnv, 0, 0) == 0); + nvlist_free(newnv); + newnv = 0; + // pool_imported = spa_import_rootpool(nv); + } else { + dprintf("%s no newnv returned\n", __func__); + } + + dprintf("%s spa_import returned %d\n", __func__, + pool_imported); + + if (pool_imported) { + /* Get bootfs and publish IOMedia */ + error = zfs_boot_publish_bootfs(zfs_hl, pools); + if (error != 0) { + dprintf("%s publish bootfs error %d\n", + __func__, error); + } + + goto out_unlocked; + } else { + /* Resume notifications */ + if (pools->notifier) + pools->notifier->enable(true); + } + } + + /* Retake pool list lock */ + mutex_enter(&pools->lock); + +next_locked: + /* Check for work */ + if (pools->disks->getCount() != 0) { + continue; + } + + /* Abort early */ + if (pools->terminating != ZFS_BOOT_ACTIVE) { + dprintf("%s terminating 6\n", __func__); + goto out_locked; + } + + dprintf("%s sleeping on lock\n", __func__); + /* Sleep on lock, thread is resumed with lock held */ + cv_timedwait_sig(&pools->cv, &pools->lock, + ddi_get_lbolt() + hz); + + /* Loop forever */ + } while (true); + +out_locked: + /* Unlock pool list lock */ + mutex_exit(&pools->lock); + +out_unlocked: + /* Cleanup new_set */ + if (new_set) { + new_set->flushCollection(); + new_set->release(); + new_set = 0; + } + + /* Teardown pool list, lock, etc */ + zfs_boot_free(); + + return; /* taskq_dispatch */ +#if 0 + thread_exit(); /* thread_create */ +#endif +} + +DSTATIC bool +zfs_boot_check_mountroot(char **pool_name, uint64_t *pool_guid) +{ + /* + * Check if the kext is loading during early boot + * and/or check if root is mounted (IORegistry?) + * Use PE Boot Args to determine the root pool name. + */ + char *zfs_boot; + char *split; + uint64_t len; + bool result = false; + uint64_t uptime = 0; + + + if (!pool_name || !pool_guid) { + dprintf("%s %s\n", __func__, + "invalid pool_name or pool_guid ptr"); + return (false); + } + + /* XXX Ugly hack to determine if this is early boot */ + /* + * Could just check if boot-uuid (or rd= or rootdev=) + * are set, and abort otherwise + * IOResource "boot-uuid" only published before root is + * mounted, or "boot-uuid-media" once discovered + */ + clock_get_uptime(&uptime); /* uptime since boot in nanoseconds */ + dprintf("%s uptime: %llu\n", __func__, uptime); + + /* 3 billion nanoseconds ~= 3 seconds */ + // if (uptime >= 3LLU<<30) { + /* 60 billion nanoseconds ~= 60 seconds */ + if (uptime >= 7LLU<<33) { + dprintf("%s %s\n", __func__, "Already booted"); + /* + * Start the getrootdir() from working, the vfs_start() call + * isn't called until first mount, which is too late for + * spa_async_dispatch(). + */ + return (false); + } else { + dprintf("%s %s\n", __func__, "Boot time"); + } + + zfs_boot = (char *)kmem_alloc(ZFS_MAX_DATASET_NAME_LEN, KM_SLEEP); + + if (!zfs_boot) { + dprintf("%s couldn't allocate zfs_boot\n", __func__); + return (false); + } + + result = PE_parse_boot_argn("zfs_boot", zfs_boot, + ZFS_MAX_DATASET_NAME_LEN); + // dprintf( "Raw zfs_boot: [%llu] {%s}\n", + // (uint64_t)strlen(zfs_boot), zfs_boot); + + result = (result && (zfs_boot != 0) && strlen(zfs_boot) > 0); + + if (!result) { + result = PE_parse_boot_argn("rd", zfs_boot, + MAXPATHLEN); + result = (result && (zfs_boot != 0) && + strlen(zfs_boot) > 0 && + strncmp(zfs_boot, "zfs:", 4)); + // dprintf("Raw rd: [%llu] {%s}\n", + // (uint64_t)strlen(zfs_boot), zfs_boot ); + } + if (!result) { + result = PE_parse_boot_argn("rootdev", zfs_boot, + MAXPATHLEN); + result = (result && (zfs_boot != 0) && + strlen(zfs_boot) > 0 && + strncmp(zfs_boot, "zfs:", 4)); + // dprintf("Raw rootdev: [%llu] {%s}\n", + // (uint64_t)strlen(zfs_boot), zfs_boot ); + } + + /* + * XXX To Do - parse zpool_guid boot arg + */ + *pool_guid = 0; + + if (result) { + /* Check for first slash in zfs_boot */ + split = strchr(zfs_boot, '/'); + if (split) { + /* copy pool name up to first slash */ + len = (split - zfs_boot); + } else { + /* or copy whole string */ + len = strlen(zfs_boot); + } + + *pool_name = (char *)kmem_alloc(len+1, KM_SLEEP); + snprintf(*pool_name, len+1, "%s", zfs_boot); + + dprintf("Got zfs_boot: [%llu] {%s}->{%s}\n", + *pool_guid, zfs_boot, *pool_name); + } else { + dprintf("%s\n", "No zfs_boot\n"); + pool_name = 0; + } + + kmem_free(zfs_boot, ZFS_MAX_DATASET_NAME_LEN); + zfs_boot = 0; + return (result); +} + +bool +zfs_boot_init(IOService *zfs_hl) +{ + IONotifier *notifier = 0; + pool_list_t *pools = 0; + char *pool_name = 0; + uint64_t pool_guid = 0; + + zfs_boot_pool_list = 0; + + if (!zfs_hl) { + dprintf("%s: No zfs_hl provided\n", __func__); + return (false); + } + + if (!zfs_boot_check_mountroot(&pool_name, &pool_guid) || + (!pool_name && pool_guid == 0)) { + /* + * kext is not being loaded during early-boot, + * or no pool is specified for import. + */ + dprintf("%s: check failed\n", __func__); + return (true); + } + + pools = (pool_list_t *)kmem_alloc(sizeof (pool_list_t), + KM_SLEEP); + if (!pools) { + goto error; + } + bzero(pools, sizeof (pool_list_t)); + + if ((pools->disks = OSSet::withCapacity( + ZFS_BOOT_PREALLOC_SET)) == NULL) { + /* Fail if memory couldn't be allocated */ + goto error; + } + + /* create the zfs_boot taskq */ + + zfs_boot_taskq = + taskq_create("zfs_boot_taskq", 100, defclsyspri, + max_ncpus, INT_MAX, + TASKQ_PREPOPULATE | TASKQ_THREADS_CPU_PCT); + + VERIFY(zfs_boot_taskq); + + /* create the lock and cv early, before notifier */ + mutex_init(&pools->lock, NULL, MUTEX_DEFAULT, NULL); + cv_init(&pools->cv, NULL, CV_DEFAULT, NULL); + + pools->pools = 0; + pools->names = 0; + pools->pool_guid = pool_guid; + pools->pool_name = pool_name; + pools->zfs_hl = zfs_hl; + /* and finally hit the _Atomic to spread the above */ + pools->terminating = ZFS_BOOT_ACTIVE; + + notifier = IOService::addMatchingNotification( + gIOFirstPublishNotification, IOService::serviceMatching( + "IOMediaBSDClient"), zfs_boot_probe_media, + zfs_hl, pools, 0); + + if (!notifier) { + /* Fail if memory couldn't be allocated */ + goto error; + } + pools->notifier = notifier; + + /* Finally, start the import thread */ + VERIFY3U(taskq_dispatch(zfs_boot_taskq, zfs_boot_import_thread, + (void*)pools, TQ_SLEEP), !=, 0); + +#if 0 +/* Alternate method of scheduling the import thread */ + (void) thread_create(NULL, 0, zfs_boot_import_thread, + pools, 0, &p0, + TS_RUN, minclsyspri); +#endif + + zfs_boot_pool_list = pools; + + return (true); + +error: + if (pools) { + if (pools->disks) { + pools->disks->flushCollection(); + pools->disks->release(); + pools->disks = 0; + } + kmem_free(pools, sizeof (pool_list_t)); + pools = 0; + } + return (false); +} + +/* Include these functions in all builds */ + +/* + * zfs_boot_update_bootinfo_vdev_leaf + * Inputs: spa: valid pool spa pointer. vd: valid vdev pointer. + * Return: 0 on success, positive integer errno on failure. + * Callers: zfs_boot_update_bootinfo_vdev + * + * called by bootinfo_vdev with each leaf vdev. + */ +DSTATIC int +zfs_boot_update_bootinfo_vdev_leaf(OSArray *array, vdev_t *vd) +{ + OSDictionary *dict; + OSString *dev_str; + OSNumber *dev_size; + vdev_disk_t *dvd; + struct io_bootinfo *info; + int error; + + /* Validate inputs */ + if (!array || !vd) { + dprintf("%s missing argument\n", __func__); + return (EINVAL); + } + + /* Should be called with leaf vdev */ + if (!vd->vdev_ops->vdev_op_leaf) { + dprintf("%s not a leaf vdev\n", __func__); + return (EINVAL); + } + + /* Skip hole vdevs */ + if (vd->vdev_ishole) { + dprintf("%s skipping hole in namespace\n", __func__); + return (0); + } + + /* No info available if missing */ + if (strcmp(vd->vdev_ops->vdev_op_type, VDEV_TYPE_MISSING) == 0) { + dprintf("%s skipping missing vdev\n", __func__); + return (0); + } + + /* Must be a disk, not a file */ + if (strcmp(vd->vdev_ops->vdev_op_type, VDEV_TYPE_DISK) != 0) { + dprintf("%s skipping non-disk vdev\n", __func__); + return (0); + } + + /* Skip obviously non-bootable vdevs */ + if (vd->vdev_islog || + vd->vdev_isl2cache || vd->vdev_isspare) { + dprintf("%s skipping non-bootable\n", __func__); + return (0); + } + + /* Get vdev type-specific data */ + dvd = (vdev_disk_t *)vd->vdev_tsd; + if (!dvd || !dvd->vd_lh) { + dprintf("%s missing dvd or ldi handle\n", __func__); + return (0); + } + + /* Allocate an ldi io_bootinfo struct */ + info = (struct io_bootinfo *)kmem_alloc( + sizeof (struct io_bootinfo), KM_SLEEP); + if (!info) { + dprintf("%s info alloc failed\n", __func__); + return (ENOMEM); + } + bzero(info, sizeof (struct io_bootinfo)); + + /* Ask the vdev handle to fill in the info */ + error = ldi_ioctl(dvd->vd_lh, DKIOCGETBOOTINFO, + (intptr_t)info, 0, 0, 0); + if (error != 0) { + dprintf("%s ioctl error %d\n", __func__, error); + kmem_free(info, sizeof (struct io_bootinfo)); + return (EIO); + } + + /* Allocate dictionary to hold the keys */ + if ((dict = OSDictionary::withCapacity(2)) == NULL) { + dprintf("%s dictionary alloc failed\n", __func__); + kmem_free(info, sizeof (struct io_bootinfo)); + return (ENOMEM); + } + + /* Keys are path (string) and size (number) */ + dev_str = OSString::withCString(info->dev_path); + dev_size = OSNumber::withNumber(info->dev_size, + (8 * sizeof (info->dev_size))); + kmem_free(info, sizeof (struct io_bootinfo)); + info = 0; + + /* Add keys to dictionary or bail */ + if (!dev_str || !dev_size || + dict->setObject(kIOBootDevicePathKey, + dev_str) == false || + dict->setObject(kIOBootDeviceSizeKey, + dev_size) == false) { + dprintf("%s dictionary setup failed\n", __func__); + if (dev_str) dev_str->release(); + if (dev_size) dev_size->release(); + dict->release(); + dict = 0; + return (ENOMEM); + } + dev_str->release(); + dev_str = 0; + dev_size->release(); + dev_size = 0; + + /* Add dict to array */ + if (array->setObject(dict) == false) { + dprintf("%s couldn't set bootinfo\n", __func__); + dict->release(); + dict = 0; + return (ENOMEM); + } + dict->release(); + dict = 0; + + return (0); +} + +/* + * zfs_boot_update_bootinfo_vdev + * Inputs: spa: valid pool spa pointer. vd: valid vdev pointer. + * Return: 0 on success, positive integer errno on failure. + * Callers: zfs_boot_update_bootinfo + * + * called by bootinfo with root vdev, and recursively calls + * itself while iterating over children (vdevs only have a + * few levels of nesting at most). + */ +DSTATIC int +zfs_boot_update_bootinfo_vdev(OSArray *array, vdev_t *vd) +{ + int c, error; + + /* Validate inputs */ + if (!array || !vd) { + dprintf("%s missing argument\n", __func__); + return (EINVAL); + } + + /* Skip obviously non-bootable vdevs */ + if (vd->vdev_islog || + vd->vdev_isl2cache || vd->vdev_isspare) { + dprintf("%s skipping non-bootable\n", __func__); + return (0); + } + + /* Process leaf vdevs */ + if (vd->vdev_ops->vdev_op_leaf) { + error = zfs_boot_update_bootinfo_vdev_leaf(array, vd); + if (error) + dprintf("%s bootinfo_vdev_leaf error %d\n", + __func__, error); + return (error); + } + + /* Iterate over child vdevs */ + for (c = 0; c < vd->vdev_children; c++) { + if (vd->vdev_child[c] == NULL) { + dprintf("%s hole in vdev namespace\n", __func__); + continue; + } + + /* Recursion */ + error = zfs_boot_update_bootinfo_vdev(array, + vd->vdev_child[c]); + if (error != 0) { + dprintf("%s bootinfo_vdev_leaf error %d\n", + __func__, error); + return (error); + } + } + + return (0); +} + +extern "C" { + +/* + * zfs_boot_update_bootinfo + * Inputs: spa: valid pool spa pointer. + * Return: 0 on success, positive integer errno on failure. + * Callers: spa_open_common, spa_vdev_add, spa_vdev_remove, + * spa_vdev_attach, spa_vdev_detach. + * + * Called from spa.c on changes to the vdev layout. This + * information is assigned to the pool proxy so all zvols + * and datasets will retrieve the property through IOKit + * since it is retrieved via recursion. + * (see bless-105/Misc/BLCreateBooterInformationDictionary.c). + * If IOBootDevice property is needed for each dataset and + * zvol, we can revisit this and assign/update on all of + * these (already implemented a prototype that worked fine). + * + * Note: bootinfo is only collected for data vdevs. + * XXX We only want boot helpers there, unless there is a + * compelling argument for log, cache, or spares having + * boot helpers. + */ +int +zfs_boot_update_bootinfo(spa_t *spa) +{ + ZFSPool *pool_proxy; + OSArray *array; + int error; + + if (!spa) { + dprintf("%s missing spa\n", __func__); + return (EINVAL); + } + + /* XXX Could count vdevs first? */ + if ((array = OSArray::withCapacity(1)) == NULL) { + dprintf("%s allocation failed\n", __func__); + return (ENOMEM); + } + + /* Grab necessary locks */ + mutex_enter(&spa_namespace_lock); + spa_open_ref(spa, FTAG); + + /* Get pool proxy */ + if (!spa->spa_iokit_proxy || + (pool_proxy = spa->spa_iokit_proxy->proxy) == NULL) { + spa_close(spa, FTAG); + mutex_exit(&spa_namespace_lock); + dprintf("%s no spa_pool_proxy\n", __func__); + return (0); + } + /* Avoid it disappearing from under us */ + pool_proxy->retain(); + + /* Don't need to hold this throughout */ + mutex_exit(&spa_namespace_lock); + + /* vdev state lock only requires an spa open ref */ + spa_vdev_state_enter(spa, SCL_NONE); + + /* Iterate over all vdevs */ + if ((error = zfs_boot_update_bootinfo_vdev(array, + spa->spa_root_vdev)) != 0) { + dprintf("%s bootinfo_vdev error %d\n", + __func__, error); + + /* Drop locks */ + (void) spa_vdev_state_exit(spa, NULL, 0); + mutex_enter(&spa_namespace_lock); + spa_close(spa, FTAG); + mutex_exit(&spa_namespace_lock); + array->release(); + pool_proxy->release(); + return (error); + } + + /* Release locks, passing NULL vd (no change) */ + error = spa_vdev_state_exit(spa, NULL, 0); + if (error != 0) { + dprintf("%s spa_vdev_state_exit error %d\n", + __func__, error); + } + + /* setProperty adds a retain */ + pool_proxy->setProperty(kIOBootDeviceKey, array); + pool_proxy->release(); + array->release(); + + /* Drop locks */ + mutex_enter(&spa_namespace_lock); + spa_close(spa, FTAG); + mutex_exit(&spa_namespace_lock); + return (0); +} + +} /* extern "C" */ + +#if 0 +#ifdef ZFS_BOOT +/* Remainder only needed for boot */ + +#define DPRINTF_FUNC() dprintf("%s\n", __func__) + +#pragma mark - ZFSBootDevice + +OSDefineMetaClassAndStructors(ZFSBootDevice, IOBlockStorageDevice); +char ZFSBootDevice::vendorString[4] = "ZFS"; +char ZFSBootDevice::revisionString[4] = "0.1"; +char ZFSBootDevice::infoString[12] = "ZFS dataset"; + +#if 0 +int +zfs_boot_get_path(char *path, int len) +{ + OSString *disk = 0; + + if (!path || len == 0) { + dprintf("%s: invalid argument\n", __func__); + return (-1); + } + + if (bootdev) { + disk = OSDynamicCast(OSString, + bootdev->getProperty(kIOBSDNameKey, gIOServicePlane, + kIORegistryIterateRecursively)); + } + + if (disk) { + snprintf(path, len, "/dev/%s", disk->getCStringNoCopy()); + return (0); + } + + return (-1); +} +#endif + +bool +ZFSBootDevice::init(OSDictionary *properties) +{ + /* Allocate dictionaries and symbols */ + OSDictionary *pdict = OSDictionary::withCapacity(2); + OSDictionary *ddict = OSDictionary::withCapacity(4); + const OSSymbol *virtualSymbol = OSSymbol::withCString( + kIOPropertyPhysicalInterconnectTypeVirtual); + const OSSymbol *ramSymbol = OSSymbol::withCString( + kIOPropertyInterconnectRAMKey); + const OSSymbol *ssdSymbol = OSSymbol::withCString( + kIOPropertyMediumTypeSolidStateKey); + OSNumber *physSize = OSNumber::withNumber((uint32_t)4096, 32); + OSNumber *logSize = OSNumber::withNumber((uint32_t)512, 32); + const OSSymbol *vendorSymbol = 0; + const OSSymbol *revisionSymbol = 0; + const OSSymbol *blankSymbol = 0; + OSBoolean *rdonly = 0; + OSString *str = 0; + const char *cstr = 0; + bool ret = false; + + DPRINTF_FUNC(); + + /* Validate allocations */ + if (!pdict || !ddict || !virtualSymbol || !ramSymbol || + !ssdSymbol || !physSize || !logSize) { + dprintf("ZFSBootDevice::%s allocation failed\n", __func__); + goto error; + } + + /* Init class statics every time an instance inits */ + /* Shared across instances, but doesn't hurt to reprint */ + snprintf(vendorString, strlen("ZFS")+1, "ZFS"); + snprintf(revisionString, strlen("0.1")+1, "0.1"); + snprintf(infoString, strlen("ZFS dataset")+1, "ZFS dataset"); + + /* For IORegistry keys, cache OSSymbols for class statics */ + /* Leverages OSSymbol cahce pool to reuse across instances */ + vendorSymbol = OSSymbol::withCString(vendorString); + revisionSymbol = OSSymbol::withCString(revisionString); + blankSymbol = OSSymbol::withCString(""); + if (!vendorSymbol || !revisionSymbol || !blankSymbol) { + dprintf("ZFSBootDevice::%s class symbols failed\n", __func__); + goto error; + } + + /* Call super init */ + if (IOBlockStorageDevice::init(properties) == false) { + dprintf("ZFSBootDevice::%s device init failed\n", __func__); + goto error; + } + + /* Set class private vars */ + productString = NULL; + isReadOnly = false; // XXX should really be true initially + + /* Set Protocol Characteristics */ + if (pdict->setObject(kIOPropertyPhysicalInterconnectLocationKey, + ramSymbol) == false || + pdict->setObject(kIOPropertyPhysicalInterconnectTypeKey, + virtualSymbol) == false) { + dprintf("%s pdict set properties failed\n", __func__); + goto error; + } + setProperty(kIOPropertyProtocolCharacteristicsKey, pdict); + + /* Set Device Characteristics */ + if (ddict->setObject(kIOPropertyVendorNameKey, + vendorSymbol) == false || + ddict->setObject(kIOPropertyProductRevisionLevelKey, + revisionSymbol) == false || + ddict->setObject(kIOPropertyProductSerialNumberKey, + blankSymbol) == false || + ddict->setObject(kIOPropertyPhysicalBlockSizeKey, + physSize) == false || + ddict->setObject(kIOPropertyLogicalBlockSizeKey, + logSize) == false || + ddict->setObject(kIOPropertyMediumTypeKey, + ssdSymbol) == false) { + dprintf("%s ddict set properties failed\n", __func__); + goto error; + } + setProperty(kIOPropertyDeviceCharacteristicsKey, ddict); + + /* Check for passed in readonly status */ + if (properties && (rdonly = OSDynamicCast(OSBoolean, + properties->getObject(ZFS_BOOT_DATASET_RDONLY_KEY))) != NULL) { + /* Got the boolean */ + isReadOnly = rdonly->getValue(); + dprintf("ZFSBootDevice %s set %s\n", __func__, + (isReadOnly ? "readonly" : "readwrite")); + } + + /* Check for passed in dataset UUID */ + if (properties && (str = OSDynamicCast(OSString, + properties->getObject(ZFS_BOOT_DATASET_UUID_KEY))) != NULL && + (cstr = str->getCStringNoCopy()) != NULL) { + /* Got the string, try to set UUID */ + str->retain(); + if (ddict->setObject("Dataset UUID", str) == false) { + dprintf("ZFSBootDevice::%s failed UUID [%s]\n", + __func__, cstr); + str->release(); + goto error; + } + dprintf("ZFSBootDevice::%s set UUID [%s]\n", + __func__, cstr); + str->release(); + } + + /* Check for passed in dataset name */ + if (properties && (str = OSDynamicCast(OSString, + properties->getObject(ZFS_BOOT_DATASET_NAME_KEY))) != NULL && + (cstr = str->getCStringNoCopy()) != NULL) { + /* Got the string, try to set name */ + str->retain(); + if (setDatasetName(cstr) == false) { + /* Unlikely */ + dprintf("ZFSBootDevice %s couldn't setup dataset" + " name property [%s]\n", __func__, cstr); + str->release(); + goto error; + } + + dprintf("ZFSBootDevice %s set dataset name [%s]\n", + __func__, cstr); + str->release(); + } else { + if (setDatasetName("invalid") == false) { + dprintf("ZFSBootDevice::%s setDatasetName failed\n", + __func__); + goto error; + } + dprintf("ZFSBootDevice %s set name [invalid]\n", __func__); + } + + /* Success */ + ret = true; + +error: + if (pdict) pdict->release(); + if (ddict) ddict->release(); + if (virtualSymbol) virtualSymbol->release(); + if (ramSymbol) ramSymbol->release(); + if (ssdSymbol) ssdSymbol->release(); + if (physSize) physSize->release(); + if (logSize) logSize->release(); + if (vendorSymbol) vendorSymbol->release(); + if (revisionSymbol) revisionSymbol->release(); + if (blankSymbol) blankSymbol->release(); + return (ret); +} + +void +ZFSBootDevice::free() +{ + char *pstring = (char *)productString; + productString = 0; + + if (pstring) kmem_free(pstring, strlen(pstring) + 1); + + IOBlockStorageDevice::free(); +} + +#if 0 +bool +ZFSBootDevice::attach(IOService *provider) +{ + DPRINTF_FUNC(); + // return (IOMedia::attach(provider)); + return (IOBlockStorageDevice::attach(provider)); +} + +void +ZFSBootDevice::detach(IOService *provider) +{ + DPRINTF_FUNC(); + // IOMedia::detach(provider); + IOBlockStorageDevice::detach(provider); +} + +bool +ZFSBootDevice::start(IOService *provider) +{ + DPRINTF_FUNC(); + // return (IOMedia::start(provider)); + return (IOBlockStorageDevice::start(provider)); +} + +void +ZFSBootDevice::stop(IOService *provider) +{ + DPRINTF_FUNC(); + // IOMedia::stop(provider); + IOBlockStorageDevice::stop(provider); +} + +IOService* +ZFSBootDevice::probe(IOService *provider, SInt32 *score) +{ + DPRINTF_FUNC(); + // return (IOMedia::probe(provider, score)); + return (IOBlockStorageDevice::probe(provider, score)); +} +#endif + +IOReturn +ZFSBootDevice::doSynchronizeCache(void) +{ + dprintf("ZFSBootDevice %s\n", __func__); + return (kIOReturnSuccess); +} + +IOReturn +ZFSBootDevice::doAsyncReadWrite(IOMemoryDescriptor *buffer, + UInt64 block, UInt64 nblks, + IOStorageAttributes *attributes, + IOStorageCompletion *completion) +{ + char zero[ZFS_BOOT_DEV_BSIZE]; + size_t len, cur, off = 0; + + DPRINTF_FUNC(); + + if (!buffer) { + IOStorage::complete(completion, kIOReturnError, 0); + return (kIOReturnSuccess); + } + + /* Read vs. write */ + if (buffer->getDirection() == kIODirectionIn) { + /* Zero the read buffer */ + bzero(zero, ZFS_BOOT_DEV_BSIZE); + len = buffer->getLength(); + while (len > 0) { + cur = (len > ZFS_BOOT_DEV_BSIZE ? + ZFS_BOOT_DEV_BSIZE : len); + buffer->writeBytes(/* offset */ off, + /* buf */ zero, /* length */ cur); + off += cur; + len -= cur; + } + // dprintf("%s: read: %llu %llu\n", + // __func__, block, nblks); + IOStorage::complete(completion, kIOReturnSuccess, + buffer->getLength()); + return (kIOReturnSuccess); + } + + if (buffer->getDirection() != kIODirectionOut) { + dprintf("%s invalid direction %d\n", __func__, + buffer->getDirection()); + IOStorage::complete(completion, kIOReturnError, 0); + return (kIOReturnSuccess); + } + + /* + * XXX For now this just returns error for all writes. + * If it turns out that mountroot/bdevvp try to + * verify writable status by reading a block and writing + * it back to disk, lie and say it succeeded. + */ + dprintf("%s: write: %llu %llu\n", __func__, block, nblks); + IOStorage::complete(completion, kIOReturnError, 0); + return (kIOReturnSuccess); +} + +IOReturn +ZFSBootDevice::doEjectMedia() +{ + DPRINTF_FUNC(); + /* XXX Called at shutdown, maybe return success? */ + return (kIOReturnError); +} + +IOReturn +ZFSBootDevice::doFormatMedia(UInt64 byteCapacity) +{ + DPRINTF_FUNC(); + /* XXX shouldn't need it */ + return (kIOReturnError); + // return (kIOReturnSuccess); +} + +UInt32 +ZFSBootDevice::doGetFormatCapacities(UInt64 *capacities, + UInt32 capacitiesMaxCount) const +{ + DPRINTF_FUNC(); + if (capacities && capacitiesMaxCount > 0) { + capacities[0] = (ZFS_BOOT_DEV_BSIZE * ZFS_BOOT_DEV_BCOUNT); + dprintf("ZFSBootDevice %s: capacity %llu\n", + __func__, capacities[0]); + } + + /* Always inform caller of capacity count */ + return (1); +} + +/* Assign dataset name from null-terminated string */ +bool +ZFSBootDevice::setDatasetName(const char *dsname) +{ + OSDictionary *dict; + OSString *dsstr; + char *newname, *oldname; + size_t len; + + DPRINTF_FUNC(); + + /* Validate arguments */ + if (!dsname || (len = strnlen(dsname, + ZFS_MAX_DATASET_NAME_LEN)) == 0) { + dprintf("%s: missing argument\n", __func__); + return (false); + } + + /* Truncate too-long names (shouldn't happen) */ + if (len == ZFS_MAX_DATASET_NAME_LEN && + dsname[ZFS_MAX_DATASET_NAME_LEN] != '\0') { + dprintf("%s: dsname too long [%s]\n", + __func__, dsname); + /* XXX Just truncate the name */ + len--; + } + + /* Allocate room for name plus null char */ + newname = (char *)kmem_alloc(len+1, KM_SLEEP); + if (!newname) { + dprintf("ZFSBootDevice::%s string alloc failed\n", __func__); + return (false); + } + snprintf(newname, len+1, "%s", dsname); + newname[len] = '\0'; /* just in case */ + + /* Save an OSString copy for IORegistry */ + dsstr = OSString::withCString(newname); + if (!dsstr) { + dprintf("ZFSBootDevice::%s OSString failed\n", __func__); + kmem_free(newname, len+1); + return (false); + } + + /* Swap into class private var */ + oldname = productString; + productString = newname; + newname = 0; + if (oldname) { + kmem_free(oldname, strlen(oldname)+1); + oldname = 0; + } + + /* Get and clone device characteristics prop dict */ + if ((dict = OSDynamicCast(OSDictionary, + getProperty(kIOPropertyDeviceCharacteristicsKey))) == NULL || + (dict = OSDictionary::withDictionary(dict)) == NULL) { + dprintf("%s couldn't clone prop dict\n", __func__); + /* Should only happen during initialization */ + } + + if (dict) { + /* Copy string, add to dictionary, and replace prop dict */ + if (dict->setObject(kIOPropertyProductNameKey, + dsstr) == false || + setProperty(kIOPropertyDeviceCharacteristicsKey, + dict) == false) { + dprintf("%s couldn't set name\n", __func__); + dsstr->release(); + dict->release(); + return (false); + } + dict->release(); + dict = 0; + } + + /* Finally, set the IORegistryEntry/IOService name */ + setName(dsstr->getCStringNoCopy()); + dsstr->release(); + + return (true); +} + +/* Returns full dataset name from instance private var */ +char * +ZFSBootDevice::getProductString() +{ + dprintf("ZFSBootDevice %s [%s]\n", productString); + /* Return class private string */ + return (productString); +} + +/* Returns readonly status from instance private var */ +IOReturn +ZFSBootDevice::reportWriteProtection(bool *isWriteProtected) +{ + DPRINTF_FUNC(); + if (isWriteProtected) *isWriteProtected = isReadOnly; + return (kIOReturnSuccess); +} + +/* These return class static string for all instances */ +char * +ZFSBootDevice::getVendorString() +{ + dprintf("ZFSBootDevice %s [%s]\n", vendorString); + /* Return class static string */ + return (vendorString); +} +char * +ZFSBootDevice::getRevisionString() +{ + dprintf("ZFSBootDevice %s [%s]\n", revisionString); + /* Return class static string */ + return (revisionString); +} +char * +ZFSBootDevice::getAdditionalDeviceInfoString() +{ + dprintf("ZFSBootDevice %s [%s]\n", infoString); + /* Return class static string */ + return (infoString); +} + +/* Always return media present and unchanged */ +IOReturn +ZFSBootDevice::reportMediaState(bool *mediaPresent, + bool *changedState) +{ + DPRINTF_FUNC(); + if (mediaPresent) *mediaPresent = true; + if (changedState) *changedState = false; + return (kIOReturnSuccess); +} + +/* Always report nonremovable and nonejectable */ +IOReturn +ZFSBootDevice::reportRemovability(bool *isRemoveable) +{ + DPRINTF_FUNC(); + if (isRemoveable) *isRemoveable = false; + return (kIOReturnSuccess); +} +IOReturn +ZFSBootDevice::reportEjectability(bool *isEjectable) +{ + DPRINTF_FUNC(); + if (isEjectable) *isEjectable = false; + return (kIOReturnSuccess); +} + +/* Always report 512b blocksize */ +IOReturn +ZFSBootDevice::reportBlockSize(UInt64 *blockSize) +{ + DPRINTF_FUNC(); + if (!blockSize) + return (kIOReturnError); + + *blockSize = ZFS_BOOT_DEV_BSIZE; + return (kIOReturnSuccess); +} + +/* XXX Calculate from dev_bcount, should get size from objset */ +/* XXX Can issue message kIOMessageMediaParametersHaveChanged to update */ +IOReturn +ZFSBootDevice::reportMaxValidBlock(UInt64 *maxBlock) +{ + DPRINTF_FUNC(); + if (!maxBlock) + return (kIOReturnError); + + // *maxBlock = 0; + *maxBlock = ZFS_BOOT_DEV_BCOUNT - 1; + dprintf("ZFSBootDevice %s: maxBlock %llu\n", __func__, *maxBlock); + + return (kIOReturnSuccess); +} +#endif /* ZFS_BOOT */ +#endif /* 0 */ diff --git a/module/os/macos/zfs/zfs_ctldir.c b/module/os/macos/zfs/zfs_ctldir.c new file mode 100644 index 000000000000..40979fad3d6a --- /dev/null +++ b/module/os/macos/zfs/zfs_ctldir.c @@ -0,0 +1,1517 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (C) 2011 Lawrence Livermore National Security, LLC. + * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). + * LLNL-CODE-403049. + * Rewritten for Linux by: + * Rohan Puri + * Brian Behlendorf + * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved. + * Copyright (c) 2018 George Melikov. All Rights Reserved. + * Copyright (c) 2019 Datto, Inc. All rights reserved. + * Copyright (c) 2020 Jorgen Lundman. All rights reserved. + */ + +/* + * ZFS control directory (a.k.a. ".zfs") + * + * This directory provides a common location for all ZFS meta-objects. + * Currently, this is only the 'snapshot' and 'shares' directory, but this may + * expand in the future. The elements are built dynamically, as the hierarchy + * does not actually exist on disk. + * + * For 'snapshot', we don't want to have all snapshots always mounted, because + * this would take up a huge amount of space in /etc/mnttab. We have three + * types of objects: + * + * ctldir ------> snapshotdir -------> snapshot + * | + * | + * V + * mounted fs + * + * The 'snapshot' node contains just enough information to lookup '..' and act + * as a mountpoint for the snapshot. Whenever we lookup a specific snapshot, we + * perform an automount of the underlying filesystem and return the + * corresponding vnode. + * + * All mounts are handled automatically by an user mode helper which invokes + * the mount procedure. Unmounts are handled by allowing the mount + * point to expire so the kernel may automatically unmount it. + * + * The '.zfs', '.zfs/snapshot', and all directories created under + * '.zfs/snapshot' (ie: '.zfs/snapshot/') all share the same + * zfsvfs_t as the head filesystem (what '.zfs' lives under). + * + * File systems mounted on top of the '.zfs/snapshot/' paths + * (ie: snapshots) are complete ZFS filesystems and have their own unique + * zfsvfs_t. However, the fsid reported by these mounts will be the same + * as that used by the parent zfsvfs_t to make NFS happy. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "zfs_namecheck.h" + +extern kmem_cache_t *znode_cache; +extern uint64_t vnop_num_vnodes; + +/* + * Apple differences; + * + * We don't have 'shares' directory, so only 'snapshot' is relevant. + * + * We can not issue mount from kernel, so involve zed. + * - see zfs_ctldir_snapdir.c + * + * All vnodes point to znode_t, no special case nodes. + */ + +/* List of zfsctl mounts waiting to be mounted */ +static kmutex_t zfsctl_mounts_lock; +static list_t zfsctl_mounts_list; +struct zfsctl_mounts_waiting { + kmutex_t zcm_lock; + kcondvar_t zcm_cv; + list_node_t zcm_node; + char zcm_name[ZFS_MAX_DATASET_NAME_LEN]; +}; +typedef struct zfsctl_mounts_waiting zfsctl_mounts_waiting_t; + + +/* + * Control Directory Tunables (.zfs) + */ +int zfs_expire_snapshot = ZFSCTL_EXPIRE_SNAPSHOT; +int zfs_admin_snapshot = 1; +int zfs_auto_snapshot = 1; + +static kmutex_t zfsctl_unmount_lock; +static kcondvar_t zfsctl_unmount_cv; +static boolean_t zfsctl_unmount_thread_exit; + +static kmutex_t zfsctl_unmount_list_lock; +static list_t zfsctl_unmount_list; + +struct zfsctl_unmount_delay { + char *se_name; /* full snapshot name */ + spa_t *se_spa; /* pool spa */ + uint64_t se_objsetid; /* snapshot objset id */ + time_t se_time; + list_node_t se_nodelink; +}; +typedef struct zfsctl_unmount_delay zfsctl_unmount_delay_t; + + +/* + * Check if the given vnode is a part of the virtual .zfs directory. + */ +boolean_t +zfsctl_is_node(struct vnode *ip) +{ + return (ITOZ(ip)->z_is_ctldir); +} + +typedef int (**vnode_operations)(void *); + + +/* + * Allocate a new vnode with the passed id and ops. + */ +static struct vnode * +zfsctl_vnode_alloc(zfsvfs_t *zfsvfs, uint64_t id, + char *name) +{ + timestruc_t now; + struct vnode *vp = NULL; + znode_t *zp = NULL; + struct vnode_fsparam vfsp; + + dprintf("%s\n", __func__); + + zp = kmem_cache_alloc(znode_cache, KM_SLEEP); + + gethrestime(&now); + ASSERT3P(zp->z_dirlocks, ==, NULL); + ASSERT3P(zp->z_acl_cached, ==, NULL); + ASSERT3P(zp->z_xattr_cached, ==, NULL); + zp->z_zfsvfs = zfsvfs; + zp->z_id = id; + zp->z_unlinked = B_FALSE; + zp->z_atime_dirty = B_FALSE; + zp->z_zn_prefetch = B_FALSE; + zp->z_is_sa = B_FALSE; + zp->z_is_mapped = B_FALSE; + zp->z_is_ctldir = B_TRUE; + zp->z_is_stale = B_FALSE; + zp->z_sa_hdl = NULL; + zp->z_blksz = 0; + zp->z_seq = 0; + zp->z_mapcnt = 0; + zp->z_size = 0; + zp->z_pflags = 0; + zp->z_mode = 0; + zp->z_sync_cnt = 0; + zp->z_gen = 0; + zp->z_mode = (S_IFDIR | (S_IRWXU|S_IRWXG|S_IRWXO)); + zp->z_uid = 0; + zp->z_gid = 0; + ZFS_TIME_ENCODE(&now, zp->z_atime); + + zp->z_snap_mount_time = 0; /* Allow automount attempt */ + + strlcpy(zp->z_name_cache, name, sizeof (zp->z_name_cache)); + + bzero(&vfsp, sizeof (vfsp)); + vfsp.vnfs_str = "zfs"; + vfsp.vnfs_mp = zfsvfs->z_vfs; + vfsp.vnfs_vtype = IFTOVT((mode_t)zp->z_mode); + vfsp.vnfs_fsnode = zp; + vfsp.vnfs_flags = VNFS_ADDFSREF; + + dprintf("%s zp %p with vp %p zfsvfs %p vfs %p: vtype %u: '%s'\n", + __func__, + zp, vp, zfsvfs, zfsvfs->z_vfs, vfsp.vnfs_vtype, name); + + /* Tag root directory */ + if (id == zfsvfs->z_root) + vfsp.vnfs_markroot = 1; + + /* + * This creates a vnode with VSYSTEM set, this is so that unmount's + * vflush() (called before our vfs_unmount) will pass (and not block + * waiting for the usercount ref to be released). We then release the + * VROOT vnode in zfsctl_destroy, and release the usercount ref. + * Because of this, we need to call vnode_recycle() ourselves in destroy + */ + if (id == ZFSCTL_INO_ROOT) + vfsp.vnfs_marksystem = 1; + + vfsp.vnfs_vops = zfs_ctldirops; + + while (vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, &vp) != 0) { + kpreempt(KPREEMPT_SYNC); + } + atomic_inc_64(&vnop_num_vnodes); + + dprintf("Assigned zp %p with vp %p zfsvfs %p\n", zp, vp, zp->z_zfsvfs); + + vnode_settag(vp, VT_ZFS); + + zp->z_vid = vnode_vid(vp); + zp->z_vnode = vp; + + mutex_enter(&zfsvfs->z_znodes_lock); + list_insert_tail(&zfsvfs->z_all_znodes, zp); + membar_producer(); + if (id < zfsvfs->z_ctldir_startid) + zfsvfs->z_ctldir_startid = id; + mutex_exit(&zfsvfs->z_znodes_lock); + + return (vp); +} + +/* + * Lookup the vnode with given id, it will be allocated if needed. + */ +static struct vnode * +zfsctl_vnode_lookup(zfsvfs_t *zfsvfs, uint64_t id, + char *name) +{ + struct vnode *ip = NULL; + int error = 0; + + dprintf("%s: looking for id %llu name '%s'\n", __func__, + id, name); + + while (ip == NULL) { + + error = zfs_vfs_vget(zfsvfs->z_vfs, id, &ip, NULL); + if (error == 0 && ip != NULL) + break; + + /* May fail due to concurrent zfsctl_vnode_alloc() */ + ip = zfsctl_vnode_alloc(zfsvfs, id, name); + } + + return (ip); +} + +/* + * Create the '.zfs' directory. This directory is cached as part of the VFS + * structure. This results in a hold on the zfsvfs_t. The code in zfs_umount() + * therefore checks against a vfs_count of 2 instead of 1. This reference + * is removed when the ctldir is destroyed in the unmount. All other entities + * under the '.zfs' directory are created dynamically as needed. + * + * Because the dynamically created '.zfs' directory entries assume the use + * of 64-bit vnode numbers this support must be disabled on 32-bit systems. + */ +int +zfsctl_create(zfsvfs_t *zfsvfs) +{ + ASSERT(zfsvfs->z_ctldir == NULL); + + dprintf("%s\n", __func__); + + /* Create root node, tagged with VSYSTEM - see above */ + zfsvfs->z_ctldir = zfsctl_vnode_alloc(zfsvfs, ZFSCTL_INO_ROOT, + ZFS_CTLDIR_NAME); + if (zfsvfs->z_ctldir == NULL) + return (SET_ERROR(ENOENT)); + + vnode_ref(zfsvfs->z_ctldir); + VN_RELE(zfsvfs->z_ctldir); + + dprintf("%s: done %p\n", __func__, zfsvfs->z_ctldir); + + return (0); +} + +/* + * Destroy the '.zfs' directory or remove a snapshot from + * zfs_snapshots_by_name. Only called when the filesystem is unmounted. + */ +void +zfsctl_destroy(zfsvfs_t *zfsvfs) +{ + if (zfsvfs->z_ctldir) { + if (VN_HOLD(zfsvfs->z_ctldir) == 0) { + vnode_rele(zfsvfs->z_ctldir); + /* Because tagged VSYSTEM, we manually call recycle */ + vnode_recycle(zfsvfs->z_ctldir); + VN_RELE(zfsvfs->z_ctldir); + } + zfsvfs->z_ctldir = NULL; + } +} + +/* + * Given a root znode, retrieve the associated .zfs directory. + * Add a hold to the vnode and return it. + */ +struct vnode * +zfsctl_root(znode_t *zp) +{ + ASSERT(zfs_has_ctldir(zp)); + VN_HOLD(ZTOZSB(zp)->z_ctldir); + return (ZTOZSB(zp)->z_ctldir); +} + + +struct vnode * +zfs_root_dotdot(struct vnode *vp) +{ + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = ZTOZSB(zp); + znode_t *rootzp = NULL; + struct vnode *retvp = NULL; + + dprintf("%s: for id %llu\n", __func__, zp->z_id); + + if (zp->z_id == ZFSCTL_INO_ROOT) + zfs_zget(zfsvfs, zfsvfs->z_root, &rootzp); + else if (zp->z_id == ZFSCTL_INO_SNAPDIR) + retvp = zfsctl_root(zp); + else + retvp = zfsctl_vnode_lookup(zfsvfs, ZFSCTL_INO_SNAPDIR, + "snapshot"); + + if (rootzp != NULL) + retvp = ZTOV(rootzp); + + dprintf("%s: for id %llu -> vp %p\n", __func__, zp->z_id, retvp); + return (retvp); +} + +/* + * Special case the handling of "..". + */ +int +zfsctl_root_lookup(struct vnode *dvp, char *name, struct vnode **vpp, + int flags, cred_t *cr, int *direntflags, struct componentname *realpnp) +{ + znode_t *dzp = VTOZ(dvp); + zfsvfs_t *zfsvfs = ZTOZSB(dzp); + int error = 0; + uint64_t id = ZFSCTL_INO_ROOT; + + dprintf("%s: '%s'\n", __func__, name); + + ZFS_ENTER(zfsvfs); + + if (strcmp(name, "..") == 0) { + *vpp = zfs_root_dotdot(dvp); + } else if (strcmp(name, ZFS_SNAPDIR_NAME) == 0) { + *vpp = zfsctl_vnode_lookup(zfsvfs, ZFSCTL_INO_SNAPDIR, + name); + } else { + error = dmu_snapshot_lookup(zfsvfs->z_os, name, &id); + if (error != 0) + goto out; + *vpp = zfsctl_vnode_lookup(zfsvfs, ZFSCTL_INO_SHARES - id, + name); + } + + if (*vpp == NULL) { + error = SET_ERROR(ENOENT); + } + +out: + ZFS_EXIT(zfsvfs); + + return (error); +} + +int +zfsctl_vnop_lookup(struct vnop_lookup_args *ap) +#if 0 + struct vnop_lookup_args { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + vfs_context_t a_context; + }; +#endif +{ + int direntflags = 0; + int error; + struct componentname *cnp = ap->a_cnp; + char *filename = NULL; + int filename_num_bytes = 0; + cred_t *cr = (cred_t *)vfs_context_ucred((ap)->a_context); + + /* + * Darwin uses namelen as an optimisation, for example it can be + * set to 5 for the string "alpha/beta" to look up "alpha". In this + * case we need to copy it out to null-terminate. + */ + if (cnp->cn_nameptr[cnp->cn_namelen] != 0) { + filename_num_bytes = cnp->cn_namelen + 1; + filename = (char *)kmem_alloc(filename_num_bytes, KM_SLEEP); + bcopy(cnp->cn_nameptr, filename, cnp->cn_namelen); + filename[cnp->cn_namelen] = '\0'; + } + + error = zfsctl_root_lookup(ap->a_dvp, + filename ? filename : cnp->cn_nameptr, + ap->a_vpp, /* flags */ 0, cr, &direntflags, NULL); + + /* If we are to create a directory, change error code for XNU */ + if ((error == ENOENT) && + (cnp->cn_flags & ISLASTCN)) { + if ((cnp->cn_nameiop == CREATE) || + (cnp->cn_nameiop == RENAME)) + error = EJUSTRETURN; + } + + if (filename != NULL) + kmem_free(filename, filename_num_bytes); + + return (error); +} + +/* Quick output function for readdir */ +#define DIRENT_RECLEN(namelen, ext) \ + ((ext) ? \ + ((sizeof (struct direntry) + (namelen) - (MAXPATHLEN-1) + 7) & ~7) \ + : \ + ((sizeof (struct dirent) - (NAME_MAX+1)) + (((namelen)+1 + 7) &~ 7))) + +static int zfsctl_dir_emit(const char *name, uint64_t id, enum vtype type, + struct vnop_readdir_args *ap, uint64_t **next) +{ + ZFS_UIO_INIT_XNU(uio, ap->a_uio); + boolean_t extended = (ap->a_flags & VNODE_READDIR_EXTENDED); + struct direntry *eodp; /* Extended */ + struct dirent *odp; /* Standard */ + int namelen; + void *buf; + int error = 0; + ushort_t reclen; + + dprintf("%s '%s'\n", __func__, name); + + namelen = strlen(name); + reclen = DIRENT_RECLEN(namelen, extended); + + if (reclen > zfs_uio_resid(uio)) + return (EINVAL); + + buf = kmem_zalloc(reclen, KM_SLEEP); + + if (extended) { + eodp = buf; + + /* + * NOTE: d_seekoff is the offset for the *next* entry - + * so poke in the previous struct with this id + */ + eodp->d_seekoff = zfs_uio_offset(uio) + 1; + + eodp->d_ino = id; + eodp->d_type = type; + + (void) bcopy(name, eodp->d_name, namelen + 1); + eodp->d_namlen = namelen; + eodp->d_reclen = reclen; + + } else { + odp = buf; + + odp->d_ino = id; + odp->d_type = type; + (void) bcopy(name, odp->d_name, namelen + 1); + odp->d_namlen = namelen; + odp->d_reclen = reclen; + + } + + /* Copyout this entry */ + error = zfs_uiomove(buf, (long)reclen, UIO_READ, uio); + + kmem_free(buf, reclen); + return (error); +} + +int +zfsctl_vnop_readdir_root(struct vnop_readdir_args *ap) +#if 0 + struct vnop_readdir_args { + struct vnode a_vp; + struct uio *a_uio; + int a_flags; + int *a_eofflag; + int *a_numdirent; + vfs_context_t a_context; + }; +#endif +{ + int error = 0; + uint64_t *next = NULL; + int entries = 0; + uint64_t offset; + ZFS_UIO_INIT_XNU(uio, ap->a_uio); + znode_t *zp = VTOZ(ap->a_vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + + dprintf("%s\n", __func__); + + ZFS_ENTER(zfsvfs); + + *ap->a_numdirent = 0; + + offset = zfs_uio_offset(uio); + + while (offset < 3 && error == 0) { + + switch (offset) { + case 0: /* "." */ + error = zfsctl_dir_emit(".", ZFSCTL_INO_ROOT, + DT_DIR, ap, &next); + break; + + case 1: /* ".." */ + error = zfsctl_dir_emit("..", 2, + DT_DIR, ap, &next); + break; + + case 2: + error = zfsctl_dir_emit(ZFS_SNAPDIR_NAME, + ZFSCTL_INO_SNAPDIR, DT_DIR, ap, &next); + break; + } + + if (error == ENOENT) { + dprintf("end of snapshots reached\n"); + break; + } + + if (error != 0) { + dprintf("emit error\n"); + break; + } + + entries++; + offset++; + zfs_uio_setoffset(uio, offset); + } + + zfs_uio_setoffset(uio, offset); + + /* Finished without error? Set EOF */ + if (offset >= 3 && error == 0) { + *ap->a_eofflag = 1; + dprintf("Setting eof\n"); + } + + *ap->a_numdirent = entries; + dprintf("Returning %d entries\n", entries); + + ZFS_EXIT(zfsvfs); + + return (error); +} + +int +zfsctl_vnop_readdir_snapdir(struct vnop_readdir_args *ap) +#if 0 + struct vnop_readdir_args { + struct vnode a_vp; + struct uio *a_uio; + int a_flags; + int *a_eofflag; + int *a_numdirent; + vfs_context_t a_context; + }; +#endif +{ + int error = 0; + uint64_t *next = NULL; + int entries = 0; + uint64_t offset; + ZFS_UIO_INIT_XNU(uio, ap->a_uio); + boolean_t case_conflict; + uint64_t id; + char snapname[MAXNAMELEN]; + znode_t *zp = VTOZ(ap->a_vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + + dprintf("%s\n", __func__); + + ZFS_ENTER(zfsvfs); + + *ap->a_numdirent = 0; + + offset = zfs_uio_offset(uio); + + while (error == 0) { + + switch (offset) { + case 0: /* "." */ + error = zfsctl_dir_emit(".", ZFSCTL_INO_SNAPDIR, + DT_DIR, ap, &next); + break; + + case 1: /* ".." */ + error = zfsctl_dir_emit("..", ZFSCTL_INO_ROOT, + DT_DIR, ap, &next); + break; + + default: + dsl_pool_config_enter(dmu_objset_pool(zfsvfs->z_os), + FTAG); + error = dmu_snapshot_list_next(zfsvfs->z_os, + MAXNAMELEN, snapname, &id, &offset, &case_conflict); + dsl_pool_config_exit(dmu_objset_pool(zfsvfs->z_os), + FTAG); + if (error) + break; + + error = zfsctl_dir_emit(snapname, + ZFSCTL_INO_SHARES - id, DT_DIR, ap, &next); + break; + } + + if (error != 0) { + dprintf("emit error\n"); + break; + } + + entries++; + offset++; + zfs_uio_setoffset(uio, offset); + } + + zfs_uio_setoffset(uio, offset); + + /* Finished without error? Set EOF */ + if (error == ENOENT) { + *ap->a_eofflag = 1; + dprintf("Setting eof\n"); + error = 0; + } + + *ap->a_numdirent = entries; + dprintf("Returning %d entries\n", entries); + + ZFS_EXIT(zfsvfs); + + return (error); +} + + +/* We need to spit out a valid "." ".." entries for mount to work */ +int +zfsctl_vnop_readdir_snapdirs(struct vnop_readdir_args *ap) +#if 0 + struct vnop_readdir_args { + struct vnode a_vp; + struct uio *a_uio; + int a_flags; + int *a_eofflag; + int *a_numdirent; + vfs_context_t a_context; + }; +#endif +{ + int error = 0; + uint64_t *next = NULL; + int entries = 0; + uint64_t offset; + ZFS_UIO_INIT_XNU(uio, ap->a_uio); + znode_t *zp = VTOZ(ap->a_vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + + ZFS_ENTER(zfsvfs); + + *ap->a_numdirent = 0; + + offset = zfs_uio_offset(uio); + + dprintf("%s: for id %llu: offset %llu\n", __func__, + zp->z_id, offset); + + while (error == 0) { + + switch (offset) { + case 0: /* "." */ + error = zfsctl_dir_emit(".", ZFSCTL_INO_SNAPDIR, + DT_DIR, ap, &next); + break; + + case 1: /* ".." */ + error = zfsctl_dir_emit("..", ZFSCTL_INO_ROOT, + DT_DIR, ap, &next); + break; + + default: + error = ENOENT; + break; + } + + if (error != 0) { + dprintf("emit error\n"); + break; + } + + entries++; + offset++; + zfs_uio_setoffset(uio, offset); + } + + zfs_uio_setoffset(uio, offset); + + /* Finished without error? Set EOF */ + if (error == ENOENT) { + *ap->a_eofflag = 1; + dprintf("Setting eof\n"); + error = 0; + } + + *ap->a_numdirent = entries; + dprintf("Returning %d entries\n", entries); + + ZFS_EXIT(zfsvfs); + + return (error); +} + +int +zfsctl_vnop_readdir(struct vnop_readdir_args *ap) +#if 0 + struct vnop_readdir_args { + struct vnode a_vp; + struct uio *a_uio; + int a_flags; + int *a_eofflag; + int *a_numdirent; + vfs_context_t a_context; + }; +#endif +{ + znode_t *zp = VTOZ(ap->a_vp); + + dprintf("%s\n", __func__); + + /* Which directory are we to output? */ + switch (zp->z_id) { + case ZFSCTL_INO_ROOT: + return (zfsctl_vnop_readdir_root(ap)); + case ZFSCTL_INO_SNAPDIR: + return (zfsctl_vnop_readdir_snapdir(ap)); + default: + return (zfsctl_vnop_readdir_snapdirs(ap)); + } + return (EINVAL); +} + +int +zfsctl_vnop_getattr(struct vnop_getattr_args *ap) +#if 0 + struct vnop_getattr_args { + struct vnode *a_vp; + struct vnode_vattr *a_vap; + vfs_context_t a_context; + }; +#endif +{ + vattr_t *vap = ap->a_vap; + struct vnode *vp = ap->a_vp; + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + timestruc_t now; + + ZFS_ENTER(zfsvfs); + + gethrestime(&now); + + if (VATTR_IS_ACTIVE(vap, va_rdev)) + VATTR_RETURN(vap, va_rdev, zfsvfs->z_rdev); + if (VATTR_IS_ACTIVE(vap, va_nlink)) + VATTR_RETURN(vap, va_nlink, + vnode_isdir(vp) ? zp->z_size : zp->z_links); + if (VATTR_IS_ACTIVE(vap, va_total_size)) + VATTR_RETURN(vap, va_total_size, 512); + if (VATTR_IS_ACTIVE(vap, va_total_alloc)) + VATTR_RETURN(vap, va_total_alloc, 512); + if (VATTR_IS_ACTIVE(vap, va_data_size)) + VATTR_RETURN(vap, va_data_size, 0); + if (VATTR_IS_ACTIVE(vap, va_data_alloc)) + VATTR_RETURN(vap, va_data_alloc, 0); + if (VATTR_IS_ACTIVE(vap, va_iosize)) + VATTR_RETURN(vap, va_iosize, 512); + if (VATTR_IS_ACTIVE(vap, va_uid)) + VATTR_RETURN(vap, va_uid, 0); + if (VATTR_IS_ACTIVE(vap, va_gid)) + VATTR_RETURN(vap, va_gid, 0); + if (VATTR_IS_ACTIVE(vap, va_mode)) + VATTR_RETURN(vap, va_mode, S_IFDIR | + S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + if (VATTR_IS_ACTIVE(vap, va_flags)) + VATTR_RETURN(vap, va_flags, zfs_getbsdflags(zp)); + + if (VATTR_IS_ACTIVE(vap, va_acl)) { + VATTR_RETURN(vap, va_uuuid, kauth_null_guid); + VATTR_RETURN(vap, va_guuid, kauth_null_guid); + VATTR_RETURN(vap, va_acl, NULL); + } + + // crtime, atime, mtime, ctime, btime + uint64_t timez[2]; + timez[0] = zfsvfs->z_mount_time; + timez[1] = 0; + + if (VATTR_IS_ACTIVE(vap, va_create_time)) { + ZFS_TIME_DECODE(&vap->va_create_time, timez); + VATTR_SET_SUPPORTED(vap, va_create_time); + } + if (VATTR_IS_ACTIVE(vap, va_access_time)) { + ZFS_TIME_DECODE(&vap->va_access_time, timez); + VATTR_SET_SUPPORTED(vap, va_access_time); + } + if (VATTR_IS_ACTIVE(vap, va_modify_time)) { + ZFS_TIME_DECODE(&vap->va_modify_time, timez); + VATTR_SET_SUPPORTED(vap, va_modify_time); + } + if (VATTR_IS_ACTIVE(vap, va_change_time)) { + ZFS_TIME_DECODE(&vap->va_change_time, timez); + VATTR_SET_SUPPORTED(vap, va_change_time); + } + if (VATTR_IS_ACTIVE(vap, va_backup_time)) { + ZFS_TIME_DECODE(&vap->va_backup_time, timez); + VATTR_SET_SUPPORTED(vap, va_backup_time); + } + if (VATTR_IS_ACTIVE(vap, va_addedtime)) { + ZFS_TIME_DECODE(&vap->va_addedtime, timez); + VATTR_SET_SUPPORTED(vap, va_addedtime); + } + + if (VATTR_IS_ACTIVE(vap, va_fileid)) + VATTR_RETURN(vap, va_fileid, zp->z_id); + if (VATTR_IS_ACTIVE(vap, va_linkid)) + VATTR_RETURN(vap, va_linkid, zp->z_id); + if (VATTR_IS_ACTIVE(vap, va_parentid)) { + switch (zp->z_id) { + case ZFSCTL_INO_ROOT: + // ".zfs" parent is mount, 2 on osx + VATTR_RETURN(vap, va_linkid, 2); + break; + case ZFSCTL_INO_SNAPDIR: + // ".zfs/snapshot" parent is ".zfs" + VATTR_RETURN(vap, va_linkid, ZFSCTL_INO_ROOT); + break; + default: + // ".zfs/snapshot/$name" parent ".zfs/snapshot" + VATTR_RETURN(vap, va_linkid, + ZFSCTL_INO_SNAPDIR); + break; + } + } + if (VATTR_IS_ACTIVE(vap, va_fsid)) + VATTR_RETURN(vap, va_fsid, zfsvfs->z_rdev); + + if (VATTR_IS_ACTIVE(vap, va_filerev)) + VATTR_RETURN(vap, va_filerev, 0); + if (VATTR_IS_ACTIVE(vap, va_gen)) + VATTR_RETURN(vap, va_gen, zp->z_gen); + if (VATTR_IS_ACTIVE(vap, va_type)) + VATTR_RETURN(vap, va_type, vnode_vtype(ZTOV(zp))); + if (VATTR_IS_ACTIVE(vap, va_name)) { + strlcpy(vap->va_name, zp->z_name_cache, MAXPATHLEN); + VATTR_SET_SUPPORTED(vap, va_name); + } + + /* Don't include '.' and '..' in the number of entries */ + if (VATTR_IS_ACTIVE(vap, va_nchildren) && vnode_isdir(vp)) { + VATTR_RETURN(vap, va_nchildren, + zp->z_links > 3 ? zp->z_links-2 : 1); + } + if (VATTR_IS_ACTIVE(vap, va_dirlinkcount) && vnode_isdir(vp)) + VATTR_RETURN(vap, va_dirlinkcount, 1); + +#ifdef VNODE_ATTR_va_fsid64 + if (VATTR_IS_ACTIVE(vap, va_fsid64)) { + vap->va_fsid64.val[0] = + vfs_statfs(zfsvfs->z_vfs)->f_fsid.val[0]; + vap->va_fsid64.val[1] = vfs_typenum(zfsvfs->z_vfs); + VATTR_SET_SUPPORTED(vap, va_fsid64); + } +#endif + + ZFS_EXIT(zfsvfs); + + return (0); +} + +int +zfsctl_vnop_access(struct vnop_access_args *ap) +{ + int accmode = ap->a_action; + dprintf("zfsctl_access\n"); + + if (accmode & VWRITE) + return (EACCES); + return (0); +} + +int +zfsctl_vnop_open(struct vnop_open_args *ap) +{ + int flags = ap->a_mode; + + if (flags & FWRITE) + return (EACCES); + + return (zfsctl_snapshot_mount(ap->a_vp, 0)); +} + +int +zfsctl_vnop_close(struct vnop_close_args *ap) +{ + dprintf("%s\n", __func__); + return (0); +} + +int +zfsctl_vnop_inactive(struct vnop_inactive_args *ap) +{ + dprintf("%s\n", __func__); + return (0); +} + +int +zfsctl_vnop_reclaim(struct vnop_reclaim_args *ap) +{ + struct vnode *vp = ap->a_vp; + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + + dprintf("%s vp %p\n", __func__, vp); + vnode_removefsref(vp); /* ADDREF from vnode_create */ + vnode_clearfsnode(vp); /* vp->v_data = NULL */ + + mutex_enter(&zfsvfs->z_znodes_lock); + if (list_link_active(&zp->z_link_node)) { + list_remove(&zfsvfs->z_all_znodes, zp); + } + mutex_exit(&zfsvfs->z_znodes_lock); + + zp->z_vnode = NULL; + kmem_cache_free(znode_cache, zp); + + return (0); +} + +/* + * Construct a full dataset name in full_name: "pool/dataset@snap_name" + */ +static int +zfsctl_snapshot_name(zfsvfs_t *zfsvfs, const char *snap_name, int len, + char *full_name) +{ + objset_t *os = zfsvfs->z_os; + + if (zfs_component_namecheck(snap_name, NULL, NULL) != 0) + return (SET_ERROR(EILSEQ)); + + dmu_objset_name(os, full_name); + if ((strlen(full_name) + 1 + strlen(snap_name)) >= len) + return (SET_ERROR(ENAMETOOLONG)); + + (void) strcat(full_name, "@"); + (void) strcat(full_name, snap_name); + + return (0); +} + +int +zfsctl_snapshot_mount(struct vnode *vp, int flags) +{ + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + int ret = 0; + /* + * If we are here for a snapdirs directory, attempt to get zed + * to mount the snapshot for the user. If successful, forward the + * vnop_open() to them (ourselves). + * Use a timeout in case zed is not running. + */ + + if (zfs_auto_snapshot == 0) + return (0); + + ZFS_ENTER(zfsvfs); + if (((zp->z_id >= zfsvfs->z_ctldir_startid) && + (zp->z_id <= ZFSCTL_INO_SNAPDIRS))) { + hrtime_t now; + now = gethrtime(); + + /* + * If z_snap_mount_time is set, check if it is old enough to + * retry, if so, set z_snap_mount_time to zero. + */ + if (now - zp->z_snap_mount_time > SEC2NSEC(60)) + atomic_cas_64((uint64_t *)&zp->z_snap_mount_time, + (uint64_t)zp->z_snap_mount_time, + 0ULL); + + /* + * Attempt mount, make sure only to issue one request, by + * attempting to CAS in current time in place of zero. + */ + if (atomic_cas_64((uint64_t *)&zp->z_snap_mount_time, 0ULL, + (uint64_t)now) == 0ULL) { + char full_name[ZFS_MAX_DATASET_NAME_LEN]; + + /* First! */ + ret = zfsctl_snapshot_name(zfsvfs, zp->z_name_cache, + ZFS_MAX_DATASET_NAME_LEN, full_name); + + if (ret == 0) { + zfsctl_mounts_waiting_t *zcm; + + /* Create condvar to wait for mount to happen */ + + zcm = kmem_zalloc( + sizeof (zfsctl_mounts_waiting_t), KM_SLEEP); + mutex_init(&zcm->zcm_lock, NULL, MUTEX_DEFAULT, + NULL); + cv_init(&zcm->zcm_cv, NULL, CV_DEFAULT, NULL); + strlcpy(zcm->zcm_name, full_name, + sizeof (zcm->zcm_name)); + + dprintf("%s: requesting mount for '%s'\n", + __func__, full_name); + + mutex_enter(&zfsctl_mounts_lock); + list_insert_tail(&zfsctl_mounts_list, zcm); + mutex_exit(&zfsctl_mounts_lock); + + mutex_enter(&zcm->zcm_lock); + zfs_ereport_snapshot_post( + FM_EREPORT_ZFS_SNAPSHOT_MOUNT, + dmu_objset_spa(zfsvfs->z_os), full_name); + + /* Now we wait hoping zed comes back to us */ + ret = cv_timedwait(&zcm->zcm_cv, &zcm->zcm_lock, + ddi_get_lbolt() + SEC_TO_TICK(6)); + + dprintf("%s: finished waiting %d\n", + __func__, ret); + + mutex_exit(&zcm->zcm_lock); + + mutex_enter(&zfsctl_mounts_lock); + list_remove(&zfsctl_mounts_list, zcm); + mutex_exit(&zfsctl_mounts_lock); + + mutex_destroy(&zcm->zcm_lock); + cv_destroy(&zcm->zcm_cv); + + kmem_free(zcm, + sizeof (zfsctl_mounts_waiting_t)); + + /* + * If we mounted, make it re-open it so + * the process that issued the access will + * see the mounted content + */ + if (ret >= 0) { + /* Remove the cache entry */ + cache_purge(vp); + cache_purge_negatives(vp); + ret = ERESTART; + } + } + } + } + + ZFS_EXIT(zfsvfs); + + return (ret); +} + +/* Called whenever zfs_vfs_mount() is called with a snapshot */ +void +zfsctl_mount_signal(char *osname, boolean_t mounting) +{ + zfsctl_mounts_waiting_t *zcm; + + dprintf("%s: looking for snapshot '%s'\n", __func__, osname); + + mutex_enter(&zfsctl_mounts_lock); + for (zcm = list_head(&zfsctl_mounts_list); + zcm; + zcm = list_next(&zfsctl_mounts_list, zcm)) { + if (strncmp(zcm->zcm_name, osname, sizeof (zcm->zcm_name)) == 0) + break; + } + mutex_exit(&zfsctl_mounts_lock); + + /* Is there someone to wake up? */ + if (zcm != NULL) { + mutex_enter(&zcm->zcm_lock); + cv_signal(&zcm->zcm_cv); + mutex_exit(&zcm->zcm_lock); + dprintf("%s: mount waiter found and signalled\n", __func__); + } + + zfsctl_unmount_delay_t *zcu; + + /* Add or remove mount to/from list of active mounts */ + + if (mounting) { + /* Add active mounts to the list */ + zcu = kmem_alloc(sizeof (zfsctl_unmount_delay_t), KM_SLEEP); + zcu->se_name = kmem_strdup(osname); + zcu->se_time = gethrestime_sec(); + list_link_init(&zcu->se_nodelink); + + mutex_enter(&zfsctl_unmount_list_lock); + list_insert_tail(&zfsctl_unmount_list, zcu); + mutex_exit(&zfsctl_unmount_list_lock); + + } else { + /* Unmounting */ + mutex_enter(&zfsctl_unmount_list_lock); + for (zcu = list_head(&zfsctl_unmount_list); + zcu != NULL; + zcu = list_next(&zfsctl_unmount_list, zcu)) { + if (strcmp(osname, zcu->se_name) == 0) { + list_remove(&zfsctl_unmount_list, zcu); + kmem_strfree(zcu->se_name); + kmem_free(zcu, sizeof (zfsctl_unmount_delay_t)); + break; + } + } + mutex_exit(&zfsctl_unmount_list_lock); + } +} + +int +zfsctl_snapshot_unmount_node(struct vnode *vp, const char *full_name, + int flags) +{ + znode_t *zp = VTOZ(vp); + + dprintf("%s\n", __func__); + + if (zp == NULL) + return (ENOENT); + + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + int ret = ENOENT; + /* + * If we are here for a snapdirs directory, attempt to get zed + * to mount the snapshot for the user. If successful, forward the + * vnop_open() to them (ourselves). + * Use a timeout in case zed is not running. + */ + + ZFS_ENTER(zfsvfs); + + if (zp->z_id == zfsvfs->z_root) { + hrtime_t now; + now = gethrtime(); + + /* + * If z_snap_mount_time is set, check if it is old enough to + * retry, if so, set z_snap_mount_time to zero. + */ + if (now - zp->z_snap_mount_time > SEC2NSEC(60)) + atomic_cas_64((uint64_t *)&zp->z_snap_mount_time, + (uint64_t)zp->z_snap_mount_time, + 0ULL); + + /* + * Attempt unmount, make sure only to issue one request, by + * attempting to CAS in current time in place of zero. + */ + if (atomic_cas_64((uint64_t *)&zp->z_snap_mount_time, 0ULL, + (uint64_t)now) == 0ULL) { + + ret = 0; + + /* First! */ + + if (ret == 0) { + zfsctl_mounts_waiting_t *zcm; + + /* Create condvar to wait for mount to happen */ + + zcm = kmem_zalloc( + sizeof (zfsctl_mounts_waiting_t), KM_SLEEP); + mutex_init(&zcm->zcm_lock, NULL, MUTEX_DEFAULT, + NULL); + cv_init(&zcm->zcm_cv, NULL, CV_DEFAULT, NULL); + strlcpy(zcm->zcm_name, full_name, + sizeof (zcm->zcm_name)); + + dprintf("%s: requesting unmount for '%s'\n", + __func__, full_name); + + mutex_enter(&zfsctl_mounts_lock); + list_insert_tail(&zfsctl_mounts_list, zcm); + mutex_exit(&zfsctl_mounts_lock); + + mutex_enter(&zcm->zcm_lock); + zfs_ereport_snapshot_post( + FM_EREPORT_ZFS_SNAPSHOT_UNMOUNT, + dmu_objset_spa(zfsvfs->z_os), full_name); + + /* Now we wait hoping zed comes back to us */ + ret = cv_timedwait(&zcm->zcm_cv, &zcm->zcm_lock, + ddi_get_lbolt() + (hz * 3)); + + dprintf("%s: finished waiting %d\n", + __func__, ret); + + mutex_exit(&zcm->zcm_lock); + + mutex_enter(&zfsctl_mounts_lock); + list_remove(&zfsctl_mounts_list, zcm); + mutex_exit(&zfsctl_mounts_lock); + + kmem_free(zcm, + sizeof (zfsctl_mounts_waiting_t)); + + /* Allow mounts to happen immediately */ + zp->z_snap_mount_time = 0; + + /* + * If we unmounted, alert caller + */ + if (ret >= 0) + ret = 0; + + } + } + } + + ZFS_EXIT(zfsvfs); + + return (ret); +} + +int +zfsctl_snapshot_unmount(const char *snapname, int flags) +{ + znode_t *rootzp; + zfsvfs_t *zfsvfs; + + dprintf("%s\n", __func__); + + if (strchr(snapname, '@') == NULL) + return (0); + + int err = getzfsvfs(snapname, &zfsvfs); + if (err != 0) { + ASSERT3P(zfsvfs, ==, NULL); + return (0); + } + ASSERT(!dsl_pool_config_held(dmu_objset_pool(zfsvfs->z_os))); + + err = zfs_zget(zfsvfs, zfsvfs->z_root, &rootzp); + if (err == 0) { + zfsctl_snapshot_unmount_node(ZTOV(rootzp), snapname, flags); + VN_RELE(ZTOV(rootzp)); + } + + vfs_unbusy(zfsvfs->z_vfs); + return (0); +} + +int +zfsctl_vnop_mkdir(struct vnop_mkdir_args *ap) +#if 0 + struct vnop_mkdir_args { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vnode_vattr *a_vap; + vfs_context_t a_context; + }; +#endif +{ + cred_t *cr = (cred_t *)vfs_context_ucred((ap)->a_context); + znode_t *dzp = VTOZ(ap->a_dvp); + zfsvfs_t *zfsvfs = dzp->z_zfsvfs; + char *dsname; + int error; + + if (zfs_admin_snapshot == 0) + return (SET_ERROR(EACCES)); + + dsname = kmem_alloc(ZFS_MAX_DATASET_NAME_LEN, KM_SLEEP); + + if (zfs_component_namecheck(ap->a_cnp->cn_nameptr, NULL, NULL) != 0) { + error = SET_ERROR(EILSEQ); + goto out; + } + + dmu_objset_name(zfsvfs->z_os, dsname); + + error = zfs_secpolicy_snapshot_perms(dsname, cr); + if (error != 0) + goto out; + + if (error == 0) { + error = dmu_objset_snapshot_one(dsname, ap->a_cnp->cn_nameptr); + if (error != 0) + goto out; + + error = zfsctl_root_lookup(ap->a_dvp, ap->a_cnp->cn_nameptr, + ap->a_vpp, 0, cr, NULL, NULL); + } + +out: + kmem_free(dsname, ZFS_MAX_DATASET_NAME_LEN); + + return (error); +} + +int +zfsctl_vnop_rmdir(struct vnop_rmdir_args *ap) +#if 0 + struct vnop_rmdir_args { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + vfs_context_t a_context; + }; +#endif +{ + cred_t *cr = (cred_t *)vfs_context_ucred((ap)->a_context); + znode_t *dzp = VTOZ(ap->a_dvp); + zfsvfs_t *zfsvfs = dzp->z_zfsvfs; + char *snapname, *real; + char *name = ap->a_cnp->cn_nameptr; + int error; + + dprintf("%s: '%s'\n", __func__, name); + + if (zfs_admin_snapshot == 0) + return (SET_ERROR(EACCES)); + + ZFS_ENTER(zfsvfs); + + snapname = kmem_alloc(ZFS_MAX_DATASET_NAME_LEN, KM_SLEEP); + real = kmem_alloc(ZFS_MAX_DATASET_NAME_LEN, KM_SLEEP); + + if (zfsvfs->z_case == ZFS_CASE_INSENSITIVE) { + error = dmu_snapshot_realname(zfsvfs->z_os, name, + real, ZFS_MAX_DATASET_NAME_LEN, NULL); + if (error == 0) { + name = real; + } else if (error != ENOTSUP) { + goto out; + } + } + + error = zfsctl_snapshot_name(zfsvfs, name, + ZFS_MAX_DATASET_NAME_LEN, snapname); + if (error == 0) + error = zfs_secpolicy_destroy_perms(snapname, cr); + if (error != 0) + goto out; + + error = zfsctl_snapshot_unmount_node(ap->a_vp, snapname, MNT_FORCE); + if ((error == 0) || (error == ENOENT)) { + error = dsl_destroy_snapshot(snapname, B_FALSE); + + /* Destroy the vnode */ + if (ap->a_vp != NULL) { + dprintf("%s: releasing vp\n", __func__); + vnode_recycle(ap->a_vp); + } + } + +out: + kmem_free(snapname, ZFS_MAX_DATASET_NAME_LEN); + kmem_free(real, ZFS_MAX_DATASET_NAME_LEN); + + ZFS_EXIT(zfsvfs); + return (error); +} + +static void +zfsctl_unmount_thread(void *notused) +{ + callb_cpr_t cpr; + zfsctl_unmount_delay_t *zcu; + time_t now; + CALLB_CPR_INIT(&cpr, &zfsctl_unmount_lock, callb_generic_cpr, FTAG); + + dprintf("%s is alive\n", __func__); + + mutex_enter(&zfsctl_unmount_lock); + while (!zfsctl_unmount_thread_exit) { + + CALLB_CPR_SAFE_BEGIN(&cpr); + (void) cv_timedwait(&zfsctl_unmount_cv, + &zfsctl_unmount_lock, ddi_get_lbolt() + (hz<<6)); + CALLB_CPR_SAFE_END(&cpr, &zfsctl_unmount_lock); + + if (!zfsctl_unmount_thread_exit) { + /* + * Loop all active mounts, if any are older + * than ZFSCTL_EXPIRE_SNAPSHOT, then we update + * their timestamp and attempt unmount. + */ + now = gethrestime_sec(); + mutex_enter(&zfsctl_unmount_list_lock); + for (zcu = list_head(&zfsctl_unmount_list); + zcu != NULL; + zcu = list_next(&zfsctl_unmount_list, zcu)) { + if ((now > zcu->se_time) && + ((now - zcu->se_time) > + zfs_expire_snapshot)) { + zcu->se_time = now; + zfsctl_snapshot_unmount(zcu->se_name, + 0); + } + } + mutex_exit(&zfsctl_unmount_list_lock); + } + } + + zfsctl_unmount_thread_exit = FALSE; + cv_broadcast(&zfsctl_unmount_cv); + CALLB_CPR_EXIT(&cpr); + dprintf("ZFS: zfsctl_unmount thread exit\n"); + thread_exit(); +} + +/* + * Initialize the various pieces we'll need to create and manipulate .zfs + * directories. Currently this is unused but available. + */ +void +zfsctl_init(void) +{ + mutex_init(&zfsctl_mounts_lock, NULL, MUTEX_DEFAULT, NULL); + list_create(&zfsctl_mounts_list, sizeof (zfsctl_mounts_waiting_t), + offsetof(zfsctl_mounts_waiting_t, zcm_node)); + + mutex_init(&zfsctl_unmount_list_lock, NULL, MUTEX_DEFAULT, NULL); + list_create(&zfsctl_unmount_list, sizeof (zfsctl_unmount_delay_t), + offsetof(zfsctl_unmount_delay_t, se_nodelink)); + + mutex_init(&zfsctl_unmount_lock, NULL, MUTEX_DEFAULT, NULL); + cv_init(&zfsctl_unmount_cv, NULL, CV_DEFAULT, NULL); + zfsctl_unmount_thread_exit = FALSE; + + (void) thread_create(NULL, 0, zfsctl_unmount_thread, NULL, 0, &p0, + TS_RUN, minclsyspri); +} + +/* + * Cleanup the various pieces we needed for .zfs directories. In particular + * ensure the expiry timer is canceled safely. + */ +void +zfsctl_fini(void) +{ + mutex_destroy(&zfsctl_mounts_lock); + list_destroy(&zfsctl_mounts_list); + + mutex_destroy(&zfsctl_unmount_list_lock); + list_destroy(&zfsctl_unmount_list); + + mutex_enter(&zfsctl_unmount_lock); + zfsctl_unmount_thread_exit = TRUE; + while (zfsctl_unmount_thread_exit) { + cv_signal(&zfsctl_unmount_cv); + cv_wait(&zfsctl_unmount_cv, &zfsctl_unmount_lock); + } + mutex_exit(&zfsctl_unmount_lock); + + mutex_destroy(&zfsctl_unmount_lock); + cv_destroy(&zfsctl_unmount_cv); +} + +module_param(zfs_admin_snapshot, int, 0644); +MODULE_PARM_DESC(zfs_admin_snapshot, "Enable mkdir/rmdir/mv in .zfs/snapshot"); + +module_param(zfs_expire_snapshot, int, 0644); +MODULE_PARM_DESC(zfs_expire_snapshot, "Seconds to expire .zfs/snapshot"); diff --git a/module/os/macos/zfs/zfs_debug.c b/module/os/macos/zfs/zfs_debug.c new file mode 100644 index 000000000000..785be09894b7 --- /dev/null +++ b/module/os/macos/zfs/zfs_debug.c @@ -0,0 +1,264 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2014 by Delphix. All rights reserved. + */ + +#include + +typedef struct zfs_dbgmsg { + list_node_t zdm_node; + time_t zdm_timestamp; + int zdm_size; + char zdm_msg[1]; /* variable length allocation */ +} zfs_dbgmsg_t; + +list_t zfs_dbgmsgs; +int zfs_dbgmsg_size; +kmutex_t zfs_dbgmsgs_lock; +int zfs_dbgmsg_maxsize = 4<<20; /* 4MB */ +kstat_t *zfs_dbgmsg_kstat; + +int zfs_dbgmsg_enable = 1; + +static int +zfs_dbgmsg_headers(char *buf, size_t size) +{ + (void) snprintf(buf, size, "%-12s %-8s\n", "timestamp", "message"); + + return (0); +} + +static int +zfs_dbgmsg_data(char *buf, size_t size, void *data) +{ + zfs_dbgmsg_t *zdm = (zfs_dbgmsg_t *)data; + + (void) snprintf(buf, size, "%-12llu %-s\n", + (u_longlong_t)zdm->zdm_timestamp, zdm->zdm_msg); + + return (0); +} + +static void * +zfs_dbgmsg_addr(kstat_t *ksp, loff_t n) +{ + zfs_dbgmsg_t *zdm = (zfs_dbgmsg_t *)ksp->ks_private; + + ASSERT(MUTEX_HELD(&zfs_dbgmsgs_lock)); + + if (n == 0) + ksp->ks_private = list_head(&zfs_dbgmsgs); + else if (zdm) + ksp->ks_private = list_next(&zfs_dbgmsgs, zdm); + + return (ksp->ks_private); +} + +static void +zfs_dbgmsg_purge(int max_size) +{ + zfs_dbgmsg_t *zdm; + int size; + + ASSERT(MUTEX_HELD(&zfs_dbgmsgs_lock)); + + while (zfs_dbgmsg_size > max_size) { + zdm = list_remove_head(&zfs_dbgmsgs); + if (zdm == NULL) + return; + + size = zdm->zdm_size; + kmem_free(zdm, size); + zfs_dbgmsg_size -= size; + } +} + +static int +zfs_dbgmsg_update(kstat_t *ksp, int rw) +{ + if (rw == KSTAT_WRITE) + zfs_dbgmsg_purge(0); + + return (0); +} + +/* + * Debug logging is enabled by default for production kernel builds. + * The overhead for this is negligible and the logs can be valuable when + * debugging. For non-production user space builds all debugging except + * logging is enabled since performance is no longer a concern. + */ +void +zfs_dbgmsg_init(void) +{ + list_create(&zfs_dbgmsgs, sizeof (zfs_dbgmsg_t), + offsetof(zfs_dbgmsg_t, zdm_node)); + mutex_init(&zfs_dbgmsgs_lock, NULL, MUTEX_DEFAULT, NULL); + + zfs_dbgmsg_kstat = kstat_create("zfs", 0, "dbgmsg", "misc", + KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL); + if (zfs_dbgmsg_kstat) { + zfs_dbgmsg_kstat->ks_lock = &zfs_dbgmsgs_lock; + zfs_dbgmsg_kstat->ks_ndata = UINT32_MAX; + zfs_dbgmsg_kstat->ks_private = NULL; + zfs_dbgmsg_kstat->ks_update = zfs_dbgmsg_update; + kstat_set_raw_ops(zfs_dbgmsg_kstat, zfs_dbgmsg_headers, + zfs_dbgmsg_data, zfs_dbgmsg_addr); + kstat_install(zfs_dbgmsg_kstat); + } +} + +void +zfs_dbgmsg_fini(void) +{ + zfs_dbgmsg_t *zdm; + + if (zfs_dbgmsg_kstat) + kstat_delete(zfs_dbgmsg_kstat); + + while ((zdm = list_remove_head(&zfs_dbgmsgs)) != NULL) { + int size = sizeof (zfs_dbgmsg_t) + strlen(zdm->zdm_msg); + kmem_free(zdm, size); + zfs_dbgmsg_size -= size; + } + mutex_destroy(&zfs_dbgmsgs_lock); + ASSERT0(zfs_dbgmsg_size); +} + +void +__set_error(const char *file, const char *func, int line, int err) +{ + /* + * To enable this: + * + * $ echo 512 >/sys/module/zfs/parameters/zfs_flags + */ + if (zfs_flags & ZFS_DEBUG_SET_ERROR) + __dprintf(B_FALSE, file, func, line, "error %lu", err); +} + +/* + * Print these messages by running: + * echo ::zfs_dbgmsg | mdb -k + * + * Monitor these messages by running: + * dtrace -qn 'zfs-dbgmsg{printf("%s\n", stringof(arg0))}' + * + * When used with libzpool, monitor with: + * dtrace -qn 'zfs$pid::zfs_dbgmsg:probe1{printf("%s\n", copyinstr(arg1))}' + */ + +/* + * MacOS X's dtrace doesn't handle the PROBEs, so + * we have a utility function that we can watch with + * sudo dtrace -qn '__zfs_dbgmsg:entry{printf("%s\n", stringof(arg0));}' + */ +noinline void +__zfs_dbgmsg(char *buf) +{ + int size = sizeof (zfs_dbgmsg_t) + strlen(buf); + zfs_dbgmsg_t *zdm = kmem_zalloc(size, KM_SLEEP); + zdm->zdm_size = size; + zdm->zdm_timestamp = gethrestime_sec(); + strlcpy(zdm->zdm_msg, buf, size); + + mutex_enter(&zfs_dbgmsgs_lock); + list_insert_tail(&zfs_dbgmsgs, zdm); + zfs_dbgmsg_size += size; + zfs_dbgmsg_purge(MAX(zfs_dbgmsg_maxsize, 0)); + mutex_exit(&zfs_dbgmsgs_lock); +} + +void +__dprintf(boolean_t dprint, const char *file, const char *func, + int line, const char *fmt, ...) +{ + int size, i; + va_list adx; + char *buf, *nl; + char *prefix = (dprint) ? "dprintf: " : ""; + const char *newfile; + + /* + * Get rid of annoying prefix to filename. + */ + newfile = strrchr(file, '/'); + if (newfile != NULL) { + newfile = newfile + 1; /* Get rid of leading / */ + } else { + newfile = file; + } + + va_start(adx, fmt); + size = vsnprintf(NULL, 0, fmt, adx); + va_end(adx); + + size += snprintf(NULL, 0, "%s%s:%d:%s(): ", prefix, newfile, line, + func); + + size++; /* null byte in the "buf" string */ + + /* + * There is one byte of string in sizeof (zfs_dbgmsg_t), used + * for the terminating null. + */ + buf = kmem_alloc(size, KM_SLEEP); + int roger = 0; + + va_start(adx, fmt); + i = snprintf(buf, size + 1, "%s%s:%d:%s(): ", + prefix, newfile, line, func); + roger = vsnprintf(buf + i, size -i + 1, fmt, adx); + va_end(adx); + + /* + * Get rid of trailing newline for dprintf logs. + */ + if (dprint && buf[0] != '\0') { + nl = &buf[strlen(buf) - 1]; + if (*nl == '\n') + *nl = '\0'; + } + + DTRACE_PROBE1(zfs__dbgmsg, char *, zdm->zdm_msg); + + __zfs_dbgmsg(buf); + + /* Also emit string to log/console */ + printf("%s\n", buf); + + kmem_free(buf, size); +} + +void +zfs_dbgmsg_print(const char *tag) +{ + zfs_dbgmsg_t *zdm; + + (void) printf("ZFS_DBGMSG(%s):\n", tag); + mutex_enter(&zfs_dbgmsgs_lock); + for (zdm = list_head(&zfs_dbgmsgs); zdm; + zdm = list_next(&zfs_dbgmsgs, zdm)) + (void) printf("%s\n", zdm->zdm_msg); + mutex_exit(&zfs_dbgmsgs_lock); +} diff --git a/module/os/macos/zfs/zfs_dir.c b/module/os/macos/zfs/zfs_dir.c new file mode 100644 index 000000000000..57baee5003d4 --- /dev/null +++ b/module/os/macos/zfs/zfs_dir.c @@ -0,0 +1,1218 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2016 by Delphix. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * zfs_match_find() is used by zfs_dirent_lock() to perform zap lookups + * of names after deciding which is the appropriate lookup interface. + */ +static int +zfs_match_find(zfsvfs_t *zfsvfs, znode_t *dzp, char *name, matchtype_t mt, + boolean_t update, int *deflags, struct componentname *rpnp, uint64_t *zoid) +{ + boolean_t conflict = B_FALSE; + int error; + + if (zfsvfs->z_norm) { + size_t bufsz = 0; + char *buf = NULL; + + if (rpnp) { + buf = rpnp->cn_nameptr; + bufsz = rpnp->cn_namelen; + } + + /* + * In the non-mixed case we only expect there would ever + * be one match, but we need to use the normalizing lookup. + */ + error = zap_lookup_norm(zfsvfs->z_os, dzp->z_id, name, 8, 1, + zoid, mt, buf, bufsz, &conflict); + } else { + error = zap_lookup(zfsvfs->z_os, dzp->z_id, name, 8, 1, zoid); + } + + /* + * Allow multiple entries provided the first entry is + * the object id. Non-zpl consumers may safely make + * use of the additional space. + * + * XXX: This should be a feature flag for compatibility + */ + if (error == EOVERFLOW) + error = 0; + + if (zfsvfs->z_norm && !error && deflags) + *deflags = conflict ? ED_CASE_CONFLICT : 0; + + *zoid = ZFS_DIRENT_OBJ(*zoid); + + return (error); +} + +/* + * Lock a directory entry. A dirlock on protects that name + * in dzp's directory zap object. As long as you hold a dirlock, you can + * assume two things: (1) dzp cannot be reaped, and (2) no other thread + * can change the zap entry for (i.e. link or unlink) this name. + * + * Input arguments: + * dzp - znode for directory + * name - name of entry to lock + * flag - ZNEW: if the entry already exists, fail with EEXIST. + * ZEXISTS: if the entry does not exist, fail with ENOENT. + * ZSHARED: allow concurrent access with other ZSHARED callers. + * ZXATTR: we want dzp's xattr directory + * ZCILOOK: On a mixed sensitivity file system, + * this lookup should be case-insensitive. + * ZCIEXACT: On a purely case-insensitive file system, + * this lookup should be case-sensitive. + * ZRENAMING: we are locking for renaming, force narrow locks + * ZHAVELOCK: Don't grab the z_name_lock for this call. The + * current thread already holds it. + * + * Output arguments: + * zpp - pointer to the znode for the entry (NULL if there isn't one) + * dlpp - pointer to the dirlock for this entry (NULL on error) + * direntflags - (case-insensitive lookup only) + * flags if multiple case-sensitive matches exist in directory + * realpnp - (case-insensitive lookup only) + * actual name matched within the directory + * + * Return value: 0 on success or errno on failure. + * + * NOTE: Always checks for, and rejects, '.' and '..'. + * NOTE: For case-insensitive file systems we take wide locks (see below), + * but return znode pointers to a single match. + */ +int +zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, znode_t **zpp, + int flag, int *direntflags, struct componentname *realpnp) +{ + zfsvfs_t *zfsvfs = ZTOZSB(dzp); + zfs_dirlock_t *dl; + boolean_t update; + matchtype_t mt = 0; + uint64_t zoid; + int error = 0; + int cmpflags; + + *zpp = NULL; + *dlpp = NULL; + + /* + * Verify that we are not trying to lock '.', '..', or '.zfs' + */ + if ((name[0] == '.' && + (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) || + (zfs_has_ctldir(dzp) && strcmp(name, ZFS_CTLDIR_NAME) == 0)) + return (SET_ERROR(EEXIST)); + + /* + * Case sensitivity and normalization preferences are set when + * the file system is created. These are stored in the + * zfsvfs->z_case and zfsvfs->z_norm fields. These choices + * affect what vnodes can be cached in the DNLC, how we + * perform zap lookups, and the "width" of our dirlocks. + * + * A normal dirlock locks a single name. Note that with + * normalization a name can be composed multiple ways, but + * when normalized, these names all compare equal. A wide + * dirlock locks multiple names. We need these when the file + * system is supporting mixed-mode access. It is sometimes + * necessary to lock all case permutations of file name at + * once so that simultaneous case-insensitive/case-sensitive + * behaves as rationally as possible. + */ + + /* + * When matching we may need to normalize & change case according to + * FS settings. + * + * Note that a normalized match is necessary for a case insensitive + * filesystem when the lookup request is not exact because normalization + * can fold case independent of normalizing code point sequences. + * + * See the table above zfs_dropname(). + */ + if (zfsvfs->z_norm != 0) { + mt = MT_NORMALIZE; + + /* + * Determine if the match needs to honor the case specified in + * lookup, and if so keep track of that so that during + * normalization we don't fold case. + */ + if ((zfsvfs->z_case == ZFS_CASE_INSENSITIVE && + (flag & ZCIEXACT)) || + (zfsvfs->z_case == ZFS_CASE_MIXED && !(flag & ZCILOOK))) { + mt |= MT_MATCH_CASE; + } + } + + /* + * Only look in or update the DNLC if we are looking for the + * name on a file system that does not require normalization + * or case folding. We can also look there if we happen to be + * on a non-normalizing, mixed sensitivity file system IF we + * are looking for the exact name. + * + * Maybe can add TO-UPPERed version of name to dnlc in ci-only + * case for performance improvement? + */ + update = !zfsvfs->z_norm || + (zfsvfs->z_case == ZFS_CASE_MIXED && + !(zfsvfs->z_norm & ~U8_TEXTPREP_TOUPPER) && !(flag & ZCILOOK)); + + /* + * ZRENAMING indicates we are in a situation where we should + * take narrow locks regardless of the file system's + * preferences for normalizing and case folding. This will + * prevent us deadlocking trying to grab the same wide lock + * twice if the two names happen to be case-insensitive + * matches. + */ + if (flag & ZRENAMING) + cmpflags = 0; + else + cmpflags = zfsvfs->z_norm; + + /* + * Wait until there are no locks on this name. + * + * Don't grab the lock if it is already held. However, cannot + * have both ZSHARED and ZHAVELOCK together. + */ + ASSERT(!(flag & ZSHARED) || !(flag & ZHAVELOCK)); + if (!(flag & ZHAVELOCK)) + rw_enter(&dzp->z_name_lock, RW_READER); + + mutex_enter(&dzp->z_lock); + for (;;) { + if (dzp->z_unlinked && !(flag & ZXATTR)) { + mutex_exit(&dzp->z_lock); + if (!(flag & ZHAVELOCK)) + rw_exit(&dzp->z_name_lock); + return (SET_ERROR(ENOENT)); + } + for (dl = dzp->z_dirlocks; dl != NULL; dl = dl->dl_next) { + if ((u8_strcmp(name, dl->dl_name, 0, cmpflags, + U8_UNICODE_LATEST, &error) == 0) || error != 0) + break; + } + if (error != 0) { + mutex_exit(&dzp->z_lock); + if (!(flag & ZHAVELOCK)) + rw_exit(&dzp->z_name_lock); + return (SET_ERROR(ENOENT)); + } + if (dl == NULL) { + /* + * Allocate a new dirlock and add it to the list. + */ + dl = kmem_alloc(sizeof (zfs_dirlock_t), KM_SLEEP); + cv_init(&dl->dl_cv, NULL, CV_DEFAULT, NULL); + dl->dl_name = name; + dl->dl_sharecnt = 0; + dl->dl_namelock = 0; + dl->dl_namesize = 0; + dl->dl_dzp = dzp; + dl->dl_next = dzp->z_dirlocks; + dzp->z_dirlocks = dl; + break; + } + if ((flag & ZSHARED) && dl->dl_sharecnt != 0) + break; + cv_wait(&dl->dl_cv, &dzp->z_lock); + } + + /* + * If the z_name_lock was NOT held for this dirlock record it. + */ + if (flag & ZHAVELOCK) + dl->dl_namelock = 1; + + if ((flag & ZSHARED) && ++dl->dl_sharecnt > 1 && dl->dl_namesize == 0) { + /* + * We're the second shared reference to dl. Make a copy of + * dl_name in case the first thread goes away before we do. + * Note that we initialize the new name before storing its + * pointer into dl_name, because the first thread may load + * dl->dl_name at any time. It'll either see the old value, + * which belongs to it, or the new shared copy; either is OK. + */ + dl->dl_namesize = strlen(dl->dl_name) + 1; + name = kmem_alloc(dl->dl_namesize, KM_SLEEP); + bcopy(dl->dl_name, name, dl->dl_namesize); + dl->dl_name = name; + } + + mutex_exit(&dzp->z_lock); + + /* + * We have a dirlock on the name. (Note that it is the dirlock, + * not the dzp's z_lock, that protects the name in the zap object.) + * See if there's an object by this name; if so, put a hold on it. + */ + if (flag & ZXATTR) { + error = sa_lookup(dzp->z_sa_hdl, SA_ZPL_XATTR(zfsvfs), &zoid, + sizeof (zoid)); + if (error == 0) + error = (zoid == 0 ? SET_ERROR(ENOENT) : 0); + } else { + error = zfs_match_find(zfsvfs, dzp, name, mt, + update, direntflags, realpnp, &zoid); + } + if (error) { + if (error != ENOENT || (flag & ZEXISTS)) { + zfs_dirent_unlock(dl); + return (error); + } + } else { + if (flag & ZNEW) { + zfs_dirent_unlock(dl); + return (SET_ERROR(EEXIST)); + } + error = zfs_zget(zfsvfs, zoid, zpp); + if (error) { + zfs_dirent_unlock(dl); + return (error); + } + } + + *dlpp = dl; + + return (0); +} + +/* + * Unlock this directory entry and wake anyone who was waiting for it. + */ +void +zfs_dirent_unlock(zfs_dirlock_t *dl) +{ + znode_t *dzp = dl->dl_dzp; + zfs_dirlock_t **prev_dl, *cur_dl; + + mutex_enter(&dzp->z_lock); + + if (!dl->dl_namelock) + rw_exit(&dzp->z_name_lock); + + if (dl->dl_sharecnt > 1) { + dl->dl_sharecnt--; + mutex_exit(&dzp->z_lock); + return; + } + prev_dl = &dzp->z_dirlocks; + while ((cur_dl = *prev_dl) != dl) + prev_dl = &cur_dl->dl_next; + *prev_dl = dl->dl_next; + cv_broadcast(&dl->dl_cv); + mutex_exit(&dzp->z_lock); + + if (dl->dl_namesize != 0) + kmem_free(dl->dl_name, dl->dl_namesize); + cv_destroy(&dl->dl_cv); + kmem_free(dl, sizeof (*dl)); +} + +/* + * Look up an entry in a directory. + * + * NOTE: '.' and '..' are handled as special cases because + * no directory entries are actually stored for them. If this is + * the root of a filesystem, then '.zfs' is also treated as a + * special pseudo-directory. + */ +int +zfs_dirlook(znode_t *dzp, char *name, znode_t **zpp, int flags, + int *deflg, struct componentname *rpnp) +{ + zfs_dirlock_t *dl; + znode_t *zp; + struct vnode *vp; + int error = 0; + uint64_t parent; + + if (name[0] == 0 || (name[0] == '.' && name[1] == 0)) { + *zpp = dzp; + zhold(*zpp); + } else if (name[0] == '.' && name[1] == '.' && name[2] == 0) { + zfsvfs_t *zfsvfs = ZTOZSB(dzp); + + /* + * If we are a snapshot mounted under .zfs, return + * the inode pointer for the snapshot directory. + */ + if ((error = sa_lookup(dzp->z_sa_hdl, + SA_ZPL_PARENT(zfsvfs), &parent, sizeof (parent))) != 0) + return (error); + + if (parent == dzp->z_id && zfsvfs->z_parent != zfsvfs) { + error = zfsctl_root_lookup(zfsvfs->z_parent->z_ctldir, + "snapshot", &vp, 0, kcred, NULL, NULL); + if (error == 0) + *zpp = VTOZ(vp); + return (error); + } + rw_enter(&dzp->z_parent_lock, RW_READER); + error = zfs_zget(zfsvfs, parent, &zp); + if (error == 0) + *zpp = zp; + rw_exit(&dzp->z_parent_lock); + } else if (zfs_has_ctldir(dzp) && strcmp(name, ZFS_CTLDIR_NAME) == 0) { + vp = zfsctl_root(dzp); + if (vp != NULL) + *zpp = VTOZ(vp); + else + error = ENOENT; + } else { + int zf; + + zf = ZEXISTS | ZSHARED; + if (flags & FIGNORECASE) + zf |= ZCILOOK; + + error = zfs_dirent_lock(&dl, dzp, name, &zp, zf, deflg, rpnp); + if (error == 0) { + *zpp = zp; + zfs_dirent_unlock(dl); + dzp->z_zn_prefetch = B_TRUE; /* enable prefetching */ + } + rpnp = NULL; + } + + if ((flags & FIGNORECASE) && rpnp && !error) + (void) strlcpy(rpnp->cn_nameptr, name, rpnp->cn_namelen); + + return (error); +} + +/* + * unlinked Set (formerly known as the "delete queue") Error Handling + * + * When dealing with the unlinked set, we dmu_tx_hold_zap(), but we + * don't specify the name of the entry that we will be manipulating. We + * also fib and say that we won't be adding any new entries to the + * unlinked set, even though we might (this is to lower the minimum file + * size that can be deleted in a full filesystem). So on the small + * chance that the nlink list is using a fat zap (ie. has more than + * 2000 entries), we *may* not pre-read a block that's needed. + * Therefore it is remotely possible for some of the assertions + * regarding the unlinked set below to fail due to i/o error. On a + * nondebug system, this will result in the space being leaked. + */ +void +zfs_unlinked_add(znode_t *zp, dmu_tx_t *tx) +{ + zfsvfs_t *zfsvfs = ZTOZSB(zp); + + ASSERT(zp->z_unlinked); + + VERIFY3U(0, ==, + zap_add_int(zfsvfs->z_os, zfsvfs->z_unlinkedobj, zp->z_id, tx)); + + dataset_kstats_update_nunlinks_kstat(&zfsvfs->z_kstat, 1); +} + +/* + * Clean up any znodes that had no links when we either crashed or + * (force) umounted the file system. + */ +static void +zfs_unlinked_drain_task(void *arg) +{ + zfsvfs_t *zfsvfs = arg; + zap_cursor_t zc; + zap_attribute_t zap; + dmu_object_info_t doi; + znode_t *zp; + int error; + + /* + * Iterate over the contents of the unlinked set. + */ + for (zap_cursor_init(&zc, zfsvfs->z_os, zfsvfs->z_unlinkedobj); + zap_cursor_retrieve(&zc, &zap) == 0 && + zfsvfs->z_drain_state == ZFS_DRAIN_RUNNING; + zap_cursor_advance(&zc)) { + + /* + * See what kind of object we have in list + */ + + error = dmu_object_info(zfsvfs->z_os, + zap.za_first_integer, &doi); + if (error != 0) + continue; + + ASSERT((doi.doi_type == DMU_OT_PLAIN_FILE_CONTENTS) || + (doi.doi_type == DMU_OT_DIRECTORY_CONTENTS)); + /* + * We need to re-mark these list entries for deletion, + * so we pull them back into core and set zp->z_unlinked. + */ + error = zfs_zget(zfsvfs, zap.za_first_integer, &zp); + + /* + * We may pick up znodes that are already marked for deletion. + * This could happen during the purge of an extended attribute + * directory. All we need to do is skip over them, since they + * are already in the system marked z_unlinked. + */ + if (error != 0) + continue; + + zp->z_unlinked = B_TRUE; + + /* + * zrele() decrements the znode's ref count and may cause + * it to be synchronously freed. We interrupt freeing + * of this znode by checking the return value of + * dmu_objset_zfs_unmounting() in dmu_free_long_range() + * when an unmount is requested. + */ + zrele(zp); + ASSERT3B(zfsvfs->z_unmounted, ==, B_FALSE); + } + zap_cursor_fini(&zc); + + mutex_enter(&zfsvfs->z_drain_lock); + zfsvfs->z_drain_state = ZFS_DRAIN_SHUTDOWN; + cv_broadcast(&zfsvfs->z_drain_cv); + mutex_exit(&zfsvfs->z_drain_lock); +} + +/* + * Sets z_draining then tries to dispatch async unlinked drain. + * If that fails executes synchronous unlinked drain. + */ +void +zfs_unlinked_drain(zfsvfs_t *zfsvfs) +{ + ASSERT3B(zfsvfs->z_unmounted, ==, B_FALSE); + + mutex_enter(&zfsvfs->z_drain_lock); + ASSERT(zfsvfs->z_drain_state == ZFS_DRAIN_SHUTDOWN); + zfsvfs->z_drain_state = ZFS_DRAIN_RUNNING; + mutex_exit(&zfsvfs->z_drain_lock); + + if (taskq_dispatch( + dsl_pool_unlinked_drain_taskq(dmu_objset_pool(zfsvfs->z_os)), + zfs_unlinked_drain_task, zfsvfs, TQ_SLEEP) == 0) { + zfs_dbgmsg("async zfs_unlinked_drain dispatch failed"); + zfs_unlinked_drain_task(zfsvfs); + } +} + +/* + * Wait for the unlinked drain taskq task to stop. This will interrupt the + * unlinked set processing if it is in progress. + */ +void +zfs_unlinked_drain_stop_wait(zfsvfs_t *zfsvfs) +{ + ASSERT3B(zfsvfs->z_unmounted, ==, B_FALSE); + + mutex_enter(&zfsvfs->z_drain_lock); + while (zfsvfs->z_drain_state != ZFS_DRAIN_SHUTDOWN) { + zfsvfs->z_drain_state = ZFS_DRAIN_SHUTDOWN_REQ; + cv_wait(&zfsvfs->z_drain_cv, &zfsvfs->z_drain_lock); + } + mutex_exit(&zfsvfs->z_drain_lock); +} + +/* + * Delete the entire contents of a directory. Return a count + * of the number of entries that could not be deleted. If we encounter + * an error, return a count of at least one so that the directory stays + * in the unlinked set. + * + * NOTE: this function assumes that the directory is inactive, + * so there is no need to lock its entries before deletion. + * Also, it assumes the directory contents is *only* regular + * files. + */ +static int +zfs_purgedir(znode_t *dzp) +{ + zap_cursor_t zc; + zap_attribute_t zap; + znode_t *xzp; + dmu_tx_t *tx; + zfsvfs_t *zfsvfs = ZTOZSB(dzp); + zfs_dirlock_t dl; + int skipped = 0; + int error; + + for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id); + (error = zap_cursor_retrieve(&zc, &zap)) == 0; + zap_cursor_advance(&zc)) { + error = zfs_zget_ext(zfsvfs, + ZFS_DIRENT_OBJ(zap.za_first_integer), &xzp, + ZGET_FLAG_ASYNC); + if (error) { + skipped += 1; + continue; + } + + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_sa(tx, dzp->z_sa_hdl, B_FALSE); + dmu_tx_hold_zap(tx, dzp->z_id, FALSE, zap.za_name); + dmu_tx_hold_sa(tx, xzp->z_sa_hdl, B_FALSE); + dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); + /* Is this really needed ? */ + zfs_sa_upgrade_txholds(tx, xzp); + dmu_tx_mark_netfree(tx); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + dmu_tx_abort(tx); + /* Be aware this is not the "normal" zfs_zrele_async */ + zfs_znode_asyncput(xzp); + skipped += 1; + continue; + } + bzero(&dl, sizeof (dl)); + dl.dl_dzp = dzp; + dl.dl_name = zap.za_name; + + error = zfs_link_destroy(&dl, xzp, tx, 0, NULL); + if (error) + skipped += 1; + dmu_tx_commit(tx); + + /* Be aware this is not the "normal" zfs_zrele_async() */ + zfs_znode_asyncput(xzp); + } + zap_cursor_fini(&zc); + if (error != ENOENT) + skipped += 1; + return (skipped); +} + +void +zfs_rmnode(znode_t *zp) +{ + zfsvfs_t *zfsvfs = ZTOZSB(zp); + objset_t *os = zfsvfs->z_os; + znode_t *xzp = NULL; + dmu_tx_t *tx; + uint64_t acl_obj; + uint64_t xattr_obj; + int error; + + /* + * If this is an attribute directory, purge its contents. + */ + if (S_ISDIR(zp->z_mode) && (zp->z_pflags & ZFS_XATTR)) { + if (zfs_purgedir(zp) != 0) { + /* + * Not enough space to delete some xattrs. + * Leave it in the unlinked set. + */ + zfs_znode_dmu_fini(zp); + + return; + } + } + + /* + * Free up all the data in the file. We don't do this for directories + * because we need truncate and remove to be in the same tx, like in + * zfs_znode_delete(). Otherwise, if we crash here we'll end up with + * an inconsistent truncated zap object in the delete queue. Note a + * truncated file is harmless since it only contains user data. + */ + if (S_ISREG(zp->z_mode)) { + error = dmu_free_long_range(os, zp->z_id, 0, DMU_OBJECT_END); + if (error) { + /* + * Not enough space or we were interrupted by unmount. + * Leave the file in the unlinked set. + */ + zfs_znode_dmu_fini(zp); + return; + } + } + + /* + * If the file has extended attributes, we're going to unlink + * the xattr dir. + */ + error = sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zfsvfs), + &xattr_obj, sizeof (xattr_obj)); + if (error == 0 && xattr_obj) { + error = zfs_zget_ext(zfsvfs, xattr_obj, &xzp, + ZGET_FLAG_ASYNC); + ASSERT(error == 0); + } + + acl_obj = zfs_external_acl(zp); + + /* + * Set up the final transaction. + */ + tx = dmu_tx_create(os); + dmu_tx_hold_free(tx, zp->z_id, 0, DMU_OBJECT_END); + dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); + if (xzp) { + dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, TRUE, NULL); + dmu_tx_hold_sa(tx, xzp->z_sa_hdl, B_FALSE); + } + if (acl_obj) + dmu_tx_hold_free(tx, acl_obj, 0, DMU_OBJECT_END); + + zfs_sa_upgrade_txholds(tx, zp); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + /* + * Not enough space to delete the file. Leave it in the + * unlinked set, leaking it until the fs is remounted (at + * which point we'll call zfs_unlinked_drain() to process it). + */ + dmu_tx_abort(tx); + zfs_znode_dmu_fini(zp); + goto out; + } + + if (xzp) { + ASSERT(error == 0); + mutex_enter(&xzp->z_lock); + xzp->z_unlinked = B_TRUE; /* mark xzp for deletion */ + xzp->z_links = 0; /* no more links to it */ + VERIFY(0 == sa_update(xzp->z_sa_hdl, SA_ZPL_LINKS(zfsvfs), + &xzp->z_links, sizeof (xzp->z_links), tx)); + mutex_exit(&xzp->z_lock); + zfs_unlinked_add(xzp, tx); + } + + mutex_enter(&os->os_dsl_dataset->ds_dir->dd_activity_lock); + + /* + * Remove this znode from the unlinked set. If a has rollback has + * occurred while a file is open and unlinked. Then when the file + * is closed post rollback it will not exist in the rolled back + * version of the unlinked object. + */ + error = zap_remove_int(zfsvfs->z_os, zfsvfs->z_unlinkedobj, + zp->z_id, tx); + VERIFY(error == 0 || error == ENOENT); + + uint64_t count; + if (zap_count(os, zfsvfs->z_unlinkedobj, &count) == 0 && count == 0) { + cv_broadcast(&os->os_dsl_dataset->ds_dir->dd_activity_cv); + } + + mutex_exit(&os->os_dsl_dataset->ds_dir->dd_activity_lock); + + dataset_kstats_update_nunlinked_kstat(&zfsvfs->z_kstat, 1); + + zfs_znode_delete(zp, tx); + + dmu_tx_commit(tx); +out: + + /* Be aware this is not the "normal" zfs_zrele_async() */ + if (xzp) + zfs_znode_asyncput(xzp); +} + +static uint64_t +zfs_dirent(znode_t *zp, uint64_t mode) +{ + uint64_t de = zp->z_id; + + if (ZTOZSB(zp)->z_version >= ZPL_VERSION_DIRENT_TYPE) + de |= IFTODT(mode) << 60; + return (de); +} + +/* + * Link zp into dl. Can fail in the following cases : + * - if zp has been unlinked. + * - if the number of entries with the same hash (aka. colliding entries) + * exceed the capacity of a leaf-block of fatzap and splitting of the + * leaf-block does not help. + */ +int +zfs_link_create(zfs_dirlock_t *dl, znode_t *zp, dmu_tx_t *tx, int flag) +{ + znode_t *dzp = dl->dl_dzp; + zfsvfs_t *zfsvfs = ZTOZSB(zp); + uint64_t value; + int zp_is_dir = S_ISDIR(zp->z_mode); + sa_bulk_attr_t bulk[5]; + uint64_t mtime[2], ctime[2]; + int count = 0; + int error; + + mutex_enter(&zp->z_lock); + + if (!(flag & ZRENAMING)) { + if (zp->z_unlinked) { /* no new links to unlinked zp */ + ASSERT(!(flag & (ZNEW | ZEXISTS))); + mutex_exit(&zp->z_lock); + return (SET_ERROR(ENOENT)); + } + zp->z_links++; + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_LINKS(zfsvfs), + NULL, &zp->z_links, sizeof (zp->z_links)); + } + + value = zfs_dirent(zp, zp->z_mode); + error = zap_add(ZTOZSB(zp)->z_os, dzp->z_id, dl->dl_name, 8, 1, + &value, tx); + + /* + * zap_add could fail to add the entry if it exceeds the capacity of the + * leaf-block and zap_leaf_split() failed to help. + * The caller of this routine is responsible for failing the transaction + * which will rollback the SA updates done above. + */ + if (error != 0) { + mutex_exit(&zp->z_lock); + return (error); + } + + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_PARENT(zfsvfs), NULL, + &dzp->z_id, sizeof (dzp->z_id)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL, + &zp->z_pflags, sizeof (zp->z_pflags)); + + if (!(flag & ZNEW)) { + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, + ctime, sizeof (ctime)); + zfs_tstamp_update_setup(zp, STATE_CHANGED, mtime, + ctime); + } + error = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx); + ASSERT(error == 0); + + mutex_exit(&zp->z_lock); + + mutex_enter(&dzp->z_lock); + dzp->z_size++; + if (zp_is_dir) + dzp->z_links++; + count = 0; + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_SIZE(zfsvfs), NULL, + &dzp->z_size, sizeof (dzp->z_size)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_LINKS(zfsvfs), NULL, + &dzp->z_links, sizeof (dzp->z_links)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL, + mtime, sizeof (mtime)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, + ctime, sizeof (ctime)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL, + &dzp->z_pflags, sizeof (dzp->z_pflags)); + zfs_tstamp_update_setup(dzp, CONTENT_MODIFIED, mtime, ctime); + error = sa_bulk_update(dzp->z_sa_hdl, bulk, count, tx); + ASSERT(error == 0); + mutex_exit(&dzp->z_lock); + + return (0); +} + +/* + * The match type in the code for this function should conform to: + * + * ------------------------------------------------------------------------ + * fs type | z_norm | lookup type | match type + * ---------|-------------|-------------|---------------------------------- + * CS !norm | 0 | 0 | 0 (exact) + * CS norm | formX | 0 | MT_NORMALIZE + * CI !norm | upper | !ZCIEXACT | MT_NORMALIZE + * CI !norm | upper | ZCIEXACT | MT_NORMALIZE | MT_MATCH_CASE + * CI norm | upper|formX | !ZCIEXACT | MT_NORMALIZE + * CI norm | upper|formX | ZCIEXACT | MT_NORMALIZE | MT_MATCH_CASE + * CM !norm | upper | !ZCILOOK | MT_NORMALIZE | MT_MATCH_CASE + * CM !norm | upper | ZCILOOK | MT_NORMALIZE + * CM norm | upper|formX | !ZCILOOK | MT_NORMALIZE | MT_MATCH_CASE + * CM norm | upper|formX | ZCILOOK | MT_NORMALIZE + * + * Abbreviations: + * CS = Case Sensitive, CI = Case Insensitive, CM = Case Mixed + * upper = case folding set by fs type on creation (U8_TEXTPREP_TOUPPER) + * formX = unicode normalization form set on fs creation + */ +static int +zfs_dropname(zfs_dirlock_t *dl, znode_t *zp, znode_t *dzp, dmu_tx_t *tx, + int flag) +{ + int error; + + if (ZTOZSB(zp)->z_norm) { + matchtype_t mt = MT_NORMALIZE; + + if ((ZTOZSB(zp)->z_case == ZFS_CASE_INSENSITIVE && + (flag & ZCIEXACT)) || + (ZTOZSB(zp)->z_case == ZFS_CASE_MIXED && + !(flag & ZCILOOK))) { + mt |= MT_MATCH_CASE; + } + + error = zap_remove_norm(ZTOZSB(zp)->z_os, dzp->z_id, + dl->dl_name, mt, tx); + } else { + error = zap_remove(ZTOZSB(zp)->z_os, dzp->z_id, dl->dl_name, + tx); + } + + return (error); +} + +/* + * Unlink zp from dl, and mark zp for deletion if this was the last link. Can + * fail if zp is a mount point (EBUSY) or a non-empty directory (ENOTEMPTY). + * If 'unlinkedp' is NULL, we put unlinked znodes on the unlinked list. + * If it's non-NULL, we use it to indicate whether the znode needs deletion, + * and it's the caller's job to do it. + */ +int +zfs_link_destroy(zfs_dirlock_t *dl, znode_t *zp, dmu_tx_t *tx, int flag, + boolean_t *unlinkedp) +{ + znode_t *dzp = dl->dl_dzp; + zfsvfs_t *zfsvfs = ZTOZSB(dzp); + int zp_is_dir = S_ISDIR(zp->z_mode); + boolean_t unlinked = B_FALSE; + sa_bulk_attr_t bulk[5]; + uint64_t mtime[2], ctime[2]; + int count = 0; + int error; + + if (!(flag & ZRENAMING)) { + mutex_enter(&zp->z_lock); + + if (zp_is_dir && !zfs_dirempty(zp)) { + mutex_exit(&zp->z_lock); + return (SET_ERROR(ENOTEMPTY)); + } + + /* + * If we get here, we are going to try to remove the object. + * First try removing the name from the directory; if that + * fails, return the error. + */ + error = zfs_dropname(dl, zp, dzp, tx, flag); + if (error != 0) { + mutex_exit(&zp->z_lock); + return (error); + } + + if (zp->z_links <= zp_is_dir) { + zfs_panic_recover("zfs: link count on %lu is %u, " + "should be at least %u", zp->z_id, + (int)zp->z_links, zp_is_dir + 1); + zp->z_links = zp_is_dir + 1; + } + if (--zp->z_links == zp_is_dir) { + zp->z_unlinked = B_TRUE; + zp->z_links = 0; + unlinked = B_TRUE; + } else { + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), + NULL, &ctime, sizeof (ctime)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), + NULL, &zp->z_pflags, sizeof (zp->z_pflags)); + zfs_tstamp_update_setup(zp, STATE_CHANGED, mtime, + ctime); + } + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_LINKS(zfsvfs), + NULL, &zp->z_links, sizeof (zp->z_links)); + error = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx); + count = 0; + ASSERT(error == 0); + mutex_exit(&zp->z_lock); + } else { + error = zfs_dropname(dl, zp, dzp, tx, flag); + if (error != 0) + return (error); + } + + mutex_enter(&dzp->z_lock); + dzp->z_size--; /* one dirent removed */ + if (zp_is_dir) + dzp->z_links--; /* ".." link from zp */ + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_LINKS(zfsvfs), + NULL, &dzp->z_links, sizeof (dzp->z_links)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_SIZE(zfsvfs), + NULL, &dzp->z_size, sizeof (dzp->z_size)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), + NULL, ctime, sizeof (ctime)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), + NULL, mtime, sizeof (mtime)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), + NULL, &dzp->z_pflags, sizeof (dzp->z_pflags)); + zfs_tstamp_update_setup(dzp, CONTENT_MODIFIED, mtime, ctime); + error = sa_bulk_update(dzp->z_sa_hdl, bulk, count, tx); + ASSERT(error == 0); + mutex_exit(&dzp->z_lock); + + if (unlinkedp != NULL) + *unlinkedp = unlinked; + else if (unlinked) + zfs_unlinked_add(zp, tx); + + return (0); +} + +/* + * Indicate whether the directory is empty. Works with or without z_lock + * held, but can only be consider a hint in the latter case. Returns true + * if only "." and ".." remain and there's no work in progress. + * + * The internal ZAP size, rather than zp->z_size, needs to be checked since + * some consumers (Lustre) do not strictly maintain an accurate SA_ZPL_SIZE. + */ +boolean_t +zfs_dirempty(znode_t *dzp) +{ + zfsvfs_t *zfsvfs = ZTOZSB(dzp); + uint64_t count; + int error; + + if (dzp->z_dirlocks != NULL) + return (B_FALSE); + + error = zap_count(zfsvfs->z_os, dzp->z_id, &count); + if (error != 0 || count != 0) + return (B_FALSE); + + return (B_TRUE); +} + +int +zfs_make_xattrdir(znode_t *zp, vattr_t *vap, znode_t **xzpp, cred_t *cr) +{ + zfsvfs_t *zfsvfs = ZTOZSB(zp); + znode_t *xzp; + dmu_tx_t *tx; + int error; + zfs_acl_ids_t acl_ids; + boolean_t fuid_dirtied; +#ifdef DEBUG + uint64_t parent; +#endif + + *xzpp = NULL; + + if ((error = zfs_zaccess(zp, ACE_WRITE_NAMED_ATTRS, 0, B_FALSE, cr))) + return (error); + + if ((error = zfs_acl_ids_create(zp, IS_XATTR, vap, cr, NULL, + &acl_ids)) != 0) + return (error); + if (zfs_acl_ids_overquota(zfsvfs, &acl_ids, zp->z_projid)) { + zfs_acl_ids_free(&acl_ids); + return (SET_ERROR(EDQUOT)); + } + + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_sa_create(tx, acl_ids.z_aclp->z_acl_bytes + + ZFS_SA_BASE_ATTR_SIZE); + dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE); + dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); + fuid_dirtied = zfsvfs->z_fuid_dirty; + if (fuid_dirtied) + zfs_fuid_txhold(zfsvfs, tx); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + zfs_acl_ids_free(&acl_ids); + dmu_tx_abort(tx); + return (error); + } + zfs_mknode(zp, vap, tx, cr, IS_XATTR, &xzp, &acl_ids); + + if (fuid_dirtied) + zfs_fuid_sync(zfsvfs, tx); + +#ifdef DEBUG + error = sa_lookup(xzp->z_sa_hdl, SA_ZPL_PARENT(zfsvfs), + &parent, sizeof (parent)); + ASSERT(error == 0 && parent == zp->z_id); +#endif + + VERIFY(0 == sa_update(zp->z_sa_hdl, SA_ZPL_XATTR(zfsvfs), &xzp->z_id, + sizeof (xzp->z_id), tx)); + + if (!zp->z_unlinked) + (void) zfs_log_create(zfsvfs->z_log, tx, TX_MKXATTR, zp, + xzp, "", NULL, acl_ids.z_fuidp, vap); + + zfs_acl_ids_free(&acl_ids); + dmu_tx_commit(tx); + +#ifdef __APPLE__ + /* + * OS X - attach the vnode _after_ committing the transaction + */ + zfs_znode_getvnode(xzp, zfsvfs); +#endif + + *xzpp = xzp; + + return (0); +} + +/* + * Return a znode for the extended attribute directory for zp. + * ** If the directory does not already exist, it is created ** + * + * IN: zp - znode to obtain attribute directory from + * cr - credentials of caller + * flags - flags from the VOP_LOOKUP call + * + * OUT: xipp - pointer to extended attribute znode + * + * RETURN: 0 on success + * error number on failure + */ +int +zfs_get_xattrdir(znode_t *zp, znode_t **xzpp, cred_t *cr, int flags) +{ + zfsvfs_t *zfsvfs = ZTOZSB(zp); + znode_t *xzp; + zfs_dirlock_t *dl; + vattr_t va; + int error; +top: + error = zfs_dirent_lock(&dl, zp, "", &xzp, ZXATTR, NULL, NULL); + if (error) + return (error); + + if (xzp != NULL) { + *xzpp = xzp; + zfs_dirent_unlock(dl); + return (0); + } + + if (!(flags & CREATE_XATTR_DIR)) { + zfs_dirent_unlock(dl); + return (SET_ERROR(ENOENT)); + } + + if (zfs_is_readonly(zfsvfs)) { + zfs_dirent_unlock(dl); + return (SET_ERROR(EROFS)); + } + + /* + * The ability to 'create' files in an attribute + * directory comes from the write_xattr permission on the base file. + * + * The ability to 'search' an attribute directory requires + * read_xattr permission on the base file. + * + * Once in a directory the ability to read/write attributes + * is controlled by the permissions on the attribute file. + */ + va.va_mask = ATTR_TYPE | ATTR_MODE | ATTR_UID | ATTR_GID; + va.va_type = VDIR; + va.va_mode = S_IFDIR | S_ISVTX | 0777; + zfs_fuid_map_ids(zp, cr, &va.va_uid, &va.va_gid); + + error = zfs_make_xattrdir(zp, &va, xzpp, cr); + zfs_dirent_unlock(dl); + + if (error == ERESTART) { + /* NB: we already did dmu_tx_wait() if necessary */ + goto top; + } + + return (error); +} + +/* + * Decide whether it is okay to remove within a sticky directory. + * + * In sticky directories, write access is not sufficient; + * you can remove entries from a directory only if: + * + * you own the directory, + * you own the entry, + * you have write access to the entry, + * or you are privileged (checked in secpolicy...). + * + * The function returns 0 if remove access is granted. + */ +int +zfs_sticky_remove_access(znode_t *zdp, znode_t *zp, cred_t *cr) +{ + uid_t uid; + uid_t downer; + uid_t fowner; + zfsvfs_t *zfsvfs = ZTOZSB(zdp); + + if (zfsvfs->z_replay) + return (0); + + if ((zdp->z_mode & S_ISVTX) == 0) + return (0); + + downer = zfs_fuid_map_id(zfsvfs, zdp->z_uid, cr, ZFS_OWNER); + fowner = zfs_fuid_map_id(zfsvfs, zp->z_uid, cr, ZFS_OWNER); + + if ((uid = crgetuid(cr)) == downer || uid == fowner || + (vnode_isreg(ZTOV(zp)) && + zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr) == 0)) + return (0); + else + return (secpolicy_vnode_remove(ZTOV(zp), cr)); +} diff --git a/module/os/macos/zfs/zfs_file_os.c b/module/os/macos/zfs/zfs_file_os.c new file mode 100644 index 000000000000..6a7fc63a14ef --- /dev/null +++ b/module/os/macos/zfs/zfs_file_os.c @@ -0,0 +1,434 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#include +#include +#include +#include +#include + +#define FILE_FD_NOTUSED -1 + +/* + * Open file + * + * path - fully qualified path to file + * flags - file attributes O_READ / O_WRITE / O_EXCL + * fpp - pointer to return file pointer + * + * Returns 0 on success underlying error on failure. + */ +noinline int +zfs_file_open(const char *path, int flags, int mode, zfs_file_t **fpp) +{ + struct vnode *vp = NULL; + vfs_context_t vctx; + int error; + + if (!(flags & O_CREAT) && (flags & O_WRONLY)) + flags |= O_EXCL; + + vctx = vfs_context_create((vfs_context_t)0); + error = vnode_open(path, flags, mode, 0, &vp, vctx); + if (error == 0 && vp != NULL) { + zfs_file_t *zf; + zf = (zfs_file_t *)kmem_zalloc(sizeof (zfs_file_t), KM_SLEEP); + zf->f_vnode = vp; + zf->f_fd = FILE_FD_NOTUSED; + + /* Optional, implemented O_APPEND: set offset to file size. */ + if (flags & O_APPEND) + zf->f_ioflags |= IO_APPEND; + + /* O_TRUNC is broken */ + if (flags & O_TRUNC) { + struct vnode_attr va; + + VATTR_INIT(&va); + VATTR_SET(&va, va_data_size, 0); + error = vnode_setattr(vp, &va, vctx); + } + + *fpp = zf; + } + (void) vfs_context_rele(vctx); + + return (error); +} + +void +zfs_file_close(zfs_file_t *fp) +{ + vfs_context_t vctx; + vctx = vfs_context_create((vfs_context_t)0); + vnode_close(fp->f_vnode, fp->f_writes ? FWASWRITTEN : 0, vctx); + (void) vfs_context_rele(vctx); + + kmem_free(fp, sizeof (zfs_file_t)); +} + +static int +zfs_file_write_impl(zfs_file_t *fp, const void *buf, size_t count, + loff_t *off, ssize_t *resid) +{ + int error; + ssize_t local_resid = count; + + /* If we came with a 'fd' use it, as it can handle pipes. */ + if (fp->f_fd == FILE_FD_NOTUSED) + error = zfs_vn_rdwr(UIO_WRITE, fp->f_vnode, (caddr_t)buf, count, + *off, UIO_SYSSPACE, fp->f_ioflags, RLIM64_INFINITY, + kcred, &local_resid); + else + error = spl_vn_rdwr(UIO_WRITE, fp, (caddr_t)buf, count, + *off, UIO_SYSSPACE, fp->f_ioflags, RLIM64_INFINITY, + kcred, &local_resid); + + if (error != 0) + return (SET_ERROR(error)); + + fp->f_writes = 1; + + if (resid != NULL) + *resid = local_resid; + else if (local_resid != 0) + return (SET_ERROR(EIO)); + + *off += count - local_resid; + + return (0); +} + +/* + * Stateful write - use os internal file pointer to determine where to + * write and update on successful completion. + * + * fp - pointer to file (pipe, socket, etc) to write to + * buf - buffer to write + * count - # of bytes to write + * resid - pointer to count of unwritten bytes (if short write) + * + * Returns 0 on success errno on failure. + */ +int +zfs_file_write(zfs_file_t *fp, const void *buf, size_t count, ssize_t *resid) +{ + loff_t off = fp->f_offset; + ssize_t rc; + + rc = zfs_file_write_impl(fp, buf, count, &off, resid); + if (rc == 0) + fp->f_offset = off; + + return (SET_ERROR(rc)); +} + +/* + * Stateless write - os internal file pointer is not updated. + * + * fp - pointer to file (pipe, socket, etc) to write to + * buf - buffer to write + * count - # of bytes to write + * off - file offset to write to (only valid for seekable types) + * resid - pointer to count of unwritten bytes + * + * Returns 0 on success errno on failure. + */ +int +zfs_file_pwrite(zfs_file_t *fp, const void *buf, size_t count, loff_t off, + ssize_t *resid) +{ + return (zfs_file_write_impl(fp, buf, count, &off, resid)); +} + +static ssize_t +zfs_file_read_impl(zfs_file_t *fp, void *buf, size_t count, loff_t *off, + ssize_t *resid) +{ + int error; + ssize_t local_resid = count; + + /* If we have realvp, it's faster to call its spl_vn_rdwr */ + if (fp->f_fd == FILE_FD_NOTUSED) + error = zfs_vn_rdwr(UIO_READ, fp->f_vnode, buf, count, + *off, UIO_SYSSPACE, 0, RLIM64_INFINITY, + kcred, &local_resid); + else + error = spl_vn_rdwr(UIO_READ, fp, buf, count, + *off, UIO_SYSSPACE, 0, RLIM64_INFINITY, + kcred, &local_resid); + + if (error) + return (SET_ERROR(error)); + + *off += count - local_resid; + if (resid != NULL) + *resid = local_resid; + + return (SET_ERROR(0)); +} + +/* + * Stateful read - use os internal file pointer to determine where to + * read and update on successful completion. + * + * fp - pointer to file (pipe, socket, etc) to read from + * buf - buffer to write + * count - # of bytes to read + * resid - pointer to count of unread bytes (if short read) + * + * Returns 0 on success errno on failure. + */ +int +zfs_file_read(zfs_file_t *fp, void *buf, size_t count, ssize_t *resid) +{ + loff_t off = fp->f_offset; + int rc; + + rc = zfs_file_read_impl(fp, buf, count, &off, resid); + if (rc == 0) + fp->f_offset = off; + return (rc); +} + +/* + * Stateless read - os internal file pointer is not updated. + * + * fp - pointer to file (pipe, socket, etc) to read from + * buf - buffer to write + * count - # of bytes to write + * off - file offset to read from (only valid for seekable types) + * resid - pointer to count of unwritten bytes (if short read) + * + * Returns 0 on success errno on failure. + */ +int +zfs_file_pread(zfs_file_t *fp, void *buf, size_t count, loff_t off, + ssize_t *resid) +{ + return (zfs_file_read_impl(fp, buf, count, &off, resid)); +} + +/* + * lseek - set / get file pointer + * + * fp - pointer to file (pipe, socket, etc) to read from + * offp - value to seek to, returns current value plus passed offset + * whence - see man pages for standard lseek whence values + * + * Returns 0 on success errno on failure (ESPIPE for non seekable types) + */ +int +zfs_file_seek(zfs_file_t *fp, loff_t *offp, int whence) +{ + if (*offp < 0 || *offp > MAXOFFSET_T) + return (EINVAL); + + switch (whence) { + case SEEK_SET: + fp->f_offset = *offp; + break; + case SEEK_CUR: + fp->f_offset += *offp; + *offp = fp->f_offset; + break; + case SEEK_END: + /* Implement this if eventually needed: get filesize */ + VERIFY0(whence == SEEK_END); + break; + } + + return (0); +} + +/* + * Get file attributes + * + * filp - file pointer + * zfattr - pointer to file attr structure + * + * Currently only used for fetching size and file mode. + * + * Returns 0 on success or error code of underlying getattr call on failure. + */ +int +zfs_file_getattr(zfs_file_t *filp, zfs_file_attr_t *zfattr) +{ + vfs_context_t vctx; + int rc; + vattr_t vap; + + VATTR_INIT(&vap); + VATTR_WANTED(&vap, va_size); + VATTR_WANTED(&vap, va_mode); + + vctx = vfs_context_create((vfs_context_t)0); + rc = vnode_getattr(filp->f_vnode, &vap, vctx); + (void) vfs_context_rele(vctx); + + if (rc) + return (rc); + + zfattr->zfa_size = vap.va_size; + zfattr->zfa_mode = vap.va_mode; + + return (0); +} + +/* + * Sync file to disk + * + * filp - file pointer + * flags - O_SYNC and or O_DSYNC + * + * Returns 0 on success or error code of underlying sync call on failure. + */ +int +zfs_file_fsync(zfs_file_t *filp, int flags) +{ + vfs_context_t vctx; + int rc; + + vctx = vfs_context_create((vfs_context_t)0); + rc = VNOP_FSYNC(filp->f_vnode, (flags == FSYNC), vctx); + (void) vfs_context_rele(vctx); + return (rc); +} + +/* + * fallocate - allocate or free space on disk + * + * fp - file pointer + * mode (non-standard options for hole punching etc) + * offset - offset to start allocating or freeing from + * len - length to free / allocate + * + * OPTIONAL + */ +int +zfs_file_fallocate(zfs_file_t *fp, int mode, loff_t offset, loff_t len) +{ + int rc; + struct flock flck = { 0 }; + + flck.l_type = F_FREESP; + flck.l_start = offset; + flck.l_len = len; + flck.l_whence = 0; + + rc = VOP_SPACE(fp->f_vnode, F_FREESP, &flck, + 0, 0, kcred, NULL); + + return (rc); +} + +/* + * Request current file pointer offset + * + * fp - pointer to file + * + * Returns current file offset. + */ +loff_t +zfs_file_off(zfs_file_t *fp) +{ + return (fp->f_offset); +} + +/* + * Request file pointer private data + * + * fp - pointer to file + * + * Returns pointer to file private data. + */ +extern kmutex_t zfsdev_state_lock; +dev_t zfsdev_get_dev(void); + +void * +zfs_file_private(zfs_file_t *fp) +{ + dev_t dev; + void *zs; + + dev = zfsdev_get_dev(); + dprintf("%s: fetching dev x%x\n", __func__, dev); + if (dev == 0) + return (NULL); + + mutex_enter(&zfsdev_state_lock); + zs = zfsdev_get_state(minor(dev), ZST_ALL); + mutex_exit(&zfsdev_state_lock); + dprintf("%s: searching minor %d %p\n", __func__, minor(dev), zs); + + return (zs); +} + +/* + * unlink file + * + * path - fully qualified file path + * + * Returns 0 on success. + * + * OPTIONAL + */ +int +zfs_file_unlink(const char *path) +{ + return (EOPNOTSUPP); +} + +/* + * Get reference to file pointer + * + * fd - input file descriptor + * fpp - pointer to file pointer + * + * Returns 0 on success EBADF on failure. + */ +int file_flags(int, int *); +int +zfs_file_get(int fd, zfs_file_t **fpp) +{ + *fpp = getf(fd); + if (*fpp == NULL) + return (EBADF); + + int flags = 0; + if (file_flags(fd, &flags) == 0) { + if (flags & O_APPEND) { + (*fpp)->f_ioflags = IO_APPEND; + } + } + return (0); +} + +/* + * Drop reference to file pointer + * + * fd - input file descriptor + */ +void +zfs_file_put(int fd) +{ + releasef(fd); +} diff --git a/module/os/macos/zfs/zfs_fuid_os.c b/module/os/macos/zfs/zfs_fuid_os.c new file mode 100644 index 000000000000..8d6e9b9c54cd --- /dev/null +++ b/module/os/macos/zfs/zfs_fuid_os.c @@ -0,0 +1,52 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#ifdef _KERNEL +#include +#include +#include +#endif +#include + +uint64_t +zfs_fuid_create_cred(zfsvfs_t *zfsvfs, zfs_fuid_type_t type, + cred_t *cr, zfs_fuid_info_t **fuidp) +{ + uid_t id; + + VERIFY(type == ZFS_OWNER || type == ZFS_GROUP); + + id = (type == ZFS_OWNER) ? crgetuid(cr) : crgetgid(cr); + + if (IS_EPHEMERAL(id)) + return ((type == ZFS_OWNER) ? UID_NOBODY : GID_NOBODY); + + return ((uint64_t)id); +} diff --git a/module/os/macos/zfs/zfs_ioctl_os.c b/module/os/macos/zfs/zfs_ioctl_os.c new file mode 100644 index 000000000000..60cb60b3e5fe --- /dev/null +++ b/module/os/macos/zfs/zfs_ioctl_os.c @@ -0,0 +1,379 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2013, 2020 Jorgen Lundman + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +int zfs_major = 0; +int zfs_bmajor = 0; +static void *zfs_devnode = NULL; +#define ZFS_MAJOR -24 + +boolean_t +zfs_vfs_held(zfsvfs_t *zfsvfs) +{ + return (zfsvfs->z_vfs != NULL); +} + +int +zfs_vfs_ref(zfsvfs_t **zfvp) +{ + int error = 0; + + if (*zfvp == NULL || (*zfvp)->z_vfs == NULL) + return (SET_ERROR(ESRCH)); + + error = vfs_busy((*zfvp)->z_vfs, LK_NOWAIT); + if (error != 0) { + *zfvp = NULL; + error = SET_ERROR(ESRCH); + } + return (error); +} + +void +zfs_vfs_rele(zfsvfs_t *zfsvfs) +{ + vfs_unbusy(zfsvfs->z_vfs); +} + +static uint_t zfsdev_private_tsd; + +dev_t +zfsdev_get_dev(void) +{ + return ((dev_t)tsd_get(zfsdev_private_tsd)); +} + + +static int +zfsdev_open(dev_t dev, int flags, int devtype, struct proc *p) +{ + int error; + + mutex_enter(&zfsdev_state_lock); + if (zfsdev_get_state(minor(dev), ZST_ALL)) { + mutex_exit(&zfsdev_state_lock); + return (0); + } + error = zfsdev_state_init((void *)dev); + mutex_exit(&zfsdev_state_lock); + + return (-error); +} + +static int +zfsdev_release(dev_t dev, int flags, int devtype, struct proc *p) +{ + mutex_enter(&zfsdev_state_lock); + zfsdev_state_destroy((void *)dev); + mutex_exit(&zfsdev_state_lock); + + return (0); +} + +/* !static : so we can dtrace */ +int +zfsdev_ioctl(dev_t dev, ulong_t cmd, caddr_t arg, __unused int xflag, + struct proc *p) +{ + uint_t len, vecnum; + zfs_iocparm_t *zit; + zfs_cmd_t *zc; + int error, rc; + user_addr_t uaddr; + + /* Translate XNU ioctl to enum table: */ + len = IOCPARM_LEN(cmd); + vecnum = cmd - _IOWR('Z', ZFS_IOC_FIRST, zfs_iocparm_t); + zit = (void *)arg; + uaddr = (user_addr_t)zit->zfs_cmd; + + if (len != sizeof (zfs_iocparm_t)) { + /* + * printf("len %d vecnum: %d sizeof (zfs_cmd_t) %lu\n", + * len, vecnum, sizeof (zfs_cmd_t)); + */ + /* + * We can get plenty raw ioctl()s here, for exaple open() will + * cause spec_open() to issue DKIOCGETTHROTTLEMASK. + */ + return (EINVAL); + } + + zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); + + if (copyin(uaddr, zc, sizeof (zfs_cmd_t))) { + error = SET_ERROR(EFAULT); + goto out; + } + + error = zfsdev_ioctl_common(vecnum, zc, 0); + + rc = copyout(zc, uaddr, sizeof (*zc)); + + if (error == 0 && rc != 0) + error = -SET_ERROR(EFAULT); + + /* + * OSX must return(0) or XNU doesn't copyout(). Save the real + * rc to userland + */ + zit->zfs_ioc_error = error; + error = 0; + +out: + kmem_free(zc, sizeof (zfs_cmd_t)); + return (error); + +} + +/* for spa_iokit_dataset_proxy_create */ +#include +#include + + +static int +zfs_secpolicy_os_none(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) +{ + return (0); +} + +static const zfs_ioc_key_t zfs_keys_proxy_dataset[] = { + {ZPOOL_CONFIG_POOL_NAME, DATA_TYPE_STRING, 0}, +}; + +static int +zfs_ioc_osx_proxy_dataset(const char *unused, nvlist_t *innvl, + nvlist_t *outnvl) +{ + int error; + char *osname = NULL; + char value[MAXPATHLEN * 2]; + + if (nvlist_lookup_string(innvl, + ZPOOL_CONFIG_POOL_NAME, &osname) != 0) + return (EINVAL); + + /* XXX Get osname */ + + /* Create new virtual disk, and return /dev/disk name */ + error = zfs_osx_proxy_create(osname); + + if (error == 0) + error = zfs_osx_proxy_get_bsdname(osname, + value, sizeof (value)); + + if (error == 0) { + + fnvlist_add_string(outnvl, ZPOOL_CONFIG_POOL_NAME, osname); + fnvlist_add_string(outnvl, ZPOOL_CONFIG_PATH, value); + + printf("%s: Created virtual disk '%s' for '%s'\n", __func__, + value, osname); + } + + return (error); +} + +static const zfs_ioc_key_t zfs_keys_proxy_remove[] = { + {ZPOOL_CONFIG_POOL_NAME, DATA_TYPE_STRING, 0}, +}; + +static int +zfs_ioc_osx_proxy_remove(const char *unused, nvlist_t *innvl, + nvlist_t *outnvl) +{ + int error; + char *osname = NULL; + char value[MAXPATHLEN * 2]; + + if (nvlist_lookup_string(innvl, + ZPOOL_CONFIG_POOL_NAME, &osname) != 0) + return (EINVAL); + + zfs_osx_proxy_remove(osname); + + return (0); +} + +void +zfs_ioctl_init_os(void) +{ + /* APPLE Specific ioctls */ + zfs_ioctl_register("proxy_dataset", ZFS_IOC_PROXY_DATASET, + zfs_ioc_osx_proxy_dataset, zfs_secpolicy_os_none, + NO_NAME, POOL_CHECK_NONE, + B_FALSE, B_FALSE, zfs_keys_proxy_dataset, + ARRAY_SIZE(zfs_keys_proxy_dataset)); + zfs_ioctl_register("proxy_remove", ZFS_IOC_PROXY_REMOVE, + zfs_ioc_osx_proxy_remove, zfs_secpolicy_config, + NO_NAME, POOL_CHECK_NONE, + B_FALSE, B_FALSE, zfs_keys_proxy_remove, + ARRAY_SIZE(zfs_keys_proxy_remove)); +} + +/* ioctl handler for block device. Relay to zvol */ +static int +zfsdev_bioctl(dev_t dev, ulong_t cmd, caddr_t data, + __unused int flag, struct proc *p) +{ + return (zvol_os_ioctl(dev, cmd, data, 1, NULL, NULL)); +} + +static struct bdevsw zfs_bdevsw = { + .d_open = zvol_os_open, + .d_close = zvol_os_close, + .d_strategy = zvol_os_strategy, + .d_ioctl = zfsdev_bioctl, /* block ioctl handler */ + .d_dump = eno_dump, + .d_psize = zvol_os_get_volume_blocksize, + .d_type = D_DISK, +}; + +static struct cdevsw zfs_cdevsw = { + .d_open = zfsdev_open, + .d_close = zfsdev_release, + .d_read = zvol_os_read, + .d_write = zvol_os_write, + .d_ioctl = zfsdev_ioctl, + .d_stop = eno_stop, + .d_reset = eno_reset, + .d_ttys = NULL, + .d_select = eno_select, + .d_mmap = eno_mmap, + .d_strategy = eno_strat, + .d_reserved_1 = eno_getc, + .d_reserved_2 = eno_putc, + .d_type = D_DISK +}; + +/* Callback to create a unique minor for each open */ +static int +zfs_devfs_clone(__unused dev_t dev, int action) +{ + static minor_t minorx; + + if (action == DEVFS_CLONE_ALLOC) { + mutex_enter(&zfsdev_state_lock); + minorx = zfsdev_minor_alloc(); + mutex_exit(&zfsdev_state_lock); + return (minorx); + } + return (-1); +} + +int +zfsdev_attach(void) +{ + dev_t dev; + + zfs_bmajor = bdevsw_add(-1, &zfs_bdevsw); + zfs_major = cdevsw_add_with_bdev(-1, &zfs_cdevsw, zfs_bmajor); + + if (zfs_major < 0) { + printf("ZFS: zfs_attach() failed to allocate a major number\n"); + return (-1); + } + + dev = makedev(zfs_major, 0); /* Get the device number */ + zfs_devnode = devfs_make_node_clone(dev, DEVFS_CHAR, UID_ROOT, + GID_WHEEL, 0666, zfs_devfs_clone, "zfs", 0); + if (!zfs_devnode) { + printf("ZFS: devfs_make_node() failed\n"); + return (-1); + } + + wrap_avl_init(); + wrap_unicode_init(); + wrap_nvpair_init(); + wrap_zcommon_init(); + wrap_icp_init(); + wrap_lua_init(); + wrap_zstd_init(); + + tsd_create(&zfsdev_private_tsd, NULL); + + kstat_osx_init(); + return (0); +} + +void +zfsdev_detach(void) +{ + kstat_osx_fini(); + + tsd_destroy(&zfsdev_private_tsd); + + wrap_zstd_fini(); + wrap_lua_fini(); + wrap_icp_fini(); + wrap_zcommon_fini(); + wrap_nvpair_fini(); + wrap_unicode_fini(); + wrap_avl_fini(); + + if (zfs_devnode) { + devfs_remove(zfs_devnode); + zfs_devnode = NULL; + } + if (zfs_major) { + (void) cdevsw_remove(zfs_major, &zfs_cdevsw); + zfs_major = 0; + } +} + +uint64_t +zfs_max_nvlist_src_size_os(void) +{ + if (zfs_max_nvlist_src_size != 0) + return (zfs_max_nvlist_src_size); + + return (KMALLOC_MAX_SIZE); +} diff --git a/module/os/macos/zfs/zfs_kstat_osx.c b/module/os/macos/zfs/zfs_kstat_osx.c new file mode 100644 index 000000000000..55075f73623f --- /dev/null +++ b/module/os/macos/zfs/zfs_kstat_osx.c @@ -0,0 +1,899 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2014, 2020 Jorgen Lundman + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef _KERNEL +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * In Solaris the tunable are set via /etc/system. Until we have a load + * time configuration, we add them to writable kstat tunables. + * + * This table is more or less populated from IllumOS mdb zfs_params sources + * https://github.com/illumos/illumos-gate/blob/master/ + * usr/src/cmd/mdb/common/modules/zfs/zfs.c#L336-L392 + * + */ + + + +osx_kstat_t osx_kstat = { + { "spa_version", KSTAT_DATA_UINT64 }, + { "zpl_version", KSTAT_DATA_UINT64 }, + + { "active_vnodes", KSTAT_DATA_UINT64 }, + { "vnop_debug", KSTAT_DATA_UINT64 }, + { "reclaim_nodes", KSTAT_DATA_UINT64 }, + { "ignore_negatives", KSTAT_DATA_UINT64 }, + { "ignore_positives", KSTAT_DATA_UINT64 }, + { "create_negatives", KSTAT_DATA_UINT64 }, + { "force_formd_normalized", KSTAT_DATA_UINT64 }, + { "skip_unlinked_drain", KSTAT_DATA_UINT64 }, + { "use_system_sync", KSTAT_DATA_UINT64 }, + + { "zfs_arc_max", KSTAT_DATA_UINT64 }, + { "zfs_arc_min", KSTAT_DATA_UINT64 }, + { "zfs_arc_meta_limit", KSTAT_DATA_UINT64 }, + { "zfs_arc_meta_min", KSTAT_DATA_UINT64 }, + { "zfs_arc_grow_retry", KSTAT_DATA_UINT64 }, + { "zfs_arc_shrink_shift", KSTAT_DATA_UINT64 }, + { "zfs_arc_p_min_shift", KSTAT_DATA_UINT64 }, + { "zfs_arc_average_blocksize", KSTAT_DATA_UINT64 }, + + { "l2arc_write_max", KSTAT_DATA_UINT64 }, + { "l2arc_write_boost", KSTAT_DATA_UINT64 }, + { "l2arc_headroom", KSTAT_DATA_UINT64 }, + { "l2arc_headroom_boost", KSTAT_DATA_UINT64 }, + { "l2arc_feed_secs", KSTAT_DATA_UINT64 }, + { "l2arc_feed_min_ms", KSTAT_DATA_UINT64 }, + + { "max_active", KSTAT_DATA_UINT64 }, + { "sync_read_min_active", KSTAT_DATA_UINT64 }, + { "sync_read_max_active", KSTAT_DATA_UINT64 }, + { "sync_write_min_active", KSTAT_DATA_UINT64 }, + { "sync_write_max_active", KSTAT_DATA_UINT64 }, + { "async_read_min_active", KSTAT_DATA_UINT64 }, + { "async_read_max_active", KSTAT_DATA_UINT64 }, + { "async_write_min_active", KSTAT_DATA_UINT64 }, + { "async_write_max_active", KSTAT_DATA_UINT64 }, + { "scrub_min_active", KSTAT_DATA_UINT64 }, + { "scrub_max_active", KSTAT_DATA_UINT64 }, + { "async_write_min_dirty_pct", KSTAT_DATA_INT64 }, + { "async_write_max_dirty_pct", KSTAT_DATA_INT64 }, + { "aggregation_limit", KSTAT_DATA_INT64 }, + { "read_gap_limit", KSTAT_DATA_INT64 }, + { "write_gap_limit", KSTAT_DATA_INT64 }, + + {"zfs_arc_lotsfree_percent", KSTAT_DATA_INT64 }, + {"zfs_arc_sys_free", KSTAT_DATA_INT64 }, + {"zfs_dirty_data_max", KSTAT_DATA_INT64 }, + {"zfs_delay_max_ns", KSTAT_DATA_INT64 }, + {"zfs_delay_min_dirty_percent", KSTAT_DATA_INT64 }, + {"zfs_delay_scale", KSTAT_DATA_INT64 }, + {"spa_asize_inflation", KSTAT_DATA_INT64 }, + {"zfs_prefetch_disable", KSTAT_DATA_INT64 }, + {"zfetch_max_streams", KSTAT_DATA_INT64 }, + {"zfetch_min_sec_reap", KSTAT_DATA_INT64 }, + {"zfetch_array_rd_sz", KSTAT_DATA_INT64 }, + {"zfs_default_bs", KSTAT_DATA_INT64 }, + {"zfs_default_ibs", KSTAT_DATA_INT64 }, + {"metaslab_aliquot", KSTAT_DATA_INT64 }, + {"spa_max_replication_override", KSTAT_DATA_INT64 }, + {"spa_mode_global", KSTAT_DATA_INT64 }, + {"zfs_flags", KSTAT_DATA_INT64 }, + {"zfs_txg_timeout", KSTAT_DATA_INT64 }, + {"zfs_vdev_cache_max", KSTAT_DATA_INT64 }, + {"zfs_vdev_cache_size", KSTAT_DATA_INT64 }, + {"zfs_vdev_cache_bshift", KSTAT_DATA_INT64 }, + {"vdev_mirror_shift", KSTAT_DATA_INT64 }, + {"zfs_scrub_limit", KSTAT_DATA_INT64 }, + {"zfs_no_scrub_io", KSTAT_DATA_INT64 }, + {"zfs_no_scrub_prefetch", KSTAT_DATA_INT64 }, + {"fzap_default_block_shift", KSTAT_DATA_INT64 }, + {"zfs_immediate_write_sz", KSTAT_DATA_INT64 }, +// {"zfs_read_chunk_size", KSTAT_DATA_INT64 }, + {"zfs_nocacheflush", KSTAT_DATA_INT64 }, + {"zil_replay_disable", KSTAT_DATA_INT64 }, + {"metaslab_df_alloc_threshold", KSTAT_DATA_INT64 }, + {"metaslab_df_free_pct", KSTAT_DATA_INT64 }, + {"zio_injection_enabled", KSTAT_DATA_INT64 }, + {"zvol_immediate_write_sz", KSTAT_DATA_INT64 }, + + { "l2arc_noprefetch", KSTAT_DATA_INT64 }, + { "l2arc_feed_again", KSTAT_DATA_INT64 }, + { "l2arc_norw", KSTAT_DATA_INT64 }, + + {"zfs_recover", KSTAT_DATA_INT64 }, + + {"zfs_free_bpobj_enabled", KSTAT_DATA_INT64 }, + + {"zfs_send_corrupt_data", KSTAT_DATA_UINT64 }, + {"zfs_send_queue_length", KSTAT_DATA_UINT64 }, + {"zfs_recv_queue_length", KSTAT_DATA_UINT64 }, + + {"zvol_inhibit_dev", KSTAT_DATA_UINT64 }, + {"zfs_send_set_freerecords_bit", KSTAT_DATA_UINT64 }, + + {"zfs_write_implies_delete_child", KSTAT_DATA_UINT64 }, + {"zfs_send_holes_without_birth_time", KSTAT_DATA_UINT64 }, + + {"dbuf_cache_max_bytes", KSTAT_DATA_UINT64 }, + + {"zfs_vdev_queue_depth_pct", KSTAT_DATA_UINT64 }, + {"zio_dva_throttle_enabled", KSTAT_DATA_UINT64 }, + + {"zfs_lua_max_instrlimit", KSTAT_DATA_UINT64 }, + {"zfs_lua_max_memlimit", KSTAT_DATA_UINT64 }, + + {"zfs_trim_extent_bytes_max", KSTAT_DATA_UINT64 }, + {"zfs_trim_extent_bytes_min", KSTAT_DATA_UINT64 }, + {"zfs_trim_metaslab_skip", KSTAT_DATA_UINT64 }, + {"zfs_trim_txg_batch", KSTAT_DATA_UINT64 }, + {"zfs_trim_queue_limit", KSTAT_DATA_UINT64 }, + + {"zfs_send_unmodified_spill_blocks", KSTAT_DATA_UINT64 }, + {"zfs_special_class_metadata_reserve_pct", KSTAT_DATA_UINT64 }, + + {"zfs_vdev_raidz_impl", KSTAT_DATA_STRING }, + {"icp_gcm_impl", KSTAT_DATA_STRING }, + {"icp_aes_impl", KSTAT_DATA_STRING }, + {"zfs_fletcher_4_impl", KSTAT_DATA_STRING }, + + {"zfs_expire_snapshot", KSTAT_DATA_UINT64 }, + {"zfs_admin_snapshot", KSTAT_DATA_UINT64 }, + {"zfs_auto_snapshot", KSTAT_DATA_UINT64 }, + + {"zfs_spa_discard_memory_limit", KSTAT_DATA_UINT64 }, + {"zfs_async_block_max_blocks", KSTAT_DATA_UINT64 }, + {"zfs_initialize_chunk_size", KSTAT_DATA_UINT64 }, + {"zfs_scan_suspend_progress", KSTAT_DATA_UINT64 }, + {"zfs_removal_suspend_progress", KSTAT_DATA_UINT64 }, + {"zfs_livelist_max_entries", KSTAT_DATA_UINT64 }, + + {"zfs_allow_redacted_dataset_mount", KSTAT_DATA_UINT64 }, + {"zfs_checksum_events_per_second", KSTAT_DATA_UINT64 }, + {"zfs_commit_timeout_pct", KSTAT_DATA_UINT64 }, + {"zfs_compressed_arc_enabled", KSTAT_DATA_UINT64 }, + {"zfs_condense_indirect_commit_entry_delay_ms", KSTAT_DATA_UINT64 }, + {"zfs_condense_min_mapping_bytes", KSTAT_DATA_UINT64 }, + {"zfs_deadman_checktime_ms", KSTAT_DATA_UINT64 }, + {"zfs_deadman_failmode", KSTAT_DATA_STRING }, + {"zfs_deadman_synctime_ms", KSTAT_DATA_UINT64 }, + {"zfs_deadman_ziotime_ms", KSTAT_DATA_UINT64 }, + {"zfs_disable_ivset_guid_check", KSTAT_DATA_UINT64 }, + {"zfs_initialize_value", KSTAT_DATA_UINT64 }, + {"zfs_keep_log_spacemaps_at_export", KSTAT_DATA_UINT64 }, + {"l2arc_rebuild_blocks_min_l2size", KSTAT_DATA_UINT64 }, + {"l2arc_rebuild_enabled", KSTAT_DATA_UINT64 }, + {"l2arc_trim_ahead", KSTAT_DATA_UINT64 }, + {"zfs_livelist_condense_new_alloc", KSTAT_DATA_UINT64 }, + {"zfs_livelist_condense_sync_cancel", KSTAT_DATA_UINT64 }, + {"zfs_livelist_condense_sync_pause", KSTAT_DATA_UINT64 }, + {"zfs_livelist_condense_zthr_cancel", KSTAT_DATA_UINT64 }, + {"zfs_livelist_condense_zthr_pause", KSTAT_DATA_UINT64 }, + {"zfs_livelist_min_percent_shared", KSTAT_DATA_UINT64 }, + {"zfs_max_dataset_nesting", KSTAT_DATA_UINT64 }, + {"zfs_max_missing_tvds", KSTAT_DATA_UINT64 }, + {"metaslab_debug_load", KSTAT_DATA_UINT64 }, + {"metaslab_force_ganging", KSTAT_DATA_UINT64 }, + {"zfs_multihost_fail_intervals", KSTAT_DATA_UINT64 }, + {"zfs_multihost_import_intervals", KSTAT_DATA_UINT64 }, + {"zfs_multihost_interval", KSTAT_DATA_UINT64 }, + {"zfs_override_estimate_recordsize", KSTAT_DATA_UINT64 }, + {"zfs_remove_max_segment", KSTAT_DATA_UINT64 }, + {"zfs_resilver_min_time_ms", KSTAT_DATA_UINT64 }, + {"zfs_scan_legacy", KSTAT_DATA_UINT64 }, + {"zfs_scan_vdev_limit", KSTAT_DATA_UINT64 }, + {"zfs_slow_io_events_per_second", KSTAT_DATA_UINT64 }, + {"spa_load_verify_data", KSTAT_DATA_UINT64 }, + {"spa_load_verify_metadata", KSTAT_DATA_UINT64 }, + {"zfs_unlink_suspend_progress", KSTAT_DATA_UINT64 }, + {"zfs_vdev_min_ms_count", KSTAT_DATA_UINT64 }, + {"vdev_validate_skip", KSTAT_DATA_UINT64 }, + {"zfs_zevent_len_max", KSTAT_DATA_UINT64 }, + {"zio_slow_io_ms", KSTAT_DATA_UINT64 }, + {"l2arc_mfuonly", KSTAT_DATA_UINT64 }, + {"zfs_multihost_history", KSTAT_DATA_UINT64 }, + {"zfs_rebuild_scrub_enabled", KSTAT_DATA_UINT64 }, + {"zfs_txg_history", KSTAT_DATA_UINT64 }, + {"vdev_file_physical_ashift", KSTAT_DATA_UINT64 }, + {"zvol_volmode", KSTAT_DATA_UINT64 }, + {"zfs_zevent_retain_max", KSTAT_DATA_UINT64 }, + +}; + +static char vdev_raidz_string[KSTAT_STRLEN] = { 0 }; +static char icp_gcm_string[KSTAT_STRLEN] = { 0 }; +static char icp_aes_string[KSTAT_STRLEN] = { 0 }; +static char zfs_fletcher_4_string[KSTAT_STRLEN] = { 0 }; + +static kstat_t *osx_kstat_ksp; + +#if !defined(__OPTIMIZE__) +#pragma GCC diagnostic ignored "-Wframe-larger-than=" +#endif + +extern kstat_t *arc_ksp; + +static int osx_kstat_update(kstat_t *ksp, int rw) +{ + osx_kstat_t *ks = ksp->ks_data; + + if (rw == KSTAT_WRITE) { + + /* Darwin */ + + zfs_vnop_ignore_negatives = + ks->darwin_ignore_negatives.value.ui64; + zfs_vnop_ignore_positives = + ks->darwin_ignore_positives.value.ui64; + zfs_vnop_create_negatives = + ks->darwin_create_negatives.value.ui64; + zfs_vnop_force_formd_normalized_output = + ks->darwin_force_formd_normalized.value.ui64; + zfs_vnop_skip_unlinked_drain = + ks->darwin_skip_unlinked_drain.value.ui64; + zfs_vfs_sync_paranoia = + ks->darwin_use_system_sync.value.ui64; + + /* ARC */ + arc_kstat_update_osx(ksp, rw); + + /* L2ARC */ + l2arc_write_max = ks->l2arc_write_max.value.ui64; + l2arc_write_boost = ks->l2arc_write_boost.value.ui64; + l2arc_headroom = ks->l2arc_headroom.value.ui64; + l2arc_headroom_boost = ks->l2arc_headroom_boost.value.ui64; + l2arc_feed_secs = ks->l2arc_feed_secs.value.ui64; + l2arc_feed_min_ms = ks->l2arc_feed_min_ms.value.ui64; + + l2arc_noprefetch = ks->l2arc_noprefetch.value.i64; + l2arc_feed_again = ks->l2arc_feed_again.value.i64; + l2arc_norw = ks->l2arc_norw.value.i64; + + /* vdev_queue */ + + zfs_vdev_max_active = + ks->zfs_vdev_max_active.value.ui64; + zfs_vdev_sync_read_min_active = + ks->zfs_vdev_sync_read_min_active.value.ui64; + zfs_vdev_sync_read_max_active = + ks->zfs_vdev_sync_read_max_active.value.ui64; + zfs_vdev_sync_write_min_active = + ks->zfs_vdev_sync_write_min_active.value.ui64; + zfs_vdev_sync_write_max_active = + ks->zfs_vdev_sync_write_max_active.value.ui64; + zfs_vdev_async_read_min_active = + ks->zfs_vdev_async_read_min_active.value.ui64; + zfs_vdev_async_read_max_active = + ks->zfs_vdev_async_read_max_active.value.ui64; + zfs_vdev_async_write_min_active = + ks->zfs_vdev_async_write_min_active.value.ui64; + zfs_vdev_async_write_max_active = + ks->zfs_vdev_async_write_max_active.value.ui64; + zfs_vdev_scrub_min_active = + ks->zfs_vdev_scrub_min_active.value.ui64; + zfs_vdev_scrub_max_active = + ks->zfs_vdev_scrub_max_active.value.ui64; + zfs_vdev_async_write_active_min_dirty_percent = + ks->zfs_vdev_async_write_active_min_dirty_percent.value.i64; + zfs_vdev_async_write_active_max_dirty_percent = + ks->zfs_vdev_async_write_active_max_dirty_percent.value.i64; + zfs_vdev_aggregation_limit = + ks->zfs_vdev_aggregation_limit.value.i64; + zfs_vdev_read_gap_limit = + ks->zfs_vdev_read_gap_limit.value.i64; + zfs_vdev_write_gap_limit = + ks->zfs_vdev_write_gap_limit.value.i64; + + zfs_dirty_data_max = + ks->zfs_dirty_data_max.value.i64; + zfs_delay_max_ns = + ks->zfs_delay_max_ns.value.i64; + zfs_delay_min_dirty_percent = + ks->zfs_delay_min_dirty_percent.value.i64; + zfs_delay_scale = + ks->zfs_delay_scale.value.i64; + spa_asize_inflation = + ks->spa_asize_inflation.value.i64; + zfs_prefetch_disable = + ks->zfs_prefetch_disable.value.i64; + zfetch_max_streams = + ks->zfetch_max_streams.value.i64; + zfetch_min_sec_reap = + ks->zfetch_min_sec_reap.value.i64; + zfetch_array_rd_sz = + ks->zfetch_array_rd_sz.value.i64; + zfs_default_bs = + ks->zfs_default_bs.value.i64; + zfs_default_ibs = + ks->zfs_default_ibs.value.i64; + metaslab_aliquot = + ks->metaslab_aliquot.value.i64; + spa_max_replication_override = + ks->spa_max_replication_override.value.i64; + spa_mode_global = + ks->spa_mode_global.value.i64; + zfs_flags = + ks->zfs_flags.value.i64; + zfs_txg_timeout = + ks->zfs_txg_timeout.value.i64; + zfs_vdev_cache_max = + ks->zfs_vdev_cache_max.value.i64; + zfs_vdev_cache_size = + ks->zfs_vdev_cache_size.value.i64; + zfs_no_scrub_io = + ks->zfs_no_scrub_io.value.i64; + zfs_no_scrub_prefetch = + ks->zfs_no_scrub_prefetch.value.i64; + fzap_default_block_shift = + ks->fzap_default_block_shift.value.i64; + zfs_immediate_write_sz = + ks->zfs_immediate_write_sz.value.i64; +// zfs_read_chunk_size = +// ks->zfs_read_chunk_size.value.i64; + zfs_nocacheflush = + ks->zfs_nocacheflush.value.i64; + zil_replay_disable = + ks->zil_replay_disable.value.i64; + metaslab_df_alloc_threshold = + ks->metaslab_df_alloc_threshold.value.i64; + metaslab_df_free_pct = + ks->metaslab_df_free_pct.value.i64; + zio_injection_enabled = + ks->zio_injection_enabled.value.i64; + zvol_immediate_write_sz = + ks->zvol_immediate_write_sz.value.i64; + + zfs_recover = + ks->zfs_recover.value.i64; + + zfs_free_bpobj_enabled = + ks->zfs_free_bpobj_enabled.value.i64; + + zfs_send_corrupt_data = + ks->zfs_send_corrupt_data.value.ui64; + zfs_send_queue_length = + ks->zfs_send_queue_length.value.ui64; + zfs_recv_queue_length = + ks->zfs_recv_queue_length.value.ui64; + + zvol_inhibit_dev = + ks->zvol_inhibit_dev.value.ui64; + zfs_send_set_freerecords_bit = + ks->zfs_send_set_freerecords_bit.value.ui64; + + zfs_write_implies_delete_child = + ks->zfs_write_implies_delete_child.value.ui64; + send_holes_without_birth_time = + ks->zfs_send_holes_without_birth_time.value.ui64; + + dbuf_cache_max_bytes = + ks->dbuf_cache_max_bytes.value.ui64; + + zfs_vdev_queue_depth_pct = + ks->zfs_vdev_queue_depth_pct.value.ui64; + + zio_dva_throttle_enabled = + (boolean_t)ks->zio_dva_throttle_enabled.value.ui64; + + zfs_lua_max_instrlimit = + ks->zfs_lua_max_instrlimit.value.ui64; + zfs_lua_max_memlimit = + ks->zfs_lua_max_memlimit.value.ui64; + + zfs_trim_extent_bytes_max = + ks->zfs_trim_extent_bytes_max.value.ui64; + zfs_trim_extent_bytes_min = + ks->zfs_trim_extent_bytes_min.value.ui64; + zfs_trim_metaslab_skip = + ks->zfs_trim_metaslab_skip.value.ui64; + zfs_trim_txg_batch = + ks->zfs_trim_txg_batch.value.ui64; + zfs_trim_queue_limit = + ks->zfs_trim_queue_limit.value.ui64; + + zfs_send_unmodified_spill_blocks = + ks->zfs_send_unmodified_spill_blocks.value.ui64; + zfs_special_class_metadata_reserve_pct = + ks->zfs_special_class_metadata_reserve_pct.value.ui64; + + // Check if string has changed (from KREAD), if so, update. + if (strcmp(vdev_raidz_string, + KSTAT_NAMED_STR_PTR(&ks->zfs_vdev_raidz_impl)) != 0) + vdev_raidz_impl_set( + KSTAT_NAMED_STR_PTR(&ks->zfs_vdev_raidz_impl)); + + if (strcmp(icp_gcm_string, + KSTAT_NAMED_STR_PTR(&ks->icp_gcm_impl)) != 0) + gcm_impl_set(KSTAT_NAMED_STR_PTR(&ks->icp_gcm_impl)); + + if (strcmp(icp_aes_string, + KSTAT_NAMED_STR_PTR(&ks->icp_aes_impl)) != 0) + aes_impl_set(KSTAT_NAMED_STR_PTR(&ks->icp_aes_impl)); + + if (strcmp(zfs_fletcher_4_string, + KSTAT_NAMED_STR_PTR(&ks->zfs_fletcher_4_impl)) != 0) + fletcher_4_impl_set( + KSTAT_NAMED_STR_PTR(&ks->zfs_fletcher_4_impl)); + + zfs_expire_snapshot = + ks->zfs_expire_snapshot.value.ui64; + zfs_admin_snapshot = + ks->zfs_admin_snapshot.value.ui64; + zfs_auto_snapshot = + ks->zfs_auto_snapshot.value.ui64; + + zfs_spa_discard_memory_limit = + ks->zfs_spa_discard_memory_limit.value.ui64; + zfs_async_block_max_blocks = + ks->zfs_async_block_max_blocks.value.ui64; + zfs_initialize_chunk_size = + ks->zfs_initialize_chunk_size.value.ui64; + zfs_scan_suspend_progress = + ks->zfs_scan_suspend_progress.value.ui64; + zfs_removal_suspend_progress = + ks->zfs_removal_suspend_progress.value.ui64; + zfs_livelist_max_entries = + ks->zfs_livelist_max_entries.value.ui64; + + zfs_allow_redacted_dataset_mount = + ks->zfs_allow_redacted_dataset_mount.value.ui64; + zfs_checksum_events_per_second = + ks->zfs_checksum_events_per_second.value.ui64; + zfs_commit_timeout_pct = + ks->zfs_commit_timeout_pct.value.ui64; + zfs_compressed_arc_enabled = + ks->zfs_compressed_arc_enabled.value.ui64; + zfs_condense_indirect_commit_entry_delay_ms = + ks->zfs_condense_indirect_commit_entry_delay_ms.value.ui64; + zfs_condense_min_mapping_bytes = + ks->zfs_condense_min_mapping_bytes.value.ui64; + zfs_deadman_checktime_ms = + ks->zfs_deadman_checktime_ms.value.ui64; + + if (strcmp(zfs_deadman_failmode, + KSTAT_NAMED_STR_PTR(&ks->zfs_vdev_raidz_impl)) != 0) { + char *buf = + KSTAT_NAMED_STR_PTR(&ks->zfs_vdev_raidz_impl); + if (strcmp(buf, "wait") == 0) + zfs_deadman_failmode = "wait"; + if (strcmp(buf, "continue") == 0) + zfs_deadman_failmode = "continue"; + if (strcmp(buf, "panic") == 0) + zfs_deadman_failmode = "panic"; + param_set_deadman_failmode_common(buf); + } + + zfs_deadman_synctime_ms = + ks->zfs_deadman_synctime_ms.value.ui64; + zfs_deadman_ziotime_ms = + ks->zfs_deadman_ziotime_ms.value.ui64; + zfs_disable_ivset_guid_check = + ks->zfs_disable_ivset_guid_check.value.ui64; + zfs_initialize_value = + ks->zfs_initialize_value.value.ui64; + zfs_keep_log_spacemaps_at_export = + ks->zfs_keep_log_spacemaps_at_export.value.ui64; + l2arc_rebuild_blocks_min_l2size = + ks->l2arc_rebuild_blocks_min_l2size.value.ui64; + l2arc_rebuild_enabled = + ks->l2arc_rebuild_enabled.value.ui64; + l2arc_trim_ahead = ks->l2arc_trim_ahead.value.ui64; + zfs_livelist_condense_new_alloc = + ks->zfs_livelist_condense_new_alloc.value.ui64; + zfs_livelist_condense_sync_cancel = + ks->zfs_livelist_condense_sync_cancel.value.ui64; + zfs_livelist_condense_sync_pause = + ks->zfs_livelist_condense_sync_pause.value.ui64; + zfs_livelist_condense_zthr_cancel = + ks->zfs_livelist_condense_zthr_cancel.value.ui64; + zfs_livelist_condense_zthr_pause = + ks->zfs_livelist_condense_zthr_pause.value.ui64; + zfs_livelist_min_percent_shared = + ks->zfs_livelist_min_percent_shared.value.ui64; + zfs_max_dataset_nesting = + ks->zfs_max_dataset_nesting.value.ui64; + zfs_max_missing_tvds = + ks->zfs_max_missing_tvds.value.ui64; + metaslab_debug_load = ks->metaslab_debug_load.value.ui64; + metaslab_force_ganging = + ks->metaslab_force_ganging.value.ui64; + zfs_multihost_fail_intervals = + ks->zfs_multihost_fail_intervals.value.ui64; + zfs_multihost_import_intervals = + ks->zfs_multihost_import_intervals.value.ui64; + zfs_multihost_interval = + ks->zfs_multihost_interval.value.ui64; + zfs_override_estimate_recordsize = + ks->zfs_override_estimate_recordsize.value.ui64; + zfs_remove_max_segment = + ks->zfs_remove_max_segment.value.ui64; + zfs_resilver_min_time_ms = + ks->zfs_resilver_min_time_ms.value.ui64; + zfs_scan_legacy = ks->zfs_scan_legacy.value.ui64; + zfs_scan_vdev_limit = + ks->zfs_scan_vdev_limit.value.ui64; + zfs_slow_io_events_per_second = + ks->zfs_slow_io_events_per_second.value.ui64; + spa_load_verify_data = + ks->spa_load_verify_data.value.ui64; + spa_load_verify_metadata = + ks->spa_load_verify_metadata.value.ui64; + zfs_unlink_suspend_progress = + ks->zfs_unlink_suspend_progress.value.ui64; + zfs_vdev_min_ms_count = ks->zfs_vdev_min_ms_count.value.ui64; + vdev_validate_skip = ks->vdev_validate_skip.value.ui64; + zfs_zevent_len_max = ks->zfs_zevent_len_max.value.ui64; + zio_slow_io_ms = ks->zio_slow_io_ms.value.ui64; + + l2arc_mfuonly = ks->l2arc_mfuonly.value.ui64; + zfs_multihost_history = ks->zfs_multihost_history.value.ui64; + zfs_rebuild_scrub_enabled = + ks->zfs_rebuild_scrub_enabled.value.ui64; + zfs_txg_history = ks->zfs_txg_history.value.ui64; + vdev_file_physical_ashift = + ks->vdev_file_physical_ashift.value.ui64; + zvol_volmode = ks->zvol_volmode.value.ui64; + zfs_zevent_retain_max = ks->zfs_zevent_retain_max.value.ui64; + + } else { + + /* kstat READ */ + ks->spa_version.value.ui64 = SPA_VERSION; + ks->zpl_version.value.ui64 = ZPL_VERSION; + + /* Darwin */ + ks->darwin_active_vnodes.value.ui64 = vnop_num_vnodes; + ks->darwin_reclaim_nodes.value.ui64 = vnop_num_reclaims; + ks->darwin_ignore_negatives.value.ui64 = + zfs_vnop_ignore_negatives; + ks->darwin_ignore_positives.value.ui64 = + zfs_vnop_ignore_positives; + ks->darwin_create_negatives.value.ui64 = + zfs_vnop_create_negatives; + ks->darwin_force_formd_normalized.value.ui64 = + zfs_vnop_force_formd_normalized_output; + ks->darwin_skip_unlinked_drain.value.ui64 = + zfs_vnop_skip_unlinked_drain; + ks->darwin_use_system_sync.value.ui64 = zfs_vfs_sync_paranoia; + + /* ARC */ + arc_kstat_update_osx(ksp, rw); + + /* L2ARC */ + ks->l2arc_write_max.value.ui64 = l2arc_write_max; + ks->l2arc_write_boost.value.ui64 = l2arc_write_boost; + ks->l2arc_headroom.value.ui64 = l2arc_headroom; + ks->l2arc_headroom_boost.value.ui64 = l2arc_headroom_boost; + ks->l2arc_feed_secs.value.ui64 = l2arc_feed_secs; + ks->l2arc_feed_min_ms.value.ui64 = l2arc_feed_min_ms; + + ks->l2arc_noprefetch.value.i64 = l2arc_noprefetch; + ks->l2arc_feed_again.value.i64 = l2arc_feed_again; + ks->l2arc_norw.value.i64 = l2arc_norw; + + /* vdev_queue */ + ks->zfs_vdev_max_active.value.ui64 = + zfs_vdev_max_active; + ks->zfs_vdev_sync_read_min_active.value.ui64 = + zfs_vdev_sync_read_min_active; + ks->zfs_vdev_sync_read_max_active.value.ui64 = + zfs_vdev_sync_read_max_active; + ks->zfs_vdev_sync_write_min_active.value.ui64 = + zfs_vdev_sync_write_min_active; + ks->zfs_vdev_sync_write_max_active.value.ui64 = + zfs_vdev_sync_write_max_active; + ks->zfs_vdev_async_read_min_active.value.ui64 = + zfs_vdev_async_read_min_active; + ks->zfs_vdev_async_read_max_active.value.ui64 = + zfs_vdev_async_read_max_active; + ks->zfs_vdev_async_write_min_active.value.ui64 = + zfs_vdev_async_write_min_active; + ks->zfs_vdev_async_write_max_active.value.ui64 = + zfs_vdev_async_write_max_active; + ks->zfs_vdev_scrub_min_active.value.ui64 = + zfs_vdev_scrub_min_active; + ks->zfs_vdev_scrub_max_active.value.ui64 = + zfs_vdev_scrub_max_active; + ks->zfs_vdev_async_write_active_min_dirty_percent.value.i64 = + zfs_vdev_async_write_active_min_dirty_percent; + ks->zfs_vdev_async_write_active_max_dirty_percent.value.i64 = + zfs_vdev_async_write_active_max_dirty_percent; + ks->zfs_vdev_aggregation_limit.value.i64 = + zfs_vdev_aggregation_limit; + ks->zfs_vdev_read_gap_limit.value.i64 = + zfs_vdev_read_gap_limit; + ks->zfs_vdev_write_gap_limit.value.i64 = + zfs_vdev_write_gap_limit; + + ks->zfs_dirty_data_max.value.i64 = + zfs_dirty_data_max; + ks->zfs_delay_max_ns.value.i64 = + zfs_delay_max_ns; + ks->zfs_delay_min_dirty_percent.value.i64 = + zfs_delay_min_dirty_percent; + ks->zfs_delay_scale.value.i64 = + zfs_delay_scale; + ks->spa_asize_inflation.value.i64 = + spa_asize_inflation; + ks->zfs_prefetch_disable.value.i64 = + zfs_prefetch_disable; + ks->zfetch_max_streams.value.i64 = + zfetch_max_streams; + ks->zfetch_min_sec_reap.value.i64 = + zfetch_min_sec_reap; + ks->zfetch_array_rd_sz.value.i64 = + zfetch_array_rd_sz; + ks->zfs_default_bs.value.i64 = + zfs_default_bs; + ks->zfs_default_ibs.value.i64 = + zfs_default_ibs; + ks->metaslab_aliquot.value.i64 = + metaslab_aliquot; + ks->spa_max_replication_override.value.i64 = + spa_max_replication_override; + ks->spa_mode_global.value.i64 = + spa_mode_global; + ks->zfs_flags.value.i64 = + zfs_flags; + ks->zfs_txg_timeout.value.i64 = + zfs_txg_timeout; + ks->zfs_vdev_cache_max.value.i64 = + zfs_vdev_cache_max; + ks->zfs_vdev_cache_size.value.i64 = + zfs_vdev_cache_size; + ks->zfs_no_scrub_io.value.i64 = + zfs_no_scrub_io; + ks->zfs_no_scrub_prefetch.value.i64 = + zfs_no_scrub_prefetch; + ks->fzap_default_block_shift.value.i64 = + fzap_default_block_shift; + ks->zfs_immediate_write_sz.value.i64 = + zfs_immediate_write_sz; +// ks->zfs_read_chunk_size.value.i64 = +// zfs_read_chunk_size; + ks->zfs_nocacheflush.value.i64 = + zfs_nocacheflush; + ks->zil_replay_disable.value.i64 = + zil_replay_disable; + ks->metaslab_df_alloc_threshold.value.i64 = + metaslab_df_alloc_threshold; + ks->metaslab_df_free_pct.value.i64 = + metaslab_df_free_pct; + ks->zio_injection_enabled.value.i64 = + zio_injection_enabled; + ks->zvol_immediate_write_sz.value.i64 = + zvol_immediate_write_sz; + + ks->zfs_recover.value.i64 = + zfs_recover; + + ks->zfs_free_bpobj_enabled.value.i64 = + zfs_free_bpobj_enabled; + + ks->zfs_send_corrupt_data.value.ui64 = + zfs_send_corrupt_data; + ks->zfs_send_queue_length.value.ui64 = + zfs_send_queue_length; + ks->zfs_recv_queue_length.value.ui64 = + zfs_recv_queue_length; + + ks->zvol_inhibit_dev.value.ui64 = + zvol_inhibit_dev; + ks->zfs_send_set_freerecords_bit.value.ui64 = + zfs_send_set_freerecords_bit; + + ks->zfs_write_implies_delete_child.value.ui64 = + zfs_write_implies_delete_child; + ks->zfs_send_holes_without_birth_time.value.ui64 = + send_holes_without_birth_time; + + ks->dbuf_cache_max_bytes.value.ui64 = dbuf_cache_max_bytes; + + ks->zfs_vdev_queue_depth_pct.value.ui64 = + zfs_vdev_queue_depth_pct; + ks->zio_dva_throttle_enabled.value.ui64 = + (uint64_t)zio_dva_throttle_enabled; + + ks->zfs_lua_max_instrlimit.value.ui64 = zfs_lua_max_instrlimit; + ks->zfs_lua_max_memlimit.value.ui64 = zfs_lua_max_memlimit; + + ks->zfs_trim_extent_bytes_max.value.ui64 = + zfs_trim_extent_bytes_max; + ks->zfs_trim_extent_bytes_min.value.ui64 = + zfs_trim_extent_bytes_min; + ks->zfs_trim_metaslab_skip.value.ui64 = + zfs_trim_metaslab_skip; + ks->zfs_trim_txg_batch.value.ui64 = + zfs_trim_txg_batch; + ks->zfs_trim_queue_limit.value.ui64 = + zfs_trim_queue_limit; + + ks->zfs_send_unmodified_spill_blocks.value.ui64 = + zfs_send_unmodified_spill_blocks; + ks->zfs_special_class_metadata_reserve_pct.value.ui64 = + zfs_special_class_metadata_reserve_pct; + + vdev_raidz_impl_get(vdev_raidz_string, + sizeof (vdev_raidz_string)); + kstat_named_setstr(&ks->zfs_vdev_raidz_impl, vdev_raidz_string); + + gcm_impl_get(icp_gcm_string, sizeof (icp_gcm_string)); + kstat_named_setstr(&ks->icp_gcm_impl, icp_gcm_string); + + aes_impl_get(icp_aes_string, sizeof (icp_aes_string)); + kstat_named_setstr(&ks->icp_aes_impl, icp_aes_string); + + fletcher_4_get(zfs_fletcher_4_string, + sizeof (zfs_fletcher_4_string)); + kstat_named_setstr(&ks->zfs_fletcher_4_impl, + zfs_fletcher_4_string); + + ks->zfs_expire_snapshot.value.ui64 = zfs_expire_snapshot; + ks->zfs_admin_snapshot.value.ui64 = zfs_admin_snapshot; + ks->zfs_auto_snapshot.value.ui64 = zfs_auto_snapshot; + + ks->zfs_spa_discard_memory_limit.value.ui64 = + zfs_spa_discard_memory_limit; + ks->zfs_async_block_max_blocks.value.ui64 = + zfs_async_block_max_blocks; + ks->zfs_initialize_chunk_size.value.ui64 = + zfs_initialize_chunk_size; + ks->zfs_scan_suspend_progress.value.ui64 = + zfs_scan_suspend_progress; + ks->zfs_livelist_max_entries.value.ui64 = + zfs_livelist_max_entries; + + ks->zfs_allow_redacted_dataset_mount.value.ui64 = + zfs_allow_redacted_dataset_mount; + ks->zfs_checksum_events_per_second.value.ui64 = + zfs_checksum_events_per_second; + ks->zfs_commit_timeout_pct.value.ui64 = zfs_commit_timeout_pct; + ks->zfs_compressed_arc_enabled.value.ui64 = + zfs_compressed_arc_enabled; + ks->zfs_condense_indirect_commit_entry_delay_ms.value.ui64 = + zfs_condense_indirect_commit_entry_delay_ms; + ks->zfs_condense_min_mapping_bytes.value.ui64 = + zfs_condense_min_mapping_bytes; + ks->zfs_deadman_checktime_ms.value.ui64 = + zfs_deadman_checktime_ms; + + kstat_named_setstr(&ks->zfs_deadman_failmode, + zfs_deadman_failmode); + + ks->zfs_deadman_synctime_ms.value.ui64 = + zfs_deadman_synctime_ms; + ks->zfs_deadman_ziotime_ms.value.ui64 = zfs_deadman_ziotime_ms; + ks->zfs_disable_ivset_guid_check.value.ui64 = + zfs_disable_ivset_guid_check; + ks->zfs_initialize_value.value.ui64 = zfs_initialize_value; + ks->zfs_keep_log_spacemaps_at_export.value.ui64 = + zfs_keep_log_spacemaps_at_export; + ks->l2arc_rebuild_blocks_min_l2size.value.ui64 = + l2arc_rebuild_blocks_min_l2size; + ks->l2arc_rebuild_enabled.value.ui64 = l2arc_rebuild_enabled; + ks->l2arc_trim_ahead.value.ui64 = l2arc_trim_ahead; + ks->zfs_livelist_condense_new_alloc.value.ui64 = + zfs_livelist_condense_new_alloc; + ks->zfs_livelist_condense_sync_cancel.value.ui64 = + zfs_livelist_condense_sync_cancel; + ks->zfs_livelist_condense_sync_pause.value.ui64 = + zfs_livelist_condense_sync_pause; + ks->zfs_livelist_condense_zthr_cancel.value.ui64 = + zfs_livelist_condense_zthr_cancel; + ks->zfs_livelist_condense_zthr_pause.value.ui64 = + zfs_livelist_condense_zthr_pause; + ks->zfs_livelist_min_percent_shared.value.ui64 = + zfs_livelist_min_percent_shared; + ks->zfs_max_dataset_nesting.value.ui64 = + zfs_max_dataset_nesting; + ks->zfs_max_missing_tvds.value.ui64 = zfs_max_missing_tvds; + ks->metaslab_debug_load.value.ui64 = metaslab_debug_load; + ks->metaslab_force_ganging.value.ui64 = metaslab_force_ganging; + ks->zfs_multihost_fail_intervals.value.ui64 = + zfs_multihost_fail_intervals; + ks->zfs_multihost_import_intervals.value.ui64 = + zfs_multihost_import_intervals; + ks->zfs_multihost_interval.value.ui64 = zfs_multihost_interval; + ks->zfs_override_estimate_recordsize.value.ui64 = + zfs_override_estimate_recordsize; + ks->zfs_remove_max_segment.value.ui64 = zfs_remove_max_segment; + ks->zfs_resilver_min_time_ms.value.ui64 = + zfs_resilver_min_time_ms; + ks->zfs_scan_legacy.value.ui64 = zfs_scan_legacy; + ks->zfs_scan_vdev_limit.value.ui64 = zfs_scan_vdev_limit; + ks->zfs_slow_io_events_per_second.value.ui64 = + zfs_slow_io_events_per_second; + ks->spa_load_verify_data.value.ui64 = spa_load_verify_data; + ks->spa_load_verify_metadata.value.ui64 = + spa_load_verify_metadata; + ks->zfs_unlink_suspend_progress.value.ui64 = + zfs_unlink_suspend_progress; + ks->zfs_vdev_min_ms_count.value.ui64 = zfs_vdev_min_ms_count; + ks->vdev_validate_skip.value.ui64 = vdev_validate_skip; + ks->zfs_zevent_len_max.value.ui64 = zfs_zevent_len_max; + ks->zio_slow_io_ms.value.ui64 = zio_slow_io_ms; + + ks->l2arc_mfuonly.value.ui64 = l2arc_mfuonly; + ks->zfs_multihost_history.value.ui64 = zfs_multihost_history; + ks->zfs_rebuild_scrub_enabled.value.ui64 = + zfs_rebuild_scrub_enabled; + ks->zfs_txg_history.value.ui64 = zfs_txg_history; + ks->vdev_file_physical_ashift.value.ui64 = + vdev_file_physical_ashift; + ks->zvol_volmode.value.ui64 = zvol_volmode; + ks->zfs_zevent_retain_max.value.ui64 = zfs_zevent_retain_max; + + } + + return (0); +} + + + +int +kstat_osx_init(void) +{ + osx_kstat_ksp = kstat_create("zfs", 0, "tunable", "darwin", + KSTAT_TYPE_NAMED, sizeof (osx_kstat) / sizeof (kstat_named_t), + KSTAT_FLAG_VIRTUAL|KSTAT_FLAG_WRITABLE); + + if (osx_kstat_ksp != NULL) { + osx_kstat_ksp->ks_data = &osx_kstat; + osx_kstat_ksp->ks_update = osx_kstat_update; + kstat_install(osx_kstat_ksp); + } + + return (0); +} + +void +kstat_osx_fini(void) +{ + if (osx_kstat_ksp != NULL) { + kstat_delete(osx_kstat_ksp); + osx_kstat_ksp = NULL; + } +} diff --git a/module/os/macos/zfs/zfs_osx.cpp b/module/os/macos/zfs/zfs_osx.cpp new file mode 100644 index 000000000000..390a3b3359b3 --- /dev/null +++ b/module/os/macos/zfs/zfs_osx.cpp @@ -0,0 +1,309 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2013-2020, Jorgen Lundman. All rights reserved. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +// Define the superclass. +#define super IOService + +OSDefineMetaClassAndStructors(org_openzfsonosx_zfs_zvol, IOService) + +extern "C" { + +#include +#include +#include + +extern SInt32 zfs_active_fs_count; + +#ifdef DEBUG +#define ZFS_DEBUG_STR " (DEBUG mode)" +#else +#define ZFS_DEBUG_STR "" +#endif + +static char spl_gitrev[64] = ZFS_META_VERSION "-" ZFS_META_RELEASE; + +SYSCTL_DECL(_zfs); +SYSCTL_NODE(, OID_AUTO, zfs, CTLFLAG_RD, 0, ""); +SYSCTL_STRING(_zfs, OID_AUTO, kext_version, + CTLFLAG_RD | CTLFLAG_LOCKED, + spl_gitrev, 0, "ZFS KEXT Version"); + + +extern kern_return_t _start(kmod_info_t *ki, void *data); +extern kern_return_t _stop(kmod_info_t *ki, void *data); + +__attribute__((visibility("default"))) KMOD_EXPLICIT_DECL(org.openzfsonosx.zfs, + "1.0.0", _start, _stop) +kmod_start_func_t *_realmain = 0; +kmod_stop_func_t *_antimain = 0; +int _kext_apple_cc = __APPLE_CC__; + +} // Extern "C" + +bool +org_openzfsonosx_zfs_zvol::init(OSDictionary* dict) +{ + bool res; + + /* Need an OSSet for open clients */ + _openClients = OSSet::withCapacity(1); + if (_openClients == NULL) { + dprintf("client OSSet failed"); + return (false); + } + + res = super::init(dict); + + // IOLog("ZFS::init\n"); + return (res); +} + +void +org_openzfsonosx_zfs_zvol::free(void) +{ + OSSafeReleaseNULL(_openClients); + + // IOLog("ZFS::free\n"); + super::free(); +} + +bool +org_openzfsonosx_zfs_zvol::isOpen(const IOService *forClient) const +{ + bool ret; + ret = IOService::isOpen(forClient); + return (ret); +} + +bool +org_openzfsonosx_zfs_zvol::handleOpen(IOService *client, + IOOptionBits options, void *arg) +{ + bool ret = true; + + dprintf(""); + + _openClients->setObject(client); + ret = _openClients->containsObject(client); + + return (ret); +} + +bool +org_openzfsonosx_zfs_zvol::handleIsOpen(const IOService *client) const +{ + bool ret; + + dprintf(""); + + ret = _openClients->containsObject(client); + + return (ret); +} + +void +org_openzfsonosx_zfs_zvol::handleClose(IOService *client, + IOOptionBits options) +{ + dprintf(""); + + if (_openClients->containsObject(client) == false) { + dprintf("not open"); + } + + _openClients->removeObject(client); +} + +IOService* +org_openzfsonosx_zfs_zvol::probe(IOService *provider, SInt32 *score) +{ + IOService *res = super::probe(provider, score); + return (res); +} + + +/* + * + * ************************************************************************ + * + * Kernel Module Load + * + * ************************************************************************ + * + */ + +bool +org_openzfsonosx_zfs_zvol::start(IOService *provider) +{ + bool res = super::start(provider); + + IOLog("ZFS: Loading module ... \n"); + + if (!res) + return (res); + + /* Fire up all SPL modules and threads */ + spl_start(NULL, NULL); + + /* registerService() allows zconfigd to match against the service */ + this->registerService(); + + /* + * hostid is left as 0 on OSX, and left to be set if developers wish to + * use it. If it is 0, we will hash the hardware.uuid into a 32 bit + * value and set the hostid. + */ + if (!zone_get_hostid(NULL)) { + uint32_t myhostid = 0; + IORegistryEntry *ioregroot = + IORegistryEntry::getRegistryRoot(); + if (ioregroot) { + IORegistryEntry *macmodel = + ioregroot->getChildEntry(gIOServicePlane); + + if (macmodel) { + OSObject *ioplatformuuidobj; + ioplatformuuidobj = + macmodel->getProperty(kIOPlatformUUIDKey); + if (ioplatformuuidobj) { + OSString *ioplatformuuidstr = + OSDynamicCast(OSString, + ioplatformuuidobj); + + myhostid = fnv_32a_str( + ioplatformuuidstr-> + getCStringNoCopy(), + FNV1_32A_INIT); + + sysctlbyname("kern.hostid", NULL, NULL, + &myhostid, sizeof (myhostid)); + printf("ZFS: hostid set to %08x from " + "UUID '%s'\n", myhostid, + ioplatformuuidstr-> + getCStringNoCopy()); + } + } + } + } + + /* Register ZFS KEXT Version sysctl - separate to kstats */ + sysctl_register_oid(&sysctl__zfs); + sysctl_register_oid(&sysctl__zfs_kext_version); + + /* Init LDI */ + int error = 0; + error = ldi_init(NULL); + if (error) { + IOLog("%s ldi_init error %d\n", __func__, error); + goto failure; + } + + /* Start ZFS itself */ + zfs_kmod_init(); + + /* Register fs with XNU */ + zfs_vfsops_init(); + + /* + * When is the best time to start the system_taskq? It is strictly + * speaking not used by SPL, but by ZFS. ZFS should really start it? + */ + system_taskq_init(); + + res = zfs_boot_init((IOService *)this); + + printf("ZFS: Loaded module v%s-%s%s, " + "ZFS pool version %s, ZFS filesystem version %s\n", + ZFS_META_VERSION, ZFS_META_RELEASE, ZFS_DEBUG_STR, + SPA_VERSION_STRING, ZPL_VERSION_STRING); + + return (true); + +failure: + spl_stop(NULL, NULL); + sysctl_unregister_oid(&sysctl__zfs_kext_version); + sysctl_unregister_oid(&sysctl__zfs); + return (false); +} + +/* Here we are, at the end of all things */ +void +org_openzfsonosx_zfs_zvol::stop(IOService *provider) +{ + + zfs_boot_fini(); + + IOLog("ZFS: Attempting to unload ...\n"); + + super::stop(provider); + + zfs_vfsops_fini(); + + zfs_kmod_fini(); + + system_taskq_fini(); + + ldi_fini(); + + sysctl_unregister_oid(&sysctl__zfs_kext_version); + sysctl_unregister_oid(&sysctl__zfs); + + spl_stop(NULL, NULL); + + printf("ZFS: Unloaded module v%s-%s%s\n", + ZFS_META_VERSION, ZFS_META_RELEASE, ZFS_DEBUG_STR); + + /* + * There is no way to ensure all threads have actually got to the + * thread_exit() call, before we exit here (and XNU unloads all + * memory for the KEXT). So we increase the odds of that happening + * by delaying a little bit before we return to XNU. Quite possibly + * the worst "solution" but Apple has not given any good options. + */ + delay(hz*5); +} diff --git a/module/os/macos/zfs/zfs_racct.c b/module/os/macos/zfs/zfs_racct.c new file mode 100644 index 000000000000..7b5d510c303f --- /dev/null +++ b/module/os/macos/zfs/zfs_racct.c @@ -0,0 +1,32 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#include + +void +zfs_racct_read(uint64_t size, uint64_t iops) +{ +} + +void +zfs_racct_write(uint64_t size, uint64_t iops) +{ +} diff --git a/module/os/macos/zfs/zfs_vfsops.c b/module/os/macos/zfs/zfs_vfsops.c new file mode 100644 index 000000000000..316f0ce6ddb4 --- /dev/null +++ b/module/os/macos/zfs/zfs_vfsops.c @@ -0,0 +1,3001 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011 Pawel Jakub Dawidek . + * Copyright (c) 2012, 2015 by Delphix. All rights reserved. + * Copyright 2016 Nexenta Systems, Inc. All rights reserved. + */ + +/* Portions Copyright 2010 Robert Milkowski */ +/* Portions Copyright 2013,2020 Jorgen Lundman */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zfs_comutil.h" + +#include +#include +#include +#include +#include +#include + +unsigned int zfs_vnop_skip_unlinked_drain = 0; + +int zfs_module_start(kmod_info_t *ki, void *data); +int zfs_module_stop(kmod_info_t *ki, void *data); +extern int getzfsvfs(const char *dsname, zfsvfs_t **zfvp); + +void arc_os_init(void); +void arc_os_fini(void); + +/* + * AVL tree of hardlink entries, which we need to map for Finder. The va_linkid + * needs to be unique for each hardlink target, as well as, return the znode + * in vget(va_linkid). Unfortunately, the va_linkid is 32bit (lost in the + * syscall translation to userland struct). We sort the AVL tree by + * -> directory id + * -> z_id + * -> name + * + */ +static int hardlinks_compare(const void *arg1, const void *arg2) +{ + const hardlinks_t *node1 = arg1; + const hardlinks_t *node2 = arg2; + int value; + if (node1->hl_parent > node2->hl_parent) + return (1); + if (node1->hl_parent < node2->hl_parent) + return (-1); + if (node1->hl_fileid > node2->hl_fileid) + return (1); + if (node1->hl_fileid < node2->hl_fileid) + return (-1); + + value = strncmp(node1->hl_name, node2->hl_name, PATH_MAX); + if (value < 0) + return (-1); + if (value > 0) + return (1); + return (0); +} + +/* + * Lookup same information from linkid, to get at parentid, objid and name + */ +static int hardlinks_compare_linkid(const void *arg1, const void *arg2) +{ + const hardlinks_t *node1 = arg1; + const hardlinks_t *node2 = arg2; + if (node1->hl_linkid > node2->hl_linkid) + return (1); + if (node1->hl_linkid < node2->hl_linkid) + return (-1); + return (0); +} + +extern int +zfs_obtain_xattr(znode_t *, const char *, mode_t, cred_t *, vnode_t **, int); + + +/* + * We need to keep a count of active fs's. + * This is necessary to prevent our kext + * from being unloaded after a umount -f + */ +uint32_t zfs_active_fs_count = 0; + +extern void zfs_ioctl_init(void); +extern void zfs_ioctl_fini(void); + +int +zfs_is_readonly(zfsvfs_t *zfsvfs) +{ + return (!!(vfs_isrdonly(zfsvfs->z_vfs))); +} + +/* + * The OS sync ignored by default, as ZFS handles internal periodic + * syncs. (As per illumos) Unfortunately, we can not tell the difference + * of when users run "sync" by hand. Sync is called on umount though. + */ +uint64_t zfs_vfs_sync_paranoia = 0; + +int +zfs_vfs_sync(struct mount *vfsp, __unused int waitfor, + __unused vfs_context_t context) +{ + /* + * Data integrity is job one. We don't want a compromised kernel + * writing to the storage pool, so we never sync during panic. + */ + if (spl_panicstr()) + return (0); + + /* Check if sysctl setting wants sync - and we are not unmounting */ + if (zfs_vfs_sync_paranoia == 0 && + !vfs_isunmount(vfsp)) + return (0); + + if (vfsp != NULL) { + /* + * Sync a specific filesystem. + */ + zfsvfs_t *zfsvfs = vfs_fsprivate(vfsp); + dsl_pool_t *dp; + + ZFS_ENTER(zfsvfs); + dp = dmu_objset_pool(zfsvfs->z_os); + + /* + * If the system is shutting down, then skip any + * filesystems which may exist on a suspended pool. + */ + if (spl_system_inshutdown() && spa_suspended(dp->dp_spa)) { + ZFS_EXIT(zfsvfs); + return (0); + } + + if (zfsvfs->z_log != NULL) + zil_commit(zfsvfs->z_log, 0); + + ZFS_EXIT(zfsvfs); + + } else { + /* + * Sync all ZFS filesystems. This is what happens when you + * run sync(1M). Unlike other filesystems, ZFS honors the + * request by waiting for all pools to commit all dirty data. + */ + spa_sync_allpools(); + } + + return (0); +} + +static void +atime_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + + if (newval == B_TRUE) { + zfsvfs->z_atime = B_TRUE; + vfs_clearflags(zfsvfs->z_vfs, (uint64_t)MNT_NOATIME); + } else { + zfsvfs->z_atime = B_FALSE; + vfs_setflags(zfsvfs->z_vfs, (uint64_t)MNT_NOATIME); + } +} + +static void +xattr_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + + /* + * Apple does have MNT_NOUSERXATTR mount option, but unfortunately + * the VFS layer returns EACCESS if xattr access is attempted. + * Finder etc, will do so, even if filesystem capabilities is set + * without xattr, rendering the mount option useless. We no longer + * set it, and handle xattrs being disabled internally. + */ + + if (newval == ZFS_XATTR_OFF) { + zfsvfs->z_xattr = B_FALSE; + // vfs_setflags(zfsvfs->z_vfs, (uint64_t)MNT_NOUSERXATTR); + } else { + zfsvfs->z_xattr = B_TRUE; + // vfs_clearflags(zfsvfs->z_vfs, (uint64_t)MNT_NOUSERXATTR); + + if (newval == ZFS_XATTR_SA) + zfsvfs->z_xattr_sa = B_TRUE; + else + zfsvfs->z_xattr_sa = B_FALSE; + } +} + +static void +blksz_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + ASSERT3U(newval, <=, spa_maxblocksize(dmu_objset_spa(zfsvfs->z_os))); + ASSERT3U(newval, >=, SPA_MINBLOCKSIZE); + ASSERT(ISP2(newval)); + + zfsvfs->z_max_blksz = newval; + // zfsvfs->z_vfs->mnt_stat.f_iosize = newval; +} + +static void +readonly_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + if (newval == B_TRUE) { + vfs_setflags(zfsvfs->z_vfs, (uint64_t)MNT_RDONLY); + } else { + vfs_clearflags(zfsvfs->z_vfs, (uint64_t)MNT_RDONLY); + } +} + +static void +devices_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + if (newval == B_FALSE) { + vfs_setflags(zfsvfs->z_vfs, (uint64_t)MNT_NODEV); + } else { + vfs_clearflags(zfsvfs->z_vfs, (uint64_t)MNT_NODEV); + } +} + +static void +setuid_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + if (newval == B_FALSE) { + vfs_setflags(zfsvfs->z_vfs, (uint64_t)MNT_NOSUID); + } else { + vfs_clearflags(zfsvfs->z_vfs, (uint64_t)MNT_NOSUID); + } +} + +static void +exec_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + if (newval == B_FALSE) { + vfs_setflags(zfsvfs->z_vfs, (uint64_t)MNT_NOEXEC); + } else { + vfs_clearflags(zfsvfs->z_vfs, (uint64_t)MNT_NOEXEC); + } +} + +static void +snapdir_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + zfsvfs->z_show_ctldir = newval; + cache_purgevfs(zfsvfs->z_vfs); +} + +static void +vscan_changed_cb(void *arg, uint64_t newval) +{ + // zfsvfs_t *zfsvfs = arg; + // zfsvfs->z_vscan = newval; +} + +static void +acl_mode_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + zfsvfs->z_acl_mode = newval; +} + +static void +acl_inherit_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + + zfsvfs->z_acl_inherit = newval; +} + +static void +finderbrowse_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + if (newval == B_FALSE) { + vfs_setflags(zfsvfs->z_vfs, (uint64_t)MNT_DONTBROWSE); + } else { + vfs_clearflags(zfsvfs->z_vfs, (uint64_t)MNT_DONTBROWSE); + } +} +static void +ignoreowner_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + if (newval == B_FALSE) { + vfs_clearflags(zfsvfs->z_vfs, (uint64_t)MNT_IGNORE_OWNERSHIP); + } else { + vfs_setflags(zfsvfs->z_vfs, (uint64_t)MNT_IGNORE_OWNERSHIP); + } +} + +static void +mimic_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + struct vfsstatfs *vfsstatfs; + vfsstatfs = vfs_statfs(zfsvfs->z_vfs); + + if (newval == 0) { + strlcpy(vfsstatfs->f_fstypename, "zfs", MFSTYPENAMELEN); + } else { + strlcpy(vfsstatfs->f_fstypename, "hfs", MFSTYPENAMELEN); + } +} + +static int +zfs_register_callbacks(struct mount *vfsp) +{ + struct dsl_dataset *ds = NULL; + + objset_t *os = NULL; + zfsvfs_t *zfsvfs = NULL; + boolean_t readonly = B_FALSE; + boolean_t do_readonly = B_FALSE; + boolean_t setuid = B_FALSE; + boolean_t do_setuid = B_FALSE; + boolean_t exec = B_FALSE; + boolean_t do_exec = B_FALSE; + boolean_t devices = B_FALSE; + boolean_t do_devices = B_FALSE; + boolean_t xattr = B_FALSE; + boolean_t do_xattr = B_FALSE; + boolean_t atime = B_FALSE; + boolean_t do_atime = B_FALSE; + boolean_t finderbrowse = B_FALSE; + boolean_t do_finderbrowse = B_FALSE; + boolean_t ignoreowner = B_FALSE; + boolean_t do_ignoreowner = B_FALSE; + int error = 0; + + ASSERT(vfsp); + zfsvfs = vfs_fsprivate(vfsp); + ASSERT(zfsvfs); + os = zfsvfs->z_os; + + /* + * This function can be called for a snapshot when we update snapshot's + * mount point, which isn't really supported. + */ + if (dmu_objset_is_snapshot(os)) + return (EOPNOTSUPP); + + /* + * The act of registering our callbacks will destroy any mount + * options we may have. In order to enable temporary overrides + * of mount options, we stash away the current values and + * restore them after we register the callbacks. + */ +#define vfs_optionisset(X, Y, Z) (vfs_flags(X)&(Y)) + + if (vfs_optionisset(vfsp, MNT_RDONLY, NULL) || + !spa_writeable(dmu_objset_spa(os))) { + readonly = B_TRUE; + do_readonly = B_TRUE; + } + if (vfs_optionisset(vfsp, MNT_NODEV, NULL)) { + devices = B_FALSE; + do_devices = B_TRUE; + } + /* xnu SETUID, not IllumOS SUID */ + if (vfs_optionisset(vfsp, MNT_NOSUID, NULL)) { + setuid = B_FALSE; + do_setuid = B_TRUE; + } + if (vfs_optionisset(vfsp, MNT_NOEXEC, NULL)) { + exec = B_FALSE; + do_exec = B_TRUE; + } + if (vfs_optionisset(vfsp, MNT_NOUSERXATTR, NULL)) { + xattr = B_FALSE; + do_xattr = B_TRUE; + } + if (vfs_optionisset(vfsp, MNT_NOATIME, NULL)) { + atime = B_FALSE; + do_atime = B_TRUE; + } + if (vfs_optionisset(vfsp, MNT_DONTBROWSE, NULL)) { + finderbrowse = B_FALSE; + do_finderbrowse = B_TRUE; + } + if (vfs_optionisset(vfsp, MNT_IGNORE_OWNERSHIP, NULL)) { + ignoreowner = B_TRUE; + do_ignoreowner = B_TRUE; + } + + /* + * nbmand is a special property. It can only be changed at + * mount time. + * + * This is weird, but it is documented to only be changeable + * at mount time. + */ + + /* + * Register property callbacks. + * + * It would probably be fine to just check for i/o error from + * the first prop_register(), but I guess I like to go + * overboard... + */ + ds = dmu_objset_ds(os); + dsl_pool_config_enter(dmu_objset_pool(os), FTAG); + error = dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_ATIME), atime_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_XATTR), xattr_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_RECORDSIZE), blksz_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_READONLY), readonly_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_DEVICES), devices_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_SETUID), setuid_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_EXEC), exec_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_SNAPDIR), snapdir_changed_cb, zfsvfs); + // This appears to be PROP_PRIVATE, investigate if we want this + // ZOL calls this ACLTYPE + error = error ? error : dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_ACLMODE), acl_mode_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_ACLINHERIT), acl_inherit_changed_cb, + zfsvfs); + error = error ? error : dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_VSCAN), vscan_changed_cb, zfsvfs); + + error = error ? error : dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_BROWSE), finderbrowse_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_IGNOREOWNER), + ignoreowner_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_MIMIC), mimic_changed_cb, zfsvfs); + + dsl_pool_config_exit(dmu_objset_pool(os), FTAG); + if (error) + goto unregister; + + /* + * Invoke our callbacks to restore temporary mount options. + */ + if (do_readonly) + readonly_changed_cb(zfsvfs, readonly); + if (do_setuid) + setuid_changed_cb(zfsvfs, setuid); + if (do_exec) + exec_changed_cb(zfsvfs, exec); + if (do_devices) + devices_changed_cb(zfsvfs, devices); + if (do_xattr) + xattr_changed_cb(zfsvfs, xattr); + if (do_atime) + atime_changed_cb(zfsvfs, atime); + + if (do_finderbrowse) + finderbrowse_changed_cb(zfsvfs, finderbrowse); + if (do_ignoreowner) + ignoreowner_changed_cb(zfsvfs, ignoreowner); + + return (0); + +unregister: + dsl_prop_unregister_all(ds, zfsvfs); + return (error); +} + +/* + * Takes a dataset, a property, a value and that value's setpoint as + * found in the ZAP. Checks if the property has been changed in the vfs. + * If so, val and setpoint will be overwritten with updated content. + * Otherwise, they are left unchanged. + */ +int +zfs_get_temporary_prop(dsl_dataset_t *ds, zfs_prop_t zfs_prop, uint64_t *val, + char *setpoint) +{ + int error; + zfsvfs_t *zfvp; + mount_t vfsp; + objset_t *os; + uint64_t tmp = *val; + + error = dmu_objset_from_ds(ds, &os); + if (error != 0) + return (error); + + if (dmu_objset_type(os) != DMU_OST_ZFS) + return (EINVAL); + + mutex_enter(&os->os_user_ptr_lock); + zfvp = dmu_objset_get_user(os); + mutex_exit(&os->os_user_ptr_lock); + if (zfvp == NULL) + return (ESRCH); + + vfsp = zfvp->z_vfs; + + switch (zfs_prop) { + case ZFS_PROP_ATIME: +// if (vfsp->vfs_do_atime) +// tmp = vfsp->vfs_atime; + break; + case ZFS_PROP_RELATIME: +// if (vfsp->vfs_do_relatime) +// tmp = vfsp->vfs_relatime; + break; + case ZFS_PROP_DEVICES: +// if (vfsp->vfs_do_devices) +// tmp = vfsp->vfs_devices; + break; + case ZFS_PROP_EXEC: +// if (vfsp->vfs_do_exec) +// tmp = vfsp->vfs_exec; + break; + case ZFS_PROP_SETUID: +// if (vfsp->vfs_do_setuid) +// tmp = vfsp->vfs_setuid; + break; + case ZFS_PROP_READONLY: +// if (vfsp->vfs_do_readonly) +// tmp = vfsp->vfs_readonly; + break; + case ZFS_PROP_XATTR: +// if (vfsp->vfs_do_xattr) +// tmp = vfsp->vfs_xattr; + break; + case ZFS_PROP_NBMAND: +// if (vfsp->vfs_do_nbmand) +// tmp = vfsp->vfs_nbmand; + break; + default: + return (ENOENT); + } + + if (tmp != *val) { + (void) strlcpy(setpoint, "temporary", ZFS_MAX_DATASET_NAME_LEN); + *val = tmp; + } + return (0); +} + + +/* + * Associate this zfsvfs with the given objset, which must be owned. + * This will cache a bunch of on-disk state from the objset in the + * zfsvfs. + */ +static int +zfsvfs_init(zfsvfs_t *zfsvfs, objset_t *os) +{ + int error; + uint64_t val; + + zfsvfs->z_max_blksz = SPA_OLD_MAXBLOCKSIZE; + zfsvfs->z_show_ctldir = ZFS_SNAPDIR_VISIBLE; + zfsvfs->z_os = os; + + /* Volume status "all ok" */ + zfsvfs->z_notification_conditions = 0; + zfsvfs->z_freespace_notify_warninglimit = 0; + zfsvfs->z_freespace_notify_dangerlimit = 0; + zfsvfs->z_freespace_notify_desiredlevel = 0; + + error = zfs_get_zplprop(os, ZFS_PROP_VERSION, &zfsvfs->z_version); + if (error != 0) + return (error); + if (zfsvfs->z_version > + zfs_zpl_version_map(spa_version(dmu_objset_spa(os)))) { + (void) printf("Can't mount a version %lld file system " + "on a version %lld pool\n. Pool must be upgraded to mount " + "this file system.\n", (u_longlong_t)zfsvfs->z_version, + (u_longlong_t)spa_version(dmu_objset_spa(os))); + return (SET_ERROR(ENOTSUP)); + } + error = zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &val); + if (error != 0) + return (error); + zfsvfs->z_norm = (int)val; + + error = zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &val); + if (error != 0) + return (error); + zfsvfs->z_utf8 = (val != 0); + + error = zfs_get_zplprop(os, ZFS_PROP_CASE, &val); + if (error != 0) + return (error); + zfsvfs->z_case = (uint_t)val; + + error = zfs_get_zplprop(os, ZFS_PROP_ACLMODE, &val); + if (error != 0) + return (error); + zfsvfs->z_acl_mode = (uint_t)val; + + zfs_get_zplprop(os, ZFS_PROP_LASTUNMOUNT, &val); + zfsvfs->z_last_unmount_time = val; + + /* + * Fold case on file systems that are always or sometimes case + * insensitive. + */ + if (zfsvfs->z_case == ZFS_CASE_INSENSITIVE || + zfsvfs->z_case == ZFS_CASE_MIXED) + zfsvfs->z_norm |= U8_TEXTPREP_TOUPPER; + + zfsvfs->z_use_fuids = USE_FUIDS(zfsvfs->z_version, zfsvfs->z_os); + zfsvfs->z_use_sa = USE_SA(zfsvfs->z_version, zfsvfs->z_os); + + uint64_t sa_obj = 0; + if (zfsvfs->z_use_sa) { + /* should either have both of these objects or none */ + error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_SA_ATTRS, 8, 1, + &sa_obj); + + if (error != 0) + return (error); + + error = zfs_get_zplprop(os, ZFS_PROP_XATTR, &val); + if ((error == 0) && (val == ZFS_XATTR_SA)) + zfsvfs->z_xattr_sa = B_TRUE; + } + + error = sa_setup(os, sa_obj, zfs_attr_table, ZPL_END, + &zfsvfs->z_attr_table); + if (error != 0) + return (error); + + if (zfsvfs->z_version >= ZPL_VERSION_SA) + sa_register_update_callback(os, zfs_sa_upgrade); + + error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_ROOT_OBJ, 8, 1, + &zfsvfs->z_root); + if (error != 0) + return (error); + ASSERT(zfsvfs->z_root != 0); + + error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_UNLINKED_SET, 8, 1, + &zfsvfs->z_unlinkedobj); + if (error != 0) + return (error); + + error = zap_lookup(os, MASTER_NODE_OBJ, + zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA], + 8, 1, &zfsvfs->z_userquota_obj); + if (error == ENOENT) + zfsvfs->z_userquota_obj = 0; + else if (error != 0) + return (error); + + error = zap_lookup(os, MASTER_NODE_OBJ, + zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA], + 8, 1, &zfsvfs->z_groupquota_obj); + if (error == ENOENT) + zfsvfs->z_groupquota_obj = 0; + else if (error != 0) + return (error); + + error = zap_lookup(os, MASTER_NODE_OBJ, + zfs_userquota_prop_prefixes[ZFS_PROP_PROJECTQUOTA], + 8, 1, &zfsvfs->z_projectquota_obj); + if (error == ENOENT) + zfsvfs->z_projectquota_obj = 0; + else if (error != 0) + return (error); + + error = zap_lookup(os, MASTER_NODE_OBJ, + zfs_userquota_prop_prefixes[ZFS_PROP_USEROBJQUOTA], + 8, 1, &zfsvfs->z_userobjquota_obj); + if (error == ENOENT) + zfsvfs->z_userobjquota_obj = 0; + else if (error != 0) + return (error); + + error = zap_lookup(os, MASTER_NODE_OBJ, + zfs_userquota_prop_prefixes[ZFS_PROP_GROUPOBJQUOTA], + 8, 1, &zfsvfs->z_groupobjquota_obj); + if (error == ENOENT) + zfsvfs->z_groupobjquota_obj = 0; + else if (error != 0) + return (error); + + error = zap_lookup(os, MASTER_NODE_OBJ, + zfs_userquota_prop_prefixes[ZFS_PROP_PROJECTOBJQUOTA], + 8, 1, &zfsvfs->z_projectobjquota_obj); + if (error == ENOENT) + zfsvfs->z_projectobjquota_obj = 0; + else if (error != 0) + return (error); + + error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_FUID_TABLES, 8, 1, + &zfsvfs->z_fuid_obj); + if (error == ENOENT) + zfsvfs->z_fuid_obj = 0; + else if (error != 0) + return (error); + + error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_SHARES_DIR, 8, 1, + &zfsvfs->z_shares_dir); + if (error == ENOENT) + zfsvfs->z_shares_dir = 0; + else if (error != 0) + return (error); + + return (0); +} + +int +zfsvfs_create(const char *osname, boolean_t readonly, zfsvfs_t **zfvp) +{ + objset_t *os; + zfsvfs_t *zfsvfs; + int error; + + zfsvfs = kmem_zalloc(sizeof (zfsvfs_t), KM_SLEEP); + + /* + * We claim to always be readonly so we can open snapshots; + * other ZPL code will prevent us from writing to snapshots. + */ + error = dmu_objset_own(osname, DMU_OST_ZFS, B_TRUE, B_TRUE, + zfsvfs, &os); + if (error != 0) { + kmem_free(zfsvfs, sizeof (zfsvfs_t)); + return (error); + } + + error = zfsvfs_create_impl(zfvp, zfsvfs, os); + if (error != 0) { + dmu_objset_disown(os, B_TRUE, zfsvfs); + } + return (error); +} + +int +zfsvfs_create_impl(zfsvfs_t **zfvp, zfsvfs_t *zfsvfs, objset_t *os) +{ + int error; + + zfsvfs->z_vfs = NULL; + zfsvfs->z_parent = zfsvfs; + + mutex_init(&zfsvfs->z_znodes_lock, NULL, MUTEX_DEFAULT, NULL); + mutex_init(&zfsvfs->z_lock, NULL, MUTEX_DEFAULT, NULL); + list_create(&zfsvfs->z_all_znodes, sizeof (znode_t), + offsetof(znode_t, z_link_node)); + + zfsvfs->z_ctldir_startid = ZFSCTL_INO_SNAPDIRS; + + rrm_init(&zfsvfs->z_teardown_lock, B_FALSE); + + rw_init(&zfsvfs->z_teardown_inactive_lock, NULL, RW_DEFAULT, NULL); + rw_init(&zfsvfs->z_fuid_lock, NULL, RW_DEFAULT, NULL); + + int size = MIN(1 << (highbit64(zfs_object_mutex_size) - 1), + ZFS_OBJ_MTX_MAX); + zfsvfs->z_hold_size = size; + zfsvfs->z_hold_trees = vmem_zalloc(sizeof (avl_tree_t) * size, + KM_SLEEP); + zfsvfs->z_hold_locks = vmem_zalloc(sizeof (kmutex_t) * size, KM_SLEEP); + for (int i = 0; i != size; i++) { + avl_create(&zfsvfs->z_hold_trees[i], zfs_znode_hold_compare, + sizeof (znode_hold_t), offsetof(znode_hold_t, zh_node)); + mutex_init(&zfsvfs->z_hold_locks[i], NULL, MUTEX_DEFAULT, NULL); + } + + rw_init(&zfsvfs->z_hardlinks_lock, NULL, RW_DEFAULT, NULL); + avl_create(&zfsvfs->z_hardlinks, hardlinks_compare, + sizeof (hardlinks_t), offsetof(hardlinks_t, hl_node)); + avl_create(&zfsvfs->z_hardlinks_linkid, hardlinks_compare_linkid, + sizeof (hardlinks_t), offsetof(hardlinks_t, hl_node_linkid)); + zfsvfs->z_rdonly = 0; + + mutex_init(&zfsvfs->z_drain_lock, NULL, MUTEX_DEFAULT, NULL); + cv_init(&zfsvfs->z_drain_cv, NULL, CV_DEFAULT, NULL); + + error = zfsvfs_init(zfsvfs, os); + if (error != 0) { + *zfvp = NULL; + kmem_free(zfsvfs, sizeof (zfsvfs_t)); + return (error); + } + + *zfvp = zfsvfs; + return (0); +} + +static int +zfsvfs_setup(zfsvfs_t *zfsvfs, boolean_t mounting) +{ + int error; + boolean_t readonly = vfs_isrdonly(zfsvfs->z_vfs); + + error = zfs_register_callbacks(zfsvfs->z_vfs); + if (error) + return (error); + + zfsvfs->z_log = zil_open(zfsvfs->z_os, zfs_get_data); + + /* + * If we are not mounting (ie: online recv), then we don't + * have to worry about replaying the log as we blocked all + * operations out since we closed the ZIL. + */ + if (mounting) { + + ASSERT3P(zfsvfs->z_kstat.dk_kstats, ==, NULL); + dataset_kstats_create(&zfsvfs->z_kstat, zfsvfs->z_os); + + /* + * During replay we remove the read only flag to + * allow replays to succeed. + */ + + if (readonly != 0) + readonly_changed_cb(zfsvfs, B_FALSE); + else { + zap_stats_t zs; + if (zap_get_stats(zfsvfs->z_os, zfsvfs->z_unlinkedobj, + &zs) == 0) { + dataset_kstats_update_nunlinks_kstat( + &zfsvfs->z_kstat, zs.zs_num_entries); + dprintf_ds(zfsvfs->z_os->os_dsl_dataset, + "num_entries in unlinked set: %llu", + zs.zs_num_entries); + } + + if (!zfs_vnop_skip_unlinked_drain) + zfs_unlinked_drain(zfsvfs); + dsl_dir_t *dd = zfsvfs->z_os->os_dsl_dataset->ds_dir; + dd->dd_activity_cancelled = B_FALSE; + } + + /* + * Parse and replay the intent log. + * + * Because of ziltest, this must be done after + * zfs_unlinked_drain(). (Further note: ziltest + * doesn't use readonly mounts, where + * zfs_unlinked_drain() isn't called.) This is because + * ziltest causes spa_sync() to think it's committed, + * but actually it is not, so the intent log contains + * many txg's worth of changes. + * + * In particular, if object N is in the unlinked set in + * the last txg to actually sync, then it could be + * actually freed in a later txg and then reallocated + * in a yet later txg. This would write a "create + * object N" record to the intent log. Normally, this + * would be fine because the spa_sync() would have + * written out the fact that object N is free, before + * we could write the "create object N" intent log + * record. + * + * But when we are in ziltest mode, we advance the "open + * txg" without actually spa_sync()-ing the changes to + * disk. So we would see that object N is still + * allocated and in the unlinked set, and there is an + * intent log record saying to allocate it. + */ + if (spa_writeable(dmu_objset_spa(zfsvfs->z_os))) { + if (zil_replay_disable) { + zil_destroy(zfsvfs->z_log, B_FALSE); + } else { + zfsvfs->z_replay = B_TRUE; + zil_replay(zfsvfs->z_os, zfsvfs, + zfs_replay_vector); + zfsvfs->z_replay = B_FALSE; + } + } + + /* restore readonly bit */ + if (readonly != 0) + readonly_changed_cb(zfsvfs, B_TRUE); + } + + /* + * Set the objset user_ptr to track its zfsvfs. + */ + mutex_enter(&zfsvfs->z_os->os_user_ptr_lock); + dmu_objset_set_user(zfsvfs->z_os, zfsvfs); + mutex_exit(&zfsvfs->z_os->os_user_ptr_lock); + + return (0); +} + +extern krwlock_t zfsvfs_lock; /* in zfs_znode.c */ + +void +zfsvfs_free(zfsvfs_t *zfsvfs) +{ + int i, size = zfsvfs->z_hold_size; + + zfs_fuid_destroy(zfsvfs); + + cv_destroy(&zfsvfs->z_drain_cv); + mutex_destroy(&zfsvfs->z_drain_lock); + mutex_destroy(&zfsvfs->z_znodes_lock); + mutex_destroy(&zfsvfs->z_lock); + list_destroy(&zfsvfs->z_all_znodes); + rrm_destroy(&zfsvfs->z_teardown_lock); + rw_destroy(&zfsvfs->z_teardown_inactive_lock); + rw_destroy(&zfsvfs->z_fuid_lock); + + for (i = 0; i != size; i++) { + avl_destroy(&zfsvfs->z_hold_trees[i]); + mutex_destroy(&zfsvfs->z_hold_locks[i]); + } + kmem_free(zfsvfs->z_hold_trees, sizeof (avl_tree_t) * size); + kmem_free(zfsvfs->z_hold_locks, sizeof (kmutex_t) * size); + + dprintf("ZFS: Unloading hardlink AVLtree: %lu\n", + avl_numnodes(&zfsvfs->z_hardlinks)); + void *cookie = NULL; + hardlinks_t *hardlink; + rw_destroy(&zfsvfs->z_hardlinks_lock); + while ((hardlink = avl_destroy_nodes(&zfsvfs->z_hardlinks_linkid, + &cookie))) { + } + cookie = NULL; + while ((hardlink = avl_destroy_nodes(&zfsvfs->z_hardlinks, &cookie))) { + kmem_free(hardlink, sizeof (*hardlink)); + } + avl_destroy(&zfsvfs->z_hardlinks); + avl_destroy(&zfsvfs->z_hardlinks_linkid); + + dataset_kstats_destroy(&zfsvfs->z_kstat); + kmem_free(zfsvfs, sizeof (zfsvfs_t)); + dprintf("-zfsvfs_free\n"); +} + +static void +zfs_set_fuid_feature(zfsvfs_t *zfsvfs) +{ + zfsvfs->z_use_fuids = USE_FUIDS(zfsvfs->z_version, zfsvfs->z_os); + if (zfsvfs->z_vfs) { +#if 0 + if (zfsvfs->z_use_fuids) { + vfs_set_feature(zfsvfs->z_vfs, VFSFT_XVATTR); + vfs_set_feature(zfsvfs->z_vfs, VFSFT_SYSATTR_VIEWS); + vfs_set_feature(zfsvfs->z_vfs, VFSFT_ACEMASKONACCESS); + vfs_set_feature(zfsvfs->z_vfs, VFSFT_ACLONCREATE); + vfs_set_feature(zfsvfs->z_vfs, VFSFT_ACCESS_FILTER); + vfs_set_feature(zfsvfs->z_vfs, VFSFT_REPARSE); + } else { + vfs_clear_feature(zfsvfs->z_vfs, VFSFT_XVATTR); + vfs_clear_feature(zfsvfs->z_vfs, VFSFT_SYSATTR_VIEWS); + vfs_clear_feature(zfsvfs->z_vfs, VFSFT_ACEMASKONACCESS); + vfs_clear_feature(zfsvfs->z_vfs, VFSFT_ACLONCREATE); + vfs_clear_feature(zfsvfs->z_vfs, VFSFT_ACCESS_FILTER); + vfs_clear_feature(zfsvfs->z_vfs, VFSFT_REPARSE); + } +#endif + } + zfsvfs->z_use_sa = USE_SA(zfsvfs->z_version, zfsvfs->z_os); +} + + +static int +zfs_domount(struct mount *vfsp, dev_t mount_dev, char *osname, + vfs_context_t ctx) +{ + int error = 0; + zfsvfs_t *zfsvfs; + uint64_t mimic = 0; + struct timeval tv; + + ASSERT(vfsp); + ASSERT(osname); + + error = zfsvfs_create(osname, B_FALSE, &zfsvfs); + if (error) + return (error); + zfsvfs->z_vfs = vfsp; + + zfsvfs->z_rdev = mount_dev; + + /* HFS sets this prior to mounting */ + vfs_setflags(vfsp, (uint64_t)((unsigned int)MNT_DOVOLFS)); + /* Advisory locking should be handled at the VFS layer */ + vfs_setlocklocal(vfsp); + + /* + * Record the mount time (for Spotlight) + */ + microtime(&tv); + zfsvfs->z_mount_time = tv.tv_sec; + + vfs_setfsprivate(vfsp, zfsvfs); + + /* + * The fsid is 64 bits, composed of an 8-bit fs type, which + * separates our fsid from any other filesystem types, and a + * 56-bit objset unique ID. The objset unique ID is unique to + * all objsets open on this system, provided by unique_create(). + * The 8-bit fs type must be put in the low bits of fsid[1] + * because that's where other Solaris filesystems put it. + */ + + error = dsl_prop_get_integer(osname, "com.apple.mimic", &mimic, NULL); + if (zfsvfs->z_rdev) { + struct vfsstatfs *vfsstatfs; + vfsstatfs = vfs_statfs(vfsp); + vfsstatfs->f_fsid.val[0] = zfsvfs->z_rdev; + vfsstatfs->f_fsid.val[1] = vfs_typenum(vfsp); + } else { + // Otherwise, ask VFS to give us a random unique one. + vfs_getnewfsid(vfsp); + struct vfsstatfs *vfsstatfs; + vfsstatfs = vfs_statfs(vfsp); + zfsvfs->z_rdev = vfsstatfs->f_fsid.val[0]; + } + + /* + * If we are readonly (ie, waiting for rootmount) we need to reply + * honestly, so launchd runs fsck_zfs and mount_zfs + */ + if (mimic) { + struct vfsstatfs *vfsstatfs; + vfsstatfs = vfs_statfs(vfsp); + strlcpy(vfsstatfs->f_fstypename, "hfs", MFSTYPENAMELEN); + } + + /* + * Set features for file system. + */ + zfs_set_fuid_feature(zfsvfs); + + if (dmu_objset_is_snapshot(zfsvfs->z_os)) { + uint64_t pval; + char fsname[ZFS_MAX_DATASET_NAME_LEN]; + zfsvfs_t *fs_zfsvfs; + + dmu_fsname(osname, fsname); + error = getzfsvfs(fsname, &fs_zfsvfs); + if (error == 0) { + if (fs_zfsvfs->z_unmounted) + error = SET_ERROR(EINVAL); + vfs_unbusy(fs_zfsvfs->z_vfs); + } + if (error) { + printf("file system '%s' is unmounted : error %d\n", + fsname, + error); + goto out; + } + + atime_changed_cb(zfsvfs, B_FALSE); + readonly_changed_cb(zfsvfs, B_TRUE); + if ((error = dsl_prop_get_integer(osname, "xattr", &pval, + NULL))) + goto out; + xattr_changed_cb(zfsvfs, pval); + zfsvfs->z_issnap = B_TRUE; + zfsvfs->z_os->os_sync = ZFS_SYNC_DISABLED; + + mutex_enter(&zfsvfs->z_os->os_user_ptr_lock); + dmu_objset_set_user(zfsvfs->z_os, zfsvfs); + mutex_exit(&zfsvfs->z_os->os_user_ptr_lock); + + zfsctl_mount_signal(osname, B_TRUE); + + } else { + if ((error = zfsvfs_setup(zfsvfs, B_TRUE))) + goto out; + } + + vfs_setflags(vfsp, (uint64_t)((unsigned int)MNT_JOURNALED)); + + if ((vfs_flags(vfsp) & MNT_ROOTFS) != 0) { + /* Root FS */ + vfs_clearflags(vfsp, + (uint64_t)((unsigned int)MNT_UNKNOWNPERMISSIONS)); + vfs_clearflags(vfsp, + (uint64_t)((unsigned int)MNT_IGNORE_OWNERSHIP)); + } + +#if 1 // Want .zfs or not + if (!zfsvfs->z_issnap) { + zfsctl_create(zfsvfs); + } +#endif + +out: + if (error) { + vfs_setfsprivate(vfsp, NULL); + dmu_objset_disown(zfsvfs->z_os, B_TRUE, zfsvfs); + zfsvfs_free(zfsvfs); + } else { + atomic_inc_32(&zfs_active_fs_count); + } + + return (error); +} + +void +zfs_unregister_callbacks(zfsvfs_t *zfsvfs) +{ + objset_t *os = zfsvfs->z_os; + + /* + * Unregister properties. + */ + if (!dmu_objset_is_snapshot(os)) { + dsl_prop_unregister_all(dmu_objset_ds(os), zfsvfs); + } +} + +/* + * zfs_vfs_mountroot + * Given a device vnode created by vfs_mountroot bdevvp, + * and with the root pool already imported, root mount the + * dataset specified in the pool's bootfs property. + * + * Inputs: + * mp: VFS mount struct + * devvp: device vnode, currently only used to retrieve the + * dev_t for the fsid. Could vnode_get, vnode_ref, vnode_put, + * with matching get/rele/put in zfs_vfs_umount, but this is + * already done by XNU as well. + * ctx: VFS context, unused. + * + * Return: + * 0 on success, positive int on failure. + */ +int +zfs_vfs_mountroot(struct mount *mp, struct vnode *devvp, vfs_context_t ctx) +{ + /* + * static int zfsrootdone = 0; + */ + zfsvfs_t *zfsvfs = NULL; + spa_t *spa = 0; + char *zfs_bootfs = 0; + dev_t dev = 0; + int error = EINVAL; + + printf("ZFS: %s\n", __func__); + ASSERT(mp); + ASSERT(devvp); + ASSERT(ctx); + if (!mp || !devvp | !ctx) { + cmn_err(CE_NOTE, "%s: missing one of mp %p devvp %p" + " or ctx %p", __func__, mp, devvp, ctx); + return (EINVAL); + } + + /* Look up bootfs variable from pool here */ + zfs_bootfs = kmem_alloc(MAXPATHLEN, KM_SLEEP); + if (!zfs_bootfs) { + cmn_err(CE_NOTE, "%s: bootfs alloc failed", + __func__); + return (ENOMEM); + } + + mutex_enter(&spa_namespace_lock); + spa = spa_next(NULL); + if (!spa) { + mutex_exit(&spa_namespace_lock); + cmn_err(CE_NOTE, "%s: no pool available", + __func__); + goto out; + } + + error = dsl_dsobj_to_dsname(spa_name(spa), + spa_bootfs(spa), zfs_bootfs); + if (error != 0) { + mutex_exit(&spa_namespace_lock); + cmn_err(CE_NOTE, "%s: bootfs to name error %d", + __func__, error); + goto out; + } + mutex_exit(&spa_namespace_lock); + + /* + * By setting the dev_t value in the mount vfsp, + * mount_zfs will be called with the /dev/diskN + * proxy, but we can leave the dataset name in + * the mountedfrom field + */ + dev = vnode_specrdev(devvp); + + dprintf("Setting readonly\n"); + + if ((error = zfs_domount(mp, dev, zfs_bootfs, ctx)) != 0) { + printf("zfs_domount: error %d", error); + goto out; + } + + zfsvfs = (zfsvfs_t *)vfs_fsprivate(mp); + ASSERT(zfsvfs); + if (!zfsvfs) { + cmn_err(CE_NOTE, "missing zfsvfs"); + goto out; + } + + /* Set this mount to read-only */ + zfsvfs->z_rdonly = 1; + + /* + * Due to XNU mount flags, readonly gets set off for a short + * while, which means mimic will kick in if enabled. But we need + * to reply with true "zfs" until root has been remounted RW, so + * that launchd tries to run mount_zfs instead of mount_hfs + */ + mimic_changed_cb(zfsvfs, B_FALSE); + + /* + * Leave rootvp held. The root file system is never unmounted. + * + * XXX APPLE + * xnu will in fact call vfs_unmount on the root filesystem + * during shutdown/reboot. + */ + +out: + + if (zfs_bootfs) { + kmem_free(zfs_bootfs, MAXPATHLEN); + } + return (error); + +} + +/*ARGSUSED*/ +int +zfs_vfs_mount(struct mount *vfsp, vnode_t *mvp /* devvp */, + user_addr_t data, vfs_context_t context) +{ + char *osname = NULL; + char *options = NULL; + int error = 0; + int rdonly = 0; + int mflag = 0; + char *proxy = NULL; + struct zfs_mount_args mnt_args; + size_t osnamelen = 0; + uint32_t cmdflags = 0; + + cmdflags = (uint32_t)vfs_flags(vfsp) & MNT_CMDFLAGS; + rdonly = vfs_isrdonly(vfsp); + + if (!data) { + /* + * From 10.12, if you set VFS_TBLCANMOUNTROOT, XNU will + * call vfs_mountroot if set (and we can not set it), OR + * call vfs_mount if not set. Since data is always passed NULL + * in this case, we know we are supposed to call mountroot. + */ + dprintf("ZFS: vfs_mount -> vfs_mountroot\n"); + return (zfs_vfs_mountroot(vfsp, mvp, context)); + } + + /* + * Get the objset name (the "special" mount argument). + */ + if (data) { + + // Clear the struct, so that "flags" is null if only given path. + bzero(&mnt_args, sizeof (mnt_args)); + + osname = kmem_alloc(MAXPATHLEN, KM_SLEEP); + + if (vfs_context_is64bit(context)) { + if ((error = ddi_copyin((void *)data, + (caddr_t)&mnt_args, sizeof (mnt_args), 0))) { + dprintf("%s: error on mnt_args copyin %d\n", + __func__, error); + goto out; + } + } else { + user32_addr_t tmp; + if ((error = ddi_copyin((void *)data, + (caddr_t)&tmp, sizeof (tmp), 0))) { + printf("%s: error on mnt_args copyin32 %d\n", + __func__, error); + goto out; + } + /* munge into LP64 addr */ + mnt_args.fspec = (char *)CAST_USER_ADDR_T(tmp); + } + + // Copy over the string + if ((error = ddi_copyinstr((const void *)mnt_args.fspec, osname, + MAXPATHLEN, &osnamelen))) { + dprintf("%s: error on osname copyin %d\n", + __func__, error); + if (!mvp) + goto out; + } + } + + proxy = kmem_alloc(MAXPATHLEN, KM_SLEEP); + *proxy = 0; + + /* + * Translate /dev/disk path into dataset name + * After this; + * "proxy" will have "/dev/disk" (IF given) + * "osname" has the dataset name as usual + */ + if (strncmp(osname, "/dev/disk", 9) == 0) { + strlcpy(proxy, osname, MAXPATHLEN); + error = zfs_osx_proxy_get_osname(osname, + osname, MAXPATHLEN); + if (error != 0) { + printf("%s couldn't get dataset from %s\n", + __func__, osname); + error = ENOENT; + goto out; + } + dprintf("%s got new osname %s\n", __func__, osname); + } + + if (mnt_args.struct_size == sizeof (mnt_args)) { + mflag = mnt_args.mflag; + options = kmem_alloc(mnt_args.optlen, KM_SLEEP); + error = ddi_copyin((const void *)mnt_args.optptr, + (caddr_t)options, mnt_args.optlen, 0); + } + + if (mflag & MS_RDONLY) { + dprintf("%s: adding MNT_RDONLY\n", __func__); + cmdflags |= MNT_RDONLY; + } + + if (mflag & MS_OVERLAY) { + dprintf("%s: adding MNT_UNION\n", __func__); + cmdflags |= MNT_UNION; + } + + if (mflag & MS_FORCE) { + dprintf("%s: adding MNT_FORCE\n", __func__); + cmdflags |= MNT_FORCE; + } + + if (mflag & MS_REMOUNT) { + dprintf("%s: adding MNT_UPDATE on MS_REMOUNT\n", __func__); + cmdflags |= MNT_UPDATE; + } + + vfs_setflags(vfsp, (uint64_t)cmdflags); + + /* + * When doing a remount, we simply refresh our temporary properties + * according to those options set in the current VFS options. + */ + if (cmdflags & MNT_UPDATE) { + + error = 0; + // Used after fsck + if (cmdflags & MNT_RELOAD) { + goto out; + } + + /* refresh mount options */ + zfsvfs_t *zfsvfs = vfs_fsprivate(vfsp); + + if (zfsvfs != NULL) { + if (zfsvfs->z_rdonly == 0 && + (cmdflags & MNT_RDONLY || + vfs_isrdonly(vfsp))) { + /* downgrade */ + dprintf("%s: downgrade requested\n", __func__); + zfsvfs->z_rdonly = 1; + readonly_changed_cb(zfsvfs, B_TRUE); + zfs_unregister_callbacks(zfsvfs); + error = zfs_register_callbacks(vfsp); + if (error) { + dprintf("%s: remount returned %d", + __func__, error); + } + } + + if (vfs_iswriteupgrade(vfsp)) { + /* upgrade */ + dprintf("%s: upgrade requested\n", __func__); + zfsvfs->z_rdonly = 0; + readonly_changed_cb(zfsvfs, B_FALSE); + zfs_unregister_callbacks(zfsvfs); + error = zfs_register_callbacks(vfsp); + if (error) { + dprintf("%s: remount returned %d", + __func__, error); + } + } + } + goto out; + } + + if (vfs_fsprivate(vfsp) != NULL) { + dprintf("already mounted\n"); + error = 0; + goto out; + } + + error = zfs_domount(vfsp, 0, osname, context); + if (error) { + dprintf("%s: zfs_domount returned %d\n", + __func__, error); + goto out; + } + +out: + + if (error == 0) { + + /* Indicate to VFS that we support ACLs. */ + vfs_setextendedsecurity(vfsp); + + // Set /dev/disk name if we have one, otherwise, datasetname + vfs_mountedfrom(vfsp, proxy && *proxy ? proxy : osname); + + } + + if (error) + dprintf("zfs_vfs_mount: error %d\n", error); + + if (osname) + kmem_free(osname, MAXPATHLEN); + + if (proxy) + kmem_free(proxy, MAXPATHLEN); + + if (options) + kmem_free(options, mnt_args.optlen); + + return (error); +} + + +int +zfs_vfs_getattr(struct mount *mp, struct vfs_attr *fsap, + __unused vfs_context_t context) +{ + zfsvfs_t *zfsvfs = vfs_fsprivate(mp); + uint64_t refdbytes, availbytes, usedobjs, availobjs; + uint64_t log_blksize; + uint64_t log_blkcnt; + + // dprintf("vfs_getattr\n"); + + ZFS_ENTER(zfsvfs); + + int mimic_on = 0; + struct vfsstatfs *vfsstatfs; + vfsstatfs = vfs_statfs(zfsvfs->z_vfs); + if (strcmp(vfsstatfs->f_fstypename, "zfs") != 0) + mimic_on = 1; + + /* + * Finder will show the old/incorrect size, we can force a sync of the + * pool to make it correct, but that has side effects which are + * undesirable. + */ + /* txg_wait_synced(dmu_objset_pool(zfsvfs->z_os), 0); */ + + dmu_objset_space(zfsvfs->z_os, + &refdbytes, &availbytes, &usedobjs, &availobjs); + + VFSATTR_RETURN(fsap, f_objcount, usedobjs); + VFSATTR_RETURN(fsap, f_maxobjcount, 0x7fffffffffffffff); + /* + * Carbon depends on f_filecount and f_dircount so + * make up some values based on total objects. + */ + VFSATTR_RETURN(fsap, f_filecount, usedobjs - (usedobjs / 4)); + VFSATTR_RETURN(fsap, f_dircount, usedobjs / 4); + + /* + * Model after HFS in working out if we should use the legacy size + * 512, or go to 4096. Note that XNU only likes those two + * blocksizes, so we don't use the ZFS recordsize + */ + log_blkcnt = (u_int64_t)((refdbytes + availbytes) >> SPA_MINBLOCKSHIFT); + log_blksize = (log_blkcnt > 0x000000007fffffff) ? + 4096 : (1 << SPA_MINBLOCKSHIFT); + + /* + * The underlying storage pool actually uses multiple block sizes. + * We report the fragsize as the smallest block size we support, + * and we report our blocksize as the filesystem's maximum blocksize. + */ + VFSATTR_RETURN(fsap, f_bsize, log_blksize); + VFSATTR_RETURN(fsap, f_iosize, zfsvfs->z_max_blksz); + + /* + * The following report "total" blocks of various kinds in the + * file system, but reported in terms of f_frsize - the + * "fragment" size. + */ + VFSATTR_RETURN(fsap, f_blocks, + (u_int64_t)((refdbytes + availbytes) / log_blksize)); + VFSATTR_RETURN(fsap, f_bfree, (u_int64_t)(availbytes / log_blksize)); + VFSATTR_RETURN(fsap, f_bavail, fsap->f_bfree); + VFSATTR_RETURN(fsap, f_bused, fsap->f_blocks - fsap->f_bfree); + + /* + * statvfs() should really be called statufs(), because it assumes + * static metadata. ZFS doesn't preallocate files, so the best + * we can do is report the max that could possibly fit in f_files, + * and that minus the number actually used in f_ffree. + * For f_ffree, report the smaller of the number of object available + * and the number of blocks (each object will take at least a block). + */ + VFSATTR_RETURN(fsap, f_ffree, (u_int64_t)MIN(availobjs, fsap->f_bfree)); + VFSATTR_RETURN(fsap, f_files, fsap->f_ffree + usedobjs); + + if (VFSATTR_IS_ACTIVE(fsap, f_fsid)) { + fsap->f_fsid.val[0] = zfsvfs->z_rdev; + fsap->f_fsid.val[1] = vfs_typenum(mp); + VFSATTR_SET_SUPPORTED(fsap, f_fsid); + } + if (VFSATTR_IS_ACTIVE(fsap, f_capabilities)) { + fsap->f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] = + VOL_CAP_FMT_PERSISTENTOBJECTIDS | + VOL_CAP_FMT_HARDLINKS | // ZFS + VOL_CAP_FMT_SPARSE_FILES | // ZFS + VOL_CAP_FMT_2TB_FILESIZE | // ZFS + VOL_CAP_FMT_JOURNAL | VOL_CAP_FMT_JOURNAL_ACTIVE | // ZFS + VOL_CAP_FMT_SYMBOLICLINKS | // msdos.. + // ZFS has root times just fine + /* VOL_CAP_FMT_NO_ROOT_TIMES | */ + // Ask XNU to remember zero-runs, instead of writing + // zeros to it. + VOL_CAP_FMT_ZERO_RUNS | + VOL_CAP_FMT_CASE_PRESERVING | + VOL_CAP_FMT_FAST_STATFS | + VOL_CAP_FMT_PATH_FROM_ID | + VOL_CAP_FMT_64BIT_OBJECT_IDS | + /* VOL_CAP_FMT_DECMPFS_COMPRESSION | */ + VOL_CAP_FMT_HIDDEN_FILES; + + fsap->f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] = + VOL_CAP_INT_ATTRLIST | // ZFS + VOL_CAP_INT_NFSEXPORT | // ZFS + VOL_CAP_INT_EXTENDED_SECURITY | // ZFS +#if NAMEDSTREAMS + VOL_CAP_INT_NAMEDSTREAMS | // ZFS +#endif + VOL_CAP_INT_EXTENDED_ATTR | // ZFS + VOL_CAP_INT_VOL_RENAME | // msdos.. + VOL_CAP_INT_ADVLOCK | + // ZFS does not yet have exchangedata (it's in a branch) + /* VOL_CAP_INT_EXCHANGEDATA| */ + // ZFS does not yet have copyfile + /* VOL_CAP_INT_COPYFILE| */ + // ZFS does not yet have allocate + /* VOL_CAP_INT_ALLOCATE| */ + VOL_CAP_INT_FLOCK; + + fsap->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED1] = + 0; + fsap->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED2] = + 0; + + /* + * This is the list of valid capabilities at time of + * compile. The valid list should have them all defined + * and the "capability" list above should enable only + * those we have implemented + */ + fsap->f_capabilities.valid[VOL_CAPABILITIES_FORMAT] = + VOL_CAP_FMT_PERSISTENTOBJECTIDS | + VOL_CAP_FMT_SYMBOLICLINKS | + VOL_CAP_FMT_HARDLINKS | + VOL_CAP_FMT_JOURNAL | + VOL_CAP_FMT_JOURNAL_ACTIVE | + VOL_CAP_FMT_NO_ROOT_TIMES | + VOL_CAP_FMT_SPARSE_FILES | + VOL_CAP_FMT_ZERO_RUNS | + VOL_CAP_FMT_CASE_SENSITIVE | + VOL_CAP_FMT_CASE_PRESERVING | + VOL_CAP_FMT_FAST_STATFS | + VOL_CAP_FMT_2TB_FILESIZE | + VOL_CAP_FMT_OPENDENYMODES | + VOL_CAP_FMT_PATH_FROM_ID | + VOL_CAP_FMT_64BIT_OBJECT_IDS | + VOL_CAP_FMT_NO_VOLUME_SIZES | + VOL_CAP_FMT_DECMPFS_COMPRESSION | + VOL_CAP_FMT_HIDDEN_FILES; + fsap->f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] = + VOL_CAP_INT_SEARCHFS | + VOL_CAP_INT_ATTRLIST | + VOL_CAP_INT_NFSEXPORT | + VOL_CAP_INT_READDIRATTR | + VOL_CAP_INT_EXCHANGEDATA | + VOL_CAP_INT_COPYFILE | + VOL_CAP_INT_ALLOCATE | + VOL_CAP_INT_VOL_RENAME | + VOL_CAP_INT_ADVLOCK | + VOL_CAP_INT_FLOCK | + VOL_CAP_INT_EXTENDED_ATTR | + VOL_CAP_INT_USERACCESS | +#if NAMEDSTREAMS + VOL_CAP_INT_NAMEDSTREAMS | +#endif + VOL_CAP_INT_MANLOCK; + + fsap->f_capabilities.valid[VOL_CAPABILITIES_RESERVED1] = 0; + fsap->f_capabilities.valid[VOL_CAPABILITIES_RESERVED2] = 0; + + /* Check if we are case-sensitive */ + if (zfsvfs->z_case == ZFS_CASE_SENSITIVE) + fsap->f_capabilities.capabilities[ + VOL_CAPABILITIES_FORMAT] |= + VOL_CAP_FMT_CASE_SENSITIVE; + + /* Check if xattr is enabled */ + if (zfsvfs->z_xattr == B_TRUE) { + fsap->f_capabilities.capabilities[ + VOL_CAPABILITIES_INTERFACES] |= + VOL_CAP_INT_EXTENDED_ATTR; + } + + if (mimic_on) { + fsap->f_capabilities.capabilities[ + VOL_CAPABILITIES_FORMAT] |= + VOL_CAP_FMT_DECMPFS_COMPRESSION; + } + + VFSATTR_SET_SUPPORTED(fsap, f_capabilities); + } + + if (VFSATTR_IS_ACTIVE(fsap, f_attributes)) { + fsap->f_attributes.validattr.commonattr = + ATTR_CMN_NAME | + ATTR_CMN_DEVID | + ATTR_CMN_FSID | + ATTR_CMN_OBJTYPE | + ATTR_CMN_OBJTAG | + ATTR_CMN_OBJID | + ATTR_CMN_OBJPERMANENTID | + ATTR_CMN_PAROBJID | + /* ATTR_CMN_SCRIPT | */ + ATTR_CMN_CRTIME | + ATTR_CMN_MODTIME | + ATTR_CMN_CHGTIME | + ATTR_CMN_ACCTIME | + /* ATTR_CMN_BKUPTIME | */ + ATTR_CMN_FNDRINFO | + ATTR_CMN_OWNERID | + ATTR_CMN_GRPID | + ATTR_CMN_ACCESSMASK | + ATTR_CMN_FLAGS | + ATTR_CMN_USERACCESS | + ATTR_CMN_EXTENDED_SECURITY | + ATTR_CMN_UUID | + ATTR_CMN_GRPUUID | +#ifdef ATTR_CMN_DOCUMENT_ID + ATTR_CMN_DOCUMENT_ID | +#endif +#ifdef ATTR_CMN_GEN_COUNT + ATTR_CMN_GEN_COUNT | +#endif + 0; + fsap->f_attributes.validattr.volattr = + ATTR_VOL_FSTYPE | + ATTR_VOL_SIGNATURE | + ATTR_VOL_SIZE | + ATTR_VOL_SPACEFREE | + ATTR_VOL_SPACEAVAIL | + ATTR_VOL_MINALLOCATION | + ATTR_VOL_ALLOCATIONCLUMP | + ATTR_VOL_IOBLOCKSIZE | + ATTR_VOL_OBJCOUNT | + ATTR_VOL_FILECOUNT | + ATTR_VOL_DIRCOUNT | + ATTR_VOL_MAXOBJCOUNT | + /* ATTR_VOL_MOUNTPOINT | */ + ATTR_VOL_NAME | + ATTR_VOL_MOUNTFLAGS | + /* ATTR_VOL_MOUNTEDDEVICE | */ + /* ATTR_VOL_ENCODINGSUSED | */ + ATTR_VOL_CAPABILITIES | + ATTR_VOL_ATTRIBUTES; + fsap->f_attributes.validattr.dirattr = + ATTR_DIR_LINKCOUNT | + ATTR_DIR_ENTRYCOUNT | + ATTR_DIR_MOUNTSTATUS; + fsap->f_attributes.validattr.fileattr = + ATTR_FILE_LINKCOUNT | + ATTR_FILE_TOTALSIZE | + ATTR_FILE_ALLOCSIZE | + /* ATTR_FILE_IOBLOCKSIZE */ + ATTR_FILE_DEVTYPE | + /* ATTR_FILE_FORKCOUNT */ + /* ATTR_FILE_FORKLIST */ + ATTR_FILE_DATALENGTH | + ATTR_FILE_DATAALLOCSIZE | + ATTR_FILE_RSRCLENGTH | + ATTR_FILE_RSRCALLOCSIZE; + fsap->f_attributes.validattr.forkattr = 0; + fsap->f_attributes.nativeattr.commonattr = + ATTR_CMN_NAME | + ATTR_CMN_DEVID | + ATTR_CMN_FSID | + ATTR_CMN_OBJTYPE | + ATTR_CMN_OBJTAG | + ATTR_CMN_OBJID | + ATTR_CMN_OBJPERMANENTID | + ATTR_CMN_PAROBJID | + /* ATTR_CMN_SCRIPT | */ + ATTR_CMN_CRTIME | + ATTR_CMN_MODTIME | + /* ATTR_CMN_CHGTIME | */ /* Supported but not native */ + ATTR_CMN_ACCTIME | + /* ATTR_CMN_BKUPTIME | */ + /* ATTR_CMN_FNDRINFO | */ + ATTR_CMN_OWNERID | /* Supported but not native */ + ATTR_CMN_GRPID | /* Supported but not native */ + ATTR_CMN_ACCESSMASK | /* Supported but not native */ + ATTR_CMN_FLAGS | + ATTR_CMN_USERACCESS | + ATTR_CMN_EXTENDED_SECURITY | + ATTR_CMN_UUID | + ATTR_CMN_GRPUUID | +#ifdef ATTR_CMN_DOCUMENT_ID + ATTR_CMN_DOCUMENT_ID | +#endif +#ifdef ATTR_CMN_GEN_COUNT + ATTR_CMN_GEN_COUNT | +#endif + 0; + fsap->f_attributes.nativeattr.volattr = + ATTR_VOL_FSTYPE | + ATTR_VOL_SIGNATURE | + ATTR_VOL_SIZE | + ATTR_VOL_SPACEFREE | + ATTR_VOL_SPACEAVAIL | + ATTR_VOL_MINALLOCATION | + ATTR_VOL_ALLOCATIONCLUMP | + ATTR_VOL_IOBLOCKSIZE | + ATTR_VOL_OBJCOUNT | + ATTR_VOL_FILECOUNT | + ATTR_VOL_DIRCOUNT | + ATTR_VOL_MAXOBJCOUNT | + /* ATTR_VOL_MOUNTPOINT | */ + ATTR_VOL_NAME | + ATTR_VOL_MOUNTFLAGS | + /* ATTR_VOL_MOUNTEDDEVICE | */ + /* ATTR_VOL_ENCODINGSUSED */ + ATTR_VOL_CAPABILITIES | + ATTR_VOL_ATTRIBUTES; + fsap->f_attributes.nativeattr.dirattr = 0; + fsap->f_attributes.nativeattr.fileattr = + /* ATTR_FILE_LINKCOUNT | */ /* Supported but not native */ + ATTR_FILE_TOTALSIZE | + ATTR_FILE_ALLOCSIZE | + /* ATTR_FILE_IOBLOCKSIZE */ + ATTR_FILE_DEVTYPE | + /* ATTR_FILE_FORKCOUNT */ + /* ATTR_FILE_FORKLIST */ + ATTR_FILE_DATALENGTH | + ATTR_FILE_DATAALLOCSIZE | + ATTR_FILE_RSRCLENGTH | + ATTR_FILE_RSRCALLOCSIZE; + fsap->f_attributes.nativeattr.forkattr = 0; + + VFSATTR_SET_SUPPORTED(fsap, f_attributes); + } + if (VFSATTR_IS_ACTIVE(fsap, f_create_time)) { + char osname[MAXNAMELEN]; + uint64_t value; + + // Get dataset name + dmu_objset_name(zfsvfs->z_os, osname); + dsl_prop_get_integer(osname, "CREATION", + &value, NULL); + fsap->f_create_time.tv_sec = value; + fsap->f_create_time.tv_nsec = 0; + VFSATTR_SET_SUPPORTED(fsap, f_create_time); + } + if (VFSATTR_IS_ACTIVE(fsap, f_modify_time)) { + timestruc_t now; + uint64_t mtime[2]; + + gethrestime(&now); + ZFS_TIME_ENCODE(&now, mtime); + // fsap->f_modify_time = mtime; + ZFS_TIME_DECODE(&fsap->f_modify_time, mtime); + + VFSATTR_SET_SUPPORTED(fsap, f_modify_time); + } + /* + * For Carbon compatibility, pretend to support this legacy/unused + * attribute + */ + if (VFSATTR_IS_ACTIVE(fsap, f_backup_time)) { + fsap->f_backup_time.tv_sec = 0; + fsap->f_backup_time.tv_nsec = 0; + VFSATTR_SET_SUPPORTED(fsap, f_backup_time); + } + + if (VFSATTR_IS_ACTIVE(fsap, f_vol_name)) { + char osname[MAXNAMELEN], *slash; + dmu_objset_name(zfsvfs->z_os, osname); + + slash = strrchr(osname, '/'); + if (slash) { + /* Advance past last slash */ + slash += 1; + } else { + /* Copy whole osname (pool root) */ + slash = osname; + } + strlcpy(fsap->f_vol_name, slash, MAXPATHLEN); + + VFSATTR_SET_SUPPORTED(fsap, f_vol_name); + dprintf("vfs_getattr: volume name '%s'\n", fsap->f_vol_name); + } + + /* If we are mimicking, we need userland know we are really ZFS */ + if (mimic_on) { + VFSATTR_RETURN(fsap, f_fssubtype, + zfsvfs->z_case == ZFS_CASE_SENSITIVE ? 2 : 0); + } else { + // 0x83 or 0x81 HFS + JOURNAL and optional CASESENSITIVE + VFSATTR_RETURN(fsap, f_fssubtype, + zfsvfs->z_case == ZFS_CASE_SENSITIVE ? 0x83 : 0x81); + } + /* + * According to joshade over at + * https://github.com/joshado/liberate-applefileserver/blob/ + * master/liberate.m + * the following values need to be returned for it to be considered + * by Apple's AFS. + */ + VFSATTR_RETURN(fsap, f_signature, 0x482b); /* "H+" in ascii */ + VFSATTR_RETURN(fsap, f_carbon_fsid, 0); + // Make up a UUID here, based on the name + if (VFSATTR_IS_ACTIVE(fsap, f_uuid)) { + + char osname[MAXNAMELEN]; + int error; + // Get dataset name + dmu_objset_name(zfsvfs->z_os, osname); + dprintf("%s: osname [%s]\n", __func__, osname); + + if ((error = zfs_vfs_uuid_gen(osname, + fsap->f_uuid)) != 0) { + dprintf("%s uuid_gen error %d\n", __func__, error); + } else { + /* return f_uuid in fsap */ + VFSATTR_SET_SUPPORTED(fsap, f_uuid); + } + } + + uint64_t missing = 0; + missing = (fsap->f_active ^ (fsap->f_active & fsap->f_supported)); + if (missing != 0) { + dprintf("%s: asked %08llx reply %08llx missing %08llx\n", + __func__, fsap->f_active, fsap->f_supported, + missing); + } + + ZFS_EXIT(zfsvfs); + + return (0); +} + +int +zfs_vnode_lock(vnode_t *vp, int flags) +{ + int error; + + ASSERT(vp != NULL); + + error = vn_lock(vp, flags); + return (error); +} + +/* + * The ARC has requested that the filesystem drop entries from the dentry + * and inode caches. This can occur when the ARC needs to free meta data + * blocks but can't because they are all pinned by entries in these caches. + */ + +/* Get vnode for the root object of this mount */ +int +zfs_vfs_root(struct mount *mp, vnode_t **vpp, __unused vfs_context_t context) +{ + zfsvfs_t *zfsvfs = vfs_fsprivate(mp); + znode_t *rootzp = NULL; + int error; + + if (!zfsvfs) { + struct vfsstatfs *stat = 0; + if (mp) stat = vfs_statfs(mp); + if (stat) + dprintf("%s mp on %s from %s\n", __func__, + stat->f_mntonname, stat->f_mntfromname); + dprintf("%s no zfsvfs yet for mp\n", __func__); + return (EINVAL); + } + + ZFS_ENTER(zfsvfs); + + error = zfs_zget(zfsvfs, zfsvfs->z_root, &rootzp); + if (error == 0) + *vpp = ZTOV(rootzp); + else + *vpp = NULL; + + ZFS_EXIT(zfsvfs); + + if (error == 0 && *vpp != NULL) + if (vnode_vtype(*vpp) != VDIR) { + panic("%s: not a directory\n", __func__); + } + + return (error); +} + +/* + * Teardown the zfsvfs::z_os. + * + * Note, if 'unmounting' is FALSE, we return with the 'z_teardown_lock' + * and 'z_teardown_inactive_lock' held. + */ +static int +zfsvfs_teardown(zfsvfs_t *zfsvfs, boolean_t unmounting) +{ + znode_t *zp; + /* + * We have experienced deadlocks with dmu_recv_end happening between + * suspend_fs() and resume_fs(). Clearly something is not quite ready + * so we will wait for pools to be synced first. + * This is considered a temporary solution until we can work out + * the full issue. + */ + + zfs_unlinked_drain_stop_wait(zfsvfs); + + /* + * If someone has not already unmounted this file system, + * drain the iput_taskq to ensure all active references to the + * zfs_sb_t have been handled only then can it be safely destroyed. + */ + if (zfsvfs->z_os) { + /* + * If we're unmounting we have to wait for the list to + * drain completely. + * + * If we're not unmounting there's no guarantee the list + * will drain completely, but iputs run from the taskq + * may add the parents of dir-based xattrs to the taskq + * so we want to wait for these. + * + * We can safely read z_nr_znodes without locking because the + * VFS has already blocked operations which add to the + * z_all_znodes list and thus increment z_nr_znodes. + */ + int round = 0; + while (!list_empty(&zfsvfs->z_all_znodes)) { + taskq_wait_outstanding(dsl_pool_zrele_taskq( + dmu_objset_pool(zfsvfs->z_os)), 0); + if (++round > 1 && !unmounting) + break; + break; /* Only loop once - osx can get stuck */ + } + } + + rrm_enter(&zfsvfs->z_teardown_lock, RW_WRITER, FTAG); + + if (!unmounting) { + /* + * We purge the parent filesystem's vfsp as the parent + * filesystem and all of its snapshots have their vnode's + * v_vfsp set to the parent's filesystem's vfsp. Note, + * 'z_parent' is self referential for non-snapshots. + */ + cache_purgevfs(zfsvfs->z_parent->z_vfs); + } + + /* + * Close the zil. NB: Can't close the zil while zfs_inactive + * threads are blocked as zil_close can call zfs_inactive. + */ + if (zfsvfs->z_log) { + zil_close(zfsvfs->z_log); + zfsvfs->z_log = NULL; + } + + rw_enter(&zfsvfs->z_teardown_inactive_lock, RW_WRITER); + + /* + * If we are not unmounting (ie: online recv) and someone already + * unmounted this file system while we were doing the switcheroo, + * or a reopen of z_os failed then just bail out now. + */ + if (!unmounting && (zfsvfs->z_unmounted || zfsvfs->z_os == NULL)) { + rw_exit(&zfsvfs->z_teardown_inactive_lock); + rrm_exit(&zfsvfs->z_teardown_lock, FTAG); + return (SET_ERROR(EIO)); + } + /* + * At this point there are no VFS ops active, and any new VFS ops + * will fail with EIO since we have z_teardown_lock for writer (only + * relevant for forced unmount). + * + * Release all holds on dbufs. We also grab an extra reference to all + * the remaining inodes so that the kernel does not attempt to free + * any inodes of a suspended fs. This can cause deadlocks since the + * zfs_resume_fs() process may involve starting threads, which might + * attempt to free unreferenced inodes to free up memory for the new + * thread. + */ + if (!unmounting) { + mutex_enter(&zfsvfs->z_znodes_lock); + for (zp = list_head(&zfsvfs->z_all_znodes); zp != NULL; + zp = list_next(&zfsvfs->z_all_znodes, zp)) { + if (zp->z_sa_hdl) + zfs_znode_dmu_fini(zp); + if (VN_HOLD(ZTOV(zp)) == 0) { + vnode_ref(ZTOV(zp)); + zp->z_suspended = B_TRUE; + VN_RELE(ZTOV(zp)); + } + } + mutex_exit(&zfsvfs->z_znodes_lock); + } + + /* + * If we are unmounting, set the unmounted flag and let new VFS ops + * unblock. zfs_inactive will have the unmounted behavior, and all + * other VFS ops will fail with EIO. + */ + if (unmounting) { + zfsvfs->z_unmounted = B_TRUE; + rw_exit(&zfsvfs->z_teardown_inactive_lock); + rrm_exit(&zfsvfs->z_teardown_lock, FTAG); + } + + /* + * z_os will be NULL if there was an error in attempting to reopen + * zfsvfs, so just return as the properties had already been + * unregistered and cached data had been evicted before. + */ + if (zfsvfs->z_os == NULL) + return (0); + + /* + * Unregister properties. + */ + zfs_unregister_callbacks(zfsvfs); + + /* + * Evict cached data + */ + /* + * Evict cached data. We must write out any dirty data before + * disowning the dataset. + */ + objset_t *os = zfsvfs->z_os; + boolean_t os_dirty = B_FALSE; + for (int t = 0; t < TXG_SIZE; t++) { + if (dmu_objset_is_dirty(os, t)) { + os_dirty = B_TRUE; + break; + } + } + if (!zfs_is_readonly(zfsvfs) && os_dirty) { + txg_wait_synced(dmu_objset_pool(zfsvfs->z_os), 0); + } + dmu_objset_evict_dbufs(zfsvfs->z_os); + dsl_dir_t *dd = os->os_dsl_dataset->ds_dir; + dsl_dir_cancel_waiters(dd); + + return (0); +} + +int +zfs_vfs_unmount(struct mount *mp, int mntflags, vfs_context_t context) +{ + zfsvfs_t *zfsvfs = vfs_fsprivate(mp); + objset_t *os; + char osname[MAXNAMELEN]; + int ret; + /* cred_t *cr = (cred_t *)vfs_context_ucred(context); */ + int destroyed_zfsctl = 0; + + dprintf("%s\n", __func__); + + zfs_unlinked_drain_stop_wait(zfsvfs); + + /* Save osname for later */ + dmu_objset_name(zfsvfs->z_os, osname); + + /* + * We might skip the sync called in the unmount path, since + * zfs_vfs_sync() is generally ignoring xnu's calls, and alas, + * mount_isforce() is set AFTER that sync call, so we can not + * detect unmount is inflight. But why not just sync now, it + * is safe. Optionally, sync if (mount_isforce()); + */ + spa_sync_allpools(); + + /* + * We purge the parent filesystem's vfsp as the parent filesystem + * and all of its snapshots have their vnode's v_vfsp set to the + * parent's filesystem's vfsp. Note, 'z_parent' is self + * referential for non-snapshots. + */ + cache_purgevfs(zfsvfs->z_parent->z_vfs); + + /* + * Unmount any snapshots mounted under .zfs before unmounting the + * dataset itself. + * + * Unfortunately, XNU will check for mounts in preflight, and + * simply not call us at all if snapshots are mounted. + * We expect userland to unmount snapshots now. + */ + + ret = vflush(mp, NULLVP, SKIPSYSTEM); + + if (mntflags & MNT_FORCE) { + /* + * Mark file system as unmounted before calling + * vflush(FORCECLOSE). This way we ensure no future vnops + * will be called and risk operating on DOOMED vnodes. + */ + rrm_enter(&zfsvfs->z_teardown_lock, RW_WRITER, FTAG); + zfsvfs->z_unmounted = B_TRUE; + rrm_exit(&zfsvfs->z_teardown_lock, FTAG); + } + + /* + * We must release ctldir before vflush on osx. + */ + if (zfsvfs->z_ctldir != NULL) { + destroyed_zfsctl = 1; + zfsctl_destroy(zfsvfs); + } + + /* + * Flush all the files. + */ + ret = vflush(mp, NULLVP, + (mntflags & MNT_FORCE) ? FORCECLOSE|SKIPSYSTEM : SKIPSYSTEM); + + if ((ret != 0) && !(mntflags & MNT_FORCE)) { + if (destroyed_zfsctl) + zfsctl_create(zfsvfs); + return (ret); + } + + /* If we are ourselves a snapshot */ + if (dmu_objset_is_snapshot(zfsvfs->z_os)) { + /* Wake up anyone waiting for unmount */ + zfsctl_mount_signal(osname, B_FALSE); + } + + if (!vfs_isrdonly(zfsvfs->z_vfs) && + spa_writeable(dmu_objset_spa(zfsvfs->z_os)) && + !(mntflags & MNT_FORCE)) { + /* Update the last-unmount time for Spotlight's next mount */ + timestruc_t now; + dmu_tx_t *tx; + int error; + uint64_t value; + + dprintf("ZFS: '%s' Updating spotlight LASTUNMOUNT property\n", + osname); + + gethrestime(&now); + zfsvfs->z_last_unmount_time = now.tv_sec; + + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE, NULL); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + dmu_tx_abort(tx); + } else { + value = zfsvfs->z_last_unmount_time; + error = zap_update(zfsvfs->z_os, MASTER_NODE_OBJ, + zfs_prop_to_name(ZFS_PROP_LASTUNMOUNT), + 8, 1, + &value, tx); + dmu_tx_commit(tx); + } + dprintf("ZFS: '%s' set lastunmount to 0x%lx (%d)\n", + osname, zfsvfs->z_last_unmount_time, error); + } + + /* + * Last chance to dump unreferenced system files. + */ + (void) vflush(mp, NULLVP, FORCECLOSE); + + VERIFY(zfsvfs_teardown(zfsvfs, B_TRUE) == 0); + os = zfsvfs->z_os; + + /* + * z_os will be NULL if there was an error in + * attempting to reopen zfsvfs. + */ + if (os != NULL) { + /* + * Unset the objset user_ptr. + */ + mutex_enter(&os->os_user_ptr_lock); + dmu_objset_set_user(os, NULL); + mutex_exit(&os->os_user_ptr_lock); + + /* + * Finally release the objset + */ + dmu_objset_disown(os, B_TRUE, zfsvfs); + } + + zfs_freevfs(zfsvfs->z_vfs); + + return (0); +} + +static int +zfs_vget_internal(zfsvfs_t *zfsvfs, ino64_t ino, vnode_t **vpp) +{ + znode_t *zp; + int err; + + dprintf("vget get %llu\n", ino); + + /* + * Check to see if we expect to find this in the hardlink avl tree of + * hashes. Use the MSB set high as indicator. + */ + hardlinks_t *findnode = NULL; + if ((1ULL<<31) & ino) { + hardlinks_t *searchnode; + avl_index_t loc; + + searchnode = kmem_alloc(sizeof (hardlinks_t), KM_SLEEP); + + dprintf("ZFS: vget looking for (%llx,%llu)\n", ino, ino); + + searchnode->hl_linkid = ino; + + rw_enter(&zfsvfs->z_hardlinks_lock, RW_READER); + findnode = avl_find(&zfsvfs->z_hardlinks_linkid, searchnode, + &loc); + rw_exit(&zfsvfs->z_hardlinks_lock); + + kmem_free(searchnode, sizeof (hardlinks_t)); + + if (findnode) { + dprintf("ZFS: vget found (%llu, %llu, %u): '%s'\n", + findnode->hl_parent, + findnode->hl_fileid, findnode->hl_linkid, + findnode->hl_name); + // Lookup the actual zp instead + ino = findnode->hl_fileid; + } // findnode + } // MSB set + + + /* We can not be locked during zget. */ + if (!ino) { + dprintf("%s: setting ino from %lld to 2\n", __func__, ino); + ino = 2; + } + + err = zfs_zget(zfsvfs, ino, &zp); + + if (err) { + dprintf("zget failed %d\n", err); + return (err); + } + + /* Don't expose EA objects! */ + if (zp->z_pflags & ZFS_XATTR) { + err = ENOENT; + goto out; + } + if (zp->z_unlinked) { + err = EINVAL; + goto out; + } + + *vpp = ZTOV(zp); + + err = zfs_vnode_lock(*vpp, 0 /* flags */); + + /* + * Spotlight requires that vap->va_name() is set when returning + * from vfs_vget, so that vfs_getrealpath() can succeed in returning + * a path to mds. + */ + char *name = kmem_alloc(MAXPATHLEN + 2, KM_SLEEP); + + /* Root can't lookup in ZAP */ + if (zp->z_id == zfsvfs->z_root) { + + dmu_objset_name(zfsvfs->z_os, name); + dprintf("vget: set root '%s'\n", name); + vnode_update_identity(*vpp, NULL, name, + strlen(name), 0, VNODE_UPDATE_NAME); + + } else { + uint64_t parent; + + // if its a hardlink cache + if (findnode) { + + dprintf("vget: updating vnode to '%s' parent %llu\n", + findnode->hl_name, findnode->hl_parent); + + vnode_update_identity(*vpp, + NULL, findnode->hl_name, + strlen(findnode->hl_name), 0, + VNODE_UPDATE_NAME|VNODE_UPDATE_PARENT); + mutex_enter(&zp->z_lock); + strlcpy(zp->z_name_cache, findnode->hl_name, PATH_MAX); + zp->z_finder_parentid = findnode->hl_parent; + mutex_exit(&zp->z_lock); + + + // If we already have the name, cached in zfs_vnop_lookup + } else if (zp->z_name_cache[0]) { + dprintf("vget: cached name '%s'\n", zp->z_name_cache); + vnode_update_identity(*vpp, NULL, zp->z_name_cache, + strlen(zp->z_name_cache), 0, + VNODE_UPDATE_NAME); + + /* If needed, if findnode is set, update the parentid */ + + } else { + + /* Lookup name from ID, grab parent */ + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_PARENT(zfsvfs), + &parent, sizeof (parent)) == 0); + + if (zap_value_search(zfsvfs->z_os, parent, zp->z_id, + ZFS_DIRENT_OBJ(-1ULL), name) == 0) { + + dprintf("vget: set name '%s'\n", name); + vnode_update_identity(*vpp, NULL, name, + strlen(name), 0, + VNODE_UPDATE_NAME); + } else { + dprintf("vget: unable to get name for %llu\n", + zp->z_id); + } // !zap_search + } + } // rootid + + kmem_free(name, MAXPATHLEN + 2); + +out: + + if (err != 0) { + VN_RELE(ZTOV(zp)); + *vpp = NULL; + } + + return (err); +} + +/* + * Get a vnode from a file id (ignoring the generation) + * + * Use by NFS Server (readdirplus) and VFS (build_path) + */ +int +zfs_vfs_vget(struct mount *mp, ino64_t ino, vnode_t **vpp, + __unused vfs_context_t context) +{ + zfsvfs_t *zfsvfs = vfs_fsprivate(mp); + int error; + + dprintf("%s: %llu\n", __func__, ino); + + ZFS_ENTER(zfsvfs); + + /* We also need to handle (.zfs) and (.zfs/snapshot). */ + if ((ino == ZFSCTL_INO_ROOT) && (zfsvfs->z_ctldir != NULL)) { + if (VN_HOLD(zfsvfs->z_ctldir) == 0) { + *vpp = zfsvfs->z_ctldir; + error = 0; + } else { + error = ENOENT; + } + ZFS_EXIT(zfsvfs); + return (error); + } + + /* + * This one is trickier, we have no reference to it, but it is + * in the all list. A little expensive to search list, but at + * least "snapshot" is infrequently accessed + * We also need to check if it is a ".zfs/snapshot/$name" entry - + * luckily we keep the "lowest" ID seen, so we only need to check + * when it is in the range. + */ + if (zfsvfs->z_ctldir != NULL) { + + /* + * Either it is the snapdir itself, or one of the snapshot + * directories inside it + */ + if ((ino == ZFSCTL_INO_SNAPDIR) || + ((ino >= zfsvfs->z_ctldir_startid) && + (ino <= ZFSCTL_INO_SNAPDIRS))) { + znode_t *zp; + + mutex_enter(&zfsvfs->z_znodes_lock); + for (zp = list_head(&zfsvfs->z_all_znodes); zp; + zp = list_next(&zfsvfs->z_all_znodes, zp)) { + if (zp->z_id == ino) + break; + } + mutex_exit(&zfsvfs->z_znodes_lock); + + error = ENOENT; + if (zp != NULL) { + if (VN_HOLD(ZTOV(zp)) == 0) { + *vpp = ZTOV(zp); + error = 0; + } + } + + ZFS_EXIT(zfsvfs); + return (error); + } + } + + /* + * On Mac OS X we always export the root directory id as 2. + * So we don't expect to see the real root directory id + * from zfs_vfs_vget KPI (unless of course the real id was + * already 2). + */ + ino = INO_XNUTOZFS(ino, zfsvfs->z_root); + + error = zfs_vget_internal(zfsvfs, ino, vpp); + + ZFS_EXIT(zfsvfs); + return (error); +} + +int +zfs_vfs_setattr(__unused struct mount *mp, __unused struct vfs_attr *fsap, + __unused vfs_context_t context) +{ + // 10a286 bits has an implementation of this: to set volume name. + return (ENOTSUP); +} + +/* + * NFS Server File Handle File ID + */ +typedef struct zfs_zfid { + uint8_t zf_object[8]; /* obj[i] = obj >> (8 * i) */ + uint8_t zf_gen[8]; /* gen[i] = gen >> (8 * i) */ +} zfs_zfid_t; + +/* + * File handle to vnode pointer + */ +int +zfs_vfs_fhtovp(struct mount *mp, int fhlen, unsigned char *fhp, + vnode_t **vpp, __unused vfs_context_t context) +{ + dprintf("%s\n", __func__); + zfsvfs_t *zfsvfs = vfs_fsprivate(mp); + zfs_zfid_t *zfid = (zfs_zfid_t *)fhp; + znode_t *zp; + uint64_t obj_num = 0; + uint64_t fid_gen = 0; + uint64_t zp_gen; + int i; + int error; + + *vpp = NULL; + + ZFS_ENTER(zfsvfs); + + if (fhlen < sizeof (zfs_zfid_t)) { + error = EINVAL; + goto out; + } + + /* + * Grab the object and gen numbers in an endian neutral manner + */ + for (i = 0; i < sizeof (zfid->zf_object); i++) + obj_num |= ((uint64_t)zfid->zf_object[i]) << (8 * i); + + for (i = 0; i < sizeof (zfid->zf_gen); i++) + fid_gen |= ((uint64_t)zfid->zf_gen[i]) << (8 * i); + + if ((error = zfs_zget(zfsvfs, obj_num, &zp))) { + goto out; + } + + zp_gen = zp->z_gen; + if (zp_gen == 0) + zp_gen = 1; + + if (zp->z_unlinked || zp_gen != fid_gen) { + vnode_put(ZTOV(zp)); + error = EINVAL; + goto out; + } + *vpp = ZTOV(zp); +out: + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * Vnode pointer to File handle + * + * XXX Do we want to check the DSL sharenfs property? + */ +int +zfs_vfs_vptofh(vnode_t *vp, int *fhlenp, unsigned char *fhp, + __unused vfs_context_t context) +{ + dprintf("%s\n", __func__); + zfsvfs_t *zfsvfs = vfs_fsprivate(vnode_mount(vp)); + zfs_zfid_t *zfid = (zfs_zfid_t *)fhp; + znode_t *zp = VTOZ(vp); + uint64_t obj_num; + uint64_t zp_gen; + int i; + + if (*fhlenp < sizeof (zfs_zfid_t)) { + return (EOVERFLOW); + } + + ZFS_ENTER(zfsvfs); + + obj_num = zp->z_id; + zp_gen = zp->z_gen; + if (zp_gen == 0) + zp_gen = 1; + + /* + * Store the object and gen numbers in an endian neutral manner + */ + for (i = 0; i < sizeof (zfid->zf_object); i++) + zfid->zf_object[i] = (uint8_t)(obj_num >> (8 * i)); + + for (i = 0; i < sizeof (zfid->zf_gen); i++) + zfid->zf_gen[i] = (uint8_t)(zp_gen >> (8 * i)); + + *fhlenp = sizeof (zfs_zfid_t); + + ZFS_EXIT(zfsvfs); + return (0); +} + +/* + * Block out VOPs and close zfsvfs_t::z_os + * + * Note, if successful, then we return with the 'z_teardown_lock' and + * 'z_teardown_inactive_lock' write held. We leave ownership of the underlying + * dataset and objset intact so that they can be atomically handed off during + * a subsequent rollback or recv operation and the resume thereafter. + */ +int +zfs_suspend_fs(zfsvfs_t *zfsvfs) +{ + int error; + + if ((error = zfsvfs_teardown(zfsvfs, B_FALSE)) != 0) + return (error); + + return (0); +} + +/* + * Reopen zfsvfs_t::z_os and release VOPs. + */ +int +zfs_resume_fs(zfsvfs_t *zfsvfs, dsl_dataset_t *ds) +{ + int err, err2; + znode_t *zp; + + ASSERT(RRM_WRITE_HELD(&zfsvfs->z_teardown_lock)); + ASSERT(RW_WRITE_HELD(&zfsvfs->z_teardown_inactive_lock)); + + /* + * We already own this, so just update the objset_t, as the one we + * had before may have been evicted. + */ + objset_t *os; + VERIFY3P(ds->ds_owner, ==, zfsvfs); + VERIFY(dsl_dataset_long_held(ds)); + dsl_pool_t *dp = spa_get_dsl(dsl_dataset_get_spa(ds)); + dsl_pool_config_enter(dp, FTAG); + VERIFY0(dmu_objset_from_ds(ds, &os)); + dsl_pool_config_exit(dp, FTAG); + + err = zfsvfs_init(zfsvfs, os); + if (err != 0) + goto bail; + + ds->ds_dir->dd_activity_cancelled = B_FALSE; + VERIFY(zfsvfs_setup(zfsvfs, B_FALSE) == 0); + + zfs_set_fuid_feature(zfsvfs); + + /* + * Attempt to re-establish all the active inodes with their + * dbufs. If a zfs_rezget() fails, then we unhash the inode + * and mark it stale. This prevents a collision if a new + * inode/object is created which must use the same inode + * number. The stale inode will be be released when the + * VFS prunes the dentry holding the remaining references + * on the stale inode. + */ + mutex_enter(&zfsvfs->z_znodes_lock); + for (zp = list_head(&zfsvfs->z_all_znodes); zp; + zp = list_next(&zfsvfs->z_all_znodes, zp)) { + err2 = zfs_rezget(zp); + if (err2) { + zp->z_is_stale = B_TRUE; + } + + /* see comment in zfs_suspend_fs() */ + if (zp->z_suspended) { + if (vnode_getwithref(ZTOV(zp)) == 0) { + vnode_rele(ZTOV(zp)); + zfs_zrele_async(zp); + zp->z_suspended = B_FALSE; + } + } + } + mutex_exit(&zfsvfs->z_znodes_lock); + + if (!vfs_isrdonly(zfsvfs->z_vfs) && !zfsvfs->z_unmounted) { + /* + * zfs_suspend_fs() could have interrupted freeing + * of dnodes. We need to restart this freeing so + * that we don't "leak" the space. + */ + zfs_unlinked_drain(zfsvfs); + } + + cache_purgevfs(zfsvfs->z_parent->z_vfs); + +bail: + /* release the VFS ops */ + rw_exit(&zfsvfs->z_teardown_inactive_lock); + rrm_exit(&zfsvfs->z_teardown_lock, FTAG); + + if (err) { + /* + * Since we couldn't setup the sa framework, try to force + * unmount this file system. + */ + if (zfsvfs->z_os) + zfs_vfs_unmount(zfsvfs->z_vfs, 0, NULL); + } + return (err); +} + + +void +zfs_freevfs(struct mount *vfsp) +{ + zfsvfs_t *zfsvfs = vfs_fsprivate(vfsp); + + dprintf("+freevfs\n"); + + vfs_setfsprivate(vfsp, NULL); + + zfsvfs_free(zfsvfs); + + atomic_dec_32(&zfs_active_fs_count); + dprintf("-freevfs\n"); +} + +struct fromname_struct { + char *oldname; + char *newname; +}; +typedef struct fromname_struct fromname_t; + +static int +zfsvfs_update_fromname_callback(mount_t mp, void *arg) +{ + fromname_t *frna = (fromname_t *)arg; + struct vfsstatfs *vsf = vfs_statfs(mp); + + if (vsf->f_mntfromname, frna->oldname, + sizeof (vsf->f_mntfromname) == 0) { + vfs_mountedfrom(mp, frna->newname); + return (VFS_RETURNED_DONE); + } + + return (VFS_RETURNED); +} + +void +zfsvfs_update_fromname(const char *oldname, const char *newname) +{ + fromname_t frna; + + // find oldname's vfsp + // vfs_mountedfrom(vfsp, newname); + frna.oldname = oldname; + frna.newname = newname; + vfs_iterate(0, zfsvfs_update_fromname_callback, (void *)&frna); +} + +void +zfs_init(void) +{ + + printf("ZFS filesystem version: " ZPL_VERSION_STRING "\n"); + + /* + * Initialize .zfs directory structures + */ + zfsctl_init(); + + /* + * Initialize znode cache, vnode ops, etc... + */ + zfs_znode_init(); + + dmu_objset_register_type(DMU_OST_ZFS, zpl_get_file_info); + + /* Start arc_os - reclaim thread */ + arc_os_init(); + +} + +void +zfs_fini(void) +{ + arc_os_fini(); + zfsctl_fini(); + zfs_znode_fini(); +} + +int +zfs_busy(void) +{ + return (zfs_active_fs_count != 0); +} + +/* + * Release VOPs and unmount a suspended filesystem. + */ +int +zfs_end_fs(zfsvfs_t *zfsvfs, dsl_dataset_t *ds) +{ + ASSERT(RRM_WRITE_HELD(&zfsvfs->z_teardown_lock)); + ASSERT(RW_WRITE_HELD(&zfsvfs->z_teardown_inactive_lock)); + + /* + * We already own this, so just hold and rele it to update the + * objset_t, as the one we had before may have been evicted. + */ + objset_t *os; + VERIFY3P(ds->ds_owner, ==, zfsvfs); + VERIFY(dsl_dataset_long_held(ds)); + dsl_pool_t *dp = spa_get_dsl(dsl_dataset_get_spa(ds)); + dsl_pool_config_enter(dp, FTAG); + VERIFY0(dmu_objset_from_ds(ds, &os)); + dsl_pool_config_exit(dp, FTAG); + zfsvfs->z_os = os; + + /* release the VOPs */ + rw_exit(&zfsvfs->z_teardown_inactive_lock); + rrm_exit(&zfsvfs->z_teardown_lock, FTAG); + + /* + * Try to force unmount this file system. + */ + zfs_vfs_unmount(zfsvfs->z_vfs, 0, NULL); + zfsvfs->z_unmounted = B_TRUE; + return (0); +} + +int +zfs_set_version(zfsvfs_t *zfsvfs, uint64_t newvers) +{ + int error; + objset_t *os = zfsvfs->z_os; + dmu_tx_t *tx; + + if (newvers < ZPL_VERSION_INITIAL || newvers > ZPL_VERSION) + return (SET_ERROR(EINVAL)); + + if (newvers < zfsvfs->z_version) + return (SET_ERROR(EINVAL)); + + if (zfs_spa_version_map(newvers) > + spa_version(dmu_objset_spa(zfsvfs->z_os))) + return (SET_ERROR(ENOTSUP)); + + tx = dmu_tx_create(os); + dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, B_FALSE, ZPL_VERSION_STR); + if (newvers >= ZPL_VERSION_SA && !zfsvfs->z_use_sa) { + dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, B_TRUE, + ZFS_SA_ATTRS); + dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); + } + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + dmu_tx_abort(tx); + return (error); + } + + error = zap_update(os, MASTER_NODE_OBJ, ZPL_VERSION_STR, + 8, 1, &newvers, tx); + + if (error) { + dmu_tx_commit(tx); + return (error); + } + + if (newvers >= ZPL_VERSION_SA && !zfsvfs->z_use_sa) { + uint64_t sa_obj; + + ASSERT3U(spa_version(dmu_objset_spa(zfsvfs->z_os)), >=, + SPA_VERSION_SA); + sa_obj = zap_create(os, DMU_OT_SA_MASTER_NODE, + DMU_OT_NONE, 0, tx); + + error = zap_add(os, MASTER_NODE_OBJ, + ZFS_SA_ATTRS, 8, 1, &sa_obj, tx); + ASSERT(error == 0); + + VERIFY(0 == sa_set_sa_object(os, sa_obj)); + sa_register_update_callback(os, zfs_sa_upgrade); + } + + spa_history_log_internal(dmu_objset_spa(os), "upgrade", tx, + "oldver=%llu newver=%llu dataset = %llu", zfsvfs->z_version, + newvers, dmu_objset_id(os)); + + dmu_tx_commit(tx); + + zfsvfs->z_version = newvers; + os->os_version = newvers; + + zfs_set_fuid_feature(zfsvfs); + + return (0); +} + +/* + * Read a property stored within the master node. + */ +int +zfs_get_zplprop(objset_t *os, zfs_prop_t prop, uint64_t *value) +{ + uint64_t *cached_copy = NULL; + + /* + * Figure out where in the objset_t the cached copy would live, if it + * is available for the requested property. + */ + if (os != NULL) { + switch (prop) { + case ZFS_PROP_VERSION: + cached_copy = &os->os_version; + break; + case ZFS_PROP_NORMALIZE: + cached_copy = &os->os_normalization; + break; + case ZFS_PROP_UTF8ONLY: + cached_copy = &os->os_utf8only; + break; + case ZFS_PROP_CASE: + cached_copy = &os->os_casesensitivity; + break; + default: + break; + } + } + if (cached_copy != NULL && *cached_copy != OBJSET_PROP_UNINITIALIZED) { + *value = *cached_copy; + return (0); + } + + /* + * If the property wasn't cached, look up the file system's value for + * the property. For the version property, we look up a slightly + * different string. + */ + const char *pname; + int error = ENOENT; + if (prop == ZFS_PROP_VERSION) { + pname = ZPL_VERSION_STR; + } else { + pname = zfs_prop_to_name(prop); + } + + if (os != NULL) { + ASSERT3U(os->os_phys->os_type, ==, DMU_OST_ZFS); + error = zap_lookup(os, MASTER_NODE_OBJ, pname, 8, 1, value); + } + + if (error == ENOENT) { + /* No value set, use the default value */ + switch (prop) { + case ZFS_PROP_VERSION: + *value = ZPL_VERSION; + break; + case ZFS_PROP_NORMALIZE: + case ZFS_PROP_UTF8ONLY: + *value = 0; + break; + case ZFS_PROP_CASE: + *value = ZFS_CASE_SENSITIVE; + break; + case ZFS_PROP_ACLMODE: + *value = ZFS_ACLTYPE_OFF; + break; + default: + return (error); + } + error = 0; + } + + /* + * If one of the methods for getting the property value above worked, + * copy it into the objset_t's cache. + */ + if (error == 0 && cached_copy != NULL) { + *cached_copy = *value; + } + + return (error); +} + +/* + * Return true if the coresponding vfs's unmounted flag is set. + * Otherwise return false. + * If this function returns true we know VFS unmount has been initiated. + */ +boolean_t +zfs_get_vfs_flag_unmounted(objset_t *os) +{ + zfsvfs_t *zfvp; + boolean_t unmounted = B_FALSE; + + ASSERT(dmu_objset_type(os) == DMU_OST_ZFS); + + mutex_enter(&os->os_user_ptr_lock); + zfvp = dmu_objset_get_user(os); + if (zfvp != NULL && zfvp->z_vfs != NULL && + (vfs_isunmount(zfvp->z_vfs))) + unmounted = B_TRUE; + mutex_exit(&os->os_user_ptr_lock); + + return (unmounted); +} diff --git a/module/os/macos/zfs/zfs_vnops.c b/module/os/macos/zfs/zfs_vnops.c new file mode 100644 index 000000000000..d8bcc45f1574 --- /dev/null +++ b/module/os/macos/zfs/zfs_vnops.c @@ -0,0 +1,3685 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018 by Delphix. All rights reserved. + * Copyright (c) 2015 by Chunwei Chen. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. + * Copyright (c) 2013, 2020 Jorgen Lundman + */ + +/* Portions Copyright 2007 Jeremy Teo */ +/* Portions Copyright 2010 Robert Milkowski */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int zfs_vnop_force_formd_normalized_output = 0; /* disabled by default */ + +/* + * Programming rules. + * + * Each vnode op performs some logical unit of work. To do this, the ZPL must + * properly lock its in-core state, create a DMU transaction, do the work, + * record this work in the intent log (ZIL), commit the DMU transaction, + * and wait for the intent log to commit if it is a synchronous operation. + * Moreover, the vnode ops must work in both normal and log replay context. + * The ordering of events is important to avoid deadlocks and references + * to freed memory. The example below illustrates the following Big Rules: + * + * (1) A check must be made in each zfs thread for a mounted file system. + * This is done avoiding races using ZFS_ENTER(zfsvfs). + * A ZFS_EXIT(zfsvfs) is needed before all returns. Any znodes + * must be checked with ZFS_VERIFY_ZP(zp). Both of these macros + * can return EIO from the calling function. + * + * (2) zrele() should always be the last thing except for zil_commit() + * (if necessary) and ZFS_EXIT(). This is for 3 reasons: + * First, if it's the last reference, the vnode/znode + * can be freed, so the zp may point to freed memory. Second, the last + * reference will call zfs_zinactive(), which may induce a lot of work -- + * pushing cached pages (which acquires range locks) and syncing out + * cached atime changes. Third, zfs_zinactive() may require a new tx, + * which could deadlock the system if you were already holding one. + * If you must call zrele() within a tx then use zfs_zrele_async(). + * + * (3) All range locks must be grabbed before calling dmu_tx_assign(), + * as they can span dmu_tx_assign() calls. + * + * (4) If ZPL locks are held, pass TXG_NOWAIT as the second argument to + * dmu_tx_assign(). This is critical because we don't want to block + * while holding locks. + * + * If no ZPL locks are held (aside from ZFS_ENTER()), use TXG_WAIT. This + * reduces lock contention and CPU usage when we must wait (note that if + * throughput is constrained by the storage, nearly every transaction + * must wait). + * + * Note, in particular, that if a lock is sometimes acquired before + * the tx assigns, and sometimes after (e.g. z_lock), then failing + * to use a non-blocking assign can deadlock the system. The scenario: + * + * Thread A has grabbed a lock before calling dmu_tx_assign(). + * Thread B is in an already-assigned tx, and blocks for this lock. + * Thread A calls dmu_tx_assign(TXG_WAIT) and blocks in txg_wait_open() + * forever, because the previous txg can't quiesce until B's tx commits. + * + * If dmu_tx_assign() returns ERESTART and zfsvfs->z_assign is TXG_NOWAIT, + * then drop all locks, call dmu_tx_wait(), and try again. On subsequent + * calls to dmu_tx_assign(), pass TXG_NOTHROTTLE in addition to TXG_NOWAIT, + * to indicate that this operation has already called dmu_tx_wait(). + * This will ensure that we don't retry forever, waiting a short bit + * each time. + * + * (5) If the operation succeeded, generate the intent log entry for it + * before dropping locks. This ensures that the ordering of events + * in the intent log matches the order in which they actually occurred. + * During ZIL replay the zfs_log_* functions will update the sequence + * number to indicate the zil transaction has replayed. + * + * (6) At the end of each vnode op, the DMU tx must always commit, + * regardless of whether there were any errors. + * + * (7) After dropping all locks, invoke zil_commit(zilog, foid) + * to ensure that synchronous semantics are provided when necessary. + * + * In general, this is how things should be ordered in each vnode op: + * + * ZFS_ENTER(zfsvfs); // exit if unmounted + * top: + * zfs_dirent_lock(&dl, ...) // lock directory entry (may igrab()) + * rw_enter(...); // grab any other locks you need + * tx = dmu_tx_create(...); // get DMU tx + * dmu_tx_hold_*(); // hold each object you might modify + * error = dmu_tx_assign(tx, (waited ? TXG_NOTHROTTLE : 0) | TXG_NOWAIT); + * if (error) { + * rw_exit(...); // drop locks + * zfs_dirent_unlock(dl); // unlock directory entry + * zrele(...); // release held znodes + * if (error == ERESTART) { + * waited = B_TRUE; + * dmu_tx_wait(tx); + * dmu_tx_abort(tx); + * goto top; + * } + * dmu_tx_abort(tx); // abort DMU tx + * ZFS_EXIT(zfsvfs); // finished in zfs + * return (error); // really out of space + * } + * error = do_real_work(); // do whatever this VOP does + * if (error == 0) + * zfs_log_*(...); // on success, make ZIL entry + * dmu_tx_commit(tx); // commit DMU tx -- error or not + * rw_exit(...); // drop locks + * zfs_dirent_unlock(dl); // unlock directory entry + * zrele(...); // release held znodes + * zil_commit(zilog, foid); // synchronous when necessary + * ZFS_EXIT(zfsvfs); // finished in zfs + * return (error); // done, report error + */ + +/* + * Virus scanning is unsupported. It would be possible to add a hook + * here to performance the required virus scan. This could be done + * entirely in the kernel or potentially as an update to invoke a + * scanning utility. + */ +static int +zfs_vscan(struct vnode *vp, cred_t *cr, int async) +{ + return (0); +} + +/* ARGSUSED */ +int +zfs_open(struct vnode *vp, int mode, int flag, cred_t *cr) +{ + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = ITOZSB(vp); + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + + /* Honor ZFS_APPENDONLY file attribute */ + if ((mode & FWRITE) && (zp->z_pflags & ZFS_APPENDONLY) && + ((flag & O_APPEND) == 0)) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EPERM)); + } + + /* Virus scan eligible files on open */ + if (!zfs_has_ctldir(zp) && zfsvfs->z_vscan && S_ISREG(zp->z_mode) && + !(zp->z_pflags & ZFS_AV_QUARANTINED) && zp->z_size > 0) { + if (zfs_vscan(vp, cr, 0) != 0) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EACCES)); + } + } + + /* Keep a count of the synchronous opens in the znode */ + if (flag & (FSYNC | FDSYNC)) + atomic_inc_32(&zp->z_sync_cnt); + + ZFS_EXIT(zfsvfs); + return (0); +} + +/* ARGSUSED */ +int +zfs_close(struct vnode *vp, int flag, cred_t *cr) +{ + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = ITOZSB(vp); + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + + /* Decrement the synchronous opens in the znode */ + if (flag & (FSYNC | FDSYNC)) + atomic_dec_32(&zp->z_sync_cnt); + + if (!zfs_has_ctldir(zp) && zfsvfs->z_vscan && S_ISREG(zp->z_mode) && + !(zp->z_pflags & ZFS_AV_QUARANTINED) && zp->z_size > 0) + VERIFY(zfs_vscan(vp, cr, 1) == 0); + + ZFS_EXIT(zfsvfs); + return (0); +} + +#if defined(_KERNEL) +/* + * When a file is memory mapped, we must keep the IO data synchronized + * between the DMU cache and the memory mapped pages. What this means: + * + * On Write: If we find a memory mapped page, we write to *both* + * the page and the dmu buffer. + */ +void +update_pages(znode_t *zp, int64_t start, int len, + objset_t *os) +{ + int error = 0; + vm_offset_t vaddr = 0; + upl_t upl; + upl_page_info_t *pl = NULL; + int upl_size; + int upl_page; + off_t off; + + off = start & (PAGE_SIZE - 1); + start &= ~PAGE_MASK; + + upl_size = (off + len + (PAGE_SIZE - 1)) & ~PAGE_MASK; + + // dprintf("update_pages: start 0x%llx len 0x%llx: 1st off x%llx\n", + // start, len, off); + /* + * Create a UPL for the current range and map its + * page list into the kernel virtual address space. + */ + error = ubc_create_upl(ZTOV(zp), start, upl_size, &upl, &pl, + UPL_FILE_IO | UPL_SET_LITE); + if ((error != KERN_SUCCESS) || !upl) { + printf("ZFS: update_pages failed to ubc_create_upl: %d\n", + error); + return; + } + + if (ubc_upl_map(upl, &vaddr) != KERN_SUCCESS) { + printf("ZFS: update_pages failed to ubc_upl_map: %d\n", + error); + (void) ubc_upl_abort(upl, UPL_ABORT_FREE_ON_EMPTY); + return; + } + for (upl_page = 0; len > 0; ++upl_page) { + uint64_t nbytes = MIN(PAGESIZE - off, len); + /* + * We don't want a new page to "appear" in the middle of + * the file update (because it may not get the write + * update data), so we grab a lock to block + * zfs_getpage(). + */ + rw_enter(&zp->z_map_lock, RW_WRITER); + if (pl && upl_valid_page(pl, upl_page)) { + rw_exit(&zp->z_map_lock); + (void) dmu_read(os, zp->z_id, start+off, nbytes, + (void *)(vaddr+off), DMU_READ_PREFETCH); + + } else { // !upl_valid_page + rw_exit(&zp->z_map_lock); + } + vaddr += PAGE_SIZE; + start += PAGE_SIZE; + len -= nbytes; + off = 0; + } + + /* + * Unmap the page list and free the UPL. + */ + (void) ubc_upl_unmap(upl); + /* + * We want to abort here since due to dmu_write() + * we effectively didn't dirty any pages. + */ + (void) ubc_upl_abort(upl, UPL_ABORT_FREE_ON_EMPTY); +} + +/* + * When a file is memory mapped, we must keep the IO data synchronized + * between the DMU cache and the memory mapped pages. What this means: + * + * On Read: We "read" preferentially from memory mapped pages, + * else we default from the dmu buffer. + * + * NOTE: We will always "break up" the IO into PAGESIZE uiomoves when + * the file is memory mapped. + */ +int +mappedread(struct znode *zp, int nbytes, zfs_uio_t *uio) +{ + objset_t *os = zp->z_zfsvfs->z_os; + int len = nbytes; + int error = 0; + vm_offset_t vaddr = 0; + upl_t upl; + upl_page_info_t *pl = NULL; + off_t upl_start; + int upl_size; + int upl_page; + off_t off; + + upl_start = zfs_uio_offset(uio); + off = upl_start & PAGE_MASK; + upl_start &= ~PAGE_MASK; + upl_size = (off + nbytes + (PAGE_SIZE - 1)) & ~PAGE_MASK; + + /* + * Create a UPL for the current range and map its + * page list into the kernel virtual address space. + */ + error = ubc_create_upl(ZTOV(zp), upl_start, upl_size, &upl, &pl, + UPL_FILE_IO | UPL_SET_LITE); + if ((error != KERN_SUCCESS) || !upl) { + return (EIO); + } + + if (ubc_upl_map(upl, &vaddr) != KERN_SUCCESS) { + (void) ubc_upl_abort(upl, UPL_ABORT_FREE_ON_EMPTY); + return (ENOMEM); + } + + for (upl_page = 0; len > 0; ++upl_page) { + uint64_t bytes = MIN(PAGE_SIZE - off, len); + if (pl && upl_valid_page(pl, upl_page)) { + zfs_uio_setrw(uio, UIO_READ); + error = zfs_uiomove((caddr_t)vaddr + off, bytes, + UIO_READ, uio); + } else { + error = dmu_read_uio(os, zp->z_id, uio, bytes); + } + + vaddr += PAGE_SIZE; + len -= bytes; + off = 0; + if (error) + break; + } + + /* + * Unmap the page list and free the UPL. + */ + (void) ubc_upl_unmap(upl); + (void) ubc_upl_abort(upl, UPL_ABORT_FREE_ON_EMPTY); + + return (error); +} +#endif /* _KERNEL */ + +unsigned long zfs_delete_blocks = DMU_MAX_DELETEBLKCNT; + +/* + * Write the bytes to a file. + * + * IN: zp - znode of file to be written to + * data - bytes to write + * len - number of bytes to write + * pos - offset to start writing at + * + * OUT: resid - remaining bytes to write + * + * RETURN: 0 if success + * positive error code if failure + * + * Timestamps: + * zp - ctime|mtime updated if byte count > 0 + */ +int +zfs_write_simple(znode_t *zp, const void *data, size_t len, + loff_t pos, size_t *presid) +{ + int error = 0; + ssize_t resid; + + error = zfs_vn_rdwr(UIO_WRITE, ZTOV(zp), __DECONST(void *, data), len, + pos, UIO_SYSSPACE, IO_SYNC, RLIM64_INFINITY, NOCRED, &resid); + + if (error) { + return (SET_ERROR(error)); + } else if (presid == NULL) { + if (resid != 0) { + error = SET_ERROR(EIO); + } + } else { + *presid = resid; + } + return (error); +} + +/* + * Drop a reference on the passed inode asynchronously. This ensures + * that the caller will never drop the last reference on an inode in + * the current context. Doing so while holding open a tx could result + * in a deadlock if iput_final() re-enters the filesystem code. + */ +void +zfs_zrele_async(znode_t *zp) +{ + struct vnode *vp = ZTOV(zp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + objset_t *os = zfsvfs->z_os; + + ASSERT(os != NULL); + + /* If iocount > 1, AND, vp is set (not async_get) */ + if (vp != NULL && vnode_iocount(vp) > 1) { + VN_RELE(vp); + return; + } + + ASSERT3P(vp, !=, NULL); + + VERIFY(taskq_dispatch(dsl_pool_zrele_taskq(dmu_objset_pool(os)), + (task_func_t *)vnode_put, vp, TQ_SLEEP) != TASKQID_INVALID); +} + +/* + * Lookup an entry in a directory, or an extended attribute directory. + * If it exists, return a held inode reference for it. + * + * IN: zdp - znode of directory to search. + * nm - name of entry to lookup. + * flags - LOOKUP_XATTR set if looking for an attribute. + * cr - credentials of caller. + * direntflags - directory lookup flags + * realpnp - returned pathname. + * + * OUT: zpp - znode of located entry, NULL if not found. + * + * RETURN: 0 on success, error code on failure. + * + * Timestamps: + * NA + */ +/* ARGSUSED */ +int +zfs_lookup(znode_t *zdp, char *nm, znode_t **zpp, int flags, + cred_t *cr, int *direntflags, struct componentname *realpnp) +{ + zfsvfs_t *zfsvfs = ZTOZSB(zdp); + int error = 0; + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zdp); + + *zpp = NULL; + + /* + * OsX has separate vnops for XATTR activity + */ + + + if (!S_ISDIR(zdp->z_mode)) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(ENOTDIR)); + } + + /* + * Check accessibility of directory. + */ + + if ((error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr))) { + ZFS_EXIT(zfsvfs); + return (error); + } + + if (zfsvfs->z_utf8 && u8_validate(nm, strlen(nm), + NULL, U8_VALIDATE_ENTIRE, &error) < 0) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EILSEQ)); + } + + error = zfs_dirlook(zdp, nm, zpp, flags, direntflags, realpnp); + + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * Attempt to create a new entry in a directory. If the entry + * already exists, truncate the file if permissible, else return + * an error. Return the ip of the created or trunc'd file. + * + * IN: dzp - znode of directory to put new file entry in. + * name - name of new file entry. + * vap - attributes of new file. + * excl - flag indicating exclusive or non-exclusive mode. + * mode - mode to open file with. + * cr - credentials of caller. + * flag - file flag. + * vsecp - ACL to be set + * + * OUT: zpp - znode of created or trunc'd entry. + * + * RETURN: 0 on success, error code on failure. + * + * Timestamps: + * dzp - ctime|mtime updated if new entry created + * zp - ctime|mtime always, atime if new + */ + +/* ARGSUSED */ +int +zfs_create(znode_t *dzp, char *name, vattr_t *vap, int excl, + int mode, znode_t **zpp, cred_t *cr, int flag, vsecattr_t *vsecp) +{ + znode_t *zp = NULL; + zfsvfs_t *zfsvfs = ZTOZSB(dzp); + zilog_t *zilog; + objset_t *os; + zfs_dirlock_t *dl; + dmu_tx_t *tx; + int error; + uid_t uid; + gid_t gid; + zfs_acl_ids_t acl_ids; + boolean_t fuid_dirtied; + boolean_t have_acl = B_FALSE; + boolean_t waited = B_FALSE; + + /* + * If we have an ephemeral id, ACL, or XVATTR then + * make sure file system is at proper version + */ + + gid = crgetgid(cr); + uid = crgetuid(cr); + + if (zfsvfs->z_use_fuids == B_FALSE && + (vsecp || IS_EPHEMERAL(uid) || IS_EPHEMERAL(gid))) + return (SET_ERROR(EINVAL)); + + if (name == NULL) + return (SET_ERROR(EINVAL)); + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(dzp); + os = zfsvfs->z_os; + zilog = zfsvfs->z_log; + + if (zfsvfs->z_utf8 && u8_validate(name, strlen(name), + NULL, U8_VALIDATE_ENTIRE, &error) < 0) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EILSEQ)); + } + + if (vap->va_mask & ATTR_XVATTR) { + if ((error = secpolicy_xvattr(vap, + crgetuid(cr), cr, vap->va_mode)) != 0) { + ZFS_EXIT(zfsvfs); + return (error); + } + } + +top: + *zpp = NULL; + if (*name == '\0') { + /* + * Null component name refers to the directory itself. + */ + zhold(dzp); + zp = dzp; + dl = NULL; + error = 0; + } else { + /* possible igrab(zp) */ + int zflg = 0; + + if (flag & FIGNORECASE) + zflg |= ZCILOOK; + + error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg, + NULL, NULL); + if (error) { + if (have_acl) + zfs_acl_ids_free(&acl_ids); + if (strcmp(name, "..") == 0) + error = SET_ERROR(EISDIR); + ZFS_EXIT(zfsvfs); + return (error); + } + } + + if (zp == NULL) { + uint64_t txtype; + uint64_t projid = ZFS_DEFAULT_PROJID; + + /* + * Create a new file object and update the directory + * to reference it. + */ + if ((error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr))) { + if (have_acl) + zfs_acl_ids_free(&acl_ids); + goto out; + } + + /* + * We only support the creation of regular files in + * extended attribute directories. + */ + + if ((dzp->z_pflags & ZFS_XATTR) && !S_ISREG(vap->va_mode)) { + if (have_acl) + zfs_acl_ids_free(&acl_ids); + error = SET_ERROR(EINVAL); + goto out; + } + + if (!have_acl && (error = zfs_acl_ids_create(dzp, 0, vap, + cr, vsecp, &acl_ids)) != 0) + goto out; + have_acl = B_TRUE; + + if (S_ISREG(vap->va_mode) || S_ISDIR(vap->va_mode)) + projid = zfs_inherit_projid(dzp); + if (zfs_acl_ids_overquota(zfsvfs, &acl_ids, projid)) { + zfs_acl_ids_free(&acl_ids); + error = SET_ERROR(EDQUOT); + goto out; + } + + tx = dmu_tx_create(os); + + dmu_tx_hold_sa_create(tx, acl_ids.z_aclp->z_acl_bytes + + ZFS_SA_BASE_ATTR_SIZE); + + fuid_dirtied = zfsvfs->z_fuid_dirty; + if (fuid_dirtied) + zfs_fuid_txhold(zfsvfs, tx); + dmu_tx_hold_zap(tx, dzp->z_id, TRUE, name); + dmu_tx_hold_sa(tx, dzp->z_sa_hdl, B_FALSE); + if (!zfsvfs->z_use_sa && + acl_ids.z_aclp->z_acl_bytes > ZFS_ACE_SPACE) { + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, + 0, acl_ids.z_aclp->z_acl_bytes); + } + + error = dmu_tx_assign(tx, + (waited ? TXG_NOTHROTTLE : 0) | TXG_NOWAIT); + if (error) { + zfs_dirent_unlock(dl); + if (error == ERESTART) { + waited = B_TRUE; + dmu_tx_wait(tx); + dmu_tx_abort(tx); + goto top; + } + zfs_acl_ids_free(&acl_ids); + dmu_tx_abort(tx); + ZFS_EXIT(zfsvfs); + return (error); + } + + zfs_mknode(dzp, vap, tx, cr, 0, &zp, &acl_ids); + + error = zfs_link_create(dl, zp, tx, ZNEW); + if (error != 0) { + /* + * Since, we failed to add the directory entry for it, + * delete the newly created dnode. + */ + zfs_znode_delete(zp, tx); + zfs_acl_ids_free(&acl_ids); + dmu_tx_commit(tx); + + /* + * Failed, have zp but on OsX we don't have a vp, as it + * would have been attached below, and we've cleared out + * zp, signal then not to call zrele() on it. + */ + if (ZTOV(zp) == NULL) { + zfs_znode_free(zp); + zp = NULL; + } + + goto out; + } + + if (fuid_dirtied) + zfs_fuid_sync(zfsvfs, tx); + + txtype = zfs_log_create_txtype(Z_FILE, vsecp, vap); + if (flag & FIGNORECASE) + txtype |= TX_CI; + zfs_log_create(zilog, tx, txtype, dzp, zp, name, + vsecp, acl_ids.z_fuidp, vap); + zfs_acl_ids_free(&acl_ids); + dmu_tx_commit(tx); + + /* + * OS X - attach the vnode _after_ committing the transaction + */ + zfs_znode_getvnode(zp, zfsvfs); + + } else { + int aflags = (flag & O_APPEND) ? V_APPEND : 0; + + if (have_acl) + zfs_acl_ids_free(&acl_ids); + have_acl = B_FALSE; + + /* + * A directory entry already exists for this name. + */ + /* + * Can't truncate an existing file if in exclusive mode. + */ + if (excl) { + error = SET_ERROR(EEXIST); + goto out; + } + /* + * Can't open a directory for writing. + */ + if (S_ISDIR(zp->z_mode)) { + error = SET_ERROR(EISDIR); + goto out; + } + /* + * Verify requested access to file. + */ + if (mode && (error = zfs_zaccess_rwx(zp, mode, aflags, cr))) { + goto out; + } + + mutex_enter(&dzp->z_lock); + dzp->z_seq++; + mutex_exit(&dzp->z_lock); + + /* + * Truncate regular files if requested. + */ + if (S_ISREG(zp->z_mode) && + (vap->va_mask & ATTR_SIZE) && (vap->va_size == 0)) { + /* we can't hold any locks when calling zfs_freesp() */ + if (dl) { + zfs_dirent_unlock(dl); + dl = NULL; + } + error = zfs_freesp(zp, 0, 0, mode, TRUE); + } + } +out: + + if (dl) + zfs_dirent_unlock(dl); + + if (error) { + if (zp) + zrele(zp); + } else { + *zpp = zp; + } + + if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) + zil_commit(zilog, 0); + + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * Remove an entry from a directory. + * + * IN: dzp - znode of directory to remove entry from. + * name - name of entry to remove. + * cr - credentials of caller. + * flags - case flags. + * + * RETURN: 0 if success + * error code if failure + * + * Timestamps: + * dzp - ctime|mtime + * ip - ctime (if nlink > 0) + */ + +uint64_t null_xattr = 0; + +/*ARGSUSED*/ +int +zfs_remove(znode_t *dzp, char *name, cred_t *cr, int flags) +{ + znode_t *zp; + znode_t *xzp; + zfsvfs_t *zfsvfs = ZTOZSB(dzp); + zilog_t *zilog; + uint64_t acl_obj, xattr_obj; + uint64_t xattr_obj_unlinked = 0; + uint64_t obj = 0; + uint64_t links; + zfs_dirlock_t *dl; + dmu_tx_t *tx; + boolean_t may_delete_now, delete_now = FALSE; + boolean_t unlinked, toobig = FALSE; + uint64_t txtype; + struct componentname *realnmp = NULL; + struct componentname realnm = { 0 }; + int error; + int zflg = ZEXISTS; + boolean_t waited = B_FALSE; + + if (name == NULL) + return (SET_ERROR(EINVAL)); + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(dzp); + zilog = zfsvfs->z_log; + + if (flags & FIGNORECASE) { + zflg |= ZCILOOK; + + realnm.cn_nameptr = kmem_zalloc(MAXPATHLEN, KM_SLEEP); + realnm.cn_namelen = MAXPATHLEN; + realnmp = &realnm; + } + +top: + xattr_obj = 0; + xzp = NULL; + /* + * Attempt to lock directory; fail if entry doesn't exist. + */ + if ((error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg, + NULL, realnmp))) { + if (realnmp) + kmem_free(realnm.cn_nameptr, realnm.cn_namelen); + ZFS_EXIT(zfsvfs); + return (error); + } + + if ((error = zfs_zaccess_delete(dzp, zp, cr))) { + goto out; + } + + /* + * Need to use rmdir for removing directories. + */ + if (S_ISDIR(zp->z_mode)) { + error = SET_ERROR(EPERM); + goto out; + } + + mutex_enter(&zp->z_lock); + may_delete_now = vnode_iocount(ZTOV(zp)) == 1 && + !(zp->z_is_mapped); + mutex_exit(&zp->z_lock); + + /* + * We may delete the znode now, or we may put it in the unlinked set; + * it depends on whether we're the last link, and on whether there are + * other holds on the inode. So we dmu_tx_hold() the right things to + * allow for either case. + */ + obj = zp->z_id; + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_zap(tx, dzp->z_id, FALSE, name); + dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); + zfs_sa_upgrade_txholds(tx, zp); + zfs_sa_upgrade_txholds(tx, dzp); + if (may_delete_now) { + toobig = zp->z_size > zp->z_blksz * zfs_delete_blocks; + /* if the file is too big, only hold_free a token amount */ + dmu_tx_hold_free(tx, zp->z_id, 0, + (toobig ? DMU_MAX_ACCESS : DMU_OBJECT_END)); + } + + /* are there any extended attributes? */ + error = sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zfsvfs), + &xattr_obj, sizeof (xattr_obj)); + if (error == 0 && xattr_obj) { + error = zfs_zget(zfsvfs, xattr_obj, &xzp); + ASSERT0(error); + dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE); + dmu_tx_hold_sa(tx, xzp->z_sa_hdl, B_FALSE); + } + + mutex_enter(&zp->z_lock); + if ((acl_obj = zfs_external_acl(zp)) != 0 && may_delete_now) + dmu_tx_hold_free(tx, acl_obj, 0, DMU_OBJECT_END); + mutex_exit(&zp->z_lock); + + /* charge as an update -- would be nice not to charge at all */ + dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); + + /* + * Mark this transaction as typically resulting in a net free of space + */ + dmu_tx_mark_netfree(tx); + + error = dmu_tx_assign(tx, (waited ? TXG_NOTHROTTLE : 0) | TXG_NOWAIT); + if (error) { + zfs_dirent_unlock(dl); + if (error == ERESTART) { + waited = B_TRUE; + dmu_tx_wait(tx); + dmu_tx_abort(tx); + zrele(zp); + if (xzp) + zrele(xzp); + goto top; + } + if (realnmp) + kmem_free(realnm.cn_nameptr, realnm.cn_namelen); + dmu_tx_abort(tx); + zrele(zp); + if (xzp) + zrele(xzp); + ZFS_EXIT(zfsvfs); + return (error); + } + + /* + * Remove the directory entry. + */ + error = zfs_link_destroy(dl, zp, tx, zflg, &unlinked); + + if (error) { + dmu_tx_commit(tx); + goto out; + } + + if (unlinked) { + /* + * Hold z_lock so that we can make sure that the ACL obj + * hasn't changed. Could have been deleted due to + * zfs_sa_upgrade(). + */ + mutex_enter(&zp->z_lock); + (void) sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zfsvfs), + &xattr_obj_unlinked, sizeof (xattr_obj_unlinked)); + delete_now = may_delete_now && !toobig && + vnode_iocount(ZTOV(zp)) == 1 && + !(zp->z_is_mapped) && xattr_obj == xattr_obj_unlinked && + zfs_external_acl(zp) == acl_obj; + } + + if (delete_now) { + if (xattr_obj_unlinked) { + mutex_enter(&xzp->z_lock); + xzp->z_unlinked = B_TRUE; + links = 0; + error = sa_update(xzp->z_sa_hdl, SA_ZPL_LINKS(zfsvfs), + &links, sizeof (links), tx); + ASSERT3U(error, ==, 0); + mutex_exit(&xzp->z_lock); + zfs_unlinked_add(xzp, tx); + + if (zp->z_is_sa) + error = sa_remove(zp->z_sa_hdl, + SA_ZPL_XATTR(zfsvfs), tx); + else + error = sa_update(zp->z_sa_hdl, + SA_ZPL_XATTR(zfsvfs), &null_xattr, + sizeof (uint64_t), tx); + ASSERT0(error); + } + /* + * Add to the unlinked set because a new reference could be + * taken concurrently resulting in a deferred destruction. + */ + zfs_unlinked_add(zp, tx); + mutex_exit(&zp->z_lock); + } else if (unlinked) { + mutex_exit(&zp->z_lock); + zfs_unlinked_add(zp, tx); + } + + txtype = TX_REMOVE; + if (flags & FIGNORECASE) + txtype |= TX_CI; + zfs_log_remove(zilog, tx, txtype, dzp, name, obj, unlinked); + + dmu_tx_commit(tx); +out: + if (realnmp) + kmem_free(realnm.cn_nameptr, realnm.cn_namelen); + + zfs_dirent_unlock(dl); + + if (delete_now) + zrele(zp); + else + zfs_zrele_async(zp); + + if (xzp) + zfs_zrele_async(xzp); + + if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) + zil_commit(zilog, 0); + + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * Create a new directory and insert it into dzp using the name + * provided. Return a pointer to the inserted directory. + * + * IN: dzp - znode of directory to add subdir to. + * dirname - name of new directory. + * vap - attributes of new directory. + * cr - credentials of caller. + * flags - case flags. + * vsecp - ACL to be set + * + * OUT: zpp - znode of created directory. + * + * RETURN: 0 if success + * error code if failure + * + * Timestamps: + * dzp - ctime|mtime updated + * zpp - ctime|mtime|atime updated + */ +/*ARGSUSED*/ +int +zfs_mkdir(znode_t *dzp, char *dirname, vattr_t *vap, znode_t **zpp, + cred_t *cr, int flags, vsecattr_t *vsecp) +{ + znode_t *zp; + zfsvfs_t *zfsvfs = ZTOZSB(dzp); + zilog_t *zilog; + zfs_dirlock_t *dl; + uint64_t txtype; + dmu_tx_t *tx; + int error; + int zf = ZNEW; + uid_t uid; + gid_t gid = crgetgid(cr); + zfs_acl_ids_t acl_ids; + boolean_t fuid_dirtied; + boolean_t waited = B_FALSE; + + ASSERT(S_ISDIR(vap->va_mode)); + + /* + * If we have an ephemeral id, ACL, or XVATTR then + * make sure file system is at proper version + */ + + uid = crgetuid(cr); + if (zfsvfs->z_use_fuids == B_FALSE && + (vsecp || IS_EPHEMERAL(uid) || IS_EPHEMERAL(gid))) + return (SET_ERROR(EINVAL)); + + if (dirname == NULL) + return (SET_ERROR(EINVAL)); + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(dzp); + zilog = zfsvfs->z_log; + + if (dzp->z_pflags & ZFS_XATTR) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EINVAL)); + } + + if (zfsvfs->z_utf8 && u8_validate(dirname, + strlen(dirname), NULL, U8_VALIDATE_ENTIRE, &error) < 0) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EILSEQ)); + } + if (flags & FIGNORECASE) + zf |= ZCILOOK; + + if (vap->va_mask & ATTR_XVATTR) { + if ((error = secpolicy_xvattr(vap, + crgetuid(cr), cr, vap->va_mode)) != 0) { + ZFS_EXIT(zfsvfs); + return (error); + } + } + + if ((error = zfs_acl_ids_create(dzp, 0, vap, cr, + vsecp, &acl_ids)) != 0) { + ZFS_EXIT(zfsvfs); + return (error); + } + /* + * First make sure the new directory doesn't exist. + * + * Existence is checked first to make sure we don't return + * EACCES instead of EEXIST which can cause some applications + * to fail. + */ +top: + *zpp = NULL; + + if ((error = zfs_dirent_lock(&dl, dzp, dirname, &zp, zf, + NULL, NULL))) { + zfs_acl_ids_free(&acl_ids); + ZFS_EXIT(zfsvfs); + return (error); + } + + if ((error = zfs_zaccess(dzp, ACE_ADD_SUBDIRECTORY, 0, B_FALSE, cr))) { + zfs_acl_ids_free(&acl_ids); + zfs_dirent_unlock(dl); + ZFS_EXIT(zfsvfs); + return (error); + } + + if (zfs_acl_ids_overquota(zfsvfs, &acl_ids, zfs_inherit_projid(dzp))) { + zfs_acl_ids_free(&acl_ids); + zfs_dirent_unlock(dl); + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EDQUOT)); + } + + /* + * Add a new entry to the directory. + */ + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_zap(tx, dzp->z_id, TRUE, dirname); + dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); + fuid_dirtied = zfsvfs->z_fuid_dirty; + if (fuid_dirtied) + zfs_fuid_txhold(zfsvfs, tx); + if (!zfsvfs->z_use_sa && acl_ids.z_aclp->z_acl_bytes > ZFS_ACE_SPACE) { + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, + acl_ids.z_aclp->z_acl_bytes); + } + + dmu_tx_hold_sa_create(tx, acl_ids.z_aclp->z_acl_bytes + + ZFS_SA_BASE_ATTR_SIZE); + + error = dmu_tx_assign(tx, (waited ? TXG_NOTHROTTLE : 0) | TXG_NOWAIT); + if (error) { + zfs_dirent_unlock(dl); + if (error == ERESTART) { + waited = B_TRUE; + dmu_tx_wait(tx); + dmu_tx_abort(tx); + goto top; + } + zfs_acl_ids_free(&acl_ids); + dmu_tx_abort(tx); + ZFS_EXIT(zfsvfs); + return (error); + } + + /* + * Create new node. + */ + zfs_mknode(dzp, vap, tx, cr, 0, &zp, &acl_ids); + + /* + * Now put new name in parent dir. + */ + error = zfs_link_create(dl, zp, tx, ZNEW); + if (error != 0) { + zfs_znode_delete(zp, tx); + goto out; + } + + if (fuid_dirtied) + zfs_fuid_sync(zfsvfs, tx); + + *zpp = zp; + + txtype = zfs_log_create_txtype(Z_DIR, vsecp, vap); + if (flags & FIGNORECASE) + txtype |= TX_CI; + zfs_log_create(zilog, tx, txtype, dzp, zp, dirname, vsecp, + acl_ids.z_fuidp, vap); + +out: + zfs_acl_ids_free(&acl_ids); + + dmu_tx_commit(tx); + /* + * OS X - attach the vnode _after_ committing the transaction + */ + zfs_znode_getvnode(zp, zfsvfs); + + zfs_dirent_unlock(dl); + + if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) + zil_commit(zilog, 0); + + if (error != 0) { + zrele(zp); + } else { + } + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * Remove a directory subdir entry. If the current working + * directory is the same as the subdir to be removed, the + * remove will fail. + * + * IN: dzp - znode of directory to remove from. + * name - name of directory to be removed. + * cwd - inode of current working directory. + * cr - credentials of caller. + * flags - case flags + * + * RETURN: 0 on success, error code on failure. + * + * Timestamps: + * dzp - ctime|mtime updated + */ +/*ARGSUSED*/ +int +zfs_rmdir(znode_t *dzp, char *name, znode_t *cwd, cred_t *cr, + int flags) +{ + znode_t *zp; + zfsvfs_t *zfsvfs = ZTOZSB(dzp); + zilog_t *zilog; + zfs_dirlock_t *dl; + dmu_tx_t *tx; + int error; + int zflg = ZEXISTS; + boolean_t waited = B_FALSE; + + if (name == NULL) + return (SET_ERROR(EINVAL)); + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(dzp); + zilog = zfsvfs->z_log; + + if (flags & FIGNORECASE) + zflg |= ZCILOOK; +top: + zp = NULL; + + /* + * Attempt to lock directory; fail if entry doesn't exist. + */ + if ((error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg, + NULL, NULL))) { + ZFS_EXIT(zfsvfs); + return (error); + } + + if ((error = zfs_zaccess_delete(dzp, zp, cr))) { + goto out; + } + + if (ZTOTYPE(zp) != VDIR) { + error = SET_ERROR(ENOTDIR); + goto out; + } + + if (zp == cwd) { + error = SET_ERROR(EINVAL); + goto out; + } + + /* + * Grab a lock on the directory to make sure that no one is + * trying to add (or lookup) entries while we are removing it. + */ + rw_enter(&zp->z_name_lock, RW_WRITER); + + /* + * Grab a lock on the parent pointer to make sure we play well + * with the treewalk and directory rename code. + */ + rw_enter(&zp->z_parent_lock, RW_WRITER); + + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_zap(tx, dzp->z_id, FALSE, name); + dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); + dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); + zfs_sa_upgrade_txholds(tx, zp); + zfs_sa_upgrade_txholds(tx, dzp); + dmu_tx_mark_netfree(tx); + error = dmu_tx_assign(tx, (waited ? TXG_NOTHROTTLE : 0) | TXG_NOWAIT); + if (error) { + rw_exit(&zp->z_parent_lock); + rw_exit(&zp->z_name_lock); + zfs_dirent_unlock(dl); + if (error == ERESTART) { + waited = B_TRUE; + dmu_tx_wait(tx); + dmu_tx_abort(tx); + zrele(zp); + goto top; + } + dmu_tx_abort(tx); + zrele(zp); + ZFS_EXIT(zfsvfs); + return (error); + } + + error = zfs_link_destroy(dl, zp, tx, zflg, NULL); + + if (error == 0) { + uint64_t txtype = TX_RMDIR; + if (flags & FIGNORECASE) + txtype |= TX_CI; + zfs_log_remove(zilog, tx, txtype, dzp, name, ZFS_NO_OBJECT, + B_FALSE); + } + + dmu_tx_commit(tx); + + rw_exit(&zp->z_parent_lock); + rw_exit(&zp->z_name_lock); +out: + zfs_dirent_unlock(dl); + + zrele(zp); + + if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) + zil_commit(zilog, 0); + + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * Read directory entries from the given directory cursor position and emit + * name and position for each entry. + * + * IN: ip - inode of directory to read. + * ctx - directory entry context. + * cr - credentials of caller. + * + * RETURN: 0 if success + * error code if failure + * + * Timestamps: + * ip - atime updated + * + * Note that the low 4 bits of the cookie returned by zap is always zero. + * This allows us to use the low range for "special" directory entries: + * We use 0 for '.', and 1 for '..'. If this is the root of the filesystem, + * we use the offset 2 for the '.zfs' directory. + */ +/* ARGSUSED */ +int +zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, int *eofp, + int flags, int *a_numdirent) +{ + + znode_t *zp = VTOZ(vp); + boolean_t extended = (flags & VNODE_READDIR_EXTENDED); + struct direntry *eodp; /* Extended */ + struct dirent *odp; /* Standard */ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + objset_t *os; + caddr_t outbuf; + size_t bufsize; + zap_cursor_t zc; + zap_attribute_t zap; + uint_t bytes_wanted; + uint64_t offset; /* must be unsigned; checks for < 1 */ + uint64_t parent; + int local_eof; + int outcount; + int error = 0; + uint8_t prefetch; + uint8_t type; + int numdirent = 0; + char *bufptr; + boolean_t isdotdir = B_TRUE; + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + + if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_PARENT(zfsvfs), + &parent, sizeof (parent))) != 0) + goto out; + + /* + * If we are not given an eof variable, + * use a local one. + */ + if (eofp == NULL) + eofp = &local_eof; + + /* + * Check for valid iov_len. + */ + if (zfs_uio_iovlen(uio, 0) <= 0) { + error = EINVAL; + goto out; + } + + /* + * Quit if directory has been removed (posix) + */ + if ((*eofp = zp->z_unlinked) != 0) { + goto out; + } + + error = 0; + os = zfsvfs->z_os; + offset = zfs_uio_offset(uio); + prefetch = zp->z_zn_prefetch; + + /* + * Initialize the iterator cursor. + */ + if (offset <= 3) { + /* + * Start iteration from the beginning of the directory. + */ + zap_cursor_init(&zc, os, zp->z_id); + } else { + /* + * The offset is a serialized cursor. + */ + zap_cursor_init_serialized(&zc, os, zp->z_id, offset); + } + + /* + * Get space to change directory entries into fs independent format. + */ + bytes_wanted = zfs_uio_iovlen(uio, 0); + bufsize = (size_t)bytes_wanted; + outbuf = kmem_alloc(bufsize, KM_SLEEP); + bufptr = (char *)outbuf; + + /* + * Transform to file-system independent format + */ + + outcount = 0; + while (outcount < bytes_wanted) { + ino64_t objnum; + ushort_t reclen; + uint64_t *next = NULL; + size_t namelen; + int force_formd_normalized_output; + size_t nfdlen; + + /* + * Special case `.', `..', and `.zfs'. + */ + if (offset == 0) { + (void) strlcpy(zap.za_name, ".", MAXNAMELEN); + zap.za_normalization_conflict = 0; + objnum = (zp->z_id == zfsvfs->z_root) ? 2 : zp->z_id; + type = DT_DIR; + } else if (offset == 1) { + (void) strlcpy(zap.za_name, "..", MAXNAMELEN); + zap.za_normalization_conflict = 0; + objnum = (parent == zfsvfs->z_root) ? 2 : parent; + objnum = (zp->z_id == zfsvfs->z_root) ? 1 : objnum; + type = DT_DIR; + } else if (offset == 2 && zfs_show_ctldir(zp)) { + (void) strlcpy(zap.za_name, ZFS_CTLDIR_NAME, + MAXNAMELEN); + zap.za_normalization_conflict = 0; + objnum = ZFSCTL_INO_ROOT; + type = DT_DIR; + } else { + + /* This is not a special case directory */ + isdotdir = B_FALSE; + + /* + * Grab next entry. + */ + if ((error = zap_cursor_retrieve(&zc, &zap))) { + if ((*eofp = (error == ENOENT)) != 0) + break; + else + goto update; + } + + /* + * Allow multiple entries provided the first entry is + * the object id. Non-zpl consumers may safely make + * use of the additional space. + * + * XXX: This should be a feature flag for compatibility + */ + if (zap.za_integer_length != 8 || + zap.za_num_integers != 1) { + cmn_err(CE_WARN, "zap_readdir: bad directory " + "entry, obj = %lld, offset = %lld\n", + (u_longlong_t)zp->z_id, + (u_longlong_t)offset); + error = SET_ERROR(ENXIO); + goto update; + } + + objnum = ZFS_DIRENT_OBJ(zap.za_first_integer); + /* + * MacOS X can extract the object type here such as: + * uint8_t type = ZFS_DIRENT_TYPE(zap.za_first_integer); + */ + type = ZFS_DIRENT_TYPE(zap.za_first_integer); + + } + + /* emit start */ + +#define DIRENT_RECLEN(namelen, ext) \ + ((ext) ? \ + ((sizeof (struct direntry) + (namelen) - (MAXPATHLEN-1) + 7) & ~7) \ + : \ + ((sizeof (struct dirent) - (NAME_MAX+1)) + (((namelen)+1 + 7) &~ 7))) + + /* + * Check if name will fit. + * + * Note: non-ascii names may expand (3x) when converted to NFD + */ + namelen = strlen(zap.za_name); + + /* sysctl to force formD normalization of vnop output */ + if (zfs_vnop_force_formd_normalized_output && + !is_ascii_str(zap.za_name)) + force_formd_normalized_output = 1; + else + force_formd_normalized_output = 0; + + if (force_formd_normalized_output) + namelen = MIN(extended ? MAXPATHLEN-1 : MAXNAMLEN, + namelen * 3); + + reclen = DIRENT_RECLEN(namelen, extended); + + /* + * Will this entry fit in the buffer? + */ + if (outcount + reclen > bufsize) { + /* + * Did we manage to fit anything in the buffer? + */ + if (!outcount) { + error = (EINVAL); + goto update; + } + break; + } + + if (extended) { + /* + * Add extended flag entry: + */ + eodp = (struct direntry *)bufptr; + /* NOTE: d_seekoff is the offset for the *next* entry */ + next = &(eodp->d_seekoff); + eodp->d_ino = INO_ZFSTOXNU(objnum, zfsvfs->z_root); + eodp->d_type = type; + + /* + * Mac OS X: non-ascii names are UTF-8 NFC on disk + * so convert to NFD before exporting them. + */ + namelen = strlen(zap.za_name); + if (!force_formd_normalized_output || + utf8_normalizestr((const u_int8_t *)zap.za_name, + namelen, (u_int8_t *)eodp->d_name, &nfdlen, + MAXPATHLEN-1, UTF_DECOMPOSED) != 0) { + /* ASCII or normalization failed, copy zap */ + if ((namelen > 0)) + (void) bcopy(zap.za_name, eodp->d_name, + namelen + 1); + } else { + /* Normalization succeeded (in buffer) */ + namelen = nfdlen; + } + eodp->d_namlen = namelen; + eodp->d_reclen = reclen = + DIRENT_RECLEN(namelen, extended); + + } else { + /* + * Add normal entry: + */ + + odp = (struct dirent *)bufptr; + odp->d_ino = INO_ZFSTOXNU(objnum, zfsvfs->z_root); + odp->d_type = type; + + /* + * Mac OS X: non-ascii names are UTF-8 NFC on disk + * so convert to NFD before exporting them. + */ + namelen = strlen(zap.za_name); + if (!force_formd_normalized_output || + utf8_normalizestr((const u_int8_t *)zap.za_name, + namelen, (u_int8_t *)odp->d_name, &nfdlen, + MAXNAMLEN, UTF_DECOMPOSED) != 0) { + /* ASCII or normalization failed, copy zap */ + if ((namelen > 0)) + (void) bcopy(zap.za_name, odp->d_name, + namelen + 1); + } else { + /* Normalization succeeded (in buffer). */ + namelen = nfdlen; + } + odp->d_namlen = namelen; + odp->d_reclen = reclen = + DIRENT_RECLEN(namelen, extended); + } + + outcount += reclen; + bufptr += reclen; + numdirent++; + + ASSERT(outcount <= bufsize); + + /* emit done */ + + /* Prefetch znode */ + if (prefetch) + dmu_prefetch(os, objnum, 0, 0, 0, + ZIO_PRIORITY_SYNC_READ); + + /* + * Move to the next entry, fill in the previous offset. + */ + if (offset > 2 || (offset == 2 && !zfs_show_ctldir(zp))) { + zap_cursor_advance(&zc); + offset = zap_cursor_serialize(&zc); + } else { + offset += 1; + } + + if (extended) + *next = offset; + } + zp->z_zn_prefetch = B_FALSE; /* a lookup will re-enable pre-fetching */ + + /* All done, copy temporary buffer to userland */ + if ((error = zfs_uiomove(outbuf, (long)outcount, UIO_READ, uio))) { + /* + * Reset the pointer. + */ + offset = zfs_uio_offset(uio); + } + + +update: + zap_cursor_fini(&zc); + if (outbuf) { + kmem_free(outbuf, bufsize); + } + + if (error == ENOENT) + error = 0; + + zfs_uio_setoffset(uio, offset); + if (a_numdirent) + *a_numdirent = numdirent; + +out: + ZFS_EXIT(zfsvfs); + + dprintf("-zfs_readdir: num %d\n", numdirent); + + return (error); +} + +ulong_t zfs_fsync_sync_cnt = 4; + +/* Explore if we can use zfs/zfs_vnops.c's zfs_fsync() */ +int +zfs_fsync(znode_t *zp, int syncflag, cred_t *cr) +{ + zfsvfs_t *zfsvfs = ZTOZSB(zp); + vnode_t *vp = ZTOV(zp); + + if (zp->z_is_mapped /* && !(syncflag & FNODSYNC) */ && + vnode_isreg(vp) && !vnode_isswap(vp)) { + cluster_push(vp, /* waitdata ? IO_SYNC : */ 0); + } + + (void) tsd_set(zfs_fsyncer_key, (void *)zfs_fsync_sync_cnt); + + if (zfsvfs->z_os->os_sync != ZFS_SYNC_DISABLED && + !vnode_isrecycled(ZTOV(zp))) { + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + zil_commit(zfsvfs->z_log, zp->z_id); + ZFS_EXIT(zfsvfs); + } + tsd_set(zfs_fsyncer_key, NULL); + + return (0); +} + +/* + * Get the requested file attributes and place them in the provided + * vattr structure. + * + * IN: vp - vnode of file. + * vap - va_mask identifies requested attributes. + * If ATTR_XVATTR set, then optional attrs are requested + * flags - ATTR_NOACLCHECK (CIFS server context) + * cr - credentials of caller. + * ct - caller context + * + * OUT: vap - attribute values. + * + * RETURN: 0 (always succeeds) + */ +int +zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) +{ + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + int error = 0; + uint64_t links; + uint64_t mtime[2], ctime[2], crtime[2], rdev; + xvattr_t *xvap = (xvattr_t *)vap; /* vap may be an xvattr_t * */ + xoptattr_t *xoap = NULL; + boolean_t skipaclchk = /* (flags&ATTR_NOACLCHECK)?B_TRUE: */ B_FALSE; + sa_bulk_attr_t bulk[4]; + int count = 0; + + VERIFY3P(zp->z_zfsvfs, ==, vfs_fsprivate(vnode_mount(vp))); + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + + zfs_fuid_map_ids(zp, cr, &vap->va_uid, &vap->va_gid); + + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL, &mtime, 16); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, &ctime, 16); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CRTIME(zfsvfs), NULL, &crtime, 16); + if (vnode_isblk(vp) || vnode_ischr(vp)) + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_RDEV(zfsvfs), NULL, + &rdev, 8); + + if ((error = sa_bulk_lookup(zp->z_sa_hdl, bulk, count)) != 0) { + ZFS_EXIT(zfsvfs); + return (error); + } + + /* + * If ACL is trivial don't bother looking for ACE_READ_ATTRIBUTES. + * Also, if we are the owner don't bother, since owner should + * always be allowed to read basic attributes of file. + */ + if (!(zp->z_pflags & ZFS_ACL_TRIVIAL) && + (vap->va_uid != crgetuid(cr))) { + if ((error = zfs_zaccess(zp, ACE_READ_ATTRIBUTES, 0, + skipaclchk, cr))) { + ZFS_EXIT(zfsvfs); + return (error); + } + } + + /* + * Return all attributes. It's cheaper to provide the answer + * than to determine whether we were asked the question. + */ + + mutex_enter(&zp->z_lock); + vap->va_type = IFTOVT(zp->z_mode); + vap->va_mode = zp->z_mode & ~S_IFMT; + vap->va_nodeid = zp->z_id; + if (vnode_isvroot((vp)) && zfs_show_ctldir(zp)) + links = zp->z_links + 1; + else + links = zp->z_links; + vap->va_nlink = MIN(links, LINK_MAX); /* nlink_t limit! */ + vap->va_size = zp->z_size; + if (vnode_isblk(vp) || vnode_ischr(vp)) + vap->va_rdev = zfs_cmpldev(rdev); + + vap->va_flags = 0; /* FreeBSD: Reset chflags(2) flags. */ + + /* + * Add in any requested optional attributes and the create time. + * Also set the corresponding bits in the returned attribute bitmap. + */ + if ((xoap = xva_getxoptattr(xvap)) != NULL && zfsvfs->z_use_fuids) { + if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE)) { + xoap->xoa_archive = + ((zp->z_pflags & ZFS_ARCHIVE) != 0); + XVA_SET_RTN(xvap, XAT_ARCHIVE); + } + + if (XVA_ISSET_REQ(xvap, XAT_READONLY)) { + xoap->xoa_readonly = + ((zp->z_pflags & ZFS_READONLY) != 0); + XVA_SET_RTN(xvap, XAT_READONLY); + } + + if (XVA_ISSET_REQ(xvap, XAT_SYSTEM)) { + xoap->xoa_system = + ((zp->z_pflags & ZFS_SYSTEM) != 0); + XVA_SET_RTN(xvap, XAT_SYSTEM); + } + + if (XVA_ISSET_REQ(xvap, XAT_HIDDEN)) { + xoap->xoa_hidden = + ((zp->z_pflags & ZFS_HIDDEN) != 0); + XVA_SET_RTN(xvap, XAT_HIDDEN); + } + + if (XVA_ISSET_REQ(xvap, XAT_NOUNLINK)) { + xoap->xoa_nounlink = + ((zp->z_pflags & ZFS_NOUNLINK) != 0); + XVA_SET_RTN(xvap, XAT_NOUNLINK); + } + + if (XVA_ISSET_REQ(xvap, XAT_IMMUTABLE)) { + xoap->xoa_immutable = + ((zp->z_pflags & ZFS_IMMUTABLE) != 0); + XVA_SET_RTN(xvap, XAT_IMMUTABLE); + } + + if (XVA_ISSET_REQ(xvap, XAT_APPENDONLY)) { + xoap->xoa_appendonly = + ((zp->z_pflags & ZFS_APPENDONLY) != 0); + XVA_SET_RTN(xvap, XAT_APPENDONLY); + } + + if (XVA_ISSET_REQ(xvap, XAT_NODUMP)) { + xoap->xoa_nodump = + ((zp->z_pflags & ZFS_NODUMP) != 0); + XVA_SET_RTN(xvap, XAT_NODUMP); + } + + if (XVA_ISSET_REQ(xvap, XAT_OPAQUE)) { + xoap->xoa_opaque = + ((zp->z_pflags & ZFS_OPAQUE) != 0); + XVA_SET_RTN(xvap, XAT_OPAQUE); + } + + if (XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED)) { + xoap->xoa_av_quarantined = + ((zp->z_pflags & ZFS_AV_QUARANTINED) != 0); + XVA_SET_RTN(xvap, XAT_AV_QUARANTINED); + } + + if (XVA_ISSET_REQ(xvap, XAT_AV_MODIFIED)) { + xoap->xoa_av_modified = + ((zp->z_pflags & ZFS_AV_MODIFIED) != 0); + XVA_SET_RTN(xvap, XAT_AV_MODIFIED); + } + + if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP) && + vnode_isreg(vp)) { + zfs_sa_get_scanstamp(zp, xvap); + } + if (XVA_ISSET_REQ(xvap, XAT_CREATETIME)) { + uint64_t times[2]; + + (void) sa_lookup(zp->z_sa_hdl, SA_ZPL_CRTIME(zfsvfs), + times, sizeof (times)); + ZFS_TIME_DECODE(&xoap->xoa_createtime, times); + XVA_SET_RTN(xvap, XAT_CREATETIME); + } + + if (XVA_ISSET_REQ(xvap, XAT_REPARSE)) { + xoap->xoa_reparse = ((zp->z_pflags & ZFS_REPARSE) != 0); + XVA_SET_RTN(xvap, XAT_REPARSE); + } + if (XVA_ISSET_REQ(xvap, XAT_GEN)) { + xoap->xoa_generation = zp->z_gen; + XVA_SET_RTN(xvap, XAT_GEN); + } + + if (XVA_ISSET_REQ(xvap, XAT_OFFLINE)) { + xoap->xoa_offline = + ((zp->z_pflags & ZFS_OFFLINE) != 0); + XVA_SET_RTN(xvap, XAT_OFFLINE); + } + + if (XVA_ISSET_REQ(xvap, XAT_SPARSE)) { + xoap->xoa_sparse = + ((zp->z_pflags & ZFS_SPARSE) != 0); + XVA_SET_RTN(xvap, XAT_SPARSE); + } + } + + ZFS_TIME_DECODE(&vap->va_atime, zp->z_atime); + ZFS_TIME_DECODE(&vap->va_mtime, mtime); + ZFS_TIME_DECODE(&vap->va_ctime, ctime); + ZFS_TIME_DECODE(&vap->va_crtime, crtime); + + mutex_exit(&zp->z_lock); + + /* + * If we are told to ignore owners, we scribble over the + * uid and gid here unless root. + */ + if (((unsigned int)vfs_flags(zfsvfs->z_vfs)) & MNT_IGNORE_OWNERSHIP) { + if (kauth_cred_getuid(cr) != 0) { + vap->va_uid = UNKNOWNUID; + vap->va_gid = UNKNOWNGID; + } + } + + ZFS_EXIT(zfsvfs); + return (0); +} + +/* + * Set the file attributes to the values contained in the + * vattr structure. + * + * IN: zp - znode of file to be modified. + * vap - new attribute values. + * If AT_XVATTR set, then optional attrs are being set + * flags - ATTR_UTIME set if non-default time values provided. + * - ATTR_NOACLCHECK (CIFS context only). + * cr - credentials of caller. + * ct - caller context + * + * RETURN: 0 on success, error code on failure. + * + * Timestamps: + * vp - ctime updated, mtime updated if size changed. + */ +/* ARGSUSED */ +int +zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr) +{ + vnode_t *vp = ZTOV(zp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + objset_t *os = zfsvfs->z_os; + zilog_t *zilog; + dmu_tx_t *tx; + vattr_t oldva; + xvattr_t tmpxvattr; + uint_t mask = vap->va_mask; + uint_t saved_mask = 0; + uint64_t saved_mode; + int trim_mask = 0; + uint64_t new_mode; + uint64_t new_uid, new_gid; + uint64_t xattr_obj; + uint64_t mtime[2], ctime[2], crtime[2]; + uint64_t projid = ZFS_INVALID_PROJID; + znode_t *attrzp; + int need_policy = FALSE; + int err, err2; + zfs_fuid_info_t *fuidp = NULL; + xvattr_t *xvap = (xvattr_t *)vap; /* vap may be an xvattr_t * */ + xoptattr_t *xoap; + zfs_acl_t *aclp; + boolean_t skipaclchk = (flags & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE; + boolean_t fuid_dirtied = B_FALSE; + sa_bulk_attr_t bulk[7], xattr_bulk[7]; + int count = 0, xattr_count = 0; + + if (mask == 0) + return (0); + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + + zilog = zfsvfs->z_log; + + /* + * Make sure that if we have ephemeral uid/gid or xvattr specified + * that file system is at proper version level + */ + + if (zfsvfs->z_use_fuids == B_FALSE && + (((mask & ATTR_UID) && IS_EPHEMERAL(vap->va_uid)) || + ((mask & ATTR_GID) && IS_EPHEMERAL(vap->va_gid)) || + (mask & ATTR_XVATTR))) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EINVAL)); + } + + if (mask & ATTR_SIZE && vnode_vtype(vp) == VDIR) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EISDIR)); + } + + if (mask & ATTR_SIZE && vnode_vtype(vp) != VREG && + vnode_vtype(vp) != VFIFO) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EINVAL)); + } + + /* + * If this is an xvattr_t, then get a pointer to the structure of + * optional attributes. If this is NULL, then we have a vattr_t. + */ + xoap = xva_getxoptattr(xvap); + + xva_init(&tmpxvattr); + + /* + * Immutable files can only alter immutable bit and atime + */ + if ((zp->z_pflags & ZFS_IMMUTABLE) && + ((mask & (ATTR_SIZE|ATTR_UID|ATTR_GID|ATTR_MTIME|ATTR_MODE)) || + ((mask & ATTR_XVATTR) && XVA_ISSET_REQ(xvap, XAT_CREATETIME)))) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EPERM)); + } + + /* + * Note: ZFS_READONLY is handled in zfs_zaccess_common. + */ + + /* + * Verify timestamps doesn't overflow 32 bits. + * ZFS can handle large timestamps, but 32bit syscalls can't + * handle times greater than 2039. This check should be removed + * once large timestamps are fully supported. + */ + if (mask & (ATTR_ATIME | ATTR_MTIME)) { + if (((mask & ATTR_ATIME) && + TIMESPEC_OVERFLOW(&vap->va_atime)) || + ((mask & ATTR_MTIME) && + TIMESPEC_OVERFLOW(&vap->va_mtime))) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EOVERFLOW)); + } + } + if (xoap != NULL && (mask & ATTR_XVATTR)) { + if (XVA_ISSET_REQ(xvap, XAT_CREATETIME) && + TIMESPEC_OVERFLOW(&vap->va_create_time)) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EOVERFLOW)); + } + + if (XVA_ISSET_REQ(xvap, XAT_PROJID)) { + if (!dmu_objset_projectquota_enabled(os) || + (!S_ISREG(zp->z_mode) && !S_ISDIR(zp->z_mode))) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EOPNOTSUPP)); + } + + projid = xoap->xoa_projid; + if (unlikely(projid == ZFS_INVALID_PROJID)) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EINVAL)); + } + + if (projid == zp->z_projid && zp->z_pflags & ZFS_PROJID) + projid = ZFS_INVALID_PROJID; + else + need_policy = TRUE; + } + + if (XVA_ISSET_REQ(xvap, XAT_PROJINHERIT) && + (xoap->xoa_projinherit != + ((zp->z_pflags & ZFS_PROJINHERIT) != 0)) && + (!dmu_objset_projectquota_enabled(os) || + (!S_ISREG(zp->z_mode) && !S_ISDIR(zp->z_mode)))) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EOPNOTSUPP)); + } + } + + attrzp = NULL; + aclp = NULL; + + if (zfs_is_readonly(zfsvfs)) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EROFS)); + } + + /* + * First validate permissions + */ + + if (mask & ATTR_SIZE) { + /* + * XXX - Note, we are not providing any open + * mode flags here (like FNDELAY), so we may + * block if there are locks present... this + * should be addressed in openat(). + */ + /* XXX - would it be OK to generate a log record here? */ + err = zfs_freesp(zp, vap->va_size, 0, 0, FALSE); + if (err) { + ZFS_EXIT(zfsvfs); + return (err); + } + } + + if (mask & (ATTR_ATIME|ATTR_MTIME) || + ((mask & ATTR_XVATTR) && (XVA_ISSET_REQ(xvap, XAT_HIDDEN) || + XVA_ISSET_REQ(xvap, XAT_READONLY) || + XVA_ISSET_REQ(xvap, XAT_ARCHIVE) || + XVA_ISSET_REQ(xvap, XAT_OFFLINE) || + XVA_ISSET_REQ(xvap, XAT_SPARSE) || + XVA_ISSET_REQ(xvap, XAT_CREATETIME) || + XVA_ISSET_REQ(xvap, XAT_SYSTEM)))) { + need_policy = zfs_zaccess(zp, ACE_WRITE_ATTRIBUTES, 0, + skipaclchk, cr); + } + + if (mask & (ATTR_UID|ATTR_GID)) { + int idmask = (mask & (ATTR_UID|ATTR_GID)); + int take_owner; + int take_group; + + /* + * NOTE: even if a new mode is being set, + * we may clear S_ISUID/S_ISGID bits. + */ + + if (!(mask & ATTR_MODE)) + vap->va_mode = zp->z_mode; + + /* + * Take ownership or chgrp to group we are a member of + */ + + take_owner = (mask & ATTR_UID) && (vap->va_uid == crgetuid(cr)); + take_group = (mask & ATTR_GID) && + zfs_groupmember(zfsvfs, vap->va_gid, cr); + + /* + * If both ATTR_UID and ATTR_GID are set then take_owner and + * take_group must both be set in order to allow taking + * ownership. + * + * Otherwise, send the check through secpolicy_vnode_setattr() + * + */ + + if (((idmask == (ATTR_UID|ATTR_GID)) && + take_owner && take_group) || + ((idmask == ATTR_UID) && take_owner) || + ((idmask == ATTR_GID) && take_group)) { + if (zfs_zaccess(zp, ACE_WRITE_OWNER, 0, + skipaclchk, cr) == 0) { + /* + * Remove setuid/setgid for non-privileged users + */ + secpolicy_setid_clear(vap, cr); + trim_mask = (mask & (ATTR_UID|ATTR_GID)); + } else { + need_policy = TRUE; + } + } else { + need_policy = TRUE; + } + } + + oldva.va_mode = zp->z_mode; + zfs_fuid_map_ids(zp, cr, &oldva.va_uid, &oldva.va_gid); + if (mask & ATTR_XVATTR) { + /* + * Update xvattr mask to include only those attributes + * that are actually changing. + * + * the bits will be restored prior to actually setting + * the attributes so the caller thinks they were set. + */ + if (XVA_ISSET_REQ(xvap, XAT_APPENDONLY)) { + if (xoap->xoa_appendonly != + ((zp->z_pflags & ZFS_APPENDONLY) != 0)) { + need_policy = TRUE; + } else { + XVA_CLR_REQ(xvap, XAT_APPENDONLY); + XVA_SET_REQ(&tmpxvattr, XAT_APPENDONLY); + } + } + + if (XVA_ISSET_REQ(xvap, XAT_PROJINHERIT)) { + if (xoap->xoa_projinherit != + ((zp->z_pflags & ZFS_PROJINHERIT) != 0)) { + need_policy = TRUE; + } else { + XVA_CLR_REQ(xvap, XAT_PROJINHERIT); + XVA_SET_REQ(&tmpxvattr, XAT_PROJINHERIT); + } + } + + if (XVA_ISSET_REQ(xvap, XAT_NOUNLINK)) { + if (xoap->xoa_nounlink != + ((zp->z_pflags & ZFS_NOUNLINK) != 0)) { + need_policy = TRUE; + } else { + XVA_CLR_REQ(xvap, XAT_NOUNLINK); + XVA_SET_REQ(&tmpxvattr, XAT_NOUNLINK); + } + } + + if (XVA_ISSET_REQ(xvap, XAT_IMMUTABLE)) { + if (xoap->xoa_immutable != + ((zp->z_pflags & ZFS_IMMUTABLE) != 0)) { + need_policy = TRUE; + } else { + XVA_CLR_REQ(xvap, XAT_IMMUTABLE); + XVA_SET_REQ(&tmpxvattr, XAT_IMMUTABLE); + } + } + + if (XVA_ISSET_REQ(xvap, XAT_NODUMP)) { + if (xoap->xoa_nodump != + ((zp->z_pflags & ZFS_NODUMP) != 0)) { + need_policy = TRUE; + } else { + XVA_CLR_REQ(xvap, XAT_NODUMP); + XVA_SET_REQ(&tmpxvattr, XAT_NODUMP); + } + } + + if (XVA_ISSET_REQ(xvap, XAT_AV_MODIFIED)) { + if (xoap->xoa_av_modified != + ((zp->z_pflags & ZFS_AV_MODIFIED) != 0)) { + need_policy = TRUE; + } else { + XVA_CLR_REQ(xvap, XAT_AV_MODIFIED); + XVA_SET_REQ(&tmpxvattr, XAT_AV_MODIFIED); + } + } + + if (XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED)) { + if ((vnode_vtype(vp) != VREG && + xoap->xoa_av_quarantined) || + xoap->xoa_av_quarantined != + ((zp->z_pflags & ZFS_AV_QUARANTINED) != 0)) { + need_policy = TRUE; + } else { + XVA_CLR_REQ(xvap, XAT_AV_QUARANTINED); + XVA_SET_REQ(&tmpxvattr, XAT_AV_QUARANTINED); + } + } + + if (XVA_ISSET_REQ(xvap, XAT_REPARSE)) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EPERM)); + } + + if (need_policy == FALSE && + (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP) || + XVA_ISSET_REQ(xvap, XAT_OPAQUE))) { + need_policy = TRUE; + } + } + + if (mask & ATTR_MODE) { + if (zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr) == 0) { + err = secpolicy_setid_setsticky_clear(vp, vap, + &oldva, cr); + if (err) { + ZFS_EXIT(zfsvfs); + return (err); + } + trim_mask |= ATTR_MODE; + } else { + need_policy = TRUE; + } + } + + if (need_policy) { + /* + * If trim_mask is set then take ownership + * has been granted or write_acl is present and user + * has the ability to modify mode. In that case remove + * UID|GID and or MODE from mask so that + * secpolicy_vnode_setattr() doesn't revoke it. + */ + + if (trim_mask) { + saved_mask = vap->va_mask; + vap->va_mask &= ~trim_mask; + if (trim_mask & ATTR_MODE) { + /* + * Save the mode, as secpolicy_vnode_setattr() + * will overwrite it with ova.va_mode. + */ + saved_mode = vap->va_mode; + } + } + err = secpolicy_vnode_setattr(cr, vp, vap, &oldva, flags, + (int (*)(void *, int, cred_t *))zfs_zaccess_unix, zp); + if (err) { + ZFS_EXIT(zfsvfs); + return (err); + } + + if (trim_mask) { + vap->va_mask |= saved_mask; + if (trim_mask & ATTR_MODE) { + /* + * Recover the mode after + * secpolicy_vnode_setattr(). + */ + vap->va_mode = saved_mode; + } + } + } + + /* + * secpolicy_vnode_setattr, or take ownership may have + * changed va_mask + */ + mask = vap->va_mask; + + if ((mask & (ATTR_UID | ATTR_GID)) || projid != ZFS_INVALID_PROJID) { + err = sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zfsvfs), + &xattr_obj, sizeof (xattr_obj)); + + if (err == 0 && xattr_obj) { + err = zfs_zget(zp->z_zfsvfs, xattr_obj, &attrzp); + if (err) + goto out2; + } + if (mask & ATTR_UID) { + new_uid = zfs_fuid_create(zfsvfs, + (uint64_t)vap->va_uid, cr, ZFS_OWNER, &fuidp); + if (new_uid != zp->z_uid && + zfs_id_overquota(zfsvfs, DMU_USERUSED_OBJECT, + new_uid)) { + if (attrzp) + zrele(attrzp); + err = SET_ERROR(EDQUOT); + goto out2; + } + } + + if (mask & ATTR_GID) { + new_gid = zfs_fuid_create(zfsvfs, (uint64_t)vap->va_gid, + cr, ZFS_GROUP, &fuidp); + if (new_gid != zp->z_gid && + zfs_id_overquota(zfsvfs, DMU_GROUPUSED_OBJECT, + new_gid)) { + if (attrzp) + zrele(attrzp); + err = SET_ERROR(EDQUOT); + goto out2; + } + } + + if (projid != ZFS_INVALID_PROJID && + zfs_id_overquota(zfsvfs, DMU_PROJECTUSED_OBJECT, projid)) { + if (attrzp) + zrele(attrzp); + err = SET_ERROR(EDQUOT); + goto out2; + } + } + tx = dmu_tx_create(os); + + if (mask & ATTR_MODE) { + uint64_t pmode = zp->z_mode; + uint64_t acl_obj; + new_mode = (pmode & S_IFMT) | (vap->va_mode & ~S_IFMT); + + if (zp->z_zfsvfs->z_acl_mode == ZFS_ACL_RESTRICTED && + !(zp->z_pflags & ZFS_ACL_TRIVIAL)) { + err = SET_ERROR(EPERM); + goto out; + } + + if ((err = zfs_acl_chmod_setattr(zp, &aclp, new_mode))) + goto out; + + if (!zp->z_is_sa && ((acl_obj = zfs_external_acl(zp)) != 0)) { + /* + * Are we upgrading ACL from old V0 format + * to V1 format? + */ + if (zfsvfs->z_version >= ZPL_VERSION_FUID && + zfs_znode_acl_version(zp) == + ZFS_ACL_VERSION_INITIAL) { + dmu_tx_hold_free(tx, acl_obj, 0, + DMU_OBJECT_END); + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, + 0, aclp->z_acl_bytes); + } else { + dmu_tx_hold_write(tx, acl_obj, 0, + aclp->z_acl_bytes); + } + } else if (!zp->z_is_sa && aclp->z_acl_bytes > ZFS_ACE_SPACE) { + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, + 0, aclp->z_acl_bytes); + } + dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE); + } else { + if (((mask & ATTR_XVATTR) && + XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) || + (projid != ZFS_INVALID_PROJID && + !(zp->z_pflags & ZFS_PROJID))) + dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE); + else + dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); + } + + if (attrzp) { + dmu_tx_hold_sa(tx, attrzp->z_sa_hdl, B_FALSE); + } + + fuid_dirtied = zfsvfs->z_fuid_dirty; + if (fuid_dirtied) + zfs_fuid_txhold(zfsvfs, tx); + + zfs_sa_upgrade_txholds(tx, zp); + + err = dmu_tx_assign(tx, TXG_WAIT); + if (err) + goto out; + + count = 0; + /* + * Set each attribute requested. + * We group settings according to the locks they need to acquire. + * + * Note: you cannot set ctime directly, although it will be + * updated as a side-effect of calling this function. + */ + + if (projid != ZFS_INVALID_PROJID && !(zp->z_pflags & ZFS_PROJID)) { + /* + * For the existed object that is upgraded from old system, + * its on-disk layout has no slot for the project ID attribute. + * But quota accounting logic needs to access related slots by + * offset directly. So we need to adjust old objects' layout + * to make the project ID to some unified and fixed offset. + */ + if (attrzp) + err = sa_add_projid(attrzp->z_sa_hdl, tx, projid); + if (err == 0) + err = sa_add_projid(zp->z_sa_hdl, tx, projid); + + if (unlikely(err == EEXIST)) + err = 0; + else if (err != 0) + goto out; + else + projid = ZFS_INVALID_PROJID; + } + + if (mask & (ATTR_UID|ATTR_GID|ATTR_MODE)) + mutex_enter(&zp->z_acl_lock); + + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL, + &zp->z_pflags, sizeof (zp->z_pflags)); + + if (attrzp) { + if (mask & (ATTR_UID|ATTR_GID|ATTR_MODE)) + mutex_enter(&attrzp->z_acl_lock); + SA_ADD_BULK_ATTR(xattr_bulk, xattr_count, + SA_ZPL_FLAGS(zfsvfs), NULL, &attrzp->z_pflags, + sizeof (attrzp->z_pflags)); + if (projid != ZFS_INVALID_PROJID) { + attrzp->z_projid = projid; + SA_ADD_BULK_ATTR(xattr_bulk, xattr_count, + SA_ZPL_PROJID(zfsvfs), NULL, &attrzp->z_projid, + sizeof (attrzp->z_projid)); + } + } + + if (mask & (ATTR_UID|ATTR_GID)) { + + if (mask & ATTR_UID) { + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_UID(zfsvfs), NULL, + &new_uid, sizeof (new_uid)); + zp->z_uid = new_uid; + if (attrzp) { + SA_ADD_BULK_ATTR(xattr_bulk, xattr_count, + SA_ZPL_UID(zfsvfs), NULL, &new_uid, + sizeof (new_uid)); + attrzp->z_uid = new_uid; + } + } + + if (mask & ATTR_GID) { + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_GID(zfsvfs), + NULL, &new_gid, sizeof (new_gid)); + zp->z_gid = new_gid; + if (attrzp) { + SA_ADD_BULK_ATTR(xattr_bulk, xattr_count, + SA_ZPL_GID(zfsvfs), NULL, &new_gid, + sizeof (new_gid)); + attrzp->z_gid = new_gid; + } + } + if (!(mask & ATTR_MODE)) { + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MODE(zfsvfs), + NULL, &new_mode, sizeof (new_mode)); + new_mode = zp->z_mode; + } + err = zfs_acl_chown_setattr(zp); + ASSERT(err == 0); + if (attrzp) { + err = zfs_acl_chown_setattr(attrzp); + ASSERT(err == 0); + } + } + + if (mask & ATTR_MODE) { + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MODE(zfsvfs), NULL, + &new_mode, sizeof (new_mode)); + zp->z_mode = new_mode; + ASSERT3U((uintptr_t)aclp, !=, 0); + err = zfs_aclset_common(zp, aclp, cr, tx); + ASSERT0(err); + if (zp->z_acl_cached) + zfs_acl_free(zp->z_acl_cached); + zp->z_acl_cached = aclp; + aclp = NULL; + } + + + if (mask & ATTR_ATIME) { + ZFS_TIME_ENCODE(&vap->va_atime, zp->z_atime); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_ATIME(zfsvfs), NULL, + &zp->z_atime, sizeof (zp->z_atime)); + } + + if (mask & ATTR_MTIME) { + ZFS_TIME_ENCODE(&vap->va_mtime, mtime); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL, + mtime, sizeof (mtime)); + } + + if (mask & ATTR_CRTIME) { + ZFS_TIME_ENCODE(&vap->va_crtime, crtime); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CRTIME(zfsvfs), NULL, + crtime, sizeof (crtime)); + } + + if (projid != ZFS_INVALID_PROJID) { + zp->z_projid = projid; + SA_ADD_BULK_ATTR(bulk, count, + SA_ZPL_PROJID(zfsvfs), NULL, &zp->z_projid, + sizeof (zp->z_projid)); + } + + /* XXX - shouldn't this be done *before* the ATIME/MTIME checks? */ + if (mask & ATTR_SIZE && !(mask & ATTR_MTIME)) { + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), + NULL, mtime, sizeof (mtime)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, + &ctime, sizeof (ctime)); + zfs_tstamp_update_setup(zp, CONTENT_MODIFIED, mtime, ctime); + } else if (mask != 0) { + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, + &ctime, sizeof (ctime)); + zfs_tstamp_update_setup(zp, STATE_CHANGED, mtime, ctime); + if (attrzp) { + SA_ADD_BULK_ATTR(xattr_bulk, xattr_count, + SA_ZPL_CTIME(zfsvfs), NULL, + &ctime, sizeof (ctime)); + zfs_tstamp_update_setup(attrzp, STATE_CHANGED, + mtime, ctime); + } + } + + /* + * Do this after setting timestamps to prevent timestamp + * update from toggling bit + */ + + if (xoap && (mask & ATTR_XVATTR)) { + + if (XVA_ISSET_REQ(xvap, XAT_CREATETIME)) + xoap->xoa_createtime = vap->va_create_time; + /* + * restore trimmed off masks + * so that return masks can be set for caller. + */ + + if (XVA_ISSET_REQ(&tmpxvattr, XAT_APPENDONLY)) { + XVA_SET_REQ(xvap, XAT_APPENDONLY); + } + if (XVA_ISSET_REQ(&tmpxvattr, XAT_NOUNLINK)) { + XVA_SET_REQ(xvap, XAT_NOUNLINK); + } + if (XVA_ISSET_REQ(&tmpxvattr, XAT_IMMUTABLE)) { + XVA_SET_REQ(xvap, XAT_IMMUTABLE); + } + if (XVA_ISSET_REQ(&tmpxvattr, XAT_NODUMP)) { + XVA_SET_REQ(xvap, XAT_NODUMP); + } + if (XVA_ISSET_REQ(&tmpxvattr, XAT_AV_MODIFIED)) { + XVA_SET_REQ(xvap, XAT_AV_MODIFIED); + } + if (XVA_ISSET_REQ(&tmpxvattr, XAT_AV_QUARANTINED)) { + XVA_SET_REQ(xvap, XAT_AV_QUARANTINED); + } + if (XVA_ISSET_REQ(&tmpxvattr, XAT_PROJINHERIT)) { + XVA_SET_REQ(xvap, XAT_PROJINHERIT); + } + + if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) + ASSERT(vnode_isreg(vp)); + + zfs_xvattr_set(zp, xvap, tx); + } + + if (fuid_dirtied) + zfs_fuid_sync(zfsvfs, tx); + + if (mask != 0) + zfs_log_setattr(zilog, tx, TX_SETATTR, zp, vap, mask, fuidp); + + if (mask & (ATTR_UID|ATTR_GID|ATTR_MODE)) + mutex_exit(&zp->z_acl_lock); + + if (attrzp) { + if (mask & (ATTR_UID|ATTR_GID|ATTR_MODE)) + mutex_exit(&attrzp->z_acl_lock); + } +out: + if (err == 0 && attrzp) { + err2 = sa_bulk_update(attrzp->z_sa_hdl, xattr_bulk, + xattr_count, tx); + ASSERT(err2 == 0); + } + + if (attrzp) + zrele(attrzp); + + if (aclp) + zfs_acl_free(aclp); + + if (fuidp) { + zfs_fuid_info_free(fuidp); + fuidp = NULL; + } + + if (err) { + dmu_tx_abort(tx); + } else { + err2 = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx); + dmu_tx_commit(tx); + } + +out2: + if (os->os_sync == ZFS_SYNC_ALWAYS) + zil_commit(zilog, 0); + + ZFS_EXIT(zfsvfs); + return (err); +} + +typedef struct zfs_zlock { + krwlock_t *zl_rwlock; /* lock we acquired */ + znode_t *zl_znode; /* znode we held */ + struct zfs_zlock *zl_next; /* next in list */ +} zfs_zlock_t; + +/* + * Drop locks and release vnodes that were held by zfs_rename_lock(). + */ +static void +zfs_rename_unlock(zfs_zlock_t **zlpp) +{ + zfs_zlock_t *zl; + + while ((zl = *zlpp) != NULL) { + if (zl->zl_znode != NULL) + zfs_zrele_async(zl->zl_znode); + rw_exit(zl->zl_rwlock); + *zlpp = zl->zl_next; + kmem_free(zl, sizeof (*zl)); + } +} + +/* + * Search back through the directory tree, using the ".." entries. + * Lock each directory in the chain to prevent concurrent renames. + * Fail any attempt to move a directory into one of its own descendants. + * XXX - z_parent_lock can overlap with map or grow locks + */ +static int +zfs_rename_lock(znode_t *szp, znode_t *tdzp, znode_t *sdzp, zfs_zlock_t **zlpp) +{ + zfs_zlock_t *zl; + znode_t *zp = tdzp; + uint64_t rootid = ZTOZSB(zp)->z_root; + uint64_t oidp = zp->z_id; + krwlock_t *rwlp = &szp->z_parent_lock; + krw_t rw = RW_WRITER; + + /* + * First pass write-locks szp and compares to zp->z_id. + * Later passes read-lock zp and compare to zp->z_parent. + */ + do { + if (!rw_tryenter(rwlp, rw)) { + /* + * Another thread is renaming in this path. + * Note that if we are a WRITER, we don't have any + * parent_locks held yet. + */ + if (rw == RW_READER && zp->z_id > szp->z_id) { + /* + * Drop our locks and restart + */ + zfs_rename_unlock(&zl); + *zlpp = NULL; + zp = tdzp; + oidp = zp->z_id; + rwlp = &szp->z_parent_lock; + rw = RW_WRITER; + continue; + } else { + /* + * Wait for other thread to drop its locks + */ + rw_enter(rwlp, rw); + } + } + + zl = kmem_alloc(sizeof (*zl), KM_SLEEP); + zl->zl_rwlock = rwlp; + zl->zl_znode = NULL; + zl->zl_next = *zlpp; + *zlpp = zl; + + if (oidp == szp->z_id) /* We're a descendant of szp */ + return (SET_ERROR(EINVAL)); + + if (oidp == rootid) /* We've hit the top */ + return (0); + + if (rw == RW_READER) { /* i.e. not the first pass */ + int error = zfs_zget(ZTOZSB(zp), oidp, &zp); + if (error) + return (error); + zl->zl_znode = zp; + } + (void) sa_lookup(zp->z_sa_hdl, SA_ZPL_PARENT(ZTOZSB(zp)), + &oidp, sizeof (oidp)); + rwlp = &zp->z_parent_lock; + rw = RW_READER; + + } while (zp->z_id != sdzp->z_id); + + return (0); +} + +/* + * Move an entry from the provided source directory to the target + * directory. Change the entry name as indicated. + * + * IN: sdzp - Source directory containing the "old entry". + * snm - Old entry name. + * tdzp - Target directory to contain the "new entry". + * tnm - New entry name. + * cr - credentials of caller. + * flags - case flags + * + * RETURN: 0 on success, error code on failure. + * + * Timestamps: + * sdzp,tdzp - ctime|mtime updated + */ +/*ARGSUSED*/ +int +zfs_rename(znode_t *sdzp, char *snm, znode_t *tdzp, char *tnm, + cred_t *cr, int flags) +{ + znode_t *szp, *tzp; + zfsvfs_t *zfsvfs = ZTOZSB(sdzp); + zilog_t *zilog; + uint64_t addtime[2]; + zfs_dirlock_t *sdl, *tdl; + dmu_tx_t *tx; + zfs_zlock_t *zl; + int cmp, serr, terr; + int error = 0; + int zflg = 0; + boolean_t waited = B_FALSE; + + if (snm == NULL || tnm == NULL) + return (SET_ERROR(EINVAL)); + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(sdzp); + zilog = zfsvfs->z_log; + + ZFS_VERIFY_ZP(tdzp); + + /* + * We check i_sb because snapshots and the ctldir must have different + * super blocks. + */ + // Can't we use zp->z_zfsvfs in place of zp->vp->v_vfs ? + if (VTOM(ZTOV(tdzp)) != VTOM(ZTOV(sdzp)) || + zfsctl_is_node(ZTOV(tdzp))) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EXDEV)); + } + + if (zfsvfs->z_utf8 && u8_validate(tnm, + strlen(tnm), NULL, U8_VALIDATE_ENTIRE, &error) < 0) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EILSEQ)); + } + + if (flags & FIGNORECASE) + zflg |= ZCILOOK; + +top: + szp = NULL; + tzp = NULL; + zl = NULL; + + /* + * This is to prevent the creation of links into attribute space + * by renaming a linked file into/outof an attribute directory. + * See the comment in zfs_link() for why this is considered bad. + */ + if ((tdzp->z_pflags & ZFS_XATTR) != (sdzp->z_pflags & ZFS_XATTR)) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EINVAL)); + } + + /* + * Lock source and target directory entries. To prevent deadlock, + * a lock ordering must be defined. We lock the directory with + * the smallest object id first, or if it's a tie, the one with + * the lexically first name. + */ + if (sdzp->z_id < tdzp->z_id) { + cmp = -1; + } else if (sdzp->z_id > tdzp->z_id) { + cmp = 1; + } else { + /* + * First compare the two name arguments without + * considering any case folding. + */ + int nofold = (zfsvfs->z_norm & ~U8_TEXTPREP_TOUPPER); + + cmp = u8_strcmp(snm, tnm, 0, nofold, U8_UNICODE_LATEST, &error); + ASSERT(error == 0 || !zfsvfs->z_utf8); + if (cmp == 0) { + /* + * POSIX: "If the old argument and the new argument + * both refer to links to the same existing file, + * the rename() function shall return successfully + * and perform no other action." + */ + ZFS_EXIT(zfsvfs); + return (0); + } + /* + * If the file system is case-folding, then we may + * have some more checking to do. A case-folding file + * system is either supporting mixed case sensitivity + * access or is completely case-insensitive. Note + * that the file system is always case preserving. + * + * In mixed sensitivity mode case sensitive behavior + * is the default. FIGNORECASE must be used to + * explicitly request case insensitive behavior. + * + * If the source and target names provided differ only + * by case (e.g., a request to rename 'tim' to 'Tim'), + * we will treat this as a special case in the + * case-insensitive mode: as long as the source name + * is an exact match, we will allow this to proceed as + * a name-change request. + */ + if ((zfsvfs->z_case == ZFS_CASE_INSENSITIVE || + (zfsvfs->z_case == ZFS_CASE_MIXED && + flags & FIGNORECASE)) && + u8_strcmp(snm, tnm, 0, zfsvfs->z_norm, U8_UNICODE_LATEST, + &error) == 0) { + /* + * case preserving rename request, require exact + * name matches + */ + zflg |= ZCIEXACT; + zflg &= ~ZCILOOK; + } + } + + /* + * If the source and destination directories are the same, we should + * grab the z_name_lock of that directory only once. + */ + if (sdzp == tdzp) { + zflg |= ZHAVELOCK; + rw_enter(&sdzp->z_name_lock, RW_READER); + } + + if (cmp < 0) { + serr = zfs_dirent_lock(&sdl, sdzp, snm, &szp, + ZEXISTS | zflg, NULL, NULL); + terr = zfs_dirent_lock(&tdl, + tdzp, tnm, &tzp, ZRENAMING | zflg, NULL, NULL); + } else { + terr = zfs_dirent_lock(&tdl, + tdzp, tnm, &tzp, zflg, NULL, NULL); + serr = zfs_dirent_lock(&sdl, + sdzp, snm, &szp, ZEXISTS | ZRENAMING | zflg, + NULL, NULL); + } + + if (serr) { + /* + * Source entry invalid or not there. + */ + if (!terr) { + zfs_dirent_unlock(tdl); + if (tzp) + zrele(tzp); + } + + if (sdzp == tdzp) + rw_exit(&sdzp->z_name_lock); + + if (strcmp(snm, ".") == 0 || strcmp(snm, "..") == 0) + serr = EINVAL; + ZFS_EXIT(zfsvfs); + return (serr); + } + if (terr) { + zfs_dirent_unlock(sdl); + zrele(szp); + + if (sdzp == tdzp) + rw_exit(&sdzp->z_name_lock); + + if (strcmp(tnm, "..") == 0) + terr = EINVAL; + ZFS_EXIT(zfsvfs); + return (terr); + } + + /* + * If we are using project inheritance, means if the directory has + * ZFS_PROJINHERIT set, then its descendant directories will inherit + * not only the project ID, but also the ZFS_PROJINHERIT flag. Under + * such case, we only allow renames into our tree when the project + * IDs are the same. + */ + if (tdzp->z_pflags & ZFS_PROJINHERIT && + tdzp->z_projid != szp->z_projid) { + error = SET_ERROR(EXDEV); + goto out; + } + + /* + * Must have write access at the source to remove the old entry + * and write access at the target to create the new entry. + * Note that if target and source are the same, this can be + * done in a single check. + */ + + if ((error = zfs_zaccess_rename(sdzp, szp, tdzp, tzp, cr))) + goto out; + + if (S_ISDIR(szp->z_mode)) { + /* + * Check to make sure rename is valid. + * Can't do a move like this: /usr/a/b to /usr/a/b/c/d + */ + if ((error = zfs_rename_lock(szp, tdzp, sdzp, &zl))) + goto out; + } + + /* + * Does target exist? + */ + if (tzp) { + /* + * Source and target must be the same type. + */ + if (S_ISDIR(szp->z_mode)) { + if (!S_ISDIR(tzp->z_mode)) { + error = SET_ERROR(ENOTDIR); + goto out; + } + } else { + if (S_ISDIR(tzp->z_mode)) { + error = SET_ERROR(EISDIR); + goto out; + } + } + /* + * POSIX dictates that when the source and target + * entries refer to the same file object, rename + * must do nothing and exit without error. + */ + if (szp->z_id == tzp->z_id) { + error = 0; + goto out; + } + +#if defined(MAC_OS_X_VERSION_10_12) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) + /* If renamex(VFS_RENAME_EXCL) is used, error out */ + if (flags & VFS_RENAME_EXCL) { + error = EEXIST; + goto out; + } +#endif + + } + + tx = dmu_tx_create(zfsvfs->z_os); +#ifdef __APPLE__ + /* ADDTIME might grow SA */ + dmu_tx_hold_sa(tx, szp->z_sa_hdl, B_TRUE); +#else + dmu_tx_hold_sa(tx, szp->z_sa_hdl, B_FALSE); +#endif + dmu_tx_hold_sa(tx, sdzp->z_sa_hdl, B_FALSE); + dmu_tx_hold_zap(tx, sdzp->z_id, FALSE, snm); + dmu_tx_hold_zap(tx, tdzp->z_id, TRUE, tnm); + if (sdzp != tdzp) { + dmu_tx_hold_sa(tx, tdzp->z_sa_hdl, B_FALSE); + zfs_sa_upgrade_txholds(tx, tdzp); + } + if (tzp) { + dmu_tx_hold_sa(tx, tzp->z_sa_hdl, B_FALSE); + zfs_sa_upgrade_txholds(tx, tzp); + } + + zfs_sa_upgrade_txholds(tx, szp); + dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); + error = dmu_tx_assign(tx, (waited ? TXG_NOTHROTTLE : 0) | TXG_NOWAIT); + if (error) { + if (zl != NULL) + zfs_rename_unlock(&zl); + zfs_dirent_unlock(sdl); + zfs_dirent_unlock(tdl); + + if (sdzp == tdzp) + rw_exit(&sdzp->z_name_lock); + + if (error == ERESTART) { + waited = B_TRUE; + dmu_tx_wait(tx); + dmu_tx_abort(tx); + zrele(szp); + if (tzp) + zrele(tzp); + goto top; + } + dmu_tx_abort(tx); + zrele(szp); + if (tzp) + zrele(tzp); + ZFS_EXIT(zfsvfs); + return (error); + } + + if (tzp) /* Attempt to remove the existing target */ + error = zfs_link_destroy(tdl, tzp, tx, zflg, NULL); + + if (error == 0) { + error = zfs_link_create(tdl, szp, tx, ZRENAMING); + if (error == 0) { + szp->z_pflags |= ZFS_AV_MODIFIED; + if (tdzp->z_pflags & ZFS_PROJINHERIT) + szp->z_pflags |= ZFS_PROJINHERIT; + + error = sa_update(szp->z_sa_hdl, SA_ZPL_FLAGS(zfsvfs), + (void *)&szp->z_pflags, sizeof (uint64_t), tx); + ASSERT0(error); + +#ifdef __APPLE__ + /* + * If we moved an entry into a different directory + * (sdzp != tdzp) then we also need to update ADDEDTIME + * (ADDTIME) property for FinderInfo. We are already + * inside error == 0 conditional + */ + if ((sdzp != tdzp) && + zfsvfs->z_use_sa == B_TRUE) { + timestruc_t now; + gethrestime(&now); + ZFS_TIME_ENCODE(&now, addtime); + error = sa_update(szp->z_sa_hdl, + SA_ZPL_ADDTIME(zfsvfs), (void *)&addtime, + sizeof (addtime), tx); + } +#endif + + error = zfs_link_destroy(sdl, szp, tx, ZRENAMING, NULL); + if (error == 0) { + zfs_log_rename(zilog, tx, TX_RENAME | + (flags & FIGNORECASE ? TX_CI : 0), sdzp, + sdl->dl_name, tdzp, tdl->dl_name, szp); + + /* + * Update cached name - for vget, and access + * without calling vnop_lookup first - it is + * easier to clear it out and let getattr + * look it up if needed. + */ + if (tzp) { + mutex_enter(&tzp->z_lock); + tzp->z_name_cache[0] = 0; + mutex_exit(&tzp->z_lock); + } + if (szp) { + mutex_enter(&szp->z_lock); + szp->z_name_cache[0] = 0; + mutex_exit(&szp->z_lock); + } + + } else { + /* + * At this point, we have successfully created + * the target name, but have failed to remove + * the source name. Since the create was done + * with the ZRENAMING flag, there are + * complications; for one, the link count is + * wrong. The easiest way to deal with this + * is to remove the newly created target, and + * return the original error. This must + * succeed; fortunately, it is very unlikely to + * fail, since we just created it. + */ + VERIFY3U(zfs_link_destroy(tdl, szp, tx, + ZRENAMING, NULL), ==, 0); + } + } else { + /* + * If we had removed the existing target, subsequent + * call to zfs_link_create() to add back the same entry + * but, the new dnode (szp) should not fail. + */ + ASSERT(tzp == NULL); + } + } + + dmu_tx_commit(tx); +out: + if (zl != NULL) + zfs_rename_unlock(&zl); + + zfs_dirent_unlock(sdl); + zfs_dirent_unlock(tdl); + + if (sdzp == tdzp) + rw_exit(&sdzp->z_name_lock); + + zrele(szp); + if (tzp) { + zrele(tzp); + } + + if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) + zil_commit(zilog, 0); + + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * Insert the indicated symbolic reference entry into the directory. + * + * IN: dzp - Directory to contain new symbolic link. + * name - Name of directory entry in dip. + * vap - Attributes of new entry. + * link - Name for new symlink entry. + * cr - credentials of caller. + * flags - case flags + * + * OUT: zpp - Znode for new symbolic link. + * + * RETURN: 0 on success, error code on failure. + * + * Timestamps: + * dip - ctime|mtime updated + */ +/*ARGSUSED*/ +int +zfs_symlink(znode_t *dzp, char *name, vattr_t *vap, char *link, + znode_t **zpp, cred_t *cr, int flags) +{ + znode_t *zp; + zfs_dirlock_t *dl; + dmu_tx_t *tx; + zfsvfs_t *zfsvfs = ZTOZSB(dzp); + zilog_t *zilog; + uint64_t len = strlen(link); + int error; + int zflg = ZNEW; + zfs_acl_ids_t acl_ids; + boolean_t fuid_dirtied; + uint64_t txtype = TX_SYMLINK; + boolean_t waited = B_FALSE; + + ASSERT(S_ISLNK(vap->va_mode)); + + if (name == NULL) + return (SET_ERROR(EINVAL)); + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(dzp); + zilog = zfsvfs->z_log; + + if (zfsvfs->z_utf8 && u8_validate(name, strlen(name), + NULL, U8_VALIDATE_ENTIRE, &error) < 0) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EILSEQ)); + } + if (flags & FIGNORECASE) + zflg |= ZCILOOK; + + if (len > MAXPATHLEN) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(ENAMETOOLONG)); + } + + if ((error = zfs_acl_ids_create(dzp, 0, + vap, cr, NULL, &acl_ids)) != 0) { + ZFS_EXIT(zfsvfs); + return (error); + } +top: + *zpp = NULL; + + /* + * Attempt to lock directory; fail if entry already exists. + */ + error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg, NULL, NULL); + if (error) { + zfs_acl_ids_free(&acl_ids); + ZFS_EXIT(zfsvfs); + return (error); + } + + if ((error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr))) { + zfs_acl_ids_free(&acl_ids); + zfs_dirent_unlock(dl); + ZFS_EXIT(zfsvfs); + return (error); + } + + if (zfs_acl_ids_overquota(zfsvfs, &acl_ids, ZFS_DEFAULT_PROJID)) { + zfs_acl_ids_free(&acl_ids); + zfs_dirent_unlock(dl); + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EDQUOT)); + } + tx = dmu_tx_create(zfsvfs->z_os); + fuid_dirtied = zfsvfs->z_fuid_dirty; + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, MAX(1, len)); + dmu_tx_hold_zap(tx, dzp->z_id, TRUE, name); + dmu_tx_hold_sa_create(tx, acl_ids.z_aclp->z_acl_bytes + + ZFS_SA_BASE_ATTR_SIZE + len); + dmu_tx_hold_sa(tx, dzp->z_sa_hdl, B_FALSE); + if (!zfsvfs->z_use_sa && acl_ids.z_aclp->z_acl_bytes > ZFS_ACE_SPACE) { + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, + acl_ids.z_aclp->z_acl_bytes); + } + if (fuid_dirtied) + zfs_fuid_txhold(zfsvfs, tx); + error = dmu_tx_assign(tx, (waited ? TXG_NOTHROTTLE : 0) | TXG_NOWAIT); + if (error) { + zfs_dirent_unlock(dl); + if (error == ERESTART) { + waited = B_TRUE; + dmu_tx_wait(tx); + dmu_tx_abort(tx); + goto top; + } + zfs_acl_ids_free(&acl_ids); + dmu_tx_abort(tx); + ZFS_EXIT(zfsvfs); + return (error); + } + + /* + * Create a new object for the symlink. + * for version 4 ZPL datsets the symlink will be an SA attribute + */ + zfs_mknode(dzp, vap, tx, cr, 0, &zp, &acl_ids); + + if (fuid_dirtied) + zfs_fuid_sync(zfsvfs, tx); + + mutex_enter(&zp->z_lock); + if (zp->z_is_sa) + error = sa_update(zp->z_sa_hdl, SA_ZPL_SYMLINK(zfsvfs), + link, len, tx); + else + zfs_sa_symlink(zp, link, len, tx); + mutex_exit(&zp->z_lock); + + zp->z_size = len; + (void) sa_update(zp->z_sa_hdl, SA_ZPL_SIZE(zfsvfs), + &zp->z_size, sizeof (zp->z_size), tx); + /* + * Insert the new object into the directory. + */ + error = zfs_link_create(dl, zp, tx, ZNEW); + if (error != 0) { + zfs_znode_delete(zp, tx); + } else { + if (flags & FIGNORECASE) + txtype |= TX_CI; + zfs_log_symlink(zilog, tx, txtype, dzp, zp, name, link); + } + + zfs_acl_ids_free(&acl_ids); + + dmu_tx_commit(tx); + + zfs_dirent_unlock(dl); + + /* + * OS X - attach the vnode _after_ committing the transaction + */ + zfs_znode_getvnode(zp, zfsvfs); + + if (error == 0) { + *zpp = zp; + + if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) + zil_commit(zilog, 0); + } else { + zrele(zp); + } + + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * Return, in the buffer contained in the provided uio structure, + * the symbolic path referred to by ip. + * + * IN: ip - inode of symbolic link + * uio - structure to contain the link path. + * cr - credentials of caller. + * + * RETURN: 0 if success + * error code if failure + * + * Timestamps: + * ip - atime updated + */ +/* ARGSUSED */ +int +zfs_readlink(struct vnode *vp, zfs_uio_t *uio, cred_t *cr) +{ + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = ITOZSB(vp); + int error; + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + + mutex_enter(&zp->z_lock); + if (zp->z_is_sa) + error = sa_lookup_uio(zp->z_sa_hdl, + SA_ZPL_SYMLINK(zfsvfs), uio); + else + error = zfs_sa_readlink(zp, uio); + mutex_exit(&zp->z_lock); + + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * Insert a new entry into directory tdzp referencing szp. + * + * IN: tdzp - Directory to contain new entry. + * szp - znode of new entry. + * name - name of new entry. + * cr - credentials of caller. + * flags - case flags. + * + * RETURN: 0 if success + * error code if failure + * + * Timestamps: + * tdzp - ctime|mtime updated + * szp - ctime updated + */ +/* ARGSUSED */ +int +zfs_link(znode_t *tdzp, znode_t *szp, char *name, cred_t *cr, + int flags) +{ + struct vnode *svp = ZTOV(szp); + znode_t *tzp; + zfsvfs_t *zfsvfs = ZTOZSB(tdzp); + zilog_t *zilog; + zfs_dirlock_t *dl; + dmu_tx_t *tx; + int error; + int zf = ZNEW; + uint64_t parent; + uid_t owner; + boolean_t waited = B_FALSE; + boolean_t is_tmpfile = 0; + uint64_t txg; + + ASSERT(S_ISDIR(tdzp->z_mode)); + + if (name == NULL) + return (SET_ERROR(EINVAL)); + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(tdzp); + zilog = zfsvfs->z_log; + +#ifdef __APPLE__ + if (VTOM(svp) != VTOM(ZTOV(tdzp))) { + ZFS_EXIT(zfsvfs); + return (EXDEV); + } +#endif + + /* + * POSIX dictates that we return EPERM here. + * Better choices include ENOTSUP or EISDIR. + */ + if (vnode_isdir(svp)) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EPERM)); + } + + ZFS_VERIFY_ZP(szp); + + /* + * If we are using project inheritance, means if the directory has + * ZFS_PROJINHERIT set, then its descendant directories will inherit + * not only the project ID, but also the ZFS_PROJINHERIT flag. Under + * such case, we only allow hard link creation in our tree when the + * project IDs are the same. + */ + if (tdzp->z_pflags & ZFS_PROJINHERIT && + tdzp->z_projid != szp->z_projid) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EXDEV)); + } + + /* Prevent links to .zfs/shares files */ + + if ((error = sa_lookup(szp->z_sa_hdl, SA_ZPL_PARENT(zfsvfs), + &parent, sizeof (uint64_t))) != 0) { + ZFS_EXIT(zfsvfs); + return (error); + } + if (parent == zfsvfs->z_shares_dir) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EPERM)); + } + + if (zfsvfs->z_utf8 && u8_validate(name, + strlen(name), NULL, U8_VALIDATE_ENTIRE, &error) < 0) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EILSEQ)); + } + if (flags & FIGNORECASE) + zf |= ZCILOOK; + + /* + * We do not support links between attributes and non-attributes + * because of the potential security risk of creating links + * into "normal" file space in order to circumvent restrictions + * imposed in attribute space. + */ + if ((szp->z_pflags & ZFS_XATTR) != (tdzp->z_pflags & ZFS_XATTR)) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EINVAL)); + } + + owner = zfs_fuid_map_id(zfsvfs, KUID_TO_SUID(szp->z_uid), + cr, ZFS_OWNER); + if (owner != crgetuid(cr) && secpolicy_basic_link(cr) != 0) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EPERM)); + } + + if ((error = zfs_zaccess(tdzp, ACE_ADD_FILE, 0, B_FALSE, cr))) { + ZFS_EXIT(zfsvfs); + return (error); + } + +top: + /* + * Attempt to lock directory; fail if entry already exists. + */ + error = zfs_dirent_lock(&dl, tdzp, name, &tzp, zf, NULL, NULL); + if (error) { + ZFS_EXIT(zfsvfs); + return (error); + } + + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_sa(tx, szp->z_sa_hdl, B_FALSE); + dmu_tx_hold_zap(tx, tdzp->z_id, TRUE, name); + if (is_tmpfile) + dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); + + zfs_sa_upgrade_txholds(tx, szp); + zfs_sa_upgrade_txholds(tx, tdzp); + error = dmu_tx_assign(tx, (waited ? TXG_NOTHROTTLE : 0) | TXG_NOWAIT); + if (error) { + zfs_dirent_unlock(dl); + if (error == ERESTART) { + waited = B_TRUE; + dmu_tx_wait(tx); + dmu_tx_abort(tx); + goto top; + } + dmu_tx_abort(tx); + ZFS_EXIT(zfsvfs); + return (error); + } + + error = zfs_link_create(dl, szp, tx, 0); + + if (error == 0) { + uint64_t txtype = TX_LINK; + if (flags & FIGNORECASE) + txtype |= TX_CI; + zfs_log_link(zilog, tx, txtype, tdzp, szp, name); + } else if (is_tmpfile) { + /* restore z_unlinked since when linking failed */ + szp->z_unlinked = B_TRUE; + } + txg = dmu_tx_get_txg(tx); + dmu_tx_commit(tx); + + zfs_dirent_unlock(dl); + + if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) + zil_commit(zilog, 0); + + ZFS_EXIT(zfsvfs); + return (error); +} + +/*ARGSUSED*/ +void +zfs_inactive(struct vnode *vp) +{ + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = ITOZSB(vp); + int error; + + rw_enter(&zfsvfs->z_teardown_inactive_lock, RW_READER); + if (zp->z_sa_hdl == NULL) { + /* + * The fs has been unmounted, or we did a + * suspend/resume and this file no longer exists. + */ + rw_exit(&zfsvfs->z_teardown_inactive_lock); + vnode_recycle(vp); + return; + } + + if (zp->z_unlinked) { + /* + * Fast path to recycle a vnode of a removed file. + */ + rw_exit(&zfsvfs->z_teardown_inactive_lock); + vnode_recycle(vp); + return; + } + + if (zp->z_atime_dirty && zp->z_unlinked == 0) { + dmu_tx_t *tx = dmu_tx_create(zfsvfs->z_os); + + dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); + zfs_sa_upgrade_txholds(tx, zp); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + dmu_tx_abort(tx); + } else { + (void) sa_update(zp->z_sa_hdl, SA_ZPL_ATIME(zfsvfs), + (void *)&zp->z_atime, sizeof (zp->z_atime), tx); + zp->z_atime_dirty = 0; + dmu_tx_commit(tx); + } + } + rw_exit(&zfsvfs->z_teardown_inactive_lock); +} + +/* + * Free or allocate space in a file. Currently, this function only + * supports the `F_FREESP' command. However, this command is somewhat + * misnamed, as its functionality includes the ability to allocate as + * well as free space. + * + * IN: zp - znode of file to free data in. + * cmd - action to take (only F_FREESP supported). + * bfp - section of file to free/alloc. + * flag - current file open mode flags. + * offset - current file offset. + * cr - credentials of caller. + * + * RETURN: 0 on success, error code on failure. + * + * Timestamps: + * zp - ctime|mtime updated + */ +/* ARGSUSED */ +int +zfs_space(znode_t *zp, int cmd, flock64_t *bfp, int flag, + offset_t offset, cred_t *cr) +{ + zfsvfs_t *zfsvfs = ZTOZSB(zp); + uint64_t off, len; + int error; + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + + if (cmd != F_FREESP) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EINVAL)); + } + + /* + * Callers might not be able to detect properly that we are read-only, + * so check it explicitly here. + */ + if (zfs_is_readonly(zfsvfs)) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EROFS)); + } + + if (bfp->l_len < 0) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EINVAL)); + } + + /* + * Permissions aren't checked on Solaris because on this OS + * zfs_space() can only be called with an opened file handle. + * On Linux we can get here through truncate_range() which + * operates directly on inodes, so we need to check access rights. + */ + if ((error = zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr))) { + ZFS_EXIT(zfsvfs); + return (error); + } + + off = bfp->l_start; + len = bfp->l_len; /* 0 means from off to end of file */ + + error = zfs_freesp(zp, off, len, flag, TRUE); + + ZFS_EXIT(zfsvfs); + return (error); +} diff --git a/module/os/macos/zfs/zfs_vnops_osx.c b/module/os/macos/zfs/zfs_vnops_osx.c new file mode 100644 index 000000000000..2916cac082e0 --- /dev/null +++ b/module/os/macos/zfs/zfs_vnops_osx.c @@ -0,0 +1,5397 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2013 Will Andrews + * Copyright (c) 2013, 2020 Jorgen Lundman + */ + +/* + * XXX GENERAL COMPATIBILITY ISSUES + * + * 'name' is a common argument, but in OS X (and FreeBSD), we need to pass + * the componentname pointer, so other things can use them. We should + * change the 'name' argument to be an opaque name pointer, and define + * OS-dependent macros that yield the desired results when needed. + * + * On OS X, VFS performs access checks before calling anything, so + * zfs_zaccess_* calls are not used. Not true on FreeBSD, though. Perhaps + * those calls should be conditionally #if 0'd? + * + * On OS X, VFS & I/O objects are often opaque, e.g. uio_t and struct vnode + * require using functions to access elements of an object. Should convert + * the Solaris code to use macros on other platforms. + * + * OS X and FreeBSD appear to use similar zfs-vfs interfaces; see Apple's + * comment in zfs_remove() about the fact that VFS holds the last ref while + * in Solaris it's the ZFS code that does. On FreeBSD, the code Apple + * refers to here results in a panic if the branch is actually taken. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + + + +#ifdef _KERNEL +#include +#include +unsigned int zfs_vnop_ignore_negatives = 0; +unsigned int zfs_vnop_ignore_positives = 0; +unsigned int zfs_vnop_create_negatives = 1; +#endif + +#define DECLARE_CRED(ap) \ + cred_t *cr = (cred_t *)vfs_context_ucred((ap)->a_context) +#define DECLARE_CONTEXT(ap) \ + caller_context_t *ct = (caller_context_t *)(ap)->a_context +#define DECLARE_CRED_AND_CONTEXT(ap) \ + DECLARE_CRED(ap); \ + DECLARE_CONTEXT(ap) + +/* Empty FinderInfo struct */ +static u_int32_t emptyfinfo[8] = {0}; + +/* + * zfs vfs operations. + */ +static struct vfsops zfs_vfsops_template = { + zfs_vfs_mount, + zfs_vfs_start, + zfs_vfs_unmount, + zfs_vfs_root, + zfs_vfs_quotactl, + zfs_vfs_getattr, + zfs_vfs_sync, + zfs_vfs_vget, + zfs_vfs_fhtovp, + zfs_vfs_vptofh, + zfs_vfs_init, + zfs_vfs_sysctl, + zfs_vfs_setattr, +#if defined(MAC_OS_X_VERSION_10_12) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) + NULL, /* vfs_ioctl */ + NULL, /* vfs_vget_snapdir */ + NULL +#else + {NULL} +#endif +}; + +#define ZFS_VNOP_TBL_CNT 6 + +static struct vnodeopv_desc *zfs_vnodeop_opv_desc_list[ZFS_VNOP_TBL_CNT] = +{ + &zfs_dvnodeop_opv_desc, + &zfs_fvnodeop_opv_desc, + &zfs_symvnodeop_opv_desc, + &zfs_xdvnodeop_opv_desc, + &zfs_fifonodeop_opv_desc, + &zfs_ctldir_opv_desc, +}; + +static vfstable_t zfs_vfsconf; + +int +zfs_vnop_removexattr_int(zfsvfs_t *zfsvfs, znode_t *zp, const char *name, + cred_t *cr); + +int +zfs_vfs_init(__unused struct vfsconf *vfsp) +{ + return (0); +} + +int +zfs_vfs_start(__unused struct mount *mp, __unused int flags, + __unused vfs_context_t context) +{ + return (0); +} + +int +zfs_vfs_quotactl(__unused struct mount *mp, __unused int cmds, + __unused uid_t uid, __unused caddr_t datap, __unused vfs_context_t context) +{ + dprintf("%s ENOTSUP\n", __func__); + return (ENOTSUP); +} + +static kmutex_t zfs_findernotify_lock; +static kcondvar_t zfs_findernotify_thread_cv; +static boolean_t zfs_findernotify_thread_exit; + +#define VNODE_EVENT_ATTRIB 0x00000008 + +static int +zfs_findernotify_callback(mount_t mp, __unused void *arg) +{ + vfs_context_t kernelctx = spl_vfs_context_kernel(); + struct vnode *rootvp, *vp; + znode_t *zp = NULL; + + /* + * Since potentially other filesystems could be using "our" + * fssubtype, and we don't always announce as "zfs" due to + * hfs-mimic requirements, we have to make extra care here to + * make sure this "mp" really is ZFS. + */ + zfsvfs_t *zfsvfs; + + zfsvfs = vfs_fsprivate(mp); + + /* + * The first entry in struct zfsvfs is the vfs ptr, so they + * should be equal if it is ZFS + */ + if (!zfsvfs || + (mp != zfsvfs->z_vfs)) + return (VFS_RETURNED); + + // Filesystem ZFS? Confirm the location of root_id in zfsvfs + if (zfsvfs->z_root != INO_ROOT) + return (VFS_RETURNED); + + + /* Guard against unmount */ + ZFS_ENTER_ERROR(zfsvfs, VFS_RETURNED); + + /* Check if space usage has changed enough to bother updating */ + uint64_t refdbytes, availbytes, usedobjs, availobjs; + uint64_t delta; + dmu_objset_space(zfsvfs->z_os, + &refdbytes, &availbytes, &usedobjs, &availobjs); + if (availbytes >= zfsvfs->z_findernotify_space) { + delta = availbytes - zfsvfs->z_findernotify_space; + } else { + delta = zfsvfs->z_findernotify_space - availbytes; + } + +#define ZFS_FINDERNOTIFY_THRESHOLD (1ULL<<20) + + /* Under the limit ? */ + if (delta <= ZFS_FINDERNOTIFY_THRESHOLD) goto out; + + /* Over threashold, so we will notify finder, remember value */ + zfsvfs->z_findernotify_space = availbytes; + + /* If old value is zero (first run), don't bother */ + if (availbytes == delta) + goto out; + + dprintf("ZFS: findernotify %p space delta %llu\n", mp, delta); + + // Grab the root zp + if (zfs_zget(zfsvfs, zfsvfs->z_root, &zp) == 0) { + + rootvp = ZTOV(zp); + + struct componentname cn; + char *tmpname = ".fseventsd"; + + bzero(&cn, sizeof (cn)); + cn.cn_nameiop = LOOKUP; + cn.cn_flags = ISLASTCN; + // cn.cn_context = kernelctx; + cn.cn_pnbuf = tmpname; + cn.cn_pnlen = sizeof (tmpname); + cn.cn_nameptr = cn.cn_pnbuf; + cn.cn_namelen = strlen(tmpname); + + // Attempt to lookup .Trashes + if (!VOP_LOOKUP(rootvp, &vp, &cn, kernelctx)) { + + // Send the event to wake up Finder + struct vnode_attr vattr; + // Also calls VATTR_INIT + spl_vfs_get_notify_attributes(&vattr); + // Fill in vap + vnode_getattr(vp, &vattr, kernelctx); + // Send event + spl_vnode_notify(vp, VNODE_EVENT_ATTRIB, + &vattr); + + // Cleanup vp + vnode_put(vp); + + } // VNOP_LOOKUP + + // Cleanup rootvp + vnode_put(rootvp); + + } // VFS_ROOT + +out: + ZFS_EXIT(zfsvfs); + + return (VFS_RETURNED); +} + + +static void +zfs_findernotify_thread(void *notused) +{ + callb_cpr_t cpr; + + dprintf("ZFS: findernotify thread start\n"); + CALLB_CPR_INIT(&cpr, &zfs_findernotify_lock, callb_generic_cpr, FTAG); + + mutex_enter(&zfs_findernotify_lock); + while (!zfs_findernotify_thread_exit) { + + /* Sleep 32 seconds */ + CALLB_CPR_SAFE_BEGIN(&cpr); + (void) cv_timedwait(&zfs_findernotify_thread_cv, + &zfs_findernotify_lock, ddi_get_lbolt() + (hz<<5)); + CALLB_CPR_SAFE_END(&cpr, &zfs_findernotify_lock); + + if (!zfs_findernotify_thread_exit) + vfs_iterate(LK_NOWAIT, zfs_findernotify_callback, NULL); + + } + + zfs_findernotify_thread_exit = FALSE; + cv_broadcast(&zfs_findernotify_thread_cv); + CALLB_CPR_EXIT(&cpr); /* drops arc_reclaim_lock */ + dprintf("ZFS: findernotify thread exit\n"); + thread_exit(); +} + +void +zfs_start_notify_thread(void) +{ + mutex_init(&zfs_findernotify_lock, NULL, MUTEX_DEFAULT, NULL); + cv_init(&zfs_findernotify_thread_cv, NULL, CV_DEFAULT, NULL); + zfs_findernotify_thread_exit = FALSE; + (void) thread_create(NULL, 0, zfs_findernotify_thread, NULL, 0, &p0, + TS_RUN, minclsyspri); +} + + +void +zfs_stop_notify_thread(void) +{ + mutex_enter(&zfs_findernotify_lock); + zfs_findernotify_thread_exit = TRUE; + /* + * The reclaim thread will set arc_reclaim_thread_exit back to + * FALSE when it is finished exiting; we're waiting for that. + */ + while (zfs_findernotify_thread_exit) { + cv_signal(&zfs_findernotify_thread_cv); + cv_wait(&zfs_findernotify_thread_cv, &zfs_findernotify_lock); + } + mutex_exit(&zfs_findernotify_lock); + mutex_destroy(&zfs_findernotify_lock); + cv_destroy(&zfs_findernotify_thread_cv); +} + +int +zfs_vfs_sysctl(int *name, __unused uint_t namelen, user_addr_t oldp, + size_t *oldlenp, user_addr_t newp, size_t newlen, + __unused vfs_context_t context) +{ +#if 0 + int error; + switch (name[0]) { + case ZFS_SYSCTL_FOOTPRINT: { + zfs_footprint_stats_t *footprint; + size_t copyinsize; + size_t copyoutsize; + int max_caches; + int act_caches; + + if (newp) { + return (EINVAL); + } + if (!oldp) { + *oldlenp = sizeof (zfs_footprint_stats_t); + return (0); + } + copyinsize = *oldlenp; + if (copyinsize < sizeof (zfs_footprint_stats_t)) { + *oldlenp = sizeof (zfs_footprint_stats_t); + return (ENOMEM); + } + footprint = kmem_zalloc(copyinsize, KM_SLEEP); + + max_caches = copyinsize - sizeof (zfs_footprint_stats_t); + max_caches += sizeof (kmem_cache_stats_t); + max_caches /= sizeof (kmem_cache_stats_t); + + footprint->version = ZFS_FOOTPRINT_VERSION; + + footprint->memory_stats.current = zfs_footprint.current; + footprint->memory_stats.target = zfs_footprint.target; + footprint->memory_stats.highest = zfs_footprint.highest; + footprint->memory_stats.maximum = zfs_footprint.maximum; + + arc_get_stats(&footprint->arc_stats); + + kmem_cache_stats(&footprint->cache_stats[0], max_caches, + &act_caches); + footprint->caches_count = act_caches; + footprint->thread_count = zfs_threads; + + copyoutsize = sizeof (zfs_footprint_stats_t) + + ((act_caches - 1) * sizeof (kmem_cache_stats_t)); + + error = ddi_copyout(footprint, oldp, copyoutsize, 0); + + kmem_free(footprint, copyinsize); + + return (error); + } + + case ZFS_SYSCTL_CONFIG_DEBUGMSG: + error = sysctl_int(oldp, oldlenp, newp, newlen, + &zfs_msg_buf_enabled); + return (error); + + case ZFS_SYSCTL_CONFIG_zdprintf: +#ifdef ZFS_DEBUG + error = sysctl_int(oldp, oldlenp, newp, newlen, + &zfs_zdprintf_enabled); +#else + error = ENOTSUP; +#endif + return (error); + } +#endif + return (ENOTSUP); +} + +/* + * All these functions could be declared as 'static' but to assist with + * dtrace debugging, we do not. + */ + +int +zfs_vnop_open(struct vnop_open_args *ap) +#if 0 + struct vnop_open_args { + struct vnode *a_vp; + int a_mode; + vfs_context_t a_context; + }; +#endif +{ + DECLARE_CRED(ap); + int err = 0; + + err = zfs_open(ap->a_vp, ap->a_mode, 0, cr); + + if (err) dprintf("zfs_open() failed %d\n", err); + return (err); +} + +int +zfs_vnop_close(struct vnop_close_args *ap) +#if 0 + struct vnop_close_args { + struct vnode *a_vp; + int a_fflag; + vfs_context_t a_context; + }; +#endif +{ +// int count = 1; +// int offset = 0; +// DECLARE_CRED_AND_CONTEXT(ap); + DECLARE_CRED(ap); + + return (zfs_close(ap->a_vp, ap->a_fflag, cr)); +} + +int +zfs_vnop_ioctl(struct vnop_ioctl_args *ap) +#if 0 + struct vnop_ioctl_args { + struct vnode *a_vp; + ulong_t a_command; + caddr_t a_data; + int a_fflag; + kauth_cred_t a_cred; + struct proc *a_p; + }; +#endif +{ + /* OS X has no use for zfs_ioctl(). */ + znode_t *zp = VTOZ(ap->a_vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + int error = 0; + DECLARE_CRED_AND_CONTEXT(ap); + + dprintf("vnop_ioctl %08lx: VTYPE %d\n", ap->a_command, + vnode_vtype(ZTOV(zp))); + + ZFS_ENTER(zfsvfs); + if (IFTOVT((mode_t)zp->z_mode) == VFIFO) { + dprintf("ZFS: FIFO ioctl %02lx ('%lu' + %lu)\n", + ap->a_command, (ap->a_command&0xff00)>>8, + ap->a_command&0xff); + error = fifo_ioctl(ap); + error = 0; + ZFS_EXIT(zfsvfs); + goto out; + } + + if ((IFTOVT((mode_t)zp->z_mode) == VBLK) || + (IFTOVT((mode_t)zp->z_mode) == VCHR)) { + dprintf("ZFS: spec ioctl %02lx ('%lu' + %lu)\n", + ap->a_command, (ap->a_command&0xff00)>>8, + ap->a_command&0xff); + error = spec_ioctl(ap); + ZFS_EXIT(zfsvfs); + goto out; + } + ZFS_EXIT(zfsvfs); + + switch (ap->a_command) { + + /* ioctl supported by ZFS and POSIX */ + + case F_FULLFSYNC: + dprintf("%s F_FULLFSYNC\n", __func__); +#ifdef F_BARRIERFSYNC + case F_BARRIERFSYNC: + dprintf("%s F_BARRIERFSYNC\n", __func__); +#endif + error = zfs_fsync(VTOZ(ap->a_vp), /* flag */0, cr); + break; + + case F_CHKCLEAN: + dprintf("%s F_CHKCLEAN\n", __func__); + /* + * normally calls http://fxr.watson.org/fxr/source/bsd/ + * vfs/vfs_cluster.c?v=xnu-2050.18.24#L5839 + */ + /* XXX Why don't we? */ + off_t fsize = zp->z_size; + error = is_file_clean(ap->a_vp, fsize); + break; + + case F_RDADVISE: + dprintf("%s F_RDADVISE\n", __func__); + uint64_t file_size; + struct radvisory *ra; + int len; + + ra = (struct radvisory *)(ap->a_data); + + file_size = zp->z_size; + len = ra->ra_count; + + /* XXX Check request size */ + if (ra->ra_offset > file_size) { + dprintf("invalid request offset\n"); + error = EFBIG; + break; + } + + if ((ra->ra_offset + len) > file_size) { + len = file_size - ra->ra_offset; + dprintf("%s truncating F_RDADVISE from" + " %08x -> %08x\n", __func__, + ra->ra_count, len); + } + + /* + * Rather than advisory_read (which calls + * cluster_io->VNOP_BLOCKMAP), prefetch + * the level 0 metadata and level 1 data + * at the requested offset + length. + */ + // error = advisory_read(ap->a_vp, file_size, + // ra->ra_offset, len); + dmu_prefetch(zfsvfs->z_os, zp->z_id, + 0, 0, 0, ZIO_PRIORITY_SYNC_READ); + dmu_prefetch(zfsvfs->z_os, zp->z_id, + 1, ra->ra_offset, len, + ZIO_PRIORITY_SYNC_READ); +#if 0 + { + const char *name = vnode_getname(ap->a_vp); + printf("%s F_RDADVISE: prefetch issued for " + "[%s](0x%016llx) (0x%016llx 0x%08x)\n", __func__, + (name ? name : ""), zp->z_id, + ra->ra_offset, len); + if (name) vnode_putname(name); + } +#endif + + break; + + case SPOTLIGHT_GET_MOUNT_TIME: + case SPOTLIGHT_IOC_GET_MOUNT_TIME: + case SPOTLIGHT_FSCTL_GET_MOUNT_TIME: + dprintf("%s SPOTLIGHT_GET_MOUNT_TIME\n", __func__); + *(uint32_t *)ap->a_data = zfsvfs->z_mount_time; + break; + case SPOTLIGHT_GET_UNMOUNT_TIME: + dprintf("%s SPOTLIGHT_GET_UNMOUNT_TIME\n", __func__); + *(uint32_t *)ap->a_data = zfsvfs->z_last_unmount_time; + break; + case SPOTLIGHT_FSCTL_GET_LAST_MTIME: + case SPOTLIGHT_IOC_GET_LAST_MTIME: + dprintf("%s SPOTLIGHT_FSCTL_GET_LAST_MTIME\n", + __func__); + *(uint32_t *)ap->a_data = zfsvfs->z_last_unmount_time; + break; + + case HFS_SET_ALWAYS_ZEROFILL: + dprintf("%s HFS_SET_ALWAYS_ZEROFILL\n", __func__); + /* Required by Spotlight search */ + break; + case HFS_EXT_BULKACCESS_FSCTL: + dprintf("%s HFS_EXT_BULKACCESS_FSCTL\n", __func__); + /* Required by Spotlight search */ + break; + +#ifdef FSIOC_FIOSEEKHOLE + case FSIOC_FIOSEEKHOLE: + case FSCTL_FIOSEEKHOLE: + { + loff_t off; + off = *(loff_t *)ap->a_data; + /* offset parameter is in/out */ + error = zfs_holey(zp, SEEK_HOLE, &off); + if (error) + break; + *(loff_t *)ap->a_data = off; + break; + } +#endif + +#ifdef FSIOC_FIOSEEKDATA + case FSIOC_FIOSEEKDATA: + case FSCTL_FIOSEEKDATA: + { + loff_t off; + off = *(loff_t *)ap->a_data; + /* offset parameter is in/out */ + error = zfs_holey(zp, SEEK_DATA, &off); + if (error) + break; + *(loff_t *)ap->a_data = off; + break; + } +#endif + + + /* ioctl required to simulate HFS mimic behavior */ + case 0x80005802: + dprintf("%s 0x80005802 unknown\n", __func__); + /* unknown - from subsystem read, 'X', 2 */ + break; + + case HFS_GETPATH: + case HFSIOC_GETPATH: + dprintf("%s HFS_GETPATH\n", __func__); + { + struct vfsstatfs *vfsp; + struct vnode *file_vp; + ino64_t cnid; + int outlen; + char *bufptr; + int flags = 0; + + /* Caller must be owner of file system. */ + vfsp = vfs_statfs(zfsvfs->z_vfs); + if (proc_suser(current_proc()) && + kauth_cred_getuid((kauth_cred_t)cr) != + vfsp->f_owner) { + error = EACCES; + goto out; + } + /* Target vnode must be file system's root. */ + if (!vnode_isvroot(ap->a_vp)) { + error = EINVAL; + goto out; + } + + /* We are passed a string containing inode # */ + bufptr = (char *)ap->a_data; + cnid = strtoul(bufptr, NULL, 10); + if (ap->a_fflag & HFS_GETPATH_VOLUME_RELATIVE) { + flags |= BUILDPATH_VOLUME_RELATIVE; + } + + if ((error = zfs_vfs_vget(zfsvfs->z_vfs, cnid, + &file_vp, (vfs_context_t)ct))) { + goto out; + } + + error = spl_build_path(file_vp, bufptr, + MAXPATHLEN, &outlen, flags, + (vfs_context_t)ct); + + vnode_put(file_vp); + + dprintf("ZFS: HFS_GETPATH done %d : '%s'\n", + error, error ? "" : bufptr); + } + break; + + case HFS_TRANSFER_DOCUMENT_ID: + case HFSIOC_TRANSFER_DOCUMENT_ID: + dprintf("%s HFS_TRANSFER_DOCUMENT_ID\n", __func__); + { + u_int32_t to_fd = *(u_int32_t *)ap->a_data; + file_t *to_fp; + struct vnode *to_vp; + znode_t *to_zp; + + to_fp = getf(to_fd); + if (to_fp == NULL) { + error = EBADF; + goto out; + } + + to_vp = getf_vnode(to_fp); + + if ((error = vnode_getwithref(to_vp))) { + releasef(to_fd); + goto out; + } + + /* Confirm it is inside our mount */ + if (((zfsvfs_t *)vfs_fsprivate( + vnode_mount(to_vp))) != zfsvfs) { + error = EXDEV; + goto transfer_out; + } + + to_zp = VTOZ(to_vp); + + /* Source should have UF_TRACKED */ + if (!(zp->z_pflags & ZFS_TRACKED)) { + dprintf("ZFS: source is not TRACKED\n"); + error = EINVAL; + /* dest should NOT have UF_TRACKED */ + } else if (to_zp->z_pflags & ZFS_TRACKED) { + dprintf("ZFS: dest already TRACKED\n"); + error = EEXIST; + /* should be valid types */ + } else if ( + (IFTOVT((mode_t)zp->z_mode) == VDIR) || + (IFTOVT((mode_t)zp->z_mode) == VREG) || + (IFTOVT((mode_t)zp->z_mode) == VLNK)) { + /* + * Make sure source has a document id + * - although it can't + */ + if (!zp->z_document_id) + zfs_setattr_generate_id(zp, 0, + NULL); + + /* transfer over */ + to_zp->z_document_id = + zp->z_document_id; + zp->z_document_id = 0; + to_zp->z_pflags |= ZFS_TRACKED; + zp->z_pflags &= ~ZFS_TRACKED; + + /* Commit to disk */ + zfs_setattr_set_documentid(to_zp, + B_TRUE); + zfs_setattr_set_documentid(zp, + B_TRUE); /* also update flags */ + dprintf("ZFS: Moved docid %u from " + "id %llu to id %llu\n", + to_zp->z_document_id, zp->z_id, + to_zp->z_id); + } +transfer_out: + vnode_put(to_vp); + releasef(to_fd); + } + break; + + + case F_MAKECOMPRESSED: + dprintf("%s F_MAKECOMPRESSED\n", __func__); + /* + * Not entirely sure what this does, but HFS comments + * include: "Make the file compressed; truncate & + * toggle BSD bits" + * makes compressed copy of allocated blocks + * shortens file to new length + * sets BSD bits to indicate per-file compression + * + * On HFS, locks cnode and compresses its data. ZFS + * inband compression makes this obsolete. + */ + if (vfs_isrdonly(zfsvfs->z_vfs) || + !spa_writeable(dmu_objset_spa(zfsvfs->z_os))) { + error = EROFS; + goto out; + } + + /* Are there any other usecounts/FDs? */ + if (vnode_isinuse(ap->a_vp, 1)) { + error = EBUSY; + goto out; + } + + if (zp->z_pflags & ZFS_IMMUTABLE) { + error = EINVAL; + goto out; + } + + /* Return failure */ + error = EINVAL; + break; + + case HFS_PREV_LINK: + case HFS_NEXT_LINK: + case HFSIOC_PREV_LINK: + case HFSIOC_NEXT_LINK: + dprintf("%s HFS_PREV/NEXT_LINK\n", __func__); + { + /* + * Find sibling linkids with hardlinks. a_data points + * to the "current" linkid, and look up either prev + * or next (a_command) linkid. Return in a_data. + */ + uint32_t linkfileid; + struct vfsstatfs *vfsp; + /* Caller must be owner of file system. */ + vfsp = vfs_statfs(zfsvfs->z_vfs); + if ((kauth_cred_getuid(cr) == 0) && + kauth_cred_getuid(cr) != vfsp->f_owner) { + error = EACCES; + goto out; + } + /* Target vnode must be file system's root. */ + if (!vnode_isvroot(ap->a_vp)) { + error = EINVAL; + goto out; + } + linkfileid = *(uint32_t *)ap->a_data; + if (linkfileid < 16) { /* kHFSFirstUserCatalogNodeID */ + error = EINVAL; + goto out; + } + + /* + * Attempt to find the linkid in the hardlink_link + * AVL tree. If found, call to get prev or next. + */ + hardlinks_t *searchnode, *findnode, *sibling; + avl_index_t loc; + + searchnode = kmem_zalloc(sizeof (hardlinks_t), + KM_SLEEP); + searchnode->hl_linkid = linkfileid; + + rw_enter(&zfsvfs->z_hardlinks_lock, RW_READER); + findnode = avl_find(&zfsvfs->z_hardlinks_linkid, + searchnode, &loc); + kmem_free(searchnode, sizeof (hardlinks_t)); + + if (!findnode) { + rw_exit(&zfsvfs->z_hardlinks_lock); + *(uint32_t *)ap->a_data = 0; + dprintf("ZFS: HFS_NEXT_LINK/HFS_PREV_LINK %u " + "not found\n", linkfileid); + goto out; + } + + if (ap->a_command != HFS_NEXT_LINK) { + + while ((sibling = + AVL_NEXT(&zfsvfs->z_hardlinks_linkid, + findnode)) != NULL) { + if (findnode->hl_fileid == + sibling->hl_fileid) + break; + } + + } else { + + while ((sibling = + AVL_PREV(&zfsvfs->z_hardlinks_linkid, + findnode)) != NULL) { + if (findnode->hl_fileid == + sibling->hl_fileid) + break; + } + + } + rw_exit(&zfsvfs->z_hardlinks_lock); + + dprintf("ZFS: HFS_%s_LINK %u sibling %u\n", + (ap->a_command != HFS_NEXT_LINK) ? "NEXT" : "PREV", + linkfileid, + sibling ? sibling->hl_linkid : 0); + + // Did we get a new node? + if (sibling == NULL) { + *(uint32_t *)ap->a_data = 0; + goto out; + } + + *(uint32_t *)ap->a_data = sibling->hl_linkid; + error = 0; + } + break; + + case HFS_RESIZE_PROGRESS: + case HFSIOC_RESIZE_PROGRESS: + dprintf("%s HFS_RESIZE_PROGRESS\n", __func__); + /* fail as if requested of non-root fs */ + error = EINVAL; + break; + + case HFS_RESIZE_VOLUME: + case HFSIOC_RESIZE_VOLUME: + dprintf("%s HFS_RESIZE_VOLUME\n", __func__); + /* fail as if requested of non-root fs */ + error = EINVAL; + break; + + case HFS_CHANGE_NEXT_ALLOCATION: + case HFSIOC_CHANGE_NEXT_ALLOCATION: + dprintf("%s HFS_CHANGE_NEXT_ALLOCATION\n", __func__); + /* fail as if requested of non-root fs */ + error = EINVAL; + break; + + case HFS_CHANGE_NEXTCNID: + case HFSIOC_CHANGE_NEXTCNID: + dprintf("%s HFS_CHANGE_NEXTCNID\n", __func__); + /* FIXME : fail as though read only */ + error = EROFS; + break; + + case F_FREEZE_FS: + dprintf("%s F_FREEZE_FS\n", __func__); + /* Dont support freeze */ + error = ENOTSUP; + break; + + case F_THAW_FS: + dprintf("%s F_THAW_FS\n", __func__); + /* dont support fail as though insufficient privilege */ + error = EACCES; + break; + + case HFS_BULKACCESS_FSCTL: + case HFSIOC_BULKACCESS: + dprintf("%s HFS_BULKACCESS_FSCTL\n", __func__); + /* Respond as if HFS_STANDARD flag is set */ + error = EINVAL; + break; + + case HFS_FSCTL_GET_VERY_LOW_DISK: + case HFSIOC_GET_VERY_LOW_DISK: + dprintf("%s HFS_FSCTL_GET_VERY_LOW_DISK\n", __func__); + *(uint32_t *)ap->a_data = + zfsvfs->z_freespace_notify_dangerlimit; + break; + + case HFS_FSCTL_SET_VERY_LOW_DISK: + case HFSIOC_SET_VERY_LOW_DISK: + dprintf("%s HFS_FSCTL_SET_VERY_LOW_DISK\n", __func__); + if (*(uint32_t *)ap->a_data >= + zfsvfs->z_freespace_notify_warninglimit) { + error = EINVAL; + } else { + zfsvfs->z_freespace_notify_dangerlimit = + *(uint32_t *)ap->a_data; + } + break; + + case HFS_FSCTL_GET_LOW_DISK: + case HFSIOC_GET_LOW_DISK: + dprintf("%s HFS_FSCTL_GET_LOW_DISK\n", __func__); + *(uint32_t *)ap->a_data = + zfsvfs->z_freespace_notify_warninglimit; + break; + + case HFS_FSCTL_SET_LOW_DISK: + case HFSIOC_SET_LOW_DISK: + dprintf("%s HFS_FSCTL_SET_LOW_DISK\n", __func__); + if (*(uint32_t *)ap->a_data >= + zfsvfs->z_freespace_notify_desiredlevel || + *(uint32_t *)ap->a_data <= + zfsvfs->z_freespace_notify_dangerlimit) { + error = EINVAL; + } else { + zfsvfs->z_freespace_notify_warninglimit = + *(uint32_t *)ap->a_data; + } + break; + + case HFS_FSCTL_GET_DESIRED_DISK: + case HFSIOC_GET_DESIRED_DISK: + dprintf("%s HFS_FSCTL_GET_DESIRED_DISK\n", __func__); + *(uint32_t *)ap->a_data = + zfsvfs->z_freespace_notify_desiredlevel; + break; + + case HFS_FSCTL_SET_DESIRED_DISK: + case HFSIOC_SET_DESIRED_DISK: + dprintf("%s HFS_FSCTL_SET_DESIRED_DISK\n", __func__); + if (*(uint32_t *)ap->a_data <= + zfsvfs->z_freespace_notify_warninglimit) { + error = EINVAL; + } else { + zfsvfs->z_freespace_notify_desiredlevel = + *(uint32_t *)ap->a_data; + } + break; + + case HFS_VOLUME_STATUS: + case HFSIOC_VOLUME_STATUS: + dprintf("%s HFS_VOLUME_STATUS\n", __func__); + /* For now we always reply "all ok" */ + *(uint32_t *)ap->a_data = + zfsvfs->z_notification_conditions; + break; + + case HFS_SET_BOOT_INFO: + dprintf("%s HFS_SET_BOOT_INFO\n", __func__); + /* + * ZFS booting is not supported, mimic selection + * of a non-root HFS volume + */ + *(uint32_t *)ap->a_data = 0; + error = EINVAL; + break; + case HFS_GET_BOOT_INFO: + { + u_int32_t vcbFndrInfo[8]; + dprintf("%s HFS_GET_BOOT_INFO\n", __func__); + /* + * ZFS booting is not supported, mimic selection + * of a non-root HFS volume + */ + memset(vcbFndrInfo, 0, sizeof (vcbFndrInfo)); + struct vfsstatfs *vfsstatfs; + vfsstatfs = vfs_statfs(zfsvfs->z_vfs); + vcbFndrInfo[6] = vfsstatfs->f_fsid.val[0]; + vcbFndrInfo[7] = vfsstatfs->f_fsid.val[1]; + bcopy(vcbFndrInfo, ap->a_data, + sizeof (vcbFndrInfo)); + } + break; + case HFS_MARK_BOOT_CORRUPT: + dprintf("%s HFS_MARK_BOOT_CORRUPT\n", __func__); + /* + * ZFS booting is not supported, mimic selection + * of a non-root HFS volume + */ + *(uint32_t *)ap->a_data = 0; + error = EINVAL; + break; + + case HFS_FSCTL_GET_JOURNAL_INFO: + case HFSIOC_GET_JOURNAL_INFO: + dprintf("%s HFS_FSCTL_GET_JOURNAL_INFO\n", __func__); + /* + * XXX We're setting the mount as 'Journaled' + * so this might conflict + * Respond as though journal is empty/disabled + */ + { + struct hfs_journal_info *jip; + jip = (struct hfs_journal_info *)ap->a_data; + jip->jstart = 0; + jip->jsize = 0; + } + break; + + case HFS_DISABLE_METAZONE: + dprintf("%s HFS_DISABLE_METAZONE\n", __func__); + /* fail as though insufficient privs */ + error = EACCES; + break; + +#ifdef HFS_GET_FSINFO + case HFS_GET_FSINFO: + case HFSIOC_GET_FSINFO: + dprintf("%s HFS_GET_FSINFO\n", __func__); + break; +#endif + +#ifdef HFS_REPIN_HOTFILE_STATE + case HFS_REPIN_HOTFILE_STATE: + case HFSIOC_REPIN_HOTFILE_STATE: + dprintf("%s HFS_REPIN_HOTFILE_STATE\n", __func__); + break; +#endif + +#ifdef HFS_SET_HOTFILE_STATE + case HFS_SET_HOTFILE_STATE: + case HFSIOC_SET_HOTFILE_STATE: + dprintf("%s HFS_SET_HOTFILE_STATE\n", __func__); + break; +#endif + +#ifdef APFSIOC_GET_NEAR_LOW_DISK + case APFSIOC_GET_NEAR_LOW_DISK: + dprintf("%s APFSIOC_GET_NEAR_LOW_DISK\n", __func__); + *(uint32_t *)ap->a_data = + zfsvfs->z_freespace_notify_warninglimit; + break; +#endif + +#ifdef APFSIOC_SET_NEAR_LOW_DISK + case APFSIOC_SET_NEAR_LOW_DISK: + dprintf("%s APFSIOC_SET_NEAR_LOW_DISK\n", __func__); + if (*(uint32_t *)ap->a_data >= + zfsvfs->z_freespace_notify_desiredlevel || + *(uint32_t *)ap->a_data <= + zfsvfs->z_freespace_notify_dangerlimit) { + error = EINVAL; + } else { + zfsvfs->z_freespace_notify_warninglimit = + *(uint32_t *)ap->a_data; + } + break; +#endif + + /* End HFS mimic ioctl */ + + default: + dprintf("%s: Unknown ioctl %02lx ('%lu' + %lu)\n", + __func__, ap->a_command, (ap->a_command&0xff00)>>8, + ap->a_command&0xff); + error = ENOTTY; + } + +out: + if (error) { + dprintf("%s: failing ioctl: %02lx ('%lu' + %lu) returned %d\n", + __func__, ap->a_command, (ap->a_command&0xff00)>>8, + ap->a_command&0xff, error); + } + + return (error); +} + + +int +zfs_vnop_read(struct vnop_read_args *ap) +#if 0 + struct vnop_read_args { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + vfs_context_t a_context; + }; +#endif +{ + int ioflag = zfs_ioflags(ap->a_ioflag); + int error; + /* uint64_t resid; */ +// DECLARE_CRED_AND_CONTEXT(ap); + DECLARE_CRED(ap); + ZFS_UIO_INIT_XNU(uio, ap->a_uio); + + /* resid = uio_resid(ap->a_uio); */ + error = zfs_read(VTOZ(ap->a_vp), uio, ioflag, cr); + + if (error) dprintf("vnop_read %d\n", error); + return (error); +} + +int +zfs_vnop_write(struct vnop_write_args *ap) +#if 0 + struct vnop_write_args { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + vfs_context_t a_context; + }; +#endif +{ + int ioflag = zfs_ioflags(ap->a_ioflag); + int error; + DECLARE_CRED(ap); + ZFS_UIO_INIT_XNU(uio, ap->a_uio); + + // dprintf("zfs_vnop_write(vp %p, offset 0x%llx size 0x%llx\n", + // ap->a_vp, uio_offset(ap->a_uio), uio_resid(ap->a_uio)); + + error = zfs_write(VTOZ(ap->a_vp), uio, ioflag, cr); + + /* + * Mac OS X: pageout requires that the UBC file size be current. + * Possibly, we could update it only if size has changed. + */ + + /* if (tx_bytes != 0) { */ + if (!error) { + ubc_setsize(ap->a_vp, VTOZ(ap->a_vp)->z_size); + } else { + dprintf("%s error %d\n", __func__, error); + } + + return (error); +} + +int +zfs_vnop_access(struct vnop_access_args *ap) +#if 0 + struct vnop_access_args { + struct vnodeop_desc *a_desc; + struct vnode a_vp; + int a_action; + vfs_context_t a_context; + }; +#endif +{ + int error = ENOTSUP; + int action = ap->a_action; + int mode = 0; + DECLARE_CRED(ap); + + /* + * KAUTH_VNODE_READ_EXTATTRIBUTES, as well? + * KAUTH_VNODE_WRITE_EXTATTRIBUTES + */ + if (action & KAUTH_VNODE_READ_DATA) + mode |= VREAD; + if (action & KAUTH_VNODE_WRITE_DATA) + mode |= VWRITE; + if (action & KAUTH_VNODE_EXECUTE) + mode |= VEXEC; + + dprintf("vnop_access: action %04x -> mode %04x\n", action, mode); + error = zfs_access(VTOZ(ap->a_vp), mode, 0, cr); + + if (error) dprintf("%s: error %d\n", __func__, error); + return (error); +} + + +/* + * hard link references? + * Read the comment in zfs_getattr_znode_unlocked for the reason + * for this hackery. Since getattr(VA_NAME) is extremely common + * call in OSX, we opt to always save the name. We need to be careful + * as zfs_dirlook can return ctldir node as well (".zfs"). + * Hardlinks also need to be able to return the correct parentid. + */ +static void zfs_cache_name(struct vnode *vp, struct vnode *dvp, char *filename) +{ + znode_t *zp; + if (!vp || + !filename || + !filename[0] || + zfsctl_is_node(vp) || + !VTOZ(vp)) + return; + + // Only cache files, or we might end up caching "." + if (!vnode_isreg(vp)) + return; + + zp = VTOZ(vp); + + mutex_enter(&zp->z_lock); + + strlcpy(zp->z_name_cache, filename, + MAXPATHLEN); + + // If hardlink, remember the parentid. + if (((zp->z_links > 1) || (zp->z_finder_hardlink)) && + (IFTOVT((mode_t)zp->z_mode) == VREG) && dvp) { + zp->z_finder_parentid = VTOZ(dvp)->z_id; + } + + mutex_exit(&zp->z_lock); +} + + +int +zfs_vnop_lookup(struct vnop_lookup_args *ap) +#if 0 + struct vnop_lookup_args { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + vfs_context_t a_context; + }; +#endif +{ + struct componentname *cnp = ap->a_cnp; + DECLARE_CRED(ap); + int error; + int negative_cache = 0; + znode_t *zp = NULL; + int direntflags = 0; + char filename[MAXNAMELEN]; + + *ap->a_vpp = NULL; /* In case we return an error */ + + /* + * Darwin uses namelen as an optimisation, for example it can be + * set to 5 for the string "alpha/beta" to look up "alpha". In this + * case we need to copy it out to null-terminate. + */ + bcopy(cnp->cn_nameptr, filename, cnp->cn_namelen); + filename[cnp->cn_namelen] = '\0'; + +#if 1 + /* + * cache_lookup() returns 0 for no-entry + * -1 for cache found (a_vpp set) + * ENOENT for negative cache + */ + error = cache_lookup(ap->a_dvp, ap->a_vpp, cnp); + if (error) { + /* We found a cache entry, positive or negative. */ + if (error == -1) { /* Positive entry? */ + if (!zfs_vnop_ignore_positives) { + error = 0; + goto exit; /* Positive cache, return it */ + } + /* Release iocount held by cache_lookup */ + vnode_put(*ap->a_vpp); + } + /* Negatives are only followed if not CREATE, from HFS+. */ + if (cnp->cn_nameiop != CREATE) { + if (!zfs_vnop_ignore_negatives) { + goto exit; /* Negative cache hit */ + } + negative_cache = 1; + } + } +#endif + + dprintf("+vnop_lookup '%s' %s\n", filename, + negative_cache ? "negative_cache":""); + + /* + * 'cnp' passed to us is 'readonly' as XNU does not expect a return + * name, but most likely expects it correct in getattr. + */ + struct componentname cn2; + cn2.cn_nameptr = filename; + cn2.cn_namelen = MAXNAMELEN; + cn2.cn_nameiop = cnp->cn_nameiop; + cn2.cn_flags = cnp->cn_flags; + + error = zfs_lookup(VTOZ(ap->a_dvp), filename, &zp, /* flags */ 0, cr, + &direntflags, &cn2); + /* flags can be LOOKUP_XATTR | FIGNORECASE */ + +#if 1 + /* + * It appears that VFS layer adds negative cache entries for us, so + * we do not need to add them here, or they are duplicated. + */ + if ((error == ENOENT) && zfs_vnop_create_negatives) { + if ((ap->a_cnp->cn_nameiop == CREATE || + ap->a_cnp->cn_nameiop == RENAME) && + (cnp->cn_flags & ISLASTCN)) { + error = EJUSTRETURN; + goto exit; + } + /* Insert name into cache (as non-existent) if appropriate. */ + if ((cnp->cn_flags & MAKEENTRY) && + ap->a_cnp->cn_nameiop != CREATE) { + cache_enter(ap->a_dvp, NULL, ap->a_cnp); + dprintf("Negative-cache made for '%s'\n", + filename); + } + } /* ENOENT */ +#endif + +exit: + + if (error == 0 && (zp != NULL)) { + dprintf("back with zp %p: name '%s'\n", zp, filename); + + *ap->a_vpp = ZTOV(zp); + + zfs_cache_name(*ap->a_vpp, ap->a_dvp, filename); + + } + + dprintf("-vnop_lookup %d : dvp %llu '%s'\n", error, + VTOZ(ap->a_dvp)->z_id, filename); + + return (error); +} + +int +zfs_vnop_create(struct vnop_create_args *ap) +#if 0 + struct vnop_create_args { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vnode_vattr *a_vap; + vfs_context_t a_context; + }; +#endif +{ + struct componentname *cnp = ap->a_cnp; + vattr_t *vap = ap->a_vap; + DECLARE_CRED(ap); + vcexcl_t excl; + int mode = 0; /* FIXME */ + int error; + znode_t *zp = NULL; + + dprintf("vnop_create: '%s'\n", cnp->cn_nameptr); + + /* + * extern int zfs_create(struct vnode *dvp, char *name, vattr_t *vap, + * int excl, int mode, struct vnode **vpp, cred_t *cr); + */ + excl = (vap->va_vaflags & VA_EXCLUSIVE) ? EXCL : NONEXCL; + + error = zfs_create(VTOZ(ap->a_dvp), cnp->cn_nameptr, vap, excl, mode, + &zp, cr, 0, NULL); + if (!error) { + cache_purge_negatives(ap->a_dvp); + *ap->a_vpp = ZTOV(zp); + } else { + dprintf("%s error %d\n", __func__, error); + } + + return (error); +} + + +static int zfs_remove_hardlink(struct vnode *vp, struct vnode *dvp, char *name) +{ + /* + * Because we store hash of hardlinks in an AVLtree, we need to remove + * any entries in it upon deletion. Since it is complicated to know + * if an entry was a hardlink, we simply check if the avltree has the + * name. + */ + hardlinks_t *searchnode, *findnode; + avl_index_t loc; + + if (!vp || !VTOZ(vp)) + return (1); + if (!dvp || !VTOZ(dvp)) + return (1); + znode_t *zp = VTOZ(vp); + znode_t *dzp = VTOZ(dvp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + int ishardlink = 0; + + ishardlink = ((zp->z_links > 1) && + (IFTOVT((mode_t)zp->z_mode) == VREG)) ? 1 : 0; + if (zp->z_finder_hardlink) + ishardlink = 1; + + if (!ishardlink) + return (0); + + dprintf("ZFS: removing hash (%llu,%llu,'%s')\n", + dzp->z_id, zp->z_id, name); + + // Attempt to remove from hardlink avl, if its there + searchnode = kmem_zalloc(sizeof (hardlinks_t), KM_SLEEP); + searchnode->hl_parent = dzp->z_id == zfsvfs->z_root ? 2 : dzp->z_id; + searchnode->hl_fileid = zp->z_id; + strlcpy(searchnode->hl_name, name, PATH_MAX); + + rw_enter(&zfsvfs->z_hardlinks_lock, RW_READER); + findnode = avl_find(&zfsvfs->z_hardlinks, searchnode, &loc); + rw_exit(&zfsvfs->z_hardlinks_lock); + kmem_free(searchnode, sizeof (hardlinks_t)); + + // Found it? remove it + if (findnode) { + rw_enter(&zfsvfs->z_hardlinks_lock, RW_WRITER); + avl_remove(&zfsvfs->z_hardlinks, findnode); + avl_remove(&zfsvfs->z_hardlinks_linkid, findnode); + rw_exit(&zfsvfs->z_hardlinks_lock); + kmem_free(findnode, sizeof (*findnode)); + dprintf("ZFS: removed hash '%s'\n", name); + mutex_enter(&zp->z_lock); + zp->z_name_cache[0] = 0; + zp->z_finder_parentid = 0; + mutex_exit(&zp->z_lock); + return (1); + } + return (0); +} + + +static int zfs_rename_hardlink(struct vnode *vp, struct vnode *tvp, + struct vnode *fdvp, struct vnode *tdvp, + char *from, char *to) +{ + /* + * Because we store hash of hardlinks in an AVLtree, we need to update + * any entries in it upon rename. Since it is complicated to know + * if an entry was a hardlink, we simply check if the avltree has the + * name. + */ + hardlinks_t *searchnode, *findnode, *delnode; + avl_index_t loc; + uint64_t parent_fid, parent_tid; + int ishardlink = 0; + + if (!vp || !VTOZ(vp)) + return (0); + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + + ishardlink = ((zp->z_links > 1) && + (IFTOVT((mode_t)zp->z_mode) == VREG)) ? 1 : 0; + if (zp->z_finder_hardlink) + ishardlink = 1; + + if (!ishardlink) + return (0); + + if (!fdvp || !VTOZ(fdvp)) + return (0); + parent_fid = VTOZ(fdvp)->z_id; + parent_fid = parent_fid == zfsvfs->z_root ? 2 : parent_fid; + + if (!tdvp || !VTOZ(tdvp)) { + parent_tid = parent_fid; + } else { + parent_tid = VTOZ(tdvp)->z_id; + parent_tid = parent_tid == zfsvfs->z_root ? 2 : parent_tid; + } + + dprintf("ZFS: looking to rename hardlinks (%llu,%llu,%s)\n", + parent_fid, zp->z_id, from); + + + // Attempt to remove from hardlink avl, if its there + searchnode = kmem_zalloc(sizeof (hardlinks_t), KM_SLEEP); + searchnode->hl_parent = parent_fid; + searchnode->hl_fileid = zp->z_id; + strlcpy(searchnode->hl_name, from, PATH_MAX); + + rw_enter(&zfsvfs->z_hardlinks_lock, RW_READER); + findnode = avl_find(&zfsvfs->z_hardlinks, searchnode, &loc); + rw_exit(&zfsvfs->z_hardlinks_lock); + + // Found it? update it + if (findnode) { + + rw_enter(&zfsvfs->z_hardlinks_lock, RW_WRITER); + + // Technically, we do not need to re-do the _linkid AVL here. + avl_remove(&zfsvfs->z_hardlinks, findnode); + avl_remove(&zfsvfs->z_hardlinks_linkid, findnode); + + // If we already have a hashid for "to" and the rename + // presumably unlinked it, we need to remove it first. + searchnode->hl_parent = parent_tid; + strlcpy(searchnode->hl_name, to, PATH_MAX); + delnode = avl_find(&zfsvfs->z_hardlinks, searchnode, &loc); + if (delnode) { + dprintf("ZFS: apparently %llu:'%s' exists, deleting\n", + parent_tid, to); + avl_remove(&zfsvfs->z_hardlinks, delnode); + avl_remove(&zfsvfs->z_hardlinks_linkid, delnode); + kmem_free(delnode, sizeof (*delnode)); + } + + dprintf("ZFS: renamed hash %llu (%llu:'%s' to %llu:'%s'): %s\n", + zp->z_id, + parent_fid, from, + parent_tid, to, + delnode ? "deleted":""); + + // Update source node to new hash, and name. + findnode->hl_parent = parent_tid; + strlcpy(findnode->hl_name, to, PATH_MAX); + // zp->z_finder_parentid = parent_tid; + + avl_add(&zfsvfs->z_hardlinks, findnode); + avl_add(&zfsvfs->z_hardlinks_linkid, findnode); + + rw_exit(&zfsvfs->z_hardlinks_lock); + kmem_free(searchnode, sizeof (hardlinks_t)); + + return (1); + } + + kmem_free(searchnode, sizeof (hardlinks_t)); + return (0); +} + + +int +zfs_vnop_remove(struct vnop_remove_args *ap) +#if 0 + struct vnop_remove_args { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + int a_flags; + vfs_context_t a_context; + }; +#endif +{ +// DECLARE_CRED_AND_CONTEXT(ap); + DECLARE_CRED(ap); + int error; + + dprintf("vnop_remove: %p (%s)\n", ap->a_vp, ap->a_cnp->cn_nameptr); + + /* + * extern int zfs_remove ( struct vnode *dvp, char *name, cred_t *cr, + * caller_context_t *ct, int flags); + */ + error = zfs_remove(VTOZ(ap->a_dvp), ap->a_cnp->cn_nameptr, cr, + /* flags */0); + if (!error) { + cache_purge(ap->a_vp); + + zfs_remove_hardlink(ap->a_vp, + ap->a_dvp, + ap->a_cnp->cn_nameptr); + } else { + dprintf("%s error %d\n", __func__, error); + } + + return (error); +} + +int +zfs_vnop_mkdir(struct vnop_mkdir_args *ap) +#if 0 + struct vnop_mkdir_args { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vnode_vattr *a_vap; + vfs_context_t a_context; + }; +#endif +{ +// DECLARE_CRED_AND_CONTEXT(ap); + DECLARE_CRED(ap); + int error; + + dprintf("vnop_mkdir '%s'\n", ap->a_cnp->cn_nameptr); + +#if 0 + /* Let's deny OS X fseventd for now */ + if (ap->a_cnp->cn_nameptr && + strcmp(ap->a_cnp->cn_nameptr, ".fseventsd") == 0) + return (EINVAL); +#endif + +#if 0 + /* spotlight for now */ + if (ap->a_cnp->cn_nameptr && + strcmp(ap->a_cnp->cn_nameptr, ".Spotlight-V100") == 0) + return (EINVAL); +#endif + /* + * extern int zfs_mkdir(struct vnode *dvp, char *dirname, vattr_t *vap, + * struct vnode **vpp, cred_t *cr, caller_context_t *ct, int flags, + * vsecattr_t *vsecp); + */ + znode_t *zp = NULL; + ap->a_vap->va_mode |= S_IFDIR; + error = zfs_mkdir(VTOZ(ap->a_dvp), ap->a_cnp->cn_nameptr, ap->a_vap, + &zp, cr, /* flags */0, /* vsecp */NULL); + if (!error) { + *ap->a_vpp = ZTOV(zp); + cache_purge_negatives(ap->a_dvp); + vnode_update_identity(*ap->a_vpp, ap->a_dvp, + (const char *)ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen, + 0, VNODE_UPDATE_NAME); + + VERIFY3P(zp->z_zfsvfs, ==, + vfs_fsprivate(vnode_mount(*ap->a_vpp))); + + + } else { + dprintf("%s error %d\n", __func__, error); + } + + return (error); +} + +int +zfs_vnop_rmdir(struct vnop_rmdir_args *ap) +#if 0 + struct vnop_rmdir_args { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + vfs_context_t a_context; + }; +#endif +{ +// DECLARE_CRED_AND_CONTEXT(ap); + DECLARE_CRED(ap); + int error; + + dprintf("vnop_rmdir\n"); + + /* + * extern int zfs_rmdir(struct vnode *dvp, char *name, + * struct vnode *cwd, cred_t *cr, caller_context_t *ct, int flags); + */ + error = zfs_rmdir(VTOZ(ap->a_dvp), ap->a_cnp->cn_nameptr, + /* cwd */NULL, cr, /* flags */0); + if (!error) { + cache_purge(ap->a_vp); + } else { + dprintf("%s error %d\n", __func__, error); + } + + return (error); +} + +int +zfs_vnop_readdir(struct vnop_readdir_args *ap) +#if 0 + struct vnop_readdir_args { + struct vnode a_vp; + struct uio *a_uio; + int a_flags; + int *a_eofflag; + int *a_numdirent; + vfs_context_t a_context; + }; +#endif +{ + int error; + DECLARE_CRED(ap); + ZFS_UIO_INIT_XNU(uio, ap->a_uio); + + dprintf("+readdir: %p\n", ap->a_vp); + + /* + * XXX This interface needs vfs_has_feature. + * XXX zfs_readdir() also needs to grow support for passing back the + * number of entries (OS X/FreeBSD) and cookies (FreeBSD). However, + * it should be the responsibility of the OS caller to malloc/free + * space for that. + */ + + /* + * extern int zfs_readdir(struct vnode *vp, uio_t *uio, cred_t *cr, + * int *eofp, int flags, int *a_numdirent); + */ + *ap->a_numdirent = 0; + + error = zfs_readdir(ap->a_vp, uio, cr, ap->a_eofflag, ap->a_flags, + ap->a_numdirent); + + /* .zfs dirs can be completely empty */ + if (*ap->a_numdirent == 0) + *ap->a_numdirent = 2; /* . and .. */ + + if (error) { + dprintf("-readdir %d (nument %d)\n", error, *ap->a_numdirent); + } + return (error); +} + +int +zfs_vnop_fsync(struct vnop_fsync_args *ap) +#if 0 + struct vnop_fsync_args { + struct vnode *a_vp; + int a_waitfor; + vfs_context_t a_context; + }; +#endif +{ + znode_t *zp = VTOZ(ap->a_vp); + zfsvfs_t *zfsvfs; +// DECLARE_CRED_AND_CONTEXT(ap); + DECLARE_CRED(ap); + int err; + + /* + * Check if this znode has already been synced, freed, and recycled + * by znode_pageout_func. + * + * XXX What is this? Substitute for Illumos vn_has_cached_data()? + */ + if (zp == NULL) + return (0); + + zfsvfs = zp->z_zfsvfs; + + if (!zfsvfs) + return (0); + + /* + * If we come here via vnode_create()->vclean() we can not end up in + * zil_commit() or we will deadlock. But we know that vnop_reclaim will + * be called next, so we just return success. + */ + if (vnode_isrecycled(ap->a_vp)) + return (0); + + err = zfs_fsync(VTOZ(ap->a_vp), /* flag */0, cr); + + if (err) dprintf("%s err %d\n", __func__, err); + + return (err); +} + +int +zfs_vnop_getattr(struct vnop_getattr_args *ap) +#if 0 + struct vnop_getattr_args { + struct vnode *a_vp; + struct vnode_vattr *a_vap; + vfs_context_t a_context; + }; +#endif +{ + int error; + DECLARE_CRED_AND_CONTEXT(ap); + + /* dprintf("+vnop_getattr zp %p vp %p\n", VTOZ(ap->a_vp), ap->a_vp); */ + + error = zfs_getattr(ap->a_vp, ap->a_vap, /* flags */0, cr, ct); + + if (error == 0) { + error = zfs_getattr_znode_unlocked(ap->a_vp, ap->a_vap); + } + if (error) + dprintf("-vnop_getattr '%p' %d\n", (ap->a_vp), error); + + return (error); +} + +int +zfs_vnop_setattr(struct vnop_setattr_args *ap) +#if 0 + struct vnop_setattr_args { + struct vnode *a_vp; + struct vnode_vattr *a_vap; + vfs_context_t a_context; + }; +#endif +{ + DECLARE_CRED(ap); + vattr_t *vap = ap->a_vap; + uint_t mask; + int error = 0; + znode_t *zp = VTOZ(ap->a_vp); + + /* Translate OS X requested mask to ZFS */ + mask = vap->va_mask; + + /* + * Both 'flags' and 'acl' can come to setattr, but without 'mode' set. + * However, ZFS assumes 'mode' is also set. We need to look up 'mode' in + * this case. + */ + if ((VATTR_IS_ACTIVE(vap, va_flags) || VATTR_IS_ACTIVE(vap, va_acl)) && + !VATTR_IS_ACTIVE(vap, va_mode)) { + uint64_t mode; + + mask |= ATTR_MODE; + + dprintf("fetching MODE for FLAGS or ACL\n"); + ZFS_ENTER(zp->z_zfsvfs); + ZFS_VERIFY_ZP(zp); + (void) sa_lookup(zp->z_sa_hdl, SA_ZPL_MODE(zp->z_zfsvfs), &mode, + sizeof (mode)); + vap->va_mode = mode; + ZFS_EXIT(zp->z_zfsvfs); + } + if (VATTR_IS_ACTIVE(vap, va_flags)) { + + /* + * If TRACKED is wanted, and not previously set, + * go set DocumentID + */ + if ((vap->va_flags & UF_TRACKED) && + !(zp->z_pflags & ZFS_TRACKED)) { + zfs_setattr_generate_id(zp, 0, NULL); + /* flags updated in vnops */ + zfs_setattr_set_documentid(zp, B_FALSE); + } + +#ifndef DECMPFS_XATTR_NAME +#define DECMPFS_XATTR_NAME "com.apple.decmpfs" +#endif + + /* If they are trying to turn on compression.. */ + if (vap->va_flags & UF_COMPRESSED) { + zp->z_skip_truncate_undo_decmpfs = B_TRUE; + dprintf("setattr trying to set COMPRESSED!\n"); + /* We return failure here, stops libarchive */ + return (SET_ERROR(ENOTSUP)); + } + /* Map OS X file flags to zfs file flags */ + zfs_setbsdflags(zp, vap->va_flags); + dprintf("OS X flags %08x changed to ZFS %04llx\n", + vap->va_flags, zp->z_pflags); + vap->va_flags = zp->z_pflags; + + } + + vap->va_mask = mask; + + /* + * If z_skip_truncate_undo_decmpfs is set, and they are trying to + * va_size == 0 (truncate), we undo the decmpfs work here. This is + * because we can not stop (no error, or !feature works) macOS from + * using decmpfs. + */ + if ((VATTR_IS_ACTIVE(vap, va_total_size) || + VATTR_IS_ACTIVE(vap, va_data_size)) && + zp->z_skip_truncate_undo_decmpfs) { + zp->z_skip_truncate_undo_decmpfs = B_FALSE; + + dprintf("setattr setsize with compress attempted\n"); + + if (zfs_vnop_removexattr_int(zp->z_zfsvfs, zp, + DECMPFS_XATTR_NAME, NULL) == 0) { + /* Successfully deleted the XATTR - skip truncate */ + VATTR_CLEAR_ACTIVE(vap, va_total_size); + VATTR_CLEAR_ACTIVE(vap, va_data_size); + dprintf("setattr skipping truncate!\n"); + } + } + + error = zfs_setattr(VTOZ(ap->a_vp), ap->a_vap, /* flag */0, cr); + + dprintf("vnop_setattr: called on vp %p with mask %04x, err=%d\n", + ap->a_vp, mask, error); + + if (!error) { + /* If successful, tell OS X which fields ZFS set. */ + if (VATTR_IS_ACTIVE(vap, va_data_size)) { + dprintf("ZFS: setattr new size %llx %llx\n", + vap->va_size, ubc_getsize(ap->a_vp)); + ubc_setsize(ap->a_vp, vap->va_size); + VATTR_SET_SUPPORTED(vap, va_data_size); + } + if (VATTR_IS_ACTIVE(vap, va_mode)) + VATTR_SET_SUPPORTED(vap, va_mode); + if (VATTR_IS_ACTIVE(vap, va_acl)) + VATTR_SET_SUPPORTED(vap, va_acl); + if (VATTR_IS_ACTIVE(vap, va_uid)) + VATTR_SET_SUPPORTED(vap, va_uid); + if (VATTR_IS_ACTIVE(vap, va_gid)) + VATTR_SET_SUPPORTED(vap, va_gid); + if (VATTR_IS_ACTIVE(vap, va_access_time)) + VATTR_SET_SUPPORTED(vap, va_access_time); + if (VATTR_IS_ACTIVE(vap, va_modify_time)) + VATTR_SET_SUPPORTED(vap, va_modify_time); + if (VATTR_IS_ACTIVE(vap, va_change_time)) + VATTR_SET_SUPPORTED(vap, va_change_time); + if (VATTR_IS_ACTIVE(vap, va_create_time)) + VATTR_SET_SUPPORTED(vap, va_create_time); + if (VATTR_IS_ACTIVE(vap, va_backup_time)) + VATTR_SET_SUPPORTED(vap, va_backup_time); + if (VATTR_IS_ACTIVE(vap, va_flags)) { + VATTR_SET_SUPPORTED(vap, va_flags); + } + + /* + * If we are told to ignore owners, we scribble over the uid + * and gid here unless root. + */ + if (((unsigned int)vfs_flags(zp->z_zfsvfs->z_vfs)) & + MNT_IGNORE_OWNERSHIP) { + if (kauth_cred_getuid(cr) != 0) { + vap->va_uid = UNKNOWNUID; + vap->va_gid = UNKNOWNGID; + } + } + } + +#if 1 + uint64_t missing = 0; + missing = (vap->va_active ^ (vap->va_active & vap->va_supported)); + if (missing != 0) { + dprintf("vnop_setattr:: asked %08llx replied %08llx " + "missing %08llx\n", vap->va_active, + vap->va_supported, missing); + } +#endif + + if (error) + dprintf("ZFS: vnop_setattr return failure %d\n", error); + return (error); +} + +int +zfs_vnop_rename(struct vnop_rename_args *ap) +#if 0 + struct vnop_rename_args { + struct vnode *a_fdvp; + struct vnode *a_fvp; + struct componentname *a_fcnp; + struct vnode *a_tdvp; + struct vnode *a_tvp; + struct componentname *a_tcnp; + vfs_context_t a_context; + }; +#endif +{ +// DECLARE_CRED_AND_CONTEXT(ap); + DECLARE_CRED(ap); + int error; + + dprintf("vnop_rename\n"); + + /* + * extern int zfs_rename(struct vnode *sdvp, char *snm, + * struct vnode *tdvp, char *tnm, cred_t *cr, caller_context_t *ct, + * int flags); + */ + error = zfs_rename(VTOZ(ap->a_fdvp), ap->a_fcnp->cn_nameptr, + VTOZ(ap->a_tdvp), ap->a_tcnp->cn_nameptr, cr, /* flags */0); + + if (!error) { + cache_purge_negatives(ap->a_fdvp); + cache_purge_negatives(ap->a_tdvp); + cache_purge(ap->a_fvp); + + zfs_rename_hardlink(ap->a_fvp, ap->a_tvp, + ap->a_fdvp, ap->a_tdvp, + ap->a_fcnp->cn_nameptr, + ap->a_tcnp->cn_nameptr); + if (ap->a_tvp) { + cache_purge(ap->a_tvp); + } + +#ifdef __APPLE__ + /* + * After a rename, the VGET path /.vol/$fsid/$ino fails for + * a short period on hardlinks (until someone calls lookup). + * So until we can figure out exactly why this is, we drive + * a lookup here to ensure that vget will work + * (Finder/Spotlight). + */ + if (ap->a_fvp && VTOZ(ap->a_fvp) && + VTOZ(ap->a_fvp)->z_finder_hardlink) { + struct vnode *vp; + if (VOP_LOOKUP(ap->a_tdvp, &vp, ap->a_tcnp, + spl_vfs_context_kernel()) == 0) + vnode_put(vp); + } +#endif + + } + + if (error) dprintf("%s: error %d\n", __func__, error); + return (error); +} + +#if defined(MAC_OS_X_VERSION_10_12) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) +int +zfs_vnop_renamex(struct vnop_renamex_args *ap) +#if 0 + struct vnop_renamex_args { + struct vnode *a_fdvp; + struct vnode *a_fvp; + struct componentname *a_fcnp; + struct vnode *a_tdvp; + struct vnode *a_tvp; + struct componentname *a_tcnp; + struct vnode_attr *a_vap; // Reserved for future use + vfs_rename_flags_t a_flags; + vfs_context_t a_context; + }; +#endif +{ + DECLARE_CRED(ap); + int error; + + dprintf("vnop_renamex\n"); + + /* + * extern int zfs_rename(struct vnode *sdvp, char *snm, + * struct vnode *tdvp, char *tnm, cred_t *cr, caller_context_t *ct, + * int flags); + * + * Currently, hfs only supports one flag, VFS_RENAME_EXCL, so + * we will do the same. Since zfs_rename() only has logic for + * FIGNORECASE, passing VFS_RENAME_EXCL should be ok, if a bit + * hacky. + */ + error = zfs_rename(VTOZ(ap->a_fdvp), ap->a_fcnp->cn_nameptr, + VTOZ(ap->a_tdvp), ap->a_tcnp->cn_nameptr, cr, + (ap->a_flags&VFS_RENAME_EXCL)); + + if (!error) { + cache_purge_negatives(ap->a_fdvp); + cache_purge_negatives(ap->a_tdvp); + cache_purge(ap->a_fvp); + + zfs_rename_hardlink(ap->a_fvp, ap->a_tvp, + ap->a_fdvp, ap->a_tdvp, + ap->a_fcnp->cn_nameptr, + ap->a_tcnp->cn_nameptr); + if (ap->a_tvp) { + cache_purge(ap->a_tvp); + } + +#ifdef __APPLE__ + /* + * After a rename, the VGET path /.vol/$fsid/$ino fails for + * a short period on hardlinks (until someone calls lookup). + * So until we can figure out exactly why this is, we drive + * a lookup here to ensure that vget will work + * (Finder/Spotlight). + */ + if (ap->a_fvp && VTOZ(ap->a_fvp) && + VTOZ(ap->a_fvp)->z_finder_hardlink) { + struct vnode *vp; + if (VOP_LOOKUP(ap->a_tdvp, &vp, ap->a_tcnp, + spl_vfs_context_kernel()) == 0) + vnode_put(vp); + } +#endif + + } + + if (error) dprintf("%s: error %d\n", __func__, error); + return (error); +} +#endif // vnop_renamex_args + +int +zfs_vnop_symlink(struct vnop_symlink_args *ap) +#if 0 + struct vnop_symlink_args { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vnode_vattr *a_vap; + char *a_target; + vfs_context_t a_context; + }; +#endif +{ + DECLARE_CRED(ap); + int error; + + dprintf("vnop_symlink\n"); + + /* + * extern int zfs_symlink(struct vnode *dvp, struct vnode **vpp, + * char *name, vattr_t *vap, char *link, cred_t *cr); + */ + + /* OS X doesn't need to set vap->va_mode? */ + znode_t *zp = NULL; + ap->a_vap->va_mode |= S_IFLNK; + error = zfs_symlink(VTOZ(ap->a_dvp), ap->a_cnp->cn_nameptr, + ap->a_vap, ap->a_target, &zp, cr, 0); + if (!error) { + *ap->a_vpp = ZTOV(zp); + cache_purge_negatives(ap->a_dvp); + } else { + dprintf("%s: error %d\n", __func__, error); + } + /* XXX zfs_attach_vnode()? */ + return (error); +} + + +int +zfs_vnop_readlink(struct vnop_readlink_args *ap) +#if 0 + struct vnop_readlink_args { + struct vnode *vp; + struct uio *uio; + vfs_context_t a_context; + }; +#endif +{ +// DECLARE_CRED_AND_CONTEXT(ap); + DECLARE_CRED(ap); + ZFS_UIO_INIT_XNU(uio, ap->a_uio); + + dprintf("vnop_readlink\n"); + + /* + * extern int zfs_readlink(struct vnode *vp, uio_t *uio, cred_t *cr, + * caller_context_t *ct); + */ + return (zfs_readlink(ap->a_vp, uio, cr)); +} + +int +zfs_vnop_link(struct vnop_link_args *ap) +#if 0 + struct vnop_link_args { + struct vnode *a_vp; + struct vnode *a_tdvp; + struct componentname *a_cnp; + vfs_context_t a_context; + }; +#endif +{ +// DECLARE_CRED_AND_CONTEXT(ap); + DECLARE_CRED(ap); + int error; + + dprintf("vnop_link\n"); + + /* XXX Translate this inside zfs_link() instead. */ + if (vnode_mount(ap->a_vp) != vnode_mount(ap->a_tdvp)) { + dprintf("%s: vp and tdvp on different mounts\n", __func__); + return (EXDEV); + } + + /* + * XXX Understand why Apple made this comparison in so many places where + * others do not. + */ + if (ap->a_cnp->cn_namelen >= ZAP_MAXNAMELEN) { + dprintf("%s: name too long %d\n", __func__, + ap->a_cnp->cn_namelen); + return (ENAMETOOLONG); + } + + /* + * extern int zfs_link(struct vnode *tdvp, struct vnode *svp, + * char *name, cred_t *cr, caller_context_t *ct, int flags); + */ + + error = zfs_link(VTOZ(ap->a_tdvp), VTOZ(ap->a_vp), + ap->a_cnp->cn_nameptr, cr, 0); + if (!error) { + // Set source vnode to multipath too, zfs_get_vnode() + // handles the target + vnode_setmultipath(ap->a_vp); + cache_purge(ap->a_vp); + cache_purge_negatives(ap->a_tdvp); + } else { + dprintf("%s error %d\n", __func__, error); + } + + return (error); +} + +int +zfs_vnop_pagein(struct vnop_pagein_args *ap) +#if 0 + struct vnop_pagein_args { + struct vnode *a_vp; + upl_t a_pl; + vm_offset_t a_pl_offset; + off_t a_foffset; + size_t a_size; + int a_flags; + vfs_context_t a_context; + }; +#endif +{ + /* XXX Crib this from the Apple zfs_vnops.c. */ + struct vnode *vp = ap->a_vp; + offset_t off = ap->a_f_offset; + size_t len = ap->a_size; + upl_t upl = ap->a_pl; + vm_offset_t upl_offset = ap->a_pl_offset; + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + caddr_t vaddr = NULL; + /* vm_offset_t vaddr = NULL; */ + int flags = ap->a_flags; + int need_unlock = 0; + int error = 0; + uint64_t file_sz; + + dprintf("+vnop_pagein: %p/%p off 0x%llx size 0x%lx filesz 0x%llx\n", + zp, vp, off, len, zp->z_size); + + if (upl == (upl_t)NULL) + panic("zfs_vnop_pagein: no upl!"); + + if (len <= 0) { + dprintf("zfs_vnop_pagein: invalid size %ld", len); + if (!(flags & UPL_NOCOMMIT)) + (void) ubc_upl_abort(upl, 0); + return (EINVAL); + } + + ZFS_ENTER(zfsvfs); + + file_sz = zp->z_size; + + /* ASSERT(zp->z_dbuf_held && zp->z_phys); */ + /* can't fault passed EOF */ + if ((off < 0) || (off >= file_sz) || + (len & PAGE_MASK) || (upl_offset & PAGE_MASK)) { + dprintf("passed EOF or size error\n"); + ZFS_EXIT(zfsvfs); + if (!(flags & UPL_NOCOMMIT)) + ubc_upl_abort_range(upl, upl_offset, len, + (UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY)); + return (EFAULT); + } + + /* + * If we already own the lock, then we must be page faulting in the + * middle of a write to this file (i.e., we are writing to this file + * using data from a mapped region of the file). + */ + if (!rw_write_held(&zp->z_map_lock)) { + rw_enter(&zp->z_map_lock, RW_WRITER); + need_unlock = TRUE; + } + + + if (ubc_upl_map(upl, (vm_offset_t *)&vaddr) != KERN_SUCCESS) { + dprintf("zfs_vnop_pagein: failed to ubc_upl_map"); + if (!(flags & UPL_NOCOMMIT)) + (void) ubc_upl_abort(upl, 0); + if (need_unlock) + rw_exit(&zp->z_map_lock); + ZFS_EXIT(zfsvfs); + return (ENOMEM); + } + + dprintf("vaddr %p with upl_off 0x%lx\n", vaddr, upl_offset); + vaddr += upl_offset; + + /* Can't read beyond EOF - but we need to zero those extra bytes. */ + if (off + len > file_sz) { + uint64_t newend = file_sz - off; + + dprintf("ZFS: pagein zeroing offset 0x%llx for 0x%llx bytes.\n", + newend, len - newend); + memset(&vaddr[newend], 0, len - newend); + len = newend; + } + /* + * Fill pages with data from the file. + */ + while (len > 0) { + uint64_t readlen; + + readlen = MIN(PAGESIZE, len); + + dprintf("pagein from off 0x%llx len 0x%llx into " + "address %p (len 0x%lx)\n", + off, readlen, vaddr, len); + + error = dmu_read(zp->z_zfsvfs->z_os, zp->z_id, off, readlen, + (void *)vaddr, DMU_READ_PREFETCH); + if (error) { + printf("zfs_vnop_pagein: dmu_read err %d\n", error); + break; + } + off += readlen; + vaddr += readlen; + len -= readlen; + } + ubc_upl_unmap(upl); + + if (!(flags & UPL_NOCOMMIT)) { + if (error) + ubc_upl_abort_range(upl, upl_offset, ap->a_size, + (UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY)); + else + ubc_upl_commit_range(upl, upl_offset, ap->a_size, + (UPL_COMMIT_CLEAR_DIRTY | + UPL_COMMIT_FREE_ON_EMPTY)); + } + ZFS_ACCESSTIME_STAMP(zfsvfs, zp); + + /* + * We can't grab the range lock for the page as reader which would stop + * truncation as this leads to deadlock. So we need to recheck the file + * size. + */ + if (ap->a_f_offset >= file_sz) + error = EFAULT; + if (need_unlock) + rw_exit(&zp->z_map_lock); + + ZFS_EXIT(zfsvfs); + if (error) dprintf("%s error %d\n", __func__, error); + return (error); +} + + + + +static int +zfs_pageout(zfsvfs_t *zfsvfs, znode_t *zp, upl_t upl, vm_offset_t upl_offset, + offset_t off, size_t size, int flags) +{ + dmu_tx_t *tx; + zfs_locked_range_t *lr; + uint64_t filesz; + int err = 0; + size_t len = size; + + dprintf("+vnop_pageout: %p/%p off 0x%llx len 0x%lx upl_off 0x%lx: " + "blksz 0x%x, z_size 0x%llx upl %p flags 0x%x\n", zp, ZTOV(zp), + off, len, upl_offset, zp->z_blksz, + zp->z_size, upl, flags); + + if (upl == (upl_t)NULL) { + dprintf("ZFS: vnop_pageout: failed on NULL upl\n"); + return (EINVAL); + } + /* + * We can't leave this function without either calling upl_commit or + * upl_abort. So use the non-error version. + */ + ZFS_ENTER_IFERROR(zfsvfs) { + if (!(flags & UPL_NOCOMMIT)) + (void) ubc_upl_abort(upl, + UPL_ABORT_DUMP_PAGES|UPL_ABORT_FREE_ON_EMPTY); + dprintf("ZFS: vnop_pageout: abort on z_unmounted\n"); + ZFS_EXIT(zfsvfs); + return (EIO); + } + + + /* ASSERT(zp->z_dbuf_held); */ /* field no longer present in znode. */ + + if (len <= 0) { + if (!(flags & UPL_NOCOMMIT)) + (void) ubc_upl_abort(upl, + UPL_ABORT_DUMP_PAGES|UPL_ABORT_FREE_ON_EMPTY); + err = EINVAL; + goto exit; + } + if (vnode_vfsisrdonly(ZTOV(zp))) { + if (!(flags & UPL_NOCOMMIT)) + ubc_upl_abort_range(upl, upl_offset, len, + UPL_ABORT_FREE_ON_EMPTY); + err = EROFS; + goto exit; + } + + filesz = zp->z_size; /* get consistent copy of zp_size */ + + if (off < 0 || off >= filesz || (off & PAGE_MASK_64) || + (len & PAGE_MASK)) { + if (!(flags & UPL_NOCOMMIT)) + ubc_upl_abort_range(upl, upl_offset, len, + UPL_ABORT_FREE_ON_EMPTY); + err = EINVAL; + goto exit; + } + + uint64_t pgsize = roundup(filesz, PAGESIZE); + + /* Any whole pages beyond the end of the file while we abort */ + if ((size + off) > pgsize) { + printf("ZFS: pageout abort outside pages (rounded 0x%llx > " + "UPLlen 0x%llx\n", pgsize, size + off); + ubc_upl_abort_range(upl, pgsize, + pgsize - (size + off), + UPL_ABORT_FREE_ON_EMPTY); + } + + dprintf("ZFS: starting with size %lx\n", len); + +top: + lr = zfs_rangelock_enter(&zp->z_rangelock, off, len, RL_WRITER); + /* + * can't push pages passed end-of-file + */ + filesz = zp->z_size; + if (off >= filesz) { + /* ignore all pages */ + err = 0; + goto out; + } else if (off + len > filesz) { +#if 0 + int npages = btopr(filesz - off); + page_t *trunc; + + page_list_break(&pp, &trunc, npages); + /* ignore pages past end of file */ + if (trunc) + pvn_write_done(trunc, flags); +#endif + len = filesz - off; + } + + tx = dmu_tx_create(zfsvfs->z_os); + if (!tx) { + dprintf("ZFS: zfs_vnops_osx: NULL TX encountered!\n"); + if (!(flags & UPL_NOCOMMIT)) + ubc_upl_abort_range(upl, upl_offset, len, + UPL_ABORT_FREE_ON_EMPTY); + err = EINVAL; + goto exit; + } + dmu_tx_hold_write(tx, zp->z_id, off, len); + + dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); + zfs_sa_upgrade_txholds(tx, zp); + err = dmu_tx_assign(tx, TXG_WAIT); + if (err != 0) { + if (err == ERESTART) { + zfs_rangelock_exit(lr); + dmu_tx_wait(tx); + dmu_tx_abort(tx); + goto top; + } + dmu_tx_abort(tx); + goto out; + } + + caddr_t va; + + if (ubc_upl_map(upl, (vm_offset_t *)&va) != KERN_SUCCESS) { + err = EINVAL; + goto out; + } + + va += upl_offset; + while (len >= PAGESIZE) { + ssize_t sz = PAGESIZE; + + dprintf("pageout: dmu_write off 0x%llx size 0x%lx\n", off, sz); + + dmu_write(zfsvfs->z_os, zp->z_id, off, sz, va, tx); + va += sz; + off += sz; + len -= sz; + } + + /* + * The last, possibly partial block needs to have the data zeroed that + * would extend past the size of the file. + */ + if (len > 0) { + ssize_t sz = len; + + dprintf("pageout: dmu_writeX off 0x%llx size 0x%lx\n", off, sz); + dmu_write(zfsvfs->z_os, zp->z_id, off, sz, va, tx); + + va += sz; + off += sz; + len -= sz; + + /* + * Zero out the remainder of the PAGE that didn't fit within + * the file size. + */ + // bzero(va, PAGESIZE-sz); + // dprintf("zero last 0x%lx bytes.\n", PAGESIZE-sz); + + } + ubc_upl_unmap(upl); + + if (err == 0) { + uint64_t mtime[2], ctime[2]; + sa_bulk_attr_t bulk[3]; + int count = 0; + + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL, + &mtime, 16); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, + &ctime, 16); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL, + &zp->z_pflags, 8); + zfs_tstamp_update_setup(zp, CONTENT_MODIFIED, mtime, ctime); + err = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx); + ASSERT0(err); + zfs_log_write(zfsvfs->z_log, tx, TX_WRITE, zp, off, len, 0, + NULL, NULL); + } + dmu_tx_commit(tx); + +out: + zfs_rangelock_exit(lr); + if (flags & UPL_IOSYNC) + zil_commit(zfsvfs->z_log, zp->z_id); + + if (!(flags & UPL_NOCOMMIT)) { + if (err) + ubc_upl_abort_range(upl, upl_offset, size, + (UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY)); + else + ubc_upl_commit_range(upl, upl_offset, size, + (UPL_COMMIT_CLEAR_DIRTY | + UPL_COMMIT_FREE_ON_EMPTY)); + } +exit: + ZFS_EXIT(zfsvfs); + if (err) dprintf("%s err %d\n", __func__, err); + return (err); +} + + + +int +zfs_vnop_pageout(struct vnop_pageout_args *ap) +#if 0 + struct vnop_pageout_args { + struct vnode *a_vp; + upl_t a_pl; + vm_offset_t a_pl_offset; + off_t a_foffset; + size_t a_size; + int a_flags; + vfs_context_t a_context; + }; +#endif +{ + struct vnode *vp = ap->a_vp; + int flags = ap->a_flags; + upl_t upl = ap->a_pl; + vm_offset_t upl_offset = ap->a_pl_offset; + size_t len = ap->a_size; + offset_t off = ap->a_f_offset; + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = NULL; + int error; + + if (!zp || !zp->z_zfsvfs) { + if (!(flags & UPL_NOCOMMIT)) + ubc_upl_abort(upl, + (UPL_ABORT_DUMP_PAGES|UPL_ABORT_FREE_ON_EMPTY)); + dprintf("ZFS: vnop_pageout: null zp or zfsvfs\n"); + return (ENXIO); + } + + zfsvfs = zp->z_zfsvfs; + + dprintf("+vnop_pageout: off 0x%llx len 0x%lx upl_off 0x%lx: " + "blksz 0x%x, z_size 0x%llx\n", off, len, upl_offset, zp->z_blksz, + zp->z_size); + + /* + * XXX Crib this too, although Apple uses parts of zfs_putapage(). + * Break up that function into smaller bits so it can be reused. + */ + error = zfs_pageout(zfsvfs, zp, upl, upl_offset, ap->a_f_offset, + len, flags); + + return (error); +} + + +static int bluster_pageout(zfsvfs_t *zfsvfs, znode_t *zp, upl_t upl, + upl_offset_t upl_offset, off_t f_offset, int size, + uint64_t filesize, int flags, caddr_t vaddr, + dmu_tx_t *tx) +{ + int io_size; + int rounded_size; + off_t max_size; + int is_clcommit = 0; + + if ((flags & UPL_NOCOMMIT) == 0) + is_clcommit = 1; + + /* + * If they didn't specify any I/O, then we are done... + * we can't issue an abort because we don't know how + * big the upl really is + */ + if (size <= 0) { + dprintf("%s invalid size %d\n", __func__, size); + return (EINVAL); + } + + if (vnode_vfsisrdonly(ZTOV(zp))) { + if (is_clcommit) + ubc_upl_abort_range(upl, upl_offset, size, + UPL_ABORT_FREE_ON_EMPTY); + dprintf("%s: readonly fs\n", __func__); + return (EROFS); + } + + /* + * can't page-in from a negative offset + * or if we're starting beyond the EOF + * or if the file offset isn't page aligned + * or the size requested isn't a multiple of PAGE_SIZE + */ + if (f_offset < 0 || f_offset >= filesize || + (f_offset & PAGE_MASK_64) || (size & PAGE_MASK)) { + if (is_clcommit) + ubc_upl_abort_range(upl, upl_offset, size, + UPL_ABORT_FREE_ON_EMPTY); + dprintf("%s: invalid offset or size\n", __func__); + return (EINVAL); + } + max_size = filesize - f_offset; + + if (size < max_size) + io_size = size; + else + io_size = max_size; + + rounded_size = (io_size + (PAGE_SIZE - 1)) & ~PAGE_MASK; + + if (size > rounded_size) { + if (is_clcommit) + ubc_upl_abort_range(upl, upl_offset + rounded_size, + size - rounded_size, UPL_ABORT_FREE_ON_EMPTY); + } + +#if 1 + if (f_offset + size > filesize) { + dprintf("ZFS: lowering size %u to %llu\n", + size, f_offset > filesize ? 0 : filesize - f_offset); + if (f_offset > filesize) + size = 0; + else + size = filesize - f_offset; + } +#endif + + dmu_write(zfsvfs->z_os, zp->z_id, f_offset, size, + &vaddr[upl_offset], tx); + + return (0); +} + + + + +/* + * In V2 of vnop_pageout, we are given a NULL upl, so that we can + * grab the file locks first, then request the upl to lock down pages. + */ +int +zfs_vnop_pageoutv2(struct vnop_pageout_args *ap) +#if 0 + struct vnop_pageout_args { + struct vnode *a_vp; + upl_t a_pl; + vm_offset_t a_pl_offset; + off_t a_foffset; + size_t a_size; + int a_flags; + vfs_context_t a_context; + }; +#endif +{ + struct vnode *vp = ap->a_vp; + int a_flags = ap->a_flags; + vm_offset_t a_pl_offset = ap->a_pl_offset; + size_t a_size = ap->a_size; + upl_t upl = ap->a_pl; + upl_page_info_t *pl; + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = NULL; + int error = 0; + uint64_t filesize; + zfs_locked_range_t *lr; + dmu_tx_t *tx; + caddr_t vaddr = NULL; + int merror = 0; + + /* + * We can still get into this function as non-v2 style, by the default + * pager (ie, swap - when we eventually support it) + */ + if (upl) { + dprintf("ZFS: Relaying vnop_pageoutv2 to vnop_pageout\n"); + return (zfs_vnop_pageout(ap)); + } + + if (!zp || !zp->z_zfsvfs) { + dprintf("ZFS: vnop_pageout: null zp or zfsvfs\n"); + return (ENXIO); + } + + if (ZTOV(zp) == NULL) { + dprintf("ZFS: vnop_pageout: null vp\n"); + return (ENXIO); + } + + // XNU can call us with iocount == 0 && usecount == 0. Grab + // a ref now so the vp doesn't reclaim while we are in here. + if (vnode_get(ZTOV(zp)) != 0) { + dprintf("ZFS: vnop_pageout: vnode_ref failed.\n"); + return (ENXIO); + } + + mutex_enter(&zp->z_lock); + + sa_handle_t *z_sa_hdl; + z_sa_hdl = zp->z_sa_hdl; + if (!z_sa_hdl) { + mutex_exit(&zp->z_lock); + vnode_put(ZTOV(zp)); + dprintf("ZFS: vnop_pageout: null sa_hdl\n"); + return (ENXIO); + } + + zfsvfs = zp->z_zfsvfs; + + mutex_exit(&zp->z_lock); + + if (error) { + dprintf("ZFS: %s: can't hold_sa: %d\n", __func__, error); + vnode_put(ZTOV(zp)); + return (ENXIO); + } + + dprintf("+vnop_pageout2: off 0x%llx len 0x%lx upl_off 0x%lx: " + "blksz 0x%x, z_size 0x%llx\n", ap->a_f_offset, a_size, + a_pl_offset, zp->z_blksz, + zp->z_size); + + + /* Start the pageout request */ + /* + * We can't leave this function without either calling upl_commit or + * upl_abort. So use the non-error version. + */ + ZFS_ENTER_IFERROR(zfsvfs) { + dprintf("ZFS: vnop_pageoutv2: abort on z_unmounted\n"); + error = EIO; + goto exit_abort; + } + if (vfs_flags(zfsvfs->z_vfs) & MNT_RDONLY) { + dprintf("ZFS: vnop_pageoutv2: readonly\n"); + error = EROFS; + goto exit_abort; + } + + lr = zfs_rangelock_enter(&zp->z_rangelock, ap->a_f_offset, a_size, + RL_WRITER); + + /* Grab UPL now */ + int request_flags; + + /* + * we're in control of any UPL we commit + * make sure someone hasn't accidentally passed in UPL_NOCOMMIT + */ + a_flags &= ~UPL_NOCOMMIT; + a_pl_offset = 0; + + if (a_flags & UPL_MSYNC) { + request_flags = UPL_UBC_MSYNC | UPL_RET_ONLY_DIRTY; + } else { + request_flags = UPL_UBC_PAGEOUT | UPL_RET_ONLY_DIRTY; + } + + error = ubc_create_upl(vp, ap->a_f_offset, ap->a_size, &upl, &pl, + request_flags); + if (error || (upl == NULL)) { + dprintf("ZFS: Failed to create UPL! %d\n", error); + goto pageout_done; + } + + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_write(tx, zp->z_id, ap->a_f_offset, ap->a_size); + + // NULL z_sa_hdl + if (z_sa_hdl != NULL) + dmu_tx_hold_sa(tx, z_sa_hdl, B_FALSE); + + zfs_sa_upgrade_txholds(tx, zp); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error != 0) { + dmu_tx_abort(tx); + ubc_upl_abort(upl, (UPL_ABORT_ERROR|UPL_ABORT_FREE_ON_EMPTY)); + goto pageout_done; + } + + off_t f_offset; + int64_t offset; + int64_t isize; + int64_t pg_index; + + filesize = zp->z_size; /* get consistent copy of zp_size */ + + isize = ap->a_size; + f_offset = ap->a_f_offset; + + /* + * Scan from the back to find the last page in the UPL, so that we + * aren't looking at a UPL that may have already been freed by the + * preceding aborts/completions. + */ + for (pg_index = ((isize) / PAGE_SIZE); pg_index > 0; ) { + if (upl_page_present(pl, --pg_index)) + break; + if (pg_index == 0) { + dprintf("ZFS: failed on pg_index\n"); + dmu_tx_commit(tx); + ubc_upl_abort_range(upl, 0, isize, + UPL_ABORT_FREE_ON_EMPTY); + goto pageout_done; + } + } + + dprintf("ZFS: isize %llu pg_index %llu\n", isize, pg_index); + /* + * initialize the offset variables before we touch the UPL. + * a_f_offset is the position into the file, in bytes + * offset is the position into the UPL, in bytes + * pg_index is the pg# of the UPL we're operating on. + * isize is the offset into the UPL of the last non-clean page. + */ + isize = ((pg_index + 1) * PAGE_SIZE); + + offset = 0; + pg_index = 0; + while (isize > 0) { + int64_t xsize; + int64_t num_of_pages; + + // printf("isize %d for page %d\n", isize, pg_index); + + if (!upl_page_present(pl, pg_index)) { + /* + * we asked for RET_ONLY_DIRTY, so it's possible + * to get back empty slots in the UPL. + * just skip over them + */ + f_offset += PAGE_SIZE; + offset += PAGE_SIZE; + isize -= PAGE_SIZE; + pg_index++; + + continue; + } + if (!upl_dirty_page(pl, pg_index)) { + /* + * hfs has a call to panic here, but we trigger this + * *a lot* so unsure what is going on + */ + dprintf("zfs_vnop_pageoutv2: unforeseen clean page " + "@ index %lld for UPL %p\n", pg_index, upl); + f_offset += PAGE_SIZE; + offset += PAGE_SIZE; + isize -= PAGE_SIZE; + pg_index++; + continue; + } + + /* + * We know that we have at least one dirty page. + * Now checking to see how many in a row we have + */ + num_of_pages = 1; + xsize = isize - PAGE_SIZE; + + while (xsize > 0) { + if (!upl_dirty_page(pl, pg_index + num_of_pages)) + break; + num_of_pages++; + xsize -= PAGE_SIZE; + } + xsize = num_of_pages * PAGE_SIZE; + + if (!vnode_isswap(vp)) { + off_t end_of_range; + + end_of_range = f_offset + xsize - 1; + if (end_of_range >= filesize) { + end_of_range = (off_t)(filesize - 1); + } + } + + // Map it if needed + if (!vaddr) { + if ((ubc_upl_map(upl, (vm_offset_t *)&vaddr) != + KERN_SUCCESS) || vaddr == NULL) { + error = EINVAL; + vaddr = NULL; + dprintf("ZFS: unable to map\n"); + goto out; + } + dprintf("ZFS: Mapped %p\n", vaddr); + } + + + dprintf("ZFS: bluster offset %lld fileoff %lld size %lld " + "filesize %lld\n", offset, f_offset, xsize, filesize); + merror = bluster_pageout(zfsvfs, zp, upl, offset, f_offset, + xsize, filesize, a_flags, vaddr, tx); + /* remember the first error */ + if ((error == 0) && (merror)) + error = merror; + + f_offset += xsize; + offset += xsize; + isize -= xsize; + pg_index += num_of_pages; + } // while isize + + /* finish off transaction */ + if (error == 0) { + uint64_t mtime[2], ctime[2]; + sa_bulk_attr_t bulk[3]; + int count = 0; + + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL, + &mtime, 16); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, + &ctime, 16); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL, + &zp->z_pflags, 8); + zfs_tstamp_update_setup(zp, CONTENT_MODIFIED, mtime, ctime); + zfs_log_write(zfsvfs->z_log, tx, TX_WRITE, zp, ap->a_f_offset, + a_size, 0, NULL, NULL); + } + dmu_tx_commit(tx); + + // unmap + if (vaddr) { + ubc_upl_unmap(upl); + vaddr = NULL; + } +out: + zfs_rangelock_exit(lr); + if (a_flags & UPL_IOSYNC) + zil_commit(zfsvfs->z_log, zp->z_id); + + if (error) + ubc_upl_abort(upl, (UPL_ABORT_ERROR|UPL_ABORT_FREE_ON_EMPTY)); + else + ubc_upl_commit_range(upl, 0, a_size, UPL_COMMIT_FREE_ON_EMPTY); + + upl = NULL; + + vnode_put(ZTOV(zp)); + + ZFS_EXIT(zfsvfs); + if (error) + dprintf("ZFS: pageoutv2 failed %d\n", error); + return (error); + +pageout_done: + zfs_rangelock_exit(lr); + +exit_abort: + dprintf("ZFS: pageoutv2 aborted %d\n", error); + // VERIFY(ubc_create_upl(vp, off, len, &upl, &pl, flags) == 0); + // ubc_upl_abort(upl, UPL_ABORT_FREE_ON_EMPTY); + + vnode_put(ZTOV(zp)); + + if (zfsvfs) + ZFS_EXIT(zfsvfs); + return (error); +} + + + + + + +int +zfs_vnop_mmap(struct vnop_mmap_args *ap) +#if 0 + struct vnop_mmap_args { + struct vnode *a_vp; + int a_fflags; + kauth_cred_t a_cred; + struct proc *a_p; + }; +#endif +{ + struct vnode *vp = ap->a_vp; + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs; + + if (!zp) + return (ENODEV); + + zfsvfs = zp->z_zfsvfs; + + dprintf("+vnop_mmap: %p\n", ap->a_vp); + + ZFS_ENTER(zfsvfs); + + if (!vnode_isreg(vp)) { + ZFS_EXIT(zfsvfs); + return (ENODEV); + } + mutex_enter(&zp->z_lock); + zp->z_is_mapped = 1; + mutex_exit(&zp->z_lock); + + ZFS_EXIT(zfsvfs); + dprintf("-vnop_mmap\n"); + return (0); +} + +int +zfs_vnop_mnomap(struct vnop_mnomap_args *ap) +#if 0 + struct vnop_mnomap_args { + struct vnode *a_vp; + int a_fflags; + kauth_cred_t a_cred; + struct proc *a_p; + }; +#endif +{ + struct vnode *vp = ap->a_vp; + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + + dprintf("+vnop_mnomap: %p\n", ap->a_vp); + + ZFS_ENTER(zfsvfs); + + if (!vnode_isreg(vp)) { + ZFS_EXIT(zfsvfs); + return (ENODEV); + } + mutex_enter(&zp->z_lock); + /* + * If a file as been mmaped even once, it needs to keep "z_is_mapped" + * high because it will potentially keep pages in the UPL cache we need + * to update on writes. We can either drop the UPL pages here, or simply + * keep updating both places on zfs_write(). + */ + /* zp->z_is_mapped = 0; */ + mutex_exit(&zp->z_lock); + + ZFS_EXIT(zfsvfs); + dprintf("-vnop_mnomap\n"); + return (0); +} + + + + +int +zfs_vnop_inactive(struct vnop_inactive_args *ap) +#if 0 + struct vnop_inactive_args { + struct vnode *a_vp; + vfs_context_t a_context; + }; +#endif +{ + struct vnode *vp = ap->a_vp; + zfs_inactive(vp); + return (0); +} + + + +#ifdef _KERNEL +uint64_t vnop_num_reclaims = 0; +uint64_t vnop_num_vnodes = 0; +#endif + + +int +zfs_vnop_reclaim(struct vnop_reclaim_args *ap) +#if 0 + struct vnop_reclaim_args { + struct vnode *a_vp; + vfs_context_t a_context; + }; +#endif +{ + /* + * Care needs to be taken here, we may already have called reclaim + * from vnop_inactive, if so, very little needs to be done. + */ + + struct vnode *vp = ap->a_vp; + znode_t *zp = NULL; + zfsvfs_t *zfsvfs = NULL; + + /* Destroy the vm object and flush associated pages. */ +#ifndef __APPLE__ + vnode_destroy_vobject(vp); +#endif + + /* Already been released? */ + zp = VTOZ(vp); + ASSERT(zp != NULL); + dprintf("+vnop_reclaim zp %p/%p type %d\n", zp, vp, vnode_vtype(vp)); + if (!zp) goto out; + + zfsvfs = zp->z_zfsvfs; + + if (!zfsvfs) { + dprintf("ZFS: vnop_reclaim with zfsvfs == NULL\n"); + return (0); + } + + if (zfsctl_is_node(vp)) { + dprintf("ZFS: vnop_reclaim with ctldir node\n"); + return (0); + } + + ZTOV(zp) = NULL; + + /* + * Purge old data structures associated with the denode. + */ + vnode_clearfsnode(vp); /* vp->v_data = NULL */ + vnode_removefsref(vp); /* ADDREF from vnode_create */ + atomic_dec_64(&vnop_num_vnodes); + + dprintf("+vnop_reclaim zp %p/%p unlinked %d unmount " + "%d sa_hdl %p\n", zp, vp, zp->z_unlinked, + zfsvfs->z_unmounted, zp->z_sa_hdl); + + rw_enter(&zfsvfs->z_teardown_inactive_lock, RW_READER); + if (zp->z_sa_hdl == NULL) { + zfs_znode_free(zp); + } else { + zfs_zinactive(zp); + zfs_znode_free(zp); + } + rw_exit(&zfsvfs->z_teardown_inactive_lock); + +#ifdef _KERNEL + atomic_inc_64(&vnop_num_reclaims); +#endif + +out: + return (0); +} + + + + + +int +zfs_vnop_mknod(struct vnop_mknod_args *ap) +#if 0 + struct vnop_mknod_args { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vnode_vattr *vap; + vfs_context_t a_context; + }; +#endif +{ + struct vnop_create_args create_ap; + int error; + + dprintf("%s\n", __func__); + + bzero(&create_ap, sizeof (struct vnop_create_args)); + + create_ap.a_dvp = ap->a_dvp; + create_ap.a_vpp = ap->a_vpp; + create_ap.a_cnp = ap->a_cnp; + create_ap.a_vap = ap->a_vap; + create_ap.a_context = ap->a_context; + + error = zfs_vnop_create(&create_ap); + if (error) dprintf("%s error %d\n", __func__, error); + return (error); +} + +int +zfs_vnop_allocate(struct vnop_allocate_args *ap) +#if 0 + struct vnop_allocate_args { + struct vnode *a_vp; + off_t a_length; + u_int32_t a_flags; + off_t *a_bytesallocated; + off_t a_offset; + vfs_context_t a_context; + }; +#endif +{ + struct vnode *vp = ap->a_vp; + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs; + uint64_t wantedsize = 0, filesize = 0; + int err = 0; + + dprintf("%s %llu %d %llu %llu: '%s'\n", __func__, ap->a_length, + ap->a_flags, (ap->a_bytesallocated ? *ap->a_bytesallocated : 0), + ap->a_offset, zp->z_name_cache); + + /* + * This code has been reverted: + * https://github.com/openzfsonosx/zfs/issues/631 + * Most likely not correctly aligned, and too-large offsets. + */ + return (0); + + if (!zp || !zp->z_sa_hdl) + return (ENODEV); + +// *ap->a_bytesallocated = 0; + + if (!vnode_isreg(vp)) { + ZFS_EXIT(zfsvfs); + return (ENODEV); + } + + filesize = zp->z_size; + wantedsize = ap->a_length; + + if (ap->a_flags & ALLOCATEFROMPEOF) + wantedsize += filesize; + else if (ap->a_flags & ALLOCATEFROMVOL) + /* blockhint = ap->a_offset / blocksize */ // yeah, no idea + dprintf("%s: help, allocatefromvolume set?\n", __func__); + + dprintf("%s: filesize %llu wantedsize %llu\n", __func__, + filesize, wantedsize); + + // If we are extending + if (wantedsize > filesize) { + + err = zfs_freesp(zp, wantedsize, 0, FWRITE, B_TRUE); + + // If we are truncating, Apple claims this code is never called. + } else if (wantedsize < filesize) { + + dprintf("%s: file shrinking branch taken?\n", __func__); + + } + + if (!err) { + *(ap->a_bytesallocated) = wantedsize - filesize; + } + + ZFS_EXIT(zfsvfs); + dprintf("-%s: %d\n", __func__, err); + return (err); +} + +int +zfs_vnop_whiteout(struct vnop_whiteout_args *ap) +#if 0 + struct vnop_whiteout_args { + struct vnode *a_dvp; + struct componentname *a_cnp; + int a_flags; + vfs_context_t a_context; + }; +#endif +{ + dprintf("vnop_whiteout: ENOTSUP\n"); + + return (ENOTSUP); +} + +int +zfs_vnop_pathconf(struct vnop_pathconf_args *ap) +#if 0 + struct vnop_pathconf_args { + struct vnode *a_vp; + int a_name; + register_t *a_retval; + vfs_context_t a_context; + }; +#endif +{ + int32_t *valp = ap->a_retval; + int error = 0; + + dprintf("+vnop_pathconf a_name %d\n", ap->a_name); + + switch (ap->a_name) { + case _PC_LINK_MAX: + *valp = INT_MAX; + break; + case _PC_PIPE_BUF: + *valp = PIPE_BUF; + break; + case _PC_CHOWN_RESTRICTED: + *valp = 200112; /* POSIX */ + break; + case _PC_NO_TRUNC: + *valp = 200112; /* POSIX */ + break; + case _PC_NAME_MAX: + case _PC_NAME_CHARS_MAX: + *valp = ZAP_MAXNAMELEN - 1; /* 255 */ + break; + case _PC_PATH_MAX: + case _PC_SYMLINK_MAX: + *valp = PATH_MAX; /* 1024 */ + break; + case _PC_CASE_SENSITIVE: + { + znode_t *zp = VTOZ(ap->a_vp); + *valp = 1; + if (zp && zp->z_zfsvfs) { + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + *valp = (zfsvfs->z_case == ZFS_CASE_SENSITIVE) ? 1 : 0; + } + } + break; + case _PC_CASE_PRESERVING: + *valp = 1; + break; +/* + * OS X 10.6 does not define this. + */ +#ifndef _PC_XATTR_SIZE_BITS +#define _PC_XATTR_SIZE_BITS 26 +#endif +/* + * Even though ZFS has 64 bit limit on XATTR size, there would appear to be a + * limit in SMB2 that the bit size returned has to be 18, or we will get an + * error from most XATTR calls (STATUS_ALLOTTED_SPACE_EXCEEDED). + */ +#ifndef AD_XATTR_SIZE_BITS +#define AD_XATTR_SIZE_BITS 18 +#endif + case _PC_XATTR_SIZE_BITS: + *valp = AD_XATTR_SIZE_BITS; + break; + case _PC_FILESIZEBITS: + *valp = 64; + break; + default: + printf("ZFS: unknown pathconf %d called.\n", ap->a_name); + error = EINVAL; + } + + if (error) dprintf("%s vp %p : %d\n", __func__, ap->a_vp, error); + return (error); +} + +int +zfs_vnop_getxattr(struct vnop_getxattr_args *ap) +#if 0 + struct vnop_getxattr_args { + struct vnodeop_desc *a_desc; + struct vnode *a_vp; + char *a_name; + struct uio *a_uio; + size_t *a_size; + int a_options; + vfs_context_t a_context; + }; +#endif +{ + DECLARE_CRED(ap); + struct vnode *vp = ap->a_vp; + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + ZFS_UIO_INIT_XNU(uio, ap->a_uio); + struct componentname cn = { 0 }; + int error = 0; + int size = 0; + uint64_t resid = ap->a_uio ? zfs_uio_resid(uio) : 0; + znode_t *xdzp = NULL, *xzp = NULL; + char *value = NULL; + + dprintf("+getxattr vp %p: '%s'\n", ap->a_vp, ap->a_name); + + /* xattrs disabled? */ + if (zfsvfs->z_xattr == B_FALSE) { + return (ENOTSUP); + } + + ZFS_ENTER(zfsvfs); + + if (zfsvfs->z_use_sa && zfsvfs->z_xattr_sa && zp->z_is_sa) { + + rw_enter(&zp->z_xattr_lock, RW_READER); + if (zp->z_xattr_cached == NULL) + error = -zfs_sa_get_xattr(zp); + rw_exit(&zp->z_xattr_lock); + + value = kmem_zalloc(resid, KM_SLEEP); + rw_enter(&zp->z_xattr_lock, RW_READER); + size = zpl_xattr_get_sa(vp, ap->a_name, value, resid); + rw_exit(&zp->z_xattr_lock); + + if (size < 0) { + error = -size; + goto out; + } + + /* Finderinfo checks */ + if (resid && + bcmp(ap->a_name, XATTR_FINDERINFO_NAME, + sizeof (XATTR_FINDERINFO_NAME)) == 0) { + + /* Must be 32 bytes */ + if (resid != sizeof (emptyfinfo) || + size != sizeof (emptyfinfo)) { + error = ERANGE; + goto out; + } + + /* According to HFS zero out some fields */ + finderinfo_update((uint8_t *)value, zp); + + /* If FinderInfo is empty > it doesn't exist */ + if (bcmp(value, emptyfinfo, + sizeof (emptyfinfo)) == 0) { + error = ENOATTR; + dprintf("empty finderinfo, so noattr\n"); + goto out; + } + + } + + + if (size > 0) { + // Size lookup ? + if (resid == 0) { + *ap->a_size = size; + } else { + error = zfs_uiomove(value, size, UIO_READ, + uio); + } + } + + goto out; + } + + /* Legacy xattr */ + + /* Grab the hidden attribute directory vnode. */ + if ((error = zfs_get_xattrdir(zp, &xdzp, cr, 0))) { + goto out; + } + + cn.cn_namelen = strlen(ap->a_name) + 1; + cn.cn_nameptr = (char *)kmem_zalloc(cn.cn_namelen, KM_SLEEP); + + /* Lookup the attribute name. */ + if ((error = zfs_dirlook(xdzp, (char *)ap->a_name, &xzp, 0, NULL, + &cn))) { + goto out; + } + + /* + * If we are dealing with FinderInfo, we duplicate the UIO first + * so that we can uiomove to/from it to modify contents. + */ + if (!error && + bcmp(ap->a_name, XATTR_FINDERINFO_NAME, + sizeof (XATTR_FINDERINFO_NAME)) == 0) { + ssize_t local_resid; + zfs_file_t zf; + u_int8_t finderinfo[32]; + + /* Read the attribute data. */ + /* FinderInfo is 32 bytes */ + // Size 0 to getsize, otherwise, it should be 32 bytes. + if (zfs_uio_resid(uio) > 0 && zfs_uio_resid(uio) < 32) { + error = ERANGE; + goto out; + } + + /* Use the convenience wrappers to read to SYSSPACE */ + zf.f_vnode = ZTOV(xzp); + zf.f_fd = -1; + + error = zfs_file_pread(&zf, &finderinfo, + sizeof (finderinfo), 0ULL, &local_resid); + + if (local_resid != 0) { + error = ERANGE; + } else { + + /* Update size if requested */ + if (ap->a_size) + *ap->a_size = (size_t)sizeof (finderinfo); + + /* According to HFS we are to zero out some fields */ + finderinfo_update((uint8_t *)&finderinfo, zp); + + /* If Finder Info is empty then it doesn't exist. */ + if (bcmp(finderinfo, emptyfinfo, + sizeof (emptyfinfo)) == 0) { + error = ENOATTR; + } else if (zfs_uio_resid(uio) > 0) { + /* Copy out the data we just modified */ + error = zfs_uiomove(&finderinfo, + sizeof (finderinfo), UIO_READ, uio); + + } /* Not empty */ + } /* Correct size */ + + /* We are done */ + goto out; + } /* Is finder info */ + + /* If NOT finderinfo */ + + if (ap->a_uio == NULL) { + + /* Query xattr size. */ + if (ap->a_size) { + mutex_enter(&xzp->z_lock); + *ap->a_size = (size_t)xzp->z_size; + mutex_exit(&xzp->z_lock); + } + + } else { + + /* Read xattr */ + error = zfs_read(xzp, uio, 0, cr); + + if (ap->a_size && ap->a_uio) { + *ap->a_size = (size_t)resid - zfs_uio_resid(uio); + } + + } + +out: + + if (error == ENOENT) + error = ENOATTR; + if (value != NULL) + kmem_free(value, resid); + + if (cn.cn_nameptr) + kmem_free(cn.cn_nameptr, cn.cn_namelen); + if (xzp) { + zrele(xzp); + } + if (xdzp) { + zrele(xdzp); + } + + ZFS_EXIT(zfsvfs); + dprintf("-getxattr vp %p : %d size %lu: %s\n", ap->a_vp, error, + !error && ap->a_size ? *ap->a_size : 0, + ap->a_uio == NULL ? "sizelookup" : "xattrread"); + return (error); +} + +int +zfs_vnop_setxattr(struct vnop_setxattr_args *ap) +#if 0 + struct vnop_setxattr_args { + struct vnodeop_desc *a_desc; + struct vnode *a_vp; + char *a_name; + struct uio *a_uio; + int a_options; + vfs_context_t a_context; + }; +#endif +{ + DECLARE_CRED(ap); + ZFS_UIO_INIT_XNU(uio, ap->a_uio); + struct vnode *vp = ap->a_vp; + struct vnode *xvp = NULLVP; + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + int flag; + int error = 0; + znode_t *xdzp = NULL; + + dprintf("+setxattr vp %p '%s' (enabled: %d) resid %lu\n", ap->a_vp, + ap->a_name, zfsvfs->z_xattr, zfs_uio_resid(uio)); + + /* xattrs disabled? */ + if (zfsvfs->z_xattr == B_FALSE) { + return (ENOTSUP); + } + + if (ap->a_name == NULL || ap->a_name[0] == '\0') { + return (EINVAL); /* invalid name */ + } + + ZFS_ENTER(zfsvfs); + + if (strlen(ap->a_name) >= ZAP_MAXNAMELEN) { + error = ENAMETOOLONG; + goto out; + } + + if (ap->a_options & XATTR_CREATE) + flag = ZNEW; /* expect no pre-existing entry */ + else if (ap->a_options & XATTR_REPLACE) + flag = ZEXISTS; /* expect an existing entry */ + else + flag = 0; + + + /* Preferentially store the xattr as a SA for better performance */ + if (zfsvfs->z_use_sa && zfsvfs->z_xattr_sa && zp->z_is_sa) { + char *value; + uint64_t size; + + rw_enter(&zp->z_xattr_lock, RW_READER); + if (zp->z_xattr_cached == NULL) + error = -zfs_sa_get_xattr(zp); + rw_exit(&zp->z_xattr_lock); + + rw_enter(&zp->z_xattr_lock, RW_WRITER); + + /* New, expect it to not exist .. */ + if ((flag & ZNEW) && + (zpl_xattr_get_sa(vp, ap->a_name, NULL, 0) > 0)) { + error = EEXIST; + rw_exit(&zp->z_xattr_lock); + goto out; + } + + /* Replace, XATTR must exist .. */ + if ((flag & ZEXISTS) && + ((error = + zpl_xattr_get_sa(vp, ap->a_name, NULL, 0)) <= 0) && + error == -ENOENT) { + error = ENOATTR; + rw_exit(&zp->z_xattr_lock); + goto out; + } + + size = zfs_uio_resid(uio); + value = kmem_zalloc(size, KM_SLEEP); + + size_t bytes; + + /* Copy in the xattr value */ + zfs_uiocopy(value, size, UIO_WRITE, + uio, &bytes); + + /* Finderinfo checks */ + if (!error && bytes && + bcmp(ap->a_name, XATTR_FINDERINFO_NAME, + sizeof (XATTR_FINDERINFO_NAME)) == 0) { + + /* Must be 32 bytes */ + if (bytes != sizeof (emptyfinfo)) { + error = ERANGE; + rw_exit(&zp->z_xattr_lock); + kmem_free(value, size); + goto out; + } + + /* According to HFS we are to zero out some fields */ + finderinfo_update((uint8_t *)value, zp); + } + + error = zpl_xattr_set_sa(vp, ap->a_name, + value, bytes, + flag, cr); + rw_exit(&zp->z_xattr_lock); + kmem_free(value, size); + + goto out; + } + + /* Legacy xattr */ + + if ((error = zfs_get_xattrdir(zp, &xdzp, cr, CREATE_XATTR_DIR))) { + goto out; + } + + /* Lookup or create the named attribute. */ + error = zpl_obtain_xattr(xdzp, ap->a_name, VTOZ(vp)->z_mode, cr, + &xvp, flag); + if (error) + goto out; + + /* Write the attribute data. */ + + /* OsX setxattr() replaces xattrs */ + error = zfs_freesp(VTOZ(xvp), 0, 0, VTOZ(vp)->z_mode, TRUE); + + /* Special case for Finderinfo */ + if (!error && + bcmp(ap->a_name, XATTR_FINDERINFO_NAME, + sizeof (XATTR_FINDERINFO_NAME)) == 0) { + + u_int8_t finderinfo[32]; + + /* Read the attribute data. */ + /* FinderInfo is 32 bytes */ + if ((user_size_t)zfs_uio_resid(uio) < 32) { + error = ERANGE; + goto out; + } + + /* Copy in the finderinfo to our space */ + error = zfs_uiomove(&finderinfo, + sizeof (finderinfo), UIO_WRITE, uio); + if (error) + goto out; + + /* Zero out some fields, according to HFS */ + finderinfo_update((uint8_t *)&finderinfo, zp); + + /* + * TODO: + * When writing FINDERINFO, we need to replace the + * ADDEDTIME date with actual crtime and not let + * userland overwrite it. + */ + + /* Empty Finderinfo is non-existent. */ + if (bcmp(finderinfo, emptyfinfo, sizeof (emptyfinfo)) == 0) { + /* Attempt to delete it? */ + error = zfs_remove(xdzp, (char *)ap->a_name, cr, 0); + goto out; + } + + /* Build a new uio to call zfs_write() to make it go in txg */ + struct uio *luio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE); + if (luio == NULL) { + error = ENOMEM; + goto out; + } + uio_addiov(luio, (user_addr_t)&finderinfo, sizeof (finderinfo)); + ZFS_UIO_INIT_XNU(tmpuio, luio); + + error = zfs_write(VTOZ(xvp), tmpuio, 0, cr); + + if (uio_resid(luio) != 0) + error = ERANGE; + + uio_free(luio); + + goto out; + } /* Finderinfo */ + + /* Write XATTR to disk */ + error = zfs_write(VTOZ(xvp), uio, 0, cr); + +out: + + if (error == ENOENT) + error = ENOATTR; + + if (xdzp) { + zrele(xdzp); + } + if (xvp) { + VN_RELE(xvp); + } + + ZFS_EXIT(zfsvfs); + dprintf("-setxattr vp %p: err %d: resid %llx\n", ap->a_vp, error, + uio_resid(ap->a_uio)); + return (error); +} + +int +zfs_vnop_removexattr_int(zfsvfs_t *zfsvfs, znode_t *zp, const char *name, + cred_t *cr) +{ + struct vnode *vp = ZTOV(zp); + struct componentname cn = { 0 }; + int error; + uint64_t xattr; + znode_t *xdzp = NULL, *xzp = NULL; + + dprintf("+removexattr_int vp %p '%s'\n", vp, name); + + ZFS_ENTER(zfsvfs); + + /* + * Recursive attributes are not allowed. + */ + if (zp->z_pflags & ZFS_XATTR) { + error = EINVAL; + goto out; + } + + if (zfsvfs->z_use_sa && zfsvfs->z_xattr_sa && zp->z_is_sa) { + nvlist_t *nvl; + + rw_enter(&zp->z_xattr_lock, RW_READER); + if (zp->z_xattr_cached == NULL) + error = -zfs_sa_get_xattr(zp); + rw_exit(&zp->z_xattr_lock); + + nvl = zp->z_xattr_cached; + + rw_enter(&zp->z_xattr_lock, RW_WRITER); + error = -nvlist_remove(nvl, name, DATA_TYPE_BYTE_ARRAY); + + dprintf("ZFS: removexattr nvlist_remove said %d\n", error); + if (!error) { + /* Update the SA for adds, modss, and removals. */ + error = -zfs_sa_set_xattr(zp); + rw_exit(&zp->z_xattr_lock); + goto out; + } + rw_exit(&zp->z_xattr_lock); + } + + sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zfsvfs), &xattr, sizeof (xattr)); + if (xattr == 0) { + error = ENOATTR; + goto out; + } + + /* Grab the hidden attribute directory vnode. */ + if ((error = zfs_get_xattrdir(zp, &xdzp, cr, 0))) { + goto out; + } + + cn.cn_namelen = strlen(name)+1; + cn.cn_nameptr = (char *)kmem_zalloc(cn.cn_namelen, KM_SLEEP); + + /* Lookup the attribute name. */ + if ((error = zfs_dirlook(xdzp, (char *)name, &xzp, 0, NULL, + &cn))) { + if (error == ENOENT) + error = ENOATTR; + goto out; + } + + error = zfs_remove(xdzp, (char *)name, cr, /* flags */0); + +out: + if (cn.cn_nameptr) + kmem_free(cn.cn_nameptr, cn.cn_namelen); + + if (xzp) { + zrele(xzp); + } + if (xdzp) { + zrele(xdzp); + } + + ZFS_EXIT(zfsvfs); + if (error) dprintf("%s vp %p: error %d\n", __func__, vp, error); + return (error); +} + +int +zfs_vnop_removexattr(struct vnop_removexattr_args *ap) +#if 0 + struct vnop_removexattr_args { + struct vnodeop_desc *a_desc; + struct vnode *a_vp; + char *a_name; + int a_options; + vfs_context_t a_context; + }; +#endif +{ + DECLARE_CRED(ap); + struct vnode *vp = ap->a_vp; + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + + dprintf("+removexattr vp %p '%s'\n", ap->a_vp, ap->a_name); + + /* xattrs disabled? */ + if (zfsvfs->z_xattr == B_FALSE) { + return (ENOTSUP); + } + + return (zfs_vnop_removexattr_int(zfsvfs, zp, ap->a_name, cr)); +} + + +int +zfs_vnop_listxattr(struct vnop_listxattr_args *ap) +#if 0 + struct vnop_listxattr_args { + struct vnodeop_desc *a_desc; + vnode_t a_vp; + uio_t a_uio; + size_t *a_size; + int a_options; + vfs_context_t a_context; + }; +#endif +{ + DECLARE_CRED(ap); + struct vnode *vp = ap->a_vp; + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + ZFS_UIO_INIT_XNU(uio, ap->a_uio); + zap_cursor_t zc; + zap_attribute_t za; + objset_t *os; + size_t size = 0; + char *nameptr; + char nfd_name[ZAP_MAXNAMELEN]; + size_t namelen; + int error = 0; + uint64_t xattr; + int force_formd_normalized_output; + znode_t *xdzp = NULL; + + dprintf("+listxattr vp %p: \n", ap->a_vp); + + /* xattrs disabled? */ + if (zfsvfs->z_xattr == B_FALSE) { + return (EINVAL); + } + + ZFS_ENTER(zfsvfs); + + /* + * Recursive attributes are not allowed. + */ + if (zp->z_pflags & ZFS_XATTR) { + error = EINVAL; + goto out; + } + + if (zfsvfs->z_use_sa && zp->z_is_sa && zp->z_xattr_cached) { + nvpair_t *nvp = NULL; + + rw_enter(&zp->z_xattr_lock, RW_READER); + if (zp->z_xattr_cached == NULL) + error = -zfs_sa_get_xattr(zp); + rw_exit(&zp->z_xattr_lock); + + while ((nvp = nvlist_next_nvpair(zp->z_xattr_cached, nvp)) != + NULL) { + ASSERT3U(nvpair_type(nvp), ==, DATA_TYPE_BYTE_ARRAY); + + if (xattr_protected(nvpair_name(nvp))) + continue; /* skip */ + + /* HFS: If it is finderinfo, and it is empty, skip it */ + if (bcmp(nvpair_name(nvp), XATTR_FINDERINFO_NAME, + sizeof (XATTR_FINDERINFO_NAME)) == 0) { + u_int8_t finderinfo[32]; + int size; + + rw_enter(&zp->z_xattr_lock, RW_WRITER); + size = zpl_xattr_get_sa(vp, nvpair_name(nvp), + &finderinfo, sizeof (finderinfo)); + rw_exit(&zp->z_xattr_lock); + if (size == sizeof (finderinfo)) { + finderinfo_update((uint8_t *) + &finderinfo, zp); + if (bcmp(finderinfo, emptyfinfo, + sizeof (emptyfinfo)) == 0) { + dprintf("skipping empty " + "finderinfo\n"); + // continue; // Skip + } // is empty + } // Correct size + } // finderinfo + + namelen = strlen(nvpair_name(nvp)) + 1; /* Null byte */ + + /* Just checking for space requirements? */ + if (ap->a_uio == NULL) { + size += namelen; + } else { + if (namelen > zfs_uio_resid(uio)) { + error = ERANGE; + break; + } + dprintf("ZFS: listxattr '%s'\n", + nvpair_name(nvp)); + error = zfs_uiomove((caddr_t)nvpair_name(nvp), + namelen, UIO_READ, uio); + if (error) + break; + } + } /* while nvlist */ + } /* SA xattr */ + if (error) goto out; + + /* Do we even have any attributes? */ + if (sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zfsvfs), &xattr, + sizeof (xattr)) || (xattr == 0)) { + goto out; /* all done */ + } + + /* Grab the hidden attribute directory vnode. */ + if (zfs_get_xattrdir(zp, &xdzp, cr, 0) != 0) { + goto out; + } + os = zfsvfs->z_os; + + for (zap_cursor_init(&zc, os, xdzp->z_id); + zap_cursor_retrieve(&zc, &za) == 0; zap_cursor_advance(&zc)) { + + if (xattr_protected(za.za_name)) + continue; /* skip */ + + + /* HFS: If it is finderinfo, and it is empty, skip it */ + if (bcmp(za.za_name, XATTR_FINDERINFO_NAME, + sizeof (XATTR_FINDERINFO_NAME)) == 0) { + u_int8_t finderinfo[32]; + zfs_file_t zf; + ssize_t local_resid = 0; + struct componentname cn = { 0 }; + znode_t *xzp = NULL; + + cn.cn_namelen = strlen(za.za_name) + 1; + cn.cn_nameptr = (char *)kmem_zalloc(cn.cn_namelen, + KM_SLEEP); + + /* Lookup the attribute name. */ + if (zfs_dirlook(xdzp, za.za_name, &xzp, 0, NULL, + &cn) == 0) { + + zf.f_vnode = ZTOV(xzp); + zf.f_fd = -1; + + error = zfs_file_pread(&zf, &finderinfo, + sizeof (finderinfo), 0ULL, &local_resid); + if (local_resid == 0) { + finderinfo_update((uint8_t *) + &finderinfo, zp); + if (bcmp(finderinfo, emptyfinfo, + sizeof (emptyfinfo)) == 0) { + dprintf("skipping empty " + "finderinfo\n"); + // continue; // Skip + } // is empty + } // Correct size + zrele(xzp); + } // lookup + kmem_free(cn.cn_nameptr, cn.cn_namelen); + } // finderinfo + + + /* + * Mac OS X: non-ascii names are UTF-8 NFC on disk + * so convert to NFD before exporting them. + */ + namelen = strlen(za.za_name); + + if (force_formd_normalized_output && + !is_ascii_str(za.za_name)) + force_formd_normalized_output = 1; + else + force_formd_normalized_output = 0; + + if (force_formd_normalized_output && + utf8_normalizestr((const u_int8_t *)za.za_name, namelen, + (u_int8_t *)nfd_name, &namelen, sizeof (nfd_name), + UTF_DECOMPOSED) == 0) { + nameptr = nfd_name; + } else { + nameptr = &za.za_name[0]; + } + ++namelen; /* account for NULL termination byte */ + if (ap->a_uio == NULL) { + size += namelen; + } else { + if (namelen > zfs_uio_resid(uio)) { + error = ERANGE; + break; + } + error = zfs_uiomove((caddr_t)nameptr, namelen, UIO_READ, + uio); + if (error) + break; + } + } + zap_cursor_fini(&zc); +out: + if (ap->a_uio == NULL) { + *ap->a_size = size; + } + if (xdzp) { + zrele(xdzp); + } + + ZFS_EXIT(zfsvfs); + if (error) { + dprintf("%s vp %p: error %d size %ld\n", __func__, + ap->a_vp, error, size); + } + return (error); +} + +#ifdef HAVE_NAMED_STREAMS +int +zfs_vnop_getnamedstream(struct vnop_getnamedstream_args *ap) +#if 0 + struct vnop_getnamedstream_args { + struct vnode *a_vp; + struct vnode **a_svpp; + char *a_name; + }; +#endif +{ + DECLARE_CRED(ap); + struct vnode *vp = ap->a_vp; + struct vnode **svpp = ap->a_svpp; + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + struct componentname cn = { 0 }; + int error = ENOATTR; + znode_t *xdzp = NULL; + znode_t *xzp = NULL; + + dprintf("+getnamedstream vp %p '%s': op %u\n", ap->a_vp, ap->a_name, + ap->a_operation); + + *svpp = NULLVP; + + ZFS_ENTER(zfsvfs); + + /* + * Mac OS X only supports the "com.apple.ResourceFork" stream. + */ + if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, + sizeof (XATTR_RESOURCEFORK_NAME)) != 0) + goto out; + + /* Only regular files */ + if (!vnode_isreg(vp)) { + return (EPERM); + } + + /* Grab the hidden attribute directory vnode. */ + if (zfs_get_xattrdir(zp, &xdzp, cr, 0) != 0) + goto out; + + cn.cn_namelen = strlen(ap->a_name) + 1; + cn.cn_nameptr = (char *)kmem_zalloc(cn.cn_namelen, KM_SLEEP); + + /* Lookup the attribute name. */ + if ((error = zfs_dirlook(xdzp, (char *)ap->a_name, &xzp, 0, NULL, + &cn))) { + if (error == ENOENT) + error = ENOATTR; + } else { + *svpp = ZTOV(xzp); + } + + kmem_free(cn.cn_nameptr, cn.cn_namelen); + +out: + if (xdzp) + zrele(xdzp); + +#if 0 // Disabled, not sure its required and empty vnodes are odd. + /* + * If the lookup is NS_OPEN, they are accessing "..namedfork/rsrc" + * to which we should return 0 with empty vp to empty file. + * See hfs_vnop_getnamedstream() + */ + if ((error == ENOATTR) && + ap->a_operation == NS_OPEN) { + + if ((error = zfs_get_xattrdir(zp, &xdvp, cr, + CREATE_XATTR_DIR)) == 0) { + /* Lookup or create the named attribute. */ + error = zpl_obtain_xattr(VTOZ(xdvp), ap->a_name, + VTOZ(vp)->z_mode, cr, ap->a_svpp, + ZNEW); + vnode_put(xdvp); + } + } +#endif + + ZFS_EXIT(zfsvfs); + if (error) dprintf("%s vp %p: error %d\n", __func__, ap->a_vp, error); + return (error); +} + +int +zfs_vnop_makenamedstream(struct vnop_makenamedstream_args *ap) +#if 0 + struct vnop_makenamedstream_args { + struct vnode *a_vp; + struct vnode **a_svpp; + char *a_name; + }; +#endif +{ + DECLARE_CRED(ap); + struct vnode *vp = ap->a_vp; + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + struct componentname cn; + struct vnode_attr vattr; + int error = 0; + znode_t *xdzp = NULL; + znode_t *xzp = NULL; + + dprintf("+makenamedstream vp %p: '%s'\n", ap->a_vp, ap->a_name); + + *ap->a_svpp = NULLVP; + + ZFS_ENTER(zfsvfs); + + /* Only regular files can have a resource fork stream. */ + if (!vnode_isreg(vp)) { + error = EPERM; + goto out; + } + + /* + * Mac OS X only supports the "com.apple.ResourceFork" stream. + */ + if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, + sizeof (XATTR_RESOURCEFORK_NAME)) != 0) { + error = ENOATTR; + goto out; + } + + /* Grab the hidden attribute directory vnode. */ + if ((error = zfs_get_xattrdir(zp, &xdzp, cr, CREATE_XATTR_DIR))) + goto out; + + bzero(&cn, sizeof (cn)); + cn.cn_nameiop = CREATE; + cn.cn_flags = ISLASTCN; + cn.cn_nameptr = (char *)ap->a_name; + cn.cn_namelen = strlen(cn.cn_nameptr); + + VATTR_INIT(&vattr); + VATTR_SET(&vattr, va_type, VREG); + VATTR_SET(&vattr, va_mode, VTOZ(vp)->z_mode & ~S_IFMT); + + error = zfs_create(xdzp, (char *)ap->a_name, &vattr, NONEXCL, + VTOZ(vp)->z_mode, &xzp, cr, 0, NULL); + + if (error == 0) + *ap->a_svpp = ZTOV(xzp); + +out: + if (xdzp) + zrele(xdzp); + + ZFS_EXIT(zfsvfs); + if (error) dprintf("%s vp %p: error %d\n", __func__, ap->a_vp, error); + return (error); +} + +int +zfs_vnop_removenamedstream(struct vnop_removenamedstream_args *ap) +#if 0 + struct vnop_removenamedstream_args { + struct vnode *a_vp; + struct vnode **a_svpp; + char *a_name; + }; +#endif +{ + struct vnode *svp = ap->a_svp; + znode_t *zp = VTOZ(svp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + int error = 0; + + dprintf("zfs_vnop_removenamedstream: %p '%s'\n", + svp, ap->a_name); + ZFS_ENTER(zfsvfs); + + /* + * Mac OS X only supports the "com.apple.ResourceFork" stream. + */ + if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, + sizeof (XATTR_RESOURCEFORK_NAME)) != 0) { + error = ENOATTR; + goto out; + } + + /* ### MISING CODE ### */ + /* + * It turns out that even though APPLE uses makenamedstream() to + * create a stream, for example compression, they use vnop_removexattr + * to delete it, so this appears not in use. + */ + dprintf("zfs_vnop_removenamedstream\n"); + error = EPERM; +out: + ZFS_EXIT(zfsvfs); + return (ENOTSUP); +} +#endif /* HAVE_NAMED_STREAMS */ + +/* + * The Darwin kernel's HFS+ appears to implement this by two methods, + * + * if (ap->a_options & FSOPT_EXCHANGE_DATA_ONLY) is set + * ** Copy the data of the files over (including rsrc) + * + * if not set + * ** exchange FileID between the two nodes, copy over vnode information + * like that of *time records, uid/gid, flags, mode, linkcount, + * finderinfo, c_desc, c_attr, c_flag, and cache_purge(). + * + * This call is deprecated in 10.8 + */ +int +zfs_vnop_exchange(struct vnop_exchange_args *ap) +#if 0 + struct vnop_exchange_args { + struct vnode *a_fvp; + struct vnode *a_tvp; + int a_options; + vfs_context_t a_context; + }; +#endif +{ + vnode_t *fvp = ap->a_fvp; + vnode_t *tvp = ap->a_tvp; + znode_t *fzp; + zfsvfs_t *zfsvfs; + + /* The files must be on the same volume. */ + if (vnode_mount(fvp) != vnode_mount(tvp)) { + dprintf("%s fvp and tvp not in same mountpoint\n", + __func__); + return (EXDEV); + } + + if (fvp == tvp) { + dprintf("%s fvp == tvp\n", __func__); + return (EINVAL); + } + + /* Only normal files can be exchanged. */ + if (!vnode_isreg(fvp) || !vnode_isreg(tvp)) { + dprintf("%s fvp or tvp is not a regular file\n", + __func__); + return (EINVAL); + } + + fzp = VTOZ(fvp); + zfsvfs = fzp->z_zfsvfs; + + ZFS_ENTER(zfsvfs); + + /* ADD MISSING CODE HERE */ + + ZFS_EXIT(zfsvfs); + printf("vnop_exchange: ENOTSUP\n"); + return (ENOTSUP); +} + +int +zfs_vnop_revoke(struct vnop_revoke_args *ap) +#if 0 + struct vnop_revoke_args { + struct vnode *a_vp; + int a_flags; + vfs_context_t a_context; + }; +#endif +{ + return (vn_revoke(ap->a_vp, ap->a_flags, ap->a_context)); +} + +int +zfs_vnop_blktooff(struct vnop_blktooff_args *ap) +#if 0 + struct vnop_blktooff_args { + struct vnode *a_vp; + daddr64_t a_lblkno; + off_t *a_offset; + }; +#endif +{ + dprintf("vnop_blktooff: 0\n"); + return (ENOTSUP); +} + +int +zfs_vnop_offtoblk(struct vnop_offtoblk_args *ap) +#if 0 + struct vnop_offtoblk_args { + struct vnode *a_vp; + off_t a_offset; + daddr64_t *a_lblkno; + }; +#endif +{ + dprintf("+vnop_offtoblk\n"); + return (ENOTSUP); +} + +int +zfs_vnop_blockmap(struct vnop_blockmap_args *ap) +#if 0 + struct vnop_blockmap_args { + struct vnode *a_vp; + off_t a_foffset; + size_t a_size; + daddr64_t *a_bpn; + size_t *a_run; + void *a_poff; + int a_flags; +}; +#endif +{ + dprintf("+vnop_blockmap\n"); + return (ENOTSUP); + +#if 0 + znode_t *zp; + zfsvfs_t *zfsvfs; + + ASSERT(ap); + ASSERT(ap->a_vp); + ASSERT(ap->a_size); + + if (!ap->a_bpn) { + return (0); + } + + if (vnode_isdir(ap->a_vp)) { + return (ENOTSUP); + } + + zp = VTOZ(ap->a_vp); + if (!zp) + return (ENODEV); + + zfsvfs = zp->z_zfsvfs; + if (!zfsvfs) + return (ENODEV); + + /* Return full request size as contiguous */ + if (ap->a_run) { + // *ap->a_run = ap->a_size; + *ap->a_run = 0; + } + if (ap->a_poff) { + *((int *)(ap->a_poff)) = 0; + /* + * returning offset of -1 asks the + * caller to zero the ranges + */ + // *((int *)(ap->a_poff)) = -1; + } + *ap->a_bpn = 0; +// *ap->a_bpn = (daddr64_t)(ap->a_foffset / zfsvfs->z_max_blksz); + + dprintf("%s ret %lu %d %llu\n", __func__, + ap->a_size, *((int *)(ap->a_poff)), *((uint64_t *)(ap->a_bpn))); + + return (0); +#endif +} + +int +zfs_vnop_strategy(struct vnop_strategy_args *ap) +#if 0 + struct vnop_strategy_args { + struct buf *a_bp; + }; +#endif +{ + dprintf("vnop_strategy: 0\n"); + return (ENOTSUP); +} + +int +zfs_vnop_select(struct vnop_select_args *ap) +#if 0 + struct vnop_select_args { + struct vnode *a_vp; + int a_which; + int a_fflags; + kauth_cred_t a_cred; + void *a_wql; + struct proc *a_p; + }; +#endif +{ + dprintf("vnop_select: 1\n"); + return (1); +} + +#ifdef WITH_READDIRATTR +int +zfs_vnop_readdirattr(struct vnop_readdirattr_args *ap) +#if 0 + struct vnop_readdirattr_args { + struct vnodeop_desc *a_desc; + struct vnode *a_vp; + struct attrlist *a_alist; + struct uio *a_uio; + ulong_t a_maxcount; + ulong_t a_options; + ulong_t *a_newstate; + int *a_eofflag; + ulong_t *a_actualcount; + vfs_context_t a_context; + }; +#endif +{ + struct vnode *vp = ap->a_vp; + struct attrlist *alp = ap->a_alist; + struct uio *uio = ap->a_uio; + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + zap_cursor_t zc; + zap_attribute_t zap; + attrinfo_t attrinfo; + int maxcount = ap->a_maxcount; + uint64_t offset = (uint64_t)uio_offset(uio); + u_int32_t fixedsize; + u_int32_t maxsize; + u_int32_t attrbufsize; + void *attrbufptr = NULL; + void *attrptr; + void *varptr; /* variable-length storage area */ + boolean_t user64 = vfs_context_is64bit(ap->a_context); + int prefetch = 0; + int error = 0; + +#if 0 + dprintf("+vnop_readdirattr\n"); +#endif + + *(ap->a_actualcount) = 0; + *(ap->a_eofflag) = 0; + + /* + * Check for invalid options or invalid uio. + */ + if (((ap->a_options & ~(FSOPT_NOINMEMUPDATE | FSOPT_NOFOLLOW)) != 0) || + (uio_resid(uio) <= 0) || (maxcount <= 0)) { + dprintf("%s invalid argument\n"); + return (EINVAL); + } + /* + * Reject requests for unsupported attributes. + */ + if ((alp->bitmapcount != ZFS_ATTR_BIT_MAP_COUNT) || + (alp->commonattr & ~ZFS_ATTR_CMN_VALID) || + (alp->dirattr & ~ZFS_ATTR_DIR_VALID) || + (alp->fileattr & ~ZFS_ATTR_FILE_VALID) || + (alp->volattr != 0 || alp->forkattr != 0)) { + dprintf("%s unsupported attr\n"); + return (EINVAL); + } + /* + * Check if we should prefetch znodes + */ + if ((alp->commonattr & ~ZFS_DIR_ENT_ATTRS) || + (alp->dirattr != 0) || (alp->fileattr != 0)) { + prefetch = TRUE; + } + + /* + * Setup a buffer to hold the packed attributes. + */ + fixedsize = sizeof (u_int32_t) + getpackedsize(alp, user64); + maxsize = fixedsize; + if (alp->commonattr & ATTR_CMN_NAME) + maxsize += ZAP_MAXNAMELEN + 1; + attrbufptr = (void*)kmem_zalloc(maxsize, KM_SLEEP); + if (attrbufptr == NULL) { + dprintf("%s kmem_zalloc failed\n"); + return (ENOMEM); + } + attrptr = attrbufptr; + varptr = (char *)attrbufptr + fixedsize; + + attrinfo.ai_attrlist = alp; + attrinfo.ai_varbufend = (char *)attrbufptr + maxsize; + attrinfo.ai_context = ap->a_context; + + ZFS_ENTER(zfsvfs); + + /* + * Initialize the zap iterator cursor. + */ + + if (offset <= 3) { + /* + * Start iteration from the beginning of the directory. + */ + zap_cursor_init(&zc, zfsvfs->z_os, zp->z_id); + } else { + /* + * The offset is a serialized cursor. + */ + zap_cursor_init_serialized(&zc, zfsvfs->z_os, zp->z_id, offset); + } + + while (1) { + ino64_t objnum; + enum vtype vtype = VNON; + znode_t *tmp_zp = NULL; + + /* + * Note that the low 4 bits of the cookie returned by zap is + * always zero. This allows us to use the low nibble for + * "special" entries: + * We use 0 for '.', and 1 for '..' (ignored here). + * If this is the root of the filesystem, we use the offset 2 + * for the *'.zfs' directory. + */ + if (offset <= 1) { + offset = 2; + continue; + } else if (offset == 2 && zfs_show_ctldir(zp)) { + (void) strlcpy(zap.za_name, ZFS_CTLDIR_NAME, + MAXNAMELEN); + objnum = ZFSCTL_INO_ROOT; + vtype = VDIR; + } else { + /* + * Grab next entry. + */ + if ((error = zap_cursor_retrieve(&zc, &zap))) { + *(ap->a_eofflag) = (error == ENOENT); + goto update; + } + + if (zap.za_integer_length != 8 || + zap.za_num_integers != 1) { + error = ENXIO; + goto update; + } + + objnum = ZFS_DIRENT_OBJ(zap.za_first_integer); + vtype = DTTOVT(ZFS_DIRENT_TYPE(zap.za_first_integer)); + /* Check if vtype is MIA */ + if ((vtype == 0) && !prefetch && (alp->dirattr || + alp->fileattr || + (alp->commonattr & ATTR_CMN_OBJTYPE))) { + prefetch = 1; + } + } + + /* Grab znode if required */ + if (prefetch) { + dmu_prefetch(zfsvfs->z_os, objnum, 0, 0); + if ((error = zfs_zget(zfsvfs, objnum, &tmp_zp)) == 0) { + if (vtype == VNON) { + /* SA_LOOKUP? */ + vtype = IFTOVT(tmp_zp->z_mode); + } + } else { + tmp_zp = NULL; + error = ENXIO; + goto skip_entry; + /* + * Currently ".zfs" entry is skipped, as we have + * no methods to pack that into the attrs (all + * helper functions take znode_t *, and .zfs is + * not a znode_t *). Add dummy .zfs code here if + * it is desirable to show .zfs in Finder. + */ + } + } + + /* + * Setup for the next item's attribute list + */ + *((u_int32_t *)attrptr) = 0; /* byte count slot */ + attrptr = ((u_int32_t *)attrptr) + 1; /* fixed attr start */ + attrinfo.ai_attrbufpp = &attrptr; + attrinfo.ai_varbufpp = &varptr; + + /* + * Pack entries into attribute buffer. + */ + if (alp->commonattr) { + commonattrpack(&attrinfo, zfsvfs, tmp_zp, zap.za_name, + objnum, vtype, user64); + } + if (alp->dirattr && vtype == VDIR) { + dirattrpack(&attrinfo, tmp_zp); + } + if (alp->fileattr && vtype != VDIR) { + fileattrpack(&attrinfo, zfsvfs, tmp_zp); + } + /* All done with tmp znode. */ + if (prefetch && tmp_zp) { + vnode_put(ZTOV(tmp_zp)); + tmp_zp = NULL; + } + attrbufsize = ((char *)varptr - (char *)attrbufptr); + + /* + * Make sure there's enough buffer space remaining. + */ + if (uio_resid(uio) < 0 || + attrbufsize > (u_int32_t)uio_resid(uio)) { + break; + } else { + *((u_int32_t *)attrbufptr) = attrbufsize; + error = uiomove((caddr_t)attrbufptr, attrbufsize, + UIO_READ, uio); + if (error != 0) + break; + attrptr = attrbufptr; + /* Point to variable-length storage */ + varptr = (char *)attrbufptr + fixedsize; + *(ap->a_actualcount) += 1; + + /* + * Move to the next entry, fill in the previous offset. + */ + skip_entry: + if ((offset > 2) || ((offset == 2) && + !zfs_show_ctldir(zp))) { + zap_cursor_advance(&zc); + offset = zap_cursor_serialize(&zc); + } else { + offset += 1; + } + + /* Termination checks */ + if (--maxcount <= 0 || uio_resid(uio) < 0 || + (u_int32_t)uio_resid(uio) < (fixedsize + + ZAP_AVENAMELEN)) { + break; + } + } + } +update: + zap_cursor_fini(&zc); + + if (attrbufptr) { + kmem_free(attrbufptr, maxsize); + } + if (error == ENOENT) { + error = 0; + } + ZFS_ACCESSTIME_STAMP(zfsvfs, zp); + + /* XXX newstate TBD */ + *ap->a_newstate = zp->z_atime[0] + zp->z_atime[1]; + uio_setoffset(uio, offset); + + ZFS_EXIT(zfsvfs); + dprintf("-readdirattr: error %d\n", error); + return (error); +} +#endif + + +#ifdef WITH_SEARCHFS +int +zfs_vnop_searchfs(struct vnop_searchfs_args *ap) +#if 0 + struct vnop_searchfs_args { + struct vnodeop_desc *a_desc; + struct vnode *a_vp; + void *a_searchparams1; + void *a_searchparams2; + struct attrlist *a_searchattrs; + ulong_t a_maxmatches; + struct timeval *a_timelimit; + struct attrlist *a_returnattrs; + ulong_t *a_nummatches; + ulong_t a_scriptcode; + ulong_t a_options; + struct uio *a_uio; + struct searchstate *a_searchstate; + vfs_context_t a_context; + }; +#endif +{ + printf("vnop_searchfs called, type %d\n", vnode_vtype(ap->a_vp)); + + *(ap->a_nummatches) = 0; + + return (ENOTSUP); +} +#endif + + + +/* + * Predeclare these here so that the compiler assumes that this is an "old + * style" function declaration that does not include arguments so that we won't + * get type mismatch errors in the initializations that follow. + */ +static int zfs_inval(void); +static int zfs_isdir(void); + +static int +zfs_inval() +{ + dprintf("ZFS: Bad vnop: returning EINVAL\n"); + return (EINVAL); +} + +static int +zfs_isdir() +{ + dprintf("ZFS: Bad vnop: returning EISDIR\n"); + return (EISDIR); +} + + +#define VOPFUNC int (*)(void *) + +/* Directory vnode operations template */ +int (**zfs_dvnodeops) (void *); +struct vnodeopv_entry_desc zfs_dvnodeops_template[] = { + {&vnop_default_desc, (VOPFUNC)vn_default_error }, + {&vnop_lookup_desc, (VOPFUNC)zfs_vnop_lookup}, + {&vnop_create_desc, (VOPFUNC)zfs_vnop_create}, + {&vnop_whiteout_desc, (VOPFUNC)zfs_vnop_whiteout}, + {&vnop_mknod_desc, (VOPFUNC)zfs_vnop_mknod}, + {&vnop_open_desc, (VOPFUNC)zfs_vnop_open}, + {&vnop_close_desc, (VOPFUNC)zfs_vnop_close}, + {&vnop_access_desc, (VOPFUNC)zfs_vnop_access}, + {&vnop_getattr_desc, (VOPFUNC)zfs_vnop_getattr}, + {&vnop_setattr_desc, (VOPFUNC)zfs_vnop_setattr}, + {&vnop_read_desc, (VOPFUNC)zfs_isdir}, + {&vnop_write_desc, (VOPFUNC)zfs_isdir}, + {&vnop_ioctl_desc, (VOPFUNC)zfs_vnop_ioctl}, + {&vnop_select_desc, (VOPFUNC)zfs_isdir}, + {&vnop_bwrite_desc, (VOPFUNC)zfs_isdir}, + {&vnop_fsync_desc, (VOPFUNC)zfs_vnop_fsync}, + {&vnop_remove_desc, (VOPFUNC)zfs_vnop_remove}, + {&vnop_link_desc, (VOPFUNC)zfs_vnop_link}, + {&vnop_rename_desc, (VOPFUNC)zfs_vnop_rename}, +#if defined(MAC_OS_X_VERSION_10_12) && \ + (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) + {&vnop_renamex_desc, (VOPFUNC)zfs_vnop_renamex}, +#endif + {&vnop_mkdir_desc, (VOPFUNC)zfs_vnop_mkdir}, + {&vnop_rmdir_desc, (VOPFUNC)zfs_vnop_rmdir}, + {&vnop_symlink_desc, (VOPFUNC)zfs_vnop_symlink}, + {&vnop_readdir_desc, (VOPFUNC)zfs_vnop_readdir}, + {&vnop_inactive_desc, (VOPFUNC)zfs_vnop_inactive}, + {&vnop_reclaim_desc, (VOPFUNC)zfs_vnop_reclaim}, + {&vnop_pathconf_desc, (VOPFUNC)zfs_vnop_pathconf}, + {&vnop_revoke_desc, (VOPFUNC)zfs_vnop_revoke}, + {&vnop_getxattr_desc, (VOPFUNC)zfs_vnop_getxattr}, + {&vnop_setxattr_desc, (VOPFUNC)zfs_vnop_setxattr}, + {&vnop_removexattr_desc, (VOPFUNC)zfs_vnop_removexattr}, + {&vnop_listxattr_desc, (VOPFUNC)zfs_vnop_listxattr}, +#ifdef WITH_READDIRATTR + {&vnop_readdirattr_desc, (VOPFUNC)zfs_vnop_readdirattr}, +#endif +#ifdef WITH_SEARCHFS + {&vnop_searchfs_desc, (VOPFUNC)zfs_vnop_searchfs}, +#endif + {NULL, (VOPFUNC)NULL } +}; +struct vnodeopv_desc zfs_dvnodeop_opv_desc = +{ &zfs_dvnodeops, zfs_dvnodeops_template }; + +/* Regular file vnode operations template */ +int (**zfs_fvnodeops) (void *); +struct vnodeopv_entry_desc zfs_fvnodeops_template[] = { + {&vnop_default_desc, (VOPFUNC)vn_default_error }, + {&vnop_whiteout_desc, (VOPFUNC)zfs_vnop_whiteout}, + {&vnop_open_desc, (VOPFUNC)zfs_vnop_open}, + {&vnop_close_desc, (VOPFUNC)zfs_vnop_close}, + {&vnop_access_desc, (VOPFUNC)zfs_vnop_access}, + {&vnop_getattr_desc, (VOPFUNC)zfs_vnop_getattr}, + {&vnop_setattr_desc, (VOPFUNC)zfs_vnop_setattr}, + {&vnop_read_desc, (VOPFUNC)zfs_vnop_read}, + {&vnop_write_desc, (VOPFUNC)zfs_vnop_write}, + {&vnop_ioctl_desc, (VOPFUNC)zfs_vnop_ioctl}, + {&vnop_select_desc, (VOPFUNC)zfs_vnop_select}, + {&vnop_fsync_desc, (VOPFUNC)zfs_vnop_fsync}, + {&vnop_inactive_desc, (VOPFUNC)zfs_vnop_inactive}, + {&vnop_reclaim_desc, (VOPFUNC)zfs_vnop_reclaim}, + {&vnop_pathconf_desc, (VOPFUNC)zfs_vnop_pathconf}, + {&vnop_bwrite_desc, (VOPFUNC)zfs_inval}, + {&vnop_pagein_desc, (VOPFUNC)zfs_vnop_pagein}, +#if HAVE_PAGEOUT_V2 + {&vnop_pageout_desc, (VOPFUNC)zfs_vnop_pageoutv2}, +#else + {&vnop_pageout_desc, (VOPFUNC)zfs_vnop_pageout}, +#endif + {&vnop_mmap_desc, (VOPFUNC)zfs_vnop_mmap}, + {&vnop_mnomap_desc, (VOPFUNC)zfs_vnop_mnomap}, + {&vnop_blktooff_desc, (VOPFUNC)zfs_vnop_blktooff}, + {&vnop_offtoblk_desc, (VOPFUNC)zfs_vnop_offtoblk}, + {&vnop_blockmap_desc, (VOPFUNC)zfs_vnop_blockmap}, + {&vnop_strategy_desc, (VOPFUNC)zfs_vnop_strategy}, + {&vnop_allocate_desc, (VOPFUNC)zfs_vnop_allocate}, + {&vnop_revoke_desc, (VOPFUNC)zfs_vnop_revoke}, + {&vnop_exchange_desc, (VOPFUNC)zfs_vnop_exchange}, + {&vnop_getxattr_desc, (VOPFUNC)zfs_vnop_getxattr}, + {&vnop_setxattr_desc, (VOPFUNC)zfs_vnop_setxattr}, + {&vnop_removexattr_desc, (VOPFUNC)zfs_vnop_removexattr}, + {&vnop_listxattr_desc, (VOPFUNC)zfs_vnop_listxattr}, +#ifdef HAVE_NAMED_STREAMS + {&vnop_getnamedstream_desc, (VOPFUNC)zfs_vnop_getnamedstream}, + {&vnop_makenamedstream_desc, (VOPFUNC)zfs_vnop_makenamedstream}, + {&vnop_removenamedstream_desc, (VOPFUNC)zfs_vnop_removenamedstream}, +#endif +#ifdef WITH_SEARCHFS + {&vnop_searchfs_desc, (VOPFUNC)zfs_vnop_searchfs}, +#endif + {NULL, (VOPFUNC)NULL } +}; +struct vnodeopv_desc zfs_fvnodeop_opv_desc = +{ &zfs_fvnodeops, zfs_fvnodeops_template }; + +/* Symbolic link vnode operations template */ +int (**zfs_symvnodeops) (void *); +struct vnodeopv_entry_desc zfs_symvnodeops_template[] = { + {&vnop_default_desc, (VOPFUNC)vn_default_error }, + {&vnop_open_desc, (VOPFUNC)zfs_vnop_open}, + {&vnop_close_desc, (VOPFUNC)zfs_vnop_close}, + {&vnop_access_desc, (VOPFUNC)zfs_vnop_access}, + {&vnop_getattr_desc, (VOPFUNC)zfs_vnop_getattr}, + {&vnop_setattr_desc, (VOPFUNC)zfs_vnop_setattr}, + {&vnop_ioctl_desc, (VOPFUNC)zfs_vnop_ioctl}, + {&vnop_readlink_desc, (VOPFUNC)zfs_vnop_readlink}, + {&vnop_inactive_desc, (VOPFUNC)zfs_vnop_inactive}, + {&vnop_reclaim_desc, (VOPFUNC)zfs_vnop_reclaim}, + {&vnop_pathconf_desc, (VOPFUNC)zfs_vnop_pathconf}, + {&vnop_revoke_desc, (VOPFUNC)zfs_vnop_revoke}, + {&vnop_getxattr_desc, (VOPFUNC)zfs_vnop_getxattr}, + {&vnop_setxattr_desc, (VOPFUNC)zfs_vnop_setxattr}, + {&vnop_removexattr_desc, (VOPFUNC)zfs_vnop_removexattr}, + {&vnop_listxattr_desc, (VOPFUNC)zfs_vnop_listxattr}, + {NULL, (VOPFUNC)NULL } +}; +struct vnodeopv_desc zfs_symvnodeop_opv_desc = +{ &zfs_symvnodeops, zfs_symvnodeops_template }; + +/* Extended attribtue directory vnode operations template */ +int (**zfs_xdvnodeops) (void *); +struct vnodeopv_entry_desc zfs_xdvnodeops_template[] = { + {&vnop_default_desc, (VOPFUNC)vn_default_error }, + {&vnop_lookup_desc, (VOPFUNC)zfs_vnop_lookup}, + {&vnop_create_desc, (VOPFUNC)zfs_vnop_create}, + {&vnop_whiteout_desc, (VOPFUNC)zfs_vnop_whiteout}, + {&vnop_mknod_desc, (VOPFUNC)zfs_inval}, + {&vnop_open_desc, (VOPFUNC)zfs_vnop_open}, + {&vnop_close_desc, (VOPFUNC)zfs_vnop_close}, + {&vnop_access_desc, (VOPFUNC)zfs_vnop_access}, + {&vnop_getattr_desc, (VOPFUNC)zfs_vnop_getattr}, + {&vnop_setattr_desc, (VOPFUNC)zfs_vnop_setattr}, + {&vnop_read_desc, (VOPFUNC)zfs_vnop_read}, + {&vnop_write_desc, (VOPFUNC)zfs_vnop_write}, + {&vnop_ioctl_desc, (VOPFUNC)zfs_vnop_ioctl}, + {&vnop_select_desc, (VOPFUNC)zfs_vnop_select}, + {&vnop_fsync_desc, (VOPFUNC)zfs_vnop_fsync}, + {&vnop_remove_desc, (VOPFUNC)zfs_vnop_remove}, + {&vnop_link_desc, (VOPFUNC)zfs_vnop_link}, + {&vnop_rename_desc, (VOPFUNC)zfs_vnop_rename}, + {&vnop_mkdir_desc, (VOPFUNC)zfs_inval}, + {&vnop_rmdir_desc, (VOPFUNC)zfs_vnop_rmdir}, + {&vnop_symlink_desc, (VOPFUNC)zfs_inval}, + {&vnop_readdir_desc, (VOPFUNC)zfs_vnop_readdir}, + {&vnop_inactive_desc, (VOPFUNC)zfs_vnop_inactive}, + {&vnop_reclaim_desc, (VOPFUNC)zfs_vnop_reclaim}, + {&vnop_pathconf_desc, (VOPFUNC)zfs_vnop_pathconf}, + {NULL, (VOPFUNC)NULL } +}; +struct vnodeopv_desc zfs_xdvnodeop_opv_desc = +{ &zfs_xdvnodeops, zfs_xdvnodeops_template }; + +/* Error vnode operations template */ +int (**zfs_evnodeops) (void *); +struct vnodeopv_entry_desc zfs_evnodeops_template[] = { + {&vnop_default_desc, (VOPFUNC)vn_default_error }, + {&vnop_inactive_desc, (VOPFUNC)zfs_vnop_inactive}, + {&vnop_reclaim_desc, (VOPFUNC)zfs_vnop_reclaim}, + {&vnop_pathconf_desc, (VOPFUNC)zfs_vnop_pathconf}, + {NULL, (VOPFUNC)NULL } +}; +struct vnodeopv_desc zfs_evnodeop_opv_desc = +{ &zfs_evnodeops, zfs_evnodeops_template }; + +int (**zfs_fifonodeops)(void *); +struct vnodeopv_entry_desc zfs_fifonodeops_template[] = { + { &vnop_default_desc, (VOPFUNC)vn_default_error }, + { &vnop_lookup_desc, (VOPFUNC)fifo_lookup }, + { &vnop_create_desc, (VOPFUNC)fifo_create }, + { &vnop_mknod_desc, (VOPFUNC)fifo_mknod }, + { &vnop_open_desc, (VOPFUNC)fifo_open }, + { &vnop_close_desc, (VOPFUNC)fifo_close }, + { &vnop_getattr_desc, (VOPFUNC)zfs_vnop_getattr }, + { &vnop_setattr_desc, (VOPFUNC)zfs_vnop_setattr }, + { &vnop_read_desc, (VOPFUNC)fifo_read }, + { &vnop_write_desc, (VOPFUNC)fifo_write }, + { &vnop_ioctl_desc, (VOPFUNC)fifo_ioctl }, + { &vnop_select_desc, (VOPFUNC)fifo_select }, + { &vnop_revoke_desc, (VOPFUNC)fifo_revoke }, + { &vnop_mmap_desc, (VOPFUNC)fifo_mmap }, + { &vnop_fsync_desc, (VOPFUNC)zfs_vnop_fsync }, + { &vnop_remove_desc, (VOPFUNC)fifo_remove }, + { &vnop_link_desc, (VOPFUNC)fifo_link }, + { &vnop_rename_desc, (VOPFUNC)fifo_rename }, + { &vnop_mkdir_desc, (VOPFUNC)fifo_mkdir }, + { &vnop_rmdir_desc, (VOPFUNC)fifo_rmdir }, + { &vnop_symlink_desc, (VOPFUNC)fifo_symlink }, + { &vnop_readdir_desc, (VOPFUNC)fifo_readdir }, + { &vnop_readlink_desc, (VOPFUNC)fifo_readlink }, + { &vnop_inactive_desc, (VOPFUNC)zfs_vnop_inactive }, + { &vnop_reclaim_desc, (VOPFUNC)zfs_vnop_reclaim }, + { &vnop_strategy_desc, (VOPFUNC)fifo_strategy }, + { &vnop_pathconf_desc, (VOPFUNC)fifo_pathconf }, + { &vnop_advlock_desc, (VOPFUNC)err_advlock }, + { &vnop_bwrite_desc, (VOPFUNC)zfs_inval }, + { &vnop_pagein_desc, (VOPFUNC)zfs_vnop_pagein }, +#if HAVE_PAGEOUT_V2 + { &vnop_pageout_desc, (VOPFUNC)zfs_vnop_pageoutv2 }, +#else + { &vnop_pageout_desc, (VOPFUNC)zfs_vnop_pageout }, +#endif + { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, + { &vnop_blktooff_desc, (VOPFUNC)zfs_vnop_blktooff }, + { &vnop_offtoblk_desc, (VOPFUNC)zfs_vnop_offtoblk }, + { &vnop_blockmap_desc, (VOPFUNC)zfs_vnop_blockmap }, + { &vnop_getxattr_desc, (VOPFUNC)zfs_vnop_getxattr}, + { &vnop_setxattr_desc, (VOPFUNC)zfs_vnop_setxattr}, + { &vnop_removexattr_desc, (VOPFUNC)zfs_vnop_removexattr}, + { &vnop_listxattr_desc, (VOPFUNC)zfs_vnop_listxattr}, + { (struct vnodeop_desc *)NULL, (VOPFUNC)NULL } +}; +struct vnodeopv_desc zfs_fifonodeop_opv_desc = + { &zfs_fifonodeops, zfs_fifonodeops_template }; + + +/* + * .zfs/snapdir vnops + */ +int (**zfs_ctldirops) (void *); +struct vnodeopv_entry_desc zfs_ctldir_template[] = { + {&vnop_default_desc, (VOPFUNC)vn_default_error }, + {&vnop_lookup_desc, (VOPFUNC)zfsctl_vnop_lookup}, + {&vnop_getattr_desc, (VOPFUNC)zfsctl_vnop_getattr}, + {&vnop_readdir_desc, (VOPFUNC)zfsctl_vnop_readdir}, + {&vnop_mkdir_desc, (VOPFUNC)zfsctl_vnop_mkdir}, + {&vnop_rmdir_desc, (VOPFUNC)zfsctl_vnop_rmdir}, + /* We also need to define these for the top ones to work */ + {&vnop_open_desc, (VOPFUNC)zfsctl_vnop_open}, + {&vnop_close_desc, (VOPFUNC)zfsctl_vnop_close}, + {&vnop_access_desc, (VOPFUNC)zfsctl_vnop_access}, + {&vnop_inactive_desc, (VOPFUNC)zfsctl_vnop_inactive}, + {&vnop_reclaim_desc, (VOPFUNC)zfsctl_vnop_reclaim}, + {&vnop_revoke_desc, (VOPFUNC)err_revoke}, + {&vnop_fsync_desc, (VOPFUNC)nop_fsync}, + {NULL, (VOPFUNC)NULL } +}; +struct vnodeopv_desc zfs_ctldir_opv_desc = +{ &zfs_ctldirops, zfs_ctldir_template }; + +/* + * Get new vnode for znode. + * + * This function uses zp->z_zfsvfs, zp->z_mode, zp->z_flags, zp->z_id and sets + * zp->z_vnode and zp->z_vid. + */ +int +zfs_znode_getvnode(znode_t *zp, zfsvfs_t *zfsvfs) +{ + struct vnode_fsparam vfsp; + struct vnode *vp = NULL; + + dprintf("getvnode zp %p with vp %p zfsvfs %p vfs %p\n", zp, vp, + zfsvfs, zfsvfs->z_vfs); + + if (zp->z_vnode) + panic("zp %p vnode already set\n", zp->z_vnode); + + bzero(&vfsp, sizeof (vfsp)); + vfsp.vnfs_str = "zfs"; + vfsp.vnfs_mp = zfsvfs->z_vfs; + vfsp.vnfs_vtype = IFTOVT((mode_t)zp->z_mode); + vfsp.vnfs_fsnode = zp; + vfsp.vnfs_flags = VNFS_ADDFSREF; + + /* Tag root directory */ + if (zp->z_id == zfsvfs->z_root) { + vfsp.vnfs_markroot = 1; + } + + switch (vfsp.vnfs_vtype) { + case VDIR: + if (zp->z_pflags & ZFS_XATTR) { + vfsp.vnfs_vops = zfs_xdvnodeops; + } else { + vfsp.vnfs_vops = zfs_dvnodeops; + } + zp->z_zn_prefetch = B_TRUE; /* z_prefetch default is enabled */ + break; + case VBLK: + case VCHR: + { + uint64_t rdev; + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_RDEV(zfsvfs), + &rdev, sizeof (rdev)) == 0); + + vfsp.vnfs_rdev = zfs_cmpldev(rdev); + } + /* FALLTHROUGH */ + case VSOCK: + vfsp.vnfs_vops = zfs_fvnodeops; + break; + case VFIFO: + vfsp.vnfs_vops = zfs_fifonodeops; + break; + case VREG: + vfsp.vnfs_vops = zfs_fvnodeops; + vfsp.vnfs_filesize = zp->z_size; + break; + case VLNK: + vfsp.vnfs_vops = zfs_symvnodeops; +#if 0 + vfsp.vnfs_filesize = ???; +#endif + break; + default: + vfsp.vnfs_vops = zfs_fvnodeops; + printf("ZFS: Warning, error-vnops selected: vtype %d\n", + vfsp.vnfs_vtype); + break; + } + + /* + * vnode_create() has a habit of calling both vnop_reclaim() and + * vnop_fsync(), which can create havok as we are already holding locks. + */ + + while (vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, &vp) != 0) { + kpreempt(KPREEMPT_SYNC); + } + atomic_inc_64(&vnop_num_vnodes); + + dprintf("Assigned zp %p with vp %p zfsvfs %p\n", zp, vp, zp->z_zfsvfs); + + /* + * Unfortunately, when it comes to IOCTL_GET_BOOT_INFO and getting + * the volume finderinfo, XNU checks the tags, and only acts on + * HFS. So we have to set it to HFS on the root. It is pretty gross + * but until XNU adds supporting code.. + * We no longer use tags in ZFS. + */ + if (zp->z_id == zfsvfs->z_root) + vnode_settag(vp, VT_HFS); + else + vnode_settag(vp, VT_ZFS); + + zp->z_vid = vnode_vid(vp); + zp->z_vnode = vp; + + /* + * OS X Finder is hardlink agnostic, so we need to mark vp's that + * are hardlinks, so that it forces a lookup each time, ignoring + * the name cache. + */ + if ((zp->z_links > 1) && (IFTOVT((mode_t)zp->z_mode) == VREG)) + vnode_setmultipath(vp); + + return (0); +} + + +/* + * Called by taskq, to call zfs_znode_getvnode( vnode_create( - and + * attach vnode to znode. + */ +void +zfs_znode_asyncgetvnode_impl(void *arg) +{ + znode_t *zp = (znode_t *)arg; + VERIFY3P(zp, !=, NULL); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + VERIFY3P(zfsvfs, !=, NULL); + + // Attach vnode, done as different thread + zfs_znode_getvnode(zp, zfsvfs); + + // Wake up anyone blocked on us + mutex_enter(&zp->z_attach_lock); + taskq_init_ent(&zp->z_attach_taskq); + cv_broadcast(&zp->z_attach_cv); + mutex_exit(&zp->z_attach_lock); + +} + + +/* + * If the znode's vnode is not yet attached (zp->z_vnode == NULL) + * we call taskq_wait to wait for it to complete. + * We guarantee znode has a vnode at the return of function only + * when return is "0". On failure to wait, it returns -1, and caller + * may consider waiting by other means. + */ +int +zfs_znode_asyncwait(zfsvfs_t *zfsvfs, znode_t *zp) +{ + int ret = -1; + + if (zp == NULL) + return (ret); + + if (zfsvfs == NULL) + return (ret); + + ZFS_ENTER_IFERROR(zfsvfs) + goto out; + + if (zfsvfs->z_os == NULL) + goto out; + + // Work out if we need to block, that is, we have + // no vnode AND a taskq was launched. Unsure if we should + // look inside taskqent node like this. + mutex_enter(&zp->z_attach_lock); + if (zp->z_vnode == NULL && + zp->z_attach_taskq.tqent_func != NULL) { + // We need to block and wait for taskq to finish. + cv_wait(&zp->z_attach_cv, &zp->z_attach_lock); + ret = 0; + } + mutex_exit(&zp->z_attach_lock); + +out: + ZFS_EXIT(zfsvfs); + return (ret); +} + +/* + * Called in place of VN_RELE() for the places that uses ZGET_FLAG_ASYNC. + */ +void +zfs_znode_asyncput_impl(znode_t *zp) +{ + // Make sure the other thread finished zfs_znode_getvnode(); + // This may block, if waiting is required. + zfs_znode_asyncwait(zp->z_zfsvfs, zp); + + // Safe to release now that it is attached. + VN_RELE(ZTOV(zp)); + +} + +/* + * Called in place of VN_RELE() for the places that uses ZGET_FLAG_ASYNC, + * where we also taskq it - as we come from reclaim. + */ +void +zfs_znode_asyncput(znode_t *zp) +{ + dsl_pool_t *dp = dmu_objset_pool(zp->z_zfsvfs->z_os); + taskq_t *tq = dsl_pool_zrele_taskq(dp); + vnode_t *vp = ZTOV(zp); + + VERIFY3P(tq, !=, NULL); + + /* If iocount > 1, AND, vp is set (not async_get) */ + if (vp != NULL && vnode_iocount(vp) > 1) { + VN_RELE(vp); + return; + } + + VERIFY(taskq_dispatch( + (taskq_t *)tq, + (task_func_t *)zfs_znode_asyncput_impl, zp, TQ_SLEEP) != 0); +} + +/* + * Attach a new vnode to the znode asynchronically. We do this using + * a taskq to call it, and then wait to release the iocount. + * Called of zget_ext(..., ZGET_FLAG_ASYNC); will use + * zfs_znode_asyncput(zp) instead of VN_RELE(vp). + */ +int +zfs_znode_asyncgetvnode(znode_t *zp, zfsvfs_t *zfsvfs) +{ + VERIFY(zp != NULL); + VERIFY(zfsvfs != NULL); + + // We should not have a vnode here. + VERIFY3P(ZTOV(zp), ==, NULL); + + dsl_pool_t *dp = dmu_objset_pool(zfsvfs->z_os); + taskq_t *tq = dsl_pool_zrele_taskq(dp); + VERIFY3P(tq, !=, NULL); + + mutex_enter(&zp->z_attach_lock); + taskq_dispatch_ent(tq, + (task_func_t *)zfs_znode_asyncgetvnode_impl, + zp, + TQ_SLEEP, + &zp->z_attach_taskq); + mutex_exit(&zp->z_attach_lock); + return (0); +} + + + +/* + * Maybe these should live in vfsops + */ +int +zfs_vfsops_init(void) +{ + struct vfs_fsentry vfe; + + /* Start thread to notify Finder of changes */ + zfs_start_notify_thread(); + + vfe.vfe_vfsops = &zfs_vfsops_template; + vfe.vfe_vopcnt = ZFS_VNOP_TBL_CNT; + vfe.vfe_opvdescs = zfs_vnodeop_opv_desc_list; + + strlcpy(vfe.vfe_fsname, "zfs", MFSNAMELEN); + + /* + * Note: must set VFS_TBLGENERICMNTARGS with VFS_TBLLOCALVOL + * to suppress local mount argument handling. + */ + vfe.vfe_flags = VFS_TBLTHREADSAFE | VFS_TBLNOTYPENUM | VFS_TBLLOCALVOL | + VFS_TBL64BITREADY | VFS_TBLNATIVEXATTR | VFS_TBLGENERICMNTARGS | + VFS_TBLREADDIR_EXTENDED; + +#if HAVE_PAGEOUT_V2 + vfe.vfe_flags |= VFS_TBLVNOP_PAGEOUTV2; +#endif + +#ifdef VFS_TBLCANMOUNTROOT // From 10.12 + vfe.vfe_flags |= VFS_TBLCANMOUNTROOT; +#endif + + vfe.vfe_reserv[0] = 0; + vfe.vfe_reserv[1] = 0; + + if (vfs_fsadd(&vfe, &zfs_vfsconf) != 0) + return (KERN_FAILURE); + else + return (KERN_SUCCESS); +} + +int +zfs_vfsops_fini(void) +{ + + zfs_stop_notify_thread(); + + return (vfs_fsremove(zfs_vfsconf)); +} diff --git a/module/os/macos/zfs/zfs_vnops_osx_lib.c b/module/os/macos/zfs/zfs_vnops_osx_lib.c new file mode 100644 index 000000000000..4a7a67569300 --- /dev/null +++ b/module/os/macos/zfs/zfs_vnops_osx_lib.c @@ -0,0 +1,2235 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2013 Will Andrews + * Copyright (c) 2013, 2020 Jorgen Lundman + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +extern int zfs_vnop_force_formd_normalized_output; /* disabled by default */ + +/* + * Unfortunately Apple defines "KAUTH_VNODE_ACCESS (1<<31)" which + * generates: "warning: signed shift result (0x80000000) sets the + * sign bit of the shift expression's type ('int') and becomes negative." + * So until they fix their define, we override it here. + */ + +#if KAUTH_VNODE_ACCESS == 0x80000000 +#undef KAUTH_VNODE_ACCESS +#define KAUTH_VNODE_ACCESS (1ULL<<31) +#endif + + + +int zfs_hardlink_addmap(znode_t *zp, uint64_t parentid, uint32_t linkid); + +/* Originally from illumos:uts/common/sys/vfs.h */ +typedef uint64_t vfs_feature_t; +#define VFSFT_XVATTR 0x100000001 /* Supports xvattr for attrs */ +#define VFSFT_CASEINSENSITIVE 0x100000002 /* Supports case-insensitive */ +#define VFSFT_NOCASESENSITIVE 0x100000004 /* NOT case-sensitive */ +#define VFSFT_DIRENTFLAGS 0x100000008 /* Supports dirent flags */ +#define VFSFT_ACLONCREATE 0x100000010 /* Supports ACL on create */ +#define VFSFT_ACEMASKONACCESS 0x100000020 /* Can use ACEMASK for access */ +#define VFSFT_SYSATTR_VIEWS 0x100000040 /* Supports sysattr view i/f */ +#define VFSFT_ACCESS_FILTER 0x100000080 /* dirents filtered by access */ +#define VFSFT_REPARSE 0x100000100 /* Supports reparse point */ +#define VFSFT_ZEROCOPY_SUPPORTED 0x100000200 /* Supports loaning buffers */ + +#define ZFS_SUPPORTED_VATTRS \ + (VNODE_ATTR_va_mode | \ + VNODE_ATTR_va_uid | \ + VNODE_ATTR_va_gid | \ + VNODE_ATTR_va_fsid | \ + VNODE_ATTR_va_fileid | \ + VNODE_ATTR_va_nlink | \ + VNODE_ATTR_va_data_size | \ + VNODE_ATTR_va_total_size | \ + VNODE_ATTR_va_rdev | \ + VNODE_ATTR_va_gen | \ + VNODE_ATTR_va_create_time | \ + VNODE_ATTR_va_access_time | \ + VNODE_ATTR_va_modify_time | \ + VNODE_ATTR_va_change_time | \ + VNODE_ATTR_va_backup_time | \ + VNODE_ATTR_va_flags | \ + VNODE_ATTR_va_parentid | \ + VNODE_ATTR_va_iosize | \ + VNODE_ATTR_va_filerev | \ + VNODE_ATTR_va_type | \ + VNODE_ATTR_va_encoding | \ + 0) + +// VNODE_ATTR_va_uuuid | +// VNODE_ATTR_va_guuid | + + + + + + + + +/* + * fnv_32a_str - perform a 32 bit Fowler/Noll/Vo FNV-1a hash on a string + * + * input: + * str - string to hash + * hval - previous hash value or 0 if first call + * + * returns: + * 32 bit hash as a static hash type + * + * NOTE: To use the recommended 32 bit FNV-1a hash, use FNV1_32A_INIT as the + * hval arg on the first call to either fnv_32a_buf() or fnv_32a_str(). + */ +uint32_t +fnv_32a_str(const char *str, uint32_t hval) +{ + unsigned char *s = (unsigned char *)str; /* unsigned string */ + + /* + * FNV-1a hash each octet in the buffer + */ + while (*s) { + + /* xor the bottom with the current octet */ + hval ^= (uint32_t)*s++; + + /* multiply by the 32 bit FNV magic prime mod 2^32 */ +#if defined(NO_FNV_GCC_OPTIMIZATION) + hval *= FNV_32_PRIME; +#else + hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + + (hval<<24); +#endif + } + + /* return our new hash value */ + return (hval); +} + +/* + * fnv_32a_buf - perform a 32 bit Fowler/Noll/Vo FNV-1a hash on a buffer + * + * input: + * buf- start of buffer to hash + * len- length of buffer in octets + * hval- previous hash value or 0 if first call + * + * returns: + * 32 bit hash as a static hash type + * + * NOTE: To use the recommended 32 bit FNV-1a hash, use FNV1_32A_INIT as the + * hval arg on the first call to either fnv_32a_buf() or fnv_32a_str(). + */ +uint32_t +fnv_32a_buf(void *buf, size_t len, uint32_t hval) +{ + unsigned char *bp = (unsigned char *)buf; /* start of buffer */ + unsigned char *be = bp + len; /* beyond end of buffer */ + + /* + * FNV-1a hash each octet in the buffer + */ + while (bp < be) { + + /* xor the bottom with the current octet */ + hval ^= (uint32_t)*bp++; + + /* multiply by the 32 bit FNV magic prime mod 2^32 */ +#if defined(NO_FNV_GCC_OPTIMIZATION) + hval *= FNV_32_PRIME; +#else + hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + + (hval<<24); +#endif + } + + /* return our new hash value */ + return (hval); +} + +int +zfs_getattr_znode_unlocked(struct vnode *vp, vattr_t *vap) +{ + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + int error = 0; + uint64_t parent; + sa_bulk_attr_t bulk[4]; + int count = 0; +#ifdef VNODE_ATTR_va_addedtime + uint64_t addtime[2] = { 0 }; +#endif + int ishardlink = 0; + + // printf("getattr_osx\n"); + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + + if (VATTR_IS_ACTIVE(vap, va_acl)) { + // printf("want acl\n"); + VATTR_RETURN(vap, va_uuuid, kauth_null_guid); + VATTR_RETURN(vap, va_guuid, kauth_null_guid); + + // dprintf("Calling getacl\n"); + if ((error = zfs_getacl(zp, &vap->va_acl, B_FALSE, NULL))) { + // dprintf("zfs_getacl returned error %d\n", error); + error = 0; + } else { + + VATTR_SET_SUPPORTED(vap, va_acl); + /* va_acl implies va_uuuid & va_guuid are supported. */ + VATTR_RETURN(vap, va_uuuid, kauth_null_guid); + VATTR_RETURN(vap, va_guuid, kauth_null_guid); + } + + } + + mutex_enter(&zp->z_lock); + + ishardlink = ((zp->z_links > 1) && + (IFTOVT((mode_t)zp->z_mode) == VREG)) ? 1 : 0; + if (zp->z_finder_hardlink == TRUE) + ishardlink = 1; + else if (ishardlink) + zp->z_finder_hardlink = TRUE; + + /* Work out which SA we need to fetch */ + + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_PARENT(zfsvfs), NULL, &parent, 8); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL, + &zp->z_pflags, 8); + + /* + * Unfortunately, sa_bulk_lookup does not let you handle optional + * SA entries - so have to look up the optionals individually. + */ + error = sa_bulk_lookup(zp->z_sa_hdl, bulk, count); + if (error) { + dprintf("ZFS: Warning: getattr failed sa_bulk_lookup: %d, " + "parent %llu, flags %llu\n", error, parent, zp->z_pflags); + mutex_exit(&zp->z_lock); + ZFS_EXIT(zfsvfs); + return (0); + } + +#ifdef VNODE_ATTR_va_addedtime + if (VATTR_IS_ACTIVE(vap, va_addedtime)) { + sa_lookup(zp->z_sa_hdl, SA_ZPL_ADDTIME(zfsvfs), + &addtime, sizeof (addtime)); + } +#endif + + /* + * On Mac OS X we always export the root directory id as 2 + */ + vap->va_fileid = INO_ZFSTOXNU(zp->z_id, zfsvfs->z_root); + + vap->va_data_size = zp->z_size; + vap->va_total_size = zp->z_size; + // vap->va_gen = zp->z_gen; + vap->va_gen = 0; +#if defined(DEBUG) || defined(ZFS_DEBUG) + if (zp->z_gen != 0) + dprintf("%s: va_gen %lld -> 0\n", __func__, zp->z_gen); +#endif + + if (vnode_isdir(vp)) { + vap->va_nlink = zp->z_size; + } else { + vap->va_nlink = zp->z_links; + } + + + /* + * Carbon compatibility, pretend to support this legacy attribute + */ + if (VATTR_IS_ACTIVE(vap, va_backup_time)) { + vap->va_backup_time.tv_sec = 0; + vap->va_backup_time.tv_nsec = 0; + VATTR_SET_SUPPORTED(vap, va_backup_time); + } + vap->va_flags = zfs_getbsdflags(zp); + /* + * On Mac OS X we always export the root directory id as 2 + * and its parent as 1 + */ + if (zp->z_id == zfsvfs->z_root) + vap->va_parentid = 1; + else if (parent == zfsvfs->z_root) + vap->va_parentid = 2; + else + vap->va_parentid = parent; + + // Hardlinks: Return cached parentid, make it 2 if root. + if (ishardlink && zp->z_finder_parentid) + vap->va_parentid = (zp->z_finder_parentid == zfsvfs->z_root) ? + 2 : zp->z_finder_parentid; + + vap->va_iosize = zp->z_blksz ? zp->z_blksz : zfsvfs->z_max_blksz; + // vap->va_iosize = 512; + VATTR_SET_SUPPORTED(vap, va_iosize); + + /* Don't include '.' and '..' in the number of entries */ + if (VATTR_IS_ACTIVE(vap, va_nchildren) && vnode_isdir(vp)) { + VATTR_RETURN(vap, va_nchildren, vap->va_nlink - 2); + } + + /* + * va_dirlinkcount is the count of directory hard links. When a file + * system does not support ATTR_DIR_LINKCOUNT, xnu will default to 1. + * Since we claim to support ATTR_DIR_LINKCOUNT both as valid and as + * native, we'll just return 1. We set 1 for this value in dirattrpack + * as well. If in the future ZFS actually supports directory hard links, + * we can return a real value. + */ + if (VATTR_IS_ACTIVE(vap, va_dirlinkcount) && vnode_isdir(vp)) { + VATTR_RETURN(vap, va_dirlinkcount, 1); + } + + + if (VATTR_IS_ACTIVE(vap, va_data_alloc) || + VATTR_IS_ACTIVE(vap, va_total_alloc)) { + uint32_t blksize; + u_longlong_t nblks; + sa_object_size(zp->z_sa_hdl, &blksize, &nblks); + vap->va_data_alloc = (uint64_t)512LL * (uint64_t)nblks; + vap->va_total_alloc = vap->va_data_alloc; + vap->va_supported |= VNODE_ATTR_va_data_alloc | + VNODE_ATTR_va_total_alloc; + } + + if (VATTR_IS_ACTIVE(vap, va_name)) { + vap->va_name[0] = 0; + + if (!vnode_isvroot(vp)) { + + /* + * Finder (Carbon) relies on getattr returning the + * correct name for hardlinks to work, so we store the + * lookup name in vnop_lookup if file references are + * high, then set the return name here. + * If we also want ATTR_CMN_* lookups to work, we need + * to set a unique va_linkid for each entry, and based + * on the linkid in the lookup, return the correct name. + * It is set in zfs_vnop_lookup(). + * Since zap_value_search is a slow call, we only use + * it if we have not cached the name in vnop_lookup. + */ + + // Cached name, from vnop_lookup + if (ishardlink && + zp->z_name_cache[0]) { + + strlcpy(vap->va_name, zp->z_name_cache, + MAXPATHLEN); + VATTR_SET_SUPPORTED(vap, va_name); + + } else if (zp->z_name_cache[0]) { + + strlcpy(vap->va_name, zp->z_name_cache, + MAXPATHLEN); + VATTR_SET_SUPPORTED(vap, va_name); + + } else { + + // Go find the name. + if (zap_value_search(zfsvfs->z_os, parent, + zp->z_id, ZFS_DIRENT_OBJ(-1ULL), + vap->va_name) == 0) { + VATTR_SET_SUPPORTED(vap, va_name); + // Might as well keep this name too. + strlcpy(zp->z_name_cache, vap->va_name, + MAXPATHLEN); + } // zap_value_search + + } + + dprintf("getattr: %p return name '%s':%04llx\n", vp, + vap->va_name, vap->va_linkid); + + + } else { + /* + * The vroot objects must return a unique name for + * Finder to be able to distringuish between mounts. + * For this reason we simply return the fullname, + * from the statfs mountedfrom + * + * dataset mountpoint + * foo /bar + * As we used to return "foo" to ATTR_CMN_NAME of + * "/bar" we change this to return "bar" as expected. + */ + char *r, *osname; + osname = vfs_statfs(zfsvfs->z_vfs)->f_mntonname; + r = strrchr(osname, '/'); + strlcpy(vap->va_name, + r ? &r[1] : osname, + MAXPATHLEN); + VATTR_SET_SUPPORTED(vap, va_name); + dprintf("getattr root returning '%s'\n", vap->va_name); + } + } + + if (VATTR_IS_ACTIVE(vap, va_linkid)) { + + /* + * Apple needs a little extra care with HARDLINKs. All hardlink + * targets return the same va_fileid (POSIX) but also return + * a unique va_linkid. This we generate by hashing the (unique) + * name and store as va_linkid. However, Finder will call + * vfs_vget() with linkid and expect to receive the correct link + * target, so we need to add it to the AVL z_hardlinks. + */ + if (ishardlink) { + hardlinks_t *searchnode, *findnode; + avl_index_t loc; + + // If we don't have a linkid, make one. + searchnode = kmem_alloc(sizeof (hardlinks_t), KM_SLEEP); + searchnode->hl_parent = vap->va_parentid; + searchnode->hl_fileid = zp->z_id; + strlcpy(searchnode->hl_name, zp->z_name_cache, + PATH_MAX); + + rw_enter(&zfsvfs->z_hardlinks_lock, RW_READER); + findnode = avl_find(&zfsvfs->z_hardlinks, searchnode, + &loc); + rw_exit(&zfsvfs->z_hardlinks_lock); + kmem_free(searchnode, sizeof (hardlinks_t)); + + if (!findnode) { + static uint32_t zfs_hardlink_sequence = + 1ULL<<31; + uint32_t id; + + id = atomic_inc_32_nv(&zfs_hardlink_sequence); + + zfs_hardlink_addmap(zp, vap->va_parentid, id); + VATTR_RETURN(vap, va_linkid, id); + + } else { + VATTR_RETURN(vap, va_linkid, + findnode->hl_linkid); + } + + } else { // !ishardlink - use same as fileid + + VATTR_RETURN(vap, va_linkid, vap->va_fileid); + + } + + } // active linkid + + if (VATTR_IS_ACTIVE(vap, va_filerev)) { + VATTR_RETURN(vap, va_filerev, 0); + } + if (VATTR_IS_ACTIVE(vap, va_fsid)) { + VATTR_RETURN(vap, va_fsid, zfsvfs->z_rdev); + } + if (VATTR_IS_ACTIVE(vap, va_type)) { + VATTR_RETURN(vap, va_type, vnode_vtype(ZTOV(zp))); + } + if (VATTR_IS_ACTIVE(vap, va_encoding)) { + VATTR_RETURN(vap, va_encoding, kTextEncodingMacUnicode); + } +#ifdef VNODE_ATTR_va_addedtime + /* + * ADDEDTIME should come from finderinfo according to hfs_attrlist.c + * in ZFS we can use crtime, and add logic to getxattr finderinfo to + * copy the ADDEDTIME into the structure. See vnop_getxattr + */ + if (VATTR_IS_ACTIVE(vap, va_addedtime)) { + /* Lookup the ADDTIME if it exists, if not, use CRTIME */ + if ((addtime[0] == 0) && (addtime[1])) { + dprintf("ZFS: ADDEDTIME using crtime %llu (error %d)\n", + vap->va_crtime.tv_sec, error); + vap->va_addedtime.tv_sec = vap->va_crtime.tv_sec; + vap->va_addedtime.tv_nsec = vap->va_crtime.tv_nsec; + } else { + dprintf("ZFS: ADDEDTIME using addtime %llu\n", + addtime[0]); + ZFS_TIME_DECODE(&vap->va_addedtime, addtime); + } + VATTR_SET_SUPPORTED(vap, va_addedtime); + } +#endif +#ifdef VNODE_ATTR_va_fsid64 + if (VATTR_IS_ACTIVE(vap, va_fsid64)) { + vap->va_fsid64.val[0] = + vfs_statfs(zfsvfs->z_vfs)->f_fsid.val[0]; + vap->va_fsid64.val[1] = vfs_typenum(zfsvfs->z_vfs); + VATTR_SET_SUPPORTED(vap, va_fsid64); + } +#endif +#ifdef VNODE_ATTR_va_write_gencount + if (VATTR_IS_ACTIVE(vap, va_write_gencount)) { + if (!zp->z_write_gencount) + atomic_inc_64(&zp->z_write_gencount); + VATTR_RETURN(vap, va_write_gencount, + (uint32_t)zp->z_write_gencount); + } +#endif + +#ifdef VNODE_ATTR_va_document_id + if (VATTR_IS_ACTIVE(vap, va_document_id)) { + + if (!zp->z_document_id) { + zfs_setattr_generate_id(zp, parent, vap->va_name); + } + + VATTR_RETURN(vap, va_document_id, zp->z_document_id); + } +#endif /* VNODE_ATTR_va_document_id */ + + +#if 0 // Issue #192 + if (VATTR_IS_ACTIVE(vap, va_uuuid)) { + kauth_cred_uid2guid(zp->z_uid, &vap->va_uuuid); + } + if (VATTR_IS_ACTIVE(vap, va_guuid)) { + kauth_cred_gid2guid(zp->z_gid, &vap->va_guuid); + } +#endif + + if (ishardlink) { + dprintf("ZFS:getattr(%s,%llu,%llu) parent %llu: cache_parent " + "%llu: va_nlink %u\n", VATTR_IS_ACTIVE(vap, va_name) ? + vap->va_name : zp->z_name_cache, + vap->va_fileid, + VATTR_IS_ACTIVE(vap, va_linkid) ? vap->va_linkid : 0, + vap->va_parentid, + zp->z_finder_parentid, + vap->va_nlink); + } + + vap->va_supported |= ZFS_SUPPORTED_VATTRS; + uint64_t missing = 0; + missing = (vap->va_active ^ (vap->va_active & vap->va_supported)); + if (missing != 0) { + dprintf("vnop_getattr:: asked %08llx replied %08llx " + " missing %08llx\n", + vap->va_active, vap->va_supported, + missing); + } + + mutex_exit(&zp->z_lock); + + ZFS_EXIT(zfsvfs); + return (error); +} + +boolean_t +vfs_has_feature(vfs_t *vfsp, vfs_feature_t vfsft) +{ + + switch (vfsft) { + case VFSFT_CASEINSENSITIVE: + case VFSFT_NOCASESENSITIVE: + return (B_TRUE); + default: + return (B_FALSE); + } +} + +int +zfs_access_native_mode(struct vnode *vp, int *mode, cred_t *cr, + caller_context_t *ct) +{ + int accmode = *mode & (VREAD|VWRITE|VEXEC /* |VAPPEND */); + int error = 0; + int flag = 0; // FIXME + + if (accmode != 0) + error = zfs_access(vp, accmode, flag, cr); + + *mode &= ~(accmode); + + return (error); +} + +int +zfs_ioflags(int ap_ioflag) +{ + int flags = 0; + + if (ap_ioflag & IO_APPEND) + flags |= FAPPEND; + if (ap_ioflag & IO_NDELAY) + flags |= FNONBLOCK; + if (ap_ioflag & IO_SYNC) + flags |= (FSYNC | FDSYNC | FRSYNC); + + return (flags); +} + +int +zfs_vnop_ioctl_fullfsync(struct vnode *vp, vfs_context_t ct, zfsvfs_t *zfsvfs) +{ + int error; + + error = zfs_fsync(VTOZ(vp), /* syncflag */ 0, NULL); + if (error) + return (error); + + if (zfsvfs->z_log != NULL) + zil_commit(zfsvfs->z_log, 0); + else + txg_wait_synced(dmu_objset_pool(zfsvfs->z_os), 0); + return (0); +} + +uint32_t +zfs_getbsdflags(znode_t *zp) +{ + uint32_t bsdflags = 0; + uint64_t zflags = zp->z_pflags; + + if (zflags & ZFS_NODUMP) + bsdflags |= UF_NODUMP; + if (zflags & ZFS_UIMMUTABLE) + bsdflags |= UF_IMMUTABLE; + if (zflags & ZFS_UAPPENDONLY) + bsdflags |= UF_APPEND; + if (zflags & ZFS_OPAQUE) + bsdflags |= UF_OPAQUE; + if (zflags & ZFS_HIDDEN) + bsdflags |= UF_HIDDEN; + if (zflags & ZFS_TRACKED) + bsdflags |= UF_TRACKED; + if (zflags & ZFS_COMPRESSED) + bsdflags |= UF_COMPRESSED; + + if (zflags & ZFS_SIMMUTABLE) + bsdflags |= SF_IMMUTABLE; + if (zflags & ZFS_SAPPENDONLY) + bsdflags |= SF_APPEND; + /* + * Due to every file getting archive set automatically, and OSX + * don't let you move/copy it as a user, we disable archive connection + * for now + * if (zflags & ZFS_ARCHIVE) + * bsdflags |= SF_ARCHIVED; + */ + dprintf("getbsd changing zfs %08lx to osx %08lx\n", + zflags, bsdflags); + return (bsdflags); +} + +void +zfs_setbsdflags(znode_t *zp, uint32_t bsdflags) +{ + uint64_t zflags; + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_FLAGS(zp->z_zfsvfs), + &zflags, sizeof (zflags)) == 0); + + if (bsdflags & UF_NODUMP) + zflags |= ZFS_NODUMP; + else + zflags &= ~ZFS_NODUMP; + + if (bsdflags & UF_IMMUTABLE) + zflags |= ZFS_UIMMUTABLE; + else + zflags &= ~ZFS_UIMMUTABLE; + + if (bsdflags & UF_APPEND) + zflags |= ZFS_UAPPENDONLY; + else + zflags &= ~ZFS_UAPPENDONLY; + + if (bsdflags & UF_OPAQUE) + zflags |= ZFS_OPAQUE; + else + zflags &= ~ZFS_OPAQUE; + + if (bsdflags & UF_HIDDEN) + zflags |= ZFS_HIDDEN; + else + zflags &= ~ZFS_HIDDEN; + + if (bsdflags & UF_TRACKED) + zflags |= ZFS_TRACKED; + else + zflags &= ~ZFS_TRACKED; + + if (bsdflags & UF_COMPRESSED) + zflags |= ZFS_COMPRESSED; + else + zflags &= ~ZFS_COMPRESSED; + + /* + * if (bsdflags & SF_ARCHIVED) + * zflags |= ZFS_ARCHIVE; + * else + * zflags &= ~ZFS_ARCHIVE; + */ + if (bsdflags & SF_IMMUTABLE) + zflags |= ZFS_SIMMUTABLE; + else + zflags &= ~ZFS_SIMMUTABLE; + + if (bsdflags & SF_APPEND) + zflags |= ZFS_SAPPENDONLY; + else + zflags &= ~ZFS_SAPPENDONLY; + + zp->z_pflags = zflags; + dprintf("setbsd changing osx %08lx to zfs %08lx\n", + bsdflags, zflags); + + /* + * (void )sa_update(zp->z_sa_hdl, SA_ZPL_FLAGS(zp->z_zfsvfs), + * (void *)&zp->z_pflags, sizeof (uint64_t), tx); + */ +} + +/* + * Lookup/Create an extended attribute entry. + * + * Input arguments: + * dzp - znode for hidden attribute directory + * name - name of attribute + * flag - ZNEW: if the entry already exists, fail with EEXIST. + * ZEXISTS: if the entry does not exist, fail with ENOENT. + * + * Output arguments: + * vpp - pointer to the vnode for the entry (NULL if there isn't one) + * + * Return value: 0 on success or errno value on failure. + */ +int +zpl_obtain_xattr(znode_t *dzp, const char *name, mode_t mode, cred_t *cr, + vnode_t **vpp, int flag) +{ + znode_t *xzp = NULL; + zfsvfs_t *zfsvfs = dzp->z_zfsvfs; + zilog_t *zilog; + zfs_dirlock_t *dl; + dmu_tx_t *tx; + struct vnode_attr vattr; + int error; + struct componentname cn = { 0 }; + zfs_acl_ids_t acl_ids; + + /* zfs_dirent_lock() expects a component name */ + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(dzp); + zilog = zfsvfs->z_log; + + VATTR_INIT(&vattr); + VATTR_SET(&vattr, va_type, VREG); + VATTR_SET(&vattr, va_mode, mode & ~S_IFMT); + + if ((error = zfs_acl_ids_create(dzp, 0, + &vattr, cr, NULL, &acl_ids)) != 0) { + ZFS_EXIT(zfsvfs); + return (error); + } + + cn.cn_namelen = strlen(name)+1; + cn.cn_nameptr = (char *)kmem_zalloc(cn.cn_namelen, KM_SLEEP); + +top: + /* Lock the attribute entry name. */ + if ((error = zfs_dirent_lock(&dl, dzp, (char *)name, &xzp, flag, + NULL, &cn))) { + goto out; + } + /* If the name already exists, we're done. */ + if (xzp != NULL) { + zfs_dirent_unlock(dl); + goto out; + } + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_sa(tx, dzp->z_sa_hdl, B_FALSE); + dmu_tx_hold_zap(tx, dzp->z_id, TRUE, (char *)name); + dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); + +#if 1 // FIXME + if (dzp->z_pflags & ZFS_INHERIT_ACE) { + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, SPA_MAXBLOCKSIZE); + } +#endif + zfs_sa_upgrade_txholds(tx, dzp); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + zfs_dirent_unlock(dl); + if (error == ERESTART) { + dmu_tx_wait(tx); + dmu_tx_abort(tx); + goto top; + } + dmu_tx_abort(tx); + goto out; + } + + zfs_mknode(dzp, &vattr, tx, cr, 0, &xzp, &acl_ids); + + /* + * ASSERT(xzp->z_id == zoid); + */ + (void) zfs_link_create(dl, xzp, tx, ZNEW); + zfs_log_create(zilog, tx, TX_CREATE, dzp, xzp, (char *)name, + NULL /* vsecp */, 0 /* acl_ids.z_fuidp */, &vattr); + dmu_tx_commit(tx); + + /* + * OS X - attach the vnode _after_ committing the transaction + */ + zfs_znode_getvnode(xzp, zfsvfs); + + zfs_dirent_unlock(dl); +out: + zfs_acl_ids_free(&acl_ids); + if (cn.cn_nameptr) + kmem_free(cn.cn_nameptr, cn.cn_namelen); + + /* The REPLACE error if doesn't exist is ENOATTR */ + if ((flag & ZEXISTS) && (error == ENOENT)) + error = ENOATTR; + + if (xzp) + *vpp = ZTOV(xzp); + + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * ace_trivial: + * determine whether an ace_t acl is trivial + * + * Trivialness implies that the acl is composed of only + * owner, group, everyone entries. ACL can't + * have read_acl denied, and write_owner/write_acl/write_attributes + * can only be owner@ entry. + */ +int +ace_trivial_common(void *acep, int aclcnt, + uint64_t (*walk)(void *, uint64_t, int aclcnt, + uint16_t *, uint16_t *, uint32_t *)) +{ + uint16_t flags; + uint32_t mask; + uint16_t type; + uint64_t cookie = 0; + + while ((cookie = walk(acep, cookie, aclcnt, &flags, &type, &mask))) { + switch (flags & ACE_TYPE_FLAGS) { + case ACE_OWNER: + case ACE_GROUP|ACE_IDENTIFIER_GROUP: + case ACE_EVERYONE: + break; + default: + return (1); + + } + + if (flags & (ACE_FILE_INHERIT_ACE| + ACE_DIRECTORY_INHERIT_ACE|ACE_NO_PROPAGATE_INHERIT_ACE| + ACE_INHERIT_ONLY_ACE)) + return (1); + + /* + * Special check for some special bits + * + * Don't allow anybody to deny reading basic + * attributes or a files ACL. + */ + if ((mask & (ACE_READ_ACL|ACE_READ_ATTRIBUTES)) && + (type == ACE_ACCESS_DENIED_ACE_TYPE)) + return (1); + + /* + * Delete permission is never set by default + */ + if (mask & ACE_DELETE) + return (1); + + /* + * Child delete permission should be accompanied by write + */ + if ((mask & ACE_DELETE_CHILD) && !(mask & ACE_WRITE_DATA)) + return (1); + /* + * only allow owner@ to have + * write_acl/write_owner/write_attributes/write_xattr/ + */ + + if (type == ACE_ACCESS_ALLOWED_ACE_TYPE && + (!(flags & ACE_OWNER) && (mask & + (ACE_WRITE_OWNER|ACE_WRITE_ACL| ACE_WRITE_ATTRIBUTES| + ACE_WRITE_NAMED_ATTRS)))) + return (1); + + } + + return (0); +} + + +void +acl_trivial_access_masks(mode_t mode, boolean_t isdir, trivial_acl_t *masks) +{ + uint32_t read_mask = ACE_READ_DATA; + uint32_t write_mask = ACE_WRITE_DATA|ACE_APPEND_DATA; + uint32_t execute_mask = ACE_EXECUTE; + + if (isdir) + write_mask |= ACE_DELETE_CHILD; + + masks->deny1 = 0; + if (!(mode & S_IRUSR) && (mode & (S_IRGRP|S_IROTH))) + masks->deny1 |= read_mask; + if (!(mode & S_IWUSR) && (mode & (S_IWGRP|S_IWOTH))) + masks->deny1 |= write_mask; + if (!(mode & S_IXUSR) && (mode & (S_IXGRP|S_IXOTH))) + masks->deny1 |= execute_mask; + + masks->deny2 = 0; + if (!(mode & S_IRGRP) && (mode & S_IROTH)) + masks->deny2 |= read_mask; + if (!(mode & S_IWGRP) && (mode & S_IWOTH)) + masks->deny2 |= write_mask; + if (!(mode & S_IXGRP) && (mode & S_IXOTH)) + masks->deny2 |= execute_mask; + + masks->allow0 = 0; + if ((mode & S_IRUSR) && (!(mode & S_IRGRP) && (mode & S_IROTH))) + masks->allow0 |= read_mask; + if ((mode & S_IWUSR) && (!(mode & S_IWGRP) && (mode & S_IWOTH))) + masks->allow0 |= write_mask; + if ((mode & S_IXUSR) && (!(mode & S_IXGRP) && (mode & S_IXOTH))) + masks->allow0 |= execute_mask; + + masks->owner = ACE_WRITE_ATTRIBUTES|ACE_WRITE_OWNER|ACE_WRITE_ACL| + ACE_WRITE_NAMED_ATTRS|ACE_READ_ACL|ACE_READ_ATTRIBUTES| + ACE_READ_NAMED_ATTRS|ACE_SYNCHRONIZE; + if (mode & S_IRUSR) + masks->owner |= read_mask; + if (mode & S_IWUSR) + masks->owner |= write_mask; + if (mode & S_IXUSR) + masks->owner |= execute_mask; + + masks->group = ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_READ_NAMED_ATTRS| + ACE_SYNCHRONIZE; + if (mode & S_IRGRP) + masks->group |= read_mask; + if (mode & S_IWGRP) + masks->group |= write_mask; + if (mode & S_IXGRP) + masks->group |= execute_mask; + + masks->everyone = ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_READ_NAMED_ATTRS| + ACE_SYNCHRONIZE; + if (mode & S_IROTH) + masks->everyone |= read_mask; + if (mode & S_IWOTH) + masks->everyone |= write_mask; + if (mode & S_IXOTH) + masks->everyone |= execute_mask; +} + +void commonattrpack(attrinfo_t *aip, zfsvfs_t *zfsvfs, znode_t *zp, + const char *name, ino64_t objnum, enum vtype vtype, + boolean_t user64) +{ + attrgroup_t commonattr = aip->ai_attrlist->commonattr; + void *attrbufptr = *aip->ai_attrbufpp; + void *varbufptr = *aip->ai_varbufpp; + struct mount *mp = zfsvfs->z_vfs; + cred_t *cr = (cred_t *)vfs_context_ucred(aip->ai_context); + finderinfo_t finderinfo; + + /* + * We should probably combine all the sa_lookup into a bulk + * lookup operand. + */ + + finderinfo.fi_flags = 0; + + if (ATTR_CMN_NAME & commonattr) { + nameattrpack(aip, name, strlen(name)); + attrbufptr = *aip->ai_attrbufpp; + varbufptr = *aip->ai_varbufpp; + } + if (ATTR_CMN_DEVID & commonattr) { + *((dev_t *)attrbufptr) = vfs_statfs(mp)->f_fsid.val[0]; + attrbufptr = ((dev_t *)attrbufptr) + 1; + } + if (ATTR_CMN_FSID & commonattr) { + *((fsid_t *)attrbufptr) = vfs_statfs(mp)->f_fsid; + attrbufptr = ((fsid_t *)attrbufptr) + 1; + } + if (ATTR_CMN_OBJTYPE & commonattr) { + *((fsobj_type_t *)attrbufptr) = vtype; + attrbufptr = ((fsobj_type_t *)attrbufptr) + 1; + } + if (ATTR_CMN_OBJTAG & commonattr) { + *((fsobj_tag_t *)attrbufptr) = VT_ZFS; + attrbufptr = ((fsobj_tag_t *)attrbufptr) + 1; + } + /* + * Note: ATTR_CMN_OBJID is lossy (only 32 bits). + */ + if ((ATTR_CMN_OBJID | ATTR_CMN_OBJPERMANENTID) & commonattr) { + u_int32_t fileid; + /* + * On Mac OS X we always export the root directory id as 2 + */ + fileid = (objnum == zfsvfs->z_root) ? 2 : objnum; + + if (ATTR_CMN_OBJID & commonattr) { + ((fsobj_id_t *)attrbufptr)->fid_objno = fileid; + ((fsobj_id_t *)attrbufptr)->fid_generation = 0; + attrbufptr = ((fsobj_id_t *)attrbufptr) + 1; + } + if (ATTR_CMN_OBJPERMANENTID & commonattr) { + ((fsobj_id_t *)attrbufptr)->fid_objno = fileid; + ((fsobj_id_t *)attrbufptr)->fid_generation = 0; + attrbufptr = ((fsobj_id_t *)attrbufptr) + 1; + } + } + /* + * Note: ATTR_CMN_PAROBJID is lossy (only 32 bits). + */ + if (ATTR_CMN_PAROBJID & commonattr) { + uint64_t parentid; + + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_PARENT(zfsvfs), + &parentid, sizeof (parentid)) == 0); + + /* + * On Mac OS X we always export the root + * directory id as 2 and its parent as 1 + */ + if (zp && zp->z_id == zfsvfs->z_root) + parentid = 1; + else if (parentid == zfsvfs->z_root) + parentid = 2; + + ASSERT(parentid != 0); + + ((fsobj_id_t *)attrbufptr)->fid_objno = (uint32_t)parentid; + ((fsobj_id_t *)attrbufptr)->fid_generation = 0; + attrbufptr = ((fsobj_id_t *)attrbufptr) + 1; + } + if (ATTR_CMN_SCRIPT & commonattr) { + *((text_encoding_t *)attrbufptr) = kTextEncodingMacUnicode; + attrbufptr = ((text_encoding_t *)attrbufptr) + 1; + } + if (ATTR_CMN_CRTIME & commonattr) { + uint64_t times[2]; + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_CRTIME(zfsvfs), + times, sizeof (times)) == 0); + if (user64) { + ZFS_TIME_DECODE((timespec_user64_t *)attrbufptr, + times); + attrbufptr = ((timespec_user64_t *)attrbufptr) + 1; + } else { + ZFS_TIME_DECODE((timespec_user32_t *)attrbufptr, + times); + attrbufptr = ((timespec_user32_t *)attrbufptr) + 1; + } + } + if (ATTR_CMN_MODTIME & commonattr) { + uint64_t times[2]; + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_MTIME(zfsvfs), + times, sizeof (times)) == 0); + if (user64) { + ZFS_TIME_DECODE((timespec_user64_t *)attrbufptr, + times); + attrbufptr = ((timespec_user64_t *)attrbufptr) + 1; + } else { + ZFS_TIME_DECODE((timespec_user32_t *)attrbufptr, + times); + attrbufptr = ((timespec_user32_t *)attrbufptr) + 1; + } + } + if (ATTR_CMN_CHGTIME & commonattr) { + uint64_t times[2]; + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_CTIME(zfsvfs), + times, sizeof (times)) == 0); + if (user64) { + ZFS_TIME_DECODE((timespec_user64_t *)attrbufptr, + times); + attrbufptr = ((timespec_user64_t *)attrbufptr) + 1; + } else { + ZFS_TIME_DECODE((timespec_user32_t *)attrbufptr, + times); + attrbufptr = ((timespec_user32_t *)attrbufptr) + 1; + } + } + if (ATTR_CMN_ACCTIME & commonattr) { + uint64_t times[2]; + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_ATIME(zfsvfs), + times, sizeof (times)) == 0); + if (user64) { + ZFS_TIME_DECODE((timespec_user64_t *)attrbufptr, + times); + attrbufptr = ((timespec_user64_t *)attrbufptr) + 1; + } else { + ZFS_TIME_DECODE((timespec_user32_t *)attrbufptr, + times); + attrbufptr = ((timespec_user32_t *)attrbufptr) + 1; + } + } + if (ATTR_CMN_BKUPTIME & commonattr) { + /* legacy attribute -- just pass zero */ + if (user64) { + ((timespec_user64_t *)attrbufptr)->tv_sec = 0; + ((timespec_user64_t *)attrbufptr)->tv_nsec = 0; + attrbufptr = ((timespec_user64_t *)attrbufptr) + 1; + } else { + ((timespec_user32_t *)attrbufptr)->tv_sec = 0; + ((timespec_user32_t *)attrbufptr)->tv_nsec = 0; + attrbufptr = ((timespec_user32_t *)attrbufptr) + 1; + } + } + if (ATTR_CMN_FNDRINFO & commonattr) { + uint64_t val; + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_FLAGS(zfsvfs), + &val, sizeof (val)) == 0); + getfinderinfo(zp, cr, &finderinfo); + /* Shadow ZFS_HIDDEN to Finder Info's invisible bit */ + if (val & ZFS_HIDDEN) { + finderinfo.fi_flags |= + OSSwapHostToBigConstInt16(kIsInvisible); + } + bcopy(&finderinfo, attrbufptr, sizeof (finderinfo)); + attrbufptr = (char *)attrbufptr + 32; + } + if (ATTR_CMN_OWNERID & commonattr) { + uint64_t val; + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_UID(zfsvfs), + &val, sizeof (val)) == 0); + *((uid_t *)attrbufptr) = val; + attrbufptr = ((uid_t *)attrbufptr) + 1; + } + if (ATTR_CMN_GRPID & commonattr) { + uint64_t val; + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_GID(zfsvfs), + &val, sizeof (val)) == 0); + *((gid_t *)attrbufptr) = val; + attrbufptr = ((gid_t *)attrbufptr) + 1; + } + if (ATTR_CMN_ACCESSMASK & commonattr) { + uint64_t val; + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_MODE(zfsvfs), + &val, sizeof (val)) == 0); + *((u_int32_t *)attrbufptr) = val; + attrbufptr = ((u_int32_t *)attrbufptr) + 1; + } + if (ATTR_CMN_FLAGS & commonattr) { + // TODO, sa_lookup of ZPL_FLAGS + u_int32_t flags = zfs_getbsdflags(zp); + + /* Shadow Finder Info's invisible bit to UF_HIDDEN */ + if ((ATTR_CMN_FNDRINFO & commonattr) && + (OSSwapBigToHostInt16(finderinfo.fi_flags) & kIsInvisible)) + flags |= UF_HIDDEN; + + *((u_int32_t *)attrbufptr) = flags; + attrbufptr = ((u_int32_t *)attrbufptr) + 1; + } + if (ATTR_CMN_USERACCESS & commonattr) { + u_int32_t user_access = 0; + uint64_t val; + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_FLAGS(zfsvfs), + &val, sizeof (val)) == 0); + + user_access = getuseraccess(zp, aip->ai_context); + + /* Also consider READ-ONLY file system. */ + if (vfs_flags(mp) & MNT_RDONLY) { + user_access &= ~W_OK; + } + + /* Locked objects are not writable either */ + if ((val & ZFS_IMMUTABLE) && + (vfs_context_suser(aip->ai_context) != 0)) { + user_access &= ~W_OK; + } + + *((u_int32_t *)attrbufptr) = user_access; + attrbufptr = ((u_int32_t *)attrbufptr) + 1; + } + if (ATTR_CMN_FILEID & commonattr) { + /* + * On Mac OS X we always export the root directory id as 2 + */ + if (objnum == zfsvfs->z_root) + objnum = 2; + + *((u_int64_t *)attrbufptr) = objnum; + attrbufptr = ((u_int64_t *)attrbufptr) + 1; + } + if (ATTR_CMN_PARENTID & commonattr) { + uint64_t parentid; + + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_PARENT(zfsvfs), + &parentid, sizeof (parentid)) == 0); + + /* + * On Mac OS X we always export the root + * directory id as 2 and its parent as 1 + */ + if (zp && zp->z_id == zfsvfs->z_root) + parentid = 1; + else if (parentid == zfsvfs->z_root) + parentid = 2; + + ASSERT(parentid != 0); + + *((u_int64_t *)attrbufptr) = parentid; + attrbufptr = ((u_int64_t *)attrbufptr) + 1; + } + + *aip->ai_attrbufpp = attrbufptr; + *aip->ai_varbufpp = varbufptr; +} + +void +dirattrpack(attrinfo_t *aip, znode_t *zp) +{ + attrgroup_t dirattr = aip->ai_attrlist->dirattr; + void *attrbufptr = *aip->ai_attrbufpp; + + if (ATTR_DIR_LINKCOUNT & dirattr) { + *((u_int32_t *)attrbufptr) = 1; /* no dir hard links */ + attrbufptr = ((u_int32_t *)attrbufptr) + 1; + } + if (ATTR_DIR_ENTRYCOUNT & dirattr) { + uint64_t val; + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_SIZE(zp->z_zfsvfs), + &val, sizeof (val)) == 0); + *((u_int32_t *)attrbufptr) = (uint32_t)val; + attrbufptr = ((u_int32_t *)attrbufptr) + 1; + } + if (ATTR_DIR_MOUNTSTATUS & dirattr && zp) { + vnode_t *vp = ZTOV(zp); + + if (vp != NULL && vnode_mountedhere(vp) != NULL) + *((u_int32_t *)attrbufptr) = DIR_MNTSTATUS_MNTPOINT; + else + *((u_int32_t *)attrbufptr) = 0; + attrbufptr = ((u_int32_t *)attrbufptr) + 1; + } + *aip->ai_attrbufpp = attrbufptr; +} + +void +fileattrpack(attrinfo_t *aip, zfsvfs_t *zfsvfs, znode_t *zp) +{ + attrgroup_t fileattr = aip->ai_attrlist->fileattr; + void *attrbufptr = *aip->ai_attrbufpp; + void *varbufptr = *aip->ai_varbufpp; + uint64_t allocsize = 0; + cred_t *cr = (cred_t *)vfs_context_ucred(aip->ai_context); + + if ((ATTR_FILE_ALLOCSIZE | ATTR_FILE_DATAALLOCSIZE) & fileattr && zp) { + uint32_t blksize; + u_longlong_t nblks; + + sa_object_size(zp->z_sa_hdl, &blksize, &nblks); + allocsize = (uint64_t)512LL * (uint64_t)nblks; + } + if (ATTR_FILE_LINKCOUNT & fileattr) { + uint64_t val; + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_LINKS(zfsvfs), + &val, sizeof (val)) == 0); + *((u_int32_t *)attrbufptr) = val; + attrbufptr = ((u_int32_t *)attrbufptr) + 1; + } + if (ATTR_FILE_TOTALSIZE & fileattr) { + uint64_t val; + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_SIZE(zfsvfs), + &val, sizeof (val)) == 0); + *((off_t *)attrbufptr) = val; + attrbufptr = ((off_t *)attrbufptr) + 1; + } + if (ATTR_FILE_ALLOCSIZE & fileattr) { + *((off_t *)attrbufptr) = allocsize; + attrbufptr = ((off_t *)attrbufptr) + 1; + } + if (ATTR_FILE_IOBLOCKSIZE & fileattr && zp) { + *((u_int32_t *)attrbufptr) = + zp->z_blksz ? zp->z_blksz : zfsvfs->z_max_blksz; + attrbufptr = ((u_int32_t *)attrbufptr) + 1; + } + if (ATTR_FILE_DEVTYPE & fileattr) { + uint64_t mode, val = 0; + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_MODE(zfsvfs), + &mode, sizeof (mode)) == 0); + sa_lookup(zp->z_sa_hdl, SA_ZPL_RDEV(zfsvfs), + &val, sizeof (val)); + if (S_ISBLK(mode) || S_ISCHR(mode)) + *((u_int32_t *)attrbufptr) = (u_int32_t)val; + else + *((u_int32_t *)attrbufptr) = 0; + attrbufptr = ((u_int32_t *)attrbufptr) + 1; + } + if (ATTR_FILE_DATALENGTH & fileattr) { + uint64_t val; + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_SIZE(zfsvfs), + &val, sizeof (val)) == 0); + *((off_t *)attrbufptr) = val; + attrbufptr = ((off_t *)attrbufptr) + 1; + } + if (ATTR_FILE_DATAALLOCSIZE & fileattr) { + *((off_t *)attrbufptr) = allocsize; + attrbufptr = ((off_t *)attrbufptr) + 1; + } + if ((ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE) & fileattr) { + uint64_t rsrcsize = 0; + uint64_t xattr; + + if (!sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zfsvfs), + &xattr, sizeof (xattr)) && + xattr) { + znode_t *xdzp = NULL, *xzp = NULL; + struct componentname cn = { 0 }; + char *name = NULL; + + name = spa_strdup(XATTR_RESOURCEFORK_NAME); + cn.cn_namelen = strlen(name)+1; + cn.cn_nameptr = kmem_zalloc(cn.cn_namelen, KM_SLEEP); + + /* Grab the hidden attribute directory vnode. */ + if (zfs_get_xattrdir(zp, &xdzp, cr, 0) == 0 && + zfs_dirlook(xdzp, name, &xzp, 0, NULL, + &cn) == 0) { + rsrcsize = xzp->z_size; + } + spa_strfree(name); + kmem_free(cn.cn_nameptr, cn.cn_namelen); + + if (xzp) + zrele(xzp); + if (xdzp) + zrele(xdzp); + } + if (ATTR_FILE_RSRCLENGTH & fileattr) { + *((off_t *)attrbufptr) = rsrcsize; + attrbufptr = ((off_t *)attrbufptr) + 1; + } + if (ATTR_FILE_RSRCALLOCSIZE & fileattr) { + *((off_t *)attrbufptr) = roundup(rsrcsize, 512); + attrbufptr = ((off_t *)attrbufptr) + 1; + } + } + *aip->ai_attrbufpp = attrbufptr; + *aip->ai_varbufpp = varbufptr; +} + +void +nameattrpack(attrinfo_t *aip, const char *name, int namelen) +{ + void *varbufptr; + struct attrreference *attr_refptr; + u_int32_t attrlen; + size_t nfdlen, freespace; + int force_formd_normalized_output; + + varbufptr = *aip->ai_varbufpp; + attr_refptr = (struct attrreference *)(*aip->ai_attrbufpp); + + freespace = (char *)aip->ai_varbufend - (char *)varbufptr; + /* + * Mac OS X: non-ascii names are UTF-8 NFC on disk + * so convert to NFD before exporting them. + */ + + if (zfs_vnop_force_formd_normalized_output && + !is_ascii_str(name)) + force_formd_normalized_output = 1; + else + force_formd_normalized_output = 0; + + namelen = strlen(name); + if (!force_formd_normalized_output || + utf8_normalizestr((const u_int8_t *)name, namelen, + (u_int8_t *)varbufptr, &nfdlen, + freespace, UTF_DECOMPOSED) != 0) { + /* ASCII or normalization failed, just copy zap name. */ + strncpy((char *)varbufptr, name, MIN(freespace, namelen+1)); + } else { + /* Normalization succeeded (already in buffer). */ + namelen = nfdlen; + } + attrlen = namelen + 1; + attr_refptr->attr_dataoffset = (char *)varbufptr - (char *)attr_refptr; + attr_refptr->attr_length = attrlen; + /* + * Advance beyond the space just allocated and + * round up to the next 4-byte boundary: + */ + varbufptr = ((char *)varbufptr) + attrlen + ((4 - (attrlen & 3)) & 3); + ++attr_refptr; + + *aip->ai_attrbufpp = attr_refptr; + *aip->ai_varbufpp = varbufptr; +} + +int +getpackedsize(struct attrlist *alp, boolean_t user64) +{ + attrgroup_t attrs; + int timespecsize; + int size = 0; + + timespecsize = user64 ? sizeof (timespec_user64_t) : + sizeof (timespec_user32_t); + + if ((attrs = alp->commonattr) != 0) { + if (attrs & ATTR_CMN_NAME) + size += sizeof (struct attrreference); + if (attrs & ATTR_CMN_DEVID) + size += sizeof (dev_t); + if (attrs & ATTR_CMN_FSID) + size += sizeof (fsid_t); + if (attrs & ATTR_CMN_OBJTYPE) + size += sizeof (fsobj_type_t); + if (attrs & ATTR_CMN_OBJTAG) + size += sizeof (fsobj_tag_t); + if (attrs & ATTR_CMN_OBJID) + size += sizeof (fsobj_id_t); + if (attrs & ATTR_CMN_OBJPERMANENTID) + size += sizeof (fsobj_id_t); + if (attrs & ATTR_CMN_PAROBJID) + size += sizeof (fsobj_id_t); + if (attrs & ATTR_CMN_SCRIPT) + size += sizeof (text_encoding_t); + if (attrs & ATTR_CMN_CRTIME) + size += timespecsize; + if (attrs & ATTR_CMN_MODTIME) + size += timespecsize; + if (attrs & ATTR_CMN_CHGTIME) + size += timespecsize; + if (attrs & ATTR_CMN_ACCTIME) + size += timespecsize; + if (attrs & ATTR_CMN_BKUPTIME) + size += timespecsize; + if (attrs & ATTR_CMN_FNDRINFO) + size += 32 * sizeof (u_int8_t); + if (attrs & ATTR_CMN_OWNERID) + size += sizeof (uid_t); + if (attrs & ATTR_CMN_GRPID) + size += sizeof (gid_t); + if (attrs & ATTR_CMN_ACCESSMASK) + size += sizeof (u_int32_t); + if (attrs & ATTR_CMN_FLAGS) + size += sizeof (u_int32_t); + if (attrs & ATTR_CMN_USERACCESS) + size += sizeof (u_int32_t); + if (attrs & ATTR_CMN_FILEID) + size += sizeof (u_int64_t); + if (attrs & ATTR_CMN_PARENTID) + size += sizeof (u_int64_t); + /* + * Also add: + * ATTR_CMN_GEN_COUNT (|FSOPT_ATTR_CMN_EXTENDED) + * ATTR_CMN_DOCUMENT_ID (|FSOPT_ATTR_CMN_EXTENDED) + * ATTR_CMN_EXTENDED_SECURITY + * ATTR_CMN_UUID + * ATTR_CMN_GRPUUID + * ATTR_CMN_FULLPATH + * ATTR_CMN_ADDEDTIME + * ATTR_CMN_ERROR + * ATTR_CMN_DATA_PROTECT_FLAGS + */ + } + if ((attrs = alp->dirattr) != 0) { + if (attrs & ATTR_DIR_LINKCOUNT) + size += sizeof (u_int32_t); + if (attrs & ATTR_DIR_ENTRYCOUNT) + size += sizeof (u_int32_t); + if (attrs & ATTR_DIR_MOUNTSTATUS) + size += sizeof (u_int32_t); + } + if ((attrs = alp->fileattr) != 0) { + if (attrs & ATTR_FILE_LINKCOUNT) + size += sizeof (u_int32_t); + if (attrs & ATTR_FILE_TOTALSIZE) + size += sizeof (off_t); + if (attrs & ATTR_FILE_ALLOCSIZE) + size += sizeof (off_t); + if (attrs & ATTR_FILE_IOBLOCKSIZE) + size += sizeof (u_int32_t); + if (attrs & ATTR_FILE_DEVTYPE) + size += sizeof (u_int32_t); + if (attrs & ATTR_FILE_DATALENGTH) + size += sizeof (off_t); + if (attrs & ATTR_FILE_DATAALLOCSIZE) + size += sizeof (off_t); + if (attrs & ATTR_FILE_RSRCLENGTH) + size += sizeof (off_t); + if (attrs & ATTR_FILE_RSRCALLOCSIZE) + size += sizeof (off_t); + } + return (size); +} + + +void +getfinderinfo(znode_t *zp, cred_t *cr, finderinfo_t *fip) +{ + znode_t *xdzp = NULL; + znode_t *xzp = NULL; + struct uio *auio = NULL; + struct componentname cn = { 0 }; + int error; + uint64_t xattr = 0; + char *name = NULL; + + if (sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zp->z_zfsvfs), + &xattr, sizeof (xattr)) || + (xattr == 0)) { + goto nodata; + } + + // Change this to internal uio ? + auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ); + if (auio == NULL) { + goto nodata; + } + uio_addiov(auio, CAST_USER_ADDR_T(fip), sizeof (finderinfo_t)); + + /* + * Grab the hidden attribute directory vnode. + * + * XXX - switch to embedded Finder Info when it becomes available + */ + if ((error = zfs_get_xattrdir(zp, &xdzp, cr, 0))) { + goto out; + } + + name = spa_strdup(XATTR_FINDERINFO_NAME); + cn.cn_namelen = strlen(name)+1; + cn.cn_nameptr = kmem_zalloc(cn.cn_namelen, KM_SLEEP); + + if ((error = zfs_dirlook(xdzp, name, &xzp, 0, NULL, &cn))) { + goto out; + } + + ZFS_UIO_INIT_XNU(uio, auio); + error = dmu_read_uio(zp->z_zfsvfs->z_os, xzp->z_id, uio, + sizeof (finderinfo_t)); +out: + if (name) + spa_strfree(name); + if (cn.cn_nameptr) + kmem_free(cn.cn_nameptr, cn.cn_namelen); + if (auio) + uio_free(auio); + if (xzp) + zrele(xzp); + if (xdzp) + zrele(xdzp); + if (error == 0) + return; +nodata: + bzero(fip, sizeof (finderinfo_t)); +} + +#define KAUTH_DIR_WRITE (KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | \ + KAUTH_VNODE_ADD_SUBDIRECTORY | \ + KAUTH_VNODE_DELETE_CHILD) + +#define KAUTH_DIR_READ (KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY) + +#define KAUTH_DIR_EXECUTE (KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH) + +#define KAUTH_FILE_WRITE (KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA) + +#define KAUTH_FILE_READ (KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA) + +#define KAUTH_FILE_EXECUTE (KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE) + +/* + * Compute the same user access value as getattrlist(2) + */ +u_int32_t +getuseraccess(znode_t *zp, vfs_context_t ctx) +{ + vnode_t *vp; + u_int32_t user_access = 0; + zfs_acl_phys_t acl_phys; + int error; + /* Only take the expensive vnode_authorize path when we have an ACL */ + + error = sa_lookup(zp->z_sa_hdl, SA_ZPL_ZNODE_ACL(zp->z_zfsvfs), + &acl_phys, sizeof (acl_phys)); + + if (error || acl_phys.z_acl_count == 0) { + kauth_cred_t cred = vfs_context_ucred(ctx); + uint64_t obj_uid; + uint64_t obj_mode; + + /* User id 0 (root) always gets access. */ + if (!vfs_context_suser(ctx)) { + return (R_OK | W_OK | X_OK); + } + + sa_lookup(zp->z_sa_hdl, SA_ZPL_UID(zp->z_zfsvfs), + &obj_uid, sizeof (obj_uid)); + sa_lookup(zp->z_sa_hdl, SA_ZPL_MODE(zp->z_zfsvfs), + &obj_mode, sizeof (obj_mode)); + + // obj_uid = pzp->zp_uid; + obj_mode = obj_mode & MODEMASK; + if (obj_uid == UNKNOWNUID) { + obj_uid = kauth_cred_getuid(cred); + } + if ((obj_uid == kauth_cred_getuid(cred)) || + (obj_uid == UNKNOWNUID)) { + return (((u_int32_t)obj_mode & S_IRWXU) >> 6); + } + /* Otherwise, settle for 'others' access. */ + return ((u_int32_t)obj_mode & S_IRWXO); + } + vp = ZTOV(zp); + if (vnode_isdir(vp)) { + if (vnode_authorize(vp, NULLVP, KAUTH_DIR_WRITE, ctx) == 0) + user_access |= W_OK; + if (vnode_authorize(vp, NULLVP, KAUTH_DIR_READ, ctx) == 0) + user_access |= R_OK; + if (vnode_authorize(vp, NULLVP, KAUTH_DIR_EXECUTE, ctx) == 0) + user_access |= X_OK; + } else { + if (vnode_authorize(vp, NULLVP, KAUTH_FILE_WRITE, ctx) == 0) + user_access |= W_OK; + if (vnode_authorize(vp, NULLVP, KAUTH_FILE_READ, ctx) == 0) + user_access |= R_OK; + if (vnode_authorize(vp, NULLVP, KAUTH_FILE_EXECUTE, ctx) == 0) + user_access |= X_OK; + } + return (user_access); +} + + + +static unsigned char fingerprint[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, + 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef}; + +/* + * Convert "Well Known" GUID to enum type. + */ +int +kauth_wellknown_guid(guid_t *guid) +{ + uint32_t last = 0; + + if (memcmp(fingerprint, guid->g_guid, sizeof (fingerprint))) + return (KAUTH_WKG_NOT); + + last = BE_32(*((u_int32_t *)&guid->g_guid[12])); + + switch (last) { + case 0x0c: + return (KAUTH_WKG_EVERYBODY); + case 0x0a: + return (KAUTH_WKG_OWNER); + case 0x10: + return (KAUTH_WKG_GROUP); + case 0xFFFFFFFE: + return (KAUTH_WKG_NOBODY); + } + + return (KAUTH_WKG_NOT); +} + + +/* + * Set GUID to "well known" guid, based on enum type + */ +void +nfsacl_set_wellknown(int wkg, guid_t *guid) +{ + /* + * All WKGs begin with the same 12 bytes. + */ + bcopy(fingerprint, (void *)guid, 12); + /* + * The final 4 bytes are our code (in network byte order). + */ + switch (wkg) { + case 4: + *((u_int32_t *)&guid->g_guid[12]) = BE_32(0x0000000c); + break; + case 3: + *((u_int32_t *)&guid->g_guid[12]) = BE_32(0xfffffffe); + break; + case 1: + *((u_int32_t *)&guid->g_guid[12]) = BE_32(0x0000000a); + break; + case 2: + *((u_int32_t *)&guid->g_guid[12]) = BE_32(0x00000010); + }; +} + + +/* + * Convert Darwin ACL list, into ZFS ACL "aces" list. + */ +void +aces_from_acl(ace_t *aces, int *nentries, struct kauth_acl *k_acl, + int *seen_type) +{ + int i; + ace_t *ace; + guid_t *guidp; + kauth_ace_rights_t ace_rights; + uid_t who; + uint32_t mask = 0; + uint16_t flags = 0; + uint16_t type = 0; + u_int32_t ace_flags; + int wkg; + int err = 0; + + *nentries = k_acl->acl_entrycount; + + // bzero(aces, sizeof (*aces) * *nentries); + + // *nentries = aclp->acl_cnt; + + for (i = 0; i < *nentries; i++) { + // entry = &(aclp->acl_entry[i]); + + flags = 0; + mask = 0; + + ace = &(aces[i]); + + /* Note Mac OS X GUID is a 128-bit identifier */ + guidp = &k_acl->acl_ace[i].ace_applicable; + + who = -1; + wkg = kauth_wellknown_guid(guidp); + + switch (wkg) { + case KAUTH_WKG_OWNER: + flags |= ACE_OWNER; + if (seen_type) *seen_type |= ACE_OWNER; + break; + case KAUTH_WKG_GROUP: + flags |= ACE_GROUP|ACE_IDENTIFIER_GROUP; + if (seen_type) *seen_type |= ACE_GROUP; + break; + case KAUTH_WKG_EVERYBODY: + flags |= ACE_EVERYONE; + if (seen_type) *seen_type |= ACE_EVERYONE; + break; + + case KAUTH_WKG_NOBODY: + default: + /* Try to get a uid from supplied guid */ + err = kauth_cred_guid2uid(guidp, &who); + if (err) { + err = kauth_cred_guid2gid(guidp, &who); + if (!err) { + flags |= ACE_IDENTIFIER_GROUP; + } + } + if (err) { + *nentries = 0; + return; + } + + } // switch + + ace->a_who = who; + + ace_rights = k_acl->acl_ace[i].ace_rights; + if (ace_rights & KAUTH_VNODE_READ_DATA) + mask |= ACE_READ_DATA; + if (ace_rights & KAUTH_VNODE_WRITE_DATA) + mask |= ACE_WRITE_DATA; + if (ace_rights & KAUTH_VNODE_APPEND_DATA) + mask |= ACE_APPEND_DATA; + if (ace_rights & KAUTH_VNODE_READ_EXTATTRIBUTES) + mask |= ACE_READ_NAMED_ATTRS; + if (ace_rights & KAUTH_VNODE_WRITE_EXTATTRIBUTES) + mask |= ACE_WRITE_NAMED_ATTRS; + if (ace_rights & KAUTH_VNODE_EXECUTE) + mask |= ACE_EXECUTE; + if (ace_rights & KAUTH_VNODE_DELETE_CHILD) + mask |= ACE_DELETE_CHILD; + if (ace_rights & KAUTH_VNODE_READ_ATTRIBUTES) + mask |= ACE_READ_ATTRIBUTES; + if (ace_rights & KAUTH_VNODE_WRITE_ATTRIBUTES) + mask |= ACE_WRITE_ATTRIBUTES; + if (ace_rights & KAUTH_VNODE_DELETE) + mask |= ACE_DELETE; + if (ace_rights & KAUTH_VNODE_READ_SECURITY) + mask |= ACE_READ_ACL; + if (ace_rights & KAUTH_VNODE_WRITE_SECURITY) + mask |= ACE_WRITE_ACL; + if (ace_rights & KAUTH_VNODE_TAKE_OWNERSHIP) + mask |= ACE_WRITE_OWNER; + if (ace_rights & KAUTH_VNODE_SYNCHRONIZE) + mask |= ACE_SYNCHRONIZE; + ace->a_access_mask = mask; + + ace_flags = k_acl->acl_ace[i].ace_flags; + if (ace_flags & KAUTH_ACE_FILE_INHERIT) + flags |= ACE_FILE_INHERIT_ACE; + if (ace_flags & KAUTH_ACE_DIRECTORY_INHERIT) + flags |= ACE_DIRECTORY_INHERIT_ACE; + if (ace_flags & KAUTH_ACE_LIMIT_INHERIT) + flags |= ACE_NO_PROPAGATE_INHERIT_ACE; + if (ace_flags & KAUTH_ACE_ONLY_INHERIT) + flags |= ACE_INHERIT_ONLY_ACE; + ace->a_flags = flags; + + switch (ace_flags & KAUTH_ACE_KINDMASK) { + case KAUTH_ACE_PERMIT: + type = ACE_ACCESS_ALLOWED_ACE_TYPE; + break; + case KAUTH_ACE_DENY: + type = ACE_ACCESS_DENIED_ACE_TYPE; + break; + case KAUTH_ACE_AUDIT: + type = ACE_SYSTEM_AUDIT_ACE_TYPE; + break; + case KAUTH_ACE_ALARM: + type = ACE_SYSTEM_ALARM_ACE_TYPE; + break; + } + ace->a_type = type; + dprintf(" ACL: %d type %04x, mask %04x, flags %04x, who %d\n", + i, type, mask, flags, who); + } + +} + +void +finderinfo_update(uint8_t *finderinfo, znode_t *zp) +{ + u_int8_t *finfo = NULL; + struct timespec va_crtime; + + /* Advance finfo by 16 bytes to the 2nd half of the finderinfo */ + finfo = (u_int8_t *)finderinfo + 16; + + /* Don't expose a symlink's private type/creator. */ + if (IFTOVT((mode_t)zp->z_mode) == VLNK) { + struct FndrFileInfo *fip; + + fip = (struct FndrFileInfo *)finderinfo; + fip->fdType = 0; + fip->fdCreator = 0; + } + + /* hfs_xattr.c hfs_zero_hidden_fields() */ + if ((IFTOVT((mode_t)zp->z_mode) == VREG) || + (IFTOVT((mode_t)zp->z_mode) == VLNK)) { + struct FndrExtendedFileInfo *extinfo = + (struct FndrExtendedFileInfo *)finfo; + extinfo->document_id = 0; + extinfo->date_added = 0; + extinfo->write_gen_counter = 0; + } + + if (IFTOVT((mode_t)zp->z_mode) == VDIR) { + struct FndrExtendedDirInfo *extinfo = + (struct FndrExtendedDirInfo *)finfo; + extinfo->document_id = 0; + extinfo->date_added = 0; + extinfo->write_gen_counter = 0; + } + +} + + + +int +zpl_xattr_set_sa(struct vnode *vp, const char *name, const void *value, + size_t size, int flags, cred_t *cr) +{ + znode_t *zp = VTOZ(vp); + nvlist_t *nvl; + size_t sa_size; + int error; + + ASSERT(zp->z_xattr_cached); + nvl = zp->z_xattr_cached; + + if (value == NULL) { + error = -nvlist_remove(nvl, name, DATA_TYPE_BYTE_ARRAY); + if (error == -ENOENT) + return (error); + // error = zpl_xattr_set_dir(vp, name, NULL, 0, flags, cr); + } else { + /* Limited to 32k to keep nvpair memory allocations small */ + if (size > DXATTR_MAX_ENTRY_SIZE) + return (-EFBIG); + + /* Prevent the DXATTR SA from consuming the entire SA region */ + error = -nvlist_size(nvl, &sa_size, NV_ENCODE_XDR); + if (error) + return (error); + + if (sa_size > DXATTR_MAX_SA_SIZE) + return (-EFBIG); + error = -nvlist_add_byte_array(nvl, name, + (uchar_t *)value, size); + if (error) + return (error); + } + + /* Update the SA for additions, modifications, and removals. */ + if (!error) + error = -zfs_sa_set_xattr(zp); + + ASSERT3S(error, <=, 0); + + return (error); +} + +int +zpl_xattr_get_sa(struct vnode *vp, const char *name, void *value, size_t size) +{ + znode_t *zp = VTOZ(vp); + uchar_t *nv_value; + uint_t nv_size; + int error = 0; + +#ifdef __LINUX__ + ASSERT(RW_LOCK_HELD(&zp->z_xattr_lock)); +#endif + + mutex_enter(&zp->z_lock); + if (zp->z_xattr_cached == NULL) + error = -zfs_sa_get_xattr(zp); + mutex_exit(&zp->z_lock); + + if (error) + return (error); + + ASSERT(zp->z_xattr_cached); + error = -nvlist_lookup_byte_array(zp->z_xattr_cached, name, + &nv_value, &nv_size); + if (error) + return (error); + + if (size == 0 || value == NULL) + return (nv_size); + if (size < nv_size) + return (-ERANGE); + + memcpy(value, nv_value, nv_size); + + return (nv_size); +} + + + +/* + * Document ID. Persistant IDs that can survive "safe saving". + * 'revisiond' appears to use fchflags(UF_TRACKED) on files/dirs + * that it wishes to use DocumentIDs with. Here, we will lookup + * if an entry already has a DocumentID stored in SA, but if not, + * hash the DocumentID for (PARENTID + filename) and return it. + * In vnop_setattr for UF_TRACKED, we will store the DocumentID to + * disk. + * Although it is not entirely clear which situations we should handle + * we do handle: + * + * Case 1: + * "file.txt" gets chflag(UF_TRACKED) and DocumentID set. + * "file.txt" is renamed to "file.tmp". DocumentID is kept. + * "file.txt" is re-created, DocumentID remains same, but not saved. + * + * Case 2: + * "file.txt" gets chflag(UF_TRACKED) and DocumentID set. + * "file.txt" is moved to another directory. DocumentID is kept. + * + * It is interesting to note that HFS+ has "tombstones" which is + * created when a UF_TRACKED entry is unlinked, or, renamed. + * Then if a new entry is created with same PARENT+name, and matching + * tombstone is found, will inherit the DocumentID, and UF_TRACKED flag. + * + * We may need to implement this as well. + * + * If "name" or "parent" is known, pass it along, or it needs to look it up. + * + */ +void +zfs_setattr_generate_id(znode_t *zp, uint64_t val, char *name) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + char *nameptr = NULL; + char *filename = NULL; + uint64_t parent = val; + int error = 0; + uint64_t docid = 0; + + if (!zp->z_document_id && zp->z_sa_hdl) { + + error = sa_lookup(zp->z_sa_hdl, SA_ZPL_DOCUMENTID(zfsvfs), + &docid, sizeof (docid)); + if (!error && docid) { + zp->z_document_id = docid; + return; + } + + /* Have name? */ + if (name && *name) { + nameptr = name; + } else { + /* Do we have parent? */ + if (!parent) { + VERIFY(sa_lookup(zp->z_sa_hdl, + SA_ZPL_PARENT(zfsvfs), &parent, + sizeof (parent)) == 0); + } + /* Lookup filename */ + filename = kmem_zalloc(MAXPATHLEN + 2, KM_SLEEP); + if (zap_value_search(zfsvfs->z_os, parent, zp->z_id, + ZFS_DIRENT_OBJ(-1ULL), filename) == 0) { + + nameptr = filename; + // Might as well keep this name too. + strlcpy(zp->z_name_cache, filename, + MAXPATHLEN); + } + } + + zp->z_document_id = fnv_32a_buf(&parent, sizeof (parent), + FNV1_32A_INIT); + if (nameptr) + zp->z_document_id = + fnv_32a_str(nameptr, zp->z_document_id); + + if (filename) + kmem_free(filename, MAXPATHLEN + 2); + } // !document_id +} + +/* + * setattr asked for UF_TRACKED to be set, which means we will make sure + * we have a hash made (includes getting filename) and stored in SA. + */ +int +zfs_setattr_set_documentid(znode_t *zp, boolean_t update_flags) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + int error = 0; + dmu_tx_t *tx; + int count = 0; + sa_bulk_attr_t bulk[2]; + + dprintf("ZFS: vnop_setattr(UF_TRACKED) obj %llu : documentid %08u\n", + zp->z_id, + zp->z_document_id); + + /* Write the new documentid to SA */ + if ((zfsvfs->z_use_sa == B_TRUE) && + !vfs_isrdonly(zfsvfs->z_vfs) && + spa_writeable(dmu_objset_spa(zfsvfs->z_os))) { + + uint64_t docid = zp->z_document_id; // 32->64 + + if (update_flags == B_TRUE) { + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), + NULL, &zp->z_pflags, 8); + } + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_DOCUMENTID(zfsvfs), NULL, + &docid, sizeof (docid)); + + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE); + + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + dmu_tx_abort(tx); + } else { + error = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx); + dmu_tx_commit(tx); + } + + if (error) + dprintf("ZFS: sa_update(SA_ZPL_DOCUMENTID) failed %d\n", + error); + + } // if z_use_sa && !readonly + + return (error); +} + +int +zfs_hardlink_addmap(znode_t *zp, uint64_t parentid, uint32_t linkid) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + hardlinks_t *searchnode, *findnode; + avl_index_t loc; + + searchnode = kmem_alloc(sizeof (hardlinks_t), KM_SLEEP); + searchnode->hl_parent = parentid; + searchnode->hl_fileid = zp->z_id; + strlcpy(searchnode->hl_name, zp->z_name_cache, PATH_MAX); + + rw_enter(&zfsvfs->z_hardlinks_lock, RW_WRITER); + findnode = avl_find(&zfsvfs->z_hardlinks, searchnode, &loc); + kmem_free(searchnode, sizeof (hardlinks_t)); + if (!findnode) { + // Add hash entry + zp->z_finder_hardlink = TRUE; + findnode = kmem_alloc(sizeof (hardlinks_t), KM_SLEEP); + + findnode->hl_parent = parentid; + findnode->hl_fileid = zp->z_id; + strlcpy(findnode->hl_name, zp->z_name_cache, PATH_MAX); + + findnode->hl_linkid = linkid; + + avl_add(&zfsvfs->z_hardlinks, findnode); + avl_add(&zfsvfs->z_hardlinks_linkid, findnode); + dprintf("ZFS: Inserted new hardlink node (%llu,%llu,'%s') " + "<-> (%x,%u)\n", + findnode->hl_parent, + findnode->hl_fileid, findnode->hl_name, + findnode->hl_linkid, findnode->hl_linkid); + } + rw_exit(&zfsvfs->z_hardlinks_lock); + + return (findnode ? 1 : 0); +} + +/* dst buffer must be at least UUID_PRINTABLE_STRING_LENGTH bytes */ + +int +zfs_vfs_uuid_unparse(uuid_t uuid, char *dst) +{ + if (!uuid || !dst) { + dprintf("%s missing argument\n", __func__); + return (EINVAL); + } + + snprintf(dst, UUID_PRINTABLE_STRING_LENGTH, "%02X%02X%02X%02X-" + "%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + + return (0); +} + +int +zfs_vfs_uuid_gen(const char *osname, uuid_t uuid) +{ + MD5_CTX md5c; + /* namespace (generated by uuidgen) */ + /* 50670853-FBD2-4EC3-9802-73D847BF7E62 */ + char namespace[16] = {0x50, 0x67, 0x08, 0x53, /* - */ + 0xfb, 0xd2, /* - */ 0x4e, 0xc3, /* - */ + 0x98, 0x02, /* - */ + 0x73, 0xd8, 0x47, 0xbf, 0x7e, 0x62}; + + /* Validate arguments */ + if (!osname || !uuid || strlen(osname) == 0) { + dprintf("%s missing argument\n", __func__); + return (EINVAL); + } + + /* + * UUID version 3 (MD5) namespace variant: + * hash namespace (uuid) together with name + */ + MD5Init(&md5c); + MD5Update(&md5c, &namespace, sizeof (namespace)); + MD5Update(&md5c, osname, strlen(osname)); + MD5Final(uuid, &md5c); + + /* + * To make UUID version 3, twiddle a few bits: + * xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx + * [uint32]-[uin-t32]-[uin-t32][uint32] + * M should be 0x3 to indicate uuid v3 + * N should be 0x8, 0x9, 0xa, or 0xb + */ + uuid[6] = (uuid[6] & 0x0F) | 0x30; + uuid[8] = (uuid[8] & 0x3F) | 0x80; + + /* Print all caps */ + // dprintf("%s UUIDgen: [%s](%ld)->" + dprintf("%s UUIDgen: [%s](%ld) -> " + "[%02X%02X%02X%02X-%02X%02X-%02X%02X-" + "%02X%02X-%02X%02X%02X%02X%02X%02X]\n", + __func__, osname, strlen(osname), + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + + return (0); +} + +int +uio_prefaultpages(ssize_t n, struct uio *uio) +{ + return (0); +} + +/* No #pragma weaks here! */ +void +dmu_buf_add_ref(dmu_buf_t *db, void *tag) +{ + dbuf_add_ref((dmu_buf_impl_t *)db, tag); +} + +boolean_t +dmu_buf_try_add_ref(dmu_buf_t *db, objset_t *os, uint64_t object, + uint64_t blkid, void *tag) +{ + return (dbuf_try_add_ref(db, os, object, blkid, tag)); +} diff --git a/module/os/macos/zfs/zfs_znode.c b/module/os/macos/zfs/zfs_znode.c new file mode 100644 index 000000000000..ad6c8e102841 --- /dev/null +++ b/module/os/macos/zfs/zfs_znode.c @@ -0,0 +1,2350 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Portions Copyright 2007-2009 Apple Inc. All rights reserved. + * Use is subject to license terms. + * Copyright (c) 2012, 2018 by Delphix. All rights reserved. + */ + +/* Portions Copyright 2007 Jeremy Teo */ +/* Portions Copyright 2011 Martin Matuska */ +/* Portions Copyright 2013 Jorgen Lundman */ + +#ifdef _KERNEL +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif /* _KERNEL */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zfs_prop.h" +#include "zfs_comutil.h" + +/* Used by fstat(1). */ +#ifndef __APPLE__ +SYSCTL_INT(_debug_sizeof, OID_AUTO, znode, CTLFLAG_RD, 0, sizeof (znode_t), + "sizeof (znode_t)"); +#endif +void +zfs_release_sa_handle(sa_handle_t *hdl, dmu_buf_t *db, void *tag); + +/* + * Functions needed for userland (ie: libzpool) are not put under + * #ifdef_KERNEL; the rest of the functions have dependencies + * (such as VFS logic) that will not compile easily in userland. + */ +#ifdef _KERNEL +/* + * This is used by the test suite so that it can delay znodes from being + * freed in order to inspect the unlinked set. + */ +int zfs_unlink_suspend_progress = 0; + +/* + * This callback is invoked when acquiring a RL_WRITER or RL_APPEND lock on + * z_rangelock. It will modify the offset and length of the lock to reflect + * znode-specific information, and convert RL_APPEND to RL_WRITER. This is + * called with the rangelock_t's rl_lock held, which avoids races. + */ + +kmem_cache_t *znode_cache = NULL; +static kmem_cache_t *znode_hold_cache = NULL; +unsigned int zfs_object_mutex_size = ZFS_OBJ_MTX_SZ; + +/* + * This callback is invoked when acquiring a RL_WRITER or RL_APPEND lock on + * z_rangelock. It will modify the offset and length of the lock to reflect + * znode-specific information, and convert RL_APPEND to RL_WRITER. This is + * called with the rangelock_t's rl_lock held, which avoids races. + */ +static void +zfs_rangelock_cb(zfs_locked_range_t *new, void *arg) +{ + znode_t *zp = arg; + + /* + * If in append mode, convert to writer and lock starting at the + * current end of file. + */ + if (new->lr_type == RL_APPEND) { + new->lr_offset = zp->z_size; + new->lr_type = RL_WRITER; + } + + /* + * If we need to grow the block size then lock the whole file range. + */ + uint64_t end_size = MAX(zp->z_size, new->lr_offset + new->lr_length); + if (end_size > zp->z_blksz && (!ISP2(zp->z_blksz) || + zp->z_blksz < zp->z_zfsvfs->z_max_blksz)) { + new->lr_offset = 0; + new->lr_length = UINT64_MAX; + } +} + +/*ARGSUSED*/ +#if 0 // unused function +static void +znode_evict_error(dmu_buf_t *dbuf, void *user_ptr) +{ + /* + * We should never drop all dbuf refs without first clearing + * the eviction callback. + */ + panic("evicting znode %p\n", user_ptr); +} +#endif + +extern struct vop_vector zfs_vnodeops; +extern struct vop_vector zfs_fifoops; +extern struct vop_vector zfs_shareops; + +/* + * XXX: We cannot use this function as a cache constructor, because + * there is one global cache for all file systems and we need + * to pass vfsp here, which is not possible, because argument + * 'cdrarg' is defined at kmem_cache_create() time. + */ +/*ARGSUSED*/ +static int +zfs_znode_cache_constructor(void *buf, void *arg, int kmflags) +{ + znode_t *zp = buf; + + list_link_init(&zp->z_link_node); + + mutex_init(&zp->z_lock, NULL, MUTEX_DEFAULT, NULL); + rw_init(&zp->z_map_lock, NULL, RW_DEFAULT, NULL); + rw_init(&zp->z_parent_lock, NULL, RW_DEFAULT, NULL); + rw_init(&zp->z_name_lock, NULL, RW_DEFAULT, NULL); + mutex_init(&zp->z_acl_lock, NULL, MUTEX_DEFAULT, NULL); + rw_init(&zp->z_xattr_lock, NULL, RW_DEFAULT, NULL); + zfs_rangelock_init(&zp->z_rangelock, zfs_rangelock_cb, zp); + + mutex_init(&zp->z_attach_lock, NULL, MUTEX_DEFAULT, NULL); + cv_init(&zp->z_attach_cv, NULL, CV_DEFAULT, NULL); + + zp->z_dirlocks = NULL; + zp->z_acl_cached = NULL; + zp->z_xattr_cached = NULL; + zp->z_xattr_parent = 0; + zp->z_skip_truncate_undo_decmpfs = B_FALSE; + return (0); +} + +/*ARGSUSED*/ +static void +zfs_znode_cache_destructor(void *buf, void *arg) +{ + znode_t *zp = buf; + + ASSERT(ZTOV(zp) == NULL); + ASSERT(!list_link_active(&zp->z_link_node)); + mutex_destroy(&zp->z_lock); + rw_destroy(&zp->z_map_lock); + rw_destroy(&zp->z_parent_lock); + rw_destroy(&zp->z_name_lock); + mutex_destroy(&zp->z_acl_lock); + rw_destroy(&zp->z_xattr_lock); + zfs_rangelock_fini(&zp->z_rangelock); + mutex_destroy(&zp->z_attach_lock); + cv_destroy(&zp->z_attach_cv); + + ASSERT(zp->z_dirlocks == NULL); + ASSERT(zp->z_acl_cached == NULL); + ASSERT(zp->z_xattr_cached == NULL); +} + +static int +zfs_znode_hold_cache_constructor(void *buf, void *arg, int kmflags) +{ + znode_hold_t *zh = buf; + + mutex_init(&zh->zh_lock, NULL, MUTEX_DEFAULT, NULL); + zfs_refcount_create(&zh->zh_refcount); + zh->zh_obj = ZFS_NO_OBJECT; + + return (0); +} + +static void +zfs_znode_hold_cache_destructor(void *buf, void *arg) +{ + znode_hold_t *zh = buf; + + mutex_destroy(&zh->zh_lock); + zfs_refcount_destroy(&zh->zh_refcount); +} + +void +zfs_znode_init(void) +{ + /* + * Initialize zcache. The KMC_SLAB hint is used in order that it be + * backed by kmalloc() when on the Linux slab in order that any + * wait_on_bit() operations on the related inode operate properly. + */ + ASSERT(znode_cache == NULL); + znode_cache = kmem_cache_create("zfs_znode_cache", + sizeof (znode_t), 0, + zfs_znode_cache_constructor, + zfs_znode_cache_destructor, NULL, NULL, + NULL, 0); + + ASSERT(znode_hold_cache == NULL); + znode_hold_cache = kmem_cache_create("zfs_znode_hold_cache", + sizeof (znode_hold_t), 0, zfs_znode_hold_cache_constructor, + zfs_znode_hold_cache_destructor, NULL, NULL, NULL, 0); +} + +void +zfs_znode_fini(void) +{ + /* + * Cleanup zcache + */ + if (znode_cache) + kmem_cache_destroy(znode_cache); + znode_cache = NULL; + + if (znode_hold_cache) + kmem_cache_destroy(znode_hold_cache); + znode_hold_cache = NULL; +} + +/* + * The zfs_znode_hold_enter() / zfs_znode_hold_exit() functions are used to + * serialize access to a znode and its SA buffer while the object is being + * created or destroyed. This kind of locking would normally reside in the + * znode itself but in this case that's impossible because the znode and SA + * buffer may not yet exist. Therefore the locking is handled externally + * with an array of mutexs and AVLs trees which contain per-object locks. + * + * In zfs_znode_hold_enter() a per-object lock is created as needed, inserted + * in to the correct AVL tree and finally the per-object lock is held. In + * zfs_znode_hold_exit() the process is reversed. The per-object lock is + * released, removed from the AVL tree and destroyed if there are no waiters. + * + * This scheme has two important properties: + * + * 1) No memory allocations are performed while holding one of the z_hold_locks. + * This ensures evict(), which can be called from direct memory reclaim, will + * never block waiting on a z_hold_locks which just happens to have hashed + * to the same index. + * + * 2) All locks used to serialize access to an object are per-object and never + * shared. This minimizes lock contention without creating a large number + * of dedicated locks. + * + * On the downside it does require znode_lock_t structures to be frequently + * allocated and freed. However, because these are backed by a kmem cache + * and very short lived this cost is minimal. + */ +int +zfs_znode_hold_compare(const void *a, const void *b) +{ + const znode_hold_t *zh_a = (const znode_hold_t *)a; + const znode_hold_t *zh_b = (const znode_hold_t *)b; + + return (TREE_CMP(zh_a->zh_obj, zh_b->zh_obj)); +} + +boolean_t +zfs_znode_held(zfsvfs_t *zfsvfs, uint64_t obj) +{ + znode_hold_t *zh, search; + int i = ZFS_OBJ_HASH(zfsvfs, obj); + boolean_t held; + + search.zh_obj = obj; + + mutex_enter(&zfsvfs->z_hold_locks[i]); + zh = avl_find(&zfsvfs->z_hold_trees[i], &search, NULL); + held = (zh && MUTEX_HELD(&zh->zh_lock)) ? B_TRUE : B_FALSE; + mutex_exit(&zfsvfs->z_hold_locks[i]); + + return (held); +} + +static znode_hold_t * +zfs_znode_hold_enter(zfsvfs_t *zfsvfs, uint64_t obj) +{ + znode_hold_t *zh, *zh_new, search; + int i = ZFS_OBJ_HASH(zfsvfs, obj); + boolean_t found = B_FALSE; + + zh_new = kmem_cache_alloc(znode_hold_cache, KM_SLEEP); + zh_new->zh_obj = obj; + search.zh_obj = obj; + + mutex_enter(&zfsvfs->z_hold_locks[i]); + zh = avl_find(&zfsvfs->z_hold_trees[i], &search, NULL); + if (likely(zh == NULL)) { + zh = zh_new; + avl_add(&zfsvfs->z_hold_trees[i], zh); + } else { + ASSERT3U(zh->zh_obj, ==, obj); + found = B_TRUE; + } + zfs_refcount_add(&zh->zh_refcount, NULL); + mutex_exit(&zfsvfs->z_hold_locks[i]); + + if (found == B_TRUE) + kmem_cache_free(znode_hold_cache, zh_new); + + ASSERT(MUTEX_NOT_HELD(&zh->zh_lock)); + ASSERT3S(zfs_refcount_count(&zh->zh_refcount), >, 0); + mutex_enter(&zh->zh_lock); + + return (zh); +} + +static void +zfs_znode_hold_exit(zfsvfs_t *zfsvfs, znode_hold_t *zh) +{ + int i = ZFS_OBJ_HASH(zfsvfs, zh->zh_obj); + boolean_t remove = B_FALSE; + + ASSERT(zfs_znode_held(zfsvfs, zh->zh_obj)); + ASSERT3S(zfs_refcount_count(&zh->zh_refcount), >, 0); + mutex_exit(&zh->zh_lock); + + mutex_enter(&zfsvfs->z_hold_locks[i]); + if (zfs_refcount_remove(&zh->zh_refcount, NULL) == 0) { + avl_remove(&zfsvfs->z_hold_trees[i], zh); + remove = B_TRUE; + } + mutex_exit(&zfsvfs->z_hold_locks[i]); + + if (remove == B_TRUE) + kmem_cache_free(znode_hold_cache, zh); +} + +int +zfs_create_share_dir(zfsvfs_t *zfsvfs, dmu_tx_t *tx) +{ + int error = 0; +#if 0 // FIXME, uses vnode struct, not ptr + zfs_acl_ids_t acl_ids; + vattr_t vattr; + znode_t *sharezp; + struct vnode *vp, *vnode; + znode_t *zp; + + vattr.va_mask = ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_TYPE; + vattr.va_type = VDIR; + vattr.va_mode = S_IFDIR|0555; + vattr.va_uid = crgetuid(kcred); + vattr.va_gid = crgetgid(kcred); + + sharezp = kmem_cache_alloc(znode_cache, KM_SLEEP); + sharezp->z_unlinked = 0; + sharezp->z_atime_dirty = 0; + sharezp->z_zfsvfs = zfsvfs; + sharezp->z_is_sa = zfsvfs->z_use_sa; + + sharezp->z_vnode = vnode; + vnode.v_data = sharezp; + + vp = ZTOV(sharezp); + vp->v_type = VDIR; + + VERIFY(0 == zfs_acl_ids_create(sharezp, IS_ROOT_NODE, &vattr, + kcred, NULL, &acl_ids)); + zfs_mknode(sharezp, &vattr, tx, kcred, IS_ROOT_NODE, &zp, &acl_ids); + ASSERT3P(zp, ==, sharezp); + POINTER_INVALIDATE(&sharezp->z_zfsvfs); + error = zap_add(zfsvfs->z_os, MASTER_NODE_OBJ, + ZFS_SHARES_DIR, 8, 1, &sharezp->z_id, tx); + zfsvfs->z_shares_dir = sharezp->z_id; + + zfs_acl_ids_free(&acl_ids); + ZTOV(sharezp)->v_data = NULL; + ZTOV(sharezp)->v_count = 0; + ZTOV(sharezp)->v_holdcnt = 0; + zp->z_vnode = NULL; + sa_handle_destroy(sharezp->z_sa_hdl); + sharezp->z_vnode = NULL; + kmem_cache_free(znode_cache, sharezp); +#endif + return (error); +} + +/* + * define a couple of values we need available + * for both 64 and 32 bit environments. + */ +#ifndef NBITSMINOR64 +#define NBITSMINOR64 32 +#endif +#ifndef MAXMAJ64 +#define MAXMAJ64 0xffffffffUL +#endif +#ifndef MAXMIN64 +#define MAXMIN64 0xffffffffUL +#endif + +/* + * Create special expldev for ZFS private use. + * Can't use standard expldev since it doesn't do + * what we want. The standard expldev() takes a + * dev32_t in LP64 and expands it to a long dev_t. + * We need an interface that takes a dev32_t in ILP32 + * and expands it to a long dev_t. + */ +static uint64_t +zfs_expldev(dev_t dev) +{ + return (((uint64_t)major(dev) << NBITSMINOR64) | minor(dev)); +} +/* + * Special cmpldev for ZFS private use. + * Can't use standard cmpldev since it takes + * a long dev_t and compresses it to dev32_t in + * LP64. We need to do a compaction of a long dev_t + * to a dev32_t in ILP32. + */ +dev_t +zfs_cmpldev(uint64_t dev) +{ + return (makedev((dev >> NBITSMINOR64), (dev & MAXMIN64))); +} + +static void +zfs_znode_sa_init(zfsvfs_t *zfsvfs, znode_t *zp, + dmu_buf_t *db, dmu_object_type_t obj_type, + sa_handle_t *sa_hdl) +{ + ASSERT(zfs_znode_held(zfsvfs, zp->z_id)); + + mutex_enter(&zp->z_lock); + + ASSERT(zp->z_sa_hdl == NULL); + ASSERT(zp->z_acl_cached == NULL); + if (sa_hdl == NULL) { + VERIFY(0 == sa_handle_get_from_db(zfsvfs->z_os, db, zp, + SA_HDL_SHARED, &zp->z_sa_hdl)); + } else { + zp->z_sa_hdl = sa_hdl; + sa_set_userp(sa_hdl, zp); + } + + zp->z_is_sa = (obj_type == DMU_OT_SA) ? B_TRUE : B_FALSE; + + mutex_exit(&zp->z_lock); +} + +void +zfs_znode_dmu_fini(znode_t *zp) +{ + ASSERT(zfs_znode_held(ZTOZSB(zp), zp->z_id) || zp->z_unlinked || + RW_WRITE_HELD(&ZTOZSB(zp)->z_teardown_inactive_lock)); + + sa_handle_destroy(zp->z_sa_hdl); + zp->z_sa_hdl = NULL; +} + +#if 0 // Until we need it ? +static void +zfs_vnode_destroy(struct vnode *vp) +{ + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = ZTOZSB(zp); + + if (vp != NULL) { + znode_t *zp = VTOZ(vp); + + if (zp != NULL) { + mutex_enter(&zfsvfs->z_znodes_lock); + if (list_link_active(&zp->z_link_node)) { + list_remove(&zfsvfs->z_all_znodes, zp); + } + mutex_exit(&zfsvfs->z_znodes_lock); + + if (zp->z_acl_cached) { + zfs_acl_free(zp->z_acl_cached); + zp->z_acl_cached = NULL; + } + + if (zp->z_xattr_cached) { + nvlist_free(zp->z_xattr_cached); + zp->z_xattr_cached = NULL; + } + + kmem_cache_free(znode_cache, zp); + } + + vnode_clearfsnode(vp); + vnode_put(vp); + vnode_recycle(vp); + } +} +#endif + +/* + * Construct a new znode/vnode and intialize. + * + * This does not do a call to dmu_set_user() that is + * up to the caller to do, in case you don't want to + * return the znode + */ +static znode_t * +zfs_znode_alloc(zfsvfs_t *zfsvfs, dmu_buf_t *db, int blksz, + dmu_object_type_t obj_type, sa_handle_t *hdl) +{ + znode_t *zp; + struct vnode *vp; + uint64_t mode; + uint64_t parent; + sa_bulk_attr_t bulk[11]; + int count = 0; + uint64_t projid = ZFS_DEFAULT_PROJID; + + zp = kmem_cache_alloc(znode_cache, KM_SLEEP); + + ASSERT(zp->z_dirlocks == NULL); + ASSERT(!POINTER_IS_VALID(zp->z_zfsvfs)); + + /* + * Defer setting z_zfsvfs until the znode is ready to be a candidate for + * the zfs_znode_move() callback. + */ + zp->z_vnode = NULL; + zp->z_sa_hdl = NULL; + zp->z_unlinked = 0; + zp->z_atime_dirty = 0; + zp->z_mapcnt = 0; + zp->z_id = db->db_object; + zp->z_blksz = blksz; + zp->z_seq = 0x7A4653; + zp->z_sync_cnt = 0; + + zp->z_is_mapped = 0; + zp->z_is_ctldir = 0; + zp->z_vid = 0; + zp->z_uid = 0; + zp->z_gid = 0; + zp->z_size = 0; + zp->z_name_cache[0] = 0; + zp->z_finder_parentid = 0; + zp->z_finder_hardlink = FALSE; + + taskq_init_ent(&zp->z_attach_taskq); + + vp = ZTOV(zp); /* Does nothing in OSX */ + + zfs_znode_sa_init(zfsvfs, zp, db, obj_type, hdl); + + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MODE(zfsvfs), NULL, &mode, 8); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_GEN(zfsvfs), NULL, &zp->z_gen, 8); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_SIZE(zfsvfs), NULL, + &zp->z_size, 8); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_LINKS(zfsvfs), NULL, + &zp->z_links, 8); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL, + &zp->z_pflags, 8); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_PARENT(zfsvfs), NULL, &parent, 8); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_ATIME(zfsvfs), NULL, + &zp->z_atime, 16); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_UID(zfsvfs), NULL, + &zp->z_uid, 8); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_GID(zfsvfs), NULL, + &zp->z_gid, 8); + + if (sa_bulk_lookup(zp->z_sa_hdl, bulk, count) != 0 || zp->z_gen == 0 || + (dmu_objset_projectquota_enabled(zfsvfs->z_os) && + (zp->z_pflags & ZFS_PROJID) && + sa_lookup(zp->z_sa_hdl, SA_ZPL_PROJID(zfsvfs), &projid, 8) != 0)) { + if (hdl == NULL) + sa_handle_destroy(zp->z_sa_hdl); + zp->z_sa_hdl = NULL; + printf("znode_alloc: sa_bulk_lookup failed - aborting\n"); + kmem_cache_free(znode_cache, zp); + return (NULL); + } + + zp->z_projid = projid; + zp->z_mode = mode; + + mutex_enter(&zfsvfs->z_znodes_lock); + list_insert_tail(&zfsvfs->z_all_znodes, zp); + membar_producer(); + /* + * Everything else must be valid before assigning z_zfsvfs makes the + * znode eligible for zfs_znode_move(). + */ + zp->z_zfsvfs = zfsvfs; + mutex_exit(&zfsvfs->z_znodes_lock); + + return (zp); +} + + +static uint64_t empty_xattr; +static uint64_t pad[4]; +static zfs_acl_phys_t acl_phys; +/* + * Create a new DMU object to hold a zfs znode. + * + * IN: dzp - parent directory for new znode + * vap - file attributes for new znode + * tx - dmu transaction id for zap operations + * cr - credentials of caller + * flag - flags: + * IS_ROOT_NODE - new object will be root + * IS_XATTR - new object is an attribute + * bonuslen - length of bonus buffer + * setaclp - File/Dir initial ACL + * fuidp - Tracks fuid allocation. + * + * OUT: zpp - allocated znode + * + * OS X implementation notes: + * + * The caller of zfs_mknode() is expected to call zfs_znode_getvnode() + * AFTER the dmu_tx_commit() is performed. This prevents deadlocks + * since vnode_create can indirectly attempt to clean a dirty vnode. + * + * The current list of callers includes: + * zfs_vnop_create + * zfs_vnop_mkdir + * zfs_vnop_symlink + * zfs_obtain_xattr + * zfs_make_xattrdir + */ +void +zfs_mknode(znode_t *dzp, vattr_t *vap, dmu_tx_t *tx, cred_t *cr, + uint_t flag, znode_t **zpp, zfs_acl_ids_t *acl_ids) +{ + uint64_t crtime[2], atime[2], mtime[2], ctime[2]; + uint64_t mode, size, links, parent, pflags; + uint64_t projid = ZFS_DEFAULT_PROJID; + uint64_t dzp_pflags = 0; + uint64_t rdev = 0; + zfsvfs_t *zfsvfs = dzp->z_zfsvfs; + dmu_buf_t *db; + timestruc_t now; + uint64_t gen, obj; + int bonuslen; + int dnodesize; + sa_handle_t *sa_hdl; + dmu_object_type_t obj_type; + sa_bulk_attr_t *sa_attrs; + int cnt = 0; + zfs_acl_locator_cb_t locate = { 0 }; + int err = 0; + znode_hold_t *zh; + + ASSERT(vap && (vap->va_mask & (ATTR_TYPE|ATTR_MODE)) == + (ATTR_TYPE|ATTR_MODE)); + + if (zfsvfs->z_replay) { + obj = vap->va_nodeid; + now = vap->va_ctime; /* see zfs_replay_create() */ + gen = vap->va_nblocks; /* ditto */ + dnodesize = vap->va_fsid; /* ditto */ + } else { + obj = 0; + gethrestime(&now); + gen = dmu_tx_get_txg(tx); + dnodesize = dmu_objset_dnodesize(zfsvfs->z_os); + } + + if (dnodesize == 0) + dnodesize = DNODE_MIN_SIZE; + + obj_type = zfsvfs->z_use_sa ? DMU_OT_SA : DMU_OT_ZNODE; + bonuslen = (obj_type == DMU_OT_SA) ? + DN_BONUS_SIZE(dnodesize) : ZFS_OLD_ZNODE_PHYS_SIZE; + + /* + * Create a new DMU object. + */ + /* + * There's currently no mechanism for pre-reading the blocks that will + * be needed to allocate a new object, so we accept the small chance + * that there will be an i/o error and we will fail one of the + * assertions below. + */ + if (vap->va_type == VDIR) { + if (zfsvfs->z_replay) { + VERIFY0(zap_create_claim_norm_dnsize(zfsvfs->z_os, obj, + zfsvfs->z_norm, DMU_OT_DIRECTORY_CONTENTS, + obj_type, bonuslen, dnodesize, tx)); + } else { + obj = zap_create_norm_dnsize(zfsvfs->z_os, + zfsvfs->z_norm, DMU_OT_DIRECTORY_CONTENTS, + obj_type, bonuslen, dnodesize, tx); + } + } else { + if (zfsvfs->z_replay) { + VERIFY0(dmu_object_claim_dnsize(zfsvfs->z_os, obj, + DMU_OT_PLAIN_FILE_CONTENTS, 0, + obj_type, bonuslen, dnodesize, tx)); + } else { + obj = dmu_object_alloc_dnsize(zfsvfs->z_os, + DMU_OT_PLAIN_FILE_CONTENTS, 0, + obj_type, bonuslen, dnodesize, tx); + } + } + + zh = zfs_znode_hold_enter(zfsvfs, obj); + VERIFY0(sa_buf_hold(zfsvfs->z_os, obj, NULL, &db)); + + /* + * If this is the root, fix up the half-initialized parent pointer + * to reference the just-allocated physical data area. + */ + if (flag & IS_ROOT_NODE) { + dzp->z_id = obj; + } else { + dzp_pflags = dzp->z_pflags; + } + + /* + * If parent is an xattr, so am I. + */ + if (dzp_pflags & ZFS_XATTR) { + flag |= IS_XATTR; + } + + if (zfsvfs->z_use_fuids) + pflags = ZFS_ARCHIVE | ZFS_AV_MODIFIED; + else + pflags = 0; + + if (vap->va_type == VDIR) { + size = 2; /* contents ("." and "..") */ + links = (flag & (IS_ROOT_NODE | IS_XATTR)) ? 2 : 1; + } else { + size = links = 0; + } + + if (vap->va_type == VBLK || vap->va_type == VCHR) { + rdev = zfs_expldev(vap->va_rdev); + } + + parent = dzp->z_id; + mode = acl_ids->z_mode; + if (flag & IS_XATTR) + pflags |= ZFS_XATTR; + + if (S_ISREG(vap->va_mode) || S_ISDIR(vap->va_mode)) { + /* + * With ZFS_PROJID flag, we can easily know whether there is + * project ID stored on disk or not. See zfs_space_delta_cb(). + */ + if (obj_type != DMU_OT_ZNODE && + dmu_objset_projectquota_enabled(zfsvfs->z_os)) + pflags |= ZFS_PROJID; + + /* + * Inherit project ID from parent if required. + */ + projid = zfs_inherit_projid(dzp); + if (dzp->z_pflags & ZFS_PROJINHERIT) + pflags |= ZFS_PROJINHERIT; + } + + /* + * No execs denied will be deterimed when zfs_mode_compute() is called. + */ + pflags |= acl_ids->z_aclp->z_hints & + (ZFS_ACL_TRIVIAL|ZFS_INHERIT_ACE|ZFS_ACL_AUTO_INHERIT| + ZFS_ACL_DEFAULTED|ZFS_ACL_PROTECTED); + + ZFS_TIME_ENCODE(&now, crtime); + ZFS_TIME_ENCODE(&now, ctime); + + if (vap->va_mask & ATTR_ATIME) { + ZFS_TIME_ENCODE(&vap->va_atime, atime); + } else { + ZFS_TIME_ENCODE(&now, atime); + } + + if (vap->va_mask & ATTR_MTIME) { + ZFS_TIME_ENCODE(&vap->va_mtime, mtime); + } else { + ZFS_TIME_ENCODE(&now, mtime); + } + + /* Now add in all of the "SA" attributes */ + VERIFY(0 == sa_handle_get_from_db(zfsvfs->z_os, db, NULL, SA_HDL_SHARED, + &sa_hdl)); + + /* + * Setup the array of attributes to be replaced/set on the new file + * + * order for DMU_OT_ZNODE is critical since it needs to be constructed + * in the old znode_phys_t format. Don't change this ordering + */ + sa_attrs = kmem_alloc(sizeof (sa_bulk_attr_t) * ZPL_END, KM_SLEEP); + + if (obj_type == DMU_OT_ZNODE) { + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_ATIME(zfsvfs), + NULL, &atime, 16); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_MTIME(zfsvfs), + NULL, &mtime, 16); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_CTIME(zfsvfs), + NULL, &ctime, 16); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_CRTIME(zfsvfs), + NULL, &crtime, 16); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_GEN(zfsvfs), + NULL, &gen, 8); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_MODE(zfsvfs), + NULL, &mode, 8); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_SIZE(zfsvfs), + NULL, &size, 8); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_PARENT(zfsvfs), + NULL, &parent, 8); + } else { + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_MODE(zfsvfs), + NULL, &mode, 8); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_SIZE(zfsvfs), + NULL, &size, 8); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_GEN(zfsvfs), + NULL, &gen, 8); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_UID(zfsvfs), + NULL, &acl_ids->z_fuid, 8); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_GID(zfsvfs), + NULL, &acl_ids->z_fgid, 8); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_PARENT(zfsvfs), + NULL, &parent, 8); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_FLAGS(zfsvfs), + NULL, &pflags, 8); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_ATIME(zfsvfs), + NULL, &atime, 16); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_MTIME(zfsvfs), + NULL, &mtime, 16); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_CTIME(zfsvfs), + NULL, &ctime, 16); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_CRTIME(zfsvfs), + NULL, &crtime, 16); + } + + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_LINKS(zfsvfs), NULL, &links, 8); + + if (obj_type == DMU_OT_ZNODE) { + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_XATTR(zfsvfs), NULL, + &empty_xattr, 8); + } else if (dmu_objset_projectquota_enabled(zfsvfs->z_os) && + pflags & ZFS_PROJID) { + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_PROJID(zfsvfs), + NULL, &projid, 8); + } + if (obj_type == DMU_OT_ZNODE || + (vap->va_type == VBLK || vap->va_type == VCHR)) { + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_RDEV(zfsvfs), + NULL, &rdev, 8); + + } + if (obj_type == DMU_OT_ZNODE) { + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_FLAGS(zfsvfs), + NULL, &pflags, 8); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_UID(zfsvfs), NULL, + &acl_ids->z_fuid, 8); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_GID(zfsvfs), NULL, + &acl_ids->z_fgid, 8); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_PAD(zfsvfs), NULL, pad, + sizeof (uint64_t) * 4); + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_ZNODE_ACL(zfsvfs), NULL, + &acl_phys, sizeof (zfs_acl_phys_t)); + } else if (acl_ids->z_aclp->z_version >= ZFS_ACL_VERSION_FUID) { + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_DACL_COUNT(zfsvfs), NULL, + &acl_ids->z_aclp->z_acl_count, 8); + locate.cb_aclp = acl_ids->z_aclp; + SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_DACL_ACES(zfsvfs), + zfs_acl_data_locator, &locate, + acl_ids->z_aclp->z_acl_bytes); + mode = zfs_mode_compute(mode, acl_ids->z_aclp, &pflags, + acl_ids->z_fuid, acl_ids->z_fgid); + } + + VERIFY(sa_replace_all_by_template(sa_hdl, sa_attrs, cnt, tx) == 0); + + if (!(flag & IS_ROOT_NODE)) { + /* + * We must not hold any locks while calling vnode_create inside + * zfs_znode_alloc(), as it may call either of vnop_reclaim, or + * vnop_fsync. If it is not enough to just release ZFS_OBJ_HOLD + * we will have to attach the vnode after the dmu_commit like + * maczfs does, in each vnop caller. + */ + do { + *zpp = zfs_znode_alloc(zfsvfs, db, 0, obj_type, sa_hdl); + } while (*zpp == NULL); + + VERIFY(*zpp != NULL); + VERIFY(dzp != NULL); + } else { + /* + * If we are creating the root node, the "parent" we + * passed in is the znode for the root. + */ + *zpp = dzp; + + (*zpp)->z_sa_hdl = sa_hdl; + } + + (*zpp)->z_pflags = pflags; + (*zpp)->z_mode = mode; + (*zpp)->z_dnodesize = dnodesize; + (*zpp)->z_projid = projid; + + if (vap->va_mask & ATTR_XVATTR) + zfs_xvattr_set(*zpp, (xvattr_t *)vap, tx); + + if (obj_type == DMU_OT_ZNODE || + acl_ids->z_aclp->z_version < ZFS_ACL_VERSION_FUID) { + err = zfs_aclset_common(*zpp, acl_ids->z_aclp, cr, tx); + ASSERT(err == 0); + } + + kmem_free(sa_attrs, sizeof (sa_bulk_attr_t) * ZPL_END); + zfs_znode_hold_exit(zfsvfs, zh); +} + +/* + * Update in-core attributes. It is assumed the caller will be doing an + * sa_bulk_update to push the changes out. + */ +void +zfs_xvattr_set(znode_t *zp, xvattr_t *xvap, dmu_tx_t *tx) +{ + xoptattr_t *xoap; + + xoap = xva_getxoptattr(xvap); + ASSERT(xoap); + + if (XVA_ISSET_REQ(xvap, XAT_CREATETIME)) { + uint64_t times[2]; + ZFS_TIME_ENCODE(&xoap->xoa_createtime, times); + (void) sa_update(zp->z_sa_hdl, SA_ZPL_CRTIME(zp->z_zfsvfs), + ×, sizeof (times), tx); + XVA_SET_RTN(xvap, XAT_CREATETIME); + } + if (XVA_ISSET_REQ(xvap, XAT_READONLY)) { + ZFS_ATTR_SET(zp, ZFS_READONLY, xoap->xoa_readonly, + zp->z_pflags, tx); + XVA_SET_RTN(xvap, XAT_READONLY); + } + if (XVA_ISSET_REQ(xvap, XAT_HIDDEN)) { + ZFS_ATTR_SET(zp, ZFS_HIDDEN, xoap->xoa_hidden, + zp->z_pflags, tx); + XVA_SET_RTN(xvap, XAT_HIDDEN); + } + if (XVA_ISSET_REQ(xvap, XAT_SYSTEM)) { + ZFS_ATTR_SET(zp, ZFS_SYSTEM, xoap->xoa_system, + zp->z_pflags, tx); + XVA_SET_RTN(xvap, XAT_SYSTEM); + } + if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE)) { + ZFS_ATTR_SET(zp, ZFS_ARCHIVE, xoap->xoa_archive, + zp->z_pflags, tx); + XVA_SET_RTN(xvap, XAT_ARCHIVE); + } + if (XVA_ISSET_REQ(xvap, XAT_IMMUTABLE)) { + ZFS_ATTR_SET(zp, ZFS_IMMUTABLE, xoap->xoa_immutable, + zp->z_pflags, tx); + XVA_SET_RTN(xvap, XAT_IMMUTABLE); + } + if (XVA_ISSET_REQ(xvap, XAT_NOUNLINK)) { + ZFS_ATTR_SET(zp, ZFS_NOUNLINK, xoap->xoa_nounlink, + zp->z_pflags, tx); + XVA_SET_RTN(xvap, XAT_NOUNLINK); + } + if (XVA_ISSET_REQ(xvap, XAT_APPENDONLY)) { + ZFS_ATTR_SET(zp, ZFS_APPENDONLY, xoap->xoa_appendonly, + zp->z_pflags, tx); + XVA_SET_RTN(xvap, XAT_APPENDONLY); + } + if (XVA_ISSET_REQ(xvap, XAT_NODUMP)) { + ZFS_ATTR_SET(zp, ZFS_NODUMP, xoap->xoa_nodump, + zp->z_pflags, tx); + XVA_SET_RTN(xvap, XAT_NODUMP); + } + if (XVA_ISSET_REQ(xvap, XAT_OPAQUE)) { + ZFS_ATTR_SET(zp, ZFS_OPAQUE, xoap->xoa_opaque, + zp->z_pflags, tx); + XVA_SET_RTN(xvap, XAT_OPAQUE); + } + if (XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED)) { + ZFS_ATTR_SET(zp, ZFS_AV_QUARANTINED, + xoap->xoa_av_quarantined, zp->z_pflags, tx); + XVA_SET_RTN(xvap, XAT_AV_QUARANTINED); + } + if (XVA_ISSET_REQ(xvap, XAT_AV_MODIFIED)) { + ZFS_ATTR_SET(zp, ZFS_AV_MODIFIED, xoap->xoa_av_modified, + zp->z_pflags, tx); + XVA_SET_RTN(xvap, XAT_AV_MODIFIED); + } + if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) { + zfs_sa_set_scanstamp(zp, xvap, tx); + XVA_SET_RTN(xvap, XAT_AV_SCANSTAMP); + } + if (XVA_ISSET_REQ(xvap, XAT_REPARSE)) { + ZFS_ATTR_SET(zp, ZFS_REPARSE, xoap->xoa_reparse, + zp->z_pflags, tx); + XVA_SET_RTN(xvap, XAT_REPARSE); + } + if (XVA_ISSET_REQ(xvap, XAT_OFFLINE)) { + ZFS_ATTR_SET(zp, ZFS_OFFLINE, xoap->xoa_offline, + zp->z_pflags, tx); + XVA_SET_RTN(xvap, XAT_OFFLINE); + } + if (XVA_ISSET_REQ(xvap, XAT_SPARSE)) { + ZFS_ATTR_SET(zp, ZFS_SPARSE, xoap->xoa_sparse, + zp->z_pflags, tx); + XVA_SET_RTN(xvap, XAT_SPARSE); + } +} + +int +zfs_zget(zfsvfs_t *zfsvfs, uint64_t obj_num, znode_t **zpp) +{ + return (zfs_zget_ext(zfsvfs, obj_num, zpp, 0)); +} + +int +zfs_zget_ext(zfsvfs_t *zfsvfs, uint64_t obj_num, znode_t **zpp, + int flags) +{ + dmu_object_info_t doi; + dmu_buf_t *db; + znode_t *zp; + znode_hold_t *zh; + struct vnode *vp = NULL; + sa_handle_t *hdl; + uint32_t vid; + int err; + + dprintf("+zget %llu\n", obj_num); + + *zpp = NULL; + +again: + zh = zfs_znode_hold_enter(zfsvfs, obj_num); + + err = sa_buf_hold(zfsvfs->z_os, obj_num, NULL, &db); + if (err) { + zfs_znode_hold_exit(zfsvfs, zh); + return (err); + } + + dmu_object_info_from_db(db, &doi); + if (doi.doi_bonus_type != DMU_OT_SA && + (doi.doi_bonus_type != DMU_OT_ZNODE || + (doi.doi_bonus_type == DMU_OT_ZNODE && + doi.doi_bonus_size < sizeof (znode_phys_t)))) { + sa_buf_rele(db, NULL); + zfs_znode_hold_exit(zfsvfs, zh); + return (SET_ERROR(EINVAL)); + } + + hdl = dmu_buf_get_user(db); + if (hdl != NULL) { + zp = sa_get_userdata(hdl); + + + /* + * Since "SA" does immediate eviction we + * should never find a sa handle that doesn't + * know about the znode. + */ + ASSERT3P(zp, !=, NULL); + + mutex_enter(&zp->z_lock); + + /* + * Since zp may disappear after we unlock below, + * we save a copy of vp and it's vid + */ + vid = zp->z_vid; + vp = ZTOV(zp); + + /* + * Since we do immediate eviction of the z_dbuf, we + * should never find a dbuf with a znode that doesn't + * know about the dbuf. + */ + ASSERT3U(zp->z_id, ==, obj_num); + + /* + * OS X can return the znode when the file is unlinked + * in order to support the sync of open-unlinked files + */ + if (!(flags & ZGET_FLAG_UNLINKED) && zp->z_unlinked) { + mutex_exit(&zp->z_lock); + sa_buf_rele(db, NULL); + zfs_znode_hold_exit(zfsvfs, zh); + return (ENOENT); + } + + mutex_exit(&zp->z_lock); + sa_buf_rele(db, NULL); + zfs_znode_hold_exit(zfsvfs, zh); + + /* + * We are racing zfs_znode_getvnode() and we got here first, we + * need to let it get ahead + */ + if (!vp) { + + // Wait until attached, if we can. + if ((flags & ZGET_FLAG_ASYNC) && + zfs_znode_asyncwait(zfsvfs, zp) == 0) { + dprintf("%s: waited on z_vnode OK\n", __func__); + } else { + dprintf("%s: async racing attach\n", __func__); + // Could be zp is being torn down, idle a bit, + // and retry. This branch is rarely executed. + kpreempt(KPREEMPT_SYNC); + } + goto again; + } + + /* + * Due to vnode_create() -> zfs_fsync() -> zil_commit() -> + * zget() -> vnode_getwithvid() -> deadlock. Unsure why + * vnode_getwithvid() ends up sleeping in msleep() but + * vnode_get() does not. + * As we can deadlock here using vnode_getwithvid() we will use + * the simpler vnode_get() in the ASYNC cases. We verify the + * vids match below. + */ + if ((flags & ZGET_FLAG_ASYNC)) + err = vnode_get(vp); + else + err = vnode_getwithvid(vp, vid); + + if (err != 0) { + dprintf("ZFS: vnode_get() returned %d\n", err); + kpreempt(KPREEMPT_SYNC); + goto again; + } + + /* + * Since we had to drop all of our locks above, make sure + * that we have the vnode and znode we had before. + */ + mutex_enter(&zp->z_lock); + if ((vid != zp->z_vid) || (vp != ZTOV(zp))) { + mutex_exit(&zp->z_lock); + /* + * Release the wrong vp from vnode_getwithvid(). + */ + VN_RELE(vp); + dprintf("ZFS: the vids do not match part 1\n"); + goto again; + } + if (vnode_vid(vp) != zp->z_vid) + dprintf("ZFS: the vids do not match\n"); + mutex_exit(&zp->z_lock); + + *zpp = zp; + + return (0); + } // if vnode != NULL + + /* + * Not found create new znode/vnode + * but only if file exists. + * + * There is a small window where zfs_vget() could + * find this object while a file create is still in + * progress. This is checked for in zfs_znode_alloc() + * + * if zfs_znode_alloc() fails it will drop the hold on the + * bonus buffer. + */ + + zp = NULL; + zp = zfs_znode_alloc(zfsvfs, db, doi.doi_data_block_size, + doi.doi_bonus_type, NULL); + if (zp == NULL) { + err = SET_ERROR(ENOENT); + zfs_znode_hold_exit(zfsvfs, zh); + dprintf("zget returning %d\n", err); + return (err); + } + + dprintf("zget create: %llu setting to %p\n", obj_num, zp); + *zpp = zp; + + // Spawn taskq to attach while we are locked + if (flags & ZGET_FLAG_ASYNC) { + zfs_znode_asyncgetvnode(zp, zfsvfs); + } + + zfs_znode_hold_exit(zfsvfs, zh); + + /* Attach a vnode to our new znode */ + if (!(flags & ZGET_FLAG_ASYNC)) { + zfs_znode_getvnode(zp, zfsvfs); + } + + dprintf("zget returning %d\n", err); + return (err); +} + + +int +zfs_rezget(znode_t *zp) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + dmu_object_info_t doi; + dmu_buf_t *db; + struct vnode *vp; + uint64_t obj_num = zp->z_id; + uint64_t mode, size; + sa_bulk_attr_t bulk[8]; + int err; + int count = 0; + uint64_t gen; + uint64_t projid = ZFS_DEFAULT_PROJID; + znode_hold_t *zh; + + if (zp->z_is_ctldir) + return (0); + + zh = zfs_znode_hold_enter(zfsvfs, obj_num); + + mutex_enter(&zp->z_acl_lock); + if (zp->z_acl_cached) { + zfs_acl_free(zp->z_acl_cached); + zp->z_acl_cached = NULL; + } + mutex_exit(&zp->z_acl_lock); + + rw_enter(&zp->z_xattr_lock, RW_WRITER); + if (zp->z_xattr_cached) { + nvlist_free(zp->z_xattr_cached); + zp->z_xattr_cached = NULL; + } + + rw_exit(&zp->z_xattr_lock); + + ASSERT(zp->z_sa_hdl == NULL); + err = sa_buf_hold(zfsvfs->z_os, obj_num, NULL, &db); + if (err) { + zfs_znode_hold_exit(zfsvfs, zh); + return (err); + } + + dmu_object_info_from_db(db, &doi); + if (doi.doi_bonus_type != DMU_OT_SA && + (doi.doi_bonus_type != DMU_OT_ZNODE || + (doi.doi_bonus_type == DMU_OT_ZNODE && + doi.doi_bonus_size < sizeof (znode_phys_t)))) { + sa_buf_rele(db, NULL); + zfs_znode_hold_exit(zfsvfs, zh); + return (SET_ERROR(EINVAL)); + } + + zfs_znode_sa_init(zfsvfs, zp, db, doi.doi_bonus_type, NULL); + size = zp->z_size; + + /* reload cached values */ + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_GEN(zfsvfs), NULL, + &gen, sizeof (gen)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_SIZE(zfsvfs), NULL, + &zp->z_size, sizeof (zp->z_size)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_LINKS(zfsvfs), NULL, + &zp->z_links, sizeof (zp->z_links)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL, + &zp->z_pflags, + sizeof (zp->z_pflags)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_ATIME(zfsvfs), NULL, + &zp->z_atime, sizeof (zp->z_atime)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_UID(zfsvfs), NULL, + &zp->z_uid, sizeof (zp->z_uid)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_GID(zfsvfs), NULL, + &zp->z_gid, sizeof (zp->z_gid)); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MODE(zfsvfs), NULL, + &mode, sizeof (mode)); + + if (sa_bulk_lookup(zp->z_sa_hdl, bulk, count)) { + zfs_znode_dmu_fini(zp); + zfs_znode_hold_exit(zfsvfs, zh); + return (SET_ERROR(EIO)); + } + + if (dmu_objset_projectquota_enabled(zfsvfs->z_os)) { + err = sa_lookup(zp->z_sa_hdl, SA_ZPL_PROJID(zfsvfs), + &projid, 8); + if (err != 0 && err != ENOENT) { + zfs_znode_dmu_fini(zp); + zfs_znode_hold_exit(zfsvfs, zh); + return (SET_ERROR(err)); + } + } + + zp->z_projid = projid; + zp->z_mode = mode; + + if (gen != zp->z_gen) { + zfs_znode_dmu_fini(zp); + zfs_znode_hold_exit(zfsvfs, zh); + return (SET_ERROR(EIO)); + } + + /* + * XXXPJD: Not sure how is that possible, but under heavy + * zfs recv -F load it happens that z_gen is the same, but + * vnode type is different than znode type. This would mean + * that for example regular file was replaced with directory + * which has the same object number. + */ + vp = ZTOV(zp); + if (vp != NULL && + vnode_vtype(vp) != IFTOVT((mode_t)zp->z_mode)) { + zfs_znode_dmu_fini(zp); + zfs_znode_hold_exit(zfsvfs, zh); + return (EIO); + } + + zp->z_blksz = doi.doi_data_block_size; + if (vp != NULL) { + vn_pages_remove(vp, 0, 0); + if (zp->z_size != size) + vnode_pager_setsize(vp, zp->z_size); + } + + /* + * If the file has zero links, then it has been unlinked on the send + * side and it must be in the received unlinked set. + * We call zfs_znode_dmu_fini() now to prevent any accesses to the + * stale data and to prevent automatical removal of the file in + * zfs_zinactive(). The file will be removed either when it is removed + * on the send side and the next incremental stream is received or + * when the unlinked set gets processed. + */ + zp->z_unlinked = (zp->z_links == 0); + if (zp->z_unlinked) + zfs_znode_dmu_fini(zp); + + zfs_znode_hold_exit(zfsvfs, zh); + + return (0); +} + +void +zfs_znode_delete(znode_t *zp, dmu_tx_t *tx) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + objset_t *os = zfsvfs->z_os; + uint64_t obj = zp->z_id; + uint64_t acl_obj = zfs_external_acl(zp); + znode_hold_t *zh; + + zh = zfs_znode_hold_enter(zfsvfs, obj); + if (acl_obj) { + VERIFY(!zp->z_is_sa); + VERIFY(0 == dmu_object_free(os, acl_obj, tx)); + } + VERIFY(0 == dmu_object_free(os, obj, tx)); + zfs_znode_dmu_fini(zp); + zfs_znode_hold_exit(zfsvfs, zh); +} + +void +zfs_zinactive(znode_t *zp) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + uint64_t z_id = zp->z_id; + znode_hold_t *zh; + + ASSERT(zp->z_sa_hdl); + + /* + * Don't allow a zfs_zget() while were trying to release this znode + */ + zh = zfs_znode_hold_enter(zfsvfs, z_id); + + mutex_enter(&zp->z_lock); + + /* + * If this was the last reference to a file with no links, remove + * the file from the file system unless the file system is mounted + * read-only. That can happen, for example, if the file system was + * originally read-write, the file was opened, then unlinked and + * the file system was made read-only before the file was finally + * closed. The file will remain in the unlinked set. + */ + if (zp->z_unlinked) { + ASSERT(!zfsvfs->z_issnap); + + if (!(vfs_isrdonly(zfsvfs->z_vfs)) && + !zfs_unlink_suspend_progress) { + mutex_exit(&zp->z_lock); + zfs_znode_hold_exit(zfsvfs, zh); + zfs_rmnode(zp); + return; + } + } + + mutex_exit(&zp->z_lock); + zfs_znode_dmu_fini(zp); + + zfs_znode_hold_exit(zfsvfs, zh); +} + +void +zfs_znode_free(znode_t *zp) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + + mutex_enter(&zfsvfs->z_znodes_lock); + zp->z_vnode = NULL; + zp->z_zfsvfs = NULL; + POINTER_INVALIDATE(&zp->z_zfsvfs); + list_remove(&zfsvfs->z_all_znodes, zp); /* XXX */ + mutex_exit(&zfsvfs->z_znodes_lock); + + if (zp->z_acl_cached) { + zfs_acl_free(zp->z_acl_cached); + zp->z_acl_cached = NULL; + } + + if (zp->z_xattr_cached) { + nvlist_free(zp->z_xattr_cached); + zp->z_xattr_cached = NULL; + } + + ASSERT(zp->z_sa_hdl == NULL); + + kmem_cache_free(znode_cache, zp); +} + + +/* + * Prepare to update znode time stamps. + * + * IN: zp - znode requiring timestamp update + * flag - ATTR_MTIME, ATTR_CTIME, ATTR_ATIME flags + * have_tx - true of caller is creating a new txg + * + * OUT: zp - new atime (via underlying inode's i_atime) + * mtime - new mtime + * ctime - new ctime + * + * NOTE: The arguments are somewhat redundant. The following condition + * is always true: + * + * have_tx == !(flag & ATTR_ATIME) + */ +void +zfs_tstamp_update_setup_ext(znode_t *zp, uint_t flag, uint64_t mtime[2], + uint64_t ctime[2], boolean_t have_tx) +{ + timestruc_t now; + + gethrestime(&now); + + if (have_tx) { /* will sa_bulk_update happen really soon? */ + zp->z_atime_dirty = 0; + zp->z_seq++; + } else { + zp->z_atime_dirty = 1; + } + + if (flag & ATTR_ATIME) { + ZFS_TIME_ENCODE(&now, zp->z_atime); + } + + if (flag & ATTR_MTIME) { + ZFS_TIME_ENCODE(&now, mtime); + if (zp->z_zfsvfs->z_use_fuids) { + zp->z_pflags |= (ZFS_ARCHIVE | + ZFS_AV_MODIFIED); + } + } + + if (flag & ATTR_CTIME) { + ZFS_TIME_ENCODE(&now, ctime); + if (zp->z_zfsvfs->z_use_fuids) + zp->z_pflags |= ZFS_ARCHIVE; + } +} + +void +zfs_tstamp_update_setup(znode_t *zp, uint_t flag, uint64_t mtime[2], + uint64_t ctime[2]) +{ + zfs_tstamp_update_setup_ext(zp, flag, mtime, ctime, B_TRUE); +} + +/* + * Grow the block size for a file. + * + * IN: zp - znode of file to free data in. + * size - requested block size + * tx - open transaction. + * + * NOTE: this function assumes that the znode is write locked. + */ +void +zfs_grow_blocksize(znode_t *zp, uint64_t size, dmu_tx_t *tx) +{ + int error; + u_longlong_t dummy; + + if (size <= zp->z_blksz) + return; + /* + * If the file size is already greater than the current blocksize, + * we will not grow. If there is more than one block in a file, + * the blocksize cannot change. + */ + if (zp->z_blksz && zp->z_size > zp->z_blksz) + return; + + error = dmu_object_set_blocksize(zp->z_zfsvfs->z_os, + zp->z_id, + size, 0, tx); + + if (error == ENOTSUP) + return; + ASSERT(error == 0); + + /* What blocksize did we actually get? */ + dmu_object_size_from_db(sa_get_db(zp->z_sa_hdl), &zp->z_blksz, &dummy); +} + +#ifdef sun +/* + * This is a dummy interface used when pvn_vplist_dirty() should *not* + * be calling back into the fs for a putpage(). E.g.: when truncating + * a file, the pages being "thrown away* don't need to be written out. + */ +/* ARGSUSED */ +static int +zfs_no_putpage(struct vnode *vp, page_t *pp, u_offset_t *offp, size_t *lenp, + int flags, cred_t *cr) +{ + ASSERT(0); + return (0); +} +#endif /* sun */ + +/* + * Increase the file length + * + * IN: zp - znode of file to free data in. + * end - new end-of-file + * + * RETURN: 0 on success, error code on failure + */ +static int +zfs_extend(znode_t *zp, uint64_t end) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + dmu_tx_t *tx; + zfs_locked_range_t *lr; + uint64_t newblksz; + int error; + + /* + * We will change zp_size, lock the whole file. + */ + lr = zfs_rangelock_enter(&zp->z_rangelock, 0, UINT64_MAX, RL_WRITER); + + /* + * Nothing to do if file already at desired length. + */ + if (end <= zp->z_size) { + zfs_rangelock_exit(lr); + return (0); + } + + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); + zfs_sa_upgrade_txholds(tx, zp); + if (end > zp->z_blksz && + (!ISP2(zp->z_blksz) || zp->z_blksz < zfsvfs->z_max_blksz)) { + /* + * We are growing the file past the current block size. + */ + if (zp->z_blksz > zp->z_zfsvfs->z_max_blksz) { + /* + * File's blocksize is already larger than the + * "recordsize" property. Only let it grow to + * the next power of 2. + */ + ASSERT(!ISP2(zp->z_blksz)); + newblksz = MIN(end, 1 << highbit64(zp->z_blksz)); + } else { + newblksz = MIN(end, zp->z_zfsvfs->z_max_blksz); + } + dmu_tx_hold_write(tx, zp->z_id, 0, newblksz); + } else { + newblksz = 0; + } + + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + dmu_tx_abort(tx); + zfs_rangelock_exit(lr); + return (error); + } + + if (newblksz) + zfs_grow_blocksize(zp, newblksz, tx); + + zp->z_size = end; + + VERIFY(0 == sa_update(zp->z_sa_hdl, SA_ZPL_SIZE(zp->z_zfsvfs), + &zp->z_size, + sizeof (zp->z_size), tx)); + + vnode_pager_setsize(ZTOV(zp), end); + + zfs_rangelock_exit(lr); + + dmu_tx_commit(tx); + + return (0); +} + + +/* + * Free space in a file. + * + * IN: zp - znode of file to free data in. + * off - start of section to free. + * len - length of section to free. + * + * RETURN: 0 on success, error code on failure + */ +static int +zfs_free_range(znode_t *zp, uint64_t off, uint64_t len) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + zfs_locked_range_t *lr; + int error; + + /* + * Lock the range being freed. + */ + lr = zfs_rangelock_enter(&zp->z_rangelock, off, len, RL_WRITER); + + /* + * Nothing to do if file already at desired length. + */ + if (off >= zp->z_size) { + zfs_rangelock_exit(lr); + return (0); + } + + if (off + len > zp->z_size) + len = zp->z_size - off; + + error = dmu_free_long_range(zfsvfs->z_os, zp->z_id, off, len); + + if (error == 0) { + /* + * In FreeBSD we cannot free block in the middle of a file, + * but only at the end of a file, so this code path should + * never happen. + */ + vnode_pager_setsize(ZTOV(zp), off); + } + +#ifdef _LINUX + /* + * Zero partial page cache entries. This must be done under a + * range lock in order to keep the ARC and page cache in sync. + */ + if (zp->z_is_mapped) { + loff_t first_page, last_page, page_len; + loff_t first_page_offset, last_page_offset; + + /* first possible full page in hole */ + first_page = (off + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + /* last page of hole */ + last_page = (off + len) >> PAGE_CACHE_SHIFT; + + /* offset of first_page */ + first_page_offset = first_page << PAGE_CACHE_SHIFT; + /* offset of last_page */ + last_page_offset = last_page << PAGE_CACHE_SHIFT; + + /* truncate whole pages */ + if (last_page_offset > first_page_offset) { + truncate_inode_pages_range(ZTOI(zp)->i_mapping, + first_page_offset, last_page_offset - 1); + } + + /* truncate sub-page ranges */ + if (first_page > last_page) { + /* entire punched area within a single page */ + zfs_zero_partial_page(zp, off, len); + } else { + /* beginning of punched area at the end of a page */ + page_len = first_page_offset - off; + if (page_len > 0) + zfs_zero_partial_page(zp, off, page_len); + + /* end of punched area at the beginning of a page */ + page_len = off + len - last_page_offset; + if (page_len > 0) + zfs_zero_partial_page(zp, last_page_offset, + page_len); + } + } +#endif + zfs_rangelock_exit(lr); + + return (error); +} + +/* + * Truncate a file + * + * IN: zp - znode of file to free data in. + * end - new end-of-file. + * + * RETURN: 0 on success, error code on failure + */ +static int +zfs_trunc(znode_t *zp, uint64_t end) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + struct vnode *vp = ZTOV(zp); + dmu_tx_t *tx; + zfs_locked_range_t *lr; + int error; + sa_bulk_attr_t bulk[2]; + int count = 0; + /* + * We will change zp_size, lock the whole file. + */ + lr = zfs_rangelock_enter(&zp->z_rangelock, 0, UINT64_MAX, RL_WRITER); + + /* + * Nothing to do if file already at desired length. + */ + if (end >= zp->z_size) { + zfs_rangelock_exit(lr); + return (0); + } + + error = dmu_free_long_range(zfsvfs->z_os, zp->z_id, end, + DMU_OBJECT_END); + if (error) { + zfs_rangelock_exit(lr); + return (error); + } + + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); + zfs_sa_upgrade_txholds(tx, zp); + dmu_tx_mark_netfree(tx); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + dmu_tx_abort(tx); + zfs_rangelock_exit(lr); + return (error); + } + + zp->z_size = end; + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_SIZE(zfsvfs), + NULL, &zp->z_size, sizeof (zp->z_size)); + + if (end == 0) { + zp->z_pflags &= ~ZFS_SPARSE; + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), + NULL, &zp->z_pflags, 8); + } + VERIFY(sa_bulk_update(zp->z_sa_hdl, bulk, count, tx) == 0); + + dmu_tx_commit(tx); + + /* + * Clear any mapped pages in the truncated region. This has to + * happen outside of the transaction to avoid the possibility of + * a deadlock with someone trying to push a page that we are + * about to invalidate. + */ + vnode_pager_setsize(vp, end); + + zfs_rangelock_exit(lr); + + return (0); +} + +/* + * Free space in a file + * + * IN: zp - znode of file to free data in. + * off - start of range + * len - end of range (0 => EOF) + * flag - current file open mode flags. + * log - TRUE if this action should be logged + * + * RETURN: 0 on success, error code on failure + */ +int +zfs_freesp(znode_t *zp, uint64_t off, uint64_t len, int flag, boolean_t log) +{ +// struct vnode *vp = ZTOV(zp); + dmu_tx_t *tx; + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + zilog_t *zilog = zfsvfs->z_log; + uint64_t mode; + uint64_t mtime[2], ctime[2]; + sa_bulk_attr_t bulk[3]; + int count = 0; + int error; + + if (vnode_isfifo(ZTOV(zp))) + return (0); + + if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_MODE(zfsvfs), &mode, + sizeof (mode))) != 0) + return (error); + + if (off > zp->z_size) { + error = zfs_extend(zp, off+len); + if (error == 0 && log) + goto log; + goto out; + } + + if (len == 0) { + error = zfs_trunc(zp, off); + } else { + if ((error = zfs_free_range(zp, off, len)) == 0 && + off + len > zp->z_size) + error = zfs_extend(zp, off+len); + } + if (error || !log) + goto out; +log: + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); + zfs_sa_upgrade_txholds(tx, zp); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + dmu_tx_abort(tx); + goto out; + } + + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL, mtime, 16); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, ctime, 16); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), + NULL, &zp->z_pflags, 8); + zfs_tstamp_update_setup(zp, CONTENT_MODIFIED, mtime, ctime); + error = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx); + ASSERT(error == 0); + + zfs_log_truncate(zilog, tx, TX_TRUNCATE, zp, off, len); + + dmu_tx_commit(tx); + + error = 0; + +out: + + return (error); +} + +void +zfs_create_fs(objset_t *os, cred_t *cr, nvlist_t *zplprops, dmu_tx_t *tx) +{ + zfsvfs_t *zfsvfs; + uint64_t moid, obj, sa_obj, version; + uint64_t sense = ZFS_CASE_SENSITIVE; + uint64_t norm = 0; + nvpair_t *elem; + int size; + int error; + int i; + znode_t *rootzp = NULL; + vattr_t vattr; + znode_t *zp; + zfs_acl_ids_t acl_ids; + + /* + * First attempt to create master node. + */ + /* + * In an empty objset, there are no blocks to read and thus + * there can be no i/o errors (which we assert below). + */ + moid = MASTER_NODE_OBJ; + error = zap_create_claim(os, moid, DMU_OT_MASTER_NODE, + DMU_OT_NONE, 0, tx); + ASSERT(error == 0); + + /* + * Set starting attributes. + */ + version = zfs_zpl_version_map(spa_version(dmu_objset_spa(os))); + elem = NULL; + while ((elem = nvlist_next_nvpair(zplprops, elem)) != NULL) { + /* For the moment we expect all zpl props to be uint64_ts */ + uint64_t val; + char *name; + + ASSERT(nvpair_type(elem) == DATA_TYPE_UINT64); + VERIFY(nvpair_value_uint64(elem, &val) == 0); + name = nvpair_name(elem); + if (strcmp(name, zfs_prop_to_name(ZFS_PROP_VERSION)) == 0) { + if (val < version) + version = val; + } else { + error = zap_update(os, moid, name, 8, 1, &val, tx); + } + ASSERT(error == 0); + if (strcmp(name, zfs_prop_to_name(ZFS_PROP_NORMALIZE)) == 0) + norm = val; + else if (strcmp(name, zfs_prop_to_name(ZFS_PROP_CASE)) == 0) + sense = val; + } + ASSERT(version != 0); + error = zap_update(os, moid, ZPL_VERSION_STR, 8, 1, &version, tx); + + /* + * Create zap object used for SA attribute registration + */ + + if (version >= ZPL_VERSION_SA) { + sa_obj = zap_create(os, DMU_OT_SA_MASTER_NODE, + DMU_OT_NONE, 0, tx); + error = zap_add(os, moid, ZFS_SA_ATTRS, 8, 1, &sa_obj, tx); + ASSERT(error == 0); + } else { + sa_obj = 0; + } + /* + * Create a delete queue. + */ + obj = zap_create(os, DMU_OT_UNLINKED_SET, DMU_OT_NONE, 0, tx); + + error = zap_add(os, moid, ZFS_UNLINKED_SET, 8, 1, &obj, tx); + ASSERT(error == 0); + + /* + * Create root znode. Create minimal znode/vnode/zfsvfs + * to allow zfs_mknode to work. + */ + VATTR_NULL(&vattr); + vattr.va_mask = ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_TYPE; + vattr.va_type = VDIR; + vattr.va_mode = S_IFDIR|0755; + vattr.va_uid = crgetuid(cr); + vattr.va_gid = crgetgid(cr); + + rootzp = kmem_cache_alloc(znode_cache, KM_SLEEP); + ASSERT(!POINTER_IS_VALID(rootzp->z_zfsvfs)); + rootzp->z_unlinked = 0; + rootzp->z_atime_dirty = 0; + rootzp->z_is_sa = USE_SA(version, os); + rootzp->z_projid = ZFS_DEFAULT_PROJID; + + rootzp->z_vnode = NULL; +#ifndef __APPLE__ + vnode.v_type = VDIR; + vnode.v_data = rootzp; + rootzp->z_vnode = &vnode; +#endif + + zfsvfs = kmem_alloc(sizeof (zfsvfs_t), KM_SLEEP); +#ifdef __APPLE__ + bzero(zfsvfs, sizeof (zfsvfs_t)); +#endif + zfsvfs->z_os = os; + zfsvfs->z_parent = zfsvfs; + zfsvfs->z_version = version; + zfsvfs->z_use_fuids = USE_FUIDS(version, os); + zfsvfs->z_use_sa = USE_SA(version, os); + zfsvfs->z_norm = norm; + + error = sa_setup(os, sa_obj, zfs_attr_table, ZPL_END, + &zfsvfs->z_attr_table); + + ASSERT(error == 0); + + /* + * Fold case on file systems that are always or sometimes case + * insensitive. + */ + if (sense == ZFS_CASE_INSENSITIVE || sense == ZFS_CASE_MIXED) + zfsvfs->z_norm |= U8_TEXTPREP_TOUPPER; + + mutex_init(&zfsvfs->z_znodes_lock, NULL, MUTEX_DEFAULT, NULL); + list_create(&zfsvfs->z_all_znodes, sizeof (znode_t), + offsetof(znode_t, z_link_node)); + + size = MIN(1 << (highbit64(zfs_object_mutex_size)-1), ZFS_OBJ_MTX_MAX); + zfsvfs->z_hold_size = size; + zfsvfs->z_hold_trees = kmem_zalloc(sizeof (avl_tree_t) * size, + KM_SLEEP); + zfsvfs->z_hold_locks = kmem_zalloc(sizeof (kmutex_t) * size, KM_SLEEP); + for (i = 0; i != size; i++) { + avl_create(&zfsvfs->z_hold_trees[i], zfs_znode_hold_compare, + sizeof (znode_hold_t), offsetof(znode_hold_t, zh_node)); + mutex_init(&zfsvfs->z_hold_locks[i], NULL, MUTEX_DEFAULT, NULL); + } + + rootzp->z_zfsvfs = zfsvfs; + VERIFY(0 == zfs_acl_ids_create(rootzp, IS_ROOT_NODE, &vattr, + cr, NULL, &acl_ids)); + zfs_mknode(rootzp, &vattr, tx, cr, IS_ROOT_NODE, &zp, &acl_ids); + ASSERT3P(zp, ==, rootzp); + error = zap_add(os, moid, ZFS_ROOT_OBJ, 8, 1, &rootzp->z_id, tx); + ASSERT(error == 0); + zfs_acl_ids_free(&acl_ids); + POINTER_INVALIDATE(&rootzp->z_zfsvfs); + + sa_handle_destroy(rootzp->z_sa_hdl); + rootzp->z_sa_hdl = NULL; + rootzp->z_vnode = NULL; + kmem_cache_free(znode_cache, rootzp); + + for (i = 0; i != size; i++) { + avl_destroy(&zfsvfs->z_hold_trees[i]); + mutex_destroy(&zfsvfs->z_hold_locks[i]); + } + + /* + * Create shares directory + */ + + error = zfs_create_share_dir(zfsvfs, tx); + + ASSERT(error == 0); + + list_destroy(&zfsvfs->z_all_znodes); + mutex_destroy(&zfsvfs->z_znodes_lock); + + kmem_free(zfsvfs->z_hold_trees, sizeof (avl_tree_t) * size); + kmem_free(zfsvfs->z_hold_locks, sizeof (kmutex_t) * size); + + kmem_free(zfsvfs, sizeof (zfsvfs_t)); +} + +#endif /* _KERNEL */ + +static int +zfs_sa_setup(objset_t *osp, sa_attr_type_t **sa_table) +{ + uint64_t sa_obj = 0; + int error; + + error = zap_lookup(osp, MASTER_NODE_OBJ, ZFS_SA_ATTRS, 8, 1, &sa_obj); + if (error != 0 && error != ENOENT) + return (error); + + error = sa_setup(osp, sa_obj, zfs_attr_table, ZPL_END, sa_table); + return (error); +} +static int +zfs_grab_sa_handle(objset_t *osp, uint64_t obj, sa_handle_t **hdlp, + dmu_buf_t **db, void *tag) +{ + dmu_object_info_t doi; + int error; + + if ((error = sa_buf_hold(osp, obj, tag, db)) != 0) + return (error); + + dmu_object_info_from_db(*db, &doi); + if (((doi.doi_bonus_type != DMU_OT_SA) && + (doi.doi_bonus_type != DMU_OT_ZNODE)) || + ((doi.doi_bonus_type == DMU_OT_ZNODE) && + (doi.doi_bonus_size < sizeof (znode_phys_t)))) { + sa_buf_rele(*db, tag); + return (SET_ERROR(ENOTSUP)); + } + + error = sa_handle_get(osp, obj, NULL, SA_HDL_PRIVATE, hdlp); + if (error != 0) { + sa_buf_rele(*db, tag); + return (error); + } + return (0); +} + +void +zfs_release_sa_handle(sa_handle_t *hdl, dmu_buf_t *db, void *tag) +{ + sa_handle_destroy(hdl); + sa_buf_rele(db, tag); +} + +/* + * Given an object number, return its parent object number and whether + * or not the object is an extended attribute directory. + */ +static int +zfs_obj_to_pobj(objset_t *osp, sa_handle_t *hdl, sa_attr_type_t *sa_table, + uint64_t *pobjp, int *is_xattrdir) +{ + uint64_t parent; + uint64_t pflags; + uint64_t mode; + uint64_t parent_mode; + sa_bulk_attr_t bulk[3]; + sa_handle_t *sa_hdl; + dmu_buf_t *sa_db; + int count = 0; + int error; + + SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_PARENT], NULL, + &parent, sizeof (parent)); + SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_FLAGS], NULL, + &pflags, sizeof (pflags)); + SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_MODE], NULL, + &mode, sizeof (mode)); + + if ((error = sa_bulk_lookup(hdl, bulk, count)) != 0) + return (error); + + /* + * When a link is removed its parent pointer is not changed and will + * be invalid. There are two cases where a link is removed but the + * file stays around, when it goes to the delete queue and when there + * are additional links. + */ + error = zfs_grab_sa_handle(osp, parent, &sa_hdl, &sa_db, FTAG); + if (error != 0) + return (error); + + error = sa_lookup(sa_hdl, ZPL_MODE, &parent_mode, sizeof (parent_mode)); + zfs_release_sa_handle(sa_hdl, sa_db, FTAG); + if (error != 0) + return (error); + + *is_xattrdir = ((pflags & ZFS_XATTR) != 0) && S_ISDIR(mode); + + /* + * Extended attributes can be applied to files, directories, etc. + * Otherwise the parent must be a directory. + */ + if (!*is_xattrdir && !S_ISDIR(parent_mode)) + return ((EINVAL)); + + *pobjp = parent; + + return (0); +} + +/* + * Given an object number, return some zpl level statistics + */ +static int +zfs_obj_to_stats_impl(sa_handle_t *hdl, sa_attr_type_t *sa_table, + zfs_stat_t *sb) +{ + sa_bulk_attr_t bulk[4]; + int count = 0; + + SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_MODE], NULL, + &sb->zs_mode, sizeof (sb->zs_mode)); + SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_GEN], NULL, + &sb->zs_gen, sizeof (sb->zs_gen)); + SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_LINKS], NULL, + &sb->zs_links, sizeof (sb->zs_links)); + SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_CTIME], NULL, + &sb->zs_ctime, sizeof (sb->zs_ctime)); + + return (sa_bulk_lookup(hdl, bulk, count)); +} + +static int +zfs_obj_to_path_impl(objset_t *osp, uint64_t obj, sa_handle_t *hdl, + sa_attr_type_t *sa_table, char *buf, int len) +{ + sa_handle_t *sa_hdl; + sa_handle_t *prevhdl = NULL; + dmu_buf_t *prevdb = NULL; + dmu_buf_t *sa_db = NULL; + char *path = buf + len - 1; + int error; + + *path = '\0'; + sa_hdl = hdl; + + uint64_t deleteq_obj; + VERIFY0(zap_lookup(osp, MASTER_NODE_OBJ, + ZFS_UNLINKED_SET, sizeof (uint64_t), 1, &deleteq_obj)); + error = zap_lookup_int(osp, deleteq_obj, obj); + if (error == 0) { + return (ESTALE); + } else if (error != ENOENT) { + return (error); + } + error = 0; + + for (;;) { + uint64_t pobj = 0; + char component[MAXNAMELEN + 2]; + size_t complen; + int is_xattrdir = 0; + + if (prevdb) + zfs_release_sa_handle(prevhdl, prevdb, FTAG); + + if ((error = zfs_obj_to_pobj(osp, sa_hdl, sa_table, &pobj, + &is_xattrdir)) != 0) + break; + + if (pobj == obj) { + if (path[0] != '/') + *--path = '/'; + break; + } + + component[0] = '/'; + if (is_xattrdir) { + (void) snprintf(component + 1, MAXNAMELEN+1, + ""); + } else { + error = zap_value_search(osp, pobj, obj, + ZFS_DIRENT_OBJ(-1ULL), + component + 1); + if (error != 0) + break; + } + + complen = strlen(component); + path -= complen; + ASSERT(path >= buf); + bcopy(component, path, complen); + obj = pobj; + + if (sa_hdl != hdl) { + prevhdl = sa_hdl; + prevdb = sa_db; + } + error = zfs_grab_sa_handle(osp, obj, &sa_hdl, &sa_db, FTAG); + if (error != 0) { + sa_hdl = prevhdl; + sa_db = prevdb; + break; + } + } + + if (sa_hdl != NULL && sa_hdl != hdl) { + ASSERT(sa_db != NULL); + zfs_release_sa_handle(sa_hdl, sa_db, FTAG); + } + + if (error == 0) + (void) memmove(buf, path, buf + len - path); + + return (error); +} + +int +zfs_obj_to_path(objset_t *osp, uint64_t obj, char *buf, int len) +{ + sa_attr_type_t *sa_table; + sa_handle_t *hdl; + dmu_buf_t *db; + int error; + + error = zfs_sa_setup(osp, &sa_table); + if (error != 0) + return (error); + + error = zfs_grab_sa_handle(osp, obj, &hdl, &db, FTAG); + if (error != 0) + return (error); + + error = zfs_obj_to_path_impl(osp, obj, hdl, sa_table, buf, len); + + zfs_release_sa_handle(hdl, db, FTAG); + return (error); +} + +int +zfs_obj_to_stats(objset_t *osp, uint64_t obj, zfs_stat_t *sb, + char *buf, int len) +{ + char *path = buf + len - 1; + sa_attr_type_t *sa_table; + sa_handle_t *hdl; + dmu_buf_t *db; + int error; + + *path = '\0'; + + error = zfs_sa_setup(osp, &sa_table); + if (error != 0) + return (error); + + error = zfs_grab_sa_handle(osp, obj, &hdl, &db, FTAG); + if (error != 0) + return (error); + + error = zfs_obj_to_stats_impl(hdl, sa_table, sb); + if (error != 0) { + zfs_release_sa_handle(hdl, db, FTAG); + return (error); + } + + error = zfs_obj_to_path_impl(osp, obj, hdl, sa_table, buf, len); + + zfs_release_sa_handle(hdl, db, FTAG); + return (error); +} + +void +zfs_znode_update_vfs(znode_t *zp) +{ + ubc_setsize(ZTOV(zp), zp->z_size); +} diff --git a/module/os/macos/zfs/zio_crypt.c b/module/os/macos/zfs/zio_crypt.c new file mode 100644 index 000000000000..b4bd6cce6171 --- /dev/null +++ b/module/os/macos/zfs/zio_crypt.c @@ -0,0 +1,2050 @@ +/* + * CDDL HEADER START + * + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2017, Datto, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This file is responsible for handling all of the details of generating + * encryption parameters and performing encryption and authentication. + * + * BLOCK ENCRYPTION PARAMETERS: + * Encryption /Authentication Algorithm Suite (crypt): + * The encryption algorithm, mode, and key length we are going to use. We + * currently support AES in either GCM or CCM modes with 128, 192, and 256 bit + * keys. All authentication is currently done with SHA512-HMAC. + * + * Plaintext: + * The unencrypted data that we want to encrypt. + * + * Initialization Vector (IV): + * An initialization vector for the encryption algorithms. This is used to + * "tweak" the encryption algorithms so that two blocks of the same data are + * encrypted into different ciphertext outputs, thus obfuscating block patterns. + * The supported encryption modes (AES-GCM and AES-CCM) require that an IV is + * never reused with the same encryption key. This value is stored unencrypted + * and must simply be provided to the decryption function. We use a 96 bit IV + * (as recommended by NIST) for all block encryption. For non-dedup blocks we + * derive the IV randomly. The first 64 bits of the IV are stored in the second + * word of DVA[2] and the remaining 32 bits are stored in the upper 32 bits of + * blk_fill. This is safe because encrypted blocks can't use the upper 32 bits + * of blk_fill. We only encrypt level 0 blocks, which normally have a fill count + * of 1. The only exception is for DMU_OT_DNODE objects, where the fill count of + * level 0 blocks is the number of allocated dnodes in that block. The on-disk + * format supports at most 2^15 slots per L0 dnode block, because the maximum + * block size is 16MB (2^24). In either case, for level 0 blocks this number + * will still be smaller than UINT32_MAX so it is safe to store the IV in the + * top 32 bits of blk_fill, while leaving the bottom 32 bits of the fill count + * for the dnode code. + * + * Master key: + * This is the most important secret data of an encrypted dataset. It is used + * along with the salt to generate that actual encryption keys via HKDF. We + * do not use the master key to directly encrypt any data because there are + * theoretical limits on how much data can actually be safely encrypted with + * any encryption mode. The master key is stored encrypted on disk with the + * user's wrapping key. Its length is determined by the encryption algorithm. + * For details on how this is stored see the block comment in dsl_crypt.c + * + * Salt: + * Used as an input to the HKDF function, along with the master key. We use a + * 64 bit salt, stored unencrypted in the first word of DVA[2]. Any given salt + * can be used for encrypting many blocks, so we cache the current salt and the + * associated derived key in zio_crypt_t so we do not need to derive it again + * needlessly. + * + * Encryption Key: + * A secret binary key, generated from an HKDF function used to encrypt and + * decrypt data. + * + * Message Authentication Code (MAC) + * The MAC is an output of authenticated encryption modes such as AES-GCM and + * AES-CCM. Its purpose is to ensure that an attacker cannot modify encrypted + * data on disk and return garbage to the application. Effectively, it is a + * checksum that can not be reproduced by an attacker. We store the MAC in the + * second 128 bits of blk_cksum, leaving the first 128 bits for a truncated + * regular checksum of the ciphertext which can be used for scrubbing. + * + * OBJECT AUTHENTICATION: + * Some object types, such as DMU_OT_MASTER_NODE cannot be encrypted because + * they contain some info that always needs to be readable. To prevent this + * data from being altered, we authenticate this data using SHA512-HMAC. This + * will produce a MAC (similar to the one produced via encryption) which can + * be used to verify the object was not modified. HMACs do not require key + * rotation or IVs, so we can keep up to the full 3 copies of authenticated + * data. + * + * ZIL ENCRYPTION: + * ZIL blocks have their bp written to disk ahead of the associated data, so we + * cannot store the MAC there as we normally do. For these blocks the MAC is + * stored in the embedded checksum within the zil_chain_t header. The salt and + * IV are generated for the block on bp allocation instead of at encryption + * time. In addition, ZIL blocks have some pieces that must be left in plaintext + * for claiming even though all of the sensitive user data still needs to be + * encrypted. The function zio_crypt_init_uios_zil() handles parsing which + * pieces of the block need to be encrypted. All data that is not encrypted is + * authenticated using the AAD mechanisms that the supported encryption modes + * provide for. In order to preserve the semantics of the ZIL for encrypted + * datasets, the ZIL is not protected at the objset level as described below. + * + * DNODE ENCRYPTION: + * Similarly to ZIL blocks, the core part of each dnode_phys_t needs to be left + * in plaintext for scrubbing and claiming, but the bonus buffers might contain + * sensitive user data. The function zio_crypt_init_uios_dnode() handles parsing + * which which pieces of the block need to be encrypted. For more details about + * dnode authentication and encryption, see zio_crypt_init_uios_dnode(). + * + * OBJECT SET AUTHENTICATION: + * Up to this point, everything we have encrypted and authenticated has been + * at level 0 (or -2 for the ZIL). If we did not do any further work the + * on-disk format would be susceptible to attacks that deleted or rearranged + * the order of level 0 blocks. Ideally, the cleanest solution would be to + * maintain a tree of authentication MACs going up the bp tree. However, this + * presents a problem for raw sends. Send files do not send information about + * indirect blocks so there would be no convenient way to transfer the MACs and + * they cannot be recalculated on the receive side without the master key which + * would defeat one of the purposes of raw sends in the first place. Instead, + * for the indirect levels of the bp tree, we use a regular SHA512 of the MACs + * from the level below. We also include some portable fields from blk_prop such + * as the lsize and compression algorithm to prevent the data from being + * misinterpreted. + * + * At the objset level, we maintain 2 separate 256 bit MACs in the + * objset_phys_t. The first one is "portable" and is the logical root of the + * MAC tree maintained in the metadnode's bps. The second, is "local" and is + * used as the root MAC for the user accounting objects, which are also not + * transferred via "zfs send". The portable MAC is sent in the DRR_BEGIN payload + * of the send file. The useraccounting code ensures that the useraccounting + * info is not present upon a receive, so the local MAC can simply be cleared + * out at that time. For more info about objset_phys_t authentication, see + * zio_crypt_do_objset_hmacs(). + * + * CONSIDERATIONS FOR DEDUP: + * In order for dedup to work, blocks that we want to dedup with one another + * need to use the same IV and encryption key, so that they will have the same + * ciphertext. Normally, one should never reuse an IV with the same encryption + * key or else AES-GCM and AES-CCM can both actually leak the plaintext of both + * blocks. In this case, however, since we are using the same plaintext as + * well all that we end up with is a duplicate of the original ciphertext we + * already had. As a result, an attacker with read access to the raw disk will + * be able to tell which blocks are the same but this information is given away + * by dedup anyway. In order to get the same IVs and encryption keys for + * equivalent blocks of data we use an HMAC of the plaintext. We use an HMAC + * here so that a reproducible checksum of the plaintext is never available to + * the attacker. The HMAC key is kept alongside the master key, encrypted on + * disk. The first 64 bits of the HMAC are used in place of the random salt, and + * the next 96 bits are used as the IV. As a result of this mechanism, dedup + * will only work within a clone family since encrypted dedup requires use of + * the same master and HMAC keys. + */ + +/* + * After encrypting many blocks with the same key we may start to run up + * against the theoretical limits of how much data can securely be encrypted + * with a single key using the supported encryption modes. The most obvious + * limitation is that our risk of generating 2 equivalent 96 bit IVs increases + * the more IVs we generate (which both GCM and CCM modes strictly forbid). + * This risk actually grows surprisingly quickly over time according to the + * Birthday Problem. With a total IV space of 2^(96 bits), and assuming we have + * generated n IVs with a cryptographically secure RNG, the approximate + * probability p(n) of a collision is given as: + * + * p(n) ~= e^(-n*(n-1)/(2*(2^96))) + * + * [http://www.math.cornell.edu/~mec/2008-2009/TianyiZheng/Birthday.html] + * + * Assuming that we want to ensure that p(n) never goes over 1 / 1 trillion + * we must not write more than 398,065,730 blocks with the same encryption key. + * Therefore, we rotate our keys after 400,000,000 blocks have been written by + * generating a new random 64 bit salt for our HKDF encryption key generation + * function. + */ +#define ZFS_KEY_MAX_SALT_USES_DEFAULT 400000000 +#define ZFS_CURRENT_MAX_SALT_USES \ + (MIN(zfs_key_max_salt_uses, ZFS_KEY_MAX_SALT_USES_DEFAULT)) +unsigned long zfs_key_max_salt_uses = ZFS_KEY_MAX_SALT_USES_DEFAULT; + +typedef struct blkptr_auth_buf { + uint64_t bab_prop; /* blk_prop - portable mask */ + uint8_t bab_mac[ZIO_DATA_MAC_LEN]; /* MAC from blk_cksum */ + uint64_t bab_pad; /* reserved for future use */ +} blkptr_auth_buf_t; + +zio_crypt_info_t zio_crypt_table[ZIO_CRYPT_FUNCTIONS] = { + {"", ZC_TYPE_NONE, 0, "inherit"}, + {"", ZC_TYPE_NONE, 0, "on"}, + {"", ZC_TYPE_NONE, 0, "off"}, + {SUN_CKM_AES_CCM, ZC_TYPE_CCM, 16, "aes-128-ccm"}, + {SUN_CKM_AES_CCM, ZC_TYPE_CCM, 24, "aes-192-ccm"}, + {SUN_CKM_AES_CCM, ZC_TYPE_CCM, 32, "aes-256-ccm"}, + {SUN_CKM_AES_GCM, ZC_TYPE_GCM, 16, "aes-128-gcm"}, + {SUN_CKM_AES_GCM, ZC_TYPE_GCM, 24, "aes-192-gcm"}, + {SUN_CKM_AES_GCM, ZC_TYPE_GCM, 32, "aes-256-gcm"} +}; + +void +zio_crypt_key_destroy(zio_crypt_key_t *key) +{ + rw_destroy(&key->zk_salt_lock); + + /* free crypto templates */ + crypto_destroy_ctx_template(key->zk_current_tmpl); + crypto_destroy_ctx_template(key->zk_hmac_tmpl); + + /* zero out sensitive data */ + bzero(key, sizeof (zio_crypt_key_t)); +} + +int +zio_crypt_key_init(uint64_t crypt, zio_crypt_key_t *key) +{ + int ret; + crypto_mechanism_t mech; + uint_t keydata_len; + + ASSERT(key != NULL); + ASSERT3U(crypt, <, ZIO_CRYPT_FUNCTIONS); + + keydata_len = zio_crypt_table[crypt].ci_keylen; + bzero(key, sizeof (zio_crypt_key_t)); + + /* fill keydata buffers and salt with random data */ + ret = random_get_bytes((uint8_t *)&key->zk_guid, sizeof (uint64_t)); + if (ret != 0) + goto error; + + ret = random_get_bytes(key->zk_master_keydata, keydata_len); + if (ret != 0) + goto error; + + ret = random_get_bytes(key->zk_hmac_keydata, SHA512_HMAC_KEYLEN); + if (ret != 0) + goto error; + + ret = random_get_bytes(key->zk_salt, ZIO_DATA_SALT_LEN); + if (ret != 0) + goto error; + + /* derive the current key from the master key */ + ret = hkdf_sha512(key->zk_master_keydata, keydata_len, NULL, 0, + key->zk_salt, ZIO_DATA_SALT_LEN, key->zk_current_keydata, + keydata_len); + if (ret != 0) + goto error; + + /* initialize keys for the ICP */ + key->zk_current_key.ck_format = CRYPTO_KEY_RAW; + key->zk_current_key.ck_data = key->zk_current_keydata; + key->zk_current_key.ck_length = CRYPTO_BYTES2BITS(keydata_len); + + key->zk_hmac_key.ck_format = CRYPTO_KEY_RAW; + key->zk_hmac_key.ck_data = &key->zk_hmac_key; + key->zk_hmac_key.ck_length = CRYPTO_BYTES2BITS(SHA512_HMAC_KEYLEN); + + /* + * Initialize the crypto templates. It's ok if this fails because + * this is just an optimization. + */ + mech.cm_type = crypto_mech2id(zio_crypt_table[crypt].ci_mechname); + ret = crypto_create_ctx_template(&mech, &key->zk_current_key, + &key->zk_current_tmpl, KM_SLEEP); + if (ret != CRYPTO_SUCCESS) + key->zk_current_tmpl = NULL; + + mech.cm_type = crypto_mech2id(SUN_CKM_SHA512_HMAC); + ret = crypto_create_ctx_template(&mech, &key->zk_hmac_key, + &key->zk_hmac_tmpl, KM_SLEEP); + if (ret != CRYPTO_SUCCESS) + key->zk_hmac_tmpl = NULL; + + key->zk_crypt = crypt; + key->zk_version = ZIO_CRYPT_KEY_CURRENT_VERSION; + key->zk_salt_count = 0; + rw_init(&key->zk_salt_lock, NULL, RW_DEFAULT, NULL); + + return (0); + +error: + zio_crypt_key_destroy(key); + return (ret); +} + +static int +zio_crypt_key_change_salt(zio_crypt_key_t *key) +{ + int ret = 0; + uint8_t salt[ZIO_DATA_SALT_LEN]; + crypto_mechanism_t mech; + uint_t keydata_len = zio_crypt_table[key->zk_crypt].ci_keylen; + + /* generate a new salt */ + ret = random_get_bytes(salt, ZIO_DATA_SALT_LEN); + if (ret != 0) + goto error; + + rw_enter(&key->zk_salt_lock, RW_WRITER); + + /* someone beat us to the salt rotation, just unlock and return */ + if (key->zk_salt_count < ZFS_CURRENT_MAX_SALT_USES) + goto out_unlock; + + /* derive the current key from the master key and the new salt */ + ret = hkdf_sha512(key->zk_master_keydata, keydata_len, NULL, 0, + salt, ZIO_DATA_SALT_LEN, key->zk_current_keydata, keydata_len); + if (ret != 0) + goto out_unlock; + + /* assign the salt and reset the usage count */ + bcopy(salt, key->zk_salt, ZIO_DATA_SALT_LEN); + key->zk_salt_count = 0; + + /* destroy the old context template and create the new one */ + crypto_destroy_ctx_template(key->zk_current_tmpl); + ret = crypto_create_ctx_template(&mech, &key->zk_current_key, + &key->zk_current_tmpl, KM_SLEEP); + if (ret != CRYPTO_SUCCESS) + key->zk_current_tmpl = NULL; + + rw_exit(&key->zk_salt_lock); + + return (0); + +out_unlock: + rw_exit(&key->zk_salt_lock); +error: + return (ret); +} + +/* See comment above zfs_key_max_salt_uses definition for details */ +int +zio_crypt_key_get_salt(zio_crypt_key_t *key, uint8_t *salt) +{ + int ret; + boolean_t salt_change; + + rw_enter(&key->zk_salt_lock, RW_READER); + + bcopy(key->zk_salt, salt, ZIO_DATA_SALT_LEN); + salt_change = (atomic_inc_64_nv(&key->zk_salt_count) >= + ZFS_CURRENT_MAX_SALT_USES); + + rw_exit(&key->zk_salt_lock); + + if (salt_change) { + ret = zio_crypt_key_change_salt(key); + if (ret != 0) + goto error; + } + + return (0); + +error: + return (ret); +} + +/* + * This function handles all encryption and decryption in zfs. When + * encrypting it expects puio to reference the plaintext and cuio to + * reference the ciphertext. cuio must have enough space for the + * ciphertext + room for a MAC. datalen should be the length of the + * plaintext / ciphertext alone. + */ +static int +zio_do_crypt_uio(boolean_t encrypt, uint64_t crypt, crypto_key_t *key, + crypto_ctx_template_t tmpl, uint8_t *ivbuf, uint_t datalen, + zfs_uio_t *puio, zfs_uio_t *cuio, uint8_t *authbuf, uint_t auth_len) +{ + int ret; + crypto_data_t plaindata, cipherdata; + CK_AES_CCM_PARAMS ccmp; + CK_AES_GCM_PARAMS gcmp; + crypto_mechanism_t mech; + zio_crypt_info_t crypt_info; + uint_t plain_full_len, maclen; + + ASSERT3U(crypt, <, ZIO_CRYPT_FUNCTIONS); + ASSERT3U(key->ck_format, ==, CRYPTO_KEY_RAW); + + /* lookup the encryption info */ + crypt_info = zio_crypt_table[crypt]; + + /* the mac will always be the last iovec_t in the cipher uio */ + maclen = cuio->uio_iov[cuio->uio_iovcnt - 1].iov_len; + + ASSERT(maclen <= ZIO_DATA_MAC_LEN); + + /* setup encryption mechanism (same as crypt) */ + mech.cm_type = crypto_mech2id(crypt_info.ci_mechname); + + /* + * Strangely, the ICP requires that plain_full_len must include + * the MAC length when decrypting, even though the UIO does not + * need to have the extra space allocated. + */ + if (encrypt) { + plain_full_len = datalen; + } else { + plain_full_len = datalen + maclen; + } + + /* + * setup encryption params (currently only AES CCM and AES GCM + * are supported) + */ + if (crypt_info.ci_crypt_type == ZC_TYPE_CCM) { + ccmp.ulNonceSize = ZIO_DATA_IV_LEN; + ccmp.ulAuthDataSize = auth_len; + ccmp.authData = authbuf; + ccmp.ulMACSize = maclen; + ccmp.nonce = ivbuf; + ccmp.ulDataSize = plain_full_len; + + mech.cm_param = (char *)(&ccmp); + mech.cm_param_len = sizeof (CK_AES_CCM_PARAMS); + } else { + gcmp.ulIvLen = ZIO_DATA_IV_LEN; + gcmp.ulIvBits = CRYPTO_BYTES2BITS(ZIO_DATA_IV_LEN); + gcmp.ulAADLen = auth_len; + gcmp.pAAD = authbuf; + gcmp.ulTagBits = CRYPTO_BYTES2BITS(maclen); + gcmp.pIv = ivbuf; + + mech.cm_param = (char *)(&gcmp); + mech.cm_param_len = sizeof (CK_AES_GCM_PARAMS); + } + + /* populate the cipher and plain data structs. */ + plaindata.cd_format = CRYPTO_DATA_UIO; + plaindata.cd_offset = 0; + plaindata.cd_uio = puio; + plaindata.cd_miscdata = NULL; + plaindata.cd_length = plain_full_len; + + cipherdata.cd_format = CRYPTO_DATA_UIO; + cipherdata.cd_offset = 0; + cipherdata.cd_uio = cuio; + cipherdata.cd_miscdata = NULL; + cipherdata.cd_length = datalen + maclen; + + /* perform the actual encryption */ + if (encrypt) { + ret = crypto_encrypt(&mech, &plaindata, key, tmpl, &cipherdata, + NULL); + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + } else { + ret = crypto_decrypt(&mech, &cipherdata, key, tmpl, &plaindata, + NULL); + if (ret != CRYPTO_SUCCESS) { + ASSERT3U(ret, ==, CRYPTO_INVALID_MAC); + ret = SET_ERROR(ECKSUM); + goto error; + } + } + + return (0); + +error: + return (ret); +} + +int +zio_crypt_key_wrap(crypto_key_t *cwkey, zio_crypt_key_t *key, uint8_t *iv, + uint8_t *mac, uint8_t *keydata_out, uint8_t *hmac_keydata_out) +{ + int ret; + zfs_uio_t puio, cuio; + uint64_t aad[3]; + iovec_t plain_iovecs[2], cipher_iovecs[3]; + uint64_t crypt = key->zk_crypt; + uint_t enc_len, keydata_len, aad_len; + + ASSERT3U(crypt, <, ZIO_CRYPT_FUNCTIONS); + ASSERT3U(cwkey->ck_format, ==, CRYPTO_KEY_RAW); + + keydata_len = zio_crypt_table[crypt].ci_keylen; + + /* generate iv for wrapping the master and hmac key */ + ret = random_get_pseudo_bytes(iv, WRAPPING_IV_LEN); + if (ret != 0) + goto error; + + /* initialize zfs_uio_ts */ + plain_iovecs[0].iov_base = key->zk_master_keydata; + plain_iovecs[0].iov_len = keydata_len; + plain_iovecs[1].iov_base = key->zk_hmac_keydata; + plain_iovecs[1].iov_len = SHA512_HMAC_KEYLEN; + + cipher_iovecs[0].iov_base = keydata_out; + cipher_iovecs[0].iov_len = keydata_len; + cipher_iovecs[1].iov_base = hmac_keydata_out; + cipher_iovecs[1].iov_len = SHA512_HMAC_KEYLEN; + cipher_iovecs[2].iov_base = mac; + cipher_iovecs[2].iov_len = WRAPPING_MAC_LEN; + + /* + * Although we don't support writing to the old format, we do + * support rewrapping the key so that the user can move and + * quarantine datasets on the old format. + */ + if (key->zk_version == 0) { + aad_len = sizeof (uint64_t); + aad[0] = LE_64(key->zk_guid); + } else { + ASSERT3U(key->zk_version, ==, ZIO_CRYPT_KEY_CURRENT_VERSION); + aad_len = sizeof (uint64_t) * 3; + aad[0] = LE_64(key->zk_guid); + aad[1] = LE_64(crypt); + aad[2] = LE_64(key->zk_version); + } + + enc_len = zio_crypt_table[crypt].ci_keylen + SHA512_HMAC_KEYLEN; + puio.uio_iov = plain_iovecs; + puio.uio_iovcnt = 2; + puio.uio_segflg = UIO_SYSSPACE; + cuio.uio_iov = cipher_iovecs; + cuio.uio_iovcnt = 3; + cuio.uio_segflg = UIO_SYSSPACE; + + /* encrypt the keys and store the resulting ciphertext and mac */ + ret = zio_do_crypt_uio(B_TRUE, crypt, cwkey, NULL, iv, enc_len, + &puio, &cuio, (uint8_t *)aad, aad_len); + if (ret != 0) + goto error; + + return (0); + +error: + return (ret); +} + +int +zio_crypt_key_unwrap(crypto_key_t *cwkey, uint64_t crypt, uint64_t version, + uint64_t guid, uint8_t *keydata, uint8_t *hmac_keydata, uint8_t *iv, + uint8_t *mac, zio_crypt_key_t *key) +{ + crypto_mechanism_t mech; + zfs_uio_t puio, cuio; + uint64_t aad[3]; + iovec_t plain_iovecs[2], cipher_iovecs[3]; + uint_t enc_len, keydata_len, aad_len; + int ret; + + ASSERT3U(crypt, <, ZIO_CRYPT_FUNCTIONS); + ASSERT3U(cwkey->ck_format, ==, CRYPTO_KEY_RAW); + + rw_init(&key->zk_salt_lock, NULL, RW_DEFAULT, NULL); + + keydata_len = zio_crypt_table[crypt].ci_keylen; + + /* initialize zfs_uio_ts */ + plain_iovecs[0].iov_base = key->zk_master_keydata; + plain_iovecs[0].iov_len = keydata_len; + plain_iovecs[1].iov_base = key->zk_hmac_keydata; + plain_iovecs[1].iov_len = SHA512_HMAC_KEYLEN; + + cipher_iovecs[0].iov_base = keydata; + cipher_iovecs[0].iov_len = keydata_len; + cipher_iovecs[1].iov_base = hmac_keydata; + cipher_iovecs[1].iov_len = SHA512_HMAC_KEYLEN; + cipher_iovecs[2].iov_base = mac; + cipher_iovecs[2].iov_len = WRAPPING_MAC_LEN; + + if (version == 0) { + aad_len = sizeof (uint64_t); + aad[0] = LE_64(guid); + } else { + ASSERT3U(version, ==, ZIO_CRYPT_KEY_CURRENT_VERSION); + aad_len = sizeof (uint64_t) * 3; + aad[0] = LE_64(guid); + aad[1] = LE_64(crypt); + aad[2] = LE_64(version); + } + + enc_len = keydata_len + SHA512_HMAC_KEYLEN; + puio.uio_iov = plain_iovecs; + puio.uio_segflg = UIO_SYSSPACE; + puio.uio_iovcnt = 2; + cuio.uio_iov = cipher_iovecs; + cuio.uio_iovcnt = 3; + cuio.uio_segflg = UIO_SYSSPACE; + + /* decrypt the keys and store the result in the output buffers */ + ret = zio_do_crypt_uio(B_FALSE, crypt, cwkey, NULL, iv, enc_len, + &puio, &cuio, (uint8_t *)aad, aad_len); + if (ret != 0) + goto error; + + /* generate a fresh salt */ + ret = random_get_bytes(key->zk_salt, ZIO_DATA_SALT_LEN); + if (ret != 0) + goto error; + + /* derive the current key from the master key */ + ret = hkdf_sha512(key->zk_master_keydata, keydata_len, NULL, 0, + key->zk_salt, ZIO_DATA_SALT_LEN, key->zk_current_keydata, + keydata_len); + if (ret != 0) + goto error; + + /* initialize keys for ICP */ + key->zk_current_key.ck_format = CRYPTO_KEY_RAW; + key->zk_current_key.ck_data = key->zk_current_keydata; + key->zk_current_key.ck_length = CRYPTO_BYTES2BITS(keydata_len); + + key->zk_hmac_key.ck_format = CRYPTO_KEY_RAW; + key->zk_hmac_key.ck_data = key->zk_hmac_keydata; + key->zk_hmac_key.ck_length = CRYPTO_BYTES2BITS(SHA512_HMAC_KEYLEN); + + /* + * Initialize the crypto templates. It's ok if this fails because + * this is just an optimization. + */ + mech.cm_type = crypto_mech2id(zio_crypt_table[crypt].ci_mechname); + ret = crypto_create_ctx_template(&mech, &key->zk_current_key, + &key->zk_current_tmpl, KM_SLEEP); + if (ret != CRYPTO_SUCCESS) + key->zk_current_tmpl = NULL; + + mech.cm_type = crypto_mech2id(SUN_CKM_SHA512_HMAC); + ret = crypto_create_ctx_template(&mech, &key->zk_hmac_key, + &key->zk_hmac_tmpl, KM_SLEEP); + if (ret != CRYPTO_SUCCESS) + key->zk_hmac_tmpl = NULL; + + key->zk_crypt = crypt; + key->zk_version = version; + key->zk_guid = guid; + key->zk_salt_count = 0; + + return (0); + +error: + zio_crypt_key_destroy(key); + return (ret); +} + +int +zio_crypt_generate_iv(uint8_t *ivbuf) +{ + int ret; + + /* randomly generate the IV */ + ret = random_get_pseudo_bytes(ivbuf, ZIO_DATA_IV_LEN); + if (ret != 0) + goto error; + + return (0); + +error: + bzero(ivbuf, ZIO_DATA_IV_LEN); + return (ret); +} + +int +zio_crypt_do_hmac(zio_crypt_key_t *key, uint8_t *data, uint_t datalen, + uint8_t *digestbuf, uint_t digestlen) +{ + int ret; + crypto_mechanism_t mech; + crypto_data_t in_data, digest_data; + uint8_t raw_digestbuf[SHA512_DIGEST_LENGTH]; + + ASSERT3U(digestlen, <=, SHA512_DIGEST_LENGTH); + + /* initialize sha512-hmac mechanism and crypto data */ + mech.cm_type = crypto_mech2id(SUN_CKM_SHA512_HMAC); + mech.cm_param = NULL; + mech.cm_param_len = 0; + + /* initialize the crypto data */ + in_data.cd_format = CRYPTO_DATA_RAW; + in_data.cd_offset = 0; + in_data.cd_length = datalen; + in_data.cd_raw.iov_base = (char *)data; + in_data.cd_raw.iov_len = in_data.cd_length; + + digest_data.cd_format = CRYPTO_DATA_RAW; + digest_data.cd_offset = 0; + digest_data.cd_length = SHA512_DIGEST_LENGTH; + digest_data.cd_raw.iov_base = (char *)raw_digestbuf; + digest_data.cd_raw.iov_len = digest_data.cd_length; + + /* generate the hmac */ + ret = crypto_mac(&mech, &in_data, &key->zk_hmac_key, key->zk_hmac_tmpl, + &digest_data, NULL); + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + + bcopy(raw_digestbuf, digestbuf, digestlen); + + return (0); + +error: + bzero(digestbuf, digestlen); + return (ret); +} + +int +zio_crypt_generate_iv_salt_dedup(zio_crypt_key_t *key, uint8_t *data, + uint_t datalen, uint8_t *ivbuf, uint8_t *salt) +{ + int ret; + uint8_t digestbuf[SHA512_DIGEST_LENGTH]; + + ret = zio_crypt_do_hmac(key, data, datalen, + digestbuf, SHA512_DIGEST_LENGTH); + if (ret != 0) + return (ret); + + bcopy(digestbuf, salt, ZIO_DATA_SALT_LEN); + bcopy(digestbuf + ZIO_DATA_SALT_LEN, ivbuf, ZIO_DATA_IV_LEN); + + return (0); +} + +/* + * The following functions are used to encode and decode encryption parameters + * into blkptr_t and zil_header_t. The ICP wants to use these parameters as + * byte strings, which normally means that these strings would not need to deal + * with byteswapping at all. However, both blkptr_t and zil_header_t may be + * byteswapped by lower layers and so we must "undo" that byteswap here upon + * decoding and encoding in a non-native byteorder. These functions require + * that the byteorder bit is correct before being called. + */ +void +zio_crypt_encode_params_bp(blkptr_t *bp, uint8_t *salt, uint8_t *iv) +{ + uint64_t val64; + uint32_t val32; + + ASSERT(BP_IS_ENCRYPTED(bp)); + + if (!BP_SHOULD_BYTESWAP(bp)) { + bcopy(salt, &bp->blk_dva[2].dva_word[0], sizeof (uint64_t)); + bcopy(iv, &bp->blk_dva[2].dva_word[1], sizeof (uint64_t)); + bcopy(iv + sizeof (uint64_t), &val32, sizeof (uint32_t)); + BP_SET_IV2(bp, val32); + } else { + bcopy(salt, &val64, sizeof (uint64_t)); + bp->blk_dva[2].dva_word[0] = BSWAP_64(val64); + + bcopy(iv, &val64, sizeof (uint64_t)); + bp->blk_dva[2].dva_word[1] = BSWAP_64(val64); + + bcopy(iv + sizeof (uint64_t), &val32, sizeof (uint32_t)); + BP_SET_IV2(bp, BSWAP_32(val32)); + } +} + +void +zio_crypt_decode_params_bp(const blkptr_t *bp, uint8_t *salt, uint8_t *iv) +{ + uint64_t val64; + uint32_t val32; + + ASSERT(BP_IS_PROTECTED(bp)); + + /* for convenience, so callers don't need to check */ + if (BP_IS_AUTHENTICATED(bp)) { + bzero(salt, ZIO_DATA_SALT_LEN); + bzero(iv, ZIO_DATA_IV_LEN); + return; + } + + if (!BP_SHOULD_BYTESWAP(bp)) { + bcopy(&bp->blk_dva[2].dva_word[0], salt, sizeof (uint64_t)); + bcopy(&bp->blk_dva[2].dva_word[1], iv, sizeof (uint64_t)); + + val32 = (uint32_t)BP_GET_IV2(bp); + bcopy(&val32, iv + sizeof (uint64_t), sizeof (uint32_t)); + } else { + val64 = BSWAP_64(bp->blk_dva[2].dva_word[0]); + bcopy(&val64, salt, sizeof (uint64_t)); + + val64 = BSWAP_64(bp->blk_dva[2].dva_word[1]); + bcopy(&val64, iv, sizeof (uint64_t)); + + val32 = BSWAP_32((uint32_t)BP_GET_IV2(bp)); + bcopy(&val32, iv + sizeof (uint64_t), sizeof (uint32_t)); + } +} + +void +zio_crypt_encode_mac_bp(blkptr_t *bp, uint8_t *mac) +{ + uint64_t val64; + + ASSERT(BP_USES_CRYPT(bp)); + ASSERT3U(BP_GET_TYPE(bp), !=, DMU_OT_OBJSET); + + if (!BP_SHOULD_BYTESWAP(bp)) { + bcopy(mac, &bp->blk_cksum.zc_word[2], sizeof (uint64_t)); + bcopy(mac + sizeof (uint64_t), &bp->blk_cksum.zc_word[3], + sizeof (uint64_t)); + } else { + bcopy(mac, &val64, sizeof (uint64_t)); + bp->blk_cksum.zc_word[2] = BSWAP_64(val64); + + bcopy(mac + sizeof (uint64_t), &val64, sizeof (uint64_t)); + bp->blk_cksum.zc_word[3] = BSWAP_64(val64); + } +} + +void +zio_crypt_decode_mac_bp(const blkptr_t *bp, uint8_t *mac) +{ + uint64_t val64; + + ASSERT(BP_USES_CRYPT(bp) || BP_IS_HOLE(bp)); + + /* for convenience, so callers don't need to check */ + if (BP_GET_TYPE(bp) == DMU_OT_OBJSET) { + bzero(mac, ZIO_DATA_MAC_LEN); + return; + } + + if (!BP_SHOULD_BYTESWAP(bp)) { + bcopy(&bp->blk_cksum.zc_word[2], mac, sizeof (uint64_t)); + bcopy(&bp->blk_cksum.zc_word[3], mac + sizeof (uint64_t), + sizeof (uint64_t)); + } else { + val64 = BSWAP_64(bp->blk_cksum.zc_word[2]); + bcopy(&val64, mac, sizeof (uint64_t)); + + val64 = BSWAP_64(bp->blk_cksum.zc_word[3]); + bcopy(&val64, mac + sizeof (uint64_t), sizeof (uint64_t)); + } +} + +void +zio_crypt_encode_mac_zil(void *data, uint8_t *mac) +{ + zil_chain_t *zilc = data; + + bcopy(mac, &zilc->zc_eck.zec_cksum.zc_word[2], sizeof (uint64_t)); + bcopy(mac + sizeof (uint64_t), &zilc->zc_eck.zec_cksum.zc_word[3], + sizeof (uint64_t)); +} + +void +zio_crypt_decode_mac_zil(const void *data, uint8_t *mac) +{ + /* + * The ZIL MAC is embedded in the block it protects, which will + * not have been byteswapped by the time this function has been called. + * As a result, we don't need to worry about byteswapping the MAC. + */ + const zil_chain_t *zilc = data; + + bcopy(&zilc->zc_eck.zec_cksum.zc_word[2], mac, sizeof (uint64_t)); + bcopy(&zilc->zc_eck.zec_cksum.zc_word[3], mac + sizeof (uint64_t), + sizeof (uint64_t)); +} + +/* + * This routine takes a block of dnodes (src_abd) and copies only the bonus + * buffers to the same offsets in the dst buffer. datalen should be the size + * of both the src_abd and the dst buffer (not just the length of the bonus + * buffers). + */ +void +zio_crypt_copy_dnode_bonus(abd_t *src_abd, uint8_t *dst, uint_t datalen) +{ + uint_t i, max_dnp = datalen >> DNODE_SHIFT; + uint8_t *src; + dnode_phys_t *dnp, *sdnp, *ddnp; + + src = abd_borrow_buf_copy(src_abd, datalen); + + sdnp = (dnode_phys_t *)src; + ddnp = (dnode_phys_t *)dst; + + for (i = 0; i < max_dnp; i += sdnp[i].dn_extra_slots + 1) { + dnp = &sdnp[i]; + if (dnp->dn_type != DMU_OT_NONE && + DMU_OT_IS_ENCRYPTED(dnp->dn_bonustype) && + dnp->dn_bonuslen != 0) { + bcopy(DN_BONUS(dnp), DN_BONUS(&ddnp[i]), + DN_MAX_BONUS_LEN(dnp)); + } + } + + abd_return_buf(src_abd, src, datalen); +} + +/* + * This function decides what fields from blk_prop are included in + * the on-disk various MAC algorithms. + */ +static void +zio_crypt_bp_zero_nonportable_blkprop(blkptr_t *bp, uint64_t version) +{ + /* + * Version 0 did not properly zero out all non-portable fields + * as it should have done. We maintain this code so that we can + * do read-only imports of pools on this version. + */ + if (version == 0) { + BP_SET_DEDUP(bp, 0); + BP_SET_CHECKSUM(bp, 0); + BP_SET_PSIZE(bp, SPA_MINBLOCKSIZE); + return; + } + + ASSERT3U(version, ==, ZIO_CRYPT_KEY_CURRENT_VERSION); + + /* + * The hole_birth feature might set these fields even if this bp + * is a hole. We zero them out here to guarantee that raw sends + * will function with or without the feature. + */ + if (BP_IS_HOLE(bp)) { + bp->blk_prop = 0ULL; + return; + } + + /* + * At L0 we want to verify these fields to ensure that data blocks + * can not be reinterpreted. For instance, we do not want an attacker + * to trick us into returning raw lz4 compressed data to the user + * by modifying the compression bits. At higher levels, we cannot + * enforce this policy since raw sends do not convey any information + * about indirect blocks, so these values might be different on the + * receive side. Fortunately, this does not open any new attack + * vectors, since any alterations that can be made to a higher level + * bp must still verify the correct order of the layer below it. + */ + if (BP_GET_LEVEL(bp) != 0) { + BP_SET_BYTEORDER(bp, 0); + BP_SET_COMPRESS(bp, 0); + + /* + * psize cannot be set to zero or it will trigger + * asserts, but the value doesn't really matter as + * long as it is constant. + */ + BP_SET_PSIZE(bp, SPA_MINBLOCKSIZE); + } + + BP_SET_DEDUP(bp, 0); + BP_SET_CHECKSUM(bp, 0); +} + +static void +zio_crypt_bp_auth_init(uint64_t version, boolean_t should_bswap, blkptr_t *bp, + blkptr_auth_buf_t *bab, uint_t *bab_len) +{ + blkptr_t tmpbp = *bp; + + if (should_bswap) + byteswap_uint64_array(&tmpbp, sizeof (blkptr_t)); + + ASSERT(BP_USES_CRYPT(&tmpbp) || BP_IS_HOLE(&tmpbp)); + ASSERT0(BP_IS_EMBEDDED(&tmpbp)); + + zio_crypt_decode_mac_bp(&tmpbp, bab->bab_mac); + + /* + * We always MAC blk_prop in LE to ensure portability. This + * must be done after decoding the mac, since the endianness + * will get zero'd out here. + */ + zio_crypt_bp_zero_nonportable_blkprop(&tmpbp, version); + bab->bab_prop = LE_64(tmpbp.blk_prop); + bab->bab_pad = 0ULL; + + /* version 0 did not include the padding */ + *bab_len = sizeof (blkptr_auth_buf_t); + if (version == 0) + *bab_len -= sizeof (uint64_t); +} + +static int +zio_crypt_bp_do_hmac_updates(crypto_context_t ctx, uint64_t version, + boolean_t should_bswap, blkptr_t *bp) +{ + int ret; + uint_t bab_len; + blkptr_auth_buf_t bab; + crypto_data_t cd; + + zio_crypt_bp_auth_init(version, should_bswap, bp, &bab, &bab_len); + cd.cd_format = CRYPTO_DATA_RAW; + cd.cd_offset = 0; + cd.cd_length = bab_len; + cd.cd_raw.iov_base = (char *)&bab; + cd.cd_raw.iov_len = cd.cd_length; + + ret = crypto_mac_update(ctx, &cd, NULL); + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + + return (0); + +error: + return (ret); +} + +static void +zio_crypt_bp_do_indrect_checksum_updates(SHA2_CTX *ctx, uint64_t version, + boolean_t should_bswap, blkptr_t *bp) +{ + uint_t bab_len; + blkptr_auth_buf_t bab; + + zio_crypt_bp_auth_init(version, should_bswap, bp, &bab, &bab_len); + SHA2Update(ctx, &bab, bab_len); +} + +static void +zio_crypt_bp_do_aad_updates(uint8_t **aadp, uint_t *aad_len, uint64_t version, + boolean_t should_bswap, blkptr_t *bp) +{ + uint_t bab_len; + blkptr_auth_buf_t bab; + + zio_crypt_bp_auth_init(version, should_bswap, bp, &bab, &bab_len); + bcopy(&bab, *aadp, bab_len); + *aadp += bab_len; + *aad_len += bab_len; +} + +static int +zio_crypt_do_dnode_hmac_updates(crypto_context_t ctx, uint64_t version, + boolean_t should_bswap, dnode_phys_t *dnp) +{ + int ret, i; + dnode_phys_t *adnp; + boolean_t le_bswap = (should_bswap == ZFS_HOST_BYTEORDER); + crypto_data_t cd; + uint8_t tmp_dncore[offsetof(dnode_phys_t, dn_blkptr)]; + + cd.cd_format = CRYPTO_DATA_RAW; + cd.cd_offset = 0; + + /* authenticate the core dnode (masking out non-portable bits) */ + bcopy(dnp, tmp_dncore, sizeof (tmp_dncore)); + adnp = (dnode_phys_t *)tmp_dncore; + if (le_bswap) { + adnp->dn_datablkszsec = BSWAP_16(adnp->dn_datablkszsec); + adnp->dn_bonuslen = BSWAP_16(adnp->dn_bonuslen); + adnp->dn_maxblkid = BSWAP_64(adnp->dn_maxblkid); + adnp->dn_used = BSWAP_64(adnp->dn_used); + } + adnp->dn_flags &= DNODE_CRYPT_PORTABLE_FLAGS_MASK; + adnp->dn_used = 0; + + cd.cd_length = sizeof (tmp_dncore); + cd.cd_raw.iov_base = (char *)adnp; + cd.cd_raw.iov_len = cd.cd_length; + + ret = crypto_mac_update(ctx, &cd, NULL); + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + + for (i = 0; i < dnp->dn_nblkptr; i++) { + ret = zio_crypt_bp_do_hmac_updates(ctx, version, + should_bswap, &dnp->dn_blkptr[i]); + if (ret != 0) + goto error; + } + + if (dnp->dn_flags & DNODE_FLAG_SPILL_BLKPTR) { + ret = zio_crypt_bp_do_hmac_updates(ctx, version, + should_bswap, DN_SPILL_BLKPTR(dnp)); + if (ret != 0) + goto error; + } + + return (0); + +error: + return (ret); +} + +/* + * objset_phys_t blocks introduce a number of exceptions to the normal + * authentication process. objset_phys_t's contain 2 separate HMACS for + * protecting the integrity of their data. The portable_mac protects the + * metadnode. This MAC can be sent with a raw send and protects against + * reordering of data within the metadnode. The local_mac protects the user + * accounting objects which are not sent from one system to another. + * + * In addition, objset blocks are the only blocks that can be modified and + * written to disk without the key loaded under certain circumstances. During + * zil_claim() we need to be able to update the zil_header_t to complete + * claiming log blocks and during raw receives we need to write out the + * portable_mac from the send file. Both of these actions are possible + * because these fields are not protected by either MAC so neither one will + * need to modify the MACs without the key. However, when the modified blocks + * are written out they will be byteswapped into the host machine's native + * endianness which will modify fields protected by the MAC. As a result, MAC + * calculation for objset blocks works slightly differently from other block + * types. Where other block types MAC the data in whatever endianness is + * written to disk, objset blocks always MAC little endian version of their + * values. In the code, should_bswap is the value from BP_SHOULD_BYTESWAP() + * and le_bswap indicates whether a byteswap is needed to get this block + * into little endian format. + */ +int +zio_crypt_do_objset_hmacs(zio_crypt_key_t *key, void *data, uint_t datalen, + boolean_t should_bswap, uint8_t *portable_mac, uint8_t *local_mac) +{ + int ret; + crypto_mechanism_t mech; + crypto_context_t ctx; + crypto_data_t cd; + objset_phys_t *osp = data; + uint64_t intval; + boolean_t le_bswap = (should_bswap == ZFS_HOST_BYTEORDER); + uint8_t raw_portable_mac[SHA512_DIGEST_LENGTH]; + uint8_t raw_local_mac[SHA512_DIGEST_LENGTH]; + + /* initialize HMAC mechanism */ + mech.cm_type = crypto_mech2id(SUN_CKM_SHA512_HMAC); + mech.cm_param = NULL; + mech.cm_param_len = 0; + + cd.cd_format = CRYPTO_DATA_RAW; + cd.cd_offset = 0; + + /* calculate the portable MAC from the portable fields and metadnode */ + ret = crypto_mac_init(&mech, &key->zk_hmac_key, NULL, &ctx, NULL); + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + + /* add in the os_type */ + intval = (le_bswap) ? osp->os_type : BSWAP_64(osp->os_type); + cd.cd_length = sizeof (uint64_t); + cd.cd_raw.iov_base = (char *)&intval; + cd.cd_raw.iov_len = cd.cd_length; + + ret = crypto_mac_update(ctx, &cd, NULL); + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + + /* add in the portable os_flags */ + intval = osp->os_flags; + if (should_bswap) + intval = BSWAP_64(intval); + intval &= OBJSET_CRYPT_PORTABLE_FLAGS_MASK; + if (!ZFS_HOST_BYTEORDER) + intval = BSWAP_64(intval); + + cd.cd_length = sizeof (uint64_t); + cd.cd_raw.iov_base = (char *)&intval; + cd.cd_raw.iov_len = cd.cd_length; + + ret = crypto_mac_update(ctx, &cd, NULL); + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + + /* add in fields from the metadnode */ + ret = zio_crypt_do_dnode_hmac_updates(ctx, key->zk_version, + should_bswap, &osp->os_meta_dnode); + if (ret) + goto error; + + /* store the final digest in a temporary buffer and copy what we need */ + cd.cd_length = SHA512_DIGEST_LENGTH; + cd.cd_raw.iov_base = (char *)raw_portable_mac; + cd.cd_raw.iov_len = cd.cd_length; + + ret = crypto_mac_final(ctx, &cd, NULL); + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + + bcopy(raw_portable_mac, portable_mac, ZIO_OBJSET_MAC_LEN); + + /* + * This is necessary here as we check next whether + * OBJSET_FLAG_USERACCOUNTING_COMPLETE or + * OBJSET_FLAG_USEROBJACCOUNTING are set in order to + * decide if the local_mac should be zeroed out. + */ + intval = osp->os_flags; + if (should_bswap) + intval = BSWAP_64(intval); + + /* + * The local MAC protects the user, group and project accounting. + * If these objects are not present, the local MAC is zeroed out. + */ + if ((datalen >= OBJSET_PHYS_SIZE_V3 && + osp->os_userused_dnode.dn_type == DMU_OT_NONE && + osp->os_groupused_dnode.dn_type == DMU_OT_NONE && + osp->os_projectused_dnode.dn_type == DMU_OT_NONE) || + (datalen >= OBJSET_PHYS_SIZE_V2 && + osp->os_userused_dnode.dn_type == DMU_OT_NONE && + osp->os_groupused_dnode.dn_type == DMU_OT_NONE) || + (datalen <= OBJSET_PHYS_SIZE_V1) || + (((intval & OBJSET_FLAG_USERACCOUNTING_COMPLETE) == 0 || + (intval & OBJSET_FLAG_USEROBJACCOUNTING_COMPLETE) == 0) && + key->zk_version > 0)) { + bzero(local_mac, ZIO_OBJSET_MAC_LEN); + return (0); + } + + /* calculate the local MAC from the userused and groupused dnodes */ + ret = crypto_mac_init(&mech, &key->zk_hmac_key, NULL, &ctx, NULL); + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + + /* add in the non-portable os_flags */ + intval = osp->os_flags; + if (should_bswap) + intval = BSWAP_64(intval); + intval &= ~OBJSET_CRYPT_PORTABLE_FLAGS_MASK; + if (!ZFS_HOST_BYTEORDER) + intval = BSWAP_64(intval); + + cd.cd_length = sizeof (uint64_t); + cd.cd_raw.iov_base = (char *)&intval; + cd.cd_raw.iov_len = cd.cd_length; + + ret = crypto_mac_update(ctx, &cd, NULL); + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + + /* add in fields from the user accounting dnodes */ + if (osp->os_userused_dnode.dn_type != DMU_OT_NONE) { + ret = zio_crypt_do_dnode_hmac_updates(ctx, key->zk_version, + should_bswap, &osp->os_userused_dnode); + if (ret) + goto error; + } + + if (osp->os_groupused_dnode.dn_type != DMU_OT_NONE) { + ret = zio_crypt_do_dnode_hmac_updates(ctx, key->zk_version, + should_bswap, &osp->os_groupused_dnode); + if (ret) + goto error; + } + + if (osp->os_projectused_dnode.dn_type != DMU_OT_NONE && + datalen >= OBJSET_PHYS_SIZE_V3) { + ret = zio_crypt_do_dnode_hmac_updates(ctx, key->zk_version, + should_bswap, &osp->os_projectused_dnode); + if (ret) + goto error; + } + + /* store the final digest in a temporary buffer and copy what we need */ + cd.cd_length = SHA512_DIGEST_LENGTH; + cd.cd_raw.iov_base = (char *)raw_local_mac; + cd.cd_raw.iov_len = cd.cd_length; + + ret = crypto_mac_final(ctx, &cd, NULL); + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + + bcopy(raw_local_mac, local_mac, ZIO_OBJSET_MAC_LEN); + + return (0); + +error: + bzero(portable_mac, ZIO_OBJSET_MAC_LEN); + bzero(local_mac, ZIO_OBJSET_MAC_LEN); + return (ret); +} + +static void +zio_crypt_destroy_uio(zfs_uio_t *uio) +{ + if (uio->uio_iov) + kmem_free((void *)uio->uio_iov, + uio->uio_iovcnt * sizeof (iovec_t)); +} + +/* + * This function parses an uncompressed indirect block and returns a checksum + * of all the portable fields from all of the contained bps. The portable + * fields are the MAC and all of the fields from blk_prop except for the dedup, + * checksum, and psize bits. For an explanation of the purpose of this, see + * the comment block on object set authentication. + */ +static int +zio_crypt_do_indirect_mac_checksum_impl(boolean_t generate, void *buf, + uint_t datalen, uint64_t version, boolean_t byteswap, uint8_t *cksum) +{ + blkptr_t *bp; + int i, epb = datalen >> SPA_BLKPTRSHIFT; + SHA2_CTX ctx; + uint8_t digestbuf[SHA512_DIGEST_LENGTH]; + + /* checksum all of the MACs from the layer below */ + SHA2Init(SHA512, &ctx); + for (i = 0, bp = buf; i < epb; i++, bp++) { + zio_crypt_bp_do_indrect_checksum_updates(&ctx, version, + byteswap, bp); + } + SHA2Final(digestbuf, &ctx); + + if (generate) { + bcopy(digestbuf, cksum, ZIO_DATA_MAC_LEN); + return (0); + } + + if (bcmp(digestbuf, cksum, ZIO_DATA_MAC_LEN) != 0) + return (SET_ERROR(ECKSUM)); + + return (0); +} + +int +zio_crypt_do_indirect_mac_checksum(boolean_t generate, void *buf, + uint_t datalen, boolean_t byteswap, uint8_t *cksum) +{ + int ret; + + /* + * Unfortunately, callers of this function will not always have + * easy access to the on-disk format version. This info is + * normally found in the DSL Crypto Key, but the checksum-of-MACs + * is expected to be verifiable even when the key isn't loaded. + * Here, instead of doing a ZAP lookup for the version for each + * zio, we simply try both existing formats. + */ + ret = zio_crypt_do_indirect_mac_checksum_impl(generate, buf, + datalen, ZIO_CRYPT_KEY_CURRENT_VERSION, byteswap, cksum); + if (ret == ECKSUM) { + ASSERT(!generate); + ret = zio_crypt_do_indirect_mac_checksum_impl(generate, + buf, datalen, 0, byteswap, cksum); + } + + return (ret); +} + +int +zio_crypt_do_indirect_mac_checksum_abd(boolean_t generate, abd_t *abd, + uint_t datalen, boolean_t byteswap, uint8_t *cksum) +{ + int ret; + void *buf; + + buf = abd_borrow_buf_copy(abd, datalen); + ret = zio_crypt_do_indirect_mac_checksum(generate, buf, datalen, + byteswap, cksum); + abd_return_buf(abd, buf, datalen); + + return (ret); +} + +/* + * Special case handling routine for encrypting / decrypting ZIL blocks. + * We do not check for the older ZIL chain because the encryption feature + * was not available before the newer ZIL chain was introduced. The goal + * here is to encrypt everything except the blkptr_t of a lr_write_t and + * the zil_chain_t header. Everything that is not encrypted is authenticated. + */ +static int +zio_crypt_init_uios_zil(boolean_t encrypt, uint8_t *plainbuf, + uint8_t *cipherbuf, uint_t datalen, boolean_t byteswap, zfs_uio_t *puio, + zfs_uio_t *cuio, uint_t *enc_len, uint8_t **authbuf, uint_t *auth_len, + boolean_t *no_crypt) +{ + int ret; + uint64_t txtype, lr_len; + uint_t nr_src, nr_dst, crypt_len; + uint_t aad_len = 0, nr_iovecs = 0, total_len = 0; + iovec_t *src_iovecs = NULL, *dst_iovecs = NULL; + uint8_t *src, *dst, *slrp, *dlrp, *blkend, *aadp; + zil_chain_t *zilc; + lr_t *lr; + uint8_t *aadbuf = zio_buf_alloc(datalen); + + /* cipherbuf always needs an extra iovec for the MAC */ + if (encrypt) { + src = plainbuf; + dst = cipherbuf; + nr_src = 0; + nr_dst = 1; + } else { + src = cipherbuf; + dst = plainbuf; + nr_src = 1; + nr_dst = 0; + } + + /* find the start and end record of the log block */ + zilc = (zil_chain_t *)src; + slrp = src + sizeof (zil_chain_t); + aadp = aadbuf; + blkend = src + ((byteswap) ? BSWAP_64(zilc->zc_nused) : zilc->zc_nused); + + /* calculate the number of encrypted iovecs we will need */ + for (; slrp < blkend; slrp += lr_len) { + lr = (lr_t *)slrp; + + if (!byteswap) { + txtype = lr->lrc_txtype; + lr_len = lr->lrc_reclen; + } else { + txtype = BSWAP_64(lr->lrc_txtype); + lr_len = BSWAP_64(lr->lrc_reclen); + } + + nr_iovecs++; + if (txtype == TX_WRITE && lr_len != sizeof (lr_write_t)) + nr_iovecs++; + } + + nr_src += nr_iovecs; + nr_dst += nr_iovecs; + + /* allocate the iovec arrays */ + if (nr_src != 0) { + src_iovecs = kmem_alloc(nr_src * sizeof (iovec_t), KM_SLEEP); + if (src_iovecs == NULL) { + ret = SET_ERROR(ENOMEM); + goto error; + } + } + + if (nr_dst != 0) { + dst_iovecs = kmem_alloc(nr_dst * sizeof (iovec_t), KM_SLEEP); + if (dst_iovecs == NULL) { + ret = SET_ERROR(ENOMEM); + goto error; + } + } + + /* + * Copy the plain zil header over and authenticate everything except + * the checksum that will store our MAC. If we are writing the data + * the embedded checksum will not have been calculated yet, so we don't + * authenticate that. + */ + bcopy(src, dst, sizeof (zil_chain_t)); + bcopy(src, aadp, sizeof (zil_chain_t) - sizeof (zio_eck_t)); + aadp += sizeof (zil_chain_t) - sizeof (zio_eck_t); + aad_len += sizeof (zil_chain_t) - sizeof (zio_eck_t); + + /* loop over records again, filling in iovecs */ + nr_iovecs = 0; + slrp = src + sizeof (zil_chain_t); + dlrp = dst + sizeof (zil_chain_t); + + for (; slrp < blkend; slrp += lr_len, dlrp += lr_len) { + lr = (lr_t *)slrp; + + if (!byteswap) { + txtype = lr->lrc_txtype; + lr_len = lr->lrc_reclen; + } else { + txtype = BSWAP_64(lr->lrc_txtype); + lr_len = BSWAP_64(lr->lrc_reclen); + } + + /* copy the common lr_t */ + bcopy(slrp, dlrp, sizeof (lr_t)); + bcopy(slrp, aadp, sizeof (lr_t)); + aadp += sizeof (lr_t); + aad_len += sizeof (lr_t); + + ASSERT3P(src_iovecs, !=, NULL); + ASSERT3P(dst_iovecs, !=, NULL); + + /* + * If this is a TX_WRITE record we want to encrypt everything + * except the bp if exists. If the bp does exist we want to + * authenticate it. + */ + if (txtype == TX_WRITE) { + crypt_len = sizeof (lr_write_t) - + sizeof (lr_t) - sizeof (blkptr_t); + src_iovecs[nr_iovecs].iov_base = slrp + sizeof (lr_t); + src_iovecs[nr_iovecs].iov_len = crypt_len; + dst_iovecs[nr_iovecs].iov_base = dlrp + sizeof (lr_t); + dst_iovecs[nr_iovecs].iov_len = crypt_len; + + /* copy the bp now since it will not be encrypted */ + bcopy(slrp + sizeof (lr_write_t) - sizeof (blkptr_t), + dlrp + sizeof (lr_write_t) - sizeof (blkptr_t), + sizeof (blkptr_t)); + bcopy(slrp + sizeof (lr_write_t) - sizeof (blkptr_t), + aadp, sizeof (blkptr_t)); + aadp += sizeof (blkptr_t); + aad_len += sizeof (blkptr_t); + nr_iovecs++; + total_len += crypt_len; + + if (lr_len != sizeof (lr_write_t)) { + crypt_len = lr_len - sizeof (lr_write_t); + src_iovecs[nr_iovecs].iov_base = + slrp + sizeof (lr_write_t); + src_iovecs[nr_iovecs].iov_len = crypt_len; + dst_iovecs[nr_iovecs].iov_base = + dlrp + sizeof (lr_write_t); + dst_iovecs[nr_iovecs].iov_len = crypt_len; + nr_iovecs++; + total_len += crypt_len; + } + } else { + crypt_len = lr_len - sizeof (lr_t); + src_iovecs[nr_iovecs].iov_base = slrp + sizeof (lr_t); + src_iovecs[nr_iovecs].iov_len = crypt_len; + dst_iovecs[nr_iovecs].iov_base = dlrp + sizeof (lr_t); + dst_iovecs[nr_iovecs].iov_len = crypt_len; + nr_iovecs++; + total_len += crypt_len; + } + } + + *no_crypt = (nr_iovecs == 0); + *enc_len = total_len; + *authbuf = aadbuf; + *auth_len = aad_len; + + if (encrypt) { + puio->uio_iov = src_iovecs; + puio->uio_iovcnt = nr_src; + cuio->uio_iov = dst_iovecs; + cuio->uio_iovcnt = nr_dst; + } else { + puio->uio_iov = dst_iovecs; + puio->uio_iovcnt = nr_dst; + cuio->uio_iov = src_iovecs; + cuio->uio_iovcnt = nr_src; + } + + return (0); + +error: + zio_buf_free(aadbuf, datalen); + if (src_iovecs != NULL) + kmem_free(src_iovecs, nr_src * sizeof (iovec_t)); + if (dst_iovecs != NULL) + kmem_free(dst_iovecs, nr_dst * sizeof (iovec_t)); + + *enc_len = 0; + *authbuf = NULL; + *auth_len = 0; + *no_crypt = B_FALSE; + puio->uio_iov = NULL; + puio->uio_iovcnt = 0; + cuio->uio_iov = NULL; + cuio->uio_iovcnt = 0; + return (ret); +} + +/* + * Special case handling routine for encrypting / decrypting dnode blocks. + */ +static int +zio_crypt_init_uios_dnode(boolean_t encrypt, uint64_t version, + uint8_t *plainbuf, uint8_t *cipherbuf, uint_t datalen, boolean_t byteswap, + zfs_uio_t *puio, zfs_uio_t *cuio, uint_t *enc_len, uint8_t **authbuf, + uint_t *auth_len, boolean_t *no_crypt) +{ + int ret; + uint_t nr_src, nr_dst, crypt_len; + uint_t aad_len = 0, nr_iovecs = 0, total_len = 0; + uint_t i, j, max_dnp = datalen >> DNODE_SHIFT; + iovec_t *src_iovecs = NULL, *dst_iovecs = NULL; + uint8_t *src, *dst, *aadp; + dnode_phys_t *dnp, *adnp, *sdnp, *ddnp; + uint8_t *aadbuf = zio_buf_alloc(datalen); + + if (encrypt) { + src = plainbuf; + dst = cipherbuf; + nr_src = 0; + nr_dst = 1; + } else { + src = cipherbuf; + dst = plainbuf; + nr_src = 1; + nr_dst = 0; + } + + sdnp = (dnode_phys_t *)src; + ddnp = (dnode_phys_t *)dst; + aadp = aadbuf; + + /* + * Count the number of iovecs we will need to do the encryption by + * counting the number of bonus buffers that need to be encrypted. + */ + for (i = 0; i < max_dnp; i += sdnp[i].dn_extra_slots + 1) { + /* + * This block may still be byteswapped. However, all of the + * values we use are either uint8_t's (for which byteswapping + * is a noop) or a * != 0 check, which will work regardless + * of whether or not we byteswap. + */ + if (sdnp[i].dn_type != DMU_OT_NONE && + DMU_OT_IS_ENCRYPTED(sdnp[i].dn_bonustype) && + sdnp[i].dn_bonuslen != 0) { + nr_iovecs++; + } + } + + nr_src += nr_iovecs; + nr_dst += nr_iovecs; + + if (nr_src != 0) { + src_iovecs = kmem_alloc(nr_src * sizeof (iovec_t), KM_SLEEP); + if (src_iovecs == NULL) { + ret = SET_ERROR(ENOMEM); + goto error; + } + } + + if (nr_dst != 0) { + dst_iovecs = kmem_alloc(nr_dst * sizeof (iovec_t), KM_SLEEP); + if (dst_iovecs == NULL) { + ret = SET_ERROR(ENOMEM); + goto error; + } + } + + nr_iovecs = 0; + + /* + * Iterate through the dnodes again, this time filling in the uios + * we allocated earlier. We also concatenate any data we want to + * authenticate onto aadbuf. + */ + for (i = 0; i < max_dnp; i += sdnp[i].dn_extra_slots + 1) { + dnp = &sdnp[i]; + + /* copy over the core fields and blkptrs (kept as plaintext) */ + bcopy(dnp, &ddnp[i], (uint8_t *)DN_BONUS(dnp) - (uint8_t *)dnp); + + if (dnp->dn_flags & DNODE_FLAG_SPILL_BLKPTR) { + bcopy(DN_SPILL_BLKPTR(dnp), DN_SPILL_BLKPTR(&ddnp[i]), + sizeof (blkptr_t)); + } + + /* + * Handle authenticated data. We authenticate everything in + * the dnode that can be brought over when we do a raw send. + * This includes all of the core fields as well as the MACs + * stored in the bp checksums and all of the portable bits + * from blk_prop. We include the dnode padding here in case it + * ever gets used in the future. Some dn_flags and dn_used are + * not portable so we mask those out values out of the + * authenticated data. + */ + crypt_len = offsetof(dnode_phys_t, dn_blkptr); + bcopy(dnp, aadp, crypt_len); + adnp = (dnode_phys_t *)aadp; + adnp->dn_flags &= DNODE_CRYPT_PORTABLE_FLAGS_MASK; + adnp->dn_used = 0; + aadp += crypt_len; + aad_len += crypt_len; + + for (j = 0; j < dnp->dn_nblkptr; j++) { + zio_crypt_bp_do_aad_updates(&aadp, &aad_len, + version, byteswap, &dnp->dn_blkptr[j]); + } + + if (dnp->dn_flags & DNODE_FLAG_SPILL_BLKPTR) { + zio_crypt_bp_do_aad_updates(&aadp, &aad_len, + version, byteswap, DN_SPILL_BLKPTR(dnp)); + } + + /* + * If this bonus buffer needs to be encrypted, we prepare an + * iovec_t. The encryption / decryption functions will fill + * this in for us with the encrypted or decrypted data. + * Otherwise we add the bonus buffer to the authenticated + * data buffer and copy it over to the destination. The + * encrypted iovec extends to DN_MAX_BONUS_LEN(dnp) so that + * we can guarantee alignment with the AES block size + * (128 bits). + */ + crypt_len = DN_MAX_BONUS_LEN(dnp); + if (dnp->dn_type != DMU_OT_NONE && + DMU_OT_IS_ENCRYPTED(dnp->dn_bonustype) && + dnp->dn_bonuslen != 0) { + ASSERT3U(nr_iovecs, <, nr_src); + ASSERT3U(nr_iovecs, <, nr_dst); + ASSERT3P(src_iovecs, !=, NULL); + ASSERT3P(dst_iovecs, !=, NULL); + src_iovecs[nr_iovecs].iov_base = DN_BONUS(dnp); + src_iovecs[nr_iovecs].iov_len = crypt_len; + dst_iovecs[nr_iovecs].iov_base = DN_BONUS(&ddnp[i]); + dst_iovecs[nr_iovecs].iov_len = crypt_len; + + nr_iovecs++; + total_len += crypt_len; + } else { + bcopy(DN_BONUS(dnp), DN_BONUS(&ddnp[i]), crypt_len); + bcopy(DN_BONUS(dnp), aadp, crypt_len); + aadp += crypt_len; + aad_len += crypt_len; + } + } + + *no_crypt = (nr_iovecs == 0); + *enc_len = total_len; + *authbuf = aadbuf; + *auth_len = aad_len; + + if (encrypt) { + puio->uio_iov = src_iovecs; + puio->uio_iovcnt = nr_src; + cuio->uio_iov = dst_iovecs; + cuio->uio_iovcnt = nr_dst; + } else { + puio->uio_iov = dst_iovecs; + puio->uio_iovcnt = nr_dst; + cuio->uio_iov = src_iovecs; + cuio->uio_iovcnt = nr_src; + } + + return (0); + +error: + zio_buf_free(aadbuf, datalen); + if (src_iovecs != NULL) + kmem_free(src_iovecs, nr_src * sizeof (iovec_t)); + if (dst_iovecs != NULL) + kmem_free(dst_iovecs, nr_dst * sizeof (iovec_t)); + + *enc_len = 0; + *authbuf = NULL; + *auth_len = 0; + *no_crypt = B_FALSE; + puio->uio_iov = NULL; + puio->uio_iovcnt = 0; + cuio->uio_iov = NULL; + cuio->uio_iovcnt = 0; + return (ret); +} + +static int +zio_crypt_init_uios_normal(boolean_t encrypt, uint8_t *plainbuf, + uint8_t *cipherbuf, uint_t datalen, zfs_uio_t *puio, zfs_uio_t *cuio, + uint_t *enc_len) +{ + int ret; + uint_t nr_plain = 1, nr_cipher = 2; + iovec_t *plain_iovecs = NULL, *cipher_iovecs = NULL; + + /* allocate the iovecs for the plain and cipher data */ + plain_iovecs = kmem_alloc(nr_plain * sizeof (iovec_t), + KM_SLEEP); + if (!plain_iovecs) { + ret = SET_ERROR(ENOMEM); + goto error; + } + + cipher_iovecs = kmem_alloc(nr_cipher * sizeof (iovec_t), + KM_SLEEP); + if (!cipher_iovecs) { + ret = SET_ERROR(ENOMEM); + goto error; + } + + plain_iovecs[0].iov_base = plainbuf; + plain_iovecs[0].iov_len = datalen; + cipher_iovecs[0].iov_base = cipherbuf; + cipher_iovecs[0].iov_len = datalen; + + *enc_len = datalen; + puio->uio_iov = plain_iovecs; + puio->uio_iovcnt = nr_plain; + cuio->uio_iov = cipher_iovecs; + cuio->uio_iovcnt = nr_cipher; + + return (0); + +error: + if (plain_iovecs != NULL) + kmem_free(plain_iovecs, nr_plain * sizeof (iovec_t)); + if (cipher_iovecs != NULL) + kmem_free(cipher_iovecs, nr_cipher * sizeof (iovec_t)); + + *enc_len = 0; + puio->uio_iov = NULL; + puio->uio_iovcnt = 0; + cuio->uio_iov = NULL; + cuio->uio_iovcnt = 0; + return (ret); +} + +/* + * This function builds up the plaintext (puio) and ciphertext (cuio) uios so + * that they can be used for encryption and decryption by zio_do_crypt_uio(). + * Most blocks will use zio_crypt_init_uios_normal(), with ZIL and dnode blocks + * requiring special handling to parse out pieces that are to be encrypted. The + * authbuf is used by these special cases to store additional authenticated + * data (AAD) for the encryption modes. + */ +static int +zio_crypt_init_uios(boolean_t encrypt, uint64_t version, dmu_object_type_t ot, + uint8_t *plainbuf, uint8_t *cipherbuf, uint_t datalen, boolean_t byteswap, + uint8_t *mac, zfs_uio_t *puio, zfs_uio_t *cuio, uint_t *enc_len, + uint8_t **authbuf, uint_t *auth_len, boolean_t *no_crypt) +{ + int ret; + iovec_t *mac_iov; + + ASSERT(DMU_OT_IS_ENCRYPTED(ot) || ot == DMU_OT_NONE); + + /* route to handler */ + switch (ot) { + case DMU_OT_INTENT_LOG: + ret = zio_crypt_init_uios_zil(encrypt, plainbuf, cipherbuf, + datalen, byteswap, puio, cuio, enc_len, authbuf, auth_len, + no_crypt); + break; + case DMU_OT_DNODE: + ret = zio_crypt_init_uios_dnode(encrypt, version, plainbuf, + cipherbuf, datalen, byteswap, puio, cuio, enc_len, authbuf, + auth_len, no_crypt); + break; + default: + ret = zio_crypt_init_uios_normal(encrypt, plainbuf, cipherbuf, + datalen, puio, cuio, enc_len); + *authbuf = NULL; + *auth_len = 0; + *no_crypt = B_FALSE; + break; + } + + if (ret != 0) + goto error; + + /* populate the uios */ + puio->uio_segflg = UIO_SYSSPACE; + cuio->uio_segflg = UIO_SYSSPACE; + + mac_iov = ((iovec_t *)&cuio->uio_iov[cuio->uio_iovcnt - 1]); + mac_iov->iov_base = mac; + mac_iov->iov_len = ZIO_DATA_MAC_LEN; + + return (0); + +error: + return (ret); +} + +/* + * Primary encryption / decryption entrypoint for zio data. + */ +int +zio_do_crypt_data(boolean_t encrypt, zio_crypt_key_t *key, + dmu_object_type_t ot, boolean_t byteswap, uint8_t *salt, uint8_t *iv, + uint8_t *mac, uint_t datalen, uint8_t *plainbuf, uint8_t *cipherbuf, + boolean_t *no_crypt) +{ + int ret; + boolean_t locked = B_FALSE; + uint64_t crypt = key->zk_crypt; + uint_t keydata_len = zio_crypt_table[crypt].ci_keylen; + uint_t enc_len, auth_len; + zfs_uio_t puio, cuio; + uint8_t enc_keydata[MASTER_KEY_MAX_LEN]; + crypto_key_t tmp_ckey, *ckey = NULL; + crypto_ctx_template_t tmpl; + uint8_t *authbuf = NULL; + + /* + * If the needed key is the current one, just use it. Otherwise we + * need to generate a temporary one from the given salt + master key. + * If we are encrypting, we must return a copy of the current salt + * so that it can be stored in the blkptr_t. + */ + rw_enter(&key->zk_salt_lock, RW_READER); + locked = B_TRUE; + + if (bcmp(salt, key->zk_salt, ZIO_DATA_SALT_LEN) == 0) { + ckey = &key->zk_current_key; + tmpl = key->zk_current_tmpl; + } else { + rw_exit(&key->zk_salt_lock); + locked = B_FALSE; + + ret = hkdf_sha512(key->zk_master_keydata, keydata_len, NULL, 0, + salt, ZIO_DATA_SALT_LEN, enc_keydata, keydata_len); + if (ret != 0) + goto error; + + tmp_ckey.ck_format = CRYPTO_KEY_RAW; + tmp_ckey.ck_data = enc_keydata; + tmp_ckey.ck_length = CRYPTO_BYTES2BITS(keydata_len); + + ckey = &tmp_ckey; + tmpl = NULL; + } + + /* + * Attempt to use QAT acceleration if we can. We currently don't + * do this for metadnode and ZIL blocks, since they have a much + * more involved buffer layout and the qat_crypt() function only + * works in-place. + */ + if (qat_crypt_use_accel(datalen) && + ot != DMU_OT_INTENT_LOG && ot != DMU_OT_DNODE) { + uint8_t *srcbuf, *dstbuf; + + if (encrypt) { + srcbuf = plainbuf; + dstbuf = cipherbuf; + } else { + srcbuf = cipherbuf; + dstbuf = plainbuf; + } + + ret = qat_crypt((encrypt) ? QAT_ENCRYPT : QAT_DECRYPT, srcbuf, + dstbuf, NULL, 0, iv, mac, ckey, key->zk_crypt, datalen); + if (ret == CPA_STATUS_SUCCESS) { + if (locked) { + rw_exit(&key->zk_salt_lock); + locked = B_FALSE; + } + + return (0); + } + /* If the hardware implementation fails fall back to software */ + } + + bzero(&puio, sizeof (zfs_uio_t)); + bzero(&cuio, sizeof (zfs_uio_t)); + + /* create uios for encryption */ + ret = zio_crypt_init_uios(encrypt, key->zk_version, ot, plainbuf, + cipherbuf, datalen, byteswap, mac, &puio, &cuio, &enc_len, + &authbuf, &auth_len, no_crypt); + if (ret != 0) + goto error; + + /* perform the encryption / decryption in software */ + ret = zio_do_crypt_uio(encrypt, key->zk_crypt, ckey, tmpl, iv, enc_len, + &puio, &cuio, authbuf, auth_len); + if (ret != 0) + goto error; + + if (locked) { + rw_exit(&key->zk_salt_lock); + locked = B_FALSE; + } + + if (authbuf != NULL) + zio_buf_free(authbuf, datalen); + if (ckey == &tmp_ckey) + bzero(enc_keydata, keydata_len); + zio_crypt_destroy_uio(&puio); + zio_crypt_destroy_uio(&cuio); + + return (0); + +error: + if (locked) + rw_exit(&key->zk_salt_lock); + if (authbuf != NULL) + zio_buf_free(authbuf, datalen); + if (ckey == &tmp_ckey) + bzero(enc_keydata, keydata_len); + zio_crypt_destroy_uio(&puio); + zio_crypt_destroy_uio(&cuio); + + return (ret); +} + +/* + * Simple wrapper around zio_do_crypt_data() to work with abd's instead of + * linear buffers. + */ +int +zio_do_crypt_abd(boolean_t encrypt, zio_crypt_key_t *key, dmu_object_type_t ot, + boolean_t byteswap, uint8_t *salt, uint8_t *iv, uint8_t *mac, + uint_t datalen, abd_t *pabd, abd_t *cabd, boolean_t *no_crypt) +{ + int ret; + void *ptmp, *ctmp; + + if (encrypt) { + ptmp = abd_borrow_buf_copy(pabd, datalen); + ctmp = abd_borrow_buf(cabd, datalen); + } else { + ptmp = abd_borrow_buf(pabd, datalen); + ctmp = abd_borrow_buf_copy(cabd, datalen); + } + + ret = zio_do_crypt_data(encrypt, key, ot, byteswap, salt, iv, mac, + datalen, ptmp, ctmp, no_crypt); + if (ret != 0) + goto error; + + if (encrypt) { + abd_return_buf(pabd, ptmp, datalen); + abd_return_buf_copy(cabd, ctmp, datalen); + } else { + abd_return_buf_copy(pabd, ptmp, datalen); + abd_return_buf(cabd, ctmp, datalen); + } + + return (0); + +error: + if (encrypt) { + abd_return_buf(pabd, ptmp, datalen); + abd_return_buf_copy(cabd, ctmp, datalen); + } else { + abd_return_buf_copy(pabd, ptmp, datalen); + abd_return_buf(cabd, ctmp, datalen); + } + + return (ret); +} + +#if defined(_KERNEL) +/* BEGIN CSTYLED */ +module_param(zfs_key_max_salt_uses, ulong, 0644); +MODULE_PARM_DESC(zfs_key_max_salt_uses, "Max number of times a salt value " + "can be used for generating encryption keys before it is rotated"); +/* END CSTYLED */ +#endif diff --git a/module/os/macos/zfs/zvolIO.cpp b/module/os/macos/zfs/zvolIO.cpp new file mode 100644 index 000000000000..249b57032f6f --- /dev/null +++ b/module/os/macos/zfs/zvolIO.cpp @@ -0,0 +1,1174 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2013-2020, Jorgen Lundman. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * ZVOL Device + */ + +// Define the superclass +#define super IOBlockStorageDevice + +#define ZVOL_BSIZE DEV_BSIZE + +static const char *ZVOL_PRODUCT_NAME_PREFIX = "ZVOL "; + +/* Wrapper for zvol_state pointer to IOKit device */ +typedef struct zvol_iokit { + org_openzfsonosx_zfs_zvol_device *dev; +} zvol_iokit_t; + +OSDefineMetaClassAndStructors(org_openzfsonosx_zfs_zvol_device, IOBlockStorageDevice) + +bool +org_openzfsonosx_zfs_zvol_device::init(zvol_state_t *c_zv, + OSDictionary *properties) +{ + zvol_iokit_t *iokitdev = NULL; + + dprintf("zvolIO_device:init\n"); + + if (!c_zv || c_zv->zv_zso->zvo_iokitdev != NULL) { + dprintf("zvol %s invalid c_zv\n", __func__); + return (false); + } + + if ((iokitdev = (zvol_iokit_t *)kmem_alloc(sizeof (zvol_iokit_t), + KM_SLEEP)) == NULL) { + printf("zvol %s wrapper alloc failed\n", __func__); + return (false); + } + + if (super::init(properties) == false) { + printf("zvol %s super init failed\n", __func__); + kmem_free(iokitdev, sizeof (zvol_iokit_t)); + return (false); + } + + /* Store reference to zvol_state_t in the iokitdev */ + zv = c_zv; + /* Store reference to iokitdev in zvol_state_t */ + iokitdev->dev = this; + + /* Assign to zv once completely initialized */ + zv->zv_zso->zvo_iokitdev = iokitdev; + + /* Apply the name from the full dataset path */ + if (strlen(zv->zv_name) != 0) { + setName(zv->zv_name); + } + + return (true); +} + +bool +org_openzfsonosx_zfs_zvol_device::attach(IOService* provider) +{ + OSDictionary *protocolCharacteristics = 0; + OSDictionary *deviceCharacteristics = 0; + OSDictionary *storageFeatures = 0; + OSBoolean *unmapFeature = 0; + const OSSymbol *propSymbol = 0; + OSString *dataString = 0; + OSNumber *dataNumber = 0; + + char product_name[strlen(ZVOL_PRODUCT_NAME_PREFIX) + MAXPATHLEN + 1]; + + if (!provider) { + dprintf("ZVOL attach missing provider\n"); + return (false); + } + + if (super::attach(provider) == false) + return (false); + + /* + * We want to set some additional properties for ZVOLs, in + * particular, "Virtual Device", and type "File" + * (or is Internal better?) + * + * Finally "Generic" type. + * + * These properties are defined in *protocol* characteristics + */ + + protocolCharacteristics = OSDictionary::withCapacity(3); + + if (!protocolCharacteristics) { + IOLog("failed to create dict for protocolCharacteristics.\n"); + return (true); + } + + propSymbol = OSSymbol::withCString( + kIOPropertyPhysicalInterconnectTypeVirtual); + + if (!propSymbol) { + IOLog("could not create interconnect type string\n"); + return (true); + } + protocolCharacteristics->setObject( + kIOPropertyPhysicalInterconnectTypeKey, propSymbol); + + propSymbol->release(); + propSymbol = 0; + + propSymbol = OSSymbol::withCString(kIOPropertyInterconnectFileKey); + if (!propSymbol) { + IOLog("could not create interconnect location string\n"); + return (true); + } + protocolCharacteristics->setObject( + kIOPropertyPhysicalInterconnectLocationKey, propSymbol); + + propSymbol->release(); + propSymbol = 0; + + setProperty(kIOPropertyProtocolCharacteristicsKey, + protocolCharacteristics); + + protocolCharacteristics->release(); + protocolCharacteristics = 0; + + /* + * We want to set some additional properties for ZVOLs, in + * particular, physical block size (volblocksize) of the + * underlying ZVOL, and 'logical' block size presented by + * the virtual disk. Also set physical bytes per sector. + * + * These properties are defined in *device* characteristics + */ + + deviceCharacteristics = OSDictionary::withCapacity(3); + + if (!deviceCharacteristics) { + IOLog("failed to create dict for deviceCharacteristics.\n"); + return (true); + } + + /* Set this device to be an SSD, for priority and VM paging */ + propSymbol = OSSymbol::withCString( + kIOPropertyMediumTypeSolidStateKey); + if (!propSymbol) { + IOLog("could not create medium type string\n"); + return (true); + } + deviceCharacteristics->setObject(kIOPropertyMediumTypeKey, + propSymbol); + + propSymbol->release(); + propSymbol = 0; + + /* Set logical block size to ZVOL_BSIZE (512b) */ + dataNumber = OSNumber::withNumber(ZVOL_BSIZE, + 8 * sizeof (ZVOL_BSIZE)); + + deviceCharacteristics->setObject(kIOPropertyLogicalBlockSizeKey, + dataNumber); + + dprintf("logicalBlockSize %llu\n", + dataNumber->unsigned64BitValue()); + + dataNumber->release(); + dataNumber = 0; + + /* Set physical block size to match volblocksize property */ + dataNumber = OSNumber::withNumber(zv->zv_volblocksize, + 8 * sizeof (zv->zv_volblocksize)); + + deviceCharacteristics->setObject(kIOPropertyPhysicalBlockSizeKey, + dataNumber); + + dprintf("physicalBlockSize %llu\n", + dataNumber->unsigned64BitValue()); + + dataNumber->release(); + dataNumber = 0; + + /* Set physical bytes per sector to match volblocksize property */ + dataNumber = OSNumber::withNumber((uint64_t)(zv->zv_volblocksize), + 8 * sizeof (uint64_t)); + + deviceCharacteristics->setObject(kIOPropertyBytesPerPhysicalSectorKey, + dataNumber); + + dprintf("physicalBytesPerSector %llu\n", + dataNumber->unsigned64BitValue()); + + dataNumber->release(); + dataNumber = 0; + + /* Publish the Device / Media name */ + (void) snprintf(product_name, sizeof (product_name), "%s%s", + ZVOL_PRODUCT_NAME_PREFIX, zv->zv_name); + dataString = OSString::withCString(product_name); + deviceCharacteristics->setObject(kIOPropertyProductNameKey, dataString); + dataString->release(); + dataString = 0; + + /* Apply these characteristics */ + setProperty(kIOPropertyDeviceCharacteristicsKey, + deviceCharacteristics); + + deviceCharacteristics->release(); + deviceCharacteristics = 0; + + /* + * ZVOL unmap support + * + * These properties are defined in IOStorageFeatures + */ + + storageFeatures = OSDictionary::withCapacity(1); + if (!storageFeatures) { + IOLog("failed to create dictionary for storageFeatures.\n"); + return (true); + } + + /* Set unmap feature */ + unmapFeature = OSBoolean::withBoolean(true); + storageFeatures->setObject(kIOStorageFeatureUnmap, unmapFeature); + unmapFeature->release(); + unmapFeature = 0; + + /* Apply these storage features */ + setProperty(kIOStorageFeaturesKey, storageFeatures); + storageFeatures->release(); + storageFeatures = 0; + + + /* + * Set transfer limits: + * + * Maximum transfer size (bytes) + * Maximum transfer block count + * Maximum transfer block size (bytes) + * Maximum transfer segment count + * Maximum transfer segment size (bytes) + * Minimum transfer segment size (bytes) + * + * We will need to establish safe defaults for all / per volblocksize + * + * Example: setProperty(kIOMinimumSegmentAlignmentByteCountKey, 1, 1); + */ + + /* + * Finally "Generic" type, set as a device property. Tried setting this + * to the string "ZVOL" however the OS does not recognize it as a block + * storage device. This would probably be possible by extending the + * IOBlockStorage Device / Driver relationship. + */ + + setProperty(kIOBlockStorageDeviceTypeKey, + kIOBlockStorageDeviceTypeGeneric); + + return (true); +} + +int +org_openzfsonosx_zfs_zvol_device::renameDevice(void) +{ + OSDictionary *deviceDict; + OSString *nameStr; + char *newstr; + int len; + + /* Length of string and null terminating character */ + len = strlen(ZVOL_PRODUCT_NAME_PREFIX) + strlen(zv->zv_name) + 1; + newstr = (char *)kmem_alloc(len, KM_SLEEP); + if (!newstr) { + dprintf("%s string alloc failed\n", __func__); + return (ENOMEM); + } + + /* Append prefix and dsl name */ + snprintf(newstr, len, "%s%s", ZVOL_PRODUCT_NAME_PREFIX, zv->zv_name); + nameStr = OSString::withCString(newstr); + kmem_free(newstr, len); + + if (!nameStr) { + dprintf("%s couldn't allocate name string\n", __func__); + return (ENOMEM); + } + + /* Fetch current device characteristics dictionary */ + deviceDict = OSDynamicCast(OSDictionary, + getProperty(kIOPropertyDeviceCharacteristicsKey)); + if (!deviceDict || (deviceDict = + OSDictionary::withDictionary(deviceDict)) == NULL) { + dprintf("couldn't clone device characteristics\n"); + /* Allocate new dict */ + if (!deviceDict && + (deviceDict = OSDictionary::withCapacity(1)) == NULL) { + dprintf("%s OSDictionary alloc failed\n", __func__); + nameStr->release(); + return (ENOMEM); + } + + } + + /* Add or replace the product name */ + if (deviceDict->setObject(kIOPropertyProductNameKey, + nameStr) == false) { + dprintf("%s couldn't set product name\n", __func__); + nameStr->release(); + deviceDict->release(); + return (ENXIO); + } + nameStr->release(); + nameStr = 0; + + /* Set IORegistry property */ + if (setProperty(kIOPropertyDeviceCharacteristicsKey, + deviceDict) == false) { + dprintf("%s couldn't set IORegistry property\n", __func__); + deviceDict->release(); + return (ENXIO); + } + deviceDict->release(); + deviceDict = 0; + + /* Apply the name from the full dataset path */ + setName(zv->zv_name); + + return (0); +} + +int +org_openzfsonosx_zfs_zvol_device::offlineDevice(void) +{ + IOService *client; + + if ((client = this->getClient()) == NULL) { + return (ENOENT); + } + + /* Ask IOBlockStorageDevice to offline media */ + if (client->message(kIOMessageMediaStateHasChanged, + this, (void *)kIOMediaStateOffline) != kIOReturnSuccess) { + dprintf("%s failed\n", __func__); + return (ENXIO); + } + + return (0); +} + +int +org_openzfsonosx_zfs_zvol_device::onlineDevice(void) +{ + IOService *client; + + if ((client = this->getClient()) == NULL) { + return (ENOENT); + } + + /* Ask IOBlockStorageDevice to online media */ + if (client->message(kIOMessageMediaStateHasChanged, + this, (void *)kIOMediaStateOnline) != kIOReturnSuccess) { + dprintf("%s failed\n", __func__); + return (ENXIO); + } + + return (0); +} + +int +org_openzfsonosx_zfs_zvol_device::refreshDevice(void) +{ + IOService *client; + + if ((client = this->getClient()) == NULL) { + return (ENOENT); + } + + /* Ask IOBlockStorageDevice to reset the media params */ + if (client->message(kIOMessageMediaParametersHaveChanged, + this) != kIOReturnSuccess) { + dprintf("%s failed\n", __func__); + return (ENXIO); + } + + return (0); +} + +int +org_openzfsonosx_zfs_zvol_device::getBSDName(void) +{ + IORegistryEntry *ioregdevice = 0; + OSObject *bsdnameosobj = 0; + OSString* bsdnameosstr = 0; + + ioregdevice = OSDynamicCast(IORegistryEntry, this); + + if (!ioregdevice) + return (-1); + + bsdnameosobj = ioregdevice->getProperty(kIOBSDNameKey, + gIOServicePlane, kIORegistryIterateRecursively); + + if (!bsdnameosobj) + return (-1); + + bsdnameosstr = OSDynamicCast(OSString, bsdnameosobj); + + IOLog("zvol: bsd name is '%s'\n", + bsdnameosstr->getCStringNoCopy()); + + if (!zv) + return (-1); + + zv->zv_zso->zvo_bsdname[0] = 'r'; // for 'rdiskX'. + strlcpy(&zv->zv_zso->zvo_bsdname[1], + bsdnameosstr->getCStringNoCopy(), + sizeof (zv->zv_zso->zvo_bsdname)-1); + /* + * IOLog("name assigned '%s'\n", zv->zv_zso->zvo_bsdname); + */ + + return (0); +} + +void +org_openzfsonosx_zfs_zvol_device::detach(IOService *provider) +{ + super::detach(provider); +} + +void +org_openzfsonosx_zfs_zvol_device::clearState(void) +{ + zv = NULL; +} + +bool +org_openzfsonosx_zfs_zvol_device::handleOpen(IOService *client, + IOOptionBits options, void *argument) +{ + IOStorageAccess access = (uintptr_t)argument; + bool ret = false; + int openflags = 0; + + if (super::handleOpen(client, options, argument) == false) + return (false); + + /* Device terminating? */ + if (zv == NULL || + zv->zv_zso == NULL || + zv->zv_zso->zvo_iokitdev == NULL) + return (false); + + if (access & kIOStorageAccessReaderWriter) { + openflags = FWRITE | ZVOL_EXCL; + } else { + openflags = FREAD; + } + + /* + * Don't use 'zv' until it has been verified by zvol_os_open_zv() + * and returned as opened, then it holds an open count and can be + * used. + */ + + if (zvol_os_open_zv(zv, zv->zv_zso->zvo_openflags, 0, NULL) == 0) { + ret = true; + } + + if (ret) + zv->zv_zso->zvo_openflags = openflags; + + + dprintf("Open %s (openflags %llx)\n", (ret ? "done" : "failed"), + ret ? zv->zv_zso->zvo_openflags : 0); + + if (ret == false) + super::handleClose(client, options); + + return (ret); +} + +void +org_openzfsonosx_zfs_zvol_device::handleClose(IOService *client, + IOOptionBits options) +{ + super::handleClose(client, options); + + /* Terminating ? */ + if (zv == NULL || + zv->zv_zso == NULL || + zv->zv_zso->zvo_iokitdev == NULL) + return; + + zvol_os_close_zv(zv, zv->zv_zso->zvo_openflags, 0, NULL); + +} + +IOReturn +org_openzfsonosx_zfs_zvol_device::doAsyncReadWrite( + IOMemoryDescriptor *buffer, UInt64 block, UInt64 nblks, + IOStorageAttributes *attributes, IOStorageCompletion *completion) +{ + IODirection direction; + IOByteCount actualByteCount; + struct iomem iomem; + iomem.buf = NULL; + + // Return errors for incoming I/O if we have been terminated. + if (isInactive() == true) { + dprintf("asyncReadWrite notActive fail\n"); + return (kIOReturnNotAttached); + } + + // These variables are set in zvol_first_open(), which should have been + // called already. + if (!zv->zv_dn) { + dprintf("asyncReadWrite no zvol dnode\n"); + return (kIOReturnNotAttached); + } + + // Ensure the start block is within the disk capacity. + if ((block)*(ZVOL_BSIZE) >= zv->zv_volsize) { + dprintf("asyncReadWrite start block outside volume\n"); + return (kIOReturnBadArgument); + } + + // Shorten the read, if beyond the end + if (((block + nblks)*(ZVOL_BSIZE)) > zv->zv_volsize) { + dprintf("asyncReadWrite block shortening needed\n"); + return (kIOReturnBadArgument); + } + + // Get the buffer direction, whether this is a read or a write. + direction = buffer->getDirection(); + if ((direction != kIODirectionIn) && (direction != kIODirectionOut)) { + dprintf("asyncReadWrite kooky direction\n"); + return (kIOReturnBadArgument); + } + + // dprintf("%s offset @block %llu numblocks %llu: blksz %u\n", + // direction == kIODirectionIn ? "Read" : "Write", + // block, nblks, (ZVOL_BSIZE)); + + /* Perform the read or write operation through the transport driver. */ + actualByteCount = (nblks*(ZVOL_BSIZE)); + + iomem.buf = buffer; + + /* Make sure we don't go away while the command is being executed */ + /* Open should be holding a retain */ + + if (direction == kIODirectionIn) { + + if (zvol_os_read_zv(zv, (block*(ZVOL_BSIZE)), + actualByteCount, &iomem)) { + + actualByteCount = 0; + } + + } else { + + if (zvol_os_write_zv(zv, (block*(ZVOL_BSIZE)), + actualByteCount, &iomem)) { + actualByteCount = 0; + } + + } + + /* Open should be holding a retain */ + iomem.buf = NULL; // overkill + + if (actualByteCount != nblks*(ZVOL_BSIZE)) + dprintf("Read/Write operation failed\n"); + + // Call the completion function. + (completion->action)(completion->target, completion->parameter, + kIOReturnSuccess, actualByteCount); + + return (kIOReturnSuccess); +} + +IOReturn +org_openzfsonosx_zfs_zvol_device::doDiscard(UInt64 block, UInt64 nblks) +{ + dprintf("doDiscard called with block, nblks (%llu, %llu)\n", + block, nblks); + uint64_t bytes = 0; + uint64_t off = 0; + + /* Convert block/nblks to offset/bytes */ + off = block * ZVOL_BSIZE; + bytes = nblks * ZVOL_BSIZE; + dprintf("calling zvol_unmap with offset, bytes (%llu, %llu)\n", + off, bytes); + + if (zvol_os_unmap(zv, off, bytes) == 0) + return (kIOReturnSuccess); + else + return (kIOReturnError); +} + + +IOReturn +org_openzfsonosx_zfs_zvol_device::doUnmap(IOBlockStorageDeviceExtent *extents, + UInt32 extentsCount, UInt32 options = 0) +{ + UInt32 i = 0; + IOReturn result; + + dprintf("doUnmap called with (%u) extents and options (%u)\n", + (uint32_t)extentsCount, (uint32_t)options); + + if (options > 0 || !extents) { + return (kIOReturnUnsupported); + } + + for (i = 0; i < extentsCount; i++) { + + result = doDiscard(extents[i].blockStart, + extents[i].blockCount); + + if (result != kIOReturnSuccess) { + return (result); + } + } + + return (kIOReturnSuccess); +} + +UInt32 +org_openzfsonosx_zfs_zvol_device::doGetFormatCapacities(UInt64* capacities, + UInt32 capacitiesMaxCount) const +{ + dprintf("formatCap\n"); + + /* + * Ensure that the array is sufficient to hold all our formats + * (we require one element). + */ + if ((capacities != NULL) && (capacitiesMaxCount < 1)) + return (0); + /* Error, return an array size of 0. */ + + /* + * The caller may provide a NULL array if it wishes to query the number + * of formats that we support. + */ + if (capacities != NULL) + capacities[0] = zv->zv_volsize; + + dprintf("returning capacity[0] size %llu\n", zv->zv_volsize); + + return (1); +} + +char * +org_openzfsonosx_zfs_zvol_device::getProductString(void) +{ + dprintf("getProduct %p\n", zv); + + if (zv) + return (zv->zv_name); + + return ((char *)"ZVolume"); +} + +IOReturn +org_openzfsonosx_zfs_zvol_device::reportBlockSize(UInt64 *blockSize) +{ + dprintf("reportBlockSize %llu\n", *blockSize); + + if (blockSize) *blockSize = (ZVOL_BSIZE); + + return (kIOReturnSuccess); +} + +IOReturn +org_openzfsonosx_zfs_zvol_device::reportMaxValidBlock(UInt64 *maxBlock) +{ + dprintf("reportMaxValidBlock %llu\n", *maxBlock); + + if (maxBlock) *maxBlock = ((zv->zv_volsize / (ZVOL_BSIZE)) - 1); + + return (kIOReturnSuccess); +} + +IOReturn +org_openzfsonosx_zfs_zvol_device::reportMediaState(bool *mediaPresent, + bool *changedState) +{ + dprintf("reportMediaState\n"); + if (mediaPresent) *mediaPresent = true; + if (changedState) *changedState = false; + return (kIOReturnSuccess); +} + +IOReturn +org_openzfsonosx_zfs_zvol_device::reportPollRequirements(bool *pollRequired, + bool *pollIsExpensive) +{ + dprintf("reportPollReq\n"); + if (pollRequired) *pollRequired = false; + if (pollIsExpensive) *pollIsExpensive = false; + return (kIOReturnSuccess); +} + +IOReturn +org_openzfsonosx_zfs_zvol_device::reportRemovability(bool *isRemovable) +{ + dprintf("reportRemova\n"); + if (isRemovable) *isRemovable = false; + return (kIOReturnSuccess); +} + +IOReturn +org_openzfsonosx_zfs_zvol_device::doEjectMedia(void) +{ + dprintf("ejectMedia\n"); +/* XXX */ + // Only 10.6 needs special work to eject + // if ((version_major == 10) && (version_minor == 8)) + // destroyBlockStorageDevice(zvol); + // } + + return (kIOReturnSuccess); +} + +IOReturn +org_openzfsonosx_zfs_zvol_device::doFormatMedia(UInt64 byteCapacity) +{ + dprintf("doFormat\n"); + return (kIOReturnSuccess); +} + +IOReturn +org_openzfsonosx_zfs_zvol_device::doLockUnlockMedia(bool doLock) +{ + dprintf("doLockUnlock\n"); + return (kIOReturnSuccess); +} + +IOReturn +org_openzfsonosx_zfs_zvol_device::doSynchronizeCache(void) +{ + dprintf("doSync\n"); + if (zv && zv->zv_zilog) { + zil_commit(zv->zv_zilog, ZVOL_OBJ); + } + return (kIOReturnSuccess); +} + +char * +org_openzfsonosx_zfs_zvol_device::getVendorString(void) +{ + dprintf("getVendor\n"); + return ((char *)"ZVOL"); +} + +char * +org_openzfsonosx_zfs_zvol_device::getRevisionString(void) +{ + dprintf("getRevision\n"); + return ((char *)ZFS_META_VERSION); +} + +char * +org_openzfsonosx_zfs_zvol_device::getAdditionalDeviceInfoString(void) +{ + dprintf("getAdditional\n"); + return ((char *)"ZFS Volume"); +} + +IOReturn +org_openzfsonosx_zfs_zvol_device::reportEjectability(bool *isEjectable) +{ + dprintf("reportEjecta\n"); + /* + * Which do we prefer? If you eject it, you can't get volume back until + * you import it again. + */ + + if (isEjectable) *isEjectable = false; + return (kIOReturnSuccess); +} + +/* XXX deprecated function */ +IOReturn +org_openzfsonosx_zfs_zvol_device::reportLockability(bool *isLockable) +{ + dprintf("reportLocka\n"); + if (isLockable) *isLockable = true; + return (kIOReturnSuccess); +} + +IOReturn +org_openzfsonosx_zfs_zvol_device::reportWriteProtection(bool *isWriteProtected) +{ + dprintf("reportWritePro: %d\n", *isWriteProtected); + + if (!isWriteProtected) + return (kIOReturnSuccess); + + if (zv && (zv->zv_flags & ZVOL_RDONLY)) + *isWriteProtected = true; + else + *isWriteProtected = false; + + return (kIOReturnSuccess); +} + +IOReturn +org_openzfsonosx_zfs_zvol_device::getWriteCacheState(bool *enabled) +{ + dprintf("getCacheState\n"); + if (enabled) *enabled = true; + return (kIOReturnSuccess); +} + +IOReturn +org_openzfsonosx_zfs_zvol_device::setWriteCacheState(bool enabled) +{ + dprintf("setWriteCache\n"); + return (kIOReturnSuccess); +} + +extern "C" { + +/* C interfaces */ +int +zvolCreateNewDevice(zvol_state_t *zv) +{ + org_openzfsonosx_zfs_zvol_device *zvol; + ZFSPool *pool_proxy; + spa_t *spa; + dprintf("%s\n", __func__); + + /* We must have a valid zvol_state_t */ + if (!zv || !zv->zv_objset) { + dprintf("%s missing zv or objset\n", __func__); + return (EINVAL); + } + + /* We need the spa to get the pool proxy */ + if ((spa = dmu_objset_spa(zv->zv_objset)) == NULL) { + dprintf("%s couldn't get spa\n", __func__); + return (EINVAL); + } + if (spa->spa_iokit_proxy == NULL || + (pool_proxy = spa->spa_iokit_proxy->proxy) == NULL) { + dprintf("%s missing IOKit pool proxy\n", __func__); + return (EINVAL); + } + + zvol = new org_openzfsonosx_zfs_zvol_device; + + /* Validate creation, initialize and attach */ + if (!zvol || zvol->init(zv) == false || + zvol->attach(pool_proxy) == false) { + dprintf("%s device creation failed\n", __func__); + if (zvol) zvol->release(); + return (ENOMEM); + } + /* Start the service */ + if (zvol->start(pool_proxy) == false) { + dprintf("%s device start failed\n", __func__); + zvol->detach(pool_proxy); + zvol->release(); + return (ENXIO); + } + + /* Open pool_proxy provider */ + if (pool_proxy->open(zvol) == false) { + dprintf("%s open provider failed\n", __func__); + zvol->stop(pool_proxy); + zvol->detach(pool_proxy); + zvol->release(); + return (ENXIO); + } + /* Is retained by provider */ + zvol->release(); + zvol = 0; + + return (0); +} + +int +zvolRegisterDevice(zvol_state_t *zv) +{ + org_openzfsonosx_zfs_zvol_device *zvol; + OSDictionary *matching; + IOService *service = 0; + IOMedia *media = 0; + OSString *nameStr = 0, *bsdName = 0; + uint64_t timeout = (5ULL * kSecondScale); + bool ret = false; + + if (!zv || !zv->zv_zso->zvo_iokitdev || zv->zv_name[0] == 0) { + dprintf("%s missing zv, iokitdev, or name\n", __func__); + return (EINVAL); + } + + if ((zvol = zv->zv_zso->zvo_iokitdev->dev) == NULL) { + dprintf("%s couldn't get zvol device\n", __func__); + return (EINVAL); + } + + if (!zvol->getVendorString()) { + return (EINVAL); + } + + /* Create matching string and dictionary */ + { + char str[MAXNAMELEN]; + snprintf(str, MAXNAMELEN, "%s %s Media", + zvol->getVendorString(), zv->zv_name); + if ((nameStr = OSString::withCString(str)) == NULL) { + dprintf("%s problem with name string\n", __func__); + return (ENOMEM); + } + } + + + matching = IOService::serviceMatching("IOMedia"); + if (!matching || !matching->setObject(gIONameMatchKey, nameStr)) { + dprintf("%s couldn't get matching dictionary\n", __func__); + return (ENOMEM); + } + + /* Register device for service matching */ + zvol->registerService(kIOServiceAsynchronous); + + /* Wait for upper layer BSD client */ + dprintf("%s waiting for IOMedia\n", __func__); + /* Wait for up to 5 seconds */ + service = IOService::waitForMatchingService(matching, timeout); + dprintf("%s %s service\n", __func__, (service ? "got" : "no")); + + if (!service) { + dprintf("%s couldn't get matching service\n", __func__); + return (false); + } + + dprintf("%s casting to IOMedia\n", __func__); + media = OSDynamicCast(IOMedia, service); + + if (!media) { + dprintf("%s no IOMedia\n", __func__); + service->release(); + return (false); + } + + dprintf("%s getting IOBSDNameKey\n", __func__); + bsdName = OSDynamicCast(OSString, + media->getProperty(kIOBSDNameKey)); + + if (bsdName) { + const char *str = bsdName->getCStringNoCopy(); + dprintf("%s Got bsd name [%s]\n", + __func__, str); + zv->zv_zso->zvo_bsdname[0] = 'r'; + snprintf(zv->zv_zso->zvo_bsdname+1, + sizeof (zv->zv_zso->zvo_bsdname)-1, + "%s", str); + dprintf("%s zvol bsdname set to %s\n", __func__, + zv->zv_zso->zvo_bsdname); + zvol_add_symlink(zv, zv->zv_zso->zvo_bsdname+1, + zv->zv_zso->zvo_bsdname); + ret = true; + } else { + dprintf("%s couldn't get BSD Name\n", __func__); + } + + /* Release retain held by waitForMatchingService */ + service->release(); + + dprintf("%s complete\n", __func__); + return (ret); +} + +/* Struct passed in will be freed before returning */ +void * +zvolRemoveDevice(zvol_iokit_t *iokitdev) +{ + org_openzfsonosx_zfs_zvol_device *zvol; + dprintf("%s\n", __func__); + + if (!iokitdev) { + dprintf("%s missing argument\n", __func__); + return (NULL); + } + + zvol = iokitdev->dev; + /* Free the wrapper struct */ + kmem_free(iokitdev, sizeof (zvol_iokit_t)); + + if (zvol == NULL) { + dprintf("%s couldn't get IOKit handle\n", __func__); + return (NULL); + } + + /* Mark us as terminating */ + zvol->clearState(); + + return (zvol); +} + +/* + * zvolRemoveDevice continued.. + * terminate() will block and we can deadlock, so it is issued as a + * separate thread. Done from zvol_os.c as it is easier in C. + */ +int +zvolRemoveDeviceTerminate(void *arg) +{ + org_openzfsonosx_zfs_zvol_device *zvol = (org_openzfsonosx_zfs_zvol_device *)arg; + + IOLog("zvolRemoveDeviceTerminate\n"); + + /* Terminate */ + if (zvol->terminate(kIOServiceTerminate|kIOServiceSynchronous| + kIOServiceRequired) == false) { + IOLog("%s terminate failed\n", __func__); + } + + return (0); +} + +/* Called with zv->zv_name already updated */ +int +zvolRenameDevice(zvol_state_t *zv) +{ + org_openzfsonosx_zfs_zvol_device *zvol = NULL; + int error; + + if (!zv || strnlen(zv->zv_name, 1) == 0) { + dprintf("%s missing argument\n", __func__); + return (EINVAL); + } + + if ((zvol = zv->zv_zso->zvo_iokitdev->dev) == NULL) { + dprintf("%s couldn't get zvol device\n", __func__); + return (EINVAL); + } + + /* Set IORegistry name and property */ + if ((error = zvol->renameDevice()) != 0) { + dprintf("%s renameDevice error %d\n", __func__, error); + return (error); + } + + /* + * XXX This works, but if there is a volume mounted on + * the zvol at the time it is uncleanly ejected. + * We just need to add diskutil unmount to `zfs rename`, + * like zpool export. + */ + /* Inform clients of this device that name has changed */ + if (zvol->offlineDevice() != 0 || + zvol->onlineDevice() != 0) { + dprintf("%s media reset failed\n", __func__); + return (ENXIO); + } + + return (0); +} + +/* Called with zvol volsize already updated */ +int +zvolSetVolsize(zvol_state_t *zv) +{ + org_openzfsonosx_zfs_zvol_device *zvol; + int error; + + dprintf("%s\n", __func__); + + if (!zv || !zv->zv_zso->zvo_iokitdev) { + dprintf("%s invalid zvol\n", __func__); + return (EINVAL); + } + + /* Cast to correct type */ + if ((zvol = zv->zv_zso->zvo_iokitdev->dev) == NULL) { + dprintf("%s couldn't cast IOKit handle\n", __func__); + return (ENXIO); + } + /* + * XXX This works fine, even if volume is mounted, + * but only tested expanding the zvol and only with + * GPT/APM/MBR partition map (not volume on whole-zvol). + */ + /* Inform clients of this device that size has changed */ + if ((error = zvol->refreshDevice()) != 0) { + dprintf("%s refreshDevice error %d\n", __func__, error); + return (error); + } + + return (0); +} + +uint64_t +zvolIO_kit_read(struct iomem *iomem, uint64_t offset, + char *address, uint64_t len) +{ + IOByteCount done; + // IOLog("zvolIO_kit_read offset %p count %llx to offset %llx\n", + // address, len, offset); + ASSERT(iomem && address && len > 0); + + done = iomem->buf->writeBytes(offset, (void *)address, len); + + return (done); +} + +uint64_t +zvolIO_kit_write(struct iomem *iomem, uint64_t offset, + char *address, uint64_t len) +{ + IOByteCount done; + // IOLog("zvolIO_kit_write offset %p count %llx to offset %llx\n", + // address, len, offset); + ASSERT(iomem && address && len > 0); + + done = iomem->buf->readBytes(offset, (void *)address, len); + + return (done); +} + +} /* extern "C" */ diff --git a/module/os/macos/zfs/zvol_os.c b/module/os/macos/zfs/zvol_os.c new file mode 100644 index 000000000000..8748c660aa41 --- /dev/null +++ b/module/os/macos/zfs/zvol_os.c @@ -0,0 +1,1149 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2020 by Jorgen Lundman. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static uint32_t zvol_major = ZVOL_MAJOR; + +unsigned int zvol_request_sync = 0; +unsigned int zvol_prefetch_bytes = (128 * 1024); +unsigned long zvol_max_discard_blocks = 16384; +unsigned int zvol_threads = 8; + +taskq_t *zvol_taskq; + +typedef struct zv_request { + zvol_state_t *zv; + + void (*zv_func)(void *); + void *zv_arg; + + taskq_ent_t ent; +} zv_request_t; + +int +dmu_read_iokit_dnode(dnode_t *dn, uint64_t *offset, + uint64_t position, uint64_t *size, struct iomem *iomem); +int +dmu_write_iokit_dnode(dnode_t *dn, uint64_t *offset, uint64_t position, + uint64_t *size, struct iomem *iomem, dmu_tx_t *tx); + +#define ZVOL_LOCK_HELD (1<<0) +#define ZVOL_LOCK_SPA (1<<1) +#define ZVOL_LOCK_SUSPEND (1<<2) + +static void +zvol_os_spawn_cb(void *param) +{ + zv_request_t *zvr = (zv_request_t *)param; + + zvr->zv_func(zvr->zv_arg); + + kmem_free(zvr, sizeof (zv_request_t)); +} + +static void +zvol_os_spawn(void (*func)(void *), void *arg) +{ + zv_request_t *zvr; + zvr = kmem_alloc(sizeof (zv_request_t), KM_SLEEP); + zvr->zv_arg = arg; + zvr->zv_func = func; + + taskq_init_ent(&zvr->ent); + + taskq_dispatch_ent(zvol_taskq, + zvol_os_spawn_cb, zvr, 0, &zvr->ent); +} + +/* + * Given a path, return TRUE if path is a ZVOL. + */ +static boolean_t +zvol_os_is_zvol(const char *device) +{ +#if 0 + struct stat stbf; + + // Stat device, get major/minor, match zv + if (stat(device, &stbf) == 0) { + if (S_ISBLK(stbf.st_mode) || S_ISCHR(stbf.st_mode)) { + dev_t dev = makedevice(stbf.st_major, stbf.st_minor); + + zvol_state_t *zv; + zv = zvol_find_by_dev(dev); + if (zv != NULL) { + mutex_exit(&zv->zv_state_lock); + return (B_TRUE); + } + } + } +#endif + return (B_FALSE); +} + +/* + * Make sure zv is still in the list (not freed) and if it is + * grab the locks in the correct order. + * Can we rely on list_link_active() instead of looping list? + * Return value: + * 0 : not found. No locks. + * ZVOL_LOCK_HELD : found and zv->zv_state_lock held + * |ZVOL_LOCK_SPA : spa_namespace_lock held + * |ZVOL_LOCK_SUSPEND : zv->zv_state_lock held + * call zvol_os_verify_lock_exit() to release + */ +static int +zvol_os_verify_and_lock(zvol_state_t *node, boolean_t takesuspend) +{ + zvol_state_t *zv; + int ret = ZVOL_LOCK_HELD; + +retry: + rw_enter(&zvol_state_lock, RW_READER); + for (zv = list_head(&zvol_state_list); zv != NULL; + zv = list_next(&zvol_state_list, zv)) { + + /* Until we find the node ... */ + if (zv != node) + continue; + + /* If this is to be first open, deal with spa_namespace */ + if (zv->zv_open_count == 0 && + !mutex_owned(&spa_namespace_lock)) { + /* + * We need to guarantee that the namespace lock is held + * to avoid spurious failures in zvol_first_open. + */ + ret |= ZVOL_LOCK_SPA; + if (!mutex_tryenter(&spa_namespace_lock)) { + rw_exit(&zvol_state_lock); + mutex_enter(&spa_namespace_lock); + /* Sadly, this will restart for loop */ + goto retry; + } + } + + mutex_enter(&zv->zv_state_lock); + + /* + * make sure zvol is not suspended during first open + * (hold zv_suspend_lock) and respect proper lock acquisition + * ordering - zv_suspend_lock before zv_state_lock + */ + if (zv->zv_open_count == 0 || takesuspend) { + ret |= ZVOL_LOCK_SUSPEND; + if (!rw_tryenter(&zv->zv_suspend_lock, RW_READER)) { + mutex_exit(&zv->zv_state_lock); + + /* If we hold spa_namespace, we can deadlock */ + if (ret & ZVOL_LOCK_SPA) { + rw_exit(&zvol_state_lock); + mutex_exit(&spa_namespace_lock); + ret &= ~ZVOL_LOCK_SPA; + dprintf("%s: spa_namespace loop\n", + __func__); + /* Let's not busy loop */ + delay(hz>>2); + goto retry; + } + rw_enter(&zv->zv_suspend_lock, RW_READER); + mutex_enter(&zv->zv_state_lock); + /* check to see if zv_suspend_lock is needed */ + if (zv->zv_open_count != 0) { + rw_exit(&zv->zv_suspend_lock); + ret &= ~ZVOL_LOCK_SUSPEND; + } + } + } + rw_exit(&zvol_state_lock); + + /* Success */ + return (ret); + + } /* for */ + + /* Not found */ + rw_exit(&zvol_state_lock); + + /* It's possible we grabbed spa, but then didn't re-find zv */ + if (ret & ZVOL_LOCK_SPA) + mutex_exit(&spa_namespace_lock); + return (0); +} + +static void +zvol_os_verify_lock_exit(zvol_state_t *zv, int locks) +{ + if (locks & ZVOL_LOCK_SPA) + mutex_exit(&spa_namespace_lock); + mutex_exit(&zv->zv_state_lock); + if (locks & ZVOL_LOCK_SUSPEND) + rw_exit(&zv->zv_suspend_lock); +} + +static void +zvol_os_register_device_cb(void *param) +{ + zvol_state_t *zv = (zvol_state_t *)param; + int locks; + + if ((locks = zvol_os_verify_and_lock(zv, zv->zv_open_count == 0)) == 0) + return; + + zvol_os_verify_lock_exit(zv, locks); + + /* This is a bit racy? */ + zvolRegisterDevice(zv); + +} + +int +zvol_os_write(dev_t dev, struct uio *uio, int p) +{ + return (ENOTSUP); +} + +int +zvol_os_read(dev_t dev, struct uio *uio, int p) +{ + return (ENOTSUP); +} + +int +zvol_os_write_zv(zvol_state_t *zv, uint64_t position, + uint64_t count, struct iomem *iomem) +{ + uint64_t volsize; + zfs_locked_range_t *lr; + int error = 0; + boolean_t sync; + uint64_t offset = 0; + uint64_t bytes; + + if (zv == NULL) + return (ENXIO); + + /* Some requests are just for flush and nothing else. */ + if (count == 0) + return (0); + + volsize = zv->zv_volsize; + if (count > 0 && + (position >= volsize)) + return (EIO); + + rw_enter(&zv->zv_suspend_lock, RW_READER); + + /* + * Open a ZIL if this is the first time we have written to this + * zvol. We protect zv->zv_zilog with zv_suspend_lock rather + * than zv_state_lock so that we don't need to acquire an + * additional lock in this path. + */ + if (zv->zv_zilog == NULL) { + rw_exit(&zv->zv_suspend_lock); + rw_enter(&zv->zv_suspend_lock, RW_WRITER); + if (zv->zv_zilog == NULL) { + zv->zv_zilog = zil_open(zv->zv_objset, + zvol_get_data); + zv->zv_flags |= ZVOL_WRITTEN_TO; + } + rw_downgrade(&zv->zv_suspend_lock); + } + + dprintf("zvol_write_iokit(position %llu offset " + "0x%llx bytes 0x%llx)\n", position, offset, count); + + sync = (zv->zv_objset->os_sync == ZFS_SYNC_ALWAYS); + + /* Lock the entire range */ + lr = zfs_rangelock_enter(&zv->zv_rangelock, position, count, + RL_WRITER); + + /* Iterate over (DMU_MAX_ACCESS/2) segments */ + while (count > 0 && (position + offset) < volsize) { + /* bytes for this segment */ + bytes = MIN(count, DMU_MAX_ACCESS >> 1); + dmu_tx_t *tx = dmu_tx_create(zv->zv_objset); + + /* don't write past the end */ + if (bytes > volsize - (position + offset)) + bytes = volsize - (position + offset); + + dmu_tx_hold_write_by_dnode(tx, zv->zv_dn, position+offset, + bytes); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + dmu_tx_abort(tx); + break; + } + + /* + * offset and bytes are mutated by dmu_write_iokit_dnode, + * save them for zvol_log_write if the call succeeds + */ + uint64_t save_offset = offset; + uint64_t save_bytes = bytes; + + error = dmu_write_iokit_dnode(zv->zv_dn, &offset, + position, &bytes, iomem, tx); + + if (error == 0) { + count -= MIN(count, + (DMU_MAX_ACCESS >> 1)) + bytes; + zvol_log_write(zv, tx, position+save_offset, save_bytes, + sync); + } + dmu_tx_commit(tx); + + if (error) + break; + } + zfs_rangelock_exit(lr); + + dataset_kstats_update_write_kstats(&zv->zv_kstat, offset); + + if (sync) + zil_commit(zv->zv_zilog, ZVOL_OBJ); + + rw_exit(&zv->zv_suspend_lock); + + return (error); +} + +int +zvol_os_read_zv(zvol_state_t *zv, uint64_t position, + uint64_t count, struct iomem *iomem) +{ + uint64_t volsize; + zfs_locked_range_t *lr; + int error = 0; + uint64_t offset = 0; + + if (zv == NULL) + return (ENXIO); + + volsize = zv->zv_volsize; + if (count > 0 && + (position >= volsize)) + return (EIO); + + rw_enter(&zv->zv_suspend_lock, RW_READER); + + lr = zfs_rangelock_enter(&zv->zv_rangelock, position, count, + RL_READER); + + while (count > 0 && (position+offset) < volsize) { + uint64_t bytes = MIN(count, DMU_MAX_ACCESS >> 1); + + /* don't read past the end */ + if (bytes > volsize - (position + offset)) + bytes = volsize - (position + offset); + + dprintf("%s %llu offset %llu len %llu bytes %llu\n", + "zvol_read_iokit: position", + position, offset, count, bytes); + + error = dmu_read_iokit_dnode(zv->zv_dn, &offset, position, + &bytes, iomem); + + if (error) { + /* convert checksum errors into IO errors */ + if (error == ECKSUM) + error = EIO; + break; + } + count -= MIN(count, DMU_MAX_ACCESS >> 1) - bytes; + } + zfs_rangelock_exit(lr); + + dataset_kstats_update_read_kstats(&zv->zv_kstat, offset); + + rw_exit(&zv->zv_suspend_lock); + return (error); +} + +int +zvol_os_unmap(zvol_state_t *zv, uint64_t off, uint64_t bytes) +{ + zfs_locked_range_t *lr = NULL; + dmu_tx_t *tx = NULL; + int error = 0; + uint64_t end = off + bytes; + + if (zv == NULL) + return (ENXIO); + + /* + * XNU's wipefs_wipe() will issue one giant unmap for the entire + * device; + * zfs create -V 8g BOOM/vol + * zvolIO doDiscard calling zvol_unmap with offset, bytes (0, 858992) + * Which will both take too long, and is uneccessary. We will ignore + * any unmaps deemed "too large". + */ + if ((off == 0ULL) && + (zv->zv_volsize > (1ULL << 24)) && /* 16Mb slop */ + (bytes >= (zv->zv_volsize - (1ULL << 24)))) + return (0); + + rw_enter(&zv->zv_suspend_lock, RW_READER); + + /* + * Open a ZIL if this is the first time we have written to this + * zvol. We protect zv->zv_zilog with zv_suspend_lock rather + * than zv_state_lock so that we don't need to acquire an + * additional lock in this path. + */ + if (zv->zv_zilog == NULL) { + rw_exit(&zv->zv_suspend_lock); + rw_enter(&zv->zv_suspend_lock, RW_WRITER); + if (zv->zv_zilog == NULL) { + zv->zv_zilog = zil_open(zv->zv_objset, + zvol_get_data); + zv->zv_flags |= ZVOL_WRITTEN_TO; + } + rw_downgrade(&zv->zv_suspend_lock); + } + + off = P2ROUNDUP(off, zv->zv_volblocksize); + end = P2ALIGN(end, zv->zv_volblocksize); + + if (end > zv->zv_volsize) /* don't write past the end */ + end = zv->zv_volsize; + + if (off >= end) { + /* Return success- caller does not need to know */ + goto out; + } + + bytes = end - off; + lr = zfs_rangelock_enter(&zv->zv_rangelock, off, bytes, RL_WRITER); + + tx = dmu_tx_create(zv->zv_objset); + + dmu_tx_mark_netfree(tx); + + error = dmu_tx_assign(tx, TXG_WAIT); + + if (error) { + dmu_tx_abort(tx); + } else { + + zvol_log_truncate(zv, tx, off, bytes, B_TRUE); + + dmu_tx_commit(tx); + + error = dmu_free_long_range(zv->zv_objset, + ZVOL_OBJ, off, bytes); + } + + zfs_rangelock_exit(lr); + + if (error == 0) { + /* + * If the 'sync' property is set to 'always' then + * treat this as a synchronous operation + * (i.e. commit to zil). + */ + if (zv->zv_objset->os_sync == ZFS_SYNC_ALWAYS) { + zil_commit(zv->zv_zilog, ZVOL_OBJ); + } + } + +out: + rw_exit(&zv->zv_suspend_lock); + return (error); +} + +int +zvol_os_update_volsize(zvol_state_t *zv, uint64_t volsize) +{ + zv->zv_volsize = volsize; + return (0); +} + +static void +zvol_os_clear_private_cb(void *param) +{ + zvolRemoveDeviceTerminate(param); +} + +static void +zvol_os_clear_private(zvol_state_t *zv) +{ + void *term; + + dprintf("%s\n", __func__); + + /* We can do all removal work, except call terminate. */ + term = zvolRemoveDevice(zv->zv_zso->zvo_iokitdev); + if (term == NULL) + return; + + zvol_remove_symlink(zv); + + zv->zv_zso->zvo_iokitdev = NULL; + + /* Call terminate in the background */ + zvol_os_spawn(zvol_os_clear_private_cb, term); + +} + +/* + * Find a zvol_state_t given the full major+minor dev_t. If found, + * return with zv_state_lock taken, otherwise, return (NULL) without + * taking zv_state_lock. + */ +static zvol_state_t * +zvol_os_find_by_dev(dev_t dev) +{ + zvol_state_t *zv; + + rw_enter(&zvol_state_lock, RW_READER); + for (zv = list_head(&zvol_state_list); zv != NULL; + zv = list_next(&zvol_state_list, zv)) { + mutex_enter(&zv->zv_state_lock); + if (zv->zv_zso->zvo_dev == dev) { + rw_exit(&zvol_state_lock); + return (zv); + } + mutex_exit(&zv->zv_state_lock); + } + rw_exit(&zvol_state_lock); + + return (NULL); +} + +void +zvol_os_validate_dev(zvol_state_t *zv) +{ +} + +/* + * Allocate memory for a new zvol_state_t and setup the required + * request queue and generic disk structures for the block device. + */ +static zvol_state_t * +zvol_os_alloc(dev_t dev, const char *name) +{ + zvol_state_t *zv; + struct zvol_state_os *zso; + uint64_t volmode; + + if (dsl_prop_get_integer(name, "volmode", &volmode, NULL) != 0) + return (NULL); + + if (volmode == ZFS_VOLMODE_DEFAULT) + volmode = zvol_volmode; + + if (volmode == ZFS_VOLMODE_NONE) + return (NULL); + + zv = kmem_zalloc(sizeof (zvol_state_t), KM_SLEEP); + zso = kmem_zalloc(sizeof (struct zvol_state_os), KM_SLEEP); + zv->zv_zso = zso; + + list_link_init(&zv->zv_next); + mutex_init(&zv->zv_state_lock, NULL, MUTEX_DEFAULT, NULL); + + zv->zv_open_count = 0; + strlcpy(zv->zv_name, name, MAXNAMELEN); + + zfs_rangelock_init(&zv->zv_rangelock, NULL, NULL); + rw_init(&zv->zv_suspend_lock, NULL, RW_DEFAULT, NULL); + + return (zv); +#if 0 +out_kmem: + kmem_free(zso, sizeof (struct zvol_state_os)); + kmem_free(zv, sizeof (zvol_state_t)); + return (NULL); +#endif +} + +/* + * Cleanup then free a zvol_state_t which was created by zvol_alloc(). + * At this time, the structure is not opened by anyone, is taken off + * the zvol_state_list, and has its private data set to NULL. + * The zvol_state_lock is dropped. + * + */ +static void +zvol_os_free(zvol_state_t *zv) +{ + ASSERT(!RW_LOCK_HELD(&zv->zv_suspend_lock)); + ASSERT(!MUTEX_HELD(&zv->zv_state_lock)); + ASSERT(zv->zv_open_count == 0); + + rw_destroy(&zv->zv_suspend_lock); + zfs_rangelock_fini(&zv->zv_rangelock); + + mutex_destroy(&zv->zv_state_lock); + dataset_kstats_destroy(&zv->zv_kstat); + + kmem_free(zv->zv_zso, sizeof (struct zvol_state_os)); + kmem_free(zv, sizeof (zvol_state_t)); +} + +void +zvol_wait_close(zvol_state_t *zv) +{ +} + +/* + * Create a block device minor node and setup the linkage between it + * and the specified volume. Once this function returns the block + * device is live and ready for use. + */ +static int +zvol_os_create_minor(const char *name) +{ + zvol_state_t *zv; + objset_t *os; + dmu_object_info_t *doi; + uint64_t volsize; + unsigned minor = 0; + int error = 0; + uint64_t hash = zvol_name_hash(name); + + dprintf("%s\n", __func__); + + if (zvol_inhibit_dev) + return (0); + + // minor? + zv = zvol_find_by_name_hash(name, hash, RW_NONE); + if (zv) { + ASSERT(MUTEX_HELD(&zv->zv_state_lock)); + mutex_exit(&zv->zv_state_lock); + return (SET_ERROR(EEXIST)); + } + + doi = kmem_alloc(sizeof (dmu_object_info_t), KM_SLEEP); + + error = dmu_objset_own(name, DMU_OST_ZVOL, B_TRUE, B_TRUE, FTAG, &os); + if (error) + goto out_doi; + + error = dmu_object_info(os, ZVOL_OBJ, doi); + if (error) + goto out_dmu_objset_disown; + + error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &volsize); + if (error) + goto out_dmu_objset_disown; + + zv = zvol_os_alloc(makedevice(zvol_major, minor), name); + if (zv == NULL) { + error = SET_ERROR(EAGAIN); + goto out_dmu_objset_disown; + } + zv->zv_hash = hash; + + if (dmu_objset_is_snapshot(os)) + zv->zv_flags |= ZVOL_RDONLY; + + zv->zv_volblocksize = doi->doi_data_block_size; + zv->zv_volsize = volsize; + zv->zv_objset = os; + + // set_capacity(zv->zv_zso->zvo_disk, zv->zv_volsize >> 9); + + if (spa_writeable(dmu_objset_spa(os))) { + if (zil_replay_disable) + zil_destroy(dmu_objset_zil(os), B_FALSE); + else + zil_replay(os, zv, zvol_replay_vector); + } + + dataset_kstats_create(&zv->zv_kstat, zv->zv_objset); + + /* Create the IOKit zvol while owned */ + if ((error = zvolCreateNewDevice(zv)) != 0) { + dprintf("%s zvolCreateNewDevice error %d\n", + __func__, error); + } + + zv->zv_objset = NULL; +out_dmu_objset_disown: + dmu_objset_disown(os, B_TRUE, FTAG); +out_doi: + kmem_free(doi, sizeof (dmu_object_info_t)); + + if (error == 0) { + rw_enter(&zvol_state_lock, RW_WRITER); + zvol_insert(zv); + rw_exit(&zvol_state_lock); + + /* Register (async) IOKit zvol after disown and unlock */ + /* The callback with release the mutex */ + zvol_os_spawn(zvol_os_register_device_cb, zv); + + } else { + + } + + dprintf("%s complete\n", __func__); + return (error); +} + + +static void zvol_os_rename_device_cb(void *param) +{ + zvol_state_t *zv = (zvol_state_t *)param; + int locks; + if ((locks = zvol_os_verify_and_lock(zv, zv->zv_open_count == 0)) == 0) + return; + + zvol_add_symlink(zv, zv->zv_zso->zvo_bsdname + 1, + zv->zv_zso->zvo_bsdname); + + zvol_os_verify_lock_exit(zv, locks); + zvolRenameDevice(zv); +} + +static void +zvol_os_rename_minor(zvol_state_t *zv, const char *newname) +{ + // int readonly = get_disk_ro(zv->zv_zso->zvo_disk); + + ASSERT(RW_LOCK_HELD(&zvol_state_lock)); + ASSERT(MUTEX_HELD(&zv->zv_state_lock)); + + zvol_remove_symlink(zv); + + strlcpy(zv->zv_name, newname, sizeof (zv->zv_name)); + + /* move to new hashtable entry */ + zv->zv_hash = zvol_name_hash(zv->zv_name); + hlist_del(&zv->zv_hlink); + hlist_add_head(&zv->zv_hlink, ZVOL_HT_HEAD(zv->zv_hash)); + + zvol_os_spawn(zvol_os_rename_device_cb, zv); + + /* + * The block device's read-only state is briefly changed causing + * a KOBJ_CHANGE uevent to be issued. This ensures udev detects + * the name change and fixes the symlinks. This does not change + * ZVOL_RDONLY in zv->zv_flags so the actual read-only state never + * changes. This would normally be done using kobject_uevent() but + * that is a GPL-only symbol which is why we need this workaround. + */ + // set_disk_ro(zv->zv_zso->zvo_disk, !readonly); + // set_disk_ro(zv->zv_zso->zvo_disk, readonly); +} + +static void +zvol_os_set_disk_ro(zvol_state_t *zv, int flags) +{ + // set_disk_ro(zv->zv_zso->zvo_disk, flags); +} + +static void +zvol_os_set_capacity(zvol_state_t *zv, uint64_t capacity) +{ + // set_capacity(zv->zv_zso->zvo_disk, capacity); +} + +int +zvol_os_open_zv(zvol_state_t *zv, int flag, int otyp, struct proc *p) +{ + int error = 0; + int locks; + + /* + * make sure zvol is not suspended during first open + * (hold zv_suspend_lock) and respect proper lock acquisition + * ordering - zv_suspend_lock before zv_state_lock + */ + if ((locks = zvol_os_verify_and_lock(zv, zv->zv_open_count == 0)) + == 0) { + return (SET_ERROR(ENOENT)); + } + + ASSERT(MUTEX_HELD(&zv->zv_state_lock)); + ASSERT(zv->zv_open_count != 0 || RW_READ_HELD(&zv->zv_suspend_lock)); + + /* + * We often race opens due to DiskArb. So if spa_namespace_lock is + * already held, potentially a zvol_first_open() is already in progress + */ + if (zv->zv_open_count == 0) { + error = zvol_first_open(zv, !(flag & FWRITE)); + if (error) + goto out_mutex; + } + + if ((flag & FWRITE) && (zv->zv_flags & ZVOL_RDONLY)) { + error = EROFS; + goto out_open_count; + } + + zv->zv_open_count++; + + zvol_os_verify_lock_exit(zv, locks); + return (0); + +out_open_count: + if (zv->zv_open_count == 0) + zvol_last_close(zv); + +out_mutex: + zvol_os_verify_lock_exit(zv, locks); + + if (error == EINTR) { + error = ERESTART; + schedule(); + } + return (SET_ERROR(error)); +} + +int +zvol_os_open(dev_t devp, int flag, int otyp, struct proc *p) +{ + zvol_state_t *zv; + int error = 0; + + if (!getminor(devp)) + return (0); + + zv = zvol_os_find_by_dev(devp); + if (zv == NULL) { + return (SET_ERROR(ENXIO)); + } + + error = zvol_os_open_zv(zv, flag, otyp, p); + + mutex_exit(&zv->zv_state_lock); + return (SET_ERROR(error)); +} + +int +zvol_os_close_zv(zvol_state_t *zv, int flag, int otyp, struct proc *p) +{ + int locks; + + if ((locks = zvol_os_verify_and_lock(zv, TRUE)) == 0) + return (SET_ERROR(ENOENT)); + + ASSERT(MUTEX_HELD(&zv->zv_state_lock)); + ASSERT(zv->zv_open_count != 1 || RW_READ_HELD(&zv->zv_suspend_lock)); + + zv->zv_open_count--; + + if (zv->zv_open_count == 0) + zvol_last_close(zv); + + zvol_os_verify_lock_exit(zv, locks); + + return (0); +} + +int +zvol_os_close(dev_t dev, int flag, int otyp, struct proc *p) +{ + zvol_state_t *zv; + int error = 0; + + if (!getminor(dev)) + return (0); + + zv = zvol_os_find_by_dev(dev); + if (zv == NULL) { + return (SET_ERROR(-ENXIO)); + } + + error = zvol_os_close_zv(zv, flag, otyp, p); + + mutex_exit(&zv->zv_state_lock); + return (0); +} + +void +zvol_os_strategy(struct buf *bp) +{ + +} + +int +zvol_os_get_volume_blocksize(dev_t dev) +{ + /* XNU can only handle two sizes. */ + return (DEV_BSIZE); +} + +int +zvol_os_ioctl(dev_t dev, unsigned long cmd, caddr_t data, int isblk, + cred_t *cr, int *rvalp) +{ + int error = 0; + u_int32_t *f; + u_int64_t *o; + zvol_state_t *zv = NULL; + + dprintf("%s\n", __func__); + + if (!getminor(dev)) + return (ENXIO); + + zv = zvol_os_find_by_dev(dev); + + if (zv == NULL) { + dprintf("zv is NULL\n"); + return (ENXIO); + } + + f = (u_int32_t *)data; + o = (u_int64_t *)data; + + switch (cmd) { + + case DKIOCGETMAXBLOCKCOUNTREAD: + dprintf("DKIOCGETMAXBLOCKCOUNTREAD\n"); + *o = 32; + break; + + case DKIOCGETMAXBLOCKCOUNTWRITE: + dprintf("DKIOCGETMAXBLOCKCOUNTWRITE\n"); + *o = 32; + break; + case DKIOCGETMAXSEGMENTCOUNTREAD: + dprintf("DKIOCGETMAXSEGMENTCOUNTREAD\n"); + *o = 32; + break; + + case DKIOCGETMAXSEGMENTCOUNTWRITE: + dprintf("DKIOCGETMAXSEGMENTCOUNTWRITE\n"); + *o = 32; + break; + + case DKIOCGETBLOCKSIZE: + dprintf("DKIOCGETBLOCKSIZE: %llu\n", + zv->zv_volblocksize); + *f = zv->zv_volblocksize; + break; + + case DKIOCSETBLOCKSIZE: + dprintf("DKIOCSETBLOCKSIZE %lu\n", *f); + + if (!isblk) { + /* We can only do this for a block device */ + error = ENODEV; + break; + } + + if (zvol_check_volblocksize(zv->zv_name, + (uint64_t)*f)) { + error = EINVAL; + break; + } + + /* set the new block size */ + zv->zv_volblocksize = (uint64_t)*f; + dprintf("setblocksize changed: %llu\n", + zv->zv_volblocksize); + break; + + case DKIOCISWRITABLE: + dprintf("DKIOCISWRITABLE\n"); + if (zv && (zv->zv_flags & ZVOL_RDONLY)) + *f = 0; + else + *f = 1; + break; +#ifdef DKIOCGETBLOCKCOUNT32 + case DKIOCGETBLOCKCOUNT32: + dprintf("DKIOCGETBLOCKCOUNT32: %lu\n", + (uint32_t)zv->zv_volsize / zv->zv_volblocksize); + *f = (uint32_t)zv->zv_volsize / zv->zv_volblocksize; + break; +#endif + + case DKIOCGETBLOCKCOUNT: + dprintf("DKIOCGETBLOCKCOUNT: %llu\n", + zv->zv_volsize / zv->zv_volblocksize); + *o = (uint64_t)zv->zv_volsize / zv->zv_volblocksize; + break; + + case DKIOCGETBASE: + dprintf("DKIOCGETBASE\n"); + /* + * What offset should we say? + * 0 is ok for FAT but to HFS + */ + *o = zv->zv_volblocksize * 0; + break; + + case DKIOCGETPHYSICALBLOCKSIZE: + dprintf("DKIOCGETPHYSICALBLOCKSIZE\n"); + *f = zv->zv_volblocksize; + break; + +#ifdef DKIOCGETTHROTTLEMASK + case DKIOCGETTHROTTLEMASK: + dprintf("DKIOCGETTHROTTLEMASK\n"); + *o = 0; + break; +#endif + + case DKIOCGETMAXBYTECOUNTREAD: + *o = SPA_MAXBLOCKSIZE; + break; + + case DKIOCGETMAXBYTECOUNTWRITE: + *o = SPA_MAXBLOCKSIZE; + break; +#ifdef DKIOCUNMAP + case DKIOCUNMAP: + dprintf("DKIOCUNMAP\n"); + *f = 1; + break; +#endif + + case DKIOCGETFEATURES: + *f = 0; + break; + +#ifdef DKIOCISSOLIDSTATE + case DKIOCISSOLIDSTATE: + dprintf("DKIOCISSOLIDSTATE\n"); + *f = 0; + break; +#endif + + case DKIOCISVIRTUAL: + *f = 1; + break; + + case DKIOCGETMAXSEGMENTBYTECOUNTREAD: + *o = 32 * zv->zv_volblocksize; + break; + + case DKIOCGETMAXSEGMENTBYTECOUNTWRITE: + *o = 32 * zv->zv_volblocksize; + break; + + case DKIOCSYNCHRONIZECACHE: + dprintf("DKIOCSYNCHRONIZECACHE\n"); + break; + + default: + dprintf("unknown ioctl: ENOTTY\n"); + error = ENOTTY; + break; + } + + mutex_exit(&zv->zv_state_lock); + + return (SET_ERROR(error)); +} + +const static zvol_platform_ops_t zvol_macos_ops = { + .zv_free = zvol_os_free, + .zv_rename_minor = zvol_os_rename_minor, + .zv_create_minor = zvol_os_create_minor, + .zv_update_volsize = zvol_os_update_volsize, + .zv_clear_private = zvol_os_clear_private, + .zv_is_zvol = zvol_os_is_zvol, + .zv_set_disk_ro = zvol_os_set_disk_ro, + .zv_set_capacity = zvol_os_set_capacity, +}; + +int +zvol_init(void) +{ + int threads = MIN(MAX(zvol_threads, 1), 1024); + + zvol_taskq = taskq_create(ZVOL_DRIVER, threads, maxclsyspri-4, + threads * 2, INT_MAX, TASKQ_PREPOPULATE); + if (zvol_taskq == NULL) { + return (-ENOMEM); + } + + zvol_init_impl(); + zvol_register_ops(&zvol_macos_ops); + return (0); +} + +void +zvol_fini(void) +{ + zvol_fini_impl(); + taskq_destroy(zvol_taskq); +} + + + +/* + * Due to OS X limitations in /dev, we create a symlink for "/dev/zvol" to + * "/var/run/zfs" (if we can) and for each pool, create the traditional + * ZFS Volume symlinks. + * + * Ie, for ZVOL $POOL/$VOLUME + * BSDName /dev/disk2 /dev/rdisk2 + * /dev/zvol -> /var/run/zfs + * /var/run/zfs/zvol/dsk/$POOL/$VOLUME -> /dev/disk2 + * /var/run/zfs/zvol/rdsk/$POOL/$VOLUME -> /dev/rdisk2 + * + * Note, we do not create symlinks for the partitioned slices. + * + */ + +void +zvol_add_symlink(zvol_state_t *zv, const char *bsd_disk, const char *bsd_rdisk) +{ + zfs_ereport_zvol_post(FM_EREPORT_ZVOL_CREATE_SYMLINK, + zv->zv_name, bsd_disk, bsd_rdisk); +} + + +void +zvol_remove_symlink(zvol_state_t *zv) +{ + if (!zv || !zv->zv_name[0]) + return; + + zfs_ereport_zvol_post(FM_EREPORT_ZVOL_REMOVE_SYMLINK, + zv->zv_name, &zv->zv_zso->zvo_bsdname[1], + zv->zv_zso->zvo_bsdname); +} diff --git a/module/zcommon/zfs_fletcher.c b/module/zcommon/zfs_fletcher.c index 7a9de4a4309d..7d2670c2d5b4 100644 --- a/module/zcommon/zfs_fletcher.c +++ b/module/zcommon/zfs_fletcher.c @@ -187,7 +187,7 @@ static const fletcher_4_ops_t *fletcher_4_impls[] = { #if defined(__x86_64) && defined(HAVE_AVX512BW) &fletcher_4_avx512bw_ops, #endif -#if defined(__aarch64__) && !defined(__FreeBSD__) +#if defined(__aarch64__) && !defined(__FreeBSD__) && !defined(__APPLE__) &fletcher_4_aarch64_neon_ops, #endif }; diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c index daca7032ce41..9d6772778eb5 100644 --- a/module/zcommon/zfs_prop.c +++ b/module/zcommon/zfs_prop.c @@ -383,6 +383,7 @@ zfs_prop_init(void) { NULL } }; +#ifdef __APPLE__ /* __APPLE__ */ static zprop_index_t devdisk_table[] = { { "poolonly", ZFS_DEVDISK_POOLONLY }, @@ -398,6 +399,7 @@ zfs_prop_init(void) { NULL } }; /* ___APPLE___ */ +#endif /* inherit index properties */ zprop_register_index(ZFS_PROP_REDUNDANT_METADATA, "redundant_metadata", @@ -606,8 +608,9 @@ zfs_prop_init(void) "RSNAPS"); #ifdef __APPLE__ - zprop_register_index(ZFS_PROP_BROWSE, "com.apple.browse", 1,PROP_INHERIT, - ZFS_TYPE_FILESYSTEM, "on | off", "COM.APPLE.BROWSE", boolean_table); + zprop_register_index(ZFS_PROP_BROWSE, "com.apple.browse", 1, + PROP_INHERIT, ZFS_TYPE_FILESYSTEM, "on | off", "COM.APPLE.BROWSE", + boolean_table); zprop_register_index(ZFS_PROP_IGNOREOWNER, "com.apple.ignoreowner", 0, PROP_INHERIT, ZFS_TYPE_FILESYSTEM, "on | off", "COM.APPLE.IGNOREOWNER", boolean_table); diff --git a/module/zcommon/zprop_common.c b/module/zcommon/zprop_common.c index bc02716cb044..debec63433c8 100644 --- a/module/zcommon/zprop_common.c +++ b/module/zcommon/zprop_common.c @@ -77,7 +77,8 @@ zfs_mod_supported_prop(const char *name, zfs_type_t type) * The equivalent _can_ be done on FreeBSD by way of the sysctl * tree, but this has not been done yet. */ -#if defined(_KERNEL) || defined(LIB_ZPOOL_BUILD) || defined(__FreeBSD__) || defined(__APPLE__) +#if defined(_KERNEL) || defined(LIB_ZPOOL_BUILD) || \ + defined(__FreeBSD__) || defined(__APPLE__) return (B_TRUE); #else return (zfs_mod_supported(type == ZFS_TYPE_POOL ? diff --git a/module/zfs/arc.c b/module/zfs/arc.c index f0ae3938a333..b95baef530f4 100644 --- a/module/zfs/arc.c +++ b/module/zfs/arc.c @@ -7662,9 +7662,19 @@ arc_init(void) offsetof(arc_prune_t, p_node)); mutex_init(&arc_prune_mtx, NULL, MUTEX_DEFAULT, NULL); +#ifdef __APPLE__ +#ifndef _KERNEL +#define TASKQ_REALLY_DYNAMIC 0x0 +#endif arc_prune_taskq = taskq_create("arc_prune", 100, defclsyspri, boot_ncpus, INT_MAX, TASKQ_PREPOPULATE | TASKQ_DYNAMIC | + TASKQ_REALLY_DYNAMIC | TASKQ_THREADS_CPU_PCT); +#else + arc_prune_taskq = taskq_create("arc_prune", 100, defclsyspri, + boot_ncpus, INT_MAX, TASKQ_PREPOPULATE | TASKQ_DYNAMIC | + TASKQ_THREADS_CPU_PCT); +#endif arc_ksp = kstat_create("zfs", 0, "arcstats", "misc", KSTAT_TYPE_NAMED, sizeof (arc_stats) / sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL); diff --git a/module/zfs/dmu.c b/module/zfs/dmu.c index 931a770645ff..5ed5bbc89a06 100644 --- a/module/zfs/dmu.c +++ b/module/zfs/dmu.c @@ -1243,7 +1243,7 @@ extern uint64_t zvolIO_kit_write(struct iomem *iomem, uint64_t offset, int dmu_read_iokit_dnode(dnode_t *dn, uint64_t *offset, - uint64_t position, uint64_t *size, struct iomem *iomem) + uint64_t position, uint64_t *size, struct iomem *iomem) { int err; @@ -1405,12 +1405,12 @@ dmu_write_iokit_dnode(dnode_t *dn, uint64_t *offset, uint64_t position, int err = 0; int i; - err = dmu_buf_hold_array_by_dnode(dn, *offset+position, *size, + err = dmu_buf_hold_array_by_dnode(dn, *offset+position, *size, FALSE, FTAG, &numbufs, &dbp, DMU_READ_PREFETCH); if (err) return (err); - while(*size > 0) { + while (*size > 0) { for (i = 0; i < numbufs; i++) { int tocpy; @@ -1423,7 +1423,8 @@ dmu_write_iokit_dnode(dnode_t *dn, uint64_t *offset, uint64_t position, bufoff = (position + *offset) - db->db_offset; tocpy = (int)MIN(db->db_size - bufoff, *size); - ASSERT(i == 0 || i == numbufs-1 || tocpy == db->db_size); + ASSERT(i == 0 || i == numbufs-1 || + tocpy == db->db_size); if (tocpy == db->db_size) dmu_buf_will_fill(db, tx); diff --git a/module/zfs/metaslab.c b/module/zfs/metaslab.c index 3b2b79b2f42f..5c1d1d6470ca 100644 --- a/module/zfs/metaslab.c +++ b/module/zfs/metaslab.c @@ -850,9 +850,17 @@ metaslab_group_create(metaslab_class_t *mc, vdev_t *vd, int allocators) metaslab_group_allocator_t *mga = &mg->mg_allocator[i]; zfs_refcount_create_tracked(&mga->mga_alloc_queue_depth); } - +#ifdef __APPLE__ +#ifndef _KERNEL +#define TASKQ_REALLY_DYNAMIC 0x0 +#endif + mg->mg_taskq = taskq_create("metaslab_group_taskq", metaslab_load_pct, + maxclsyspri, 10, INT_MAX, TASKQ_THREADS_CPU_PCT | TASKQ_DYNAMIC + | TASKQ_REALLY_DYNAMIC); +#else mg->mg_taskq = taskq_create("metaslab_group_taskq", metaslab_load_pct, maxclsyspri, 10, INT_MAX, TASKQ_THREADS_CPU_PCT | TASKQ_DYNAMIC); +#endif return (mg); } diff --git a/module/zfs/spa.c b/module/zfs/spa.c index 93266baefa3d..3540bd57bddf 100644 --- a/module/zfs/spa.c +++ b/module/zfs/spa.c @@ -1307,7 +1307,7 @@ spa_activate(spa_t *spa, spa_mode_t mode) spa_error_entry_compare, sizeof (spa_error_entry_t), offsetof(spa_error_entry_t, se_avl)); -#if defined (_KERNEL) && defined (__APPLE__) +#if defined(_KERNEL) && defined(__APPLE__) spa_activate_os(spa); #endif @@ -1448,7 +1448,7 @@ spa_deactivate(spa_t *spa) spa->spa_did = 0; } -#if defined (_KERNEL) && defined (__APPLE__) +#if defined(_KERNEL) && defined(__APPLE__) spa_deactivate_os(spa); #endif @@ -6002,7 +6002,7 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props, spa->spa_minref = zfs_refcount_count(&spa->spa_refcount); spa->spa_load_state = SPA_LOAD_NONE; -#if defined (__APPLE__) && defined (_KERNEL) +#if defined(__APPLE__) && defined(_KERNEL) spa_create_os(spa); #endif @@ -6189,12 +6189,12 @@ spa_import(char *pool, nvlist_t *config, nvlist_t *props, uint64_t flags) mutex_exit(&spa_namespace_lock); - zvol_create_minors_recursive(pool); - -#if defined (__APPLE__) && defined (_KERNEL) +#if defined(__APPLE__) && defined(_KERNEL) spa_create_os(spa); #endif + zvol_create_minors_recursive(pool); + return (0); } @@ -6426,7 +6426,7 @@ spa_export_common(const char *pool, int new_state, nvlist_t **oldconfig, } export_spa: -#if defined (__APPLE__) && defined (_KERNEL) +#if defined(__APPLE__) && defined(_KERNEL) spa_export_os(spa); #endif diff --git a/module/zfs/vdev_raidz_math.c b/module/zfs/vdev_raidz_math.c index 25d76970e99a..6e0e97694a69 100644 --- a/module/zfs/vdev_raidz_math.c +++ b/module/zfs/vdev_raidz_math.c @@ -61,7 +61,7 @@ const raidz_impl_ops_t *raidz_all_maths[] = { #if defined(__x86_64) && defined(HAVE_AVX512BW) /* only x86_64 for now */ &vdev_raidz_avx512bw_impl, #endif -#if defined(__aarch64__) && !defined(__FreeBSD__) +#if defined(__aarch64__) && !defined(__FreeBSD__) && !defined(__APPLE__) &vdev_raidz_aarch64_neon_impl, &vdev_raidz_aarch64_neonx2_impl, #endif diff --git a/module/zfs/zfs_fm.c b/module/zfs/zfs_fm.c index 242ac564099d..50ddab51ca82 100644 --- a/module/zfs/zfs_fm.c +++ b/module/zfs/zfs_fm.c @@ -1456,7 +1456,8 @@ zfs_ereport_snapshot_post(const char *subclass, spa_t *spa, const char *name) subclass, spa, NULL, NULL, NULL, 0, 0); - if (ereport == NULL) return; + if (ereport == NULL) + return; VERIFY0(nvlist_add_string(ereport, "snapshot_name", name)); diff --git a/module/zfs/zfs_log.c b/module/zfs/zfs_log.c index 0526017404f1..74b0a51a56b6 100644 --- a/module/zfs/zfs_log.c +++ b/module/zfs/zfs_log.c @@ -256,15 +256,17 @@ zfs_xattr_owner_unlinked(znode_t *zp) } if (tzp != zp) zrele(tzp); -#elif __APPLE__ +#elif defined(__APPLE__) + VERIFY(ZTOV(zp) != NULL); if (VN_HOLD(ZTOV(zp)) == 0) { /* - * if zp is XATTR node, keep walking up via z_xattr_parent until we - * get the owner + * if zp is XATTR node, keep walking up via z_xattr_parent + * until we get the owner */ while (zp->z_pflags & ZFS_XATTR) { ASSERT3U(zp->z_xattr_parent, !=, 0); - if (zfs_zget(ZTOZSB(zp), zp->z_xattr_parent, &dzp) != 0) { + if (zfs_zget(ZTOZSB(zp), zp->z_xattr_parent, + &dzp) != 0) { unlinked = 1; break; } diff --git a/module/zfs/zfs_replay.c b/module/zfs/zfs_replay.c index 5b215c04703e..83be2ab817ee 100644 --- a/module/zfs/zfs_replay.c +++ b/module/zfs/zfs_replay.c @@ -71,7 +71,7 @@ zfs_init_vattr(vattr_t *vap, uint64_t mask, uint64_t mode, bzero(vap, sizeof (*vap)); vap->va_mask = (uint_t)mask; vap->va_mode = mode; -#if defined (__FreeBSD__) || defined (__APPLE__) +#if defined(__FreeBSD__) || defined(__APPLE__) vap->va_type = IFTOVT(mode); #endif vap->va_uid = (uid_t)(IS_EPHEMERAL(uid)) ? -1 : uid; diff --git a/module/zfs/zfs_sa.c b/module/zfs/zfs_sa.c index 69ff77935a00..4687b59f8bbe 100644 --- a/module/zfs/zfs_sa.c +++ b/module/zfs/zfs_sa.c @@ -66,9 +66,9 @@ sa_attr_reg_t zfs_attr_table[ZPL_END+1] = { {"ZPL_DACL_ACES", 0, SA_ACL, 0}, {"ZPL_DXATTR", 0, SA_UINT8_ARRAY, 0}, {"ZPL_PROJID", sizeof (uint64_t), SA_UINT64_ARRAY, 0}, -#if defined (__APPLE__) - {"ZPL_ADDTIME", sizeof (uint64_t) * 2, SA_UINT64_ARRAY, 0}, - {"ZPL_DOCUMENTID", sizeof (uint64_t), SA_UINT64_ARRAY, 0}, +#if defined(__APPLE__) + {"ZPL_ADDTIME", sizeof (uint64_t) * 2, SA_UINT64_ARRAY, 0}, + {"ZPL_DOCUMENTID", sizeof (uint64_t), SA_UINT64_ARRAY, 0}, #endif {NULL, 0, 0, 0} }; diff --git a/module/zfs/zio.c b/module/zfs/zio.c index 87ccb6861850..ec872036a6ee 100644 --- a/module/zfs/zio.c +++ b/module/zfs/zio.c @@ -1893,7 +1893,9 @@ zio_taskq_dispatch(zio_t *zio, zio_taskq_type_t q, boolean_t cutinline) * to a single taskq at a time. It would be a grievous error * to dispatch the zio to another taskq at the same time. */ +#ifndef __APPLE__ ASSERT(taskq_empty_ent(&zio->io_tqent)); +#endif spa_taskq_dispatch_ent(spa, t, q, (task_func_t *)zio_execute, zio, flags, &zio->io_tqent); } diff --git a/module/zstd/lib/zstd.c b/module/zstd/lib/zstd.c index acdd4d9dac9d..176b18af3cb9 100644 --- a/module/zstd/lib/zstd.c +++ b/module/zstd/lib/zstd.c @@ -3074,6 +3074,11 @@ size_t FSE_decompress(void* dst, size_t dstCapacity, const void* cSrc, size_t cS /*-************************************* * Dependencies ***************************************/ +#ifdef __APPLE__ +/* We have no arm_neon.h header, figure this out. */ +#undef __ARM_NEON +#endif + #if !defined(ZSTD_NO_INTRINSICS) && defined(__ARM_NEON) #include #endif @@ -5189,7 +5194,7 @@ ZSTDLIB_API size_t ZSTD_insertBlock (ZSTD_DCtx* dctx, const void* blockStart, * * You can contact the author at : * - xxHash source repository : https://github.com/Cyan4973/xxHash - * + * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). @@ -5466,7 +5471,7 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src * You can contact the author at : * - xxHash homepage: http://www.xxhash.com * - xxHash source repository : https://github.com/Cyan4973/xxHash - * + * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). @@ -6558,7 +6563,7 @@ typedef enum { * - ZSTD_overlap_src_before_dst: The src and dst may overlap, but they MUST be at least 8 bytes apart. * The src buffer must be before the dst buffer. */ -MEM_STATIC FORCE_INLINE_ATTR +MEM_STATIC FORCE_INLINE_ATTR void ZSTD_wildcopy(void* dst, const void* src, ptrdiff_t length, ZSTD_overlap_e const ovtype) { ptrdiff_t diff = (BYTE*)dst - (const BYTE*)src; @@ -12517,7 +12522,7 @@ static size_t ZSTD_compressSubBlock_multi(const seqStore_t* seqStorePtr, if (sp < send) { seqDef const* seq; repcodes_t rep; - memcpy(&rep, prevCBlock->rep, sizeof(rep)); + memcpy(&rep, prevCBlock->rep, sizeof(rep)); for (seq = sstart; seq < sp; ++seq) { rep = ZSTD_updateRep(rep.rep, seq->offset - 1, ZSTD_getSequenceLength(seqStorePtr, seq).litLength == 0); } @@ -26030,7 +26035,7 @@ static void ZSTD_DCtx_updateOversizedDuration(ZSTD_DStream* zds, size_t const ne { if (ZSTD_DCtx_isOverflow(zds, neededInBuffSize, neededOutBuffSize)) zds->oversizedDuration++; - else + else zds->oversizedDuration = 0; } @@ -26237,7 +26242,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB { int const tooSmall = (zds->inBuffSize < neededInBuffSize) || (zds->outBuffSize < neededOutBuffSize); int const tooLarge = ZSTD_DCtx_isOversizedTooLong(zds); - + if (tooSmall || tooLarge) { size_t const bufferSize = neededInBuffSize + neededOutBuffSize; DEBUGLOG(4, "inBuff : from %u to %u", diff --git a/scripts/cmd-macos.sh b/scripts/cmd-macos.sh new file mode 100755 index 000000000000..0e5891d851f3 --- /dev/null +++ b/scripts/cmd-macos.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +if test x"$#" = "x0" ; then + printf "You need to supply a command.\n" + exit 1 +fi + +cmd=$1 +shift + +READLINK=$(which greadlink 2>/dev/null) +if test "x$READLINK" = "x" ; then + READLINK=$(which readlink 2>/dev/null) +fi + +if ! test "x$READLINK" = "x" ; then + $READLINK -f . > /dev/null 2>&1 + if ! test x$? = "x0" ; then + unset READLINK + else + CANONICALIZE="$READLINK -f" + fi +fi + +if test "x$READLINK" = "x" ; then + REALPATH=$(which grealpath 2>/dev/null) + if test "x$REALPATH" = "x" ; then + REALPATH=$(which realpath 2>/dev/null) + fi + if test "x$REALPATH" = "x" ; then + CANONICALIZE=readlink + else + CANONICALIZE=$REALPATH + fi +fi + +topdir=$(dirname "$($CANONICALIZE "$0")") + +if test "x$topdir" = x"." ; then + if ! test -f zfs.release.in ; then + printf "cd into the zfs source directory or install GNU readlink or realpath.\n" + printf "Homebrew: brew install coreutils\n" + printf "MacPorts: port install coreutils\n" + printf "Gentoo Prefix: emerge sys-apps/coreutils\n" + exit 1 + fi +fi + +topdir=$topdir/../ + +# lib/ +for lib in nvpair uutil zpool zfs zfs_core; do + export DYLD_LIBRARY_PATH=$topdir/lib/lib${lib}/.libs:$DYLD_LIBRARY_PATH +done + +# lib/os/macos/ +for lib in diskmgt; do + export DYLD_LIBRARY_PATH=$topdir/lib/os/macos/lib${lib}/.libs:$DYLD_LIBRARY_PATH +done + +for c in zdb zfs zpool ztest; do + export PATH=${topdir}/cmd/${c}/.libs:$PATH +done + +#echo PATH=$PATH +#echo DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH +exec "${topdir}/cmd/$cmd/.libs/$cmd" "$@" diff --git a/scripts/debug-macos.sh b/scripts/debug-macos.sh new file mode 100755 index 000000000000..09c67be45a3b --- /dev/null +++ b/scripts/debug-macos.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +if test x"$#" = "x0" ; then + printf "You need to supply a command.\n" + exit 1 +fi + +cmd=$1 +shift + +READLINK=$(which greadlink 2>/dev/null) +if test "x$READLINK" = "x" ; then + READLINK=$(which readlink 2>/dev/null) +fi + +if ! test "x$READLINK" = "x" ; then + $READLINK -f . > /dev/null 2>&1 + if ! test x$? = "x0" ; then + unset READLINK + else + CANONICALIZE="$READLINK -f" + fi +fi + +if test "x$READLINK" = "x" ; then + REALPATH=$(which grealpath 2>/dev/null) + if test "x$REALPATH" = "x" ; then + REALPATH=$(which realpath 2>/dev/null) + fi + if test "x$REALPATH" = "x" ; then + CANONICALIZE=readlink + else + CANONICALIZE=$REALPATH + fi +fi + +topdir=$(dirname "$($CANONICALIZE "$0")") + +if test "x$topdir" = x"." ; then + if ! test -f zfs.release.in ; then + printf "cd into the zfs source directory or install GNU readlink or realpath.\n" + printf "Homebrew: brew install coreutils\n" + printf "MacPorts: port install coreutils\n" + printf "Gentoo Prefix: emerge sys-apps/coreutils\n" + exit 1 + fi +fi + +topdir=$topdir/../ + +# lib/ +for lib in nvpair uutil zpool zfs zfs_core; do + export DYLD_LIBRARY_PATH=$topdir/lib/lib${lib}/.libs:$DYLD_LIBRARY_PATH +done + +# lib/os/macos/ +for lib in diskmgt; do + export DYLD_LIBRARY_PATH=$topdir/lib/os/macos/lib${lib}/.libs:$DYLD_LIBRARY_PATH +done + +for c in zdb zfs zpool ztest; do + export PATH=${topdir}/cmd/${c}/.libs:$PATH +done + +#echo PATH=$PATH +#echo DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH +exec lldb "${topdir}/cmd/$cmd/.libs/$cmd" "$@" diff --git a/scripts/load_macos.sh b/scripts/load_macos.sh new file mode 100755 index 000000000000..2e8a1880b734 --- /dev/null +++ b/scripts/load_macos.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Expected to be run from the root of the source tree, as root; +# ./scripts/load_macos.sh +# +# Copies compiled zfs.kext to /tmp/ and prepares the requirements +# for load. +# + +rsync -ar module/os/macos/zfs/zfs.kext/ /tmp/zfs.kext/ + +chown -R root:wheel /tmp/zfs.kext + +kextload -v /tmp/zfs.kext || kextutil /tmp/zfs.kext + +# log stream --source --predicate 'sender == "zfs"' --style compact + diff --git a/scripts/pkg_macos.sh b/scripts/pkg_macos.sh new file mode 100755 index 000000000000..01b98e81185b --- /dev/null +++ b/scripts/pkg_macos.sh @@ -0,0 +1,456 @@ +#!/usr/bin/env bash + +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# Copyright (c) 2020 by ilovezfs +# Copyright (c) 2020 by Jorgen Lundman + +# +# A script to produce an installable .pkg for macOS. +# +# Environment variables: +# +# $PKG_CODESIGN_KEY: Set to name of certificate for codesigning, +# as named in Keychain. For example; +# "Developer ID Application: Joergen Lundman (735AM5QEU3)" +# +# $PKG_NOTARIZE_KEY: Set to the notarize key you can create on +# Apple developer pages. For example; +# "awvz-fqoi-cxag-tymn" +# +# $PKG_INSTALL_KEY: Set to the name of certificate for installer +# signing, as named in Keychain, For example; +# "Developer ID Installer: Joergen Lundman (735AM5QEU3)" +# + +BASE_DIR=$(dirname "$0") +SCRIPT_COMMON=common.sh +if [ -f "${BASE_DIR}/${SCRIPT_COMMON}" ]; then + . "${BASE_DIR}/${SCRIPT_COMMON}" +else + echo "Missing helper script ${SCRIPT_COMMON}" && exit 1 +fi + +WORKNAME="macos-root" +WORKDIR="/private/var/tmp/${WORKNAME}" + +# If there are two dots "10.15.4", eat it +OS=$(sw_vers | awk '{if ($1 == "ProductVersion:") print $2;}') +OS=$(echo "$OS" | awk -F . '{if ($1 == 10) print $1"."$2; else print $1}') + + +function usage +{ + echo "$0: Create installable pkg for macOS" + echo "" + echo " Options:" + echo " -l skip make install step, using a folder of a previous run" + echo " -L copy and fix external libraries" + exit +} + +while getopts "hlL" opt; do + case $opt in + l ) skip_install=1 ;; + L ) fix_libraries=1 ;; + h ) usage + exit 2 + ;; + * ) echo "Invalid argument: -$OPTARG"; + usage + exit 1 + esac +done + +case ${OS} in + 10.8|10.9|10.10|10.11|10.12|10.13) + unset PKG_NOTARIZE_KEY + echo "No notarize for OS $OS" + ;; +esac + +# pass remaining arguments on +shift $((OPTIND - 1)) + + +echo "" +echo "Creating pkg for macOS installation... " +echo "" + +if [ -z "$PKG_CODESIGN_KEY" ]; then + echo "\$PKG_CODESIGN_KEY not set to certificate name, skipping codesign." +fi + +if [ -z "$PKG_NOTARIZE_KEY" ]; then + echo "\$PKG_NOTARIZE_KEY not set to pass-key, skipping notarize." +fi + +if [ -z "$PKG_INSTALL_KEY" ]; then + echo "\$PKG_INSTALL_KEY not set to certificate name, skipping pkg install signing." +fi + +version=$(awk < META '{if ($1 == "Version:") print $2;}') +prefix="/usr/local" +if [ -f "${BASE_DIR}/../config.status" ]; then + prefix=$(grep 'S\["prefix"\]' "${BASE_DIR}/../config.status" | tr '=' ' ' | awk '{print $2;}' | tr -d '"') +fi + +echo "Version is $version" +echo "Prefix set to $prefix" +echo "" + +sleep 3 + +if [ -z $skip_install ]; then + rm -rf ${WORKDIR} || true + + mkdir -p ${WORKDIR} || fail "Unable to create $WORKDIR" + + echo "Running \"make install DESTDIR=$WORKDIR\" ... " + make install DESTDIR="${WORKDIR}" || fail "Make install failed." +fi + +echo "" +echo "make install completed." +echo "" + +# Make an attempt to figure out where "zpool" is installed to, +# repo default is /usr/local/sbin/ but macOS prefers /usr/local/bin +pushd $WORKDIR || fail "failed to create workdir" +file=$(find usr -type f -name zpool) +bindir=$(dirname "$file") +popd || fail "failed to popd" + +codesign_dirs=" +${WORKDIR}/Library/Extensions/zfs.kext/ +" +codesign_files=" +${WORKDIR}/${bindir}/zdb +${WORKDIR}/${bindir}/zed +${WORKDIR}/${bindir}/zfs +${WORKDIR}/${bindir}/zhack +${WORKDIR}/${bindir}/zinject +${WORKDIR}/${bindir}/zpool +${WORKDIR}/${bindir}/zstream +${WORKDIR}/${bindir}/ztest +${WORKDIR}/${bindir}/raidz_test +${WORKDIR}/${bindir}/zfs_ids_to_path +${WORKDIR}/${bindir}/InvariantDisks +${WORKDIR}/${bindir}/zfs_util +${WORKDIR}/${bindir}/zconfigd +${WORKDIR}/${prefix}/libexec/zfs/zpool_influxdb +${WORKDIR}/${prefix}/lib/libnvpair.a +${WORKDIR}/${prefix}/lib/libuutil.a +${WORKDIR}/${prefix}/lib/libzfs.a +${WORKDIR}/${prefix}/lib/libzpool.a +${WORKDIR}/${prefix}/lib/libzfs_core.a +${WORKDIR}/${prefix}/lib/librt.so.1 +${WORKDIR}/${prefix}/lib/libnvpair.?.dylib +${WORKDIR}/${prefix}/lib/libuutil.?.dylib +${WORKDIR}/${prefix}/lib/libzfs.?.dylib +${WORKDIR}/${prefix}/lib/libzpool.?.dylib +${WORKDIR}/${prefix}/lib/libzfs_core.?.dylib +${WORKDIR}/${prefix}/lib/libzfsbootenv.?.dylib +${WORKDIR}/${prefix}/lib/libdiskmgt.1.dylib +${WORKDIR}/Library/Filesystems/zfs.fs/Contents/Resources/zfs_util +${WORKDIR}/Library/Filesystems/zfs.fs/Contents/Resources/mount_zfs +" + +codesign_all="$codesign_files $codesign_dirs" + +function fail +{ + echo "$@" + exit 1 +} + +function do_unlock +{ + cert=$1 + + echo "Looking for certificate ${cert} ..." + + keychain=$(security find-certificate -c "${cert}" | awk '{if ($1 == "keychain:") print $2;}'|tr -d '"') + + echo "Unlocking keychain $keychain ..." + security unlock-keychain "${keychain}" || fail "Unable to unlock keychain" + + retval=${keychain} +} + +function do_codesign +{ + failures=0 + + echo "" + + do_unlock "${PKG_CODESIGN_KEY}" + + echo "OS $OS" + if [ x"$OS" == x"10.11" ] || + [ x"$OS" == x"10.10" ] || + [ x"$OS" == x"10.9" ]; then + extra="" + else + extra="--options runtime" + fi + + for file in ${codesign_all} + do + echo "$file" + codesign --timestamp ${extra} -fvs "${PKG_CODESIGN_KEY}" "${file}" || failures=$((failures+1)) + done + + if [ "$failures" -ne 0 ]; then + echo "codesign phase had $failures issues ..." + exit 1 + fi + + # Look into where mount_zfs umount_zfs fsck_zfs went .. + # also: InvariantDisks +} + +# Delete everything in a directory, keeping a list +# of files. +# argument 1: directory path "/tmp/dir" +# argument 2: keep list "(item1|item2|tem3)" +function delete_and_keep +{ + # Keep only commands that apply to macOS + directory="$1" + keep="$2" + + pushd "${directory}" || fail "Unable to cd to ${directory}" + + shopt -s extglob + + for file in * + do + case "${file}" in + !${keep}) + echo "Deleting non macOS file \"$file\"" + rm -f "${file}" + ;; + *) + # echo "Not deleting $file" + esac + done + shopt -u extglob + popd || fail "failed to popd" +} + +function do_prune +{ + + delete_and_keep "${WORKDIR}/${bindir}/" "(zfs|zpool|zdb|zed|zhack|zinject|zstream|zstreamdump|ztest|InvariantDisks|zfs_util|zconfigd|arc_summary|arcstat|dbufstat|fsck.zfs|raidz_test|zfs_ids_to_path|zpool_influxdb)" + + pushd "${WORKDIR}" || fail "Unable to cd to ${WORKDIR}" + + # Using relative paths here for safety + rm -rf \ +"./${prefix}/share/zfs-macos/runfiles" \ +"./${prefix}/share/zfs-macos/test-runner" \ +"./${prefix}/share/zfs-macos/zfs-tests" \ +"./${prefix}/src" + + popd || fail "failed to popd" +} + +# Find any libraries we link with outside of zfs (and system). +# For example, /usr/local/opt/openssl/lib/libssl.1.1.0.dylib +# and copy them into /usr/local/zfs/lib - then update the paths to +# those libraries in all cmds and libraries. +# To do, handle "/usr/local/zfs/" from ./configure arguments (--prefix) +function copy_fix_libraries +{ + echo "Fixing external libraries ... " + fixlib=$(otool -L ${codesign_files} | egrep '/usr/local/opt/|/opt/local/lib/' |awk '{print $1;}' | grep '\.dylib$' | sort | uniq) + + # Add the libs into codesign list - both to be codesigned, and updated + # between themselves (libssl depends on libcrypt) + # copy over, build array of relative paths to add. + fixlib_relative="" + for lib in $fixlib + do + dir=$(dirname "$lib") + name=$(basename "$lib" .dylib) + echo " working on ${name}.dylib ..." + rsync -ar --include "${name}*" --exclude="*" "${dir}/" "${WORKDIR}/${prefix}/lib/" + fixlib_relative="${fixlib_relative} ${WORKDIR}/${prefix}/lib/${name}.dylib" + done + + echo "Adding in new libraries: " + echo "${fixlib_relative}" + + # Add new libraries + codesign_files="${codesign_files} ${fixlib_relative}" + codesign_all="${codesign_all} ${fixlib_relative}" + + # Fix up paths between binaries and libraries + for lib in $fixlib + do + dir=$(dirname "$lib") + name=$(basename "$lib" .dylib) + + # We could just change $lib into $prefix, which will work for + # zfs libraries. But libssl having libcrypto might have a different + # path, so lookup the source path each time. + for file in $codesign_files + do + chmod u+w "${file}" + src=$(otool -L "$file" | awk '{print $1;}' | grep "${name}.dylib") + install_name_tool -change "${src}" "${prefix}/lib/${name}.dylib" "${file}" + done + done +} + +# Upload .pkg file +# Staple .pkg file +function do_notarize +{ + echo "Uploading PKG to Apple ..." + + TFILE="out-altool.xml" + RFILE="req-altool.xml" + xcrun altool --notarize-app -f my_package_new.pkg --primary-bundle-id org.openzfsonosx.zfs -u lundman@lundman.net -p "$PKG_NOTARIZE_KEY" --output-format xml > ${TFILE} + + GUID=$(/usr/libexec/PlistBuddy -c "Print :notarization-upload:RequestUUID" ${TFILE}) + echo "Uploaded. GUID ${GUID}" + echo "Waiting for Apple to notarize..." + while true + do + sleep 10 + echo "Querying Apple." + + xcrun altool --notarization-info "${GUID}" -u lundman@lundman.net -p "$PKG_NOTARIZE_KEY" --output-format xml > ${RFILE} + status=$(/usr/libexec/PlistBuddy -c "Print :notarization-info:Status" ${RFILE}) + if [ "$status" != "in progress" ]; then + echo "Status: $status ." + break + fi + echo "Status: $status - sleeping ..." + sleep 30 + done + + echo "Stapling PKG ..." + xcrun stapler staple my_package_new.pkg + ret=$? + xcrun stapler validate -v my_package_new.pkg + + if [ $ret != 0 ]; then + echo "Failed to notarize: $ret" + grep "https://" ${RFILE} + exit 1 + fi + +} + +echo "Pruning install area ..." +do_prune + +if [ -n "$fix_libraries" ]; then + copy_fix_libraries + copy_fix_libraries +fi + +if [ -n "$PKG_CODESIGN_KEY" ]; then + do_codesign +fi + +sleep 1 + +echo "Creating pkg ... " + +sign=() + +if [ -n "$PKG_INSTALL_KEY" ]; then + do_unlock "${PKG_INSTALL_KEY}" + #sign=(--sign "$PKG_INSTALL_KEY" --keychain "$retval" --keychain ~/Library/Keychains/login.keychain-db) + echo sign=--sign "$PKG_INSTALL_KEY" --keychain "$retval" + sign=(--sign "$PKG_INSTALL_KEY" --keychain "$retval") +fi + +rm -f my_package.pkg +pkgbuild --root "${WORKDIR}" --identifier org.openzfsonosx.zfs --version "${version}" --scripts "${BASE_DIR}/../contrib/macOS/pkg-scripts/" "${sign[@]}" my_package.pkg + +ret=$? + +echo "pkgbuild result $ret" + +if [ $ret != 0 ]; then + fail "pkgbuild failed" +fi + +friendly=$(awk '/SOFTWARE LICENSE AGREEMENT FOR macOS/' '/System/Library/CoreServices/Setup Assistant.app/Contents/Resources/en.lproj/OSXSoftwareLicense.rtf' | awk -F 'macOS ' '{print $NF}' | tr -d '\\') +if [ -z "$friendly" ]; then + friendly=$(awk '/SOFTWARE LICENSE AGREEMENT FOR OS X/' '/System/Library/CoreServices/Setup Assistant.app/Contents/Resources/en.lproj/OSXSoftwareLicense.rtf' | awk -F 'OS X ' '{print $NF}' | awk '{print substr($0, 0, length($0)-1)}') +fi + +friendly=$(echo "$friendly" | tr ' ' '.') + +# Now fiddle with pkg to make it nicer +productbuild --synthesize --package ./my_package.pkg distribution.xml + +sed < distribution.xml > distribution_new.xml -e \ +"s## Open ZFS on OsX ${version} - ${friendly}-${OS}\\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ +\\ +\\ +\\ +\\ +#g" + +sed -i "" \ + -e "/SCRIPT_REPLACE/{r ${BASE_DIR}/../contrib/macOS/resources/javascript.js" \ + -e 'd' -e '}' \ + distribution_new.xml + +rm -f my_package_new.pkg +productbuild --distribution distribution_new.xml --resources "${BASE_DIR}/../contrib/macOS/resources/" --scripts "${BASE_DIR}/../contrib/macOS/product-scripts" "${sign[@]}" --package-path ./my_package.pkg my_package_new.pkg + +if [ -n "$PKG_NOTARIZE_KEY" ]; then + do_notarize +fi + +arch=$(uname -m) +if [ x"$arch" == x"arm64" ]; then + name="OpenZFSonOsX-${version}-${friendly}-${OS}-${arch}.pkg" +else + name="OpenZFSonOsX-${version}-${friendly}-${OS}.pkg" +fi + +mv my_package_new.pkg "${name}" +ls -l "${name}" + +# Cleanup +rm -f my_package.pkg distribution.xml distribution_new.xml req-altool.xml out-altool.xml + +echo "All done."

    OeC{chd1v2XLu(Y99x=14p}Nf^wMa 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�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