static int ng_vlan_rcvmsg(node_p node, item_p item, hook_p lasthook) { const priv_p priv = NG_NODE_PRIVATE(node); struct ng_mesg *msg, *resp = NULL; struct ng_vlan_filter *vf; hook_p hook; struct ng_vlan_table *t; uintptr_t hook_data; int i, vlan_count; uint16_t vid; int error = 0; NGI_GET_MSG(item, msg); /* Deal with message according to cookie and command. */ switch (msg->header.typecookie) { case NGM_VLAN_COOKIE: switch (msg->header.cmd) { case NGM_VLAN_ADD_FILTER: /* Check that message is long enough. */ if (msg->header.arglen != sizeof(*vf)) { error = EINVAL; break; } vf = (struct ng_vlan_filter *)msg->data; /* Sanity check the VLAN ID value. */ #ifdef NG_VLAN_USE_OLD_VLAN_NAME if (vf->vid == 0 && vf->vid != vf->vlan) { vf->vid = vf->vlan; } else if (vf->vid != 0 && vf->vlan != 0 && vf->vid != vf->vlan) { error = EINVAL; break; } #endif if (vf->vid & ~EVL_VLID_MASK || vf->pcp & ~7 || vf->cfi & ~1) { error = EINVAL; break; } /* Check that a referenced hook exists. */ hook = ng_findhook(node, vf->hook_name); if (hook == NULL) { error = ENOENT; break; } /* And is not one of the special hooks. */ if (hook == priv->downstream_hook || hook == priv->nomatch_hook) { error = EINVAL; break; } /* And is not already in service. */ if (IS_HOOK_VLAN_SET(NG_HOOK_PRIVATE(hook))) { error = EEXIST; break; } /* Check we don't already trap this VLAN. */ if (priv->vlan_hook[vf->vid] != NULL) { error = EEXIST; break; } /* Link vlan and hook together. */ NG_HOOK_SET_PRIVATE(hook, (void *)(HOOK_VLAN_TAG_SET_MASK | EVL_MAKETAG(vf->vid, vf->pcp, vf->cfi))); priv->vlan_hook[vf->vid] = hook; break; case NGM_VLAN_DEL_FILTER: /* Check that message is long enough. */ if (msg->header.arglen != NG_HOOKSIZ) { error = EINVAL; break; } /* Check that hook exists and is active. */ hook = ng_findhook(node, (char *)msg->data); if (hook == NULL) { error = ENOENT; break; } hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook); if (IS_HOOK_VLAN_SET(hook_data) == 0) { error = ENOENT; break; } KASSERT(priv->vlan_hook[EVL_VLANOFTAG(hook_data)] == hook, ("%s: NGM_VLAN_DEL_FILTER: Invalid VID for Hook = %s\n", __func__, (char *)msg->data)); /* Purge a rule that refers to this hook. */ priv->vlan_hook[EVL_VLANOFTAG(hook_data)] = NULL; NG_HOOK_SET_PRIVATE(hook, NULL); break; case NGM_VLAN_DEL_VID_FLT: /* Check that message is long enough. */ if (msg->header.arglen != sizeof(uint16_t)) { error = EINVAL; break; } vid = (*((uint16_t *)msg->data)); /* Sanity check the VLAN ID value. */ if (vid & ~EVL_VLID_MASK) { error = EINVAL; break; } /* Check that hook exists and is active. */ hook = priv->vlan_hook[vid]; if (hook == NULL) { error = ENOENT; break; } hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook); if (IS_HOOK_VLAN_SET(hook_data) == 0) { error = ENOENT; break; } KASSERT(EVL_VLANOFTAG(hook_data) == vid, ("%s: NGM_VLAN_DEL_VID_FLT:" " Invalid VID Hook = %us, must be: %us\n", __func__, (uint16_t )EVL_VLANOFTAG(hook_data), vid)); /* Purge a rule that refers to this hook. */ priv->vlan_hook[vid] = NULL; NG_HOOK_SET_PRIVATE(hook, NULL); break; case NGM_VLAN_GET_TABLE: /* Calculate vlans. */ vlan_count = 0; for (i = 0; i < (EVL_VLID_MASK + 1); i ++) { if (priv->vlan_hook[i] != NULL && NG_HOOK_IS_VALID(priv->vlan_hook[i])) vlan_count ++; } /* Allocate memory for responce. */ NG_MKRESPONSE(resp, msg, sizeof(*t) + vlan_count * sizeof(*t->filter), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } /* Pack data to responce. */ t = (struct ng_vlan_table *)resp->data; t->n = 0; vf = &t->filter[0]; for (i = 0; i < (EVL_VLID_MASK + 1); i ++) { hook = priv->vlan_hook[i]; if (hook == NULL || NG_HOOK_NOT_VALID(hook)) continue; hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook); if (IS_HOOK_VLAN_SET(hook_data) == 0) continue; KASSERT(EVL_VLANOFTAG(hook_data) == i, ("%s: NGM_VLAN_GET_TABLE:" " hook %s VID = %us, must be: %i\n", __func__, NG_HOOK_NAME(hook), (uint16_t)EVL_VLANOFTAG(hook_data), i)); #ifdef NG_VLAN_USE_OLD_VLAN_NAME vf->vlan = i; #endif vf->vid = i; vf->pcp = EVL_PRIOFTAG(hook_data); vf->cfi = EVL_CFIOFTAG(hook_data); strncpy(vf->hook_name, NG_HOOK_NAME(hook), NG_HOOKSIZ); vf ++; t->n ++; } break; case NGM_VLAN_GET_DECAP: NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } (*((uint32_t *)resp->data)) = priv->decap_enable; break; case NGM_VLAN_SET_DECAP: if (msg->header.arglen != sizeof(uint32_t)) { error = EINVAL; break; } priv->decap_enable = (*((uint32_t *)msg->data)); break; case NGM_VLAN_GET_ENCAP: NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } (*((uint32_t *)resp->data)) = priv->encap_enable; break; case NGM_VLAN_SET_ENCAP: if (msg->header.arglen != sizeof(uint32_t)) { error = EINVAL; break; } priv->encap_enable = (*((uint32_t *)msg->data)); break; case NGM_VLAN_GET_ENCAP_PROTO: NG_MKRESPONSE(resp, msg, sizeof(uint16_t), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } (*((uint16_t *)resp->data)) = ntohs(priv->encap_proto); break; case NGM_VLAN_SET_ENCAP_PROTO: if (msg->header.arglen != sizeof(uint16_t)) { error = EINVAL; break; } priv->encap_proto = htons((*((uint16_t *)msg->data))); break; default: /* Unknown command. */ error = EINVAL; break; } break; case NGM_FLOW_COOKIE: { struct ng_mesg *copy; /* * Flow control messages should come only * from downstream. */ if (lasthook == NULL) break; if (lasthook != priv->downstream_hook) break; /* Broadcast the event to all uplinks. */ for (i = 0; i < (EVL_VLID_MASK + 1); i ++) { if (priv->vlan_hook[i] == NULL) continue; NG_COPYMESSAGE(copy, msg, M_NOWAIT); if (copy == NULL) continue; NG_SEND_MSG_HOOK(error, node, copy, priv->vlan_hook[i], 0); } break; } default: /* Unknown type cookie. */ error = EINVAL; break; } NG_RESPOND_MSG(error, node, item, resp); NG_FREE_MSG(msg); return (error); }
/* * active open (soconnect). * * State of affairs on entry: * soisconnecting (so_state |= SS_ISCONNECTING) * tcbinfo not locked (This has changed - used to be WLOCKed) * inp WLOCKed * tp->t_state = TCPS_SYN_SENT * rtalloc1, RT_UNLOCK on rt. */ int t4_connect(struct toedev *tod, struct socket *so, struct rtentry *rt, struct sockaddr *nam) { struct adapter *sc = tod->tod_softc; struct toepcb *toep = NULL; struct wrqe *wr = NULL; struct ifnet *rt_ifp = rt->rt_ifp; struct vi_info *vi; int mtu_idx, rscale, qid_atid, rc, isipv6, txqid, rxqid; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp = intotcpcb(inp); int reason; struct offload_settings settings; uint16_t vid = 0xfff, pcp = 0; INP_WLOCK_ASSERT(inp); KASSERT(nam->sa_family == AF_INET || nam->sa_family == AF_INET6, ("%s: dest addr %p has family %u", __func__, nam, nam->sa_family)); if (rt_ifp->if_type == IFT_ETHER) vi = rt_ifp->if_softc; else if (rt_ifp->if_type == IFT_L2VLAN) { struct ifnet *ifp = VLAN_TRUNKDEV(rt_ifp); vi = ifp->if_softc; VLAN_TAG(rt_ifp, &vid); VLAN_PCP(rt_ifp, &pcp); } else if (rt_ifp->if_type == IFT_IEEE8023ADLAG) DONT_OFFLOAD_ACTIVE_OPEN(ENOSYS); /* XXX: implement lagg+TOE */ else DONT_OFFLOAD_ACTIVE_OPEN(ENOTSUP); rw_rlock(&sc->policy_lock); settings = *lookup_offload_policy(sc, OPEN_TYPE_ACTIVE, NULL, EVL_MAKETAG(vid, pcp, 0), inp); rw_runlock(&sc->policy_lock); if (!settings.offload) DONT_OFFLOAD_ACTIVE_OPEN(EPERM); if (settings.txq >= 0 && settings.txq < vi->nofldtxq) txqid = settings.txq; else txqid = arc4random() % vi->nofldtxq; txqid += vi->first_ofld_txq; if (settings.rxq >= 0 && settings.rxq < vi->nofldrxq) rxqid = settings.rxq; else rxqid = arc4random() % vi->nofldrxq; rxqid += vi->first_ofld_rxq; toep = alloc_toepcb(vi, txqid, rxqid, M_NOWAIT | M_ZERO); if (toep == NULL) DONT_OFFLOAD_ACTIVE_OPEN(ENOMEM); toep->tid = alloc_atid(sc, toep); if (toep->tid < 0) DONT_OFFLOAD_ACTIVE_OPEN(ENOMEM); toep->l2te = t4_l2t_get(vi->pi, rt_ifp, rt->rt_flags & RTF_GATEWAY ? rt->rt_gateway : nam); if (toep->l2te == NULL) DONT_OFFLOAD_ACTIVE_OPEN(ENOMEM); isipv6 = nam->sa_family == AF_INET6; wr = alloc_wrqe(act_open_cpl_size(sc, isipv6), toep->ctrlq); if (wr == NULL) DONT_OFFLOAD_ACTIVE_OPEN(ENOMEM); toep->vnet = so->so_vnet; set_ulp_mode(toep, select_ulp_mode(so, sc, &settings)); SOCKBUF_LOCK(&so->so_rcv); /* opt0 rcv_bufsiz initially, assumes its normal meaning later */ toep->rx_credits = min(select_rcv_wnd(so) >> 10, M_RCV_BUFSIZ); SOCKBUF_UNLOCK(&so->so_rcv); /* * The kernel sets request_r_scale based on sb_max whereas we need to * take hardware's MAX_RCV_WND into account too. This is normally a * no-op as MAX_RCV_WND is much larger than the default sb_max. */ if (tp->t_flags & TF_REQ_SCALE) rscale = tp->request_r_scale = select_rcv_wscale(); else rscale = 0; mtu_idx = find_best_mtu_idx(sc, &inp->inp_inc, &settings); qid_atid = V_TID_QID(toep->ofld_rxq->iq.abs_id) | V_TID_TID(toep->tid) | V_TID_COOKIE(CPL_COOKIE_TOM); if (isipv6) { struct cpl_act_open_req6 *cpl = wrtod(wr); struct cpl_t5_act_open_req6 *cpl5 = (void *)cpl; struct cpl_t6_act_open_req6 *cpl6 = (void *)cpl; if ((inp->inp_vflag & INP_IPV6) == 0) DONT_OFFLOAD_ACTIVE_OPEN(ENOTSUP); toep->ce = t4_hold_lip(sc, &inp->in6p_laddr, NULL); if (toep->ce == NULL) DONT_OFFLOAD_ACTIVE_OPEN(ENOENT); switch (chip_id(sc)) { case CHELSIO_T4: INIT_TP_WR(cpl, 0); cpl->params = select_ntuple(vi, toep->l2te); break; case CHELSIO_T5: INIT_TP_WR(cpl5, 0); cpl5->iss = htobe32(tp->iss); cpl5->params = select_ntuple(vi, toep->l2te); break; case CHELSIO_T6: default: INIT_TP_WR(cpl6, 0); cpl6->iss = htobe32(tp->iss); cpl6->params = select_ntuple(vi, toep->l2te); break; } OPCODE_TID(cpl) = htobe32(MK_OPCODE_TID(CPL_ACT_OPEN_REQ6, qid_atid)); cpl->local_port = inp->inp_lport; cpl->local_ip_hi = *(uint64_t *)&inp->in6p_laddr.s6_addr[0]; cpl->local_ip_lo = *(uint64_t *)&inp->in6p_laddr.s6_addr[8]; cpl->peer_port = inp->inp_fport; cpl->peer_ip_hi = *(uint64_t *)&inp->in6p_faddr.s6_addr[0]; cpl->peer_ip_lo = *(uint64_t *)&inp->in6p_faddr.s6_addr[8]; cpl->opt0 = calc_opt0(so, vi, toep->l2te, mtu_idx, rscale, toep->rx_credits, toep->ulp_mode, &settings); cpl->opt2 = calc_opt2a(so, toep, &settings); } else { struct cpl_act_open_req *cpl = wrtod(wr); struct cpl_t5_act_open_req *cpl5 = (void *)cpl; struct cpl_t6_act_open_req *cpl6 = (void *)cpl; switch (chip_id(sc)) { case CHELSIO_T4: INIT_TP_WR(cpl, 0); cpl->params = select_ntuple(vi, toep->l2te); break; case CHELSIO_T5: INIT_TP_WR(cpl5, 0); cpl5->iss = htobe32(tp->iss); cpl5->params = select_ntuple(vi, toep->l2te); break; case CHELSIO_T6: default: INIT_TP_WR(cpl6, 0); cpl6->iss = htobe32(tp->iss); cpl6->params = select_ntuple(vi, toep->l2te); break; } OPCODE_TID(cpl) = htobe32(MK_OPCODE_TID(CPL_ACT_OPEN_REQ, qid_atid)); inp_4tuple_get(inp, &cpl->local_ip, &cpl->local_port, &cpl->peer_ip, &cpl->peer_port); cpl->opt0 = calc_opt0(so, vi, toep->l2te, mtu_idx, rscale, toep->rx_credits, toep->ulp_mode, &settings); cpl->opt2 = calc_opt2a(so, toep, &settings); } CTR5(KTR_CXGBE, "%s: atid %u (%s), toep %p, inp %p", __func__, toep->tid, tcpstates[tp->t_state], toep, inp); offload_socket(so, toep); rc = t4_l2t_send(sc, wr, toep->l2te); if (rc == 0) { toep->flags |= TPF_CPL_PENDING; return (0); } undo_offload_socket(so); reason = __LINE__; failed: CTR3(KTR_CXGBE, "%s: not offloading (%d), rc %d", __func__, reason, rc); if (wr) free_wrqe(wr); if (toep) { if (toep->tid >= 0) free_atid(sc, toep->tid); if (toep->l2te) t4_l2t_release(toep->l2te); if (toep->ce) t4_release_lip(sc, toep->ce); free_toepcb(toep); } return (rc); }