Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions config/kernel-dentry-operations.m4
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ dnl #
dnl # 2.6.38 API change
dnl # Added d_set_d_op() helper function.
dnl #
dnl # 6.17 API change
dnl # d_set_d_op() removed. No direct replacement.
dnl #
AC_DEFUN([ZFS_AC_KERNEL_SRC_D_SET_D_OP], [
ZFS_LINUX_TEST_SRC([d_set_d_op], [
#include <linux/dcache.h>
Expand All @@ -34,22 +37,21 @@ AC_DEFUN([ZFS_AC_KERNEL_SRC_D_SET_D_OP], [

AC_DEFUN([ZFS_AC_KERNEL_D_SET_D_OP], [
AC_MSG_CHECKING([whether d_set_d_op() is available])
ZFS_LINUX_TEST_RESULT_SYMBOL([d_set_d_op],
[d_set_d_op], [fs/dcache.c], [
ZFS_LINUX_TEST_RESULT([d_set_d_op], [
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_D_SET_D_OP, 1,
[Define if d_set_d_op() is available])
], [
ZFS_LINUX_TEST_ERROR([d_set_d_op])
AC_MSG_RESULT(no)
])
])

AC_DEFUN([ZFS_AC_KERNEL_SRC_DENTRY], [
ZFS_AC_KERNEL_SRC_D_OBTAIN_ALIAS
ZFS_AC_KERNEL_SRC_D_SET_D_OP
ZFS_AC_KERNEL_SRC_S_D_OP
])

AC_DEFUN([ZFS_AC_KERNEL_DENTRY], [
ZFS_AC_KERNEL_D_OBTAIN_ALIAS
ZFS_AC_KERNEL_D_SET_D_OP
ZFS_AC_KERNEL_S_D_OP
])
2 changes: 2 additions & 0 deletions config/kernel.m4
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [
ZFS_AC_KERNEL_SRC_COMMIT_METADATA
ZFS_AC_KERNEL_SRC_SETATTR_PREPARE
ZFS_AC_KERNEL_SRC_INSERT_INODE_LOCKED
ZFS_AC_KERNEL_SRC_DENTRY
ZFS_AC_KERNEL_SRC_TRUNCATE_SETSIZE
ZFS_AC_KERNEL_SRC_SECURITY_INODE
ZFS_AC_KERNEL_SRC_FST_MOUNT
Expand Down Expand Up @@ -188,6 +189,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [
ZFS_AC_KERNEL_COMMIT_METADATA
ZFS_AC_KERNEL_SETATTR_PREPARE
ZFS_AC_KERNEL_INSERT_INODE_LOCKED
ZFS_AC_KERNEL_DENTRY
ZFS_AC_KERNEL_TRUNCATE_SETSIZE
ZFS_AC_KERNEL_SECURITY_INODE
ZFS_AC_KERNEL_FST_MOUNT
Expand Down
26 changes: 0 additions & 26 deletions include/os/linux/kernel/linux/dcache_compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,32 +60,6 @@
} while (0)
#endif

/*
* 2.6.30 API change,
* The const keyword was added to the 'struct dentry_operations' in
* the dentry structure. To handle this we define an appropriate
* dentry_operations_t typedef which can be used.
*/
typedef const struct dentry_operations dentry_operations_t;

/*
* 2.6.38 API addition,
* Added d_clear_d_op() helper function which clears some flags and the
* registered dentry->d_op table. This is required because d_set_d_op()
* issues a warning when the dentry operations table is already set.
* For the .zfs control directory to work properly we must be able to
* override the default operations table and register custom .d_automount
* and .d_revalidate callbacks.
*/
static inline void
d_clear_d_op(struct dentry *dentry)
{
dentry->d_op = NULL;
dentry->d_flags &= ~(
DCACHE_OP_HASH | DCACHE_OP_COMPARE |
DCACHE_OP_REVALIDATE | DCACHE_OP_DELETE);
}

/*
* Walk and invalidate all dentry aliases of an inode
* unless it's a mountpoint
Expand Down
2 changes: 1 addition & 1 deletion include/sys/zvol.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ extern int zvol_set_volsize(const char *, uint64_t);
extern int zvol_set_volthreading(const char *, boolean_t);
extern int zvol_set_common(const char *, zfs_prop_t, zprop_source_t, uint64_t);
extern int zvol_set_ro(const char *, boolean_t);
extern zvol_state_handle_t *zvol_suspend(const char *);
extern int zvol_suspend(const char *, zvol_state_handle_t **);
extern int zvol_resume(zvol_state_handle_t *);
extern void *zvol_tag(zvol_state_handle_t *);

Expand Down
55 changes: 48 additions & 7 deletions module/os/linux/zfs/zpl_ctldir.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ zpl_snapdir_revalidate(struct dentry *dentry, unsigned int flags)
return (!!dentry->d_inode);
}

static dentry_operations_t zpl_dops_snapdirs = {
static const struct dentry_operations zpl_dops_snapdirs = {
/*
* Auto mounting of snapshots is only supported for 2.6.37 and
* newer kernels. Prior to this kernel the ops->follow_link()
Expand All @@ -215,6 +215,51 @@ static dentry_operations_t zpl_dops_snapdirs = {
.d_revalidate = zpl_snapdir_revalidate,
};

/*
* For the .zfs control directory to work properly we must be able to override
* the default operations table and register custom .d_automount and
* .d_revalidate callbacks.
*/
static void
set_snapdir_dentry_ops(struct dentry *dentry, unsigned int extraflags) {
static const unsigned int op_flags =
DCACHE_OP_HASH | DCACHE_OP_COMPARE |
DCACHE_OP_REVALIDATE | DCACHE_OP_DELETE |
DCACHE_OP_PRUNE | DCACHE_OP_WEAK_REVALIDATE | DCACHE_OP_REAL;

#ifdef HAVE_D_SET_D_OP
/*
* d_set_d_op() will set the DCACHE_OP_ flags according to what it
* finds in the passed dentry_operations, so we don't have to.
*
* We clear the flags and the old op table before calling d_set_d_op()
* because issues a warning when the dentry operations table is already
* set.
*/
dentry->d_op = NULL;
dentry->d_flags &= ~op_flags;
d_set_d_op(dentry, &zpl_dops_snapdirs);
dentry->d_flags |= extraflags;
#else
/*
* Since 6.17 there's no exported way to modify dentry ops, so we have
* to reach in and do it ourselves. This should be safe for our very
* narrow use case, which is to create or splice in an entry to give
* access to a snapshot.
*
* We need to set the op flags directly. We hardcode
* DCACHE_OP_REVALIDATE because that's the only operation we have; if
* we ever extend zpl_dops_snapdirs we will need to update the op flags
* to match.
*/
spin_lock(&dentry->d_lock);
dentry->d_op = &zpl_dops_snapdirs;
dentry->d_flags &= ~op_flags;
dentry->d_flags |= DCACHE_OP_REVALIDATE | extraflags;
spin_unlock(&dentry->d_lock);
#endif
}

static struct dentry *
zpl_snapdir_lookup(struct inode *dip, struct dentry *dentry,
unsigned int flags)
Expand All @@ -236,10 +281,7 @@ zpl_snapdir_lookup(struct inode *dip, struct dentry *dentry,
return (ERR_PTR(error));

ASSERT(error == 0 || ip == NULL);
d_clear_d_op(dentry);
d_set_d_op(dentry, &zpl_dops_snapdirs);
dentry->d_flags |= DCACHE_NEED_AUTOMOUNT;

set_snapdir_dentry_ops(dentry, DCACHE_NEED_AUTOMOUNT);
return (d_splice_alias(ip, dentry));
}

Expand Down Expand Up @@ -373,8 +415,7 @@ zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, umode_t mode)

error = -zfsctl_snapdir_mkdir(dip, dname(dentry), vap, &ip, cr, 0);
if (error == 0) {
d_clear_d_op(dentry);
d_set_d_op(dentry, &zpl_dops_snapdirs);
set_snapdir_dentry_ops(dentry, 0);
d_instantiate(dentry, ip);
}

Expand Down
4 changes: 2 additions & 2 deletions module/zfs/zfs_ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -4726,7 +4726,7 @@ zfs_ioc_rollback(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
error = error ? error : resume_err;
}
zfs_vfs_rele(zfsvfs);
} else if ((zv = zvol_suspend(fsname)) != NULL) {
} else if (zvol_suspend(fsname, &zv) == 0) {
error = dsl_dataset_rollback(fsname, target, zvol_tag(zv),
outnvl);
zvol_resume(zv);
Expand Down Expand Up @@ -5448,7 +5448,7 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, const char *origin,
}
error = error ? error : end_err;
zfs_vfs_rele(zfsvfs);
} else if ((zv = zvol_suspend(tofs)) != NULL) {
} else if (zvol_suspend(tofs, &zv) == 0) {
error = dmu_recv_end(&drc, zvol_tag(zv));
zvol_resume(zv);
} else {
Expand Down
23 changes: 19 additions & 4 deletions module/zfs/zvol.c
Original file line number Diff line number Diff line change
Expand Up @@ -1145,20 +1145,34 @@ zvol_tag(zvol_state_t *zv)
/*
* Suspend the zvol for recv and rollback.
*/
zvol_state_t *
zvol_suspend(const char *name)
int
zvol_suspend(const char *name, zvol_state_t **zvp)
{
zvol_state_t *zv;

zv = zvol_find_by_name(name, RW_WRITER);

if (zv == NULL)
return (NULL);
return (SET_ERROR(ENOENT));

/* block all I/O, release in zvol_resume. */
ASSERT(MUTEX_HELD(&zv->zv_state_lock));
ASSERT(RW_WRITE_HELD(&zv->zv_suspend_lock));

/*
* If it's being removed, unlock and return error. It doesn't make any
* sense to try to suspend a zvol being removed, but being here also
* means that zvol_remove_minors_impl() is about to call zvol_remove()
* and then destroy the zvol_state_t, so returning a pointer to it for
* the caller to mess with would be a disaster anyway.
*/
if (zv->zv_flags & ZVOL_REMOVING) {
mutex_exit(&zv->zv_state_lock);
rw_exit(&zv->zv_suspend_lock);
/* NB: Returning EIO here to match zfsvfs_teardown() */
return (SET_ERROR(EIO));
}

atomic_inc(&zv->zv_suspend_ref);

if (zv->zv_open_count > 0)
Expand All @@ -1171,7 +1185,8 @@ zvol_suspend(const char *name)
mutex_exit(&zv->zv_state_lock);

/* zv_suspend_lock is released in zvol_resume() */
return (zv);
*zvp = zv;
return (0);
}

int
Expand Down
Loading