diff options
-rw-r--r-- | drivers/hv/hv_kvp.c | 218 | ||||
-rw-r--r-- | include/linux/hyperv.h | 2 | ||||
-rw-r--r-- | tools/hv/hv_kvp_daemon.c | 7 |
3 files changed, 176 insertions, 51 deletions
diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 779109b6f4f0..cfe60b02e3e8 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -42,9 +42,10 @@ 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; } diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index a2d8c547f91b..e88a979107b5 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -119,6 +119,8 @@ */ #define REG_SZ 1 +#define REG_U32 4 +#define REG_U64 8 enum hv_kvp_exchg_op { KVP_OP_GET = 0, diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 00d3f7c099e0..a98878c874be 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -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; |