stream.c 5.09 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
8
9
10
11
/*
 *     SUCS NET3:
 *
 *     Generic stream handling routines. These are generic for most
 *     protocols. Even IP. Tonight 8-).
 *     This is used because TCP, LLC (others too) layer all have mostly
 *     identical sendmsg() and recvmsg() code.
 *     So we (will) share it here.
 *
 *     Authors:        Arnaldo Carvalho de Melo <acme@conectiva.com.br>
 *                     (from old tcp.c code)
12
 *                     Alan Cox <alan@lxorguk.ukuu.org.uk> (Borrowed comments 8-))
Linus Torvalds's avatar
Linus Torvalds committed
13
14
15
16
17
18
19
20
21
22
23
 */

#include <linux/module.h>
#include <linux/net.h>
#include <linux/signal.h>
#include <linux/tcp.h>
#include <linux/wait.h>
#include <net/sock.h>

/**
 * sk_stream_write_space - stream socket write_space callback.
24
 * @sk: socket
Linus Torvalds's avatar
Linus Torvalds committed
25
26
27
28
29
30
 *
 * FIXME: write proper description
 */
void sk_stream_write_space(struct sock *sk)
{
	struct socket *sock = sk->sk_socket;
31
	struct socket_wq *wq;
Linus Torvalds's avatar
Linus Torvalds committed
32

33
	if (sk_stream_is_writeable(sk) && sock) {
Linus Torvalds's avatar
Linus Torvalds committed
34
35
		clear_bit(SOCK_NOSPACE, &sock->flags);

36
37
38
39
		rcu_read_lock();
		wq = rcu_dereference(sk->sk_wq);
		if (wq_has_sleeper(wq))
			wake_up_interruptible_poll(&wq->wait, POLLOUT |
40
						POLLWRNORM | POLLWRBAND);
41
		if (wq && wq->fasync_list && !(sk->sk_shutdown & SEND_SHUTDOWN))
42
			sock_wake_async(sock, SOCK_WAKE_SPACE, POLL_OUT);
43
		rcu_read_unlock();
Linus Torvalds's avatar
Linus Torvalds committed
44
45
46
47
48
49
	}
}
EXPORT_SYMBOL(sk_stream_write_space);

/**
 * sk_stream_wait_connect - Wait for a socket to get into the connected state
50
51
 * @sk: sock to wait on
 * @timeo_p: for how long to wait
Linus Torvalds's avatar
Linus Torvalds committed
52
53
54
55
56
57
58
 *
 * Must be called with the socket locked.
 */
int sk_stream_wait_connect(struct sock *sk, long *timeo_p)
{
	struct task_struct *tsk = current;
	DEFINE_WAIT(wait);
59
	int done;
Linus Torvalds's avatar
Linus Torvalds committed
60

61
	do {
62
63
64
		int err = sock_error(sk);
		if (err)
			return err;
Linus Torvalds's avatar
Linus Torvalds committed
65
66
67
68
69
70
71
		if ((1 << sk->sk_state) & ~(TCPF_SYN_SENT | TCPF_SYN_RECV))
			return -EPIPE;
		if (!*timeo_p)
			return -EAGAIN;
		if (signal_pending(tsk))
			return sock_intr_errno(*timeo_p);

Eric Dumazet's avatar
Eric Dumazet committed
72
		prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
Linus Torvalds's avatar
Linus Torvalds committed
73
		sk->sk_write_pending++;
74
		done = sk_wait_event(sk, timeo_p,
75
				     !sk->sk_err &&
76
				     !((1 << sk->sk_state) &
77
				       ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)));
Eric Dumazet's avatar
Eric Dumazet committed
78
		finish_wait(sk_sleep(sk), &wait);
Linus Torvalds's avatar
Linus Torvalds committed
79
		sk->sk_write_pending--;
80
	} while (!done);
Linus Torvalds's avatar
Linus Torvalds committed
81
82
83
84
85
86
	return 0;
}
EXPORT_SYMBOL(sk_stream_wait_connect);

/**
 * sk_stream_closing - Return 1 if we still have things to send in our buffers.
87
 * @sk: socket to verify
Linus Torvalds's avatar
Linus Torvalds committed
88
89
90
91
92
93
94
95
96
97
98
99
100
 */
static inline int sk_stream_closing(struct sock *sk)
{
	return (1 << sk->sk_state) &
	       (TCPF_FIN_WAIT1 | TCPF_CLOSING | TCPF_LAST_ACK);
}

