ethtool -i has served us well for a long time, but its showing its limitations more and more. The device information should also be reported per device not per-netdev. Lay foundation for a simple devlink-based way of reading device info. Add driver name and device serial number as initial pieces of information exposed via this new API. v3: - rename helpers (Jiri); - rename driver name attr (Jiri); - remove double spacing in commit message (Jiri). RFC v2: - wrap the skb into an opaque structure (Jiri); - allow the serial number of be any length (Jiri & Andrew); - add driver name (Jonathan).
Unified · Split
| @@ -429,6 +429,7 @@ enum devlink_param_wol_types { · 428 unmodified lines | |||
| 429 | } | 429 | } |
| 430 | 430 | ||
| 431 | struct devlink_region; | 431 | struct devlink_region; |
| 432 | struct devlink_info_req; | ||
| 432 | 433 | ||
| 433 | typedef void devlink_snapshot_data_dest_t(const void *data); | 434 | typedef void devlink_snapshot_data_dest_t(const void *data); |
| 434 | 435 | ||
| @@ -484,6 +485,8 @@ struct devlink_ops { · 49 unmodified lines | |||
| 484 | int (*eswitch_encap_mode_get)(struct devlink *devlink, u8 *p_encap_mode); | 485 | int (*eswitch_encap_mode_get)(struct devlink *devlink, u8 *p_encap_mode); |
| 485 | int (*eswitch_encap_mode_set)(struct devlink *devlink, u8 encap_mode, | 486 | int (*eswitch_encap_mode_set)(struct devlink *devlink, u8 encap_mode, |
| 486 | struct netlink_ext_ack *extack); | 487 | struct netlink_ext_ack *extack); |
| 488 | int (*info_get)(struct devlink *devlink, struct devlink_info_req *req, | ||
| 489 | struct netlink_ext_ack *extack); | ||
| 487 | }; | 490 | }; |
| 488 | 491 | ||
| 489 | static inline void *devlink_priv(struct devlink *devlink) | 492 | static inline void *devlink_priv(struct devlink *devlink) |
| @@ -607,6 +610,10 @@ u32 devlink_region_shapshot_id_get(struct devlink *devlink); · 117 unmodified lines | |||
| 607 | int devlink_region_snapshot_create(struct devlink_region *region, u64 data_len, | 610 | int devlink_region_snapshot_create(struct devlink_region *region, u64 data_len, |
| 608 | u8 *data, u32 snapshot_id, | 611 | u8 *data, u32 snapshot_id, |
| 609 | devlink_snapshot_data_dest_t *data_destructor); | 612 | devlink_snapshot_data_dest_t *data_destructor); |
| 613 | int devlink_info_serial_number_put(struct devlink_info_req *req, | ||
| 614 | const char *sn); | ||
| 615 | int devlink_info_driver_name_put(struct devlink_info_req *req, | ||
| 616 | const char *name); | ||
| 610 | 617 | ||
| 611 | #else | 618 | #else |
| 612 | 619 | ||
| @@ -905,6 +912,17 @@ devlink_region_snapshot_create(struct devlink_region *region, u64 data_len, · 292 unmodified lines | |||
| 905 | return 0; | 912 | return 0; |
| 906 | } | 913 | } |
| 907 | 914 | ||
| 915 | static inline int | ||
| 916 | devlink_info_driver_name_put(struct devlink_info_req *req, const char *name) | ||
| 917 | { | ||
| 918 | return 0; | ||
| 919 | } | ||
| 920 | |||
| 921 | static inline int | ||
| 922 | devlink_info_serial_number_put(struct devlink_info_req *req, const char *sn) | ||
| 923 | { | ||
| 924 | return 0; | ||
| 925 | } | ||
| 908 | #endif | 926 | #endif |
| 909 | 927 | ||
| 910 | #endif /* _NET_DEVLINK_H_ */ | 928 | #endif /* _NET_DEVLINK_H_ */ |
| @@ -94,6 +94,8 @@ enum devlink_command { · 93 unmodified lines | |||
| 94 | DEVLINK_CMD_PORT_PARAM_NEW, | 94 | DEVLINK_CMD_PORT_PARAM_NEW, |
| 95 | DEVLINK_CMD_PORT_PARAM_DEL, | 95 | DEVLINK_CMD_PORT_PARAM_DEL, |
| 96 | 96 | ||
| 97 | DEVLINK_CMD_INFO_GET, /* can dump */ | ||
| 98 | |||
| 97 | /* add new commands above here */ | 99 | /* add new commands above here */ |
| 98 | __DEVLINK_CMD_MAX, | 100 | __DEVLINK_CMD_MAX, |
| 99 | DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1 | 101 | DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1 |
| @@ -290,6 +292,9 @@ enum devlink_attr { · 190 unmodified lines | |||
| 290 | DEVLINK_ATTR_REGION_CHUNK_ADDR, /* u64 */ | 292 | DEVLINK_ATTR_REGION_CHUNK_ADDR, /* u64 */ |
| 291 | DEVLINK_ATTR_REGION_CHUNK_LEN, /* u64 */ | 293 | DEVLINK_ATTR_REGION_CHUNK_LEN, /* u64 */ |
| 292 | 294 | ||
| 295 | DEVLINK_ATTR_INFO_DRIVER_NAME, /* string */ | ||
| 296 | DEVLINK_ATTR_INFO_SERIAL_NUMBER, /* string */ | ||
| 297 | |||
| 293 | /* add new attributes above here, update the policy in devlink.c */ | 298 | /* add new attributes above here, update the policy in devlink.c */ |
| 294 | 299 | ||
| 295 | __DEVLINK_ATTR_MAX, | 300 | __DEVLINK_ATTR_MAX, |
| @@ -3714,6 +3714,110 @@ out: · 3713 unmodified lines | |||
| 3714 | return 0; | 3714 | return 0; |
| 3715 | } | 3715 | } |
| 3716 | 3716 | ||
| 3717 | struct devlink_info_req { | ||
| 3718 | struct sk_buff *msg; | ||
| 3719 | }; | ||
| 3720 | |||
| 3721 | int devlink_info_driver_name_put(struct devlink_info_req *req, const char *name) | ||
| 3722 | { | ||
| 3723 | return nla_put_string(req->msg, DEVLINK_ATTR_INFO_DRIVER_NAME, name); | ||
| 3724 | } | ||
| 3725 | EXPORT_SYMBOL_GPL(devlink_info_driver_name_put); | ||
| 3726 | |||
| 3727 | int devlink_info_serial_number_put(struct devlink_info_req *req, const char *sn) | ||
| 3728 | { | ||
| 3729 | return nla_put_string(req->msg, DEVLINK_ATTR_INFO_SERIAL_NUMBER, sn); | ||
| 3730 | } | ||
| 3731 | EXPORT_SYMBOL_GPL(devlink_info_serial_number_put); | ||
| 3732 | |||
| 3733 | static int | ||
| 3734 | devlink_nl_info_fill(struct sk_buff *msg, struct devlink *devlink, | ||
| 3735 | enum devlink_command cmd, u32 portid, | ||
| 3736 | u32 seq, int flags, struct netlink_ext_ack *extack) | ||
| 3737 | { | ||
| 3738 | struct devlink_info_req req; | ||
| 3739 | void *hdr; | ||
| 3740 | int err; | ||
| 3741 | |||
| 3742 | hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd); | ||
| 3743 | if (!hdr) | ||
| 3744 | return -EMSGSIZE; | ||
| 3745 | |||
| 3746 | err = -EMSGSIZE; | ||
| 3747 | if (devlink_nl_put_handle(msg, devlink)) | ||
| 3748 | goto err_cancel_msg; | ||
| 3749 | |||
| 3750 | req.msg = msg; | ||
| 3751 | err = devlink->ops->info_get(devlink, &req, extack); | ||
| 3752 | if (err) | ||
| 3753 | goto err_cancel_msg; | ||
| 3754 | |||
| 3755 | genlmsg_end(msg, hdr); | ||
| 3756 | return 0; | ||
| 3757 | |||
| 3758 | err_cancel_msg: | ||
| 3759 | genlmsg_cancel(msg, hdr); | ||
| 3760 | return err; | ||
| 3761 | } | ||
| 3762 | |||
| 3763 | static int devlink_nl_cmd_info_get_doit(struct sk_buff *skb, | ||
| 3764 | struct genl_info *info) | ||
| 3765 | { | ||
| 3766 | struct devlink *devlink = info->user_ptr[0]; | ||
| 3767 | struct sk_buff *msg; | ||
| 3768 | int err; | ||
| 3769 | |||
| 3770 | if (!devlink->ops || !devlink->ops->info_get) | ||
| 3771 | return -EOPNOTSUPP; | ||
| 3772 | |||
| 3773 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
| 3774 | if (!msg) | ||
| 3775 | return -ENOMEM; | ||
| 3776 | |||
| 3777 | err = devlink_nl_info_fill(msg, devlink, DEVLINK_CMD_INFO_GET, | ||
| 3778 | info->snd_portid, info->snd_seq, 0, | ||
| 3779 | info->extack); | ||
| 3780 | if (err) { | ||
| 3781 | nlmsg_free(msg); | ||
| 3782 | return err; | ||
| 3783 | } | ||
| 3784 | |||
| 3785 | return genlmsg_reply(msg, info); | ||
| 3786 | } | ||
| 3787 | |||
| 3788 | static int devlink_nl_cmd_info_get_dumpit(struct sk_buff *msg, | ||
| 3789 | struct netlink_callback *cb) | ||
| 3790 | { | ||
| 3791 | struct devlink *devlink; | ||
| 3792 | int start = cb->args[0]; | ||
| 3793 | int idx = 0; | ||
| 3794 | int err; | ||
| 3795 | |||
| 3796 | mutex_lock(&devlink_mutex); | ||
| 3797 | list_for_each_entry(devlink, &devlink_list, list) { | ||
| 3798 | if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) | ||
| 3799 | continue; | ||
| 3800 | if (idx < start) { | ||
| 3801 | idx++; | ||
| 3802 | continue; | ||
| 3803 | } | ||
| 3804 | |||
| 3805 | mutex_lock(&devlink->lock); | ||
| 3806 | err = devlink_nl_info_fill(msg, devlink, DEVLINK_CMD_INFO_GET, | ||
| 3807 | NETLINK_CB(cb->skb).portid, | ||
| 3808 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | ||
| 3809 | cb->extack); | ||
| 3810 | mutex_unlock(&devlink->lock); | ||
| 3811 | if (err) | ||
| 3812 | break; | ||
| 3813 | idx++; | ||
| 3814 | } | ||
| 3815 | mutex_unlock(&devlink_mutex); | ||
| 3816 | |||
| 3817 | cb->args[0] = idx; | ||
| 3818 | return msg->len; | ||
| 3819 | } | ||
| 3820 | |||
| 3717 | static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { | 3821 | static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { |
| 3718 | [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING }, | 3822 | [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING }, |
| 3719 | [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING }, | 3823 | [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING }, |
| @@ -3974,6 +4078,14 @@ static const struct genl_ops devlink_nl_ops[] = { · 254 unmodified lines | |||
| 3974 | .flags = GENL_ADMIN_PERM, | 4078 | .flags = GENL_ADMIN_PERM, |
| 3975 | .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, | 4079 | .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, |
| 3976 | }, | 4080 | }, |
| 4081 | { | ||
| 4082 | .cmd = DEVLINK_CMD_INFO_GET, | ||
| 4083 | .doit = devlink_nl_cmd_info_get_doit, | ||
| 4084 | .dumpit = devlink_nl_cmd_info_get_dumpit, | ||
| 4085 | .policy = devlink_nl_policy, | ||
| 4086 | .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, | ||
| 4087 | /* can be retrieved by unprivileged users */ | ||
| 4088 | }, | ||
| 3977 | }; | 4089 | }; |
| 3978 | 4090 | ||
| 3979 | static struct genl_family devlink_nl_family __ro_after_init = { | 4091 | static struct genl_family devlink_nl_family __ro_after_init = { |