static int get_filter_steerq(struct net_device *dev, struct ch_filter_specification *fs) { struct adapter *adapter = netdev2adap(dev); int iq; /* If the user has requested steering matching Ingress Packets * to a specific Queue Set, we need to make sure it's in range * for the port and map that into the Absolute Queue ID of the * Queue Set's Response Queue. */ if (!fs->dirsteer) { if (fs->iq) return -EINVAL; iq = 0; } else { struct port_info *pi = netdev_priv(dev); /* If the iq id is greater than the number of qsets, * then assume it is an absolute qid. */ if (fs->iq < pi->nqsets) iq = adapter->sge.ethrxq[pi->first_qset + fs->iq].rspq.abs_id; else iq = fs->iq; } return iq; }
int cxgb4_del_filter(struct net_device *dev, int filter_id, struct ch_filter_specification *fs) { struct filter_ctx ctx; int ret; /* If we are shutting down the adapter do not wait for completion */ if (netdev2adap(dev)->flags & SHUTTING_DOWN) return __cxgb4_del_filter(dev, filter_id, fs, NULL); init_completion(&ctx.completion); ret = __cxgb4_del_filter(dev, filter_id, fs, &ctx); if (ret) goto out; /* Wait for reply */ ret = wait_for_completion_timeout(&ctx.completion, 10 * HZ); if (!ret) return -ETIMEDOUT; ret = ctx.result; out: return ret; }
int cxgb4_get_filter_counters(struct net_device *dev, unsigned int fidx, u64 *hitcnt, u64 *bytecnt, bool hash) { struct adapter *adapter = netdev2adap(dev); return get_filter_count(adapter, fidx, hitcnt, bytecnt, hash); }
int cxgb4_get_free_ftid(struct net_device *dev, int family) { struct adapter *adap = netdev2adap(dev); struct tid_info *t = &adap->tids; int ftid; spin_lock_bh(&t->ftid_lock); if (family == PF_INET) { ftid = find_first_zero_bit(t->ftid_bmap, t->nftids); if (ftid >= t->nftids) ftid = -1; } else { if (is_t6(adap->params.chip)) { ftid = bitmap_find_free_region(t->ftid_bmap, t->nftids, 1); if (ftid < 0) goto out_unlock; /* this is only a lookup, keep the found region * unallocated */ bitmap_release_region(t->ftid_bmap, ftid, 1); } else { ftid = bitmap_find_free_region(t->ftid_bmap, t->nftids, 2); if (ftid < 0) goto out_unlock; bitmap_release_region(t->ftid_bmap, ftid, 2); } } out_unlock: spin_unlock_bh(&t->ftid_lock); return ftid; }
static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct adapter *adapter = netdev2adap(dev); u32 exprom_vers; strlcpy(info->driver, cxgb4_driver_name, sizeof(info->driver)); strlcpy(info->version, cxgb4_driver_version, sizeof(info->version)); strlcpy(info->bus_info, pci_name(adapter->pdev), sizeof(info->bus_info)); if (adapter->params.fw_vers) snprintf(info->fw_version, sizeof(info->fw_version), "%u.%u.%u.%u, TP %u.%u.%u.%u", FW_HDR_FW_VER_MAJOR_G(adapter->params.fw_vers), FW_HDR_FW_VER_MINOR_G(adapter->params.fw_vers), FW_HDR_FW_VER_MICRO_G(adapter->params.fw_vers), FW_HDR_FW_VER_BUILD_G(adapter->params.fw_vers), FW_HDR_FW_VER_MAJOR_G(adapter->params.tp_vers), FW_HDR_FW_VER_MINOR_G(adapter->params.tp_vers), FW_HDR_FW_VER_MICRO_G(adapter->params.tp_vers), FW_HDR_FW_VER_BUILD_G(adapter->params.tp_vers)); if (!t4_get_exprom_version(adapter, &exprom_vers)) snprintf(info->erom_version, sizeof(info->erom_version), "%u.%u.%u.%u", FW_HDR_FW_VER_MAJOR_G(exprom_vers), FW_HDR_FW_VER_MINOR_G(exprom_vers), FW_HDR_FW_VER_MICRO_G(exprom_vers), FW_HDR_FW_VER_BUILD_G(exprom_vers)); }
static int cxgb4_del_hash_filter(struct net_device *dev, int filter_id, struct filter_ctx *ctx) { struct adapter *adapter = netdev2adap(dev); struct tid_info *t = &adapter->tids; struct cpl_abort_req *abort_req; struct cpl_abort_rpl *abort_rpl; struct cpl_set_tcb_field *req; struct ulptx_idata *aligner; struct work_request_hdr *wr; struct filter_entry *f; struct sk_buff *skb; unsigned int wrlen; int ret; netdev_dbg(dev, "%s: filter_id = %d ; nftids = %d\n", __func__, filter_id, adapter->tids.nftids); if (filter_id > adapter->tids.ntids) return -E2BIG; f = lookup_tid(t, filter_id); if (!f) { netdev_err(dev, "%s: no filter entry for filter_id = %d", __func__, filter_id); return -EINVAL; } ret = writable_filter(f); if (ret) return ret; if (!f->valid) return -EINVAL; f->ctx = ctx; f->pending = 1; wrlen = roundup(sizeof(*wr) + (sizeof(*req) + sizeof(*aligner)) + sizeof(*abort_req) + sizeof(*abort_rpl), 16); skb = alloc_skb(wrlen, GFP_KERNEL); if (!skb) { netdev_err(dev, "%s: could not allocate skb ..\n", __func__); return -ENOMEM; } set_wr_txq(skb, CPL_PRIORITY_CONTROL, f->fs.val.iport & 0x3); req = (struct cpl_set_tcb_field *)__skb_put(skb, wrlen); INIT_ULPTX_WR(req, wrlen, 0, 0); wr = (struct work_request_hdr *)req; wr++; req = (struct cpl_set_tcb_field *)wr; mk_set_tcb_ulp(f, req, TCB_RSS_INFO_W, TCB_RSS_INFO_V(TCB_RSS_INFO_M), TCB_RSS_INFO_V(adapter->sge.fw_evtq.abs_id), 0, 1); aligner = (struct ulptx_idata *)(req + 1); abort_req = (struct cpl_abort_req *)(aligner + 1); mk_abort_req_ulp(abort_req, f->tid); abort_rpl = (struct cpl_abort_rpl *)(abort_req + 1); mk_abort_rpl_ulp(abort_rpl, f->tid); t4_ofld_send(adapter, skb); return 0; }
static int set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data) { u8 *buf; int err = 0; u32 aligned_offset, aligned_len, *p; struct adapter *adapter = netdev2adap(dev); if (eeprom->magic != EEPROM_MAGIC) return -EINVAL; aligned_offset = eeprom->offset & ~3; aligned_len = (eeprom->len + (eeprom->offset & 3) + 3) & ~3; if (adapter->pf > 0) { u32 start = 1024 + adapter->pf * EEPROMPFSIZE; if (aligned_offset < start || aligned_offset + aligned_len > start + EEPROMPFSIZE) return -EPERM; } if (aligned_offset != eeprom->offset || aligned_len != eeprom->len) { /* RMW possibly needed for first or last words. */ buf = kmalloc(aligned_len, GFP_KERNEL); if (!buf) return -ENOMEM; err = eeprom_rd_phys(adapter, aligned_offset, (u32 *)buf); if (!err && aligned_len > 4) err = eeprom_rd_phys(adapter, aligned_offset + aligned_len - 4, (u32 *)&buf[aligned_len - 4]); if (err) goto out; memcpy(buf + (eeprom->offset & 3), data, eeprom->len); } else { buf = data; } err = t4_seeprom_wp(adapter, false); if (err) goto out; for (p = (u32 *)buf; !err && aligned_len; aligned_len -= 4, p++) { err = eeprom_wr_phys(adapter, aligned_offset, *p); aligned_offset += 4; } if (!err) err = t4_seeprom_wp(adapter, true); out: if (buf != data) kfree(buf); return err; }
static void get_regs(struct net_device *dev, struct ethtool_regs *regs, void *buf) { struct adapter *adap = netdev2adap(dev); size_t buf_size; buf_size = t4_get_regs_len(adap); regs->version = mk_adap_vers(adap); t4_get_regs(adap, buf, buf_size); }
static u64 hash_filter_ntuple(struct ch_filter_specification *fs, struct net_device *dev) { struct adapter *adap = netdev2adap(dev); struct tp_params *tp = &adap->params.tp; u64 ntuple = 0; /* Initialize each of the fields which we care about which are present * in the Compressed Filter Tuple. */ if (tp->vlan_shift >= 0 && fs->mask.ivlan) ntuple |= (FT_VLAN_VLD_F | fs->val.ivlan) << tp->vlan_shift; if (tp->port_shift >= 0 && fs->mask.iport) ntuple |= (u64)fs->val.iport << tp->port_shift; if (tp->protocol_shift >= 0) { if (!fs->val.proto) ntuple |= (u64)IPPROTO_TCP << tp->protocol_shift; else ntuple |= (u64)fs->val.proto << tp->protocol_shift; } if (tp->tos_shift >= 0 && fs->mask.tos) ntuple |= (u64)(fs->val.tos) << tp->tos_shift; if (tp->vnic_shift >= 0) { if ((adap->params.tp.ingress_config & VNIC_F) && fs->mask.pfvf_vld) ntuple |= (u64)((fs->val.pfvf_vld << 16) | (fs->val.pf << 13) | (fs->val.vf)) << tp->vnic_shift; else ntuple |= (u64)((fs->val.ovlan_vld << 16) | (fs->val.ovlan)) << tp->vnic_shift; } if (tp->macmatch_shift >= 0 && fs->mask.macidx) ntuple |= (u64)(fs->val.macidx) << tp->macmatch_shift; if (tp->ethertype_shift >= 0 && fs->mask.ethtype) ntuple |= (u64)(fs->val.ethtype) << tp->ethertype_shift; if (tp->matchtype_shift >= 0 && fs->mask.matchtype) ntuple |= (u64)(fs->val.matchtype) << tp->matchtype_shift; if (tp->frag_shift >= 0 && fs->mask.frag) ntuple |= (u64)(fs->val.frag) << tp->frag_shift; if (tp->fcoe_shift >= 0 && fs->mask.fcoe) ntuple |= (u64)(fs->val.fcoe) << tp->fcoe_shift; return ntuple; }
static int identify_port(struct net_device *dev, enum ethtool_phys_id_state state) { unsigned int val; struct adapter *adap = netdev2adap(dev); if (state == ETHTOOL_ID_ACTIVE) val = 0xffff; else if (state == ETHTOOL_ID_INACTIVE) val = 0; else return -EINVAL; return t4_identify_port(adap, adap->pf, netdev2pinfo(dev)->viid, val); }
/* Check a delete filter request for validity and send it to the hardware. * Return 0 on success, an error number otherwise. We attach any provided * filter operation context to the internal filter specification in order to * facilitate signaling completion of the operation. */ int __cxgb4_del_filter(struct net_device *dev, int filter_id, struct ch_filter_specification *fs, struct filter_ctx *ctx) { struct adapter *adapter = netdev2adap(dev); unsigned int chip_ver = CHELSIO_CHIP_VERSION(adapter->params.chip); struct filter_entry *f; unsigned int max_fidx; int ret; if (fs && fs->hash) { if (is_hashfilter(adapter)) return cxgb4_del_hash_filter(dev, filter_id, ctx); netdev_err(dev, "%s: Exact-match filters only supported with Hash Filter configuration\n", __func__); return -EINVAL; } max_fidx = adapter->tids.nftids; if (filter_id != (max_fidx + adapter->tids.nsftids - 1) && filter_id >= max_fidx) return -E2BIG; f = &adapter->tids.ftid_tab[filter_id]; ret = writable_filter(f); if (ret) return ret; if (f->valid) { f->ctx = ctx; cxgb4_clear_ftid(&adapter->tids, filter_id, f->fs.type ? PF_INET6 : PF_INET, chip_ver); return del_filter_wr(adapter, filter_id); } /* If the caller has passed in a Completion Context then we need to * mark it as a successful completion so they don't stall waiting * for it. */ if (ctx) { ctx->result = 0; complete(&ctx->completion); } return ret; }
static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e, u8 *data) { int i, err = 0; struct adapter *adapter = netdev2adap(dev); u8 *buf = kmalloc(EEPROMSIZE, GFP_KERNEL); if (!buf) return -ENOMEM; e->magic = EEPROM_MAGIC; for (i = e->offset & ~3; !err && i < e->offset + e->len; i += 4) err = eeprom_rd_phys(adapter, i, (u32 *)&buf[i]); if (!err) memcpy(data, buf + e->offset, e->len); kfree(buf); return err; }
/* cxgb4_get_srq_entry: read the SRQ table entry * @dev: Pointer to the net_device * @idx: Index to the srq * @entryp: pointer to the srq entry * * Sends CPL_SRQ_TABLE_REQ message for the given index. * Contents will be returned in CPL_SRQ_TABLE_RPL message. * * Returns zero if the read is successful, else a error * number will be returned. Caller should not use the srq * entry if the return value is non-zero. * * */ int cxgb4_get_srq_entry(struct net_device *dev, int srq_idx, struct srq_entry *entryp) { struct cpl_srq_table_req *req; struct adapter *adap; struct sk_buff *skb; struct srq_data *s; int rc = -ENODEV; adap = netdev2adap(dev); s = adap->srq; if (!(adap->flags & FULL_INIT_DONE) || !s) goto out; skb = alloc_skb(sizeof(*req), GFP_KERNEL); if (!skb) return -ENOMEM; req = (struct cpl_srq_table_req *) __skb_put_zero(skb, sizeof(*req)); INIT_TP_WR(req, 0); OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SRQ_TABLE_REQ, TID_TID_V(srq_idx) | TID_QID_V(adap->sge.fw_evtq.abs_id))); req->idx = srq_idx; mutex_lock(&s->lock); s->entryp = entryp; t4_mgmt_tx(adap, skb); rc = wait_for_completion_timeout(&s->comp, SRQ_WAIT_TO); if (rc) rc = 0; else /* !rc means we timed out */ rc = -ETIMEDOUT; WARN_ON_ONCE(entryp->idx != srq_idx); mutex_unlock(&s->lock); out: return rc; }
static int set_flash(struct net_device *netdev, struct ethtool_flash *ef) { int ret; const struct firmware *fw; struct adapter *adap = netdev2adap(netdev); unsigned int mbox = PCIE_FW_MASTER_M + 1; u32 pcie_fw; unsigned int master; u8 master_vld = 0; pcie_fw = t4_read_reg(adap, PCIE_FW_A); master = PCIE_FW_MASTER_G(pcie_fw); if (pcie_fw & PCIE_FW_MASTER_VLD_F) master_vld = 1; /* if csiostor is the master return */ if (master_vld && (master != adap->pf)) { dev_warn(adap->pdev_dev, "cxgb4 driver needs to be loaded as MASTER to support FW flash\n"); return -EOPNOTSUPP; } ef->data[sizeof(ef->data) - 1] = '\0'; ret = request_firmware(&fw, ef->data, adap->pdev_dev); if (ret < 0) return ret; /* If the adapter has been fully initialized then we'll go ahead and * try to get the firmware's cooperation in upgrading to the new * firmware image otherwise we'll try to do the entire job from the * host ... and we always "force" the operation in this path. */ if (adap->flags & FULL_INIT_DONE) mbox = adap->mbox; ret = t4_fw_upgrade(adap, mbox, fw->data, fw->size, 1); release_firmware(fw); if (!ret) dev_info(adap->pdev_dev, "loaded firmware %s, reload cxgb4 driver\n", ef->data); return ret; }
static int cxgb4_set_hash_filter(struct net_device *dev, struct ch_filter_specification *fs, struct filter_ctx *ctx) { struct adapter *adapter = netdev2adap(dev); struct tid_info *t = &adapter->tids; struct filter_entry *f; struct sk_buff *skb; int iq, atid, size; int ret = 0; u32 iconf; fill_default_mask(fs); ret = validate_filter(dev, fs); if (ret) return ret; iq = get_filter_steerq(dev, fs); if (iq < 0) return iq; f = kzalloc(sizeof(*f), GFP_KERNEL); if (!f) return -ENOMEM; f->fs = *fs; f->ctx = ctx; f->dev = dev; f->fs.iq = iq; /* If the new filter requires loopback Destination MAC and/or VLAN * rewriting then we need to allocate a Layer 2 Table (L2T) entry for * the filter. */ if (f->fs.newdmac || f->fs.newvlan) { /* allocate L2T entry for new filter */ f->l2t = t4_l2t_alloc_switching(adapter, f->fs.vlan, f->fs.eport, f->fs.dmac); if (!f->l2t) { ret = -ENOMEM; goto out_err; } } /* If the new filter requires loopback Source MAC rewriting then * we need to allocate a SMT entry for the filter. */ if (f->fs.newsmac) { f->smt = cxgb4_smt_alloc_switching(f->dev, f->fs.smac); if (!f->smt) { if (f->l2t) { cxgb4_l2t_release(f->l2t); f->l2t = NULL; } ret = -ENOMEM; goto free_l2t; } } atid = cxgb4_alloc_atid(t, f); if (atid < 0) { ret = atid; goto free_smt; } iconf = adapter->params.tp.ingress_config; if (iconf & VNIC_F) { f->fs.val.ovlan = (fs->val.pf << 13) | fs->val.vf; f->fs.mask.ovlan = (fs->mask.pf << 13) | fs->mask.vf; f->fs.val.ovlan_vld = fs->val.pfvf_vld; f->fs.mask.ovlan_vld = fs->mask.pfvf_vld; } size = sizeof(struct cpl_t6_act_open_req); if (f->fs.type) { ret = cxgb4_clip_get(f->dev, (const u32 *)&f->fs.val.lip, 1); if (ret) goto free_atid; skb = alloc_skb(size, GFP_KERNEL); if (!skb) { ret = -ENOMEM; goto free_clip; } mk_act_open_req6(f, skb, ((adapter->sge.fw_evtq.abs_id << 14) | atid), adapter); } else { skb = alloc_skb(size, GFP_KERNEL); if (!skb) { ret = -ENOMEM; goto free_atid; } mk_act_open_req(f, skb, ((adapter->sge.fw_evtq.abs_id << 14) | atid), adapter); } f->pending = 1; set_wr_txq(skb, CPL_PRIORITY_SETUP, f->fs.val.iport & 0x3); t4_ofld_send(adapter, skb); return 0; free_clip: cxgb4_clip_release(f->dev, (const u32 *)&f->fs.val.lip, 1); free_atid: cxgb4_free_atid(t, atid); free_smt: if (f->smt) { cxgb4_smt_release(f->smt); f->smt = NULL; } free_l2t: if (f->l2t) { cxgb4_l2t_release(f->l2t); f->l2t = NULL; } out_err: kfree(f); return ret; }
static u32 get_msglevel(struct net_device *dev) { return netdev2adap(dev)->msg_enable; }
static void set_msglevel(struct net_device *dev, u32 val) { netdev2adap(dev)->msg_enable = val; }
/* Validate filter spec against configuration done on the card. */ static int validate_filter(struct net_device *dev, struct ch_filter_specification *fs) { struct adapter *adapter = netdev2adap(dev); u32 fconf, iconf; /* Check for unconfigured fields being used. */ fconf = adapter->params.tp.vlan_pri_map; iconf = adapter->params.tp.ingress_config; if (unsupported(fconf, FCOE_F, fs->val.fcoe, fs->mask.fcoe) || unsupported(fconf, PORT_F, fs->val.iport, fs->mask.iport) || unsupported(fconf, TOS_F, fs->val.tos, fs->mask.tos) || unsupported(fconf, ETHERTYPE_F, fs->val.ethtype, fs->mask.ethtype) || unsupported(fconf, MACMATCH_F, fs->val.macidx, fs->mask.macidx) || unsupported(fconf, MPSHITTYPE_F, fs->val.matchtype, fs->mask.matchtype) || unsupported(fconf, FRAGMENTATION_F, fs->val.frag, fs->mask.frag) || unsupported(fconf, PROTOCOL_F, fs->val.proto, fs->mask.proto) || unsupported(fconf, VNIC_ID_F, fs->val.pfvf_vld, fs->mask.pfvf_vld) || unsupported(fconf, VNIC_ID_F, fs->val.ovlan_vld, fs->mask.ovlan_vld) || unsupported(fconf, VLAN_F, fs->val.ivlan_vld, fs->mask.ivlan_vld)) return -EOPNOTSUPP; /* T4 inconveniently uses the same FT_VNIC_ID_W bits for both the Outer * VLAN Tag and PF/VF/VFvld fields based on VNIC_F being set * in TP_INGRESS_CONFIG. Hense the somewhat crazy checks * below. Additionally, since the T4 firmware interface also * carries that overlap, we need to translate any PF/VF * specification into that internal format below. */ if (is_field_set(fs->val.pfvf_vld, fs->mask.pfvf_vld) && is_field_set(fs->val.ovlan_vld, fs->mask.ovlan_vld)) return -EOPNOTSUPP; if (unsupported(iconf, VNIC_F, fs->val.pfvf_vld, fs->mask.pfvf_vld) || (is_field_set(fs->val.ovlan_vld, fs->mask.ovlan_vld) && (iconf & VNIC_F))) return -EOPNOTSUPP; if (fs->val.pf > 0x7 || fs->val.vf > 0x7f) return -ERANGE; fs->mask.pf &= 0x7; fs->mask.vf &= 0x7f; /* If the user is requesting that the filter action loop * matching packets back out one of our ports, make sure that * the egress port is in range. */ if (fs->action == FILTER_SWITCH && fs->eport >= adapter->params.nports) return -ERANGE; /* Don't allow various trivially obvious bogus out-of-range values... */ if (fs->val.iport >= adapter->params.nports) return -ERANGE; /* T4 doesn't support removing VLAN Tags for loop back filters. */ if (is_t4(adapter->params.chip) && fs->action == FILTER_SWITCH && (fs->newvlan == VLAN_REMOVE || fs->newvlan == VLAN_REWRITE)) return -EOPNOTSUPP; return 0; }
static int get_regs_len(struct net_device *dev) { struct adapter *adap = netdev2adap(dev); return t4_get_regs_len(adap); }
/* Check a Chelsio Filter Request for validity, convert it into our internal * format and send it to the hardware. Return 0 on success, an error number * otherwise. We attach any provided filter operation context to the internal * filter specification in order to facilitate signaling completion of the * operation. */ int __cxgb4_set_filter(struct net_device *dev, int filter_id, struct ch_filter_specification *fs, struct filter_ctx *ctx) { struct adapter *adapter = netdev2adap(dev); unsigned int chip_ver = CHELSIO_CHIP_VERSION(adapter->params.chip); unsigned int max_fidx, fidx; struct filter_entry *f; u32 iconf; int iq, ret; if (fs->hash) { if (is_hashfilter(adapter)) return cxgb4_set_hash_filter(dev, fs, ctx); netdev_err(dev, "%s: Exact-match filters only supported with Hash Filter configuration\n", __func__); return -EINVAL; } max_fidx = adapter->tids.nftids; if (filter_id != (max_fidx + adapter->tids.nsftids - 1) && filter_id >= max_fidx) return -E2BIG; fill_default_mask(fs); ret = validate_filter(dev, fs); if (ret) return ret; iq = get_filter_steerq(dev, fs); if (iq < 0) return iq; /* IPv6 filters occupy four slots and must be aligned on * four-slot boundaries. IPv4 filters only occupy a single * slot and have no alignment requirements but writing a new * IPv4 filter into the middle of an existing IPv6 filter * requires clearing the old IPv6 filter and hence we prevent * insertion. */ if (fs->type == 0) { /* IPv4 */ /* For T6, If our IPv4 filter isn't being written to a * multiple of two filter index and there's an IPv6 * filter at the multiple of 2 base slot, then we need * to delete that IPv6 filter ... * For adapters below T6, IPv6 filter occupies 4 entries. * Hence we need to delete the filter in multiple of 4 slot. */ if (chip_ver < CHELSIO_T6) fidx = filter_id & ~0x3; else fidx = filter_id & ~0x1; if (fidx != filter_id && adapter->tids.ftid_tab[fidx].fs.type) { f = &adapter->tids.ftid_tab[fidx]; if (f->valid) { dev_err(adapter->pdev_dev, "Invalid location. IPv6 requires 4 slots and is occupying slots %u to %u\n", fidx, fidx + 3); return -EINVAL; } } } else { /* IPv6 */ if (chip_ver < CHELSIO_T6) { /* Ensure that the IPv6 filter is aligned on a * multiple of 4 boundary. */ if (filter_id & 0x3) { dev_err(adapter->pdev_dev, "Invalid location. IPv6 must be aligned on a 4-slot boundary\n"); return -EINVAL; } /* Check all except the base overlapping IPv4 filter * slots. */ for (fidx = filter_id + 1; fidx < filter_id + 4; fidx++) { f = &adapter->tids.ftid_tab[fidx]; if (f->valid) { dev_err(adapter->pdev_dev, "Invalid location. IPv6 requires 4 slots and an IPv4 filter exists at %u\n", fidx); return -EBUSY; } } } else { /* For T6, CLIP being enabled, IPv6 filter would occupy * 2 entries. */ if (filter_id & 0x1) return -EINVAL; /* Check overlapping IPv4 filter slot */ fidx = filter_id + 1; f = &adapter->tids.ftid_tab[fidx]; if (f->valid) { pr_err("%s: IPv6 filter requires 2 indices. IPv4 filter already present at %d. Please remove IPv4 filter first.\n", __func__, fidx); return -EBUSY; } } } /* Check to make sure that provided filter index is not * already in use by someone else */ f = &adapter->tids.ftid_tab[filter_id]; if (f->valid) return -EBUSY; fidx = filter_id + adapter->tids.ftid_base; ret = cxgb4_set_ftid(&adapter->tids, filter_id, fs->type ? PF_INET6 : PF_INET, chip_ver); if (ret) return ret; /* Check t make sure the filter requested is writable ... */ ret = writable_filter(f); if (ret) { /* Clear the bits we have set above */ cxgb4_clear_ftid(&adapter->tids, filter_id, fs->type ? PF_INET6 : PF_INET, chip_ver); return ret; } if (is_t6(adapter->params.chip) && fs->type && ipv6_addr_type((const struct in6_addr *)fs->val.lip) != IPV6_ADDR_ANY) { ret = cxgb4_clip_get(dev, (const u32 *)&fs->val.lip, 1); if (ret) { cxgb4_clear_ftid(&adapter->tids, filter_id, PF_INET6, chip_ver); return ret; } } /* Convert the filter specification into our internal format. * We copy the PF/VF specification into the Outer VLAN field * here so the rest of the code -- including the interface to * the firmware -- doesn't have to constantly do these checks. */ f->fs = *fs; f->fs.iq = iq; f->dev = dev; iconf = adapter->params.tp.ingress_config; if (iconf & VNIC_F) { f->fs.val.ovlan = (fs->val.pf << 13) | fs->val.vf; f->fs.mask.ovlan = (fs->mask.pf << 13) | fs->mask.vf; f->fs.val.ovlan_vld = fs->val.pfvf_vld; f->fs.mask.ovlan_vld = fs->mask.pfvf_vld; } /* Attempt to set the filter. If we don't succeed, we clear * it and return the failure. */ f->ctx = ctx; f->tid = fidx; /* Save the actual tid */ ret = set_filter_wr(adapter, filter_id); if (ret) { cxgb4_clear_ftid(&adapter->tids, filter_id, fs->type ? PF_INET6 : PF_INET, chip_ver); clear_filter(adapter, f); } return ret; }