[JF:20127] [Release] linux 3.6 Locking

Seiji Kaneko skaneko @ a2.mbn.or.jp
2012年 10月 2日 (火) 07:58:06 JST

---------- >8 ---------- >8
TITL: Locking
CONT: Linux VFS の内部ロック取得規則の解説
NAME: filesystems/Locking
JDAT: 2012/10/02
BVER: 3.6
AUTH: unknown
TRNS: Seiji Kaneko < skaneko at a2 dot mbn dot or dot jp >
---------- >8 ---------- >8
Linux-3.6/Documentation/filesystems/Locking の和訳です。
翻訳団体: JF プロジェクト < http://linuxjf.sourceforge.jp/ >
更新日 : 2012/10/02
翻訳者 : Seiji Kaneko < skaneko at mbn dot or dot jp >
査読者 : Takeshi Hamasaki < hmatrjp at users dot sourceforge dot jp >
#	The text below describes the locking rules for VFS-related methods.
#It is (believed to be) up-to-date. *Please*, if you change anything in
#prototypes or locking protocols - update this file. And update the relevant
#instances in the tree, don't leave that to maintainers of filesystems/devices/
#etc. At the very least, put the list of dubious cases in the end of this file.
#Don't turn it into log - maintainers of out-of-the-tree code are supposed to
#be able to use diff(1).
#	Thing currently missing here: socket operations. Alexey?
以下の文書は VFS 関連メソッドでのロック取得規則を記載したものです。これは
最新版 (のはず) です。したがって、プロトタイプやロックプロトコルに何らかの
変更を加えた場合には、*必ず* このファイルを更新してください。また、ツリー
メンテナは diff(1) を使うことができるという想定ですので。
現在不足しているのは、ソケット処理です。Alexey さん、いかが。

--------------------------- dentry_operations --------------------------
	int (*d_revalidate)(struct dentry *, unsigned int);
	int (*d_hash)(const struct dentry *, const struct inode *,
			struct qstr *);
	int (*d_compare)(const struct dentry *, const struct inode *,
			const struct dentry *, const struct inode *,
			unsigned int, const char *, const struct qstr *);
	int (*d_delete)(struct dentry *);
	void (*d_release)(struct dentry *);
	void (*d_iput)(struct dentry *, struct inode *);
	char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen);
	struct vfsmount *(*d_automount)(struct path *path);
	int (*d_manage)(struct dentry *, bool);

#locking rules:
#		rename_lock	->d_lock	may block	rcu-walk
#d_revalidate:	no		no		yes (ref-walk)	maybe
#d_hash		no		no		no		maybe
#d_compare:	yes		no		no		maybe
#d_delete:	no		yes		no		no
#d_release:	no		no		yes		no
#d_prune:        no              yes             no              no
#d_iput:		no		no		yes		no
#d_dname:	no		no		no		no
#d_automount:	no		no		yes		no
#d_manage:	no		no		yes (ref-walk)	maybe
		rename_lock	->d_lock	ブロック可能性	rcu-walk
d_revalidate:	いいえ		いいえ		はい (ref-walk)	可能性有
d_hash		いいえ		いいえ		いいえ		可能性有
d_compare:	はい		いいえ		いいえ		可能性有
d_delete:	いいえ		はい		いいえ		いいえ
d_release:	いいえ		いいえ		はい		いいえ
d_prune:        いいえ          はい            いいえ          いいえ
d_iput:		いいえ		いいえ		はい		いいえ
d_dname:	いいえ		いいえ		いいえ		いいえ
d_automount:	いいえ		いいえ		はい		いいえ
d_manage:	いいえ		いいえ		はい (ref-walk)	可能性有

--------------------------- inode_operations ---------------------------
	int (*create) (struct inode *,struct dentry *,umode_t, bool);
	struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
	int (*link) (struct dentry *,struct inode *,struct dentry *);
	int (*unlink) (struct inode *,struct dentry *);
	int (*symlink) (struct inode *,struct dentry *,const char *);
	int (*mkdir) (struct inode *,struct dentry *,umode_t);
	int (*rmdir) (struct inode *,struct dentry *);
	int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
	int (*rename) (struct inode *, struct dentry *,
			struct inode *, struct dentry *);
	int (*readlink) (struct dentry *, char __user *,int);
	void * (*follow_link) (struct dentry *, struct nameidata *);
	void (*put_link) (struct dentry *, struct nameidata *, void *);
	void (*truncate) (struct inode *);
	int (*permission) (struct inode *, int, unsigned int);
	int (*get_acl)(struct inode *, int);
	int (*setattr) (struct dentry *, struct iattr *);
	int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *);
	int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
	ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
	ssize_t (*listxattr) (struct dentry *, char *, size_t);
	int (*removexattr) (struct dentry *, const char *);
	int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len);
	void (*update_time)(struct inode *, struct timespec *, int);
	int (*atomic_open)(struct inode *, struct dentry *,
				struct file *, unsigned open_flag,
				umode_t create_mode, int *opened);

#locking rules:
#	all may block
#lookup:		yes
#create:		yes
#link:		yes (both)
#mknod:		yes
#symlink:	yes
#mkdir:		yes
#unlink:		yes (both)
#rmdir:		yes (both)	(see below)
#rename:		yes (all)	(see below)
#readlink:	no
#follow_link:	no
#put_link:	no
#truncate:	yes		(see below)
#setattr:	yes
#permission:	no (may not block if called in rcu-walk mode)
#get_acl:	no
#getattr:	no
#setxattr:	yes
#getxattr:	no
#listxattr:	no
#removexattr:	yes
#fiemap:		no
#update_time:	no
#atomic_open:	yes
lookup:		はい
create:		はい
link:		はい (両方で)
mknod:		はい
symlink:	はい
mkdir:		はい
unlink:		はい (両方で)
rmdir:		はい (両方で)	(下記参照)
rename:		はい (すべて)	(下記参照)
readlink:	いいえ
follow_link:	いいえ
put_link:	いいえ
truncate:	はい		(下記参照)
setattr:	はい
permission:	いいえ (rcu-walk モードで呼ばれた場合、ブロック不可)
get_acl:	いいえ
getattr:	いいえ
setxattr:	はい
getxattr:	いいえ
listxattr:	いいえ
removexattr:	はい
fiemap:		いいえ
update_time:	いいえ
atomic_open:	はい

#	Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on
#	cross-directory ->rename() has (per-superblock) ->s_vfs_rename_sem.
#	->truncate() is never called directly - it's a callback, not a
#method. It's called by vmtruncate() - deprecated library function normally used by
#->setattr(). Locking information above applies to that call (i.e. is
#inherited from ->setattr() - vmtruncate() is used when ATTR_SIZE had been
#See Documentation/filesystems/directory-locking for more detailed discussion
#of the locking scheme for directory operations.
       加えて、->rmdir(), ->unlink() および ->rename() は操作される側の ->i_mutex
       ディレクトリを渡る ->rename() は、スーパーブロックごとの
       ->s_vfs_rename_sem を持ちます。->truncate() は直接は呼ばれません。
       これはコールバックで、メソッドではないためです。これは vmtruncate()
       - 通常は ->setattr() から使われる非推奨となったライブラリ関数 - から呼ばれま
       す。上記のロック情報はこの呼び出しにも適用されます (つまり、
       ->setattr() から継承されます。vmtruncate() は ATTR_SIZE が渡された

Documentation/filesystems/directory-locking を参照ください。
--------------------------- super_operations ---------------------------
	struct inode *(*alloc_inode)(struct super_block *sb);
	void (*destroy_inode)(struct inode *);
	void (*dirty_inode) (struct inode *, int flags);
	int (*write_inode) (struct inode *, struct writeback_control *wbc);
	int (*drop_inode) (struct inode *);
	void (*evict_inode) (struct inode *);
	void (*put_super) (struct super_block *);
	int (*sync_fs)(struct super_block *sb, int wait);
	void (*write_super_lockfs) (struct super_block *);
	void (*unlockfs) (struct super_block *);
	int (*statfs) (struct dentry *, struct kstatfs *);
	int (*remount_fs) (struct super_block *, int *, char *);
	void (*umount_begin) (struct super_block *);
	int (*show_options)(struct seq_file *, struct dentry *);
	ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
	ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
	int (*bdev_try_to_free_page)(struct super_block*, struct page*, gfp_t);

#locking rules:
#	All may block [not true, see below]
	すべてブロックの可能性あり (ほんとうは異なる。以下参照)。
#			s_umount
#drop_inode:				!!!inode->i_lock!!!
#put_super:		write
#sync_fs:		read
#freeze_fs:		write
#unfreeze_fs:		write
#statfs:			maybe(read)	(see below)
#remount_fs:		write
#umount_begin:		no
#show_options:		no		(namespace_sem)
#quota_read:		no		(see below)
#quota_write:		no		(see below)
#bdev_try_to_free_page:	no		(see below)
drop_inode:				!!!inode->i_lock!!!
put_super:		書き込み
sync_fs:		読み出し
freeze_fs:		書き込み
unfreeze_fs:		書き込み
statfs:			可能性あり(読み出し) (下記参照)
remount_fs:		書き込み
umount_begin:		いいえ
show_options:		いいえ		(namespace_sem)
quota_read:		いいえ		(下記参照)
quota_write:		いいえ		(下記参照)
bdev_try_to_free_page:	いいえ		(下記参照)

#->statfs() has s_umount (shared) when called by ustat(2) (native or
#compat), but that's an accident of bad API; s_umount is used to pin
#the superblock down when we only have dev_t given us by userland to
#identify the superblock.  Everything else (statfs(), fstatfs(), etc.)
#doesn't hold it when calling ->statfs() - superblock is pinned down
#by resolving the pathname passed to syscall.
#->quota_read() and ->quota_write() functions are both guaranteed to
#be the only ones operating on the quota file by the quota code (via
#dqio_sem) (unless an admin really wants to screw up something and
#writes to quota files with quotas on). For other details about locking
#see also dquot_operations section.
#->bdev_try_to_free_page is called from the ->releasepage handler of
#the block device inode.  See there for more details.
->statfs() は、ustat(2) (ネイティブまたは互換の両方共) から呼ばれた場合に、
s_umount (共有) ロックを取得しますが、これはまずい API の事故ともいうべきも
のです。ここで、s_umount は、ユーザランドから呼ばれた場合にスーパブロックを
特定するための情報が dev_t しかない場合に、スーパブロックをピン止めする処理
に用いられます。他の全ての場合 (statfs(), fstatfs() など) では、statfs() を
呼ぶ際には s_umount を保持しません。こちらの場合には、syscall から渡された
->quota_read() と ->quota_write() の両方とも、quota ファイルを扱う処理はこ
れだけであることが quota コードによって保証されています (管理者が何かを壊し
たくて、quota 有効時に quota ファイルに対して書き込みを行おうとしない限り)。
ロックに関するこれ以外の詳細については、dquot_operations セクションを参照
->bdev_try_to_free_page は、ブロックデバイス inode の ->releasepage ハンド

--------------------------- file_system_type ---------------------------
	int (*get_sb) (struct file_system_type *, int,
		       const char *, void *, struct vfsmount *);
	struct dentry *(*mount) (struct file_system_type *, int,
		       const char *, void *);
	void (*kill_sb) (struct super_block *);
#locking rules:
#		may block	
#get_sb		yes		
#mount		yes
#kill_sb		yes		
get_sb		あり		
mount		あり
kill_sb		あり		

#->mount() returns ERR_PTR or the root dentry; its superblock should be locked
#on return.
#->kill_sb() takes a write-locked superblock, does all shutdown work on it,
#unlocks and drops the reference.
->mount() は、ERR_PTR または root dentry を返します。ファイルシステムの
->kill_sb() は書き込みロックされたスーパーブロックを取り、それに対して全て

--------------------------- address_space_operations --------------------------
	int (*writepage)(struct page *page, struct writeback_control *wbc);
	int (*readpage)(struct file *, struct page *);
	int (*sync_page)(struct page *);
	int (*writepages)(struct address_space *, struct writeback_control *);
	int (*set_page_dirty)(struct page *page);
	int (*readpages)(struct file *filp, struct address_space *mapping,
			struct list_head *pages, unsigned nr_pages);
	int (*write_begin)(struct file *, struct address_space *mapping,
				loff_t pos, unsigned len, unsigned flags,
				struct page **pagep, void **fsdata);
	int (*write_end)(struct file *, struct address_space *mapping,
				loff_t pos, unsigned len, unsigned copied,
				struct page *page, void *fsdata);
	sector_t (*bmap)(struct address_space *, sector_t);
	int (*invalidatepage) (struct page *, unsigned long);
	int (*releasepage) (struct page *, int);
	void (*freepage)(struct page *);
	int (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
			loff_t offset, unsigned long nr_segs);
	int (*get_xip_mem)(struct address_space *, pgoff_t, int, void **,
				unsigned long *);
	int (*migratepage)(struct address_space *, struct page *, struct page *);
	int (*launder_page)(struct page *);
	int (*is_partially_uptodate)(struct page *, read_descriptor_t *, unsigned long);
	int (*error_remove_page)(struct address_space *, struct page *);
	int (*swap_activate)(struct file *);
	int (*swap_deactivate)(struct file *);

#locking rules:
#	All except set_page_dirty and freepage may block
        set_page_dirty と freepage 以外はブロックの可能性があります。

#			PageLocked(page)	i_mutex
#writepage:		yes, unlocks (see below)
#readpage:		yes, unlocks
#sync_page:		maybe
#set_page_dirty		no
#write_begin:		locks the page		yes
#write_end:		yes, unlocks		yes
#invalidatepage:		yes
#releasepage:		yes
#freepage:		yes
#get_xip_mem:					maybe
#migratepage:		yes (both)
#launder_page:		yes
#is_partially_uptodate:	yes
#error_remove_page:	yes
#swap_activate:		no
#swap_deactivate:	no
			PageLocked(page)	i_mutex
writepage:		はい。アンロックします (下記参照)
readpage:		はい。アンロックします
sync_page:		可能性あり
set_page_dirty		いいえ
write_begin:		ページをロックします		はい
write_end:		はい。アンロックします		はい
invalidatepage:		はい
releasepage:		はい
freepage:		はい
get_xip_mem:					可能性あり
migratepage:		はい (両方共)
launder_page:		はい
is_partially_uptodate:	はい
error_remove_page:	はい
swap_activate:		いいえ
swap_deactivate:	いいえ

#	->write_begin(), ->write_end(), ->sync_page() and ->readpage()
#may be called from the request handler (/dev/loop)
	->write_begin(), ->write_end(), ->sync_page() および
	->readpage() は、リクエストハンドラ (/dev/loop) から呼ばれる可能

#	->readpage() unlocks the page, either synchronously or via I/O
	->readpage() は、同期的あるいは I/O 完了時にページをアンロックし

#	->readpages() populates the pagecache with the passed pages and starts
#I/O against them.  They come unlocked upon I/O completion.
	->readpages() は渡されたページでページキャッシュを埋め、それに対
	して I/O を開始します。I/O 完了でロックが解除されます。

#	->writepage() is used for two purposes: for "memory cleansing" and for
#"sync".  These are quite different operations and the behaviour may differ
#depending upon the mode.
	->writepage() には二つの目的があります。ひとつは「メモリ消去」で、
	もうひとつは ”sync" です。この二つはまったく異なった処理で、挙動

#If writepage is called for sync (wbc->sync_mode != WBC_SYNC_NONE) then
#it *must* start I/O against the page, even if that would involve
#blocking on in-progress I/O.
writepage が sync のために呼ばれた (wbc->sync_mode != WBC_SYNC_NONE) 場
合、進行中の I/O 処理をブロックすることになっても、対象ページに対して I/O

#If writepage is called for memory cleansing (sync_mode ==
#WBC_SYNC_NONE) then its role is to get as much writeout underway as
#possible.  So writepage should try to avoid blocking against
#currently-in-progress I/O.
writepage がメモリ消去のために呼ばれた (sync_mode == WBC_SYNC_NONE) 場合、
writepage は進行中の I/O をブロックすることを避けるべきです。

#If the filesystem is not called for "sync" and it determines that it
#would need to block against in-progress I/O to be able to start new I/O
#against the page the filesystem should redirty the page with
#redirty_page_for_writepage(), then unlock the page and return zero.
#This may also be done to avoid internal deadlocks, but rarely.
ファイルシステムが sync のために呼ばれていない場合に、対象ページに対して新
しい I/O を開始するために進行中の I/O 処理をブロックする必要があると判断さ
れたならば、ファイルシステムは redirty_page_for_writepage() を使って対象
ページを dirty に戻して、その後ページをアンロックして戻り値 0 で戻るべきです。

#If the filesystem is called for sync then it must wait on any
#in-progress I/O and then start new I/O.
ファイルシステムが sync のために呼ばれている場合には、進行中の I/O がある
場合にはそれを待ち、その後で新しい I/O を開始します。

#The filesystem should unlock the page synchronously, before returning to the
#caller, unless ->writepage() returns special WRITEPAGE_ACTIVATE
#value. WRITEPAGE_ACTIVATE means that page cannot really be written out
#currently, and VM should stop calling ->writepage() on this page for some
#time. VM does this by moving page to the head of the active list, hence the
ファイルシステムは、->writepage() が特別な WRITEPAGE_ACTIVATE 値を返して
WRITEPAGE_ACTIVATE は該当ページを書き戻すことが現時点では本当にできず、VM
がそのページに ->writepage() を発行するのを一時的に止めるべきことを意味し
ます。VM はこの処理を、当該ページを active

#Unless the filesystem is going to redirty_page_for_writepage(), unlock the page
#and return zero, writepage *must* run set_page_writeback() against the page,
#followed by unlocking it.  Once set_page_writeback() has been run against the
#page, write I/O can be submitted and the write I/O completion handler must run
#end_page_writeback() once the I/O is complete.  If no I/O is submitted, the
#filesystem must run end_page_writeback() against the page before returning from
ファイルシステムが redirty_page_for_writepage() を行って、ページをアンロ
ックし、0 を返そうとしているのではない限り、writepage は
set_page_writeback() を対象ページに必ず実行し、引き続いてアンロックしなけ
ればいけません。当該ページに対して set_page_writeback() を実行した後は、write
I/O を投入することができ、write I/O 完了ハンドラは I/O の完了後
end_page_writeback() を一回実行しなければいけません。I/O が投入されていな
い場合は、ファイルシステムは writepage から戻る前に対象ページに
end_page_writeback() を実行しなければいけません。

#That is: after 2.5.12, pages which are under writeout are *not* locked.  Note,
#if the filesystem needs the page to be locked during writeout, that is ok, too,
#the page is allowed to be unlocked at any point in time between the calls to
#set_page_writeback() and end_page_writeback().
この趣旨は、2.5.12 以降では、書き出し処理中のページはロックされて*いない*
ページは set_page_writeback() の呼び出しから end_page_writeback() の呼び出し

#Note, failure to run either redirty_page_for_writepage() or the combination of
#set_page_writeback()/end_page_writeback() on a page submitted to writepage
#will leave the page itself marked clean but it will be tagged as dirty in the
#radix tree.  This incoherency can lead to all sorts of hard-to-debug problems
#in the filesystem like having dirty inodes at umount and losing written data.
注意点として、redirty_page_for_writepage() の実行か、
set_page_writeback()/end_page_writeback() の実行の組み合わせのどちらかで
writepage 投入が失敗したページは、radix ツリー上でダーティにタグ付けられて

#	->sync_page() locking rules are not well-defined - usually it is called
#with lock on page, but that is not guaranteed. Considering the currently
#existing instances of this method ->sync_page() itself doesn't look
	->sync_page() のロック規則は明確には定義されていません。この関数
	そもそも sync_page() 自体が明確に定義されてはいないようです。

#	->writepages() is used for periodic writeback and for syscall-initiated
#sync operations.  The address_space should start I/O against at least
#*nr_to_write pages.  *nr_to_write must be decremented for each page which is
#written.  The address_space implementation may write more (or less) pages
#than *nr_to_write asks for, but it should try to be reasonably close.  If
#nr_to_write is NULL, all dirty pages must be written.
	->writepages() は定期的なライトバックと、syscall で起動される
	sync 処理に用いられます。アドレス空間では、少なくとも
	*nr_to_write 数のページに対して I/O を開始すべきで、ページが書き
	込まれる度に *nr_to_write から 1 づつ引いていきます。アドレス空間
	処理の実装では、要求された *nr_to_write 以上 (または以下) のペー
	うよう頑張るべきです。nr_to_write が NULL の場合は、全てのダーテ

#writepages should _only_ write pages which are present on
writepages は、mapping->io_pages に存在するページ*のみ*を書き込むよう

#	->set_page_dirty() is called from various places in the kernel
#when the target page is marked as needing writeback.  It may be called
#under spinlock (it cannot block) and is sometimes called with the page
#not locked.
	->set_page_dirty() は、対象となるページを書き込むべきであるとマー
	spinlock 状態から呼ぶこともできますし (ブロック不可です)、ロック

#	->bmap() is currently used by legacy ioctl() (FIBMAP) provided by some
#filesystems and by the swapper. The latter will eventually go away.  Please,
#keep it that way and don't breed new callers.
	->bmap() は現在のところ、一部のファイルシステムで提供されている従来
	互換 ioctl() (FIBMAP) とスワッパで使われています。後者の利用は

#	->invalidatepage() is called when the filesystem must attempt to drop
#some or all of the buffers from the page when it is being truncated.  It
#returns zero on success.  If ->invalidatepage is zero, the kernel uses
#block_invalidatepage() instead.
	->invalidatepage() は、ファイルシステムがページから一部あるいは全
	合に呼ばれます。成功時には 0 が返ります。もし ->invalidatepage が
	0 ならば、カーネルは block_invalidatepage() を代わりに使用します。

#	->releasepage() is called when the kernel is about to try to drop the
#buffers from the page in preparation for freeing it.  It returns zero to
#indicate that the buffers are (or may be) freeable.  If ->releasepage is zero,
#the kernel assumes that the fs has no private interest in the buffers.
	->releasepage() は、カーネルがページ解放の準備のためページからバ
	ッファを捨てようとする際に呼ばれます。バッファが解放可能である (と
	考えられる) 場合、0 が返ります。->releasepage が 0 の場合、カー

#	->freepage() is called when the kernel is done dropping the page
#from the page cache.
        ->freepage() は、カーネルがページキャッシュからのページ削除を完了し

#	->launder_page() may be called prior to releasing a page if
#it is still found to be dirty. It returns zero if the page was successfully
#cleaned, or an error value if not. Note that in order to prevent the page
#getting mapped back in and redirtied, it needs to be kept locked
#across the entire operation.
	->launder_page() は、ダーティであることが発見されたページの解放に
	0 を返し、できなかった場合にはエラーを返します。ページが再マップさ

#	->swap_activate will be called with a non-zero argument on
#files backing (non block device backed) swapfiles. A return value
#of zero indicates success, in which case this file can be used for
#backing swapspace. The swapspace operations will be proxied to the
#address space operations.
	->swap_activate はスワップファイルとして使うファイル (ブロックデバ
	イスではないスワップファイル) に対し、0 以外の引数で呼ばれます。0

#	->swap_deactivate() will be called in the sys_swapoff()
#path after ->swap_activate() returned success.
	->swap_deactivate() は、sys_swapoff() パス内で、->swap_activate()

----------------------- file_lock_operations ------------------------------
	void (*fl_copy_lock)(struct file_lock *, struct file_lock *);
	void (*fl_release_private)(struct file_lock *);

#locking rules:
#			file_lock_lock	may block
#fl_copy_lock:		yes		no
#fl_release_private:	maybe		no
			file_lock_lock	ブロックの可能性
fl_copy_lock:		はい		いいえ
fl_release_private:	可能性あり	いいえ

----------------------- lock_manager_operations ---------------------------
	int (*lm_compare_owner)(struct file_lock *, struct file_lock *);
	void (*lm_notify)(struct file_lock *);  /* unblock callback */
	int (*lm_grant)(struct file_lock *, struct file_lock *, int);
	void (*lm_break)(struct file_lock *); /* break_lease callback */
	int (*lm_change)(struct file_lock **, int);

#locking rules:
#			file_lock_lock	may block
#lm_compare_owner:	yes		no
#lm_notify:		yes		no
#lm_grant:		no		no
#lm_break:		yes		no
#lm_change		yes		no
			file_lock_lock	ブロックの可能性
lm_compare_owner:	はい		いいえ
lm_notify:		はい		いいえ
lm_grant:		いいえ		いいえ
lm_break:		はい		いいえ
lm_change		はい		いいえ

--------------------------- buffer_head -----------------------------------
	void (*b_end_io)(struct buffer_head *bh, int uptodate);

#locking rules:
#	called from interrupts. In other words, extreme care is needed here.
#bh is locked, but that's all warranties we have here. Currently only RAID1,
#highmem, fs/buffer.c, and fs/ntfs/aops.c are providing these. Block devices
#call this method upon the IO completion.
	必要です。bh はロックされていますが、ここではそれが唯一の保護とい
	うことになります。現在 RAID1, highmem, fs/buffer.c と
	fs/nfs/app.c が本機能を提供しています。ブロックデバイスは I/O 完

--------------------------- block_device_operations -----------------------
	int (*open) (struct block_device *, fmode_t);
	int (*release) (struct gendisk *, fmode_t);
	int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
	int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
	int (*direct_access) (struct block_device *, sector_t, void **, unsigned long *);
	int (*media_changed) (struct gendisk *);
	void (*unlock_native_capacity) (struct gendisk *);
	int (*revalidate_disk) (struct gendisk *);
	int (*getgeo)(struct block_device *, struct hd_geometry *);
	void (*swap_slot_free_notify) (struct block_device *, unsigned long);

#locking rules:
#open:			yes
#release:		yes
#ioctl:			no
#compat_ioctl:		no
#direct_access:		no
#media_changed:		no
#unlock_native_capacity:	no
#revalidate_disk:	no
#getgeo:			no
#swap_slot_free_notify:	no	(see below)
open:			はい
release:		はい
ioctl:			いいえ
compat_ioctl:		いいえ
direct_access:		いいえ
media_changed:		いいえ
unlock_native_capacity:	いいえ
revalidate_disk:	いいえ
getgeo:			いいえ
swap_slot_free_notify:	いいえ (下記参照)

#media_changed, unlock_native_capacity and revalidate_disk are called only from
media_changed、unlock_native_capacity、revalidate_disk は
check_disk_change() からのみ呼ばれます。

#swap_slot_free_notify is called with swap_lock and sometimes the page lock
swap_slot_free_notify は swap_lock を保持した状態で呼ばれ、ページロックを

--------------------------- file_operations -------------------------------
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	int (*readdir) (struct file *, void *, filldir_t);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, loff_t start, loff_t end, int datasync);
	int (*aio_fsync) (struct kiocb *, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*readv) (struct file *, const struct iovec *, unsigned long,
			loff_t *);
	ssize_t (*writev) (struct file *, const struct iovec *, unsigned long,
			loff_t *);
	ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t,
			void __user *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t,
			loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long,
			unsigned long, unsigned long, unsigned long);
	int (*check_flags)(int);
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *,
			size_t, unsigned int);
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *,
			size_t, unsigned int);
	int (*setlease)(struct file *, long, struct file_lock **);
	long (*fallocate)(struct file *, int, loff_t, loff_t);

