]> www.infradead.org Git - users/hch/configfs.git/commitdiff
cifs: Add tracing for the cifs_tcon struct refcounting
authorDavid Howells <dhowells@redhat.com>
Thu, 4 Apr 2024 12:51:36 +0000 (13:51 +0100)
committerSteve French <stfrench@microsoft.com>
Fri, 19 Apr 2024 21:02:09 +0000 (16:02 -0500)
Add tracing for the refcounting/lifecycle of the cifs_tcon struct, marking
different events with different labels and giving each tcon its own debug
ID so that the tracelines corresponding to individual tcons can be
distinguished.  This can be enabled with:

echo 1 >/sys/kernel/debug/tracing/events/cifs/smb3_tcon_ref/enable

Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
cc: Shyam Prasad N <nspmangalore@gmail.com>
cc: linux-cifs@vger.kernel.org
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/cifsfs.c
fs/smb/client/cifsglob.h
fs/smb/client/cifsproto.h
fs/smb/client/connect.c
fs/smb/client/fscache.c
fs/smb/client/misc.c
fs/smb/client/smb2misc.c
fs/smb/client/smb2ops.c
fs/smb/client/smb2pdu.c
fs/smb/client/smb2transport.c
fs/smb/client/trace.h

index d41eedbff674abb0e62e52ae6cc585aaa5d83d77..30781789dfd9f539abbe7499d65496d05607d5df 100644 (file)
@@ -739,6 +739,8 @@ static void cifs_umount_begin(struct super_block *sb)
 
        spin_lock(&cifs_tcp_ses_lock);
        spin_lock(&tcon->tc_lock);
+       trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
+                           netfs_trace_tcon_ref_see_umount);
        if ((tcon->tc_count > 1) || (tcon->status == TID_EXITING)) {
                /* we have other mounts to same share or we have
                   already tried to umount this and woken up
index fc09d1c0ee070fc7b3ecd17e02e3872238a668ba..6ff35570db813a533ef9221fad6a7aea99e6d1d2 100644 (file)
@@ -1190,6 +1190,7 @@ struct cifs_fattr {
  */
 struct cifs_tcon {
        struct list_head tcon_list;
+       int debug_id;           /* Debugging for tracing */
        int tc_count;
        struct list_head rlist; /* reconnect list */
        spinlock_t tc_lock;  /* protect anything here that is not protected */
index 8e0a348f1f660ebc14498c7fd7d342693411c106..fbc358c09da3b1d7ffc495d0c461e32509f95c1c 100644 (file)
@@ -303,7 +303,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
                     struct TCP_Server_Info *primary_server);
 extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
                                 int from_reconnect);
-extern void cifs_put_tcon(struct cifs_tcon *tcon);
+extern void cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace);
 
 extern void cifs_release_automount_timer(void);
 
@@ -530,8 +530,9 @@ extern int CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses);
 
 extern struct cifs_ses *sesInfoAlloc(void);
 extern void sesInfoFree(struct cifs_ses *);
-extern struct cifs_tcon *tcon_info_alloc(bool dir_leases_enabled);
-extern void tconInfoFree(struct cifs_tcon *);
+extern struct cifs_tcon *tcon_info_alloc(bool dir_leases_enabled,
+                                        enum smb3_tcon_ref_trace trace);
+extern void tconInfoFree(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace);
 
 extern int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
                   __u32 *pexpected_response_sequence_number);
@@ -721,8 +722,6 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
                return options;
 }
 
-struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
-void cifs_put_tcon_super(struct super_block *sb);
 int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry);
 
 /* Put references of @ses and its children */
index 4e35970681bf052dc343c23935549600f5ce8859..7a16e12f5da879bbbb8ace98a4ec4f30aafec33e 100644 (file)
@@ -1943,7 +1943,7 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
        }
 
        /* no need to setup directory caching on IPC share, so pass in false */
-       tcon = tcon_info_alloc(false);
+       tcon = tcon_info_alloc(false, netfs_trace_tcon_ref_new_ipc);
        if (tcon == NULL)
                return -ENOMEM;
 
@@ -1960,7 +1960,7 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
 
        if (rc) {
                cifs_server_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc);
-               tconInfoFree(tcon);
+               tconInfoFree(tcon, netfs_trace_tcon_ref_free_ipc_fail);
                goto out;
        }
 
@@ -2043,7 +2043,7 @@ void __cifs_put_smb_ses(struct cifs_ses *ses)
         * files on session close, as specified in MS-SMB2 3.3.5.6 Receiving an
         * SMB2 LOGOFF Request.
         */
-       tconInfoFree(tcon);
+       tconInfoFree(tcon, netfs_trace_tcon_ref_free_ipc);
        if (do_logoff) {
                xid = get_xid();
                rc = server->ops->logoff(xid, ses);
@@ -2432,6 +2432,8 @@ cifs_find_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
                        continue;
                }
                ++tcon->tc_count;
+               trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
+                                   netfs_trace_tcon_ref_get_find);
                spin_unlock(&tcon->tc_lock);
                spin_unlock(&cifs_tcp_ses_lock);
                return tcon;
@@ -2441,7 +2443,7 @@ cifs_find_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
 }
 
 void
-cifs_put_tcon(struct cifs_tcon *tcon)
+cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
 {
        unsigned int xid;
        struct cifs_ses *ses;
@@ -2457,6 +2459,7 @@ cifs_put_tcon(struct cifs_tcon *tcon)
        cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count);
        spin_lock(&cifs_tcp_ses_lock);
        spin_lock(&tcon->tc_lock);
+       trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count - 1, trace);
        if (--tcon->tc_count > 0) {
                spin_unlock(&tcon->tc_lock);
                spin_unlock(&cifs_tcp_ses_lock);
@@ -2493,7 +2496,7 @@ cifs_put_tcon(struct cifs_tcon *tcon)
        _free_xid(xid);
 
        cifs_fscache_release_super_cookie(tcon);
-       tconInfoFree(tcon);
+       tconInfoFree(tcon, netfs_trace_tcon_ref_free);
        cifs_put_smb_ses(ses);
 }
 
@@ -2547,7 +2550,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
                nohandlecache = ctx->nohandlecache;
        else
                nohandlecache = true;
-       tcon = tcon_info_alloc(!nohandlecache);
+       tcon = tcon_info_alloc(!nohandlecache, netfs_trace_tcon_ref_new);
        if (tcon == NULL) {
                rc = -ENOMEM;
                goto out_fail;
@@ -2737,7 +2740,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
        return tcon;
 
 out_fail:
-       tconInfoFree(tcon);
+       tconInfoFree(tcon, netfs_trace_tcon_ref_free_fail);
        return ERR_PTR(rc);
 }
 
@@ -2754,7 +2757,7 @@ cifs_put_tlink(struct tcon_link *tlink)
        }
 
        if (!IS_ERR(tlink_tcon(tlink)))
-               cifs_put_tcon(tlink_tcon(tlink));
+               cifs_put_tcon(tlink_tcon(tlink), netfs_trace_tcon_ref_put_tlink);
        kfree(tlink);
 }
 
@@ -3319,7 +3322,7 @@ void cifs_mount_put_conns(struct cifs_mount_ctx *mnt_ctx)
        int rc = 0;
 
        if (mnt_ctx->tcon)
-               cifs_put_tcon(mnt_ctx->tcon);
+               cifs_put_tcon(mnt_ctx->tcon, netfs_trace_tcon_ref_put_mnt_ctx);
        else if (mnt_ctx->ses)
                cifs_put_smb_ses(mnt_ctx->ses);
        else if (mnt_ctx->server)
index 113bde8f1e6133ba7270edae49116ff680e49886..1a895e6243ee9aaf21fc8405893ce52ed14303a2 100644 (file)
@@ -94,6 +94,11 @@ int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)
                }
                pr_err("Cache volume key already in use (%s)\n", key);
                vcookie = NULL;
+               trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
+                                   netfs_trace_tcon_ref_see_fscache_collision);
+       } else {
+               trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
+                                   netfs_trace_tcon_ref_see_fscache_okay);
        }
 
        tcon->fscache = vcookie;
@@ -115,6 +120,8 @@ void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon)
        cifs_fscache_fill_volume_coherency(tcon, &cd);
        fscache_relinquish_volume(tcon->fscache, &cd, false);
        tcon->fscache = NULL;
