1*46a97191SGreg Kroah-Hartman /* 2*46a97191SGreg Kroah-Hartman * An implementation of key value pair (KVP) functionality for Linux. 3*46a97191SGreg Kroah-Hartman * 4*46a97191SGreg Kroah-Hartman * 5*46a97191SGreg Kroah-Hartman * Copyright (C) 2010, Novell, Inc. 6*46a97191SGreg Kroah-Hartman * Author : K. Y. Srinivasan <ksrinivasan@novell.com> 7*46a97191SGreg Kroah-Hartman * 8*46a97191SGreg Kroah-Hartman * This program is free software; you can redistribute it and/or modify it 9*46a97191SGreg Kroah-Hartman * under the terms of the GNU General Public License version 2 as published 10*46a97191SGreg Kroah-Hartman * by the Free Software Foundation. 11*46a97191SGreg Kroah-Hartman * 12*46a97191SGreg Kroah-Hartman * This program is distributed in the hope that it will be useful, but 13*46a97191SGreg Kroah-Hartman * WITHOUT ANY WARRANTY; without even the implied warranty of 14*46a97191SGreg Kroah-Hartman * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 15*46a97191SGreg Kroah-Hartman * NON INFRINGEMENT. See the GNU General Public License for more 16*46a97191SGreg Kroah-Hartman * details. 17*46a97191SGreg Kroah-Hartman * 18*46a97191SGreg Kroah-Hartman * You should have received a copy of the GNU General Public License 19*46a97191SGreg Kroah-Hartman * along with this program; if not, write to the Free Software 20*46a97191SGreg Kroah-Hartman * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21*46a97191SGreg Kroah-Hartman * 22*46a97191SGreg Kroah-Hartman */ 23*46a97191SGreg Kroah-Hartman #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 24*46a97191SGreg Kroah-Hartman 25*46a97191SGreg Kroah-Hartman #include <linux/net.h> 26*46a97191SGreg Kroah-Hartman #include <linux/nls.h> 27*46a97191SGreg Kroah-Hartman #include <linux/connector.h> 28*46a97191SGreg Kroah-Hartman #include <linux/workqueue.h> 29*46a97191SGreg Kroah-Hartman #include <linux/hyperv.h> 30*46a97191SGreg Kroah-Hartman 31*46a97191SGreg Kroah-Hartman #include "hv_kvp.h" 32*46a97191SGreg Kroah-Hartman 33*46a97191SGreg Kroah-Hartman 34*46a97191SGreg Kroah-Hartman 35*46a97191SGreg Kroah-Hartman /* 36*46a97191SGreg Kroah-Hartman * Global state maintained for transaction that is being processed. 37*46a97191SGreg Kroah-Hartman * Note that only one transaction can be active at any point in time. 38*46a97191SGreg Kroah-Hartman * 39*46a97191SGreg Kroah-Hartman * This state is set when we receive a request from the host; we 40*46a97191SGreg Kroah-Hartman * cleanup this state when the transaction is completed - when we respond 41*46a97191SGreg Kroah-Hartman * to the host with the key value. 42*46a97191SGreg Kroah-Hartman */ 43*46a97191SGreg Kroah-Hartman 44*46a97191SGreg Kroah-Hartman static struct { 45*46a97191SGreg Kroah-Hartman bool active; /* transaction status - active or not */ 46*46a97191SGreg Kroah-Hartman int recv_len; /* number of bytes received. */ 47*46a97191SGreg Kroah-Hartman int index; /* current index */ 48*46a97191SGreg Kroah-Hartman struct vmbus_channel *recv_channel; /* chn we got the request */ 49*46a97191SGreg Kroah-Hartman u64 recv_req_id; /* request ID. */ 50*46a97191SGreg Kroah-Hartman } kvp_transaction; 51*46a97191SGreg Kroah-Hartman 52*46a97191SGreg Kroah-Hartman static void kvp_send_key(struct work_struct *dummy); 53*46a97191SGreg Kroah-Hartman 54*46a97191SGreg Kroah-Hartman #define TIMEOUT_FIRED 1 55*46a97191SGreg Kroah-Hartman 56*46a97191SGreg Kroah-Hartman static void kvp_respond_to_host(char *key, char *value, int error); 57*46a97191SGreg Kroah-Hartman static void kvp_work_func(struct work_struct *dummy); 58*46a97191SGreg Kroah-Hartman static void kvp_register(void); 59*46a97191SGreg Kroah-Hartman 60*46a97191SGreg Kroah-Hartman static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func); 61*46a97191SGreg Kroah-Hartman static DECLARE_WORK(kvp_sendkey_work, kvp_send_key); 62*46a97191SGreg Kroah-Hartman 63*46a97191SGreg Kroah-Hartman static struct cb_id kvp_id = { CN_KVP_IDX, CN_KVP_VAL }; 64*46a97191SGreg Kroah-Hartman static const char kvp_name[] = "kvp_kernel_module"; 65*46a97191SGreg Kroah-Hartman static u8 *recv_buffer; 66*46a97191SGreg Kroah-Hartman /* 67*46a97191SGreg Kroah-Hartman * Register the kernel component with the user-level daemon. 68*46a97191SGreg Kroah-Hartman * As part of this registration, pass the LIC version number. 69*46a97191SGreg Kroah-Hartman */ 70*46a97191SGreg Kroah-Hartman 71*46a97191SGreg Kroah-Hartman static void 72*46a97191SGreg Kroah-Hartman kvp_register(void) 73*46a97191SGreg Kroah-Hartman { 74*46a97191SGreg Kroah-Hartman 75*46a97191SGreg Kroah-Hartman struct cn_msg *msg; 76*46a97191SGreg Kroah-Hartman 77*46a97191SGreg Kroah-Hartman msg = kzalloc(sizeof(*msg) + strlen(HV_DRV_VERSION) + 1 , GFP_ATOMIC); 78*46a97191SGreg Kroah-Hartman 79*46a97191SGreg Kroah-Hartman if (msg) { 80*46a97191SGreg Kroah-Hartman msg->id.idx = CN_KVP_IDX; 81*46a97191SGreg Kroah-Hartman msg->id.val = CN_KVP_VAL; 82*46a97191SGreg Kroah-Hartman msg->seq = KVP_REGISTER; 83*46a97191SGreg Kroah-Hartman strcpy(msg->data, HV_DRV_VERSION); 84*46a97191SGreg Kroah-Hartman msg->len = strlen(HV_DRV_VERSION) + 1; 85*46a97191SGreg Kroah-Hartman cn_netlink_send(msg, 0, GFP_ATOMIC); 86*46a97191SGreg Kroah-Hartman kfree(msg); 87*46a97191SGreg Kroah-Hartman } 88*46a97191SGreg Kroah-Hartman } 89*46a97191SGreg Kroah-Hartman static void 90*46a97191SGreg Kroah-Hartman kvp_work_func(struct work_struct *dummy) 91*46a97191SGreg Kroah-Hartman { 92*46a97191SGreg Kroah-Hartman /* 93*46a97191SGreg Kroah-Hartman * If the timer fires, the user-mode component has not responded; 94*46a97191SGreg Kroah-Hartman * process the pending transaction. 95*46a97191SGreg Kroah-Hartman */ 96*46a97191SGreg Kroah-Hartman kvp_respond_to_host("Unknown key", "Guest timed out", TIMEOUT_FIRED); 97*46a97191SGreg Kroah-Hartman } 98*46a97191SGreg Kroah-Hartman 99*46a97191SGreg Kroah-Hartman /* 100*46a97191SGreg Kroah-Hartman * Callback when data is received from user mode. 101*46a97191SGreg Kroah-Hartman */ 102*46a97191SGreg Kroah-Hartman 103*46a97191SGreg Kroah-Hartman static void 104*46a97191SGreg Kroah-Hartman kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) 105*46a97191SGreg Kroah-Hartman { 106*46a97191SGreg Kroah-Hartman struct hv_ku_msg *message; 107*46a97191SGreg Kroah-Hartman 108*46a97191SGreg Kroah-Hartman message = (struct hv_ku_msg *)msg->data; 109*46a97191SGreg Kroah-Hartman if (msg->seq == KVP_REGISTER) { 110*46a97191SGreg Kroah-Hartman pr_info("KVP: user-mode registering done.\n"); 111*46a97191SGreg Kroah-Hartman kvp_register(); 112*46a97191SGreg Kroah-Hartman } 113*46a97191SGreg Kroah-Hartman 114*46a97191SGreg Kroah-Hartman if (msg->seq == KVP_USER_SET) { 115*46a97191SGreg Kroah-Hartman /* 116*46a97191SGreg Kroah-Hartman * Complete the transaction by forwarding the key value 117*46a97191SGreg Kroah-Hartman * to the host. But first, cancel the timeout. 118*46a97191SGreg Kroah-Hartman */ 119*46a97191SGreg Kroah-Hartman if (cancel_delayed_work_sync(&kvp_work)) 120*46a97191SGreg Kroah-Hartman kvp_respond_to_host(message->kvp_key, 121*46a97191SGreg Kroah-Hartman message->kvp_value, 122*46a97191SGreg Kroah-Hartman !strlen(message->kvp_key)); 123*46a97191SGreg Kroah-Hartman } 124*46a97191SGreg Kroah-Hartman } 125*46a97191SGreg Kroah-Hartman 126*46a97191SGreg Kroah-Hartman static void 127*46a97191SGreg Kroah-Hartman kvp_send_key(struct work_struct *dummy) 128*46a97191SGreg Kroah-Hartman { 129*46a97191SGreg Kroah-Hartman struct cn_msg *msg; 130*46a97191SGreg Kroah-Hartman int index = kvp_transaction.index; 131*46a97191SGreg Kroah-Hartman 132*46a97191SGreg Kroah-Hartman msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC); 133*46a97191SGreg Kroah-Hartman 134*46a97191SGreg Kroah-Hartman if (msg) { 135*46a97191SGreg Kroah-Hartman msg->id.idx = CN_KVP_IDX; 136*46a97191SGreg Kroah-Hartman msg->id.val = CN_KVP_VAL; 137*46a97191SGreg Kroah-Hartman msg->seq = KVP_KERNEL_GET; 138*46a97191SGreg Kroah-Hartman ((struct hv_ku_msg *)msg->data)->kvp_index = index; 139*46a97191SGreg Kroah-Hartman msg->len = sizeof(struct hv_ku_msg); 140*46a97191SGreg Kroah-Hartman cn_netlink_send(msg, 0, GFP_ATOMIC); 141*46a97191SGreg Kroah-Hartman kfree(msg); 142*46a97191SGreg Kroah-Hartman } 143*46a97191SGreg Kroah-Hartman return; 144*46a97191SGreg Kroah-Hartman } 145*46a97191SGreg Kroah-Hartman 146*46a97191SGreg Kroah-Hartman /* 147*46a97191SGreg Kroah-Hartman * Send a response back to the host. 148*46a97191SGreg Kroah-Hartman */ 149*46a97191SGreg Kroah-Hartman 150*46a97191SGreg Kroah-Hartman static void 151*46a97191SGreg Kroah-Hartman kvp_respond_to_host(char *key, char *value, int error) 152*46a97191SGreg Kroah-Hartman { 153*46a97191SGreg Kroah-Hartman struct hv_kvp_msg *kvp_msg; 154*46a97191SGreg Kroah-Hartman struct hv_kvp_msg_enumerate *kvp_data; 155*46a97191SGreg Kroah-Hartman char *key_name; 156*46a97191SGreg Kroah-Hartman struct icmsg_hdr *icmsghdrp; 157*46a97191SGreg Kroah-Hartman int keylen, valuelen; 158*46a97191SGreg Kroah-Hartman u32 buf_len; 159*46a97191SGreg Kroah-Hartman struct vmbus_channel *channel; 160*46a97191SGreg Kroah-Hartman u64 req_id; 161*46a97191SGreg Kroah-Hartman 162*46a97191SGreg Kroah-Hartman /* 163*46a97191SGreg Kroah-Hartman * If a transaction is not active; log and return. 164*46a97191SGreg Kroah-Hartman */ 165*46a97191SGreg Kroah-Hartman 166*46a97191SGreg Kroah-Hartman if (!kvp_transaction.active) { 167*46a97191SGreg Kroah-Hartman /* 168*46a97191SGreg Kroah-Hartman * This is a spurious call! 169*46a97191SGreg Kroah-Hartman */ 170*46a97191SGreg Kroah-Hartman pr_warn("KVP: Transaction not active\n"); 171*46a97191SGreg Kroah-Hartman return; 172*46a97191SGreg Kroah-Hartman } 173*46a97191SGreg Kroah-Hartman /* 174*46a97191SGreg Kroah-Hartman * Copy the global state for completing the transaction. Note that 175*46a97191SGreg Kroah-Hartman * only one transaction can be active at a time. 176*46a97191SGreg Kroah-Hartman */ 177*46a97191SGreg Kroah-Hartman 178*46a97191SGreg Kroah-Hartman buf_len = kvp_transaction.recv_len; 179*46a97191SGreg Kroah-Hartman channel = kvp_transaction.recv_channel; 180*46a97191SGreg Kroah-Hartman req_id = kvp_transaction.recv_req_id; 181*46a97191SGreg Kroah-Hartman 182*46a97191SGreg Kroah-Hartman kvp_transaction.active = false; 183*46a97191SGreg Kroah-Hartman 184*46a97191SGreg Kroah-Hartman if (channel->onchannel_callback == NULL) 185*46a97191SGreg Kroah-Hartman /* 186*46a97191SGreg Kroah-Hartman * We have raced with util driver being unloaded; 187*46a97191SGreg Kroah-Hartman * silently return. 188*46a97191SGreg Kroah-Hartman */ 189*46a97191SGreg Kroah-Hartman return; 190*46a97191SGreg Kroah-Hartman 191*46a97191SGreg Kroah-Hartman icmsghdrp = (struct icmsg_hdr *) 192*46a97191SGreg Kroah-Hartman &recv_buffer[sizeof(struct vmbuspipe_hdr)]; 193*46a97191SGreg Kroah-Hartman kvp_msg = (struct hv_kvp_msg *) 194*46a97191SGreg Kroah-Hartman &recv_buffer[sizeof(struct vmbuspipe_hdr) + 195*46a97191SGreg Kroah-Hartman sizeof(struct icmsg_hdr)]; 196*46a97191SGreg Kroah-Hartman kvp_data = &kvp_msg->kvp_data; 197*46a97191SGreg Kroah-Hartman key_name = key; 198*46a97191SGreg Kroah-Hartman 199*46a97191SGreg Kroah-Hartman /* 200*46a97191SGreg Kroah-Hartman * If the error parameter is set, terminate the host's enumeration. 201*46a97191SGreg Kroah-Hartman */ 202*46a97191SGreg Kroah-Hartman if (error) { 203*46a97191SGreg Kroah-Hartman /* 204*46a97191SGreg Kroah-Hartman * We don't support this index or the we have timedout; 205*46a97191SGreg Kroah-Hartman * terminate the host-side iteration by returning an error. 206*46a97191SGreg Kroah-Hartman */ 207*46a97191SGreg Kroah-Hartman icmsghdrp->status = HV_E_FAIL; 208*46a97191SGreg Kroah-Hartman goto response_done; 209*46a97191SGreg Kroah-Hartman } 210*46a97191SGreg Kroah-Hartman 211*46a97191SGreg Kroah-Hartman /* 212*46a97191SGreg Kroah-Hartman * The windows host expects the key/value pair to be encoded 213*46a97191SGreg Kroah-Hartman * in utf16. 214*46a97191SGreg Kroah-Hartman */ 215*46a97191SGreg Kroah-Hartman keylen = utf8s_to_utf16s(key_name, strlen(key_name), 216*46a97191SGreg Kroah-Hartman (wchar_t *)kvp_data->data.key); 217*46a97191SGreg Kroah-Hartman kvp_data->data.key_size = 2*(keylen + 1); /* utf16 encoding */ 218*46a97191SGreg Kroah-Hartman valuelen = utf8s_to_utf16s(value, strlen(value), 219*46a97191SGreg Kroah-Hartman (wchar_t *)kvp_data->data.value); 220*46a97191SGreg Kroah-Hartman kvp_data->data.value_size = 2*(valuelen + 1); /* utf16 encoding */ 221*46a97191SGreg Kroah-Hartman 222*46a97191SGreg Kroah-Hartman kvp_data->data.value_type = REG_SZ; /* all our values are strings */ 223*46a97191SGreg Kroah-Hartman icmsghdrp->status = HV_S_OK; 224*46a97191SGreg Kroah-Hartman 225*46a97191SGreg Kroah-Hartman response_done: 226*46a97191SGreg Kroah-Hartman icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; 227*46a97191SGreg Kroah-Hartman 228*46a97191SGreg Kroah-Hartman vmbus_sendpacket(channel, recv_buffer, buf_len, req_id, 229*46a97191SGreg Kroah-Hartman VM_PKT_DATA_INBAND, 0); 230*46a97191SGreg Kroah-Hartman 231*46a97191SGreg Kroah-Hartman } 232*46a97191SGreg Kroah-Hartman 233*46a97191SGreg Kroah-Hartman /* 234*46a97191SGreg Kroah-Hartman * This callback is invoked when we get a KVP message from the host. 235*46a97191SGreg Kroah-Hartman * The host ensures that only one KVP transaction can be active at a time. 236*46a97191SGreg Kroah-Hartman * KVP implementation in Linux needs to forward the key to a user-mde 237*46a97191SGreg Kroah-Hartman * component to retrive the corresponding value. Consequently, we cannot 238*46a97191SGreg Kroah-Hartman * respond to the host in the conext of this callback. Since the host 239*46a97191SGreg Kroah-Hartman * guarantees that at most only one transaction can be active at a time, 240*46a97191SGreg Kroah-Hartman * we stash away the transaction state in a set of global variables. 241*46a97191SGreg Kroah-Hartman */ 242*46a97191SGreg Kroah-Hartman 243*46a97191SGreg Kroah-Hartman void hv_kvp_onchannelcallback(void *context) 244*46a97191SGreg Kroah-Hartman { 245*46a97191SGreg Kroah-Hartman struct vmbus_channel *channel = context; 246*46a97191SGreg Kroah-Hartman u32 recvlen; 247*46a97191SGreg Kroah-Hartman u64 requestid; 248*46a97191SGreg Kroah-Hartman 249*46a97191SGreg Kroah-Hartman struct hv_kvp_msg *kvp_msg; 250*46a97191SGreg Kroah-Hartman struct hv_kvp_msg_enumerate *kvp_data; 251*46a97191SGreg Kroah-Hartman 252*46a97191SGreg Kroah-Hartman struct icmsg_hdr *icmsghdrp; 253*46a97191SGreg Kroah-Hartman struct icmsg_negotiate *negop = NULL; 254*46a97191SGreg Kroah-Hartman 255*46a97191SGreg Kroah-Hartman 256*46a97191SGreg Kroah-Hartman vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid); 257*46a97191SGreg Kroah-Hartman 258*46a97191SGreg Kroah-Hartman if (recvlen > 0) { 259*46a97191SGreg Kroah-Hartman icmsghdrp = (struct icmsg_hdr *)&recv_buffer[ 260*46a97191SGreg Kroah-Hartman sizeof(struct vmbuspipe_hdr)]; 261*46a97191SGreg Kroah-Hartman 262*46a97191SGreg Kroah-Hartman if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 263*46a97191SGreg Kroah-Hartman prep_negotiate_resp(icmsghdrp, negop, recv_buffer); 264*46a97191SGreg Kroah-Hartman } else { 265*46a97191SGreg Kroah-Hartman kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ 266*46a97191SGreg Kroah-Hartman sizeof(struct vmbuspipe_hdr) + 267*46a97191SGreg Kroah-Hartman sizeof(struct icmsg_hdr)]; 268*46a97191SGreg Kroah-Hartman 269*46a97191SGreg Kroah-Hartman kvp_data = &kvp_msg->kvp_data; 270*46a97191SGreg Kroah-Hartman 271*46a97191SGreg Kroah-Hartman /* 272*46a97191SGreg Kroah-Hartman * We only support the "get" operation on 273*46a97191SGreg Kroah-Hartman * "KVP_POOL_AUTO" pool. 274*46a97191SGreg Kroah-Hartman */ 275*46a97191SGreg Kroah-Hartman 276*46a97191SGreg Kroah-Hartman if ((kvp_msg->kvp_hdr.pool != KVP_POOL_AUTO) || 277*46a97191SGreg Kroah-Hartman (kvp_msg->kvp_hdr.operation != 278*46a97191SGreg Kroah-Hartman KVP_OP_ENUMERATE)) { 279*46a97191SGreg Kroah-Hartman icmsghdrp->status = HV_E_FAIL; 280*46a97191SGreg Kroah-Hartman goto callback_done; 281*46a97191SGreg Kroah-Hartman } 282*46a97191SGreg Kroah-Hartman 283*46a97191SGreg Kroah-Hartman /* 284*46a97191SGreg Kroah-Hartman * Stash away this global state for completing the 285*46a97191SGreg Kroah-Hartman * transaction; note transactions are serialized. 286*46a97191SGreg Kroah-Hartman */ 287*46a97191SGreg Kroah-Hartman kvp_transaction.recv_len = recvlen; 288*46a97191SGreg Kroah-Hartman kvp_transaction.recv_channel = channel; 289*46a97191SGreg Kroah-Hartman kvp_transaction.recv_req_id = requestid; 290*46a97191SGreg Kroah-Hartman kvp_transaction.active = true; 291*46a97191SGreg Kroah-Hartman kvp_transaction.index = kvp_data->index; 292*46a97191SGreg Kroah-Hartman 293*46a97191SGreg Kroah-Hartman /* 294*46a97191SGreg Kroah-Hartman * Get the information from the 295*46a97191SGreg Kroah-Hartman * user-mode component. 296*46a97191SGreg Kroah-Hartman * component. This transaction will be 297*46a97191SGreg Kroah-Hartman * completed when we get the value from 298*46a97191SGreg Kroah-Hartman * the user-mode component. 299*46a97191SGreg Kroah-Hartman * Set a timeout to deal with 300*46a97191SGreg Kroah-Hartman * user-mode not responding. 301*46a97191SGreg Kroah-Hartman */ 302*46a97191SGreg Kroah-Hartman schedule_work(&kvp_sendkey_work); 303*46a97191SGreg Kroah-Hartman schedule_delayed_work(&kvp_work, 5*HZ); 304*46a97191SGreg Kroah-Hartman 305*46a97191SGreg Kroah-Hartman return; 306*46a97191SGreg Kroah-Hartman 307*46a97191SGreg Kroah-Hartman } 308*46a97191SGreg Kroah-Hartman 309*46a97191SGreg Kroah-Hartman callback_done: 310*46a97191SGreg Kroah-Hartman 311*46a97191SGreg Kroah-Hartman icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION 312*46a97191SGreg Kroah-Hartman | ICMSGHDRFLAG_RESPONSE; 313*46a97191SGreg Kroah-Hartman 314*46a97191SGreg Kroah-Hartman vmbus_sendpacket(channel, recv_buffer, 315*46a97191SGreg Kroah-Hartman recvlen, requestid, 316*46a97191SGreg Kroah-Hartman VM_PKT_DATA_INBAND, 0); 317*46a97191SGreg Kroah-Hartman } 318*46a97191SGreg Kroah-Hartman 319*46a97191SGreg Kroah-Hartman } 320*46a97191SGreg Kroah-Hartman 321*46a97191SGreg Kroah-Hartman int 322*46a97191SGreg Kroah-Hartman hv_kvp_init(struct hv_util_service *srv) 323*46a97191SGreg Kroah-Hartman { 324*46a97191SGreg Kroah-Hartman int err; 325*46a97191SGreg Kroah-Hartman 326*46a97191SGreg Kroah-Hartman err = cn_add_callback(&kvp_id, kvp_name, kvp_cn_callback); 327*46a97191SGreg Kroah-Hartman if (err) 328*46a97191SGreg Kroah-Hartman return err; 329*46a97191SGreg Kroah-Hartman recv_buffer = srv->recv_buffer; 330*46a97191SGreg Kroah-Hartman 331*46a97191SGreg Kroah-Hartman return 0; 332*46a97191SGreg Kroah-Hartman } 333*46a97191SGreg Kroah-Hartman 334*46a97191SGreg Kroah-Hartman void hv_kvp_deinit(void) 335*46a97191SGreg Kroah-Hartman { 336*46a97191SGreg Kroah-Hartman cn_del_callback(&kvp_id); 337*46a97191SGreg Kroah-Hartman cancel_delayed_work_sync(&kvp_work); 338*46a97191SGreg Kroah-Hartman cancel_work_sync(&kvp_sendkey_work); 339*46a97191SGreg Kroah-Hartman } 340