]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/dev/cxgb/ulp/tom/cxgb_listen.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / dev / cxgb / ulp / tom / cxgb_listen.c
1 /**************************************************************************
2
3 Copyright (c) 2007, Chelsio Inc.
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, 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
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.
27
28 ***************************************************************************/
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
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 <cxgb_osdep.h>
58 #include <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 <t3cdev.h>
68 #include <common/cxgb_firmware_exports.h>
69 #include <common/cxgb_t3_cpl.h>
70 #include <common/cxgb_tcb.h>
71 #include <common/cxgb_ctl_defs.h>
72 #include <cxgb_offload.h>
73 #include <ulp/toecore/cxgb_toedev.h>
74 #include <ulp/tom/cxgb_l2t.h>
75 #include <ulp/tom/cxgb_defs.h>
76 #include <ulp/tom/cxgb_tom.h>
77 #include <ulp/tom/cxgb_t3_ddp.h>
78 #include <ulp/tom/cxgb_toepcb.h>
79
80
81 static struct listen_info *listen_hash_add(struct tom_data *d, struct socket *so, unsigned int stid);
82 static int listen_hash_del(struct tom_data *d, struct socket *so);
83
84 /*
85  * Process a CPL_CLOSE_LISTSRV_RPL message.  If the status is good we release
86  * the STID.
87  */
88 static int
89 do_close_server_rpl(struct t3cdev *cdev, struct mbuf *m, void *ctx)
90 {
91         struct cpl_close_listserv_rpl *rpl = cplhdr(m);
92         unsigned int stid = GET_TID(rpl);
93
94         if (rpl->status != CPL_ERR_NONE)
95                 log(LOG_ERR, "Unexpected CLOSE_LISTSRV_RPL status %u for "
96                        "STID %u\n", rpl->status, stid);
97         else {
98                 struct listen_ctx *listen_ctx = (struct listen_ctx *)ctx;
99
100                 cxgb_free_stid(cdev, stid);
101                 free(listen_ctx, M_CXGB);
102         }
103
104         return (CPL_RET_BUF_DONE);
105 }
106
107 /*
108  * Process a CPL_PASS_OPEN_RPL message.  Remove the socket from the listen hash
109  * table and free the STID if there was any error, otherwise nothing to do.
110  */
111 static int
112 do_pass_open_rpl(struct t3cdev *cdev, struct mbuf *m, void *ctx)
113 {
114         struct cpl_pass_open_rpl *rpl = cplhdr(m);
115
116         if (rpl->status != CPL_ERR_NONE) {
117                 int stid = GET_TID(rpl);
118                 struct listen_ctx *listen_ctx = (struct listen_ctx *)ctx;
119                 struct tom_data *d = listen_ctx->tom_data;
120                 struct socket *lso = listen_ctx->lso;
121
122 #if VALIDATE_TID
123                 if (!lso)
124                         return (CPL_RET_UNKNOWN_TID | CPL_RET_BUF_DONE);
125 #endif
126                 /*
127                  * Note: It is safe to unconditionally call listen_hash_del()
128                  * at this point without risking unhashing a reincarnation of
129                  * an already closed socket (i.e., there is no listen, close,
130                  * listen, free the sock for the second listen while processing
131                  * a message for the first race) because we are still holding
132                  * a reference on the socket.  It is possible that the unhash
133                  * will fail because the socket is already closed, but we can't
134                  * unhash the wrong socket because it is impossible for the
135                  * socket to which this message refers to have reincarnated.
136                  */
137                 listen_hash_del(d, lso);
138                 cxgb_free_stid(cdev, stid);
139 #ifdef notyet
140                 /*
141                  * XXX need to unreference the inpcb
142                  * but we have no way of knowing that other TOMs aren't referencing it 
143                  */
144                 sock_put(lso);
145 #endif
146                 free(listen_ctx, M_CXGB);
147         }
148         return CPL_RET_BUF_DONE;
149 }
150
151 void
152 t3_init_listen_cpl_handlers(void)
153 {
154         t3tom_register_cpl_handler(CPL_PASS_OPEN_RPL, do_pass_open_rpl);
155         t3tom_register_cpl_handler(CPL_CLOSE_LISTSRV_RPL, do_close_server_rpl);
156 }
157
158 static inline int
159 listen_hashfn(const struct socket *so)
160 {
161         return ((unsigned long)so >> 10) & (LISTEN_INFO_HASH_SIZE - 1);
162 }
163
164 /*
165  * Create and add a listen_info entry to the listen hash table.  This and the
166  * listen hash table functions below cannot be called from softirqs.
167  */
168 static struct listen_info *
169 listen_hash_add(struct tom_data *d, struct socket *so, unsigned int stid)
170 {
171         struct listen_info *p;
172
173         p = malloc(sizeof(*p), M_CXGB, M_NOWAIT|M_ZERO);
174         if (p) {
175                 int bucket = listen_hashfn(so);
176
177                 p->so = so;     /* just a key, no need to take a reference */
178                 p->stid = stid;
179                 mtx_lock(&d->listen_lock);              
180                 p->next = d->listen_hash_tab[bucket];
181                 d->listen_hash_tab[bucket] = p;
182                 mtx_unlock(&d->listen_lock);
183         }
184         return p;
185 }
186
187 /*
188  * Given a pointer to a listening socket return its server TID by consulting
189  * the socket->stid map.  Returns -1 if the socket is not in the map.
190  */
191 static int
192 listen_hash_find(struct tom_data *d, struct socket *so)
193 {
194         int stid = -1, bucket = listen_hashfn(so);
195         struct listen_info *p;
196
197         mtx_lock(&d->listen_lock);
198         for (p = d->listen_hash_tab[bucket]; p; p = p->next)
199                 if (p->so == so) {
200                         stid = p->stid;
201                         break;
202                 }
203         mtx_unlock(&d->listen_lock);
204         return stid;
205 }
206
207 /*
208  * Delete the listen_info structure for a listening socket.  Returns the server
209  * TID for the socket if it is present in the socket->stid map, or -1.
210  */
211 static int
212 listen_hash_del(struct tom_data *d, struct socket *so)
213 {
214         int bucket, stid = -1;
215         struct listen_info *p, **prev;
216
217         bucket = listen_hashfn(so);
218         prev  = &d->listen_hash_tab[bucket];
219
220         mtx_lock(&d->listen_lock);
221         for (p = *prev; p; prev = &p->next, p = p->next)
222                 if (p->so == so) {
223                         stid = p->stid;
224                         *prev = p->next;
225                         free(p, M_CXGB);
226                         break;
227                 }
228         mtx_unlock(&d->listen_lock);
229         
230         return (stid);
231 }
232
233 /*
234  * Start a listening server by sending a passive open request to HW.
235  */
236 void
237 t3_listen_start(struct toedev *dev, struct socket *so, struct t3cdev *cdev)
238 {
239         int stid;
240         struct mbuf *m;
241         struct cpl_pass_open_req *req;
242         struct tom_data *d = TOM_DATA(dev);
243         struct inpcb *inp = so_sotoinpcb(so);
244         struct listen_ctx *ctx;
245
246         if (!TOM_TUNABLE(dev, activated))
247                 return;
248
249         if (listen_hash_find(d, so) != -1)
250                 return;
251         
252         CTR1(KTR_TOM, "start listen on port %u", ntohs(inp->inp_lport));
253         ctx = malloc(sizeof(*ctx), M_CXGB, M_NOWAIT|M_ZERO);
254
255         if (!ctx)
256                 return;
257
258         ctx->tom_data = d;
259         ctx->lso = so;
260         ctx->ulp_mode = TOM_TUNABLE(dev, ddp) && !(so_options_get(so) & SO_NO_DDP) ? ULP_MODE_TCPDDP : 0;
261         LIST_INIT(&ctx->synq_head);
262         
263         stid = cxgb_alloc_stid(d->cdev, d->client, ctx);
264         if (stid < 0)
265                 goto free_ctx;
266
267         m = m_gethdr(M_NOWAIT, MT_DATA);
268         if (m == NULL)
269                 goto free_stid;
270         m->m_pkthdr.len = m->m_len = sizeof(*req);
271         
272         if (!listen_hash_add(d, so, stid))
273                 goto free_all;
274
275         req = mtod(m, struct cpl_pass_open_req *);
276         req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
277         OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_PASS_OPEN_REQ, stid));
278         req->local_port = inp->inp_lport; 
279         memcpy(&req->local_ip, &inp->inp_laddr, 4);
280         req->peer_port = 0;
281         req->peer_ip = 0;
282         req->peer_netmask = 0;
283         req->opt0h = htonl(F_DELACK | F_TCAM_BYPASS);
284         req->opt0l = htonl(V_RCV_BUFSIZ(16));
285         req->opt1 = htonl(V_CONN_POLICY(CPL_CONN_POLICY_ASK));
286
287         m_set_priority(m, CPL_PRIORITY_LISTEN); 
288         cxgb_ofld_send(cdev, m);
289         return;
290
291 free_all:
292         m_free(m);
293 free_stid:
294         cxgb_free_stid(cdev, stid);
295 #if 0   
296         sock_put(sk);
297 #endif  
298 free_ctx:
299         free(ctx, M_CXGB);
300 }
301
302 /*
303  * Stop a listening server by sending a close_listsvr request to HW.
304  * The server TID is freed when we get the reply.
305  */
306 void
307 t3_listen_stop(struct toedev *dev, struct socket *so, struct t3cdev *cdev)
308 {
309         struct mbuf *m;
310         struct cpl_close_listserv_req *req;
311         struct listen_ctx *lctx;
312         int stid = listen_hash_del(TOM_DATA(dev), so);
313         
314         if (stid < 0)
315                 return;
316
317         lctx = cxgb_get_lctx(cdev, stid);
318         /*
319          * Do this early so embryonic connections are marked as being aborted
320          * while the stid is still open.  This ensures pass_establish messages
321          * that arrive while we are closing the server will be able to locate
322          * the listening socket.
323          */
324         t3_reset_synq(lctx);
325
326         /* Send the close ASAP to stop further passive opens */
327         m = m_gethdr(M_NOWAIT, MT_DATA);
328         if (m == NULL) {
329                 /*
330                  * XXX allocate from lowmem cache
331                  */
332         }
333         m->m_pkthdr.len = m->m_len = sizeof(*req);
334
335         req = mtod(m, struct cpl_close_listserv_req *);
336         req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
337         OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_CLOSE_LISTSRV_REQ, stid));
338         req->cpu_idx = 0;
339         m_set_priority(m, CPL_PRIORITY_LISTEN);
340         cxgb_ofld_send(cdev, m);
341
342         t3_disconnect_acceptq(so);
343 }