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 * 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 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 * 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 * 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 * 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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