146a97191SGreg Kroah-Hartman /* 246a97191SGreg Kroah-Hartman * An implementation of key value pair (KVP) functionality for Linux. 346a97191SGreg Kroah-Hartman * 446a97191SGreg Kroah-Hartman * 546a97191SGreg Kroah-Hartman * Copyright (C) 2010, Novell, Inc. 646a97191SGreg Kroah-Hartman * Author : K. Y. Srinivasan <ksrinivasan@novell.com> 746a97191SGreg Kroah-Hartman * 846a97191SGreg Kroah-Hartman * This program is free software; you can redistribute it and/or modify it 946a97191SGreg Kroah-Hartman * under the terms of the GNU General Public License version 2 as published 1046a97191SGreg Kroah-Hartman * by the Free Software Foundation. 1146a97191SGreg Kroah-Hartman * 1246a97191SGreg Kroah-Hartman * This program is distributed in the hope that it will be useful, but 1346a97191SGreg Kroah-Hartman * WITHOUT ANY WARRANTY; without even the implied warranty of 1446a97191SGreg Kroah-Hartman * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 1546a97191SGreg Kroah-Hartman * NON INFRINGEMENT. See the GNU General Public License for more 1646a97191SGreg Kroah-Hartman * details. 1746a97191SGreg Kroah-Hartman * 1846a97191SGreg Kroah-Hartman * You should have received a copy of the GNU General Public License 1946a97191SGreg Kroah-Hartman * along with this program; if not, write to the Free Software 2046a97191SGreg Kroah-Hartman * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2146a97191SGreg Kroah-Hartman * 2246a97191SGreg Kroah-Hartman */ 2346a97191SGreg Kroah-Hartman #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 2446a97191SGreg Kroah-Hartman 2546a97191SGreg Kroah-Hartman #include <linux/net.h> 2646a97191SGreg Kroah-Hartman #include <linux/nls.h> 2746a97191SGreg Kroah-Hartman #include <linux/connector.h> 2846a97191SGreg Kroah-Hartman #include <linux/workqueue.h> 2946a97191SGreg Kroah-Hartman #include <linux/hyperv.h> 3046a97191SGreg Kroah-Hartman 3146a97191SGreg Kroah-Hartman 3246a97191SGreg Kroah-Hartman 3346a97191SGreg Kroah-Hartman /* 3446a97191SGreg Kroah-Hartman * Global state maintained for transaction that is being processed. 3546a97191SGreg Kroah-Hartman * Note that only one transaction can be active at any point in time. 3646a97191SGreg Kroah-Hartman * 3746a97191SGreg Kroah-Hartman * This state is set when we receive a request from the host; we 3846a97191SGreg Kroah-Hartman * cleanup this state when the transaction is completed - when we respond 3946a97191SGreg Kroah-Hartman * to the host with the key value. 4046a97191SGreg Kroah-Hartman */ 4146a97191SGreg Kroah-Hartman 4246a97191SGreg Kroah-Hartman static struct { 4346a97191SGreg Kroah-Hartman bool active; /* transaction status - active or not */ 4446a97191SGreg Kroah-Hartman int recv_len; /* number of bytes received. */ 4546a97191SGreg Kroah-Hartman int index; /* current index */ 4646a97191SGreg Kroah-Hartman struct vmbus_channel *recv_channel; /* chn we got the request */ 4746a97191SGreg Kroah-Hartman u64 recv_req_id; /* request ID. */ 4846a97191SGreg Kroah-Hartman } kvp_transaction; 4946a97191SGreg Kroah-Hartman 5046a97191SGreg Kroah-Hartman static void kvp_send_key(struct work_struct *dummy); 5146a97191SGreg Kroah-Hartman 5246a97191SGreg Kroah-Hartman #define TIMEOUT_FIRED 1 5346a97191SGreg Kroah-Hartman 5446a97191SGreg Kroah-Hartman static void kvp_respond_to_host(char *key, char *value, int error); 5546a97191SGreg Kroah-Hartman static void kvp_work_func(struct work_struct *dummy); 5646a97191SGreg Kroah-Hartman static void kvp_register(void); 5746a97191SGreg Kroah-Hartman 5846a97191SGreg Kroah-Hartman static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func); 5946a97191SGreg Kroah-Hartman static DECLARE_WORK(kvp_sendkey_work, kvp_send_key); 6046a97191SGreg Kroah-Hartman 6146a97191SGreg Kroah-Hartman static struct cb_id kvp_id = { CN_KVP_IDX, CN_KVP_VAL }; 6246a97191SGreg Kroah-Hartman static const char kvp_name[] = "kvp_kernel_module"; 6346a97191SGreg Kroah-Hartman static u8 *recv_buffer; 6446a97191SGreg Kroah-Hartman /* 6546a97191SGreg Kroah-Hartman * Register the kernel component with the user-level daemon. 6646a97191SGreg Kroah-Hartman * As part of this registration, pass the LIC version number. 6746a97191SGreg Kroah-Hartman */ 6846a97191SGreg Kroah-Hartman 6946a97191SGreg Kroah-Hartman static void 7046a97191SGreg Kroah-Hartman kvp_register(void) 7146a97191SGreg Kroah-Hartman { 7246a97191SGreg Kroah-Hartman 7346a97191SGreg Kroah-Hartman struct cn_msg *msg; 7426403354SK. Y. Srinivasan struct hv_kvp_msg *kvp_msg; 7526403354SK. Y. Srinivasan char *version; 7646a97191SGreg Kroah-Hartman 7726403354SK. Y. Srinivasan msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg), GFP_ATOMIC); 7846a97191SGreg Kroah-Hartman 7946a97191SGreg Kroah-Hartman if (msg) { 8026403354SK. Y. Srinivasan kvp_msg = (struct hv_kvp_msg *)msg->data; 81*e485ceacSK. Y. Srinivasan version = kvp_msg->body.kvp_register.version; 8246a97191SGreg Kroah-Hartman msg->id.idx = CN_KVP_IDX; 8346a97191SGreg Kroah-Hartman msg->id.val = CN_KVP_VAL; 8426403354SK. Y. Srinivasan 8526403354SK. Y. Srinivasan kvp_msg->kvp_hdr.operation = KVP_OP_REGISTER; 8626403354SK. Y. Srinivasan strcpy(version, HV_DRV_VERSION); 8726403354SK. Y. Srinivasan msg->len = sizeof(struct hv_kvp_msg); 8846a97191SGreg Kroah-Hartman cn_netlink_send(msg, 0, GFP_ATOMIC); 8946a97191SGreg Kroah-Hartman kfree(msg); 9046a97191SGreg Kroah-Hartman } 9146a97191SGreg Kroah-Hartman } 9246a97191SGreg Kroah-Hartman static void 9346a97191SGreg Kroah-Hartman kvp_work_func(struct work_struct *dummy) 9446a97191SGreg Kroah-Hartman { 9546a97191SGreg Kroah-Hartman /* 9646a97191SGreg Kroah-Hartman * If the timer fires, the user-mode component has not responded; 9746a97191SGreg Kroah-Hartman * process the pending transaction. 9846a97191SGreg Kroah-Hartman */ 9946a97191SGreg Kroah-Hartman kvp_respond_to_host("Unknown key", "Guest timed out", TIMEOUT_FIRED); 10046a97191SGreg Kroah-Hartman } 10146a97191SGreg Kroah-Hartman 10246a97191SGreg Kroah-Hartman /* 10346a97191SGreg Kroah-Hartman * Callback when data is received from user mode. 10446a97191SGreg Kroah-Hartman */ 10546a97191SGreg Kroah-Hartman 10646a97191SGreg Kroah-Hartman static void 10746a97191SGreg Kroah-Hartman kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) 10846a97191SGreg Kroah-Hartman { 10926403354SK. Y. Srinivasan struct hv_kvp_msg *message; 11026403354SK. Y. Srinivasan struct hv_kvp_msg_enumerate *data; 11146a97191SGreg Kroah-Hartman 11226403354SK. Y. Srinivasan message = (struct hv_kvp_msg *)msg->data; 11326403354SK. Y. Srinivasan if (message->kvp_hdr.operation == KVP_OP_REGISTER) { 11446a97191SGreg Kroah-Hartman pr_info("KVP: user-mode registering done.\n"); 11546a97191SGreg Kroah-Hartman kvp_register(); 11646a97191SGreg Kroah-Hartman } 11746a97191SGreg Kroah-Hartman 11826403354SK. Y. Srinivasan if (message->kvp_hdr.operation == KVP_OP_ENUMERATE) { 11926403354SK. Y. Srinivasan data = &message->body.kvp_enum_data; 12046a97191SGreg Kroah-Hartman /* 12146a97191SGreg Kroah-Hartman * Complete the transaction by forwarding the key value 12246a97191SGreg Kroah-Hartman * to the host. But first, cancel the timeout. 12346a97191SGreg Kroah-Hartman */ 12446a97191SGreg Kroah-Hartman if (cancel_delayed_work_sync(&kvp_work)) 125*e485ceacSK. Y. Srinivasan kvp_respond_to_host(data->data.key, 126*e485ceacSK. Y. Srinivasan data->data.value, 12726403354SK. Y. Srinivasan !strlen(data->data.key)); 12846a97191SGreg Kroah-Hartman } 12946a97191SGreg Kroah-Hartman } 13046a97191SGreg Kroah-Hartman 13146a97191SGreg Kroah-Hartman static void 13246a97191SGreg Kroah-Hartman kvp_send_key(struct work_struct *dummy) 13346a97191SGreg Kroah-Hartman { 13446a97191SGreg Kroah-Hartman struct cn_msg *msg; 13526403354SK. Y. Srinivasan struct hv_kvp_msg *message; 13646a97191SGreg Kroah-Hartman int index = kvp_transaction.index; 13746a97191SGreg Kroah-Hartman 13846a97191SGreg Kroah-Hartman msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC); 13946a97191SGreg Kroah-Hartman 14046a97191SGreg Kroah-Hartman if (msg) { 14146a97191SGreg Kroah-Hartman msg->id.idx = CN_KVP_IDX; 14246a97191SGreg Kroah-Hartman msg->id.val = CN_KVP_VAL; 14326403354SK. Y. Srinivasan 14426403354SK. Y. Srinivasan message = (struct hv_kvp_msg *)msg->data; 14526403354SK. Y. Srinivasan message->kvp_hdr.operation = KVP_OP_ENUMERATE; 14626403354SK. Y. Srinivasan message->body.kvp_enum_data.index = index; 14726403354SK. Y. Srinivasan msg->len = sizeof(struct hv_kvp_msg); 14846a97191SGreg Kroah-Hartman cn_netlink_send(msg, 0, GFP_ATOMIC); 14946a97191SGreg Kroah-Hartman kfree(msg); 15046a97191SGreg Kroah-Hartman } 15146a97191SGreg Kroah-Hartman return; 15246a97191SGreg Kroah-Hartman } 15346a97191SGreg Kroah-Hartman 15446a97191SGreg Kroah-Hartman /* 15546a97191SGreg Kroah-Hartman * Send a response back to the host. 15646a97191SGreg Kroah-Hartman */ 15746a97191SGreg Kroah-Hartman 15846a97191SGreg Kroah-Hartman static void 15946a97191SGreg Kroah-Hartman kvp_respond_to_host(char *key, char *value, int error) 16046a97191SGreg Kroah-Hartman { 16146a97191SGreg Kroah-Hartman struct hv_kvp_msg *kvp_msg; 16246a97191SGreg Kroah-Hartman struct hv_kvp_msg_enumerate *kvp_data; 16346a97191SGreg Kroah-Hartman char *key_name; 16446a97191SGreg Kroah-Hartman struct icmsg_hdr *icmsghdrp; 16546a97191SGreg Kroah-Hartman int keylen, valuelen; 16646a97191SGreg Kroah-Hartman u32 buf_len; 16746a97191SGreg Kroah-Hartman struct vmbus_channel *channel; 16846a97191SGreg Kroah-Hartman u64 req_id; 16946a97191SGreg Kroah-Hartman 17046a97191SGreg Kroah-Hartman /* 17146a97191SGreg Kroah-Hartman * If a transaction is not active; log and return. 17246a97191SGreg Kroah-Hartman */ 17346a97191SGreg Kroah-Hartman 17446a97191SGreg Kroah-Hartman if (!kvp_transaction.active) { 17546a97191SGreg Kroah-Hartman /* 17646a97191SGreg Kroah-Hartman * This is a spurious call! 17746a97191SGreg Kroah-Hartman */ 17846a97191SGreg Kroah-Hartman pr_warn("KVP: Transaction not active\n"); 17946a97191SGreg Kroah-Hartman return; 18046a97191SGreg Kroah-Hartman } 18146a97191SGreg Kroah-Hartman /* 18246a97191SGreg Kroah-Hartman * Copy the global state for completing the transaction. Note that 18346a97191SGreg Kroah-Hartman * only one transaction can be active at a time. 18446a97191SGreg Kroah-Hartman */ 18546a97191SGreg Kroah-Hartman 18646a97191SGreg Kroah-Hartman buf_len = kvp_transaction.recv_len; 18746a97191SGreg Kroah-Hartman channel = kvp_transaction.recv_channel; 18846a97191SGreg Kroah-Hartman req_id = kvp_transaction.recv_req_id; 18946a97191SGreg Kroah-Hartman 19046a97191SGreg Kroah-Hartman kvp_transaction.active = false; 19146a97191SGreg Kroah-Hartman 19246a97191SGreg Kroah-Hartman if (channel->onchannel_callback == NULL) 19346a97191SGreg Kroah-Hartman /* 19446a97191SGreg Kroah-Hartman * We have raced with util driver being unloaded; 19546a97191SGreg Kroah-Hartman * silently return. 19646a97191SGreg Kroah-Hartman */ 19746a97191SGreg Kroah-Hartman return; 19846a97191SGreg Kroah-Hartman 19946a97191SGreg Kroah-Hartman icmsghdrp = (struct icmsg_hdr *) 20046a97191SGreg Kroah-Hartman &recv_buffer[sizeof(struct vmbuspipe_hdr)]; 20146a97191SGreg Kroah-Hartman kvp_msg = (struct hv_kvp_msg *) 20246a97191SGreg Kroah-Hartman &recv_buffer[sizeof(struct vmbuspipe_hdr) + 20346a97191SGreg Kroah-Hartman sizeof(struct icmsg_hdr)]; 20426403354SK. Y. Srinivasan kvp_data = &kvp_msg->body.kvp_enum_data; 20546a97191SGreg Kroah-Hartman key_name = key; 20646a97191SGreg Kroah-Hartman 20746a97191SGreg Kroah-Hartman /* 20846a97191SGreg Kroah-Hartman * If the error parameter is set, terminate the host's enumeration. 20946a97191SGreg Kroah-Hartman */ 21046a97191SGreg Kroah-Hartman if (error) { 21146a97191SGreg Kroah-Hartman /* 21246a97191SGreg Kroah-Hartman * We don't support this index or the we have timedout; 21346a97191SGreg Kroah-Hartman * terminate the host-side iteration by returning an error. 21446a97191SGreg Kroah-Hartman */ 21546a97191SGreg Kroah-Hartman icmsghdrp->status = HV_E_FAIL; 21646a97191SGreg Kroah-Hartman goto response_done; 21746a97191SGreg Kroah-Hartman } 21846a97191SGreg Kroah-Hartman 21946a97191SGreg Kroah-Hartman /* 22046a97191SGreg Kroah-Hartman * The windows host expects the key/value pair to be encoded 22146a97191SGreg Kroah-Hartman * in utf16. 22246a97191SGreg Kroah-Hartman */ 2230720a06aSAlan Stern keylen = utf8s_to_utf16s(key_name, strlen(key_name), UTF16_HOST_ENDIAN, 2240720a06aSAlan Stern (wchar_t *) kvp_data->data.key, 2250720a06aSAlan Stern HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2); 22646a97191SGreg Kroah-Hartman kvp_data->data.key_size = 2*(keylen + 1); /* utf16 encoding */ 2270720a06aSAlan Stern valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN, 2280720a06aSAlan Stern (wchar_t *) kvp_data->data.value, 2290720a06aSAlan Stern HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2); 23046a97191SGreg Kroah-Hartman kvp_data->data.value_size = 2*(valuelen + 1); /* utf16 encoding */ 23146a97191SGreg Kroah-Hartman 23246a97191SGreg Kroah-Hartman kvp_data->data.value_type = REG_SZ; /* all our values are strings */ 23346a97191SGreg Kroah-Hartman icmsghdrp->status = HV_S_OK; 23446a97191SGreg Kroah-Hartman 23546a97191SGreg Kroah-Hartman response_done: 23646a97191SGreg Kroah-Hartman icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; 23746a97191SGreg Kroah-Hartman 23846a97191SGreg Kroah-Hartman vmbus_sendpacket(channel, recv_buffer, buf_len, req_id, 23946a97191SGreg Kroah-Hartman VM_PKT_DATA_INBAND, 0); 24046a97191SGreg Kroah-Hartman 24146a97191SGreg Kroah-Hartman } 24246a97191SGreg Kroah-Hartman 24346a97191SGreg Kroah-Hartman /* 24446a97191SGreg Kroah-Hartman * This callback is invoked when we get a KVP message from the host. 24546a97191SGreg Kroah-Hartman * The host ensures that only one KVP transaction can be active at a time. 24646a97191SGreg Kroah-Hartman * KVP implementation in Linux needs to forward the key to a user-mde 24746a97191SGreg Kroah-Hartman * component to retrive the corresponding value. Consequently, we cannot 24846a97191SGreg Kroah-Hartman * respond to the host in the conext of this callback. Since the host 24946a97191SGreg Kroah-Hartman * guarantees that at most only one transaction can be active at a time, 25046a97191SGreg Kroah-Hartman * we stash away the transaction state in a set of global variables. 25146a97191SGreg Kroah-Hartman */ 25246a97191SGreg Kroah-Hartman 25346a97191SGreg Kroah-Hartman void hv_kvp_onchannelcallback(void *context) 25446a97191SGreg Kroah-Hartman { 25546a97191SGreg Kroah-Hartman struct vmbus_channel *channel = context; 25646a97191SGreg Kroah-Hartman u32 recvlen; 25746a97191SGreg Kroah-Hartman u64 requestid; 25846a97191SGreg Kroah-Hartman 25946a97191SGreg Kroah-Hartman struct hv_kvp_msg *kvp_msg; 26046a97191SGreg Kroah-Hartman struct hv_kvp_msg_enumerate *kvp_data; 26146a97191SGreg Kroah-Hartman 26246a97191SGreg Kroah-Hartman struct icmsg_hdr *icmsghdrp; 26346a97191SGreg Kroah-Hartman struct icmsg_negotiate *negop = NULL; 26446a97191SGreg Kroah-Hartman 26546a97191SGreg Kroah-Hartman 26646a97191SGreg Kroah-Hartman vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid); 26746a97191SGreg Kroah-Hartman 26846a97191SGreg Kroah-Hartman if (recvlen > 0) { 26946a97191SGreg Kroah-Hartman icmsghdrp = (struct icmsg_hdr *)&recv_buffer[ 27046a97191SGreg Kroah-Hartman sizeof(struct vmbuspipe_hdr)]; 27146a97191SGreg Kroah-Hartman 27246a97191SGreg Kroah-Hartman if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 273da0e9631SGreg Kroah-Hartman vmbus_prep_negotiate_resp(icmsghdrp, negop, recv_buffer); 27446a97191SGreg Kroah-Hartman } else { 27546a97191SGreg Kroah-Hartman kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ 27646a97191SGreg Kroah-Hartman sizeof(struct vmbuspipe_hdr) + 27746a97191SGreg Kroah-Hartman sizeof(struct icmsg_hdr)]; 27846a97191SGreg Kroah-Hartman 27926403354SK. Y. Srinivasan kvp_data = &kvp_msg->body.kvp_enum_data; 28046a97191SGreg Kroah-Hartman 28146a97191SGreg Kroah-Hartman /* 28246a97191SGreg Kroah-Hartman * We only support the "get" operation on 28346a97191SGreg Kroah-Hartman * "KVP_POOL_AUTO" pool. 28446a97191SGreg Kroah-Hartman */ 28546a97191SGreg Kroah-Hartman 28646a97191SGreg Kroah-Hartman if ((kvp_msg->kvp_hdr.pool != KVP_POOL_AUTO) || 28746a97191SGreg Kroah-Hartman (kvp_msg->kvp_hdr.operation != 28846a97191SGreg Kroah-Hartman KVP_OP_ENUMERATE)) { 28946a97191SGreg Kroah-Hartman icmsghdrp->status = HV_E_FAIL; 29046a97191SGreg Kroah-Hartman goto callback_done; 29146a97191SGreg Kroah-Hartman } 29246a97191SGreg Kroah-Hartman 29346a97191SGreg Kroah-Hartman /* 29446a97191SGreg Kroah-Hartman * Stash away this global state for completing the 29546a97191SGreg Kroah-Hartman * transaction; note transactions are serialized. 29646a97191SGreg Kroah-Hartman */ 29746a97191SGreg Kroah-Hartman kvp_transaction.recv_len = recvlen; 29846a97191SGreg Kroah-Hartman kvp_transaction.recv_channel = channel; 29946a97191SGreg Kroah-Hartman kvp_transaction.recv_req_id = requestid; 30046a97191SGreg Kroah-Hartman kvp_transaction.active = true; 30146a97191SGreg Kroah-Hartman kvp_transaction.index = kvp_data->index; 30246a97191SGreg Kroah-Hartman 30346a97191SGreg Kroah-Hartman /* 30446a97191SGreg Kroah-Hartman * Get the information from the 30546a97191SGreg Kroah-Hartman * user-mode component. 30646a97191SGreg Kroah-Hartman * component. This transaction will be 30746a97191SGreg Kroah-Hartman * completed when we get the value from 30846a97191SGreg Kroah-Hartman * the user-mode component. 30946a97191SGreg Kroah-Hartman * Set a timeout to deal with 31046a97191SGreg Kroah-Hartman * user-mode not responding. 31146a97191SGreg Kroah-Hartman */ 31246a97191SGreg Kroah-Hartman schedule_work(&kvp_sendkey_work); 31346a97191SGreg Kroah-Hartman schedule_delayed_work(&kvp_work, 5*HZ); 31446a97191SGreg Kroah-Hartman 31546a97191SGreg Kroah-Hartman return; 31646a97191SGreg Kroah-Hartman 31746a97191SGreg Kroah-Hartman } 31846a97191SGreg Kroah-Hartman 31946a97191SGreg Kroah-Hartman callback_done: 32046a97191SGreg Kroah-Hartman 32146a97191SGreg Kroah-Hartman icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION 32246a97191SGreg Kroah-Hartman | ICMSGHDRFLAG_RESPONSE; 32346a97191SGreg Kroah-Hartman 32446a97191SGreg Kroah-Hartman vmbus_sendpacket(channel, recv_buffer, 32546a97191SGreg Kroah-Hartman recvlen, requestid, 32646a97191SGreg Kroah-Hartman VM_PKT_DATA_INBAND, 0); 32746a97191SGreg Kroah-Hartman } 32846a97191SGreg Kroah-Hartman 32946a97191SGreg Kroah-Hartman } 33046a97191SGreg Kroah-Hartman 33146a97191SGreg Kroah-Hartman int 33246a97191SGreg Kroah-Hartman hv_kvp_init(struct hv_util_service *srv) 33346a97191SGreg Kroah-Hartman { 33446a97191SGreg Kroah-Hartman int err; 33546a97191SGreg Kroah-Hartman 33646a97191SGreg Kroah-Hartman err = cn_add_callback(&kvp_id, kvp_name, kvp_cn_callback); 33746a97191SGreg Kroah-Hartman if (err) 33846a97191SGreg Kroah-Hartman return err; 33946a97191SGreg Kroah-Hartman recv_buffer = srv->recv_buffer; 34046a97191SGreg Kroah-Hartman 34146a97191SGreg Kroah-Hartman return 0; 34246a97191SGreg Kroah-Hartman } 34346a97191SGreg Kroah-Hartman 34446a97191SGreg Kroah-Hartman void hv_kvp_deinit(void) 34546a97191SGreg Kroah-Hartman { 34646a97191SGreg Kroah-Hartman cn_del_callback(&kvp_id); 34746a97191SGreg Kroah-Hartman cancel_delayed_work_sync(&kvp_work); 34846a97191SGreg Kroah-Hartman cancel_work_sync(&kvp_sendkey_work); 34946a97191SGreg Kroah-Hartman } 350