From patchwork Fri Dec 1 16:36:58 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Maxime Chevallier X-Patchwork-Id: 13476150 X-Patchwork-Delegate: kuba@kernel.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="WIv8nj2Z" Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [IPv6:2001:4b98:dc4:8::226]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 900111713; Fri, 1 Dec 2023 08:37:20 -0800 (PST) Received: by mail.gandi.net (Postfix) with ESMTPSA id 0BD2DC0008; Fri, 1 Dec 2023 16:37:17 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1701448639; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=UUPrERWVmZXfJBxo+Ko10zUTpdUI1iWcuWQHSDOYj1w=; b=WIv8nj2ZV2CZjYIhBAxYUu/IOA8Kx2/sL3iIPGhlNOJhAmmi8q1Zke4hJKrnt0XhuVI1nj E9NS8SSDORWSOkCbnwb22kS+RwcE7J/YHAIWiHm8GeVsj2jG/UPiyA8gKKstAeTPCPXxQB Nbdr+C8lam3L17dAw6gmJKSKW7OPpShOXfFvSx+TCIBQ6AoBaWxPQ1pdiHuH23usOLdZGs 2O/TqFC89hEVak1t1Xo+G5+yOvhHv1TZKPWRXnWjGeqXRpquIQWuVch4l4y1GUDnUgZEpo aIVxgHobgw/gjaFUoRuWVGUhtPKOu2H+v7tsCjJah7OyQRBIrN5lduJ7EM3VfA== From: Maxime Chevallier To: davem@davemloft.net Cc: Maxime Chevallier , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, thomas.petazzoni@bootlin.com, Andrew Lunn , Jakub Kicinski , Eric Dumazet , Paolo Abeni , Russell King , linux-arm-kernel@lists.infradead.org, Christophe Leroy , Herve Codina , Florian Fainelli , Heiner Kallweit , Vladimir Oltean , =?utf-8?q?K=C3=B6ry_Maincent?= , Jesse Brandeburg , Jonathan Corbet , =?utf-8?q?Marek_Beh=C3=BAn?= , Piergiorgio Beruto , Oleksij Rempel , =?utf-8?q?Nicol=C3=B2_Veronese?= Subject: [RFC PATCH net-next v3 08/13] netlink: specs: add ethnl PHY_GET command set Date: Fri, 1 Dec 2023 17:36:58 +0100 Message-ID: <20231201163704.1306431-9-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231201163704.1306431-1-maxime.chevallier@bootlin.com> References: <20231201163704.1306431-1-maxime.chevallier@bootlin.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-GND-Sasl: maxime.chevallier@bootlin.com X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC The PHY_GET command, supporting both DUMP and GET operations, is used to retrieve the list of PHYs connected to a netdevice, and get topology information to know where exactly it sits on the physical link. Add the netlink specs corresponding to that command, and bump the ethtool-user.c|h autogenerated files. Signed-off-by: Maxime Chevallier --- V3: New patch Documentation/netlink/specs/ethtool.yaml | 65 ++++++ tools/net/ynl/generated/ethtool-user.c | 257 +++++++++++++++++++++++ tools/net/ynl/generated/ethtool-user.h | 153 ++++++++++++++ 3 files changed, 475 insertions(+) diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml index 4e0790648913..280b090b5f7c 100644 --- a/Documentation/netlink/specs/ethtool.yaml +++ b/Documentation/netlink/specs/ethtool.yaml @@ -16,6 +16,11 @@ definitions: name: stringset type: enum entries: [] + - + name: phy-upstream-type + enum-name: + type: enum + entries: [ mac, phy ] attribute-sets: - @@ -942,6 +947,45 @@ attribute-sets: - name: burst-tmr type: u32 + - + name: phy-upstream + attributes: + - + name: index + type: u32 + - + name: sfp-name + type: string + - + name: phy + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: index + type: u32 + - + name: drvname + type: string + - + name: name + type: string + - + name: upstream-type + type: u8 + enum: phy-upstream-type + - + name: upstream + type: nest + nested-attributes: phy-upstream + - + name: downstream-sfp-name + type: string + - + name: id + type: u32 operations: enum-model: directional @@ -1692,3 +1736,24 @@ operations: name: mm-ntf doc: Notification for change in MAC Merge configuration. notify: mm-get + - + name: phy-get + doc: Get PHY devices attached to an interface + + attribute-set: phy + + do: &phy-get-op + request: + attributes: + - header + reply: + attributes: + - header + - index + - drvname + - name + - upstream-type + - upstream + - downstream-sfp-name + - id + dump: *phy-get-op diff --git a/tools/net/ynl/generated/ethtool-user.c b/tools/net/ynl/generated/ethtool-user.c index 295661eb3a3e..b63ba2d2e25e 100644 --- a/tools/net/ynl/generated/ethtool-user.c +++ b/tools/net/ynl/generated/ethtool-user.c @@ -59,6 +59,7 @@ static const char * const ethtool_op_strmap[] = { [41] = "plca-ntf", [ETHTOOL_MSG_MM_GET] = "mm-get", [43] = "mm-ntf", + [ETHTOOL_MSG_PHY_GET] = "phy-get", }; const char *ethtool_op_str(int op) @@ -91,6 +92,18 @@ const char *ethtool_stringset_str(enum ethtool_stringset value) return ethtool_stringset_strmap[value]; } +static const char * const ethtool_phy_upstream_type_strmap[] = { + [0] = "mac", + [1] = "phy", +}; + +const char *ethtool_phy_upstream_type_str(int value) +{ + if (value < 0 || value >= (int)MNL_ARRAY_SIZE(ethtool_phy_upstream_type_strmap)) + return NULL; + return ethtool_phy_upstream_type_strmap[value]; +} + /* Policies */ struct ynl_policy_attr ethtool_header_policy[ETHTOOL_A_HEADER_MAX + 1] = { [ETHTOOL_A_HEADER_DEV_INDEX] = { .name = "dev-index", .type = YNL_PT_U32, }, @@ -154,6 +167,16 @@ struct ynl_policy_nest ethtool_mm_stat_nest = { .table = ethtool_mm_stat_policy, }; +struct ynl_policy_attr ethtool_phy_upstream_policy[ETHTOOL_A_PHY_UPSTREAM_MAX + 1] = { + [ETHTOOL_A_PHY_UPSTREAM_INDEX] = { .name = "index", .type = YNL_PT_U32, }, + [ETHTOOL_A_PHY_UPSTREAM_SFP_NAME] = { .name = "sfp-name", .type = YNL_PT_NUL_STR, }, +}; + +struct ynl_policy_nest ethtool_phy_upstream_nest = { + .max_attr = ETHTOOL_A_PHY_UPSTREAM_MAX, + .table = ethtool_phy_upstream_policy, +}; + struct ynl_policy_attr ethtool_cable_result_policy[ETHTOOL_A_CABLE_RESULT_MAX + 1] = { [ETHTOOL_A_CABLE_RESULT_PAIR] = { .name = "pair", .type = YNL_PT_U8, }, [ETHTOOL_A_CABLE_RESULT_CODE] = { .name = "code", .type = YNL_PT_U8, }, @@ -667,6 +690,22 @@ struct ynl_policy_nest ethtool_mm_nest = { .table = ethtool_mm_policy, }; +struct ynl_policy_attr ethtool_phy_policy[ETHTOOL_A_PHY_MAX + 1] = { + [ETHTOOL_A_PHY_HEADER] = { .name = "header", .type = YNL_PT_NEST, .nest = ðtool_header_nest, }, + [ETHTOOL_A_PHY_INDEX] = { .name = "index", .type = YNL_PT_U32, }, + [ETHTOOL_A_PHY_DRVNAME] = { .name = "drvname", .type = YNL_PT_NUL_STR, }, + [ETHTOOL_A_PHY_NAME] = { .name = "name", .type = YNL_PT_NUL_STR, }, + [ETHTOOL_A_PHY_UPSTREAM_TYPE] = { .name = "upstream-type", .type = YNL_PT_U8, }, + [ETHTOOL_A_PHY_UPSTREAM] = { .name = "upstream", .type = YNL_PT_NEST, .nest = ðtool_phy_upstream_nest, }, + [ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME] = { .name = "downstream-sfp-name", .type = YNL_PT_NUL_STR, }, + [ETHTOOL_A_PHY_ID] = { .name = "id", .type = YNL_PT_U32, }, +}; + +struct ynl_policy_nest ethtool_phy_nest = { + .max_attr = ETHTOOL_A_PHY_MAX, + .table = ethtool_phy_policy, +}; + /* Common nested types */ void ethtool_header_free(struct ethtool_header *obj) { @@ -899,6 +938,42 @@ int ethtool_mm_stat_parse(struct ynl_parse_arg *yarg, return 0; } +void ethtool_phy_upstream_free(struct ethtool_phy_upstream *obj) +{ + free(obj->sfp_name); +} + +int ethtool_phy_upstream_parse(struct ynl_parse_arg *yarg, + const struct nlattr *nested) +{ + struct ethtool_phy_upstream *dst = yarg->data; + const struct nlattr *attr; + + mnl_attr_for_each_nested(attr, nested) { + unsigned int type = mnl_attr_get_type(attr); + + if (type == ETHTOOL_A_PHY_UPSTREAM_INDEX) { + if (ynl_attr_validate(yarg, attr)) + return MNL_CB_ERROR; + dst->_present.index = 1; + dst->index = mnl_attr_get_u32(attr); + } else if (type == ETHTOOL_A_PHY_UPSTREAM_SFP_NAME) { + unsigned int len; + + if (ynl_attr_validate(yarg, attr)) + return MNL_CB_ERROR; + + len = strnlen(mnl_attr_get_str(attr), mnl_attr_get_payload_len(attr)); + dst->_present.sfp_name_len = len; + dst->sfp_name = malloc(len + 1); + memcpy(dst->sfp_name, mnl_attr_get_str(attr), len); + dst->sfp_name[len] = 0; + } + } + + return 0; +} + void ethtool_cable_result_free(struct ethtool_cable_result *obj) { } @@ -6158,6 +6233,188 @@ int ethtool_mm_set(struct ynl_sock *ys, struct ethtool_mm_set_req *req) return 0; } +/* ============== ETHTOOL_MSG_PHY_GET ============== */ +/* ETHTOOL_MSG_PHY_GET - do */ +void ethtool_phy_get_req_free(struct ethtool_phy_get_req *req) +{ + ethtool_header_free(&req->header); + free(req); +} + +void ethtool_phy_get_rsp_free(struct ethtool_phy_get_rsp *rsp) +{ + ethtool_header_free(&rsp->header); + free(rsp->drvname); + free(rsp->name); + ethtool_phy_upstream_free(&rsp->upstream); + free(rsp->downstream_sfp_name); + free(rsp); +} + +int ethtool_phy_get_rsp_parse(const struct nlmsghdr *nlh, void *data) +{ + struct ynl_parse_arg *yarg = data; + struct ethtool_phy_get_rsp *dst; + const struct nlattr *attr; + struct ynl_parse_arg parg; + + dst = yarg->data; + parg.ys = yarg->ys; + + mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) { + unsigned int type = mnl_attr_get_type(attr); + + if (type == ETHTOOL_A_PHY_HEADER) { + if (ynl_attr_validate(yarg, attr)) + return MNL_CB_ERROR; + dst->_present.header = 1; + + parg.rsp_policy = ðtool_header_nest; + parg.data = &dst->header; + if (ethtool_header_parse(&parg, attr)) + return MNL_CB_ERROR; + } else if (type == ETHTOOL_A_PHY_INDEX) { + if (ynl_attr_validate(yarg, attr)) + return MNL_CB_ERROR; + dst->_present.index = 1; + dst->index = mnl_attr_get_u32(attr); + } else if (type == ETHTOOL_A_PHY_DRVNAME) { + unsigned int len; + + if (ynl_attr_validate(yarg, attr)) + return MNL_CB_ERROR; + + len = strnlen(mnl_attr_get_str(attr), mnl_attr_get_payload_len(attr)); + dst->_present.drvname_len = len; + dst->drvname = malloc(len + 1); + memcpy(dst->drvname, mnl_attr_get_str(attr), len); + dst->drvname[len] = 0; + } else if (type == ETHTOOL_A_PHY_NAME) { + unsigned int len; + + if (ynl_attr_validate(yarg, attr)) + return MNL_CB_ERROR; + + len = strnlen(mnl_attr_get_str(attr), mnl_attr_get_payload_len(attr)); + dst->_present.name_len = len; + dst->name = malloc(len + 1); + memcpy(dst->name, mnl_attr_get_str(attr), len); + dst->name[len] = 0; + } else if (type == ETHTOOL_A_PHY_UPSTREAM_TYPE) { + if (ynl_attr_validate(yarg, attr)) + return MNL_CB_ERROR; + dst->_present.upstream_type = 1; + dst->upstream_type = mnl_attr_get_u8(attr); + } else if (type == ETHTOOL_A_PHY_UPSTREAM) { + if (ynl_attr_validate(yarg, attr)) + return MNL_CB_ERROR; + dst->_present.upstream = 1; + + parg.rsp_policy = ðtool_phy_upstream_nest; + parg.data = &dst->upstream; + if (ethtool_phy_upstream_parse(&parg, attr)) + return MNL_CB_ERROR; + } else if (type == ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME) { + unsigned int len; + + if (ynl_attr_validate(yarg, attr)) + return MNL_CB_ERROR; + + len = strnlen(mnl_attr_get_str(attr), mnl_attr_get_payload_len(attr)); + dst->_present.downstream_sfp_name_len = len; + dst->downstream_sfp_name = malloc(len + 1); + memcpy(dst->downstream_sfp_name, mnl_attr_get_str(attr), len); + dst->downstream_sfp_name[len] = 0; + } else if (type == ETHTOOL_A_PHY_ID) { + if (ynl_attr_validate(yarg, attr)) + return MNL_CB_ERROR; + dst->_present.id = 1; + dst->id = mnl_attr_get_u32(attr); + } + } + + return MNL_CB_OK; +} + +struct ethtool_phy_get_rsp * +ethtool_phy_get(struct ynl_sock *ys, struct ethtool_phy_get_req *req) +{ + struct ynl_req_state yrs = { .yarg = { .ys = ys, }, }; + struct ethtool_phy_get_rsp *rsp; + struct nlmsghdr *nlh; + int err; + + nlh = ynl_gemsg_start_req(ys, ys->family_id, ETHTOOL_MSG_PHY_GET, 1); + ys->req_policy = ðtool_phy_nest; + yrs.yarg.rsp_policy = ðtool_phy_nest; + + if (req->_present.header) + ethtool_header_put(nlh, ETHTOOL_A_PHY_HEADER, &req->header); + + rsp = calloc(1, sizeof(*rsp)); + yrs.yarg.data = rsp; + yrs.cb = ethtool_phy_get_rsp_parse; + yrs.rsp_cmd = ETHTOOL_MSG_PHY_GET; + + err = ynl_exec(ys, nlh, &yrs); + if (err < 0) + goto err_free; + + return rsp; + +err_free: + ethtool_phy_get_rsp_free(rsp); + return NULL; +} + +/* ETHTOOL_MSG_PHY_GET - dump */ +void ethtool_phy_get_list_free(struct ethtool_phy_get_list *rsp) +{ + struct ethtool_phy_get_list *next = rsp; + + while ((void *)next != YNL_LIST_END) { + rsp = next; + next = rsp->next; + + ethtool_header_free(&rsp->obj.header); + free(rsp->obj.drvname); + free(rsp->obj.name); + ethtool_phy_upstream_free(&rsp->obj.upstream); + free(rsp->obj.downstream_sfp_name); + free(rsp); + } +} + +struct ethtool_phy_get_list * +ethtool_phy_get_dump(struct ynl_sock *ys, struct ethtool_phy_get_req_dump *req) +{ + struct ynl_dump_state yds = {}; + struct nlmsghdr *nlh; + int err; + + yds.ys = ys; + yds.alloc_sz = sizeof(struct ethtool_phy_get_list); + yds.cb = ethtool_phy_get_rsp_parse; + yds.rsp_cmd = ETHTOOL_MSG_PHY_GET; + yds.rsp_policy = ðtool_phy_nest; + + nlh = ynl_gemsg_start_dump(ys, ys->family_id, ETHTOOL_MSG_PHY_GET, 1); + ys->req_policy = ðtool_phy_nest; + + if (req->_present.header) + ethtool_header_put(nlh, ETHTOOL_A_PHY_HEADER, &req->header); + + err = ynl_exec_dump(ys, nlh, &yds); + if (err < 0) + goto free_list; + + return yds.first; + +free_list: + ethtool_phy_get_list_free(yds.first); + return NULL; +} + /* ETHTOOL_MSG_CABLE_TEST_NTF - event */ int ethtool_cable_test_ntf_rsp_parse(const struct nlmsghdr *nlh, void *data) { diff --git a/tools/net/ynl/generated/ethtool-user.h b/tools/net/ynl/generated/ethtool-user.h index 97c079c0f332..59ebb0a1a09f 100644 --- a/tools/net/ynl/generated/ethtool-user.h +++ b/tools/net/ynl/generated/ethtool-user.h @@ -20,6 +20,7 @@ extern const struct ynl_family ynl_ethtool_family; const char *ethtool_op_str(int op); const char *ethtool_udp_tunnel_type_str(int value); const char *ethtool_stringset_str(enum ethtool_stringset value); +const char *ethtool_phy_upstream_type_str(int value); /* Common nested types */ struct ethtool_header { @@ -90,6 +91,16 @@ struct ethtool_mm_stat { __u64 hold_count; }; +struct ethtool_phy_upstream { + struct { + __u32 index:1; + __u32 sfp_name_len; + } _present; + + __u32 index; + char *sfp_name; +}; + struct ethtool_cable_result { struct { __u32 pair:1; @@ -6018,6 +6029,148 @@ ethtool_mm_set_req_set_tx_min_frag_size(struct ethtool_mm_set_req *req, */ int ethtool_mm_set(struct ynl_sock *ys, struct ethtool_mm_set_req *req); +/* ============== ETHTOOL_MSG_PHY_GET ============== */ +/* ETHTOOL_MSG_PHY_GET - do */ +struct ethtool_phy_get_req { + struct { + __u32 header:1; + } _present; + + struct ethtool_header header; +}; + +static inline struct ethtool_phy_get_req *ethtool_phy_get_req_alloc(void) +{ + return calloc(1, sizeof(struct ethtool_phy_get_req)); +} +void ethtool_phy_get_req_free(struct ethtool_phy_get_req *req); + +static inline void +ethtool_phy_get_req_set_header_dev_index(struct ethtool_phy_get_req *req, + __u32 dev_index) +{ + req->_present.header = 1; + req->header._present.dev_index = 1; + req->header.dev_index = dev_index; +} +static inline void +ethtool_phy_get_req_set_header_dev_name(struct ethtool_phy_get_req *req, + const char *dev_name) +{ + free(req->header.dev_name); + req->header._present.dev_name_len = strlen(dev_name); + req->header.dev_name = malloc(req->header._present.dev_name_len + 1); + memcpy(req->header.dev_name, dev_name, req->header._present.dev_name_len); + req->header.dev_name[req->header._present.dev_name_len] = 0; +} +static inline void +ethtool_phy_get_req_set_header_flags(struct ethtool_phy_get_req *req, + __u32 flags) +{ + req->_present.header = 1; + req->header._present.flags = 1; + req->header.flags = flags; +} +static inline void +ethtool_phy_get_req_set_header_phy_index(struct ethtool_phy_get_req *req, + __u32 phy_index) +{ + req->_present.header = 1; + req->header._present.phy_index = 1; + req->header.phy_index = phy_index; +} + +struct ethtool_phy_get_rsp { + struct { + __u32 header:1; + __u32 index:1; + __u32 drvname_len; + __u32 name_len; + __u32 upstream_type:1; + __u32 upstream:1; + __u32 downstream_sfp_name_len; + __u32 id:1; + } _present; + + struct ethtool_header header; + __u32 index; + char *drvname; + char *name; + __u8 upstream_type; + struct ethtool_phy_upstream upstream; + char *downstream_sfp_name; + __u32 id; +}; + +void ethtool_phy_get_rsp_free(struct ethtool_phy_get_rsp *rsp); + +/* + * Get PHY devices attached to an interface + */ +struct ethtool_phy_get_rsp * +ethtool_phy_get(struct ynl_sock *ys, struct ethtool_phy_get_req *req); + +/* ETHTOOL_MSG_PHY_GET - dump */ +struct ethtool_phy_get_req_dump { + struct { + __u32 header:1; + } _present; + + struct ethtool_header header; +}; + +static inline struct ethtool_phy_get_req_dump * +ethtool_phy_get_req_dump_alloc(void) +{ + return calloc(1, sizeof(struct ethtool_phy_get_req_dump)); +} +void ethtool_phy_get_req_dump_free(struct ethtool_phy_get_req_dump *req); + +static inline void +ethtool_phy_get_req_dump_set_header_dev_index(struct ethtool_phy_get_req_dump *req, + __u32 dev_index) +{ + req->_present.header = 1; + req->header._present.dev_index = 1; + req->header.dev_index = dev_index; +} +static inline void +ethtool_phy_get_req_dump_set_header_dev_name(struct ethtool_phy_get_req_dump *req, + const char *dev_name) +{ + free(req->header.dev_name); + req->header._present.dev_name_len = strlen(dev_name); + req->header.dev_name = malloc(req->header._present.dev_name_len + 1); + memcpy(req->header.dev_name, dev_name, req->header._present.dev_name_len); + req->header.dev_name[req->header._present.dev_name_len] = 0; +} +static inline void +ethtool_phy_get_req_dump_set_header_flags(struct ethtool_phy_get_req_dump *req, + __u32 flags) +{ + req->_present.header = 1; + req->header._present.flags = 1; + req->header.flags = flags; +} +static inline void +ethtool_phy_get_req_dump_set_header_phy_index(struct ethtool_phy_get_req_dump *req, + __u32 phy_index) +{ + req->_present.header = 1; + req->header._present.phy_index = 1; + req->header.phy_index = phy_index; +} + +struct ethtool_phy_get_list { + struct ethtool_phy_get_list *next; + struct ethtool_phy_get_rsp obj __attribute__((aligned(8))); +}; + +void ethtool_phy_get_list_free(struct ethtool_phy_get_list *rsp); + +struct ethtool_phy_get_list * +ethtool_phy_get_dump(struct ynl_sock *ys, struct ethtool_phy_get_req_dump *req); + /* ETHTOOL_MSG_CABLE_TEST_NTF - event */ struct ethtool_cable_test_ntf_rsp { struct {