]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/infiniband/hw/usnic/usnic_fwd.c
Merge branch 'libnvdimm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdim...
[karo-tx-linux.git] / drivers / infiniband / hw / usnic / usnic_fwd.c
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 }