|
|
| version 1.37, 2007/06/08 16:06:22 | version 1.38, 2007/07/06 04:02:21 |
|---|---|
| Line 2540 xfs_dm_mkdir_by_handle( | Line 2540 xfs_dm_mkdir_by_handle( |
| } | } |
| /* | |
| * Probe and Punch | |
| * | |
| * Hole punching alignment is based on the underlying device base | |
| * allocation size or PAGE_CACHE_SIZE. Because it is not defined | |
| * in the DMAPI spec, we can align how we choose here. Round | |
| * inwards (offset up and length down) to the block, extent or | |
| * page size whichever is bigger. Our DMAPI implementation rounds | |
| * the hole geometry strictly inwards. If this is not possible, | |
| * return EINVAL for both for xfs_dm_probe_hole and xfs_dm_punch_hole | |
| * which differs from the DMAPI spec. | |
| * Note that length = 0 is special - it means "punch to EOF" | |
| * and at that point we treat the punch as remove everything | |
| * past offset (including preallocation past EOF). | |
| */ | |
| STATIC int | |
| xfs_dm_round_hole( | |
| dm_off_t offset, | |
| dm_size_t length, | |
| dm_size_t align, | |
| xfs_fsize_t filesize, | |
| dm_off_t *roff, | |
| dm_size_t *rlen) | |
| { | |
| dm_off_t off = offset; | |
| dm_size_t len = length; | |
| /* Try to round offset up to the nearest boundary */ | |
| *roff = roundup_64(off, align); | |
| if ((*roff >= filesize) || (len && (len < align))) | |
| return -EINVAL; | |
| if ((len == 0) || ((off + len) == filesize)) { | |
| /* punch to EOF */ | |
| *rlen = 0; | |
| } else { | |
| /* Round length down to the nearest boundary. */ | |
| ASSERT(len >= align); | |
| ASSERT(align > (*roff - off)); | |
| len -= *roff - off; | |
| *rlen = len - do_mod(len, align); | |
| if (*rlen == 0) | |
| return -EINVAL; /* requested length is too small */ | |
| } | |
| #ifdef CONFIG_DMAPI_DEBUG | |
| printk("xfs_dm_round_hole: off %lu, len %ld, align %lu, " | |
| "filesize %llu, roff %ld, rlen %ld\n", | |
| offset, length, align, filesize, *roff, *rlen); | |
| #endif | |
| return 0; /* hole geometry successfully rounded */ | |
| } | |
| /* ARGSUSED */ | /* ARGSUSED */ |
| STATIC int | STATIC int |
| xfs_dm_probe_hole( | xfs_dm_probe_hole( |
| struct inode *inode, | struct inode *inode, |
| dm_right_t right, | dm_right_t right, |
| dm_off_t off, | dm_off_t off, |
| dm_size_t len, /* we ignore this for now */ | dm_size_t len, |
| dm_off_t __user *roffp, | dm_off_t __user *roffp, |
| dm_size_t __user *rlenp) | dm_size_t __user *rlenp) |
| { | { |
| Line 2556 xfs_dm_probe_hole( | Line 2610 xfs_dm_probe_hole( |
| xfs_mount_t *mp; | xfs_mount_t *mp; |
| uint lock_flags; | uint lock_flags; |
| xfs_fsize_t realsize; | xfs_fsize_t realsize; |
| u_int bsize; | dm_size_t align; |
| bhv_vnode_t *vp = vn_from_inode(inode); | bhv_vnode_t *vp = vn_from_inode(inode); |
| int error; | |
| /* Returns negative errors to DMAPI */ | /* Returns negative errors to DMAPI */ |
| if (right < DM_RIGHT_SHARED) | if (right < DM_RIGHT_SHARED) |
| return(-EACCES); | return -EACCES; |
| ip = xfs_vtoi(vp); | ip = xfs_vtoi(vp); |
| if ((ip->i_d.di_mode & S_IFMT) != S_IFREG) | if ((ip->i_d.di_mode & S_IFMT) != S_IFREG) |
| return(-EINVAL); | return -EINVAL; |
| mp = ip->i_mount; | mp = ip->i_mount; |
| bsize = mp->m_sb.sb_blocksize; | |
| lock_flags = XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL; | lock_flags = XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL; |
| xfs_ilock(ip, lock_flags); | xfs_ilock(ip, lock_flags); |
| realsize = ip->i_size; | realsize = ip->i_size; |
| xfs_iunlock(ip, lock_flags); | xfs_iunlock(ip, lock_flags); |
| if (off >= realsize) | |
| return(-E2BIG); | |
| roff = (off + bsize-1) & ~(bsize-1); | if ((off + len) > realsize) |
| rlen = 0; /* Only support punches to EOF for now */ | return -E2BIG; |
| align = xfs_get_extsz_hint(ip); | |
| if (align == 0) | |
| align = 1; | |
| align <<= mp->m_sb.sb_blocklog; | |
| align = MAX(align, PAGE_CACHE_SIZE); | |
| error = xfs_dm_round_hole(off, len, align, realsize, &roff, &rlen); | |
| if (error) | |
| return error; | |
| if (copy_to_user( roffp, &roff, sizeof(roff))) | if (copy_to_user( roffp, &roff, sizeof(roff))) |
| return(-EFAULT); | return -EFAULT; |
| if (copy_to_user( rlenp, &rlen, sizeof(rlen))) | if (copy_to_user( rlenp, &rlen, sizeof(rlen))) |
| return(-EFAULT); | return -EFAULT; |
| return(0); | return(0); |
| } | } |
| Line 2598 xfs_dm_punch_hole( | Line 2660 xfs_dm_punch_hole( |
| xfs_flock64_t bf; | xfs_flock64_t bf; |
| int error = 0; | int error = 0; |
| bhv_desc_t *xbdp; | bhv_desc_t *xbdp; |
| xfs_inode_t *xip; | xfs_inode_t *ip; |
| xfs_mount_t *mp; | xfs_mount_t *mp; |
| u_int bsize; | dm_size_t align; |
| xfs_fsize_t realsize; | xfs_fsize_t realsize; |
| bhv_vnode_t *vp = vn_from_inode(inode); | bhv_vnode_t *vp = vn_from_inode(inode); |
| int punch_to_eof = 0; | dm_off_t roff; |
| dm_size_t rlen; | |
| /* Returns negative errors to DMAPI */ | /* Returns negative errors to DMAPI */ |
| Line 2623 xfs_dm_punch_hole( | Line 2686 xfs_dm_punch_hole( |
| VNODE_POSITION_XFS, VNODE_POSITION_XFS); | VNODE_POSITION_XFS, VNODE_POSITION_XFS); |
| ASSERT(xbdp); | ASSERT(xbdp); |
| xip = xfs_vtoi(vp); | ip = xfs_vtoi(vp); |
| mp = xip->i_mount; | mp = ip->i_mount; |
| bsize = mp->m_sb.sb_blocksize; | |
| if (off & (bsize-1)) { | |
| error = -EAGAIN; | |
| goto put_and_out; | |
| } | |
| if (len & (bsize-1)) { | |
| error = -EAGAIN; | |
| goto put_and_out; | |
| } | |
| down_rw_sems(inode, DM_SEM_FLAG_WR); | down_rw_sems(inode, DM_SEM_FLAG_WR); |
| xfs_ilock(xip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); | xfs_ilock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); |
| realsize = xip->i_size; | realsize = ip->i_size; |
| xfs_iunlock(ip, XFS_ILOCK_EXCL); | |
| align = xfs_get_extsz_hint(ip); | |
| if (align == 0) | |
| align = 1; | |
| if ((off >= realsize) || ((off + len) > realsize)) { | align <<= mp->m_sb.sb_blocklog; |
| xfs_iunlock(xip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); | align = MAX(align, PAGE_CACHE_SIZE); |
| if ((off + len) > realsize) { | |
| xfs_iunlock(ip, XFS_IOLOCK_EXCL); | |
| error = -E2BIG; | error = -E2BIG; |
| goto up_and_out; | goto up_and_out; |
| } | } |
| if (len == 0) | |
| punch_to_eof = 1; | |
| /* | if ((off + len) == realsize) |
| * When we are punching to EOF, we have to make sure we punch the | len = 0; |
| * last partial block that contains EOF. Round up the length to | |
| * make sure we punch the block and not just zero it. | |
| */ | |
| if (punch_to_eof) | |
| len = roundup_64((realsize - off), bsize); | |
| xfs_iunlock(xip, XFS_ILOCK_EXCL); | error = xfs_dm_round_hole(off, len, align, realsize, &roff, &rlen); |
| if (error || (off != roff) || (len != rlen)) { | |
| xfs_iunlock(ip, XFS_IOLOCK_EXCL); | |
| error = -EINVAL; | |
| goto up_and_out; | |
| } | |
| bf.l_type = 0; | bf.l_type = 0; |
| bf.l_whence = 0; | bf.l_whence = 0; |
| bf.l_start = (xfs_off_t)off; | bf.l_start = (xfs_off_t)off; |
| bf.l_len = (xfs_off_t)len; | if (len) { |
| bf.l_len = len; | |
| } | |
| else { | |
| /* | |
| * When we are punching to EOF, we have to make sure we punch | |
| * the last partial block that contains EOF. Round up | |
| * the length to make sure we punch the block and not just | |
| * zero it. | |
| */ | |
| bf.l_len = roundup_64((realsize - off), mp->m_sb.sb_blocksize); | |
| } | |
| #ifdef CONFIG_DMAPI_DEBUG | |
| printk("xfs_dm_punch_hole: off %lu, len %ld, align %lu\n", | |
| off, len, align); | |
| #endif | |
| error = xfs_change_file_space(xbdp, XFS_IOC_UNRESVSP, &bf, | error = xfs_change_file_space(xbdp, XFS_IOC_UNRESVSP, &bf, |
| (xfs_off_t)off, sys_cred, ATTR_DMI|ATTR_NOLOCK); | (xfs_off_t)off, sys_cred, ATTR_DMI|ATTR_NOLOCK); |
| Line 2671 xfs_dm_punch_hole( | Line 2746 xfs_dm_punch_hole( |
| * may have been (speculatively) preallocated. No point in | * may have been (speculatively) preallocated. No point in |
| * leaving them around if we are migrating the file.... | * leaving them around if we are migrating the file.... |
| */ | */ |
| if (!error && punch_to_eof) { | if (!error && (len == 0)) { |
| error = xfs_free_eofblocks(mp, xip, XFS_FREE_EOF_NOLOCK); | error = xfs_free_eofblocks(mp, ip, XFS_FREE_EOF_NOLOCK); |
| if (!error) { | if (!error) { |
| /* Update linux inode block count after free above */ | /* Update linux inode block count after free above */ |
| inode->i_blocks = XFS_FSB_TO_BB(mp, | inode->i_blocks = XFS_FSB_TO_BB(mp, |
| xip->i_d.di_nblocks + xip->i_delayed_blks); | ip->i_d.di_nblocks + ip->i_delayed_blks); |
| } | } |
| } | } |
| /* Let threads in send_data_event know we punched the file. */ | /* Let threads in send_data_event know we punched the file. */ |
| xip->i_iocore.io_dmstate++; | ip->i_iocore.io_dmstate++; |
| xfs_iunlock(xip, XFS_IOLOCK_EXCL); | xfs_iunlock(ip, XFS_IOLOCK_EXCL); |
| VMODIFY(vp); | VMODIFY(vp); |
| up_and_out: | up_and_out: |
| up_rw_sems(inode, DM_SEM_FLAG_WR); | up_rw_sems(inode, DM_SEM_FLAG_WR); |
| put_and_out: | |
| put_write_access(inode); | put_write_access(inode); |
| return error; | return error; |