Merge branch 'master' into next
authorJames Morris <jmorris@namei.org>
Tue, 18 Nov 2008 07:52:37 +0000 (18:52 +1100)
committerJames Morris <jmorris@namei.org>
Tue, 18 Nov 2008 07:52:37 +0000 (18:52 +1100)
Conflicts:
fs/cifs/misc.c

Merge to resolve above, per the patch below.

Signed-off-by: James Morris <jmorris@namei.org>
diff --cc fs/cifs/misc.c
index ec36410,addd1dc..0000000
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@@ -347,13 -338,13 +338,13 @@@ header_assemble(struct smb_hdr *buffer
   /*  BB Add support for establishing new tCon and SMB Session  */
   /*      with userid/password pairs found on the smb session   */
   /* for other target tcp/ip addresses  BB    */
 - if (current->fsuid != treeCon->ses->linux_uid) {
 + 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->linux_uid == current_fsuid()) {
   if (ses->server == treeCon->ses->server) {
   cFYI(1, ("found matching uid substitute right smb_uid"));
   buffer->Uid = ses->Suid;

1  2 
Documentation/kernel-parameters.txt
fs/cifs/connect.c
fs/cifs/misc.c
kernel/Makefile
kernel/exit.c
kernel/fork.c
net/core/scm.c

@@@ -629,7 -629,7 +629,7 @@@ and is between 256 and 4096 characters
  
        digiepca=       [HW,SERIAL]
                        See drivers/char/README.epca and
-                       Documentation/digiepca.txt.
+                       Documentation/serial/digiepca.txt.
  
        disable_mtrr_cleanup [X86]
        enable_mtrr_cleanup [X86]
                        See header of drivers/scsi/fdomain.c.
  
        floppy=         [HW]
-                       See Documentation/floppy.txt.
+                       See Documentation/blockdev/floppy.txt.
  
        force_pal_cache_flush
                        [IA-64] Avoid check_sal_cache_flush which may hang on
                        the same attribute, the last one is used.
  
        load_ramdisk=   [RAM] List of ramdisks to load from floppy
-                       See Documentation/ramdisk.txt.
+                       See Documentation/blockdev/ramdisk.txt.
  
        lockd.nlm_grace_period=P  [NFS] Assign grace period.
                        Format: <integer>
                        instruction doesn't work correctly and not to
                        use it.
  
 +      no_file_caps    Tells the kernel not to honor file capabilities.  The
 +                      only way then for a file to be executed with privilege
 +                      is to be setuid root or executed by root.
 +
        nohalt          [IA-64] Tells the kernel not to use the power saving
                        function PAL_HALT_LIGHT when idle. This increases
                        power-consumption. On the positive side, it reduces
  
        pcd.            [PARIDE]
                        See header of drivers/block/paride/pcd.c.
-                       See also Documentation/paride.txt.
+                       See also Documentation/blockdev/paride.txt.
  
        pci=option[,option...]  [PCI] various PCI subsystem options:
                off             [X86] don't probe for the PCI bus
        pcmv=           [HW,PCMCIA] BadgePAD 4
  
        pd.             [PARIDE]
-                       See Documentation/paride.txt.
+                       See Documentation/blockdev/paride.txt.
  
        pdcchassis=     [PARISC,HW] Disable/Enable PDC Chassis Status codes at
                        boot time.
                        See arch/parisc/kernel/pdc_chassis.c
  
        pf.             [PARIDE]
-                       See Documentation/paride.txt.
+                       See Documentation/blockdev/paride.txt.
  
        pg.             [PARIDE]
-                       See Documentation/paride.txt.
+                       See Documentation/blockdev/paride.txt.
  
        pirq=           [SMP,APIC] Manual mp-table setup
                        See Documentation/x86/i386/IO-APIC.txt.
  
        prompt_ramdisk= [RAM] List of RAM disks to prompt for floppy disk
                        before loading.
-                       See Documentation/ramdisk.txt.
+                       See Documentation/blockdev/ramdisk.txt.
  
        psmouse.proto=  [HW,MOUSE] Highest PS2 mouse protocol extension to
                        probe for; one of (bare|imps|exps|lifebook|any).
                        <io>,<mss_io>,<mss_irq>,<mss_dma>,<mpu_io>,<mpu_irq>
  
        pt.             [PARIDE]
-                       See Documentation/paride.txt.
+                       See Documentation/blockdev/paride.txt.
  
        pty.legacy_count=
                        [KNL] Number of legacy pty's. Overwrites compiled-in
                        See Documentation/md.txt.
  
        ramdisk_blocksize=      [RAM]
-                       See Documentation/ramdisk.txt.
+                       See Documentation/blockdev/ramdisk.txt.
  
        ramdisk_size=   [RAM] Sizes of RAM disks in kilobytes
-                       See Documentation/ramdisk.txt.
+                       See Documentation/blockdev/ramdisk.txt.
  
        rcupdate.blimit=        [KNL,BOOT]
                        Set maximum number of finished RCU callbacks to process
                        See Documentation/sonypi.txt
  
        specialix=      [HW,SERIAL] Specialix multi-serial port adapter
-                       See Documentation/specialix.txt.
+                       See Documentation/serial/specialix.txt.
  
        spia_io_base=   [HW,MTD]
        spia_fio_base=
diff --combined fs/cifs/connect.c
@@@ -124,7 -124,7 +124,7 @@@ static in
  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;
  
        /* 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) {
        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 +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 +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);
        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);
                        }
                }
                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);
        }
        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);
@@@ -833,8 -836,8 +836,8 @@@ cifs_parse_mount_options(char *options
        /* null target name indicates to use *SMBSERVR default called name
           if we end up sending RFC1001 session initialize */
        vol->target_rfc1001_name[0] = 0;
 -      vol->linux_uid = current->uid;  /* current->euid instead? */
 -      vol->linux_gid = current->gid;
 +      vol->linux_uid = current_uid();  /* use current_euid() instead? */
 +      vol->linux_gid = current_gid();
        vol->dir_mode = S_IRWXUGO;
        /* 2767 perms indicate mandatory locking support */
        vol->file_mode = (S_IRWXUGO | S_ISGID) & (~S_IXGRP);
        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 +1943,90 @@@ convert_delimiter(char *path, char deli
        }
  }
  
- 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 +2035,12 @@@ cifs_mount(struct super_block *sb, stru
  {
        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;
  
  
  /* 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;
  
        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) {
                }
        }
  
-       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);
                } 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);
                        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);
        } 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) {
        /* 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 +3539,7 @@@ CIFSTCon(unsigned int xid, struct cifsS
        /* 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);
  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 +3737,7 @@@ int cifs_setup_session(unsigned int xid
                cFYI(1, ("CIFS Session Established successfully"));
                        spin_lock(&GlobalMid_Lock);
                        pSesInfo->status = CifsGood;
+                       pSesInfo->need_reconnect = false;
                        spin_unlock(&GlobalMid_Lock);
        }
  
diff --combined fs/cifs/misc.c
@@@ -75,12 -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 +93,7 @@@ sesInfoFree(struct cifsSesInfo *buf_to_
                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 +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 +127,7 @@@ tconInfoFree(struct cifsTconInfo *buf_t
                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);
  }
@@@ -347,13 -338,13 +338,13 @@@ header_assemble(struct smb_hdr *buffer
                /*  BB Add support for establishing new tCon and SMB Session  */
                /*      with userid/password pairs found on the smb session   */
                /*      for other target tcp/ip addresses               BB    */
 -                              if (current->fsuid != treeCon->ses->linux_uid) {
 +                              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->linux_uid == current_fsuid()) {
                                                        if (ses->server == treeCon->ses->server) {
                                                                cFYI(1, ("found matching uid substitute right smb_uid"));
                                                                buffer->Uid = ses->Suid;
                                                        }
                                                }
                                        }
-                                       read_unlock(&GlobalSMBSeslock);
+                                       read_unlock(&cifs_tcp_ses_lock);
                                }
                        }
                }
@@@ -497,9 -488,10 +488,10 @@@ boo
  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"));
                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;
  }
diff --combined kernel/Makefile
@@@ -9,10 -9,8 +9,8 @@@ obj-y     = sched.o fork.o exec_domain.
            rcupdate.o extable.o params.o posix-timers.o \
            kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
            hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
 -          notifier.o ksysfs.o pm_qos_params.o sched_clock.o
 +          notifier.o ksysfs.o pm_qos_params.o sched_clock.o cred.o
  
- CFLAGS_REMOVE_sched.o = -mno-spe
  ifdef CONFIG_FUNCTION_TRACER
  # Do not trace debug files and internal ftrace files
  CFLAGS_REMOVE_lockdep.o = -pg
@@@ -21,7 -19,7 +19,7 @@@ CFLAGS_REMOVE_mutex-debug.o = -p
  CFLAGS_REMOVE_rtmutex-debug.o = -pg
  CFLAGS_REMOVE_cgroup-debug.o = -pg
  CFLAGS_REMOVE_sched_clock.o = -pg
- CFLAGS_REMOVE_sched.o = -mno-spe -pg
+ CFLAGS_REMOVE_sched.o = -pg
  endif
  
  obj-$(CONFIG_FREEZER) += freezer.o
diff --combined kernel/exit.c
  #include <linux/cn_proc.h>
  #include <linux/mutex.h>
  #include <linux/futex.h>
