📄 balloc.c
字号:
; } } start = grp_goal; if (!claim_block(sb_bgl_lock(EXT4_SB(sb), group), grp_goal, bitmap_bh)) { /* * The block was allocated by another thread, or it was * allocated and then freed by another thread */ start++; grp_goal++; if (start >= end) goto fail_access; goto repeat; } num++; grp_goal++; while (num < *count && grp_goal < end && ext4_test_allocatable(grp_goal, bitmap_bh) && claim_block(sb_bgl_lock(EXT4_SB(sb), group), grp_goal, bitmap_bh)) { num++; grp_goal++; } *count = num; return grp_goal - num;fail_access: *count = num; return -1;}/** * find_next_reservable_window(): * find a reservable space within the given range. * It does not allocate the reservation window for now: * alloc_new_reservation() will do the work later. * * @search_head: the head of the searching list; * This is not necessarily the list head of the whole filesystem * * We have both head and start_block to assist the search * for the reservable space. The list starts from head, * but we will shift to the place where start_block is, * then start from there, when looking for a reservable space. * * @size: the target new reservation window size * * @group_first_block: the first block we consider to start * the real search from * * @last_block: * the maximum block number that our goal reservable space * could start from. This is normally the last block in this * group. The search will end when we found the start of next * possible reservable space is out of this boundary. * This could handle the cross boundary reservation window * request. * * basically we search from the given range, rather than the whole * reservation double linked list, (start_block, last_block) * to find a free region that is of my size and has not * been reserved. * */static int find_next_reservable_window( struct ext4_reserve_window_node *search_head, struct ext4_reserve_window_node *my_rsv, struct super_block * sb, ext4_fsblk_t start_block, ext4_fsblk_t last_block){ struct rb_node *next; struct ext4_reserve_window_node *rsv, *prev; ext4_fsblk_t cur; int size = my_rsv->rsv_goal_size; /* TODO: make the start of the reservation window byte-aligned */ /* cur = *start_block & ~7;*/ cur = start_block; rsv = search_head; if (!rsv) return -1; while (1) { if (cur <= rsv->rsv_end) cur = rsv->rsv_end + 1; /* TODO? * in the case we could not find a reservable space * that is what is expected, during the re-search, we could * remember what's the largest reservable space we could have * and return that one. * * For now it will fail if we could not find the reservable * space with expected-size (or more)... */ if (cur > last_block) return -1; /* fail */ prev = rsv; next = rb_next(&rsv->rsv_node); rsv = rb_entry(next,struct ext4_reserve_window_node,rsv_node); /* * Reached the last reservation, we can just append to the * previous one. */ if (!next) break; if (cur + size <= rsv->rsv_start) { /* * Found a reserveable space big enough. We could * have a reservation across the group boundary here */ break; } } /* * we come here either : * when we reach the end of the whole list, * and there is empty reservable space after last entry in the list. * append it to the end of the list. * * or we found one reservable space in the middle of the list, * return the reservation window that we could append to. * succeed. */ if ((prev != my_rsv) && (!rsv_is_empty(&my_rsv->rsv_window))) rsv_window_remove(sb, my_rsv); /* * Let's book the whole avaliable window for now. We will check the * disk bitmap later and then, if there are free blocks then we adjust * the window size if it's larger than requested. * Otherwise, we will remove this node from the tree next time * call find_next_reservable_window. */ my_rsv->rsv_start = cur; my_rsv->rsv_end = cur + size - 1; my_rsv->rsv_alloc_hit = 0; if (prev != my_rsv) ext4_rsv_window_add(sb, my_rsv); return 0;}/** * alloc_new_reservation()--allocate a new reservation window * * To make a new reservation, we search part of the filesystem * reservation list (the list that inside the group). We try to * allocate a new reservation window near the allocation goal, * or the beginning of the group, if there is no goal. * * We first find a reservable space after the goal, then from * there, we check the bitmap for the first free block after * it. If there is no free block until the end of group, then the * whole group is full, we failed. Otherwise, check if the free * block is inside the expected reservable space, if so, we * succeed. * If the first free block is outside the reservable space, then * start from the first free block, we search for next available * space, and go on. * * on succeed, a new reservation will be found and inserted into the list * It contains at least one free block, and it does not overlap with other * reservation windows. * * failed: we failed to find a reservation window in this group * * @rsv: the reservation * * @grp_goal: The goal (group-relative). It is where the search for a * free reservable space should start from. * if we have a grp_goal(grp_goal >0 ), then start from there, * no grp_goal(grp_goal = -1), we start from the first block * of the group. * * @sb: the super block * @group: the group we are trying to allocate in * @bitmap_bh: the block group block bitmap * */static int alloc_new_reservation(struct ext4_reserve_window_node *my_rsv, ext4_grpblk_t grp_goal, struct super_block *sb, unsigned int group, struct buffer_head *bitmap_bh){ struct ext4_reserve_window_node *search_head; ext4_fsblk_t group_first_block, group_end_block, start_block; ext4_grpblk_t first_free_block; struct rb_root *fs_rsv_root = &EXT4_SB(sb)->s_rsv_window_root; unsigned long size; int ret; spinlock_t *rsv_lock = &EXT4_SB(sb)->s_rsv_window_lock; group_first_block = ext4_group_first_block_no(sb, group); group_end_block = group_first_block + (EXT4_BLOCKS_PER_GROUP(sb) - 1); if (grp_goal < 0) start_block = group_first_block; else start_block = grp_goal + group_first_block; size = my_rsv->rsv_goal_size; if (!rsv_is_empty(&my_rsv->rsv_window)) { /* * if the old reservation is cross group boundary * and if the goal is inside the old reservation window, * we will come here when we just failed to allocate from * the first part of the window. We still have another part * that belongs to the next group. In this case, there is no * point to discard our window and try to allocate a new one * in this group(which will fail). we should * keep the reservation window, just simply move on. * * Maybe we could shift the start block of the reservation * window to the first block of next group. */ if ((my_rsv->rsv_start <= group_end_block) && (my_rsv->rsv_end > group_end_block) && (start_block >= my_rsv->rsv_start)) return -1; if ((my_rsv->rsv_alloc_hit > (my_rsv->rsv_end - my_rsv->rsv_start + 1) / 2)) { /* * if the previously allocation hit ratio is * greater than 1/2, then we double the size of * the reservation window the next time, * otherwise we keep the same size window */ size = size * 2; if (size > EXT4_MAX_RESERVE_BLOCKS) size = EXT4_MAX_RESERVE_BLOCKS; my_rsv->rsv_goal_size= size; } } spin_lock(rsv_lock); /* * shift the search start to the window near the goal block */ search_head = search_reserve_window(fs_rsv_root, start_block); /* * find_next_reservable_window() simply finds a reservable window * inside the given range(start_block, group_end_block). * * To make sure the reservation window has a free bit inside it, we * need to check the bitmap after we found a reservable window. */retry: ret = find_next_reservable_window(search_head, my_rsv, sb, start_block, group_end_block); if (ret == -1) { if (!rsv_is_empty(&my_rsv->rsv_window)) rsv_window_remove(sb, my_rsv); spin_unlock(rsv_lock); return -1; } /* * On success, find_next_reservable_window() returns the * reservation window where there is a reservable space after it. * Before we reserve this reservable space, we need * to make sure there is at least a free block inside this region. * * searching the first free bit on the block bitmap and copy of * last committed bitmap alternatively, until we found a allocatable * block. Search start from the start block of the reservable space * we just found. */ spin_unlock(rsv_lock); first_free_block = bitmap_search_next_usable_block( my_rsv->rsv_start - group_first_block, bitmap_bh, group_end_block - group_first_block + 1); if (first_free_block < 0) { /* * no free block left on the bitmap, no point * to reserve the space. return failed. */ spin_lock(rsv_lock); if (!rsv_is_empty(&my_rsv->rsv_window)) rsv_window_remove(sb, my_rsv); spin_unlock(rsv_lock); return -1; /* failed */ } start_block = first_free_block + group_first_block; /* * check if the first free block is within the * free space we just reserved */ if (start_block >= my_rsv->rsv_start && start_block <= my_rsv->rsv_end) return 0; /* success */ /* * if the first free bit we found is out of the reservable space * continue search for next reservable space, * start from where the free block is, * we also shift the list head to where we stopped last time */ search_head = my_rsv; spin_lock(rsv_lock); goto retry;}/** * try_to_extend_reservation() * @my_rsv: given reservation window * @sb: super block * @size: the delta to extend * * Attempt to expand the reservation window large enough to have * required number of free blocks * * Since ext4_try_to_allocate() will always allocate blocks within * the reservation window range, if the window size is too small, * multiple blocks allocation has to stop at the end of the reservation * window. To make this more efficient, given the total number of * blocks needed and the current size of the window, we try to * expand the reservation window size if necessary on a best-effort * basis before ext4_new_blocks() tries to allocate blocks, */static void try_to_extend_reservation(struct ext4_reserve_window_node *my_rsv, struct super_block *sb, int size){ struct ext4_reserve_window_node *next_rsv; struct rb_node *next; spinlock_t *rsv_lock = &EXT4_SB(sb)->s_rsv_window_lock; if (!spin_trylock(rsv_lock)) return; next = rb_next(&my_rsv->rsv_node); if (!next) my_rsv->rsv_end += size; else { next_rsv = rb_entry(next, struct ext4_reserve_window_node, rsv_node); if ((next_rsv->rsv_start - my_rsv->rsv_end - 1) >= size) my_rsv->rsv_end += size; else my_rsv->rsv_end = next_rsv->rsv_start - 1; } spin_unlock(rsv_lock);}/** * ext4_try_to_allocate_with_rsv() * @sb: superblock * @handle: handle to this transaction * @group: given allocation block group * @bitmap_bh: bufferhead holds the block bitmap * @grp_goal: given target block within the group * @count: target number of blocks to allocate * @my_rsv: reservation window * @errp: pointer to store the error code * * This is the main function used to allocate a new block and its reservation * window. * * Each time when a new block allocation is need, first try to allocate from * its own reservation. If it does not have a reservation window, instead of * looking for a free bit on bitmap first, then look up the reservation list to * see if it is inside somebody else's reservation window, we try to allocate a * reservation window for it starting from the goal first. Then do the block * allocation within the reservation window. * * This will avoid keeping on searching the reservation list again and * again when somebody is looking for a free block (without * reservation), and there are lots of free blocks, but they are all * being reserved. * * We use a red-black tree for the per-filesystem reservation list. * */static ext4_grpblk_text4_try_to_allocate_with_rsv(struct super_block *sb, handle_t *handle, unsigned int group, struct buffer_head *bitmap_bh, ext4_grpblk_t grp_goal, struct ext4_reserve_window_node * my_rsv, unsigned long *count, int *errp){ ext4_fsblk_t group_first_block, group_last_block; ext4_grpblk_t ret = 0; int fatal; unsigned long num = *count; *errp = 0; /* * Make sure we use undo access for the bitmap, because it is critical * that we do the frozen_data COW on bitmap buffers in all cases even * if the buffer is in BJ_Forget state in the committing transaction. */ BUFFER_TRACE(bitmap_bh, "get undo access for new block"); fatal = ext4_journal_get_undo_access(handle, bitmap_bh); if (fatal) { *errp = fatal; return -1; } /* * we don't deal with reservation when * filesystem is mounted without reservation * or the file is not a regular file * or last attempt to allocate a block with reservation turned on failed */ if (my_rsv == NULL ) { ret = ext4_try_to_allocate(sb, handle, group, bitmap_bh, grp_goal, count, NULL); goto out; } /* * grp_goal is a group relative block number (if there is a goal) * 0 <= grp_goal < EXT4_BLOCKS_PER_GROUP(sb) * first block is a filesystem wide block number * first block is the block number of the first block in this group */ group_first_block = ext4_group_first_block_no(sb, group); group_last_block = group_first_block + (EXT4_BLOCKS_PER_GROUP(sb) - 1); /* * Basically we will allocate a new block from inode's reservation * window. * * We need to allocate a new reservation window, if: * a) inode does not have a reservation window; or * b) last attempt to allocate a block from existing reservation * failed; or * c) we come here with a goal and with a reservation window * * We do not need to allocate a new reservation window if we come here * at the beginning with a goal and the goal is inside the window, or * we don't have a goal but already have a reservation window. * then we could go to allocate from the reservation window directly. */ while (1) { if (rsv_is_empty(&my_rsv->rsv_window) || (ret < 0) || !goal_in_my_reservation(&my_rsv->rsv_window, grp_goal, group, sb)) { if (my_rsv->rsv_goal_size < *count) my_rsv->rsv_goal_size = *count; ret = alloc_new_reservation(my_rsv, grp_goal, sb, group, bitmap_bh); if (ret < 0) break; /* failed */ if (!goal_in_my_reservation(&my_rsv->rsv_window, grp_goal, group, sb)) grp_goal = -1; } else if (grp_goal >= 0) { int curr = my_rsv->rsv_end - (grp_goal + group_first_block) + 1; if (curr < *count) try_to_extend_reservation(my_rsv, sb, *count - curr); } if ((my_rsv->rsv_start > group_last_block) || (my_rsv->rsv_end < group_first_block)) { rsv_window_dump(&EXT4_SB(sb)->s_rsv_window_root, 1); BUG(); } ret = ext4_try_to_allocate(sb, handle, group, bitmap_bh, grp_goal, &num, &my_rsv->rsv_window); if (ret >= 0) { my_rsv->rsv_alloc_hit += num; *count = num; break; /* succeed */ } num = *count; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -