net/ice/base: fix reference count on VSI list update
[dpdk.git] / drivers / net / cxgbe / clip_tbl.c
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  */
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  */
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  */
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_atomic32_dec_and_test(&ce->refcnt)) {
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  */
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_atomic32_read(&e->refcnt) == 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
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_atomic32_read(&ce->refcnt)) {
118                         rte_memcpy(ce->addr, lip, sizeof(ce->addr));
119                         if (v6) {
120                                 ce->type = FILTER_TYPE_IPV6;
121                                 rte_atomic32_set(&ce->refcnt, 1);
122                                 ret = clip6_get_mbox(dev, lip);
123                                 if (ret)
124                                         dev_debug(adap,
125                                                   "CLIP FW ADD CMD failed: %d",
126                                                   ret);
127                         } else {
128                                 ce->type = FILTER_TYPE_IPV4;
129                         }
130                 } else {
131                         rte_atomic32_inc(&ce->refcnt);
132                 }
133                 t4_os_unlock(&ce->lock);
134         }
135         t4_os_write_unlock(&ctbl->lock);
136
137         return ret ? NULL : ce;
138 }
139
140 /**
141  * cxgbe_clip_alloc - Allocate a IPV6 CLIP entry
142  * @dev: rte_eth_dev pointer
143  * @lip: IPV6 address to add
144  * Returns pointer to the CLIP entry created
145  *
146  * Allocates a IPV6 CLIP entry to be used for a filter rule.
147  */
148 struct clip_entry *cxgbe_clip_alloc(struct rte_eth_dev *dev, u32 *lip)
149 {
150         return t4_clip_alloc(dev, lip, FILTER_TYPE_IPV6);
151 }
152
153 /**
154  * Initialize CLIP Table
155  */
156 struct clip_tbl *t4_init_clip_tbl(unsigned int clipt_start,
157                                   unsigned int clipt_end)
158 {
159         unsigned int clipt_size;
160         struct clip_tbl *ctbl;
161         unsigned int i;
162
163         if (clipt_start >= clipt_end)
164                 return NULL;
165
166         clipt_size = clipt_end - clipt_start + 1;
167
168         ctbl = t4_os_alloc(sizeof(*ctbl) +
169                            clipt_size * sizeof(struct clip_entry));
170         if (!ctbl)
171                 return NULL;
172
173         ctbl->clipt_start = clipt_start;
174         ctbl->clipt_size = clipt_size;
175
176         t4_os_rwlock_init(&ctbl->lock);
177
178         for (i = 0; i < ctbl->clipt_size; i++) {
179                 t4_os_lock_init(&ctbl->cl_list[i].lock);
180                 rte_atomic32_set(&ctbl->cl_list[i].refcnt, 0);
181         }
182
183         return ctbl;
184 }
185
186 /**
187  * Cleanup CLIP Table
188  */
189 void t4_cleanup_clip_tbl(struct adapter *adap)
190 {
191         if (adap->clipt)
192                 t4_os_free(adap->clipt);
193 }