void sk_stream_wait_close(struct sock *sk, long timeout)
{
	if (timeout) {
		DEFINE_WAIT(wait);

		do {
Eric Dumazet's avatar
Eric Dumazet committed
101
			prepare_to_wait(sk_sleep(sk), &wait,
Linus Torvalds's avatar
Linus Torvalds committed
102
103
104
105
106
					TASK_INTERRUPTIBLE);
			if (sk_wait_event(sk, &timeout, !sk_stream_closing(sk)))
				break;
		} while (!signal_pending(current) && timeout);

Eric Dumazet's avatar
Eric Dumazet committed
107
		finish_wait(sk_sleep(sk), &wait);
Linus Torvalds's avatar
Linus Torvalds committed
108
109
110
111
112
113
	}
}
EXPORT_SYMBOL(sk_stream_wait_close);

/**
 * sk_stream_wait_memory - Wait for more memory for a socket
114
115
 * @sk: socket to wait for memory
 * @timeo_p: for how long
Linus Torvalds's avatar
Linus Torvalds committed
116
117
118
119
120
121
122
123
124
 */
int sk_stream_wait_memory(struct sock *sk, long *timeo_p)
{
	int err = 0;
	long vm_wait = 0;
	long current_timeo = *timeo_p;
	DEFINE_WAIT(wait);

	if (sk_stream_memory_free(sk))
125
		current_timeo = vm_wait = (prandom_u32() % (HZ / 5)) + 2;
Linus Torvalds's avatar
Linus Torvalds committed
126
127
128
129

	while (1) {
		set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);

Eric Dumazet's avatar
Eric Dumazet committed
130
		prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
Linus Torvalds's avatar
Linus Torvalds committed
131
132
133
134
135
136
137
138
139
140
141
142
143

		if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
			goto do_error;
		if (!*timeo_p)
			goto do_nonblock;
		if (signal_pending(current))
			goto do_interrupted;
		clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
		if (sk_stream_memory_free(sk) && !vm_wait)
			break;

		set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
		sk->sk_write_pending++;
144
145
146
147
		sk_wait_event(sk, &current_timeo, sk->sk_err ||
						  (sk->sk_shutdown & SEND_SHUTDOWN) ||
						  (sk_stream_memory_free(sk) &&
						  !vm_wait));
Linus Torvalds's avatar
Linus Torvalds committed
148
149
150
151
152
153
154
155
156
157
158
159
160
		sk->sk_write_pending--;

		if (vm_wait) {
			vm_wait -= current_timeo;
			current_timeo = *timeo_p;
			if (current_timeo != MAX_SCHEDULE_TIMEOUT &&
			    (current_timeo -= vm_wait) < 0)
				current_timeo = 0;
			vm_wait = 0;
		}
		*timeo_p = current_timeo;
	}
out:
Eric Dumazet's avatar
Eric Dumazet committed
161
	finish_wait(sk_sleep(sk), &wait);
Linus Torvalds's avatar
Linus Torvalds committed
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
	return err;

do_error:
	err = -EPIPE;
	goto out;
do_nonblock:
	err = -EAGAIN;
	goto out;
do_interrupted:
	err = sock_intr_errno(*timeo_p);
	goto out;
}
EXPORT_SYMBOL(sk_stream_wait_memory);

int sk_stream_error(struct sock *sk, int flags, int err)
{
	if (err == -EPIPE)
		err = sock_error(sk) ? : -EPIPE;
	if (err == -EPIPE && !(flags & MSG_NOSIGNAL))
		send_sig(SIGPIPE, current, 0);
	return err;
}
EXPORT_SYMBOL(sk_stream_error);

void sk_stream_kill_queues(struct sock *sk)
{
	/* First the read buffer. */
	__skb_queue_purge(&sk->sk_receive_queue);

	/* Next, the error queue. */
	__skb_queue_purge(&sk->sk_error_queue);

	/* Next, the write queue. */
195
	WARN_ON(!skb_queue_empty(&sk->sk_write_queue));
Linus Torvalds's avatar
Linus Torvalds committed
196
197

	/* Account for returned memory. */
198
	sk_mem_reclaim(sk);
Linus Torvalds's avatar
Linus Torvalds committed
199

200
201
	WARN_ON(sk->sk_wmem_queued);
	WARN_ON(sk->sk_forward_alloc);
Linus Torvalds's avatar
Linus Torvalds committed
202
203
204
205
206
207
208

	/* It is _impossible_ for the backlog to contain anything
	 * when we get here.  All user references to this socket
	 * have gone away, only the net layer knows can touch it.
	 */
}
EXPORT_SYMBOL(sk_stream_kill_queues);