sysfs, kernfs: revamp sysfs_dirent active_ref lockdep annotation
Currently, sysfs_dirent active_ref lockdep annotation uses attribute->[s]key as the lockdep key, which forces kernfs_create_file_ns() to assume that sysfs_dirent->priv is pointing to a struct attribute which may not be true for non-sysfs users. This patch restructures the lockdep annotation such that * kernfs_ops contains lockdep_key which is used by default for files created kernfs_create_file_ns(). * kernfs_create_file_ns_key() is introduced which takes an extra @key argument. The created file will use the specified key for active_ref lockdep annotation. If NULL is specified, lockdep for the file is disabled. * sysfs_add_file_mode_ns() is updated to use kernfs_create_file_ns_key() with the appropriate key from the attribute or NULL if ignore_lockdep is set. This makes the lockdep annotation properly contained in kernfs while allowing sysfs to cleanly keep its current behavior. This patch doesn't introduce any behavior differences. Signed-off-by: Tejun Heo <tj@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
		
					parent
					
						
							
								2b25a62901
							
						
					
				
			
			
				commit
				
					
						517e64f578
					
				
			
		
					 4 changed files with 56 additions and 52 deletions
				
			
		| 
						 | 
					@ -150,7 +150,7 @@ struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd)
 | 
				
			||||||
	if (!atomic_inc_unless_negative(&sd->s_active))
 | 
						if (!atomic_inc_unless_negative(&sd->s_active))
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (likely(!sysfs_ignore_lockdep(sd)))
 | 
						if (sd->s_flags & SYSFS_FLAG_LOCKDEP)
 | 
				
			||||||
		rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_);
 | 
							rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_);
 | 
				
			||||||
	return sd;
 | 
						return sd;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -169,7 +169,7 @@ void sysfs_put_active(struct sysfs_dirent *sd)
 | 
				
			||||||
	if (unlikely(!sd))
 | 
						if (unlikely(!sd))
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (likely(!sysfs_ignore_lockdep(sd)))
 | 
						if (sd->s_flags & SYSFS_FLAG_LOCKDEP)
 | 
				
			||||||
		rwsem_release(&sd->dep_map, 1, _RET_IP_);
 | 
							rwsem_release(&sd->dep_map, 1, _RET_IP_);
 | 
				
			||||||
	v = atomic_dec_return(&sd->s_active);
 | 
						v = atomic_dec_return(&sd->s_active);
 | 
				
			||||||
	if (likely(v != SD_DEACTIVATED_BIAS))
 | 
						if (likely(v != SD_DEACTIVATED_BIAS))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,7 +58,7 @@ static struct sysfs_open_file *sysfs_of(struct file *file)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static const struct kernfs_ops *kernfs_ops(struct sysfs_dirent *sd)
 | 
					static const struct kernfs_ops *kernfs_ops(struct sysfs_dirent *sd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (!sysfs_ignore_lockdep(sd))
 | 
						if (sd->s_flags & SYSFS_FLAG_LOCKDEP)
 | 
				
			||||||
		lockdep_assert_held(sd);
 | 
							lockdep_assert_held(sd);
 | 
				
			||||||
	return sd->s_attr.ops;
 | 
						return sd->s_attr.ops;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -71,7 +71,7 @@ static const struct sysfs_ops *sysfs_file_ops(struct sysfs_dirent *sd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct kobject *kobj = sd->s_parent->priv;
 | 
						struct kobject *kobj = sd->s_parent->priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!sysfs_ignore_lockdep(sd))
 | 
						if (sd->s_flags & SYSFS_FLAG_LOCKDEP)
 | 
				
			||||||
		lockdep_assert_held(sd);
 | 
							lockdep_assert_held(sd);
 | 
				
			||||||
	return kobj->ktype ? kobj->ktype->sysfs_ops : NULL;
 | 
						return kobj->ktype ? kobj->ktype->sysfs_ops : NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -942,6 +942,7 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd,
 | 
				
			||||||
			   const struct attribute *attr, bool is_bin,
 | 
								   const struct attribute *attr, bool is_bin,
 | 
				
			||||||
			   umode_t mode, const void *ns)
 | 
								   umode_t mode, const void *ns)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct lock_class_key *key = NULL;
 | 
				
			||||||
	const struct kernfs_ops *ops;
 | 
						const struct kernfs_ops *ops;
 | 
				
			||||||
	struct sysfs_dirent *sd;
 | 
						struct sysfs_dirent *sd;
 | 
				
			||||||
	loff_t size;
 | 
						loff_t size;
 | 
				
			||||||
