udp: Implement udp_bpf_recvmsg() for sockmap
[linux-2.6-microblaze.git] / net / core / devlink.c
index 738d434..737b61c 100644 (file)
@@ -87,6 +87,9 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_trap_report);
 
 static const struct nla_policy devlink_function_nl_policy[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1] = {
        [DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR] = { .type = NLA_BINARY },
+       [DEVLINK_PORT_FN_ATTR_STATE] =
+               NLA_POLICY_RANGE(NLA_U8, DEVLINK_PORT_FN_STATE_INACTIVE,
+                                DEVLINK_PORT_FN_STATE_ACTIVE),
 };
 
 static LIST_HEAD(devlink_list);
@@ -690,6 +693,15 @@ static int devlink_nl_port_attrs_put(struct sk_buff *msg,
                if (nla_put_u8(msg, DEVLINK_ATTR_PORT_EXTERNAL, attrs->pci_vf.external))
                        return -EMSGSIZE;
                break;
+       case DEVLINK_PORT_FLAVOUR_PCI_SF:
+               if (nla_put_u32(msg, DEVLINK_ATTR_PORT_CONTROLLER_NUMBER,
+                               attrs->pci_sf.controller) ||
+                   nla_put_u16(msg, DEVLINK_ATTR_PORT_PCI_PF_NUMBER,
+                               attrs->pci_sf.pf) ||
+                   nla_put_u32(msg, DEVLINK_ATTR_PORT_PCI_SF_NUMBER,
+                               attrs->pci_sf.sf))
+                       return -EMSGSIZE;
+               break;
        case DEVLINK_PORT_FLAVOUR_PHYSICAL:
        case DEVLINK_PORT_FLAVOUR_CPU:
        case DEVLINK_PORT_FLAVOUR_DSA:
@@ -712,6 +724,83 @@ static int devlink_nl_port_attrs_put(struct sk_buff *msg,
        return 0;
 }
 
+static int
+devlink_port_fn_hw_addr_fill(struct devlink *devlink, const struct devlink_ops *ops,
+                            struct devlink_port *port, struct sk_buff *msg,
+                            struct netlink_ext_ack *extack, bool *msg_updated)
+{
+       u8 hw_addr[MAX_ADDR_LEN];
+       int hw_addr_len;
+       int err;
+
+       if (!ops->port_function_hw_addr_get)
+               return 0;
+
+       err = ops->port_function_hw_addr_get(devlink, port, hw_addr, &hw_addr_len, extack);
+       if (err) {
+               if (err == -EOPNOTSUPP)
+                       return 0;
+               return err;
+       }
+       err = nla_put(msg, DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, hw_addr_len, hw_addr);
+       if (err)
+               return err;
+       *msg_updated = true;
+       return 0;
+}
+
+static bool
+devlink_port_fn_state_valid(enum devlink_port_fn_state state)
+{
+       return state == DEVLINK_PORT_FN_STATE_INACTIVE ||
+              state == DEVLINK_PORT_FN_STATE_ACTIVE;
+}
+
+static bool
+devlink_port_fn_opstate_valid(enum devlink_port_fn_opstate opstate)
+{
+       return opstate == DEVLINK_PORT_FN_OPSTATE_DETACHED ||
+              opstate == DEVLINK_PORT_FN_OPSTATE_ATTACHED;
+}
+
+static int
+devlink_port_fn_state_fill(struct devlink *devlink,
+                          const struct devlink_ops *ops,
+                          struct devlink_port *port, struct sk_buff *msg,
+                          struct netlink_ext_ack *extack,
+                          bool *msg_updated)
+{
+       enum devlink_port_fn_opstate opstate;
+       enum devlink_port_fn_state state;
+       int err;
+
+       if (!ops->port_fn_state_get)
+               return 0;
+
+       err = ops->port_fn_state_get(devlink, port, &state, &opstate, extack);
+       if (err) {
+               if (err == -EOPNOTSUPP)
+                       return 0;
+               return err;
+       }
+       if (!devlink_port_fn_state_valid(state)) {
+               WARN_ON_ONCE(1);
+               NL_SET_ERR_MSG_MOD(extack, "Invalid state read from driver");
+               return -EINVAL;
+       }
+       if (!devlink_port_fn_opstate_valid(opstate)) {
+               WARN_ON_ONCE(1);
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Invalid operational state read from driver");
+               return -EINVAL;
+       }
+       if (nla_put_u8(msg, DEVLINK_PORT_FN_ATTR_STATE, state) ||
+           nla_put_u8(msg, DEVLINK_PORT_FN_ATTR_OPSTATE, opstate))
+               return -EMSGSIZE;
+       *msg_updated = true;
+       return 0;
+}
+
 static int
 devlink_nl_port_function_attrs_put(struct sk_buff *msg, struct devlink_port *port,
                                   struct netlink_ext_ack *extack)
@@ -719,36 +808,22 @@ devlink_nl_port_function_attrs_put(struct sk_buff *msg, struct devlink_port *por
        struct devlink *devlink = port->devlink;
        const struct devlink_ops *ops;
        struct nlattr *function_attr;
-       bool empty_nest = true;
-       int err = 0;
+       bool msg_updated = false;
+       int err;
 
        function_attr = nla_nest_start_noflag(msg, DEVLINK_ATTR_PORT_FUNCTION);
        if (!function_attr)
                return -EMSGSIZE;
 
        ops = devlink->ops;
-       if (ops->port_function_hw_addr_get) {
-               int hw_addr_len;
-               u8 hw_addr[MAX_ADDR_LEN];
-
-               err = ops->port_function_hw_addr_get(devlink, port, hw_addr, &hw_addr_len, extack);
-               if (err == -EOPNOTSUPP) {
-                       /* Port function attributes are optional for a port. If port doesn't
-                        * support function attribute, returning -EOPNOTSUPP is not an error.
-                        */
-                       err = 0;
-                       goto out;
-               } else if (err) {
-                       goto out;
-               }
-               err = nla_put(msg, DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, hw_addr_len, hw_addr);
-               if (err)
-                       goto out;
-               empty_nest = false;
-       }
-
+       err = devlink_port_fn_hw_addr_fill(devlink, ops, port, msg,
+                                          extack, &msg_updated);
+       if (err)
+               goto out;
+       err = devlink_port_fn_state_fill(devlink, ops, port, msg, extack,
+                                        &msg_updated);
 out:
-       if (err || empty_nest)
+       if (err || !msg_updated)
                nla_nest_cancel(msg, function_attr);
        else
                nla_nest_end(msg, function_attr);
@@ -986,7 +1061,6 @@ devlink_port_function_hw_addr_set(struct devlink *devlink, struct devlink_port *
        const struct devlink_ops *ops;
        const u8 *hw_addr;
        int hw_addr_len;
-       int err;
 
        hw_addr = nla_data(attr);
        hw_addr_len = nla_len(attr);
@@ -1011,12 +1085,25 @@ devlink_port_function_hw_addr_set(struct devlink *devlink, struct devlink_port *
                return -EOPNOTSUPP;
        }
 
-       err = ops->port_function_hw_addr_set(devlink, port, hw_addr, hw_addr_len, extack);
-       if (err)
-               return err;
+       return ops->port_function_hw_addr_set(devlink, port, hw_addr, hw_addr_len, extack);
+}
 
-       devlink_port_notify(port, DEVLINK_CMD_PORT_NEW);
-       return 0;
+static int devlink_port_fn_state_set(struct devlink *devlink,
+                                    struct devlink_port *port,
+                                    const struct nlattr *attr,
+                                    struct netlink_ext_ack *extack)
+{
+       enum devlink_port_fn_state state;
+       const struct devlink_ops *ops;
+
+       state = nla_get_u8(attr);
+       ops = devlink->ops;
+       if (!ops->port_fn_state_set) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Function does not support state setting");
+               return -EOPNOTSUPP;
+       }
+       return ops->port_fn_state_set(devlink, port, state, extack);
 }
 
 static int
@@ -1034,9 +1121,21 @@ devlink_port_function_set(struct devlink *devlink, struct devlink_port *port,
        }
 
        attr = tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR];
-       if (attr)
+       if (attr) {
                err = devlink_port_function_hw_addr_set(devlink, port, attr, extack);
+               if (err)
+                       return err;
+       }
+       /* Keep this as the last function attribute set, so that when
+        * multiple port function attributes are set along with state,
+        * Those can be applied first before activating the state.
+        */
+       attr = tb[DEVLINK_PORT_FN_ATTR_STATE];
+       if (attr)
+               err = devlink_port_fn_state_set(devlink, port, attr, extack);
 
+       if (!err)
+               devlink_port_notify(port, DEVLINK_CMD_PORT_NEW);
        return err;
 }
 
@@ -1136,6 +1235,111 @@ static int devlink_nl_cmd_port_unsplit_doit(struct sk_buff *skb,
        return devlink_port_unsplit(devlink, port_index, info->extack);
 }
 
+static int devlink_port_new_notifiy(struct devlink *devlink,
+                                   unsigned int port_index,
+                                   struct genl_info *info)
+{
+       struct devlink_port *devlink_port;
+       struct sk_buff *msg;
+       int err;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       mutex_lock(&devlink->lock);
+       devlink_port = devlink_port_get_by_index(devlink, port_index);
+       if (!devlink_port) {
+               err = -ENODEV;
+               goto out;
+       }
+
+       err = devlink_nl_port_fill(msg, devlink, devlink_port,
+                                  DEVLINK_CMD_NEW, info->snd_portid,
+                                  info->snd_seq, 0, NULL);
+       if (err)
+               goto out;
+
+       err = genlmsg_reply(msg, info);
+       mutex_unlock(&devlink->lock);
+       return err;
+
+out:
+       mutex_unlock(&devlink->lock);
+       nlmsg_free(msg);
+       return err;
+}
+
+static int devlink_nl_cmd_port_new_doit(struct sk_buff *skb,
+                                       struct genl_info *info)
+{
+       struct netlink_ext_ack *extack = info->extack;
+       struct devlink_port_new_attrs new_attrs = {};
+       struct devlink *devlink = info->user_ptr[0];
+       unsigned int new_port_index;
+       int err;
+
+       if (!devlink->ops->port_new || !devlink->ops->port_del)
+               return -EOPNOTSUPP;
+
+       if (!info->attrs[DEVLINK_ATTR_PORT_FLAVOUR] ||
+           !info->attrs[DEVLINK_ATTR_PORT_PCI_PF_NUMBER]) {
+               NL_SET_ERR_MSG_MOD(extack, "Port flavour or PCI PF are not specified");
+               return -EINVAL;
+       }
+       new_attrs.flavour = nla_get_u16(info->attrs[DEVLINK_ATTR_PORT_FLAVOUR]);
+       new_attrs.pfnum =
+               nla_get_u16(info->attrs[DEVLINK_ATTR_PORT_PCI_PF_NUMBER]);
+
+       if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) {
+               /* Port index of the new port being created by driver. */
+               new_attrs.port_index =
+                       nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
+               new_attrs.port_index_valid = true;
+       }
+       if (info->attrs[DEVLINK_ATTR_PORT_CONTROLLER_NUMBER]) {
+               new_attrs.controller =
+                       nla_get_u16(info->attrs[DEVLINK_ATTR_PORT_CONTROLLER_NUMBER]);
+               new_attrs.controller_valid = true;
+       }
+       if (new_attrs.flavour == DEVLINK_PORT_FLAVOUR_PCI_SF &&
+           info->attrs[DEVLINK_ATTR_PORT_PCI_SF_NUMBER]) {
+               new_attrs.sfnum = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_PCI_SF_NUMBER]);
+               new_attrs.sfnum_valid = true;
+       }
+
+       err = devlink->ops->port_new(devlink, &new_attrs, extack,
+                                    &new_port_index);
+       if (err)
+               return err;
+
+       err = devlink_port_new_notifiy(devlink, new_port_index, info);
+       if (err && err != -ENODEV) {
+               /* Fail to send the response; destroy newly created port. */
+               devlink->ops->port_del(devlink, new_port_index, extack);
+       }
+       return err;
+}
+
+static int devlink_nl_cmd_port_del_doit(struct sk_buff *skb,
+                                       struct genl_info *info)
+{
+       struct netlink_ext_ack *extack = info->extack;
+       struct devlink *devlink = info->user_ptr[0];
+       unsigned int port_index;
+
+       if (!devlink->ops->port_del)
+               return -EOPNOTSUPP;
+
+       if (!info->attrs[DEVLINK_ATTR_PORT_INDEX]) {
+               NL_SET_ERR_MSG_MOD(extack, "Port index is not specified");
+               return -EINVAL;
+       }
+       port_index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
+
+       return devlink->ops->port_del(devlink, port_index, extack);
+}
+
 static int devlink_nl_sb_fill(struct sk_buff *msg, struct devlink *devlink,
                              struct devlink_sb *devlink_sb,
                              enum devlink_command cmd, u32 portid,
@@ -7594,6 +7798,10 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
        [DEVLINK_ATTR_RELOAD_ACTION] = NLA_POLICY_RANGE(NLA_U8, DEVLINK_RELOAD_ACTION_DRIVER_REINIT,
                                                        DEVLINK_RELOAD_ACTION_MAX),
        [DEVLINK_ATTR_RELOAD_LIMITS] = NLA_POLICY_BITFIELD32(DEVLINK_RELOAD_LIMITS_VALID_MASK),
+       [DEVLINK_ATTR_PORT_FLAVOUR] = { .type = NLA_U16 },
+       [DEVLINK_ATTR_PORT_PCI_PF_NUMBER] = { .type = NLA_U16 },
+       [DEVLINK_ATTR_PORT_PCI_SF_NUMBER] = { .type = NLA_U32 },
+       [DEVLINK_ATTR_PORT_CONTROLLER_NUMBER] = { .type = NLA_U32 },
 };
 
 static const struct genl_small_ops devlink_nl_ops[] = {
@@ -7633,6 +7841,18 @@ static const struct genl_small_ops devlink_nl_ops[] = {
                .flags = GENL_ADMIN_PERM,
                .internal_flags = DEVLINK_NL_FLAG_NO_LOCK,
        },
+       {
+               .cmd = DEVLINK_CMD_PORT_NEW,
+               .doit = devlink_nl_cmd_port_new_doit,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NO_LOCK,
+       },
+       {
+               .cmd = DEVLINK_CMD_PORT_DEL,
+               .doit = devlink_nl_cmd_port_del_doit,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NO_LOCK,
+       },
        {
                .cmd = DEVLINK_CMD_SB_GET,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
@@ -8372,6 +8592,32 @@ void devlink_port_attrs_pci_vf_set(struct devlink_port *devlink_port, u32 contro
 }
 EXPORT_SYMBOL_GPL(devlink_port_attrs_pci_vf_set);
 
+/**
+ *     devlink_port_attrs_pci_sf_set - Set PCI SF port attributes
+ *
+ *     @devlink_port: devlink port
+ *     @controller: associated controller number for the devlink port instance
+ *     @pf: associated PF for the devlink port instance
+ *     @sf: associated SF of a PF for the devlink port instance
+ */
+void devlink_port_attrs_pci_sf_set(struct devlink_port *devlink_port, u32 controller,
+                                  u16 pf, u32 sf)
+{
+       struct devlink_port_attrs *attrs = &devlink_port->attrs;
+       int ret;
+
+       if (WARN_ON(devlink_port->registered))
+               return;
+       ret = __devlink_port_attrs_set(devlink_port,
+                                      DEVLINK_PORT_FLAVOUR_PCI_SF);
+       if (ret)
+               return;
+       attrs->pci_sf.controller = controller;
+       attrs->pci_sf.pf = pf;
+       attrs->pci_sf.sf = sf;
+}
+EXPORT_SYMBOL_GPL(devlink_port_attrs_pci_sf_set);
+
 static int __devlink_port_phys_port_name_get(struct devlink_port *devlink_port,
                                             char *name, size_t len)
 {
@@ -8420,6 +8666,10 @@ static int __devlink_port_phys_port_name_get(struct devlink_port *devlink_port,
                n = snprintf(name, len, "pf%uvf%u",
                             attrs->pci_vf.pf, attrs->pci_vf.vf);
                break;
+       case DEVLINK_PORT_FLAVOUR_PCI_SF:
+               n = snprintf(name, len, "pf%usf%u", attrs->pci_sf.pf,
+                            attrs->pci_sf.sf);
+               break;
        }
 
        if (n >= len)
@@ -8617,6 +8867,10 @@ EXPORT_SYMBOL_GPL(devlink_dpipe_table_unregister);
  *     @resource_id: resource's id
  *     @parent_resource_id: resource's parent id
  *     @size_params: size parameters
+ *
+ *     Generic resources should reuse the same names across drivers.
+ *     Please see the generic resources list at:
+ *     Documentation/networking/devlink/devlink-resource.rst
  */
 int devlink_resource_register(struct devlink *devlink,
                              const char *resource_name,
@@ -9508,6 +9762,7 @@ static const struct devlink_trap devlink_trap_generic[] = {
        DEVLINK_TRAP(GTP_PARSING, DROP),
        DEVLINK_TRAP(ESP_PARSING, DROP),
        DEVLINK_TRAP(BLACKHOLE_NEXTHOP, DROP),
+       DEVLINK_TRAP(DMAC_FILTER, DROP),
 };
 
 #define DEVLINK_TRAP_GROUP(_id)                                                      \