From 9b2a48c5f81a1b76e1a60117a266192507ff8217 Mon Sep 17 00:00:00 2001
From: Steve French <stevef@stevef95.austin.ibm.com>
Date: Mon, 8 Sep 2003 05:02:46 -0700
Subject: [PATCH] Fix spinlock usage for SMP safety

---
 fs/cifs/CHANGES       |  1 +
 fs/cifs/cifs_debug.c  |  8 ++++--
 fs/cifs/cifsencrypt.c |  6 ++--
 fs/cifs/cifsfs.c      | 29 ++++++++++---------
 fs/cifs/cifsglob.h    | 27 ++++++++++++++++--
 fs/cifs/connect.c     |  6 ++--
 fs/cifs/dir.c         |  1 +
 fs/cifs/file.c        | 65 +++++++++++++++++++++++++++++--------------
 fs/cifs/misc.c        | 14 +++++-----
 fs/cifs/transport.c   | 21 ++++++--------
 10 files changed, 111 insertions(+), 67 deletions(-)

diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES
index bf82a93594f7..1a5de2f3defb 100644
--- a/fs/cifs/CHANGES
+++ b/fs/cifs/CHANGES
@@ -2,6 +2,7 @@ Version 0.92
 ------------
 Active smb transactions should never go negative (fix double FreeXid). Fix
 list processing in file routines. Check return code on kmalloc in open.
+Fix spinlock usage for SMP.
 
 Version 0.91
 ------------
diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
index decc2fdbffaf..ae70574dec73 100644
--- a/fs/cifs/cifs_debug.c
+++ b/fs/cifs/cifs_debug.c
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs_debug.c
  *
- *   Copyright (c) International Business Machines  Corp., 2000,2002
+ *   Copyright (C) International Business Machines  Corp., 2000,2003
  *
  *   Modified by Steve French (sfrench@us.ibm.com)
  *
@@ -84,12 +84,12 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset,
 		ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
 		length =
 		    sprintf(buf,
-			    "\n%d) Name: %s  Domain: %s Mounts: %d ServerOS: %s  ServerNOS: %s\n\tCapabilities: 0x%x",
+			    "\n%d) Name: %s  Domain: %s Mounts: %d ServerOS: %s  \n\tServerNOS: %s\tCapabilities: 0x%x",
 				i, ses->serverName, ses->serverDomain, atomic_read(&ses->inUse),
 				ses->serverOS, ses->serverNOS, ses->capabilities);
 		buf += length;
 		if(ses->server)
-			buf += sprintf(buf, "\tLocal Users To Same Server: %d SecMode: 0x%x",
+			buf += sprintf(buf, "\n\tLocal Users To Same Server: %d SecMode: 0x%x",
 				atomic_read(&ses->server->socketUseCount),ses->server->secMode);
 	}
 	read_unlock(&GlobalSMBSeslock);
@@ -123,6 +123,8 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset,
 			    sprintf(buf, " type: %d ",
 				    tcon->fsDevInfo.DeviceType);
 		buf += length;
+		if(tcon->tidStatus == CifsNeedReconnect)
+			buf += sprintf(buf, "\tDISCONNECTED ");
 	}
 	read_unlock(&GlobalSMBSeslock);
 	length = sprintf(buf, "\n");
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index c25067dac20b..98cabaf2d738 100755
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/cifsencrypt.c
  *
- *   Copyright (c) International Business Machines  Corp., 2003
+ *   Copyright (C) International Business Machines  Corp., 2003
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -64,13 +64,13 @@ int cifs_sign_smb(struct smb_hdr * cifs_pdu, struct cifsSesInfo * ses,
 	if((le32_to_cpu(cifs_pdu->Flags2) & SMBFLG2_SECURITY_SIGNATURE) == 0) 
 		return rc;
 
-	write_lock(&GlobalMid_Lock);
+	spin_lock(&GlobalMid_Lock);
 	cifs_pdu->Signature.Sequence.SequenceNumber = cpu_to_le32(ses->sequence_number);
 	cifs_pdu->Signature.Sequence.Reserved = 0;
 	
 	*pexpected_response_sequence_number = ses->sequence_number++;
 	ses->sequence_number++;
-	write_unlock(&GlobalMid_Lock);
+	spin_unlock(&GlobalMid_Lock);
 
 	rc = cifs_calculate_signature(cifs_pdu, ses->mac_signing_key,smb_signature);
 	if(rc)
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index fd3641203e4c..c4824f13c422 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/cifsfs.c
  *
- *   Copyright (c) International Business Machines  Corp., 2002
+ *   Copyright (C) International Business Machines  Corp., 2002,2003
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   Common Internet FileSystem (CIFS) client
@@ -571,8 +571,6 @@ cifs_destroy_mids(void)
 
 static int cifs_oplock_thread(void * dummyarg)
 {
-	struct list_head * tmp;
-	struct list_head * tmp1;
 	struct oplock_q_entry * oplock_item;
 	struct cifsTconInfo *pTcon;
 	struct inode * inode;
@@ -585,19 +583,22 @@ static int cifs_oplock_thread(void * dummyarg)
 	oplockThread = current;
 	do {
 		set_current_state(TASK_INTERRUPTIBLE);
-		schedule_timeout(100*HZ);
-		/* BB add missing code */
-		write_lock(&GlobalMid_Lock); 
-		list_for_each_safe(tmp, tmp1, &GlobalOplock_Q) {
-			oplock_item = list_entry(tmp, struct oplock_q_entry,
-							       qhead);
+		
+		schedule_timeout(39*HZ);
+		spin_lock(&GlobalMid_Lock);
+		if(list_empty(&GlobalOplock_Q)) {
+			spin_unlock(&GlobalMid_Lock);
+			schedule_timeout(39*HZ);
+		} else {
+			oplock_item = list_entry(GlobalOplock_Q.next, 
+				struct oplock_q_entry, qhead);
 			if(oplock_item) {
 				pTcon = oplock_item->tcon;
 				inode = oplock_item->pinode;
 				netfid = oplock_item->netfid;
 				DeleteOplockQEntry(oplock_item);
-				write_unlock(&GlobalMid_Lock);
-				if (S_ISREG(inode->i_mode))
+				spin_unlock(&GlobalMid_Lock);
+				if (S_ISREG(inode->i_mode)) 
 					rc = filemap_fdatawrite(inode->i_mapping);
 				else
 					rc = 0;
@@ -609,11 +610,9 @@ static int cifs_oplock_thread(void * dummyarg)
 					0, LOCKING_ANDX_OPLOCK_RELEASE,
 					0 /* wait flag */);
 				cFYI(1,("Oplock release rc = %d ",rc));
-				write_lock(&GlobalMid_Lock);
 			} else
-				break;
+				spin_unlock(&GlobalMid_Lock);
 		}
-		write_unlock(&GlobalMid_Lock);
 	} while(!signal_pending(current));
 	complete_and_exit (&cifs_oplock_exited, 0);
 }
@@ -640,7 +639,7 @@ init_cifs(void)
 	GlobalTotalActiveXid = 0;
 	GlobalMaxActiveXid = 0;
 	GlobalSMBSeslock = RW_LOCK_UNLOCKED;
-	GlobalMid_Lock = RW_LOCK_UNLOCKED;
+	GlobalMid_Lock = SPIN_LOCK_UNLOCKED;
 
 	rc = cifs_init_inodecache();
 	if (!rc) {
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 1ff8862d39e9..b2022189e25d 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/cifsglob.h
  *
- *   Copyright (c) International Business Machines  Corp., 2002
+ *   Copyright (C) International Business Machines  Corp., 2002,2003
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -212,6 +212,7 @@ struct cifsFileInfo {
 	int endOfSearch:1;	/* we have reached end of search */
 	int closePend:1;	/* file is marked to close */
 	int emptyDir:1;
+	int invalidHandle:1;  /* file closed via session abend */
 	char * search_resume_name;
 	unsigned int resume_name_length;
 	__u32 resume_key;
@@ -294,7 +295,27 @@ struct servers_not_supported { /* @z4a */
  * following to be declared.
  */
 
-/* BB Every global should have an associated mutex for safe update BB */
+/****************************************************************************
+ *  Locking notes.  All updates to global variables and lists should be
+ *                  protected by spinlocks or semaphores.
+ *
+ *  Spinlocks
+ *  ---------
+ *  GlobalMid_Lock protects:
+ *	list operations on pending_mid_q and oplockQ
+ *      updates to XID counters, multiplex id  and SMB sequence numbers
+ *  GlobalSMBSesLock protects:
+ *	list operations on tcp and SMB session lists and tCon lists
+ *  f_owner.lock protects certain per file struct operations
+ *  mapping->page_lock protects certain per page operations
+ *
+ *  Semaphores
+ *  ----------
+ *  sesSem     operations on smb session
+ *  tconSem    operations on tree connection
+ *  i_sem      inode operations 
+ *
+ ****************************************************************************/
 
 #ifdef DECLARE_GLOBALS_HERE
 #define GLOBAL_EXTERN
@@ -327,7 +348,7 @@ GLOBAL_EXTERN struct list_head GlobalOplock_Q;
 GLOBAL_EXTERN unsigned int GlobalCurrentXid;	/* protected by GlobalMid_Sem */
 GLOBAL_EXTERN unsigned int GlobalTotalActiveXid;	/* prot by GlobalMid_Sem */
 GLOBAL_EXTERN unsigned int GlobalMaxActiveXid;	/* prot by GlobalMid_Sem */
-GLOBAL_EXTERN rwlock_t GlobalMid_Lock;  /* protects above and list operations */
+GLOBAL_EXTERN spinlock_t GlobalMid_Lock;  /* protects above and list operations */
 					/* on midQ entries */
 GLOBAL_EXTERN char Local_System_Name[15];
 
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 81151b425694..9a6efc967fcf 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/connect.c
  *
- *   Copyright (c) International Business Machines  Corp., 2002
+ *   Copyright (C) International Business Machines  Corp., 2002,2003
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -272,7 +272,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
 				}
 
 				task_to_wake = NULL;
-				read_lock(&GlobalMid_Lock);
+				spin_lock(&GlobalMid_Lock);
 				list_for_each(tmp, &server->pending_mid_q) {
 					mid_entry = list_entry(tmp, struct
 							       mid_q_entry,
@@ -288,7 +288,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
 						    MID_RESPONSE_RECEIVED;
 					}
 				}
-				read_unlock(&GlobalMid_Lock);
+				spin_unlock(&GlobalMid_Lock);
 				if (task_to_wake) {
 					smb_buffer = NULL;	/* will be freed by users thread after he is done */
 					wake_up_process(task_to_wake);
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index b1245a53db80..0ccf7b0c55ff 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -218,6 +218,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
 				pCifsFile->netfid = fileHandle;
 				pCifsFile->pid = current->tgid;
 				pCifsFile->pInode = newinode;
+				pCifsFile->invalidHandle = FALSE;
 				/* pCifsFile->pfile = file; */ /* put in at open time */
 				write_lock(&GlobalSMBSeslock);
 				list_add(&pCifsFile->tlist,&pTcon->openFileList);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 5517fb0f4d75..7c8d8db447c2 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -144,6 +144,7 @@ cifs_open(struct inode *inode, struct file *file)
 			pCifsFile->pid = current->pid;
 			pCifsFile->pfile = file; /* needed for writepage */
 			pCifsFile->pInode = inode;
+			pCifsFile->invalidHandle = FALSE;
 			write_lock(&file->f_owner.lock);
 			write_lock(&GlobalSMBSeslock);
 			list_add(&pCifsFile->tlist,&pTcon->openFileList);
@@ -215,33 +216,51 @@ int reopen_files(struct cifsTconInfo * pTcon, struct nls_table * nlsinfo)
 	struct list_head *tmp;
 	struct list_head *tmp1;
 
-/* list all files open on tree connection */
-	write_lock(&GlobalSMBSeslock); /* BB change to semaphore */
+/* list all files open on tree connection and mark them invalid */
+	write_lock(&GlobalSMBSeslock);
 	list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {            
 		open_file = list_entry(tmp,struct cifsFileInfo, tlist);
 		if(open_file) {
-			if(open_file->search_resume_name) {
-				kfree(open_file->search_resume_name);
-			}
-			file = open_file->pfile;
-			list_del(&open_file->flist);
-			list_del(&open_file->tlist);
-			kfree(open_file);
-			if(file) {                
-				file->private_data = NULL;
-				if(file->f_dentry == 0) {
-					cFYI(1,("Null dentry for file %p",file));
-				} else {
-					rc = cifs_open(file->f_dentry->d_inode,file);
-					if(rc) {
-						cFYI(1,("reconnecting file %s failed with %d",
-							file->f_dentry->d_name.name,rc));
+			open_file->invalidHandle = TRUE;
+		}
+	}
+
+	/* reopen files */
+	for(;;) {
+		/* BB need to fix above to check list end and skip entries we do not need to reopen */
+		if(list_empty(&pTcon->openFileList)) {
+				break;
+		} else {
+			open_file = list_entry(tmp,struct cifsFileInfo, tlist);
+			if(open_file) {
+				if(open_file->invalidHandle == FALSE)
+					continue;
+				if(open_file->search_resume_name) {
+					kfree(open_file->search_resume_name);
+				}
+				file = open_file->pfile;
+				list_del(&open_file->flist);
+				list_del(&open_file->tlist);
+				kfree(open_file);
+				if(file) {                
+					file->private_data = NULL;
+					if(file->f_dentry == 0) {
+						cFYI(1,("Null dentry for file %p",file));
 					} else {
-						cFYI(1,("reconnection of %s succeeded",
+						write_unlock(&GlobalSMBSeslock);
+						rc = cifs_open(file->f_dentry->d_inode,file);
+						write_lock(&GlobalSMBSeslock);
+						if(rc) {
+							cFYI(1,("reconnecting file %s failed with %d",
+								file->f_dentry->d_name.name,rc));
+						} else {
+							cFYI(1,("reconnection of %s succeeded",
 							file->f_dentry->d_name.name));
+						}
 					}
-				} 
+				}
 			}
+
 		}
 	}
 	write_unlock(&GlobalSMBSeslock);
@@ -264,6 +283,7 @@ cifs_close(struct inode *inode, struct file *file)
 	pTcon = cifs_sb->tcon;
 	if (pSMBFile) {
 		write_lock(&file->f_owner.lock);
+		pSMBFile->invalidHandle = TRUE;
 		list_del(&pSMBFile->flist);
 		list_del(&pSMBFile->tlist);
 		write_unlock(&file->f_owner.lock);
@@ -1242,8 +1262,10 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
 					rc = 0;
 					break;
 				}
-			} else
+			} else {
+				cifsFile->invalidHandle = TRUE;
 				CIFSFindClose(xid, pTcon, cifsFile->netfid);
+			}
 			if(cifsFile->search_resume_name) {
 				kfree(cifsFile->search_resume_name);
 				cifsFile->search_resume_name = NULL;
@@ -1267,6 +1289,7 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
 				cifsFile =
 				    (struct cifsFileInfo *) file->private_data;
 				cifsFile->netfid = searchHandle;
+				cifsFile->invalidHandle = FALSE;
 			} else {
 				rc = -ENOMEM;
 				break;
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 65d84985a3ce..c874a2dc97cc 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/misc.c
  *
- *   Copyright (c) International Business Machines  Corp., 2002,2003
+ *   Copyright (C) International Business Machines  Corp., 2002,2003
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -42,23 +42,23 @@ _GetXid(void)
 {
 	unsigned int xid;
 
-	write_lock(&GlobalMid_Lock);
+	spin_lock(&GlobalMid_Lock);
 	GlobalTotalActiveXid++;
 	if (GlobalTotalActiveXid > GlobalMaxActiveXid)
 		GlobalMaxActiveXid = GlobalTotalActiveXid;	/* keep high water mark for number of simultaneous vfs ops in our filesystem */
 	xid = GlobalCurrentXid++;
-	write_unlock(&GlobalMid_Lock);
+	spin_unlock(&GlobalMid_Lock);
 	return xid;
 }
 
 void
 _FreeXid(unsigned int xid)
 {
-	write_lock(&GlobalMid_Lock);
+	spin_lock(&GlobalMid_Lock);
 	/* if(GlobalTotalActiveXid == 0)
 		BUG(); */
 	GlobalTotalActiveXid--;
-	write_unlock(&GlobalMid_Lock);
+	spin_unlock(&GlobalMid_Lock);
 }
 
 struct cifsSesInfo *
@@ -217,10 +217,10 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
 	buffer->Pid = tmp & 0xFFFF;
 	tmp >>= 16;
 	buffer->PidHigh = tmp & 0xFFFF;
-	write_lock(&GlobalMid_Lock);
+	spin_lock(&GlobalMid_Lock);
 	GlobalMid++;
 	buffer->Mid = GlobalMid;
-	write_unlock(&GlobalMid_Lock);
+	spin_unlock(&GlobalMid_Lock);
 	if (treeCon) {
 		buffer->Tid = treeCon->tid;
 		if (treeCon->ses) {
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 377eebddc7e3..ff013e77d440 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/transport.c
  *
- *   Copyright (c) International Business Machines  Corp., 2002
+ *   Copyright (C) International Business Machines  Corp., 2002,2003
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -39,7 +39,6 @@ AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
 	struct mid_q_entry *temp;
 	int timeout = 10 * HZ;
 
-/* BB add spinlock to protect midq for each session BB */
 	if (ses == NULL) {
 		cERROR(1, ("Null session passed in to AllocMidQEntry "));
 		return NULL;
@@ -72,11 +71,11 @@ AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
 	}
 
 	if (ses->server->tcpStatus == CifsGood) {
-		write_lock(&GlobalMid_Lock);
+		spin_lock(&GlobalMid_Lock);
 		list_add_tail(&temp->qhead, &ses->server->pending_mid_q);
 		atomic_inc(&midCount);
 		temp->midState = MID_REQUEST_ALLOCATED;
-		write_unlock(&GlobalMid_Lock);
+		spin_unlock(&GlobalMid_Lock);
 	} else { 
 		cERROR(1,("Need to reconnect after session died to server"));
 		if (temp)
@@ -89,12 +88,11 @@ AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
 void
 DeleteMidQEntry(struct mid_q_entry *midEntry)
 {
-	/* BB add spinlock to protect midq for each session BB */
-	write_lock(&GlobalMid_Lock);
+	spin_lock(&GlobalMid_Lock);
 	midEntry->midState = MID_FREE;
 	list_del(&midEntry->qhead);
 	atomic_dec(&midCount);
-	write_unlock(&GlobalMid_Lock);
+	spin_unlock(&GlobalMid_Lock);
 	buf_release(midEntry->resp_buf);
 	kmem_cache_free(cifs_mid_cachep, midEntry);
 }
@@ -115,9 +113,9 @@ AllocOplockQEntry(struct inode * pinode, __u16 fid, struct cifsTconInfo * tcon)
 		temp->pinode = pinode;
 		temp->tcon = tcon;
 		temp->netfid = fid;
-		write_lock(&GlobalMid_Lock);
+		spin_lock(&GlobalMid_Lock);
 		list_add_tail(&temp->qhead, &GlobalOplock_Q);
-		write_unlock(&GlobalMid_Lock);
+		spin_unlock(&GlobalMid_Lock);
 	}
 	return temp;
 
@@ -125,11 +123,10 @@ AllocOplockQEntry(struct inode * pinode, __u16 fid, struct cifsTconInfo * tcon)
 
 void DeleteOplockQEntry(struct oplock_q_entry * oplockEntry)
 {
-	/* BB add spinlock to protect midq for each session BB */
-	write_lock(&GlobalMid_Lock); 
+	spin_lock(&GlobalMid_Lock); 
     /* should we check if list empty first? */
 	list_del(&oplockEntry->qhead);
-	write_unlock(&GlobalMid_Lock);
+	spin_unlock(&GlobalMid_Lock);
 	kmem_cache_free(cifs_oplock_cachep, oplockEntry);
 }
 
-- 
2.30.9