RHEL4/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         * rcu_dereference() 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
 139        /*
 140         * before setting the ids->entries to the new array, there must be a
 141         * smp_wmb() to make sure the memcpyed contents of the new array are
 142         * visible before the new array becomes visible.
 143         */
 144        smp_wmb();      /* prevent seeing new array uninitialized. */
 145        ids->entries = new;
 146        smp_wmb();      /* prevent indexing into old array based on new size. */
 147        ids->size = newsize;
 148
 149        ipc_rcu_putref(old);
 150        return ids->size;
 151}
 152
 153/**
 154 *      ipc_addid       -       add an IPC identifier
 155 *      @ids: IPC identifier set
 156 *      @new: new IPC permission set
 157 *      @size: new size limit for the id array
 158 *
 159 *      Add an entry 'new' to the IPC arrays. The permissions object is
 160 *      initialised and the first free entry is set up and the id assigned
 161 *      is returned. The list is returned in a locked state on success.
 162 *      On failure the list is not locked and -1 is returned.
 163 *
 164 *      Called with ipc_ids.sem held.
 165 */
 166 
 167int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
 168{
 169        int id;
 170
 171        size = grow_ary(ids,size);
 172
 173        /*
 174         * rcu_dereference()() is not needed here since
 175         * ipc_ids.sem is held
 176         */
 177        for (id = 0; id < size; id++) {
 178                if(ids->entries[id].p == NULL)
 179                        goto found;
 180        }
 181        return -1;
 182found:
 183        ids->in_use++;
 184        if (id > ids->max_id)
 185                ids->max_id = id;
 186
 187        new->cuid = new->uid = current->euid;
 188        new->gid = new->cgid = current->egid;
 189
 190        new->seq = ids->seq++;
 191        if(ids->seq > ids->seq_max)
 192                ids->seq = 0;
 193
 194        new->lock = SPIN_LOCK_UNLOCKED;
 195        new->deleted = 0;
 196        rcu_read_lock();
 197        spin_lock(&new->lock);
 198        ids->entries[id].p = new;
 199        return id;
 200}
 201
 202/**
 203 *      ipc_rmid        -       remove an IPC identifier
 204 *      @ids: identifier set
 205 *      @id: Identifier to remove
 206 *
 207 *      The identifier must be valid, and in use. The kernel will panic if
 208 *      fed an invalid identifier. The entry is removed and internal
 209 *      variables recomputed. The object associated with the identifier
 210 *      is returned.
 211 *      ipc_ids.sem and the spinlock for this ID is hold before this function
 212 *      is called, and remain locked on the exit.
 213 */
 214 
 215struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id)
 216{
 217        struct kern_ipc_perm* p;
 218        int lid = id % SEQ_MULTIPLIER;
 219        if(lid >= ids->size)
 220                BUG();
 221
 222        /* 
 223         * do not need a rcu_dereference()() here to force ordering
 224         * on Alpha, since the ipc_ids.sem is held.
 225         */     
 226        p = ids->entries[lid].p;
 227        ids->entries[lid].p = NULL;
 228        if(p==NULL)
 229                BUG();
 230        ids->in_use--;
 231
 232        if (lid == ids->max_id) {
 233                do {
 234                        lid--;
 235                        if(lid == -1)
 236                                break;
 237                } while (ids->entries[lid].p == NULL);
 238                ids->max_id = lid;
 239        }
 240        p->deleted = 1;
 241        return p;
 242}
 243
 244/**
 245 *      ipc_alloc       -       allocate ipc space
 246 *      @size: size desired
 247 *
 248 *      Allocate memory from the appropriate pools and return a pointer to it.
 249 *      NULL is returned if the allocation fails
 250 */
 251 
 252void* ipc_alloc(int size)
 253{
 254        void* out;
 255        if(size > PAGE_SIZE)
 256                out = vmalloc(size);
 257        else
 258                out = kmalloc(size, GFP_KERNEL);
 259        return out;
 260}
 261
 262/**
 263 *      ipc_free        -       free ipc space
 264 *      @ptr: pointer returned by ipc_alloc
 265 *      @size: size of block
 266 *
 267 *      Free a block created with ipc_alloc. The caller must know the size
 268 *      used in the allocation call.
 269 */
 270
 271void ipc_free(void* ptr, int size)
 272{
 273        if(size > PAGE_SIZE)
 274                vfree(ptr);
 275        else
 276                kfree(ptr);
 277}
 278
 279/*
 280 * rcu allocations:
 281 * There are three headers that are prepended to the actual allocation:
 282 * - during use: ipc_rcu_hdr.
 283 * - during the rcu grace period: ipc_rcu_grace.
 284 * - [only if vmalloc]: ipc_rcu_sched.
 285 * Their lifetime doesn't overlap, thus the headers share the same memory.
 286 * Unlike a normal union, they are right-aligned, thus some container_of
 287 * forward/backward casting is necessary:
 288 */
 289struct ipc_rcu_hdr
 290{
 291        int refcount;
 292        int is_vmalloc;
 293        void *data[0];
 294};
 295
 296
 297struct ipc_rcu_grace
 298{
 299        struct rcu_head rcu;
 300        /* "void *" makes sure alignment of following data is sane. */
 301        void *data[0];
 302};
 303
 304struct ipc_rcu_sched
 305{
 306        struct work_struct work;
 307        /* "void *" makes sure alignment of following data is sane. */
 308        void *data[0];
 309};
 310
 311#define HDRLEN_KMALLOC          (sizeof(struct ipc_rcu_grace) > sizeof(struct ipc_rcu_hdr) ? \
 312                                        sizeof(struct ipc_rcu_grace) : sizeof(struct ipc_rcu_hdr))
 313#define HDRLEN_VMALLOC          (sizeof(struct ipc_rcu_sched) > HDRLEN_KMALLOC ? \
 314                                        sizeof(struct ipc_rcu_sched) : HDRLEN_KMALLOC)
 315
 316static inline int rcu_use_vmalloc(int size)
 317{
 318        /* Too big for a single page? */
 319        if (HDRLEN_KMALLOC + size > PAGE_SIZE)
 320                return 1;
 321        return 0;
 322}
 323
 324/**
 325 *      ipc_rcu_alloc   -       allocate ipc and rcu space 
 326 *      @size: size desired
 327 *
 328 *      Allocate memory for the rcu header structure +  the object.
 329 *      Returns the pointer to the object.
 330 *      NULL is returned if the allocation fails. 
 331 */
 332 
 333void* ipc_rcu_alloc(int size)
 334{
 335        void* out;
 336        /* 
 337         * We prepend the allocation with the rcu struct, and
 338         * workqueue if necessary (for vmalloc). 
 339         */
 340        if (rcu_use_vmalloc(size)) {
 341                out = vmalloc(HDRLEN_VMALLOC + size);
 342                if (out) {
 343                        out += HDRLEN_VMALLOC;
 344                        container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 1;
 345                        container_of(out, struct ipc_rcu_hdr, data)->refcount = 1;
 346                }
 347        } else {
 348                out = kmalloc(HDRLEN_KMALLOC + size, GFP_KERNEL);
 349                if (out) {
 350                        out += HDRLEN_KMALLOC;
 351                        container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 0;
 352                        container_of(out, struct ipc_rcu_hdr, data)->refcount = 1;
 353                }
 354        }
 355
 356        return out;
 357}
 358
 359void ipc_rcu_getref(void *ptr)
 360{
 361        container_of(ptr, struct ipc_rcu_hdr, data)->refcount++;
 362}
 363
 364/**
 365 *      ipc_schedule_free       - free ipc + rcu space
 366 * 
 367 * Since RCU callback function is called in bh,
 368 * we need to defer the vfree to schedule_work
 369 */
 370static void ipc_schedule_free(struct rcu_head *head)
 371{
 372        struct ipc_rcu_grace *grace =
 373                container_of(head, struct ipc_rcu_grace, rcu);
 374        struct ipc_rcu_sched *sched =
 375                        container_of(&(grace->data[0]), struct ipc_rcu_sched, data[0]);
 376
 377        INIT_WORK(&sched->work, vfree, sched);
 378        schedule_work(&sched->work);
 379}
 380
 381/**
 382 *      ipc_immediate_free      - free ipc + rcu space
 383 *
 384 *      Free from the RCU callback context
 385 *
 386 */
 387static void ipc_immediate_free(struct rcu_head *head)
 388{
 389        struct ipc_rcu_grace *free =
 390                container_of(head, struct ipc_rcu_grace, rcu);
 391        kfree(free);
 392}
 393
 394void ipc_rcu_putref(void *ptr)
 395{
 396        if (--container_of(ptr, struct ipc_rcu_hdr, data)->refcount > 0)
 397                return;
 398
 399        if (container_of(ptr, struct ipc_rcu_hdr, data)->is_vmalloc) {
 400                call_rcu(&container_of(ptr, struct ipc_rcu_grace, data)->rcu,
 401                                ipc_schedule_free);
 402        } else {
 403                call_rcu(&container_of(ptr, struct ipc_rcu_grace, data)->rcu,
 404                                ipc_immediate_free);
 405        }
 406}
 407
 408/**
 409 *      ipcperms        -       check IPC permissions
 410 *      @ipcp: IPC permission set
 411 *      @flag: desired permission set.
 412 *
 413 *      Check user, group, other permissions for access
 414 *      to ipc resources. return 0 if allowed
 415 */
 416 
 417int ipcperms (struct kern_ipc_perm *ipcp, short flag)
 418{       /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */
 419        int requested_mode, granted_mode;
 420
 421        requested_mode = (flag >> 6) | (flag >> 3) | flag;
 422        granted_mode = ipcp->mode;
 423        if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
 424                granted_mode >>= 6;
 425        else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid))
 426                granted_mode >>= 3;
 427        /* is there some bit set in requested_mode but not in granted_mode? */
 428        if ((requested_mode & ~granted_mode & 0007) && 
 429            !capable(CAP_IPC_OWNER))
 430                return -1;
 431
 432        return security_ipc_permission(ipcp, flag);
 433}
 434
 435/*
 436 * Functions to convert between the kern_ipc_perm structure and the
 437 * old/new ipc_perm structures
 438 */
 439
 440/**
 441 *      kernel_to_ipc64_perm    -       convert kernel ipc permissions to user
 442 *      @in: kernel permissions
 443 *      @out: new style IPC permissions
 444 *
 445 *      Turn the kernel object 'in' into a set of permissions descriptions
 446 *      for returning to userspace (out).
 447 */
 448 
 449
 450void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out)
 451{
 452        out->key        = in->key;
 453        out->uid        = in->uid;
 454        out->gid        = in->gid;
 455        out->cuid       = in->cuid;
 456        out->cgid       = in->cgid;
 457        out->mode       = in->mode;
 458        out->seq        = in->seq;
 459}
 460
 461/**
 462 *      ipc64_perm_to_ipc_perm  -       convert old ipc permissions to new
 463 *      @in: new style IPC permissions
 464 *      @out: old style IPC permissions
 465 *
 466 *      Turn the new style permissions object in into a compatibility
 467 *      object and store it into the 'out' pointer.
 468 */
 469 
 470void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out)
 471{
 472        out->key        = in->key;
 473        SET_UID(out->uid, in->uid);
 474        SET_GID(out->gid, in->gid);
 475        SET_UID(out->cuid, in->cuid);
 476        SET_GID(out->cgid, in->cgid);
 477        out->mode       = in->mode;
 478        out->seq        = in->seq;
 479}
 480
 481/*
 482 * So far only shm_get_stat() calls ipc_get() via shm_get(), so ipc_get()
 483 * is called with shm_ids.sem locked.  Since grow_ary() is also called with
 484 * shm_ids.sem down(for Shared Memory), there is no need to add read 
 485 * barriers here to gurantee the writes in grow_ary() are seen in order 
 486 * here (for Alpha).
 487 *
 488 * However ipc_get() itself does not necessary require ipc_ids.sem down. So
 489 * if in the future ipc_get() is used by other places without ipc_ids.sem
 490 * down, then ipc_get() needs read memery barriers as ipc_lock() does.
 491 */
 492struct kern_ipc_perm* ipc_get(struct ipc_ids* ids, int id)
 493{
 494        struct kern_ipc_perm* out;
 495        int lid = id % SEQ_MULTIPLIER;
 496        if(lid >= ids->size)
 497                return NULL;
 498        out = ids->entries[lid].p;
 499        return out;
 500}
 501
 502struct kern_ipc_perm* ipc_lock(struct ipc_ids* ids, int id)
 503{
 504        struct kern_ipc_perm* out;
 505        int lid = id % SEQ_MULTIPLIER;
 506        struct ipc_id* entries;
 507
 508        rcu_read_lock();
 509        if(lid >= ids->size) {
 510                rcu_read_unlock();
 511                return NULL;
 512        }
 513
 514        /* 
 515         * Note: The following two read barriers are corresponding
 516         * to the two write barriers in grow_ary(). They guarantee 
 517         * the writes are seen in the same order on the read side. 
 518         * smp_rmb() has effect on all CPUs.  rcu_dereference()
 519         * is used if there are data dependency between two reads, and 
 520         * has effect only on Alpha.
 521         */
 522        smp_rmb(); /* prevent indexing old array with new size */
 523        entries = rcu_dereference(ids->entries);
 524        out = entries[lid].p;
 525        if(out == NULL) {
 526                rcu_read_unlock();
 527                return NULL;
 528        }
 529        spin_lock(&out->lock);
 530        
 531        /* ipc_rmid() may have already freed the ID while ipc_lock
 532         * was spinning: here verify that the structure is still valid
 533         */
 534        if (out->deleted) {
 535                spin_unlock(&out->lock);
 536                rcu_read_unlock();
 537                return NULL;
 538        }
 539        return out;
 540}
 541
 542void ipc_lock_by_ptr(struct kern_ipc_perm *perm)
 543{
 544        rcu_read_lock();
 545        spin_lock(&perm->lock);
 546}
 547
 548void ipc_unlock(struct kern_ipc_perm* perm)
 549{
 550        spin_unlock(&perm->lock);
 551        rcu_read_unlock();
 552}
 553
 554int ipc_buildid(struct ipc_ids* ids, int id, int seq)
 555{
 556        return SEQ_MULTIPLIER*seq + id;
 557}
 558
 559int ipc_checkid(struct ipc_ids* ids, struct kern_ipc_perm* ipcp, int uid)
 560{
 561        if(uid/SEQ_MULTIPLIER != ipcp->seq)
 562                return 1;
 563        return 0;
 564}
 565
 566#ifdef __ARCH_WANT_IPC_PARSE_VERSION
 567
 568
 569/**
 570 *      ipc_parse_version       -       IPC call version
 571 *      @cmd: pointer to command
 572 *
 573 *      Return IPC_64 for new style IPC and IPC_OLD for old style IPC. 
 574 *      The cmd value is turned from an encoding command and version into
 575 *      just the command code.
 576 */
 577 
 578int ipc_parse_version (int *cmd)
 579{
 580        if (*cmd & IPC_64) {
 581                *cmd ^= IPC_64;
 582                return IPC_64;
 583        } else {
 584                return IPC_OLD;
 585        }
 586}
 587
 588#endif /* __ARCH_WANT_IPC_PARSE_VERSION */
 589