]>
Commit | Line | Data |
---|---|---|
280c2908 JB |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | ||
3 | #include "ctree.h" | |
4 | #include "space-info.h" | |
5 | #include "sysfs.h" | |
6 | #include "volumes.h" | |
7 | ||
8 | u64 btrfs_space_info_used(struct btrfs_space_info *s_info, | |
9 | bool may_use_included) | |
10 | { | |
11 | ASSERT(s_info); | |
12 | return s_info->bytes_used + s_info->bytes_reserved + | |
13 | s_info->bytes_pinned + s_info->bytes_readonly + | |
14 | (may_use_included ? s_info->bytes_may_use : 0); | |
15 | } | |
16 | ||
17 | /* | |
18 | * after adding space to the filesystem, we need to clear the full flags | |
19 | * on all the space infos. | |
20 | */ | |
21 | void btrfs_clear_space_info_full(struct btrfs_fs_info *info) | |
22 | { | |
23 | struct list_head *head = &info->space_info; | |
24 | struct btrfs_space_info *found; | |
25 | ||
26 | rcu_read_lock(); | |
27 | list_for_each_entry_rcu(found, head, list) | |
28 | found->full = 0; | |
29 | rcu_read_unlock(); | |
30 | } | |
31 | ||
32 | static const char *alloc_name(u64 flags) | |
33 | { | |
34 | switch (flags) { | |
35 | case BTRFS_BLOCK_GROUP_METADATA|BTRFS_BLOCK_GROUP_DATA: | |
36 | return "mixed"; | |
37 | case BTRFS_BLOCK_GROUP_METADATA: | |
38 | return "metadata"; | |
39 | case BTRFS_BLOCK_GROUP_DATA: | |
40 | return "data"; | |
41 | case BTRFS_BLOCK_GROUP_SYSTEM: | |
42 | return "system"; | |
43 | default: | |
44 | WARN_ON(1); | |
45 | return "invalid-combination"; | |
46 | }; | |
47 | } | |
48 | ||
49 | static int create_space_info(struct btrfs_fs_info *info, u64 flags) | |
50 | { | |
51 | ||
52 | struct btrfs_space_info *space_info; | |
53 | int i; | |
54 | int ret; | |
55 | ||
56 | space_info = kzalloc(sizeof(*space_info), GFP_NOFS); | |
57 | if (!space_info) | |
58 | return -ENOMEM; | |
59 | ||
60 | ret = percpu_counter_init(&space_info->total_bytes_pinned, 0, | |
61 | GFP_KERNEL); | |
62 | if (ret) { | |
63 | kfree(space_info); | |
64 | return ret; | |
65 | } | |
66 | ||
67 | for (i = 0; i < BTRFS_NR_RAID_TYPES; i++) | |
68 | INIT_LIST_HEAD(&space_info->block_groups[i]); | |
69 | init_rwsem(&space_info->groups_sem); | |
70 | spin_lock_init(&space_info->lock); | |
71 | space_info->flags = flags & BTRFS_BLOCK_GROUP_TYPE_MASK; | |
72 | space_info->force_alloc = CHUNK_ALLOC_NO_FORCE; | |
73 | init_waitqueue_head(&space_info->wait); | |
74 | INIT_LIST_HEAD(&space_info->ro_bgs); | |
75 | INIT_LIST_HEAD(&space_info->tickets); | |
76 | INIT_LIST_HEAD(&space_info->priority_tickets); | |
77 | ||
78 | ret = kobject_init_and_add(&space_info->kobj, &space_info_ktype, | |
79 | info->space_info_kobj, "%s", | |
80 | alloc_name(space_info->flags)); | |
81 | if (ret) { | |
82 | kobject_put(&space_info->kobj); | |
83 | return ret; | |
84 | } | |
85 | ||
86 | list_add_rcu(&space_info->list, &info->space_info); | |
87 | if (flags & BTRFS_BLOCK_GROUP_DATA) | |
88 | info->data_sinfo = space_info; | |
89 | ||
90 | return ret; | |
91 | } | |
92 | ||
93 | int btrfs_init_space_info(struct btrfs_fs_info *fs_info) | |
94 | { | |
95 | struct btrfs_super_block *disk_super; | |
96 | u64 features; | |
97 | u64 flags; | |
98 | int mixed = 0; | |
99 | int ret; | |
100 | ||
101 | disk_super = fs_info->super_copy; | |
102 | if (!btrfs_super_root(disk_super)) | |
103 | return -EINVAL; | |
104 | ||
105 | features = btrfs_super_incompat_flags(disk_super); | |
106 | if (features & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS) | |
107 | mixed = 1; | |
108 | ||
109 | flags = BTRFS_BLOCK_GROUP_SYSTEM; | |
110 | ret = create_space_info(fs_info, flags); | |
111 | if (ret) | |
112 | goto out; | |
113 | ||
114 | if (mixed) { | |
115 | flags = BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA; | |
116 | ret = create_space_info(fs_info, flags); | |
117 | } else { | |
118 | flags = BTRFS_BLOCK_GROUP_METADATA; | |
119 | ret = create_space_info(fs_info, flags); | |
120 | if (ret) | |
121 | goto out; | |
122 | ||
123 | flags = BTRFS_BLOCK_GROUP_DATA; | |
124 | ret = create_space_info(fs_info, flags); | |
125 | } | |
126 | out: | |
127 | return ret; | |
128 | } | |
129 | ||
130 | void btrfs_update_space_info(struct btrfs_fs_info *info, u64 flags, | |
131 | u64 total_bytes, u64 bytes_used, | |
132 | u64 bytes_readonly, | |
133 | struct btrfs_space_info **space_info) | |
134 | { | |
135 | struct btrfs_space_info *found; | |
136 | int factor; | |
137 | ||
138 | factor = btrfs_bg_type_to_factor(flags); | |
139 | ||
140 | found = btrfs_find_space_info(info, flags); | |
141 | ASSERT(found); | |
142 | spin_lock(&found->lock); | |
143 | found->total_bytes += total_bytes; | |
144 | found->disk_total += total_bytes * factor; | |
145 | found->bytes_used += bytes_used; | |
146 | found->disk_used += bytes_used * factor; | |
147 | found->bytes_readonly += bytes_readonly; | |
148 | if (total_bytes > 0) | |
149 | found->full = 0; | |
150 | btrfs_space_info_add_new_bytes(info, found, | |
151 | total_bytes - bytes_used - | |
152 | bytes_readonly); | |
153 | spin_unlock(&found->lock); | |
154 | *space_info = found; | |
155 | } | |
156 | ||
157 | struct btrfs_space_info *btrfs_find_space_info(struct btrfs_fs_info *info, | |
158 | u64 flags) | |
159 | { | |
160 | struct list_head *head = &info->space_info; | |
161 | struct btrfs_space_info *found; | |
162 | ||
163 | flags &= BTRFS_BLOCK_GROUP_TYPE_MASK; | |
164 | ||
165 | rcu_read_lock(); | |
166 | list_for_each_entry_rcu(found, head, list) { | |
167 | if (found->flags & flags) { | |
168 | rcu_read_unlock(); | |
169 | return found; | |
170 | } | |
171 | } | |
172 | rcu_read_unlock(); | |
173 | return NULL; | |
174 | } |