aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/sysfs')
-rw-r--r--fs/sysfs/file.c109
-rw-r--r--fs/sysfs/sysfs.h3
2 files changed, 111 insertions, 1 deletions
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index 3c91a57a1ed2..b13ba94cf8ac 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -49,6 +49,22 @@ static struct sysfs_ops subsys_sysfs_ops = {
49 .store = subsys_attr_store, 49 .store = subsys_attr_store,
50}; 50};
51 51
52/*
53 * There's one sysfs_buffer for each open file and one
54 * sysfs_open_dirent for each sysfs_dirent with one or more open
55 * files.
56 *
57 * filp->private_data points to sysfs_buffer and
58 * sysfs_dirent->s_attr.open points to sysfs_open_dirent. s_attr.open
59 * is protected by sysfs_open_dirent_lock.
60 */
61static spinlock_t sysfs_open_dirent_lock = SPIN_LOCK_UNLOCKED;
62
63struct sysfs_open_dirent {
64 atomic_t refcnt;
65 struct list_head buffers; /* goes through sysfs_buffer.list */
66};
67
52struct sysfs_buffer { 68struct sysfs_buffer {
53 size_t count; 69 size_t count;
54 loff_t pos; 70 loff_t pos;
@@ -57,6 +73,7 @@ struct sysfs_buffer {
57 struct mutex mutex; 73 struct mutex mutex;
58 int needs_read_fill; 74 int needs_read_fill;
59 int event; 75 int event;
76 struct list_head list;
60}; 77};
61 78
62/** 79/**
@@ -237,6 +254,86 @@ sysfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t
237 return len; 254 return len;
238} 255}
239 256
257/**
258 * sysfs_get_open_dirent - get or create sysfs_open_dirent
259 * @sd: target sysfs_dirent
260 * @buffer: sysfs_buffer for this instance of open
261 *
262 * If @sd->s_attr.open exists, increment its reference count;
263 * otherwise, create one. @buffer is chained to the buffers
264 * list.
265 *
266 * LOCKING:
267 * Kernel thread context (may sleep).
268 *
269 * RETURNS:
270 * 0 on success, -errno on failure.
271 */
272static int sysfs_get_open_dirent(struct sysfs_dirent *sd,
273 struct sysfs_buffer *buffer)
274{
275 struct sysfs_open_dirent *od, *new_od = NULL;
276
277 retry:
278 spin_lock(&sysfs_open_dirent_lock);
279
280 if (!sd->s_attr.open && new_od) {
281 sd->s_attr.open = new_od;
282 new_od = NULL;
283 }
284
285 od = sd->s_attr.open;
286 if (od) {
287 atomic_inc(&od->refcnt);
288 list_add_tail(&buffer->list, &od->buffers);
289 }
290
291 spin_unlock(&sysfs_open_dirent_lock);
292
293 if (od) {
294 kfree(new_od);
295 return 0;
296 }
297
298 /* not there, initialize a new one and retry */
299 new_od = kmalloc(sizeof(*new_od), GFP_KERNEL);
300 if (!new_od)
301 return -ENOMEM;
302
303 atomic_set(&new_od->refcnt, 0);
304 INIT_LIST_HEAD(&new_od->buffers);
305 goto retry;
306}
307
308/**
309 * sysfs_put_open_dirent - put sysfs_open_dirent
310 * @sd: target sysfs_dirent
311 * @buffer: associated sysfs_buffer
312 *
313 * Put @sd->s_attr.open and unlink @buffer from the buffers list.
314 * If reference count reaches zero, disassociate and free it.
315 *
316 * LOCKING:
317 * None.
318 */
319static void sysfs_put_open_dirent(struct sysfs_dirent *sd,
320 struct sysfs_buffer *buffer)
321{
322 struct sysfs_open_dirent *od = sd->s_attr.open;
323
324 spin_lock(&sysfs_open_dirent_lock);
325
326 list_del(&buffer->list);
327 if (atomic_dec_and_test(&od->refcnt))
328 sd->s_attr.open = NULL;
329 else
330 od = NULL;
331
332 spin_unlock(&sysfs_open_dirent_lock);
333
334 kfree(od);
335}
336
240static int sysfs_open_file(struct inode *inode, struct file *file) 337static int sysfs_open_file(struct inode *inode, struct file *file)
241{ 338{
242 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 339 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
@@ -298,19 +395,29 @@ static int sysfs_open_file(struct inode *inode, struct file *file)
298 buffer->ops = ops; 395 buffer->ops = ops;
299 file->private_data = buffer; 396 file->private_data = buffer;
300 397
398 /* make sure we have open dirent struct */
399 error = sysfs_get_open_dirent(attr_sd, buffer);
400 if (error)
401 goto err_free;
402
301 /* open succeeded, put active references */ 403 /* open succeeded, put active references */
302 sysfs_put_active_two(attr_sd); 404 sysfs_put_active_two(attr_sd);
303 return 0; 405 return 0;
304 406
407 err_free:
408 kfree(buffer);
305 err_out: 409 err_out:
306 sysfs_put_active_two(attr_sd); 410 sysfs_put_active_two(attr_sd);
307 return error; 411 return error;
308} 412}
309 413
310static int sysfs_release(struct inode * inode, struct file * filp) 414static int sysfs_release(struct inode *inode, struct file *filp)
311{ 415{
416 struct sysfs_dirent *sd = filp->f_path.dentry->d_fsdata;
312 struct sysfs_buffer *buffer = filp->private_data; 417 struct sysfs_buffer *buffer = filp->private_data;
313 418
419 sysfs_put_open_dirent(sd, buffer);
420
314 if (buffer->page) 421 if (buffer->page)
315 free_page((unsigned long)buffer->page); 422 free_page((unsigned long)buffer->page);
316 kfree(buffer); 423 kfree(buffer);
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index 42b0327d1624..3adce7d5e4f7 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -1,3 +1,5 @@
1struct sysfs_open_dirent;
2
1/* type-specific structures for sysfs_dirent->s_* union members */ 3/* type-specific structures for sysfs_dirent->s_* union members */
2struct sysfs_elem_dir { 4struct sysfs_elem_dir {
3 struct kobject *kobj; 5 struct kobject *kobj;
@@ -11,6 +13,7 @@ struct sysfs_elem_symlink {
11 13
12struct sysfs_elem_attr { 14struct sysfs_elem_attr {
13 struct attribute *attr; 15 struct attribute *attr;
16 struct sysfs_open_dirent *open;
14}; 17};
15 18
16struct sysfs_elem_bin_attr { 19struct sysfs_elem_bin_attr {