diff options
Diffstat (limited to 'fs/btrfs/sysfs.c')
-rw-r--r-- | fs/btrfs/sysfs.c | 623 |
1 files changed, 623 insertions, 0 deletions
diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index 5b326cd60a4a..865f4cf9a769 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c | |||
@@ -22,24 +22,647 @@ | |||
22 | #include <linux/completion.h> | 22 | #include <linux/completion.h> |
23 | #include <linux/buffer_head.h> | 23 | #include <linux/buffer_head.h> |
24 | #include <linux/kobject.h> | 24 | #include <linux/kobject.h> |
25 | #include <linux/bug.h> | ||
26 | #include <linux/genhd.h> | ||
25 | 27 | ||
26 | #include "ctree.h" | 28 | #include "ctree.h" |
27 | #include "disk-io.h" | 29 | #include "disk-io.h" |
28 | #include "transaction.h" | 30 | #include "transaction.h" |
31 | #include "sysfs.h" | ||
32 | #include "volumes.h" | ||
33 | |||
34 | static inline struct btrfs_fs_info *to_fs_info(struct kobject *kobj); | ||
35 | |||
36 | static u64 get_features(struct btrfs_fs_info *fs_info, | ||
37 | enum btrfs_feature_set set) | ||
38 | { | ||
39 | struct btrfs_super_block *disk_super = fs_info->super_copy; | ||
40 | if (set == FEAT_COMPAT) | ||
41 | return btrfs_super_compat_flags(disk_super); | ||
42 | else if (set == FEAT_COMPAT_RO) | ||
43 | return btrfs_super_compat_ro_flags(disk_super); | ||
44 | else | ||
45 | return btrfs_super_incompat_flags(disk_super); | ||
46 | } | ||
47 | |||
48 | static void set_features(struct btrfs_fs_info *fs_info, | ||
49 | enum btrfs_feature_set set, u64 features) | ||
50 | { | ||
51 | struct btrfs_super_block *disk_super = fs_info->super_copy; | ||
52 | if (set == FEAT_COMPAT) | ||
53 | btrfs_set_super_compat_flags(disk_super, features); | ||
54 | else if (set == FEAT_COMPAT_RO) | ||
55 | btrfs_set_super_compat_ro_flags(disk_super, features); | ||
56 | else | ||
57 | btrfs_set_super_incompat_flags(disk_super, features); | ||
58 | } | ||
59 | |||
60 | static int can_modify_feature(struct btrfs_feature_attr *fa) | ||
61 | { | ||
62 | int val = 0; | ||
63 | u64 set, clear; | ||
64 | switch (fa->feature_set) { | ||
65 | case FEAT_COMPAT: | ||
66 | set = BTRFS_FEATURE_COMPAT_SAFE_SET; | ||
67 | clear = BTRFS_FEATURE_COMPAT_SAFE_CLEAR; | ||
68 | break; | ||
69 | case FEAT_COMPAT_RO: | ||
70 | set = BTRFS_FEATURE_COMPAT_RO_SAFE_SET; | ||
71 | clear = BTRFS_FEATURE_COMPAT_RO_SAFE_CLEAR; | ||
72 | break; | ||
73 | case FEAT_INCOMPAT: | ||
74 | set = BTRFS_FEATURE_INCOMPAT_SAFE_SET; | ||
75 | clear = BTRFS_FEATURE_INCOMPAT_SAFE_CLEAR; | ||
76 | break; | ||
77 | default: | ||
78 | printk(KERN_WARNING "btrfs: sysfs: unknown feature set %d\n", | ||
79 | fa->feature_set); | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | if (set & fa->feature_bit) | ||
84 | val |= 1; | ||
85 | if (clear & fa->feature_bit) | ||
86 | val |= 2; | ||
87 | |||
88 | return val; | ||
89 | } | ||
90 | |||
91 | static ssize_t btrfs_feature_attr_show(struct kobject *kobj, | ||
92 | struct kobj_attribute *a, char *buf) | ||
93 | { | ||
94 | int val = 0; | ||
95 | struct btrfs_fs_info *fs_info = to_fs_info(kobj); | ||
96 | struct btrfs_feature_attr *fa = to_btrfs_feature_attr(a); | ||
97 | if (fs_info) { | ||
98 | u64 features = get_features(fs_info, fa->feature_set); | ||
99 | if (features & fa->feature_bit) | ||
100 | val = 1; | ||
101 | } else | ||
102 | val = can_modify_feature(fa); | ||
103 | |||
104 | return snprintf(buf, PAGE_SIZE, "%d\n", val); | ||
105 | } | ||
106 | |||
107 | static ssize_t btrfs_feature_attr_store(struct kobject *kobj, | ||
108 | struct kobj_attribute *a, | ||
109 | const char *buf, size_t count) | ||
110 | { | ||
111 | struct btrfs_fs_info *fs_info; | ||
112 | struct btrfs_feature_attr *fa = to_btrfs_feature_attr(a); | ||
113 | struct btrfs_trans_handle *trans; | ||
114 | u64 features, set, clear; | ||
115 | unsigned long val; | ||
116 | int ret; | ||
117 | |||
118 | fs_info = to_fs_info(kobj); | ||
119 | if (!fs_info) | ||
120 | return -EPERM; | ||
121 | |||
122 | ret = kstrtoul(skip_spaces(buf), 0, &val); | ||
123 | if (ret) | ||
124 | return ret; | ||
125 | |||
126 | if (fa->feature_set == FEAT_COMPAT) { | ||
127 | set = BTRFS_FEATURE_COMPAT_SAFE_SET; | ||
128 | clear = BTRFS_FEATURE_COMPAT_SAFE_CLEAR; | ||
129 | } else if (fa->feature_set == FEAT_COMPAT_RO) { | ||
130 | set = BTRFS_FEATURE_COMPAT_RO_SAFE_SET; | ||
131 | clear = BTRFS_FEATURE_COMPAT_RO_SAFE_CLEAR; | ||
132 | } else { | ||
133 | set = BTRFS_FEATURE_INCOMPAT_SAFE_SET; | ||
134 | clear = BTRFS_FEATURE_INCOMPAT_SAFE_CLEAR; | ||
135 | } | ||
136 | |||
137 | features = get_features(fs_info, fa->feature_set); | ||
138 | |||
139 | /* Nothing to do */ | ||
140 | if ((val && (features & fa->feature_bit)) || | ||
141 | (!val && !(features & fa->feature_bit))) | ||
142 | return count; | ||
143 | |||
144 | if ((val && !(set & fa->feature_bit)) || | ||
145 | (!val && !(clear & fa->feature_bit))) { | ||
146 | btrfs_info(fs_info, | ||
147 | "%sabling feature %s on mounted fs is not supported.", | ||
148 | val ? "En" : "Dis", fa->kobj_attr.attr.name); | ||
149 | return -EPERM; | ||
150 | } | ||
151 | |||
152 | btrfs_info(fs_info, "%s %s feature flag", | ||
153 | val ? "Setting" : "Clearing", fa->kobj_attr.attr.name); | ||
154 | |||
155 | trans = btrfs_start_transaction(fs_info->fs_root, 0); | ||
156 | if (IS_ERR(trans)) | ||
157 | return PTR_ERR(trans); | ||
158 | |||
159 | spin_lock(&fs_info->super_lock); | ||
160 | features = get_features(fs_info, fa->feature_set); | ||
161 | if (val) | ||
162 | features |= fa->feature_bit; | ||
163 | else | ||
164 | features &= ~fa->feature_bit; | ||
165 | set_features(fs_info, fa->feature_set, features); | ||
166 | spin_unlock(&fs_info->super_lock); | ||
167 | |||
168 | ret = btrfs_commit_transaction(trans, fs_info->fs_root); | ||
169 | if (ret) | ||
170 | return ret; | ||
171 | |||
172 | return count; | ||
173 | } | ||
174 | |||
175 | static umode_t btrfs_feature_visible(struct kobject *kobj, | ||
176 | struct attribute *attr, int unused) | ||
177 | { | ||
178 | struct btrfs_fs_info *fs_info = to_fs_info(kobj); | ||
179 | umode_t mode = attr->mode; | ||
180 | |||
181 | if (fs_info) { | ||
182 | struct btrfs_feature_attr *fa; | ||
183 | u64 features; | ||
184 | |||
185 | fa = attr_to_btrfs_feature_attr(attr); | ||
186 | features = get_features(fs_info, fa->feature_set); | ||
187 | |||
188 | if (can_modify_feature(fa)) | ||
189 | mode |= S_IWUSR; | ||
190 | else if (!(features & fa->feature_bit)) | ||
191 | mode = 0; | ||
192 | } | ||
193 | |||
194 | return mode; | ||
195 | } | ||
196 | |||
197 | BTRFS_FEAT_ATTR_INCOMPAT(mixed_backref, MIXED_BACKREF); | ||
198 | BTRFS_FEAT_ATTR_INCOMPAT(default_subvol, DEFAULT_SUBVOL); | ||
199 | BTRFS_FEAT_ATTR_INCOMPAT(mixed_groups, MIXED_GROUPS); | ||
200 | BTRFS_FEAT_ATTR_INCOMPAT(compress_lzo, COMPRESS_LZO); | ||
201 | BTRFS_FEAT_ATTR_INCOMPAT(big_metadata, BIG_METADATA); | ||
202 | BTRFS_FEAT_ATTR_INCOMPAT(extended_iref, EXTENDED_IREF); | ||
203 | BTRFS_FEAT_ATTR_INCOMPAT(raid56, RAID56); | ||
204 | BTRFS_FEAT_ATTR_INCOMPAT(skinny_metadata, SKINNY_METADATA); | ||
205 | BTRFS_FEAT_ATTR_INCOMPAT(no_holes, NO_HOLES); | ||
206 | |||
207 | static struct attribute *btrfs_supported_feature_attrs[] = { | ||
208 | BTRFS_FEAT_ATTR_PTR(mixed_backref), | ||
209 | BTRFS_FEAT_ATTR_PTR(default_subvol), | ||
210 | BTRFS_FEAT_ATTR_PTR(mixed_groups), | ||
211 | BTRFS_FEAT_ATTR_PTR(compress_lzo), | ||
212 | BTRFS_FEAT_ATTR_PTR(big_metadata), | ||
213 | BTRFS_FEAT_ATTR_PTR(extended_iref), | ||
214 | BTRFS_FEAT_ATTR_PTR(raid56), | ||
215 | BTRFS_FEAT_ATTR_PTR(skinny_metadata), | ||
216 | BTRFS_FEAT_ATTR_PTR(no_holes), | ||
217 | NULL | ||
218 | }; | ||
219 | |||
220 | static const struct attribute_group btrfs_feature_attr_group = { | ||
221 | .name = "features", | ||
222 | .is_visible = btrfs_feature_visible, | ||
223 | .attrs = btrfs_supported_feature_attrs, | ||
224 | }; | ||
225 | |||
226 | static ssize_t btrfs_show_u64(u64 *value_ptr, spinlock_t *lock, char *buf) | ||
227 | { | ||
228 | u64 val; | ||
229 | if (lock) | ||
230 | spin_lock(lock); | ||
231 | val = *value_ptr; | ||
232 | if (lock) | ||
233 | spin_unlock(lock); | ||
234 | return snprintf(buf, PAGE_SIZE, "%llu\n", val); | ||
235 | } | ||
236 | |||
237 | static ssize_t global_rsv_size_show(struct kobject *kobj, | ||
238 | struct kobj_attribute *ka, char *buf) | ||
239 | { | ||
240 | struct btrfs_fs_info *fs_info = to_fs_info(kobj->parent); | ||
241 | struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv; | ||
242 | return btrfs_show_u64(&block_rsv->size, &block_rsv->lock, buf); | ||
243 | } | ||
244 | BTRFS_ATTR(global_rsv_size, 0444, global_rsv_size_show); | ||
245 | |||
246 | static ssize_t global_rsv_reserved_show(struct kobject *kobj, | ||
247 | struct kobj_attribute *a, char *buf) | ||
248 | { | ||
249 | struct btrfs_fs_info *fs_info = to_fs_info(kobj->parent); | ||
250 | struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv; | ||
251 | return btrfs_show_u64(&block_rsv->reserved, &block_rsv->lock, buf); | ||
252 | } | ||
253 | BTRFS_ATTR(global_rsv_reserved, 0444, global_rsv_reserved_show); | ||
254 | |||
255 | #define to_space_info(_kobj) container_of(_kobj, struct btrfs_space_info, kobj) | ||
256 | |||
257 | static ssize_t raid_bytes_show(struct kobject *kobj, | ||
258 | struct kobj_attribute *attr, char *buf); | ||
259 | BTRFS_RAID_ATTR(total_bytes, raid_bytes_show); | ||
260 | BTRFS_RAID_ATTR(used_bytes, raid_bytes_show); | ||
261 | |||
262 | static ssize_t raid_bytes_show(struct kobject *kobj, | ||
263 | struct kobj_attribute *attr, char *buf) | ||
264 | |||
265 | { | ||
266 | struct btrfs_space_info *sinfo = to_space_info(kobj->parent); | ||
267 | struct btrfs_block_group_cache *block_group; | ||
268 | int index = kobj - sinfo->block_group_kobjs; | ||
269 | u64 val = 0; | ||
270 | |||
271 | down_read(&sinfo->groups_sem); | ||
272 | list_for_each_entry(block_group, &sinfo->block_groups[index], list) { | ||
273 | if (&attr->attr == BTRFS_RAID_ATTR_PTR(total_bytes)) | ||
274 | val += block_group->key.offset; | ||
275 | else | ||
276 | val += btrfs_block_group_used(&block_group->item); | ||
277 | } | ||
278 | up_read(&sinfo->groups_sem); | ||
279 | return snprintf(buf, PAGE_SIZE, "%llu\n", val); | ||
280 | } | ||
281 | |||
282 | static struct attribute *raid_attributes[] = { | ||
283 | BTRFS_RAID_ATTR_PTR(total_bytes), | ||
284 | BTRFS_RAID_ATTR_PTR(used_bytes), | ||
285 | NULL | ||
286 | }; | ||
287 | |||
288 | static void release_raid_kobj(struct kobject *kobj) | ||
289 | { | ||
290 | kobject_put(kobj->parent); | ||
291 | } | ||
292 | |||
293 | struct kobj_type btrfs_raid_ktype = { | ||
294 | .sysfs_ops = &kobj_sysfs_ops, | ||
295 | .release = release_raid_kobj, | ||
296 | .default_attrs = raid_attributes, | ||
297 | }; | ||
298 | |||
299 | #define SPACE_INFO_ATTR(field) \ | ||
300 | static ssize_t btrfs_space_info_show_##field(struct kobject *kobj, \ | ||
301 | struct kobj_attribute *a, \ | ||
302 | char *buf) \ | ||
303 | { \ | ||
304 | struct btrfs_space_info *sinfo = to_space_info(kobj); \ | ||
305 | return btrfs_show_u64(&sinfo->field, &sinfo->lock, buf); \ | ||
306 | } \ | ||
307 | BTRFS_ATTR(field, 0444, btrfs_space_info_show_##field) | ||
308 | |||
309 | static ssize_t btrfs_space_info_show_total_bytes_pinned(struct kobject *kobj, | ||
310 | struct kobj_attribute *a, | ||
311 | char *buf) | ||
312 | { | ||
313 | struct btrfs_space_info *sinfo = to_space_info(kobj); | ||
314 | s64 val = percpu_counter_sum(&sinfo->total_bytes_pinned); | ||
315 | return snprintf(buf, PAGE_SIZE, "%lld\n", val); | ||
316 | } | ||
317 | |||
318 | SPACE_INFO_ATTR(flags); | ||
319 | SPACE_INFO_ATTR(total_bytes); | ||
320 | SPACE_INFO_ATTR(bytes_used); | ||
321 | SPACE_INFO_ATTR(bytes_pinned); | ||
322 | SPACE_INFO_ATTR(bytes_reserved); | ||
323 | SPACE_INFO_ATTR(bytes_may_use); | ||
324 | SPACE_INFO_ATTR(disk_used); | ||
325 | SPACE_INFO_ATTR(disk_total); | ||
326 | BTRFS_ATTR(total_bytes_pinned, 0444, btrfs_space_info_show_total_bytes_pinned); | ||
327 | |||
328 | static struct attribute *space_info_attrs[] = { | ||
329 | BTRFS_ATTR_PTR(flags), | ||
330 | BTRFS_ATTR_PTR(total_bytes), | ||
331 | BTRFS_ATTR_PTR(bytes_used), | ||
332 | BTRFS_ATTR_PTR(bytes_pinned), | ||
333 | BTRFS_ATTR_PTR(bytes_reserved), | ||
334 | BTRFS_ATTR_PTR(bytes_may_use), | ||
335 | BTRFS_ATTR_PTR(disk_used), | ||
336 | BTRFS_ATTR_PTR(disk_total), | ||
337 | BTRFS_ATTR_PTR(total_bytes_pinned), | ||
338 | NULL, | ||
339 | }; | ||
340 | |||
341 | static void space_info_release(struct kobject *kobj) | ||
342 | { | ||
343 | struct btrfs_space_info *sinfo = to_space_info(kobj); | ||
344 | percpu_counter_destroy(&sinfo->total_bytes_pinned); | ||
345 | kfree(sinfo); | ||
346 | } | ||
347 | |||
348 | struct kobj_type space_info_ktype = { | ||
349 | .sysfs_ops = &kobj_sysfs_ops, | ||
350 | .release = space_info_release, | ||
351 | .default_attrs = space_info_attrs, | ||
352 | }; | ||
353 | |||
354 | static const struct attribute *allocation_attrs[] = { | ||
355 | BTRFS_ATTR_PTR(global_rsv_reserved), | ||
356 | BTRFS_ATTR_PTR(global_rsv_size), | ||
357 | NULL, | ||
358 | }; | ||
359 | |||
360 | static ssize_t btrfs_label_show(struct kobject *kobj, | ||
361 | struct kobj_attribute *a, char *buf) | ||
362 | { | ||
363 | struct btrfs_fs_info *fs_info = to_fs_info(kobj); | ||
364 | return snprintf(buf, PAGE_SIZE, "%s\n", fs_info->super_copy->label); | ||
365 | } | ||
366 | |||
367 | static ssize_t btrfs_label_store(struct kobject *kobj, | ||
368 | struct kobj_attribute *a, | ||
369 | const char *buf, size_t len) | ||
370 | { | ||
371 | struct btrfs_fs_info *fs_info = to_fs_info(kobj); | ||
372 | struct btrfs_trans_handle *trans; | ||
373 | struct btrfs_root *root = fs_info->fs_root; | ||
374 | int ret; | ||
375 | |||
376 | if (len >= BTRFS_LABEL_SIZE) { | ||
377 | pr_err("BTRFS: unable to set label with more than %d bytes\n", | ||
378 | BTRFS_LABEL_SIZE - 1); | ||
379 | return -EINVAL; | ||
380 | } | ||
381 | |||
382 | trans = btrfs_start_transaction(root, 0); | ||
383 | if (IS_ERR(trans)) | ||
384 | return PTR_ERR(trans); | ||
385 | |||
386 | spin_lock(&root->fs_info->super_lock); | ||
387 | strcpy(fs_info->super_copy->label, buf); | ||
388 | spin_unlock(&root->fs_info->super_lock); | ||
389 | ret = btrfs_commit_transaction(trans, root); | ||
390 | |||
391 | if (!ret) | ||
392 | return len; | ||
393 | |||
394 | return ret; | ||
395 | } | ||
396 | BTRFS_ATTR_RW(label, 0644, btrfs_label_show, btrfs_label_store); | ||
397 | |||
398 | static struct attribute *btrfs_attrs[] = { | ||
399 | BTRFS_ATTR_PTR(label), | ||
400 | NULL, | ||
401 | }; | ||
402 | |||
403 | static void btrfs_release_super_kobj(struct kobject *kobj) | ||
404 | { | ||
405 | struct btrfs_fs_info *fs_info = to_fs_info(kobj); | ||
406 | complete(&fs_info->kobj_unregister); | ||
407 | } | ||
408 | |||
409 | static struct kobj_type btrfs_ktype = { | ||
410 | .sysfs_ops = &kobj_sysfs_ops, | ||
411 | .release = btrfs_release_super_kobj, | ||
412 | .default_attrs = btrfs_attrs, | ||
413 | }; | ||
414 | |||
415 | static inline struct btrfs_fs_info *to_fs_info(struct kobject *kobj) | ||
416 | { | ||
417 | if (kobj->ktype != &btrfs_ktype) | ||
418 | return NULL; | ||
419 | return container_of(kobj, struct btrfs_fs_info, super_kobj); | ||
420 | } | ||
421 | |||
422 | #define NUM_FEATURE_BITS 64 | ||
423 | static char btrfs_unknown_feature_names[3][NUM_FEATURE_BITS][13]; | ||
424 | static struct btrfs_feature_attr btrfs_feature_attrs[3][NUM_FEATURE_BITS]; | ||
425 | |||
426 | static u64 supported_feature_masks[3] = { | ||
427 | [FEAT_COMPAT] = BTRFS_FEATURE_COMPAT_SUPP, | ||
428 | [FEAT_COMPAT_RO] = BTRFS_FEATURE_COMPAT_RO_SUPP, | ||
429 | [FEAT_INCOMPAT] = BTRFS_FEATURE_INCOMPAT_SUPP, | ||
430 | }; | ||
431 | |||
432 | static int addrm_unknown_feature_attrs(struct btrfs_fs_info *fs_info, bool add) | ||
433 | { | ||
434 | int set; | ||
435 | |||
436 | for (set = 0; set < FEAT_MAX; set++) { | ||
437 | int i; | ||
438 | struct attribute *attrs[2]; | ||
439 | struct attribute_group agroup = { | ||
440 | .name = "features", | ||
441 | .attrs = attrs, | ||
442 | }; | ||
443 | u64 features = get_features(fs_info, set); | ||
444 | features &= ~supported_feature_masks[set]; | ||
445 | |||
446 | if (!features) | ||
447 | continue; | ||
448 | |||
449 | attrs[1] = NULL; | ||
450 | for (i = 0; i < NUM_FEATURE_BITS; i++) { | ||
451 | struct btrfs_feature_attr *fa; | ||
452 | |||
453 | if (!(features & (1ULL << i))) | ||
454 | continue; | ||
455 | |||
456 | fa = &btrfs_feature_attrs[set][i]; | ||
457 | attrs[0] = &fa->kobj_attr.attr; | ||
458 | if (add) { | ||
459 | int ret; | ||
460 | ret = sysfs_merge_group(&fs_info->super_kobj, | ||
461 | &agroup); | ||
462 | if (ret) | ||
463 | return ret; | ||
464 | } else | ||
465 | sysfs_unmerge_group(&fs_info->super_kobj, | ||
466 | &agroup); | ||
467 | } | ||
468 | |||
469 | } | ||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | static void __btrfs_sysfs_remove_one(struct btrfs_fs_info *fs_info) | ||
474 | { | ||
475 | kobject_del(&fs_info->super_kobj); | ||
476 | kobject_put(&fs_info->super_kobj); | ||
477 | wait_for_completion(&fs_info->kobj_unregister); | ||
478 | } | ||
479 | |||
480 | void btrfs_sysfs_remove_one(struct btrfs_fs_info *fs_info) | ||
481 | { | ||
482 | if (fs_info->space_info_kobj) { | ||
483 | sysfs_remove_files(fs_info->space_info_kobj, allocation_attrs); | ||
484 | kobject_del(fs_info->space_info_kobj); | ||
485 | kobject_put(fs_info->space_info_kobj); | ||
486 | } | ||
487 | kobject_del(fs_info->device_dir_kobj); | ||
488 | kobject_put(fs_info->device_dir_kobj); | ||
489 | addrm_unknown_feature_attrs(fs_info, false); | ||
490 | sysfs_remove_group(&fs_info->super_kobj, &btrfs_feature_attr_group); | ||
491 | __btrfs_sysfs_remove_one(fs_info); | ||
492 | } | ||
493 | |||
494 | const char * const btrfs_feature_set_names[3] = { | ||
495 | [FEAT_COMPAT] = "compat", | ||
496 | [FEAT_COMPAT_RO] = "compat_ro", | ||
497 | [FEAT_INCOMPAT] = "incompat", | ||
498 | }; | ||
499 | |||
500 | char *btrfs_printable_features(enum btrfs_feature_set set, u64 flags) | ||
501 | { | ||
502 | size_t bufsize = 4096; /* safe max, 64 names * 64 bytes */ | ||
503 | int len = 0; | ||
504 | int i; | ||
505 | char *str; | ||
506 | |||
507 | str = kmalloc(bufsize, GFP_KERNEL); | ||
508 | if (!str) | ||
509 | return str; | ||
510 | |||
511 | for (i = 0; i < ARRAY_SIZE(btrfs_feature_attrs[set]); i++) { | ||
512 | const char *name; | ||
513 | |||
514 | if (!(flags & (1ULL << i))) | ||
515 | continue; | ||
516 | |||
517 | name = btrfs_feature_attrs[set][i].kobj_attr.attr.name; | ||
518 | len += snprintf(str + len, bufsize - len, "%s%s", | ||
519 | len ? "," : "", name); | ||
520 | } | ||
521 | |||
522 | return str; | ||
523 | } | ||
524 | |||
525 | static void init_feature_attrs(void) | ||
526 | { | ||
527 | struct btrfs_feature_attr *fa; | ||
528 | int set, i; | ||
529 | |||
530 | BUILD_BUG_ON(ARRAY_SIZE(btrfs_unknown_feature_names) != | ||
531 | ARRAY_SIZE(btrfs_feature_attrs)); | ||
532 | BUILD_BUG_ON(ARRAY_SIZE(btrfs_unknown_feature_names[0]) != | ||
533 | ARRAY_SIZE(btrfs_feature_attrs[0])); | ||
534 | |||
535 | memset(btrfs_feature_attrs, 0, sizeof(btrfs_feature_attrs)); | ||
536 | memset(btrfs_unknown_feature_names, 0, | ||
537 | sizeof(btrfs_unknown_feature_names)); | ||
538 | |||
539 | for (i = 0; btrfs_supported_feature_attrs[i]; i++) { | ||
540 | struct btrfs_feature_attr *sfa; | ||
541 | struct attribute *a = btrfs_supported_feature_attrs[i]; | ||
542 | int bit; | ||
543 | sfa = attr_to_btrfs_feature_attr(a); | ||
544 | bit = ilog2(sfa->feature_bit); | ||
545 | fa = &btrfs_feature_attrs[sfa->feature_set][bit]; | ||
546 | |||
547 | fa->kobj_attr.attr.name = sfa->kobj_attr.attr.name; | ||
548 | } | ||
549 | |||
550 | for (set = 0; set < FEAT_MAX; set++) { | ||
551 | for (i = 0; i < ARRAY_SIZE(btrfs_feature_attrs[set]); i++) { | ||
552 | char *name = btrfs_unknown_feature_names[set][i]; | ||
553 | fa = &btrfs_feature_attrs[set][i]; | ||
554 | |||
555 | if (fa->kobj_attr.attr.name) | ||
556 | continue; | ||
557 | |||
558 | snprintf(name, 13, "%s:%u", | ||
559 | btrfs_feature_set_names[set], i); | ||
560 | |||
561 | fa->kobj_attr.attr.name = name; | ||
562 | fa->kobj_attr.attr.mode = S_IRUGO; | ||
563 | fa->feature_set = set; | ||
564 | fa->feature_bit = 1ULL << i; | ||
565 | } | ||
566 | } | ||
567 | } | ||
568 | |||
569 | static int add_device_membership(struct btrfs_fs_info *fs_info) | ||
570 | { | ||
571 | int error = 0; | ||
572 | struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; | ||
573 | struct btrfs_device *dev; | ||
574 | |||
575 | fs_info->device_dir_kobj = kobject_create_and_add("devices", | ||
576 | &fs_info->super_kobj); | ||
577 | if (!fs_info->device_dir_kobj) | ||
578 | return -ENOMEM; | ||
579 | |||
580 | list_for_each_entry(dev, &fs_devices->devices, dev_list) { | ||
581 | struct hd_struct *disk; | ||
582 | struct kobject *disk_kobj; | ||
583 | |||
584 | if (!dev->bdev) | ||
585 | continue; | ||
586 | |||
587 | disk = dev->bdev->bd_part; | ||
588 | disk_kobj = &part_to_dev(disk)->kobj; | ||
589 | |||
590 | error = sysfs_create_link(fs_info->device_dir_kobj, | ||
591 | disk_kobj, disk_kobj->name); | ||
592 | if (error) | ||
593 | break; | ||
594 | } | ||
595 | |||
596 | return error; | ||
597 | } | ||
29 | 598 | ||
30 | /* /sys/fs/btrfs/ entry */ | 599 | /* /sys/fs/btrfs/ entry */ |
31 | static struct kset *btrfs_kset; | 600 | static struct kset *btrfs_kset; |
32 | 601 | ||
602 | int btrfs_sysfs_add_one(struct btrfs_fs_info *fs_info) | ||
603 | { | ||
604 | int error; | ||
605 | |||
606 | init_completion(&fs_info->kobj_unregister); | ||
607 | fs_info->super_kobj.kset = btrfs_kset; | ||
608 | error = kobject_init_and_add(&fs_info->super_kobj, &btrfs_ktype, NULL, | ||
609 | "%pU", fs_info->fsid); | ||
610 | if (error) | ||
611 | return error; | ||
612 | |||
613 | error = sysfs_create_group(&fs_info->super_kobj, | ||
614 | &btrfs_feature_attr_group); | ||
615 | if (error) { | ||
616 | __btrfs_sysfs_remove_one(fs_info); | ||
617 | return error; | ||
618 | } | ||
619 | |||
620 | error = addrm_unknown_feature_attrs(fs_info, true); | ||
621 | if (error) | ||
622 | goto failure; | ||
623 | |||
624 | error = add_device_membership(fs_info); | ||
625 | if (error) | ||
626 | goto failure; | ||
627 | |||
628 | fs_info->space_info_kobj = kobject_create_and_add("allocation", | ||
629 | &fs_info->super_kobj); | ||
630 | if (!fs_info->space_info_kobj) { | ||
631 | error = -ENOMEM; | ||
632 | goto failure; | ||
633 | } | ||
634 | |||
635 | error = sysfs_create_files(fs_info->space_info_kobj, allocation_attrs); | ||
636 | if (error) | ||
637 | goto failure; | ||
638 | |||
639 | return 0; | ||
640 | failure: | ||
641 | btrfs_sysfs_remove_one(fs_info); | ||
642 | return error; | ||
643 | } | ||
644 | |||
33 | int btrfs_init_sysfs(void) | 645 | int btrfs_init_sysfs(void) |
34 | { | 646 | { |
647 | int ret; | ||
35 | btrfs_kset = kset_create_and_add("btrfs", NULL, fs_kobj); | 648 | btrfs_kset = kset_create_and_add("btrfs", NULL, fs_kobj); |
36 | if (!btrfs_kset) | 649 | if (!btrfs_kset) |
37 | return -ENOMEM; | 650 | return -ENOMEM; |
651 | |||
652 | init_feature_attrs(); | ||
653 | |||
654 | ret = sysfs_create_group(&btrfs_kset->kobj, &btrfs_feature_attr_group); | ||
655 | if (ret) { | ||
656 | kset_unregister(btrfs_kset); | ||
657 | return ret; | ||
658 | } | ||
659 | |||
38 | return 0; | 660 | return 0; |
39 | } | 661 | } |
40 | 662 | ||
41 | void btrfs_exit_sysfs(void) | 663 | void btrfs_exit_sysfs(void) |
42 | { | 664 | { |
665 | sysfs_remove_group(&btrfs_kset->kobj, &btrfs_feature_attr_group); | ||
43 | kset_unregister(btrfs_kset); | 666 | kset_unregister(btrfs_kset); |
44 | } | 667 | } |
45 | 668 | ||