1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 /* Copyright (C) 2016-2019 Netronome Systems, Inc. */ 3 4 #include <linux/bitops.h> 5 6 #include "ccm.h" 7 #include "nfp_app.h" 8 #include "nfp_net.h" 9 10 #define ccm_warn(app, msg...) nn_dp_warn(&(app)->ctrl->dp, msg) 11 12 #define NFP_CCM_TAG_ALLOC_SPAN (U16_MAX / 4) 13 14 static bool nfp_ccm_all_tags_busy(struct nfp_ccm *ccm) 15 { 16 u16 used_tags; 17 18 used_tags = ccm->tag_alloc_next - ccm->tag_alloc_last; 19 20 return used_tags > NFP_CCM_TAG_ALLOC_SPAN; 21 } 22 23 static int nfp_ccm_alloc_tag(struct nfp_ccm *ccm) 24 { 25 /* CCM is for FW communication which is request-reply. To make sure 26 * we don't reuse the message ID too early after timeout - limit the 27 * number of requests in flight. 28 */ 29 if (unlikely(nfp_ccm_all_tags_busy(ccm))) { 30 ccm_warn(ccm->app, "all FW request contexts busy!\n"); 31 return -EAGAIN; 32 } 33 34 WARN_ON(__test_and_set_bit(ccm->tag_alloc_next, ccm->tag_allocator)); 35 return ccm->tag_alloc_next++; 36 } 37 38 static void nfp_ccm_free_tag(struct nfp_ccm *ccm, u16 tag) 39 { 40 WARN_ON(!__test_and_clear_bit(tag, ccm->tag_allocator)); 41 42 while (!test_bit(ccm->tag_alloc_last, ccm->tag_allocator) && 43 ccm->tag_alloc_last != ccm->tag_alloc_next) 44 ccm->tag_alloc_last++; 45 } 46 47 static struct sk_buff *__nfp_ccm_reply(struct nfp_ccm *ccm, u16 tag) 48 { 49 unsigned int msg_tag; 50 struct sk_buff *skb; 51 52 skb_queue_walk(&ccm->replies, skb) { 53 msg_tag = nfp_ccm_get_tag(skb); 54 if (msg_tag == tag) { 55 nfp_ccm_free_tag(ccm, tag); 56 __skb_unlink(skb, &ccm->replies); 57 return skb; 58 } 59 } 60 61 return NULL; 62 } 63 64 static struct sk_buff * 65 nfp_ccm_reply(struct nfp_ccm *ccm, struct nfp_app *app, u16 tag) 66 { 67 struct sk_buff *skb; 68 69 nfp_ctrl_lock(app->ctrl); 70 skb = __nfp_ccm_reply(ccm, tag); 71 nfp_ctrl_unlock(app->ctrl); 72 73 return skb; 74 } 75 76 static struct sk_buff * 77 nfp_ccm_reply_drop_tag(struct nfp_ccm *ccm, struct nfp_app *app, u16 tag) 78 { 79 struct sk_buff *skb; 80 81 nfp_ctrl_lock(app->ctrl); 82 skb = __nfp_ccm_reply(ccm, tag); 83 if (!skb) 84 nfp_ccm_free_tag(ccm, tag); 85 nfp_ctrl_unlock(app->ctrl); 86 87 return skb; 88 } 89 90 static struct sk_buff * 91 nfp_ccm_wait_reply(struct nfp_ccm *ccm, struct nfp_app *app, 92 enum nfp_ccm_type type, int tag) 93 { 94 struct sk_buff *skb; 95 int i, err; 96 97 for (i = 0; i < 50; i++) { 98 udelay(4); 99 skb = nfp_ccm_reply(ccm, app, tag); 100 if (skb) 101 return skb; 102 } 103 104 err = wait_event_interruptible_timeout(ccm->wq, 105 skb = nfp_ccm_reply(ccm, app, 106 tag), 107 msecs_to_jiffies(5000)); 108 /* We didn't get a response - try last time and atomically drop 109 * the tag even if no response is matched. 110 */ 111 if (!skb) 112 skb = nfp_ccm_reply_drop_tag(ccm, app, tag); 113 if (err < 0) { 114 ccm_warn(app, "%s waiting for response to 0x%02x: %d\n", 115 err == ERESTARTSYS ? "interrupted" : "error", 116 type, err); 117 return ERR_PTR(err); 118 } 119 if (!skb) { 120 ccm_warn(app, "timeout waiting for response to 0x%02x\n", type); 121 return ERR_PTR(-ETIMEDOUT); 122 } 123 124 return skb; 125 } 126 127 struct sk_buff * 128 nfp_ccm_communicate(struct nfp_ccm *ccm, struct sk_buff *skb, 129 enum nfp_ccm_type type, unsigned int reply_size) 130 { 131 struct nfp_app *app = ccm->app; 132 struct nfp_ccm_hdr *hdr; 133 int reply_type, tag; 134 135 nfp_ctrl_lock(app->ctrl); 136 tag = nfp_ccm_alloc_tag(ccm); 137 if (tag < 0) { 138 nfp_ctrl_unlock(app->ctrl); 139 dev_kfree_skb_any(skb); 140 return ERR_PTR(tag); 141 } 142 143 hdr = (void *)skb->data; 144 hdr->ver = NFP_CCM_ABI_VERSION; 145 hdr->type = type; 146 hdr->tag = cpu_to_be16(tag); 147 148 __nfp_app_ctrl_tx(app, skb); 149 150 nfp_ctrl_unlock(app->ctrl); 151 152 skb = nfp_ccm_wait_reply(ccm, app, type, tag); 153 if (IS_ERR(skb)) 154 return skb; 155 156 reply_type = nfp_ccm_get_type(skb); 157 if (reply_type != __NFP_CCM_REPLY(type)) { 158 ccm_warn(app, "cmsg drop - wrong type 0x%02x != 0x%02lx!\n", 159 reply_type, __NFP_CCM_REPLY(type)); 160 goto err_free; 161 } 162 /* 0 reply_size means caller will do the validation */ 163 if (reply_size && skb->len != reply_size) { 164 ccm_warn(app, "cmsg drop - type 0x%02x wrong size %d != %d!\n", 165 type, skb->len, reply_size); 166 goto err_free; 167 } 168 169 return skb; 170 err_free: 171 dev_kfree_skb_any(skb); 172 return ERR_PTR(-EIO); 173 } 174 175 void nfp_ccm_rx(struct nfp_ccm *ccm, struct sk_buff *skb) 176 { 177 struct nfp_app *app = ccm->app; 178 unsigned int tag; 179 180 if (unlikely(skb->len < sizeof(struct nfp_ccm_hdr))) { 181 ccm_warn(app, "cmsg drop - too short %d!\n", skb->len); 182 goto err_free; 183 } 184 185 nfp_ctrl_lock(app->ctrl); 186 187 tag = nfp_ccm_get_tag(skb); 188 if (unlikely(!test_bit(tag, ccm->tag_allocator))) { 189 ccm_warn(app, "cmsg drop - no one is waiting for tag %u!\n", 190 tag); 191 goto err_unlock; 192 } 193 194 __skb_queue_tail(&ccm->replies, skb); 195 wake_up_interruptible_all(&ccm->wq); 196 197 nfp_ctrl_unlock(app->ctrl); 198 return; 199 200 err_unlock: 201 nfp_ctrl_unlock(app->ctrl); 202 err_free: 203 dev_kfree_skb_any(skb); 204 } 205 206 int nfp_ccm_init(struct nfp_ccm *ccm, struct nfp_app *app) 207 { 208 ccm->app = app; 209 skb_queue_head_init(&ccm->replies); 210 init_waitqueue_head(&ccm->wq); 211 return 0; 212 } 213 214 void nfp_ccm_clean(struct nfp_ccm *ccm) 215 { 216 WARN_ON(!skb_queue_empty(&ccm->replies)); 217 } 218