162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 262306a36Sopenharmony_ci/* Copyright (C) 2016-2019 Netronome Systems, Inc. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/bitops.h> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include "ccm.h" 762306a36Sopenharmony_ci#include "nfp_app.h" 862306a36Sopenharmony_ci#include "nfp_net.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define ccm_warn(app, msg...) nn_dp_warn(&(app)->ctrl->dp, msg) 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define NFP_CCM_TAG_ALLOC_SPAN (U16_MAX / 4) 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic bool nfp_ccm_all_tags_busy(struct nfp_ccm *ccm) 1562306a36Sopenharmony_ci{ 1662306a36Sopenharmony_ci u16 used_tags; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci used_tags = ccm->tag_alloc_next - ccm->tag_alloc_last; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci return used_tags > NFP_CCM_TAG_ALLOC_SPAN; 2162306a36Sopenharmony_ci} 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int nfp_ccm_alloc_tag(struct nfp_ccm *ccm) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci /* CCM is for FW communication which is request-reply. To make sure 2662306a36Sopenharmony_ci * we don't reuse the message ID too early after timeout - limit the 2762306a36Sopenharmony_ci * number of requests in flight. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci if (unlikely(nfp_ccm_all_tags_busy(ccm))) { 3062306a36Sopenharmony_ci ccm_warn(ccm->app, "all FW request contexts busy!\n"); 3162306a36Sopenharmony_ci return -EAGAIN; 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci WARN_ON(__test_and_set_bit(ccm->tag_alloc_next, ccm->tag_allocator)); 3562306a36Sopenharmony_ci return ccm->tag_alloc_next++; 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic void nfp_ccm_free_tag(struct nfp_ccm *ccm, u16 tag) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci WARN_ON(!__test_and_clear_bit(tag, ccm->tag_allocator)); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci while (!test_bit(ccm->tag_alloc_last, ccm->tag_allocator) && 4362306a36Sopenharmony_ci ccm->tag_alloc_last != ccm->tag_alloc_next) 4462306a36Sopenharmony_ci ccm->tag_alloc_last++; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic struct sk_buff *__nfp_ccm_reply(struct nfp_ccm *ccm, u16 tag) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci unsigned int msg_tag; 5062306a36Sopenharmony_ci struct sk_buff *skb; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci skb_queue_walk(&ccm->replies, skb) { 5362306a36Sopenharmony_ci msg_tag = nfp_ccm_get_tag(skb); 5462306a36Sopenharmony_ci if (msg_tag == tag) { 5562306a36Sopenharmony_ci nfp_ccm_free_tag(ccm, tag); 5662306a36Sopenharmony_ci __skb_unlink(skb, &ccm->replies); 5762306a36Sopenharmony_ci return skb; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return NULL; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic struct sk_buff * 6562306a36Sopenharmony_cinfp_ccm_reply(struct nfp_ccm *ccm, struct nfp_app *app, u16 tag) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct sk_buff *skb; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci nfp_ctrl_lock(app->ctrl); 7062306a36Sopenharmony_ci skb = __nfp_ccm_reply(ccm, tag); 7162306a36Sopenharmony_ci nfp_ctrl_unlock(app->ctrl); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return skb; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic struct sk_buff * 7762306a36Sopenharmony_cinfp_ccm_reply_drop_tag(struct nfp_ccm *ccm, struct nfp_app *app, u16 tag) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct sk_buff *skb; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci nfp_ctrl_lock(app->ctrl); 8262306a36Sopenharmony_ci skb = __nfp_ccm_reply(ccm, tag); 8362306a36Sopenharmony_ci if (!skb) 8462306a36Sopenharmony_ci nfp_ccm_free_tag(ccm, tag); 8562306a36Sopenharmony_ci nfp_ctrl_unlock(app->ctrl); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return skb; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic struct sk_buff * 9162306a36Sopenharmony_cinfp_ccm_wait_reply(struct nfp_ccm *ccm, struct nfp_app *app, 9262306a36Sopenharmony_ci enum nfp_ccm_type type, int tag) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct sk_buff *skb; 9562306a36Sopenharmony_ci int i, err; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci for (i = 0; i < 50; i++) { 9862306a36Sopenharmony_ci udelay(4); 9962306a36Sopenharmony_ci skb = nfp_ccm_reply(ccm, app, tag); 10062306a36Sopenharmony_ci if (skb) 10162306a36Sopenharmony_ci return skb; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci err = wait_event_interruptible_timeout(ccm->wq, 10562306a36Sopenharmony_ci skb = nfp_ccm_reply(ccm, app, 10662306a36Sopenharmony_ci tag), 10762306a36Sopenharmony_ci msecs_to_jiffies(5000)); 10862306a36Sopenharmony_ci /* We didn't get a response - try last time and atomically drop 10962306a36Sopenharmony_ci * the tag even if no response is matched. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ci if (!skb) 11262306a36Sopenharmony_ci skb = nfp_ccm_reply_drop_tag(ccm, app, tag); 11362306a36Sopenharmony_ci if (err < 0) { 11462306a36Sopenharmony_ci ccm_warn(app, "%s waiting for response to 0x%02x: %d\n", 11562306a36Sopenharmony_ci err == ERESTARTSYS ? "interrupted" : "error", 11662306a36Sopenharmony_ci type, err); 11762306a36Sopenharmony_ci return ERR_PTR(err); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci if (!skb) { 12062306a36Sopenharmony_ci ccm_warn(app, "timeout waiting for response to 0x%02x\n", type); 12162306a36Sopenharmony_ci return ERR_PTR(-ETIMEDOUT); 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return skb; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistruct sk_buff * 12862306a36Sopenharmony_cinfp_ccm_communicate(struct nfp_ccm *ccm, struct sk_buff *skb, 12962306a36Sopenharmony_ci enum nfp_ccm_type type, unsigned int reply_size) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct nfp_app *app = ccm->app; 13262306a36Sopenharmony_ci struct nfp_ccm_hdr *hdr; 13362306a36Sopenharmony_ci int reply_type, tag; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci nfp_ctrl_lock(app->ctrl); 13662306a36Sopenharmony_ci tag = nfp_ccm_alloc_tag(ccm); 13762306a36Sopenharmony_ci if (tag < 0) { 13862306a36Sopenharmony_ci nfp_ctrl_unlock(app->ctrl); 13962306a36Sopenharmony_ci dev_kfree_skb_any(skb); 14062306a36Sopenharmony_ci return ERR_PTR(tag); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci hdr = (void *)skb->data; 14462306a36Sopenharmony_ci hdr->ver = NFP_CCM_ABI_VERSION; 14562306a36Sopenharmony_ci hdr->type = type; 14662306a36Sopenharmony_ci hdr->tag = cpu_to_be16(tag); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci __nfp_app_ctrl_tx(app, skb); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci nfp_ctrl_unlock(app->ctrl); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci skb = nfp_ccm_wait_reply(ccm, app, type, tag); 15362306a36Sopenharmony_ci if (IS_ERR(skb)) 15462306a36Sopenharmony_ci return skb; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci reply_type = nfp_ccm_get_type(skb); 15762306a36Sopenharmony_ci if (reply_type != __NFP_CCM_REPLY(type)) { 15862306a36Sopenharmony_ci ccm_warn(app, "cmsg drop - wrong type 0x%02x != 0x%02lx!\n", 15962306a36Sopenharmony_ci reply_type, __NFP_CCM_REPLY(type)); 16062306a36Sopenharmony_ci goto err_free; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci /* 0 reply_size means caller will do the validation */ 16362306a36Sopenharmony_ci if (reply_size && skb->len != reply_size) { 16462306a36Sopenharmony_ci ccm_warn(app, "cmsg drop - type 0x%02x wrong size %d != %d!\n", 16562306a36Sopenharmony_ci type, skb->len, reply_size); 16662306a36Sopenharmony_ci goto err_free; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return skb; 17062306a36Sopenharmony_cierr_free: 17162306a36Sopenharmony_ci dev_kfree_skb_any(skb); 17262306a36Sopenharmony_ci return ERR_PTR(-EIO); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_civoid nfp_ccm_rx(struct nfp_ccm *ccm, struct sk_buff *skb) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct nfp_app *app = ccm->app; 17862306a36Sopenharmony_ci unsigned int tag; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (unlikely(skb->len < sizeof(struct nfp_ccm_hdr))) { 18162306a36Sopenharmony_ci ccm_warn(app, "cmsg drop - too short %d!\n", skb->len); 18262306a36Sopenharmony_ci goto err_free; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci nfp_ctrl_lock(app->ctrl); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci tag = nfp_ccm_get_tag(skb); 18862306a36Sopenharmony_ci if (unlikely(!test_bit(tag, ccm->tag_allocator))) { 18962306a36Sopenharmony_ci ccm_warn(app, "cmsg drop - no one is waiting for tag %u!\n", 19062306a36Sopenharmony_ci tag); 19162306a36Sopenharmony_ci goto err_unlock; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci __skb_queue_tail(&ccm->replies, skb); 19562306a36Sopenharmony_ci wake_up_interruptible_all(&ccm->wq); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci nfp_ctrl_unlock(app->ctrl); 19862306a36Sopenharmony_ci return; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cierr_unlock: 20162306a36Sopenharmony_ci nfp_ctrl_unlock(app->ctrl); 20262306a36Sopenharmony_cierr_free: 20362306a36Sopenharmony_ci dev_kfree_skb_any(skb); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ciint nfp_ccm_init(struct nfp_ccm *ccm, struct nfp_app *app) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci ccm->app = app; 20962306a36Sopenharmony_ci skb_queue_head_init(&ccm->replies); 21062306a36Sopenharmony_ci init_waitqueue_head(&ccm->wq); 21162306a36Sopenharmony_ci return 0; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_civoid nfp_ccm_clean(struct nfp_ccm *ccm) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci WARN_ON(!skb_queue_empty(&ccm->replies)); 21762306a36Sopenharmony_ci} 218