in btt.c [1311:1424]
static int btt_write_pg(struct btt *btt, struct bio_integrity_payload *bip,
sector_t sector, struct page *page, unsigned int off,
unsigned int len)
{
int ret = 0;
struct arena_info *arena = NULL;
u32 premap = 0, old_postmap, new_postmap, lane = 0, i;
struct log_entry log;
int sub;
while (len) {
u32 cur_len;
int e_flag;
retry:
lane = nd_region_acquire_lane(btt->nd_region);
ret = lba_to_arena(btt, sector, &premap, &arena);
if (ret)
goto out_lane;
cur_len = min(btt->sector_size, len);
if ((arena->flags & IB_FLAG_ERROR_MASK) != 0) {
ret = -EIO;
goto out_lane;
}
if (btt_is_badblock(btt, arena, arena->freelist[lane].block))
arena->freelist[lane].has_err = 1;
if (mutex_is_locked(&arena->err_lock)
|| arena->freelist[lane].has_err) {
nd_region_release_lane(btt->nd_region, lane);
ret = arena_clear_freelist_error(arena, lane);
if (ret)
return ret;
/* OK to acquire a different lane/free block */
goto retry;
}
new_postmap = arena->freelist[lane].block;
/* Wait if the new block is being read from */
for (i = 0; i < arena->nfree; i++)
while (arena->rtt[i] == (RTT_VALID | new_postmap))
cpu_relax();
if (new_postmap >= arena->internal_nlba) {
ret = -EIO;
goto out_lane;
}
ret = btt_data_write(arena, new_postmap, page, off, cur_len);
if (ret)
goto out_lane;
if (bip) {
ret = btt_rw_integrity(btt, bip, arena, new_postmap,
WRITE);
if (ret)
goto out_lane;
}
lock_map(arena, premap);
ret = btt_map_read(arena, premap, &old_postmap, NULL, &e_flag,
NVDIMM_IO_ATOMIC);
if (ret)
goto out_map;
if (old_postmap >= arena->internal_nlba) {
ret = -EIO;
goto out_map;
}
if (e_flag)
set_e_flag(old_postmap);
log.lba = cpu_to_le32(premap);
log.old_map = cpu_to_le32(old_postmap);
log.new_map = cpu_to_le32(new_postmap);
log.seq = cpu_to_le32(arena->freelist[lane].seq);
sub = arena->freelist[lane].sub;
ret = btt_flog_write(arena, lane, sub, &log);
if (ret)
goto out_map;
ret = btt_map_write(arena, premap, new_postmap, 0, 0,
NVDIMM_IO_ATOMIC);
if (ret)
goto out_map;
unlock_map(arena, premap);
nd_region_release_lane(btt->nd_region, lane);
if (e_flag) {
ret = arena_clear_freelist_error(arena, lane);
if (ret)
return ret;
}
len -= cur_len;
off += cur_len;
sector += btt->sector_size >> SECTOR_SHIFT;
}
return 0;
out_map:
unlock_map(arena, premap);
out_lane:
nd_region_release_lane(btt->nd_region, lane);
return ret;
}