diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2012-10-21 20:57:19 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-22 01:50:37 -0400 |
commit | 2f0157f13f42800aa3d9017ebb0fb80a65f7b2de (patch) | |
tree | 7b2b3299b6ef785790336042ab28c0bffa37debb /fs/char_dev.c | |
parent | 6f0c0580b70c89094b3422ba81118c7b959c7556 (diff) |
char_dev: pin parent kobject
In certain cases (for example when a cdev structure is embedded into
another object whose lifetime is controlled by a separate kobject) it is
beneficial to tie lifetime of another object to the lifetime of
character device so that related object is not freed until after
char_dev object is freed.
To achieve this let's pin kobject's parent when doing cdev_add() and
unpin when last reference to cdev structure is being released.
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Acked-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/char_dev.c')
-rw-r--r-- | fs/char_dev.c | 18 |
1 files changed, 17 insertions, 1 deletions
diff --git a/fs/char_dev.c b/fs/char_dev.c index 3f152b92a94a..afc2bb691780 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c | |||
@@ -471,9 +471,19 @@ static int exact_lock(dev_t dev, void *data) | |||
471 | */ | 471 | */ |
472 | int cdev_add(struct cdev *p, dev_t dev, unsigned count) | 472 | int cdev_add(struct cdev *p, dev_t dev, unsigned count) |
473 | { | 473 | { |
474 | int error; | ||
475 | |||
474 | p->dev = dev; | 476 | p->dev = dev; |
475 | p->count = count; | 477 | p->count = count; |
476 | return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p); | 478 | |
479 | error = kobj_map(cdev_map, dev, count, NULL, | ||
480 | exact_match, exact_lock, p); | ||
481 | if (error) | ||
482 | return error; | ||
483 | |||
484 | kobject_get(p->kobj.parent); | ||
485 | |||
486 | return 0; | ||
477 | } | 487 | } |
478 | 488 | ||
479 | static void cdev_unmap(dev_t dev, unsigned count) | 489 | static void cdev_unmap(dev_t dev, unsigned count) |
@@ -498,14 +508,20 @@ void cdev_del(struct cdev *p) | |||
498 | static void cdev_default_release(struct kobject *kobj) | 508 | static void cdev_default_release(struct kobject *kobj) |
499 | { | 509 | { |
500 | struct cdev *p = container_of(kobj, struct cdev, kobj); | 510 | struct cdev *p = container_of(kobj, struct cdev, kobj); |
511 | struct kobject *parent = kobj->parent; | ||
512 | |||
501 | cdev_purge(p); | 513 | cdev_purge(p); |
514 | kobject_put(parent); | ||
502 | } | 515 | } |
503 | 516 | ||
504 | static void cdev_dynamic_release(struct kobject *kobj) | 517 | static void cdev_dynamic_release(struct kobject *kobj) |
505 | { | 518 | { |
506 | struct cdev *p = container_of(kobj, struct cdev, kobj); | 519 | struct cdev *p = container_of(kobj, struct cdev, kobj); |
520 | struct kobject *parent = kobj->parent; | ||
521 | |||
507 | cdev_purge(p); | 522 | cdev_purge(p); |
508 | kfree(p); | 523 | kfree(p); |
524 | kobject_put(parent); | ||
509 | } | 525 | } |
510 | 526 | ||
511 | static struct kobj_type ktype_cdev_default = { | 527 | static struct kobj_type ktype_cdev_default = { |