linux/net/atm/resources.c
<<
>>
Prefs
   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, struct device *parent,
  78                                 const struct atmdev_ops *ops, int number,
  79                                 unsigned long *flags)
  80{
  81        struct atm_dev *dev, *inuse;
  82
  83        dev = __alloc_atm_dev(type);
  84        if (!dev) {
  85                pr_err("no space for dev %s\n", type);
  86                return NULL;
  87        }
  88        mutex_lock(&atm_dev_mutex);
  89        if (number != -1) {
  90                inuse = __atm_dev_lookup(number);
  91                if (inuse) {
  92                        atm_dev_put(inuse);
  93                        mutex_unlock(&atm_dev_mutex);
  94                        kfree(dev);
  95                        return NULL;
  96                }
  97                dev->number = number;
  98        } else {
  99                dev->number = 0;
 100                while ((inuse = __atm_dev_lookup(dev->number))) {
 101                        atm_dev_put(inuse);
 102                        dev->number++;
 103                }
 104        }
 105
 106        dev->ops = ops;
 107        if (flags)
 108                dev->flags = *flags;
 109        else
 110                memset(&dev->flags, 0, sizeof(dev->flags));
 111        memset(&dev->stats, 0, sizeof(dev->stats));
 112        atomic_set(&dev->refcnt, 1);
 113
 114        if (atm_proc_dev_register(dev) < 0) {
 115                pr_err("atm_proc_dev_register failed for dev %s\n", type);
 116                goto out_fail;
 117        }
 118
 119        if (atm_register_sysfs(dev, parent) < 0) {
 120                pr_err("atm_register_sysfs failed for dev %s\n", type);
 121                atm_proc_dev_deregister(dev);
 122                goto out_fail;
 123        }
 124
 125        list_add_tail(&dev->dev_list, &atm_devs);
 126
 127out:
 128        mutex_unlock(&atm_dev_mutex);
 129        return dev;
 130
 131out_fail:
 132        kfree(dev);
 133        dev = NULL;
 134        goto out;
 135}
 136EXPORT_SYMBOL(atm_dev_register);
 137
 138void atm_dev_deregister(struct atm_dev *dev)
 139{
 140        BUG_ON(test_bit(ATM_DF_REMOVED, &dev->flags));
 141        set_bit(ATM_DF_REMOVED, &dev->flags);
 142
 143        /*
 144         * if we remove current device from atm_devs list, new device
 145         * with same number can appear, such we need deregister proc,
 146         * release async all vccs and remove them from vccs list too
 147         */
 148        mutex_lock(&atm_dev_mutex);
 149        list_del(&dev->dev_list);
 150        mutex_unlock(&atm_dev_mutex);
 151
 152        atm_dev_release_vccs(dev);
 153        atm_unregister_sysfs(dev);
 154        atm_proc_dev_deregister(dev);
 155
 156        atm_dev_put(dev);
 157}
 158EXPORT_SYMBOL(atm_dev_deregister);
 159
 160static void copy_aal_stats(struct k_atm_aal_stats *from,
 161    struct atm_aal_stats *to)
 162{
 163#define __HANDLE_ITEM(i) to->i = atomic_read(&from->i)
 164        __AAL_STAT_ITEMS
 165#undef __HANDLE_ITEM
 166}
 167
 168static void subtract_aal_stats(struct k_atm_aal_stats *from,
 169    struct atm_aal_stats *to)
 170{
 171#define __HANDLE_ITEM(i) atomic_sub(to->i, &from->i)
 172        __AAL_STAT_ITEMS
 173#undef __HANDLE_ITEM
 174}
 175
 176static int fetch_stats(struct atm_dev *dev, struct atm_dev_stats __user *arg,
 177                       int zero)
 178{
 179        struct atm_dev_stats tmp;
 180        int error = 0;
 181
 182        copy_aal_stats(&dev->stats.aal0, &tmp.aal0);
 183        copy_aal_stats(&dev->stats.aal34, &tmp.aal34);
 184        copy_aal_stats(&dev->stats.aal5, &tmp.aal5);
 185        if (arg)
 186                error = copy_to_user(arg, &tmp, sizeof(tmp));
 187        if (zero && !error) {
 188                subtract_aal_stats(&dev->stats.aal0, &tmp.aal0);
 189                subtract_aal_stats(&dev->stats.aal34, &tmp.aal34);
 190                subtract_aal_stats(&dev->stats.aal5, &tmp.aal5);
 191        }
 192        return error ? -EFAULT : 0;
 193}
 194
 195int atm_dev_ioctl(unsigned int cmd, void __user *arg, int compat)
 196{
 197        void __user *buf;
 198        int error, len, number, size = 0;
 199        struct atm_dev *dev;
 200        struct list_head *p;
 201        int *tmp_buf, *tmp_p;
 202        int __user *sioc_len;
 203        int __user *iobuf_len;
 204
 205#ifndef CONFIG_COMPAT
 206        compat = 0; /* Just so the compiler _knows_ */
 207#endif
 208
 209        switch (cmd) {
 210        case ATM_GETNAMES:
 211                if (compat) {
 212#ifdef CONFIG_COMPAT
 213                        struct compat_atm_iobuf __user *ciobuf = arg;
 214                        compat_uptr_t cbuf;
 215                        iobuf_len = &ciobuf->length;
 216                        if (get_user(cbuf, &ciobuf->buffer))
 217                                return -EFAULT;
 218                        buf = compat_ptr(cbuf);
 219#endif
 220                } else {
 221                        struct atm_iobuf __user *iobuf = arg;
 222                        iobuf_len = &iobuf->length;
 223                        if (get_user(buf, &iobuf->buffer))
 224                                return -EFAULT;
 225                }
 226                if (get_user(len, iobuf_len))
 227                        return -EFAULT;
 228                mutex_lock(&atm_dev_mutex);
 229                list_for_each(p, &atm_devs)
 230                        size += sizeof(int);
 231                if (size > len) {
 232                        mutex_unlock(&atm_dev_mutex);
 233                        return -E2BIG;
 234                }
 235                tmp_buf = kmalloc(size, GFP_ATOMIC);
 236                if (!tmp_buf) {
 237                        mutex_unlock(&atm_dev_mutex);
 238                        return -ENOMEM;
 239                }
 240                tmp_p = tmp_buf;
 241                list_for_each(p, &atm_devs) {
 242                        dev = list_entry(p, struct atm_dev, dev_list);
 243                        *tmp_p++ = dev->number;
 244                }
 245                mutex_unlock(&atm_dev_mutex);
 246                error = ((copy_to_user(buf, tmp_buf, size)) ||
 247                         put_user(size, iobuf_len))
 248                        ? -EFAULT : 0;
 249                kfree(tmp_buf);
 250                return error;
 251        default:
 252                break;
 253        }
 254
 255        if (compat) {
 256#ifdef CONFIG_COMPAT
 257                struct compat_atmif_sioc __user *csioc = arg;
 258                compat_uptr_t carg;
 259
 260                sioc_len = &csioc->length;
 261                if (get_user(carg, &csioc->arg))
 262                        return -EFAULT;
 263                buf = compat_ptr(carg);
 264
 265                if (get_user(len, &csioc->length))
 266                        return -EFAULT;
 267                if (get_user(number, &csioc->number))
 268                        return -EFAULT;
 269#endif
 270        } else {
 271                struct atmif_sioc __user *sioc = arg;
 272
 273                sioc_len = &sioc->length;
 274                if (get_user(buf, &sioc->arg))
 275                        return -EFAULT;
 276                if (get_user(len, &sioc->length))
 277                        return -EFAULT;
 278                if (get_user(number, &sioc->number))
 279                        return -EFAULT;
 280        }
 281
 282        dev = try_then_request_module(atm_dev_lookup(number), "atm-device-%d",
 283                                      number);
 284        if (!dev)
 285                return -ENODEV;
 286
 287        switch (cmd) {
 288        case ATM_GETTYPE:
 289                size = strlen(dev->type) + 1;
 290                if (copy_to_user(buf, dev->type, size)) {
 291                        error = -EFAULT;
 292                        goto done;
 293                }
 294                break;
 295        case ATM_GETESI:
 296                size = ESI_LEN;
 297                if (copy_to_user(buf, dev->esi, size)) {
 298                        error = -EFAULT;
 299                        goto done;
 300                }
 301                break;
 302        case ATM_SETESI:
 303        {
 304                int i;
 305
 306                for (i = 0; i < ESI_LEN; i++)
 307                        if (dev->esi[i]) {
 308                                error = -EEXIST;
 309                                goto done;
 310                        }
 311        }
 312        /* fall through */
 313        case ATM_SETESIF:
 314        {
 315                unsigned char esi[ESI_LEN];
 316
 317                if (!capable(CAP_NET_ADMIN)) {
 318                        error = -EPERM;
 319                        goto done;
 320                }
 321                if (copy_from_user(esi, buf, ESI_LEN)) {
 322                        error = -EFAULT;
 323                        goto done;
 324                }
 325                memcpy(dev->esi, esi, ESI_LEN);
 326                error =  ESI_LEN;
 327                goto done;
 328        }
 329        case ATM_GETSTATZ:
 330                if (!capable(CAP_NET_ADMIN)) {
 331                        error = -EPERM;
 332                        goto done;
 333                }
 334                /* fall through */
 335        case ATM_GETSTAT:
 336                size = sizeof(struct atm_dev_stats);
 337                error = fetch_stats(dev, buf, cmd == ATM_GETSTATZ);
 338                if (error)
 339                        goto done;
 340                break;
 341        case ATM_GETCIRANGE:
 342                size = sizeof(struct atm_cirange);
 343                if (copy_to_user(buf, &dev->ci_range, size)) {
 344                        error = -EFAULT;
 345                        goto done;
 346                }
 347                break;
 348        case ATM_GETLINKRATE:
 349                size = sizeof(int);
 350                if (copy_to_user(buf, &dev->link_rate, size)) {
 351                        error = -EFAULT;
 352                        goto done;
 353                }
 354                break;
 355        case ATM_RSTADDR:
 356                if (!capable(CAP_NET_ADMIN)) {
 357                        error = -EPERM;
 358                        goto done;
 359                }
 360                atm_reset_addr(dev, ATM_ADDR_LOCAL);
 361                break;
 362        case ATM_ADDADDR:
 363        case ATM_DELADDR:
 364        case ATM_ADDLECSADDR:
 365        case ATM_DELLECSADDR:
 366        {
 367                struct sockaddr_atmsvc addr;
 368
 369                if (!capable(CAP_NET_ADMIN)) {
 370                        error = -EPERM;
 371                        goto done;
 372                }
 373
 374                if (copy_from_user(&addr, buf, sizeof(addr))) {
 375                        error = -EFAULT;
 376                        goto done;
 377                }
 378                if (cmd == ATM_ADDADDR || cmd == ATM_ADDLECSADDR)
 379                        error = atm_add_addr(dev, &addr,
 380                                             (cmd == ATM_ADDADDR ?
 381                                              ATM_ADDR_LOCAL : ATM_ADDR_LECS));
 382                else
 383                        error = atm_del_addr(dev, &addr,
 384                                             (cmd == ATM_DELADDR ?
 385                                              ATM_ADDR_LOCAL : ATM_ADDR_LECS));
 386                goto done;
 387        }
 388        case ATM_GETADDR:
 389        case ATM_GETLECSADDR:
 390                error = atm_get_addr(dev, buf, len,
 391                                     (cmd == ATM_GETADDR ?
 392                                      ATM_ADDR_LOCAL : ATM_ADDR_LECS));
 393                if (error < 0)
 394                        goto done;
 395                size = error;
 396                /* may return 0, but later on size == 0 means "don't
 397                   write the length" */
 398                error = put_user(size, sioc_len) ? -EFAULT : 0;
 399                goto done;
 400        case ATM_SETLOOP:
 401                if (__ATM_LM_XTRMT((int) (unsigned long) buf) &&
 402                    __ATM_LM_XTLOC((int) (unsigned long) buf) >
 403                    __ATM_LM_XTRMT((int) (unsigned long) buf)) {
 404                        error = -EINVAL;
 405                        goto done;
 406                }
 407                /* fall through */
 408        case ATM_SETCIRANGE:
 409        case SONET_GETSTATZ:
 410        case SONET_SETDIAG:
 411        case SONET_CLRDIAG:
 412        case SONET_SETFRAMING:
 413                if (!capable(CAP_NET_ADMIN)) {
 414                        error = -EPERM;
 415                        goto done;
 416                }
 417                /* fall through */
 418        default:
 419                if (compat) {
 420#ifdef CONFIG_COMPAT
 421                        if (!dev->ops->compat_ioctl) {
 422                                error = -EINVAL;
 423                                goto done;
 424                        }
 425                        size = dev->ops->compat_ioctl(dev, cmd, buf);
 426#endif
 427                } else {
 428                        if (!dev->ops->ioctl) {
 429                                error = -EINVAL;
 430                                goto done;
 431                        }
 432                        size = dev->ops->ioctl(dev, cmd, buf);
 433                }
 434                if (size < 0) {
 435                        error = (size == -ENOIOCTLCMD ? -ENOTTY : size);
 436                        goto done;
 437                }
 438        }
 439
 440        if (size)
 441                error = put_user(size, sioc_len) ? -EFAULT : 0;
 442        else
 443                error = 0;
 444done:
 445        atm_dev_put(dev);
 446        return error;
 447}
 448
 449void *atm_dev_seq_start(struct seq_file *seq, loff_t *pos)
 450{
 451        mutex_lock(&atm_dev_mutex);
 452        return seq_list_start_head(&atm_devs, *pos);
 453}
 454
 455void atm_dev_seq_stop(struct seq_file *seq, void *v)
 456{
 457        mutex_unlock(&atm_dev_mutex);
 458}
 459
 460void *atm_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 461{
 462        return seq_list_next(v, &atm_devs, pos);
 463}
 464
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.