Received: by 2002:a25:4158:0:0:0:0:0 with SMTP id o85csp846259yba; Wed, 24 Apr 2019 10:32:11 -0700 (PDT) X-Google-Smtp-Source: APXvYqyJ+/I2gg4NqPswzF/9C3QyaPR+Vl6XzlBeqv8XVL3wgq3Ft+s5m9jjhs2+ifozwhIp4gB1 X-Received: by 2002:aa7:86ce:: with SMTP id h14mr34563843pfo.84.1556127131579; Wed, 24 Apr 2019 10:32:11 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1556127131; cv=none; d=google.com; s=arc-20160816; b=U/j+g+L2lhZGJsnqri8Ac/NoqaSNL1OYOylOw9gpoxYkZ2YD/Ntb1lIjxCgLlp3j0I 3vr1SwlMwaXqvKDKFIwdoftyWc0runBU8oGb+JYynwHnO7EPT16zXd+2iBngiF13/Vgq d1Wa660J9hwrrpyF9bJWWgpe1ak3PwqfJOWgmKEr1H2BVCrLHhWHJhl8p2QlQm/OK5aH c1R+sU8jO2ZUg/4MqJe5nR5o0a+/O5HxJ/urp5BQhMmWGNDD+Z2CRnE5jYrLmAqgfGg7 HtHpXd+WTi4PXfaIGH/7yZUjh6fqwB2CkOfrxP8k8dKp1wo6ZQ5Jh79UD7DRTnukYCCK cGKQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=0vALqS0lJEddZMGek4qdI9MC/srBVosw3GOsfAm1RSs=; b=G/lbO+0HbgYZafqPACoCp5BV0jWzCjYZqNOnbyfGlhgqx/fwypBtYXMPj3DN4RsslM 8FbV/EXI0TviMvFsrjO0a9qK+Fs3hLki0rcxakcUJakhVQ4m0FsGNzCGe4rh2kc6CdOX 5kagfjjtPJHlNxzbtgUWfE7G5W3O1n+pSWBqD+Gn4DBpWHeYz50zCLQbubWkRQ2R8jLy S5qtkZyxRsZF31xxfRuAkyN6TBwl8Ad6OdpIH54YO9CR6+2C1ms3exR7CyLPPOvd7Giu F0uR7+ayBHBguxLKaIdr+sx8Ph8Q69T4f5fSJ2NIYXsV3oBKYP0vngo0vZkXq5L7/tvB jMgA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=TAIlaSAz; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 134si18601858pga.249.2019.04.24.10.31.56; Wed, 24 Apr 2019 10:32:11 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=TAIlaSAz; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390610AbfDXRaR (ORCPT + 99 others); Wed, 24 Apr 2019 13:30:17 -0400 Received: from mail.kernel.org ([198.145.29.99]:56566 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390923AbfDXRaN (ORCPT ); Wed, 24 Apr 2019 13:30:13 -0400 Received: from localhost (62-193-50-229.as16211.net [62.193.50.229]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 44C4D2054F; Wed, 24 Apr 2019 17:30:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1556127012; bh=9Hr+vbr3MYJUocQqbkui8bZAihin5517wLa+64ztmbE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=TAIlaSAziEiH7fxzX+PeyRFteOktpON2fe8ov2QyVc7GENZu4Ute/MLWv0zobDYlM 95kDtQT4KztFSfbzALCgetGnEoAh3tnQQlw7qbpxgINw7HUeTEdRZtwSLyekIcoNuP MPncGlejq8/eoePUDNYjo+S2U2q5xOzfaI7cN3Xc= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Aurelien Aptel , Steve French , Pavel Shilovsky Subject: [PATCH 4.19 28/96] CIFS: keep FileInfo handle live during oplock break Date: Wed, 24 Apr 2019 19:09:33 +0200 Message-Id: <20190424170921.753222908@linuxfoundation.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190424170919.829037226@linuxfoundation.org> References: <20190424170919.829037226@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Aurelien Aptel commit b98749cac4a695f084a5ff076f4510b23e353ecd upstream. In the oplock break handler, writing pending changes from pages puts the FileInfo handle. If the refcount reaches zero it closes the handle and waits for any oplock break handler to return, thus causing a deadlock. To prevent this situation: * We add a wait flag to cifsFileInfo_put() to decide whether we should wait for running/pending oplock break handlers * We keep an additionnal reference of the SMB FileInfo handle so that for the rest of the handler putting the handle won't close it. - The ref is bumped everytime we queue the handler via the cifs_queue_oplock_break() helper. - The ref is decremented at the end of the handler This bug was triggered by xfstest 464. Also important fix to address the various reports of oops in smb2_push_mandatory_locks Signed-off-by: Aurelien Aptel Signed-off-by: Steve French Reviewed-by: Pavel Shilovsky CC: Stable Signed-off-by: Greg Kroah-Hartman --- fs/cifs/cifsglob.h | 2 ++ fs/cifs/file.c | 30 +++++++++++++++++++++++++----- fs/cifs/misc.c | 25 +++++++++++++++++++++++-- fs/cifs/smb2misc.c | 6 +++--- 4 files changed, 53 insertions(+), 10 deletions(-) --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1263,6 +1263,7 @@ cifsFileInfo_get_locked(struct cifsFileI } struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file); +void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_hdlr); void cifsFileInfo_put(struct cifsFileInfo *cifs_file); #define CIFS_CACHE_READ_FLG 1 @@ -1763,6 +1764,7 @@ GLOBAL_EXTERN spinlock_t gidsidlock; #endif /* CONFIG_CIFS_ACL */ void cifs_oplock_break(struct work_struct *work); +void cifs_queue_oplock_break(struct cifsFileInfo *cfile); extern const struct slow_work_ops cifs_oplock_break_ops; extern struct workqueue_struct *cifsiod_wq; --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -358,13 +358,31 @@ cifsFileInfo_get(struct cifsFileInfo *ci return cifs_file; } -/* - * Release a reference on the file private data. This may involve closing - * the filehandle out on the server. Must be called without holding - * tcon->open_file_lock and cifs_file->file_info_lock. +/** + * cifsFileInfo_put - release a reference of file priv data + * + * Always potentially wait for oplock handler. See _cifsFileInfo_put(). */ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) { + _cifsFileInfo_put(cifs_file, true); +} + +/** + * _cifsFileInfo_put - release a reference of file priv data + * + * This may involve closing the filehandle @cifs_file out on the + * server. Must be called without holding tcon->open_file_lock and + * cifs_file->file_info_lock. + * + * If @wait_for_oplock_handler is true and we are releasing the last + * reference, wait for any running oplock break handler of the file + * and cancel any pending one. If calling this function from the + * oplock break handler, you need to pass false. + * + */ +void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler) +{ struct inode *inode = d_inode(cifs_file->dentry); struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink); struct TCP_Server_Info *server = tcon->ses->server; @@ -411,7 +429,8 @@ void cifsFileInfo_put(struct cifsFileInf spin_unlock(&tcon->open_file_lock); - oplock_break_cancelled = cancel_work_sync(&cifs_file->oplock_break); + oplock_break_cancelled = wait_oplock_handler ? + cancel_work_sync(&cifs_file->oplock_break) : false; if (!tcon->need_reconnect && !cifs_file->invalidHandle) { struct TCP_Server_Info *server = tcon->ses->server; @@ -4170,6 +4189,7 @@ void cifs_oplock_break(struct work_struc cinode); cifs_dbg(FYI, "Oplock release rc = %d\n", rc); } + _cifsFileInfo_put(cfile, false /* do not wait for ourself */); cifs_done_oplock_break(cinode); } --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -490,8 +490,7 @@ is_valid_oplock_break(char *buffer, stru CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, &pCifsInode->flags); - queue_work(cifsoplockd_wq, - &netfile->oplock_break); + cifs_queue_oplock_break(netfile); netfile->oplock_break_cancelled = false; spin_unlock(&tcon->open_file_lock); @@ -588,6 +587,28 @@ void cifs_put_writer(struct cifsInodeInf spin_unlock(&cinode->writers_lock); } +/** + * cifs_queue_oplock_break - queue the oplock break handler for cfile + * + * This function is called from the demultiplex thread when it + * receives an oplock break for @cfile. + * + * Assumes the tcon->open_file_lock is held. + * Assumes cfile->file_info_lock is NOT held. + */ +void cifs_queue_oplock_break(struct cifsFileInfo *cfile) +{ + /* + * Bump the handle refcount now while we hold the + * open_file_lock to enforce the validity of it for the oplock + * break handler. The matching put is done at the end of the + * handler. + */ + cifsFileInfo_get(cfile); + + queue_work(cifsoplockd_wq, &cfile->oplock_break); +} + void cifs_done_oplock_break(struct cifsInodeInfo *cinode) { clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c @@ -555,7 +555,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tc clear_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, &cinode->flags); - queue_work(cifsoplockd_wq, &cfile->oplock_break); + cifs_queue_oplock_break(cfile); kfree(lw); return true; } @@ -719,8 +719,8 @@ smb2_is_valid_oplock_break(char *buffer, CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, &cinode->flags); spin_unlock(&cfile->file_info_lock); - queue_work(cifsoplockd_wq, - &cfile->oplock_break); + + cifs_queue_oplock_break(cfile); spin_unlock(&tcon->open_file_lock); spin_unlock(&cifs_tcp_ses_lock);