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

Commit 2f301227 authored by Zhu Yi's avatar Zhu Yi Committed by John W. Linville
Browse files

iwlwifi: use paged Rx



This switches the iwlwifi driver to use paged skb from linear skb for Rx
buffer. So that it relieves some Rx buffer allocation pressure for the
memory subsystem. Currently iwlwifi (4K for 3945) requests 8K bytes for
Rx buffer. Due to the trailing skb_shared_info in the skb->data,
alloc_skb() will do the next order allocation, which is 16K bytes. This
is suboptimal and more likely to fail when the system is under memory
usage pressure. Switching to paged Rx skb lets us allocate the RXB
directly by alloc_pages(), so that only order 1 allocation is required.

It also adjusts the area spin_lock (with IRQ disabled) protected in the
tasklet because tasklet guarentees to run only on one CPU and the new
unprotected code can be preempted by the IRQ handler. This saves us from
spawning another workqueue to make skb_linearize/__pskb_pull_tail happy
(which cannot be called in hard irq context).

Finally, mac80211 doesn't support paged Rx yet. So we linearize the skb
for all the management frames and software decryption or defragmentation
required data frames before handed to mac80211. For all the other frames,
we __pskb_pull_tail 64 bytes in the linear area of the skb for mac80211
to handle them properly.

