[PATCH v2 2/3] drivers:hv Convert VMBus and its descendants to PnP

From: Jake Oshins
Date: Tue Mar 03 2015 - 15:59:40 EST


This patch makes hv_vmbus and the devices that it discovers use the pnp layer,
so that address space claims made by these devices will hook into the regions
of address space exposed by ACPI, which already hooks into the pnp layer.

Previous feedback from Dan Carpenter has been incorporated. (Thank you.)

Signed-off-by: Jake Oshins <jakeo@xxxxxxxxxxxxx>
---
drivers/hid/hid-hyperv.c | 6 +-
drivers/hv/channel_mgmt.c | 5 +-
drivers/hv/hyperv_vmbus.h | 1 +
drivers/hv/vmbus_drv.c | 117 ++++++++++++++++++----------------
drivers/input/serio/hyperv-keyboard.c | 24 +++----
drivers/net/hyperv/netvsc.c | 5 +-
drivers/net/hyperv/netvsc_drv.c | 4 +-
drivers/net/hyperv/rndis_filter.c | 4 +-
drivers/scsi/storvsc_drv.c | 2 +-
drivers/video/fbdev/hyperv_fb.c | 29 +++++----
include/linux/hyperv.h | 15 +++--
11 files changed, 115 insertions(+), 97 deletions(-)

diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c
index 6039f07..0cf9105 100644
--- a/drivers/hid/hid-hyperv.c
+++ b/drivers/hid/hid-hyperv.c
@@ -309,7 +309,7 @@ static void mousevsc_on_receive(struct hv_device *device,
hid_input_report(input_dev->hid_device, HID_INPUT_REPORT,
input_dev->input_buf, len, 1);

- pm_wakeup_event(&input_dev->device->device, 0);
+ pm_wakeup_event(&input_dev->device->pnp_dev->dev, 0);

break;
default:
@@ -552,7 +552,7 @@ static int mousevsc_probe(struct hv_device *device,
goto probe_err2;
}

- device_init_wakeup(&device->device, true);
+ device_init_wakeup(&device->pnp_dev->dev, true);

input_dev->connected = true;
input_dev->init_complete = true;
@@ -576,7 +576,7 @@ static int mousevsc_remove(struct hv_device *dev)
{
struct mousevsc_dev *input_dev = hv_get_drvdata(dev);

- device_init_wakeup(&dev->device, false);
+ device_init_wakeup(&dev->pnp_dev->dev, false);
vmbus_close(dev->channel);
hid_hw_stop(input_dev->hid_device);
hid_destroy_device(input_dev->hid_device);
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index 3736f71..fcb1be8 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -218,8 +218,8 @@ static void vmbus_process_rescind_offer(struct work_struct *work)
struct vmbus_channel_relid_released msg;
struct device *dev;

- if (channel->device_obj) {
- dev = get_device(&channel->device_obj->device);
+ if (channel->device_obj && channel->device_obj->pnp_dev) {
+ dev = get_device(&channel->device_obj->pnp_dev->dev);
if (dev) {
vmbus_device_unregister(channel->device_obj);
put_device(dev);
@@ -359,6 +359,7 @@ static void vmbus_process_offer(struct work_struct *work)
newchannel->device_obj = vmbus_device_create(
&newchannel->offermsg.offer.if_type,
&newchannel->offermsg.offer.if_instance,
+ newchannel->offermsg.offer.mmio_megabytes,
newchannel);
if (!newchannel->device_obj)
goto err_free_chan;
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 44b1c94..73b9bc0 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -676,6 +676,7 @@ extern struct vmbus_connection vmbus_connection;

struct hv_device *vmbus_device_create(const uuid_le *type,
const uuid_le *instance,
+ u16 mmio_mb,
struct vmbus_channel *channel);

int vmbus_device_register(struct hv_device *child_device_obj);
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index f518b8d7..7783f4c 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -487,6 +487,13 @@ static int vmbus_probe(struct device *child_device)
struct hv_device *dev = device_to_hv_device(child_device);
const struct hv_vmbus_device_id *dev_id;

+ ret = pnp_activate_dev(dev->pnp_dev);
+ if (ret) {
+ pr_err("Unable to activate child device %s\n",
+ dev_name(&dev->pnp_dev->dev));
+ return ret;
+ }
+
dev_id = hv_vmbus_get_id(drv->id_table, dev->dev_type.b);
if (drv->probe) {
ret = drv->probe(dev, dev_id);
@@ -510,6 +517,8 @@ static int vmbus_remove(struct device *child_device)
struct hv_driver *drv = drv_to_hv_drv(child_device->driver);
struct hv_device *dev = device_to_hv_device(child_device);

+ pnp_disable_dev(dev->pnp_dev);
+
if (drv->remove)
drv->remove(dev);
else
@@ -549,8 +558,10 @@ static void vmbus_device_release(struct device *device)
{
struct hv_device *hv_dev = device_to_hv_device(device);

- kfree(hv_dev);
+ if (hv_dev->pnp_dev)
+ free_pnp_descendant(hv_dev->pnp_dev);

+ kfree(hv_dev);
}

/* The one and only one */
@@ -579,34 +590,6 @@ static void vmbus_onmessage_work(struct work_struct *work)
kfree(ctx);
}

-static void hv_process_timer_expiration(struct hv_message *msg, int cpu)
-{
- struct clock_event_device *dev = hv_context.clk_evt[cpu];
-
- if (dev->event_handler)
- dev->event_handler(dev);
-
- msg->header.message_type = HVMSG_NONE;
-
- /*
- * Make sure the write to MessageType (ie set to
- * HVMSG_NONE) happens before we read the
- * MessagePending and EOMing. Otherwise, the EOMing
- * will not deliver any more messages since there is
- * no empty slot
- */
- mb();
-
- if (msg->header.message_flags.msg_pending) {
- /*
- * This will cause message queue rescan to
- * possibly deliver another msg from the
- * hypervisor
- */
- wrmsrl(HV_X64_MSR_EOM, 0);
- }
-}
-
static void vmbus_on_msg_dpc(unsigned long data)
{
int cpu = smp_processor_id();
@@ -696,12 +679,8 @@ static void vmbus_isr(void)
msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT;

/* Check if there are actual msgs to be processed */
- if (msg->header.message_type != HVMSG_NONE) {
- if (msg->header.message_type == HVMSG_TIMER_EXPIRED)
- hv_process_timer_expiration(msg, cpu);
- else
- tasklet_schedule(&msg_dpc);
- }
+ if (msg->header.message_type != HVMSG_NONE)
+ tasklet_schedule(&msg_dpc);
}

/*
@@ -814,6 +793,7 @@ EXPORT_SYMBOL_GPL(vmbus_driver_unregister);
*/
struct hv_device *vmbus_device_create(const uuid_le *type,
const uuid_le *instance,
+ u16 mmio_mb,
struct vmbus_channel *channel)
{
struct hv_device *child_device_obj;
@@ -825,6 +805,7 @@ struct hv_device *vmbus_device_create(const uuid_le *type,
}

child_device_obj->channel = channel;
+ child_device_obj->mmio_mb = mmio_mb;
memcpy(&child_device_obj->dev_type, type, sizeof(uuid_le));
memcpy(&child_device_obj->dev_instance, instance,
sizeof(uuid_le));
@@ -838,28 +819,54 @@ struct hv_device *vmbus_device_create(const uuid_le *type,
*/
int vmbus_device_register(struct hv_device *child_device_obj)
{
- int ret = 0;
-
+ int ret;
+ char device_id[40];
+ resource_size_t bytes = child_device_obj->mmio_mb * 0x100000;
static atomic_t device_num = ATOMIC_INIT(0);

- dev_set_name(&child_device_obj->device, "vmbus_0_%d",
+
+ sprintf(device_id, "{%pUl}", child_device_obj->dev_instance.b);
+ child_device_obj->pnp_dev = alloc_pnp_descendant(device_id);
+ if (!child_device_obj->pnp_dev)
+ return -ENOMEM;
+
+ dev_set_name(&child_device_obj->pnp_dev->dev, "vmbus_0_%d",
atomic_inc_return(&device_num));

- child_device_obj->device.bus = &hv_bus;
- child_device_obj->device.parent = &hv_acpi_dev->dev;
- child_device_obj->device.release = vmbus_device_release;
+ child_device_obj->pnp_dev->data = child_device_obj;
+ child_device_obj->pnp_dev->dev.bus = &hv_bus;
+ child_device_obj->pnp_dev->dev.parent = &hv_acpi_dev->dev;
+ child_device_obj->pnp_dev->dev.release = vmbus_device_release;

- /*
- * Register with the LDM. This will kick off the driver/device
- * binding...which will eventually call vmbus_match() and vmbus_probe()
- */
- ret = device_register(&child_device_obj->device);
+ if (bytes) {
+ /*
+ * Add a memory option that is aligned on the length. All VMBus
+ * channels can tolerate their memory regions going above
+ * the 32-bit line.
+ */
+ ret = pnp_descendant_memory_option(child_device_obj->pnp_dev,
+ (u64)0x100000000,
+ (u64)(-1),
+ bytes,
+ bytes,
+ IORESOURCE_MEM_WRITEABLE);
+ if (ret)
+ goto err_free_des;
+ }

- if (ret)
- pr_err("Unable to register child device\n");
- else
- pr_debug("child device %s registered\n",
- dev_name(&child_device_obj->device));
+ ret = pnp_add_descendant(child_device_obj->pnp_dev);
+ if (ret) {
+ pr_err("Unable to register child device %s\n",
+ dev_name(&child_device_obj->pnp_dev->dev));
+ goto err_free_des;
+ }
+
+ pr_debug("child device %s registered\n",
+ dev_name(&child_device_obj->pnp_dev->dev));
+ return 0;
+
+err_free_des:
+ free_pnp_descendant(child_device_obj->pnp_dev);

return ret;
}
@@ -870,14 +877,14 @@ int vmbus_device_register(struct hv_device *child_device_obj)
*/
void vmbus_device_unregister(struct hv_device *device_obj)
{
- pr_debug("child device %s unregistered\n",
- dev_name(&device_obj->device));
-
/*
* Kick off the process of unregistering the device.
* This will call vmbus_remove() and eventually vmbus_device_release()
*/
- device_unregister(&device_obj->device);
+ pnp_remove_descendant(device_obj->pnp_dev);
+
+ pr_debug("child device %s unregistered\n",
+ dev_name(&device_obj->pnp_dev->dev));
}


diff --git a/drivers/input/serio/hyperv-keyboard.c b/drivers/input/serio/hyperv-keyboard.c
index e74e5d6..0115e23 100644
--- a/drivers/input/serio/hyperv-keyboard.c
+++ b/drivers/input/serio/hyperv-keyboard.c
@@ -124,7 +124,7 @@ static void hv_kbd_on_receive(struct hv_device *hv_dev,
* goes away).
*/
if (msg_length < sizeof(struct synth_kbd_protocol_response)) {
- dev_err(&hv_dev->device,
+ dev_err(&hv_dev->pnp_dev->dev,
"Illegal protocol response packet (len: %d)\n",
msg_length);
break;
@@ -143,7 +143,7 @@ static void hv_kbd_on_receive(struct hv_device *hv_dev,
* goes away).
*/
if (msg_length < sizeof(struct synth_kbd_keystroke)) {
- dev_err(&hv_dev->device,
+ dev_err(&hv_dev->pnp_dev->dev,
"Illegal keyboard event packet (len: %d)\n",
msg_length);
break;
@@ -177,12 +177,12 @@ static void hv_kbd_on_receive(struct hv_device *hv_dev,
* state because the Enter-UP can trigger a wakeup at once.
*/
if (!(info & IS_BREAK))
- pm_wakeup_event(&hv_dev->device, 0);
+ pm_wakeup_event(&hv_dev->pnp_dev->dev, 0);

break;

default:
- dev_err(&hv_dev->device,
+ dev_err(&hv_dev->pnp_dev->dev,
"unhandled message type %d\n", msg_type);
}
}
@@ -225,7 +225,7 @@ static void hv_kbd_handle_received_packet(struct hv_device *hv_dev,
* Drop the packet and hope
* the problem magically goes away.
*/
- dev_err(&hv_dev->device,
+ dev_err(&hv_dev->pnp_dev->dev,
"Illegal packet (type: %d, tid: %llx, size: %d)\n",
desc->type, req_id, msg_sz);
break;
@@ -236,7 +236,7 @@ static void hv_kbd_handle_received_packet(struct hv_device *hv_dev,
break;

default:
- dev_err(&hv_dev->device,
+ dev_err(&hv_dev->pnp_dev->dev,
"unhandled packet type %d, tid %llx len %d\n",
desc->type, req_id, bytes_recvd);
break;
@@ -309,7 +309,7 @@ static int hv_kbd_connect_to_vsp(struct hv_device *hv_dev)
response = &kbd_dev->protocol_resp;
proto_status = __le32_to_cpu(response->proto_status);
if (!(proto_status & PROTOCOL_ACCEPTED)) {
- dev_err(&hv_dev->device,
+ dev_err(&hv_dev->pnp_dev->dev,
"synth_kbd protocol request failed (version %d)\n",
SYNTH_KBD_VERSION);
return -ENODEV;
@@ -360,12 +360,12 @@ static int hv_kbd_probe(struct hv_device *hv_dev,
init_completion(&kbd_dev->wait_event);
hv_set_drvdata(hv_dev, kbd_dev);

- hv_serio->dev.parent = &hv_dev->device;
+ hv_serio->dev.parent = &hv_dev->pnp_dev->dev;
hv_serio->id.type = SERIO_8042_XL;
hv_serio->port_data = kbd_dev;
- strlcpy(hv_serio->name, dev_name(&hv_dev->device),
+ strlcpy(hv_serio->name, dev_name(&hv_dev->pnp_dev->dev),
sizeof(hv_serio->name));
- strlcpy(hv_serio->phys, dev_name(&hv_dev->device),
+ strlcpy(hv_serio->phys, dev_name(&hv_dev->pnp_dev->dev),
sizeof(hv_serio->phys));

hv_serio->start = hv_kbd_start;
@@ -386,7 +386,7 @@ static int hv_kbd_probe(struct hv_device *hv_dev,

serio_register_port(kbd_dev->hv_serio);

- device_init_wakeup(&hv_dev->device, true);
+ device_init_wakeup(&hv_dev->pnp_dev->dev, true);

return 0;

@@ -402,7 +402,7 @@ static int hv_kbd_remove(struct hv_device *hv_dev)
{
struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev);

- device_init_wakeup(&hv_dev->device, false);
+ device_init_wakeup(&hv_dev->pnp_dev->dev, false);
serio_unregister_port(kbd_dev->hv_serio);
vmbus_close(hv_dev->channel);
kfree(kbd_dev);
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 208eb05..00ecfba 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -379,7 +379,8 @@ static int netvsc_init_buf(struct hv_device *device)
net_device->send_section_cnt =
net_device->send_buf_size/net_device->send_section_size;

- dev_info(&device->device, "Send section size: %d, Section count:%d\n",
+ dev_info(&device->pnp_dev->dev,
+ "Send section size: %d, Section count:%d\n",
net_device->send_section_size, net_device->send_section_cnt);

/* Setup state for managing the send buffer. */
@@ -557,7 +558,7 @@ int netvsc_device_remove(struct hv_device *device)
* At this point, no one should be accessing net_device
* except in here
*/
- dev_notice(&device->device, "net device safe to remove\n");
+ dev_notice(&device->pnp_dev->dev, "net device safe to remove\n");

/* Now, we can close the channel safely */
vmbus_close(device->channel);
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index a06bd66..63f76ec4 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -867,7 +867,7 @@ static int netvsc_probe(struct hv_device *dev,
NETIF_F_IP_CSUM | NETIF_F_TSO;

net->ethtool_ops = &ethtool_ops;
- SET_NETDEV_DEV(net, &dev->device);
+ SET_NETDEV_DEV(net, &dev->pnp_dev->dev);

/* Notify the netvsc driver of the new device */
device_info.ring_size = ring_size;
@@ -906,7 +906,7 @@ static int netvsc_remove(struct hv_device *dev)
net = net_device->ndev;

if (net == NULL) {
- dev_err(&dev->device, "No net device to remove\n");
+ dev_err(&dev->pnp_dev->dev, "No net device to remove\n");
return 0;
}

diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
index ca81de0..958d24d3 100644
--- a/drivers/net/hyperv/rndis_filter.c
+++ b/drivers/net/hyperv/rndis_filter.c
@@ -1080,7 +1080,7 @@ int rndis_filter_device_add(struct hv_device *dev,

device_info->link_state = rndis_device->link_state;

- dev_info(&dev->device, "Device MAC %pM link state %s\n",
+ dev_info(&dev->pnp_dev->dev, "Device MAC %pM link state %s\n",
rndis_device->hw_mac_adr,
device_info->link_state ? "down" : "up");

@@ -1105,7 +1105,7 @@ int rndis_filter_device_add(struct hv_device *dev,
NETVSC_PACKET_SIZE);
if (!net_device->sub_cb_buf) {
net_device->num_chn = 1;
- dev_info(&dev->device, "No memory for subchannels.\n");
+ dev_info(&dev->pnp_dev->dev, "No memory for subchannels.\n");
goto out;
}

diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index efc6e44..1f15a26b 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -1781,7 +1781,7 @@ static int storvsc_probe(struct hv_device *device,
host->max_cmd_len = STORVSC_MAX_CMD_LEN;

/* Register the HBA and start the scsi bus scan */
- ret = scsi_add_host(host, &device->device);
+ ret = scsi_add_host(host, &device->pnp_dev->dev);
if (ret != 0)
goto err_out2;

diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c
index 4254336..161157e 100644
--- a/drivers/video/fbdev/hyperv_fb.c
+++ b/drivers/video/fbdev/hyperv_fb.c
@@ -675,26 +675,22 @@ static void hvfb_get_option(struct fb_info *info)


/* Get framebuffer memory from Hyper-V video pci space */
-static int hvfb_getmem(struct fb_info *info)
+static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
{
struct hvfb_par *par = info->par;
struct pci_dev *pdev = NULL;
void __iomem *fb_virt;
+ struct resource *res;
int gen2vm = efi_enabled(EFI_BOOT);
int ret;

- par->mem.name = KBUILD_MODNAME;
- par->mem.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (gen2vm) {
- ret = allocate_resource(&hyperv_mmio, &par->mem,
- screen_fb_size,
- 0, -1,
- screen_fb_size,
- NULL, NULL);
- if (ret != 0) {
- pr_err("Unable to allocate framebuffer memory\n");
+ res = pnp_get_resource(hdev->pnp_dev, IORESOURCE_MEM, 0);
+ if (!res) {
+ pr_err("Unable to fetch FB claim\n");
return -ENODEV;
}
+ par->mem = *res;
} else {
pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT,
PCI_DEVICE_ID_HYPERV_VIDEO, NULL);
@@ -707,6 +703,8 @@ static int hvfb_getmem(struct fb_info *info)
pci_resource_len(pdev, 0) < screen_fb_size)
goto err1;

+ par->mem.name = KBUILD_MODNAME;
+ par->mem.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
par->mem.end = pci_resource_end(pdev, 0);
par->mem.start = par->mem.end - screen_fb_size + 1;
ret = request_resource(&pdev->resource[0], &par->mem);
@@ -747,7 +745,8 @@ static int hvfb_getmem(struct fb_info *info)
err3:
iounmap(fb_virt);
err2:
- release_resource(&par->mem);
+ if (!gen2vm)
+ release_resource(&par->mem);
err1:
if (!gen2vm)
pci_dev_put(pdev);
@@ -759,9 +758,11 @@ err1:
static void hvfb_putmem(struct fb_info *info)
{
struct hvfb_par *par = info->par;
+ int gen2vm = efi_enabled(EFI_BOOT);

iounmap(info->screen_base);
- release_resource(&par->mem);
+ if (!gen2vm)
+ release_resource(&par->mem);
}


@@ -772,7 +773,7 @@ static int hvfb_probe(struct hv_device *hdev,
struct hvfb_par *par;
int ret;

- info = framebuffer_alloc(sizeof(struct hvfb_par), &hdev->device);
+ info = framebuffer_alloc(sizeof(struct hvfb_par), &hdev->pnp_dev->dev);
if (!info) {
pr_err("No memory for framebuffer info\n");
return -ENOMEM;
@@ -792,7 +793,7 @@ static int hvfb_probe(struct hv_device *hdev,
goto error1;
}

- ret = hvfb_getmem(info);
+ ret = hvfb_getmem(hdev, info);
if (ret) {
pr_err("No memory for framebuffer\n");
goto error2;
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index 5a2ba67..796cc32 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -35,6 +35,7 @@
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/mod_devicetable.h>
+#include <linux/pnp.h>


#define MAX_PAGE_BUFFER_COUNT 32
@@ -928,7 +929,11 @@ struct hv_device {
/* the device instance id of this device */
uuid_le dev_instance;

- struct device device;
+ /* the amount of memory address space that should be
+ reserved for this channel, in megabytes */
+ u16 mmio_mb;
+
+ struct pnp_dev *pnp_dev;

struct vmbus_channel *channel;
};
@@ -936,7 +941,9 @@ struct hv_device {

static inline struct hv_device *device_to_hv_device(struct device *d)
{
- return container_of(d, struct hv_device, device);
+ struct pnp_dev *pnp_dev = container_of(d, struct pnp_dev, dev);
+
+ return (struct hv_device *)(pnp_dev->data);
}

static inline struct hv_driver *drv_to_hv_drv(struct device_driver *d)
@@ -946,12 +953,12 @@ static inline struct hv_driver *drv_to_hv_drv(struct device_driver *d)

static inline void hv_set_drvdata(struct hv_device *dev, void *data)
{
- dev_set_drvdata(&dev->device, data);
+ dev_set_drvdata(&dev->pnp_dev->dev, data);
}

static inline void *hv_get_drvdata(struct hv_device *dev)
{
- return dev_get_drvdata(&dev->device);
+ return dev_get_drvdata(&dev->pnp_dev->dev);
}

/* Vmbus interface */
--
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/