This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / ipc / util.c
index 645ab4e..84453b3 100644 (file)
@@ -65,6 +65,7 @@ void __init ipc_init_ids(struct ipc_ids* ids, int size)
 
        if(size > IPCMNI)
                size = IPCMNI;
+       ids->size = size;
        ids->in_use = 0;
        ids->max_id = -1;
        ids->seq = 0;
@@ -76,17 +77,14 @@ void __init ipc_init_ids(struct ipc_ids* ids, int size)
                        ids->seq_max = seq_limit;
        }
 
-       ids->entries = ipc_rcu_alloc(sizeof(struct kern_ipc_perm *)*size +
-                                    sizeof(struct ipc_id_ary));
+       ids->entries = ipc_rcu_alloc(sizeof(struct ipc_id)*size);
 
        if(ids->entries == NULL) {
                printk(KERN_ERR "ipc_init_ids() failed, ipc service disabled.\n");
-               size = 0;
-               ids->entries = &ids->nullentry;
+               ids->size = 0;
        }
-       ids->entries->size = size;
-       for(i=0;i<size;i++)
-               ids->entries->p[i] = NULL;
+       for(i=0;i<ids->size;i++)
+               ids->entries[i].p = NULL;
 }
 
 /**
@@ -109,7 +107,7 @@ int ipc_findkey(struct ipc_ids* ids, key_t key)
         * since ipc_ids.sem is held
         */
        for (id = 0; id <= max_id; id++) {
-               p = ids->entries->p[id];
+               p = ids->entries[id].p;
                if (p==NULL)
                        continue;
                if (!vx_check(p->xid, VX_IDENT))
@@ -125,36 +123,36 @@ int ipc_findkey(struct ipc_ids* ids, key_t key)
  */
 static int grow_ary(struct ipc_ids* ids, int newsize)
 {
-       struct ipc_id_ary* new;
-       struct ipc_id_ary* old;
+       struct ipc_id* new;
+       struct ipc_id* old;
        int i;
-       int size = ids->entries->size;
 
        if(newsize > IPCMNI)
                newsize = IPCMNI;
-       if(newsize <= size)
+       if(newsize <= ids->size)
                return newsize;
 
-       new = ipc_rcu_alloc(sizeof(struct kern_ipc_perm *)*newsize +
-                           sizeof(struct ipc_id_ary));
+       new = ipc_rcu_alloc(sizeof(struct ipc_id)*newsize);
        if(new == NULL)
-               return size;
-       new->size = newsize;
-       memcpy(new->p, ids->entries->p, sizeof(struct kern_ipc_perm *)*size +
-                                       sizeof(struct ipc_id_ary));
-       for(i=size;i<newsize;i++) {
-               new->p[i] = NULL;
+               return ids->size;
+       memcpy(new, ids->entries, sizeof(struct ipc_id)*ids->size);
+       for(i=ids->size;i<newsize;i++) {
+               new[i].p = NULL;
        }
        old = ids->entries;
 
        /*
-        * Use rcu_assign_pointer() to make sure the memcpyed contents
-        * of the new array are visible before the new array becomes visible.
+        * before setting the ids->entries to the new array, there must be a
+        * smp_wmb() to make sure the memcpyed contents of the new array are
+        * visible before the new array becomes visible.
         */
-       rcu_assign_pointer(ids->entries, new);
+       smp_wmb();      /* prevent seeing new array uninitialized. */
+       ids->entries = new;
+       smp_wmb();      /* prevent indexing into old array based on new size. */
+       ids->size = newsize;
 
        ipc_rcu_putref(old);
-       return newsize;
+       return ids->size;
 }
 
 /**
@@ -182,7 +180,7 @@ int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
         * ipc_ids.sem is held
         */
        for (id = 0; id < size; id++) {
-               if(ids->entries->p[id] == NULL)
+               if(ids->entries[id].p == NULL)
                        goto found;
        }
        return -1;
@@ -198,11 +196,11 @@ found:
        if(ids->seq > ids->seq_max)
                ids->seq = 0;
 
-       spin_lock_init(&new->lock);
+       new->lock = SPIN_LOCK_UNLOCKED;
        new->deleted = 0;
        rcu_read_lock();
        spin_lock(&new->lock);
-       ids->entries->p[id] = new;
+       ids->entries[id].p = new;
        return id;
 }
 
@@ -223,15 +221,15 @@ struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id)
 {
        struct kern_ipc_perm* p;
        int lid = id % SEQ_MULTIPLIER;
-       if(lid >= ids->entries->size)
+       if(lid >= ids->size)
                BUG();
 
        /* 
         * do not need a rcu_dereference()() here to force ordering
         * on Alpha, since the ipc_ids.sem is held.
         */     
-       p = ids->entries->p[lid];
-       ids->entries->p[lid] = NULL;
+       p = ids->entries[lid].p;
+       ids->entries[lid].p = NULL;
        if(p==NULL)
                BUG();
        ids->in_use--;
@@ -241,7 +239,7 @@ struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id)
                        lid--;
                        if(lid == -1)
                                break;
-               } while (ids->entries->p[lid] == NULL);
+               } while (ids->entries[lid].p == NULL);
                ids->max_id = lid;
        }
        p->deleted = 1;
@@ -502,9 +500,9 @@ struct kern_ipc_perm* ipc_get(struct ipc_ids* ids, int id)
 {
        struct kern_ipc_perm* out;
        int lid = id % SEQ_MULTIPLIER;
-       if(lid >= ids->entries->size)
+       if(lid >= ids->size)
                return NULL;
-       out = ids->entries->p[lid];
+       out = ids->entries[lid].p;
        return out;
 }
 
@@ -512,15 +510,25 @@ struct kern_ipc_perm* ipc_lock(struct ipc_ids* ids, int id)
 {
        struct kern_ipc_perm* out;
        int lid = id % SEQ_MULTIPLIER;
-       struct ipc_id_ary* entries;
+       struct ipc_id* entries;
 
        rcu_read_lock();
-       entries = rcu_dereference(ids->entries);
-       if(lid >= entries->size) {
+       if(lid >= ids->size) {
                rcu_read_unlock();
                return NULL;
        }
-       out = entries->p[lid];
+
+       /* 
+        * Note: The following two read barriers are corresponding
+        * to the two write barriers in grow_ary(). They guarantee 
+        * the writes are seen in the same order on the read side. 
+        * smp_rmb() has effect on all CPUs.  rcu_dereference()
+        * is used if there are data dependency between two reads, and 
+        * has effect only on Alpha.
+        */
+       smp_rmb(); /* prevent indexing old array with new size */
+       entries = rcu_dereference(ids->entries);
+       out = entries[lid].p;
        if(out == NULL) {
                rcu_read_unlock();
                return NULL;