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, 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