From 68fe8004456298070247fd23c3af91b332d98d35 Mon Sep 17 00:00:00 2001
From: Dave Wysochanski <dwysocha@redhat.com>
Date: Mon, 21 Oct 2019 07:28:04 -0400
Subject: [PATCH] debug cifs lock_sem deadlock - redhat bug 1760835

debug of https://bugzilla.redhat.com/show_bug.cgi?id=1760835

cifs_dbg trace of lock_sem
turn off ratelimiting
---
 Makefile            |  2 +-
 fs/cifs/cifsfs.c    |  1 +
 fs/cifs/cifsproto.h | 34 ++++++++++++++++++++++++++++++++++
 fs/cifs/file.c      | 32 ++++++++++++++++++++++++++++++++
 fs/cifs/smb2file.c  |  2 ++
 lib/ratelimit.c     |  3 +--
 6 files changed, 71 insertions(+), 3 deletions(-)

diff --git a/Makefile b/Makefile
index 5475cdb6d57d..47b0d42f0418 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 VERSION = 5
 PATCHLEVEL = 4
 SUBLEVEL = 0
-EXTRAVERSION = -rc4
+EXTRAVERSION = -rc4-dbg3
 NAME = Nesting Opossum
 
 # *DOCUMENTATION*
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index c049c7b3aa87..431a9d46b409 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -1335,6 +1335,7 @@ static ssize_t cifs_copy_file_range(struct file *src_file, loff_t off,
 	struct cifsInodeInfo *cifsi = inode;
 
 	inode_init_once(&cifsi->vfs_inode);
+	cifs_sem_op_trace_init_rwsem(&cifsi->lock_sem);
 	init_rwsem(&cifsi->lock_sem);
 }
 
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index e53e9f62b87b..5488e5e11e40 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -65,6 +65,40 @@ extern int smb_send(struct TCP_Server_Info *, struct smb_hdr *,
 	else							\
 		trace_smb3_exit_done(curr_xid, __func__);	\
 } while (0)
+
+
+/* Debug lock_sem */
+#define cifs_sem_op_trace(SEM, OP) \
+do { \
+	cifs_dbg(VFS, "file=%s line=%d pid=%d cifs_sem_op op=%s sem=%px\n", \
+		 __FILE__, __LINE__, current->pid, #OP, SEM); \
+} while(0)
+
+#define cifs_sem_op_trace_init_rwsem(X) \
+do { \
+	cifs_sem_op_trace(X, init_rwsem); \
+} while (0)
+
+#define cifs_sem_op_trace_down_read(X) \
+do { \
+	cifs_sem_op_trace(X, down_read); \
+} while (0)
+
+#define cifs_sem_op_trace_up_read(X) \
+do { \
+	cifs_sem_op_trace(X, up_read); \
+} while (0)
+
+#define cifs_sem_op_trace_down_write(X) \
+do { \
+	cifs_sem_op_trace(X, down_write); \
+} while (0)
+
+#define cifs_sem_op_trace_up_write(X) \
+do { \
+	cifs_sem_op_trace(X, up_write); \
+} while (0)
+
 extern int init_cifs_idmap(void);
 extern void exit_cifs_idmap(void);
 extern int init_cifs_spnego(void);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 5ad15de2bb4f..58e554cc397a 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -270,6 +270,7 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
 	struct cifs_fid_locks *cur;
 	bool has_locks = false;
 
+	cifs_sem_op_trace_down_read(&cinode->lock_sem);
 	down_read(&cinode->lock_sem);
 	list_for_each_entry(cur, &cinode->llist, llist) {
 		if (!list_empty(&cur->locks)) {
@@ -277,6 +278,7 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
 			break;
 		}
 	}
+	cifs_sem_op_trace_up_read(&cinode->lock_sem);
 	up_read(&cinode->lock_sem);
 	return has_locks;
 }
@@ -306,8 +308,10 @@ struct cifsFileInfo *
 	INIT_LIST_HEAD(&fdlocks->locks);
 	fdlocks->cfile = cfile;
 	cfile->llist = fdlocks;
+	cifs_sem_op_trace_down_write(&cinode->lock_sem);
 	down_write(&cinode->lock_sem);
 	list_add(&fdlocks->llist, &cinode->llist);
+	cifs_sem_op_trace_up_write(&cinode->lock_sem);
 	up_write(&cinode->lock_sem);
 
 	cfile->count = 1;
@@ -464,6 +468,7 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
 	 * Delete any outstanding lock records. We'll lose them when the file
 	 * is closed anyway.
 	 */
