Drivers: hv: Support the newly introduced KVP messages in
authorOlaf Hering <ohering@suse.de>
Mon, 26 Mar 2012 13:52:26 +0000 (15:52 +0200)
committerOlaf Hering <ohering@suse.de>
Mon, 26 Mar 2012 13:52:45 +0000 (15:52 +0200)
the driver.

suse-commit: 582cc2f004b4ea122c50b0f60d7b459a13b46f23

drivers/hv/hv_kvp.c
include/linux/hyperv.h
tools/hv/hv_kvp_daemon.c

index 779109b..cfe60b0 100644 (file)
 static struct {
        bool active; /* transaction status - active or not */
        int recv_len; /* number of bytes received. */
-       int index; /* current index */
+       struct hv_kvp_msg  *kvp_msg; /* current message */
        struct vmbus_channel *recv_channel; /* chn we got the request */
        u64 recv_req_id; /* request ID. */
+       void *kvp_context; /* for the channel callback */
 } kvp_transaction;
 
 static void kvp_send_key(struct work_struct *dummy);
@@ -110,12 +111,15 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
        struct hv_kvp_msg_enumerate *data;
 
        message = (struct hv_kvp_msg *)msg->data;
-       if (message->kvp_hdr.operation == KVP_OP_REGISTER) {
+       switch (message->kvp_hdr.operation) {
+       case KVP_OP_REGISTER:
                pr_info("KVP: user-mode registering done.\n");
                kvp_register();
-       }
+               kvp_transaction.active = false;
+               hv_kvp_onchannelcallback(kvp_transaction.kvp_context);
+               break;
 
-       if (message->kvp_hdr.operation == KVP_OP_ENUMERATE) {
+       default:
                data = &message->body.kvp_enum_data;
                /*
                 * Complete the transaction by forwarding the key value
@@ -133,21 +137,104 @@ kvp_send_key(struct work_struct *dummy)
 {
        struct cn_msg *msg;
        struct hv_kvp_msg *message;
-       int index = kvp_transaction.index;
+       struct hv_kvp_msg *in_msg;
+       __u8 operation = kvp_transaction.kvp_msg->kvp_hdr.operation;
+       __u8 pool = kvp_transaction.kvp_msg->kvp_hdr.pool;
+       __u32 val32;
+       __u64 val64;
 
        msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC);
+       if (!msg)
+               return;
 
-       if (msg) {
-               msg->id.idx =  CN_KVP_IDX;
-               msg->id.val = CN_KVP_VAL;
+       msg->id.idx =  CN_KVP_IDX;
+       msg->id.val = CN_KVP_VAL;
 
-               message = (struct hv_kvp_msg *)msg->data;
-               message->kvp_hdr.operation = KVP_OP_ENUMERATE;
-               message->body.kvp_enum_data.index = index;
-               msg->len = sizeof(struct hv_kvp_msg);
-               cn_netlink_send(msg, 0, GFP_ATOMIC);
-               kfree(msg);
+       message = (struct hv_kvp_msg *)msg->data;
+       message->kvp_hdr.operation = operation;
+       message->kvp_hdr.pool = pool;
+       in_msg = kvp_transaction.kvp_msg;
+
+       /*
+        * The key/value strings sent from the host are encoded in
+        * in utf16; convert it to utf8 strings.
+        * The host assures us that the utf16 strings will not exceed
+        * the max lengths specified. We will however, reserve room
+        * for the string terminating character - in the utf16s_utf8s()
+        * function we limit the size of the buffer where the converted
+        * string is placed to HV_KVP_EXCHANGE_MAX_*_SIZE -1 to gaurantee
+        * that the strings can be properly terminated!
+        */
+
+       switch (message->kvp_hdr.operation) {
+       case KVP_OP_SET:
+               switch (in_msg->body.kvp_set.data.value_type) {
+               case REG_SZ:
+                       /*
+                        * The value is a string - utf16 encoding.
+                        */
+                       message->body.kvp_set.data.value_size =
+                               utf16s_to_utf8s(
+                               (wchar_t *)in_msg->body.kvp_set.data.value,
+                               in_msg->body.kvp_set.data.value_size,
+                               UTF16_LITTLE_ENDIAN,
+                               message->body.kvp_set.data.value,
+                               HV_KVP_EXCHANGE_MAX_VALUE_SIZE - 1) + 1;
+                               break;
+
+               case REG_U32:
+                       /*
+                        * The value is a 32 bit scalar.
+                        * We save this as a utf8 string.
+                        */
+                       val32 = in_msg->body.kvp_set.data.value_u32;
+                       message->body.kvp_set.data.value_size =
+                               sprintf(message->body.kvp_set.data.value,
+                                       "%d", val32) + 1;
+                       break;
+
+               case REG_U64:
+                       /*
+                        * The value is a 64 bit scalar.
+                        * We save this as a utf8 string.
+                        */
+                       val64 = in_msg->body.kvp_set.data.value_u64;
+                       message->body.kvp_set.data.value_size =
+                               sprintf(message->body.kvp_set.data.value,
+                                       "%llu", val64) + 1;
+                       break;
+
+               }
+       case KVP_OP_GET:
+               message->body.kvp_set.data.key_size =
+                       utf16s_to_utf8s(
+                       (wchar_t *)in_msg->body.kvp_set.data.key,
+                       in_msg->body.kvp_set.data.key_size,
+                       UTF16_LITTLE_ENDIAN,
+                       message->body.kvp_set.data.key,
+                       HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1;
+                       break;
+
+       case KVP_OP_DELETE:
+               message->body.kvp_delete.key_size =
+                       utf16s_to_utf8s(
+                       (wchar_t *)in_msg->body.kvp_delete.key,
+                       in_msg->body.kvp_delete.key_size,
+                       UTF16_LITTLE_ENDIAN,
+                       message->body.kvp_delete.key,
+                       HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1;
+                       break;
+
+       case KVP_OP_ENUMERATE:
+               message->body.kvp_enum_data.index =
+                       in_msg->body.kvp_enum_data.index;
+                       break;
        }
+
+       msg->len = sizeof(struct hv_kvp_msg);
+       cn_netlink_send(msg, 0, GFP_ATOMIC);
+       kfree(msg);
+
        return;
 }
 
@@ -159,10 +246,11 @@ static void
 kvp_respond_to_host(char *key, char *value, int error)
 {
        struct hv_kvp_msg  *kvp_msg;
-       struct hv_kvp_msg_enumerate  *kvp_data;
+       struct hv_kvp_exchg_msg_value  *kvp_data;
        char    *key_name;
        struct icmsg_hdr *icmsghdrp;
-       int     keylen, valuelen;
+       int     keylen = 0;
+       int     valuelen = 0;
        u32     buf_len;
        struct vmbus_channel *channel;
        u64     req_id;
@@ -189,6 +277,9 @@ kvp_respond_to_host(char *key, char *value, int error)
 
        kvp_transaction.active = false;
 
+       icmsghdrp = (struct icmsg_hdr *)
+                       &recv_buffer[sizeof(struct vmbuspipe_hdr)];
+
        if (channel->onchannel_callback == NULL)
                /*
                 * We have raced with util driver being unloaded;
@@ -196,41 +287,66 @@ kvp_respond_to_host(char *key, char *value, int error)
                 */
                return;
 
-       icmsghdrp = (struct icmsg_hdr *)
-                       &recv_buffer[sizeof(struct vmbuspipe_hdr)];
-       kvp_msg = (struct hv_kvp_msg *)
-                       &recv_buffer[sizeof(struct vmbuspipe_hdr) +
-                       sizeof(struct icmsg_hdr)];
-       kvp_data = &kvp_msg->body.kvp_enum_data;
-       key_name = key;
 
        /*
         * If the error parameter is set, terminate the host's enumeration.
         */
        if (error) {
                /*
-                * We don't support this index or the we have timedout;
+                * Something failed or the we have timedout;
                 * terminate the host-side iteration by returning an error.
                 */
                icmsghdrp->status = HV_E_FAIL;
                goto response_done;
        }
 
+       icmsghdrp->status = HV_S_OK;
+
+       kvp_msg = (struct hv_kvp_msg *)
+                       &recv_buffer[sizeof(struct vmbuspipe_hdr) +
+                       sizeof(struct icmsg_hdr)];
+
+       switch (kvp_transaction.kvp_msg->kvp_hdr.operation) {
+       case KVP_OP_GET:
+               kvp_data = &kvp_msg->body.kvp_get.data;
+               goto copy_value;
+
+       case KVP_OP_SET:
+       case KVP_OP_DELETE:
+               goto response_done;
+
+       default:
+               break;
+       }
+
+       kvp_data = &kvp_msg->body.kvp_enum_data.data;
+       key_name = key;
+
        /*
         * The windows host expects the key/value pair to be encoded
-        * in utf16.
+        * in utf16. Ensure that the key/value size reported to the host
+        * will be less than or equal to the MAX size (including the
+        * terminating character).
         */
        keylen = utf8s_to_utf16s(key_name, strlen(key_name), UTF16_HOST_ENDIAN,
-                               (wchar_t *) kvp_data->data.key,
-                               HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2);
-       kvp_data->data.key_size = 2*(keylen + 1); /* utf16 encoding */
+                               (wchar_t *) kvp_data->key,
+                               (HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2) - 2);
+       kvp_data->key_size = 2*(keylen + 1); /* utf16 encoding */
+
+copy_value:
        valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN,
-                               (wchar_t *) kvp_data->data.value,
-                               HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2);
-       kvp_data->data.value_size = 2*(valuelen + 1); /* utf16 encoding */
+                               (wchar_t *) kvp_data->value,
+                               (HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2);
+       kvp_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;
+       /*
+        * If the utf8s to utf16s conversion failed; notify host
+        * of the error.
+        */
+       if ((keylen < 0) || (valuelen < 0))
+               icmsghdrp->status = HV_E_FAIL;
+
+       kvp_data->value_type = REG_SZ; /* all our values are strings */
 
 response_done:
        icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
@@ -257,11 +373,18 @@ void hv_kvp_onchannelcallback(void *context)
        u64 requestid;
 
        struct hv_kvp_msg *kvp_msg;
-       struct hv_kvp_msg_enumerate *kvp_data;
 
        struct icmsg_hdr *icmsghdrp;
        struct icmsg_negotiate *negop = NULL;
 
+       if (kvp_transaction.active) {
+               /*
+                * We will defer processing this callback once
+                * the current transaction is complete.
+                */
+               kvp_transaction.kvp_context = context;
+               return;
+       }
 
        vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid);
 
@@ -276,29 +399,16 @@ void hv_kvp_onchannelcallback(void *context)
                                sizeof(struct vmbuspipe_hdr) +
                                sizeof(struct icmsg_hdr)];
 
