diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-08-21 16:24:32 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-08-22 13:55:03 -0400 |
commit | 47eba33a0997fc7362ae41cf28cea687e28bd731 (patch) | |
tree | d20f1127549aca2a1add4f9ea02c919cb7549aea /drivers/w1/w1.c | |
parent | 3e1026b3fa2f61d33ce6a9e42a22398cc4ab8e58 (diff) |
w1: remove race with sysfs file creation
W1 slave sysfs files are created _after_ userspace is notified that the
device has been added to the system. Fix that race by moving the
creation/remove of the files to the bus notifier that is there for doing
this type of thing.
Acked-by: Evgeniy Polyakov <zbr@ioremap.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/w1/w1.c')
-rw-r--r-- | drivers/w1/w1.c | 111 |
1 files changed, 73 insertions, 38 deletions
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 0459df843c58..3c798db0152c 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c | |||
@@ -587,6 +587,73 @@ end: | |||
587 | return err; | 587 | return err; |
588 | } | 588 | } |
589 | 589 | ||
590 | /* | ||
591 | * Handle sysfs file creation and removal here, before userspace is told that | ||
592 | * the device is added / removed from the system | ||
593 | */ | ||
594 | static int w1_bus_notify(struct notifier_block *nb, unsigned long action, | ||
595 | void *data) | ||
596 | { | ||
597 | struct device *dev = data; | ||
598 | struct w1_slave *sl; | ||
599 | int err; | ||
600 | |||
601 | /* | ||
602 | * Only care about slave devices at the moment. Yes, we should use a | ||
603 | * separate "type" for this, but for now, look at the release function | ||
604 | * to know which type it is... | ||
605 | */ | ||
606 | if (dev->release != w1_slave_release) | ||
607 | return 0; | ||
608 | |||
609 | sl = dev_to_w1_slave(dev); | ||
610 | |||
611 | switch (action) { | ||
612 | case BUS_NOTIFY_ADD_DEVICE: | ||
613 | /* Create our sysfs files before userspace is told about it */ | ||
614 | /* Create "name" entry */ | ||
615 | err = device_create_file(&sl->dev, &w1_slave_attr_name); | ||
616 | if (err < 0) { | ||
617 | dev_err(&sl->dev, | ||
618 | "sysfs file creation for [%s] failed. err=%d\n", | ||
619 | dev_name(&sl->dev), err); | ||
620 | return err; | ||
621 | } | ||
622 | |||
623 | /* Create "id" entry */ | ||
624 | err = device_create_file(&sl->dev, &w1_slave_attr_id); | ||
625 | if (err < 0) { | ||
626 | dev_err(&sl->dev, | ||
627 | "sysfs file creation for [%s] failed. err=%d\n", | ||
628 | dev_name(&sl->dev), err); | ||
629 | return err; | ||
630 | } | ||
631 | |||
632 | /* if the family driver needs to initialize something... */ | ||
633 | if (sl->family->fops && sl->family->fops->add_slave && | ||
634 | ((err = sl->family->fops->add_slave(sl)) < 0)) { | ||
635 | dev_err(&sl->dev, | ||
636 | "sysfs file creation for [%s] failed. err=%d\n", | ||
637 | dev_name(&sl->dev), err); | ||
638 | return err; | ||
639 | } | ||
640 | |||
641 | break; | ||
642 | case BUS_NOTIFY_DEL_DEVICE: | ||
643 | /* Remove our sysfs files */ | ||
644 | if (sl->family->fops && sl->family->fops->remove_slave) | ||
645 | sl->family->fops->remove_slave(sl); | ||
646 | device_remove_file(&sl->dev, &w1_slave_attr_id); | ||
647 | device_remove_file(&sl->dev, &w1_slave_attr_name); | ||
648 | break; | ||
649 | } | ||
650 | return 0; | ||
651 | } | ||
652 | |||
653 | static struct notifier_block w1_bus_nb = { | ||
654 | .notifier_call = w1_bus_notify, | ||
655 | }; | ||
656 | |||
590 | static int __w1_attach_slave_device(struct w1_slave *sl) | 657 | static int __w1_attach_slave_device(struct w1_slave *sl) |
591 | { | 658 | { |
592 | int err; | 659 | int err; |
@@ -615,44 +682,13 @@ static int __w1_attach_slave_device(struct w1_slave *sl) | |||
615 | return err; | 682 | return err; |
616 | } | 683 | } |
617 | 684 | ||
618 | /* Create "name" entry */ | ||
619 | err = device_create_file(&sl->dev, &w1_slave_attr_name); | ||
620 | if (err < 0) { | ||
621 | dev_err(&sl->dev, | ||
622 | "sysfs file creation for [%s] failed. err=%d\n", | ||
623 | dev_name(&sl->dev), err); | ||
624 | goto out_unreg; | ||
625 | } | ||
626 | |||
627 | /* Create "id" entry */ | ||
628 | err = device_create_file(&sl->dev, &w1_slave_attr_id); | ||
629 | if (err < 0) { | ||
630 | dev_err(&sl->dev, | ||
631 | "sysfs file creation for [%s] failed. err=%d\n", | ||
632 | dev_name(&sl->dev), err); | ||
633 | goto out_rem1; | ||
634 | } | ||
635 | 685 | ||
636 | /* if the family driver needs to initialize something... */ | 686 | dev_set_uevent_suppress(&sl->dev, false); |
637 | if (sl->family->fops && sl->family->fops->add_slave && | 687 | kobject_uevent(&sl->dev.kobj, KOBJ_ADD); |
638 | ((err = sl->family->fops->add_slave(sl)) < 0)) { | ||
639 | dev_err(&sl->dev, | ||
640 | "sysfs file creation for [%s] failed. err=%d\n", | ||
641 | dev_name(&sl->dev), err); | ||
642 | goto out_rem2; | ||
643 | } | ||
644 | 688 | ||
645 | list_add_tail(&sl->w1_slave_entry, &sl->master->slist); | 689 | list_add_tail(&sl->w1_slave_entry, &sl->master->slist); |
646 | 690 | ||
647 | return 0; | 691 | return 0; |
648 | |||
649 | out_rem2: | ||
650 | device_remove_file(&sl->dev, &w1_slave_attr_id); | ||
651 | out_rem1: | ||
652 | device_remove_file(&sl->dev, &w1_slave_attr_name); | ||
653 | out_unreg: | ||
654 | device_unregister(&sl->dev); | ||
655 | return err; | ||
656 | } | 692 | } |
657 | 693 | ||
658 | static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) | 694 | static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) |
@@ -723,16 +759,11 @@ void w1_slave_detach(struct w1_slave *sl) | |||
723 | 759 | ||
724 | list_del(&sl->w1_slave_entry); | 760 | list_del(&sl->w1_slave_entry); |
725 | 761 | ||
726 | if (sl->family->fops && sl->family->fops->remove_slave) | ||
727 | sl->family->fops->remove_slave(sl); | ||
728 | |||
729 | memset(&msg, 0, sizeof(msg)); | 762 | memset(&msg, 0, sizeof(msg)); |
730 | memcpy(msg.id.id, &sl->reg_num, sizeof(msg.id)); | 763 | memcpy(msg.id.id, &sl->reg_num, sizeof(msg.id)); |
731 | msg.type = W1_SLAVE_REMOVE; | 764 | msg.type = W1_SLAVE_REMOVE; |
732 | w1_netlink_send(sl->master, &msg); | 765 | w1_netlink_send(sl->master, &msg); |
733 | 766 | ||
734 | device_remove_file(&sl->dev, &w1_slave_attr_id); | ||
735 | device_remove_file(&sl->dev, &w1_slave_attr_name); | ||
736 | device_unregister(&sl->dev); | 767 | device_unregister(&sl->dev); |
737 | 768 | ||
738 | wait_for_completion(&sl->released); | 769 | wait_for_completion(&sl->released); |
@@ -1017,6 +1048,10 @@ static int __init w1_init(void) | |||
1017 | goto err_out_exit_init; | 1048 | goto err_out_exit_init; |
1018 | } | 1049 | } |
1019 | 1050 | ||
1051 | retval = bus_register_notifier(&w1_bus_type, &w1_bus_nb); | ||
1052 | if (retval) | ||
1053 | goto err_out_bus_unregister; | ||
1054 | |||
1020 | retval = driver_register(&w1_master_driver); | 1055 | retval = driver_register(&w1_master_driver); |
1021 | if (retval) { | 1056 | if (retval) { |
1022 | printk(KERN_ERR | 1057 | printk(KERN_ERR |