From patchwork Fri Aug 31 09:41:28 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Shilovsky X-Patchwork-Id: 1392381 Return-Path: X-Original-To: patchwork-cifs-client@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 09E303FDF6 for ; Fri, 31 Aug 2012 09:41:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752102Ab2HaJly (ORCPT ); Fri, 31 Aug 2012 05:41:54 -0400 Received: from mail-lb0-f174.google.com ([209.85.217.174]:40987 "EHLO mail-lb0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751780Ab2HaJly (ORCPT ); Fri, 31 Aug 2012 05:41:54 -0400 Received: by mail-lb0-f174.google.com with SMTP id gj3so1089918lbb.19 for ; Fri, 31 Aug 2012 02:41:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:subject:date:message-id:x-mailer:in-reply-to:references; bh=nZ39jwYpOU5BsFS9s7XtJLbkrKCL54cGYSM+wdch33M=; b=00jzNR4PXdIL82dBnR7tUulUEnPHcoHX8dAxOQR20jjzLuSBJekOnpaKv6cDmZcBY4 7sCn0hL8JqDerpDZI8bSaMlPZymICxzlKL9VcTjEM7x9+a6UtAbr+C8q6nXP9M2YKMht Ur6rIf7sYc8d5i3bafg+hqNOnXZrpF+maR4KhgsV0LZNaoHJ/BXDTi36A90KCk99PxS0 i5xT95BeNU+lGdiEssY+hLEmoD5AfmMz6vMg6O435Eg4gx7Cw0DBidRmzoj3775pHkPD QD3wZ1t8Tl/L8AmaXnFK7HNA1jbxG9NaiE+/TTncNbjkaw1HG5qrMor4TOD60etEQnBb pEMw== Received: by 10.112.100.8 with SMTP id eu8mr2433418lbb.12.1346406113334; Fri, 31 Aug 2012 02:41:53 -0700 (PDT) Received: from localhost.localdomain ([95.84.16.136]) by mx.google.com with ESMTPS id lv13sm4283257lab.8.2012.08.31.02.41.50 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 31 Aug 2012 02:41:51 -0700 (PDT) From: Pavel Shilovsky To: linux-cifs@vger.kernel.org Subject: [PATCH v2 7/7] CIFS: Check for mandatory brlocks on read/write Date: Fri, 31 Aug 2012 13:41:28 +0400 Message-Id: <1346406088-4537-8-git-send-email-pshilovsky@etersoft.ru> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1346406088-4537-1-git-send-email-pshilovsky@etersoft.ru> References: <1346406088-4537-1-git-send-email-pshilovsky@etersoft.ru> Sender: linux-cifs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-cifs@vger.kernel.org Currently CIFS code accept read/write ops on mandatory locked area when two processes use the same file descriptor - it's wrong. Fix this by serializing io and brlock operations on the inode. Signed-off-by: Pavel Shilovsky --- fs/cifs/cifsproto.h | 4 ++ fs/cifs/file.c | 97 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 75 insertions(+), 26 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 15e38dc..c758ee7 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -180,6 +180,10 @@ extern struct smb_vol *cifs_get_volume_info(char *mount_data, extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *); extern void cifs_umount(struct cifs_sb_info *); extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); +extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, + __u64 length, __u8 type, + struct cifsLockInfo **conf_lock, + bool rw_check); #if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) extern void cifs_dfs_release_automount_timer(void); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index daeb817..c1b93b7 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -704,10 +704,10 @@ cifs_del_lock_waiters(struct cifsLockInfo *lock) } } -static bool +extern bool cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset, __u64 length, __u8 type, struct cifsFileInfo *cfile, - struct cifsLockInfo **conf_lock) + struct cifsLockInfo **conf_lock, bool rw_check) { struct cifsLockInfo *li; struct cifsFileInfo *cur_cfile = fdlocks->cfile; @@ -717,19 +717,24 @@ cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset, if (offset + length <= li->offset || offset >= li->offset + li->length) continue; + if (rw_check && server->ops->compare_fids(cfile, cur_cfile) && + current->tgid == li->pid) + continue; if ((type & server->vals->shared_lock_type) && ((server->ops->compare_fids(cfile, cur_cfile) && current->tgid == li->pid) || type == li->type)) continue; - *conf_lock = li; + if (conf_lock) + *conf_lock = li; return true; } return false; } -static bool +bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length, - __u8 type, struct cifsLockInfo **conf_lock) + __u8 type, struct cifsLockInfo **conf_lock, + bool rw_check) { bool rc = false; struct cifs_fid_locks *cur; @@ -737,7 +742,7 @@ cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length, list_for_each_entry(cur, &cinode->llist, llist) { rc = cifs_find_fid_lock_conflict(cur, offset, length, type, - cfile, conf_lock); + cfile, conf_lock, rw_check); if (rc) break; } @@ -765,7 +770,7 @@ cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length, down_read(&cinode->lock_sem); exist = cifs_find_lock_conflict(cfile, offset, length, type, - &conf_lock); + &conf_lock, false); if (exist) { flock->fl_start = conf_lock->offset; flock->fl_end = conf_lock->offset + conf_lock->length - 1; @@ -812,7 +817,7 @@ try_again: down_write(&cinode->lock_sem); exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length, - lock->type, &conf_lock); + lock->type, &conf_lock, false); if (!exist && cinode->can_cache_brlcks) { list_add_tail(&lock->llist, &cfile->llist->locks); up_write(&cinode->lock_sem); @@ -2394,15 +2399,18 @@ ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov, return written; } -ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) +ssize_t +cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos) { - struct inode *inode; - - inode = iocb->ki_filp->f_path.dentry->d_inode; - - if (CIFS_I(inode)->clientCanCacheAll) - return generic_file_aio_write(iocb, iov, nr_segs, pos); + struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode; + struct cifsInodeInfo *cinode = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifsFileInfo *cfile = (struct cifsFileInfo *) + iocb->ki_filp->private_data; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct TCP_Server_Info *server = tcon->ses->server; + int rc = -EACCES; /* * In strict cache mode we need to write the data to the server exactly @@ -2411,7 +2419,25 @@ ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov, * not on the region from pos to ppos+len-1. */ - return cifs_user_writev(iocb, iov, nr_segs, pos); + if (!cinode->clientCanCacheAll) + return cifs_user_writev(iocb, iov, nr_segs, pos); + + if (cap_unix(tcon->ses) && + (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && + ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) + return generic_file_aio_write(iocb, iov, nr_segs, pos); + + /* + * We need to hold the sem to be sure nobody modifies lock list + * with a brlock that prevents writing. + */ + down_read(&cinode->lock_sem); + if (!cifs_find_lock_conflict(cfile, pos, iov_length(iov, nr_segs), + server->vals->exclusive_lock_type, NULL, + true)) + rc = generic_file_aio_write(iocb, iov, nr_segs, pos); + up_read(&cinode->lock_sem); + return rc; } static struct cifs_readdata * @@ -2746,15 +2772,17 @@ ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov, return read; } -ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) +ssize_t +cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos) { - struct inode *inode; - - inode = iocb->ki_filp->f_path.dentry->d_inode; - - if (CIFS_I(inode)->clientCanCacheRead) - return generic_file_aio_read(iocb, iov, nr_segs, pos); + struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode; + struct cifsInodeInfo *cinode = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifsFileInfo *cfile = (struct cifsFileInfo *) + iocb->ki_filp->private_data; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + int rc = -EACCES; /* * In strict cache mode we need to read from the server all the time @@ -2764,8 +2792,25 @@ ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov, * on pages affected by this read but not on the region from pos to * pos+len-1. */ + if (!cinode->clientCanCacheRead) + return cifs_user_readv(iocb, iov, nr_segs, pos); - return cifs_user_readv(iocb, iov, nr_segs, pos); + if (cap_unix(tcon->ses) && + (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && + ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) + return generic_file_aio_read(iocb, iov, nr_segs, pos); + + /* + * We need to hold the sem to be sure nobody modifies lock list + * with a brlock that prevents reading. + */ + down_read(&cinode->lock_sem); + if (!cifs_find_lock_conflict(cfile, pos, iov_length(iov, nr_segs), + tcon->ses->server->vals->shared_lock_type, + NULL, true)) + rc = generic_file_aio_read(iocb, iov, nr_segs, pos); + up_read(&cinode->lock_sem); + return rc; } static ssize_t