114b50f80SVitaly Kuznetsov /* 214b50f80SVitaly Kuznetsov * Kernel/userspace transport abstraction for Hyper-V util driver. 314b50f80SVitaly Kuznetsov * 414b50f80SVitaly Kuznetsov * Copyright (C) 2015, Vitaly Kuznetsov <vkuznets@redhat.com> 514b50f80SVitaly Kuznetsov * 614b50f80SVitaly Kuznetsov * This program is free software; you can redistribute it and/or modify it 714b50f80SVitaly Kuznetsov * under the terms of the GNU General Public License version 2 as published 814b50f80SVitaly Kuznetsov * by the Free Software Foundation. 914b50f80SVitaly Kuznetsov * 1014b50f80SVitaly Kuznetsov * This program is distributed in the hope that it will be useful, but 1114b50f80SVitaly Kuznetsov * WITHOUT ANY WARRANTY; without even the implied warranty of 1214b50f80SVitaly Kuznetsov * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 1314b50f80SVitaly Kuznetsov * NON INFRINGEMENT. See the GNU General Public License for more 1414b50f80SVitaly Kuznetsov * details. 1514b50f80SVitaly Kuznetsov * 1614b50f80SVitaly Kuznetsov */ 1714b50f80SVitaly Kuznetsov 1814b50f80SVitaly Kuznetsov #include <linux/slab.h> 1914b50f80SVitaly Kuznetsov #include <linux/fs.h> 2014b50f80SVitaly Kuznetsov #include <linux/poll.h> 2114b50f80SVitaly Kuznetsov 2214b50f80SVitaly Kuznetsov #include "hyperv_vmbus.h" 2314b50f80SVitaly Kuznetsov #include "hv_utils_transport.h" 2414b50f80SVitaly Kuznetsov 2514b50f80SVitaly Kuznetsov static DEFINE_SPINLOCK(hvt_list_lock); 2614b50f80SVitaly Kuznetsov static struct list_head hvt_list = LIST_HEAD_INIT(hvt_list); 2714b50f80SVitaly Kuznetsov 2814b50f80SVitaly Kuznetsov static void hvt_reset(struct hvutil_transport *hvt) 2914b50f80SVitaly Kuznetsov { 3014b50f80SVitaly Kuznetsov mutex_lock(&hvt->outmsg_lock); 3114b50f80SVitaly Kuznetsov kfree(hvt->outmsg); 3214b50f80SVitaly Kuznetsov hvt->outmsg = NULL; 3314b50f80SVitaly Kuznetsov hvt->outmsg_len = 0; 3414b50f80SVitaly Kuznetsov mutex_unlock(&hvt->outmsg_lock); 3514b50f80SVitaly Kuznetsov if (hvt->on_reset) 3614b50f80SVitaly Kuznetsov hvt->on_reset(); 3714b50f80SVitaly Kuznetsov } 3814b50f80SVitaly Kuznetsov 3914b50f80SVitaly Kuznetsov static ssize_t hvt_op_read(struct file *file, char __user *buf, 4014b50f80SVitaly Kuznetsov size_t count, loff_t *ppos) 4114b50f80SVitaly Kuznetsov { 4214b50f80SVitaly Kuznetsov struct hvutil_transport *hvt; 4314b50f80SVitaly Kuznetsov int ret; 4414b50f80SVitaly Kuznetsov 4514b50f80SVitaly Kuznetsov hvt = container_of(file->f_op, struct hvutil_transport, fops); 4614b50f80SVitaly Kuznetsov 4714b50f80SVitaly Kuznetsov if (wait_event_interruptible(hvt->outmsg_q, hvt->outmsg_len > 0)) 4814b50f80SVitaly Kuznetsov return -EINTR; 4914b50f80SVitaly Kuznetsov 5014b50f80SVitaly Kuznetsov mutex_lock(&hvt->outmsg_lock); 5114b50f80SVitaly Kuznetsov if (!hvt->outmsg) { 5214b50f80SVitaly Kuznetsov ret = -EAGAIN; 5314b50f80SVitaly Kuznetsov goto out_unlock; 5414b50f80SVitaly Kuznetsov } 5514b50f80SVitaly Kuznetsov 5614b50f80SVitaly Kuznetsov if (count < hvt->outmsg_len) { 5714b50f80SVitaly Kuznetsov ret = -EINVAL; 5814b50f80SVitaly Kuznetsov goto out_unlock; 5914b50f80SVitaly Kuznetsov } 6014b50f80SVitaly Kuznetsov 6114b50f80SVitaly Kuznetsov if (!copy_to_user(buf, hvt->outmsg, hvt->outmsg_len)) 6214b50f80SVitaly Kuznetsov ret = hvt->outmsg_len; 6314b50f80SVitaly Kuznetsov else 6414b50f80SVitaly Kuznetsov ret = -EFAULT; 6514b50f80SVitaly Kuznetsov 6614b50f80SVitaly Kuznetsov kfree(hvt->outmsg); 6714b50f80SVitaly Kuznetsov hvt->outmsg = NULL; 6814b50f80SVitaly Kuznetsov hvt->outmsg_len = 0; 6914b50f80SVitaly Kuznetsov 7014b50f80SVitaly Kuznetsov out_unlock: 7114b50f80SVitaly Kuznetsov mutex_unlock(&hvt->outmsg_lock); 7214b50f80SVitaly Kuznetsov return ret; 7314b50f80SVitaly Kuznetsov } 7414b50f80SVitaly Kuznetsov 7514b50f80SVitaly Kuznetsov static ssize_t hvt_op_write(struct file *file, const char __user *buf, 7614b50f80SVitaly Kuznetsov size_t count, loff_t *ppos) 7714b50f80SVitaly Kuznetsov { 7814b50f80SVitaly Kuznetsov struct hvutil_transport *hvt; 7914b50f80SVitaly Kuznetsov u8 *inmsg; 8014b50f80SVitaly Kuznetsov 8114b50f80SVitaly Kuznetsov hvt = container_of(file->f_op, struct hvutil_transport, fops); 8214b50f80SVitaly Kuznetsov 8314b50f80SVitaly Kuznetsov inmsg = kzalloc(count, GFP_KERNEL); 8414b50f80SVitaly Kuznetsov if (copy_from_user(inmsg, buf, count)) { 8514b50f80SVitaly Kuznetsov kfree(inmsg); 8614b50f80SVitaly Kuznetsov return -EFAULT; 8714b50f80SVitaly Kuznetsov } 8814b50f80SVitaly Kuznetsov if (hvt->on_msg(inmsg, count)) 8914b50f80SVitaly Kuznetsov return -EFAULT; 9014b50f80SVitaly Kuznetsov kfree(inmsg); 9114b50f80SVitaly Kuznetsov 9214b50f80SVitaly Kuznetsov return count; 9314b50f80SVitaly Kuznetsov } 9414b50f80SVitaly Kuznetsov 9514b50f80SVitaly Kuznetsov static unsigned int hvt_op_poll(struct file *file, poll_table *wait) 9614b50f80SVitaly Kuznetsov { 9714b50f80SVitaly Kuznetsov struct hvutil_transport *hvt; 9814b50f80SVitaly Kuznetsov 9914b50f80SVitaly Kuznetsov hvt = container_of(file->f_op, struct hvutil_transport, fops); 10014b50f80SVitaly Kuznetsov 10114b50f80SVitaly Kuznetsov poll_wait(file, &hvt->outmsg_q, wait); 10214b50f80SVitaly Kuznetsov if (hvt->outmsg_len > 0) 10314b50f80SVitaly Kuznetsov return POLLIN | POLLRDNORM; 10414b50f80SVitaly Kuznetsov 10514b50f80SVitaly Kuznetsov return 0; 10614b50f80SVitaly Kuznetsov } 10714b50f80SVitaly Kuznetsov 10814b50f80SVitaly Kuznetsov static int hvt_op_open(struct inode *inode, struct file *file) 10914b50f80SVitaly Kuznetsov { 11014b50f80SVitaly Kuznetsov struct hvutil_transport *hvt; 11114b50f80SVitaly Kuznetsov 11214b50f80SVitaly Kuznetsov hvt = container_of(file->f_op, struct hvutil_transport, fops); 11314b50f80SVitaly Kuznetsov 11414b50f80SVitaly Kuznetsov /* 11514b50f80SVitaly Kuznetsov * Switching to CHARDEV mode. We switch bach to INIT when device 11614b50f80SVitaly Kuznetsov * gets released. 11714b50f80SVitaly Kuznetsov */ 11814b50f80SVitaly Kuznetsov if (hvt->mode == HVUTIL_TRANSPORT_INIT) 11914b50f80SVitaly Kuznetsov hvt->mode = HVUTIL_TRANSPORT_CHARDEV; 12014b50f80SVitaly Kuznetsov else if (hvt->mode == HVUTIL_TRANSPORT_NETLINK) { 12114b50f80SVitaly Kuznetsov /* 12214b50f80SVitaly Kuznetsov * We're switching from netlink communication to using char 12314b50f80SVitaly Kuznetsov * device. Issue the reset first. 12414b50f80SVitaly Kuznetsov */ 12514b50f80SVitaly Kuznetsov hvt_reset(hvt); 12614b50f80SVitaly Kuznetsov hvt->mode = HVUTIL_TRANSPORT_CHARDEV; 12714b50f80SVitaly Kuznetsov } else 12814b50f80SVitaly Kuznetsov return -EBUSY; 12914b50f80SVitaly Kuznetsov 13014b50f80SVitaly Kuznetsov return 0; 13114b50f80SVitaly Kuznetsov } 13214b50f80SVitaly Kuznetsov 13314b50f80SVitaly Kuznetsov static int hvt_op_release(struct inode *inode, struct file *file) 13414b50f80SVitaly Kuznetsov { 13514b50f80SVitaly Kuznetsov struct hvutil_transport *hvt; 13614b50f80SVitaly Kuznetsov 13714b50f80SVitaly Kuznetsov hvt = container_of(file->f_op, struct hvutil_transport, fops); 13814b50f80SVitaly Kuznetsov 13914b50f80SVitaly Kuznetsov hvt->mode = HVUTIL_TRANSPORT_INIT; 14014b50f80SVitaly Kuznetsov /* 14114b50f80SVitaly Kuznetsov * Cleanup message buffers to avoid spurious messages when the daemon 14214b50f80SVitaly Kuznetsov * connects back. 14314b50f80SVitaly Kuznetsov */ 14414b50f80SVitaly Kuznetsov hvt_reset(hvt); 14514b50f80SVitaly Kuznetsov 14614b50f80SVitaly Kuznetsov return 0; 14714b50f80SVitaly Kuznetsov } 14814b50f80SVitaly Kuznetsov 14914b50f80SVitaly Kuznetsov static void hvt_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) 15014b50f80SVitaly Kuznetsov { 15114b50f80SVitaly Kuznetsov struct hvutil_transport *hvt, *hvt_found = NULL; 15214b50f80SVitaly Kuznetsov 15314b50f80SVitaly Kuznetsov spin_lock(&hvt_list_lock); 15414b50f80SVitaly Kuznetsov list_for_each_entry(hvt, &hvt_list, list) { 15514b50f80SVitaly Kuznetsov if (hvt->cn_id.idx == msg->id.idx && 15614b50f80SVitaly Kuznetsov hvt->cn_id.val == msg->id.val) { 15714b50f80SVitaly Kuznetsov hvt_found = hvt; 15814b50f80SVitaly Kuznetsov break; 15914b50f80SVitaly Kuznetsov } 16014b50f80SVitaly Kuznetsov } 16114b50f80SVitaly Kuznetsov spin_unlock(&hvt_list_lock); 16214b50f80SVitaly Kuznetsov if (!hvt_found) { 16314b50f80SVitaly Kuznetsov pr_warn("hvt_cn_callback: spurious message received!\n"); 16414b50f80SVitaly Kuznetsov return; 16514b50f80SVitaly Kuznetsov } 16614b50f80SVitaly Kuznetsov 16714b50f80SVitaly Kuznetsov /* 16814b50f80SVitaly Kuznetsov * Switching to NETLINK mode. Switching to CHARDEV happens when someone 16914b50f80SVitaly Kuznetsov * opens the device. 17014b50f80SVitaly Kuznetsov */ 17114b50f80SVitaly Kuznetsov if (hvt->mode == HVUTIL_TRANSPORT_INIT) 17214b50f80SVitaly Kuznetsov hvt->mode = HVUTIL_TRANSPORT_NETLINK; 17314b50f80SVitaly Kuznetsov 17414b50f80SVitaly Kuznetsov if (hvt->mode == HVUTIL_TRANSPORT_NETLINK) 17514b50f80SVitaly Kuznetsov hvt_found->on_msg(msg->data, msg->len); 17614b50f80SVitaly Kuznetsov else 17714b50f80SVitaly Kuznetsov pr_warn("hvt_cn_callback: unexpected netlink message!\n"); 17814b50f80SVitaly Kuznetsov } 17914b50f80SVitaly Kuznetsov 18014b50f80SVitaly Kuznetsov int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len) 18114b50f80SVitaly Kuznetsov { 18214b50f80SVitaly Kuznetsov struct cn_msg *cn_msg; 18314b50f80SVitaly Kuznetsov int ret = 0; 18414b50f80SVitaly Kuznetsov 18514b50f80SVitaly Kuznetsov if (hvt->mode == HVUTIL_TRANSPORT_INIT) { 18614b50f80SVitaly Kuznetsov return -EINVAL; 18714b50f80SVitaly Kuznetsov } else if (hvt->mode == HVUTIL_TRANSPORT_NETLINK) { 18814b50f80SVitaly Kuznetsov cn_msg = kzalloc(sizeof(*cn_msg) + len, GFP_ATOMIC); 1899dd6a064SDan Carpenter if (!cn_msg) 19014b50f80SVitaly Kuznetsov return -ENOMEM; 19114b50f80SVitaly Kuznetsov cn_msg->id.idx = hvt->cn_id.idx; 19214b50f80SVitaly Kuznetsov cn_msg->id.val = hvt->cn_id.val; 19314b50f80SVitaly Kuznetsov cn_msg->len = len; 19414b50f80SVitaly Kuznetsov memcpy(cn_msg->data, msg, len); 19514b50f80SVitaly Kuznetsov ret = cn_netlink_send(cn_msg, 0, 0, GFP_ATOMIC); 19614b50f80SVitaly Kuznetsov kfree(cn_msg); 19714b50f80SVitaly Kuznetsov return ret; 19814b50f80SVitaly Kuznetsov } 19914b50f80SVitaly Kuznetsov /* HVUTIL_TRANSPORT_CHARDEV */ 20014b50f80SVitaly Kuznetsov mutex_lock(&hvt->outmsg_lock); 20114b50f80SVitaly Kuznetsov if (hvt->outmsg) { 20214b50f80SVitaly Kuznetsov /* Previous message wasn't received */ 20314b50f80SVitaly Kuznetsov ret = -EFAULT; 20414b50f80SVitaly Kuznetsov goto out_unlock; 20514b50f80SVitaly Kuznetsov } 20614b50f80SVitaly Kuznetsov hvt->outmsg = kzalloc(len, GFP_KERNEL); 207cdc0c0c9SOlaf Hering if (hvt->outmsg) { 20814b50f80SVitaly Kuznetsov memcpy(hvt->outmsg, msg, len); 20914b50f80SVitaly Kuznetsov hvt->outmsg_len = len; 21014b50f80SVitaly Kuznetsov wake_up_interruptible(&hvt->outmsg_q); 211cdc0c0c9SOlaf Hering } else 212cdc0c0c9SOlaf Hering ret = -ENOMEM; 21314b50f80SVitaly Kuznetsov out_unlock: 21414b50f80SVitaly Kuznetsov mutex_unlock(&hvt->outmsg_lock); 21514b50f80SVitaly Kuznetsov return ret; 21614b50f80SVitaly Kuznetsov } 21714b50f80SVitaly Kuznetsov 21814b50f80SVitaly Kuznetsov struct hvutil_transport *hvutil_transport_init(const char *name, 21914b50f80SVitaly Kuznetsov u32 cn_idx, u32 cn_val, 22014b50f80SVitaly Kuznetsov int (*on_msg)(void *, int), 22114b50f80SVitaly Kuznetsov void (*on_reset)(void)) 22214b50f80SVitaly Kuznetsov { 22314b50f80SVitaly Kuznetsov struct hvutil_transport *hvt; 22414b50f80SVitaly Kuznetsov 22514b50f80SVitaly Kuznetsov hvt = kzalloc(sizeof(*hvt), GFP_KERNEL); 22614b50f80SVitaly Kuznetsov if (!hvt) 22714b50f80SVitaly Kuznetsov return NULL; 22814b50f80SVitaly Kuznetsov 22914b50f80SVitaly Kuznetsov hvt->cn_id.idx = cn_idx; 23014b50f80SVitaly Kuznetsov hvt->cn_id.val = cn_val; 23114b50f80SVitaly Kuznetsov 23214b50f80SVitaly Kuznetsov hvt->mdev.minor = MISC_DYNAMIC_MINOR; 23314b50f80SVitaly Kuznetsov hvt->mdev.name = name; 23414b50f80SVitaly Kuznetsov 23514b50f80SVitaly Kuznetsov hvt->fops.owner = THIS_MODULE; 23614b50f80SVitaly Kuznetsov hvt->fops.read = hvt_op_read; 23714b50f80SVitaly Kuznetsov hvt->fops.write = hvt_op_write; 23814b50f80SVitaly Kuznetsov hvt->fops.poll = hvt_op_poll; 23914b50f80SVitaly Kuznetsov hvt->fops.open = hvt_op_open; 24014b50f80SVitaly Kuznetsov hvt->fops.release = hvt_op_release; 24114b50f80SVitaly Kuznetsov 24214b50f80SVitaly Kuznetsov hvt->mdev.fops = &hvt->fops; 24314b50f80SVitaly Kuznetsov 24414b50f80SVitaly Kuznetsov init_waitqueue_head(&hvt->outmsg_q); 24514b50f80SVitaly Kuznetsov mutex_init(&hvt->outmsg_lock); 24614b50f80SVitaly Kuznetsov 24714b50f80SVitaly Kuznetsov spin_lock(&hvt_list_lock); 24814b50f80SVitaly Kuznetsov list_add(&hvt->list, &hvt_list); 24914b50f80SVitaly Kuznetsov spin_unlock(&hvt_list_lock); 25014b50f80SVitaly Kuznetsov 25114b50f80SVitaly Kuznetsov hvt->on_msg = on_msg; 25214b50f80SVitaly Kuznetsov hvt->on_reset = on_reset; 25314b50f80SVitaly Kuznetsov 25414b50f80SVitaly Kuznetsov if (misc_register(&hvt->mdev)) 25514b50f80SVitaly Kuznetsov goto err_free_hvt; 25614b50f80SVitaly Kuznetsov 25714b50f80SVitaly Kuznetsov /* Use cn_id.idx/cn_id.val to determine if we need to setup netlink */ 25814b50f80SVitaly Kuznetsov if (hvt->cn_id.idx > 0 && hvt->cn_id.val > 0 && 25914b50f80SVitaly Kuznetsov cn_add_callback(&hvt->cn_id, name, hvt_cn_callback)) 26014b50f80SVitaly Kuznetsov goto err_free_hvt; 26114b50f80SVitaly Kuznetsov 26214b50f80SVitaly Kuznetsov return hvt; 26314b50f80SVitaly Kuznetsov 26414b50f80SVitaly Kuznetsov err_free_hvt: 26514b50f80SVitaly Kuznetsov kfree(hvt); 26614b50f80SVitaly Kuznetsov return NULL; 26714b50f80SVitaly Kuznetsov } 26814b50f80SVitaly Kuznetsov 26914b50f80SVitaly Kuznetsov void hvutil_transport_destroy(struct hvutil_transport *hvt) 27014b50f80SVitaly Kuznetsov { 27114b50f80SVitaly Kuznetsov spin_lock(&hvt_list_lock); 27214b50f80SVitaly Kuznetsov list_del(&hvt->list); 27314b50f80SVitaly Kuznetsov spin_unlock(&hvt_list_lock); 27414b50f80SVitaly Kuznetsov if (hvt->cn_id.idx > 0 && hvt->cn_id.val > 0) 27514b50f80SVitaly Kuznetsov cn_del_callback(&hvt->cn_id); 27614b50f80SVitaly Kuznetsov misc_deregister(&hvt->mdev); 27714b50f80SVitaly Kuznetsov kfree(hvt->outmsg); 27814b50f80SVitaly Kuznetsov kfree(hvt); 27914b50f80SVitaly Kuznetsov } 280