| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	JFFS2 LOCKING DOCUMENTATION | 
					
						
							|  |  |  | 	--------------------------- | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | At least theoretically, JFFS2 does not require the Big Kernel Lock | 
					
						
							|  |  |  | (BKL), which was always helpfully obtained for it by Linux 2.4 VFS | 
					
						
							|  |  |  | code. It has its own locking, as described below. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This document attempts to describe the existing locking rules for | 
					
						
							|  |  |  | JFFS2. It is not expected to remain perfectly up to date, but ought to | 
					
						
							|  |  |  | be fairly close. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	alloc_sem | 
					
						
							|  |  |  | 	--------- | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-22 15:13:40 +01:00
										 |  |  | The alloc_sem is a per-filesystem mutex, used primarily to ensure | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | contiguous allocation of space on the medium. It is automatically | 
					
						
							|  |  |  | obtained during space allocations (jffs2_reserve_space()) and freed | 
					
						
							|  |  |  | upon write completion (jffs2_complete_reservation()). Note that | 
					
						
							|  |  |  | the garbage collector will obtain this right at the beginning of | 
					
						
							|  |  |  | jffs2_garbage_collect_pass() and release it at the end, thereby | 
					
						
							|  |  |  | preventing any other write activity on the file system during a | 
					
						
							|  |  |  | garbage collect pass. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | When writing new nodes, the alloc_sem must be held until the new nodes | 
					
						
							|  |  |  | have been properly linked into the data structures for the inode to | 
					
						
							|  |  |  | which they belong. This is for the benefit of NAND flash - adding new | 
					
						
							|  |  |  | nodes to an inode may obsolete old ones, and by holding the alloc_sem | 
					
						
							|  |  |  | until this happens we ensure that any data in the write-buffer at the | 
					
						
							|  |  |  | time this happens are part of the new node, not just something that | 
					
						
							|  |  |  | was written afterwards. Hence, we can ensure the newly-obsoleted nodes | 
					
						
							|  |  |  | don't actually get erased until the write-buffer has been flushed to | 
					
						
							|  |  |  | the medium. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | With the introduction of NAND flash support and the write-buffer,  | 
					
						
							|  |  |  | the alloc_sem is also used to protect the wbuf-related members of the | 
					
						
							|  |  |  | jffs2_sb_info structure. Atomically reading the wbuf_len member to see | 
					
						
							|  |  |  | if the wbuf is currently holding any data is permitted, though. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Ordering constraints: See f->sem. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-22 15:13:40 +01:00
										 |  |  | 	File Mutex f->sem | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	--------------------- | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-22 15:13:40 +01:00
										 |  |  | This is the JFFS2-internal equivalent of the inode mutex i->i_sem. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | It protects the contents of the jffs2_inode_info private inode data, | 
					
						
							|  |  |  | including the linked list of node fragments (but see the notes below on | 
					
						
							|  |  |  | erase_completion_lock), etc. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The reason that the i_sem itself isn't used for this purpose is to | 
					
						
							|  |  |  | avoid deadlocks with garbage collection -- the VFS will lock the i_sem | 
					
						
							|  |  |  | before calling a function which may need to allocate space. The | 
					
						
							|  |  |  | allocation may trigger garbage-collection, which may need to move a | 
					
						
							|  |  |  | node belonging to the inode which was locked in the first place by the | 
					
						
							|  |  |  | VFS. If the garbage collection code were to attempt to lock the i_sem | 
					
						
							|  |  |  | of the inode from which it's garbage-collecting a physical node, this | 
					
						
							|  |  |  | lead to deadlock, unless we played games with unlocking the i_sem | 
					
						
							|  |  |  | before calling the space allocation functions. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Instead of playing such games, we just have an extra internal | 
					
						
							| 
									
										
										
										
											2008-04-22 15:13:40 +01:00
										 |  |  | mutex, which is obtained by the garbage collection code and also | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | by the normal file system code _after_ allocation of space. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Ordering constraints:  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	1. Never attempt to allocate space or lock alloc_sem with  | 
					
						
							|  |  |  | 	   any f->sem held. | 
					
						
							| 
									
										
										
										
											2008-04-22 15:13:40 +01:00
										 |  |  | 	2. Never attempt to lock two file mutexes in one thread. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	   No ordering rules have been made for doing so. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	erase_completion_lock spinlock | 
					
						
							|  |  |  | 	------------------------------ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This is used to serialise access to the eraseblock lists, to the | 
					
						
							|  |  |  | per-eraseblock lists of physical jffs2_raw_node_ref structures, and | 
					
						
							|  |  |  | (NB) the per-inode list of physical nodes. The latter is a special | 
					
						
							|  |  |  | case - see below. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | As the MTD API no longer permits erase-completion callback functions | 
					
						
							|  |  |  | to be called from bottom-half (timer) context (on the basis that nobody | 
					
						
							|  |  |  | ever actually implemented such a thing), it's now sufficient to use | 
					
						
							|  |  |  | a simple spin_lock() rather than spin_lock_bh(). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Note that the per-inode list of physical nodes (f->nodes) is a special | 
					
						
							|  |  |  | case. Any changes to _valid_ nodes (i.e. ->flash_offset & 1 == 0) in | 
					
						
							| 
									
										
										
										
											2008-04-22 15:13:40 +01:00
										 |  |  | the list are protected by the file mutex f->sem. But the erase code | 
					
						
							|  |  |  | may remove _obsolete_ nodes from the list while holding only the | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | erase_completion_lock. So you can walk the list only while holding the | 
					
						
							|  |  |  | erase_completion_lock, and can drop the lock temporarily mid-walk as | 
					
						
							|  |  |  | long as the pointer you're holding is to a _valid_ node, not an | 
					
						
							|  |  |  | obsolete one. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The erase_completion_lock is also used to protect the c->gc_task | 
					
						
							|  |  |  | pointer when the garbage collection thread exits. The code to kill the | 
					
						
							|  |  |  | GC thread locks it, sends the signal, then unlocks it - while the GC | 
					
						
							|  |  |  | thread itself locks it, zeroes c->gc_task, then unlocks on the exit path. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	inocache_lock spinlock | 
					
						
							|  |  |  | 	---------------------- | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This spinlock protects the hashed list (c->inocache_list) of the | 
					
						
							|  |  |  | in-core jffs2_inode_cache objects (each inode in JFFS2 has the | 
					
						
							|  |  |  | correspondent jffs2_inode_cache object). So, the inocache_lock | 
					
						
							|  |  |  | has to be locked while walking the c->inocache_list hash buckets. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-13 14:22:38 +01:00
										 |  |  | This spinlock also covers allocation of new inode numbers, which is | 
					
						
							|  |  |  | currently just '++->highest_ino++', but might one day get more complicated | 
					
						
							|  |  |  | if we need to deal with wrapping after 4 milliard inode numbers are used. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | Note, the f->sem guarantees that the correspondent jffs2_inode_cache | 
					
						
							|  |  |  | will not be removed. So, it is allowed to access it without locking | 
					
						
							|  |  |  | the inocache_lock spinlock.  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Ordering constraints:  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	If both erase_completion_lock and inocache_lock are needed, the | 
					
						
							|  |  |  | 	c->erase_completion has to be acquired first. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	erase_free_sem | 
					
						
							|  |  |  | 	-------------- | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-22 15:13:40 +01:00
										 |  |  | This mutex is only used by the erase code which frees obsolete node | 
					
						
							|  |  |  | references and the jffs2_garbage_collect_deletion_dirent() function. | 
					
						
							|  |  |  | The latter function on NAND flash must read _obsolete_ nodes to | 
					
						
							|  |  |  | determine whether the 'deletion dirent' under consideration can be | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | discarded or whether it is still required to show that an inode has | 
					
						
							|  |  |  | been unlinked. Because reading from the flash may sleep, the | 
					
						
							|  |  |  | erase_completion_lock cannot be held, so an alternative, more | 
					
						
							|  |  |  | heavyweight lock was required to prevent the erase code from freeing | 
					
						
							|  |  |  | the jffs2_raw_node_ref structures in question while the garbage | 
					
						
							|  |  |  | collection code is looking at them. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Suggestions for alternative solutions to this problem would be welcomed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	wbuf_sem | 
					
						
							|  |  |  | 	-------- | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This read/write semaphore protects against concurrent access to the | 
					
						
							|  |  |  | write-behind buffer ('wbuf') used for flash chips where we must write | 
					
						
							|  |  |  | in blocks. It protects both the contents of the wbuf and the metadata | 
					
						
							|  |  |  | which indicates which flash region (if any) is currently covered by  | 
					
						
							|  |  |  | the buffer. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Ordering constraints: | 
					
						
							|  |  |  | 	Lock wbuf_sem last, after the alloc_sem or and f->sem. | 
					
						
							| 
									
										
										
										
											2006-05-13 15:14:14 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	c->xattr_sem | 
					
						
							|  |  |  | 	------------ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This read/write semaphore protects against concurrent access to the | 
					
						
							|  |  |  | xattr related objects which include stuff in superblock and ic->xref. | 
					
						
							|  |  |  | In read-only path, write-semaphore is too much exclusion. It's enough | 
					
						
							|  |  |  | by read-semaphore. But you must hold write-semaphore when updating, | 
					
						
							|  |  |  | creating or deleting any xattr related object. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Once xattr_sem released, there would be no assurance for the existence | 
					
						
							|  |  |  | of those objects. Thus, a series of processes is often required to retry, | 
					
						
							|  |  |  | when updating such a object is necessary under holding read semaphore. | 
					
						
							|  |  |  | For example, do_jffs2_getxattr() holds read-semaphore to scan xref and | 
					
						
							|  |  |  | xdatum at first. But it retries this process with holding write-semaphore | 
					
						
							|  |  |  | after release read-semaphore, if it's necessary to load name/value pair | 
					
						
							|  |  |  | from medium. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Ordering constraints: | 
					
						
							|  |  |  | 	Lock xattr_sem last, after the alloc_sem. |