xref: /linux/drivers/infiniband/hw/usnic/usnic_fwd.c (revision ca55b2fef3a9373fcfc30f82fd26bc7fccbda732)
1 /*
2  * Copyright (c) 2013, Cisco Systems, Inc. All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * BSD license below:
9  *
10  *     Redistribution and use in source and binary forms, with or
11  *     without modification, are permitted provided that the following
12  *     conditions are met:
13  *
14  *      - Redistributions of source code must retain the above
15  *        copyright notice, this list of conditions and the following
16  *        disclaimer.
17  *
18  *      - Redistributions in binary form must reproduce the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer in the documentation and/or other materials
21  *        provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  *
32  */
33 #include <linux/netdevice.h>
34 #include <linux/pci.h>
35 
36 #include "enic_api.h"
37 #include "usnic_common_pkt_hdr.h"
38 #include "usnic_fwd.h"
39 #include "usnic_log.h"
40 
41 static int usnic_fwd_devcmd_locked(struct usnic_fwd_dev *ufdev, int vnic_idx,
42 					enum vnic_devcmd_cmd cmd, u64 *a0,
43 					u64 *a1)
44 {
45 	int status;
46 	struct net_device *netdev = ufdev->netdev;
47 
48 	lockdep_assert_held(&ufdev->lock);
49 
50 	status = enic_api_devcmd_proxy_by_index(netdev,
51 			vnic_idx,
52 			cmd,
53 			a0, a1,
54 			1000);
55 	if (status) {
56 		if (status == ERR_EINVAL && cmd == CMD_DEL_FILTER) {
57 			usnic_dbg("Dev %s vnic idx %u cmd %u already deleted",
58 					ufdev->name, vnic_idx, cmd);
59 		} else {
60 			usnic_err("Dev %s vnic idx %u cmd %u failed with status %d\n",
61 					ufdev->name, vnic_idx, cmd,
62 					status);
63 		}
64 	} else {
65 		usnic_dbg("Dev %s vnic idx %u cmd %u success",
66 				ufdev->name, vnic_idx, cmd);
67 	}
68 
69 	return status;
70 }
71 
72 static int usnic_fwd_devcmd(struct usnic_fwd_dev *ufdev, int vnic_idx,
73 				enum vnic_devcmd_cmd cmd, u64 *a0, u64 *a1)
74 {
75 	int status;
76 
77 	spin_lock(&ufdev->lock);
78 	status = usnic_fwd_devcmd_locked(ufdev, vnic_idx, cmd, a0, a1);
79 	spin_unlock(&ufdev->lock);
80 
81 	return status;
82 }
83 
84 struct usnic_fwd_dev *usnic_fwd_dev_alloc(struct pci_dev *pdev)
85 {
86 	struct usnic_fwd_dev *ufdev;
87 
88 	ufdev = kzalloc(sizeof(*ufdev), GFP_KERNEL);
89 	if (!ufdev)
90 		return NULL;
91 
92 	ufdev->pdev = pdev;
93 	ufdev->netdev = pci_get_drvdata(pdev);
94 	spin_lock_init(&ufdev->lock);
95 	strncpy(ufdev->name, netdev_name(ufdev->netdev),
96 			sizeof(ufdev->name) - 1);
97 
98 	return ufdev;
99 }
100 
101 void usnic_fwd_dev_free(struct usnic_fwd_dev *ufdev)
102 {
103 	kfree(ufdev);
104 }
105 
106 void usnic_fwd_set_mac(struct usnic_fwd_dev *ufdev, char mac[ETH_ALEN])
107 {
108 	spin_lock(&ufdev->lock);
109 	memcpy(&ufdev->mac, mac, sizeof(ufdev->mac));
110 	spin_unlock(&ufdev->lock);
111 }
112 
113 int usnic_fwd_add_ipaddr(struct usnic_fwd_dev *ufdev, __be32 inaddr)
114 {
115 	int status;
116 
117 	spin_lock(&ufdev->lock);
118 	if (ufdev->inaddr == 0) {
119 		ufdev->inaddr = inaddr;
120 		status = 0;
121 	} else {
122 		status = -EFAULT;
123 	}
124 	spin_unlock(&ufdev->lock);
125 
126 	return status;
127 }
128 
129 void usnic_fwd_del_ipaddr(struct usnic_fwd_dev *ufdev)
130 {
131 	spin_lock(&ufdev->lock);
132 	ufdev->inaddr = 0;
133 	spin_unlock(&ufdev->lock);
134 }
135 
136 void usnic_fwd_carrier_up(struct usnic_fwd_dev *ufdev)
137 {
138 	spin_lock(&ufdev->lock);
139 	ufdev->link_up = 1;
140 	spin_unlock(&ufdev->lock);
141 }
142 
143 void usnic_fwd_carrier_down(struct usnic_fwd_dev *ufdev)
144 {
145 	spin_lock(&ufdev->lock);
146 	ufdev->link_up = 0;
147 	spin_unlock(&ufdev->lock);
148 }
149 
150 void usnic_fwd_set_mtu(struct usnic_fwd_dev *ufdev, unsigned int mtu)
151 {
152 	spin_lock(&ufdev->lock);
153 	ufdev->mtu = mtu;
154 	spin_unlock(&ufdev->lock);
155 }
156 
157 static int usnic_fwd_dev_ready_locked(struct usnic_fwd_dev *ufdev)
158 {
159 	lockdep_assert_held(&ufdev->lock);
160 
161 	if (!ufdev->link_up)
162 		return -EPERM;
163 
164 	return 0;
165 }
166 
167 static int validate_filter_locked(struct usnic_fwd_dev *ufdev,
168 					struct filter *filter)
169 {
170 
171 	lockdep_assert_held(&ufdev->lock);
172 
173 	if (filter->type == FILTER_IPV4_5TUPLE) {
174 		if (!(filter->u.ipv4.flags & FILTER_FIELD_5TUP_DST_AD))
175 			return -EACCES;
176 		if (!(filter->u.ipv4.flags & FILTER_FIELD_5TUP_DST_PT))
177 			return -EBUSY;
178 		else if (ufdev->inaddr == 0)
179 			return -EINVAL;
180 		else if (filter->u.ipv4.dst_port == 0)
181 			return -ERANGE;
182 		else if (ntohl(ufdev->inaddr) != filter->u.ipv4.dst_addr)
183 			return -EFAULT;
184 		else
185 			return 0;
186 	}
187 
188 	return 0;
189 }
190 
191 static void fill_tlv(struct filter_tlv *tlv, struct filter *filter,
192 		struct filter_action *action)
193 {
194 	tlv->type = CLSF_TLV_FILTER;
195 	tlv->length = sizeof(struct filter);
196 	*((struct filter *)&tlv->val) = *filter;
197 
198 	tlv = (struct filter_tlv *)((char *)tlv + sizeof(struct filter_tlv) +
199 			sizeof(struct filter));
200 	tlv->type = CLSF_TLV_ACTION;
201 	tlv->length = sizeof(struct filter_action);
202 	*((struct filter_action *)&tlv->val) = *action;
203 }
204 
205 struct usnic_fwd_flow*
206 usnic_fwd_alloc_flow(struct usnic_fwd_dev *ufdev, struct filter *filter,
207 				struct usnic_filter_action *uaction)
208 {
209 	struct filter_tlv *tlv;
210 	struct pci_dev *pdev;
211 	struct usnic_fwd_flow *flow;
212 	uint64_t a0, a1;
213 	uint64_t tlv_size;
214 	dma_addr_t tlv_pa;
215 	int status;
216 
217 	pdev = ufdev->pdev;
218 	tlv_size = (2*sizeof(struct filter_tlv) + sizeof(struct filter) +
219 			sizeof(struct filter_action));
220 
221 	flow = kzalloc(sizeof(*flow), GFP_ATOMIC);
222 	if (!flow)
223 		return ERR_PTR(-ENOMEM);
224 
225 	tlv = pci_alloc_consistent(pdev, tlv_size, &tlv_pa);
226 	if (!tlv) {
227 		usnic_err("Failed to allocate memory\n");
228 		status = -ENOMEM;
229 		goto out_free_flow;
230 	}
231 
232 	fill_tlv(tlv, filter, &uaction->action);
233 
234 	spin_lock(&ufdev->lock);
235 	status = usnic_fwd_dev_ready_locked(ufdev);
236 	if (status) {
237 		usnic_err("Forwarding dev %s not ready with status %d\n",
238 				ufdev->name, status);
239 		goto out_free_tlv;
240 	}
241 
242 	status = validate_filter_locked(ufdev, filter);
243 	if (status) {
244 		usnic_err("Failed to validate filter with status %d\n",
245 				status);
246 		goto out_free_tlv;
247 	}
248 
249 	/* Issue Devcmd */
250 	a0 = tlv_pa;
251 	a1 = tlv_size;
252 	status = usnic_fwd_devcmd_locked(ufdev, uaction->vnic_idx,
253 						CMD_ADD_FILTER, &a0, &a1);
254 	if (status) {
255 		usnic_err("VF %s Filter add failed with status:%d",
256 				ufdev->name, status);
257 		status = -EFAULT;
258 		goto out_free_tlv;
259 	} else {
260 		usnic_dbg("VF %s FILTER ID:%llu", ufdev->name, a0);
261 	}
262 
263 	flow->flow_id = (uint32_t) a0;
264 	flow->vnic_idx = uaction->vnic_idx;
265 	flow->ufdev = ufdev;
266 
267 out_free_tlv:
268 	spin_unlock(&ufdev->lock);
269 	pci_free_consistent(pdev, tlv_size, tlv, tlv_pa);
270 	if (!status)
271 		return flow;
272 out_free_flow:
273 	kfree(flow);
274 	return ERR_PTR(status);
275 }
276 
277 int usnic_fwd_dealloc_flow(struct usnic_fwd_flow *flow)
278 {
279 	int status;
280 	u64 a0, a1;
281 
282 	a0 = flow->flow_id;
283 
284 	status = usnic_fwd_devcmd(flow->ufdev, flow->vnic_idx,
285 					CMD_DEL_FILTER, &a0, &a1);
286 	if (status) {
287 		if (status == ERR_EINVAL) {
288 			usnic_dbg("Filter %u already deleted for VF Idx %u pf: %s status: %d",
289 					flow->flow_id, flow->vnic_idx,
290 					flow->ufdev->name, status);
291 		} else {
292 			usnic_err("PF %s VF Idx %u Filter: %u FILTER DELETE failed with status %d",
293 					flow->ufdev->name, flow->vnic_idx,
294 					flow->flow_id, status);
295 		}
296 		status = 0;
297 		/*
298 		 * Log the error and fake success to the caller because if
299 		 * a flow fails to be deleted in the firmware, it is an
300 		 * unrecoverable error.
301 		 */
302 	} else {
303 		usnic_dbg("PF %s VF Idx %u Filter: %u FILTER DELETED",
304 				flow->ufdev->name, flow->vnic_idx,
305 				flow->flow_id);
306 	}
307 
308 	kfree(flow);
309 	return status;
310 }
311 
312 int usnic_fwd_enable_qp(struct usnic_fwd_dev *ufdev, int vnic_idx, int qp_idx)
313 {
314 	int status;
315 	struct net_device *pf_netdev;
316 	u64 a0, a1;
317 
318 	pf_netdev = ufdev->netdev;
319 	a0 = qp_idx;
320 	a1 = CMD_QP_RQWQ;
321 
322 	status = usnic_fwd_devcmd(ufdev, vnic_idx, CMD_QP_ENABLE,
323 						&a0, &a1);
324 	if (status) {
325 		usnic_err("PF %s VNIC Index %u RQ Index: %u ENABLE Failed with status %d",
326 				netdev_name(pf_netdev),
327 				vnic_idx,
328 				qp_idx,
329 				status);
330 	} else {
331 		usnic_dbg("PF %s VNIC Index %u RQ Index: %u ENABLED",
332 				netdev_name(pf_netdev),
333 				vnic_idx, qp_idx);
334 	}
335 
336 	return status;
337 }
338 
339 int usnic_fwd_disable_qp(struct usnic_fwd_dev *ufdev, int vnic_idx, int qp_idx)
340 {
341 	int status;
342 	u64 a0, a1;
343 	struct net_device *pf_netdev;
344 
345 	pf_netdev = ufdev->netdev;
346 	a0 = qp_idx;
347 	a1 = CMD_QP_RQWQ;
348 
349 	status = usnic_fwd_devcmd(ufdev, vnic_idx, CMD_QP_DISABLE,
350 			&a0, &a1);
351 	if (status) {
352 		usnic_err("PF %s VNIC Index %u RQ Index: %u DISABLE Failed with status %d",
353 				netdev_name(pf_netdev),
354 				vnic_idx,
355 				qp_idx,
356 				status);
357 	} else {
358 		usnic_dbg("PF %s VNIC Index %u RQ Index: %u DISABLED",
359 				netdev_name(pf_netdev),
360 				vnic_idx,
361 				qp_idx);
362 	}
363 
364 	return status;
365 }
366