#locking rules:
#	All may block except for ->setlease.
#	No VFS locks held on entry except for ->setlease.
#->setlease has the file_list_lock held and must not sleep.
#	All except ->poll() may block.
       ->setlease 以外はブロックの可能性があります。
       ->setlease 以外は、呼び出し時に VFS ロックを保持しません。

->setlease は、file_list_lock を保持して呼ばれ、sleep してはいけません。

#->llseek() locking has moved from llseek to the individual llseek
#implementations.  If your fs is not using generic_file_llseek, you
#need to acquire and release the appropriate locks in your ->llseek().
#For many filesystems, it is probably safe to acquire the inode
#mutex or just to use i_size_read() instead.
#Note: this does not protect the file->f_pos against concurrent modifications
#since this is something the userspace has to take care about.
->llseek() ロック処理は llseek 自体から、個々の llseek の実装側に移されま
した。作成しているファイルシステムが generic_file_llseek() を使っていない
なら、自分の ->llseek() 実装中で必要なロックの取得と解放を行う必要がありま
す。多くのファイルシステムでは、inode mutex を確保する処理、および代わりに
単に i_size_read() を使う処理はおそらく安全です。

注意: この処理では、file->f_pos が同時に更新される場合の保護を行いません。

#->fasync() is responsible for maintaining the FASYNC bit in filp->f_flags.
#Most instances call fasync_helper(), which does that maintenance, so it's
#not normally something one needs to worry about.  Return values > 0 will be
#mapped to zero in the VFS layer.

->fasync() は、filp->f_flags 中の FASYNC ビットを維持する責任を負います。
ほとんどのインスタンスではメンテナンスを行う fasync_helper() を使っており、
普通はあまり心配する必要はない事項です。戻り値が >0 の場合は VFS レイヤで
0 にマップされています。

#->readdir() and ->ioctl() on directories must be changed. Ideally we would
#move ->readdir() to inode_operations and use a separate method for directory
#->ioctl() or kill the latter completely. One of the problems is that for
#anything that resembles union-mount we won't have a struct file for all
#components. And there are other reasons why the current interface is a mess...
ディレクトリに関する ->readdir() と ->ioctl() には変更が必要です。理想的に
は、readdir() を inode_operation 側に移して、ディレクトリの ->ioctl() に
は独立したメソッドを用いるか、(ディレクトリの ->ioctl() を) 完全に止めにし
てしまうかです。そうした場合の問題点の一つは、union-mount の類のものについ
杯理由があって、現在のインターフェースはぐちゃぐちゃとしか思えません ……

#->read on directories probably must go away - we should just enforce -EISDIR
#in sys_read() and friends.
ディレクトリに対する ->read は捨てるべきなのでしょう。単に sys_read() と
そのお仲間では -EISDIR を使うよう強く推奨することによって。

--------------------------- dquot_operations -------------------------------
	int (*write_dquot) (struct dquot *);
	int (*acquire_dquot) (struct dquot *);
	int (*release_dquot) (struct dquot *);
	int (*mark_dirty) (struct dquot *);
	int (*write_info) (struct super_block *, int);

#These operations are intended to be more or less wrapping functions that ensure
#a proper locking wrt the filesystem and call the generic quota operations.

#What filesystem should expect from the generic quota functions:

#		FS recursion	Held locks when called
#write_dquot:	yes		dqonoff_sem or dqptr_sem
#acquire_dquot:	yes		dqonoff_sem or dqptr_sem
#release_dquot:	yes		dqonoff_sem or dqptr_sem
#mark_dirty:	no		-
#write_info:	yes		dqonoff_sem
		FS 再帰		呼ばれるとき保持しているロック
write_dquot:	はい		dqonoff_sem または dqptr_sem
acquire_dquot:	はい		dqonoff_sem または dqptr_sem
release_dquot:	はい		dqonoff_sem または dqptr_sem
mark_dirty:	いいえ		-
write_info:	はい		dqonoff_sem

#FS recursion means calling ->quota_read() and ->quota_write() from superblock
FS 再帰とは、->quota_read() や ->quota_write() をスーパーブロック操作から

