1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2008 Red Hat, Inc. All rights reserved. |
4 | * |
5 | * This file is released under the GPL. |
6 | */ |
7 | |
8 | #include <linux/sysfs.h> |
9 | #include <linux/dm-ioctl.h> |
10 | #include "dm-core.h" |
11 | #include "dm-rq.h" |
12 | |
13 | struct dm_sysfs_attr { |
14 | struct attribute attr; |
15 | ssize_t (*show)(struct mapped_device *md, char *p); |
16 | ssize_t (*store)(struct mapped_device *md, const char *p, size_t count); |
17 | }; |
18 | |
19 | #define DM_ATTR_RO(_name) \ |
20 | struct dm_sysfs_attr dm_attr_##_name = \ |
21 | __ATTR(_name, 0444, dm_attr_##_name##_show, NULL) |
22 | |
23 | static ssize_t dm_attr_show(struct kobject *kobj, struct attribute *attr, |
24 | char *page) |
25 | { |
26 | struct dm_sysfs_attr *dm_attr; |
27 | struct mapped_device *md; |
28 | ssize_t ret; |
29 | |
30 | dm_attr = container_of(attr, struct dm_sysfs_attr, attr); |
31 | if (!dm_attr->show) |
32 | return -EIO; |
33 | |
34 | md = dm_get_from_kobject(kobj); |
35 | if (!md) |
36 | return -EINVAL; |
37 | |
38 | ret = dm_attr->show(md, page); |
39 | dm_put(md); |
40 | |
41 | return ret; |
42 | } |
43 | |
44 | #define DM_ATTR_RW(_name) \ |
45 | struct dm_sysfs_attr dm_attr_##_name = \ |
46 | __ATTR(_name, 0644, dm_attr_##_name##_show, dm_attr_##_name##_store) |
47 | |
48 | static ssize_t dm_attr_store(struct kobject *kobj, struct attribute *attr, |
49 | const char *page, size_t count) |
50 | { |
51 | struct dm_sysfs_attr *dm_attr; |
52 | struct mapped_device *md; |
53 | ssize_t ret; |
54 | |
55 | dm_attr = container_of(attr, struct dm_sysfs_attr, attr); |
56 | if (!dm_attr->store) |
57 | return -EIO; |
58 | |
59 | md = dm_get_from_kobject(kobj); |
60 | if (!md) |
61 | return -EINVAL; |
62 | |
63 | ret = dm_attr->store(md, page, count); |
64 | dm_put(md); |
65 | |
66 | return ret; |
67 | } |
68 | |
69 | static ssize_t dm_attr_name_show(struct mapped_device *md, char *buf) |
70 | { |
71 | if (dm_copy_name_and_uuid(md, name: buf, NULL)) |
72 | return -EIO; |
73 | |
74 | strcat(p: buf, q: "\n" ); |
75 | return strlen(buf); |
76 | } |
77 | |
78 | static ssize_t dm_attr_uuid_show(struct mapped_device *md, char *buf) |
79 | { |
80 | if (dm_copy_name_and_uuid(md, NULL, uuid: buf)) |
81 | return -EIO; |
82 | |
83 | strcat(p: buf, q: "\n" ); |
84 | return strlen(buf); |
85 | } |
86 | |
87 | static ssize_t dm_attr_suspended_show(struct mapped_device *md, char *buf) |
88 | { |
89 | sprintf(buf, fmt: "%d\n" , dm_suspended_md(md)); |
90 | |
91 | return strlen(buf); |
92 | } |
93 | |
94 | static ssize_t dm_attr_use_blk_mq_show(struct mapped_device *md, char *buf) |
95 | { |
96 | /* Purely for userspace compatibility */ |
97 | sprintf(buf, fmt: "%d\n" , true); |
98 | |
99 | return strlen(buf); |
100 | } |
101 | |
102 | static DM_ATTR_RO(name); |
103 | static DM_ATTR_RO(uuid); |
104 | static DM_ATTR_RO(suspended); |
105 | static DM_ATTR_RO(use_blk_mq); |
106 | static DM_ATTR_RW(rq_based_seq_io_merge_deadline); |
107 | |
108 | static struct attribute *dm_attrs[] = { |
109 | &dm_attr_name.attr, |
110 | &dm_attr_uuid.attr, |
111 | &dm_attr_suspended.attr, |
112 | &dm_attr_use_blk_mq.attr, |
113 | &dm_attr_rq_based_seq_io_merge_deadline.attr, |
114 | NULL, |
115 | }; |
116 | ATTRIBUTE_GROUPS(dm); |
117 | |
118 | static const struct sysfs_ops dm_sysfs_ops = { |
119 | .show = dm_attr_show, |
120 | .store = dm_attr_store, |
121 | }; |
122 | |
123 | static const struct kobj_type dm_ktype = { |
124 | .sysfs_ops = &dm_sysfs_ops, |
125 | .default_groups = dm_groups, |
126 | .release = dm_kobject_release, |
127 | }; |
128 | |
129 | /* |
130 | * Initialize kobj |
131 | * because nobody using md yet, no need to call explicit dm_get/put |
132 | */ |
133 | int dm_sysfs_init(struct mapped_device *md) |
134 | { |
135 | return kobject_init_and_add(kobj: dm_kobject(md), ktype: &dm_ktype, |
136 | parent: &disk_to_dev(dm_disk(md))->kobj, |
137 | fmt: "%s" , "dm" ); |
138 | } |
139 | |
140 | /* |
141 | * Remove kobj, called after all references removed |
142 | */ |
143 | void dm_sysfs_exit(struct mapped_device *md) |
144 | { |
145 | struct kobject *kobj = dm_kobject(md); |
146 | |
147 | kobject_put(kobj); |
148 | wait_for_completion(dm_get_completion_from_kobject(kobj)); |
149 | } |
150 | |