/*
* Copyright (c) 2009, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/module.h>
#include "hv_api.h"
#include "logging.h"
#include "vmbus_private.h"
#define NUM_PAGES_SPANNED(addr, len) \
((PAGE_ALIGN(addr + len) >> PAGE_SHIFT) - (addr >> PAGE_SHIFT))
/* Internal routines */
static int create_gpadl_header(
void *kbuffer, /* must be phys and virt contiguous */
u32 size, /* page-size multiple */
struct vmbus_channel_msginfo **msginfo,
u32 *messagecount);
static void dump_vmbus_channel(struct vmbus_channel *channel);
static void vmbus_setevent(struct vmbus_channel *channel);
#if 0
static void DumpMonitorPage(struct hv_monitor_page *MonitorPage)
{
int i = 0;
int j = 0;
DPRINT_DBG(VMBUS, "monitorPage - %p, trigger state - %d",
MonitorPage, MonitorPage->trigger_state);
for (i = 0; i < 4; i++)
DPRINT_DBG(VMBUS, "trigger group (%d) - %llx", i,
MonitorPage->trigger_group[i].as_uint64);
for (i = 0; i < 4; i++) {
for (j = 0; j < 32; j++) {
DPRINT_DBG(VMBUS, "latency (%d)(%d) - %llx", i, j,
MonitorPage->latency[i][j]);
}
}
for (i = 0; i < 4; i++) {
for (j = 0; j < 32; j++) {
DPRINT_DBG(VMBUS, "param-conn id (%d)(%d) - %d", i, j,
MonitorPage->parameter[i][j].connectionid.asu32);
DPRINT_DBG(VMBUS, "param-flag (%d)(%d) - %d", i, j,
MonitorPage->parameter[i][j].flag_number);
}
}
}
#endif
/*
* vmbus_setevent- Trigger an event notification on the specified
* channel.
*/
static void vmbus_setevent(struct vmbus_channel *channel)
{
struct hv_monitor_page *monitorpage;
if (channel->offermsg.monitor_allocated) {
/* Each u32 represents 32 channels */
sync_set_bit(channel->offermsg.child_relid & 31,
(unsigned long *) vmbus_connection.send_int_page +
(channel->offermsg.child_relid >> 5));
monitorpage = vmbus_connection.monitor_pages;
monitorpage++; /* Get the child to parent monitor page */
sync_set_bit(channel->monitor_bit,
(unsigned long *)&monitorpage->trigger_group
[channel->monitor_grp].pending);
} else {
vmbus_set_event(channel->offermsg.child_relid);
}
}
#if 0
static void VmbusChannelClearEvent(struct vmbus_channel *channel)
{
struct hv_monitor_page *monitorPage;
if (Channel->offermsg.monitor_allocated) {
/* Each u32 represents 32 channels */
sync_clear_bit(Channel->offermsg.child_relid & 31,
(unsigned long *)vmbus_connection.send_int_page +
(Channel->offermsg.child_relid >> 5));
monitorPage = (struct hv_monitor_page *)
vmbus_connection.monitor_pages;
monitorPage++; /* Get the child to parent monitor page */
sync_clear_bit(Channel->monitor_bit,
(unsigned long *)&monitorPage->trigger_group
[Channel->monitor_grp].Pending);
}
}
#endif
/*
* vmbus_get_debug_info -Retrieve various channel debug info
*/
void vmbus_get_debug_info(struct vmbus_channel *channel,
struct vmbus_channel_debug_info *debuginfo)
{
struct hv_monitor_page *monitorpage;
u8 monitor_group = (u8)channel->offermsg.monitorid / 32;
u8 monitor_offset = (u8)channel->offermsg.monitorid % 32;
/* u32 monitorBit = 1 << monitorOffset; */
debuginfo->relid = channel->offermsg.child_relid;
debuginfo->state = channel->state;
memcpy(&debuginfo->interfacetype,
&channel->offermsg.offer.if_type, sizeof(struct hv_guid));
memcpy(&debuginfo->interface_instance,
&channel->offermsg.offer.if_instance,
sizeof(struct hv_guid));
monitorpage = (struct hv_monitor_page *)vmbus_connection.monitor_pages;
debuginfo->monitorid = channel->offermsg.monitorid;
debuginfo->servermonitor_pending =
monitorpage->trigger_group[monitor_group].pending;
debuginfo->servermonitor_latency =
monitorpage->latency[monitor_group][monitor_offset];
debuginfo->servermonitor_connectionid =
monitorpage->parameter[monitor_group]
[monitor_offset].connectionid.u.id;
monitorpage++;
debuginfo->clientmonitor_pending =
monitorpage->trigger_group[monitor_group].pending;
debuginfo->clientmonitor_latency =
monitorpage->latency[monitor_group][monitor_offset];
debuginfo->clientmonitor_connectionid =
monitorpage->parameter[monitor_group]
[monitor_offset].connectionid.u.id;
ringbuffer_get_debuginfo(&channel->inbound, &debuginfo->inbound);
ringbuffer_get_debuginfo(&channel->outbound, &debuginfo->outbound);
}
/*
* vmbus_open - Open the specified channel.
*/
int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
u32 recv_ringbuffer_size, void *userdata, u32 userdatalen,
void (*onchannelcallback)(void *context), void *context)
{
struct