aboutsummaryrefslogtreecommitdiffstats
path: root/package/kernel/mac80211/patches/319-0001-brcmfmac-fix-lockup-when-removing-P2P-interface-afte.patch
blob: 073d012dea37ebfc34ffac98797c5eebc48a6c6d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
Date: Fri, 17 Jun 2016 12:29:21 +0200
Subject: [PATCH] brcmfmac: fix lockup when removing P2P interface after
 event timeout
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Removing P2P interface is handled by sending a proper request to the
firmware. On success firmware triggers an event and driver's handler
removes a matching interface.

However on event timeout we remove interface directly from the cfg80211
callback. Current code doesn't handle this case correctly as it always
assumes rtnl to be unlocked.

Fix it by adding an extra rtnl_locked parameter to functions and calling
unregister_netdevice when needed.

Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---

--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
@@ -548,12 +548,16 @@ fail:
 	return -EBADE;
 }
 
-static void brcmf_net_detach(struct net_device *ndev)
+static void brcmf_net_detach(struct net_device *ndev, bool rtnl_locked)
 {
-	if (ndev->reg_state == NETREG_REGISTERED)
-		unregister_netdev(ndev);
-	else
+	if (ndev->reg_state == NETREG_REGISTERED) {
+		if (rtnl_locked)
+			unregister_netdevice(ndev);
+		else
+			unregister_netdev(ndev);
+	} else {
 		brcmf_cfg80211_free_netdev(ndev);
+	}
 }
 
 void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on)
