Merge to Fedora kernel-2.6.18-1.2255_FC5-vs2.0.2.2-rc9 patched with stable patch...
[linux-2.6.git] / fs / cifs / cifsfs.c
index 8cc23e7..9373e22 100644 (file)
@@ -32,6 +32,8 @@
 #include <linux/seq_file.h>
 #include <linux/vfs.h>
 #include <linux/mempool.h>
 #include <linux/seq_file.h>
 #include <linux/vfs.h>
 #include <linux/mempool.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
 #include "cifsfs.h"
 #include "cifspdu.h"
 #define DECLARE_GLOBALS_HERE
 #include "cifsfs.h"
 #include "cifspdu.h"
 #define DECLARE_GLOBALS_HERE
@@ -54,11 +56,13 @@ unsigned int experimEnabled = 0;
 unsigned int linuxExtEnabled = 1;
 unsigned int lookupCacheEnabled = 1;
 unsigned int multiuser_mount = 0;
 unsigned int linuxExtEnabled = 1;
 unsigned int lookupCacheEnabled = 1;
 unsigned int multiuser_mount = 0;
-unsigned int extended_security = 0;
-unsigned int ntlmv2_support = 0;
+unsigned int extended_security = CIFSSEC_DEF;
+/* unsigned int ntlmv2_support = 0; */
 unsigned int sign_CIFS_PDUs = 1;
 extern struct task_struct * oplockThread; /* remove sparse warning */
 struct task_struct * oplockThread = NULL;
 unsigned int sign_CIFS_PDUs = 1;
 extern struct task_struct * oplockThread; /* remove sparse warning */
 struct task_struct * oplockThread = NULL;
+extern struct task_struct * dnotifyThread; /* remove sparse warning */
+struct task_struct * dnotifyThread = NULL;
 unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
 module_param(CIFSMaxBufSize, int, 0);
 MODULE_PARM_DESC(CIFSMaxBufSize,"Network buffer size (not including header). Default: 16384 Range: 8192 to 130048");
 unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
 module_param(CIFSMaxBufSize, int, 0);
 MODULE_PARM_DESC(CIFSMaxBufSize,"Network buffer size (not including header). Default: 16384 Range: 8192 to 130048");
@@ -72,8 +76,6 @@ unsigned int cifs_max_pending = CIFS_MAX_REQ;
 module_param(cifs_max_pending, int, 0);
 MODULE_PARM_DESC(cifs_max_pending,"Simultaneous requests to server. Default: 50 Range: 2 to 256");
 
 module_param(cifs_max_pending, int, 0);
 MODULE_PARM_DESC(cifs_max_pending,"Simultaneous requests to server. Default: 50 Range: 2 to 256");
 
-static DECLARE_COMPLETION(cifs_oplock_exited);
-
 extern mempool_t *cifs_sm_req_poolp;
 extern mempool_t *cifs_req_poolp;
 extern mempool_t *cifs_mid_poolp;
 extern mempool_t *cifs_sm_req_poolp;
 extern mempool_t *cifs_req_poolp;
 extern mempool_t *cifs_mid_poolp;
@@ -89,13 +91,10 @@ cifs_read_super(struct super_block *sb, void *data,
        int rc = 0;
 
        sb->s_flags |= MS_NODIRATIME; /* and probably even noatime */
        int rc = 0;
 
        sb->s_flags |= MS_NODIRATIME; /* and probably even noatime */
-       sb->s_fs_info = kmalloc(sizeof(struct cifs_sb_info),GFP_KERNEL);
+       sb->s_fs_info = kzalloc(sizeof(struct cifs_sb_info),GFP_KERNEL);
        cifs_sb = CIFS_SB(sb);
        if(cifs_sb == NULL)
                return -ENOMEM;
        cifs_sb = CIFS_SB(sb);
        if(cifs_sb == NULL)
                return -ENOMEM;
-       else
-               memset(cifs_sb,0,sizeof(struct cifs_sb_info));
-       
 
        rc = cifs_mount(sb, cifs_sb, data, devname);
 
 
        rc = cifs_mount(sb, cifs_sb, data, devname);
 
@@ -167,8 +166,9 @@ cifs_put_super(struct super_block *sb)
 }
 
 static int
 }
 
 static int
