Skip to content

Commit 3974320

Browse files
AstralBobAndreas Gruenbacher
authored andcommitted
GFS2: Implement iomap for block_map
This patch implements iomap for block mapping, and switches the block_map function to use it under the covers. The additional IOMAP_F_BOUNDARY iomap flag indicates when iomap has reached a "metadata boundary" and fetching the next mapping is likely to incur an additional I/O. This flag is used for setting the bh buffer boundary flag. Signed-off-by: Bob Peterson <[email protected]> Signed-off-by: Andreas Gruenbacher <[email protected]>
1 parent 5f8bd44 commit 3974320

File tree

4 files changed

+276
-69
lines changed

4 files changed

+276
-69
lines changed

fs/gfs2/bmap.c

Lines changed: 205 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/blkdev.h>
1414
#include <linux/gfs2_ondisk.h>
1515
#include <linux/crc32.h>
16+
#include <linux/iomap.h>
1617

1718
#include "gfs2.h"
1819
#include "incore.h"
@@ -505,47 +506,46 @@ static inline unsigned int hptrs(struct gfs2_sbd *sdp, const unsigned int hgt)
505506
* Returns: errno on error
506507
*/
507508

508-
static int gfs2_bmap_alloc(struct inode *inode, const sector_t lblock,
509-
bool zero_new, struct metapath *mp,
510-
const size_t maxlen, sector_t *dblock,
511-
unsigned *dblks)
509+
static int gfs2_iomap_alloc(struct inode *inode, struct iomap *iomap,
510+
unsigned flags, struct metapath *mp)
512511
{
513512
struct gfs2_inode *ip = GFS2_I(inode);
514513
struct gfs2_sbd *sdp = GFS2_SB(inode);
515514
struct super_block *sb = sdp->sd_vfs;
516515
struct buffer_head *dibh = mp->mp_bh[0];
517516
u64 bn;
518517
unsigned n, i, blks, alloced = 0, iblks = 0, branch_start = 0;
518+
unsigned dblks = 0;
519519
unsigned ptrs_per_blk;
520520
const unsigned end_of_metadata = mp->mp_fheight - 1;
521521
int ret;
522-
int eob = 0;
523522
enum alloc_state state;
524523
__be64 *ptr;
525524
__be64 zero_bn = 0;
525+
size_t maxlen = iomap->length >> inode->i_blkbits;
526526

527527
BUG_ON(mp->mp_aheight < 1);
528528
BUG_ON(dibh == NULL);
529529

530-
*dblock = 0;
531-
*dblks = 0;
532530
gfs2_trans_add_meta(ip->i_gl, dibh);
533531

534532
if (mp->mp_fheight == mp->mp_aheight) {
535533
struct buffer_head *bh;
534+
int eob;
535+
536536
/* Bottom indirect block exists, find unalloced extent size */
537537
ptr = metapointer(end_of_metadata, mp);
538538
bh = mp->mp_bh[end_of_metadata];
539-
*dblks = gfs2_extent_length(bh->b_data, bh->b_size, ptr,
540-
maxlen, &eob);
541-
BUG_ON(*dblks < 1);
539+
dblks = gfs2_extent_length(bh->b_data, bh->b_size, ptr,
540+
maxlen, &eob);
541+
BUG_ON(dblks < 1);
542542
state = ALLOC_DATA;
543543
} else {
544544
/* Need to allocate indirect blocks */
545545
ptrs_per_blk = mp->mp_fheight > 1 ? sdp->sd_inptrs :
546546
sdp->sd_diptrs;
547-
*dblks = min(maxlen, (size_t)(ptrs_per_blk -
548-
mp->mp_list[end_of_metadata]));
547+
dblks = min(maxlen, (size_t)(ptrs_per_blk -
548+
mp->mp_list[end_of_metadata]));
549549
if (mp->mp_fheight == ip->i_height) {
550550
/* Writing into existing tree, extend tree down */
551551
iblks = mp->mp_fheight - mp->mp_aheight;
@@ -561,7 +561,7 @@ static int gfs2_bmap_alloc(struct inode *inode, const sector_t lblock,
561561

562562
/* start of the second part of the function (state machine) */
563563

564-
blks = *dblks + iblks;
564+
blks = dblks + iblks;
565565
i = mp->mp_aheight;
566566
do {
567567
int error;
@@ -618,74 +618,153 @@ static int gfs2_bmap_alloc(struct inode *inode, const sector_t lblock,
618618
break;
619619
/* Tree complete, adding data blocks */
620620
case ALLOC_DATA:
621-
BUG_ON(n > *dblks);
621+
BUG_ON(n > dblks);
622622
BUG_ON(mp->mp_bh[end_of_metadata] == NULL);
623623
gfs2_trans_add_meta(ip->i_gl, mp->mp_bh[end_of_metadata]);
624-
*dblks = n;
624+
dblks = n;
625625
ptr = metapointer(end_of_metadata, mp);
626-
*dblock = bn;
626+
iomap->addr = bn << inode->i_blkbits;
627+
iomap->flags |= IOMAP_F_NEW;
627628
while (n-- > 0)
628629
*ptr++ = cpu_to_be64(bn++);
629-
if (zero_new) {
630-
ret = sb_issue_zeroout(sb, *dblock, *dblks,
631-
GFP_NOFS);
630+
if (flags & IOMAP_ZERO) {
631+
ret = sb_issue_zeroout(sb, iomap->addr >> inode->i_blkbits,
632+
dblks, GFP_NOFS);
632633
if (ret) {
633634
fs_err(sdp,
634635
"Failed to zero data buffers\n");
636+
flags &= ~IOMAP_ZERO;
635637
}
636638
}
637639
break;
638640
}
639-
} while ((state != ALLOC_DATA) || !(*dblock));
641+
} while (iomap->addr == IOMAP_NULL_ADDR);
640642

643+
iomap->length = (u64)dblks << inode->i_blkbits;
641644
ip->i_height = mp->mp_fheight;
642645
gfs2_add_inode_blocks(&ip->i_inode, alloced);
643646
gfs2_dinode_out(ip, mp->mp_bh[0]->b_data);
644647
return 0;
645648
}
646649

647650
/**
648-
* gfs2_block_map - Map a block from an inode to a disk block
651+
* hole_size - figure out the size of a hole
649652
* @inode: The inode
650-
* @lblock: The logical block number
651-
* @bh_map: The bh to be mapped
652-
* @create: True if its ok to alloc blocks to satify the request
653+
* @lblock: The logical starting block number
654+
* @mp: The metapath
653655
*
654-
* Sets buffer_mapped() if successful, sets buffer_boundary() if a
655-
* read of metadata will be required before the next block can be
656-
* mapped. Sets buffer_new() if new blocks were allocated.
656+
* Returns: The hole size in bytes
657657
*
658-
* Returns: errno
659658
*/
659+
static u64 hole_size(struct inode *inode, sector_t lblock, struct metapath *mp)
660+
{
661+
struct gfs2_inode *ip = GFS2_I(inode);
662+
struct gfs2_sbd *sdp = GFS2_SB(inode);
663+
struct metapath mp_eof;
664+
u64 factor = 1;
665+
int hgt;
666+
u64 holesz = 0;
667+
const __be64 *first, *end, *ptr;
668+
const struct buffer_head *bh;
669+
u64 lblock_stop = (i_size_read(inode) - 1) >> inode->i_blkbits;
670+
int zeroptrs;
671+
bool done = false;
672+
673+
/* Get another metapath, to the very last byte */
674+
find_metapath(sdp, lblock_stop, &mp_eof, ip->i_height);
675+
for (hgt = ip->i_height - 1; hgt >= 0 && !done; hgt--) {
676+
bh = mp->mp_bh[hgt];
677+
if (bh) {
678+
zeroptrs = 0;
679+
first = metapointer(hgt, mp);
680+
end = (const __be64 *)(bh->b_data + bh->b_size);
681+
682+
for (ptr = first; ptr < end; ptr++) {
683+
if (*ptr) {
684+
done = true;
685+
break;
686+
} else {
687+
zeroptrs++;
688+
}
689+
}
690+
} else {
691+
zeroptrs = sdp->sd_inptrs;
692+
}
693+
if (factor * zeroptrs >= lblock_stop - lblock + 1) {
694+
holesz = lblock_stop - lblock + 1;
695+
break;
696+
}
697+
holesz += factor * zeroptrs;
660698

661-
int gfs2_block_map(struct inode *inode, sector_t lblock,
662-
struct buffer_head *bh_map, int create)
699+
factor *= sdp->sd_inptrs;
700+
if (hgt && (mp->mp_list[hgt - 1] < mp_eof.mp_list[hgt - 1]))
701+
(mp->mp_list[hgt - 1])++;
702+
}
703+
return holesz << inode->i_blkbits;
704+
}
705+
706+
static void gfs2_stuffed_iomap(struct inode *inode, struct iomap *iomap)
707+
{
708+
struct gfs2_inode *ip = GFS2_I(inode);
709+
710+
iomap->addr = (ip->i_no_addr << inode->i_blkbits) +
711+
sizeof(struct gfs2_dinode);
712+
iomap->offset = 0;
713+
iomap->length = i_size_read(inode);
714+
iomap->type = IOMAP_MAPPED;
715+
iomap->flags = IOMAP_F_DATA_INLINE;
716+
}
717+
718+
/**
719+
* gfs2_iomap_begin - Map blocks from an inode to disk blocks
720+
* @inode: The inode
721+
* @pos: Starting position in bytes
722+
* @length: Length to map, in bytes
723+
* @flags: iomap flags
724+
* @iomap: The iomap structure
725+
*
726+
* Returns: errno
727+
*/
728+
int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length,
729+
unsigned flags, struct iomap *iomap)
663730
{
664731
struct gfs2_inode *ip = GFS2_I(inode);
665732
struct gfs2_sbd *sdp = GFS2_SB(inode);
733+
struct metapath mp = { .mp_aheight = 1, };
666734
unsigned int factor = sdp->sd_sb.sb_bsize;
667-
const size_t maxlen = bh_map->b_size >> inode->i_blkbits;
668735
const u64 *arr = sdp->sd_heightsize;
669736
__be64 *ptr;
670-
u64 size;
671-
struct metapath mp;
737+
sector_t lblock;
738+
sector_t lend;
672739
int ret;
673740
int eob;
674741
unsigned int len;
675742
struct buffer_head *bh;
676743
u8 height;
677-
bool zero_new = false;
678-
sector_t dblock = 0;
679-
unsigned dblks;
680744

681-
BUG_ON(maxlen == 0);
745+
trace_gfs2_iomap_start(ip, pos, length, flags);
746+
if (!length) {
747+
ret = -EINVAL;
748+
goto out;
749+
}
682750

683-
memset(&mp, 0, sizeof(mp));
684-
bmap_lock(ip, create);
685-
clear_buffer_mapped(bh_map);
686-
clear_buffer_new(bh_map);
687-
clear_buffer_boundary(bh_map);
688-
trace_gfs2_bmap(ip, bh_map, lblock, create, 1);
751+
if ((flags & IOMAP_REPORT) && gfs2_is_stuffed(ip)) {
752+
gfs2_stuffed_iomap(inode, iomap);
753+
if (pos >= iomap->length)
754+
return -ENOENT;
755+
ret = 0;
756+
goto out;
757+
}
758+
759+
lblock = pos >> inode->i_blkbits;
760+
lend = (pos + length + sdp->sd_sb.sb_bsize - 1) >> inode->i_blkbits;
761+
762+
iomap->offset = lblock << inode->i_blkbits;
763+
iomap->addr = IOMAP_NULL_ADDR;
764+
iomap->type = IOMAP_HOLE;
765+
iomap->length = (u64)(lend - lblock) << inode->i_blkbits;
766+
iomap->flags = IOMAP_F_MERGED;
767+
bmap_lock(ip, 0);
689768

690769
/*
691770
* Directory data blocks have a struct gfs2_meta_header header, so the
@@ -699,56 +778,114 @@ int gfs2_block_map(struct inode *inode, sector_t lblock,
699778

700779
ret = gfs2_meta_inode_buffer(ip, &mp.mp_bh[0]);
701780
if (ret)
702-
goto out;
781+
goto out_release;
703782

704783
height = ip->i_height;
705-
size = (lblock + 1) * factor;
706-
while (size > arr[height])
784+
while ((lblock + 1) * factor > arr[height])
707785
height++;
708786
find_metapath(sdp, lblock, &mp, height);
709-
mp.mp_aheight = 1;
710787
if (height > ip->i_height || gfs2_is_stuffed(ip))
711788
goto do_alloc;
789+
712790
ret = lookup_metapath(ip, &mp);
713791
if (ret < 0)
714-
goto out;
792+
goto out_release;
793+
715794
if (mp.mp_aheight != ip->i_height)
716795
goto do_alloc;
796+
717797
ptr = metapointer(ip->i_height - 1, &mp);
718798
if (*ptr == 0)
719799
goto do_alloc;
720-
map_bh(bh_map, inode->i_sb, be64_to_cpu(*ptr));
800+
801+
iomap->type = IOMAP_MAPPED;
802+
iomap->addr = be64_to_cpu(*ptr) << inode->i_blkbits;
803+
721804
bh = mp.mp_bh[ip->i_height - 1];
722-
len = gfs2_extent_length(bh->b_data, bh->b_size, ptr, maxlen, &eob);
723-
bh_map->b_size = (len << inode->i_blkbits);
805+
len = gfs2_extent_length(bh->b_data, bh->b_size, ptr, lend - lblock, &eob);
724806
if (eob)
725-
set_buffer_boundary(bh_map);
807+
iomap->flags |= IOMAP_F_BOUNDARY;
808+
iomap->length = (u64)len << inode->i_blkbits;
809+
726810
ret = 0;
727-
out:
811+
812+
out_release:
728813
release_metapath(&mp);
729-
trace_gfs2_bmap(ip, bh_map, lblock, create, ret);
730-
bmap_unlock(ip, create);
814+
bmap_unlock(ip, 0);
815+
out:
816+
trace_gfs2_iomap_end(ip, iomap, ret);
731817
return ret;
732818

733819
do_alloc:
734-
/* All allocations are done here, firstly check create flag */
735-
if (!create) {
736-
BUG_ON(gfs2_is_stuffed(ip));
820+
if (!(flags & IOMAP_WRITE)) {
821+
if (pos >= i_size_read(inode)) {
822+
ret = -ENOENT;
823+
goto out_release;
824+
}
737825
ret = 0;
738-
goto out;
826+
iomap->length = hole_size(inode, lblock, &mp);
827+
goto out_release;
739828
}
740829

741-
/* At this point ret is the tree depth of already allocated blocks */
830+
ret = gfs2_iomap_alloc(inode, iomap, flags, &mp);
831+
goto out_release;
832+
}
833+
834+
/**
835+
* gfs2_block_map - Map a block from an inode to a disk block
836+
* @inode: The inode
837+
* @lblock: The logical block number
838+
* @bh_map: The bh to be mapped
839+
* @create: True if its ok to alloc blocks to satify the request
840+
*
841+
* Sets buffer_mapped() if successful, sets buffer_boundary() if a
842+
* read of metadata will be required before the next block can be
843+
* mapped. Sets buffer_new() if new blocks were allocated.
844+
*
845+
* Returns: errno
846+
*/
847+
848+
int gfs2_block_map(struct inode *inode, sector_t lblock,
849+
struct buffer_head *bh_map, int create)
850+
{
851+
struct gfs2_inode *ip = GFS2_I(inode);
852+
struct iomap iomap;
853+
int ret, flags = 0;
854+
855+
clear_buffer_mapped(bh_map);
856+
clear_buffer_new(bh_map);
857+
clear_buffer_boundary(bh_map);
858+
trace_gfs2_bmap(ip, bh_map, lblock, create, 1);
859+
860+
if (create)
861+
flags |= IOMAP_WRITE;
742862
if (buffer_zeronew(bh_map))
743-
zero_new = true;
744-
ret = gfs2_bmap_alloc(inode, lblock, zero_new, &mp, maxlen, &dblock,
745-
&dblks);
746-
if (ret == 0) {
747-
map_bh(bh_map, inode->i_sb, dblock);
748-
bh_map->b_size = dblks << inode->i_blkbits;
749-
set_buffer_new(bh_map);
863+
flags |= IOMAP_ZERO;
864+
ret = gfs2_iomap_begin(inode, (loff_t)lblock << inode->i_blkbits,
865+
bh_map->b_size, flags, &iomap);
866+
if (ret) {
867+
if (!create && ret == -ENOENT) {
868+
/* Return unmapped buffer beyond the end of file. */
869+
ret = 0;
870+
}
871+
goto out;
872+
}
873+
874+
if (iomap.length > bh_map->b_size) {
875+
iomap.length = bh_map->b_size;
876+
iomap.flags &= ~IOMAP_F_BOUNDARY;
750877
}
751-
goto out;
878+
if (iomap.addr != IOMAP_NULL_ADDR)
879+
map_bh(bh_map, inode->i_sb, iomap.addr >> inode->i_blkbits);
880+
bh_map->b_size = iomap.length;
881+
if (iomap.flags & IOMAP_F_BOUNDARY)
882+
set_buffer_boundary(bh_map);
883+
if (iomap.flags & IOMAP_F_NEW)
884+
set_buffer_new(bh_map);
885+
886+
out:
887+
trace_gfs2_bmap(ip, bh_map, lblock, create, ret);
888+
return ret;
752889
}
753890

754891
/*

0 commit comments

Comments
 (0)