Userspace/kernel communication via netlink has a number of issues: - It is hard for userspace to figure out if the kernel part was loaded or not and this fact can change as there is a way to enable/disable the service from host side. Racy daemon startup is also a problem. - When the userspace daemon restarts/dies kernel part doesn't receive a notification. - Netlink communication is not stable under heavy load. - ... Re-implement the communication using misc char device. Use ioctl to do kernel/userspace version negotiation (doesn't make much sense at this moment as we're breaking backwards compatibility but can be used in future). Signed-off-by: Vitaly Kuznetsov <vkuznets@xxxxxxxxxx> --- drivers/hv/hv_kvp.c | 396 +++++++++++++++++++++++++++----------------- include/uapi/linux/hyperv.h | 8 + tools/hv/hv_kvp_daemon.c | 187 ++++----------------- 3 files changed, 287 insertions(+), 304 deletions(-) diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index beb8105..8078b1a 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -22,12 +22,16 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/net.h> #include <linux/nls.h> -#include <linux/connector.h> +#include <linux/fs.h> +#include <linux/sched.h> #include <linux/workqueue.h> +#include <linux/mutex.h> #include <linux/hyperv.h> +#include <linux/miscdevice.h> +#include <linux/poll.h> +#include <linux/uaccess.h> /* * Pre win8 version numbers used in ws2008 and ws 2008 r2 (win7) @@ -45,46 +49,41 @@ #define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR) /* - * Global state maintained for transaction that is being processed. - * Note that only one transaction can be active at any point in time. - * - * This state is set when we receive a request from the host; we - * cleanup this state when the transaction is completed - when we respond - * to the host with the key value. + * Global state maintained for the device. Note that only one transaction can + * be active at any point in time. */ +enum kvp_device_state { + KVP_DEVICE_INITIALIZING = 0, /* driver was loaded */ + KVP_DEVICE_OPENED, /* device was opened */ + KVP_READY, /* userspace was registered */ + KVP_HOSTMSG_RECEIVED, /* message from host was received */ + KVP_USERMSG_READY, /* message for userspace is ready */ + KVP_USERSPACE_REQ, /* request to userspace was sent */ + KVP_USERSPACE_RECV, /* reply from userspace was received */ + KVP_DEVICE_DYING, /* driver unload is in progress */ +}; + static struct { - bool active; /* transaction status - active or not */ + int state; /* kvp_device_state */ int recv_len; /* number of bytes received. */ - struct hv_kvp_msg *kvp_msg; /* current message */ struct vmbus_channel *recv_channel; /* chn we got the request */ u64 recv_req_id; /* request ID. */ void *kvp_context; /* for the channel callback */ -} kvp_transaction; - -/* - * Before we can accept KVP messages from the host, we need - * to handshake with the user level daemon. This state tracks - * if we are in the handshake phase. - */ -static bool in_hand_shake = true; - -/* - * This state maintains the version number registered by the daemon. - */ -static int dm_reg_value; + int dm_reg_value; /* daemon version number */ + struct mutex lock; /* syncronization */ + struct hv_kvp_msg user_msg; /* message to/from userspace */ + struct hv_kvp_msg host_msg; /* message to/from host */ + wait_queue_head_t proc_list; /* waiting processes */ +} kvp_device; static void kvp_send_key(struct work_struct *dummy); - - -static void kvp_respond_to_host(struct hv_kvp_msg *msg, int error); +static void kvp_respond_to_host(int error); static void kvp_work_func(struct work_struct *dummy); -static void kvp_register(int); static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func); static DECLARE_WORK(kvp_sendkey_work, kvp_send_key); -static struct cb_id kvp_id = { CN_KVP_IDX, CN_KVP_VAL }; static const char kvp_name[] = "kvp_kernel_module"; static u8 *recv_buffer; /* @@ -92,31 +91,8 @@ static u8 *recv_buffer; * As part of this registration, pass the LIC version number. * This number has no meaning, it satisfies the registration protocol. */ -#define HV_DRV_VERSION "3.1" - -static void -kvp_register(int reg_value) -{ - - struct cn_msg *msg; - struct hv_kvp_msg *kvp_msg; - char *version; - - msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg), GFP_ATOMIC); +#define HV_DRV_VERSION 31 - if (msg) { - kvp_msg = (struct hv_kvp_msg *)msg->data; - version = kvp_msg->body.kvp_register.version; - msg->id.idx = CN_KVP_IDX; - msg->id.val = CN_KVP_VAL; - - kvp_msg->kvp_hdr.operation = reg_value; - strcpy(version, HV_DRV_VERSION); - msg->len = sizeof(struct hv_kvp_msg); - cn_netlink_send(msg, 0, 0, GFP_ATOMIC); - kfree(msg); - } -} static void kvp_work_func(struct work_struct *dummy) { @@ -124,7 +100,7 @@ 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); + kvp_respond_to_host(HV_E_FAIL); } static void poll_channel(struct vmbus_channel *channel) @@ -138,36 +114,26 @@ static void poll_channel(struct vmbus_channel *channel) } -static int kvp_handle_handshake(struct hv_kvp_msg *msg) +static int kvp_handle_handshake(u32 op) { - int ret = 1; + int ret = 0; - switch (msg->kvp_hdr.operation) { + switch (op) { case KVP_OP_REGISTER: - dm_reg_value = KVP_OP_REGISTER; + kvp_device.dm_reg_value = KVP_OP_REGISTER; pr_info("KVP: IP injection functionality not available\n"); pr_info("KVP: Upgrade the KVP daemon\n"); break; case KVP_OP_REGISTER1: - dm_reg_value = KVP_OP_REGISTER1; + kvp_device.dm_reg_value = KVP_OP_REGISTER1; break; default: pr_info("KVP: incompatible daemon\n"); pr_info("KVP: KVP version: %d, Daemon version: %d\n", - KVP_OP_REGISTER1, msg->kvp_hdr.operation); - ret = 0; + KVP_OP_REGISTER1, op); + ret = 1; } - if (ret) { - /* - * We have a compatible daemon; complete the handshake. - */ - pr_info("KVP: user-mode registering done.\n"); - kvp_register(dm_reg_value); - kvp_transaction.active = false; - if (kvp_transaction.kvp_context) - poll_channel(kvp_transaction.kvp_context); - } return ret; } @@ -176,25 +142,11 @@ static int kvp_handle_handshake(struct hv_kvp_msg *msg) * Callback when data is received from user mode. */ -static void -kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) +static void kvp_userwrite_callback(void) { - struct hv_kvp_msg *message; + struct hv_kvp_msg *message = &kvp_device.user_msg; 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; - } + int error = 0; /* * Based on the version of the daemon, we propagate errors from the @@ -203,7 +155,7 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) data = &message->body.kvp_enum_data; - switch (dm_reg_value) { + switch (kvp_device.dm_reg_value) { case KVP_OP_REGISTER: /* * Null string is used to pass back error condition. @@ -226,10 +178,9 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) * to the host. But first, cancel the timeout. */ if (cancel_delayed_work_sync(&kvp_work)) - kvp_respond_to_host(message, error); + kvp_respond_to_host(error); } - static int process_ob_ipinfo(void *in_msg, void *out_msg, int op) { struct hv_kvp_msg *in = in_msg; @@ -337,32 +288,21 @@ static void process_ib_ipinfo(void *in_msg, void *out_msg, int op) } } - - - 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; + struct hv_kvp_msg *message = &kvp_device.user_msg; + struct hv_kvp_msg *in_msg = &kvp_device.host_msg; + __u8 operation = in_msg->kvp_hdr.operation; + __u8 pool = in_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; + mutex_lock(&kvp_device.lock); - message = (struct hv_kvp_msg *)msg->data; + memset(message, 0, sizeof(struct hv_kvp_msg)); 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 @@ -446,15 +386,10 @@ kvp_send_key(struct work_struct *dummy) 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); - } + kvp_device.state = KVP_USERMSG_READY; + wake_up_interruptible(&kvp_device.proc_list); - kfree(msg); + mutex_unlock(&kvp_device.lock); return; } @@ -463,10 +398,10 @@ kvp_send_key(struct work_struct *dummy) * Send a response back to the host. */ -static void -kvp_respond_to_host(struct hv_kvp_msg *msg_to_host, int error) +static void kvp_respond_to_host(int error) { struct hv_kvp_msg *kvp_msg; + struct hv_kvp_msg *msg_to_host = &kvp_device.user_msg; struct hv_kvp_exchg_msg_value *kvp_data; char *key_name; char *value; @@ -479,26 +414,13 @@ kvp_respond_to_host(struct hv_kvp_msg *msg_to_host, int error) int ret; /* - * If a transaction is not active; log and return. - */ - - if (!kvp_transaction.active) { - /* - * This is a spurious call! - */ - pr_warn("KVP: Transaction not active\n"); - return; - } - /* * Copy the global state for completing the transaction. Note that * only one transaction can be active at a time. */ - buf_len = kvp_transaction.recv_len; - channel = kvp_transaction.recv_channel; - req_id = kvp_transaction.recv_req_id; - - kvp_transaction.active = false; + buf_len = kvp_device.recv_len; + channel = kvp_device.recv_channel; + req_id = kvp_device.recv_req_id; icmsghdrp = (struct icmsg_hdr *) &recv_buffer[sizeof(struct vmbuspipe_hdr)]; @@ -528,7 +450,8 @@ kvp_respond_to_host(struct hv_kvp_msg *msg_to_host, int error) &recv_buffer[sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; - switch (kvp_transaction.kvp_msg->kvp_hdr.operation) { + + switch (kvp_device.host_msg.kvp_hdr.operation) { case KVP_OP_GET_IP_INFO: ret = process_ob_ipinfo(msg_to_host, (struct hv_kvp_ip_msg *)kvp_msg, @@ -586,6 +509,17 @@ response_done: vmbus_sendpacket(channel, recv_buffer, buf_len, req_id, VM_PKT_DATA_INBAND, 0); + + /* We're ready to process next request, reset the device state */ + if (kvp_device.state == KVP_USERSPACE_RECV || + kvp_device.state == KVP_USERSPACE_REQ) + kvp_device.state = KVP_READY; + /* + * Make sure device state was set before polling the channel as + * processing can happen on a different CPU. + */ + smp_mb(); + poll_channel(channel); } @@ -612,14 +546,15 @@ void hv_kvp_onchannelcallback(void *context) int util_fw_version; int kvp_srv_version; - if (kvp_transaction.active) { + if (kvp_device.state > KVP_READY) { /* * We will defer processing this callback once * the current transaction is complete. */ - kvp_transaction.kvp_context = context; + kvp_device.kvp_context = channel; return; } + kvp_device.kvp_context = NULL; vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 4, &recvlen, &requestid); @@ -661,11 +596,19 @@ void hv_kvp_onchannelcallback(void *context) * transaction; note transactions are serialized. */ - kvp_transaction.recv_len = recvlen; - kvp_transaction.recv_channel = channel; - kvp_transaction.recv_req_id = requestid; - kvp_transaction.active = true; - kvp_transaction.kvp_msg = kvp_msg; + kvp_device.recv_len = recvlen; + kvp_device.recv_channel = channel; + kvp_device.recv_req_id = requestid; + + if (kvp_device.state != KVP_READY) { + /* Userspace daemon is not connected, fail. */ + kvp_respond_to_host(HV_E_FAIL); + return; + } + + kvp_device.state = KVP_HOSTMSG_RECEIVED; + memcpy(&kvp_device.host_msg, kvp_msg, + sizeof(struct hv_kvp_msg)); /* * Get the information from the @@ -690,17 +633,166 @@ void hv_kvp_onchannelcallback(void *context) recvlen, requestid, VM_PKT_DATA_INBAND, 0); } +} + +static int kvp_op_open(struct inode *inode, struct file *f) +{ + if (kvp_device.state != KVP_DEVICE_INITIALIZING) + return -EBUSY; + kvp_device.state = KVP_DEVICE_OPENED; + return 0; +} + +static int kvp_op_release(struct inode *inode, struct file *f) +{ + kvp_device.state = KVP_DEVICE_INITIALIZING; + return 0; +} + +static ssize_t kvp_op_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int ret = 0; + + if (kvp_device.state == KVP_DEVICE_DYING) + return -EFAULT; + + if (count != sizeof(struct hv_kvp_msg)) { + pr_warn("kvp_op_write: invalid write len: %d (expected: %d)\n", + (int)count, (int)sizeof(struct hv_kvp_msg)); + return -EINVAL; + } + mutex_lock(&kvp_device.lock); + + if (kvp_device.state == KVP_USERSPACE_REQ) { + if (!copy_from_user(&kvp_device.user_msg, buf, + sizeof(struct hv_kvp_msg))) { + kvp_device.state = KVP_USERSPACE_RECV; + kvp_userwrite_callback(); + ret = sizeof(struct hv_kvp_msg); + } else + ret = -EFAULT; + } else { + pr_warn("kvp_op_write: invalid transaction state: %d\n", + kvp_device.state); + ret = -EINVAL; + } + + mutex_unlock(&kvp_device.lock); + return ret; } +static ssize_t kvp_op_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t ret = 0; + + if (kvp_device.state == KVP_DEVICE_DYING) + return -EFAULT; + + if (count != sizeof(struct hv_kvp_msg)) { + pr_warn("kvp_op_read: invalid read len: %d (expected: %d)\n", + (int)count, (int)sizeof(struct hv_kvp_msg)); + return -EINVAL; + } + + if (wait_event_interruptible(kvp_device.proc_list, + kvp_device.state == KVP_USERMSG_READY || + kvp_device.state == KVP_DEVICE_DYING)) + return -EFAULT; + + if (kvp_device.state != KVP_USERMSG_READY) + return -EFAULT; + + mutex_lock(&kvp_device.lock); + + if (!copy_to_user(buf, &kvp_device.user_msg, + sizeof(struct hv_kvp_msg))) { + kvp_device.state = KVP_USERSPACE_REQ; + ret = sizeof(struct hv_kvp_msg); + } else + ret = -EFAULT; + + mutex_unlock(&kvp_device.lock); + return ret; +} + +static unsigned int kvp_op_poll(struct file *file, poll_table *wait) +{ + if (kvp_device.state == KVP_DEVICE_DYING) + return -EFAULT; + + poll_wait(file, &kvp_device.proc_list, wait); + if (kvp_device.state == KVP_USERMSG_READY) + return POLLIN | POLLRDNORM; + return 0; +} + +static long kvp_op_ioctl(struct file *fp, + unsigned int cmd, unsigned long arg) +{ + long ret = 0; + void __user *argp = (void __user *)arg; + u32 val32; + + if (kvp_device.state == KVP_DEVICE_DYING) + return -EFAULT; + + /* The only ioctl we have is registation */ + if (kvp_device.state != KVP_DEVICE_OPENED) + return -EINVAL; + + mutex_lock(&kvp_device.lock); + + switch (cmd) { + case HYPERV_KVP_REGISTER: + if (copy_from_user(&val32, argp, sizeof(val32))) { + ret = -EFAULT; + break; + } + if (!kvp_handle_handshake(val32)) { + val32 = (u32)HV_DRV_VERSION; + if (copy_to_user(argp, &val32, sizeof(val32))) { + ret = -EFAULT; + break; + } + kvp_device.state = KVP_READY; + pr_info("KVP: user-mode registering done.\n"); + if (kvp_device.kvp_context) + poll_channel(kvp_device.kvp_context); + } else + ret = -EINVAL; + break; + + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&kvp_device.lock); + return ret; +} + +static const struct file_operations kvp_fops = { + .owner = THIS_MODULE, + .read = kvp_op_read, + .write = kvp_op_write, + .release = kvp_op_release, + .open = kvp_op_open, + .poll = kvp_op_poll, + .unlocked_ioctl = kvp_op_ioctl +}; + +static struct miscdevice kvp_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "vmbus/hv_kvp", + .fops = &kvp_fops, +}; + int hv_kvp_init(struct hv_util_service *srv) { - int err; - - err = cn_add_callback(&kvp_id, kvp_name, kvp_cn_callback); - if (err) - return err; recv_buffer = srv->recv_buffer; /* @@ -709,14 +801,20 @@ hv_kvp_init(struct hv_util_service *srv) * Defer processing channel callbacks until the daemon * has registered. */ - kvp_transaction.active = true; + kvp_device.state = KVP_DEVICE_INITIALIZING; + init_waitqueue_head(&kvp_device.proc_list); + mutex_init(&kvp_device.lock); - return 0; + return misc_register(&kvp_misc); } void hv_kvp_deinit(void) { - cn_del_callback(&kvp_id); + kvp_device.state = KVP_DEVICE_DYING; + /* Make sure nobody sees the old state */ + smp_mb(); + wake_up_interruptible(&kvp_device.proc_list); cancel_delayed_work_sync(&kvp_work); cancel_work_sync(&kvp_sendkey_work); + misc_deregister(&kvp_misc); } diff --git a/include/uapi/linux/hyperv.h b/include/uapi/linux/hyperv.h index bb1cb73..80713a3 100644 --- a/include/uapi/linux/hyperv.h +++ b/include/uapi/linux/hyperv.h @@ -26,6 +26,7 @@ #define _UAPI_HYPERV_H #include <linux/uuid.h> +#include <linux/types.h> /* * Framework version for util services. @@ -389,4 +390,11 @@ struct hv_kvp_ip_msg { struct hv_kvp_ipaddr_value kvp_ip_val; } __attribute__((packed)); +/* + * Userspace registration ioctls. Userspace daemons are supposed to pass their + * version as a parameter and get driver version back. KVP daemon supplies + * either KVP_OP_REGISTER or KVP_OP_REGISTER1. + */ +#define HYPERV_KVP_REGISTER _IOWR('v', 0, __u32) + #endif /* _UAPI_HYPERV_H */ diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 408bb07..0c3cac7 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -33,7 +33,6 @@ #include <ctype.h> #include <errno.h> #include <arpa/inet.h> -#include <linux/connector.h> #include <linux/hyperv.h> #include <linux/netlink.h> #include <ifaddrs.h> @@ -44,6 +43,7 @@ #include <dirent.h> #include <net/if.h> #include <getopt.h> +#include <sys/ioctl.h> /* * KVP protocol: The user mode component first registers with the @@ -79,9 +79,6 @@ enum { DNS }; -static struct sockaddr_nl addr; -static int in_hand_shake = 1; - static char *os_name = ""; static char *os_major = ""; static char *os_minor = ""; @@ -1387,34 +1384,6 @@ kvp_get_domain_name(char *buffer, int length) freeaddrinfo(info); } -static int -netlink_send(int fd, struct cn_msg *msg) -{ - struct nlmsghdr nlh = { .nlmsg_type = NLMSG_DONE }; - unsigned int size; - struct msghdr message; - struct iovec iov[2]; - - size = sizeof(struct cn_msg) + msg->len; - - nlh.nlmsg_pid = getpid(); - nlh.nlmsg_len = NLMSG_LENGTH(size); - - iov[0].iov_base = &nlh; - iov[0].iov_len = sizeof(nlh); - - iov[1].iov_base = msg; - iov[1].iov_len = size; - - memset(&message, 0, sizeof(message)); - message.msg_name = &addr; - message.msg_namelen = sizeof(addr); - message.msg_iov = iov; - message.msg_iovlen = 2; - - return sendmsg(fd, &message, 0); -} - void print_usage(char *argv[]) { fprintf(stderr, "Usage: %s [options]\n" @@ -1425,23 +1394,18 @@ void print_usage(char *argv[]) int main(int argc, char *argv[]) { - int fd, len, nl_group; + int kvp_fd, len; int error; - struct cn_msg *message; struct pollfd pfd; - struct nlmsghdr *incoming_msg; - struct cn_msg *incoming_cn_msg; - struct hv_kvp_msg *hv_msg; - char *p; + struct hv_kvp_msg hv_msg[1]; char *key_value; char *key_name; int op; int pool; char *if_name; struct hv_kvp_ipaddr_value *kvp_ip_val; - char *kvp_recv_buffer; - size_t kvp_recv_buffer_len; int daemonize = 1, long_index = 0, opt; + __u32 daemon_ver = (__u32)KVP_OP_REGISTER1; static struct option long_options[] = { {"help", no_argument, 0, 'h' }, @@ -1468,12 +1432,14 @@ int main(int argc, char *argv[]) openlog("KVP", 0, LOG_USER); syslog(LOG_INFO, "KVP starting; pid is:%d", getpid()); - kvp_recv_buffer_len = NLMSG_LENGTH(0) + sizeof(struct cn_msg) + sizeof(struct hv_kvp_msg); - kvp_recv_buffer = calloc(1, kvp_recv_buffer_len); - if (!kvp_recv_buffer) { - syslog(LOG_ERR, "Failed to allocate netlink buffer"); + kvp_fd = open("/dev/vmbus/hv_kvp", O_RDWR); + + if (kvp_fd < 0) { + syslog(LOG_ERR, "open /dev/vmbus/hv_kvp failed; error: %d %s", + errno, strerror(errno)); exit(EXIT_FAILURE); } + /* * Retrieve OS release information. */ @@ -1489,100 +1455,44 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } - fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); - if (fd < 0) { - syslog(LOG_ERR, "netlink socket creation failed; error: %d %s", errno, - strerror(errno)); - exit(EXIT_FAILURE); - } - addr.nl_family = AF_NETLINK; - addr.nl_pad = 0; - addr.nl_pid = 0; - addr.nl_groups = 0; - - - error = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); - if (error < 0) { - syslog(LOG_ERR, "bind failed; error: %d %s", errno, strerror(errno)); - close(fd); - exit(EXIT_FAILURE); - } - nl_group = CN_KVP_IDX; - - if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)) < 0) { - syslog(LOG_ERR, "setsockopt failed; error: %d %s", errno, strerror(errno)); - close(fd); - exit(EXIT_FAILURE); - } - /* * Register ourselves with the kernel. */ - message = (struct cn_msg *)kvp_recv_buffer; - message->id.idx = CN_KVP_IDX; - message->id.val = CN_KVP_VAL; - - hv_msg = (struct hv_kvp_msg *)message->data; - hv_msg->kvp_hdr.operation = KVP_OP_REGISTER1; - message->ack = 0; - message->len = sizeof(struct hv_kvp_msg); - - len = netlink_send(fd, message); - if (len < 0) { - syslog(LOG_ERR, "netlink_send failed; error: %d %s", errno, strerror(errno)); - close(fd); + if (ioctl(kvp_fd, HYPERV_KVP_REGISTER, &daemon_ver)) { + syslog(LOG_ERR, "registration to kernel failed; error: %d %s", + errno, strerror(errno)); + close(kvp_fd); exit(EXIT_FAILURE); } - pfd.fd = fd; + syslog(LOG_INFO, "KVP LIC Version: %d", daemon_ver); + + pfd.fd = kvp_fd; while (1) { - struct sockaddr *addr_p = (struct sockaddr *) &addr; - socklen_t addr_l = sizeof(addr); pfd.events = POLLIN; pfd.revents = 0; if (poll(&pfd, 1, -1) < 0) { syslog(LOG_ERR, "poll failed; error: %d %s", errno, strerror(errno)); if (errno == EINVAL) { - close(fd); + close(kvp_fd); exit(EXIT_FAILURE); } else continue; } - len = recvfrom(fd, kvp_recv_buffer, kvp_recv_buffer_len, 0, - addr_p, &addr_l); - - if (len < 0) { - int saved_errno = errno; - syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s", - addr.nl_pid, errno, strerror(errno)); - - if (saved_errno == ENOBUFS) { - syslog(LOG_ERR, "receive error: ignored"); - continue; - } + len = read(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg)); - close(fd); - return -1; - } + if (len != sizeof(struct hv_kvp_msg)) { + syslog(LOG_ERR, "read failed; error:%d %s", + errno, strerror(errno)); - if (addr.nl_pid) { - syslog(LOG_WARNING, "Received packet from untrusted pid:%u", - addr.nl_pid); - continue; + close(kvp_fd); + return EXIT_FAILURE; } - incoming_msg = (struct nlmsghdr *)kvp_recv_buffer; - - if (incoming_msg->nlmsg_type != NLMSG_DONE) - continue; - - incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); - hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; - /* * We will use the KVP header information to pass back * the error from this daemon. So, first copy the state @@ -1592,24 +1502,6 @@ int main(int argc, char *argv[]) pool = hv_msg->kvp_hdr.pool; hv_msg->error = HV_S_OK; - if ((in_hand_shake) && (op == KVP_OP_REGISTER1)) { - /* - * Driver is registering with us; stash away the version - * information. - */ - in_hand_shake = 0; - p = (char *)hv_msg->body.kvp_register.version; - lic_version = malloc(strlen(p) + 1); - if (lic_version) { - strcpy(lic_version, p); - syslog(LOG_INFO, "KVP LIC Version: %s", - lic_version); - } else { - syslog(LOG_ERR, "malloc failed"); - } - continue; - } - switch (op) { case KVP_OP_GET_IP_INFO: kvp_ip_val = &hv_msg->body.kvp_ip_val; @@ -1702,7 +1594,6 @@ int main(int argc, char *argv[]) goto kvp_done; } - hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; key_name = (char *)hv_msg->body.kvp_enum_data.data.key; key_value = (char *)hv_msg->body.kvp_enum_data.data.value; @@ -1753,31 +1644,17 @@ int main(int argc, char *argv[]) hv_msg->error = HV_S_CONT; break; } - /* - * Send the value back to the kernel. The response is - * already in the receive buffer. Update the cn_msg header to - * reflect the key value that has been added to the message - */ -kvp_done: - - incoming_cn_msg->id.idx = CN_KVP_IDX; - incoming_cn_msg->id.val = CN_KVP_VAL; - incoming_cn_msg->ack = 0; - incoming_cn_msg->len = sizeof(struct hv_kvp_msg); - - len = netlink_send(fd, incoming_cn_msg); - if (len < 0) { - int saved_errno = errno; - syslog(LOG_ERR, "net_link send failed; error: %d %s", errno, - strerror(errno)); - - if (saved_errno == ENOMEM || saved_errno == ENOBUFS) { - syslog(LOG_ERR, "send error: ignored"); - continue; - } + /* Send the value back to the kernel. */ +kvp_done: + len = write(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg)); + if (len != sizeof(struct hv_kvp_msg)) { + syslog(LOG_ERR, "write failed; error: %d %s", errno, + strerror(errno)); exit(EXIT_FAILURE); } } + close(kvp_fd); + exit(0); } -- 1.9.3 -- To unsubscribe from this list: send the line "unsubscribe linux-api" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html