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