Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 051e7cd4 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Tim Shimmin
Browse files

[XFS] use filldir internally



Currently xfs has a rather complicated internal scheme to allow for
different directory formats in IRIX. This patch rips all code related to
this out and pushes useage of the Linux filldir callback into the lowlevel
directory code. This does not make the code any less portable because
filldir can be used to create dirents of all possible variations
(including the IRIX ones as proved by the IRIX binary emulation code under
arch/mips/).

This patch get rid of an unessecary copy in the readdir path, about 400
lines of code and one of the last two users of the uio structure.

This version is updated to deal with dmapi aswell which greatly simplifies
the get_dirattrs code. The dmapi part has been tested using the
get_dirattrs tools from the xfstest dmapi suite1 with various small and
large directories.

SGI-PV: 968563
SGI-Modid: xfs-linux-melb:xfs-kern:29478a

Signed-off-by: default avatarChristoph Hellwig <hch@infradead.org>
Signed-off-by: default avatarDavid Chinner <dgc@sgi.com>
Signed-off-by: default avatarTim Shimmin <tes@sgi.com>
parent 2bdf7cd0
Loading
Loading
Loading
Loading
+23 −67
Original line number Diff line number Diff line
@@ -233,74 +233,30 @@ xfs_file_readdir(
	void		*dirent,
	filldir_t	filldir)
{
	int		error = 0;
	bhv_vnode_t	*vp = vn_from_inode(filp->f_path.dentry->d_inode);
	uio_t		uio;
	iovec_t		iov;
	int		eof = 0;
	caddr_t		read_buf;
	int		namelen, size = 0;
	size_t		rlen = PAGE_CACHE_SIZE;
	xfs_off_t	start_offset, curr_offset;
	xfs_dirent_t	*dbp = NULL;

	/* Try fairly hard to get memory */
	do {
		if ((read_buf = kmalloc(rlen, GFP_KERNEL)))
			break;
		rlen >>= 1;
	} while (rlen >= 1024);

	if (read_buf == NULL)
		return -ENOMEM;

	uio.uio_iov = &iov;
	uio.uio_segflg = UIO_SYSSPACE;
	curr_offset = filp->f_pos;
	if (filp->f_pos != 0x7fffffff)
		uio.uio_offset = filp->f_pos;
	else
		uio.uio_offset = 0xffffffff;

	while (!eof) {
		uio.uio_resid = iov.iov_len = rlen;
		iov.iov_base = read_buf;
		uio.uio_iovcnt = 1;

		start_offset = uio.uio_offset;

		error = bhv_vop_readdir(vp, &uio, NULL, &eof);
		if ((uio.uio_offset == start_offset) || error) {
			size = 0;
			break;
		}

		size = rlen - uio.uio_resid;
		dbp = (xfs_dirent_t *)read_buf;
		while (size > 0) {
			namelen = strlen(dbp->d_name);
	struct inode	*inode = filp->f_path.dentry->d_inode;
	bhv_vnode_t	*vp = vn_from_inode(inode);
	int		error;
	size_t		bufsize;

			if (filldir(dirent, dbp->d_name, namelen,
					(loff_t) curr_offset & 0x7fffffff,
					(ino_t) dbp->d_ino,
					DT_UNKNOWN)) {
				goto done;
			}
			size -= dbp->d_reclen;
			curr_offset = (loff_t)dbp->d_off /* & 0x7fffffff */;
			dbp = (xfs_dirent_t *)((char *)dbp + dbp->d_reclen);
		}
	}
done:
	if (!error) {
		if (size == 0)
			filp->f_pos = uio.uio_offset & 0x7fffffff;
		else if (dbp)
			filp->f_pos = curr_offset;
	}
	/*
	 * The Linux API doesn't pass down the total size of the buffer
	 * we read into down to the filesystem.  With the filldir concept
	 * it's not needed for correct information, but the XFS dir2 leaf
	 * code wants an estimate of the buffer size to calculate it's
	 * readahead window and size the buffers used for mapping to
	 * physical blocks.
	 *
	 * Try to give it an estimate that's good enough, maybe at some
	 * point we can change the ->readdir prototype to include the
	 * buffer size.
	 */
	bufsize = (size_t)min_t(loff_t, PAGE_SIZE, inode->i_size);

	kfree(read_buf);
	error = bhv_vop_readdir(vp, dirent, bufsize,
				(xfs_off_t *)&filp->f_pos, filldir);
	if (error)
		return -error;
	return 0;
}

