xref: /linux-6.15/net/ethtool/debug.c (revision ff419afa)
1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 #include "netlink.h"
4 #include "common.h"
5 #include "bitset.h"
6 
7 struct debug_req_info {
8 	struct ethnl_req_info		base;
9 };
10 
11 struct debug_reply_data {
12 	struct ethnl_reply_data		base;
13 	u32				msg_mask;
14 };
15 
16 #define DEBUG_REPDATA(__reply_base) \
17 	container_of(__reply_base, struct debug_reply_data, base)
18 
19 const struct nla_policy ethnl_debug_get_policy[] = {
20 	[ETHTOOL_A_DEBUG_HEADER]	= { .type = NLA_NESTED },
21 };
22 
23 static int debug_prepare_data(const struct ethnl_req_info *req_base,
24 			      struct ethnl_reply_data *reply_base,
25 			      struct genl_info *info)
26 {
27 	struct debug_reply_data *data = DEBUG_REPDATA(reply_base);
28 	struct net_device *dev = reply_base->dev;
29 	int ret;
30 
31 	if (!dev->ethtool_ops->get_msglevel)
32 		return -EOPNOTSUPP;
33 
34 	ret = ethnl_ops_begin(dev);
35 	if (ret < 0)
36 		return ret;
37 	data->msg_mask = dev->ethtool_ops->get_msglevel(dev);
38 	ethnl_ops_complete(dev);
39 
40 	return 0;
41 }
42 
43 static int debug_reply_size(const struct ethnl_req_info *req_base,
44 			    const struct ethnl_reply_data *reply_base)
45 {
46 	const struct debug_reply_data *data = DEBUG_REPDATA(reply_base);
47 	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
48 
49 	return ethnl_bitset32_size(&data->msg_mask, NULL, NETIF_MSG_CLASS_COUNT,
50 				   netif_msg_class_names, compact);
51 }
52 
53 static int debug_fill_reply(struct sk_buff *skb,
54 			    const struct ethnl_req_info *req_base,
55 			    const struct ethnl_reply_data *reply_base)
56 {
57 	const struct debug_reply_data *data = DEBUG_REPDATA(reply_base);
58 	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
59 
60 	return ethnl_put_bitset32(skb, ETHTOOL_A_DEBUG_MSGMASK, &data->msg_mask,
61 				  NULL, NETIF_MSG_CLASS_COUNT,
62 				  netif_msg_class_names, compact);
63 }
64 
65 const struct ethnl_request_ops ethnl_debug_request_ops = {
66 	.request_cmd		= ETHTOOL_MSG_DEBUG_GET,
67 	.reply_cmd		= ETHTOOL_MSG_DEBUG_GET_REPLY,
68 	.hdr_attr		= ETHTOOL_A_DEBUG_HEADER,
69 	.req_info_size		= sizeof(struct debug_req_info),
70 	.reply_data_size	= sizeof(struct debug_reply_data),
71 
72 	.prepare_data		= debug_prepare_data,
73 	.reply_size		= debug_reply_size,
74 	.fill_reply		= debug_fill_reply,
75 };
76 
77 /* DEBUG_SET */
78 
79 const struct nla_policy ethnl_debug_set_policy[] = {
80 	[ETHTOOL_A_DEBUG_HEADER]	= { .type = NLA_NESTED },
81 	[ETHTOOL_A_DEBUG_MSGMASK]	= { .type = NLA_NESTED },
82 };
83 
84 int ethnl_set_debug(struct sk_buff *skb, struct genl_info *info)
85 {
86 	struct ethnl_req_info req_info = {};
87 	struct nlattr **tb = info->attrs;
88 	struct net_device *dev;
89 	bool mod = false;
90 	u32 msg_mask;
91 	int ret;
92 
93 	ret = ethnl_parse_header_dev_get(&req_info,
94 					 tb[ETHTOOL_A_DEBUG_HEADER],
95 					 genl_info_net(info), info->extack,
96 					 true);
97 	if (ret < 0)
98 		return ret;
99 	dev = req_info.dev;
100 	ret = -EOPNOTSUPP;
101 	if (!dev->ethtool_ops->get_msglevel || !dev->ethtool_ops->set_msglevel)
102 		goto out_dev;
103 
104 	rtnl_lock();
105 	ret = ethnl_ops_begin(dev);
106 	if (ret < 0)
107 		goto out_rtnl;
108 
109 	msg_mask = dev->ethtool_ops->get_msglevel(dev);
110 	ret = ethnl_update_bitset32(&msg_mask, NETIF_MSG_CLASS_COUNT,
111 				    tb[ETHTOOL_A_DEBUG_MSGMASK],
112 				    netif_msg_class_names, info->extack, &mod);
113 	if (ret < 0 || !mod)
114 		goto out_ops;
115 
116 	dev->ethtool_ops->set_msglevel(dev, msg_mask);
117 	ethtool_notify(dev, ETHTOOL_MSG_DEBUG_NTF, NULL);
118 
119 out_ops:
120 	ethnl_ops_complete(dev);
121 out_rtnl:
122 	rtnl_unlock();
123 out_dev:
124 	dev_put(dev);
125 	return ret;
126 }
127