From: Stefan Metzmacher Date: Tue, 21 Oct 2025 14:48:12 +0000 (+0200) Subject: smb: smbdirect: introduce smbdirect_socket_create_{kern,accepting}() and smbdirect_so... X-Git-Url: http://git.ipfire.org/index.cgi?a=commitdiff_plain;h=20cd3cc4420bdb9f63644cd140e2682f634e651e;p=thirdparty%2Fkernel%2Flinux.git 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 Cc: Tom Talpey Cc: Long Li Cc: Namjae Jeon Cc: linux-cifs@vger.kernel.org Cc: samba-technical@lists.samba.org Signed-off-by: Stefan Metzmacher Acked-by: Namjae Jeon Signed-off-by: Steve French --- diff --git a/fs/smb/common/smbdirect/smbdirect_socket.c b/fs/smb/common/smbdirect/smbdirect_socket.c index 8eb021cd7ceec..f70cd395812bd 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); @@ -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, diff --git a/fs/smb/common/smbdirect/smbdirect_socket.h b/fs/smb/common/smbdirect/smbdirect_socket.h index 97e6330249ccb..5a6386e4a0216 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;