mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-27 03:19:35 +03:00
ec21397127
When we finish a zfs receive, dmu_recv_end_sync() calls zvol_create_minors(async=TRUE). This kicks off some other threads that create the minor device nodes (in /dev/zvol/poolname/...). These async threads call zvol_prefetch_minors_impl() and zvol_create_minor(), which both call dmu_objset_own(), which puts a "long hold" on the dataset. Since the zvol minor node creation is asynchronous, this can happen after the `ZFS_IOC_RECV[_NEW]` ioctl and `zfs receive` process have completed. After the first receive ioctl has completed, userland may attempt to do another receive into the same dataset (e.g. the next incremental stream). This second receive and the asynchronous minor node creation can interfere with one another in several different ways, because they both require exclusive access to the dataset: 1. When the second receive is finishing up, dmu_recv_end_check() does dsl_dataset_handoff_check(), which can fail with EBUSY if the async minor node creation already has a "long hold" on this dataset. This causes the 2nd receive to fail. 2. The async udev rule can fail if zvol_id and/or systemd-udevd try to open the device while the the second receive's async attempt at minor node creation owns the dataset (via zvol_prefetch_minors_impl). This causes the minor node (/dev/zd*) to exist, but the udev-generated /dev/zvol/... to not exist. 3. The async minor node creation can silently fail with EBUSY if the first receive's zvol_create_minor() trys to own the dataset while the second receive's zvol_prefetch_minors_impl already owns the dataset. To address these problems, this change synchronously creates the minor node. To avoid the lock ordering problems that the asynchrony was introduced to fix (see #3681), we create the minor nodes from open context, with no locks held, rather than from syncing contex as was originally done. Implementation notes: We generally do not need to traverse children or prefetch anything (e.g. when running the recv, snapshot, create, or clone subcommands of zfs). We only need recursion when importing/opening a pool and when loading encryption keys. The existing recursive, asynchronous, prefetching code is preserved for use in these cases. Channel programs may need to create zvol minor nodes, when creating a snapshot of a zvol with the snapdev property set. We figure out what snapshots are created when running the LUA program in syncing context. In this case we need to remember what snapshots were created, and then try to create their minor nodes from open context, after the LUA code has completed. There are additional zvol use cases that asynchronously own the dataset, which can cause similar problems. E.g. changing the volmode or snapdev properties. These are less problematic because they are not recursive and don't touch datasets that are not involved in the operation, there is still potential for interference with subsequent operations. In the future, these cases should be similarly converted to create the zvol minor node synchronously from open context. The async tasks of removing and renaming minors do not own the objset, so they do not have this problem. However, it may make sense to also convert these operations to happen synchronously from open context, in the future. Reviewed-by: Paul Dagnelie <pcd@delphix.com> Reviewed-by: Prakash Surya <prakash.surya@delphix.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Matthew Ahrens <mahrens@delphix.com> External-issue: DLPX-65948 Closes #7863 Closes #9885
66 lines
2.3 KiB
C
66 lines
2.3 KiB
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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
|
|
* Copyright (c) 2016 Actifio, Inc. All rights reserved.
|
|
*/
|
|
|
|
#ifndef _SYS_ZVOL_H
|
|
#define _SYS_ZVOL_H
|
|
|
|
#include <sys/zfs_context.h>
|
|
|
|
#define ZVOL_OBJ 1ULL
|
|
#define ZVOL_ZAP_OBJ 2ULL
|
|
|
|
#define SPEC_MAXOFFSET_T ((1LL << ((NBBY * sizeof (daddr32_t)) + \
|
|
DEV_BSHIFT - 1)) - 1)
|
|
|
|
extern void zvol_create_minor(const char *);
|
|
extern void zvol_create_minors_recursive(const char *);
|
|
extern void zvol_remove_minors(spa_t *, const char *, boolean_t);
|
|
extern void zvol_rename_minors(spa_t *, const char *, const char *, boolean_t);
|
|
|
|
#ifdef _KERNEL
|
|
struct zvol_state;
|
|
typedef struct zvol_state zvol_state_handle_t;
|
|
|
|
extern int zvol_check_volsize(uint64_t, uint64_t);
|
|
extern int zvol_check_volblocksize(const char *, uint64_t);
|
|
extern int zvol_get_stats(objset_t *, nvlist_t *);
|
|
extern boolean_t zvol_is_zvol(const char *);
|
|
extern void zvol_create_cb(objset_t *, void *, cred_t *, dmu_tx_t *);
|
|
extern int zvol_set_volsize(const char *, uint64_t);
|
|
extern int zvol_set_volblocksize(const char *, uint64_t);
|
|
extern int zvol_set_snapdev(const char *, zprop_source_t, uint64_t);
|
|
extern int zvol_set_volmode(const char *, zprop_source_t, uint64_t);
|
|
extern zvol_state_handle_t *zvol_suspend(const char *);
|
|
extern int zvol_resume(zvol_state_handle_t *);
|
|
extern void *zvol_tag(zvol_state_handle_t *);
|
|
|
|
extern int zvol_init(void);
|
|
extern void zvol_fini(void);
|
|
extern int zvol_busy(void);
|
|
|
|
#endif /* _KERNEL */
|
|
#endif /* _SYS_ZVOL_H */
|