diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 7d2124a537943..36af7cc89cafc 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -3107,34 +3107,7 @@ static inline gfp_t bpf_memcg_flags(gfp_t flags)
 	return flags;
 }
 
-#define BPF_GENERIC_TMP_LOG_SIZE	256
-
-struct bpf_generic_log {
-	char		kbuf[BPF_GENERIC_TMP_LOG_SIZE];
-	char __user	*ubuf;
-	u32		len_used;
-	u32		len_total;
-};
-
-__printf(2, 3) void bpf_generic_log_write(struct bpf_generic_log *log,
-			const char *fmt, ...);
-static inline void bpf_generic_log_init(struct bpf_generic_log *log,
-			const struct bpf_generic_user_log *ulog)
-{
-	log->ubuf = (char __user *) (unsigned long) ulog->log_buf;
-	log->len_total = ulog->log_size;
-	log->len_used = 0;
-}
-
-#define BPF_GENERIC_LOG_WRITE(log, ulog, fmt, ...)	do {	\
-	const char *____fmt = (fmt);				\
-	bpf_generic_log_init(log, ulog);			\
-	bpf_generic_log_write(log, ____fmt, ##__VA_ARGS__);	\
-} while (0)
-
-#define BPF_GENERIC_ULOG_WRITE(ulog, fmt, ...)	do {			\
-	struct bpf_generic_log ____log;					\
-	BPF_GENERIC_LOG_WRITE(&____log, ulog, fmt, ##__VA_ARGS__);	\
-} while (0)
+__printf(2, 3) void bpf_ulog_once(const struct bpf_generic_user_log *ulog,
+				  const char *fmt, ...);
 
 #endif /* _LINUX_BPF_H */
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 34fa334938ba5..8a458cfcd91bd 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -1549,7 +1549,6 @@ union bpf_attr {
 		};
 		__u32		attach_type;	/* attach type */
 		__u32		flags;		/* extra flags */
-		struct bpf_generic_user_log log; /* user log */
 		union {
 			__u32		target_btf_id;	/* btf_id of target to attach to */
 			struct {
@@ -1585,6 +1584,9 @@ union bpf_attr {
 				__s32		priority;
 				__u32		flags;
 			} netfilter;
+			struct {
+				struct bpf_generic_user_log ulog; /* user log */
+			} xdp;
 		};
 	} link_create;
 
diff --git a/kernel/bpf/log.c b/kernel/bpf/log.c
index be56b153bbf0b..4d197b52ea207 100644
--- a/kernel/bpf/log.c
+++ b/kernel/bpf/log.c
@@ -326,15 +326,32 @@ __printf(2, 3) void bpf_log(struct bpf_verifier_log *log,
 }
 EXPORT_SYMBOL_GPL(bpf_log);
 
-static inline void __bpf_generic_log_write(struct bpf_generic_log *log, const char *fmt,
-				      va_list args)
+#define BPF_USER_TMP_LOG_SIZE	256
+
+struct bpf_user_log {
+	char		kbuf[BPF_USER_TMP_LOG_SIZE];
+	char __user	*ubuf;
+	u32		len_used;
+	u32		len_total;
+};
+
+static inline void bpf_ulog_init(struct bpf_user_log *log,
+				 const struct bpf_generic_user_log *ulog)
+{
+	log->ubuf = (char __user *) (unsigned long) ulog->log_buf;
+	log->len_total = ulog->log_size;
+	log->len_used = 0;
+}
+
+static inline void bpf_ulog_write(struct bpf_user_log *log,
+				  const char *fmt, va_list args)
 {
 	unsigned int n;
 
-	n = vscnprintf(log->kbuf, BPF_GENERIC_TMP_LOG_SIZE, fmt, args);
+	n = vscnprintf(log->kbuf, BPF_USER_TMP_LOG_SIZE, fmt, args);
 
-	WARN_ONCE(n >= BPF_GENERIC_TMP_LOG_SIZE - 1,
-		  "bpf generic log truncated - local buffer too short\n");
+	WARN_ONCE(n >= BPF_USER_TMP_LOG_SIZE - 1,
+		  "bpf user log line truncated - local buffer too short\n");
 
 	n = min(log->len_total - log->len_used - 1, n);
 	log->kbuf[n] = '\0';
@@ -345,16 +362,18 @@ static inline void __bpf_generic_log_write(struct bpf_generic_log *log, const ch
 		log->ubuf = NULL;
 }
 
-__printf(2, 3) void bpf_generic_log_write(struct bpf_generic_log *log,
-				     const char *fmt, ...)
+__printf(2, 3) void bpf_ulog_once(const struct bpf_generic_user_log *ulog,
+				  const char *fmt, ...)
 {
+	struct bpf_user_log log;
 	va_list args;
 
-	if (!log->ubuf || !log->len_total)
+	if (!ulog->log_buf || !ulog->log_size)
 		return;
 
+	bpf_ulog_init(&log, ulog);
 	va_start(args, fmt);
-	__bpf_generic_log_write(log, fmt, args);
+	bpf_ulog_write(&log, fmt, args);
 	va_end(args);
 }
-EXPORT_SYMBOL_GPL(bpf_generic_log_write);
+EXPORT_SYMBOL_GPL(bpf_ulog_once);
diff --git a/net/core/dev.c b/net/core/dev.c
index e933809c0a72f..311d3f3bc11b6 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -9409,13 +9409,6 @@ static const struct bpf_link_ops bpf_xdp_link_lops = {
 	.update_prog = bpf_xdp_link_update,
 };
 
-static inline void bpf_xdp_link_log(const union bpf_attr *attr, struct netlink_ext_ack *extack)
-{
-	const struct bpf_generic_user_log *ulog = &attr->link_create.log;
-
-	BPF_GENERIC_ULOG_WRITE(ulog, extack->_msg);
-}
-
 int bpf_xdp_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
 {
 	struct net *net = current->nsproxy->net_ns;
@@ -9454,7 +9447,7 @@ int bpf_xdp_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
 	if (err) {
 		link->dev = NULL;
 		bpf_link_cleanup(&link_primer);
-		bpf_xdp_link_log(attr, &extack);
+		bpf_ulog_once(&attr->link_create.xdp.ulog, extack._msg);
 		goto out_put_dev;
 	}
 
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 34fa334938ba5..b81508bf758fa 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -1549,7 +1549,6 @@ union bpf_attr {
 		};
 		__u32		attach_type;	/* attach type */
 		__u32		flags;		/* extra flags */
-		struct bpf_generic_user_log log; /* user log */
 		union {
 			__u32		target_btf_id;	/* btf_id of target to attach to */
 			struct {
@@ -1585,6 +1584,9 @@ union bpf_attr {
 				__s32		priority;
 				__u32		flags;
 			} netfilter;
+			struct {
+				struct bpf_generic_user_log ulog;  /* user log */
+			} xdp;
 		};
 	} link_create;
 