+       trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
+                           netfs_trace_tcon_ref_see_fscache_relinq);
 }
 
 void cifs_fscache_get_inode_cookie(struct inode *inode)
index ad44f8d66b3775ce5c585b2c2b989b9d79cb623f..07c468ddb88a89d65f8a48433a055759deb3da26 100644 (file)
@@ -111,9 +111,10 @@ sesInfoFree(struct cifs_ses *buf_to_free)
 }
 
 struct cifs_tcon *
-tcon_info_alloc(bool dir_leases_enabled)
+tcon_info_alloc(bool dir_leases_enabled, enum smb3_tcon_ref_trace trace)
 {
        struct cifs_tcon *ret_buf;
+       static atomic_t tcon_debug_id;
 
        ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL);
        if (!ret_buf)
@@ -130,7 +131,8 @@ tcon_info_alloc(bool dir_leases_enabled)
 
        atomic_inc(&tconInfoAllocCount);
        ret_buf->status = TID_NEW;
-       ++ret_buf->tc_count;
+       ret_buf->debug_id = atomic_inc_return(&tcon_debug_id);
+       ret_buf->tc_count = 1;
        spin_lock_init(&ret_buf->tc_lock);
        INIT_LIST_HEAD(&ret_buf->openFileList);
        INIT_LIST_HEAD(&ret_buf->tcon_list);
@@ -142,17 +144,19 @@ tcon_info_alloc(bool dir_leases_enabled)
 #ifdef CONFIG_CIFS_FSCACHE
        mutex_init(&ret_buf->fscache_lock);
 #endif
+       trace_smb3_tcon_ref(ret_buf->debug_id, ret_buf->tc_count, trace);
 
        return ret_buf;
 }
 
 void
-tconInfoFree(struct cifs_tcon *tcon)
+tconInfoFree(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
 {
        if (tcon == NULL) {
                cifs_dbg(FYI, "Null buffer passed to tconInfoFree\n");
                return;
        }
+       trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, trace);
        free_cached_dirs(tcon->cfids);
        atomic_dec(&tconInfoAllocCount);
        kfree(tcon->nativeFileSystem);
index cc72be5a93a933b09c45256c2a7d7615478f54c0..677ef6f99a5be407fb9c73baba7918cf5e28244e 100644 (file)
@@ -767,7 +767,7 @@ smb2_cancelled_close_fid(struct work_struct *work)
        if (rc)
                cifs_tcon_dbg(VFS, "Close cancelled mid failed rc:%d\n", rc);
 
-       cifs_put_tcon(tcon);
+       cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_close_fid);
        kfree(cancelled);
 }
 
@@ -811,6 +811,8 @@ smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid,
        if (tcon->tc_count <= 0) {
                struct TCP_Server_Info *server = NULL;
 
+               trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
+                                   netfs_trace_tcon_ref_see_cancelled_close);
                WARN_ONCE(tcon->tc_count < 0, "tcon refcount is negative");
                spin_unlock(&cifs_tcp_ses_lock);
 
@@ -823,12 +825,14 @@ smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid,
                return 0;
        }
        tcon->tc_count++;
+       trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
+                           netfs_trace_tcon_ref_get_cancelled_close);
        spin_unlock(&cifs_tcp_ses_lock);
 
        rc = __smb2_handle_cancelled_cmd(tcon, SMB2_CLOSE_HE, 0,
                                         persistent_fid, volatile_fid);
        if (rc)
-               cifs_put_tcon(tcon);
+               cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_close);
 
        return rc;
 }
@@ -856,7 +860,7 @@ smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *serve
                                         rsp->PersistentFileId,
                                         rsp->VolatileFileId);
        if (rc)
-               cifs_put_tcon(tcon);
+               cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_mid);
 
        return rc;
 }
index 78c94d0350fe9970fab31564aeba6870d71859bd..28f0b7d19d534b18bff680bb739247889ac7675b 100644 (file)
@@ -2915,8 +2915,11 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
                tcon = list_first_entry_or_null(&ses->tcon_list,
                                                struct cifs_tcon,
                                                tcon_list);
-               if (tcon)
+               if (tcon) {
                        tcon->tc_count++;
+                       trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
+                                           netfs_trace_tcon_ref_get_dfs_refer);
+               }
                spin_unlock(&cifs_tcp_ses_lock);
        }
 
@@ -2980,6 +2983,8 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
                /* ipc tcons are not refcounted */
                spin_lock(&cifs_tcp_ses_lock);
                tcon->tc_count--;
+               trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
+                                   netfs_trace_tcon_ref_dec_dfs_refer);
                /* tc_count can never go negative */
                WARN_ON(tcon->tc_count < 0);
                spin_unlock(&cifs_tcp_ses_lock);
index 86c647a947ccd1065a8edb0712e113351839b96f..a5efce03cb58e2d995862f8c3b0cc081f8beed5b 100644 (file)
@@ -4138,6 +4138,8 @@ void smb2_reconnect_server(struct work_struct *work)
                list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
                        if (tcon->need_reconnect || tcon->need_reopen_files) {
                                tcon->tc_count++;
+                               trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
+                                                   netfs_trace_tcon_ref_get_reconnect_server);
                                list_add_tail(&tcon->rlist, &tmp_list);
                                tcon_selected = true;
                        }
@@ -4176,14 +4178,14 @@ void smb2_reconnect_server(struct work_struct *work)
                if (tcon->ipc)
                        cifs_put_smb_ses(tcon->ses);
                else
-                       cifs_put_tcon(tcon);
+                       cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_reconnect_server);
        }
 
        if (!ses_exist)
                goto done;
 
        /* allocate a dummy tcon struct used for reconnect */
-       tcon = tcon_info_alloc(false);
+       tcon = tcon_info_alloc(false, netfs_trace_tcon_ref_new_reconnect_server);
        if (!tcon) {
                resched = true;
                list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) {
@@ -4206,7 +4208,7 @@ void smb2_reconnect_server(struct work_struct *work)
                list_del_init(&ses->rlist);
                cifs_put_smb_ses(ses);
        }
-       tconInfoFree(tcon);
+       tconInfoFree(tcon, netfs_trace_tcon_ref_free_reconnect_server);
 
 done:
        cifs_dbg(FYI, "Reconnecting tcons and channels finished\n");
index 1d6e54f7879e6a5e8034a90d30471fecc02d2d1b..02135a6053051ee6848f8df90be30fc2c805af6d 100644 (file)
@@ -189,6 +189,8 @@ smb2_find_smb_sess_tcon_unlocked(struct cifs_ses *ses, __u32  tid)
                if (tcon->tid != tid)
                        continue;
                ++tcon->tc_count;
+               trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
+                                   netfs_trace_tcon_ref_get_find_sess_tcon);
                return tcon;
        }
 
index 5e83cb9da9028e0d15383e6e19413ea1da31e553..604e52876cd2d98e9a86941b0c527dbe6d8abe6a 100644 (file)
@@ -3,6 +3,9 @@
  *   Copyright (C) 2018, Microsoft Corporation.
  *
  *   Author(s): Steve French <stfrench@microsoft.com>
