]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/dev/cxgb/ulp/tom/cxgb_listen.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.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 <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
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);
82
83 /*
84  * Process a CPL_CLOSE_LISTSRV_RPL message.  If the status is good we release
85  * the STID.
86  */
87 static int
88 do_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  */
110 static int
111 do_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
150 void
151 t3_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
157 static inline int
158 listen_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  */
167 static struct listen_info *
168 listen_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  */
190 static int
191 listen_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  */
210 static int
211 listen_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  */
235 void
236 t3_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
290 free_all:
291         m_free(m);
292 free_stid:
293         cxgb_free_stid(cdev, stid);
294 #if 0   
295         sock_put(sk);
296 #endif  
297 free_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  */
305 void
306 t3_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 }