vmbus: add per-channel sysfs info

This extends existing vmbus related sysfs structure to provide per-channel
state information. This is useful when diagnosing issues with multiple
queues in networking and storage.

The existing sysfs only displayed information about the primary
channel. The one place it reported multiple channels was the
channel_vp_mapping file which violated the sysfs convention
of one value per file.

Signed-off-by: Stephen Hemminger <sthemmin@microsoft.com>
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Stephen Hemminger 2017-09-21 20:58:49 -07:00 committed by Greg Kroah-Hartman
parent 876a1b08d0
commit c2e5df616e
5 changed files with 246 additions and 13 deletions

View File

@ -41,3 +41,59 @@ KernelVersion: 4.5
Contact: K. Y. Srinivasan <kys@microsoft.com> Contact: K. Y. Srinivasan <kys@microsoft.com>
Description: The 16 bit vendor ID of the device Description: The 16 bit vendor ID of the device
Users: tools/hv/lsvmbus and user level RDMA libraries Users: tools/hv/lsvmbus and user level RDMA libraries
What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/cpu
Date: September. 2017
KernelVersion: 4.14
Contact: Stephen Hemminger <sthemmin@microsoft.com>
Description: VCPU (sub)channel is affinitized to
Users: tools/hv/lsvmbus and other debuggig tools
What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/cpu
Date: September. 2017
KernelVersion: 4.14
Contact: Stephen Hemminger <sthemmin@microsoft.com>
Description: VCPU (sub)channel is affinitized to
Users: tools/hv/lsvmbus and other debuggig tools
What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/in_mask
Date: September. 2017
KernelVersion: 4.14
Contact: Stephen Hemminger <sthemmin@microsoft.com>
Description: Inbound channel signaling state
Users: Debuggig tools
What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/latency
Date: September. 2017
KernelVersion: 4.14
Contact: Stephen Hemminger <sthemmin@microsoft.com>
Description: Channel signaling latency
Users: Debuggig tools
What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/out_mask
Date: September. 2017
KernelVersion: 4.14
Contact: Stephen Hemminger <sthemmin@microsoft.com>
Description: Outbound channel signaling state
Users: Debuggig tools
What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/pending
Date: September. 2017
KernelVersion: 4.14
Contact: Stephen Hemminger <sthemmin@microsoft.com>
Description: Channel interrupt pending state
Users: Debuggig tools
What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/read_avail
Date: September. 2017
KernelVersion: 4.14
Contact: Stephen Hemminger <sthemmin@microsoft.com>
Description: Bytes availabble to read
Users: Debuggig tools
What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/write_avail
Date: September. 2017
KernelVersion: 4.14
Contact: Stephen Hemminger <sthemmin@microsoft.com>
Description: Bytes availabble to write
Users: Debuggig tools

View File

