mirror of
https://git.proxmox.com/git/mirror_iproute2
synced 2025-08-08 12:58:33 +00:00
devlink: implement flash status monitoring
Listen to status notifications coming from kernel during flashing and put them on stdout to inform user about the status. Signed-off-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David Ahern <dsahern@gmail.com>
This commit is contained in:
parent
853be43f9e
commit
9b13cddfe2
@ -25,6 +25,7 @@
|
|||||||
#include <linux/devlink.h>
|
#include <linux/devlink.h>
|
||||||
#include <libmnl/libmnl.h>
|
#include <libmnl/libmnl.h>
|
||||||
#include <netinet/ether.h>
|
#include <netinet/ether.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include "SNAPSHOT.h"
|
#include "SNAPSHOT.h"
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
@ -96,6 +97,18 @@ pr_out_sp(unsigned int num, const char *fmt, ...)
|
|||||||
g_new_line_count = 0; \
|
g_new_line_count = 0; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __attribute__((format(printf, 1, 2)))
|
||||||
|
pr_out_tty(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
if (!isatty(STDOUT_FILENO))
|
||||||
|
return;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vprintf(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
static void __pr_out_indent_inc(void)
|
static void __pr_out_indent_inc(void)
|
||||||
{
|
{
|
||||||
if (g_indent_level + INDENT_STR_STEP > INDENT_STR_MAXLEN)
|
if (g_indent_level + INDENT_STR_STEP > INDENT_STR_MAXLEN)
|
||||||
@ -135,9 +148,8 @@ static int _mnlg_socket_recv_run(struct mnlg_socket *nlg,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _mnlg_socket_sndrcv(struct mnlg_socket *nlg,
|
static int _mnlg_socket_send(struct mnlg_socket *nlg,
|
||||||
const struct nlmsghdr *nlh,
|
const struct nlmsghdr *nlh)
|
||||||
mnl_cb_t data_cb, void *data)
|
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
@ -146,6 +158,18 @@ static int _mnlg_socket_sndrcv(struct mnlg_socket *nlg,
|
|||||||
pr_err("Failed to call mnlg_socket_send\n");
|
pr_err("Failed to call mnlg_socket_send\n");
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _mnlg_socket_sndrcv(struct mnlg_socket *nlg,
|
||||||
|
const struct nlmsghdr *nlh,
|
||||||
|
mnl_cb_t data_cb, void *data)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = _mnlg_socket_send(nlg, nlh);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
return _mnlg_socket_recv_run(nlg, data_cb, data);
|
return _mnlg_socket_recv_run(nlg, data_cb, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2855,9 +2879,151 @@ static void cmd_dev_flash_help(void)
|
|||||||
pr_err("Usage: devlink dev flash DEV file PATH [ component NAME ]\n");
|
pr_err("Usage: devlink dev flash DEV file PATH [ component NAME ]\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct cmd_dev_flash_status_ctx {
|
||||||
|
struct dl *dl;
|
||||||
|
char *last_msg;
|
||||||
|
char *last_component;
|
||||||
|
uint8_t not_first:1,
|
||||||
|
last_pc:1,
|
||||||
|
received_end:1,
|
||||||
|
flash_done:1;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int nullstrcmp(const char *str1, const char *str2)
|
||||||
|
{
|
||||||
|
if (str1 && str2)
|
||||||
|
return strcmp(str1, str2);
|
||||||
|
if (!str1 && !str2)
|
||||||
|
return 0;
|
||||||
|
return str1 ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_dev_flash_status_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
|
{
|
||||||
|
struct cmd_dev_flash_status_ctx *ctx = data;
|
||||||
|
struct dl_opts *opts = &ctx->dl->opts;
|
||||||
|
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
|
||||||
|
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
|
||||||
|
const char *component = NULL;
|
||||||
|
uint64_t done = 0, total = 0;
|
||||||
|
const char *msg = NULL;
|
||||||
|
const char *bus_name;
|
||||||
|
const char *dev_name;
|
||||||
|
|
||||||
|
if (genl->cmd != DEVLINK_CMD_FLASH_UPDATE_STATUS &&
|
||||||
|
genl->cmd != DEVLINK_CMD_FLASH_UPDATE_END)
|
||||||
|
return MNL_CB_STOP;
|
||||||
|
|
||||||
|
mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
|
||||||
|
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
|
||||||
|
return MNL_CB_ERROR;
|
||||||
|
bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]);
|
||||||
|
dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]);
|
||||||
|
if (strcmp(bus_name, opts->bus_name) ||
|
||||||
|
strcmp(dev_name, opts->dev_name))
|
||||||
|
return MNL_CB_ERROR;
|
||||||
|
|
||||||
|
if (genl->cmd == DEVLINK_CMD_FLASH_UPDATE_END && ctx->not_first) {
|
||||||
|
pr_out("\n");
|
||||||
|
free(ctx->last_msg);
|
||||||
|
free(ctx->last_component);
|
||||||
|
ctx->received_end = 1;
|
||||||
|
return MNL_CB_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG])
|
||||||
|
msg = mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG]);
|
||||||
|
if (tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT])
|
||||||
|
component = mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT]);
|
||||||
|
if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE])
|
||||||
|
done = mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE]);
|
||||||
|
if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL])
|
||||||
|
total = mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL]);
|
||||||
|
|
||||||
|
if (!nullstrcmp(msg, ctx->last_msg) &&
|
||||||
|
!nullstrcmp(component, ctx->last_component) &&
|
||||||
|
ctx->last_pc && ctx->not_first) {
|
||||||
|
pr_out_tty("\b\b\b\b\b"); /* clean percentage */
|
||||||
|
} else {
|
||||||
|
if (ctx->not_first)
|
||||||
|
pr_out("\n");
|
||||||
|
if (component) {
|
||||||
|
pr_out("[%s] ", component);
|
||||||
|
free(ctx->last_component);
|
||||||
|
ctx->last_component = strdup(component);
|
||||||
|
}
|
||||||
|
if (msg) {
|
||||||
|
pr_out("%s", msg);
|
||||||
|
free(ctx->last_msg);
|
||||||
|
ctx->last_msg = strdup(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (total) {
|
||||||
|
pr_out_tty(" %3lu%%", (done * 100) / total);
|
||||||
|
ctx->last_pc = 1;
|
||||||
|
} else {
|
||||||
|
ctx->last_pc = 0;
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
ctx->not_first = 1;
|
||||||
|
|
||||||
|
return MNL_CB_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_dev_flash_fds_process(struct cmd_dev_flash_status_ctx *ctx,
|
||||||
|
struct mnlg_socket *nlg_ntf,
|
||||||
|
int pipe_r)
|
||||||
|
{
|
||||||
|
int nlfd = mnlg_socket_get_fd(nlg_ntf);
|
||||||
|
fd_set fds[3];
|
||||||
|
int fdmax;
|
||||||
|
int i;
|
||||||
|
int err;
|
||||||
|
int err2;
|
||||||
|
|
||||||
|
for (i = 0; i < 3; i++)
|
||||||
|
FD_ZERO(&fds[i]);
|
||||||
|
FD_SET(pipe_r, &fds[0]);
|
||||||
|
fdmax = pipe_r + 1;
|
||||||
|
FD_SET(nlfd, &fds[0]);
|
||||||
|
if (nlfd >= fdmax)
|
||||||
|
fdmax = nlfd + 1;
|
||||||
|
|
||||||
|
while (select(fdmax, &fds[0], &fds[1], &fds[2], NULL) < 0) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
pr_err("select() failed\n");
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
if (FD_ISSET(nlfd, &fds[0])) {
|
||||||
|
err = _mnlg_socket_recv_run(nlg_ntf,
|
||||||
|
cmd_dev_flash_status_cb, ctx);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if (FD_ISSET(pipe_r, &fds[0])) {
|
||||||
|
err = read(pipe_r, &err2, sizeof(err2));
|
||||||
|
if (err == -1) {
|
||||||
|
pr_err("Failed to read pipe\n");
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
if (err2)
|
||||||
|
return err2;
|
||||||
|
ctx->flash_done = 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int cmd_dev_flash(struct dl *dl)
|
static int cmd_dev_flash(struct dl *dl)
|
||||||
{
|
{
|
||||||
|
struct cmd_dev_flash_status_ctx ctx = {.dl = dl,};
|
||||||
|
struct mnlg_socket *nlg_ntf;
|
||||||
struct nlmsghdr *nlh;
|
struct nlmsghdr *nlh;
|
||||||
|
int pipe_r, pipe_w;
|
||||||
|
int pipe_fds[2];
|
||||||
|
pid_t pid;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
|
if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
|
||||||
@ -2873,7 +3039,48 @@ static int cmd_dev_flash(struct dl *dl)
|
|||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
|
nlg_ntf = mnlg_socket_open(DEVLINK_GENL_NAME, DEVLINK_GENL_VERSION);
|
||||||
|
if (!nlg_ntf)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = _mnlg_socket_group_add(nlg_ntf, DEVLINK_GENL_MCGRP_CONFIG_NAME);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = pipe(pipe_fds);
|
||||||
|
if (err == -1)
|
||||||
|
return -errno;
|
||||||
|
pipe_r = pipe_fds[0];
|
||||||
|
pipe_w = pipe_fds[1];
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
if (pid == -1) {
|
||||||
|
close(pipe_r);
|
||||||
|
close(pipe_w);
|
||||||
|
return -errno;
|
||||||
|
} else if (!pid) {
|
||||||
|
/* In child, just execute the flash and pass returned
|
||||||
|
* value through pipe once it is done.
|
||||||
|
*/
|
||||||
|
close(pipe_r);
|
||||||
|
err = _mnlg_socket_send(dl->nlg, nlh);
|
||||||
|
write(pipe_w, &err, sizeof(err));
|
||||||
|
close(pipe_w);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
close(pipe_w);
|
||||||
|
|
||||||
|
do {
|
||||||
|
err = cmd_dev_flash_fds_process(&ctx, nlg_ntf, pipe_r);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
} while (!ctx.flash_done || (ctx.not_first && !ctx.received_end));
|
||||||
|
|
||||||
|
err = _mnlg_socket_recv_run(dl->nlg, NULL, NULL);
|
||||||
|
out:
|
||||||
|
close(pipe_r);
|
||||||
|
mnlg_socket_close(nlg_ntf);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cmd_dev(struct dl *dl)
|
static int cmd_dev(struct dl *dl)
|
||||||
|
@ -320,3 +320,8 @@ void mnlg_socket_close(struct mnlg_socket *nlg)
|
|||||||
free(nlg->buf);
|
free(nlg->buf);
|
||||||
free(nlg);
|
free(nlg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mnlg_socket_get_fd(struct mnlg_socket *nlg)
|
||||||
|
{
|
||||||
|
return mnl_socket_get_fd(nlg->nl);
|
||||||
|
}
|
||||||
|
@ -23,5 +23,6 @@ int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data);
|
|||||||
int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name);
|
int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name);
|
||||||
struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version);
|
struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version);
|
||||||
void mnlg_socket_close(struct mnlg_socket *nlg);
|
void mnlg_socket_close(struct mnlg_socket *nlg);
|
||||||
|
int mnlg_socket_get_fd(struct mnlg_socket *nlg);
|
||||||
|
|
||||||
#endif /* _MNLG_H_ */
|
#endif /* _MNLG_H_ */
|
||||||
|
@ -244,6 +244,17 @@ Sets the parameter internal_error_reset of specified devlink device to true.
|
|||||||
devlink dev reload pci/0000:01:00.0
|
devlink dev reload pci/0000:01:00.0
|
||||||
.RS 4
|
.RS 4
|
||||||
Performs hot reload of specified devlink device.
|
Performs hot reload of specified devlink device.
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
devlink dev flash pci/0000:01:00.0 file firmware.bin
|
||||||
|
.RS 4
|
||||||
|
Flashes the specified devlink device with provided firmware file name. If the driver supports it, user gets updates about the flash status. For example:
|
||||||
|
.br
|
||||||
|
Preparing to flash
|
||||||
|
.br
|
||||||
|
Flashing 100%
|
||||||
|
.br
|
||||||
|
Flashing done
|
||||||
|
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
.BR devlink (8),
|
.BR devlink (8),
|
||||||
|
Loading…
Reference in New Issue
Block a user