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 #include "hv_kvp.h" 3246a97191SGreg Kroah-Hartman 3346a97191SGreg Kroah-Hartman 3446a97191SGreg Kroah-Hartman 3546a97191SGreg Kroah-Hartman /* 3646a97191SGreg Kroah-Hartman * Global state maintained for transaction that is being processed. 3746a97191SGreg Kroah-Hartman * Note that only one transaction can be active at any point in time. 3846a97191SGreg Kroah-Hartman * 3946a97191SGreg Kroah-Hartman * This state is set when we receive a request from the host; we 4046a97191SGreg Kroah-Hartman * cleanup this state when the transaction is completed - when we respond 4146a97191SGreg Kroah-Hartman * to the host with the key value. 4246a97191SGreg Kroah-Hartman */ 4346a97191SGreg Kroah-Hartman 4446a97191SGreg Kroah-Hartman static struct { 4546a97191SGreg Kroah-Hartman bool active; /* transaction status - active or not */ 4646a97191SGreg Kroah-Hartman int recv_len; /* number of bytes received. */ 4746a97191SGreg Kroah-Hartman int index; /* current index */ 4846a97191SGreg Kroah-Hartman struct vmbus_channel *recv_channel; /* chn we got the request */ 4946a97191SGreg Kroah-Hartman u64 recv_req_id; /* request ID. */ 5046a97191SGreg Kroah-Hartman } kvp_transaction; 5146a97191SGreg Kroah-Hartman 5246a97191SGreg Kroah-Hartman static void kvp_send_key(struct work_struct *dummy); 5346a97191SGreg Kroah-Hartman 5446a97191SGreg Kroah-Hartman #define TIMEOUT_FIRED 1 5546a97191SGreg Kroah-Hartman 5646a97191SGreg Kroah-Hartman static void kvp_respond_to_host(char *key, char *value, int error); 5746a97191SGreg Kroah-Hartman static void kvp_work_func(struct work_struct *dummy); 5846a97191SGreg Kroah-Hartman static void kvp_register(void); 5946a97191SGreg Kroah-Hartman 6046a97191SGreg Kroah-Hartman static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func); 6146a97191SGreg Kroah-Hartman static DECLARE_WORK(kvp_sendkey_work, kvp_send_key); 6246a97191SGreg Kroah-Hartman 6346a97191SGreg Kroah-Hartman static struct cb_id kvp_id = { CN_KVP_IDX, CN_KVP_VAL }; 6446a97191SGreg Kroah-Hartman static const char kvp_name[] = "kvp_kernel_module"; 6546a97191SGreg Kroah-Hartman static u8 *recv_buffer; 6646a97191SGreg Kroah-Hartman /* 6746a97191SGreg Kroah-Hartman * Register the kernel component with the user-level daemon. 6846a97191SGreg Kroah-Hartman * As part of this registration, pass the LIC version number. 6946a97191SGreg Kroah-Hartman */ 7046a97191SGreg Kroah-Hartman 7146a97191SGreg Kroah-Hartman static void 7246a97191SGreg Kroah-Hartman kvp_register(void) 7346a97191SGreg Kroah-Hartman { 7446a97191SGreg Kroah-Hartman 7546a97191SGreg Kroah-Hartman struct cn_msg *msg; 7646a97191SGreg Kroah-Hartman 7746a97191SGreg Kroah-Hartman msg = kzalloc(sizeof(*msg) + strlen(HV_DRV_VERSION) + 1 , GFP_ATOMIC); 7846a97191SGreg Kroah-Hartman 7946a97191SGreg Kroah-Hartman if (msg) { 8046a97191SGreg Kroah-Hartman msg->id.idx = CN_KVP_IDX; 8146a97191SGreg Kroah-Hartman msg->id.val = CN_KVP_VAL; 8246a97191SGreg Kroah-Hartman msg->seq = KVP_REGISTER; 8346a97191SGreg Kroah-Hartman strcpy(msg->data, HV_DRV_VERSION); 8446a97191SGreg Kroah-Hartman msg->len = strlen(HV_DRV_VERSION) + 1; 8546a97191SGreg Kroah-Hartman cn_netlink_send(msg, 0, GFP_ATOMIC); 8646a97191SGreg Kroah-Hartman kfree(msg); 8746a97191SGreg Kroah-Hartman } 8846a97191SGreg Kroah-Hartman } 8946a97191SGreg Kroah-Hartman static void 9046a97191SGreg Kroah-Hartman kvp_work_func(struct work_struct *dummy) 9146a97191SGreg Kroah-Hartman { 9246a97191SGreg Kroah-Hartman /* 9346a97191SGreg Kroah-Hartman * If the timer fires, the user-mode component has not responded; 9446a97191SGreg Kroah-Hartman * process the pending transaction. 9546a97191SGreg Kroah-Hartman */ 9646a97191SGreg Kroah-Hartman kvp_respond_to_host("Unknown key", "Guest timed out", TIMEOUT_FIRED); 9746a97191SGreg Kroah-Hartman } 9846a97191SGreg Kroah-Hartman 9946a97191SGreg Kroah-Hartman /* 10046a97191SGreg Kroah-Hartman * Callback when data is received from user mode. 10146a97191SGreg Kroah-Hartman */ 10246a97191SGreg Kroah-Hartman 10346a97191SGreg Kroah-Hartman static void 10446a97191SGreg Kroah-Hartman kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) 10546a97191SGreg Kroah-Hartman { 10646a97191SGreg Kroah-Hartman struct hv_ku_msg *message; 10746a97191SGreg Kroah-Hartman 10846a97191SGreg Kroah-Hartman message = (struct hv_ku_msg *)msg->data; 10946a97191SGreg Kroah-Hartman if (msg->seq == KVP_REGISTER) { 11046a97191SGreg Kroah-Hartman pr_info("KVP: user-mode registering done.\n"); 11146a97191SGreg Kroah-Hartman kvp_register(); 11246a97191SGreg Kroah-Hartman } 11346a97191SGreg Kroah-Hartman 11446a97191SGreg Kroah-Hartman if (msg->seq == KVP_USER_SET) { 11546a97191SGreg Kroah-Hartman /* 11646a97191SGreg Kroah-Hartman * Complete the transaction by forwarding the key value 11746a97191SGreg Kroah-Hartman * to the host. But first, cancel the timeout. 11846a97191SGreg Kroah-Hartman */ 11946a97191SGreg Kroah-Hartman if (cancel_delayed_work_sync(&kvp_work)) 12046a97191SGreg Kroah-Hartman kvp_respond_to_host(message->kvp_key, 12146a97191SGreg Kroah-Hartman message->kvp_value, 12246a97191SGreg Kroah-Hartman !strlen(message->kvp_key)); 12346a97191SGreg Kroah-Hartman } 12446a97191SGreg Kroah-Hartman } 12546a97191SGreg Kroah-Hartman 12646a97191SGreg Kroah-Hartman static void 12746a97191SGreg Kroah-Hartman kvp_send_key(struct work_struct *dummy) 12846a97191SGreg Kroah-Hartman { 12946a97191SGreg Kroah-Hartman struct cn_msg *msg; 13046a97191SGreg Kroah-Hartman int index = kvp_transaction.index; 13146a97191SGreg Kroah-Hartman 13246a97191SGreg Kroah-Hartman msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC); 13346a97191SGreg Kroah-Hartman 13446a97191SGreg Kroah-Hartman if (msg) { 13546a97191SGreg Kroah-Hartman msg->id.idx = CN_KVP_IDX; 13646a97191SGreg Kroah-Hartman msg->id.val = CN_KVP_VAL; 13746a97191SGreg Kroah-Hartman msg->seq = KVP_KERNEL_GET; 13846a97191SGreg Kroah-Hartman ((struct hv_ku_msg *)msg->data)->kvp_index = index; 13946a97191SGreg Kroah-Hartman msg->len = sizeof(struct hv_ku_msg); 14046a97191SGreg Kroah-Hartman cn_netlink_send(msg, 0, GFP_ATOMIC); 14146a97191SGreg Kroah-Hartman kfree(msg); 14246a97191SGreg Kroah-Hartman } 14346a97191SGreg Kroah-Hartman return; 14446a97191SGreg Kroah-Hartman } 14546a97191SGreg Kroah-Hartman 14646a97191SGreg Kroah-Hartman /* 14746a97191SGreg Kroah-Hartman * Send a response back to the host. 14846a97191SGreg Kroah-Hartman */ 14946a97191SGreg Kroah-Hartman 15046a97191SGreg Kroah-Hartman static void 15146a97191SGreg Kroah-Hartman kvp_respond_to_host(char *key, char *value, int error) 15246a97191SGreg Kroah-Hartman { 15346a97191SGreg Kroah-Hartman struct hv_kvp_msg *kvp_msg; 15446a97191SGreg Kroah-Hartman struct hv_kvp_msg_enumerate *kvp_data; 15546a97191SGreg Kroah-Hartman char *key_name; 15646a97191SGreg Kroah-Hartman struct icmsg_hdr *icmsghdrp; 15746a97191SGreg Kroah-Hartman int keylen, valuelen; 15846a97191SGreg Kroah-Hartman u32 buf_len; 15946a97191SGreg Kroah-Hartman struct vmbus_channel *channel; 16046a97191SGreg Kroah-Hartman u64 req_id; 16146a97191SGreg Kroah-Hartman 16246a97191SGreg Kroah-Hartman /* 16346a97191SGreg Kroah-Hartman * If a transaction is not active; log and return. 16446a97191SGreg Kroah-Hartman */ 16546a97191SGreg Kroah-Hartman 16646a97191SGreg Kroah-Hartman if (!kvp_transaction.active) { 16746a97191SGreg Kroah-Hartman /* 16846a97191SGreg Kroah-Hartman * This is a spurious call! 16946a97191SGreg Kroah-Hartman */ 17046a97191SGreg Kroah-Hartman pr_warn("KVP: Transaction not active\n"); 17146a97191SGreg Kroah-Hartman return; 17246a97191SGreg Kroah-Hartman } 17346a97191SGreg Kroah-Hartman /* 17446a97191SGreg Kroah-Hartman * Copy the global state for completing the transaction. Note that 17546a97191SGreg Kroah-Hartman * only one transaction can be active at a time. 17646a97191SGreg Kroah-Hartman */ 17746a97191SGreg Kroah-Hartman 17846a97191SGreg Kroah-Hartman buf_len = kvp_transaction.recv_len; 17946a97191SGreg Kroah-Hartman channel = kvp_transaction.recv_channel; 18046a97191SGreg Kroah-Hartman req_id = kvp_transaction.recv_req_id; 18146a97191SGreg Kroah-Hartman 18246a97191SGreg Kroah-Hartman kvp_transaction.active = false; 18346a97191SGreg Kroah-Hartman 18446a97191SGreg Kroah-Hartman if (channel->onchannel_callback == NULL) 18546a97191SGreg Kroah-Hartman /* 18646a97191SGreg Kroah-Hartman * We have raced with util driver being unloaded; 18746a97191SGreg Kroah-Hartman * silently return. 18846a97191SGreg Kroah-Hartman */ 18946a97191SGreg Kroah-Hartman return; 19046a97191SGreg Kroah-Hartman 19146a97191SGreg Kroah-Hartman icmsghdrp = (struct icmsg_hdr *) 19246a97191SGreg Kroah-Hartman &recv_buffer[sizeof(struct vmbuspipe_hdr)]; 19346a97191SGreg Kroah-Hartman kvp_msg = (struct hv_kvp_msg *) 19446a97191SGreg Kroah-Hartman &recv_buffer[sizeof(struct vmbuspipe_hdr) + 19546a97191SGreg Kroah-Hartman sizeof(struct icmsg_hdr)]; 19646a97191SGreg Kroah-Hartman kvp_data = &kvp_msg->kvp_data; 19746a97191SGreg Kroah-Hartman key_name = key; 19846a97191SGreg Kroah-Hartman 19946a97191SGreg Kroah-Hartman /* 20046a97191SGreg Kroah-Hartman * If the error parameter is set, terminate the host's enumeration. 20146a97191SGreg Kroah-Hartman */ 20246a97191SGreg Kroah-Hartman if (error) { 20346a97191SGreg Kroah-Hartman /* 20446a97191SGreg Kroah-Hartman * We don't support this index or the we have timedout; 20546a97191SGreg Kroah-Hartman * terminate the host-side iteration by returning an error. 20646a97191SGreg Kroah-Hartman */ 20746a97191SGreg Kroah-Hartman icmsghdrp->status = HV_E_FAIL; 20846a97191SGreg Kroah-Hartman goto response_done; 20946a97191SGreg Kroah-Hartman } 21046a97191SGreg Kroah-Hartman 21146a97191SGreg Kroah-Hartman /* 21246a97191SGreg Kroah-Hartman * The windows host expects the key/value pair to be encoded 21346a97191SGreg Kroah-Hartman * in utf16. 21446a97191SGreg Kroah-Hartman */ 215*0720a06aSAlan Stern keylen = utf8s_to_utf16s(key_name, strlen(key_name), UTF16_HOST_ENDIAN, 216*0720a06aSAlan Stern (wchar_t *) kvp_data->data.key, 217*0720a06aSAlan Stern HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2); 21846a97191SGreg Kroah-Hartman kvp_data->data.key_size = 2*(keylen + 1); /* utf16 encoding */ 219*0720a06aSAlan Stern valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN, 220*0720a06aSAlan Stern (wchar_t *) kvp_data->data.value, 221*0720a06aSAlan Stern HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2); 22246a97191SGreg Kroah-Hartman kvp_data->data.value_size = 2*(valuelen + 1); /* utf16 encoding */ 22346a97191SGreg Kroah-Hartman 22446a97191SGreg Kroah-Hartman kvp_data->data.value_type = REG_SZ; /* all our values are strings */ 22546a97191SGreg Kroah-Hartman icmsghdrp->status = HV_S_OK; 22646a97191SGreg Kroah-Hartman 22746a97191SGreg Kroah-Hartman response_done: 22846a97191SGreg Kroah-Hartman icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; 22946a97191SGreg Kroah-Hartman 23046a97191SGreg Kroah-Hartman vmbus_sendpacket(channel, recv_buffer, buf_len, req_id, 23146a97191SGreg Kroah-Hartman VM_PKT_DATA_INBAND, 0); 23246a97191SGreg Kroah-Hartman 23346a97191SGreg Kroah-Hartman } 23446a97191SGreg Kroah-Hartman 23546a97191SGreg Kroah-Hartman /* 23646a97191SGreg Kroah-Hartman * This callback is invoked when we get a KVP message from the host. 23746a97191SGreg Kroah-Hartman * The host ensures that only one KVP transaction can be active at a time. 23846a97191SGreg Kroah-Hartman * KVP implementation in Linux needs to forward the key to a user-mde 23946a97191SGreg Kroah-Hartman * component to retrive the corresponding value. Consequently, we cannot 24046a97191SGreg Kroah-Hartman * respond to the host in the conext of this callback. Since the host 24146a97191SGreg Kroah-Hartman * guarantees that at most only one transaction can be active at a time, 24246a97191SGreg Kroah-Hartman * we stash away the transaction state in a set of global variables. 24346a97191SGreg Kroah-Hartman */ 24446a97191SGreg Kroah-Hartman 24546a97191SGreg Kroah-Hartman void hv_kvp_onchannelcallback(void *context) 24646a97191SGreg Kroah-Hartman { 24746a97191SGreg Kroah-Hartman struct vmbus_channel *channel = context; 24846a97191SGreg Kroah-Hartman u32 recvlen; 24946a97191SGreg Kroah-Hartman u64 requestid; 25046a97191SGreg Kroah-Hartman 25146a97191SGreg Kroah-Hartman struct hv_kvp_msg *kvp_msg; 25246a97191SGreg Kroah-Hartman struct hv_kvp_msg_enumerate *kvp_data; 25346a97191SGreg Kroah-Hartman 25446a97191SGreg Kroah-Hartman struct icmsg_hdr *icmsghdrp; 25546a97191SGreg Kroah-Hartman struct icmsg_negotiate *negop = NULL; 25646a97191SGreg Kroah-Hartman 25746a97191SGreg Kroah-Hartman 25846a97191SGreg Kroah-Hartman vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid); 25946a97191SGreg Kroah-Hartman 26046a97191SGreg Kroah-Hartman if (recvlen > 0) { 26146a97191SGreg Kroah-Hartman icmsghdrp = (struct icmsg_hdr *)&recv_buffer[ 26246a97191SGreg Kroah-Hartman sizeof(struct vmbuspipe_hdr)]; 26346a97191SGreg Kroah-Hartman 26446a97191SGreg Kroah-Hartman if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 265da0e9631SGreg Kroah-Hartman vmbus_prep_negotiate_resp(icmsghdrp, negop, recv_buffer); 26646a97191SGreg Kroah-Hartman } else { 26746a97191SGreg Kroah-Hartman kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ 26846a97191SGreg Kroah-Hartman sizeof(struct vmbuspipe_hdr) + 26946a97191SGreg Kroah-Hartman sizeof(struct icmsg_hdr)]; 27046a97191SGreg Kroah-Hartman 27146a97191SGreg Kroah-Hartman kvp_data = &kvp_msg->kvp_data; 27246a97191SGreg Kroah-Hartman 27346a97191SGreg Kroah-Hartman /* 27446a97191SGreg Kroah-Hartman * We only support the "get" operation on 27546a97191SGreg Kroah-Hartman * "KVP_POOL_AUTO" pool. 27646a97191SGreg Kroah-Hartman */ 27746a97191SGreg Kroah-Hartman 27846a97191SGreg Kroah-Hartman if ((kvp_msg->kvp_hdr.pool != KVP_POOL_AUTO) || 27946a97191SGreg Kroah-Hartman (kvp_msg->kvp_hdr.operation != 28046a97191SGreg Kroah-Hartman KVP_OP_ENUMERATE)) { 28146a97191SGreg Kroah-Hartman icmsghdrp->status = HV_E_FAIL; 28246a97191SGreg Kroah-Hartman goto callback_done; 28346a97191SGreg Kroah-Hartman } 28446a97191SGreg Kroah-Hartman 28546a97191SGreg Kroah-Hartman /* 28646a97191SGreg Kroah-Hartman * Stash away this global state for completing the 28746a97191SGreg Kroah-Hartman * transaction; note transactions are serialized. 28846a97191SGreg Kroah-Hartman */ 28946a97191SGreg Kroah-Hartman kvp_transaction.recv_len = recvlen; 29046a97191SGreg Kroah-Hartman kvp_transaction.recv_channel = channel; 29146a97191SGreg Kroah-Hartman kvp_transaction.recv_req_id = requestid; 29246a97191SGreg Kroah-Hartman kvp_transaction.active = true; 29346a97191SGreg Kroah-Hartman kvp_transaction.index = kvp_data->index; 29446a97191SGreg Kroah-Hartman 29546a97191SGreg Kroah-Hartman /* 29646a97191SGreg Kroah-Hartman * Get the information from the 29746a97191SGreg Kroah-Hartman * user-mode component. 29846a97191SGreg Kroah-Hartman * component. This transaction will be 29946a97191SGreg Kroah-Hartman * completed when we get the value from 30046a97191SGreg Kroah-Hartman * the user-mode component. 30146a97191SGreg Kroah-Hartman * Set a timeout to deal with 30246a97191SGreg Kroah-Hartman * user-mode not responding. 30346a97191SGreg Kroah-Hartman */ 30446a97191SGreg Kroah-Hartman schedule_work(&kvp_sendkey_work); 30546a97191SGreg Kroah-Hartman schedule_delayed_work(&kvp_work, 5*HZ); 30646a97191SGreg Kroah-Hartman 30746a97191SGreg Kroah-Hartman return; 30846a97191SGreg Kroah-Hartman 30946a97191SGreg Kroah-Hartman } 31046a97191SGreg Kroah-Hartman 31146a97191SGreg Kroah-Hartman callback_done: 31246a97191SGreg Kroah-Hartman 31346a97191SGreg Kroah-Hartman icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION 31446a97191SGreg Kroah-Hartman | ICMSGHDRFLAG_RESPONSE; 31546a97191SGreg Kroah-Hartman 31646a97191SGreg Kroah-Hartman vmbus_sendpacket(channel, recv_buffer, 31746a97191SGreg Kroah-Hartman recvlen, requestid, 31846a97191SGreg Kroah-Hartman VM_PKT_DATA_INBAND, 0); 31946a97191SGreg Kroah-Hartman } 32046a97191SGreg Kroah-Hartman 32146a97191SGreg Kroah-Hartman } 32246a97191SGreg Kroah-Hartman 32346a97191SGreg Kroah-Hartman int 32446a97191SGreg Kroah-Hartman hv_kvp_init(struct hv_util_service *srv) 32546a97191SGreg Kroah-Hartman { 32646a97191SGreg Kroah-Hartman int err; 32746a97191SGreg Kroah-Hartman 32846a97191SGreg Kroah-Hartman err = cn_add_callback(&kvp_id, kvp_name, kvp_cn_callback); 32946a97191SGreg Kroah-Hartman if (err) 33046a97191SGreg Kroah-Hartman return err; 33146a97191SGreg Kroah-Hartman recv_buffer = srv->recv_buffer; 33246a97191SGreg Kroah-Hartman 33346a97191SGreg Kroah-Hartman return 0; 33446a97191SGreg Kroah-Hartman } 33546a97191SGreg Kroah-Hartman 33646a97191SGreg Kroah-Hartman void hv_kvp_deinit(void) 33746a97191SGreg Kroah-Hartman { 33846a97191SGreg Kroah-Hartman cn_del_callback(&kvp_id); 33946a97191SGreg Kroah-Hartman cancel_delayed_work_sync(&kvp_work); 34046a97191SGreg Kroah-Hartman cancel_work_sync(&kvp_sendkey_work); 34146a97191SGreg Kroah-Hartman } 342