ext4_mb_discard_group_preallocations(struct super_block *sb,
					ext4_group_t group, int needed)
{
	struct ext4_group_info *grp = ext4_get_group_info(sb, group);
	struct buffer_head *bitmap_bh = NULL;
	struct ext4_prealloc_space *pa, *tmp;
	struct list_head list;
	struct ext4_buddy e4b;
	int err;
	int busy = 0;
	int free = 0;

	mb_debug(sb, "discard preallocation for group %u\n", group);
	if (list_empty(&grp->bb_prealloc_list))
		goto out_dbg;

	bitmap_bh = ext4_read_block_bitmap(sb, group);
	if (IS_ERR(bitmap_bh)) {
		err = PTR_ERR(bitmap_bh);
		ext4_error_err(sb, -err,
			       "Error %d reading block bitmap for %u",
			       err, group);
		goto out_dbg;
	}

	err = ext4_mb_load_buddy(sb, group, &e4b);
	if (err) {
		ext4_warning(sb, "Error %d loading buddy information for %u",
			     err, group);
		put_bh(bitmap_bh);
		goto out_dbg;
	}

	if (needed == 0)
		needed = EXT4_CLUSTERS_PER_GROUP(sb) + 1;

	INIT_LIST_HEAD(&list);
repeat:
	ext4_lock_group(sb, group);
	this_cpu_inc(discard_pa_seq);
	list_for_each_entry_safe(pa, tmp,
				&grp->bb_prealloc_list, pa_group_list) {
		spin_lock(&pa->pa_lock);
		if (atomic_read(&pa->pa_count)) {
			spin_unlock(&pa->pa_lock);
			busy = 1;
			continue;
		}
		if (pa->pa_deleted) {
			spin_unlock(&pa->pa_lock);
			continue;
		}

		/* seems this one can be freed ... */
		pa->pa_deleted = 1;

		/* we can trust pa_free ... */
		free += pa->pa_free;

		spin_unlock(&pa->pa_lock);

		list_del(&pa->pa_group_list);
		list_add(&pa->u.pa_tmp_list, &list);
	}

	/* if we still need more blocks and some PAs were used, try again */
	if (free < needed && busy) {
		busy = 0;
		ext4_unlock_group(sb, group);
		cond_resched();
		goto repeat;
	}

	/* found anything to free? */
	if (list_empty(&list)) {
		BUG_ON(free != 0);
		mb_debug(sb, "Someone else may have freed PA for this group %u\n",
			 group);
		goto out;
	}

	/* now free all selected PAs */
	list_for_each_entry_safe(pa, tmp, &list, u.pa_tmp_list) {

		/* remove from object (inode or locality group) */
		spin_lock(pa->pa_obj_lock);
		list_del_rcu(&pa->pa_inode_list);
		spin_unlock(pa->pa_obj_lock);

		if (pa->pa_type == MB_GROUP_PA)
			ext4_mb_release_group_pa(&e4b, pa);
		else
			ext4_mb_release_inode_pa(&e4b, bitmap_bh, pa);

		list_del(&pa->u.pa_tmp_list);
		call_rcu(&(pa)->u.pa_rcu, ext4_mb_pa_callback);
	}

out:
	ext4_unlock_group(sb, group);
	ext4_mb_unload_buddy(&e4b);
	put_bh(bitmap_bh);
out_dbg:
	mb_debug(sb, "discarded (%d) blocks preallocated for group %u bb_free (%d)\n",
		 free, group, grp->bb_free);
	return free;
}
ext4_mb_discard_group_preallocations(struct super_block *sb,
					ext4_group_t group, int needed)
{
	struct ext4_group_info *grp = ext4_get_group_info(sb, group);
	struct buffer_head *bitmap_bh = NULL;
	struct ext4_prealloc_space *pa, *tmp;
	struct list_head list;
	struct ext4_buddy e4b;
	int err;
	int busy = 0;
	int free = 0;

	mb_debug(sb, "discard preallocation for group %u\n", group);
	if (list_empty(&grp->bb_prealloc_list))
		goto out_dbg;

	bitmap_bh = ext4_read_block_bitmap(sb, group);
	if (IS_ERR(bitmap_bh)) {
		err = PTR_ERR(bitmap_bh);
		ext4_error_err(sb, -err,
			       "Error %d reading block bitmap for %u",
			       err, group);
		goto out_dbg;
	}

	err = ext4_mb_load_buddy(sb, group, &e4b);
	if (err) {
		ext4_warning(sb, "Error %d loading buddy information for %u",
			     err, group);
		put_bh(bitmap_bh);
		goto out_dbg;
	}

	if (needed == 0)
		needed = EXT4_CLUSTERS_PER_GROUP(sb) + 1;

	INIT_LIST_HEAD(&list);
repeat:
	ext4_lock_group(sb, group);
	this_cpu_inc(discard_pa_seq);
	list_for_each_entry_safe(pa, tmp,
				&grp->bb_prealloc_list, pa_group_list) {
		spin_lock(&pa->pa_lock);
		if (atomic_read(&pa->pa_count)) {
			spin_unlock(&pa->pa_lock);
			busy = 1;
			continue;
		}
		if (pa->pa_deleted) {
			spin_unlock(&pa->pa_lock);
			continue;
		}

		 
		pa->pa_deleted = 1;

		 
		free += pa->pa_free;

		spin_unlock(&pa->pa_lock);

		list_del(&pa->pa_group_list);
		list_add(&pa->u.pa_tmp_list, &list);
	}

	 
	if (free < needed && busy) {
		busy = 0;
		ext4_unlock_group(sb, group);
		cond_resched();
		goto repeat;
	}

	 
	if (list_empty(&list)) {
		BUG_ON(free != 0);
		mb_debug(sb, "Someone else may have freed PA for this group %u\n",
			 group);
		goto out;
	}

	 
	list_for_each_entry_safe(pa, tmp, &list, u.pa_tmp_list) {

		 
		spin_lock(pa->pa_obj_lock);
		list_del_rcu(&pa->pa_inode_list);
		spin_unlock(pa->pa_obj_lock);

		if (pa->pa_type == MB_GROUP_PA)
			ext4_mb_release_group_pa(&e4b, pa);
		else
			ext4_mb_release_inode_pa(&e4b, bitmap_bh, pa);

		list_del(&pa->u.pa_tmp_list);
		call_rcu(&(pa)->u.pa_rcu, ext4_mb_pa_callback);
	}

out:
	ext4_unlock_group(sb, group);
	ext4_mb_unload_buddy(&e4b);
	put_bh(bitmap_bh);
out_dbg:
	mb_debug(sb, "discarded (%d) blocks preallocated for group %u bb_free (%d)\n",
		 free, group, grp->bb_free);
	return free;
}