@ -350,7 +350,7 @@ static void free_channel(struct vmbus_channel *channel)
{ {
tasklet_kill(&channel->callback_event); tasklet_kill(&channel->callback_event);
kfree_rcu(channel, rcu); kobject_put(&channel->kobj);
} }
static void percpu_channel_enq(void *arg) static void percpu_channel_enq(void *arg)
@ -513,6 +513,14 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
newchannel->state = CHANNEL_OPEN_STATE; newchannel->state = CHANNEL_OPEN_STATE;
if (!fnew) { if (!fnew) {
struct hv_device *dev
= newchannel->primary_channel->device_obj;
if (vmbus_add_channel_kobj(dev, newchannel)) {
atomic_dec(&vmbus_connection.offer_in_progress);
goto err_free_chan;
}
if (channel->sc_creation_callback != NULL) if (channel->sc_creation_callback != NULL)
channel->sc_creation_callback(newchannel); channel->sc_creation_callback(newchannel);
return; return;

View File

@ -373,6 +373,8 @@ struct hv_device *vmbus_device_create(const uuid_le *type,
int vmbus_device_register(struct hv_device *child_device_obj); int vmbus_device_register(struct hv_device *child_device_obj);
void vmbus_device_unregister(struct hv_device *device_obj); void vmbus_device_unregister(struct hv_device *device_obj);
int vmbus_add_channel_kobj(struct hv_device *device_obj,
struct vmbus_channel *channel);
struct vmbus_channel *relid2channel(u32 relid); struct vmbus_channel *relid2channel(u32 relid);

View File

@ -107,28 +107,30 @@ static void print_alias_name(struct hv_device *hv_dev, char *alias_name)
sprintf(&alias_name[i], "%02x", hv_dev->dev_type.b[i/2]); sprintf(&alias_name[i], "%02x", hv_dev->dev_type.b[i/2]);
} }
static u8 channel_monitor_group(struct vmbus_channel *channel) static u8 channel_monitor_group(const struct vmbus_channel *channel)
{ {
return (u8)channel->offermsg.monitorid / 32; return (u8)channel->offermsg.monitorid / 32;
} }
static u8 channel_monitor_offset(struct vmbus_channel *channel) static u8 channel_monitor_offset(const struct vmbus_channel *channel)
{ {
return (u8)channel->offermsg.monitorid % 32; return (u8)channel->offermsg.monitorid % 32;
} }
static u32 channel_pending(struct vmbus_channel *channel, static u32 channel_pending(const struct vmbus_channel *channel,
struct hv_monitor_page *monitor_page) const struct hv_monitor_page *monitor_page)
{ {
u8 monitor_group = channel_monitor_group(channel); u8 monitor_group = channel_monitor_group(channel);
return monitor_page->trigger_group[monitor_group].pending; return monitor_page->trigger_group[monitor_group].pending;
} }
static u32 channel_latency(struct vmbus_channel *channel, static u32 channel_latency(const struct vmbus_channel *channel,
struct hv_monitor_page *monitor_page) const struct hv_monitor_page *monitor_page)
{ {
u8 monitor_group = channel_monitor_group(channel); u8 monitor_group = channel_monitor_group(channel);
u8 monitor_offset = channel_monitor_offset(channel); u8 monitor_offset = channel_monitor_offset(channel);
return monitor_page->latency[monitor_group][monitor_offset]; return monitor_page->latency[monitor_group][monitor_offset];
} }
@ -1134,6 +1136,145 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver)
} }
EXPORT_SYMBOL_GPL(vmbus_driver_unregister); EXPORT_SYMBOL_GPL(vmbus_driver_unregister);
/*
* Called when last reference to channel is gone.
*/
static void vmbus_chan_release(struct kobject *kobj)
{
struct vmbus_channel *channel
= container_of(kobj, struct vmbus_channel, kobj);
kfree_rcu(channel, rcu);
}
struct vmbus_chan_attribute {
struct attribute attr;
ssize_t (*show)(const struct vmbus_channel *chan, char *buf);
ssize_t (*store)(struct vmbus_channel *chan,
const char *buf, size_t count);
};
#define VMBUS_CHAN_ATTR(_name, _mode, _show, _store) \
struct vmbus_chan_attribute chan_attr_##_name \
= __ATTR(_name, _mode, _show, _store)
#define VMBUS_CHAN_ATTR_RW(_name) \
struct vmbus_chan_attribute chan_attr_##_name = __ATTR_RW(_name)
#define VMBUS_CHAN_ATTR_RO(_name) \
struct vmbus_chan_attribute chan_attr_##_name = __ATTR_RO(_name)
#define VMBUS_CHAN_ATTR_WO(_name) \
struct vmbus_chan_attribute chan_attr_##_name = __ATTR_WO(_name)
static ssize_t vmbus_chan_attr_show(struct kobject *kobj,
struct attribute *attr, char *buf)
{
const struct vmbus_chan_attribute *attribute
= container_of(attr, struct vmbus_chan_attribute, attr);
const struct vmbus_channel *chan
= container_of(kobj, struct vmbus_channel, kobj);
if (!attribute->show)
return -EIO;
return attribute->show(chan, buf);
}
static const struct sysfs_ops vmbus_chan_sysfs_ops = {
.show = vmbus_chan_attr_show,
};
static ssize_t out_mask_show(const struct vmbus_channel *channel, char *buf)
{
const struct hv_ring_buffer_info *rbi = &channel->outbound;
return sprintf(buf, "%u\n", rbi->ring_buffer->interrupt_mask);
}
VMBUS_CHAN_ATTR_RO(out_mask);
static ssize_t in_mask_show(const struct vmbus_channel *channel, char *buf)
{
const struct hv_ring_buffer_info *rbi = &channel->inbound;
return sprintf(buf, "%u\n", rbi->ring_buffer->interrupt_mask);
}
VMBUS_CHAN_ATTR_RO(in_mask);
static ssize_t read_avail_show(const struct vmbus_channel *channel, char *buf)
{
const struct hv_ring_buffer_info *rbi = &channel->inbound;
return sprintf(buf, "%u\n", hv_get_bytes_to_read(rbi));
}
VMBUS_CHAN_ATTR_RO(read_avail);
static ssize_t write_avail_show(const struct vmbus_channel *channel, char *buf)
{
const struct hv_ring_buffer_info *rbi = &channel->outbound;
return sprintf(buf, "%u\n", hv_get_bytes_to_write(rbi));
}
VMBUS_CHAN_ATTR_RO(write_avail);
static ssize_t show_target_cpu(const struct vmbus_channel *channel, char *buf)
{
return sprintf(buf, "%u\n", channel->target_cpu);
}
VMBUS_CHAN_ATTR(cpu, S_IRUGO, show_target_cpu, NULL);
static ssize_t channel_pending_show(const struct vmbus_channel *channel,
char *buf)
{
return sprintf(buf, "%d\n",
channel_pending(channel,
vmbus_connection.monitor_pages[1]));
}
VMBUS_CHAN_ATTR(pending, S_IRUGO, channel_pending_show, NULL);
static ssize_t channel_latency_show(const struct vmbus_channel *channel,
char *buf)
{
return sprintf(buf, "%d\n",
channel_latency(channel,
vmbus_connection.monitor_pages[1]));
}
VMBUS_CHAN_ATTR(latency, S_IRUGO, channel_latency_show, NULL);
static struct attribute *vmbus_chan_attrs[] = {
&chan_attr_out_mask.attr,
&chan_attr_in_mask.attr,
&chan_attr_read_avail.attr,
&chan_attr_write_avail.attr,
&chan_attr_cpu.attr,
&chan_attr_pending.attr,
&chan_attr_latency.attr,
NULL
};
static struct kobj_type vmbus_chan_ktype = {
.sysfs_ops = &vmbus_chan_sysfs_ops,
.release = vmbus_chan_release,
.default_attrs = vmbus_chan_attrs,
};
/*
* vmbus_add_channel_kobj - setup a sub-directory under device/channels
*/
int vmbus_add_channel_kobj(struct hv_device *dev, struct vmbus_channel *channel)
{
struct kobject *kobj = &channel->kobj;
u32 relid = channel->offermsg.child_relid;
int ret;
kobj->kset = dev->channels_kset;
ret = kobject_init_and_add(kobj, &vmbus_chan_ktype, NULL,
"%u", relid);
if (ret)
return ret;
kobject_uevent(kobj, KOBJ_ADD);
return 0;
}
/* /*
* vmbus_device_create - Creates and registers a new child device * vmbus_device_create - Creates and registers a new child device
* on the vmbus. * on the vmbus.
@ -1165,7 +1306,8 @@ struct hv_device *vmbus_device_create(const uuid_le *type,
*/ */
int vmbus_device_register(struct hv_device *child_device_obj) int vmbus_device_register(struct hv_device *child_device_obj)
{ {
int ret = 0; struct kobject *kobj = &child_device_obj->device.kobj;
int ret;
dev_set_name(&child_device_obj->device, "%pUl", dev_set_name(&child_device_obj->device, "%pUl",
child_device_obj->channel->offermsg.offer.if_instance.b); child_device_obj->channel->offermsg.offer.if_instance.b);
@ -1179,13 +1321,32 @@ int vmbus_device_register(struct hv_device *child_device_obj)
* binding...which will eventually call vmbus_match() and vmbus_probe() * binding...which will eventually call vmbus_match() and vmbus_probe()
*/ */
ret = device_register(&child_device_obj->device); ret = device_register(&child_device_obj->device);
if (ret) {
if (ret)
pr_err("Unable to register child device\n"); pr_err("Unable to register child device\n");
else return ret;
pr_debug("child device %s registered\n", }
dev_name(&child_device_obj->device));
child_device_obj->channels_kset = kset_create_and_add("channels",
NULL, kobj);
if (!child_device_obj->channels_kset) {
ret = -ENOMEM;
goto err_dev_unregister;
}
ret = vmbus_add_channel_kobj(child_device_obj,
child_device_obj->channel);
if (ret) {
pr_err("Unable to register primary channeln");
goto err_kset_unregister;
}
return 0;
err_kset_unregister:
kset_unregister(child_device_obj->channels_kset);
err_dev_unregister:
device_unregister(&child_device_obj->device);
return ret; return ret;
} }

View File

@ -828,6 +828,11 @@ struct vmbus_channel {
*/ */
struct rcu_head rcu; struct rcu_head rcu;
/*
* For sysfs per-channel properties.
*/
struct kobject kobj;
/* /*
* For performance critical channels (storage, networking * For performance critical channels (storage, networking
* etc,), Hyper-V has a mechanism to enhance the throughput * etc,), Hyper-V has a mechanism to enhance the throughput
@ -1089,6 +1094,7 @@ struct hv_device {
struct device device; struct device device;
struct vmbus_channel *channel; struct vmbus_channel *channel;
struct kset *channels_kset;
}; };