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