Whamcloud - gitweb
* Landed b_cray_portals_merge (3148, 3158)
[fs/lustre-release.git] / lnet / ulnds / socklnd / tcplnd.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  Copyright (c) 2002 Cray Inc.
5  *  Copyright (c) 2003 Cluster File Systems, Inc.
6  *
7  *   This file is part of Lustre, http://www.lustre.org.
8  *
9  *   Lustre is free software; you can redistribute it and/or
10  *   modify it under the terms of version 2 of the GNU General Public
11  *   License as published by the Free Software Foundation.
12  *
13  *   Lustre is distributed in the hope that it will be useful,
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *   GNU General Public License for more details.
17  *
18  *   You should have received a copy of the GNU General Public License
19  *   along with Lustre; if not, write to the Free Software
20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */
22
23 /* tcpnal.c:
24    This file implements the TCP-based nal by providing glue
25    between the connection service and the generic NAL implementation */
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <stdarg.h>
30 #include <unistd.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <pqtimer.h>
35 #include <dispatch.h>
36 #include <bridge.h>
37 #include <ipmap.h>
38 #include <connection.h>
39 #include <pthread.h>
40 #include <errno.h>
41 #ifndef __CYGWIN__
42 #include <syscall.h>
43 #endif
44
45 /* Function:  tcpnal_send
46  * Arguments: nal:     pointer to my nal control block
47  *            private: unused
48  *            cookie:  passed back to the portals library
49  *            hdr:     pointer to the portals header
50  *            nid:     destination node
51  *            pid:     destination process
52  *            data:    body of the message
53  *            len:     length of the body
54  * Returns: zero on success
55  *
56  * sends a packet to the peer, after insuring that a connection exists
57  */
58 ptl_err_t tcpnal_send(nal_cb_t *n,
59                       void *private,
60                       lib_msg_t *cookie,
61                       ptl_hdr_t *hdr,
62                       int type,
63                       ptl_nid_t nid,
64                       ptl_pid_t pid,
65                       unsigned int niov,
66                       struct iovec *iov,
67                       size_t offset,
68                       size_t len)
69 {
70     connection c;
71     bridge b=(bridge)n->nal_data;
72     struct iovec tiov[257];
73     static pthread_mutex_t send_lock = PTHREAD_MUTEX_INITIALIZER;
74     ptl_err_t rc = PTL_OK;
75     int   sysrc;
76     int   total;
77     int   ntiov;
78     int i;
79
80     if (!(c=force_tcp_connection((manager)b->lower,
81                                  PNAL_IP(nid,b),
82                                  PNAL_PORT(nid,pid),
83                                  b->local)))
84         return(PTL_FAIL);
85
86     /* TODO: these results should be checked. furthermore, provision
87        must be made for the SIGPIPE which is delivered when
88        writing on a tcp socket which has closed underneath
89        the application. there is a linux flag in the sendmsg
90        call which turns off the signally behaviour, but its
91        nonstandard */
92
93     LASSERT (niov <= 256);
94
95     tiov[0].iov_base = hdr;
96     tiov[0].iov_len = sizeof(ptl_hdr_t);
97     ntiov = 1 + lib_extract_iov(256, &tiov[1], niov, iov, offset, len);
98
99     pthread_mutex_lock(&send_lock);
100 #if 1
101     for (i = total = 0; i < ntiov; i++)
102             total += tiov[i].iov_len;
103     
104     sysrc = syscall(SYS_writev, c->fd, tiov, ntiov);
105     if (sysrc != total) {
106             fprintf (stderr, "BAD SEND rc %d != %d, errno %d\n",
107                      rc, total, errno);
108             rc = PTL_FAIL;
109     }
110 #else
111     for (i = total = 0; i <= ntiov; i++) {
112             rc = send(c->fd, tiov[i].iov_base, tiov[i].iov_len, 0);
113             
114             if (rc != tiov[i].iov_len) {
115                     fprintf (stderr, "BAD SEND rc %d != %d, errno %d\n",
116                              rc, tiov[i].iov_len, errno);
117                     rc = PTL_FAIL;
118                     break;
119             }
120             total += rc;
121     }
122 #endif
123 #if 0
124     fprintf (stderr, "sent %s total %d in %d frags\n", 
125              hdr->type == PTL_MSG_ACK ? "ACK" :
126              hdr->type == PTL_MSG_PUT ? "PUT" :
127              hdr->type == PTL_MSG_GET ? "GET" :
128              hdr->type == PTL_MSG_REPLY ? "REPLY" :
129              hdr->type == PTL_MSG_HELLO ? "HELLO" : "UNKNOWN",
130              total, niov + 1);
131 #endif
132     pthread_mutex_unlock(&send_lock);
133
134     if (rc == PTL_OK) {
135             /* NB the NAL only calls lib_finalize() if it returns PTL_OK
136              * from cb_send() */
137             lib_finalize(n, private, cookie, PTL_OK);
138     }
139
140     return(rc);
141 }
142
143
144 /* Function:  tcpnal_recv
145  * Arguments: nal_cb_t *nal:     pointer to my nal control block
146  *            void *private:     connection pointer passed through
147  *                               lib_parse()
148  *            lib_msg_t *cookie: passed back to portals library
149  *            user_ptr data:     pointer to the destination buffer
150  *            size_t mlen:       length of the body
151  *            size_t rlen:       length of data in the network
152  * Returns: zero on success
153  *
154  * blocking read of the requested data. must drain out the
155  * difference of mainpulated and requested lengths from the network
156  */
157 ptl_err_t tcpnal_recv(nal_cb_t *n,
158                       void *private,
159                       lib_msg_t *cookie,
160                       unsigned int niov,
161                       struct iovec *iov,
162                       size_t offset,
163                       size_t mlen,
164                       size_t rlen)
165
166 {
167     struct iovec tiov[256];
168     int ntiov;
169     int i;
170
171     if (!niov)
172             goto finalize;
173
174     LASSERT(mlen);
175     LASSERT(rlen);
176     LASSERT(rlen >= mlen);
177
178     ntiov = lib_extract_iov(256, tiov, niov, iov, offset, mlen);
179     
180     /* FIXME
181      * 1. Is this effecient enough? change to use readv() directly?
182      * 2. need check return from read_connection()
183      * - MeiJia
184      */
185     for (i = 0; i < ntiov; i++)
186         read_connection(private, tiov[i].iov_base, tiov[i].iov_len);
187
188 finalize:
189     /* FIXME; we always assume success here... */
190     lib_finalize(n, private, cookie, PTL_OK);
191
192     if (mlen!=rlen){
193         char *trash=malloc(rlen-mlen);
194         
195         /*TODO: check error status*/
196         read_connection(private,trash,rlen-mlen);
197         free(trash);
198     }
199
200     return(PTL_OK);
201 }
202
203
204 /* Function:  from_connection: 
205  * Arguments: c: the connection to read from 
206  * Returns: whether or not to continue reading from this connection,
207  *          expressed as a 1 to continue, and a 0 to not
208  *
209  *  from_connection() is called from the select loop when i/o is 
210  *  available. It attempts to read the portals header and 
211  *  pass it to the generic library for processing.
212  */
213 static int from_connection(void *a, void *d)
214 {
215     connection c = d;
216     bridge b = a;
217     ptl_hdr_t hdr;
218
219     if (read_connection(c, (unsigned char *)&hdr, sizeof(hdr))){
220         lib_parse(b->nal_cb, &hdr, c);
221         return(1);
222     }
223     return(0);
224 }
225
226
227 static void tcpnal_shutdown(bridge b)
228 {
229     shutdown_connections(b->lower);
230 }
231
232 /* Function:  PTL_IFACE_TCP
233  * Arguments: pid_request: desired port number to bind to
234  *            desired: passed NAL limits structure
235  *            actual: returned NAL limits structure
236  * Returns: a nal structure on success, or null on failure
237  */
238 int tcpnal_init(bridge b)
239 {
240     manager m;
241         
242     b->nal_cb->cb_send=tcpnal_send;
243     b->nal_cb->cb_recv=tcpnal_recv;
244     b->shutdown=tcpnal_shutdown;
245     
246     if (!(m=init_connections(PNAL_PORT(b->nal_cb->ni.nid,
247                                        b->nal_cb->ni.pid),
248                              from_connection,b))){
249         /* TODO: this needs to shut down the
250            newly created junk */
251         return(PTL_NAL_FAILED);
252     }
253     /* XXX cfs hack */
254     b->nal_cb->ni.pid=0;
255     b->lower=m;
256     return(PTL_OK);
257 }