POSIXロック


POSIXロックは、F_SETLK/F_SETLKWでfcntlシステムコールすると、fcntl_setlk()コールする事で設定されます。最初にfcntlシステムコールで指定したstruct flock __user *を、カーネル空間へと複写して、ロックするinodeを取得します。

このinodeが強制ロックで書き込みモードでマップされていたらロックできません。flock_to_posix_lock()は、struct flockに設定されているユーザ引数から、struct file_lockを適切に初期化します。

switch (flock.l_type)は、ファイルが読み込みモードでオープンされていないのに、共有ロック設定しようとしている。とか言ったチェックをします。

do_lock_file_wait()でロックを設定します。以降の処理はロック設定中にそのファイルがクローズされた場合の処理です(たぶん)。fcheck(fd)でファイルIDのFILEオブジェクトを改めて取得し、それが処理対象としているFILEオブジェクトと同じかチェックします。もし、異なっているとそのファイルIDのファイルはクローズされました。その時、ロック処理がロック設定なら、flock.l_type = F_UNLCKとしgoto againでそのロックを解除します。
int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
               struct flock __user *l)
{
       struct file_lock *file_lock = locks_alloc_lock();
       struct flock flock;
       struct inode *inode;
       struct file *f;
       int error;

       if (file_lock == NULL)
               return -ENOLCK;

       error = -EFAULT;
       if (copy_from_user(&flock, l, sizeof(flock)))
               goto out;

       inode = filp->f_path.dentry->d_inode;

       if (mandatory_lock(inode) && mapping_writably_mapped(filp->f_mapping)) {
               error = -EAGAIN;
               goto out;
       }

again:
       error = flock_to_posix_lock(filp, file_lock, &flock);
       if (error)
               goto out;
       if (cmd == F_SETLKW) {
               file_lock->fl_flags |= FL_SLEEP;
       }
       
       error = -EBADF;
       switch (flock.l_type) {
       case F_RDLCK:
               if (!(filp->f_mode & FMODE_READ))
                       goto out;
               break;
       case F_WRLCK:
               if (!(filp->f_mode & FMODE_WRITE))
                       goto out;
               break;
       case F_UNLCK:
               break;
       default:
               error = -EINVAL;
               goto out;
       }

       error = do_lock_file_wait(filp, cmd, file_lock);

       spin_lock(&current->files->file_lock);
       f = fcheck(fd);
       spin_unlock(&current->files->file_lock);
       if (!error && f != filp && flock.l_type != F_UNLCK) {
               flock.l_type = F_UNLCK;
               goto again;
       }

out:
       locks_free_lock(file_lock);
       return error;
}
flock_to_posix_lock()から vfs_lock_file()がコールされ、FILEコールバック関数のfilp->f_op->lock()が定義されていない(inodeのコールバックと言う事)と、__posix_lock_file()がコールされます。

POSIXロックは、ファイルの任意の領域に、しかも複数に設定できます。そのため共有ロック領域の一部に排他ロックを設定する場合、ロックを分割したり、また、同じロックで連続する領域となる場合、そのロックを解除して改めて領域を拡大してロックしたりしています。まあ、そのような処理でごちゃごちゃしています。そんなことで、new_fl/new_fl2と前もって、2つのfile_lockオブジェクトを取得しています。(設定中に、もう1つfile_lockオブジェクトが必要となって、locks_alloc_lock()で取得できなかった場合、設定中のロックを元に戻す処理が必要となるためだと・・・)

