Skip to contentSkip to conversation

xgetdents system call

RFC PATCH In review Abhi Das 5 participants 14 messages Jul 25, 2014 – Jul 30, 2014

Subsystems (5)
x86 VFS ABI/API GFS2 file system +1 more subsystems
lore
Share & tools
lore

Abhi Das

author
#0 lore raw

cover xgetdents system call

This system call takes 5 arguments:
fd - file descriptor of the directory
flags - flags used by xstat
mask - field mask used to request stat/xattr info
buf - user buf to return collected info
count - size of user buffer in bytes

xgetdents() should read the directory entries, collect stat and xattr information for each entry as requested and return all the data back to the user in a container structure (linux_xdirent) as part of the supplied user buffer.

Abhi Das (5):
fs: xstat system call VFS bits
fs: Add xgetdents system call and xreaddir file operation
gfs2: Add a dynamic buffer backed by a vector of pages
gfs2: Add sort functionality with extra parameter
gfs2: Add xreaddir file operation and supporting functions

 arch/x86/syscalls/syscall_32.tbl |    3 +
 arch/x86/syscalls/syscall_64.tbl |    3 +
 fs/gfs2/Makefile                 |    3 +-
 fs/gfs2/dir.c                    |   80 +--
 fs/gfs2/dir.h                    |   13 +-
 fs/gfs2/export.c                 |    2 +-
 fs/gfs2/file.c                   |   17 +-
 fs/gfs2/incore.h                 |    6 +
 fs/gfs2/inode.c                  |    3 +-
 fs/gfs2/inode.h                  |    5 +
 fs/gfs2/ops_fstype.c             |    4 +
 fs/gfs2/sys.c                    |   26 +-
 fs/gfs2/util.c                   |  363 ++++++++++++++
 fs/gfs2/util.h                   |   47 ++
 fs/gfs2/xattr.c                  |   27 +-
 fs/gfs2/xattr.h                  |   23 +
 fs/gfs2/xreaddir.c               | 1024 ++++++++++++++++++++++++++++++++++++++
 fs/gfs2/xreaddir.h               |   84 ++++
 fs/readdir.c                     |   42 ++
 fs/stat.c                        |  340 ++++++++++++-
 include/linux/fs.h               |    5 +
 include/linux/stat.h             |   16 +-
 include/linux/syscalls.h         |    5 +
 include/uapi/linux/fcntl.h       |    1 +
 include/uapi/linux/stat.h        |  143 ++++++
 25 files changed, 2196 insertions(+), 89 deletions(-)
 create mode 100644 fs/gfs2/xreaddir.c
 create mode 100644 fs/gfs2/xreaddir.h
Signature · 1 line
1.8.1.4
author
#1 lore raw

RFC PATCH 1/5 fs: xstat system call VFS bits

This patch adds the VFS bits of the xstat patchset by David Howells. The xgetdents syscall uses this to obtain stat information for directory entries.

---
 arch/x86/syscalls/syscall_32.tbl |   2 +
 arch/x86/syscalls/syscall_64.tbl |   2 +
 fs/stat.c                        | 338 ++++++++++++++++++++++++++++++++++++---
 include/linux/fs.h               |   4 +
 include/linux/stat.h             |  14 +-
 include/linux/syscalls.h         |   5 +
 include/uapi/linux/fcntl.h       |   1 +
 include/uapi/linux/stat.h        | 110 +++++++++++++
 8 files changed, 453 insertions(+), 23 deletions(-)
arch/x86/syscalls/syscall_32.tbl +2 -0
index d6b8679..6d6ca37 100644
@@ -360,3 +360,5 @@
 351	i386	sched_setattr		sys_sched_setattr
 352	i386	sched_getattr		sys_sched_getattr
 353	i386	renameat2		sys_renameat2
+354	i386	xstat			sys_xstat
+355	i386	fxstat			sys_fxstat
arch/x86/syscalls/syscall_64.tbl +2 -0
index ec255a1..1308ee3 100644
@@ -323,6 +323,8 @@
 314	common	sched_setattr		sys_sched_setattr
 315	common	sched_getattr		sys_sched_getattr
 316	common	renameat2		sys_renameat2
+317	common 	xstat			sys_xstat
+318	common 	fxstat			sys_fxstat
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
fs/stat.c +316 -22

430 lines not on this page · raw message

include/linux/fs.h +4 -0
index 338e6f7..b91f235 100644
@@ -1207,6 +1207,7 @@ struct super_block {
 
 	char s_id[32];				/* Informational name */
 	u8 s_uuid[16];				/* UUID */
+	unsigned char           s_volume_id[16]; /* Volume identifier */
 
 	void 			*s_fs_info;	/* Filesystem private info */
 	unsigned int		s_max_links;
@@ -2519,6 +2520,7 @@ extern int generic_readlink(struct dentry *, char __user *, int);
 extern void generic_fillattr(struct inode *, struct kstat *);
 int vfs_getattr_nosec(struct path *path, struct kstat *stat);
 extern int vfs_getattr(struct path *, struct kstat *);
+extern int vfs_xgetattr(struct path *, struct kstat *);
 void __inode_add_bytes(struct inode *inode, loff_t bytes);
 void inode_add_bytes(struct inode *inode, loff_t bytes);
 void __inode_sub_bytes(struct inode *inode, loff_t bytes);
@@ -2533,6 +2535,8 @@ extern int vfs_stat(const char __user *, struct kstat *);
 extern int vfs_lstat(const char __user *, struct kstat *);
 extern int vfs_fstat(unsigned int, struct kstat *);
 extern int vfs_fstatat(int , const char __user *, struct kstat *, int);
+extern int vfs_xstat(int, const char __user *, int, struct kstat *);
+extern int vfs_fxstat(unsigned int, struct kstat *);
 
 extern int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
 		    unsigned long arg);
include/linux/stat.h +13 -1
index 075cb0c..552e047 100644
@@ -2,6 +2,7 @@
 #define _LINUX_STAT_H
 
 
+#include <linux/types.h>
 #include <asm/stat.h>
 #include <uapi/linux/stat.h>
 
