[PATCH 0/5] cifs: fix oopses and mem corruption with concurrent mount/umount (try #5) - Kernel

This is a discussion on [PATCH 0/5] cifs: fix oopses and mem corruption with concurrent mount/umount (try #5) - Kernel ; This patchset is intended to fix the oopses, memory corruption and mount failures when using the reproducer detailed here: https://bugzilla.samba.org/show_bug.cgi?id=5720 This is the fifth attempt at this. Since the fourth attempt, I've changed the socket handling cleanup patch to also ...

+ Reply to Thread
Results 1 to 6 of 6

Thread: [PATCH 0/5] cifs: fix oopses and mem corruption with concurrent mount/umount (try #5)

  1. [PATCH 0/5] cifs: fix oopses and mem corruption with concurrent mount/umount (try #5)

    This patchset is intended to fix the oopses, memory corruption and mount
    failures when using the reproducer detailed here:

    https://bugzilla.samba.org/show_bug.cgi?id=5720

    This is the fifth attempt at this. Since the fourth attempt, I've
    changed the socket handling cleanup patch to also fix some problems with
    IPv6 mounts, and have added a patch to change the tcon->tidStatus from
    an enum to a bitfield.

    The other main difference is that I've moved from the more granular
    locking scheme in the earlier patches to one where all of the new lists
    and refcounts are protected by the cifs_tcp_session_lock. That lock has
    also been converted to a r/w spinlock.

    The patch is a little larger than it really needs to be simply because
    I've also taken this opportunity to move areas of the code that I was
    touching to more closely resemble standard kernel coding style. There
    are still style problems with it -- several lines are over 80 chars.
    Fixing them will mean restructuring the code further however, and there
    are already concerns about the size of this patchset. I've decided to
    draw the line here...

    This patchset is based on Steve French's cifs-2.6 git tree and should
    apply cleanly to its current head.

    There's still some remaining cleanup work that can be done here. The
    cifs_mount code could stand to be broken up into smaller functions.
    cifs_debug_data_proc_show could also stand to be reorganized to better
    reflect the heirarchy of server->session->tcon. It also needs to be
    updated to handle IPv6 addresses correctly. Those changes are probably
    more suitable in follow-on patches. I'd like to see these go in before I
    spend time working on them.

    I've been able to run the reproducer in the above BZ overnight on this
    patchset. Without it, it usually crashes within a few minutes.

    Jeff Layton (5):
    cifs: clean up server protocol handling for TCP_Server_Info
    cifs: convert tcon->tidStatus to a bitfield
    cifs: disable sharing session and tcon and add new TCP sharing code
    cifs: reinstate sharing of SMB sessions sans races
    cifs: reinstate sharing of tree connections

    fs/cifs/cifs_debug.c | 277 +++++++++++++++++--------------
    fs/cifs/cifs_spnego.c | 4 +-
    fs/cifs/cifsfs.c | 37 +++--
    fs/cifs/cifsglob.h | 33 ++--
    fs/cifs/cifssmb.c | 68 ++------
    fs/cifs/connect.c | 439 ++++++++++++++++++++++++-------------------------
    fs/cifs/file.c | 2 +-
    fs/cifs/misc.c | 89 +++++------
    8 files changed, 464 insertions(+), 485 deletions(-)

    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  2. [PATCH 1/5] cifs: clean up server protocol handling

    We're currently declaring both a sockaddr_in and sockaddr6_in on the
    stack, but we really only need storage for one of them. Declare a
    sockaddr struct and cast it to the proper type. Also, eliminate the
    protocolType field in the TCP_Server_Info struct. It's redundant since
    we have a sa_family field in the sockaddr anyway.

    We may need to revisit this if SCTP is ever implemented, but for now
    this will simplify the code.

    CIFS over IPv6 also has a number of problems currently. This fixes all
    of them that I found. Eventually, it would be nice to move more of the
    code to be protocol independent, but this is a start.

    Signed-off-by: Jeff Layton
    ---
    fs/cifs/cifs_spnego.c | 4 +-
    fs/cifs/cifsglob.h | 8 -------
    fs/cifs/connect.c | 55 +++++++++++++++++++++++++-----------------------
    3 files changed, 31 insertions(+), 36 deletions(-)

    diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c
    index 9310d0c..f8eb14a 100644
    --- a/fs/cifs/cifs_spnego.c
    +++ b/fs/cifs/cifs_spnego.c
    @@ -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
    diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
    index 1fc051e..58b8d5c 100644
    --- a/fs/cifs/cifsglob.h
    +++ b/fs/cifs/cifsglob.h
    @@ -84,13 +84,6 @@ enum securityEnum {
    MSKerberos, /* MS Kerberos via SPNEGO */
    };

    -enum protocolEnum {
    - IPV4 = 0,
    - IPV6,
    - SCTP
    - /* Netbios frames protocol not supported at this time */
    -};
    -
    struct mac_key {
    unsigned int len;
    union {
    @@ -137,7 +130,6 @@ struct TCP_Server_Info {
    void *Server_NlsInfo; /* BB - placeholder for future NLS info */
    unsigned short server_codepage; /* codepage for the server */
    unsigned long ip_address; /* IP addr for the server if known */
    - enum protocolEnum protocolType;
    char versionMajor;
    char versionMinor;
    bool svlocal:1; /* local server or remote */
    diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
    index c682be8..22357ec 100644
    --- a/fs/cifs/connect.c
    +++ b/fs/cifs/connect.c
    @@ -193,7 +193,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 {
    @@ -1897,10 +1897,10 @@ 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;
    @@ -1911,6 +1911,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;
    @@ -1933,16 +1934,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) {
    @@ -1982,14 +1983,14 @@ 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,
    + if (addr.sa_family == 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) {
    + else if (addr.sa_family == AF_INET6) {
    cFYI(1, ("looking for ipv6 address"));
    existingCifsSes = cifs_find_tcp_session(NULL /* no ipv4 addr */,
    - &sin_server6.sin6_addr,
    + &sin_server6->sin6_addr,
    volume_info.username, &srvTcp);
    } else {
    rc = -EINVAL;
    @@ -1999,24 +2000,23 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
    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) {
    + 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);
    @@ -2031,12 +2031,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);
    @@ -2088,7 +2091,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
    else {
    pSesInfo->server = srvTcp;
    sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
    - NIPQUAD(sin_server.sin_addr.s_addr));
    + NIPQUAD(sin_server->sin_addr.s_addr));
    }

    if (!rc) {
    @@ -2207,7 +2210,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
    "mount option supported"));

    tcon =
    - find_unc(sin_server.sin_addr.s_addr, volume_info.UNC,
    + find_unc(sin_server->sin_addr.s_addr, volume_info.UNC,
    volume_info.username);
    if (tcon) {
    cFYI(1, ("Found match on UNC path"));
    --
    1.5.5.1

    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  3. [PATCH 5/5] cifs: reinstate sharing of tree connections

    Use a similar approach to the SMB session sharing. Add a list of tcons
    attached to each SMB session. Move the refcount to non-atomic. Protect
    all of the above with the cifs_tcp_session_lock. Add functions to
    properly find and put references to the tcons.

    Signed-off-by: Jeff Layton
    ---
    fs/cifs/cifs_debug.c | 236 +++++++++++++++++++++++++++-----------------------
    fs/cifs/cifsfs.c | 8 +-
    fs/cifs/cifsglob.h | 7 +-
    fs/cifs/cifssmb.c | 34 +------
    fs/cifs/connect.c | 93 ++++++++++++++------
    fs/cifs/misc.c | 73 +++++++--------
    6 files changed, 239 insertions(+), 212 deletions(-)

    diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
    index cd4793f..ae4efb6 100644
    --- a/fs/cifs/cifs_debug.c
    +++ b/fs/cifs/cifs_debug.c
    @@ -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, *tmp2, *tmp3;
    + 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"
    @@ -123,8 +124,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)

    i = 0;
    read_lock(&cifs_tcp_session_lock);
    - list_for_each(tmp, &cifs_tcp_session_list) {
    - server = list_entry(tmp, struct TCP_Server_Info,
    + list_for_each(tmp1, &cifs_tcp_session_list) {
    + server = list_entry(tmp1, struct TCP_Server_Info,
    tcp_session_list);
    i++;
    list_for_each(tmp2, &server->smb_session_list) {
    @@ -133,12 +134,12 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
    if ((ses->serverDomain == NULL) ||
    (ses->serverOS == NULL) ||
    (ses->serverNOS == NULL)) {
    - seq_printf(m, "\nentry for %s not fully "
    - "displayed\n\t", ses->serverName);
    + 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 Mounts: %d OS:"
    - " %s \n\tNOS: %s\tCapability: 0x%x\n\tSMB"
    + "\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,
    ses->refcount, ses->serverOS, ses->serverNOS,
    @@ -155,14 +156,44 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
    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->refcount);
    + if (tcon->nativeFileSystem) {
    + seq_printf(m, "Type: %s ",
    + tcon->nativeFileSystem);
    + }
    + seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x"
    + "\nPathComponentMax: %d Status: 0x%lx",
    + 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 (!test_bit(CIFS_CONNECTED, &tcon->tidStatus))
    + seq_puts(m, "\tDISCONNECTED ");
    + seq_putc(m, '\n');
    + }
    +
    + seq_puts(m, "\n\tMIDs:\n");

    spin_lock(&GlobalMid_Lock);
    list_for_each(tmp3, &server->pending_mid_q) {
    - mid_entry = list_entry(tmp3, struct
    - mid_q_entry,
    + 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,
    @@ -176,41 +207,6 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
    read_unlock(&cifs_tcp_session_lock);
    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: 0x%lx",
    - 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 (!test_bit(CIFS_CONNECTED, &tcon->tidStatus))
    - seq_puts(m, "\tDISCONNECTED ");
    - }
    - read_unlock(&GlobalSMBSeslock);
    -
    - seq_putc(m, '\n');
    -
    /* BB add code to dump additional info such as TCP session info now */
    return 0;
    }
    @@ -234,7 +230,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 +240,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_session_lock);
    + list_for_each(tmp1, &cifs_tcp_session_list) {
    + server = list_entry(tmp1, struct TCP_Server_Info,
    + tcp_session_list);
    + list_for_each(tmp2, &server->smb_session_list) {
    + ses = list_entry(tmp2, struct cifsSesInfo,
    + smb_session_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_session_lock);
    }

    return count;
    @@ -277,7 +284,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 +315,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 (!test_bit(CIFS_CONNECTED, &tcon->tidStatus))
    - 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_session_lock);
    + list_for_each(tmp1, &cifs_tcp_session_list) {
    + server = list_entry(tmp1, struct TCP_Server_Info,
    + tcp_session_list);
    + list_for_each(tmp2, &server->smb_session_list) {
    + ses = list_entry(tmp2, struct cifsSesInfo,
    + smb_session_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 (!test_bit(CIFS_CONNECTED, &tcon->tidStatus))
    + 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_session_lock);

    seq_putc(m, '\n');
    return 0;
    diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
    index f930c90..ee9d3b2 100644
    --- a/fs/cifs/cifsfs.c
    +++ b/fs/cifs/cifsfs.c
    @@ -520,10 +520,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_session_lock);
    + if (tcon->refcount == 1)
    set_bit(CIFS_CLOSING, &tcon->tidStatus);
    - up(&tcon->tconSem);
    + read_unlock(&cifs_tcp_session_lock);

    /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */
    /* cancel_notify_requests(tcon); */
    @@ -1066,7 +1067,6 @@ init_cifs(void)
    int rc = 0;
    cifs_proc_init();
    /* INIT_LIST_HEAD(&GlobalServerList);*/ /* BB not implemented yet */
    - INIT_LIST_HEAD(&GlobalTreeConnectionList);
    INIT_LIST_HEAD(&GlobalOplock_Q);
    INIT_LIST_HEAD(&cifs_tcp_session_list);
    #ifdef CONFIG_CIFS_EXPERIMENTAL
    diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
    index e7e59eb..405d06d 100644
    --- a/fs/cifs/cifsglob.h
    +++ b/fs/cifs/cifsglob.h
    @@ -193,6 +193,7 @@ struct cifsUidInfo {
    */
    struct cifsSesInfo {
    struct list_head smb_session_list;
    + struct list_head tcon_list;
    struct semaphore sesSem;
    #if 0
    struct cifsUidInfo *uidInfo; /* pointer to user info */
    @@ -229,16 +230,15 @@ struct cifsSesInfo {
    * session
    */
    struct cifsTconInfo {
    - struct list_head cifsConnectionList;
    + struct list_head tcon_list;
    + int refcount;
    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 */
    unsigned long tidStatus;
    - atomic_t useCount; /* how many explicit/implicit mounts to share */
    #ifdef CONFIG_CIFS_STATS
    atomic_t num_smbs_sent;
    atomic_t num_writes;
    @@ -599,7 +599,6 @@ require use of the stronger protocol */
    GLOBAL_EXTERN struct smbUidInfo *GlobalUidList[UID_HASH];

    /* GLOBAL_EXTERN struct list_head GlobalServerList; BB not implemented yet */
    -GLOBAL_EXTERN struct list_head GlobalTreeConnectionList;
    GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */
    extern struct list_head cifs_tcp_session_list;
    extern rwlock_t cifs_tcp_session_lock;
    diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
    index e761b65..9f198a3 100644
    --- a/fs/cifs/cifssmb.c
    +++ b/fs/cifs/cifssmb.c
    @@ -744,50 +744,26 @@ 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;
    - }

    /* No need to return error on this operation if tid invalidated and
    closed on server already e.g. due to tcp session crashing */
    - if (!test_bit(CIFS_CONNECTED, &tcon->tidStatus)) {
    - up(&tcon->tconSem);
    + if (!test_bit(CIFS_CONNECTED, &tcon->tidStatus))
    return 0;
    - }

    - if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) {
    - up(&tcon->tconSem);
    + if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
    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;

    diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
    index 63505f1..3273f46 100644
    --- a/fs/cifs/connect.c
    +++ b/fs/cifs/connect.c
    @@ -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;
    @@ -149,13 +149,12 @@ cifs_reconnect(struct TCP_Server_Info *server)
    ses = list_entry(tmp, struct cifsSesInfo, smb_session_list);
    ses->status = CifsNeedReconnect;
    ses->ipc_tid = 0;
    - }
    - read_unlock(&cifs_tcp_session_lock);
    - list_for_each(tmp, &GlobalTreeConnectionList) {
    - tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
    - if ((tcon->ses) && (tcon->ses->server == server))
    + list_for_each(tmp2, &ses->tcon_list) {
    + tcon = list_entry(tmp2, struct cifsTconInfo, tcon_list);
    clear_bit(CIFS_CONNECTED, &tcon->tidStatus);
    + }
    }
    + read_unlock(&cifs_tcp_session_lock);
    /* do not want to be sending data on a socket we are freeing */
    down(&server->tcpSem);
    if (server->ssocket) {
    @@ -1452,6 +1451,52 @@ cifs_put_smb_session(struct cifsSesInfo *ses)
    cifs_put_tcp_session(server);
    }

    +static struct cifsTconInfo *
    +cifs_find_tcon(struct cifsSesInfo *ses, const char *unc)
    +{
    + struct list_head *tmp;
    + struct cifsTconInfo *tcon;
    +
    + write_lock(&cifs_tcp_session_lock);
    + list_for_each(tmp, &ses->tcon_list) {
    + tcon = list_entry(tmp, struct cifsTconInfo, tcon_list);
    + if (test_bit(CIFS_CLOSING, &tcon->tidStatus))
    + continue;
    + if (strncmp(tcon->treeName, unc, MAX_TREE_SIZE))
    + continue;
    +
    + ++tcon->refcount;
    + write_unlock(&cifs_tcp_session_lock);
    + return tcon;
    + }
    + write_unlock(&cifs_tcp_session_lock);
    + return NULL;
    +}
    +
    +static void
    +cifs_put_tcon(struct cifsTconInfo *tcon)
    +{
    + int xid;
    + struct cifsSesInfo *ses = tcon->ses;
    +
    + write_lock(&cifs_tcp_session_lock);
    + if (--tcon->refcount > 0) {
    + write_unlock(&cifs_tcp_session_lock);
    + return;
    + }
    +
    + list_del_init(&tcon->tcon_list);
    + write_unlock(&cifs_tcp_session_lock);
    +
    + xid = GetXid();
    + CIFSSMBTDis(xid, tcon);
    + _FreeXid(xid);
    +
    + DeleteTconOplockQEntries(tcon);
    + tconInfoFree(tcon);
    + cifs_put_smb_session(ses);
    +}
    +
    int
    get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
    const struct nls_table *nls_codepage, unsigned int *pnum_referrals,
    @@ -2209,8 +2254,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
    cERROR(1, ("mount option dynperm ignored if cifsacl "
    "mount option supported"));

    + tcon = cifs_find_tcon(pSesInfo, volume_info.UNC);
    if (tcon) {
    cFYI(1, ("Found match on UNC path"));
    + /* existing tcon already has a reference */
    + cifs_put_smb_session(pSesInfo);
    +
    /* 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
    @@ -2260,6 +2309,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
    tcon->retry = volume_info.retry;
    tcon->nocase = volume_info.nocase;
    tcon->seal = volume_info.seal;
    + tcon->ses = pSesInfo;
    + ++tcon->refcount;
    + write_lock(&cifs_tcp_session_lock);
    + list_add(&tcon->tcon_list,
    + &pSesInfo->tcon_list);
    + write_unlock(&cifs_tcp_session_lock);
    }
    }
    }
    @@ -2279,17 +2334,13 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
    /* If find_unc succeeded then rc == 0 so we can not end */
    /* up accidently freeing someone elses tcon struct */
    if (tcon)
    - tconInfoFree(tcon);
    -
    - /* should also end up putting our tcp session ref if needed */
    - if (pSesInfo)
    + cifs_put_tcon(tcon);
    + else if (pSesInfo)
    cifs_put_smb_session(pSesInfo);
    else
    cifs_put_tcp_session(srvTcp);
    } else {
    - atomic_inc(&tcon->useCount);
    cifs_sb->tcon = tcon;
    - tcon->ses = pSesInfo;

    /* do not care if following two calls succeed - informational */
    if (!tcon->ipc) {
    @@ -3564,23 +3615,10 @@ 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);
    - cifs_put_smb_session(ses);
    - }
    + if (cifs_sb->tcon)
    + cifs_put_tcon(cifs_sb->tcon);

    cifs_sb->tcon = NULL;
    tmp = cifs_sb->prepath;
    @@ -3588,7 +3626,6 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
    cifs_sb->prepath = NULL;
    kfree(tmp);

    - FreeXid(xid);
    return rc;
    }

    diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
    index 21f4f35..3f511d7 100644
    --- a/fs/cifs/misc.c
    +++ b/fs/cifs/misc.c
    @@ -78,6 +78,7 @@ sesInfoAlloc(void)
    atomic_inc(&sesInfoAllocCount);
    ret_buf->status = CifsNew;
    INIT_LIST_HEAD(&ret_buf->smb_session_list);
    + INIT_LIST_HEAD(&ret_buf->tcon_list);
    init_MUTEX(&ret_buf->sesSem);
    }
    return ret_buf;
    @@ -106,16 +107,12 @@ 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);
    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;
    }
    @@ -127,10 +124,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);
    }
    @@ -491,9 +485,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"));
    @@ -548,42 +543,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_session_lock);
    + list_for_each(tmp, &srv->smb_session_list) {
    + ses = list_entry(tmp, struct cifsSesInfo, smb_session_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_session_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_session_lock);
    cFYI(1, ("No matching file for oplock break"));
    return true;
    }
    }
    - read_unlock(&GlobalSMBSeslock);
    + read_unlock(&cifs_tcp_session_lock);
    cFYI(1, ("Can not process oplock break for non-existent connection"));
    return true;
    }
    --
    1.5.5.1

    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  4. [PATCH 4/5] cifs: reinstate sharing of SMB sessions sans races

    We do this by abandoning the global list of SMB sessions and instead
    moving to a per-server list. This entails adding a new list head to the
    TCP_Server_Info struct. The refcounting for the cifsSesInfo is moved to
    a non-atomic variable. We have to protect it by a lock anyway, so there's
    no benefit to making it an atomic. The list and refcount are protected
    by the global cifs_tcp_session_lock.

    The patch also adds a new routines to find and put SMB sessions and
    that properly take and put references under the lock.

    Signed-off-by: Jeff Layton
    ---
    fs/cifs/cifs_debug.c | 52 ++++++++--------
    fs/cifs/cifsfs.c | 17 +++---
    fs/cifs/cifsglob.h | 8 +-
    fs/cifs/cifsproto.h | 1 -
    fs/cifs/cifssmb.c | 10 +---
    fs/cifs/connect.c | 155 +++++++++++++++++++++++++++++---------------------
    fs/cifs/misc.c | 15 ++---
    7 files changed, 133 insertions(+), 125 deletions(-)

    diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
    index e1804ca..cd4793f 100644
    --- a/fs/cifs/cifs_debug.c
    +++ b/fs/cifs/cifs_debug.c
    @@ -107,9 +107,9 @@ 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 *tmp, *tmp2, *tmp3;
    struct mid_q_entry *mid_entry;
    + struct TCP_Server_Info *server;
    struct cifsSesInfo *ses;
    struct cifsTconInfo *tcon;
    int i;
    @@ -122,43 +122,44 @@ 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_session_lock);
    + list_for_each(tmp, &cifs_tcp_session_list) {
    + server = list_entry(tmp, struct TCP_Server_Info,
    + tcp_session_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,
    + list_for_each(tmp2, &server->smb_session_list) {
    + ses = list_entry(tmp2, struct cifsSesInfo,
    + smb_session_list);
    + 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"
    " session status: %d\t",
    i, ses->serverName, ses->serverDomain,
    - atomic_read(&ses->inUse),
    - ses->serverOS, ses->serverNOS,
    + ses->refcount, 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,
    - ses->server->refcount,
    - ses->server->secMode,
    - atomic_read(&ses->server->inFlight));
    + "Server: %d SecMode: 0x%x Req On Wire: %d",
    + server->tcpStatus, server->refcount, 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");

    spin_lock(&GlobalMid_Lock);
    - list_for_each(tmp1, &ses->server->pending_mid_q) {
    - mid_entry = list_entry(tmp1, struct
    + 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:"
    @@ -171,9 +172,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
    }
    spin_unlock(&GlobalMid_Lock);
    }
    -
    }
    - read_unlock(&GlobalSMBSeslock);
    + read_unlock(&cifs_tcp_session_lock);
    seq_putc(m, '\n');

    seq_puts(m, "Shares:");
    diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
    index 5a14d96..f930c90 100644
    --- a/fs/cifs/cifsfs.c
    +++ b/fs/cifs/cifsfs.c
    @@ -1037,24 +1037,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_session_lock);
    + list_for_each(tmp, &cifs_tcp_session_list) {
    + server = list_entry(tmp, struct TCP_Server_Info,
    + tcp_session_list);
    + if (atomic_read(&server->inFlight))
    + wake_up_all(&server->response_q);
    }
    - read_unlock(&GlobalSMBSeslock);
    + read_unlock(&cifs_tcp_session_lock);
    } while (!kthread_should_stop());

    return 0;
    @@ -1066,7 +1066,6 @@ 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(&GlobalOplock_Q);
    INIT_LIST_HEAD(&cifs_tcp_session_list);
    diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
    index 0aec791..e7e59eb 100644
    --- a/fs/cifs/cifsglob.h
    +++ b/fs/cifs/cifsglob.h
    @@ -119,7 +119,8 @@ struct cifs_cred {
    */

    struct TCP_Server_Info {
    - struct list_head tcp_session_list;
    + struct list_head tcp_session_list; /* link to global tcp sess list */
    + struct list_head smb_session_list; /* list of SMB sessions */
    int refcount; /* reference counter */
    /* 15 character server name + 0x20 16th byte indicating type = srv */
    char server_RFC1001_name[SERVER_NAME_LEN_WITH_NULL];
    @@ -191,13 +192,13 @@ 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_session_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 refcount; /* reference counter */
    enum statusEnum status;
    unsigned overrideSecFlg; /* if non-zero override global sec flags */
    __u16 ipc_tid; /* special tid for connection to IPC share */
    @@ -598,7 +599,6 @@ require use of the stronger protocol */
    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 GlobalSMBSeslock; /* protects list inserts on 3 above */
    extern struct list_head cifs_tcp_session_list;
    diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
    index 0250a99..6f21ecb 100644
    --- a/fs/cifs/cifsproto.h
    +++ b/fs/cifs/cifsproto.h
    @@ -102,7 +102,6 @@ extern void acl_to_uid_mode(struct inode *inode, const char *path,
    const __u16 *pfid);
    extern int mode_to_acl(struct inode *inode, const char *path, __u64);

    -extern void cifs_put_tcp_session(struct TCP_Server_Info *server);
    extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
    const char *);
    extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
    diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
    index 83e1527..e761b65 100644
    --- a/fs/cifs/cifssmb.c
    +++ b/fs/cifs/cifssmb.c
    @@ -806,11 +806,6 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
    else
    return -EIO;

    - atomic_dec(&ses->inUse);
    - if (atomic_read(&ses->inUse) > 0) {
    - up(&ses->sesSem);
    - return -EBUSY;
    - }
    rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
    if (rc) {
    up(&ses->sesSem);
    @@ -829,10 +824,6 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)

    pSMB->AndXCommand = 0xFF;
    rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
    - if (ses->server) {
    - cifs_put_tcp_session(ses->server);
    - rc = 0;
    - }
    up(&ses->sesSem);

    /* if session dead then we do not need to do ulogoff,
    @@ -840,6 +831,7 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
    error */
    if (rc == -EAGAIN)
    rc = 0;
    +
    return rc;
    }

    diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
    index dbc6b48..63505f1 100644
    --- a/fs/cifs/connect.c
    +++ b/fs/cifs/connect.c
    @@ -144,23 +144,18 @@ 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;
    - }
    - }
    - /* else tcp and smb sessions need reconnection */
    + read_lock(&cifs_tcp_session_lock);
    + list_for_each(tmp, &server->smb_session_list) {
    + ses = list_entry(tmp, struct cifsSesInfo, smb_session_list);
    + ses->status = CifsNeedReconnect;
    + ses->ipc_tid = 0;
    }
    + read_unlock(&cifs_tcp_session_lock);
    list_for_each(tmp, &GlobalTreeConnectionList) {
    tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
    if ((tcon->ses) && (tcon->ses->server == server))
    clear_bit(CIFS_CONNECTED, &tcon->tidStatus);
    }
    - read_unlock(&GlobalSMBSeslock);
    /* do not want to be sending data on a socket we are freeing */
    down(&server->tcpSem);
    if (server->ssocket) {
    @@ -696,29 +691,25 @@ multi_t2_fnd:
    if (smallbuf) /* no sense logging a debug message if NULL */
    cifs_small_buf_release(smallbuf);

    - read_lock(&GlobalSMBSeslock);
    + read_lock(&cifs_tcp_session_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_session_list) {
    + ses = list_entry(tmp, struct cifsSesInfo,
    + smb_session_list);
    + ses->status = CifsExiting;
    + ses->server = NULL;
    }
    - read_unlock(&GlobalSMBSeslock);
    + read_unlock(&cifs_tcp_session_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_session_list) {
    ses = list_entry(tmp, struct cifsSesInfo,
    - cifsSessionList);
    - if (ses->server == server)
    - ses->status = CifsExiting;
    + smb_session_list);
    + ses->status = CifsExiting;
    }

    spin_lock(&GlobalMid_Lock);
    @@ -733,7 +724,7 @@ multi_t2_fnd:
    }
    }
    spin_unlock(&GlobalMid_Lock);
    - read_unlock(&GlobalSMBSeslock);
    + read_unlock(&cifs_tcp_session_lock);
    /* 1/8th of sec is more than enough time for them to exit */
    msleep(125);
    }
    @@ -755,14 +746,12 @@ 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;
    + read_lock(&cifs_tcp_session_lock);
    + list_for_each(tmp, &server->smb_session_list) {
    + ses = list_entry(tmp, struct cifsSesInfo, smb_session_list);
    + ses->server = NULL;
    }
    - write_unlock(&GlobalSMBSeslock);
    + read_unlock(&cifs_tcp_session_lock);

    kfree(server->hostname);
    task_to_wake = xchg(&server->tsk, NULL);
    @@ -1396,7 +1385,7 @@ cifs_find_tcp_session(struct sockaddr *addr)
    return NULL;
    }

    -void
    +static void
    cifs_put_tcp_session(struct TCP_Server_Info *server)
    {
    struct task_struct *task;
    @@ -1419,6 +1408,50 @@ cifs_put_tcp_session(struct TCP_Server_Info *server)
    force_sig(SIGKILL, task);
    }

    +static struct cifsSesInfo *
    +cifs_find_smb_session(struct TCP_Server_Info *server, char *username)
    +{
    + struct list_head *tmp;
    + struct cifsSesInfo *ses;
    +
    + write_lock(&cifs_tcp_session_lock);
    + list_for_each(tmp, &server->smb_session_list) {
    + ses = list_entry(tmp, struct cifsSesInfo, smb_session_list);
    + if (strncmp(ses->userName, username, MAX_USERNAME_SIZE))
    + continue;
    +
    + ++ses->refcount;
    + write_unlock(&cifs_tcp_session_lock);
    + return ses;
    + }
    + write_unlock(&cifs_tcp_session_lock);
    + return NULL;
    +}
    +
    +static void
    +cifs_put_smb_session(struct cifsSesInfo *ses)
    +{
    + int xid;
    + struct TCP_Server_Info *server = ses->server;
    +
    + write_lock(&cifs_tcp_session_lock);
    + if (--ses->refcount > 0) {
    + write_unlock(&cifs_tcp_session_lock);
    + return;
    + }
    +
    + list_del_init(&ses->smb_session_list);
    + write_unlock(&cifs_tcp_session_lock);
    +
    + if (ses->status == CifsGood) {
    + xid = GetXid();
    + CIFSSMBLogoff(xid, ses);
    + _FreeXid(xid);
    + }
    + sesInfoFree(ses);
    + cifs_put_tcp_session(server);
    +}
    +
    int
    get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
    const struct nls_table *nls_codepage, unsigned int *pnum_referrals,
    @@ -1867,7 +1900,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
    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;

    @@ -2021,6 +2053,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
    volume_info.target_rfc1001_name, 16);
    srvTcp->sequence_number = 0;
    INIT_LIST_HEAD(&srvTcp->tcp_session_list);
    + INIT_LIST_HEAD(&srvTcp->smb_session_list);
    ++srvTcp->refcount;
    write_lock(&cifs_tcp_session_lock);
    list_add(&srvTcp->tcp_session_list,
    @@ -2029,10 +2062,16 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
    }
    }

    - if (existingCifsSes) {
    - pSesInfo = existingCifsSes;
    + pSesInfo = cifs_find_smb_session(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) {
    cFYI(1, ("Session needs reconnect"));
    @@ -2046,9 +2085,15 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
    if (pSesInfo == NULL)
    rc = -ENOMEM;
    else {
    + /* new SMB session uses our srvTcp ref */
    pSesInfo->server = srvTcp;
    sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
    NIPQUAD(sin_server->sin_addr.s_addr));
    + ++pSesInfo->refcount;
    + write_lock(&cifs_tcp_session_lock);
    + list_add(&pSesInfo->smb_session_list,
    + &srvTcp->smb_session_list);
    + write_unlock(&cifs_tcp_session_lock);
    }

    if (!rc) {
    @@ -2212,7 +2257,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
    }
    }
    if (!rc) {
    - atomic_inc(&pSesInfo->inUse);
    tcon->retry = volume_info.retry;
    tcon->nocase = volume_info.nocase;
    tcon->seal = volume_info.seal;
    @@ -2230,28 +2274,18 @@ 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 */
    + /* on error free sesinfo and tcon struct if needed */
    if (rc) {
    /* If find_unc succeeded then rc == 0 so we can not end */
    /* up accidently freeing someone elses tcon struct */
    if (tcon)
    tconInfoFree(tcon);

    - if (existingCifsSes == NULL) {
    - if (pSesInfo) {
    - if ((pSesInfo->server) &&
    - (pSesInfo->status == CifsGood))
    - CIFSSMBLogoff(xid, pSesInfo);
    - else {
    - cFYI(1, ("No session or bad tcon"));
    - if (pSesInfo->server)
    - cifs_put_tcp_session(
    - pSesInfo->server);
    - }
    - sesInfoFree(pSesInfo);
    - /* pSesInfo = NULL; */
    - }
    - }
    + /* should also end up putting our tcp session ref if needed */
    + if (pSesInfo)
    + cifs_put_smb_session(pSesInfo);
    + else
    + cifs_put_tcp_session(srvTcp);
    } else {
    atomic_inc(&tcon->useCount);
    cifs_sb->tcon = tcon;
    @@ -3545,16 +3579,7 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
    }
    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
    - cFYI(1, ("No session or bad tcon"));
    + cifs_put_smb_session(ses);
    }

    cifs_sb->tcon = NULL;
    @@ -3562,8 +3587,6 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
    cifs_sb->prepathlen = 0;
    cifs_sb->prepath = NULL;
    kfree(tmp);
    - if (ses)
    - sesInfoFree(ses);

    FreeXid(xid);
    return rc;
    diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
    index 7736a38..21f4f35 100644
    --- a/fs/cifs/misc.c
    +++ b/fs/cifs/misc.c
    @@ -75,12 +75,10 @@ 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);
    + INIT_LIST_HEAD(&ret_buf->smb_session_list);
    init_MUTEX(&ret_buf->sesSem);
    - write_unlock(&GlobalSMBSeslock);
    }
    return ret_buf;
    }
    @@ -93,10 +91,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);
    @@ -349,9 +344,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_session_lock);
    + list_for_each(temp_item, &treeCon->ses->server->smb_session_list) {
    + ses = list_entry(temp_item, struct cifsSesInfo, smb_session_list);
    if (ses->linux_uid == current->fsuid) {
    if (ses->server == treeCon->ses->server) {
    cFYI(1, ("found matching uid substitute right smb_uid"));
    @@ -363,7 +358,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
    }
    }
    }
    - read_unlock(&GlobalSMBSeslock);
    + read_unlock(&cifs_tcp_session_lock);
    }
    }
    }
    --
    1.5.5.1

    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  5. [PATCH 3/5] cifs: disable sharing session and tcon and add new TCP sharing code

    The code that allows these structs to be shared is extremely racy.
    Disable the sharing of SMB and tcon structs for now until we can
    come up with a way to do this that's race free.

    We want to continue to share TCP sessions, however since they are
    required for multiuser mounts. For that, implement a new (hopefully
    race-free) scheme. Add a new global list of TCP sessions, and take
    care to get a reference to it whenever we're dealing with one.

    Signed-off-by: Jeff Layton
    ---
    fs/cifs/cifs_debug.c | 2 +-
    fs/cifs/cifsfs.c | 8 ++
    fs/cifs/cifsglob.h | 5 +-
    fs/cifs/cifsproto.h | 1 +
    fs/cifs/cifssmb.c | 18 ++---
    fs/cifs/connect.c | 192 ++++++++++++++++----------------------------------
    6 files changed, 84 insertions(+), 142 deletions(-)

    diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
    index ed16bbb..e1804ca 100644
    --- a/fs/cifs/cifs_debug.c
    +++ b/fs/cifs/cifs_debug.c
    @@ -144,7 +144,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
    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->refcount,
    ses->server->secMode,
    atomic_read(&ses->server->inFlight));

    diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
    index 3d6129f..5a14d96 100644
    --- a/fs/cifs/cifsfs.c
    +++ b/fs/cifs/cifsfs.c
    @@ -91,6 +91,12 @@ extern mempool_t *cifs_mid_poolp;

    extern struct kmem_cache *cifs_oplock_cachep;

    +/* global list of TCP_Server_Info structs */
    +struct list_head cifs_tcp_session_list;
    +
    +/* protects cifs_tcp_session_list and TCP_Server_Info refcount */
    +rwlock_t cifs_tcp_session_lock;
    +
    static int
    cifs_read_super(struct super_block *sb, void *data,
    const char *devname, int silent)
    @@ -1063,6 +1069,7 @@ init_cifs(void)
    INIT_LIST_HEAD(&GlobalSMBSessionList);
    INIT_LIST_HEAD(&GlobalTreeConnectionList);
    INIT_LIST_HEAD(&GlobalOplock_Q);
    + INIT_LIST_HEAD(&cifs_tcp_session_list);
    #ifdef CONFIG_CIFS_EXPERIMENTAL
    INIT_LIST_HEAD(&GlobalDnotifyReqList);
    INIT_LIST_HEAD(&GlobalDnotifyRsp_Q);
    @@ -1089,6 +1096,7 @@ init_cifs(void)
    GlobalMaxActiveXid = 0;
    memset(Local_System_Name, 0, 15);
    rwlock_init(&GlobalSMBSeslock);
    + rwlock_init(&cifs_tcp_session_lock);
    spin_lock_init(&GlobalMid_Lock);

    if (cifs_max_pending < 2) {
    diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
    index f97ddef..0aec791 100644
    --- a/fs/cifs/cifsglob.h
    +++ b/fs/cifs/cifsglob.h
    @@ -119,6 +119,8 @@ struct cifs_cred {
    */

    struct TCP_Server_Info {
    + struct list_head tcp_session_list;
    + int refcount; /* 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];
    @@ -139,7 +141,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 */
    @@ -600,6 +601,8 @@ GLOBAL_EXTERN struct smbUidInfo *GlobalUidList[UID_HASH];
    GLOBAL_EXTERN struct list_head GlobalSMBSessionList;
    GLOBAL_EXTERN struct list_head GlobalTreeConnectionList;
    GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */
    +extern struct list_head cifs_tcp_session_list;
    +extern rwlock_t cifs_tcp_session_lock;

    GLOBAL_EXTERN struct list_head GlobalOplock_Q;

    diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
    index 6f21ecb..0250a99 100644
    --- a/fs/cifs/cifsproto.h
    +++ b/fs/cifs/cifsproto.h
    @@ -102,6 +102,7 @@ extern void acl_to_uid_mode(struct inode *inode, const char *path,
    const __u16 *pfid);
    extern int mode_to_acl(struct inode *inode, const char *path, __u64);

    +extern void cifs_put_tcp_session(struct TCP_Server_Info *server);
    extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
    const char *);
    extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
    diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
    index 29bd4a4..83e1527 100644
    --- a/fs/cifs/cifssmb.c
    +++ b/fs/cifs/cifssmb.c
    @@ -666,8 +666,9 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
    rc = -EIO;
    goto neg_err_exit;
    }
    -
    - if (server->socketUseCount.counter > 1) {
    + read_lock(&cifs_tcp_session_lock);
    + if (server->refcount > 1) {
    + read_unlock(&cifs_tcp_session_lock);
    if (memcmp(server->server_GUID,
    pSMBr->u.extended_response.
    GUID, 16) != 0) {
    @@ -676,9 +677,11 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
    pSMBr->u.extended_response.GUID,
    16);
    }
    - } else
    + } else {
    + read_unlock(&cifs_tcp_session_lock);
    memcpy(server->server_GUID,
    pSMBr->u.extended_response.GUID, 16);
    + }

    if (count == 16) {
    server->secType = RawNTLMSSP;
    @@ -827,13 +830,8 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
    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;
    - }
    + cifs_put_tcp_session(ses->server);
    + rc = 0;
    }
    up(&ses->sesSem);

    diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
    index 9d627f9..dbc6b48 100644
    --- a/fs/cifs/connect.c
    +++ b/fs/cifs/connect.c
    @@ -659,6 +659,11 @@ multi_t2_fnd:
    }
    } /* end while !EXITING */

    + /* take it off the list, if it's not already */
    + write_lock(&cifs_tcp_session_lock);
    + list_del_init(&server->tcp_session_list);
    + write_unlock(&cifs_tcp_session_lock);
    +
    spin_lock(&GlobalMid_Lock);
    server->tcpStatus = CifsExiting;
    spin_unlock(&GlobalMid_Lock);
    @@ -1357,92 +1362,61 @@ 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;
    + struct TCP_Server_Info *server;
    + struct sockaddr_in *addr4 = (struct sockaddr_in *) addr;
    + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr;

    - *psrvTcp = NULL;
    + write_lock(&cifs_tcp_session_lock);
    + list_for_each(tmp, &cifs_tcp_session_list) {
    + server = list_entry(tmp, struct TCP_Server_Info,
    + tcp_session_list);

    - read_lock(&GlobalSMBSeslock);
    - list_for_each(tmp, &GlobalSMBSessionList) {
    - ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
    - if (!ses->server)
    + /* Only accept sessions that have done successful negotiate */
    + if (server->tcpStatus == CifsNew)
    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?? */
    -
    - /* found a match on the TCP session */
    - *psrvTcp = 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;

    - /* 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 */
    + ++server->refcount;
    + write_unlock(&cifs_tcp_session_lock);
    + return server;
    }
    - read_unlock(&GlobalSMBSeslock);
    -
    + write_unlock(&cifs_tcp_session_lock);
    return NULL;
    }

    -static struct cifsTconInfo *
    -find_unc(__be32 new_target_ip_addr, char *uncName, char *userName)
    +void
    +cifs_put_tcp_session(struct TCP_Server_Info *server)
    {
    - struct list_head *tmp;
    - struct cifsTconInfo *tcon;
    - __be32 old_ip;
    -
    - read_lock(&GlobalSMBSeslock);
    -
    - list_for_each(tmp, &GlobalTreeConnectionList) {
    - cFYI(1, ("Next tcon"));
    - tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
    - if (!tcon->ses || !tcon->ses->server)
    - 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));
    -
    - if (old_ip != new_target_ip_addr)
    - continue;
    -
    - /* 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));
    -
    - if (strncmp(tcon->treeName, uncName, MAX_TREE_SIZE))
    - continue;
    + struct task_struct *task;

    - cFYI(1, ("and old usr: %s new: %s",
    - tcon->treeName, uncName));
    + write_lock(&cifs_tcp_session_lock);
    + if (--server->refcount > 0) {
    + write_unlock(&cifs_tcp_session_lock);
    + return;
    + }

    - if (strncmp(tcon->ses->userName, userName, MAX_USERNAME_SIZE))
    - continue;
    + list_del_init(&server->tcp_session_list);
    + write_unlock(&cifs_tcp_session_lock);

    - /* matched smb session (user name) */
    - read_unlock(&GlobalSMBSeslock);
    - return tcon;
    - }
    + spin_lock(&GlobalMid_Lock);
    + server->tcpStatus = CifsExiting;
    + spin_unlock(&GlobalMid_Lock);

    - read_unlock(&GlobalSMBSeslock);
    - return NULL;
    + task = xchg(&server->tsk, NULL);
    + if (task)
    + force_sig(SIGKILL, task);
    }

    int
    @@ -1881,16 +1855,6 @@ convert_delimiter(char *path, char delim)
    }
    }

    -static void
    -kill_cifsd(struct TCP_Server_Info *server)
    -{
    - struct task_struct *task;
    -
    - task = xchg(&server->tsk, NULL);
    - if (task)
    - force_sig(SIGKILL, task);
    -}
    -
    int
    cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
    char *mount_data, const char *devname)
    @@ -1983,20 +1947,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
    }
    }

    - if (addr.sa_family == AF_INET)
    - existingCifsSes = cifs_find_tcp_session(&sin_server->sin_addr,
    - NULL /* no ipv6 addr */,
    - volume_info.username, &srvTcp);
    - else if (addr.sa_family == 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;
    - }
    -
    + srvTcp = cifs_find_tcp_session(&addr);
    if (srvTcp) {
    cFYI(1, ("Existing tcp session with server found"));
    } else { /* create socket */
    @@ -2069,6 +2020,12 @@ 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_session_list);
    + ++srvTcp->refcount;
    + write_lock(&cifs_tcp_session_lock);
    + list_add(&srvTcp->tcp_session_list,
    + &cifs_tcp_session_list);
    + write_unlock(&cifs_tcp_session_lock);
    }
    }

    @@ -2120,8 +2077,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
    rc = cifs_setup_session(xid, pSesInfo,
    cifs_sb->local_nls);
    up(&pSesInfo->sesSem);
    - if (!rc)
    - atomic_inc(&srvTcp->socketUseCount);
    }
    }

    @@ -2209,9 +2164,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
    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);
    if (tcon) {
    cFYI(1, ("Found match on UNC path"));
    /* we can have only one retry value for a connection
    @@ -2280,35 +2232,21 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,

    /* 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 */
    + /* If find_unc succeeded then rc == 0 so we can not end */
    + /* up accidently freeing someone elses tcon struct */
    + if (tcon)
    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 {
    + (pSesInfo->status == CifsGood))
    + CIFSSMBLogoff(xid, pSesInfo);
    + 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);
    - }
    + if (pSesInfo->server)
    + cifs_put_tcp_session(
    + pSesInfo->server);
    }
    sesInfoFree(pSesInfo);
    /* pSesInfo = NULL; */
    @@ -3614,13 +3552,7 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
    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"));
    }
    --
    1.5.5.1

    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  6. [PATCH 2/5] cifs: convert tcon->tidStatus to a bitfield

    Currently, it's an enum, but we need to track a number of different
    states with it. In particular we need to know if the tcon needs to be
    reconnected and whether it's on the way to being freed. A field of
    flags is better suited for this purpose.

    Signed-off-by: Jeff Layton
    ---
    fs/cifs/cifs_debug.c | 6 +++---
    fs/cifs/cifsfs.c | 4 ++--
    fs/cifs/cifsglob.h | 7 ++++++-
    fs/cifs/cifssmb.c | 12 +++++++-----
    fs/cifs/connect.c | 4 ++--
    fs/cifs/file.c | 2 +-
    fs/cifs/misc.c | 1 -
    7 files changed, 21 insertions(+), 15 deletions(-)

    diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
    index 69a12aa..ed16bbb 100644
    --- a/fs/cifs/cifs_debug.c
    +++ b/fs/cifs/cifs_debug.c
    @@ -192,7 +192,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
    tcon->nativeFileSystem);
    }
    seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x"
    - "\nPathComponentMax: %d Status: %d",
    + "\nPathComponentMax: %d Status: 0x%lx",
    le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),
    le32_to_cpu(tcon->fsAttrInfo.Attributes),
    le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
    @@ -204,7 +204,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
    else
    seq_printf(m, " type: %d ", dev_type);

    - if (tcon->tidStatus == CifsNeedReconnect)
    + if (!test_bit(CIFS_CONNECTED, &tcon->tidStatus))
    seq_puts(m, "\tDISCONNECTED ");
    }
    read_unlock(&GlobalSMBSeslock);
    @@ -311,7 +311,7 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
    i++;
    tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
    seq_printf(m, "\n%d) %s", i, tcon->treeName);
    - if (tcon->tidStatus == CifsNeedReconnect)
    + if (!test_bit(CIFS_CONNECTED, &tcon->tidStatus))
    seq_puts(m, "\tDISCONNECTED ");
    seq_printf(m, "\nSMBs: %d Oplock Breaks: %d",
    atomic_read(&tcon->num_smbs_sent),
    diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
    index ac5915d..3d6129f 100644
    --- a/fs/cifs/cifsfs.c
    +++ b/fs/cifs/cifsfs.c
    @@ -516,7 +516,7 @@ static void cifs_umount_begin(struct super_block *sb)
    return;
    down(&tcon->tconSem);
    if (atomic_read(&tcon->useCount) == 1)
    - tcon->tidStatus = CifsExiting;
    + set_bit(CIFS_CLOSING, &tcon->tidStatus);
    up(&tcon->tconSem);

    /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */
    @@ -1013,7 +1013,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 (!test_bit(CIFS_CONNECTED, &pTcon->tidStatus)) {
    rc = CIFSSMBLock(0, pTcon, netfid,
    0 /* len */ , 0 /* offset */, 0,
    0, LOCKING_ANDX_OPLOCK_RELEASE,
    diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
    index 58b8d5c..f97ddef 100644
    --- a/fs/cifs/cifsglob.h
    +++ b/fs/cifs/cifsglob.h
    @@ -73,6 +73,10 @@ enum statusEnum {
    CifsNeedReconnect
    };

    +/* flags for tcon and session status */
    +#define CIFS_CONNECTED (0)
    +#define CIFS_CLOSING (1)
    +
    enum securityEnum {
    PLAINTXT = 0, /* Legacy with Plaintext passwords */
    LANMAN, /* Legacy LANMAN auth */
    @@ -217,6 +221,7 @@ struct cifsSesInfo {
    which do not negotiate NTLM or POSIX dialects, but instead
    negotiate one of the older LANMAN dialects */
    #define CIFS_SES_LANMAN 8
    +
    /*
    * there is one of these for each connection to a resource on a particular
    * session
    @@ -230,7 +235,7 @@ struct cifsTconInfo {
    char *nativeFileSystem;
    __u16 tid; /* The 2 byte tree id */
    __u16 Flags; /* optional support bits */
    - enum statusEnum tidStatus;
    + unsigned long tidStatus;
    atomic_t useCount; /* how many explicit/implicit mounts to share */
    #ifdef CONFIG_CIFS_STATS
    atomic_t num_smbs_sent;
    diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
    index d5eac48..29bd4a4 100644
    --- a/fs/cifs/cifssmb.c
    +++ b/fs/cifs/cifssmb.c
    @@ -148,7 +148,7 @@ small_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 (test_bit(CIFS_CLOSING, &tcon->tidStatus)) {
    /* only tree disconnect, open, and write,
    (and ulogoff which does not have tcon)
    are allowed as we start force umount */
    @@ -193,7 +193,8 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
    if (tcon->ses->status == CifsNeedReconnect)
    rc = cifs_setup_session(0, tcon->ses,
    nls_codepage);
    - if (!rc && (tcon->tidStatus == CifsNeedReconnect)) {
    + if (!rc &&
    + !test_bit(CIFS_CONNECTED, &tcon->tidStatus)) {
    mark_open_files_invalid(tcon);
    rc = CIFSTCon(0, tcon->ses, tcon->treeName,
    tcon, nls_codepage);
    @@ -295,7 +296,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 (test_bit(CIFS_CLOSING, &tcon->tidStatus)) {
    /* only tree disconnect, open, and write,
    (and ulogoff which does not have tcon)
    are allowed as we start force umount */
    @@ -340,7 +341,8 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
    if (tcon->ses->status == CifsNeedReconnect)
    rc = cifs_setup_session(0, tcon->ses,
    nls_codepage);
    - if (!rc && (tcon->tidStatus == CifsNeedReconnect)) {
    + if (!rc &&
    + !test_bit(CIFS_CONNECTED, &tcon->tidStatus)) {
    mark_open_files_invalid(tcon);
    rc = CIFSTCon(0, tcon->ses, tcon->treeName,
    tcon, nls_codepage);
    @@ -759,7 +761,7 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)

    /* 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) {
    + if (!test_bit(CIFS_CONNECTED, &tcon->tidStatus)) {
    up(&tcon->tconSem);
    return 0;
    }
    diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
    index 22357ec..9d627f9 100644
    --- a/fs/cifs/connect.c
    +++ b/fs/cifs/connect.c
    @@ -158,7 +158,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
    list_for_each(tmp, &GlobalTreeConnectionList) {
    tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
    if ((tcon->ses) && (tcon->ses->server == server))
    - tcon->tidStatus = CifsNeedReconnect;
    + clear_bit(CIFS_CONNECTED, &tcon->tidStatus);
    }
    read_unlock(&GlobalSMBSeslock);
    /* do not want to be sending data on a socket we are freeing */
    @@ -3520,7 +3520,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
    /* if (rc) rc = map_smb_to_linux_error(smb_buffer_response); */
    /* above now done in SendReceive */
    if ((rc == 0) && (tcon != NULL)) {
    - tcon->tidStatus = CifsGood;
    + set_bit(CIFS_CONNECTED, &tcon->tidStatus);
    tcon->tid = smb_buffer_response->Tid;
    bcc_ptr = pByteArea(smb_buffer_response);
    length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2);
    diff --git a/fs/cifs/file.c b/fs/cifs/file.c
    index ead1a3b..94da3d2 100644
    --- a/fs/cifs/file.c
    +++ b/fs/cifs/file.c
    @@ -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 (test_bit(CIFS_CONNECTED, &pTcon->tidStatus)) {
    timeout = 2;
    while ((atomic_read(&pSMBFile->wrtPending) != 0)
    && (timeout <= 2048)) {
    diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
    index 88786ba..7736a38 100644
    --- a/fs/cifs/misc.c
    +++ b/fs/cifs/misc.c
    @@ -115,7 +115,6 @@ tconInfoAlloc(void)
    atomic_inc(&tconInfoAllocCount);
    list_add(&ret_buf->cifsConnectionList,
    &GlobalTreeConnectionList);
    - ret_buf->tidStatus = CifsNew;
    INIT_LIST_HEAD(&ret_buf->openFileList);
    init_MUTEX(&ret_buf->tconSem);
    #ifdef CONFIG_CIFS_STATS
    --
    1.5.5.1

    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

+ Reply to Thread