mirror of
https://github.com/versity/scoutfs.git
synced 2026-01-08 04:55:21 +00:00
The initial code for setting the timeout used the wrong parsed variable. Signed-off-by: Zach Brown <zab@versity.com>
639 lines
17 KiB
C
639 lines
17 KiB
C
/*
|
|
* Copyright (C) 2017 Versity Software, Inc. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public
|
|
* License v2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*/
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/types.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/namei.h>
|
|
|
|
#include <linux/parser.h>
|
|
#include <linux/inet.h>
|
|
#include <linux/string.h>
|
|
#include <linux/in.h>
|
|
|
|
#include "msg.h"
|
|
#include "options.h"
|
|
#include "super.h"
|
|
#include "inode.h"
|
|
#include "alloc.h"
|
|
|
|
enum {
|
|
Opt_acl,
|
|
Opt_data_prealloc_blocks,
|
|
Opt_data_prealloc_contig_only,
|
|
Opt_log_merge_wait_timeout_ms,
|
|
Opt_metadev_path,
|
|
Opt_noacl,
|
|
Opt_orphan_scan_delay_ms,
|
|
Opt_quorum_heartbeat_timeout_ms,
|
|
Opt_quorum_slot_nr,
|
|
Opt_err,
|
|
};
|
|
|
|
static const match_table_t tokens = {
|
|
{Opt_acl, "acl"},
|
|
{Opt_data_prealloc_blocks, "data_prealloc_blocks=%s"},
|
|
{Opt_data_prealloc_contig_only, "data_prealloc_contig_only=%s"},
|
|
{Opt_log_merge_wait_timeout_ms, "log_merge_wait_timeout_ms=%s"},
|
|
{Opt_metadev_path, "metadev_path=%s"},
|
|
{Opt_noacl, "noacl"},
|
|
{Opt_orphan_scan_delay_ms, "orphan_scan_delay_ms=%s"},
|
|
{Opt_quorum_heartbeat_timeout_ms, "quorum_heartbeat_timeout_ms=%s"},
|
|
{Opt_quorum_slot_nr, "quorum_slot_nr=%s"},
|
|
{Opt_err, NULL}
|
|
};
|
|
|
|
struct options_info {
|
|
seqlock_t seqlock;
|
|
struct scoutfs_mount_options opts;
|
|
struct scoutfs_sysfs_attrs sysfs_attrs;
|
|
};
|
|
|
|
#define DECLARE_OPTIONS_INFO(sb, name) \
|
|
struct options_info *name = SCOUTFS_SB(sb)->options_info
|
|
|
|
static int parse_bdev_path(struct super_block *sb, substring_t *substr,
|
|
char **bdev_path_ret)
|
|
{
|
|
char *bdev_path;
|
|
struct inode *bdev_inode;
|
|
struct path path;
|
|
bool got_path = false;
|
|
int ret;
|
|
|
|
bdev_path = match_strdup(substr);
|
|
if (!bdev_path) {
|
|
scoutfs_err(sb, "bdev string dup failed");
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
ret = kern_path(bdev_path, LOOKUP_FOLLOW, &path);
|
|
if (ret) {
|
|
scoutfs_err(sb, "path %s not found for bdev: error %d",
|
|
bdev_path, ret);
|
|
goto out;
|
|
}
|
|
got_path = true;
|
|
|
|
bdev_inode = d_inode(path.dentry);
|
|
if (!S_ISBLK(bdev_inode->i_mode)) {
|
|
scoutfs_err(sb, "path %s for bdev is not a block device",
|
|
bdev_path);
|
|
ret = -ENOTBLK;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if (got_path) {
|
|
path_put(&path);
|
|
}
|
|
|
|
if (ret < 0) {
|
|
kfree(bdev_path);
|
|
} else {
|
|
*bdev_path_ret = bdev_path;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void free_options(struct scoutfs_mount_options *opts)
|
|
{
|
|
kfree(opts->metadev_path);
|
|
}
|
|
|
|
#define MIN_LOG_MERGE_WAIT_TIMEOUT_MS 100UL
|
|
#define DEFAULT_LOG_MERGE_WAIT_TIMEOUT_MS 500
|
|
#define MAX_LOG_MERGE_WAIT_TIMEOUT_MS (60 * MSEC_PER_SEC)
|
|
|
|
#define MIN_ORPHAN_SCAN_DELAY_MS 100UL
|
|
#define DEFAULT_ORPHAN_SCAN_DELAY_MS (10 * MSEC_PER_SEC)
|
|
#define MAX_ORPHAN_SCAN_DELAY_MS (60 * MSEC_PER_SEC)
|
|
|
|
#define MIN_DATA_PREALLOC_BLOCKS 1ULL
|
|
#define MAX_DATA_PREALLOC_BLOCKS ((unsigned long long)SCOUTFS_BLOCK_SM_MAX)
|
|
|
|
static void init_default_options(struct scoutfs_mount_options *opts)
|
|
{
|
|
memset(opts, 0, sizeof(*opts));
|
|
|
|
opts->data_prealloc_blocks = SCOUTFS_DATA_PREALLOC_DEFAULT_BLOCKS;
|
|
opts->data_prealloc_contig_only = 1;
|
|
opts->log_merge_wait_timeout_ms = DEFAULT_LOG_MERGE_WAIT_TIMEOUT_MS;
|
|
opts->orphan_scan_delay_ms = -1;
|
|
opts->quorum_heartbeat_timeout_ms = SCOUTFS_QUORUM_DEF_HB_TIMEO_MS;
|
|
opts->quorum_slot_nr = -1;
|
|
}
|
|
|
|
static int verify_log_merge_wait_timeout_ms(struct super_block *sb, int ret, int val)
|
|
{
|
|
if (ret < 0) {
|
|
scoutfs_err(sb, "failed to parse log_merge_wait_timeout_ms value");
|
|
return -EINVAL;
|
|
}
|
|
if (val < MIN_LOG_MERGE_WAIT_TIMEOUT_MS || val > MAX_LOG_MERGE_WAIT_TIMEOUT_MS) {
|
|
scoutfs_err(sb, "invalid log_merge_wait_timeout_ms value %d, must be between %lu and %lu",
|
|
val, MIN_LOG_MERGE_WAIT_TIMEOUT_MS, MAX_LOG_MERGE_WAIT_TIMEOUT_MS);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int verify_quorum_heartbeat_timeout_ms(struct super_block *sb, int ret, u64 val)
|
|
{
|
|
if (ret < 0) {
|
|
scoutfs_err(sb, "failed to parse quorum_heartbeat_timeout_ms value");
|
|
return -EINVAL;
|
|
}
|
|
if (val < SCOUTFS_QUORUM_MIN_HB_TIMEO_MS || val > SCOUTFS_QUORUM_MAX_HB_TIMEO_MS) {
|
|
scoutfs_err(sb, "invalid quorum_heartbeat_timeout_ms value %llu, must be between %lu and %lu",
|
|
val, SCOUTFS_QUORUM_MIN_HB_TIMEO_MS, SCOUTFS_QUORUM_MAX_HB_TIMEO_MS);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Parse the option string into our options struct. This can allocate
|
|
* memory in the struct. The caller is responsible for always calling
|
|
* free_options() when the struct is destroyed, including when we return
|
|
* an error.
|
|
*/
|
|
static int parse_options(struct super_block *sb, char *options, struct scoutfs_mount_options *opts)
|
|
{
|
|
substring_t args[MAX_OPT_ARGS];
|
|
u64 nr64;
|
|
int nr;
|
|
int token;
|
|
char *p;
|
|
int ret;
|
|
|
|
while ((p = strsep(&options, ",")) != NULL) {
|
|
if (!*p)
|
|
continue;
|
|
|
|
token = match_token(p, tokens, args);
|
|
switch (token) {
|
|
|
|
case Opt_acl:
|
|
sb->s_flags |= SB_POSIXACL;
|
|
break;
|
|
|
|
case Opt_data_prealloc_blocks:
|
|
ret = match_u64(args, &nr64);
|
|
if (ret < 0 ||
|
|
nr64 < MIN_DATA_PREALLOC_BLOCKS || nr64 > MAX_DATA_PREALLOC_BLOCKS) {
|
|
scoutfs_err(sb, "invalid data_prealloc_blocks option, must be between %llu and %llu",
|
|
MIN_DATA_PREALLOC_BLOCKS, MAX_DATA_PREALLOC_BLOCKS);
|
|
if (ret == 0)
|
|
ret = -EINVAL;
|
|
return ret;
|
|
}
|
|
opts->data_prealloc_blocks = nr64;
|
|
break;
|
|
|
|
case Opt_data_prealloc_contig_only:
|
|
ret = match_int(args, &nr);
|
|
if (ret < 0 || nr < 0 || nr > 1) {
|
|
scoutfs_err(sb, "invalid data_prealloc_contig_only option, bool must only be 0 or 1");
|
|
if (ret == 0)
|
|
ret = -EINVAL;
|
|
return ret;
|
|
}
|
|
opts->data_prealloc_contig_only = nr;
|
|
break;
|
|
|
|
case Opt_log_merge_wait_timeout_ms:
|
|
ret = match_int(args, &nr);
|
|
ret = verify_log_merge_wait_timeout_ms(sb, ret, nr);
|
|
if (ret < 0)
|
|
return ret;
|
|
opts->log_merge_wait_timeout_ms = nr;
|
|
break;
|
|
|
|
case Opt_metadev_path:
|
|
ret = parse_bdev_path(sb, &args[0], &opts->metadev_path);
|
|
if (ret < 0)
|
|
return ret;
|
|
break;
|
|
|
|
case Opt_noacl:
|
|
sb->s_flags &= ~SB_POSIXACL;
|
|
break;
|
|
|
|
case Opt_orphan_scan_delay_ms:
|
|
if (opts->orphan_scan_delay_ms != -1) {
|
|
scoutfs_err(sb, "multiple orphan_scan_delay_ms options provided, only provide one.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = match_int(args, &nr);
|
|
if (ret < 0 ||
|
|
nr < MIN_ORPHAN_SCAN_DELAY_MS || nr > MAX_ORPHAN_SCAN_DELAY_MS) {
|
|
scoutfs_err(sb, "invalid orphan_scan_delay_ms option, must be between %lu and %lu",
|
|
MIN_ORPHAN_SCAN_DELAY_MS, MAX_ORPHAN_SCAN_DELAY_MS);
|
|
if (ret == 0)
|
|
ret = -EINVAL;
|
|
return ret;
|
|
}
|
|
opts->orphan_scan_delay_ms = nr;
|
|
break;
|
|
|
|
case Opt_quorum_heartbeat_timeout_ms:
|
|
ret = match_u64(args, &nr64);
|
|
ret = verify_quorum_heartbeat_timeout_ms(sb, ret, nr64);
|
|
if (ret < 0)
|
|
return ret;
|
|
opts->quorum_heartbeat_timeout_ms = nr64;
|
|
break;
|
|
|
|
case Opt_quorum_slot_nr:
|
|
if (opts->quorum_slot_nr != -1) {
|
|
scoutfs_err(sb, "multiple quorum_slot_nr options provided, only provide one.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = match_int(args, &nr);
|
|
if (ret < 0 || nr < 0 || nr >= SCOUTFS_QUORUM_MAX_SLOTS) {
|
|
scoutfs_err(sb, "invalid quorum_slot_nr option, must be between 0 and %u",
|
|
SCOUTFS_QUORUM_MAX_SLOTS - 1);
|
|
if (ret == 0)
|
|
ret = -EINVAL;
|
|
return ret;
|
|
}
|
|
opts->quorum_slot_nr = nr;
|
|
break;
|
|
|
|
default:
|
|
scoutfs_err(sb, "Unknown or malformed option, \"%s\"", p);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
if (opts->orphan_scan_delay_ms == -1)
|
|
opts->orphan_scan_delay_ms = DEFAULT_ORPHAN_SCAN_DELAY_MS;
|
|
|
|
if (!opts->metadev_path) {
|
|
scoutfs_err(sb, "Required mount option \"metadev_path\" not found");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void scoutfs_options_read(struct super_block *sb, struct scoutfs_mount_options *opts)
|
|
{
|
|
DECLARE_OPTIONS_INFO(sb, optinf);
|
|
unsigned int seq;
|
|
|
|
if (WARN_ON_ONCE(optinf == NULL)) {
|
|
/* trying to use options before early setup or after destroy */
|
|
init_default_options(opts);
|
|
return;
|
|
}
|
|
|
|
do {
|
|
seq = read_seqbegin(&optinf->seqlock);
|
|
memcpy(opts, &optinf->opts, sizeof(struct scoutfs_mount_options));
|
|
} while (read_seqretry(&optinf->seqlock, seq));
|
|
}
|
|
|
|
/*
|
|
* Early setup that parses and stores the options so that the rest of
|
|
* setup can use them. Full options setup that relies on other
|
|
* components will be done later.
|
|
*/
|
|
int scoutfs_options_early_setup(struct super_block *sb, char *options)
|
|
{
|
|
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
|
|
struct scoutfs_mount_options opts;
|
|
struct options_info *optinf;
|
|
int ret;
|
|
|
|
init_default_options(&opts);
|
|
|
|
ret = parse_options(sb, options, &opts);
|
|
if (ret < 0)
|
|
goto out;
|
|
|
|
optinf = kzalloc(sizeof(struct options_info), GFP_KERNEL);
|
|
if (!optinf) {
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
seqlock_init(&optinf->seqlock);
|
|
scoutfs_sysfs_init_attrs(sb, &optinf->sysfs_attrs);
|
|
|
|
write_seqlock(&optinf->seqlock);
|
|
optinf->opts = opts;
|
|
write_sequnlock(&optinf->seqlock);
|
|
|
|
sbi->options_info = optinf;
|
|
ret = 0;
|
|
out:
|
|
if (ret < 0)
|
|
free_options(&opts);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int scoutfs_options_show(struct seq_file *seq, struct dentry *root)
|
|
{
|
|
struct super_block *sb = root->d_sb;
|
|
struct scoutfs_mount_options opts;
|
|
const bool is_acl = !!(sb->s_flags & SB_POSIXACL);
|
|
|
|
scoutfs_options_read(sb, &opts);
|
|
|
|
if (is_acl)
|
|
seq_puts(seq, ",acl");
|
|
seq_printf(seq, ",data_prealloc_blocks=%llu", opts.data_prealloc_blocks);
|
|
seq_printf(seq, ",data_prealloc_contig_only=%u", opts.data_prealloc_contig_only);
|
|
seq_printf(seq, ",metadev_path=%s", opts.metadev_path);
|
|
if (!is_acl)
|
|
seq_puts(seq, ",noacl");
|
|
seq_printf(seq, ",orphan_scan_delay_ms=%u", opts.orphan_scan_delay_ms);
|
|
if (opts.quorum_slot_nr >= 0)
|
|
seq_printf(seq, ",quorum_slot_nr=%d", opts.quorum_slot_nr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t data_prealloc_blocks_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct super_block *sb = SCOUTFS_SYSFS_ATTRS_SB(kobj);
|
|
struct scoutfs_mount_options opts;
|
|
|
|
scoutfs_options_read(sb, &opts);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%llu", opts.data_prealloc_blocks);
|
|
}
|
|
static ssize_t data_prealloc_blocks_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct super_block *sb = SCOUTFS_SYSFS_ATTRS_SB(kobj);
|
|
DECLARE_OPTIONS_INFO(sb, optinf);
|
|
char nullterm[30]; /* more than enough for octal -U64_MAX */
|
|
u64 val;
|
|
int len;
|
|
int ret;
|
|
|
|
len = min(count, sizeof(nullterm) - 1);
|
|
memcpy(nullterm, buf, len);
|
|
nullterm[len] = '\0';
|
|
|
|
ret = kstrtoll(nullterm, 0, &val);
|
|
if (ret < 0 || val < MIN_DATA_PREALLOC_BLOCKS || val > MAX_DATA_PREALLOC_BLOCKS) {
|
|
scoutfs_err(sb, "invalid data_prealloc_blocks option, must be between %llu and %llu",
|
|
MIN_DATA_PREALLOC_BLOCKS, MAX_DATA_PREALLOC_BLOCKS);
|
|
return -EINVAL;
|
|
}
|
|
|
|
write_seqlock(&optinf->seqlock);
|
|
optinf->opts.data_prealloc_blocks = val;
|
|
write_sequnlock(&optinf->seqlock);
|
|
|
|
return count;
|
|
}
|
|
SCOUTFS_ATTR_RW(data_prealloc_blocks);
|
|
|
|
static ssize_t data_prealloc_contig_only_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct super_block *sb = SCOUTFS_SYSFS_ATTRS_SB(kobj);
|
|
struct scoutfs_mount_options opts;
|
|
|
|
scoutfs_options_read(sb, &opts);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%u", opts.data_prealloc_contig_only);
|
|
}
|
|
static ssize_t data_prealloc_contig_only_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct super_block *sb = SCOUTFS_SYSFS_ATTRS_SB(kobj);
|
|
DECLARE_OPTIONS_INFO(sb, optinf);
|
|
char nullterm[20]; /* more than enough for octal -U32_MAX */
|
|
long val;
|
|
int len;
|
|
int ret;
|
|
|
|
len = min(count, sizeof(nullterm) - 1);
|
|
memcpy(nullterm, buf, len);
|
|
nullterm[len] = '\0';
|
|
|
|
ret = kstrtol(nullterm, 0, &val);
|
|
if (ret < 0 || val < 0 || val > 1) {
|
|
scoutfs_err(sb, "invalid data_prealloc_contig_only option, bool must be 0 or 1");
|
|
return -EINVAL;
|
|
}
|
|
|
|
write_seqlock(&optinf->seqlock);
|
|
optinf->opts.data_prealloc_contig_only = val;
|
|
write_sequnlock(&optinf->seqlock);
|
|
|
|
return count;
|
|
}
|
|
SCOUTFS_ATTR_RW(data_prealloc_contig_only);
|
|
|
|
static ssize_t log_merge_wait_timeout_ms_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct super_block *sb = SCOUTFS_SYSFS_ATTRS_SB(kobj);
|
|
struct scoutfs_mount_options opts;
|
|
|
|
scoutfs_options_read(sb, &opts);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%u", opts.log_merge_wait_timeout_ms);
|
|
}
|
|
static ssize_t log_merge_wait_timeout_ms_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct super_block *sb = SCOUTFS_SYSFS_ATTRS_SB(kobj);
|
|
DECLARE_OPTIONS_INFO(sb, optinf);
|
|
char nullterm[30]; /* more than enough for octal -U64_MAX */
|
|
int val;
|
|
int len;
|
|
int ret;
|
|
|
|
len = min(count, sizeof(nullterm) - 1);
|
|
memcpy(nullterm, buf, len);
|
|
nullterm[len] = '\0';
|
|
|
|
ret = kstrtoint(nullterm, 0, &val);
|
|
ret = verify_log_merge_wait_timeout_ms(sb, ret, val);
|
|
if (ret == 0) {
|
|
write_seqlock(&optinf->seqlock);
|
|
optinf->opts.log_merge_wait_timeout_ms = val;
|
|
write_sequnlock(&optinf->seqlock);
|
|
ret = count;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
SCOUTFS_ATTR_RW(log_merge_wait_timeout_ms);
|
|
|
|
static ssize_t metadev_path_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct super_block *sb = SCOUTFS_SYSFS_ATTRS_SB(kobj);
|
|
struct scoutfs_mount_options opts;
|
|
|
|
scoutfs_options_read(sb, &opts);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%s", opts.metadev_path);
|
|
}
|
|
SCOUTFS_ATTR_RO(metadev_path);
|
|
|
|
static ssize_t orphan_scan_delay_ms_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct super_block *sb = SCOUTFS_SYSFS_ATTRS_SB(kobj);
|
|
struct scoutfs_mount_options opts;
|
|
|
|
scoutfs_options_read(sb, &opts);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%u", opts.orphan_scan_delay_ms);
|
|
}
|
|
static ssize_t orphan_scan_delay_ms_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct super_block *sb = SCOUTFS_SYSFS_ATTRS_SB(kobj);
|
|
DECLARE_OPTIONS_INFO(sb, optinf);
|
|
char nullterm[20]; /* more than enough for octal -U32_MAX */
|
|
long val;
|
|
int len;
|
|
int ret;
|
|
|
|
len = min(count, sizeof(nullterm) - 1);
|
|
memcpy(nullterm, buf, len);
|
|
nullterm[len] = '\0';
|
|
|
|
ret = kstrtol(nullterm, 0, &val);
|
|
if (ret < 0 || val < MIN_ORPHAN_SCAN_DELAY_MS || val > MAX_ORPHAN_SCAN_DELAY_MS) {
|
|
scoutfs_err(sb, "invalid orphan_scan_delay_ms value written to options sysfs file, must be between %lu and %lu",
|
|
MIN_ORPHAN_SCAN_DELAY_MS, MAX_ORPHAN_SCAN_DELAY_MS);
|
|
return -EINVAL;
|
|
}
|
|
|
|
write_seqlock(&optinf->seqlock);
|
|
optinf->opts.orphan_scan_delay_ms = val;
|
|
write_sequnlock(&optinf->seqlock);
|
|
|
|
scoutfs_inode_schedule_orphan_dwork(sb);
|
|
|
|
return count;
|
|
}
|
|
SCOUTFS_ATTR_RW(orphan_scan_delay_ms);
|
|
|
|
static ssize_t quorum_heartbeat_timeout_ms_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct super_block *sb = SCOUTFS_SYSFS_ATTRS_SB(kobj);
|
|
struct scoutfs_mount_options opts;
|
|
|
|
scoutfs_options_read(sb, &opts);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%llu", opts.quorum_heartbeat_timeout_ms);
|
|
}
|
|
static ssize_t quorum_heartbeat_timeout_ms_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct super_block *sb = SCOUTFS_SYSFS_ATTRS_SB(kobj);
|
|
DECLARE_OPTIONS_INFO(sb, optinf);
|
|
char nullterm[30]; /* more than enough for octal -U64_MAX */
|
|
u64 val;
|
|
int len;
|
|
int ret;
|
|
|
|
len = min(count, sizeof(nullterm) - 1);
|
|
memcpy(nullterm, buf, len);
|
|
nullterm[len] = '\0';
|
|
|
|
ret = kstrtoll(nullterm, 0, &val);
|
|
ret = verify_quorum_heartbeat_timeout_ms(sb, ret, val);
|
|
if (ret == 0) {
|
|
write_seqlock(&optinf->seqlock);
|
|
optinf->opts.quorum_heartbeat_timeout_ms = val;
|
|
write_sequnlock(&optinf->seqlock);
|
|
ret = count;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
SCOUTFS_ATTR_RW(quorum_heartbeat_timeout_ms);
|
|
|
|
static ssize_t quorum_slot_nr_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct super_block *sb = SCOUTFS_SYSFS_ATTRS_SB(kobj);
|
|
struct scoutfs_mount_options opts;
|
|
|
|
scoutfs_options_read(sb, &opts);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", opts.quorum_slot_nr);
|
|
}
|
|
SCOUTFS_ATTR_RO(quorum_slot_nr);
|
|
|
|
static struct attribute *options_attrs[] = {
|
|
SCOUTFS_ATTR_PTR(data_prealloc_blocks),
|
|
SCOUTFS_ATTR_PTR(data_prealloc_contig_only),
|
|
SCOUTFS_ATTR_PTR(log_merge_wait_timeout_ms),
|
|
SCOUTFS_ATTR_PTR(metadev_path),
|
|
SCOUTFS_ATTR_PTR(orphan_scan_delay_ms),
|
|
SCOUTFS_ATTR_PTR(quorum_heartbeat_timeout_ms),
|
|
SCOUTFS_ATTR_PTR(quorum_slot_nr),
|
|
NULL,
|
|
};
|
|
|
|
int scoutfs_options_setup(struct super_block *sb)
|
|
{
|
|
DECLARE_OPTIONS_INFO(sb, optinf);
|
|
int ret;
|
|
|
|
ret = scoutfs_sysfs_create_attrs(sb, &optinf->sysfs_attrs, options_attrs, "mount_options");
|
|
if (ret < 0)
|
|
scoutfs_options_destroy(sb);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* We remove the sysfs files early in unmount so that they can't try to call other subsystems
|
|
* as they're being destroyed.
|
|
*/
|
|
void scoutfs_options_stop(struct super_block *sb)
|
|
{
|
|
DECLARE_OPTIONS_INFO(sb, optinf);
|
|
|
|
if (optinf)
|
|
scoutfs_sysfs_destroy_attrs(sb, &optinf->sysfs_attrs);
|
|
}
|
|
|
|
void scoutfs_options_destroy(struct super_block *sb)
|
|
{
|
|
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
|
|
DECLARE_OPTIONS_INFO(sb, optinf);
|
|
|
|
scoutfs_options_stop(sb);
|
|
|
|
if (optinf) {
|
|
free_options(&optinf->opts);
|
|
kfree(optinf);
|
|
sbi->options_info = NULL;
|
|
}
|
|
}
|