-cifs_statfs(struct super_block *sb, struct kstatfs *buf)
+cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
 {
+       struct super_block *sb = dentry->d_sb;
        int xid; 
        int rc = -EOPNOTSUPP;
        struct cifs_sb_info *cifs_sb;
        int xid; 
        int rc = -EOPNOTSUPP;
        struct cifs_sb_info *cifs_sb;
@@ -202,6 +202,10 @@ cifs_statfs(struct super_block *sb, struct kstatfs *buf)
 #endif /* CIFS_EXPERIMENTAL */
        rc = CIFSSMBQFSInfo(xid, pTcon, buf);
 
 #endif /* CIFS_EXPERIMENTAL */
        rc = CIFSSMBQFSInfo(xid, pTcon, buf);
 
+       /* Old Windows servers do not support level 103, retry with level 
+          one if old server failed the previous call */ 
+       if(rc)
+               rc = SMBOldQFSInfo(xid, pTcon, buf);
        /*     
           int f_type;
           __fsid_t f_fsid;
        /*     
           int f_type;
           __fsid_t f_fsid;
@@ -251,9 +255,8 @@ cifs_alloc_inode(struct super_block *sb)
        file data or metadata */
        cifs_inode->clientCanCacheRead = FALSE;
        cifs_inode->clientCanCacheAll = FALSE;
        file data or metadata */
        cifs_inode->clientCanCacheRead = FALSE;
        cifs_inode->clientCanCacheAll = FALSE;
-       cifs_inode->vfs_inode.i_blksize = CIFS_MAX_MSGSIZE;
        cifs_inode->vfs_inode.i_blkbits = 14;  /* 2**14 = CIFS_MAX_MSGSIZE */
        cifs_inode->vfs_inode.i_blkbits = 14;  /* 2**14 = CIFS_MAX_MSGSIZE */
-
+       cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME;
        INIT_LIST_HEAD(&cifs_inode->openFileList);
        return &cifs_inode->vfs_inode;
 }
        INIT_LIST_HEAD(&cifs_inode->openFileList);
        return &cifs_inode->vfs_inode;
 }
@@ -398,6 +401,42 @@ static struct quotactl_ops cifs_quotactl_ops = {
 };
 #endif
 
 };
 #endif
 
