aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/infiniband
diff options
context:
space:
mode:
authorUpinder Malhi <umalhi@cisco.com>2014-01-09 17:48:07 -0500
committerRoland Dreier <roland@purestorage.com>2014-01-14 03:44:41 -0500
commit2183b990b67b761f81c68a18f60df028e080cf05 (patch)
treea4584a808fa389bd51394b4e8c1ef5fe2979fe48 /drivers/infiniband
parent301a0dd68e5ddd22d992a58f466b621987d9df3b (diff)
IB/usnic: Push all forwarding state to usnic_fwd.[hc]
Push all of the usnic device forwarding state - such as mtu, mac - to usnic_fwd_dev. Furthermore, usnic_fwd.h exposes a improved interface for rest of the usnic code. The primary improvement is that usnic_fwd.h's flow management interface takes in high-level *filter* and *action* structures now, instead of low-level paramaters such as vnic_idx, rq_idx. Signed-off-by: Upinder Malhi <umalhi@cisco.com> Signed-off-by: Roland Dreier <roland@purestorage.com>
Diffstat (limited to 'drivers/infiniband')
-rw-r--r--drivers/infiniband/hw/usnic/usnic_fwd.c269
-rw-r--r--drivers/infiniband/hw/usnic/usnic_fwd.h68
2 files changed, 212 insertions, 125 deletions
diff --git a/drivers/infiniband/hw/usnic/usnic_fwd.c b/drivers/infiniband/hw/usnic/usnic_fwd.c
index 8e42216362e7..33fdd771e599 100644
--- a/drivers/infiniband/hw/usnic/usnic_fwd.c
+++ b/drivers/infiniband/hw/usnic/usnic_fwd.c
@@ -23,6 +23,49 @@
23#include "usnic_fwd.h" 23#include "usnic_fwd.h"
24#include "usnic_log.h" 24#include "usnic_log.h"
25 25
26static 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
57static 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
26struct usnic_fwd_dev *usnic_fwd_dev_alloc(struct pci_dev *pdev) 69struct usnic_fwd_dev *usnic_fwd_dev_alloc(struct pci_dev *pdev)
27{ 70{
28 struct usnic_fwd_dev *ufdev; 71 struct usnic_fwd_dev *ufdev;
@@ -34,6 +77,8 @@ struct usnic_fwd_dev *usnic_fwd_dev_alloc(struct pci_dev *pdev)
34 ufdev->pdev = pdev; 77 ufdev->pdev = pdev;
35 ufdev->netdev = pci_get_drvdata(pdev); 78 ufdev->netdev = pci_get_drvdata(pdev);
36 spin_lock_init(&ufdev->lock); 79 spin_lock_init(&ufdev->lock);
80 strncpy(ufdev->name, netdev_name(ufdev->netdev),
81 sizeof(ufdev->name) - 1);
37 82
38 return ufdev; 83 return ufdev;
39} 84}
@@ -43,200 +88,208 @@ void usnic_fwd_dev_free(struct usnic_fwd_dev *ufdev)
43 kfree(ufdev); 88 kfree(ufdev);
44} 89}
45 90
46static int usnic_fwd_devcmd(struct usnic_fwd_dev *ufdev, int vnic_idx, 91void usnic_fwd_set_mac(struct usnic_fwd_dev *ufdev, char mac[ETH_ALEN])
47 enum vnic_devcmd_cmd cmd, u64 *a0, u64 *a1)
48{ 92{
49 int status; 93 spin_lock(&ufdev->lock);
50 struct net_device *netdev = ufdev->netdev; 94 memcpy(&ufdev->mac, mac, sizeof(ufdev->mac));
95 spin_unlock(&ufdev->lock);
96}
51 97
98void usnic_fwd_carrier_up(struct usnic_fwd_dev *ufdev)
99{
52 spin_lock(&ufdev->lock); 100 spin_lock(&ufdev->lock);
53 status = enic_api_devcmd_proxy_by_index(netdev, 101 ufdev->link_up = 1;
54 vnic_idx,
55 cmd,
56 a0, a1,
57 1000);
58 spin_unlock(&ufdev->lock); 102 spin_unlock(&ufdev->lock);
59 if (status) { 103}
60 if (status == ERR_EINVAL && cmd == CMD_DEL_FILTER) {
61 usnic_dbg("Dev %s vnic idx %u cmd %u already deleted",
62 netdev_name(netdev), vnic_idx, cmd);
63 } else {
64 usnic_err("Dev %s vnic idx %u cmd %u failed with status %d\n",
65 netdev_name(netdev), vnic_idx, cmd,
66 status);
67 }
68 } else {
69 usnic_dbg("Dev %s vnic idx %u cmd %u success",
70 netdev_name(netdev), vnic_idx,
71 cmd);
72 }
73 104
74 return status; 105void usnic_fwd_carrier_down(struct usnic_fwd_dev *ufdev)
106{
107 spin_lock(&ufdev->lock);
108 ufdev->link_up = 0;
109 spin_unlock(&ufdev->lock);
110}
111
112void usnic_fwd_set_mtu(struct usnic_fwd_dev *ufdev, unsigned int mtu)
113{
114 spin_lock(&ufdev->lock);
115 ufdev->mtu = mtu;
116 spin_unlock(&ufdev->lock);
117}
118
119static int usnic_fwd_dev_ready_locked(struct usnic_fwd_dev *ufdev)
120{
121 lockdep_assert_held(&ufdev->lock);
122
123 if (!ufdev->link_up)
124 return -EPERM;
125
126 return 0;
127}
128
129static void fill_tlv(struct filter_tlv *tlv, struct filter *filter,
130 struct filter_action *action)
131{
132 tlv->type = CLSF_TLV_FILTER;
133 tlv->length = sizeof(struct filter);
134 *((struct filter *)&tlv->val) = *filter;
135
136 tlv = (struct filter_tlv *)((char *)tlv + sizeof(struct filter_tlv) +
137 sizeof(struct filter));
138 tlv->type = CLSF_TLV_ACTION;
139 tlv->length = sizeof(struct filter_action);
140 *((struct filter_action *)&tlv->val) = *action;
75} 141}
76 142
77int usnic_fwd_add_usnic_filter(struct usnic_fwd_dev *ufdev, int vnic_idx, 143struct usnic_fwd_flow*
78 int rq_idx, struct usnic_fwd_filter *fwd_filter, 144usnic_fwd_alloc_flow(struct usnic_fwd_dev *ufdev, struct filter *filter,
79 struct usnic_fwd_filter_hndl **filter_hndl) 145 struct usnic_filter_action *uaction)
80{ 146{
81 struct filter_tlv *tlv, *tlv_va; 147 struct filter_tlv *tlv;
82 struct filter *filter;
83 struct filter_action *action;
84 struct pci_dev *pdev; 148 struct pci_dev *pdev;
85 struct usnic_fwd_filter_hndl *usnic_filter_hndl; 149 struct usnic_fwd_flow *flow;
86 int status; 150 uint64_t a0, a1;
87 u64 a0, a1; 151 uint64_t tlv_size;
88 u64 tlv_size;
89 dma_addr_t tlv_pa; 152 dma_addr_t tlv_pa;
153 int status;
90 154
91 pdev = ufdev->pdev; 155 pdev = ufdev->pdev;
92 tlv_size = (2*sizeof(struct filter_tlv) + 156 tlv_size = (2*sizeof(struct filter_tlv) + sizeof(struct filter) +
93 sizeof(struct filter) + 157 sizeof(struct filter_action));
94 sizeof(struct filter_action)); 158
159 flow = kzalloc(sizeof(*flow), GFP_ATOMIC);
160 if (!flow)
161 return ERR_PTR(-ENOMEM);
162
95 tlv = pci_alloc_consistent(pdev, tlv_size, &tlv_pa); 163 tlv = pci_alloc_consistent(pdev, tlv_size, &tlv_pa);
96 if (!tlv) { 164 if (!tlv) {
97 usnic_err("Failed to allocate memory\n"); 165 usnic_err("Failed to allocate memory\n");
98 return -ENOMEM; 166 status = -ENOMEM;
167 goto out_free_flow;
99 } 168 }
100 169
101 usnic_filter_hndl = kzalloc(sizeof(*usnic_filter_hndl), GFP_ATOMIC); 170 fill_tlv(tlv, filter, &uaction->action);
102 if (!usnic_filter_hndl) { 171
103 usnic_err("Failed to allocate memory for hndl\n"); 172 spin_lock(&ufdev->lock);
104 pci_free_consistent(pdev, tlv_size, tlv, tlv_pa); 173 status = usnic_fwd_dev_ready_locked(ufdev);
105 return -ENOMEM; 174 if (status) {
175 usnic_err("Forwarding dev %s not ready with status %d\n",
176 ufdev->name, status);
177 goto out_free_tlv;
106 } 178 }
107 179
108 tlv_va = tlv; 180 /* Issue Devcmd */
109 a0 = tlv_pa; 181 a0 = tlv_pa;
110 a1 = tlv_size; 182 a1 = tlv_size;
111 memset(tlv, 0, tlv_size); 183 status = usnic_fwd_devcmd_locked(ufdev, uaction->vnic_idx,
112 tlv->type = CLSF_TLV_FILTER; 184 CMD_ADD_FILTER, &a0, &a1);
113 tlv->length = sizeof(struct filter);
114 filter = (struct filter *)&tlv->val;
115 filter->type = FILTER_USNIC_ID;
116 filter->u.usnic.ethtype = USNIC_ROCE_ETHERTYPE;
117 filter->u.usnic.flags = FILTER_FIELD_USNIC_ETHTYPE |
118 FILTER_FIELD_USNIC_ID |
119 FILTER_FIELD_USNIC_PROTO;
120 filter->u.usnic.proto_version = (USNIC_ROCE_GRH_VER <<
121 USNIC_ROCE_GRH_VER_SHIFT)
122 | USNIC_PROTO_VER;
123 filter->u.usnic.usnic_id = fwd_filter->port_num;
124 tlv = (struct filter_tlv *)((char *)tlv + sizeof(struct filter_tlv) +
125 sizeof(struct filter));
126 tlv->type = CLSF_TLV_ACTION;
127 tlv->length = sizeof(struct filter_action);
128 action = (struct filter_action *)&tlv->val;
129 action->type = FILTER_ACTION_RQ_STEERING;
130 action->u.rq_idx = rq_idx;
131
132 status = usnic_fwd_devcmd(ufdev, vnic_idx, CMD_ADD_FILTER, &a0, &a1);
133 pci_free_consistent(pdev, tlv_size, tlv_va, tlv_pa);
134 if (status) { 185 if (status) {
135 usnic_err("VF %s Filter add failed with status:%d", 186 usnic_err("VF %s Filter add failed with status:%d",
136 pci_name(pdev), 187 ufdev->name, status);
137 status); 188 status = -EFAULT;
138 kfree(usnic_filter_hndl); 189 goto out_free_tlv;
139 return status;
140 } else { 190 } else {
141 usnic_dbg("VF %s FILTER ID:%u", 191 usnic_dbg("VF %s FILTER ID:%llu", ufdev->name, a0);
142 pci_name(pdev),
143 (u32)a0);
144 } 192 }
145 193
146 usnic_filter_hndl->type = FILTER_USNIC_ID; 194 flow->flow_id = (uint32_t) a0;
147 usnic_filter_hndl->id = (u32)a0; 195 flow->vnic_idx = uaction->vnic_idx;
148 usnic_filter_hndl->vnic_idx = vnic_idx; 196 flow->ufdev = ufdev;
149 usnic_filter_hndl->ufdev = ufdev;
150 usnic_filter_hndl->filter = fwd_filter;
151 *filter_hndl = usnic_filter_hndl;
152 197
153 return status; 198out_free_tlv:
199 spin_unlock(&ufdev->lock);
200 pci_free_consistent(pdev, tlv_size, tlv, tlv_pa);
201 if (!status)
202 return flow;
203out_free_flow:
204 kfree(flow);
205 return ERR_PTR(status);
154} 206}
155 207
156int usnic_fwd_del_filter(struct usnic_fwd_filter_hndl *filter_hndl) 208int usnic_fwd_dealloc_flow(struct usnic_fwd_flow *flow)
157{ 209{
158 int status; 210 int status;
159 u64 a0, a1; 211 u64 a0, a1;
160 struct net_device *netdev;
161 212
162 netdev = filter_hndl->ufdev->netdev; 213 a0 = flow->flow_id;
163 a0 = filter_hndl->id;
164 214
165 status = usnic_fwd_devcmd(filter_hndl->ufdev, filter_hndl->vnic_idx, 215 status = usnic_fwd_devcmd(flow->ufdev, flow->vnic_idx,
166 CMD_DEL_FILTER, &a0, &a1); 216 CMD_DEL_FILTER, &a0, &a1);
167 if (status) { 217 if (status) {
168 if (status == ERR_EINVAL) { 218 if (status == ERR_EINVAL) {
169 usnic_dbg("Filter %u already deleted for VF Idx %u pf: %s status: %d", 219 usnic_dbg("Filter %u already deleted for VF Idx %u pf: %s status: %d",
170 filter_hndl->id, filter_hndl->vnic_idx, 220 flow->flow_id, flow->vnic_idx,
171 netdev_name(netdev), status); 221 flow->ufdev->name, status);
172 status = 0;
173 kfree(filter_hndl);
174 } else { 222 } else {
175 usnic_err("PF %s VF Idx %u Filter: %u FILTER DELETE failed with status %d", 223 usnic_err("PF %s VF Idx %u Filter: %u FILTER DELETE failed with status %d",
176 netdev_name(netdev), 224 flow->ufdev->name, flow->vnic_idx,
177 filter_hndl->vnic_idx, filter_hndl->id, 225 flow->flow_id, status);
178 status);
179 } 226 }
227 status = 0;
228 /*
229 * Log the error and fake success to the caller because if
230 * a flow fails to be deleted in the firmware, it is an
231 * unrecoverable error.
232 */
180 } else { 233 } else {
181 usnic_dbg("PF %s VF Idx %u Filter: %u FILTER DELETED", 234 usnic_dbg("PF %s VF Idx %u Filter: %u FILTER DELETED",
182 netdev_name(netdev), filter_hndl->vnic_idx, 235 flow->ufdev->name, flow->vnic_idx,
183 filter_hndl->id); 236 flow->flow_id);
184 kfree(filter_hndl);
185 } 237 }
186 238
239 kfree(flow);
187 return status; 240 return status;
188} 241}
189 242
190int usnic_fwd_enable_rq(struct usnic_fwd_dev *ufdev, int vnic_idx, int rq_idx) 243int usnic_fwd_enable_qp(struct usnic_fwd_dev *ufdev, int vnic_idx, int qp_idx)
191{ 244{
192 int status; 245 int status;
193 struct net_device *pf_netdev; 246 struct net_device *pf_netdev;
194 u64 a0, a1; 247 u64 a0, a1;
195 248
196 pf_netdev = ufdev->netdev; 249 pf_netdev = ufdev->netdev;
197 a0 = rq_idx; 250 a0 = qp_idx;
198 a1 = CMD_QP_RQWQ; 251 a1 = CMD_QP_RQWQ;
199 252
200 status = usnic_fwd_devcmd(ufdev, vnic_idx, CMD_QP_ENABLE, &a0, &a1); 253 status = usnic_fwd_devcmd(ufdev, vnic_idx, CMD_QP_ENABLE,
201 254 &a0, &a1);
202 if (status) { 255 if (status) {
203 usnic_err("PF %s VNIC Index %u RQ Index: %u ENABLE Failed with status %d", 256 usnic_err("PF %s VNIC Index %u RQ Index: %u ENABLE Failed with status %d",
204 netdev_name(pf_netdev), 257 netdev_name(pf_netdev),
205 vnic_idx, 258 vnic_idx,
206 rq_idx, 259 qp_idx,
207 status); 260 status);
208 } else { 261 } else {
209 usnic_dbg("PF %s VNIC Index %u RQ Index: %u ENABLED", 262 usnic_dbg("PF %s VNIC Index %u RQ Index: %u ENABLED",
210 netdev_name(pf_netdev), 263 netdev_name(pf_netdev),
211 vnic_idx, rq_idx); 264 vnic_idx, qp_idx);
212 } 265 }
213 266
214 return status; 267 return status;
215} 268}
216 269
217int usnic_fwd_disable_rq(struct usnic_fwd_dev *ufdev, int vnic_idx, int rq_idx) 270int usnic_fwd_disable_qp(struct usnic_fwd_dev *ufdev, int vnic_idx, int qp_idx)
218{ 271{
219 int status; 272 int status;
220 u64 a0, a1; 273 u64 a0, a1;
221 struct net_device *pf_netdev; 274 struct net_device *pf_netdev;
222 275
223 pf_netdev = ufdev->netdev; 276 pf_netdev = ufdev->netdev;
224 a0 = rq_idx; 277 a0 = qp_idx;
225 a1 = CMD_QP_RQWQ; 278 a1 = CMD_QP_RQWQ;
226 279
227 status = usnic_fwd_devcmd(ufdev, vnic_idx, CMD_QP_DISABLE, &a0, &a1); 280 status = usnic_fwd_devcmd(ufdev, vnic_idx, CMD_QP_DISABLE,
228 281 &a0, &a1);
229 if (status) { 282 if (status) {
230 usnic_err("PF %s VNIC Index %u RQ Index: %u DISABLE Failed with status %d", 283 usnic_err("PF %s VNIC Index %u RQ Index: %u DISABLE Failed with status %d",
231 netdev_name(pf_netdev), 284 netdev_name(pf_netdev),
232 vnic_idx, 285 vnic_idx,
233 rq_idx, 286 qp_idx,
234 status); 287 status);
235 } else { 288 } else {
236 usnic_dbg("PF %s VNIC Index %u RQ Index: %u DISABLED", 289 usnic_dbg("PF %s VNIC Index %u RQ Index: %u DISABLED",
237 netdev_name(pf_netdev), 290 netdev_name(pf_netdev),
238 vnic_idx, 291 vnic_idx,
239 rq_idx); 292 qp_idx);
240 } 293 }
241 294
242 return status; 295 return status;
diff --git a/drivers/infiniband/hw/usnic/usnic_fwd.h b/drivers/infiniband/hw/usnic/usnic_fwd.h
index 6973901da8af..b146eb97d82c 100644
--- a/drivers/infiniband/hw/usnic/usnic_fwd.h
+++ b/drivers/infiniband/hw/usnic/usnic_fwd.h
@@ -20,39 +20,73 @@
20#define USNIC_FWD_H_ 20#define USNIC_FWD_H_
21 21
22#include <linux/if.h> 22#include <linux/if.h>
23#include <linux/netdevice.h>
23#include <linux/pci.h> 24#include <linux/pci.h>
24#include <linux/spinlock.h> 25#include <linux/in.h>
25 26
26#include "usnic_abi.h" 27#include "usnic_abi.h"
28#include "usnic_common_pkt_hdr.h"
27#include "vnic_devcmd.h" 29#include "vnic_devcmd.h"
28 30
29struct usnic_fwd_dev { 31struct usnic_fwd_dev {
30 struct pci_dev *pdev; 32 struct pci_dev *pdev;
31 struct net_device *netdev; 33 struct net_device *netdev;
32 spinlock_t lock; 34 spinlock_t lock;
35 /*
36 * The following fields can be read directly off the device.
37 * However, they should be set by a accessor function, except name,
38 * which cannot be changed.
39 */
40 bool link_up;
41 char mac[ETH_ALEN];
42 unsigned int mtu;
43 char name[IFNAMSIZ+1];
33}; 44};
34 45
35struct usnic_fwd_filter { 46struct usnic_fwd_flow {
36 enum usnic_transport_type transport; 47 uint32_t flow_id;
37 u16 port_num; 48 struct usnic_fwd_dev *ufdev;
49 unsigned int vnic_idx;
38}; 50};
39 51
40struct usnic_fwd_filter_hndl { 52struct usnic_filter_action {
41 enum filter_type type; 53 int vnic_idx;
42 u32 id; 54 struct filter_action action;
43 u32 vnic_idx;
44 struct usnic_fwd_dev *ufdev;
45 struct list_head link;
46 struct usnic_fwd_filter *filter;
47}; 55};
48 56
49struct usnic_fwd_dev *usnic_fwd_dev_alloc(struct pci_dev *pdev); 57struct usnic_fwd_dev *usnic_fwd_dev_alloc(struct pci_dev *pdev);
50void usnic_fwd_dev_free(struct usnic_fwd_dev *ufdev); 58void usnic_fwd_dev_free(struct usnic_fwd_dev *ufdev);
51int usnic_fwd_add_usnic_filter(struct usnic_fwd_dev *ufdev, int vnic_idx, 59
52 int rq_idx, struct usnic_fwd_filter *filter, 60void usnic_fwd_set_mac(struct usnic_fwd_dev *ufdev, char mac[ETH_ALEN]);
53 struct usnic_fwd_filter_hndl **filter_hndl); 61void usnic_fwd_carrier_up(struct usnic_fwd_dev *ufdev);
54int usnic_fwd_del_filter(struct usnic_fwd_filter_hndl *filter_hndl); 62void usnic_fwd_carrier_down(struct usnic_fwd_dev *ufdev);
55int usnic_fwd_enable_rq(struct usnic_fwd_dev *ufdev, int vnic_idx, int rq_idx); 63void usnic_fwd_set_mtu(struct usnic_fwd_dev *ufdev, unsigned int mtu);
56int usnic_fwd_disable_rq(struct usnic_fwd_dev *ufdev, int vnic_idx, int rq_idx); 64
65/*
66 * Allocate a flow on this forwarding device. Whoever calls this function,
67 * must monitor netdev events on ufdev's netdevice. If NETDEV_REBOOT or
68 * NETDEV_DOWN is seen, flow will no longer function and must be
69 * immediately freed by calling usnic_dealloc_flow.
70 */
71struct usnic_fwd_flow*
72usnic_fwd_alloc_flow(struct usnic_fwd_dev *ufdev, struct filter *filter,
73 struct usnic_filter_action *action);
74int usnic_fwd_dealloc_flow(struct usnic_fwd_flow *flow);
75int usnic_fwd_enable_qp(struct usnic_fwd_dev *ufdev, int vnic_idx, int qp_idx);
76int usnic_fwd_disable_qp(struct usnic_fwd_dev *ufdev, int vnic_idx, int qp_idx);
77
78static inline void usnic_fwd_init_usnic_filter(struct filter *filter,
79 uint32_t usnic_id)
80{
81 filter->type = FILTER_USNIC_ID;
82 filter->u.usnic.ethtype = USNIC_ROCE_ETHERTYPE;
83 filter->u.usnic.flags = FILTER_FIELD_USNIC_ETHTYPE |
84 FILTER_FIELD_USNIC_ID |
85 FILTER_FIELD_USNIC_PROTO;
86 filter->u.usnic.proto_version = (USNIC_ROCE_GRH_VER <<
87 USNIC_ROCE_GRH_VER_SHIFT) |
88 USNIC_PROTO_VER;
89 filter->u.usnic.usnic_id = usnic_id;
90}
57 91
58#endif /* !USNIC_FWD_H_ */ 92#endif /* !USNIC_FWD_H_ */