で、ポイントとなるところは、対象となるロックを走査すると時、posix_same_owner()としている所です。ここでは、fl->fl_owner = current->filesであるfl->fl_ownerが、同じかどうかチェックします。POSIXロックは同じFILEオブジェクトでなければ(マルチスレッド間)、有効とならないと言うわけです。なお、if (request->fl_flags & FL_ACCESS)は、ロックが競合するかどうかチェックするだけです。
static int __posix_lock_file(struct inode *inode, struct file_lock *request, struct  file_lock *conflock)
{
       struct file_lock *fl;
       struct file_lock *new_fl = NULL;
       struct file_lock *new_fl2 = NULL;
       struct file_lock *left = NULL;
       struct file_lock *right = NULL;
       struct file_lock **before;
       int error, added = 0;

       if (!(request->fl_flags & FL_ACCESS) &&
           (request->fl_type != F_UNLCK ||
            request->fl_start != 0 || request->fl_end != OFFSET_MAX)) {
               new_fl = locks_alloc_lock();
               new_fl2 = locks_alloc_lock();
       }

       lock_flocks();
       if (request->fl_type != F_UNLCK) {
               for_each_lock(inode, before) {
                       fl = *before;
                       if (!IS_POSIX(fl))
                               continue;
                       if (!posix_locks_conflict(request, fl))
                               continue;
                       if (conflock)
                               __locks_copy_lock(conflock, fl);
                       error = -EAGAIN;
                       if (!(request->fl_flags & FL_SLEEP))
                               goto out;
                       error = -EDEADLK;
                       if (posix_locks_deadlock(request, fl))
                               goto out;
                       error = FILE_LOCK_DEFERRED;
                       locks_insert_block(fl, request);
                       goto out;
               }
       }

       error = 0;
       if (request->fl_flags & FL_ACCESS)
               goto out;

       
       before = &inode->i_flock;

       while ((fl = *before) && (!IS_POSIX(fl) ||
                                 !posix_same_owner(request, fl))) {
               before = &fl->fl_next;
       }

       while ((fl = *before) && posix_same_owner(request, fl)) {
               if (request->fl_type == fl->fl_type) {
                      if (fl->fl_end < request->fl_start - 1)
                               goto next_lock;
                      if (fl->fl_start - 1 > request->fl_end)
                               break;

                       if (fl->fl_start > request->fl_start)
                               fl->fl_start = request->fl_start;
                       else
                               request->fl_start = fl->fl_start;
                       if (fl->fl_end < request->fl_end)
                               fl->fl_end = request->fl_end;
                       else
                               request->fl_end = fl->fl_end;
                       if (added) {
                               locks_delete_lock(before);
                               continue;
                       }
                       request = fl;
                       added = 1;
               }
               else {
                       if (fl->fl_end < request->fl_start)
                               goto next_lock;
                       if (fl->fl_start > request->fl_end)
                               break;
                       if (request->fl_type == F_UNLCK)
                               added = 1;
                       if (fl->fl_start < request->fl_start)
                               left = fl;
                       if (fl->fl_end > request->fl_end) {
                               right = fl;
                               break;
                       }
                       if (fl->fl_start >= request->fl_start) {
                               if (added) {
                                       locks_delete_lock(before);
                                       continue;
                               }
                               locks_wake_up_blocks(fl);
                               fl->fl_start = request->fl_start;
                               fl->fl_end = request->fl_end;
                               fl->fl_type = request->fl_type;
                               locks_release_private(fl);
                               locks_copy_private(fl, request);
                               request = fl;
                               added = 1;
                       }
               }
       next_lock:
               before = &fl->fl_next;
       }

       error = -ENOLCK; /* "no luck" */
       if (right && left == right && !new_fl2)
               goto out;

       error = 0;
       if (!added) {
               if (request->fl_type == F_UNLCK) {
                       if (request->fl_flags & FL_EXISTS)
                               error = -ENOENT;
                       goto out;
               }

               if (!new_fl) {
                       error = -ENOLCK;
                       goto out;
               }
               locks_copy_lock(new_fl, request);
               locks_insert_lock(before, new_fl);
               new_fl = NULL;
       }
       if (right) {
               if (left == right) {
                       left = new_fl2;
                       new_fl2 = NULL;
                       locks_copy_lock(left, right);
                       locks_insert_lock(before, left);
               }
               right->fl_start = request->fl_end + 1;
               locks_wake_up_blocks(right);
       }
       if (left) {
               left->fl_end = request->fl_start - 1;
               locks_wake_up_blocks(left);
       }
out:
       unlock_flocks();
       if (new_fl)
               locks_free_lock(new_fl);
       if (new_fl2)
               locks_free_lock(new_fl2);
       return error;
}

static int posix_same_owner(struct file_lock *fl1, struct file_lock *fl2)
{
       if (fl1->fl_lmops && fl1->fl_lmops->lm_compare_owner)
               return fl2->fl_lmops == fl1->fl_lmops &&
                       fl1->fl_lmops->lm_compare_owner(fl1, fl2);
       return fl1->fl_owner == fl2->fl_owner;
}

最終更新 2012/11/07 19:13:59 - north
(2012/11/07 19:13:59 作成)


検索

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