net/sfc/base: implement vSwitch create/destroy
authorGautam Dawar <gdawar@solarflare.com>
Mon, 10 Jun 2019 07:38:36 +0000 (08:38 +0100)
committerFerruh Yigit <ferruh.yigit@intel.com>
Thu, 20 Jun 2019 21:42:04 +0000 (23:42 +0200)
The vSwitch create API takes an array of num_vports client driver
allocated vPort config entries where entry at index 0 contains the PF
configuration and rest num_vports-1 entries refer to vPort configuration
for VFs 0 to (num_vports-2). The required hierarchy
(vswitch/vport/vadaptor) is created within this API. The destroy API
tears down this hierarchy and releases memory for the vSwitch object.

Signed-off-by: Gautam Dawar <gdawar@solarflare.com>
Signed-off-by: Andrew Rybchenko <arybchenko@solarflare.com>
drivers/net/sfc/base/efx.h
drivers/net/sfc/base/efx_evb.c
drivers/net/sfc/base/efx_impl.h

index 664efc8..492c8c6 100644 (file)
@@ -1411,6 +1411,9 @@ typedef struct efx_nic_cfg_s {
        uint32_t                enc_assigned_port;
 } efx_nic_cfg_t;
 
+#define        EFX_VPORT_PCI_FUNCTION_IS_PF(configp) \
+       ((configp)->evc_function == 0xffff)
+
 #define        EFX_PCI_FUNCTION_IS_PF(_encp)   ((_encp)->enc_vf == 0xffff)
 #define        EFX_PCI_FUNCTION_IS_VF(_encp)   ((_encp)->enc_vf != 0xffff)
 
@@ -3369,6 +3372,31 @@ typedef enum efx_vport_type_e {
 #define        EFX_FILTER_VID_UNSPEC   0xffff
 #define        EFX_DEFAULT_VSWITCH_ID  1
 
+/* Default VF VLAN ID on creation */
+#define                EFX_VF_VID_DEFAULT      EFX_FILTER_VID_UNSPEC
+#define                EFX_VPORT_ID_INVALID    0
+
+typedef struct efx_vport_config_s {
+       /* Either VF index or 0xffff for PF */
+       uint16_t        evc_function;
+       /* VLAN ID of the associated function */
+       uint16_t        evc_vid;
+       /* vport id shared with client driver */
+       efx_vport_id_t  evc_vport_id;
+       /* MAC address of the associated function */
+       uint8_t         evc_mac_addr[EFX_MAC_ADDR_LEN];
+       /*
+        * vports created with this flag set may only transfer traffic on the
+        * VLANs permitted by the vport. Also, an attempt to install filter with
+        * VLAN will be refused unless requesting function has VLAN privilege.
+        */
+       boolean_t       evc_vlan_restrict;
+       /* Whether this function is assigned or not */
+       boolean_t       evc_vport_assigned;
+} efx_vport_config_t;
+
+typedef        struct  efx_vswitch_s   efx_vswitch_t;
+
 extern __checkReturn   efx_rc_t
 efx_evb_init(
        __in            efx_nic_t *enp);
@@ -3377,6 +3405,18 @@ extern                   void
 efx_evb_fini(
        __in            efx_nic_t *enp);
 
+extern __checkReturn   efx_rc_t
+efx_evb_vswitch_create(
+       __in                            efx_nic_t *enp,
+       __in                            uint32_t num_vports,
+       __inout_ecount(num_vports)      efx_vport_config_t *vport_configp,
+       __deref_out                     efx_vswitch_t **evpp);
+
+extern __checkReturn   efx_rc_t
+efx_evb_vswitch_destroy(
+       __in                            efx_nic_t *enp,
+       __in                            efx_vswitch_t *evp);
+
 #endif /* EFSYS_OPT_EVB */
 
 #ifdef __cplusplus
index ff240f9..27b466f 100644 (file)
@@ -107,7 +107,7 @@ fail1:
        return (rc);
 }
 
-       void
+                       void
 efx_evb_fini(
        __in            efx_nic_t *enp)
 {
@@ -125,4 +125,271 @@ efx_evb_fini(
        enp->en_mod_flags &= ~EFX_MOD_EVB;
 }
 
+/*
+ * efx_is_zero_eth_addr returns TRUE if the passed MAC address has all bytes
+ * equal to zero. A vport is assigned a MAC address after creation and this
+ * function checks if that has happened. It is called in the clean-up function
+ * before calling eeo_vport_mac_addr_del to ensure that the vport actually had
+ * an allocated MAC address.
+ */
+
+__checkReturn                          boolean_t
+efx_is_zero_eth_addr(
+       __in_bcount(EFX_MAC_ADDR_LEN)   const uint8_t *addrp)
+{
+       return (!(addrp[0] | addrp[1] | addrp[2] |
+               addrp[3] | addrp[4] | addrp[5]));
+}
+
+static                 void
+efx_evb_free_vport(
+       __in            efx_nic_t *enp,
+       __in            efx_vswitch_id_t vswitch_id,
+       __inout         efx_vport_config_t *configp)
+{
+       const efx_evb_ops_t *eeop = enp->en_eeop;
+
+       /* If any callback fails, continue clean-up with others functions */
+       if (EFX_VPORT_PCI_FUNCTION_IS_PF(configp)) {
+               /* free vadaptor */
+               if ((configp->evc_vport_id != EFX_VPORT_ID_INVALID) &&
+                   (eeop->eeo_vadaptor_free(enp, vswitch_id,
+                   configp->evc_vport_id) != 0)) {
+                       EFSYS_PROBE2(eeo_vadaptor_free,
+                           uint16_t, configp->evc_function,
+                           uint32_t, configp->evc_vport_id);
+               }
+       } else {
+               if (configp->evc_vport_assigned == B_TRUE) {
+                       if (eeop->eeo_vport_assign(enp, vswitch_id,
+                           EVB_PORT_ID_NULL,
+                           configp->evc_function) != 0) {
+                               EFSYS_PROBE1(eeo_vport_assign,
+                                   uint16_t, configp->evc_function);
+                       }
+                       configp->evc_vport_assigned = B_FALSE;
+               }
+       }
+
+       /*
+        * Call eeo_vport_mac_addr_del after checking that this vport is
+        * actually allocated a MAC address in call to efx_evb_configure_vport
+        */
+       if (!efx_is_zero_eth_addr(configp->evc_mac_addr)) {
+               if (eeop->eeo_vport_mac_addr_del(enp, vswitch_id,
+                   configp->evc_vport_id,
+                   configp->evc_mac_addr) != 0) {
+                       EFSYS_PROBE1(eeo_vport_mac_addr_del,
+                           uint16_t, configp->evc_function);
+               }
+               memset(configp->evc_mac_addr, 0x00, EFX_MAC_ADDR_LEN);
+       }
+
+       if (configp->evc_vport_id != EFX_VPORT_ID_INVALID) {
+               if (eeop->eeo_vport_free(enp, vswitch_id,
+                   configp->evc_vport_id) != 0) {
+                       EFSYS_PROBE1(eeo_vport_free,
+                           uint16_t, configp->evc_function);
+               }
+               configp->evc_vport_id = EFX_VPORT_ID_INVALID;
+       }
+}
+
+static                                 void
+efx_evb_free_vports(
+       __in                            efx_nic_t *enp,
+       __in                            efx_vswitch_id_t vswitch_id,
+       __in                            uint32_t num_vports,
+       __inout_ecount(num_vports)      efx_vport_config_t *vport_configp)
+{
+       efx_vport_config_t *configp;
+       uint32_t i;
+
+       if (vport_configp == NULL) {
+               EFSYS_PROBE(null_vport_config);
+               return;
+       }
+
+       for (i = 0; i < num_vports; i++) {
+               configp = vport_configp + i;
+               efx_evb_free_vport(enp, vswitch_id, configp);
+       }
+}
+
+static __checkReturn   efx_rc_t
+efx_evb_configure_vport(
+       __in            efx_nic_t *enp,
+       __in            efx_vswitch_id_t vswitch_id,
+       __in            const efx_evb_ops_t *eeop,
+       __inout         efx_vport_config_t *configp)
+{
+       efx_rc_t rc;
+       efx_vport_id_t vport_id;
+
+       if ((rc = eeop->eeo_vport_alloc(enp, vswitch_id,
+                       EFX_VPORT_TYPE_NORMAL, configp->evc_vid,
+                       configp->evc_vlan_restrict, &vport_id)) != 0)
+               goto fail1;
+
+       configp->evc_vport_id = vport_id;
+
+       if ((rc = eeop->eeo_vport_mac_addr_add(enp, vswitch_id,
+                       configp->evc_vport_id,
+                       configp->evc_mac_addr)) != 0)
+               goto fail2;
+
+       if (EFX_VPORT_PCI_FUNCTION_IS_PF(configp)) {
+               if ((rc = eeop->eeo_vadaptor_alloc(enp, vswitch_id,
+                               configp->evc_vport_id)) != 0)
+                       goto fail3;
+       } else {
+               if ((rc = eeop->eeo_vport_assign(enp, vswitch_id,
+                               configp->evc_vport_id,
+                               configp->evc_function)) != 0)
+                       goto fail4;
+               configp->evc_vport_assigned = B_TRUE;
+       }
+
+       return (0);
+
+fail4:
+       EFSYS_PROBE(fail4);
+fail3:
+       EFSYS_PROBE(fail3);
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn                   efx_rc_t
+efx_evb_vswitch_create(
+       __in                            efx_nic_t *enp,
+       __in                            uint32_t num_vports,
+       __inout_ecount(num_vports)      efx_vport_config_t *vport_configp,
+       __deref_out                     efx_vswitch_t **evpp)
+{
+       efx_vswitch_t *evp;
+       efx_rc_t rc;
+       efx_vswitch_id_t vswitch_id;
+       efx_vport_config_t *configp;
+       const efx_evb_ops_t *eeop = enp->en_eeop;
+       uint32_t i;
+
+       /* vport_configp is a caller allocated array filled in with vports
+        * configuration. Index 0 carries the PF vport configuration and next
+        * num_vports - 1 indices carry VFs configuration.
+        */
+       EFSYS_ASSERT((num_vports != 0) && (vport_configp != NULL) &&
+               (evpp != NULL));
+       EFSYS_ASSERT(enp->en_mod_flags & EFX_MOD_EVB);
+       EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_NIC));
+
+       if ((eeop->eeo_vswitch_alloc == NULL) ||
+           (eeop->eeo_vport_alloc == NULL) ||
+           (eeop->eeo_vport_free == NULL) ||
+           (eeop->eeo_vport_mac_addr_add == NULL) ||
+           (eeop->eeo_vport_mac_addr_del == NULL) ||
+           (eeop->eeo_vadaptor_alloc == NULL) ||
+           (eeop->eeo_vadaptor_free == NULL) ||
+           (eeop->eeo_vport_assign == NULL) ||
+           (eeop->eeo_vswitch_free == NULL)) {
+               rc = ENOTSUP;
+               goto fail1;
+       }
+
+       /* Allocate a vSwitch object */
+       EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (efx_vswitch_t), evp);
+
+       if (evp == NULL) {
+               rc = ENOMEM;
+               goto fail2;
+       }
+
+       if ((rc = eeop->eeo_vswitch_alloc(enp, &vswitch_id)) != 0)
+               goto fail3;
+
+       evp->ev_enp = enp;
+       evp->ev_num_vports = num_vports;
+       evp->ev_evcp = vport_configp;
+       evp->ev_vswitch_id = vswitch_id;
+
+       for (i = 0; i < num_vports; i++) {
+               configp = vport_configp + i;
+
+               if ((rc = efx_evb_configure_vport(enp, vswitch_id, eeop,
+                               configp)) != 0)
+                       goto fail4;
+       }
+
+       enp->en_vswitchp = evp;
+       *evpp = evp;
+       return (0);
+
+fail4:
+       EFSYS_PROBE(fail4);
+       efx_evb_free_vports(enp, vswitch_id, i + 1, vport_configp);
+       /* Free the vSwitch */
+       eeop->eeo_vswitch_free(enp, vswitch_id);
+
+fail3:
+       EFSYS_PROBE(fail3);
+       /* Free the vSwitch object */
+       EFSYS_KMEM_FREE(enp->en_esip, sizeof (efx_vswitch_t), evp);
+
+fail2:
+       EFSYS_PROBE(fail2);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+
+       __checkReturn   efx_rc_t
+efx_evb_vswitch_destroy(
+       __in            efx_nic_t *enp,
+       __in            efx_vswitch_t *evp)
+{
+       const efx_evb_ops_t *eeop = enp->en_eeop;
+       efx_vswitch_id_t vswitch_id;
+       efx_rc_t rc;
+
+       EFSYS_ASSERT(evp != NULL);
+       EFSYS_ASSERT(enp->en_mod_flags & EFX_MOD_EVB);
+
+       if ((eeop->eeo_vport_mac_addr_del == NULL) ||
+           (eeop->eeo_vadaptor_free == NULL) ||
+           (eeop->eeo_vport_assign == NULL) ||
+           (eeop->eeo_vport_free == NULL) ||
+           (eeop->eeo_vswitch_free == NULL)) {
+               rc = ENOTSUP;
+               goto fail1;
+       }
+
+       vswitch_id  = evp->ev_vswitch_id;
+       efx_evb_free_vports(enp, vswitch_id,
+               evp->ev_num_vports, evp->ev_evcp);
+
+       /* Free the vSwitch object */
+       EFSYS_KMEM_FREE(enp->en_esip, sizeof (efx_vswitch_t), evp);
+       enp->en_vswitchp = NULL;
+
+       /* Free the vSwitch */
+       if ((rc = eeop->eeo_vswitch_free(enp, vswitch_id)) != 0)
+               goto fail2;
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+       return (rc);
+}
+
 #endif
