linux-bk/ipc/util.c
<<
>>
Prefs
   1/*
   2 * linux/ipc/util.c
   3 * Copyright (C) 1992 Krishna Balasubramanian
   4 *
   5 * Sep 1997 - Call suser() last after "normal" permission checks so we
   6 *            get BSD style process accounting right.
   7 *            Occurs in several places in the IPC code.
   8 *            Chris Evans, <chris@ferret.lmh.ox.ac.uk>
   9 * Nov 1999 - ipc helper functions, unified SMP locking
  10 *            Manfred Spraul <manfreds@colorfullife.com>
  11 * Oct 2002 - One lock per IPC id. RCU ipc_free for lock-free grow_ary().
  12 *            Mingming Cao <cmm@us.ibm.com>
  13 */
  14
  15#include <linux/config.h>
  16#include <linux/mm.h>
  17#include <linux/shm.h>
  18#include <linux/init.h>
  19#include <linux/msg.h>
  20#include <linux/smp_lock.h>
  21#include <linux/vmalloc.h>
  22#include <linux/slab.h>
  23#include <linux/highuid.h>
  24#include <linux/security.h>
  25#include <linux/rcupdate.h>
  26#include <linux/workqueue.h>
  27
  28#include <asm/unistd.h>
  29
  30#include "util.h"
  31
  32/**
  33 *      ipc_init        -       initialise IPC subsystem
  34 *
  35 *      The various system5 IPC resources (semaphores, messages and shared
  36 *      memory are initialised
  37 */
  38 
  39static int __init ipc_init(void)
  40{
  41        sem_init();
  42        msg_init();
  43        shm_init();
  44        return 0;
  45}
  46__initcall(ipc_init);
  47
  48/**
  49 *      ipc_init_ids            -       initialise IPC identifiers
  50 *      @ids: Identifier set
  51 *      @size: Number of identifiers
  52 *
  53 *      Given a size for the ipc identifier range (limited below IPCMNI)
  54 *      set up the sequence range to use then allocate and initialise the
  55 *      array itself. 
  56 */
  57 
  58void __init ipc_init_ids(struct ipc_ids* ids, int size)
  59{
  60        int i;
  61        sema_init(&ids->sem,1);
  62
  63        if(size > IPCMNI)
  64                size = IPCMNI;
  65        ids->size = size;
  66        ids->in_use = 0;
  67        ids->max_id = -1;
  68        ids->seq = 0;
  69        {
  70                int seq_limit = INT_MAX/SEQ_MULTIPLIER;
  71                if(seq_limit > USHRT_MAX)
  72                        ids->seq_max = USHRT_MAX;
  73                 else
  74                        ids->seq_max = seq_limit;
  75        }
  76
  77        ids->entries = ipc_rcu_alloc(sizeof(struct ipc_id)*size);
  78
  79        if(ids->entries == NULL) {
  80                printk(KERN_ERR "ipc_init_ids() failed, ipc service disabled.\n");
  81                ids->size = 0;
  82        }
  83        for(i=0;i<ids->size;i++)
  84                ids->entries[i].p = NULL;
  85}
  86
  87/**
  88 *      ipc_findkey     -       find a key in an ipc identifier set     
  89 *      @ids: Identifier set
  90 *      @key: The key to find
  91 *      
  92 *      Requires ipc_ids.sem locked.
  93 *      Returns the identifier if found or -1 if not.
  94 */
  95 
  96int ipc_findkey(struct ipc_ids* ids, key_t key)
  97{
  98        int id;
  99        struct kern_ipc_perm* p;
 100        int max_id = ids->max_id;
 101
 102        /*
 103         * read_barrier_depends is not needed here
 104         * since ipc_ids.sem is held
 105         */
 106        for (id = 0; id <= max_id; id++) {
 107                p = ids->entries[id].p;
 108                if(p==NULL)
 109                        continue;
 110                if (key == p->key)
 111                        return id;
 112        }
 113        return -1;
 114}
 115
 116/*
 117 * Requires ipc_ids.sem locked
 118 */
 119static int grow_ary(struct ipc_ids* ids, int newsize)
 120{
 121        struct ipc_id* new;
 122        struct ipc_id* old;
 123        int i;
 124
 125        if(newsize > IPCMNI)
 126                newsize = IPCMNI;
 127        if(newsize <= ids->size)
 128                return newsize;
 129
 130        new = ipc_rcu_alloc(sizeof(struct ipc_id)*newsize);
 131        if(new == NULL)
 132                return ids->size;
 133        memcpy(new, ids->entries, sizeof(struct ipc_id)*ids->size);
 134        for(i=ids->size;i<newsize;i++) {
 135                new[i].p = NULL;
 136        }
 137        old = ids->entries;
 138        i = ids->size;
 139
 140        /*
 141         * before setting the ids->entries to the new array, there must be a
 142         * smp_wmb() to make sure the memcpyed contents of the new array are
 143         * visible before the new array becomes visible.
 144         */
 145        smp_wmb();      /* prevent seeing new array uninitialized. */
 146        ids->entries = new;
 147        smp_wmb();      /* prevent indexing into old array based on new size. */
 148        ids->size = newsize;
 149
 150        ipc_rcu_free(old, sizeof(struct ipc_id)*i);
 151        return ids->size;
 152}
 153
 154/**
 155 *      ipc_addid       -       add an IPC identifier
 156 *      @ids: IPC identifier set
 157 *      @new: new IPC permission set
 158 *      @size: new size limit for the id array
 159 *
 160 *      Add an entry 'new' to the IPC arrays. The permissions object is
 161 *      initialised and the first free entry is set up and the id assigned
 162 *      is returned. The list is returned in a locked state on success.
 163 *      On failure the list is not locked and -1 is returned.
 164 *
 165 *      Called with ipc_ids.sem held.
 166 */
 167 
 168int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
 169{
 170        int id;
 171
 172        size = grow_ary(ids,size);
 173
 174        /*
 175         * read_barrier_depends() is not needed here since
 176         * ipc_ids.sem is held
 177         */
 178        for (id = 0; id < size; id++) {
 179                if(ids->entries[id].p == NULL)
 180                        goto found;
 181        }
 182        return -1;
 183found:
 184        ids->in_use++;
 185        if (id > ids->max_id)
 186                ids->max_id = id;
 187
 188        new->cuid = new->uid = current->euid;
 189        new->gid = new->cgid = current->egid;
 190
 191        new->seq = ids->seq++;
 192        if(ids->seq > ids->seq_max)
 193                ids->seq = 0;
 194
 195        new->lock = SPIN_LOCK_UNLOCKED;
 196        new->deleted = 0;
 197        rcu_read_lock();
 198        spin_lock(&new->lock);
 199        ids->entries[id].p = new;
 200        return id;
 201}
 202
 203/**
 204 *      ipc_rmid        -       remove an IPC identifier
 205 *      @ids: identifier set
 206 *      @id: Identifier to remove
 207 *
 208 *      The identifier must be valid, and in use. The kernel will panic if
 209 *      fed an invalid identifier. The entry is removed and internal
 210 *      variables recomputed. The object associated with the identifier
 211 *      is returned.
 212 *      ipc_ids.sem and the spinlock for this ID is hold before this function
 213 *      is called, and remain locked on the exit.
 214 */
 215 
 216struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id)
 217{
 218        struct kern_ipc_perm* p;
 219        int lid = id % SEQ_MULTIPLIER;
 220        if(lid >= ids->size)
 221                BUG();
 222
 223        /* 
 224         * do not need a read_barrier_depends() here to force ordering
 225         * on Alpha, since the ipc_ids.sem is held.
 226         */     
 227        p = ids->entries[lid].p;
 228        ids->entries[lid].p = NULL;
 229        if(p==NULL)
 230                BUG();
 231        ids->in_use--;
 232
 233        if (lid == ids->max_id) {
 234                do {
 235                        lid--;
 236                        if(lid == -1)
 237                                break;
 238                } while (ids->entries[lid].p == NULL);
 239                ids->max_id = lid;
 240        }
 241        p->deleted = 1;
 242        return p;
 243}
 244
 245/**
 246 *      ipc_alloc       -       allocate ipc space
 247 *      @size: size desired
 248 *
 249 *      Allocate memory from the appropriate pools and return a pointer to it.
 250 *      NULL is returned if the allocation fails
 251 */
 252 
 253void* ipc_alloc(int size)
 254{
 255        void* out;
 256        if(size > PAGE_SIZE)
 257                out = vmalloc(size);
 258        else
 259                out = kmalloc(size, GFP_KERNEL);
 260        return out;
 261}
 262
 263/**
 264 *      ipc_free        -       free ipc space
 265 *      @ptr: pointer returned by ipc_alloc
 266 *      @size: size of block
 267 *
 268 *      Free a block created with ipc_alloc. The caller must know the size
 269 *      used in the allocation call.
 270 */
 271
 272void ipc_free(void* ptr, int size)
 273{
 274        if(size > PAGE_SIZE)
 275                vfree(ptr);
 276        else
 277                kfree(ptr);
 278}
 279
 280struct ipc_rcu_kmalloc
 281{
 282        struct rcu_head rcu;
 283        /* "void *" makes sure alignment of following data is sane. */
 284        void *data[0];
 285};
 286
 287struct ipc_rcu_vmalloc
 288{
 289        struct rcu_head rcu;
 290        struct work_struct work;
 291        /* "void *" makes sure alignment of following data is sane. */
 292        void *data[0];
 293};
 294
 295static inline int rcu_use_vmalloc(int size)
 296{
 297        /* Too big for a single page? */
 298        if (sizeof(struct ipc_rcu_kmalloc) + size > PAGE_SIZE)
 299                return 1;
 300        return 0;
 301}
 302
 303/**
 304 *      ipc_rcu_alloc   -       allocate ipc and rcu space 
 305 *      @size: size desired
 306 *
 307 *      Allocate memory for the rcu header structure +  the object.
 308 *      Returns the pointer to the object.
 309 *      NULL is returned if the allocation fails. 
 310 */
 311 
 312void* ipc_rcu_alloc(int size)
 313{
 314        void* out;
 315        /* 
 316         * We prepend the allocation with the rcu struct, and
 317         * workqueue if necessary (for vmalloc). 
 318         */
 319        if (rcu_use_vmalloc(size)) {
 320                out = vmalloc(sizeof(struct ipc_rcu_vmalloc) + size);
 321                if (out) out += sizeof(struct ipc_rcu_vmalloc);
 322        } else {
 323                out = kmalloc(sizeof(struct ipc_rcu_kmalloc)+size, GFP_KERNEL);
 324                if (out) out += sizeof(struct ipc_rcu_kmalloc);
 325        }
 326
 327        return out;
 328}
 329
 330/**
 331 *      ipc_schedule_free       - free ipc + rcu space
 332 * 
 333 * Since RCU callback function is called in bh,
 334 * we need to defer the vfree to schedule_work
 335 */
 336static void ipc_schedule_free(struct rcu_head *head)
 337{
 338        struct ipc_rcu_vmalloc *free =
 339                container_of(head, struct ipc_rcu_vmalloc, rcu);
 340
 341        INIT_WORK(&free->work, vfree, free);
 342        schedule_work(&free->work);
 343}
 344
 345/**
 346 *      ipc_immediate_free      - free ipc + rcu space
 347 *
 348 *      Free from the RCU callback context
 349 *
 350 */
 351static void ipc_immediate_free(struct rcu_head *head)
 352{
 353        struct ipc_rcu_kmalloc *free =
 354                container_of(head, struct ipc_rcu_kmalloc, rcu);
 355        kfree(free);
 356}
 357
 358
 359
 360void ipc_rcu_free(void* ptr, int size)
 361{
 362        if (rcu_use_vmalloc(size)) {
 363                struct ipc_rcu_vmalloc *free;
 364                free = ptr - sizeof(*free);
 365                call_rcu(&free->rcu, ipc_schedule_free);
 366        } else {
 367                struct ipc_rcu_kmalloc *free;
 368                free = ptr - sizeof(*free);
 369                call_rcu(&free->rcu, ipc_immediate_free);
 370        }
 371
 372}
 373
 374/**
 375 *      ipcperms        -       check IPC permissions
 376 *      @ipcp: IPC permission set
 377 *      @flag: desired permission set.
 378 *
 379 *      Check user, group, other permissions for access
 380 *      to ipc resources. return 0 if allowed
 381 */
 382 
 383int ipcperms (struct kern_ipc_perm *ipcp, short flag)
 384{       /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */
 385        int requested_mode, granted_mode;
 386
 387        requested_mode = (flag >> 6) | (flag >> 3) | flag;
 388        granted_mode = ipcp->mode;
 389        if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
 390                granted_mode >>= 6;
 391        else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid))
 392                granted_mode >>= 3;
 393        /* is there some bit set in requested_mode but not in granted_mode? */
 394        if ((requested_mode & ~granted_mode & 0007) && 
 395            !capable(CAP_IPC_OWNER))
 396                return -1;
 397
 398        return security_ipc_permission(ipcp, flag);
 399}
 400
 401/*
 402 * Functions to convert between the kern_ipc_perm structure and the
 403 * old/new ipc_perm structures
 404 */
 405
 406/**
 407 *      kernel_to_ipc64_perm    -       convert kernel ipc permissions to user
 408 *      @in: kernel permissions
 409 *      @out: new style IPC permissions
 410 *
 411 *      Turn the kernel object 'in' into a set of permissions descriptions
 412 *      for returning to userspace (out).
 413 */
 414 
 415
 416void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out)
 417{
 418        out->key        = in->key;
 419        out->uid        = in->uid;
 420        out->gid        = in->gid;
 421        out->cuid       = in->cuid;
 422        out->cgid       = in->cgid;
 423        out->mode       = in->mode;
 424        out->seq        = in->seq;
 425}
 426
 427/**
 428 *      ipc64_perm_to_ipc_perm  -       convert old ipc permissions to new
 429 *      @in: new style IPC permissions
 430 *      @out: old style IPC permissions
 431 *
 432 *      Turn the new style permissions object in into a compatibility
 433 *      object and store it into the 'out' pointer.
 434 */
 435 
 436void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out)
 437{
 438        out->key        = in->key;
 439        SET_UID(out->uid, in->uid);
 440        SET_GID(out->gid, in->gid);
 441        SET_UID(out->cuid, in->cuid);
 442        SET_GID(out->cgid, in->cgid);
 443        out->mode       = in->mode;
 444        out->seq        = in->seq;
 445}
 446
 447/*
 448 * So far only shm_get_stat() calls ipc_get() via shm_get(), so ipc_get()
 449 * is called with shm_ids.sem locked.  Since grow_ary() is also called with
 450 * shm_ids.sem down(for Shared Memory), there is no need to add read 
 451 * barriers here to gurantee the writes in grow_ary() are seen in order 
 452 * here (for Alpha).
 453 *
 454 * However ipc_get() itself does not necessary require ipc_ids.sem down. So
 455 * if in the future ipc_get() is used by other places without ipc_ids.sem
 456 * down, then ipc_get() needs read memery barriers as ipc_lock() does.
 457 */
 458struct kern_ipc_perm* ipc_get(struct ipc_ids* ids, int id)
 459{
 460        struct kern_ipc_perm* out;
 461        int lid = id % SEQ_MULTIPLIER;
 462        if(lid >= ids->size)
 463                return NULL;
 464        out = ids->entries[lid].p;
 465        return out;
 466}
 467
 468struct kern_ipc_perm* ipc_lock(struct ipc_ids* ids, int id)
 469{
 470        struct kern_ipc_perm* out;
 471        int lid = id % SEQ_MULTIPLIER;
 472        struct ipc_id* entries;
 473
 474        rcu_read_lock();
 475        if(lid >= ids->size) {
 476                rcu_read_unlock();
 477                return NULL;
 478        }
 479
 480        /* 
 481         * Note: The following two read barriers are corresponding
 482         * to the two write barriers in grow_ary(). They guarantee 
 483         * the writes are seen in the same order on the read side. 
 484         * smp_rmb() has effect on all CPUs.  read_barrier_depends() 
 485         * is used if there are data dependency between two reads, and 
 486         * has effect only on Alpha.
 487         */
 488        smp_rmb(); /* prevent indexing old array with new size */
 489        entries = ids->entries;
 490        read_barrier_depends(); /*prevent seeing new array unitialized */
 491        out = entries[lid].p;
 492        if(out == NULL) {
 493                rcu_read_unlock();
 494                return NULL;
 495        }
 496        spin_lock(&out->lock);
 497        
 498        /* ipc_rmid() may have already freed the ID while ipc_lock
 499         * was spinning: here verify that the structure is still valid
 500         */
 501        if (out->deleted) {
 502                spin_unlock(&out->lock);
 503                rcu_read_unlock();
 504                return NULL;
 505        }
 506        return out;
 507}
 508
 509void ipc_unlock(struct kern_ipc_perm* perm)
 510{
 511        spin_unlock(&perm->lock);
 512        rcu_read_unlock();
 513}
 514
 515int ipc_buildid(struct ipc_ids* ids, int id, int seq)
 516{
 517        return SEQ_MULTIPLIER*seq + id;
 518}
 519
 520int ipc_checkid(struct ipc_ids* ids, struct kern_ipc_perm* ipcp, int uid)
 521{
 522        if(uid/SEQ_MULTIPLIER != ipcp->seq)
 523                return 1;
 524        return 0;
 525}
 526
 527#ifdef __ARCH_WANT_IPC_PARSE_VERSION
 528
 529
 530/**
 531 *      ipc_parse_version       -       IPC call version
 532 *      @cmd: pointer to command
 533 *
 534 *      Return IPC_64 for new style IPC and IPC_OLD for old style IPC. 
 535 *      The cmd value is turned from an encoding command and version into
 536 *      just the command code.
 537 */
 538 
 539int ipc_parse_version (int *cmd)
 540{
 541        if (*cmd & IPC_64) {
 542                *cmd ^= IPC_64;
 543                return IPC_64;
 544        } else {
 545                return IPC_OLD;
 546        }
 547}
 548
 549#endif /* __ARCH_WANT_IPC_PARSE_VERSION */
 550
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.