Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bf8536e1 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "wil6210: fix locking in wmi_call"

parents 58735f6e ac6c786f
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -2305,7 +2305,7 @@ static int wil_cfg80211_del_station(struct wiphy *wiphy,
		     params->mac, params->reason_code, vif->mid);

	mutex_lock(&wil->mutex);
	wil6210_disconnect(vif, params->mac, params->reason_code, false);
	wil6210_disconnect(vif, params->mac, params->reason_code);
	mutex_unlock(&wil->mutex);

	return 0;
+10 −5
Original line number Diff line number Diff line
@@ -651,10 +651,10 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
	enum { max_count = 4096 };
	struct wil_blob_wrapper *wil_blob = file->private_data;
	struct wil6210_priv *wil = wil_blob->wil;
	loff_t pos = *ppos;
	loff_t aligned_pos, pos = *ppos;
	size_t available = wil_blob->blob.size;
	void *buf;
	size_t ret;
	size_t unaligned_bytes, aligned_count, ret;
	int rc;

	if (test_bit(wil_status_suspending, wil_blob->wil->status) ||
@@ -672,7 +672,12 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
	if (count > max_count)
		count = max_count;

	buf = kmalloc(count, GFP_KERNEL);
	/* set pos to 4 bytes aligned */
	unaligned_bytes = pos % 4;
	aligned_pos = pos - unaligned_bytes;
	aligned_count = count + unaligned_bytes;

	buf = kmalloc(aligned_count, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

@@ -683,9 +688,9 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
	}

	wil_memcpy_fromio_32(buf, (const void __iomem *)
			     wil_blob->blob.data + pos, count);
			     wil_blob->blob.data + aligned_pos, aligned_count);

	ret = copy_to_user(user_buf, buf, count);
	ret = copy_to_user(user_buf, buf + unaligned_bytes, count);

	wil_pm_runtime_put(wil);

+133 −46
Original line number Diff line number Diff line
@@ -204,8 +204,21 @@ static void wil_ring_fini_tx(struct wil6210_priv *wil, int id)
	wil->txrx_ops.ring_fini_tx(wil, ring);
}

static void wil_disconnect_cid(struct wil6210_vif *vif, int cid,
			       u16 reason_code, bool from_event)
static bool wil_vif_is_connected(struct wil6210_priv *wil, u8 mid)
{
	int i;

	for (i = 0; i < WIL6210_MAX_CID; i++) {
		if (wil->sta[i].mid == mid &&
		    wil->sta[i].status == wil_sta_connected)
			return true;
	}

	return false;
}

static void wil_disconnect_cid_complete(struct wil6210_vif *vif, int cid,
					u16 reason_code)
__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
{
	uint i;
@@ -216,24 +229,14 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
	int min_ring_id = wil_get_min_tx_ring_id(wil);

	might_sleep();
	wil_dbg_misc(wil, "disconnect_cid: CID %d, MID %d, status %d\n",
	wil_dbg_misc(wil,
		     "disconnect_cid_complete: CID %d, MID %d, status %d\n",
		     cid, sta->mid, sta->status);
	/* inform upper/lower layers */
	/* inform upper layers */
	if (sta->status != wil_sta_unused) {
		if (vif->mid != sta->mid) {
			wil_err(wil, "STA MID mismatch with VIF MID(%d)\n",
				vif->mid);
			/* let FW override sta->mid but be more strict with
			 * user space requests
			 */
			if (!from_event)
				return;
		}
		if (!from_event) {
			bool del_sta = (wdev->iftype == NL80211_IFTYPE_AP) ?
						disable_ap_sme : false;
			wmi_disconnect_sta(vif, sta->addr, reason_code,
					   true, del_sta);
		}

		switch (wdev->iftype) {
@@ -274,36 +277,20 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
	sta->stats.tx_latency_min_us = U32_MAX;
}

static bool wil_vif_is_connected(struct wil6210_priv *wil, u8 mid)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
		if (wil->sta[i].mid == mid &&
		    wil->sta[i].status == wil_sta_connected)
			return true;
	}

	return false;
}

static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
				u16 reason_code, bool from_event)
static void _wil6210_disconnect_complete(struct wil6210_vif *vif,
					 const u8 *bssid, u16 reason_code)
{
	struct wil6210_priv *wil = vif_to_wil(vif);
	int cid = -ENOENT;
	struct net_device *ndev;
	struct wireless_dev *wdev;

	if (unlikely(!vif))
		return;

	ndev = vif_to_ndev(vif);
	wdev = vif_to_wdev(vif);

	might_sleep();
	wil_info(wil, "bssid=%pM, reason=%d, ev%s\n", bssid,
		 reason_code, from_event ? "+" : "-");
	wil_info(wil, "disconnect_complete: bssid=%pM, reason=%d\n",
		 bssid, reason_code);

	/* Cases are:
	 * - disconnect single STA, still connected
@@ -318,14 +305,15 @@ static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
	if (bssid && !is_broadcast_ether_addr(bssid) &&
	    !ether_addr_equal_unaligned(ndev->dev_addr, bssid)) {
		cid = wil_find_cid(wil, vif->mid, bssid);
		wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n",
		wil_dbg_misc(wil,
			     "Disconnect complete %pM, CID=%d, reason=%d\n",
			     bssid, cid, reason_code);
		if (cid >= 0) /* disconnect 1 peer */
			wil_disconnect_cid(vif, cid, reason_code, from_event);
			wil_disconnect_cid_complete(vif, cid, reason_code);
	} else { /* all */
		wil_dbg_misc(wil, "Disconnect all\n");
		wil_dbg_misc(wil, "Disconnect complete all\n");
		for (cid = 0; cid < WIL6210_MAX_CID; cid++)
			wil_disconnect_cid(vif, cid, reason_code, from_event);
			wil_disconnect_cid_complete(vif, cid, reason_code);
	}

	/* link state */
@@ -371,6 +359,84 @@ static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
	}
}

static int wil_disconnect_cid(struct wil6210_vif *vif, int cid,
			      u16 reason_code)
{
	struct wil6210_priv *wil = vif_to_wil(vif);
	struct wireless_dev *wdev = vif_to_wdev(vif);
	struct wil_sta_info *sta = &wil->sta[cid];
	bool del_sta = false;

	might_sleep();
	wil_dbg_misc(wil, "disconnect_cid: CID %d, MID %d, status %d\n",
		     cid, sta->mid, sta->status);

	if (sta->status == wil_sta_unused)
		return 0;

	if (vif->mid != sta->mid) {
		wil_err(wil, "STA MID mismatch with VIF MID(%d)\n", vif->mid);
		return -EINVAL;
	}

	/* inform lower layers */
	if (wdev->iftype == NL80211_IFTYPE_AP && disable_ap_sme)
		del_sta = true;

	/* disconnect by sending command disconnect/del_sta and wait
	 * synchronously for WMI_DISCONNECT_EVENTID event.
	 */
	return wmi_disconnect_sta(vif, sta->addr, reason_code, del_sta);
}

static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
				u16 reason_code)
{
	struct wil6210_priv *wil;
	struct net_device *ndev;
	struct wireless_dev *wdev;
	int cid = -ENOENT;

	if (unlikely(!vif))
		return;

	wil = vif_to_wil(vif);
	ndev = vif_to_ndev(vif);
	wdev = vif_to_wdev(vif);

	might_sleep();
	wil_info(wil, "disconnect bssid=%pM, reason=%d\n", bssid, reason_code);

	/* Cases are:
	 * - disconnect single STA, still connected
	 * - disconnect single STA, already disconnected
	 * - disconnect all
	 *
	 * For "disconnect all", there are 3 options:
	 * - bssid == NULL
	 * - bssid is broadcast address (ff:ff:ff:ff:ff:ff)
	 * - bssid is our MAC address
	 */
	if (bssid && !is_broadcast_ether_addr(bssid) &&
	    !ether_addr_equal_unaligned(ndev->dev_addr, bssid)) {
		cid = wil_find_cid(wil, vif->mid, bssid);
		wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n",
			     bssid, cid, reason_code);
		if (cid >= 0) /* disconnect 1 peer */
			wil_disconnect_cid(vif, cid, reason_code);
	} else { /* all */
		wil_dbg_misc(wil, "Disconnect all\n");
		for (cid = 0; cid < WIL6210_MAX_CID; cid++)
			wil_disconnect_cid(vif, cid, reason_code);
	}

	/* call event handler manually after processing wmi_call,
	 * to avoid deadlock - disconnect event handler acquires
	 * wil->mutex while it is already held here
	 */
	_wil6210_disconnect_complete(vif, bssid, reason_code);
}