+	cifs_sem_op_trace_down_write(&cifsi->lock_sem);
 	down_write(&cifsi->lock_sem);
 	list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) {
 		list_del(&li->llist);
@@ -472,6 +477,7 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
 	}
 	list_del(&cifs_file->llist->llist);
 	kfree(cifs_file->llist);
+	cifs_sem_op_trace_up_write(&cifsi->lock_sem);
 	up_write(&cifsi->lock_sem);
 
 	cifs_put_tlink(cifs_file->tlink);
@@ -621,9 +627,11 @@ int cifs_open(struct inode *inode, struct file *file)
 	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
 	int rc = 0;
 
+	cifs_sem_op_trace_down_read(&cinode->lock_sem);
 	down_read_nested(&cinode->lock_sem, SINGLE_DEPTH_NESTING);
 	if (cinode->can_cache_brlcks) {
 		/* can cache locks - no need to relock */
+		cifs_sem_op_trace_up_read(&cinode->lock_sem);
 		up_read(&cinode->lock_sem);
 		return rc;
 	}
@@ -635,6 +643,7 @@ int cifs_open(struct inode *inode, struct file *file)
 	else
 		rc = tcon->ses->server->ops->push_mand_locks(cfile);
 
+	cifs_sem_op_trace_up_read(&cinode->lock_sem);
 	up_read(&cinode->lock_sem);
 	return rc;
 }
@@ -1001,6 +1010,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
 	struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
 	bool exist;
 
+	cifs_sem_op_trace_down_read(&cinode->lock_sem);
 	down_read(&cinode->lock_sem);
 
 	exist = cifs_find_lock_conflict(cfile, offset, length, type,
@@ -1019,6 +1029,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
 	else
 		flock->fl_type = F_UNLCK;
 
+	cifs_sem_op_trace_up_read(&cinode->lock_sem);
 	up_read(&cinode->lock_sem);
 	return rc;
 }
@@ -1027,8 +1038,10 @@ int cifs_closedir(struct inode *inode, struct file *file)
 cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock)
 {
 	struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
+	cifs_sem_op_trace_down_write(&cinode->lock_sem);
 	down_write(&cinode->lock_sem);
 	list_add_tail(&lock->llist, &cfile->llist->locks);
+	cifs_sem_op_trace_up_write(&cinode->lock_sem);
 	up_write(&cinode->lock_sem);
 }
 
@@ -1049,6 +1062,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
 
 try_again:
 	exist = false;
+	cifs_sem_op_trace_down_write(&cinode->lock_sem);
 	down_write(&cinode->lock_sem);
 
 	exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length,
@@ -1056,6 +1070,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
 					CIFS_LOCK_OP);
 	if (!exist && cinode->can_cache_brlcks) {
 		list_add_tail(&lock->llist, &cfile->llist->locks);
+		cifs_sem_op_trace_up_write(&cinode->lock_sem);
 		up_write(&cinode->lock_sem);
 		return rc;
 	}
@@ -1066,16 +1081,19 @@ int cifs_closedir(struct inode *inode, struct file *file)
 		rc = -EACCES;
 	else {
 		list_add_tail(&lock->blist, &conf_lock->blist);
+		cifs_sem_op_trace_up_write(&cinode->lock_sem);
 		up_write(&cinode->lock_sem);
 		rc = wait_event_interruptible(lock->block_q,
 					(lock->blist.prev == &lock->blist) &&
 					(lock->blist.next == &lock->blist));
 		if (!rc)
 			goto try_again;
+		cifs_sem_op_trace_down_write(&cinode->lock_sem);
 		down_write(&cinode->lock_sem);
 		list_del_init(&lock->blist);
 	}
 
+	cifs_sem_op_trace_up_write(&cinode->lock_sem);
 	up_write(&cinode->lock_sem);
 	return rc;
 }
@@ -1097,6 +1115,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
 	if ((flock->fl_flags & FL_POSIX) == 0)
 		return 1;
 
+	cifs_sem_op_trace_down_read(&cinode->lock_sem);
 	down_read(&cinode->lock_sem);
 	posix_test_lock(file, flock);
 
@@ -1105,6 +1124,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
 		rc = 1;
 	}
 
+	cifs_sem_op_trace_up_read(&cinode->lock_sem);
 	up_read(&cinode->lock_sem);
 	return rc;
 }
@@ -1125,13 +1145,16 @@ int cifs_closedir(struct inode *inode, struct file *file)
 		return rc;
 
 try_again:
