1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | |
3 | #include <net/switchdev.h> |
4 | |
5 | #include "br_private_mrp.h" |
6 | |
7 | static enum br_mrp_hw_support |
8 | br_mrp_switchdev_port_obj(struct net_bridge *br, |
9 | const struct switchdev_obj *obj, bool add) |
10 | { |
11 | int err; |
12 | |
13 | if (add) |
14 | err = switchdev_port_obj_add(dev: br->dev, obj, NULL); |
15 | else |
16 | err = switchdev_port_obj_del(dev: br->dev, obj); |
17 | |
18 | /* In case of success just return and notify the SW that doesn't need |
19 | * to do anything |
20 | */ |
21 | if (!err) |
22 | return BR_MRP_HW; |
23 | |
24 | if (err != -EOPNOTSUPP) |
25 | return BR_MRP_NONE; |
26 | |
27 | /* Continue with SW backup */ |
28 | return BR_MRP_SW; |
29 | } |
30 | |
31 | int br_mrp_switchdev_add(struct net_bridge *br, struct br_mrp *mrp) |
32 | { |
33 | struct switchdev_obj_mrp mrp_obj = { |
34 | .obj.orig_dev = br->dev, |
35 | .obj.id = SWITCHDEV_OBJ_ID_MRP, |
36 | .p_port = rtnl_dereference(mrp->p_port)->dev, |
37 | .s_port = rtnl_dereference(mrp->s_port)->dev, |
38 | .ring_id = mrp->ring_id, |
39 | .prio = mrp->prio, |
40 | }; |
41 | |
42 | if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) |
43 | return 0; |
44 | |
45 | return switchdev_port_obj_add(dev: br->dev, obj: &mrp_obj.obj, NULL); |
46 | } |
47 | |
48 | int br_mrp_switchdev_del(struct net_bridge *br, struct br_mrp *mrp) |
49 | { |
50 | struct switchdev_obj_mrp mrp_obj = { |
51 | .obj.orig_dev = br->dev, |
52 | .obj.id = SWITCHDEV_OBJ_ID_MRP, |
53 | .p_port = NULL, |
54 | .s_port = NULL, |
55 | .ring_id = mrp->ring_id, |
56 | }; |
57 | |
58 | if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) |
59 | return 0; |
60 | |
61 | return switchdev_port_obj_del(dev: br->dev, obj: &mrp_obj.obj); |
62 | } |
63 | |
64 | enum br_mrp_hw_support |
65 | br_mrp_switchdev_set_ring_role(struct net_bridge *br, struct br_mrp *mrp, |
66 | enum br_mrp_ring_role_type role) |
67 | { |
68 | struct switchdev_obj_ring_role_mrp mrp_role = { |
69 | .obj.orig_dev = br->dev, |
70 | .obj.id = SWITCHDEV_OBJ_ID_RING_ROLE_MRP, |
71 | .ring_role = role, |
72 | .ring_id = mrp->ring_id, |
73 | .sw_backup = false, |
74 | }; |
75 | enum br_mrp_hw_support support; |
76 | int err; |
77 | |
78 | if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) |
79 | return BR_MRP_SW; |
80 | |
81 | support = br_mrp_switchdev_port_obj(br, obj: &mrp_role.obj, |
82 | add: role != BR_MRP_RING_ROLE_DISABLED); |
83 | if (support != BR_MRP_SW) |
84 | return support; |
85 | |
86 | /* If the driver can't configure to run completely the protocol in HW, |
87 | * then try again to configure the HW so the SW can run the protocol. |
88 | */ |
89 | mrp_role.sw_backup = true; |
90 | if (role != BR_MRP_RING_ROLE_DISABLED) |
91 | err = switchdev_port_obj_add(dev: br->dev, obj: &mrp_role.obj, NULL); |
92 | else |
93 | err = switchdev_port_obj_del(dev: br->dev, obj: &mrp_role.obj); |
94 | |
95 | if (!err) |
96 | return BR_MRP_SW; |
97 | |
98 | return BR_MRP_NONE; |
99 | } |
100 | |
101 | enum br_mrp_hw_support |
102 | br_mrp_switchdev_send_ring_test(struct net_bridge *br, struct br_mrp *mrp, |
103 | u32 interval, u8 max_miss, u32 period, |
104 | bool monitor) |
105 | { |
106 | struct switchdev_obj_ring_test_mrp test = { |
107 | .obj.orig_dev = br->dev, |
108 | .obj.id = SWITCHDEV_OBJ_ID_RING_TEST_MRP, |
109 | .interval = interval, |
110 | .max_miss = max_miss, |
111 | .ring_id = mrp->ring_id, |
112 | .period = period, |
113 | .monitor = monitor, |
114 | }; |
115 | |
116 | if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) |
117 | return BR_MRP_SW; |
118 | |
119 | return br_mrp_switchdev_port_obj(br, obj: &test.obj, add: interval != 0); |
120 | } |
121 | |
122 | int br_mrp_switchdev_set_ring_state(struct net_bridge *br, |
123 | struct br_mrp *mrp, |
124 | enum br_mrp_ring_state_type state) |
125 | { |
126 | struct switchdev_obj_ring_state_mrp mrp_state = { |
127 | .obj.orig_dev = br->dev, |
128 | .obj.id = SWITCHDEV_OBJ_ID_RING_STATE_MRP, |
129 | .ring_state = state, |
130 | .ring_id = mrp->ring_id, |
131 | }; |
132 | |
133 | if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) |
134 | return 0; |
135 | |
136 | return switchdev_port_obj_add(dev: br->dev, obj: &mrp_state.obj, NULL); |
137 | } |
138 | |
139 | enum br_mrp_hw_support |
140 | br_mrp_switchdev_set_in_role(struct net_bridge *br, struct br_mrp *mrp, |
141 | u16 in_id, u32 ring_id, |
142 | enum br_mrp_in_role_type role) |
143 | { |
144 | struct switchdev_obj_in_role_mrp mrp_role = { |
145 | .obj.orig_dev = br->dev, |
146 | .obj.id = SWITCHDEV_OBJ_ID_IN_ROLE_MRP, |
147 | .in_role = role, |
148 | .in_id = mrp->in_id, |
149 | .ring_id = mrp->ring_id, |
150 | .i_port = rtnl_dereference(mrp->i_port)->dev, |
151 | .sw_backup = false, |
152 | }; |
153 | enum br_mrp_hw_support support; |
154 | int err; |
155 | |
156 | if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) |
157 | return BR_MRP_SW; |
158 | |
159 | support = br_mrp_switchdev_port_obj(br, obj: &mrp_role.obj, |
160 | add: role != BR_MRP_IN_ROLE_DISABLED); |
161 | if (support != BR_MRP_NONE) |
162 | return support; |
163 | |
164 | /* If the driver can't configure to run completely the protocol in HW, |
165 | * then try again to configure the HW so the SW can run the protocol. |
166 | */ |
167 | mrp_role.sw_backup = true; |
168 | if (role != BR_MRP_IN_ROLE_DISABLED) |
169 | err = switchdev_port_obj_add(dev: br->dev, obj: &mrp_role.obj, NULL); |
170 | else |
171 | err = switchdev_port_obj_del(dev: br->dev, obj: &mrp_role.obj); |
172 | |
173 | if (!err) |
174 | return BR_MRP_SW; |
175 | |
176 | return BR_MRP_NONE; |
177 | } |
178 | |
179 | int br_mrp_switchdev_set_in_state(struct net_bridge *br, struct br_mrp *mrp, |
180 | enum br_mrp_in_state_type state) |
181 | { |
182 | struct switchdev_obj_in_state_mrp mrp_state = { |
183 | .obj.orig_dev = br->dev, |
184 | .obj.id = SWITCHDEV_OBJ_ID_IN_STATE_MRP, |
185 | .in_state = state, |
186 | .in_id = mrp->in_id, |
187 | }; |
188 | |
189 | if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) |
190 | return 0; |
191 | |
192 | return switchdev_port_obj_add(dev: br->dev, obj: &mrp_state.obj, NULL); |
193 | } |
194 | |
195 | enum br_mrp_hw_support |
196 | br_mrp_switchdev_send_in_test(struct net_bridge *br, struct br_mrp *mrp, |
197 | u32 interval, u8 max_miss, u32 period) |
198 | { |
199 | struct switchdev_obj_in_test_mrp test = { |
200 | .obj.orig_dev = br->dev, |
201 | .obj.id = SWITCHDEV_OBJ_ID_IN_TEST_MRP, |
202 | .interval = interval, |
203 | .max_miss = max_miss, |
204 | .in_id = mrp->in_id, |
205 | .period = period, |
206 | }; |
207 | |
208 | if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) |
209 | return BR_MRP_SW; |
210 | |
211 | return br_mrp_switchdev_port_obj(br, obj: &test.obj, add: interval != 0); |
212 | } |
213 | |
214 | int br_mrp_port_switchdev_set_state(struct net_bridge_port *p, u32 state) |
215 | { |
216 | struct switchdev_attr attr = { |
217 | .orig_dev = p->dev, |
218 | .id = SWITCHDEV_ATTR_ID_PORT_STP_STATE, |
219 | .u.stp_state = state, |
220 | }; |
221 | |
222 | if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) |
223 | return 0; |
224 | |
225 | return switchdev_port_attr_set(dev: p->dev, attr: &attr, NULL); |
226 | } |
227 | |
228 | int br_mrp_port_switchdev_set_role(struct net_bridge_port *p, |
229 | enum br_mrp_port_role_type role) |
230 | { |
231 | struct switchdev_attr attr = { |
232 | .orig_dev = p->dev, |
233 | .id = SWITCHDEV_ATTR_ID_MRP_PORT_ROLE, |
234 | .u.mrp_port_role = role, |
235 | }; |
236 | |
237 | if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) |
238 | return 0; |
239 | |
240 | return switchdev_port_attr_set(dev: p->dev, attr: &attr, NULL); |
241 | } |
242 | |