@@ -651,7 +655,7 @@ struct brcmf_if *brcmf_add_if(struct brc
 			brcmf_err("ERROR: netdev:%s already exists\n",
 				  ifp->ndev->name);
 			netif_stop_queue(ifp->ndev);
-			brcmf_net_detach(ifp->ndev);
+			brcmf_net_detach(ifp->ndev, false);
 			drvr->iflist[bsscfgidx] = NULL;
 		} else {
 			brcmf_dbg(INFO, "netdev:%s ignore IF event\n",
@@ -699,7 +703,8 @@ struct brcmf_if *brcmf_add_if(struct brc
 	return ifp;
 }
 
-static void brcmf_del_if(struct brcmf_pub *drvr, s32 bsscfgidx)
+static void brcmf_del_if(struct brcmf_pub *drvr, s32 bsscfgidx,
+			 bool rtnl_locked)
 {
 	struct brcmf_if *ifp;
 
@@ -729,7 +734,7 @@ static void brcmf_del_if(struct brcmf_pu
 			cancel_work_sync(&ifp->multicast_work);
 			cancel_work_sync(&ifp->ndoffload_work);
 		}
-		brcmf_net_detach(ifp->ndev);
+		brcmf_net_detach(ifp->ndev, rtnl_locked);
 	} else {
 		/* Only p2p device interfaces which get dynamically created
 		 * end up here. In this case the p2p module should be informed
@@ -743,14 +748,14 @@ static void brcmf_del_if(struct brcmf_pu
 	}
 }
 
-void brcmf_remove_interface(struct brcmf_if *ifp)
+void brcmf_remove_interface(struct brcmf_if *ifp, bool rtnl_locked)
 {
 	if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bsscfgidx] != ifp))
 		return;
 	brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", ifp->bsscfgidx,
 		  ifp->ifidx);
 	brcmf_fws_del_interface(ifp);
-	brcmf_del_if(ifp->drvr, ifp->bsscfgidx);
+	brcmf_del_if(ifp->drvr, ifp->bsscfgidx, rtnl_locked);
 }
 
 #ifdef CONFIG_INET
@@ -1057,9 +1062,9 @@ fail:
 		brcmf_fws_deinit(drvr);
 	}
 	if (ifp)
-		brcmf_net_detach(ifp->ndev);
+		brcmf_net_detach(ifp->ndev, false);
 	if (p2p_ifp)
-		brcmf_net_detach(p2p_ifp->ndev);
+		brcmf_net_detach(p2p_ifp->ndev, false);
 	drvr->iflist[0] = NULL;
 	drvr->iflist[1] = NULL;
 	if (drvr->settings->ignore_probe_fail)
@@ -1128,7 +1133,7 @@ void brcmf_detach(struct device *dev)
 
 	/* make sure primary interface removed last */
 	for (i = BRCMF_MAX_IFS-1; i > -1; i--)
-		brcmf_remove_interface(drvr->iflist[i]);
+		brcmf_remove_interface(drvr->iflist[i], false);
 
 	brcmf_cfg80211_detach(drvr->config);
 
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
@@ -216,7 +216,7 @@ struct brcmf_if *brcmf_get_ifp(struct br
 int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
 struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx,
 			      bool is_p2pdev, char *name, u8 *mac_addr);
-void brcmf_remove_interface(struct brcmf_if *ifp);
+void brcmf_remove_interface(struct brcmf_if *ifp, bool rtnl_locked);
 void brcmf_txflowblock_if(struct brcmf_if *ifp,
 			  enum brcmf_netif_stop_reason reason, bool state);
 void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success);
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
@@ -183,7 +183,7 @@ static void brcmf_fweh_handle_if_event(s
 	err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data);
 
 	if (ifp && ifevent->action == BRCMF_E_IF_DEL)
-		brcmf_remove_interface(ifp);
+		brcmf_remove_interface(ifp, false);
 }
 
 /**
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
@@ -2289,7 +2289,7 @@ int brcmf_p2p_del_vif(struct wiphy *wiph
 			err = 0;
 	}
 	if (err)
-		brcmf_remove_interface(vif->ifp);
+		brcmf_remove_interface(vif->ifp, true);
 
 	brcmf_cfg80211_arm_vif_event(cfg, NULL);
 	if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE)
@@ -2395,7 +2395,7 @@ void brcmf_p2p_detach(struct brcmf_p2p_i
 	if (vif != NULL) {
 		brcmf_p2p_cancel_remain_on_channel(vif->ifp);
 		brcmf_p2p_deinit_discovery(p2p);
-		brcmf_remove_interface(vif->ifp);
+		brcmf_remove_interface(vif->ifp, false);
 	}
 	/* just set it all to zero */
 	memset(p2p, 0, sizeof(*p2p));
>(dom, dom->start_info_pfn, 1); struct xen_ia64_boot_param_ia64 *bp = (struct xen_ia64_boot_param_ia64 *)(start_info + 1); xc_dom_printf("%s\n", __FUNCTION__); memset(start_info, 0, sizeof(*start_info)); sprintf(start_info->magic, dom->guest_type); start_info->flags = dom->flags; start_info->nr_pages = dom->total_pages; start_info->store_mfn = dom->xenstore_pfn; start_info->store_evtchn = dom->xenstore_evtchn; start_info->console.domU.mfn = dom->console_pfn; start_info->console.domU.evtchn = dom->console_evtchn; /* * domain_start and domain_size are abused for arch_setup hypercall * so that we need to clear them here. */ XEN_IA64_MEMMAP_INFO_NUM_PAGES(bp) = 0; XEN_IA64_MEMMAP_INFO_PFN(bp) = 0; if ( dom->ramdisk_blob ) { start_info->mod_start = dom->ramdisk_seg.vstart; start_info->mod_len = dom->ramdisk_seg.vend - dom->ramdisk_seg.vstart; bp->initrd_start = start_info->mod_start; bp->initrd_size = start_info->mod_len; } bp->command_line = (dom->start_info_pfn << PAGE_SHIFT_IA64) + offsetof(start_info_t, cmd_line); if ( dom->cmdline ) { strncpy((char *)start_info->cmd_line, dom->cmdline, MAX_GUEST_CMDLINE); start_info->cmd_line[MAX_GUEST_CMDLINE - 1] = '\0'; } return 0; } int shared_info_ia64(struct xc_dom_image *dom, void *ptr) { shared_info_ia64_t *shared_info = ptr; int i; xc_dom_printf("%s: called\n", __FUNCTION__); memset(shared_info, 0, sizeof(*shared_info)); for (i = 0; i < MAX_VIRT_CPUS; i++) shared_info->vcpu_info[i].evtchn_upcall_mask = 1; shared_info->arch.start_info_pfn = dom->start_info_pfn; shared_info->arch.memmap_info_num_pages = 1; //XXX shared_info->arch.memmap_info_pfn = dom->start_info_pfn - 1; return 0; } extern unsigned long xc_ia64_fpsr_default(void); static int vcpu_ia64(struct xc_dom_image *dom, void *ptr) { vcpu_guest_context_ia64_t *ctxt = ptr; xc_dom_printf("%s: called\n", __FUNCTION__); /* clear everything */ memset(ctxt, 0, sizeof(*ctxt)); ctxt->flags = 0; /* PSR is set according to SAL 3.2.4: AC, IC and BN are set. */ ctxt->regs.psr = IA64_PSR_AC | IA64_PSR_IC | IA64_PSR_BN; ctxt->regs.ip = dom->parms.virt_entry; ctxt->regs.cfm = 1UL << 63; #ifdef __ia64__ /* FIXME */ ctxt->regs.ar.fpsr = xc_ia64_fpsr_default(); #endif ctxt->regs.r[28] = (dom->start_info_pfn << PAGE_SHIFT_IA64) + sizeof(start_info_ia64_t); return 0; } /* ------------------------------------------------------------------------ */ static struct xc_dom_arch xc_dom_arch = { .guest_type = "xen-3.0-ia64", .native_protocol = XEN_IO_PROTO_ABI_IA64, .page_shift = PAGE_SHIFT_IA64, .alloc_magic_pages = alloc_magic_pages, .start_info = start_info_ia64, .shared_info = shared_info_ia64, .vcpu = vcpu_ia64, }; static struct xc_dom_arch xc_dom_arch_ia64be = { .guest_type = "xen-3.0-ia64be", .native_protocol = XEN_IO_PROTO_ABI_IA64, .page_shift = PAGE_SHIFT_IA64, .alloc_magic_pages = alloc_magic_pages, .start_info = start_info_ia64, .shared_info = shared_info_ia64, .vcpu = vcpu_ia64, }; static void __init register_arch_hooks(void) { xc_dom_register_arch_hooks(&xc_dom_arch); xc_dom_register_arch_hooks(&xc_dom_arch_ia64be); } #include "xc_efi.h" int arch_setup_meminit(struct xc_dom_image *dom) { xen_pfn_t pfn; int rc; unsigned long start; unsigned long nbr; /* setup initial p2m */ if (dom->guest_type && strcmp(dom->guest_type, "hvm-3.0-ia64-sioemu") == 0) { start = FW_MEM_BASE >> PAGE_SHIFT_IA64; nbr = FW_MEM_SIZE >> PAGE_SHIFT_IA64; } else { start = 0; nbr = dom->total_pages; } /* setup initial p2m */ dom->p2m_host = xc_dom_malloc(dom, sizeof(xen_pfn_t) * nbr); for ( pfn = 0; pfn < nbr; pfn++ ) dom->p2m_host[pfn] = start + pfn; /* allocate guest memory */ rc = xc_domain_memory_populate_physmap(dom->guest_xc, dom->guest_domid, nbr, 0, 0, dom->p2m_host); return rc; } static int ia64_setup_memmap(struct xc_dom_image *dom) { unsigned int page_size = XC_DOM_PAGE_SIZE(dom); unsigned long memmap_info_num_pages; unsigned long memmap_info_pfn; xen_ia64_memmap_info_t* memmap_info; unsigned int num_mds; efi_memory_desc_t *md; char* start_info; struct xen_ia64_boot_param* bp; /* setup memmap page */ memmap_info_num_pages = 1; memmap_info_pfn = dom->start_info_pfn - 1; xc_dom_printf("%s: memmap: mfn 0x%" PRIpfn " pages 0x%lx\n", __FUNCTION__, memmap_info_pfn, memmap_info_num_pages); memmap_info = xc_map_foreign_range(dom->guest_xc, dom->guest_domid, page_size * memmap_info_num_pages, PROT_READ | PROT_WRITE, memmap_info_pfn); if (NULL == memmap_info) return -1; /* [0, total_pages) */ memmap_info->efi_memdesc_size = sizeof(md[0]); memmap_info->efi_memdesc_version = EFI_MEMORY_DESCRIPTOR_VERSION; num_mds = 0; md = (efi_memory_desc_t*)&memmap_info->memdesc; md[num_mds].type = EFI_CONVENTIONAL_MEMORY; md[num_mds].pad = 0; md[num_mds].phys_addr = 0; md[num_mds].virt_addr = 0; md[num_mds].num_pages = dom->total_pages << (PAGE_SHIFT - EFI_PAGE_SHIFT); md[num_mds].attribute = EFI_MEMORY_WB; num_mds++; memmap_info->efi_memmap_size = num_mds * sizeof(md[0]); munmap(memmap_info, page_size * memmap_info_num_pages); assert(num_mds <= (page_size * memmap_info_num_pages - offsetof(typeof(*memmap_info), memdesc))/sizeof(*md)); /* * kludge: we need to pass memmap_info page's pfn and other magic pages * somehow. * we use xen_ia64_boot_param::efi_memmap::{efi_memmap, efi_memmap_size} * for this purpose */ start_info = xc_map_foreign_range(dom->guest_xc, dom->guest_domid, page_size, PROT_READ | PROT_WRITE, dom->start_info_pfn); if (NULL == start_info) return -1; bp = (struct xen_ia64_boot_param*)(start_info + sizeof(start_info_t)); memset(bp, 0, sizeof(*bp)); XEN_IA64_MEMMAP_INFO_NUM_PAGES(bp) = memmap_info_num_pages; XEN_IA64_MEMMAP_INFO_PFN(bp) = memmap_info_pfn; munmap(start_info, page_size); return 0; } int arch_setup_bootearly(struct xc_dom_image *dom) { DECLARE_DOMCTL; int rc; xc_dom_printf("%s: setup firmware for %s\n", __FUNCTION__, dom->guest_type); if (dom->guest_type && strcmp(dom->guest_type, "hvm-3.0-ia64-sioemu") == 0) { memset(&domctl, 0, sizeof(domctl)); domctl.u.arch_setup.flags = XEN_DOMAINSETUP_sioemu_guest; domctl.u.arch_setup.bp = 0; domctl.u.arch_setup.maxmem = 0; domctl.cmd = XEN_DOMCTL_arch_setup; domctl.domain = dom->guest_domid; rc = xc_domctl(dom->guest_xc, &domctl); xc_dom_printf("%s: hvm-3.0-ia64-sioemu: %d\n", __FUNCTION__, rc); return rc; } rc = ia64_setup_memmap(dom); if (rc) return rc; memset(&domctl, 0, sizeof(domctl)); domctl.cmd = XEN_DOMCTL_arch_setup; domctl.domain = dom->guest_domid; domctl.u.arch_setup.flags = XEN_DOMAINSETUP_query; rc = do_domctl(dom->guest_xc, &domctl); if (rc) return rc; rc = xen_ia64_dom_fw_setup(dom, domctl.u.arch_setup.hypercall_imm, (dom->start_info_pfn << PAGE_SHIFT) + sizeof(start_info_t), dom->total_pages << PAGE_SHIFT); if (rc) return rc; memset(&domctl, 0, sizeof(domctl)); domctl.cmd = XEN_DOMCTL_arch_setup; domctl.domain = dom->guest_domid; domctl.u.arch_setup.flags = 0; domctl.u.arch_setup.bp = (dom->start_info_pfn << PAGE_SHIFT) + sizeof(start_info_t); domctl.u.arch_setup.maxmem = dom->total_pages << PAGE_SHIFT; domctl.u.arch_setup.vhpt_size_log2 = dom->vhpt_size_log2; rc = do_domctl(dom->guest_xc, &domctl); return rc; } int arch_setup_bootlate(struct xc_dom_image *dom) { unsigned int page_size = XC_DOM_PAGE_SIZE(dom); shared_info_t *shared_info; /* setup shared_info page */ xc_dom_printf("%s: shared_info: mfn 0x%" PRIpfn "\n", __FUNCTION__, dom->shared_info_mfn); shared_info = xc_map_foreign_range(dom->guest_xc, dom->guest_domid, page_size, PROT_READ | PROT_WRITE, dom->shared_info_mfn); if ( shared_info == NULL ) return -1; dom->arch_hooks->shared_info(dom, shared_info); munmap(shared_info, page_size); return 0; } /* * Local variables: * mode: C * c-set-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */