| 
    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. 
 |