[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 --------------------------
prototypes:
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 ---------------------------
prototypes:
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
ロッキングルール
すべてブロック可能性あり。
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
#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
#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() - 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
#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 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
#alloc_inode:
#destroy_inode:
#dirty_inode:
#write_inode:
#drop_inode: !!!inode->i_lock!!!
#evict_inode:
#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)
s_umount
alloc_inode:
destroy_inode:
dirty_inode:
write_inode:
drop_inode: !!!inode->i_lock!!!
evict_inode:
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 ---------------------------
prototypes:
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 --------------------------
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);
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
#writepages:
#set_page_dirty no
#readpages:
#write_begin: locks the page yes
#write_end: yes, unlocks yes
#bmap:
#invalidatepage: yes
#releasepage: yes
#freepage: yes
#direct_IO:
#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: 可能性あり
writepages:
set_page_dirty いいえ
readpages:
write_begin: ページをロックします はい
write_end: はい。アンロックします はい
bmap:
invalidatepage: はい
releasepage: はい
freepage: はい
direct_IO:
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
#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. 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 ------------------------------
prototypes:
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 ---------------------------
prototypes:
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 -----------------------------------
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 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:
ロッキングルール:
bd_mutex
#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
#check_disk_change().
media_changed、unlock_native_capacity、revalidate_disk は
check_disk_change() からのみ呼ばれます。
#swap_slot_free_notify is called with swap_lock and sometimes the page lock
#held.
swap_slot_free_notify は swap_lock を保持した状態で呼ばれ、ページロックを
保持している場合もあります。
--------------------------- 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 *);
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 -------------------------------
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:
# 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
#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)
(何かを壊したか、何かが壊れていることに気がついて、自分でそれを直さない場合
は - 少なくともここに記載してください)
---------- >8 ---------- >8
--
Seiji Kaneko
JF メーリングリストの案内