Skip to contentSkip to conversation

GFS: mount and tuning options

PATCH In review David Teigland 4 participants 10 messages Oct 10, 2005 – Oct 12, 2005

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 11/16 GFS: mount and tuning options

There are a variety of mount options, tunable parameters, internal statistics, and methods of online file system manipulation.

---

 fs/gfs2/ioctl.c  |  808 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/gfs2/ioctl.h  |   15 +
 fs/gfs2/mount.c  |  211 ++++++++++++++
 fs/gfs2/mount.h  |   15 +
 fs/gfs2/resize.c |  284 +++++++++++++++++++
 fs/gfs2/resize.h |   19 +
 fs/gfs2/sys.c    |  617 +++++++++++++++++++++++++++++++++++++++++
 fs/gfs2/sys.h    |   24 +
 8 files changed, 1993 insertions(+)
fs/gfs2/ioctl.c +808 -0
@@ -0,0 +1,808 @@
+/*
+ * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ */
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <linux/gfs2_ioctl.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+
+#include "gfs2.h"
+#include "bmap.h"
+#include "dir.h"
+#include "eattr.h"
+#include "glock.h"
+#include "glops.h"
+#include "inode.h"
+#include "ioctl.h"
+#include "jdata.h"
+#include "log.h"
+#include "meta_io.h"
+#include "quota.h"
+#include "resize.h"
+#include "rgrp.h"
+#include "super.h"
+#include "trans.h"
+
+#define ARG_SIZE 32
+
+/**
+ * gi_get_super - Return the "struct gfs2_sb" for a filesystem
+ * @sdp:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int gi_get_super(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	struct gfs2_holder sb_gh;
+	struct buffer_head *bh;
+	struct gfs2_sb *sb;
+	int error;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	if (gi->gi_size != sizeof(struct gfs2_sb))
+		return -EINVAL;
+
+	sb = kmalloc(sizeof(struct gfs2_sb), GFP_KERNEL);
+	if (!sb)
+		return -ENOMEM;
+
+	error = gfs2_glock_nq_num(sdp,
+				 GFS2_SB_LOCK, &gfs2_meta_glops,
+				 LM_ST_SHARED, 0, &sb_gh);
+	if (error)
+		goto out;
+
+	error = gfs2_meta_read(sb_gh.gh_gl,
+			       GFS2_SB_ADDR >> sdp->sd_fsb2bb_shift,
+			       DIO_START | DIO_WAIT,
+			       &bh);
+	if (error) {
+		gfs2_glock_dq_uninit(&sb_gh);
+		goto out;
+	}
+	gfs2_sb_in(sb, bh->b_data);
+	brelse(bh);
+
+	gfs2_glock_dq_uninit(&sb_gh);
+
+	if (copy_to_user(gi->gi_data, sb, sizeof(struct gfs2_sb)))
+		error = -EFAULT;
+	else
+		error = sizeof(struct gfs2_sb);
+
+ out:
+	kfree(sb);
+
+	return error;
+}
+
+static int gi_get_file_stat(struct gfs2_inode *ip, struct gfs2_ioctl *gi)
+{
+	struct gfs2_holder i_gh;
+	struct gfs2_dinode *di;
+	int error;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	if (gi->gi_size != sizeof(struct gfs2_dinode))
+		return -EINVAL;
+
+	di = kmalloc(sizeof(struct gfs2_dinode), GFP_KERNEL);
+	if (!di)
+		return -ENOMEM;
+
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
+	if (error)
+		goto out;
+	memcpy(di, &ip->i_di, sizeof(struct gfs2_dinode));
+	gfs2_glock_dq_uninit(&i_gh);
+
+	if (copy_to_user(gi->gi_data, di, sizeof(struct gfs2_dinode)))
+		error = -EFAULT;
+	else
+		error = sizeof(struct gfs2_dinode);
+
+ out:
+	kfree(di);
+
+	return error;
+}
+
+static int gi_set_file_flag(struct gfs2_inode *ip, struct gfs2_ioctl *gi)
+{
+	char buf[ARG_SIZE];
+	int set;
+	uint32_t flag;
+	struct gfs2_holder i_gh;
+	struct buffer_head *dibh;
+	int error;
+
+	if (gi->gi_argc != 3)
+		return -EINVAL;
+
+	if (strncpy_from_user(buf, gi->gi_argv[1], ARG_SIZE) < 0)
+		return -EFAULT;
+	buf[ARG_SIZE - 1] = 0;
+
+	if (strcmp(buf, "set") == 0)
+		set = 1;
+	else if (strcmp(buf, "clear") == 0)
+		set = 0;
+	else
+		return -EINVAL;
+
+	if (strncpy_from_user(buf, gi->gi_argv[2], ARG_SIZE) < 0)
+		return -EFAULT;
+	buf[ARG_SIZE - 1] = 0;
+
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
+	if (error)
+		return error;
+
+	error = -EACCES;
+	if (ip->i_di.di_uid != current->fsuid && !capable(CAP_FOWNER))
+		goto out;
+
+	error = -EINVAL;
+
+	if (strcmp(buf, "jdata") == 0) {
+		if (!S_ISREG(ip->i_di.di_mode) || ip->i_di.di_size)
+			goto out;
+		flag = GFS2_DIF_JDATA;
+	} else if (strcmp(buf, "directio") == 0) {
+		if (!S_ISREG(ip->i_di.di_mode))
+			goto out;
+		flag = GFS2_DIF_DIRECTIO;
+	} else if (strcmp(buf, "immutable") == 0) {
+		/* The IMMUTABLE flag can only be changed by
+		   the relevant capability. */
+		error = -EPERM;
+		if (!capable(CAP_LINUX_IMMUTABLE))
+			goto out;
+		flag = GFS2_DIF_IMMUTABLE;
+	} else if (strcmp(buf, "appendonly") == 0) {
+		/* The APPENDONLY flag can only be changed by
+		   the relevant capability. */
+		error = -EPERM;
+		if (!capable(CAP_LINUX_IMMUTABLE))
+			goto out;
+		flag = GFS2_DIF_APPENDONLY;
+	} else if (strcmp(buf, "inherit_jdata") == 0) {
+		if (!S_ISDIR(ip->i_di.di_mode))
+			goto out;
+		flag = GFS2_DIF_INHERIT_JDATA;
+	} else if (strcmp(buf, "inherit_directio") == 0) {
+		if (S_ISDIR(ip->i_di.di_mode))
+			goto out;
+		flag = GFS2_DIF_INHERIT_DIRECTIO;
+	} else
+		goto out;
+
+	error = gfs2_trans_begin(ip->i_sbd, RES_DINODE, 0);
+	if (error)
+		goto out;
+
+	error = gfs2_meta_inode_buffer(ip, &dibh);
+	if (error)
+		goto out_trans_end;
+
+	if (set)
+		ip->i_di.di_flags |= flag;
+	else
+		ip->i_di.di_flags &= ~flag;
+
+	gfs2_trans_add_bh(ip->i_gl, dibh);
+	gfs2_dinode_out(&ip->i_di, dibh->b_data);
+
+	brelse(dibh);
+
+ out_trans_end:
+	gfs2_trans_end(ip->i_sbd);
+
+ out:
+	gfs2_glock_dq_uninit(&i_gh);
+
+	return error;
+
+}
+
+static int gi_get_bmap(struct gfs2_inode *ip, struct gfs2_ioctl *gi)
+{
+	struct gfs2_holder gh;
+	uint64_t lblock, dblock = 0;
+	int new = 0;
+	int error;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	if (gi->gi_size != sizeof(uint64_t))
+		return -EINVAL;
+
+	error = copy_from_user(&lblock, gi->gi_data, sizeof(uint64_t));
+	if (error)
+		return -EFAULT;
+
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
+	if (error)
+		return error;
+
+	error = -EACCES;
+	if (ip->i_di.di_uid == current->fsuid || capable(CAP_FOWNER)) {
+		error = 0;
+		if (!gfs2_is_stuffed(ip))
+			error = gfs2_block_map(ip, lblock, &new, &dblock, NULL);
+	}
+
+	gfs2_glock_dq_uninit(&gh);
+
+	if (!error) {
+		error = copy_to_user(gi->gi_data, &dblock, sizeof(uint64_t));
+		if (error)
+			error = -EFAULT;
+	}
+
+	return error;
+}
+
+/**
+ * gi_get_file_meta - Return all the metadata for a file
+ * @ip:
+ * @gi:
+ *
+ * Returns: the number of bytes copied, or -errno
+ */
+
+static int gi_get_file_meta(struct gfs2_inode *ip, struct gfs2_ioctl *gi)
+{
+	struct gfs2_holder i_gh;
+	struct gfs2_user_buffer ub;
+	int error;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+
+	ub.ub_data = gi->gi_data;
+	ub.ub_size = gi->gi_size;
+	ub.ub_count = 0;
+
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
+	if (error)
+		return error;
+
+	error = -EACCES;
+	if (ip->i_di.di_uid != current->fsuid && !capable(CAP_FOWNER))
+		goto out;
+
+	error = gfs2_get_file_meta(ip, &ub);
+	if (error)
+		goto out;
+
+	if (S_ISDIR(ip->i_di.di_mode) &&
+	    (ip->i_di.di_flags & GFS2_DIF_EXHASH)) {
+		error = gfs2_get_dir_meta(ip, &ub);
+		if (error)
+			goto out;
+	}
+
+	if (ip->i_di.di_eattr) {
+		error = gfs2_get_eattr_meta(ip, &ub);
+		if (error)
+			goto out;
+	}
+
+	error = ub.ub_count;
+
+ out:
+	gfs2_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gi_do_file_flush - sync out all dirty data and
+ *                    drop the cache (and lock) for a file.
+ * @ip:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int gi_do_file_flush(struct gfs2_inode *ip, struct gfs2_ioctl *gi)
+{
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	gfs2_glock_force_drop(ip->i_gl);
+	return 0;
+}
+
+/**
+ * gi2hip - return the "struct gfs2_inode" for a hidden file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: the "struct gfs2_inode"
+ */
+
+static struct gfs2_inode *gi2hip(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	char buf[ARG_SIZE];
+
+	if (gi->gi_argc != 2)
+		return ERR_PTR(-EINVAL);
+
+	if (strncpy_from_user(buf, gi->gi_argv[1], ARG_SIZE) < 0)
+		return ERR_PTR(-EFAULT);
+	buf[ARG_SIZE - 1] = 0;
+
+	if (strcmp(buf, "jindex") == 0)
+		return sdp->sd_jindex;
+	if (strcmp(buf, "rindex") == 0)
+		return sdp->sd_rindex;
+	if (strcmp(buf, "quota") == 0)
+		return sdp->sd_quota_inode;
+
+	return ERR_PTR(-EINVAL);
+}
+
+/**
+ * gi_get_hfile_stat - get stat info on a hidden file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: the number of bytes copied, or -errno
+ */
+
+static int gi_get_hfile_stat(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	struct gfs2_inode *ip;
+	struct gfs2_dinode *di;
+	struct gfs2_holder i_gh;
+	int error;
+
+	ip = gi2hip(sdp, gi);
+	if (IS_ERR(ip))
+		return PTR_ERR(ip);
+
+	if (gi->gi_size != sizeof(struct gfs2_dinode))
+		return -EINVAL;
+
+	di = kmalloc(sizeof(struct gfs2_dinode), GFP_KERNEL);
+	if (!di)
+		return -ENOMEM;
+
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
+	if (error)
+		goto out;
+	memcpy(di, &ip->i_di, sizeof(struct gfs2_dinode));
+	gfs2_glock_dq_uninit(&i_gh);
+
+	if (copy_to_user(gi->gi_data, di, sizeof(struct gfs2_dinode)))
+		error = -EFAULT;
+	else
+		error = sizeof(struct gfs2_dinode);
+
+ out:
+	kfree(di);
+
+	return error;
+}
+
+/**
+ * gi_do_hfile_read - Read data from a hidden file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: the number of bytes read, or -errno
+ */
+
+static int gi_do_hfile_read(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	struct gfs2_inode *ip;
+	struct gfs2_holder i_gh;
+	int error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	ip = gi2hip(sdp, gi);
+	if (IS_ERR(ip))
+		return PTR_ERR(ip);
+
+	if (!S_ISREG(ip->i_di.di_mode))
+		return -EINVAL;
+
+	if (!access_ok(VERIFY_WRITE, gi->gi_data, gi->gi_size))
+		return -EFAULT;
+
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, 0, &i_gh);
+	if (error)
+		return error;
+
+	error = gfs2_jdata_read(ip, gi->gi_data, gi->gi_offset, gi->gi_size,
+				gfs2_copy2user);
+
+	gfs2_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gi_do_hfile_write - Write data to a hidden file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: the number of bytes written, or -errno
+ */
+
+static int gi_do_hfile_write(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	struct gfs2_inode *ip;
+	struct gfs2_alloc *al = NULL;
+	struct gfs2_holder i_gh;
+	unsigned int data_blocks, ind_blocks;
+	int alloc_required;
+	int error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	ip = gi2hip(sdp, gi);
+	if (IS_ERR(ip))
+		return PTR_ERR(ip);
+
+	if (!S_ISREG(ip->i_di.di_mode))
+		return -EINVAL;
+
+	if (!access_ok(VERIFY_READ, gi->gi_data, gi->gi_size))
+		return -EFAULT;
+
+	gfs2_write_calc_reserv(ip, gi->gi_size, &data_blocks, &ind_blocks);
+
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE,
+				   LM_FLAG_PRIORITY, &i_gh);
+	if (error)
+		return error;
+
+	if (!gfs2_is_jdata(ip)) {
+		gfs2_consist_inode(ip);
+		error = -EIO;
+		goto out;
+	}
+
+	error = gfs2_write_alloc_required(ip, gi->gi_offset, gi->gi_size,
+					  &alloc_required);
+	if (error)
+		goto out;
+
+	if (alloc_required) {
+		al = gfs2_alloc_get(ip);
+
+		al->al_requested = data_blocks + ind_blocks;
+
+		error = gfs2_inplace_reserve(ip);
+		if (error)
+			goto out_alloc;
+
+		error = gfs2_trans_begin(sdp,
+					 al->al_rgd->rd_ri.ri_length +
+					 data_blocks + ind_blocks +
+					 RES_DINODE + RES_STATFS, 0);
+		if (error)
+			goto out_relse;
+	} else {
+		error = gfs2_trans_begin(sdp, data_blocks + RES_DINODE, 0);
+		if (error)
+			goto out;
+	}
+
+	error = gfs2_jdata_write(ip, gi->gi_data, gi->gi_offset, gi->gi_size,
+				 gfs2_copy_from_user);
+
+	gfs2_trans_end(sdp);
+
+ out_relse:
+	if (alloc_required)
+		gfs2_inplace_release(ip);
+
+ out_alloc:
+	if (alloc_required)
+		gfs2_alloc_put(ip);
+
+ out:
+	gfs2_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gi_do_hfile_trunc - truncate a hidden file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: the number of bytes copied, or -errno
+ */
+
+static int gi_do_hfile_trunc(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	struct gfs2_inode *ip;
+	struct gfs2_holder i_gh;
+	int error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	ip = gi2hip(sdp, gi);
+	if (IS_ERR(ip))
+		return PTR_ERR(ip);
+
+	if (!S_ISREG(ip->i_di.di_mode))
+		return -EINVAL;
+
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
+	if (error)
+		return error;
+
+	error = gfs2_truncatei(ip, gi->gi_offset, NULL);
+
+	gfs2_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gi_do_quota_sync - sync the outstanding quota changes for a FS
+ * @sdp:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int gi_do_quota_sync(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	return gfs2_quota_sync(sdp);
+}
+
+/**
+ * gi_do_quota_refresh - Refresh the a quota LVB from the quota file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int gi_do_quota_refresh(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	char buf[ARG_SIZE];
+	int user;
+	uint32_t id;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (gi->gi_argc != 2)
+		return -EINVAL;
+
+	if (strncpy_from_user(buf, gi->gi_argv[1], ARG_SIZE) < 0)
+		return -EFAULT;
+	buf[ARG_SIZE - 1] = 0;
+
+	switch (buf[0]) {
+	case 'u':
+		user = 1;
+		break;
+	case 'g':
+		user = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (buf[1] != ':')
+		return -EINVAL;
+
+	if (sscanf(buf + 2, "%u", &id) != 1)
+		return -EINVAL;
+
+	return gfs2_quota_refresh(sdp, user, id);
+}
+
+/**
+ * gi_do_quota_read - read quota values from the quota file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int gi_do_quota_read(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	char buf[ARG_SIZE];
+	int user;
+	uint32_t id;
+	struct gfs2_quota q;
+	int error;
+
+	if (gi->gi_argc != 2)
+		return -EINVAL;
+	if (gi->gi_size != sizeof(struct gfs2_quota))
+		return -EINVAL;
+
+	if (strncpy_from_user(buf, gi->gi_argv[1], ARG_SIZE) < 0)
+		return -EFAULT;
+	buf[ARG_SIZE - 1] = 0;
+
+	switch (buf[0]) {
+	case 'u':
+		user = 1;
+		break;
+	case 'g':
+		user = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (buf[1] != ':')
+		return -EINVAL;
+
+	if (sscanf(buf + 2, "%u", &id) != 1)
+		return -EINVAL;
+
+	error = gfs2_quota_read(sdp, user, id, &q);
+	if (error)
+		return error;
+
+	if (copy_to_user(gi->gi_data, &q, sizeof(struct gfs2_quota)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int gi_resize_add_rgrps(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	if (gi->gi_size % sizeof(struct gfs2_rindex))
+		return -EINVAL;
+
+	return gfs2_resize_add_rgrps(sdp, gi->gi_data, gi->gi_size);
+}
+
+static int gi_rename2system(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	char new_dir[ARG_SIZE], new_name[ARG_SIZE];
+	struct gfs2_inode *old_dip, *ip, *new_dip;
+	int put_new_dip = 0;
+	int error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (gi->gi_argc != 3)
+		return -EINVAL;
+
+	if (strncpy_from_user(new_dir, gi->gi_argv[1], ARG_SIZE) < 0)
+		return -EFAULT;
+	new_dir[ARG_SIZE - 1] = 0;
+	if (strncpy_from_user(new_name, gi->gi_argv[2], ARG_SIZE) < 0)
+		return -EFAULT;
+	new_name[ARG_SIZE - 1] = 0;
+
+	error = gfs2_lookup_simple(sdp->sd_root_dir, ".gfs2_admin", &old_dip);
+	if (error)
+		return error;
+
+	error = -ENOTDIR;
+	if (!S_ISDIR(old_dip->i_di.di_mode))
+		goto out;
+
+	error = gfs2_lookup_simple(old_dip, "new_inode", &ip);
+	if (error)
+		goto out;
+
+	if (!strcmp(new_dir, "per_node")) {
+		error = gfs2_lookup_simple(sdp->sd_master_dir, "per_node",
+					   &new_dip);
+		if (error)
+			goto out2;
+		put_new_dip = 1;
+	} else if (!strcmp(new_dir, "jindex"))
+		new_dip = sdp->sd_jindex;
+	else {
+		error = -EINVAL;
+		goto out2;
+	}
+
+	error = gfs2_rename2system(ip, old_dip, "new_inode", new_dip, new_name);
+
+	if (put_new_dip)
+		gfs2_inode_put(new_dip);
+
+ out2:
+	gfs2_inode_put(ip);
+	
+ out:
+	gfs2_inode_put(old_dip);
+
+	return error;
+}
+
+int gfs2_ioctl_i(struct gfs2_inode *ip, void *arg)
+{
+	struct gfs2_ioctl *gi_user = (struct gfs2_ioctl *)arg;
+	struct gfs2_ioctl gi;
+	char **argv;
+	char arg0[ARG_SIZE];
+	int error = -EFAULT;
+
+	if (copy_from_user(&gi, gi_user, sizeof(struct gfs2_ioctl)))
+		return -EFAULT;
+	if (!gi.gi_argc)
+		return -EINVAL;
+	argv = kcalloc(gi.gi_argc, sizeof(char *), GFP_KERNEL);
+	if (!argv)
+		return -ENOMEM;
+	if (copy_from_user(argv, gi.gi_argv, gi.gi_argc * sizeof(char *)))
+		goto out;
+	gi.gi_argv = argv;
+
+	if (strncpy_from_user(arg0, argv[0], ARG_SIZE) < 0)
+		goto out;
+	arg0[ARG_SIZE - 1] = 0;
+
+	if (strcmp(arg0, "get_super") == 0)
+		error = gi_get_super(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "get_file_stat") == 0)
+		error = gi_get_file_stat(ip, &gi);
+	else if (strcmp(arg0, "set_file_flag") == 0)
+		error = gi_set_file_flag(ip, &gi);
+	else if (strcmp(arg0, "get_bmap") == 0)
+		error = gi_get_bmap(ip, &gi);
+	else if (strcmp(arg0, "get_file_meta") == 0)
+		error = gi_get_file_meta(ip, &gi);
+	else if (strcmp(arg0, "do_file_flush") == 0)
+		error = gi_do_file_flush(ip, &gi);
+	else if (strcmp(arg0, "get_hfile_stat") == 0)
+		error = gi_get_hfile_stat(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_hfile_read") == 0)
+		error = gi_do_hfile_read(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_hfile_write") == 0)
+		error = gi_do_hfile_write(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_hfile_trunc") == 0)
+		error = gi_do_hfile_trunc(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_quota_sync") == 0)
+		error = gi_do_quota_sync(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_quota_refresh") == 0)
+		error = gi_do_quota_refresh(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_quota_read") == 0)
+		error = gi_do_quota_read(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "resize_add_rgrps") == 0)
+		error = gi_resize_add_rgrps(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "rename2system") == 0)
+		error = gi_rename2system(ip->i_sbd, &gi);
+	else
+		error = -ENOTTY;
+
+ out:
+	kfree(argv);
+
+	return error;
+}
+
fs/gfs2/ioctl.h +15 -0
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ */
+
+#ifndef __IOCTL_DOT_H__
+#define __IOCTL_DOT_H__
+
+int gfs2_ioctl_i(struct gfs2_inode *ip, void *arg);
+
+#endif /* __IOCTL_DOT_H__ */
fs/gfs2/sys.c +617 -0

617 lines not on this page · raw message

fs/gfs2/sys.h +24 -0
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ */
+
+#ifndef __SYS_DOT_H__
+#define __SYS_DOT_H__
+
+/* Allow args to be passed to GFS2 when using an initial ram disk */
+extern char *gfs2_sys_margs;
+extern spinlock_t gfs2_sys_margs_lock;
+
+int gfs2_sys_fs_add(struct gfs2_sbd *sdp);
+void gfs2_sys_fs_del(struct gfs2_sbd *sdp);
+
+int gfs2_sys_init(void);
+void gfs2_sys_uninit(void);
+
+#endif /* __SYS_DOT_H__ */
+
fs/gfs2/resize.c +284 -0

284 lines not on this page · raw message

fs/gfs2/resize.h +19 -0
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ */
+
+#ifndef __RESIZE_DOT_H__
+#define __RESIZE_DOT_H__
+
+int gfs2_resize_add_rgrps(struct gfs2_sbd *sdp, char __user *buf,
+			  unsigned int size);
+int gfs2_rename2system(struct gfs2_inode *ip,
+		       struct gfs2_inode *old_dip, char *old_name,
+		       struct gfs2_inode *new_dip, char *new_name);
+
+#endif /* __RESIZE_DOT_H__ */
fs/gfs2/mount.c +211 -0

211 lines not on this page · raw message

fs/gfs2/mount.h +15 -0
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ */
+
+#ifndef __MOUNT_DOT_H__
+#define __MOUNT_DOT_H__
+
+int gfs2_mount_args(struct gfs2_sbd *sdp, char *data_arg, int remount);
+
+#endif /* __MOUNT_DOT_H__ */
#1 lore raw
Quoted 7 lines · David Teigland
fs/gfs2/sys.c +7 -0
+static ssize_t statfs_show(struct gfs2_sbd *sdp, char *buf)
+{
+	struct gfs2_statfs_change sc;
+	int rv;
+
+	if (gfs2_tune_get(sdp, gt_statfs_slow))
+		rv = gfs2_statfs_slow(sdp, &sc);

fs/gfs2/sys.c line 112

Quoted 10 lines
fs/gfs2/sys.c +10 -0
+	else
+		rv = gfs2_statfs_i(sdp, &sc);
+
+	if (rv)
+		goto out;
+
+	rv += sprintf(buf + rv, "bsize %u\n", sdp->sd_sb.sb_bsize);
+	rv += sprintf(buf + rv, "total %lld\n", sc.sc_total);
+	rv += sprintf(buf + rv, "free %lld\n", sc.sc_free);
+	rv += sprintf(buf + rv, "dinodes %lld\n", sc.sc_dinodes);

No, 1 value per sysfs file please.

Quoted 1 line

+/* FIXME: this should go under fs_subsys, /sys/fs/ */

Then put it there, there is a patch floating around that creates /sys/fs/ but I haven't applied it as I need a user for it before I do. Feel free to add that patch to your patch series.

thanks,

greg k-h

#2 lore raw
Quoted 12 lines · Greg KH
Quoted 11 lines · David Teigland
fs/gfs2/sys.c +11 -0
+static ssize_t statfs_show(struct gfs2_sbd *sdp, char *buf)
+{
+	struct gfs2_statfs_change sc;
+	int rv;
+
+	if (gfs2_tune_get(sdp, gt_statfs_slow))
+		rv = gfs2_statfs_slow(sdp, &sc);
+	else
+		rv = gfs2_statfs_i(sdp, &sc);
+
+	if (rv)
Quoted 8 lines

fs/gfs2/sys.c line 112

Quoted 6 lines
fs/gfs2/sys.c +6 -0
+		goto out;
+
+	rv += sprintf(buf + rv, "bsize %u\n", sdp->sd_sb.sb_bsize);
+	rv += sprintf(buf + rv, "total %lld\n", sc.sc_total);
+	rv += sprintf(buf + rv, "free %lld\n", sc.sc_free);
+	rv += sprintf(buf + rv, "dinodes %lld\n", sc.sc_dinodes);

No, 1 value per sysfs file please.

I'm aware of that rule and have followed it everywhere else. This is a special case where the one statfs produces three results.

Quoted 5 lines
Quoted 1 line

+/* FIXME: this should go under fs_subsys, /sys/fs/ */

Then put it there, there is a patch floating around that creates /sys/fs/ but I haven't applied it as I need a user for it before I do. Feel free to add that patch to your patch series.

OK, in the meantime, here it is:
http://marc.theaimsgroup.com/?l=linux-fsdevel&m=112548673418028&w=2

Dave

#3 lore raw
Quoted 22 lines · David Teigland
Quoted 12 lines · Greg KH
Quoted 11 lines · David Teigland
fs/gfs2/sys.c +11 -0
+static ssize_t statfs_show(struct gfs2_sbd *sdp, char *buf)
+{
+	struct gfs2_statfs_change sc;
+	int rv;
+
+	if (gfs2_tune_get(sdp, gt_statfs_slow))
+		rv = gfs2_statfs_slow(sdp, &sc);
+	else
+		rv = gfs2_statfs_i(sdp, &sc);
+
+	if (rv)
Quoted 8 lines

fs/gfs2/sys.c line 112

Quoted 6 lines
fs/gfs2/sys.c +6 -0
+		goto out;
+
+	rv += sprintf(buf + rv, "bsize %u\n", sdp->sd_sb.sb_bsize);
+	rv += sprintf(buf + rv, "total %lld\n", sc.sc_total);
+	rv += sprintf(buf + rv, "free %lld\n", sc.sc_free);
+	rv += sprintf(buf + rv, "dinodes %lld\n", sc.sc_dinodes);

No, 1 value per sysfs file please.

Quoted 2 lines

I'm aware of that rule and have followed it everywhere else. This is a special case where the one statfs produces three results.

Then why not have 4 different files, for the result of the last "statfs" command?

thanks,

greg k-h

#4 lore raw
Quoted 7 lines · Greg KH
Quoted 7 lines
Quoted 6 lines

fs/gfs2/sys.c line 112

Quoted 4 lines
fs/gfs2/sys.c +4 -0
+	rv += sprintf(buf + rv, "bsize %u\n", sdp->sd_sb.sb_bsize);
+	rv += sprintf(buf + rv, "total %lld\n", sc.sc_total);
+	rv += sprintf(buf + rv, "free %lld\n", sc.sc_free);
+	rv += sprintf(buf + rv, "dinodes %lld\n", sc.sc_dinodes);

No, 1 value per sysfs file please.

Quoted 5 lines
Quoted 2 lines

I'm aware of that rule and have followed it everywhere else. This is a special case where the one statfs produces three results.

Then why not have 4 different files, for the result of the last "statfs" command?

More to the point, what the hell is that doing in sysfs in the first place?

#6 lore raw
In reply to David Teigland
Quoted 2 lines · David Teigland

There are a variety of mount options, tunable parameters, internal statistics, and methods of online file system manipulation.

Could you explain WTF are you doing with rename here? This pile of ioctls is every bit as bad as sys_reiser4(); kindly provide a detailed description of the API you've introduced and explain why nothing saner would do...

#5 lore raw
In reply to Al Viro
Quoted 8 lines · Al Viro
Quoted 7 lines · Greg KH
Quoted 7 lines
Quoted 6 lines

fs/gfs2/sys.c line 112

Quoted 4 lines
fs/gfs2/sys.c +4 -0
+	rv += sprintf(buf + rv, "bsize %u\n", sdp->sd_sb.sb_bsize);
+	rv += sprintf(buf + rv, "total %lld\n", sc.sc_total);
+	rv += sprintf(buf + rv, "free %lld\n", sc.sc_free);
+	rv += sprintf(buf + rv, "dinodes %lld\n", sc.sc_dinodes);

No, 1 value per sysfs file please.

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

I'm aware of that rule and have followed it everywhere else. This is a special case where the one statfs produces three results.

Then why not have 4 different files, for the result of the last "statfs" command?

More to the point, what the hell is that doing in sysfs in the first place?

It gave extended, gfs-specific usage information not available through df. It's not valuable enough to keep around for the confusion and questions it's raised so I've removed it.

Dave

#7 lore raw
In reply to Al Viro
Quoted 8 lines · Al Viro
Quoted 2 lines · David Teigland

There are a variety of mount options, tunable parameters, internal statistics, and methods of online file system manipulation.

Could you explain WTF are you doing with rename here? This pile of ioctls is every bit as bad as sys_reiser4(); kindly provide a detailed description of the API you've introduced and explain why nothing saner would do...

First some background that I've copied from elsewhere: The superblock contains a pointer to a "master" directory that contains various system inodes. The inodes in the master directory are:

1) A directory named "jindex" containing all the journal files. The journals are named "journal0", "journal1", ..., "journalX"

2) A directory named "per_node" that contains a bunch of files where each node can store data specific to that node. Each node has files "inum_rangeX", "statfs_changeX", "unlinked_tagX", and "quota_changeX". So, there are a set of these four files for each journal in the jindex directory.

3) A file named "inum" that contains the next cluster-wide inode number.

4) A file named "statfs" that contains the cluster-wide statfs information.

5) A file named "rindex" that contains the locations of all the RGs in the filesystem. (RG's == resource groups == allocation groups)

6) A file named "quota" that contains the quota values (UID and GID) for the filesystem.

7) A directory named "root" that is the root directory of the user-visible filesystem.

The ioctls "hfile_stat", "hfile_read", "hfile_write", "hfile_trunc" are used to operate on the hidden system files. I notice we're not using trunc, so it can be removed. stat/read/write could be replaced with a few specific ioctl's if that's preferred.

The next issue is adding journals (and the associated system files) to a fs. The gfs2_jadd command does this with the fs online. If you created the fs with 8 journals and you now want 12 machines to mount it at once, you need to add 4 journals by running "gfs2_jadd -j 4 /path/to/fs".

Say gfs2_jadd is adding a 9th journal (id 8) ...

creates ordinary file /.gfs2_admin/new_inode writes to new_inode initializing it as an inum_range file moves .gfs2_admin/new_inode to per_node/inum_range8

creates ordinary file /.gfs2_admin/new_inode writes to new_inode initializing it as a statfs_change file moves .gfs2_admin/new_inode to per_node/statfs_change8

same for unlinked_tag8 and quota_change8

creates ordinary file /.gfs2_admin/new_inode writes to new_inode initializing it as a journal file moves .gfs2_admin/new_inode to jindex/journal8

(keeping in mind that the "per_node" and "jindex" dirs and the files
under them are in the hidden/system portion of the fs)

The create and write steps use ordinary system calls. The "move" step uses the "rename2system" ioctl to move .gfs2_admin/new_inode to the specified system file. The new files are synced before being renamed so in case of a crash only correctly formed files are found in the hidden dirs. Only when the final journal file is moved into place is the fs ready to accept a new mounter.