- #include <linux/compat.h>
  #include <linux/pipe_fs_i.h>
  #include <linux/audit.h> /* for audit_free() */
  #include <linux/resource.h>
  #include <linux/blkdev.h>
  #include <linux/task_io_accounting_ops.h>
  #include <linux/tracehook.h>
 +#include <linux/init_task.h>
  #include <trace/sched.h>
  
  #include <asm/uaccess.h>
  #include <asm/unistd.h>
  #include <asm/pgtable.h>
  #include <asm/mmu_context.h>
 +#include "cred-internals.h"
  
  static void exit_mm(struct task_struct * tsk);
  
@@@ -167,10 -164,7 +166,10 @@@ void release_task(struct task_struct * 
        int zap_leader;
  repeat:
        tracehook_prepare_release_task(p);
 -      atomic_dec(&p->user->processes);
 +      /* don't need to get the RCU readlock here - the process is dead and
 +       * can't be modifying its own credentials */
 +      atomic_dec(&__task_cred(p)->user->processes);
 +
        proc_flush_task(p);
        write_lock_irq(&tasklist_lock);
        tracehook_finish_release_task(p);
@@@ -345,12 -339,12 +344,12 @@@ static void reparent_to_kthreadd(void
        /* cpus_allowed? */
        /* rt_priority? */
        /* signals? */
 -      security_task_reparent_to_init(current);
        memcpy(current->signal->rlim, init_task.signal->rlim,
               sizeof(current->signal->rlim));
 -      atomic_inc(&(INIT_USER->__count));
 +
 +      atomic_inc(&init_cred.usage);
 +      commit_creds(&init_cred);
        write_unlock_irq(&tasklist_lock);
 -      switch_uid(INIT_USER);
  }
  
  void __set_special_pids(struct pid *pid)
@@@ -1064,14 -1058,6 +1063,6 @@@ NORET_TYPE void do_exit(long code
                exit_itimers(tsk->signal);
        }
        acct_collect(code, group_dead);
- #ifdef CONFIG_FUTEX
-       if (unlikely(tsk->robust_list))
-               exit_robust_list(tsk);
- #ifdef CONFIG_COMPAT
-       if (unlikely(tsk->compat_robust_list))
-               compat_exit_robust_list(tsk);
- #endif
- #endif
        if (group_dead)
                tty_audit_exit();
        if (unlikely(tsk->audit_context))
        check_stack_usage();
        exit_thread();
        cgroup_exit(tsk, 1);
 -      exit_keys(tsk);
  
        if (group_dead && tsk->signal->leader)
                disassociate_ctty(1);
@@@ -1276,12 -1263,12 +1267,12 @@@ static int wait_task_zombie(struct task
        unsigned long state;
        int retval, status, traced;
        pid_t pid = task_pid_vnr(p);
 +      uid_t uid = __task_cred(p)->uid;
  
        if (!likely(options & WEXITED))
                return 0;
  
        if (unlikely(options & WNOWAIT)) {
 -              uid_t uid = p->uid;
                int exit_code = p->exit_code;
                int why, status;
  
        if (!retval && infop)
                retval = put_user(pid, &infop->si_pid);
        if (!retval && infop)
 -              retval = put_user(p->uid, &infop->si_uid);
 +              retval = put_user(uid, &infop->si_uid);
        if (!retval)
                retval = pid;
  
@@@ -1467,8 -1454,7 +1458,8 @@@ static int wait_task_stopped(int ptrace
        if (!unlikely(options & WNOWAIT))
                p->exit_code = 0;
  
 -      uid = p->uid;
 +      /* don't need the RCU readlock here as we're holding a spinlock */
 +      uid = __task_cred(p)->uid;
  unlock_sig:
        spin_unlock_irq(&p->sighand->siglock);
        if (!exit_code)
@@@ -1542,10 -1528,10 +1533,10 @@@ static int wait_task_continued(struct t
        }
        if (!unlikely(options & WNOWAIT))
                p->signal->flags &= ~SIGNAL_STOP_CONTINUED;
 +      uid = __task_cred(p)->uid;
        spin_unlock_irq(&p->sighand->siglock);
  
        pid = task_pid_vnr(p);
 -      uid = p->uid;
        get_task_struct(p);
        read_unlock(&tasklist_lock);
  
diff --combined kernel/fork.c
@@@ -40,6 -40,7 +40,7 @@@
  #include <linux/jiffies.h>
  #include <linux/tracehook.h>
  #include <linux/futex.h>
+ #include <linux/compat.h>
  #include <linux/task_io_accounting_ops.h>
  #include <linux/rcupdate.h>
  #include <linux/ptrace.h>
@@@ -146,8 -147,9 +147,8 @@@ void __put_task_struct(struct task_stru
        WARN_ON(atomic_read(&tsk->usage));
        WARN_ON(tsk == current);
  
 -      security_task_free(tsk);
 -      free_uid(tsk->user);
 -      put_group_info(tsk->group_info);
 +      put_cred(tsk->real_cred);
 +      put_cred(tsk->cred);
        delayacct_tsk_free(tsk);
  
        if (!profile_handoff_task(tsk))
@@@ -518,6 -520,16 +519,16 @@@ void mm_release(struct task_struct *tsk
  {
        struct completion *vfork_done = tsk->vfork_done;
  
+       /* Get rid of any futexes when releasing the mm */
+ #ifdef CONFIG_FUTEX
+       if (unlikely(tsk->robust_list))
+               exit_robust_list(tsk);
+ #ifdef CONFIG_COMPAT
+       if (unlikely(tsk->compat_robust_list))
+               compat_exit_robust_list(tsk);
+ #endif
+ #endif
        /* Get rid of any cached register state */
        deactivate_mm(tsk, mm);
  
@@@ -803,6 -815,12 +814,6 @@@ static int copy_signal(unsigned long cl
        if (!sig)
                return -ENOMEM;
  
 -      ret = copy_thread_group_keys(tsk);
 -      if (ret < 0) {
 -              kmem_cache_free(signal_cachep, sig);
 -              return ret;
 -      }
 -
        atomic_set(&sig->count, 1);
        atomic_set(&sig->live, 1);
        init_waitqueue_head(&sig->wait_chldexit);
  void __cleanup_signal(struct signal_struct *sig)
  {
        thread_group_cputime_free(sig);
 -      exit_thread_group_keys(sig);
        tty_kref_put(sig->tty);
        kmem_cache_free(signal_cachep, sig);
  }
@@@ -962,16 -981,16 +973,16 @@@ static struct task_struct *copy_process
        DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
  #endif
        retval = -EAGAIN;
 -      if (atomic_read(&p->user->processes) >=
 +      if (atomic_read(&p->real_cred->user->processes) >=
                        p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
                if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
 -                  p->user != current->nsproxy->user_ns->root_user)
 +                  p->real_cred->user != current->nsproxy->user_ns->root_user)
                        goto bad_fork_free;
        }
  
 -      atomic_inc(&p->user->__count);
 -      atomic_inc(&p->user->processes);
 -      get_group_info(p->group_info);
 +      retval = copy_creds(p, clone_flags);
 +      if (retval < 0)
 +              goto bad_fork_free;
  
        /*
         * If multiple threads are within copy_process(), then this check
        do_posix_clock_monotonic_gettime(&p->start_time);
        p->real_start_time = p->start_time;
        monotonic_to_bootbased(&p->real_start_time);
 -#ifdef CONFIG_SECURITY
 -      p->security = NULL;
 -#endif
 -      p->cap_bset = current->cap_bset;
        p->io_context = NULL;
        p->audit_context = NULL;
        cgroup_fork(p);
        /* Perform scheduler related setup. Assign this task to a CPU. */
        sched_fork(p, clone_flags);
  
 -      if ((retval = security_task_alloc(p)))
 -              goto bad_fork_cleanup_policy;
        if ((retval = audit_alloc(p)))
 -              goto bad_fork_cleanup_security;
 +              goto bad_fork_cleanup_policy;
        /* copy all the process information */
        if ((retval = copy_semundo(clone_flags, p)))
                goto bad_fork_cleanup_audit;
                goto bad_fork_cleanup_sighand;
        if ((retval = copy_mm(clone_flags, p)))
                goto bad_fork_cleanup_signal;
 -      if ((retval = copy_keys(clone_flags, p)))
 -              goto bad_fork_cleanup_mm;
        if ((retval = copy_namespaces(clone_flags, p)))
 -              goto bad_fork_cleanup_keys;
 +              goto bad_fork_cleanup_mm;
        if ((retval = copy_io(clone_flags, p)))
                goto bad_fork_cleanup_namespaces;
        retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
@@@ -1251,6 -1278,8 +1262,6 @@@ bad_fork_cleanup_io
        put_io_context(p->io_context);
  bad_fork_cleanup_namespaces:
        exit_task_namespaces(p);
 -bad_fork_cleanup_keys:
 -      exit_keys(p);
  bad_fork_cleanup_mm:
        if (p->mm)
                mmput(p->mm);
@@@ -1266,6 -1295,8 +1277,6 @@@ bad_fork_cleanup_semundo
        exit_sem(p);
  bad_fork_cleanup_audit:
        audit_free(p);
 -bad_fork_cleanup_security:
 -      security_task_free(p);
  bad_fork_cleanup_policy:
  #ifdef CONFIG_NUMA
        mpol_put(p->mempolicy);
@@@ -1278,9 -1309,9 +1289,9 @@@ bad_fork_cleanup_cgroup
  bad_fork_cleanup_put_domain:
        module_put(task_thread_info(p)->exec_domain->module);
  bad_fork_cleanup_count:
 -      put_group_info(p->group_info);
 -      atomic_dec(&p->user->processes);
 -      free_uid(p->user);
 +      atomic_dec(&p->cred->user->processes);
 +      put_cred(p->real_cred);
 +      put_cred(p->cred);
  bad_fork_free:
        free_task(p);
  fork_out:
diff --combined net/core/scm.c
  
  static __inline__ int scm_check_creds(struct ucred *creds)
  {
 +      const struct cred *cred = current_cred();
 +
        if ((creds->pid == task_tgid_vnr(current) || capable(CAP_SYS_ADMIN)) &&
 -          ((creds->uid == current->uid || creds->uid == current->euid ||
 -            creds->uid == current->suid) || capable(CAP_SETUID)) &&
 -          ((creds->gid == current->gid || creds->gid == current->egid ||
 -            creds->gid == current->sgid) || capable(CAP_SETGID))) {
 +          ((creds->uid == cred->uid   || creds->uid == cred->euid ||
 +            creds->uid == cred->suid) || capable(CAP_SETUID)) &&
 +          ((creds->gid == cred->gid   || creds->gid == cred->egid ||
 +            creds->gid == cred->sgid) || capable(CAP_SETGID))) {
               return 0;
        }
        return -EPERM;
@@@ -77,7 -75,6 +77,6 @@@ static int scm_fp_copy(struct cmsghdr *
                if (!fpl)
                        return -ENOMEM;
                *fplp = fpl;
-               INIT_LIST_HEAD(&fpl->list);
                fpl->count = 0;
        }
        fpp = &fpl->fp[fpl->count];
@@@ -303,7 -300,6 +302,6 @@@ struct scm_fp_list *scm_fp_dup(struct s
  
        new_fpl = kmalloc(sizeof(*fpl), GFP_KERNEL);
        if (new_fpl) {
-               INIT_LIST_HEAD(&new_fpl->list);
                for (i=fpl->count-1; i>=0; i--)
                        get_file(fpl->fp[i]);
                memcpy(new_fpl, fpl, sizeof(*fpl));