cxgb_listen.c revision 181067
1/************************************************************************** 2 3Copyright (c) 2007, Chelsio Inc. 4All rights reserved. 5 6Redistribution and use in source and binary forms, with or without 7modification, are permitted provided that the following conditions are met: 8 9 1. Redistributions of source code must retain the above copyright notice, 10 this list of conditions and the following disclaimer. 11 12 2. Neither the name of the Chelsio Corporation nor the names of its 13 contributors may be used to endorse or promote products derived from 14 this software without specific prior written permission. 15 16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26POSSIBILITY OF SUCH DAMAGE. 27 28***************************************************************************/ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: head/sys/dev/cxgb/ulp/tom/cxgb_listen.c 181067 2008-07-31 20:28:58Z kmacy $"); 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/fcntl.h> 36#include <sys/limits.h> 37#include <sys/lock.h> 38#include <sys/mbuf.h> 39#include <sys/mutex.h> 40 41#include <sys/sockopt.h> 42#include <sys/sockstate.h> 43#include <sys/sockbuf.h> 44 45#include <sys/socket.h> 46#include <sys/syslog.h> 47 48#include <net/if.h> 49#include <net/route.h> 50 51#include <netinet/in.h> 52#include <netinet/in_pcb.h> 53#include <netinet/in_systm.h> 54#include <netinet/in_var.h> 55 56 57#include <dev/cxgb/cxgb_osdep.h> 58#include <dev/cxgb/sys/mbufq.h> 59 60#include <netinet/tcp.h> 61#include <netinet/tcp_var.h> 62#include <netinet/tcp_fsm.h> 63 64#include <netinet/tcp_offload.h> 65#include <net/route.h> 66 67#include <dev/cxgb/t3cdev.h> 68#include <dev/cxgb/common/cxgb_firmware_exports.h> 69#include <dev/cxgb/common/cxgb_t3_cpl.h> 70#include <dev/cxgb/common/cxgb_tcb.h> 71#include <dev/cxgb/common/cxgb_ctl_defs.h> 72#include <dev/cxgb/cxgb_offload.h> 73#include <dev/cxgb/ulp/toecore/cxgb_toedev.h> 74#include <dev/cxgb/ulp/tom/cxgb_defs.h> 75#include <dev/cxgb/ulp/tom/cxgb_tom.h> 76#include <dev/cxgb/ulp/tom/cxgb_t3_ddp.h> 77#include <dev/cxgb/ulp/tom/cxgb_toepcb.h> 78 79 80static struct listen_info *listen_hash_add(struct tom_data *d, struct socket *so, unsigned int stid); 81static int listen_hash_del(struct tom_data *d, struct socket *so); 82 83/* 84 * Process a CPL_CLOSE_LISTSRV_RPL message. If the status is good we release 85 * the STID. 86 */ 87static int 88do_close_server_rpl(struct t3cdev *cdev, struct mbuf *m, void *ctx) 89{ 90 struct cpl_close_listserv_rpl *rpl = cplhdr(m); 91 unsigned int stid = GET_TID(rpl); 92 93 if (rpl->status != CPL_ERR_NONE) 94 log(LOG_ERR, "Unexpected CLOSE_LISTSRV_RPL status %u for " 95 "STID %u\n", rpl->status, stid); 96 else { 97 struct listen_ctx *listen_ctx = (struct listen_ctx *)ctx; 98 99 cxgb_free_stid(cdev, stid); 100 free(listen_ctx, M_CXGB); 101 } 102 103 return (CPL_RET_BUF_DONE); 104} 105 106/* 107 * Process a CPL_PASS_OPEN_RPL message. Remove the socket from the listen hash 108 * table and free the STID if there was any error, otherwise nothing to do. 109 */ 110static int 111do_pass_open_rpl(struct t3cdev *cdev, struct mbuf *m, void *ctx) 112{ 113 struct cpl_pass_open_rpl *rpl = cplhdr(m); 114 115 if (rpl->status != CPL_ERR_NONE) { 116 int stid = GET_TID(rpl); 117 struct listen_ctx *listen_ctx = (struct listen_ctx *)ctx; 118 struct tom_data *d = listen_ctx->tom_data; 119 struct socket *lso = listen_ctx->lso; 120 121#if VALIDATE_TID 122 if (!lso) 123 return (CPL_RET_UNKNOWN_TID | CPL_RET_BUF_DONE); 124#endif 125 /* 126 * Note: It is safe to unconditionally call listen_hash_del() 127 * at this point without risking unhashing a reincarnation of 128 * an already closed socket (i.e., there is no listen, close, 129 * listen, free the sock for the second listen while processing 130 * a message for the first race) because we are still holding 131 * a reference on the socket. It is possible that the unhash 132 * will fail because the socket is already closed, but we can't 133 * unhash the wrong socket because it is impossible for the 134 * socket to which this message refers to have reincarnated. 135 */ 136 listen_hash_del(d, lso); 137 cxgb_free_stid(cdev, stid); 138#ifdef notyet 139 /* 140 * XXX need to unreference the inpcb 141 * but we have no way of knowing that other TOMs aren't referencing it 142 */ 143 sock_put(lso); 144#endif 145 free(listen_ctx, M_CXGB); 146 } 147 return CPL_RET_BUF_DONE; 148} 149 150void 151t3_init_listen_cpl_handlers(void) 152{ 153 t3tom_register_cpl_handler(CPL_PASS_OPEN_RPL, do_pass_open_rpl); 154 t3tom_register_cpl_handler(CPL_CLOSE_LISTSRV_RPL, do_close_server_rpl); 155} 156 157static inline int 158listen_hashfn(const struct socket *so) 159{ 160 return ((unsigned long)so >> 10) & (LISTEN_INFO_HASH_SIZE - 1); 161} 162 163/* 164 * Create and add a listen_info entry to the listen hash table. This and the 165 * listen hash table functions below cannot be called from softirqs. 166 */ 167static struct listen_info * 168listen_hash_add(struct tom_data *d, struct socket *so, unsigned int stid) 169{ 170 struct listen_info *p; 171 172 p = malloc(sizeof(*p), M_CXGB, M_NOWAIT|M_ZERO); 173 if (p) { 174 int bucket = listen_hashfn(so); 175 176 p->so = so; /* just a key, no need to take a reference */ 177 p->stid = stid; 178 mtx_lock(&d->listen_lock); 179 p->next = d->listen_hash_tab[bucket]; 180 d->listen_hash_tab[bucket] = p; 181 mtx_unlock(&d->listen_lock); 182 } 183 return p; 184} 185 186/* 187 * Given a pointer to a listening socket return its server TID by consulting 188 * the socket->stid map. Returns -1 if the socket is not in the map. 189 */ 190static int 191listen_hash_find(struct tom_data *d, struct socket *so) 192{ 193 int stid = -1, bucket = listen_hashfn(so); 194 struct listen_info *p; 195 196 mtx_lock(&d->listen_lock); 197 for (p = d->listen_hash_tab[bucket]; p; p = p->next) 198 if (p->so == so) { 199 stid = p->stid; 200 break; 201 } 202 mtx_unlock(&d->listen_lock); 203 return stid; 204} 205 206/* 207 * Delete the listen_info structure for a listening socket. Returns the server 208 * TID for the socket if it is present in the socket->stid map, or -1. 209 */ 210static int 211listen_hash_del(struct tom_data *d, struct socket *so) 212{ 213 int bucket, stid = -1; 214 struct listen_info *p, **prev; 215 216 bucket = listen_hashfn(so); 217 prev = &d->listen_hash_tab[bucket]; 218 219 mtx_lock(&d->listen_lock); 220 for (p = *prev; p; prev = &p->next, p = p->next) 221 if (p->so == so) { 222 stid = p->stid; 223 *prev = p->next; 224 free(p, M_CXGB); 225 break; 226 } 227 mtx_unlock(&d->listen_lock); 228 229 return (stid); 230} 231 232/* 233 * Start a listening server by sending a passive open request to HW. 234 */ 235void 236t3_listen_start(struct toedev *dev, struct socket *so, struct t3cdev *cdev) 237{ 238 int stid; 239 struct mbuf *m; 240 struct cpl_pass_open_req *req; 241 struct tom_data *d = TOM_DATA(dev); 242 struct inpcb *inp = so_sotoinpcb(so); 243 struct listen_ctx *ctx; 244 245 if (!TOM_TUNABLE(dev, activated)) 246 return; 247 248 if (listen_hash_find(d, so) != -1) 249 return; 250 251 CTR1(KTR_TOM, "start listen on port %u", ntohs(inp->inp_lport)); 252 ctx = malloc(sizeof(*ctx), M_CXGB, M_NOWAIT|M_ZERO); 253 254 if (!ctx) 255 return; 256 257 ctx->tom_data = d; 258 ctx->lso = so; 259 ctx->ulp_mode = TOM_TUNABLE(dev, ddp) && !(so_options_get(so) & SO_NO_DDP) ? ULP_MODE_TCPDDP : 0; 260 LIST_INIT(&ctx->synq_head); 261 262 stid = cxgb_alloc_stid(d->cdev, d->client, ctx); 263 if (stid < 0) 264 goto free_ctx; 265 266 m = m_gethdr(M_NOWAIT, MT_DATA); 267 if (m == NULL) 268 goto free_stid; 269 m->m_pkthdr.len = m->m_len = sizeof(*req); 270 271 if (!listen_hash_add(d, so, stid)) 272 goto free_all; 273 274 req = mtod(m, struct cpl_pass_open_req *); 275 req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 276 OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_PASS_OPEN_REQ, stid)); 277 req->local_port = inp->inp_lport; 278 memcpy(&req->local_ip, &inp->inp_laddr, 4); 279 req->peer_port = 0; 280 req->peer_ip = 0; 281 req->peer_netmask = 0; 282 req->opt0h = htonl(F_DELACK | F_TCAM_BYPASS); 283 req->opt0l = htonl(V_RCV_BUFSIZ(16)); 284 req->opt1 = htonl(V_CONN_POLICY(CPL_CONN_POLICY_ASK)); 285 286 m_set_priority(m, CPL_PRIORITY_LISTEN); 287 cxgb_ofld_send(cdev, m); 288 return; 289 290free_all: 291 m_free(m); 292free_stid: 293 cxgb_free_stid(cdev, stid); 294#if 0 295 sock_put(sk); 296#endif 297free_ctx: 298 free(ctx, M_CXGB); 299} 300 301/* 302 * Stop a listening server by sending a close_listsvr request to HW. 303 * The server TID is freed when we get the reply. 304 */ 305void 306t3_listen_stop(struct toedev *dev, struct socket *so, struct t3cdev *cdev) 307{ 308 struct mbuf *m; 309 struct cpl_close_listserv_req *req; 310 struct listen_ctx *lctx; 311 int stid = listen_hash_del(TOM_DATA(dev), so); 312 313 if (stid < 0) 314 return; 315 316 lctx = cxgb_get_lctx(cdev, stid); 317 /* 318 * Do this early so embryonic connections are marked as being aborted 319 * while the stid is still open. This ensures pass_establish messages 320 * that arrive while we are closing the server will be able to locate 321 * the listening socket. 322 */ 323 t3_reset_synq(lctx); 324 325 /* Send the close ASAP to stop further passive opens */ 326 m = m_gethdr(M_NOWAIT, MT_DATA); 327 if (m == NULL) { 328 /* 329 * XXX allocate from lowmem cache 330 */ 331 } 332 m->m_pkthdr.len = m->m_len = sizeof(*req); 333 334 req = mtod(m, struct cpl_close_listserv_req *); 335 req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 336 OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_CLOSE_LISTSRV_REQ, stid)); 337 req->cpu_idx = 0; 338 m_set_priority(m, CPL_PRIORITY_LISTEN); 339 cxgb_ofld_send(cdev, m); 340 341 t3_disconnect_acceptq(so); 342} 343