STATIC int
+4 −4
Original line number Diff line number Diff line
@@ -161,8 +161,8 @@ typedef int (*vop_rename_t)(bhv_desc_t *, bhv_vname_t *, bhv_vnode_t *,
typedef int	(*vop_mkdir_t)(bhv_desc_t *, bhv_vname_t *, struct bhv_vattr *,
				bhv_vnode_t **, struct cred *);
typedef int	(*vop_rmdir_t)(bhv_desc_t *, bhv_vname_t *, struct cred *);
typedef int	(*vop_readdir_t)(bhv_desc_t *, struct uio *, struct cred *,
				int *);
typedef int	(*vop_readdir_t)(bhv_desc_t *, void *dirent, size_t bufsize,
				 xfs_off_t *offset, filldir_t filldir);
typedef int	(*vop_symlink_t)(bhv_desc_t *, bhv_vname_t *, struct bhv_vattr*,
				char *, bhv_vnode_t **, struct cred *);
typedef int	(*vop_readlink_t)(bhv_desc_t *, struct uio *, int,
@@ -267,8 +267,8 @@ typedef struct bhv_vnodeops {
#define	bhv_vop_mkdir(dp,d,vap,vpp,cr)					\
		VOP(vop_mkdir, dp)(VNHEAD(dp),d,vap,vpp,cr)
#define	bhv_vop_rmdir(dp,d,cr)	 	VOP(vop_rmdir, dp)(VNHEAD(dp),d,cr)
#define	bhv_vop_readdir(vp,uiop,cr,eofp)				\
		VOP(vop_readdir, vp)(VNHEAD(vp),uiop,cr,eofp)
#define	bhv_vop_readdir(vp,dirent,bufsize,offset,filldir)		\
		VOP(vop_readdir, vp)(VNHEAD(vp),dirent,bufsize,offset,filldir)
#define	bhv_vop_symlink(dvp,d,vap,tnm,vpp,cr)				\
		VOP(vop_symlink, dvp)(VNHEAD(dvp),d,vap,tnm,vpp,cr)
#define	bhv_vop_readlink(vp,uiop,fl,cr)					\
+18 −103
Original line number Diff line number Diff line
@@ -43,8 +43,6 @@
#include "xfs_dir2_trace.h"
#include "xfs_error.h"

static int	xfs_dir2_put_dirent64_direct(xfs_dir2_put_args_t *pa);
static int	xfs_dir2_put_dirent64_uio(xfs_dir2_put_args_t *pa);

void
xfs_dir_mount(
@@ -293,47 +291,35 @@ xfs_dir_removename(
 * Read a directory.
 */
int
xfs_dir_getdents(
	xfs_trans_t	*tp,
	xfs_inode_t	*dp,
	uio_t		*uio,		/* caller's buffer control */
	int		*eofp)		/* out: eof reached */
xfs_readdir(
	bhv_desc_t	*dir_bdp,
	void		*dirent,
	size_t		bufsize,
	xfs_off_t	*offset,
	filldir_t	filldir)
{
	int		alignment;	/* alignment required for ABI */
	xfs_dirent_t	*dbp;		/* malloc'ed buffer */
	xfs_dir2_put_t	put;		/* entry formatting routine */
	xfs_inode_t	*dp = XFS_BHVTOI(dir_bdp);
	int		rval;		/* return value */
	int		v;		/* type-checking value */

	vn_trace_entry(BHV_TO_VNODE(dir_bdp), __FUNCTION__,
					       (inst_t *)__return_address);

	if (XFS_FORCED_SHUTDOWN(dp->i_mount))
		return XFS_ERROR(EIO);

	ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
	XFS_STATS_INC(xs_dir_getdents);
	/*
	 * If our caller has given us a single contiguous aligned memory buffer,
	 * just work directly within that buffer.  If it's in user memory,
	 * lock it down first.
	 */
	alignment = sizeof(xfs_off_t) - 1;
	if ((uio->uio_iovcnt == 1) &&
	    (((__psint_t)uio->uio_iov[0].iov_base & alignment) == 0) &&
	    ((uio->uio_iov[0].iov_len & alignment) == 0)) {
		dbp = NULL;
		put = xfs_dir2_put_dirent64_direct;
	} else {
		dbp = kmem_alloc(sizeof(*dbp) + MAXNAMELEN, KM_SLEEP);
		put = xfs_dir2_put_dirent64_uio;
	}

	*eofp = 0;
	if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
		rval = xfs_dir2_sf_getdents(dp, uio, eofp, dbp, put);
	else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
		rval = xfs_dir2_sf_getdents(dp, dirent, offset, filldir);
	else if ((rval = xfs_dir2_isblock(NULL, dp, &v)))
		;
	else if (v)
		rval = xfs_dir2_block_getdents(tp, dp, uio, eofp, dbp, put);
		rval = xfs_dir2_block_getdents(dp, dirent, offset, filldir);
	else
		rval = xfs_dir2_leaf_getdents(tp, dp, uio, eofp, dbp, put);
	if (dbp != NULL)
		kmem_free(dbp, sizeof(*dbp) + MAXNAMELEN);
		rval = xfs_dir2_leaf_getdents(dp, dirent, bufsize, offset,
					      filldir);
	return rval;
}

@@ -612,77 +598,6 @@ xfs_dir2_isleaf(
	return 0;
}

/*
 * Getdents put routine for 64-bit ABI, direct form.
 */
static int
xfs_dir2_put_dirent64_direct(
	xfs_dir2_put_args_t	*pa)
{
	xfs_dirent_t		*idbp;		/* dirent pointer */
	iovec_t			*iovp;		/* io vector */
	int			namelen;	/* entry name length */
	int			reclen;		/* entry total length */
	uio_t			*uio;		/* I/O control */

	namelen = pa->namelen;
	reclen = DIRENTSIZE(namelen);
	uio = pa->uio;
	/*
	 * Won't fit in the remaining space.
	 */
	if (reclen > uio->uio_resid) {
		pa->done = 0;
		return 0;
	}
	iovp = uio->uio_iov;
	idbp = (xfs_dirent_t *)iovp->iov_base;
	iovp->iov_base = (char *)idbp + reclen;
	iovp->iov_len -= reclen;
	uio->uio_resid -= reclen;
	idbp->d_reclen = reclen;
	idbp->d_ino = pa->ino;
	idbp->d_off = pa->cook;
	idbp->d_name[namelen] = '\0';
	pa->done = 1;
	memcpy(idbp->d_name, pa->name, namelen);
	return 0;
}

/*
 * Getdents put routine for 64-bit ABI, uio form.
 */
static int
xfs_dir2_put_dirent64_uio(
	xfs_dir2_put_args_t	*pa)
{
	xfs_dirent_t		*idbp;		/* dirent pointer */
	int			namelen;	/* entry name length */
	int			reclen;		/* entry total length */
	int			rval;		/* return value */
	uio_t			*uio;		/* I/O control */

	namelen = pa->namelen;
	reclen = DIRENTSIZE(namelen);
	uio = pa->uio;
	/*
	 * Won't fit in the remaining space.
	 */
	if (reclen > uio->uio_resid) {
		pa->done = 0;
		return 0;
	}
	idbp = pa->dbp;
	idbp->d_reclen = reclen;
	idbp->d_ino = pa->ino;
	idbp->d_off = pa->cook;
	idbp->d_name[namelen] = '\0';
	memcpy(idbp->d_name, pa->name, namelen);
	rval = xfs_uio_read((caddr_t)idbp, reclen, uio);
	pa->done = (rval == 0);
	return rval;
}

/*
 * Remove the given block from the directory.
 * This routine is used for data and free blocks, leaf/node are done
+2 −17
Original line number Diff line number Diff line
@@ -59,21 +59,6 @@ typedef __uint32_t xfs_dir2_db_t;
 */
typedef	xfs_off_t	xfs_dir2_off_t;

/*
 * For getdents, argument struct for put routines.
 */
typedef int (*xfs_dir2_put_t)(struct xfs_dir2_put_args *pa);
typedef struct xfs_dir2_put_args {
	xfs_off_t	cook;		/* cookie of (next) entry */
	xfs_intino_t	ino;		/* inode number */
	xfs_dirent_t	*dbp;		/* buffer pointer */
	char		*name;		/* directory entry name */
	int		namelen;	/* length of name */
	int		done;		/* output: set if value was stored */
	xfs_dir2_put_t	put;		/* put function ptr (i/o) */
	struct uio	*uio;		/* uio control structure */
} xfs_dir2_put_args_t;

/*
 * Generic directory interface routines
 */
@@ -92,8 +77,6 @@ extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp,
				char *name, int namelen, xfs_ino_t ino,
				xfs_fsblock_t *first,
				struct xfs_bmap_free *flist, xfs_extlen_t tot);
extern int xfs_dir_getdents(struct xfs_trans *tp, struct xfs_inode *dp,
				uio_t *uio, int *eofp);
extern int xfs_dir_replace(struct xfs_trans *tp, struct xfs_inode *dp,
				char *name, int namelen, xfs_ino_t inum,
				xfs_fsblock_t *first,
@@ -101,6 +84,8 @@ extern int xfs_dir_replace(struct xfs_trans *tp, struct xfs_inode *dp,
extern int xfs_dir_canenter(struct xfs_trans *tp, struct xfs_inode *dp,
				char *name, int namelen);
extern int xfs_dir_ino_validate(struct xfs_mount *mp, xfs_ino_t ino);
extern int xfs_readdir(bhv_desc_t *dir_bdp, void *dirent, size_t bufsize,
		       xfs_off_t *offset, filldir_t filldir);

/*
 * Utility routines for v2 directories.
+23 −40
Original line number Diff line number Diff line
@@ -432,12 +432,10 @@ xfs_dir2_block_addname(
 */
int						/* error */
xfs_dir2_block_getdents(
	xfs_trans_t		*tp,		/* transaction (NULL) */
	xfs_inode_t		*dp,		/* incore inode */
	uio_t			*uio,		/* caller's buffer control */
	int			*eofp,		/* eof reached? (out) */
	xfs_dirent_t		*dbp,		/* caller's buffer */
	xfs_dir2_put_t		put)		/* abi's formatting function */
	void			*dirent,
	xfs_off_t		*offset,
	filldir_t		filldir)
{
	xfs_dir2_block_t	*block;		/* directory block structure */
	xfs_dabuf_t		*bp;		/* buffer for block */
@@ -447,31 +445,32 @@ xfs_dir2_block_getdents(
	char			*endptr;	/* end of the data entries */
	int			error;		/* error return value */
	xfs_mount_t		*mp;		/* filesystem mount point */
	xfs_dir2_put_args_t	p;		/* arg package for put rtn */
	char			*ptr;		/* current data entry */
	int			wantoff;	/* starting block offset */
	xfs_ino_t		ino;
	xfs_off_t		cook;

	mp = dp->i_mount;
	/*
	 * If the block number in the offset is out of range, we're done.
	 */
	if (xfs_dir2_dataptr_to_db(mp, uio->uio_offset) > mp->m_dirdatablk) {
		*eofp = 1;
	if (xfs_dir2_dataptr_to_db(mp, *offset) > mp->m_dirdatablk) {
		return 0;
	}
	/*
	 * Can't read the block, give up, else get dabuf in bp.
	 */
	if ((error =
	    xfs_da_read_buf(tp, dp, mp->m_dirdatablk, -1, &bp, XFS_DATA_FORK))) {
	error = xfs_da_read_buf(NULL, dp, mp->m_dirdatablk, -1,
				&bp, XFS_DATA_FORK);
	if (error)
		return error;
	}

	ASSERT(bp != NULL);
	/*
	 * Extract the byte offset we start at from the seek pointer.
	 * We'll skip entries before this.
	 */
	wantoff = xfs_dir2_dataptr_to_off(mp, uio->uio_offset);
	wantoff = xfs_dir2_dataptr_to_off(mp, *offset);
	block = bp->data;
	xfs_dir2_data_check(dp, bp);
	/*
@@ -480,9 +479,7 @@ xfs_dir2_block_getdents(
	btp = xfs_dir2_block_tail_p(mp, block);
	ptr = (char *)block->u;
	endptr = (char *)xfs_dir2_block_leaf_p(btp);
	p.dbp = dbp;
	p.put = put;
	p.uio = uio;

	/*
	 * Loop over the data portion of the block.
	 * Each object is a real entry (dep) or an unused one (dup).
@@ -508,33 +505,24 @@ xfs_dir2_block_getdents(
		 */
		if ((char *)dep - (char *)block < wantoff)
			continue;
		/*
		 * Set up argument structure for put routine.
		 */
		p.namelen = dep->namelen;

		p.cook = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
		cook = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
						    ptr - (char *)block);
		p.ino = be64_to_cpu(dep->inumber);
		ino = be64_to_cpu(dep->inumber);
#if XFS_BIG_INUMS
		p.ino += mp->m_inoadd;
		ino += mp->m_inoadd;
#endif
		p.name = (char *)dep->name;

		/*
		 * Put the entry in the caller's buffer.
		 */
		error = p.put(&p);

		/*
		 * If it didn't fit, set the final offset to here & return.
		 */
		if (!p.done) {
			uio->uio_offset =
				xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
		if (filldir(dirent, dep->name, dep->namelen, cook,
			    ino, DT_UNKNOWN)) {
			*offset = xfs_dir2_db_off_to_dataptr(mp,
					mp->m_dirdatablk,
					(char *)dep - (char *)block);
			xfs_da_brelse(tp, bp);
			return error;
			xfs_da_brelse(NULL, bp);
			return 0;
		}
	}

@@ -542,13 +530,8 @@ xfs_dir2_block_getdents(
	 * Reached the end of the block.
	 * Set the offset to a non-existent block 1 and return.
	 */
	*eofp = 1;

	uio->uio_offset =
		xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0);

	xfs_da_brelse(tp, bp);

	*offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0);
	xfs_da_brelse(NULL, bp);
	return 0;
}

Loading