2019-04-28 18:34:02 +03:00
// SPDX-License-Identifier: GPL-2.0
2016-06-21 02:23:11 +03:00
/*
* Copyright ( C ) 2010 Red Hat , Inc .
2018-06-20 01:10:57 +03:00
* Copyright ( c ) 2016 - 2018 Christoph Hellwig .
2016-06-21 02:23:11 +03:00
*/
# include <linux/module.h>
# include <linux/compiler.h>
# include <linux/fs.h>
# include <linux/iomap.h>
2019-11-22 03:14:49 +03:00
# include "trace.h"
2017-02-04 01:47:37 +03:00
2016-06-21 02:23:11 +03:00
/*
* Execute a iomap write on a segment of the mapping that spans a
* contiguous range of pages that have identical block mapping state .
*
* This avoids the need to map pages individually , do individual allocations
* for each page and most importantly avoid the need for filesystem specific
* locking per page . Instead , all the operations are amortised over the entire
* range of pages . It is assumed that the filesystems will lock whatever
* resources they require in the iomap_begin call , and release them in the
* iomap_end call .
*/
2016-09-19 04:24:49 +03:00
loff_t
2016-06-21 02:23:11 +03:00
iomap_apply ( struct inode * inode , loff_t pos , loff_t length , unsigned flags ,
2017-01-28 10:20:26 +03:00
const struct iomap_ops * ops , void * data , iomap_actor_t actor )
2016-06-21 02:23:11 +03:00
{
2019-10-19 02:44:10 +03:00
struct iomap iomap = { . type = IOMAP_HOLE } ;
struct iomap srcmap = { . type = IOMAP_HOLE } ;
2016-06-21 02:23:11 +03:00
loff_t written = 0 , ret ;
2019-10-19 02:44:10 +03:00
u64 end ;
2016-06-21 02:23:11 +03:00
2019-11-22 03:14:49 +03:00
trace_iomap_apply ( inode , pos , length , flags , ops , actor , _RET_IP_ ) ;
2016-06-21 02:23:11 +03:00
/*
* Need to map a range from start position for length bytes . This can
* span multiple pages - it is only guaranteed to return a range of a
* single type of pages ( e . g . all into a hole , all mapped or all
* unwritten ) . Failure at this point has nothing to undo .
*
* If allocation is required for this range , reserve the space now so
* that the allocation is guaranteed to succeed later on . Once we copy
* the data into the page cache pages , then we cannot fail otherwise we
* expose transient stale data . If the reserve fails , we can safely
* back out at this point as there is nothing to undo .
*/
2019-10-19 02:44:10 +03:00
ret = ops - > iomap_begin ( inode , pos , length , flags , & iomap , & srcmap ) ;
2016-06-21 02:23:11 +03:00
if ( ret )
return ret ;
2020-07-06 20:49:27 +03:00
if ( WARN_ON ( iomap . offset > pos ) ) {
written = - EIO ;
goto out ;
}
if ( WARN_ON ( iomap . length = = 0 ) ) {
written = - EIO ;
goto out ;
}
2016-06-21 02:23:11 +03:00
2019-11-22 03:14:49 +03:00
trace_iomap_apply_dstmap ( inode , & iomap ) ;
if ( srcmap . type ! = IOMAP_HOLE )
trace_iomap_apply_srcmap ( inode , & srcmap ) ;
2016-06-21 02:23:11 +03:00
/*
* Cut down the length to the one actually provided by the filesystem ,
* as it might not be able to give us the whole size that we requested .
*/
2019-10-19 02:44:10 +03:00
end = iomap . offset + iomap . length ;
if ( srcmap . type ! = IOMAP_HOLE )
end = min ( end , srcmap . offset + srcmap . length ) ;
if ( pos + length > end )
length = end - pos ;
2016-06-21 02:23:11 +03:00
/*
2019-10-19 02:44:10 +03:00
* Now that we have guaranteed that the space allocation will succeed ,
2016-06-21 02:23:11 +03:00
* we can do the copy - in page by page without having to worry about
* failures exposing transient data .
2019-10-19 02:44:10 +03:00
*
* To support COW operations , we read in data for partially blocks from
* the srcmap if the file system filled it in . In that case we the
* length needs to be limited to the earlier of the ends of the iomaps .
* If the file system did not provide a srcmap we pass in the normal
* iomap into the actors so that they don ' t need to have special
* handling for the two cases .
2016-06-21 02:23:11 +03:00
*/
2019-10-19 02:44:10 +03:00
written = actor ( inode , pos , length , data , & iomap ,
srcmap . type ! = IOMAP_HOLE ? & srcmap : & iomap ) ;
2016-06-21 02:23:11 +03:00
2020-07-06 20:49:27 +03:00
out :
2016-06-21 02:23:11 +03:00
/*
* Now the data has been copied , commit the range we ' ve copied . This
* should not fail unless the filesystem has had a fatal error .
*/
2016-08-17 01:42:34 +03:00
if ( ops - > iomap_end ) {
ret = ops - > iomap_end ( inode , pos , length ,
written > 0 ? written : 0 ,
flags , & iomap ) ;
}
2016-06-21 02:23:11 +03:00
return written ? written : ret ;
}