1 /* 2 * An implementation of key value pair (KVP) functionality for Linux. 3 * 4 * 5 * Copyright (C) 2010, Novell, Inc. 6 * Author : K. Y. Srinivasan <ksrinivasan@novell.com> 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License version 2 as published 10 * by the Free Software Foundation. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 15 * NON INFRINGEMENT. See the GNU General Public License for more 16 * details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 */ 23 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 24 25 #include <linux/net.h> 26 #include <linux/nls.h> 27 #include <linux/connector.h> 28 #include <linux/workqueue.h> 29 #include <linux/hyperv.h> 30 31 32 /* 33 * Pre win8 version numbers used in ws2008 and ws 2008 r2 (win7) 34 */ 35 #define WS2008_SRV_MAJOR 1 36 #define WS2008_SRV_MINOR 0 37 #define WS2008_SRV_VERSION (WS2008_SRV_MAJOR << 16 | WS2008_SRV_MINOR) 38 39 #define WIN7_SRV_MAJOR 3 40 #define WIN7_SRV_MINOR 0 41 #define WIN7_SRV_VERSION (WIN7_SRV_MAJOR << 16 | WIN7_SRV_MINOR) 42 43 #define WIN8_SRV_MAJOR 4 44 #define WIN8_SRV_MINOR 0 45 #define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR) 46 47 /* 48 * Global state maintained for transaction that is being processed. 49 * Note that only one transaction can be active at any point in time. 50 * 51 * This state is set when we receive a request from the host; we 52 * cleanup this state when the transaction is completed - when we respond 53 * to the host with the key value. 54 */ 55 56 static struct { 57 bool active; /* transaction status - active or not */ 58 int recv_len; /* number of bytes received. */ 59 struct hv_kvp_msg *kvp_msg; /* current message */ 60 struct vmbus_channel *recv_channel; /* chn we got the request */ 61 u64 recv_req_id; /* request ID. */ 62 void *kvp_context; /* for the channel callback */ 63 } kvp_transaction; 64 65 /* 66 * Before we can accept KVP messages from the host, we need 67 * to handshake with the user level daemon. This state tracks 68 * if we are in the handshake phase. 69 */ 70 static bool in_hand_shake = true; 71 72 /* 73 * This state maintains the version number registered by the daemon. 74 */ 75 static int dm_reg_value; 76 77 static void kvp_send_key(struct work_struct *dummy); 78 79 80 static void kvp_respond_to_host(struct hv_kvp_msg *msg, int error); 81 static void kvp_work_func(struct work_struct *dummy); 82 static void kvp_register(int); 83 84 static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func); 85 static DECLARE_WORK(kvp_sendkey_work, kvp_send_key); 86 87 static struct cb_id kvp_id = { CN_KVP_IDX, CN_KVP_VAL }; 88 static const char kvp_name[] = "kvp_kernel_module"; 89 static u8 *recv_buffer; 90 /* 91 * Register the kernel component with the user-level daemon. 92 * As part of this registration, pass the LIC version number. 93 * This number has no meaning, it satisfies the registration protocol. 94 */ 95 #define HV_DRV_VERSION "3.1" 96 97 static void 98 kvp_register(int reg_value) 99 { 100 101 struct cn_msg *msg; 102 struct hv_kvp_msg *kvp_msg; 103 char *version; 104 105 msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg), GFP_ATOMIC); 106 107 if (msg) { 108 kvp_msg = (struct hv_kvp_msg *)msg->data; 109 version = kvp_msg->body.kvp_register.version; 110 msg->id.idx = CN_KVP_IDX; 111 msg->id.val = CN_KVP_VAL; 112 113 kvp_msg->kvp_hdr.operation = reg_value; 114 strcpy(version, HV_DRV_VERSION); 115 msg->len = sizeof(struct hv_kvp_msg); 116 cn_netlink_send(msg, 0, GFP_ATOMIC); 117 kfree(msg); 118 } 119 } 120 static void 121 kvp_work_func(struct work_struct *dummy) 122 { 123 /* 124 * If the timer fires, the user-mode component has not responded; 125 * process the pending transaction. 126 */ 127 kvp_respond_to_host(NULL, HV_E_FAIL); 128 } 129 130 static int kvp_handle_handshake(struct hv_kvp_msg *msg) 131 { 132 int ret = 1; 133 134 switch (msg->kvp_hdr.operation) { 135 case KVP_OP_REGISTER: 136 dm_reg_value = KVP_OP_REGISTER; 137 pr_info("KVP: IP injection functionality not available\n"); 138 pr_info("KVP: Upgrade the KVP daemon\n"); 139 break; 140 case KVP_OP_REGISTER1: 141 dm_reg_value = KVP_OP_REGISTER1; 142 break; 143 default: 144 pr_info("KVP: incompatible daemon\n"); 145 pr_info("KVP: KVP version: %d, Daemon version: %d\n", 146 KVP_OP_REGISTER1, msg->kvp_hdr.operation); 147 ret = 0; 148 } 149 150 if (ret) { 151 /* 152 * We have a compatible daemon; complete the handshake. 153 */ 154 pr_info("KVP: user-mode registering done.\n"); 155 kvp_register(dm_reg_value); 156 kvp_transaction.active = false; 157 if (kvp_transaction.kvp_context) 158 hv_kvp_onchannelcallback(kvp_transaction.kvp_context); 159 } 160 return ret; 161 } 162 163 164 /* 165 * Callback when data is received from user mode. 166 */ 167 168 static void 169 kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) 170 { 171 struct hv_kvp_msg *message; 172 struct hv_kvp_msg_enumerate *data; 173 int error = 0; 174 175 message = (struct hv_kvp_msg *)msg->data; 176 177 /* 178 * If we are negotiating the version information 179 * with the daemon; handle that first. 180 */ 181 182 if (in_hand_shake) { 183 if (kvp_handle_handshake(message)) 184 in_hand_shake = false; 185 return; 186 } 187 188 /* 189 * Based on the version of the daemon, we propagate errors from the 190 * daemon differently. 191 */ 192 193 data = &message->body.kvp_enum_data; 194 195 switch (dm_reg_value) { 196 case KVP_OP_REGISTER: 197 /* 198 * Null string is used to pass back error condition. 199 */ 200 if (data->data.key[0] == 0) 201 error = HV_S_CONT; 202 break; 203 204 case KVP_OP_REGISTER1: 205 /* 206 * We use the message header information from 207 * the user level daemon to transmit errors. 208 */ 209 error = message->error; 210 break; 211 } 212 213 /* 214 * Complete the transaction by forwarding the key value 215 * to the host. But first, cancel the timeout. 216 */ 217 if (cancel_delayed_work_sync(&kvp_work)) 218 kvp_respond_to_host(message, error); 219 } 220 221 222 static int process_ob_ipinfo(void *in_msg, void *out_msg, int op) 223 { 224 struct hv_kvp_msg *in = in_msg; 225 struct hv_kvp_ip_msg *out = out_msg; 226 int len; 227 228 switch (op) { 229 case KVP_OP_GET_IP_INFO: 230 /* 231 * Transform all parameters into utf16 encoding. 232 */ 233 len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.ip_addr, 234 strlen((char *)in->body.kvp_ip_val.ip_addr), 235 UTF16_HOST_ENDIAN, 236 (wchar_t *)out->kvp_ip_val.ip_addr, 237 MAX_IP_ADDR_SIZE); 238 if (len < 0) 239 return len; 240 241 len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.sub_net, 242 strlen((char *)in->body.kvp_ip_val.sub_net), 243 UTF16_HOST_ENDIAN, 244 (wchar_t *)out->kvp_ip_val.sub_net, 245 MAX_IP_ADDR_SIZE); 246 if (len < 0) 247 return len; 248 249 len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.gate_way, 250 strlen((char *)in->body.kvp_ip_val.gate_way), 251 UTF16_HOST_ENDIAN, 252 (wchar_t *)out->kvp_ip_val.gate_way, 253 MAX_GATEWAY_SIZE); 254 if (len < 0) 255 return len; 256 257 len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.dns_addr, 258 strlen((char *)in->body.kvp_ip_val.dns_addr), 259 UTF16_HOST_ENDIAN, 260 (wchar_t *)out->kvp_ip_val.dns_addr, 261 MAX_IP_ADDR_SIZE); 262 if (len < 0) 263 return len; 264 265 len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.adapter_id, 266 strlen((char *)in->body.kvp_ip_val.adapter_id), 267 UTF16_HOST_ENDIAN, 268 (wchar_t *)out->kvp_ip_val.adapter_id, 269 MAX_IP_ADDR_SIZE); 270 if (len < 0) 271 return len; 272 273 out->kvp_ip_val.dhcp_enabled = 274 in->body.kvp_ip_val.dhcp_enabled; 275 out->kvp_ip_val.addr_family = 276 in->body.kvp_ip_val.addr_family; 277 } 278 279 return 0; 280 } 281 282 static void process_ib_ipinfo(void *in_msg, void *out_msg, int op) 283 { 284 struct hv_kvp_ip_msg *in = in_msg; 285 struct hv_kvp_msg *out = out_msg; 286 287 switch (op) { 288 case KVP_OP_SET_IP_INFO: 289 /* 290 * Transform all parameters into utf8 encoding. 291 */ 292 utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.ip_addr, 293 MAX_IP_ADDR_SIZE, 294 UTF16_LITTLE_ENDIAN, 295 (__u8 *)out->body.kvp_ip_val.ip_addr, 296 MAX_IP_ADDR_SIZE); 297 298 utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.sub_net, 299 MAX_IP_ADDR_SIZE, 300 UTF16_LITTLE_ENDIAN, 301 (__u8 *)out->body.kvp_ip_val.sub_net, 302 MAX_IP_ADDR_SIZE); 303 304 utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.gate_way, 305 MAX_GATEWAY_SIZE, 306 UTF16_LITTLE_ENDIAN, 307 (__u8 *)out->body.kvp_ip_val.gate_way, 308 MAX_GATEWAY_SIZE); 309 310 utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.dns_addr, 311 MAX_IP_ADDR_SIZE, 312 UTF16_LITTLE_ENDIAN, 313 (__u8 *)out->body.kvp_ip_val.dns_addr, 314 MAX_IP_ADDR_SIZE); 315 316 out->body.kvp_ip_val.dhcp_enabled = in->kvp_ip_val.dhcp_enabled; 317 318 default: 319 utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.adapter_id, 320 MAX_ADAPTER_ID_SIZE, 321 UTF16_LITTLE_ENDIAN, 322 (__u8 *)out->body.kvp_ip_val.adapter_id, 323 MAX_ADAPTER_ID_SIZE); 324 325 out->body.kvp_ip_val.addr_family = in->kvp_ip_val.addr_family; 326 } 327 } 328 329 330 331 332 static void 333 kvp_send_key(struct work_struct *dummy) 334 { 335 struct cn_msg *msg; 336 struct hv_kvp_msg *message; 337 struct hv_kvp_msg *in_msg; 338 __u8 operation = kvp_transaction.kvp_msg->kvp_hdr.operation; 339 __u8 pool = kvp_transaction.kvp_msg->kvp_hdr.pool; 340 __u32 val32; 341 __u64 val64; 342 343 msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC); 344 if (!msg) 345 return; 346 347 msg->id.idx = CN_KVP_IDX; 348 msg->id.val = CN_KVP_VAL; 349 350 message = (struct hv_kvp_msg *)msg->data; 351 message->kvp_hdr.operation = operation; 352 message->kvp_hdr.pool = pool; 353 in_msg = kvp_transaction.kvp_msg; 354 355 /* 356 * The key/value strings sent from the host are encoded in 357 * in utf16; convert it to utf8 strings. 358 * The host assures us that the utf16 strings will not exceed 359 * the max lengths specified. We will however, reserve room 360 * for the string terminating character - in the utf16s_utf8s() 361 * function we limit the size of the buffer where the converted 362 * string is placed to HV_KVP_EXCHANGE_MAX_*_SIZE -1 to gaurantee 363 * that the strings can be properly terminated! 364 */ 365 366 switch (message->kvp_hdr.operation) { 367 case KVP_OP_SET_IP_INFO: 368 process_ib_ipinfo(in_msg, message, KVP_OP_SET_IP_INFO); 369 break; 370 case KVP_OP_GET_IP_INFO: 371 process_ib_ipinfo(in_msg, message, KVP_OP_GET_IP_INFO); 372 break; 373 case KVP_OP_SET: 374 switch (in_msg->body.kvp_set.data.value_type) { 375 case REG_SZ: 376 /* 377 * The value is a string - utf16 encoding. 378 */ 379 message->body.kvp_set.data.value_size = 380 utf16s_to_utf8s( 381 (wchar_t *)in_msg->body.kvp_set.data.value, 382 in_msg->body.kvp_set.data.value_size, 383 UTF16_LITTLE_ENDIAN, 384 message->body.kvp_set.data.value, 385 HV_KVP_EXCHANGE_MAX_VALUE_SIZE - 1) + 1; 386 break; 387 388 case REG_U32: 389 /* 390 * The value is a 32 bit scalar. 391 * We save this as a utf8 string. 392 */ 393 val32 = in_msg->body.kvp_set.data.value_u32; 394 message->body.kvp_set.data.value_size = 395 sprintf(message->body.kvp_set.data.value, 396 "%d", val32) + 1; 397 break; 398 399 case REG_U64: 400 /* 401 * The value is a 64 bit scalar. 402 * We save this as a utf8 string. 403 */ 404 val64 = in_msg->body.kvp_set.data.value_u64; 405 message->body.kvp_set.data.value_size = 406 sprintf(message->body.kvp_set.data.value, 407 "%llu", val64) + 1; 408 break; 409 410 } 411 case KVP_OP_GET: 412 message->body.kvp_set.data.key_size = 413 utf16s_to_utf8s( 414 (wchar_t *)in_msg->body.kvp_set.data.key, 415 in_msg->body.kvp_set.data.key_size, 416 UTF16_LITTLE_ENDIAN, 417 message->body.kvp_set.data.key, 418 HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1; 419 break; 420 421 case KVP_OP_DELETE: 422 message->body.kvp_delete.key_size = 423 utf16s_to_utf8s( 424 (wchar_t *)in_msg->body.kvp_delete.key, 425 in_msg->body.kvp_delete.key_size, 426 UTF16_LITTLE_ENDIAN, 427 message->body.kvp_delete.key, 428 HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1; 429 break; 430 431 case KVP_OP_ENUMERATE: 432 message->body.kvp_enum_data.index = 433 in_msg->body.kvp_enum_data.index; 434 break; 435 } 436 437 msg->len = sizeof(struct hv_kvp_msg); 438 cn_netlink_send(msg, 0, GFP_ATOMIC); 439 kfree(msg); 440 441 return; 442 } 443 444 /* 445 * Send a response back to the host. 446 */ 447 448 static void 449 kvp_respond_to_host(struct hv_kvp_msg *msg_to_host, int error) 450 { 451 struct hv_kvp_msg *kvp_msg; 452 struct hv_kvp_exchg_msg_value *kvp_data; 453 char *key_name; 454 char *value; 455 struct icmsg_hdr *icmsghdrp; 456 int keylen = 0; 457 int valuelen = 0; 458 u32 buf_len; 459 struct vmbus_channel *channel; 460 u64 req_id; 461 int ret; 462 463 /* 464 * If a transaction is not active; log and return. 465 */ 466 467 if (!kvp_transaction.active) { 468 /* 469 * This is a spurious call! 470 */ 471 pr_warn("KVP: Transaction not active\n"); 472 return; 473 } 474 /* 475 * Copy the global state for completing the transaction. Note that 476 * only one transaction can be active at a time. 477 */ 478 479 buf_len = kvp_transaction.recv_len; 480 channel = kvp_transaction.recv_channel; 481 req_id = kvp_transaction.recv_req_id; 482 483 kvp_transaction.active = false; 484 485 icmsghdrp = (struct icmsg_hdr *) 486 &recv_buffer[sizeof(struct vmbuspipe_hdr)]; 487 488 if (channel->onchannel_callback == NULL) 489 /* 490 * We have raced with util driver being unloaded; 491 * silently return. 492 */ 493 return; 494 495 icmsghdrp->status = error; 496 497 /* 498 * If the error parameter is set, terminate the host's enumeration 499 * on this pool. 500 */ 501 if (error) { 502 /* 503 * Something failed or we have timedout; 504 * terminate the current host-side iteration. 505 */ 506 goto response_done; 507 } 508 509 kvp_msg = (struct hv_kvp_msg *) 510 &recv_buffer[sizeof(struct vmbuspipe_hdr) + 511 sizeof(struct icmsg_hdr)]; 512 513 switch (kvp_transaction.kvp_msg->kvp_hdr.operation) { 514 case KVP_OP_GET_IP_INFO: 515 ret = process_ob_ipinfo(msg_to_host, 516 (struct hv_kvp_ip_msg *)kvp_msg, 517 KVP_OP_GET_IP_INFO); 518 if (ret < 0) 519 icmsghdrp->status = HV_E_FAIL; 520 521 goto response_done; 522 case KVP_OP_SET_IP_INFO: 523 goto response_done; 524 case KVP_OP_GET: 525 kvp_data = &kvp_msg->body.kvp_get.data; 526 goto copy_value; 527 528 case KVP_OP_SET: 529 case KVP_OP_DELETE: 530 goto response_done; 531 532 default: 533 break; 534 } 535 536 kvp_data = &kvp_msg->body.kvp_enum_data.data; 537 key_name = msg_to_host->body.kvp_enum_data.data.key; 538 539 /* 540 * The windows host expects the key/value pair to be encoded 541 * in utf16. Ensure that the key/value size reported to the host 542 * will be less than or equal to the MAX size (including the 543 * terminating character). 544 */ 545 keylen = utf8s_to_utf16s(key_name, strlen(key_name), UTF16_HOST_ENDIAN, 546 (wchar_t *) kvp_data->key, 547 (HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2) - 2); 548 kvp_data->key_size = 2*(keylen + 1); /* utf16 encoding */ 549 550 copy_value: 551 value = msg_to_host->body.kvp_enum_data.data.value; 552 valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN, 553 (wchar_t *) kvp_data->value, 554 (HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2); 555 kvp_data->value_size = 2*(valuelen + 1); /* utf16 encoding */ 556 557 /* 558 * If the utf8s to utf16s conversion failed; notify host 559 * of the error. 560 */ 561 if ((keylen < 0) || (valuelen < 0)) 562 icmsghdrp->status = HV_E_FAIL; 563 564 kvp_data->value_type = REG_SZ; /* all our values are strings */ 565 566 response_done: 567 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; 568 569 vmbus_sendpacket(channel, recv_buffer, buf_len, req_id, 570 VM_PKT_DATA_INBAND, 0); 571 572 } 573 574 /* 575 * This callback is invoked when we get a KVP message from the host. 576 * The host ensures that only one KVP transaction can be active at a time. 577 * KVP implementation in Linux needs to forward the key to a user-mde 578 * component to retrive the corresponding value. Consequently, we cannot 579 * respond to the host in the conext of this callback. Since the host 580 * guarantees that at most only one transaction can be active at a time, 581 * we stash away the transaction state in a set of global variables. 582 */ 583 584 void hv_kvp_onchannelcallback(void *context) 585 { 586 struct vmbus_channel *channel = context; 587 u32 recvlen; 588 u64 requestid; 589 590 struct hv_kvp_msg *kvp_msg; 591 592 struct icmsg_hdr *icmsghdrp; 593 struct icmsg_negotiate *negop = NULL; 594 int util_fw_version; 595 int kvp_srv_version; 596 597 if (kvp_transaction.active) { 598 /* 599 * We will defer processing this callback once 600 * the current transaction is complete. 601 */ 602 kvp_transaction.kvp_context = context; 603 return; 604 } 605 606 vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen, 607 &requestid); 608 609 if (recvlen > 0) { 610 icmsghdrp = (struct icmsg_hdr *)&recv_buffer[ 611 sizeof(struct vmbuspipe_hdr)]; 612 613 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 614 /* 615 * Based on the host, select appropriate 616 * framework and service versions we will 617 * negotiate. 618 */ 619 switch (vmbus_proto_version) { 620 case (VERSION_WS2008): 621 util_fw_version = UTIL_WS2K8_FW_VERSION; 622 kvp_srv_version = WS2008_SRV_VERSION; 623 break; 624 case (VERSION_WIN7): 625 util_fw_version = UTIL_FW_VERSION; 626 kvp_srv_version = WIN7_SRV_VERSION; 627 break; 628 default: 629 util_fw_version = UTIL_FW_VERSION; 630 kvp_srv_version = WIN8_SRV_VERSION; 631 } 632 vmbus_prep_negotiate_resp(icmsghdrp, negop, 633 recv_buffer, util_fw_version, 634 kvp_srv_version); 635 636 } else { 637 kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ 638 sizeof(struct vmbuspipe_hdr) + 639 sizeof(struct icmsg_hdr)]; 640 641 /* 642 * Stash away this global state for completing the 643 * transaction; note transactions are serialized. 644 */ 645 646 kvp_transaction.recv_len = recvlen; 647 kvp_transaction.recv_channel = channel; 648 kvp_transaction.recv_req_id = requestid; 649 kvp_transaction.active = true; 650 kvp_transaction.kvp_msg = kvp_msg; 651 652 /* 653 * Get the information from the 654 * user-mode component. 655 * component. This transaction will be 656 * completed when we get the value from 657 * the user-mode component. 658 * Set a timeout to deal with 659 * user-mode not responding. 660 */ 661 schedule_work(&kvp_sendkey_work); 662 schedule_delayed_work(&kvp_work, 5*HZ); 663 664 return; 665 666 } 667 668 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION 669 | ICMSGHDRFLAG_RESPONSE; 670 671 vmbus_sendpacket(channel, recv_buffer, 672 recvlen, requestid, 673 VM_PKT_DATA_INBAND, 0); 674 } 675 676 } 677 678 int 679 hv_kvp_init(struct hv_util_service *srv) 680 { 681 int err; 682 683 err = cn_add_callback(&kvp_id, kvp_name, kvp_cn_callback); 684 if (err) 685 return err; 686 recv_buffer = srv->recv_buffer; 687 688 /* 689 * When this driver loads, the user level daemon that 690 * processes the host requests may not yet be running. 691 * Defer processing channel callbacks until the daemon 692 * has registered. 693 */ 694 kvp_transaction.active = true; 695 696 return 0; 697 } 698 699 void hv_kvp_deinit(void) 700 { 701 cn_del_callback(&kvp_id); 702 cancel_delayed_work_sync(&kvp_work); 703 cancel_work_sync(&kvp_sendkey_work); 704 } 705