summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2025-10-21 17:48:12 +0300
committerSteve French <stfrench@microsoft.com>2026-04-16 05:58:20 +0300
commit20cd3cc4420bdb9f63644cd140e2682f634e651e (patch)
tree7a1b88f04f9cc3edeed8bee4ca2196f07d3bf688
parenteb3ed1e9048cf7b2a38112f48e0f1b772bb7860d (diff)
downloadlinux-20cd3cc4420bdb9f63644cd140e2682f634e651e.tar.xz
smb: smbdirect: introduce smbdirect_socket_create_{kern,accepting}() and smbdirect_socket_release()
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>
-rw-r--r--fs/smb/common/smbdirect/smbdirect_socket.c95
-rw-r--r--fs/smb/common/smbdirect/smbdirect_socket.h33
2 files changed, 127 insertions, 1 deletions
diff --git a/fs/smb/common/smbdirect/smbdirect_socket.c b/fs/smb/common/smbdirect/smbdirect_socket.c
index 8eb021cd7cee..f70cd395812b 100644
--- a/fs/smb/common/smbdirect/smbdirect_socket.c
+++ b/fs/smb/common/smbdirect/smbdirect_socket.c
@@ -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);
@@ -101,6 +125,32 @@ static int smbdirect_socket_init_accepting(struct rdma_cm_id *id, struct smbdire
}
__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,
diff --git a/fs/smb/common/smbdirect/smbdirect_socket.h b/fs/smb/common/smbdirect/smbdirect_socket.h
index 97e6330249cc..5a6386e4a021 100644
--- a/fs/smb/common/smbdirect/smbdirect_socket.h
+++ b/fs/smb/common/smbdirect/smbdirect_socket.h
@@ -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;