mirror of
https://github.com/versity/scoutfs.git
synced 2025-12-23 05:25:18 +00:00
Add resize_devices ioctl and scoutfs command
Add a scoutfs command that uses an ioctl to send a request to the server to safely use a device that has grown. Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
@@ -977,6 +977,39 @@ int scoutfs_alloc_move(struct super_block *sb, struct scoutfs_alloc *alloc,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add new free space to an allocator. _ext_insert will make sure that it doesn't
|
||||
* overlap with any existing extents. This is done by the server in a transaction that
|
||||
* also updates total_*_blocks in the super so we don't verify.
|
||||
*/
|
||||
int scoutfs_alloc_insert(struct super_block *sb, struct scoutfs_alloc *alloc,
|
||||
struct scoutfs_block_writer *wri, struct scoutfs_alloc_root *root,
|
||||
u64 start, u64 len)
|
||||
{
|
||||
struct alloc_ext_args args = {
|
||||
.alloc = alloc,
|
||||
.wri = wri,
|
||||
.root = root,
|
||||
.zone = SCOUTFS_FREE_EXTENT_BLKNO_ZONE,
|
||||
};
|
||||
|
||||
return scoutfs_ext_insert(sb, &alloc_ext_ops, &args, start, len, 0, 0);
|
||||
}
|
||||
|
||||
int scoutfs_alloc_remove(struct super_block *sb, struct scoutfs_alloc *alloc,
|
||||
struct scoutfs_block_writer *wri, struct scoutfs_alloc_root *root,
|
||||
u64 start, u64 len)
|
||||
{
|
||||
struct alloc_ext_args args = {
|
||||
.alloc = alloc,
|
||||
.wri = wri,
|
||||
.root = root,
|
||||
.zone = SCOUTFS_FREE_EXTENT_BLKNO_ZONE,
|
||||
};
|
||||
|
||||
return scoutfs_ext_remove(sb, &alloc_ext_ops, &args, start, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* We only trim one block, instead of looping trimming all, because the
|
||||
* caller is assuming that we do a fixed amount of work when they check
|
||||
|
||||
@@ -132,6 +132,12 @@ int scoutfs_alloc_move(struct super_block *sb, struct scoutfs_alloc *alloc,
|
||||
struct scoutfs_alloc_root *dst,
|
||||
struct scoutfs_alloc_root *src, u64 total,
|
||||
__le64 *exclusive, __le64 *vacant, u64 zone_blocks);
|
||||
int scoutfs_alloc_insert(struct super_block *sb, struct scoutfs_alloc *alloc,
|
||||
struct scoutfs_block_writer *wri, struct scoutfs_alloc_root *root,
|
||||
u64 start, u64 len);
|
||||
int scoutfs_alloc_remove(struct super_block *sb, struct scoutfs_alloc *alloc,
|
||||
struct scoutfs_block_writer *wri, struct scoutfs_alloc_root *root,
|
||||
u64 start, u64 len);
|
||||
|
||||
int scoutfs_alloc_fill_list(struct super_block *sb,
|
||||
struct scoutfs_alloc *alloc,
|
||||
|
||||
@@ -297,6 +297,14 @@ int scoutfs_client_clear_volopt(struct super_block *sb, struct scoutfs_volume_op
|
||||
volopt, sizeof(*volopt), NULL, 0);
|
||||
}
|
||||
|
||||
int scoutfs_client_resize_devices(struct super_block *sb, struct scoutfs_net_resize_devices *nrd)
|
||||
{
|
||||
struct client_info *client = SCOUTFS_SB(sb)->client_info;
|
||||
|
||||
return scoutfs_net_sync_request(sb, client->conn, SCOUTFS_NET_CMD_RESIZE_DEVICES,
|
||||
nrd, sizeof(*nrd), NULL, 0);
|
||||
}
|
||||
|
||||
/* The client is receiving a invalidation request from the server */
|
||||
static int client_lock(struct super_block *sb,
|
||||
struct scoutfs_net_connection *conn, u8 cmd, u64 id,
|
||||
|
||||
@@ -33,6 +33,7 @@ int scoutfs_client_open_ino_map(struct super_block *sb, u64 group_nr,
|
||||
int scoutfs_client_get_volopt(struct super_block *sb, struct scoutfs_volume_options *volopt);
|
||||
int scoutfs_client_set_volopt(struct super_block *sb, struct scoutfs_volume_options *volopt);
|
||||
int scoutfs_client_clear_volopt(struct super_block *sb, struct scoutfs_volume_options *volopt);
|
||||
int scoutfs_client_resize_devices(struct super_block *sb, struct scoutfs_net_resize_devices *nrd);
|
||||
|
||||
int scoutfs_client_setup(struct super_block *sb);
|
||||
void scoutfs_client_destroy(struct super_block *sb);
|
||||
|
||||
@@ -986,6 +986,7 @@ enum scoutfs_net_cmd {
|
||||
SCOUTFS_NET_CMD_GET_VOLOPT,
|
||||
SCOUTFS_NET_CMD_SET_VOLOPT,
|
||||
SCOUTFS_NET_CMD_CLEAR_VOLOPT,
|
||||
SCOUTFS_NET_CMD_RESIZE_DEVICES,
|
||||
SCOUTFS_NET_CMD_FAREWELL,
|
||||
SCOUTFS_NET_CMD_UNKNOWN,
|
||||
};
|
||||
@@ -1028,6 +1029,11 @@ struct scoutfs_net_roots {
|
||||
struct scoutfs_btree_root srch_root;
|
||||
};
|
||||
|
||||
struct scoutfs_net_resize_devices {
|
||||
__le64 new_total_meta_blocks;
|
||||
__le64 new_total_data_blocks;
|
||||
};
|
||||
|
||||
struct scoutfs_net_lock {
|
||||
struct scoutfs_key key;
|
||||
__le64 write_seq;
|
||||
|
||||
@@ -867,13 +867,21 @@ static long scoutfs_ioc_statfs_more(struct file *file, unsigned long arg)
|
||||
{
|
||||
struct super_block *sb = file_inode(file)->i_sb;
|
||||
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
|
||||
struct scoutfs_super_block *super = &sbi->super;
|
||||
struct scoutfs_super_block *super;
|
||||
struct scoutfs_ioctl_statfs_more sfm;
|
||||
int ret;
|
||||
|
||||
if (get_user(sfm.valid_bytes, (__u64 __user *)arg))
|
||||
return -EFAULT;
|
||||
|
||||
super = kzalloc(sizeof(struct scoutfs_super_block), GFP_NOFS);
|
||||
if (!super)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = scoutfs_read_super(sb, super);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
sfm.valid_bytes = min_t(u64, sfm.valid_bytes,
|
||||
sizeof(struct scoutfs_ioctl_statfs_more));
|
||||
sfm.fsid = le64_to_cpu(super->hdr.fsid);
|
||||
@@ -884,12 +892,15 @@ static long scoutfs_ioc_statfs_more(struct file *file, unsigned long arg)
|
||||
|
||||
ret = scoutfs_client_get_last_seq(sb, &sfm.committed_seq);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out;
|
||||
|
||||
if (copy_to_user((void __user *)arg, &sfm, sfm.valid_bytes))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
ret = -EFAULT;
|
||||
else
|
||||
ret = 0;
|
||||
out:
|
||||
kfree(super);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct copy_alloc_detail_args {
|
||||
@@ -993,6 +1004,37 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long scoutfs_ioc_resize_devices(struct file *file, unsigned long arg)
|
||||
{
|
||||
struct super_block *sb = file_inode(file)->i_sb;
|
||||
struct scoutfs_ioctl_resize_devices __user *urd = (void __user *)arg;
|
||||
struct scoutfs_ioctl_resize_devices rd;
|
||||
struct scoutfs_net_resize_devices nrd;
|
||||
int ret;
|
||||
|
||||
if (!(file->f_mode & FMODE_READ)) {
|
||||
ret = -EBADF;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN)) {
|
||||
ret = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (copy_from_user(&rd, urd, sizeof(rd))) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
nrd.new_total_meta_blocks = cpu_to_le64(rd.new_total_meta_blocks);
|
||||
nrd.new_total_data_blocks = cpu_to_le64(rd.new_total_data_blocks);
|
||||
|
||||
ret = scoutfs_client_resize_devices(sb, &nrd);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
long scoutfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
@@ -1022,6 +1064,8 @@ long scoutfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
return scoutfs_ioc_alloc_detail(file, arg);
|
||||
case SCOUTFS_IOC_MOVE_BLOCKS:
|
||||
return scoutfs_ioc_move_blocks(file, arg);
|
||||
case SCOUTFS_IOC_RESIZE_DEVICES:
|
||||
return scoutfs_ioc_resize_devices(file, arg);
|
||||
}
|
||||
|
||||
return -ENOTTY;
|
||||
|
||||
@@ -477,4 +477,12 @@ struct scoutfs_ioctl_move_blocks {
|
||||
#define SCOUTFS_IOC_MOVE_BLOCKS _IOR(SCOUTFS_IOCTL_MAGIC, 13, \
|
||||
struct scoutfs_ioctl_move_blocks)
|
||||
|
||||
struct scoutfs_ioctl_resize_devices {
|
||||
__u64 new_total_meta_blocks;
|
||||
__u64 new_total_data_blocks;
|
||||
};
|
||||
|
||||
#define SCOUTFS_IOC_RESIZE_DEVICES \
|
||||
_IOR(SCOUTFS_IOCTL_MAGIC, 14, struct scoutfs_ioctl_resize_devices)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2563,6 +2563,103 @@ out:
|
||||
return scoutfs_net_response(sb, conn, cmd, id, ret, NULL, 0);
|
||||
}
|
||||
|
||||
static u64 device_blocks(struct block_device *bdev, int shift)
|
||||
{
|
||||
return i_size_read(bdev->bd_inode) >> shift;
|
||||
}
|
||||
|
||||
static int server_resize_devices(struct super_block *sb, struct scoutfs_net_connection *conn,
|
||||
u8 cmd, u64 id, void *arg, u16 arg_len)
|
||||
{
|
||||
DECLARE_SERVER_INFO(sb, server);
|
||||
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
|
||||
struct scoutfs_super_block *super = &SCOUTFS_SB(sb)->super;
|
||||
struct scoutfs_net_resize_devices *nrd;
|
||||
u64 meta_tot;
|
||||
u64 meta_start;
|
||||
u64 meta_len;
|
||||
u64 data_tot;
|
||||
u64 data_start;
|
||||
u64 data_len;
|
||||
int ret;
|
||||
int err;
|
||||
|
||||
if (arg_len != sizeof(struct scoutfs_net_resize_devices)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
nrd = arg;
|
||||
|
||||
meta_tot = le64_to_cpu(nrd->new_total_meta_blocks);
|
||||
data_tot = le64_to_cpu(nrd->new_total_data_blocks);
|
||||
|
||||
scoutfs_server_hold_commit(sb);
|
||||
mutex_lock(&server->alloc_mutex);
|
||||
|
||||
if (meta_tot == le64_to_cpu(super->total_meta_blocks))
|
||||
meta_tot = 0;
|
||||
if (data_tot == le64_to_cpu(super->total_data_blocks))
|
||||
data_tot = 0;
|
||||
|
||||
if (!meta_tot && !data_tot) {
|
||||
ret = 0;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* we don't support shrinking */
|
||||
if ((meta_tot && (meta_tot < le64_to_cpu(super->total_meta_blocks))) ||
|
||||
(data_tot && (data_tot < le64_to_cpu(super->total_data_blocks)))) {
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* must be within devices */
|
||||
if ((meta_tot > device_blocks(sbi->meta_bdev, SCOUTFS_BLOCK_LG_SHIFT)) ||
|
||||
(data_tot > device_blocks(sb->s_bdev, SCOUTFS_BLOCK_SM_SHIFT))) {
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* extents are only used if _tot is set */
|
||||
meta_start = le64_to_cpu(super->total_meta_blocks);
|
||||
meta_len = meta_tot - meta_start;
|
||||
data_start = le64_to_cpu(super->total_data_blocks);
|
||||
data_len = data_tot - data_start;
|
||||
|
||||
if (meta_tot) {
|
||||
ret = scoutfs_alloc_insert(sb, &server->alloc, &server->wri,
|
||||
server->meta_avail, meta_start, meta_len);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (data_tot) {
|
||||
ret = scoutfs_alloc_insert(sb, &server->alloc, &server->wri,
|
||||
&super->data_alloc, data_start, data_len);
|
||||
if (ret < 0) {
|
||||
if (meta_tot) {
|
||||
err = scoutfs_alloc_remove(sb, &server->alloc, &server->wri,
|
||||
server->meta_avail, meta_start,
|
||||
meta_len);
|
||||
WARN_ON_ONCE(err); /* btree blocks are dirty.. really unlikely? */
|
||||
}
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
|
||||
if (meta_tot)
|
||||
super->total_meta_blocks = cpu_to_le64(meta_tot);
|
||||
if (data_tot)
|
||||
super->total_data_blocks = cpu_to_le64(data_tot);
|
||||
|
||||
ret = 0;
|
||||
unlock:
|
||||
mutex_unlock(&server->alloc_mutex);
|
||||
ret = scoutfs_server_apply_commit(sb, ret);
|
||||
out:
|
||||
return scoutfs_net_response(sb, conn, cmd, id, ret, NULL, 0);
|
||||
};
|
||||
|
||||
static void init_mounted_client_key(struct scoutfs_key *key, u64 rid)
|
||||
{
|
||||
*key = (struct scoutfs_key) {
|
||||
@@ -3199,6 +3296,7 @@ static scoutfs_net_request_t server_req_funcs[] = {
|
||||
[SCOUTFS_NET_CMD_GET_VOLOPT] = server_get_volopt,
|
||||
[SCOUTFS_NET_CMD_SET_VOLOPT] = server_set_volopt,
|
||||
[SCOUTFS_NET_CMD_CLEAR_VOLOPT] = server_clear_volopt,
|
||||
[SCOUTFS_NET_CMD_RESIZE_DEVICES] = server_resize_devices,
|
||||
[SCOUTFS_NET_CMD_FAREWELL] = server_farewell,
|
||||
};
|
||||
|
||||
|
||||
27
tests/golden/resize-devices
Normal file
27
tests/golden/resize-devices
Normal file
@@ -0,0 +1,27 @@
|
||||
== make initial small fs
|
||||
== 0s do nothing
|
||||
== shrinking fails
|
||||
resize_devices ioctl failed: Invalid argument (22)
|
||||
scoutfs: resize-devices failed: Invalid argument (22)
|
||||
resize_devices ioctl failed: Invalid argument (22)
|
||||
scoutfs: resize-devices failed: Invalid argument (22)
|
||||
resize_devices ioctl failed: Invalid argument (22)
|
||||
scoutfs: resize-devices failed: Invalid argument (22)
|
||||
== existing sizes do nothing
|
||||
== growing outside device fails
|
||||
resize_devices ioctl failed: Invalid argument (22)
|
||||
scoutfs: resize-devices failed: Invalid argument (22)
|
||||
resize_devices ioctl failed: Invalid argument (22)
|
||||
scoutfs: resize-devices failed: Invalid argument (22)
|
||||
resize_devices ioctl failed: Invalid argument (22)
|
||||
scoutfs: resize-devices failed: Invalid argument (22)
|
||||
== resizing meta works
|
||||
== resizing data works
|
||||
== shrinking back fails
|
||||
resize_devices ioctl failed: Invalid argument (22)
|
||||
scoutfs: resize-devices failed: Invalid argument (22)
|
||||
resize_devices ioctl failed: Invalid argument (22)
|
||||
scoutfs: resize-devices failed: Invalid argument (22)
|
||||
== resizing again does nothing
|
||||
== resizing to full works
|
||||
== cleanup extra fs
|
||||
@@ -29,6 +29,7 @@ lock-conflicting-batch-commit.sh
|
||||
cross-mount-data-free.sh
|
||||
persistent-item-vers.sh
|
||||
setup-error-teardown.sh
|
||||
resize-devices.sh
|
||||
fence-and-reclaim.sh
|
||||
orphan-inodes.sh
|
||||
mount-unmount-race.sh
|
||||
|
||||
149
tests/tests/resize-devices.sh
Normal file
149
tests/tests/resize-devices.sh
Normal file
@@ -0,0 +1,149 @@
|
||||
#
|
||||
# Some basic tests of online resizing metadata and data devices.
|
||||
#
|
||||
|
||||
statfs_total() {
|
||||
local single="total_$1_blocks"
|
||||
local mnt="$2"
|
||||
|
||||
scoutfs statfs -s $single -p "$mnt"
|
||||
}
|
||||
|
||||
df_free() {
|
||||
local md="$1"
|
||||
local mnt="$2"
|
||||
|
||||
scoutfs df -p "$mnt" | awk '($1 == "'$md'") { print $5; exit }'
|
||||
}
|
||||
|
||||
same_totals() {
|
||||
cur_meta_tot=$(statfs_total meta "$SCR")
|
||||
cur_data_tot=$(statfs_total data "$SCR")
|
||||
|
||||
test "$cur_meta_tot" == "$exp_meta_tot" || \
|
||||
t_fail "cur total_meta_blocks $cur_meta_tot != expected $exp_meta_tot"
|
||||
test "$cur_data_tot" == "$exp_data_tot" || \
|
||||
t_fail "cur total_data_blocks $cur_data_tot != expected $exp_data_tot"
|
||||
}
|
||||
|
||||
#
|
||||
# make sure that the specified devices have grown by doubling. The
|
||||
# total blocks can be tested exactly but the df reported total needs
|
||||
# some slop to account for reserved blocks and concurrent allocation.
|
||||
#
|
||||
devices_grew() {
|
||||
cur_meta_tot=$(statfs_total meta "$SCR")
|
||||
cur_data_tot=$(statfs_total data "$SCR")
|
||||
cur_meta_df=$(df_free MetaData "$SCR")
|
||||
cur_data_df=$(df_free Data "$SCR")
|
||||
|
||||
local grow_meta_tot=$(echo "$exp_meta_tot * 2" | bc)
|
||||
local grow_data_tot=$(echo "$exp_data_tot * 2" | bc)
|
||||
local grow_meta_df=$(echo "($exp_meta_df * 1.95)/1" | bc)
|
||||
local grow_data_df=$(echo "($exp_data_df * 1.95)/1" | bc)
|
||||
|
||||
if [ "$1" == "meta" ]; then
|
||||
test "$cur_meta_tot" == "$grow_meta_tot" || \
|
||||
t_fail "cur total_meta_blocks $cur_meta_tot != grown $grow_meta_tot"
|
||||
test "$cur_meta_df" -lt "$grow_meta_df" && \
|
||||
t_fail "cur meta df total $cur_meta_df < grown $grow_meta_df"
|
||||
exp_meta_tot=$cur_meta_tot
|
||||
exp_meta_df=$cur_meta_df
|
||||
shift
|
||||
fi
|
||||
|
||||
if [ "$1" == "data" ]; then
|
||||
test "$cur_data_tot" == "$grow_data_tot" || \
|
||||
t_fail "cur total_data_blocks $cur_data_tot != grown $grow_data_tot"
|
||||
test "$cur_data_df" -lt "$grow_data_df" && \
|
||||
t_fail "cur data df total $cur_data_df < grown $grow_data_df"
|
||||
exp_data_tot=$cur_data_tot
|
||||
exp_data_df=$cur_data_df
|
||||
fi
|
||||
}
|
||||
|
||||
# first calculate small mkfs based on device size
|
||||
size_meta=$(blockdev --getsize64 "$T_EX_META_DEV")
|
||||
size_data=$(blockdev --getsize64 "$T_EX_DATA_DEV")
|
||||
quarter_meta=$(echo "$size_meta / 4" | bc)
|
||||
quarter_data=$(echo "$size_data / 4" | bc)
|
||||
|
||||
# XXX this is all pretty manual, would be nice to have helpers
|
||||
echo "== make initial small fs"
|
||||
scoutfs mkfs -A -f -Q 0,127.0.0.1,53000 -m $quarter_meta -d $quarter_data \
|
||||
"$T_EX_META_DEV" "$T_EX_DATA_DEV" > $T_TMP.mkfs.out 2>&1 || \
|
||||
t_fail "mkfs failed"
|
||||
SCR="/mnt/scoutfs.enospc"
|
||||
mkdir -p "$SCR"
|
||||
mount -t scoutfs -o metadev_path=$T_EX_META_DEV,quorum_slot_nr=0 \
|
||||
"$T_EX_DATA_DEV" "$SCR"
|
||||
|
||||
# then calculate sizes based on blocks that mkfs used
|
||||
quarter_meta=$(echo "$(statfs_total meta "$SCR") * 64 * 1024" | bc)
|
||||
quarter_data=$(echo "$(statfs_total data "$SCR") * 4 * 1024" | bc)
|
||||
whole_meta=$(echo "$quarter_meta * 4" | bc)
|
||||
whole_data=$(echo "$quarter_data * 4" | bc)
|
||||
outsize_meta=$(echo "$whole_meta * 2" | bc)
|
||||
outsize_data=$(echo "$whole_data * 2" | bc)
|
||||
half_meta=$(echo "$whole_meta / 2" | bc)
|
||||
half_data=$(echo "$whole_data / 2" | bc)
|
||||
shrink_meta=$(echo "$quarter_meta / 2" | bc)
|
||||
shrink_data=$(echo "$quarter_data / 2" | bc)
|
||||
|
||||
# and save expected values for checks
|
||||
exp_meta_tot=$(statfs_total meta "$SCR")
|
||||
exp_meta_df=$(df_free MetaData "$SCR")
|
||||
exp_data_tot=$(statfs_total data "$SCR")
|
||||
exp_data_df=$(df_free Data "$SCR")
|
||||
|
||||
echo "== 0s do nothing"
|
||||
scoutfs resize-devices -p "$SCR"
|
||||
scoutfs resize-devices -p "$SCR" -m 0
|
||||
scoutfs resize-devices -p "$SCR" -d 0
|
||||
scoutfs resize-devices -p "$SCR" -m 0 -d 0
|
||||
|
||||
echo "== shrinking fails"
|
||||
scoutfs resize-devices -p "$SCR" -m $shrink_meta
|
||||
scoutfs resize-devices -p "$SCR" -d $shrink_data
|
||||
scoutfs resize-devices -p "$SCR" -m $shrink_meta -d $shrink_data
|
||||
same_totals
|
||||
|
||||
echo "== existing sizes do nothing"
|
||||
scoutfs resize-devices -p "$SCR" -m $quarter_meta
|
||||
scoutfs resize-devices -p "$SCR" -d $quarter_data
|
||||
scoutfs resize-devices -p "$SCR" -m $quarter_meta -d $quarter_data
|
||||
same_totals
|
||||
|
||||
echo "== growing outside device fails"
|
||||
scoutfs resize-devices -p "$SCR" -m $outsize_meta
|
||||
scoutfs resize-devices -p "$SCR" -d $outsize_data
|
||||
scoutfs resize-devices -p "$SCR" -m $outsize_meta -d $outsize_data
|
||||
same_totals
|
||||
|
||||
echo "== resizing meta works"
|
||||
scoutfs resize-devices -p "$SCR" -m $half_meta
|
||||
devices_grew meta
|
||||
|
||||
echo "== resizing data works"
|
||||
scoutfs resize-devices -p "$SCR" -d $half_data
|
||||
devices_grew data
|
||||
|
||||
echo "== shrinking back fails"
|
||||
scoutfs resize-devices -p "$SCR" -m $quarter_meta
|
||||
scoutfs resize-devices -p "$SCR" -m $quarter_data
|
||||
same_totals
|
||||
|
||||
echo "== resizing again does nothing"
|
||||
scoutfs resize-devices -p "$SCR" -m $half_meta
|
||||
scoutfs resize-devices -p "$SCR" -m $half_data
|
||||
same_totals
|
||||
|
||||
echo "== resizing to full works"
|
||||
scoutfs resize-devices -p "$SCR" -m $whole_meta -d $whole_data
|
||||
devices_grew meta data
|
||||
|
||||
echo "== cleanup extra fs"
|
||||
umount "$SCR"
|
||||
rmdir "$SCR"
|
||||
|
||||
t_pass
|
||||
@@ -103,6 +103,63 @@ Ignore presence of existing data on the data and metadata devices.
|
||||
.PD
|
||||
|
||||
.TP
|
||||
.BI "resize-devices [-p|--path PATH] [-m|--meta-size SIZE] [-d|--data-size SIZE]"
|
||||
.sp
|
||||
Resize the metadata or data devices of a mounted ScoutFS filesystem.
|
||||
.sp
|
||||
ScoutFS metadata has free extent records and fields in the super block
|
||||
that reflect the size of the devices in use. This command sends a
|
||||
request to the server to change the size of the device that can be used
|
||||
by updating free extents and setting the super block fields.
|
||||
.sp
|
||||
The specified sizes are in bytes and are translated into block counts.
|
||||
If the specified sizes are not a multiple of the metadata or data block
|
||||
sizes then a message is output and the resized size is truncated down to
|
||||
the next whole block. Specifying either a size of 0 or the current
|
||||
device size makes no change. The current size of the devices can be
|
||||
seen, in units of their respective block sizes, in the total_meta_blocks
|
||||
and total_data_blocks fields returned by the scoutfs statfs command (via
|
||||
the statfs_more ioctl).
|
||||
.sp
|
||||
Shrinking is not supported. Specifying a smaller size for either device
|
||||
will return an error and neither device will be resized.
|
||||
.sp
|
||||
Specifying a larger size will expand the initial size of the device that
|
||||
will be used. Free space records are added for the expanded region and
|
||||
can be used once the resizing transaction is complete.
|
||||
.sp
|
||||
The resizing action is performed in a transaction on the server. This
|
||||
command will hang until a server is elected and running and can service
|
||||
the reqeust. The server serializes any concurrent requests to resize.
|
||||
.sp
|
||||
The new sizes must fit within the current sizes of the mounted devices.
|
||||
Presumably this command is being performed as part of a larger
|
||||
coordinated resize of the underlying devices. The device must be
|
||||
expanded before ScoutFS can use the larger device and ScoutFS must stop
|
||||
using a region to shrink before it could be removed from the device
|
||||
(which is not currently supported).
|
||||
.sp
|
||||
The resize will be committed by the server before the response is sent
|
||||
to the client. The system can be using the new device size before the
|
||||
result is communicated through the client and this command completes.
|
||||
The client could crash and the server could still have performed the
|
||||
resize.
|
||||
.RS 1.0i
|
||||
.PD 0
|
||||
.TP
|
||||
.sp
|
||||
.B "-p, --path PATH"
|
||||
A path in the mounted ScoutFS filesystem which will have its devices
|
||||
resized.
|
||||
.TP
|
||||
.B "-m, --meta-size SIZE"
|
||||
.B "-d, --data-size SIZE"
|
||||
The new size of the metadata or data device to use, in bytes. Size is given as
|
||||
an integer followed by a units digit: "K", "M", "G", "T", "P", to denote
|
||||
kibibytes, mebibytes, etc.
|
||||
.RE
|
||||
.PD
|
||||
|
||||
.BI "stat FILE [-s|--single-field FIELD-NAME]"
|
||||
.sp
|
||||
Display ScoutFS-specific metadata fields for the given file.
|
||||
|
||||
120
utils/src/resize_devices.c
Normal file
120
utils/src/resize_devices.c
Normal file
@@ -0,0 +1,120 @@
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <argp.h>
|
||||
|
||||
#include "sparse.h"
|
||||
#include "parse.h"
|
||||
#include "util.h"
|
||||
#include "format.h"
|
||||
#include "ioctl.h"
|
||||
#include "cmd.h"
|
||||
|
||||
struct resize_args {
|
||||
char *path;
|
||||
u64 meta_size;
|
||||
u64 data_size;
|
||||
};
|
||||
|
||||
static int do_resize_devices(struct resize_args *args)
|
||||
{
|
||||
struct scoutfs_ioctl_resize_devices rd;
|
||||
int ret;
|
||||
int fd;
|
||||
|
||||
if (args->meta_size & SCOUTFS_BLOCK_LG_MASK) {
|
||||
printf("metadata device size %llu is not a multiple of %u metadata block size, truncating down to %llu byte size\n",
|
||||
args->meta_size, SCOUTFS_BLOCK_LG_SIZE,
|
||||
args->meta_size & ~(u64)SCOUTFS_BLOCK_LG_MASK);
|
||||
}
|
||||
|
||||
if (args->data_size & SCOUTFS_BLOCK_SM_MASK) {
|
||||
printf("data device size %llu is not a multiple of %u data block size, truncating down to %llu byte size\n",
|
||||
args->data_size, SCOUTFS_BLOCK_SM_SIZE,
|
||||
args->data_size & ~(u64)SCOUTFS_BLOCK_SM_MASK);
|
||||
}
|
||||
|
||||
fd = get_path(args->path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
rd.new_total_meta_blocks = args->meta_size >> SCOUTFS_BLOCK_LG_SHIFT;
|
||||
rd.new_total_data_blocks = args->data_size >> SCOUTFS_BLOCK_SM_SHIFT;
|
||||
|
||||
ret = ioctl(fd, SCOUTFS_IOC_RESIZE_DEVICES, &rd);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "resize_devices ioctl failed: %s (%d)\n", strerror(errno), errno);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return ret;
|
||||
};
|
||||
|
||||
static int parse_opt(int key, char *arg, struct argp_state *state)
|
||||
{
|
||||
struct resize_args *args = state->input;
|
||||
int ret;
|
||||
|
||||
switch (key) {
|
||||
case 'm': /* meta-size */
|
||||
{
|
||||
ret = parse_human(arg, &args->meta_size);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
case 'd': /* data-size */
|
||||
{
|
||||
ret = parse_human(arg, &args->data_size);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
args->path = strdup_or_error(state, arg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct argp_option options[] = {
|
||||
{ "path", 'p', "PATH", 0, "Path to ScoutFS filesystem"},
|
||||
{ "meta-size", 'm', "SIZE", 0, "New metadata device size (bytes or KMGTP units)"},
|
||||
{ "data-size", 'd', "SIZE", 0, "New data device size (bytes or KMGTP units)"},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static struct argp argp = {
|
||||
options,
|
||||
parse_opt,
|
||||
"",
|
||||
"Online resize of metadata and/or data devices",
|
||||
};
|
||||
|
||||
static int resize_devices_cmd(int argc, char **argv)
|
||||
{
|
||||
|
||||
struct resize_args resize_args = {NULL,};
|
||||
int ret;
|
||||
|
||||
ret = argp_parse(&argp, argc, argv, 0, NULL, &resize_args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return do_resize_devices(&resize_args);
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) read_xattr_totals_ctor(void)
|
||||
{
|
||||
cmd_register_argp("resize-devices", &argp, GROUP_CORE, resize_devices_cmd);
|
||||
}
|
||||
Reference in New Issue
Block a user