diff options
Diffstat (limited to 'fs/sysfs/bin.c')
-rw-r--r-- | fs/sysfs/bin.c | 95 |
1 files changed, 62 insertions, 33 deletions
diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index 5dc47fe5de5e..618b8aea6a7b 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c | |||
@@ -23,6 +23,7 @@ | |||
23 | struct bin_buffer { | 23 | struct bin_buffer { |
24 | struct mutex mutex; | 24 | struct mutex mutex; |
25 | void *buffer; | 25 | void *buffer; |
26 | int mmapped; | ||
26 | }; | 27 | }; |
27 | 28 | ||
28 | static int | 29 | static int |
@@ -30,12 +31,20 @@ fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) | |||
30 | { | 31 | { |
31 | struct sysfs_dirent *attr_sd = dentry->d_fsdata; | 32 | struct sysfs_dirent *attr_sd = dentry->d_fsdata; |
32 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; | 33 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; |
33 | struct kobject * kobj = to_kobj(dentry->d_parent); | 34 | struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; |
35 | int rc; | ||
36 | |||
37 | /* need attr_sd for attr, its parent for kobj */ | ||
38 | if (!sysfs_get_active_two(attr_sd)) | ||
39 | return -ENODEV; | ||
34 | 40 | ||
35 | if (!attr->read) | 41 | rc = -EIO; |
36 | return -EIO; | 42 | if (attr->read) |
43 | rc = attr->read(kobj, buffer, off, count); | ||
37 | 44 | ||
38 | return attr->read(kobj, buffer, off, count); | 45 | sysfs_put_active_two(attr_sd); |
46 | |||
47 | return rc; | ||
39 | } | 48 | } |
40 | 49 | ||
41 | static ssize_t | 50 | static ssize_t |
@@ -79,12 +88,20 @@ flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count) | |||
79 | { | 88 | { |
80 | struct sysfs_dirent *attr_sd = dentry->d_fsdata; | 89 | struct sysfs_dirent *attr_sd = dentry->d_fsdata; |
81 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; | 90 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; |
82 | struct kobject *kobj = to_kobj(dentry->d_parent); | 91 | struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; |
92 | int rc; | ||
93 | |||
94 | /* need attr_sd for attr, its parent for kobj */ | ||
95 | if (!sysfs_get_active_two(attr_sd)) | ||
96 | return -ENODEV; | ||
83 | 97 | ||
84 | if (!attr->write) | 98 | rc = -EIO; |
85 | return -EIO; | 99 | if (attr->write) |
100 | rc = attr->write(kobj, buffer, offset, count); | ||
86 | 101 | ||
87 | return attr->write(kobj, buffer, offset, count); | 102 | sysfs_put_active_two(attr_sd); |
103 | |||
104 | return rc; | ||
88 | } | 105 | } |
89 | 106 | ||
90 | static ssize_t write(struct file *file, const char __user *userbuf, | 107 | static ssize_t write(struct file *file, const char __user *userbuf, |
@@ -124,14 +141,24 @@ static int mmap(struct file *file, struct vm_area_struct *vma) | |||
124 | struct bin_buffer *bb = file->private_data; | 141 | struct bin_buffer *bb = file->private_data; |
125 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | 142 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
126 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; | 143 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; |
127 | struct kobject *kobj = to_kobj(file->f_path.dentry->d_parent); | 144 | struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; |
128 | int rc; | 145 | int rc; |
129 | 146 | ||
130 | if (!attr->mmap) | ||
131 | return -EINVAL; | ||
132 | |||
133 | mutex_lock(&bb->mutex); | 147 | mutex_lock(&bb->mutex); |
134 | rc = attr->mmap(kobj, attr, vma); | 148 | |
149 | /* need attr_sd for attr, its parent for kobj */ | ||
150 | if (!sysfs_get_active_two(attr_sd)) | ||
151 | return -ENODEV; | ||
152 | |||
153 | rc = -EINVAL; | ||
154 | if (attr->mmap) | ||
155 | rc = attr->mmap(kobj, attr, vma); | ||
156 | |||
157 | if (rc == 0 && !bb->mmapped) | ||
158 | bb->mmapped = 1; | ||
159 | else | ||
160 | sysfs_put_active_two(attr_sd); | ||
161 | |||
135 | mutex_unlock(&bb->mutex); | 162 | mutex_unlock(&bb->mutex); |
136 | 163 | ||
137 | return rc; | 164 | return rc; |
@@ -139,58 +166,60 @@ static int mmap(struct file *file, struct vm_area_struct *vma) | |||
139 | 166 | ||
140 | static int open(struct inode * inode, struct file * file) | 167 | static int open(struct inode * inode, struct file * file) |
141 | { | 168 | { |
142 | struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent); | ||
143 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | 169 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
144 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; | 170 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; |
145 | struct bin_buffer *bb = NULL; | 171 | struct bin_buffer *bb = NULL; |
146 | int error = -EINVAL; | 172 | int error; |
147 | 173 | ||
148 | if (!kobj || !attr) | 174 | /* need attr_sd for attr */ |
149 | goto Done; | 175 | if (!sysfs_get_active(attr_sd)) |
176 | return -ENODEV; | ||
150 | 177 | ||
151 | /* Grab the module reference for this attribute if we have one */ | 178 | /* Grab the module reference for this attribute */ |
152 | error = -ENODEV; | 179 | error = -ENODEV; |
153 | if (!try_module_get(attr->attr.owner)) | 180 | if (!try_module_get(attr->attr.owner)) |
154 | goto Done; | 181 | goto err_sput; |
155 | 182 | ||
156 | error = -EACCES; | 183 | error = -EACCES; |
157 | if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap)) | 184 | if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap)) |
158 | goto Error; | 185 | goto err_mput; |
159 | if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap)) | 186 | if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap)) |
160 | goto Error; | 187 | goto err_mput; |
161 | 188 | ||
162 | error = -ENOMEM; | 189 | error = -ENOMEM; |
163 | bb = kzalloc(sizeof(*bb), GFP_KERNEL); | 190 | bb = kzalloc(sizeof(*bb), GFP_KERNEL); |
164 | if (!bb) | 191 | if (!bb) |
165 | goto Error; | 192 | goto err_mput; |
166 | 193 | ||
167 | bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); | 194 | bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); |
168 | if (!bb->buffer) | 195 | if (!bb->buffer) |
169 | goto Error; | 196 | goto err_mput; |
170 | 197 | ||
171 | mutex_init(&bb->mutex); | 198 | mutex_init(&bb->mutex); |
172 | file->private_data = bb; | 199 | file->private_data = bb; |
173 | 200 | ||
174 | error = 0; | 201 | /* open succeeded, put active reference and pin attr_sd */ |
175 | goto Done; | 202 | sysfs_put_active(attr_sd); |
203 | sysfs_get(attr_sd); | ||
204 | return 0; | ||
176 | 205 | ||
177 | Error: | 206 | err_mput: |
178 | kfree(bb); | ||
179 | module_put(attr->attr.owner); | 207 | module_put(attr->attr.owner); |
180 | Done: | 208 | err_sput: |
181 | if (error) | 209 | sysfs_put_active(attr_sd); |
182 | kobject_put(kobj); | 210 | kfree(bb); |
183 | return error; | 211 | return error; |
184 | } | 212 | } |
185 | 213 | ||
186 | static int release(struct inode * inode, struct file * file) | 214 | static int release(struct inode * inode, struct file * file) |
187 | { | 215 | { |
188 | struct kobject * kobj = to_kobj(file->f_path.dentry->d_parent); | ||
189 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | 216 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
190 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; | 217 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; |
191 | struct bin_buffer *bb = file->private_data; | 218 | struct bin_buffer *bb = file->private_data; |
192 | 219 | ||
193 | kobject_put(kobj); | 220 | if (bb->mmapped) |
221 | sysfs_put_active_two(attr_sd); | ||
222 | sysfs_put(attr_sd); | ||
194 | module_put(attr->attr.owner); | 223 | module_put(attr->attr.owner); |
195 | kfree(bb->buffer); | 224 | kfree(bb->buffer); |
196 | kfree(bb); | 225 | kfree(bb); |