+/**
+ * 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.
+ *
+ * on succeed, it returns the reservation window to be appended to.
+ * failed, return NULL.
+ */
+static struct ext3_reserve_window_node *find_next_reservable_window(
+ struct ext3_reserve_window_node *search_head,
+ unsigned long size, int *start_block,
+ int last_block)
+{
+ struct rb_node *next;
+ struct ext3_reserve_window_node *rsv, *prev;
+ int cur;
+
+ /* TODO: make the start of the reservation window byte-aligned */
+ /* cur = *start_block & ~7;*/
+ cur = *start_block;
+ rsv = search_head;
+ if (!rsv)
+ return NULL;
+
+ 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 NULL; /* fail */
+
+ prev = rsv;
+ next = rb_next(&rsv->rsv_node);
+ rsv = list_entry(next, struct ext3_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.
+ */
+ *start_block = cur;
+ return prev;
+}
+
+/**
+ * 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
+ *
+ * @goal: The goal (group-relative). It is where the search for a
+ * free reservable space should start from.
+ * if we have a goal(goal >0 ), then start from there,
+ * no goal(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 ext3_reserve_window_node *my_rsv,
+ int goal, struct super_block *sb,
+ unsigned int group, struct buffer_head *bitmap_bh)
+{
+ struct ext3_reserve_window_node *search_head;
+ int group_first_block, group_end_block, start_block;
+ int first_free_block;
+ int reservable_space_start;
+ struct ext3_reserve_window_node *prev_rsv;
+ struct rb_root *fs_rsv_root = &EXT3_SB(sb)->s_rsv_window_root;
+ unsigned long size;
+
+ group_first_block = le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block) +
+ group * EXT3_BLOCKS_PER_GROUP(sb);
+ group_end_block = group_first_block + EXT3_BLOCKS_PER_GROUP(sb) - 1;
+
+ if (goal < 0)
+ start_block = group_first_block;
+ else
+ start_block = goal + group_first_block;
+
+ size = atomic_read(&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 ((atomic_read(&my_rsv->rsv_alloc_hit) >
+ (my_rsv->rsv_end - my_rsv->rsv_start + 1) / 2)) {
+ /*
+ * if we previously allocation hit ration is greater than half
+ * we double the size of reservation window next time
+ * otherwise keep the same
+ */
+ size = size * 2;
+ if (size > EXT3_MAX_RESERVE_BLOCKS)
+ size = EXT3_MAX_RESERVE_BLOCKS;
+ atomic_set(&my_rsv->rsv_goal_size, size);
+ }
+ }
+ /*
+ * 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:
+ prev_rsv = find_next_reservable_window(search_head, size,
+ &start_block, group_end_block);
+ if (prev_rsv == NULL)
+ goto failed;
+ reservable_space_start = start_block;
+ /*
+ * 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.
+ */
+ first_free_block = bitmap_search_next_usable_block(
+ reservable_space_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.
+ */
+ goto failed;
+ }
+ start_block = first_free_block + group_first_block;
+ /*
+ * check if the first free block is within the
+ * free space we just found
+ */
+ if ((start_block >= reservable_space_start) &&
+ (start_block < reservable_space_start + size))
+ goto found_rsv_window;
+ /*
+ * if the first free bit we found is out of the reservable space
+ * this means there is no free block on the reservable space
+ * we should 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 = prev_rsv;
+ goto retry;
+
+found_rsv_window:
+ /*
+ * great! the reservable space contains some free blocks.
+ * if the search returns that we should add the new
+ * window just next to where the old window, we don't
+ * need to remove the old window first then add it to the
+ * same place, just update the new start and new end.
+ */
+ if (my_rsv != prev_rsv) {
+ if (!rsv_is_empty(&my_rsv->rsv_window))
+ rsv_window_remove(sb, my_rsv);
+ }
+ my_rsv->rsv_start = reservable_space_start;
+ my_rsv->rsv_end = my_rsv->rsv_start + size - 1;
+ atomic_set(&my_rsv->rsv_alloc_hit, 0);
+ if (my_rsv != prev_rsv) {
+ ext3_rsv_window_add(sb, my_rsv);
+ }
+ return 0; /* succeed */
+failed:
+ /*
+ * failed to find a new reservation window in the current
+ * group, remove the current(stale) reservation window
+ * if there is any
+ */
+ if (!rsv_is_empty(&my_rsv->rsv_window))
+ rsv_window_remove(sb, my_rsv);
+ return -1; /* failed */
+}
+
+/*
+ * 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 someboday is looking for a free block (without
+ * reservation), and there are lots of free blocks, but they are all
+ * being reserved.
+ *
+ * We use a sorted double linked list for the per-filesystem reservation list.
+ * The insert, remove and find a free space(non-reserved) operations for the
+ * sorted double linked list should be fast.
+ *
+ */
+static int
+ext3_try_to_allocate_with_rsv(struct super_block *sb, handle_t *handle,
+ unsigned int group, struct buffer_head *bitmap_bh,
+ int goal, struct ext3_reserve_window_node * my_rsv,
+ int *errp)
+{
+ spinlock_t *rsv_lock;
+ unsigned long group_first_block;
+ int ret = 0;
+ int fatal;
+ int credits = 0;
+
+ *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 = ext3_journal_get_undo_access(handle, bitmap_bh, &credits);