cxgb_listen.c revision 174641
1174641Skmacy/************************************************************************** 2174641Skmacy 3174641SkmacyCopyright (c) 2007, Chelsio Inc. 4174641SkmacyAll rights reserved. 5174641Skmacy 6174641SkmacyRedistribution and use in source and binary forms, with or without 7174641Skmacymodification, are permitted provided that the following conditions are met: 8174641Skmacy 9174641Skmacy 1. Redistributions of source code must retain the above copyright notice, 10174641Skmacy this list of conditions and the following disclaimer. 11174641Skmacy 12174641Skmacy 2. Neither the name of the Chelsio Corporation nor the names of its 13174641Skmacy contributors may be used to endorse or promote products derived from 14174641Skmacy this software without specific prior written permission. 15174641Skmacy 16174641SkmacyTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17174641SkmacyAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18174641SkmacyIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19174641SkmacyARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20174641SkmacyLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21174641SkmacyCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22174641SkmacySUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23174641SkmacyINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24174641SkmacyCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25174641SkmacyARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26174641SkmacyPOSSIBILITY OF SUCH DAMAGE. 27174641Skmacy 28174641Skmacy***************************************************************************/ 29174641Skmacy 30174641Skmacy#include <sys/cdefs.h> 31174641Skmacy__FBSDID("$FreeBSD: head/sys/dev/cxgb/ulp/tom/cxgb_listen.c 174641 2007-12-16 05:27:26Z kmacy $"); 32174641Skmacy 33174641Skmacy#include <sys/param.h> 34174641Skmacy#include <sys/systm.h> 35174641Skmacy#include <sys/fcntl.h> 36174641Skmacy#include <sys/limits.h> 37174641Skmacy#include <sys/lock.h> 38174641Skmacy#include <sys/mbuf.h> 39174641Skmacy#include <sys/mutex.h> 40174641Skmacy#include <sys/socket.h> 41174641Skmacy#include <sys/socketvar.h> 42174641Skmacy#include <sys/syslog.h> 43174641Skmacy 44174641Skmacy#include <net/if.h> 45174641Skmacy#include <net/route.h> 46174641Skmacy 47174641Skmacy#include <netinet/in.h> 48174641Skmacy#include <netinet/in_pcb.h> 49174641Skmacy#include <netinet/in_systm.h> 50174641Skmacy#include <netinet/in_var.h> 51174641Skmacy 52174641Skmacy 53174641Skmacy#include <dev/cxgb/cxgb_osdep.h> 54174641Skmacy#include <dev/cxgb/sys/mbufq.h> 55174641Skmacy 56174641Skmacy#include <netinet/tcp.h> 57174641Skmacy#include <netinet/tcp_var.h> 58174641Skmacy#include <netinet/tcp_fsm.h> 59174641Skmacy 60174641Skmacy#include <netinet/tcp_ofld.h> 61174641Skmacy#include <net/route.h> 62174641Skmacy 63174641Skmacy#include <dev/cxgb/t3cdev.h> 64174641Skmacy#include <dev/cxgb/common/cxgb_firmware_exports.h> 65174641Skmacy#include <dev/cxgb/common/cxgb_t3_cpl.h> 66174641Skmacy#include <dev/cxgb/common/cxgb_tcb.h> 67174641Skmacy#include <dev/cxgb/common/cxgb_ctl_defs.h> 68174641Skmacy#include <dev/cxgb/cxgb_l2t.h> 69174641Skmacy#include <dev/cxgb/cxgb_offload.h> 70174641Skmacy#include <dev/cxgb/ulp/toecore/cxgb_toedev.h> 71174641Skmacy#include <dev/cxgb/ulp/tom/cxgb_defs.h> 72174641Skmacy#include <dev/cxgb/ulp/tom/cxgb_tom.h> 73174641Skmacy#include <dev/cxgb/ulp/tom/cxgb_t3_ddp.h> 74174641Skmacy#include <dev/cxgb/ulp/tom/cxgb_toepcb.h> 75174641Skmacy 76174641Skmacy 77174641Skmacystatic struct listen_info *listen_hash_add(struct tom_data *d, struct socket *so, unsigned int stid); 78174641Skmacystatic int listen_hash_del(struct tom_data *d, struct socket *so); 79174641Skmacy 80174641Skmacy/* 81174641Skmacy * Process a CPL_CLOSE_LISTSRV_RPL message. If the status is good we release 82174641Skmacy * the STID. 83174641Skmacy */ 84174641Skmacystatic int 85174641Skmacydo_close_server_rpl(struct t3cdev *cdev, struct mbuf *m, void *ctx) 86174641Skmacy{ 87174641Skmacy struct cpl_close_listserv_rpl *rpl = cplhdr(m); 88174641Skmacy unsigned int stid = GET_TID(rpl); 89174641Skmacy 90174641Skmacy if (rpl->status != CPL_ERR_NONE) 91174641Skmacy log(LOG_ERR, "Unexpected CLOSE_LISTSRV_RPL status %u for " 92174641Skmacy "STID %u\n", rpl->status, stid); 93174641Skmacy else { 94174641Skmacy struct listen_ctx *listen_ctx = (struct listen_ctx *)ctx; 95174641Skmacy 96174641Skmacy cxgb_free_stid(cdev, stid); 97174641Skmacy free(listen_ctx, M_CXGB); 98174641Skmacy } 99174641Skmacy 100174641Skmacy return (CPL_RET_BUF_DONE); 101174641Skmacy} 102174641Skmacy 103174641Skmacy/* 104174641Skmacy * Process a CPL_PASS_OPEN_RPL message. Remove the socket from the listen hash 105174641Skmacy * table and free the STID if there was any error, otherwise nothing to do. 106174641Skmacy */ 107174641Skmacystatic int 108174641Skmacydo_pass_open_rpl(struct t3cdev *cdev, struct mbuf *m, void *ctx) 109174641Skmacy{ 110174641Skmacy struct cpl_pass_open_rpl *rpl = cplhdr(m); 111174641Skmacy 112174641Skmacy if (rpl->status != CPL_ERR_NONE) { 113174641Skmacy int stid = GET_TID(rpl); 114174641Skmacy struct listen_ctx *listen_ctx = (struct listen_ctx *)ctx; 115174641Skmacy struct tom_data *d = listen_ctx->tom_data; 116174641Skmacy struct socket *lso = listen_ctx->lso; 117174641Skmacy 118174641Skmacy#if VALIDATE_TID 119174641Skmacy if (!lso) 120174641Skmacy return (CPL_RET_UNKNOWN_TID | CPL_RET_BUF_DONE); 121174641Skmacy#endif 122174641Skmacy /* 123174641Skmacy * Note: It is safe to unconditionally call listen_hash_del() 124174641Skmacy * at this point without risking unhashing a reincarnation of 125174641Skmacy * an already closed socket (i.e., there is no listen, close, 126174641Skmacy * listen, free the sock for the second listen while processing 127174641Skmacy * a message for the first race) because we are still holding 128174641Skmacy * a reference on the socket. It is possible that the unhash 129174641Skmacy * will fail because the socket is already closed, but we can't 130174641Skmacy * unhash the wrong socket because it is impossible for the 131174641Skmacy * socket to which this message refers to have reincarnated. 132174641Skmacy */ 133174641Skmacy listen_hash_del(d, lso); 134174641Skmacy cxgb_free_stid(cdev, stid); 135174641Skmacy#ifdef notyet 136174641Skmacy /* 137174641Skmacy * XXX need to unreference the inpcb 138174641Skmacy * but we have no way of knowing that other TOMs aren't referencing it 139174641Skmacy */ 140174641Skmacy sock_put(lso); 141174641Skmacy#endif 142174641Skmacy free(listen_ctx, M_CXGB); 143174641Skmacy } 144174641Skmacy return CPL_RET_BUF_DONE; 145174641Skmacy} 146174641Skmacy 147174641Skmacyvoid 148174641Skmacyt3_init_listen_cpl_handlers(void) 149174641Skmacy{ 150174641Skmacy t3tom_register_cpl_handler(CPL_PASS_OPEN_RPL, do_pass_open_rpl); 151174641Skmacy t3tom_register_cpl_handler(CPL_CLOSE_LISTSRV_RPL, do_close_server_rpl); 152174641Skmacy} 153174641Skmacy 154174641Skmacystatic inline int 155174641Skmacylisten_hashfn(const struct socket *so) 156174641Skmacy{ 157174641Skmacy return ((unsigned long)so >> 10) & (LISTEN_INFO_HASH_SIZE - 1); 158174641Skmacy} 159174641Skmacy 160174641Skmacy/* 161174641Skmacy * Create and add a listen_info entry to the listen hash table. This and the 162174641Skmacy * listen hash table functions below cannot be called from softirqs. 163174641Skmacy */ 164174641Skmacystatic struct listen_info * 165174641Skmacylisten_hash_add(struct tom_data *d, struct socket *so, unsigned int stid) 166174641Skmacy{ 167174641Skmacy struct listen_info *p; 168174641Skmacy 169174641Skmacy p = malloc(sizeof(*p), M_CXGB, M_NOWAIT|M_ZERO); 170174641Skmacy if (p) { 171174641Skmacy int bucket = listen_hashfn(so); 172174641Skmacy 173174641Skmacy p->so = so; /* just a key, no need to take a reference */ 174174641Skmacy p->stid = stid; 175174641Skmacy mtx_lock(&d->listen_lock); 176174641Skmacy p->next = d->listen_hash_tab[bucket]; 177174641Skmacy d->listen_hash_tab[bucket] = p; 178174641Skmacy mtx_unlock(&d->listen_lock); 179174641Skmacy } 180174641Skmacy return p; 181174641Skmacy} 182174641Skmacy 183174641Skmacy#if 0 184174641Skmacy/* 185174641Skmacy * Given a pointer to a listening socket return its server TID by consulting 186174641Skmacy * the socket->stid map. Returns -1 if the socket is not in the map. 187174641Skmacy */ 188174641Skmacystatic int 189174641Skmacylisten_hash_find(struct tom_data *d, struct socket *so) 190174641Skmacy{ 191174641Skmacy int stid = -1, bucket = listen_hashfn(so); 192174641Skmacy struct listen_info *p; 193174641Skmacy 194174641Skmacy spin_lock(&d->listen_lock); 195174641Skmacy for (p = d->listen_hash_tab[bucket]; p; p = p->next) 196174641Skmacy if (p->sk == sk) { 197174641Skmacy stid = p->stid; 198174641Skmacy break; 199174641Skmacy } 200174641Skmacy spin_unlock(&d->listen_lock); 201174641Skmacy return stid; 202174641Skmacy} 203174641Skmacy#endif 204174641Skmacy 205174641Skmacy/* 206174641Skmacy * Delete the listen_info structure for a listening socket. Returns the server 207174641Skmacy * TID for the socket if it is present in the socket->stid map, or -1. 208174641Skmacy */ 209174641Skmacystatic int 210174641Skmacylisten_hash_del(struct tom_data *d, struct socket *so) 211174641Skmacy{ 212174641Skmacy int bucket, stid = -1; 213174641Skmacy struct listen_info *p, **prev; 214174641Skmacy 215174641Skmacy bucket = listen_hashfn(so); 216174641Skmacy prev = &d->listen_hash_tab[bucket]; 217174641Skmacy 218174641Skmacy mtx_lock(&d->listen_lock); 219174641Skmacy for (p = *prev; p; prev = &p->next, p = p->next) 220174641Skmacy if (p->so == so) { 221174641Skmacy stid = p->stid; 222174641Skmacy *prev = p->next; 223174641Skmacy free(p, M_CXGB); 224174641Skmacy break; 225174641Skmacy } 226174641Skmacy mtx_unlock(&d->listen_lock); 227174641Skmacy 228174641Skmacy return (stid); 229174641Skmacy} 230174641Skmacy 231174641Skmacy/* 232174641Skmacy * Start a listening server by sending a passive open request to HW. 233174641Skmacy */ 234174641Skmacyvoid 235174641Skmacyt3_listen_start(struct toedev *dev, struct socket *so, struct t3cdev *cdev) 236174641Skmacy{ 237174641Skmacy int stid; 238174641Skmacy struct mbuf *m; 239174641Skmacy struct cpl_pass_open_req *req; 240174641Skmacy struct tom_data *d = TOM_DATA(dev); 241174641Skmacy struct inpcb *inp = sotoinpcb(so); 242174641Skmacy struct listen_ctx *ctx; 243174641Skmacy 244174641Skmacy if (!TOM_TUNABLE(dev, activated)) 245174641Skmacy return; 246174641Skmacy 247174641Skmacy printf("start listen\n"); 248174641Skmacy 249174641Skmacy ctx = malloc(sizeof(*ctx), M_CXGB, M_NOWAIT); 250174641Skmacy 251174641Skmacy if (!ctx) 252174641Skmacy return; 253174641Skmacy 254174641Skmacy ctx->tom_data = d; 255174641Skmacy ctx->lso = so; 256174641Skmacy ctx->ulp_mode = 0; /* DDP if the default */ 257174641Skmacy LIST_INIT(&ctx->synq_head); 258174641Skmacy 259174641Skmacy stid = cxgb_alloc_stid(d->cdev, d->client, ctx); 260174641Skmacy if (stid < 0) 261174641Skmacy goto free_ctx; 262174641Skmacy 263174641Skmacy#ifdef notyet 264174641Skmacy /* 265174641Skmacy * XXX need to mark inpcb as referenced 266174641Skmacy */ 267174641Skmacy sock_hold(sk); 268174641Skmacy#endif 269174641Skmacy m = m_gethdr(M_NOWAIT, MT_DATA); 270174641Skmacy if (m == NULL) 271174641Skmacy goto free_stid; 272174641Skmacy m->m_pkthdr.len = m->m_len = sizeof(*req); 273174641Skmacy 274174641Skmacy if (!listen_hash_add(d, so, stid)) 275174641Skmacy goto free_all; 276174641Skmacy 277174641Skmacy req = mtod(m, struct cpl_pass_open_req *); 278174641Skmacy req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 279174641Skmacy OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_PASS_OPEN_REQ, stid)); 280174641Skmacy req->local_port = inp->inp_lport; 281174641Skmacy memcpy(&req->local_ip, &inp->inp_laddr, 4); 282174641Skmacy req->peer_port = 0; 283174641Skmacy req->peer_ip = 0; 284174641Skmacy req->peer_netmask = 0; 285174641Skmacy req->opt0h = htonl(F_DELACK | F_TCAM_BYPASS); 286174641Skmacy req->opt0l = htonl(V_RCV_BUFSIZ(16)); 287174641Skmacy req->opt1 = htonl(V_CONN_POLICY(CPL_CONN_POLICY_ASK)); 288174641Skmacy 289174641Skmacy m_set_priority(m, CPL_PRIORITY_LISTEN); 290174641Skmacy cxgb_ofld_send(cdev, m); 291174641Skmacy return; 292174641Skmacy 293174641Skmacyfree_all: 294174641Skmacy m_free(m); 295174641Skmacyfree_stid: 296174641Skmacy cxgb_free_stid(cdev, stid); 297174641Skmacy#if 0 298174641Skmacy sock_put(sk); 299174641Skmacy#endif 300174641Skmacyfree_ctx: 301174641Skmacy free(ctx, M_CXGB); 302174641Skmacy} 303174641Skmacy 304174641Skmacy/* 305174641Skmacy * Stop a listening server by sending a close_listsvr request to HW. 306174641Skmacy * The server TID is freed when we get the reply. 307174641Skmacy */ 308174641Skmacyvoid 309174641Skmacyt3_listen_stop(struct toedev *dev, struct socket *so, struct t3cdev *cdev) 310174641Skmacy{ 311174641Skmacy struct mbuf *m; 312174641Skmacy struct cpl_close_listserv_req *req; 313174641Skmacy struct listen_ctx *lctx; 314174641Skmacy int stid = listen_hash_del(TOM_DATA(dev), so); 315174641Skmacy 316174641Skmacy if (stid < 0) 317174641Skmacy return; 318174641Skmacy 319174641Skmacy lctx = cxgb_get_lctx(cdev, stid); 320174641Skmacy /* 321174641Skmacy * Do this early so embryonic connections are marked as being aborted 322174641Skmacy * while the stid is still open. This ensures pass_establish messages 323174641Skmacy * that arrive while we are closing the server will be able to locate 324174641Skmacy * the listening socket. 325174641Skmacy */ 326174641Skmacy t3_reset_synq(lctx); 327174641Skmacy 328174641Skmacy /* Send the close ASAP to stop further passive opens */ 329174641Skmacy m = m_gethdr(M_NOWAIT, MT_DATA); 330174641Skmacy if (m == NULL) { 331174641Skmacy /* 332174641Skmacy * XXX allocate from lowmem cache 333174641Skmacy */ 334174641Skmacy } 335174641Skmacy m->m_pkthdr.len = m->m_len = sizeof(*req); 336174641Skmacy 337174641Skmacy req = mtod(m, struct cpl_close_listserv_req *); 338174641Skmacy req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 339174641Skmacy OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_CLOSE_LISTSRV_REQ, stid)); 340174641Skmacy req->cpu_idx = 0; 341174641Skmacy m_set_priority(m, CPL_PRIORITY_LISTEN); 342174641Skmacy cxgb_ofld_send(cdev, m); 343174641Skmacy 344174641Skmacy t3_disconnect_acceptq(so); 345174641Skmacy} 346