@@ -882,6 +882,7 @@ static int ceph_writepages_start(struct address_space *mapping,
struct ceph_writeback_ctl ceph_wbc;
bool should_loop, range_whole = false;
bool done = false;
+ unsigned int seq;
dout("writepages_start %p (mode=%s)\n", inode,
wbc->sync_mode == WB_SYNC_NONE ? "NONE" :
@@ -896,8 +897,13 @@ static int ceph_writepages_start(struct address_space *mapping,
mapping_set_error(mapping, -EIO);
return -EIO; /* we're in a forced umount, don't write! */
}
- if (fsc->mount_options->wsize < wsize)
- wsize = fsc->mount_options->wsize;
+
+ do {
+ seq = read_seqbegin(&fsc->mount_options->opt_seqlock);
+
+ if (fsc->mount_options->wsize < wsize)
+ wsize = fsc->mount_options->wsize;
+ } while (read_seqretry(&fsc->mount_options->opt_seqlock, seq));
pagevec_init(&pvec);
@@ -490,11 +490,17 @@ static void __cap_set_timeouts(struct ceph_mds_client *mdsc,
struct ceph_inode_info *ci)
{
struct ceph_mount_options *opt = mdsc->fsc->mount_options;
+ unsigned int seq;
+
+ do {
+ seq = read_seqbegin(&opt->opt_seqlock);
+
+ ci->i_hold_caps_min = round_jiffies(jiffies +
+ opt->caps_wanted_delay_min * HZ);
+ ci->i_hold_caps_max = round_jiffies(jiffies +
+ opt->caps_wanted_delay_max * HZ);
+ } while (read_seqretry(&opt->opt_seqlock, seq));
- ci->i_hold_caps_min = round_jiffies(jiffies +
- opt->caps_wanted_delay_min * HZ);
- ci->i_hold_caps_max = round_jiffies(jiffies +
- opt->caps_wanted_delay_max * HZ);
dout("__cap_set_timeouts %p min %lu max %lu\n", &ci->vfs_inode,
ci->i_hold_caps_min - jiffies, ci->i_hold_caps_max - jiffies);
}
@@ -2099,6 +2099,7 @@ int ceph_alloc_readdir_reply_buffer(struct ceph_mds_request *req,
struct ceph_inode_info *ci = ceph_inode(dir);
struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info;
struct ceph_mount_options *opt = req->r_mdsc->fsc->mount_options;
+ unsigned int max_readdir = opt->max_readdir;
size_t size = sizeof(struct ceph_mds_reply_dir_entry);
unsigned int num_entries;
int order;
@@ -2107,7 +2108,7 @@ int ceph_alloc_readdir_reply_buffer(struct ceph_mds_request *req,
num_entries = ci->i_files + ci->i_subdirs;
spin_unlock(&ci->i_ceph_lock);
num_entries = max(num_entries, 1U);
- num_entries = min(num_entries, opt->max_readdir);
+ num_entries = min(num_entries, max_readdir);
order = get_order(size * num_entries);
while (order >= 0) {
@@ -2122,7 +2123,7 @@ int ceph_alloc_readdir_reply_buffer(struct ceph_mds_request *req,
return -ENOMEM;
num_entries = (PAGE_SIZE << order) / size;
- num_entries = min(num_entries, opt->max_readdir);
+ num_entries = min(num_entries, max_readdir);
rinfo->dir_buf_size = PAGE_SIZE << order;
req->r_num_caps = num_entries + 1;
@@ -271,8 +271,14 @@ static int ceph_parse_mount_param(struct fs_context *fc,
int token, ret;
ret = ceph_parse_param(param, pctx->copts, fc);
- if (ret != -ENOPARAM)
- return ret;
+ if (ret != -ENOPARAM) {
+ /* Low level options are not reconfigurable */
+ if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE)
+ return invalf(fc, "ceph: reconfiguration of %s not allowed",
+ param->key);
+ else
+ return ret;
+ }
token = fs_parse(fc, &ceph_mount_parameters, param, &result);
dout("%s fs_parse '%s' token %d\n", __func__, param->key, token);
@@ -1070,14 +1076,17 @@ static int ceph_compare_super(struct super_block *sb, struct fs_context *fc)
*/
static atomic_long_t bdi_seq = ATOMIC_LONG_INIT(0);
-static int ceph_setup_bdi(struct super_block *sb, struct ceph_fs_client *fsc)
+static int ceph_setup_bdi(struct super_block *sb, struct ceph_fs_client *fsc,
+ bool update)
{
int err;
- err = super_setup_bdi_name(sb, "ceph-%ld",
- atomic_long_inc_return(&bdi_seq));
- if (err)
- return err;
+ if (!update) {
+ err = super_setup_bdi_name(sb, "ceph-%ld",
+ atomic_long_inc_return(&bdi_seq));
+ if (err)
+ return err;
+ }
/* set ra_pages based on rasize mount option? */
sb->s_bdi->ra_pages = fsc->mount_options->rasize >> PAGE_SHIFT;
@@ -1133,7 +1142,7 @@ static int ceph_get_tree(struct fs_context *fc)
dout("get_sb got existing client %p\n", fsc);
} else {
dout("get_sb using new client %p\n", fsc);
- err = ceph_setup_bdi(sb, fsc);
+ err = ceph_setup_bdi(sb, fsc, false);
if (err < 0)
goto out_splat;
}
@@ -1178,7 +1187,52 @@ static void ceph_free_fc(struct fs_context *fc)
static int ceph_reconfigure_fc(struct fs_context *fc)
{
- sync_filesystem(fc->root->d_sb);
+ struct super_block *sb = fc->root->d_sb;
+ struct ceph_fs_client *fsc = ceph_sb_to_client(sb);
+ struct ceph_mount_options *fsopt = fsc->mount_options;
+ struct ceph_parse_opts_ctx *pctx = fc->fs_private;
+ struct ceph_mount_options *new_fsopt = pctx->opts;
+
+ sync_filesystem(sb);
+
+ if (strcmp_null(new_fsopt->snapdir_name, fsopt->snapdir_name))
+ return invalf(fc, "ceph: reconfiguration of snapdir_name not allowed");
+
+ if (strcmp_null(new_fsopt->mds_namespace, fsopt->mds_namespace))
+ return invalf(fc, "ceph: reconfiguration of mds_namespace not allowed");
+
+ fsopt->rsize = new_fsopt->rsize;
+ fsopt->rasize = new_fsopt->rasize;
+ ceph_setup_bdi(sb, fsc, true);
+
+ write_seqlock(&fsopt->opt_seqlock);
+ fsopt->wsize = new_fsopt->wsize;
+ fsopt->caps_wanted_delay_min = new_fsopt->caps_wanted_delay_min;
+ fsopt->caps_wanted_delay_max = new_fsopt->caps_wanted_delay_max;
+ write_sequnlock(&fsopt->opt_seqlock);
+
+#ifdef CONFIG_CEPH_FSCACHE
+ if (strcmp_null(new_fsopt->fscache_uniq, fsopt->fscache_uniq) ||
+ ((new_fsopt->flags & CEPH_MOUNT_OPT_FSCACHE) !=
+ (fsopt->flags & CEPH_MOUNT_OPT_FSCACHE)))
+ return invalf(fc, "ceph: reconfiguration of fscache not allowed");
+#endif
+
+ fsopt->flags = new_fsopt->flags;
+
+ fsopt->max_readdir_bytes = new_fsopt->max_readdir_bytes;
+ fsopt->congestion_kb = new_fsopt->congestion_kb;
+
+ fsopt->caps_max = new_fsopt->caps_max;
+ fsopt->max_readdir = new_fsopt->max_readdir;
+ ceph_adjust_caps_max_min(fsc->mdsc, fsopt);
+
+#ifdef CONFIG_CEPH_FS_POSIX_ACL
+ if (fc->sb_flags & SB_POSIXACL)
+ sb->s_flags |= SB_POSIXACL;
+ else
+ sb->s_flags &= ~SB_POSIXACL;
+#endif
return 0;
}
@@ -1209,25 +1263,64 @@ static int ceph_init_fs_context(struct fs_context *fc)
if (!pctx->opts)
goto nomem;
+#ifdef CONFIG_CEPH_FS_POSIX_ACL
+ fc->sb_flags |= SB_POSIXACL;
+#endif
+
fsopt = pctx->opts;
- fsopt->flags = CEPH_MOUNT_OPT_DEFAULT;
- fsopt->wsize = CEPH_MAX_WRITE_SIZE;
- fsopt->rsize = CEPH_MAX_READ_SIZE;
- fsopt->rasize = CEPH_RASIZE_DEFAULT;
- fsopt->snapdir_name = kstrdup(CEPH_SNAPDIRNAME_DEFAULT, GFP_KERNEL);
- if (!fsopt->snapdir_name)
- goto nomem;
+ if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
+ struct super_block *sb = fc->root->d_sb;
+ struct ceph_fs_client *fsc = ceph_sb_to_client(sb);
+ struct ceph_mount_options *old = fsc->mount_options;
- fsopt->caps_wanted_delay_min = CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT;
- fsopt->caps_wanted_delay_max = CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT;
- fsopt->max_readdir = CEPH_MAX_READDIR_DEFAULT;
- fsopt->max_readdir_bytes = CEPH_MAX_READDIR_BYTES_DEFAULT;
- fsopt->congestion_kb = default_congestion_kb();
+ fsopt->flags = old->flags;
+
+ fsopt->wsize = old->wsize;
+ fsopt->rsize = old->rsize;
+ fsopt->rasize = old->rasize;
+
+ if (old->fscache_uniq) {
+ fsopt->fscache_uniq = kstrdup(old->fscache_uniq,
+ GFP_KERNEL);
+ if (!fsopt->fscache_uniq)
+ goto nomem;
+ }
+
+ fsopt->snapdir_name = kstrdup(old->snapdir_name, GFP_KERNEL);
+ if (!fsopt->snapdir_name)
+ goto nomem;
+
+ fsopt->caps_wanted_delay_min = old->caps_wanted_delay_min;
+ fsopt->caps_wanted_delay_max = old->caps_wanted_delay_max;
+ fsopt->max_readdir = old->max_readdir;
+ fsopt->max_readdir_bytes = old->max_readdir_bytes;
+ fsopt->congestion_kb = old->congestion_kb;
+ fsopt->caps_max = old->caps_max;
+ fsopt->max_readdir = old->max_readdir;
#ifdef CONFIG_CEPH_FS_POSIX_ACL
- fc->sb_flags |= SB_POSIXACL;
+ if (!(sb->s_flags & SB_POSIXACL))
+ fc->sb_flags &= ~SB_POSIXACL;
#endif
+ } else {
+ fsopt->flags = CEPH_MOUNT_OPT_DEFAULT;
+
+ fsopt->wsize = CEPH_MAX_WRITE_SIZE;
+ fsopt->rsize = CEPH_MAX_READ_SIZE;
+ fsopt->rasize = CEPH_RASIZE_DEFAULT;
+
+ fsopt->snapdir_name = kstrdup(CEPH_SNAPDIRNAME_DEFAULT, GFP_KERNEL);
+ if (!fsopt->snapdir_name)
+ goto nomem;
+
+ fsopt->caps_wanted_delay_min = CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT;
+ fsopt->caps_wanted_delay_max = CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT;
+ fsopt->max_readdir = CEPH_MAX_READDIR_DEFAULT;
+ fsopt->max_readdir_bytes = CEPH_MAX_READDIR_BYTES_DEFAULT;
+ fsopt->congestion_kb = default_congestion_kb();
+ seqlock_init(&fsopt->opt_seqlock);
+ }
fc->fs_private = pctx;
fc->ops = &ceph_context_ops;
@@ -96,6 +96,8 @@ struct ceph_mount_options {
char *mds_namespace; /* default NULL */
char *server_path; /* default "/" */
char *fscache_uniq; /* default NULL */
+
+ seqlock_t opt_seqlock;
};
struct ceph_fs_client {