Commit | Line | Data |
---|---|---|
bac8a4d5 KC |
1 | /* uislib.c |
2 | * | |
f6d0c1e6 | 3 | * Copyright (C) 2010 - 2013 UNISYS CORPORATION |
bac8a4d5 KC |
4 | * All rights reserved. |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or (at | |
9 | * your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | |
14 | * NON INFRINGEMENT. See the GNU General Public License for more | |
15 | * details. | |
16 | */ | |
17 | ||
18 | /* @ALL_INSPECTED */ | |
19 | #define EXPORT_SYMTAB | |
20 | #include <linux/kernel.h> | |
21 | #include <linux/highmem.h> | |
22 | #ifdef CONFIG_MODVERSIONS | |
23 | #include <config/modversions.h> | |
24 | #endif | |
25 | #include <linux/module.h> | |
28fa597f | 26 | #include <linux/debugfs.h> |
bac8a4d5 | 27 | |
1d2def98 BR |
28 | #include <linux/types.h> |
29 | #include <linux/uuid.h> | |
bac8a4d5 KC |
30 | |
31 | #include <linux/version.h> | |
32 | #include "uniklog.h" | |
33 | #include "diagnostics/appos_subsystems.h" | |
34 | #include "uisutils.h" | |
35 | #include "vbuschannel.h" | |
36 | ||
37 | #include <linux/proc_fs.h> | |
38 | #include <linux/uaccess.h> /* for copy_from_user */ | |
39 | #include <linux/ctype.h> /* for toupper */ | |
40 | #include <linux/list.h> | |
41 | ||
42 | #include "sparstop.h" | |
43 | #include "visorchipset.h" | |
44 | #include "chanstub.h" | |
45 | #include "version.h" | |
46 | #include "guestlinuxdebug.h" | |
47 | ||
48 | #define SET_PROC_OWNER(x, y) | |
49 | ||
bac8a4d5 KC |
50 | #define POLLJIFFIES_NORMAL 1 |
51 | /* Choose whether or not you want to wakeup the request-polling thread | |
52 | * after an IO termination: | |
53 | * this is shorter than using __FILE__ (full path name) in | |
54 | * debug/info/error messages | |
55 | */ | |
56 | #define CURRENT_FILE_PC UISLIB_PC_uislib_c | |
57 | #define __MYFILE__ "uislib.c" | |
58 | ||
59 | /* global function pointers that act as callback functions into virtpcimod */ | |
2df7cc62 | 60 | int (*virt_control_chan_func)(struct guest_msgs *); |
bac8a4d5 KC |
61 | |
62 | static int ProcReadBufferValid; | |
63 | static char *ProcReadBuffer; /* Note this MUST be global, | |
64 | * because the contents must */ | |
65 | static unsigned int chipset_inited; | |
a8d7f21d | 66 | |
bac8a4d5 KC |
67 | #define WAIT_ON_CALLBACK(handle) \ |
68 | do { \ | |
69 | if (handle) \ | |
70 | break; \ | |
71 | UIS_THREAD_WAIT; \ | |
72 | } while (1) | |
73 | ||
74 | static struct bus_info *BusListHead; | |
75 | static rwlock_t BusListLock; | |
76 | static int BusListCount; /* number of buses in the list */ | |
77 | static int MaxBusCount; /* maximum number of buses expected */ | |
5fc0229a | 78 | static u64 PhysicalDataChan; |
bac8a4d5 KC |
79 | static int PlatformNumber; |
80 | ||
bac8a4d5 KC |
81 | static struct uisthread_info Incoming_ThreadInfo; |
82 | static BOOL Incoming_Thread_Started = FALSE; | |
a8d7f21d KC |
83 | static LIST_HEAD(List_Polling_Device_Channels); |
84 | static unsigned long long tot_moved_to_tail_cnt; | |
85 | static unsigned long long tot_wait_cnt; | |
86 | static unsigned long long tot_wakeup_cnt; | |
87 | static unsigned long long tot_schedule_cnt; | |
88 | static int en_smart_wakeup = 1; | |
bac8a4d5 | 89 | static DEFINE_SEMAPHORE(Lock_Polling_Device_Channels); /* unlocked */ |
a8d7f21d | 90 | static DECLARE_WAIT_QUEUE_HEAD(Wakeup_Polling_Device_Channels); |
bac8a4d5 KC |
91 | static int Go_Polling_Device_Channels; |
92 | ||
bac8a4d5 KC |
93 | #define CALLHOME_PROC_ENTRY_FN "callhome" |
94 | #define CALLHOME_THROTTLED_PROC_ENTRY_FN "callhome_throttled" | |
b27a00de | 95 | |
28fa597f BR |
96 | #define DIR_DEBUGFS_ENTRY "uislib" |
97 | static struct dentry *dir_debugfs; | |
98 | ||
99 | #define PLATFORMNUMBER_DEBUGFS_ENTRY_FN "platform" | |
100 | static struct dentry *platformnumber_debugfs_read; | |
101 | ||
b913a2ef BR |
102 | #define CYCLES_BEFORE_WAIT_DEBUGFS_ENTRY_FN "cycles_before_wait" |
103 | static struct dentry *cycles_before_wait_debugfs_read; | |
104 | ||
81d2d7de BR |
105 | #define SMART_WAKEUP_DEBUGFS_ENTRY_FN "smart_wakeup" |
106 | static struct dentry *smart_wakeup_debugfs_entry; | |
107 | ||
7ec96720 BR |
108 | #define INFO_DEBUGFS_ENTRY_FN "info" |
109 | static struct dentry *info_debugfs_entry; | |
110 | ||
a8d7f21d | 111 | static unsigned long long cycles_before_wait, wait_cycles; |
bac8a4d5 KC |
112 | |
113 | /*****************************************************/ | |
114 | /* local functions */ | |
115 | /*****************************************************/ | |
116 | ||
7ec96720 | 117 | static ssize_t info_debugfs_read(struct file *file, char __user *buf, |
bac8a4d5 | 118 | size_t len, loff_t *offset); |
7ec96720 BR |
119 | static const struct file_operations debugfs_info_fops = { |
120 | .read = info_debugfs_read, | |
bac8a4d5 KC |
121 | }; |
122 | ||
bac8a4d5 | 123 | static void |
3ab47701 | 124 | init_msg_header(struct controlvm_message *msg, u32 id, uint rsp, uint svr) |
bac8a4d5 | 125 | { |
3ab47701 | 126 | memset(msg, 0, sizeof(struct controlvm_message)); |
98d7b594 BR |
127 | msg->hdr.id = id; |
128 | msg->hdr.flags.response_expected = rsp; | |
129 | msg->hdr.flags.server = svr; | |
bac8a4d5 KC |
130 | } |
131 | ||
a8d7f21d | 132 | static __iomem void * |
5fc0229a | 133 | init_vbus_channel(u64 channelAddr, u32 channelBytes) |
bac8a4d5 | 134 | { |
3db5540d | 135 | void __iomem *rc = NULL; |
a8d7f21d | 136 | void __iomem *pChan = uislib_ioremap_cache(channelAddr, channelBytes); |
ddc9f84b | 137 | |
bac8a4d5 KC |
138 | if (!pChan) { |
139 | LOGERR("CONTROLVM_BUS_CREATE error: ioremap_cache of channelAddr:%Lx for channelBytes:%llu failed", | |
140 | (unsigned long long) channelAddr, | |
141 | (unsigned long long) channelBytes); | |
d9355f89 KC |
142 | rc = NULL; |
143 | goto Away; | |
bac8a4d5 | 144 | } |
93a84565 | 145 | if (!SPAR_VBUS_CHANNEL_OK_CLIENT(pChan, NULL)) { |
548950a2 KC |
146 | ERRDRV("%s channel cannot be used", __func__); |
147 | uislib_iounmap(pChan); | |
148 | rc = NULL; | |
149 | goto Away; | |
bac8a4d5 | 150 | } |
d9355f89 | 151 | rc = pChan; |
bac8a4d5 KC |
152 | Away: |
153 | return rc; | |
154 | } | |
155 | ||
156 | static int | |
3ab47701 | 157 | create_bus(struct controlvm_message *msg, char *buf) |
bac8a4d5 | 158 | { |
b3c55b13 | 159 | u32 busNo, deviceCount; |
bac8a4d5 KC |
160 | struct bus_info *tmp, *bus; |
161 | size_t size; | |
162 | ||
163 | if (MaxBusCount == BusListCount) { | |
164 | LOGERR("CONTROLVM_BUS_CREATE Failed: max buses:%d already created\n", | |
165 | MaxBusCount); | |
166 | POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, MaxBusCount, | |
167 | POSTCODE_SEVERITY_ERR); | |
168 | return CONTROLVM_RESP_ERROR_MAX_BUSES; | |
169 | } | |
170 | ||
2ea5117b BR |
171 | busNo = msg->cmd.create_bus.bus_no; |
172 | deviceCount = msg->cmd.create_bus.dev_count; | |
bac8a4d5 KC |
173 | |
174 | POSTCODE_LINUX_4(BUS_CREATE_ENTRY_PC, busNo, deviceCount, | |
175 | POSTCODE_SEVERITY_INFO); | |
176 | ||
177 | size = | |
178 | sizeof(struct bus_info) + | |
179 | (deviceCount * sizeof(struct device_info *)); | |
60140462 | 180 | bus = kzalloc(size, GFP_ATOMIC); |
bac8a4d5 KC |
181 | if (!bus) { |
182 | LOGERR("CONTROLVM_BUS_CREATE Failed: kmalloc for bus failed.\n"); | |
183 | POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, busNo, | |
184 | POSTCODE_SEVERITY_ERR); | |
185 | return CONTROLVM_RESP_ERROR_KMALLOC_FAILED; | |
186 | } | |
187 | ||
bac8a4d5 KC |
188 | /* Currently by default, the bus Number is the GuestHandle. |
189 | * Configure Bus message can override this. | |
190 | */ | |
98d7b594 | 191 | if (msg->hdr.flags.test_message) { |
bac8a4d5 | 192 | /* This implies we're the IOVM so set guest handle to 0... */ |
43ecb9fe BR |
193 | bus->guest_handle = 0; |
194 | bus->bus_no = busNo; | |
195 | bus->local_vnic = 1; | |
bac8a4d5 | 196 | } else |
43ecb9fe BR |
197 | bus->bus_no = bus->guest_handle = busNo; |
198 | sprintf(bus->name, "%d", (int) bus->bus_no); | |
199 | bus->device_count = deviceCount; | |
bac8a4d5 KC |
200 | bus->device = |
201 | (struct device_info **) ((char *) bus + sizeof(struct bus_info)); | |
2ea5117b | 202 | bus->bus_inst_uuid = msg->cmd.create_bus.bus_inst_uuid; |
43ecb9fe BR |
203 | bus->bus_channel_bytes = 0; |
204 | bus->bus_channel = NULL; | |
bac8a4d5 KC |
205 | |
206 | /* add bus to our bus list - but check for duplicates first */ | |
207 | read_lock(&BusListLock); | |
208 | for (tmp = BusListHead; tmp; tmp = tmp->next) { | |
43ecb9fe | 209 | if (tmp->bus_no == bus->bus_no) |
bac8a4d5 KC |
210 | break; |
211 | } | |
212 | read_unlock(&BusListLock); | |
213 | if (tmp) { | |
214 | /* found a bus already in the list with same busNo - | |
215 | * reject add | |
216 | */ | |
217 | LOGERR("CONTROLVM_BUS_CREATE Failed: bus %d already exists.\n", | |
43ecb9fe BR |
218 | bus->bus_no); |
219 | POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->bus_no, | |
bac8a4d5 | 220 | POSTCODE_SEVERITY_ERR); |
60140462 | 221 | kfree(bus); |
bac8a4d5 KC |
222 | return CONTROLVM_RESP_ERROR_ALREADY_DONE; |
223 | } | |
2ea5117b BR |
224 | if ((msg->cmd.create_bus.channel_addr != 0) |
225 | && (msg->cmd.create_bus.channel_bytes != 0)) { | |
226 | bus->bus_channel_bytes = msg->cmd.create_bus.channel_bytes; | |
43ecb9fe | 227 | bus->bus_channel = |
2ea5117b BR |
228 | init_vbus_channel(msg->cmd.create_bus.channel_addr, |
229 | msg->cmd.create_bus.channel_bytes); | |
bac8a4d5 KC |
230 | } |
231 | /* the msg is bound for virtpci; send guest_msgs struct to callback */ | |
98d7b594 | 232 | if (!msg->hdr.flags.server) { |
bac8a4d5 | 233 | struct guest_msgs cmd; |
ddc9f84b | 234 | |
bac8a4d5 | 235 | cmd.msgtype = GUEST_ADD_VBUS; |
34e6230b | 236 | cmd.add_vbus.bus_no = busNo; |
43ecb9fe | 237 | cmd.add_vbus.chanptr = bus->bus_channel; |
34e6230b | 238 | cmd.add_vbus.dev_count = deviceCount; |
2ea5117b BR |
239 | cmd.add_vbus.bus_uuid = msg->cmd.create_bus.bus_data_type_uuid; |
240 | cmd.add_vbus.instance_uuid = msg->cmd.create_bus.bus_inst_uuid; | |
2df7cc62 | 241 | if (!virt_control_chan_func) { |
bac8a4d5 | 242 | LOGERR("CONTROLVM_BUS_CREATE Failed: virtpci callback not registered."); |
43ecb9fe | 243 | POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->bus_no, |
bac8a4d5 | 244 | POSTCODE_SEVERITY_ERR); |
d21bb450 | 245 | kfree(bus); |
bac8a4d5 KC |
246 | return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE; |
247 | } | |
2df7cc62 | 248 | if (!virt_control_chan_func(&cmd)) { |
bac8a4d5 | 249 | LOGERR("CONTROLVM_BUS_CREATE Failed: virtpci GUEST_ADD_VBUS returned error."); |
43ecb9fe | 250 | POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->bus_no, |
bac8a4d5 | 251 | POSTCODE_SEVERITY_ERR); |
d21bb450 | 252 | kfree(bus); |
bac8a4d5 KC |
253 | return |
254 | CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR; | |
255 | } | |
256 | } | |
bac8a4d5 KC |
257 | |
258 | /* add bus at the head of our list */ | |
259 | write_lock(&BusListLock); | |
260 | if (!BusListHead) | |
261 | BusListHead = bus; | |
262 | else { | |
263 | bus->next = BusListHead; | |
264 | BusListHead = bus; | |
265 | } | |
266 | BusListCount++; | |
267 | write_unlock(&BusListLock); | |
268 | ||
43ecb9fe | 269 | POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, bus->bus_no, |
bac8a4d5 KC |
270 | POSTCODE_SEVERITY_INFO); |
271 | return CONTROLVM_RESP_SUCCESS; | |
272 | } | |
273 | ||
274 | static int | |
3ab47701 | 275 | destroy_bus(struct controlvm_message *msg, char *buf) |
bac8a4d5 KC |
276 | { |
277 | int i; | |
278 | struct bus_info *bus, *prev = NULL; | |
81e4c97e | 279 | struct guest_msgs cmd; |
b3c55b13 | 280 | u32 busNo; |
bac8a4d5 | 281 | |
2ea5117b | 282 | busNo = msg->cmd.destroy_bus.bus_no; |
bac8a4d5 | 283 | |
bac8a4d5 | 284 | read_lock(&BusListLock); |
81e4c97e BR |
285 | |
286 | bus = BusListHead; | |
287 | while (bus) { | |
43ecb9fe | 288 | if (bus->bus_no == busNo) |
bac8a4d5 | 289 | break; |
81e4c97e BR |
290 | prev = bus; |
291 | bus = bus->next; | |
bac8a4d5 KC |
292 | } |
293 | ||
294 | if (!bus) { | |
295 | LOGERR("CONTROLVM_BUS_DESTROY Failed: failed to find bus %d.\n", | |
296 | busNo); | |
297 | read_unlock(&BusListLock); | |
298 | return CONTROLVM_RESP_ERROR_ALREADY_DONE; | |
299 | } | |
81e4c97e BR |
300 | |
301 | /* verify that this bus has no devices. */ | |
43ecb9fe | 302 | for (i = 0; i < bus->device_count; i++) { |
81e4c97e BR |
303 | if (bus->device[i] != NULL) { |
304 | LOGERR("CONTROLVM_BUS_DESTROY Failed: device %i attached to bus %d.", | |
305 | i, busNo); | |
306 | read_unlock(&BusListLock); | |
307 | return CONTROLVM_RESP_ERROR_BUS_DEVICE_ATTACHED; | |
308 | } | |
309 | } | |
310 | read_unlock(&BusListLock); | |
311 | ||
98d7b594 | 312 | if (msg->hdr.flags.server) |
81e4c97e BR |
313 | goto remove; |
314 | ||
315 | /* client messages require us to call the virtpci callback associated | |
316 | with this bus. */ | |
317 | cmd.msgtype = GUEST_DEL_VBUS; | |
a990356d | 318 | cmd.del_vbus.bus_no = busNo; |
2df7cc62 | 319 | if (!virt_control_chan_func) { |
81e4c97e BR |
320 | LOGERR("CONTROLVM_BUS_DESTROY Failed: virtpci callback not registered."); |
321 | return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE; | |
322 | } | |
2df7cc62 | 323 | if (!virt_control_chan_func(&cmd)) { |
81e4c97e BR |
324 | LOGERR("CONTROLVM_BUS_DESTROY Failed: virtpci GUEST_DEL_VBUS returned error."); |
325 | return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR; | |
326 | } | |
327 | ||
328 | /* finally, remove the bus from the list */ | |
329 | remove: | |
330 | write_lock(&BusListLock); | |
331 | if (prev) /* not at head */ | |
332 | prev->next = bus->next; | |
333 | else | |
334 | BusListHead = bus->next; | |
335 | BusListCount--; | |
336 | write_unlock(&BusListLock); | |
337 | ||
43ecb9fe BR |
338 | if (bus->bus_channel) { |
339 | uislib_iounmap(bus->bus_channel); | |
340 | bus->bus_channel = NULL; | |
bac8a4d5 KC |
341 | } |
342 | ||
60140462 | 343 | kfree(bus); |
bac8a4d5 KC |
344 | return CONTROLVM_RESP_SUCCESS; |
345 | } | |
346 | ||
347 | static int | |
3ab47701 | 348 | create_device(struct controlvm_message *msg, char *buf) |
bac8a4d5 KC |
349 | { |
350 | struct device_info *dev; | |
351 | struct bus_info *bus; | |
b3c55b13 | 352 | u32 busNo, devNo; |
bac8a4d5 | 353 | int result = CONTROLVM_RESP_SUCCESS; |
5fc0229a | 354 | u64 minSize = MIN_IO_CHANNEL_SIZE; |
38ab19b6 | 355 | struct req_handler_info *pReqHandler; |
bac8a4d5 | 356 | |
f91b9262 BR |
357 | busNo = msg->cmd.create_device.bus_no; |
358 | devNo = msg->cmd.create_device.dev_no; | |
bac8a4d5 KC |
359 | |
360 | POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, devNo, busNo, | |
361 | POSTCODE_SEVERITY_INFO); | |
362 | ||
60140462 | 363 | dev = kzalloc(sizeof(struct device_info), GFP_ATOMIC); |
bac8a4d5 KC |
364 | if (!dev) { |
365 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: kmalloc for dev failed.\n"); | |
366 | POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo, | |
367 | POSTCODE_SEVERITY_ERR); | |
368 | return CONTROLVM_RESP_ERROR_KMALLOC_FAILED; | |
369 | } | |
370 | ||
f91b9262 | 371 | dev->channel_uuid = msg->cmd.create_device.data_type_uuid; |
2ea5117b | 372 | dev->intr = msg->cmd.create_device.intr; |
f91b9262 | 373 | dev->channel_addr = msg->cmd.create_device.channel_addr; |
f796e84c BR |
374 | dev->bus_no = busNo; |
375 | dev->dev_no = devNo; | |
bac8a4d5 KC |
376 | sema_init(&dev->interrupt_callback_lock, 1); /* unlocked */ |
377 | sprintf(dev->devid, "vbus%u:dev%u", (unsigned) busNo, (unsigned) devNo); | |
378 | /* map the channel memory for the device. */ | |
98d7b594 | 379 | if (msg->hdr.flags.test_message) |
f796e84c | 380 | dev->chanptr = (void __iomem *)__va(dev->channel_addr); |
bac8a4d5 | 381 | else { |
ea2cfd65 | 382 | pReqHandler = req_handler_find(dev->channel_uuid); |
bac8a4d5 KC |
383 | if (pReqHandler) |
384 | /* generic service handler registered for this | |
385 | * channel | |
386 | */ | |
387 | minSize = pReqHandler->min_channel_bytes; | |
f91b9262 | 388 | if (minSize > msg->cmd.create_device.channel_bytes) { |
bac8a4d5 | 389 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: channel size is too small, channel size:0x%lx, required size:0x%lx", |
f91b9262 | 390 | (ulong) msg->cmd.create_device.channel_bytes, |
bac8a4d5 KC |
391 | (ulong) minSize); |
392 | POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo, | |
393 | POSTCODE_SEVERITY_ERR); | |
394 | result = CONTROLVM_RESP_ERROR_CHANNEL_SIZE_TOO_SMALL; | |
395 | goto Away; | |
396 | } | |
397 | dev->chanptr = | |
f796e84c | 398 | uislib_ioremap_cache(dev->channel_addr, |
f91b9262 | 399 | msg->cmd.create_device.channel_bytes); |
bac8a4d5 KC |
400 | if (!dev->chanptr) { |
401 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: ioremap_cache of channelAddr:%Lx for channelBytes:%llu failed", | |
f796e84c | 402 | dev->channel_addr, |
f91b9262 | 403 | msg->cmd.create_device.channel_bytes); |
bac8a4d5 KC |
404 | result = CONTROLVM_RESP_ERROR_IOREMAP_FAILED; |
405 | POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo, | |
406 | POSTCODE_SEVERITY_ERR); | |
407 | goto Away; | |
408 | } | |
409 | } | |
f91b9262 BR |
410 | dev->instance_uuid = msg->cmd.create_device.dev_inst_uuid; |
411 | dev->channel_bytes = msg->cmd.create_device.channel_bytes; | |
bac8a4d5 KC |
412 | |
413 | read_lock(&BusListLock); | |
414 | for (bus = BusListHead; bus; bus = bus->next) { | |
43ecb9fe | 415 | if (bus->bus_no == busNo) { |
bac8a4d5 | 416 | /* make sure the device number is valid */ |
43ecb9fe | 417 | if (devNo >= bus->device_count) { |
bac8a4d5 | 418 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: device (%d) >= deviceCount (%d).", |
43ecb9fe | 419 | devNo, bus->device_count); |
bac8a4d5 KC |
420 | result = CONTROLVM_RESP_ERROR_MAX_DEVICES; |
421 | POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, | |
422 | devNo, busNo, | |
423 | POSTCODE_SEVERITY_ERR); | |
424 | read_unlock(&BusListLock); | |
425 | goto Away; | |
426 | } | |
427 | /* make sure this device is not already set */ | |
428 | if (bus->device[devNo]) { | |
429 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: device %d is already exists.", | |
430 | devNo); | |
431 | POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, | |
432 | devNo, busNo, | |
433 | POSTCODE_SEVERITY_ERR); | |
434 | result = CONTROLVM_RESP_ERROR_ALREADY_DONE; | |
435 | read_unlock(&BusListLock); | |
436 | goto Away; | |
437 | } | |
438 | read_unlock(&BusListLock); | |
439 | /* the msg is bound for virtpci; send | |
440 | * guest_msgs struct to callback | |
441 | */ | |
98d7b594 | 442 | if (!msg->hdr.flags.server) { |
bac8a4d5 | 443 | struct guest_msgs cmd; |
ddc9f84b | 444 | |
f796e84c | 445 | if (!uuid_le_cmp(dev->channel_uuid, |
9eee5d1f | 446 | spar_vhba_channel_protocol_uuid)) { |
9fd1b95a BR |
447 | wait_for_valid_guid(&(( |
448 | struct channel_header | |
449 | __iomem *) (dev-> | |
bac8a4d5 | 450 | chanptr))-> |
a8a31f61 | 451 | chtype); |
93a84565 | 452 | if (!SPAR_VHBA_CHANNEL_OK_CLIENT |
bac8a4d5 KC |
453 | (dev->chanptr, NULL)) { |
454 | LOGERR("CONTROLVM_DEVICE_CREATE Failed:[CLIENT]VHBA dev %d chan invalid.", | |
455 | devNo); | |
456 | POSTCODE_LINUX_4 | |
457 | (DEVICE_CREATE_FAILURE_PC, | |
458 | devNo, busNo, | |
459 | POSTCODE_SEVERITY_ERR); | |
460 | result = CONTROLVM_RESP_ERROR_CHANNEL_INVALID; | |
461 | goto Away; | |
462 | } | |
463 | cmd.msgtype = GUEST_ADD_VHBA; | |
464 | cmd.add_vhba.chanptr = dev->chanptr; | |
8bd352ef BR |
465 | cmd.add_vhba.bus_no = busNo; |
466 | cmd.add_vhba.device_no = devNo; | |
467 | cmd.add_vhba.instance_uuid = | |
f796e84c | 468 | dev->instance_uuid; |
bac8a4d5 KC |
469 | cmd.add_vhba.intr = dev->intr; |
470 | } else | |
f796e84c | 471 | if (!uuid_le_cmp(dev->channel_uuid, |
9eee5d1f | 472 | spar_vnic_channel_protocol_uuid)) { |
9fd1b95a BR |
473 | wait_for_valid_guid(&(( |
474 | struct channel_header | |
475 | __iomem *) (dev-> | |
bac8a4d5 | 476 | chanptr))-> |
a8a31f61 | 477 | chtype); |
93a84565 | 478 | if (!SPAR_VNIC_CHANNEL_OK_CLIENT |
bac8a4d5 KC |
479 | (dev->chanptr, NULL)) { |
480 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: VNIC[CLIENT] dev %d chan invalid.", | |
481 | devNo); | |
482 | POSTCODE_LINUX_4 | |
483 | (DEVICE_CREATE_FAILURE_PC, | |
484 | devNo, busNo, | |
485 | POSTCODE_SEVERITY_ERR); | |
486 | result = CONTROLVM_RESP_ERROR_CHANNEL_INVALID; | |
487 | goto Away; | |
488 | } | |
489 | cmd.msgtype = GUEST_ADD_VNIC; | |
490 | cmd.add_vnic.chanptr = dev->chanptr; | |
8bd352ef BR |
491 | cmd.add_vnic.bus_no = busNo; |
492 | cmd.add_vnic.device_no = devNo; | |
493 | cmd.add_vnic.instance_uuid = | |
f796e84c | 494 | dev->instance_uuid; |
bac8a4d5 KC |
495 | cmd.add_vhba.intr = dev->intr; |
496 | } else { | |
497 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: unknown channelTypeGuid.\n"); | |
498 | POSTCODE_LINUX_4 | |
499 | (DEVICE_CREATE_FAILURE_PC, devNo, | |
500 | busNo, POSTCODE_SEVERITY_ERR); | |
501 | result = CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN; | |
502 | goto Away; | |
503 | } | |
504 | ||
2df7cc62 | 505 | if (!virt_control_chan_func) { |
bac8a4d5 KC |
506 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: virtpci callback not registered."); |
507 | POSTCODE_LINUX_4 | |
508 | (DEVICE_CREATE_FAILURE_PC, devNo, | |
509 | busNo, POSTCODE_SEVERITY_ERR); | |
510 | result = CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE; | |
511 | goto Away; | |
512 | } | |
513 | ||
2df7cc62 | 514 | if (!virt_control_chan_func(&cmd)) { |
bac8a4d5 KC |
515 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: virtpci GUEST_ADD_[VHBA||VNIC] returned error."); |
516 | POSTCODE_LINUX_4 | |
517 | (DEVICE_CREATE_FAILURE_PC, devNo, | |
518 | busNo, POSTCODE_SEVERITY_ERR); | |
519 | result = CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR; | |
520 | goto Away; | |
521 | } | |
522 | } | |
523 | bus->device[devNo] = dev; | |
524 | POSTCODE_LINUX_4(DEVICE_CREATE_SUCCESS_PC, devNo, busNo, | |
525 | POSTCODE_SEVERITY_INFO); | |
526 | return CONTROLVM_RESP_SUCCESS; | |
527 | } | |
528 | } | |
529 | read_unlock(&BusListLock); | |
530 | ||
531 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: failed to find bus %d.", busNo); | |
532 | POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo, | |
533 | POSTCODE_SEVERITY_ERR); | |
534 | result = CONTROLVM_RESP_ERROR_BUS_INVALID; | |
535 | ||
536 | Away: | |
98d7b594 | 537 | if (!msg->hdr.flags.test_message) { |
bac8a4d5 KC |
538 | uislib_iounmap(dev->chanptr); |
539 | dev->chanptr = NULL; | |
540 | } | |
541 | ||
60140462 | 542 | kfree(dev); |
bac8a4d5 KC |
543 | return result; |
544 | } | |
545 | ||
546 | static int | |
3ab47701 | 547 | pause_device(struct controlvm_message *msg) |
bac8a4d5 | 548 | { |
b3c55b13 | 549 | u32 busNo, devNo; |
bac8a4d5 KC |
550 | struct bus_info *bus; |
551 | struct device_info *dev; | |
552 | struct guest_msgs cmd; | |
b5114432 | 553 | int retval = CONTROLVM_RESP_SUCCESS; |
bac8a4d5 | 554 | |
2ea5117b BR |
555 | busNo = msg->cmd.device_change_state.bus_no; |
556 | devNo = msg->cmd.device_change_state.dev_no; | |
bac8a4d5 KC |
557 | |
558 | read_lock(&BusListLock); | |
559 | for (bus = BusListHead; bus; bus = bus->next) { | |
43ecb9fe | 560 | if (bus->bus_no == busNo) { |
bac8a4d5 | 561 | /* make sure the device number is valid */ |
43ecb9fe | 562 | if (devNo >= bus->device_count) { |
bac8a4d5 | 563 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: device(%d) >= deviceCount(%d).", |
43ecb9fe | 564 | devNo, bus->device_count); |
b5114432 | 565 | retval = CONTROLVM_RESP_ERROR_DEVICE_INVALID; |
bac8a4d5 | 566 | } else { |
b5114432 SM |
567 | /* make sure this device exists */ |
568 | dev = bus->device[devNo]; | |
569 | if (!dev) { | |
570 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: device %d does not exist.", | |
571 | devNo); | |
572 | retval = | |
573 | CONTROLVM_RESP_ERROR_ALREADY_DONE; | |
574 | } | |
bac8a4d5 KC |
575 | } |
576 | break; | |
577 | } | |
578 | } | |
bac8a4d5 KC |
579 | if (!bus) { |
580 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: bus %d does not exist", | |
581 | busNo); | |
b5114432 | 582 | retval = CONTROLVM_RESP_ERROR_BUS_INVALID; |
bac8a4d5 | 583 | } |
b5114432 SM |
584 | read_unlock(&BusListLock); |
585 | if (retval == CONTROLVM_RESP_SUCCESS) { | |
586 | /* the msg is bound for virtpci; send | |
587 | * guest_msgs struct to callback | |
588 | */ | |
f796e84c | 589 | if (!uuid_le_cmp(dev->channel_uuid, |
9eee5d1f | 590 | spar_vhba_channel_protocol_uuid)) { |
b5114432 SM |
591 | cmd.msgtype = GUEST_PAUSE_VHBA; |
592 | cmd.pause_vhba.chanptr = dev->chanptr; | |
f796e84c | 593 | } else if (!uuid_le_cmp(dev->channel_uuid, |
9eee5d1f | 594 | spar_vnic_channel_protocol_uuid)) { |
b5114432 SM |
595 | cmd.msgtype = GUEST_PAUSE_VNIC; |
596 | cmd.pause_vnic.chanptr = dev->chanptr; | |
597 | } else { | |
598 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: unknown channelTypeGuid.\n"); | |
599 | return CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN; | |
600 | } | |
2df7cc62 | 601 | if (!virt_control_chan_func) { |
b5114432 SM |
602 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE Failed: virtpci callback not registered."); |
603 | return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE; | |
604 | } | |
2df7cc62 | 605 | if (!virt_control_chan_func(&cmd)) { |
b5114432 SM |
606 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: virtpci GUEST_PAUSE_[VHBA||VNIC] returned error."); |
607 | return | |
608 | CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR; | |
609 | } | |
610 | } | |
611 | return retval; | |
bac8a4d5 KC |
612 | } |
613 | ||
614 | static int | |
3ab47701 | 615 | resume_device(struct controlvm_message *msg) |
bac8a4d5 | 616 | { |
b3c55b13 | 617 | u32 busNo, devNo; |
bac8a4d5 KC |
618 | struct bus_info *bus; |
619 | struct device_info *dev; | |
620 | struct guest_msgs cmd; | |
4f01952d | 621 | int retval = CONTROLVM_RESP_SUCCESS; |
bac8a4d5 | 622 | |
2ea5117b BR |
623 | busNo = msg->cmd.device_change_state.bus_no; |
624 | devNo = msg->cmd.device_change_state.dev_no; | |
bac8a4d5 KC |
625 | |
626 | read_lock(&BusListLock); | |
627 | for (bus = BusListHead; bus; bus = bus->next) { | |
43ecb9fe | 628 | if (bus->bus_no == busNo) { |
bac8a4d5 | 629 | /* make sure the device number is valid */ |
43ecb9fe | 630 | if (devNo >= bus->device_count) { |
bac8a4d5 | 631 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: device(%d) >= deviceCount(%d).", |
43ecb9fe | 632 | devNo, bus->device_count); |
4f01952d | 633 | retval = CONTROLVM_RESP_ERROR_DEVICE_INVALID; |
bac8a4d5 | 634 | } else { |
4f01952d SM |
635 | /* make sure this device exists */ |
636 | dev = bus->device[devNo]; | |
637 | if (!dev) { | |
638 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: device %d does not exist.", | |
639 | devNo); | |
640 | retval = | |
641 | CONTROLVM_RESP_ERROR_ALREADY_DONE; | |
642 | } | |
bac8a4d5 KC |
643 | } |
644 | break; | |
645 | } | |
646 | } | |
647 | ||
648 | if (!bus) { | |
649 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: bus %d does not exist", | |
650 | busNo); | |
4f01952d | 651 | retval = CONTROLVM_RESP_ERROR_BUS_INVALID; |
bac8a4d5 | 652 | } |
4f01952d SM |
653 | read_unlock(&BusListLock); |
654 | /* the msg is bound for virtpci; send | |
655 | * guest_msgs struct to callback | |
656 | */ | |
657 | if (retval == CONTROLVM_RESP_SUCCESS) { | |
f796e84c | 658 | if (!uuid_le_cmp(dev->channel_uuid, |
9eee5d1f | 659 | spar_vhba_channel_protocol_uuid)) { |
4f01952d SM |
660 | cmd.msgtype = GUEST_RESUME_VHBA; |
661 | cmd.resume_vhba.chanptr = dev->chanptr; | |
f796e84c | 662 | } else if (!uuid_le_cmp(dev->channel_uuid, |
9eee5d1f | 663 | spar_vnic_channel_protocol_uuid)) { |
4f01952d SM |
664 | cmd.msgtype = GUEST_RESUME_VNIC; |
665 | cmd.resume_vnic.chanptr = dev->chanptr; | |
666 | } else { | |
667 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: unknown channelTypeGuid.\n"); | |
668 | return CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN; | |
669 | } | |
2df7cc62 | 670 | if (!virt_control_chan_func) { |
4f01952d SM |
671 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE Failed: virtpci callback not registered."); |
672 | return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE; | |
673 | } | |
2df7cc62 | 674 | if (!virt_control_chan_func(&cmd)) { |
4f01952d SM |
675 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: virtpci GUEST_RESUME_[VHBA||VNIC] returned error."); |
676 | return | |
677 | CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR; | |
678 | } | |
679 | } | |
680 | return retval; | |
bac8a4d5 KC |
681 | } |
682 | ||
683 | static int | |
3ab47701 | 684 | destroy_device(struct controlvm_message *msg, char *buf) |
bac8a4d5 | 685 | { |
b3c55b13 | 686 | u32 busNo, devNo; |
bac8a4d5 KC |
687 | struct bus_info *bus; |
688 | struct device_info *dev; | |
689 | struct guest_msgs cmd; | |
3aa2ec58 | 690 | int retval = CONTROLVM_RESP_SUCCESS; |
bac8a4d5 | 691 | |
2ea5117b BR |
692 | busNo = msg->cmd.destroy_device.bus_no; |
693 | devNo = msg->cmd.destroy_device.bus_no; | |
bac8a4d5 KC |
694 | |
695 | read_lock(&BusListLock); | |
696 | LOGINF("destroy_device called for busNo=%u, devNo=%u", busNo, devNo); | |
697 | for (bus = BusListHead; bus; bus = bus->next) { | |
43ecb9fe | 698 | if (bus->bus_no == busNo) { |
bac8a4d5 | 699 | /* make sure the device number is valid */ |
43ecb9fe | 700 | if (devNo >= bus->device_count) { |
bac8a4d5 | 701 | LOGERR("CONTROLVM_DEVICE_DESTORY Failed: device(%d) >= deviceCount(%d).", |
43ecb9fe | 702 | devNo, bus->device_count); |
3aa2ec58 | 703 | retval = CONTROLVM_RESP_ERROR_DEVICE_INVALID; |
bac8a4d5 | 704 | } else { |
3aa2ec58 SM |
705 | /* make sure this device exists */ |
706 | dev = bus->device[devNo]; | |
707 | if (!dev) { | |
708 | LOGERR("CONTROLVM_DEVICE_DESTROY Failed: device %d does not exist.", | |
709 | devNo); | |
710 | retval = | |
711 | CONTROLVM_RESP_ERROR_ALREADY_DONE; | |
712 | } | |
bac8a4d5 | 713 | } |
bac8a4d5 KC |
714 | break; |
715 | } | |
716 | } | |
717 | ||
718 | if (!bus) { | |
719 | LOGERR("CONTROLVM_DEVICE_DESTROY Failed: bus %d does not exist", | |
720 | busNo); | |
3aa2ec58 | 721 | retval = CONTROLVM_RESP_ERROR_BUS_INVALID; |
bac8a4d5 | 722 | } |
3aa2ec58 SM |
723 | read_unlock(&BusListLock); |
724 | if (retval == CONTROLVM_RESP_SUCCESS) { | |
725 | /* the msg is bound for virtpci; send | |
726 | * guest_msgs struct to callback | |
727 | */ | |
f796e84c | 728 | if (!uuid_le_cmp(dev->channel_uuid, |
9eee5d1f | 729 | spar_vhba_channel_protocol_uuid)) { |
3aa2ec58 SM |
730 | cmd.msgtype = GUEST_DEL_VHBA; |
731 | cmd.del_vhba.chanptr = dev->chanptr; | |
f796e84c | 732 | } else if (!uuid_le_cmp(dev->channel_uuid, |
9eee5d1f | 733 | spar_vnic_channel_protocol_uuid)) { |
3aa2ec58 SM |
734 | cmd.msgtype = GUEST_DEL_VNIC; |
735 | cmd.del_vnic.chanptr = dev->chanptr; | |
736 | } else { | |
737 | LOGERR("CONTROLVM_DEVICE_DESTROY Failed: unknown channelTypeGuid.\n"); | |
738 | return | |
739 | CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN; | |
740 | } | |
2df7cc62 | 741 | if (!virt_control_chan_func) { |
3aa2ec58 SM |
742 | LOGERR("CONTROLVM_DEVICE_DESTORY Failed: virtpci callback not registered."); |
743 | return | |
744 | CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE; | |
745 | } | |
2df7cc62 | 746 | if (!virt_control_chan_func(&cmd)) { |
3aa2ec58 SM |
747 | LOGERR("CONTROLVM_DEVICE_DESTROY Failed: virtpci GUEST_DEL_[VHBA||VNIC] returned error."); |
748 | return | |
749 | CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR; | |
750 | } | |
751 | /* you must disable channel interrupts BEFORE you unmap the channel, | |
752 | * because if you unmap first, there may still be some activity going | |
753 | * on which accesses the channel and you will get a "unable to handle | |
754 | * kernel paging request" | |
755 | */ | |
756 | if (dev->polling) { | |
757 | LOGINF("calling uislib_disable_channel_interrupts"); | |
758 | uislib_disable_channel_interrupts(busNo, devNo); | |
759 | } | |
760 | /* unmap the channel memory for the device. */ | |
98d7b594 | 761 | if (!msg->hdr.flags.test_message) { |
3aa2ec58 SM |
762 | LOGINF("destroy_device, doing iounmap"); |
763 | uislib_iounmap(dev->chanptr); | |
764 | } | |
765 | kfree(dev); | |
766 | bus->device[devNo] = NULL; | |
767 | } | |
768 | return retval; | |
bac8a4d5 KC |
769 | } |
770 | ||
bac8a4d5 | 771 | static int |
3ab47701 | 772 | init_chipset(struct controlvm_message *msg, char *buf) |
bac8a4d5 KC |
773 | { |
774 | POSTCODE_LINUX_2(CHIPSET_INIT_ENTRY_PC, POSTCODE_SEVERITY_INFO); | |
775 | ||
2ea5117b BR |
776 | MaxBusCount = msg->cmd.init_chipset.bus_count; |
777 | PlatformNumber = msg->cmd.init_chipset.platform_number; | |
bac8a4d5 KC |
778 | PhysicalDataChan = 0; |
779 | ||
780 | /* We need to make sure we have our functions registered | |
781 | * before processing messages. If we are a test vehicle the | |
98d7b594 | 782 | * test_message for init_chipset will be set. We can ignore the |
bac8a4d5 | 783 | * waits for the callbacks, since this will be manually entered |
98d7b594 | 784 | * from a user. If no test_message is set, we will wait for the |
bac8a4d5 KC |
785 | * functions. |
786 | */ | |
98d7b594 | 787 | if (!msg->hdr.flags.test_message) |
2df7cc62 | 788 | WAIT_ON_CALLBACK(virt_control_chan_func); |
bac8a4d5 KC |
789 | |
790 | chipset_inited = 1; | |
791 | POSTCODE_LINUX_2(CHIPSET_INIT_EXIT_PC, POSTCODE_SEVERITY_INFO); | |
792 | ||
793 | return CONTROLVM_RESP_SUCCESS; | |
794 | } | |
795 | ||
bac8a4d5 | 796 | static int |
b3c55b13 | 797 | delete_bus_glue(u32 busNo) |
bac8a4d5 | 798 | { |
3ab47701 | 799 | struct controlvm_message msg; |
bac8a4d5 KC |
800 | |
801 | init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, 0); | |
2ea5117b | 802 | msg.cmd.destroy_bus.bus_no = busNo; |
bac8a4d5 KC |
803 | if (destroy_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { |
804 | LOGERR("destroy_bus failed. busNo=0x%x\n", busNo); | |
805 | return 0; | |
806 | } | |
807 | return 1; | |
808 | } | |
809 | ||
810 | static int | |
b3c55b13 | 811 | delete_device_glue(u32 busNo, u32 devNo) |
bac8a4d5 | 812 | { |
3ab47701 | 813 | struct controlvm_message msg; |
bac8a4d5 KC |
814 | |
815 | init_msg_header(&msg, CONTROLVM_DEVICE_DESTROY, 0, 0); | |
2ea5117b BR |
816 | msg.cmd.destroy_device.bus_no = busNo; |
817 | msg.cmd.destroy_device.dev_no = devNo; | |
bac8a4d5 KC |
818 | if (destroy_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { |
819 | LOGERR("destroy_device failed. busNo=0x%x devNo=0x%x\n", busNo, | |
820 | devNo); | |
821 | return 0; | |
822 | } | |
823 | return 1; | |
824 | } | |
825 | ||
826 | int | |
e1242538 BR |
827 | uislib_client_inject_add_bus(u32 bus_no, uuid_le inst_uuid, |
828 | u64 channel_addr, ulong n_channel_bytes) | |
bac8a4d5 | 829 | { |
3ab47701 | 830 | struct controlvm_message msg; |
bac8a4d5 | 831 | |
e1242538 | 832 | LOGINF("enter busNo=0x%x\n", bus_no); |
bac8a4d5 | 833 | /* step 0: init the chipset */ |
e1242538 | 834 | POSTCODE_LINUX_3(CHIPSET_INIT_ENTRY_PC, bus_no, POSTCODE_SEVERITY_INFO); |
bac8a4d5 KC |
835 | |
836 | if (!chipset_inited) { | |
837 | /* step: initialize the chipset */ | |
838 | init_msg_header(&msg, CONTROLVM_CHIPSET_INIT, 0, 0); | |
839 | /* this change is needed so that console will come up | |
840 | * OK even when the bus 0 create comes in late. If the | |
841 | * bus 0 create is the first create, then the add_vnic | |
842 | * will work fine, but if the bus 0 create arrives | |
843 | * after number 4, then the add_vnic will fail, and the | |
844 | * ultraboot will fail. | |
845 | */ | |
2ea5117b BR |
846 | msg.cmd.init_chipset.bus_count = 23; |
847 | msg.cmd.init_chipset.switch_count = 0; | |
bac8a4d5 KC |
848 | if (init_chipset(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { |
849 | LOGERR("init_chipset failed.\n"); | |
850 | return 0; | |
851 | } | |
852 | LOGINF("chipset initialized\n"); | |
e1242538 | 853 | POSTCODE_LINUX_3(CHIPSET_INIT_EXIT_PC, bus_no, |
bac8a4d5 KC |
854 | POSTCODE_SEVERITY_INFO); |
855 | } | |
856 | ||
857 | /* step 1: create a bus */ | |
e1242538 BR |
858 | POSTCODE_LINUX_3(BUS_CREATE_ENTRY_PC, bus_no, |
859 | POSTCODE_SEVERITY_WARNING); | |
bac8a4d5 | 860 | init_msg_header(&msg, CONTROLVM_BUS_CREATE, 0, 0); |
2ea5117b BR |
861 | msg.cmd.create_bus.bus_no = bus_no; |
862 | msg.cmd.create_bus.dev_count = 23; /* devNo+1; */ | |
863 | msg.cmd.create_bus.channel_addr = channel_addr; | |
864 | msg.cmd.create_bus.channel_bytes = n_channel_bytes; | |
bac8a4d5 KC |
865 | if (create_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { |
866 | LOGERR("create_bus failed.\n"); | |
e1242538 | 867 | POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no, |
bac8a4d5 KC |
868 | POSTCODE_SEVERITY_ERR); |
869 | return 0; | |
870 | } | |
e1242538 | 871 | POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, bus_no, POSTCODE_SEVERITY_INFO); |
bac8a4d5 KC |
872 | |
873 | return 1; | |
874 | } | |
875 | EXPORT_SYMBOL_GPL(uislib_client_inject_add_bus); | |
876 | ||
877 | ||
878 | int | |
ac15ba59 | 879 | uislib_client_inject_del_bus(u32 bus_no) |
bac8a4d5 | 880 | { |
ac15ba59 | 881 | return delete_bus_glue(bus_no); |
bac8a4d5 KC |
882 | } |
883 | EXPORT_SYMBOL_GPL(uislib_client_inject_del_bus); | |
884 | ||
885 | int | |
062d312d | 886 | uislib_client_inject_pause_vhba(u32 bus_no, u32 dev_no) |
bac8a4d5 | 887 | { |
3ab47701 | 888 | struct controlvm_message msg; |
bac8a4d5 KC |
889 | int rc; |
890 | ||
891 | init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0); | |
2ea5117b BR |
892 | msg.cmd.device_change_state.bus_no = bus_no; |
893 | msg.cmd.device_change_state.dev_no = dev_no; | |
894 | msg.cmd.device_change_state.state = segment_state_standby; | |
bac8a4d5 KC |
895 | rc = pause_device(&msg); |
896 | if (rc != CONTROLVM_RESP_SUCCESS) { | |
897 | LOGERR("VHBA pause_device failed. busNo=0x%x devNo=0x%x\n", | |
062d312d | 898 | bus_no, dev_no); |
bac8a4d5 KC |
899 | return rc; |
900 | } | |
901 | return 0; | |
902 | } | |
903 | EXPORT_SYMBOL_GPL(uislib_client_inject_pause_vhba); | |
904 | ||
905 | int | |
ae47a51b | 906 | uislib_client_inject_resume_vhba(u32 bus_no, u32 dev_no) |
bac8a4d5 | 907 | { |
3ab47701 | 908 | struct controlvm_message msg; |
bac8a4d5 KC |
909 | int rc; |
910 | ||
911 | init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0); | |
2ea5117b BR |
912 | msg.cmd.device_change_state.bus_no = bus_no; |
913 | msg.cmd.device_change_state.dev_no = dev_no; | |
914 | msg.cmd.device_change_state.state = segment_state_running; | |
bac8a4d5 KC |
915 | rc = resume_device(&msg); |
916 | if (rc != CONTROLVM_RESP_SUCCESS) { | |
917 | LOGERR("VHBA resume_device failed. busNo=0x%x devNo=0x%x\n", | |
ae47a51b | 918 | bus_no, dev_no); |
bac8a4d5 KC |
919 | return rc; |
920 | } | |
921 | return 0; | |
922 | ||
923 | } | |
924 | EXPORT_SYMBOL_GPL(uislib_client_inject_resume_vhba); | |
925 | ||
926 | int | |
3d3b7154 | 927 | uislib_client_inject_add_vhba(u32 bus_no, u32 dev_no, |
5fc0229a | 928 | u64 phys_chan_addr, u32 chan_bytes, |
3d3b7154 | 929 | int is_test_addr, uuid_le inst_uuid, |
4eddbf13 | 930 | struct irq_info *intr) |
bac8a4d5 | 931 | { |
3ab47701 | 932 | struct controlvm_message msg; |
bac8a4d5 | 933 | |
3d3b7154 | 934 | LOGINF(" enter busNo=0x%x devNo=0x%x\n", bus_no, dev_no); |
bac8a4d5 KC |
935 | /* chipset init'ed with bus bus has been previously created - |
936 | * Verify it still exists step 2: create the VHBA device on the | |
937 | * bus | |
938 | */ | |
3d3b7154 | 939 | POSTCODE_LINUX_4(VHBA_CREATE_ENTRY_PC, dev_no, bus_no, |
bac8a4d5 KC |
940 | POSTCODE_SEVERITY_INFO); |
941 | ||
942 | init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, 0); | |
943 | if (is_test_addr) | |
944 | /* signify that the physical channel address does NOT | |
945 | * need to be ioremap()ed | |
946 | */ | |
98d7b594 | 947 | msg.hdr.flags.test_message = 1; |
f91b9262 BR |
948 | msg.cmd.create_device.bus_no = bus_no; |
949 | msg.cmd.create_device.dev_no = dev_no; | |
950 | msg.cmd.create_device.dev_inst_uuid = inst_uuid; | |
bac8a4d5 | 951 | if (intr) |
2ea5117b | 952 | msg.cmd.create_device.intr = *intr; |
bac8a4d5 | 953 | else |
2ea5117b | 954 | memset(&msg.cmd.create_device.intr, 0, |
4eddbf13 | 955 | sizeof(struct irq_info)); |
f91b9262 | 956 | msg.cmd.create_device.channel_addr = phys_chan_addr; |
bac8a4d5 KC |
957 | if (chan_bytes < MIN_IO_CHANNEL_SIZE) { |
958 | LOGERR("wrong channel size.chan_bytes = 0x%x IO_CHANNEL_SIZE= 0x%x\n", | |
959 | chan_bytes, (unsigned int) MIN_IO_CHANNEL_SIZE); | |
960 | POSTCODE_LINUX_4(VHBA_CREATE_FAILURE_PC, chan_bytes, | |
961 | MIN_IO_CHANNEL_SIZE, POSTCODE_SEVERITY_ERR); | |
962 | return 0; | |
963 | } | |
f91b9262 BR |
964 | msg.cmd.create_device.channel_bytes = chan_bytes; |
965 | msg.cmd.create_device.data_type_uuid = spar_vhba_channel_protocol_uuid; | |
bac8a4d5 KC |
966 | if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { |
967 | LOGERR("VHBA create_device failed.\n"); | |
3d3b7154 | 968 | POSTCODE_LINUX_4(VHBA_CREATE_FAILURE_PC, dev_no, bus_no, |
bac8a4d5 KC |
969 | POSTCODE_SEVERITY_ERR); |
970 | return 0; | |
971 | } | |
3d3b7154 | 972 | POSTCODE_LINUX_4(VHBA_CREATE_SUCCESS_PC, dev_no, bus_no, |
bac8a4d5 KC |
973 | POSTCODE_SEVERITY_INFO); |
974 | return 1; | |
975 | } | |
976 | EXPORT_SYMBOL_GPL(uislib_client_inject_add_vhba); | |
977 | ||
978 | int | |
6bc962ac | 979 | uislib_client_inject_del_vhba(u32 bus_no, u32 dev_no) |
bac8a4d5 | 980 | { |
6bc962ac | 981 | return delete_device_glue(bus_no, dev_no); |
bac8a4d5 KC |
982 | } |
983 | EXPORT_SYMBOL_GPL(uislib_client_inject_del_vhba); | |
984 | ||
985 | int | |
94a887da | 986 | uislib_client_inject_add_vnic(u32 bus_no, u32 dev_no, |
5fc0229a | 987 | u64 phys_chan_addr, u32 chan_bytes, |
94a887da | 988 | int is_test_addr, uuid_le inst_uuid, |
4eddbf13 | 989 | struct irq_info *intr) |
bac8a4d5 | 990 | { |
3ab47701 | 991 | struct controlvm_message msg; |
bac8a4d5 | 992 | |
94a887da | 993 | LOGINF(" enter busNo=0x%x devNo=0x%x\n", bus_no, dev_no); |
bac8a4d5 KC |
994 | /* chipset init'ed with bus bus has been previously created - |
995 | * Verify it still exists step 2: create the VNIC device on the | |
996 | * bus | |
997 | */ | |
94a887da | 998 | POSTCODE_LINUX_4(VNIC_CREATE_ENTRY_PC, dev_no, bus_no, |
bac8a4d5 KC |
999 | POSTCODE_SEVERITY_INFO); |
1000 | ||
1001 | init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, 0); | |
1002 | if (is_test_addr) | |
1003 | /* signify that the physical channel address does NOT | |
1004 | * need to be ioremap()ed | |
1005 | */ | |
98d7b594 | 1006 | msg.hdr.flags.test_message = 1; |
f91b9262 BR |
1007 | msg.cmd.create_device.bus_no = bus_no; |
1008 | msg.cmd.create_device.dev_no = dev_no; | |
1009 | msg.cmd.create_device.dev_inst_uuid = inst_uuid; | |
bac8a4d5 | 1010 | if (intr) |
2ea5117b | 1011 | msg.cmd.create_device.intr = *intr; |
bac8a4d5 | 1012 | else |
2ea5117b | 1013 | memset(&msg.cmd.create_device.intr, 0, |
4eddbf13 | 1014 | sizeof(struct irq_info)); |
f91b9262 | 1015 | msg.cmd.create_device.channel_addr = phys_chan_addr; |
bac8a4d5 KC |
1016 | if (chan_bytes < MIN_IO_CHANNEL_SIZE) { |
1017 | LOGERR("wrong channel size.chan_bytes = 0x%x IO_CHANNEL_SIZE= 0x%x\n", | |
1018 | chan_bytes, (unsigned int) MIN_IO_CHANNEL_SIZE); | |
1019 | POSTCODE_LINUX_4(VNIC_CREATE_FAILURE_PC, chan_bytes, | |
1020 | MIN_IO_CHANNEL_SIZE, POSTCODE_SEVERITY_ERR); | |
1021 | return 0; | |
1022 | } | |
f91b9262 BR |
1023 | msg.cmd.create_device.channel_bytes = chan_bytes; |
1024 | msg.cmd.create_device.data_type_uuid = spar_vnic_channel_protocol_uuid; | |
bac8a4d5 KC |
1025 | if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { |
1026 | LOGERR("VNIC create_device failed.\n"); | |
94a887da | 1027 | POSTCODE_LINUX_4(VNIC_CREATE_FAILURE_PC, dev_no, bus_no, |
bac8a4d5 KC |
1028 | POSTCODE_SEVERITY_ERR); |
1029 | return 0; | |
1030 | } | |
1031 | ||
94a887da | 1032 | POSTCODE_LINUX_4(VNIC_CREATE_SUCCESS_PC, dev_no, bus_no, |
bac8a4d5 KC |
1033 | POSTCODE_SEVERITY_INFO); |
1034 | return 1; | |
1035 | } | |
1036 | EXPORT_SYMBOL_GPL(uislib_client_inject_add_vnic); | |
1037 | ||
1038 | int | |
68a4b12c | 1039 | uislib_client_inject_pause_vnic(u32 bus_no, u32 dev_no) |
bac8a4d5 | 1040 | { |
3ab47701 | 1041 | struct controlvm_message msg; |
bac8a4d5 KC |
1042 | int rc; |
1043 | ||
1044 | init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0); | |
2ea5117b BR |
1045 | msg.cmd.device_change_state.bus_no = bus_no; |
1046 | msg.cmd.device_change_state.dev_no = dev_no; | |
1047 | msg.cmd.device_change_state.state = segment_state_standby; | |
bac8a4d5 KC |
1048 | rc = pause_device(&msg); |
1049 | if (rc != CONTROLVM_RESP_SUCCESS) { | |
1050 | LOGERR("VNIC pause_device failed. busNo=0x%x devNo=0x%x\n", | |
68a4b12c | 1051 | bus_no, dev_no); |
bac8a4d5 KC |
1052 | return -1; |
1053 | } | |
1054 | return 0; | |
1055 | } | |
1056 | EXPORT_SYMBOL_GPL(uislib_client_inject_pause_vnic); | |
1057 | ||
1058 | int | |
3fe7cec4 | 1059 | uislib_client_inject_resume_vnic(u32 bus_no, u32 dev_no) |
bac8a4d5 | 1060 | { |
3ab47701 | 1061 | struct controlvm_message msg; |
bac8a4d5 KC |
1062 | int rc; |
1063 | ||
1064 | init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0); | |
2ea5117b BR |
1065 | msg.cmd.device_change_state.bus_no = bus_no; |
1066 | msg.cmd.device_change_state.dev_no = dev_no; | |
1067 | msg.cmd.device_change_state.state = segment_state_running; | |
bac8a4d5 KC |
1068 | rc = resume_device(&msg); |
1069 | if (rc != CONTROLVM_RESP_SUCCESS) { | |
1070 | LOGERR("VNIC resume_device failed. busNo=0x%x devNo=0x%x\n", | |
3fe7cec4 | 1071 | bus_no, dev_no); |
bac8a4d5 KC |
1072 | return -1; |
1073 | } | |
1074 | return 0; | |
1075 | ||
1076 | } | |
1077 | EXPORT_SYMBOL_GPL(uislib_client_inject_resume_vnic); | |
1078 | ||
1079 | int | |
bdb628d0 | 1080 | uislib_client_inject_del_vnic(u32 bus_no, u32 dev_no) |
bac8a4d5 | 1081 | { |
bdb628d0 | 1082 | return delete_device_glue(bus_no, dev_no); |
bac8a4d5 KC |
1083 | } |
1084 | EXPORT_SYMBOL_GPL(uislib_client_inject_del_vnic); | |
1085 | ||
a8d7f21d | 1086 | static int |
b3c55b13 | 1087 | uislib_client_add_vnic(u32 busNo) |
bac8a4d5 KC |
1088 | { |
1089 | BOOL busCreated = FALSE; | |
1090 | int devNo = 0; /* Default to 0, since only one device | |
1091 | * will be created for this bus... */ | |
3ab47701 | 1092 | struct controlvm_message msg; |
bac8a4d5 KC |
1093 | |
1094 | init_msg_header(&msg, CONTROLVM_BUS_CREATE, 0, 0); | |
98d7b594 | 1095 | msg.hdr.flags.test_message = 1; |
2ea5117b BR |
1096 | msg.cmd.create_bus.bus_no = busNo; |
1097 | msg.cmd.create_bus.dev_count = 4; | |
1098 | msg.cmd.create_bus.channel_addr = 0; | |
1099 | msg.cmd.create_bus.channel_bytes = 0; | |
bac8a4d5 KC |
1100 | if (create_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { |
1101 | LOGERR("client create_bus failed"); | |
1102 | return 0; | |
1103 | } | |
1104 | busCreated = TRUE; | |
1105 | ||
1106 | init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, 0); | |
98d7b594 | 1107 | msg.hdr.flags.test_message = 1; |
f91b9262 BR |
1108 | msg.cmd.create_device.bus_no = busNo; |
1109 | msg.cmd.create_device.dev_no = devNo; | |
1110 | msg.cmd.create_device.dev_inst_uuid = NULL_UUID_LE; | |
2ea5117b | 1111 | memset(&msg.cmd.create_device.intr, 0, sizeof(struct irq_info)); |
f91b9262 BR |
1112 | msg.cmd.create_device.channel_addr = PhysicalDataChan; |
1113 | msg.cmd.create_device.channel_bytes = MIN_IO_CHANNEL_SIZE; | |
1114 | msg.cmd.create_device.data_type_uuid = spar_vnic_channel_protocol_uuid; | |
bac8a4d5 KC |
1115 | if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { |
1116 | LOGERR("client create_device failed"); | |
1117 | goto AwayCleanup; | |
1118 | } | |
1119 | ||
1120 | return 1; | |
1121 | ||
1122 | AwayCleanup: | |
1123 | if (busCreated) { | |
1124 | init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, 0); | |
98d7b594 | 1125 | msg.hdr.flags.test_message = 1; |
2ea5117b | 1126 | msg.cmd.destroy_bus.bus_no = busNo; |
bac8a4d5 KC |
1127 | if (destroy_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) |
1128 | LOGERR("client destroy_bus failed.\n"); | |
1129 | } | |
1130 | ||
1131 | return 0; | |
1132 | } /* end uislib_client_add_vnic */ | |
1133 | EXPORT_SYMBOL_GPL(uislib_client_add_vnic); | |
1134 | ||
a8d7f21d | 1135 | static int |
b3c55b13 | 1136 | uislib_client_delete_vnic(u32 busNo) |
bac8a4d5 KC |
1137 | { |
1138 | int devNo = 0; /* Default to 0, since only one device | |
1139 | * will be created for this bus... */ | |
3ab47701 | 1140 | struct controlvm_message msg; |
bac8a4d5 KC |
1141 | |
1142 | init_msg_header(&msg, CONTROLVM_DEVICE_DESTROY, 0, 0); | |
98d7b594 | 1143 | msg.hdr.flags.test_message = 1; |
2ea5117b BR |
1144 | msg.cmd.destroy_device.bus_no = busNo; |
1145 | msg.cmd.destroy_device.dev_no = devNo; | |
bac8a4d5 KC |
1146 | if (destroy_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { |
1147 | /* Don't error exit - try to see if bus can be destroyed... */ | |
1148 | LOGERR("client destroy_device failed.\n"); | |
1149 | } | |
1150 | ||
1151 | init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, 0); | |
98d7b594 | 1152 | msg.hdr.flags.test_message = 1; |
2ea5117b | 1153 | msg.cmd.destroy_bus.bus_no = busNo; |
bac8a4d5 KC |
1154 | if (destroy_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) |
1155 | LOGERR("client destroy_bus failed.\n"); | |
1156 | ||
1157 | return 1; | |
1158 | } | |
1159 | EXPORT_SYMBOL_GPL(uislib_client_delete_vnic); | |
60140462 | 1160 | /* end client_delete_vnic */ |
bac8a4d5 KC |
1161 | |
1162 | void * | |
1163 | uislib_cache_alloc(struct kmem_cache *cur_pool, char *fn, int ln) | |
1164 | { | |
1165 | /* __GFP_NORETRY means "ok to fail", meaning kmalloc() can | |
1166 | * return NULL. If you do NOT specify __GFP_NORETRY, Linux | |
1167 | * will go to extreme measures to get memory for you (like, | |
1168 | * invoke oom killer), which will probably cripple the system. | |
1169 | */ | |
1170 | void *p = kmem_cache_alloc(cur_pool, GFP_ATOMIC | __GFP_NORETRY); | |
ddc9f84b | 1171 | |
bac8a4d5 KC |
1172 | if (p == NULL) { |
1173 | LOGERR("uislib_malloc failed to alloc uiscmdrsp @%s:%d", | |
1174 | fn, ln); | |
1175 | return NULL; | |
1176 | } | |
1177 | return p; | |
1178 | } | |
1179 | EXPORT_SYMBOL_GPL(uislib_cache_alloc); | |
1180 | ||
1181 | void | |
1182 | uislib_cache_free(struct kmem_cache *cur_pool, void *p, char *fn, int ln) | |
1183 | { | |
1184 | if (p == NULL) { | |
1185 | LOGERR("uislib_free NULL pointer @%s:%d", fn, ln); | |
1186 | return; | |
1187 | } | |
1188 | kmem_cache_free(cur_pool, p); | |
1189 | } | |
1190 | EXPORT_SYMBOL_GPL(uislib_cache_free); | |
1191 | ||
1192 | /*****************************************************/ | |
1193 | /* proc filesystem callback functions */ | |
1194 | /*****************************************************/ | |
1195 | ||
27dd5548 KC |
1196 | #define PLINE(...) uisutil_add_proc_line_ex(&tot, buff, \ |
1197 | buff_len, __VA_ARGS__) | |
bac8a4d5 KC |
1198 | |
1199 | static int | |
7ec96720 | 1200 | info_debugfs_read_helper(char **buff, int *buff_len) |
bac8a4d5 KC |
1201 | { |
1202 | int i, tot = 0; | |
1203 | struct bus_info *bus; | |
1204 | ||
27dd5548 KC |
1205 | if (PLINE("\nBuses:\n") < 0) |
1206 | goto err_done; | |
bac8a4d5 KC |
1207 | |
1208 | read_lock(&BusListLock); | |
1209 | for (bus = BusListHead; bus; bus = bus->next) { | |
1210 | ||
27dd5548 | 1211 | if (PLINE(" bus=0x%p, busNo=%d, deviceCount=%d\n", |
43ecb9fe | 1212 | bus, bus->bus_no, bus->device_count) < 0) |
27dd5548 | 1213 | goto err_done_unlock; |
bac8a4d5 | 1214 | |
27dd5548 KC |
1215 | |
1216 | if (PLINE(" Devices:\n") < 0) | |
1217 | goto err_done_unlock; | |
bac8a4d5 | 1218 | |
43ecb9fe | 1219 | for (i = 0; i < bus->device_count; i++) { |
bac8a4d5 | 1220 | if (bus->device[i]) { |
27dd5548 | 1221 | if (PLINE(" busNo %d, device[%i]: 0x%p, chanptr=0x%p, swtch=0x%p\n", |
43ecb9fe | 1222 | bus->bus_no, i, bus->device[i], |
27dd5548 KC |
1223 | bus->device[i]->chanptr, |
1224 | bus->device[i]->swtch) < 0) | |
1225 | goto err_done_unlock; | |
1226 | ||
1227 | if (PLINE(" first_busy_cnt=%llu, moved_to_tail_cnt=%llu, last_on_list_cnt=%llu\n", | |
1228 | bus->device[i]->first_busy_cnt, | |
1229 | bus->device[i]->moved_to_tail_cnt, | |
1230 | bus->device[i]->last_on_list_cnt) < 0) | |
1231 | goto err_done_unlock; | |
bac8a4d5 KC |
1232 | } |
1233 | } | |
1234 | } | |
1235 | read_unlock(&BusListLock); | |
1236 | ||
27dd5548 | 1237 | if (PLINE("UisUtils_Registered_Services: %d\n", |
e9b9262a | 1238 | atomic_read(&uisutils_registered_services)) < 0) |
27dd5548 KC |
1239 | goto err_done; |
1240 | if (PLINE("cycles_before_wait %llu wait_cycles:%llu\n", | |
1241 | cycles_before_wait, wait_cycles) < 0) | |
1242 | goto err_done; | |
1243 | if (PLINE("tot_wakeup_cnt %llu:tot_wait_cnt %llu:tot_schedule_cnt %llu\n", | |
1244 | tot_wakeup_cnt, tot_wait_cnt, tot_schedule_cnt) < 0) | |
1245 | goto err_done; | |
1246 | if (PLINE("en_smart_wakeup %d\n", en_smart_wakeup) < 0) | |
1247 | goto err_done; | |
1248 | if (PLINE("tot_moved_to_tail_cnt %llu\n", tot_moved_to_tail_cnt) < 0) | |
1249 | goto err_done; | |
bac8a4d5 KC |
1250 | |
1251 | return tot; | |
bac8a4d5 | 1252 | |
27dd5548 KC |
1253 | err_done_unlock: |
1254 | read_unlock(&BusListLock); | |
1255 | err_done: | |
bac8a4d5 KC |
1256 | return -1; |
1257 | } | |
1258 | ||
1259 | static ssize_t | |
7ec96720 BR |
1260 | info_debugfs_read(struct file *file, char __user *buf, |
1261 | size_t len, loff_t *offset) | |
bac8a4d5 KC |
1262 | { |
1263 | char *temp; | |
1264 | int totalBytes = 0; | |
1265 | int remaining_bytes = PROC_READ_BUFFER_SIZE; | |
1266 | ||
1267 | /* *start = buf; */ | |
1268 | if (ProcReadBuffer == NULL) { | |
1269 | DBGINF("ProcReadBuffer == NULL; allocating buffer.\n."); | |
60140462 | 1270 | ProcReadBuffer = vmalloc(PROC_READ_BUFFER_SIZE); |
bac8a4d5 KC |
1271 | |
1272 | if (ProcReadBuffer == NULL) { | |
1273 | LOGERR("failed to allocate buffer to provide proc data.\n"); | |
1274 | return -ENOMEM; | |
1275 | } | |
1276 | } | |
1277 | ||
1278 | temp = ProcReadBuffer; | |
1279 | ||
1280 | if ((*offset == 0) || (!ProcReadBufferValid)) { | |
7ec96720 | 1281 | DBGINF("calling info_debugfs_read_helper.\n"); |
bac8a4d5 | 1282 | /* if the read fails, then -1 will be returned */ |
7ec96720 | 1283 | totalBytes = info_debugfs_read_helper(&temp, &remaining_bytes); |
bac8a4d5 KC |
1284 | ProcReadBufferValid = 1; |
1285 | } else | |
1286 | totalBytes = strlen(ProcReadBuffer); | |
1287 | ||
1288 | return simple_read_from_buffer(buf, len, offset, | |
1289 | ProcReadBuffer, totalBytes); | |
1290 | } | |
1291 | ||
bac8a4d5 | 1292 | static struct device_info * |
b3c55b13 | 1293 | find_dev(u32 busNo, u32 devNo) |
bac8a4d5 KC |
1294 | { |
1295 | struct bus_info *bus; | |
1296 | struct device_info *dev = NULL; | |
1297 | ||
1298 | read_lock(&BusListLock); | |
1299 | for (bus = BusListHead; bus; bus = bus->next) { | |
43ecb9fe | 1300 | if (bus->bus_no == busNo) { |
bac8a4d5 | 1301 | /* make sure the device number is valid */ |
43ecb9fe | 1302 | if (devNo >= bus->device_count) { |
bac8a4d5 KC |
1303 | LOGERR("%s bad busNo, devNo=%d,%d", |
1304 | __func__, | |
1305 | (int) (busNo), (int) (devNo)); | |
1306 | goto Away; | |
1307 | } | |
1308 | dev = bus->device[devNo]; | |
1309 | if (!dev) | |
1310 | LOGERR("%s bad busNo, devNo=%d,%d", | |
1311 | __func__, | |
1312 | (int) (busNo), (int) (devNo)); | |
1313 | goto Away; | |
1314 | } | |
1315 | } | |
1316 | Away: | |
1317 | read_unlock(&BusListLock); | |
1318 | return dev; | |
1319 | } | |
1320 | ||
1321 | /* This thread calls the "interrupt" function for each device that has | |
1322 | * enabled such using uislib_enable_channel_interrupts(). The "interrupt" | |
1323 | * function typically reads and processes the devices's channel input | |
1324 | * queue. This thread repeatedly does this, until the thread is told to stop | |
1325 | * (via uisthread_stop()). Sleeping rules: | |
1326 | * - If we have called the "interrupt" function for all devices, and all of | |
1327 | * them have reported "nothing processed" (returned 0), then we will go to | |
1328 | * sleep for a maximum of POLLJIFFIES_NORMAL jiffies. | |
1329 | * - If anyone calls uislib_force_channel_interrupt(), the above jiffy | |
1330 | * sleep will be interrupted, and we will resume calling the "interrupt" | |
1331 | * function for all devices. | |
1332 | * - The list of devices is dynamically re-ordered in order to | |
1333 | * attempt to preserve fairness. Whenever we spin thru the list of | |
1334 | * devices and call the dev->interrupt() function, if we find | |
1335 | * devices which report that there is still more work to do, the | |
1336 | * the first such device we find is moved to the end of the device | |
1337 | * list. This ensures that extremely busy devices don't starve out | |
1338 | * less-busy ones. | |
1339 | * | |
1340 | */ | |
1341 | static int | |
1342 | Process_Incoming(void *v) | |
1343 | { | |
1344 | unsigned long long cur_cycles, old_cycles, idle_cycles, delta_cycles; | |
1345 | struct list_head *new_tail = NULL; | |
1346 | int i; | |
ddc9f84b | 1347 | |
bac8a4d5 KC |
1348 | UIS_DAEMONIZE("dev_incoming"); |
1349 | for (i = 0; i < 16; i++) { | |
1350 | old_cycles = get_cycles(); | |
1351 | wait_event_timeout(Wakeup_Polling_Device_Channels, | |
1352 | 0, POLLJIFFIES_NORMAL); | |
1353 | cur_cycles = get_cycles(); | |
1354 | if (wait_cycles == 0) { | |
1355 | wait_cycles = (cur_cycles - old_cycles); | |
1356 | } else { | |
1357 | if (wait_cycles < (cur_cycles - old_cycles)) | |
1358 | wait_cycles = (cur_cycles - old_cycles); | |
1359 | } | |
1360 | } | |
1361 | LOGINF("wait_cycles=%llu", wait_cycles); | |
1362 | cycles_before_wait = wait_cycles; | |
1363 | idle_cycles = 0; | |
1364 | Go_Polling_Device_Channels = 0; | |
1365 | while (1) { | |
1366 | struct list_head *lelt, *tmp; | |
1367 | struct device_info *dev = NULL; | |
1368 | ||
1369 | /* poll each channel for input */ | |
f2170625 | 1370 | down(&Lock_Polling_Device_Channels); |
bac8a4d5 KC |
1371 | new_tail = NULL; |
1372 | list_for_each_safe(lelt, tmp, &List_Polling_Device_Channels) { | |
1373 | int rc = 0; | |
ddc9f84b | 1374 | |
bac8a4d5 KC |
1375 | dev = list_entry(lelt, struct device_info, |
1376 | list_polling_device_channels); | |
f2170625 | 1377 | down(&dev->interrupt_callback_lock); |
bac8a4d5 KC |
1378 | if (dev->interrupt) |
1379 | rc = dev->interrupt(dev->interrupt_context); | |
1380 | else | |
1381 | continue; | |
f2170625 | 1382 | up(&dev->interrupt_callback_lock); |
bac8a4d5 KC |
1383 | if (rc) { |
1384 | /* dev->interrupt returned, but there | |
1385 | * is still more work to do. | |
1386 | * Reschedule work to occur as soon as | |
1387 | * possible. */ | |
1388 | idle_cycles = 0; | |
1389 | if (new_tail == NULL) { | |
1390 | dev->first_busy_cnt++; | |
1391 | if (! | |
1392 | (list_is_last | |
1393 | (lelt, | |
1394 | &List_Polling_Device_Channels))) { | |
1395 | new_tail = lelt; | |
1396 | dev->moved_to_tail_cnt++; | |
1397 | } else | |
1398 | dev->last_on_list_cnt++; | |
1399 | } | |
1400 | ||
1401 | } | |
1402 | if (Incoming_ThreadInfo.should_stop) | |
1403 | break; | |
1404 | } | |
1405 | if (new_tail != NULL) { | |
1406 | tot_moved_to_tail_cnt++; | |
1407 | list_move_tail(new_tail, &List_Polling_Device_Channels); | |
1408 | } | |
f2170625 | 1409 | up(&Lock_Polling_Device_Channels); |
bac8a4d5 KC |
1410 | cur_cycles = get_cycles(); |
1411 | delta_cycles = cur_cycles - old_cycles; | |
1412 | old_cycles = cur_cycles; | |
1413 | ||
1414 | /* At this point, we have scanned thru all of the | |
1415 | * channels, and at least one of the following is true: | |
1416 | * - there is no input waiting on any of the channels | |
1417 | * - we have received a signal to stop this thread | |
1418 | */ | |
1419 | if (Incoming_ThreadInfo.should_stop) | |
1420 | break; | |
1421 | if (en_smart_wakeup == 0xFF) { | |
1422 | LOGINF("en_smart_wakeup set to 0xff, to force exiting process_incoming"); | |
1423 | break; | |
1424 | } | |
1425 | /* wait for POLLJIFFIES_NORMAL jiffies, or until | |
1426 | * someone wakes up Wakeup_Polling_Device_Channels, | |
1427 | * whichever comes first only do a wait when we have | |
1428 | * been idle for cycles_before_wait cycles. | |
1429 | */ | |
1430 | if (idle_cycles > cycles_before_wait) { | |
1431 | Go_Polling_Device_Channels = 0; | |
1432 | tot_wait_cnt++; | |
1433 | wait_event_timeout(Wakeup_Polling_Device_Channels, | |
1434 | Go_Polling_Device_Channels, | |
1435 | POLLJIFFIES_NORMAL); | |
1436 | Go_Polling_Device_Channels = 1; | |
1437 | } else { | |
1438 | tot_schedule_cnt++; | |
1439 | schedule(); | |
1440 | idle_cycles = idle_cycles + delta_cycles; | |
1441 | } | |
1442 | } | |
1443 | DBGINF("exiting.\n"); | |
1444 | complete_and_exit(&Incoming_ThreadInfo.has_stopped, 0); | |
1445 | } | |
1446 | ||
1447 | static BOOL | |
1448 | Initialize_incoming_thread(void) | |
1449 | { | |
1450 | if (Incoming_Thread_Started) | |
1451 | return TRUE; | |
1452 | if (!uisthread_start(&Incoming_ThreadInfo, | |
1453 | &Process_Incoming, NULL, "dev_incoming")) { | |
1454 | LOGERR("uisthread_start Initialize_incoming_thread ****FAILED"); | |
1455 | return FALSE; | |
1456 | } | |
1457 | Incoming_Thread_Started = TRUE; | |
1458 | return TRUE; | |
1459 | } | |
1460 | ||
1461 | /* Add a new device/channel to the list being processed by | |
1462 | * Process_Incoming(). | |
1463 | * <interrupt> - indicates the function to call periodically. | |
1464 | * <interrupt_context> - indicates the data to pass to the <interrupt> | |
1465 | * function. | |
1466 | */ | |
1467 | void | |
93d1304f | 1468 | uislib_enable_channel_interrupts(u32 bus_no, u32 dev_no, |
bac8a4d5 KC |
1469 | int (*interrupt)(void *), |
1470 | void *interrupt_context) | |
1471 | { | |
1472 | struct device_info *dev; | |
ddc9f84b | 1473 | |
93d1304f | 1474 | dev = find_dev(bus_no, dev_no); |
bac8a4d5 | 1475 | if (!dev) { |
93d1304f BR |
1476 | LOGERR("%s busNo=%d, devNo=%d", __func__, (int) (bus_no), |
1477 | (int) (dev_no)); | |
bac8a4d5 KC |
1478 | return; |
1479 | } | |
f2170625 | 1480 | down(&Lock_Polling_Device_Channels); |
bac8a4d5 KC |
1481 | Initialize_incoming_thread(); |
1482 | dev->interrupt = interrupt; | |
1483 | dev->interrupt_context = interrupt_context; | |
1484 | dev->polling = TRUE; | |
1485 | list_add_tail(&(dev->list_polling_device_channels), | |
1486 | &List_Polling_Device_Channels); | |
f2170625 | 1487 | up(&Lock_Polling_Device_Channels); |
bac8a4d5 KC |
1488 | } |
1489 | EXPORT_SYMBOL_GPL(uislib_enable_channel_interrupts); | |
1490 | ||
1491 | /* Remove a device/channel from the list being processed by | |
1492 | * Process_Incoming(). | |
1493 | */ | |
1494 | void | |
d0dd33f3 | 1495 | uislib_disable_channel_interrupts(u32 bus_no, u32 dev_no) |
bac8a4d5 KC |
1496 | { |
1497 | struct device_info *dev; | |
ddc9f84b | 1498 | |
d0dd33f3 | 1499 | dev = find_dev(bus_no, dev_no); |
bac8a4d5 | 1500 | if (!dev) { |
d0dd33f3 BR |
1501 | LOGERR("%s busNo=%d, devNo=%d", __func__, (int) (bus_no), |
1502 | (int) (dev_no)); | |
bac8a4d5 KC |
1503 | return; |
1504 | } | |
f2170625 | 1505 | down(&Lock_Polling_Device_Channels); |
bac8a4d5 KC |
1506 | list_del(&dev->list_polling_device_channels); |
1507 | dev->polling = FALSE; | |
1508 | dev->interrupt = NULL; | |
f2170625 | 1509 | up(&Lock_Polling_Device_Channels); |
bac8a4d5 KC |
1510 | } |
1511 | EXPORT_SYMBOL_GPL(uislib_disable_channel_interrupts); | |
1512 | ||
1513 | static void | |
1514 | do_wakeup_polling_device_channels(struct work_struct *dummy) | |
1515 | { | |
1516 | if (!Go_Polling_Device_Channels) { | |
1517 | Go_Polling_Device_Channels = 1; | |
1518 | wake_up(&Wakeup_Polling_Device_Channels); | |
1519 | } | |
1520 | } | |
1521 | ||
a8d7f21d KC |
1522 | static DECLARE_WORK(Work_wakeup_polling_device_channels, |
1523 | do_wakeup_polling_device_channels); | |
bac8a4d5 KC |
1524 | |
1525 | /* Call this function when you want to send a hint to Process_Incoming() that | |
1526 | * your device might have more requests. | |
1527 | */ | |
1528 | void | |
f7b33ff4 | 1529 | uislib_force_channel_interrupt(u32 bus_no, u32 dev_no) |
bac8a4d5 KC |
1530 | { |
1531 | if (en_smart_wakeup == 0) | |
1532 | return; | |
1533 | if (Go_Polling_Device_Channels) | |
1534 | return; | |
1535 | /* The point of using schedule_work() instead of just doing | |
1536 | * the work inline is to force a slight delay before waking up | |
1537 | * the Process_Incoming() thread. | |
1538 | */ | |
1539 | tot_wakeup_cnt++; | |
1540 | schedule_work(&Work_wakeup_polling_device_channels); | |
1541 | } | |
1542 | EXPORT_SYMBOL_GPL(uislib_force_channel_interrupt); | |
1543 | ||
1544 | /*****************************************************/ | |
1545 | /* Module Init & Exit functions */ | |
1546 | /*****************************************************/ | |
1547 | ||
1548 | static int __init | |
1549 | uislib_mod_init(void) | |
1550 | { | |
1551 | ||
fcd0157e KC |
1552 | if (!unisys_spar_platform) |
1553 | return -ENODEV; | |
1554 | ||
bac8a4d5 KC |
1555 | LOGINF("MONITORAPIS"); |
1556 | ||
1557 | LOGINF("sizeof(struct uiscmdrsp):%lu bytes\n", | |
1558 | (ulong) sizeof(struct uiscmdrsp)); | |
1559 | LOGINF("sizeof(struct phys_info):%lu\n", | |
1560 | (ulong) sizeof(struct phys_info)); | |
1561 | LOGINF("sizeof(uiscmdrsp_scsi):%lu\n", | |
1562 | (ulong) sizeof(struct uiscmdrsp_scsi)); | |
1563 | LOGINF("sizeof(uiscmdrsp_net):%lu\n", | |
1564 | (ulong) sizeof(struct uiscmdrsp_net)); | |
1565 | LOGINF("sizeof(CONTROLVM_MESSAGE):%lu bytes\n", | |
3ab47701 | 1566 | (ulong) sizeof(struct controlvm_message)); |
d19642f6 BR |
1567 | LOGINF("sizeof(struct spar_controlvm_channel_protocol):%lu bytes\n", |
1568 | (ulong) sizeof(struct spar_controlvm_channel_protocol)); | |
bac8a4d5 | 1569 | LOGINF("sizeof(CHANNEL_HEADER):%lu bytes\n", |
9fd1b95a | 1570 | (ulong) sizeof(struct channel_header)); |
bac8a4d5 KC |
1571 | LOGINF("sizeof(ULTRA_IO_CHANNEL_PROTOCOL):%lu bytes\n", |
1572 | (ulong) sizeof(ULTRA_IO_CHANNEL_PROTOCOL)); | |
1573 | LOGINF("SIZEOF_CMDRSP:%lu bytes\n", SIZEOF_CMDRSP); | |
1574 | LOGINF("SIZEOF_PROTOCOL:%lu bytes\n", SIZEOF_PROTOCOL); | |
1575 | ||
1576 | /* initialize global pointers to NULL */ | |
1577 | BusListHead = NULL; | |
1578 | BusListCount = MaxBusCount = 0; | |
1579 | rwlock_init(&BusListLock); | |
2df7cc62 | 1580 | virt_control_chan_func = NULL; |
bac8a4d5 KC |
1581 | |
1582 | /* Issue VMCALL_GET_CONTROLVM_ADDR to get CtrlChanPhysAddr and | |
1583 | * then map this physical address to a virtual address. */ | |
1584 | POSTCODE_LINUX_2(DRIVER_ENTRY_PC, POSTCODE_SEVERITY_INFO); | |
1585 | ||
28fa597f | 1586 | dir_debugfs = debugfs_create_dir(DIR_DEBUGFS_ENTRY, NULL); |
28fa597f | 1587 | if (dir_debugfs) { |
7ec96720 BR |
1588 | info_debugfs_entry = debugfs_create_file( |
1589 | INFO_DEBUGFS_ENTRY_FN, 0444, dir_debugfs, NULL, | |
1590 | &debugfs_info_fops); | |
1591 | ||
28fa597f BR |
1592 | platformnumber_debugfs_read = debugfs_create_u32( |
1593 | PLATFORMNUMBER_DEBUGFS_ENTRY_FN, 0444, dir_debugfs, | |
1594 | &PlatformNumber); | |
bac8a4d5 | 1595 | |
b913a2ef BR |
1596 | cycles_before_wait_debugfs_read = debugfs_create_u64( |
1597 | CYCLES_BEFORE_WAIT_DEBUGFS_ENTRY_FN, 0666, dir_debugfs, | |
1598 | &cycles_before_wait); | |
bac8a4d5 | 1599 | |
81d2d7de BR |
1600 | smart_wakeup_debugfs_entry = debugfs_create_bool( |
1601 | SMART_WAKEUP_DEBUGFS_ENTRY_FN, 0666, dir_debugfs, | |
1602 | &en_smart_wakeup); | |
1603 | } | |
bac8a4d5 | 1604 | |
bac8a4d5 KC |
1605 | POSTCODE_LINUX_3(DRIVER_EXIT_PC, 0, POSTCODE_SEVERITY_INFO); |
1606 | return 0; | |
1607 | } | |
1608 | ||
1609 | static void __exit | |
1610 | uislib_mod_exit(void) | |
1611 | { | |
bac8a4d5 | 1612 | if (ProcReadBuffer) { |
60140462 | 1613 | vfree(ProcReadBuffer); |
bac8a4d5 KC |
1614 | ProcReadBuffer = NULL; |
1615 | } | |
1616 | ||
7ec96720 | 1617 | debugfs_remove(info_debugfs_entry); |
81d2d7de | 1618 | debugfs_remove(smart_wakeup_debugfs_entry); |
b913a2ef | 1619 | debugfs_remove(cycles_before_wait_debugfs_read); |
28fa597f BR |
1620 | debugfs_remove(platformnumber_debugfs_read); |
1621 | debugfs_remove(dir_debugfs); | |
1622 | ||
bac8a4d5 | 1623 | DBGINF("goodbye.\n"); |
bac8a4d5 KC |
1624 | } |
1625 | ||
1626 | module_init(uislib_mod_init); | |
1627 | module_exit(uislib_mod_exit); | |
1628 | ||
bac8a4d5 KC |
1629 | MODULE_LICENSE("GPL"); |
1630 | MODULE_AUTHOR("Usha Srinivasan"); | |
1631 | MODULE_ALIAS("uislib"); | |
1632 | /* this is extracted during depmod and kept in modules.dep */ |