LEASEロックのブレイク
ファイルのオープン毎に、LEASEロックのチェックを__break_lease()をコールする事で行われます。まずブレイクプロセスのopenの書き込み/読み込みかに応じて、新規にfile_lockオブジェクトをF_WRLCK/F_RDLCKで割り当てます。このfile_lockオブジェクトはLEASEロックを取得するというのでなく、そのメンバーを、ブレイク処理のため(競合チェック/ウエイトリスト)に利用することにあるようです。
lock_flocks()は単にスピンロックを取得しているだけで、time_out_leases()が実際のロックを解除する処理です。この関数はLEASEロックを、ブレイク時間を越えたかどうかで走査してチェックしているだけです。従って、ブレイクするまでウエイトする事はありません。
inode->i_flockがNULL(いかなるロックが無い)、!IS_LEASE(flock)がLEASEロックは無い。場合、ロックブレイクにかかる処理は必要ありません。
locks_conflict()でロックの競合をチェックします。これはどちらかが排他ロックなら競合となります。(ここでの処理は、すでに対象ファイルはロックを有しています。)なお、このチェックではLEASEロックとしてでなく、POSIXロック/FLOCKロックを考慮しています。
i_have_this_lease = 1は、LEASEロックを有しているプロセスが、改めてopenしたケースの対応です。この場合、通常にopenされるのですが、同時にLEASEロックは解除されます。
for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next)で、すべてのLEASEロックをブレイク処理の設定を施します(実際のブレイク処理は、time_out_leases())。書き込み要求なら、全LEASEロックは解除(FL_UNLOCK_PENDING)とし、ブレイクタイムとしてfl->fl_break_timeに設定します。読み込み要求なら、排他ロックを共通ロックに変更(FL_DOWNGRADE_PENDING)とし、fl->fl_downgrade_timeにブレイクタイムとします。そしてfl->fl_lmops->lm_break()でこのロックを有しているプロセスにシグナルを送信します。
ブレイクタイムはjiffies + lease_break_time * HZで、現時のjiffiesから、lease_break_time * HZとなる。lease_break_timeは標準秒は45で、なお0の場合、ブレイクしないことになるため、break_time++としている。
restart:ラベルで、wait_event_interruptible_timeou()で、呼び出しプロセスが、LEASEロックが解除までのbreak_time秒ウエイトする。なお第二引数の条件となる!new_fl->fl_nextで、ここにはブロッカとなるロックオブジェクトが設定されている。たぶんこれを有しているプロセスが削除された場合でないか・・・。
なお、タイムアウトでwait_event_interruptible_timeou()から復帰すると、このロックブロックを削除し、再度time_out_leases()でロックの解除を行うことになる。
この2つのタイマは、対象とならないタイマは0となっていて、past_time()の引数が0のとき、falseを返すようになっている。もしtrueが返ってきたら、タイムアップで、該当するロックをlease_modify()で、F_RDLCK/F_UNLCKに変更する。
lock_flocks()は単にスピンロックを取得しているだけで、time_out_leases()が実際のロックを解除する処理です。この関数はLEASEロックを、ブレイク時間を越えたかどうかで走査してチェックしているだけです。従って、ブレイクするまでウエイトする事はありません。
inode->i_flockがNULL(いかなるロックが無い)、!IS_LEASE(flock)がLEASEロックは無い。場合、ロックブレイクにかかる処理は必要ありません。
locks_conflict()でロックの競合をチェックします。これはどちらかが排他ロックなら競合となります。(ここでの処理は、すでに対象ファイルはロックを有しています。)なお、このチェックではLEASEロックとしてでなく、POSIXロック/FLOCKロックを考慮しています。
i_have_this_lease = 1は、LEASEロックを有しているプロセスが、改めてopenしたケースの対応です。この場合、通常にopenされるのですが、同時にLEASEロックは解除されます。
for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next)で、すべてのLEASEロックをブレイク処理の設定を施します(実際のブレイク処理は、time_out_leases())。書き込み要求なら、全LEASEロックは解除(FL_UNLOCK_PENDING)とし、ブレイクタイムとしてfl->fl_break_timeに設定します。読み込み要求なら、排他ロックを共通ロックに変更(FL_DOWNGRADE_PENDING)とし、fl->fl_downgrade_timeにブレイクタイムとします。そしてfl->fl_lmops->lm_break()でこのロックを有しているプロセスにシグナルを送信します。
ブレイクタイムはjiffies + lease_break_time * HZで、現時のjiffiesから、lease_break_time * HZとなる。lease_break_timeは標準秒は45で、なお0の場合、ブレイクしないことになるため、break_time++としている。
restart:ラベルで、wait_event_interruptible_timeou()で、呼び出しプロセスが、LEASEロックが解除までのbreak_time秒ウエイトする。なお第二引数の条件となる!new_fl->fl_nextで、ここにはブロッカとなるロックオブジェクトが設定されている。たぶんこれを有しているプロセスが削除された場合でないか・・・。
なお、タイムアウトでwait_event_interruptible_timeou()から復帰すると、このロックブロックを削除し、再度time_out_leases()でロックの解除を行うことになる。
int __break_lease(struct inode *inode, unsigned int mode) { int error = 0; struct file_lock *new_fl, *flock; struct file_lock *fl; unsigned long break_time; int i_have_this_lease = 0; int want_write = (mode & O_ACCMODE) != O_RDONLY; new_fl = lease_alloc(NULL, want_write ? F_WRLCK : F_RDLCK); if (IS_ERR(new_fl)) return PTR_ERR(new_fl); lock_flocks(); time_out_leases(inode); flock = inode->i_flock; if ((flock == NULL) || !IS_LEASE(flock)) goto out; if (!locks_conflict(flock, new_fl)) goto out; for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) if (fl->fl_owner == current->files) i_have_this_lease = 1; break_time = 0; if (lease_break_time > 0) { break_time = jiffies + lease_break_time * HZ; if (break_time == 0) break_time++; } for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) { if (want_write) { if (fl->fl_flags & FL_UNLOCK_PENDING) continue; fl->fl_flags |= FL_UNLOCK_PENDING; fl->fl_break_time = break_time; } else { if (lease_breaking(flock)) continue; fl->fl_flags |= FL_DOWNGRADE_PENDING; fl->fl_downgrade_time = break_time; } fl->fl_lmops->lm_break(fl); } if (i_have_this_lease || (mode & O_NONBLOCK)) { error = -EWOULDBLOCK; goto out; } restart: break_time = flock->fl_break_time; if (break_time != 0) { break_time -= jiffies; if (break_time == 0) break_time++; } locks_insert_block(flock, new_fl); unlock_flocks(); error = wait_event_interruptible_timeout(new_fl->fl_wait, !new_fl->fl_next, break_time); lock_flocks(); __locks_delete_block(new_fl); if (error >= 0) { if (error == 0) time_out_leases(inode); for (flock = inode->i_flock; flock && IS_LEASE(flock); flock = flock->fl_next) { if (locks_conflict(new_fl, flock)) goto restart; } error = 0; } out: unlock_flocks(); locks_free_lock(new_fl); return error; }time_out_leases()でLEASEロックの解除を行う。解除チェック対象となるLEASEロックは、ロック状態が、FL_UNLOCK_PENDING(書き込みopen)/FL_DOWNGRADE_PENDING(読み込みopen)のロックオブジェクトで、ブレイクタイマfl->fl_downgrade_time/fl->fl_break_timeを経過タイムをチェックする。
この2つのタイマは、対象とならないタイマは0となっていて、past_time()の引数が0のとき、falseを返すようになっている。もしtrueが返ってきたら、タイムアップで、該当するロックをlease_modify()で、F_RDLCK/F_UNLCKに変更する。
static void time_out_leases(struct inode *inode) { struct file_lock **before; struct file_lock *fl; before = &inode->i_flock; while ((fl = *before) && IS_LEASE(fl) && lease_breaking(fl)) { if (past_time(fl->fl_downgrade_time)) lease_modify(before, F_RDLCK); if (past_time(fl->fl_break_time)) lease_modify(before, F_UNLCK); if (fl == *before) /* lease_modify may have freed fl */ before = &fl->fl_next; } }lease_manager_opsオペレーションの.lm_breakのコールバックは、 lease_break_callback()で、kill_fasy()でSIGIOシグナルを送信する。
static const struct lock_manager_operations lease_manager_ops = { .lm_break = lease_break_callback, .lm_release_private = lease_release_private_callback, .lm_change = lease_modify, }; static void lease_break_callback(struct file_lock *fl) { kill_fasync(&fl->fl_fasync, SIGIO, POLL_MSG); }