[JF:20005] [Draft] kernel-doc 2.6.34 filesystems/Locking
Seiji Kaneko
skaneko @ a2.mbn.or.jp
2010年 5月 23日 (日) 23:04:55 JST
かねこです。題記のものを。
33 からの変更点は削除だけなので、何もなければリリース予定です。
------>8------------>8------------>8------------>8------------>8
=========================================================
これは、
Linux-2.6.34/Documentation/filesystems/Locking の和訳です。
翻訳団体: JF プロジェクト < http://www.linux.or.jp/JF/ >
更新日 : 2010/5/21
翻訳者 : Seiji Kaneko < skaneko at mbn dot or 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 --------------------------
prototypes:
int (*d_revalidate)(struct dentry *, int);
int (*d_hash) (struct dentry *, struct qstr *);
int (*d_compare) (struct dentry *, struct qstr *, 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);
#locking rules:
ロッキングルール
# none have BKL
# dcache_lock rename_lock ->d_lock may block
#d_revalidate: no no no yes
#d_hash no no no yes
#d_compare: no yes no no
#d_delete: yes no yes no
#d_release: no no no yes
#d_iput: no no no yes
#d_dname: no no no no
いずれも BKL は持ちません。
dcache_lock rename_lock ->d_lock block 可能性
d_revalidate: いいえ いいえ いいえ はい
d_hash いいえ いいえ いいえ はい
d_compare: いいえ はい いいえ いいえ
d_delete: はい いいえ はい いいえ
d_release: いいえ いいえ いいえ はい
d_iput: いいえ いいえ いいえ はい
d_dname: いいえ いいえ いいえ いいえ
--------------------------- inode_operations ---------------------------
prototypes:
int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameid
ata *);
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 *,int);
int (*rmdir) (struct inode *,struct dentry *);
int (*mknod) (struct inode *,struct dentry *,int,dev_t);
int (*rename) (struct inode *, struct dentry *,
struct inode *, struct dentry *);
int (*readlink) (struct dentry *, char __user *,int);
int (*follow_link) (struct dentry *, struct nameidata *);
void (*truncate) (struct inode *);
int (*permission) (struct inode *, int, struct nameidata *);
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 *);
#locking rules:
# all may block, none have BKL
ロッキングルール
すべてブロック可能性あり、BKL は持ちません。
i_mutex(inode)
#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
#truncate: yes (see below)
#setattr: yes
#permission: no
#getattr: no
#setxattr: yes
#getxattr: no
#listxattr: no
#removexattr: yes
lookup: はい
create: はい
link: はい (両方で)
mknod: はい
symlink: はい
mkdir: はい
unlink: はい (両方で)
rmdir: はい (両方で) (下記参照)
rename: はい (すべて) (下記参照)
readlink: いいえ
follow_link: いいえ
truncate: はい (下記参照)
setattr: はい
permission: いいえ
getattr: いいえ
setxattr: はい
getxattr: いいえ
listxattr: いいえ
removexattr: はい
# Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on
#victim.
# 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() - 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
#passed).
#
#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 ---------------------------
prototypes:
struct inode *(*alloc_inode)(struct super_block *sb);
void (*destroy_inode)(struct inode *);
void (*dirty_inode) (struct inode *);
int (*write_inode) (struct inode *, int);
void (*drop_inode) (struct inode *);
void (*delete_inode) (struct inode *);
void (*put_super) (struct super_block *);
void (*write_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 (*clear_inode) (struct inode *);
void (*umount_begin) (struct super_block *);
int (*show_options)(struct seq_file *, struct vfsmount *);
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);
#locking rules:
# All may block.
ロッキングルール
すべてブロックの可能性あり。
# None have BKL
# s_umount
#alloc_inode:
#destroy_inode:
#dirty_inode: (must not sleep)
#write_inode:
#drop_inode: !!!inode_lock!!!
#delete_inode:
#put_super: write
#write_super: read
#sync_fs: read
#freeze_fs: read
#unfreeze_fs: read
#statfs: no
#remount_fs: maybe (see below)
#clear_inode:
#umount_begin: no
#show_options: no (namespace_sem)
#quota_read: no (see below)
#quota_write: no (see below)
どれも BKL を取得しない。
s_umount
alloc_inode:
destroy_inode:
dirty_inode: (sleep 不可)
write_inode:
drop_inode: !!!inode_lock!!!
delete_inode:
put_super: 書き込み
write_super: 読み出し
sync_fs: 読み出し
freeze_fs: 読み出し
unfreeze_fs: 読み出し
statfs: いいえ
remount_fs: 恐らく (下記参照)
clear_inode:
umount_begin: いいえ
show_options: いいえ (namespace_sem)
quota_read: いいえ (下記参照)
quota_write: いいえ (下記参照)
#->remount_fs() will have the s_umount exclusive lock if it's already mounted.
#When called from get_sb_single, it does NOT have the s_umount lock.
#->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.
->remount_fs() は既にマウントされているなら、s_umount 排他ロックを持って
いるでしょう。get_sb_single から呼ばれる場合には、s_umount ロックを持って
いることは*ありません*。
->quota_read() と ->quota_write() の両方とも、quota ファイルを扱う処理はこ
れだけであることが quota コードによって保証されています (管理者が何かを壊し
たくて、quota 有効時に quota ファイルに対して書き込みを行おうとしない限り)。
ロックに関するこれ以外の詳細については、dquot_operations セクションを参照
ください。
--------------------------- file_system_type ---------------------------
prototypes:
int (*get_sb) (struct file_system_type *, int,
const char *, void *, struct vfsmount *);
void (*kill_sb) (struct super_block *);
#locking rules:
ロッキングルール
# may block BKL
#get_sb yes no
#kill_sb yes no
ブロックの可能性 BKL
get_sb あり いいえ
kill_sb あり いいえ
#->get_sb() returns error or 0 with locked superblock attached to the vfsmount
#(exclusive on ->s_umount).
#->kill_sb() takes a write-locked superblock, does all shutdown work on it,
#unlocks and drops the reference.
->get_sb() は、vfsmount にロックされたスーパーブロックがアタッチされた状
態 (つまり、->s_umount と排他です) なら 0 を返し、それ以外ではエラーを返します 。
->kill_sb() は書き込みロックされたスーパーブロックを取り、それに対して全て
のシャットダウン処理を行い、ロックを解除して参照を解放します。
--------------------------- address_space_operations --------------------------
prototypes:
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);
int (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
loff_t offset, unsigned long nr_segs);
int (*launder_page) (struct page *);
#locking rules:
# All except set_page_dirty may block
ロッキングルール:
set_page_dirty 以外はブロックの可能性があります。
# BKL PageLocked(page) i_sem
#writepage: no yes, unlocks (see below)
#readpage: no yes, unlocks
#sync_page: no maybe
#writepages: no
#set_page_dirty no no
#readpages: no
#write_begin: no locks the page yes
#write_end: no yes, unlocks yes
#perform_write: no n/a yes
#bmap: no
#invalidatepage: no yes
#releasepage: no yes
#direct_IO: no
#launder_page: no yes
BKL PageLocked(page) i_sem
writepage: いいえ はい。アンロックします (下記参照)。
readpage: いいえ はい。アンロックします。
sync_page: いいえ 可能性あり
writepages: いいえ
set_page_dirty いいえ いいえ
readpages: いいえ
write_begin: いいえ ページをロックします はい
write_end: いいえ はい。アンロックします。 はい
perform_write: いいえ 該当せず はい
bmap: いいえ
invalidatepage: いいえ はい
releasepage: いいえ はい
direct_IO: いいえ
launder_page: いいえ はい
# ->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
#completion.
->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
#name.
ファイルシステムは、->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
#writepage.
ファイルシステムが 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
#well-defined...
->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
#mapping->io_pages.
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. All
#instances do not actually need the BKL. Please, keep it that way and don't
#breed new callers.
->bmap() は現在のところ、一部のファイルシステムとスワッパで提供さ
れている従来互換 ioctl() (FIBMAP) で使われています。後者の利用は
将来消滅方向です。全てのインスタンスは、実際は BKL を必要としてい
ません。この方向性を守って、新規の利用を行わないようにしてください。
# ->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 の場合、カー
ネルはファイルシステムがバッファに対して個別の要求事項を持っていな
いと判断します。
# ->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 を返し、できなかった場合にはエラーを返します。ページが再マップさ
れて再度ダーティに戻されることを避けるため、処理全体でロックをとっ
たままにしておく必要があります。
# Note: currently almost all instances of address_space methods are
#using BKL for internal serialization and that's one of the worst sources
#of contention. Normally they are calling library functions (in fs/buffer.c)
#and pass foo_get_block() as a callback (on local block-based filesystems,
#indeed). BKL is not needed for library stuff and is usually taken by
#foo_get_block(). It's an overkill, since block bitmaps can be protected by
#internal fs locking and real critical areas are much smaller than the areas
#filesystems protect now.
注意: 現在ほとんど全てのアドレス空間メソッドのインスタンスで、内部
の同期のために BKL を用いており、これが競合の最大の主原因の一つに
なっています。通常これらはライブラリ関数を呼び出し (fs/buffer.c
内の)、(ローカルのブロックベースのファイルシステムでは) コールバッ
クとして foo_get_block() を渡します。BKL はライブラリの類では必要
なわけではなく、通常は foo_get_block() で取得されます。ブロックビ
ットマップは内部のファイルシステムロックで保護することもでき、真に
クリティカルな領域はファイルシステム保護範囲よりずっと小さいため、
これはやりすぎです。
----------------------- file_lock_operations ------------------------------
prototypes:
void (*fl_insert)(struct file_lock *); /* lock insertion callback */
void (*fl_remove)(struct file_lock *); /* lock removal callback */
void (*fl_copy_lock)(struct file_lock *, struct file_lock *);
void (*fl_release_private)(struct file_lock *);
#locking rules:
# BKL may block
#fl_insert: yes no
#fl_remove: yes no
#fl_copy_lock: yes no
#fl_release_private: yes yes
ロッキングルール:
BKL ブロックの可能性
fl_insert: はい いいえ
fl_remove: はい いいえ
fl_copy_lock: はい いいえ
fl_release_private: はい はい
----------------------- lock_manager_operations ---------------------------
prototypes:
int (*fl_compare_owner)(struct file_lock *, struct file_lock *);
void (*fl_notify)(struct file_lock *); /* unblock callback */
void (*fl_copy_lock)(struct file_lock *, struct file_lock *);
void (*fl_release_private)(struct file_lock *);
void (*fl_break)(struct file_lock *); /* break_lease callback */
#locking rules:
# BKL may block
#fl_compare_owner: yes no
#fl_notify: yes no
#fl_copy_lock: yes no
#fl_release_private: yes yes
#fl_break: yes no
ロッキングルール:
BKL ブロックの可能性
fl_compare_owner: はい いいえ
fl_notify: はい いいえ
fl_copy_lock: はい いいえ
fl_release_private: はい はい
fl_break: はい いいえ
# Currently only NFSD and NLM provide instances of this class. None of the
#them block. If you have out-of-tree instances - please, show up. Locking
#in that area will change.
現在は NFSD と NLM のみがこのクラスのインスタンスを提供しています。
いずれもブロックしません。ツリー外のインスタンスを作成する場合は、
お願いですから公表してください。このエリアのロック規則には変更があ
るかもしれません。
--------------------------- buffer_head -----------------------------------
prototypes:
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 -----------------------
prototypes:
int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);
int (*media_changed) (struct gendisk *);
int (*revalidate_disk) (struct gendisk *);
#locking rules:
# BKL bd_sem
#open: yes yes
#release: yes yes
#ioctl: yes no
#media_changed: no no
#revalidate_disk: no no
ロッキングルール:
BKL bd_sem
open: はい はい
release: はい はい
ioctl: はい いいえ
media_changed: いいえ いいえ
revalidate_disk: いいえ いいえ
#The last two are called only from check_disk_change().
最後のふたつは check_disk_change() からのみ呼ばれます。
--------------------------- file_operations -------------------------------
prototypes:
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 *);
int (*ioctl) (struct inode *, struct file *, unsigned int,
unsigned long);
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 *, struct dentry *, 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 (*dir_notify)(struct file *, unsigned long);
};
#locking rules:
# All except ->poll() may block.
# BKL
#llseek: no (see below)
#read: no
#aio_read: no
#write: no
#aio_write: no
#readdir: no
#poll: no
#ioctl: yes (see below)
#unlocked_ioctl: no (see below)
#compat_ioctl: no
#mmap: no
#open: no
#flush: no
#release: no
#fsync: no (see below)
#aio_fsync: no
#fasync: no
#lock: yes
#readv: no
#writev: no
#sendfile: no
#sendpage: no
#get_unmapped_area: no
#check_flags: no
#dir_notify: no
ロッキングルール:
->poll() 以外はブロックする可能性があります。
BKL
llseek: いいえ (下記参照)
read: いいえ
aio_read: いいえ
write: いいえ
aio_write: いいえ
readdir: いいえ
poll: いいえ
ioctl: はい (下記参照)
unlocked_ioctl: いいえ (下記参照)
compat_ioctl: いいえ
mmap: いいえ
open: いいえ
flush: いいえ
release: いいえ
fsync: いいえ (下記参照)
aio_fsync: いいえ
fasync: いいえ
lock: はい
readv: いいえ
writev: いいえ
sendfile: いいえ
sendpage: いいえ
get_unmapped_area: いいえ
check_flags: いいえ
dir_notify: いいえ
#->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
#semaphore. Note some filesystems (i.e. remote ones) provide no
#protection for i_size so you will need to use the BKL.
->llseek() ロック処理は llseek 自体から、個々の llseek の実装側に移されま
した。作成しているファイルシステムが generic_file_llseek() を使っていない
なら、自分の ->llseek() 実装中で必要なロックの取得と解放を行う必要がありま
す。多くのファイルシステムでは、inode セマフォを確保する処理はおそらく安
全です。但し、一部のファイルシステム (特にリモートのもの) は i_size に対す
る保護を提供しないため、BKL を使う必要がおそらくあるでしょう。
#Note: ext2_release() was *the* source of contention on fs-intensive
#loads and dropping BKL on ->release() helps to get rid of that (we still
#grab BKL for cases when we close a file that had been opened r/w, but that
#can and should be done using the internal locking with smaller critical areas).
#Current worst offender is ext2_get_block()...
注意: ext2_release() はファイルシステムへの負荷集中時、競合のまさに主要因
となり、->release() 時に BKL を解放することは問題対処の助けになります (そ
れでも読み書きモードで開いたファイルのクローズなどの場合には BKL をつかみ
ますが、それはより狭いクリティカル領域の内部のロックで処理可能ですし、
そうすべきでもあります)。現在の最悪の犯罪者は ext2_get_block() です…
#->fasync() is called without BKL protection, and 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() は BKL による保護なしに呼ばれ、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 の類のものについ
ては全てのコンポーネントの構造体がなくなってしまうことです。これ以外にも一
杯理由があって、現在のインターフェースはぐちゃぐちゃとしか思えません ……
#->ioctl() on regular files is superceded by the ->unlocked_ioctl() that
#doesn't take the BKL.
通常のファイルに対する ->ioctl() は、BKL を取得しない ->unlocked_ioctl()
に置き換えられます。
#->read on directories probably must go away - we should just enforce -EISDIR
#in sys_read() and friends.
ディレクトリに対する ->read は捨てるべきなのでしょう。単に sys_read() と
そのお仲間では -EISDIR を使うよう強く推奨することによって。
#->fsync() has i_mutex on inode.
->fsync() は inode 上で i_mutex を保持します。
--------------------------- dquot_operations -------------------------------
prototypes:
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
#operations.
FS 再帰とは、->quota_read() や ->quota_write() をスーパーブロック操作から
呼ぶことです。
#More details about quota locking can be found in fs/dquot.c.
クオータロック処理の詳細については、fs/dquot.c に記載されています。
--------------------------- vm_operations_struct -----------------------------
prototypes:
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:
# BKL mmap_sem PageLocked(page)
#open: no yes
#close: no yes
#fault: no yes can return with page locked
#page_mkwrite: no yes can return with page locked
#access: no yes
ロッキングルール:
BKL 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
#VM_IO | VM_PFNMAP VMAs.
->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)
(何かを壊したか、何かが壊れていることに気がついて、自分でそれを直さない場合
は - 少なくともここに記載してください)
#ipc/shm.c::shm_delete() - may need BKL.
#->read() and ->write() in many drivers are (probably) missing BK
ipc/shm.c::shm_delete() - 恐らく BKL が必要
->read() および ->write() : 多くのドライバで (恐らく) BK が抜けている。
------>8------------>8------------>8------------>8------------>8
--
Seiji Kaneko
JF メーリングリストの案内