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"
# include "xfs_log.h"
# 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"
2012-02-29 13:53:50 +04:00
# include "xfs_inode_item.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_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"
2012-11-06 18:50:38 +04:00
# include "xfs_icache.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
2012-03-27 18:34:50 +04:00
error = xfs_qm_dqattach ( 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 )
2012-03-27 18:34:50 +04:00
return XFS_ERROR ( error ) ;
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
*/
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
*/
2012-03-27 18:34:50 +04:00
if ( error ) {
2005-04-17 02:20:36 +04:00
xfs_trans_cancel ( tp , 0 ) ;
2012-03-27 18:34:50 +04:00
return XFS_ERROR ( error ) ;
}
2005-04-17 02:20:36 +04:00
xfs_ilock ( ip , XFS_ILOCK_EXCL ) ;
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 )
2012-03-27 18:34:50 +04:00
goto out_trans_cancel ;
2005-04-17 02:20:36 +04:00
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 )
2012-03-27 18:34:50 +04:00
goto out_bmap_cancel ;
2005-04-17 02:20:36 +04:00
/*
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 )
2012-03-27 18:34:50 +04:00
goto out_bmap_cancel ;
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 )
2012-03-27 18:34:50 +04:00
goto out_unlock ;
2005-04-17 02:20:36 +04:00
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 ) {
2012-03-27 18:34:50 +04:00
error = XFS_ERROR ( ENOSPC ) ;
goto out_unlock ;
2006-09-28 05:03:20 +04:00
}
2012-03-27 18:34:50 +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
2012-03-27 18:34:50 +04:00
out_unlock :
xfs_iunlock ( ip , XFS_ILOCK_EXCL ) ;
return error ;
2005-04-17 02:20:36 +04:00
2012-03-27 18:34:50 +04:00
out_bmap_cancel :
2005-04-17 02:20:36 +04:00
xfs_bmap_cancel ( & free_list ) ;
2012-05-08 14:48:53 +04:00
xfs_trans_unreserve_quota_nblks ( tp , ip , ( long ) qblocks , 0 , quota_flag ) ;
2012-03-27 18:34:50 +04:00
out_trans_cancel :
2005-04-17 02:20:36 +04:00
xfs_trans_cancel ( tp , XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT ) ;
2012-03-27 18:34:50 +04:00
goto out_unlock ;
2005-04-17 02:20:36 +04:00
}
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 ) ) ) ;
2012-06-08 09:44:54 +04:00
count_fsb = XFS_B_TO_FSB ( mp , mp - > m_super - > s_maxbytes ) ;
2006-01-11 07:28:28 +03:00
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 ;
}
2013-02-11 09:05:01 +04:00
/*
* Determine the initial size of the preallocation . We are beyond the current
* EOF here , but we need to take into account whether this is a sparse write or
* an extending write when determining the preallocation size . Hence we need to
* look up the extent that ends at the current write offset and use the result
* to determine the preallocation size .
*
* If the extent is a hole , then preallocation is essentially disabled .
* Otherwise we take the size of the preceeding data extent as the basis for the
* preallocation size . If the size of the extent is greater than half the
* maximum extent length , then use the current offset as the basis . This ensures
* that for large files the preallocation size always extends to MAXEXTLEN
* rather than falling short due to things like stripe unit / width alignment of
* real extents .
*/
2013-02-24 23:04:37 +04:00
STATIC xfs_fsblock_t
2013-02-11 09:05:01 +04:00
xfs_iomap_eof_prealloc_initial_size (
struct xfs_mount * mp ,
struct xfs_inode * ip ,
xfs_off_t offset ,
xfs_bmbt_irec_t * imap ,
int nimaps )
{
xfs_fileoff_t start_fsb ;
int imaps = 1 ;
int error ;
ASSERT ( nimaps > = imaps ) ;
/* if we are using a specific prealloc size, return now */
if ( mp - > m_flags & XFS_MOUNT_DFLT_IOSIZE )
return 0 ;
/*
* As we write multiple pages , the offset will always align to the
* start of a page and hence point to a hole at EOF . i . e . if the size is
* 4096 bytes , we only have one block at FSB 0 , but XFS_B_TO_FSB ( 4096 )
* will return FSB 1. Hence if there are blocks in the file , we want to
* point to the block prior to the EOF block and not the hole that maps
* directly at @ offset .
*/
start_fsb = XFS_B_TO_FSB ( mp , offset ) ;
if ( start_fsb )
start_fsb - - ;
error = xfs_bmapi_read ( ip , start_fsb , 1 , imap , & imaps , XFS_BMAPI_ENTIRE ) ;
if ( error )
return 0 ;
ASSERT ( imaps = = 1 ) ;
if ( imap [ 0 ] . br_startblock = = HOLESTARTBLOCK )
return 0 ;
if ( imap [ 0 ] . br_blockcount < = ( MAXEXTLEN > > 1 ) )
2013-02-19 19:24:41 +04:00
return imap [ 0 ] . br_blockcount < < 1 ;
2013-02-11 09:05:01 +04:00
return XFS_B_TO_FSB ( mp , offset ) ;
}
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 ,
2013-02-11 09:05:01 +04:00
struct xfs_inode * ip ,
xfs_off_t offset ,
struct xfs_bmbt_irec * imap ,
int nimaps )
2011-01-04 03:35:03 +03:00
{
xfs_fsblock_t alloc_blocks = 0 ;
2013-03-18 18:51:43 +04:00
int shift = 0 ;
int64_t freesp ;
2011-01-04 03:35:03 +03:00
2013-02-11 09:05:01 +04:00
alloc_blocks = xfs_iomap_eof_prealloc_initial_size ( mp , ip , offset ,
imap , nimaps ) ;
2013-03-18 18:51:43 +04:00
if ( ! alloc_blocks )
goto check_writeio ;
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 + + ;
2011-01-04 03:35:03 +03:00
}
2013-03-18 18:51:43 +04:00
if ( shift )
alloc_blocks > > = shift ;
/*
* If we are still trying to allocate more space than is
* available , squash the prealloc hard . This can happen if we
* have a large file on a small filesystem and the above
* lowspace thresholds are smaller than MAXEXTLEN .
*/
while ( alloc_blocks & & alloc_blocks > = freesp )
alloc_blocks > > = 4 ;
2011-01-04 03:35:03 +03:00
2013-03-18 18:51:43 +04:00
check_writeio :
2011-01-04 03:35:03 +03:00
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 ] ;
2012-10-08 14:56:04 +04:00
int prealloc ;
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 ) ;
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 ) {
2013-02-11 09:05:01 +04:00
xfs_fsblock_t alloc_blocks ;
alloc_blocks = xfs_iomap_prealloc_size ( mp , ip , offset , imap ,
XFS_WRITE_IMAPS ) ;
2011-01-04 03:35:03 +03:00
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
2012-04-29 16:43:19 +04:00
/*
* Make sure preallocation does not create extents beyond the range we
* actually support in this filesystem .
*/
2012-06-08 09:44:53 +04:00
if ( last_fsb > XFS_B_TO_FSB ( mp , mp - > m_super - > s_maxbytes ) )
last_fsb = XFS_B_TO_FSB ( mp , mp - > m_super - > s_maxbytes ) ;
2012-04-29 16:43:19 +04:00
ASSERT ( last_fsb > offset_fsb ) ;
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
/*
2012-10-08 14:56:04 +04:00
* If bmapi returned us nothing , we got either ENOSPC or EDQUOT . Retry
2011-01-04 03:35:03 +03:00
* 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 ) ;
2012-10-08 14:56:04 +04:00
if ( prealloc ) {
prealloc = 0 ;
error = 0 ;
goto retry ;
2011-01-04 03:35:03 +03:00
}
2012-10-08 14:56:04 +04:00
return XFS_ERROR ( error ? error : ENOSPC ) ;
2005-04-17 02:20:36 +04:00
}
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
2012-11-06 18:50:38 +04:00
/*
* Tag the inode as speculatively preallocated so we can reclaim this
* space on demand , if necessary .
*/
if ( prealloc )
xfs_inode_set_eofblocks_tag ( ip ) ;
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 ,
2012-10-05 05:06:58 +04:00
count_fsb ,
XFS_BMAPI_STACK_SWITCH ,
& first_block , 1 ,
2011-09-19 00:40:52 +04:00
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 ;
2012-02-29 13:53:50 +04:00
xfs_fsize_t i_size ;
2006-01-11 07:28:28 +03:00
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
*/
2012-06-12 18:20:39 +04:00
sb_start_intwrite ( mp - > m_super ) ;
2009-10-19 08:00:03 +04:00
tp = _xfs_trans_alloc ( mp , XFS_TRANS_STRAT_WRITE , KM_NOFS ) ;
2012-06-12 18:20:39 +04:00
tp - > t_flags | = XFS_TRANS_RESERVE | XFS_TRANS_FREEZE_PROT ;
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 ;
2012-02-29 13:53:50 +04:00
/*
* Log the updated inode size as we go . We have to be careful
* to only log it up to the actual write offset if it is
* halfway into a block .
*/
i_size = XFS_FSB_TO_B ( mp , offset_fsb + count_fsb ) ;
if ( i_size > offset + count )
i_size = offset + count ;
i_size = xfs_new_eof ( ip , i_size ) ;
if ( i_size ) {
ip - > i_d . di_size = i_size ;
xfs_trans_log_inode ( tp , ip , XFS_ILOG_CORE ) ;
}
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 ) ;
}