@@ -586,27 +586,35 @@ xfs_add_to_ioend(
ioend->io_size += bh->b_size;
}
-STATIC void
-xfs_map_buffer(
+sector_t
+xfs_imap_to_sector(
struct inode *inode,
- struct buffer_head *bh,
struct xfs_bmbt_irec *imap,
xfs_off_t offset)
{
- sector_t bn;
- struct xfs_mount *m = XFS_I(inode)->i_mount;
- xfs_off_t iomap_offset = XFS_FSB_TO_B(m, imap->br_startoff);
- xfs_daddr_t iomap_bn = xfs_fsb_to_db(XFS_I(inode), imap->br_startblock);
+ struct xfs_mount *mp = XFS_I(inode)->i_mount;
+ xfs_off_t iomap_offset;
+ xfs_daddr_t iomap_bn;
ASSERT(imap->br_startblock != HOLESTARTBLOCK);
ASSERT(imap->br_startblock != DELAYSTARTBLOCK);
- bn = (iomap_bn >> (inode->i_blkbits - BBSHIFT)) +
- ((offset - iomap_offset) >> inode->i_blkbits);
+ iomap_bn = xfs_fsb_to_db(XFS_I(inode), imap->br_startblock);
+ iomap_offset = XFS_FSB_TO_B(mp, imap->br_startoff);
- ASSERT(bn || XFS_IS_REALTIME_INODE(XFS_I(inode)));
+ return iomap_bn + BTOBB(offset - iomap_offset);
+}
- bh->b_blocknr = bn;
+STATIC void
+xfs_map_buffer(
+ struct inode *inode,
+ struct buffer_head *bh,
+ struct xfs_bmbt_irec *imap,
+ xfs_off_t offset)
+{
+ bh->b_blocknr = xfs_imap_to_sector(inode, imap, offset) >>
+ (inode->i_blkbits - BBSHIFT);
+ ASSERT(bh->b_blocknr || XFS_IS_REALTIME_INODE(XFS_I(inode)));
set_buffer_mapped(bh);
}
@@ -617,11 +625,7 @@ xfs_map_at_offset(
struct xfs_bmbt_irec *imap,
xfs_off_t offset)
{
- ASSERT(imap->br_startblock != HOLESTARTBLOCK);
- ASSERT(imap->br_startblock != DELAYSTARTBLOCK);
-
xfs_map_buffer(inode, bh, imap, offset);
- set_buffer_mapped(bh);
clear_buffer_delay(bh);
clear_buffer_unwritten(bh);
}
@@ -1396,7 +1400,8 @@ __xfs_get_blocks(
if (create &&
(!nimaps ||
(imap.br_startblock == HOLESTARTBLOCK ||
- imap.br_startblock == DELAYSTARTBLOCK))) {
+ imap.br_startblock == DELAYSTARTBLOCK) ||
+ (IS_DAX(inode) && ISUNWRITTEN(&imap)))) {
if (direct || xfs_get_extsz_hint(ip)) {
/*
* Drop the ilock in preparation for starting the block
@@ -1441,6 +1446,9 @@ __xfs_get_blocks(
goto out_unlock;
}
+ if (IS_DAX(inode))
+ ASSERT(!ISUNWRITTEN(&imap));
+
/* trim mapping down to size requested */
if (direct || size > (1 << inode->i_blkbits))
xfs_map_trim_size(inode, iblock, bh_result,
@@ -18,6 +18,8 @@
#ifndef __XFS_AOPS_H__
#define __XFS_AOPS_H__
+struct xfs_bmbt_irec;
+
extern mempool_t *xfs_ioend_pool;
/*
@@ -62,4 +64,7 @@ void xfs_end_io_dax_write(struct buffer_head *bh, int uptodate);
extern void xfs_count_page_state(struct page *, int *, int *);
+sector_t xfs_imap_to_sector(struct inode *inode, struct xfs_bmbt_irec *imap,
+ xfs_off_t offset);
+
#endif /* __XFS_AOPS_H__ */
@@ -131,6 +131,7 @@ xfs_iomap_write_direct(
uint qblocks, resblks, resrtextents;
int committed;
int error;
+ int bmapi_flags = XFS_BMAPI_PREALLOC;
error = xfs_qm_dqattach(ip, 0);
if (error)
@@ -196,13 +197,26 @@ xfs_iomap_write_direct(
xfs_trans_ijoin(tp, ip, 0);
/*
+ * For DAX, we do not allocate unwritten extents, but instead we zero
+ * the block before we commit the transaction. Hence if we are mapping
+ * unwritten extents here, we need to convert them to written so that we
+ * don't need an unwritten extent callback here.
+ *
+ * Block zeroing for DAX is effectively a memset operation and so should
+ * not block on anything when we call it after the block allocation or
+ * conversion before we commit the transaction.
+ */
+ if (IS_DAX(VFS_I(ip)))
+ bmapi_flags = XFS_BMAPI_CONVERT;
+
+ /*
* From this point onwards we overwrite the imap pointer that the
* caller gave to us.
*/
xfs_bmap_init(&free_list, &firstfsb);
nimaps = 1;
error = xfs_bmapi_write(tp, ip, offset_fsb, count_fsb,
- XFS_BMAPI_PREALLOC, &firstfsb, 0,
+ bmapi_flags, &firstfsb, 0,
imap, &nimaps, &free_list);
if (error)
goto out_bmap_cancel;
@@ -213,6 +227,28 @@ xfs_iomap_write_direct(
error = xfs_bmap_finish(&tp, &free_list, &committed);
if (error)
goto out_bmap_cancel;
+
+ /* DAX needs to zero the entire allocated extent here */
+ if (IS_DAX(VFS_I(ip)) && nimaps) {
+ sector_t sector = xfs_imap_to_sector(VFS_I(ip), imap, offset);
+
+ ASSERT(!ISUNWRITTEN(imap));
+ ASSERT(nimaps == 1);
+ error = dax_clear_blocks(VFS_I(ip),
+ sector >> (VFS_I(ip)->i_blkbits - BBSHIFT),
+ XFS_FSB_TO_B(mp, imap->br_blockcount));
+ if (error) {
+ xfs_warn(mp,
+ "err %d, off/cnt %lld/%ld, sector %ld, bytes %lld, im.stblk %lld, im.stoff %lld, im.blkcnt %lld",
+ error, offset, count,
+ xfs_imap_to_sector(VFS_I(ip), imap, offset),
+ XFS_FSB_TO_B(mp, imap->br_blockcount),
+ imap->br_startblock, imap->br_startoff,
+ imap->br_blockcount);
+ goto out_trans_cancel;
+ }
+ }
+
error = xfs_trans_commit(tp);
if (error)
goto out_unlock;