int ethnl_ops_begin(struct net_device *dev)
{ int ret;
if (!dev) return -ENODEV;
if (dev->dev.parent)
pm_runtime_get_sync(dev->dev.parent);
netdev_ops_assert_locked(dev);
if (!netif_device_present(dev) ||
dev->reg_state >= NETREG_UNREGISTERING) {
ret = -ENODEV; goto err;
}
if (dev->ethtool_ops->begin) {
ret = dev->ethtool_ops->begin(dev); if (ret) goto err;
}
return 0;
err: if (dev->dev.parent)
pm_runtime_put(dev->dev.parent);
return ret;
}
void ethnl_ops_complete(struct net_device *dev)
{ if (dev->ethtool_ops->complete)
dev->ethtool_ops->complete(dev);
if (dev->dev.parent)
pm_runtime_put(dev->dev.parent);
}
/** * ethnl_parse_header_dev_get() - parse request header * @req_info: structure to put results into * @header: nest attribute with request header * @net: request netns * @extack: netlink extack for error reporting * @require_dev: fail if no device identified in header * * Parse request header in nested attribute @nest and puts results into * the structure pointed to by @req_info. Extack from @info is used for error * reporting. If req_info->dev is not null on return, reference to it has * been taken. If error is returned, *req_info is null initialized and no * reference is held. * * Return: 0 on success or negative error code
*/ int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info, conststruct nlattr *header, struct net *net, struct netlink_ext_ack *extack, bool require_dev)
{ struct nlattr *tb[ARRAY_SIZE(ethnl_header_policy_phy)]; conststruct nlattr *devname_attr; struct net_device *dev = NULL;
u32 flags = 0; int ret;
if (!header) { if (!require_dev) return 0;
NL_SET_ERR_MSG(extack, "request header missing"); return -EINVAL;
} /* No validation here, command policy should have a nested policy set * for the header, therefore validation should have already been done.
*/
ret = nla_parse_nested(tb, ARRAY_SIZE(ethnl_header_policy_phy) - 1, header,
NULL, extack); if (ret < 0) return ret; if (tb[ETHTOOL_A_HEADER_FLAGS])
flags = nla_get_u32(tb[ETHTOOL_A_HEADER_FLAGS]);
devname_attr = tb[ETHTOOL_A_HEADER_DEV_NAME]; if (tb[ETHTOOL_A_HEADER_DEV_INDEX]) {
u32 ifindex = nla_get_u32(tb[ETHTOOL_A_HEADER_DEV_INDEX]);
dev = netdev_get_by_index(net, ifindex, &req_info->dev_tracker,
GFP_KERNEL); if (!dev) {
NL_SET_ERR_MSG_ATTR(extack,
tb[ETHTOOL_A_HEADER_DEV_INDEX], "no device matches ifindex"); return -ENODEV;
} /* if both ifindex and ifname are passed, they must match */ if (devname_attr &&
strncmp(dev->name, nla_data(devname_attr), IFNAMSIZ)) {
netdev_put(dev, &req_info->dev_tracker);
NL_SET_ERR_MSG_ATTR(extack, header, "ifindex and name do not match"); return -ENODEV;
}
} elseif (devname_attr) {
dev = netdev_get_by_name(net, nla_data(devname_attr),
&req_info->dev_tracker, GFP_KERNEL); if (!dev) {
NL_SET_ERR_MSG_ATTR(extack, devname_attr, "no device matches name"); return -ENODEV;
}
} elseif (require_dev) {
NL_SET_ERR_MSG_ATTR(extack, header, "neither ifindex nor name specified"); return -EINVAL;
}
if (tb[ETHTOOL_A_HEADER_PHY_INDEX]) { if (dev) {
req_info->phy_index = nla_get_u32(tb[ETHTOOL_A_HEADER_PHY_INDEX]);
} else {
NL_SET_ERR_MSG_ATTR(extack, header, "phy_index set without a netdev"); return -EINVAL;
}
}
if (!req_info->phy_index) return req_info->dev->phydev;
phydev = phy_link_topo_get_phy(req_info->dev, req_info->phy_index); if (!phydev && tb) {
NL_SET_ERR_MSG_ATTR(extack, tb[header], "no phy matching phyindex"); return ERR_PTR(-ENODEV);
}
return phydev;
}
/** * ethnl_fill_reply_header() - Put common header into a reply message * @skb: skb with the message * @dev: network device to describe in header * @attrtype: attribute type to use for the nest * * Create a nested attribute with attributes describing given network device. * * Return: 0 on success, error value (-EMSGSIZE only) on error
*/ int ethnl_fill_reply_header(struct sk_buff *skb, struct net_device *dev,
u16 attrtype)
{ struct nlattr *nest;
if (!dev) return 0;
nest = nla_nest_start(skb, attrtype); if (!nest) return -EMSGSIZE;
if (nla_put_u32(skb, ETHTOOL_A_HEADER_DEV_INDEX, (u32)dev->ifindex) ||
nla_put_string(skb, ETHTOOL_A_HEADER_DEV_NAME, dev->name)) goto nla_put_failure; /* If more attributes are put into reply header, ethnl_header_size() * must be updated to account for them.
*/
/** * ethnl_reply_init() - Create skb for a reply and fill device identification * @payload: payload length (without netlink and genetlink header) * @dev: device the reply is about (may be null) * @cmd: ETHTOOL_MSG_* message type for reply * @hdr_attrtype: attribute type for common header * @info: genetlink info of the received packet we respond to * @ehdrp: place to store payload pointer returned by genlmsg_new() * * Return: pointer to allocated skb on success, NULL on error
*/ struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd,
u16 hdr_attrtype, struct genl_info *info, void **ehdrp)
{ struct sk_buff *skb;
skb = genlmsg_new(payload, GFP_KERNEL); if (!skb) goto err;
*ehdrp = genlmsg_put_reply(skb, info, ðtool_genl_family, 0, cmd); if (!*ehdrp) goto err_free;
if (dev) { int ret;
ret = ethnl_fill_reply_header(skb, dev, hdr_attrtype); if (ret < 0) goto err_free;
} return skb;
err_free:
nlmsg_free(skb);
err: if (info)
GENL_SET_ERR_MSG(info, "failed to setup reply message"); return NULL;
}
/** * struct ethnl_dump_ctx - context structure for generic dumpit() callback * @ops: request ops of currently processed message type * @req_info: parsed request header of processed request * @reply_data: data needed to compose the reply * @pos_ifindex: saved iteration position - ifindex * * These parameters are kept in struct netlink_callback as context preserved * between iterations. They are initialized by ethnl_default_start() and used * in ethnl_default_dumpit() and ethnl_default_done().
*/ struct ethnl_dump_ctx { conststruct ethnl_request_ops *ops; struct ethnl_req_info *req_info; struct ethnl_reply_data *reply_data; unsignedlong pos_ifindex;
};
/** * struct ethnl_perphy_dump_ctx - context for dumpit() PHY-aware callbacks * @ethnl_ctx: generic ethnl context * @ifindex: For Filtered DUMP requests, the ifindex of the targeted netdev * @pos_phyindex: iterator position for multi-msg DUMP
*/ struct ethnl_perphy_dump_ctx { struct ethnl_dump_ctx ethnl_ctx; unsignedint ifindex; unsignedlong pos_phyindex;
};
/** * ethnl_default_parse() - Parse request message * @req_info: pointer to structure to put data into * @info: genl_info from the request * @request_ops: struct request_ops for request type * @require_dev: fail if no device identified in header * * Parse universal request header and call request specific ->parse_request() * callback (if defined) to parse the rest of the message. * * Return: 0 on success or negative error code
*/ staticint ethnl_default_parse(struct ethnl_req_info *req_info, conststruct genl_info *info, conststruct ethnl_request_ops *request_ops, bool require_dev)
{ struct nlattr **tb = info->attrs; int ret;
ret = ethnl_parse_header_dev_get(req_info, tb[request_ops->hdr_attr],
genl_info_net(info), info->extack,
require_dev); if (ret < 0) return ret;
if (request_ops->parse_request) {
ret = request_ops->parse_request(req_info, tb, info->extack); if (ret < 0) goto err_dev;
}
/** * ethnl_init_reply_data() - Initialize reply data for GET request * @reply_data: pointer to embedded struct ethnl_reply_data * @ops: instance of struct ethnl_request_ops describing the layout * @dev: network device to initialize the reply for * * Fills the reply data part with zeros and sets the dev member. Must be called * before calling the ->fill_reply() callback (for each iteration when handling * dump requests).
*/ staticvoid ethnl_init_reply_data(struct ethnl_reply_data *reply_data, conststruct ethnl_request_ops *ops, struct net_device *dev)
{
memset(reply_data, 0, ops->reply_data_size);
reply_data->dev = dev;
}
/* default ->doit() handler for GET type requests */ staticint ethnl_default_doit(struct sk_buff *skb, struct genl_info *info)
{ struct ethnl_reply_data *reply_data = NULL; struct ethnl_req_info *req_info = NULL; const u8 cmd = info->genlhdr->cmd; conststruct ethnl_request_ops *ops; int hdr_len, reply_len; struct sk_buff *rskb; void *reply_payload; int ret;
ops = ethnl_default_requests[cmd]; if (WARN_ONCE(!ops, "cmd %u has no ethnl_request_ops\n", cmd)) return -EOPNOTSUPP; if (GENL_REQ_ATTR_CHECK(info, ops->hdr_attr)) return -EINVAL;
req_info = kzalloc(ops->req_info_size, GFP_KERNEL); if (!req_info) return -ENOMEM;
reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL); if (!reply_data) {
kfree(req_info); return -ENOMEM;
}
ret = ethnl_default_parse(req_info, info, ops, !ops->allow_nodev_do); if (ret < 0) goto err_free;
ethnl_init_reply_data(reply_data, ops, req_info->dev);
rtnl_lock(); if (req_info->dev)
netdev_lock_ops(req_info->dev);
ret = ops->prepare_data(req_info, reply_data, info); if (req_info->dev)
netdev_unlock_ops(req_info->dev);
rtnl_unlock(); if (ret < 0) goto err_dev;
ret = ops->reply_size(req_info, reply_data); if (ret < 0) goto err_cleanup;
reply_len = ret;
ret = -ENOMEM;
rskb = ethnl_reply_init(reply_len + ethnl_reply_header_size(),
req_info->dev, ops->reply_cmd,
ops->hdr_attr, info, &reply_payload); if (!rskb) goto err_cleanup;
hdr_len = rskb->len;
ret = ops->fill_reply(rskb, req_info, reply_data); if (ret < 0) goto err_msg;
WARN_ONCE(rskb->len - hdr_len > reply_len, "ethnl cmd %d: calculated reply length %d, but consumed %d\n",
cmd, reply_len, rskb->len - hdr_len); if (ops->cleanup_data)
ops->cleanup_data(reply_data);
ghdr = nlmsg_data(cb->nlh);
ops = ethnl_default_requests[ghdr->cmd]; if (WARN_ONCE(!ops, "cmd %u has no ethnl_request_ops\n", ghdr->cmd)) return -EOPNOTSUPP;
req_info = kzalloc(ops->req_info_size, GFP_KERNEL); if (!req_info) return -ENOMEM;
reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL); if (!reply_data) {
ret = -ENOMEM; goto free_req_info;
}
ret = ethnl_default_parse(req_info, &info->info, ops, false); if (ret < 0) goto free_reply_data; if (req_info->dev) { /* We ignore device specification in dump requests but as the * same parser as for non-dump (doit) requests is used, it * would take reference to the device if it finds one
*/
netdev_put(req_info->dev, &req_info->dev_tracker);
req_info->dev = NULL;
}
ghdr = nlmsg_data(cb->nlh);
ops = ethnl_default_requests[ghdr->cmd]; if (WARN_ONCE(!ops, "cmd %u has no ethnl_request_ops\n", ghdr->cmd)) return -EOPNOTSUPP;
req_info = kzalloc(ops->req_info_size, GFP_KERNEL); if (!req_info) return -ENOMEM;
reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL); if (!reply_data) {
ret = -ENOMEM; goto free_req_info;
}
/* Unlike per-dev dump, don't ignore dev. The dump handler * will notice it and dump PHYs from given dev. We only keep track of * the dev's ifindex, .dumpit() will grab and release the netdev itself.
*/
ret = ethnl_default_parse(req_info, &info->info, ops, false); if (ret < 0) goto free_reply_data; if (req_info->dev) {
phy_ctx->ifindex = req_info->dev->ifindex;
netdev_put(req_info->dev, &req_info->dev_tracker);
req_info->dev = NULL;
}
/* We can re-use the original dump_one as ->prepare_data in * commands use ethnl_req_get_phydev(), which gets the PHY from * the req_info->phy_index
*/
ret = ethnl_default_dump_one(skb, dev, ethnl_ctx, info); if (ret) return ret;
}
ctx->pos_phyindex = 0;
return 0;
}
staticint ethnl_perphy_dump_all_dev(struct sk_buff *skb, struct ethnl_perphy_dump_ctx *ctx, conststruct genl_info *info)
{ struct ethnl_dump_ctx *ethnl_ctx = &ctx->ethnl_ctx; struct net *net = sock_net(skb->sk);
netdevice_tracker dev_tracker; struct net_device *dev; int ret = 0;
/* per-PHY commands use ethnl_req_get_phydev(), which needs the * net_device in the req_info
*/
ethnl_ctx->req_info->dev = dev;
ret = ethnl_perphy_dump_one_dev(skb, ctx, info);
ops = ethnl_default_requests[cmd]; if (WARN_ONCE(!ops, "cmd %u has no ethnl_request_ops\n", cmd)) return -EOPNOTSUPP; if (GENL_REQ_ATTR_CHECK(info, ops->hdr_attr)) return -EINVAL;
req_info = kzalloc(ops->req_info_size, GFP_KERNEL); if (!req_info) return -ENOMEM;
ret = ethnl_default_parse(req_info, info, ops, true); if (ret < 0) goto out_free_req;
if (ops->set_validate) {
ret = ops->set_validate(req_info, info); /* 0 means nothing to do */ if (ret <= 0) goto out_dev;
}
dev = req_info->dev;
rtnl_lock();
netdev_lock_ops(dev);
dev->cfg_pending = kmemdup(dev->cfg, sizeof(*dev->cfg),
GFP_KERNEL_ACCOUNT); if (!dev->cfg_pending) {
ret = -ENOMEM; goto out_tie_cfg;
}
ret = ethnl_ops_begin(dev); if (ret < 0) goto out_free_cfg;
ret = ops->set(req_info, info); if (ret < 0) goto out_ops;
swap(dev->cfg, dev->cfg_pending); if (!ret) goto out_ops;
ethnl_notify(dev, ops->set_ntf_cmd, req_info);
dev = netdev_notifier_info_to_dev(info);
extack = netdev_notifier_info_to_extack(info);
switch (event) { case NETDEV_FEAT_CHANGE:
ethnl_notify_features(ptr); break; case NETDEV_PRE_UP: if (dev->ethtool->module_fw_flash_in_progress) {
NL_SET_ERR_MSG(extack, "Can't set port up while flashing module firmware"); return NOTIFY_BAD;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.