Blame view

net/atm/common.c 19.9 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
  /* net/atm/common.c - ATM sockets (common part for PVC and SVC) */
  
  /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
99824461e   Joe Perches   net/atm: Convert ...
4
  #define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
7
8
9
10
11
12
13
  #include <linux/module.h>
  #include <linux/kmod.h>
  #include <linux/net.h>		/* struct socket, struct proto_ops */
  #include <linux/atm.h>		/* ATM stuff */
  #include <linux/atmdev.h>
  #include <linux/socket.h>	/* SOL_SOCKET */
  #include <linux/errno.h>	/* error codes */
  #include <linux/capability.h>
e49332bd1   Jesper Juhl   [PATCH] misc veri...
14
  #include <linux/mm.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
17
18
19
20
  #include <linux/sched.h>
  #include <linux/time.h>		/* struct timeval */
  #include <linux/skbuff.h>
  #include <linux/bitops.h>
  #include <linux/init.h>
  #include <net/sock.h>		/* struct sock */
a8147d737   Joe Perches   net/atm/common.c:...
21
22
  #include <linux/uaccess.h>
  #include <linux/poll.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
  #include <asm/atomic.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
27
28
29
30
  
  #include "resources.h"		/* atm_find_dev */
  #include "common.h"		/* prototypes */
  #include "protocols.h"		/* atm_init_<transport> */
  #include "addr.h"		/* address registry */
  #include "signaling.h"		/* for WAITING and sigd_attach */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
  struct hlist_head vcc_hash[VCC_HTABLE_SIZE];
a8147d737   Joe Perches   net/atm/common.c:...
32
  EXPORT_SYMBOL(vcc_hash);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
  DEFINE_RWLOCK(vcc_sklist_lock);
