📄 balloc.c
字号:
{ struct ext2_reserve_window_node *next_rsv; struct rb_node *next; spinlock_t *rsv_lock = &EXT2_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 ext2_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);}/** * ext2_try_to_allocate_with_rsv() * @sb: superblock * @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 * * 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 ext2_grpblk_text2_try_to_allocate_with_rsv(struct super_block *sb, unsigned int group, struct buffer_head *bitmap_bh, ext2_grpblk_t grp_goal, struct ext2_reserve_window_node * my_rsv, unsigned long *count){ ext2_fsblk_t group_first_block, group_last_block; ext2_grpblk_t ret = 0; unsigned long num = *count; /* * 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) { return ext2_try_to_allocate(sb, group, bitmap_bh, grp_goal, count, NULL); } /* * grp_goal is a group relative block number (if there is a goal) * 0 <= grp_goal < EXT2_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 = ext2_group_first_block_no(sb, group); group_last_block = group_first_block + (EXT2_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(&EXT2_SB(sb)->s_rsv_window_root, 1); BUG(); } ret = ext2_try_to_allocate(sb, 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; } return ret;}/** * ext2_has_free_blocks() * @sbi: in-core super block structure. * * Check if filesystem has at least 1 free block available for allocation. */static int ext2_has_free_blocks(struct ext2_sb_info *sbi){ ext2_fsblk_t free_blocks, root_blocks; free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter); root_blocks = le32_to_cpu(sbi->s_es->s_r_blocks_count); if (free_blocks < root_blocks + 1 && !capable(CAP_SYS_RESOURCE) && sbi->s_resuid != current->fsuid && (sbi->s_resgid == 0 || !in_group_p (sbi->s_resgid))) { return 0; } return 1;}/* * ext2_new_blocks() -- core block(s) allocation function * @inode: file inode * @goal: given target block(filesystem wide) * @count: target number of blocks to allocate * @errp: error code * * ext2_new_blocks uses a goal block to assist allocation. If the goal is * free, or there is a free block within 32 blocks of the goal, that block * is allocated. Otherwise a forward search is made for a free block; within * each block group the search first looks for an entire free byte in the block * bitmap, and then for any free bit if that fails. * This function also updates quota and i_blocks field. */ext2_fsblk_t ext2_new_blocks(struct inode *inode, ext2_fsblk_t goal, unsigned long *count, int *errp){ struct buffer_head *bitmap_bh = NULL; struct buffer_head *gdp_bh; int group_no; int goal_group; ext2_grpblk_t grp_target_blk; /* blockgroup relative goal block */ ext2_grpblk_t grp_alloc_blk; /* blockgroup-relative allocated block*/ ext2_fsblk_t ret_block; /* filesyetem-wide allocated block */ int bgi; /* blockgroup iteration index */ int performed_allocation = 0; ext2_grpblk_t free_blocks; /* number of free blocks in a group */ struct super_block *sb; struct ext2_group_desc *gdp; struct ext2_super_block *es; struct ext2_sb_info *sbi; struct ext2_reserve_window_node *my_rsv = NULL; struct ext2_block_alloc_info *block_i; unsigned short windowsz = 0; unsigned long ngroups; unsigned long num = *count; *errp = -ENOSPC; sb = inode->i_sb; if (!sb) { printk("ext2_new_blocks: nonexistent device"); return 0; } /* * Check quota for allocation of this block. */ if (DQUOT_ALLOC_BLOCK(inode, num)) { *errp = -EDQUOT; return 0; } sbi = EXT2_SB(sb); es = EXT2_SB(sb)->s_es; ext2_debug("goal=%lu.\n", goal); /* * Allocate a block from reservation only when * filesystem is mounted with reservation(default,-o reservation), and * it's a regular file, and * the desired window size is greater than 0 (One could use ioctl * command EXT2_IOC_SETRSVSZ to set the window size to 0 to turn off * reservation on that particular file) */ block_i = EXT2_I(inode)->i_block_alloc_info; if (block_i) { windowsz = block_i->rsv_window_node.rsv_goal_size; if (windowsz > 0) my_rsv = &block_i->rsv_window_node; } if (!ext2_has_free_blocks(sbi)) { *errp = -ENOSPC; goto out; } /* * First, test whether the goal block is free. */ if (goal < le32_to_cpu(es->s_first_data_block) || goal >= le32_to_cpu(es->s_blocks_count)) goal = le32_to_cpu(es->s_first_data_block); group_no = (goal - le32_to_cpu(es->s_first_data_block)) / EXT2_BLOCKS_PER_GROUP(sb); goal_group = group_no;retry_alloc: gdp = ext2_get_group_desc(sb, group_no, &gdp_bh); if (!gdp) goto io_error; free_blocks = le16_to_cpu(gdp->bg_free_blocks_count); /* * if there is not enough free blocks to make a new resevation * turn off reservation for this allocation */ if (my_rsv && (free_blocks < windowsz) && (rsv_is_empty(&my_rsv->rsv_window))) my_rsv = NULL; if (free_blocks > 0) { grp_target_blk = ((goal - le32_to_cpu(es->s_first_data_block)) % EXT2_BLOCKS_PER_GROUP(sb)); bitmap_bh = read_block_bitmap(sb, group_no); if (!bitmap_bh) goto io_error; grp_alloc_blk = ext2_try_to_allocate_with_rsv(sb, group_no, bitmap_bh, grp_target_blk, my_rsv, &num); if (grp_alloc_blk >= 0) goto allocated; } ngroups = EXT2_SB(sb)->s_groups_count; smp_rmb(); /* * Now search the rest of the groups. We assume that * i and gdp correctly point to the last group visited. */ for (bgi = 0; bgi < ngroups; bgi++) { group_no++; if (group_no >= ngroups) group_no = 0; gdp = ext2_get_group_desc(sb, group_no, &gdp_bh); if (!gdp) goto io_error; free_blocks = le16_to_cpu(gdp->bg_free_blocks_count); /* * skip this group if the number of * free blocks is less than half of the reservation * window size. */ if (free_blocks <= (windowsz/2)) continue; brelse(bitmap_bh); bitmap_bh = read_block_bitmap(sb, group_no); if (!bitmap_bh) goto io_error; /* * try to allocate block(s) from this group, without a goal(-1). */ grp_alloc_blk = ext2_try_to_allocate_with_rsv(sb, group_no, bitmap_bh, -1, my_rsv, &num); if (grp_alloc_blk >= 0) goto allocated; } /* * We may end up a bogus ealier ENOSPC error due to * filesystem is "full" of reservations, but * there maybe indeed free blocks avaliable on disk * In this case, we just forget about the reservations * just do block allocation as without reservations. */ if (my_rsv) { my_rsv = NULL; windowsz = 0; group_no = goal_group; goto retry_alloc; } /* No space left on the device */ *errp = -ENOSPC; goto out;allocated: ext2_debug("using block group %d(%d)\n", group_no, gdp->bg_free_blocks_count); ret_block = grp_alloc_blk + ext2_group_first_block_no(sb, group_no); if (in_range(le32_to_cpu(gdp->bg_block_bitmap), ret_block, num) || in_range(le32_to_cpu(gdp->bg_inode_bitmap), ret_block, num) || in_range(ret_block, le32_to_cpu(gdp->bg_inode_table), EXT2_SB(sb)->s_itb_per_group) || in_range(ret_block + num - 1, le32_to_cpu(gdp->bg_inode_table), EXT2_SB(sb)->s_itb_per_group)) ext2_error(sb, "ext2_new_blocks", "Allocating block in system zone - " "blocks from "E2FSBLK", length %lu", ret_block, num); performed_allocation = 1; if (ret_block + num - 1 >= le32_to_cpu(es->s_blocks_count)) { ext2_error(sb, "ext2_new_blocks", "block("E2FSBLK") >= blocks count(%d) - " "block_group = %d, es == %p ", ret_block, le32_to_cpu(es->s_blocks_count), group_no, es); goto out; } group_adjust_blocks(sb, group_no, gdp, gdp_bh, -num); percpu_counter_sub(&sbi->s_freeblocks_counter, num); mark_buffer_dirty(bitmap_bh); if (sb->s_flags & MS_SYNCHRONOUS) sync_dirty_buffer(bitmap_bh); *errp = 0; brelse(bitmap_bh); DQUOT_FREE_BLOCK(inode, *count-num); *count = num; return ret_block;io_error: *errp = -EIO;out: /* * Undo the block allocation */ if (!performed_allocation) DQUOT_FREE_BLOCK(inode, *count); brelse(bitmap_bh); return 0;}ext2_fsblk_t ext2_new_block(struct inode *inode, unsigned long goal, int *errp){ unsigned long count = 1; return ext2_new_blocks(inode, goal, &count, errp);}#ifdef EXT2FS_DEBUGstatic const int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};unsigned long ext2_count_free (struct buffer_head * map, unsigned int numchars){ unsigned int i; unsigned long sum = 0; if (!map) return (0); for (i = 0; i < numchars; i++) sum += nibblemap[map->b_data[i] & 0xf] + nibblemap[(map->b_data[i] >> 4) & 0xf]; return (sum);}#endif /* EXT2FS_DEBUG */unsigned long ext2_count_free_blocks (struct super_block * sb){ struct ext2_group_desc * desc; unsigned long desc_count = 0; int i;#ifdef EXT2FS_DEBUG unsigned long bitmap_count, x; struct ext2_super_block *es; es = EXT2_SB(sb)->s_es; desc_count = 0; bitmap_count = 0; desc = NULL; for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++) { struct buffer_head *bitmap_bh; desc = ext2_get_group_desc (sb, i, NULL); if (!desc) continue; desc_count += le16_to_cpu(desc->bg_free_blocks_count); bitmap_bh = read_block_bitmap(sb, i); if (!bitmap_bh) continue; x = ext2_count_free(bitmap_bh, sb->s_blocksize); printk ("group %d: stored = %d, counted = %lu\n", i, le16_to_cpu(desc->bg_free_blocks_count), x); bitmap_count += x; brelse(bitmap_bh); } printk("ext2_count_free_blocks: stored = %lu, computed = %lu, %lu\n", (long)le32_to_cpu(es->s_free_blocks_count), desc_count, bitmap_count); return bitmap_count;#else for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++) { desc = ext2_get_group_desc (sb, i, NULL); if (!desc) continue; desc_count += le16_to_cpu(desc->bg_free_blocks_count); } return desc_count;#endif}static inline int test_root(int a, int b){ int num = b; while (a > num) num *= b; return num == a;}static int ext2_group_sparse(int group){ if (group <= 1) return 1; return (test_root(group, 3) || test_root(group, 5) || test_root(group, 7));}/** * ext2_bg_has_super - number of blocks used by the superblock in group * @sb: superblock for filesystem * @group: group number to check * * Return the number of blocks used by the superblock (primary or backup) * in this group. Currently this will be only 0 or 1. */int ext2_bg_has_super(struct super_block *sb, int group){ if (EXT2_HAS_RO_COMPAT_FEATURE(sb,EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)&& !ext2_group_sparse(group)) return 0; return 1;}/** * ext2_bg_num_gdb - number of blocks used by the group table in group * @sb: superblock for filesystem * @group: group number to check * * Return the number of blocks used by the group descriptor table * (primary or backup) in this group. In the future there may be a * different number of descriptor blocks in each group. */unsigned long ext2_bg_num_gdb(struct super_block *sb, int group){ if (EXT2_HAS_RO_COMPAT_FEATURE(sb,EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)&& !ext2_group_sparse(group)) return 0; return EXT2_SB(sb)->s_gdb_count;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -