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