+	cifs_sem_op_trace_down_write(&cinode->lock_sem);
 	down_write(&cinode->lock_sem);
 	if (!cinode->can_cache_brlcks) {
+		cifs_sem_op_trace_up_write(&cinode->lock_sem);
 		up_write(&cinode->lock_sem);
 		return rc;
 	}
 
 	rc = posix_lock_file(file, flock, NULL);
+	cifs_sem_op_trace_up_write(&cinode->lock_sem);
 	up_write(&cinode->lock_sem);
 	if (rc == FILE_LOCK_DEFERRED) {
 		rc = wait_event_interruptible(flock->fl_wait, !flock->fl_blocker);
@@ -1331,8 +1354,10 @@ struct lock_to_push {
 	int rc = 0;
 
 	/* we are going to update can_cache_brlcks here - need a write access */
+	cifs_sem_op_trace_down_write(&cinode->lock_sem);
 	down_write(&cinode->lock_sem);
 	if (!cinode->can_cache_brlcks) {
+		cifs_sem_op_trace_up_write(&cinode->lock_sem);
 		up_write(&cinode->lock_sem);
 		return rc;
 	}
@@ -1345,6 +1370,7 @@ struct lock_to_push {
 		rc = tcon->ses->server->ops->push_mand_locks(cfile);
 
 	cinode->can_cache_brlcks = false;
+	cifs_sem_op_trace_up_write(&cinode->lock_sem);
 	up_write(&cinode->lock_sem);
 	return rc;
 }
@@ -1522,6 +1548,7 @@ struct lock_to_push {
 	if (!buf)
 		return -ENOMEM;
 
+	cifs_sem_op_trace_down_write(&cinode->lock_sem);
 	down_write(&cinode->lock_sem);
 	for (i = 0; i < 2; i++) {
 		cur = buf;
@@ -1592,6 +1619,7 @@ struct lock_to_push {
 		}
 	}
 
+	cifs_sem_op_trace_up_write(&cinode->lock_sem);
 	up_write(&cinode->lock_sem);
 	kfree(buf);
 	return rc;
@@ -3148,6 +3176,7 @@ ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from)
 	 * We need to hold the sem to be sure nobody modifies lock list
 	 * with a brlock that prevents writing.
 	 */
+	cifs_sem_op_trace_down_read(&cinode->lock_sem);
 	down_read(&cinode->lock_sem);
 
 	rc = generic_write_checks(iocb, from);
@@ -3161,6 +3190,7 @@ ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from)
 	else
 		rc = -EACCES;
 out:
+	cifs_sem_op_trace_up_read(&cinode->lock_sem);
 	up_read(&cinode->lock_sem);
 	inode_unlock(inode);
 
@@ -3887,11 +3917,13 @@ ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to)
 	 * We need to hold the sem to be sure nobody modifies lock list
 	 * with a brlock that prevents reading.
 	 */
+	cifs_sem_op_trace_down_read(&cinode->lock_sem);
 	down_read(&cinode->lock_sem);
 	if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(to),
 				     tcon->ses->server->vals->shared_lock_type,
 				     0, NULL, CIFS_READ_OP))
 		rc = generic_file_read_iter(iocb, to);
+	cifs_sem_op_trace_up_read(&cinode->lock_sem);
 	up_read(&cinode->lock_sem);
 	return rc;
 }
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
index e6a1fc72018f..9f89a765d977 100644
--- a/fs/cifs/smb2file.c
+++ b/fs/cifs/smb2file.c
@@ -145,6 +145,7 @@
 
 	cur = buf;
 
+	cifs_sem_op_trace_down_write(&cinode->lock_sem);
 	down_write(&cinode->lock_sem);
 	list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) {
 		if (flock->fl_start > li->offset ||
@@ -206,6 +207,7 @@
 		} else
 			cifs_free_llist(&tmp_llist);
 	}
+	cifs_sem_op_trace_up_write(&cinode->lock_sem);
 	up_write(&cinode->lock_sem);
 
 	kfree(buf);
diff --git a/lib/ratelimit.c b/lib/ratelimit.c
index e01a93f46f83..bc87f790304c 100644
--- a/lib/ratelimit.c
+++ b/lib/ratelimit.c
@@ -29,8 +29,7 @@ int ___ratelimit(struct ratelimit_state *rs, const char *func)
 	unsigned long flags;
 	int ret;
 
-	if (!rs->interval)
-		return 1;
+	return 1;
 
 	/*
 	 * If we contend on this state's lock then almost
-- 
1.8.3.1

