LEASEロック


LEASEロックは、設定された共有/排他ロックを貸し出します。貸し出し対象のファイルに、別プロセスが参照し競合が発生した場合、貸し出しているプロセスにシグナルが送られ、/proc/sys/fs/lease-break-time:45秒後にそのロックが解除されます。以下はその検証を行うサンプルです。このシグナルでリースしているプロセスは、ロックが解除されてもいい旨の処理をする事になります。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>

// コンパイルで宣言されていないと怒られた
# define F_SETLEASE      1024

void sigio(int sig)
{
       printf("someone access\n");
}

int main(int argc, char *argv[])
{
       int     fd;
       struct sigaction sa = { .sa_handler = sigio };
       sigaction(SIGIO, &sa, NULL);

       fd = open("/tmp/test", O_RDWR);
       fcntl(fd, F_SETLEASE, F_WRLCK);
       while (1) {
               sleep(1);
       }
       return 0;
}

実行結果

[kitamura@localhost test]$ echo "LEASE lock" > /tmp/test
[kitamura@localhost test]$ ./lease &
[1] 4751
[kitamura@localhost test]$ date ; cat /tmp/test  ; date
2012年 10月 24日 水曜日 11:22:35 JST
someone access
LEASE lock
2012年 10月 24日 水曜日 11:23:20 JST
[kitamura@localhost test]$
[kitamura@localhost test]$ date ; cat /tmp/test  ; date
2012年 10月 24日 水曜日 11:23:33 JST
LEASE lock
2012年 10月 24日 水曜日 11:23:34 JST
[kitamura@localhost test]$
cat /tmp/testは45秒後に/tmp/testに参照している。この時、リースしているプロセスにSIGIOが送られ、sigio()が動作している。なお次の/tmp/testの参照ではウエイトしていない。リースは解除されている事になる。

実装

LEASEロックは、F_SETLEASEでのfcntlシステムコールで設定する。この時do_fcntl_add_lease()がコールされここでLEASEロックの設定が行われる。

lease_alloc()でキャッシュからfile_lockオブジェクトを取得し、lease_init()をコールして初期化する。初期化からロックは全ファイルに渡って行われる。 fl->fl_lmops = &lease_manager_opsのコールバック関数で、LEASEプロセスにシグナルを送る。

fasync_alloc()のfasync_structオブジェクトは、本来キャラクタデバイスのFASYNCシグナル処理(入力毎にシグナルを送る。)のためであるが、LEASEロックのシグナル送信も、このfasync_structオブジェクトで行っている。

inode下に__vfs_setlease()でロックオブジェクトを、file_lockオブジェクト下にfasync_insert_entry()で、fasync_structオブジェクトを登録する。
static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg)
{
       struct file_lock *fl, *ret;
       struct fasync_struct *new;
       int error;

       fl = lease_alloc(filp, arg);
       if (IS_ERR(fl))
               return PTR_ERR(fl);

       new = fasync_alloc();
       if (!new) {
               locks_free_lock(fl);
               return -ENOMEM;
       }
       ret = fl;
       lock_flocks();
       error = __vfs_setlease(filp, arg, &ret);
       if (error) {
               unlock_flocks();
               locks_free_lock(fl);
               goto out_free_fasync;
       }
       if (ret != fl)
               locks_free_lock(fl);

       if (!fasync_insert_entry(fd, filp, &ret->fl_fasync, new))
               new = NULL;

       error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
       unlock_flocks();

out_free_fasync:
       if (new)
               fasync_free(new);
       return error;
}

static int lease_init(struct file *filp, int type, struct file_lock *fl)
{
       if (assign_type(fl, type) != 0)
               return -EINVAL;

       fl->fl_owner = current->files;
       fl->fl_pid = current->tgid;

       fl->fl_file = filp;
       fl->fl_flags = FL_LEASE;
       fl->fl_start = 0;
       fl->fl_end = OFFSET_MAX;
       fl->fl_ops = NULL;
       fl->fl_lmops = &lease_manager_ops;
       return 0;
}
__vfs_setlease()からgeneric_add_lease()がコールされる。最初に競合のチェックが行われる。共有ロックの場合、ファイルが書き込みで使用されていないか、また排他ロックの場合、ファイルがどこからも参照されていないかチェックする。

次にLEASEロック間での競合が無いか、inodeのロックリストを走査することでチェックする。if (fl->fl_file == filp)は、同じFILEオブジェクトを有するプロセスが、LEASEロックを有していて、そしてそのファイルのLEASEロックを取得しようとする場合で、この場合LEASEロックの変更で、file_lockオブジェクトのコールバック関数lm_change()が、そしてlocks_insert_lock()で、file_lockをリストする。
int generic_add_lease(struct file *filp, long arg, struct file_lock **flp)
{
       struct file_lock *fl, **before, **my_before = NULL, *lease;
       struct dentry *dentry = filp->f_path.dentry;
       struct inode *inode = dentry->d_inode;
       int error;

       lease = *flp;

       error = -EAGAIN;
       if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
               goto out;
       if ((arg == F_WRLCK)
           && ((dentry->d_count > 1)
               || (atomic_read(&inode->i_count) > 1)))
               goto out;

       error = -EAGAIN;
       for (before = &inode->i_flock;
                       ((fl = *before) != NULL) && IS_LEASE(fl);
                       before = &fl->fl_next) {
               if (fl->fl_file == filp) {
                       my_before = before;
                       continue;
               }
               if (arg == F_WRLCK)
                       goto out;
               if (fl->fl_flags & FL_UNLOCK_PENDING)
                       goto out;
       }

       if (my_before != NULL) {
               error = lease->fl_lmops->lm_change(my_before, arg);
               if (!error)
                       *flp = *my_before;
               goto out;
       }

       error = -EINVAL;
       if (!leases_enable)
               goto out;

       locks_insert_lock(before, lease);
       return 0;

out:
       return error;
}

補足

LEASEロックのブレイクは、他のプロセスからの参照によるが、読み込みでオープンされたら、ロックを共有ロック(ダウングレード)に、書き込みでオープンされたらロックの解除と、別々の管理としている

最終更新 2012/10/26 16:20:37 - north
(2012/10/24 20:09:10 作成)


検索

アクセス数
3588662
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。