1237263Snp/*- 2237263Snp * Copyright (c) 2012 Chelsio Communications, Inc. 3237263Snp * All rights reserved. 4237263Snp * 5237263Snp * Redistribution and use in source and binary forms, with or without 6237263Snp * modification, are permitted provided that the following conditions 7237263Snp * are met: 8237263Snp * 1. Redistributions of source code must retain the above copyright 9237263Snp * notice, this list of conditions and the following disclaimer. 10237263Snp * 2. Redistributions in binary form must reproduce the above copyright 11237263Snp * notice, this list of conditions and the following disclaimer in the 12237263Snp * documentation and/or other materials provided with the distribution. 13237263Snp * 14237263Snp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15237263Snp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16237263Snp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17237263Snp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18237263Snp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19237263Snp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20237263Snp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21237263Snp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22237263Snp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23237263Snp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24237263Snp * SUCH DAMAGE. 25237263Snp */ 26174641Skmacy 27237263Snp#include <sys/cdefs.h> 28237263Snp__FBSDID("$FreeBSD$"); 29174641Skmacy 30237263Snp#include "opt_inet.h" 31174641Skmacy 32237263Snp#ifdef TCP_OFFLOAD 33237263Snp#include <sys/param.h> 34237263Snp#include <sys/refcount.h> 35237263Snp#include <sys/socket.h> 36237263Snp#include <sys/socketvar.h> 37237263Snp#include <sys/sysctl.h> 38237263Snp#include <net/if.h> 39257241Sglebius#include <net/if_var.h> 40237263Snp#include <net/route.h> 41237263Snp#include <netinet/in.h> 42237263Snp#include <netinet/ip.h> 43293309Smelifaro#include <netinet/in_fib.h> 44237263Snp#include <netinet/in_pcb.h> 45237263Snp#include <netinet/in_var.h> 46239544Snp#include <netinet/tcp_timer.h> 47237263Snp#define TCPSTATES 48237263Snp#include <netinet/tcp_fsm.h> 49294889Sglebius#include <netinet/tcp_var.h> 50237263Snp#include <netinet/toecore.h> 51174641Skmacy 52237263Snp#include "cxgb_include.h" 53237263Snp#include "ulp/tom/cxgb_tom.h" 54237263Snp#include "ulp/tom/cxgb_l2t.h" 55237263Snp#include "ulp/tom/cxgb_toepcb.h" 56174641Skmacy 57237263Snpstatic void t3_send_reset_synqe(struct toedev *, struct synq_entry *); 58174641Skmacy 59237263Snpstatic int 60237263Snpalloc_stid(struct tid_info *t, void *ctx) 61237263Snp{ 62237263Snp int stid = -1; 63174641Skmacy 64237263Snp mtx_lock(&t->stid_lock); 65237263Snp if (t->sfree) { 66237263Snp union listen_entry *p = t->sfree; 67174641Skmacy 68237263Snp stid = (p - t->stid_tab) + t->stid_base; 69237263Snp t->sfree = p->next; 70237263Snp p->ctx = ctx; 71237263Snp t->stids_in_use++; 72237263Snp } 73237263Snp mtx_unlock(&t->stid_lock); 74237263Snp return (stid); 75237263Snp} 76181067Skmacy 77237263Snpstatic void 78237263Snpfree_stid(struct tid_info *t, int stid) 79237263Snp{ 80237263Snp union listen_entry *p = stid2entry(t, stid); 81181067Skmacy 82237263Snp mtx_lock(&t->stid_lock); 83237263Snp p->next = t->sfree; 84237263Snp t->sfree = p; 85237263Snp t->stids_in_use--; 86237263Snp mtx_unlock(&t->stid_lock); 87237263Snp} 88174641Skmacy 89237263Snpstatic struct listen_ctx * 90237263Snpalloc_lctx(struct tom_data *td, struct inpcb *inp, int qset) 91237263Snp{ 92237263Snp struct listen_ctx *lctx; 93174641Skmacy 94237263Snp INP_WLOCK_ASSERT(inp); 95174641Skmacy 96237263Snp lctx = malloc(sizeof(struct listen_ctx), M_CXGB, M_NOWAIT | M_ZERO); 97237263Snp if (lctx == NULL) 98237263Snp return (NULL); 99174641Skmacy 100237263Snp lctx->stid = alloc_stid(&td->tid_maps, lctx); 101237263Snp if (lctx->stid < 0) { 102237263Snp free(lctx, M_CXGB); 103237263Snp return (NULL); 104237263Snp } 105174641Skmacy 106237263Snp lctx->inp = inp; 107237263Snp in_pcbref(inp); 108174641Skmacy 109237263Snp lctx->qset = qset; 110237263Snp refcount_init(&lctx->refcnt, 1); 111237263Snp TAILQ_INIT(&lctx->synq); 112174641Skmacy 113237263Snp return (lctx); 114237263Snp} 115174641Skmacy 116237263Snp/* Don't call this directly, use release_lctx instead */ 117237263Snpstatic int 118237263Snpfree_lctx(struct tom_data *td, struct listen_ctx *lctx) 119237263Snp{ 120237263Snp struct inpcb *inp = lctx->inp; 121174641Skmacy 122237263Snp INP_WLOCK_ASSERT(inp); 123237263Snp KASSERT(lctx->refcnt == 0, 124237263Snp ("%s: refcnt %d", __func__, lctx->refcnt)); 125237263Snp KASSERT(TAILQ_EMPTY(&lctx->synq), 126237263Snp ("%s: synq not empty.", __func__)); 127237263Snp KASSERT(lctx->stid >= 0, ("%s: bad stid %d.", __func__, lctx->stid)); 128174641Skmacy 129237263Snp CTR4(KTR_CXGB, "%s: stid %u, lctx %p, inp %p", 130237263Snp __func__, lctx->stid, lctx, lctx->inp); 131237263Snp 132237263Snp free_stid(&td->tid_maps, lctx->stid); 133237263Snp free(lctx, M_CXGB); 134237263Snp 135237263Snp return in_pcbrele_wlocked(inp); 136237263Snp} 137237263Snp 138237263Snpstatic void 139237263Snphold_lctx(struct listen_ctx *lctx) 140237263Snp{ 141237263Snp 142237263Snp refcount_acquire(&lctx->refcnt); 143237263Snp} 144237263Snp 145237263Snpstatic inline uint32_t 146237263Snplisten_hashfn(void *key, u_long mask) 147237263Snp{ 148237263Snp 149237263Snp return (fnv_32_buf(&key, sizeof(key), FNV1_32_INIT) & mask); 150237263Snp} 151237263Snp 152174641Skmacy/* 153237263Snp * Add a listen_ctx entry to the listen hash table. 154237263Snp */ 155237263Snpstatic void 156237263Snplisten_hash_add(struct tom_data *td, struct listen_ctx *lctx) 157237263Snp{ 158237263Snp int bucket = listen_hashfn(lctx->inp, td->listen_mask); 159237263Snp 160237263Snp mtx_lock(&td->lctx_hash_lock); 161237263Snp LIST_INSERT_HEAD(&td->listen_hash[bucket], lctx, link); 162237263Snp td->lctx_count++; 163237263Snp mtx_unlock(&td->lctx_hash_lock); 164237263Snp} 165237263Snp 166237263Snp/* 167237263Snp * Look for the listening socket's context entry in the hash and return it. 168237263Snp */ 169237263Snpstatic struct listen_ctx * 170237263Snplisten_hash_find(struct tom_data *td, struct inpcb *inp) 171237263Snp{ 172237263Snp int bucket = listen_hashfn(inp, td->listen_mask); 173237263Snp struct listen_ctx *lctx; 174237263Snp 175237263Snp mtx_lock(&td->lctx_hash_lock); 176237263Snp LIST_FOREACH(lctx, &td->listen_hash[bucket], link) { 177237263Snp if (lctx->inp == inp) 178237263Snp break; 179237263Snp } 180237263Snp mtx_unlock(&td->lctx_hash_lock); 181237263Snp 182237263Snp return (lctx); 183237263Snp} 184237263Snp 185237263Snp/* 186237263Snp * Removes the listen_ctx structure for inp from the hash and returns it. 187237263Snp */ 188237263Snpstatic struct listen_ctx * 189237263Snplisten_hash_del(struct tom_data *td, struct inpcb *inp) 190237263Snp{ 191237263Snp int bucket = listen_hashfn(inp, td->listen_mask); 192237263Snp struct listen_ctx *lctx, *l; 193237263Snp 194237263Snp mtx_lock(&td->lctx_hash_lock); 195237263Snp LIST_FOREACH_SAFE(lctx, &td->listen_hash[bucket], link, l) { 196237263Snp if (lctx->inp == inp) { 197237263Snp LIST_REMOVE(lctx, link); 198237263Snp td->lctx_count--; 199237263Snp break; 200237263Snp } 201237263Snp } 202237263Snp mtx_unlock(&td->lctx_hash_lock); 203237263Snp 204237263Snp return (lctx); 205237263Snp} 206237263Snp 207237263Snp/* 208237263Snp * Releases a hold on the lctx. Must be called with the listening socket's inp 209237263Snp * locked. The inp may be freed by this function and it returns NULL to 210237263Snp * indicate this. 211237263Snp */ 212237263Snpstatic struct inpcb * 213237263Snprelease_lctx(struct tom_data *td, struct listen_ctx *lctx) 214237263Snp{ 215237263Snp struct inpcb *inp = lctx->inp; 216237263Snp int inp_freed = 0; 217237263Snp 218237263Snp INP_WLOCK_ASSERT(inp); 219237263Snp if (refcount_release(&lctx->refcnt)) 220237263Snp inp_freed = free_lctx(td, lctx); 221237263Snp 222237263Snp return (inp_freed ? NULL : inp); 223237263Snp} 224237263Snp 225237263Snpstatic int 226237263Snpcreate_server(struct adapter *sc, struct listen_ctx *lctx) 227237263Snp{ 228237263Snp struct mbuf *m; 229237263Snp struct cpl_pass_open_req *req; 230237263Snp struct inpcb *inp = lctx->inp; 231237263Snp 232237263Snp m = M_GETHDR_OFLD(lctx->qset, CPL_PRIORITY_CONTROL, req); 233237263Snp if (m == NULL) 234237263Snp return (ENOMEM); 235237263Snp 236237263Snp req->wr.wrh_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 237237263Snp OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_PASS_OPEN_REQ, lctx->stid)); 238237263Snp req->local_port = inp->inp_lport; 239237263Snp memcpy(&req->local_ip, &inp->inp_laddr, 4); 240237263Snp req->peer_port = 0; 241237263Snp req->peer_ip = 0; 242237263Snp req->peer_netmask = 0; 243237263Snp req->opt0h = htonl(F_DELACK | F_TCAM_BYPASS); 244237263Snp req->opt0l = htonl(V_RCV_BUFSIZ(16)); 245237263Snp req->opt1 = htonl(V_CONN_POLICY(CPL_CONN_POLICY_ASK)); 246237263Snp 247237263Snp t3_offload_tx(sc, m); 248237263Snp 249237263Snp return (0); 250237263Snp} 251237263Snp 252237263Snpstatic int 253237263Snpdestroy_server(struct adapter *sc, struct listen_ctx *lctx) 254237263Snp{ 255237263Snp struct mbuf *m; 256237263Snp struct cpl_close_listserv_req *req; 257237263Snp 258237263Snp m = M_GETHDR_OFLD(lctx->qset, CPL_PRIORITY_CONTROL, req); 259237263Snp if (m == NULL) 260237263Snp return (ENOMEM); 261237263Snp 262237263Snp req->wr.wrh_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 263237263Snp OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_CLOSE_LISTSRV_REQ, 264237263Snp lctx->stid)); 265237263Snp req->cpu_idx = 0; 266237263Snp 267237263Snp t3_offload_tx(sc, m); 268237263Snp 269237263Snp return (0); 270237263Snp} 271237263Snp 272237263Snp/* 273174641Skmacy * Process a CPL_CLOSE_LISTSRV_RPL message. If the status is good we release 274174641Skmacy * the STID. 275174641Skmacy */ 276174641Skmacystatic int 277237263Snpdo_close_server_rpl(struct sge_qset *qs, struct rsp_desc *r, struct mbuf *m) 278174641Skmacy{ 279237263Snp struct adapter *sc = qs->adap; 280237263Snp struct tom_data *td = sc->tom_softc; 281237263Snp struct cpl_close_listserv_rpl *rpl = mtod(m, void *); 282174641Skmacy unsigned int stid = GET_TID(rpl); 283237263Snp struct listen_ctx *lctx = lookup_stid(&td->tid_maps, stid); 284237263Snp struct inpcb *inp = lctx->inp; 285174641Skmacy 286237263Snp CTR3(KTR_CXGB, "%s: stid %u, status %u", __func__, stid, rpl->status); 287174641Skmacy 288237263Snp if (rpl->status != CPL_ERR_NONE) { 289237263Snp log(LOG_ERR, "%s: failed (%u) to close listener for stid %u", 290237263Snp __func__, rpl->status, stid); 291237263Snp } else { 292237263Snp INP_WLOCK(inp); 293237263Snp KASSERT(listen_hash_del(td, lctx->inp) == NULL, 294237263Snp ("%s: inp %p still in listen hash", __func__, inp)); 295237263Snp if (release_lctx(td, lctx) != NULL) 296237263Snp INP_WUNLOCK(inp); 297174641Skmacy } 298174641Skmacy 299237263Snp m_freem(m); 300237263Snp return (0); 301174641Skmacy} 302174641Skmacy 303174641Skmacy/* 304237263Snp * Process a CPL_PASS_OPEN_RPL message. Remove the lctx from the listen hash 305237263Snp * table and free it if there was any error, otherwise nothing to do. 306174641Skmacy */ 307174641Skmacystatic int 308237263Snpdo_pass_open_rpl(struct sge_qset *qs, struct rsp_desc *r, struct mbuf *m) 309174641Skmacy{ 310237263Snp struct adapter *sc = qs->adap; 311237263Snp struct tom_data *td = sc->tom_softc; 312237263Snp struct cpl_pass_open_rpl *rpl = mtod(m, void *); 313237263Snp int stid = GET_TID(rpl); 314237263Snp struct listen_ctx *lctx; 315237263Snp struct inpcb *inp; 316174641Skmacy 317237263Snp /* 318237263Snp * We get these replies also when setting up HW filters. Just throw 319237263Snp * those away. 320237263Snp */ 321237263Snp if (stid >= td->tid_maps.stid_base + td->tid_maps.nstids) 322237263Snp goto done; 323237263Snp 324237263Snp lctx = lookup_stid(&td->tid_maps, stid); 325237263Snp inp = lctx->inp; 326237263Snp 327237263Snp INP_WLOCK(inp); 328237263Snp 329237263Snp CTR4(KTR_CXGB, "%s: stid %u, status %u, flags 0x%x", 330237263Snp __func__, stid, rpl->status, lctx->flags); 331237263Snp 332237263Snp lctx->flags &= ~LCTX_RPL_PENDING; 333237263Snp 334174641Skmacy if (rpl->status != CPL_ERR_NONE) { 335237263Snp log(LOG_ERR, "%s: %s: hw listen (stid %d) failed: %d\n", 336237263Snp __func__, device_get_nameunit(sc->dev), stid, rpl->status); 337237263Snp } 338174641Skmacy 339237263Snp#ifdef INVARIANTS 340237263Snp /* 341237263Snp * If the inp has been dropped (listening socket closed) then 342237263Snp * listen_stop must have run and taken the inp out of the hash. 343237263Snp */ 344237263Snp if (inp->inp_flags & INP_DROPPED) { 345237263Snp KASSERT(listen_hash_del(td, inp) == NULL, 346237263Snp ("%s: inp %p still in listen hash", __func__, inp)); 347237263Snp } 348174641Skmacy#endif 349237263Snp 350237263Snp if (inp->inp_flags & INP_DROPPED && rpl->status != CPL_ERR_NONE) { 351237263Snp if (release_lctx(td, lctx) != NULL) 352237263Snp INP_WUNLOCK(inp); 353237263Snp goto done; 354174641Skmacy } 355237263Snp 356237263Snp /* 357237263Snp * Listening socket stopped listening earlier and now the chip tells us 358237263Snp * it has started the hardware listener. Stop it; the lctx will be 359237263Snp * released in do_close_server_rpl. 360237263Snp */ 361237263Snp if (inp->inp_flags & INP_DROPPED) { 362237263Snp destroy_server(sc, lctx); 363237263Snp INP_WUNLOCK(inp); 364237263Snp goto done; 365237263Snp } 366237263Snp 367237263Snp /* 368237263Snp * Failed to start hardware listener. Take inp out of the hash and 369237263Snp * release our reference on it. An error message has been logged 370237263Snp * already. 371237263Snp */ 372237263Snp if (rpl->status != CPL_ERR_NONE) { 373237263Snp listen_hash_del(td, inp); 374237263Snp if (release_lctx(td, lctx) != NULL) 375237263Snp INP_WUNLOCK(inp); 376237263Snp goto done; 377237263Snp } 378237263Snp 379237263Snp /* hardware listener open for business */ 380237263Snp 381237263Snp INP_WUNLOCK(inp); 382237263Snpdone: 383237263Snp m_freem(m); 384237263Snp return (0); 385174641Skmacy} 386174641Skmacy 387237263Snpstatic void 388237263Snppass_accept_req_to_protohdrs(const struct cpl_pass_accept_req *cpl, 389237263Snp struct in_conninfo *inc, struct tcphdr *th, struct tcpopt *to) 390174641Skmacy{ 391237263Snp const struct tcp_options *t3opt = &cpl->tcp_options; 392237263Snp 393237263Snp bzero(inc, sizeof(*inc)); 394237263Snp inc->inc_faddr.s_addr = cpl->peer_ip; 395237263Snp inc->inc_laddr.s_addr = cpl->local_ip; 396237263Snp inc->inc_fport = cpl->peer_port; 397237263Snp inc->inc_lport = cpl->local_port; 398237263Snp 399237263Snp bzero(th, sizeof(*th)); 400237263Snp th->th_sport = cpl->peer_port; 401237263Snp th->th_dport = cpl->local_port; 402237263Snp th->th_seq = be32toh(cpl->rcv_isn); /* as in tcp_fields_to_host */ 403237263Snp th->th_flags = TH_SYN; 404237263Snp 405237263Snp bzero(to, sizeof(*to)); 406237263Snp if (t3opt->mss) { 407237263Snp to->to_flags |= TOF_MSS; 408237263Snp to->to_mss = be16toh(t3opt->mss); 409237263Snp } 410237263Snp if (t3opt->wsf) { 411237263Snp to->to_flags |= TOF_SCALE; 412237263Snp to->to_wscale = t3opt->wsf; 413237263Snp } 414237263Snp if (t3opt->tstamp) 415237263Snp to->to_flags |= TOF_TS; 416237263Snp if (t3opt->sack) 417237263Snp to->to_flags |= TOF_SACKPERM; 418174641Skmacy} 419174641Skmacy 420237263Snpstatic inline void 421237263Snphold_synqe(struct synq_entry *synqe) 422174641Skmacy{ 423237263Snp 424237263Snp refcount_acquire(&synqe->refcnt); 425174641Skmacy} 426174641Skmacy 427237263Snpstatic inline void 428237263Snprelease_synqe(struct synq_entry *synqe) 429237263Snp{ 430237263Snp 431237263Snp if (refcount_release(&synqe->refcnt)) 432237263Snp m_freem(synqe->m); 433237263Snp} 434237263Snp 435174641Skmacy/* 436237263Snp * Use the trailing space in the mbuf in which the PASS_ACCEPT_REQ arrived to 437237263Snp * store some state temporarily. There will be enough room in the mbuf's 438237263Snp * trailing space as the CPL is not that large. 439237263Snp * 440237263Snp * XXX: bad hack. 441174641Skmacy */ 442237263Snpstatic struct synq_entry * 443237263Snpmbuf_to_synq_entry(struct mbuf *m) 444174641Skmacy{ 445237263Snp int len = roundup(sizeof (struct synq_entry), 8); 446174641Skmacy 447237263Snp if (__predict_false(M_TRAILINGSPACE(m) < len)) { 448237263Snp panic("%s: no room for synq_entry (%td, %d)\n", __func__, 449237263Snp M_TRAILINGSPACE(m), len); 450237263Snp } 451174641Skmacy 452276563Srwatson return ((void *)(M_START(m) + M_SIZE(m) - len)); 453174641Skmacy} 454174641Skmacy 455237263Snp#ifdef KTR 456237263Snp#define REJECT_PASS_ACCEPT() do { \ 457237263Snp reject_reason = __LINE__; \ 458237263Snp goto reject; \ 459237263Snp} while (0) 460237263Snp#else 461237263Snp#define REJECT_PASS_ACCEPT() do { goto reject; } while (0) 462237263Snp#endif 463237263Snp 464174641Skmacy/* 465237263Snp * The context associated with a tid entry via insert_tid could be a synq_entry 466237263Snp * or a toepcb. The only way CPL handlers can tell is via a bit in these flags. 467174641Skmacy */ 468237263SnpCTASSERT(offsetof(struct toepcb, tp_flags) == offsetof(struct synq_entry, flags)); 469237263Snp 470237263Snp/* 471237263Snp * Handle a CPL_PASS_ACCEPT_REQ message. 472237263Snp */ 473174641Skmacystatic int 474237263Snpdo_pass_accept_req(struct sge_qset *qs, struct rsp_desc *r, struct mbuf *m) 475174641Skmacy{ 476237263Snp struct adapter *sc = qs->adap; 477237263Snp struct tom_data *td = sc->tom_softc; 478237263Snp struct toedev *tod = &td->tod; 479237263Snp const struct cpl_pass_accept_req *req = mtod(m, void *); 480237263Snp unsigned int stid = G_PASS_OPEN_TID(ntohl(req->tos_tid)); 481237263Snp unsigned int tid = GET_TID(req); 482237263Snp struct listen_ctx *lctx = lookup_stid(&td->tid_maps, stid); 483237263Snp struct l2t_entry *e = NULL; 484293309Smelifaro struct nhop4_basic nh4; 485237263Snp struct sockaddr_in nam; 486237263Snp struct inpcb *inp; 487237263Snp struct socket *so; 488237263Snp struct port_info *pi; 489237263Snp struct ifnet *ifp; 490237263Snp struct in_conninfo inc; 491237263Snp struct tcphdr th; 492237263Snp struct tcpopt to; 493237263Snp struct synq_entry *synqe = NULL; 494237263Snp int i; 495237263Snp#ifdef KTR 496237263Snp int reject_reason; 497237263Snp#endif 498174641Skmacy 499237263Snp CTR4(KTR_CXGB, "%s: stid %u, tid %u, lctx %p", __func__, stid, tid, 500237263Snp lctx); 501237263Snp 502237263Snp pass_accept_req_to_protohdrs(req, &inc, &th, &to); 503237263Snp 504237263Snp /* 505237263Snp * Don't offload if the interface that received the SYN doesn't have 506237263Snp * IFCAP_TOE enabled. 507237263Snp */ 508237263Snp pi = NULL; 509237263Snp for_each_port(sc, i) { 510237263Snp if (memcmp(sc->port[i].hw_addr, req->dst_mac, ETHER_ADDR_LEN)) 511237263Snp continue; 512237263Snp pi = &sc->port[i]; 513237263Snp break; 514237263Snp } 515237263Snp if (pi == NULL) 516237263Snp REJECT_PASS_ACCEPT(); 517237263Snp ifp = pi->ifp; 518237263Snp if ((ifp->if_capenable & IFCAP_TOE4) == 0) 519237263Snp REJECT_PASS_ACCEPT(); 520237263Snp 521237263Snp /* 522237263Snp * Don't offload if the outgoing interface for the route back to the 523237263Snp * peer is not the same as the interface that received the SYN. 524237263Snp */ 525237263Snp bzero(&nam, sizeof(nam)); 526237263Snp nam.sin_len = sizeof(nam); 527237263Snp nam.sin_family = AF_INET; 528237263Snp nam.sin_addr = inc.inc_faddr; 529293309Smelifaro if (fib4_lookup_nh_basic(RT_DEFAULT_FIB, nam.sin_addr, 0, 0, &nh4) != 0) 530237263Snp REJECT_PASS_ACCEPT(); 531237263Snp else { 532293309Smelifaro nam.sin_addr = nh4.nh_addr; 533293309Smelifaro if (nh4.nh_ifp == ifp) 534293309Smelifaro e = t3_l2t_get(pi, ifp, (struct sockaddr *)&nam); 535237263Snp if (e == NULL) 536237263Snp REJECT_PASS_ACCEPT(); /* no l2te, or ifp mismatch */ 537237263Snp } 538237263Snp 539286227Sjch INP_INFO_RLOCK(&V_tcbinfo); 540237263Snp 541237263Snp /* Don't offload if the 4-tuple is already in use */ 542237263Snp if (toe_4tuple_check(&inc, &th, ifp) != 0) { 543286227Sjch INP_INFO_RUNLOCK(&V_tcbinfo); 544237263Snp REJECT_PASS_ACCEPT(); 545237263Snp } 546237263Snp 547237263Snp inp = lctx->inp; /* listening socket (not owned by the TOE) */ 548237263Snp INP_WLOCK(inp); 549237263Snp if (__predict_false(inp->inp_flags & INP_DROPPED)) { 550237263Snp /* 551237263Snp * The listening socket has closed. The reply from the TOE to 552237263Snp * our CPL_CLOSE_LISTSRV_REQ will ultimately release all 553237263Snp * resources tied to this listen context. 554237263Snp */ 555237263Snp INP_WUNLOCK(inp); 556286227Sjch INP_INFO_RUNLOCK(&V_tcbinfo); 557237263Snp REJECT_PASS_ACCEPT(); 558237263Snp } 559237263Snp so = inp->inp_socket; 560237263Snp 561237263Snp /* Reuse the mbuf that delivered the CPL to us */ 562237263Snp synqe = mbuf_to_synq_entry(m); 563237263Snp synqe->flags = TP_IS_A_SYNQ_ENTRY; 564237263Snp synqe->m = m; 565237263Snp synqe->lctx = lctx; 566237263Snp synqe->tid = tid; 567237263Snp synqe->e = e; 568237263Snp synqe->opt0h = calc_opt0h(so, 0, 0, e); 569237263Snp synqe->qset = pi->first_qset + (arc4random() % pi->nqsets); 570237263Snp SOCKBUF_LOCK(&so->so_rcv); 571237263Snp synqe->rx_credits = min(select_rcv_wnd(so) >> 10, M_RCV_BUFSIZ); 572237263Snp SOCKBUF_UNLOCK(&so->so_rcv); 573237263Snp refcount_init(&synqe->refcnt, 1); 574237263Snp atomic_store_rel_int(&synqe->reply, RPL_OK); 575237263Snp 576237263Snp insert_tid(td, synqe, tid); 577237263Snp TAILQ_INSERT_TAIL(&lctx->synq, synqe, link); 578237263Snp hold_synqe(synqe); 579237263Snp hold_lctx(lctx); 580237263Snp 581237263Snp /* syncache_add releases both pcbinfo and pcb locks */ 582237263Snp toe_syncache_add(&inc, &to, &th, inp, tod, synqe); 583237263Snp INP_UNLOCK_ASSERT(inp); 584237263Snp INP_INFO_UNLOCK_ASSERT(&V_tcbinfo); 585237263Snp 586237263Snp /* 587237263Snp * If we replied during syncache_add (reply is RPL_DONE), good. 588237263Snp * Otherwise (reply is unchanged - RPL_OK) it's no longer ok to reply. 589237263Snp * The mbuf will stick around as long as the entry is in the syncache. 590237263Snp * The kernel is free to retry syncache_respond but we'll ignore it due 591237263Snp * to RPL_DONT. 592237263Snp */ 593237263Snp if (atomic_cmpset_int(&synqe->reply, RPL_OK, RPL_DONT)) { 594237263Snp 595237263Snp INP_WLOCK(inp); 596237263Snp if (__predict_false(inp->inp_flags & INP_DROPPED)) { 597237263Snp /* listener closed. synqe must have been aborted. */ 598237263Snp KASSERT(synqe->flags & TP_ABORT_SHUTDOWN, 599237263Snp ("%s: listener %p closed but synqe %p not aborted", 600237263Snp __func__, inp, synqe)); 601237263Snp 602237263Snp CTR5(KTR_CXGB, 603237263Snp "%s: stid %u, tid %u, lctx %p, synqe %p, ABORTED", 604237263Snp __func__, stid, tid, lctx, synqe); 605237263Snp INP_WUNLOCK(inp); 606237263Snp release_synqe(synqe); 607237263Snp return (__LINE__); 608174641Skmacy } 609237263Snp 610237263Snp KASSERT(!(synqe->flags & TP_ABORT_SHUTDOWN), 611237263Snp ("%s: synqe %p aborted, but listener %p not dropped.", 612237263Snp __func__, synqe, inp)); 613237263Snp 614237263Snp TAILQ_REMOVE(&lctx->synq, synqe, link); 615237263Snp release_synqe(synqe); /* removed from synq list */ 616237263Snp inp = release_lctx(td, lctx); 617237263Snp if (inp) 618237263Snp INP_WUNLOCK(inp); 619237263Snp 620237263Snp release_synqe(synqe); /* about to exit function */ 621237263Snp REJECT_PASS_ACCEPT(); 622237263Snp } 623237263Snp 624237263Snp KASSERT(synqe->reply == RPL_DONE, 625237263Snp ("%s: reply %d", __func__, synqe->reply)); 626237263Snp 627237263Snp CTR3(KTR_CXGB, "%s: stid %u, tid %u, OK", __func__, stid, tid); 628237263Snp release_synqe(synqe); 629237263Snp return (0); 630237263Snp 631237263Snpreject: 632237263Snp CTR4(KTR_CXGB, "%s: stid %u, tid %u, REJECT (%d)", __func__, stid, tid, 633237263Snp reject_reason); 634237263Snp 635237263Snp if (synqe == NULL) 636237263Snp m_freem(m); 637237263Snp if (e) 638237263Snp l2t_release(td->l2t, e); 639237263Snp queue_tid_release(tod, tid); 640237263Snp 641237263Snp return (0); 642174641Skmacy} 643174641Skmacy 644237263Snpstatic void 645237263Snppass_establish_to_protohdrs(const struct cpl_pass_establish *cpl, 646237263Snp struct in_conninfo *inc, struct tcphdr *th, struct tcpopt *to) 647237263Snp{ 648237263Snp uint16_t tcp_opt = be16toh(cpl->tcp_opt); 649237263Snp 650237263Snp bzero(inc, sizeof(*inc)); 651237263Snp inc->inc_faddr.s_addr = cpl->peer_ip; 652237263Snp inc->inc_laddr.s_addr = cpl->local_ip; 653237263Snp inc->inc_fport = cpl->peer_port; 654237263Snp inc->inc_lport = cpl->local_port; 655237263Snp 656237263Snp bzero(th, sizeof(*th)); 657237263Snp th->th_sport = cpl->peer_port; 658237263Snp th->th_dport = cpl->local_port; 659237263Snp th->th_flags = TH_ACK; 660237263Snp th->th_seq = be32toh(cpl->rcv_isn); /* as in tcp_fields_to_host */ 661237263Snp th->th_ack = be32toh(cpl->snd_isn); /* ditto */ 662237263Snp 663237263Snp bzero(to, sizeof(*to)); 664237263Snp if (G_TCPOPT_TSTAMP(tcp_opt)) 665237263Snp to->to_flags |= TOF_TS; 666237263Snp} 667237263Snp 668174641Skmacy/* 669237263Snp * Process a CPL_PASS_ESTABLISH message. The T3 has already established a 670237263Snp * connection and we need to do the software side setup. 671174641Skmacy */ 672174641Skmacystatic int 673237263Snpdo_pass_establish(struct sge_qset *qs, struct rsp_desc *r, struct mbuf *m) 674174641Skmacy{ 675237263Snp struct adapter *sc = qs->adap; 676237263Snp struct tom_data *td = sc->tom_softc; 677237263Snp struct cpl_pass_establish *cpl = mtod(m, void *); 678237263Snp struct toedev *tod = &td->tod; 679237263Snp unsigned int tid = GET_TID(cpl); 680237263Snp struct synq_entry *synqe = lookup_tid(&td->tid_maps, tid); 681237263Snp struct toepcb *toep; 682237263Snp struct socket *so; 683237263Snp struct listen_ctx *lctx = synqe->lctx; 684286227Sjch struct inpcb *inp = lctx->inp, *new_inp; 685237263Snp struct tcpopt to; 686237263Snp struct tcphdr th; 687237263Snp struct in_conninfo inc; 688237263Snp#ifdef KTR 689237263Snp int stid = G_PASS_OPEN_TID(ntohl(cpl->tos_tid)); 690237263Snp#endif 691174641Skmacy 692237263Snp CTR5(KTR_CXGB, "%s: stid %u, tid %u, lctx %p, inp_flags 0x%x", 693237263Snp __func__, stid, tid, lctx, inp->inp_flags); 694174641Skmacy 695237263Snp KASSERT(qs->idx == synqe->qset, 696237263Snp ("%s qset mismatch %d %d", __func__, qs->idx, synqe->qset)); 697237263Snp 698286227Sjch INP_INFO_RLOCK(&V_tcbinfo); /* for syncache_expand */ 699237263Snp INP_WLOCK(inp); 700237263Snp 701237263Snp if (__predict_false(inp->inp_flags & INP_DROPPED)) { 702237263Snp /* 703237263Snp * The listening socket has closed. The TOM must have aborted 704237263Snp * all the embryonic connections (including this one) that were 705237263Snp * on the lctx's synq. do_abort_rpl for the tid is responsible 706237263Snp * for cleaning up. 707237263Snp */ 708237263Snp KASSERT(synqe->flags & TP_ABORT_SHUTDOWN, 709237263Snp ("%s: listen socket dropped but tid %u not aborted.", 710237263Snp __func__, tid)); 711237263Snp INP_WUNLOCK(inp); 712286227Sjch INP_INFO_RUNLOCK(&V_tcbinfo); 713237263Snp m_freem(m); 714237263Snp return (0); 715237263Snp } 716237263Snp 717237263Snp pass_establish_to_protohdrs(cpl, &inc, &th, &to); 718237263Snp 719237263Snp /* Lie in order to pass the checks in syncache_expand */ 720237263Snp to.to_tsecr = synqe->ts; 721237263Snp th.th_ack = synqe->iss + 1; 722237263Snp 723237263Snp toep = toepcb_alloc(tod); 724237263Snp if (toep == NULL) { 725237263Snpreset: 726237263Snp t3_send_reset_synqe(tod, synqe); 727237263Snp INP_WUNLOCK(inp); 728286227Sjch INP_INFO_RUNLOCK(&V_tcbinfo); 729237263Snp m_freem(m); 730237263Snp return (0); 731237263Snp } 732237263Snp toep->tp_qset = qs->idx; 733237263Snp toep->tp_l2t = synqe->e; 734237263Snp toep->tp_tid = tid; 735237263Snp toep->tp_rx_credits = synqe->rx_credits; 736237263Snp 737237263Snp synqe->toep = toep; 738237263Snp synqe->cpl = cpl; 739237263Snp 740237263Snp so = inp->inp_socket; 741237263Snp if (!toe_syncache_expand(&inc, &to, &th, &so) || so == NULL) { 742237263Snp toepcb_free(toep); 743237263Snp goto reset; 744237263Snp } 745237263Snp 746286227Sjch /* New connection inpcb is already locked by syncache_expand(). */ 747286227Sjch new_inp = sotoinpcb(so); 748286227Sjch INP_WLOCK_ASSERT(new_inp); 749286227Sjch 750239544Snp if (__predict_false(!(synqe->flags & TP_SYNQE_EXPANDED))) { 751239544Snp tcp_timer_activate(intotcpcb(new_inp), TT_KEEP, 0); 752239544Snp t3_offload_socket(tod, synqe, so); 753239544Snp } 754239544Snp 755286227Sjch INP_WUNLOCK(new_inp); 756286227Sjch 757237263Snp /* Remove the synq entry and release its reference on the lctx */ 758237263Snp TAILQ_REMOVE(&lctx->synq, synqe, link); 759237263Snp inp = release_lctx(td, lctx); 760237263Snp if (inp) 761237263Snp INP_WUNLOCK(inp); 762286227Sjch INP_INFO_RUNLOCK(&V_tcbinfo); 763237263Snp release_synqe(synqe); 764237263Snp 765237263Snp m_freem(m); 766237263Snp return (0); 767174641Skmacy} 768174641Skmacy 769237263Snpvoid 770237263Snpt3_init_listen_cpl_handlers(struct adapter *sc) 771237263Snp{ 772237263Snp t3_register_cpl_handler(sc, CPL_PASS_OPEN_RPL, do_pass_open_rpl); 773237263Snp t3_register_cpl_handler(sc, CPL_CLOSE_LISTSRV_RPL, do_close_server_rpl); 774237263Snp t3_register_cpl_handler(sc, CPL_PASS_ACCEPT_REQ, do_pass_accept_req); 775237263Snp t3_register_cpl_handler(sc, CPL_PASS_ESTABLISH, do_pass_establish); 776237263Snp} 777237263Snp 778174641Skmacy/* 779174641Skmacy * Start a listening server by sending a passive open request to HW. 780237263Snp * 781237263Snp * Can't take adapter lock here and access to sc->flags, sc->open_device_map, 782237263Snp * sc->offload_map, if_capenable are all race prone. 783174641Skmacy */ 784237263Snpint 785237263Snpt3_listen_start(struct toedev *tod, struct tcpcb *tp) 786174641Skmacy{ 787237263Snp struct tom_data *td = t3_tomdata(tod); 788237263Snp struct adapter *sc = tod->tod_softc; 789237263Snp struct port_info *pi; 790237263Snp struct inpcb *inp = tp->t_inpcb; 791237263Snp struct listen_ctx *lctx; 792237263Snp int i; 793174641Skmacy 794237263Snp INP_WLOCK_ASSERT(inp); 795174641Skmacy 796237263Snp if ((inp->inp_vflag & INP_IPV4) == 0) 797237263Snp return (0); 798174641Skmacy 799237263Snp#ifdef notyet 800237263Snp ADAPTER_LOCK(sc); 801237263Snp if (IS_BUSY(sc)) { 802237263Snp log(LOG_ERR, "%s: listen request ignored, %s is busy", 803237263Snp __func__, device_get_nameunit(sc->dev)); 804237263Snp goto done; 805237263Snp } 806174641Skmacy 807237263Snp KASSERT(sc->flags & TOM_INIT_DONE, 808237263Snp ("%s: TOM not initialized", __func__)); 809237263Snp#endif 810174641Skmacy 811237263Snp if ((sc->open_device_map & sc->offload_map) == 0) 812237263Snp goto done; /* no port that's UP with IFCAP_TOE enabled */ 813174641Skmacy 814237263Snp /* 815237263Snp * Find a running port with IFCAP_TOE4. We'll use the first such port's 816237263Snp * queues to send the passive open and receive the reply to it. 817237263Snp * 818237263Snp * XXX: need a way to mark an port in use by offload. if_cxgbe should 819237263Snp * then reject any attempt to bring down such a port (and maybe reject 820237263Snp * attempts to disable IFCAP_TOE on that port too?). 821237263Snp */ 822237263Snp for_each_port(sc, i) { 823237263Snp if (isset(&sc->open_device_map, i) && 824237263Snp sc->port[i].ifp->if_capenable & IFCAP_TOE4) 825237263Snp break; 826237263Snp } 827237263Snp KASSERT(i < sc->params.nports, 828237263Snp ("%s: no running port with TOE capability enabled.", __func__)); 829237263Snp pi = &sc->port[i]; 830174641Skmacy 831237263Snp if (listen_hash_find(td, inp) != NULL) 832237263Snp goto done; /* already setup */ 833174641Skmacy 834237263Snp lctx = alloc_lctx(td, inp, pi->first_qset); 835237263Snp if (lctx == NULL) { 836237263Snp log(LOG_ERR, 837237263Snp "%s: listen request ignored, %s couldn't allocate lctx\n", 838237263Snp __func__, device_get_nameunit(sc->dev)); 839237263Snp goto done; 840237263Snp } 841237263Snp listen_hash_add(td, lctx); 842237263Snp 843237263Snp CTR5(KTR_CXGB, "%s: stid %u (%s), lctx %p, inp %p", __func__, 844237263Snp lctx->stid, tcpstates[tp->t_state], lctx, inp); 845237263Snp 846237263Snp if (create_server(sc, lctx) != 0) { 847237263Snp log(LOG_ERR, "%s: %s failed to create hw listener.\n", __func__, 848237263Snp device_get_nameunit(sc->dev)); 849237263Snp (void) listen_hash_del(td, inp); 850237263Snp inp = release_lctx(td, lctx); 851237263Snp /* can't be freed, host stack has a reference */ 852237263Snp KASSERT(inp != NULL, ("%s: inp freed", __func__)); 853237263Snp goto done; 854237263Snp } 855237263Snp lctx->flags |= LCTX_RPL_PENDING; 856237263Snpdone: 857237263Snp#ifdef notyet 858237263Snp ADAPTER_UNLOCK(sc); 859237263Snp#endif 860237263Snp return (0); 861174641Skmacy} 862174641Skmacy 863174641Skmacy/* 864174641Skmacy * Stop a listening server by sending a close_listsvr request to HW. 865174641Skmacy * The server TID is freed when we get the reply. 866174641Skmacy */ 867237263Snpint 868237263Snpt3_listen_stop(struct toedev *tod, struct tcpcb *tp) 869174641Skmacy{ 870174641Skmacy struct listen_ctx *lctx; 871237263Snp struct adapter *sc = tod->tod_softc; 872237263Snp struct tom_data *td = t3_tomdata(tod); 873237263Snp struct inpcb *inp = tp->t_inpcb; 874237263Snp struct synq_entry *synqe; 875174641Skmacy 876237263Snp INP_WLOCK_ASSERT(inp); 877237263Snp 878237263Snp lctx = listen_hash_del(td, inp); 879237263Snp if (lctx == NULL) 880237263Snp return (ENOENT); /* no hardware listener for this inp */ 881237263Snp 882237263Snp CTR4(KTR_CXGB, "%s: stid %u, lctx %p, flags %x", __func__, lctx->stid, 883237263Snp lctx, lctx->flags); 884237263Snp 885174641Skmacy /* 886237263Snp * If the reply to the PASS_OPEN is still pending we'll wait for it to 887237263Snp * arrive and clean up when it does. 888174641Skmacy */ 889237263Snp if (lctx->flags & LCTX_RPL_PENDING) { 890237263Snp KASSERT(TAILQ_EMPTY(&lctx->synq), 891237263Snp ("%s: synq not empty.", __func__)); 892237263Snp return (EINPROGRESS); 893237263Snp } 894174641Skmacy 895237263Snp /* 896237263Snp * The host stack will abort all the connections on the listening 897237263Snp * socket's so_comp. It doesn't know about the connections on the synq 898237263Snp * so we need to take care of those. 899237263Snp */ 900237263Snp TAILQ_FOREACH(synqe, &lctx->synq, link) { 901237263Snp KASSERT(synqe->lctx == lctx, ("%s: synq corrupt", __func__)); 902237263Snp t3_send_reset_synqe(tod, synqe); 903174641Skmacy } 904174641Skmacy 905237263Snp destroy_server(sc, lctx); 906237263Snp return (0); 907237263Snp} 908174641Skmacy 909237263Snpvoid 910237263Snpt3_syncache_added(struct toedev *tod __unused, void *arg) 911237263Snp{ 912237263Snp struct synq_entry *synqe = arg; 913237263Snp 914237263Snp hold_synqe(synqe); 915174641Skmacy} 916237263Snp 917237263Snpvoid 918237263Snpt3_syncache_removed(struct toedev *tod __unused, void *arg) 919237263Snp{ 920237263Snp struct synq_entry *synqe = arg; 921237263Snp 922237263Snp release_synqe(synqe); 923237263Snp} 924237263Snp 925237263Snpint 926237263Snpt3_syncache_respond(struct toedev *tod, void *arg, struct mbuf *m) 927237263Snp{ 928237263Snp struct adapter *sc = tod->tod_softc; 929237263Snp struct synq_entry *synqe = arg; 930237263Snp struct l2t_entry *e = synqe->e; 931237263Snp struct ip *ip = mtod(m, struct ip *); 932237263Snp struct tcphdr *th = (void *)(ip + 1); 933237263Snp struct cpl_pass_accept_rpl *rpl; 934237263Snp struct mbuf *r; 935237263Snp struct listen_ctx *lctx = synqe->lctx; 936237263Snp struct tcpopt to; 937237263Snp int mtu_idx, cpu_idx; 938237263Snp 939237263Snp /* 940237263Snp * The first time we run it's during the call to syncache_add. That's 941237263Snp * the only one we care about. 942237263Snp */ 943237263Snp if (atomic_cmpset_int(&synqe->reply, RPL_OK, RPL_DONE) == 0) 944237263Snp goto done; /* reply to the CPL only if it's ok to do so */ 945237263Snp 946237263Snp r = M_GETHDR_OFLD(lctx->qset, CPL_PRIORITY_CONTROL, rpl); 947237263Snp if (r == NULL) 948237263Snp goto done; 949237263Snp 950237263Snp /* 951237263Snp * Use only the provided mbuf (with ip and tcp headers) and what's in 952237263Snp * synqe. Avoid looking at the listening socket (lctx->inp) here. 953237263Snp * 954237263Snp * XXX: if the incoming SYN had the TCP timestamp option but the kernel 955237263Snp * decides it doesn't want to use TCP timestamps we have no way of 956237263Snp * relaying this info to the chip on a per-tid basis (all we have is a 957237263Snp * global knob). 958237263Snp */ 959237263Snp bzero(&to, sizeof(to)); 960237263Snp tcp_dooptions(&to, (void *)(th + 1), (th->th_off << 2) - sizeof(*th), 961237263Snp TO_SYN); 962237263Snp 963237263Snp /* stash them for later */ 964237263Snp synqe->iss = be32toh(th->th_seq); 965237263Snp synqe->ts = to.to_tsval; 966237263Snp 967237263Snp mtu_idx = find_best_mtu_idx(sc, NULL, to.to_mss); 968237263Snp cpu_idx = sc->rrss_map[synqe->qset]; 969237263Snp 970237263Snp rpl->wr.wrh_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 971237263Snp rpl->wr.wrh_lo = 0; 972237263Snp OPCODE_TID(rpl) = htonl(MK_OPCODE_TID(CPL_PASS_ACCEPT_RPL, synqe->tid)); 973237263Snp rpl->opt2 = calc_opt2(cpu_idx); 974237263Snp rpl->rsvd = rpl->opt2; /* workaround for HW bug */ 975237263Snp rpl->peer_ip = ip->ip_dst.s_addr; 976237263Snp rpl->opt0h = synqe->opt0h | 977237263Snp calc_opt0h(NULL, mtu_idx, to.to_wscale, NULL); 978237263Snp rpl->opt0l_status = htobe32(CPL_PASS_OPEN_ACCEPT) | 979237263Snp calc_opt0l(NULL, synqe->rx_credits); 980237263Snp 981237263Snp l2t_send(sc, r, e); 982237263Snpdone: 983237263Snp m_freem(m); 984237263Snp return (0); 985237263Snp} 986237263Snp 987237263Snpint 988237263Snpdo_abort_req_synqe(struct sge_qset *qs, struct rsp_desc *r, struct mbuf *m) 989237263Snp{ 990237263Snp struct adapter *sc = qs->adap; 991237263Snp struct tom_data *td = sc->tom_softc; 992237263Snp struct toedev *tod = &td->tod; 993237263Snp const struct cpl_abort_req_rss *req = mtod(m, void *); 994237263Snp unsigned int tid = GET_TID(req); 995237263Snp struct synq_entry *synqe = lookup_tid(&td->tid_maps, tid); 996237263Snp struct listen_ctx *lctx = synqe->lctx; 997237263Snp struct inpcb *inp = lctx->inp; 998237263Snp 999237263Snp KASSERT(synqe->flags & TP_IS_A_SYNQ_ENTRY, 1000237263Snp ("%s: !SYNQ_ENTRY", __func__)); 1001237263Snp 1002237263Snp CTR6(KTR_CXGB, "%s: tid %u, synqe %p (%x), lctx %p, status %d", 1003237263Snp __func__, tid, synqe, synqe->flags, synqe->lctx, req->status); 1004237263Snp 1005237263Snp INP_WLOCK(inp); 1006237263Snp 1007237263Snp if (!(synqe->flags & TP_ABORT_REQ_RCVD)) { 1008237263Snp synqe->flags |= TP_ABORT_REQ_RCVD; 1009237263Snp synqe->flags |= TP_ABORT_SHUTDOWN; 1010237263Snp INP_WUNLOCK(inp); 1011237263Snp m_freem(m); 1012237263Snp return (0); 1013237263Snp } 1014237263Snp synqe->flags &= ~TP_ABORT_REQ_RCVD; 1015237263Snp 1016237263Snp /* 1017237263Snp * If we'd sent a reset on this synqe, we'll ignore this and clean up in 1018237263Snp * the T3's reply to our reset instead. 1019237263Snp */ 1020237263Snp if (synqe->flags & TP_ABORT_RPL_PENDING) { 1021237263Snp synqe->flags |= TP_ABORT_RPL_SENT; 1022237263Snp INP_WUNLOCK(inp); 1023237263Snp } else { 1024237263Snp TAILQ_REMOVE(&lctx->synq, synqe, link); 1025237263Snp inp = release_lctx(td, lctx); 1026237263Snp if (inp) 1027237263Snp INP_WUNLOCK(inp); 1028237263Snp release_tid(tod, tid, qs->idx); 1029237263Snp l2t_release(td->l2t, synqe->e); 1030237263Snp release_synqe(synqe); 1031237263Snp } 1032237263Snp 1033237263Snp send_abort_rpl(tod, tid, qs->idx); 1034237263Snp m_freem(m); 1035237263Snp return (0); 1036237263Snp} 1037237263Snp 1038237263Snpint 1039237263Snpdo_abort_rpl_synqe(struct sge_qset *qs, struct rsp_desc *r, struct mbuf *m) 1040237263Snp{ 1041237263Snp struct adapter *sc = qs->adap; 1042237263Snp struct tom_data *td = sc->tom_softc; 1043237263Snp struct toedev *tod = &td->tod; 1044237263Snp const struct cpl_abort_rpl_rss *rpl = mtod(m, void *); 1045237263Snp unsigned int tid = GET_TID(rpl); 1046237263Snp struct synq_entry *synqe = lookup_tid(&td->tid_maps, tid); 1047237263Snp struct listen_ctx *lctx = synqe->lctx; 1048237263Snp struct inpcb *inp = lctx->inp; 1049237263Snp 1050237263Snp CTR3(KTR_CXGB, "%s: tid %d, synqe %p, status %d", tid, synqe, 1051237263Snp rpl->status); 1052237263Snp 1053237263Snp INP_WLOCK(inp); 1054237263Snp 1055237263Snp if (synqe->flags & TP_ABORT_RPL_PENDING) { 1056237263Snp if (!(synqe->flags & TP_ABORT_RPL_RCVD)) { 1057237263Snp synqe->flags |= TP_ABORT_RPL_RCVD; 1058237263Snp INP_WUNLOCK(inp); 1059237263Snp } else { 1060237263Snp synqe->flags &= ~TP_ABORT_RPL_RCVD; 1061237263Snp synqe->flags &= TP_ABORT_RPL_PENDING; 1062237263Snp 1063237263Snp TAILQ_REMOVE(&lctx->synq, synqe, link); 1064237263Snp inp = release_lctx(td, lctx); 1065237263Snp if (inp) 1066237263Snp INP_WUNLOCK(inp); 1067237263Snp release_tid(tod, tid, qs->idx); 1068237263Snp l2t_release(td->l2t, synqe->e); 1069237263Snp release_synqe(synqe); 1070237263Snp } 1071237263Snp } 1072237263Snp 1073237263Snp m_freem(m); 1074237263Snp return (0); 1075237263Snp} 1076237263Snp 1077237263Snpstatic void 1078237263Snpt3_send_reset_synqe(struct toedev *tod, struct synq_entry *synqe) 1079237263Snp{ 1080237263Snp struct cpl_abort_req *req; 1081237263Snp unsigned int tid = synqe->tid; 1082237263Snp struct adapter *sc = tod->tod_softc; 1083237263Snp struct mbuf *m; 1084237263Snp#ifdef INVARIANTS 1085237263Snp struct listen_ctx *lctx = synqe->lctx; 1086237263Snp struct inpcb *inp = lctx->inp; 1087237263Snp#endif 1088237263Snp 1089237263Snp INP_WLOCK_ASSERT(inp); 1090237263Snp 1091237263Snp CTR4(KTR_CXGB, "%s: tid %d, synqe %p (%x)", __func__, tid, synqe, 1092237263Snp synqe->flags); 1093237263Snp 1094237263Snp if (synqe->flags & TP_ABORT_SHUTDOWN) 1095237263Snp return; 1096237263Snp 1097237263Snp synqe->flags |= (TP_ABORT_RPL_PENDING | TP_ABORT_SHUTDOWN); 1098237263Snp 1099237263Snp m = M_GETHDR_OFLD(synqe->qset, CPL_PRIORITY_DATA, req); 1100237263Snp if (m == NULL) 1101237263Snp CXGB_UNIMPLEMENTED(); 1102237263Snp 1103237263Snp req->wr.wrh_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_HOST_ABORT_CON_REQ)); 1104237263Snp req->wr.wrh_lo = htonl(V_WR_TID(tid)); 1105237263Snp OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_ABORT_REQ, tid)); 1106237263Snp req->rsvd0 = 0; 1107237263Snp req->rsvd1 = !(synqe->flags & TP_DATASENT); 1108237263Snp req->cmd = CPL_ABORT_SEND_RST; 1109237263Snp 1110237263Snp l2t_send(sc, m, synqe->e); 1111237263Snp} 1112237263Snp 1113237263Snpvoid 1114237263Snpt3_offload_socket(struct toedev *tod, void *arg, struct socket *so) 1115237263Snp{ 1116237263Snp struct adapter *sc = tod->tod_softc; 1117237263Snp struct tom_data *td = sc->tom_softc; 1118237263Snp struct synq_entry *synqe = arg; 1119237263Snp#ifdef INVARIANTS 1120237263Snp struct inpcb *inp = sotoinpcb(so); 1121237263Snp#endif 1122237263Snp struct cpl_pass_establish *cpl = synqe->cpl; 1123237263Snp struct toepcb *toep = synqe->toep; 1124237263Snp 1125286227Sjch INP_INFO_RLOCK_ASSERT(&V_tcbinfo); /* prevents bad race with accept() */ 1126237263Snp INP_WLOCK_ASSERT(inp); 1127237263Snp 1128237263Snp offload_socket(so, toep); 1129237263Snp make_established(so, cpl->snd_isn, cpl->rcv_isn, cpl->tcp_opt); 1130237263Snp update_tid(td, toep, synqe->tid); 1131239544Snp synqe->flags |= TP_SYNQE_EXPANDED; 1132237263Snp} 1133237263Snp#endif 1134