File Index
Files and directories
Sign in
FILE

xe_hw_engine_class_sysfs.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/xe/xe_hw_engine_class_sysfs.c C
  1// SPDX-License-Identifier: MIT
  2/*
  3 * Copyright © 2023 Intel Corporation
  4 */
  5
  6#include <drm/drm_managed.h>
  7#include <linux/kobject.h>
  8#include <linux/sysfs.h>
  9
 10#include "xe_device.h"
 11#include "xe_gt.h"
 12#include "xe_hw_engine_class_sysfs.h"
 13#include "xe_pm.h"
 14
 15#define MAX_ENGINE_CLASS_NAME_LEN    16
 16static int xe_add_hw_engine_class_defaults(struct xe_device *xe,
 17					   struct kobject *parent);
 18
 19/**
 20 * xe_hw_engine_timeout_in_range - Helper to check if timeout is in range
 21 * @timeout: timeout to validate
 22 * @min: min value of valid range
 23 * @max: max value of valid range
 24 *
 25 * This helper helps to validate if timeout is in min-max range of HW engine
 26 * scheduler.
 27 *
 28 * Returns: Returns false value for failure and true for success.
 29 */
 30bool xe_hw_engine_timeout_in_range(u64 timeout, u64 min, u64 max)
 31{
 32	return timeout >= min && timeout <= max;
 33}
 34
 35static void xe_hw_engine_sysfs_kobj_release(struct kobject *kobj)
 36{
 37	kfree(kobj);
 38}
 39
 40static ssize_t xe_hw_engine_class_sysfs_attr_show(struct kobject *kobj,
 41						  struct attribute *attr,
 42						  char *buf)
 43{
 44	struct xe_device *xe = kobj_to_xe(kobj);
 45	struct kobj_attribute *kattr;
 46	ssize_t ret = -EIO;
 47
 48	kattr = container_of(attr, struct kobj_attribute, attr);
 49	if (kattr->show) {
 50		xe_pm_runtime_get(xe);
 51		ret = kattr->show(kobj, kattr, buf);
 52		xe_pm_runtime_put(xe);
 53	}
 54
 55	return ret;
 56}
 57
 58static ssize_t xe_hw_engine_class_sysfs_attr_store(struct kobject *kobj,
 59						   struct attribute *attr,
 60						   const char *buf,
 61						   size_t count)
 62{
 63	struct xe_device *xe = kobj_to_xe(kobj);
 64	struct kobj_attribute *kattr;
 65	ssize_t ret = -EIO;
 66
 67	kattr = container_of(attr, struct kobj_attribute, attr);
 68	if (kattr->store) {
 69		xe_pm_runtime_get(xe);
 70		ret = kattr->store(kobj, kattr, buf, count);
 71		xe_pm_runtime_put(xe);
 72	}
 73
 74	return ret;
 75}
 76
 77static const struct sysfs_ops xe_hw_engine_class_sysfs_ops = {
 78	.show = xe_hw_engine_class_sysfs_attr_show,
 79	.store = xe_hw_engine_class_sysfs_attr_store,
 80};
 81
 82static const struct kobj_type kobj_xe_hw_engine_type = {
 83	.release = xe_hw_engine_sysfs_kobj_release,
 84	.sysfs_ops = &xe_hw_engine_class_sysfs_ops,
 85};
 86
 87static const struct kobj_type kobj_xe_hw_engine_type_def = {
 88	.release = xe_hw_engine_sysfs_kobj_release,
 89	.sysfs_ops = &kobj_sysfs_ops,
 90};
 91
 92static ssize_t job_timeout_max_store(struct kobject *kobj,
 93				     struct kobj_attribute *attr,
 94				     const char *buf, size_t count)
 95{
 96	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
 97	u32 timeout;
 98	int err;
 99
100	err = kstrtou32(buf, 0, &timeout);
101	if (err)
102		return err;
103
104	if (timeout < eclass->sched_props.job_timeout_min)
105		return -EINVAL;
106
107	if (!xe_hw_engine_timeout_in_range(timeout,
108					   XE_HW_ENGINE_JOB_TIMEOUT_MIN,
109					   XE_HW_ENGINE_JOB_TIMEOUT_MAX))
110		return -EINVAL;
111
112	WRITE_ONCE(eclass->sched_props.job_timeout_max, timeout);
113
114	return count;
115}
116
117static ssize_t job_timeout_max_show(struct kobject *kobj,
118				    struct kobj_attribute *attr, char *buf)
119{
120	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
121
122	return sysfs_emit(buf, "%u\n", eclass->sched_props.job_timeout_max);
123}
124
125static const struct kobj_attribute job_timeout_max_attr =
126__ATTR(job_timeout_max, 0644, job_timeout_max_show, job_timeout_max_store);
127
128static ssize_t job_timeout_min_store(struct kobject *kobj,
129				     struct kobj_attribute *attr,
130				     const char *buf, size_t count)
131{
132	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
133	u32 timeout;
134	int err;
135
136	err = kstrtou32(buf, 0, &timeout);
137	if (err)
138		return err;
139
140	if (timeout > eclass->sched_props.job_timeout_max)
141		return -EINVAL;
142
143	if (!xe_hw_engine_timeout_in_range(timeout,
144					   XE_HW_ENGINE_JOB_TIMEOUT_MIN,
145					   XE_HW_ENGINE_JOB_TIMEOUT_MAX))
146		return -EINVAL;
147
148	WRITE_ONCE(eclass->sched_props.job_timeout_min, timeout);
149
150	return count;
151}
152
153static ssize_t job_timeout_min_show(struct kobject *kobj,
154				    struct kobj_attribute *attr, char *buf)
155{
156	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
157
158	return sysfs_emit(buf, "%u\n", eclass->sched_props.job_timeout_min);
159}
160
161static const struct kobj_attribute job_timeout_min_attr =
162__ATTR(job_timeout_min, 0644, job_timeout_min_show, job_timeout_min_store);
163
164static ssize_t job_timeout_store(struct kobject *kobj,
165				 struct kobj_attribute *attr,
166				 const char *buf, size_t count)
167{
168	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
169	u32 min = eclass->sched_props.job_timeout_min;
170	u32 max = eclass->sched_props.job_timeout_max;
171	u32 timeout;
172	int err;
173
174	err = kstrtou32(buf, 0, &timeout);
175	if (err)
176		return err;
177
178	if (!xe_hw_engine_timeout_in_range(timeout, min, max))
179		return -EINVAL;
180
181	WRITE_ONCE(eclass->sched_props.job_timeout_ms, timeout);
182
183	return count;
184}
185
186static ssize_t job_timeout_show(struct kobject *kobj,
187				struct kobj_attribute *attr, char *buf)
188{
189	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
190
191	return sysfs_emit(buf, "%u\n", eclass->sched_props.job_timeout_ms);
192}
193
194static const struct kobj_attribute job_timeout_attr =
195__ATTR(job_timeout_ms, 0644, job_timeout_show, job_timeout_store);
196
197static ssize_t job_timeout_default(struct kobject *kobj,
198				   struct kobj_attribute *attr, char *buf)
199{
200	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);
201
202	return sysfs_emit(buf, "%u\n", eclass->defaults.job_timeout_ms);
203}
204
205static const struct kobj_attribute job_timeout_def =
206__ATTR(job_timeout_ms, 0444, job_timeout_default, NULL);
207
208static ssize_t job_timeout_min_default(struct kobject *kobj,
209				       struct kobj_attribute *attr, char *buf)
210{
211	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);
212
213	return sysfs_emit(buf, "%u\n", eclass->defaults.job_timeout_min);
214}
215
216static const struct kobj_attribute job_timeout_min_def =
217__ATTR(job_timeout_min, 0444, job_timeout_min_default, NULL);
218
219static ssize_t job_timeout_max_default(struct kobject *kobj,
220				       struct kobj_attribute *attr, char *buf)
221{
222	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);
223
224	return sysfs_emit(buf, "%u\n", eclass->defaults.job_timeout_max);
225}
226
227static const struct kobj_attribute job_timeout_max_def =
228__ATTR(job_timeout_max, 0444, job_timeout_max_default, NULL);
229
230static ssize_t timeslice_duration_store(struct kobject *kobj,
231					struct kobj_attribute *attr,
232					const char *buf, size_t count)
233{
234	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
235	u32 min = eclass->sched_props.timeslice_min;
236	u32 max = eclass->sched_props.timeslice_max;
237	u32 duration;
238	int err;
239
240	err = kstrtou32(buf, 0, &duration);
241	if (err)
242		return err;
243
244	if (!xe_hw_engine_timeout_in_range(duration, min, max))
245		return -EINVAL;
246
247	WRITE_ONCE(eclass->sched_props.timeslice_us, duration);
248
249	return count;
250}
251
252static ssize_t timeslice_duration_max_store(struct kobject *kobj,
253					    struct kobj_attribute *attr,
254					    const char *buf, size_t count)
255{
256	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
257	u32 duration;
258	int err;
259
260	err = kstrtou32(buf, 0, &duration);
261	if (err)
262		return err;
263
264	if (duration < eclass->sched_props.timeslice_min)
265		return -EINVAL;
266
267	if (!xe_hw_engine_timeout_in_range(duration,
268					   XE_HW_ENGINE_TIMESLICE_MIN,
269					   XE_HW_ENGINE_TIMESLICE_MAX))
270		return -EINVAL;
271
272	WRITE_ONCE(eclass->sched_props.timeslice_max, duration);
273
274	return count;
275}
276
277static ssize_t timeslice_duration_max_show(struct kobject *kobj,
278					   struct kobj_attribute *attr,
279					   char *buf)
280{
281	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
282
283	return sysfs_emit(buf, "%u\n", eclass->sched_props.timeslice_max);
284}
285
286static const struct kobj_attribute timeslice_duration_max_attr =
287	__ATTR(timeslice_duration_max, 0644, timeslice_duration_max_show,
288	       timeslice_duration_max_store);
289
290static ssize_t timeslice_duration_min_store(struct kobject *kobj,
291					    struct kobj_attribute *attr,
292					    const char *buf, size_t count)
293{
294	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
295	u32 duration;
296	int err;
297
298	err = kstrtou32(buf, 0, &duration);
299	if (err)
300		return err;
301
302	if (duration > eclass->sched_props.timeslice_max)
303		return -EINVAL;
304
305	if (!xe_hw_engine_timeout_in_range(duration,
306					   XE_HW_ENGINE_TIMESLICE_MIN,
307					   XE_HW_ENGINE_TIMESLICE_MAX))
308		return -EINVAL;
309
310	WRITE_ONCE(eclass->sched_props.timeslice_min, duration);
311
312	return count;
313}
314
315static ssize_t timeslice_duration_min_show(struct kobject *kobj,
316					   struct kobj_attribute *attr,
317					   char *buf)
318{
319	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
320
321	return sysfs_emit(buf, "%u\n", eclass->sched_props.timeslice_min);
322}
323
324static const struct kobj_attribute timeslice_duration_min_attr =
325	__ATTR(timeslice_duration_min, 0644, timeslice_duration_min_show,
326	       timeslice_duration_min_store);
327
328static ssize_t timeslice_duration_show(struct kobject *kobj,
329				       struct kobj_attribute *attr, char *buf)
330{
331	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
332
333	return sysfs_emit(buf, "%u\n", eclass->sched_props.timeslice_us);
334}
335
336static const struct kobj_attribute timeslice_duration_attr =
337	__ATTR(timeslice_duration_us, 0644, timeslice_duration_show,
338	       timeslice_duration_store);
339
340static ssize_t timeslice_default(struct kobject *kobj,
341				 struct kobj_attribute *attr, char *buf)
342{
343	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);
344
345	return sysfs_emit(buf, "%u\n", eclass->defaults.timeslice_us);
346}
347
348static const struct kobj_attribute timeslice_duration_def =
349__ATTR(timeslice_duration_us, 0444, timeslice_default, NULL);
350
351static ssize_t timeslice_min_default(struct kobject *kobj,
352				     struct kobj_attribute *attr, char *buf)
353{
354	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);
355
356	return sysfs_emit(buf, "%u\n", eclass->defaults.timeslice_min);
357}
358
359static const struct kobj_attribute timeslice_duration_min_def =
360__ATTR(timeslice_duration_min, 0444, timeslice_min_default, NULL);
361
362static ssize_t timeslice_max_default(struct kobject *kobj,
363				     struct kobj_attribute *attr, char *buf)
364{
365	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);
366
367	return sysfs_emit(buf, "%u\n", eclass->defaults.timeslice_max);
368}
369
370static const struct kobj_attribute timeslice_duration_max_def =
371__ATTR(timeslice_duration_max, 0444, timeslice_max_default, NULL);
372
373static ssize_t preempt_timeout_store(struct kobject *kobj,
374				     struct kobj_attribute *attr,
375				     const char *buf, size_t count)
376{
377	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
378	u32 min = eclass->sched_props.preempt_timeout_min;
379	u32 max = eclass->sched_props.preempt_timeout_max;
380	u32 timeout;
381	int err;
382
383	err = kstrtou32(buf, 0, &timeout);
384	if (err)
385		return err;
386
387	if (!xe_hw_engine_timeout_in_range(timeout, min, max))
388		return -EINVAL;
389
390	WRITE_ONCE(eclass->sched_props.preempt_timeout_us, timeout);
391
392	return count;
393}
394
395static ssize_t preempt_timeout_show(struct kobject *kobj,
396				    struct kobj_attribute *attr, char *buf)
397{
398	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
399
400	return sysfs_emit(buf, "%u\n", eclass->sched_props.preempt_timeout_us);
401}
402
403static const struct kobj_attribute preempt_timeout_attr =
404__ATTR(preempt_timeout_us, 0644, preempt_timeout_show, preempt_timeout_store);
405
406static ssize_t preempt_timeout_default(struct kobject *kobj,
407				       struct kobj_attribute *attr,
408				       char *buf)
409{
410	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);
411
412	return sysfs_emit(buf, "%u\n", eclass->defaults.preempt_timeout_us);
413}
414
415static const struct kobj_attribute preempt_timeout_def =
416__ATTR(preempt_timeout_us, 0444, preempt_timeout_default, NULL);
417
418static ssize_t preempt_timeout_min_default(struct kobject *kobj,
419					   struct kobj_attribute *attr,
420					   char *buf)
421{
422	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);
423
424	return sysfs_emit(buf, "%u\n", eclass->defaults.preempt_timeout_min);
425}
426
427static const struct kobj_attribute preempt_timeout_min_def =
428__ATTR(preempt_timeout_min, 0444, preempt_timeout_min_default, NULL);
429
430static ssize_t preempt_timeout_max_default(struct kobject *kobj,
431					   struct kobj_attribute *attr,
432					   char *buf)
433{
434	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);
435
436	return sysfs_emit(buf, "%u\n", eclass->defaults.preempt_timeout_max);
437}
438
439static const struct kobj_attribute preempt_timeout_max_def =
440__ATTR(preempt_timeout_max, 0444, preempt_timeout_max_default, NULL);
441
442static ssize_t preempt_timeout_max_store(struct kobject *kobj,
443					 struct kobj_attribute *attr,
444					 const char *buf, size_t count)
445{
446	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
447	u32 timeout;
448	int err;
449
450	err = kstrtou32(buf, 0, &timeout);
451	if (err)
452		return err;
453
454	if (timeout < eclass->sched_props.preempt_timeout_min)
455		return -EINVAL;
456
457	if (!xe_hw_engine_timeout_in_range(timeout,
458					   XE_HW_ENGINE_PREEMPT_TIMEOUT_MIN,
459					   XE_HW_ENGINE_PREEMPT_TIMEOUT_MAX))
460		return -EINVAL;
461
462	WRITE_ONCE(eclass->sched_props.preempt_timeout_max, timeout);
463
464	return count;
465}
466
467static ssize_t preempt_timeout_max_show(struct kobject *kobj,
468					struct kobj_attribute *attr, char *buf)
469{
470	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
471
472	return sysfs_emit(buf, "%u\n", eclass->sched_props.preempt_timeout_max);
473}
474
475static const struct kobj_attribute preempt_timeout_max_attr =
476	__ATTR(preempt_timeout_max, 0644, preempt_timeout_max_show,
477	       preempt_timeout_max_store);
478
479static ssize_t preempt_timeout_min_store(struct kobject *kobj,
480					 struct kobj_attribute *attr,
481					 const char *buf, size_t count)
482{
483	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
484	u32 timeout;
485	int err;
486
487	err = kstrtou32(buf, 0, &timeout);
488	if (err)
489		return err;
490
491	if (timeout > eclass->sched_props.preempt_timeout_max)
492		return -EINVAL;
493
494	if (!xe_hw_engine_timeout_in_range(timeout,
495					   XE_HW_ENGINE_PREEMPT_TIMEOUT_MIN,
496					   XE_HW_ENGINE_PREEMPT_TIMEOUT_MAX))
497		return -EINVAL;
498
499	WRITE_ONCE(eclass->sched_props.preempt_timeout_min, timeout);
500
501	return count;
502}
503
504static ssize_t preempt_timeout_min_show(struct kobject *kobj,
505					struct kobj_attribute *attr, char *buf)
506{
507	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
508
509	return sysfs_emit(buf, "%u\n", eclass->sched_props.preempt_timeout_min);
510}
511
512static const struct kobj_attribute preempt_timeout_min_attr =
513	__ATTR(preempt_timeout_min, 0644, preempt_timeout_min_show,
514	       preempt_timeout_min_store);
515
516static const struct attribute *defaults[] = {
517	&job_timeout_def.attr,
518	&job_timeout_min_def.attr,
519	&job_timeout_max_def.attr,
520	&timeslice_duration_def.attr,
521	&timeslice_duration_min_def.attr,
522	&timeslice_duration_max_def.attr,
523	&preempt_timeout_def.attr,
524	&preempt_timeout_min_def.attr,
525	&preempt_timeout_max_def.attr,
526	NULL
527};
528
529static const struct attribute * const files[] = {
530	&job_timeout_attr.attr,
531	&job_timeout_min_attr.attr,
532	&job_timeout_max_attr.attr,
533	&timeslice_duration_attr.attr,
534	&timeslice_duration_min_attr.attr,
535	&timeslice_duration_max_attr.attr,
536	&preempt_timeout_attr.attr,
537	&preempt_timeout_min_attr.attr,
538	&preempt_timeout_max_attr.attr,
539	NULL
540};
541
542static void kobj_xe_hw_engine_class_fini(void *arg)
543{
544	struct kobject *kobj = arg;
545
546	sysfs_remove_files(kobj, files);
547	kobject_put(kobj);
548}
549
550static struct kobj_eclass *
551kobj_xe_hw_engine_class(struct xe_device *xe, struct kobject *parent, const char *name)
552{
553	struct kobj_eclass *keclass;
554	int err = 0;
555
556	keclass = kzalloc(sizeof(*keclass), GFP_KERNEL);
557	if (!keclass)
558		return NULL;
559
560	kobject_init(&keclass->base, &kobj_xe_hw_engine_type);
561	if (kobject_add(&keclass->base, parent, "%s", name)) {
562		kobject_put(&keclass->base);
563		return NULL;
564	}
565	keclass->xe = xe;
566
567	err = devm_add_action_or_reset(xe->drm.dev, kobj_xe_hw_engine_class_fini,
568				       &keclass->base);
569	if (err)
570		return NULL;
571
572	return keclass;
573}
574
575static void hw_engine_class_defaults_fini(void *arg)
576{
577	struct kobject *kobj = arg;
578
579	sysfs_remove_files(kobj, defaults);
580	kobject_put(kobj);
581}
582
583static int xe_add_hw_engine_class_defaults(struct xe_device *xe,
584					   struct kobject *parent)
585{
586	struct kobject *kobj;
587	int err = 0;
588
589	kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
590	if (!kobj)
591		return -ENOMEM;
592
593	kobject_init(kobj, &kobj_xe_hw_engine_type_def);
594	err = kobject_add(kobj, parent, "%s", ".defaults");
595	if (err)
596		goto err_object;
597
598	err = sysfs_create_files(kobj, defaults);
599	if (err)
600		goto err_object;
601
602	return devm_add_action_or_reset(xe->drm.dev, hw_engine_class_defaults_fini, kobj);
603
604err_object:
605	kobject_put(kobj);
606	return err;
607}
608ALLOW_ERROR_INJECTION(xe_add_hw_engine_class_defaults, ERRNO); /* See xe_pci_probe() */
609
610
611static void hw_engine_class_sysfs_fini(void *arg)
612{
613	struct kobject *kobj = arg;
614
615	kobject_put(kobj);
616}
617
618/**
619 * xe_hw_engine_class_sysfs_init - Init HW engine classes on GT.
620 * @gt: Xe GT.
621 *
622 * This routine creates sysfs for HW engine classes and adds methods
623 * to get/set different scheduling properties for HW engines class.
624 *
625 * Returns: Returns error value for failure and 0 for success.
626 */
627int xe_hw_engine_class_sysfs_init(struct xe_gt *gt)
628{
629	struct xe_device *xe = gt_to_xe(gt);
630	struct xe_hw_engine *hwe;
631	enum xe_hw_engine_id id;
632	struct kobject *kobj;
633	u16 class_mask = 0;
634	int err = 0;
635
636	kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
637	if (!kobj)
638		return -ENOMEM;
639
640	kobject_init(kobj, &kobj_xe_hw_engine_type);
641
642	err = kobject_add(kobj, gt->sysfs, "engines");
643	if (err)
644		goto err_object;
645
646	for_each_hw_engine(hwe, gt, id) {
647		const char *name;
648		struct kobj_eclass *keclass;
649
650		if (hwe->class == XE_ENGINE_CLASS_OTHER ||
651		    hwe->class == XE_ENGINE_CLASS_MAX)
652			continue;
653
654		if ((class_mask >> hwe->class) & 1)
655			continue;
656
657		class_mask |= 1 << hwe->class;
658		name = xe_hw_engine_class_to_str(hwe->class);
659		if (!name) {
660			err = -EINVAL;
661			goto err_object;
662		}
663
664		keclass = kobj_xe_hw_engine_class(xe, kobj, name);
665		if (!keclass) {
666			err = -EINVAL;
667			goto err_object;
668		}
669
670		keclass->eclass = hwe->eclass;
671		err = xe_add_hw_engine_class_defaults(xe, &keclass->base);
672		if (err)
673			goto err_object;
674
675		err = sysfs_create_files(&keclass->base, files);
676		if (err)
677			goto err_object;
678	}
679
680	return devm_add_action_or_reset(xe->drm.dev, hw_engine_class_sysfs_fini, kobj);
681
682err_object:
683	kobject_put(kobj);
684	return err;
685}
~/ Read-only file preview.