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

Commit 08b8aa09 authored by Michal Kazior's avatar Michal Kazior Committed by Kalle Valo
Browse files

ath10k: abort incomplete scatter-gather pci tx properly



This prevents leaving incomplete scatter-gather
transfer on CE rings which can lead firmware to
crash.

Reported-By: default avatarAvery Pennarun <apenwarr@gmail.com>
Signed-off-by: default avatarMichal Kazior <michal.kazior@tieto.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent 7147a131
Loading
Loading
Loading
Loading
+27 −0
Original line number Original line Diff line number Diff line
@@ -329,6 +329,33 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
	return ret;
	return ret;
}
}


void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe)
{
	struct ath10k *ar = pipe->ar;
	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
	struct ath10k_ce_ring *src_ring = pipe->src_ring;
	u32 ctrl_addr = pipe->ctrl_addr;

	lockdep_assert_held(&ar_pci->ce_lock);

	/*
	 * This function must be called only if there is an incomplete
	 * scatter-gather transfer (before index register is updated)
	 * that needs to be cleaned up.
	 */
	if (WARN_ON_ONCE(src_ring->write_index == src_ring->sw_index))
		return;

	if (WARN_ON_ONCE(src_ring->write_index ==
			 ath10k_ce_src_ring_write_index_get(ar, ctrl_addr)))
		return;

	src_ring->write_index--;
	src_ring->write_index &= src_ring->nentries_mask;

	src_ring->per_transfer_context[src_ring->write_index] = NULL;
}

int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
		   void *per_transfer_context,
		   void *per_transfer_context,
		   u32 buffer,
		   u32 buffer,
+2 −0
Original line number Original line Diff line number Diff line
@@ -160,6 +160,8 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
			  unsigned int transfer_id,
			  unsigned int transfer_id,
			  unsigned int flags);
			  unsigned int flags);


void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe);

void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
				void (*send_cb)(struct ath10k_ce_pipe *),
				void (*send_cb)(struct ath10k_ce_pipe *),
				int disable_interrupts);
				int disable_interrupts);
+11 −6
Original line number Original line Diff line number Diff line
@@ -765,7 +765,7 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
	unsigned int nentries_mask;
	unsigned int nentries_mask;
	unsigned int sw_index;
	unsigned int sw_index;
	unsigned int write_index;
	unsigned int write_index;
	int err, i;
	int err, i = 0;


	spin_lock_bh(&ar_pci->ce_lock);
	spin_lock_bh(&ar_pci->ce_lock);


@@ -776,7 +776,7 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
	if (unlikely(CE_RING_DELTA(nentries_mask,
	if (unlikely(CE_RING_DELTA(nentries_mask,
				   write_index, sw_index - 1) < n_items)) {
				   write_index, sw_index - 1) < n_items)) {
		err = -ENOBUFS;
		err = -ENOBUFS;
		goto unlock;
		goto err;
	}
	}


	for (i = 0; i < n_items - 1; i++) {
	for (i = 0; i < n_items - 1; i++) {
@@ -793,7 +793,7 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
					    items[i].transfer_id,
					    items[i].transfer_id,
					    CE_SEND_FLAG_GATHER);
					    CE_SEND_FLAG_GATHER);
		if (err)
		if (err)
			goto unlock;
			goto err;
	}
	}


	/* `i` is equal to `n_items -1` after for() */
	/* `i` is equal to `n_items -1` after for() */
@@ -811,10 +811,15 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
				    items[i].transfer_id,
				    items[i].transfer_id,
				    0);
				    0);
	if (err)
	if (err)
		goto unlock;
		goto err;

	spin_unlock_bh(&ar_pci->ce_lock);
	return 0;

err:
	for (; i > 0; i--)
		__ath10k_ce_send_revert(ce_pipe);


	err = 0;
unlock:
	spin_unlock_bh(&ar_pci->ce_lock);
	spin_unlock_bh(&ar_pci->ce_lock);
	return err;
	return err;
}
}