Next is exapanding the size of the fs. To do this, gfs2_grow first opens the device and initializes the new space with RG headers. Second, it uses the "resize_add_rgrps" ioctl to add new structures defining the space to the "rindex" system file. I'm looking into using hfile_write for this.

Other ioctls:
get_super - copy struct gfs2_sb to user space
get_file_stat - copy struct gfs2_dinode to user space for given file set_file_flag - set gfs-specific flag in inode
get_bmap - map file block to disk block
get_file_meta - return all the metadata for a file or dir do_file_flush - sync out all dirty data and drop the cache and lock do_quota_sync - sync outstanding quota change (moving to sysfs) do_quota_refresh - refresh quota lvb from the quota file (moving to sysfs) do_quota_read - read quota values from quota file

Some of these we could do without if they're objectionable. Regardless, we'll take a closer look to see if any don't qualify as useful enough.

Finally, how ioctl is implemented. All the commands above are multiplexed through one actual ioctl (GFS2_IOCTL_SUPER) that passes in:

struct gfs2_ioctl {
        unsigned int gi_argc;
        char **gi_argv;

        char __user *gi_data;
        unsigned int gi_size;
        uint64_t gi_offset;
};

- argv[0] is the command string, e.g. "set_file_flag", "rename2system",
- argv[x] are other string arguments for the command, e.g. for set_file_flag argv[1] is either "set" or "clear". For rename2system argv[1] is the destination directory and argv[2] is the new name.
- gi_data, gi_size, gi_offset - data returned to caller when needed

