/* * List of resources to be mapped to user space * can be extended up to MAX_UIO_MAPS(5) items
*/ enum hv_uio_map {
TXRX_RING_MAP = 0,
INT_PAGE_MAP,
MON_PAGE_MAP,
RECV_BUF_MAP,
SEND_BUF_MAP
};
staticvoid set_event(struct vmbus_channel *channel, s32 irq_state)
{
channel->inbound.ring_buffer->interrupt_mask = !irq_state; if (!channel->offermsg.monitor_allocated && irq_state) { /* MB is needed for host to see the interrupt mask first */
virt_mb();
vmbus_set_event(channel);
}
}
/* * This is the irqcontrol callback to be registered to uio_info. * It can be used to disable/enable interrupt from user space processes. * * @param info * pointer to uio_info. * @param irq_state * state value. 1 to enable interrupt, 0 to disable interrupt.
*/ staticint
hv_uio_irqcontrol(struct uio_info *info, s32 irq_state)
{ struct hv_uio_private_data *pdata = info->priv; struct hv_device *dev = pdata->device; struct vmbus_channel *primary, *sc;
/* * Callback from vmbus_event when something is in inbound ring.
*/ staticvoid hv_uio_channel_cb(void *context)
{ struct vmbus_channel *chan = context; struct hv_device *hv_dev; struct hv_uio_private_data *pdata;
virt_mb();
/* * The callback may come from a subchannel, in which case look * for the hv device in the primary channel
*/
hv_dev = chan->primary_channel ?
chan->primary_channel->device_obj : chan->device_obj;
pdata = hv_get_drvdata(hv_dev);
uio_event_notify(&pdata->info);
}
/* * Callback from vmbus_event when channel is rescinded. * It is meant for rescind of primary channels only.
*/ staticvoid hv_uio_rescind(struct vmbus_channel *channel)
{ struct hv_device *hv_dev = channel->device_obj; struct hv_uio_private_data *pdata = hv_get_drvdata(hv_dev);
/* * Turn off the interrupt file handle * Next read for event will return -EIO
*/
pdata->info.irq = 0;
/* Wake up reader */
uio_event_notify(&pdata->info);
/* * With rescind callback registered, rescind path will not unregister the device * from vmbus when the primary channel is rescinded. * Without it, rescind handling is incomplete and next onoffer msg does not come. * Unregister the device from vmbus here.
*/
vmbus_device_unregister(channel->device_obj);
}
/* Function used for mmap of ring buffer sysfs interface. * The ring buffer is allocated as contiguous memory by vmbus_open
*/ staticint
hv_uio_ring_mmap(struct vmbus_channel *channel, struct vm_area_struct *vma)
{ void *ring_buffer = page_address(channel->ringbuffer_page);
if (channel->state != CHANNEL_OPENED_STATE) return -ENODEV;
/* Callback from VMBUS subsystem when new channel created. */ staticvoid
hv_uio_new_channel(struct vmbus_channel *new_sc)
{ struct hv_device *hv_dev = new_sc->primary_channel->device_obj; struct device *device = &hv_dev->device; const size_t ring_bytes = SZ_2M; int ret;
/* Create host communication ring */
ret = vmbus_open(new_sc, ring_bytes, ring_bytes, NULL, 0,
hv_uio_channel_cb, new_sc); if (ret) {
dev_err(device, "vmbus_open subchannel failed: %d\n", ret); return;
}
set_channel_read_mode(new_sc, HV_CALL_ISR);
ret = hv_create_ring_sysfs(new_sc, hv_uio_ring_mmap); if (ret) {
dev_err(device, "sysfs create ring bin file failed; %d\n", ret);
vmbus_close(new_sc);
}
}
/* free the reserved buffers for send and receive */ staticvoid
hv_uio_cleanup(struct hv_device *dev, struct hv_uio_private_data *pdata)
{ if (pdata->send_gpadl.gpadl_handle) {
vmbus_teardown_gpadl(dev->channel, &pdata->send_gpadl); if (!pdata->send_gpadl.decrypted)
vfree(pdata->send_buf);
}
if (pdata->recv_gpadl.gpadl_handle) {
vmbus_teardown_gpadl(dev->channel, &pdata->recv_gpadl); if (!pdata->recv_gpadl.decrypted)
vfree(pdata->recv_buf);
}
}
/* VMBus primary channel is opened on first use */ staticint
hv_uio_open(struct uio_info *info, struct inode *inode)
{ struct hv_uio_private_data *pdata
= container_of(info, struct hv_uio_private_data, info); struct hv_device *dev = pdata->device; int ret;
if (atomic_inc_return(&pdata->refcnt) != 1) return 0;
if (channel->device_id == HV_NIC) {
pdata->recv_buf = vzalloc(RECV_BUFFER_SIZE); if (!pdata->recv_buf) {
ret = -ENOMEM; goto fail_free_ring;
}
ret = vmbus_establish_gpadl(channel, pdata->recv_buf,
RECV_BUFFER_SIZE, &pdata->recv_gpadl); if (ret) { if (!pdata->recv_gpadl.decrypted)
vfree(pdata->recv_buf); goto fail_close;
}
/* put Global Physical Address Label in name */
snprintf(pdata->recv_name, sizeof(pdata->recv_name), "recv:%u", pdata->recv_gpadl.gpadl_handle);
pdata->info.mem[RECV_BUF_MAP].name = pdata->recv_name;
pdata->info.mem[RECV_BUF_MAP].addr = (uintptr_t)pdata->recv_buf;
pdata->info.mem[RECV_BUF_MAP].size = RECV_BUFFER_SIZE;
pdata->info.mem[RECV_BUF_MAP].memtype = UIO_MEM_VIRTUAL;
pdata->send_buf = vzalloc(SEND_BUFFER_SIZE); if (!pdata->send_buf) {
ret = -ENOMEM; goto fail_close;
}
ret = vmbus_establish_gpadl(channel, pdata->send_buf,
SEND_BUFFER_SIZE, &pdata->send_gpadl); if (ret) { if (!pdata->send_gpadl.decrypted)
vfree(pdata->send_buf); goto fail_close;
}
ret = uio_register_device(&dev->device, &pdata->info); if (ret) {
dev_err(&dev->device, "hv_uio register failed\n"); goto fail_close;
}
/* * This internally calls sysfs_update_group, which returns a non-zero value if it executes * before sysfs_create_group. This is expected as the 'ring' will be created later in * vmbus_device_register() -> vmbus_add_channel_kobj(). Thus, no need to check the return * value and print warning. * * Creating/exposing sysfs in driver probe is not encouraged as it can lead to race * conditions with userspace. For backward compatibility, "ring" sysfs could not be removed * or decoupled from uio_hv_generic probe. Userspace programs can make use of inotify * APIs to make sure that ring is created.
*/
hv_create_ring_sysfs(channel, hv_uio_ring_mmap);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.