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