FYI. Long ago we discussed key value approach on top of virtio-serial. -------- Original Message -------- Subject: [PATCH]: An implementation of HyperV KVP functionality Date: Thu, 11 Nov 2010 13:03:10 -0700 From: Ky Srinivasan <ksrinivasan@xxxxxxxxxx> To: <devel@xxxxxxxxxxxxxxxxxxxx>, <Virtualization@xxxxxxxxxxxxxx> CC: Haiyang Zhang <haiyangz@xxxxxxxxxxxxx>, Greg KH <gregkh@xxxxxxx>I am enclosing a patch that implements the KVP (Key Value Pair) functionality for Linux guests on HyperV. This functionality allows Microsoft Management stack to query information from the guest. This functionality is implemented in two parts: (a) A kernel component that communicates with the host and (b) A user level daemon that implements data gathering. The attached patch (kvp.patch) implements the kernel component. I am also attaching the code for the user-level daemon (kvp_daemon.c) for reference.
Regards, K. Y
From: K. Y. Srinivasan <ksrinivasan@xxxxxxxxxx> Subject: An implementation of key/value pair feature (KVP) for Linux on HyperV. Signed-off-by: K. Y. Srinivasan <ksrinivasan@xxxxxxxxxx> Index: linux.trees.git/drivers/staging/hv/kvp.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux.trees.git/drivers/staging/hv/kvp.c 2010-11-11 13:45:17.000000000 -0500 @@ -0,0 +1,404 @@ +/* + * An implementation of key value pair (KVP) functionality for Linux. + * + * + * Copyright (C) 2010, Novell, Inc. + * Author : K. Y. Srinivasan <ksrinivasan@xxxxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +#include <linux/net.h> +#include <linux/nls.h> +#include <linux/connector.h> + +#include "logging.h" +#include "osd.h" +#include "vmbus.h" +#include "vmbus_packet_format.h" +#include "vmbus_channel_interface.h" +#include "version_info.h" +#include "channel.h" +#include "vmbus_private.h" +#include "vmbus_api.h" +#include "utils.h" +#include "kvp.h" + + +/* + * + * The following definitions are shared with the user-mode component; do not + * change any of this without making the corresponding changes in + * the KVP user-mode component. + */ + +#define CN_KVP_VAL 0x1 /* This supports queries from the kernel */ +#define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */ + + +/* + * KVP protocol: The user mode component first registers with the + * the kernel component. Subsequently, the kernel component requests, data + * for the specified keys. In response to this message the user mode component + * fills in the value corresponding to the specified key. We overload the + * sequence field in the cn_msg header to define our KVP message types. + * + * XXXKYS: Have a shared header file between the user and kernel (TODO) + */ + +enum kvp_op { + KVP_REGISTER = 0, /* Register the user mode component */ + KVP_KERNEL_GET,/*Kernel is requesting the value for the specified key*/ + KVP_KERNEL_SET, /*Kernel is providing the value for the specified key*/ + KVP_USER_GET, /*User is requesting the value for the specified key*/ + KVP_USER_SET /*User is providing the value for the specified key*/ +}; + + + +#define KVP_KEY_SIZE 512 +#define KVP_VALUE_SIZE 2048 + + +typedef struct kvp_msg { + __u32 kvp_key; /* Key */ + __u8 kvp_value[0]; /* Corresponding value */ +} kvp_msg_t; + +/* + * End of shared definitions. + */ + +/* + * Registry value types. + */ + +#define REG_SZ 1 + +/* + * Array of keys we support in Linux. + * + */ +#define KVP_MAX_KEY 10 +#define KVP_LIC_VERSION 1 + + +static char *kvp_keys[KVP_MAX_KEY] = {"FullyQualifiedDomainName", + "IntegrationServicesVersion", + "NetworkAddressIPv4", + "NetworkAddressIPv6", + "OSBuildNumber", + "OSName", + "OSMajorVersion", + "OSMinorVersion", + "OSVersion", + "ProcessorArchitecture", + }; + +/* + * Global state maintained for transaction that is being processed. + * Note that only one transaction can be active at any point in time. + * + * This state is set when we receive a request from the host; we + * cleanup this state when the transaction is completed - when we respond + * to the host with the key value. + */ + +static u8 *recv_buffer; /* the receive buffer that we allocated */ +static int recv_len; /* number of bytes received. */ +static struct vmbus_channel *recv_channel; /*chn on which we got the request*/ +static u64 recv_req_id; /* request ID. */ +static int kvp_current_index; + +static int +kvp_send_key(int index); + +static +void kvp_respond_to_host(int key, char *value); + +static struct cb_id kvp_id = { CN_KVP_IDX, CN_KVP_VAL }; +static char kvp_name[] = "kvp_kernel_module"; + +static bool kvp_transaction_active; + +static struct timer_list kvp_timer; + +static void kvp_timer_func(unsigned long __data) +{ + u32 key = *((u32 *)__data); + /* + * If the timer fires, the user-mode component has not responded; + * process the pending transaction. + */ + kvp_respond_to_host(key, "Guest timed out"); +} + +/* + * Callback when data is received from user mode. + */ + +static void +kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) +{ + struct kvp_msg *message; + + message = (struct kvp_msg *)msg->data; + if (msg->seq == KVP_REGISTER) + printk(KERN_WARNING "KVP: user-mode registering done.\n"); + + if (msg->seq == KVP_USER_SET) { + /* + * Complete the transaction by forwarding the key value + * to the host. But first, cancel the timeout. + */ + if (del_timer_sync(&kvp_timer)) + kvp_respond_to_host(message->kvp_key, + message->kvp_value); + } +} + +static int +kvp_send_key(int index) +{ + struct cn_msg *msg; + + msg = kzalloc(sizeof(*msg) + sizeof(kvp_msg_t) , GFP_ATOMIC); + + if (msg) { + msg->id.idx = CN_KVP_IDX; + msg->id.val = CN_KVP_VAL; + msg->seq = KVP_KERNEL_GET; + ((kvp_msg_t *)msg->data)->kvp_key = index; + msg->len = sizeof(kvp_msg_t); + cn_netlink_send(msg, 0, GFP_ATOMIC); + kfree(msg); + return 0; + } + return 1; +} + +/* + * Send a response back to the host. + * key specifies the key for which the value is being returned. + */ + +static +void kvp_respond_to_host(int key, char *value) +{ + ic_kvp_msg_t *kvp_msg; + ic_kvp_msg_enumerate_t *kvp_data; + char *key_name; + struct icmsg_hdr *icmsghdrp; + int keylen, valuelen; + u8 *buf; + u32 buf_len; + struct vmbus_channel *channel; + u64 req_id; + + /* + * If a transaction is not active; log and return. + */ + + if (!kvp_transaction_active) { + /* + * This is a spurious call! + */ + printk(KERN_WARNING "KVP: Transaction not active\n"); + return; + } + /* + * Copy the global state for completing the transaction. Note that + * only one transaction can be active at a time. + */ + + buf = recv_buffer; + buf_len = recv_len; + channel = recv_channel; + req_id = recv_req_id; + + kvp_transaction_active = false; + + icmsghdrp = (struct icmsg_hdr *)&buf[sizeof(struct vmbuspipe_hdr)]; + kvp_msg = (ic_kvp_msg_t *)&buf[sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + kvp_data = &kvp_msg->kvp_data; + key_name = kvp_keys[key]; + + /* + * The windows host expects the key/value pair to be encoded + * in utf16. + */ + keylen = utf8s_to_utf16s(key_name, strlen(key_name), + (wchar_t *)kvp_data->data.key); + kvp_data->data.key_size = 2*(keylen + 1); /* utf16 encoding */ + valuelen = utf8s_to_utf16s(value, strlen(value), + (wchar_t *)kvp_data->data.value); + kvp_data->data.value_size = 2*(valuelen + 1); /* utf16 encoding */ + + kvp_data->data.value_type = REG_SZ; /* all our values are strings */ + icmsghdrp->status = HV_S_OK; + + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; + + vmbus_sendpacket(channel, buf, buf_len, req_id, + VmbusPacketTypeDataInBand, 0); + + /* + * Free up the buffer that was allocatted when we received a message + * from the host. + */ + kfree(buf); +} + +/* + * This callback is invoked when we get a KVP message from the host. + * The host ensures that only one KVP transaction can be active at a time. + * KVP implementation in Linux needs to forward the key to a user-mde + * component to retrive the corresponding value. Consequently, we cannot + * respond to the host in the conext of this callback. Since the host + * guarantees that at most only one transaction can be active at a time, + * we stash away the transaction state in a set of global variables. + */ + +void kvp_onchannelcallback(void *context) +{ + struct vmbus_channel *channel = context; + u8 *buf; + u32 buflen, recvlen; + u64 requestid; + + ic_kvp_msg_t *kvp_msg; + ic_kvp_msg_enumerate_t *kvp_data; + + struct icmsg_hdr *icmsghdrp; + struct icmsg_negotiate *negop = NULL; + + + buflen = PAGE_SIZE; + buf = kmalloc(buflen, GFP_ATOMIC); + + vmbus_recvpacket(channel, buf, buflen, &recvlen, &requestid); + + if (recvlen > 0) { + DPRINT_DBG(VMBUS, "KVP packet: len=%d, requestid=%lld", + recvlen, requestid); + + icmsghdrp = (struct icmsg_hdr *)&buf[ + sizeof(struct vmbuspipe_hdr)]; + + if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { + prep_negotiate_resp(icmsghdrp, negop, buf); + } else { + kvp_msg = (ic_kvp_msg_t *)&buf[ + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + + kvp_data = &kvp_msg->kvp_data; + + /* + * We only support the "get" operation on + * "ICKvpExchangePoolAuto" pool. + */ + + if ((kvp_msg->kvp_hdr.pool != ICKvpExchangePoolAuto) || + (kvp_msg->kvp_hdr.operation != + ICKvpExchangeOperationEnumerate) || + kvp_transaction_active) { + if (kvp_transaction_active) + printk(KERN_WARNING + "KVP: Invalid call\n"); + icmsghdrp->status = HV_E_FAIL; + goto callback_done; + } + + /* + * Stash away this global state for completing the + * transaction; note transactions are serialized. + */ + recv_buffer = buf; + recv_len = recvlen; + recv_channel = channel; + recv_req_id = requestid; + + switch (kvp_data->index) { + case (KVP_MAX_KEY): + /* + * We don't support this key + * and any key beyond this. + */ + icmsghdrp->status = HV_E_FAIL; + goto callback_done; + + case (KVP_LIC_VERSION): + kvp_transaction_active = true; + kvp_respond_to_host(kvp_data->index, + HV_DRV_VERSION); + return; + default: + /* + * Get the information from the + * user-mode component. + * component. This transaction will be + * completed when we get the value from + * the user-mode component. + * Set a timeout to deal with + * user-mode not responding. + */ + kvp_current_index = kvp_data->index; + mod_timer(&kvp_timer, jiffies+75); + kvp_transaction_active = true; + kvp_send_key(kvp_data->index); + return; + } + + } + +callback_done: + + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION + | ICMSGHDRFLAG_RESPONSE; + + vmbus_sendpacket(channel, buf, + recvlen, requestid, + VmbusPacketTypeDataInBand, 0); + } + + kfree(buf); + + +} + +int +kvp_init(void) +{ + int err; + + err = cn_add_callback(&kvp_id, kvp_name, kvp_cn_callback); + if (err) + return err; + + setup_timer(&kvp_timer, kvp_timer_func, + (unsigned long)&kvp_current_index); + return 0; +} + +void kvp_deinit(void) +{ + cn_del_callback(&kvp_id); + del_timer_sync(&kvp_timer); +} + Index: linux.trees.git/drivers/staging/hv/Makefile =================================================================== --- linux.trees.git.orig/drivers/staging/hv/Makefile 2010-11-10 14:01:55.000000000 -0500 +++ linux.trees.git/drivers/staging/hv/Makefile 2010-11-11 11:24:54.000000000 -0500 @@ -2,7 +2,7 @@ obj-$(CONFIG_HYPERV) += hv_vmbus.o hv_t obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o obj-$(CONFIG_HYPERV_BLOCK) += hv_blkvsc.o obj-$(CONFIG_HYPERV_NET) += hv_netvsc.o -obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o +obj-$(CONFIG_HYPERV_UTILS) += hv_util.o hv_vmbus-y := vmbus_drv.o osd.o \ vmbus.o hv.o connection.o channel.o \ @@ -10,3 +10,4 @@ hv_vmbus-y := vmbus_drv.o osd.o \ hv_storvsc-y := storvsc_drv.o storvsc.o hv_blkvsc-y := blkvsc_drv.o blkvsc.o hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o +hv_util-y := hv_utils.o kvp.o Index: linux.trees.git/drivers/staging/hv/kvp.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux.trees.git/drivers/staging/hv/kvp.h 2010-11-10 14:03:47.000000000 -0500 @@ -0,0 +1,101 @@ +/* + * An implementation of key value pair (KVP) functionality for Linux. + * + * + * Copyright (C) 2010, Novell, Inc. + * Author : K. Y. Srinivasan <ksrinivasan@xxxxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#ifndef _KVP_H +#define _KVP_H_ + +/* + * Maximum value size - used for both key names and value data, and includes + * any applicable NULL terminators. + * + * Note: This limit is somewhat arbitrary, but falls easily within what is + * supported for all native guests (back to Win 2000) and what is reasonable + * for the IC KVP exchange functionality. Note that Windows Me/98/95 are + * limited to 255 character key names. + * + * MSDN recommends not storing data values larger than 2048 bytes in the + * registry. + * + * Note: This value is used in defining the KVP exchange message - this value + * cannot be modified without affecting the message size and compatability. + */ + +/* + * bytes, including any null terminators + */ +#define IC_KVP_EXCHANGE_MAX_VALUE_SIZE (2048) + + +/* + * Maximum key size - the registry limit for the length of an entry name + * is 256 characters, including the null terminator + */ + +#define IC_KVP_EXCHANGE_MAX_KEY_SIZE (512) + + +typedef enum { + ICKvpExchangeOperationGet = 0, + ICKvpExchangeOperationSet, + ICKvpExchangeOperationDelete, + ICKvpExchangeOperationEnumerate, + ICKvpExchangeOperationCount /* Number of operations, must be last. */ +} IC_KVP_EXCHANGE_OPERATION; + +typedef enum { + ICKvpExchangePoolExternal = 0, + ICKvpExchangePoolGuest, + ICKvpExchangePoolAuto, + ICKvpExchangePoolAutoExternal, + ICKvpExchangePoolInternal, + ICKvpExchangePoolCount /* Number of pools, must be last. */ +} IC_KVP_EXCHANGE_POOL; + +typedef struct ic_kvp_hdr { + u8 operation; + u8 pool; +} ic_kvp_hdr_t; + +typedef struct ic_kvp_exchg_msg_value { + u32 value_type; + u32 key_size; + u32 value_size; + u8 key[IC_KVP_EXCHANGE_MAX_KEY_SIZE]; + u8 value[IC_KVP_EXCHANGE_MAX_VALUE_SIZE]; +} ic_kvp_exchg_msg_value_t; + +typedef struct ic_kvp__msg_enumerate { + u32 index; + ic_kvp_exchg_msg_value_t data; +} ic_kvp_msg_enumerate_t; + +typedef struct ic_kvp_msg { + ic_kvp_hdr_t kvp_hdr; + ic_kvp_msg_enumerate_t kvp_data; +} ic_kvp_msg_t; + +int kvp_init(void); +void kvp_deinit(void); +void kvp_onchannelcallback(void *); + +#endif /* _KVP_H */ + Index: linux.trees.git/drivers/staging/hv/utils.h =================================================================== --- linux.trees.git.orig/drivers/staging/hv/utils.h 2010-11-10 10:30:23.000000000 -0500 +++ linux.trees.git/drivers/staging/hv/utils.h 2010-11-10 14:03:47.000000000 -0500 @@ -102,6 +102,7 @@ struct ictimesync_data{ #define HV_SHUTDOWN_MSG 0 #define HV_TIMESYNC_MSG 1 #define HV_HEARTBEAT_MSG 2 +#define HV_KVP_MSG 3 struct hyperv_service_callback { u8 msg_type; Index: linux.trees.git/drivers/staging/hv/hv_utils.c =================================================================== --- linux.trees.git.orig/drivers/staging/hv/hv_utils.c 2010-11-10 14:02:40.000000000 -0500 +++ linux.trees.git/drivers/staging/hv/hv_utils.c 2010-11-11 13:42:38.000000000 -0500 @@ -37,6 +37,7 @@ #include "vmbus_private.h" #include "vmbus_api.h" #include "utils.h" +#include "kvp.h" static void shutdown_onchannelcallback(void *context) @@ -268,6 +269,9 @@ static int __init init_hyperv_utils(void { printk(KERN_INFO "Registering HyperV Utility Driver\n"); + if (kvp_init()) + return -ENODEV; + if (!dmi_check_system(hv_utils_dmi_table)) return -ENODEV; @@ -283,6 +287,10 @@ static int __init init_hyperv_utils(void &heartbeat_onchannelcallback; hv_cb_utils[HV_HEARTBEAT_MSG].callback = &heartbeat_onchannelcallback; + hv_cb_utils[HV_KVP_MSG].channel->OnChannelCallback = + &kvp_onchannelcallback; + + return 0; } @@ -301,6 +309,12 @@ static void exit_hyperv_utils(void) hv_cb_utils[HV_HEARTBEAT_MSG].channel->OnChannelCallback = &chn_cb_negotiate; hv_cb_utils[HV_HEARTBEAT_MSG].callback = &chn_cb_negotiate; + + hv_cb_utils[HV_KVP_MSG].channel->OnChannelCallback = + &chn_cb_negotiate; + + kvp_deinit(); + } module_init(init_hyperv_utils); Index: linux.trees.git/include/linux/connector.h =================================================================== --- linux.trees.git.orig/include/linux/connector.h 2010-11-09 17:22:15.000000000 -0500 +++ linux.trees.git/include/linux/connector.h 2010-11-11 13:14:52.000000000 -0500 @@ -42,8 +42,9 @@ #define CN_VAL_DM_USERSPACE_LOG 0x1 #define CN_IDX_DRBD 0x8 #define CN_VAL_DRBD 0x1 +#define CN_KVP_IDX 0x9 /* MSFT KVP functionality */ -#define CN_NETLINK_USERS 8 +#define CN_NETLINK_USERS 10 /* * Maximum connector's message size.
Attachment:
kvp_daemon.c
Description: Binary data
_______________________________________________ Virtualization mailing list Virtualization@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linux-foundation.org/mailman/listinfo/virtualization