Re: RFC: Attach locks to cifsFileInfo instead of cifsInodeInfo

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



2011/11/17 Sachin Prabhu <sprabhu@xxxxxxxxxx>:
> Pavel,
>
> I've made some changes to the locking code which makes it easier to
> unlock all locks on an inode. This fixes the earlier problem where
> unlock  requests would only process the locks for that open file
> instance while ignoring the locks on other open file instances for that
> inode.
>
> However we still have one major issue. In case of unlocks, the current
> code will only return locks which are completely within the range
> specified by the unlock command. If we get an unlock request which
> divides a  brlock into 2, we ignore the unlock request for that brlock.
> This will result in a situation where the application finds a lock on a
> part of the file it expected to be unlocked. Should we check for these
> cases and return an EFAULT in cases where the unlock request will not
> completely unlock the entire range?

Yes, I think that we should report to the userspace to let the
application know about the failure. I found this on fcntl man page:

ENOLCK
Too many segment locks open, lock table is full, or a remote locking
protocol failed (e.g., locking over NFS).

I think this fits our scenario.

>
> Sachin Prabhu
>
> ----
>
> Attach locks to cifsFileInfo instead of cifsInodeInfo
>
> Locks in CIFS are attached to an open file instances ie. all
> lock/unlock requests made to the file server should contain the fileid
> which represents the open file instance. Grouping all brlocks according
> to the fileid therefore makes it easier to process especially when
> multiple lock or unlock requests for a file are to be made.
>
> Signed-off-by: Sachin Prabhu <sprabhu@xxxxxxxxxx>
>
> diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
> index 8f1fe32..8a71b0d 100644
> --- a/fs/cifs/cifsfs.c
> +++ b/fs/cifs/cifsfs.c
> @@ -949,7 +949,6 @@ cifs_init_once(void *inode)
>        struct cifsInodeInfo *cifsi = inode;
>
>        inode_init_once(&cifsi->vfs_inode);
> -       INIT_LIST_HEAD(&cifsi->llist);
>        mutex_init(&cifsi->lock_mutex);
>  }
>
> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> index 8238aa1..409d932 100644
> --- a/fs/cifs/cifsglob.h
> +++ b/fs/cifs/cifsglob.h
> @@ -527,6 +527,7 @@ struct cifs_search_info {
>  struct cifsFileInfo {
>        struct list_head tlist; /* pointer to next fid owned by tcon */
>        struct list_head flist; /* next fid (file instance) for this inode */
> +       struct list_head llist; /* brlocks for this file */
>        unsigned int uid;       /* allows finding which FileInfo structure */
>        __u32 pid;              /* process id who opened file */
>        __u16 netfid;           /* file id from remote */
> @@ -567,9 +568,9 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
>  */
>
>  struct cifsInodeInfo {
> -       struct list_head llist;         /* brlocks for this inode */
>        bool can_cache_brlcks;
> -       struct mutex lock_mutex;        /* protect two fields above */
> +       struct mutex lock_mutex;        /* protect cifsFileInfo->llist
> +                                          & cifsInodeInfo->can_cache_brlcks */
>        /* BB add in lists for dirty pages i.e. write caching info for oplock */
>        struct list_head openFileList;
>        __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
> diff --git a/fs/cifs/file.c b/fs/cifs/file.c
> index cf0b153..3f6b169 100644
> --- a/fs/cifs/file.c
> +++ b/fs/cifs/file.c
> @@ -264,6 +264,7 @@ cifs_new_fileinfo(__u16 fileHandle, struct file *file,
>        pCifsFile->tlink = cifs_get_tlink(tlink);
>        mutex_init(&pCifsFile->fh_mutex);
>        INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break);
> +       INIT_LIST_HEAD(&pCifsFile->llist);
>
>        spin_lock(&cifs_file_list_lock);
>        list_add(&pCifsFile->tlist, &(tlink_tcon(tlink)->openFileList));
> @@ -333,15 +334,11 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
>        /* Delete any outstanding lock records. We'll lose them when the file
>         * is closed anyway.
>         */
> -       mutex_lock(&cifsi->lock_mutex);
> -       list_for_each_entry_safe(li, tmp, &cifsi->llist, llist) {
> -               if (li->netfid != cifs_file->netfid)
> -                       continue;
> +       list_for_each_entry_safe(li, tmp, &cifs_file->llist, llist) {
>                list_del(&li->llist);
>                cifs_del_lock_waiters(li);
>                kfree(li);
>        }
> -       mutex_unlock(&cifsi->lock_mutex);
>
>        cifs_put_tlink(cifs_file->tlink);
>        dput(cifs_file->dentry);
> @@ -672,47 +669,82 @@ cifs_del_lock_waiters(struct cifsLockInfo *lock)
>  }
>
>  static bool
> -__cifs_find_lock_conflict(struct cifsInodeInfo *cinode, __u64 offset,
> -                       __u64 length, __u8 type, __u16 netfid,
> -                       struct cifsLockInfo **conf_lock)
> +__cifs_find_lock_conflict_file(struct cifsFileInfo *cifs_file, __u64 offset,
> +                              __u64 length, __u8 type,
> +                              struct cifsLockInfo **conf_lock)
>  {
> -       struct cifsLockInfo *li, *tmp;
> +       struct cifsLockInfo *li;
>
> -       list_for_each_entry_safe(li, tmp, &cinode->llist, llist) {
> +       list_for_each_entry(li, &cifs_file->llist, llist) {
> +               /* Check if range matches */
>                if (offset + length <= li->offset ||
>                    offset >= li->offset + li->length)
>                        continue;
> -               else if ((type & LOCKING_ANDX_SHARED_LOCK) &&
> -                        ((netfid == li->netfid && current->tgid == li->pid) ||
> -                         type == li->type))
> -                       continue;
> -               else {
> -                       *conf_lock = li;
> -                       return true;
> +               /* Is the request for a shared lock */
> +               if (type == LOCKING_ANDX_SHARED_LOCK) {
> +                       /* Does this process already posses a lock? */
> +                       if (current->tgid == li->pid)
> +                               return false;
You should check if netfids match too, because it is not allowed to
set a shared lock by one handle to an exclusive lock by another.

> +                       /* Is the existing lock a shared lock too? */
> +                       if (li->type == LOCKING_ANDX_SHARED_LOCK)
> +                               return false;
>                }
> +               /* We have found a conflicting lock */
> +               *conf_lock = li;
> +               return true;
> +       }
> +       return false;
> +}
> +
> +static bool
> +__cifs_find_lock_conflict(struct cifsFileInfo *cifs_file, __u64 offset,
> +                         __u64 length, __u8 type,
> +                         struct cifsLockInfo **conf_lock)
> +{
> +       struct cifsFileInfo *file, *tmp;
> +       bool rc;
> +       struct cifsInodeInfo *cifs_inode = CIFS_I(cifs_file->dentry->d_inode);
> +
> +       /* We need to check across every open instance of the inode */
> +       spin_lock(&cifs_file_list_lock);
> +       list_for_each_entry_safe(file, tmp, &cifs_inode->openFileList, flist) {
> +               cifsFileInfo_get(file);
> +               spin_unlock(&cifs_file_list_lock);
> +
> +               rc = __cifs_find_lock_conflict_file(file, offset, length,
> +                                                   type, conf_lock);
> +               if (rc)
> +                       return true;
> +
> +               cifsFileInfo_put(file);
> +               spin_lock(&cifs_file_list_lock);
>        }
> +       spin_unlock(&cifs_file_list_lock);
> +
>        return false;
>  }
>
>  static bool
> -cifs_find_lock_conflict(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock,
> +cifs_find_lock_conflict(struct cifsFileInfo *cifs_file,
> +                       struct cifsLockInfo *lock,
>                        struct cifsLockInfo **conf_lock)
>  {
> -       return __cifs_find_lock_conflict(cinode, lock->offset, lock->length,
> -                                        lock->type, lock->netfid, conf_lock);
> +       return __cifs_find_lock_conflict(cifs_file, lock->offset, lock->length,
> +                                        lock->type, conf_lock);
>  }
>
>  static int
> -cifs_lock_test(struct cifsInodeInfo *cinode, __u64 offset, __u64 length,
> -              __u8 type, __u16 netfid, struct file_lock *flock)
> +cifs_lock_test(struct cifsFileInfo *cifs_file, __u64 offset, __u64 length,
> +              __u8 type, struct file_lock *flock)
>  {
>        int rc = 0;
>        struct cifsLockInfo *conf_lock;
>        bool exist;
> +       struct cifsInodeInfo *cinode = CIFS_I(cifs_file->dentry->d_inode);
>
>        mutex_lock(&cinode->lock_mutex);
>
> -       exist = __cifs_find_lock_conflict(cinode, offset, length, type, netfid,
> +       exist = __cifs_find_lock_conflict(cifs_file, offset, length, type,
>                                          &conf_lock);
>        if (exist) {
>                flock->fl_start = conf_lock->offset;
> @@ -732,18 +764,21 @@ cifs_lock_test(struct cifsInodeInfo *cinode, __u64 offset, __u64 length,
>  }
>
>  static void
> -cifs_lock_add(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock)
> +cifs_lock_add(struct cifsFileInfo *cifs_file, struct cifsLockInfo *lock)
>  {
> +       struct cifsInodeInfo *cinode = CIFS_I(cifs_file->dentry->d_inode);
> +
>        mutex_lock(&cinode->lock_mutex);
> -       list_add_tail(&lock->llist, &cinode->llist);
> +       list_add_tail(&lock->llist, &cifs_file->llist);
>        mutex_unlock(&cinode->lock_mutex);
>  }
>
>  static int
> -cifs_lock_add_if(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock,
> +cifs_lock_add_if(struct cifsFileInfo *cifs_file, struct cifsLockInfo *lock,
>                 bool wait)
>  {
>        struct cifsLockInfo *conf_lock;
> +       struct cifsInodeInfo *cinode = CIFS_I(cifs_file->dentry->d_inode);
>        bool exist;
>        int rc = 0;
>
> @@ -751,9 +786,9 @@ try_again:
>        exist = false;
>        mutex_lock(&cinode->lock_mutex);
>
> -       exist = cifs_find_lock_conflict(cinode, lock, &conf_lock);
> +       exist = cifs_find_lock_conflict(cifs_file, lock, &conf_lock);
>        if (!exist && cinode->can_cache_brlcks) {
> -               list_add_tail(&lock->llist, &cinode->llist);
> +               list_add_tail(&lock->llist, &cifs_file->llist);
>                mutex_unlock(&cinode->lock_mutex);
>                return rc;
>        }
> @@ -823,7 +858,7 @@ static int
>  cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
>  {
>        int xid, rc = 0, stored_rc;
> -       struct cifsLockInfo *li, *tmp;
> +       struct cifsLockInfo *li;
>        struct cifs_tcon *tcon;
>        struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
>        unsigned int num, max_num;
> @@ -854,7 +889,7 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
>        for (i = 0; i < 2; i++) {
>                cur = buf;
>                num = 0;
> -               list_for_each_entry_safe(li, tmp, &cinode->llist, llist) {
> +               list_for_each_entry(li, &cfile->llist, llist) {
>                        if (li->type != types[i])
>                                continue;
>                        cur->Pid = cpu_to_le16(li->pid);
> @@ -865,8 +900,10 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
>                        if (++num == max_num) {
>                                stored_rc = cifs_lockv(xid, tcon, cfile->netfid,
>                                                       li->type, 0, num, buf);
> -                               if (stored_rc)
> +                               if (stored_rc) {
> +                                       cERROR(1, "Error while syncing locks");
>                                        rc = stored_rc;
> +                               }
>                                cur = buf;
>                                num = 0;
>                        } else
> @@ -876,8 +913,10 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
>                if (num) {
>                        stored_rc = cifs_lockv(xid, tcon, cfile->netfid,
>                                               types[i], 0, num, buf);
> -                       if (stored_rc)
> +                       if (stored_rc) {
> +                               cERROR(1, "Error while syncing locks");
>                                rc = stored_rc;
> +                       }
>                }
>        }
>
> @@ -1026,7 +1065,6 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u8 type,
>        __u64 length = 1 + flock->fl_end - flock->fl_start;
>        struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
>        struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
> -       struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
>        __u16 netfid = cfile->netfid;
>
>        if (posix_lck) {
> @@ -1046,8 +1084,7 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u8 type,
>                return rc;
>        }
>
> -       rc = cifs_lock_test(cinode, flock->fl_start, length, type, netfid,
> -                           flock);
> +       rc = cifs_lock_test(cfile, flock->fl_start, length, type, flock);
>        if (!rc)
>                return rc;
>
> @@ -1108,7 +1145,8 @@ cifs_free_llist(struct list_head *llist)
>  }
>
>  static int
> -cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid)
> +cifs_unlock_range_file(struct cifsFileInfo *cfile, struct file_lock *flock,
> +                      int xid)
>  {
>        int rc = 0, stored_rc;
>        int types[] = {LOCKING_ANDX_LARGE_FILES,
> @@ -1134,15 +1172,11 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid)
>        for (i = 0; i < 2; i++) {
>                cur = buf;
>                num = 0;
> -               list_for_each_entry_safe(li, tmp, &cinode->llist, llist) {
> +               list_for_each_entry_safe(li, tmp, &cfile->llist, llist) {
>                        if (flock->fl_start > li->offset ||
>                            (flock->fl_start + length) <
>                            (li->offset + li->length))
>                                continue;
> -                       if (current->tgid != li->pid)
> -                               continue;
> -                       if (cfile->netfid != li->netfid)
> -                               continue;
>                        if (types[i] != li->type)
>                                continue;
>                        if (!cinode->can_cache_brlcks) {
> @@ -1172,7 +1206,7 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid)
>                                                 * the inode list.
>                                                 */
>                                                cifs_move_llist(&tmp_llist,
> -                                                               &cinode->llist);
> +                                                               &cfile->llist);
>                                                rc = stored_rc;
>                                        } else
>                                                /*
> @@ -1198,7 +1232,7 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid)
>                        stored_rc = cifs_lockv(xid, tcon, cfile->netfid,
>                                               types[i], num, 0, buf);
>                        if (stored_rc) {
> -                               cifs_move_llist(&tmp_llist, &cinode->llist);
> +                               cifs_move_llist(&tmp_llist, &cfile->llist);
>                                rc = stored_rc;
>                        } else
>                                cifs_free_llist(&tmp_llist);
> @@ -1211,6 +1245,30 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid)
>  }
>
>  static int
> +cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid)
> +{
> +       struct cifsFileInfo *file, *tmp;
> +       struct cifsInodeInfo *cifs_inode = CIFS_I(cfile->dentry->d_inode);
> +       int rc = 0;
> +
> +       spin_lock(&cifs_file_list_lock);
> +       list_for_each_entry_safe(file, tmp, &cifs_inode->openFileList, flist) {
> +               if (file->pid != current->pid)
> +                       continue;
The same cifsFileInfo structure can be shared between several
processes and every can set a brlock on it. But file->pid still has
the pid only of the process who opened the file. So, such a check
should be done in cifs_unlock_range_file with lock->pid.

> +               cifsFileInfo_get(file);
> +               spin_unlock(&cifs_file_list_lock);
> +               rc = cifs_unlock_range_file(file, flock, xid);
> +               if (rc < 0)
> +                       cFYI(1, "VFS is out of sync with lock on server!");
> +               cifsFileInfo_put(file);
> +               spin_lock(&cifs_file_list_lock);
> +       }
> +       spin_unlock(&cifs_file_list_lock);
> +
> +       return rc;
> +}
> +
> +static int
>  cifs_setlk(struct file *file,  struct file_lock *flock, __u8 type,
>           bool wait_flag, bool posix_lck, int lock, int unlock, int xid)
>  {
> @@ -1218,7 +1276,6 @@ cifs_setlk(struct file *file,  struct file_lock *flock, __u8 type,
>        __u64 length = 1 + flock->fl_end - flock->fl_start;
>        struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
>        struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
> -       struct cifsInodeInfo *cinode = CIFS_I(file->f_path.dentry->d_inode);
>        __u16 netfid = cfile->netfid;
>
>        if (posix_lck) {
> @@ -1249,7 +1306,7 @@ cifs_setlk(struct file *file,  struct file_lock *flock, __u8 type,
>                if (!lock)
>                        return -ENOMEM;
>
> -               rc = cifs_lock_add_if(cinode, lock, wait_flag);
> +               rc = cifs_lock_add_if(cfile, lock, wait_flag);
>                if (rc < 0)
>                        kfree(lock);
>                if (rc <= 0)
> @@ -1262,7 +1319,7 @@ cifs_setlk(struct file *file,  struct file_lock *flock, __u8 type,
>                        goto out;
>                }
>
> -               cifs_lock_add(cinode, lock);
> +               cifs_lock_add(cfile, lock);
>        } else if (unlock)
>                rc = cifs_unlock_range(cfile, flock, xid);
>
>
>

In general, I like your idea to group brlocks to the file structure
(that reverts my previous change) but this should be done in two steps
to make the history bisectable:
1) move locks to cifsFileInfo,
2) change cifs_unlock_range to unlock all process locks that are in
the requested unlock range.

-- 
Best regards,
Pavel Shilovsky.
--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux