diff options
Diffstat (limited to 'sys-kernel/zfs-dkms/files/kernel-4.18.patch')
-rw-r--r-- | sys-kernel/zfs-dkms/files/kernel-4.18.patch | 1393 |
1 files changed, 1393 insertions, 0 deletions
diff --git a/sys-kernel/zfs-dkms/files/kernel-4.18.patch b/sys-kernel/zfs-dkms/files/kernel-4.18.patch new file mode 100644 index 00000000..ef8d648d --- /dev/null +++ b/sys-kernel/zfs-dkms/files/kernel-4.18.patch @@ -0,0 +1,1393 @@ +From 3cf6af50e0bc02b294bf974a8e653e12c40b5263 Mon Sep 17 00:00:00 2001 +From: Brian Behlendorf <behlendorf1@llnl.gov> +Date: Sun, 12 Aug 2018 18:22:03 -0400 +Subject: [PATCH] Linux 4.18 compat: inode timespec -> timespec64 + +Commit torvalds/linux@95582b0 changes the inode i_atime, i_mtime, +and i_ctime members form timespec's to timespec64's to make them +2038 safe. As part of this change the current_time() function was +also updated to return the timespec64 type. + +Resolve this issue by introducing a new inode_timespec_t type which +is defined to match the timespec type used by the inode. It should +be used when working with inode timestamps to ensure matching types. + +The timestruc_t type under Illumos was used in a similar fashion but +was specified to always be a timespec_t. Rather than incorrectly +define this type all timespec_t types have been replaced by the new +inode_timespec_t type. + +Finally, the kernel and user space 'sys/time.h' headers were aligned +with each other. They define as appropriate for the context several +constants as macros and include static inline implementation of +gethrestime(), gethrestime_sec(), and gethrtime(). + +Reviewed-by: Chunwei Chen <tuxoko@gmail.com> +Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> +Closes #7643 +Backported-by: Richard Yao <ryao@gentoo.org> +--- + config/kernel-current-time.m4 | 7 +++--- + include/sys/dmu.h | 2 +- + include/sys/dmu_objset.h | 2 +- + include/sys/dsl_dir.h | 4 ++-- + include/sys/spa_impl.h | 2 +- + include/sys/xvattr.h | 2 +- + include/sys/zfs_context.h | 9 +------ + include/sys/zfs_znode.h | 33 ++++++++++++++++++------- + include/sys/zpl.h | 9 +++++++ + lib/libspl/Makefile.am | 2 -- + lib/libspl/gethrestime.c | 38 ----------------------------- + lib/libspl/gethrtime.c | 45 ----------------------------------- + lib/libspl/include/sys/time.h | 37 +++++++++++++++++++++------- + lib/libzpool/kernel.c | 4 ++-- + module/zfs/dmu_objset.c | 2 +- + module/zfs/dsl_dir.c | 6 ++--- + module/zfs/fm.c | 2 +- + module/zfs/zfs_ctldir.c | 2 +- + module/zfs/zfs_vnops.c | 4 ++-- + module/zfs/zfs_znode.c | 4 ++-- + module/zfs/zpl_inode.c | 5 ++-- + 21 files changed, 88 insertions(+), 133 deletions(-) + delete mode 100644 lib/libspl/gethrestime.c + delete mode 100644 lib/libspl/gethrtime.c + +diff --git a/config/kernel-current-time.m4 b/config/kernel-current-time.m4 +index 2ede9ff38c4..c7d5c9b5200 100644 +--- a/config/kernel-current-time.m4 ++++ b/config/kernel-current-time.m4 +@@ -1,15 +1,14 @@ + dnl # + dnl # 4.9, current_time() added ++dnl # 4.18, return type changed from timespec to timespec64 + dnl # + AC_DEFUN([ZFS_AC_KERNEL_CURRENT_TIME], + [AC_MSG_CHECKING([whether current_time() exists]) + ZFS_LINUX_TRY_COMPILE_SYMBOL([ + #include <linux/fs.h> + ], [ +- struct inode ip; +- struct timespec now __attribute__ ((unused)); +- +- now = current_time(&ip); ++ struct inode ip __attribute__ ((unused)); ++ ip.i_atime = current_time(&ip); + ], [current_time], [fs/inode.c], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_CURRENT_TIME, 1, [current_time() exists]) +diff --git a/include/sys/dmu.h b/include/sys/dmu.h +index bcdf7d646fb..755a90561ae 100644 +--- a/include/sys/dmu.h ++++ b/include/sys/dmu.h +@@ -891,7 +891,7 @@ uint64_t dmu_objset_fsid_guid(objset_t *os); + /* + * Get the [cm]time for an objset's snapshot dir + */ +-timestruc_t dmu_objset_snap_cmtime(objset_t *os); ++inode_timespec_t dmu_objset_snap_cmtime(objset_t *os); + + int dmu_objset_is_snapshot(objset_t *os); + +diff --git a/include/sys/dmu_objset.h b/include/sys/dmu_objset.h +index a836e03722c..531e81d4d88 100644 +--- a/include/sys/dmu_objset.h ++++ b/include/sys/dmu_objset.h +@@ -179,7 +179,7 @@ int dmu_objset_find_dp(struct dsl_pool *dp, uint64_t ddobj, + int func(struct dsl_pool *, struct dsl_dataset *, void *), + void *arg, int flags); + void dmu_objset_evict_dbufs(objset_t *os); +-timestruc_t dmu_objset_snap_cmtime(objset_t *os); ++inode_timespec_t dmu_objset_snap_cmtime(objset_t *os); + + /* called from dsl */ + void dmu_objset_sync(objset_t *os, zio_t *zio, dmu_tx_t *tx); +diff --git a/include/sys/dsl_dir.h b/include/sys/dsl_dir.h +index 69b0b6a5355..80e83fdc4d3 100644 +--- a/include/sys/dsl_dir.h ++++ b/include/sys/dsl_dir.h +@@ -103,7 +103,7 @@ struct dsl_dir { + /* Protected by dd_lock */ + kmutex_t dd_lock; + list_t dd_props; /* list of dsl_prop_record_t's */ +- timestruc_t dd_snap_cmtime; /* last time snapshot namespace changed */ ++ inode_timespec_t dd_snap_cmtime; /* last snapshot namespace change */ + uint64_t dd_origin_txg; + + /* gross estimate of space used by in-flight tx's */ +@@ -159,7 +159,7 @@ boolean_t dsl_dir_is_clone(dsl_dir_t *dd); + void dsl_dir_new_refreservation(dsl_dir_t *dd, struct dsl_dataset *ds, + uint64_t reservation, cred_t *cr, dmu_tx_t *tx); + void dsl_dir_snap_cmtime_update(dsl_dir_t *dd); +-timestruc_t dsl_dir_snap_cmtime(dsl_dir_t *dd); ++inode_timespec_t dsl_dir_snap_cmtime(dsl_dir_t *dd); + void dsl_dir_set_reservation_sync_impl(dsl_dir_t *dd, uint64_t value, + dmu_tx_t *tx); + void dsl_dir_zapify(dsl_dir_t *dd, dmu_tx_t *tx); +diff --git a/include/sys/spa_impl.h b/include/sys/spa_impl.h +index b1e78c1d592..fa7490ace67 100644 +--- a/include/sys/spa_impl.h ++++ b/include/sys/spa_impl.h +@@ -153,7 +153,7 @@ struct spa { + uint64_t spa_freeze_txg; /* freeze pool at this txg */ + uint64_t spa_load_max_txg; /* best initial ub_txg */ + uint64_t spa_claim_max_txg; /* highest claimed birth txg */ +- timespec_t spa_loaded_ts; /* 1st successful open time */ ++ inode_timespec_t spa_loaded_ts; /* 1st successful open time */ + objset_t *spa_meta_objset; /* copy of dp->dp_meta_objset */ + kmutex_t spa_evicting_os_lock; /* Evicting objset list lock */ + list_t spa_evicting_os_list; /* Objsets being evicted. */ +diff --git a/include/sys/xvattr.h b/include/sys/xvattr.h +index 4779b632163..5d38927cd4b 100644 +--- a/include/sys/xvattr.h ++++ b/include/sys/xvattr.h +@@ -47,7 +47,7 @@ + * Structure of all optional attributes. + */ + typedef struct xoptattr { +- timestruc_t xoa_createtime; /* Create time of file */ ++ inode_timespec_t xoa_createtime; /* Create time of file */ + uint8_t xoa_archive; + uint8_t xoa_system; + uint8_t xoa_readonly; +diff --git a/include/sys/zfs_context.h b/include/sys/zfs_context.h +index 4fe35342dee..68c58f95531 100644 +--- a/include/sys/zfs_context.h ++++ b/include/sys/zfs_context.h +@@ -527,7 +527,7 @@ extern char *vn_dumpdir; + #define AV_SCANSTAMP_SZ 32 /* length of anti-virus scanstamp */ + + typedef struct xoptattr { +- timestruc_t xoa_createtime; /* Create time of file */ ++ inode_timespec_t xoa_createtime; /* Create time of file */ + uint8_t xoa_archive; + uint8_t xoa_system; + uint8_t xoa_readonly; +@@ -640,13 +640,6 @@ extern void delay(clock_t ticks); + #define USEC_TO_TICK(usec) ((usec) / (MICROSEC / hz)) + #define NSEC_TO_TICK(usec) ((usec) / (NANOSEC / hz)) + +-#define gethrestime_sec() time(NULL) +-#define gethrestime(t) \ +- do {\ +- (t)->tv_sec = gethrestime_sec();\ +- (t)->tv_nsec = 0;\ +- } while (0); +- + #define max_ncpus 64 + #define boot_ncpus (sysconf(_SC_NPROCESSORS_ONLN)) + +diff --git a/include/sys/zfs_znode.h b/include/sys/zfs_znode.h +index c292f03739e..26d1eb37557 100644 +--- a/include/sys/zfs_znode.h ++++ b/include/sys/zfs_znode.h +@@ -270,19 +270,36 @@ typedef struct znode_hold { + + extern unsigned int zfs_object_mutex_size; + +-/* Encode ZFS stored time values from a struct timespec */ ++/* ++ * Encode ZFS stored time values from a struct timespec / struct timespec64. ++ */ + #define ZFS_TIME_ENCODE(tp, stmp) \ +-{ \ ++do { \ + (stmp)[0] = (uint64_t)(tp)->tv_sec; \ + (stmp)[1] = (uint64_t)(tp)->tv_nsec; \ +-} ++} while (0) + +-/* Decode ZFS stored time values to a struct timespec */ ++#if defined(HAVE_INODE_TIMESPEC64_TIMES) ++/* ++ * Decode ZFS stored time values to a struct timespec64 ++ * 4.18 and newer kernels. ++ */ + #define ZFS_TIME_DECODE(tp, stmp) \ +-{ \ +- (tp)->tv_sec = (time_t)(stmp)[0]; \ +- (tp)->tv_nsec = (long)(stmp)[1]; \ +-} ++do { \ ++ (tp)->tv_sec = (time64_t)(stmp)[0]; \ ++ (tp)->tv_nsec = (long)(stmp)[1]; \ ++} while (0) ++#else ++/* ++ * Decode ZFS stored time values to a struct timespec ++ * 4.17 and older kernels. ++ */ ++#define ZFS_TIME_DECODE(tp, stmp) \ ++do { \ ++ (tp)->tv_sec = (time_t)(stmp)[0]; \ ++ (tp)->tv_nsec = (long)(stmp)[1]; \ ++} while (0) ++#endif /* HAVE_INODE_TIMESPEC64_TIMES */ + + /* + * Timestamp defines +diff --git a/include/sys/zpl.h b/include/sys/zpl.h +index 65ed4313603..e433fbc64ea 100644 +--- a/include/sys/zpl.h ++++ b/include/sys/zpl.h +@@ -189,4 +189,13 @@ zpl_dir_emit_dots(struct file *file, zpl_dir_context_t *ctx) + } + #endif /* HAVE_VFS_ITERATE */ + ++/* ++ * Linux 4.18, inode times converted from timespec to timespec64. ++ */ ++#if defined(HAVE_INODE_TIMESPEC64_TIMES) ++#define zpl_inode_timespec_trunc(ts, gran) timespec64_trunc(ts, gran) ++#else ++#define zpl_inode_timespec_trunc(ts, gran) timespec_trunc(ts, gran) ++#endif ++ + #endif /* _SYS_ZPL_H */ +diff --git a/lib/libspl/Makefile.am b/lib/libspl/Makefile.am +index 59bc8ffb42f..a6e63cb8868 100644 +--- a/lib/libspl/Makefile.am ++++ b/lib/libspl/Makefile.am +@@ -19,8 +19,6 @@ noinst_LTLIBRARIES = libspl.la + + USER_C = \ + getexecname.c \ +- gethrtime.c \ +- gethrestime.c \ + getmntany.c \ + list.c \ + mkdirp.c \ +diff --git a/lib/libspl/gethrestime.c b/lib/libspl/gethrestime.c +deleted file mode 100644 +index d37cc2d5994..00000000000 +--- a/lib/libspl/gethrestime.c ++++ /dev/null +@@ -1,38 +0,0 @@ +-/* +- * 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 <time.h> +-#include <sys/time.h> +- +-void +-gethrestime(timestruc_t *ts) +-{ +- struct timeval tv; +- +- gettimeofday(&tv, NULL); +- ts->tv_sec = tv.tv_sec; +- ts->tv_nsec = tv.tv_usec * NSEC_PER_USEC; +-} +diff --git a/lib/libspl/gethrtime.c b/lib/libspl/gethrtime.c +deleted file mode 100644 +index 95ceb18e119..00000000000 +--- a/lib/libspl/gethrtime.c ++++ /dev/null +@@ -1,45 +0,0 @@ +-/* +- * 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 <time.h> +-#include <sys/time.h> +-#include <stdlib.h> +-#include <stdio.h> +- +-hrtime_t +-gethrtime(void) +-{ +- struct timespec ts; +- int rc; +- +- rc = clock_gettime(CLOCK_MONOTONIC, &ts); +- if (rc) { +- fprintf(stderr, "Error: clock_gettime() = %d\n", rc); +- abort(); +- } +- +- return ((((u_int64_t)ts.tv_sec) * NANOSEC) + ts.tv_nsec); +-} +diff --git a/lib/libspl/include/sys/time.h b/lib/libspl/include/sys/time.h +index dc645fa5c37..04b3ba87bd3 100644 +--- a/lib/libspl/include/sys/time.h ++++ b/lib/libspl/include/sys/time.h +@@ -27,8 +27,9 @@ + #ifndef _LIBSPL_SYS_TIME_H + #define _LIBSPL_SYS_TIME_H + +-#include_next <sys/time.h> ++#include <time.h> + #include <sys/types.h> ++#include_next <sys/time.h> + + #ifndef SEC + #define SEC 1 +@@ -70,13 +71,33 @@ + #define SEC2NSEC(m) ((hrtime_t)(m) * (NANOSEC / SEC)) + #endif + +- + typedef long long hrtime_t; +-typedef struct timespec timestruc_t; +-typedef struct timespec timespec_t; +- +- +-extern hrtime_t gethrtime(void); +-extern void gethrestime(timestruc_t *); ++typedef struct timespec timespec_t; ++typedef struct timespec inode_timespec_t; ++ ++static inline void ++gethrestime(inode_timespec_t *ts) ++{ ++ struct timeval tv; ++ (void) gettimeofday(&tv, NULL); ++ ts->tv_sec = tv.tv_sec; ++ ts->tv_nsec = tv.tv_usec * NSEC_PER_USEC; ++} ++ ++static inline time_t ++gethrestime_sec(void) ++{ ++ struct timeval tv; ++ (void) gettimeofday(&tv, NULL); ++ return (tv.tv_sec); ++} ++ ++static inline hrtime_t ++gethrtime(void) ++{ ++ struct timespec ts; ++ (void) clock_gettime(CLOCK_MONOTONIC, &ts); ++ return ((((u_int64_t)ts.tv_sec) * NANOSEC) + ts.tv_nsec); ++} + + #endif /* _LIBSPL_SYS_TIME_H */ +diff --git a/lib/libzpool/kernel.c b/lib/libzpool/kernel.c +index e67d13c9265..3ea8778b1dc 100644 +--- a/lib/libzpool/kernel.c ++++ b/lib/libzpool/kernel.c +@@ -498,7 +498,7 @@ cv_timedwait(kcondvar_t *cv, kmutex_t *mp, clock_t abstime) + { + int error; + struct timeval tv; +- timestruc_t ts; ++ struct timespec ts; + clock_t delta; + + ASSERT3U(cv->cv_magic, ==, CV_MAGIC); +@@ -536,7 +536,7 @@ cv_timedwait_hires(kcondvar_t *cv, kmutex_t *mp, hrtime_t tim, hrtime_t res, + { + int error; + struct timeval tv; +- timestruc_t ts; ++ struct timespec ts; + hrtime_t delta; + + ASSERT(flag == 0 || flag == CALLOUT_FLAG_ABSOLUTE); +diff --git a/module/zfs/dmu_objset.c b/module/zfs/dmu_objset.c +index 3425d542f98..449ebedfa16 100644 +--- a/module/zfs/dmu_objset.c ++++ b/module/zfs/dmu_objset.c +@@ -860,7 +860,7 @@ dmu_objset_evict_done(objset_t *os) + kmem_free(os, sizeof (objset_t)); + } + +-timestruc_t ++inode_timespec_t + dmu_objset_snap_cmtime(objset_t *os) + { + return (dsl_dir_snap_cmtime(os->os_dsl_dataset->ds_dir)); +diff --git a/module/zfs/dsl_dir.c b/module/zfs/dsl_dir.c +index a3ef5896a3f..deecf6bc53e 100644 +--- a/module/zfs/dsl_dir.c ++++ b/module/zfs/dsl_dir.c +@@ -1975,10 +1975,10 @@ dsl_dir_transfer_possible(dsl_dir_t *sdd, dsl_dir_t *tdd, + return (0); + } + +-timestruc_t ++inode_timespec_t + dsl_dir_snap_cmtime(dsl_dir_t *dd) + { +- timestruc_t t; ++ inode_timespec_t t; + + mutex_enter(&dd->dd_lock); + t = dd->dd_snap_cmtime; +@@ -1990,7 +1990,7 @@ dsl_dir_snap_cmtime(dsl_dir_t *dd) + void + dsl_dir_snap_cmtime_update(dsl_dir_t *dd) + { +- timestruc_t t; ++ inode_timespec_t t; + + gethrestime(&t); + mutex_enter(&dd->dd_lock); +diff --git a/module/zfs/fm.c b/module/zfs/fm.c +index cb148149376..9d26cc99eeb 100644 +--- a/module/zfs/fm.c ++++ b/module/zfs/fm.c +@@ -508,8 +508,8 @@ zfs_zevent_insert(zevent_t *ev) + int + zfs_zevent_post(nvlist_t *nvl, nvlist_t *detector, zevent_cb_t *cb) + { ++ inode_timespec_t tv; + int64_t tv_array[2]; +- timestruc_t tv; + uint64_t eid; + size_t nvl_size = 0; + zevent_t *ev; +diff --git a/module/zfs/zfs_ctldir.c b/module/zfs/zfs_ctldir.c +index 3b5fb196f1d..3ff2c101b67 100644 +--- a/module/zfs/zfs_ctldir.c ++++ b/module/zfs/zfs_ctldir.c +@@ -451,7 +451,7 @@ static struct inode * + zfsctl_inode_alloc(zfsvfs_t *zfsvfs, uint64_t id, + const struct file_operations *fops, const struct inode_operations *ops) + { +- struct timespec now; ++ inode_timespec_t now; + struct inode *ip; + znode_t *zp; + +diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c +index 0d2b61a1c31..34ea751c320 100644 +--- a/module/zfs/zfs_vnops.c ++++ b/module/zfs/zfs_vnops.c +@@ -3158,7 +3158,7 @@ zfs_setattr(struct inode *ip, vattr_t *vap, int flags, cred_t *cr) + + if (mask & (ATTR_MTIME | ATTR_SIZE)) { + ZFS_TIME_ENCODE(&vap->va_mtime, mtime); +- ZTOI(zp)->i_mtime = timespec_trunc(vap->va_mtime, ++ ZTOI(zp)->i_mtime = zpl_inode_timespec_trunc(vap->va_mtime, + ZTOI(zp)->i_sb->s_time_gran); + + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL, +@@ -3167,7 +3167,7 @@ zfs_setattr(struct inode *ip, vattr_t *vap, int flags, cred_t *cr) + + if (mask & (ATTR_CTIME | ATTR_SIZE)) { + ZFS_TIME_ENCODE(&vap->va_ctime, ctime); +- ZTOI(zp)->i_ctime = timespec_trunc(vap->va_ctime, ++ ZTOI(zp)->i_ctime = zpl_inode_timespec_trunc(vap->va_ctime, + ZTOI(zp)->i_sb->s_time_gran); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, + ctime, sizeof (ctime)); +diff --git a/module/zfs/zfs_znode.c b/module/zfs/zfs_znode.c +index f508a248f0f..e222c791183 100644 +--- a/module/zfs/zfs_znode.c ++++ b/module/zfs/zfs_znode.c +@@ -700,7 +700,7 @@ zfs_mknode(znode_t *dzp, vattr_t *vap, dmu_tx_t *tx, cred_t *cr, + uint64_t rdev = 0; + zfsvfs_t *zfsvfs = ZTOZSB(dzp); + dmu_buf_t *db; +- timestruc_t now; ++ inode_timespec_t now; + uint64_t gen, obj; + int bonuslen; + int dnodesize; +@@ -1349,7 +1349,7 @@ void + zfs_tstamp_update_setup(znode_t *zp, uint_t flag, uint64_t mtime[2], + uint64_t ctime[2]) + { +- timestruc_t now; ++ inode_timespec_t now; + + gethrestime(&now); + +diff --git a/module/zfs/zpl_inode.c b/module/zfs/zpl_inode.c +index 3b5643d0917..41b91cabcb9 100644 +--- a/module/zfs/zpl_inode.c ++++ b/module/zfs/zpl_inode.c +@@ -384,9 +384,10 @@ zpl_setattr(struct dentry *dentry, struct iattr *ia) + vap->va_mtime = ia->ia_mtime; + vap->va_ctime = ia->ia_ctime; + +- if (vap->va_mask & ATTR_ATIME) +- ip->i_atime = timespec_trunc(ia->ia_atime, ++ if (vap->va_mask & ATTR_ATIME) { ++ ip->i_atime = zpl_inode_timespec_trunc(ia->ia_atime, + ip->i_sb->s_time_gran); ++ } + + cookie = spl_fstrans_mark(); + error = -zfs_setattr(ip, vap, 0, cr); +From ecaa72315658207608c9e43df870de77af36c1ff Mon Sep 17 00:00:00 2001 +From: Brian Behlendorf <behlendorf1@llnl.gov> +Date: Fri, 15 Jun 2018 15:05:21 -0700 +Subject: [PATCH] Linux compat 4.18: check_disk_size_change() + +Added support for the bops->check_events() interface which was +added in the 2.6.38 kernel to replace bops->media_changed(). +Fully implementing this functionality allows the volume resize +code to rely on revalidate_disk(), which is the preferred +mechanism, and removes the need to use check_disk_size_change(). + +In order for bops->check_events() to lookup the zvol_state_t +stored in the disk->private_data the zvol_state_lock needs to +be held. Since the check events interface may poll the mutex +has been converted to a rwlock for better concurrently. The +rwlock need only be taken as a writer in the zvol_free() path +when disk->private_data is set to NULL. + +The configure checks for the block_device_operations structure +were consolidated in a single kernel-block-device-operations.m4 +file. + +The ZFS_AC_KERNEL_BDEV_BLOCK_DEVICE_OPERATIONS configure checks +and assoicated dead code was removed. This interface was added +to the 2.6.28 kernel which predates the oldest supported 2.6.32 +kernel and will therefore always be available. + +Updated maximum Linux version in META file. The 4.17 kernel +was released on 2018-06-03 and ZoL is compatible with the +finalized kernel. + +Reviewed-by: Boris Protopopov <boris.protopopov@actifio.com> +Reviewed-by: Sara Hartse <sara.hartse@delphix.com> +Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> +Closes #7611 +Backported-by: Richard Yao <ryao@gentoo.org> +--- + config/kernel-bdev-block-device-operations.m4 | 34 --- + ...el-block-device-operations-release-void.m4 | 29 -- + config/kernel-block-device-operations.m4 | 57 ++++ + config/kernel.m4 | 2 +- + include/linux/blkdev_compat.h | 1 + + module/zfs/zvol.c | 286 ++++++++---------- + 6 files changed, 192 insertions(+), 217 deletions(-) + delete mode 100644 config/kernel-bdev-block-device-operations.m4 + delete mode 100644 config/kernel-block-device-operations-release-void.m4 + create mode 100644 config/kernel-block-device-operations.m4 + +diff --git a/config/kernel-bdev-block-device-operations.m4 b/config/kernel-bdev-block-device-operations.m4 +deleted file mode 100644 +index faacc195da9..00000000000 +--- a/config/kernel-bdev-block-device-operations.m4 ++++ /dev/null +@@ -1,34 +0,0 @@ +-dnl # +-dnl # 2.6.x API change +-dnl # +-AC_DEFUN([ZFS_AC_KERNEL_BDEV_BLOCK_DEVICE_OPERATIONS], [ +- AC_MSG_CHECKING([block device operation prototypes]) +- tmp_flags="$EXTRA_KCFLAGS" +- EXTRA_KCFLAGS="${NO_UNUSED_BUT_SET_VARIABLE}" +- ZFS_LINUX_TRY_COMPILE([ +- #include <linux/blkdev.h> +- +- int blk_open(struct block_device *bdev, fmode_t mode) +- { return 0; } +- int blk_ioctl(struct block_device *bdev, fmode_t mode, +- unsigned x, unsigned long y) { return 0; } +- int blk_compat_ioctl(struct block_device * bdev, fmode_t mode, +- unsigned x, unsigned long y) { return 0; } +- +- static const struct block_device_operations +- bops __attribute__ ((unused)) = { +- .open = blk_open, +- .release = NULL, +- .ioctl = blk_ioctl, +- .compat_ioctl = blk_compat_ioctl, +- }; +- ],[ +- ],[ +- AC_MSG_RESULT(struct block_device) +- AC_DEFINE(HAVE_BDEV_BLOCK_DEVICE_OPERATIONS, 1, +- [struct block_device_operations use bdevs]) +- ],[ +- AC_MSG_RESULT(struct inode) +- ]) +- EXTRA_KCFLAGS="$tmp_flags" +-]) +diff --git a/config/kernel-block-device-operations-release-void.m4 b/config/kernel-block-device-operations-release-void.m4 +deleted file mode 100644 +index a73f858722a..00000000000 +--- a/config/kernel-block-device-operations-release-void.m4 ++++ /dev/null +@@ -1,29 +0,0 @@ +-dnl # +-dnl # 3.10.x API change +-dnl # +-AC_DEFUN([ZFS_AC_KERNEL_BLOCK_DEVICE_OPERATIONS_RELEASE_VOID], [ +- AC_MSG_CHECKING([whether block_device_operations.release is void]) +- tmp_flags="$EXTRA_KCFLAGS" +- EXTRA_KCFLAGS="${NO_UNUSED_BUT_SET_VARIABLE}" +- ZFS_LINUX_TRY_COMPILE([ +- #include <linux/blkdev.h> +- +- void blk_release(struct gendisk *g, fmode_t mode) { return; } +- +- static const struct block_device_operations +- bops __attribute__ ((unused)) = { +- .open = NULL, +- .release = blk_release, +- .ioctl = NULL, +- .compat_ioctl = NULL, +- }; +- ],[ +- ],[ +- AC_MSG_RESULT(void) +- AC_DEFINE(HAVE_BLOCK_DEVICE_OPERATIONS_RELEASE_VOID, 1, +- [struct block_device_operations.release returns void]) +- ],[ +- AC_MSG_RESULT(int) +- ]) +- EXTRA_KCFLAGS="$tmp_flags" +-]) +diff --git a/config/kernel-block-device-operations.m4 b/config/kernel-block-device-operations.m4 +new file mode 100644 +index 00000000000..5f2811c1534 +--- /dev/null ++++ b/config/kernel-block-device-operations.m4 +@@ -0,0 +1,57 @@ ++dnl # ++dnl # 2.6.38 API change ++dnl # ++AC_DEFUN([ZFS_AC_KERNEL_BLOCK_DEVICE_OPERATIONS_CHECK_EVENTS], [ ++ AC_MSG_CHECKING([whether bops->check_events() exists]) ++ tmp_flags="$EXTRA_KCFLAGS" ++ EXTRA_KCFLAGS="${NO_UNUSED_BUT_SET_VARIABLE}" ++ ZFS_LINUX_TRY_COMPILE([ ++ #include <linux/blkdev.h> ++ ++ unsigned int blk_check_events(struct gendisk *disk, ++ unsigned int clearing) { return (0); } ++ ++ static const struct block_device_operations ++ bops __attribute__ ((unused)) = { ++ .check_events = blk_check_events, ++ }; ++ ],[ ++ ],[ ++ AC_MSG_RESULT(yes) ++ AC_DEFINE(HAVE_BLOCK_DEVICE_OPERATIONS_CHECK_EVENTS, 1, ++ [bops->check_events() exists]) ++ ],[ ++ AC_MSG_RESULT(no) ++ ]) ++ EXTRA_KCFLAGS="$tmp_flags" ++]) ++ ++dnl # ++dnl # 3.10.x API change ++dnl # ++AC_DEFUN([ZFS_AC_KERNEL_BLOCK_DEVICE_OPERATIONS_RELEASE_VOID], [ ++ AC_MSG_CHECKING([whether bops->release() is void]) ++ tmp_flags="$EXTRA_KCFLAGS" ++ EXTRA_KCFLAGS="${NO_UNUSED_BUT_SET_VARIABLE}" ++ ZFS_LINUX_TRY_COMPILE([ ++ #include <linux/blkdev.h> ++ ++ void blk_release(struct gendisk *g, fmode_t mode) { return; } ++ ++ static const struct block_device_operations ++ bops __attribute__ ((unused)) = { ++ .open = NULL, ++ .release = blk_release, ++ .ioctl = NULL, ++ .compat_ioctl = NULL, ++ }; ++ ],[ ++ ],[ ++ AC_MSG_RESULT(void) ++ AC_DEFINE(HAVE_BLOCK_DEVICE_OPERATIONS_RELEASE_VOID, 1, ++ [bops->release() returns void]) ++ ],[ ++ AC_MSG_RESULT(int) ++ ]) ++ EXTRA_KCFLAGS="$tmp_flags" ++]) +diff --git a/config/kernel.m4 b/config/kernel.m4 +index 375e4b79ae6..c7ca260c5f0 100644 +--- a/config/kernel.m4 ++++ b/config/kernel.m4 +@@ -12,7 +12,7 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [ + ZFS_AC_KERNEL_CURRENT_BIO_TAIL + ZFS_AC_KERNEL_SUPER_USER_NS + ZFS_AC_KERNEL_SUBMIT_BIO +- ZFS_AC_KERNEL_BDEV_BLOCK_DEVICE_OPERATIONS ++ ZFS_AC_KERNEL_BLOCK_DEVICE_OPERATIONS_CHECK_EVENTS + ZFS_AC_KERNEL_BLOCK_DEVICE_OPERATIONS_RELEASE_VOID + ZFS_AC_KERNEL_TYPE_FMODE_T + ZFS_AC_KERNEL_3ARG_BLKDEV_GET +diff --git a/include/linux/blkdev_compat.h b/include/linux/blkdev_compat.h +index f99980ab3e0..27f05662662 100644 +--- a/include/linux/blkdev_compat.h ++++ b/include/linux/blkdev_compat.h +@@ -32,6 +32,7 @@ + #include <linux/blkdev.h> + #include <linux/elevator.h> + #include <linux/backing-dev.h> ++#include <linux/msdos_fs.h> /* for SECTOR_* */ + + #ifndef HAVE_FMODE_T + typedef unsigned __bitwise__ fmode_t; +diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c +index 3e7059b340b..e57d2e70332 100644 +--- a/module/zfs/zvol.c ++++ b/module/zfs/zvol.c +@@ -99,7 +99,7 @@ unsigned long zvol_max_discard_blocks = 16384; + unsigned int zvol_volmode = ZFS_VOLMODE_GEOM; + + static taskq_t *zvol_taskq; +-static kmutex_t zvol_state_lock; ++static krwlock_t zvol_state_lock; + static list_t zvol_state_list; + + #define ZVOL_HT_SIZE 1024 +@@ -176,17 +176,17 @@ zvol_find_by_dev(dev_t dev) + { + zvol_state_t *zv; + +- mutex_enter(&zvol_state_lock); ++ 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_dev == dev) { +- mutex_exit(&zvol_state_lock); ++ rw_exit(&zvol_state_lock); + return (zv); + } + mutex_exit(&zv->zv_state_lock); + } +- mutex_exit(&zvol_state_lock); ++ rw_exit(&zvol_state_lock); + + return (NULL); + } +@@ -204,7 +204,7 @@ zvol_find_by_name_hash(const char *name, uint64_t hash, int mode) + zvol_state_t *zv; + struct hlist_node *p = NULL; + +- mutex_enter(&zvol_state_lock); ++ rw_enter(&zvol_state_lock, RW_READER); + hlist_for_each(p, ZVOL_HT_HEAD(hash)) { + zv = hlist_entry(p, zvol_state_t, zv_hlink); + mutex_enter(&zv->zv_state_lock); +@@ -227,12 +227,12 @@ zvol_find_by_name_hash(const char *name, uint64_t hash, int mode) + strncmp(zv->zv_name, name, MAXNAMELEN) + == 0); + } +- mutex_exit(&zvol_state_lock); ++ rw_exit(&zvol_state_lock); + return (zv); + } + mutex_exit(&zv->zv_state_lock); + } +- mutex_exit(&zvol_state_lock); ++ rw_exit(&zvol_state_lock); + + return (NULL); + } +@@ -339,24 +339,6 @@ zvol_get_stats(objset_t *os, nvlist_t *nv) + return (SET_ERROR(error)); + } + +-static void +-zvol_size_changed(zvol_state_t *zv, uint64_t volsize) +-{ +- struct block_device *bdev; +- +- ASSERT(MUTEX_HELD(&zv->zv_state_lock)); +- +- bdev = bdget_disk(zv->zv_disk, 0); +- if (bdev == NULL) +- return; +- +- set_capacity(zv->zv_disk, volsize >> 9); +- zv->zv_volsize = volsize; +- check_disk_size_change(zv->zv_disk, bdev); +- +- bdput(bdev); +-} +- + /* + * Sanity check volume size. + */ +@@ -409,31 +391,17 @@ zvol_update_volsize(uint64_t volsize, objset_t *os) + return (error); + } + +-static int +-zvol_update_live_volsize(zvol_state_t *zv, uint64_t volsize) +-{ +- zvol_size_changed(zv, volsize); +- +- /* +- * We should post a event here describing the expansion. However, +- * the zfs_ereport_post() interface doesn't nicely support posting +- * events for zvols, it assumes events relate to vdevs or zios. +- */ +- +- return (0); +-} +- + /* +- * Set ZFS_PROP_VOLSIZE set entry point. ++ * Set ZFS_PROP_VOLSIZE set entry point. Note that modifying the volume ++ * size will result in a udev "change" event being generated. + */ + int + zvol_set_volsize(const char *name, uint64_t volsize) + { +- zvol_state_t *zv = NULL; + objset_t *os = NULL; +- int error; +- dmu_object_info_t *doi; ++ struct gendisk *disk = NULL; + uint64_t readonly; ++ int error; + boolean_t owned = B_FALSE; + + error = dsl_prop_get_integer(name, +@@ -443,7 +411,7 @@ zvol_set_volsize(const char *name, uint64_t volsize) + if (readonly) + return (SET_ERROR(EROFS)); + +- zv = zvol_find_by_name(name, RW_READER); ++ zvol_state_t *zv = zvol_find_by_name(name, RW_READER); + + ASSERT(zv == NULL || (MUTEX_HELD(&zv->zv_state_lock) && + RW_READ_HELD(&zv->zv_suspend_lock))); +@@ -464,16 +432,18 @@ zvol_set_volsize(const char *name, uint64_t volsize) + os = zv->zv_objset; + } + +- doi = kmem_alloc(sizeof (dmu_object_info_t), KM_SLEEP); ++ dmu_object_info_t *doi = kmem_alloc(sizeof (*doi), KM_SLEEP); + + if ((error = dmu_object_info(os, ZVOL_OBJ, doi)) || + (error = zvol_check_volsize(volsize, doi->doi_data_block_size))) + goto out; + + error = zvol_update_volsize(volsize, os); +- +- if (error == 0 && zv != NULL) +- error = zvol_update_live_volsize(zv, volsize); ++ if (error == 0 && zv != NULL) { ++ zv->zv_volsize = volsize; ++ zv->zv_changed = 1; ++ disk = zv->zv_disk; ++ } + out: + kmem_free(doi, sizeof (dmu_object_info_t)); + +@@ -488,6 +458,9 @@ zvol_set_volsize(const char *name, uint64_t volsize) + if (zv != NULL) + mutex_exit(&zv->zv_state_lock); + ++ if (disk != NULL) ++ revalidate_disk(disk); ++ + return (SET_ERROR(error)); + } + +@@ -543,8 +516,8 @@ zvol_set_volblocksize(const char *name, uint64_t volblocksize) + if (zv == NULL) + return (SET_ERROR(ENXIO)); + +- ASSERT(MUTEX_HELD(&zv->zv_state_lock) && +- RW_READ_HELD(&zv->zv_suspend_lock)); ++ ASSERT(MUTEX_HELD(&zv->zv_state_lock)); ++ ASSERT(RW_READ_HELD(&zv->zv_suspend_lock)); + + if (zv->zv_flags & ZVOL_RDONLY) { + mutex_exit(&zv->zv_state_lock); +@@ -1120,7 +1093,7 @@ zvol_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio) + static void + zvol_insert(zvol_state_t *zv) + { +- ASSERT(MUTEX_HELD(&zvol_state_lock)); ++ ASSERT(RW_WRITE_HELD(&zvol_state_lock)); + ASSERT3U(MINOR(zv->zv_dev) & ZVOL_MINOR_MASK, ==, 0); + list_insert_head(&zvol_state_list, zv); + hlist_add_head(&zv->zv_hlink, ZVOL_HT_HEAD(zv->zv_hash)); +@@ -1132,7 +1105,7 @@ zvol_insert(zvol_state_t *zv) + static void + zvol_remove(zvol_state_t *zv) + { +- ASSERT(MUTEX_HELD(&zvol_state_lock)); ++ ASSERT(RW_WRITE_HELD(&zvol_state_lock)); + list_remove(&zvol_state_list, zv); + hlist_del(&zv->zv_hlink); + } +@@ -1148,8 +1121,8 @@ zvol_setup_zv(zvol_state_t *zv) + uint64_t ro; + objset_t *os = zv->zv_objset; + +- ASSERT(MUTEX_HELD(&zv->zv_state_lock) && +- RW_LOCK_HELD(&zv->zv_suspend_lock)); ++ ASSERT(MUTEX_HELD(&zv->zv_state_lock)); ++ ASSERT(RW_LOCK_HELD(&zv->zv_suspend_lock)); + + error = dsl_prop_get_integer(zv->zv_name, "readonly", &ro, NULL); + if (error) +@@ -1227,8 +1200,8 @@ zvol_suspend(const char *name) + return (NULL); + + /* block all I/O, release in zvol_resume. */ +- ASSERT(MUTEX_HELD(&zv->zv_state_lock) && +- RW_WRITE_HELD(&zv->zv_suspend_lock)); ++ ASSERT(MUTEX_HELD(&zv->zv_state_lock)); ++ ASSERT(RW_WRITE_HELD(&zv->zv_suspend_lock)); + + atomic_inc(&zv->zv_suspend_ref); + +@@ -1349,9 +1322,7 @@ zvol_open(struct block_device *bdev, fmode_t flag) + int error = 0; + boolean_t drop_suspend = B_FALSE; + +- ASSERT(!mutex_owned(&zvol_state_lock)); +- +- mutex_enter(&zvol_state_lock); ++ rw_enter(&zvol_state_lock, RW_READER); + /* + * Obtain a copy of private_data under the zvol_state_lock to make + * sure that either the result of zvol free code path setting +@@ -1360,7 +1331,7 @@ zvol_open(struct block_device *bdev, fmode_t flag) + */ + zv = bdev->bd_disk->private_data; + if (zv == NULL) { +- mutex_exit(&zvol_state_lock); ++ rw_exit(&zvol_state_lock); + return (SET_ERROR(-ENXIO)); + } + +@@ -1378,8 +1349,7 @@ zvol_open(struct block_device *bdev, fmode_t flag) + } else { + rw_exit(&zv->zv_suspend_lock); + } +- +- mutex_exit(&zvol_state_lock); ++ rw_exit(&zvol_state_lock); + + if (zv->zv_open_count == 0) { + error = zvol_first_open(zv); +@@ -1394,11 +1364,18 @@ zvol_open(struct block_device *bdev, fmode_t flag) + + zv->zv_open_count++; + ++ mutex_exit(&zv->zv_state_lock); ++ if (drop_suspend) ++ rw_exit(&zv->zv_suspend_lock); ++ + check_disk_change(bdev); + ++ return (0); ++ + out_open_count: + if (zv->zv_open_count == 0) + zvol_last_close(zv); ++ + out_mutex: + mutex_exit(&zv->zv_state_lock); + if (drop_suspend) +@@ -1419,26 +1396,34 @@ zvol_release(struct gendisk *disk, fmode_t mode) + zvol_state_t *zv; + boolean_t drop_suspend = B_FALSE; + +- ASSERT(!mutex_owned(&zvol_state_lock)); +- +- mutex_enter(&zvol_state_lock); ++ rw_enter(&zvol_state_lock, RW_READER); + zv = disk->private_data; +- ASSERT(zv && zv->zv_open_count > 0); +- +- /* take zv_suspend_lock before zv_state_lock */ +- rw_enter(&zv->zv_suspend_lock, RW_READER); + + mutex_enter(&zv->zv_state_lock); +- mutex_exit(&zvol_state_lock); ++ ASSERT(zv && zv->zv_open_count > 0); + + /* + * make sure zvol is not suspended during last close + * (hold zv_suspend_lock), otherwise, drop the lock + */ +- if (zv->zv_open_count == 1) +- drop_suspend = B_TRUE; +- else +- rw_exit(&zv->zv_suspend_lock); ++ if (zv->zv_open_count == 1) { ++ if (!rw_tryenter(&zv->zv_suspend_lock, RW_READER)) { ++ mutex_exit(&zv->zv_state_lock); ++ 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 != 1) { ++ rw_exit(&zv->zv_suspend_lock); ++ drop_suspend = B_FALSE; ++ } ++ } ++ } else { ++ drop_suspend = B_FALSE; ++ } ++ rw_exit(&zvol_state_lock); ++ ++ 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) +@@ -1461,7 +1446,7 @@ zvol_ioctl(struct block_device *bdev, fmode_t mode, + zvol_state_t *zv = bdev->bd_disk->private_data; + int error = 0; + +- ASSERT(zv && zv->zv_open_count > 0); ++ ASSERT3U(zv->zv_open_count, >, 0); + + switch (cmd) { + case BLKFLSBUF: +@@ -1501,23 +1486,62 @@ zvol_compat_ioctl(struct block_device *bdev, fmode_t mode, + #define zvol_compat_ioctl NULL + #endif + ++/* ++ * Linux 2.6.38 preferred interface. ++ */ ++#ifdef HAVE_BLOCK_DEVICE_OPERATIONS_CHECK_EVENTS ++static unsigned int ++zvol_check_events(struct gendisk *disk, unsigned int clearing) ++{ ++ unsigned int mask = 0; ++ ++ rw_enter(&zvol_state_lock, RW_READER); ++ ++ zvol_state_t *zv = disk->private_data; ++ if (zv != NULL) { ++ mutex_enter(&zv->zv_state_lock); ++ mask = zv->zv_changed ? DISK_EVENT_MEDIA_CHANGE : 0; ++ zv->zv_changed = 0; ++ mutex_exit(&zv->zv_state_lock); ++ } ++ ++ rw_exit(&zvol_state_lock); ++ ++ return (mask); ++} ++#else + static int zvol_media_changed(struct gendisk *disk) + { ++ int changed = 0; ++ ++ rw_enter(&zvol_state_lock, RW_READER); ++ + zvol_state_t *zv = disk->private_data; ++ if (zv != NULL) { ++ mutex_enter(&zv->zv_state_lock); ++ changed = zv->zv_changed; ++ zv->zv_changed = 0; ++ mutex_exit(&zv->zv_state_lock); ++ } + +- ASSERT(zv && zv->zv_open_count > 0); ++ rw_exit(&zvol_state_lock); + +- return (zv->zv_changed); ++ return (changed); + } ++#endif + + static int zvol_revalidate_disk(struct gendisk *disk) + { +- zvol_state_t *zv = disk->private_data; ++ rw_enter(&zvol_state_lock, RW_READER); + +- ASSERT(zv && zv->zv_open_count > 0); ++ zvol_state_t *zv = disk->private_data; ++ if (zv != NULL) { ++ mutex_enter(&zv->zv_state_lock); ++ set_capacity(zv->zv_disk, zv->zv_volsize >> SECTOR_BITS); ++ mutex_exit(&zv->zv_state_lock); ++ } + +- zv->zv_changed = 0; +- set_capacity(zv->zv_disk, zv->zv_volsize >> 9); ++ rw_exit(&zvol_state_lock); + + return (0); + } +@@ -1534,7 +1558,7 @@ zvol_getgeo(struct block_device *bdev, struct hd_geometry *geo) + zvol_state_t *zv = bdev->bd_disk->private_data; + sector_t sectors; + +- ASSERT(zv && zv->zv_open_count > 0); ++ ASSERT3U(zv->zv_open_count, >, 0); + + sectors = get_capacity(zv->zv_disk); + +@@ -1567,68 +1591,20 @@ zvol_probe(dev_t dev, int *part, void *arg) + return (kobj); + } + +-#ifdef HAVE_BDEV_BLOCK_DEVICE_OPERATIONS + static struct block_device_operations zvol_ops = { + .open = zvol_open, + .release = zvol_release, + .ioctl = zvol_ioctl, + .compat_ioctl = zvol_compat_ioctl, +- .media_changed = zvol_media_changed, +- .revalidate_disk = zvol_revalidate_disk, +- .getgeo = zvol_getgeo, +- .owner = THIS_MODULE, +-}; +- +-#else /* HAVE_BDEV_BLOCK_DEVICE_OPERATIONS */ +- +-static int +-zvol_open_by_inode(struct inode *inode, struct file *file) +-{ +- return (zvol_open(inode->i_bdev, file->f_mode)); +-} +- +-static int +-zvol_release_by_inode(struct inode *inode, struct file *file) +-{ +- return (zvol_release(inode->i_bdev->bd_disk, file->f_mode)); +-} +- +-static int +-zvol_ioctl_by_inode(struct inode *inode, struct file *file, +- unsigned int cmd, unsigned long arg) +-{ +- if (file == NULL || inode == NULL) +- return (SET_ERROR(-EINVAL)); +- +- return (zvol_ioctl(inode->i_bdev, file->f_mode, cmd, arg)); +-} +- +-#ifdef CONFIG_COMPAT +-static long +-zvol_compat_ioctl_by_inode(struct file *file, +- unsigned int cmd, unsigned long arg) +-{ +- if (file == NULL) +- return (SET_ERROR(-EINVAL)); +- +- return (zvol_compat_ioctl(file->f_dentry->d_inode->i_bdev, +- file->f_mode, cmd, arg)); +-} ++#ifdef HAVE_BLOCK_DEVICE_OPERATIONS_CHECK_EVENTS ++ .check_events = zvol_check_events, + #else +-#define zvol_compat_ioctl_by_inode NULL +-#endif +- +-static struct block_device_operations zvol_ops = { +- .open = zvol_open_by_inode, +- .release = zvol_release_by_inode, +- .ioctl = zvol_ioctl_by_inode, +- .compat_ioctl = zvol_compat_ioctl_by_inode, + .media_changed = zvol_media_changed, ++#endif + .revalidate_disk = zvol_revalidate_disk, + .getgeo = zvol_getgeo, + .owner = THIS_MODULE, + }; +-#endif /* HAVE_BDEV_BLOCK_DEVICE_OPERATIONS */ + + /* + * Allocate memory for a new zvol_state_t and setup the required +@@ -1681,6 +1657,10 @@ zvol_alloc(dev_t dev, const char *name) + rw_init(&zv->zv_suspend_lock, NULL, RW_DEFAULT, NULL); + + zv->zv_disk->major = zvol_major; ++#ifdef HAVE_BLOCK_DEVICE_OPERATIONS_CHECK_EVENTS ++ zv->zv_disk->events = DISK_EVENT_MEDIA_CHANGE; ++#endif ++ + if (volmode == ZFS_VOLMODE_DEV) { + /* + * ZFS_VOLMODE_DEV disable partitioning on ZVOL devices: set +@@ -1725,7 +1705,6 @@ zvol_free(void *arg) + { + zvol_state_t *zv = arg; + +- ASSERT(!MUTEX_HELD(&zvol_state_lock)); + ASSERT(!RW_LOCK_HELD(&zv->zv_suspend_lock)); + ASSERT(!MUTEX_HELD(&zv->zv_state_lock)); + ASSERT(zv->zv_open_count == 0); +@@ -1852,9 +1831,9 @@ zvol_create_minor_impl(const char *name) + kmem_free(doi, sizeof (dmu_object_info_t)); + + if (error == 0) { +- mutex_enter(&zvol_state_lock); ++ rw_enter(&zvol_state_lock, RW_WRITER); + zvol_insert(zv); +- mutex_exit(&zvol_state_lock); ++ rw_exit(&zvol_state_lock); + add_disk(zv->zv_disk); + } else { + ida_simple_remove(&zvol_ida, idx); +@@ -1871,7 +1850,7 @@ zvol_rename_minor(zvol_state_t *zv, const char *newname) + { + int readonly = get_disk_ro(zv->zv_disk); + +- ASSERT(MUTEX_HELD(&zvol_state_lock)); ++ ASSERT(RW_LOCK_HELD(&zvol_state_lock)); + ASSERT(MUTEX_HELD(&zv->zv_state_lock)); + + strlcpy(zv->zv_name, newname, sizeof (zv->zv_name)); +@@ -2111,7 +2090,7 @@ zvol_remove_minors_impl(const char *name) + list_create(&free_list, sizeof (zvol_state_t), + offsetof(zvol_state_t, zv_next)); + +- mutex_enter(&zvol_state_lock); ++ rw_enter(&zvol_state_lock, RW_WRITER); + + for (zv = list_head(&zvol_state_list); zv != NULL; zv = zv_next) { + zv_next = list_next(&zvol_state_list, zv); +@@ -2136,15 +2115,15 @@ zvol_remove_minors_impl(const char *name) + zvol_remove(zv); + + /* +- * clear this while holding zvol_state_lock so +- * zvol_open won't open it ++ * Cleared while holding zvol_state_lock as a writer ++ * which will prevent zvol_open() from opening it. + */ + zv->zv_disk->private_data = NULL; + + /* Drop zv_state_lock before zvol_free() */ + mutex_exit(&zv->zv_state_lock); + +- /* try parallel zv_free, if failed do it in place */ ++ /* Try parallel zv_free, if failed do it in place */ + t = taskq_dispatch(system_taskq, zvol_free, zv, + TQ_SLEEP); + if (t == TASKQID_INVALID) +@@ -2155,11 +2134,9 @@ zvol_remove_minors_impl(const char *name) + mutex_exit(&zv->zv_state_lock); + } + } +- mutex_exit(&zvol_state_lock); ++ rw_exit(&zvol_state_lock); + +- /* +- * Drop zvol_state_lock before calling zvol_free() +- */ ++ /* Drop zvol_state_lock before calling zvol_free() */ + while ((zv = list_head(&free_list)) != NULL) { + list_remove(&free_list, zv); + zvol_free(zv); +@@ -2178,7 +2155,7 @@ zvol_remove_minor_impl(const char *name) + if (zvol_inhibit_dev) + return; + +- mutex_enter(&zvol_state_lock); ++ rw_enter(&zvol_state_lock, RW_WRITER); + + for (zv = list_head(&zvol_state_list); zv != NULL; zv = zv_next) { + zv_next = list_next(&zvol_state_list, zv); +@@ -2198,7 +2175,10 @@ zvol_remove_minor_impl(const char *name) + } + zvol_remove(zv); + +- /* clear this so zvol_open won't open it */ ++ /* ++ * Cleared while holding zvol_state_lock as a writer ++ * which will prevent zvol_open() from opening it. ++ */ + zv->zv_disk->private_data = NULL; + + mutex_exit(&zv->zv_state_lock); +@@ -2209,7 +2189,7 @@ zvol_remove_minor_impl(const char *name) + } + + /* Drop zvol_state_lock before calling zvol_free() */ +- mutex_exit(&zvol_state_lock); ++ rw_exit(&zvol_state_lock); + + if (zv != NULL) + zvol_free(zv); +@@ -2230,7 +2210,7 @@ zvol_rename_minors_impl(const char *oldname, const char *newname) + oldnamelen = strlen(oldname); + newnamelen = strlen(newname); + +- mutex_enter(&zvol_state_lock); ++ rw_enter(&zvol_state_lock, RW_READER); + + for (zv = list_head(&zvol_state_list); zv != NULL; zv = zv_next) { + zv_next = list_next(&zvol_state_list, zv); +@@ -2258,7 +2238,7 @@ zvol_rename_minors_impl(const char *oldname, const char *newname) + mutex_exit(&zv->zv_state_lock); + } + +- mutex_exit(&zvol_state_lock); ++ rw_exit(&zvol_state_lock); + } + + typedef struct zvol_snapdev_cb_arg { +@@ -2635,7 +2615,7 @@ zvol_init(void) + + list_create(&zvol_state_list, sizeof (zvol_state_t), + offsetof(zvol_state_t, zv_next)); +- mutex_init(&zvol_state_lock, NULL, MUTEX_DEFAULT, NULL); ++ rw_init(&zvol_state_lock, NULL, RW_DEFAULT, NULL); + ida_init(&zvol_ida); + + zvol_taskq = taskq_create(ZVOL_DRIVER, threads, maxclsyspri, +@@ -2672,7 +2652,7 @@ zvol_init(void) + taskq_destroy(zvol_taskq); + out: + ida_destroy(&zvol_ida); +- mutex_destroy(&zvol_state_lock); ++ rw_destroy(&zvol_state_lock); + list_destroy(&zvol_state_list); + + return (SET_ERROR(error)); +@@ -2689,7 +2669,7 @@ zvol_fini(void) + + taskq_destroy(zvol_taskq); + list_destroy(&zvol_state_list); +- mutex_destroy(&zvol_state_lock); ++ rw_destroy(&zvol_state_lock); + + ida_destroy(&zvol_ida); + } |