void wil_disconnect_worker(struct work_struct *work)
{
	struct wil6210_vif *vif = container_of(work,
@@ -695,20 +761,41 @@ void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps)
 * @vif: virtual interface context
 * @bssid: peer to disconnect, NULL to disconnect all
 * @reason_code: Reason code for the Disassociation frame
 * @from_event: whether is invoked from FW event handler
 *
 * Disconnect and release associated resources. If invoked not from the
 * FW event handler, issue WMI command(s) to trigger MAC disconnect.
 * Disconnect and release associated resources. Issue WMI
 * command(s) to trigger MAC disconnect. When command was issued
 * successfully, call the wil6210_disconnect_complete function
 * to handle the event synchronously
 */
void wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
			u16 reason_code, bool from_event)
			u16 reason_code)
{
	struct wil6210_priv *wil = vif_to_wil(vif);

	wil_dbg_misc(wil, "disconnecting\n");

	del_timer_sync(&vif->connect_timer);
	_wil6210_disconnect(vif, bssid, reason_code);
}

/**
 * wil6210_disconnect_complete - handle disconnect event
 * @vif: virtual interface context
 * @bssid: peer to disconnect, NULL to disconnect all
 * @reason_code: Reason code for the Disassociation frame
 *
 * Release associated resources and indicate upper layers the
 * connection is terminated.
 */
void wil6210_disconnect_complete(struct wil6210_vif *vif, const u8 *bssid,
				 u16 reason_code)
{
	struct wil6210_priv *wil = vif_to_wil(vif);

	wil_dbg_misc(wil, "disconnect\n");
	wil_dbg_misc(wil, "got disconnect\n");

	del_timer_sync(&vif->connect_timer);
	_wil6210_disconnect(vif, bssid, reason_code, from_event);
	_wil6210_disconnect_complete(vif, bssid, reason_code);
}

void wil_priv_deinit(struct wil6210_priv *wil)
@@ -1545,7 +1632,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
		if (vif) {
			cancel_work_sync(&vif->disconnect_worker);
			wil6210_disconnect(vif, NULL,
					   WLAN_REASON_DEAUTH_LEAVING, false);
					   WLAN_REASON_DEAUTH_LEAVING);
		}
	}
	wil_bcast_fini_all(wil);
+1 −1
Original line number Diff line number Diff line
@@ -510,7 +510,7 @@ void wil_vif_remove(struct wil6210_priv *wil, u8 mid)
	}

	mutex_lock(&wil->mutex);
	wil6210_disconnect(vif, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
	wil6210_disconnect(vif, NULL, WLAN_REASON_DEAUTH_LEAVING);
	mutex_unlock(&wil->mutex);

	ndev = vif_to_ndev(vif);
+18 −36
Original line number Diff line number Diff line
@@ -160,7 +160,7 @@ static int wil_ring_alloc_skb_edma(struct wil6210_priv *wil,
				   struct wil_ring *ring, u32 i)
{
	struct device *dev = wil_to_dev(wil);
	unsigned int sz = ALIGN(wil->rx_buf_len, 4);
	unsigned int sz = wil->rx_buf_len;
	dma_addr_t pa;
	u16 buff_id;
	struct list_head *active = &wil->rx_buff_mgmt.active;
@@ -234,9 +234,10 @@ static int wil_rx_refill_edma(struct wil6210_priv *wil)
	struct wil_ring *ring = &wil->ring_rx;
	u32 next_head;
	int rc = 0;
	u32 swtail = *ring->edma_rx_swtail.va;
	ring->swtail = *ring->edma_rx_swtail.va;

	for (; next_head = wil_ring_next_head(ring), (next_head != swtail);
	for (; next_head = wil_ring_next_head(ring),
	     (next_head != ring->swtail);
	     ring->swhead = next_head) {
		rc = wil_ring_alloc_skb_edma(wil, ring, ring->swhead);
		if (unlikely(rc)) {
@@ -264,43 +265,26 @@ static void wil_move_all_rx_buff_to_free_list(struct wil6210_priv *wil,
					      struct wil_ring *ring)
{
	struct device *dev = wil_to_dev(wil);
	u32 next_tail;
	u32 swhead = (ring->swhead + 1) % ring->size;
	struct list_head *active = &wil->rx_buff_mgmt.active;
	dma_addr_t pa;
	u16 dmalen;

	for (; next_tail = wil_ring_next_tail(ring), (next_tail != swhead);
	     ring->swtail = next_tail) {
		struct wil_rx_enhanced_desc dd, *d = &dd;
		struct wil_rx_enhanced_desc *_d =
			(struct wil_rx_enhanced_desc *)
			&ring->va[ring->swtail].rx.enhanced;
		struct sk_buff *skb;
		u16 buff_id;

		*d = *_d;
	while (!list_empty(active)) {
		struct wil_rx_buff *rx_buff =
			list_first_entry(active, struct wil_rx_buff, list);
		struct sk_buff *skb = rx_buff->skb;

		/* Extract the SKB from the rx_buff management array */
		buff_id = __le16_to_cpu(d->mac.buff_id);
		if (buff_id >= wil->rx_buff_mgmt.size) {
			wil_err(wil, "invalid buff_id %d\n", buff_id);
			continue;
		}
		skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb;
		wil->rx_buff_mgmt.buff_arr[buff_id].skb = NULL;
		if (unlikely(!skb)) {
			wil_err(wil, "No Rx skb at buff_id %d\n", buff_id);
			wil_err(wil, "No Rx skb at buff_id %d\n", rx_buff->id);
		} else {
			pa = wil_rx_desc_get_addr_edma(&d->dma);
			dmalen = le16_to_cpu(d->dma.length);
			dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE);

			rx_buff->skb = NULL;
			memcpy(&pa, skb->cb, sizeof(pa));
			dma_unmap_single(dev, pa, wil->rx_buf_len,
					 DMA_FROM_DEVICE);
			kfree_skb(skb);
		}

		/* Move the buffer from the active to the free list */
		list_move(&wil->rx_buff_mgmt.buff_arr[buff_id].list,
			  &wil->rx_buff_mgmt.free);
		list_move(&rx_buff->list, &wil->rx_buff_mgmt.free);
	}
}

@@ -602,6 +586,7 @@ static bool wil_is_rx_idle_edma(struct wil6210_priv *wil)

static void wil_rx_buf_len_init_edma(struct wil6210_priv *wil)
{
	/* RX buffer size must be aligned to 4 bytes */
	wil->rx_buf_len = rx_large_buf ?
		WIL_MAX_ETH_MTU : WIL_EDMA_RX_BUF_LEN_DEFAULT;
}
@@ -615,7 +600,6 @@ static int wil_rx_init_edma(struct wil6210_priv *wil, uint desc_ring_order)
		sizeof(struct wil_rx_status_compressed) :
		sizeof(struct wil_rx_status_extended);
	int i;
	u16 max_rx_pl_per_desc;

	/* In SW reorder one must use extended status messages */
	if (wil->use_compressed_rx_status && !wil->use_rx_hw_reordering) {
@@ -641,8 +625,6 @@ static int wil_rx_init_edma(struct wil6210_priv *wil, uint desc_ring_order)

	wil_rx_buf_len_init_edma(wil);

	max_rx_pl_per_desc = ALIGN(wil->rx_buf_len, 4);

	/* Use debugfs dbg_num_rx_srings if set, reserve one sring for TX */
	if (wil->num_rx_status_rings > WIL6210_MAX_STATUS_RINGS - 1)
		wil->num_rx_status_rings = WIL6210_MAX_STATUS_RINGS - 1;
@@ -650,7 +632,7 @@ static int wil_rx_init_edma(struct wil6210_priv *wil, uint desc_ring_order)
	wil_dbg_misc(wil, "rx_init: allocate %d status rings\n",
		     wil->num_rx_status_rings);

	rc = wil_wmi_cfg_def_rx_offload(wil, max_rx_pl_per_desc);
	rc = wil_wmi_cfg_def_rx_offload(wil, wil->rx_buf_len);
	if (rc)
		return rc;

@@ -887,7 +869,7 @@ static struct sk_buff *wil_sring_reap_rx_edma(struct wil6210_priv *wil,
	struct sk_buff *skb;
	dma_addr_t pa;
	struct wil_ring_rx_data *rxdata = &sring->rx_data;
	unsigned int sz = ALIGN(wil->rx_buf_len, 4);
	unsigned int sz = wil->rx_buf_len;
	struct wil_net_stats *stats = NULL;
	u16 dmalen;
	int cid;
Loading