xref: /openbmc/linux/fs/btrfs/space-info.c (revision 280c2908)
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 }
175