@@ -19,6 +20,12 @@
 #include <linux/uidgid.h>
 
 struct kstat {
+	u32             query_flags;                /* operational flags */
+#define KSTAT_QUERY_FLAGS (AT_FORCE_ATTR_SYNC)
+	u32             request_mask;               /* what fields the user asked for */
+	u32             result_mask;                /* what fields the user got */
+	u32             information;
+	u32             ioc_flags;                  /* inode flags (FS_IOC_GETFLAGS) */
 	u64		ino;
 	dev_t		dev;
 	umode_t		mode;
@@ -26,12 +33,17 @@ struct kstat {
 	kuid_t		uid;
 	kgid_t		gid;
 	dev_t		rdev;
+	unsigned int    tv_granularity;             /* granularity of times (in nS) */
 	loff_t		size;
-	struct timespec  atime;
+	struct timespec atime;
 	struct timespec	mtime;
 	struct timespec	ctime;
+	struct timespec btime;                      /* file creation time */
 	unsigned long	blksize;
 	unsigned long long	blocks;
+	u64             gen;                        /* inode generation */
+	u64             version;                    /* data version */
+	unsigned char   volume_id[16];              /* volume identifier */
 };
 
 #endif
include/linux/syscalls.h +5 -0
index b0881a0..cf85e40 100644
@@ -866,4 +866,9 @@ asmlinkage long sys_process_vm_writev(pid_t pid,
 asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type,
 			 unsigned long idx1, unsigned long idx2);
 asmlinkage long sys_finit_module(int fd, const char __user *uargs, int flags);
+
+asmlinkage long sys_xstat(int dfd, const char __user *path, unsigned flags,
+			  unsigned mask, struct xstat __user *buffer);
+asmlinkage long sys_fxstat(unsigned fd, unsigned flags,
+			    unsigned mask, struct xstat __user *buffer);
 #endif
include/uapi/linux/fcntl.h +1 -0
index 074b886..450b310 100644
@@ -47,6 +47,7 @@
 #define AT_SYMLINK_FOLLOW	0x400   /* Follow symbolic links.  */
 #define AT_NO_AUTOMOUNT		0x800	/* Suppress terminal automount traversal */
 #define AT_EMPTY_PATH		0x1000	/* Allow empty relative pathname */
+#define AT_FORCE_ATTR_SYNC      0x2000  /* Force the attributes to be sync'd with the server */
 
 
 #endif /* _UAPI_LINUX_FCNTL_H */
include/uapi/linux/stat.h +110 -0

115 lines not on this page · raw message

Signature · 1 line
1.8.1.4
#2 lore raw

Re: [Cluster-devel] [RFC PATCH 1/5] fs: xstat system call VFS bits

----- Original Message -----

Quoted 16 lines

This patch adds the VFS bits of the xstat patchset by David Howells. The xgetdents syscall uses this to obtain stat information for directory entries.

---
 arch/x86/syscalls/syscall_32.tbl |   2 +
 arch/x86/syscalls/syscall_64.tbl |   2 +
 fs/stat.c                        | 338
 ++++++++++++++++++++++++++++++++++++---
 include/linux/fs.h               |   4 +
 include/linux/stat.h             |  14 +-
 include/linux/syscalls.h         |   5 +
 include/uapi/linux/fcntl.h       |   1 +
 include/uapi/linux/stat.h        | 110 +++++++++++++
 8 files changed, 453 insertions(+), 23 deletions(-)

(snip)

fs/stat.c SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename, · line 632

Quoted 4 lines
fs/stat.c +4 -0
+static int xstat_get_params(unsigned int mask, struct xstat __user *buffer,
+			    struct kstat *stat)
+{
+	memset(stat, 0xde, sizeof(*stat)); // DEBUGGING

Did you forget to remove this DEBUGGING statement? In either case, the c++ comment should be /* */.

Regards,

Bob Peterson
Red Hat File Systems

author
#3 lore raw

RFC PATCH 2/5 fs: Add xgetdents system call and xreaddir file operation

In reply to Abhi Das

Also add linux_xdirent structure that will be the container for dirent, stat and xattr info.

---
 arch/x86/syscalls/syscall_32.tbl |  1 +
 arch/x86/syscalls/syscall_64.tbl |  1 +
 fs/readdir.c                     | 42 ++++++++++++++++++++++++++++++++++++++++
 fs/stat.c                        |  4 +++-
 include/linux/fs.h               |  1 +
 include/linux/stat.h             |  2 ++
 include/uapi/linux/stat.h        | 33 +++++++++++++++++++++++++++++++
 7 files changed, 83 insertions(+), 1 deletion(-)
arch/x86/syscalls/syscall_32.tbl +1 -0
index 6d6ca37..35723e3 100644
@@ -362,3 +362,4 @@
 353	i386	renameat2		sys_renameat2
 354	i386	xstat			sys_xstat
 355	i386	fxstat			sys_fxstat
+356	i386	xgetdents		sys_xgetdents
arch/x86/syscalls/syscall_64.tbl +1 -0
index 1308ee3..566aab1 100644
@@ -325,6 +325,7 @@
 316	common	renameat2		sys_renameat2
 317	common 	xstat			sys_xstat
 318	common 	fxstat			sys_fxstat
+319	common	xgetdents		sys_xgetdents
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
fs/readdir.c +42 -0

48 lines not on this page · raw message

fs/stat.c +3 -1
index 1fd0b3e..db45f8b 100644
@@ -651,7 +651,7 @@ static int xstat_get_params(unsigned int mask, struct xstat __user *buffer,
  * Otherwise we copy the extended stats to userspace and return the amount of
  * data written into the buffer (or -EFAULT).
  */
-static long xstat_set_result(struct kstat *stat, struct xstat __user *buffer)
+long xstat_set_result(struct kstat *stat, struct xstat __user *buffer)
 {
 	u32 mask = stat->result_mask, gran = stat->tv_granularity;
 
@@ -701,6 +701,8 @@ static long xstat_set_result(struct kstat *stat, struct xstat __user *buffer)
 	return 0;
 }
 
+EXPORT_SYMBOL(xstat_set_result);
+
 /*
  * System call to get extended stats by path
  */
include/linux/fs.h +1 -0
index b91f235..79c7d39 100644
@@ -1464,6 +1464,7 @@ struct file_operations {
 	ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
 	ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
 	int (*iterate) (struct file *, struct dir_context *);
+	size_t (*xreaddir) (struct file *, unsigned int, unsigned int, void __user *, size_t);
 	unsigned int (*poll) (struct file *, struct poll_table_struct *);
 	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
 	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
include/linux/stat.h +2 -0
index 552e047..75be415 100644
@@ -46,4 +46,6 @@ struct kstat {
 	unsigned char   volume_id[16];              /* volume identifier */
 };
 
+long xstat_set_result(struct kstat *stat, struct xstat __user *buffer);
+
 #endif
include/uapi/linux/stat.h +33 -0
index 2907352..d7ea6c5 100644
@@ -90,6 +90,14 @@
 #define XSTAT_VOLUME_ID        0x00008000U     /* want/got st_volume_id */
 #define XSTAT_ALL_STATS        0x0000ffffU     /* all supported stats */
 
+/* xattr request flags */
+#define XSTAT_XATTR_USER       0x00010000U     /* user.* xattrs */
+#define XSTAT_XATTR_SYSTEM     0x00020000U     /* system.* xattrs */
+#define XSTAT_XATTR_SECURITY   0x00040000U     /* security.* xattrs */
+#define XSTAT_XATTR_POSIX_ACL  0x00080000U     /* posix acl xattrs */
+#define XSTAT_XATTR_ALL        0x00ff0000U     /* all xattrs */
+#define XSTAT_XATTR_VALUES     0x01000000U     /* retrieve values along with keys */
+
 /*
  * Extended stat structures
  */
@@ -152,4 +160,29 @@ struct xstat {
 #define XSTAT_INFO_SYSTEM              0x00001000U /* File is marked system (DOS+) */
 #define XSTAT_INFO_ARCHIVE             0x00002000U /* File is marked archive (DOS+) */
 
+struct xdirent_xattr {
+	size_t       xa_value_len;   /* length of value field */
+	char         xa_name_val[1]; /* name/value pair, name is NULL terminated and 
+				      * value is xa_value_len in length */
+};
+
+/*
+ * xb_blob: first contains NULL terminated name, followed by struct xdirent_xattr
+ * objects packed together.
+ */
+struct xdirent_blob {
+	unsigned int    xb_xattr_count;
+	char            xb_blob[1]; /* contains variable length data like
+				     * NULL-terminated name, xattrs etc */
+};
+
+struct linux_xdirent {
+	unsigned long        xd_ino;
+	char                 xd_type;
+	unsigned long        xd_off;
+	struct xstat         xd_stat;
+	unsigned long        xd_reclen;
+	struct xdirent_blob  xd_blob;
+};
+
 #endif /* _UAPI_LINUX_STAT_H */
Signature · 1 line
1.8.1.4
#4 lore raw

Re: [RFC PATCH 2/5] fs: Add xgetdents system call and xreaddir file operation

[CC += linux-api]

Quoted 190 lines · Abhi Das

Also add linux_xdirent structure that will be the container for dirent, stat and xattr info.

---
 arch/x86/syscalls/syscall_32.tbl |  1 +
 arch/x86/syscalls/syscall_64.tbl |  1 +
 fs/readdir.c                     | 42 ++++++++++++++++++++++++++++++++++++++++
 fs/stat.c                        |  4 +++-
 include/linux/fs.h               |  1 +
 include/linux/stat.h             |  2 ++
 include/uapi/linux/stat.h        | 33 +++++++++++++++++++++++++++++++
 7 files changed, 83 insertions(+), 1 deletion(-)
arch/x86/syscalls/syscall_32.tbl +1 -0
index 6d6ca37..35723e3 100644
@@ -362,3 +362,4 @@
 353    i386    renameat2               sys_renameat2
 354    i386    xstat                   sys_xstat
 355    i386    fxstat                  sys_fxstat
+356    i386    xgetdents               sys_xgetdents
arch/x86/syscalls/syscall_64.tbl +1 -0
index 1308ee3..566aab1 100644
@@ -325,6 +325,7 @@
 316    common  renameat2               sys_renameat2
 317    common  xstat                   sys_xstat
 318    common  fxstat                  sys_fxstat
+319    common  xgetdents               sys_xgetdents
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
fs/readdir.c +42 -0
index 33fd922..d676088 100644
@@ -224,6 +224,48 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd,
        return error;
 }
 
+SYSCALL_DEFINE5(xgetdents, unsigned int, fd, unsigned, flags, unsigned int, mask,
+               void __user *, buf, unsigned int, count)
+{
+       struct fd f;
+       struct inode *inode;
+       int error = -ENOTDIR;
+
+       if (!count)
+               return -EINVAL;
+
+       if (!access_ok(VERIFY_WRITE, buf, count))
+               return -EFAULT;
+
+       f = fdget(fd);
+       if (!f.file)
+               return -EBADF;
+
+       inode = f.file->f_path.dentry->d_inode;
+
+       error = -ENOTSUPP;
+       if (!f.file->f_op || !f.file->f_op->xreaddir)
+               goto out;
+
+       error = security_file_permission(f.file, MAY_READ);
+       if (error)
+               goto out;
+
+       error = mutex_lock_killable(&inode->i_mutex);
+       if (error)
+               goto out;
+
+       error = -ENOENT;
+       if (!IS_DEADDIR(inode)) {
+               error = f.file->f_op->xreaddir(f.file, flags, mask, buf, count);
+               file_accessed(f.file);
+       }
+       mutex_unlock(&inode->i_mutex);
+out:
+       fdput(f);
+       return error;
+}
+
 struct getdents_callback64 {
        struct dir_context ctx;
        struct linux_dirent64 __user * current_dir;
fs/stat.c +3 -1
index 1fd0b3e..db45f8b 100644
@@ -651,7 +651,7 @@ static int xstat_get_params(unsigned int mask, struct xstat __user *buffer,
  * Otherwise we copy the extended stats to userspace and return the amount of
  * data written into the buffer (or -EFAULT).
  */
-static long xstat_set_result(struct kstat *stat, struct xstat __user *buffer)
+long xstat_set_result(struct kstat *stat, struct xstat __user *buffer)
 {
        u32 mask = stat->result_mask, gran = stat->tv_granularity;
 
@@ -701,6 +701,8 @@ static long xstat_set_result(struct kstat *stat, struct xstat __user *buffer)
        return 0;
 }
 
+EXPORT_SYMBOL(xstat_set_result);
+
 /*
  * System call to get extended stats by path
  */
include/linux/fs.h +1 -0
index b91f235..79c7d39 100644
@@ -1464,6 +1464,7 @@ struct file_operations {
        ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
        ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
        int (*iterate) (struct file *, struct dir_context *);
+       size_t (*xreaddir) (struct file *, unsigned int, unsigned int, void __user *, size_t);
        unsigned int (*poll) (struct file *, struct poll_table_struct *);
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
include/linux/stat.h +2 -0
index 552e047..75be415 100644
@@ -46,4 +46,6 @@ struct kstat {
        unsigned char   volume_id[16];              /* volume identifier */
 };
 
+long xstat_set_result(struct kstat *stat, struct xstat __user *buffer);
+
 #endif
include/uapi/linux/stat.h +33 -1
index 2907352..d7ea6c5 100644
@@ -90,6 +90,14 @@
 #define XSTAT_VOLUME_ID        0x00008000U     /* want/got st_volume_id */
 #define XSTAT_ALL_STATS        0x0000ffffU     /* all supported stats */
 
+/* xattr request flags */
+#define XSTAT_XATTR_USER       0x00010000U     /* user.* xattrs */
+#define XSTAT_XATTR_SYSTEM     0x00020000U     /* system.* xattrs */
+#define XSTAT_XATTR_SECURITY   0x00040000U     /* security.* xattrs */
+#define XSTAT_XATTR_POSIX_ACL  0x00080000U     /* posix acl xattrs */
+#define XSTAT_XATTR_ALL        0x00ff0000U     /* all xattrs */
+#define XSTAT_XATTR_VALUES     0x01000000U     /* retrieve values along with keys */
+
 /*
  * Extended stat structures
  */
@@ -152,4 +160,29 @@ struct xstat {
 #define XSTAT_INFO_SYSTEM              0x00001000U /* File is marked system (DOS+) */
 #define XSTAT_INFO_ARCHIVE             0x00002000U /* File is marked archive (DOS+) */
 
+struct xdirent_xattr {
+       size_t       xa_value_len;   /* length of value field */
+       char         xa_name_val[1]; /* name/value pair, name is NULL terminated and
+                                     * value is xa_value_len in length */
+};
+
+/*
+ * xb_blob: first contains NULL terminated name, followed by struct xdirent_xattr
+ * objects packed together.
+ */
+struct xdirent_blob {
+       unsigned int    xb_xattr_count;
+       char            xb_blob[1]; /* contains variable length data like
+                                    * NULL-terminated name, xattrs etc */
+};
+
+struct linux_xdirent {
+       unsigned long        xd_ino;
+       char                 xd_type;
+       unsigned long        xd_off;
+       struct xstat         xd_stat;
+       unsigned long        xd_reclen;
+       struct xdirent_blob  xd_blob;
+};
+
 #endif /* _UAPI_LINUX_STAT_H */
--

1.8.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Signature · 3 lines
Michael Kerrisk Linux man-pages maintainer;
http://www.kernel.org/doc/man-pages/
Author of "The Linux Programming Interface", http://blog.man7.org/
author
#5 lore raw

RFC PATCH 3/5 gfs2: Add a dynamic buffer backed by a vector of pages

In reply to Abhi Das

This patch adds a new buffer called 'vbuf' that is backed by a vector of pages. It is dynamic and can be expanded as needed with low overhead.

---
 fs/gfs2/util.c | 299 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/gfs2/util.h |  43 +++++++++
 2 files changed, 342 insertions(+)
fs/gfs2/util.c +299 -0
index 86d2035..7345489 100644
@@ -263,3 +263,302 @@ int gfs2_io_error_bh_i(struct gfs2_sbd *sdp, struct buffer_head *bh,
 	return rv;
 }
 
+/*
+ * Fast vector-of-pages backed buffer
+ */
+
+static int vp_alloc_pages(struct vp_ctx *vpx, int start, int end)
+{
+	int i;
+
+	for (i = start; i < end; i++) {
+		vpx->vp_pages[i] = alloc_page(GFP_KERNEL | GFP_NOFS);
+		if (vpx->vp_pages[i] == NULL)
+			goto free;
+	}
+	return 0;
+free:
+	for (i = start; i < end; i++)
+		if (vpx->vp_pages[i]) {
+			__free_page(vpx->vp_pages[i]);
+			vpx->vp_pages[i] = NULL;
+		}
+	return -ENOMEM;
+}
+
+static void vp_free_pages(struct vp_ctx *vpx)
+{
+	int i;
+
+	for (i = 0; i < vpx->vp_size; i++)
+		if (vpx->vp_pages[i]) {
+			__free_page(vpx->vp_pages[i]);
+			vpx->vp_pages[i] = NULL;
+		}
+}
+
+static int vp_extend(struct vp_ctx *vpx, int size)
+{
+	struct gfs2_sbd *sdp = vpx->vp_sdp;
+
+	/* first make room for more pointers */
+	if (size <= 0)
+		return -EINVAL;
+
+	vpx->vp_pages = krealloc(vpx->vp_pages,
+				 sizeof(struct page *) * (vpx->vp_size + size),
+				 GFP_KERNEL);
+	if (vpx->vp_pages == NULL)
+		goto out;
+
+	/* Zero out the new pointers and allocate pages*/
+	memset(&vpx->vp_pages[vpx->vp_size], 0, sizeof(struct page *) * size);
+	if (vp_alloc_pages(vpx, vpx->vp_size, vpx->vp_size + size))
+		goto out;
+
+	vpx->vp_size += size;
+	return 0;
+out:
+	return -ENOMEM;
+}
+
+int vp_init(struct gfs2_sbd *sdp, struct vbuf *vb, int init_cap)
+{
+	int cap, err = -ENOMEM;
+	struct vp_ctx *vpx;
+
+	cap = DIV_ROUND_UP(init_cap, PAGE_SIZE);
+
+	vpx = kmalloc(sizeof(struct vp_ctx), GFP_KERNEL);
+	if (vpx == NULL)
+		goto out;
+
+	vpx->vp_magic = VP_MAGIC;
+	vpx->vp_size = cap;
+	vpx->vp_pages = kzalloc(sizeof(struct page *) * cap, GFP_KERNEL);
+	if (vpx->vp_pages == NULL)
+		goto free;
+
+	if (vp_alloc_pages(vpx, 0, cap))
+		goto free_all;
+
+	vpx->vp_baseptr = vpx->vp_top = page_address(vpx->vp_pages[0]);
+	vpx->vp_sdp = sdp;
+	vb->v_ptr = vpx->vp_baseptr;
+	vb->v_opaque = vpx;
+
+	err = 0;
+	goto out;
+
+free_all:
+	vp_free_pages(vpx);
+	kfree(vpx->vp_pages);
+free:
+	kfree(vpx);
+	vpx = NULL;
+out:
+	return err;
+}
+
+void vp_uninit(struct vbuf *vb)
+{
+	struct vp_ctx *vpx;
+
+	if (!vb || !vb->v_opaque)
+		return;
+
+	vpx = vb->v_opaque;
+	if (vpx->vp_magic != VP_MAGIC)
+		return;
+
+	vp_free_pages(vpx);
+	kfree(vpx->vp_pages);
+	kfree(vpx);
+	vb->v_ptr = vb->v_opaque = NULL;
+}
+
+static int vp_rw_pages(struct vp_ctx *vpx, void *to, const void *from,
+		       size_t count, int what)
+{
+	int pg_ind, pg_off, bytes, rw = 0;
+
+	while (count > 0) {
+		pg_ind = what == VP_READ ? VP_PAGE_INDEX(vpx, from)
+			: VP_PAGE_INDEX(vpx, to);
+		pg_off = what == VP_READ ? VP_PAGE_OFFSET(vpx, from)
+			: VP_PAGE_OFFSET(vpx, to);
+		bytes = what == VP_READ ? VP_PAGE_BYTES_LEFT(vpx, from)
+			: VP_PAGE_BYTES_LEFT(vpx, to);
+		bytes = min(count, (size_t) bytes);
+
+		if (what == VP_READ)
+			memcpy(to, VP_PAGE_PTR(vpx, pg_ind, pg_off), bytes);
+		else {
+			if (what == VP_WRITE)
+				memcpy(VP_PAGE_PTR(vpx, pg_ind, pg_off),
+				       from, bytes);
+			else if (what == VP_MEMSET)
+				memset(VP_PAGE_PTR(vpx, pg_ind, pg_off),
+				       (*(const int*)from), bytes);
+			if ((to + count) > vpx->vp_top)
+				vpx->vp_top = to + count;
+		}
+		to += bytes;
+		if (what != VP_MEMSET)
+			from += bytes;
+		rw += bytes;
+		count -= bytes;
+	}
+	return rw;
+}
+
+struct vp_ctx* vp_get_vpx(struct vbuf *vb)
+{
+	struct vp_ctx *vpx = NULL;
+
+	if (!vb || !vb->v_opaque)
+		goto out;
+
+	vpx = vb->v_opaque;
+	if (vpx->vp_magic != VP_MAGIC) {
+		vpx = NULL;
+		goto out;
+	}
+out:
+	return vpx;
+}
+
+int vp_read(struct vbuf *vb, void *to, const void *from, size_t count)
+{
+	struct vp_ctx *vpx;
+	void *buf_end = NULL;
+	int err = -EINVAL;
+
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return err;
+
+	buf_end = vpx->vp_baseptr + PAGE_SIZE * vpx->vp_size;
+	if (count <= 0 || (from + count > buf_end))
+		return err;
+
+	return vp_rw_pages(vpx, to, from, count, VP_READ);
+}
+
+int vp_write_i(struct vp_ctx *vpx, void *to, const void *from,
+	       size_t count, int what)
+{
+	int err = -EINVAL;
+	void *buf_end = NULL;
+
+	/* Check to see if the write will fall within limits */
+	buf_end = vpx->vp_baseptr + PAGE_SIZE * vpx->vp_size;
+	if (to < vpx->vp_baseptr ||
+	    (to + count) > (buf_end + PAGE_SIZE * VP_MAX_ALLOC_STEP))
+		return err;
+
+	if ((to + count) > buf_end) {
+		err = vp_extend(vpx, DIV_ROUND_UP(to + count - buf_end,
+						  PAGE_SIZE));
+		if (err)
+			return err;
+	}
+	return vp_rw_pages(vpx, to, from, count, what);
+}
+
+int vp_write(struct vbuf *vb, void *to, const void *from, size_t count)
+{
+	struct vp_ctx *vpx;
+	int err = -EINVAL;
+
+	if (count <= 0 || count > VP_MAX_WRITE)
+		return err;
+
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return err;
+
+	return vp_write_i(vpx, to, from, count, VP_WRITE);
+}
+
+int vp_append(struct vbuf *vb, const void *from, size_t count)
+{
+	struct vp_ctx *vpx;
+	int err = -EINVAL;
+
+	if (count <= 0 || count > VP_MAX_WRITE)
+		return err;
+
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return err;
+	
+	return vp_write_i(vpx, vpx->vp_top, from, count, VP_WRITE);
+}
+
+int vp_memset(struct vbuf *vb, void *to, int c, size_t count)
+{
+	struct vp_ctx *vpx;
+	int err = -EINVAL;
+
+	if (count <= 0 || count > VP_MAX_WRITE)
+		return err;
+
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return err;
+
+	err = vp_write_i(vpx, to, &c, count, VP_MEMSET);
+
+	return err;
+}
+
+/*
+ * Returns the linear ptr to the top of this buffer
+ */
+void* vp_get_top(struct vbuf *vb)
+{
+	struct vp_ctx *vpx;
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return NULL;
+	else
+		return vpx->vp_top;
+}
+
+/*
+ * vp_get_page_count: Returns # of pages allocated to this buffer
+ */
+size_t vp_get_page_count(struct vbuf *vb)
+{
+	struct vp_ctx *vpx;
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return -EINVAL;
+	return vpx->vp_size;
+}
+
+/*
+ * vp_get_size: Returns number of bytes used in this buffer
+ */
+size_t vp_get_size(struct vbuf *vb)
+{
+	struct vp_ctx *vpx;
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return -EINVAL;
+	return vpx->vp_top - vpx->vp_baseptr;
+}
+
+/*
+ * vp_reset: Doesn't deallocate, simply moves
+ * vp_top to vp_baseptr, making the entire buffer
+ * available for writing.
+ */
+void vp_reset(struct vbuf *vb)
+{
+	struct vp_ctx *vpx;
+	vpx = vp_get_vpx(vb);
+	if (vpx)
+		vpx->vp_top = vpx->vp_baseptr;
+}
fs/gfs2/util.h +43 -0

53 lines not on this page · raw message

Signature · 1 line
1.8.1.4
#6 lore raw

Re: [Cluster-devel] [RFC PATCH 3/5] gfs2: Add a dynamic buffer backed by a vector of pages

----- Original Message -----

Quoted 8 lines
fs/gfs2/util.c +8 -0
+static int vp_alloc_pages(struct vp_ctx *vpx, int start, int end)
+{
+	int i;
+
+	for (i = start; i < end; i++) {
+		vpx->vp_pages[i] = alloc_page(GFP_KERNEL | GFP_NOFS);
+		if (vpx->vp_pages[i] == NULL)
+			goto free;

fs/gfs2/util.c int gfs2_io_error_bh_i(struct gfs2_sbd *sdp, struct buffer_head *bh, · line 287

Quoted 10 lines
fs/gfs2/util.c +10 -0
+	}
+	return 0;
+free:
+	for (i = start; i < end; i++)
+		if (vpx->vp_pages[i]) {
+			__free_page(vpx->vp_pages[i]);
+			vpx->vp_pages[i] = NULL;
+		}
+	return -ENOMEM;
+}

I'm concerned that if we have a value for vpx->vp_pages[i] that is nonzero (uninitialized) _after_ an element in which we couldn't allocate a page, we could try to free the nonzero value. For that reason, I always prefer to do something like this instead:

free:

     while (i > start) {
          i--;
          __free_page(vpx->vp_pages[i]);
          vpx->vp_pages[i] = NULL;
     }
     return -ENOMEM;

(snip)

fs/gfs2/util.c int gfs2_io_error_bh_i(struct gfs2_sbd *sdp, struct buffer_head *bh, · line 425

Quoted 11 lines
fs/gfs2/util.c +11 -0
+struct vp_ctx* vp_get_vpx(struct vbuf *vb)
+{
+	struct vp_ctx *vpx = NULL;
+
+	if (!vb || !vb->v_opaque)
+		goto out;
+
+	vpx = vb->v_opaque;
+	if (vpx->vp_magic != VP_MAGIC) {
+		vpx = NULL;
+		goto out;

This goto is unnecessary, and if you eliminate it, you don't need the brackets.

fs/gfs2/util.c int gfs2_io_error_bh_i(struct gfs2_sbd *sdp, struct buffer_head *bh, · line 429

Quoted 4 lines
fs/gfs2/util.c +4 -0
+	}
+out:
+	return vpx;
+}

Regards,

Bob Peterson
Red Hat File Systems

author
#7 lore raw

RFC PATCH 4/5 gfs2: Add sort functionality with extra parameter

In reply to Abhi Das

This is essentially a copy of the sort function and its helpers from lib/sort.c with the exception of an additional 'void *opaque' parameter added here.
custom cmp_func and swap_func functions can make use of the opaque field for relevant context

---
 fs/gfs2/util.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/gfs2/util.h |  4 ++++
 2 files changed, 59 insertions(+)
fs/gfs2/util.c +55 -0
index 7345489..2c1aee3 100644
@@ -562,3 +562,58 @@ void vp_reset(struct vbuf *vb)
 	if (vpx)
 		vpx->vp_top = vpx->vp_baseptr;
 }
+
+/* heap sort that takes an opaque context - directly copied from lib/sort.c */
+static void u32_swap(void *opaque, void *a, void *b, int size)
+{
+	u32 t = *(u32 *)a;
+	*(u32 *)a = *(u32 *)b;
+	*(u32 *)b = t;
+}
+
+static void generic_swap(void *opaque, void *a, void *b, int size)
+{
+	char t;
+	do {
+		t = *(char *)a;
+		*(char *)a++ = *(char *)b;
+		*(char *)b++ = t;
+	} while (--size > 0);
+}
+
+void ctx_sort(void *opaque, void *base, size_t num, size_t size,
+	      int (*cmp_func)(void *, const void *, const void *),
+	      void (*swap_func)(void *, void *, void *, int size))
+{
+	int i = (num/2 - 1) * size, n = num * size, c, r;
+
+	if (!swap_func)
+		swap_func = (size == 4 ? u32_swap : generic_swap);
+
+	/* heapify */
+	for ( ; i >= 0; i -= size) {
+		for (r = i; r * 2 + size < n; r  = c) {
+			c = r * 2 + size;
+			if (c < n - size &&
+			    cmp_func(opaque, base + c, base + c + size) < 0)
+				c += size;
+			if (cmp_func(opaque, base + r, base + c) >= 0)
+				break;
+			swap_func(opaque, base + r, base + c, size);
+		}
+	}
+
+	/* sort */
+	for (i = n - size; i > 0; i -= size) {
+		swap_func(opaque, base, base + i, size);
+		for (r = 0; r * 2 + size < i; r = c) {
+			c = r * 2 + size;
+			if (c < i - size &&
+			    cmp_func(opaque, base + c, base + c + size) < 0)
+				c += size;
+			if (cmp_func(opaque, base + r, base + c) >= 0)
+				break;
+			swap_func(opaque, base + r, base + c, size);
+		}
+	}
+}
fs/gfs2/util.h +4 -0
index 40fb692..3e10d72 100644
@@ -210,5 +210,9 @@ int vp_read(struct vbuf *vb, void *to, const void *from, size_t count);
 int vp_write(struct vbuf *vb, void *to, const void *from, size_t count);
 int vp_append(struct vbuf *vb, const void *from, size_t count);
 int vp_memset(struct vbuf *vb, void *to, int c, size_t count);
+
+void ctx_sort(void *opaque, void *base, size_t num, size_t size,
+	      int (*cmp_func)(void *, const void *, const void *),
+	      void (*swap_func)(void *, void *, void *, int size));
  
 #endif /* __UTIL_DOT_H__ */
Signature · 1 line
1.8.1.4
author
#8 lore raw

RFC PATCH 5/5 gfs2: Add xreaddir file operation and supporting functions

In reply to Abhi Das

This patch adds support in GFS2 for the xgetdents syscall by implementing the xreaddir file operation.

GFS2 uses vbufs (buffer backed by a vector of pages) to store intermediate data like dirents, stat info and extended attribute keys/values to eventually bundle them into a container structure to return to the user.

---
 fs/gfs2/Makefile     |    3 +-
 fs/gfs2/dir.c        |   80 ++--
 fs/gfs2/dir.h        |   13 +-
 fs/gfs2/export.c     |    2 +-
 fs/gfs2/file.c       |   17 +-
 fs/gfs2/incore.h     |    6 +
 fs/gfs2/inode.c      |    3 +-
 fs/gfs2/inode.h      |    5 +
 fs/gfs2/ops_fstype.c |    4 +
 fs/gfs2/sys.c        |   26 +-
 fs/gfs2/util.c       |    9 +
 fs/gfs2/xattr.c      |   27 +-
 fs/gfs2/xattr.h      |   23 ++
 fs/gfs2/xreaddir.c   | 1024 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/gfs2/xreaddir.h   |   84 +++++
 15 files changed, 1260 insertions(+), 66 deletions(-)
 create mode 100644 fs/gfs2/xreaddir.c
 create mode 100644 fs/gfs2/xreaddir.h
fs/gfs2/Makefile +2 -1
index 8612820..da8253b 100644
@@ -4,7 +4,8 @@ gfs2-y := acl.o bmap.o dir.o xattr.o glock.o \
 	glops.o log.o lops.o main.o meta_io.o \
 	aops.o dentry.o export.o file.o \
 	ops_fstype.o inode.o quota.o \
-	recovery.o rgrp.o super.o sys.o trans.o util.o
+	recovery.o rgrp.o super.o sys.o \
+	trans.o util.o xreaddir.o
 
 gfs2-$(CONFIG_GFS2_FS_LOCKING_DLM) += lock_dlm.o
 
fs/gfs2/dir.c +45 -35

190 lines not on this page · raw message

fs/gfs2/dir.h +12 -1
index 126c65d..8d40590 100644
@@ -12,6 +12,10 @@
 
 #include <linux/dcache.h>
 #include <linux/crc32.h>
+#include "util.h"
+
+#define gfs2_disk_hash2offset(h) (((u64)(h)) >> 1)
+#define gfs2_dir_offset2hash(p) ((u32)(((u64)(p)) << 1))
 
 struct inode;
 struct gfs2_inode;
@@ -25,6 +29,13 @@ struct gfs2_diradd {
 	struct buffer_head *bh;
 };
 
+typedef int (*process_dent_t)(const struct gfs2_dirent *, loff_t, void *, filldir_t);
+extern int compare_dents_i(const struct gfs2_dirent *dent_a,
+			   const struct gfs2_dirent *dent_b);
+extern int foreach_dent(u64 *offset, void *opaque, filldir_t filldir,
+			const struct gfs2_dirent **darr, u32 entries,
+			int *copied, process_dent_t pd_fn);
+ 
 extern struct inode *gfs2_dir_search(struct inode *dir,
 				     const struct qstr *filename,
 				     bool fail_on_exist);
@@ -40,7 +51,7 @@ static inline void gfs2_dir_no_add(struct gfs2_diradd *da)
 }
 extern int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry);
 extern int gfs2_dir_read(struct inode *inode, struct dir_context *ctx,
-			 struct file_ra_state *f_ra);
+			 struct gfs2_xrdir_ctx *xc, struct file_ra_state *f_ra);
 extern int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename,
 			  const struct gfs2_inode *nip, unsigned int new_type);
 
fs/gfs2/export.c +1 -1
index 8b9b377..1f5085d 100644
@@ -114,7 +114,7 @@ static int gfs2_get_name(struct dentry *parent, char *name,
 	if (error)
 		return error;
 
-	error = gfs2_dir_read(dir, &gnfd.ctx, &f_ra);
+	error = gfs2_dir_read(dir, &gnfd.ctx, NULL, &f_ra);
 
 	gfs2_glock_dq_uninit(&gh);
 
fs/gfs2/file.c +15 -2
index 26b3f95..d2d7561f 100644
@@ -16,6 +16,8 @@
 #include <linux/blkdev.h>
 #include <linux/mm.h>
 #include <linux/mount.h>
+#include <linux/stat.h>
+#include <linux/sort.h>
 #include <linux/fs.h>
 #include <linux/gfs2_ondisk.h>
 #include <linux/falloc.h>
@@ -40,6 +42,7 @@
 #include "rgrp.h"
 #include "trans.h"
 #include "util.h"
+#include "xreaddir.h"
 
 /**
  * gfs2_llseek - seek to a location in a file
@@ -100,7 +103,7 @@ static int gfs2_readdir(struct file *file, struct dir_context *ctx)
 	if (error)
 		return error;
 
-	error = gfs2_dir_read(dir, ctx, &file->f_ra);
+	error = gfs2_dir_read(dir, ctx, NULL, &file->f_ra);
 
 	gfs2_glock_dq_uninit(&d_gh);
 
@@ -562,8 +565,13 @@ int gfs2_open_common(struct inode *inode, struct file *file)
 		return -ENOMEM;
 
 	mutex_init(&fp->f_fl_mutex);
-
 	gfs2_assert_warn(GFS2_SB(inode), !file->private_data);
+
+	if (S_ISDIR(inode->i_mode)) {
+		ret = gfs2_xrdir_ctx_init(fp, GFS2_SB(inode));
+		if (ret)
+			return ret;
+	}
 	file->private_data = fp;
 	return 0;
 }
@@ -617,6 +625,9 @@ static int gfs2_release(struct inode *inode, struct file *file)
 {
 	struct gfs2_inode *ip = GFS2_I(inode);
 
+	if (S_ISDIR(ip->i_inode.i_mode))
+		gfs2_xrdir_ctx_uninit((struct gfs2_file *)file->private_data);
+
 	kfree(file->private_data);
 	file->private_data = NULL;
 
@@ -1075,6 +1086,7 @@ const struct file_operations gfs2_file_fops = {
 
 const struct file_operations gfs2_dir_fops = {
 	.iterate	= gfs2_readdir,
+	.xreaddir       = gfs2_xreaddir,
 	.unlocked_ioctl	= gfs2_ioctl,
 	.open		= gfs2_open,
 	.release	= gfs2_release,
@@ -1105,6 +1117,7 @@ const struct file_operations gfs2_file_fops_nolock = {
 
 const struct file_operations gfs2_dir_fops_nolock = {
 	.iterate	= gfs2_readdir,
+	.xreaddir       = gfs2_xreaddir,
 	.unlocked_ioctl	= gfs2_ioctl,
 	.open		= gfs2_open,
 	.release	= gfs2_release,
fs/gfs2/incore.h +6 -0
index 67d310c..f86b6d3 100644
@@ -414,6 +414,7 @@ static inline struct gfs2_sbd *GFS2_SB(const struct inode *inode)
 struct gfs2_file {
 	struct mutex f_fl_mutex;
 	struct gfs2_holder f_fl_gh;
+	struct gfs2_xrdir_ctx *f_xrctx;
 };
 
 struct gfs2_revoke_replay {
@@ -570,6 +571,8 @@ struct gfs2_tune {
 	unsigned int gt_complain_secs;
 	unsigned int gt_statfs_quantum;
 	unsigned int gt_statfs_slow;
+	unsigned int gt_max_vb_pages; /* Max pages to utilize for vector-page buffers */
+	unsigned int gt_max_xrdir_dents; /* Maximum dents to process per collect cycle (conserves memory) */
 };
 
 enum {
@@ -812,6 +815,9 @@ struct gfs2_sbd {
 	struct dentry *debugfs_dentry_glocks;
 	struct dentry *debugfs_dentry_glstats;
 	struct dentry *debugfs_dentry_sbstats;
+
+	/* Vector Pages accounting */
+	atomic_t sd_vb_page_count;
 };
 
 static inline void gfs2_glstats_inc(struct gfs2_glock *gl, int which)
fs/gfs2/inode.c +2 -1
index e62e594..46c3602 100644
@@ -1833,7 +1833,8 @@ static int gfs2_getattr(struct vfsmount *mnt, struct dentry *dentry,
 		}
 	}
 
-	generic_fillattr(inode, stat);
+	gfs2_getattr_i(ip, stat);
+
 	if (unlock)
 		gfs2_glock_dq_uninit(&gh);
 	else if (frozen_root && atomic_dec_and_test(&sdp->sd_frozen_root))
fs/gfs2/inode.h +5 -0
index ba4d949..665f508 100644
@@ -93,6 +93,11 @@ err:
 	return -EIO;
 }
 
+static inline void gfs2_getattr_i(struct gfs2_inode *ip, struct kstat *stat)
+{
+	generic_fillattr(&ip->i_inode, stat);
+}
+
 extern struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type, 
 				       u64 no_addr, u64 no_formal_ino,
 				       int non_block);
fs/gfs2/ops_fstype.c +4 -0
index bc564c0..2d541ba 100644
@@ -60,6 +60,8 @@ static void gfs2_tune_init(struct gfs2_tune *gt)
 	gt->gt_new_files_jdata = 0;
 	gt->gt_max_readahead = 1 << 18;
 	gt->gt_complain_secs = 10;
+	gt->gt_max_vb_pages = 65536;
+	gt->gt_max_xrdir_dents = 25000;
 }
 
 static struct gfs2_sbd *init_sbd(struct super_block *sb)
@@ -135,6 +137,8 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb)
 	atomic_set(&sdp->sd_frozen_root, 0);
 	init_waitqueue_head(&sdp->sd_frozen_root_wait);
 
+	atomic_set(&sdp->sd_vb_page_count, 0);
+
 	return sdp;
 }
 
fs/gfs2/sys.c +22 -4
index 3ab566b..279aa86 100644
@@ -548,8 +548,8 @@ static ssize_t quota_scale_store(struct gfs2_sbd *sdp, const char *buf,
 	return len;
 }
 
-static ssize_t tune_set(struct gfs2_sbd *sdp, unsigned int *field,
-			int check_zero, const char *buf, size_t len)
+static ssize_t tune_set(struct gfs2_sbd *sdp, unsigned int *field, int check_zero,
+			unsigned int min, unsigned int max, const char *buf, size_t len)
 {
 	struct gfs2_tune *gt = &sdp->sd_tune;
 	unsigned int x;
@@ -562,6 +562,12 @@ static ssize_t tune_set(struct gfs2_sbd *sdp, unsigned int *field,
 	if (check_zero && !x)
 		return -EINVAL;
 
+	if (min && x < min)
+		return -EINVAL;
+
+	if (max && x > max)
+		return -EINVAL;
+
 	spin_lock(&gt->gt_spin);
 	*field = x;
 	spin_unlock(&gt->gt_spin);
@@ -578,13 +584,21 @@ static ssize_t name##_show(struct gfs2_sbd *sdp, char *buf)                   \
 }                                                                             \
 TUNE_ATTR_3(name, name##_show, store)
 
-#define TUNE_ATTR(name, check_zero)                                           \
+#define TUNE_ATTR(name, check_zero)                                                \
+static ssize_t name##_store(struct gfs2_sbd *sdp, const char *buf, size_t len)     \
+{                                                                                  \
+	return tune_set(sdp, &sdp->sd_tune.gt_##name, check_zero, 0, 0, buf, len); \
+}                                                                                  \
+TUNE_ATTR_2(name, name##_store)
+
+#define TUNE_ATTR_B(name, min, max)                                           \
 static ssize_t name##_store(struct gfs2_sbd *sdp, const char *buf, size_t len)\
 {                                                                             \
-	return tune_set(sdp, &sdp->sd_tune.gt_##name, check_zero, buf, len);  \
+	return tune_set(sdp, &sdp->sd_tune.gt_##name, 0, min, max, buf, len); \
 }                                                                             \
 TUNE_ATTR_2(name, name##_store)
 
+
 TUNE_ATTR(quota_warn_period, 0);
 TUNE_ATTR(quota_quantum, 0);
 TUNE_ATTR(max_readahead, 0);
@@ -593,6 +607,8 @@ TUNE_ATTR(statfs_slow, 0);
 TUNE_ATTR(new_files_jdata, 0);
 TUNE_ATTR(statfs_quantum, 1);
 TUNE_ATTR_3(quota_scale, quota_scale_show, quota_scale_store);
+TUNE_ATTR_B(max_vb_pages, 32, 8388608); /* total capacity can be 128K to 32G bytes */
+TUNE_ATTR(max_xrdir_dents, 0);
 
 static struct attribute *tune_attrs[] = {
 	&tune_attr_quota_warn_period.attr,
@@ -603,6 +619,8 @@ static struct attribute *tune_attrs[] = {
 	&tune_attr_statfs_quantum.attr,
 	&tune_attr_quota_scale.attr,
 	&tune_attr_new_files_jdata.attr,
+	&tune_attr_max_vb_pages.attr,
+	&tune_attr_max_xrdir_dents.attr,
 	NULL,
 };
 
fs/gfs2/util.c +9 -0
index 2c1aee3..793f69e 100644
@@ -301,6 +301,9 @@ static int vp_extend(struct vp_ctx *vpx, int size)
 {
 	struct gfs2_sbd *sdp = vpx->vp_sdp;
 
+	if ((gfs2_tune_get(sdp, gt_max_vb_pages)
+	     - atomic_read(&sdp->sd_vb_page_count)) < size)
+		goto out;
 	/* first make room for more pointers */
 	if (size <= 0)
 		return -EINVAL;
@@ -317,6 +320,7 @@ static int vp_extend(struct vp_ctx *vpx, int size)
 		goto out;
 
 	vpx->vp_size += size;
+	atomic_add(size, &sdp->sd_vb_page_count);
 	return 0;
 out:
 	return -ENOMEM;
@@ -328,6 +332,9 @@ int vp_init(struct gfs2_sbd *sdp, struct vbuf *vb, int init_cap)
 	struct vp_ctx *vpx;
 
 	cap = DIV_ROUND_UP(init_cap, PAGE_SIZE);
+	if ((gfs2_tune_get(sdp, gt_max_vb_pages)
+	     - atomic_read(&sdp->sd_vb_page_count)) < cap)
+		goto out;
 
 	vpx = kmalloc(sizeof(struct vp_ctx), GFP_KERNEL);
 	if (vpx == NULL)
@@ -344,6 +351,7 @@ int vp_init(struct gfs2_sbd *sdp, struct vbuf *vb, int init_cap)
 
 	vpx->vp_baseptr = vpx->vp_top = page_address(vpx->vp_pages[0]);
 	vpx->vp_sdp = sdp;
+	atomic_add(cap, &sdp->sd_vb_page_count);
 	vb->v_ptr = vpx->vp_baseptr;
 	vb->v_opaque = vpx;
 
@@ -373,6 +381,7 @@ void vp_uninit(struct vbuf *vb)
 
 	vp_free_pages(vpx);
 	kfree(vpx->vp_pages);
+	atomic_sub(vpx->vp_size, &vpx->vp_sdp->sd_vb_page_count);
 	kfree(vpx);
 	vb->v_ptr = vb->v_opaque = NULL;
 }
fs/gfs2/xattr.c +6 -21
index 0b81f78..f156b21 100644
@@ -11,6 +11,7 @@
 #include <linux/spinlock.h>
 #include <linux/completion.h>
 #include <linux/buffer_head.h>
+#include <linux/sort.h>
 #include <linux/xattr.h>
 #include <linux/gfs2_ondisk.h>
 #include <linux/posix_acl_xattr.h>
@@ -19,6 +20,7 @@
 #include "gfs2.h"
 #include "incore.h"
 #include "acl.h"
+#include "dir.h"
 #include "xattr.h"
 #include "glock.h"
 #include "inode.h"
@@ -27,6 +29,7 @@
 #include "rgrp.h"
 #include "trans.h"
 #include "util.h"
+#include "xreaddir.h"
 
 /**
  * ea_calc_size - returns the acutal number of bytes the request will take up
@@ -72,10 +75,6 @@ static int ea_check_size(struct gfs2_sbd *sdp, unsigned int nsize, size_t dsize)
 	return 0;
 }
 
-typedef int (*ea_call_t) (struct gfs2_inode *ip, struct buffer_head *bh,
-			  struct gfs2_ea_header *ea,
-			  struct gfs2_ea_header *prev, void *private);
-
 static int ea_foreach_i(struct gfs2_inode *ip, struct buffer_head *bh,
 			ea_call_t ea_call, void *data)
 {
@@ -113,7 +112,7 @@ fail:
 	return -EIO;
 }
 
-static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data)
+int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data)
 {
 	struct buffer_head *bh, *eabh;
 	__be64 *eablk, *end;
@@ -374,28 +373,14 @@ static int ea_list_i(struct gfs2_inode *ip, struct buffer_head *bh,
 		return 0;
 
 	if (er->er_data_len) {
-		char *prefix = NULL;
+		char prefix[9];
 		unsigned int l = 0;
 		char c = 0;
 
 		if (ei->ei_size + ea_size > er->er_data_len)
 			return -ERANGE;
 
-		switch (ea->ea_type) {
-		case GFS2_EATYPE_USR:
-			prefix = "user.";
-			l = 5;
-			break;
-		case GFS2_EATYPE_SYS:
-			prefix = "system.";
-			l = 7;
-			break;
-		case GFS2_EATYPE_SECURITY:
-			prefix = "security.";
-			l = 9;
-			break;
-		}
-
+		l = ea_prefix(ea, prefix, 9);
 		BUG_ON(l == 0);
 
 		memcpy(er->er_data + ei->ei_size, prefix, l);
fs/gfs2/xattr.h +23 -0
index d392f83..c09f090 100644
@@ -10,6 +10,8 @@
 #ifndef __EATTR_DOT_H__
 #define __EATTR_DOT_H__
 
+#include "dir.h"
+
 struct gfs2_inode;
 struct iattr;
 
@@ -53,9 +55,30 @@ struct gfs2_ea_location {
 	struct gfs2_ea_header *el_prev;
 };
 
+static __inline__ int ea_prefix(struct gfs2_ea_header *ea, char *buf, int size)
+{
+	BUG_ON(size < 9);
+	switch (ea->ea_type) {
+	case GFS2_EATYPE_USR:
+		strncpy(buf, "user.", 5);
+		return 5;
+	case GFS2_EATYPE_SYS:
+		strncpy(buf, "system.", 7);
+		return 7;
+	case GFS2_EATYPE_SECURITY:
+		strncpy(buf, "security.", 9);
+		return 9;
+	}
+	return 0;
+}
+
 extern int __gfs2_xattr_set(struct inode *inode, const char *name,
 			    const void *value, size_t size,
 			    int flags, int type);
+typedef int (*ea_call_t) (struct gfs2_inode *ip, struct buffer_head *bh,
+			  struct gfs2_ea_header *ea,
+			  struct gfs2_ea_header *prev, void *private);
+extern int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data);
 extern ssize_t gfs2_listxattr(struct dentry *dentry, char *buffer, size_t size);
 extern int gfs2_ea_dealloc(struct gfs2_inode *ip);
 
fs/gfs2/xreaddir.c new file +1024 -0

1,024 lines not on this page · raw message

fs/gfs2/xreaddir.h new file +84 -0

84 lines not on this page · raw message

Signature · 1 line
1.8.1.4
#9 lore raw

Re: [RFC PATCH 5/5] gfs2: Add xreaddir file operation and supporting functions

Quoted 2 lines · On Fri, 25 Jul 2014 12:38:08 -0500 Abhi Das <[email protected]> wrote:

This patch adds support in GFS2 for the xgetdents syscall by implementing the xreaddir file operation.

So I was trying to make sense of this, and ran into one little thing that jumped out at me:

fs/gfs2/xreaddir.c line 755

Quoted 5 lines
fs/gfs2/xreaddir.c new file +5 -0
+static int gfs2_xrdir_to_user_vars(struct gfs2_xrdir_ctx *xc,
+				   struct gfs2_xdirent *x,
+				   struct gfs2_xdirent *x_vb_p,
+				   struct linux_xdirent __user *lxd,
+				   size_t count, size_t *bytes)

Now, I'll readily admit that I could be overly confused by this function. When the variables are named "x", "xx", "xc", "x_vb_p", "xblob", and "lxd", it all starts to run together. But still...

fs/gfs2/xreaddir.c line 788

Quoted 2 lines
fs/gfs2/xreaddir.c new file +2 -0
+	if ((xc->xc_xattr_mask & XSTAT_XATTR_ALL) &&
+		lxd->xd_blob.xb_xattr_count) {

How can that be right? lxd is __user, it doesn't seem right to be dereferencing it directly...?

Thanks,

jon

#10 lore raw

Re: [RFC PATCH 5/5] gfs2: Add xreaddir file operation and supporting functions

----- Original Message -----

Quoted 15 lines

From: "Jonathan Corbet" <[email protected]>
To: "Abhi Das" <[email protected]>
Cc: linux-kernel at vger.kernel.org, linux-fsdevel at vger.kernel.org, cluster-devel at redhat.com
Sent: Tuesday, July 29, 2014 1:58:08 PM
Subject: Re: [RFC PATCH 5/5] gfs2: Add xreaddir file operation and supporting functions

Quoted 2 lines · On Fri, 25 Jul 2014 12:38:08 -0500 Abhi Das <[email protected]> wrote:

This patch adds support in GFS2 for the xgetdents syscall by implementing the xreaddir file operation.

So I was trying to make sense of this, and ran into one little thing that jumped out at me:

Quoted 10 lines

fs/gfs2/xreaddir.c line 755

Quoted 5 lines
fs/gfs2/xreaddir.c new file +5 -0
+static int gfs2_xrdir_to_user_vars(struct gfs2_xrdir_ctx *xc,
+				   struct gfs2_xdirent *x,
+				   struct gfs2_xdirent *x_vb_p,
+				   struct linux_xdirent __user *lxd,
+				   size_t count, size_t *bytes)

Now, I'll readily admit that I could be overly confused by this function. When the variables are named "x", "xx", "xc", "x_vb_p", "xblob", and "lxd", it all starts to run together. But still...

As I went along writing this patch, I ended up needing more structs and names and this is the ugly result :(. I guess some of these structs/names can be reviewed and changed as needed. This is just a proof of concept patch to illustrate how such a syscall can be implemented.

Quoted 5 lines

fs/gfs2/xreaddir.c line 788

Quoted 2 lines
fs/gfs2/xreaddir.c new file +2 -0
+	if ((xc->xc_xattr_mask & XSTAT_XATTR_ALL) &&
+		lxd->xd_blob.xb_xattr_count) {

How can that be right? lxd is __user, it doesn't seem right to be dereferencing it directly...?

Wouldn't the call to access_ok() at the start of the syscall take care of this? All the __user pointers point to areas within the user supplied buffer buf and overflow past the end of the buffer for the last lxd is checked for.

The 2/5 patch in this series adds the following in fs/readdir.c:

+SYSCALL_DEFINE5(xgetdents, unsigned int, fd, unsigned, flags, unsigned int, mask,
+ void __user *, buf, unsigned int, count)
...
...
...
+ if (!access_ok(VERIFY_WRITE, buf, count))
+ return -EFAULT;

Quoted 5 lines

Thanks,

jon

Cheers!
--Abhi

#11 lore raw

RFC PATCH 5/5 gfs2: Add xreaddir file operation and supporting functions

Quoted 7 lines · Abhijith Das

----- Original Message -----

Quoted 5 lines

From: "Jonathan Corbet" <[email protected]>
To: "Abhi Das" <[email protected]>
Cc: linux-kernel at vger.kernel.org, linux-fsdevel at vger.kernel.org, cluster-devel at redhat.com
Sent: Tuesday, July 29, 2014 1:58:08 PM
Subject: Re: [RFC PATCH 5/5] gfs2: Add xreaddir file operation and supporting functions

[snip]

Quoted 11 lines
Quoted 4 lines

fs/gfs2/xreaddir.c line 788

Quoted 2 lines
fs/gfs2/xreaddir.c new file +2 -0
+	if ((xc->xc_xattr_mask & XSTAT_XATTR_ALL) &&
+		lxd->xd_blob.xb_xattr_count) {

How can that be right? lxd is __user, it doesn't seem right to be dereferencing it directly...?

Wouldn't the call to access_ok() at the start of the syscall take care of this? All the __user pointers point to areas within the user supplied buffer buf and overflow past the end of the buffer for the last lxd is checked for.

The 2/5 patch in this series adds the following in fs/readdir.c:

Review

Quoted 7 lines
(quoted diff) +4 -0
+SYSCALL_DEFINE5(xgetdents, unsigned int, fd, unsigned, flags, unsigned int, mask,
+               void __user *, buf, unsigned int, count)
...
...
...
+       if (!access_ok(VERIFY_WRITE, buf, count))
+               return -EFAULT;

The access_ok() call only checks the source memory at the time of the call, it is possible for the page to become invalid for reading or writing to at any subsequent time. So that the kernel's accessor functions are required here. I think sparse should warn about this too.

However, the intention was to show that the xreaddir approach is not ideal due to its complexity, and also due to it being very much single purpose compared with the readahead approach, and from that point of view it shouldn't affect the performance measurements in this case,

Steve.

#12 lore raw

Re: [RFC PATCH 5/5] gfs2: Add xreaddir file operation and supporting functions

In reply to Abhijith Das
Quoted 9 lines · On Tue, 29 Jul 2014 18:25:57 -0400 (EDT) Abhijith Das <[email protected]> wrote:
Quoted 5 lines

fs/gfs2/xreaddir.c line 788

Quoted 2 lines
fs/gfs2/xreaddir.c new file +2 -0
+	if ((xc->xc_xattr_mask & XSTAT_XATTR_ALL) &&
+		lxd->xd_blob.xb_xattr_count) {  

How can that be right? lxd is __user, it doesn't seem right to be dereferencing it directly...?

Wouldn't the call to access_ok() at the start of the syscall take care of this? All the __user pointers point to areas within the user supplied buffer buf and overflow past the end of the buffer for the last lxd is checked for.

No, dereferencing user-space pointers in the kernel is never OK. What if user space remapped that page after the access_ok() call? You need to use copy_*_user() to get at user-space structures from the kernel.

jon

#13 lore raw
In reply to Abhi Das
Quoted 11 lines · Abhi Das

This system call takes 5 arguments:
fd - file descriptor of the directory
flags - flags used by xstat
mask - field mask used to request stat/xattr info
buf - user buf to return collected info
count - size of user buffer in bytes

xgetdents() should read the directory entries, collect stat and xattr information for each entry as requested and return all the data back to the user in a container structure (linux_xdirent) as part of the supplied user buffer.

Hello Abhi,

As per Documentation/SubmitChecklist, please CC linux-api on patches that change the kerne-user-space API/ABI. (See
https://www.kernel.org/doc/man-pages/linux-api-ml.html for more details.)

Cheers,

Michael

Quoted 43 lines

Abhi Das (5):
fs: xstat system call VFS bits
fs: Add xgetdents system call and xreaddir file operation
gfs2: Add a dynamic buffer backed by a vector of pages
gfs2: Add sort functionality with extra parameter
gfs2: Add xreaddir file operation and supporting functions

 arch/x86/syscalls/syscall_32.tbl |    3 +
 arch/x86/syscalls/syscall_64.tbl |    3 +
 fs/gfs2/Makefile                 |    3 +-
 fs/gfs2/dir.c                    |   80 +--
 fs/gfs2/dir.h                    |   13 +-
 fs/gfs2/export.c                 |    2 +-
 fs/gfs2/file.c                   |   17 +-
 fs/gfs2/incore.h                 |    6 +
 fs/gfs2/inode.c                  |    3 +-
 fs/gfs2/inode.h                  |    5 +
 fs/gfs2/ops_fstype.c             |    4 +
 fs/gfs2/sys.c                    |   26 +-
 fs/gfs2/util.c                   |  363 ++++++++++++++
 fs/gfs2/util.h                   |   47 ++
 fs/gfs2/xattr.c                  |   27 +-
 fs/gfs2/xattr.h                  |   23 +
 fs/gfs2/xreaddir.c               | 1024 ++++++++++++++++++++++++++++++++++++++
 fs/gfs2/xreaddir.h               |   84 ++++
 fs/readdir.c                     |   42 ++
 fs/stat.c                        |  340 ++++++++++++-
 include/linux/fs.h               |    5 +
 include/linux/stat.h             |   16 +-
 include/linux/syscalls.h         |    5 +
 include/uapi/linux/fcntl.h       |    1 +
 include/uapi/linux/stat.h        |  143 ++++++
 25 files changed, 2196 insertions(+), 89 deletions(-)
 create mode 100644 fs/gfs2/xreaddir.c
 create mode 100644 fs/gfs2/xreaddir.h

--
1.8.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

Signature · 3 lines
Michael Kerrisk Linux man-pages maintainer;
http://www.kernel.org/doc/man-pages/
Author of "The Linux Programming Interface", http://blog.man7.org/