1*9edbe6f3SJiri Pirko // SPDX-License-Identifier: GPL-2.0-or-later
2*9edbe6f3SJiri Pirko /*
3*9edbe6f3SJiri Pirko * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4*9edbe6f3SJiri Pirko * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5*9edbe6f3SJiri Pirko */
6*9edbe6f3SJiri Pirko
7*9edbe6f3SJiri Pirko #include "devl_internal.h"
8*9edbe6f3SJiri Pirko
9*9edbe6f3SJiri Pirko static struct devlink_linecard *
devlink_linecard_get_by_index(struct devlink * devlink,unsigned int linecard_index)10*9edbe6f3SJiri Pirko devlink_linecard_get_by_index(struct devlink *devlink,
11*9edbe6f3SJiri Pirko unsigned int linecard_index)
12*9edbe6f3SJiri Pirko {
13*9edbe6f3SJiri Pirko struct devlink_linecard *devlink_linecard;
14*9edbe6f3SJiri Pirko
15*9edbe6f3SJiri Pirko list_for_each_entry(devlink_linecard, &devlink->linecard_list, list) {
16*9edbe6f3SJiri Pirko if (devlink_linecard->index == linecard_index)
17*9edbe6f3SJiri Pirko return devlink_linecard;
18*9edbe6f3SJiri Pirko }
19*9edbe6f3SJiri Pirko return NULL;
20*9edbe6f3SJiri Pirko }
21*9edbe6f3SJiri Pirko
devlink_linecard_index_exists(struct devlink * devlink,unsigned int linecard_index)22*9edbe6f3SJiri Pirko static bool devlink_linecard_index_exists(struct devlink *devlink,
23*9edbe6f3SJiri Pirko unsigned int linecard_index)
24*9edbe6f3SJiri Pirko {
25*9edbe6f3SJiri Pirko return devlink_linecard_get_by_index(devlink, linecard_index);
26*9edbe6f3SJiri Pirko }
27*9edbe6f3SJiri Pirko
28*9edbe6f3SJiri Pirko static struct devlink_linecard *
devlink_linecard_get_from_attrs(struct devlink * devlink,struct nlattr ** attrs)29*9edbe6f3SJiri Pirko devlink_linecard_get_from_attrs(struct devlink *devlink, struct nlattr **attrs)
30*9edbe6f3SJiri Pirko {
31*9edbe6f3SJiri Pirko if (attrs[DEVLINK_ATTR_LINECARD_INDEX]) {
32*9edbe6f3SJiri Pirko u32 linecard_index = nla_get_u32(attrs[DEVLINK_ATTR_LINECARD_INDEX]);
33*9edbe6f3SJiri Pirko struct devlink_linecard *linecard;
34*9edbe6f3SJiri Pirko
35*9edbe6f3SJiri Pirko linecard = devlink_linecard_get_by_index(devlink, linecard_index);
36*9edbe6f3SJiri Pirko if (!linecard)
37*9edbe6f3SJiri Pirko return ERR_PTR(-ENODEV);
38*9edbe6f3SJiri Pirko return linecard;
39*9edbe6f3SJiri Pirko }
40*9edbe6f3SJiri Pirko return ERR_PTR(-EINVAL);
41*9edbe6f3SJiri Pirko }
42*9edbe6f3SJiri Pirko
43*9edbe6f3SJiri Pirko static struct devlink_linecard *
devlink_linecard_get_from_info(struct devlink * devlink,struct genl_info * info)44*9edbe6f3SJiri Pirko devlink_linecard_get_from_info(struct devlink *devlink, struct genl_info *info)
45*9edbe6f3SJiri Pirko {
46*9edbe6f3SJiri Pirko return devlink_linecard_get_from_attrs(devlink, info->attrs);
47*9edbe6f3SJiri Pirko }
48*9edbe6f3SJiri Pirko
devlink_nl_put_nested_handle(struct sk_buff * msg,struct devlink * devlink)49*9edbe6f3SJiri Pirko static int devlink_nl_put_nested_handle(struct sk_buff *msg, struct devlink *devlink)
50*9edbe6f3SJiri Pirko {
51*9edbe6f3SJiri Pirko struct nlattr *nested_attr;
52*9edbe6f3SJiri Pirko
53*9edbe6f3SJiri Pirko nested_attr = nla_nest_start(msg, DEVLINK_ATTR_NESTED_DEVLINK);
54*9edbe6f3SJiri Pirko if (!nested_attr)
55*9edbe6f3SJiri Pirko return -EMSGSIZE;
56*9edbe6f3SJiri Pirko if (devlink_nl_put_handle(msg, devlink))
57*9edbe6f3SJiri Pirko goto nla_put_failure;
58*9edbe6f3SJiri Pirko
59*9edbe6f3SJiri Pirko nla_nest_end(msg, nested_attr);
60*9edbe6f3SJiri Pirko return 0;
61*9edbe6f3SJiri Pirko
62*9edbe6f3SJiri Pirko nla_put_failure:
63*9edbe6f3SJiri Pirko nla_nest_cancel(msg, nested_attr);
64*9edbe6f3SJiri Pirko return -EMSGSIZE;
65*9edbe6f3SJiri Pirko }
66*9edbe6f3SJiri Pirko
67*9edbe6f3SJiri Pirko struct devlink_linecard_type {
68*9edbe6f3SJiri Pirko const char *type;
69*9edbe6f3SJiri Pirko const void *priv;
70*9edbe6f3SJiri Pirko };
71*9edbe6f3SJiri Pirko
devlink_nl_linecard_fill(struct sk_buff * msg,struct devlink * devlink,struct devlink_linecard * linecard,enum devlink_command cmd,u32 portid,u32 seq,int flags,struct netlink_ext_ack * extack)72*9edbe6f3SJiri Pirko static int devlink_nl_linecard_fill(struct sk_buff *msg,
73*9edbe6f3SJiri Pirko struct devlink *devlink,
74*9edbe6f3SJiri Pirko struct devlink_linecard *linecard,
75*9edbe6f3SJiri Pirko enum devlink_command cmd, u32 portid,
76*9edbe6f3SJiri Pirko u32 seq, int flags,
77*9edbe6f3SJiri Pirko struct netlink_ext_ack *extack)
78*9edbe6f3SJiri Pirko {
79*9edbe6f3SJiri Pirko struct devlink_linecard_type *linecard_type;
80*9edbe6f3SJiri Pirko struct nlattr *attr;
81*9edbe6f3SJiri Pirko void *hdr;
82*9edbe6f3SJiri Pirko int i;
83*9edbe6f3SJiri Pirko
84*9edbe6f3SJiri Pirko hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
85*9edbe6f3SJiri Pirko if (!hdr)
86*9edbe6f3SJiri Pirko return -EMSGSIZE;
87*9edbe6f3SJiri Pirko
88*9edbe6f3SJiri Pirko if (devlink_nl_put_handle(msg, devlink))
89*9edbe6f3SJiri Pirko goto nla_put_failure;
90*9edbe6f3SJiri Pirko if (nla_put_u32(msg, DEVLINK_ATTR_LINECARD_INDEX, linecard->index))
91*9edbe6f3SJiri Pirko goto nla_put_failure;
92*9edbe6f3SJiri Pirko if (nla_put_u8(msg, DEVLINK_ATTR_LINECARD_STATE, linecard->state))
93*9edbe6f3SJiri Pirko goto nla_put_failure;
94*9edbe6f3SJiri Pirko if (linecard->type &&
95*9edbe6f3SJiri Pirko nla_put_string(msg, DEVLINK_ATTR_LINECARD_TYPE, linecard->type))
96*9edbe6f3SJiri Pirko goto nla_put_failure;
97*9edbe6f3SJiri Pirko
98*9edbe6f3SJiri Pirko if (linecard->types_count) {
99*9edbe6f3SJiri Pirko attr = nla_nest_start(msg,
100*9edbe6f3SJiri Pirko DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES);
101*9edbe6f3SJiri Pirko if (!attr)
102*9edbe6f3SJiri Pirko goto nla_put_failure;
103*9edbe6f3SJiri Pirko for (i = 0; i < linecard->types_count; i++) {
104*9edbe6f3SJiri Pirko linecard_type = &linecard->types[i];
105*9edbe6f3SJiri Pirko if (nla_put_string(msg, DEVLINK_ATTR_LINECARD_TYPE,
106*9edbe6f3SJiri Pirko linecard_type->type)) {
107*9edbe6f3SJiri Pirko nla_nest_cancel(msg, attr);
108*9edbe6f3SJiri Pirko goto nla_put_failure;
109*9edbe6f3SJiri Pirko }
110*9edbe6f3SJiri Pirko }
111*9edbe6f3SJiri Pirko nla_nest_end(msg, attr);
112*9edbe6f3SJiri Pirko }
113*9edbe6f3SJiri Pirko
114*9edbe6f3SJiri Pirko if (linecard->nested_devlink &&
115*9edbe6f3SJiri Pirko devlink_nl_put_nested_handle(msg, linecard->nested_devlink))
116*9edbe6f3SJiri Pirko goto nla_put_failure;
117*9edbe6f3SJiri Pirko
118*9edbe6f3SJiri Pirko genlmsg_end(msg, hdr);
119*9edbe6f3SJiri Pirko return 0;
120*9edbe6f3SJiri Pirko
121*9edbe6f3SJiri Pirko nla_put_failure:
122*9edbe6f3SJiri Pirko genlmsg_cancel(msg, hdr);
123*9edbe6f3SJiri Pirko return -EMSGSIZE;
124*9edbe6f3SJiri Pirko }
125*9edbe6f3SJiri Pirko
devlink_linecard_notify(struct devlink_linecard * linecard,enum devlink_command cmd)126*9edbe6f3SJiri Pirko static void devlink_linecard_notify(struct devlink_linecard *linecard,
127*9edbe6f3SJiri Pirko enum devlink_command cmd)
128*9edbe6f3SJiri Pirko {
129*9edbe6f3SJiri Pirko struct devlink *devlink = linecard->devlink;
130*9edbe6f3SJiri Pirko struct sk_buff *msg;
131*9edbe6f3SJiri Pirko int err;
132*9edbe6f3SJiri Pirko
133*9edbe6f3SJiri Pirko WARN_ON(cmd != DEVLINK_CMD_LINECARD_NEW &&
134*9edbe6f3SJiri Pirko cmd != DEVLINK_CMD_LINECARD_DEL);
135*9edbe6f3SJiri Pirko
136*9edbe6f3SJiri Pirko if (!xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED))
137*9edbe6f3SJiri Pirko return;
138*9edbe6f3SJiri Pirko
139*9edbe6f3SJiri Pirko msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
140*9edbe6f3SJiri Pirko if (!msg)
141*9edbe6f3SJiri Pirko return;
142*9edbe6f3SJiri Pirko
143*9edbe6f3SJiri Pirko err = devlink_nl_linecard_fill(msg, devlink, linecard, cmd, 0, 0, 0,
144*9edbe6f3SJiri Pirko NULL);
145*9edbe6f3SJiri Pirko if (err) {
146*9edbe6f3SJiri Pirko nlmsg_free(msg);
147*9edbe6f3SJiri Pirko return;
148*9edbe6f3SJiri Pirko }
149*9edbe6f3SJiri Pirko
150*9edbe6f3SJiri Pirko genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
151*9edbe6f3SJiri Pirko msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
152*9edbe6f3SJiri Pirko }
153*9edbe6f3SJiri Pirko
devlink_linecards_notify_register(struct devlink * devlink)154*9edbe6f3SJiri Pirko void devlink_linecards_notify_register(struct devlink *devlink)
155*9edbe6f3SJiri Pirko {
156*9edbe6f3SJiri Pirko struct devlink_linecard *linecard;
157*9edbe6f3SJiri Pirko
158*9edbe6f3SJiri Pirko list_for_each_entry(linecard, &devlink->linecard_list, list)
159*9edbe6f3SJiri Pirko devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
160*9edbe6f3SJiri Pirko }
161*9edbe6f3SJiri Pirko
devlink_linecards_notify_unregister(struct devlink * devlink)162*9edbe6f3SJiri Pirko void devlink_linecards_notify_unregister(struct devlink *devlink)
163*9edbe6f3SJiri Pirko {
164*9edbe6f3SJiri Pirko struct devlink_linecard *linecard;
165*9edbe6f3SJiri Pirko
166*9edbe6f3SJiri Pirko list_for_each_entry_reverse(linecard, &devlink->linecard_list, list)
167*9edbe6f3SJiri Pirko devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL);
168*9edbe6f3SJiri Pirko }
169*9edbe6f3SJiri Pirko
devlink_nl_linecard_get_doit(struct sk_buff * skb,struct genl_info * info)170*9edbe6f3SJiri Pirko int devlink_nl_linecard_get_doit(struct sk_buff *skb, struct genl_info *info)
171*9edbe6f3SJiri Pirko {
172*9edbe6f3SJiri Pirko struct devlink *devlink = info->user_ptr[0];
173*9edbe6f3SJiri Pirko struct devlink_linecard *linecard;
174*9edbe6f3SJiri Pirko struct sk_buff *msg;
175*9edbe6f3SJiri Pirko int err;
176*9edbe6f3SJiri Pirko
177*9edbe6f3SJiri Pirko linecard = devlink_linecard_get_from_info(devlink, info);
178*9edbe6f3SJiri Pirko if (IS_ERR(linecard))
179*9edbe6f3SJiri Pirko return PTR_ERR(linecard);
180*9edbe6f3SJiri Pirko
181*9edbe6f3SJiri Pirko msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
182*9edbe6f3SJiri Pirko if (!msg)
183*9edbe6f3SJiri Pirko return -ENOMEM;
184*9edbe6f3SJiri Pirko
185*9edbe6f3SJiri Pirko mutex_lock(&linecard->state_lock);
186*9edbe6f3SJiri Pirko err = devlink_nl_linecard_fill(msg, devlink, linecard,
187*9edbe6f3SJiri Pirko DEVLINK_CMD_LINECARD_NEW,
188*9edbe6f3SJiri Pirko info->snd_portid, info->snd_seq, 0,
189*9edbe6f3SJiri Pirko info->extack);
190*9edbe6f3SJiri Pirko mutex_unlock(&linecard->state_lock);
191*9edbe6f3SJiri Pirko if (err) {
192*9edbe6f3SJiri Pirko nlmsg_free(msg);
193*9edbe6f3SJiri Pirko return err;
194*9edbe6f3SJiri Pirko }
195*9edbe6f3SJiri Pirko
196*9edbe6f3SJiri Pirko return genlmsg_reply(msg, info);
197*9edbe6f3SJiri Pirko }
198*9edbe6f3SJiri Pirko
devlink_nl_linecard_get_dump_one(struct sk_buff * msg,struct devlink * devlink,struct netlink_callback * cb,int flags)199*9edbe6f3SJiri Pirko static int devlink_nl_linecard_get_dump_one(struct sk_buff *msg,
200*9edbe6f3SJiri Pirko struct devlink *devlink,
201*9edbe6f3SJiri Pirko struct netlink_callback *cb,
202*9edbe6f3SJiri Pirko int flags)
203*9edbe6f3SJiri Pirko {
204*9edbe6f3SJiri Pirko struct devlink_nl_dump_state *state = devlink_dump_state(cb);
205*9edbe6f3SJiri Pirko struct devlink_linecard *linecard;
206*9edbe6f3SJiri Pirko int idx = 0;
207*9edbe6f3SJiri Pirko int err = 0;
208*9edbe6f3SJiri Pirko
209*9edbe6f3SJiri Pirko list_for_each_entry(linecard, &devlink->linecard_list, list) {
210*9edbe6f3SJiri Pirko if (idx < state->idx) {
211*9edbe6f3SJiri Pirko idx++;
212*9edbe6f3SJiri Pirko continue;
213*9edbe6f3SJiri Pirko }
214*9edbe6f3SJiri Pirko mutex_lock(&linecard->state_lock);
215*9edbe6f3SJiri Pirko err = devlink_nl_linecard_fill(msg, devlink, linecard,
216*9edbe6f3SJiri Pirko DEVLINK_CMD_LINECARD_NEW,
217*9edbe6f3SJiri Pirko NETLINK_CB(cb->skb).portid,
218*9edbe6f3SJiri Pirko cb->nlh->nlmsg_seq, flags,
219*9edbe6f3SJiri Pirko cb->extack);
220*9edbe6f3SJiri Pirko mutex_unlock(&linecard->state_lock);
221*9edbe6f3SJiri Pirko if (err) {
222*9edbe6f3SJiri Pirko state->idx = idx;
223*9edbe6f3SJiri Pirko break;
224*9edbe6f3SJiri Pirko }
225*9edbe6f3SJiri Pirko idx++;
226*9edbe6f3SJiri Pirko }
227*9edbe6f3SJiri Pirko
228*9edbe6f3SJiri Pirko return err;
229*9edbe6f3SJiri Pirko }
230*9edbe6f3SJiri Pirko
devlink_nl_linecard_get_dumpit(struct sk_buff * skb,struct netlink_callback * cb)231*9edbe6f3SJiri Pirko int devlink_nl_linecard_get_dumpit(struct sk_buff *skb,
232*9edbe6f3SJiri Pirko struct netlink_callback *cb)
233*9edbe6f3SJiri Pirko {
234*9edbe6f3SJiri Pirko return devlink_nl_dumpit(skb, cb, devlink_nl_linecard_get_dump_one);
235*9edbe6f3SJiri Pirko }
236*9edbe6f3SJiri Pirko
237*9edbe6f3SJiri Pirko static struct devlink_linecard_type *
devlink_linecard_type_lookup(struct devlink_linecard * linecard,const char * type)238*9edbe6f3SJiri Pirko devlink_linecard_type_lookup(struct devlink_linecard *linecard,
239*9edbe6f3SJiri Pirko const char *type)
240*9edbe6f3SJiri Pirko {
241*9edbe6f3SJiri Pirko struct devlink_linecard_type *linecard_type;
242*9edbe6f3SJiri Pirko int i;
243*9edbe6f3SJiri Pirko
244*9edbe6f3SJiri Pirko for (i = 0; i < linecard->types_count; i++) {
245*9edbe6f3SJiri Pirko linecard_type = &linecard->types[i];
246*9edbe6f3SJiri Pirko if (!strcmp(type, linecard_type->type))
247*9edbe6f3SJiri Pirko return linecard_type;
248*9edbe6f3SJiri Pirko }
249*9edbe6f3SJiri Pirko return NULL;
250*9edbe6f3SJiri Pirko }
251*9edbe6f3SJiri Pirko
devlink_linecard_type_set(struct devlink_linecard * linecard,const char * type,struct netlink_ext_ack * extack)252*9edbe6f3SJiri Pirko static int devlink_linecard_type_set(struct devlink_linecard *linecard,
253*9edbe6f3SJiri Pirko const char *type,
254*9edbe6f3SJiri Pirko struct netlink_ext_ack *extack)
255*9edbe6f3SJiri Pirko {
256*9edbe6f3SJiri Pirko const struct devlink_linecard_ops *ops = linecard->ops;
257*9edbe6f3SJiri Pirko struct devlink_linecard_type *linecard_type;
258*9edbe6f3SJiri Pirko int err;
259*9edbe6f3SJiri Pirko
260*9edbe6f3SJiri Pirko mutex_lock(&linecard->state_lock);
261*9edbe6f3SJiri Pirko if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) {
262*9edbe6f3SJiri Pirko NL_SET_ERR_MSG(extack, "Line card is currently being provisioned");
263*9edbe6f3SJiri Pirko err = -EBUSY;
264*9edbe6f3SJiri Pirko goto out;
265*9edbe6f3SJiri Pirko }
266*9edbe6f3SJiri Pirko if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) {
267*9edbe6f3SJiri Pirko NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned");
268*9edbe6f3SJiri Pirko err = -EBUSY;
269*9edbe6f3SJiri Pirko goto out;
270*9edbe6f3SJiri Pirko }
271*9edbe6f3SJiri Pirko
272*9edbe6f3SJiri Pirko linecard_type = devlink_linecard_type_lookup(linecard, type);
273*9edbe6f3SJiri Pirko if (!linecard_type) {
274*9edbe6f3SJiri Pirko NL_SET_ERR_MSG(extack, "Unsupported line card type provided");
275*9edbe6f3SJiri Pirko err = -EINVAL;
276*9edbe6f3SJiri Pirko goto out;
277*9edbe6f3SJiri Pirko }
278*9edbe6f3SJiri Pirko
279*9edbe6f3SJiri Pirko if (linecard->state != DEVLINK_LINECARD_STATE_UNPROVISIONED &&
280*9edbe6f3SJiri Pirko linecard->state != DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) {
281*9edbe6f3SJiri Pirko NL_SET_ERR_MSG(extack, "Line card already provisioned");
282*9edbe6f3SJiri Pirko err = -EBUSY;
283*9edbe6f3SJiri Pirko /* Check if the line card is provisioned in the same
284*9edbe6f3SJiri Pirko * way the user asks. In case it is, make the operation
285*9edbe6f3SJiri Pirko * to return success.
286*9edbe6f3SJiri Pirko */
287*9edbe6f3SJiri Pirko if (ops->same_provision &&
288*9edbe6f3SJiri Pirko ops->same_provision(linecard, linecard->priv,
289*9edbe6f3SJiri Pirko linecard_type->type,
290*9edbe6f3SJiri Pirko linecard_type->priv))
291*9edbe6f3SJiri Pirko err = 0;
292*9edbe6f3SJiri Pirko goto out;
293*9edbe6f3SJiri Pirko }
294*9edbe6f3SJiri Pirko
295*9edbe6f3SJiri Pirko linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING;
296*9edbe6f3SJiri Pirko linecard->type = linecard_type->type;
297*9edbe6f3SJiri Pirko devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
298*9edbe6f3SJiri Pirko mutex_unlock(&linecard->state_lock);
299*9edbe6f3SJiri Pirko err = ops->provision(linecard, linecard->priv, linecard_type->type,
300*9edbe6f3SJiri Pirko linecard_type->priv, extack);
301*9edbe6f3SJiri Pirko if (err) {
302*9edbe6f3SJiri Pirko /* Provisioning failed. Assume the linecard is unprovisioned
303*9edbe6f3SJiri Pirko * for future operations.
304*9edbe6f3SJiri Pirko */
305*9edbe6f3SJiri Pirko mutex_lock(&linecard->state_lock);
306*9edbe6f3SJiri Pirko linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
307*9edbe6f3SJiri Pirko linecard->type = NULL;
308*9edbe6f3SJiri Pirko devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
309*9edbe6f3SJiri Pirko mutex_unlock(&linecard->state_lock);
310*9edbe6f3SJiri Pirko }
311*9edbe6f3SJiri Pirko return err;
312*9edbe6f3SJiri Pirko
313*9edbe6f3SJiri Pirko out:
314*9edbe6f3SJiri Pirko mutex_unlock(&linecard->state_lock);
315*9edbe6f3SJiri Pirko return err;
316*9edbe6f3SJiri Pirko }
317*9edbe6f3SJiri Pirko
devlink_linecard_type_unset(struct devlink_linecard * linecard,struct netlink_ext_ack * extack)318*9edbe6f3SJiri Pirko static int devlink_linecard_type_unset(struct devlink_linecard *linecard,
319*9edbe6f3SJiri Pirko struct netlink_ext_ack *extack)
320*9edbe6f3SJiri Pirko {
321*9edbe6f3SJiri Pirko int err;
322*9edbe6f3SJiri Pirko
323*9edbe6f3SJiri Pirko mutex_lock(&linecard->state_lock);
324*9edbe6f3SJiri Pirko if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) {
325*9edbe6f3SJiri Pirko NL_SET_ERR_MSG(extack, "Line card is currently being provisioned");
326*9edbe6f3SJiri Pirko err = -EBUSY;
327*9edbe6f3SJiri Pirko goto out;
328*9edbe6f3SJiri Pirko }
329*9edbe6f3SJiri Pirko if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) {
330*9edbe6f3SJiri Pirko NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned");
331*9edbe6f3SJiri Pirko err = -EBUSY;
332*9edbe6f3SJiri Pirko goto out;
333*9edbe6f3SJiri Pirko }
334*9edbe6f3SJiri Pirko if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) {
335*9edbe6f3SJiri Pirko linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
336*9edbe6f3SJiri Pirko linecard->type = NULL;
337*9edbe6f3SJiri Pirko devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
338*9edbe6f3SJiri Pirko err = 0;
339*9edbe6f3SJiri Pirko goto out;
340*9edbe6f3SJiri Pirko }
341*9edbe6f3SJiri Pirko
342*9edbe6f3SJiri Pirko if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONED) {
343*9edbe6f3SJiri Pirko NL_SET_ERR_MSG(extack, "Line card is not provisioned");
344*9edbe6f3SJiri Pirko err = 0;
345*9edbe6f3SJiri Pirko goto out;
346*9edbe6f3SJiri Pirko }
347*9edbe6f3SJiri Pirko linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONING;
348*9edbe6f3SJiri Pirko devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
349*9edbe6f3SJiri Pirko mutex_unlock(&linecard->state_lock);
350*9edbe6f3SJiri Pirko err = linecard->ops->unprovision(linecard, linecard->priv,
351*9edbe6f3SJiri Pirko extack);
352*9edbe6f3SJiri Pirko if (err) {
353*9edbe6f3SJiri Pirko /* Unprovisioning failed. Assume the linecard is unprovisioned
354*9edbe6f3SJiri Pirko * for future operations.
355*9edbe6f3SJiri Pirko */
356*9edbe6f3SJiri Pirko mutex_lock(&linecard->state_lock);
357*9edbe6f3SJiri Pirko linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
358*9edbe6f3SJiri Pirko linecard->type = NULL;
359*9edbe6f3SJiri Pirko devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
360*9edbe6f3SJiri Pirko mutex_unlock(&linecard->state_lock);
361*9edbe6f3SJiri Pirko }
362*9edbe6f3SJiri Pirko return err;
363*9edbe6f3SJiri Pirko
364*9edbe6f3SJiri Pirko out:
365*9edbe6f3SJiri Pirko mutex_unlock(&linecard->state_lock);
366*9edbe6f3SJiri Pirko return err;
367*9edbe6f3SJiri Pirko }
368*9edbe6f3SJiri Pirko
devlink_nl_cmd_linecard_set_doit(struct sk_buff * skb,struct genl_info * info)369*9edbe6f3SJiri Pirko int devlink_nl_cmd_linecard_set_doit(struct sk_buff *skb,
370*9edbe6f3SJiri Pirko struct genl_info *info)
371*9edbe6f3SJiri Pirko {
372*9edbe6f3SJiri Pirko struct netlink_ext_ack *extack = info->extack;
373*9edbe6f3SJiri Pirko struct devlink *devlink = info->user_ptr[0];
374*9edbe6f3SJiri Pirko struct devlink_linecard *linecard;
375*9edbe6f3SJiri Pirko int err;
376*9edbe6f3SJiri Pirko
377*9edbe6f3SJiri Pirko linecard = devlink_linecard_get_from_info(devlink, info);
378*9edbe6f3SJiri Pirko if (IS_ERR(linecard))
379*9edbe6f3SJiri Pirko return PTR_ERR(linecard);
380*9edbe6f3SJiri Pirko
381*9edbe6f3SJiri Pirko if (info->attrs[DEVLINK_ATTR_LINECARD_TYPE]) {
382*9edbe6f3SJiri Pirko const char *type;
383*9edbe6f3SJiri Pirko
384*9edbe6f3SJiri Pirko type = nla_data(info->attrs[DEVLINK_ATTR_LINECARD_TYPE]);
385*9edbe6f3SJiri Pirko if (strcmp(type, "")) {
386*9edbe6f3SJiri Pirko err = devlink_linecard_type_set(linecard, type, extack);
387*9edbe6f3SJiri Pirko if (err)
388*9edbe6f3SJiri Pirko return err;
389*9edbe6f3SJiri Pirko } else {
390*9edbe6f3SJiri Pirko err = devlink_linecard_type_unset(linecard, extack);
391*9edbe6f3SJiri Pirko if (err)
392*9edbe6f3SJiri Pirko return err;
393*9edbe6f3SJiri Pirko }
394*9edbe6f3SJiri Pirko }
395*9edbe6f3SJiri Pirko
396*9edbe6f3SJiri Pirko return 0;
397*9edbe6f3SJiri Pirko }
398*9edbe6f3SJiri Pirko
devlink_linecard_types_init(struct devlink_linecard * linecard)399*9edbe6f3SJiri Pirko static int devlink_linecard_types_init(struct devlink_linecard *linecard)
400*9edbe6f3SJiri Pirko {
401*9edbe6f3SJiri Pirko struct devlink_linecard_type *linecard_type;
402*9edbe6f3SJiri Pirko unsigned int count;
403*9edbe6f3SJiri Pirko int i;
404*9edbe6f3SJiri Pirko
405*9edbe6f3SJiri Pirko count = linecard->ops->types_count(linecard, linecard->priv);
406*9edbe6f3SJiri Pirko linecard->types = kmalloc_array(count, sizeof(*linecard_type),
407*9edbe6f3SJiri Pirko GFP_KERNEL);
408*9edbe6f3SJiri Pirko if (!linecard->types)
409*9edbe6f3SJiri Pirko return -ENOMEM;
410*9edbe6f3SJiri Pirko linecard->types_count = count;
411*9edbe6f3SJiri Pirko
412*9edbe6f3SJiri Pirko for (i = 0; i < count; i++) {
413*9edbe6f3SJiri Pirko linecard_type = &linecard->types[i];
414*9edbe6f3SJiri Pirko linecard->ops->types_get(linecard, linecard->priv, i,
415*9edbe6f3SJiri Pirko &linecard_type->type,
416*9edbe6f3SJiri Pirko &linecard_type->priv);
417*9edbe6f3SJiri Pirko }
418*9edbe6f3SJiri Pirko return 0;
419*9edbe6f3SJiri Pirko }
420*9edbe6f3SJiri Pirko
devlink_linecard_types_fini(struct devlink_linecard * linecard)421*9edbe6f3SJiri Pirko static void devlink_linecard_types_fini(struct devlink_linecard *linecard)
422*9edbe6f3SJiri Pirko {
423*9edbe6f3SJiri Pirko kfree(linecard->types);
424*9edbe6f3SJiri Pirko }
425*9edbe6f3SJiri Pirko
426*9edbe6f3SJiri Pirko /**
427*9edbe6f3SJiri Pirko * devl_linecard_create - Create devlink linecard
428*9edbe6f3SJiri Pirko *
429*9edbe6f3SJiri Pirko * @devlink: devlink
430*9edbe6f3SJiri Pirko * @linecard_index: driver-specific numerical identifier of the linecard
431*9edbe6f3SJiri Pirko * @ops: linecards ops
432*9edbe6f3SJiri Pirko * @priv: user priv pointer
433*9edbe6f3SJiri Pirko *
434*9edbe6f3SJiri Pirko * Create devlink linecard instance with provided linecard index.
435*9edbe6f3SJiri Pirko * Caller can use any indexing, even hw-related one.
436*9edbe6f3SJiri Pirko *
437*9edbe6f3SJiri Pirko * Return: Line card structure or an ERR_PTR() encoded error code.
438*9edbe6f3SJiri Pirko */
439*9edbe6f3SJiri Pirko struct devlink_linecard *
devl_linecard_create(struct devlink * devlink,unsigned int linecard_index,const struct devlink_linecard_ops * ops,void * priv)440*9edbe6f3SJiri Pirko devl_linecard_create(struct devlink *devlink, unsigned int linecard_index,
441*9edbe6f3SJiri Pirko const struct devlink_linecard_ops *ops, void *priv)
442*9edbe6f3SJiri Pirko {
443*9edbe6f3SJiri Pirko struct devlink_linecard *linecard;
444*9edbe6f3SJiri Pirko int err;
445*9edbe6f3SJiri Pirko
446*9edbe6f3SJiri Pirko if (WARN_ON(!ops || !ops->provision || !ops->unprovision ||
447*9edbe6f3SJiri Pirko !ops->types_count || !ops->types_get))
448*9edbe6f3SJiri Pirko return ERR_PTR(-EINVAL);
449*9edbe6f3SJiri Pirko
450*9edbe6f3SJiri Pirko if (devlink_linecard_index_exists(devlink, linecard_index))
451*9edbe6f3SJiri Pirko return ERR_PTR(-EEXIST);
452*9edbe6f3SJiri Pirko
453*9edbe6f3SJiri Pirko linecard = kzalloc(sizeof(*linecard), GFP_KERNEL);
454*9edbe6f3SJiri Pirko if (!linecard)
455*9edbe6f3SJiri Pirko return ERR_PTR(-ENOMEM);
456*9edbe6f3SJiri Pirko
457*9edbe6f3SJiri Pirko linecard->devlink = devlink;
458*9edbe6f3SJiri Pirko linecard->index = linecard_index;
459*9edbe6f3SJiri Pirko linecard->ops = ops;
460*9edbe6f3SJiri Pirko linecard->priv = priv;
461*9edbe6f3SJiri Pirko linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
462*9edbe6f3SJiri Pirko mutex_init(&linecard->state_lock);
463*9edbe6f3SJiri Pirko
464*9edbe6f3SJiri Pirko err = devlink_linecard_types_init(linecard);
465*9edbe6f3SJiri Pirko if (err) {
466*9edbe6f3SJiri Pirko mutex_destroy(&linecard->state_lock);
467*9edbe6f3SJiri Pirko kfree(linecard);
468*9edbe6f3SJiri Pirko return ERR_PTR(err);
469*9edbe6f3SJiri Pirko }
470*9edbe6f3SJiri Pirko
471*9edbe6f3SJiri Pirko list_add_tail(&linecard->list, &devlink->linecard_list);
472*9edbe6f3SJiri Pirko devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
473*9edbe6f3SJiri Pirko return linecard;
474*9edbe6f3SJiri Pirko }
475*9edbe6f3SJiri Pirko EXPORT_SYMBOL_GPL(devl_linecard_create);
476*9edbe6f3SJiri Pirko
477*9edbe6f3SJiri Pirko /**
478*9edbe6f3SJiri Pirko * devl_linecard_destroy - Destroy devlink linecard
479*9edbe6f3SJiri Pirko *
480*9edbe6f3SJiri Pirko * @linecard: devlink linecard
481*9edbe6f3SJiri Pirko */
devl_linecard_destroy(struct devlink_linecard * linecard)482*9edbe6f3SJiri Pirko void devl_linecard_destroy(struct devlink_linecard *linecard)
483*9edbe6f3SJiri Pirko {
484*9edbe6f3SJiri Pirko devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL);
485*9edbe6f3SJiri Pirko list_del(&linecard->list);
486*9edbe6f3SJiri Pirko devlink_linecard_types_fini(linecard);
487*9edbe6f3SJiri Pirko mutex_destroy(&linecard->state_lock);
488*9edbe6f3SJiri Pirko kfree(linecard);
489*9edbe6f3SJiri Pirko }
490*9edbe6f3SJiri Pirko EXPORT_SYMBOL_GPL(devl_linecard_destroy);
491*9edbe6f3SJiri Pirko
492*9edbe6f3SJiri Pirko /**
493*9edbe6f3SJiri Pirko * devlink_linecard_provision_set - Set provisioning on linecard
494*9edbe6f3SJiri Pirko *
495*9edbe6f3SJiri Pirko * @linecard: devlink linecard
496*9edbe6f3SJiri Pirko * @type: linecard type
497*9edbe6f3SJiri Pirko *
498*9edbe6f3SJiri Pirko * This is either called directly from the provision() op call or
499*9edbe6f3SJiri Pirko * as a result of the provision() op call asynchronously.
500*9edbe6f3SJiri Pirko */
devlink_linecard_provision_set(struct devlink_linecard * linecard,const char * type)501*9edbe6f3SJiri Pirko void devlink_linecard_provision_set(struct devlink_linecard *linecard,
502*9edbe6f3SJiri Pirko const char *type)
503*9edbe6f3SJiri Pirko {
504*9edbe6f3SJiri Pirko mutex_lock(&linecard->state_lock);
505*9edbe6f3SJiri Pirko WARN_ON(linecard->type && strcmp(linecard->type, type));
506*9edbe6f3SJiri Pirko linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED;
507*9edbe6f3SJiri Pirko linecard->type = type;
508*9edbe6f3SJiri Pirko devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
509*9edbe6f3SJiri Pirko mutex_unlock(&linecard->state_lock);
510*9edbe6f3SJiri Pirko }
511*9edbe6f3SJiri Pirko EXPORT_SYMBOL_GPL(devlink_linecard_provision_set);
512*9edbe6f3SJiri Pirko
513*9edbe6f3SJiri Pirko /**
514*9edbe6f3SJiri Pirko * devlink_linecard_provision_clear - Clear provisioning on linecard
515*9edbe6f3SJiri Pirko *
516*9edbe6f3SJiri Pirko * @linecard: devlink linecard
517*9edbe6f3SJiri Pirko *
518*9edbe6f3SJiri Pirko * This is either called directly from the unprovision() op call or
519*9edbe6f3SJiri Pirko * as a result of the unprovision() op call asynchronously.
520*9edbe6f3SJiri Pirko */
devlink_linecard_provision_clear(struct devlink_linecard * linecard)521*9edbe6f3SJiri Pirko void devlink_linecard_provision_clear(struct devlink_linecard *linecard)
522*9edbe6f3SJiri Pirko {
523*9edbe6f3SJiri Pirko mutex_lock(&linecard->state_lock);
524*9edbe6f3SJiri Pirko WARN_ON(linecard->nested_devlink);
525*9edbe6f3SJiri Pirko linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
526*9edbe6f3SJiri Pirko linecard->type = NULL;
527*9edbe6f3SJiri Pirko devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
528*9edbe6f3SJiri Pirko mutex_unlock(&linecard->state_lock);
529*9edbe6f3SJiri Pirko }
530*9edbe6f3SJiri Pirko EXPORT_SYMBOL_GPL(devlink_linecard_provision_clear);
531*9edbe6f3SJiri Pirko
532*9edbe6f3SJiri Pirko /**
533*9edbe6f3SJiri Pirko * devlink_linecard_provision_fail - Fail provisioning on linecard
534*9edbe6f3SJiri Pirko *
535*9edbe6f3SJiri Pirko * @linecard: devlink linecard
536*9edbe6f3SJiri Pirko *
537*9edbe6f3SJiri Pirko * This is either called directly from the provision() op call or
538*9edbe6f3SJiri Pirko * as a result of the provision() op call asynchronously.
539*9edbe6f3SJiri Pirko */
devlink_linecard_provision_fail(struct devlink_linecard * linecard)540*9edbe6f3SJiri Pirko void devlink_linecard_provision_fail(struct devlink_linecard *linecard)
541*9edbe6f3SJiri Pirko {
542*9edbe6f3SJiri Pirko mutex_lock(&linecard->state_lock);
543*9edbe6f3SJiri Pirko WARN_ON(linecard->nested_devlink);
544*9edbe6f3SJiri Pirko linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING_FAILED;
545*9edbe6f3SJiri Pirko devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
546*9edbe6f3SJiri Pirko mutex_unlock(&linecard->state_lock);
547*9edbe6f3SJiri Pirko }
548*9edbe6f3SJiri Pirko EXPORT_SYMBOL_GPL(devlink_linecard_provision_fail);
549*9edbe6f3SJiri Pirko
550*9edbe6f3SJiri Pirko /**
551*9edbe6f3SJiri Pirko * devlink_linecard_activate - Set linecard active
552*9edbe6f3SJiri Pirko *
553*9edbe6f3SJiri Pirko * @linecard: devlink linecard
554*9edbe6f3SJiri Pirko */
devlink_linecard_activate(struct devlink_linecard * linecard)555*9edbe6f3SJiri Pirko void devlink_linecard_activate(struct devlink_linecard *linecard)
556*9edbe6f3SJiri Pirko {
557*9edbe6f3SJiri Pirko mutex_lock(&linecard->state_lock);
558*9edbe6f3SJiri Pirko WARN_ON(linecard->state != DEVLINK_LINECARD_STATE_PROVISIONED);
559*9edbe6f3SJiri Pirko linecard->state = DEVLINK_LINECARD_STATE_ACTIVE;
560*9edbe6f3SJiri Pirko devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
561*9edbe6f3SJiri Pirko mutex_unlock(&linecard->state_lock);
562*9edbe6f3SJiri Pirko }
563*9edbe6f3SJiri Pirko EXPORT_SYMBOL_GPL(devlink_linecard_activate);
564*9edbe6f3SJiri Pirko
565*9edbe6f3SJiri Pirko /**
566*9edbe6f3SJiri Pirko * devlink_linecard_deactivate - Set linecard inactive
567*9edbe6f3SJiri Pirko *
568*9edbe6f3SJiri Pirko * @linecard: devlink linecard
569*9edbe6f3SJiri Pirko */
devlink_linecard_deactivate(struct devlink_linecard * linecard)570*9edbe6f3SJiri Pirko void devlink_linecard_deactivate(struct devlink_linecard *linecard)
571*9edbe6f3SJiri Pirko {
572*9edbe6f3SJiri Pirko mutex_lock(&linecard->state_lock);
573*9edbe6f3SJiri Pirko switch (linecard->state) {
574*9edbe6f3SJiri Pirko case DEVLINK_LINECARD_STATE_ACTIVE:
575*9edbe6f3SJiri Pirko linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED;
576*9edbe6f3SJiri Pirko devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
577*9edbe6f3SJiri Pirko break;
578*9edbe6f3SJiri Pirko case DEVLINK_LINECARD_STATE_UNPROVISIONING:
579*9edbe6f3SJiri Pirko /* Line card is being deactivated as part
580*9edbe6f3SJiri Pirko * of unprovisioning flow.
581*9edbe6f3SJiri Pirko */
582*9edbe6f3SJiri Pirko break;
583*9edbe6f3SJiri Pirko default:
584*9edbe6f3SJiri Pirko WARN_ON(1);
585*9edbe6f3SJiri Pirko break;
586*9edbe6f3SJiri Pirko }
587*9edbe6f3SJiri Pirko mutex_unlock(&linecard->state_lock);
588*9edbe6f3SJiri Pirko }
589*9edbe6f3SJiri Pirko EXPORT_SYMBOL_GPL(devlink_linecard_deactivate);
590*9edbe6f3SJiri Pirko
591*9edbe6f3SJiri Pirko /**
592*9edbe6f3SJiri Pirko * devlink_linecard_nested_dl_set - Attach/detach nested devlink
593*9edbe6f3SJiri Pirko * instance to linecard.
594*9edbe6f3SJiri Pirko *
595*9edbe6f3SJiri Pirko * @linecard: devlink linecard
596*9edbe6f3SJiri Pirko * @nested_devlink: devlink instance to attach or NULL to detach
597*9edbe6f3SJiri Pirko */
devlink_linecard_nested_dl_set(struct devlink_linecard * linecard,struct devlink * nested_devlink)598*9edbe6f3SJiri Pirko void devlink_linecard_nested_dl_set(struct devlink_linecard *linecard,
599*9edbe6f3SJiri Pirko struct devlink *nested_devlink)
600*9edbe6f3SJiri Pirko {
601*9edbe6f3SJiri Pirko mutex_lock(&linecard->state_lock);
602*9edbe6f3SJiri Pirko linecard->nested_devlink = nested_devlink;
603*9edbe6f3SJiri Pirko devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
604*9edbe6f3SJiri Pirko mutex_unlock(&linecard->state_lock);
605*9edbe6f3SJiri Pirko }
606*9edbe6f3SJiri Pirko EXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set);
607