This could be exchanged, of course, for the more tradition ioctl mess if that's any saner.

Dave

#8 lore raw
Quoted 34 lines · David Teigland
Quoted 8 lines · Al Viro
Quoted 2 lines · David Teigland

There are a variety of mount options, tunable parameters, internal statistics, and methods of online file system manipulation.

Could you explain WTF are you doing with rename here? This pile of ioctls is every bit as bad as sys_reiser4(); kindly provide a detailed description of the API you've introduced and explain why nothing saner would do...

First some background that I've copied from elsewhere: The superblock contains a pointer to a "master" directory that contains various system inodes. The inodes in the master directory are:

1) A directory named "jindex" containing all the journal files. The journals are named "journal0", "journal1", ..., "journalX"

2) A directory named "per_node" that contains a bunch of files where each node can store data specific to that node. Each node has files "inum_rangeX", "statfs_changeX", "unlinked_tagX", and "quota_changeX". So, there are a set of these four files for each journal in the jindex directory.

3) A file named "inum" that contains the next cluster-wide inode number.

4) A file named "statfs" that contains the cluster-wide statfs information.

5) A file named "rindex" that contains the locations of all the RGs in the filesystem. (RG's == resource groups == allocation groups)

6) A file named "quota" that contains the quota values (UID and GID) for the filesystem.

