diff options
author | Johan Hovold <jhovold@gmail.com> | 2012-10-25 04:29:19 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-10-25 12:37:13 -0400 |
commit | f525c05babc7938cc1d4236550fd8a659fb05960 (patch) | |
tree | bf5923b4ddf04ddd25ee199e4728c9d9c4de1c52 | |
parent | 084817d79399ab5ccab2f90a148b0369912a8369 (diff) |
USB: sierra: fix port-data memory leak
Fix port-data memory leak by moving port data allocation and
deallocation to port_probe and port_remove.
Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no
driver is bound) the port private data is no longer freed at release as
it is no longer accessible.
Note also that urb-count for multi-port interfaces has not been changed
even though the usb-serial port number is now determined from the port
and interface minor numbers.
Compile-only tested.
Cc: <stable@vger.kernel.org>
Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/usb/serial/sierra.c | 125 |
1 files changed, 58 insertions, 67 deletions
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index bb2ecaf3296b..270860f6bb2a 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c | |||
@@ -878,12 +878,7 @@ static void sierra_dtr_rts(struct usb_serial_port *port, int on) | |||
878 | 878 | ||
879 | static int sierra_startup(struct usb_serial *serial) | 879 | static int sierra_startup(struct usb_serial *serial) |
880 | { | 880 | { |
881 | struct usb_serial_port *port; | ||
882 | struct sierra_intf_private *intfdata; | 881 | struct sierra_intf_private *intfdata; |
883 | struct sierra_port_private *portdata; | ||
884 | struct sierra_iface_info *himemoryp = NULL; | ||
885 | int i; | ||
886 | u8 ifnum; | ||
887 | 882 | ||
888 | intfdata = kzalloc(sizeof(*intfdata), GFP_KERNEL); | 883 | intfdata = kzalloc(sizeof(*intfdata), GFP_KERNEL); |
889 | if (!intfdata) | 884 | if (!intfdata) |
@@ -900,77 +895,71 @@ static int sierra_startup(struct usb_serial *serial) | |||
900 | if (nmea) | 895 | if (nmea) |
901 | sierra_vsc_set_nmea(serial->dev, 1); | 896 | sierra_vsc_set_nmea(serial->dev, 1); |
902 | 897 | ||
903 | /* Now setup per port private data */ | 898 | return 0; |
904 | for (i = 0; i < serial->num_ports; i++) { | 899 | } |
905 | port = serial->port[i]; | 900 | |
906 | portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); | 901 | static void sierra_release(struct usb_serial *serial) |
907 | if (!portdata) { | 902 | { |
908 | dev_dbg(&port->dev, "%s: kmalloc for " | 903 | struct sierra_intf_private *intfdata; |
909 | "sierra_port_private (%d) failed!\n", | 904 | |
910 | __func__, i); | 905 | intfdata = usb_get_serial_data(serial); |
911 | goto err; | 906 | kfree(intfdata); |
912 | } | 907 | } |
913 | spin_lock_init(&portdata->lock); | 908 | |
914 | init_usb_anchor(&portdata->active); | 909 | static int sierra_port_probe(struct usb_serial_port *port) |
915 | init_usb_anchor(&portdata->delayed); | 910 | { |
916 | ifnum = i; | 911 | struct usb_serial *serial = port->serial; |
917 | /* Assume low memory requirements */ | 912 | struct sierra_port_private *portdata; |
918 | portdata->num_out_urbs = N_OUT_URB; | 913 | const struct sierra_iface_info *himemoryp; |
919 | portdata->num_in_urbs = N_IN_URB; | 914 | u8 ifnum; |
920 | 915 | ||
921 | /* Determine actual memory requirements */ | 916 | portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); |
922 | if (serial->num_ports == 1) { | 917 | if (!portdata) |
923 | /* Get interface number for composite device */ | 918 | return -ENOMEM; |
924 | ifnum = sierra_calc_interface(serial); | 919 | |
925 | himemoryp = | 920 | spin_lock_init(&portdata->lock); |
926 | (struct sierra_iface_info *)&typeB_interface_list; | 921 | init_usb_anchor(&portdata->active); |
927 | if (is_himemory(ifnum, himemoryp)) { | 922 | init_usb_anchor(&portdata->delayed); |
928 | portdata->num_out_urbs = N_OUT_URB_HM; | 923 | |
929 | portdata->num_in_urbs = N_IN_URB_HM; | 924 | /* Assume low memory requirements */ |
930 | } | 925 | portdata->num_out_urbs = N_OUT_URB; |
931 | } | 926 | portdata->num_in_urbs = N_IN_URB; |
932 | else { | 927 | |
933 | himemoryp = | 928 | /* Determine actual memory requirements */ |
934 | (struct sierra_iface_info *)&typeA_interface_list; | 929 | if (serial->num_ports == 1) { |
935 | if (is_himemory(i, himemoryp)) { | 930 | /* Get interface number for composite device */ |
936 | portdata->num_out_urbs = N_OUT_URB_HM; | 931 | ifnum = sierra_calc_interface(serial); |
937 | portdata->num_in_urbs = N_IN_URB_HM; | 932 | himemoryp = &typeB_interface_list; |
938 | } | 933 | } else { |
939 | } | 934 | /* This is really the usb-serial port number of the interface |
940 | dev_dbg(&serial->dev->dev, | 935 | * rather than the interface number. |
941 | "Memory usage (urbs) interface #%d, in=%d, out=%d\n", | 936 | */ |
942 | ifnum,portdata->num_in_urbs, portdata->num_out_urbs ); | 937 | ifnum = port->number - serial->minor; |
943 | /* Set the port private data pointer */ | 938 | himemoryp = &typeA_interface_list; |
944 | usb_set_serial_port_data(port, portdata); | ||
945 | } | 939 | } |
946 | 940 | ||
947 | return 0; | 941 | if (is_himemory(ifnum, himemoryp)) { |
948 | err: | 942 | portdata->num_out_urbs = N_OUT_URB_HM; |
949 | for (--i; i >= 0; --i) { | 943 | portdata->num_in_urbs = N_IN_URB_HM; |
950 | portdata = usb_get_serial_port_data(serial->port[i]); | ||
951 | kfree(portdata); | ||
952 | } | 944 | } |
953 | kfree(intfdata); | ||
954 | 945 | ||
955 | return -ENOMEM; | 946 | dev_dbg(&port->dev, |
947 | "Memory usage (urbs) interface #%d, in=%d, out=%d\n", | ||
948 | ifnum, portdata->num_in_urbs, portdata->num_out_urbs); | ||
949 | |||
950 | usb_set_serial_port_data(port, portdata); | ||
951 | |||
952 | return 0; | ||
956 | } | 953 | } |
957 | 954 | ||
958 | static void sierra_release(struct usb_serial *serial) | 955 | static int sierra_port_remove(struct usb_serial_port *port) |
959 | { | 956 | { |
960 | int i; | ||
961 | struct usb_serial_port *port; | ||
962 | struct sierra_port_private *portdata; | 957 | struct sierra_port_private *portdata; |
963 | 958 | ||
964 | for (i = 0; i < serial->num_ports; ++i) { | 959 | portdata = usb_get_serial_port_data(port); |
965 | port = serial->port[i]; | 960 | kfree(portdata); |
966 | if (!port) | 961 | |
967 | continue; | 962 | return 0; |
968 | portdata = usb_get_serial_port_data(port); | ||
969 | if (!portdata) | ||
970 | continue; | ||
971 | kfree(portdata); | ||
972 | } | ||
973 | kfree(serial->private); | ||
974 | } | 963 | } |
975 | 964 | ||
976 | #ifdef CONFIG_PM | 965 | #ifdef CONFIG_PM |
@@ -1074,6 +1063,8 @@ static struct usb_serial_driver sierra_device = { | |||
1074 | .tiocmset = sierra_tiocmset, | 1063 | .tiocmset = sierra_tiocmset, |
1075 | .attach = sierra_startup, | 1064 | .attach = sierra_startup, |
1076 | .release = sierra_release, | 1065 | .release = sierra_release, |
1066 | .port_probe = sierra_port_probe, | ||
1067 | .port_remove = sierra_port_remove, | ||
1077 | .suspend = sierra_suspend, | 1068 | .suspend = sierra_suspend, |
1078 | .resume = sierra_resume, | 1069 | .resume = sierra_resume, |
1079 | .read_int_callback = sierra_instat_callback, | 1070 | .read_int_callback = sierra_instat_callback, |