1 /*-
2  * Copyright (c) 2010 Isilon Systems, Inc.
3  * Copyright (c) 2010 iX Systems, Inc.
4  * Copyright (c) 2010 Panasas, Inc.
5  * Copyright (c) 2013-2021 Mellanox Technologies, Ltd.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice unmodified, this list of conditions, and the following
13  *    disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <linux/kobject.h>
31 #include <linux/sysfs.h>
32 
33 static void kset_join(struct kobject *kobj);
34 static void kset_leave(struct kobject *kobj);
35 static void kset_kfree(struct kobject *kobj);
36 
37 struct kobject *
kobject_create(void)38 kobject_create(void)
39 {
40 	struct kobject *kobj;
41 
42 	kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
43 	if (kobj == NULL)
44 		return (NULL);
45 	kobject_init(kobj, &linux_kfree_type);
46 
47 	return (kobj);
48 }
49 
50 
51 int
kobject_set_name_vargs(struct kobject * kobj,const char * fmt,va_list args)52 kobject_set_name_vargs(struct kobject *kobj, const char *fmt, va_list args)
53 {
54 	va_list tmp_va;
55 	int len;
56 	char *old;
57 	char *name;
58 	char dummy;
59 
60 	old = kobj->name;
61 
62 	if (old && fmt == NULL)
63 		return (0);
64 
65 	/* compute length of string */
66 	va_copy(tmp_va, args);
67 	len = vsnprintf(&dummy, 0, fmt, tmp_va);
68 	va_end(tmp_va);
69 
70 	/* account for zero termination */
71 	len++;
72 
73 	/* check for error */
74 	if (len < 1)
75 		return (-EINVAL);
76 
77 	/* allocate memory for string */
78 	name = kzalloc(len, GFP_KERNEL);
79 	if (name == NULL)
80 		return (-ENOMEM);
81 	vsnprintf(name, len, fmt, args);
82 	kobj->name = name;
83 
84 	/* free old string */
85 	kfree(old);
86 
87 	/* filter new string */
88 	for (; *name != '\0'; name++)
89 		if (*name == '/')
90 			*name = '!';
91 	return (0);
92 }
93 
94 int
kobject_set_name(struct kobject * kobj,const char * fmt,...)95 kobject_set_name(struct kobject *kobj, const char *fmt, ...)
96 {
97 	va_list args;
98 	int error;
99 
100 	va_start(args, fmt);
101 	error = kobject_set_name_vargs(kobj, fmt, args);
102 	va_end(args);
103 
104 	return (error);
105 }
106 
107 static int
kobject_add_complete(struct kobject * kobj)108 kobject_add_complete(struct kobject *kobj)
109 {
110 	const struct kobj_type *t;
111 	int error;
112 
113 	if (kobj->kset != NULL) {
114 		kset_join(kobj);
115 		kobj->parent = &kobj->kset->kobj;
116 	}
117 
118 	error = sysfs_create_dir(kobj);
119 	if (error == 0 && kobj->ktype && kobj->ktype->default_attrs) {
120 		struct attribute **attr;
121 		t = kobj->ktype;
122 
123 		for (attr = t->default_attrs; *attr != NULL; attr++) {
124 			error = sysfs_create_file(kobj, *attr);
125 			if (error)
126 				break;
127 		}
128 		if (error)
129 			sysfs_remove_dir(kobj);
130 	}
131 
132 	if (error != 0)
133 		kset_leave(kobj);
134 
135 	return (error);
136 }
137 
138 int
kobject_add(struct kobject * kobj,struct kobject * parent,const char * fmt,...)139 kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...)
140 {
141 	va_list args;
142 	int error;
143 
144 	kobj->parent = parent;
145 
146 	va_start(args, fmt);
147 	error = kobject_set_name_vargs(kobj, fmt, args);
148 	va_end(args);
149 	if (error)
150 		return (error);
151 
152 	return kobject_add_complete(kobj);
153 }
154 
155 int
kobject_init_and_add(struct kobject * kobj,const struct kobj_type * ktype,struct kobject * parent,const char * fmt,...)156 kobject_init_and_add(struct kobject *kobj, const struct kobj_type *ktype,
157     struct kobject *parent, const char *fmt, ...)
158 {
159 	va_list args;
160 	int error;
161 
162 	kobject_init(kobj, ktype);
163 	kobj->ktype = ktype;
164 	kobj->parent = parent;
165 	kobj->name = NULL;
166 
167 	va_start(args, fmt);
168 	error = kobject_set_name_vargs(kobj, fmt, args);
169 	va_end(args);
170 	if (error)
171 		return (error);
172 	return kobject_add_complete(kobj);
173 }
174 
175 void
linux_kobject_release(struct kref * kref)176 linux_kobject_release(struct kref *kref)
177 {
178 	struct kobject *kobj;
179 	char *name;
180 
181 	kobj = container_of(kref, struct kobject, kref);
182 	sysfs_remove_dir(kobj);
183 	kset_leave(kobj);
184 	name = kobj->name;
185 	if (kobj->ktype && kobj->ktype->release)
186 		kobj->ktype->release(kobj);
187 	kfree(name);
188 }
189 
190 static void
linux_kobject_kfree(struct kobject * kobj)191 linux_kobject_kfree(struct kobject *kobj)
192 {
193 	kfree(kobj);
194 }
195 
196 const struct kobj_type linux_kfree_type = {
197 	.release = linux_kobject_kfree
198 };
199 
200 void
linux_kobject_kfree_name(struct kobject * kobj)201 linux_kobject_kfree_name(struct kobject *kobj)
202 {
203 	if (kobj) {
204 		kfree(kobj->name);
205 	}
206 }
207 
208 static ssize_t
lkpi_kobj_attr_show(struct kobject * kobj,struct attribute * attr,char * buf)209 lkpi_kobj_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
210 {
211 	struct kobj_attribute *ka =
212 	    container_of(attr, struct kobj_attribute, attr);
213 
214 	if (ka->show == NULL)
215 		return (-EIO);
216 
217 	return (ka->show(kobj, ka, buf));
218 }
219 
220 static ssize_t
lkpi_kobj_attr_store(struct kobject * kobj,struct attribute * attr,const char * buf,size_t count)221 lkpi_kobj_attr_store(struct kobject *kobj, struct attribute *attr,
222     const char *buf, size_t count)
223 {
224 	struct kobj_attribute *ka =
225 	    container_of(attr, struct kobj_attribute, attr);
226 
227 	if (ka->store == NULL)
228 		return (-EIO);
229 
230 	return (ka->store(kobj, ka, buf, count));
231 }
232 
233 const struct sysfs_ops kobj_sysfs_ops = {
234 	.show	= lkpi_kobj_attr_show,
235 	.store	= lkpi_kobj_attr_store,
236 };
237 
238 const struct kobj_type linux_kset_kfree_type = {
239 	.release = kset_kfree
240 };
241 
242 static struct kset *
kset_create(const char * name,const struct kset_uevent_ops * uevent_ops,struct kobject * parent_kobj)243 kset_create(const char *name,
244     const struct kset_uevent_ops *uevent_ops,
245     struct kobject *parent_kobj)
246 {
247 	struct kset *kset;
248 
249 	kset = kzalloc(sizeof(*kset), GFP_KERNEL);
250 	if (kset == NULL)
251 		return (NULL);
252 
253 	kset->uevent_ops = uevent_ops;
254 
255 	kobject_set_name(&kset->kobj, "%s", name);
256 	kset->kobj.parent = parent_kobj;
257 	kset->kobj.kset = NULL;
258 
259 	return (kset);
260 }
261 
262 void
kset_init(struct kset * kset)263 kset_init(struct kset *kset)
264 {
265 	kobject_init(&kset->kobj, &linux_kset_kfree_type);
266 	INIT_LIST_HEAD(&kset->list);
267 	spin_lock_init(&kset->list_lock);
268 }
269 
270 static void
kset_join(struct kobject * kobj)271 kset_join(struct kobject *kobj)
272 {
273 	struct kset *kset;
274 
275 	kset = kobj->kset;
276 	if (kset == NULL)
277 		return;
278 
279 	kset_get(kobj->kset);
280 
281 	spin_lock(&kset->list_lock);
282 	list_add_tail(&kobj->entry, &kset->list);
283 	spin_unlock(&kset->list_lock);
284 }
285 
286 static void
kset_leave(struct kobject * kobj)287 kset_leave(struct kobject *kobj)
288 {
289 	struct kset *kset;
290 
291 	kset = kobj->kset;
292 	if (kset == NULL)
293 		return;
294 
295 	spin_lock(&kset->list_lock);
296 	list_del_init(&kobj->entry);
297 	spin_unlock(&kset->list_lock);
298 
299 	kset_put(kobj->kset);
300 }
301 
302 struct kset *
kset_create_and_add(const char * name,const struct kset_uevent_ops * u,struct kobject * parent_kobj)303 kset_create_and_add(const char *name, const struct kset_uevent_ops *u,
304     struct kobject *parent_kobj)
305 {
306 	int ret;
307 	struct kset *kset;
308 
309 	kset = kset_create(name, u, parent_kobj);
310 	if (kset == NULL)
311 		return (NULL);
312 
313 	ret = kset_register(kset);
314 	if (ret != 0) {
315 		linux_kobject_kfree_name(&kset->kobj);
316 		kfree(kset);
317 		return (NULL);
318 	}
319 
320 	return (kset);
321 }
322 
323 int
kset_register(struct kset * kset)324 kset_register(struct kset *kset)
325 {
326 	int ret;
327 
328 	if (kset == NULL)
329 		return -EINVAL;
330 
331 	kset_init(kset);
332 	ret = kobject_add_complete(&kset->kobj);
333 
334 	return ret;
335 }
336 
337 void
kset_unregister(struct kset * kset)338 kset_unregister(struct kset *kset)
339 {
340 	if (kset == NULL)
341 		return;
342 
343 	kobject_del(&kset->kobj);
344 	kobject_put(&kset->kobj);
345 }
346 
347 static void
kset_kfree(struct kobject * kobj)348 kset_kfree(struct kobject *kobj)
349 {
350 	struct kset *kset;
351 
352 	kset = to_kset(kobj);
353 	kfree(kset);
354 }
355