1 // SPDX-License-Identifier: GPL-2.0-only
7 struct linkstate_req_info
{
8 struct ethnl_req_info base
;
11 struct linkstate_reply_data
{
12 struct ethnl_reply_data base
;
16 struct ethtool_link_ext_stats link_stats
;
17 bool link_ext_state_provided
;
18 struct ethtool_link_ext_state_info ethtool_link_ext_state_info
;
21 #define LINKSTATE_REPDATA(__reply_base) \
22 container_of(__reply_base, struct linkstate_reply_data, base)
24 const struct nla_policy ethnl_linkstate_get_policy
[] = {
25 [ETHTOOL_A_LINKSTATE_HEADER
] =
26 NLA_POLICY_NESTED(ethnl_header_policy_stats
),
29 static int linkstate_get_sqi(struct net_device
*dev
)
31 struct phy_device
*phydev
= dev
->phydev
;
37 mutex_lock(&phydev
->lock
);
38 if (!phydev
->drv
|| !phydev
->drv
->get_sqi
)
40 else if (!phydev
->link
)
43 ret
= phydev
->drv
->get_sqi(phydev
);
44 mutex_unlock(&phydev
->lock
);
49 static int linkstate_get_sqi_max(struct net_device
*dev
)
51 struct phy_device
*phydev
= dev
->phydev
;
57 mutex_lock(&phydev
->lock
);
58 if (!phydev
->drv
|| !phydev
->drv
->get_sqi_max
)
60 else if (!phydev
->link
)
63 ret
= phydev
->drv
->get_sqi_max(phydev
);
64 mutex_unlock(&phydev
->lock
);
69 static bool linkstate_sqi_critical_error(int sqi
)
71 return sqi
< 0 && sqi
!= -EOPNOTSUPP
&& sqi
!= -ENETDOWN
;
74 static bool linkstate_sqi_valid(struct linkstate_reply_data
*data
)
76 return data
->sqi
>= 0 && data
->sqi_max
>= 0 &&
77 data
->sqi
<= data
->sqi_max
;
80 static int linkstate_get_link_ext_state(struct net_device
*dev
,
81 struct linkstate_reply_data
*data
)
85 if (!dev
->ethtool_ops
->get_link_ext_state
)
88 err
= dev
->ethtool_ops
->get_link_ext_state(dev
, &data
->ethtool_link_ext_state_info
);
92 data
->link_ext_state_provided
= true;
97 static int linkstate_prepare_data(const struct ethnl_req_info
*req_base
,
98 struct ethnl_reply_data
*reply_base
,
99 const struct genl_info
*info
)
101 struct linkstate_reply_data
*data
= LINKSTATE_REPDATA(reply_base
);
102 struct net_device
*dev
= reply_base
->dev
;
105 ret
= ethnl_ops_begin(dev
);
108 data
->link
= __ethtool_get_link(dev
);
110 ret
= linkstate_get_sqi(dev
);
111 if (linkstate_sqi_critical_error(ret
))
115 ret
= linkstate_get_sqi_max(dev
);
116 if (linkstate_sqi_critical_error(ret
))
120 if (dev
->flags
& IFF_UP
) {
121 ret
= linkstate_get_link_ext_state(dev
, data
);
122 if (ret
< 0 && ret
!= -EOPNOTSUPP
&& ret
!= -ENODATA
)
126 ethtool_stats_init((u64
*)&data
->link_stats
,
127 sizeof(data
->link_stats
) / 8);
129 if (req_base
->flags
& ETHTOOL_FLAG_STATS
) {
131 data
->link_stats
.link_down_events
=
132 READ_ONCE(dev
->phydev
->link_down_events
);
134 if (dev
->ethtool_ops
->get_link_ext_stats
)
135 dev
->ethtool_ops
->get_link_ext_stats(dev
,
141 ethnl_ops_complete(dev
);
145 static int linkstate_reply_size(const struct ethnl_req_info
*req_base
,
146 const struct ethnl_reply_data
*reply_base
)
148 struct linkstate_reply_data
*data
= LINKSTATE_REPDATA(reply_base
);
151 len
= nla_total_size(sizeof(u8
)) /* LINKSTATE_LINK */
154 if (linkstate_sqi_valid(data
)) {
155 len
+= nla_total_size(sizeof(u32
)); /* LINKSTATE_SQI */
156 len
+= nla_total_size(sizeof(u32
)); /* LINKSTATE_SQI_MAX */
159 if (data
->link_ext_state_provided
)
160 len
+= nla_total_size(sizeof(u8
)); /* LINKSTATE_EXT_STATE */
162 if (data
->ethtool_link_ext_state_info
.__link_ext_substate
)
163 len
+= nla_total_size(sizeof(u8
)); /* LINKSTATE_EXT_SUBSTATE */
165 if (data
->link_stats
.link_down_events
!= ETHTOOL_STAT_NOT_SET
)
166 len
+= nla_total_size(sizeof(u32
));
171 static int linkstate_fill_reply(struct sk_buff
*skb
,
172 const struct ethnl_req_info
*req_base
,
173 const struct ethnl_reply_data
*reply_base
)
175 struct linkstate_reply_data
*data
= LINKSTATE_REPDATA(reply_base
);
177 if (data
->link
>= 0 &&
178 nla_put_u8(skb
, ETHTOOL_A_LINKSTATE_LINK
, !!data
->link
))
181 if (linkstate_sqi_valid(data
)) {
182 if (nla_put_u32(skb
, ETHTOOL_A_LINKSTATE_SQI
, data
->sqi
))
185 if (nla_put_u32(skb
, ETHTOOL_A_LINKSTATE_SQI_MAX
,
190 if (data
->link_ext_state_provided
) {
191 if (nla_put_u8(skb
, ETHTOOL_A_LINKSTATE_EXT_STATE
,
192 data
->ethtool_link_ext_state_info
.link_ext_state
))
195 if (data
->ethtool_link_ext_state_info
.__link_ext_substate
&&
196 nla_put_u8(skb
, ETHTOOL_A_LINKSTATE_EXT_SUBSTATE
,
197 data
->ethtool_link_ext_state_info
.__link_ext_substate
))
201 if (data
->link_stats
.link_down_events
!= ETHTOOL_STAT_NOT_SET
)
202 if (nla_put_u32(skb
, ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT
,
203 data
->link_stats
.link_down_events
))
209 const struct ethnl_request_ops ethnl_linkstate_request_ops
= {
210 .request_cmd
= ETHTOOL_MSG_LINKSTATE_GET
,
211 .reply_cmd
= ETHTOOL_MSG_LINKSTATE_GET_REPLY
,
212 .hdr_attr
= ETHTOOL_A_LINKSTATE_HEADER
,
213 .req_info_size
= sizeof(struct linkstate_req_info
),
214 .reply_data_size
= sizeof(struct linkstate_reply_data
),
216 .prepare_data
= linkstate_prepare_data
,
217 .reply_size
= linkstate_reply_size
,
218 .fill_reply
= linkstate_fill_reply
,