diff options
Diffstat (limited to 'fs/sysfs/dir.c')
| -rw-r--r-- | fs/sysfs/dir.c | 132 | 
1 files changed, 67 insertions, 65 deletions
| diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 699f371b9f12..590717861c7a 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
| @@ -93,7 +93,7 @@ static void sysfs_unlink_sibling(struct sysfs_dirent *sd) | |||
| 93 | * RETURNS: | 93 | * RETURNS: | 
| 94 | * Pointer to @sd on success, NULL on failure. | 94 | * Pointer to @sd on success, NULL on failure. | 
| 95 | */ | 95 | */ | 
| 96 | static struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) | 96 | struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) | 
| 97 | { | 97 | { | 
| 98 | if (unlikely(!sd)) | 98 | if (unlikely(!sd)) | 
| 99 | return NULL; | 99 | return NULL; | 
| @@ -124,7 +124,7 @@ static struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) | |||
| 124 | * Put an active reference to @sd. This function is noop if @sd | 124 | * Put an active reference to @sd. This function is noop if @sd | 
| 125 | * is NULL. | 125 | * is NULL. | 
| 126 | */ | 126 | */ | 
| 127 | static void sysfs_put_active(struct sysfs_dirent *sd) | 127 | void sysfs_put_active(struct sysfs_dirent *sd) | 
| 128 | { | 128 | { | 
| 129 | struct completion *cmpl; | 129 | struct completion *cmpl; | 
| 130 | int v; | 130 | int v; | 
| @@ -145,45 +145,6 @@ static void sysfs_put_active(struct sysfs_dirent *sd) | |||
| 145 | } | 145 | } | 
| 146 | 146 | ||
| 147 | /** | 147 | /** | 
| 148 | * sysfs_get_active_two - get active references to sysfs_dirent and parent | ||
| 149 | * @sd: sysfs_dirent of interest | ||
| 150 | * | ||
| 151 | * Get active reference to @sd and its parent. Parent's active | ||
| 152 | * reference is grabbed first. This function is noop if @sd is | ||
| 153 | * NULL. | ||
| 154 | * | ||
| 155 | * RETURNS: | ||
| 156 | * Pointer to @sd on success, NULL on failure. | ||
| 157 | */ | ||
| 158 | struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd) | ||
| 159 | { | ||
| 160 | if (sd) { | ||
| 161 | if (sd->s_parent && unlikely(!sysfs_get_active(sd->s_parent))) | ||
| 162 | return NULL; | ||
| 163 | if (unlikely(!sysfs_get_active(sd))) { | ||
| 164 | sysfs_put_active(sd->s_parent); | ||
| 165 | return NULL; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | return sd; | ||
| 169 | } | ||
| 170 | |||
| 171 | /** | ||
| 172 | * sysfs_put_active_two - put active references to sysfs_dirent and parent | ||
| 173 | * @sd: sysfs_dirent of interest | ||
| 174 | * | ||
| 175 | * Put active references to @sd and its parent. This function is | ||
| 176 | * noop if @sd is NULL. | ||
| 177 | */ | ||
| 178 | void sysfs_put_active_two(struct sysfs_dirent *sd) | ||
| 179 | { | ||
| 180 | if (sd) { | ||
| 181 | sysfs_put_active(sd); | ||
| 182 | sysfs_put_active(sd->s_parent); | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | /** | ||
| 187 | * sysfs_deactivate - deactivate sysfs_dirent | 148 | * sysfs_deactivate - deactivate sysfs_dirent | 
| 188 | * @sd: sysfs_dirent to deactivate | 149 | * @sd: sysfs_dirent to deactivate | 
| 189 | * | 150 | * | 
| @@ -195,6 +156,10 @@ static void sysfs_deactivate(struct sysfs_dirent *sd) | |||
| 195 | int v; | 156 | int v; | 
| 196 | 157 | ||
| 197 | BUG_ON(sd->s_sibling || !(sd->s_flags & SYSFS_FLAG_REMOVED)); | 158 | BUG_ON(sd->s_sibling || !(sd->s_flags & SYSFS_FLAG_REMOVED)); | 
| 159 | |||
| 160 | if (!(sysfs_type(sd) & SYSFS_ACTIVE_REF)) | ||
| 161 | return; | ||
| 162 | |||
| 198 | sd->s_sibling = (void *)&wait; | 163 | sd->s_sibling = (void *)&wait; | 
| 199 | 164 | ||
| 200 | rwsem_acquire(&sd->dep_map, 0, 0, _RET_IP_); | 165 | rwsem_acquire(&sd->dep_map, 0, 0, _RET_IP_); | 
| @@ -354,7 +319,6 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) | |||
| 354 | 319 | ||
| 355 | atomic_set(&sd->s_count, 1); | 320 | atomic_set(&sd->s_count, 1); | 
| 356 | atomic_set(&sd->s_active, 0); | 321 | atomic_set(&sd->s_active, 0); | 
| 357 | sysfs_dirent_init_lockdep(sd); | ||
| 358 | 322 | ||
| 359 | sd->s_name = name; | 323 | sd->s_name = name; | 
| 360 | sd->s_mode = mode; | 324 | sd->s_mode = mode; | 
| @@ -681,7 +645,7 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, | |||
| 681 | } | 645 | } | 
| 682 | 646 | ||
| 683 | /* attach dentry and inode */ | 647 | /* attach dentry and inode */ | 
| 684 | inode = sysfs_get_inode(sd); | 648 | inode = sysfs_get_inode(dir->i_sb, sd); | 
| 685 | if (!inode) { | 649 | if (!inode) { | 
| 686 | ret = ERR_PTR(-ENOMEM); | 650 | ret = ERR_PTR(-ENOMEM); | 
| 687 | goto out_unlock; | 651 | goto out_unlock; | 
| @@ -837,11 +801,46 @@ static inline unsigned char dt_type(struct sysfs_dirent *sd) | |||
| 837 | return (sd->s_mode >> 12) & 15; | 801 | return (sd->s_mode >> 12) & 15; | 
| 838 | } | 802 | } | 
| 839 | 803 | ||
| 804 | static int sysfs_dir_release(struct inode *inode, struct file *filp) | ||
| 805 | { | ||
| 806 | sysfs_put(filp->private_data); | ||
| 807 | return 0; | ||
| 808 | } | ||
| 809 | |||
| 810 | static struct sysfs_dirent *sysfs_dir_pos(struct sysfs_dirent *parent_sd, | ||
| 811 | ino_t ino, struct sysfs_dirent *pos) | ||
| 812 | { | ||
| 813 | if (pos) { | ||
| 814 | int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) && | ||
| 815 | pos->s_parent == parent_sd && | ||
| 816 | ino == pos->s_ino; | ||
| 817 | sysfs_put(pos); | ||
| 818 | if (valid) | ||
| 819 | return pos; | ||
| 820 | } | ||
| 821 | pos = NULL; | ||
| 822 | if ((ino > 1) && (ino < INT_MAX)) { | ||
| 823 | pos = parent_sd->s_dir.children; | ||
| 824 | while (pos && (ino > pos->s_ino)) | ||
| 825 | pos = pos->s_sibling; | ||
| 826 | } | ||
| 827 | return pos; | ||
| 828 | } | ||
| 829 | |||
| 830 | static struct sysfs_dirent *sysfs_dir_next_pos(struct sysfs_dirent *parent_sd, | ||
| 831 | ino_t ino, struct sysfs_dirent *pos) | ||
| 832 | { | ||
| 833 | pos = sysfs_dir_pos(parent_sd, ino, pos); | ||
| 834 | if (pos) | ||
| 835 | pos = pos->s_sibling; | ||
| 836 | return pos; | ||
| 837 | } | ||
| 838 | |||
| 840 | static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) | 839 | static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) | 
| 841 | { | 840 | { | 
| 842 | struct dentry *dentry = filp->f_path.dentry; | 841 | struct dentry *dentry = filp->f_path.dentry; | 
| 843 | struct sysfs_dirent * parent_sd = dentry->d_fsdata; | 842 | struct sysfs_dirent * parent_sd = dentry->d_fsdata; | 
| 844 | struct sysfs_dirent *pos; | 843 | struct sysfs_dirent *pos = filp->private_data; | 
| 845 | ino_t ino; | 844 | ino_t ino; | 
| 846 | 845 | ||
| 847 | if (filp->f_pos == 0) { | 846 | if (filp->f_pos == 0) { | 
| @@ -857,29 +856,31 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) | |||
| 857 | if (filldir(dirent, "..", 2, filp->f_pos, ino, DT_DIR) == 0) | 856 | if (filldir(dirent, "..", 2, filp->f_pos, ino, DT_DIR) == 0) | 
| 858 | filp->f_pos++; | 857 | filp->f_pos++; | 
| 859 | } | 858 | } | 
| 860 | if ((filp->f_pos > 1) && (filp->f_pos < INT_MAX)) { | 859 | mutex_lock(&sysfs_mutex); | 
| 861 | mutex_lock(&sysfs_mutex); | 860 | for (pos = sysfs_dir_pos(parent_sd, filp->f_pos, pos); | 
| 862 | 861 | pos; | |
| 863 | /* Skip the dentries we have already reported */ | 862 | pos = sysfs_dir_next_pos(parent_sd, filp->f_pos, pos)) { | 
| 864 | pos = parent_sd->s_dir.children; | 863 | const char * name; | 
| 865 | while (pos && (filp->f_pos > pos->s_ino)) | 864 | unsigned int type; | 
| 866 | pos = pos->s_sibling; | 865 | int len, ret; | 
| 867 | 866 | ||
| 868 | for ( ; pos; pos = pos->s_sibling) { | 867 | name = pos->s_name; | 
| 869 | const char * name; | 868 | len = strlen(name); | 
| 870 | int len; | 869 | ino = pos->s_ino; | 
| 871 | 870 | type = dt_type(pos); | |
| 872 | name = pos->s_name; | 871 | filp->f_pos = ino; | 
| 873 | len = strlen(name); | 872 | filp->private_data = sysfs_get(pos); | 
| 874 | filp->f_pos = ino = pos->s_ino; | ||
| 875 | 873 | ||
| 876 | if (filldir(dirent, name, len, filp->f_pos, ino, | ||
| 877 | dt_type(pos)) < 0) | ||
| 878 | break; | ||
| 879 | } | ||
| 880 | if (!pos) | ||
| 881 | filp->f_pos = INT_MAX; | ||
| 882 | mutex_unlock(&sysfs_mutex); | 874 | mutex_unlock(&sysfs_mutex); | 
| 875 | ret = filldir(dirent, name, len, filp->f_pos, ino, type); | ||
| 876 | mutex_lock(&sysfs_mutex); | ||
| 877 | if (ret < 0) | ||
| 878 | break; | ||
| 879 | } | ||
| 880 | mutex_unlock(&sysfs_mutex); | ||
| 881 | if ((filp->f_pos > 1) && !pos) { /* EOF */ | ||
| 882 | filp->f_pos = INT_MAX; | ||
| 883 | filp->private_data = NULL; | ||
| 883 | } | 884 | } | 
| 884 | return 0; | 885 | return 0; | 
| 885 | } | 886 | } | 
| @@ -888,5 +889,6 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) | |||
| 888 | const struct file_operations sysfs_dir_operations = { | 889 | const struct file_operations sysfs_dir_operations = { | 
| 889 | .read = generic_read_dir, | 890 | .read = generic_read_dir, | 
| 890 | .readdir = sysfs_readdir, | 891 | .readdir = sysfs_readdir, | 
| 892 | .release = sysfs_dir_release, | ||
| 891 | .llseek = generic_file_llseek, | 893 | .llseek = generic_file_llseek, | 
| 892 | }; | 894 | }; | 
