2005-04-17 02:20:36 +04:00
/*
2006-06-09 08:48:12 +04:00
* Copyright ( c ) 2000 - 2006 Silicon Graphics , Inc .
2005-11-02 06:58:39 +03:00
* All Rights Reserved .
2005-04-17 02:20:36 +04:00
*
2005-11-02 06:58:39 +03:00
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
2005-04-17 02:20:36 +04:00
* published by the Free Software Foundation .
*
2005-11-02 06:58:39 +03:00
* This program is distributed in the hope that it would be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2005-04-17 02:20:36 +04:00
*
2005-11-02 06:58:39 +03:00
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write the Free Software Foundation ,
* Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
2005-04-17 02:20:36 +04:00
*/
# include "xfs.h"
# include "xfs_fs.h"
2005-11-02 06:38:42 +03:00
# include "xfs_bit.h"
2005-04-17 02:20:36 +04:00
# include "xfs_log.h"
2005-11-02 06:38:42 +03:00
# include "xfs_inum.h"
2005-04-17 02:20:36 +04:00
# include "xfs_trans.h"
# include "xfs_sb.h"
# include "xfs_ag.h"
# include "xfs_alloc.h"
# include "xfs_quota.h"
# include "xfs_mount.h"
# include "xfs_bmap_btree.h"
2005-11-02 06:38:42 +03:00
# include "xfs_alloc_btree.h"
2005-04-17 02:20:36 +04:00
# include "xfs_ialloc_btree.h"
# include "xfs_dinode.h"
# include "xfs_inode.h"
2005-11-02 06:38:42 +03:00
# include "xfs_btree.h"
2005-04-17 02:20:36 +04:00
# include "xfs_bmap.h"
# include "xfs_rtalloc.h"
# include "xfs_error.h"
# include "xfs_itable.h"
# include "xfs_rw.h"
# include "xfs_attr.h"
# include "xfs_buf_item.h"
# include "xfs_trans_space.h"
# include "xfs_utils.h"
# include "xfs_iomap.h"
2009-12-15 02:14:59 +03:00
# include "xfs_trace.h"
2005-04-17 02:20:36 +04:00
# define XFS_WRITEIO_ALIGN(mp,off) (((off) >> mp->m_writeio_log) \
< < mp - > m_writeio_log )
# define XFS_WRITE_IMAPS XFS_BMAP_MAX_NMAP
2006-01-11 07:28:28 +03:00
STATIC int
xfs_iomap_eof_align_last_fsb (
xfs_mount_t * mp ,
2007-10-11 11:34:33 +04:00
xfs_inode_t * ip ,
2006-01-11 07:28:28 +03:00
xfs_extlen_t extsize ,
xfs_fileoff_t * last_fsb )
{
xfs_fileoff_t new_last_fsb = 0 ;
2011-12-19 00:00:05 +04:00
xfs_extlen_t align = 0 ;
2006-01-11 07:28:28 +03:00
int eof , error ;
2011-12-19 00:00:05 +04:00
if ( ! XFS_IS_REALTIME_INODE ( ip ) ) {
/*
* Round up the allocation request to a stripe unit
* ( m_dalign ) boundary if the file size is > = stripe unit
* size , and we are allocating past the allocation eof .
*
* If mounted with the " -o swalloc " option the alignment is
* increased from the strip unit size to the stripe width .
*/
if ( mp - > m_swidth & & ( mp - > m_flags & XFS_MOUNT_SWALLOC ) )
align = mp - > m_swidth ;
else if ( mp - > m_dalign )
align = mp - > m_dalign ;
2011-12-19 00:00:11 +04:00
if ( align & & XFS_ISIZE ( ip ) > = XFS_FSB_TO_B ( mp , align ) )
2011-12-19 00:00:05 +04:00
new_last_fsb = roundup_64 ( * last_fsb , align ) ;
}
2006-01-11 07:28:28 +03:00
/*
* Always round up the allocation request to an extent boundary
* ( when file on a real - time subvolume or has di_extsize hint ) .
*/
if ( extsize ) {
if ( new_last_fsb )
align = roundup_64 ( new_last_fsb , extsize ) ;
else
align = extsize ;
new_last_fsb = roundup_64 ( * last_fsb , align ) ;
}
if ( new_last_fsb ) {
2007-10-11 11:34:33 +04:00
error = xfs_bmap_eof ( ip , new_last_fsb , XFS_DATA_FORK , & eof ) ;
2006-01-11 07:28:28 +03:00
if ( error )
return error ;
if ( eof )
* last_fsb = new_last_fsb ;
}
return 0 ;
}
2006-09-28 05:03:20 +04:00
STATIC int
2011-03-07 02:06:35 +03:00
xfs_alert_fsblock_zero (
2006-09-28 05:03:20 +04:00
xfs_inode_t * ip ,
xfs_bmbt_irec_t * imap )
{
2011-03-07 02:02:35 +03:00
xfs_alert_tag ( ip - > i_mount , XFS_PTAG_FSBLOCK_ZERO ,
2006-09-28 05:03:20 +04:00
" Access to block zero in inode %llu "
" start_block: %llx start_off: %llx "
" blkcnt: %llx extent-state: %x \n " ,
( unsigned long long ) ip - > i_ino ,
( unsigned long long ) imap - > br_startblock ,
( unsigned long long ) imap - > br_startoff ,
( unsigned long long ) imap - > br_blockcount ,
imap - > br_state ) ;
return EFSCORRUPTED ;
}
2010-12-10 11:42:20 +03:00
int
2005-04-17 02:20:36 +04:00
xfs_iomap_write_direct (
xfs_inode_t * ip ,
2005-05-06 00:33:40 +04:00
xfs_off_t offset ,
2005-04-17 02:20:36 +04:00
size_t count ,
2010-06-24 05:42:19 +04:00
xfs_bmbt_irec_t * imap ,
2010-12-10 11:42:19 +03:00
int nmaps )
2005-04-17 02:20:36 +04:00
{
xfs_mount_t * mp = ip - > i_mount ;
xfs_fileoff_t offset_fsb ;
xfs_fileoff_t last_fsb ;
2006-01-11 07:28:28 +03:00
xfs_filblks_t count_fsb , resaligned ;
2005-04-17 02:20:36 +04:00
xfs_fsblock_t firstfsb ;
2006-01-11 07:28:28 +03:00
xfs_extlen_t extsz , temp ;
2005-11-02 07:00:01 +03:00
int nimaps ;
2005-04-17 02:20:36 +04:00
int bmapi_flag ;
2005-06-21 09:48:47 +04:00
int quota_flag ;
2005-04-17 02:20:36 +04:00
int rt ;
xfs_trans_t * tp ;
xfs_bmap_free_t free_list ;
2006-01-11 07:28:28 +03:00
uint qblocks , resblks , resrtextents ;
2005-04-17 02:20:36 +04:00
int committed ;
2006-01-11 07:28:28 +03:00
int error ;
2005-04-17 02:20:36 +04:00
/*
* Make sure that the dquots are there . This doesn ' t hold
* the ilock across a disk read .
*/
2009-06-08 17:33:32 +04:00
error = xfs_qm_dqattach_locked ( ip , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return XFS_ERROR ( error ) ;
2006-01-11 07:28:28 +03:00
rt = XFS_IS_REALTIME_INODE ( ip ) ;
2007-06-18 10:50:37 +04:00
extsz = xfs_get_extsz_hint ( ip ) ;
2005-04-17 02:20:36 +04:00
2007-06-18 10:50:37 +04:00
offset_fsb = XFS_B_TO_FSBT ( mp , offset ) ;
last_fsb = XFS_B_TO_FSB ( mp , ( ( xfs_ufsize_t ) ( offset + count ) ) ) ;
2011-12-19 00:00:11 +04:00
if ( ( offset + count ) > XFS_ISIZE ( ip ) ) {
2008-12-22 09:56:49 +03:00
error = xfs_iomap_eof_align_last_fsb ( mp , ip , extsz , & last_fsb ) ;
2006-01-11 07:28:28 +03:00
if ( error )
goto error_out ;
2005-04-17 02:20:36 +04:00
} else {
2010-12-10 11:42:19 +03:00
if ( nmaps & & ( imap - > br_startblock = = HOLESTARTBLOCK ) )
2006-01-11 07:28:28 +03:00
last_fsb = MIN ( last_fsb , ( xfs_fileoff_t )
2010-06-24 05:42:19 +04:00
imap - > br_blockcount +
imap - > br_startoff ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-11 07:28:28 +03:00
count_fsb = last_fsb - offset_fsb ;
ASSERT ( count_fsb > 0 ) ;
resaligned = count_fsb ;
if ( unlikely ( extsz ) ) {
if ( ( temp = do_mod ( offset_fsb , extsz ) ) )
resaligned + = temp ;
if ( ( temp = do_mod ( resaligned , extsz ) ) )
resaligned + = extsz - temp ;
}
if ( unlikely ( rt ) ) {
resrtextents = qblocks = resaligned ;
resrtextents / = mp - > m_sb . sb_rextsize ;
2007-06-18 10:50:27 +04:00
resblks = XFS_DIOSTRAT_SPACE_RES ( mp , 0 ) ;
quota_flag = XFS_QMOPT_RES_RTBLKS ;
} else {
resrtextents = 0 ;
2006-01-11 07:28:28 +03:00
resblks = qblocks = XFS_DIOSTRAT_SPACE_RES ( mp , resaligned ) ;
2007-06-18 10:50:27 +04:00
quota_flag = XFS_QMOPT_RES_REGBLKS ;
}
2005-04-17 02:20:36 +04:00
/*
2005-06-21 09:48:47 +04:00
* Allocate and setup the transaction
2005-04-17 02:20:36 +04:00
*/
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
tp = xfs_trans_alloc ( mp , XFS_TRANS_DIOSTRAT ) ;
error = xfs_trans_reserve ( tp , resblks ,
2005-09-02 10:41:32 +04:00
XFS_WRITE_LOG_RES ( mp ) , resrtextents ,
2005-04-17 02:20:36 +04:00
XFS_TRANS_PERM_LOG_RES ,
XFS_WRITE_LOG_COUNT ) ;
/*
2005-06-21 09:48:47 +04:00
* Check for running out of space , note : need lock to return
2005-04-17 02:20:36 +04:00
*/
if ( error )
xfs_trans_cancel ( tp , 0 ) ;
xfs_ilock ( ip , XFS_ILOCK_EXCL ) ;
if ( error )
2005-06-21 09:48:47 +04:00
goto error_out ;
2005-04-17 02:20:36 +04:00
2009-06-08 17:33:32 +04:00
error = xfs_trans_reserve_quota_nblks ( tp , ip , qblocks , 0 , quota_flag ) ;
2006-01-11 07:28:28 +03:00
if ( error )
2005-04-17 02:20:36 +04:00
goto error1 ;
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( tp , ip , 0 ) ;
2005-04-17 02:20:36 +04:00
2011-09-19 00:40:52 +04:00
bmapi_flag = 0 ;
2011-12-19 00:00:11 +04:00
if ( offset < XFS_ISIZE ( ip ) | | extsz )
2005-04-17 02:20:36 +04:00
bmapi_flag | = XFS_BMAPI_PREALLOC ;
/*
2010-06-24 05:42:19 +04:00
* From this point onwards we overwrite the imap pointer that the
* caller gave to us .
2005-04-17 02:20:36 +04:00
*/
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( & free_list , & firstfsb ) ;
2005-06-21 09:48:47 +04:00
nimaps = 1 ;
2011-09-19 00:40:52 +04:00
error = xfs_bmapi_write ( tp , ip , offset_fsb , count_fsb , bmapi_flag ,
& firstfsb , 0 , imap , & nimaps , & free_list ) ;
2005-06-21 09:48:47 +04:00
if ( error )
2005-04-17 02:20:36 +04:00
goto error0 ;
/*
2005-06-21 09:48:47 +04:00
* Complete the transaction
2005-04-17 02:20:36 +04:00
*/
2007-02-10 10:37:16 +03:00
error = xfs_bmap_finish ( & tp , & free_list , & committed ) ;
2005-06-21 09:48:47 +04:00
if ( error )
2005-04-17 02:20:36 +04:00
goto error0 ;
2007-05-08 07:48:42 +04:00
error = xfs_trans_commit ( tp , XFS_TRANS_RELEASE_LOG_RES ) ;
2005-06-21 09:48:47 +04:00
if ( error )
2005-04-17 02:20:36 +04:00
goto error_out ;
2005-06-21 09:48:47 +04:00
/*
* Copy any maps to caller ' s array and return any error .
*/
2005-04-17 02:20:36 +04:00
if ( nimaps = = 0 ) {
2006-09-28 05:03:20 +04:00
error = ENOSPC ;
goto error_out ;
}
2010-06-24 05:42:19 +04:00
if ( ! ( imap - > br_startblock | | XFS_IS_REALTIME_INODE ( ip ) ) ) {
2011-03-07 02:06:35 +03:00
error = xfs_alert_fsblock_zero ( ip , imap ) ;
2005-04-17 02:20:36 +04:00
goto error_out ;
}
return 0 ;
2005-06-21 09:48:47 +04:00
error0 : /* Cancel bmap, unlock inode, unreserve quota blocks, cancel trans */
2005-04-17 02:20:36 +04:00
xfs_bmap_cancel ( & free_list ) ;
2009-06-08 17:33:32 +04:00
xfs_trans_unreserve_quota_nblks ( tp , ip , qblocks , 0 , quota_flag ) ;
2005-04-17 02:20:36 +04:00
2005-06-21 09:48:47 +04:00
error1 : /* Just cancel transaction */
2005-04-17 02:20:36 +04:00
xfs_trans_cancel ( tp , XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT ) ;
error_out :
return XFS_ERROR ( error ) ;
}
2006-01-11 07:28:28 +03:00
/*
2009-04-06 20:49:12 +04:00
* If the caller is doing a write at the end of the file , then extend the
* allocation out to the file system ' s write iosize . We clean up any extra
* space left over when the file is closed in xfs_inactive ( ) .
2011-01-04 03:35:03 +03:00
*
* If we find we already have delalloc preallocation beyond EOF , don ' t do more
* preallocation as it it not needed .
2006-01-11 07:28:28 +03:00
*/
STATIC int
xfs_iomap_eof_want_preallocate (
xfs_mount_t * mp ,
2007-10-11 11:34:33 +04:00
xfs_inode_t * ip ,
2006-01-11 07:28:28 +03:00
xfs_off_t offset ,
size_t count ,
xfs_bmbt_irec_t * imap ,
int nimaps ,
int * prealloc )
{
xfs_fileoff_t start_fsb ;
xfs_filblks_t count_fsb ;
xfs_fsblock_t firstblock ;
int n , error , imaps ;
2011-01-04 03:35:03 +03:00
int found_delalloc = 0 ;
2006-01-11 07:28:28 +03:00
* prealloc = 0 ;
2011-12-19 00:00:11 +04:00
if ( offset + count < = XFS_ISIZE ( ip ) )
2006-01-11 07:28:28 +03:00
return 0 ;
/*
* If there are any real blocks past eof , then don ' t
* do any speculative allocation .
*/
start_fsb = XFS_B_TO_FSBT ( mp , ( ( xfs_ufsize_t ) ( offset + count - 1 ) ) ) ;
count_fsb = XFS_B_TO_FSB ( mp , ( xfs_ufsize_t ) XFS_MAXIOFFSET ( mp ) ) ;
while ( count_fsb > 0 ) {
imaps = nimaps ;
2006-01-11 07:33:02 +03:00
firstblock = NULLFSBLOCK ;
2011-09-19 00:40:45 +04:00
error = xfs_bmapi_read ( ip , start_fsb , count_fsb , imap , & imaps ,
0 ) ;
2006-01-11 07:28:28 +03:00
if ( error )
return error ;
for ( n = 0 ; n < imaps ; n + + ) {
if ( ( imap [ n ] . br_startblock ! = HOLESTARTBLOCK ) & &
( imap [ n ] . br_startblock ! = DELAYSTARTBLOCK ) )
return 0 ;
start_fsb + = imap [ n ] . br_blockcount ;
count_fsb - = imap [ n ] . br_blockcount ;
2011-01-04 03:35:03 +03:00
if ( imap [ n ] . br_startblock = = DELAYSTARTBLOCK )
found_delalloc = 1 ;
2006-01-11 07:28:28 +03:00
}
}
2011-01-04 03:35:03 +03:00
if ( ! found_delalloc )
* prealloc = 1 ;
2006-01-11 07:28:28 +03:00
return 0 ;
}
2011-01-04 03:35:03 +03:00
/*
* If we don ' t have a user specified preallocation size , dynamically increase
* the preallocation size as the size of the file grows . Cap the maximum size
* at a single extent or less if the filesystem is near full . The closer the
* filesystem is to full , the smaller the maximum prealocation .
*/
STATIC xfs_fsblock_t
xfs_iomap_prealloc_size (
struct xfs_mount * mp ,
struct xfs_inode * ip )
{
xfs_fsblock_t alloc_blocks = 0 ;
if ( ! ( mp - > m_flags & XFS_MOUNT_DFLT_IOSIZE ) ) {
int shift = 0 ;
int64_t freesp ;
2011-01-27 04:14:12 +03:00
/*
* rounddown_pow_of_two ( ) returns an undefined result
* if we pass in alloc_blocks = 0. Hence the " + 1 " to
* ensure we always pass in a non - zero value .
*/
2011-12-19 00:00:11 +04:00
alloc_blocks = XFS_B_TO_FSB ( mp , XFS_ISIZE ( ip ) ) + 1 ;
2011-01-04 03:35:03 +03:00
alloc_blocks = XFS_FILEOFF_MIN ( MAXEXTLEN ,
rounddown_pow_of_two ( alloc_blocks ) ) ;
xfs_icsb_sync_counters ( mp , XFS_ICSB_LAZY_COUNT ) ;
freesp = mp - > m_sb . sb_fdblocks ;
if ( freesp < mp - > m_low_space [ XFS_LOWSP_5_PCNT ] ) {
shift = 2 ;
if ( freesp < mp - > m_low_space [ XFS_LOWSP_4_PCNT ] )
shift + + ;
if ( freesp < mp - > m_low_space [ XFS_LOWSP_3_PCNT ] )
shift + + ;
if ( freesp < mp - > m_low_space [ XFS_LOWSP_2_PCNT ] )
shift + + ;
if ( freesp < mp - > m_low_space [ XFS_LOWSP_1_PCNT ] )
shift + + ;
}
if ( shift )
alloc_blocks > > = shift ;
}
if ( alloc_blocks < mp - > m_writeio_blocks )
alloc_blocks = mp - > m_writeio_blocks ;
return alloc_blocks ;
}
2010-12-10 11:42:20 +03:00
int
2005-04-17 02:20:36 +04:00
xfs_iomap_write_delay (
xfs_inode_t * ip ,
2005-05-06 00:33:40 +04:00
xfs_off_t offset ,
2005-04-17 02:20:36 +04:00
size_t count ,
2010-12-10 11:42:19 +03:00
xfs_bmbt_irec_t * ret_imap )
2005-04-17 02:20:36 +04:00
{
xfs_mount_t * mp = ip - > i_mount ;
xfs_fileoff_t offset_fsb ;
xfs_fileoff_t last_fsb ;
2006-01-11 07:28:28 +03:00
xfs_off_t aligned_offset ;
xfs_fileoff_t ioalign ;
xfs_extlen_t extsz ;
2005-04-17 02:20:36 +04:00
int nimaps ;
xfs_bmbt_irec_t imap [ XFS_WRITE_IMAPS ] ;
2009-04-06 20:49:12 +04:00
int prealloc , flushed = 0 ;
2006-01-11 07:28:28 +03:00
int error ;
2005-04-17 02:20:36 +04:00
2008-04-22 11:34:00 +04:00
ASSERT ( xfs_isilocked ( ip , XFS_ILOCK_EXCL ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Make sure that the dquots are there . This doesn ' t hold
* the ilock across a disk read .
*/
2009-06-08 17:33:32 +04:00
error = xfs_qm_dqattach_locked ( ip , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( error )
return XFS_ERROR ( error ) ;
2007-06-18 10:50:37 +04:00
extsz = xfs_get_extsz_hint ( ip ) ;
2006-01-11 07:28:28 +03:00
offset_fsb = XFS_B_TO_FSBT ( mp , offset ) ;
2011-01-04 03:35:03 +03:00
2008-12-22 09:56:49 +03:00
error = xfs_iomap_eof_want_preallocate ( mp , ip , offset , count ,
2010-12-10 11:42:19 +03:00
imap , XFS_WRITE_IMAPS , & prealloc ) ;
2006-01-11 07:28:28 +03:00
if ( error )
return error ;
2005-04-17 02:20:36 +04:00
2009-04-06 20:49:12 +04:00
retry :
2006-01-11 07:28:28 +03:00
if ( prealloc ) {
2011-01-04 03:35:03 +03:00
xfs_fsblock_t alloc_blocks = xfs_iomap_prealloc_size ( mp , ip ) ;
2005-04-17 02:20:36 +04:00
aligned_offset = XFS_WRITEIO_ALIGN ( mp , ( offset + count - 1 ) ) ;
ioalign = XFS_B_TO_FSBT ( mp , aligned_offset ) ;
2011-01-04 03:35:03 +03:00
last_fsb = ioalign + alloc_blocks ;
2006-01-11 07:28:28 +03:00
} else {
last_fsb = XFS_B_TO_FSB ( mp , ( ( xfs_ufsize_t ) ( offset + count ) ) ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-11 07:28:28 +03:00
if ( prealloc | | extsz ) {
2008-12-22 09:56:49 +03:00
error = xfs_iomap_eof_align_last_fsb ( mp , ip , extsz , & last_fsb ) ;
2006-01-11 07:28:28 +03:00
if ( error )
2005-04-17 02:20:36 +04:00
return error ;
}
2006-01-11 07:28:28 +03:00
nimaps = XFS_WRITE_IMAPS ;
2011-09-19 00:40:48 +04:00
error = xfs_bmapi_delay ( ip , offset_fsb , last_fsb - offset_fsb ,
imap , & nimaps , XFS_BMAPI_ENTIRE ) ;
2011-01-04 03:35:03 +03:00
switch ( error ) {
case 0 :
case ENOSPC :
case EDQUOT :
break ;
default :
2005-04-17 02:20:36 +04:00
return XFS_ERROR ( error ) ;
2011-01-04 03:35:03 +03:00
}
2006-01-11 07:28:28 +03:00
2005-04-17 02:20:36 +04:00
/*
2011-01-04 03:35:03 +03:00
* If bmapi returned us nothing , we got either ENOSPC or EDQUOT . For
* ENOSPC , * flush all other inodes with delalloc blocks to free up
* some of the excess reserved metadata space . For both cases , retry
* without EOF preallocation .
2005-04-17 02:20:36 +04:00
*/
if ( nimaps = = 0 ) {
2009-12-15 02:14:59 +03:00
trace_xfs_delalloc_enospc ( ip , offset , count ) ;
2009-04-06 20:49:12 +04:00
if ( flushed )
2011-01-04 03:35:03 +03:00
return XFS_ERROR ( error ? error : ENOSPC ) ;
2005-04-17 02:20:36 +04:00
2011-01-04 03:35:03 +03:00
if ( error = = ENOSPC ) {
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
xfs_flush_inodes ( ip ) ;
xfs_ilock ( ip , XFS_ILOCK_EXCL ) ;
}
2009-04-06 20:49:12 +04:00
flushed = 1 ;
2005-04-17 02:20:36 +04:00
error = 0 ;
2009-04-06 20:49:12 +04:00
prealloc = 0 ;
2005-04-17 02:20:36 +04:00
goto retry ;
}
2008-04-29 06:53:21 +04:00
if ( ! ( imap [ 0 ] . br_startblock | | XFS_IS_REALTIME_INODE ( ip ) ) )
2011-03-07 02:06:35 +03:00
return xfs_alert_fsblock_zero ( ip , & imap [ 0 ] ) ;
2006-01-11 07:28:28 +03:00
* ret_imap = imap [ 0 ] ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Pass in a delayed allocate extent , convert it to real extents ;
* return to the caller the extent we create which maps on top of
* the originating callers request .
*
* Called without a lock on the inode .
2007-11-23 08:29:11 +03:00
*
* We no longer bother to look at the incoming map - all we have to
* guarantee is that whatever we allocate fills the required range .
2005-04-17 02:20:36 +04:00
*/
2010-12-10 11:42:20 +03:00
int
2005-04-17 02:20:36 +04:00
xfs_iomap_write_allocate (
xfs_inode_t * ip ,
2005-05-06 00:33:40 +04:00
xfs_off_t offset ,
2005-05-06 00:33:20 +04:00
size_t count ,
2010-12-10 11:42:19 +03:00
xfs_bmbt_irec_t * imap )
2005-04-17 02:20:36 +04:00
{
xfs_mount_t * mp = ip - > i_mount ;
xfs_fileoff_t offset_fsb , last_block ;
xfs_fileoff_t end_fsb , map_start_fsb ;
xfs_fsblock_t first_block ;
xfs_bmap_free_t free_list ;
xfs_filblks_t count_fsb ;
xfs_trans_t * tp ;
2007-11-23 08:29:11 +03:00
int nimaps , committed ;
2005-04-17 02:20:36 +04:00
int error = 0 ;
int nres ;
/*
* Make sure that the dquots are there .
*/
2009-06-08 17:33:32 +04:00
error = xfs_qm_dqattach ( ip , 0 ) ;
if ( error )
2005-04-17 02:20:36 +04:00
return XFS_ERROR ( error ) ;
2005-05-06 00:33:20 +04:00
offset_fsb = XFS_B_TO_FSBT ( mp , offset ) ;
2010-06-24 05:42:19 +04:00
count_fsb = imap - > br_blockcount ;
map_start_fsb = imap - > br_startoff ;
2005-04-17 02:20:36 +04:00
XFS_STATS_ADD ( xs_xstrat_bytes , XFS_FSB_TO_B ( mp , count_fsb ) ) ;
while ( count_fsb ! = 0 ) {
/*
* Set up a transaction with which to allocate the
* backing store for the file . Do allocations in a
* loop until we get some space in the range we are
* interested in . The other space that might be allocated
* is in the delayed allocation extent on which we sit
* but before our buffer starts .
*/
nimaps = 0 ;
while ( nimaps = = 0 ) {
tp = xfs_trans_alloc ( mp , XFS_TRANS_STRAT_WRITE ) ;
2007-06-18 10:50:27 +04:00
tp - > t_flags | = XFS_TRANS_RESERVE ;
2005-04-17 02:20:36 +04:00
nres = XFS_EXTENTADD_SPACE_RES ( mp , XFS_DATA_FORK ) ;
error = xfs_trans_reserve ( tp , nres ,
XFS_WRITE_LOG_RES ( mp ) ,
0 , XFS_TRANS_PERM_LOG_RES ,
XFS_WRITE_LOG_COUNT ) ;
if ( error ) {
xfs_trans_cancel ( tp , 0 ) ;
return XFS_ERROR ( error ) ;
}
xfs_ilock ( ip , XFS_ILOCK_EXCL ) ;
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( tp , ip , 0 ) ;
2005-04-17 02:20:36 +04:00
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( & free_list , & first_block ) ;
2005-04-17 02:20:36 +04:00
/*
2007-11-23 08:29:11 +03:00
* it is possible that the extents have changed since
* we did the read call as we dropped the ilock for a
* while . We have to be careful about truncates or hole
* punchs here - we are not allowed to allocate
* non - delalloc blocks here .
*
* The only protection against truncation is the pages
* for the range we are being asked to convert are
* locked and hence a truncate will block on them
* first .
*
* As a result , if we go beyond the range we really
* need and hit an delalloc extent boundary followed by
* a hole while we have excess blocks in the map , we
* will fill the hole incorrectly and overrun the
* transaction reservation .
*
* Using a single map prevents this as we are forced to
* check each map we look for overlap with the desired
* range and abort as soon as we find it . Also , given
* that we only return a single map , having one beyond
* what we can return is probably a bit silly .
*
* We also need to check that we don ' t go beyond EOF ;
* this is a truncate optimisation as a truncate sets
* the new file size before block on the pages we
* currently have locked under writeback . Because they
* are about to be tossed , we don ' t need to write them
* back . . . .
2005-04-17 02:20:36 +04:00
*/
2007-11-23 08:29:11 +03:00
nimaps = 1 ;
2011-12-19 00:00:11 +04:00
end_fsb = XFS_B_TO_FSB ( mp , XFS_ISIZE ( ip ) ) ;
2008-04-10 06:21:59 +04:00
error = xfs_bmap_last_offset ( NULL , ip , & last_block ,
XFS_DATA_FORK ) ;
if ( error )
goto trans_cancel ;
2005-04-17 02:20:36 +04:00
last_block = XFS_FILEOFF_MAX ( last_block , end_fsb ) ;
if ( ( map_start_fsb + count_fsb ) > last_block ) {
count_fsb = last_block - map_start_fsb ;
if ( count_fsb = = 0 ) {
error = EAGAIN ;
goto trans_cancel ;
}
}
2010-06-24 05:42:19 +04:00
/*
* From this point onwards we overwrite the imap
* pointer that the caller gave to us .
*/
2011-09-19 00:40:52 +04:00
error = xfs_bmapi_write ( tp , ip , map_start_fsb ,
count_fsb , 0 , & first_block , 1 ,
imap , & nimaps , & free_list ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto trans_cancel ;
2007-02-10 10:37:16 +03:00
error = xfs_bmap_finish ( & tp , & free_list , & committed ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto trans_cancel ;
2007-05-08 07:48:42 +04:00
error = xfs_trans_commit ( tp , XFS_TRANS_RELEASE_LOG_RES ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto error0 ;
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
}
/*
* See if we were able to allocate an extent that
* covers at least part of the callers request
*/
2010-06-24 05:42:19 +04:00
if ( ! ( imap - > br_startblock | | XFS_IS_REALTIME_INODE ( ip ) ) )
2011-03-07 02:06:35 +03:00
return xfs_alert_fsblock_zero ( ip , imap ) ;
2008-04-29 06:53:21 +04:00
2010-06-24 05:42:19 +04:00
if ( ( offset_fsb > = imap - > br_startoff ) & &
( offset_fsb < ( imap - > br_startoff +
imap - > br_blockcount ) ) ) {
2007-11-23 08:29:11 +03:00
XFS_STATS_INC ( xs_xstrat_quick ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-11-23 08:29:11 +03:00
/*
* So far we have not mapped the requested part of the
2005-04-17 02:20:36 +04:00
* file , just surrounding data , try again .
*/
2010-06-24 05:42:19 +04:00
count_fsb - = imap - > br_blockcount ;
map_start_fsb = imap - > br_startoff + imap - > br_blockcount ;
2005-04-17 02:20:36 +04:00
}
trans_cancel :
xfs_bmap_cancel ( & free_list ) ;
xfs_trans_cancel ( tp , XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT ) ;
error0 :
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
return XFS_ERROR ( error ) ;
}
int
xfs_iomap_write_unwritten (
xfs_inode_t * ip ,
2005-05-06 00:33:40 +04:00
xfs_off_t offset ,
2005-04-17 02:20:36 +04:00
size_t count )
{
xfs_mount_t * mp = ip - > i_mount ;
xfs_fileoff_t offset_fsb ;
xfs_filblks_t count_fsb ;
xfs_filblks_t numblks_fsb ;
2006-01-11 07:28:28 +03:00
xfs_fsblock_t firstfsb ;
int nimaps ;
xfs_trans_t * tp ;
xfs_bmbt_irec_t imap ;
xfs_bmap_free_t free_list ;
uint resblks ;
2005-04-17 02:20:36 +04:00
int committed ;
int error ;
2009-12-15 02:14:59 +03:00
trace_xfs_unwritten_convert ( ip , offset , count ) ;
2005-04-17 02:20:36 +04:00
offset_fsb = XFS_B_TO_FSBT ( mp , offset ) ;
count_fsb = XFS_B_TO_FSB ( mp , ( xfs_ufsize_t ) offset + count ) ;
count_fsb = ( xfs_filblks_t ) ( count_fsb - offset_fsb ) ;
2008-06-27 07:32:53 +04:00
/*
* Reserve enough blocks in this transaction for two complete extent
* btree splits . We may be converting the middle part of an unwritten
* extent and in this case we will insert two new extents in the btree
* each of which could cause a full split .
*
* This reservation amount will be used in the first call to
* xfs_bmbt_split ( ) to select an AG with enough space to satisfy the
* rest of the operation .
*/
2006-01-11 07:28:28 +03:00
resblks = XFS_DIOSTRAT_SPACE_RES ( mp , 0 ) < < 1 ;
2005-04-17 02:20:36 +04:00
2006-01-11 07:28:28 +03:00
do {
2005-04-17 02:20:36 +04:00
/*
* set up a transaction to convert the range of extents
* from unwritten to real . Do allocations in a loop until
* we have covered the range passed in .
2009-10-19 08:00:03 +04:00
*
* Note that we open code the transaction allocation here
* to pass KM_NOFS - - we can ' t risk to recursing back into
* the filesystem here as we might be asked to write out
* the same inode that we complete here and might deadlock
* on the iolock .
2005-04-17 02:20:36 +04:00
*/
2009-10-19 08:00:03 +04:00
xfs_wait_for_freeze ( mp , SB_FREEZE_TRANS ) ;
tp = _xfs_trans_alloc ( mp , XFS_TRANS_STRAT_WRITE , KM_NOFS ) ;
2007-06-18 10:50:27 +04:00
tp - > t_flags | = XFS_TRANS_RESERVE ;
2006-01-11 07:28:28 +03:00
error = xfs_trans_reserve ( tp , resblks ,
2005-04-17 02:20:36 +04:00
XFS_WRITE_LOG_RES ( mp ) , 0 ,
XFS_TRANS_PERM_LOG_RES ,
XFS_WRITE_LOG_COUNT ) ;
if ( error ) {
xfs_trans_cancel ( tp , 0 ) ;
2006-09-28 05:03:20 +04:00
return XFS_ERROR ( error ) ;
2005-04-17 02:20:36 +04:00
}
xfs_ilock ( ip , XFS_ILOCK_EXCL ) ;
2011-09-19 19:00:54 +04:00
xfs_trans_ijoin ( tp , ip , 0 ) ;
2005-04-17 02:20:36 +04:00
/*
* Modify the unwritten extent state of the buffer .
*/
2009-01-15 08:22:07 +03:00
xfs_bmap_init ( & free_list , & firstfsb ) ;
2005-04-17 02:20:36 +04:00
nimaps = 1 ;
2011-09-19 00:40:52 +04:00
error = xfs_bmapi_write ( tp , ip , offset_fsb , count_fsb ,
XFS_BMAPI_CONVERT , & firstfsb ,
2010-06-23 12:11:15 +04:00
1 , & imap , & nimaps , & free_list ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto error_on_bmapi_transaction ;
2007-02-10 10:37:16 +03:00
error = xfs_bmap_finish ( & ( tp ) , & ( free_list ) , & committed ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto error_on_bmapi_transaction ;
2007-05-08 07:48:42 +04:00
error = xfs_trans_commit ( tp , XFS_TRANS_RELEASE_LOG_RES ) ;
2005-04-17 02:20:36 +04:00
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
if ( error )
2006-09-28 05:03:20 +04:00
return XFS_ERROR ( error ) ;
2008-04-29 06:53:21 +04:00
if ( ! ( imap . br_startblock | | XFS_IS_REALTIME_INODE ( ip ) ) )
2011-03-07 02:06:35 +03:00
return xfs_alert_fsblock_zero ( ip , & imap ) ;
2005-04-17 02:20:36 +04:00
if ( ( numblks_fsb = imap . br_blockcount ) = = 0 ) {
/*
* The numblks_fsb value should always get
* smaller , otherwise the loop is stuck .
*/
ASSERT ( imap . br_blockcount ) ;
break ;
}
offset_fsb + = numblks_fsb ;
count_fsb - = numblks_fsb ;
} while ( count_fsb > 0 ) ;
return 0 ;
error_on_bmapi_transaction :
xfs_bmap_cancel ( & free_list ) ;
xfs_trans_cancel ( tp , ( XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT ) ) ;
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
return XFS_ERROR ( error ) ;
}