aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorLi Dongyang <lidongyang@novell.com>2011-03-24 06:24:28 -0400
committerroot <Chris Mason chris.mason@oracle.com>2011-03-28 05:37:47 -0400
commitf7039b1d5c32241f87a513e33120db36bf30264d (patch)
treee911bc79c795fceb3e9a618d8f18571c2a776307 /fs/btrfs
parent5378e60734f5b7bfe1b43dc191aaf6131c1befe7 (diff)
Btrfs: add btrfs_trim_fs() to handle FITRIM
We take an free extent out from allocator, trim it, then put it back, but before we trim the block group, we should make sure the block group is cached, so plus a little change to make cache_block_group() run without a transaction. Signed-off-by: Li Dongyang <lidongyang@novell.com> Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/ctree.h1
-rw-r--r--fs/btrfs/extent-tree.c50
-rw-r--r--fs/btrfs/free-space-cache.c92
-rw-r--r--fs/btrfs/free-space-cache.h2
-rw-r--r--fs/btrfs/ioctl.c46
5 files changed, 190 insertions, 1 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index a18b7bc2b22c..93a0191aded6 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2232,6 +2232,7 @@ int btrfs_error_discard_extent(struct btrfs_root *root, u64 bytenr,
2232 u64 num_bytes, u64 *actual_bytes); 2232 u64 num_bytes, u64 *actual_bytes);
2233int btrfs_force_chunk_alloc(struct btrfs_trans_handle *trans, 2233int btrfs_force_chunk_alloc(struct btrfs_trans_handle *trans,
2234 struct btrfs_root *root, u64 type); 2234 struct btrfs_root *root, u64 type);
2235int btrfs_trim_fs(struct btrfs_root *root, struct fstrim_range *range);
2235 2236
2236/* ctree.c */ 2237/* ctree.c */
2237int btrfs_bin_search(struct extent_buffer *eb, struct btrfs_key *key, 2238int btrfs_bin_search(struct extent_buffer *eb, struct btrfs_key *key,
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index e990d2d1ba4a..1efeda3b2f6f 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -440,7 +440,7 @@ static int cache_block_group(struct btrfs_block_group_cache *cache,
440 * allocate blocks for the tree root we can't do the fast caching since 440 * allocate blocks for the tree root we can't do the fast caching since
441 * we likely hold important locks. 441 * we likely hold important locks.
442 */ 442 */
443 if (!trans->transaction->in_commit && 443 if (trans && (!trans->transaction->in_commit) &&
444 (root && root != root->fs_info->tree_root)) { 444 (root && root != root->fs_info->tree_root)) {
445 spin_lock(&cache->lock); 445 spin_lock(&cache->lock);
446 if (cache->cached != BTRFS_CACHE_NO) { 446 if (cache->cached != BTRFS_CACHE_NO) {
@@ -8778,3 +8778,51 @@ int btrfs_error_discard_extent(struct btrfs_root *root, u64 bytenr,
8778{ 8778{
8779 return btrfs_discard_extent(root, bytenr, num_bytes, actual_bytes); 8779 return btrfs_discard_extent(root, bytenr, num_bytes, actual_bytes);
8780} 8780}
8781
8782int btrfs_trim_fs(struct btrfs_root *root, struct fstrim_range *range)
8783{
8784 struct btrfs_fs_info *fs_info = root->fs_info;
8785 struct btrfs_block_group_cache *cache = NULL;
8786 u64 group_trimmed;
8787 u64 start;
8788 u64 end;
8789 u64 trimmed = 0;
8790 int ret = 0;
8791
8792 cache = btrfs_lookup_block_group(fs_info, range->start);
8793
8794 while (cache) {
8795 if (cache->key.objectid >= (range->start + range->len)) {
8796 btrfs_put_block_group(cache);
8797 break;
8798 }
8799
8800 start = max(range->start, cache->key.objectid);
8801 end = min(range->start + range->len,
8802 cache->key.objectid + cache->key.offset);
8803
8804 if (end - start >= range->minlen) {
8805 if (!block_group_cache_done(cache)) {
8806 ret = cache_block_group(cache, NULL, root, 0);
8807 if (!ret)
8808 wait_block_group_cache_done(cache);
8809 }
8810 ret = btrfs_trim_block_group(cache,
8811 &group_trimmed,
8812 start,
8813 end,
8814 range->minlen);
8815
8816 trimmed += group_trimmed;
8817 if (ret) {
8818 btrfs_put_block_group(cache);
8819 break;
8820 }
8821 }
8822
8823 cache = next_block_group(fs_info->tree_root, cache);
8824 }
8825
8826 range->len = trimmed;
8827 return ret;
8828}
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index f03ef97c3b21..0037427d8a9d 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -2178,3 +2178,95 @@ void btrfs_init_free_cluster(struct btrfs_free_cluster *cluster)
2178 cluster->block_group = NULL; 2178 cluster->block_group = NULL;
2179} 2179}
2180 2180
2181int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group,
2182 u64 *trimmed, u64 start, u64 end, u64 minlen)
2183{
2184 struct btrfs_free_space *entry = NULL;
2185 struct btrfs_fs_info *fs_info = block_group->fs_info;
2186 u64 bytes = 0;
2187 u64 actually_trimmed;
2188 int ret = 0;
2189
2190 *trimmed = 0;
2191
2192 while (start < end) {
2193 spin_lock(&block_group->tree_lock);
2194
2195 if (block_group->free_space < minlen) {
2196 spin_unlock(&block_group->tree_lock);
2197 break;
2198 }
2199
2200 entry = tree_search_offset(block_group, start, 0, 1);
2201 if (!entry)
2202 entry = tree_search_offset(block_group,
2203 offset_to_bitmap(block_group,
2204 start),
2205 1, 1);
2206
2207 if (!entry || entry->offset >= end) {
2208 spin_unlock(&block_group->tree_lock);
2209 break;
2210 }
2211
2212 if (entry->bitmap) {
2213 ret = search_bitmap(block_group, entry, &start, &bytes);
2214 if (!ret) {
2215 if (start >= end) {
2216 spin_unlock(&block_group->tree_lock);
2217 break;
2218 }
2219 bytes = min(bytes, end - start);
2220 bitmap_clear_bits(block_group, entry,
2221 start, bytes);
2222 if (entry->bytes == 0)
2223 free_bitmap(block_group, entry);
2224 } else {
2225 start = entry->offset + BITS_PER_BITMAP *
2226 block_group->sectorsize;
2227 spin_unlock(&block_group->tree_lock);
2228 ret = 0;
2229 continue;
2230 }
2231 } else {
2232 start = entry->offset;
2233 bytes = min(entry->bytes, end - start);
2234 unlink_free_space(block_group, entry);
2235 kfree(entry);
2236 }
2237
2238 spin_unlock(&block_group->tree_lock);
2239
2240 if (bytes >= minlen) {
2241 int update_ret;
2242 update_ret = btrfs_update_reserved_bytes(block_group,
2243 bytes, 1, 1);
2244
2245 ret = btrfs_error_discard_extent(fs_info->extent_root,
2246 start,
2247 bytes,
2248 &actually_trimmed);
2249
2250 btrfs_add_free_space(block_group,
2251 start, bytes);
2252 if (!update_ret)
2253 btrfs_update_reserved_bytes(block_group,
2254 bytes, 0, 1);
2255
2256 if (ret)
2257 break;
2258 *trimmed += actually_trimmed;
2259 }
2260 start += bytes;
2261 bytes = 0;
2262
2263 if (fatal_signal_pending(current)) {
2264 ret = -ERESTARTSYS;
2265 break;
2266 }
2267
2268 cond_resched();
2269 }
2270
2271 return ret;
2272}
diff --git a/fs/btrfs/free-space-cache.h b/fs/btrfs/free-space-cache.h
index e49ca5c321b5..65c3b935289f 100644
--- a/fs/btrfs/free-space-cache.h
+++ b/fs/btrfs/free-space-cache.h
@@ -68,4 +68,6 @@ u64 btrfs_alloc_from_cluster(struct btrfs_block_group_cache *block_group,
68int btrfs_return_cluster_to_free_space( 68int btrfs_return_cluster_to_free_space(
69 struct btrfs_block_group_cache *block_group, 69 struct btrfs_block_group_cache *block_group,
70 struct btrfs_free_cluster *cluster); 70 struct btrfs_free_cluster *cluster);
71int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group,
72 u64 *trimmed, u64 start, u64 end, u64 minlen);
71#endif 73#endif
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 32c980ae0f1c..649f47d2afb4 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -40,6 +40,7 @@
40#include <linux/xattr.h> 40#include <linux/xattr.h>
41#include <linux/vmalloc.h> 41#include <linux/vmalloc.h>
42#include <linux/slab.h> 42#include <linux/slab.h>
43#include <linux/blkdev.h>
43#include "compat.h" 44#include "compat.h"
44#include "ctree.h" 45#include "ctree.h"
45#include "disk-io.h" 46#include "disk-io.h"
@@ -258,6 +259,49 @@ static int btrfs_ioctl_getversion(struct file *file, int __user *arg)
258 return put_user(inode->i_generation, arg); 259 return put_user(inode->i_generation, arg);
259} 260}
260 261
262static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg)
263{
264 struct btrfs_root *root = fdentry(file)->d_sb->s_fs_info;
265 struct btrfs_fs_info *fs_info = root->fs_info;
266 struct btrfs_device *device;
267 struct request_queue *q;
268 struct fstrim_range range;
269 u64 minlen = ULLONG_MAX;
270 u64 num_devices = 0;
271 int ret;
272
273 if (!capable(CAP_SYS_ADMIN))
274 return -EPERM;
275
276 mutex_lock(&fs_info->fs_devices->device_list_mutex);
277 list_for_each_entry(device, &fs_info->fs_devices->devices, dev_list) {
278 if (!device->bdev)
279 continue;
280 q = bdev_get_queue(device->bdev);
281 if (blk_queue_discard(q)) {
282 num_devices++;
283 minlen = min((u64)q->limits.discard_granularity,
284 minlen);
285 }
286 }
287 mutex_unlock(&fs_info->fs_devices->device_list_mutex);
288 if (!num_devices)
289 return -EOPNOTSUPP;
290
291 if (copy_from_user(&range, arg, sizeof(range)))
292 return -EFAULT;
293
294 range.minlen = max(range.minlen, minlen);
295 ret = btrfs_trim_fs(root, &range);
296 if (ret < 0)
297 return ret;
298
299 if (copy_to_user(arg, &range, sizeof(range)))
300 return -EFAULT;
301
302 return 0;
303}
304
261static noinline int create_subvol(struct btrfs_root *root, 305static noinline int create_subvol(struct btrfs_root *root,
262 struct dentry *dentry, 306 struct dentry *dentry,
263 char *name, int namelen, 307 char *name, int namelen,
@@ -2426,6 +2470,8 @@ long btrfs_ioctl(struct file *file, unsigned int
2426 return btrfs_ioctl_setflags(file, argp); 2470 return btrfs_ioctl_setflags(file, argp);
2427 case FS_IOC_GETVERSION: 2471 case FS_IOC_GETVERSION:
2428 return btrfs_ioctl_getversion(file, argp); 2472 return btrfs_ioctl_getversion(file, argp);
2473 case FITRIM:
2474 return btrfs_ioctl_fitrim(file, argp);
2429 case BTRFS_IOC_SNAP_CREATE: 2475 case BTRFS_IOC_SNAP_CREATE:
2430 return btrfs_ioctl_snap_create(file, argp, 0); 2476 return btrfs_ioctl_snap_create(file, argp, 0);
2431 case BTRFS_IOC_SNAP_CREATE_V2: 2477 case BTRFS_IOC_SNAP_CREATE_V2: