// SPDX-License-Identifier: GPL-2.0+ /* * NILFS dat/inode allocator * * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation. * * Originally written by Koji Sato. * Two allocators were unified by Ryusuke Konishi and Amagai Yoshiji.
*/
/** * nilfs_palloc_groups_per_desc_block - get the number of groups that a group * descriptor block can maintain * @inode: inode of metadata file using this allocator * * Return: Number of groups that a group descriptor block can maintain.
*/ staticinlineunsignedlong
nilfs_palloc_groups_per_desc_block(conststruct inode *inode)
{ return i_blocksize(inode) / sizeof(struct nilfs_palloc_group_desc);
}
/** * nilfs_palloc_groups_count - get maximum number of groups * @inode: inode of metadata file using this allocator * * Return: Maximum number of groups.
*/ staticinlineunsignedlong
nilfs_palloc_groups_count(conststruct inode *inode)
{ return 1UL << (BITS_PER_LONG - (inode->i_blkbits + 3 /* log2(8) */));
}
/** * nilfs_palloc_init_blockgroup - initialize private variables for allocator * @inode: inode of metadata file using this allocator * @entry_size: size of the persistent object * * Return: 0 on success, or a negative error code on failure.
*/ int nilfs_palloc_init_blockgroup(struct inode *inode, unsignedint entry_size)
{ struct nilfs_mdt_info *mi = NILFS_MDT(inode);
mi->mi_bgl = kmalloc(sizeof(*mi->mi_bgl), GFP_NOFS); if (!mi->mi_bgl) return -ENOMEM;
bgl_lock_init(mi->mi_bgl);
nilfs_mdt_set_entry_size(inode, entry_size, 0);
mi->mi_blocks_per_group =
DIV_ROUND_UP(nilfs_palloc_entries_per_group(inode),
mi->mi_entries_per_block) + 1; /* * Number of blocks in a group including entry blocks * and a bitmap block
*/
mi->mi_blocks_per_desc_block =
nilfs_palloc_groups_per_desc_block(inode) *
mi->mi_blocks_per_group + 1; /* * Number of blocks per descriptor including the * descriptor block
*/ return 0;
}
/** * nilfs_palloc_group - get group number and offset from an entry number * @inode: inode of metadata file using this allocator * @nr: serial number of the entry (e.g. inode number) * @offset: pointer to store offset number in the group * * Return: Number of the group that contains the entry with the index * specified by @nr.
*/ staticunsignedlong nilfs_palloc_group(conststruct inode *inode, __u64 nr, unsignedlong *offset)
{
__u64 group = nr;
/** * nilfs_palloc_desc_blkoff - get block offset of a group descriptor block * @inode: inode of metadata file using this allocator * @group: group number * * Return: Index number in the metadata file of the descriptor block of * the group specified by @group.
*/ staticunsignedlong
nilfs_palloc_desc_blkoff(conststruct inode *inode, unsignedlong group)
{ unsignedlong desc_block =
group / nilfs_palloc_groups_per_desc_block(inode); return desc_block * NILFS_MDT(inode)->mi_blocks_per_desc_block;
}
/** * nilfs_palloc_bitmap_blkoff - get block offset of a bitmap block * @inode: inode of metadata file using this allocator * @group: group number * * nilfs_palloc_bitmap_blkoff() returns block offset of the bitmap * block used to allocate/deallocate entries in the specified group. * * Return: Index number in the metadata file of the bitmap block of * the group specified by @group.
*/ staticunsignedlong
nilfs_palloc_bitmap_blkoff(conststruct inode *inode, unsignedlong group)
{ unsignedlong desc_offset =
group % nilfs_palloc_groups_per_desc_block(inode); return nilfs_palloc_desc_blkoff(inode, group) + 1 +
desc_offset * NILFS_MDT(inode)->mi_blocks_per_group;
}
/** * nilfs_palloc_group_desc_nfrees - get the number of free entries in a group * @desc: pointer to descriptor structure for the group * @lock: spin lock protecting @desc * * Return: Number of free entries written in the group descriptor @desc.
*/ staticunsignedlong
nilfs_palloc_group_desc_nfrees(conststruct nilfs_palloc_group_desc *desc,
spinlock_t *lock)
{ unsignedlong nfree;
/** * nilfs_palloc_group_desc_add_entries - adjust count of free entries * @desc: pointer to descriptor structure for the group * @lock: spin lock protecting @desc * @n: delta to be added * * Return: Number of free entries after adjusting the group descriptor * @desc.
*/ static u32
nilfs_palloc_group_desc_add_entries(struct nilfs_palloc_group_desc *desc,
spinlock_t *lock, u32 n)
{
u32 nfree;
/** * nilfs_palloc_entry_blkoff - get block offset of an entry block * @inode: inode of metadata file using this allocator * @nr: serial number of the entry (e.g. inode number) * * Return: Index number in the metadata file of the block containing * the entry specified by @nr.
*/ staticunsignedlong
nilfs_palloc_entry_blkoff(conststruct inode *inode, __u64 nr)
{ unsignedlong group, group_offset;
group = nilfs_palloc_group(inode, nr, &group_offset);
/** * nilfs_palloc_desc_block_init - initialize buffer of a group descriptor block * @inode: inode of metadata file * @bh: buffer head of the buffer to be initialized * @from: kernel address mapped for a chunk of the block * * This function does not yet support the case where block size > PAGE_SIZE.
*/ staticvoid nilfs_palloc_desc_block_init(struct inode *inode, struct buffer_head *bh, void *from)
{ struct nilfs_palloc_group_desc *desc = from; unsignedlong n = nilfs_palloc_groups_per_desc_block(inode);
__le32 nfrees;
ret = nilfs_mdt_get_block(inode, blkoff, create, init_block, bhp); if (!ret) {
spin_lock(lock); /* * The following code must be safe for change of the * cache contents during the get block call.
*/
brelse(prev->bh);
get_bh(*bhp);
prev->bh = *bhp;
prev->blkoff = blkoff;
spin_unlock(lock);
} return ret;
}
/** * nilfs_palloc_delete_block - delete a block on the persistent allocator file * @inode: inode of metadata file using this allocator * @blkoff: block offset * @prev: nilfs_bh_assoc struct of the last used buffer * @lock: spin lock protecting @prev * * Return: 0 on success, or one of the following negative error codes on * failure: * * %-EIO - I/O error (including metadata corruption). * * %-ENOENT - Non-existent block. * * %-ENOMEM - Insufficient memory available.
*/ staticint nilfs_palloc_delete_block(struct inode *inode, unsignedlong blkoff, struct nilfs_bh_assoc *prev,
spinlock_t *lock)
{
spin_lock(lock); if (prev->bh && blkoff == prev->blkoff) {
brelse(prev->bh);
prev->bh = NULL;
}
spin_unlock(lock); return nilfs_mdt_delete_block(inode, blkoff);
}
/** * nilfs_palloc_get_desc_block - get buffer head of a group descriptor block * @inode: inode of metadata file using this allocator * @group: group number * @create: create flag * @bhp: pointer to store the resultant buffer head * * Return: 0 on success, or a negative error code on failure.
*/ staticint nilfs_palloc_get_desc_block(struct inode *inode, unsignedlong group, int create, struct buffer_head **bhp)
{ struct nilfs_palloc_cache *cache = NILFS_MDT(inode)->mi_palloc_cache;
/** * nilfs_palloc_get_bitmap_block - get buffer head of a bitmap block * @inode: inode of metadata file using this allocator * @group: group number * @create: create flag * @bhp: pointer to store the resultant buffer head * * Return: 0 on success, or a negative error code on failure.
*/ staticint nilfs_palloc_get_bitmap_block(struct inode *inode, unsignedlong group, int create, struct buffer_head **bhp)
{ struct nilfs_palloc_cache *cache = NILFS_MDT(inode)->mi_palloc_cache;
/** * nilfs_palloc_delete_bitmap_block - delete a bitmap block * @inode: inode of metadata file using this allocator * @group: group number * * Return: 0 on success, or a negative error code on failure.
*/ staticint nilfs_palloc_delete_bitmap_block(struct inode *inode, unsignedlong group)
{ struct nilfs_palloc_cache *cache = NILFS_MDT(inode)->mi_palloc_cache;
/** * nilfs_palloc_get_entry_block - get buffer head of an entry block * @inode: inode of metadata file using this allocator * @nr: serial number of the entry (e.g. inode number) * @create: create flag * @bhp: pointer to store the resultant buffer head * * Return: 0 on success, or a negative error code on failure.
*/ int nilfs_palloc_get_entry_block(struct inode *inode, __u64 nr, int create, struct buffer_head **bhp)
{ struct nilfs_palloc_cache *cache = NILFS_MDT(inode)->mi_palloc_cache;
/** * nilfs_palloc_delete_entry_block - delete an entry block * @inode: inode of metadata file using this allocator * @nr: serial number of the entry * * Return: 0 on success, or a negative error code on failure.
*/ staticint nilfs_palloc_delete_entry_block(struct inode *inode, __u64 nr)
{ struct nilfs_palloc_cache *cache = NILFS_MDT(inode)->mi_palloc_cache;
/** * nilfs_palloc_group_desc_offset - calculate the byte offset of a group * descriptor in the folio containing it * @inode: inode of metadata file using this allocator * @group: group number * @bh: buffer head of the group descriptor block * * Return: Byte offset in the folio of the group descriptor for @group.
*/ static size_t nilfs_palloc_group_desc_offset(conststruct inode *inode, unsignedlong group, conststruct buffer_head *bh)
{ return offset_in_folio(bh->b_folio, bh->b_data) + sizeof(struct nilfs_palloc_group_desc) *
(group % nilfs_palloc_groups_per_desc_block(inode));
}
/** * nilfs_palloc_bitmap_offset - calculate the byte offset of a bitmap block * in the folio containing it * @bh: buffer head of the bitmap block * * Return: Byte offset in the folio of the bitmap block for @bh.
*/ static size_t nilfs_palloc_bitmap_offset(conststruct buffer_head *bh)
{ return offset_in_folio(bh->b_folio, bh->b_data);
}
/** * nilfs_palloc_entry_offset - calculate the byte offset of an entry in the * folio containing it * @inode: inode of metadata file using this allocator * @nr: serial number of the entry (e.g. inode number) * @bh: buffer head of the entry block * * Return: Byte offset in the folio of the entry @nr.
*/
size_t nilfs_palloc_entry_offset(conststruct inode *inode, __u64 nr, conststruct buffer_head *bh)
{ unsignedlong entry_index_in_group, entry_index_in_block;
/** * nilfs_palloc_find_available_slot - find available slot in a group * @bitmap: bitmap of the group * @target: offset number of an entry in the group (start point) * @bsize: size in bits * @lock: spin lock protecting @bitmap * @wrap: whether to wrap around * * Return: Offset number within the group of the found free entry, or * %-ENOSPC if not found.
*/ staticint nilfs_palloc_find_available_slot(unsignedchar *bitmap, unsignedlong target, unsignedint bsize,
spinlock_t *lock, bool wrap)
{ int pos, end = bsize;
if (likely(target < bsize)) {
pos = target; do {
pos = nilfs_find_next_zero_bit(bitmap, end, pos); if (pos >= end) break; if (!nilfs_set_bit_atomic(lock, pos, bitmap)) return pos;
} while (++pos < end);
end = target;
} if (!wrap) return -ENOSPC;
/* wrap around */ for (pos = 0; pos < end; pos++) {
pos = nilfs_find_next_zero_bit(bitmap, end, pos); if (pos >= end) break; if (!nilfs_set_bit_atomic(lock, pos, bitmap)) return pos;
}
return -ENOSPC;
}
/** * nilfs_palloc_rest_groups_in_desc_block - get the remaining number of groups * in a group descriptor block * @inode: inode of metadata file using this allocator * @curr: current group number * @max: maximum number of groups * * Return: Number of remaining descriptors (= groups) managed by the descriptor * block.
*/ staticunsignedlong
nilfs_palloc_rest_groups_in_desc_block(conststruct inode *inode, unsignedlong curr, unsignedlong max)
{ return min_t(unsignedlong,
nilfs_palloc_groups_per_desc_block(inode) -
curr % nilfs_palloc_groups_per_desc_block(inode),
max - curr + 1);
}
/** * nilfs_palloc_count_desc_blocks - count descriptor blocks number * @inode: inode of metadata file using this allocator * @desc_blocks: descriptor blocks number [out] * * Return: 0 on success, or a negative error code on failure.
*/ staticint nilfs_palloc_count_desc_blocks(struct inode *inode, unsignedlong *desc_blocks)
{
__u64 blknum; int ret;
ret = nilfs_bmap_last_key(NILFS_I(inode)->i_bmap, &blknum); if (likely(!ret))
*desc_blocks = DIV_ROUND_UP(
(unsignedlong)blknum,
NILFS_MDT(inode)->mi_blocks_per_desc_block); return ret;
}
/** * nilfs_palloc_mdt_file_can_grow - check potential opportunity for * MDT file growing * @inode: inode of metadata file using this allocator * @desc_blocks: known current descriptor blocks count * * Return: true if a group can be added in the metadata file, false if not.
*/ staticinlinebool nilfs_palloc_mdt_file_can_grow(struct inode *inode, unsignedlong desc_blocks)
{ return (nilfs_palloc_groups_per_desc_block(inode) * desc_blocks) <
nilfs_palloc_groups_count(inode);
}
/** * nilfs_palloc_count_max_entries - count max number of entries that can be * described by descriptor blocks count * @inode: inode of metadata file using this allocator * @nused: current number of used entries * @nmaxp: max number of entries [out] * * Return: 0 on success, or one of the following negative error codes on * failure: * * %-EIO - I/O error (including metadata corruption). * * %-ENOMEM - Insufficient memory available. * * %-ERANGE - Number of entries in use is out of range.
*/ int nilfs_palloc_count_max_entries(struct inode *inode, u64 nused, u64 *nmaxp)
{ unsignedlong desc_blocks = 0;
u64 entries_per_desc_block, nmax; int err;
err = nilfs_palloc_count_desc_blocks(inode, &desc_blocks); if (unlikely(err)) return err;
for (i = 0; i < ngroups; i += n) { if (group >= ngroups && wrap) { /* wrap around */
group = 0;
maxgroup = nilfs_palloc_group(inode, req->pr_entry_nr,
&maxgroup_offset) - 1;
}
ret = nilfs_palloc_get_desc_block(inode, group, 1, &desc_bh); if (ret < 0) return ret;
kunmap_local(desc);
ret = nilfs_palloc_get_bitmap_block(inode, group, 1,
&bitmap_bh); if (unlikely(ret < 0)) {
brelse(desc_bh); return ret;
}
/* * Re-kmap the folio containing the first (and * subsequent) group descriptors.
*/
desc = kmap_local_folio(desc_bh->b_folio, doff);
boff = nilfs_palloc_bitmap_offset(bitmap_bh);
bitmap = kmap_local_folio(bitmap_bh->b_folio, boff);
pos = nilfs_palloc_find_available_slot(
bitmap, group_offset, entries_per_group, lock,
wrap); /* * Since the search for a free slot in the second and * subsequent bitmap blocks always starts from the * beginning, the wrap flag only has an effect on the * first search.
*/
kunmap_local(bitmap); if (pos >= 0) goto found;
brelse(bitmap_bh);
}
kunmap_local(desc);
brelse(desc_bh);
}
/* no entries left */ return -ENOSPC;
found: /* found a free entry */
nilfs_palloc_group_desc_add_entries(&desc[j], lock, -1);
req->pr_entry_nr = entries_per_group * group + pos;
kunmap_local(desc);
/** * nilfs_palloc_prepare_free_entry - prepare to deallocate a persistent object * @inode: inode of metadata file using this allocator * @req: nilfs_palloc_req structure exchanged for the removal * * Return: 0 on success, or a negative error code on failure.
*/ int nilfs_palloc_prepare_free_entry(struct inode *inode, struct nilfs_palloc_req *req)
{ struct buffer_head *desc_bh, *bitmap_bh; unsignedlong group, group_offset; int ret;
group = nilfs_palloc_group(inode, req->pr_entry_nr, &group_offset);
ret = nilfs_palloc_get_desc_block(inode, group, 1, &desc_bh); if (ret < 0) return ret;
ret = nilfs_palloc_get_bitmap_block(inode, group, 1, &bitmap_bh); if (ret < 0) {
brelse(desc_bh); return ret;
}
j = i;
entry_start = rounddown(group_offset, epb); do { if (!nilfs_clear_bit_atomic(lock, group_offset,
bitmap)) {
nilfs_warn(inode->i_sb, "%s (ino=%lu): entry number %llu already freed",
__func__, inode->i_ino,
(unsignedlonglong)entry_nrs[j]);
} else {
n++;
}
j++; if (j >= nitems || entry_nrs[j] < group_min_nr ||
entry_nrs[j] >= group_min_nr + epg) {
change_group = true;
} else {
group_offset = entry_nrs[j] - group_min_nr; if (group_offset >= entry_start &&
group_offset < entry_start + epb) { /* This entry is in the same block */ continue;
}
}
/* Test if the entry block is empty or not */
end = entry_start + epb;
pos = nilfs_find_next_bit(bitmap, end, entry_start); if (pos >= end) {
last_nrs[nempties++] = entry_nrs[j - 1]; if (nempties >= ARRAY_SIZE(last_nrs)) break;
}
if (change_group) break;
/* Go on to the next entry block */
entry_start = rounddown(group_offset, epb);
} while (true);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.