/*ARGSUSED*/
int
-zfs_sync(zfs_sb_t *zsb, short flag, cred_t *cr)
+zfs_sync(struct super_block *sb, int wait, cred_t *cr)
{
+ zfs_sb_t *zsb = sb->s_fs_info;
+
/*
* Data integrity is job one. We don't want a compromised kernel
* writing to the storage pool, so we never sync during panic.
if (unlikely(oops_in_progress))
return (0);
+ /*
+ * Semantically, the only requirement is that the sync be initiated.
+ * The DMU syncs out txgs frequently, so there's nothing to do.
+ */
+ if (!wait)
+ return (0);
+
if (zsb != NULL) {
/*
* Sync a specific filesystem.
ZFS_ENTER(zsb);
dp = dmu_objset_pool(zsb->z_os);
-#ifdef HAVE_SHUTDOWN
/*
* If the system is shutting down, then skip any
* filesystems which may exist on a suspended pool.
- *
- * XXX: This can be implemented using the Linux reboot
- * notifiers: {un}register_reboot_notifier().
*/
- if (sys_shutdown && spa_suspended(dp->dp_spa)) {
+ if (spa_suspended(dp->dp_spa)) {
ZFS_EXIT(zsb);
return (0);
}
-#endif /* HAVE_SHUTDOWN */
if (zsb->z_log != NULL)
zil_commit(zsb->z_log, 0);
}
EXPORT_SYMBOL(zfs_sync);
+boolean_t
+zfs_is_readonly(zfs_sb_t *zsb)
+{
+ return (!!(zsb->z_sb->s_flags & MS_RDONLY));
+}
+EXPORT_SYMBOL(zfs_is_readonly);
+
static void
atime_changed_cb(void *arg, uint64_t newval)
{
- zfs_sb_t *zsb = arg;
- struct super_block *sb = zsb->z_sb;
- struct vfsmount *vfs = zsb->z_vfs;
-
- if (newval == TRUE) {
- vfs->mnt_flags &= ~MNT_NOATIME;
- sb->s_flags &= ~MS_NOATIME;
- zsb->z_atime = TRUE;
- } else {
- vfs->mnt_flags |= MNT_NOATIME;
- sb->s_flags |= MS_NOATIME;
- zsb->z_atime = FALSE;
- }
+ ((zfs_sb_t *)arg)->z_atime = newval;
}
static void
{
zfs_sb_t *zsb = arg;
- if (newval == TRUE) {
- zsb->z_flags |= ZSB_XATTR_USER;
+ if (newval == ZFS_XATTR_OFF) {
+ zsb->z_flags &= ~ZSB_XATTR;
} else {
- zsb->z_flags &= ~ZSB_XATTR_USER;
+ zsb->z_flags |= ZSB_XATTR;
+
+ if (newval == ZFS_XATTR_SA)
+ zsb->z_xattr_sa = B_TRUE;
+ else
+ zsb->z_xattr_sa = B_FALSE;
}
}
{
zfs_sb_t *zsb = arg;
struct super_block *sb = zsb->z_sb;
- struct vfsmount *vfs = zsb->z_vfs;
- if (newval) {
- vfs->mnt_flags |= MNT_READONLY;
+ if (sb == NULL)
+ return;
+
+ if (newval)
sb->s_flags |= MS_RDONLY;
- } else {
- vfs->mnt_flags &= ~MNT_READONLY;
+ else
sb->s_flags &= ~MS_RDONLY;
- }
}
static void
devices_changed_cb(void *arg, uint64_t newval)
{
- zfs_sb_t *zsb = arg;
- struct super_block *sb = zsb->z_sb;
- struct vfsmount *vfs = zsb->z_vfs;
-
- if (newval == FALSE) {
- vfs->mnt_flags |= MNT_NODEV;
- sb->s_flags |= MS_NODEV;
- } else {
- vfs->mnt_flags &= ~MNT_NODEV;
- sb->s_flags &= ~MS_NODEV;
- }
}
static void
setuid_changed_cb(void *arg, uint64_t newval)
{
- zfs_sb_t *zsb = arg;
- struct super_block *sb = zsb->z_sb;
- struct vfsmount *vfs = zsb->z_vfs;
-
- if (newval == FALSE) {
- vfs->mnt_flags |= MNT_NOSUID;
- sb->s_flags |= MS_NOSUID;
- } else {
- vfs->mnt_flags &= ~MNT_NOSUID;
- sb->s_flags &= ~MS_NOSUID;
- }
}
static void
exec_changed_cb(void *arg, uint64_t newval)
{
- zfs_sb_t *zsb = arg;
- struct super_block *sb = zsb->z_sb;
- struct vfsmount *vfs = zsb->z_vfs;
-
- if (newval == FALSE) {
- vfs->mnt_flags |= MNT_NOEXEC;
- sb->s_flags |= MS_NOEXEC;
- } else {
- vfs->mnt_flags &= ~MNT_NOEXEC;
- sb->s_flags &= ~MS_NOEXEC;
- }
}
-/*
- * The nbmand mount option can be changed at mount time.
- * We can't allow it to be toggled on live file systems or incorrect
- * behavior may be seen from cifs clients
- *
- * This property isn't registered via dsl_prop_register(), but this callback
- * will be called when a file system is first mounted
- */
static void
nbmand_changed_cb(void *arg, uint64_t newval)
{
zfs_sb_t *zsb = arg;
struct super_block *sb = zsb->z_sb;
- if (newval == TRUE) {
+ if (sb == NULL)
+ return;
+
+ if (newval == TRUE)
sb->s_flags |= MS_MANDLOCK;
- } else {
+ else
sb->s_flags &= ~MS_MANDLOCK;
- }
}
static void
int
zfs_register_callbacks(zfs_sb_t *zsb)
{
- struct vfsmount *vfsp = zsb->z_vfs;
struct dsl_dataset *ds = NULL;
objset_t *os = zsb->z_os;
- uint64_t nbmand;
- boolean_t readonly = B_FALSE;
- boolean_t setuid = B_TRUE;
- boolean_t exec = B_TRUE;
- boolean_t devices = B_TRUE;
- boolean_t xattr = B_TRUE;
- boolean_t atime = B_TRUE;
- char osname[MAXNAMELEN];
int error = 0;
- /*
- * While Linux allows multiple vfs mounts per super block we have
- * limited it artificially to one in zfs_fill_super. Thus it is
- * safe for us to modify the vfs mount fails through the callbacks.
- */
- if ((vfsp->mnt_flags & MNT_READONLY) ||
- !spa_writeable(dmu_objset_spa(os)))
- readonly = B_TRUE;
-
- if (vfsp->mnt_flags & MNT_NOSUID) {
- devices = B_FALSE;
- setuid = B_FALSE;
- } else {
- if (vfsp->mnt_flags & MNT_NODEV)
- devices = B_FALSE;
- }
-
- if (vfsp->mnt_flags & MNT_NOEXEC)
- exec = B_FALSE;
-
- if (vfsp->mnt_flags & MNT_NOATIME)
- atime = B_FALSE;
-
- /*
- * nbmand is a special property which may only be changed at
- * mount time. Unfortunately, Linux does not have a VFS mount
- * flag instead this is a super block flag. So setting this
- * option at mount time will have to wait until we can parse
- * the mount option string. For now we rely on the nbmand
- * value stored with the object set. Additional mount option
- * string to be handled:
- *
- * case: sensitive|insensitive|mixed
- * zerocopy: on|off
- */
-
- dmu_objset_name(os, osname);
- if ((error = dsl_prop_get_integer(osname, "nbmand", &nbmand, NULL)))
- return (error);
+ if (zfs_is_readonly(zsb) || !spa_writeable(dmu_objset_spa(os)))
+ readonly_changed_cb(zsb, B_TRUE);
/*
* Register property callbacks.
"aclinherit", acl_inherit_changed_cb, zsb);
error = error ? error : dsl_prop_register(ds,
"vscan", vscan_changed_cb, zsb);
+ error = error ? error : dsl_prop_register(ds,
+ "nbmand", nbmand_changed_cb, zsb);
if (error)
goto unregister;
- /*
- * Invoke our callbacks to set required flags.
- */
- readonly_changed_cb(zsb, readonly);
- setuid_changed_cb(zsb, setuid);
- exec_changed_cb(zsb, exec);
- devices_changed_cb(zsb, devices);
- xattr_changed_cb(zsb, xattr);
- atime_changed_cb(zsb, atime);
- nbmand_changed_cb(zsb, nbmand);
-
return (0);
unregister:
(void) dsl_prop_unregister(ds, "aclinherit", acl_inherit_changed_cb,
zsb);
(void) dsl_prop_unregister(ds, "vscan", vscan_changed_cb, zsb);
+ (void) dsl_prop_unregister(ds, "nbmand", nbmand_changed_cb, zsb);
return (error);
}
* Should probably make this a kmem cache, shuffle fields,
* and just bzero up to z_hold_mtx[].
*/
- zsb->z_vfs = NULL;
+ zsb->z_sb = NULL;
zsb->z_parent = zsb;
zsb->z_max_blksz = SPA_MAXBLOCKSIZE;
zsb->z_show_ctldir = ZFS_SNAPDIR_VISIBLE;
error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_SA_ATTRS, 8, 1,
&sa_obj);
if (error)
- return (error);
+ goto out;
+
+ error = zfs_get_zplprop(os, ZFS_PROP_XATTR, &zval);
+ if ((error == 0) && (zval == ZFS_XATTR_SA))
+ zsb->z_xattr_sa = B_TRUE;
} else {
/*
* Pre SA versions file systems should never touch
kmem_free(zsb, sizeof (zfs_sb_t));
return (error);
}
+EXPORT_SYMBOL(zfs_sb_create);
-static int
+int
zfs_sb_setup(zfs_sb_t *zsb, boolean_t mounting)
{
int error;
* During replay we remove the read only flag to
* allow replays to succeed.
*/
- readonly = zsb->z_vfs->mnt_flags & MNT_READONLY;
+ readonly = zfs_is_readonly(zsb);
if (readonly != 0)
- zsb->z_vfs->mnt_flags &= ~MNT_READONLY;
+ readonly_changed_cb(zsb, B_FALSE);
else
zfs_unlinked_drain(zsb);
zsb->z_replay = B_FALSE;
}
}
- zsb->z_vfs->mnt_flags |= readonly; /* restore readonly bit */
+
+ /* restore readonly bit */
+ if (readonly != 0)
+ readonly_changed_cb(zsb, B_TRUE);
}
return (0);
}
+EXPORT_SYMBOL(zfs_sb_setup);
void
zfs_sb_free(zfs_sb_t *zsb)
mutex_destroy(&zsb->z_hold_mtx[i]);
kmem_free(zsb, sizeof (zfs_sb_t));
}
+EXPORT_SYMBOL(zfs_sb_free);
static void
zfs_set_fuid_feature(zfs_sb_t *zsb)
VERIFY(dsl_prop_unregister(ds, "vscan",
vscan_changed_cb, zsb) == 0);
+
+ VERIFY(dsl_prop_unregister(ds, "nbmand",
+ nbmand_changed_cb, zsb) == 0);
}
}
EXPORT_SYMBOL(zfs_unregister_callbacks);
}
return (EACCES);
}
+EXPORT_SYMBOL(zfs_check_global_label);
#endif /* HAVE_MLSLABEL */
int
&refdbytes, &availbytes, &usedobjs, &availobjs);
/*
- * 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.
+ * The underlying storage pool actually uses multiple block
+ * size. Under Solaris frsize (fragment size) is reported as
+ * the smallest block size we support, and bsize (block size)
+ * as the filesystem's maximum block size. Unfortunately,
+ * under Linux the fragment size and block size are often used
+ * interchangeably. Thus we are forced to report both of them
+ * as the filesystem's maximum block size.
*/
- statp->f_frsize = 1UL << SPA_MINBLOCKSHIFT;
+ statp->f_frsize = zsb->z_max_blksz;
statp->f_bsize = zsb->z_max_blksz;
bshift = fls(statp->f_bsize) - 1;
* 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).
*/
- statp->f_ffree = MIN(availobjs, statp->f_bfree);
+ statp->f_ffree = MIN(availobjs, availbytes >> DNODE_SHIFT);
statp->f_files = statp->f_ffree + usedobjs;
- statp->f_fsid.val[0] = 0; /* XXX: Map up some unique ID */
+ statp->f_fsid.val[0] = dentry->d_sb->s_dev;
statp->f_fsid.val[1] = 0;
statp->f_type = ZFS_SUPER_MAGIC;
statp->f_namelen = ZFS_MAXNAMELEN;
* and 'z_teardown_inactive_lock' held.
*/
int
-zfsvfs_teardown(zfs_sb_t *zsb, boolean_t unmounting)
+zfs_sb_teardown(zfs_sb_t *zsb, boolean_t unmounting)
{
znode_t *zp;
rrw_enter(&zsb->z_teardown_lock, RW_WRITER, FTAG);
-#ifdef HAVE_DNLC
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.
+ * We purge the parent filesystem's super block as the
+ * parent filesystem and all of its snapshots have their
+ * inode's super block set to the parent's filesystem's
+ * super block. Note, 'z_parent' is self referential
+ * for non-snapshots.
*/
- (void) dnlc_purge_vfsp(zsb->z_parent->z_vfs, 0);
+ shrink_dcache_sb(zsb->z_parent->z_sb);
+ (void) spl_invalidate_inodes(zsb->z_parent->z_sb, 0);
}
-#endif /* HAVE_DNLC */
+
+ /*
+ * Drain the iput_taskq to ensure all active references to the
+ * zfs_sb_t have been handled only then can it be safely destroyed.
+ */
+ taskq_wait(dsl_pool_iput_taskq(dmu_objset_pool(zsb->z_os)));
/*
* Close the zil. NB: Can't close the zil while zfs_inactive
* Evict cached data
*/
if (dmu_objset_is_dirty_anywhere(zsb->z_os))
- if (!(zsb->z_vfs->mnt_flags & MNT_READONLY))
+ if (!zfs_is_readonly(zsb))
txg_wait_synced(dmu_objset_pool(zsb->z_os), 0);
(void) dmu_objset_evict_dbufs(zsb->z_os);
return (0);
}
+EXPORT_SYMBOL(zfs_sb_teardown);
+
+#if defined(HAVE_BDI) && !defined(HAVE_BDI_SETUP_AND_REGISTER)
+atomic_long_t zfs_bdi_seq = ATOMIC_LONG_INIT(0);
+#endif /* HAVE_BDI && !HAVE_BDI_SETUP_AND_REGISTER */
int
zfs_domount(struct super_block *sb, void *data, int silent)
uint64_t recordsize;
int error;
- /*
- * Linux allows multiple vfs mounts per super block. However, the
- * zfs_sb_t only contains a pointer for a single vfs mount. This
- * back reference in the long term could be extended to a list of
- * vfs mounts if a hook were added to the kernel to notify us when
- * a vfsmount is destroyed. Until then we must limit the number
- * of mounts per super block to one.
- */
- if (atomic_read(&sb->s_active) > 1)
- return (EBUSY);
-
error = zfs_sb_create(osname, &zsb);
if (error)
return (error);
goto out;
zsb->z_sb = sb;
- zsb->z_vfs = zmd->z_vfs;
sb->s_fs_info = zsb;
sb->s_magic = ZFS_SUPER_MAGIC;
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_blocksize = recordsize;
sb->s_blocksize_bits = ilog2(recordsize);
+#ifdef HAVE_BDI
+ /*
+ * 2.6.32 API change,
+ * Added backing_device_info (BDI) per super block interfaces. A BDI
+ * must be configured when using a non-device backed filesystem for
+ * proper writeback. This is not required for older pdflush kernels.
+ *
+ * NOTE: Linux read-ahead is disabled in favor of zfs read-ahead.
+ */
+ zsb->z_bdi.ra_pages = 0;
+ sb->s_bdi = &zsb->z_bdi;
+
+ error = -bdi_setup_and_register(&zsb->z_bdi, "zfs", BDI_CAP_MAP_COPY);
+ if (error)
+ goto out;
+#endif /* HAVE_BDI */
+
/* Set callback operations for the file system. */
sb->s_op = &zpl_super_operations;
sb->s_xattr = zpl_xattr_handlers;
-#ifdef HAVE_EXPORTS
- sb->s_export_op = &zpl_export_operations;
-#endif /* HAVE_EXPORTS */
+ sb->s_export_op = &zpl_export_operations;
/* Set features for file system. */
zfs_set_fuid_feature(zsb);
zfs_sb_t *zsb = sb->s_fs_info;
objset_t *os;
- VERIFY(zfsvfs_teardown(zsb, B_TRUE) == 0);
+ VERIFY(zfs_sb_teardown(zsb, B_TRUE) == 0);
os = zsb->z_os;
+#ifdef HAVE_BDI
+ bdi_destroy(sb->s_bdi);
+#endif /* HAVE_BDI */
+
/*
* z_os will be NULL if there was an error in
* attempting to reopen zsb.
EXPORT_SYMBOL(zfs_umount);
int
-zfs_vget(struct vfsmount *vfsp, struct inode **ipp, fid_t *fidp)
+zfs_remount(struct super_block *sb, int *flags, char *data)
+{
+ /*
+ * All namespace flags (MNT_*) and super block flags (MS_*) will
+ * be handled by the Linux VFS. Only handle custom options here.
+ */
+ return (0);
+}
+EXPORT_SYMBOL(zfs_remount);
+
+int
+zfs_vget(struct super_block *sb, struct inode **ipp, fid_t *fidp)
{
- zfs_sb_t *zsb = VTOZSB(vfsp);
+ zfs_sb_t *zsb = sb->s_fs_info;
znode_t *zp;
uint64_t object = 0;
uint64_t fid_gen = 0;
{
int error;
- if ((error = zfsvfs_teardown(zsb, B_FALSE)) != 0)
+ if ((error = zfs_sb_teardown(zsb, B_FALSE)) != 0)
return (error);
dmu_objset_disown(zsb->z_os, zsb);
}
return (error);
}
+EXPORT_SYMBOL(zfs_get_zplprop);
void
zfs_init(void)