Quoted 7 lines

7) A directory named "root" that is the root directory of the user-visible filesystem.

The ioctls "hfile_stat", "hfile_read", "hfile_write", "hfile_trunc" are used to operate on the hidden system files. I notice we're not using trunc, so it can be removed. stat/read/write could be replaced with a few specific ioctl's if that's preferred.

They are normal directories and normal files, except they are not exposed in the mount-point, right? Then why don't you simply provide a directory handle for the master directory and use normal filesystem operations for the rest?

That way you would have just one ioctl -- getmasterdir. The tool would fchdir to the handle returned and manipulate the files from there with normal syscalls. It would still see to the user-visible part throught the root directory too (since bind mounts are supported, this should not be a problem).

Or you could do even without ioctls. Just expose the files via /proc

Quoted 24 lines

The next issue is adding journals (and the associated system files) to a fs. The gfs2_jadd command does this with the fs online. If you created the fs with 8 journals and you now want 12 machines to mount it at once, you need to add 4 journals by running "gfs2_jadd -j 4 /path/to/fs".

Say gfs2_jadd is adding a 9th journal (id 8) ...

creates ordinary file /.gfs2_admin/new_inode writes to new_inode initializing it as an inum_range file moves .gfs2_admin/new_inode to per_node/inum_range8

creates ordinary file /.gfs2_admin/new_inode writes to new_inode initializing it as a statfs_change file moves .gfs2_admin/new_inode to per_node/statfs_change8

same for unlinked_tag8 and quota_change8

creates ordinary file /.gfs2_admin/new_inode writes to new_inode initializing it as a journal file moves .gfs2_admin/new_inode to jindex/journal8

(keeping in mind that the "per_node" and "jindex" dirs and the files
under them are in the hidden/system portion of the fs)

Quoted 6 lines

The create and write steps use ordinary system calls. The "move" step uses the "rename2system" ioctl to move .gfs2_admin/new_inode to the specified system file. The new files are synced before being renamed so in case of a crash only correctly formed files are found in the hidden dirs. Only when the final journal file is moved into place is the fs ready to accept a new mounter.

And with directory handle, you would just chdir there and do:
rename("root/.gfs2_admin/new_inode", "jindex/journal8")

Quoted 4 lines

Next is exapanding the size of the fs. To do this, gfs2_grow first opens the device and initializes the new space with RG headers. Second, it uses the "resize_add_rgrps" ioctl to add new structures defining the space to the "rindex" system file. I'm looking into using hfile_write for this.

Ok, if it can't be done with write, it probably needs something like ioctl. Though it could be an ioctl on that file, not on the device...

Quoted 11 lines

Other ioctls:
get_super - copy struct gfs2_sb to user space
get_file_stat - copy struct gfs2_dinode to user space for given file set_file_flag - set gfs-specific flag in inode
get_bmap - map file block to disk block
get_file_meta - return all the metadata for a file or dir do_file_flush - sync out all dirty data and drop the cache and lock do_quota_sync - sync outstanding quota change (moving to sysfs) do_quota_refresh - refresh quota lvb from the quota file (moving to sysfs) do_quota_read - read quota values from quota file

Quoted 2 lines

Some of these we could do without if they're objectionable. Regardless, we'll take a closer look to see if any don't qualify as useful enough.

Some of them would be better off as procfs or sysfs entries.

IIRC get_bmap exists elsewhere too, so that should be ok. And get_file_meta probably won't do without ioctl either.

Wouldn't the get_file_stat be included in get_file_meta?

Quoted 12 lines

Finally, how ioctl is implemented. All the commands above are multiplexed through one actual ioctl (GFS2_IOCTL_SUPER) that passes in:

struct gfs2_ioctl {
        unsigned int gi_argc;
        char **gi_argv;

        char __user *gi_data;
        unsigned int gi_size;
        uint64_t gi_offset;
};
Quoted 8 lines

- argv[0] is the command string, e.g. "set_file_flag", "rename2system",
- argv[x] are other string arguments for the command, e.g. for set_file_flag argv[1] is either "set" or "clear". For rename2system argv[1] is the destination directory and argv[2] is the new name.
- gi_data, gi_size, gi_offset - data returned to caller when needed

This could be exchanged, of course, for the more tradition ioctl mess if that's any saner.

Well, if you get rid of the access to files in the master directory by making that directory visible somehow, you will be left with a bunch of ioctls on files, which are different enough to warrant individual ioctl numbers for sake of efficiency.

--
Jan 'Bulb' Hudec <[email protected]>

#9 lore raw
Quoted 10 lines · Jan Hudec

They are normal directories and normal files, except they are not exposed in the mount-point, right? Then why don't you simply provide a directory handle for the master directory and use normal filesystem operations for the rest?

That way you would have just one ioctl -- getmasterdir. The tool would fchdir to the handle returned and manipulate the files from there with normal syscalls. It would still see to the user-visible part throught the root directory too (since bind mounts are supported, this should not be a problem).

That sounds nice, we'll give it a try.

Quoted 4 lines

Well, if you get rid of the access to files in the master directory by making that directory visible somehow, you will be left with a bunch of ioctls on files, which are different enough to warrant individual ioctl numbers for sake of efficiency.

Sure, that may well be saner when we get the ioctl command set further reduced.

Thanks,
Dave