]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
smb: smbdirect: introduce smbdirect_socket_create_{kern,accepting}() and smbdirect_so...
authorStefan Metzmacher <metze@samba.org>
Tue, 21 Oct 2025 14:48:12 +0000 (16:48 +0200)
committerSteve French <stfrench@microsoft.com>
Thu, 16 Apr 2026 02:58:20 +0000 (21:58 -0500)
This provides functions which also allocate and free struct
smbdirect_socket.

This allows callers to use the same flow as with
sock_create_kern()/sock_release().

The end goal would be to use sock_create_kern()/sock_release(), but the
first step will be to use smbdirect specific functions without any
struct socket nor struct sock.

Cc: Steve French <smfrench@gmail.com>
Cc: Tom Talpey <tom@talpey.com>
Cc: Long Li <longli@microsoft.com>
Cc: Namjae Jeon <linkinjeon@kernel.org>
Cc: linux-cifs@vger.kernel.org
Cc: samba-technical@lists.samba.org
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Acked-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/common/smbdirect/smbdirect_socket.c
fs/smb/common/smbdirect/smbdirect_socket.h

index 8eb021cd7ceec0cb6c52401b20d98969659a8499..f70cd395812bd1ec57950c072a8d1e173383ea03 100644 (file)
@@ -51,7 +51,6 @@ static int smbdirect_socket_rdma_event_handler(struct rdma_cm_id *id,
        return -ESTALE;
 }
 
-__maybe_unused /* this is temporary while this file is included in others */
 static int smbdirect_socket_init_new(struct net *net, struct smbdirect_socket *sc)
 {
        struct rdma_cm_id *id;
@@ -85,6 +84,31 @@ static int smbdirect_socket_init_new(struct net *net, struct smbdirect_socket *s
 }
 
 __maybe_unused /* this is temporary while this file is included in others */
+static int smbdirect_socket_create_kern(struct net *net, struct smbdirect_socket **_sc)
+{
+       struct smbdirect_socket *sc;
+       int ret;
+
+       ret = -ENOMEM;
+       sc = kzalloc_obj(*sc);
+       if (!sc)
+               goto alloc_failed;
+
+       ret = smbdirect_socket_init_new(net, sc);
+       if (ret)
+               goto init_failed;
+
+       kref_init(&sc->refs.destroy);
+
+       *_sc = sc;
+       return 0;
+
+init_failed:
+       kfree(sc);
+alloc_failed:
+       return ret;
+}
+
 static int smbdirect_socket_init_accepting(struct rdma_cm_id *id, struct smbdirect_socket *sc)
 {
        smbdirect_socket_init(sc);
@@ -100,6 +124,32 @@ static int smbdirect_socket_init_accepting(struct rdma_cm_id *id, struct smbdire
        return 0;
 }
 
+__maybe_unused /* this is temporary while this file is included in others */
+static int smbdirect_socket_create_accepting(struct rdma_cm_id *id, struct smbdirect_socket **_sc)
+{
+       struct smbdirect_socket *sc;
+       int ret;
+
+       ret = -ENOMEM;
+       sc = kzalloc_obj(*sc);
+       if (!sc)
+               goto alloc_failed;
+
+       ret = smbdirect_socket_init_accepting(id, sc);
+       if (ret)
+               goto init_failed;
+
+       kref_init(&sc->refs.destroy);
+
+       *_sc = sc;
+       return 0;
+
+init_failed:
+       kfree(sc);
+alloc_failed:
+       return ret;
+}
+
 __maybe_unused /* this is temporary while this file is included in others */
 static int smbdirect_socket_set_initial_parameters(struct smbdirect_socket *sc,
                                                   const struct smbdirect_socket_parameters *sp)
@@ -556,6 +606,49 @@ static void smbdirect_socket_shutdown(struct smbdirect_socket *sc)
        smbdirect_socket_schedule_cleanup_lvl(sc, SMBDIRECT_LOG_INFO, -ESHUTDOWN);
 }
 
+static void smbdirect_socket_release_disconnect(struct kref *kref)
+{
+       struct smbdirect_socket *sc =
+               container_of(kref, struct smbdirect_socket, refs.disconnect);
+
+       /*
+        * For now do a sync disconnect/destroy
+        */
+       smbdirect_socket_destroy_sync(sc);
+}
+
+static void smbdirect_socket_release_destroy(struct kref *kref)
+{
+       struct smbdirect_socket *sc =
+               container_of(kref, struct smbdirect_socket, refs.destroy);
+
+       /*
+        * Do a sync disconnect/destroy...
+        * hopefully a no-op, as it should be already
+        * in DESTROYED state, before we free the memory.
+        */
+       smbdirect_socket_destroy_sync(sc);
+       kfree(sc);
+}
+
+__maybe_unused /* this is temporary while this file is included in others */
+static void smbdirect_socket_release(struct smbdirect_socket *sc)
+{
+       /*
+        * We expect only 1 disconnect reference
+        * and if it is already 0, it's a use after free!
+        */
+       WARN_ON_ONCE(kref_read(&sc->refs.disconnect) != 1);
+       WARN_ON(!kref_put(&sc->refs.disconnect, smbdirect_socket_release_disconnect));
+
+       /*
+        * This may not trigger smbdirect_socket_release_destroy(),
+        * if struct smbdirect_socket is embedded in another structure
+        * indicated by REFCOUNT_MAX.
+        */
+       kref_put(&sc->refs.destroy, smbdirect_socket_release_destroy);
+}
+
 __maybe_unused /* this is temporary while this file is included in others */
 static int smbdirect_socket_wait_for_credits(struct smbdirect_socket *sc,
                                             enum smbdirect_socket_status expected_status,
index 97e6330249ccb5cd2ddbb1097ad45f9a9718da53..5a6386e4a0216ccf6ee637985100c428b82ae970 100644 (file)
@@ -108,6 +108,36 @@ struct smbdirect_socket {
 
        struct work_struct disconnect_work;
 
+       /*
+        * The reference counts.
+        */
+       struct {
+               /*
+                * This holds the references by the
+                * frontend, typically the smb layer.
+                *
+                * It is typically 1 and a disconnect
+                * will happen if it reaches 0.
+                */
+               struct kref disconnect;
+
+               /*
+                * This holds the reference by the
+                * backend, the code that manages
+                * the lifetime of the whole
+                * struct smbdirect_socket,
+                * if this reaches 0 it can will
+                * be freed.
+                *
+                * Can be REFCOUNT_MAX is part
+                * of another structure.
+                *
+                * This is equal or higher than
+                * the disconnect refcount.
+                */
+               struct kref destroy;
+       } refs;
+
        /* RDMA related */
        struct {
                struct rdma_cm_id *cm_id;
@@ -513,6 +543,9 @@ static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc)
        INIT_WORK(&sc->disconnect_work, __smbdirect_socket_disabled_work);
        disable_work_sync(&sc->disconnect_work);
 
+       kref_init(&sc->refs.disconnect);
+       sc->refs.destroy = (struct kref) KREF_INIT(REFCOUNT_MAX);
+
        sc->rdma.expected_event = RDMA_CM_EVENT_INTERNAL;
 
        sc->ib.poll_ctx = IB_POLL_UNBOUND_WORKQUEUE;