forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
4.1 KiB
194 lines
4.1 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
#include <linux/kernel.h> |
|
#include <linux/fs.h> |
|
#include <linux/buffer_head.h> |
|
#include <asm/div64.h> |
|
#include "omfs.h" |
|
|
|
unsigned long omfs_count_free(struct super_block *sb) |
|
{ |
|
unsigned int i; |
|
unsigned long sum = 0; |
|
struct omfs_sb_info *sbi = OMFS_SB(sb); |
|
int nbits = sb->s_blocksize * 8; |
|
|
|
for (i = 0; i < sbi->s_imap_size; i++) |
|
sum += nbits - bitmap_weight(sbi->s_imap[i], nbits); |
|
|
|
return sum; |
|
} |
|
|
|
/* |
|
* Counts the run of zero bits starting at bit up to max. |
|
* It handles the case where a run might spill over a buffer. |
|
* Called with bitmap lock. |
|
*/ |
|
static int count_run(unsigned long **addr, int nbits, |
|
int addrlen, int bit, int max) |
|
{ |
|
int count = 0; |
|
int x; |
|
|
|
for (; addrlen > 0; addrlen--, addr++) { |
|
x = find_next_bit(*addr, nbits, bit); |
|
count += x - bit; |
|
|
|
if (x < nbits || count > max) |
|
return min(count, max); |
|
|
|
bit = 0; |
|
} |
|
return min(count, max); |
|
} |
|
|
|
/* |
|
* Sets or clears the run of count bits starting with bit. |
|
* Called with bitmap lock. |
|
*/ |
|
static int set_run(struct super_block *sb, int map, |
|
int nbits, int bit, int count, int set) |
|
{ |
|
int i; |
|
int err; |
|
struct buffer_head *bh; |
|
struct omfs_sb_info *sbi = OMFS_SB(sb); |
|
|
|
err = -ENOMEM; |
|
bh = sb_bread(sb, clus_to_blk(sbi, sbi->s_bitmap_ino) + map); |
|
if (!bh) |
|
goto out; |
|
|
|
for (i = 0; i < count; i++, bit++) { |
|
if (bit >= nbits) { |
|
bit = 0; |
|
map++; |
|
|
|
mark_buffer_dirty(bh); |
|
brelse(bh); |
|
bh = sb_bread(sb, |
|
clus_to_blk(sbi, sbi->s_bitmap_ino) + map); |
|
if (!bh) |
|
goto out; |
|
} |
|
if (set) { |
|
set_bit(bit, sbi->s_imap[map]); |
|
set_bit(bit, (unsigned long *)bh->b_data); |
|
} else { |
|
clear_bit(bit, sbi->s_imap[map]); |
|
clear_bit(bit, (unsigned long *)bh->b_data); |
|
} |
|
} |
|
mark_buffer_dirty(bh); |
|
brelse(bh); |
|
err = 0; |
|
out: |
|
return err; |
|
} |
|
|
|
/* |
|
* Tries to allocate exactly one block. Returns true if successful. |
|
*/ |
|
int omfs_allocate_block(struct super_block *sb, u64 block) |
|
{ |
|
struct buffer_head *bh; |
|
struct omfs_sb_info *sbi = OMFS_SB(sb); |
|
int bits_per_entry = 8 * sb->s_blocksize; |
|
unsigned int map, bit; |
|
int ret = 0; |
|
u64 tmp; |
|
|
|
tmp = block; |
|
bit = do_div(tmp, bits_per_entry); |
|
map = tmp; |
|
|
|
mutex_lock(&sbi->s_bitmap_lock); |
|
if (map >= sbi->s_imap_size || test_and_set_bit(bit, sbi->s_imap[map])) |
|
goto out; |
|
|
|
if (sbi->s_bitmap_ino > 0) { |
|
bh = sb_bread(sb, clus_to_blk(sbi, sbi->s_bitmap_ino) + map); |
|
if (!bh) |
|
goto out; |
|
|
|
set_bit(bit, (unsigned long *)bh->b_data); |
|
mark_buffer_dirty(bh); |
|
brelse(bh); |
|
} |
|
ret = 1; |
|
out: |
|
mutex_unlock(&sbi->s_bitmap_lock); |
|
return ret; |
|
} |
|
|
|
|
|
/* |
|
* Tries to allocate a set of blocks. The request size depends on the |
|
* type: for inodes, we must allocate sbi->s_mirrors blocks, and for file |
|
* blocks, we try to allocate sbi->s_clustersize, but can always get away |
|
* with just one block. |
|
*/ |
|
int omfs_allocate_range(struct super_block *sb, |
|
int min_request, |
|
int max_request, |
|
u64 *return_block, |
|
int *return_size) |
|
{ |
|
struct omfs_sb_info *sbi = OMFS_SB(sb); |
|
int bits_per_entry = 8 * sb->s_blocksize; |
|
int ret = 0; |
|
int i, run, bit; |
|
|
|
mutex_lock(&sbi->s_bitmap_lock); |
|
for (i = 0; i < sbi->s_imap_size; i++) { |
|
bit = 0; |
|
while (bit < bits_per_entry) { |
|
bit = find_next_zero_bit(sbi->s_imap[i], bits_per_entry, |
|
bit); |
|
|
|
if (bit == bits_per_entry) |
|
break; |
|
|
|
run = count_run(&sbi->s_imap[i], bits_per_entry, |
|
sbi->s_imap_size-i, bit, max_request); |
|
|
|
if (run >= min_request) |
|
goto found; |
|
bit += run; |
|
} |
|
} |
|
ret = -ENOSPC; |
|
goto out; |
|
|
|
found: |
|
*return_block = (u64) i * bits_per_entry + bit; |
|
*return_size = run; |
|
ret = set_run(sb, i, bits_per_entry, bit, run, 1); |
|
|
|
out: |
|
mutex_unlock(&sbi->s_bitmap_lock); |
|
return ret; |
|
} |
|
|
|
/* |
|
* Clears count bits starting at a given block. |
|
*/ |
|
int omfs_clear_range(struct super_block *sb, u64 block, int count) |
|
{ |
|
struct omfs_sb_info *sbi = OMFS_SB(sb); |
|
int bits_per_entry = 8 * sb->s_blocksize; |
|
u64 tmp; |
|
unsigned int map, bit; |
|
int ret; |
|
|
|
tmp = block; |
|
bit = do_div(tmp, bits_per_entry); |
|
map = tmp; |
|
|
|
if (map >= sbi->s_imap_size) |
|
return 0; |
|
|
|
mutex_lock(&sbi->s_bitmap_lock); |
|
ret = set_run(sb, map, bits_per_entry, bit, count, 0); |
|
mutex_unlock(&sbi->s_bitmap_lock); |
|
return ret; |
|
}
|
|
|