Signed-off-by: default avatarZhu Yi <yi.zhu@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent ae751bab
Loading
Loading
Loading
Loading
+47 −20
Original line number Diff line number Diff line
@@ -293,7 +293,7 @@ static void iwl3945_tx_queue_reclaim(struct iwl_priv *priv,
static void iwl3945_rx_reply_tx(struct iwl_priv *priv,
			    struct iwl_rx_mem_buffer *rxb)
{
	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
	struct iwl_rx_packet *pkt = rxb_addr(rxb);
	u16 sequence = le16_to_cpu(pkt->hdr.sequence);
	int txq_id = SEQ_TO_QUEUE(sequence);
	int index = SEQ_TO_INDEX(sequence);
@@ -353,7 +353,7 @@ static void iwl3945_rx_reply_tx(struct iwl_priv *priv,
void iwl3945_hw_rx_statistics(struct iwl_priv *priv,
		struct iwl_rx_mem_buffer *rxb)
{
	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
	struct iwl_rx_packet *pkt = rxb_addr(rxb);
	IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n",
		     (int)sizeof(struct iwl3945_notif_statistics),
		     le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK);
@@ -543,14 +543,17 @@ static void iwl3945_pass_packet_to_mac80211(struct iwl_priv *priv,
				   struct iwl_rx_mem_buffer *rxb,
				   struct ieee80211_rx_status *stats)
{
	struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
	struct iwl_rx_packet *pkt = rxb_addr(rxb);
	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)IWL_RX_DATA(pkt);
	struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
	struct iwl3945_rx_frame_end *rx_end = IWL_RX_END(pkt);
	short len = le16_to_cpu(rx_hdr->len);
	u16 len = le16_to_cpu(rx_hdr->len);
	struct sk_buff *skb;
	int ret;

	/* We received data from the HW, so stop the watchdog */
	if (unlikely((len + IWL39_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) {
	if (unlikely(len + IWL39_RX_FRAME_SIZE >
		     PAGE_SIZE << priv->hw_params.rx_page_order)) {
		IWL_DEBUG_DROP(priv, "Corruption detected!\n");
		return;
	}
@@ -562,20 +565,45 @@ static void iwl3945_pass_packet_to_mac80211(struct iwl_priv *priv,
		return;
	}

	skb_reserve(rxb->skb, (void *)rx_hdr->payload - (void *)pkt);
	/* Set the size of the skb to the size of the frame */
	skb_put(rxb->skb, le16_to_cpu(rx_hdr->len));
	skb = alloc_skb(IWL_LINK_HDR_MAX, GFP_ATOMIC);
	if (!skb) {
		IWL_ERR(priv, "alloc_skb failed\n");
		return;
	}

	if (!iwl3945_mod_params.sw_crypto)
		iwl_set_decrypted_flag(priv,
				       (struct ieee80211_hdr *)rxb->skb->data,
				       (struct ieee80211_hdr *)rxb_addr(rxb),
				       le32_to_cpu(rx_end->status), stats);

	skb_add_rx_frag(skb, 0, rxb->page,
			(void *)rx_hdr->payload - (void *)pkt, len);

	/* mac80211 currently doesn't support paged SKB. Convert it to
	 * linear SKB for management frame and data frame requires
	 * software decryption or software defragementation. */
	if (ieee80211_is_mgmt(hdr->frame_control) ||
	    ieee80211_has_protected(hdr->frame_control) ||
	    ieee80211_has_morefrags(hdr->frame_control) ||
	    le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)
		ret = skb_linearize(skb);
	else
		ret = __pskb_pull_tail(skb, min_t(u16, IWL_LINK_HDR_MAX, len)) ?
			0 : -ENOMEM;

	if (ret) {
		kfree_skb(skb);
		goto out;
	}

	iwl_update_stats(priv, false, hdr->frame_control, len);

	memcpy(IEEE80211_SKB_RXCB(rxb->skb), stats, sizeof(*stats));
	ieee80211_rx_irqsafe(priv->hw, rxb->skb);
	rxb->skb = NULL;
	memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
	ieee80211_rx(priv->hw, skb);

 out:
	priv->alloc_rxb_page--;
	rxb->page = NULL;
}

#define IWL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6)
@@ -585,7 +613,7 @@ static void iwl3945_rx_reply_rx(struct iwl_priv *priv,
{
	struct ieee80211_hdr *header;
	struct ieee80211_rx_status rx_status;
	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
	struct iwl_rx_packet *pkt = rxb_addr(rxb);
	struct iwl3945_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
	struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
	struct iwl3945_rx_frame_end *rx_end = IWL_RX_END(pkt);
@@ -1811,7 +1839,7 @@ int iwl3945_hw_reg_set_txpower(struct iwl_priv *priv, s8 power)
static int iwl3945_send_rxon_assoc(struct iwl_priv *priv)
{
	int rc = 0;
	struct iwl_rx_packet *res = NULL;
	struct iwl_rx_packet *pkt;
	struct iwl3945_rxon_assoc_cmd rxon_assoc;
	struct iwl_host_cmd cmd = {
		.id = REPLY_RXON_ASSOC,
@@ -1840,14 +1868,14 @@ static int iwl3945_send_rxon_assoc(struct iwl_priv *priv)
	if (rc)
		return rc;

	res = (struct iwl_rx_packet *)cmd.reply_skb->data;
	if (res->hdr.flags & IWL_CMD_FAILED_MSK) {
	pkt = (struct iwl_rx_packet *)cmd.reply_page;
	if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
		IWL_ERR(priv, "Bad return from REPLY_RXON_ASSOC command\n");
		rc = -EIO;
	}

	priv->alloc_rxb_skb--;
	dev_kfree_skb_any(cmd.reply_skb);
	priv->alloc_rxb_page--;
	free_pages(cmd.reply_page, priv->hw_params.rx_page_order);

	return rc;
}
@@ -2513,8 +2541,7 @@ int iwl3945_hw_set_hw_params(struct iwl_priv *priv)
	priv->hw_params.max_txq_num = priv->cfg->num_of_queues;

	priv->hw_params.tfd_size = sizeof(struct iwl3945_tfd);
	priv->hw_params.rx_buf_size = IWL_RX_BUF_SIZE_3K;
	priv->hw_params.max_pkt_size = 2342;
	priv->hw_params.rx_page_order = get_order(IWL_RX_BUF_SIZE_3K);
	priv->hw_params.max_rxq_size = RX_QUEUE_SIZE;
	priv->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG;
	priv->hw_params.max_stations = IWL3945_STATION_COUNT;
+1 −1
Original line number Diff line number Diff line
@@ -1999,7 +1999,7 @@ static int iwl4965_tx_status_reply_tx(struct iwl_priv *priv,
static void iwl4965_rx_reply_tx(struct iwl_priv *priv,
				struct iwl_rx_mem_buffer *rxb)
{
	struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
	struct iwl_rx_packet *pkt = rxb_addr(rxb);
	u16 sequence = le16_to_cpu(pkt->hdr.sequence);
	int txq_id = SEQ_TO_QUEUE(sequence);
	int index = SEQ_TO_INDEX(sequence);
+2 −2
Original line number Diff line number Diff line
@@ -423,7 +423,7 @@ static int iwl5000_send_calib_cfg(struct iwl_priv *priv)
static void iwl5000_rx_calib_result(struct iwl_priv *priv,
			     struct iwl_rx_mem_buffer *rxb)
{
	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
	struct iwl_rx_packet *pkt = rxb_addr(rxb);
	struct iwl_calib_hdr *hdr = (struct iwl_calib_hdr *)pkt->u.raw;
	int len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
	int index;
@@ -1143,7 +1143,7 @@ static int iwl5000_tx_status_reply_tx(struct iwl_priv *priv,
static void iwl5000_rx_reply_tx(struct iwl_priv *priv,
				struct iwl_rx_mem_buffer *rxb)
{
	struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
	struct iwl_rx_packet *pkt = rxb_addr(rxb);
	u16 sequence = le16_to_cpu(pkt->hdr.sequence);
	int txq_id = SEQ_TO_QUEUE(sequence);
	int index = SEQ_TO_INDEX(sequence);
+19 −23
Original line number Diff line number Diff line
@@ -524,7 +524,7 @@ int iwl_hw_tx_queue_init(struct iwl_priv *priv,
static void iwl_rx_reply_alive(struct iwl_priv *priv,
				struct iwl_rx_mem_buffer *rxb)
{
	struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
	struct iwl_rx_packet *pkt = rxb_addr(rxb);
	struct iwl_alive_resp *palive;
	struct delayed_work *pwork;

@@ -610,7 +610,7 @@ static void iwl_rx_beacon_notif(struct iwl_priv *priv,
				struct iwl_rx_mem_buffer *rxb)
{
#ifdef CONFIG_IWLWIFI_DEBUG
	struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
	struct iwl_rx_packet *pkt = rxb_addr(rxb);
	struct iwl4965_beacon_notif *beacon =
		(struct iwl4965_beacon_notif *)pkt->u.raw;
	u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags);
@@ -634,7 +634,7 @@ static void iwl_rx_beacon_notif(struct iwl_priv *priv,
static void iwl_rx_card_state_notif(struct iwl_priv *priv,
				    struct iwl_rx_mem_buffer *rxb)
{
	struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
	struct iwl_rx_packet *pkt = rxb_addr(rxb);
	u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags);
	unsigned long status = priv->status;

@@ -786,10 +786,10 @@ void iwl_rx_handle(struct iwl_priv *priv)

		rxq->queue[i] = NULL;

		pci_unmap_single(priv->pci_dev, rxb->real_dma_addr,
				 priv->hw_params.rx_buf_size + 256,
		pci_unmap_page(priv->pci_dev, rxb->page_dma,
			       PAGE_SIZE << priv->hw_params.rx_page_order,
			       PCI_DMA_FROMDEVICE);
		pkt = (struct iwl_rx_packet *)rxb->skb->data;
		pkt = rxb_addr(rxb);

		trace_iwlwifi_dev_rx(priv, pkt,
			le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK);
@@ -825,10 +825,10 @@ void iwl_rx_handle(struct iwl_priv *priv)
		}

		if (reclaim) {
			/* Invoke any callbacks, transfer the skb to caller, and
			 * fire off the (possibly) blocking iwl_send_cmd()
			/* Invoke any callbacks, transfer the buffer to caller,
			 * and fire off the (possibly) blocking iwl_send_cmd()
			 * as we reclaim the driver command queue */
			if (rxb && rxb->skb)
			if (rxb && rxb->page)
				iwl_tx_cmd_complete(priv, rxb);
			else
				IWL_WARN(priv, "Claim null rxb?\n");
@@ -837,10 +837,10 @@ void iwl_rx_handle(struct iwl_priv *priv)
		/* For now we just don't re-use anything.  We can tweak this
		 * later to try and re-use notification packets and SKBs that
		 * fail to Rx correctly */
		if (rxb->skb != NULL) {
			priv->alloc_rxb_skb--;
			dev_kfree_skb_any(rxb->skb);
			rxb->skb = NULL;
		if (rxb->page != NULL) {
			priv->alloc_rxb_page--;
			__free_pages(rxb->page, priv->hw_params.rx_page_order);
			rxb->page = NULL;
		}

		spin_lock_irqsave(&rxq->lock, flags);
@@ -907,6 +907,8 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)
	}
#endif

	spin_unlock_irqrestore(&priv->lock, flags);

	/* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not
	 * atomic, make sure that inta covers all the interrupts that
	 * we've discovered, even if FH interrupt came in just after
@@ -928,8 +930,6 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)

		handled |= CSR_INT_BIT_HW_ERR;

		spin_unlock_irqrestore(&priv->lock, flags);

		return;
	}

@@ -1056,7 +1056,6 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)
			"flags 0x%08lx\n", inta, inta_mask, inta_fh, flags);
	}
#endif
	spin_unlock_irqrestore(&priv->lock, flags);
}

/* tasklet for iwlagn interrupt */
@@ -1086,6 +1085,9 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
				inta, inta_mask);
	}
#endif

	spin_unlock_irqrestore(&priv->lock, flags);

	/* saved interrupt in inta variable now we can reset priv->inta */
	priv->inta = 0;

@@ -1101,8 +1103,6 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)

		handled |= CSR_INT_BIT_HW_ERR;

		spin_unlock_irqrestore(&priv->lock, flags);

		return;
	}

@@ -1242,14 +1242,10 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
			 inta & ~priv->inta_mask);
	}


	/* Re-enable all interrupts */
	/* only Re-enable if diabled by irq */
	if (test_bit(STATUS_INT_ENABLED, &priv->status))
		iwl_enable_interrupts(priv);

	spin_unlock_irqrestore(&priv->lock, flags);

}


+10 −0
Original line number Diff line number Diff line
@@ -3544,6 +3544,16 @@ struct iwl_wimax_coex_cmd {
 *****************************************************************************/

struct iwl_rx_packet {
	/*
	 * The first 4 bytes of the RX frame header contain both the RX frame
	 * size and some flags.
	 * Bit fields:
	 * 31:    flag flush RB request
	 * 30:    flag ignore TC (terminal counter) request
	 * 29:    flag fast IRQ request
	 * 28-14: Reserved
	 * 13-00: RX frame size
	 */
	__le32 len_n_flags;
	struct iwl_cmd_header hdr;
	union {
Loading