Skip to content
Snippets Groups Projects
kobject.c 24.3 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
/*
 * kobject.c - library routines for handling generic kernel objects
 *
 * Copyright (c) 2002-2003 Patrick Mochel <mochel@osdl.org>
 * Copyright (c) 2006-2007 Greg Kroah-Hartman <greg@kroah.com>
 * Copyright (c) 2006-2007 Novell Inc.
Linus Torvalds's avatar
Linus Torvalds committed
 *
 * This file is released under the GPLv2.
 *
 *
 * Please see the file Documentation/kobject.txt for critical information
 * about using the kobject interface.
 */

#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/module.h>
#include <linux/stat.h>
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed

/*
 * populate_dir - populate directory with attributes.
 * @kobj: object we're working on.
Linus Torvalds's avatar
Linus Torvalds committed
 *
 * Most subsystems have a set of default attributes that are associated
 * with an object that registers with them.  This is a helper called during
 * object registration that loops through the default attributes of the
 * subsystem and creates attributes files for them in sysfs.
Linus Torvalds's avatar
Linus Torvalds committed
 */
static int populate_dir(struct kobject *kobj)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct kobj_type *t = get_ktype(kobj);
	struct attribute *attr;
Linus Torvalds's avatar
Linus Torvalds committed
	int error = 0;
	int i;
Linus Torvalds's avatar
Linus Torvalds committed
	if (t && t->default_attrs) {
		for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {
			error = sysfs_create_file(kobj, attr);
			if (error)
Linus Torvalds's avatar
Linus Torvalds committed
				break;
		}
	}
	return error;
}

static int create_dir(struct kobject *kobj)
Linus Torvalds's avatar
Linus Torvalds committed
{
	int error = 0;
	if (kobject_name(kobj)) {
		error = sysfs_create_dir(kobj);
Linus Torvalds's avatar
Linus Torvalds committed
		if (!error) {
			error = populate_dir(kobj);
			if (error)
Linus Torvalds's avatar
Linus Torvalds committed
				sysfs_remove_dir(kobj);
		}
	}
	return error;
}

static int get_kobj_path_length(struct kobject *kobj)
{
	int length = 1;
	struct kobject *parent = kobj;
Linus Torvalds's avatar
Linus Torvalds committed

	/* walk up the ancestors until we hit the one pointing to the
Linus Torvalds's avatar
Linus Torvalds committed
	 * root.
	 * Add 1 to strlen for leading '/' of each level.
	 */
	do {
		if (kobject_name(parent) == NULL)
			return 0;
Linus Torvalds's avatar
Linus Torvalds committed
		length += strlen(kobject_name(parent)) + 1;
		parent = parent->parent;
	} while (parent);
	return length;
}

static void fill_kobj_path(struct kobject *kobj, char *path, int length)
{
	struct kobject *parent;
Linus Torvalds's avatar
Linus Torvalds committed

	--length;
	for (parent = kobj; parent; parent = parent->parent) {
		int cur = strlen(kobject_name(parent));
		/* back up enough to print this name with '/' */
		length -= cur;
		strncpy(path + length, kobject_name(parent), cur);
Linus Torvalds's avatar
Linus Torvalds committed
		*(path + --length) = '/';
	}

	pr_debug("kobject: '%s' (%p): %s: path = '%s'\n", kobject_name(kobj),
		 kobj, __func__, path);
Linus Torvalds's avatar
Linus Torvalds committed
}

/**
 * kobject_get_path - generate and return the path associated with a given kobj and kset pair.
Linus Torvalds's avatar
Linus Torvalds committed
 *
 * @kobj:	kobject in question, with which to build the path
 * @gfp_mask:	the allocation type used to allocate the path
 *
 * The result must be freed by the caller with kfree().
Linus Torvalds's avatar
Linus Torvalds committed
 */
Al Viro's avatar
Al Viro committed
char *kobject_get_path(struct kobject *kobj, gfp_t gfp_mask)
Linus Torvalds's avatar
Linus Torvalds committed
{
	char *path;
	int len;

	len = get_kobj_path_length(kobj);
	if (len == 0)
		return NULL;
	path = kzalloc(len, gfp_mask);
Linus Torvalds's avatar
Linus Torvalds committed
	if (!path)
		return NULL;
	fill_kobj_path(kobj, path, len);

	return path;
}
EXPORT_SYMBOL_GPL(kobject_get_path);
Linus Torvalds's avatar
Linus Torvalds committed

/* add the kobject to its kset's list */
static void kobj_kset_join(struct kobject *kobj)
Linus Torvalds's avatar
Linus Torvalds committed
{
	if (!kobj->kset)

	kset_get(kobj->kset);
	spin_lock(&kobj->kset->list_lock);
	list_add_tail(&kobj->entry, &kobj->kset->list);
	spin_unlock(&kobj->kset->list_lock);
/* remove the kobject from its kset's list */
static void kobj_kset_leave(struct kobject *kobj)
{
	if (!kobj->kset)
		return;
Linus Torvalds's avatar
Linus Torvalds committed

	spin_lock(&kobj->kset->list_lock);
	list_del_init(&kobj->entry);
	spin_unlock(&kobj->kset->list_lock);
	kset_put(kobj->kset);
}
Linus Torvalds's avatar
Linus Torvalds committed

static void kobject_init_internal(struct kobject *kobj)
Linus Torvalds's avatar
Linus Torvalds committed
{
	if (!kobj)
		return;
	kref_init(&kobj->kref);
	INIT_LIST_HEAD(&kobj->entry);
	kobj->state_in_sysfs = 0;
	kobj->state_add_uevent_sent = 0;
	kobj->state_remove_uevent_sent = 0;
	kobj->state_initialized = 1;
static int kobject_add_internal(struct kobject *kobj)
Linus Torvalds's avatar
Linus Torvalds committed
{
	int error = 0;
Linus Torvalds's avatar
Linus Torvalds committed

	if (!kobj)
Linus Torvalds's avatar
Linus Torvalds committed
		return -ENOENT;
	if (!kobj->name || !kobj->name[0]) {
Arjan van de Ven's avatar
Arjan van de Ven committed
		WARN(1, "kobject: (%p): attempted to be registered with empty "
			 "name!\n", kobj);
Linus Torvalds's avatar
Linus Torvalds committed

	parent = kobject_get(kobj->parent);
Linus Torvalds's avatar
Linus Torvalds committed

	/* join kset if set, use it as parent if we do not already have one */
Linus Torvalds's avatar
Linus Torvalds committed
	if (kobj->kset) {
		if (!parent)
Linus Torvalds's avatar
Linus Torvalds committed
			parent = kobject_get(&kobj->kset->kobj);
		kobj_kset_join(kobj);
		kobj->parent = parent;
	pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
		 kobject_name(kobj), kobj, __func__,
		 parent ? kobject_name(parent) : "<NULL>",
		 kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
	error = create_dir(kobj);
Linus Torvalds's avatar
Linus Torvalds committed
	if (error) {
		kobj_kset_leave(kobj);
		kobject_put(parent);
		kobj->parent = NULL;

		/* be noisy on error issues */
		if (error == -EEXIST)
			printk(KERN_ERR "%s failed for %s with "
			       "-EEXIST, don't try to register things with "
			       "the same name in the same directory.\n",
			       __func__, kobject_name(kobj));
			printk(KERN_ERR "%s failed for %s (%d)\n",
Loading
Loading full blame...