diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c
index 9e0964072af9..6f0ccdeffc4f 100644
--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c
+++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c
@@ -9,6 +9,7 @@
static int mana_hwc_get_msg_index(struct hw_channel_context *hwc, u16 *msg_id)
{
struct gdma_resource *r = &hwc->inflight_msg_res;
+ struct hwc_caller_ctx *ctx;
unsigned long flags;
u32 index;
@@ -19,8 +20,17 @@ static int mana_hwc_get_msg_index(struct hw_channel_context *hwc, u16 *msg_id)
index = find_first_zero_bit(hwc->inflight_msg_res.map,
hwc->inflight_msg_res.size);
+ ctx = &hwc->caller_ctx[index];
+ spin_lock(&ctx->lock);
+ reinit_completion(&ctx->comp_event);
+ ctx->output_buf = NULL;
+ ctx->output_buflen = 0;
+ ctx->error = -EINPROGRESS;
+ ctx->status_code = 0;
+
bitmap_set(hwc->inflight_msg_res.map, index, 1);
+ spin_unlock(&ctx->lock);
spin_unlock_irqrestore(&r->lock, flags);
*msg_id = index;
@@ -90,14 +100,21 @@ static void mana_hwc_handle_resp(struct hw_channel_context *hwc, u32 resp_len,
}
ctx = hwc->caller_ctx + msg_id;
- err = mana_hwc_verify_resp_msg(ctx, resp_msg, resp_len);
- if (err)
- goto out;
- ctx->status_code = resp_msg->status;
+ spin_lock(&ctx->lock);
+ if (!ctx->output_buf) {
+ spin_unlock(&ctx->lock);
+ mana_hwc_post_rx_wqe(hwc->rxq, rx_req);
+ return;
+ }
- memcpy(ctx->output_buf, resp_msg, resp_len);
-out:
+ err = mana_hwc_verify_resp_msg(ctx, resp_msg, resp_len);
+ if (!err) {
+ ctx->status_code = resp_msg->status;
+ memcpy(ctx->output_buf, resp_msg, resp_len);
+ }
+
+ ctx->output_buf = NULL;
ctx->error = err;
/* Must post rx wqe before complete(), otherwise the next rx may@@ -106,6 +123,7 @@ static void mana_hwc_handle_resp(struct hw_channel_context *hwc, u32 resp_len,
mana_hwc_post_rx_wqe(hwc->rxq, rx_req);
complete(&ctx->comp_event);
+ spin_unlock(&ctx->lock);
}
static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self,
@@ -696,8 +714,10 @@ static int mana_hwc_test_channel(struct hw_channel_context *hwc, u16 q_depth,
if (!ctx)
return -ENOMEM;
- for (i = 0; i < q_depth; ++i)
+ for (i = 0; i < q_depth; ++i) {
init_completion(&ctx[i].comp_event);
+ spin_lock_init(&ctx[i].lock);
+ }
hwc->caller_ctx = ctx;
@@ -1000,6 +1020,37 @@ void mana_hwc_free_retained_channel(struct gdma_context *gc)
gc->hwc.gdma_context = NULL;
}
+static int mana_hwc_response_status(struct hw_channel_context *hwc,
+ u32 command, int error, u32 status)
+{
+ if (error)
+ return error;
+
+ if (!status || status == GDMA_STATUS_MORE_ENTRIES)
+ return 0;
+
+ if (status == GDMA_STATUS_CMD_UNSUPPORTED)
+ return -EOPNOTSUPP;
+
+ if (command != MANA_QUERY_PHY_STAT)
+ dev_err(hwc->dev, "Command 0x%x failed with status: 0x%x\n",
+ command, status);
+
+ return -EPROTO;
+}
+
+static void mana_hwc_finish_request(struct hw_channel_context *hwc,
+ struct hwc_caller_ctx *ctx, u16 msg_id)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->lock, flags);
+ ctx->output_buf = NULL;
+ spin_unlock_irqrestore(&ctx->lock, flags);
+
+ mana_hwc_put_msg_index(hwc, msg_id);
+}
+
int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len,
const void *req, u32 resp_len, void *resp)
{@@ -1007,26 +1058,32 @@ int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len,
struct hwc_wq *txq = hwc->txq;
struct gdma_req_hdr *req_msg;
struct hwc_caller_ctx *ctx;
+ unsigned long flags;
u32 dest_vrcq;
u32 dest_vrq;
u32 command;
+ u32 status;
u16 msg_id;
int err;
- mana_hwc_get_msg_index(hwc, &msg_id);
+ err = mana_hwc_get_msg_index(hwc, &msg_id);
+ if (err)
+ return err;
tx_wr = &txq->msg_buf->reqs[msg_id];
+ ctx = hwc->caller_ctx + msg_id;
if (req_len > tx_wr->buf_len) {
dev_err(hwc->dev, "HWC: req msg size: %d > %d\n", req_len,
tx_wr->buf_len);
- err = -EINVAL;
- goto out;
+ mana_hwc_finish_request(hwc, ctx, msg_id);
+ return -EINVAL;
}
- ctx = hwc->caller_ctx + msg_id;
+ spin_lock_irqsave(&ctx->lock, flags);
ctx->output_buf = resp;
ctx->output_buflen = resp_len;
+ spin_unlock_irqrestore(&ctx->lock, flags);
req_msg = (struct gdma_req_hdr *)tx_wr->buf_va;
if (req)@@ -1048,11 +1105,24 @@ int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len,
err = mana_hwc_post_tx_wqe(txq, tx_wr, dest_vrq, dest_vrcq, false);
if (err) {
dev_err(hwc->dev, "HWC: Failed to post send WQE: %d\n", err);
- goto out;
+ mana_hwc_finish_request(hwc, ctx, msg_id);
+ return err;
}
if (!wait_for_completion_timeout(&ctx->comp_event,
- (msecs_to_jiffies(hwc->hwc_timeout)))) {
+ msecs_to_jiffies(hwc->hwc_timeout))) {
+ spin_lock_irqsave(&ctx->lock, flags);
+ ctx->output_buf = NULL;
+ err = ctx->error;
+ status = ctx->status_code;
+ spin_unlock_irqrestore(&ctx->lock, flags);
+
+ if (err != -EINPROGRESS) {
+ mana_hwc_finish_request(hwc, ctx, msg_id);
+ return mana_hwc_response_status(hwc, command, err,
+ status);
+ }
+
if (hwc->hwc_timeout != 0)
dev_err(hwc->dev, "Command 0x%x timed out: %u ms\n",
command, hwc->hwc_timeout);@@ -1061,27 +1131,16 @@ int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len,
if (hwc->hwc_timeout > 1)
hwc->hwc_timeout = 1;
- err = -ETIMEDOUT;
- goto out;
+ mana_hwc_finish_request(hwc, ctx, msg_id);
+ return -ETIMEDOUT;
}
- if (ctx->error) {
- err = ctx->error;
- goto out;
- }
+ spin_lock_irqsave(&ctx->lock, flags);
+ ctx->output_buf = NULL;
+ err = ctx->error;
+ status = ctx->status_code;
+ spin_unlock_irqrestore(&ctx->lock, flags);
- if (ctx->status_code && ctx->status_code != GDMA_STATUS_MORE_ENTRIES) {
- if (ctx->status_code == GDMA_STATUS_CMD_UNSUPPORTED) {
- err = -EOPNOTSUPP;
- goto out;
- }
- if (command != MANA_QUERY_PHY_STAT)
- dev_err(hwc->dev, "Command 0x%x failed with status: 0x%x\n",
- command, ctx->status_code);
- err = -EPROTO;
- goto out;
- }
-out:
- mana_hwc_put_msg_index(hwc, msg_id);
- return err;
+ mana_hwc_finish_request(hwc, ctx, msg_id);
+ return mana_hwc_response_status(hwc, command, err, status);
}diff --git a/include/net/mana/hw_channel.h b/include/net/mana/hw_channel.h
index 1320bdfa9d2a..c7570a5f7376 100644
--- a/include/net/mana/hw_channel.h
+++ b/include/net/mana/hw_channel.h
@@ -168,10 +168,12 @@ struct hwc_wq {
struct hwc_caller_ctx {
struct completion comp_event;
+ /* Protects initialization, output storage and response state. */
+ spinlock_t lock;
void *output_buf;
u32 output_buflen;
- u32 error; /* Linux error code */
+ int error; /* Linux error code */
u32 status_code;
};
--
2.43.0