#More details about quota locking can be found in fs/dquot.c.
クオータロック処理の詳細については、fs/dquot.c に記載されています。

--------------------------- vm_operations_struct -----------------------------
	void (*open)(struct vm_area_struct*);
	void (*close)(struct vm_area_struct*);
	int (*fault)(struct vm_area_struct*, struct vm_fault *);
	int (*page_mkwrite)(struct vm_area_struct *, struct vm_fault *);
	int (*access)(struct vm_area_struct *, unsigned long, void*, int, int);

#locking rules:
#		mmap_sem	PageLocked(page)
#open:		yes
#close:		yes
#fault:		yes		can return with page locked
#page_mkwrite:	yes		can return with page locked
#access:		yes
		mmap_sem	PageLocked(ページ)
open:		はい
close:		はい
fault:		はい		ページをロックしたまま戻ってもよい
page_mkwrite:	はい		ページをロックしたまま戻ってもよい
access:		はい

#	->fault() is called when a previously not present pte is about
#to be faulted in. The filesystem must find and return the page associated
#with the passed in "pgoff" in the vm_fault structure. If it is possible that
#the page may be truncated and/or invalidated, then the filesystem must lock
#the page, then ensure it is not already truncated (the page lock will block
#subsequent truncate), and then return with VM_FAULT_LOCKED, and the page
#locked. The VM will unlock the page.
	->fault() は、以前存在しなかった pte をフォールトとして取り込もう
	とする際に呼ばれます。ファイルシステムは vm_fault 構造体中で
	pgoff として渡された対応ページを発見して、そのページを持って戻らな
	ければいけません。ページが切り詰められていたり (truncate)、無効に
	ずロックし、それまでに切り捨てられていないことを確認 (ページロック
	により後続の切り捨てはブロックされます) し、ページをロックした状態
	で VM_FAULT_LOCKED で返らなければなりません。VM がページをその後

