Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
Linus Torvalds [Tue, 18 Nov 2008 04:53:31 +0000 (20:53 -0800)]
* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6:
  prevent cifs_writepages() from skipping unwritten pages
  Fixed parsing of mount options when doing DFS submount
  [CIFS] Fix check for tcon seal setting and fix oops on failed mount from earlier patch
  [CIFS] Fix build break
  cifs: reinstate sharing of tree connections
  [CIFS] minor cleanup to cifs_mount
  cifs: reinstate sharing of SMB sessions sans races
  cifs: disable sharing session and tcon and add new TCP sharing code
  [CIFS] clean up server protocol handling
  [CIFS] remove unused list, add new cifs sock list to prepare for mount/umount fix
  [CIFS] Fix cifs reconnection flags
  [CIFS] Can't rely on iov length and base when kernel_recvmsg returns error

fs/cifs/cifs_debug.c
fs/cifs/cifs_dfs_ref.c
fs/cifs/cifs_spnego.c
fs/cifs/cifsfs.c
fs/cifs/cifsglob.h
fs/cifs/cifssmb.c
fs/cifs/connect.c
fs/cifs/file.c
fs/cifs/misc.c

index 69a12aa..490e34b 100644 (file)
@@ -107,12 +107,13 @@ void cifs_dump_mids(struct TCP_Server_Info *server)
 #ifdef CONFIG_PROC_FS
 static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
 {
-       struct list_head *tmp;
-       struct list_head *tmp1;
+       struct list_head *tmp1, *tmp2, *tmp3;
        struct mid_q_entry *mid_entry;
+       struct TCP_Server_Info *server;
        struct cifsSesInfo *ses;
        struct cifsTconInfo *tcon;
-       int i;
+       int i, j;
+       __u32 dev_type;
 
        seq_puts(m,
                    "Display Internal CIFS Data Structures for Debugging\n"
@@ -122,46 +123,78 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
        seq_printf(m, "Servers:");
 
        i = 0;
-       read_lock(&GlobalSMBSeslock);
-       list_for_each(tmp, &GlobalSMBSessionList) {
+       read_lock(&cifs_tcp_ses_lock);
+       list_for_each(tmp1, &cifs_tcp_ses_list) {
+               server = list_entry(tmp1, struct TCP_Server_Info,
+                                   tcp_ses_list);
                i++;
-               ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
-               if ((ses->serverDomain == NULL) || (ses->serverOS == NULL) ||
-                  (ses->serverNOS == NULL)) {
-                       seq_printf(m, "\nentry for %s not fully "
-                                       "displayed\n\t", ses->serverName);
-               } else {
-                       seq_printf(m,
-                                   "\n%d) Name: %s  Domain: %s Mounts: %d OS:"
-                                   " %s  \n\tNOS: %s\tCapability: 0x%x\n\tSMB"
+               list_for_each(tmp2, &server->smb_ses_list) {
+                       ses = list_entry(tmp2, struct cifsSesInfo,
+                                        smb_ses_list);
+                       if ((ses->serverDomain == NULL) ||
+                               (ses->serverOS == NULL) ||
+                               (ses->serverNOS == NULL)) {
+                               seq_printf(m, "\n%d) entry for %s not fully "
+                                          "displayed\n\t", i, ses->serverName);
+                       } else {
+                               seq_printf(m,
+                                   "\n%d) Name: %s  Domain: %s Uses: %d OS:"
+                                   " %s\n\tNOS: %s\tCapability: 0x%x\n\tSMB"
                                    " session status: %d\t",
                                i, ses->serverName, ses->serverDomain,
-                               atomic_read(&ses->inUse),
-                               ses->serverOS, ses->serverNOS,
+                               ses->ses_count, ses->serverOS, ses->serverNOS,
                                ses->capabilities, ses->status);
-               }
-               if (ses->server) {
+                       }
                        seq_printf(m, "TCP status: %d\n\tLocal Users To "
-                                   "Server: %d SecMode: 0x%x Req On Wire: %d",
-                               ses->server->tcpStatus,
-                               atomic_read(&ses->server->socketUseCount),
-                               ses->server->secMode,
-                               atomic_read(&ses->server->inFlight));
+                                  "Server: %d SecMode: 0x%x Req On Wire: %d",
+                                  server->tcpStatus, server->srv_count,
+                                  server->secMode,
+                                  atomic_read(&server->inFlight));
 
 #ifdef CONFIG_CIFS_STATS2
                        seq_printf(m, " In Send: %d In MaxReq Wait: %d",
-                               atomic_read(&ses->server->inSend),
-                               atomic_read(&ses->server->num_waiters));
+                               atomic_read(&server->inSend),
+                               atomic_read(&server->num_waiters));
 #endif
 
-                       seq_puts(m, "\nMIDs:\n");
+                       seq_puts(m, "\n\tShares:");
+                       j = 0;
+                       list_for_each(tmp3, &ses->tcon_list) {
+                               tcon = list_entry(tmp3, struct cifsTconInfo,
+                                                 tcon_list);
+                               ++j;
+                               dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
+                               seq_printf(m, "\n\t%d) %s Mounts: %d ", j,
+                                          tcon->treeName, tcon->tc_count);
+                               if (tcon->nativeFileSystem) {
+                                       seq_printf(m, "Type: %s ",
+                                                  tcon->nativeFileSystem);
+                               }
+                               seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x"
+                                       "\nPathComponentMax: %d Status: 0x%d",
+                                       le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),
+                                       le32_to_cpu(tcon->fsAttrInfo.Attributes),
+                                       le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
+                                       tcon->tidStatus);
+                               if (dev_type == FILE_DEVICE_DISK)
+                                       seq_puts(m, " type: DISK ");
+                               else if (dev_type == FILE_DEVICE_CD_ROM)
+                                       seq_puts(m, " type: CDROM ");
+                               else
+                                       seq_printf(m, " type: %d ", dev_type);
+
+                               if (tcon->need_reconnect)
+                                       seq_puts(m, "\tDISCONNECTED ");
+                               seq_putc(m, '\n');
+                       }
+
+                       seq_puts(m, "\n\tMIDs:\n");
 
                        spin_lock(&GlobalMid_Lock);
-                       list_for_each(tmp1, &ses->server->pending_mid_q) {
-                               mid_entry = list_entry(tmp1, struct
-                                       mid_q_entry,
+                       list_for_each(tmp3, &server->pending_mid_q) {
+                               mid_entry = list_entry(tmp3, struct mid_q_entry,
                                        qhead);
-                               seq_printf(m, "State: %d com: %d pid:"
+                               seq_printf(m, "\tState: %d com: %d pid:"
                                                " %d tsk: %p mid %d\n",
                                                mid_entry->midState,
                                                (int)mid_entry->command,
@@ -171,44 +204,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
                        }
                        spin_unlock(&GlobalMid_Lock);
                }
-
-       }
-       read_unlock(&GlobalSMBSeslock);
-       seq_putc(m, '\n');
-
-       seq_puts(m, "Shares:");
-
-       i = 0;
-       read_lock(&GlobalSMBSeslock);
-       list_for_each(tmp, &GlobalTreeConnectionList) {
-               __u32 dev_type;
-               i++;
-               tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
-               dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
-               seq_printf(m, "\n%d) %s Uses: %d ", i,
-                                tcon->treeName, atomic_read(&tcon->useCount));
-               if (tcon->nativeFileSystem) {
-                       seq_printf(m, "Type: %s ",
-                                        tcon->nativeFileSystem);
-               }
-               seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x"
-                                "\nPathComponentMax: %d Status: %d",
-                           le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),
-                           le32_to_cpu(tcon->fsAttrInfo.Attributes),
-                           le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
-                           tcon->tidStatus);
-               if (dev_type == FILE_DEVICE_DISK)
-                       seq_puts(m, " type: DISK ");
-               else if (dev_type == FILE_DEVICE_CD_ROM)
-                       seq_puts(m, " type: CDROM ");
-               else
-                       seq_printf(m, " type: %d ", dev_type);
-
-               if (tcon->tidStatus == CifsNeedReconnect)
-                       seq_puts(m, "\tDISCONNECTED ");
        }
-       read_unlock(&GlobalSMBSeslock);
-
+       read_unlock(&cifs_tcp_ses_lock);
        seq_putc(m, '\n');
 
        /* BB add code to dump additional info such as TCP session info now */
@@ -234,7 +231,9 @@ static ssize_t cifs_stats_proc_write(struct file *file,
 {
        char c;
        int rc;
-       struct list_head *tmp;
+       struct list_head *tmp1, *tmp2, *tmp3;
+       struct TCP_Server_Info *server;
+       struct cifsSesInfo *ses;
        struct cifsTconInfo *tcon;
 
        rc = get_user(c, buffer);
@@ -242,33 +241,42 @@ static ssize_t cifs_stats_proc_write(struct file *file,
                return rc;
 
        if (c == '1' || c == 'y' || c == 'Y' || c == '0') {
-               read_lock(&GlobalSMBSeslock);
 #ifdef CONFIG_CIFS_STATS2
                atomic_set(&totBufAllocCount, 0);
                atomic_set(&totSmBufAllocCount, 0);
 #endif /* CONFIG_CIFS_STATS2 */
-               list_for_each(tmp, &GlobalTreeConnectionList) {
-                       tcon = list_entry(tmp, struct cifsTconInfo,
-                                       cifsConnectionList);
-                       atomic_set(&tcon->num_smbs_sent, 0);
-                       atomic_set(&tcon->num_writes, 0);
-                       atomic_set(&tcon->num_reads, 0);
-                       atomic_set(&tcon->num_oplock_brks, 0);
-                       atomic_set(&tcon->num_opens, 0);
-                       atomic_set(&tcon->num_closes, 0);
-                       atomic_set(&tcon->num_deletes, 0);
-                       atomic_set(&tcon->num_mkdirs, 0);
-                       atomic_set(&tcon->num_rmdirs, 0);
-                       atomic_set(&tcon->num_renames, 0);
-                       atomic_set(&tcon->num_t2renames, 0);
-                       atomic_set(&tcon->num_ffirst, 0);
-                       atomic_set(&tcon->num_fnext, 0);
-                       atomic_set(&tcon->num_fclose, 0);
-                       atomic_set(&tcon->num_hardlinks, 0);
-                       atomic_set(&tcon->num_symlinks, 0);
-                       atomic_set(&tcon->num_locks, 0);
+               read_lock(&cifs_tcp_ses_lock);
+               list_for_each(tmp1, &cifs_tcp_ses_list) {
+                       server = list_entry(tmp1, struct TCP_Server_Info,
+                                           tcp_ses_list);
+                       list_for_each(tmp2, &server->smb_ses_list) {
+                               ses = list_entry(tmp2, struct cifsSesInfo,
+                                                smb_ses_list);
+                               list_for_each(tmp3, &ses->tcon_list) {
+                                       tcon = list_entry(tmp3,
+                                                         struct cifsTconInfo,
+                                                         tcon_list);
+                                       atomic_set(&tcon->num_smbs_sent, 0);
+                                       atomic_set(&tcon->num_writes, 0);
+                                       atomic_set(&tcon->num_reads, 0);
+                                       atomic_set(&tcon->num_oplock_brks, 0);
+                                       atomic_set(&tcon->num_opens, 0);
+                                       atomic_set(&tcon->num_closes, 0);
+                                       atomic_set(&tcon->num_deletes, 0);
+                                       atomic_set(&tcon->num_mkdirs, 0);
+                                       atomic_set(&tcon->num_rmdirs, 0);
+                                       atomic_set(&tcon->num_renames, 0);
+                                       atomic_set(&tcon->num_t2renames, 0);
+                                       atomic_set(&tcon->num_ffirst, 0);
+                                       atomic_set(&tcon->num_fnext, 0);
+                                       atomic_set(&tcon->num_fclose, 0);
+                                       atomic_set(&tcon->num_hardlinks, 0);
+                                       atomic_set(&tcon->num_symlinks, 0);
+                                       atomic_set(&tcon->num_locks, 0);
+                               }
+                       }
                }
-               read_unlock(&GlobalSMBSeslock);
+               read_unlock(&cifs_tcp_ses_lock);
        }
 
        return count;
@@ -277,7 +285,9 @@ static ssize_t cifs_stats_proc_write(struct file *file,
 static int cifs_stats_proc_show(struct seq_file *m, void *v)
 {
        int i;
-       struct list_head *tmp;
+       struct list_head *tmp1, *tmp2, *tmp3;
+       struct TCP_Server_Info *server;
+       struct cifsSesInfo *ses;
        struct cifsTconInfo *tcon;
 
        seq_printf(m,
@@ -306,44 +316,55 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
                GlobalCurrentXid, GlobalMaxActiveXid);
 
        i = 0;
-       read_lock(&GlobalSMBSeslock);
-       list_for_each(tmp, &GlobalTreeConnectionList) {
-               i++;
-               tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
-               seq_printf(m, "\n%d) %s", i, tcon->treeName);
-               if (tcon->tidStatus == CifsNeedReconnect)
-                       seq_puts(m, "\tDISCONNECTED ");
-               seq_printf(m, "\nSMBs: %d Oplock Breaks: %d",
-                       atomic_read(&tcon->num_smbs_sent),
-                       atomic_read(&tcon->num_oplock_brks));
-               seq_printf(m, "\nReads:  %d Bytes: %lld",
-                       atomic_read(&tcon->num_reads),
-                       (long long)(tcon->bytes_read));
-               seq_printf(m, "\nWrites: %d Bytes: %lld",
-                       atomic_read(&tcon->num_writes),
-                       (long long)(tcon->bytes_written));
-               seq_printf(m,
-                       "\nLocks: %d HardLinks: %d Symlinks: %d",
-                       atomic_read(&tcon->num_locks),
-                       atomic_read(&tcon->num_hardlinks),
-                       atomic_read(&tcon->num_symlinks));
-
-               seq_printf(m, "\nOpens: %d Closes: %d Deletes: %d",
-                       atomic_read(&tcon->num_opens),
-                       atomic_read(&tcon->num_closes),
-                       atomic_read(&tcon->num_deletes));
-               seq_printf(m, "\nMkdirs: %d Rmdirs: %d",
-                       atomic_read(&tcon->num_mkdirs),
-                       atomic_read(&tcon->num_rmdirs));
-               seq_printf(m, "\nRenames: %d T2 Renames %d",
-                       atomic_read(&tcon->num_renames),
-                       atomic_read(&tcon->num_t2renames));
-               seq_printf(m, "\nFindFirst: %d FNext %d FClose %d",
-                       atomic_read(&tcon->num_ffirst),
-                       atomic_read(&tcon->num_fnext),
-                       atomic_read(&tcon->num_fclose));
+       read_lock(&cifs_tcp_ses_lock);
+       list_for_each(tmp1, &cifs_tcp_ses_list) {
+               server = list_entry(tmp1, struct TCP_Server_Info,
+                                   tcp_ses_list);
+               list_for_each(tmp2, &server->smb_ses_list) {
+                       ses = list_entry(tmp2, struct cifsSesInfo,
+                                        smb_ses_list);
+                       list_for_each(tmp3, &ses->tcon_list) {
+                               tcon = list_entry(tmp3,
+                                                 struct cifsTconInfo,
+                                                 tcon_list);
+                               i++;
+                               seq_printf(m, "\n%d) %s", i, tcon->treeName);
+                               if (tcon->need_reconnect)
+                                       seq_puts(m, "\tDISCONNECTED ");
+                               seq_printf(m, "\nSMBs: %d Oplock Breaks: %d",
+                                       atomic_read(&tcon->num_smbs_sent),
+                                       atomic_read(&tcon->num_oplock_brks));
+                               seq_printf(m, "\nReads:  %d Bytes: %lld",
+                                       atomic_read(&tcon->num_reads),
+                                       (long long)(tcon->bytes_read));
+                               seq_printf(m, "\nWrites: %d Bytes: %lld",
+                                       atomic_read(&tcon->num_writes),
+                                       (long long)(tcon->bytes_written));
+                               seq_printf(m, "\nLocks: %d HardLinks: %d "
+                                             "Symlinks: %d",
+                                       atomic_read(&tcon->num_locks),
+                                       atomic_read(&tcon->num_hardlinks),
+                                       atomic_read(&tcon->num_symlinks));
+                               seq_printf(m, "\nOpens: %d Closes: %d"
+                                             "Deletes: %d",
+                                       atomic_read(&tcon->num_opens),
+                                       atomic_read(&tcon->num_closes),
+                                       atomic_read(&tcon->num_deletes));
+                               seq_printf(m, "\nMkdirs: %d Rmdirs: %d",
+                                       atomic_read(&tcon->num_mkdirs),
+                                       atomic_read(&tcon->num_rmdirs));
+                               seq_printf(m, "\nRenames: %d T2 Renames %d",
+                                       atomic_read(&tcon->num_renames),
+                                       atomic_read(&tcon->num_t2renames));
+                               seq_printf(m, "\nFindFirst: %d FNext %d "
+                                             "FClose %d",
+                                       atomic_read(&tcon->num_ffirst),
+                                       atomic_read(&tcon->num_fnext),
+                                       atomic_read(&tcon->num_fclose));
+                       }
+               }
        }
-       read_unlock(&GlobalSMBSeslock);
+       read_unlock(&cifs_tcp_ses_lock);
 
        seq_putc(m, '\n');
        return 0;
index d2c8eef..e1c1836 100644 (file)
@@ -106,7 +106,8 @@ static char *cifs_get_share_name(const char *node_name)
 /**
  * compose_mount_options       -       creates mount options for refferral
  * @sb_mountdata:      parent/root DFS mount options (template)
- * @ref_unc:           refferral server UNC
+ * @dentry:            point where we are going to mount
+ * @ref:               server's referral
  * @devname:           pointer for saving device name
  *
  * creates mount options for submount based on template options sb_mountdata
@@ -116,7 +117,8 @@ static char *cifs_get_share_name(const char *node_name)
  * Caller is responcible for freeing retunrned value if it is not error.
  */
 static char *compose_mount_options(const char *sb_mountdata,
-                                  const char *ref_unc,
+                                  struct dentry *dentry,
+                                  const struct dfs_info3_param *ref,
                                   char **devname)
 {
        int rc;
@@ -126,11 +128,12 @@ static char *compose_mount_options(const char *sb_mountdata,
        char *srvIP = NULL;
        char sep = ',';
        int off, noff;
+       char *fullpath;
 
        if (sb_mountdata == NULL)
                return ERR_PTR(-EINVAL);
 
-       *devname = cifs_get_share_name(ref_unc);
+       *devname = cifs_get_share_name(ref->node_name);
        rc = dns_resolve_server_name_to_ip(*devname, &srvIP);
        if (rc != 0) {
                cERROR(1, ("%s: Failed to resolve server part of %s to IP",
@@ -138,7 +141,12 @@ static char *compose_mount_options(const char *sb_mountdata,
                mountdata = ERR_PTR(rc);
                goto compose_mount_options_out;
        }
-       md_len = strlen(sb_mountdata) + strlen(srvIP) + strlen(ref_unc) + 3;
+       /* md_len = strlen(...) + 12 for 'sep+prefixpath='
+        * assuming that we have 'unc=' and 'ip=' in
+        * the original sb_mountdata
+        */
+       md_len = strlen(sb_mountdata) + strlen(srvIP) +
+               strlen(ref->node_name) + 12;
        mountdata = kzalloc(md_len+1, GFP_KERNEL);
        if (mountdata == NULL) {
                mountdata = ERR_PTR(-ENOMEM);
@@ -152,41 +160,56 @@ static char *compose_mount_options(const char *sb_mountdata,
                        strncpy(mountdata, sb_mountdata, 5);
                        off += 5;
        }
-       while ((tkn_e = strchr(sb_mountdata+off, sep))) {
-               noff = (tkn_e - (sb_mountdata+off)) + 1;
-               if (strnicmp(sb_mountdata+off, "unc=", 4) == 0) {
+
+       do {
+               tkn_e = strchr(sb_mountdata + off, sep);
+               if (tkn_e == NULL)
+                       noff = strlen(sb_mountdata + off);
+               else
+                       noff = tkn_e - (sb_mountdata + off) + 1;
+
+               if (strnicmp(sb_mountdata + off, "unc=", 4) == 0) {
                        off += noff;
                        continue;
                }
-               if (strnicmp(sb_mountdata+off, "ip=", 3) == 0) {
+               if (strnicmp(sb_mountdata + off, "ip=", 3) == 0) {
                        off += noff;
                        continue;
                }
-               if (strnicmp(sb_mountdata+off, "prefixpath=", 3) == 0) {
+               if (strnicmp(sb_mountdata + off, "prefixpath=", 11) == 0) {
                        off += noff;
                        continue;
                }
-               strncat(mountdata, sb_mountdata+off, noff);
+               strncat(mountdata, sb_mountdata + off, noff);
                off += noff;
-       }
-       strcat(mountdata, sb_mountdata+off);
+       } while (tkn_e);
+       strcat(mountdata, sb_mountdata + off);
        mountdata[md_len] = '\0';
 
        /* copy new IP and ref share name */
-       strcat(mountdata, ",ip=");
+       if (mountdata[strlen(mountdata) - 1] != sep)
+               strncat(mountdata, &sep, 1);
+       strcat(mountdata, "ip=");
        strcat(mountdata, srvIP);
-       strcat(mountdata, ",unc=");
+       strncat(mountdata, &sep, 1);
+       strcat(mountdata, "unc=");
        strcat(mountdata, *devname);
 
        /* find & copy prefixpath */
-       tkn_e = strchr(ref_unc+2, '\\');
-       if (tkn_e) {
-               tkn_e = strchr(tkn_e+1, '\\');
-               if (tkn_e) {
-                       strcat(mountdata, ",prefixpath=");
-                       strcat(mountdata, tkn_e+1);
-               }
+       tkn_e = strchr(ref->node_name + 2, '\\');
+       if (tkn_e == NULL) /* invalid unc, missing share name*/
+               goto compose_mount_options_out;
+
+       fullpath = build_path_from_dentry(dentry);
+       tkn_e = strchr(tkn_e + 1, '\\');
+       if (tkn_e || strlen(fullpath) - (ref->path_consumed)) {
+               strncat(mountdata, &sep, 1);
+               strcat(mountdata, "prefixpath=");
+               if (tkn_e)
+                       strcat(mountdata, tkn_e + 1);
+               strcat(mountdata, fullpath + (ref->path_consumed));
        }
+       kfree(fullpath);
 
        /*cFYI(1,("%s: parent mountdata: %s", __func__,sb_mountdata));*/
        /*cFYI(1, ("%s: submount mountdata: %s", __func__, mountdata ));*/
@@ -198,7 +221,7 @@ compose_mount_options_out:
 
 
 static struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent,
-               struct dentry *dentry, char *ref_unc)
+               struct dentry *dentry, const struct dfs_info3_param *ref)
 {
        struct cifs_sb_info *cifs_sb;
        struct vfsmount *mnt;
@@ -207,7 +230,7 @@ static struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent,
 
        cifs_sb = CIFS_SB(dentry->d_inode->i_sb);
        mountdata = compose_mount_options(cifs_sb->mountdata,
-                                               ref_unc, &devname);
+                                               dentry, ref, &devname);
 
        if (IS_ERR(mountdata))
                return (struct vfsmount *)mountdata;
@@ -310,7 +333,7 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
                        }
                        mnt = cifs_dfs_do_refmount(nd->path.mnt,
                                                nd->path.dentry,
-                                               referrals[i].node_name);
+                                               referrals + i);
                        cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p",
                                         __func__,
                                        referrals[i].node_name, mnt));
index fcee929..0ab2fb5 100644 (file)
@@ -73,8 +73,8 @@ struct key_type cifs_spnego_key_type = {
  * strlen(";sec=ntlmsspi") */
 #define MAX_MECH_STR_LEN       13
 
-/* max possible addr len eg FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/60 */
-#define MAX_IPV6_ADDR_LEN      42
+/* max possible addr len eg FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/128 */
+#define MAX_IPV6_ADDR_LEN      43
 
 /* strlen of "host=" */
 #define HOST_KEY_LEN           5
index ac5915d..d9cf467 100644 (file)
@@ -514,10 +514,11 @@ static void cifs_umount_begin(struct super_block *sb)
        tcon = cifs_sb->tcon;
        if (tcon == NULL)
                return;
-       down(&tcon->tconSem);
-       if (atomic_read(&tcon->useCount) == 1)
+
+       read_lock(&cifs_tcp_ses_lock);
+       if (tcon->tc_count == 1)
                tcon->tidStatus = CifsExiting;
-       up(&tcon->tconSem);
+       read_unlock(&cifs_tcp_ses_lock);
 
        /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */
        /* cancel_notify_requests(tcon); */
@@ -1013,7 +1014,7 @@ static int cifs_oplock_thread(void *dummyarg)
                                not bother sending an oplock release if session
                                to server still is disconnected since oplock
                                already released by the server in that case */
-                       if (pTcon->tidStatus != CifsNeedReconnect) {
+                       if (!pTcon->need_reconnect) {
                                rc = CIFSSMBLock(0, pTcon, netfid,
                                                0 /* len */ , 0 /* offset */, 0,
                                                0, LOCKING_ANDX_OPLOCK_RELEASE,
@@ -1031,24 +1032,24 @@ static int cifs_oplock_thread(void *dummyarg)
 static int cifs_dnotify_thread(void *dummyarg)
 {
        struct list_head *tmp;
-       struct cifsSesInfo *ses;
+       struct TCP_Server_Info *server;
 
        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->server && atomic_read(&ses->server->inFlight))
-                               wake_up_all(&ses->server->response_q);
+               read_lock(&cifs_tcp_ses_lock);
+               list_for_each(tmp, &cifs_tcp_ses_list) {
+                       server = list_entry(tmp, struct TCP_Server_Info,
+                                        tcp_ses_list);
+                       if (atomic_read(&server->inFlight))
+                               wake_up_all(&server->response_q);
                }
-               read_unlock(&GlobalSMBSeslock);
+               read_unlock(&cifs_tcp_ses_lock);
        } while (!kthread_should_stop());
 
        return 0;
@@ -1059,9 +1060,7 @@ init_cifs(void)
 {
        int rc = 0;
        cifs_proc_init();
-/*     INIT_LIST_HEAD(&GlobalServerList);*/    /* BB not implemented yet */
-       INIT_LIST_HEAD(&GlobalSMBSessionList);
-       INIT_LIST_HEAD(&GlobalTreeConnectionList);
+       INIT_LIST_HEAD(&cifs_tcp_ses_list);
        INIT_LIST_HEAD(&GlobalOplock_Q);
 #ifdef CONFIG_CIFS_EXPERIMENTAL
        INIT_LIST_HEAD(&GlobalDnotifyReqList);
@@ -1089,6 +1088,7 @@ init_cifs(void)
        GlobalMaxActiveXid = 0;
        memset(Local_System_Name, 0, 15);
        rwlock_init(&GlobalSMBSeslock);
+       rwlock_init(&cifs_tcp_ses_lock);
        spin_lock_init(&GlobalMid_Lock);
 
        if (cifs_max_pending < 2) {
index 1cb1189..f1ae1f5 100644 (file)
@@ -85,8 +85,7 @@ enum securityEnum {
 };
 
 enum protocolEnum {
-       IPV4 = 0,
-       IPV6,
+       TCP = 0,
        SCTP
        /* Netbios frames protocol not supported at this time */
 };
@@ -122,6 +121,9 @@ struct cifs_cred {
  */
 
 struct TCP_Server_Info {
+       struct list_head tcp_ses_list;
+       struct list_head smb_ses_list;
+       int srv_count; /* reference counter */
        /* 15 character server name + 0x20 16th byte indicating type = srv */
        char server_RFC1001_name[SERVER_NAME_LEN_WITH_NULL];
        char unicode_server_Name[SERVER_NAME_LEN_WITH_NULL * 2];
@@ -143,7 +145,6 @@ struct TCP_Server_Info {
        bool svlocal:1;                 /* local server or remote */
        bool noblocksnd;                /* use blocking sendmsg */
        bool noautotune;                /* do not autotune send buf sizes */
-       atomic_t socketUseCount; /* number of open cifs sessions on socket */
        atomic_t inFlight;  /* number of requests on the wire to server */
 #ifdef CONFIG_CIFS_STATS2
        atomic_t inSend; /* requests trying to send */
@@ -194,13 +195,14 @@ struct cifsUidInfo {
  * Session structure.  One of these for each uid session with a particular host
  */
 struct cifsSesInfo {
-       struct list_head cifsSessionList;
+       struct list_head smb_ses_list;
+       struct list_head tcon_list;
        struct semaphore sesSem;
 #if 0
        struct cifsUidInfo *uidInfo;    /* pointer to user info */
 #endif
        struct TCP_Server_Info *server; /* pointer to server info */
-       atomic_t inUse; /* # of mounts (tree connections) on this ses */
+       int ses_count;          /* reference counter */
        enum statusEnum status;
        unsigned overrideSecFlg;  /* if non-zero override global sec flags */
        __u16 ipc_tid;          /* special tid for connection to IPC share */
@@ -216,6 +218,7 @@ struct cifsSesInfo {
        char userName[MAX_USERNAME_SIZE + 1];
        char *domainName;
        char *password;
+       bool need_reconnect:1; /* connection reset, uid now invalid */
 };
 /* no more than one of the following three session flags may be set */
 #define CIFS_SES_NT4 1
@@ -230,16 +233,15 @@ struct cifsSesInfo {
  * session
  */
 struct cifsTconInfo {
-       struct list_head cifsConnectionList;
+       struct list_head tcon_list;
+       int tc_count;
        struct list_head openFileList;
-       struct semaphore tconSem;
        struct cifsSesInfo *ses;        /* pointer to session associated with */
        char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */
        char *nativeFileSystem;
        __u16 tid;              /* The 2 byte tree id */
        __u16 Flags;            /* optional support bits */
        enum statusEnum tidStatus;
-       atomic_t useCount;      /* how many explicit/implicit mounts to share */
 #ifdef CONFIG_CIFS_STATS
        atomic_t num_smbs_sent;
        atomic_t num_writes;
@@ -288,6 +290,7 @@ struct cifsTconInfo {
        bool unix_ext:1;  /* if false disable Linux extensions to CIFS protocol
                                for this mount even if server would support */
        bool local_lease:1; /* check leases (only) on local system not remote */
+       bool need_reconnect:1; /* connection reset, tid now invalid */
        /* BB add field for back pointer to sb struct(s)? */
 };
 
@@ -588,21 +591,21 @@ require use of the stronger protocol */
 #endif
 
 /*
- * The list of servers that did not respond with NT LM 0.12.
- * This list helps improve performance and eliminate the messages indicating
- * that we had a communications error talking to the server in this list.
+ * the list of TCP_Server_Info structures, ie each of the sockets
+ * connecting our client to a distinct server (ip address), is
+ * chained together by cifs_tcp_ses_list. The list of all our SMB
+ * sessions (and from that the tree connections) can be found
+ * by iterating over cifs_tcp_ses_list
  */
-/* Feature not supported */
-/* GLOBAL_EXTERN struct servers_not_supported *NotSuppList; */
+GLOBAL_EXTERN struct list_head         cifs_tcp_ses_list;
 
 /*
- * The following is a hash table of all the users we know about.
+ * This lock protects the cifs_tcp_ses_list, the list of smb sessions per
+ * tcp session, and the list of tcon's per smb session. It also protects
+ * the reference counters for the server, smb session, and tcon. Finally,
+ * changes to the tcon->tidStatus should be done while holding this lock.
  */
-GLOBAL_EXTERN struct smbUidInfo *GlobalUidList[UID_HASH];
-
-/* GLOBAL_EXTERN struct list_head GlobalServerList; BB not implemented yet */
-GLOBAL_EXTERN struct list_head GlobalSMBSessionList;
-GLOBAL_EXTERN struct list_head GlobalTreeConnectionList;
+GLOBAL_EXTERN rwlock_t         cifs_tcp_ses_lock;
 GLOBAL_EXTERN rwlock_t GlobalSMBSeslock;  /* protects list inserts on 3 above */
 
 GLOBAL_EXTERN struct list_head GlobalOplock_Q;
index d5eac48..bdda46d 100644 (file)
@@ -190,10 +190,10 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
                /* need to prevent multiple threads trying to
                simultaneously reconnect the same SMB session */
                        down(&tcon->ses->sesSem);
-                       if (tcon->ses->status == CifsNeedReconnect)
+                       if (tcon->ses->need_reconnect)
                                rc = cifs_setup_session(0, tcon->ses,
                                                        nls_codepage);
-                       if (!rc && (tcon->tidStatus == CifsNeedReconnect)) {
+                       if (!rc && (tcon->need_reconnect)) {
                                mark_open_files_invalid(tcon);
                                rc = CIFSTCon(0, tcon->ses, tcon->treeName,
                                              tcon, nls_codepage);
@@ -295,7 +295,7 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
           check for tcp and smb session status done differently
           for those three - in the calling routine */
        if (tcon) {
-               if (tcon->tidStatus == CifsExiting) {
+               if (tcon->need_reconnect) {
                        /* only tree disconnect, open, and write,
                          (and ulogoff which does not have tcon)
                          are allowed as we start force umount */
@@ -337,10 +337,10 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
                /* need to prevent multiple threads trying to
                simultaneously reconnect the same SMB session */
                        down(&tcon->ses->sesSem);
-                       if (tcon->ses->status == CifsNeedReconnect)
+                       if (tcon->ses->need_reconnect)
                                rc = cifs_setup_session(0, tcon->ses,
                                                        nls_codepage);
-                       if (!rc && (tcon->tidStatus == CifsNeedReconnect)) {
+                       if (!rc && (tcon->need_reconnect)) {
                                mark_open_files_invalid(tcon);
                                rc = CIFSTCon(0, tcon->ses, tcon->treeName,
                                              tcon, nls_codepage);
@@ -664,8 +664,9 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
                        rc = -EIO;
                        goto neg_err_exit;
                }
-
-               if (server->socketUseCount.counter > 1) {
+               read_lock(&cifs_tcp_ses_lock);
+               if (server->srv_count > 1) {
+                       read_unlock(&cifs_tcp_ses_lock);
                        if (memcmp(server->server_GUID,
                                   pSMBr->u.extended_response.
                                   GUID, 16) != 0) {
@@ -674,9 +675,11 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
                                        pSMBr->u.extended_response.GUID,
                                        16);
                        }
-               } else
+               } else {
+                       read_unlock(&cifs_tcp_ses_lock);
                        memcpy(server->server_GUID,
                               pSMBr->u.extended_response.GUID, 16);
+               }
 
                if (count == 16) {
                        server->secType = RawNTLMSSP;
@@ -739,50 +742,31 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
        int rc = 0;
 
        cFYI(1, ("In tree disconnect"));
-       /*
-        *  If last user of the connection and
-        *  connection alive - disconnect it
-        *  If this is the last connection on the server session disconnect it
-        *  (and inside session disconnect we should check if tcp socket needs
-        *  to be freed and kernel thread woken up).
-        */
-       if (tcon)
-               down(&tcon->tconSem);
-       else
-               return -EIO;
 
-       atomic_dec(&tcon->useCount);
-       if (atomic_read(&tcon->useCount) > 0) {
-               up(&tcon->tconSem);
-               return -EBUSY;
-       }
+       /* BB: do we need to check this? These should never be NULL. */
+       if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
+               return -EIO;
 
-       /* No need to return error on this operation if tid invalidated and
-       closed on server already e.g. due to tcp session crashing */
-       if (tcon->tidStatus == CifsNeedReconnect) {
-               up(&tcon->tconSem);
+       /*
+        * No need to return error on this operation if tid invalidated and
+        * closed on server already e.g. due to tcp session crashing. Also,
+        * the tcon is no longer on the list, so no need to take lock before
+        * checking this.
+        */
+       if (tcon->need_reconnect)
                return 0;
-       }
 
-       if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) {
-               up(&tcon->tconSem);
-               return -EIO;
-       }
        rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
                            (void **)&smb_buffer);
-       if (rc) {
-               up(&tcon->tconSem);
+       if (rc)
                return rc;
-       }
 
        rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
        if (rc)
                cFYI(1, ("Tree disconnect failed %d", rc));
 
-       up(&tcon->tconSem);
-
        /* No need to return error on this operation if tid invalidated and
-       closed on server already e.g. due to tcp session crashing */
+          closed on server already e.g. due to tcp session crashing */
        if (rc == -EAGAIN)
                rc = 0;
 
@@ -796,43 +780,36 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
        int rc = 0;
 
        cFYI(1, ("In SMBLogoff for session disconnect"));
-       if (ses)
-               down(&ses->sesSem);
-       else
+
+       /*
+        * BB: do we need to check validity of ses and server? They should
+        * always be valid since we have an active reference. If not, that
+        * should probably be a BUG()
+        */
+       if (!ses || !ses->server)
                return -EIO;
 
-       atomic_dec(&ses->inUse);
-       if (atomic_read(&ses->inUse) > 0) {
-               up(&ses->sesSem);
-               return -EBUSY;
-       }
+       down(&ses->sesSem);
+       if (ses->need_reconnect)
+               goto session_already_dead; /* no need to send SMBlogoff if uid
+                                             already closed due to reconnect */
        rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
        if (rc) {
                up(&ses->sesSem);
                return rc;
        }
 
-       if (ses->server) {
-               pSMB->hdr.Mid = GetNextMid(ses->server);
+       pSMB->hdr.Mid = GetNextMid(ses->server);
 
-               if (ses->server->secMode &
+       if (ses->server->secMode &
                   (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
                        pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
-       }
 
        pSMB->hdr.Uid = ses->Suid;
 
        pSMB->AndXCommand = 0xFF;
        rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
-       if (ses->server) {
-               atomic_dec(&ses->server->socketUseCount);
-               if (atomic_read(&ses->server->socketUseCount) == 0) {
-                       spin_lock(&GlobalMid_Lock);
-                       ses->server->tcpStatus = CifsExiting;
-                       spin_unlock(&GlobalMid_Lock);
-                       rc = -ESHUTDOWN;
-               }
-       }
+session_already_dead:
        up(&ses->sesSem);
 
        /* if session dead then we do not need to do ulogoff,
@@ -3922,6 +3899,27 @@ GetInodeNumOut:
        return rc;
 }
 
+/* computes length of UCS string converted to host codepage
+ * @src:       UCS string
+ * @maxlen:    length of the input string in UCS characters
+ *             (not in bytes)
+ *
+ * return:     size of input string in host codepage
+ */
+static int hostlen_fromUCS(const __le16 *src, const int maxlen,
+               const struct nls_table *nls_codepage) {
+       int i;
+       int hostlen = 0;
+       char to[4];
+       int charlen;
+       for (i = 0; (i < maxlen) && src[i]; ++i) {
+               charlen = nls_codepage->uni2char(le16_to_cpu(src[i]),
+                               to, NLS_MAX_CHARSET_SIZE);
+               hostlen += charlen > 0 ? charlen : 1;
+       }
+       return hostlen;
+}
+
 /* parses DFS refferal V3 structure
  * caller is responsible for freeing target_nodes
  * returns:
@@ -3932,7 +3930,8 @@ static int
 parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
                unsigned int *num_of_nodes,
                struct dfs_info3_param **target_nodes,
-               const struct nls_table *nls_codepage)
+               const struct nls_table *nls_codepage, int remap,
+               const char *searchName)
 {
        int i, rc = 0;
        char *data_end;
@@ -3983,7 +3982,17 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
                struct dfs_info3_param *node = (*target_nodes)+i;
 
                node->flags = le16_to_cpu(pSMBr->DFSFlags);
-               node->path_consumed = le16_to_cpu(pSMBr->PathConsumed);
+               if (is_unicode) {
+                       __le16 *tmp = kmalloc(strlen(searchName)*2, GFP_KERNEL);
+                       cifsConvertToUCS((__le16 *) tmp, searchName,
+                                       PATH_MAX, nls_codepage, remap);
+                       node->path_consumed = hostlen_fromUCS(tmp,
+                                       le16_to_cpu(pSMBr->PathConsumed)/2,
+                                       nls_codepage);
+                       kfree(tmp);
+               } else
+                       node->path_consumed = le16_to_cpu(pSMBr->PathConsumed);
+
                node->server_type = le16_to_cpu(ref->ServerType);
                node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
 
@@ -4116,7 +4125,8 @@ getDFSRetry:
 
        /* parse returned result into more usable form */
        rc = parse_DFS_referrals(pSMBr, num_of_nodes,
-                                target_nodes, nls_codepage);
+                                target_nodes, nls_codepage, remap,
+                                searchName);
 
 GetDFSRefExit:
        cifs_buf_release(pSMB);
index e9f9248..c7d3417 100644 (file)
@@ -124,7 +124,7 @@ static int
 cifs_reconnect(struct TCP_Server_Info *server)
 {
        int rc = 0;
-       struct list_head *tmp;
+       struct list_head *tmp, *tmp2;
        struct cifsSesInfo *ses;
        struct cifsTconInfo *tcon;
        struct mid_q_entry *mid_entry;
@@ -144,23 +144,17 @@ cifs_reconnect(struct TCP_Server_Info *server)
 
        /* before reconnecting the tcp session, mark the smb session (uid)
                and the tid bad so they are not used until reconnected */
-       read_lock(&GlobalSMBSeslock);
-       list_for_each(tmp, &GlobalSMBSessionList) {
-               ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
-               if (ses->server) {
-                       if (ses->server == server) {
-                               ses->status = CifsNeedReconnect;
-                               ses->ipc_tid = 0;
-                       }
+       read_lock(&cifs_tcp_ses_lock);
+       list_for_each(tmp, &server->smb_ses_list) {
+               ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
+               ses->need_reconnect = true;
+               ses->ipc_tid = 0;
+               list_for_each(tmp2, &ses->tcon_list) {
+                       tcon = list_entry(tmp2, struct cifsTconInfo, tcon_list);
+                       tcon->need_reconnect = true;
                }
-               /* else tcp and smb sessions need reconnection */
-       }
-       list_for_each(tmp, &GlobalTreeConnectionList) {
-               tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
-               if ((tcon->ses) && (tcon->ses->server == server))
-                       tcon->tidStatus = CifsNeedReconnect;
        }
-       read_unlock(&GlobalSMBSeslock);
+       read_unlock(&cifs_tcp_ses_lock);
        /* do not want to be sending data on a socket we are freeing */
        down(&server->tcpSem);
        if (server->ssocket) {
@@ -193,7 +187,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
        while ((server->tcpStatus != CifsExiting) &&
               (server->tcpStatus != CifsGood)) {
                try_to_freeze();
-               if (server->protocolType == IPV6) {
+               if (server->addr.sockAddr6.sin6_family == AF_INET6) {
                        rc = ipv6_connect(&server->addr.sockAddr6,
                                          &server->ssocket, server->noautotune);
                } else {
@@ -417,9 +411,14 @@ incomplete_rcv:
                        msleep(1); /* minimum sleep to prevent looping
                                allowing socket to clear and app threads to set
                                tcpStatus CifsNeedReconnect if server hung */
-                       if (pdu_length < 4)
+                       if (pdu_length < 4) {
+                               iov.iov_base = (4 - pdu_length) +
+                                                       (char *)smb_buffer;
+                               iov.iov_len = pdu_length;
+                               smb_msg.msg_control = NULL;
+                               smb_msg.msg_controllen = 0;
                                goto incomplete_rcv;
-                       else
+                       } else
                                continue;
                } else if (length <= 0) {
                        if (server->tcpStatus == CifsNew) {
@@ -654,6 +653,11 @@ multi_t2_fnd:
                }
        } /* end while !EXITING */
 
+       /* take it off the list, if it's not already */
+       write_lock(&cifs_tcp_ses_lock);
+       list_del_init(&server->tcp_ses_list);
+       write_unlock(&cifs_tcp_ses_lock);
+
        spin_lock(&GlobalMid_Lock);
        server->tcpStatus = CifsExiting;
        spin_unlock(&GlobalMid_Lock);
@@ -686,29 +690,29 @@ multi_t2_fnd:
        if (smallbuf) /* no sense logging a debug message if NULL */
                cifs_small_buf_release(smallbuf);
 
-       read_lock(&GlobalSMBSeslock);
+       /*
+        * BB: we shouldn't have to do any of this. It shouldn't be
+        * possible to exit from the thread with active SMB sessions
+        */
+       read_lock(&cifs_tcp_ses_lock);
        if (list_empty(&server->pending_mid_q)) {
                /* loop through server session structures attached to this and
                    mark them dead */
-               list_for_each(tmp, &GlobalSMBSessionList) {
-                       ses =
-                           list_entry(tmp, struct cifsSesInfo,
-                                      cifsSessionList);
-                       if (ses->server == server) {
-                               ses->status = CifsExiting;
-                               ses->server = NULL;
-                       }
+               list_for_each(tmp, &server->smb_ses_list) {
+                       ses = list_entry(tmp, struct cifsSesInfo,
+                                        smb_ses_list);
+                       ses->status = CifsExiting;
+                       ses->server = NULL;
                }
-               read_unlock(&GlobalSMBSeslock);
+               read_unlock(&cifs_tcp_ses_lock);
        } else {
                /* although we can not zero the server struct pointer yet,
                since there are active requests which may depnd on them,
                mark the corresponding SMB sessions as exiting too */
-               list_for_each(tmp, &GlobalSMBSessionList) {
+               list_for_each(tmp, &server->smb_ses_list) {
                        ses = list_entry(tmp, struct cifsSesInfo,
-                                        cifsSessionList);
-                       if (ses->server == server)
-                               ses->status = CifsExiting;
+                                        smb_ses_list);
+                       ses->status = CifsExiting;
                }
 
                spin_lock(&GlobalMid_Lock);
@@ -723,7 +727,7 @@ multi_t2_fnd:
                        }
                }
                spin_unlock(&GlobalMid_Lock);
-               read_unlock(&GlobalSMBSeslock);
+               read_unlock(&cifs_tcp_ses_lock);
                /* 1/8th of sec is more than enough time for them to exit */
                msleep(125);
        }
@@ -745,14 +749,13 @@ multi_t2_fnd:
        if there are any pointing to this (e.g
        if a crazy root user tried to kill cifsd
        kernel thread explicitly this might happen) */
-       write_lock(&GlobalSMBSeslock);
-       list_for_each(tmp, &GlobalSMBSessionList) {
-               ses = list_entry(tmp, struct cifsSesInfo,
-                               cifsSessionList);
-               if (ses->server == server)
-                       ses->server = NULL;
+       /* BB: This shouldn't be necessary, see above */
+       read_lock(&cifs_tcp_ses_lock);
+       list_for_each(tmp, &server->smb_ses_list) {
+               ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
+               ses->server = NULL;
        }
-       write_unlock(&GlobalSMBSeslock);
+       read_unlock(&cifs_tcp_ses_lock);
 
        kfree(server->hostname);
        task_to_wake = xchg(&server->tsk, NULL);
@@ -1352,94 +1355,158 @@ cifs_parse_mount_options(char *options, const char *devname,
        return 0;
 }
 
-static struct cifsSesInfo *
-cifs_find_tcp_session(struct in_addr *target_ip_addr,
-                     struct in6_addr *target_ip6_addr,
-                     char *userName, struct TCP_Server_Info **psrvTcp)
+static struct TCP_Server_Info *
+cifs_find_tcp_session(struct sockaddr *addr)
 {
        struct list_head *tmp;
-       struct cifsSesInfo *ses;
-
-       *psrvTcp = NULL;
+       struct TCP_Server_Info *server;
+       struct sockaddr_in *addr4 = (struct sockaddr_in *) addr;
+       struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr;
+
+       write_lock(&cifs_tcp_ses_lock);
+       list_for_each(tmp, &cifs_tcp_ses_list) {
+               server = list_entry(tmp, struct TCP_Server_Info,
+                                   tcp_ses_list);
+               /*
+                * the demux thread can exit on its own while still in CifsNew
+                * so don't accept any sockets in that state. Since the
+                * tcpStatus never changes back to CifsNew it's safe to check
+                * for this without a lock.
+                */
+               if (server->tcpStatus == CifsNew)
+                       continue;
 
-       read_lock(&GlobalSMBSeslock);
-       list_for_each(tmp, &GlobalSMBSessionList) {
-               ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
-               if (!ses->server)
+               if (addr->sa_family == AF_INET &&
+                   (addr4->sin_addr.s_addr !=
+                    server->addr.sockAddr.sin_addr.s_addr))
+                       continue;
+               else if (addr->sa_family == AF_INET6 &&
+                        memcmp(&server->addr.sockAddr6.sin6_addr,
+                               &addr6->sin6_addr, sizeof(addr6->sin6_addr)))
                        continue;
 
-               if (target_ip_addr &&
-                   ses->server->addr.sockAddr.sin_addr.s_addr != target_ip_addr->s_addr)
-                               continue;
-               else if (target_ip6_addr &&
-                        memcmp(&ses->server->addr.sockAddr6.sin6_addr,
-                               target_ip6_addr, sizeof(*target_ip6_addr)))
-                               continue;
-               /* BB lock server and tcp session; increment use count here?? */
+               ++server->srv_count;
+               write_unlock(&cifs_tcp_ses_lock);
+               cFYI(1, ("Existing tcp session with server found"));
+               return server;
+       }
+       write_unlock(&cifs_tcp_ses_lock);
+       return NULL;
+}
 
-               /* found a match on the TCP session */
-               *psrvTcp = ses->server;
+static void
+cifs_put_tcp_session(struct TCP_Server_Info *server)
+{
+       struct task_struct *task;
 
-               /* BB check if reconnection needed */
-               if (strncmp(ses->userName, userName, MAX_USERNAME_SIZE) == 0) {
-                       read_unlock(&GlobalSMBSeslock);
-                       /* Found exact match on both TCP and
-                          SMB sessions */
-                       return ses;
-               }
-               /* else tcp and smb sessions need reconnection */
+       write_lock(&cifs_tcp_ses_lock);
+       if (--server->srv_count > 0) {
+               write_unlock(&cifs_tcp_ses_lock);
+               return;
        }
-       read_unlock(&GlobalSMBSeslock);
 
-       return NULL;
+       list_del_init(&server->tcp_ses_list);
+       write_unlock(&cifs_tcp_ses_lock);
+
+       spin_lock(&GlobalMid_Lock);
+       server->tcpStatus = CifsExiting;
+       spin_unlock(&GlobalMid_Lock);
+
+       task = xchg(&server->tsk, NULL);
+       if (task)
+               force_sig(SIGKILL, task);
 }
 
-static struct cifsTconInfo *
-find_unc(__be32 new_target_ip_addr, char *uncName, char *userName)
+static struct cifsSesInfo *
+cifs_find_smb_ses(struct TCP_Server_Info *server, char *username)
 {
        struct list_head *tmp;
-       struct cifsTconInfo *tcon;
-       __be32 old_ip;
-
-       read_lock(&GlobalSMBSeslock);
+       struct cifsSesInfo *ses;
 
-       list_for_each(tmp, &GlobalTreeConnectionList) {
-               cFYI(1, ("Next tcon"));
-               tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
-               if (!tcon->ses || !tcon->ses->server)
+       write_lock(&cifs_tcp_ses_lock);
+       list_for_each(tmp, &server->smb_ses_list) {
+               ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
+               if (strncmp(ses->userName, username, MAX_USERNAME_SIZE))
                        continue;
 
-               old_ip = tcon->ses->server->addr.sockAddr.sin_addr.s_addr;
-               cFYI(1, ("old ip addr: %x == new ip %x ?",
-                       old_ip, new_target_ip_addr));
+               ++ses->ses_count;
+               write_unlock(&cifs_tcp_ses_lock);
+               return ses;
+       }
+       write_unlock(&cifs_tcp_ses_lock);
+       return NULL;
+}
 
-               if (old_ip != new_target_ip_addr)
-                       continue;
+static void
+cifs_put_smb_ses(struct cifsSesInfo *ses)
+{
+       int xid;
+       struct TCP_Server_Info *server = ses->server;
 
-               /* BB lock tcon, server, tcp session and increment use count? */
-               /* found a match on the TCP session */
-               /* BB check if reconnection needed */
-               cFYI(1, ("IP match, old UNC: %s new: %s",
-                       tcon->treeName, uncName));
+       write_lock(&cifs_tcp_ses_lock);
+       if (--ses->ses_count > 0) {
+               write_unlock(&cifs_tcp_ses_lock);
+               return;
+       }
 
-               if (strncmp(tcon->treeName, uncName, MAX_TREE_SIZE))
-                       continue;
+       list_del_init(&ses->smb_ses_list);
+       write_unlock(&cifs_tcp_ses_lock);
 
-               cFYI(1, ("and old usr: %s new: %s",
-                       tcon->treeName, uncName));
+       if (ses->status == CifsGood) {
+               xid = GetXid();
+               CIFSSMBLogoff(xid, ses);
+               _FreeXid(xid);
+       }
+       sesInfoFree(ses);
+       cifs_put_tcp_session(server);
+}
 
-               if (strncmp(tcon->ses->userName, userName, MAX_USERNAME_SIZE))
+static struct cifsTconInfo *
+cifs_find_tcon(struct cifsSesInfo *ses, const char *unc)
+{
+       struct list_head *tmp;
+       struct cifsTconInfo *tcon;
+
+       write_lock(&cifs_tcp_ses_lock);
+       list_for_each(tmp, &ses->tcon_list) {
+               tcon = list_entry(tmp, struct cifsTconInfo, tcon_list);
+               if (tcon->tidStatus == CifsExiting)
+                       continue;
+               if (strncmp(tcon->treeName, unc, MAX_TREE_SIZE))
                        continue;
 
-               /* matched smb session (user name) */
-               read_unlock(&GlobalSMBSeslock);
+               ++tcon->tc_count;
+               write_unlock(&cifs_tcp_ses_lock);
                return tcon;
        }
-
-       read_unlock(&GlobalSMBSeslock);
+       write_unlock(&cifs_tcp_ses_lock);
        return NULL;
 }
 
+static void
+cifs_put_tcon(struct cifsTconInfo *tcon)
+{
+       int xid;
+       struct cifsSesInfo *ses = tcon->ses;
+
+       write_lock(&cifs_tcp_ses_lock);
+       if (--tcon->tc_count > 0) {
+               write_unlock(&cifs_tcp_ses_lock);
+               return;
+       }
+
+       list_del_init(&tcon->tcon_list);
+       write_unlock(&cifs_tcp_ses_lock);
+
+       xid = GetXid();
+       CIFSSMBTDis(xid, tcon);
+       _FreeXid(xid);
+
+       DeleteTconOplockQEntries(tcon);
+       tconInfoFree(tcon);
+       cifs_put_smb_ses(ses);
+}
+
 int
 get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
             const struct nls_table *nls_codepage, unsigned int *pnum_referrals,
@@ -1876,14 +1943,90 @@ convert_delimiter(char *path, char delim)
        }
 }
 
-static void
-kill_cifsd(struct TCP_Server_Info *server)
+static void setup_cifs_sb(struct smb_vol *pvolume_info,
+                         struct cifs_sb_info *cifs_sb)
 {
-       struct task_struct *task;
-
-       task = xchg(&server->tsk, NULL);
-       if (task)
-               force_sig(SIGKILL, task);
+       if (pvolume_info->rsize > CIFSMaxBufSize) {
+               cERROR(1, ("rsize %d too large, using MaxBufSize",
+                       pvolume_info->rsize));
+               cifs_sb->rsize = CIFSMaxBufSize;
+       } else if ((pvolume_info->rsize) &&
+                       (pvolume_info->rsize <= CIFSMaxBufSize))
+               cifs_sb->rsize = pvolume_info->rsize;
+       else /* default */
+               cifs_sb->rsize = CIFSMaxBufSize;
+
+       if (pvolume_info->wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) {
+               cERROR(1, ("wsize %d too large, using 4096 instead",
+                         pvolume_info->wsize));
+               cifs_sb->wsize = 4096;
+       } else if (pvolume_info->wsize)
+               cifs_sb->wsize = pvolume_info->wsize;
+       else
+               cifs_sb->wsize = min_t(const int,
+                                       PAGEVEC_SIZE * PAGE_CACHE_SIZE,
+                                       127*1024);
+               /* old default of CIFSMaxBufSize was too small now
+                  that SMB Write2 can send multiple pages in kvec.
+                  RFC1001 does not describe what happens when frame
+                  bigger than 128K is sent so use that as max in
+                  conjunction with 52K kvec constraint on arch with 4K
+                  page size  */
+
+       if (cifs_sb->rsize < 2048) {
+               cifs_sb->rsize = 2048;
+               /* Windows ME may prefer this */
+               cFYI(1, ("readsize set to minimum: 2048"));
+       }
+       /* calculate prepath */
+       cifs_sb->prepath = pvolume_info->prepath;
+       if (cifs_sb->prepath) {
+               cifs_sb->prepathlen = strlen(cifs_sb->prepath);
+               /* we can not convert the / to \ in the path
+               separators in the prefixpath yet because we do not
+               know (until reset_cifs_unix_caps is called later)
+               whether POSIX PATH CAP is available. We normalize
+               the / to \ after reset_cifs_unix_caps is called */
+               pvolume_info->prepath = NULL;
+       } else
+               cifs_sb->prepathlen = 0;
+       cifs_sb->mnt_uid = pvolume_info->linux_uid;
+       cifs_sb->mnt_gid = pvolume_info->linux_gid;
+       cifs_sb->mnt_file_mode = pvolume_info->file_mode;
+       cifs_sb->mnt_dir_mode = pvolume_info->dir_mode;
+       cFYI(1, ("file mode: 0x%x  dir mode: 0x%x",
+               cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode));
+
+       if (pvolume_info->noperm)
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM;
+       if (pvolume_info->setuids)
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID;
+       if (pvolume_info->server_ino)
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM;
+       if (pvolume_info->remap)
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR;
+       if (pvolume_info->no_xattr)
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR;
+       if (pvolume_info->sfu_emul)
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL;
+       if (pvolume_info->nobrl)
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL;
+       if (pvolume_info->cifs_acl)
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL;
+       if (pvolume_info->override_uid)
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID;
+       if (pvolume_info->override_gid)
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID;
+       if (pvolume_info->dynperm)
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM;
+       if (pvolume_info->direct_io) {
+               cFYI(1, ("mounting share using direct i/o"));
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
+       }
+
+       if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm))
+               cERROR(1, ("mount option dynperm ignored if cifsacl "
+                          "mount option supported"));
 }
 
 int
@@ -1892,13 +2035,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 {
        int rc = 0;
        int xid;
-       int address_type = AF_INET;
        struct socket *csocket = NULL;
-       struct sockaddr_in sin_server;
-       struct sockaddr_in6 sin_server6;
+       struct sockaddr addr;
+       struct sockaddr_in *sin_server = (struct sockaddr_in *) &addr;
+       struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr;
        struct smb_vol volume_info;
        struct cifsSesInfo *pSesInfo = NULL;
-       struct cifsSesInfo *existingCifsSes = NULL;
        struct cifsTconInfo *tcon = NULL;
        struct TCP_Server_Info *srvTcp = NULL;
 
@@ -1906,6 +2048,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 
 /* cFYI(1, ("Entering cifs_mount. Xid: %d with: %s", xid, mount_data)); */
 
+       memset(&addr, 0, sizeof(struct sockaddr));
        memset(&volume_info, 0, sizeof(struct smb_vol));
        if (cifs_parse_mount_options(mount_data, devname, &volume_info)) {
                rc = -EINVAL;
@@ -1928,16 +2071,16 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 
        if (volume_info.UNCip && volume_info.UNC) {
                rc = cifs_inet_pton(AF_INET, volume_info.UNCip,
-                                   &sin_server.sin_addr.s_addr);
+                                   &sin_server->sin_addr.s_addr);
 
                if (rc <= 0) {
                        /* not ipv4 address, try ipv6 */
                        rc = cifs_inet_pton(AF_INET6, volume_info.UNCip,
-                                           &sin_server6.sin6_addr.in6_u);
+                                           &sin_server6->sin6_addr.in6_u);
                        if (rc > 0)
-                               address_type = AF_INET6;
+                               addr.sa_family = AF_INET6;
                } else {
-                       address_type = AF_INET;
+                       addr.sa_family = AF_INET;
                }
 
                if (rc <= 0) {
@@ -1977,41 +2120,25 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                }
        }
 
-       if (address_type == AF_INET)
-               existingCifsSes = cifs_find_tcp_session(&sin_server.sin_addr,
-                       NULL /* no ipv6 addr */,
-                       volume_info.username, &srvTcp);
-       else if (address_type == AF_INET6) {
-               cFYI(1, ("looking for ipv6 address"));
-               existingCifsSes = cifs_find_tcp_session(NULL /* no ipv4 addr */,
-                       &sin_server6.sin6_addr,
-                       volume_info.username, &srvTcp);
-       } else {
-               rc = -EINVAL;
-               goto out;
-       }
-
-       if (srvTcp) {
-               cFYI(1, ("Existing tcp session with server found"));
-       } else {        /* create socket */
-               if (volume_info.port)
-                       sin_server.sin_port = htons(volume_info.port);
-               else
-                       sin_server.sin_port = 0;
-               if (address_type == AF_INET6) {
+       srvTcp = cifs_find_tcp_session(&addr);
+       if (!srvTcp) { /* create socket */
+               if (addr.sa_family == AF_INET6) {
                        cFYI(1, ("attempting ipv6 connect"));
                        /* BB should we allow ipv6 on port 139? */
                        /* other OS never observed in Wild doing 139 with v6 */
-                       rc = ipv6_connect(&sin_server6, &csocket,
+                       sin_server6->sin6_port = htons(volume_info.port);
+                       rc = ipv6_connect(sin_server6, &csocket,
                                        volume_info.noblocksnd);
-               } else
-                       rc = ipv4_connect(&sin_server, &csocket,
+               } else {
+                       sin_server->sin_port = htons(volume_info.port);
+                       rc = ipv4_connect(sin_server, &csocket,
                                  volume_info.source_rfc1001_name,
                                  volume_info.target_rfc1001_name,
                                  volume_info.noblocksnd,
                                  volume_info.noautotune);
+               }
                if (rc < 0) {
-                       cERROR(1, ("Error connecting to IPv4 socket. "
+                       cERROR(1, ("Error connecting to socket. "
                                   "Aborting operation"));
                        if (csocket != NULL)
                                sock_release(csocket);
@@ -2026,12 +2153,15 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                } else {
                        srvTcp->noblocksnd = volume_info.noblocksnd;
                        srvTcp->noautotune = volume_info.noautotune;
-                       memcpy(&srvTcp->addr.sockAddr, &sin_server,
-                               sizeof(struct sockaddr_in));
+                       if (addr.sa_family == AF_INET6)
+                               memcpy(&srvTcp->addr.sockAddr6, sin_server6,
+                                       sizeof(struct sockaddr_in6));
+                       else
+                               memcpy(&srvTcp->addr.sockAddr, sin_server,
+                                       sizeof(struct sockaddr_in));
                        atomic_set(&srvTcp->inFlight, 0);
                        /* BB Add code for ipv6 case too */
                        srvTcp->ssocket = csocket;
-                       srvTcp->protocolType = IPV4;
                        srvTcp->hostname = extract_hostname(volume_info.UNC);
                        if (IS_ERR(srvTcp->hostname)) {
                                rc = PTR_ERR(srvTcp->hostname);
@@ -2061,15 +2191,28 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                        memcpy(srvTcp->server_RFC1001_name,
                                volume_info.target_rfc1001_name, 16);
                        srvTcp->sequence_number = 0;
+                       INIT_LIST_HEAD(&srvTcp->tcp_ses_list);
+                       INIT_LIST_HEAD(&srvTcp->smb_ses_list);
+                       ++srvTcp->srv_count;
+                       write_lock(&cifs_tcp_ses_lock);
+                       list_add(&srvTcp->tcp_ses_list,
+                                &cifs_tcp_ses_list);
+                       write_unlock(&cifs_tcp_ses_lock);
                }
        }
 
-       if (existingCifsSes) {
-               pSesInfo = existingCifsSes;
+       pSesInfo = cifs_find_smb_ses(srvTcp, volume_info.username);
+       if (pSesInfo) {
                cFYI(1, ("Existing smb sess found (status=%d)",
                        pSesInfo->status));
+               /*
+                * The existing SMB session already has a reference to srvTcp,
+                * so we can put back the extra one we got before
+                */
+               cifs_put_tcp_session(srvTcp);
+
                down(&pSesInfo->sesSem);
-               if (pSesInfo->status == CifsNeedReconnect) {
+               if (pSesInfo->need_reconnect) {
                        cFYI(1, ("Session needs reconnect"));
                        rc = cifs_setup_session(xid, pSesInfo,
                                                cifs_sb->local_nls);
@@ -2078,187 +2221,101 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
        } else if (!rc) {
                cFYI(1, ("Existing smb sess not found"));
                pSesInfo = sesInfoAlloc();
-               if (pSesInfo == NULL)
+               if (pSesInfo == NULL) {
                        rc = -ENOMEM;
-               else {
-                       pSesInfo->server = srvTcp;
-                       sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
-                               NIPQUAD(sin_server.sin_addr.s_addr));
-               }
+                       goto mount_fail_check;
+               }
+
+               /* new SMB session uses our srvTcp ref */
+               pSesInfo->server = srvTcp;
+               sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
+                       NIPQUAD(sin_server->sin_addr.s_addr));
+
+               write_lock(&cifs_tcp_ses_lock);
+               list_add(&pSesInfo->smb_ses_list, &srvTcp->smb_ses_list);
+               write_unlock(&cifs_tcp_ses_lock);
+
+               /* volume_info.password freed at unmount */
+               if (volume_info.password) {
+                       pSesInfo->password = volume_info.password;
+                       /* set to NULL to prevent freeing on exit */
+                       volume_info.password = NULL;
+               }
+               if (volume_info.username)
+                       strncpy(pSesInfo->userName, volume_info.username,
+                               MAX_USERNAME_SIZE);
+               if (volume_info.domainname) {
+                       int len = strlen(volume_info.domainname);
+                       pSesInfo->domainName = kmalloc(len + 1, GFP_KERNEL);
+                       if (pSesInfo->domainName)
+                               strcpy(pSesInfo->domainName,
+                                       volume_info.domainname);
+               }
+               pSesInfo->linux_uid = volume_info.linux_uid;
+               pSesInfo->overrideSecFlg = volume_info.secFlg;
+               down(&pSesInfo->sesSem);
 
-               if (!rc) {
-                       /* volume_info.password freed at unmount */
-                       if (volume_info.password) {
-                               pSesInfo->password = volume_info.password;
-                               /* set to NULL to prevent freeing on exit */
-                               volume_info.password = NULL;
-                       }
-                       if (volume_info.username)
-                               strncpy(pSesInfo->userName,
-                                       volume_info.username,
-                                       MAX_USERNAME_SIZE);
-                       if (volume_info.domainname) {
-                               int len = strlen(volume_info.domainname);
-                               pSesInfo->domainName =
-                                       kmalloc(len + 1, GFP_KERNEL);
-                               if (pSesInfo->domainName)
-                                       strcpy(pSesInfo->domainName,
-                                               volume_info.domainname);
-                       }
-                       pSesInfo->linux_uid = volume_info.linux_uid;
-                       pSesInfo->overrideSecFlg = volume_info.secFlg;
-                       down(&pSesInfo->sesSem);
-                       /* BB FIXME need to pass vol->secFlgs BB */
-                       rc = cifs_setup_session(xid, pSesInfo,
-                                               cifs_sb->local_nls);
-                       up(&pSesInfo->sesSem);
-                       if (!rc)
-                               atomic_inc(&srvTcp->socketUseCount);
-               }
+               /* BB FIXME need to pass vol->secFlgs BB */
+               rc = cifs_setup_session(xid, pSesInfo,
+                                       cifs_sb->local_nls);
+               up(&pSesInfo->sesSem);
        }
 
        /* search for existing tcon to this server share */
        if (!rc) {
-               if (volume_info.rsize > CIFSMaxBufSize) {
-                       cERROR(1, ("rsize %d too large, using MaxBufSize",
-                               volume_info.rsize));
-                       cifs_sb->rsize = CIFSMaxBufSize;
-               } else if ((volume_info.rsize) &&
-                               (volume_info.rsize <= CIFSMaxBufSize))
-                       cifs_sb->rsize = volume_info.rsize;
-               else /* default */
-                       cifs_sb->rsize = CIFSMaxBufSize;
-
-               if (volume_info.wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) {
-                       cERROR(1, ("wsize %d too large, using 4096 instead",
-                                 volume_info.wsize));
-                       cifs_sb->wsize = 4096;
-               } else if (volume_info.wsize)
-                       cifs_sb->wsize = volume_info.wsize;
-               else
-                       cifs_sb->wsize =
-                               min_t(const int, PAGEVEC_SIZE * PAGE_CACHE_SIZE,
-                                       127*1024);
-                       /* old default of CIFSMaxBufSize was too small now
-                          that SMB Write2 can send multiple pages in kvec.
-                          RFC1001 does not describe what happens when frame
-                          bigger than 128K is sent so use that as max in
-                          conjunction with 52K kvec constraint on arch with 4K
-                          page size  */
-
-               if (cifs_sb->rsize < 2048) {
-                       cifs_sb->rsize = 2048;
-                       /* Windows ME may prefer this */
-                       cFYI(1, ("readsize set to minimum: 2048"));
-               }
-               /* calculate prepath */
-               cifs_sb->prepath = volume_info.prepath;
-               if (cifs_sb->prepath) {
-                       cifs_sb->prepathlen = strlen(cifs_sb->prepath);
-                       /* we can not convert the / to \ in the path
-                       separators in the prefixpath yet because we do not
-                       know (until reset_cifs_unix_caps is called later)
-                       whether POSIX PATH CAP is available. We normalize
-                       the / to \ after reset_cifs_unix_caps is called */
-                       volume_info.prepath = NULL;
-               } else
-                       cifs_sb->prepathlen = 0;
-               cifs_sb->mnt_uid = volume_info.linux_uid;
-               cifs_sb->mnt_gid = volume_info.linux_gid;
-               cifs_sb->mnt_file_mode = volume_info.file_mode;
-               cifs_sb->mnt_dir_mode = volume_info.dir_mode;
-               cFYI(1, ("file mode: 0x%x  dir mode: 0x%x",
-                       cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode));
-
-               if (volume_info.noperm)
-                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM;
-               if (volume_info.setuids)
-                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID;
-               if (volume_info.server_ino)
-                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM;
-               if (volume_info.remap)
-                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR;
-               if (volume_info.no_xattr)
-                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR;
-               if (volume_info.sfu_emul)
-                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL;
-               if (volume_info.nobrl)
-                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL;
-               if (volume_info.cifs_acl)
-                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL;
-               if (volume_info.override_uid)
-                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID;
-               if (volume_info.override_gid)
-                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID;
-               if (volume_info.dynperm)
-                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM;
-               if (volume_info.direct_io) {
-                       cFYI(1, ("mounting share using direct i/o"));
-                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
-               }
+               setup_cifs_sb(&volume_info, cifs_sb);
 
-               if ((volume_info.cifs_acl) && (volume_info.dynperm))
-                       cERROR(1, ("mount option dynperm ignored if cifsacl "
-                                  "mount option supported"));
-
-               tcon =
-                   find_unc(sin_server.sin_addr.s_addr, volume_info.UNC,
-                            volume_info.username);
+               tcon = cifs_find_tcon(pSesInfo, volume_info.UNC);
                if (tcon) {
                        cFYI(1, ("Found match on UNC path"));
-                       /* we can have only one retry value for a connection
-                          to a share so for resources mounted more than once
-                          to the same server share the last value passed in
-                          for the retry flag is used */
-                       tcon->retry = volume_info.retry;
-                       tcon->nocase = volume_info.nocase;
-                       tcon->local_lease = volume_info.local_lease;
+                       /* existing tcon already has a reference */
+                       cifs_put_smb_ses(pSesInfo);
                        if (tcon->seal != volume_info.seal)
                                cERROR(1, ("transport encryption setting "
                                           "conflicts with existing tid"));
                } else {
                        tcon = tconInfoAlloc();
-                       if (tcon == NULL)
+                       if (tcon == NULL) {
                                rc = -ENOMEM;
-                       else {
-                               /* check for null share name ie connecting to
-                                * dfs root */
-
-                               /* BB check if this works for exactly length
-                                * three strings */
-                               if ((strchr(volume_info.UNC + 3, '\\') == NULL)
-                                   && (strchr(volume_info.UNC + 3, '/') ==
-                                       NULL)) {
-/*                                     rc = connect_to_dfs_path(xid, pSesInfo,
-                                               "", cifs_sb->local_nls,
-                                               cifs_sb->mnt_cifs_flags &
-                                                 CIFS_MOUNT_MAP_SPECIAL_CHR);*/
-                                       cFYI(1, ("DFS root not supported"));
-                                       rc = -ENODEV;
-                                       goto out;
-                               } else {
-                                       /* BB Do we need to wrap sesSem around
-                                        * this TCon call and Unix SetFS as
-                                        * we do on SessSetup and reconnect? */
-                                       rc = CIFSTCon(xid, pSesInfo,
-                                               volume_info.UNC,
-                                               tcon, cifs_sb->local_nls);
-                                       cFYI(1, ("CIFS Tcon rc = %d", rc));
-                                       if (volume_info.nodfs) {
-                                               tcon->Flags &=
-                                                       ~SMB_SHARE_IS_IN_DFS;
-                                               cFYI(1, ("DFS disabled (%d)",
-                                                       tcon->Flags));
-                                       }
-                               }
-                               if (!rc) {
-                                       atomic_inc(&pSesInfo->inUse);
-                                       tcon->retry = volume_info.retry;
-                                       tcon->nocase = volume_info.nocase;
-                                       tcon->seal = volume_info.seal;
+                               goto mount_fail_check;
+                       }
+                       tcon->ses = pSesInfo;
+
+                       /* check for null share name ie connect to dfs root */
+                       if ((strchr(volume_info.UNC + 3, '\\') == NULL)
+                           && (strchr(volume_info.UNC + 3, '/') == NULL)) {
+                               /* rc = connect_to_dfs_path(...) */
+                               cFYI(1, ("DFS root not supported"));
+                               rc = -ENODEV;
+                               goto mount_fail_check;
+                       } else {
+                               /* BB Do we need to wrap sesSem around
+                                * this TCon call and Unix SetFS as
+                                * we do on SessSetup and reconnect? */
+                               rc = CIFSTCon(xid, pSesInfo, volume_info.UNC,
+                                             tcon, cifs_sb->local_nls);
+                               cFYI(1, ("CIFS Tcon rc = %d", rc));
+                               if (volume_info.nodfs) {
+                                       tcon->Flags &= ~SMB_SHARE_IS_IN_DFS;
+                                       cFYI(1, ("DFS disabled (%d)",
+                                               tcon->Flags));
                                }
                        }
-               }
+                       if (rc)
+                               goto mount_fail_check;
+                       tcon->seal = volume_info.seal;
+                       write_lock(&cifs_tcp_ses_lock);
+                       list_add(&tcon->tcon_list, &pSesInfo->tcon_list);
+                       write_unlock(&cifs_tcp_ses_lock);
+               }
+
+               /* we can have only one retry value for a connection
+                  to a share so for resources mounted more than once
+                  to the same server share the last value passed in
+                  for the retry flag is used */
+               tcon->retry = volume_info.retry;
+               tcon->nocase = volume_info.nocase;
+               tcon->local_lease = volume_info.local_lease;
        }
        if (pSesInfo) {
                if (pSesInfo->capabilities & CAP_LARGE_FILES) {
@@ -2270,80 +2327,49 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
        /* BB FIXME fix time_gran to be larger for LANMAN sessions */
        sb->s_time_gran = 100;
 
-/* on error free sesinfo and tcon struct if needed */
+mount_fail_check:
+       /* on error free sesinfo and tcon struct if needed */
        if (rc) {
-               /* if session setup failed, use count is zero but
-               we still need to free cifsd thread */
-               if (atomic_read(&srvTcp->socketUseCount) == 0) {
-                       spin_lock(&GlobalMid_Lock);
-                       srvTcp->tcpStatus = CifsExiting;
-                       spin_unlock(&GlobalMid_Lock);
-                       kill_cifsd(srvTcp);
-               }
-                /* If find_unc succeeded then rc == 0 so we can not end */
-               if (tcon)  /* up accidently freeing someone elses tcon struct */
-                       tconInfoFree(tcon);
-               if (existingCifsSes == NULL) {
-                       if (pSesInfo) {
-                               if ((pSesInfo->server) &&
-                                   (pSesInfo->status == CifsGood)) {
-                                       int temp_rc;
-                                       temp_rc = CIFSSMBLogoff(xid, pSesInfo);
-                                       /* if the socketUseCount is now zero */
-                                       if ((temp_rc == -ESHUTDOWN) &&
-                                           (pSesInfo->server))
-                                               kill_cifsd(pSesInfo->server);
-                               } else {
-                                       cFYI(1, ("No session or bad tcon"));
-                                       if (pSesInfo->server) {
-                                               spin_lock(&GlobalMid_Lock);
-                                               srvTcp->tcpStatus = CifsExiting;
-                                               spin_unlock(&GlobalMid_Lock);
-                                               kill_cifsd(pSesInfo->server);
-                                       }
-                               }
-                               sesInfoFree(pSesInfo);
-                               /* pSesInfo = NULL; */
-                       }
-               }
-       } else {
-               atomic_inc(&tcon->useCount);
-               cifs_sb->tcon = tcon;
-               tcon->ses = pSesInfo;
-
-               /* do not care if following two calls succeed - informational */
-               if (!tcon->ipc) {
-                       CIFSSMBQFSDeviceInfo(xid, tcon);
-                       CIFSSMBQFSAttributeInfo(xid, tcon);
-               }
-
-               /* tell server which Unix caps we support */
-               if (tcon->ses->capabilities & CAP_UNIX)
-                       /* reset of caps checks mount to see if unix extensions
-                          disabled for just this mount */
-                       reset_cifs_unix_caps(xid, tcon, sb, &volume_info);
+               /* If find_unc succeeded then rc == 0 so we can not end */
+               /* up accidently freeing someone elses tcon struct */
+               if (tcon)
+                       cifs_put_tcon(tcon);
+               else if (pSesInfo)
+                       cifs_put_smb_ses(pSesInfo);
                else
-                       tcon->unix_ext = 0; /* server does not support them */
+                       cifs_put_tcp_session(srvTcp);
+               goto out;
+       }
+       cifs_sb->tcon = tcon;
 
-               /* convert forward to back slashes in prepath here if needed */
-               if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0)
-                       convert_delimiter(cifs_sb->prepath,
-                                         CIFS_DIR_SEP(cifs_sb));
+       /* do not care if following two calls succeed - informational */
+       if (!tcon->ipc) {
+               CIFSSMBQFSDeviceInfo(xid, tcon);
+               CIFSSMBQFSAttributeInfo(xid, tcon);
+       }
 
-               if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) {
-                       cifs_sb->rsize = 1024 * 127;
-                       cFYI(DBG2,
-                               ("no very large read support, rsize now 127K"));
-               }
-               if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
-                       cifs_sb->wsize = min(cifs_sb->wsize,
-                                            (tcon->ses->server->maxBuf -
-                                             MAX_CIFS_HDR_SIZE));
-               if (!(tcon->ses->capabilities & CAP_LARGE_READ_X))
-                       cifs_sb->rsize = min(cifs_sb->rsize,
-                                            (tcon->ses->server->maxBuf -
-                                             MAX_CIFS_HDR_SIZE));
+       /* tell server which Unix caps we support */
+       if (tcon->ses->capabilities & CAP_UNIX)
+               /* reset of caps checks mount to see if unix extensions
+                  disabled for just this mount */
+               reset_cifs_unix_caps(xid, tcon, sb, &volume_info);
+       else
+               tcon->unix_ext = 0; /* server does not support them */
+
+       /* convert forward to back slashes in prepath here if needed */
+       if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0)
+               convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
+
+       if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) {
+               cifs_sb->rsize = 1024 * 127;
+               cFYI(DBG2, ("no very large read support, rsize now 127K"));
        }
+       if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
+               cifs_sb->wsize = min(cifs_sb->wsize,
+                              (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE));
+       if (!(tcon->ses->capabilities & CAP_LARGE_READ_X))
+               cifs_sb->rsize = min(cifs_sb->rsize,
+                              (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE));
 
        /* volume_info.password is freed above when existing session found
        (in which case it is not needed anymore) but when new sesion is created
@@ -3513,6 +3539,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
        /* above now done in SendReceive */
        if ((rc == 0) && (tcon != NULL)) {
                tcon->tidStatus = CifsGood;
+               tcon->need_reconnect = false;
                tcon->tid = smb_buffer_response->Tid;
                bcc_ptr = pByteArea(smb_buffer_response);
                length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2);
@@ -3584,48 +3611,17 @@ int
 cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
 {
        int rc = 0;
-       int xid;
-       struct cifsSesInfo *ses = NULL;
        char *tmp;
 
-       xid = GetXid();
-
-       if (cifs_sb->tcon) {
-               ses = cifs_sb->tcon->ses; /* save ptr to ses before delete tcon!*/
-               rc = CIFSSMBTDis(xid, cifs_sb->tcon);
-               if (rc == -EBUSY) {
-                       FreeXid(xid);
-                       return 0;
-               }
-               DeleteTconOplockQEntries(cifs_sb->tcon);
-               tconInfoFree(cifs_sb->tcon);
-               if ((ses) && (ses->server)) {
-                       /* save off task so we do not refer to ses later */
-                       cFYI(1, ("About to do SMBLogoff "));
-                       rc = CIFSSMBLogoff(xid, ses);
-                       if (rc == -EBUSY) {
-                               FreeXid(xid);
-                               return 0;
-                       } else if (rc == -ESHUTDOWN) {
-                               cFYI(1, ("Waking up socket by sending signal"));
-                               if (ses->server)
-                                       kill_cifsd(ses->server);
-                               rc = 0;
-                       } /* else - we have an smb session
-                               left on this socket do not kill cifsd */
-               } else
-                       cFYI(1, ("No session or bad tcon"));
-       }
+       if (cifs_sb->tcon)
+               cifs_put_tcon(cifs_sb->tcon);
 
        cifs_sb->tcon = NULL;
        tmp = cifs_sb->prepath;
        cifs_sb->prepathlen = 0;
        cifs_sb->prepath = NULL;
        kfree(tmp);
-       if (ses)
-               sesInfoFree(ses);
 
-       FreeXid(xid);
        return rc;
 }
 
@@ -3741,6 +3737,7 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
                cFYI(1, ("CIFS Session Established successfully"));
                        spin_lock(&GlobalMid_Lock);
                        pSesInfo->status = CifsGood;
+                       pSesInfo->need_reconnect = false;
                        spin_unlock(&GlobalMid_Lock);
        }
 
index ead1a3b..6449e1a 100644 (file)
@@ -493,7 +493,7 @@ int cifs_close(struct inode *inode, struct file *file)
                if (pTcon) {
                        /* no sense reconnecting to close a file that is
                           already closed */
-                       if (pTcon->tidStatus != CifsNeedReconnect) {
+                       if (!pTcon->need_reconnect) {
                                timeout = 2;
                                while ((atomic_read(&pSMBFile->wrtPending) != 0)
                                        && (timeout <= 2048)) {
@@ -1404,7 +1404,10 @@ retry:
                        if ((wbc->nr_to_write -= n_iov) <= 0)
                                done = 1;
                        index = next;
-               }
+               } else
+                       /* Need to re-find the pages we skipped */
+                       index = pvec.pages[0]->index + 1;
+
                pagevec_release(&pvec);
        }
        if (!scanned && !done) {
index 88786ba..addd1dc 100644 (file)
@@ -75,12 +75,12 @@ sesInfoAlloc(void)
 
        ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL);
        if (ret_buf) {
-               write_lock(&GlobalSMBSeslock);
                atomic_inc(&sesInfoAllocCount);
                ret_buf->status = CifsNew;
-               list_add(&ret_buf->cifsSessionList, &GlobalSMBSessionList);
+               ++ret_buf->ses_count;
+               INIT_LIST_HEAD(&ret_buf->smb_ses_list);
+               INIT_LIST_HEAD(&ret_buf->tcon_list);
                init_MUTEX(&ret_buf->sesSem);
-               write_unlock(&GlobalSMBSeslock);
        }
        return ret_buf;
 }
@@ -93,10 +93,7 @@ sesInfoFree(struct cifsSesInfo *buf_to_free)
                return;
        }
 
-       write_lock(&GlobalSMBSeslock);
        atomic_dec(&sesInfoAllocCount);
-       list_del(&buf_to_free->cifsSessionList);
-       write_unlock(&GlobalSMBSeslock);
        kfree(buf_to_free->serverOS);
        kfree(buf_to_free->serverDomain);
        kfree(buf_to_free->serverNOS);
@@ -111,17 +108,14 @@ tconInfoAlloc(void)
        struct cifsTconInfo *ret_buf;
        ret_buf = kzalloc(sizeof(struct cifsTconInfo), GFP_KERNEL);
        if (ret_buf) {
-               write_lock(&GlobalSMBSeslock);
                atomic_inc(&tconInfoAllocCount);
-               list_add(&ret_buf->cifsConnectionList,
-                        &GlobalTreeConnectionList);
                ret_buf->tidStatus = CifsNew;
+               ++ret_buf->tc_count;
                INIT_LIST_HEAD(&ret_buf->openFileList);
-               init_MUTEX(&ret_buf->tconSem);
+               INIT_LIST_HEAD(&ret_buf->tcon_list);
 #ifdef CONFIG_CIFS_STATS
                spin_lock_init(&ret_buf->stat_lock);
 #endif
-               write_unlock(&GlobalSMBSeslock);
        }
        return ret_buf;
 }
@@ -133,10 +127,7 @@ tconInfoFree(struct cifsTconInfo *buf_to_free)
                cFYI(1, ("Null buffer passed to tconInfoFree"));
                return;
        }
-       write_lock(&GlobalSMBSeslock);
        atomic_dec(&tconInfoAllocCount);
-       list_del(&buf_to_free->cifsConnectionList);
-       write_unlock(&GlobalSMBSeslock);
        kfree(buf_to_free->nativeFileSystem);
        kfree(buf_to_free);
 }
@@ -350,9 +341,9 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
                                if (current->fsuid != treeCon->ses->linux_uid) {
                                        cFYI(1, ("Multiuser mode and UID "
                                                 "did not match tcon uid"));
-                                       read_lock(&GlobalSMBSeslock);
-                                       list_for_each(temp_item, &GlobalSMBSessionList) {
-                                               ses = list_entry(temp_item, struct cifsSesInfo, cifsSessionList);
+                                       read_lock(&cifs_tcp_ses_lock);
+                                       list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) {
+                                               ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list);
                                                if (ses->linux_uid == current->fsuid) {
                                                        if (ses->server == treeCon->ses->server) {
                                                                cFYI(1, ("found matching uid substitute right smb_uid"));
@@ -364,7 +355,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
                                                        }
                                                }
                                        }
-                                       read_unlock(&GlobalSMBSeslock);
+                                       read_unlock(&cifs_tcp_ses_lock);
                                }
                        }
                }
@@ -497,9 +488,10 @@ bool
 is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
 {
        struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf;
-       struct list_head *tmp;
-       struct list_head *tmp1;
+       struct list_head *tmp, *tmp1, *tmp2;
+       struct cifsSesInfo *ses;
        struct cifsTconInfo *tcon;
+       struct cifsInodeInfo *pCifsInode;
        struct cifsFileInfo *netfile;
 
        cFYI(1, ("Checking for oplock break or dnotify response"));
@@ -554,42 +546,42 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
                return false;
 
        /* look up tcon based on tid & uid */
-       read_lock(&GlobalSMBSeslock);
-       list_for_each(tmp, &GlobalTreeConnectionList) {
-               tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
-               if ((tcon->tid == buf->Tid) && (srv == tcon->ses->server)) {
+       read_lock(&cifs_tcp_ses_lock);
+       list_for_each(tmp, &srv->smb_ses_list) {
+               ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
+               list_for_each(tmp1, &ses->tcon_list) {
+                       tcon = list_entry(tmp1, struct cifsTconInfo, tcon_list);
+                       if (tcon->tid != buf->Tid)
+                               continue;
+
                        cifs_stats_inc(&tcon->num_oplock_brks);
-                       list_for_each(tmp1, &tcon->openFileList) {
-                               netfile = list_entry(tmp1, struct cifsFileInfo,
+                       list_for_each(tmp2, &tcon->openFileList) {
+                               netfile = list_entry(tmp2, struct cifsFileInfo,
                                                     tlist);
-                               if (pSMB->Fid == netfile->netfid) {
-                                       struct cifsInodeInfo *pCifsInode;
-                                       read_unlock(&GlobalSMBSeslock);
-                                       cFYI(1,
-                                           ("file id match, oplock break"));
-                                       pCifsInode =
-                                               CIFS_I(netfile->pInode);
-                                       pCifsInode->clientCanCacheAll = false;
-                                       if (pSMB->OplockLevel == 0)
-                                               pCifsInode->clientCanCacheRead
-                                                       = false;
-                                       pCifsInode->oplockPending = true;
-                                       AllocOplockQEntry(netfile->pInode,
-                                                         netfile->netfid,
-                                                         tcon);
-                                       cFYI(1,
-                                           ("about to wake up oplock thread"));
-                                       if (oplockThread)
-                                           wake_up_process(oplockThread);
-                                       return true;
-                               }
+                               if (pSMB->Fid != netfile->netfid)
+                                       continue;
+
+                               read_unlock(&cifs_tcp_ses_lock);
+                               cFYI(1, ("file id match, oplock break"));
+                               pCifsInode = CIFS_I(netfile->pInode);
+                               pCifsInode->clientCanCacheAll = false;
+                               if (pSMB->OplockLevel == 0)
+                                       pCifsInode->clientCanCacheRead = false;
+                               pCifsInode->oplockPending = true;
+                               AllocOplockQEntry(netfile->pInode,
+                                                 netfile->netfid, tcon);
+                               cFYI(1, ("about to wake up oplock thread"));
+                               if (oplockThread)
+                                       wake_up_process(oplockThread);
+
+                               return true;
                        }
-                       read_unlock(&GlobalSMBSeslock);
+                       read_unlock(&cifs_tcp_ses_lock);
                        cFYI(1, ("No matching file for oplock break"));
                        return true;
                }
        }
-       read_unlock(&GlobalSMBSeslock);
+       read_unlock(&cifs_tcp_ses_lock);
        cFYI(1, ("Can not process oplock break for non-existent connection"));
        return true;
 }