fb6896da37
The original add/remove code had the following problems. * parent's timestamps are updated on dentry instantiation. this is incorrect with reclaimable files. * updating parent's timestamps isn't synchronized. * parent nlink update assumes the inode is accessible which won't be true once directory dentries are made reclaimable. This patch restructures add/remove paths to resolve the above problems. Add/removal are done in the following steps. 1. sysfs_addrm_start() : acquire locks including sysfs_mutex and other resources. 2-a. sysfs_add_one() : add new sd. linking the new sd into the children list is caller's responsibility. 2-b. sysfs_remove_one() : remove a sd. unlinking the sd from the children list is caller's responsibility. 3. sysfs_addrm_finish() : release all resources and clean up. Steps 2-a and/or 2-b can be repeated multiple times. Parent's inode is looked up during sysfs_addrm_start(). If available (always at the moment), it's pinned and nlink is updated as sd's are added and removed. Timestamps are updated during finish if any sd has been added or removed. If parent's inode is not available during start, sysfs_mutex ensures that parent inode is not created till add/remove is complete. All the complexity is contained inside the helper functions. Especially, dentry/inode handling is properly hidden from the rest of sysfs which now mostly operate on sysfs_dirents. As an added bonus, codes which use these helpers to add and remove sysfs_dirents are now more structured and simpler. Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
128 lines
3.9 KiB
C
128 lines
3.9 KiB
C
struct sysfs_elem_dir {
|
|
struct kobject * kobj;
|
|
};
|
|
|
|
struct sysfs_elem_symlink {
|
|
struct sysfs_dirent * target_sd;
|
|
};
|
|
|
|
struct sysfs_elem_attr {
|
|
struct attribute * attr;
|
|
};
|
|
|
|
struct sysfs_elem_bin_attr {
|
|
struct bin_attribute * bin_attr;
|
|
};
|
|
|
|
/*
|
|
* As long as s_count reference is held, the sysfs_dirent itself is
|
|
* accessible. Dereferencing s_elem or any other outer entity
|
|
* requires s_active reference.
|
|
*/
|
|
struct sysfs_dirent {
|
|
atomic_t s_count;
|
|
atomic_t s_active;
|
|
struct sysfs_dirent * s_parent;
|
|
struct sysfs_dirent * s_sibling;
|
|
struct sysfs_dirent * s_children;
|
|
const char * s_name;
|
|
|
|
union {
|
|
struct sysfs_elem_dir dir;
|
|
struct sysfs_elem_symlink symlink;
|
|
struct sysfs_elem_attr attr;
|
|
struct sysfs_elem_bin_attr bin_attr;
|
|
} s_elem;
|
|
|
|
unsigned int s_flags;
|
|
umode_t s_mode;
|
|
ino_t s_ino;
|
|
struct dentry * s_dentry;
|
|
struct iattr * s_iattr;
|
|
atomic_t s_event;
|
|
};
|
|
|
|
#define SD_DEACTIVATED_BIAS INT_MIN
|
|
|
|
struct sysfs_addrm_cxt {
|
|
struct sysfs_dirent *parent_sd;
|
|
struct inode *parent_inode;
|
|
struct sysfs_dirent *removed;
|
|
int cnt;
|
|
};
|
|
|
|
extern struct vfsmount * sysfs_mount;
|
|
extern struct kmem_cache *sysfs_dir_cachep;
|
|
|
|
extern void sysfs_link_sibling(struct sysfs_dirent *sd);
|
|
extern void sysfs_unlink_sibling(struct sysfs_dirent *sd);
|
|
extern struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd);
|
|
extern void sysfs_put_active(struct sysfs_dirent *sd);
|
|
extern struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd);
|
|
extern void sysfs_put_active_two(struct sysfs_dirent *sd);
|
|
extern void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
|
|
struct sysfs_dirent *parent_sd);
|
|
extern void sysfs_add_one(struct sysfs_addrm_cxt *acxt,
|
|
struct sysfs_dirent *sd);
|
|
extern void sysfs_remove_one(struct sysfs_addrm_cxt *acxt,
|
|
struct sysfs_dirent *sd);
|
|
extern int sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt);
|
|
|
|
extern void sysfs_delete_inode(struct inode *inode);
|
|
extern void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode);
|
|
extern struct inode * sysfs_get_inode(struct sysfs_dirent *sd);
|
|
extern void sysfs_instantiate(struct dentry *dentry, struct inode *inode);
|
|
|
|
extern void release_sysfs_dirent(struct sysfs_dirent * sd);
|
|
extern struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
|
|
const unsigned char *name);
|
|
extern struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd,
|
|
const unsigned char *name);
|
|
extern struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode,
|
|
int type);
|
|
|
|
extern int sysfs_add_file(struct sysfs_dirent *dir_sd,
|
|
const struct attribute *attr, int type);
|
|
extern int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name);
|
|
extern struct sysfs_dirent *sysfs_find(struct sysfs_dirent *dir, const char * name);
|
|
|
|
extern int sysfs_create_subdir(struct kobject *kobj, const char *name,
|
|
struct sysfs_dirent **p_sd);
|
|
extern void sysfs_remove_subdir(struct sysfs_dirent *sd);
|
|
|
|
extern void sysfs_drop_dentry(struct sysfs_dirent *sd);
|
|
extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr);
|
|
|
|
extern spinlock_t sysfs_assoc_lock;
|
|
extern struct mutex sysfs_mutex;
|
|
extern struct super_block * sysfs_sb;
|
|
extern const struct file_operations sysfs_dir_operations;
|
|
extern const struct file_operations sysfs_file_operations;
|
|
extern const struct file_operations bin_fops;
|
|
extern const struct inode_operations sysfs_dir_inode_operations;
|
|
extern const struct inode_operations sysfs_symlink_inode_operations;
|
|
|
|
static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
|
|
{
|
|
return sd->s_flags & SYSFS_TYPE_MASK;
|
|
}
|
|
|
|
static inline struct sysfs_dirent * sysfs_get(struct sysfs_dirent * sd)
|
|
{
|
|
if (sd) {
|
|
WARN_ON(!atomic_read(&sd->s_count));
|
|
atomic_inc(&sd->s_count);
|
|
}
|
|
return sd;
|
|
}
|
|
|
|
static inline void sysfs_put(struct sysfs_dirent * sd)
|
|
{
|
|
if (sd && atomic_dec_and_test(&sd->s_count))
|
|
release_sysfs_dirent(sd);
|
|
}
|
|
|
|
static inline int sysfs_is_shadowed_inode(struct inode *inode)
|
|
{
|
|
return S_ISDIR(inode->i_mode) && inode->i_op->follow_link;
|
|
}
|