+ *
+ * Please use this 3-part article as a reference for writing new tracepoints:
+ * https://lwn.net/Articles/379903/
  */
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM cifs
 #include <linux/inet.h>
 
 /*
- * Please use this 3-part article as a reference for writing new tracepoints:
- * https://lwn.net/Articles/379903/
+ * Specify enums for tracing information.
+ */
+#define smb3_tcon_ref_traces                                         \
+       EM(netfs_trace_tcon_ref_dec_dfs_refer,          "DEC DfsRef") \
+       EM(netfs_trace_tcon_ref_free,                   "FRE       ") \
+       EM(netfs_trace_tcon_ref_free_fail,              "FRE Fail  ") \
+       EM(netfs_trace_tcon_ref_free_ipc,               "FRE Ipc   ") \
+       EM(netfs_trace_tcon_ref_free_ipc_fail,          "FRE Ipc-F ") \
+       EM(netfs_trace_tcon_ref_free_reconnect_server,  "FRE Reconn") \
+       EM(netfs_trace_tcon_ref_get_cancelled_close,    "GET Cn-Cls") \
+       EM(netfs_trace_tcon_ref_get_dfs_refer,          "GET DfsRef") \
+       EM(netfs_trace_tcon_ref_get_find,               "GET Find  ") \
+       EM(netfs_trace_tcon_ref_get_find_sess_tcon,     "GET FndSes") \
+       EM(netfs_trace_tcon_ref_get_reconnect_server,   "GET Reconn") \
+       EM(netfs_trace_tcon_ref_new,                    "NEW       ") \
+       EM(netfs_trace_tcon_ref_new_ipc,                "NEW Ipc   ") \
+       EM(netfs_trace_tcon_ref_new_reconnect_server,   "NEW Reconn") \
+       EM(netfs_trace_tcon_ref_put_cancelled_close,    "PUT Cn-Cls") \
+       EM(netfs_trace_tcon_ref_put_cancelled_close_fid, "PUT Cn-Fid") \
+       EM(netfs_trace_tcon_ref_put_cancelled_mid,      "PUT Cn-Mid") \
+       EM(netfs_trace_tcon_ref_put_mnt_ctx,            "PUT MntCtx") \
+       EM(netfs_trace_tcon_ref_put_reconnect_server,   "PUT Reconn") \
+       EM(netfs_trace_tcon_ref_put_tlink,              "PUT Tlink ") \
+       EM(netfs_trace_tcon_ref_see_cancelled_close,    "SEE Cn-Cls") \
+       EM(netfs_trace_tcon_ref_see_fscache_collision,  "SEE FV-CO!") \
+       EM(netfs_trace_tcon_ref_see_fscache_okay,       "SEE FV-Ok ") \
+       EM(netfs_trace_tcon_ref_see_fscache_relinq,     "SEE FV-Rlq") \
+       E_(netfs_trace_tcon_ref_see_umount,             "SEE Umount")
+
+#undef EM
+#undef E_
+
+/*
+ * Define those tracing enums.
+ */
+#ifndef __SMB3_DECLARE_TRACE_ENUMS_ONCE_ONLY
+#define __SMB3_DECLARE_TRACE_ENUMS_ONCE_ONLY
+
+#define EM(a, b) a,
+#define E_(a, b) a
+
+enum smb3_tcon_ref_trace { smb3_tcon_ref_traces } __mode(byte);
+
+#undef EM
+#undef E_
+#endif
+
+/*
+ * Export enum symbols via userspace.
+ */
+#define EM(a, b) TRACE_DEFINE_ENUM(a);
+#define E_(a, b) TRACE_DEFINE_ENUM(a);
+
+smb3_tcon_ref_traces;
+
+#undef EM
+#undef E_
+
+/*
+ * Now redefine the EM() and E_() macros to map the enums to the strings that
+ * will be printed in the output.
  */
+#define EM(a, b)       { a, b },
+#define E_(a, b)       { a, b }
 
 /* For logging errors in read or write */
 DECLARE_EVENT_CLASS(smb3_rw_err_class,
@@ -1125,6 +1189,30 @@ DEFINE_SMB3_CREDIT_EVENT(waitff_credits);
 DEFINE_SMB3_CREDIT_EVENT(overflow_credits);
 DEFINE_SMB3_CREDIT_EVENT(set_credits);
 
+
+TRACE_EVENT(smb3_tcon_ref,
+           TP_PROTO(unsigned int tcon_debug_id, int ref,
+                    enum smb3_tcon_ref_trace trace),
+           TP_ARGS(tcon_debug_id, ref, trace),
+           TP_STRUCT__entry(
+                   __field(unsigned int,               tcon)
+                   __field(int,                        ref)
+                   __field(enum smb3_tcon_ref_trace,   trace)
+                            ),
+           TP_fast_assign(
+                   __entry->tcon       = tcon_debug_id;
+                   __entry->ref        = ref;
+                   __entry->trace      = trace;
+                          ),
+           TP_printk("TC=%08x %s r=%u",
+                     __entry->tcon,
+                     __print_symbolic(__entry->trace, smb3_tcon_ref_traces),
+                     __entry->ref)
+           );
+
+
+#undef EM
+#undef E_
 #endif /* _CIFS_TRACE_H */
 
 #undef TRACE_INCLUDE_PATH