diff -u --recursive --new-file --exclude-from=linux-2.6.31-rc6//Documentation/dontdiff --exclude='*.lds' --exclude-from=linux-2.6.31-rc6//.gitignore linux-2.6.31-rc6/block/blk-barrier.c linux/block/blk-barrier.c --- linux-2.6.31-rc6/block/blk-barrier.c 2009-08-16 09:36:36.431146680 -0400 +++ linux/block/blk-barrier.c 2009-08-16 09:20:15.164578531 -0400 @@ -425,3 +425,4 @@ DISCARD_BARRIER, NULL); } EXPORT_SYMBOL(blkdev_issue_discard); +EXPORT_SYMBOL(__blkdev_issue_discard); diff -u --recursive --new-file --exclude-from=linux-2.6.31-rc6//Documentation/dontdiff --exclude='*.lds' --exclude-from=linux-2.6.31-rc6//.gitignore linux-2.6.31-rc6/fs/xfs/linux-2.6/xfs_ioctl.c linux/fs/xfs/linux-2.6/xfs_ioctl.c --- linux-2.6.31-rc6/fs/xfs/linux-2.6/xfs_ioctl.c 2009-08-16 09:16:39.000433070 -0400 +++ linux/fs/xfs/linux-2.6/xfs_ioctl.c 2009-08-16 09:30:38.973683042 -0400 @@ -1274,6 +1274,31 @@ return 0; } +int +xfs_ioc_trim( + struct xfs_mount *mp, + __uint32_t *argp) +{ + xfs_agnumber_t agno; + int error = 0; + __uint32_t minlen; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (get_user(minlen, argp)) + return -EFAULT; + + down_read(&mp->m_peraglock); + for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) { + error = -xfs_trim_extents(mp, agno, minlen); + if (error) + break; + } + up_read(&mp->m_peraglock); + + return error; +} + /* * Note: some of the ioctl's return positive numbers as a * byte count indicating success, such as readlink_by_handle. @@ -1523,6 +1548,9 @@ error = xfs_errortag_clearall(mp, 1); return -error; + case XFS_IOC_TRIM: + return xfs_ioc_trim(mp, arg); + default: return -ENOTTY; } diff -u --recursive --new-file --exclude-from=linux-2.6.31-rc6//Documentation/dontdiff --exclude='*.lds' --exclude-from=linux-2.6.31-rc6//.gitignore linux-2.6.31-rc6/fs/xfs/linux-2.6/xfs_ioctl32.c linux/fs/xfs/linux-2.6/xfs_ioctl32.c --- linux-2.6.31-rc6/fs/xfs/linux-2.6/xfs_ioctl32.c 2009-06-09 23:05:27.000000000 -0400 +++ linux/fs/xfs/linux-2.6/xfs_ioctl32.c 2009-08-16 09:31:21.005588977 -0400 @@ -539,6 +539,7 @@ void __user *arg = (void __user *)p; int ioflags = 0; int error; + extern int xfs_ioc_trim(struct xfs_mount *mp, __uint32_t *argp); if (filp->f_mode & FMODE_NOCMTIME) ioflags |= IO_INVIS; @@ -564,6 +565,8 @@ case XFS_IOC_ERROR_INJECTION: case XFS_IOC_ERROR_CLEARALL: return xfs_file_ioctl(filp, cmd, p); + case XFS_IOC_TRIM: + return xfs_ioc_trim(mp, arg); #ifndef BROKEN_X86_ALIGNMENT /* These are handled fine if no alignment issues */ case XFS_IOC_ALLOCSP: diff -u --recursive --new-file --exclude-from=linux-2.6.31-rc6//Documentation/dontdiff --exclude='*.lds' --exclude-from=linux-2.6.31-rc6//.gitignore linux-2.6.31-rc6/fs/xfs/xfs_alloc.h linux/fs/xfs/xfs_alloc.h --- linux-2.6.31-rc6/fs/xfs/xfs_alloc.h 2009-06-09 23:05:27.000000000 -0400 +++ linux/fs/xfs/xfs_alloc.h 2009-08-16 09:20:15.167913313 -0400 @@ -215,4 +215,7 @@ xfs_fsblock_t bno, /* starting block number of extent */ xfs_extlen_t len); /* length of extent */ +int xfs_trim_extents(struct xfs_mount *mp, xfs_agnumber_t agno, + xfs_extlen_t minlen); + #endif /* __XFS_ALLOC_H__ */ diff -u --recursive --new-file --exclude-from=linux-2.6.31-rc6//Documentation/dontdiff --exclude='*.lds' --exclude-from=linux-2.6.31-rc6//.gitignore linux-2.6.31-rc6/fs/xfs/xfs_fs.h linux/fs/xfs/xfs_fs.h --- linux-2.6.31-rc6/fs/xfs/xfs_fs.h 2009-08-16 09:16:39.017099926 -0400 +++ linux/fs/xfs/xfs_fs.h 2009-08-16 09:20:15.171246419 -0400 @@ -475,6 +475,7 @@ #define XFS_IOC_ATTRMULTI_BY_HANDLE _IOW ('X', 123, struct xfs_fsop_attrmulti_handlereq) #define XFS_IOC_FSGEOMETRY _IOR ('X', 124, struct xfs_fsop_geom) #define XFS_IOC_GOINGDOWN _IOR ('X', 125, __uint32_t) +#define XFS_IOC_TRIM _IOR ('X', 126, __uint32_t) /* XFS_IOC_GETFSUUID ---------- deprecated 140 */ --- linux-2.6.31-rc6/fs/xfs/xfs_alloc.c 2009-06-09 23:05:27.000000000 -0400 +++ linux/fs/xfs/xfs_alloc.c 2009-08-16 09:44:51.073580438 -0400 @@ -39,6 +39,9 @@ #include "xfs_alloc.h" #include "xfs_error.h" +int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, + sector_t nr_sects, gfp_t gfp_mask, + unsigned type, struct completion *completion); #define XFS_ABSDIFF(a,b) (((a) <= (b)) ? ((b) - (a)) : ((a) - (b))) @@ -2609,6 +2612,97 @@ return error; } +STATIC int +xfs_trim_extent( + struct xfs_mount *mp, + xfs_agnumber_t agno, + xfs_agblock_t fbno, + xfs_extlen_t flen) +{ + xfs_daddr_t blkno = XFS_AGB_TO_DADDR(mp, agno, fbno); + sector_t nblks = XFS_FSB_TO_BB(mp, flen); + int error; + DECLARE_COMPLETION_ONSTACK(done); + + xfs_fs_cmn_err(CE_NOTE, mp, "discarding sectors [0x%llx-0x%llx]", + blkno, (u64)nblks); + + error = -__blkdev_issue_discard(mp->m_ddev_targp->bt_bdev, + blkno, nblks, GFP_NOFS, DISCARD_BARRIER, &done); + if (error && error != EOPNOTSUPP) + xfs_fs_cmn_err(CE_NOTE, mp, "discard failed, error %d", error); + return error; +} + +/* + * Notify the underlying block device about our free extent map. + * + * This walks all free extents above a minimum threshold and notifies the + * underlying device that these blocks are unused. That information is + * useful for SSDs or thinly provisioned storage in high end arrays or + * virtualization scenarios. + */ +int +xfs_trim_extents( + struct xfs_mount *mp, + xfs_agnumber_t agno, + xfs_extlen_t minlen) /* minimum extent size to bother */ +{ + struct xfs_btree_cur *cur; /* cursor for the by-block btree */ + struct xfs_buf *agbp; /* AGF buffer pointer */ + xfs_agblock_t bno; /* block the for next search */ + xfs_agblock_t fbno; /* start block of found extent */ + xfs_extlen_t flen; /* length of found extent */ + int error; + int i; + + error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp); + if (error) + return error; + + bno = 0; + for (;;) { + cur = xfs_allocbt_init_cursor(mp, NULL, agbp, agno, + XFS_BTNUM_BNO); + + error = xfs_alloc_lookup_ge(cur, bno, minlen, &i); + if (error) + goto error0; + if (!i) { + /* + * No more free extents found: done. + */ + xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR); + break; + } + + error = xfs_alloc_get_rec(cur, &fbno, &flen, &i); + if (error) + goto error0; + XFS_WANT_CORRUPTED_GOTO(i == 1, error0); + + /* + * Pass if the freespace extent isn't long enough to bother. + */ + if (flen >= minlen) { + error = xfs_trim_extent(mp, agno, fbno, flen); + if (error) { + xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR); + break; + } + } + + xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR); + bno = fbno + flen; + } + +out: + xfs_buf_relse(agbp); + return error; +error0: + xfs_btree_del_cursor(cur, XFS_BTREE_ERROR); + goto out; +} /* * AG Busy list management