index ef6a97a..46d5389 100644 (file)
@@ -652,6 +652,17 @@ typedef struct efx_lic_ops_s {
 
 #if EFSYS_OPT_EVB
 
+struct efx_vswitch_s {
+       efx_nic_t               *ev_enp;
+       efx_vswitch_id_t        ev_vswitch_id;
+       uint32_t                ev_num_vports;
+       /*
+        * Vport configuration array: index 0 to store PF configuration
+        * and next ev_num_vports-1 entries hold VFs configuration.
+        */
+       efx_vport_config_t      *ev_evcp;
+};
+
 typedef struct efx_evb_ops_s {
        efx_rc_t        (*eeo_init)(efx_nic_t *);
        void            (*eeo_fini)(efx_nic_t *);
@@ -674,6 +685,10 @@ typedef struct efx_evb_ops_s {
                                                efx_vport_id_t, uint32_t);
 } efx_evb_ops_t;
 
+extern __checkReturn   boolean_t
+efx_is_zero_eth_addr(
+       __in_bcount(EFX_MAC_ADDR_LEN)   const uint8_t *addrp);
+
 #endif /* EFSYS_OPT_EVB */
 
 #define        EFX_DRV_VER_MAX         20
@@ -776,6 +791,7 @@ struct efx_nic_s {
 #endif /* EFX_OPTS_EF10() */
 #if EFSYS_OPT_EVB
        const efx_evb_ops_t     *en_eeop;
+       struct efx_vswitch_s    *en_vswitchp;
 #endif /* EFSYS_OPT_EVB */
 };