/* * Copyright (C) 2013-2014 Chelsio Communications. All rights reserved. * * Written by Anish Bhatt (anish@chelsio.com) * Casey Leedom (leedom@chelsio.com) * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * */ #include "cxgb4.h" /* Initialize a port's Data Center Bridging state. Typically used after a * Link Down event. */ void cxgb4_dcb_state_init(struct net_device *dev) { struct port_info *pi = netdev2pinfo(dev); struct port_dcb_info *dcb = &pi->dcb; memset(dcb, 0, sizeof(struct port_dcb_info)); dcb->state = CXGB4_DCB_STATE_START; } /* Finite State machine for Data Center Bridging. */ void cxgb4_dcb_state_fsm(struct net_device *dev, enum cxgb4_dcb_state_input input) { struct port_info *pi = netdev2pinfo(dev); struct port_dcb_info *dcb = &pi->dcb; struct adapter *adap = pi->adapter; switch (input) { case CXGB4_DCB_INPUT_FW_DISABLED: { /* Firmware tells us it's not doing DCB */ switch (dcb->state) { case CXGB4_DCB_STATE_START: { /* we're going to use Host DCB */ dcb->state = CXGB4_DCB_STATE_HOST; dcb->supported = CXGB4_DCBX_HOST_SUPPORT; dcb->enabled = 1; break; } case CXGB4_DCB_STATE_HOST: { /* we're alreaady in Host DCB mode */ break; } default: goto bad_state_transition; } break; } case CXGB4_DCB_INPUT_FW_ENABLED: { /* Firmware tells us that it is doing DCB */ switch (dcb->state) { case CXGB4_DCB_STATE_START: { /* we're going to use Firmware DCB */ dcb->state = CXGB4_DCB_STATE_FW_INCOMPLETE; dcb->supported = CXGB4_DCBX_FW_SUPPORT; break; } case CXGB4_DCB_STATE_FW_INCOMPLETE: case CXGB4_DCB_STATE_FW_ALLSYNCED: { /* we're alreaady in firmware DCB mode */ break; } default: goto bad_state_transition; } break; } case CXGB4_DCB_INPUT_FW_INCOMPLETE: { /* Firmware tells us that its DCB state is incomplete */ switch (dcb->state) { case CXGB4_DCB_STATE_FW_INCOMPLETE: { /* we're already incomplete */ break; } case CXGB4_DCB_STATE_FW_ALLSYNCED: { /* We were successfully running with firmware DCB but * now it's telling us that it's in an "incomplete * state. We need to reset back to a ground state * of incomplete. */ cxgb4_dcb_state_init(dev); dcb->state = CXGB4_DCB_STATE_FW_INCOMPLETE; dcb->supported = CXGB4_DCBX_FW_SUPPORT; linkwatch_fire_event(dev); break; } default: goto bad_state_transition; } break; } case CXGB4_DCB_INPUT_FW_ALLSYNCED: { /* Firmware tells us that its DCB state is complete */ switch (dcb->state) { case CXGB4_DCB_STATE_FW_INCOMPLETE: { dcb->state = CXGB4_DCB_STATE_FW_ALLSYNCED; dcb->enabled = 1; linkwatch_fire_event(dev); break; } case CXGB4_DCB_STATE_FW_ALLSYNCED: { /* we're already all sync'ed */ break; } default: goto bad_state_transition; } break; } default: goto bad_state_input; } return; bad_state_input: dev_err(adap->pdev_dev, "cxgb4_dcb_state_fsm: illegal input symbol %d\n", input); return; bad_state_transition: dev_err(adap->pdev_dev, "cxgb4_dcb_state_fsm: bad state transition, state = %d, input = %d\n", dcb->state, input); } /* Handle a DCB/DCBX update message from the firmware. */ void cxgb4_dcb_handle_fw_update(struct adapter *adap, const struct fw_port_cmd *pcmd) { const union fw_port_dcb *fwdcb = &pcmd->u.dcb; int port = FW_PORT_CMD_PORTID_GET(be32_to_cpu(pcmd->op_to_portid)); struct net_device *dev = adap->port[port]; struct port_info *pi = netdev_priv(dev); struct port_dcb_info *dcb = &pi->dcb; int dcb_type = pcmd->u.dcb.pgid.type; /* Handle Firmware DCB Control messages separately since they drive * our state machine. */ if (dcb_type == FW_PORT_DCB_TYPE_CONTROL) { enum cxgb4_dcb_state_input input = ((pcmd->u.dcb.control.all_syncd_pkd & FW_PORT_CMD_ALL_SYNCD) ? CXGB4_DCB_STATE_FW_ALLSYNCED : CXGB4_DCB_STATE_FW_INCOMPLETE); cxgb4_dcb_state_fsm(dev, input); return; } /* It's weird, and almost certainly an error, to get Firmware DCB * messages when we either haven't been told whether we're going to be * doing Host or Firmware DCB; and even worse when we've been told * that we're doing Host DCB! */ if (dcb->state == CXGB4_DCB_STATE_START || dcb->state == CXGB4_DCB_STATE_HOST) { dev_err(adap->pdev_dev, "Receiving Firmware DCB messages in State %d\n", dcb->state); return; } /* Now handle the general Firmware DCB update messages ... */ switch (dcb_type) { case FW_PORT_DCB_TYPE_PGID: dcb->pgid = be32_to_cpu(fwdcb->pgid.pgid); dcb->msgs |= CXGB4_DCB_FW_PGID; break; case FW_PORT_DCB_TYPE_PGRATE: dcb->pg_num_tcs_supported = fwdcb->pgrate.num_tcs_supported; memcpy(dcb->pgrate, &fwdcb->pgrate.pgrate, sizeof(dcb->pgrate)); dcb->msgs |= CXGB4_DCB_FW_PGRATE; break; case FW_PORT_DCB_TYPE_PRIORATE: memcpy(dcb->priorate, &fwdcb->priorate.strict_priorate, sizeof(dcb->priorate)); dcb->msgs |= CXGB4_DCB_FW_PRIORATE; break; case FW_PORT_DCB_TYPE_PFC: dcb->pfcen = fwdcb->pfc.pfcen; dcb->pfc_num_tcs_supported = fwdcb->pfc.max_pfc_tcs; dcb->msgs |= CXGB4_DCB_FW_PFC; break; case FW_PORT_DCB_TYPE_APP_ID: { const struct fw_port_app_priority *fwap = &fwdcb->app_priority; int idx = fwap->idx; struct app_priority *ap = &dcb->app_priority[idx]; struct dcb_app app = { .selector = fwap->sel_field, .protocol = be16_to_cpu(fwap->protocolid), .priority = fwap->user_prio_map, }; int err; err = dcb_setapp(dev, &app); if (err) dev_err(adap->pdev_dev, "Failed DCB Set Application Priority: sel=%d, prot=%d, prio=%d, err=%d\n", app.selector, app.protocol, app.priority, -err); ap->user_prio_map = fwap->user_prio_map; ap->sel_field = fwap->sel_field; ap->protocolid = be16_to_cpu(fwap->protocolid); dcb->msgs |= CXGB4_DCB_FW_APP_ID; break; } default: dev_err(adap->pdev_dev, "Unknown DCB update type received %x\n", dcb_type); break; } } /* Data Center Bridging netlink operations. */ /* Get current DCB enabled/disabled state. */ static u8 cxgb4_getstate(struct net_device *dev) { struct port_info *pi = netdev2pinfo(dev); return pi->dcb.enabled; } /* Set DCB enabled/disabled. */ static u8 cxgb4_setstate(struct net_device *dev, u8 enabled) { struct port_info *pi = netdev2pinfo(dev); /* Firmware doesn't provide any mechanism to control the DCB state. */ if (enabled != (pi->dcb.state == CXGB4_DCB_STATE_FW_ALLSYNCED)) return 1; return 0; } static void cxgb4_getpgtccfg(struct net_device *dev, int tc, u8 *prio_type, u8 *pgid, u8 *bw_per, u8 *up_tc_map, int local) { struct fw_port_cmd pcmd; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = pi->adapter; int err; *prio_type = *pgid = *bw_per = *up_tc_map = 0; if (local) INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); else INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); pcmd.u.dcb.pgid.type = FW_PORT_DCB_TYPE_PGID; err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); if (err != FW_PORT_DCB_CFG_SUCCESS) { dev_err(adap->pdev_dev, "DCB read PGID failed with %d\n", -err); return; } *pgid = (be32_to_cpu(pcmd.u.dcb.pgid.pgid) >> (tc * 4)) & 0xf; INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); if (err != FW_PORT_DCB_CFG_SUCCESS) { dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n", -err); return; } *bw_per = pcmd.u.dcb.pgrate.pgrate[*pgid]; *up_tc_map = (1 << tc); /* prio_type is link strict */ *prio_type = 0x2; } static void cxgb4_getpgtccfg_tx(struct net_device *dev, int tc, u8 *prio_type, u8 *pgid, u8 *bw_per, u8 *up_tc_map) { return cxgb4_getpgtccfg(dev, tc, prio_type, pgid, bw_per, up_tc_map, 1); } static void cxgb4_getpgtccfg_rx(struct net_device *dev, int tc, u8 *prio_type, u8 *pgid, u8 *bw_per, u8 *up_tc_map) { return cxgb4_getpgtccfg(dev, tc, prio_type, pgid, bw_per, up_tc_map, 0); } static void cxgb4_setpgtccfg_tx(struct net_device *dev, int tc, u8 prio_type, u8 pgid, u8 bw_per, u8 up_tc_map) { struct fw_port_cmd pcmd; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = pi->adapter; u32 _pgid; int err; if (pgid == DCB_ATTR_VALUE_UNDEFINED) return; if (bw_per == DCB_ATTR_VALUE_UNDEFINED) return; INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); pcmd.u.dcb.pgid.type = FW_PORT_DCB_TYPE_PGID; err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); if (err != FW_PORT_DCB_CFG_SUCCESS) { dev_err(adap->pdev_dev, "DCB read PGID failed with %d\n", -err); return; } _pgid = be32_to_cpu(pcmd.u.dcb.pgid.pgid); _pgid &= ~(0xF << (tc * 4)); _pgid |= pgid << (tc * 4); pcmd.u.dcb.pgid.pgid = cpu_to_be32(_pgid); INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id); err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); if (err != FW_PORT_DCB_CFG_SUCCESS) { dev_err(adap->pdev_dev, "DCB write PGID failed with %d\n", -err); return; } memset(&pcmd, 0, sizeof(struct fw_port_cmd)); INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); if (err != FW_PORT_DCB_CFG_SUCCESS) { dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n", -err); return; } pcmd.u.dcb.pgrate.pgrate[pgid] = bw_per; INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id); if (pi->dcb.state == CXGB4_DCB_STATE_HOST) pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY); err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); if (err != FW_PORT_DCB_CFG_SUCCESS) dev_err(adap->pdev_dev, "DCB write PGRATE failed with %d\n", -err); } static void cxgb4_getpgbwgcfg(struct net_device *dev, int pgid, u8 *bw_per, int local) { struct fw_port_cmd pcmd; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = pi->adapter; int err; if (local) INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); else INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); if (err != FW_PORT_DCB_CFG_SUCCESS) { dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n", -err); } else { *bw_per = pcmd.u.dcb.pgrate.pgrate[pgid]; } } static void cxgb4_getpgbwgcfg_tx(struct net_device *dev, int pgid, u8 *bw_per) { return cxgb4_getpgbwgcfg(dev, pgid, bw_per, 1); } static void cxgb4_getpgbwgcfg_rx(struct net_device *dev, int pgid, u8 *bw_per) { return cxgb4_getpgbwgcfg(dev, pgid, bw_per, 0); } static void cxgb4_setpgbwgcfg_tx(struct net_device *dev, int pgid, u8 bw_per) { struct fw_port_cmd pcmd; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = pi->adapter; int err; INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); if (err != FW_PORT_DCB_CFG_SUCCESS) { dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n", -err); return; } pcmd.u.dcb.pgrate.pgrate[pgid] = bw_per; INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id); if (pi->dcb.state == CXGB4_DCB_STATE_HOST) pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY); err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); if (err != FW_PORT_DCB_CFG_SUCCESS) dev_err(adap->pdev_dev, "DCB write PGRATE failed with %d\n", -err); } /* Return whether the specified Traffic Class Priority has Priority Pause * Frames enabled. */ static void cxgb4_getpfccfg(struct net_device *dev, int priority, u8 *pfccfg) { struct port_info *pi = netdev2pinfo(dev); struct port_dcb_info *dcb = &pi->dcb; if (dcb->state != CXGB4_DCB_STATE_FW_ALLSYNCED || priority >= CXGB4_MAX_PRIORITY) *pfccfg = 0; else *pfccfg = (pi->dcb.pfcen >> priority) & 1; } /* Enable/disable Priority Pause Frames for the specified Traffic Class * Priority. */ static void cxgb4_setpfccfg(struct net_device *dev, int priority, u8 pfccfg) { struct fw_port_cmd pcmd; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = pi->adapter; int err; if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED || priority >= CXGB4_MAX_PRIORITY) return; INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id); if (pi->dcb.state == CXGB4_DCB_STATE_HOST) pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY); pcmd.u.dcb.pfc.type = FW_PORT_DCB_TYPE_PFC; pcmd.u.dcb.pfc.pfcen = pi->dcb.pfcen; if (pfccfg) pcmd.u.dcb.pfc.pfcen |= (1 << priority); else pcmd.u.dcb.pfc.pfcen &= (~(1 << priority)); err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); if (err != FW_PORT_DCB_CFG_SUCCESS) { dev_err(adap->pdev_dev, "DCB PFC write failed with %d\n", -err); return; } pi->dcb.pfcen = pcmd.u.dcb.pfc.pfcen; } static u8 cxgb4_setall(struct net_device *dev) { return 0; } /* Return DCB capabilities. */ static u8 cxgb4_getcap(struct net_device *dev, int cap_id, u8 *caps) { struct port_info *pi = netdev2pinfo(dev); switch (cap_id) { case DCB_CAP_ATTR_PG: case DCB_CAP_ATTR_PFC: *caps = true; break; case DCB_CAP_ATTR_PG_TCS: /* 8 priorities for PG represented by bitmap */ *caps = 0x80; break; case DCB_CAP_ATTR_PFC_TCS: /* 8 priorities for PFC represented by bitmap */ *caps = 0x80; break; case DCB_CAP_ATTR_GSP: *caps = true; break; case DCB_CAP_ATTR_UP2TC: case DCB_CAP_ATTR_BCN: *caps = false; break; case DCB_CAP_ATTR_DCBX: *caps = pi->dcb.supported; break; default: *caps = false; } return 0; } /* Return the number of Traffic Classes for the indicated Traffic Class ID. */ static int cxgb4_getnumtcs(struct net_device *dev, int tcs_id, u8 *num) { struct port_info *pi = netdev2pinfo(dev); switch (tcs_id) { case DCB_NUMTCS_ATTR_PG: if (pi->dcb.msgs & CXGB4_DCB_FW_PGRATE) *num = pi->dcb.pg_num_tcs_supported; else *num = 0x8; break; case DCB_NUMTCS_ATTR_PFC: *num = 0x8; break; default: return -EINVAL; } return 0; } /* Set the number of Traffic Classes supported for the indicated Traffic Class * ID. */ static int cxgb4_setnumtcs(struct net_device *dev, int tcs_id, u8 num) { /* Setting the number of Traffic Classes isn't supported. */ return -ENOSYS; } /* Return whether Priority Flow Control is enabled. */ static u8 cxgb4_getpfcstate(struct net_device *dev) { struct port_info *pi = netdev2pinfo(dev); if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED) return false; return pi->dcb.pfcen != 0; } /* Enable/disable Priority Flow Control. */ static void cxgb4_setpfcstate(struct net_device *dev, u8 state) { /* We can't enable/disable Priority Flow Control but we also can't * return an error ... */ } /* Return the Application User Priority Map associated with the specified * Application ID. */ static int __cxgb4_getapp(struct net_device *dev, u8 app_idtype, u16 app_id, int peer) { struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = pi->adapter; int i; if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED) return 0; for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) { struct fw_port_cmd pcmd; int err; if (peer) INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); else INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID; pcmd.u.dcb.app_priority.idx = i; err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); if (err != FW_PORT_DCB_CFG_SUCCESS) { dev_err(adap->pdev_dev, "DCB APP read failed with %d\n", -err); return err; } if (be16_to_cpu(pcmd.u.dcb.app_priority.protocolid) == app_id) return pcmd.u.dcb.app_priority.user_prio_map; /* exhausted app list */ if (!pcmd.u.dcb.app_priority.protocolid) break; } return -EEXIST; } /* Return the Application User Priority Map associated with the specified * Application ID. Since this routine is prototyped to return "u8" we can't * return errors ... */ static u8 cxgb4_getapp(struct net_device *dev, u8 app_idtype, u16 app_id) { int result = __cxgb4_getapp(dev, app_idtype, app_id, 0); if (result < 0) result = 0; return result; } /* Write a new Application User Priority Map for the specified Application ID. * This routine is prototyped to return "u8" but other instantiations of the * DCB NetLink Operations "setapp" routines return negative errnos for errors. * We follow their lead. */ static u8 cxgb4_setapp(struct net_device *dev, u8 app_idtype, u16 app_id, u8 app_prio) { struct fw_port_cmd pcmd; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = pi->adapter; int i, err; if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED) return -EINVAL; /* DCB info gets thrown away on link up */ if (!netif_carrier_ok(dev)) return -ENOLINK; if (app_idtype != DCB_APP_IDTYPE_ETHTYPE && app_idtype != DCB_APP_IDTYPE_PORTNUM) return -EINVAL; for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) { INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID; pcmd.u.dcb.app_priority.idx = i; err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); if (err != FW_PORT_DCB_CFG_SUCCESS) { dev_err(adap->pdev_dev, "DCB app table read failed with %d\n", -err); return err; } if (be16_to_cpu(pcmd.u.dcb.app_priority.protocolid) == app_id) { /* overwrite existing app table */ pcmd.u.dcb.app_priority.protocolid = 0; break; } /* find first empty slot */ if (!pcmd.u.dcb.app_priority.protocolid) break; } if (i == CXGB4_MAX_DCBX_APP_SUPPORTED) { /* no empty slots available */ dev_err(adap->pdev_dev, "DCB app table full\n"); return -EBUSY; } /* write out new app table entry */ INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id); if (pi->dcb.state == CXGB4_DCB_STATE_HOST) pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY); pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID; pcmd.u.dcb.app_priority.protocolid = cpu_to_be16(app_id); pcmd.u.dcb.app_priority.sel_field = app_idtype; pcmd.u.dcb.app_priority.user_prio_map = app_prio; pcmd.u.dcb.app_priority.idx = i; err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); if (err != FW_PORT_DCB_CFG_SUCCESS) { dev_err(adap->pdev_dev, "DCB app table write failed with %d\n", -err); return err; } return 0; } /* Return whether IEEE Data Center Bridging has been negotiated. */ static inline int cxgb4_ieee_negotiation_complete(struct net_device *dev) { struct port_info *pi = netdev2pinfo(dev); struct port_dcb_info *dcb = &pi->dcb; return (dcb->state == CXGB4_DCB_STATE_FW_ALLSYNCED && (dcb->supported & DCB_CAP_DCBX_VER_IEEE)); } /* Fill in the Application User Priority Map associated with the * specified Application. */ static int cxgb4_ieee_getapp(struct net_device *dev, struct dcb_app *app) { int prio; if (!cxgb4_ieee_negotiation_complete(dev)) return -EINVAL; if (!(app->selector && app->protocol)) return -EINVAL; prio = dcb_getapp(dev, app); if (prio == 0) { /* If app doesn't exist in dcb_app table, try firmware * directly. */ prio = __cxgb4_getapp(dev, app->selector, app->protocol, 0); } app->priority = prio; return 0; } /* Write a new Application User Priority Map for the specified App id. */ static int cxgb4_ieee_setapp(struct net_device *dev, struct dcb_app *app) { if (!cxgb4_ieee_negotiation_complete(dev)) return -EINVAL; if (!(app->selector && app->protocol && app->priority)) return -EINVAL; cxgb4_setapp(dev, app->selector, app->protocol, app->priority); return dcb_setapp(dev, app); } /* Return our DCBX parameters. */ static u8 cxgb4_getdcbx(struct net_device *dev) { struct port_info *pi = netdev2pinfo(dev); /* This is already set by cxgb4_set_dcb_caps, so just return it */ return pi->dcb.supported; } /* Set our DCBX parameters. */ static u8 cxgb4_setdcbx(struct net_device *dev, u8 dcb_request) { struct port_info *pi = netdev2pinfo(dev); /* Filter out requests which exceed our capabilities. */ if ((dcb_request & (CXGB4_DCBX_FW_SUPPORT | CXGB4_DCBX_HOST_SUPPORT)) != dcb_request) return 1; /* Can't set DCBX capabilities if DCBX isn't enabled. */ if (!pi->dcb.state) return 1; /* There's currently no mechanism to allow for the firmware DCBX * negotiation to be changed from the Host Driver. If the caller * requests exactly the same parameters that we already have then * we'll allow them to be successfully "set" ... */ if (dcb_request != pi->dcb.supported) return 1; pi->dcb.supported = dcb_request; return 0; } static int cxgb4_getpeer_app(struct net_device *dev, struct dcb_peer_app_info *info, u16 *app_count) { struct fw_port_cmd pcmd; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = pi->adapter; int i, err = 0; if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED) return 1; info->willing = 0; info->error = 0; *app_count = 0; for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) { INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID; pcmd.u.dcb.app_priority.idx = *app_count; err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); if (err != FW_PORT_DCB_CFG_SUCCESS) { dev_err(adap->pdev_dev, "DCB app table read failed with %d\n", -err); return err; } /* find first empty slot */ if (!pcmd.u.dcb.app_priority.protocolid) break; } *app_count = i; return err; } static int cxgb4_getpeerapp_tbl(struct net_device *dev, struct dcb_app *table) { struct fw_port_cmd pcmd; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = pi->adapter; int i, err = 0; if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED) return 1; for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) { INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID; pcmd.u.dcb.app_priority.idx = i; err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); if (err != FW_PORT_DCB_CFG_SUCCESS) { dev_err(adap->pdev_dev, "DCB app table read failed with %d\n", -err); return err; } /* find first empty slot */ if (!pcmd.u.dcb.app_priority.protocolid) break; table[i].selector = pcmd.u.dcb.app_priority.sel_field; table[i].protocol = be16_to_cpu(pcmd.u.dcb.app_priority.protocolid); table[i].priority = pcmd.u.dcb.app_priority.user_prio_map; } return err; } /* Return Priority Group information. */ static int cxgb4_cee_peer_getpg(struct net_device *dev, struct cee_pg *pg) { struct fw_port_cmd pcmd; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = pi->adapter; u32 pgid; int i, err; /* We're always "willing" -- the Switch Fabric always dictates the * DCBX parameters to us. */ pg->willing = true; INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); pcmd.u.dcb.pgid.type = FW_PORT_DCB_TYPE_PGID; err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); if (err != FW_PORT_DCB_CFG_SUCCESS) { dev_err(adap->pdev_dev, "DCB read PGID failed with %d\n", -err); return err; } pgid = be32_to_cpu(pcmd.u.dcb.pgid.pgid); for (i = 0; i < CXGB4_MAX_PRIORITY; i++) pg->prio_pg[i] = (pgid >> (i * 4)) & 0xF; INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); if (err != FW_PORT_DCB_CFG_SUCCESS) { dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n", -err); return err; } for (i = 0; i < CXGB4_MAX_PRIORITY; i++) pg->pg_bw[i] = pcmd.u.dcb.pgrate.pgrate[i]; return 0; } /* Return Priority Flow Control information. */ static int cxgb4_cee_peer_getpfc(struct net_device *dev, struct cee_pfc *pfc) { struct port_info *pi = netdev2pinfo(dev); cxgb4_getnumtcs(dev, DCB_NUMTCS_ATTR_PFC, &(pfc->tcs_supported)); pfc->pfc_en = pi->dcb.pfcen; return 0; } const struct dcbnl_rtnl_ops cxgb4_dcb_ops = { .ieee_getapp = cxgb4_ieee_getapp, .ieee_setapp = cxgb4_ieee_setapp, /* CEE std */ .getstate = cxgb4_getstate, .setstate = cxgb4_setstate, .getpgtccfgtx = cxgb4_getpgtccfg_tx, .getpgbwgcfgtx = cxgb4_getpgbwgcfg_tx, .getpgtccfgrx = cxgb4_getpgtccfg_rx, .getpgbwgcfgrx = cxgb4_getpgbwgcfg_rx, .setpgtccfgtx = cxgb4_setpgtccfg_tx, .setpgbwgcfgtx = cxgb4_setpgbwgcfg_tx, .setpfccfg = cxgb4_setpfccfg, .getpfccfg = cxgb4_getpfccfg, .setall = cxgb4_setall, .getcap = cxgb4_getcap, .getnumtcs = cxgb4_getnumtcs, .setnumtcs = cxgb4_setnumtcs, .getpfcstate = cxgb4_getpfcstate, .setpfcstate = cxgb4_setpfcstate, .getapp = cxgb4_getapp, .setapp = cxgb4_setapp, /* DCBX configuration */ .getdcbx = cxgb4_getdcbx, .setdcbx = cxgb4_setdcbx, /* peer apps */ .peer_getappinfo = cxgb4_getpeer_app, .peer_getapptable = cxgb4_getpeerapp_tbl, /* CEE peer */ .cee_peer_getpg = cxgb4_cee_peer_getpg, .cee_peer_getpfc = cxgb4_cee_peer_getpfc, };