162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
262306a36Sopenharmony_ci/* Copyright (C) 2019 Netronome Systems, Inc. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/bitfield.h>
562306a36Sopenharmony_ci#include <linux/io.h>
662306a36Sopenharmony_ci#include <linux/skbuff.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "ccm.h"
962306a36Sopenharmony_ci#include "nfp_net.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/* CCM messages via the mailbox.  CMSGs get wrapped into simple TLVs
1262306a36Sopenharmony_ci * and copied into the mailbox.  Multiple messages can be copied to
1362306a36Sopenharmony_ci * form a batch.  Threads come in with CMSG formed in an skb, then
1462306a36Sopenharmony_ci * enqueue that skb onto the request queue.  If threads skb is first
1562306a36Sopenharmony_ci * in queue this thread will handle the mailbox operation.  It copies
1662306a36Sopenharmony_ci * up to 64 messages into the mailbox (making sure that both requests
1762306a36Sopenharmony_ci * and replies will fit.  After FW is done processing the batch it
1862306a36Sopenharmony_ci * copies the data out and wakes waiting threads.
1962306a36Sopenharmony_ci * If a thread is waiting it either gets its the message completed
2062306a36Sopenharmony_ci * (response is copied into the same skb as the request, overwriting
2162306a36Sopenharmony_ci * it), or becomes the first in queue.
2262306a36Sopenharmony_ci * Completions and next-to-run are signaled via the control buffer
2362306a36Sopenharmony_ci * to limit potential cache line bounces.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define NFP_CCM_MBOX_BATCH_LIMIT	64
2762306a36Sopenharmony_ci#define NFP_CCM_TIMEOUT			(NFP_NET_POLL_TIMEOUT * 1000)
2862306a36Sopenharmony_ci#define NFP_CCM_MAX_QLEN		1024
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cienum nfp_net_mbox_cmsg_state {
3162306a36Sopenharmony_ci	NFP_NET_MBOX_CMSG_STATE_QUEUED,
3262306a36Sopenharmony_ci	NFP_NET_MBOX_CMSG_STATE_NEXT,
3362306a36Sopenharmony_ci	NFP_NET_MBOX_CMSG_STATE_BUSY,
3462306a36Sopenharmony_ci	NFP_NET_MBOX_CMSG_STATE_REPLY_FOUND,
3562306a36Sopenharmony_ci	NFP_NET_MBOX_CMSG_STATE_DONE,
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/**
3962306a36Sopenharmony_ci * struct nfp_ccm_mbox_cmsg_cb - CCM mailbox specific info
4062306a36Sopenharmony_ci * @state:	processing state (/stage) of the message
4162306a36Sopenharmony_ci * @err:	error encountered during processing if any
4262306a36Sopenharmony_ci * @max_len:	max(request_len, reply_len)
4362306a36Sopenharmony_ci * @exp_reply:	expected reply length (0 means don't validate)
4462306a36Sopenharmony_ci * @posted:	the message was posted and nobody waits for the reply
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_cistruct nfp_ccm_mbox_cmsg_cb {
4762306a36Sopenharmony_ci	enum nfp_net_mbox_cmsg_state state;
4862306a36Sopenharmony_ci	int err;
4962306a36Sopenharmony_ci	unsigned int max_len;
5062306a36Sopenharmony_ci	unsigned int exp_reply;
5162306a36Sopenharmony_ci	bool posted;
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic u32 nfp_ccm_mbox_max_msg(struct nfp_net *nn)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	return round_down(nn->tlv_caps.mbox_len, 4) -
5762306a36Sopenharmony_ci		NFP_NET_CFG_MBOX_SIMPLE_VAL - /* common mbox command header */
5862306a36Sopenharmony_ci		4 * 2; /* Msg TLV plus End TLV headers */
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic void
6262306a36Sopenharmony_cinfp_ccm_mbox_msg_init(struct sk_buff *skb, unsigned int exp_reply, int max_len)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	struct nfp_ccm_mbox_cmsg_cb *cb = (void *)skb->cb;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	cb->state = NFP_NET_MBOX_CMSG_STATE_QUEUED;
6762306a36Sopenharmony_ci	cb->err = 0;
6862306a36Sopenharmony_ci	cb->max_len = max_len;
6962306a36Sopenharmony_ci	cb->exp_reply = exp_reply;
7062306a36Sopenharmony_ci	cb->posted = false;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic int nfp_ccm_mbox_maxlen(const struct sk_buff *skb)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct nfp_ccm_mbox_cmsg_cb *cb = (void *)skb->cb;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return cb->max_len;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic bool nfp_ccm_mbox_done(struct sk_buff *skb)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	struct nfp_ccm_mbox_cmsg_cb *cb = (void *)skb->cb;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return cb->state == NFP_NET_MBOX_CMSG_STATE_DONE;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic bool nfp_ccm_mbox_in_progress(struct sk_buff *skb)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct nfp_ccm_mbox_cmsg_cb *cb = (void *)skb->cb;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return cb->state != NFP_NET_MBOX_CMSG_STATE_QUEUED &&
9262306a36Sopenharmony_ci	       cb->state != NFP_NET_MBOX_CMSG_STATE_NEXT;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic void nfp_ccm_mbox_set_busy(struct sk_buff *skb)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct nfp_ccm_mbox_cmsg_cb *cb = (void *)skb->cb;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	cb->state = NFP_NET_MBOX_CMSG_STATE_BUSY;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic bool nfp_ccm_mbox_is_posted(struct sk_buff *skb)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct nfp_ccm_mbox_cmsg_cb *cb = (void *)skb->cb;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return cb->posted;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic void nfp_ccm_mbox_mark_posted(struct sk_buff *skb)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	struct nfp_ccm_mbox_cmsg_cb *cb = (void *)skb->cb;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	cb->posted = true;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic bool nfp_ccm_mbox_is_first(struct nfp_net *nn, struct sk_buff *skb)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	return skb_queue_is_first(&nn->mbox_cmsg.queue, skb);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic bool nfp_ccm_mbox_should_run(struct nfp_net *nn, struct sk_buff *skb)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct nfp_ccm_mbox_cmsg_cb *cb = (void *)skb->cb;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return cb->state == NFP_NET_MBOX_CMSG_STATE_NEXT;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic void nfp_ccm_mbox_mark_next_runner(struct nfp_net *nn)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct nfp_ccm_mbox_cmsg_cb *cb;
13162306a36Sopenharmony_ci	struct sk_buff *skb;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	skb = skb_peek(&nn->mbox_cmsg.queue);
13462306a36Sopenharmony_ci	if (!skb)
13562306a36Sopenharmony_ci		return;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	cb = (void *)skb->cb;
13862306a36Sopenharmony_ci	cb->state = NFP_NET_MBOX_CMSG_STATE_NEXT;
13962306a36Sopenharmony_ci	if (cb->posted)
14062306a36Sopenharmony_ci		queue_work(nn->mbox_cmsg.workq, &nn->mbox_cmsg.runq_work);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic void
14462306a36Sopenharmony_cinfp_ccm_mbox_write_tlv(struct nfp_net *nn, u32 off, u32 type, u32 len)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	nn_writel(nn, off,
14762306a36Sopenharmony_ci		  FIELD_PREP(NFP_NET_MBOX_TLV_TYPE, type) |
14862306a36Sopenharmony_ci		  FIELD_PREP(NFP_NET_MBOX_TLV_LEN, len));
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic void nfp_ccm_mbox_copy_in(struct nfp_net *nn, struct sk_buff *last)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct sk_buff *skb;
15462306a36Sopenharmony_ci	int reserve, i, cnt;
15562306a36Sopenharmony_ci	__be32 *data;
15662306a36Sopenharmony_ci	u32 off, len;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	off = nn->tlv_caps.mbox_off + NFP_NET_CFG_MBOX_SIMPLE_VAL;
15962306a36Sopenharmony_ci	skb = __skb_peek(&nn->mbox_cmsg.queue);
16062306a36Sopenharmony_ci	while (true) {
16162306a36Sopenharmony_ci		nfp_ccm_mbox_write_tlv(nn, off, NFP_NET_MBOX_TLV_TYPE_MSG,
16262306a36Sopenharmony_ci				       skb->len);
16362306a36Sopenharmony_ci		off += 4;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		/* Write data word by word, skb->data should be aligned */
16662306a36Sopenharmony_ci		data = (__be32 *)skb->data;
16762306a36Sopenharmony_ci		cnt = skb->len / 4;
16862306a36Sopenharmony_ci		for (i = 0 ; i < cnt; i++) {
16962306a36Sopenharmony_ci			nn_writel(nn, off, be32_to_cpu(data[i]));
17062306a36Sopenharmony_ci			off += 4;
17162306a36Sopenharmony_ci		}
17262306a36Sopenharmony_ci		if (skb->len & 3) {
17362306a36Sopenharmony_ci			__be32 tmp = 0;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci			memcpy(&tmp, &data[i], skb->len & 3);
17662306a36Sopenharmony_ci			nn_writel(nn, off, be32_to_cpu(tmp));
17762306a36Sopenharmony_ci			off += 4;
17862306a36Sopenharmony_ci		}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci		/* Reserve space if reply is bigger */
18162306a36Sopenharmony_ci		len = round_up(skb->len, 4);
18262306a36Sopenharmony_ci		reserve = nfp_ccm_mbox_maxlen(skb) - len;
18362306a36Sopenharmony_ci		if (reserve > 0) {
18462306a36Sopenharmony_ci			nfp_ccm_mbox_write_tlv(nn, off,
18562306a36Sopenharmony_ci					       NFP_NET_MBOX_TLV_TYPE_RESV,
18662306a36Sopenharmony_ci					       reserve);
18762306a36Sopenharmony_ci			off += 4 + reserve;
18862306a36Sopenharmony_ci		}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci		if (skb == last)
19162306a36Sopenharmony_ci			break;
19262306a36Sopenharmony_ci		skb = skb_queue_next(&nn->mbox_cmsg.queue, skb);
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	nfp_ccm_mbox_write_tlv(nn, off, NFP_NET_MBOX_TLV_TYPE_END, 0);
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic struct sk_buff *
19962306a36Sopenharmony_cinfp_ccm_mbox_find_req(struct nfp_net *nn, __be16 tag, struct sk_buff *last)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct sk_buff *skb;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	skb = __skb_peek(&nn->mbox_cmsg.queue);
20462306a36Sopenharmony_ci	while (true) {
20562306a36Sopenharmony_ci		if (__nfp_ccm_get_tag(skb) == tag)
20662306a36Sopenharmony_ci			return skb;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci		if (skb == last)
20962306a36Sopenharmony_ci			return NULL;
21062306a36Sopenharmony_ci		skb = skb_queue_next(&nn->mbox_cmsg.queue, skb);
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic void nfp_ccm_mbox_copy_out(struct nfp_net *nn, struct sk_buff *last)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct nfp_ccm_mbox_cmsg_cb *cb;
21762306a36Sopenharmony_ci	u8 __iomem *data, *end;
21862306a36Sopenharmony_ci	struct sk_buff *skb;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	data = nn->dp.ctrl_bar + nn->tlv_caps.mbox_off +
22162306a36Sopenharmony_ci		NFP_NET_CFG_MBOX_SIMPLE_VAL;
22262306a36Sopenharmony_ci	end = data + nn->tlv_caps.mbox_len;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	while (true) {
22562306a36Sopenharmony_ci		unsigned int length, offset, type;
22662306a36Sopenharmony_ci		struct nfp_ccm_hdr hdr;
22762306a36Sopenharmony_ci		u32 tlv_hdr;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci		tlv_hdr = readl(data);
23062306a36Sopenharmony_ci		type = FIELD_GET(NFP_NET_MBOX_TLV_TYPE, tlv_hdr);
23162306a36Sopenharmony_ci		length = FIELD_GET(NFP_NET_MBOX_TLV_LEN, tlv_hdr);
23262306a36Sopenharmony_ci		offset = data - nn->dp.ctrl_bar;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		/* Advance past the header */
23562306a36Sopenharmony_ci		data += 4;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		if (data + length > end) {
23862306a36Sopenharmony_ci			nn_dp_warn(&nn->dp, "mailbox oversized TLV type:%d offset:%u len:%u\n",
23962306a36Sopenharmony_ci				   type, offset, length);
24062306a36Sopenharmony_ci			break;
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		if (type == NFP_NET_MBOX_TLV_TYPE_END)
24462306a36Sopenharmony_ci			break;
24562306a36Sopenharmony_ci		if (type == NFP_NET_MBOX_TLV_TYPE_RESV)
24662306a36Sopenharmony_ci			goto next_tlv;
24762306a36Sopenharmony_ci		if (type != NFP_NET_MBOX_TLV_TYPE_MSG &&
24862306a36Sopenharmony_ci		    type != NFP_NET_MBOX_TLV_TYPE_MSG_NOSUP) {
24962306a36Sopenharmony_ci			nn_dp_warn(&nn->dp, "mailbox unknown TLV type:%d offset:%u len:%u\n",
25062306a36Sopenharmony_ci				   type, offset, length);
25162306a36Sopenharmony_ci			break;
25262306a36Sopenharmony_ci		}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci		if (length < 4) {
25562306a36Sopenharmony_ci			nn_dp_warn(&nn->dp, "mailbox msg too short to contain header TLV type:%d offset:%u len:%u\n",
25662306a36Sopenharmony_ci				   type, offset, length);
25762306a36Sopenharmony_ci			break;
25862306a36Sopenharmony_ci		}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		hdr.raw = cpu_to_be32(readl(data));
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		skb = nfp_ccm_mbox_find_req(nn, hdr.tag, last);
26362306a36Sopenharmony_ci		if (!skb) {
26462306a36Sopenharmony_ci			nn_dp_warn(&nn->dp, "mailbox request not found:%u\n",
26562306a36Sopenharmony_ci				   be16_to_cpu(hdr.tag));
26662306a36Sopenharmony_ci			break;
26762306a36Sopenharmony_ci		}
26862306a36Sopenharmony_ci		cb = (void *)skb->cb;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		if (type == NFP_NET_MBOX_TLV_TYPE_MSG_NOSUP) {
27162306a36Sopenharmony_ci			nn_dp_warn(&nn->dp,
27262306a36Sopenharmony_ci				   "mailbox msg not supported type:%d\n",
27362306a36Sopenharmony_ci				   nfp_ccm_get_type(skb));
27462306a36Sopenharmony_ci			cb->err = -EIO;
27562306a36Sopenharmony_ci			goto next_tlv;
27662306a36Sopenharmony_ci		}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci		if (hdr.type != __NFP_CCM_REPLY(nfp_ccm_get_type(skb))) {
27962306a36Sopenharmony_ci			nn_dp_warn(&nn->dp, "mailbox msg reply wrong type:%u expected:%lu\n",
28062306a36Sopenharmony_ci				   hdr.type,
28162306a36Sopenharmony_ci				   __NFP_CCM_REPLY(nfp_ccm_get_type(skb)));
28262306a36Sopenharmony_ci			cb->err = -EIO;
28362306a36Sopenharmony_ci			goto next_tlv;
28462306a36Sopenharmony_ci		}
28562306a36Sopenharmony_ci		if (cb->exp_reply && length != cb->exp_reply) {
28662306a36Sopenharmony_ci			nn_dp_warn(&nn->dp, "mailbox msg reply wrong size type:%u expected:%u have:%u\n",
28762306a36Sopenharmony_ci				   hdr.type, length, cb->exp_reply);
28862306a36Sopenharmony_ci			cb->err = -EIO;
28962306a36Sopenharmony_ci			goto next_tlv;
29062306a36Sopenharmony_ci		}
29162306a36Sopenharmony_ci		if (length > cb->max_len) {
29262306a36Sopenharmony_ci			nn_dp_warn(&nn->dp, "mailbox msg oversized reply type:%u max:%u have:%u\n",
29362306a36Sopenharmony_ci				   hdr.type, cb->max_len, length);
29462306a36Sopenharmony_ci			cb->err = -EIO;
29562306a36Sopenharmony_ci			goto next_tlv;
29662306a36Sopenharmony_ci		}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci		if (!cb->posted) {
29962306a36Sopenharmony_ci			__be32 *skb_data;
30062306a36Sopenharmony_ci			int i, cnt;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci			if (length <= skb->len)
30362306a36Sopenharmony_ci				__skb_trim(skb, length);
30462306a36Sopenharmony_ci			else
30562306a36Sopenharmony_ci				skb_put(skb, length - skb->len);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci			/* We overcopy here slightly, but that's okay,
30862306a36Sopenharmony_ci			 * the skb is large enough, and the garbage will
30962306a36Sopenharmony_ci			 * be ignored (beyond skb->len).
31062306a36Sopenharmony_ci			 */
31162306a36Sopenharmony_ci			skb_data = (__be32 *)skb->data;
31262306a36Sopenharmony_ci			memcpy(skb_data, &hdr, 4);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci			cnt = DIV_ROUND_UP(length, 4);
31562306a36Sopenharmony_ci			for (i = 1 ; i < cnt; i++)
31662306a36Sopenharmony_ci				skb_data[i] = cpu_to_be32(readl(data + i * 4));
31762306a36Sopenharmony_ci		}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		cb->state = NFP_NET_MBOX_CMSG_STATE_REPLY_FOUND;
32062306a36Sopenharmony_cinext_tlv:
32162306a36Sopenharmony_ci		data += round_up(length, 4);
32262306a36Sopenharmony_ci		if (data + 4 > end) {
32362306a36Sopenharmony_ci			nn_dp_warn(&nn->dp,
32462306a36Sopenharmony_ci				   "reached end of MBOX without END TLV\n");
32562306a36Sopenharmony_ci			break;
32662306a36Sopenharmony_ci		}
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	smp_wmb(); /* order the skb->data vs. cb->state */
33062306a36Sopenharmony_ci	spin_lock_bh(&nn->mbox_cmsg.queue.lock);
33162306a36Sopenharmony_ci	do {
33262306a36Sopenharmony_ci		skb = __skb_dequeue(&nn->mbox_cmsg.queue);
33362306a36Sopenharmony_ci		cb = (void *)skb->cb;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci		if (cb->state != NFP_NET_MBOX_CMSG_STATE_REPLY_FOUND) {
33662306a36Sopenharmony_ci			cb->err = -ENOENT;
33762306a36Sopenharmony_ci			smp_wmb(); /* order the cb->err vs. cb->state */
33862306a36Sopenharmony_ci		}
33962306a36Sopenharmony_ci		cb->state = NFP_NET_MBOX_CMSG_STATE_DONE;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		if (cb->posted) {
34262306a36Sopenharmony_ci			if (cb->err)
34362306a36Sopenharmony_ci				nn_dp_warn(&nn->dp,
34462306a36Sopenharmony_ci					   "mailbox posted msg failed type:%u err:%d\n",
34562306a36Sopenharmony_ci					   nfp_ccm_get_type(skb), cb->err);
34662306a36Sopenharmony_ci			dev_consume_skb_any(skb);
34762306a36Sopenharmony_ci		}
34862306a36Sopenharmony_ci	} while (skb != last);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	nfp_ccm_mbox_mark_next_runner(nn);
35162306a36Sopenharmony_ci	spin_unlock_bh(&nn->mbox_cmsg.queue.lock);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic void
35562306a36Sopenharmony_cinfp_ccm_mbox_mark_all_err(struct nfp_net *nn, struct sk_buff *last, int err)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	struct nfp_ccm_mbox_cmsg_cb *cb;
35862306a36Sopenharmony_ci	struct sk_buff *skb;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	spin_lock_bh(&nn->mbox_cmsg.queue.lock);
36162306a36Sopenharmony_ci	do {
36262306a36Sopenharmony_ci		skb = __skb_dequeue(&nn->mbox_cmsg.queue);
36362306a36Sopenharmony_ci		cb = (void *)skb->cb;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		cb->err = err;
36662306a36Sopenharmony_ci		smp_wmb(); /* order the cb->err vs. cb->state */
36762306a36Sopenharmony_ci		cb->state = NFP_NET_MBOX_CMSG_STATE_DONE;
36862306a36Sopenharmony_ci	} while (skb != last);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	nfp_ccm_mbox_mark_next_runner(nn);
37162306a36Sopenharmony_ci	spin_unlock_bh(&nn->mbox_cmsg.queue.lock);
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic void nfp_ccm_mbox_run_queue_unlock(struct nfp_net *nn)
37562306a36Sopenharmony_ci	__releases(&nn->mbox_cmsg.queue.lock)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	int space = nn->tlv_caps.mbox_len - NFP_NET_CFG_MBOX_SIMPLE_VAL;
37862306a36Sopenharmony_ci	struct sk_buff *skb, *last;
37962306a36Sopenharmony_ci	int cnt, err;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	space -= 4; /* for End TLV */
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/* First skb must fit, because it's ours and we checked it fits */
38462306a36Sopenharmony_ci	cnt = 1;
38562306a36Sopenharmony_ci	last = skb = __skb_peek(&nn->mbox_cmsg.queue);
38662306a36Sopenharmony_ci	space -= 4 + nfp_ccm_mbox_maxlen(skb);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	while (!skb_queue_is_last(&nn->mbox_cmsg.queue, last)) {
38962306a36Sopenharmony_ci		skb = skb_queue_next(&nn->mbox_cmsg.queue, last);
39062306a36Sopenharmony_ci		space -= 4 + nfp_ccm_mbox_maxlen(skb);
39162306a36Sopenharmony_ci		if (space < 0)
39262306a36Sopenharmony_ci			break;
39362306a36Sopenharmony_ci		last = skb;
39462306a36Sopenharmony_ci		nfp_ccm_mbox_set_busy(skb);
39562306a36Sopenharmony_ci		cnt++;
39662306a36Sopenharmony_ci		if (cnt == NFP_CCM_MBOX_BATCH_LIMIT)
39762306a36Sopenharmony_ci			break;
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci	spin_unlock_bh(&nn->mbox_cmsg.queue.lock);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/* Now we own all skb's marked in progress, new requests may arrive
40262306a36Sopenharmony_ci	 * at the end of the queue.
40362306a36Sopenharmony_ci	 */
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	nn_ctrl_bar_lock(nn);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	nfp_ccm_mbox_copy_in(nn, last);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	err = nfp_net_mbox_reconfig(nn, NFP_NET_CFG_MBOX_CMD_TLV_CMSG);
41062306a36Sopenharmony_ci	if (!err)
41162306a36Sopenharmony_ci		nfp_ccm_mbox_copy_out(nn, last);
41262306a36Sopenharmony_ci	else
41362306a36Sopenharmony_ci		nfp_ccm_mbox_mark_all_err(nn, last, -EIO);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	nn_ctrl_bar_unlock(nn);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	wake_up_all(&nn->mbox_cmsg.wq);
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic int nfp_ccm_mbox_skb_return(struct sk_buff *skb)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	struct nfp_ccm_mbox_cmsg_cb *cb = (void *)skb->cb;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	if (cb->err)
42562306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
42662306a36Sopenharmony_ci	return cb->err;
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci/* If wait timed out but the command is already in progress we have
43062306a36Sopenharmony_ci * to wait until it finishes.  Runners has ownership of the skbs marked
43162306a36Sopenharmony_ci * as busy.
43262306a36Sopenharmony_ci */
43362306a36Sopenharmony_cistatic int
43462306a36Sopenharmony_cinfp_ccm_mbox_unlink_unlock(struct nfp_net *nn, struct sk_buff *skb,
43562306a36Sopenharmony_ci			   enum nfp_ccm_type type)
43662306a36Sopenharmony_ci	__releases(&nn->mbox_cmsg.queue.lock)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	bool was_first;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (nfp_ccm_mbox_in_progress(skb)) {
44162306a36Sopenharmony_ci		spin_unlock_bh(&nn->mbox_cmsg.queue.lock);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		wait_event(nn->mbox_cmsg.wq, nfp_ccm_mbox_done(skb));
44462306a36Sopenharmony_ci		smp_rmb(); /* pairs with smp_wmb() after data is written */
44562306a36Sopenharmony_ci		return nfp_ccm_mbox_skb_return(skb);
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	was_first = nfp_ccm_mbox_should_run(nn, skb);
44962306a36Sopenharmony_ci	__skb_unlink(skb, &nn->mbox_cmsg.queue);
45062306a36Sopenharmony_ci	if (was_first)
45162306a36Sopenharmony_ci		nfp_ccm_mbox_mark_next_runner(nn);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	spin_unlock_bh(&nn->mbox_cmsg.queue.lock);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (was_first)
45662306a36Sopenharmony_ci		wake_up_all(&nn->mbox_cmsg.wq);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	nn_dp_warn(&nn->dp, "time out waiting for mbox response to 0x%02x\n",
45962306a36Sopenharmony_ci		   type);
46062306a36Sopenharmony_ci	return -ETIMEDOUT;
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic int
46462306a36Sopenharmony_cinfp_ccm_mbox_msg_prepare(struct nfp_net *nn, struct sk_buff *skb,
46562306a36Sopenharmony_ci			 enum nfp_ccm_type type,
46662306a36Sopenharmony_ci			 unsigned int reply_size, unsigned int max_reply_size,
46762306a36Sopenharmony_ci			 gfp_t flags)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	const unsigned int mbox_max = nfp_ccm_mbox_max_msg(nn);
47062306a36Sopenharmony_ci	unsigned int max_len;
47162306a36Sopenharmony_ci	ssize_t undersize;
47262306a36Sopenharmony_ci	int err;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	if (unlikely(!(nn->tlv_caps.mbox_cmsg_types & BIT(type)))) {
47562306a36Sopenharmony_ci		nn_dp_warn(&nn->dp,
47662306a36Sopenharmony_ci			   "message type %d not supported by mailbox\n", type);
47762306a36Sopenharmony_ci		return -EINVAL;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	/* If the reply size is unknown assume it will take the entire
48162306a36Sopenharmony_ci	 * mailbox, the callers should do their best for this to never
48262306a36Sopenharmony_ci	 * happen.
48362306a36Sopenharmony_ci	 */
48462306a36Sopenharmony_ci	if (!max_reply_size)
48562306a36Sopenharmony_ci		max_reply_size = mbox_max;
48662306a36Sopenharmony_ci	max_reply_size = round_up(max_reply_size, 4);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	/* Make sure we can fit the entire reply into the skb,
48962306a36Sopenharmony_ci	 * and that we don't have to slow down the mbox handler
49062306a36Sopenharmony_ci	 * with allocations.
49162306a36Sopenharmony_ci	 */
49262306a36Sopenharmony_ci	undersize = max_reply_size - (skb_end_pointer(skb) - skb->data);
49362306a36Sopenharmony_ci	if (undersize > 0) {
49462306a36Sopenharmony_ci		err = pskb_expand_head(skb, 0, undersize, flags);
49562306a36Sopenharmony_ci		if (err) {
49662306a36Sopenharmony_ci			nn_dp_warn(&nn->dp,
49762306a36Sopenharmony_ci				   "can't allocate reply buffer for mailbox\n");
49862306a36Sopenharmony_ci			return err;
49962306a36Sopenharmony_ci		}
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	/* Make sure that request and response both fit into the mailbox */
50362306a36Sopenharmony_ci	max_len = max(max_reply_size, round_up(skb->len, 4));
50462306a36Sopenharmony_ci	if (max_len > mbox_max) {
50562306a36Sopenharmony_ci		nn_dp_warn(&nn->dp,
50662306a36Sopenharmony_ci			   "message too big for the mailbox: %u/%u vs %u\n",
50762306a36Sopenharmony_ci			   skb->len, max_reply_size, mbox_max);
50862306a36Sopenharmony_ci		return -EMSGSIZE;
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	nfp_ccm_mbox_msg_init(skb, reply_size, max_len);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	return 0;
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic int
51762306a36Sopenharmony_cinfp_ccm_mbox_msg_enqueue(struct nfp_net *nn, struct sk_buff *skb,
51862306a36Sopenharmony_ci			 enum nfp_ccm_type type, bool critical)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	struct nfp_ccm_hdr *hdr;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	assert_spin_locked(&nn->mbox_cmsg.queue.lock);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	if (!critical && nn->mbox_cmsg.queue.qlen >= NFP_CCM_MAX_QLEN) {
52562306a36Sopenharmony_ci		nn_dp_warn(&nn->dp, "mailbox request queue too long\n");
52662306a36Sopenharmony_ci		return -EBUSY;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	hdr = (void *)skb->data;
53062306a36Sopenharmony_ci	hdr->ver = NFP_CCM_ABI_VERSION;
53162306a36Sopenharmony_ci	hdr->type = type;
53262306a36Sopenharmony_ci	hdr->tag = cpu_to_be16(nn->mbox_cmsg.tag++);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	__skb_queue_tail(&nn->mbox_cmsg.queue, skb);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	return 0;
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ciint __nfp_ccm_mbox_communicate(struct nfp_net *nn, struct sk_buff *skb,
54062306a36Sopenharmony_ci			       enum nfp_ccm_type type,
54162306a36Sopenharmony_ci			       unsigned int reply_size,
54262306a36Sopenharmony_ci			       unsigned int max_reply_size, bool critical)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	int err;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	err = nfp_ccm_mbox_msg_prepare(nn, skb, type, reply_size,
54762306a36Sopenharmony_ci				       max_reply_size, GFP_KERNEL);
54862306a36Sopenharmony_ci	if (err)
54962306a36Sopenharmony_ci		goto err_free_skb;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	spin_lock_bh(&nn->mbox_cmsg.queue.lock);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	err = nfp_ccm_mbox_msg_enqueue(nn, skb, type, critical);
55462306a36Sopenharmony_ci	if (err)
55562306a36Sopenharmony_ci		goto err_unlock;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	/* First in queue takes the mailbox lock and processes the batch */
55862306a36Sopenharmony_ci	if (!nfp_ccm_mbox_is_first(nn, skb)) {
55962306a36Sopenharmony_ci		bool to;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci		spin_unlock_bh(&nn->mbox_cmsg.queue.lock);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci		to = !wait_event_timeout(nn->mbox_cmsg.wq,
56462306a36Sopenharmony_ci					 nfp_ccm_mbox_done(skb) ||
56562306a36Sopenharmony_ci					 nfp_ccm_mbox_should_run(nn, skb),
56662306a36Sopenharmony_ci					 msecs_to_jiffies(NFP_CCM_TIMEOUT));
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		/* fast path for those completed by another thread */
56962306a36Sopenharmony_ci		if (nfp_ccm_mbox_done(skb)) {
57062306a36Sopenharmony_ci			smp_rmb(); /* pairs with wmb after data is written */
57162306a36Sopenharmony_ci			return nfp_ccm_mbox_skb_return(skb);
57262306a36Sopenharmony_ci		}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci		spin_lock_bh(&nn->mbox_cmsg.queue.lock);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci		if (!nfp_ccm_mbox_is_first(nn, skb)) {
57762306a36Sopenharmony_ci			WARN_ON(!to);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci			err = nfp_ccm_mbox_unlink_unlock(nn, skb, type);
58062306a36Sopenharmony_ci			if (err)
58162306a36Sopenharmony_ci				goto err_free_skb;
58262306a36Sopenharmony_ci			return 0;
58362306a36Sopenharmony_ci		}
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	/* run queue expects the lock held */
58762306a36Sopenharmony_ci	nfp_ccm_mbox_run_queue_unlock(nn);
58862306a36Sopenharmony_ci	return nfp_ccm_mbox_skb_return(skb);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cierr_unlock:
59162306a36Sopenharmony_ci	spin_unlock_bh(&nn->mbox_cmsg.queue.lock);
59262306a36Sopenharmony_cierr_free_skb:
59362306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
59462306a36Sopenharmony_ci	return err;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ciint nfp_ccm_mbox_communicate(struct nfp_net *nn, struct sk_buff *skb,
59862306a36Sopenharmony_ci			     enum nfp_ccm_type type,
59962306a36Sopenharmony_ci			     unsigned int reply_size,
60062306a36Sopenharmony_ci			     unsigned int max_reply_size)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	return __nfp_ccm_mbox_communicate(nn, skb, type, reply_size,
60362306a36Sopenharmony_ci					  max_reply_size, false);
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cistatic void nfp_ccm_mbox_post_runq_work(struct work_struct *work)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	struct sk_buff *skb;
60962306a36Sopenharmony_ci	struct nfp_net *nn;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	nn = container_of(work, struct nfp_net, mbox_cmsg.runq_work);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	spin_lock_bh(&nn->mbox_cmsg.queue.lock);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	skb = __skb_peek(&nn->mbox_cmsg.queue);
61662306a36Sopenharmony_ci	if (WARN_ON(!skb || !nfp_ccm_mbox_is_posted(skb) ||
61762306a36Sopenharmony_ci		    !nfp_ccm_mbox_should_run(nn, skb))) {
61862306a36Sopenharmony_ci		spin_unlock_bh(&nn->mbox_cmsg.queue.lock);
61962306a36Sopenharmony_ci		return;
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	nfp_ccm_mbox_run_queue_unlock(nn);
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic void nfp_ccm_mbox_post_wait_work(struct work_struct *work)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	struct sk_buff *skb;
62862306a36Sopenharmony_ci	struct nfp_net *nn;
62962306a36Sopenharmony_ci	int err;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	nn = container_of(work, struct nfp_net, mbox_cmsg.wait_work);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	skb = skb_peek(&nn->mbox_cmsg.queue);
63462306a36Sopenharmony_ci	if (WARN_ON(!skb || !nfp_ccm_mbox_is_posted(skb)))
63562306a36Sopenharmony_ci		/* Should never happen so it's unclear what to do here.. */
63662306a36Sopenharmony_ci		goto exit_unlock_wake;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	err = nfp_net_mbox_reconfig_wait_posted(nn);
63962306a36Sopenharmony_ci	if (!err)
64062306a36Sopenharmony_ci		nfp_ccm_mbox_copy_out(nn, skb);
64162306a36Sopenharmony_ci	else
64262306a36Sopenharmony_ci		nfp_ccm_mbox_mark_all_err(nn, skb, -EIO);
64362306a36Sopenharmony_ciexit_unlock_wake:
64462306a36Sopenharmony_ci	nn_ctrl_bar_unlock(nn);
64562306a36Sopenharmony_ci	wake_up_all(&nn->mbox_cmsg.wq);
64662306a36Sopenharmony_ci}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ciint nfp_ccm_mbox_post(struct nfp_net *nn, struct sk_buff *skb,
64962306a36Sopenharmony_ci		      enum nfp_ccm_type type, unsigned int max_reply_size)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	int err;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	err = nfp_ccm_mbox_msg_prepare(nn, skb, type, 0, max_reply_size,
65462306a36Sopenharmony_ci				       GFP_ATOMIC);
65562306a36Sopenharmony_ci	if (err)
65662306a36Sopenharmony_ci		goto err_free_skb;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	nfp_ccm_mbox_mark_posted(skb);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	spin_lock_bh(&nn->mbox_cmsg.queue.lock);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	err = nfp_ccm_mbox_msg_enqueue(nn, skb, type, false);
66362306a36Sopenharmony_ci	if (err)
66462306a36Sopenharmony_ci		goto err_unlock;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	if (nfp_ccm_mbox_is_first(nn, skb)) {
66762306a36Sopenharmony_ci		if (nn_ctrl_bar_trylock(nn)) {
66862306a36Sopenharmony_ci			nfp_ccm_mbox_copy_in(nn, skb);
66962306a36Sopenharmony_ci			nfp_net_mbox_reconfig_post(nn,
67062306a36Sopenharmony_ci						   NFP_NET_CFG_MBOX_CMD_TLV_CMSG);
67162306a36Sopenharmony_ci			queue_work(nn->mbox_cmsg.workq,
67262306a36Sopenharmony_ci				   &nn->mbox_cmsg.wait_work);
67362306a36Sopenharmony_ci		} else {
67462306a36Sopenharmony_ci			nfp_ccm_mbox_mark_next_runner(nn);
67562306a36Sopenharmony_ci		}
67662306a36Sopenharmony_ci	}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	spin_unlock_bh(&nn->mbox_cmsg.queue.lock);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	return 0;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cierr_unlock:
68362306a36Sopenharmony_ci	spin_unlock_bh(&nn->mbox_cmsg.queue.lock);
68462306a36Sopenharmony_cierr_free_skb:
68562306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
68662306a36Sopenharmony_ci	return err;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_cistruct sk_buff *
69062306a36Sopenharmony_cinfp_ccm_mbox_msg_alloc(struct nfp_net *nn, unsigned int req_size,
69162306a36Sopenharmony_ci		       unsigned int reply_size, gfp_t flags)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	unsigned int max_size;
69462306a36Sopenharmony_ci	struct sk_buff *skb;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (!reply_size)
69762306a36Sopenharmony_ci		max_size = nfp_ccm_mbox_max_msg(nn);
69862306a36Sopenharmony_ci	else
69962306a36Sopenharmony_ci		max_size = max(req_size, reply_size);
70062306a36Sopenharmony_ci	max_size = round_up(max_size, 4);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	skb = alloc_skb(max_size, flags);
70362306a36Sopenharmony_ci	if (!skb)
70462306a36Sopenharmony_ci		return NULL;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	skb_put(skb, req_size);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	return skb;
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cibool nfp_ccm_mbox_fits(struct nfp_net *nn, unsigned int size)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	return nfp_ccm_mbox_max_msg(nn) >= size;
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ciint nfp_ccm_mbox_init(struct nfp_net *nn)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	return 0;
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_civoid nfp_ccm_mbox_clean(struct nfp_net *nn)
72262306a36Sopenharmony_ci{
72362306a36Sopenharmony_ci	drain_workqueue(nn->mbox_cmsg.workq);
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ciint nfp_ccm_mbox_alloc(struct nfp_net *nn)
72762306a36Sopenharmony_ci{
72862306a36Sopenharmony_ci	skb_queue_head_init(&nn->mbox_cmsg.queue);
72962306a36Sopenharmony_ci	init_waitqueue_head(&nn->mbox_cmsg.wq);
73062306a36Sopenharmony_ci	INIT_WORK(&nn->mbox_cmsg.wait_work, nfp_ccm_mbox_post_wait_work);
73162306a36Sopenharmony_ci	INIT_WORK(&nn->mbox_cmsg.runq_work, nfp_ccm_mbox_post_runq_work);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	nn->mbox_cmsg.workq = alloc_workqueue("nfp-ccm-mbox", WQ_UNBOUND, 0);
73462306a36Sopenharmony_ci	if (!nn->mbox_cmsg.workq)
73562306a36Sopenharmony_ci		return -ENOMEM;
73662306a36Sopenharmony_ci	return 0;
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_civoid nfp_ccm_mbox_free(struct nfp_net *nn)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	destroy_workqueue(nn->mbox_cmsg.workq);
74262306a36Sopenharmony_ci	WARN_ON(!skb_queue_empty(&nn->mbox_cmsg.queue));
74362306a36Sopenharmony_ci}
744