aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/cxgbe/clip_tbl.c
blob: 5e4dc527043b8e438ac0b556aabd44b2975d7d0d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright(c) 2018 Chelsio Communications.
 * All rights reserved.
 */

#include "common.h"
#include "clip_tbl.h"

/**
 * Allocate clip entry in HW with associated IPV4/IPv6 address
 */
static int clip6_get_mbox(const struct rte_eth_dev *dev, const u32 *lip)
{
	struct adapter *adap = ethdev2adap(dev);
	struct fw_clip_cmd c;
	u64 hi = ((u64)lip[1]) << 32 | lip[0];
	u64 lo = ((u64)lip[3]) << 32 | lip[2];

	memset(&c, 0, sizeof(c));
	c.op_to_write = cpu_to_be32(V_FW_CMD_OP(FW_CLIP_CMD) |
				    F_FW_CMD_REQUEST | F_FW_CMD_WRITE);
	c.alloc_to_len16 = cpu_to_be32(F_FW_CLIP_CMD_ALLOC | FW_LEN16(c));
	c.ip_hi = hi;
	c.ip_lo = lo;
	return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
}

/**
 * Delete clip entry in HW having the associated IPV4/IPV6 address
 */
static int clip6_release_mbox(const struct rte_eth_dev *dev, const u32 *lip)
{
	struct adapter *adap = ethdev2adap(dev);
	struct fw_clip_cmd c;
	u64 hi = ((u64)lip[1]) << 32 | lip[0];
	u64 lo = ((u64)lip[3]) << 32 | lip[2];

	memset(&c, 0, sizeof(c));
	c.op_to_write = cpu_to_be32(V_FW_CMD_OP(FW_CLIP_CMD) |
				    F_FW_CMD_REQUEST | F_FW_CMD_READ);
	c.alloc_to_len16 = cpu_to_be32(F_FW_CLIP_CMD_FREE | FW_LEN16(c));
	c.ip_hi = hi;
	c.ip_lo = lo;
	return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
}

/**
 * cxgbe_clip_release - Release associated CLIP entry
 * @ce: clip entry to release
 *
 * Releases ref count and frees up a clip entry from CLIP table
 */
void cxgbe_clip_release(struct rte_eth_dev *dev, struct clip_entry *ce)
{
	int ret;

	t4_os_lock(&ce->lock);
	if (rte_atomic32_dec_and_test(&ce->refcnt)) {
		ret = clip6_release_mbox(dev, ce->addr);
		if (ret)
			dev_debug(adap, "CLIP FW DEL CMD failed: %d", ret);
	}
	t4_os_unlock(&ce->lock);
}

/**
 * find_or_alloc_clipe - Find/Allocate a free CLIP entry
 * @c: CLIP table
 * @lip: IPV4/IPV6 address to compare/add
 * Returns pointer to the IPV4/IPV6 entry found/created
 *
 * Finds/Allocates an CLIP entry to be used for a filter rule.
 */
static struct clip_entry *find_or_alloc_clipe(struct clip_tbl *c,
					      const u32 *lip)
{
	struct clip_entry *end, *e;
	struct clip_entry *first_free = NULL;
	unsigned int clipt_size = c->clipt_size;

	for (e = &c->cl_list[0], end = &c->cl_list[clipt_size]; e != end; ++e) {
		if (rte_atomic32_read(&e->refcnt) == 0) {
			if (!first_free)
				first_free = e;
		} else {
			if (memcmp(lip, e->addr, sizeof(e->addr)) == 0)
				goto exists;
		}
	}

	if (first_free) {
		e = first_free;
		goto exists;
	}

	return NULL;

exists:
	return e;
}

static struct clip_entry *t4_clip_alloc(struct rte_eth_dev *dev,
					u32 *lip, u8 v6)
{
	struct adapter *adap = ethdev2adap(dev);
	struct clip_tbl *ctbl = adap->clipt;
	struct clip_entry *ce;
	int ret = 0;

	if (!ctbl)
		return NULL;

	t4_os_write_lock(&ctbl->lock);
	ce = find_or_alloc_clipe(ctbl, lip);
	if (ce) {
		t4_os_lock(&ce->lock);
		if (!rte_atomic32_read(&ce->refcnt)) {
			rte_memcpy(ce->addr, lip, sizeof(ce->addr));
			if (v6) {
				ce->type = FILTER_TYPE_IPV6;
				rte_atomic32_set(&ce->refcnt, 1);
				ret = clip6_get_mbox(dev, lip);
				if (ret)
					dev_debug(adap,
						  "CLIP FW ADD CMD failed: %d",
						  ret);
			} else {
				ce->type = FILTER_TYPE_IPV4;
			}
		} else {
			rte_atomic32_inc(&ce->refcnt);
		}
		t4_os_unlock(&ce->lock);
	}
	t4_os_write_unlock(&ctbl->lock);

	return ret ? NULL : ce;
}

/**
 * cxgbe_clip_alloc - Allocate a IPV6 CLIP entry
 * @dev: rte_eth_dev pointer
 * @lip: IPV6 address to add
 * Returns pointer to the CLIP entry created
 *
 * Allocates a IPV6 CLIP entry to be used for a filter rule.
 */
struct clip_entry *cxgbe_clip_alloc(struct rte_eth_dev *dev, u32 *lip)
{
	return t4_clip_alloc(dev, lip, FILTER_TYPE_IPV6);
}

/**
 * Initialize CLIP Table
 */
struct clip_tbl *t4_init_clip_tbl(unsigned int clipt_start,
				  unsigned int clipt_end)
{
	unsigned int clipt_size;
	struct clip_tbl *ctbl;
	unsigned int i;

	if (clipt_start >= clipt_end)
		return NULL;

	clipt_size = clipt_end - clipt_start + 1;

	ctbl = t4_os_alloc(sizeof(*ctbl) +
			   clipt_size * sizeof(struct clip_entry));
	if (!ctbl)
		return NULL;

	ctbl->clipt_start = clipt_start;
	ctbl->clipt_size = clipt_size;

	t4_os_rwlock_init(&ctbl->lock);

	for (i = 0; i < ctbl->clipt_size; i++) {
		t4_os_lock_init(&ctbl->cl_list[i].lock);
		rte_atomic32_set(&ctbl->cl_list[i].refcnt, 0);
	}

	return ctbl;
}

/**
 * Cleanup CLIP Table
 */
void t4_cleanup_clip_tbl(struct adapter *adap)
{
	if (adap->clipt)
		t4_os_free(adap->clipt);
}