diff options
author | Jussi Kivilinna <jussi.kivilinna@mbnet.fi> | 2011-01-31 13:47:08 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-02-04 16:29:48 -0500 |
commit | 78fc800f06a72c25842e585fd747fa6a98f3f0e5 (patch) | |
tree | 2c71d432229c54b0efb3103f04815c9f74e9ca1b | |
parent | 681d119047761cc59a15c0bb86891f3a878997cf (diff) |
zd1211rw: use urb anchors for tx and fix tx-queue disabling
When stress testing AP-mode I hit OOPS when unpluging or rmmodding
driver.
It appears that when tx-queue is disabled, tx-urbs might be left pending.
These can cause ehci to call non-existing tx_urb_complete() (after rmmod)
or uninitialized/reseted private structure (after disconnect()). Add skb
queue for submitted packets and unlink pending urbs on zd_usb_disable_tx().
Part of the problem seems to be usb->free_urb_list that isn't always
working as it should, causing machine freeze when trying to free the list
in zd_usb_disable_tx(). Caching free urbs isn't what other drivers seem
to be doing (usbnet for example) so strip free_usb_list.
Patch makes tx-urb handling saner with use of urb anchors.
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/zd1211rw/zd_usb.c | 108 | ||||
-rw-r--r-- | drivers/net/wireless/zd1211rw/zd_usb.h | 8 |
2 files changed, 41 insertions, 75 deletions
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index 06041cb1c422..c32a2472eb44 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c | |||
@@ -779,19 +779,20 @@ void zd_usb_disable_tx(struct zd_usb *usb) | |||
779 | { | 779 | { |
780 | struct zd_usb_tx *tx = &usb->tx; | 780 | struct zd_usb_tx *tx = &usb->tx; |
781 | unsigned long flags; | 781 | unsigned long flags; |
782 | struct list_head *pos, *n; | 782 | |
783 | atomic_set(&tx->enabled, 0); | ||
784 | |||
785 | /* kill all submitted tx-urbs */ | ||
786 | usb_kill_anchored_urbs(&tx->submitted); | ||
783 | 787 | ||
784 | spin_lock_irqsave(&tx->lock, flags); | 788 | spin_lock_irqsave(&tx->lock, flags); |
785 | list_for_each_safe(pos, n, &tx->free_urb_list) { | 789 | WARN_ON(tx->submitted_urbs != 0); |
786 | list_del(pos); | ||
787 | usb_free_urb(list_entry(pos, struct urb, urb_list)); | ||
788 | } | ||
789 | tx->enabled = 0; | ||
790 | tx->submitted_urbs = 0; | 790 | tx->submitted_urbs = 0; |
791 | spin_unlock_irqrestore(&tx->lock, flags); | ||
792 | |||
791 | /* The stopped state is ignored, relying on ieee80211_wake_queues() | 793 | /* The stopped state is ignored, relying on ieee80211_wake_queues() |
792 | * in a potentionally following zd_usb_enable_tx(). | 794 | * in a potentionally following zd_usb_enable_tx(). |
793 | */ | 795 | */ |
794 | spin_unlock_irqrestore(&tx->lock, flags); | ||
795 | } | 796 | } |
796 | 797 | ||
797 | /** | 798 | /** |
@@ -807,63 +808,13 @@ void zd_usb_enable_tx(struct zd_usb *usb) | |||
807 | struct zd_usb_tx *tx = &usb->tx; | 808 | struct zd_usb_tx *tx = &usb->tx; |
808 | 809 | ||
809 | spin_lock_irqsave(&tx->lock, flags); | 810 | spin_lock_irqsave(&tx->lock, flags); |
810 | tx->enabled = 1; | 811 | atomic_set(&tx->enabled, 1); |
811 | tx->submitted_urbs = 0; | 812 | tx->submitted_urbs = 0; |
812 | ieee80211_wake_queues(zd_usb_to_hw(usb)); | 813 | ieee80211_wake_queues(zd_usb_to_hw(usb)); |
813 | tx->stopped = 0; | 814 | tx->stopped = 0; |
814 | spin_unlock_irqrestore(&tx->lock, flags); | 815 | spin_unlock_irqrestore(&tx->lock, flags); |
815 | } | 816 | } |
816 | 817 | ||
817 | /** | ||
818 | * alloc_tx_urb - provides an tx URB | ||
819 | * @usb: a &struct zd_usb pointer | ||
820 | * | ||
821 | * Allocates a new URB. If possible takes the urb from the free list in | ||
822 | * usb->tx. | ||
823 | */ | ||
824 | static struct urb *alloc_tx_urb(struct zd_usb *usb) | ||
825 | { | ||
826 | struct zd_usb_tx *tx = &usb->tx; | ||
827 | unsigned long flags; | ||
828 | struct list_head *entry; | ||
829 | struct urb *urb; | ||
830 | |||
831 | spin_lock_irqsave(&tx->lock, flags); | ||
832 | if (list_empty(&tx->free_urb_list)) { | ||
833 | urb = usb_alloc_urb(0, GFP_ATOMIC); | ||
834 | goto out; | ||
835 | } | ||
836 | entry = tx->free_urb_list.next; | ||
837 | list_del(entry); | ||
838 | urb = list_entry(entry, struct urb, urb_list); | ||
839 | out: | ||
840 | spin_unlock_irqrestore(&tx->lock, flags); | ||
841 | return urb; | ||
842 | } | ||
843 | |||
844 | /** | ||
845 | * free_tx_urb - frees a used tx URB | ||
846 | * @usb: a &struct zd_usb pointer | ||
847 | * @urb: URB to be freed | ||
848 | * | ||
849 | * Frees the transmission URB, which means to put it on the free URB | ||
850 | * list. | ||
851 | */ | ||
852 | static void free_tx_urb(struct zd_usb *usb, struct urb *urb) | ||
853 | { | ||
854 | struct zd_usb_tx *tx = &usb->tx; | ||
855 | unsigned long flags; | ||
856 | |||
857 | spin_lock_irqsave(&tx->lock, flags); | ||
858 | if (!tx->enabled) { | ||
859 | usb_free_urb(urb); | ||
860 | goto out; | ||
861 | } | ||
862 | list_add(&urb->urb_list, &tx->free_urb_list); | ||
863 | out: | ||
864 | spin_unlock_irqrestore(&tx->lock, flags); | ||
865 | } | ||
866 | |||
867 | static void tx_dec_submitted_urbs(struct zd_usb *usb) | 818 | static void tx_dec_submitted_urbs(struct zd_usb *usb) |
868 | { | 819 | { |
869 | struct zd_usb_tx *tx = &usb->tx; | 820 | struct zd_usb_tx *tx = &usb->tx; |
@@ -905,6 +856,16 @@ static void tx_urb_complete(struct urb *urb) | |||
905 | struct sk_buff *skb; | 856 | struct sk_buff *skb; |
906 | struct ieee80211_tx_info *info; | 857 | struct ieee80211_tx_info *info; |
907 | struct zd_usb *usb; | 858 | struct zd_usb *usb; |
859 | struct zd_usb_tx *tx; | ||
860 | |||
861 | skb = (struct sk_buff *)urb->context; | ||
862 | info = IEEE80211_SKB_CB(skb); | ||
863 | /* | ||
864 | * grab 'usb' pointer before handing off the skb (since | ||
865 | * it might be freed by zd_mac_tx_to_dev or mac80211) | ||
866 | */ | ||
867 | usb = &zd_hw_mac(info->rate_driver_data[0])->chip.usb; | ||
868 | tx = &usb->tx; | ||
908 | 869 | ||
909 | switch (urb->status) { | 870 | switch (urb->status) { |
910 | case 0: | 871 | case 0: |
@@ -922,20 +883,15 @@ static void tx_urb_complete(struct urb *urb) | |||
922 | goto resubmit; | 883 | goto resubmit; |
923 | } | 884 | } |
924 | free_urb: | 885 | free_urb: |
925 | skb = (struct sk_buff *)urb->context; | ||
926 | /* | ||
927 | * grab 'usb' pointer before handing off the skb (since | ||
928 | * it might be freed by zd_mac_tx_to_dev or mac80211) | ||
929 | */ | ||
930 | info = IEEE80211_SKB_CB(skb); | ||
931 | usb = &zd_hw_mac(info->rate_driver_data[0])->chip.usb; | ||
932 | zd_mac_tx_to_dev(skb, urb->status); | 886 | zd_mac_tx_to_dev(skb, urb->status); |
933 | free_tx_urb(usb, urb); | 887 | usb_free_urb(urb); |
934 | tx_dec_submitted_urbs(usb); | 888 | tx_dec_submitted_urbs(usb); |
935 | return; | 889 | return; |
936 | resubmit: | 890 | resubmit: |
891 | usb_anchor_urb(urb, &tx->submitted); | ||
937 | r = usb_submit_urb(urb, GFP_ATOMIC); | 892 | r = usb_submit_urb(urb, GFP_ATOMIC); |
938 | if (r) { | 893 | if (r) { |
894 | usb_unanchor_urb(urb); | ||
939 | dev_dbg_f(urb_dev(urb), "error resubmit urb %p %d\n", urb, r); | 895 | dev_dbg_f(urb_dev(urb), "error resubmit urb %p %d\n", urb, r); |
940 | goto free_urb; | 896 | goto free_urb; |
941 | } | 897 | } |
@@ -958,8 +914,14 @@ int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb) | |||
958 | int r; | 914 | int r; |
959 | struct usb_device *udev = zd_usb_to_usbdev(usb); | 915 | struct usb_device *udev = zd_usb_to_usbdev(usb); |
960 | struct urb *urb; | 916 | struct urb *urb; |
917 | struct zd_usb_tx *tx = &usb->tx; | ||
961 | 918 | ||
962 | urb = alloc_tx_urb(usb); | 919 | if (!atomic_read(&tx->enabled)) { |
920 | r = -ENOENT; | ||
921 | goto out; | ||
922 | } | ||
923 | |||
924 | urb = usb_alloc_urb(0, GFP_ATOMIC); | ||
963 | if (!urb) { | 925 | if (!urb) { |
964 | r = -ENOMEM; | 926 | r = -ENOMEM; |
965 | goto out; | 927 | goto out; |
@@ -968,13 +930,16 @@ int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb) | |||
968 | usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT), | 930 | usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT), |
969 | skb->data, skb->len, tx_urb_complete, skb); | 931 | skb->data, skb->len, tx_urb_complete, skb); |
970 | 932 | ||
933 | usb_anchor_urb(urb, &tx->submitted); | ||
971 | r = usb_submit_urb(urb, GFP_ATOMIC); | 934 | r = usb_submit_urb(urb, GFP_ATOMIC); |
972 | if (r) | 935 | if (r) { |
936 | usb_unanchor_urb(urb); | ||
973 | goto error; | 937 | goto error; |
938 | } | ||
974 | tx_inc_submitted_urbs(usb); | 939 | tx_inc_submitted_urbs(usb); |
975 | return 0; | 940 | return 0; |
976 | error: | 941 | error: |
977 | free_tx_urb(usb, urb); | 942 | usb_free_urb(urb); |
978 | out: | 943 | out: |
979 | return r; | 944 | return r; |
980 | } | 945 | } |
@@ -1005,9 +970,9 @@ static inline void init_usb_tx(struct zd_usb *usb) | |||
1005 | { | 970 | { |
1006 | struct zd_usb_tx *tx = &usb->tx; | 971 | struct zd_usb_tx *tx = &usb->tx; |
1007 | spin_lock_init(&tx->lock); | 972 | spin_lock_init(&tx->lock); |
1008 | tx->enabled = 0; | 973 | atomic_set(&tx->enabled, 0); |
1009 | tx->stopped = 0; | 974 | tx->stopped = 0; |
1010 | INIT_LIST_HEAD(&tx->free_urb_list); | 975 | init_usb_anchor(&tx->submitted); |
1011 | tx->submitted_urbs = 0; | 976 | tx->submitted_urbs = 0; |
1012 | } | 977 | } |
1013 | 978 | ||
@@ -1240,6 +1205,7 @@ static void disconnect(struct usb_interface *intf) | |||
1240 | ieee80211_unregister_hw(hw); | 1205 | ieee80211_unregister_hw(hw); |
1241 | 1206 | ||
1242 | /* Just in case something has gone wrong! */ | 1207 | /* Just in case something has gone wrong! */ |
1208 | zd_usb_disable_tx(usb); | ||
1243 | zd_usb_disable_rx(usb); | 1209 | zd_usb_disable_rx(usb); |
1244 | zd_usb_disable_int(usb); | 1210 | zd_usb_disable_int(usb); |
1245 | 1211 | ||
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zd1211rw/zd_usb.h index 1b1655cb7cb4..233ce825b71c 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.h +++ b/drivers/net/wireless/zd1211rw/zd_usb.h | |||
@@ -184,18 +184,18 @@ struct zd_usb_rx { | |||
184 | 184 | ||
185 | /** | 185 | /** |
186 | * struct zd_usb_tx - structure used for transmitting frames | 186 | * struct zd_usb_tx - structure used for transmitting frames |
187 | * @enabled: atomic enabled flag, indicates whether tx is enabled | ||
187 | * @lock: lock for transmission | 188 | * @lock: lock for transmission |
188 | * @free_urb_list: list of free URBs, contains all the URBs, which can be used | 189 | * @submitted: anchor for URBs sent to device |
189 | * @submitted_urbs: atomic integer that counts the URBs having sent to the | 190 | * @submitted_urbs: atomic integer that counts the URBs having sent to the |
190 | * device, which haven't been completed | 191 | * device, which haven't been completed |
191 | * @enabled: enabled flag, indicates whether tx is enabled | ||
192 | * @stopped: indicates whether higher level tx queues are stopped | 192 | * @stopped: indicates whether higher level tx queues are stopped |
193 | */ | 193 | */ |
194 | struct zd_usb_tx { | 194 | struct zd_usb_tx { |
195 | atomic_t enabled; | ||
195 | spinlock_t lock; | 196 | spinlock_t lock; |
196 | struct list_head free_urb_list; | 197 | struct usb_anchor submitted; |
197 | int submitted_urbs; | 198 | int submitted_urbs; |
198 | int enabled; | ||
199 | int stopped; | 199 | int stopped; |
200 | }; | 200 | }; |
201 | 201 | ||