diff options
author | Alex Vesker <valex@mellanox.com> | 2018-07-12 08:13:14 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-07-12 20:37:13 -0400 |
commit | 4e54795a27f56102649f121a34b8445e42f79ccd (patch) | |
tree | cbf6cf97506f1ee17fd9aabfbd41ac8210cda9a0 /net/core/devlink.c | |
parent | 866319bb9437614407ca36f8b16f89ab77a6a831 (diff) |
devlink: Add support for region snapshot read command
Add support for DEVLINK_CMD_REGION_READ_GET used for both reading
and dumping region data. Read allows reading from a region specific
address for given length. Dump allows reading the full region.
If only snapshot ID is provided a snapshot dump will be done.
If snapshot ID, Address and Length are provided a snapshot read
will done.
This is used for both snapshot access and will be used in the same
way to access current data on the region.
Signed-off-by: Alex Vesker <valex@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core/devlink.c')
-rw-r--r-- | net/core/devlink.c | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/net/core/devlink.c b/net/core/devlink.c index fc0836371a71..e5118dba6bb4 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c | |||
@@ -3388,6 +3388,181 @@ static int devlink_nl_cmd_region_del(struct sk_buff *skb, | |||
3388 | return 0; | 3388 | return 0; |
3389 | } | 3389 | } |
3390 | 3390 | ||
3391 | static int devlink_nl_cmd_region_read_chunk_fill(struct sk_buff *msg, | ||
3392 | struct devlink *devlink, | ||
3393 | u8 *chunk, u32 chunk_size, | ||
3394 | u64 addr) | ||
3395 | { | ||
3396 | struct nlattr *chunk_attr; | ||
3397 | int err; | ||
3398 | |||
3399 | chunk_attr = nla_nest_start(msg, DEVLINK_ATTR_REGION_CHUNK); | ||
3400 | if (!chunk_attr) | ||
3401 | return -EINVAL; | ||
3402 | |||
3403 | err = nla_put(msg, DEVLINK_ATTR_REGION_CHUNK_DATA, chunk_size, chunk); | ||
3404 | if (err) | ||
3405 | goto nla_put_failure; | ||
3406 | |||
3407 | err = nla_put_u64_64bit(msg, DEVLINK_ATTR_REGION_CHUNK_ADDR, addr, | ||
3408 | DEVLINK_ATTR_PAD); | ||
3409 | if (err) | ||
3410 | goto nla_put_failure; | ||
3411 | |||
3412 | nla_nest_end(msg, chunk_attr); | ||
3413 | return 0; | ||
3414 | |||
3415 | nla_put_failure: | ||
3416 | nla_nest_cancel(msg, chunk_attr); | ||
3417 | return err; | ||
3418 | } | ||
3419 | |||
3420 | #define DEVLINK_REGION_READ_CHUNK_SIZE 256 | ||
3421 | |||
3422 | static int devlink_nl_region_read_snapshot_fill(struct sk_buff *skb, | ||
3423 | struct devlink *devlink, | ||
3424 | struct devlink_region *region, | ||
3425 | struct nlattr **attrs, | ||
3426 | u64 start_offset, | ||
3427 | u64 end_offset, | ||
3428 | bool dump, | ||
3429 | u64 *new_offset) | ||
3430 | { | ||
3431 | struct devlink_snapshot *snapshot; | ||
3432 | u64 curr_offset = start_offset; | ||
3433 | u32 snapshot_id; | ||
3434 | int err = 0; | ||
3435 | |||
3436 | *new_offset = start_offset; | ||
3437 | |||
3438 | snapshot_id = nla_get_u32(attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]); | ||
3439 | snapshot = devlink_region_snapshot_get_by_id(region, snapshot_id); | ||
3440 | if (!snapshot) | ||
3441 | return -EINVAL; | ||
3442 | |||
3443 | if (end_offset > snapshot->data_len || dump) | ||
3444 | end_offset = snapshot->data_len; | ||
3445 | |||
3446 | while (curr_offset < end_offset) { | ||
3447 | u32 data_size; | ||
3448 | u8 *data; | ||
3449 | |||
3450 | if (end_offset - curr_offset < DEVLINK_REGION_READ_CHUNK_SIZE) | ||
3451 | data_size = end_offset - curr_offset; | ||
3452 | else | ||
3453 | data_size = DEVLINK_REGION_READ_CHUNK_SIZE; | ||
3454 | |||
3455 | data = &snapshot->data[curr_offset]; | ||
3456 | err = devlink_nl_cmd_region_read_chunk_fill(skb, devlink, | ||
3457 | data, data_size, | ||
3458 | curr_offset); | ||
3459 | if (err) | ||
3460 | break; | ||
3461 | |||
3462 | curr_offset += data_size; | ||
3463 | } | ||
3464 | *new_offset = curr_offset; | ||
3465 | |||
3466 | return err; | ||
3467 | } | ||
3468 | |||
3469 | static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, | ||
3470 | struct netlink_callback *cb) | ||
3471 | { | ||
3472 | u64 ret_offset, start_offset, end_offset = 0; | ||
3473 | struct nlattr *attrs[DEVLINK_ATTR_MAX + 1]; | ||
3474 | const struct genl_ops *ops = cb->data; | ||
3475 | struct devlink_region *region; | ||
3476 | struct nlattr *chunks_attr; | ||
3477 | const char *region_name; | ||
3478 | struct devlink *devlink; | ||
3479 | bool dump = true; | ||
3480 | void *hdr; | ||
3481 | int err; | ||
3482 | |||
3483 | start_offset = *((u64 *)&cb->args[0]); | ||
3484 | |||
3485 | err = nlmsg_parse(cb->nlh, GENL_HDRLEN + devlink_nl_family.hdrsize, | ||
3486 | attrs, DEVLINK_ATTR_MAX, ops->policy, NULL); | ||
3487 | if (err) | ||
3488 | goto out; | ||
3489 | |||
3490 | devlink = devlink_get_from_attrs(sock_net(cb->skb->sk), attrs); | ||
3491 | if (IS_ERR(devlink)) | ||
3492 | goto out; | ||
3493 | |||
3494 | mutex_lock(&devlink_mutex); | ||
3495 | mutex_lock(&devlink->lock); | ||
3496 | |||
3497 | if (!attrs[DEVLINK_ATTR_REGION_NAME] || | ||
3498 | !attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]) | ||
3499 | goto out_unlock; | ||
3500 | |||
3501 | region_name = nla_data(attrs[DEVLINK_ATTR_REGION_NAME]); | ||
3502 | region = devlink_region_get_by_name(devlink, region_name); | ||
3503 | if (!region) | ||
3504 | goto out_unlock; | ||
3505 | |||
3506 | hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, | ||
3507 | &devlink_nl_family, NLM_F_ACK | NLM_F_MULTI, | ||
3508 | DEVLINK_CMD_REGION_READ); | ||
3509 | if (!hdr) | ||
3510 | goto out_unlock; | ||
3511 | |||
3512 | err = devlink_nl_put_handle(skb, devlink); | ||
3513 | if (err) | ||
3514 | goto nla_put_failure; | ||
3515 | |||
3516 | err = nla_put_string(skb, DEVLINK_ATTR_REGION_NAME, region_name); | ||
3517 | if (err) | ||
3518 | goto nla_put_failure; | ||
3519 | |||
3520 | chunks_attr = nla_nest_start(skb, DEVLINK_ATTR_REGION_CHUNKS); | ||
3521 | if (!chunks_attr) | ||
3522 | goto nla_put_failure; | ||
3523 | |||
3524 | if (attrs[DEVLINK_ATTR_REGION_CHUNK_ADDR] && | ||
3525 | attrs[DEVLINK_ATTR_REGION_CHUNK_LEN]) { | ||
3526 | if (!start_offset) | ||
3527 | start_offset = | ||
3528 | nla_get_u64(attrs[DEVLINK_ATTR_REGION_CHUNK_ADDR]); | ||
3529 | |||
3530 | end_offset = nla_get_u64(attrs[DEVLINK_ATTR_REGION_CHUNK_ADDR]); | ||
3531 | end_offset += nla_get_u64(attrs[DEVLINK_ATTR_REGION_CHUNK_LEN]); | ||
3532 | dump = false; | ||
3533 | } | ||
3534 | |||
3535 | err = devlink_nl_region_read_snapshot_fill(skb, devlink, | ||
3536 | region, attrs, | ||
3537 | start_offset, | ||
3538 | end_offset, dump, | ||
3539 | &ret_offset); | ||
3540 | |||
3541 | if (err && err != -EMSGSIZE) | ||
3542 | goto nla_put_failure; | ||
3543 | |||
3544 | /* Check if there was any progress done to prevent infinite loop */ | ||
3545 | if (ret_offset == start_offset) | ||
3546 | goto nla_put_failure; | ||
3547 | |||
3548 | *((u64 *)&cb->args[0]) = ret_offset; | ||
3549 | |||
3550 | nla_nest_end(skb, chunks_attr); | ||
3551 | genlmsg_end(skb, hdr); | ||
3552 | mutex_unlock(&devlink->lock); | ||
3553 | mutex_unlock(&devlink_mutex); | ||
3554 | |||
3555 | return skb->len; | ||
3556 | |||
3557 | nla_put_failure: | ||
3558 | genlmsg_cancel(skb, hdr); | ||
3559 | out_unlock: | ||
3560 | mutex_unlock(&devlink->lock); | ||
3561 | mutex_unlock(&devlink_mutex); | ||
3562 | out: | ||
3563 | return 0; | ||
3564 | } | ||
3565 | |||
3391 | static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { | 3566 | static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { |
3392 | [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING }, | 3567 | [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING }, |
3393 | [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING }, | 3568 | [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING }, |
@@ -3626,6 +3801,13 @@ static const struct genl_ops devlink_nl_ops[] = { | |||
3626 | .flags = GENL_ADMIN_PERM, | 3801 | .flags = GENL_ADMIN_PERM, |
3627 | .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, | 3802 | .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, |
3628 | }, | 3803 | }, |
3804 | { | ||
3805 | .cmd = DEVLINK_CMD_REGION_READ, | ||
3806 | .dumpit = devlink_nl_cmd_region_read_dumpit, | ||
3807 | .policy = devlink_nl_policy, | ||
3808 | .flags = GENL_ADMIN_PERM, | ||
3809 | .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, | ||
3810 | }, | ||
3629 | }; | 3811 | }; |
3630 | 3812 | ||
3631 | static struct genl_family devlink_nl_family __ro_after_init = { | 3813 | static struct genl_family devlink_nl_family __ro_after_init = { |