1 /**************************************************************************
3 Copyright (c) 2007, Chelsio Inc.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
9 1. Redistributions of source code must retain the above copyright notice,
10 this list of conditions and the following disclaimer.
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.
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 POSSIBILITY OF SUCH DAMAGE.
28 ***************************************************************************/
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/fcntl.h>
36 #include <sys/limits.h>
39 #include <sys/mutex.h>
41 #include <sys/sockopt.h>
42 #include <sys/sockstate.h>
43 #include <sys/sockbuf.h>
45 #include <sys/socket.h>
46 #include <sys/syslog.h>
49 #include <net/route.h>
51 #include <netinet/in.h>
52 #include <netinet/in_pcb.h>
53 #include <netinet/in_systm.h>
54 #include <netinet/in_var.h>
57 #include <dev/cxgb/cxgb_osdep.h>
58 #include <dev/cxgb/sys/mbufq.h>
60 #include <netinet/tcp.h>
61 #include <netinet/tcp_var.h>
62 #include <netinet/tcp_fsm.h>
64 #include <netinet/tcp_offload.h>
65 #include <net/route.h>
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>
80 static struct listen_info *listen_hash_add(struct tom_data *d, struct socket *so, unsigned int stid);
81 static int listen_hash_del(struct tom_data *d, struct socket *so);
84 * Process a CPL_CLOSE_LISTSRV_RPL message. If the status is good we release
88 do_close_server_rpl(struct t3cdev *cdev, struct mbuf *m, void *ctx)
90 struct cpl_close_listserv_rpl *rpl = cplhdr(m);
91 unsigned int stid = GET_TID(rpl);
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);
97 struct listen_ctx *listen_ctx = (struct listen_ctx *)ctx;
99 cxgb_free_stid(cdev, stid);
100 free(listen_ctx, M_CXGB);
103 return (CPL_RET_BUF_DONE);
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.
111 do_pass_open_rpl(struct t3cdev *cdev, struct mbuf *m, void *ctx)
113 struct cpl_pass_open_rpl *rpl = cplhdr(m);
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;
123 return (CPL_RET_UNKNOWN_TID | CPL_RET_BUF_DONE);
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.
136 listen_hash_del(d, lso);
137 cxgb_free_stid(cdev, stid);
140 * XXX need to unreference the inpcb
141 * but we have no way of knowing that other TOMs aren't referencing it
145 free(listen_ctx, M_CXGB);
147 return CPL_RET_BUF_DONE;
151 t3_init_listen_cpl_handlers(void)
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);
158 listen_hashfn(const struct socket *so)
160 return ((unsigned long)so >> 10) & (LISTEN_INFO_HASH_SIZE - 1);
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.
167 static struct listen_info *
168 listen_hash_add(struct tom_data *d, struct socket *so, unsigned int stid)
170 struct listen_info *p;
172 p = malloc(sizeof(*p), M_CXGB, M_NOWAIT|M_ZERO);
174 int bucket = listen_hashfn(so);
176 p->so = so; /* just a key, no need to take a reference */
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);
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.
191 listen_hash_find(struct tom_data *d, struct socket *so)
193 int stid = -1, bucket = listen_hashfn(so);
194 struct listen_info *p;
196 mtx_lock(&d->listen_lock);
197 for (p = d->listen_hash_tab[bucket]; p; p = p->next)
202 mtx_unlock(&d->listen_lock);
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.
211 listen_hash_del(struct tom_data *d, struct socket *so)
213 int bucket, stid = -1;
214 struct listen_info *p, **prev;
216 bucket = listen_hashfn(so);
217 prev = &d->listen_hash_tab[bucket];
219 mtx_lock(&d->listen_lock);
220 for (p = *prev; p; prev = &p->next, p = p->next)
227 mtx_unlock(&d->listen_lock);
233 * Start a listening server by sending a passive open request to HW.
236 t3_listen_start(struct toedev *dev, struct socket *so, struct t3cdev *cdev)
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;
245 if (!TOM_TUNABLE(dev, activated))
248 if (listen_hash_find(d, so) != -1)
251 CTR1(KTR_TOM, "start listen on port %u", ntohs(inp->inp_lport));
252 ctx = malloc(sizeof(*ctx), M_CXGB, M_NOWAIT|M_ZERO);
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);
262 stid = cxgb_alloc_stid(d->cdev, d->client, ctx);
266 m = m_gethdr(M_NOWAIT, MT_DATA);
269 m->m_pkthdr.len = m->m_len = sizeof(*req);
271 if (!listen_hash_add(d, so, stid))
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);
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));
286 m_set_priority(m, CPL_PRIORITY_LISTEN);
287 cxgb_ofld_send(cdev, m);
293 cxgb_free_stid(cdev, stid);
302 * Stop a listening server by sending a close_listsvr request to HW.
303 * The server TID is freed when we get the reply.
306 t3_listen_stop(struct toedev *dev, struct socket *so, struct t3cdev *cdev)
309 struct cpl_close_listserv_req *req;
310 struct listen_ctx *lctx;
311 int stid = listen_hash_del(TOM_DATA(dev), so);
316 lctx = cxgb_get_lctx(cdev, stid);
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.
325 /* Send the close ASAP to stop further passive opens */
326 m = m_gethdr(M_NOWAIT, MT_DATA);
329 * XXX allocate from lowmem cache
332 m->m_pkthdr.len = m->m_len = sizeof(*req);
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));
338 m_set_priority(m, CPL_PRIORITY_LISTEN);
339 cxgb_ofld_send(cdev, m);
341 t3_disconnect_acceptq(so);