RPC: Add rpc support to update firmware.
Modified the above files to add rpc support to update NVMe firmware. Currently, the path parameter must be local to the RPC Server. Change-Id: I2b14e37792a2f0a5759e8b13e21137e7f346e58e Signed-off-by: Isaac Otsiabah <iotsiabah@us.fujitsu.com> Reviewed-on: https://review.gerrithub.io/369083 Reviewed-by: Paul von Stamwitz <pvonstamwitz@us.fujitsu.com> Tested-by: SPDK Automated Test System <sys_sgsw@intel.com> Reviewed-by: Daniel Verkamp <daniel.verkamp@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com>
This commit is contained in:
parent
872fdc8e41
commit
ce18c1f121
@ -1355,4 +1355,32 @@ bdev_nvme_get_spdk_running_config(FILE *fp)
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
struct spdk_nvme_ctrlr *
|
||||
spdk_bdev_nvme_get_ctrlr(void *bdev_)
|
||||
{
|
||||
struct nvme_bdev *btmp;
|
||||
struct spdk_bdev *bdev = bdev_;
|
||||
struct nvme_bdev *nbdev;
|
||||
|
||||
if (!bdev || !bdev->ctxt) {
|
||||
return NULL;
|
||||
}
|
||||
nbdev = (struct nvme_bdev *)bdev->ctxt;
|
||||
|
||||
/*
|
||||
* Make sure nbdev is NVMe bdev
|
||||
*/
|
||||
TAILQ_FOREACH(btmp, &g_nvme_bdevs, link) {
|
||||
if (btmp == nbdev) {
|
||||
if (nbdev->nvme_ctrlr) {
|
||||
return nbdev->nvme_ctrlr->ctrlr;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SPDK_LOG_REGISTER_TRACE_FLAG("bdev_nvme", SPDK_TRACE_BDEV_NVME)
|
||||
|
@ -43,5 +43,6 @@
|
||||
int spdk_bdev_nvme_create(struct spdk_nvme_transport_id *trid,
|
||||
const char *base_name,
|
||||
const char **names, size_t *count);
|
||||
struct spdk_nvme_ctrlr *spdk_bdev_nvme_get_ctrlr(void *bdev);
|
||||
|
||||
#endif // SPDK_BDEV_NVME_H
|
||||
|
@ -40,6 +40,14 @@
|
||||
#include "spdk/util.h"
|
||||
|
||||
#include "spdk_internal/log.h"
|
||||
#include "spdk_internal/bdev.h"
|
||||
|
||||
struct open_descriptors {
|
||||
void *desc;
|
||||
struct spdk_bdev *bdev;
|
||||
TAILQ_ENTRY(open_descriptors) tqlst;
|
||||
};
|
||||
typedef TAILQ_HEAD(, open_descriptors) open_descriptors_t;
|
||||
|
||||
struct rpc_construct_nvme {
|
||||
char *name;
|
||||
@ -148,3 +156,345 @@ invalid:
|
||||
free_rpc_construct_nvme(&req);
|
||||
}
|
||||
SPDK_RPC_REGISTER("construct_nvme_bdev", spdk_rpc_construct_nvme_bdev)
|
||||
|
||||
struct rpc_apply_firmware {
|
||||
char *filename;
|
||||
char *bdev_name;
|
||||
};
|
||||
|
||||
static void
|
||||
free_rpc_apply_firmware(struct rpc_apply_firmware *req)
|
||||
{
|
||||
free(req->filename);
|
||||
free(req->bdev_name);
|
||||
}
|
||||
|
||||
static const struct spdk_json_object_decoder rpc_apply_firmware_decoders[] = {
|
||||
{"filename", offsetof(struct rpc_apply_firmware, filename), spdk_json_decode_string},
|
||||
{"bdev_name", offsetof(struct rpc_apply_firmware, bdev_name), spdk_json_decode_string},
|
||||
};
|
||||
|
||||
struct firmware_update_info {
|
||||
void *fw_image;
|
||||
void *p;
|
||||
unsigned int size;
|
||||
unsigned int size_remaining;
|
||||
unsigned int offset;
|
||||
unsigned int transfer;
|
||||
|
||||
void *desc;
|
||||
struct spdk_io_channel *ch;
|
||||
struct spdk_jsonrpc_request *request;
|
||||
struct spdk_nvme_ctrlr *ctrlr;
|
||||
open_descriptors_t desc_head;
|
||||
struct rpc_apply_firmware *req;
|
||||
};
|
||||
|
||||
static void
|
||||
apply_firmware_cleanup(void *cb_arg)
|
||||
{
|
||||
struct open_descriptors *opt;
|
||||
struct firmware_update_info *firm_ctx = cb_arg;
|
||||
|
||||
if (!firm_ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (firm_ctx->fw_image) {
|
||||
spdk_dma_free(firm_ctx->fw_image);
|
||||
}
|
||||
|
||||
if (firm_ctx->req) {
|
||||
free_rpc_apply_firmware(firm_ctx->req);
|
||||
free(firm_ctx->req);
|
||||
}
|
||||
TAILQ_FOREACH(opt, &firm_ctx->desc_head, tqlst) {
|
||||
spdk_bdev_close(opt->desc);
|
||||
free(opt);
|
||||
}
|
||||
free(firm_ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
apply_firmware_complete_reset(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
|
||||
{
|
||||
int rc;
|
||||
struct spdk_json_write_ctx *w;
|
||||
struct firmware_update_info *firm_ctx = cb_arg;
|
||||
|
||||
spdk_bdev_free_io(bdev_io);
|
||||
|
||||
if (!success) {
|
||||
spdk_jsonrpc_send_error_response(firm_ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
||||
"firmware commit failed.");
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((rc = spdk_nvme_ctrlr_reset(firm_ctx->ctrlr)) != 0) {
|
||||
spdk_jsonrpc_send_error_response(firm_ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
||||
"Controller reset failed.");
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(w = spdk_jsonrpc_begin_result(firm_ctx->request))) {
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
spdk_json_write_string(w, "firmware commit succeeded. Controller reset in progress.");
|
||||
spdk_jsonrpc_end_result(firm_ctx->request, w);
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
apply_firmware_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
|
||||
{
|
||||
struct spdk_nvme_cmd *cmd;
|
||||
struct spdk_nvme_fw_commit fw_commit;
|
||||
int slot = 0;
|
||||
int rc;
|
||||
struct firmware_update_info *firm_ctx = cb_arg;
|
||||
enum spdk_nvme_fw_commit_action commit_action = SPDK_NVME_FW_COMMIT_REPLACE_AND_ENABLE_IMG;
|
||||
|
||||
if (!success) {
|
||||
spdk_jsonrpc_send_error_response(firm_ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
||||
"firmware download failed .");
|
||||
spdk_bdev_free_io(bdev_io);
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
firm_ctx->p += firm_ctx->transfer;
|
||||
firm_ctx->offset += firm_ctx->transfer;
|
||||
firm_ctx->size_remaining -= firm_ctx->transfer;
|
||||
|
||||
cmd = malloc(sizeof(struct spdk_nvme_cmd));
|
||||
if (!cmd) {
|
||||
spdk_jsonrpc_send_error_response(firm_ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
||||
"malloc failed.");
|
||||
spdk_bdev_free_io(bdev_io);
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
memset(cmd, 0, sizeof(struct spdk_nvme_cmd));
|
||||
|
||||
switch (firm_ctx->size_remaining) {
|
||||
case 0:
|
||||
/* firmware download completed. Commit firmware */
|
||||
memset(&fw_commit, 0, sizeof(struct spdk_nvme_fw_commit));
|
||||
fw_commit.fs = slot;
|
||||
fw_commit.ca = commit_action;
|
||||
|
||||
cmd->opc = SPDK_NVME_OPC_FIRMWARE_COMMIT;
|
||||
memcpy(&cmd->cdw10, &fw_commit, sizeof(uint32_t));
|
||||
rc = spdk_bdev_nvme_admin_passthru(firm_ctx->desc, firm_ctx->ch, cmd, NULL, 0,
|
||||
apply_firmware_complete_reset, firm_ctx);
|
||||
if (rc) {
|
||||
spdk_jsonrpc_send_error_response(firm_ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
||||
"firmware commit failed.");
|
||||
spdk_bdev_free_io(bdev_io);
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
firm_ctx->transfer = spdk_min(firm_ctx->size_remaining, 4096);
|
||||
cmd->opc = SPDK_NVME_OPC_FIRMWARE_IMAGE_DOWNLOAD;
|
||||
|
||||
cmd->cdw10 = (firm_ctx->transfer >> 2) - 1;
|
||||
cmd->cdw11 = firm_ctx->offset >> 2;
|
||||
rc = spdk_bdev_nvme_admin_passthru(firm_ctx->desc, firm_ctx->ch, cmd, firm_ctx->p,
|
||||
firm_ctx->transfer, apply_firmware_complete, firm_ctx);
|
||||
if (rc) {
|
||||
spdk_jsonrpc_send_error_response(firm_ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
||||
"firmware download failed.");
|
||||
spdk_bdev_free_io(bdev_io);
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
spdk_rpc_apply_nvme_firmware(struct spdk_jsonrpc_request *request,
|
||||
const struct spdk_json_val *params)
|
||||
{
|
||||
int rc;
|
||||
int fd = -1;
|
||||
struct stat fw_stat;
|
||||
struct spdk_nvme_ctrlr *ctrlr;
|
||||
char msg[1024];
|
||||
struct spdk_bdev *bdev;
|
||||
struct spdk_bdev *bdev2;
|
||||
struct open_descriptors *opt;
|
||||
struct spdk_bdev_desc *desc;
|
||||
struct spdk_nvme_cmd *cmd;
|
||||
struct firmware_update_info *firm_ctx;
|
||||
|
||||
firm_ctx = malloc(sizeof(struct firmware_update_info));
|
||||
if (!firm_ctx) {
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
||||
"Memory allocation error.");
|
||||
return;
|
||||
}
|
||||
firm_ctx->fw_image = NULL;
|
||||
TAILQ_INIT(&firm_ctx->desc_head);
|
||||
firm_ctx->request = request;
|
||||
|
||||
firm_ctx->req = malloc(sizeof(struct rpc_apply_firmware));
|
||||
if (!firm_ctx->req) {
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
||||
"Memory allocation error.");
|
||||
free(firm_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (spdk_json_decode_object(params, rpc_apply_firmware_decoders,
|
||||
SPDK_COUNTOF(rpc_apply_firmware_decoders), firm_ctx->req)) {
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
||||
"spdk_json_decode_object failed.");
|
||||
free(firm_ctx->req);
|
||||
free(firm_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((bdev = spdk_bdev_get_by_name(firm_ctx->req->bdev_name)) == NULL) {
|
||||
snprintf(msg, sizeof(msg), "bdev %s were not found", firm_ctx->req->bdev_name);
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, msg);
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ctrlr = spdk_bdev_nvme_get_ctrlr(bdev)) == NULL) {
|
||||
snprintf(msg, sizeof(msg), "Controller information for %s were not found.",
|
||||
firm_ctx->req->bdev_name);
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, msg);
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
firm_ctx->ctrlr = ctrlr;
|
||||
|
||||
for (bdev2 = spdk_bdev_first(); bdev2; bdev2 = spdk_bdev_next(bdev2)) {
|
||||
|
||||
if (spdk_bdev_nvme_get_ctrlr(bdev2) != ctrlr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((rc = spdk_bdev_open(bdev2, true, NULL, NULL, &desc)) != 0) {
|
||||
snprintf(msg, sizeof(msg), "Device %s is in use.", firm_ctx->req->bdev_name);
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, msg);
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
} else {
|
||||
if (!(opt = malloc(sizeof(struct open_descriptors)))) {
|
||||
snprintf(msg, sizeof(msg), "Memory allocation error.");
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, msg);
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
opt->desc = desc;
|
||||
opt->bdev = bdev;
|
||||
TAILQ_INSERT_TAIL(&firm_ctx->desc_head, opt, tqlst);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* find a descriptor associated with our bdev
|
||||
*/
|
||||
firm_ctx->desc = NULL;
|
||||
TAILQ_FOREACH(opt, &firm_ctx->desc_head, tqlst) {
|
||||
if (opt->bdev == bdev) {
|
||||
firm_ctx->desc = opt->desc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!firm_ctx->desc) {
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
||||
"No descriptor were found.");
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
firm_ctx->ch = spdk_bdev_get_io_channel(firm_ctx->desc);
|
||||
if (!firm_ctx->ch) {
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
||||
"No channels were found.");
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
fd = open(firm_ctx->req->filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "open file failed.");
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = fstat(fd, &fw_stat);
|
||||
if (rc < 0) {
|
||||
close(fd);
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "fstat failed.");
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
firm_ctx->size = fw_stat.st_size;
|
||||
if (fw_stat.st_size % 4) {
|
||||
close(fd);
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
||||
"Firmware image size is not multiple of 4.");
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
firm_ctx->fw_image = spdk_dma_zmalloc(firm_ctx->size, 4096, NULL);
|
||||
if (!firm_ctx->fw_image) {
|
||||
close(fd);
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
||||
"Memory allocation error.");
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
firm_ctx->p = firm_ctx->fw_image;
|
||||
|
||||
if (read(fd, firm_ctx->p, firm_ctx->size) != ((ssize_t)(firm_ctx->size))) {
|
||||
close(fd);
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
||||
"Read firmware image failed!");
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
firm_ctx->offset = 0;
|
||||
firm_ctx->size_remaining = firm_ctx->size;
|
||||
firm_ctx->transfer = spdk_min(firm_ctx->size_remaining, 4096);
|
||||
|
||||
cmd = malloc(sizeof(struct spdk_nvme_cmd));
|
||||
if (!cmd) {
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
||||
"Memory allocation error.");
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
memset(cmd, 0, sizeof(struct spdk_nvme_cmd));
|
||||
cmd->opc = SPDK_NVME_OPC_FIRMWARE_IMAGE_DOWNLOAD;
|
||||
|
||||
cmd->cdw10 = (firm_ctx->transfer >> 2) - 1;
|
||||
cmd->cdw11 = firm_ctx->offset >> 2;
|
||||
|
||||
rc = spdk_bdev_nvme_admin_passthru(firm_ctx->desc, firm_ctx->ch, cmd, firm_ctx->p,
|
||||
firm_ctx->transfer, apply_firmware_complete, firm_ctx);
|
||||
if (rc) {
|
||||
free(cmd);
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
||||
"Read firmware image failed!");
|
||||
apply_firmware_cleanup(firm_ctx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
SPDK_RPC_REGISTER("apply_nvme_firmware", spdk_rpc_apply_nvme_firmware)
|
||||
|
@ -493,6 +493,19 @@ def get_interfaces(args):
|
||||
p = subparsers.add_parser('get_interfaces', help='Display current interface list')
|
||||
p.set_defaults(func=get_interfaces)
|
||||
|
||||
def apply_firmware(args):
|
||||
|
||||
params = {
|
||||
'filename': args.filename,
|
||||
'bdev_name': args.bdev_name,
|
||||
}
|
||||
|
||||
print_dict(jsonrpc_call('apply_nvme_firmware', params))
|
||||
|
||||
p = subparsers.add_parser('apply_firmware', help='Download and commit firmware to NVMe device')
|
||||
p.add_argument('filename', help='filename of the firmware to download')
|
||||
p.add_argument('bdev_name', help='name of the NVMe device')
|
||||
p.set_defaults(func=apply_firmware)
|
||||
|
||||
def get_bdevs(args):
|
||||
params = {}
|
||||
|
Loading…
Reference in New Issue
Block a user