Staging: hv: remove OnChildDeviceRemove vmbus_driver callback
[pandora-kernel.git] / drivers / staging / hv / vmbus.c
1 /*
2  * Copyright (c) 2009, Microsoft Corporation.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License along with
14  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15  * Place - Suite 330, Boston, MA 02111-1307 USA.
16  *
17  * Authors:
18  *   Haiyang Zhang <haiyangz@microsoft.com>
19  *   Hank Janssen  <hjanssen@microsoft.com>
20  *
21  */
22 #include <linux/kernel.h>
23 #include <linux/mm.h>
24 #include <linux/slab.h>
25 #include "osd.h"
26 #include "logging.h"
27 #include "version_info.h"
28 #include "vmbus_private.h"
29
30 static const char *gDriverName = "vmbus";
31
32 /*
33  * Windows vmbus does not defined this.
34  * We defined this to be consistent with other devices
35  */
36 /* {c5295816-f63a-4d5f-8d1a-4daf999ca185} */
37 static const struct hv_guid gVmbusDeviceType = {
38         .data = {
39                 0x16, 0x58, 0x29, 0xc5, 0x3a, 0xf6, 0x5f, 0x4d,
40                 0x8d, 0x1a, 0x4d, 0xaf, 0x99, 0x9c, 0xa1, 0x85
41         }
42 };
43
44 /* {ac3760fc-9adf-40aa-9427-a70ed6de95c5} */
45 static const struct hv_guid gVmbusDeviceId = {
46         .data = {
47                 0xfc, 0x60, 0x37, 0xac, 0xdf, 0x9a, 0xaa, 0x40,
48                 0x94, 0x27, 0xa7, 0x0e, 0xd6, 0xde, 0x95, 0xc5
49         }
50 };
51
52 static struct hv_driver *gDriver; /* vmbus driver object */
53 static struct hv_device *gDevice; /* vmbus root device */
54
55 /*
56  * VmbusGetChannelOffers - Retrieve the channel offers from the parent partition
57  */
58 static void VmbusGetChannelOffers(void)
59 {
60         vmbus_request_offers();
61 }
62
63 /*
64  * VmbusChildDeviceAdd - Registers the child device with the vmbus
65  */
66 int VmbusChildDeviceAdd(struct hv_device *ChildDevice)
67 {
68         struct vmbus_driver *vmbusDriver = (struct vmbus_driver *)gDriver;
69
70         return vmbusDriver->OnChildDeviceAdd(gDevice, ChildDevice);
71 }
72
73 /*
74  * VmbusOnDeviceAdd - Callback when the root bus device is added
75  */
76 static int VmbusOnDeviceAdd(struct hv_device *dev, void *AdditionalInfo)
77 {
78         u32 *irqvector = AdditionalInfo;
79         int ret;
80
81         gDevice = dev;
82
83         memcpy(&gDevice->deviceType, &gVmbusDeviceType, sizeof(struct hv_guid));
84         memcpy(&gDevice->deviceInstance, &gVmbusDeviceId,
85                sizeof(struct hv_guid));
86
87         /* strcpy(dev->name, "vmbus"); */
88         /* SynIC setup... */
89         on_each_cpu(hv_synic_init, (void *)irqvector, 1);
90
91         /* Connect to VMBus in the root partition */
92         ret = VmbusConnect();
93
94         /* VmbusSendEvent(device->localPortId+1); */
95         return ret;
96 }
97
98 /*
99  * VmbusOnDeviceRemove - Callback when the root bus device is removed
100  */
101 static int VmbusOnDeviceRemove(struct hv_device *dev)
102 {
103         int ret = 0;
104
105         vmbus_release_unattached_channels();
106         VmbusDisconnect();
107         on_each_cpu(hv_synic_cleanup, NULL, 1);
108         return ret;
109 }
110
111 /*
112  * VmbusOnCleanup - Perform any cleanup when the driver is removed
113  */
114 static void VmbusOnCleanup(struct hv_driver *drv)
115 {
116         /* struct vmbus_driver *driver = (struct vmbus_driver *)drv; */
117
118         hv_cleanup();
119 }
120
121 /*
122  * VmbusOnMsgDPC - DPC routine to handle messages from the hypervisior
123  */
124 static void VmbusOnMsgDPC(struct hv_driver *drv)
125 {
126         int cpu = smp_processor_id();
127         void *page_addr = hv_context.synic_message_page[cpu];
128         struct hv_message *msg = (struct hv_message *)page_addr +
129                                   VMBUS_MESSAGE_SINT;
130         struct hv_message *copied;
131
132         while (1) {
133                 if (msg->header.message_type == HVMSG_NONE) {
134                         /* no msg */
135                         break;
136                 } else {
137                         copied = kmemdup(msg, sizeof(*copied), GFP_ATOMIC);
138                         if (copied == NULL)
139                                 continue;
140
141                         osd_schedule_callback(gVmbusConnection.WorkQueue,
142                                               vmbus_onmessage,
143                                               (void *)copied);
144                 }
145
146                 msg->header.message_type = HVMSG_NONE;
147
148                 /*
149                  * Make sure the write to MessageType (ie set to
150                  * HVMSG_NONE) happens before we read the
151                  * MessagePending and EOMing. Otherwise, the EOMing
152                  * will not deliver any more messages since there is
153                  * no empty slot
154                  */
155                 mb();
156
157                 if (msg->header.message_flags.msg_pending) {
158                         /*
159                          * This will cause message queue rescan to
160                          * possibly deliver another msg from the
161                          * hypervisor
162                          */
163                         wrmsrl(HV_X64_MSR_EOM, 0);
164                 }
165         }
166 }
167
168 /*
169  * VmbusOnEventDPC - DPC routine to handle events from the hypervisior
170  */
171 static void VmbusOnEventDPC(struct hv_driver *drv)
172 {
173         /* TODO: Process any events */
174         VmbusOnEvents();
175 }
176
177 /*
178  * VmbusOnISR - ISR routine
179  */
180 static int VmbusOnISR(struct hv_driver *drv)
181 {
182         int ret = 0;
183         int cpu = smp_processor_id();
184         void *page_addr;
185         struct hv_message *msg;
186         union hv_synic_event_flags *event;
187
188         page_addr = hv_context.synic_message_page[cpu];
189         msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT;
190
191         /* Check if there are actual msgs to be process */
192         if (msg->header.message_type != HVMSG_NONE) {
193                 DPRINT_DBG(VMBUS, "received msg type %d size %d",
194                                 msg->header.message_type,
195                                 msg->header.payload_size);
196                 ret |= 0x1;
197         }
198
199         /* TODO: Check if there are events to be process */
200         page_addr = hv_context.synic_event_page[cpu];
201         event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT;
202
203         /* Since we are a child, we only need to check bit 0 */
204         if (test_and_clear_bit(0, (unsigned long *) &event->flags32[0])) {
205                 DPRINT_DBG(VMBUS, "received event %d", event->flags32[0]);
206                 ret |= 0x2;
207         }
208
209         return ret;
210 }
211
212 /*
213  * VmbusInitialize - Main entry point
214  */
215 int VmbusInitialize(struct hv_driver *drv)
216 {
217         struct vmbus_driver *driver = (struct vmbus_driver *)drv;
218         int ret;
219
220         DPRINT_INFO(VMBUS, "+++++++ HV Driver version = %s +++++++",
221                     HV_DRV_VERSION);
222         DPRINT_INFO(VMBUS, "+++++++ Vmbus supported version = %d +++++++",
223                         VMBUS_REVISION_NUMBER);
224         DPRINT_INFO(VMBUS, "+++++++ Vmbus using SINT %d +++++++",
225                         VMBUS_MESSAGE_SINT);
226         DPRINT_DBG(VMBUS, "sizeof(vmbus_channel_packet_page_buffer)=%zd, "
227                         "sizeof(VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER)=%zd",
228                         sizeof(struct vmbus_channel_packet_page_buffer),
229                         sizeof(struct vmbus_channel_packet_multipage_buffer));
230
231         drv->name = gDriverName;
232         memcpy(&drv->deviceType, &gVmbusDeviceType, sizeof(struct hv_guid));
233
234         /* Setup dispatch table */
235         driver->Base.OnDeviceAdd        = VmbusOnDeviceAdd;
236         driver->Base.OnDeviceRemove     = VmbusOnDeviceRemove;
237         driver->Base.OnCleanup          = VmbusOnCleanup;
238         driver->OnIsr                   = VmbusOnISR;
239         driver->OnMsgDpc                = VmbusOnMsgDPC;
240         driver->OnEventDpc              = VmbusOnEventDPC;
241         driver->GetChannelOffers        = VmbusGetChannelOffers;
242
243         /* Hypervisor initialization...setup hypercall page..etc */
244         ret = hv_init();
245         if (ret != 0)
246                 DPRINT_ERR(VMBUS, "Unable to initialize the hypervisor - 0x%x",
247                                 ret);
248         gDriver = drv;
249
250         return ret;
251 }