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