svn commit: r293680 - head/sys/fs/ext2fs

Pedro F. Giffuni pfg at FreeBSD.org
Mon Jan 11 19:14:57 UTC 2016


Author: pfg
Date: Mon Jan 11 19:14:55 2016
New Revision: 293680
URL: https://svnweb.freebsd.org/changeset/base/293680

Log:
  ext4: add support for reading sparse files
  
  Add support for sparse files in ext4. Also implement read-ahead, which
  greatly increases the performance when transferring files from ext4.
  
  Both features implemented by Damjan Jovanovic.
  
  PR:		205816
  MFC after:	1 week

Modified:
  head/sys/fs/ext2fs/ext2_bmap.c
  head/sys/fs/ext2fs/ext2_extents.c
  head/sys/fs/ext2fs/ext2_extents.h
  head/sys/fs/ext2fs/ext2_vnops.c

Modified: head/sys/fs/ext2fs/ext2_bmap.c
==============================================================================
--- head/sys/fs/ext2fs/ext2_bmap.c	Mon Jan 11 18:11:06 2016	(r293679)
+++ head/sys/fs/ext2fs/ext2_bmap.c	Mon Jan 11 19:14:55 2016	(r293680)
@@ -102,9 +102,6 @@ ext4_bmapext(struct vnode *vp, int32_t b
 	fs = ip->i_e2fs;
 	lbn = bn;
 
-	/*
-	 * TODO: need to implement read ahead to improve the performance.
-	 */
 	if (runp != NULL)
 		*runp = 0;
 
@@ -112,15 +109,25 @@ ext4_bmapext(struct vnode *vp, int32_t b
 		*runb = 0;
 
 	ext4_ext_find_extent(fs, ip, lbn, &path);
-	ep = path.ep_ext;
-	if (ep == NULL)
-		ret = EIO;
-	else {
-		*bnp = fsbtodb(fs, lbn - ep->e_blk +
-		    (ep->e_start_lo | (daddr_t)ep->e_start_hi << 32));
+	if (path.ep_is_sparse) {
+		*bnp = -1;
+		if (runp != NULL)
+			*runp = path.ep_sparse_ext.e_len -
+			    (lbn - path.ep_sparse_ext.e_blk) - 1;
+	} else {
+		ep = path.ep_ext;
+		if (ep == NULL)
+			ret = EIO;
+		else {
+			*bnp = fsbtodb(fs, lbn - ep->e_blk +
+			    (ep->e_start_lo | (daddr_t)ep->e_start_hi << 32));
 
-		if (*bnp == 0)
-			*bnp = -1;
+			if (*bnp == 0)
+				*bnp = -1;
+
+			if (runp != NULL)
+				*runp = ep->e_len - (lbn - ep->e_blk) - 1;
+		}
 	}
 
 	if (path.ep_bp != NULL) {

Modified: head/sys/fs/ext2fs/ext2_extents.c
==============================================================================
--- head/sys/fs/ext2fs/ext2_extents.c	Mon Jan 11 18:11:06 2016	(r293679)
+++ head/sys/fs/ext2fs/ext2_extents.c	Mon Jan 11 19:14:55 2016	(r293680)
@@ -66,13 +66,14 @@ static void
 ext4_ext_binsearch(struct inode *ip, struct ext4_extent_path *path, daddr_t lbn)
 {
 	struct ext4_extent_header *ehp = path->ep_header;
-	struct ext4_extent *l, *r, *m;
+	struct ext4_extent *first, *l, *r, *m;
 
 	if (ehp->eh_ecount == 0)
 		return;
 
-	l = (struct ext4_extent *)(char *)(ehp + 1);
-	r = (struct ext4_extent *)(char *)(ehp + 1) + ehp->eh_ecount - 1;
+	first = (struct ext4_extent *)(char *)(ehp + 1);
+	l = first;
+	r = first + ehp->eh_ecount - 1;
 	while (l <= r) {
 		m = l + (r - l) / 2;
 		if (lbn < m->e_blk)
@@ -81,7 +82,25 @@ ext4_ext_binsearch(struct inode *ip, str
 			l = m + 1;
 	}
 
+	if (l == first) {
+		path->ep_sparse_ext.e_blk = lbn;
+		path->ep_sparse_ext.e_len = first->e_blk - lbn;
+		path->ep_sparse_ext.e_start_hi = 0;
+		path->ep_sparse_ext.e_start_lo = 0;
+		path->ep_is_sparse = 1;
+		return;
+	}
 	path->ep_ext = l - 1;
+	if (path->ep_ext->e_blk + path->ep_ext->e_len <= lbn) {
+		path->ep_sparse_ext.e_blk = lbn;
+		if (l <= (first + ehp->eh_ecount - 1))
+			path->ep_sparse_ext.e_len = l->e_blk - lbn;
+		else	// XXX: where does it end?
+			path->ep_sparse_ext.e_len = 1;
+		path->ep_sparse_ext.e_start_hi = 0;
+		path->ep_sparse_ext.e_start_lo = 0;
+		path->ep_is_sparse = 1;
+	}
 }
 
 /*
@@ -169,6 +188,7 @@ ext4_ext_find_extent(struct m_ext2fs *fs
 	path->ep_depth = i;
 	path->ep_ext = NULL;
 	path->ep_index = NULL;
+	path->ep_is_sparse = 0;
 
 	ext4_ext_binsearch(ip, path, lbn);
 	return (path);

Modified: head/sys/fs/ext2fs/ext2_extents.h
==============================================================================
--- head/sys/fs/ext2fs/ext2_extents.h	Mon Jan 11 18:11:06 2016	(r293679)
+++ head/sys/fs/ext2fs/ext2_extents.h	Mon Jan 11 19:14:55 2016	(r293680)
@@ -84,7 +84,11 @@ struct ext4_extent_cache {
 struct ext4_extent_path {
 	uint16_t ep_depth;
 	struct buf *ep_bp;
-	struct ext4_extent *ep_ext;
+	int ep_is_sparse;
+	union {
+		struct ext4_extent ep_sparse_ext;
+		struct ext4_extent *ep_ext;
+	};
 	struct ext4_extent_index *ep_index;
 	struct ext4_extent_header *ep_header;
 };

Modified: head/sys/fs/ext2fs/ext2_vnops.c
==============================================================================
--- head/sys/fs/ext2fs/ext2_vnops.c	Mon Jan 11 18:11:06 2016	(r293679)
+++ head/sys/fs/ext2fs/ext2_vnops.c	Mon Jan 11 19:14:55 2016	(r293680)
@@ -1787,6 +1787,7 @@ ext2_ioctl(struct vop_ioctl_args *ap)
 static int
 ext4_ext_read(struct vop_read_args *ap)
 {
+	static unsigned char zeroes[EXT2_MAX_BLOCK_SIZE];
 	struct vnode *vp;
 	struct inode *ip;
 	struct uio *uio;
@@ -1831,11 +1832,15 @@ ext4_ext_read(struct vop_read_args *ap)
 		switch (cache_type) {
 		case EXT4_EXT_CACHE_NO:
 			ext4_ext_find_extent(fs, ip, lbn, &path);
-			ep = path.ep_ext;
+			if (path.ep_is_sparse)
+				ep = &path.ep_sparse_ext;
+			else
+				ep = path.ep_ext;
 			if (ep == NULL)
 				return (EIO);
 
-			ext4_ext_put_cache(ip, ep, EXT4_EXT_CACHE_IN);
+			ext4_ext_put_cache(ip, ep,
+			    path.ep_is_sparse ? EXT4_EXT_CACHE_GAP : EXT4_EXT_CACHE_IN);
 
 			newblk = lbn - ep->e_blk + (ep->e_start_lo |
 			    (daddr_t)ep->e_start_hi << 32);
@@ -1848,7 +1853,7 @@ ext4_ext_read(struct vop_read_args *ap)
 
 		case EXT4_EXT_CACHE_GAP:
 			/* block has not been allocated yet */
-			return (0);
+			break;
 
 		case EXT4_EXT_CACHE_IN:
 			newblk = lbn - nex.e_blk + (nex.e_start_lo |
@@ -1859,24 +1864,34 @@ ext4_ext_read(struct vop_read_args *ap)
 			panic("%s: invalid cache type", __func__);
 		}
 
-		error = bread(ip->i_devvp, fsbtodb(fs, newblk), size, NOCRED, &bp);
-		if (error) {
-			brelse(bp);
-			return (error);
-		}
+		if (cache_type == EXT4_EXT_CACHE_GAP ||
+		    (cache_type == EXT4_EXT_CACHE_NO && path.ep_is_sparse)) {
+			if (xfersize > sizeof(zeroes))
+				xfersize = sizeof(zeroes);
+			error = uiomove(zeroes, xfersize, uio);
+			if (error)
+				return (error);
+		} else {
+			error = bread(ip->i_devvp, fsbtodb(fs, newblk), size,
+			    NOCRED, &bp);
+			if (error) {
+				brelse(bp);
+				return (error);
+			}
 
-		size -= bp->b_resid;
-		if (size < xfersize) {
-			if (size == 0) {
-				bqrelse(bp);
-				break;
+			size -= bp->b_resid;
+			if (size < xfersize) {
+				if (size == 0) {
+					bqrelse(bp);
+					break;
+				}
+				xfersize = size;
 			}
-			xfersize = size;
+			error = uiomove(bp->b_data + blkoffset, xfersize, uio);
+			bqrelse(bp);
+			if (error)
+				return (error);
 		}
-		error = uiomove(bp->b_data + blkoffset, (int)xfersize, uio);
-		bqrelse(bp);
-		if (error)
-			return (error);
 	}
 
 	return (0);


More information about the svn-src-head mailing list