-                       kvp_data = &kvp_msg->body.kvp_enum_data;
-
-                       /*
-                        * We only support the "get" operation on
-                        * "KVP_POOL_AUTO" pool.
-                        */
-
-                       if ((kvp_msg->kvp_hdr.pool != KVP_POOL_AUTO) ||
-                               (kvp_msg->kvp_hdr.operation !=
-                               KVP_OP_ENUMERATE)) {
-                               icmsghdrp->status = HV_E_FAIL;
-                               goto callback_done;
-                       }
-
                        /*
                         * Stash away this global state for completing the
                         * transaction; note transactions are serialized.
                         */
+
                        kvp_transaction.recv_len = recvlen;
                        kvp_transaction.recv_channel = channel;
                        kvp_transaction.recv_req_id = requestid;
                        kvp_transaction.active = true;
-                       kvp_transaction.index = kvp_data->index;
+                       kvp_transaction.kvp_msg = kvp_msg;
 
                        /*
                         * Get the information from the
@@ -316,8 +426,6 @@ void hv_kvp_onchannelcallback(void *context)
 
                }
 
-callback_done:
-
                icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
                        | ICMSGHDRFLAG_RESPONSE;
 
@@ -338,6 +446,14 @@ hv_kvp_init(struct hv_util_service *srv)
                return err;
        recv_buffer = srv->recv_buffer;
 
+       /*
+        * When this driver loads, the user level daemon that
+        * processes the host requests may not yet be running.
+        * Defer processing channel callbacks until the daemon
+        * has registered.
+        */
+       kvp_transaction.active = true;
+
        return 0;
 }
 
index a2d8c54..e88a979 100644 (file)
  */
 
 #define REG_SZ 1
+#define REG_U32 4
+#define REG_U64 8
 
 enum hv_kvp_exchg_op {
        KVP_OP_GET = 0,
index 00d3f7c..a98878c 100644 (file)
@@ -389,10 +389,16 @@ int main(void)
                        }
                        continue;
 
+               case KVP_OP_SET:
+               case KVP_OP_GET:
+               case KVP_OP_DELETE:
                default:
                        break;
                }
 
+               if (hv_msg->kvp_hdr.operation != KVP_OP_ENUMERATE)
+                       goto kvp_done;
+
                hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
                key_name = (char *)hv_msg->body.kvp_enum_data.data.key;
                key_value = (char *)hv_msg->body.kvp_enum_data.data.value;
@@ -454,6 +460,7 @@ int main(void)
                 * already in the receive buffer. Update the cn_msg header to
                 * reflect the key value that has been added to the message
                 */
+kvp_done:
 
                incoming_cn_msg->id.idx = CN_KVP_IDX;
                incoming_cn_msg->id.val = CN_KVP_VAL;