18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 48c2ecf20Sopenharmony_ci#include <linux/kernel.h> 58c2ecf20Sopenharmony_ci#include <linux/ktime.h> 68c2ecf20Sopenharmony_ci#include <linux/list.h> 78c2ecf20Sopenharmony_ci#include <linux/math64.h> 88c2ecf20Sopenharmony_ci#include <linux/sizes.h> 98c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 108c2ecf20Sopenharmony_ci#include "ctree.h" 118c2ecf20Sopenharmony_ci#include "block-group.h" 128c2ecf20Sopenharmony_ci#include "discard.h" 138c2ecf20Sopenharmony_ci#include "free-space-cache.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* 168c2ecf20Sopenharmony_ci * This contains the logic to handle async discard. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * Async discard manages trimming of free space outside of transaction commit. 198c2ecf20Sopenharmony_ci * Discarding is done by managing the block_groups on a LRU list based on free 208c2ecf20Sopenharmony_ci * space recency. Two passes are used to first prioritize discarding extents 218c2ecf20Sopenharmony_ci * and then allow for trimming in the bitmap the best opportunity to coalesce. 228c2ecf20Sopenharmony_ci * The block_groups are maintained on multiple lists to allow for multiple 238c2ecf20Sopenharmony_ci * passes with different discard filter requirements. A delayed work item is 248c2ecf20Sopenharmony_ci * used to manage discarding with timeout determined by a max of the delay 258c2ecf20Sopenharmony_ci * incurred by the iops rate limit, the byte rate limit, and the max delay of 268c2ecf20Sopenharmony_ci * BTRFS_DISCARD_MAX_DELAY. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * Note, this only keeps track of block_groups that are explicitly for data. 298c2ecf20Sopenharmony_ci * Mixed block_groups are not supported. 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * The first list is special to manage discarding of fully free block groups. 328c2ecf20Sopenharmony_ci * This is necessary because we issue a final trim for a full free block group 338c2ecf20Sopenharmony_ci * after forgetting it. When a block group becomes unused, instead of directly 348c2ecf20Sopenharmony_ci * being added to the unused_bgs list, we add it to this first list. Then 358c2ecf20Sopenharmony_ci * from there, if it becomes fully discarded, we place it onto the unused_bgs 368c2ecf20Sopenharmony_ci * list. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * The in-memory free space cache serves as the backing state for discard. 398c2ecf20Sopenharmony_ci * Consequently this means there is no persistence. We opt to load all the 408c2ecf20Sopenharmony_ci * block groups in as not discarded, so the mount case degenerates to the 418c2ecf20Sopenharmony_ci * crashing case. 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * As the free space cache uses bitmaps, there exists a tradeoff between 448c2ecf20Sopenharmony_ci * ease/efficiency for find_free_extent() and the accuracy of discard state. 458c2ecf20Sopenharmony_ci * Here we opt to let untrimmed regions merge with everything while only letting 468c2ecf20Sopenharmony_ci * trimmed regions merge with other trimmed regions. This can cause 478c2ecf20Sopenharmony_ci * overtrimming, but the coalescing benefit seems to be worth it. Additionally, 488c2ecf20Sopenharmony_ci * bitmap state is tracked as a whole. If we're able to fully trim a bitmap, 498c2ecf20Sopenharmony_ci * the trimmed flag is set on the bitmap. Otherwise, if an allocation comes in, 508c2ecf20Sopenharmony_ci * this resets the state and we will retry trimming the whole bitmap. This is a 518c2ecf20Sopenharmony_ci * tradeoff between discard state accuracy and the cost of accounting. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* This is an initial delay to give some chance for block reuse */ 558c2ecf20Sopenharmony_ci#define BTRFS_DISCARD_DELAY (120ULL * NSEC_PER_SEC) 568c2ecf20Sopenharmony_ci#define BTRFS_DISCARD_UNUSED_DELAY (10ULL * NSEC_PER_SEC) 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* Target completion latency of discarding all discardable extents */ 598c2ecf20Sopenharmony_ci#define BTRFS_DISCARD_TARGET_MSEC (6 * 60 * 60UL * MSEC_PER_SEC) 608c2ecf20Sopenharmony_ci#define BTRFS_DISCARD_MIN_DELAY_MSEC (1UL) 618c2ecf20Sopenharmony_ci#define BTRFS_DISCARD_MAX_DELAY_MSEC (1000UL) 628c2ecf20Sopenharmony_ci#define BTRFS_DISCARD_MAX_IOPS (10U) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* Montonically decreasing minimum length filters after index 0 */ 658c2ecf20Sopenharmony_cistatic int discard_minlen[BTRFS_NR_DISCARD_LISTS] = { 668c2ecf20Sopenharmony_ci 0, 678c2ecf20Sopenharmony_ci BTRFS_ASYNC_DISCARD_MAX_FILTER, 688c2ecf20Sopenharmony_ci BTRFS_ASYNC_DISCARD_MIN_FILTER 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic struct list_head *get_discard_list(struct btrfs_discard_ctl *discard_ctl, 728c2ecf20Sopenharmony_ci struct btrfs_block_group *block_group) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci return &discard_ctl->discard_list[block_group->discard_index]; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic void __add_to_discard_list(struct btrfs_discard_ctl *discard_ctl, 788c2ecf20Sopenharmony_ci struct btrfs_block_group *block_group) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci if (!btrfs_run_discard_work(discard_ctl)) 818c2ecf20Sopenharmony_ci return; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (list_empty(&block_group->discard_list) || 848c2ecf20Sopenharmony_ci block_group->discard_index == BTRFS_DISCARD_INDEX_UNUSED) { 858c2ecf20Sopenharmony_ci if (block_group->discard_index == BTRFS_DISCARD_INDEX_UNUSED) 868c2ecf20Sopenharmony_ci block_group->discard_index = BTRFS_DISCARD_INDEX_START; 878c2ecf20Sopenharmony_ci block_group->discard_eligible_time = (ktime_get_ns() + 888c2ecf20Sopenharmony_ci BTRFS_DISCARD_DELAY); 898c2ecf20Sopenharmony_ci block_group->discard_state = BTRFS_DISCARD_RESET_CURSOR; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci list_move_tail(&block_group->discard_list, 938c2ecf20Sopenharmony_ci get_discard_list(discard_ctl, block_group)); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic void add_to_discard_list(struct btrfs_discard_ctl *discard_ctl, 978c2ecf20Sopenharmony_ci struct btrfs_block_group *block_group) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci if (!btrfs_is_block_group_data_only(block_group)) 1008c2ecf20Sopenharmony_ci return; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci spin_lock(&discard_ctl->lock); 1038c2ecf20Sopenharmony_ci __add_to_discard_list(discard_ctl, block_group); 1048c2ecf20Sopenharmony_ci spin_unlock(&discard_ctl->lock); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic void add_to_discard_unused_list(struct btrfs_discard_ctl *discard_ctl, 1088c2ecf20Sopenharmony_ci struct btrfs_block_group *block_group) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci spin_lock(&discard_ctl->lock); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (!btrfs_run_discard_work(discard_ctl)) { 1138c2ecf20Sopenharmony_ci spin_unlock(&discard_ctl->lock); 1148c2ecf20Sopenharmony_ci return; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci list_del_init(&block_group->discard_list); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci block_group->discard_index = BTRFS_DISCARD_INDEX_UNUSED; 1208c2ecf20Sopenharmony_ci block_group->discard_eligible_time = (ktime_get_ns() + 1218c2ecf20Sopenharmony_ci BTRFS_DISCARD_UNUSED_DELAY); 1228c2ecf20Sopenharmony_ci block_group->discard_state = BTRFS_DISCARD_RESET_CURSOR; 1238c2ecf20Sopenharmony_ci list_add_tail(&block_group->discard_list, 1248c2ecf20Sopenharmony_ci &discard_ctl->discard_list[BTRFS_DISCARD_INDEX_UNUSED]); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci spin_unlock(&discard_ctl->lock); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic bool remove_from_discard_list(struct btrfs_discard_ctl *discard_ctl, 1308c2ecf20Sopenharmony_ci struct btrfs_block_group *block_group) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci bool running = false; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci spin_lock(&discard_ctl->lock); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (block_group == discard_ctl->block_group) { 1378c2ecf20Sopenharmony_ci running = true; 1388c2ecf20Sopenharmony_ci discard_ctl->block_group = NULL; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci block_group->discard_eligible_time = 0; 1428c2ecf20Sopenharmony_ci list_del_init(&block_group->discard_list); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci spin_unlock(&discard_ctl->lock); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return running; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/** 1508c2ecf20Sopenharmony_ci * find_next_block_group - find block_group that's up next for discarding 1518c2ecf20Sopenharmony_ci * @discard_ctl: discard control 1528c2ecf20Sopenharmony_ci * @now: current time 1538c2ecf20Sopenharmony_ci * 1548c2ecf20Sopenharmony_ci * Iterate over the discard lists to find the next block_group up for 1558c2ecf20Sopenharmony_ci * discarding checking the discard_eligible_time of block_group. 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_cistatic struct btrfs_block_group *find_next_block_group( 1588c2ecf20Sopenharmony_ci struct btrfs_discard_ctl *discard_ctl, 1598c2ecf20Sopenharmony_ci u64 now) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct btrfs_block_group *ret_block_group = NULL, *block_group; 1628c2ecf20Sopenharmony_ci int i; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci for (i = 0; i < BTRFS_NR_DISCARD_LISTS; i++) { 1658c2ecf20Sopenharmony_ci struct list_head *discard_list = &discard_ctl->discard_list[i]; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (!list_empty(discard_list)) { 1688c2ecf20Sopenharmony_ci block_group = list_first_entry(discard_list, 1698c2ecf20Sopenharmony_ci struct btrfs_block_group, 1708c2ecf20Sopenharmony_ci discard_list); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (!ret_block_group) 1738c2ecf20Sopenharmony_ci ret_block_group = block_group; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (ret_block_group->discard_eligible_time < now) 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (ret_block_group->discard_eligible_time > 1798c2ecf20Sopenharmony_ci block_group->discard_eligible_time) 1808c2ecf20Sopenharmony_ci ret_block_group = block_group; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return ret_block_group; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci/** 1888c2ecf20Sopenharmony_ci * peek_discard_list - wrap find_next_block_group() 1898c2ecf20Sopenharmony_ci * @discard_ctl: discard control 1908c2ecf20Sopenharmony_ci * @discard_state: the discard_state of the block_group after state management 1918c2ecf20Sopenharmony_ci * @discard_index: the discard_index of the block_group after state management 1928c2ecf20Sopenharmony_ci * 1938c2ecf20Sopenharmony_ci * This wraps find_next_block_group() and sets the block_group to be in use. 1948c2ecf20Sopenharmony_ci * discard_state's control flow is managed here. Variables related to 1958c2ecf20Sopenharmony_ci * discard_state are reset here as needed (eg discard_cursor). @discard_state 1968c2ecf20Sopenharmony_ci * and @discard_index are remembered as it may change while we're discarding, 1978c2ecf20Sopenharmony_ci * but we want the discard to execute in the context determined here. 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_cistatic struct btrfs_block_group *peek_discard_list( 2008c2ecf20Sopenharmony_ci struct btrfs_discard_ctl *discard_ctl, 2018c2ecf20Sopenharmony_ci enum btrfs_discard_state *discard_state, 2028c2ecf20Sopenharmony_ci int *discard_index, u64 now) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci struct btrfs_block_group *block_group; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci spin_lock(&discard_ctl->lock); 2078c2ecf20Sopenharmony_ciagain: 2088c2ecf20Sopenharmony_ci block_group = find_next_block_group(discard_ctl, now); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (block_group && now >= block_group->discard_eligible_time) { 2118c2ecf20Sopenharmony_ci if (block_group->discard_index == BTRFS_DISCARD_INDEX_UNUSED && 2128c2ecf20Sopenharmony_ci block_group->used != 0) { 2138c2ecf20Sopenharmony_ci if (btrfs_is_block_group_data_only(block_group)) 2148c2ecf20Sopenharmony_ci __add_to_discard_list(discard_ctl, block_group); 2158c2ecf20Sopenharmony_ci else 2168c2ecf20Sopenharmony_ci list_del_init(&block_group->discard_list); 2178c2ecf20Sopenharmony_ci goto again; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci if (block_group->discard_state == BTRFS_DISCARD_RESET_CURSOR) { 2208c2ecf20Sopenharmony_ci block_group->discard_cursor = block_group->start; 2218c2ecf20Sopenharmony_ci block_group->discard_state = BTRFS_DISCARD_EXTENTS; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci discard_ctl->block_group = block_group; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci if (block_group) { 2268c2ecf20Sopenharmony_ci *discard_state = block_group->discard_state; 2278c2ecf20Sopenharmony_ci *discard_index = block_group->discard_index; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci spin_unlock(&discard_ctl->lock); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return block_group; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci/** 2358c2ecf20Sopenharmony_ci * btrfs_discard_check_filter - updates a block groups filters 2368c2ecf20Sopenharmony_ci * @block_group: block group of interest 2378c2ecf20Sopenharmony_ci * @bytes: recently freed region size after coalescing 2388c2ecf20Sopenharmony_ci * 2398c2ecf20Sopenharmony_ci * Async discard maintains multiple lists with progressively smaller filters 2408c2ecf20Sopenharmony_ci * to prioritize discarding based on size. Should a free space that matches 2418c2ecf20Sopenharmony_ci * a larger filter be returned to the free_space_cache, prioritize that discard 2428c2ecf20Sopenharmony_ci * by moving @block_group to the proper filter. 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_civoid btrfs_discard_check_filter(struct btrfs_block_group *block_group, 2458c2ecf20Sopenharmony_ci u64 bytes) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct btrfs_discard_ctl *discard_ctl; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (!block_group || 2508c2ecf20Sopenharmony_ci !btrfs_test_opt(block_group->fs_info, DISCARD_ASYNC)) 2518c2ecf20Sopenharmony_ci return; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci discard_ctl = &block_group->fs_info->discard_ctl; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (block_group->discard_index > BTRFS_DISCARD_INDEX_START && 2568c2ecf20Sopenharmony_ci bytes >= discard_minlen[block_group->discard_index - 1]) { 2578c2ecf20Sopenharmony_ci int i; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci remove_from_discard_list(discard_ctl, block_group); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci for (i = BTRFS_DISCARD_INDEX_START; i < BTRFS_NR_DISCARD_LISTS; 2628c2ecf20Sopenharmony_ci i++) { 2638c2ecf20Sopenharmony_ci if (bytes >= discard_minlen[i]) { 2648c2ecf20Sopenharmony_ci block_group->discard_index = i; 2658c2ecf20Sopenharmony_ci add_to_discard_list(discard_ctl, block_group); 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/** 2738c2ecf20Sopenharmony_ci * btrfs_update_discard_index - moves a block group along the discard lists 2748c2ecf20Sopenharmony_ci * @discard_ctl: discard control 2758c2ecf20Sopenharmony_ci * @block_group: block_group of interest 2768c2ecf20Sopenharmony_ci * 2778c2ecf20Sopenharmony_ci * Increment @block_group's discard_index. If it falls of the list, let it be. 2788c2ecf20Sopenharmony_ci * Otherwise add it back to the appropriate list. 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_cistatic void btrfs_update_discard_index(struct btrfs_discard_ctl *discard_ctl, 2818c2ecf20Sopenharmony_ci struct btrfs_block_group *block_group) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci block_group->discard_index++; 2848c2ecf20Sopenharmony_ci if (block_group->discard_index == BTRFS_NR_DISCARD_LISTS) { 2858c2ecf20Sopenharmony_ci block_group->discard_index = 1; 2868c2ecf20Sopenharmony_ci return; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci add_to_discard_list(discard_ctl, block_group); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/** 2938c2ecf20Sopenharmony_ci * btrfs_discard_cancel_work - remove a block_group from the discard lists 2948c2ecf20Sopenharmony_ci * @discard_ctl: discard control 2958c2ecf20Sopenharmony_ci * @block_group: block_group of interest 2968c2ecf20Sopenharmony_ci * 2978c2ecf20Sopenharmony_ci * This removes @block_group from the discard lists. If necessary, it waits on 2988c2ecf20Sopenharmony_ci * the current work and then reschedules the delayed work. 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_civoid btrfs_discard_cancel_work(struct btrfs_discard_ctl *discard_ctl, 3018c2ecf20Sopenharmony_ci struct btrfs_block_group *block_group) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci if (remove_from_discard_list(discard_ctl, block_group)) { 3048c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&discard_ctl->work); 3058c2ecf20Sopenharmony_ci btrfs_discard_schedule_work(discard_ctl, true); 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci/** 3108c2ecf20Sopenharmony_ci * btrfs_discard_queue_work - handles queuing the block_groups 3118c2ecf20Sopenharmony_ci * @discard_ctl: discard control 3128c2ecf20Sopenharmony_ci * @block_group: block_group of interest 3138c2ecf20Sopenharmony_ci * 3148c2ecf20Sopenharmony_ci * This maintains the LRU order of the discard lists. 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_civoid btrfs_discard_queue_work(struct btrfs_discard_ctl *discard_ctl, 3178c2ecf20Sopenharmony_ci struct btrfs_block_group *block_group) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci if (!block_group || !btrfs_test_opt(block_group->fs_info, DISCARD_ASYNC)) 3208c2ecf20Sopenharmony_ci return; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (block_group->used == 0) 3238c2ecf20Sopenharmony_ci add_to_discard_unused_list(discard_ctl, block_group); 3248c2ecf20Sopenharmony_ci else 3258c2ecf20Sopenharmony_ci add_to_discard_list(discard_ctl, block_group); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (!delayed_work_pending(&discard_ctl->work)) 3288c2ecf20Sopenharmony_ci btrfs_discard_schedule_work(discard_ctl, false); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic void __btrfs_discard_schedule_work(struct btrfs_discard_ctl *discard_ctl, 3328c2ecf20Sopenharmony_ci u64 now, bool override) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct btrfs_block_group *block_group; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (!btrfs_run_discard_work(discard_ctl)) 3378c2ecf20Sopenharmony_ci return; 3388c2ecf20Sopenharmony_ci if (!override && delayed_work_pending(&discard_ctl->work)) 3398c2ecf20Sopenharmony_ci return; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci block_group = find_next_block_group(discard_ctl, now); 3428c2ecf20Sopenharmony_ci if (block_group) { 3438c2ecf20Sopenharmony_ci unsigned long delay = discard_ctl->delay; 3448c2ecf20Sopenharmony_ci u32 kbps_limit = READ_ONCE(discard_ctl->kbps_limit); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* 3478c2ecf20Sopenharmony_ci * A single delayed workqueue item is responsible for 3488c2ecf20Sopenharmony_ci * discarding, so we can manage the bytes rate limit by keeping 3498c2ecf20Sopenharmony_ci * track of the previous discard. 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_ci if (kbps_limit && discard_ctl->prev_discard) { 3528c2ecf20Sopenharmony_ci u64 bps_limit = ((u64)kbps_limit) * SZ_1K; 3538c2ecf20Sopenharmony_ci u64 bps_delay = div64_u64(discard_ctl->prev_discard * 3548c2ecf20Sopenharmony_ci MSEC_PER_SEC, bps_limit); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci delay = max(delay, msecs_to_jiffies(bps_delay)); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* 3608c2ecf20Sopenharmony_ci * This timeout is to hopefully prevent immediate discarding 3618c2ecf20Sopenharmony_ci * in a recently allocated block group. 3628c2ecf20Sopenharmony_ci */ 3638c2ecf20Sopenharmony_ci if (now < block_group->discard_eligible_time) { 3648c2ecf20Sopenharmony_ci u64 bg_timeout = block_group->discard_eligible_time - now; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci delay = max(delay, nsecs_to_jiffies(bg_timeout)); 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci mod_delayed_work(discard_ctl->discard_workers, 3708c2ecf20Sopenharmony_ci &discard_ctl->work, delay); 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci/* 3758c2ecf20Sopenharmony_ci * btrfs_discard_schedule_work - responsible for scheduling the discard work 3768c2ecf20Sopenharmony_ci * @discard_ctl: discard control 3778c2ecf20Sopenharmony_ci * @override: override the current timer 3788c2ecf20Sopenharmony_ci * 3798c2ecf20Sopenharmony_ci * Discards are issued by a delayed workqueue item. @override is used to 3808c2ecf20Sopenharmony_ci * update the current delay as the baseline delay interval is reevaluated on 3818c2ecf20Sopenharmony_ci * transaction commit. This is also maxed with any other rate limit. 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_civoid btrfs_discard_schedule_work(struct btrfs_discard_ctl *discard_ctl, 3848c2ecf20Sopenharmony_ci bool override) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci const u64 now = ktime_get_ns(); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci spin_lock(&discard_ctl->lock); 3898c2ecf20Sopenharmony_ci __btrfs_discard_schedule_work(discard_ctl, now, override); 3908c2ecf20Sopenharmony_ci spin_unlock(&discard_ctl->lock); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/** 3948c2ecf20Sopenharmony_ci * btrfs_finish_discard_pass - determine next step of a block_group 3958c2ecf20Sopenharmony_ci * @discard_ctl: discard control 3968c2ecf20Sopenharmony_ci * @block_group: block_group of interest 3978c2ecf20Sopenharmony_ci * 3988c2ecf20Sopenharmony_ci * This determines the next step for a block group after it's finished going 3998c2ecf20Sopenharmony_ci * through a pass on a discard list. If it is unused and fully trimmed, we can 4008c2ecf20Sopenharmony_ci * mark it unused and send it to the unused_bgs path. Otherwise, pass it onto 4018c2ecf20Sopenharmony_ci * the appropriate filter list or let it fall off. 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_cistatic void btrfs_finish_discard_pass(struct btrfs_discard_ctl *discard_ctl, 4048c2ecf20Sopenharmony_ci struct btrfs_block_group *block_group) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci remove_from_discard_list(discard_ctl, block_group); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (block_group->used == 0) { 4098c2ecf20Sopenharmony_ci if (btrfs_is_free_space_trimmed(block_group)) 4108c2ecf20Sopenharmony_ci btrfs_mark_bg_unused(block_group); 4118c2ecf20Sopenharmony_ci else 4128c2ecf20Sopenharmony_ci add_to_discard_unused_list(discard_ctl, block_group); 4138c2ecf20Sopenharmony_ci } else { 4148c2ecf20Sopenharmony_ci btrfs_update_discard_index(discard_ctl, block_group); 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci/** 4198c2ecf20Sopenharmony_ci * btrfs_discard_workfn - discard work function 4208c2ecf20Sopenharmony_ci * @work: work 4218c2ecf20Sopenharmony_ci * 4228c2ecf20Sopenharmony_ci * This finds the next block_group to start discarding and then discards a 4238c2ecf20Sopenharmony_ci * single region. It does this in a two-pass fashion: first extents and second 4248c2ecf20Sopenharmony_ci * bitmaps. Completely discarded block groups are sent to the unused_bgs path. 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_cistatic void btrfs_discard_workfn(struct work_struct *work) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct btrfs_discard_ctl *discard_ctl; 4298c2ecf20Sopenharmony_ci struct btrfs_block_group *block_group; 4308c2ecf20Sopenharmony_ci enum btrfs_discard_state discard_state; 4318c2ecf20Sopenharmony_ci int discard_index = 0; 4328c2ecf20Sopenharmony_ci u64 trimmed = 0; 4338c2ecf20Sopenharmony_ci u64 minlen = 0; 4348c2ecf20Sopenharmony_ci u64 now = ktime_get_ns(); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci discard_ctl = container_of(work, struct btrfs_discard_ctl, work.work); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci block_group = peek_discard_list(discard_ctl, &discard_state, 4398c2ecf20Sopenharmony_ci &discard_index, now); 4408c2ecf20Sopenharmony_ci if (!block_group || !btrfs_run_discard_work(discard_ctl)) 4418c2ecf20Sopenharmony_ci return; 4428c2ecf20Sopenharmony_ci if (now < block_group->discard_eligible_time) { 4438c2ecf20Sopenharmony_ci btrfs_discard_schedule_work(discard_ctl, false); 4448c2ecf20Sopenharmony_ci return; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* Perform discarding */ 4488c2ecf20Sopenharmony_ci minlen = discard_minlen[discard_index]; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (discard_state == BTRFS_DISCARD_BITMAPS) { 4518c2ecf20Sopenharmony_ci u64 maxlen = 0; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* 4548c2ecf20Sopenharmony_ci * Use the previous levels minimum discard length as the max 4558c2ecf20Sopenharmony_ci * length filter. In the case something is added to make a 4568c2ecf20Sopenharmony_ci * region go beyond the max filter, the entire bitmap is set 4578c2ecf20Sopenharmony_ci * back to BTRFS_TRIM_STATE_UNTRIMMED. 4588c2ecf20Sopenharmony_ci */ 4598c2ecf20Sopenharmony_ci if (discard_index != BTRFS_DISCARD_INDEX_UNUSED) 4608c2ecf20Sopenharmony_ci maxlen = discard_minlen[discard_index - 1]; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci btrfs_trim_block_group_bitmaps(block_group, &trimmed, 4638c2ecf20Sopenharmony_ci block_group->discard_cursor, 4648c2ecf20Sopenharmony_ci btrfs_block_group_end(block_group), 4658c2ecf20Sopenharmony_ci minlen, maxlen, true); 4668c2ecf20Sopenharmony_ci discard_ctl->discard_bitmap_bytes += trimmed; 4678c2ecf20Sopenharmony_ci } else { 4688c2ecf20Sopenharmony_ci btrfs_trim_block_group_extents(block_group, &trimmed, 4698c2ecf20Sopenharmony_ci block_group->discard_cursor, 4708c2ecf20Sopenharmony_ci btrfs_block_group_end(block_group), 4718c2ecf20Sopenharmony_ci minlen, true); 4728c2ecf20Sopenharmony_ci discard_ctl->discard_extent_bytes += trimmed; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci discard_ctl->prev_discard = trimmed; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* Determine next steps for a block_group */ 4788c2ecf20Sopenharmony_ci if (block_group->discard_cursor >= btrfs_block_group_end(block_group)) { 4798c2ecf20Sopenharmony_ci if (discard_state == BTRFS_DISCARD_BITMAPS) { 4808c2ecf20Sopenharmony_ci btrfs_finish_discard_pass(discard_ctl, block_group); 4818c2ecf20Sopenharmony_ci } else { 4828c2ecf20Sopenharmony_ci block_group->discard_cursor = block_group->start; 4838c2ecf20Sopenharmony_ci spin_lock(&discard_ctl->lock); 4848c2ecf20Sopenharmony_ci if (block_group->discard_state != 4858c2ecf20Sopenharmony_ci BTRFS_DISCARD_RESET_CURSOR) 4868c2ecf20Sopenharmony_ci block_group->discard_state = 4878c2ecf20Sopenharmony_ci BTRFS_DISCARD_BITMAPS; 4888c2ecf20Sopenharmony_ci spin_unlock(&discard_ctl->lock); 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci spin_lock(&discard_ctl->lock); 4938c2ecf20Sopenharmony_ci discard_ctl->block_group = NULL; 4948c2ecf20Sopenharmony_ci __btrfs_discard_schedule_work(discard_ctl, now, false); 4958c2ecf20Sopenharmony_ci spin_unlock(&discard_ctl->lock); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci/** 4998c2ecf20Sopenharmony_ci * btrfs_run_discard_work - determines if async discard should be running 5008c2ecf20Sopenharmony_ci * @discard_ctl: discard control 5018c2ecf20Sopenharmony_ci * 5028c2ecf20Sopenharmony_ci * Checks if the file system is writeable and BTRFS_FS_DISCARD_RUNNING is set. 5038c2ecf20Sopenharmony_ci */ 5048c2ecf20Sopenharmony_cibool btrfs_run_discard_work(struct btrfs_discard_ctl *discard_ctl) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci struct btrfs_fs_info *fs_info = container_of(discard_ctl, 5078c2ecf20Sopenharmony_ci struct btrfs_fs_info, 5088c2ecf20Sopenharmony_ci discard_ctl); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return (!(fs_info->sb->s_flags & SB_RDONLY) && 5118c2ecf20Sopenharmony_ci test_bit(BTRFS_FS_DISCARD_RUNNING, &fs_info->flags)); 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci/** 5158c2ecf20Sopenharmony_ci * btrfs_discard_calc_delay - recalculate the base delay 5168c2ecf20Sopenharmony_ci * @discard_ctl: discard control 5178c2ecf20Sopenharmony_ci * 5188c2ecf20Sopenharmony_ci * Recalculate the base delay which is based off the total number of 5198c2ecf20Sopenharmony_ci * discardable_extents. Clamp this between the lower_limit (iops_limit or 1ms) 5208c2ecf20Sopenharmony_ci * and the upper_limit (BTRFS_DISCARD_MAX_DELAY_MSEC). 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_civoid btrfs_discard_calc_delay(struct btrfs_discard_ctl *discard_ctl) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci s32 discardable_extents; 5258c2ecf20Sopenharmony_ci s64 discardable_bytes; 5268c2ecf20Sopenharmony_ci u32 iops_limit; 5278c2ecf20Sopenharmony_ci unsigned long delay; 5288c2ecf20Sopenharmony_ci unsigned long lower_limit = BTRFS_DISCARD_MIN_DELAY_MSEC; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci discardable_extents = atomic_read(&discard_ctl->discardable_extents); 5318c2ecf20Sopenharmony_ci if (!discardable_extents) 5328c2ecf20Sopenharmony_ci return; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci spin_lock(&discard_ctl->lock); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* 5378c2ecf20Sopenharmony_ci * The following is to fix a potential -1 discrepenancy that we're not 5388c2ecf20Sopenharmony_ci * sure how to reproduce. But given that this is the only place that 5398c2ecf20Sopenharmony_ci * utilizes these numbers and this is only called by from 5408c2ecf20Sopenharmony_ci * btrfs_finish_extent_commit() which is synchronized, we can correct 5418c2ecf20Sopenharmony_ci * here. 5428c2ecf20Sopenharmony_ci */ 5438c2ecf20Sopenharmony_ci if (discardable_extents < 0) 5448c2ecf20Sopenharmony_ci atomic_add(-discardable_extents, 5458c2ecf20Sopenharmony_ci &discard_ctl->discardable_extents); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci discardable_bytes = atomic64_read(&discard_ctl->discardable_bytes); 5488c2ecf20Sopenharmony_ci if (discardable_bytes < 0) 5498c2ecf20Sopenharmony_ci atomic64_add(-discardable_bytes, 5508c2ecf20Sopenharmony_ci &discard_ctl->discardable_bytes); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (discardable_extents <= 0) { 5538c2ecf20Sopenharmony_ci spin_unlock(&discard_ctl->lock); 5548c2ecf20Sopenharmony_ci return; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci iops_limit = READ_ONCE(discard_ctl->iops_limit); 5588c2ecf20Sopenharmony_ci if (iops_limit) 5598c2ecf20Sopenharmony_ci lower_limit = max_t(unsigned long, lower_limit, 5608c2ecf20Sopenharmony_ci MSEC_PER_SEC / iops_limit); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci delay = BTRFS_DISCARD_TARGET_MSEC / discardable_extents; 5638c2ecf20Sopenharmony_ci delay = clamp(delay, lower_limit, BTRFS_DISCARD_MAX_DELAY_MSEC); 5648c2ecf20Sopenharmony_ci discard_ctl->delay = msecs_to_jiffies(delay); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci spin_unlock(&discard_ctl->lock); 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci/** 5708c2ecf20Sopenharmony_ci * btrfs_discard_update_discardable - propagate discard counters 5718c2ecf20Sopenharmony_ci * @block_group: block_group of interest 5728c2ecf20Sopenharmony_ci * @ctl: free_space_ctl of @block_group 5738c2ecf20Sopenharmony_ci * 5748c2ecf20Sopenharmony_ci * This propagates deltas of counters up to the discard_ctl. It maintains a 5758c2ecf20Sopenharmony_ci * current counter and a previous counter passing the delta up to the global 5768c2ecf20Sopenharmony_ci * stat. Then the current counter value becomes the previous counter value. 5778c2ecf20Sopenharmony_ci */ 5788c2ecf20Sopenharmony_civoid btrfs_discard_update_discardable(struct btrfs_block_group *block_group, 5798c2ecf20Sopenharmony_ci struct btrfs_free_space_ctl *ctl) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci struct btrfs_discard_ctl *discard_ctl; 5828c2ecf20Sopenharmony_ci s32 extents_delta; 5838c2ecf20Sopenharmony_ci s64 bytes_delta; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (!block_group || 5868c2ecf20Sopenharmony_ci !btrfs_test_opt(block_group->fs_info, DISCARD_ASYNC) || 5878c2ecf20Sopenharmony_ci !btrfs_is_block_group_data_only(block_group)) 5888c2ecf20Sopenharmony_ci return; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci discard_ctl = &block_group->fs_info->discard_ctl; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci extents_delta = ctl->discardable_extents[BTRFS_STAT_CURR] - 5938c2ecf20Sopenharmony_ci ctl->discardable_extents[BTRFS_STAT_PREV]; 5948c2ecf20Sopenharmony_ci if (extents_delta) { 5958c2ecf20Sopenharmony_ci atomic_add(extents_delta, &discard_ctl->discardable_extents); 5968c2ecf20Sopenharmony_ci ctl->discardable_extents[BTRFS_STAT_PREV] = 5978c2ecf20Sopenharmony_ci ctl->discardable_extents[BTRFS_STAT_CURR]; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci bytes_delta = ctl->discardable_bytes[BTRFS_STAT_CURR] - 6018c2ecf20Sopenharmony_ci ctl->discardable_bytes[BTRFS_STAT_PREV]; 6028c2ecf20Sopenharmony_ci if (bytes_delta) { 6038c2ecf20Sopenharmony_ci atomic64_add(bytes_delta, &discard_ctl->discardable_bytes); 6048c2ecf20Sopenharmony_ci ctl->discardable_bytes[BTRFS_STAT_PREV] = 6058c2ecf20Sopenharmony_ci ctl->discardable_bytes[BTRFS_STAT_CURR]; 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci/** 6108c2ecf20Sopenharmony_ci * btrfs_discard_punt_unused_bgs_list - punt unused_bgs list to discard lists 6118c2ecf20Sopenharmony_ci * @fs_info: fs_info of interest 6128c2ecf20Sopenharmony_ci * 6138c2ecf20Sopenharmony_ci * The unused_bgs list needs to be punted to the discard lists because the 6148c2ecf20Sopenharmony_ci * order of operations is changed. In the normal sychronous discard path, the 6158c2ecf20Sopenharmony_ci * block groups are trimmed via a single large trim in transaction commit. This 6168c2ecf20Sopenharmony_ci * is ultimately what we are trying to avoid with asynchronous discard. Thus, 6178c2ecf20Sopenharmony_ci * it must be done before going down the unused_bgs path. 6188c2ecf20Sopenharmony_ci */ 6198c2ecf20Sopenharmony_civoid btrfs_discard_punt_unused_bgs_list(struct btrfs_fs_info *fs_info) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci struct btrfs_block_group *block_group, *next; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci spin_lock(&fs_info->unused_bgs_lock); 6248c2ecf20Sopenharmony_ci /* We enabled async discard, so punt all to the queue */ 6258c2ecf20Sopenharmony_ci list_for_each_entry_safe(block_group, next, &fs_info->unused_bgs, 6268c2ecf20Sopenharmony_ci bg_list) { 6278c2ecf20Sopenharmony_ci list_del_init(&block_group->bg_list); 6288c2ecf20Sopenharmony_ci btrfs_put_block_group(block_group); 6298c2ecf20Sopenharmony_ci btrfs_discard_queue_work(&fs_info->discard_ctl, block_group); 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci spin_unlock(&fs_info->unused_bgs_lock); 6328c2ecf20Sopenharmony_ci} 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci/** 6358c2ecf20Sopenharmony_ci * btrfs_discard_purge_list - purge discard lists 6368c2ecf20Sopenharmony_ci * @discard_ctl: discard control 6378c2ecf20Sopenharmony_ci * 6388c2ecf20Sopenharmony_ci * If we are disabling async discard, we may have intercepted block groups that 6398c2ecf20Sopenharmony_ci * are completely free and ready for the unused_bgs path. As discarding will 6408c2ecf20Sopenharmony_ci * now happen in transaction commit or not at all, we can safely mark the 6418c2ecf20Sopenharmony_ci * corresponding block groups as unused and they will be sent on their merry 6428c2ecf20Sopenharmony_ci * way to the unused_bgs list. 6438c2ecf20Sopenharmony_ci */ 6448c2ecf20Sopenharmony_cistatic void btrfs_discard_purge_list(struct btrfs_discard_ctl *discard_ctl) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci struct btrfs_block_group *block_group, *next; 6478c2ecf20Sopenharmony_ci int i; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci spin_lock(&discard_ctl->lock); 6508c2ecf20Sopenharmony_ci for (i = 0; i < BTRFS_NR_DISCARD_LISTS; i++) { 6518c2ecf20Sopenharmony_ci list_for_each_entry_safe(block_group, next, 6528c2ecf20Sopenharmony_ci &discard_ctl->discard_list[i], 6538c2ecf20Sopenharmony_ci discard_list) { 6548c2ecf20Sopenharmony_ci list_del_init(&block_group->discard_list); 6558c2ecf20Sopenharmony_ci spin_unlock(&discard_ctl->lock); 6568c2ecf20Sopenharmony_ci if (block_group->used == 0) 6578c2ecf20Sopenharmony_ci btrfs_mark_bg_unused(block_group); 6588c2ecf20Sopenharmony_ci spin_lock(&discard_ctl->lock); 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci spin_unlock(&discard_ctl->lock); 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_civoid btrfs_discard_resume(struct btrfs_fs_info *fs_info) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci if (!btrfs_test_opt(fs_info, DISCARD_ASYNC)) { 6678c2ecf20Sopenharmony_ci btrfs_discard_cleanup(fs_info); 6688c2ecf20Sopenharmony_ci return; 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci btrfs_discard_punt_unused_bgs_list(fs_info); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci set_bit(BTRFS_FS_DISCARD_RUNNING, &fs_info->flags); 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_civoid btrfs_discard_stop(struct btrfs_fs_info *fs_info) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci clear_bit(BTRFS_FS_DISCARD_RUNNING, &fs_info->flags); 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_civoid btrfs_discard_init(struct btrfs_fs_info *fs_info) 6828c2ecf20Sopenharmony_ci{ 6838c2ecf20Sopenharmony_ci struct btrfs_discard_ctl *discard_ctl = &fs_info->discard_ctl; 6848c2ecf20Sopenharmony_ci int i; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci spin_lock_init(&discard_ctl->lock); 6878c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&discard_ctl->work, btrfs_discard_workfn); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci for (i = 0; i < BTRFS_NR_DISCARD_LISTS; i++) 6908c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&discard_ctl->discard_list[i]); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci discard_ctl->prev_discard = 0; 6938c2ecf20Sopenharmony_ci atomic_set(&discard_ctl->discardable_extents, 0); 6948c2ecf20Sopenharmony_ci atomic64_set(&discard_ctl->discardable_bytes, 0); 6958c2ecf20Sopenharmony_ci discard_ctl->max_discard_size = BTRFS_ASYNC_DISCARD_DEFAULT_MAX_SIZE; 6968c2ecf20Sopenharmony_ci discard_ctl->delay = BTRFS_DISCARD_MAX_DELAY_MSEC; 6978c2ecf20Sopenharmony_ci discard_ctl->iops_limit = BTRFS_DISCARD_MAX_IOPS; 6988c2ecf20Sopenharmony_ci discard_ctl->kbps_limit = 0; 6998c2ecf20Sopenharmony_ci discard_ctl->discard_extent_bytes = 0; 7008c2ecf20Sopenharmony_ci discard_ctl->discard_bitmap_bytes = 0; 7018c2ecf20Sopenharmony_ci atomic64_set(&discard_ctl->discard_bytes_saved, 0); 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_civoid btrfs_discard_cleanup(struct btrfs_fs_info *fs_info) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci btrfs_discard_stop(fs_info); 7078c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&fs_info->discard_ctl.work); 7088c2ecf20Sopenharmony_ci btrfs_discard_purge_list(&fs_info->discard_ctl); 7098c2ecf20Sopenharmony_ci} 710