Fix performance regression on lmbench select benchmark
[linux-2.6] / fs / smbfs / sock.c
1 /*
2  *  sock.c
3  *
4  *  Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
5  *  Copyright (C) 1997 by Volker Lendecke
6  *
7  *  Please add a note about your changes to smbfs in the ChangeLog file.
8  */
9
10 #include <linux/fs.h>
11 #include <linux/time.h>
12 #include <linux/errno.h>
13 #include <linux/socket.h>
14 #include <linux/fcntl.h>
15 #include <linux/file.h>
16 #include <linux/in.h>
17 #include <linux/net.h>
18 #include <linux/mm.h>
19 #include <linux/netdevice.h>
20 #include <linux/workqueue.h>
21 #include <net/scm.h>
22 #include <net/tcp_states.h>
23 #include <net/ip.h>
24
25 #include <linux/smb_fs.h>
26 #include <linux/smb.h>
27 #include <linux/smbno.h>
28
29 #include <asm/uaccess.h>
30 #include <asm/ioctls.h>
31
32 #include "smb_debug.h"
33 #include "proto.h"
34 #include "request.h"
35
36
37 static int
38 _recvfrom(struct socket *socket, unsigned char *ubuf, int size, unsigned flags)
39 {
40         struct kvec iov = {ubuf, size};
41         struct msghdr msg = {.msg_flags = flags};
42         msg.msg_flags |= MSG_DONTWAIT | MSG_NOSIGNAL;
43         return kernel_recvmsg(socket, &msg, &iov, 1, size, msg.msg_flags);
44 }
45
46 /*
47  * Return the server this socket belongs to
48  */
49 static struct smb_sb_info *
50 server_from_socket(struct socket *socket)
51 {
52         return socket->sk->sk_user_data;
53 }
54
55 /*
56  * Called when there is data on the socket.
57  */
58 void
59 smb_data_ready(struct sock *sk, int len)
60 {
61         struct smb_sb_info *server = server_from_socket(sk->sk_socket);
62         void (*data_ready)(struct sock *, int) = server->data_ready;
63
64         data_ready(sk, len);
65         VERBOSE("(%p, %d)\n", sk, len);
66         smbiod_wake_up();
67 }
68
69 int
70 smb_valid_socket(struct inode * inode)
71 {
72         return (inode && S_ISSOCK(inode->i_mode) && 
73                 SOCKET_I(inode)->type == SOCK_STREAM);
74 }
75
76 static struct socket *
77 server_sock(struct smb_sb_info *server)
78 {
79         struct file *file;
80
81         if (server && (file = server->sock_file))
82         {
83 #ifdef SMBFS_PARANOIA
84                 if (!smb_valid_socket(file->f_path.dentry->d_inode))
85                         PARANOIA("bad socket!\n");
86 #endif
87                 return SOCKET_I(file->f_path.dentry->d_inode);
88         }
89         return NULL;
90 }
91
92 void
93 smb_close_socket(struct smb_sb_info *server)
94 {
95         struct file * file = server->sock_file;
96
97         if (file) {
98                 struct socket *sock = server_sock(server);
99
100                 VERBOSE("closing socket %p\n", sock);
101                 sock->sk->sk_data_ready = server->data_ready;
102                 server->sock_file = NULL;
103                 fput(file);
104         }
105 }
106
107 static int
108 smb_get_length(struct socket *socket, unsigned char *header)
109 {
110         int result;
111
112         result = _recvfrom(socket, header, 4, MSG_PEEK);
113         if (result == -EAGAIN)
114                 return -ENODATA;
115         if (result < 0) {
116                 PARANOIA("recv error = %d\n", -result);
117                 return result;
118         }
119         if (result < 4)
120                 return -ENODATA;
121
122         switch (header[0]) {
123         case 0x00:
124         case 0x82:
125                 break;
126
127         case 0x85:
128                 DEBUG1("Got SESSION KEEP ALIVE\n");
129                 _recvfrom(socket, header, 4, 0);        /* read away */
130                 return -ENODATA;
131
132         default:
133                 PARANOIA("Invalid NBT packet, code=%x\n", header[0]);
134                 return -EIO;
135         }
136
137         /* The length in the RFC NB header is the raw data length */
138         return smb_len(header);
139 }
140
141 int
142 smb_recv_available(struct smb_sb_info *server)
143 {
144         mm_segment_t oldfs;
145         int avail, err;
146         struct socket *sock = server_sock(server);
147
148         oldfs = get_fs();
149         set_fs(get_ds());
150         err = sock->ops->ioctl(sock, SIOCINQ, (unsigned long) &avail);
151         set_fs(oldfs);
152         return (err >= 0) ? avail : err;
153 }
154
155 /*
156  * Adjust the kvec to move on 'n' bytes (from nfs/sunrpc)
157  */
158 static int
159 smb_move_iov(struct kvec **data, size_t *num, struct kvec *vec, unsigned amount)
160 {
161         struct kvec *iv = *data;
162         int i;
163         int len;
164
165         /*
166          *      Eat any sent kvecs
167          */
168         while (iv->iov_len <= amount) {
169                 amount -= iv->iov_len;
170                 iv++;
171                 (*num)--;
172         }
173
174         /*
175          *      And chew down the partial one
176          */
177         vec[0].iov_len = iv->iov_len-amount;
178         vec[0].iov_base =((unsigned char *)iv->iov_base)+amount;
179         iv++;
180
181         len = vec[0].iov_len;
182
183         /*
184          *      And copy any others
185          */
186         for (i = 1; i < *num; i++) {
187                 vec[i] = *iv++;
188                 len += vec[i].iov_len;
189         }
190
191         *data = vec;
192         return len;
193 }
194
195 /*
196  * smb_receive_header
197  * Only called by the smbiod thread.
198  */
199 int
200 smb_receive_header(struct smb_sb_info *server)
201 {
202         struct socket *sock;
203         int result = 0;
204         unsigned char peek_buf[4];
205
206         result = -EIO; 
207         sock = server_sock(server);
208         if (!sock)
209                 goto out;
210         if (sock->sk->sk_state != TCP_ESTABLISHED)
211                 goto out;
212
213         if (!server->smb_read) {
214                 result = smb_get_length(sock, peek_buf);
215                 if (result < 0) {
216                         if (result == -ENODATA)
217                                 result = 0;
218                         goto out;
219                 }
220                 server->smb_len = result + 4;
221
222                 if (server->smb_len < SMB_HEADER_LEN) {
223                         PARANOIA("short packet: %d\n", result);
224                         server->rstate = SMB_RECV_DROP;
225                         result = -EIO;
226                         goto out;
227                 }
228                 if (server->smb_len > SMB_MAX_PACKET_SIZE) {
229                         PARANOIA("long packet: %d\n", result);
230                         server->rstate = SMB_RECV_DROP;
231                         result = -EIO;
232                         goto out;
233                 }
234         }
235
236         result = _recvfrom(sock, server->header + server->smb_read,
237                            SMB_HEADER_LEN - server->smb_read, 0);
238         VERBOSE("_recvfrom: %d\n", result);
239         if (result < 0) {
240                 VERBOSE("receive error: %d\n", result);
241                 goto out;
242         }
243         server->smb_read += result;
244
245         if (server->smb_read == SMB_HEADER_LEN)
246                 server->rstate = SMB_RECV_HCOMPLETE;
247 out:
248         return result;
249 }
250
251 static char drop_buffer[PAGE_SIZE];
252
253 /*
254  * smb_receive_drop - read and throw away the data
255  * Only called by the smbiod thread.
256  *
257  * FIXME: we are in the kernel, could we just tell the socket that we want
258  * to drop stuff from the buffer?
259  */
260 int
261 smb_receive_drop(struct smb_sb_info *server)
262 {
263         struct socket *sock;
264         unsigned int flags;
265         struct kvec iov;
266         struct msghdr msg;
267         int rlen = smb_len(server->header) - server->smb_read + 4;
268         int result = -EIO;
269
270         if (rlen > PAGE_SIZE)
271                 rlen = PAGE_SIZE;
272
273         sock = server_sock(server);
274         if (!sock)
275                 goto out;
276         if (sock->sk->sk_state != TCP_ESTABLISHED)
277                 goto out;
278
279         flags = MSG_DONTWAIT | MSG_NOSIGNAL;
280         iov.iov_base = drop_buffer;
281         iov.iov_len = PAGE_SIZE;
282         msg.msg_flags = flags;
283         msg.msg_name = NULL;
284         msg.msg_namelen = 0;
285         msg.msg_control = NULL;
286
287         result = kernel_recvmsg(sock, &msg, &iov, 1, rlen, flags);
288
289         VERBOSE("read: %d\n", result);
290         if (result < 0) {
291                 VERBOSE("receive error: %d\n", result);
292                 goto out;
293         }
294         server->smb_read += result;
295
296         if (server->smb_read >= server->smb_len)
297                 server->rstate = SMB_RECV_END;
298
299 out:
300         return result;
301 }
302
303 /*
304  * smb_receive
305  * Only called by the smbiod thread.
306  */
307 int
308 smb_receive(struct smb_sb_info *server, struct smb_request *req)
309 {
310         struct socket *sock;
311         unsigned int flags;
312         struct kvec iov[4];
313         struct kvec *p = req->rq_iov;
314         size_t num = req->rq_iovlen;
315         struct msghdr msg;
316         int rlen;
317         int result = -EIO;
318
319         sock = server_sock(server);
320         if (!sock)
321                 goto out;
322         if (sock->sk->sk_state != TCP_ESTABLISHED)
323                 goto out;
324
325         flags = MSG_DONTWAIT | MSG_NOSIGNAL;
326         msg.msg_flags = flags;
327         msg.msg_name = NULL;
328         msg.msg_namelen = 0;
329         msg.msg_control = NULL;
330
331         /* Dont repeat bytes and count available bufferspace */
332         rlen = min_t(int, smb_move_iov(&p, &num, iov, req->rq_bytes_recvd),
333                         (req->rq_rlen - req->rq_bytes_recvd));
334
335         result = kernel_recvmsg(sock, &msg, p, num, rlen, flags);
336
337         VERBOSE("read: %d\n", result);
338         if (result < 0) {
339                 VERBOSE("receive error: %d\n", result);
340                 goto out;
341         }
342         req->rq_bytes_recvd += result;
343         server->smb_read += result;
344
345 out:
346         return result;
347 }
348
349 /*
350  * Try to send a SMB request. This may return after sending only parts of the
351  * request. SMB_REQ_TRANSMITTED will be set if a request was fully sent.
352  *
353  * Parts of this was taken from xprt_sendmsg from net/sunrpc/xprt.c
354  */
355 int
356 smb_send_request(struct smb_request *req)
357 {
358         struct smb_sb_info *server = req->rq_server;
359         struct socket *sock;
360         struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT};
361         int slen = req->rq_slen - req->rq_bytes_sent;
362         int result = -EIO;
363         struct kvec iov[4];
364         struct kvec *p = req->rq_iov;
365         size_t num = req->rq_iovlen;
366
367         sock = server_sock(server);
368         if (!sock)
369                 goto out;
370         if (sock->sk->sk_state != TCP_ESTABLISHED)
371                 goto out;
372
373         /* Dont repeat bytes */
374         if (req->rq_bytes_sent)
375                 smb_move_iov(&p, &num, iov, req->rq_bytes_sent);
376
377         result = kernel_sendmsg(sock, &msg, p, num, slen);
378
379         if (result >= 0) {
380                 req->rq_bytes_sent += result;
381                 if (req->rq_bytes_sent >= req->rq_slen)
382                         req->rq_flags |= SMB_REQ_TRANSMITTED;
383         }
384 out:
385         return result;
386 }