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