diff options
| author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2010-08-26 03:15:30 -0400 |
|---|---|---|
| committer | Matthew Garrett <mjg@redhat.com> | 2010-10-21 09:36:48 -0400 |
| commit | c64eefd48c44fa8145ad1f96edabf4a053fffc49 (patch) | |
| tree | 6956b6d86c7253d1cd52233c3818d3041787405b /drivers/platform | |
| parent | 614ef4322200086447d5e1f79e8876213c94f499 (diff) | |
WMI: embed struct device directly into wmi_block
Instead of creating wmi_blocks and then register corresponding devices
on a separate pass do it all in one shot, since lifetime rules for both
objects are the same. This also takes care of leaking devices when
device_create fails for one of them.
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Signed-off-by: Matthew Garrett <mjg@redhat.com>
Diffstat (limited to 'drivers/platform')
| -rw-r--r-- | drivers/platform/x86/wmi.c | 176 |
1 files changed, 65 insertions, 111 deletions
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index b9a60a0aabfd..104b77c87ef5 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c | |||
| @@ -68,7 +68,7 @@ struct wmi_block { | |||
| 68 | acpi_handle handle; | 68 | acpi_handle handle; |
| 69 | wmi_notify_handler handler; | 69 | wmi_notify_handler handler; |
| 70 | void *handler_data; | 70 | void *handler_data; |
| 71 | struct device *dev; | 71 | struct device dev; |
| 72 | }; | 72 | }; |
| 73 | 73 | ||
| 74 | 74 | ||
| @@ -110,7 +110,7 @@ static struct acpi_driver acpi_wmi_driver = { | |||
| 110 | .add = acpi_wmi_add, | 110 | .add = acpi_wmi_add, |
| 111 | .remove = acpi_wmi_remove, | 111 | .remove = acpi_wmi_remove, |
| 112 | .notify = acpi_wmi_notify, | 112 | .notify = acpi_wmi_notify, |
| 113 | }, | 113 | }, |
| 114 | }; | 114 | }; |
| 115 | 115 | ||
| 116 | /* | 116 | /* |
| @@ -693,7 +693,9 @@ static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) | |||
| 693 | 693 | ||
| 694 | static void wmi_dev_free(struct device *dev) | 694 | static void wmi_dev_free(struct device *dev) |
| 695 | { | 695 | { |
| 696 | kfree(dev); | 696 | struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev); |
| 697 | |||
| 698 | kfree(wmi_block); | ||
| 697 | } | 699 | } |
| 698 | 700 | ||
| 699 | static struct class wmi_class = { | 701 | static struct class wmi_class = { |
| @@ -703,104 +705,60 @@ static struct class wmi_class = { | |||
| 703 | .dev_attrs = wmi_dev_attrs, | 705 | .dev_attrs = wmi_dev_attrs, |
| 704 | }; | 706 | }; |
| 705 | 707 | ||
| 706 | static int wmi_create_devs(void) | 708 | static struct wmi_block *wmi_create_device(const struct guid_block *gblock, |
| 709 | acpi_handle handle) | ||
| 707 | { | 710 | { |
| 708 | int result; | ||
| 709 | char guid_string[37]; | ||
| 710 | struct guid_block *gblock; | ||
| 711 | struct wmi_block *wblock; | 711 | struct wmi_block *wblock; |
| 712 | struct list_head *p; | 712 | int error; |
| 713 | struct device *guid_dev; | 713 | char guid_string[37]; |
| 714 | 714 | ||
| 715 | /* Create devices for all the GUIDs */ | 715 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); |
| 716 | list_for_each(p, &wmi_block_list) { | 716 | if (!wblock) { |
| 717 | wblock = list_entry(p, struct wmi_block, list); | 717 | error = -ENOMEM; |
| 718 | goto err_out; | ||
| 719 | } | ||
| 718 | 720 | ||
| 719 | guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL); | 721 | wblock->handle = handle; |
| 720 | if (!guid_dev) | 722 | wblock->gblock = *gblock; |
| 721 | return -ENOMEM; | ||
| 722 | 723 | ||
| 723 | wblock->dev = guid_dev; | 724 | wblock->dev.class = &wmi_class; |
| 724 | 725 | ||
| 725 | guid_dev->class = &wmi_class; | 726 | wmi_gtoa(gblock->guid, guid_string); |
| 726 | dev_set_drvdata(guid_dev, wblock); | 727 | dev_set_name(&wblock->dev, guid_string); |
| 727 | 728 | ||
| 728 | gblock = &wblock->gblock; | 729 | dev_set_drvdata(&wblock->dev, wblock); |
| 729 | 730 | ||
| 730 | wmi_gtoa(gblock->guid, guid_string); | 731 | error = device_register(&wblock->dev); |
| 731 | dev_set_name(guid_dev, guid_string); | 732 | if (error) |
| 733 | goto err_free; | ||
| 732 | 734 | ||
| 733 | result = device_register(guid_dev); | 735 | list_add_tail(&wblock->list, &wmi_block_list); |
| 734 | if (result) | 736 | return wblock; |
| 735 | return result; | ||
| 736 | } | ||
| 737 | 737 | ||
| 738 | return 0; | 738 | err_free: |
| 739 | kfree(wblock); | ||
| 740 | err_out: | ||
| 741 | return ERR_PTR(error); | ||
| 739 | } | 742 | } |
| 740 | 743 | ||
| 741 | static void wmi_remove_devs(void) | 744 | static void wmi_free_devices(void) |
| 742 | { | 745 | { |
| 743 | struct guid_block *gblock; | 746 | struct wmi_block *wblock, *next; |
| 744 | struct wmi_block *wblock; | ||
| 745 | struct list_head *p; | ||
| 746 | struct device *guid_dev; | ||
| 747 | 747 | ||
| 748 | /* Delete devices for all the GUIDs */ | 748 | /* Delete devices for all the GUIDs */ |
| 749 | list_for_each(p, &wmi_block_list) { | 749 | list_for_each_entry_safe(wblock, next, &wmi_block_list, list) |
| 750 | wblock = list_entry(p, struct wmi_block, list); | 750 | device_unregister(&wblock->dev); |
| 751 | |||
| 752 | guid_dev = wblock->dev; | ||
| 753 | gblock = &wblock->gblock; | ||
| 754 | |||
| 755 | device_unregister(guid_dev); | ||
| 756 | } | ||
| 757 | } | ||
| 758 | |||
| 759 | static void wmi_class_exit(void) | ||
| 760 | { | ||
| 761 | wmi_remove_devs(); | ||
| 762 | class_unregister(&wmi_class); | ||
| 763 | } | ||
| 764 | |||
| 765 | static int wmi_class_init(void) | ||
| 766 | { | ||
| 767 | int ret; | ||
| 768 | |||
| 769 | ret = class_register(&wmi_class); | ||
| 770 | if (ret) | ||
| 771 | return ret; | ||
| 772 | |||
| 773 | ret = wmi_create_devs(); | ||
| 774 | if (ret) | ||
| 775 | wmi_class_exit(); | ||
| 776 | |||
| 777 | return ret; | ||
| 778 | } | 751 | } |
| 779 | 752 | ||
| 780 | static bool guid_already_parsed(const char *guid_string) | 753 | static bool guid_already_parsed(const char *guid_string) |
| 781 | { | 754 | { |
| 782 | struct guid_block *gblock; | ||
| 783 | struct wmi_block *wblock; | 755 | struct wmi_block *wblock; |
| 784 | struct list_head *p; | ||
| 785 | |||
| 786 | list_for_each(p, &wmi_block_list) { | ||
| 787 | wblock = list_entry(p, struct wmi_block, list); | ||
| 788 | gblock = &wblock->gblock; | ||
| 789 | 756 | ||
| 790 | if (strncmp(gblock->guid, guid_string, 16) == 0) | 757 | list_for_each_entry(wblock, &wmi_block_list, list) |
| 758 | if (strncmp(wblock->gblock.guid, guid_string, 16) == 0) | ||
| 791 | return true; | 759 | return true; |
| 792 | } | ||
| 793 | return false; | ||
| 794 | } | ||
| 795 | 760 | ||
| 796 | static void free_wmi_blocks(void) | 761 | return false; |
| 797 | { | ||
| 798 | struct wmi_block *wblock, *next; | ||
| 799 | |||
| 800 | list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { | ||
| 801 | list_del(&wblock->list); | ||
| 802 | kfree(wblock); | ||
| 803 | } | ||
| 804 | } | 762 | } |
| 805 | 763 | ||
| 806 | /* | 764 | /* |
| @@ -814,19 +772,19 @@ static acpi_status parse_wdg(acpi_handle handle) | |||
| 814 | struct wmi_block *wblock; | 772 | struct wmi_block *wblock; |
| 815 | char guid_string[37]; | 773 | char guid_string[37]; |
| 816 | acpi_status status; | 774 | acpi_status status; |
| 775 | int retval; | ||
| 817 | u32 i, total; | 776 | u32 i, total; |
| 818 | 777 | ||
| 819 | status = acpi_evaluate_object(handle, "_WDG", NULL, &out); | 778 | status = acpi_evaluate_object(handle, "_WDG", NULL, &out); |
| 820 | |||
| 821 | if (ACPI_FAILURE(status)) | 779 | if (ACPI_FAILURE(status)) |
| 822 | return status; | 780 | return -ENXIO; |
| 823 | 781 | ||
| 824 | obj = (union acpi_object *) out.pointer; | 782 | obj = (union acpi_object *) out.pointer; |
| 825 | if (!obj) | 783 | if (!obj) |
| 826 | return AE_ERROR; | 784 | return -ENXIO; |
| 827 | 785 | ||
| 828 | if (obj->type != ACPI_TYPE_BUFFER) { | 786 | if (obj->type != ACPI_TYPE_BUFFER) { |
| 829 | status = AE_ERROR; | 787 | retval = -ENXIO; |
| 830 | goto out_free_pointer; | 788 | goto out_free_pointer; |
| 831 | } | 789 | } |
| 832 | 790 | ||
| @@ -846,31 +804,29 @@ static acpi_status parse_wdg(acpi_handle handle) | |||
| 846 | pr_info("Skipping duplicate GUID %s\n", guid_string); | 804 | pr_info("Skipping duplicate GUID %s\n", guid_string); |
| 847 | continue; | 805 | continue; |
| 848 | } | 806 | } |
| 807 | |||
| 849 | if (debug_dump_wdg) | 808 | if (debug_dump_wdg) |
| 850 | wmi_dump_wdg(&gblock[i]); | 809 | wmi_dump_wdg(&gblock[i]); |
| 851 | 810 | ||
| 852 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); | 811 | wblock = wmi_create_device(&gblock[i], handle); |
| 853 | if (!wblock) { | 812 | if (IS_ERR(wblock)) { |
| 854 | status = AE_NO_MEMORY; | 813 | retval = PTR_ERR(wblock); |
| 855 | goto out_free_pointer; | 814 | wmi_free_devices(); |
| 815 | break; | ||
| 856 | } | 816 | } |
| 857 | 817 | ||
| 858 | wblock->gblock = gblock[i]; | ||
| 859 | wblock->handle = handle; | ||
| 860 | if (debug_event) { | 818 | if (debug_event) { |
| 861 | wblock->handler = wmi_notify_debug; | 819 | wblock->handler = wmi_notify_debug; |
| 862 | wmi_method_enable(wblock, 1); | 820 | wmi_method_enable(wblock, 1); |
| 863 | } | 821 | } |
| 864 | list_add_tail(&wblock->list, &wmi_block_list); | ||
| 865 | } | 822 | } |
| 866 | 823 | ||
| 824 | retval = 0; | ||
| 825 | |||
| 867 | out_free_pointer: | 826 | out_free_pointer: |
| 868 | kfree(out.pointer); | 827 | kfree(out.pointer); |
| 869 | 828 | ||
| 870 | if (ACPI_FAILURE(status)) | 829 | return retval; |
| 871 | free_wmi_blocks(); | ||
| 872 | |||
| 873 | return status; | ||
| 874 | } | 830 | } |
| 875 | 831 | ||
| 876 | /* | 832 | /* |
| @@ -949,6 +905,7 @@ static int acpi_wmi_remove(struct acpi_device *device, int type) | |||
| 949 | { | 905 | { |
| 950 | acpi_remove_address_space_handler(device->handle, | 906 | acpi_remove_address_space_handler(device->handle, |
| 951 | ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); | 907 | ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); |
| 908 | wmi_free_devices(); | ||
| 952 | 909 | ||
| 953 | return 0; | 910 | return 0; |
| 954 | } | 911 | } |
| @@ -956,7 +913,7 @@ static int acpi_wmi_remove(struct acpi_device *device, int type) | |||
| 956 | static int acpi_wmi_add(struct acpi_device *device) | 913 | static int acpi_wmi_add(struct acpi_device *device) |
| 957 | { | 914 | { |
| 958 | acpi_status status; | 915 | acpi_status status; |
| 959 | int result = 0; | 916 | int error; |
| 960 | 917 | ||
| 961 | status = acpi_install_address_space_handler(device->handle, | 918 | status = acpi_install_address_space_handler(device->handle, |
| 962 | ACPI_ADR_SPACE_EC, | 919 | ACPI_ADR_SPACE_EC, |
| @@ -967,47 +924,44 @@ static int acpi_wmi_add(struct acpi_device *device) | |||
| 967 | return -ENODEV; | 924 | return -ENODEV; |
| 968 | } | 925 | } |
| 969 | 926 | ||
| 970 | status = parse_wdg(device->handle); | 927 | error = parse_wdg(device->handle); |
| 971 | if (ACPI_FAILURE(status)) { | 928 | if (error) { |
| 972 | acpi_remove_address_space_handler(device->handle, | 929 | acpi_remove_address_space_handler(device->handle, |
| 973 | ACPI_ADR_SPACE_EC, | 930 | ACPI_ADR_SPACE_EC, |
| 974 | &acpi_wmi_ec_space_handler); | 931 | &acpi_wmi_ec_space_handler); |
| 975 | pr_err("Failed to parse WDG method\n"); | 932 | pr_err("Failed to parse WDG method\n"); |
| 976 | return -ENODEV; | 933 | return error; |
| 977 | } | 934 | } |
| 978 | 935 | ||
| 979 | return result; | 936 | return 0; |
| 980 | } | 937 | } |
| 981 | 938 | ||
| 982 | static int __init acpi_wmi_init(void) | 939 | static int __init acpi_wmi_init(void) |
| 983 | { | 940 | { |
| 984 | int result; | 941 | int error; |
| 985 | 942 | ||
| 986 | if (acpi_disabled) | 943 | if (acpi_disabled) |
| 987 | return -ENODEV; | 944 | return -ENODEV; |
| 988 | 945 | ||
| 989 | result = acpi_bus_register_driver(&acpi_wmi_driver); | 946 | error = class_register(&wmi_class); |
| 990 | if (result < 0) { | 947 | if (error) |
| 991 | pr_err("Error loading mapper\n"); | 948 | return error; |
| 992 | return -ENODEV; | ||
| 993 | } | ||
| 994 | 949 | ||
| 995 | result = wmi_class_init(); | 950 | error = acpi_bus_register_driver(&acpi_wmi_driver); |
| 996 | if (result) { | 951 | if (error) { |
| 997 | acpi_bus_unregister_driver(&acpi_wmi_driver); | 952 | pr_err("Error loading mapper\n"); |
| 998 | return result; | 953 | class_unregister(&wmi_class); |
| 954 | return error; | ||
| 999 | } | 955 | } |
| 1000 | 956 | ||
| 1001 | pr_info("Mapper loaded\n"); | 957 | pr_info("Mapper loaded\n"); |
| 1002 | |||
| 1003 | return 0; | 958 | return 0; |
| 1004 | } | 959 | } |
| 1005 | 960 | ||
| 1006 | static void __exit acpi_wmi_exit(void) | 961 | static void __exit acpi_wmi_exit(void) |
| 1007 | { | 962 | { |
| 1008 | wmi_class_exit(); | ||
| 1009 | acpi_bus_unregister_driver(&acpi_wmi_driver); | 963 | acpi_bus_unregister_driver(&acpi_wmi_driver); |
| 1010 | free_wmi_blocks(); | 964 | class_unregister(&wmi_class); |
| 1011 | 965 | ||
| 1012 | pr_info("Mapper unloaded\n"); | 966 | pr_info("Mapper unloaded\n"); |
| 1013 | } | 967 | } |
