mirror of
				https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
				synced 2025-10-31 07:02:06 +00:00 
			
		
		
		
	 1da177e4c3
			
		
	
	
		1da177e4c3
		
	
	
	
	
		
			
			Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
		
			
				
	
	
		
			142 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			142 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| Using RCU to Protect Read-Mostly Arrays
 | |
| 
 | |
| 
 | |
| Although RCU is more commonly used to protect linked lists, it can
 | |
| also be used to protect arrays.  Three situations are as follows:
 | |
| 
 | |
| 1.  Hash Tables
 | |
| 
 | |
| 2.  Static Arrays
 | |
| 
 | |
| 3.  Resizeable Arrays
 | |
| 
 | |
| Each of these situations are discussed below.
 | |
| 
 | |
| 
 | |
| Situation 1: Hash Tables
 | |
| 
 | |
| Hash tables are often implemented as an array, where each array entry
 | |
| has a linked-list hash chain.  Each hash chain can be protected by RCU
 | |
| as described in the listRCU.txt document.  This approach also applies
 | |
| to other array-of-list situations, such as radix trees.
 | |
| 
 | |
| 
 | |
| Situation 2: Static Arrays
 | |
| 
 | |
| Static arrays, where the data (rather than a pointer to the data) is
 | |
| located in each array element, and where the array is never resized,
 | |
| have not been used with RCU.  Rik van Riel recommends using seqlock in
 | |
| this situation, which would also have minimal read-side overhead as long
 | |
| as updates are rare.
 | |
| 
 | |
| Quick Quiz:  Why is it so important that updates be rare when
 | |
| 	     using seqlock?
 | |
| 
 | |
| 
 | |
| Situation 3: Resizeable Arrays
 | |
| 
 | |
| Use of RCU for resizeable arrays is demonstrated by the grow_ary()
 | |
| function used by the System V IPC code.  The array is used to map from
 | |
| semaphore, message-queue, and shared-memory IDs to the data structure
 | |
| that represents the corresponding IPC construct.  The grow_ary()
 | |
| function does not acquire any locks; instead its caller must hold the
 | |
| ids->sem semaphore.
 | |
| 
 | |
| The grow_ary() function, shown below, does some limit checks, allocates a
 | |
| new ipc_id_ary, copies the old to the new portion of the new, initializes
 | |
| the remainder of the new, updates the ids->entries pointer to point to
 | |
| the new array, and invokes ipc_rcu_putref() to free up the old array.
 | |
| Note that rcu_assign_pointer() is used to update the ids->entries pointer,
 | |
| which includes any memory barriers required on whatever architecture
 | |
| you are running on.
 | |
| 
 | |
| 	static int grow_ary(struct ipc_ids* ids, int newsize)
 | |
| 	{
 | |
| 		struct ipc_id_ary* new;
 | |
| 		struct ipc_id_ary* old;
 | |
| 		int i;
 | |
| 		int size = ids->entries->size;
 | |
| 
 | |
| 		if(newsize > IPCMNI)
 | |
| 			newsize = IPCMNI;
 | |
| 		if(newsize <= size)
 | |
| 			return newsize;
 | |
| 
 | |
| 		new = ipc_rcu_alloc(sizeof(struct kern_ipc_perm *)*newsize +
 | |
| 				    sizeof(struct ipc_id_ary));
 | |
| 		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;
 | |
| 		}
 | |
| 		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.
 | |
| 		 */
 | |
| 		rcu_assign_pointer(ids->entries, new);
 | |
| 
 | |
| 		ipc_rcu_putref(old);
 | |
| 		return newsize;
 | |
| 	}
 | |
| 
 | |
| The ipc_rcu_putref() function decrements the array's reference count
 | |
| and then, if the reference count has dropped to zero, uses call_rcu()
 | |
| to free the array after a grace period has elapsed.
 | |
| 
 | |
| The array is traversed by the ipc_lock() function.  This function
 | |
| indexes into the array under the protection of rcu_read_lock(),
 | |
| using rcu_dereference() to pick up the pointer to the array so
 | |
| that it may later safely be dereferenced -- memory barriers are
 | |
| required on the Alpha CPU.  Since the size of the array is stored
 | |
| with the array itself, there can be no array-size mismatches, so
 | |
| a simple check suffices.  The pointer to the structure corresponding
 | |
| to the desired IPC object is placed in "out", with NULL indicating
 | |
| a non-existent entry.  After acquiring "out->lock", the "out->deleted"
 | |
| flag indicates whether the IPC object is in the process of being
 | |
| deleted, and, if not, the pointer is returned.
 | |
| 
 | |
| 	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;
 | |
| 
 | |
| 		rcu_read_lock();
 | |
| 		entries = rcu_dereference(ids->entries);
 | |
| 		if(lid >= entries->size) {
 | |
| 			rcu_read_unlock();
 | |
| 			return NULL;
 | |
| 		}
 | |
| 		out = entries->p[lid];
 | |
| 		if(out == NULL) {
 | |
| 			rcu_read_unlock();
 | |
| 			return NULL;
 | |
| 		}
 | |
| 		spin_lock(&out->lock);
 | |
| 
 | |
| 		/* ipc_rmid() may have already freed the ID while ipc_lock
 | |
| 		 * was spinning: here verify that the structure is still valid
 | |
| 		 */
 | |
| 		if (out->deleted) {
 | |
| 			spin_unlock(&out->lock);
 | |
| 			rcu_read_unlock();
 | |
| 			return NULL;
 | |
| 		}
 | |
| 		return out;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| Answer to Quick Quiz:
 | |
| 
 | |
| 	The reason that it is important that updates be rare when
 | |
| 	using seqlock is that frequent updates can livelock readers.
 | |
| 	One way to avoid this problem is to assign a seqlock for
 | |
| 	each array entry rather than to the entire array.
 |