Commit fded59fb authored by Steve French's avatar Steve French

Merge bk://linux.bkbits.net/linux-2.5

into hostme.bitkeeper.com:/repos/c/cifs/linux-2.5cifs
parents 7140df08 53e872ef
......@@ -20,6 +20,7 @@ Patch Contributors
Zwane Mwaikambo
Andi Kleen
Amrut Joshi
Shobhit Dayal
Test case and Bug Report contributors
-------------------------------------
......
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
------------
Fix oops in reopen_files when invalid dentry. drop dentry on server rename
......
/*
* 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");
......
/*
* 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)
......
/*
* 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) {
......
/*
* 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];
......
/*
* 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);
......
......@@ -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);
......
......@@ -122,6 +122,12 @@ cifs_open(struct inode *inode, struct file *file)
and calling get_inode_info with returned buf (at least
helps non-Unix server case */
buf = kmalloc(sizeof(FILE_ALL_INFO),GFP_KERNEL);
if(buf==0) {
if (full_path)
kfree(full_path);
FreeXid(xid);
return -ENOMEM;
}
rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess,
CREATE_NOT_DIR, &netfid, &oplock, buf, cifs_sb->local_nls);
if (rc) {
......@@ -138,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);
......@@ -200,48 +207,147 @@ int relock_files(struct cifsFileInfo * cifsFile)
return rc;
}
static int cifs_reopen_file(struct inode *inode, struct file *file)
{
int rc = -EACCES;
int xid, oplock;
struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *pTcon;
struct cifsFileInfo *pCifsFile;
struct cifsInodeInfo *pCifsInode;
char *full_path = NULL;
int desiredAccess = 0x20197;
int disposition = FILE_OPEN;
__u16 netfid;
FILE_ALL_INFO * buf = NULL;
xid = GetXid();
cifs_sb = CIFS_SB(inode->i_sb);
pTcon = cifs_sb->tcon;
full_path = build_path_from_dentry(file->f_dentry);
cFYI(1, (" inode = 0x%p file flags are 0x%x for %s", inode, file->f_flags,full_path));
if ((file->f_flags & O_ACCMODE) == O_RDONLY)
desiredAccess = GENERIC_READ;
else if ((file->f_flags & O_ACCMODE) == O_WRONLY)
desiredAccess = GENERIC_WRITE;
else if ((file->f_flags & O_ACCMODE) == O_RDWR)
desiredAccess = GENERIC_ALL;
if (oplockEnabled)
oplock = REQ_OPLOCK;
else
oplock = FALSE;
/* BB pass O_SYNC flag through on file attributes .. BB */
/* Also refresh inode by passing in file_info buf returned by SMBOpen
and calling get_inode_info with returned buf (at least
helps non-Unix server case */
buf = kmalloc(sizeof(FILE_ALL_INFO),GFP_KERNEL);
if(buf==0) {
if (full_path)
kfree(full_path);
FreeXid(xid);
return -ENOMEM;
}
rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess,
CREATE_NOT_DIR, &netfid, &oplock, buf, cifs_sb->local_nls);
if (rc) {
cFYI(1, ("cifs_open returned 0x%x ", rc));
cFYI(1, ("oplock: %d ", oplock));
} else {
if (file->private_data) {
pCifsFile = (struct cifsFileInfo *) file->private_data;
pCifsFile->netfid = netfid;
pCifsFile->invalidHandle = FALSE;
pCifsInode = CIFS_I(file->f_dentry->d_inode);
if(pCifsInode) {
if (pTcon->ses->capabilities & CAP_UNIX)
rc = cifs_get_inode_info_unix(&file->f_dentry->d_inode,
full_path, inode->i_sb);
else
rc = cifs_get_inode_info(&file->f_dentry->d_inode,
full_path, buf, inode->i_sb);
if(oplock == OPLOCK_EXCLUSIVE) {
pCifsInode->clientCanCacheAll = TRUE;
pCifsInode->clientCanCacheRead = TRUE;
cFYI(1,("Exclusive Oplock granted on inode %p",file->f_dentry->d_inode));
} else if(oplock == OPLOCK_READ) {
pCifsInode->clientCanCacheRead = TRUE;
pCifsInode->clientCanCacheAll = FALSE;
} else {
pCifsInode->clientCanCacheRead = FALSE;
pCifsInode->clientCanCacheAll = FALSE;
}
}
} else
rc = -EBADF;
}
if (buf)
kfree(buf);
if (full_path)
kfree(full_path);
FreeXid(xid);
return rc;
}
/* Try to reopen files that were closed when session to server was lost */
int reopen_files(struct cifsTconInfo * pTcon, struct nls_table * nlsinfo)
{
int rc = 0;
struct cifsFileInfo *open_file = NULL;
struct file * file = NULL;
struct list_head *tmp;
struct list_head *tmp1;
struct list_head invalid_file_list;
struct list_head * tmp;
struct list_head * tmp1;
/* list all files open on tree connection */
read_lock(&GlobalSMBSeslock);
INIT_LIST_HEAD(&invalid_file_list);
/* 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);
open_file->invalidHandle = TRUE;
list_move(&open_file->tlist,&invalid_file_list);
}
}
/* reopen files */
list_for_each(tmp,&invalid_file_list) {
/* BB need to fix above to check list end and skip entries we do not need to reopen */
open_file = list_entry(tmp,struct cifsFileInfo, tlist);
if(open_file == NULL) {
break;
} else {
if(open_file->invalidHandle == FALSE) {
list_move(&open_file->tlist,&pTcon->openFileList);
continue;
}
file = open_file->pfile;
list_del(&open_file->flist);
list_del(&open_file->tlist);
kfree(open_file);
if(file) {
file->private_data = NULL;
read_unlock(&GlobalSMBSeslock);
if(file->f_dentry == 0) {
cFYI(1,("Null dentry for file %p",file));
read_lock(&GlobalSMBSeslock);
if(file->f_dentry == 0) {
cFYI(1,("Null dentry for file %p",file));
} else {
write_unlock(&GlobalSMBSeslock);
rc = cifs_reopen_file(file->f_dentry->d_inode,file);
write_lock(&GlobalSMBSeslock);
list_move(&open_file->tlist,&pTcon->openFileList);
if(rc) {
cFYI(1,("reconnecting file %s failed with %d",
file->f_dentry->d_name.name,rc));
} else {
rc = cifs_open(file->f_dentry->d_inode,file);
read_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));
}
}
cFYI(1,("reconnection of %s succeeded",
file->f_dentry->d_name.name));
}
}
}
}
read_unlock(&GlobalSMBSeslock);
write_unlock(&GlobalSMBSeslock);
return rc;
}
......@@ -261,6 +367,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);
......@@ -447,7 +554,6 @@ cifs_write(struct file * file, const char *write_data,
&bytes_written,
write_data + total_written, long_op);
if (rc || (bytes_written == 0)) {
FreeXid(xid);
if (total_written)
break;
else {
......@@ -528,25 +634,28 @@ cifs_partialpagewrite(struct page *page,unsigned from, unsigned to)
cifsInode = CIFS_I(mapping->host);
read_lock(&GlobalSMBSeslock);
read_lock(&GlobalSMBSeslock); /* BB switch to semaphore */
list_for_each_safe(tmp, tmp1, &cifsInode->openFileList) {
open_file = list_entry(tmp,struct cifsFileInfo, flist);
/* We check if file is open for writing first */
if((open_file->pfile) &&
((open_file->pfile->f_flags & O_RDWR) ||
(open_file->pfile->f_flags & O_WRONLY))) {
read_unlock(&GlobalSMBSeslock);
bytes_written = cifs_write(open_file->pfile, write_data,
to-from, &offset);
read_lock(&GlobalSMBSeslock);
/* Does mm or vfs already set times? */
inode->i_atime = inode->i_mtime = CURRENT_TIME;
if ((bytes_written > 0) && (offset)) {
rc = 0;
break;
} else if(bytes_written < 0) {
rc = bytes_written;
}
}
if(tmp->next == NULL) {
cFYI(1,("File instance %p removed",tmp));
break;
}
}
read_unlock(&GlobalSMBSeslock);
if(open_file == NULL) {
......@@ -1236,8 +1345,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;
......@@ -1261,6 +1372,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;
......
/*
* 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) {
......
/*
* 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);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment