2019-09-25 19:20:30 +03:00
|
|
|
/*
|
|
|
|
* 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
|
2022-07-12 00:16:13 +03:00
|
|
|
* or https://opensource.org/licenses/CDDL-1.0.
|
2019-09-25 19:20:30 +03:00
|
|
|
* 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
|
|
|
|
*/
|
zvol: ensure device minors are properly cleaned up
Currently, if a minor is in use when we try to remove it, we'll skip it
and never come back to it again. Since the zvol state is hung off the
minor in the kernel, this can get us into weird situations if something
tries to use it after the removal fails. It's even worse at pool export,
as there's now a vestigial zvol state with no pool under it. It's
weirder again if the pool is subsequently reimported, as the zvol code
(reasonably) assumes the zvol state has been properly setup, when it's
actually left over from the previous import of the pool.
This commit attempts to tackle that by setting a flag on the zvol if its
minor can't be removed, and then checking that flag when a request is
made and rejecting it, thus stopping new work coming in.
The flag also causes a condvar to be signaled when the last client
finishes. For the case where a single minor is being removed (eg
changing volmode), it will wait for this signal before proceeding.
Meanwhile, when removing all minors, a background task is created for
each minor that couldn't be removed on the spot, and those tasks then
wake and clean up.
Since any new tasks are queued on to the pool's spa_zvol_taskq,
spa_export_common() will continue to wait at export until all minors are
removed.
Reviewed-by: Tony Hutter <hutter2@llnl.gov>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Sponsored-by: Klara, Inc.
Sponsored-by: Wasabi Technology, Inc.
Signed-off-by: Rob Norris <rob.norris@klarasystems.com>
Closes #14872
Closes #16364
2024-07-18 06:24:05 +03:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2024, Klara, Inc.
|
|
|
|
*/
|
2019-09-25 19:20:30 +03:00
|
|
|
|
|
|
|
#ifndef _SYS_ZVOL_IMPL_H
|
|
|
|
#define _SYS_ZVOL_IMPL_H
|
|
|
|
|
|
|
|
#include <sys/zfs_context.h>
|
|
|
|
|
2024-07-18 05:37:43 +03:00
|
|
|
#define ZVOL_RDONLY (1<<0) /* zvol is readonly (writes rejected) */
|
|
|
|
#define ZVOL_WRITTEN_TO (1<<1) /* zvol has been written to (needs flush) */
|
|
|
|
#define ZVOL_EXCL (1<<2) /* zvol has O_EXCL client right now */
|
zvol: ensure device minors are properly cleaned up
Currently, if a minor is in use when we try to remove it, we'll skip it
and never come back to it again. Since the zvol state is hung off the
minor in the kernel, this can get us into weird situations if something
tries to use it after the removal fails. It's even worse at pool export,
as there's now a vestigial zvol state with no pool under it. It's
weirder again if the pool is subsequently reimported, as the zvol code
(reasonably) assumes the zvol state has been properly setup, when it's
actually left over from the previous import of the pool.
This commit attempts to tackle that by setting a flag on the zvol if its
minor can't be removed, and then checking that flag when a request is
made and rejecting it, thus stopping new work coming in.
The flag also causes a condvar to be signaled when the last client
finishes. For the case where a single minor is being removed (eg
changing volmode), it will wait for this signal before proceeding.
Meanwhile, when removing all minors, a background task is created for
each minor that couldn't be removed on the spot, and those tasks then
wake and clean up.
Since any new tasks are queued on to the pool's spa_zvol_taskq,
spa_export_common() will continue to wait at export until all minors are
removed.
Reviewed-by: Tony Hutter <hutter2@llnl.gov>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Sponsored-by: Klara, Inc.
Sponsored-by: Wasabi Technology, Inc.
Signed-off-by: Rob Norris <rob.norris@klarasystems.com>
Closes #14872
Closes #16364
2024-07-18 06:24:05 +03:00
|
|
|
#define ZVOL_REMOVING (1<<3) /* zvol waiting to remove minor */
|
2019-09-25 19:20:30 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The in-core state of each volume.
|
|
|
|
*/
|
|
|
|
typedef struct zvol_state {
|
|
|
|
char zv_name[MAXNAMELEN]; /* name */
|
|
|
|
uint64_t zv_volsize; /* advertised space */
|
|
|
|
uint64_t zv_volblocksize; /* volume block size */
|
|
|
|
objset_t *zv_objset; /* objset handle */
|
|
|
|
uint32_t zv_flags; /* ZVOL_* flags */
|
|
|
|
uint32_t zv_open_count; /* open counts */
|
|
|
|
uint32_t zv_changed; /* disk changed */
|
2020-11-17 20:50:52 +03:00
|
|
|
uint32_t zv_volmode; /* volmode */
|
2019-09-25 19:20:30 +03:00
|
|
|
zilog_t *zv_zilog; /* ZIL handle */
|
2019-11-01 20:37:33 +03:00
|
|
|
zfs_rangelock_t zv_rangelock; /* for range locking */
|
2019-09-25 19:20:30 +03:00
|
|
|
dnode_t *zv_dn; /* dnode hold */
|
2020-06-06 03:17:02 +03:00
|
|
|
dataset_kstats_t zv_kstat; /* zvol kstats */
|
2019-09-25 19:20:30 +03:00
|
|
|
list_node_t zv_next; /* next zvol_state_t linkage */
|
|
|
|
uint64_t zv_hash; /* name hash */
|
|
|
|
struct hlist_node zv_hlink; /* hash link */
|
|
|
|
kmutex_t zv_state_lock; /* protects zvol_state_t */
|
|
|
|
atomic_t zv_suspend_ref; /* refcount for suspend */
|
|
|
|
krwlock_t zv_suspend_lock; /* suspend lock */
|
zvol: ensure device minors are properly cleaned up
Currently, if a minor is in use when we try to remove it, we'll skip it
and never come back to it again. Since the zvol state is hung off the
minor in the kernel, this can get us into weird situations if something
tries to use it after the removal fails. It's even worse at pool export,
as there's now a vestigial zvol state with no pool under it. It's
weirder again if the pool is subsequently reimported, as the zvol code
(reasonably) assumes the zvol state has been properly setup, when it's
actually left over from the previous import of the pool.
This commit attempts to tackle that by setting a flag on the zvol if its
minor can't be removed, and then checking that flag when a request is
made and rejecting it, thus stopping new work coming in.
The flag also causes a condvar to be signaled when the last client
finishes. For the case where a single minor is being removed (eg
changing volmode), it will wait for this signal before proceeding.
Meanwhile, when removing all minors, a background task is created for
each minor that couldn't be removed on the spot, and those tasks then
wake and clean up.
Since any new tasks are queued on to the pool's spa_zvol_taskq,
spa_export_common() will continue to wait at export until all minors are
removed.
Reviewed-by: Tony Hutter <hutter2@llnl.gov>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Sponsored-by: Klara, Inc.
Sponsored-by: Wasabi Technology, Inc.
Signed-off-by: Rob Norris <rob.norris@klarasystems.com>
Closes #14872
Closes #16364
2024-07-18 06:24:05 +03:00
|
|
|
kcondvar_t zv_removing_cv; /* ready to remove minor */
|
2019-09-25 19:20:30 +03:00
|
|
|
struct zvol_state_os *zv_zso; /* private platform state */
|
2023-10-25 00:53:27 +03:00
|
|
|
boolean_t zv_threading; /* volthreading property */
|
2019-09-25 19:20:30 +03:00
|
|
|
} zvol_state_t;
|
|
|
|
|
|
|
|
|
|
|
|
extern krwlock_t zvol_state_lock;
|
|
|
|
#define ZVOL_HT_SIZE 1024
|
|
|
|
extern struct hlist_head *zvol_htable;
|
|
|
|
#define ZVOL_HT_HEAD(hash) (&zvol_htable[(hash) & (ZVOL_HT_SIZE-1)])
|
2022-01-15 02:37:55 +03:00
|
|
|
extern zil_replay_func_t *const zvol_replay_vector[TX_MAX_TYPE];
|
2019-09-25 19:20:30 +03:00
|
|
|
|
|
|
|
extern unsigned int zvol_volmode;
|
|
|
|
extern unsigned int zvol_inhibit_dev;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* platform independent functions exported to platform code
|
|
|
|
*/
|
|
|
|
zvol_state_t *zvol_find_by_name_hash(const char *name,
|
|
|
|
uint64_t hash, int mode);
|
|
|
|
int zvol_first_open(zvol_state_t *zv, boolean_t readonly);
|
|
|
|
uint64_t zvol_name_hash(const char *name);
|
|
|
|
void zvol_remove_minors_impl(const char *name);
|
|
|
|
void zvol_last_close(zvol_state_t *zv);
|
|
|
|
void zvol_insert(zvol_state_t *zv);
|
|
|
|
void zvol_log_truncate(zvol_state_t *zv, dmu_tx_t *tx, uint64_t off,
|
2023-10-31 00:51:56 +03:00
|
|
|
uint64_t len);
|
2019-09-25 19:20:30 +03:00
|
|
|
void zvol_log_write(zvol_state_t *zv, dmu_tx_t *tx, uint64_t offset,
|
2023-10-31 00:51:56 +03:00
|
|
|
uint64_t size, boolean_t commit);
|
2021-03-20 08:53:31 +03:00
|
|
|
int zvol_get_data(void *arg, uint64_t arg2, lr_write_t *lr, char *buf,
|
|
|
|
struct lwb *lwb, zio_t *zio);
|
2019-09-25 19:20:30 +03:00
|
|
|
int zvol_init_impl(void);
|
|
|
|
void zvol_fini_impl(void);
|
2020-11-17 20:50:52 +03:00
|
|
|
void zvol_wait_close(zvol_state_t *zv);
|
2019-09-25 19:20:30 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* platform dependent functions exported to platform independent code
|
|
|
|
*/
|
2022-02-07 21:24:38 +03:00
|
|
|
void zvol_os_free(zvol_state_t *zv);
|
|
|
|
void zvol_os_rename_minor(zvol_state_t *zv, const char *newname);
|
|
|
|
int zvol_os_create_minor(const char *name);
|
|
|
|
int zvol_os_update_volsize(zvol_state_t *zv, uint64_t volsize);
|
|
|
|
boolean_t zvol_os_is_zvol(const char *path);
|
|
|
|
void zvol_os_clear_private(zvol_state_t *zv);
|
|
|
|
void zvol_os_set_disk_ro(zvol_state_t *zv, int flags);
|
|
|
|
void zvol_os_set_capacity(zvol_state_t *zv, uint64_t capacity);
|
2019-09-25 19:20:30 +03:00
|
|
|
|
|
|
|
#endif
|