* Re: [PATCH] vsock: use sock_error() to consume sk_err after connect timeout
From: Stefano Garzarella @ 2026-07-20 8:17 UTC (permalink / raw)
To: Nguyen Dinh Phi
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, syzbot+1b2c9c4a0f8708082678, virtualization, netdev,
linux-kernel
In-Reply-To: <20260719220103.684489-1-phind.uet@gmail.com>
On Mon, Jul 20, 2026 at 05:57:47AM +0800, Nguyen Dinh Phi wrote:
>After vsock_connect() exits the wait loop due to sk->sk_err being
>set, the error was read but not cleared. This left sk->sk_err set
>for subsequent operations.
So, is this a fix? If yes, we should put a Fixes tag.
Also, can you describe how to trigger the issue?
Because I see this in vsock_connect(), so I thought it was in some way
already handled:
/* sk_err might have been set as a result of an earlier
* (failed) connect attempt.
*/
sk->sk_err = 0;
>Switch to sock_error() which atomically reads and clears sk->sk_err,
>so the error is consumed when returned.
>
>Signed-off-by: Nguyen Dinh Phi <phind.uet@gmail.com>
>Reported-by: syzbot+1b2c9c4a0f8708082678@syzkaller.appspotmail.com
Can you explain how this patch fixes that issue?
(this should be the first information to be put in the commit message
IMHO)
I'd like to understand better if this is a fix of real bug or just an
improvement to the code (which is fine by me).
Thanks,
Stefano
>---
> net/vmw_vsock/af_vsock.c | 7 ++-----
> 1 file changed, 2 insertions(+), 5 deletions(-)
>
>diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
>index 622dbd046799..43eddc33ed12 100644
>--- a/net/vmw_vsock/af_vsock.c
>+++ b/net/vmw_vsock/af_vsock.c
>@@ -1847,14 +1847,11 @@ static int vsock_connect(struct socket *sock, struct sockaddr_unsized *addr,
> prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
> }
>
>- if (sk->sk_err) {
>- err = -sk->sk_err;
>+ err = sock_error(sk);
>+ if (err) {
> sk->sk_state = TCP_CLOSE;
> sock->state = SS_UNCONNECTED;
>- } else {
>- err = 0;
> }
>-
> out_wait:
> finish_wait(sk_sleep(sk), &wait);
> out:
>--
>2.53.0
>
^ permalink raw reply
* [PATCH net v4] sctp: socket: refactor sctp_skb_recv_datagram to use ERR_PTR
From: luoqing @ 2026-07-20 8:21 UTC (permalink / raw)
To: marcelo.leitner, lucien.xin, davem, edumazet, kuba, pabeni
Cc: horms, linux-sctp, netdev, linux-kernel
From: Qing Luo <luoqing@kylinos.cn>
The err output parameter in sctp_skb_recv_datagram() is passed to
callers but never validated, making error reporting unreliable.
Remove it and use ERR_PTR to encode errors directly in the return
value, which is the standard kernel pattern for this case.
Signed-off-by: Qing Luo <luoqing@kylinos.cn>
---
include/net/sctp/sctp.h | 2 +-
net/sctp/socket.c | 20 ++++++++++----------
net/sctp/ulpevent.c | 5 ++---
3 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index d50c27812504..b86d50d6b146 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -97,7 +97,7 @@ void sctp_sock_rfree(struct sk_buff *skb);
extern struct percpu_counter sctp_sockets_allocated;
int sctp_asconf_mgmt(struct sctp_sock *, struct sctp_sockaddr_entry *);
-struct sk_buff *sctp_skb_recv_datagram(struct sock *, int, int *);
+struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags);
typedef int (*sctp_callback_t)(struct sctp_endpoint *, struct sctp_transport *, void *);
void sctp_transport_walk_start(struct rhashtable_iter *iter);
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index c7b9e325ec1c..2deaa498e6cf 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -2123,9 +2123,11 @@ static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
goto out;
}
- skb = sctp_skb_recv_datagram(sk, flags, &err);
- if (!skb)
+ skb = sctp_skb_recv_datagram(sk, flags);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
goto out;
+ }
/* Get the total length of the skb including any skb's in the
* frag_list.
@@ -9082,7 +9084,7 @@ static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p)
* Note: This is pretty much the same routine as in core/datagram.c
* with a few changes to make lksctp work.
*/
-struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, int *err)
+struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags)
{
int error;
struct sk_buff *skb;
@@ -9117,21 +9119,19 @@ struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, int *err)
if (error)
goto no_packet;
- if (sk->sk_shutdown & RCV_SHUTDOWN)
+ if (sk->sk_shutdown & RCV_SHUTDOWN) {
+ error = 0;
break;
-
+ }
/* User doesn't want to wait. */
error = -EAGAIN;
if (!timeo)
goto no_packet;
- } while (sctp_wait_for_packet(sk, err, &timeo) == 0);
-
- return NULL;
+ } while (sctp_wait_for_packet(sk, &error, &timeo) == 0);
no_packet:
- *err = error;
- return NULL;
+ return ERR_PTR(error);
}
/* If sndbuf has changed, wake up per association sndbuf waiters. */
diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c
index 8920ca92a011..21ae0adbaeef 100644
--- a/net/sctp/ulpevent.c
+++ b/net/sctp/ulpevent.c
@@ -1061,10 +1061,9 @@ void sctp_ulpevent_read_nxtinfo(const struct sctp_ulpevent *event,
struct sock *sk)
{
struct sk_buff *skb;
- int err;
- skb = sctp_skb_recv_datagram(sk, MSG_PEEK | MSG_DONTWAIT, &err);
- if (skb != NULL) {
+ skb = sctp_skb_recv_datagram(sk, MSG_PEEK | MSG_DONTWAIT);
+ if (!IS_ERR_OR_NULL(skb)) {
__sctp_ulpevent_read_nxtinfo(sctp_skb2event(skb),
msghdr, skb);
/* Just release refcount here. */
--
2.25.1
>> I think it's used at [1] in sctp_recvmsg():
>>
>> skb = sctp_skb_recv_datagram(sk, flags, &err);
>> if (!skb)
>> goto out;
> Would it make more sense to ERR_PTR() etc ?
>
>
> David
Yes, you are right. The current implementation returns a negative error
code directly via err, but it would be cleaner to use ERR_PTR() to
unify the error path with other datagram receivers.
I will refactor this part in v4:
>
>
>> ...
>>
>> out:
>> release_sock(sk);
>> return err; <------ [1]
^ permalink raw reply related
* [PATCH v5 0/3] Add drm_ras netlink error event support
From: Riana Tauro @ 2026-07-20 8:22 UTC (permalink / raw)
To: intel-xe, dri-devel, netdev
Cc: aravind.iddamsetty, anshuman.gupta, rodrigo.vivi, joonas.lahtinen,
kuba, simona.vetter, airlied, pratik.bari, joshua.santosh.ranjan,
ashwin.kumar.kulkarni, shubham.kumar, ravi.kishore.koppuravuri,
raag.jadav, maarten.lankhorst, mallesh.koujalagi, soham.purkait,
Riana Tauro
Define a new netlink event 'error-event' and a new multicast group
'error-report' in drm_-as. Each event contains device name, node and
error information to identify the error triggering the event.
Add drm_ras_nl_error_event() to trigger an event from the driver.
Wire this support to xe_drm_ras to report to userspace whenever a
correctable/uncorrectable error occurs on CRI.
$ sudo ynl --family drm_ras --output-json --subscribe error-report
{
"name": "error-event",
"msg": {
"device-name": "0000:03:00.0",
"node-id": 1,
"node-name": "uncorrectable-errors",
"error-id": 1,
"error-name": "core-compute",
"error-value": 1
}
}
Rev2: use ynl in document and commit message
fix cosmetic review comments
simplify caller
Rev3: replace error-event with error-report
had has_drm_ras check
add support for correctable errors in CRI
Rev4: send an event at most once per component for each interrupt
add xe_warn for unexpected values from firmware
fix sashiko reported issues
Rev5: Remove has_listeners
send netlink event to all network namespaces
Riana Tauro (3):
drm/drm_ras: Add drm_ras netlink error event
drm/xe/xe_ras: Report correctable error events to userspace
drm/xe/xe_ras: Report uncorrectable error events to userspace
Documentation/gpu/drm-ras.rst | 21 ++++++
Documentation/netlink/specs/drm_ras.yaml | 48 ++++++++++++++
drivers/gpu/drm/drm_ras.c | 84 ++++++++++++++++++++++++
drivers/gpu/drm/drm_ras_nl.c | 6 ++
drivers/gpu/drm/drm_ras_nl.h | 4 ++
drivers/gpu/drm/xe/xe_drm_ras.c | 42 ++++++++++++
drivers/gpu/drm/xe/xe_drm_ras.h | 3 +
drivers/gpu/drm/xe/xe_ras.c | 74 +++++++++++++++++++++
include/drm/drm_ras.h | 5 ++
include/uapi/drm/drm_ras.h | 15 +++++
10 files changed, 302 insertions(+)
--
2.47.1
^ permalink raw reply
* [PATCH v5 1/3] drm/drm_ras: Add drm_ras netlink error event
From: Riana Tauro @ 2026-07-20 8:22 UTC (permalink / raw)
To: intel-xe, dri-devel, netdev
Cc: aravind.iddamsetty, anshuman.gupta, rodrigo.vivi, joonas.lahtinen,
kuba, simona.vetter, airlied, pratik.bari, joshua.santosh.ranjan,
ashwin.kumar.kulkarni, shubham.kumar, ravi.kishore.koppuravuri,
raag.jadav, maarten.lankhorst, mallesh.koujalagi, soham.purkait,
Riana Tauro, Zack McKevitt, Lijo Lazar, Hawking Zhang,
David S. Miller, Paolo Abeni, Eric Dumazet
In-Reply-To: <20260720082208.2648279-5-riana.tauro@intel.com>
Define a new netlink event 'error-event' and a new multicast group
'error-report' in drm_ras. Each event contains device name, node and
error information to identify the error triggering the event.
Add drm_ras_nl_error_event() to trigger an event from the driver.
Userspace must subscribe to 'error-report' to receive 'error-event'
notifications.
Usage:
$ sudo ynl --family drm_ras --subscribe error-report
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Zack McKevitt <zachary.mckevitt@oss.qualcomm.com>
Cc: Lijo Lazar <lijo.lazar@amd.com>
Cc: Hawking Zhang <Hawking.Zhang@amd.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Eric Dumazet <edumazet@google.com>
Signed-off-by: Riana Tauro <riana.tauro@intel.com>
Reviewed-by: Raag Jadav <raag.jadav@intel.com>
---
v2: remove redundant initialization
remove unnecessary space
use ynl in commit message and doc (Raag)
simplify doc for error-event attrs
v3: rename error-notify to error-report
Replace notify with report across the file (Raag)
v4: send event to all network namespaces (Sashiko)
remove has_listeners check
---
Documentation/gpu/drm-ras.rst | 21 ++++++
Documentation/netlink/specs/drm_ras.yaml | 48 ++++++++++++++
drivers/gpu/drm/drm_ras.c | 84 ++++++++++++++++++++++++
drivers/gpu/drm/drm_ras_nl.c | 6 ++
drivers/gpu/drm/drm_ras_nl.h | 4 ++
include/drm/drm_ras.h | 5 ++
include/uapi/drm/drm_ras.h | 15 +++++
7 files changed, 183 insertions(+)
diff --git a/Documentation/gpu/drm-ras.rst b/Documentation/gpu/drm-ras.rst
index 83c21853b74b..406e4c49bac1 100644
--- a/Documentation/gpu/drm-ras.rst
+++ b/Documentation/gpu/drm-ras.rst
@@ -56,6 +56,7 @@ User space tools can:
``node-id`` and ``error-id`` as parameters.
* Clear specific error counters with the ``clear-error-counter`` command, using both
``node-id`` and ``error-id`` as parameters.
+* Subscribe to the ``error-report`` multicast group to receive ``error-event``.
YAML-based Interface
--------------------
@@ -111,3 +112,23 @@ Example: Clear an error counter for a given node
sudo ynl --family drm_ras --do clear-error-counter --json '{"node-id":0, "error-id":1}'
None
+
+Example: Subscribe to ``error-report`` multicast group
+
+.. code-block:: bash
+
+ sudo ynl --family drm_ras --output-json --subscribe error-report
+
+.. code-block:: json
+
+ {
+ "name": "error-event",
+ "msg": {
+ "device-name": "0000:03:00.0",
+ "node-id": 1,
+ "node-name": "uncorrectable-errors",
+ "error-id": 1,
+ "error-name": "error_name1",
+ "error-value": 1
+ }
+ }
diff --git a/Documentation/netlink/specs/drm_ras.yaml b/Documentation/netlink/specs/drm_ras.yaml
index e113056f8c01..8aed3d4515e5 100644
--- a/Documentation/netlink/specs/drm_ras.yaml
+++ b/Documentation/netlink/specs/drm_ras.yaml
@@ -69,6 +69,33 @@ attribute-sets:
name: error-value
type: u32
doc: Current value of the requested error counter.
+ -
+ name: error-event-attrs
+ attributes:
+ -
+ name: device-name
+ type: string
+ doc: Device (PCI BDF, UUID) that reported the error.
+ -
+ name: node-id
+ type: u32
+ doc: ID of the node that reported the error.
+ -
+ name: node-name
+ type: string
+ doc: Name of the node that reported the error.
+ -
+ name: error-id
+ type: u32
+ doc: ID of the error counter.
+ -
+ name: error-name
+ type: string
+ doc: Name of the error.
+ -
+ name: error-value
+ type: u32
+ doc: Current value of the error counter.
operations:
list:
@@ -124,3 +151,24 @@ operations:
do:
request:
attributes: *id-attrs
+ -
+ name: error-event
+ doc: >-
+ Report an error event to userspace.
+ The event includes the device, node and error information
+ of the error that triggered the event.
+ attribute-set: error-event-attrs
+ mcgrp: error-report
+ event:
+ attributes:
+ - device-name
+ - node-id
+ - node-name
+ - error-id
+ - error-name
+ - error-value
+
+mcast-groups:
+ list:
+ -
+ name: error-report
diff --git a/drivers/gpu/drm/drm_ras.c b/drivers/gpu/drm/drm_ras.c
index d6eab29a1394..0b77e4358ea8 100644
--- a/drivers/gpu/drm/drm_ras.c
+++ b/drivers/gpu/drm/drm_ras.c
@@ -41,6 +41,11 @@
* Userspace must provide Node ID, Error ID.
* Clears specific error counter of a node if supported.
*
+ * 4. ERROR_REPORT: Subscribe to this multicast group to receive error events
+ *
+ * 5. ERROR_EVENT: Report an error event to userspace. The event contains device, node
+ * and error information that triggered the event.
+ *
* Node registration:
*
* - drm_ras_node_register(): Registers a new node and assigns
@@ -186,6 +191,34 @@ static int msg_reply_value(struct sk_buff *msg, u32 error_id,
value);
}
+static int msg_put_error_event_attrs(struct sk_buff *msg, struct drm_ras_node *node,
+ u32 error_id, const char *error_name, u32 value)
+{
+ int ret;
+
+ ret = nla_put_string(msg, DRM_RAS_A_ERROR_EVENT_ATTRS_DEVICE_NAME, node->device_name);
+ if (ret)
+ return ret;
+
+ ret = nla_put_u32(msg, DRM_RAS_A_ERROR_EVENT_ATTRS_NODE_ID, node->id);
+ if (ret)
+ return ret;
+
+ ret = nla_put_string(msg, DRM_RAS_A_ERROR_EVENT_ATTRS_NODE_NAME, node->node_name);
+ if (ret)
+ return ret;
+
+ ret = nla_put_u32(msg, DRM_RAS_A_ERROR_EVENT_ATTRS_ERROR_ID, error_id);
+ if (ret)
+ return ret;
+
+ ret = nla_put_string(msg, DRM_RAS_A_ERROR_EVENT_ATTRS_ERROR_NAME, error_name);
+ if (ret)
+ return ret;
+
+ return nla_put_u32(msg, DRM_RAS_A_ERROR_EVENT_ATTRS_ERROR_VALUE, value);
+}
+
static int doit_reply_value(struct genl_info *info, u32 node_id,
u32 error_id)
{
@@ -222,6 +255,57 @@ static int doit_reply_value(struct genl_info *info, u32 node_id,
return genlmsg_reply(msg, info);
}
+/**
+ * drm_ras_nl_error_event() - Report an error event
+ * @node: Node structure
+ * @error_id: ID of the error
+ * @error_name: Name of the error
+ * @value: Value associated with the error
+ *
+ * Report an error-event to userspace using the error-report multicast group.
+ *
+ * Return: 0 on success, or negative errno on failure.
+ */
+int drm_ras_nl_error_event(struct drm_ras_node *node, u32 error_id, const char *error_name,
+ u32 value)
+{
+ struct genl_info info;
+ struct sk_buff *msg;
+ struct nlattr *hdr;
+ int ret;
+
+ if (!error_name)
+ return -EINVAL;
+
+ genl_info_init_ntf(&info, &drm_ras_nl_family, DRM_RAS_CMD_ERROR_EVENT);
+
+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = genlmsg_iput(msg, &info);
+ if (!hdr) {
+ ret = -EMSGSIZE;
+ goto free_msg;
+ }
+
+ ret = msg_put_error_event_attrs(msg, node, error_id, error_name, value);
+ if (ret)
+ goto cancel_msg;
+
+ genlmsg_end(msg, hdr);
+ genlmsg_multicast_allns(&drm_ras_nl_family, msg, 0, DRM_RAS_NLGRP_ERROR_REPORT);
+
+ return 0;
+
+cancel_msg:
+ genlmsg_cancel(msg, hdr);
+free_msg:
+ nlmsg_free(msg);
+ return ret;
+}
+EXPORT_SYMBOL(drm_ras_nl_error_event);
+
/**
* drm_ras_nl_get_error_counter_dumpit() - Dump all Error Counters
* @skb: Netlink message buffer
diff --git a/drivers/gpu/drm/drm_ras_nl.c b/drivers/gpu/drm/drm_ras_nl.c
index dea1c1b2494e..9d3123cc9f9c 100644
--- a/drivers/gpu/drm/drm_ras_nl.c
+++ b/drivers/gpu/drm/drm_ras_nl.c
@@ -58,6 +58,10 @@ static const struct genl_split_ops drm_ras_nl_ops[] = {
},
};
+static const struct genl_multicast_group drm_ras_nl_mcgrps[] = {
+ [DRM_RAS_NLGRP_ERROR_REPORT] = { "error-report", },
+};
+
struct genl_family drm_ras_nl_family __ro_after_init = {
.name = DRM_RAS_FAMILY_NAME,
.version = DRM_RAS_FAMILY_VERSION,
@@ -66,4 +70,6 @@ struct genl_family drm_ras_nl_family __ro_after_init = {
.module = THIS_MODULE,
.split_ops = drm_ras_nl_ops,
.n_split_ops = ARRAY_SIZE(drm_ras_nl_ops),
+ .mcgrps = drm_ras_nl_mcgrps,
+ .n_mcgrps = ARRAY_SIZE(drm_ras_nl_mcgrps),
};
diff --git a/drivers/gpu/drm/drm_ras_nl.h b/drivers/gpu/drm/drm_ras_nl.h
index a398643572a5..03ec275aca92 100644
--- a/drivers/gpu/drm/drm_ras_nl.h
+++ b/drivers/gpu/drm/drm_ras_nl.h
@@ -21,6 +21,10 @@ int drm_ras_nl_get_error_counter_dumpit(struct sk_buff *skb,
int drm_ras_nl_clear_error_counter_doit(struct sk_buff *skb,
struct genl_info *info);
+enum {
+ DRM_RAS_NLGRP_ERROR_REPORT,
+};
+
extern struct genl_family drm_ras_nl_family;
#endif /* _LINUX_DRM_RAS_GEN_H */
diff --git a/include/drm/drm_ras.h b/include/drm/drm_ras.h
index 0beede3ddc4e..ee2caa0edc6f 100644
--- a/include/drm/drm_ras.h
+++ b/include/drm/drm_ras.h
@@ -80,9 +80,14 @@ struct drm_device;
#if IS_ENABLED(CONFIG_DRM_RAS)
int drm_ras_node_register(struct drm_ras_node *node);
void drm_ras_node_unregister(struct drm_ras_node *node);
+int drm_ras_nl_error_event(struct drm_ras_node *node, u32 error_id, const char *error_name,
+ u32 value);
#else
static inline int drm_ras_node_register(struct drm_ras_node *node) { return 0; }
static inline void drm_ras_node_unregister(struct drm_ras_node *node) { }
+static inline int drm_ras_nl_error_event(struct drm_ras_node *node, u32 error_id,
+ const char *error_name, u32 value)
+{ return 0; }
#endif
#endif
diff --git a/include/uapi/drm/drm_ras.h b/include/uapi/drm/drm_ras.h
index 218a3ee86805..eab8231aa87c 100644
--- a/include/uapi/drm/drm_ras.h
+++ b/include/uapi/drm/drm_ras.h
@@ -38,13 +38,28 @@ enum {
DRM_RAS_A_ERROR_COUNTER_ATTRS_MAX = (__DRM_RAS_A_ERROR_COUNTER_ATTRS_MAX - 1)
};
+enum {
+ DRM_RAS_A_ERROR_EVENT_ATTRS_DEVICE_NAME = 1,
+ DRM_RAS_A_ERROR_EVENT_ATTRS_NODE_ID,
+ DRM_RAS_A_ERROR_EVENT_ATTRS_NODE_NAME,
+ DRM_RAS_A_ERROR_EVENT_ATTRS_ERROR_ID,
+ DRM_RAS_A_ERROR_EVENT_ATTRS_ERROR_NAME,
+ DRM_RAS_A_ERROR_EVENT_ATTRS_ERROR_VALUE,
+
+ __DRM_RAS_A_ERROR_EVENT_ATTRS_MAX,
+ DRM_RAS_A_ERROR_EVENT_ATTRS_MAX = (__DRM_RAS_A_ERROR_EVENT_ATTRS_MAX - 1)
+};
+
enum {
DRM_RAS_CMD_LIST_NODES = 1,
DRM_RAS_CMD_GET_ERROR_COUNTER,
DRM_RAS_CMD_CLEAR_ERROR_COUNTER,
+ DRM_RAS_CMD_ERROR_EVENT,
__DRM_RAS_CMD_MAX,
DRM_RAS_CMD_MAX = (__DRM_RAS_CMD_MAX - 1)
};
+#define DRM_RAS_MCGRP_ERROR_REPORT "error-report"
+
#endif /* _UAPI_LINUX_DRM_RAS_H */
--
2.47.1
^ permalink raw reply related
* [PATCH v5 2/3] drm/xe/xe_ras: Report correctable error events to userspace
From: Riana Tauro @ 2026-07-20 8:22 UTC (permalink / raw)
To: intel-xe, dri-devel, netdev
Cc: aravind.iddamsetty, anshuman.gupta, rodrigo.vivi, joonas.lahtinen,
kuba, simona.vetter, airlied, pratik.bari, joshua.santosh.ranjan,
ashwin.kumar.kulkarni, shubham.kumar, ravi.kishore.koppuravuri,
raag.jadav, maarten.lankhorst, mallesh.koujalagi, soham.purkait,
Riana Tauro, Michal Wajdeczko
In-Reply-To: <20260720082208.2648279-5-riana.tauro@intel.com>
When an interrupt is received indicating that error counter has crossed
its threshold, read the current counter value and deliver a drm_ras error
event to userspace for each affected component.
To avoid sending duplicate events when the same component appears multiple
times in the response. Send the error-event once per component.
Cc: Michal Wajdeczko <michal.wajdeczko@intel.com>
Signed-off-by: Riana Tauro <riana.tauro@intel.com>
---
v2: add warns for unexpected values from system controller (Michal)
send an event at most once per component for each interrupt (Raag)
use correct parameters for get_counter (Sashiko)
v3: move unsupported logs to drm_ras layer
use get_counter directly
use BITS_PER_TYPE
move the checks before detected log (Raag)
---
drivers/gpu/drm/xe/xe_drm_ras.c | 42 +++++++++++++++++++++
drivers/gpu/drm/xe/xe_drm_ras.h | 3 ++
drivers/gpu/drm/xe/xe_ras.c | 67 +++++++++++++++++++++++++++++++++
3 files changed, 112 insertions(+)
diff --git a/drivers/gpu/drm/xe/xe_drm_ras.c b/drivers/gpu/drm/xe/xe_drm_ras.c
index 7937d8ba0ed9..0287594e1026 100644
--- a/drivers/gpu/drm/xe/xe_drm_ras.c
+++ b/drivers/gpu/drm/xe/xe_drm_ras.c
@@ -185,6 +185,48 @@ static int register_nodes(struct xe_device *xe)
return ret;
}
+/**
+ * xe_drm_ras_event() - Report drm_ras error event to userspace
+ * @xe: xe device structure
+ * @component: error component (see &enum drm_xe_ras_error_component)
+ * @severity: error severity (see &enum drm_xe_ras_error_severity)
+ * @value: value of error counter
+ *
+ * Report an error-event to userspace.
+ */
+void xe_drm_ras_event(struct xe_device *xe, u8 component, u8 severity, u32 value)
+{
+ struct xe_drm_ras *ras = &xe->ras;
+ struct xe_drm_ras_counter *info;
+ struct drm_ras_node *node;
+ int ret;
+
+ /* Event is supported only if drm_ras is enabled */
+ if (!xe->info.has_drm_ras)
+ return;
+
+ if (component >= DRM_XE_RAS_ERR_COMP_MAX) {
+ drm_warn(&xe->drm, "unsupported component %u\n", component);
+ return;
+ }
+
+ if (severity >= DRM_XE_RAS_ERR_SEV_MAX) {
+ drm_warn(&xe->drm, "unsupported severity %u\n", severity);
+ return;
+ }
+
+ node = &ras->node[severity];
+ info = ras->info[severity];
+
+ if (!info || !info[component].name)
+ return;
+
+ ret = drm_ras_nl_error_event(node, component, info[component].name, value);
+ if (ret)
+ drm_err_ratelimited(&xe->drm, "drm_ras error-event failed: %d for %s %s\n", ret,
+ info[component].name, error_severity[severity]);
+}
+
/**
* xe_drm_ras_init() - Initialize DRM RAS
* @xe: xe device instance
diff --git a/drivers/gpu/drm/xe/xe_drm_ras.h b/drivers/gpu/drm/xe/xe_drm_ras.h
index 365c70e93e82..add96bf1a7ab 100644
--- a/drivers/gpu/drm/xe/xe_drm_ras.h
+++ b/drivers/gpu/drm/xe/xe_drm_ras.h
@@ -5,11 +5,14 @@
#ifndef _XE_DRM_RAS_H_
#define _XE_DRM_RAS_H_
+#include <linux/types.h>
+
struct xe_device;
#define for_each_error_severity(i) \
for (i = 0; i < DRM_XE_RAS_ERR_SEV_MAX; i++)
int xe_drm_ras_init(struct xe_device *xe);
+void xe_drm_ras_event(struct xe_device *xe, u8 component, u8 severity, u32 value);
#endif
diff --git a/drivers/gpu/drm/xe/xe_ras.c b/drivers/gpu/drm/xe/xe_ras.c
index a31e06b8aa67..b08c664778ff 100644
--- a/drivers/gpu/drm/xe/xe_ras.c
+++ b/drivers/gpu/drm/xe/xe_ras.c
@@ -90,6 +90,8 @@ static const char * const gpu_health_states[] = {
};
static_assert(ARRAY_SIZE(gpu_health_states) == XE_RAS_HEALTH_MAX);
+static int get_counter(struct xe_device *xe, struct xe_ras_error_class *counter, u32 *value);
+
static u8 drm_to_xe_ras_severity(u8 severity)
{
switch (severity) {
@@ -102,6 +104,18 @@ static u8 drm_to_xe_ras_severity(u8 severity)
}
}
+static u8 xe_to_drm_ras_severity(u8 severity)
+{
+ switch (severity) {
+ case XE_RAS_SEV_CORRECTABLE:
+ return DRM_XE_RAS_ERR_SEV_CORRECTABLE;
+ case XE_RAS_SEV_UNCORRECTABLE:
+ return DRM_XE_RAS_ERR_SEV_UNCORRECTABLE;
+ default:
+ return DRM_XE_RAS_ERR_SEV_MAX;
+ }
+}
+
static u8 drm_to_xe_ras_component(u8 component)
{
switch (component) {
@@ -120,6 +134,24 @@ static u8 drm_to_xe_ras_component(u8 component)
}
}
+static u8 xe_to_drm_ras_component(u8 component)
+{
+ switch (component) {
+ case XE_RAS_COMP_DEVICE_MEMORY:
+ return DRM_XE_RAS_ERR_COMP_DEVICE_MEMORY;
+ case XE_RAS_COMP_CORE_COMPUTE:
+ return DRM_XE_RAS_ERR_COMP_CORE_COMPUTE;
+ case XE_RAS_COMP_PCIE:
+ return DRM_XE_RAS_ERR_COMP_PCIE;
+ case XE_RAS_COMP_FABRIC:
+ return DRM_XE_RAS_ERR_COMP_FABRIC;
+ case XE_RAS_COMP_SOC_INTERNAL:
+ return DRM_XE_RAS_ERR_COMP_SOC_INTERNAL;
+ default:
+ return DRM_XE_RAS_ERR_COMP_MAX;
+ }
+}
+
static int ras_status_to_errno(u32 status)
{
switch (status) {
@@ -218,6 +250,26 @@ static void ras_usp_aer_init(struct xe_device *xe)
dev_dbg(&usp->dev, "Uncorrectable Internal Errors downgraded and unmasked\n");
}
+static void ras_send_error_event(struct xe_device *xe, u8 severity, u8 component)
+{
+ struct xe_ras_error_class counter = {0};
+ u8 drm_severity, drm_component;
+ u32 value;
+ int ret;
+
+ counter.common.severity = severity;
+ counter.common.component = component;
+
+ ret = get_counter(xe, &counter, &value);
+ if (ret)
+ return;
+
+ drm_severity = xe_to_drm_ras_severity(severity);
+ drm_component = xe_to_drm_ras_component(component);
+
+ xe_drm_ras_event(xe, drm_component, drm_severity, value);
+}
+
static u8 handle_core_compute_errors(struct xe_ras_error_array *arr)
{
struct xe_ras_compute_error *error_info = (void *)arr->details;
@@ -312,8 +364,10 @@ void xe_ras_counter_threshold_crossed(struct xe_device *xe,
struct xe_ras_threshold_crossed *pending = (void *)&response->data;
struct xe_ras_error_class *errors = pending->counters;
u32 id, ncounters = pending->ncounters;
+ u8 sent = 0;
BUILD_BUG_ON(sizeof(response->data) < sizeof(*pending));
+ BUILD_BUG_ON(BITS_PER_TYPE(sent) < XE_RAS_COMP_MAX);
xe_device_assert_mem_access(xe);
if (!ncounters || ncounters > XE_RAS_NUM_COUNTERS)
@@ -327,8 +381,21 @@ void xe_ras_counter_threshold_crossed(struct xe_device *xe,
severity = errors[id].common.severity;
component = errors[id].common.component;
+ if (severity != XE_RAS_SEV_CORRECTABLE) {
+ xe_warn(xe, "sysctrl: unexpected severity %s (%u)\n", sev_to_str(severity),
+ severity);
+ continue;
+ }
+
xe_warn(xe, "[RAS]: %s %s detected\n",
comp_to_str(component), sev_to_str(severity));
+
+ /* Send event once per component */
+ if (sent & BIT(component))
+ continue;
+ sent |= BIT(component);
+
+ ras_send_error_event(xe, severity, component);
}
}
--
2.47.1
^ permalink raw reply related
* [PATCH v5 3/3] drm/xe/xe_ras: Report uncorrectable error events to userspace
From: Riana Tauro @ 2026-07-20 8:22 UTC (permalink / raw)
To: intel-xe, dri-devel, netdev
Cc: aravind.iddamsetty, anshuman.gupta, rodrigo.vivi, joonas.lahtinen,
kuba, simona.vetter, airlied, pratik.bari, joshua.santosh.ranjan,
ashwin.kumar.kulkarni, shubham.kumar, ravi.kishore.koppuravuri,
raag.jadav, maarten.lankhorst, mallesh.koujalagi, soham.purkait,
Riana Tauro
In-Reply-To: <20260720082208.2648279-5-riana.tauro@intel.com>
When the firmware reports uncorrectable errors in response to an AER
interrupt, deliver a drm-ras error event to userspace for each affected
component. Multiple errors for the same component within a single firmware
response are collapsed into one event to avoid duplicate notifications.
Signed-off-by: Riana Tauro <riana.tauro@intel.com>
---
drivers/gpu/drm/xe/xe_ras.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/gpu/drm/xe/xe_ras.c b/drivers/gpu/drm/xe/xe_ras.c
index b08c664778ff..cd1307539912 100644
--- a/drivers/gpu/drm/xe/xe_ras.c
+++ b/drivers/gpu/drm/xe/xe_ras.c
@@ -449,6 +449,7 @@ enum xe_ras_recovery_action xe_ras_process_errors(struct xe_device *xe)
enum xe_ras_recovery_action final_action;
u32 remaining = XE_SYSCTRL_FLOOD_LIMIT;
struct xe_ras_get_soc_error response;
+ u8 sent = 0;
size_t rlen;
int ret;
@@ -492,6 +493,12 @@ enum xe_ras_recovery_action xe_ras_process_errors(struct xe_device *xe)
xe_info(xe, "[RAS]: %s %s detected\n", comp_to_str(component),
sev_to_str(severity));
+ /* Send event once per component */
+ if (!(sent & BIT(component))) {
+ sent |= BIT(component);
+ ras_send_error_event(xe, severity, component);
+ }
+
switch (component) {
case XE_RAS_COMP_CORE_COMPUTE:
action = handle_core_compute_errors(arr);
--
2.47.1
^ permalink raw reply related
* Re: [PATCH net v2] net: erspan: set lltx to avoid sch_direct_xmit deadlock
From: Zhou, Yun @ 2026-07-20 8:24 UTC (permalink / raw)
To: Ido Schimmel, edumazet
Cc: dsahern, davem, kuba, pabeni, horms, netdev, linux-kernel
In-Reply-To: <20260720075352.GA2233846@shredder>
On 7/20/26 15:53, Ido Schimmel wrote:
> CAUTION: This email comes from a non Wind River email account!
> Do not click links or open attachments unless you recognize the sender and know the content is safe.
>
> On Mon, Jul 13, 2026 at 11:14:35PM +0800, Yun Zhou wrote:
>> erspan_xmit() re-enters the network stack via ip_tunnel_xmit(), causing
>> nested acquisition of _xmit_lock on the underlay device while already
>> holding the ERSPAN device's _xmit_lock. Both are ARPHRD_ETHER and share
>> the same lockdep class, creating an ABBA deadlock:
>>
>> sch_direct_xmit [lock erspan] -> erspan_xmit -> ip_tunnel_xmit ->
>> ip_output -> __dev_queue_xmit -> sch_direct_xmit [lock underlay]
>>
>> Set dev->lltx = true so HARD_TX_LOCK() skips the spinlock for ERSPAN.
>> This is safe as erspan_xmit() has no shared mutable state: o_seqno is
>> atomic, stats use atomic_long_inc, and dst_cache is per-CPU. GRETAP,
>> the sibling device with identical xmit structure, already sets lltx.
>
> erspan_xmit() (unlike gre_tap_xmit()) is performing non-atomic
> __clear_bit() on shared tunnel flags and KCSAN will probably flag it.
>
> Eric had a patch [1] that changes erspan_xmit() to use a private copy of
> these flags. I think it's better to wait for Eric's patch to be merged
> before setting lltx.
>
> Eric, can you please submit v2 of your patch to net?
>
> Also, doesn't ip6erspan suffer from the same problem? Please try to
> reproduce and fix.
>
Yes, ip6erspan has the same problem. I will fix it in v3.
Thanks,
Yun
^ permalink raw reply
* Re: [PATCH net v2] net: erspan: set lltx to avoid sch_direct_xmit deadlock
From: Eric Dumazet @ 2026-07-20 8:26 UTC (permalink / raw)
To: Ido Schimmel
Cc: Yun Zhou, dsahern, davem, kuba, pabeni, horms, netdev,
linux-kernel
In-Reply-To: <20260720075352.GA2233846@shredder>
On Mon, Jul 20, 2026 at 9:54 AM Ido Schimmel <idosch@nvidia.com> wrote:
>
> On Mon, Jul 13, 2026 at 11:14:35PM +0800, Yun Zhou wrote:
> > erspan_xmit() re-enters the network stack via ip_tunnel_xmit(), causing
> > nested acquisition of _xmit_lock on the underlay device while already
> > holding the ERSPAN device's _xmit_lock. Both are ARPHRD_ETHER and share
> > the same lockdep class, creating an ABBA deadlock:
> >
> > sch_direct_xmit [lock erspan] -> erspan_xmit -> ip_tunnel_xmit ->
> > ip_output -> __dev_queue_xmit -> sch_direct_xmit [lock underlay]
> >
> > Set dev->lltx = true so HARD_TX_LOCK() skips the spinlock for ERSPAN.
> > This is safe as erspan_xmit() has no shared mutable state: o_seqno is
> > atomic, stats use atomic_long_inc, and dst_cache is per-CPU. GRETAP,
> > the sibling device with identical xmit structure, already sets lltx.
>
> erspan_xmit() (unlike gre_tap_xmit()) is performing non-atomic
> __clear_bit() on shared tunnel flags and KCSAN will probably flag it.
>
> Eric had a patch [1] that changes erspan_xmit() to use a private copy of
> these flags. I think it's better to wait for Eric's patch to be merged
> before setting lltx.
>
> Eric, can you please submit v2 of your patch to net?
Sure, I can work on it today. Thanks!
>
> Also, doesn't ip6erspan suffer from the same problem? Please try to
> reproduce and fix.
>
> [1] https://lore.kernel.org/netdev/20260615140333.3161072-1-edumazet@google.com/
>
> >
> > Closes: https://syzkaller.appspot.com/bug?extid=9bda1b9fbb7fbdf9b62b
> > Reported-by: syzbot+9bda1b9fbb7fbdf9b62b@syzkaller.appspotmail.com
> > Fixes: 84e54fe0a5ea ("gre: introduce native tunnel support for ERSPAN")
> > Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
> > ---
> > v2:
> > - change subject prefix to [PATCH net]
> >
> > net/ipv4/ip_gre.c | 2 ++
> > 1 file changed, 2 insertions(+)
> >
> > diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
> > index 3efdfb4ffa21..9fbff16cda1d 100644
> > --- a/net/ipv4/ip_gre.c
> > +++ b/net/ipv4/ip_gre.c
> > @@ -1363,6 +1363,8 @@ static int erspan_tunnel_init(struct net_device *dev)
> > dev->features |= GRE_FEATURES;
> > dev->hw_features |= GRE_FEATURES;
> > dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
> > + /* Skip TX lock: xmit re-enters stack, risking ABBA with underlay */
> > + dev->lltx = true;
> > netif_keep_dst(dev);
> >
> > return ip_tunnel_init(dev);
> > --
> > 2.43.0
> >
^ permalink raw reply
* Re: [PATCH v4 4/5] vhost: synchronize with RCU readers when freeing workers
From: Stefano Garzarella @ 2026-07-20 8:39 UTC (permalink / raw)
To: Andrey Drobyshev
Cc: linux-kernel, kvm, virtualization, netdev, mst, stefanha,
dongli.zhang, maciej.szmigiero, bchaney, mark.kanda, ptikhomirov,
den
In-Reply-To: <55d7c896-b871-4c50-a324-35f5c4a9d11a@virtuozzo.com>
On Thu, Jul 16, 2026 at 09:01:22PM +0300, Andrey Drobyshev wrote:
>On 7/16/26 7:13 PM, Stefano Garzarella wrote:
>> On Thu, Jul 16, 2026 at 06:39:48PM +0300, Andrey Drobyshev wrote:
>>> On 7/16/26 11:57 AM, Stefano Garzarella wrote:
>>>> On Tue, Jul 14, 2026 at 06:16:37PM +0300, Andrey Drobyshev wrote:
>>>>> vhost_vq_work_queue() only holds the RCU read lock while it dereferences
>>>>> vq->worker and queues work on it. vhost_workers_free() however clears
>>>>> the vq->worker pointers and immediately frees the workers, without
>>>>> waiting for a grace period. A caller that fetched the worker right
>>>>> before the pointer was cleared can therefore still be queueing work on
>>>>> it while it is freed. And even when the queueing itself wins the race,
>>>>> the work is never run, so its VHOST_WORK_QUEUED bit stays set and all
>>>>> future attempts to queue it are silently skipped.
>>>>>
>>>>> None of the current callers can actually hit this: net and scsi stop
>>>>> their virtqueues before the workers are freed, and vsock unhashes the
>>>>> device and does synchronize_rcu() of its own in vhost_vsock_dev_release()
>>>>> before the workers go away. But the upcoming VHOST_RESET_OWNER support
>>>>> in vhost-vsock keeps the device hashed while its workers are freed, so
>>>>> the lockless send/cancel paths become able to race with the teardown.
>>>>>
>>>>> Close this the way vhost_worker_killed() already does: clear the
>>>>> vq->worker pointers, wait for a grace period, run whatever the last
>>>>> readers may have queued, and only then free the workers. The
>>>>> synchronize_rcu() is skipped if the device has no workers, so cleanup of
>>>>> devices which never got an owner stays cheap.
>>>>>
>>>>
>>>> Do we need a Fixes tag for this?
>>>>
>>>
>>> I'm guessing it should be:
>>>
>>> Fixes: 228a27cf78af ("vhost: Allow worker switching while work is queueing")
>>>
>>>> Thanks for pointing out that the issue wasn't occurring, but I think we
>>>> should add it because it's a sneaky problem we discovered by chance.
>>>> IMO the code should already have `synchronize_rcu()` after
>>>> `rcu_assign_pointer()` loop.
>>>>
>>>> @Michael, what do you think?
>>>>
>>>>> Suggested-by: Stefano Garzarella <sgarzare@redhat.com>
>>>>> Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
>>>>> ---
>>>>> drivers/vhost/vhost.c | 15 +++++++++++++++
>>>>> 1 file changed, 15 insertions(+)
>>>>>
>>>>> diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
>>>>> index 4c525b3e16ea..0d1414d40f4e 100644
>>>>> --- a/drivers/vhost/vhost.c
>>>>> +++ b/drivers/vhost/vhost.c
>>>>> @@ -729,6 +729,21 @@ static void vhost_workers_free(struct vhost_dev *dev)
>>>>>
>>>>> for (i = 0; i < dev->nvqs; i++)
>>>>> rcu_assign_pointer(dev->vqs[i]->worker, NULL);
>>>>> +
>>>>> + /*
>>>>> + * vhost_vq_work_queue() reads vq->worker under rcu_read_lock(), so a
>>>>> + * caller that fetched a worker before we cleared the pointers above
>>>>> + * may still be about to queue work on it. Wait for those RCU readers
>>>>> + * to finish before freeing the worker, then run whatever they queued
>>>>> + * so nothing is left with VHOST_WORK_QUEUED set. Mirrors
>>>>> + * vhost_worker_killed().
>>>>> + */
>>>>> + if (!xa_empty(&dev->worker_xa)) {
>>>>> + synchronize_rcu();
>>>>> + xa_for_each(&dev->worker_xa, i, worker)
>>>>> + vhost_run_work_list(worker);
>>>>> + }
>>>>> +
>>>>
>>>> Following sashiko review [1], I tried to undersand why we need this, but
>>>> TBH I'm really confused. That said, this seems wrong also because it
>>>> will work only with vhost_tasks, and not with kthreads.
>>>>
>>>> IIUC vhost_worker_killed() will be called anyway when calling
>>>> vhost_worker_destroy(). For vhost_tasks, it will call
>>>> vhost_task_do_stop() that calls vhost_task_stop(). This sets
>>>> VHOST_TASK_FLAGS_STOP and wait the worker on vtsk->exited before freeing
>>>> stuff. The worker breaks the loop and calls vtsk->handle_sigkill() that
>>>> is exactly vhost_worker_killed() you mentioned we are mirroring here.
>>>>
>>>
>>> Hmm, are we sure it's the case for our codepath? Looking at the
>>> vhost_task loop function:
>>>
>>>> static int vhost_task_fn(void *data)
>>>> {
>>>> for (;;) {
>>>> if (signal_pending(current)) {
>>>> if (get_signal(&ksig))
>>>> break;
>>>> }
>>>> ...
>>>> if (test_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags)) {
>>>> __set_current_state(TASK_RUNNING);
>>>> break;
>>>> }
>>>> did_work = vtsk->fn(vtsk->data);
>>>> ...
>>>> }
>>>>
>>>> ...
>>>>
>>>> if (!test_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags)) {
>>>> set_bit(VHOST_TASK_FLAGS_KILLED, &vtsk->flags);
>>>> vtsk->handle_sigkill(vtsk->data);
>>>> }
>>>> ...
>>>> }
>>>
>>> AFAICT, we exit the loop in 2 cases: signal delivery or STOP bit
>>> setting. Like you said, STOP is set by vhost_task_stop. E.g. for our
>>> RESET_OWNER case:
>>>
>>> vhost_vsock_reset_owner()
>>> vhost_dev_reset_owner()
>>> vhost_dev_cleanup()
>>> vhost_workers_free()
>>> vhost_worker_destroy()
>>> vhost_task_stop() // for vhost_task_ops backend
>>> set_bit(VHOST_TASK_FLAGS_STOP)
>>>
>>> So, first of all, actual work by .fn() callback is done after the exit
>>> checks, therefore we skip it - no chance to drain there.
>>>
>>> Secondly, the handle_sigkill() callback is deliberately NOT called in
>>> the STOP case and only called on fatal signal delivery. And for
>>> vhost_task backend the .handle_sigkill() callback is exactly
>>> vhost_worker_killed().
>>>
>>> So my understanding is: if we only call synchronize_rcu() here and leave
>>> this path undrained, then whatever work which was put by send_pkt() for
>>> the worker currently being freed - will be lost. Please correct me if
>>> I'm wrong.
>>
>> Yep, your right. But what will be the issue of loosing them?
>>
>> IIUC we are not loosing any data, just avoiding some works that will be
>> handled later when/if will set a new owner.
>>
>
>But will it actually be handled?
>
>vhost_transport_send_pkt() // called on every packet send
> virtio_vsock_skb_queue_tail(&send_pkt_queue, skb) // add skb to list
> vhost_vq_work_queue(&send_pkt_work) // try to arm the work
> vhost_worker_queue()
> if (!test_and_set_bit(VHOST_WORK_QUEUED, &work->flags)) {
> llist_add(&worker->work_list)
> }
>
>So send_pkt_queue is a list of skbs, it lives on the vhost_vsock device
>state, and survives RESET_OWNER. In that sense you're probably right
>that we aren't loosing any data.
>
>There's also send_pkt_work object, also living on the vhost_vsock device
>state. So we're accumulating skbs, and then send_pkg_work gets put in
>the worker task list - but only if it's NOT already armed in there, i.e.
>QUEUED bit is unset. And the bit gets cleared by the workload callback
>- for vhost_task backend it's vhost_run_work_list().
>
>The most important thing is WHERE this piece of work is being put. That
>is worker->work_list - this list does not survive RESET_OWNER, as we
>free the worker in vhost_workers_free().
>
>Now imagine we have RESET_OWNER racing with send_pkt. In
>vhost_workers_free() we acquire ptr to a worker but not NULL'ify it yet.
> Then on the send_pkt path we arm the send_pkt_work, set the QUEUED bit,
>and place it on the work_list of a DYING worker. Then the worker gets
>freed. Now we have send_pkt_work (a singleton struct) with QUEUED set
>in its flags, and with no worker to walk through this piece of work and
>clear this flag. As a result - send_pkt_work can't be placed in the
>list of any other worker, because it doesn't pass the "if
>(!test_and_set_bit(QUEUED)" check. Thus no new packets can be
>processed, and the connection is stalled.
>
>Does this make sense?
Honestly, I don't see the problem. The device is about to be stopped
because the VMM is resetting the owner, so the connection is going to
stall anyway, since I don't think the guest will never answer, isn't it?
>>>
>>> That said, I agree that vhost_run_work_list() will only work with
>>> vhost_task backend, not with kthreads backend. If we do
>>> vhost_worker_flush() instead - I guess it'll keep the drain here, yet
>>> become backend-agnostic. I.e.:
>>>
>>>> + if (!xa_empty(&dev->worker_xa)) {
>>>> + synchronize_rcu();
>>>> + xa_for_each(&dev->worker_xa, i, worker)
>>>> + vhost_worker_flush(worker);
>>>> + }
>>>
>>> With the last 2 lines being equivalent to just calling
>>> vhost_dev_flush(dev). And once we become backend-agnostic here, I'm
>>> guessing the warning reported by Sashiko should be dealt with as well.
>>
>> I'd avoid `if !xa_empty(&dev->worker_xa)` at all, and call
>> synchronize_rcu() in any case.
>>
>
>Agreed.
>
>> About vhost_dev_flush(), we are calling it in several places, and maybe
>> we should re-check them. E.g. we call in vhost_vsock_flush(), but it's
>> also called by vhost_dev_stop(), maybe we can avoid to call
>> vhost_vsock_flush() if we call vhost_dev_stop().
>>
>> I'm not sure we really need another one here, but if you think some
>> other works can be queued between the vhost_dev_stop() and the
>> synchronize_rcu() we are adding here, then okay, it may have sense.
>>
>
>Note that in our particular case we're gonna do:
>
>vhost_workers_free()
> vhost_dev_flush() // the flush we're planning to add
> xa_for_each(&dev->worker_xa, i, worker)
> vhost_worker_destroy(dev, worker)
> xa_destroy(&dev->worker_xa)
>
>So we walk through the XArray, destroy workers in it one by one, then
>destroy the XArray itself. Then the next time we call
>vhost_dev_flush(), e.g. from vhost_dev_stop() or wherever else, it tries
>iterating over the XArray which no longer exists - which is gonna be a
>no-op.
>
>Now, we can reach vhost_workers_free() via (at least) 2 paths:
>RESET_OWNER and device release path. On the former the flush is needed
>as I illustrated above. On the latter it's indeed redundant but is
>cheap as it's a no-op.
Again, I don't see the need for it TBH, but at the same time, I don't
think it's a problem to include it, so if you think it's necessary, go
ahead and add it.
Thanks,
Stefano
^ permalink raw reply
* Re: [PATCH net] net: pcs: xpcs: fix SGMII state reading
From: Maxime Chevallier @ 2026-07-20 8:47 UTC (permalink / raw)
To: Coia Prant, Andrew Lunn, Heiner Kallweit, Russell King,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: Jiawen Wu, netdev, linux-kernel, stable
In-Reply-To: <20260717074324.3250043-2-coiaprant@gmail.com>
Hi,
On 7/17/26 09:43, Coia Prant wrote:
> Commit 2a22b7ae2fa3 ("net: pcs: xpcs: adapt Wangxun NICs for SGMII mode")
> added a path in xpcs_get_state_c37_sgmii() that reads speed/duplex from
> BMCR after AN completes. However, BMCR does not reflect the negotiated
> result on the hardware where this has been tested:
>
> - On RK3568 (MAC side SGMII), BMCR returns a fixed hardware reset value
> - Wangxun engineer Jiawen Wu confirmed that on their side, "BMCR looks
> like it only wants to be return as 0" [0]
>
> The correct information is available in CL37_ANSGM_STS, which contains
> the actual link status and negotiated speed/duplex.
>
> This bug was previously masked by phylink core, which overrides the PCS
> link state with the PHY state when a PHY is present:
>
> /* If we have a phy, the "up" state is the union of both the
> * PHY and the MAC
> */
> if (phy)
> link_state.link &= pl->phy_state.link;
>
> Thus, when the link is down, the PHY's link_down state is applied on top
> of whatever the PCS reports, hiding the broken PCS state reading path.
>
> Modify xpcs_get_state_c37_sgmii() to:
> 1. Read link state from CL37_ANSGM_STS
> 2. If link is up, report speed/duplex from CL37_ANSGM_STS
> 3. Remove the broken BMCR reading path entirely
>
> Also properly set state->an_complete to reflect the AN completion status,
> and clear CL37_ANCMPLT_INTR when link is down to avoid stale state.
>
> [0] https://lore.kernel.org/all/000c01dd1593$2ac0b0f0$804212d0$@trustnetic.com/
>
> Fixes: 2a22b7ae2fa3 ("net: pcs: xpcs: adapt Wangxun NICs for SGMII mode")
> Cc: stable@vger.kernel.org
> Tested-by: Jiawen Wu <jiawenwu@trustnetic.com>
> Signed-off-by: Coia Prant <coiaprant@gmail.com>
Give that a test on ksz9477 that has an older XPCS for SGMII, this makes some
sense to me and no regressions were found.
Tested-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Maxime
^ permalink raw reply
* Re: [Patch net-next v3] net: phy: Add driver for Motorcomm Quad 2.5GbE phy
From: Kyle Switch @ 2026-07-20 8:47 UTC (permalink / raw)
To: Andrew Lunn
Cc: Frank.Sae, hkallweit1, linux, davem, edumazet, kuba, pabeni,
netdev, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang, jie.han
In-Reply-To: <3a576b50-f2be-476f-b702-964a8158cf1a@lunn.ch>
On 7/17/26 22:53, Andrew Lunn wrote:
> On Fri, Jul 17, 2026 at 01:48:07PM +0800, Kyle Switch wrote:
>> Add a driver for motorcomm yt8824 quad 2.5G ethernet phy, supports
>> 2.5G/1000M/100M/10M speed.
>>
>> Signed-off-by: Kyle Switch <kyle.switch@motor-comm.com>
>> ---
>>
>> changes in v3:
>> 1. Using common apis defined in phy_package.c to handle shared top
>> extend register space.
>> 2. Add dts demo in motorcomm,yt8xxx.yaml.
>> 3. Fix unnecessary redundant judgments.
>> 4. Fix BMCR registers operation using magic number.
>> 5. Rename funtion based on its approximate functionality.
>>
>> changes in v2:
>> 1. Remove duplicate code and replace it with existing api.
>>
>> .../bindings/net/motorcomm,yt8xxx.yaml | 30 +
>
> Please put binding changes in a patch of its own, in the patch
> series. You also need to Cc: the DT people.
>
> Andrew
>
Ans: as mentioned in other emails. patch v4 will remove binding changes,
and updated in another patch thread.
> ---
> pw-bot: cr
^ permalink raw reply
* Re: [Patch net-next v3] net: phy: Add driver for Motorcomm Quad 2.5GbE phy
From: Kyle Switch @ 2026-07-20 8:45 UTC (permalink / raw)
To: Krzysztof Kozlowski, Frank.Sae, andrew, hkallweit1, linux, davem,
edumazet, kuba, pabeni
Cc: netdev, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang, jie.han
In-Reply-To: <6264d655-640e-47ec-9521-5e292bfeaba6@kernel.org>
On 7/18/26 04:19, Krzysztof Kozlowski wrote:
> On 17/07/2026 07:48, Kyle Switch wrote:
>> Add a driver for motorcomm yt8824 quad 2.5G ethernet phy, supports
>> 2.5G/1000M/100M/10M speed.
>>
>> Signed-off-by: Kyle Switch <kyle.switch@motor-comm.com>
>
> Please run scripts/checkpatch.pl on the patches and fix reported
> warnings. After that, run also 'scripts/checkpatch.pl --strict' on the
> patches and (probably) fix more warnings. Some warnings can be ignored,
> especially from --strict run, but the code here looks like it needs a
> fix. Feel free to get in touch if the warning is not clear.
>
> Please use scripts/get_maintainers.pl to get a list of necessary people
> and lists to CC (and consider --no-git-fallback argument, so you will
> not CC people just because they made one commit years ago). It might
> happen, that command when run on an older kernel, gives you outdated
> entries. Therefore please be sure you base your patches on recent Linux
> kernel.
>
> Tools like b4 or scripts/get_maintainer.pl provide you proper list of
> people, so fix your workflow. Tools might also fail if you work on some
> ancient tree (don't, instead use mainline) or work on fork of kernel
> (don't, instead use mainline). Just use b4 and everything should be
> fine, although remember about `b4 prep --auto-to-cc` if you added new
> patches to the patchset.
>
Ans: Thank you for your support. The warning and error code formats
will be fixed in the following patches according to this procedure.
>> ---
>>
>> changes in v3:
>> 1. Using common apis defined in phy_package.c to handle shared top
>> extend register space.
>> 2. Add dts demo in motorcomm,yt8xxx.yaml.
>> 3. Fix unnecessary redundant judgments.
>> 4. Fix BMCR registers operation using magic number.
>> 5. Rename funtion based on its approximate functionality.
>>
>> changes in v2:
>> 1. Remove duplicate code and replace it with existing api.
>>
>> .../bindings/net/motorcomm,yt8xxx.yaml | 30 +
>> drivers/net/phy/motorcomm.c | 1808 ++++++++++++++++-
>> 2 files changed, 1750 insertions(+), 88 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml b/Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml
>> index 26688e2302ea..e7592468f658 100644
>> --- a/Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml
>> +++ b/Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml
>> @@ -149,3 +149,33 @@ examples:
>> motorcomm,auto-sleep-disabled;
>> };
>> };
>> + - |
>> + mdio {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>
> I don't understand why you are doing this. Anyway, please read the
> documents I linked and maybe there is some explanation for that change.
Ans: the bind changes will be removed in patch v4, and updated in another
patch thread.
>
> Best regards,
> Krzysztof
^ permalink raw reply
* Re: [PATCH net-next 1/5] bridge: Use direct pointer in br_is_nd_neigh_msg()
From: Nikolay Aleksandrov @ 2026-07-20 8:58 UTC (permalink / raw)
To: Danielle Ratson, netdev
Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms, ja, petrm,
fw, kuniyu, bridge, linux-kernel
In-Reply-To: <c710e4a236e69868a59c969bab31e5f29cfc0e86.1784463131.git.danieller@nvidia.com>
On 19/07/2026 16:34, Danielle Ratson wrote:
> Both callers of br_is_nd_neigh_msg() already call pskb_may_pull() to
> ensure sizeof(struct ipv6hdr) + sizeof(struct nd_msg) bytes are in the
> linear area before invoking this function. The skb_header_pointer()
> call and its fallback buffer are therefore unnecessary.
>
> Replace skb_header_pointer() with a direct cast to ipv6_hdr(skb) + 1
> and drop the now-unused 'msg' parameter and its corresponding stack
> buffer from all callers.
>
> Reviewed-by: Petr Machata <petrm@nvidia.com>
> Signed-off-by: Danielle Ratson <danieller@nvidia.com>
> ---
> net/bridge/br_arp_nd_proxy.c | 9 ++-------
> net/bridge/br_device.c | 4 ++--
> net/bridge/br_input.c | 4 ++--
> net/bridge/br_private.h | 2 +-
> 4 files changed, 7 insertions(+), 12 deletions(-)
>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
^ permalink raw reply
* Re: [Patch net-next v3] net: phy: Add driver for Motorcomm Quad 2.5GbE phy
From: Kyle Switch @ 2026-07-20 9:05 UTC (permalink / raw)
To: Andrew Lunn
Cc: Frank.Sae, hkallweit1, linux, davem, edumazet, kuba, pabeni,
netdev, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang, jie.han
In-Reply-To: <06a5b8d1-f3a5-40e9-af12-52c5c9fd99e6@lunn.ch>
On 7/17/26 23:11, Andrew Lunn wrote:
>> +/**
>> + * ytphy_read_top_ext() - read a PHY's top extended register for YT8824
>> + * @phydev: a pointer to a &struct phy_device
>> + * @regnum: register number to read
>> + *
>> + * NOTE:The caller must have taken the MDIO bus lock.
>
> Didn't i request this is done in code, not comments?
Ans: fix done in patch v4 by called lockdep_assert_held to make
sure to hold the lock.
>
>> +static int ytphy_read_top_ext(struct phy_device *phydev, u16 regnum)
>> +{
>> + struct yt8824_shared_priv *shared_priv;
>> + unsigned int offset;
>> + int ret;
>> +
>> + shared_priv = phy_package_get_priv(phydev);
>> + offset = shared_priv->offset;
>> + ret = __phy_package_write(phydev, offset, YTPHY_PAGE_SELECT, regnum);
>
> You look to be using the package wrongly.
>
> https://elixir.bootlin.com/linux/v7.1.3/source/drivers/net/phy/phy_package.c#L192
>
> * The base_addr parameter serves as cookie which has to have the same values
> * for all members of one group and as the base PHY address of the PHY package
> * for offset calculation to access generic registers of a PHY package.
> * Usually, one of the PHY addresses of the different PHYs in the package
> * provides access to these global registers.
> * The address which is given here, will be used in the __phy_package_read()
> * and __phy_package_write() convenience functions as base and added to the
> * passed offset in those functions.
>
> Although it is called base, it can be above all the others. So when
> you create the package, set the cookie value to your top address. You
> can then use __phy_package_write() directly, offset of 0.
Ans: fix done in patch v4, the base_addr is used to access to the top extend register
for phy8824.
>
>> -static struct phy_driver motorcomm_phy_drvs[] = {
>> - {
>> - PHY_ID_MATCH_EXACT(PHY_ID_YT8511),
>> - .name = "YT8511 Gigabit Ethernet",
>> - .config_init = yt8511_config_init,
>> - .suspend = genphy_suspend,
>> - .resume = genphy_resume,
>> - .read_page = yt8511_read_page,
>> - .write_page = yt8511_write_page,
>> - },
>
> I still don't like this. Please stop diff deleting all these and
> putting them back later. It makes me thing something changed, maybe
> white space?
Ans: I tried to refactor this issue by hanging the position of the newly
added code. However, the same issue may still arise in other code segments,
without modifying the existing code.
>
>> +static int yt8824_utp_softreset_paged(struct phy_device *phydev,
>> + int reg_space)
>> +{
>> + int old_page;
>> + int ret = 0;
>> + int val;
>> +
>> + old_page = phy_select_page(phydev, reg_space);
>> + if (old_page < 0)
>> + goto err_restore_page;
>> + if (reg_space == YT8824_RSSR_UTP_SPACE) {
>> + ret = __phy_read(phydev, MII_BMCR);
>> + if (ret < 0)
>> + goto err_restore_page;
>> + ret |= BMCR_RESET;
>> + ret = __phy_write(phydev, MII_BMCR, ret);
>
> So here you are using __phy_write() so it does not take the mdio lock.
>
>> + if (ret < 0)
>> + goto err_restore_page;
>> + /* wait until softreset done. */
>> + ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
>> + 50000, 600000, true);
>
> This uses phy_read(), so does take the lock.
>
> How can this be correct?
>
> Please take some time to think about locking. Please enable all the
> locking debug options in the kernel and see if you get splats. And
> also really do turn your locking comments into code.
>
Ans: this issue fixed done in patch v4. The correct procedure might be to acquire the mdio lock
within this phy_select_page() and release it within phy_restore_page(). During this period,
all interfaces that involve acquiring the mdio lock are unacceptable and incorrect.
> Andrew
^ permalink raw reply
* [PATCH] tls: don't abort the connection on signal-interrupted sends
From: Maximilian Immanuel Brandtner @ 2026-07-20 9:08 UTC (permalink / raw)
To: john.fastabend, kuba, sd, davem, edumazet, pabeni, horms,
bcodding, netdev, linux-kernel, svens, brueckner
When a signal interrupts a blocking send, tls_tx_records() treats the
resulting -ERESTARTSYS as a transmission failure and marks the socket
errored via tls_err_abort() with the raw error code. Later syscalls
return the kernel-internal errno 512 (ERESTARTSYS) to userspace, as the
signal it stems from is no longer pending during syscall exit and thus
never translated.
An interrupted send is not a connection error: the partially sent record
stays queued and is resent later. Interrupt error codes are therefore
excluded from the abort in the same way as -EAGAIN.
Fixes: b341ca51d267 ("tls: Fix tls_sw_sendmsg error handling")
Signed-off-by: Maximilian Immanuel Brandtner <maxbr@linux.ibm.com>
---
Tested on top of net commit 3f1f75536668 ("net: openvswitch: reject
oversized nested action attrs").
Several subsystems contain static helper functions to classify these
interrupt errnos. It might be worthwhile to refactor these static
functions into a generic helper function.
---
net/tls/tls_sw.c | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index d4afc90fd796..3c9e94069e82 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -405,6 +405,24 @@ static void tls_free_open_rec(struct sock *sk)
}
}
+static bool tls_is_non_restartable_err(int err)
+{
+ if (err >= 0)
+ return false;
+
+ switch (err) {
+ case -EAGAIN:
+ case -EINTR:
+ case -ERESTARTSYS:
+ case -ERESTARTNOINTR:
+ case -ERESTARTNOHAND:
+ case -ERESTART_RESTARTBLOCK:
+ return false;
+ default:
+ return true;
+ }
+}
+
int tls_tx_records(struct sock *sk, int flags)
{
struct tls_context *tls_ctx = tls_get_ctx(sk);
@@ -458,7 +476,7 @@ int tls_tx_records(struct sock *sk, int flags)
}
tx_err:
- if (rc < 0 && rc != -EAGAIN)
+ if (tls_is_non_restartable_err(rc))
tls_err_abort(sk, rc);
return rc;
--
2.55.0
^ permalink raw reply related
* Re: [PATCH net] net/mlx5: FW tracer, clamp firmware-reported num_string_db
From: Leon Romanovsky @ 2026-07-20 9:10 UTC (permalink / raw)
To: Tariq Toukan
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni, Akiva Goldberger, Gal Pressman, Kees Cook,
linux-kernel, linux-rdma, Mark Bloch, Moshe Shemesh,
Saeed Mahameed, Shay Drori
In-Reply-To: <20260717072543.1241094-1-tariqt@nvidia.com>
On Fri, Jul 17, 2026 at 10:25:43AM +0300, Tariq Toukan wrote:
> From: Akiva Goldberger <agoldberger@nvidia.com>
>
> mlx5_query_mtrc_caps() reads num_string_db from the MTRC capabilities
> register and uses it directly as a loop bound to populate the fixed-size
> base_address_out[STRINGS_DB_SECTIONS_NUM] and
> size_out[STRINGS_DB_SECTIONS_NUM] arrays in the tracer's str_db
> structure (STRINGS_DB_SECTIONS_NUM == 8).
>
> The field is 4 bits wide, so firmware can report up to 15. A value
> greater than STRINGS_DB_SECTIONS_NUM makes the loop write past the end
> of those arrays, corrupting adjacent fields of the fw_tracer structure
> on the kernel heap. Clamp the firmware-reported value before it is used.
>
> Fixes: f53aaa31cce7 ("net/mlx5: FW tracer, implement tracer logic")
> Signed-off-by: Akiva Goldberger <agoldberger@nvidia.com>
> Reviewed-by: Shay Drori <shayd@nvidia.com>
> Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
> ---
> drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
> index adcc73e2a5b3..404736c46adf 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
> @@ -64,6 +64,13 @@ static int mlx5_query_mtrc_caps(struct mlx5_fw_tracer *tracer)
> tracer->str_db.num_string_trace =
> MLX5_GET(mtrc_cap, out, num_string_trace);
> tracer->str_db.num_string_db = MLX5_GET(mtrc_cap, out, num_string_db);
> + if (tracer->str_db.num_string_db > STRINGS_DB_SECTIONS_NUM) {
> + mlx5_core_warn(dev,
> + "FWTracer: Firmware reports num_string_db (%u) > (%u), clamping\n",
> + tracer->str_db.num_string_db,
> + STRINGS_DB_SECTIONS_NUM);
> + tracer->str_db.num_string_db = STRINGS_DB_SECTIONS_NUM;
> + }
First, these lines are:
"tracer->str_db.num_string_db = min(tracer->str_db.num_string_db,
STRINGS_DB_SECTIONS_NUM);"
Second, this is a very naive approach to "securing" the system.
Everything originates from the firmware: registers, DMA, and data.
You cannot single out one field and claim the system is now "secure".
I am aware of another large vendor that added similar "clamping"
throughout their driver. That does not make the implementation correct
or particularly useful.
Thanks
> tracer->owner = !!MLX5_GET(mtrc_cap, out, trace_owner);
> tracer->str_db.loaded = false;
>
>
> base-commit: 3f1f755366687d051174739fb99f7d560202f60b
> --
> 2.44.0
>
>
^ permalink raw reply
* [PATCH net] hinic: fix leak of ethtool RSS user configuration buffers
From: Chenguang Zhao @ 2026-07-20 9:11 UTC (permalink / raw)
To: cai.huoqing, andrew+netdev, davem, edumazet, kuba, pabeni
Cc: netdev, chenguang.zhao, Chenguang Zhao
From: Chenguang Zhao <zhaochenguang@kylinos.cn>
rss_indir_user and rss_hkey_user are allocated in __set_rss_rxfh() when
the user configures RSS via ethtool, but hinic_remove() never frees them.
free_netdev() only releases the nic_dev structure itself, so the separately
allocated buffers are leaked on driver unload.
Free both buffers in hinic_remove() after unregister_netdev().
Fixes: 4fdc51bb4e92 ("hinic: add support for rss parameters with ethtool")
Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
---
drivers/net/ethernet/huawei/hinic/hinic_main.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c
index 42f4792d255b..7d9c46004bcd 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_main.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c
@@ -1433,6 +1433,9 @@ static void hinic_remove(struct pci_dev *pdev)
hinic_free_intr_coalesce(nic_dev);
+ kfree(nic_dev->rss_indir_user);
+ kfree(nic_dev->rss_hkey_user);
+
hinic_port_del_mac(nic_dev, netdev->dev_addr, 0);
hinic_hwdev_cb_unregister(nic_dev->hwdev,
--
2.25.1
^ permalink raw reply related
* [PATCH net-next v4] net: phy: Add driver for Motorcomm Quad 2.5GbE phy
From: Kyle Switch @ 2026-07-20 9:11 UTC (permalink / raw)
To: Frank.Sae, andrew, hkallweit1, linux, davem, edumazet, kuba,
pabeni, netdev, linux-kernel
Cc: ming.xu, xiaolin.xu, jianmin.wang, jie.han
Add a driver for motorcomm yt8824 quad 2.5G ethernet phy, supports
2.5G/1000M/100M/10M speed.
Signed-off-by: Kyle Switch <kyle.switch@motor-comm.com>
---
changes in v4:
1. Remove motorcomm,yt8xxx.yaml, will update in other patch thread
2. Fix locking issue. Since every interface requires switching of space,
the bus is already locked during the space switching process in
phy_select_page(), other operations within the interface cannot
be locked again before unlock in phy_restore_page().
3. Fix warning log identified during the inspection process using
checkpatch.pl.
4. Fix the way of using common api in phy_package.c
changes in v3:
1. Using common apis defined in phy_package.c to handle shared top
extend register space.
2. Add dts demo in motorcomm,yt8xxx.yaml.
3. Fix unnecessary redundant judgments.
4. Fix BMCR registers operation using magic number.
5. Rename funtion based on its approximate functionality.
changes in v2:
1. Remove duplicate code and replace it with existing api.
drivers/net/phy/motorcomm.c | 1718 ++++++++++++++++++++++++++++++++++-
1 file changed, 1698 insertions(+), 20 deletions(-)
diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c
index 5071605a1a11..ee63c2019946 100644
--- a/drivers/net/phy/motorcomm.c
+++ b/drivers/net/phy/motorcomm.c
@@ -1,23 +1,32 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Motorcomm 8511/8521/8522/8531/8531S/8821 PHY driver.
+ * Motorcomm 8511/8521/8522/8531/8531S/8821/8824 PHY driver.
*
* Author: Peter Geis <pgwipeout@gmail.com>
* Author: Frank <Frank.Sae@motor-comm.com>
+ * Author: Kyle <kyle.switch@motor-comm.com>
*/
#include <linux/etherdevice.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/phy.h>
#include <linux/property.h>
+#include "phylib.h"
+
+/* define PHY8824 top extend addr offset internal and external*/
+#define INTERNAL_PHY8824_TOP_EXTEND_OFFSET (5)
+#define EXTERNAL_PHY8824_TOP_EXTEND_OFFSET (4)
+
#define PHY_ID_YT8511 0x0000010a
#define PHY_ID_YT8521 0x0000011a
#define PHY_ID_YT8522 0x4f51e928
#define PHY_ID_YT8531 0x4f51e91b
#define PHY_ID_YT8531S 0x4f51e91a
#define PHY_ID_YT8821 0x4f51ea19
+#define PHY_ID_YT8824 0x4f51e8b8
/* YT8521/YT8531S/YT8821 Register Overview
* UTP Register space | FIBER Register space
* ------------------------------------------------------------
@@ -29,6 +38,18 @@
* ------------------------------------------------------------
*/
+/* YT8824 Register Overview
+ * UTP Register space | FIBER Register space
+ * ------------------------------------------------------------
+ * | UTP MII | FIBER MII |
+ * | UTP MMD | |
+ * | UTP Extended | FIBER Extended |
+ * | UTP Top Extended | FIBER Top Extended |
+ * ------------------------------------------------------------
+ * | Common Top Extended |
+ * ------------------------------------------------------------
+ */
+
/* 0x10 ~ 0x15 , 0x1E and 0x1F are common MII registers of yt phy */
/* Specific Function Control Register */
@@ -354,7 +375,7 @@
#define YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG 0x4B5
#define YT8821_UTP_EXT_MU_FINE_FR_F_FFE GENMASK(14, 12)
-#define YT8821_UTP_EXT_MU_FINE_FR_F_FBE GENMASK(10, 8)
+#define YT8821_UTP_EXT_MU_FINE_FR_F_FBE GENMASK(10, 8)
#define YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG 0x4D2
#define YT8821_UTP_EXT_VGA_LPF1_CAP_OTHER GENMASK(7, 4)
@@ -375,6 +396,16 @@
#define YT8821_CHIP_MODE_AUTO_BX2500_SGMII 0
#define YT8821_CHIP_MODE_FORCE_BX2500 1
+#define YT8824_RSSR_SPACE_MASK BIT(0)
+#define YT8824_RSSR_FIBER_SPACE (0x1)
+#define YT8824_RSSR_UTP_SPACE (0x0)
+#define YT8824_UTP_TEMPLATE_MODE_CTRL (0x84)
+#define YT8824_UTP_TEMPLATE_MODE_MASK GENMASK(15, 13)
+#define YT8824_UTP_TEMPLATE_TEST_MODE1 BIT(13)
+#define YT8824_SDS_CFG_MIN_PRE_MASK GENMASK(3, 0)
+#define YT8824_SDS_EN_FILL_PRE BIT(13)
+#define YT8824_SDS_TX_PRE_PADDING (0x7)
+
struct yt8521_priv {
/* combo_advertising is used for case of YT8521 in combo mode,
* this means that yt8521 may work in utp or fiber mode which depends
@@ -393,6 +424,11 @@ struct yt8521_priv {
u8 reg_page;
};
+struct yt8824_shared_priv {
+ unsigned int interface_mode;
+ unsigned int offset;
+};
+
/**
* ytphy_read_ext() - read a PHY's extended register
* @phydev: a pointer to a &struct phy_device
@@ -431,6 +467,46 @@ static int ytphy_read_ext_with_lock(struct phy_device *phydev, u16 regnum)
return ret;
}
+/**
+ * ytphy_read_top_ext() - read a PHY's top extended register for YT8824
+ * @phydev: a pointer to a &struct phy_device
+ * @regnum: register number to read
+ *
+ * Returns: the value of regnum reg or negative error code
+ */
+static int ytphy_read_top_ext(struct phy_device *phydev, u16 regnum)
+{
+ int ret;
+
+ lockdep_assert_held(&phydev->mdio.bus->mdio_lock);
+ ret = __phy_package_write(phydev, 0, YTPHY_PAGE_SELECT, regnum);
+ if (ret < 0)
+ return ret;
+
+ return __phy_package_read(phydev, 0, YTPHY_PAGE_DATA);
+}
+
+/**
+ * ytphy_write_top_ext() - write a PHY's top extended register for YT8824
+ * @phydev: a pointer to a &struct phy_device
+ * @regnum: register number to write
+ * @val: register val to write
+ *
+ * Returns: the value of regnum reg or negative error code
+ */
+static int ytphy_write_top_ext(struct phy_device *phydev, u16 regnum,
+ u16 val)
+{
+ int ret;
+
+ lockdep_assert_held(&phydev->mdio.bus->mdio_lock);
+ ret = __phy_package_write(phydev, 0, YTPHY_PAGE_SELECT, regnum);
+ if (ret < 0)
+ return ret;
+
+ return __phy_package_write(phydev, 0, YTPHY_PAGE_DATA, val);
+}
+
/**
* ytphy_write_ext() - write a PHY's extended register
* @phydev: a pointer to a &struct phy_device
@@ -593,35 +669,1016 @@ static int ytphy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
goto err_restore_page;
}
- /* Enable WOL feature */
- mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL;
- val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
- val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS;
- ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, val);
+ /* Enable WOL feature */
+ mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL;
+ val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
+ val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS;
+ ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, val);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* Enable WOL interrupt */
+ ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0,
+ YTPHY_IER_WOL);
+ if (ret < 0)
+ goto err_restore_page;
+
+ } else {
+ old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ /* Disable WOL feature */
+ mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
+ ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, 0);
+
+ /* Disable WOL interrupt */
+ ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG,
+ YTPHY_IER_WOL, 0);
+ if (ret < 0)
+ goto err_restore_page;
+ }
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_read_page() - read reg page
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: current reg space of yt8824 (YT8824_RSSR_FIBER_SPACE/
+ * YT8824_RSSR_UTP_SPACE) or negative errno code
+ */
+static int yt8824_read_page(struct phy_device *phydev)
+{
+ int old_page;
+
+ old_page = ytphy_read_top_ext(phydev, YT8521_REG_SPACE_SELECT_REG);
+ if (old_page < 0)
+ return old_page;
+
+ return old_page & YT8824_RSSR_SPACE_MASK;
+};
+
+/**
+ * yt8824_write_page() - write reg page
+ * @phydev: a pointer to a &struct phy_device
+ * @page: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE) to write.
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_write_page(struct phy_device *phydev, int page)
+{
+ int old_page;
+ u16 data;
+
+ old_page = ytphy_read_top_ext(phydev, YT8521_REG_SPACE_SELECT_REG);
+ data = old_page & (~YT8824_RSSR_SPACE_MASK);
+ data |= page;
+
+ return ytphy_write_top_ext(phydev, YT8521_REG_SPACE_SELECT_REG, data);
+};
+
+/**
+ * yt8824_utp_invalid_test_mode_paged() - config YT8824 to invalid test mode.
+ * @phydev: a pointer to a &struct phy_device
+ * @reg_space: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE).
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_utp_invalid_test_mode_paged(struct phy_device *phydev,
+ int reg_space)
+{
+ int old_page;
+ int ret = 0;
+
+ old_page = phy_select_page(phydev, reg_space);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ if (reg_space == YT8824_RSSR_UTP_SPACE) {
+ ret = __phy_read_mmd(phydev, 0x1, YT8824_UTP_TEMPLATE_MODE_CTRL);
+ if (ret < 0)
+ goto err_restore_page;
+ ret &= ~YT8824_UTP_TEMPLATE_MODE_MASK;
+ ret |= YT8824_UTP_TEMPLATE_TEST_MODE1;
+ ret = __phy_write_mmd(phydev, 0x1, YT8824_UTP_TEMPLATE_MODE_CTRL, ret);
+ if (ret < 0)
+ goto err_restore_page;
+ }
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_sds_isolate_paged() - enable YT8824 serdes isolate.
+ * @phydev: a pointer to a &struct phy_device
+ * @reg_space: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE).
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_sds_isolate_paged(struct phy_device *phydev,
+ int reg_space)
+{
+ int old_page;
+ int ret = 0;
+
+ old_page = phy_select_page(phydev, reg_space);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ if (reg_space == YT8824_RSSR_FIBER_SPACE) {
+ ret = __phy_read(phydev, MII_BMCR);
+ if (ret < 0)
+ goto err_restore_page;
+ /* isolation */
+ ret |= BMCR_ISOLATE;
+ ret = __phy_write(phydev, MII_BMCR, ret);
+ if (ret < 0)
+ goto err_restore_page;
+ }
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_utp_softreset_paged() - config YT8824 UTP softreset.
+ * @phydev: a pointer to a &struct phy_device
+ * @reg_space: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE).
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_utp_softreset_paged(struct phy_device *phydev,
+ int reg_space)
+{
+ int old_page;
+ int ret = 0;
+ int val;
+
+ old_page = phy_select_page(phydev, reg_space);
+ if (old_page < 0)
+ goto err_restore_page;
+ if (reg_space == YT8824_RSSR_UTP_SPACE) {
+ ret = __phy_read(phydev, MII_BMCR);
+ if (ret < 0)
+ goto err_restore_page;
+ ret |= BMCR_RESET;
+ ret = __phy_write(phydev, MII_BMCR, ret);
+ if (ret < 0)
+ goto err_restore_page;
+ else
+ phy_unlock_mdio_bus(phydev);
+
+ /* wait until softreset done. */
+ return phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
+ 50000, 600000, true);
+ }
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_utp_normal_test_mode_paged() - config YT8824 to normal test mode.
+ * @phydev: a pointer to a &struct phy_device
+ * @reg_space: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE).
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_utp_normal_test_mode_paged(struct phy_device *phydev,
+ int reg_space)
+{
+ int old_page;
+ int ret = 0;
+
+ old_page = phy_select_page(phydev, reg_space);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ if (reg_space == YT8824_RSSR_UTP_SPACE) {
+ /* normal mode */
+ ret = __phy_read_mmd(phydev, 0x1, YT8824_UTP_TEMPLATE_MODE_CTRL);
+ if (ret < 0)
+ goto err_restore_page;
+ ret &= ~YT8824_UTP_TEMPLATE_MODE_MASK;
+ ret = __phy_write_mmd(phydev, 0x1, YT8824_UTP_TEMPLATE_MODE_CTRL, ret);
+ if (ret < 0)
+ goto err_restore_page;
+ }
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_sds_isolate_and_softreset_paged() - disable YT8824 serdes isolate
+ * and sds softreset.
+ * @phydev: a pointer to a &struct phy_device
+ * @reg_space: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE).
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_sds_isolate_and_softreset_paged(struct phy_device *phydev,
+ int reg_space)
+{
+ int old_page;
+ int ret = 0;
+ int val;
+
+ old_page = phy_select_page(phydev, reg_space);
+ if (old_page < 0)
+ goto err_restore_page;
+ if (reg_space == YT8824_RSSR_FIBER_SPACE) {
+ ret = __phy_read(phydev, MII_BMCR);
+ if (ret < 0)
+ goto err_restore_page;
+ /* disable isolation */
+ ret &= ~BMCR_ISOLATE;
+ /* soft reset */
+ ret |= BMCR_RESET;
+ ret = __phy_write(phydev, MII_BMCR, ret);
+ if (ret < 0)
+ goto err_restore_page;
+ else
+ phy_unlock_mdio_bus(phydev);
+ /* wait until softreset done. */
+ return phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
+ 50000, 600000, true);
+ }
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_soft_reset() - called to do PHY software reset
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_soft_reset(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_INTERNAL) {
+ /* invalid test mode */
+ ret = yt8824_utp_invalid_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ ret = yt8824_utp_softreset_paged(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ /* normal mode */
+ ret = yt8824_utp_normal_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ } else {
+ /* invalid test mode */
+ ret = yt8824_utp_invalid_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* sds isolation */
+ ret = yt8824_sds_isolate_paged(phydev, YT8824_RSSR_FIBER_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* utp soft reset */
+ ret = yt8824_utp_softreset_paged(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* normal mode */
+ ret = yt8824_utp_normal_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* sds soft reset and disable isolation */
+ ret = yt8824_sds_isolate_and_softreset_paged(phydev, YT8824_RSSR_FIBER_SPACE);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+/**
+ * yt8824_config_init_paged() - config external phy8824 init
+ * @phydev: target phy_device struct
+ * @reg_space: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE).
+ *
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_config_init_paged(struct phy_device *phydev, int reg_space)
+{
+ struct yt8824_shared_priv *shared_priv;
+ int ret = 0, old_page;
+ u16 val_1, val_2, val_3, tmp;
+ u16 data = 0;
+ int port;
+
+ shared_priv = phy_package_get_priv(phydev);
+ port = phydev->mdio.addr - shared_priv->offset;
+ old_page = phy_select_page(phydev, reg_space);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ if (reg_space == YT8824_RSSR_FIBER_SPACE) {
+ /* read efuse */
+ val_1 = ytphy_read_top_ext(phydev, 0xa13e);
+ if (val_1 < 0)
+ goto err_restore_page;
+
+ val_2 = ytphy_read_top_ext(phydev, 0xa13f);
+ if (val_2 < 0)
+ goto err_restore_page;
+
+ val_3 = ytphy_read_top_ext(phydev, 0xa140);
+ if (val_3 < 0)
+ goto err_restore_page;
+
+ if (port == 0) {
+ /* Serdes optimization */
+ ret = ytphy_write_ext(phydev, 0x04be, 0x000d);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x049f, 0x7ded);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x04a9, 0x009f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* analog CDR */
+ ret = ytphy_write_ext(phydev, 0x0406, 0x0800);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized VCO */
+ ret = ytphy_write_ext(phydev, 0x0438, 0x9024);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0439, 0x00c0);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized PLL lock */
+ ret = ytphy_read_ext(phydev, 0x0429);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~(BIT(13) | BIT(12));
+ tmp = (val_1 & (BIT(7) | BIT(6)) >> 6);
+ ret |= (tmp << 12);
+ ret = ytphy_write_ext(phydev, 0x0429, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_read_ext(phydev, 0x0441);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~(BIT(1) | BIT(0));
+ tmp = (val_1 & (BIT(5) | BIT(4)) >> 4);
+ ret |= tmp;
+ ret = ytphy_write_ext(phydev, 0x0441, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_read_ext(phydev, 0x042b);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~(BIT(13) | BIT(12));
+ tmp = (val_3 & (BIT(1) | BIT(0)));
+ ret |= (tmp << 12);
+ ret = ytphy_write_ext(phydev, 0x042b, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x043a, 0x1006);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x042a, 0xf070);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* cable length threshold */
+ ret = ytphy_write_ext(phydev, 0x0491, 0x007f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0492, 0x7f7f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* Serdes training threshold */
+ ret = ytphy_write_ext(phydev, 0x0454, 0x0f14);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0497, 0x0a44);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* digital eye diagram of SerDes */
+ ret = ytphy_write_ext(phydev, 0x04cd, 0x0000);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* Serdes LDO */
+ ret = ytphy_read_ext(phydev, 0x04b5);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~(BIT(6) | BIT(5) | BIT(4));
+ tmp = (val_2 & (BIT(4) | BIT(3) | BIT(2)) >> 2);
+ ret |= (tmp << 4);
+ ret = ytphy_write_ext(phydev, 0x04b5, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_read_ext(phydev, 0x04b4);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~(BIT(10) | BIT(9) | BIT(8));
+ tmp = (val_2 & (BIT(7) | BIT(6) | BIT(5)) >> 5);
+ ret |= (tmp << 8);
+ ret = ytphy_write_ext(phydev, 0x04b4, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized Serdes RX */
+ ret = ytphy_write_ext(phydev, 0x04af, 0x45e3);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x048a, 0x0fff);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0408, 0x7c00);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x04d6, 0x007f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x044f, 0xff08);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized Serdes TX */
+ ret = ytphy_write_ext(phydev, 0x048e, 0x7d00);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x000d, 0x0606);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* Serdes manual config */
+ ret = ytphy_write_ext(phydev, 0x04b0, 0x0804);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x04b1, 0x7074);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x04af, 0x45e7);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* restart calibration */
+ ret = ytphy_write_ext(phydev, 0x0003, 0x5603);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0492, 0x7fff);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0492, 0x7f7f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x2000, 0x0040);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x2000, 0x0000);
+ if (ret < 0)
+ goto err_restore_page;
+ }
+
+ /* TX preamble padded to 8; RX IPG always > 8 */
+ data &= ~YT8824_SDS_CFG_MIN_PRE_MASK;
+ data |= YT8824_SDS_TX_PRE_PADDING;
+ data |= YT8824_SDS_EN_FILL_PRE;
+ ret = __phy_write(phydev, MII_RESV1, data);
+ if (ret < 0)
+ goto err_restore_page;
+
+ data = __phy_read(phydev, MII_BMCR);
+ if (data < 0)
+ goto err_restore_page;
+ data |= BMCR_RESET;
+ data |= BMCR_ANENABLE;
+ ret = __phy_write(phydev, MII_BMCR, data);
+ if (ret < 0)
+ goto err_restore_page;
+ } else if (reg_space == YT8824_RSSR_UTP_SPACE) {
+ /* power down */
+ data = __phy_read(phydev, MII_BMCR);
+ if (data < 0)
+ goto err_restore_page;
+ data |= BMCR_PDOWN;
+ ret = __phy_write(phydev, MII_BMCR, data);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* pll calibration */
+ ret = ytphy_write_ext(phydev, 0x0001, 0x0003);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa20e, 0x0cba);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa20a, 0xc3f1);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa20c, 0x1620);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa2b6, 0x0a00);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa2b6, 0x0e00);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimization utp */
+ ret = ytphy_write_ext(phydev, 0x0001, 0x0003);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* enable nibble */
+ ret = ytphy_write_ext(phydev, 0xa003, 0x0003);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* idle err detect enable */
+ ret = ytphy_write_ext(phydev, 0x03d0, 0x5210);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized 2.5G long cable performance */
+ ret = ytphy_write_ext(phydev, 0x0372, 0x5038);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x037c, 0x6068);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0388, 0x00a0);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized fast retrain */
+ ret = ytphy_write_ext(phydev, 0x0359, 0x2140);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x000c, 0xc1a0);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* 2.5G template tone */
+ ret = ytphy_write_ext(phydev, 0xa2fa, 0x0083);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x04e2, 0x0149);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized 2.5G template */
+ ret = ytphy_write_ext(phydev, 0x047e, 0x3939);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x047f, 0x3939);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0480, 0x3939);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0481, 0x3939);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized 1000M cable length threshold */
+ ret = ytphy_write_ext(phydev, 0x0336, 0xab0a);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0340, 0x301d);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* 100M template amplitude */
+ ret = ytphy_write_ext(phydev, 0x046e, 0x4545);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x046f, 0x4545);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0470, 0x4545);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0471, 0x4545);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized 100M cable length threshold */
+ ret = ytphy_write_ext(phydev, 0x030b, 0xaa1d);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x071f, 0x0036);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* 10M template amplitude */
+ ret = ytphy_write_ext(phydev, 0x046b, 0x1818);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x046c, 0x1818);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized 10M cable length threshold */
+ ret = ytphy_write_ext(phydev, 0x0466, 0x6c6c);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0467, 0x6c6c);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0468, 0x6c6c);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0469, 0x6c6c);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimize utp 1000M performance */
+ ret = ytphy_write_ext(phydev, 0x034a, 0xff03);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x00f8, 0xb3ff);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0059, 0x4040);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x032c, 0x5094);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x032d, 0xd094);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x032e, 0x5308);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0322, 0x6440);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x04d3, 0x5220);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x04d2, 0x5220);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized EMC CS */
+ ret = ytphy_write_ext(phydev, 0x00c8, 0xffff);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x00be, 0x6406);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x037a, 0x40ff);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized EMC RE */
+ ret = ytphy_write_ext(phydev, 0x0482, 0xffff);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa2d5, 0x1f1f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa2d6, 0x1f1f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa2d7, 0x1f1f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa2d8, 0x1f1f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa218, 0x006e);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa01d, 0xfff0);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa01e, 0xfff0);
if (ret < 0)
goto err_restore_page;
- /* Enable WOL interrupt */
- ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0,
- YTPHY_IER_WOL);
+ ret = ytphy_write_ext(phydev, 0xa01d, 0xffff);
if (ret < 0)
goto err_restore_page;
- } else {
- old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
- if (old_page < 0)
+ ret = ytphy_write_ext(phydev, 0xa01e, 0xffff);
+ if (ret < 0)
goto err_restore_page;
+ }
- /* Disable WOL feature */
- mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
- ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, 0);
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
- /* Disable WOL interrupt */
- ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG,
- YTPHY_IER_WOL, 0);
+/**
+ * yt8824_internal_config_init_paged() - config internal phy8824 init
+ * @phydev: target phy_device struct
+ * @reg_space: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE).
+ *
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_internal_config_init_paged(struct phy_device *phydev,
+ int reg_space)
+{
+ struct yt8824_shared_priv *shared_priv;
+ int old_page;
+ int port = 0;
+ int ret = 0;
+ u16 data;
+
+ old_page = phy_select_page(phydev, reg_space);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ shared_priv = phy_package_get_priv(phydev);
+ port = phydev->mdio.addr - shared_priv->offset;
+ ret = ytphy_write_ext(phydev, 0x1, 0x3);
+ if (ret < 0)
+ goto err_restore_page;
+ data = __phy_read(phydev, MII_BMCR);
+ if (data < 0)
+ goto err_restore_page;
+ data |= BMCR_PDOWN;
+ ret = __phy_write(phydev, MII_BMCR, data);
+ if (ret < 0)
+ goto err_restore_page;
+ if (port == 0) {
+ ret = ytphy_write_ext(phydev, 0xa20e, 0xcba);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa20a, 0xc3f1);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa20c, 0x1620);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa2b6, 0xa00);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa2b6, 0xe00);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa003, 0x3);
+ if (ret < 0)
+ goto err_restore_page;
+ }
+ ret = ytphy_write_ext(phydev, 0x3d0, 0x5210);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x372, 0x5038);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x37c, 0x6068);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x388, 0xa0);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x359, 0x2140);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_top_ext(phydev, 0xa2fa, 0x83);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x4e2, 0x149);
+ if (ret < 0)
+ goto err_restore_page;
+ /* 2.5G tempate */
+ ret = ytphy_write_ext(phydev, 0x47e, 0x3939);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x47f, 0x3939);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x480, 0x3939);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x481, 0x3939);
+ if (ret < 0)
+ goto err_restore_page;
+ /* 1000 cable length threshold */
+ ret = ytphy_write_ext(phydev, 0x336, 0xab0a);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x340, 0x301d);
+ if (ret < 0)
+ goto err_restore_page;
+ /* 1000 performance */
+ ret = ytphy_write_ext(phydev, 0x34a, 0xff03);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xf8, 0xb3ff);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x32c, 0x5094);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x32d, 0xd094);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x32e, 0x5308);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x322, 0x6440);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x4d3, 0x5220);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x4d2, 0x5220);
+ if (ret < 0)
+ goto err_restore_page;
+ /* 100 tempate */
+ ret = ytphy_write_ext(phydev, 0x46e, 0x4545);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x46f, 0x4545);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x470, 0x4545);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x471, 0x4545);
+ if (ret < 0)
+ goto err_restore_page;
+ /* 100 cable length threshold */
+ ret = ytphy_write_ext(phydev, 0x30b, 0xaa1d);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x71f, 0x36);
+ if (ret < 0)
+ goto err_restore_page;
+ /* 10 tempate */
+ ret = ytphy_write_ext(phydev, 0x46b, 0x1818);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x46c, 0x1818);
+ if (ret < 0)
+ goto err_restore_page;
+ /* 10 tempate MAU*/
+ ret = ytphy_write_ext(phydev, 0x466, 0x6c6c);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x467, 0x6c6c);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x468, 0x6c6c);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x469, 0x6c6c);
+ if (ret < 0)
+ goto err_restore_page;
+ /* EMC CS */
+ ret = ytphy_write_ext(phydev, 0xc8, 0xfff);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xbe, 0x6406);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x37a, 0x40ff);
+ if (ret < 0)
+ goto err_restore_page;
+ /* EMC RE*/
+ ret = ytphy_write_ext(phydev, 0x482, 0xffff);
+ if (ret < 0)
+ goto err_restore_page;
+ if (port == 0) {
+ ret = ytphy_write_ext(phydev, 0x482, 0xffff);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa2d5, 0x1f1f);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa2d6, 0x1f1f);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa2d7, 0x1f1f);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa2d8, 0x1f1f);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa218, 0x6e);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa01d, 0xfff0);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa01e, 0xfff0);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa01d, 0xffff);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa01e, 0xffff);
if (ret < 0)
goto err_restore_page;
}
+ ret = ytphy_write_ext(phydev, 0xc, 0x41a1);
+ if (ret < 0)
+ goto err_restore_page;
+ data = __phy_read_mmd(phydev, 0x1, YT8824_UTP_TEMPLATE_MODE_CTRL);
+ if (data < 0)
+ goto err_restore_page;
+ data &= ~YT8824_UTP_TEMPLATE_MODE_MASK;
+ data |= YT8824_UTP_TEMPLATE_TEST_MODE1;
+ ret = __phy_write_mmd(phydev, 0x1, YT8824_UTP_TEMPLATE_MODE_CTRL, data);
+ data = __phy_read(phydev, MII_BMCR);
+ if (data < 0)
+ goto err_restore_page;
+ data |= BMCR_RESET;
+ data |= BMCR_ANENABLE;
+ ret = __phy_write(phydev, MII_BMCR, data);
+ if (ret < 0)
+ goto err_restore_page;
+ data = __phy_read_mmd(phydev, 0x1, YT8824_UTP_TEMPLATE_MODE_CTRL);
+ if (data < 0)
+ goto err_restore_page;
+ data &= ~YT8824_UTP_TEMPLATE_MODE_MASK;
+ ret = __phy_write_mmd(phydev, 0x1, YT8824_UTP_TEMPLATE_MODE_CTRL, data);
err_restore_page:
return phy_restore_page(phydev, old_page, ret);
@@ -2437,6 +3494,217 @@ static int ytphy_utp_read_abilities(struct phy_device *phydev)
return 0;
}
+/**
+ * yt8824_config_init() - phy initializatioin
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_config_init(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_INTERNAL) {
+ ret = yt8824_internal_config_init_paged(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = yt8824_config_init_paged(phydev, YT8824_RSSR_FIBER_SPACE);
+ if (ret < 0)
+ return ret;
+ ret = yt8824_config_init_paged(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ }
+ ret = yt8824_soft_reset(phydev);
+ if (ret < 0)
+ return ret;
+
+ phydev_dbg(phydev, "%s done, phy addr: %d\n",
+ __func__, phydev->mdio.addr);
+
+ return 0;
+}
+
+/**
+ * yt8824_config_intr() - phy8824 interrupt configuration
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_config_intr(struct phy_device *phydev)
+{
+ struct yt8824_shared_priv *shared_priv;
+ int ret = 0, old_page;
+ int port;
+
+ shared_priv = phy_package_get_priv(phydev);
+ if ((phydev->mdio.addr - shared_priv->offset) < 2)
+ port = 0;
+ else
+ port = 2;
+ old_page = phy_select_page(phydev, YT8824_RSSR_UTP_SPACE);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+ /* top ext reg 0xa000
+ * bit6 int_polarity 1'b0 low active, 1'b1 high active
+ */
+ ret = ytphy_read_top_ext(phydev, YT8521_REG_SPACE_SELECT_REG);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_top_ext(phydev, YT8521_REG_SPACE_SELECT_REG, ret & (~BIT(6)));
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* top ext reg 0xa019
+ * bit5 intr_phy_pulse_en 1'b0 level, 1'b1 pulse
+ */
+ ret = ytphy_read_top_ext(phydev, 0xa019);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_top_ext(phydev, 0xa019, ret | BIT(5));
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* top ext reg 0xa018 phy_pulse_lth bit14:8
+ * top ext reg 0xa019 timer_tick_sel bit8:7
+ * bit14:8 phy_pulse_lth bit8:7 timer_tick_sel pulse width
+ * 0x007a 0x0002 10ms pulse width
+ * 0x0064 0x0001 1ms pulse width
+ * 0x004e 0x0000 100ms pulse width
+ * 0x0009 0x0000 10ms pulse width(default)
+ */
+ ret = ytphy_read_top_ext(phydev, 0xa018);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~0x7f00;
+ ret |= (0x0009 << 8);
+ ret = ytphy_write_top_ext(phydev, 0xa018, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_read_top_ext(phydev, 0xa019);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~0x180;
+ ret |= (0x0000 << 7);
+ ret = ytphy_write_top_ext(phydev, 0xa019, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* top ext reg 0xa01c interrupt state(Read Clear)
+ * bit11 PHY3 1: phy3 link up/down occurs, 0: not occur
+ * bit10 PHY2 1: phy2 link up/down occurs, 0: not occur
+ * bit9 PHY1 1: phy1 link up/down occurs, 0: not occur
+ * bit8 PHY0 1: phy0 link up/down occurs, 0: not occur
+ *
+ * bit7 PHY3 1: phy3 link down occurs, 0: not occur
+ * bit6 PHY2 1: phy2 link down occurs, 0: not occur
+ * bit5 PHY1 1: phy1 link down occurs, 0: not occur
+ * bit4 PHY0 1: phy0 link down occurs, 0: not occur
+ *
+ * bit3 PHY3 1: phy3 link up occurs, 0: not occur
+ * bit2 PHY2 1: phy2 link up occurs, 0: not occur
+ * bit1 PHY1 1: phy1 link up occurs, 0: not occur
+ * bit0 PHY0 1: phy0 link up occurs, 0: not occur
+ */
+ ret = ytphy_read_top_ext(phydev, 0xa01c);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* top ext reg 0xa01a
+ * bit5 intn_wol_mode 1'b1 enable intn_wol pin output
+ * bit4 intn_mode 1'b1 enable intn pin output
+ */
+ ret = ytphy_read_top_ext(phydev, 0xa01a);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~BIT(5);
+ ret |= BIT(4);
+ ret = ytphy_write_top_ext(phydev, 0xa01a, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* top ext reg 0xa01b interrupt mask
+ * bit11 1'b1 enable interrupt signal(link up/down) output to interrupt pin
+ * bit7 1'b1 enable interrupt signal(link down) output to interrupt pin
+ * bit3 1'b1 enable interrupt signal(link up) output to interrupt pin
+ *
+ * bit10 1'b1 enable interrupt signal(link up/down) output to interrupt pin
+ * bit6 1'b1 enable interrupt signal(link down) output to interrupt pin
+ * bit2 1'b1 enable interrupt signal(link up) output to interrupt pin
+ *
+ * bit9 1'b1 enable interrupt signal(link up/down) output to interrupt pin
+ * bit5 1'b1 enable interrupt signal(link down) output to interrupt pin
+ * bit1 1'b1 enable interrupt signal(link up) output to interrupt pin
+ *
+ * bit8 1'b1 enable interrupt signal(link up/down) output to interrupt pin
+ * bit4 1'b1 enable interrupt signal(link down) output to interrupt pin
+ * bit0 1'b1 enable interrupt signal(link up) output to interrupt pin
+ */
+ ret = ytphy_read_top_ext(phydev, 0xa01b);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret |= (BIT(port + 8) | BIT(port + 4) | BIT(port));
+ ret = ytphy_write_top_ext(phydev, 0xa01b, ret);
+ if (ret < 0)
+ goto err_restore_page;
+ }
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_handle_interrupt() - phy8824 interrupt handle
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static irqreturn_t yt8824_handle_interrupt(struct phy_device *phydev)
+{
+ int ret = 0, old_page;
+
+ old_page = phy_select_page(phydev, YT8824_RSSR_UTP_SPACE);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ /* top ext reg 0xa01c interrupt state(Read Clear)
+ * bit11 PHY3 interrupt 1: phy3 link up/down interrupt happened, 0: interrupt not happened
+ * bit10 PHY2 interrupt 1: phy2 link up/down interrupt happened, 0: interrupt not happened
+ * bit9 PHY1 interrupt 1: phy1 link up/down interrupt happened, 0: interrupt not happened
+ * bit8 PHY0 interrupt 1: phy0 link up/down interrupt happened, 0: interrupt not happened
+ * bit7 PHY3 link down 1: phy3 link down interrupt happened, 0: interrupt not happened
+ * bit6 PHY2 link down 1: phy2 link down interrupt happened, 0: interrupt not happened
+ * bit5 PHY1 link down 1: phy1 link down interrupt happened, 0: interrupt not happened
+ * bit4 PHY0 link down 1: phy0 link down interrupt happened, 0: interrupt not happened
+ * bit3 PHY3 link up 1: phy3 link up interrupt happened, 0: interrupt not happened
+ * bit2 PHY2 link up 1: phy2 link up interrupt happened, 0: interrupt not happened
+ * bit1 PHY1 link up 1: phy1 link up interrupt happened, 0: interrupt not happened
+ * bit0 PHY0 link up 1: phy0 link up interrupt happened, 0: interrupt not happened
+ */
+ ret = ytphy_read_top_ext(phydev, 0xa01c);
+ if (ret < 0)
+ goto err_restore_page;
+
+err_restore_page:
+ ret = phy_restore_page(phydev, old_page, ret);
+ if (ret > 0) {
+ phy_trigger_machine(phydev);
+ return IRQ_HANDLED;
+ } else {
+ return IRQ_NONE;
+ }
+}
+
/**
* yt8521_get_features_paged() - read supported link modes for one page
* @phydev: a pointer to a &struct phy_device
@@ -3059,6 +4327,397 @@ static int yt8821_resume(struct phy_device *phydev)
return yt8821_modify_utp_fiber_bmcr(phydev, BMCR_PDOWN, 0);
}
+/**
+ * yt8824_aneg_done() - check negotiation state.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_aneg_done(struct phy_device *phydev)
+{
+ int link = 0;
+ int old_page;
+ int ret = 0;
+
+ old_page = phy_select_page(phydev, YT8824_RSSR_UTP_SPACE);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ link = !!(__phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG) &
+ YTPHY_SSR_LINK);
+
+ phydev_dbg(phydev, "%s, phy addr: %d, link_utp: %d\n",
+ __func__, phydev->mdio.addr, link);
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_read_status_paged() - determines the speed and duplex of one page
+ * @phydev: a pointer to a &struct phy_device
+ * @page: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE) to
+ * operate.
+ * @status: The link status, include speed, duplex and link state.
+ * @lpa: link partner advertising.
+ *
+ * Returns: 1 (utp or fiber link),0 (no link) or negative errno code
+ */
+static int yt8824_read_status_paged(struct phy_device *phydev, int page,
+ int *status, int *lpa)
+{
+ int old_page;
+ int ret = 0;
+
+ page &= YT8824_RSSR_SPACE_MASK;
+ old_page = phy_select_page(phydev, page);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ ret = __phy_read(phydev, MII_LPA);
+ *lpa = ret;
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG);
+ *status = ret;
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = !!(*status & YTPHY_SSR_LINK);
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_read_status() - determines the negotiated speed and duplex
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_read_status(struct phy_device *phydev)
+{
+ int link;
+ int lpa;
+ int val;
+
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
+ phydev->link = 0;
+ phydev->speed = SPEED_UNKNOWN;
+ phydev->duplex = DUPLEX_UNKNOWN;
+
+ link = yt8824_read_status_paged(phydev,
+ YT8824_RSSR_UTP_SPACE, &val, &lpa);
+ if (link < 0)
+ return link;
+
+ if (link) {
+ phydev->link = 1;
+ phydev->pause = !!(lpa & BIT(10));
+ phydev->asym_pause = !!(lpa & BIT(11));
+
+ /* update speed & duplex */
+ yt8821_adjust_status(phydev, val);
+ } else {
+ phydev->link = 0;
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
+ phydev->speed = SPEED_UNKNOWN;
+ phydev->duplex = DUPLEX_UNKNOWN;
+ }
+
+ return 0;
+}
+
+/**
+ * yt8824_utp_power_on(): utp power on.
+ * @phydev: a pointer to a &struct phy_device
+ * @reg_space: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE)
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_utp_power_on(struct phy_device *phydev,
+ int reg_space)
+{
+ int old_page;
+ int ret = 0;
+
+ old_page = phy_select_page(phydev, reg_space);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ ret = __phy_read(phydev, MII_BMCR);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~BMCR_PDOWN;
+ ret &= ~BMCR_ISOLATE;
+
+ ret = __phy_write(phydev, MII_BMCR, ret);
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_utp_power_down(): utp power down.
+ * @phydev: a pointer to a &struct phy_device
+ * @reg_space: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE)
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_utp_power_down(struct phy_device *phydev,
+ int reg_space)
+{
+ int ret = 0, old_page;
+
+ old_page = phy_select_page(phydev, YT8824_RSSR_UTP_SPACE);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ ret = __phy_read(phydev, MII_BMCR);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = __phy_write(phydev, MII_BMCR, ret | BMCR_PDOWN);
+ if (ret < 0)
+ goto err_restore_page;
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_power_on() - set utp power on.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * NOTE: need WA like softreset
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_power_on(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_INTERNAL) {
+ /* invalid test mode */
+ ret = yt8824_utp_invalid_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ /* utp power on */
+ ret = yt8824_utp_power_on(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ /* normal mode */
+ ret = yt8824_utp_normal_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ } else {
+ /* invalid test mode */
+ ret = yt8824_utp_invalid_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* sds isolation */
+ ret = yt8824_sds_isolate_paged(phydev, YT8824_RSSR_FIBER_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* utp power on */
+ ret = yt8824_utp_power_on(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* normal mode */
+ ret = yt8824_utp_normal_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* sds soft reset and disable isolation */
+ ret = yt8824_sds_isolate_and_softreset_paged(phydev, YT8824_RSSR_FIBER_SPACE);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+/**
+ * yt8824_resume() - resume the hardware
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_resume(struct phy_device *phydev)
+{
+ return yt8824_power_on(phydev);
+}
+
+/**
+ * yt8824_power_down() - set utp power down.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * NOTE: need WA like softreset
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_power_down(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_INTERNAL) {
+ /* invalid test mode */
+ ret = yt8824_utp_invalid_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ /* utp power down */
+ ret = yt8824_utp_power_down(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ /* normal mode */
+ ret = yt8824_utp_normal_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ } else {
+ /* invalid test mode */
+ ret = yt8824_utp_invalid_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* sds isolation */
+ ret = yt8824_sds_isolate_paged(phydev, YT8824_RSSR_FIBER_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* utp power down */
+ ret = yt8824_utp_power_down(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* normal mode */
+ ret = yt8824_utp_normal_test_mode_paged(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* sds soft reset and disable isolation */
+ ret = yt8824_sds_isolate_and_softreset_paged(phydev,
+ YT8824_RSSR_FIBER_SPACE);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+/**
+ * yt8824_suspend() - suspend the hardware
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_suspend(struct phy_device *phydev)
+{
+ return yt8824_power_down(phydev);
+}
+
+/**
+ * yt8824_config_aneg()
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_config_aneg(struct phy_device *phydev)
+{
+ int phy_ctrl = 0;
+ int old_page;
+ int ret = 0;
+
+ old_page = phy_select_page(phydev, YT8824_RSSR_UTP_SPACE);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+ phydev->advertising))
+ phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
+
+ ret = __phy_modify_mmd_changed(phydev, MDIO_MMD_AN,
+ MDIO_AN_10GBT_CTRL,
+ MDIO_AN_10GBT_CTRL_ADV2_5G,
+ phy_ctrl);
+ if (ret)
+ goto err_restore_page;
+ else
+ phy_unlock_mdio_bus(phydev);
+
+ return genphy_config_aneg(phydev);
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_phy_package_probe_once() - init phy packet for phy8824.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_phy_package_probe_once(struct phy_device *phydev)
+{
+ struct yt8824_shared_priv *priv = phy_package_get_priv(phydev);
+ struct device_node *np = phy_package_get_node(phydev);
+ const char *interface_mode_name;
+
+ priv->interface_mode = PHY_INTERFACE_MODE_NA;
+ priv->offset = INTERNAL_PHY8824_TOP_EXTEND_OFFSET;
+ if (!of_property_read_string(np, "motorcomm,interface-mode",
+ &interface_mode_name)) {
+ if (!strcasecmp(interface_mode_name,
+ phy_modes(PHY_INTERFACE_MODE_USXGMII))) {
+ priv->interface_mode = PHY_INTERFACE_MODE_USXGMII;
+ priv->offset = EXTERNAL_PHY8824_TOP_EXTEND_OFFSET;
+ } else if (!strcasecmp(interface_mode_name,
+ phy_modes(PHY_INTERFACE_MODE_INTERNAL))) {
+ priv->interface_mode = PHY_INTERFACE_MODE_INTERNAL;
+ priv->offset = INTERNAL_PHY8824_TOP_EXTEND_OFFSET;
+ } else {
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+/**
+ * yt8824_probe() - phy8824 probe.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_probe(struct phy_device *phydev)
+{
+ struct device *dev = &phydev->mdio.dev;
+ struct yt8824_shared_priv *shared_priv;
+ struct yt8521_priv *priv;
+ int ret;
+
+ ret = devm_of_phy_package_join(dev, phydev, sizeof(*shared_priv));
+ if (ret)
+ return ret;
+
+ if (phy_package_probe_once(phydev)) {
+ ret = yt8824_phy_package_probe_once(phydev);
+ if (ret)
+ return ret;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ phydev->priv = priv;
+
+ return 0;
+}
+
static struct phy_driver motorcomm_phy_drvs[] = {
{
PHY_ID_MATCH_EXACT(PHY_ID_YT8511),
@@ -3145,13 +4804,31 @@ static struct phy_driver motorcomm_phy_drvs[] = {
.suspend = yt8821_suspend,
.resume = yt8821_resume,
},
+ {
+ PHY_ID_MATCH_EXACT(PHY_ID_YT8824),
+ .name = "YT8824 Quad Ports 2.5Gbps Ethernet",
+ .get_features = yt8821_get_features,
+ .read_page = yt8824_read_page,
+ .write_page = yt8824_write_page,
+ .config_intr = yt8824_config_intr,
+ .handle_interrupt = yt8824_handle_interrupt,
+ .probe = yt8824_probe,
+ .config_aneg = yt8824_config_aneg,
+ .aneg_done = yt8824_aneg_done,
+ .config_init = yt8824_config_init,
+ .read_status = yt8824_read_status,
+ .soft_reset = yt8824_soft_reset,
+ .suspend = yt8824_suspend,
+ .resume = yt8824_resume,
+ },
};
module_phy_driver(motorcomm_phy_drvs);
-MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S/8821 PHY driver");
+MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S/8821/8824 PHY driver");
MODULE_AUTHOR("Peter Geis");
MODULE_AUTHOR("Frank");
+MODULE_AUTHOR("Kyle");
MODULE_LICENSE("GPL");
static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
@@ -3161,6 +4838,7 @@ static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531) },
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8821) },
+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8824) },
{ /* sentinel */ }
};
--
2.25.1
^ permalink raw reply related
* Re: [PATCH net-next 2/5] ipv6: ndisc: Add ndisc_check_ns_na() validation helper
From: Nikolay Aleksandrov @ 2026-07-20 9:13 UTC (permalink / raw)
To: Danielle Ratson, netdev
Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms, ja, petrm,
fw, kuniyu, bridge, linux-kernel
In-Reply-To: <c7f894446494462bd72f717021aa4b6373f31131.1784463131.git.danieller@nvidia.com>
On 19/07/2026 16:34, Danielle Ratson wrote:
> Add ndisc_check_ns_na(), a standalone NS/NA packet validator modeled
> after ipv6_mc_check_mld(). It performs the RFC 4861 section 7.1.1
> (Neighbor Solicitation) and 7.1.2 (Neighbor Advertisement) mandatory
> checks that are relevant for software operating at the bridge level,
> where packets bypass the normal IPv6 stack path:
>
> - Hop Limit must be 255 (packet was not forwarded by a router)
> - ICMPv6 checksum is valid
> - ICMP Code is 0
> - ICMP length is at least 24 octets (sizeof(struct nd_msg))
> - Target Address must not be a multicast address
> - All included options have a length that is greater than zero
> - NS/DAD: destination must be a solicited-node multicast address
> - NS/DAD: no Source Link-Layer Address option when source is unspecified
> - NA: Solicited flag must be 0 when IP Destination is multicast
>
> On success the function sets the skb transport header and returns 0,
> matching the convention of ipv6_mc_check_mld().
>
> Reviewed-by: Petr Machata <petrm@nvidia.com>
> Signed-off-by: Danielle Ratson <danieller@nvidia.com>
> ---
> include/net/ndisc.h | 2 +
> net/ipv6/Makefile | 2 +-
> net/ipv6/ndisc_snoop.c | 190 +++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 193 insertions(+), 1 deletion(-)
> create mode 100644 net/ipv6/ndisc_snoop.c
>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
^ permalink raw reply
* Re: [PATCH net-next 3/5] bridge: Validate NS/NA messages using ndisc_check_ns_na()
From: Nikolay Aleksandrov @ 2026-07-20 9:14 UTC (permalink / raw)
To: Danielle Ratson, netdev
Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms, ja, petrm,
fw, kuniyu, bridge, linux-kernel
In-Reply-To: <63efa708b4b0bd9edb3a6f6a521cb5022c43aaa5.1784463131.git.danieller@nvidia.com>
On 19/07/2026 16:34, Danielle Ratson wrote:
> The bridge performs neighbor suppression by snooping NS/NA messages, but
> previously only checked the ICMPv6 type and code. This leaves it open to
> acting on malformed or spoofed packets that any RFC-compliant node should
> reject.
>
> Wire br_is_nd_neigh_msg() into the new ndisc_check_ns_na() helper, which
> enforces the full RFC 4861 section 7.1.1/7.1.2 receive validation:
> hop limit of 255, valid checksum, correct code, and type-specific rules
> (NS target not multicast; NA solicited flag clear for multicast
> destinations).
>
> MLD messages are already validated by ipv6_mc_check_mld() before the
> bridge acts on them; this brings NS/NA to the same standard.
>
> As a side effect, the skb parameter of br_is_nd_neigh_msg() changes from
> const to non-const, since ndisc_check_ns_na() may reallocate the skb head
> via pskb_may_pull() and sets the transport header. The returned pointer is
> now derived from skb_transport_header() rather than a direct cast.
>
> Reviewed-by: Petr Machata <petrm@nvidia.com>
> Signed-off-by: Danielle Ratson <danieller@nvidia.com>
> ---
> net/bridge/br_arp_nd_proxy.c | 11 ++++-------
> net/bridge/br_private.h | 2 +-
> 2 files changed, 5 insertions(+), 8 deletions(-)
>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
^ permalink raw reply
* [PATCH 0/8] driver core: prefer platform_device_set_fwnode()
From: Bartosz Golaszewski @ 2026-07-20 9:24 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
Florian Fainelli, Broadcom internal kernel review list,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
Sebastian Hesselbarth, Srinivas Kandagatla
Cc: brgl, driver-core, linuxppc-dev, linux-kernel, linux-i2c, iommu,
netdev, linux-pm, imx, linux-arm-kernel, mfd, linux-arm-msm,
linux-sound, Bartosz Golaszewski
With the final platform device and software node rework changes having
been queued in the driver core tree, Danilo pointed out that
platform_device_set_fwnode() is no longer used and should be removed. I
argued that instead we should prefer it over the OF-specific
platform_device_set_of_node().
This series converts drivers using platform_device_set_of_node() to
platform_device_set_fwnode() and does not intend any functional change
as the semantics of:
platform_device_set_of_node(pdev, np);
should be equal to those of:
platform_device_set_fwnode(pdev, of_fwnode_handle(np));
The prerequisite changes are currently in the driver core tree so this
entire series should follow them as well with Acks from maintainers. If
it doesn't make v7.3 for some reason, then I will resend these next
cycle separately targetting individual trees.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
Bartosz Golaszewski (8):
powerpc/powermac: use platform_device_set_fwnode()
i2c: pxa-pci: use platform_device_set_fwnode()
iommu/fsl: use platform_device_set_fwnode()
net: bcmgenet: use platform_device_set_fwnode()
pmdomain: imx: use platform_device_set_fwnode()
mfd: tps6586: use platform_device_set_fwnode()
net: mv643xx: use platform_device_set_fwnode()
slimbus: qcom-ngd-ctrl: use platform_device_set_fwnode()
arch/powerpc/platforms/powermac/low_i2c.c | 2 +-
drivers/i2c/busses/i2c-pxa-pci.c | 2 +-
drivers/iommu/fsl_pamu.c | 2 +-
drivers/mfd/tps6586x.c | 2 +-
drivers/net/ethernet/broadcom/genet/bcmmii.c | 3 ++-
drivers/net/ethernet/marvell/mv643xx_eth.c | 2 +-
drivers/pmdomain/imx/gpc.c | 2 +-
drivers/slimbus/qcom-ngd-ctrl.c | 2 +-
8 files changed, 9 insertions(+), 8 deletions(-)
---
base-commit: 27edebd505791748a77703e311d76f0f55a8d7ec
change-id: 20260720-pdev-set-fwnode-instead-of-of-node-e83ece371509
Best regards,
--
Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
^ permalink raw reply
* [PATCH 1/8] powerpc/powermac: use platform_device_set_fwnode()
From: Bartosz Golaszewski @ 2026-07-20 9:24 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
Florian Fainelli, Broadcom internal kernel review list,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
Sebastian Hesselbarth, Srinivas Kandagatla
Cc: brgl, driver-core, linuxppc-dev, linux-kernel, linux-i2c, iommu,
netdev, linux-pm, imx, linux-arm-kernel, mfd, linux-arm-msm,
linux-sound, Bartosz Golaszewski
In-Reply-To: <20260720-pdev-set-fwnode-instead-of-of-node-v1-0-2dee93f42c54@oss.qualcomm.com>
Prefer the higher-level platform_device_set_fwnode() over the
OF-specific platform_device_set_of_node() for dynamically allocated
platform devices.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
arch/powerpc/platforms/powermac/low_i2c.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c
index a175a32a222bab4cc7400f6ab6071f5630db2cb8..eda35534cfd72e828616f27b5f8319189d68285e 100644
--- a/arch/powerpc/platforms/powermac/low_i2c.c
+++ b/arch/powerpc/platforms/powermac/low_i2c.c
@@ -1471,7 +1471,7 @@ static int __init pmac_i2c_create_platform_devices(void)
if (bus->platform_dev == NULL)
return -ENOMEM;
bus->platform_dev->dev.platform_data = bus;
- platform_device_set_of_node(bus->platform_dev, bus->busnode);
+ platform_device_set_fwnode(bus->platform_dev, of_fwnode_handle(bus->busnode));
platform_device_add(bus->platform_dev);
}
--
2.47.3
^ permalink raw reply related
* [PATCH 2/8] i2c: pxa-pci: use platform_device_set_fwnode()
From: Bartosz Golaszewski @ 2026-07-20 9:24 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
Florian Fainelli, Broadcom internal kernel review list,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
Sebastian Hesselbarth, Srinivas Kandagatla
Cc: brgl, driver-core, linuxppc-dev, linux-kernel, linux-i2c, iommu,
netdev, linux-pm, imx, linux-arm-kernel, mfd, linux-arm-msm,
linux-sound, Bartosz Golaszewski
In-Reply-To: <20260720-pdev-set-fwnode-instead-of-of-node-v1-0-2dee93f42c54@oss.qualcomm.com>
Prefer the higher-level platform_device_set_fwnode() over the
OF-specific platform_device_set_of_node() for dynamically allocated
platform devices.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
drivers/i2c/busses/i2c-pxa-pci.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/i2c/busses/i2c-pxa-pci.c b/drivers/i2c/busses/i2c-pxa-pci.c
index 92a0647f08c69f841ca99caca757c1728b3f6fce..58532cf9f06d43a0f2173b962eb498c9e4bb81d0 100644
--- a/drivers/i2c/busses/i2c-pxa-pci.c
+++ b/drivers/i2c/busses/i2c-pxa-pci.c
@@ -77,7 +77,7 @@ static struct platform_device *add_i2c_device(struct pci_dev *dev, int bar)
}
pdev->dev.parent = &dev->dev;
- platform_device_set_of_node(pdev, child);
+ platform_device_set_fwnode(pdev, of_fwnode_handle(child));
ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res));
if (ret)
--
2.47.3
^ permalink raw reply related
* [PATCH 3/8] iommu/fsl: use platform_device_set_fwnode()
From: Bartosz Golaszewski @ 2026-07-20 9:24 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
Florian Fainelli, Broadcom internal kernel review list,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
Sebastian Hesselbarth, Srinivas Kandagatla
Cc: brgl, driver-core, linuxppc-dev, linux-kernel, linux-i2c, iommu,
netdev, linux-pm, imx, linux-arm-kernel, mfd, linux-arm-msm,
linux-sound, Bartosz Golaszewski
In-Reply-To: <20260720-pdev-set-fwnode-instead-of-of-node-v1-0-2dee93f42c54@oss.qualcomm.com>
Prefer the higher-level platform_device_set_fwnode() over the
OF-specific platform_device_set_of_node() for dynamically allocated
platform devices.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
drivers/iommu/fsl_pamu.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/iommu/fsl_pamu.c b/drivers/iommu/fsl_pamu.c
index c83bbc3faad56d6ee1c89b0a7f74028af02c81e9..268a1f752fbceab4fd24013aeea5df1b6982fbb1 100644
--- a/drivers/iommu/fsl_pamu.c
+++ b/drivers/iommu/fsl_pamu.c
@@ -975,7 +975,7 @@ static __init int fsl_pamu_init(void)
goto error_device_alloc;
}
- platform_device_set_of_node(pdev, np);
+ platform_device_set_fwnode(pdev, of_fwnode_handle(np));
ret = pamu_domain_init();
if (ret)
--
2.47.3
^ permalink raw reply related
* [PATCH 4/8] net: bcmgenet: use platform_device_set_fwnode()
From: Bartosz Golaszewski @ 2026-07-20 9:24 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
Florian Fainelli, Broadcom internal kernel review list,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
Sebastian Hesselbarth, Srinivas Kandagatla
Cc: brgl, driver-core, linuxppc-dev, linux-kernel, linux-i2c, iommu,
netdev, linux-pm, imx, linux-arm-kernel, mfd, linux-arm-msm,
linux-sound, Bartosz Golaszewski
In-Reply-To: <20260720-pdev-set-fwnode-instead-of-of-node-v1-0-2dee93f42c54@oss.qualcomm.com>
Prefer the higher-level platform_device_set_fwnode() over the
OF-specific platform_device_set_of_node() for dynamically allocated
platform devices.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
drivers/net/ethernet/broadcom/genet/bcmmii.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c
index 0f0dbabfaabbce3469de79af91d7731b8476709f..6f471723bd53f454e4b0de1716977b4382013429 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmmii.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c
@@ -492,7 +492,8 @@ static int bcmgenet_mii_register(struct bcmgenet_priv *priv)
ppdev->dev.parent = &pdev->dev;
if (dn)
- platform_device_set_of_node(ppdev, bcmgenet_mii_of_find_mdio(priv));
+ platform_device_set_fwnode(ppdev,
+ of_fwnode_handle(bcmgenet_mii_of_find_mdio(priv)));
else
ppd.phy_mask = ~0;
--
2.47.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox