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 int usnic_fwd_add_ipaddr(struct usnic_fwd_dev *ufdev, __be32 inaddr) 99 { 100 int status; 101 102 spin_lock(&ufdev->lock); 103 if (ufdev->inaddr == 0) { 104 ufdev->inaddr = inaddr; 105 status = 0; 106 } else { 107 status = -EFAULT; 108 } 109 spin_unlock(&ufdev->lock); 110 111 return status; 112 } 113 114 void usnic_fwd_del_ipaddr(struct usnic_fwd_dev *ufdev) 115 { 116 spin_lock(&ufdev->lock); 117 ufdev->inaddr = 0; 118 spin_unlock(&ufdev->lock); 119 } 120 121 void usnic_fwd_carrier_up(struct usnic_fwd_dev *ufdev) 122 { 123 spin_lock(&ufdev->lock); 124 ufdev->link_up = 1; 125 spin_unlock(&ufdev->lock); 126 } 127 128 void usnic_fwd_carrier_down(struct usnic_fwd_dev *ufdev) 129 { 130 spin_lock(&ufdev->lock); 131 ufdev->link_up = 0; 132 spin_unlock(&ufdev->lock); 133 } 134 135 void usnic_fwd_set_mtu(struct usnic_fwd_dev *ufdev, unsigned int mtu) 136 { 137 spin_lock(&ufdev->lock); 138 ufdev->mtu = mtu; 139 spin_unlock(&ufdev->lock); 140 } 141 142 static int usnic_fwd_dev_ready_locked(struct usnic_fwd_dev *ufdev) 143 { 144 lockdep_assert_held(&ufdev->lock); 145 146 if (!ufdev->link_up) 147 return -EPERM; 148 149 return 0; 150 } 151 152 static int validate_filter_locked(struct usnic_fwd_dev *ufdev, 153 struct filter *filter) 154 { 155 156 lockdep_assert_held(&ufdev->lock); 157 158 if (filter->type == FILTER_IPV4_5TUPLE) { 159 if (!(filter->u.ipv4.flags & FILTER_FIELD_5TUP_DST_AD)) 160 return -EACCES; 161 if (!(filter->u.ipv4.flags & FILTER_FIELD_5TUP_DST_PT)) 162 return -EBUSY; 163 else if (ufdev->inaddr == 0) 164 return -EINVAL; 165 else if (filter->u.ipv4.dst_port == 0) 166 return -ERANGE; 167 else if (ntohl(ufdev->inaddr) != filter->u.ipv4.dst_addr) 168 return -EFAULT; 169 else 170 return 0; 171 } 172 173 return 0; 174 } 175 176 static void fill_tlv(struct filter_tlv *tlv, struct filter *filter, 177 struct filter_action *action) 178 { 179 tlv->type = CLSF_TLV_FILTER; 180 tlv->length = sizeof(struct filter); 181 *((struct filter *)&tlv->val) = *filter; 182 183 tlv = (struct filter_tlv *)((char *)tlv + sizeof(struct filter_tlv) + 184 sizeof(struct filter)); 185 tlv->type = CLSF_TLV_ACTION; 186 tlv->length = sizeof(struct filter_action); 187 *((struct filter_action *)&tlv->val) = *action; 188 } 189 190 struct usnic_fwd_flow* 191 usnic_fwd_alloc_flow(struct usnic_fwd_dev *ufdev, struct filter *filter, 192 struct usnic_filter_action *uaction) 193 { 194 struct filter_tlv *tlv; 195 struct pci_dev *pdev; 196 struct usnic_fwd_flow *flow; 197 uint64_t a0, a1; 198 uint64_t tlv_size; 199 dma_addr_t tlv_pa; 200 int status; 201 202 pdev = ufdev->pdev; 203 tlv_size = (2*sizeof(struct filter_tlv) + sizeof(struct filter) + 204 sizeof(struct filter_action)); 205 206 flow = kzalloc(sizeof(*flow), GFP_ATOMIC); 207 if (!flow) 208 return ERR_PTR(-ENOMEM); 209 210 tlv = pci_alloc_consistent(pdev, tlv_size, &tlv_pa); 211 if (!tlv) { 212 usnic_err("Failed to allocate memory\n"); 213 status = -ENOMEM; 214 goto out_free_flow; 215 } 216 217 fill_tlv(tlv, filter, &uaction->action); 218 219 spin_lock(&ufdev->lock); 220 status = usnic_fwd_dev_ready_locked(ufdev); 221 if (status) { 222 usnic_err("Forwarding dev %s not ready with status %d\n", 223 ufdev->name, status); 224 goto out_free_tlv; 225 } 226 227 status = validate_filter_locked(ufdev, filter); 228 if (status) { 229 usnic_err("Failed to validate filter with status %d\n", 230 status); 231 goto out_free_tlv; 232 } 233 234 /* Issue Devcmd */ 235 a0 = tlv_pa; 236 a1 = tlv_size; 237 status = usnic_fwd_devcmd_locked(ufdev, uaction->vnic_idx, 238 CMD_ADD_FILTER, &a0, &a1); 239 if (status) { 240 usnic_err("VF %s Filter add failed with status:%d", 241 ufdev->name, status); 242 status = -EFAULT; 243 goto out_free_tlv; 244 } else { 245 usnic_dbg("VF %s FILTER ID:%llu", ufdev->name, a0); 246 } 247 248 flow->flow_id = (uint32_t) a0; 249 flow->vnic_idx = uaction->vnic_idx; 250 flow->ufdev = ufdev; 251 252 out_free_tlv: 253 spin_unlock(&ufdev->lock); 254 pci_free_consistent(pdev, tlv_size, tlv, tlv_pa); 255 if (!status) 256 return flow; 257 out_free_flow: 258 kfree(flow); 259 return ERR_PTR(status); 260 } 261 262 int usnic_fwd_dealloc_flow(struct usnic_fwd_flow *flow) 263 { 264 int status; 265 u64 a0, a1; 266 267 a0 = flow->flow_id; 268 269 status = usnic_fwd_devcmd(flow->ufdev, flow->vnic_idx, 270 CMD_DEL_FILTER, &a0, &a1); 271 if (status) { 272 if (status == ERR_EINVAL) { 273 usnic_dbg("Filter %u already deleted for VF Idx %u pf: %s status: %d", 274 flow->flow_id, flow->vnic_idx, 275 flow->ufdev->name, status); 276 } else { 277 usnic_err("PF %s VF Idx %u Filter: %u FILTER DELETE failed with status %d", 278 flow->ufdev->name, flow->vnic_idx, 279 flow->flow_id, status); 280 } 281 status = 0; 282 /* 283 * Log the error and fake success to the caller because if 284 * a flow fails to be deleted in the firmware, it is an 285 * unrecoverable error. 286 */ 287 } else { 288 usnic_dbg("PF %s VF Idx %u Filter: %u FILTER DELETED", 289 flow->ufdev->name, flow->vnic_idx, 290 flow->flow_id); 291 } 292 293 kfree(flow); 294 return status; 295 } 296 297 int usnic_fwd_enable_qp(struct usnic_fwd_dev *ufdev, int vnic_idx, int qp_idx) 298 { 299 int status; 300 struct net_device *pf_netdev; 301 u64 a0, a1; 302 303 pf_netdev = ufdev->netdev; 304 a0 = qp_idx; 305 a1 = CMD_QP_RQWQ; 306 307 status = usnic_fwd_devcmd(ufdev, vnic_idx, CMD_QP_ENABLE, 308 &a0, &a1); 309 if (status) { 310 usnic_err("PF %s VNIC Index %u RQ Index: %u ENABLE Failed with status %d", 311 netdev_name(pf_netdev), 312 vnic_idx, 313 qp_idx, 314 status); 315 } else { 316 usnic_dbg("PF %s VNIC Index %u RQ Index: %u ENABLED", 317 netdev_name(pf_netdev), 318 vnic_idx, qp_idx); 319 } 320 321 return status; 322 } 323 324 int usnic_fwd_disable_qp(struct usnic_fwd_dev *ufdev, int vnic_idx, int qp_idx) 325 { 326 int status; 327 u64 a0, a1; 328 struct net_device *pf_netdev; 329 330 pf_netdev = ufdev->netdev; 331 a0 = qp_idx; 332 a1 = CMD_QP_RQWQ; 333 334 status = usnic_fwd_devcmd(ufdev, vnic_idx, CMD_QP_DISABLE, 335 &a0, &a1); 336 if (status) { 337 usnic_err("PF %s VNIC Index %u RQ Index: %u DISABLE Failed with status %d", 338 netdev_name(pf_netdev), 339 vnic_idx, 340 qp_idx, 341 status); 342 } else { 343 usnic_dbg("PF %s VNIC Index %u RQ Index: %u DISABLED", 344 netdev_name(pf_netdev), 345 vnic_idx, 346 qp_idx); 347 } 348 349 return status; 350 } 351