linux/drivers/net/ethernet/cisco/enic/enic_pp.c
<<
>>
Prefs
   1/*
   2 * Copyright 2011 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
  19#include <linux/kernel.h>
  20#include <linux/string.h>
  21#include <linux/errno.h>
  22#include <linux/types.h>
  23#include <linux/netdevice.h>
  24#include <linux/etherdevice.h>
  25#include <linux/rtnetlink.h>
  26#include <net/ip.h>
  27
  28#include "vnic_vic.h"
  29#include "enic_res.h"
  30#include "enic.h"
  31#include "enic_dev.h"
  32#include "enic_pp.h"
  33
  34/*
  35 * Checks validity of vf index that came in
  36 * port profile request
  37 */
  38int enic_is_valid_pp_vf(struct enic *enic, int vf, int *err)
  39{
  40        if (vf != PORT_SELF_VF) {
  41#ifdef CONFIG_PCI_IOV
  42                if (enic_sriov_enabled(enic)) {
  43                        if (vf < 0 || vf >= enic->num_vfs) {
  44                                *err = -EINVAL;
  45                                goto err_out;
  46                        }
  47                } else {
  48                        *err = -EOPNOTSUPP;
  49                        goto err_out;
  50                }
  51#else
  52                *err = -EOPNOTSUPP;
  53                goto err_out;
  54#endif
  55        }
  56
  57        if (vf == PORT_SELF_VF && !enic_is_dynamic(enic)) {
  58                *err = -EOPNOTSUPP;
  59                goto err_out;
  60        }
  61
  62        *err = 0;
  63        return 1;
  64
  65err_out:
  66        return 0;
  67}
  68
  69static int enic_set_port_profile(struct enic *enic, int vf)
  70{
  71        struct net_device *netdev = enic->netdev;
  72        struct enic_port_profile *pp;
  73        struct vic_provinfo *vp;
  74        const u8 oui[3] = VIC_PROVINFO_CISCO_OUI;
  75        const __be16 os_type = htons(VIC_GENERIC_PROV_OS_TYPE_LINUX);
  76        const u8 *client_mac;
  77        char uuid_str[38];
  78        char client_mac_str[18];
  79        int err;
  80
  81        ENIC_PP_BY_INDEX(enic, vf, pp, &err);
  82        if (err)
  83                return err;
  84
  85        if (!(pp->set & ENIC_SET_NAME) || !strlen(pp->name))
  86                return -EINVAL;
  87
  88        vp = vic_provinfo_alloc(GFP_KERNEL, oui,
  89                VIC_PROVINFO_GENERIC_TYPE);
  90        if (!vp)
  91                return -ENOMEM;
  92
  93        VIC_PROVINFO_ADD_TLV(vp,
  94                VIC_GENERIC_PROV_TLV_PORT_PROFILE_NAME_STR,
  95                strlen(pp->name) + 1, pp->name);
  96
  97        if (!is_zero_ether_addr(pp->mac_addr)) {
  98                client_mac = pp->mac_addr;
  99        } else if (vf == PORT_SELF_VF) {
 100                client_mac = netdev->dev_addr;
 101        } else {
 102                netdev_err(netdev, "Cannot find pp mac address "
 103                        "for VF %d\n", vf);
 104                err = -EINVAL;
 105                goto add_tlv_failure;
 106        }
 107
 108        VIC_PROVINFO_ADD_TLV(vp,
 109                VIC_GENERIC_PROV_TLV_CLIENT_MAC_ADDR,
 110                ETH_ALEN, client_mac);
 111
 112        snprintf(client_mac_str, sizeof(client_mac_str), "%pM", client_mac);
 113        VIC_PROVINFO_ADD_TLV(vp,
 114                VIC_GENERIC_PROV_TLV_CLUSTER_PORT_UUID_STR,
 115                sizeof(client_mac_str), client_mac_str);
 116
 117        if (pp->set & ENIC_SET_INSTANCE) {
 118                sprintf(uuid_str, "%pUB", pp->instance_uuid);
 119                VIC_PROVINFO_ADD_TLV(vp,
 120                        VIC_GENERIC_PROV_TLV_CLIENT_UUID_STR,
 121                        sizeof(uuid_str), uuid_str);
 122        }
 123
 124        if (pp->set & ENIC_SET_HOST) {
 125                sprintf(uuid_str, "%pUB", pp->host_uuid);
 126                VIC_PROVINFO_ADD_TLV(vp,
 127                        VIC_GENERIC_PROV_TLV_HOST_UUID_STR,
 128                        sizeof(uuid_str), uuid_str);
 129        }
 130
 131        VIC_PROVINFO_ADD_TLV(vp,
 132                VIC_GENERIC_PROV_TLV_OS_TYPE,
 133                sizeof(os_type), &os_type);
 134
 135        ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, vnic_dev_init_prov2, (u8 *)vp,
 136                vic_provinfo_size(vp));
 137        err = enic_dev_status_to_errno(err);
 138
 139add_tlv_failure:
 140        vic_provinfo_free(vp);
 141
 142        return err;
 143}
 144
 145static int enic_unset_port_profile(struct enic *enic, int vf)
 146{
 147        int err;
 148
 149        ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, vnic_dev_deinit);
 150        if (err)
 151                return enic_dev_status_to_errno(err);
 152
 153        if (vf == PORT_SELF_VF)
 154                enic_reset_addr_lists(enic);
 155
 156        return 0;
 157}
 158
 159static int enic_are_pp_different(struct enic_port_profile *pp1,
 160                struct enic_port_profile *pp2)
 161{
 162        return strcmp(pp1->name, pp2->name) | !!memcmp(pp1->instance_uuid,
 163                pp2->instance_uuid, PORT_UUID_MAX) |
 164                !!memcmp(pp1->host_uuid, pp2->host_uuid, PORT_UUID_MAX) |
 165                !ether_addr_equal(pp1->mac_addr, pp2->mac_addr);
 166}
 167
 168static int enic_pp_preassociate(struct enic *enic, int vf,
 169        struct enic_port_profile *prev_pp, int *restore_pp);
 170static int enic_pp_disassociate(struct enic *enic, int vf,
 171        struct enic_port_profile *prev_pp, int *restore_pp);
 172static int enic_pp_preassociate_rr(struct enic *enic, int vf,
 173        struct enic_port_profile *prev_pp, int *restore_pp);
 174static int enic_pp_associate(struct enic *enic, int vf,
 175        struct enic_port_profile *prev_pp, int *restore_pp);
 176
 177static int (*enic_pp_handlers[])(struct enic *enic, int vf,
 178                struct enic_port_profile *prev_state,
 179                int *restore_pp) = {
 180        [PORT_REQUEST_PREASSOCIATE]     = enic_pp_preassociate,
 181        [PORT_REQUEST_PREASSOCIATE_RR]  = enic_pp_preassociate_rr,
 182        [PORT_REQUEST_ASSOCIATE]        = enic_pp_associate,
 183        [PORT_REQUEST_DISASSOCIATE]     = enic_pp_disassociate,
 184};
 185
 186static const int enic_pp_handlers_count =
 187                        ARRAY_SIZE(enic_pp_handlers);
 188
 189static int enic_pp_preassociate(struct enic *enic, int vf,
 190        struct enic_port_profile *prev_pp, int *restore_pp)
 191{
 192        return -EOPNOTSUPP;
 193}
 194
 195static int enic_pp_disassociate(struct enic *enic, int vf,
 196        struct enic_port_profile *prev_pp, int *restore_pp)
 197{
 198        struct net_device *netdev = enic->netdev;
 199        struct enic_port_profile *pp;
 200        int err;
 201
 202        ENIC_PP_BY_INDEX(enic, vf, pp, &err);
 203        if (err)
 204                return err;
 205
 206        /* Deregister mac addresses */
 207        if (!is_zero_ether_addr(pp->mac_addr))
 208                ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, vnic_dev_del_addr,
 209                        pp->mac_addr);
 210        else if (vf == PORT_SELF_VF && !is_zero_ether_addr(netdev->dev_addr))
 211                ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, vnic_dev_del_addr,
 212                        netdev->dev_addr);
 213
 214        return enic_unset_port_profile(enic, vf);
 215}
 216
 217static int enic_pp_preassociate_rr(struct enic *enic, int vf,
 218        struct enic_port_profile *prev_pp, int *restore_pp)
 219{
 220        struct enic_port_profile *pp;
 221        int err;
 222        int active = 0;
 223
 224        ENIC_PP_BY_INDEX(enic, vf, pp, &err);
 225        if (err)
 226                return err;
 227
 228        if (pp->request != PORT_REQUEST_ASSOCIATE) {
 229                /* If pre-associate is not part of an associate.
 230                We always disassociate first */
 231                err = enic_pp_handlers[PORT_REQUEST_DISASSOCIATE](enic, vf,
 232                        prev_pp, restore_pp);
 233                if (err)
 234                        return err;
 235
 236                *restore_pp = 0;
 237        }
 238
 239        *restore_pp = 0;
 240
 241        err = enic_set_port_profile(enic, vf);
 242        if (err)
 243                return err;
 244
 245        /* If pre-associate is not part of an associate. */
 246        if (pp->request != PORT_REQUEST_ASSOCIATE) {
 247                /* Enable device as standby */
 248                ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, vnic_dev_enable2,
 249                        active);
 250                err = enic_dev_status_to_errno(err);
 251        }
 252
 253        return err;
 254}
 255
 256static int enic_pp_associate(struct enic *enic, int vf,
 257        struct enic_port_profile *prev_pp, int *restore_pp)
 258{
 259        struct net_device *netdev = enic->netdev;
 260        struct enic_port_profile *pp;
 261        int err;
 262        int active = 1;
 263
 264        ENIC_PP_BY_INDEX(enic, vf, pp, &err);
 265        if (err)
 266                return err;
 267
 268        /* Check if a pre-associate was called before */
 269        if (prev_pp->request != PORT_REQUEST_PREASSOCIATE_RR ||
 270                (prev_pp->request == PORT_REQUEST_PREASSOCIATE_RR &&
 271                        enic_are_pp_different(prev_pp, pp))) {
 272                err = enic_pp_handlers[PORT_REQUEST_DISASSOCIATE](
 273                        enic, vf, prev_pp, restore_pp);
 274                if (err)
 275                        return err;
 276
 277                *restore_pp = 0;
 278        }
 279
 280        err = enic_pp_handlers[PORT_REQUEST_PREASSOCIATE_RR](
 281                        enic, vf, prev_pp, restore_pp);
 282        if (err)
 283                return err;
 284
 285        *restore_pp = 0;
 286
 287        /* Enable device as active */
 288        ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, vnic_dev_enable2, active);
 289        err = enic_dev_status_to_errno(err);
 290        if (err)
 291                return err;
 292
 293        /* Register mac address */
 294        if (!is_zero_ether_addr(pp->mac_addr))
 295                ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, vnic_dev_add_addr,
 296                        pp->mac_addr);
 297        else if (vf == PORT_SELF_VF && !is_zero_ether_addr(netdev->dev_addr))
 298                ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic, vnic_dev_add_addr,
 299                        netdev->dev_addr);
 300
 301        return 0;
 302}
 303
 304int enic_process_set_pp_request(struct enic *enic, int vf,
 305        struct enic_port_profile *prev_pp, int *restore_pp)
 306{
 307        struct enic_port_profile *pp;
 308        int err;
 309
 310        ENIC_PP_BY_INDEX(enic, vf, pp, &err);
 311        if (err)
 312                return err;
 313
 314        if (pp->request >= enic_pp_handlers_count
 315                || !enic_pp_handlers[pp->request])
 316                return -EOPNOTSUPP;
 317
 318        return enic_pp_handlers[pp->request](enic, vf, prev_pp, restore_pp);
 319}
 320
 321int enic_process_get_pp_request(struct enic *enic, int vf,
 322        int request, u16 *response)
 323{
 324        int err, status = ERR_SUCCESS;
 325
 326        switch (request) {
 327
 328        case PORT_REQUEST_PREASSOCIATE_RR:
 329        case PORT_REQUEST_ASSOCIATE:
 330                ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic,
 331                        vnic_dev_enable2_done, &status);
 332                break;
 333
 334        case PORT_REQUEST_DISASSOCIATE:
 335                ENIC_DEVCMD_PROXY_BY_INDEX(vf, err, enic,
 336                        vnic_dev_deinit_done, &status);
 337                break;
 338
 339        default:
 340                return -EINVAL;
 341        }
 342
 343        if (err)
 344                status = err;
 345
 346        switch (status) {
 347        case ERR_SUCCESS:
 348                *response = PORT_PROFILE_RESPONSE_SUCCESS;
 349                break;
 350        case ERR_EINVAL:
 351                *response = PORT_PROFILE_RESPONSE_INVALID;
 352                break;
 353        case ERR_EBADSTATE:
 354                *response = PORT_PROFILE_RESPONSE_BADSTATE;
 355                break;
 356        case ERR_ENOMEM:
 357                *response = PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES;
 358                break;
 359        case ERR_EINPROGRESS:
 360                *response = PORT_PROFILE_RESPONSE_INPROGRESS;
 361                break;
 362        default:
 363                *response = PORT_PROFILE_RESPONSE_ERROR;
 364                break;
 365        }
 366
 367        return 0;
 368}
 369