| 
						 | 
					@ -981,8 +982,12 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd,
 | 
				
			||||||
		size = battr->size;
 | 
							size = battr->size;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sd = kernfs_create_file_ns(dir_sd, attr->name, mode, size,
 | 
					#ifdef CONFIG_DEBUG_LOCK_ALLOC
 | 
				
			||||||
				   ops, (void *)attr, ns);
 | 
						if (!attr->ignore_lockdep)
 | 
				
			||||||
 | 
							key = attr->key ?: (struct lock_class_key *)&attr->skey;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						sd = kernfs_create_file_ns_key(dir_sd, attr->name, mode, size,
 | 
				
			||||||
 | 
									       ops, (void *)attr, ns, key);
 | 
				
			||||||
	if (IS_ERR(sd)) {
 | 
						if (IS_ERR(sd)) {
 | 
				
			||||||
		if (PTR_ERR(sd) == -EEXIST)
 | 
							if (PTR_ERR(sd) == -EEXIST)
 | 
				
			||||||
			sysfs_warn_dup(dir_sd, attr->name);
 | 
								sysfs_warn_dup(dir_sd, attr->name);
 | 
				
			||||||
| 
						 | 
					@ -992,7 +997,7 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * kernfs_create_file_ns - create a file
 | 
					 * kernfs_create_file_ns_key - create a file
 | 
				
			||||||
 * @parent: directory to create the file in
 | 
					 * @parent: directory to create the file in
 | 
				
			||||||
 * @name: name of the file
 | 
					 * @name: name of the file
 | 
				
			||||||
 * @mode: mode of the file
 | 
					 * @mode: mode of the file
 | 
				
			||||||
| 
						 | 
					@ -1000,14 +1005,16 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd,
 | 
				
			||||||
 * @ops: kernfs operations for the file
 | 
					 * @ops: kernfs operations for the file
 | 
				
			||||||
 * @priv: private data for the file
 | 
					 * @priv: private data for the file
 | 
				
			||||||
 * @ns: optional namespace tag of the file
 | 
					 * @ns: optional namespace tag of the file
 | 
				
			||||||
 | 
					 * @key: lockdep key for the file's active_ref, %NULL to disable lockdep
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Returns the created node on success, ERR_PTR() value on error.
 | 
					 * Returns the created node on success, ERR_PTR() value on error.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct sysfs_dirent *kernfs_create_file_ns(struct sysfs_dirent *parent,
 | 
					struct sysfs_dirent *kernfs_create_file_ns_key(struct sysfs_dirent *parent,
 | 
				
			||||||
					       const char *name,
 | 
										       const char *name,
 | 
				
			||||||
					       umode_t mode, loff_t size,
 | 
										       umode_t mode, loff_t size,
 | 
				
			||||||
					       const struct kernfs_ops *ops,
 | 
										       const struct kernfs_ops *ops,
 | 
				
			||||||
					   void *priv, const void *ns)
 | 
										       void *priv, const void *ns,
 | 
				
			||||||
 | 
										       struct lock_class_key *key)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct sysfs_addrm_cxt acxt;
 | 
						struct sysfs_addrm_cxt acxt;
 | 
				
			||||||
	struct sysfs_dirent *sd;
 | 
						struct sysfs_dirent *sd;
 | 
				
			||||||
| 
						 | 
					@ -1022,7 +1029,13 @@ struct sysfs_dirent *kernfs_create_file_ns(struct sysfs_dirent *parent,
 | 
				
			||||||
	sd->s_attr.size = size;
 | 
						sd->s_attr.size = size;
 | 
				
			||||||
	sd->s_ns = ns;
 | 
						sd->s_ns = ns;
 | 
				
			||||||
	sd->priv = priv;
 | 
						sd->priv = priv;
 | 
				
			||||||
	sysfs_dirent_init_lockdep(sd);
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_DEBUG_LOCK_ALLOC
 | 
				
			||||||
 | 
						if (key) {
 | 
				
			||||||
 | 
							lockdep_init_map(&sd->dep_map, "s_active", key, 0);
 | 
				
			||||||
 | 
							sd->s_flags |= SYSFS_FLAG_LOCKDEP;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * sd->s_attr.ops is accesible only while holding active ref.  We
 | 
						 * sd->s_attr.ops is accesible only while holding active ref.  We
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -92,43 +92,13 @@ struct sysfs_dirent {
 | 
				
			||||||
#define SYSFS_FLAG_NS			0x0020
 | 
					#define SYSFS_FLAG_NS			0x0020
 | 
				
			||||||
#define SYSFS_FLAG_HAS_SEQ_SHOW		0x0040
 | 
					#define SYSFS_FLAG_HAS_SEQ_SHOW		0x0040
 | 
				
			||||||
#define SYSFS_FLAG_HAS_MMAP		0x0080
 | 
					#define SYSFS_FLAG_HAS_MMAP		0x0080
 | 
				
			||||||
 | 
					#define SYSFS_FLAG_LOCKDEP		0x0100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
 | 
					static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return sd->s_flags & SYSFS_TYPE_MASK;
 | 
						return sd->s_flags & SYSFS_TYPE_MASK;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define sysfs_dirent_init_lockdep(sd)				\
 | 
					 | 
				
			||||||
do {								\
 | 
					 | 
				
			||||||
	struct attribute *attr = sd->priv;			\
 | 
					 | 
				
			||||||
	struct lock_class_key *key = attr->key;			\
 | 
					 | 
				
			||||||
	if (!key)						\
 | 
					 | 
				
			||||||
		key = &attr->skey;				\
 | 
					 | 
				
			||||||
								\
 | 
					 | 
				
			||||||
	lockdep_init_map(&sd->dep_map, "s_active", key, 0);	\
 | 
					 | 
				
			||||||
} while (0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Test for attributes that want to ignore lockdep for read-locking */
 | 
					 | 
				
			||||||
static inline bool sysfs_ignore_lockdep(struct sysfs_dirent *sd)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct attribute *attr = sd->priv;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return sysfs_type(sd) == SYSFS_KOBJ_ATTR && attr->ignore_lockdep;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define sysfs_dirent_init_lockdep(sd) do {} while (0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline bool sysfs_ignore_lockdep(struct sysfs_dirent *sd)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Context structure to be used while adding/removing nodes.
 | 
					 * Context structure to be used while adding/removing nodes.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,7 @@
 | 
				
			||||||
#include <linux/err.h>
 | 
					#include <linux/err.h>
 | 
				
			||||||
#include <linux/list.h>
 | 
					#include <linux/list.h>
 | 
				
			||||||
#include <linux/mutex.h>
 | 
					#include <linux/mutex.h>
 | 
				
			||||||
 | 
					#include <linux/lockdep.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct file;
 | 
					struct file;
 | 
				
			||||||
struct iattr;
 | 
					struct iattr;
 | 
				
			||||||
| 
						 | 
					@ -62,6 +63,10 @@ struct kernfs_ops {
 | 
				
			||||||
			 loff_t off);
 | 
								 loff_t off);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int (*mmap)(struct sysfs_open_file *of, struct vm_area_struct *vma);
 | 
						int (*mmap)(struct sysfs_open_file *of, struct vm_area_struct *vma);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_DEBUG_LOCK_ALLOC
 | 
				
			||||||
 | 
						struct lock_class_key	lockdep_key;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_SYSFS
 | 
					#ifdef CONFIG_SYSFS
 | 
				
			||||||
| 
						 | 
					@ -69,11 +74,12 @@ struct kernfs_ops {
 | 
				
			||||||
struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent,
 | 
					struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent,
 | 
				
			||||||
					  const char *name, void *priv,
 | 
										  const char *name, void *priv,
 | 
				
			||||||
					  const void *ns);
 | 
										  const void *ns);
 | 
				
			||||||
struct sysfs_dirent *kernfs_create_file_ns(struct sysfs_dirent *parent,
 | 
					struct sysfs_dirent *kernfs_create_file_ns_key(struct sysfs_dirent *parent,
 | 
				
			||||||
					       const char *name,
 | 
										       const char *name,
 | 
				
			||||||
					       umode_t mode, loff_t size,
 | 
										       umode_t mode, loff_t size,
 | 
				
			||||||
					       const struct kernfs_ops *ops,
 | 
										       const struct kernfs_ops *ops,
 | 
				
			||||||
					   void *priv, const void *ns);
 | 
										       void *priv, const void *ns,
 | 
				
			||||||
 | 
										       struct lock_class_key *key);
 | 
				
			||||||
struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent,
 | 
					struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent,
 | 
				
			||||||
					const char *name,
 | 
										const char *name,
 | 
				
			||||||
					struct sysfs_dirent *target);
 | 
										struct sysfs_dirent *target);
 | 
				
			||||||
| 
						 | 
					@ -94,9 +100,10 @@ kernfs_create_dir_ns(struct sysfs_dirent *parent, const char *name, void *priv,
 | 
				
			||||||
{ return ERR_PTR(-ENOSYS); }
 | 
					{ return ERR_PTR(-ENOSYS); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline struct sysfs_dirent *
 | 
					static inline struct sysfs_dirent *
 | 
				
			||||||
kernfs_create_file_ns(struct sysfs_dirent *parent, const char *name,
 | 
					kernfs_create_file_ns_key(struct sysfs_dirent *parent, const char *name,
 | 
				
			||||||
		      umode_t mode, loff_t size, const struct kernfs_ops *ops,
 | 
								  umode_t mode, loff_t size,
 | 
				
			||||||
		      void *priv, const void *ns)
 | 
								  const struct kernfs_ops *ops, void *priv,
 | 
				
			||||||
 | 
								  const void *ns, struct lock_class_key *key)
 | 
				
			||||||
{ return ERR_PTR(-ENOSYS); }
 | 
					{ return ERR_PTR(-ENOSYS); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline struct sysfs_dirent *
 | 
					static inline struct sysfs_dirent *
 | 
				
			||||||
| 
						 | 
					@ -131,6 +138,20 @@ kernfs_create_dir(struct sysfs_dirent *parent, const char *name, void *priv)
 | 
				
			||||||
	return kernfs_create_dir_ns(parent, name, priv, NULL);
 | 
						return kernfs_create_dir_ns(parent, name, priv, NULL);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline struct sysfs_dirent *
 | 
				
			||||||
 | 
					kernfs_create_file_ns(struct sysfs_dirent *parent, const char *name,
 | 
				
			||||||
 | 
							      umode_t mode, loff_t size, const struct kernfs_ops *ops,
 | 
				
			||||||
 | 
							      void *priv, const void *ns)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct lock_class_key *key = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_DEBUG_LOCK_ALLOC
 | 
				
			||||||
 | 
						key = (struct lock_class_key *)&ops->lockdep_key;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						return kernfs_create_file_ns_key(parent, name, mode, size, ops, priv,
 | 
				
			||||||
 | 
										 ns, key);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline struct sysfs_dirent *
 | 
					static inline struct sysfs_dirent *
 | 
				
			||||||
kernfs_create_file(struct sysfs_dirent *parent, const char *name, umode_t mode,
 | 
					kernfs_create_file(struct sysfs_dirent *parent, const char *name, umode_t mode,
 | 
				
			||||||
		   loff_t size, const struct kernfs_ops *ops, void *priv)
 | 
							   loff_t size, const struct kernfs_ops *ops, void *priv)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue