From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) by dpdk.org (Postfix) with ESMTP id BF23E1B16A for ; Wed, 17 Oct 2018 15:06:07 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga002.jf.intel.com ([10.7.209.21]) by fmsmga107.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Oct 2018 06:06:06 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,392,1534834800"; d="scan'208";a="100979015" Received: from silpixa00399952.ir.intel.com (HELO silpixa00399952.ger.corp.intel.com) ([10.237.223.64]) by orsmga002.jf.intel.com with ESMTP; 17 Oct 2018 06:06:04 -0700 From: David Hunt To: dev@dpdk.org Cc: john.mcnamara@intel.com, stephen@networkplumber.org, lei.a.yao@intel.com, anatoly.burakov@intel.com, David Hunt Date: Wed, 17 Oct 2018 14:05:31 +0100 Message-Id: <20181017130534.56665-8-david.hunt@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181017130534.56665-1-david.hunt@intel.com> References: <20181002084328.57127-1-david.hunt@intel.com> <20181017130534.56665-1-david.hunt@intel.com> Subject: [dpdk-dev] [PATCH v7 07/10] examples/power: add json string handling X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 17 Oct 2018 13:06:08 -0000 Add JSON string handling to vm_power_manager for JSON strings received through the fifo. The format of the JSON strings are detailed in the next patch, the vm_power_manager user guide documentation updates. This patch introduces a new dependency on Jansson, a C library for encoding, decoding and manipulating JSON data. To compile the sample app you now need to have installed libjansson4 and libjansson-dev (these may be named slightly differently depending on your Operating System) Signed-off-by: David Hunt Acked-by: Anatoly Burakov --- examples/vm_power_manager/Makefile | 6 + examples/vm_power_manager/channel_monitor.c | 371 ++++++++++++++++++-- 2 files changed, 352 insertions(+), 25 deletions(-) diff --git a/examples/vm_power_manager/Makefile b/examples/vm_power_manager/Makefile index 13a5205ba..50147c05d 100644 --- a/examples/vm_power_manager/Makefile +++ b/examples/vm_power_manager/Makefile @@ -31,6 +31,12 @@ CFLAGS += $(WERROR_FLAGS) LDLIBS += -lvirt +JANSSON := $(shell pkg-config --exists jansson; echo $$?) +ifeq ($(JANSSON), 0) +LDLIBS += $(shell pkg-config --libs jansson) +CFLAGS += -DUSE_JANSSON +endif + ifeq ($(CONFIG_RTE_BUILD_SHARED_LIB),y) ifeq ($(CONFIG_RTE_LIBRTE_IXGBE_PMD),y) diff --git a/examples/vm_power_manager/channel_monitor.c b/examples/vm_power_manager/channel_monitor.c index 53a4efe45..afb44a069 100644 --- a/examples/vm_power_manager/channel_monitor.c +++ b/examples/vm_power_manager/channel_monitor.c @@ -9,11 +9,18 @@ #include #include #include +#include #include #include #include #include - +#include +#include +#ifdef USE_JANSSON +#include +#else +#pragma message "Jansson dev libs unavailable, not including JSON parsing" +#endif #include #include #include @@ -35,6 +42,8 @@ uint64_t vsi_pkt_count_prev[384]; uint64_t rdtsc_prev[384]; +#define MAX_JSON_STRING_LEN 1024 +char json_data[MAX_JSON_STRING_LEN]; double time_period_ms = 1; static volatile unsigned run_loop = 1; @@ -43,6 +52,234 @@ static unsigned int policy_is_set; static struct epoll_event *global_events_list; static struct policy policies[MAX_CLIENTS]; +#ifdef USE_JANSSON + +union PFID { + struct ether_addr addr; + uint64_t pfid; +}; + +static int +str_to_ether_addr(const char *a, struct ether_addr *ether_addr) +{ + int i; + char *end; + unsigned long o[ETHER_ADDR_LEN]; + + i = 0; + do { + errno = 0; + o[i] = strtoul(a, &end, 16); + if (errno != 0 || end == a || (end[0] != ':' && end[0] != 0)) + return -1; + a = end + 1; + } while (++i != RTE_DIM(o) / sizeof(o[0]) && end[0] != 0); + + /* Junk at the end of line */ + if (end[0] != 0) + return -1; + + /* Support the format XX:XX:XX:XX:XX:XX */ + if (i == ETHER_ADDR_LEN) { + while (i-- != 0) { + if (o[i] > UINT8_MAX) + return -1; + ether_addr->addr_bytes[i] = (uint8_t)o[i]; + } + /* Support the format XXXX:XXXX:XXXX */ + } else if (i == ETHER_ADDR_LEN / 2) { + while (i-- != 0) { + if (o[i] > UINT16_MAX) + return -1; + ether_addr->addr_bytes[i * 2] = + (uint8_t)(o[i] >> 8); + ether_addr->addr_bytes[i * 2 + 1] = + (uint8_t)(o[i] & 0xff); + } + /* unknown format */ + } else + return -1; + + return 0; +} + +static int +set_policy_mac(struct channel_packet *pkt, int idx, char *mac) +{ + union PFID pfid; + int ret; + + /* Use port MAC address as the vfid */ + ret = str_to_ether_addr(mac, &pfid.addr); + + if (ret != 0) { + RTE_LOG(ERR, CHANNEL_MONITOR, + "Invalid mac address received in JSON\n"); + pkt->vfid[idx] = 0; + return -1; + } + + printf("Received MAC Address: %02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":" + "%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 "\n", + pfid.addr.addr_bytes[0], pfid.addr.addr_bytes[1], + pfid.addr.addr_bytes[2], pfid.addr.addr_bytes[3], + pfid.addr.addr_bytes[4], pfid.addr.addr_bytes[5]); + + pkt->vfid[idx] = pfid.pfid; + return 0; +} + + +static int +parse_json_to_pkt(json_t *element, struct channel_packet *pkt) +{ + const char *key; + json_t *value; + int ret; + + memset(pkt, 0, sizeof(struct channel_packet)); + + pkt->nb_mac_to_monitor = 0; + pkt->t_boost_status.tbEnabled = false; + pkt->workload = LOW; + pkt->policy_to_use = TIME; + pkt->command = PKT_POLICY; + pkt->core_type = CORE_TYPE_PHYSICAL; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "policy")) { + /* Recurse in to get the contents of profile */ + ret = parse_json_to_pkt(value, pkt); + if (ret) + return ret; + } else if (!strcmp(key, "instruction")) { + /* Recurse in to get the contents of instruction */ + ret = parse_json_to_pkt(value, pkt); + if (ret) + return ret; + } else if (!strcmp(key, "name")) { + strcpy(pkt->vm_name, json_string_value(value)); + } else if (!strcmp(key, "command")) { + char command[32]; + snprintf(command, 32, "%s", json_string_value(value)); + if (!strcmp(command, "power")) { + pkt->command = CPU_POWER; + } else if (!strcmp(command, "create")) { + pkt->command = PKT_POLICY; + } else if (!strcmp(command, "destroy")) { + pkt->command = PKT_POLICY_REMOVE; + } else { + RTE_LOG(ERR, CHANNEL_MONITOR, + "Invalid command received in JSON\n"); + return -1; + } + } else if (!strcmp(key, "policy_type")) { + char command[32]; + snprintf(command, 32, "%s", json_string_value(value)); + if (!strcmp(command, "TIME")) { + pkt->policy_to_use = TIME; + } else if (!strcmp(command, "TRAFFIC")) { + pkt->policy_to_use = TRAFFIC; + } else if (!strcmp(command, "WORKLOAD")) { + pkt->policy_to_use = WORKLOAD; + } else if (!strcmp(command, "BRANCH_RATIO")) { + pkt->policy_to_use = BRANCH_RATIO; + } else { + RTE_LOG(ERR, CHANNEL_MONITOR, + "Wrong policy_type received in JSON\n"); + return -1; + } + } else if (!strcmp(key, "workload")) { + char command[32]; + snprintf(command, 32, "%s", json_string_value(value)); + if (!strcmp(command, "HIGH")) { + pkt->workload = HIGH; + } else if (!strcmp(command, "MEDIUM")) { + pkt->workload = MEDIUM; + } else if (!strcmp(command, "LOW")) { + pkt->workload = LOW; + } else { + RTE_LOG(ERR, CHANNEL_MONITOR, + "Wrong workload received in JSON\n"); + return -1; + } + } else if (!strcmp(key, "busy_hours")) { + unsigned int i; + size_t size = json_array_size(value); + + for (i = 0; i < size; i++) { + int hour = (int)json_integer_value( + json_array_get(value, i)); + pkt->timer_policy.busy_hours[i] = hour; + } + } else if (!strcmp(key, "quiet_hours")) { + unsigned int i; + size_t size = json_array_size(value); + + for (i = 0; i < size; i++) { + int hour = (int)json_integer_value( + json_array_get(value, i)); + pkt->timer_policy.quiet_hours[i] = hour; + } + } else if (!strcmp(key, "core_list")) { + unsigned int i; + size_t size = json_array_size(value); + + for (i = 0; i < size; i++) { + int core = (int)json_integer_value( + json_array_get(value, i)); + pkt->vcpu_to_control[i] = core; + } + pkt->num_vcpu = size; + } else if (!strcmp(key, "mac_list")) { + unsigned int i; + size_t size = json_array_size(value); + + for (i = 0; i < size; i++) { + char mac[32]; + snprintf(mac, 32, "%s", json_string_value( + json_array_get(value, i))); + set_policy_mac(pkt, i, mac); + } + pkt->nb_mac_to_monitor = size; + } else if (!strcmp(key, "avg_packet_thresh")) { + pkt->traffic_policy.avg_max_packet_thresh = + (uint32_t)json_integer_value(value); + } else if (!strcmp(key, "max_packet_thresh")) { + pkt->traffic_policy.max_max_packet_thresh = + (uint32_t)json_integer_value(value); + } else if (!strcmp(key, "unit")) { + char unit[32]; + snprintf(unit, 32, "%s", json_string_value(value)); + if (!strcmp(unit, "SCALE_UP")) { + pkt->unit = CPU_POWER_SCALE_UP; + } else if (!strcmp(unit, "SCALE_DOWN")) { + pkt->unit = CPU_POWER_SCALE_DOWN; + } else if (!strcmp(unit, "SCALE_MAX")) { + pkt->unit = CPU_POWER_SCALE_MAX; + } else if (!strcmp(unit, "SCALE_MIN")) { + pkt->unit = CPU_POWER_SCALE_MIN; + } else if (!strcmp(unit, "ENABLE_TURBO")) { + pkt->unit = CPU_POWER_ENABLE_TURBO; + } else if (!strcmp(unit, "DISABLE_TURBO")) { + pkt->unit = CPU_POWER_DISABLE_TURBO; + } else { + RTE_LOG(ERR, CHANNEL_MONITOR, + "Invalid command received in JSON\n"); + return -1; + } + } else if (!strcmp(key, "resource_id")) { + pkt->resource_id = (uint32_t)json_integer_value(value); + } else { + RTE_LOG(ERR, CHANNEL_MONITOR, + "Unknown key received in JSON string: %s\n", + key); + } + } + return 0; +} +#endif + void channel_monitor_exit(void) { run_loop = 0; @@ -555,6 +792,103 @@ channel_monitor_init(void) return 0; } +static void +read_binary_packet(struct channel_info *chan_info) +{ + struct channel_packet pkt; + void *buffer = &pkt; + int buffer_len = sizeof(pkt); + int n_bytes, err = 0; + + while (buffer_len > 0) { + n_bytes = read(chan_info->fd, + buffer, buffer_len); + if (n_bytes == buffer_len) + break; + if (n_bytes == -1) { + err = errno; + RTE_LOG(DEBUG, CHANNEL_MONITOR, + "Received error on " + "channel '%s' read: %s\n", + chan_info->channel_path, + strerror(err)); + remove_channel(&chan_info); + break; + } + buffer = (char *)buffer + n_bytes; + buffer_len -= n_bytes; + } + if (!err) + process_request(&pkt, chan_info); +} + +#ifdef USE_JANSSON +static void +read_json_packet(struct channel_info *chan_info) +{ + struct channel_packet pkt; + int n_bytes, ret; + json_t *root; + json_error_t error; + + /* read opening brace to closing brace */ + do { + int idx = 0; + int indent = 0; + do { + n_bytes = read(chan_info->fd, &json_data[idx], 1); + if (n_bytes == 0) + break; + if (json_data[idx] == '{') + indent++; + if (json_data[idx] == '}') + indent--; + if ((indent > 0) || (idx > 0)) + idx++; + if (indent == 0) + json_data[idx] = 0; + if (idx >= MAX_JSON_STRING_LEN-1) + break; + } while (indent > 0); + + if (indent > 0) + /* + * We've broken out of the read loop without getting + * a closing brace, so throw away the data + */ + json_data[idx] = 0; + + if (strlen(json_data) == 0) + continue; + + printf("got [%s]\n", json_data); + + root = json_loads(json_data, 0, &error); + + if (root) { + /* + * Because our data is now in the json + * object, we can overwrite the pkt + * with a channel_packet struct, using + * parse_json_to_pkt() + */ + ret = parse_json_to_pkt(root, &pkt); + json_decref(root); + if (ret) { + RTE_LOG(ERR, CHANNEL_MONITOR, + "Error validating JSON profile data\n"); + break; + } + process_request(&pkt, chan_info); + } else { + RTE_LOG(ERR, CHANNEL_MONITOR, + "JSON error on line %d: %s\n", + error.line, error.text); + } + } while (n_bytes > 0); +} +#endif + void run_channel_monitor(void) { @@ -578,31 +912,18 @@ run_channel_monitor(void) } if (global_events_list[i].events & EPOLLIN) { - int n_bytes, err = 0; - struct channel_packet pkt; - void *buffer = &pkt; - int buffer_len = sizeof(pkt); - - while (buffer_len > 0) { - n_bytes = read(chan_info->fd, - buffer, buffer_len); - if (n_bytes == buffer_len) - break; - if (n_bytes == -1) { - err = errno; - RTE_LOG(DEBUG, CHANNEL_MONITOR, - "Received error on " - "channel '%s' read: %s\n", - chan_info->channel_path, - strerror(err)); - remove_channel(&chan_info); - break; - } - buffer = (char *)buffer + n_bytes; - buffer_len -= n_bytes; + switch (chan_info->type) { + case CHANNEL_TYPE_BINARY: + read_binary_packet(chan_info); + break; +#ifdef USE_JANSSON + case CHANNEL_TYPE_JSON: + read_json_packet(chan_info); + break; +#endif + default: + break; } - if (!err) - process_request(&pkt, chan_info); } } rte_delay_us(time_period_ms*1000); -- 2.17.1