Whamcloud - gitweb
bd73fb2d40b09c5feb5cd6dec19cfb03d0e1034d
[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 <procbridge.h>
37 #include <connection.h>
38 #include <errno.h>
39
40 #ifndef __CYGWIN__
41 #include <syscall.h>
42 #endif
43
44 void
45 tcpnal_notify(lnet_ni_t *ni, lnet_nid_t nid, int alive)
46 {
47         bridge     b = (bridge)ni->ni_data;
48         connection c;
49
50         if (!alive) {
51                 LBUG();
52         }
53
54         c = force_tcp_connection((manager)b->lower, nid, b->local);
55         if (c == NULL)
56                 CERROR("Can't create connection to %s\n",
57                        libcfs_nid2str(nid));
58 }
59
60 /*
61  * sends a packet to the peer, after insuring that a connection exists
62  */
63 int tcpnal_send(lnet_ni_t *ni, void *private, lnet_msg_t *lntmsg)
64 {
65         lnet_hdr_t        *hdr = &lntmsg->msg_hdr;
66         lnet_process_id_t  target = lntmsg->msg_target;
67         unsigned int       niov = lntmsg->msg_niov;
68         struct iovec      *iov = lntmsg->msg_iov;
69         unsigned int       offset = lntmsg->msg_offset;
70         unsigned int       len = lntmsg->msg_len;
71
72         connection c;
73         bridge b = (bridge)ni->ni_data;
74         struct iovec tiov[257];
75         static pthread_mutex_t send_lock = PTHREAD_MUTEX_INITIALIZER;
76         int rc = 0;
77         int   sysrc;
78         int   total;
79         int   ntiov;
80         int i;
81
82         if (!(c = force_tcp_connection((manager)b->lower, target.nid,
83                                        b->local)))
84                 return(-EIO);
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         LASSERT (len == 0 || iov != NULL);      /* I don't understand kiovs */
95
96         tiov[0].iov_base = hdr;
97         tiov[0].iov_len = sizeof(lnet_hdr_t);
98         ntiov = 1 + lnet_extract_iov(256, &tiov[1], niov, iov, offset, len);
99
100         pthread_mutex_lock(&send_lock);
101 #if 1
102         for (i = total = 0; i < ntiov; i++)
103                 total += tiov[i].iov_len;
104
105         sysrc = syscall(SYS_writev, c->fd, tiov, ntiov);
106         if (sysrc != total) {
107                 fprintf (stderr, "BAD SEND rc %d != %d, errno %d\n",
108                          rc, total, errno);
109                 rc = -errno;
110         }
111 #else
112         for (i = total = 0; i <= ntiov; i++) {
113                 rc = send(c->fd, tiov[i].iov_base, tiov[i].iov_len, 0);
114
115                 if (rc != tiov[i].iov_len) {
116                         fprintf (stderr, "BAD SEND rc %d != %d, errno %d\n",
117                                  rc, tiov[i].iov_len, errno);
118                         rc = -errno;
119                         break;
120                 }
121                 total += rc;
122         }
123 #endif
124 #if 0
125         fprintf (stderr, "sent %s total %d in %d frags\n",
126                  hdr->type == LNET_MSG_ACK ? "ACK" :
127                  hdr->type == LNET_MSG_PUT ? "PUT" :
128                  hdr->type == LNET_MSG_GET ? "GET" :
129                  hdr->type == LNET_MSG_REPLY ? "REPLY" :
130                  hdr->type == LNET_MSG_HELLO ? "HELLO" : "UNKNOWN",
131                  total, niov + 1);
132 #endif
133         pthread_mutex_unlock(&send_lock);
134
135         if (rc == 0) {
136                 /* NB the NAL only calls lnet_finalize() if it returns 0
137                  * from cb_send() */
138                 lnet_finalize(ni, lntmsg, 0);
139         }
140
141         return(rc);
142 }
143
144
145 int tcpnal_recv(lnet_ni_t     *ni,
146                 void         *private,
147                 lnet_msg_t   *cookie,
148                 int           delayed,
149                 unsigned int  niov,
150                 struct iovec *iov,
151                 lnet_kiov_t  *kiov,
152                 unsigned int  offset,
153                 unsigned int  mlen,
154                 unsigned int  rlen)
155 {
156         struct iovec tiov[256];
157         int ntiov;
158         int i;
159
160         if (mlen == 0)
161                 goto finalize;
162
163         LASSERT(iov != NULL);           /* I don't understand kiovs */
164
165         ntiov = lnet_extract_iov(256, tiov, niov, iov, offset, mlen);
166
167         /* FIXME
168          * 1. Is this effecient enough? change to use readv() directly?
169          * 2. need check return from read_connection()
170          * - MeiJia
171          */
172         for (i = 0; i < ntiov; i++)
173                 read_connection(private, tiov[i].iov_base, tiov[i].iov_len);
174
175 finalize:
176         /* FIXME; we always assume success here... */
177         lnet_finalize(ni, cookie, 0);
178
179         LASSERT(rlen >= mlen);
180
181         if (mlen != rlen){
182                 char *trash=malloc(rlen - mlen);
183
184                 /*TODO: check error status*/
185                 read_connection(private, trash, rlen - mlen);
186                 free(trash);
187         }
188
189         return(0);
190 }
191
192
193 /* Function:  from_connection:
194  * Arguments: c: the connection to read from
195  * Returns: whether or not to continue reading from this connection,
196  *          expressed as a 1 to continue, and a 0 to not
197  *
198  *  from_connection() is called from the select loop when i/o is
199  *  available. It attempts to read the portals header and
200  *  pass it to the generic library for processing.
201  */
202 static int from_connection(void *a, void *d)
203 {
204         connection c = d;
205         bridge     b = a;
206         lnet_hdr_t hdr;
207         int  rc;
208
209         if (read_connection(c, (unsigned char *)&hdr, sizeof(hdr))) {
210                 /* replace dest_nid,pid (socknal sets its own) */
211                 hdr.dest_nid = cpu_to_le64(b->b_ni->ni_nid);
212                 hdr.dest_pid = cpu_to_le32(the_lnet.ln_pid);
213
214                 rc = lnet_parse(b->b_ni, &hdr, c->peer_nid, c, 0);
215                 if (rc < 0) {
216                         CERROR("Error %d from lnet_parse\n", rc);
217                         return 0;
218                 }
219
220                 return(1);
221         }
222         return(0);
223 }
224
225
226 void tcpnal_shutdown(bridge b)
227 {
228         shutdown_connections(b->lower);
229 }
230
231 /* Function:  PTL_IFACE_TCP
232  * Arguments: pid_request: desired port number to bind to
233  *            desired: passed NAL limits structure
234  *            actual: returned NAL limits structure
235  * Returns: a nal structure on success, or null on failure
236  */
237 int tcpnal_init(bridge b)
238 {
239         manager m;
240
241         tcpnal_set_global_params();
242
243         if (!(m = init_connections(from_connection, b))) {
244                 /* TODO: this needs to shut down the newly created junk */
245                 return(-ENXIO);
246         }
247         b->lower = m;
248         return(0);
249 }