a8147d737   Joe Perches   net/atm/common.c:...
34
  EXPORT_SYMBOL(vcc_sklist_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
36
37
38
  
  static void __vcc_insert_socket(struct sock *sk)
  {
  	struct atm_vcc *vcc = atm_sk(sk);
a8147d737   Joe Perches   net/atm/common.c:...
39
  	struct hlist_head *head = &vcc_hash[vcc->vci & (VCC_HTABLE_SIZE - 1)];
81c3d5470   Eric Dumazet   [INET]: speedup i...
40
  	sk->sk_hash = vcc->vci & (VCC_HTABLE_SIZE - 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
43
44
45
46
47
48
49
  	sk_add_node(sk, head);
  }
  
  void vcc_insert_socket(struct sock *sk)
  {
  	write_lock_irq(&vcc_sklist_lock);
  	__vcc_insert_socket(sk);
  	write_unlock_irq(&vcc_sklist_lock);
  }
a8147d737   Joe Perches   net/atm/common.c:...
50
  EXPORT_SYMBOL(vcc_insert_socket);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
53
54
55
56
57
  
  static void vcc_remove_socket(struct sock *sk)
  {
  	write_lock_irq(&vcc_sklist_lock);
  	sk_del_node_init(sk);
  	write_unlock_irq(&vcc_sklist_lock);
  }
a8147d737   Joe Perches   net/atm/common.c:...
58
  static struct sk_buff *alloc_tx(struct atm_vcc *vcc, unsigned int size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
60
61
  {
  	struct sk_buff *skb;
  	struct sock *sk = sk_atm(vcc);
81e2a3d5b   Eric Dumazet   atm: sk_wmem_allo...
62
  	if (sk_wmem_alloc_get(sk) && !atm_may_send(vcc, size)) {
522400623   Stephen Hemminger   [ATM]: Replace DP...
63
64
  		pr_debug("Sorry: wmem_alloc = %d, size = %d, sndbuf = %d
  ",
99824461e   Joe Perches   net/atm: Convert ...
65
  			 sk_wmem_alloc_get(sk), size, sk->sk_sndbuf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
67
  		return NULL;
  	}
81e2a3d5b   Eric Dumazet   atm: sk_wmem_allo...
68
69
  	while (!(skb = alloc_skb(size, GFP_KERNEL)))
  		schedule();
a8147d737   Joe Perches   net/atm/common.c:...
70
71
  	pr_debug("%d += %d
  ", sk_wmem_alloc_get(sk), skb->truesize);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
73
74
  	atomic_add(skb->truesize, &sk->sk_wmem_alloc);
  	return skb;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
77
  static void vcc_sock_destruct(struct sock *sk)
  {
  	if (atomic_read(&sk->sk_rmem_alloc))
a8147d737   Joe Perches   net/atm/common.c:...
78
79
80
  		printk(KERN_DEBUG "%s: rmem leakage (%d bytes) detected.
  ",
  		       __func__, atomic_read(&sk->sk_rmem_alloc));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
82
  
  	if (atomic_read(&sk->sk_wmem_alloc))
a8147d737   Joe Perches   net/atm/common.c:...
83
84
85
  		printk(KERN_DEBUG "%s: wmem leakage (%d bytes) detected.
  ",
  		       __func__, atomic_read(&sk->sk_wmem_alloc));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
88
89
90
  }
  
  static void vcc_def_wakeup(struct sock *sk)
  {
  	read_lock(&sk->sk_callback_lock);
a57de0b43   Jiri Olsa   net: adding memor...
91
  	if (sk_has_sleeper(sk))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
93
94
95
96
97
98
99
100
  		wake_up(sk->sk_sleep);
  	read_unlock(&sk->sk_callback_lock);
  }
  
  static inline int vcc_writable(struct sock *sk)
  {
  	struct atm_vcc *vcc = atm_sk(sk);
  
  	return (vcc->qos.txtp.max_sdu +
f7d57453d   YOSHIFUJI Hideaki   [NET] ATM: Fix wh...
101
  		atomic_read(&sk->sk_wmem_alloc)) <= sk->sk_sndbuf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
103
104
  }
  
  static void vcc_write_space(struct sock *sk)
f7d57453d   YOSHIFUJI Hideaki   [NET] ATM: Fix wh...
105
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
107
108
  	read_lock(&sk->sk_callback_lock);
  
  	if (vcc_writable(sk)) {
a57de0b43   Jiri Olsa   net: adding memor...
109
  		if (sk_has_sleeper(sk))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
  			wake_up_interruptible(sk->sk_sleep);
8d8ad9d7c   Pavel Emelyanov   [NET]: Name magic...
111
  		sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
113
114
115
116
117
118
119
120
121
  	}
  
  	read_unlock(&sk->sk_callback_lock);
  }
  
  static struct proto vcc_proto = {
  	.name	  = "VCC",
  	.owner	  = THIS_MODULE,
  	.obj_size = sizeof(struct atm_vcc),
  };
f7d57453d   YOSHIFUJI Hideaki   [NET] ATM: Fix wh...
122

1b8d7ae42   Eric W. Biederman   [NET]: Make socke...
123
  int vcc_create(struct net *net, struct socket *sock, int protocol, int family)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
126
127
128
129
130
  {
  	struct sock *sk;
  	struct atm_vcc *vcc;
  
  	sock->sk = NULL;
  	if (sock->type == SOCK_STREAM)
  		return -EINVAL;
6257ff217   Pavel Emelyanov   [NET]: Forget the...
131
  	sk = sk_alloc(net, family, GFP_KERNEL, &vcc_proto);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
134
135
136
137
138
139
  	if (!sk)
  		return -ENOMEM;
  	sock_init_data(sock, sk);
  	sk->sk_state_change = vcc_def_wakeup;
  	sk->sk_write_space = vcc_write_space;
  
  	vcc = atm_sk(sk);
  	vcc->dev = NULL;
a8147d737   Joe Perches   net/atm/common.c:...
140
141
  	memset(&vcc->local, 0, sizeof(struct sockaddr_atmsvc));
  	memset(&vcc->remote, 0, sizeof(struct sockaddr_atmsvc));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
  	vcc->qos.txtp.max_sdu = 1 << 16; /* for meta VCs */
81e2a3d5b   Eric Dumazet   atm: sk_wmem_allo...
143
  	atomic_set(&sk->sk_wmem_alloc, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
145
146
147
148
149
150
151
152
  	atomic_set(&sk->sk_rmem_alloc, 0);
  	vcc->push = NULL;
  	vcc->pop = NULL;
  	vcc->push_oam = NULL;
  	vcc->vpi = vcc->vci = 0; /* no VCI/VPI yet */
  	vcc->atm_options = vcc->aal_options = 0;
  	sk->sk_destruct = vcc_sock_destruct;
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
155
156
157
158
159
160
161
162
163
164
  static void vcc_destroy_socket(struct sock *sk)
  {
  	struct atm_vcc *vcc = atm_sk(sk);
  	struct sk_buff *skb;
  
  	set_bit(ATM_VF_CLOSE, &vcc->flags);
  	clear_bit(ATM_VF_READY, &vcc->flags);
  	if (vcc->dev) {
  		if (vcc->dev->ops->close)
  			vcc->dev->ops->close(vcc);
  		if (vcc->push)
  			vcc->push(vcc, NULL); /* atmarpd has no push */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
  		while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
a8147d737   Joe Perches   net/atm/common.c:...
166
  			atm_return(vcc, skb->truesize);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
168
169
170
171
172
  			kfree_skb(skb);
  		}
  
  		module_put(vcc->dev->ops->owner);
  		atm_dev_put(vcc->dev);
  	}
9301e320e   Chas Williams   [ATM]: track and ...
173
174
  
  	vcc_remove_socket(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
177
178
179
180
181
182
183
184
185
186
187
188
  int vcc_release(struct socket *sock)
  {
  	struct sock *sk = sock->sk;
  
  	if (sk) {
  		lock_sock(sk);
  		vcc_destroy_socket(sock->sk);
  		release_sock(sk);
  		sock_put(sk);
  	}
  
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
190
191
192
193
194
195
196
197
198
  void vcc_release_async(struct atm_vcc *vcc, int reply)
  {
  	struct sock *sk = sk_atm(vcc);
  
  	set_bit(ATM_VF_CLOSE, &vcc->flags);
  	sk->sk_shutdown |= RCV_SHUTDOWN;
  	sk->sk_err = -reply;
  	clear_bit(ATM_VF_WAITING, &vcc->flags);
  	sk->sk_state_change(sk);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
  EXPORT_SYMBOL(vcc_release_async);
64bf69ddf   Stanislaw Gruszka   [ATM]: deregistra...
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
  void atm_dev_release_vccs(struct atm_dev *dev)
  {
  	int i;
  
  	write_lock_irq(&vcc_sklist_lock);
  	for (i = 0; i < VCC_HTABLE_SIZE; i++) {
  		struct hlist_head *head = &vcc_hash[i];
  		struct hlist_node *node, *tmp;
  		struct sock *s;
  		struct atm_vcc *vcc;
  
  		sk_for_each_safe(s, node, tmp, head) {
  			vcc = atm_sk(s);
  			if (vcc->dev == dev) {
  				vcc_release_async(vcc, -EPIPE);
  				sk_del_node_init(s);
  			}
  		}
  	}
  	write_unlock_irq(&vcc_sklist_lock);
  }
a8147d737   Joe Perches   net/atm/common.c:...
221
  static int adjust_tp(struct atm_trafprm *tp, unsigned char aal)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
  {
  	int max_sdu;
a8147d737   Joe Perches   net/atm/common.c:...
224
225
  	if (!tp->traffic_class)
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
  	switch (aal) {
a8147d737   Joe Perches   net/atm/common.c:...
227
228
229
230
231
232
233
234
235
236
237
238
  	case ATM_AAL0:
  		max_sdu = ATM_CELL_SIZE-1;
  		break;
  	case ATM_AAL34:
  		max_sdu = ATM_MAX_AAL34_PDU;
  		break;
  	default:
  		pr_warning("AAL problems ... (%d)
  ", aal);
  		/* fall through */
  	case ATM_AAL5:
  		max_sdu = ATM_MAX_AAL5_PDU;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
  	}
a8147d737   Joe Perches   net/atm/common.c:...
240
241
242
243
244
245
  	if (!tp->max_sdu)
  		tp->max_sdu = max_sdu;
  	else if (tp->max_sdu > max_sdu)
  		return -EINVAL;
  	if (!tp->max_cdv)
  		tp->max_cdv = ATM_MAX_CDV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
247
  	return 0;
  }
61c33e012   Mitchell Blank Jr   atm: use const wh...
248
  static int check_ci(const struct atm_vcc *vcc, short vpi, int vci)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
  {
a8147d737   Joe Perches   net/atm/common.c:...
250
  	struct hlist_head *head = &vcc_hash[vci & (VCC_HTABLE_SIZE - 1)];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
  	struct hlist_node *node;
  	struct sock *s;
  	struct atm_vcc *walk;
  
  	sk_for_each(s, node, head) {
  		walk = atm_sk(s);
  		if (walk->dev != vcc->dev)
  			continue;
  		if (test_bit(ATM_VF_ADDR, &walk->flags) && walk->vpi == vpi &&
  		    walk->vci == vci && ((walk->qos.txtp.traffic_class !=
  		    ATM_NONE && vcc->qos.txtp.traffic_class != ATM_NONE) ||
  		    (walk->qos.rxtp.traffic_class != ATM_NONE &&
  		    vcc->qos.rxtp.traffic_class != ATM_NONE)))
  			return -EADDRINUSE;
  	}
  
  	/* allow VCCs with same VPI/VCI iff they don't collide on
  	   TX/RX (but we may refuse such sharing for other reasons,
  	   e.g. if protocol requires to have both channels) */
  
  	return 0;
  }
61c33e012   Mitchell Blank Jr   atm: use const wh...
273
  static int find_ci(const struct atm_vcc *vcc, short *vpi, int *vci)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
  {
  	static short p;        /* poor man's per-device cache */
  	static int c;
  	short old_p;
  	int old_c;
  	int err;
  
  	if (*vpi != ATM_VPI_ANY && *vci != ATM_VCI_ANY) {
  		err = check_ci(vcc, *vpi, *vci);
  		return err;
  	}
  	/* last scan may have left values out of bounds for current device */
  	if (*vpi != ATM_VPI_ANY)
  		p = *vpi;
  	else if (p >= 1 << vcc->dev->ci_range.vpi_bits)
  		p = 0;
  	if (*vci != ATM_VCI_ANY)
  		c = *vci;
  	else if (c < ATM_NOT_RSV_VCI || c >= 1 << vcc->dev->ci_range.vci_bits)
  			c = ATM_NOT_RSV_VCI;
  	old_p = p;
  	old_c = c;
  	do {
  		if (!check_ci(vcc, p, c)) {
  			*vpi = p;
  			*vci = c;
  			return 0;
  		}
  		if (*vci == ATM_VCI_ANY) {
  			c++;
  			if (c >= 1 << vcc->dev->ci_range.vci_bits)
  				c = ATM_NOT_RSV_VCI;
  		}
  		if ((c == ATM_NOT_RSV_VCI || *vci != ATM_VCI_ANY) &&
  		    *vpi == ATM_VPI_ANY) {
  			p++;
a8147d737   Joe Perches   net/atm/common.c:...
310
311
  			if (p >= 1 << vcc->dev->ci_range.vpi_bits)
  				p = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
  		}
a8147d737   Joe Perches   net/atm/common.c:...
313
  	} while (old_p != p || old_c != c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
315
  	return -EADDRINUSE;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
317
318
319
320
321
322
323
324
325
326
327
  static int __vcc_connect(struct atm_vcc *vcc, struct atm_dev *dev, short vpi,
  			 int vci)
  {
  	struct sock *sk = sk_atm(vcc);
  	int error;
  
  	if ((vpi != ATM_VPI_UNSPEC && vpi != ATM_VPI_ANY &&
  	    vpi >> dev->ci_range.vpi_bits) || (vci != ATM_VCI_UNSPEC &&
  	    vci != ATM_VCI_ANY && vci >> dev->ci_range.vci_bits))
  		return -EINVAL;
  	if (vci > 0 && vci < ATM_NOT_RSV_VCI && !capable(CAP_NET_BIND_SERVICE))
  		return -EPERM;
64bf69ddf   Stanislaw Gruszka   [ATM]: deregistra...
328
  	error = -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
  	if (!try_module_get(dev->ops->owner))
64bf69ddf   Stanislaw Gruszka   [ATM]: deregistra...
330
  		return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
332
  	vcc->dev = dev;
  	write_lock_irq(&vcc_sklist_lock);
f7d57453d   YOSHIFUJI Hideaki   [NET] ATM: Fix wh...
333
  	if (test_bit(ATM_DF_REMOVED, &dev->flags) ||
64bf69ddf   Stanislaw Gruszka   [ATM]: deregistra...
334
  	    (error = find_ci(vcc, &vpi, &vci))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
338
339
340
341
342
  		write_unlock_irq(&vcc_sklist_lock);
  		goto fail_module_put;
  	}
  	vcc->vpi = vpi;
  	vcc->vci = vci;
  	__vcc_insert_socket(sk);
  	write_unlock_irq(&vcc_sklist_lock);
  	switch (vcc->qos.aal) {
a8147d737   Joe Perches   net/atm/common.c:...
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
  	case ATM_AAL0:
  		error = atm_init_aal0(vcc);
  		vcc->stats = &dev->stats.aal0;
  		break;
  	case ATM_AAL34:
  		error = atm_init_aal34(vcc);
  		vcc->stats = &dev->stats.aal34;
  		break;
  	case ATM_NO_AAL:
  		/* ATM_AAL5 is also used in the "0 for default" case */
  		vcc->qos.aal = ATM_AAL5;
  		/* fall through */
  	case ATM_AAL5:
  		error = atm_init_aal5(vcc);
  		vcc->stats = &dev->stats.aal5;
  		break;
  	default:
  		error = -EPROTOTYPE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
  	}
a8147d737   Joe Perches   net/atm/common.c:...
362
363
364
365
  	if (!error)
  		error = adjust_tp(&vcc->qos.txtp, vcc->qos.aal);
  	if (!error)
  		error = adjust_tp(&vcc->qos.rxtp, vcc->qos.aal);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
367
  	if (error)
  		goto fail;
99824461e   Joe Perches   net/atm: Convert ...
368
369
370
371
372
373
374
375
376
377
378
379
380
381
  	pr_debug("VCC %d.%d, AAL %d
  ", vpi, vci, vcc->qos.aal);
  	pr_debug("  TX: %d, PCR %d..%d, SDU %d
  ",
  		 vcc->qos.txtp.traffic_class,
  		 vcc->qos.txtp.min_pcr,
  		 vcc->qos.txtp.max_pcr,
  		 vcc->qos.txtp.max_sdu);
  	pr_debug("  RX: %d, PCR %d..%d, SDU %d
  ",
  		 vcc->qos.rxtp.traffic_class,
  		 vcc->qos.rxtp.min_pcr,
  		 vcc->qos.rxtp.max_pcr,
  		 vcc->qos.rxtp.max_sdu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
383
  
  	if (dev->ops->open) {
a8147d737   Joe Perches   net/atm/common.c:...
384
385
  		error = dev->ops->open(vcc);
  		if (error)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
387
388
389
390
391
392
393
394
395
396
397
  			goto fail;
  	}
  	return 0;
  
  fail:
  	vcc_remove_socket(sk);
  fail_module_put:
  	module_put(dev->ops->owner);
  	/* ensure we get dev module ref count correct */
  	vcc->dev = NULL;
  	return error;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
399
400
401
402
  int vcc_connect(struct socket *sock, int itf, short vpi, int vci)
  {
  	struct atm_dev *dev;
  	struct atm_vcc *vcc = ATM_SD(sock);
  	int error;
99824461e   Joe Perches   net/atm: Convert ...
403
404
  	pr_debug("(vpi %d, vci %d)
  ", vpi, vci);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
406
407
408
409
410
411
412
  	if (sock->state == SS_CONNECTED)
  		return -EISCONN;
  	if (sock->state != SS_UNCONNECTED)
  		return -EINVAL;
  	if (!(vpi || vci))
  		return -EINVAL;
  
  	if (vpi != ATM_VPI_UNSPEC && vci != ATM_VCI_UNSPEC)
a8147d737   Joe Perches   net/atm/common.c:...
413
  		clear_bit(ATM_VF_PARTIAL, &vcc->flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
  	else
a8147d737   Joe Perches   net/atm/common.c:...
415
  		if (test_bit(ATM_VF_PARTIAL, &vcc->flags))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
  			return -EINVAL;
99824461e   Joe Perches   net/atm: Convert ...
417
418
419
420
421
422
  	pr_debug("(TX: cl %d,bw %d-%d,sdu %d; "
  		 "RX: cl %d,bw %d-%d,sdu %d,AAL %s%d)
  ",
  		 vcc->qos.txtp.traffic_class, vcc->qos.txtp.min_pcr,
  		 vcc->qos.txtp.max_pcr, vcc->qos.txtp.max_sdu,
  		 vcc->qos.rxtp.traffic_class, vcc->qos.rxtp.min_pcr,
a8147d737   Joe Perches   net/atm/common.c:...
423
  		 vcc->qos.rxtp.max_pcr, vcc->qos.rxtp.max_sdu,
99824461e   Joe Perches   net/atm: Convert ...
424
425
426
  		 vcc->qos.aal == ATM_AAL5 ? "" :
  		 vcc->qos.aal == ATM_AAL0 ? "" : " ??? code ",
  		 vcc->qos.aal == ATM_AAL0 ? 0 : vcc->qos.aal);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
428
429
430
431
  	if (!test_bit(ATM_VF_HASQOS, &vcc->flags))
  		return -EBADFD;
  	if (vcc->qos.txtp.traffic_class == ATM_ANYCLASS ||
  	    vcc->qos.rxtp.traffic_class == ATM_ANYCLASS)
  		return -EINVAL;
c9933d085   Mitchell Blank Jr   [ATM]: always ret...
432
  	if (likely(itf != ATM_ITF_ANY)) {
a8147d737   Joe Perches   net/atm/common.c:...
433
434
  		dev = try_then_request_module(atm_dev_lookup(itf),
  					      "atm-device-%d", itf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
436
  		dev = NULL;
57b47a53e   Ingo Molnar   [NET]: sem2mutex ...
437
  		mutex_lock(&atm_dev_mutex);
c9933d085   Mitchell Blank Jr   [ATM]: always ret...
438
  		if (!list_empty(&atm_devs)) {
a8147d737   Joe Perches   net/atm/common.c:...
439
440
  			dev = list_entry(atm_devs.next,
  					 struct atm_dev, dev_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
441
  			atm_dev_hold(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
  		}
57b47a53e   Ingo Molnar   [NET]: sem2mutex ...
443
  		mutex_unlock(&atm_dev_mutex);
c9933d085   Mitchell Blank Jr   [ATM]: always ret...
444
445
446
447
448
449
450
  	}
  	if (!dev)
  		return -ENODEV;
  	error = __vcc_connect(vcc, dev, vpi, vci);
  	if (error) {
  		atm_dev_put(dev);
  		return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
452
  	}
  	if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC)
a8147d737   Joe Perches   net/atm/common.c:...
453
454
  		set_bit(ATM_VF_PARTIAL, &vcc->flags);
  	if (test_bit(ATM_VF_READY, &ATM_SD(sock)->flags))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
455
456
457
  		sock->state = SS_CONNECTED;
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
459
460
461
462
463
464
465
466
467
468
469
470
  int vcc_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
  		size_t size, int flags)
  {
  	struct sock *sk = sock->sk;
  	struct atm_vcc *vcc;
  	struct sk_buff *skb;
  	int copied, error = -EINVAL;
  
  	if (sock->state != SS_CONNECTED)
  		return -ENOTCONN;
  	if (flags & ~MSG_DONTWAIT)		/* only handle MSG_DONTWAIT */
  		return -EOPNOTSUPP;
  	vcc = ATM_SD(sock);
a8147d737   Joe Perches   net/atm/common.c:...
471
472
  	if (test_bit(ATM_VF_RELEASED, &vcc->flags) ||
  	    test_bit(ATM_VF_CLOSE, &vcc->flags) ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
474
475
476
477
478
  	    !test_bit(ATM_VF_READY, &vcc->flags))
  		return 0;
  
  	skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &error);
  	if (!skb)
  		return error;
f7d57453d   YOSHIFUJI Hideaki   [NET] ATM: Fix wh...
479
  	copied = skb->len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480
  	if (copied > size) {
f7d57453d   YOSHIFUJI Hideaki   [NET] ATM: Fix wh...
481
  		copied = size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482
483
  		msg->msg_flags |= MSG_TRUNC;
  	}
f7d57453d   YOSHIFUJI Hideaki   [NET] ATM: Fix wh...
484
485
486
  	error = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
  	if (error)
  		return error;
3b885787e   Neil Horman   net: Generalize s...
487
  	sock_recv_ts_and_drops(msg, sk, skb);
99824461e   Joe Perches   net/atm: Convert ...
488
489
  	pr_debug("%d -= %d
  ", atomic_read(&sk->sk_rmem_alloc), skb->truesize);
f7d57453d   YOSHIFUJI Hideaki   [NET] ATM: Fix wh...
490
491
492
  	atm_return(vcc, skb->truesize);
  	skb_free_datagram(sk, skb);
  	return copied;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
493
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494
495
496
497
498
499
500
  int vcc_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m,
  		size_t total_len)
  {
  	struct sock *sk = sock->sk;
  	DEFINE_WAIT(wait);
  	struct atm_vcc *vcc;
  	struct sk_buff *skb;
a8147d737   Joe Perches   net/atm/common.c:...
501
  	int eff, error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
  	const void __user *buff;
  	int size;
  
  	lock_sock(sk);
  	if (sock->state != SS_CONNECTED) {
  		error = -ENOTCONN;
  		goto out;
  	}
  	if (m->msg_name) {
  		error = -EISCONN;
  		goto out;
  	}
  	if (m->msg_iovlen != 1) {
  		error = -ENOSYS; /* fix this later @@@ */
  		goto out;
  	}
  	buff = m->msg_iov->iov_base;
  	size = m->msg_iov->iov_len;
  	vcc = ATM_SD(sock);
  	if (test_bit(ATM_VF_RELEASED, &vcc->flags) ||
  	    test_bit(ATM_VF_CLOSE, &vcc->flags) ||
  	    !test_bit(ATM_VF_READY, &vcc->flags)) {
  		error = -EPIPE;
  		send_sig(SIGPIPE, current, 0);
  		goto out;
  	}
  	if (!size) {
  		error = 0;
  		goto out;
  	}
  	if (size < 0 || size > vcc->qos.txtp.max_sdu) {
  		error = -EMSGSIZE;
  		goto out;
  	}
e49332bd1   Jesper Juhl   [PATCH] misc veri...
536

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537
538
539
  	eff = (size+3) & ~3; /* align to word boundary */
  	prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
  	error = 0;
a8147d737   Joe Perches   net/atm/common.c:...
540
  	while (!(skb = alloc_tx(vcc, eff))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541
542
543
544
545
546
547
548
549
  		if (m->msg_flags & MSG_DONTWAIT) {
  			error = -EAGAIN;
  			break;
  		}
  		schedule();
  		if (signal_pending(current)) {
  			error = -ERESTARTSYS;
  			break;
  		}
a8147d737   Joe Perches   net/atm/common.c:...
550
551
552
  		if (test_bit(ATM_VF_RELEASED, &vcc->flags) ||
  		    test_bit(ATM_VF_CLOSE, &vcc->flags) ||
  		    !test_bit(ATM_VF_READY, &vcc->flags)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
554
555
556
557
558
559
560
561
562
563
  			error = -EPIPE;
  			send_sig(SIGPIPE, current, 0);
  			break;
  		}
  		prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
  	}
  	finish_wait(sk->sk_sleep, &wait);
  	if (error)
  		goto out;
  	skb->dev = NULL; /* for paths shared with net_device interfaces */
  	ATM_SKB(skb)->atm_options = vcc->atm_options;
a8147d737   Joe Perches   net/atm/common.c:...
564
  	if (copy_from_user(skb_put(skb, size), buff, size)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
565
566
567
568
  		kfree_skb(skb);
  		error = -EFAULT;
  		goto out;
  	}
a8147d737   Joe Perches   net/atm/common.c:...
569
570
571
  	if (eff != size)
  		memset(skb->data + size, 0, eff-size);
  	error = vcc->dev->ops->send(vcc, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
572
573
574
575
576
  	error = error ? error : size;
  out:
  	release_sock(sk);
  	return error;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
577
578
579
580
581
  unsigned int vcc_poll(struct file *file, struct socket *sock, poll_table *wait)
  {
  	struct sock *sk = sock->sk;
  	struct atm_vcc *vcc;
  	unsigned int mask;
a57de0b43   Jiri Olsa   net: adding memor...
582
  	sock_poll_wait(file, sk->sk_sleep, wait);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
  	mask = 0;
  
  	vcc = ATM_SD(sock);
  
  	/* exceptional events */
  	if (sk->sk_err)
  		mask = POLLERR;
  
  	if (test_bit(ATM_VF_RELEASED, &vcc->flags) ||
  	    test_bit(ATM_VF_CLOSE, &vcc->flags))
  		mask |= POLLHUP;
  
  	/* readable? */
  	if (!skb_queue_empty(&sk->sk_receive_queue))
  		mask |= POLLIN | POLLRDNORM;
  
  	/* writable? */
  	if (sock->state == SS_CONNECTING &&
  	    test_bit(ATM_VF_WAITING, &vcc->flags))
  		return mask;
  
  	if (vcc->qos.txtp.traffic_class != ATM_NONE &&
  	    vcc_writable(sk))
  		mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
  
  	return mask;
  }
a8147d737   Joe Perches   net/atm/common.c:...
610
  static int atm_change_qos(struct atm_vcc *vcc, struct atm_qos *qos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
612
613
614
615
616
617
618
619
620
621
  {
  	int error;
  
  	/*
  	 * Don't let the QoS change the already connected AAL type nor the
  	 * traffic class.
  	 */
  	if (qos->aal != vcc->qos.aal ||
  	    qos->rxtp.traffic_class != vcc->qos.rxtp.traffic_class ||
  	    qos->txtp.traffic_class != vcc->qos.txtp.traffic_class)
  		return -EINVAL;
a8147d737   Joe Perches   net/atm/common.c:...
622
623
624
625
626
627
628
  	error = adjust_tp(&qos->txtp, qos->aal);
  	if (!error)
  		error = adjust_tp(&qos->rxtp, qos->aal);
  	if (error)
  		return error;
  	if (!vcc->dev->ops->change_qos)
  		return -EOPNOTSUPP;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
629
  	if (sk_atm(vcc)->sk_family == AF_ATMPVC)
a8147d737   Joe Perches   net/atm/common.c:...
630
631
  		return vcc->dev->ops->change_qos(vcc, qos, ATM_MF_SET);
  	return svc_change_qos(vcc, qos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
632
  }
61c33e012   Mitchell Blank Jr   atm: use const wh...
633
  static int check_tp(const struct atm_trafprm *tp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
634
635
  {
  	/* @@@ Should be merged with adjust_tp */
a8147d737   Joe Perches   net/atm/common.c:...
636
637
  	if (!tp->traffic_class || tp->traffic_class == ATM_ANYCLASS)
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
  	if (tp->traffic_class != ATM_UBR && !tp->min_pcr && !tp->pcr &&
a8147d737   Joe Perches   net/atm/common.c:...
639
640
641
642
  	    !tp->max_pcr)
  		return -EINVAL;
  	if (tp->min_pcr == ATM_MAX_PCR)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
643
  	if (tp->min_pcr && tp->max_pcr && tp->max_pcr != ATM_MAX_PCR &&
a8147d737   Joe Perches   net/atm/common.c:...
644
645
  	    tp->min_pcr > tp->max_pcr)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
646
647
648
649
650
651
  	/*
  	 * We allow pcr to be outside [min_pcr,max_pcr], because later
  	 * adjustment may still push it in the valid range.
  	 */
  	return 0;
  }
61c33e012   Mitchell Blank Jr   atm: use const wh...
652
  static int check_qos(const struct atm_qos *qos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
653
654
655
656
  {
  	int error;
  
  	if (!qos->txtp.traffic_class && !qos->rxtp.traffic_class)
f7d57453d   YOSHIFUJI Hideaki   [NET] ATM: Fix wh...
657
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658
659
660
  	if (qos->txtp.traffic_class != qos->rxtp.traffic_class &&
  	    qos->txtp.traffic_class && qos->rxtp.traffic_class &&
  	    qos->txtp.traffic_class != ATM_ANYCLASS &&
a8147d737   Joe Perches   net/atm/common.c:...
661
662
  	    qos->rxtp.traffic_class != ATM_ANYCLASS)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
663
  	error = check_tp(&qos->txtp);
a8147d737   Joe Perches   net/atm/common.c:...
664
665
  	if (error)
  		return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
666
667
668
669
  	return check_tp(&qos->rxtp);
  }
  
  int vcc_setsockopt(struct socket *sock, int level, int optname,
b7058842c   David S. Miller   net: Make setsock...
670
  		   char __user *optval, unsigned int optlen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
671
672
673
674
675
676
677
678
679
680
  {
  	struct atm_vcc *vcc;
  	unsigned long value;
  	int error;
  
  	if (__SO_LEVEL_MATCH(optname, level) && optlen != __SO_SIZE(optname))
  		return -EINVAL;
  
  	vcc = ATM_SD(sock);
  	switch (optname) {
a8147d737   Joe Perches   net/atm/common.c:...
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
  	case SO_ATMQOS:
  	{
  		struct atm_qos qos;
  
  		if (copy_from_user(&qos, optval, sizeof(qos)))
  			return -EFAULT;
  		error = check_qos(&qos);
  		if (error)
  			return error;
  		if (sock->state == SS_CONNECTED)
  			return atm_change_qos(vcc, &qos);
  		if (sock->state != SS_UNCONNECTED)
  			return -EBADFD;
  		vcc->qos = qos;
  		set_bit(ATM_VF_HASQOS, &vcc->flags);
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
697
  	}
a8147d737   Joe Perches   net/atm/common.c:...
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
  	case SO_SETCLP:
  		if (get_user(value, (unsigned long __user *)optval))
  			return -EFAULT;
  		if (value)
  			vcc->atm_options |= ATM_ATMOPT_CLP;
  		else
  			vcc->atm_options &= ~ATM_ATMOPT_CLP;
  		return 0;
  	default:
  		if (level == SOL_SOCKET)
  			return -EINVAL;
  		break;
  	}
  	if (!vcc->dev || !vcc->dev->ops->setsockopt)
  		return -EINVAL;
  	return vcc->dev->ops->setsockopt(vcc, level, optname, optval, optlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
714
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
715
716
717
718
719
720
721
722
723
724
725
726
727
  int vcc_getsockopt(struct socket *sock, int level, int optname,
  		   char __user *optval, int __user *optlen)
  {
  	struct atm_vcc *vcc;
  	int len;
  
  	if (get_user(len, optlen))
  		return -EFAULT;
  	if (__SO_LEVEL_MATCH(optname, level) && len != __SO_SIZE(optname))
  		return -EINVAL;
  
  	vcc = ATM_SD(sock);
  	switch (optname) {
a8147d737   Joe Perches   net/atm/common.c:...
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
  	case SO_ATMQOS:
  		if (!test_bit(ATM_VF_HASQOS, &vcc->flags))
  			return -EINVAL;
  		return copy_to_user(optval, &vcc->qos, sizeof(vcc->qos))
  			? -EFAULT : 0;
  	case SO_SETCLP:
  		return put_user(vcc->atm_options & ATM_ATMOPT_CLP ? 1 : 0,
  				(unsigned long __user *)optval) ? -EFAULT : 0;
  	case SO_ATMPVC:
  	{
  		struct sockaddr_atmpvc pvc;
  
  		if (!vcc->dev || !test_bit(ATM_VF_ADDR, &vcc->flags))
  			return -ENOTCONN;
  		pvc.sap_family = AF_ATMPVC;
  		pvc.sap_addr.itf = vcc->dev->number;
  		pvc.sap_addr.vpi = vcc->vpi;
  		pvc.sap_addr.vci = vcc->vci;
  		return copy_to_user(optval, &pvc, sizeof(pvc)) ? -EFAULT : 0;
  	}
  	default:
  		if (level == SOL_SOCKET)
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
751
752
  			break;
  	}
a8147d737   Joe Perches   net/atm/common.c:...
753
754
  	if (!vcc->dev || !vcc->dev->ops->getsockopt)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
755
756
757
758
759
760
  	return vcc->dev->ops->getsockopt(vcc, level, optname, optval, len);
  }
  
  static int __init atm_init(void)
  {
  	int error;
a8147d737   Joe Perches   net/atm/common.c:...
761
762
  	error = proto_register(&vcc_proto, 0);
  	if (error < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
763
  		goto out;
a8147d737   Joe Perches   net/atm/common.c:...
764
765
  	error = atmpvc_init();
  	if (error < 0) {
99824461e   Joe Perches   net/atm: Convert ...
766
767
  		pr_err("atmpvc_init() failed with %d
  ", error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
768
769
  		goto out_unregister_vcc_proto;
  	}
a8147d737   Joe Perches   net/atm/common.c:...
770
771
  	error = atmsvc_init();
  	if (error < 0) {
99824461e   Joe Perches   net/atm: Convert ...
772
773
  		pr_err("atmsvc_init() failed with %d
  ", error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
774
775
  		goto out_atmpvc_exit;
  	}
a8147d737   Joe Perches   net/atm/common.c:...
776
777
  	error = atm_proc_init();
  	if (error < 0) {
99824461e   Joe Perches   net/atm: Convert ...
778
779
  		pr_err("atm_proc_init() failed with %d
  ", error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
780
781
  		goto out_atmsvc_exit;
  	}
a8147d737   Joe Perches   net/atm/common.c:...
782
783
  	error = atm_sysfs_init();
  	if (error < 0) {
99824461e   Joe Perches   net/atm: Convert ...
784
785
  		pr_err("atm_sysfs_init() failed with %d
  ", error);
656d98b09   Roman Kagan   [ATM]: basic sysf...
786
787
  		goto out_atmproc_exit;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788
789
  out:
  	return error;
656d98b09   Roman Kagan   [ATM]: basic sysf...
790
791
  out_atmproc_exit:
  	atm_proc_exit();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
792
793
794
795
796
797
798
799
800
801
802
803
  out_atmsvc_exit:
  	atmsvc_exit();
  out_atmpvc_exit:
  	atmsvc_exit();
  out_unregister_vcc_proto:
  	proto_unregister(&vcc_proto);
  	goto out;
  }
  
  static void __exit atm_exit(void)
  {
  	atm_proc_exit();
656d98b09   Roman Kagan   [ATM]: basic sysf...
804
  	atm_sysfs_exit();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
805
806
807
808
  	atmsvc_exit();
  	atmpvc_exit();
  	proto_unregister(&vcc_proto);
  }
84ff602ef   Daniel Walker   [ATM]: Fix for cr...
809
  subsys_initcall(atm_init);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
810
811
812
813
814
  module_exit(atm_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_ALIAS_NETPROTO(PF_ATMPVC);
  MODULE_ALIAS_NETPROTO(PF_ATMSVC);