#	->page_mkwrite() is called when a previously read-only pte is
#about to become writeable. The filesystem again must ensure that there are
#no truncate/invalidate races, and then return with the page locked. If
#the page has been truncated, the filesystem should not look up a new page
#like the ->fault() handler, but simply return with VM_FAULT_NOPAGE, which
#will cause the VM to retry the fault.
	->page_mkwrite() は、以前読み出しのみだった pte を書き込み可能にし
	truncate/invalidate 競合がないことを必ず確認し、ページをロックして
	戻ります。もしページが切り詰められている (truncate) 場合、ファイル
	システムは ->fault() ハンドラとは異なり新しいページを探さずに
	VM_FAULT_NOPAGE で返るべきです。これにより VM はフォールト処理を

#	->access() is called when get_user_pages() fails in
#acces_process_vm(), typically used to debug a process through
#/proc/pid/mem or ptrace.  This function is needed only for
	->access() は get_user_pages() が access_process_vm() 中で失敗
	した場合、典型的には /proc/pid/mem や ptrace を使ってプロセスのデ
	バッグを行っている場合、に呼ばれます。この関数は VM_IO | VM_PFNMAP
	VMA でのみ必要です。

#			Dubious stuff

#(if you break something or notice that it is broken and do not fix it yourself
#- at least put it here)
は - 少なくともここに記載してください)

---------- >8 ---------- >8

Seiji Kaneko

JF メーリングリストの案内