• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/net/atm/
1/* net/atm/resources.c - Statically allocated resources */
2
3/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
4
5/* Fixes
6 * Arnaldo Carvalho de Melo <acme@conectiva.com.br>
7 * 2002/01 - don't free the whole struct sock on sk->destruct time,
8 * 	     use the default destruct function initialized by sock_init_data */
9
10#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
11
12#include <linux/ctype.h>
13#include <linux/string.h>
14#include <linux/atmdev.h>
15#include <linux/sonet.h>
16#include <linux/kernel.h> /* for barrier */
17#include <linux/module.h>
18#include <linux/bitops.h>
19#include <linux/capability.h>
20#include <linux/delay.h>
21#include <linux/mutex.h>
22#include <linux/slab.h>
23
24#include <net/sock.h>	 /* for struct sock */
25
26#include "common.h"
27#include "resources.h"
28#include "addr.h"
29
30
31LIST_HEAD(atm_devs);
32DEFINE_MUTEX(atm_dev_mutex);
33
34static struct atm_dev *__alloc_atm_dev(const char *type)
35{
36	struct atm_dev *dev;
37
38	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
39	if (!dev)
40		return NULL;
41	dev->type = type;
42	dev->signal = ATM_PHY_SIG_UNKNOWN;
43	dev->link_rate = ATM_OC3_PCR;
44	spin_lock_init(&dev->lock);
45	INIT_LIST_HEAD(&dev->local);
46	INIT_LIST_HEAD(&dev->lecs);
47
48	return dev;
49}
50
51static struct atm_dev *__atm_dev_lookup(int number)
52{
53	struct atm_dev *dev;
54	struct list_head *p;
55
56	list_for_each(p, &atm_devs) {
57		dev = list_entry(p, struct atm_dev, dev_list);
58		if (dev->number == number) {
59			atm_dev_hold(dev);
60			return dev;
61		}
62	}
63	return NULL;
64}
65
66struct atm_dev *atm_dev_lookup(int number)
67{
68	struct atm_dev *dev;
69
70	mutex_lock(&atm_dev_mutex);
71	dev = __atm_dev_lookup(number);
72	mutex_unlock(&atm_dev_mutex);
73	return dev;
74}
75EXPORT_SYMBOL(atm_dev_lookup);
76
77struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops,
78				 int number, unsigned long *flags)
79{
80	struct atm_dev *dev, *inuse;
81
82	dev = __alloc_atm_dev(type);
83	if (!dev) {
84		pr_err("no space for dev %s\n", type);
85		return NULL;
86	}
87	mutex_lock(&atm_dev_mutex);
88	if (number != -1) {
89		inuse = __atm_dev_lookup(number);
90		if (inuse) {
91			atm_dev_put(inuse);
92			mutex_unlock(&atm_dev_mutex);
93			kfree(dev);
94			return NULL;
95		}
96		dev->number = number;
97	} else {
98		dev->number = 0;
99		while ((inuse = __atm_dev_lookup(dev->number))) {
100			atm_dev_put(inuse);
101			dev->number++;
102		}
103	}
104
105	dev->ops = ops;
106	if (flags)
107		dev->flags = *flags;
108	else
109		memset(&dev->flags, 0, sizeof(dev->flags));
110	memset(&dev->stats, 0, sizeof(dev->stats));
111	atomic_set(&dev->refcnt, 1);
112
113	if (atm_proc_dev_register(dev) < 0) {
114		pr_err("atm_proc_dev_register failed for dev %s\n", type);
115		goto out_fail;
116	}
117
118	if (atm_register_sysfs(dev) < 0) {
119		pr_err("atm_register_sysfs failed for dev %s\n", type);
120		atm_proc_dev_deregister(dev);
121		goto out_fail;
122	}
123
124	list_add_tail(&dev->dev_list, &atm_devs);
125
126out:
127	mutex_unlock(&atm_dev_mutex);
128	return dev;
129
130out_fail:
131	kfree(dev);
132	dev = NULL;
133	goto out;
134}
135EXPORT_SYMBOL(atm_dev_register);
136
137void atm_dev_deregister(struct atm_dev *dev)
138{
139	BUG_ON(test_bit(ATM_DF_REMOVED, &dev->flags));
140	set_bit(ATM_DF_REMOVED, &dev->flags);
141
142	/*
143	 * if we remove current device from atm_devs list, new device
144	 * with same number can appear, such we need deregister proc,
145	 * release async all vccs and remove them from vccs list too
146	 */
147	mutex_lock(&atm_dev_mutex);
148	list_del(&dev->dev_list);
149	mutex_unlock(&atm_dev_mutex);
150
151	atm_dev_release_vccs(dev);
152	atm_unregister_sysfs(dev);
153	atm_proc_dev_deregister(dev);
154
155	atm_dev_put(dev);
156}
157EXPORT_SYMBOL(atm_dev_deregister);
158
159static void copy_aal_stats(struct k_atm_aal_stats *from,
160    struct atm_aal_stats *to)
161{
162#define __HANDLE_ITEM(i) to->i = atomic_read(&from->i)
163	__AAL_STAT_ITEMS
164#undef __HANDLE_ITEM
165}
166
167static void subtract_aal_stats(struct k_atm_aal_stats *from,
168    struct atm_aal_stats *to)
169{
170#define __HANDLE_ITEM(i) atomic_sub(to->i, &from->i)
171	__AAL_STAT_ITEMS
172#undef __HANDLE_ITEM
173}
174
175static int fetch_stats(struct atm_dev *dev, struct atm_dev_stats __user *arg,
176		       int zero)
177{
178	struct atm_dev_stats tmp;
179	int error = 0;
180
181	copy_aal_stats(&dev->stats.aal0, &tmp.aal0);
182	copy_aal_stats(&dev->stats.aal34, &tmp.aal34);
183	copy_aal_stats(&dev->stats.aal5, &tmp.aal5);
184	if (arg)
185		error = copy_to_user(arg, &tmp, sizeof(tmp));
186	if (zero && !error) {
187		subtract_aal_stats(&dev->stats.aal0, &tmp.aal0);
188		subtract_aal_stats(&dev->stats.aal34, &tmp.aal34);
189		subtract_aal_stats(&dev->stats.aal5, &tmp.aal5);
190	}
191	return error ? -EFAULT : 0;
192}
193
194int atm_dev_ioctl(unsigned int cmd, void __user *arg, int compat)
195{
196	void __user *buf;
197	int error, len, number, size = 0;
198	struct atm_dev *dev;
199	struct list_head *p;
200	int *tmp_buf, *tmp_p;
201	int __user *sioc_len;
202	int __user *iobuf_len;
203
204#ifndef CONFIG_COMPAT
205	compat = 0; /* Just so the compiler _knows_ */
206#endif
207
208	switch (cmd) {
209	case ATM_GETNAMES:
210		if (compat) {
211#ifdef CONFIG_COMPAT
212			struct compat_atm_iobuf __user *ciobuf = arg;
213			compat_uptr_t cbuf;
214			iobuf_len = &ciobuf->length;
215			if (get_user(cbuf, &ciobuf->buffer))
216				return -EFAULT;
217			buf = compat_ptr(cbuf);
218#endif
219		} else {
220			struct atm_iobuf __user *iobuf = arg;
221			iobuf_len = &iobuf->length;
222			if (get_user(buf, &iobuf->buffer))
223				return -EFAULT;
224		}
225		if (get_user(len, iobuf_len))
226			return -EFAULT;
227		mutex_lock(&atm_dev_mutex);
228		list_for_each(p, &atm_devs)
229			size += sizeof(int);
230		if (size > len) {
231			mutex_unlock(&atm_dev_mutex);
232			return -E2BIG;
233		}
234		tmp_buf = kmalloc(size, GFP_ATOMIC);
235		if (!tmp_buf) {
236			mutex_unlock(&atm_dev_mutex);
237			return -ENOMEM;
238		}
239		tmp_p = tmp_buf;
240		list_for_each(p, &atm_devs) {
241			dev = list_entry(p, struct atm_dev, dev_list);
242			*tmp_p++ = dev->number;
243		}
244		mutex_unlock(&atm_dev_mutex);
245		error = ((copy_to_user(buf, tmp_buf, size)) ||
246			 put_user(size, iobuf_len))
247			? -EFAULT : 0;
248		kfree(tmp_buf);
249		return error;
250	default:
251		break;
252	}
253
254	if (compat) {
255#ifdef CONFIG_COMPAT
256		struct compat_atmif_sioc __user *csioc = arg;
257		compat_uptr_t carg;
258
259		sioc_len = &csioc->length;
260		if (get_user(carg, &csioc->arg))
261			return -EFAULT;
262		buf = compat_ptr(carg);
263
264		if (get_user(len, &csioc->length))
265			return -EFAULT;
266		if (get_user(number, &csioc->number))
267			return -EFAULT;
268#endif
269	} else {
270		struct atmif_sioc __user *sioc = arg;
271
272		sioc_len = &sioc->length;
273		if (get_user(buf, &sioc->arg))
274			return -EFAULT;
275		if (get_user(len, &sioc->length))
276			return -EFAULT;
277		if (get_user(number, &sioc->number))
278			return -EFAULT;
279	}
280
281	dev = try_then_request_module(atm_dev_lookup(number), "atm-device-%d",
282				      number);
283	if (!dev)
284		return -ENODEV;
285
286	switch (cmd) {
287	case ATM_GETTYPE:
288		size = strlen(dev->type) + 1;
289		if (copy_to_user(buf, dev->type, size)) {
290			error = -EFAULT;
291			goto done;
292		}
293		break;
294	case ATM_GETESI:
295		size = ESI_LEN;
296		if (copy_to_user(buf, dev->esi, size)) {
297			error = -EFAULT;
298			goto done;
299		}
300		break;
301	case ATM_SETESI:
302	{
303		int i;
304
305		for (i = 0; i < ESI_LEN; i++)
306			if (dev->esi[i]) {
307				error = -EEXIST;
308				goto done;
309			}
310	}
311	/* fall through */
312	case ATM_SETESIF:
313	{
314		unsigned char esi[ESI_LEN];
315
316		if (!capable(CAP_NET_ADMIN)) {
317			error = -EPERM;
318			goto done;
319		}
320		if (copy_from_user(esi, buf, ESI_LEN)) {
321			error = -EFAULT;
322			goto done;
323		}
324		memcpy(dev->esi, esi, ESI_LEN);
325		error =  ESI_LEN;
326		goto done;
327	}
328	case ATM_GETSTATZ:
329		if (!capable(CAP_NET_ADMIN)) {
330			error = -EPERM;
331			goto done;
332		}
333		/* fall through */
334	case ATM_GETSTAT:
335		size = sizeof(struct atm_dev_stats);
336		error = fetch_stats(dev, buf, cmd == ATM_GETSTATZ);
337		if (error)
338			goto done;
339		break;
340	case ATM_GETCIRANGE:
341		size = sizeof(struct atm_cirange);
342		if (copy_to_user(buf, &dev->ci_range, size)) {
343			error = -EFAULT;
344			goto done;
345		}
346		break;
347	case ATM_GETLINKRATE:
348		size = sizeof(int);
349		if (copy_to_user(buf, &dev->link_rate, size)) {
350			error = -EFAULT;
351			goto done;
352		}
353		break;
354	case ATM_RSTADDR:
355		if (!capable(CAP_NET_ADMIN)) {
356			error = -EPERM;
357			goto done;
358		}
359		atm_reset_addr(dev, ATM_ADDR_LOCAL);
360		break;
361	case ATM_ADDADDR:
362	case ATM_DELADDR:
363	case ATM_ADDLECSADDR:
364	case ATM_DELLECSADDR:
365	{
366		struct sockaddr_atmsvc addr;
367
368		if (!capable(CAP_NET_ADMIN)) {
369			error = -EPERM;
370			goto done;
371		}
372
373		if (copy_from_user(&addr, buf, sizeof(addr))) {
374			error = -EFAULT;
375			goto done;
376		}
377		if (cmd == ATM_ADDADDR || cmd == ATM_ADDLECSADDR)
378			error = atm_add_addr(dev, &addr,
379					     (cmd == ATM_ADDADDR ?
380					      ATM_ADDR_LOCAL : ATM_ADDR_LECS));
381		else
382			error = atm_del_addr(dev, &addr,
383					     (cmd == ATM_DELADDR ?
384					      ATM_ADDR_LOCAL : ATM_ADDR_LECS));
385		goto done;
386	}
387	case ATM_GETADDR:
388	case ATM_GETLECSADDR:
389		error = atm_get_addr(dev, buf, len,
390				     (cmd == ATM_GETADDR ?
391				      ATM_ADDR_LOCAL : ATM_ADDR_LECS));
392		if (error < 0)
393			goto done;
394		size = error;
395		/* may return 0, but later on size == 0 means "don't
396		   write the length" */
397		error = put_user(size, sioc_len) ? -EFAULT : 0;
398		goto done;
399	case ATM_SETLOOP:
400		if (__ATM_LM_XTRMT((int) (unsigned long) buf) &&
401		    __ATM_LM_XTLOC((int) (unsigned long) buf) >
402		    __ATM_LM_XTRMT((int) (unsigned long) buf)) {
403			error = -EINVAL;
404			goto done;
405		}
406		/* fall through */
407	case ATM_SETCIRANGE:
408	case SONET_GETSTATZ:
409	case SONET_SETDIAG:
410	case SONET_CLRDIAG:
411	case SONET_SETFRAMING:
412		if (!capable(CAP_NET_ADMIN)) {
413			error = -EPERM;
414			goto done;
415		}
416		/* fall through */
417	default:
418		if (compat) {
419#ifdef CONFIG_COMPAT
420			if (!dev->ops->compat_ioctl) {
421				error = -EINVAL;
422				goto done;
423			}
424			size = dev->ops->compat_ioctl(dev, cmd, buf);
425#endif
426		} else {
427			if (!dev->ops->ioctl) {
428				error = -EINVAL;
429				goto done;
430			}
431			size = dev->ops->ioctl(dev, cmd, buf);
432		}
433		if (size < 0) {
434			error = (size == -ENOIOCTLCMD ? -EINVAL : size);
435			goto done;
436		}
437	}
438
439	if (size)
440		error = put_user(size, sioc_len) ? -EFAULT : 0;
441	else
442		error = 0;
443done:
444	atm_dev_put(dev);
445	return error;
446}
447
448void *atm_dev_seq_start(struct seq_file *seq, loff_t *pos)
449{
450	mutex_lock(&atm_dev_mutex);
451	return seq_list_start_head(&atm_devs, *pos);
452}
453
454void atm_dev_seq_stop(struct seq_file *seq, void *v)
455{
456	mutex_unlock(&atm_dev_mutex);
457}
458
459void *atm_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
460{
461	return seq_list_next(v, &atm_devs, pos);
462}
463