static int kvp_on_msg(void *msg, int len) { struct hv_kvp_msg *message = (struct hv_kvp_msg *)msg; struct hv_kvp_msg_enumerate *data; int error = 0; if (len < sizeof(*message)) return -EINVAL; /* * If we are negotiating the version information * with the daemon; handle that first. */ if (kvp_transaction.state < HVUTIL_READY) { return kvp_handle_handshake(message); } /* We didn't send anything to userspace so the reply is spurious */ if (kvp_transaction.state < HVUTIL_USERSPACE_REQ) return -EINVAL; kvp_transaction.state = HVUTIL_USERSPACE_RECV; /* * Based on the version of the daemon, we propagate errors from the * daemon differently. */ data = &message->body.kvp_enum_data; switch (dm_reg_value) { case KVP_OP_REGISTER: /* * Null string is used to pass back error condition. */ if (data->data.key[0] == 0) error = HV_S_CONT; break; case KVP_OP_REGISTER1: /* * We use the message header information from * the user level daemon to transmit errors. */ error = message->error; break; } /* * Complete the transaction by forwarding the key value * to the host. But first, cancel the timeout. */ if (cancel_delayed_work_sync(&kvp_timeout_work)) { kvp_respond_to_host(message, error); hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper); } return 0; }
static void kvp_work_func(struct work_struct *dummy) { /* * If the timer fires, the user-mode component has not responded; * process the pending transaction. */ kvp_respond_to_host(NULL, HV_E_FAIL); }
static void kvp_work_func(struct work_struct *dummy) { /* * If the timer fires, the user-mode component has not responded; * process the pending transaction. */ kvp_respond_to_host("Unknown key", "Guest timed out", TIMEOUT_FIRED); }
static void kvp_timeout_func(struct work_struct *dummy) { /* * If the timer fires, the user-mode component has not responded; * process the pending transaction. */ kvp_respond_to_host(NULL, HV_E_FAIL); hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper); }
static void kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) { struct hv_kvp_msg *message; struct hv_kvp_msg_enumerate *data; int error = 0; message = (struct hv_kvp_msg *)msg->data; /* * If we are negotiating the version information * with the daemon; handle that first. */ if (in_hand_shake) { if (kvp_handle_handshake(message)) in_hand_shake = false; return; } /* * Based on the version of the daemon, we propagate errors from the * daemon differently. */ data = &message->body.kvp_enum_data; switch (dm_reg_value) { case KVP_OP_REGISTER: /* * Null string is used to pass back error condition. */ if (data->data.key[0] == 0) error = HV_S_CONT; break; case KVP_OP_REGISTER1: /* * We use the message header information from * the user level daemon to transmit errors. */ error = message->error; break; } /* * Complete the transaction by forwarding the key value * to the host. But first, cancel the timeout. */ if (cancel_delayed_work_sync(&kvp_work)) kvp_respond_to_host(message, error); }
static void kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) { struct hv_ku_msg *message; message = (struct hv_ku_msg *)msg->data; if (msg->seq == KVP_REGISTER) { pr_info("KVP: user-mode registering done.\n"); kvp_register(); } if (msg->seq == KVP_USER_SET) { /* * Complete the transaction by forwarding the key value * to the host. But first, cancel the timeout. */ if (cancel_delayed_work_sync(&kvp_work)) kvp_respond_to_host(message->kvp_key, message->kvp_value, !strlen(message->kvp_key)); } }
static void kvp_send_key(struct work_struct *dummy) { struct cn_msg *msg; struct hv_kvp_msg *message; 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; int rc; msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC); if (!msg) return; msg->id.idx = CN_KVP_IDX; msg->id.val = CN_KVP_VAL; 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_IP_INFO: process_ib_ipinfo(in_msg, message, KVP_OP_SET_IP_INFO); break; case KVP_OP_GET_IP_INFO: process_ib_ipinfo(in_msg, message, KVP_OP_GET_IP_INFO); break; 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); rc = cn_netlink_send(msg, 0, 0, GFP_ATOMIC); if (rc) { pr_debug("KVP: failed to communicate to the daemon: %d\n", rc); if (cancel_delayed_work_sync(&kvp_work)) kvp_respond_to_host(message, HV_E_FAIL); } kfree(msg); return; }
static void kvp_on_reset(void) { if (cancel_delayed_work_sync(&kvp_timeout_work)) kvp_respond_to_host(NULL, HV_E_FAIL); kvp_transaction.state = HVUTIL_DEVICE_INIT; }
void hv_kvp_onchannelcallback(void *context) { struct vmbus_channel *channel = context; u32 recvlen; u64 requestid; struct hv_kvp_msg *kvp_msg; struct icmsg_hdr *icmsghdrp; struct icmsg_negotiate *negop = NULL; int util_fw_version; int kvp_srv_version; if (kvp_transaction.state > HVUTIL_READY) return; vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 4, &recvlen, &requestid); if (recvlen > 0) { icmsghdrp = (struct icmsg_hdr *)&recv_buffer[ sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { /* * Based on the host, select appropriate * framework and service versions we will * negotiate. */ switch (vmbus_proto_version) { case (VERSION_WS2008): util_fw_version = UTIL_WS2K8_FW_VERSION; kvp_srv_version = WS2008_SRV_VERSION; break; case (VERSION_WIN7): util_fw_version = UTIL_FW_VERSION; kvp_srv_version = WIN7_SRV_VERSION; break; default: util_fw_version = UTIL_FW_VERSION; kvp_srv_version = WIN8_SRV_VERSION; } vmbus_prep_negotiate_resp(icmsghdrp, negop, recv_buffer, util_fw_version, kvp_srv_version); } else { kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; /* * Stash away this global state for completing the * transaction; note transactions are serialized. */ kvp_transaction.recv_len = recvlen; kvp_transaction.recv_req_id = requestid; kvp_transaction.kvp_msg = kvp_msg; if (kvp_transaction.state < HVUTIL_READY) { /* Userspace is not registered yet */ kvp_respond_to_host(NULL, HV_E_FAIL); return; } kvp_transaction.state = HVUTIL_HOSTMSG_RECEIVED; /* * 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. */ schedule_work(&kvp_sendkey_work); schedule_delayed_work(&kvp_timeout_work, HV_UTIL_TIMEOUT * HZ); return; } icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; vmbus_sendpacket(channel, recv_buffer, recvlen, requestid, VM_PKT_DATA_INBAND, 0); } }
static void kvp_send_key(struct work_struct *dummy) { struct hv_kvp_msg *message; 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; int rc; /* The transaction state is wrong. */ if (kvp_transaction.state != HVUTIL_HOSTMSG_RECEIVED) return; message = kzalloc(sizeof(*message), GFP_KERNEL); if (!message) return; 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 guarantee * that the strings can be properly terminated! */ switch (message->kvp_hdr.operation) { case KVP_OP_SET_IP_INFO: process_ib_ipinfo(in_msg, message, KVP_OP_SET_IP_INFO); break; case KVP_OP_GET_IP_INFO: /* * We only need to pass on the info of operation, adapter_id * and addr_family to the userland kvp daemon. */ process_ib_ipinfo(in_msg, message, KVP_OP_GET_IP_INFO); break; 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, "%u", 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; } /* * The key is always a string - utf16 encoding. */ 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_GET: message->body.kvp_get.data.key_size = utf16s_to_utf8s( (wchar_t *)in_msg->body.kvp_get.data.key, in_msg->body.kvp_get.data.key_size, UTF16_LITTLE_ENDIAN, message->body.kvp_get.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; } kvp_transaction.state = HVUTIL_USERSPACE_REQ; rc = hvutil_transport_send(hvt, message, sizeof(*message), NULL); if (rc) { pr_debug("KVP: failed to communicate to the daemon: %d\n", rc); if (cancel_delayed_work_sync(&kvp_timeout_work)) { kvp_respond_to_host(message, HV_E_FAIL); kvp_transaction.state = HVUTIL_READY; } } kfree(message); }