xref: /dpdk/drivers/net/cxgbe/clip_tbl.c (revision e12a0166c80f65e35408f4715b2f3a60763c3741)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2018 Chelsio Communications.
3  * All rights reserved.
4  */
5 
6 #include "base/common.h"
7 #include "clip_tbl.h"
8 
9 /**
10  * Allocate clip entry in HW with associated IPV4/IPv6 address
11  */
clip6_get_mbox(const struct rte_eth_dev * dev,const u32 * lip)12 static int clip6_get_mbox(const struct rte_eth_dev *dev, const u32 *lip)
13 {
14 	struct adapter *adap = ethdev2adap(dev);
15 	struct fw_clip_cmd c;
16 	u64 hi = ((u64)lip[1]) << 32 | lip[0];
17 	u64 lo = ((u64)lip[3]) << 32 | lip[2];
18 
19 	memset(&c, 0, sizeof(c));
20 	c.op_to_write = cpu_to_be32(V_FW_CMD_OP(FW_CLIP_CMD) |
21 				    F_FW_CMD_REQUEST | F_FW_CMD_WRITE);
22 	c.alloc_to_len16 = cpu_to_be32(F_FW_CLIP_CMD_ALLOC | FW_LEN16(c));
23 	c.ip_hi = hi;
24 	c.ip_lo = lo;
25 	return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
26 }
27 
28 /**
29  * Delete clip entry in HW having the associated IPV4/IPV6 address
30  */
clip6_release_mbox(const struct rte_eth_dev * dev,const u32 * lip)31 static int clip6_release_mbox(const struct rte_eth_dev *dev, const u32 *lip)
32 {
33 	struct adapter *adap = ethdev2adap(dev);
34 	struct fw_clip_cmd c;
35 	u64 hi = ((u64)lip[1]) << 32 | lip[0];
36 	u64 lo = ((u64)lip[3]) << 32 | lip[2];
37 
38 	memset(&c, 0, sizeof(c));
39 	c.op_to_write = cpu_to_be32(V_FW_CMD_OP(FW_CLIP_CMD) |
40 				    F_FW_CMD_REQUEST | F_FW_CMD_READ);
41 	c.alloc_to_len16 = cpu_to_be32(F_FW_CLIP_CMD_FREE | FW_LEN16(c));
42 	c.ip_hi = hi;
43 	c.ip_lo = lo;
44 	return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
45 }
46 
47 /**
48  * cxgbe_clip_release - Release associated CLIP entry
49  * @ce: clip entry to release
50  *
51  * Releases ref count and frees up a clip entry from CLIP table
52  */
cxgbe_clip_release(struct rte_eth_dev * dev,struct clip_entry * ce)53 void cxgbe_clip_release(struct rte_eth_dev *dev, struct clip_entry *ce)
54 {
55 	int ret;
56 
57 	t4_os_lock(&ce->lock);
58 	if (rte_atomic_fetch_sub_explicit(&ce->refcnt, 1, rte_memory_order_relaxed) - 1 == 0) {
59 		ret = clip6_release_mbox(dev, ce->addr);
60 		if (ret)
61 			dev_debug(adap, "CLIP FW DEL CMD failed: %d", ret);
62 	}
63 	t4_os_unlock(&ce->lock);
64 }
65 
66 /**
67  * find_or_alloc_clipe - Find/Allocate a free CLIP entry
68  * @c: CLIP table
69  * @lip: IPV4/IPV6 address to compare/add
70  * Returns pointer to the IPV4/IPV6 entry found/created
71  *
72  * Finds/Allocates an CLIP entry to be used for a filter rule.
73  */
find_or_alloc_clipe(struct clip_tbl * c,const u32 * lip)74 static struct clip_entry *find_or_alloc_clipe(struct clip_tbl *c,
75 					      const u32 *lip)
76 {
77 	struct clip_entry *end, *e;
78 	struct clip_entry *first_free = NULL;
79 	unsigned int clipt_size = c->clipt_size;
80 
81 	for (e = &c->cl_list[0], end = &c->cl_list[clipt_size]; e != end; ++e) {
82 		if (rte_atomic_load_explicit(&e->refcnt, rte_memory_order_relaxed) == 0) {
83 			if (!first_free)
84 				first_free = e;
85 		} else {
86 			if (memcmp(lip, e->addr, sizeof(e->addr)) == 0)
87 				goto exists;
88 		}
89 	}
90 
91 	if (first_free) {
92 		e = first_free;
93 		goto exists;
94 	}
95 
96 	return NULL;
97 
98 exists:
99 	return e;
100 }
101 
t4_clip_alloc(struct rte_eth_dev * dev,u32 * lip,u8 v6)102 static struct clip_entry *t4_clip_alloc(struct rte_eth_dev *dev,
103 					u32 *lip, u8 v6)
104 {
105 	struct adapter *adap = ethdev2adap(dev);
106 	struct clip_tbl *ctbl = adap->clipt;
107 	struct clip_entry *ce;
108 	int ret = 0;
109 
110 	if (!ctbl)
111 		return NULL;
112 
113 	t4_os_write_lock(&ctbl->lock);
114 	ce = find_or_alloc_clipe(ctbl, lip);
115 	if (ce) {
116 		t4_os_lock(&ce->lock);
117 		if (rte_atomic_load_explicit(&ce->refcnt, rte_memory_order_relaxed) == 0) {
118 			rte_memcpy(ce->addr, lip, sizeof(ce->addr));
119 			if (v6) {
120 				ce->type = FILTER_TYPE_IPV6;
121 				rte_atomic_store_explicit(&ce->refcnt, 1,
122 						 rte_memory_order_relaxed);
123 				ret = clip6_get_mbox(dev, lip);
124 				if (ret)
125 					dev_debug(adap,
126 						  "CLIP FW ADD CMD failed: %d",
127 						  ret);
128 			} else {
129 				ce->type = FILTER_TYPE_IPV4;
130 			}
131 		} else {
132 			rte_atomic_fetch_add_explicit(&ce->refcnt, 1, rte_memory_order_relaxed);
133 		}
134 		t4_os_unlock(&ce->lock);
135 	}
136 	t4_os_write_unlock(&ctbl->lock);
137 
138 	return ret ? NULL : ce;
139 }
140 
141 /**
142  * cxgbe_clip_alloc - Allocate a IPV6 CLIP entry
143  * @dev: rte_eth_dev pointer
144  * @lip: IPV6 address to add
145  * Returns pointer to the CLIP entry created
146  *
147  * Allocates a IPV6 CLIP entry to be used for a filter rule.
148  */
cxgbe_clip_alloc(struct rte_eth_dev * dev,u32 * lip)149 struct clip_entry *cxgbe_clip_alloc(struct rte_eth_dev *dev, u32 *lip)
150 {
151 	return t4_clip_alloc(dev, lip, FILTER_TYPE_IPV6);
152 }
153 
154 /**
155  * Initialize CLIP Table
156  */
t4_init_clip_tbl(unsigned int clipt_start,unsigned int clipt_end)157 struct clip_tbl *t4_init_clip_tbl(unsigned int clipt_start,
158 				  unsigned int clipt_end)
159 {
160 	unsigned int clipt_size;
161 	struct clip_tbl *ctbl;
162 	unsigned int i;
163 
164 	if (clipt_start >= clipt_end)
165 		return NULL;
166 
167 	clipt_size = clipt_end - clipt_start + 1;
168 
169 	ctbl = t4_os_alloc(sizeof(*ctbl) +
170 			   clipt_size * sizeof(struct clip_entry));
171 	if (!ctbl)
172 		return NULL;
173 
174 	ctbl->clipt_start = clipt_start;
175 	ctbl->clipt_size = clipt_size;
176 
177 	t4_os_rwlock_init(&ctbl->lock);
178 
179 	for (i = 0; i < ctbl->clipt_size; i++) {
180 		t4_os_lock_init(&ctbl->cl_list[i].lock);
181 		ctbl->cl_list[i].refcnt = 0;
182 	}
183 
184 	return ctbl;
185 }
186 
187 /**
188  * Cleanup CLIP Table
189  */
t4_cleanup_clip_tbl(struct adapter * adap)190 void t4_cleanup_clip_tbl(struct adapter *adap)
191 {
192 	if (adap->clipt)
193 		t4_os_free(adap->clipt);
194 }
195