Blame view

net/atm/common.c 21.4 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
  #include <linux/sched.h>
  #include <linux/time.h>		/* struct timeval */
  #include <linux/skbuff.h>
  #include <linux/bitops.h>
  #include <linux/init.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
20
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
  #include <net/sock.h>		/* struct sock */
a8147d737   Joe Perches   net/atm/common.c:...
22
23
  #include <linux/uaccess.h>
  #include <linux/poll.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24

60063497a   Arun Sharma   atomic: use <linu...
25
  #include <linux/atomic.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
27
28
29
30
31
  
  #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
32
  struct hlist_head vcc_hash[VCC_HTABLE_SIZE];
a8147d737   Joe Perches   net/atm/common.c:...
33
  EXPORT_SYMBOL(vcc_hash);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
  DEFINE_RWLOCK(vcc_sklist_lock);
a8147d737   Joe Perches   net/atm/common.c:...
35
  EXPORT_SYMBOL(vcc_sklist_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36

7313bb8f3   Karl Hiramoto   atm: propagate si...
37
  static ATOMIC_NOTIFIER_HEAD(atm_dev_notify_chain);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
40
  static void __vcc_insert_socket(struct sock *sk)
  {
  	struct atm_vcc *vcc = atm_sk(sk);
a8147d737   Joe Perches   net/atm/common.c:...
41
  	struct hlist_head *head = &vcc_hash[vcc->vci & (VCC_HTABLE_SIZE - 1)];
81c3d5470   Eric Dumazet   [INET]: speedup i...
42
  	sk->sk_hash = vcc->vci & (VCC_HTABLE_SIZE - 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
45
46
47
48
49
50
51
  	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:...
52
  EXPORT_SYMBOL(vcc_insert_socket);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
54
55
56
57
58
59
  
  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:...
60
  static struct sk_buff *alloc_tx(struct atm_vcc *vcc, unsigned int size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
62
63
  {
  	struct sk_buff *skb;
  	struct sock *sk = sk_atm(vcc);
81e2a3d5b   Eric Dumazet   atm: sk_wmem_allo...
64
  	if (sk_wmem_alloc_get(sk) && !atm_may_send(vcc, size)) {
522400623   Stephen Hemminger   [ATM]: Replace DP...
65
66
  		pr_debug("Sorry: wmem_alloc = %d, size = %d, sndbuf = %d
  ",
99824461e   Joe Perches   net/atm: Convert ...
67
  			 sk_wmem_alloc_get(sk), size, sk->sk_sndbuf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
  		return NULL;
  	}
81e2a3d5b   Eric Dumazet   atm: sk_wmem_allo...
70
71
  	while (!(skb = alloc_skb(size, GFP_KERNEL)))
  		schedule();
a8147d737   Joe Perches   net/atm/common.c:...
72
73
  	pr_debug("%d += %d
  ", sk_wmem_alloc_get(sk), skb->truesize);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
76
  	atomic_add(skb->truesize, &sk->sk_wmem_alloc);
  	return skb;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
79
  static void vcc_sock_destruct(struct sock *sk)
  {
  	if (atomic_read(&sk->sk_rmem_alloc))
a8147d737   Joe Perches   net/atm/common.c:...
80
81
82
  		printk(KERN_DEBUG "%s: rmem leakage (%d bytes) detected.
  ",
  		       __func__, atomic_read(&sk->sk_rmem_alloc));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
  
  	if (atomic_read(&sk->sk_wmem_alloc))
a8147d737   Joe Perches   net/atm/common.c:...
85
86
87
  		printk(KERN_DEBUG "%s: wmem leakage (%d bytes) detected.
  ",
  		       __func__, atomic_read(&sk->sk_wmem_alloc));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
89
90
91
  }
  
  static void vcc_def_wakeup(struct sock *sk)
  {
438154823   Eric Dumazet   net: sock_def_rea...
92
93
94
95
96
97
98
  	struct socket_wq *wq;
  
  	rcu_read_lock();
  	wq = rcu_dereference(sk->sk_wq);
  	if (wq_has_sleeper(wq))
  		wake_up(&wq->wait);
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
100
101
102
103
104
105
  }
  
  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...
106
  		atomic_read(&sk->sk_wmem_alloc)) <= sk->sk_sndbuf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
109
  }
  
  static void vcc_write_space(struct sock *sk)
f7d57453d   YOSHIFUJI Hideaki   [NET] ATM: Fix wh...
110
  {
438154823   Eric Dumazet   net: sock_def_rea...
111
112
113
  	struct socket_wq *wq;
  
  	rcu_read_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
115
  
  	if (vcc_writable(sk)) {
438154823   Eric Dumazet   net: sock_def_rea...
116
117
118
  		wq = rcu_dereference(sk->sk_wq);
  		if (wq_has_sleeper(wq))
  			wake_up_interruptible(&wq->wait);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119

8d8ad9d7c   Pavel Emelyanov   [NET]: Name magic...
120
  		sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
  	}
438154823   Eric Dumazet   net: sock_def_rea...
122
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
124
125
126
127
128
129
  }
  
  static struct proto vcc_proto = {
  	.name	  = "VCC",
  	.owner	  = THIS_MODULE,
  	.obj_size = sizeof(struct atm_vcc),
  };
f7d57453d   YOSHIFUJI Hideaki   [NET] ATM: Fix wh...
130

1b8d7ae42   Eric W. Biederman   [NET]: Make socke...
131
  int vcc_create(struct net *net, struct socket *sock, int protocol, int family)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
134
135
136
137
138
  {
  	struct sock *sk;
  	struct atm_vcc *vcc;
  
  	sock->sk = NULL;
  	if (sock->type == SOCK_STREAM)
  		return -EINVAL;
6257ff217   Pavel Emelyanov   [NET]: Forget the...
139
  	sk = sk_alloc(net, family, GFP_KERNEL, &vcc_proto);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
142
143
144
145
146
147
  	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:...
148
149
  	memset(&vcc->local, 0, sizeof(struct sockaddr_atmsvc));
  	memset(&vcc->remote, 0, sizeof(struct sockaddr_atmsvc));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
  	vcc->qos.txtp.max_sdu = 1 << 16; /* for meta VCs */
81e2a3d5b   Eric Dumazet   atm: sk_wmem_allo...
151
  	atomic_set(&sk->sk_wmem_alloc, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
153
154
155
156
157
158
159
160
  	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
161
162
163
164
165
166
167
168
169
170
171
172
  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
173
  		while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
a8147d737   Joe Perches   net/atm/common.c:...
174
  			atm_return(vcc, skb->truesize);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
177
178
179
180
  			kfree_skb(skb);
  		}
  
  		module_put(vcc->dev->ops->owner);
  		atm_dev_put(vcc->dev);
  	}
9301e320e   Chas Williams   [ATM]: track and ...
181
182
  
  	vcc_remove_socket(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
185
186
187
188
189
190
191
192
193
194
195
196
  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
197
198
199
200
201
202
203
204
205
206
  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
207
  EXPORT_SYMBOL(vcc_release_async);
4e55f5785   Jorge Boncompte [DTI2]   atm: Introduce vc...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
  void vcc_process_recv_queue(struct atm_vcc *vcc)
  {
  	struct sk_buff_head queue, *rq;
  	struct sk_buff *skb, *tmp;
  	unsigned long flags;
  
  	__skb_queue_head_init(&queue);
  	rq = &sk_atm(vcc)->sk_receive_queue;
  
  	spin_lock_irqsave(&rq->lock, flags);
  	skb_queue_splice_init(rq, &queue);
  	spin_unlock_irqrestore(&rq->lock, flags);
  
  	skb_queue_walk_safe(&queue, skb, tmp) {
  		__skb_unlink(skb, &queue);
  		vcc->push(vcc, skb);
  	}
  }
  EXPORT_SYMBOL(vcc_process_recv_queue);
7313bb8f3   Karl Hiramoto   atm: propagate si...
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
  void atm_dev_signal_change(struct atm_dev *dev, char signal)
  {
  	pr_debug("%s signal=%d dev=%p number=%d dev->signal=%d
  ",
  		__func__, signal, dev, dev->number, dev->signal);
  
  	/* atm driver sending invalid signal */
  	WARN_ON(signal < ATM_PHY_SIG_LOST || signal > ATM_PHY_SIG_FOUND);
  
  	if (dev->signal == signal)
  		return; /* no change */
  
  	dev->signal = signal;
  
  	atomic_notifier_call_chain(&atm_dev_notify_chain, signal, dev);
  }
  EXPORT_SYMBOL(atm_dev_signal_change);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244

64bf69ddf   Stanislaw Gruszka   [ATM]: deregistra...
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
  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);
  }
c031235b3   Philip A. Prindeville   atm/solos-pci: Do...
266
  EXPORT_SYMBOL(atm_dev_release_vccs);
64bf69ddf   Stanislaw Gruszka   [ATM]: deregistra...
267

a8147d737   Joe Perches   net/atm/common.c:...
268
  static int adjust_tp(struct atm_trafprm *tp, unsigned char aal)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
  {
  	int max_sdu;
a8147d737   Joe Perches   net/atm/common.c:...
271
272
  	if (!tp->traffic_class)
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
  	switch (aal) {
a8147d737   Joe Perches   net/atm/common.c:...
274
275
276
277
278
279
280
281
282
283
284
285
  	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
286
  	}
a8147d737   Joe Perches   net/atm/common.c:...
287
288
289
290
291
292
  	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
293
294
  	return 0;
  }
61c33e012   Mitchell Blank Jr   atm: use const wh...
295
  static int check_ci(const struct atm_vcc *vcc, short vpi, int vci)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
  {
a8147d737   Joe Perches   net/atm/common.c:...
297
  	struct hlist_head *head = &vcc_hash[vci & (VCC_HTABLE_SIZE - 1)];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
  	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...
320
  static int find_ci(const struct atm_vcc *vcc, short *vpi, int *vci)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
  {
  	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:...
357
358
  			if (p >= 1 << vcc->dev->ci_range.vpi_bits)
  				p = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
  		}
a8147d737   Joe Perches   net/atm/common.c:...
360
  	} while (old_p != p || old_c != c);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
362
  	return -EADDRINUSE;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
365
366
367
368
369
370
371
372
373
374
  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...
375
  	error = -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
  	if (!try_module_get(dev->ops->owner))
64bf69ddf   Stanislaw Gruszka   [ATM]: deregistra...
377
  		return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
379
  	vcc->dev = dev;
  	write_lock_irq(&vcc_sklist_lock);
f7d57453d   YOSHIFUJI Hideaki   [NET] ATM: Fix wh...
380
  	if (test_bit(ATM_DF_REMOVED, &dev->flags) ||
64bf69ddf   Stanislaw Gruszka   [ATM]: deregistra...
381
  	    (error = find_ci(vcc, &vpi, &vci))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
383
384
385
386
387
388
389
  		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:...
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
  	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
408
  	}
a8147d737   Joe Perches   net/atm/common.c:...
409
410
411
412
  	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
413
414
  	if (error)
  		goto fail;
99824461e   Joe Perches   net/atm: Convert ...
415
416
417
418
419
420
421
422
423
424
425
426
427
428
  	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
429
430
  
  	if (dev->ops->open) {
a8147d737   Joe Perches   net/atm/common.c:...
431
432
  		error = dev->ops->open(vcc);
  		if (error)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433
434
435
436
437
438
439
440
441
442
443
444
  			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
445
446
447
448
449
  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 ...
450
451
  	pr_debug("(vpi %d, vci %d)
  ", vpi, vci);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
452
453
454
455
456
457
458
459
  	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:...
460
  		clear_bit(ATM_VF_PARTIAL, &vcc->flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
461
  	else
a8147d737   Joe Perches   net/atm/common.c:...
462
  		if (test_bit(ATM_VF_PARTIAL, &vcc->flags))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
  			return -EINVAL;
99824461e   Joe Perches   net/atm: Convert ...
464
465
466
467
468
469
  	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:...
470
  		 vcc->qos.rxtp.max_pcr, vcc->qos.rxtp.max_sdu,
99824461e   Joe Perches   net/atm: Convert ...
471
472
473
  		 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
474
475
476
477
478
  	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...
479
  	if (likely(itf != ATM_ITF_ANY)) {
a8147d737   Joe Perches   net/atm/common.c:...
480
481
  		dev = try_then_request_module(atm_dev_lookup(itf),
  					      "atm-device-%d", itf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
  		dev = NULL;
57b47a53e   Ingo Molnar   [NET]: sem2mutex ...
484
  		mutex_lock(&atm_dev_mutex);
c9933d085   Mitchell Blank Jr   [ATM]: always ret...
485
  		if (!list_empty(&atm_devs)) {
a8147d737   Joe Perches   net/atm/common.c:...
486
487
  			dev = list_entry(atm_devs.next,
  					 struct atm_dev, dev_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
488
  			atm_dev_hold(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
  		}
57b47a53e   Ingo Molnar   [NET]: sem2mutex ...
490
  		mutex_unlock(&atm_dev_mutex);
c9933d085   Mitchell Blank Jr   [ATM]: always ret...
491
492
493
494
495
496
497
  	}
  	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
498
499
  	}
  	if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC)
a8147d737   Joe Perches   net/atm/common.c:...
500
501
  		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
502
503
504
  		sock->state = SS_CONNECTED;
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
506
507
508
509
510
511
512
513
514
  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;
40ba84993   Jorge Boncompte [DTI2]   atm: Allow MSG_PE...
515
516
517
  
  	/* only handle MSG_DONTWAIT and MSG_PEEK */
  	if (flags & ~(MSG_DONTWAIT | MSG_PEEK))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
  		return -EOPNOTSUPP;
40ba84993   Jorge Boncompte [DTI2]   atm: Allow MSG_PE...
519

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520
  	vcc = ATM_SD(sock);
a8147d737   Joe Perches   net/atm/common.c:...
521
522
  	if (test_bit(ATM_VF_RELEASED, &vcc->flags) ||
  	    test_bit(ATM_VF_CLOSE, &vcc->flags) ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
524
525
526
527
528
  	    !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...
529
  	copied = skb->len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530
  	if (copied > size) {
f7d57453d   YOSHIFUJI Hideaki   [NET] ATM: Fix wh...
531
  		copied = size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
532
533
  		msg->msg_flags |= MSG_TRUNC;
  	}
f7d57453d   YOSHIFUJI Hideaki   [NET] ATM: Fix wh...
534
535
536
  	error = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
  	if (error)
  		return error;
3b885787e   Neil Horman   net: Generalize s...
537
  	sock_recv_ts_and_drops(msg, sk, skb);
40ba84993   Jorge Boncompte [DTI2]   atm: Allow MSG_PE...
538
539
540
541
542
543
544
  
  	if (!(flags & MSG_PEEK)) {
  		pr_debug("%d -= %d
  ", atomic_read(&sk->sk_rmem_alloc),
  			 skb->truesize);
  		atm_return(vcc, skb->truesize);
  	}
f7d57453d   YOSHIFUJI Hideaki   [NET] ATM: Fix wh...
545
546
  	skb_free_datagram(sk, skb);
  	return copied;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
548
549
550
551
552
553
554
  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:...
555
  	int eff, error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
  	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...
590

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
591
  	eff = (size+3) & ~3; /* align to word boundary */
aa3951451   Eric Dumazet   net: sk_sleep() h...
592
  	prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
  	error = 0;
a8147d737   Joe Perches   net/atm/common.c:...
594
  	while (!(skb = alloc_tx(vcc, eff))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595
596
597
598
599
600
601
602
603
  		if (m->msg_flags & MSG_DONTWAIT) {
  			error = -EAGAIN;
  			break;
  		}
  		schedule();
  		if (signal_pending(current)) {
  			error = -ERESTARTSYS;
  			break;
  		}
a8147d737   Joe Perches   net/atm/common.c:...
604
605
606
  		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
607
608
609
610
  			error = -EPIPE;
  			send_sig(SIGPIPE, current, 0);
  			break;
  		}
aa3951451   Eric Dumazet   net: sk_sleep() h...
611
  		prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
  	}
aa3951451   Eric Dumazet   net: sk_sleep() h...
613
  	finish_wait(sk_sleep(sk), &wait);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
614
615
616
617
  	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:...
618
  	if (copy_from_user(skb_put(skb, size), buff, size)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619
620
621
622
  		kfree_skb(skb);
  		error = -EFAULT;
  		goto out;
  	}
a8147d737   Joe Perches   net/atm/common.c:...
623
624
625
  	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
626
627
628
629
630
  	error = error ? error : size;
  out:
  	release_sock(sk);
  	return error;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631
632
633
634
635
  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;
aa3951451   Eric Dumazet   net: sk_sleep() h...
636
  	sock_poll_wait(file, sk_sleep(sk), wait);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
  	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:...
664
  static int atm_change_qos(struct atm_vcc *vcc, struct atm_qos *qos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
665
666
667
668
669
670
671
672
673
674
675
  {
  	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:...
676
677
678
679
680
681
682
  	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
683
  	if (sk_atm(vcc)->sk_family == AF_ATMPVC)
a8147d737   Joe Perches   net/atm/common.c:...
684
685
  		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
686
  }
61c33e012   Mitchell Blank Jr   atm: use const wh...
687
  static int check_tp(const struct atm_trafprm *tp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688
689
  {
  	/* @@@ Should be merged with adjust_tp */
a8147d737   Joe Perches   net/atm/common.c:...
690
691
  	if (!tp->traffic_class || tp->traffic_class == ATM_ANYCLASS)
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
692
  	if (tp->traffic_class != ATM_UBR && !tp->min_pcr && !tp->pcr &&
a8147d737   Joe Perches   net/atm/common.c:...
693
694
695
696
  	    !tp->max_pcr)
  		return -EINVAL;
  	if (tp->min_pcr == ATM_MAX_PCR)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
697
  	if (tp->min_pcr && tp->max_pcr && tp->max_pcr != ATM_MAX_PCR &&
a8147d737   Joe Perches   net/atm/common.c:...
698
699
  	    tp->min_pcr > tp->max_pcr)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
700
701
702
703
704
705
  	/*
  	 * 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...
706
  static int check_qos(const struct atm_qos *qos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
707
708
709
710
  {
  	int error;
  
  	if (!qos->txtp.traffic_class && !qos->rxtp.traffic_class)
f7d57453d   YOSHIFUJI Hideaki   [NET] ATM: Fix wh...
711
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
712
713
714
  	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:...
715
716
  	    qos->rxtp.traffic_class != ATM_ANYCLASS)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
  	error = check_tp(&qos->txtp);
a8147d737   Joe Perches   net/atm/common.c:...
718
719
  	if (error)
  		return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
720
721
722
723
  	return check_tp(&qos->rxtp);
  }
  
  int vcc_setsockopt(struct socket *sock, int level, int optname,
b7058842c   David S. Miller   net: Make setsock...
724
  		   char __user *optval, unsigned int optlen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
725
726
727
728
729
730
731
732
733
734
  {
  	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:...
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
  	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
751
  	}
a8147d737   Joe Perches   net/atm/common.c:...
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
  	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
768
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
769
770
771
772
773
774
775
776
777
778
779
780
781
  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:...
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
  	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;
510a05edc   Julia Lawall   net/atm: Adjust c...
805
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
806
  	}
a8147d737   Joe Perches   net/atm/common.c:...
807
808
  	if (!vcc->dev || !vcc->dev->ops->getsockopt)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
809
810
  	return vcc->dev->ops->getsockopt(vcc, level, optname, optval, len);
  }
7313bb8f3   Karl Hiramoto   atm: propagate si...
811
812
813
814
815
816
817
818
819
820
821
  int register_atmdevice_notifier(struct notifier_block *nb)
  {
  	return atomic_notifier_chain_register(&atm_dev_notify_chain, nb);
  }
  EXPORT_SYMBOL_GPL(register_atmdevice_notifier);
  
  void unregister_atmdevice_notifier(struct notifier_block *nb)
  {
  	atomic_notifier_chain_unregister(&atm_dev_notify_chain, nb);
  }
  EXPORT_SYMBOL_GPL(unregister_atmdevice_notifier);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
822
823
824
  static int __init atm_init(void)
  {
  	int error;
a8147d737   Joe Perches   net/atm/common.c:...
825
826
  	error = proto_register(&vcc_proto, 0);
  	if (error < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
827
  		goto out;
a8147d737   Joe Perches   net/atm/common.c:...
828
829
  	error = atmpvc_init();
  	if (error < 0) {
99824461e   Joe Perches   net/atm: Convert ...
830
831
  		pr_err("atmpvc_init() failed with %d
  ", error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
832
833
  		goto out_unregister_vcc_proto;
  	}
a8147d737   Joe Perches   net/atm/common.c:...
834
835
  	error = atmsvc_init();
  	if (error < 0) {
99824461e   Joe Perches   net/atm: Convert ...
836
837
  		pr_err("atmsvc_init() failed with %d
  ", error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
838
839
  		goto out_atmpvc_exit;
  	}
a8147d737   Joe Perches   net/atm/common.c:...
840
841
  	error = atm_proc_init();
  	if (error < 0) {
99824461e   Joe Perches   net/atm: Convert ...
842
843
  		pr_err("atm_proc_init() failed with %d
  ", error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
844
845
  		goto out_atmsvc_exit;
  	}
a8147d737   Joe Perches   net/atm/common.c:...
846
847
  	error = atm_sysfs_init();
  	if (error < 0) {
99824461e   Joe Perches   net/atm: Convert ...
848
849
  		pr_err("atm_sysfs_init() failed with %d
  ", error);
656d98b09   Roman Kagan   [ATM]: basic sysf...
850
851
  		goto out_atmproc_exit;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
852
853
  out:
  	return error;
656d98b09   Roman Kagan   [ATM]: basic sysf...
854
855
  out_atmproc_exit:
  	atm_proc_exit();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
856
857
858
859
860
861
862
863
864
865
866
867
  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...
868
  	atm_sysfs_exit();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
869
870
871
872
  	atmsvc_exit();
  	atmpvc_exit();
  	proto_unregister(&vcc_proto);
  }
84ff602ef   Daniel Walker   [ATM]: Fix for cr...
873
  subsys_initcall(atm_init);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
874
875
876
877
878
  module_exit(atm_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_ALIAS_NETPROTO(PF_ATMPVC);
  MODULE_ALIAS_NETPROTO(PF_ATMSVC);