+static void cifs_umount_begin(struct vfsmount * vfsmnt, int flags)
+{
+       struct cifs_sb_info *cifs_sb;
+       struct cifsTconInfo * tcon;
+
+       if (!(flags & MNT_FORCE))
+               return;
+       cifs_sb = CIFS_SB(vfsmnt->mnt_sb);
+       if(cifs_sb == NULL)
+               return;
+
+       tcon = cifs_sb->tcon;
+       if(tcon == NULL)
+               return;
+       down(&tcon->tconSem);
+       if (atomic_read(&tcon->useCount) == 1)
+               tcon->tidStatus = CifsExiting;
+       up(&tcon->tconSem);
+
+       /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */
+       /* cancel_notify_requests(tcon); */
+       if(tcon->ses && tcon->ses->server)
+       {
+               cFYI(1,("wake up tasks now - umount begin not complete"));
+               wake_up_all(&tcon->ses->server->request_q);
+               wake_up_all(&tcon->ses->server->response_q);
+               msleep(1); /* yield */
+               /* we have to kick the requests once more */
+               wake_up_all(&tcon->ses->server->response_q);
+               msleep(1);
+       }
+/* BB FIXME - finish add checks for tidStatus BB */
+
+       return;
+}
+
 static int cifs_remount(struct super_block *sb, int *flags, char *data)
 {
        *flags |= MS_NODIRATIME;
 static int cifs_remount(struct super_block *sb, int *flags, char *data)
 {
        *flags |= MS_NODIRATIME;
@@ -415,13 +454,13 @@ struct super_operations cifs_super_ops = {
    unless later we add lazy close of inodes or unless the kernel forgets to call
    us with the same number of releases (closes) as opens */
        .show_options = cifs_show_options,
    unless later we add lazy close of inodes or unless the kernel forgets to call
    us with the same number of releases (closes) as opens */
        .show_options = cifs_show_options,
-/*    .umount_begin   = cifs_umount_begin, *//* consider adding in the future */
+       .umount_begin   = cifs_umount_begin,
        .remount_fs = cifs_remount,
 };
 
        .remount_fs = cifs_remount,
 };
 
-static struct super_block *
+static int
 cifs_get_sb(struct file_system_type *fs_type,
 cifs_get_sb(struct file_system_type *fs_type,
-           int flags, const char *dev_name, void *data)
+           int flags, const char *dev_name, void *data, struct vfsmount *mnt)
 {
        int rc;
        struct super_block *sb = sget(fs_type, NULL, set_anon_super, NULL);
 {
        int rc;
        struct super_block *sb = sget(fs_type, NULL, set_anon_super, NULL);
@@ -429,70 +468,54 @@ cifs_get_sb(struct file_system_type *fs_type,
        cFYI(1, ("Devname: %s flags: %d ", dev_name, flags));
 
        if (IS_ERR(sb))
        cFYI(1, ("Devname: %s flags: %d ", dev_name, flags));
 
        if (IS_ERR(sb))
-               return sb;
+               return PTR_ERR(sb);
 
        sb->s_flags = flags;
 
 
        sb->s_flags = flags;
 
-       rc = cifs_read_super(sb, data, dev_name, flags & MS_VERBOSE ? 1 : 0);
+       rc = cifs_read_super(sb, data, dev_name, flags & MS_SILENT ? 1 : 0);
        if (rc) {
                up_write(&sb->s_umount);
                deactivate_super(sb);
        if (rc) {
                up_write(&sb->s_umount);
                deactivate_super(sb);
-               return ERR_PTR(rc);
+               return rc;
        }
        sb->s_flags |= MS_ACTIVE;
        }
        sb->s_flags |= MS_ACTIVE;
-       return sb;
+       return simple_set_mnt(mnt, sb);
 }
 
 }
 
-static ssize_t
-cifs_read_wrapper(struct file * file, char __user *read_data, size_t read_size,
-          loff_t * poffset)
+static ssize_t cifs_file_writev(struct file *file, const struct iovec *iov,
+                               unsigned long nr_segs, loff_t *ppos)
 {
 {
-       if(file->f_dentry == NULL)
-               return -EIO;
-       else if(file->f_dentry->d_inode == NULL)
-               return -EIO;
-
-       cFYI(1,("In read_wrapper size %zd at %lld",read_size,*poffset));
+       struct inode *inode = file->f_dentry->d_inode;
+       ssize_t written;
 
 
-       if(CIFS_I(file->f_dentry->d_inode)->clientCanCacheRead) {
-               return generic_file_read(file,read_data,read_size,poffset);
-       } else {
-               /* BB do we need to lock inode from here until after invalidate? */
-/*             if(file->f_dentry->d_inode->i_mapping) {
-                       filemap_fdatawrite(file->f_dentry->d_inode->i_mapping);
-                       filemap_fdatawait(file->f_dentry->d_inode->i_mapping);
-               }*/
-/*             cifs_revalidate(file->f_dentry);*/ /* BB fixme */
-
-               /* BB we should make timer configurable - perhaps 
-                  by simply calling cifs_revalidate here */
-               /* invalidate_remote_inode(file->f_dentry->d_inode);*/
-               return generic_file_read(file,read_data,read_size,poffset);
-       }
+       written = generic_file_writev(file, iov, nr_segs, ppos);
+       if (!CIFS_I(inode)->clientCanCacheAll)
+               filemap_fdatawrite(inode->i_mapping);
+       return written;
 }
 
 }
 
-static ssize_t
-cifs_write_wrapper(struct file * file, const char __user *write_data,
-           size_t write_size, loff_t * poffset) 
+static ssize_t cifs_file_aio_write(struct kiocb *iocb, const char __user *buf,
+                                  size_t count, loff_t pos)
 {
 {
+       struct inode *inode = iocb->ki_filp->f_dentry->d_inode;
        ssize_t written;
 
        ssize_t written;
 
-       if(file->f_dentry == NULL)
-               return -EIO;
-       else if(file->f_dentry->d_inode == NULL)
-               return -EIO;
-
-       cFYI(1,("In write_wrapper size %zd at %lld",write_size,*poffset));
-
-       written = generic_file_write(file,write_data,write_size,poffset);
-       if(!CIFS_I(file->f_dentry->d_inode)->clientCanCacheAll)  {
-               if(file->f_dentry->d_inode->i_mapping) {
-                       filemap_fdatawrite(file->f_dentry->d_inode->i_mapping);
-               }
-       }
+       written = generic_file_aio_write(iocb, buf, count, pos);
+       if (!CIFS_I(inode)->clientCanCacheAll)
+               filemap_fdatawrite(inode->i_mapping);
        return written;
 }
 
        return written;
 }
 
+static loff_t cifs_llseek(struct file *file, loff_t offset, int origin)
+{
+       /* origin == SEEK_END => we must revalidate the cached file length */
+       if (origin == 2) {
+               int retval = cifs_revalidate(file->f_dentry);
+               if (retval < 0)
+                       return (loff_t)retval;
+       }
+       return remote_llseek(file, offset, origin);
+}
 
 static struct file_system_type cifs_fs_type = {
        .owner = THIS_MODULE,
 
 static struct file_system_type cifs_fs_type = {
        .owner = THIS_MODULE,
@@ -553,9 +576,13 @@ struct inode_operations cifs_symlink_inode_ops = {
 #endif 
 };
 
 #endif 
 };
 
-struct file_operations cifs_file_ops = {
-       .read = cifs_read_wrapper,
-       .write = cifs_write_wrapper, 
+const struct file_operations cifs_file_ops = {
+       .read = do_sync_read,
+       .write = do_sync_write,
+       .readv = generic_file_readv,
+       .writev = cifs_file_writev,
+       .aio_read = generic_file_aio_read,
+       .aio_write = cifs_file_aio_write,
        .open = cifs_open,
        .release = cifs_close,
        .lock = cifs_lock,
        .open = cifs_open,
        .release = cifs_close,
        .lock = cifs_lock,
@@ -563,20 +590,17 @@ struct file_operations cifs_file_ops = {
        .flush = cifs_flush,
        .mmap  = cifs_file_mmap,
        .sendfile = generic_file_sendfile,
        .flush = cifs_flush,
        .mmap  = cifs_file_mmap,
        .sendfile = generic_file_sendfile,
+       .llseek = cifs_llseek,
 #ifdef CONFIG_CIFS_POSIX
        .ioctl  = cifs_ioctl,
 #endif /* CONFIG_CIFS_POSIX */
 
 #ifdef CONFIG_CIFS_EXPERIMENTAL
 #ifdef CONFIG_CIFS_POSIX
        .ioctl  = cifs_ioctl,
 #endif /* CONFIG_CIFS_POSIX */
 
 #ifdef CONFIG_CIFS_EXPERIMENTAL
-       .readv = generic_file_readv,
-       .writev = generic_file_writev,
-       .aio_read = generic_file_aio_read,
-       .aio_write = generic_file_aio_write,
        .dir_notify = cifs_dir_notify,
 #endif /* CONFIG_CIFS_EXPERIMENTAL */
 };
 
        .dir_notify = cifs_dir_notify,
 #endif /* CONFIG_CIFS_EXPERIMENTAL */
 };
 
-struct file_operations cifs_file_direct_ops = {
+const struct file_operations cifs_file_direct_ops = {
        /* no mmap, no aio, no readv - 
           BB reevaluate whether they can be done with directio, no cache */
        .read = cifs_user_read,
        /* no mmap, no aio, no readv - 
           BB reevaluate whether they can be done with directio, no cache */
        .read = cifs_user_read,
@@ -590,13 +614,54 @@ struct file_operations cifs_file_direct_ops = {
 #ifdef CONFIG_CIFS_POSIX
        .ioctl  = cifs_ioctl,
 #endif /* CONFIG_CIFS_POSIX */
 #ifdef CONFIG_CIFS_POSIX
        .ioctl  = cifs_ioctl,
 #endif /* CONFIG_CIFS_POSIX */
+       .llseek = cifs_llseek,
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+       .dir_notify = cifs_dir_notify,
+#endif /* CONFIG_CIFS_EXPERIMENTAL */
+};
+const struct file_operations cifs_file_nobrl_ops = {
+       .read = do_sync_read,
+       .write = do_sync_write,
+       .readv = generic_file_readv,
+       .writev = cifs_file_writev,
+       .aio_read = generic_file_aio_read,
+       .aio_write = cifs_file_aio_write,
+       .open = cifs_open,
+       .release = cifs_close,
+       .fsync = cifs_fsync,
+       .flush = cifs_flush,
+       .mmap  = cifs_file_mmap,
+       .sendfile = generic_file_sendfile,
+       .llseek = cifs_llseek,
+#ifdef CONFIG_CIFS_POSIX
+       .ioctl  = cifs_ioctl,
+#endif /* CONFIG_CIFS_POSIX */
 
 #ifdef CONFIG_CIFS_EXPERIMENTAL
        .dir_notify = cifs_dir_notify,
 #endif /* CONFIG_CIFS_EXPERIMENTAL */
 };
 
 
 #ifdef CONFIG_CIFS_EXPERIMENTAL
        .dir_notify = cifs_dir_notify,
 #endif /* CONFIG_CIFS_EXPERIMENTAL */
 };
 
-struct file_operations cifs_dir_ops = {
+const struct file_operations cifs_file_direct_nobrl_ops = {
+       /* no mmap, no aio, no readv - 
+          BB reevaluate whether they can be done with directio, no cache */
+       .read = cifs_user_read,
+       .write = cifs_user_write,
+       .open = cifs_open,
+       .release = cifs_close,
+       .fsync = cifs_fsync,
+       .flush = cifs_flush,
+       .sendfile = generic_file_sendfile, /* BB removeme BB */
+#ifdef CONFIG_CIFS_POSIX
+       .ioctl  = cifs_ioctl,
+#endif /* CONFIG_CIFS_POSIX */
+       .llseek = cifs_llseek,
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+       .dir_notify = cifs_dir_notify,
+#endif /* CONFIG_CIFS_EXPERIMENTAL */
+};
+
+const struct file_operations cifs_dir_ops = {
        .readdir = cifs_readdir,
        .release = cifs_closedir,
        .read    = generic_read_dir,
        .readdir = cifs_readdir,
        .release = cifs_closedir,
        .read    = generic_read_dir,
@@ -623,7 +688,8 @@ cifs_init_inodecache(void)
 {
        cifs_inode_cachep = kmem_cache_create("cifs_inode_cache",
                                              sizeof (struct cifsInodeInfo),
 {
        cifs_inode_cachep = kmem_cache_create("cifs_inode_cache",
                                              sizeof (struct cifsInodeInfo),
-                                             0, SLAB_RECLAIM_ACCOUNT,
+                                             0, (SLAB_RECLAIM_ACCOUNT|
+                                               SLAB_MEM_SPREAD),
                                              cifs_init_once, NULL);
        if (cifs_inode_cachep == NULL)
                return -ENOMEM;
                                              cifs_init_once, NULL);
        if (cifs_inode_cachep == NULL)
                return -ENOMEM;
@@ -665,16 +731,14 @@ cifs_init_request_bufs(void)
                cERROR(1,("cifs_min_rcv set to maximum (64)"));
        }
 
                cERROR(1,("cifs_min_rcv set to maximum (64)"));
        }
 
-       cifs_req_poolp = mempool_create(cifs_min_rcv,
-                                       mempool_alloc_slab,
-                                       mempool_free_slab,
-                                       cifs_req_cachep);
+       cifs_req_poolp = mempool_create_slab_pool(cifs_min_rcv,
+                                                 cifs_req_cachep);
 
        if(cifs_req_poolp == NULL) {
                kmem_cache_destroy(cifs_req_cachep);
                return -ENOMEM;
        }
 
        if(cifs_req_poolp == NULL) {
                kmem_cache_destroy(cifs_req_cachep);
                return -ENOMEM;
        }
-       /* 256 (MAX_CIFS_HDR_SIZE bytes is enough for most SMB responses and
+       /* MAX_CIFS_SMALL_BUFFER_SIZE bytes is enough for most SMB responses and
        almost all handle based requests (but not write response, nor is it
        sufficient for path based requests).  A smaller size would have
        been more efficient (compacting multiple slab items on one 4k page) 
        almost all handle based requests (but not write response, nor is it
        sufficient for path based requests).  A smaller size would have
        been more efficient (compacting multiple slab items on one 4k page) 
@@ -683,7 +747,8 @@ cifs_init_request_bufs(void)
        efficient to alloc 1 per page off the slab compared to 17K (5page) 
        alloc of large cifs buffers even when page debugging is on */
        cifs_sm_req_cachep = kmem_cache_create("cifs_small_rq",
        efficient to alloc 1 per page off the slab compared to 17K (5page) 
        alloc of large cifs buffers even when page debugging is on */
        cifs_sm_req_cachep = kmem_cache_create("cifs_small_rq",
-                       MAX_CIFS_HDR_SIZE, 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+                       MAX_CIFS_SMALL_BUFFER_SIZE, 0, SLAB_HWCACHE_ALIGN, 
+                       NULL, NULL);
        if (cifs_sm_req_cachep == NULL) {
                mempool_destroy(cifs_req_poolp);
                kmem_cache_destroy(cifs_req_cachep);
        if (cifs_sm_req_cachep == NULL) {
                mempool_destroy(cifs_req_poolp);
                kmem_cache_destroy(cifs_req_cachep);
@@ -697,10 +762,8 @@ cifs_init_request_bufs(void)
                cFYI(1,("cifs_min_small set to maximum (256)"));
        }
 
                cFYI(1,("cifs_min_small set to maximum (256)"));
        }
 
-       cifs_sm_req_poolp = mempool_create(cifs_min_small,
-                               mempool_alloc_slab,
-                               mempool_free_slab,
-                               cifs_sm_req_cachep);
+       cifs_sm_req_poolp = mempool_create_slab_pool(cifs_min_small,
+                                                    cifs_sm_req_cachep);
 
        if(cifs_sm_req_poolp == NULL) {
                mempool_destroy(cifs_req_poolp);
 
        if(cifs_sm_req_poolp == NULL) {
                mempool_destroy(cifs_req_poolp);
@@ -734,10 +797,8 @@ cifs_init_mids(void)
        if (cifs_mid_cachep == NULL)
                return -ENOMEM;
 
        if (cifs_mid_cachep == NULL)
                return -ENOMEM;
 
-       cifs_mid_poolp = mempool_create(3 /* a reasonable min simultan opers */,
-                                       mempool_alloc_slab,
-                                       mempool_free_slab,
-                                       cifs_mid_cachep);
+       /* 3 is a reasonable minimum number of simultaneous operations */
+       cifs_mid_poolp = mempool_create_slab_pool(3, cifs_mid_cachep);
        if(cifs_mid_poolp == NULL) {
                kmem_cache_destroy(cifs_mid_cachep);
                return -ENOMEM;
        if(cifs_mid_poolp == NULL) {
                kmem_cache_destroy(cifs_mid_cachep);
                return -ENOMEM;
@@ -776,14 +837,10 @@ static int cifs_oplock_thread(void * dummyarg)
        __u16  netfid;
        int rc;
 
        __u16  netfid;
        int rc;
 
-       daemonize("cifsoplockd");
-       allow_signal(SIGTERM);
-
-       oplockThread = current;
        do {
        do {
-               set_current_state(TASK_INTERRUPTIBLE);
+               if (try_to_freeze()) 
+                       continue;
                
                
-               schedule_timeout(1*HZ);  
                spin_lock(&GlobalMid_Lock);
                if(list_empty(&GlobalOplock_Q)) {
                        spin_unlock(&GlobalMid_Lock);
                spin_lock(&GlobalMid_Lock);
                if(list_empty(&GlobalOplock_Q)) {
                        spin_unlock(&GlobalMid_Lock);
@@ -801,9 +858,9 @@ static int cifs_oplock_thread(void * dummyarg)
                                DeleteOplockQEntry(oplock_item);
                                /* can not grab inode sem here since it would
                                deadlock when oplock received on delete 
                                DeleteOplockQEntry(oplock_item);
                                /* can not grab inode sem here since it would
                                deadlock when oplock received on delete 
-                               since vfs_unlink holds the i_sem across
+                               since vfs_unlink holds the i_mutex across
                                the call */
                                the call */
-                               /* down(&inode->i_sem);*/
+                               /* mutex_lock(&inode->i_mutex);*/
                                if (S_ISREG(inode->i_mode)) {
                                        rc = filemap_fdatawrite(inode->i_mapping);
                                        if(CIFS_I(inode)->clientCanCacheRead == 0) {
                                if (S_ISREG(inode->i_mode)) {
                                        rc = filemap_fdatawrite(inode->i_mapping);
                                        if(CIFS_I(inode)->clientCanCacheRead == 0) {
@@ -812,7 +869,7 @@ static int cifs_oplock_thread(void * dummyarg)
                                        }
                                } else
                                        rc = 0;
                                        }
                                } else
                                        rc = 0;
-                               /* up(&inode->i_sem);*/
+                               /* mutex_unlock(&inode->i_mutex);*/
                                if (rc)
                                        CIFS_I(inode)->write_behind_rc = rc;
                                cFYI(1,("Oplock flush inode %p rc %d",inode,rc));
                                if (rc)
                                        CIFS_I(inode)->write_behind_rc = rc;
                                cFYI(1,("Oplock flush inode %p rc %d",inode,rc));
@@ -832,10 +889,39 @@ static int cifs_oplock_thread(void * dummyarg)
                                }
                        } else
                                spin_unlock(&GlobalMid_Lock);
                                }
                        } else
                                spin_unlock(&GlobalMid_Lock);
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       schedule_timeout(1);  /* yield in case q were corrupt */
                }
                }
-       } while(!signal_pending(current));
-       complete_and_exit (&cifs_oplock_exited, 0);
-       oplockThread = NULL;
+       } while (!kthread_should_stop());
+
+       return 0;
+}
+
+static int cifs_dnotify_thread(void * dummyarg)
+{
+       struct list_head *tmp;
+       struct cifsSesInfo *ses;
+
+       do {
+               if (try_to_freeze())
+                       continue;
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(15*HZ);
+               read_lock(&GlobalSMBSeslock);
+               /* check if any stuck requests that need
+                  to be woken up and wakeq so the
+                  thread can wake up and error out */
+               list_for_each(tmp, &GlobalSMBSessionList) {
+                       ses = list_entry(tmp, struct cifsSesInfo, 
+                               cifsSessionList);
+                       if(ses && ses->server && 
+                            atomic_read(&ses->server->inFlight))
+                               wake_up_all(&ses->server->response_q);
+               }
+               read_unlock(&GlobalSMBSeslock);
+       } while (!kthread_should_stop());
+
+       return 0;
 }
 
 static int __init
 }
 
 static int __init
@@ -849,6 +935,10 @@ init_cifs(void)
        INIT_LIST_HEAD(&GlobalSMBSessionList);
        INIT_LIST_HEAD(&GlobalTreeConnectionList);
        INIT_LIST_HEAD(&GlobalOplock_Q);
        INIT_LIST_HEAD(&GlobalSMBSessionList);
        INIT_LIST_HEAD(&GlobalTreeConnectionList);
        INIT_LIST_HEAD(&GlobalOplock_Q);
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+       INIT_LIST_HEAD(&GlobalDnotifyReqList);
+       INIT_LIST_HEAD(&GlobalDnotifyRsp_Q);
+#endif 
 /*
  *  Initialize Global counters
  */
 /*
  *  Initialize Global counters
  */
@@ -859,6 +949,12 @@ init_cifs(void)
        atomic_set(&tconInfoReconnectCount, 0);
 
        atomic_set(&bufAllocCount, 0);
        atomic_set(&tconInfoReconnectCount, 0);
 
        atomic_set(&bufAllocCount, 0);
+       atomic_set(&smBufAllocCount, 0);
+#ifdef CONFIG_CIFS_STATS2
+       atomic_set(&totBufAllocCount, 0);
+       atomic_set(&totSmBufAllocCount, 0);
+#endif /* CONFIG_CIFS_STATS2 */
+
        atomic_set(&midCount, 0);
        GlobalCurrentXid = 0;
        GlobalTotalActiveXid = 0;
        atomic_set(&midCount, 0);
        GlobalCurrentXid = 0;
        GlobalTotalActiveXid = 0;
@@ -875,26 +971,48 @@ init_cifs(void)
        }
 
        rc = cifs_init_inodecache();
        }
 
        rc = cifs_init_inodecache();
-       if (!rc) {
-               rc = cifs_init_mids();
-               if (!rc) {
-                       rc = cifs_init_request_bufs();
-                       if (!rc) {
-                               rc = register_filesystem(&cifs_fs_type);
-                               if (!rc) {                
-                                       rc = (int)kernel_thread(cifs_oplock_thread, NULL, 
-                                               CLONE_FS | CLONE_FILES | CLONE_VM);
-                                       if(rc > 0)
-                                               return 0;
-                                       else 
-                                               cERROR(1,("error %d create oplock thread",rc));
-                               }
-                               cifs_destroy_request_bufs();
-                       }
-                       cifs_destroy_mids();
-               }
-               cifs_destroy_inodecache();
+       if (rc)
+               goto out_clean_proc;
+
+       rc = cifs_init_mids();
+       if (rc)
+               goto out_destroy_inodecache;
+
+       rc = cifs_init_request_bufs();
+       if (rc)
+               goto out_destroy_mids;
+
+       rc = register_filesystem(&cifs_fs_type);
+       if (rc)
+               goto out_destroy_request_bufs;
+
+       oplockThread = kthread_run(cifs_oplock_thread, NULL, "cifsoplockd");
+       if (IS_ERR(oplockThread)) {
+               rc = PTR_ERR(oplockThread);
+               cERROR(1,("error %d create oplock thread", rc));
+               goto out_unregister_filesystem;
+       }
+
+       dnotifyThread = kthread_run(cifs_dnotify_thread, NULL, "cifsdnotifyd");
+       if (IS_ERR(dnotifyThread)) {
+               rc = PTR_ERR(dnotifyThread);
+               cERROR(1,("error %d create dnotify thread", rc));
+               goto out_stop_oplock_thread;
        }
        }
+
+       return 0;
+
+ out_stop_oplock_thread:
+       kthread_stop(oplockThread);
+ out_unregister_filesystem:
+       unregister_filesystem(&cifs_fs_type);
+ out_destroy_request_bufs:
+       cifs_destroy_request_bufs();
+ out_destroy_mids:
+       cifs_destroy_mids();
+ out_destroy_inodecache:
+       cifs_destroy_inodecache();
+ out_clean_proc:
 #ifdef CONFIG_PROC_FS
        cifs_proc_clean();
 #endif
 #ifdef CONFIG_PROC_FS
        cifs_proc_clean();
 #endif
@@ -912,10 +1030,8 @@ exit_cifs(void)
        cifs_destroy_inodecache();
        cifs_destroy_mids();
        cifs_destroy_request_bufs();
        cifs_destroy_inodecache();
        cifs_destroy_mids();
        cifs_destroy_request_bufs();
-       if(oplockThread) {
-               send_sig(SIGTERM, oplockThread, 1);
-               wait_for_completion(&cifs_oplock_exited);
-       }
+       kthread_stop(oplockThread);
+       kthread_stop(dnotifyThread);
 }
 
 MODULE_AUTHOR("Steve French <sfrench@us.ibm.com>");
 }
 
 MODULE_AUTHOR("Steve French <sfrench@us.ibm.com>");