Linux: Remove old code from RX kernel implementation
[openafs.git] / src / rx / LINUX / rx_knet.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * rx_knet.c - RX kernel send, receive and timer routines.
12  *
13  * Linux implementation.
14  */
15 #include <afsconfig.h>
16 #include "afs/param.h"
17
18
19 #include <linux/version.h>
20 #include "rx/rx_kcommon.h"
21 #include "h/smp_lock.h"
22 #include <asm/uaccess.h>
23 #ifdef ADAPT_PMTU
24 #include <linux/errqueue.h>
25 #include <linux/icmp.h>
26 #endif
27
28 /* rxk_NewSocket
29  * open and bind RX socket
30  */
31 osi_socket *
32 rxk_NewSocketHost(afs_uint32 ahost, short aport)
33 {
34     struct socket *sockp;
35     struct sockaddr_in myaddr;
36     int code;
37     KERNEL_SPACE_DECL;
38 #ifdef ADAPT_PMTU
39     int pmtu = IP_PMTUDISC_WANT;
40     int do_recverr = 1;
41 #else
42     int pmtu = IP_PMTUDISC_DONT;
43 #endif
44
45     /* We need a better test for this. if you need it back, tell us
46      * how to detect it. 
47      */
48 #ifdef LINUX_KERNEL_SOCK_CREATE_V
49     code = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &sockp, 0);
50 #else
51     code = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &sockp);
52 #endif
53     if (code < 0)
54         return NULL;
55
56     /* Bind socket */
57     myaddr.sin_family = AF_INET;
58     myaddr.sin_addr.s_addr = ahost;
59     myaddr.sin_port = aport;
60     code =
61         sockp->ops->bind(sockp, (struct sockaddr *)&myaddr, sizeof(myaddr));
62
63     if (code < 0) {
64         printk("sock_release(rx_socket) FIXME\n");
65         return NULL;
66     }
67
68     TO_USER_SPACE();
69     sockp->ops->setsockopt(sockp, SOL_IP, IP_MTU_DISCOVER, (char *)&pmtu,
70                            sizeof(pmtu));
71 #ifdef ADAPT_PMTU
72     sockp->ops->setsockopt(sockp, SOL_IP, IP_RECVERR, (char *)&do_recverr,
73                            sizeof(do_recverr));
74 #endif
75     TO_KERNEL_SPACE();
76     return (osi_socket *)sockp;
77 }
78
79 osi_socket *
80 rxk_NewSocket(short aport)
81 {
82     return rxk_NewSocketHost(htonl(INADDR_ANY), aport);
83 }
84
85 /* free socket allocated by osi_NetSocket */
86 int
87 rxk_FreeSocket(struct socket *asocket)
88 {
89     AFS_STATCNT(osi_FreeSocket);
90     return 0;
91 }
92
93 #ifdef ADAPT_PMTU
94 void
95 handle_socket_error(osi_socket so)
96 {
97     KERNEL_SPACE_DECL;
98     struct msghdr msg;
99     struct cmsghdr *cmsg;
100     struct sock_extended_err *err;
101     struct sockaddr_in addr;
102     struct sockaddr *offender;
103     char *controlmsgbuf;
104     int code;
105     struct socket *sop = (struct socket *)so;
106
107     if (!(controlmsgbuf=rxi_Alloc(256)))
108         return;
109     msg.msg_name = &addr;
110     msg.msg_namelen = sizeof(addr);
111     msg.msg_iov = NULL;
112     msg.msg_iovlen = 0;
113     msg.msg_control = controlmsgbuf;
114     msg.msg_controllen = 256;
115     msg.msg_flags = 0;
116
117     TO_USER_SPACE();
118     code = sock_recvmsg(sop, &msg, 256, MSG_ERRQUEUE|MSG_DONTWAIT|MSG_TRUNC);
119     TO_KERNEL_SPACE();
120
121     if (code < 0 || !(msg.msg_flags & MSG_ERRQUEUE))
122         goto out;
123
124     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
125         if (CMSG_OK(&msg, cmsg) && cmsg->cmsg_level == SOL_IP &&
126             cmsg->cmsg_type == IP_RECVERR)
127             break;
128     }
129     if (!cmsg)
130         goto out;
131     err = CMSG_DATA(cmsg);
132     offender = SO_EE_OFFENDER(err);
133     
134     if (offender->sa_family != AF_INET)
135        goto out;
136
137     memcpy(&addr, offender, sizeof(addr));
138
139     if (err->ee_origin == SO_EE_ORIGIN_ICMP &&
140         err->ee_type == ICMP_DEST_UNREACH &&
141         err->ee_code == ICMP_FRAG_NEEDED) {
142         rxi_SetPeerMtu(ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port),
143                        err->ee_info);
144     }
145     /* other DEST_UNREACH's and TIME_EXCEEDED should be dealt with too */
146
147 out:
148     rxi_Free(controlmsgbuf, 256);
149     return;
150 }
151 #endif
152
153 /* osi_NetSend
154  *
155  * Return codes:
156  * 0 = success
157  * non-zero = failure
158  */
159 int
160 osi_NetSend(osi_socket sop, struct sockaddr_in *to, struct iovec *iovec,
161             int iovcnt, afs_int32 size, int istack)
162 {
163     KERNEL_SPACE_DECL;
164     struct msghdr msg;
165     int code;
166 #ifdef ADAPT_PMTU
167     int sockerr;
168     size_t esize;
169
170     while (1) {
171         sockerr=0;
172         esize = sizeof(sockerr);
173         TO_USER_SPACE();
174         sop->ops->getsockopt(sop, SOL_SOCKET, SO_ERROR, (char *)&sockerr,
175                            &esize);
176         TO_KERNEL_SPACE();
177         if (sockerr == 0)
178            break;
179         handle_socket_error(sop);
180     }
181 #endif
182
183     msg.msg_iovlen = iovcnt;
184     msg.msg_iov = iovec;
185     msg.msg_name = to;
186     msg.msg_namelen = sizeof(*to);
187     msg.msg_control = NULL;
188     msg.msg_controllen = 0;
189     msg.msg_flags = 0;
190
191     TO_USER_SPACE();
192     code = sock_sendmsg(sop, &msg, size);
193     TO_KERNEL_SPACE();
194     return (code < 0) ? code : 0;
195 }
196
197
198 /* osi_NetReceive
199  * OS dependent part of kernel RX listener thread.
200  *
201  * Arguments:
202  *      so      socket to receive on, typically rx_socket
203  *      from    pointer to a sockaddr_in. 
204  *      iov     array of iovecs to fill in.
205  *      iovcnt  how many iovecs there are.
206  *      lengthp IN/OUT in: total space available in iovecs. out: size of read.
207  *
208  * Return
209  * 0 if successful
210  * error code (such as EINTER) if not
211  *
212  * Environment
213  *      Note that the maximum number of iovecs is 2 + RX_MAXWVECS. This is
214  *      so we have a little space to look for packets larger than 
215  *      rx_maxReceiveSize.
216  */
217 int rxk_lastSocketError;
218 int rxk_nSocketErrors;
219 int
220 osi_NetReceive(osi_socket so, struct sockaddr_in *from, struct iovec *iov,
221                int iovcnt, int *lengthp)
222 {
223     KERNEL_SPACE_DECL;
224     struct msghdr msg;
225     int code;
226 #ifdef ADAPT_PMTU
227     int sockerr;
228     size_t esize;
229 #endif
230     struct iovec tmpvec[RX_MAXWVECS + 2];
231     struct socket *sop = (struct socket *)so;
232
233     if (iovcnt > RX_MAXWVECS + 2) {
234         osi_Panic("Too many (%d) iovecs passed to osi_NetReceive\n", iovcnt);
235     }
236 #ifdef ADAPT_PMTU
237     while (1) {
238         sockerr=0;
239         esize = sizeof(sockerr);
240         TO_USER_SPACE();
241         sop->ops->getsockopt(sop, SOL_SOCKET, SO_ERROR, (char *)&sockerr,
242                            &esize);
243         TO_KERNEL_SPACE();
244         if (sockerr == 0)
245            break;
246         handle_socket_error(so);
247     }
248 #endif
249     memcpy(tmpvec, iov, iovcnt * sizeof(struct iovec));
250     msg.msg_name = from;
251     msg.msg_iov = tmpvec;
252     msg.msg_iovlen = iovcnt;
253     msg.msg_control = NULL;
254     msg.msg_controllen = 0;
255     msg.msg_flags = 0;
256
257     TO_USER_SPACE();
258     code = sock_recvmsg(sop, &msg, *lengthp, 0);
259     TO_KERNEL_SPACE();
260
261     if (code < 0) {
262 #ifdef CONFIG_PM
263         if (
264 # ifdef PF_FREEZE
265             current->flags & PF_FREEZE
266 # else
267 #  if defined(STRUCT_TASK_STRUCT_HAS_TODO)
268             !current->todo
269 #  else
270 #   if defined(STRUCT_TASK_STRUCT_HAS_THREAD_INFO)
271             test_ti_thread_flag(current->thread_info, TIF_FREEZE)
272 #   else
273             test_ti_thread_flag(task_thread_info(current), TIF_FREEZE)
274 #   endif
275 #  endif
276 # endif
277             )
278 # ifdef LINUX_REFRIGERATOR_TAKES_PF_FREEZE
279             refrigerator(PF_FREEZE);
280 # else
281             refrigerator();
282 # endif
283             set_current_state(TASK_INTERRUPTIBLE);
284 #endif
285
286         /* Clear the error before using the socket again.
287          * Oh joy, Linux has hidden header files as well. It appears we can
288          * simply call again and have it clear itself via sock_error().
289          */
290         flush_signals(current); /* We don't want no stinkin' signals. */
291         rxk_lastSocketError = code;
292         rxk_nSocketErrors++;
293     } else {
294         *lengthp = code;
295         code = 0;
296     }
297
298     return code;
299 }
300
301 void
302 osi_StopListener(void)
303 {
304     extern struct task_struct *rxk_ListenerTask;
305
306     while (rxk_ListenerTask) {
307         if (rxk_ListenerTask) {
308             flush_signals(rxk_ListenerTask);
309             force_sig(SIGKILL, rxk_ListenerTask);
310         }
311         if (!rxk_ListenerTask)
312             break;
313         afs_osi_Sleep(&rxk_ListenerTask);
314     }
315     sock_release(rx_socket);
316     rx_socket = NULL;
317 }
318