Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:12.2
s390-tools
s390-tools-sles11sp2-cmsfs-fuse-amap.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File s390-tools-sles11sp2-cmsfs-fuse-amap.patch of Package s390-tools
Description: cmsfs-fuse: Fix block allocation on large disks Symptom: Write failure on a disk with block size 512 bytes if the disk is larger than 256 MB. Problem: The calculation of the block address to be allocated or freed is wrong for addresses that exceed one pointer block. Solution: Fix the calculation of the disk address. Also, speed up the block allocation by providing a hint where the last free block was found. Problem-ID: 75958 --- cmsfs-fuse/amap.c | 159 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 114 insertions(+), 45 deletions(-) --- a/cmsfs-fuse/amap.c +++ b/cmsfs-fuse/amap.c @@ -19,7 +19,29 @@ #include "cmsfs-fuse.h" /* - * Get block number from address. + * Hint where to look for the next free block (level 0 only). + * Updated if a free block is found. If the level 0 amap bitmap + * block is exhausted we still scan all amap blocks. + */ +struct amap_alloction_hint { + /* addr of amap bitmap block to check */ + off_t amap_addr; + /* disk addr of the last allocated or freed block */ + off_t addr; + /* offset to start of the amap data block */ + off_t offset; +}; + +static struct amap_alloction_hint amap_hint; + +static void update_amap_hint(off_t amap_addr, off_t addr) +{ + amap_hint.amap_addr = amap_addr; + amap_hint.addr = addr; +} + +/* + * Get L1 block number from address. */ static int amap_blocknumber(off_t addr) { @@ -43,7 +65,7 @@ static int amap_blocknumber_level(int le */ static off_t get_amap_addr(int level, off_t addr, off_t ptr) { - int block = amap_blocknumber_level(level, addr); + int block = amap_blocknumber_level(level, addr) % PTRS_PER_BLOCK; if (cmsfs.amap_levels == 0) return cmsfs.amap; @@ -59,30 +81,21 @@ static off_t get_amap_addr(int level, of } /* - * Mark disk address as allocated in alloc map. Unaligned addr is tolerated. + * Mark disk address as allocated in alloc map. */ -static void amap_block_set(off_t addr) +static void amap_block_set(off_t amap, int bit) { - off_t amap = get_amap_addr(cmsfs.amap_levels, addr, cmsfs.amap); - int rc, block = amap_blocknumber(addr); - unsigned int byte, bit; u8 entry; + int rc; - if (block > 0) - addr -= (off_t) block * BYTES_PER_BLOCK; - - addr >>= BITS_PER_DATA_BLOCK; - byte = addr / 8; - bit = addr % 8; - - rc = _read(&entry, sizeof(entry), amap + byte); + rc = _read(&entry, sizeof(entry), amap); BUG(rc < 0); /* already used */ BUG(entry & (1 << (7 - bit))); entry |= (1 << (7 - bit)); - rc = _write(&entry, sizeof(entry), amap + byte); + rc = _write(&entry, sizeof(entry), amap); BUG(rc < 0); } @@ -93,6 +106,7 @@ static void amap_block_clear(off_t addr) { off_t amap = get_amap_addr(cmsfs.amap_levels, addr, cmsfs.amap); int rc, block = amap_blocknumber(addr); + off_t disk_addr = addr; unsigned int byte, bit; u8 entry; @@ -112,12 +126,21 @@ static void amap_block_clear(off_t addr) entry &= ~(1 << (7 - bit)); rc = _write(&entry, sizeof(entry), amap + byte); BUG(rc < 0); + + /* + * If the freed addr is lower set the hint to it to ensure + * the amap bitmap is packed from the start. That way we do not + * need an extra check if the bitmap entry is above disk end, the + * check if we overflow the total block limit is sufficient. + */ + if (disk_addr < amap_hint.addr) + update_amap_hint(amap + byte, disk_addr); } /* * Return the first free bit in one byte. */ -static int find_first_empty_bit(u8 entry) +static inline int find_first_empty_bit(u8 entry) { u8 i; @@ -129,46 +152,89 @@ static int find_first_empty_bit(u8 entry } /* - * Look for the first unallocated block and return addr of allocated block. + * Return the number of bytes addressed by one pointer entry for the + * specified level. */ -static off_t __get_free_block(int level, off_t amap, int block_nr) +static off_t bytes_per_level(int level) +{ + off_t mult = BYTES_PER_BLOCK; + + if (!level) + return 0; + level--; + while (level--) + mult *= PTRS_PER_BLOCK; + return mult; +} + +static inline int get_amap_entry_bit(off_t amap) { - off_t ptr, addr = amap; - unsigned int bit; - int left, rc, i; u8 entry; + int rc; + + rc = _read(&entry, sizeof(entry), amap); + BUG(rc < 0); + + if (entry == 0xff) + return -1; + return find_first_empty_bit(entry); +} + +static off_t __get_free_block_fast(void) +{ + off_t addr, amap = amap_hint.amap_addr & ~DATA_BLOCK_MASK; + int bit, i = amap_hint.amap_addr & DATA_BLOCK_MASK; + + for (; i < cmsfs.blksize; i++) { + bit = get_amap_entry_bit(amap + i); + if (bit == -1) + continue; + + /* Calculate the addr for the free block we've found. */ + addr = (off_t) amap_blocknumber(amap_hint.addr) * BYTES_PER_BLOCK; + addr += i * 8 * cmsfs.blksize; + addr += bit * cmsfs.blksize; + + amap_block_set(amap + i, bit); + update_amap_hint(amap + i, addr); + return addr; + } + return 0; +} + +/* + * Look for the first unallocated block and return addr of allocated block. + */ +static off_t __get_free_block(int level, off_t amap, off_t addr) +{ + off_t ptr; + int bit, i; if (level > 0) { - level--; - left = PTRS_PER_BLOCK; - while (left--) { - ptr = get_fixed_pointer(addr); + for (i = 0; i < PTRS_PER_BLOCK; i++) { + ptr = get_fixed_pointer(amap); if (!ptr) return 0; - ptr = __get_free_block(level, ptr, block_nr); + ptr = __get_free_block(level - 1, ptr, + addr + i * bytes_per_level(level)); if (ptr) return ptr; - addr += PTR_SIZE; - block_nr++; + amap += PTR_SIZE; } return 0; } for (i = 0; i < cmsfs.blksize; i++) { - rc = _read(&entry, sizeof(entry), amap + i); - BUG(rc < 0); - - if (entry != 0xff) { - /* get first empty bit and add to addr */ - bit = find_first_empty_bit(entry); - - /* bit -> addr */ - addr = ((off_t) cmsfs.blksize * i * 8 + - (off_t) cmsfs.blksize * bit) + - (off_t) block_nr * BYTES_PER_BLOCK; - amap_block_set(addr); - return addr; - } + bit = get_amap_entry_bit(amap + i); + if (bit == -1) + continue; + amap_block_set(amap + i, bit); + /* add byte offset */ + addr += i * 8 * cmsfs.blksize; + /* add bit offset */ + addr += bit * cmsfs.blksize; + update_amap_hint(amap + i, addr); + return addr; } return 0; } @@ -178,11 +244,14 @@ static off_t __get_free_block(int level, */ off_t get_free_block(void) { - off_t addr; + off_t addr = 0; if (cmsfs.used_blocks + cmsfs.reserved_blocks >= cmsfs.total_blocks) return -ENOSPC; - addr = __get_free_block(cmsfs.amap_levels, cmsfs.amap, 0); + if (amap_hint.amap_addr) + addr = __get_free_block_fast(); + if (!addr) + addr = __get_free_block(cmsfs.amap_levels, cmsfs.amap, 0); BUG(!addr); cmsfs.used_blocks++;
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor