mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-03 18:20:25 +02:00 
			
		
		
		
	With this fix, all code paths should now be obtaining the page lock before f->sem. Reported-by: Szabó Tamás <sztomi89@gmail.com> Tested-by: Thomas Betker <thomas.betker@rohde-schwarz.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> Cc: stable@vger.kernel.org
		
			
				
	
	
		
			169 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			169 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
 | 
						|
	JFFS2 LOCKING DOCUMENTATION
 | 
						|
	---------------------------
 | 
						|
 | 
						|
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
 | 
						|
	---------
 | 
						|
 | 
						|
The alloc_sem is a per-filesystem mutex, used primarily to ensure
 | 
						|
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.
 | 
						|
 | 
						|
 | 
						|
	File Mutex f->sem
 | 
						|
	---------------------
 | 
						|
 | 
						|
This is the JFFS2-internal equivalent of the inode mutex i->i_sem.
 | 
						|
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
 | 
						|
mutex, which is obtained by the garbage collection code and also
 | 
						|
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.
 | 
						|
	2. Never attempt to lock two file mutexes in one thread.
 | 
						|
	   No ordering rules have been made for doing so.
 | 
						|
	3. Never lock a page cache page with f->sem held.
 | 
						|
 | 
						|
 | 
						|
	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
 | 
						|
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
 | 
						|
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.
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
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
 | 
						|
	--------------
 | 
						|
 | 
						|
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
 | 
						|
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.
 | 
						|
 | 
						|
 | 
						|
	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.
 |