Commit | Line | Data |
---|---|---|
bac8a4d5 KC |
1 | /* uislib.c |
2 | * | |
3 | * Copyright � 2010 - 2013 UNISYS CORPORATION | |
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> | |
26 | ||
27 | #include "commontypes.h" | |
28 | ||
29 | #include <linux/version.h> | |
30 | #include "uniklog.h" | |
31 | #include "diagnostics/appos_subsystems.h" | |
32 | #include "uisutils.h" | |
33 | #include "vbuschannel.h" | |
34 | ||
35 | #include <linux/proc_fs.h> | |
36 | #include <linux/uaccess.h> /* for copy_from_user */ | |
37 | #include <linux/ctype.h> /* for toupper */ | |
38 | #include <linux/list.h> | |
39 | ||
40 | #include "sparstop.h" | |
41 | #include "visorchipset.h" | |
42 | #include "chanstub.h" | |
43 | #include "version.h" | |
44 | #include "guestlinuxdebug.h" | |
45 | ||
46 | #define SET_PROC_OWNER(x, y) | |
47 | ||
48 | #define UISLIB_TEST_PROC | |
49 | #define POLLJIFFIES_NORMAL 1 | |
50 | /* Choose whether or not you want to wakeup the request-polling thread | |
51 | * after an IO termination: | |
52 | * this is shorter than using __FILE__ (full path name) in | |
53 | * debug/info/error messages | |
54 | */ | |
55 | #define CURRENT_FILE_PC UISLIB_PC_uislib_c | |
56 | #define __MYFILE__ "uislib.c" | |
57 | ||
58 | /* global function pointers that act as callback functions into virtpcimod */ | |
59 | int (*VirtControlChanFunc)(struct guest_msgs *); | |
60 | ||
61 | static int ProcReadBufferValid; | |
62 | static char *ProcReadBuffer; /* Note this MUST be global, | |
63 | * because the contents must */ | |
64 | static unsigned int chipset_inited; | |
a8d7f21d | 65 | |
bac8a4d5 KC |
66 | #define WAIT_ON_CALLBACK(handle) \ |
67 | do { \ | |
68 | if (handle) \ | |
69 | break; \ | |
70 | UIS_THREAD_WAIT; \ | |
71 | } while (1) | |
72 | ||
73 | static struct bus_info *BusListHead; | |
74 | static rwlock_t BusListLock; | |
75 | static int BusListCount; /* number of buses in the list */ | |
76 | static int MaxBusCount; /* maximum number of buses expected */ | |
77 | static U64 PhysicalDataChan; | |
78 | static int PlatformNumber; | |
79 | ||
bac8a4d5 KC |
80 | static struct uisthread_info Incoming_ThreadInfo; |
81 | static BOOL Incoming_Thread_Started = FALSE; | |
a8d7f21d KC |
82 | static LIST_HEAD(List_Polling_Device_Channels); |
83 | static unsigned long long tot_moved_to_tail_cnt; | |
84 | static unsigned long long tot_wait_cnt; | |
85 | static unsigned long long tot_wakeup_cnt; | |
86 | static unsigned long long tot_schedule_cnt; | |
87 | static int en_smart_wakeup = 1; | |
bac8a4d5 | 88 | static DEFINE_SEMAPHORE(Lock_Polling_Device_Channels); /* unlocked */ |
a8d7f21d | 89 | static DECLARE_WAIT_QUEUE_HEAD(Wakeup_Polling_Device_Channels); |
bac8a4d5 KC |
90 | static int Go_Polling_Device_Channels; |
91 | ||
92 | static struct proc_dir_entry *uislib_proc_dir; | |
93 | static struct proc_dir_entry *uislib_proc_vbus_dir; | |
94 | static struct proc_dir_entry *vnic_proc_entry; /* Used to be "datachan" */ | |
95 | static struct proc_dir_entry *ctrlchan_proc_entry; | |
96 | static struct proc_dir_entry *pmem_proc_entry; | |
97 | static struct proc_dir_entry *info_proc_entry; | |
98 | static struct proc_dir_entry *switch_proc_entry; | |
99 | static struct proc_dir_entry *extport_proc_entry; | |
100 | static struct proc_dir_entry *platformnumber_proc_entry; | |
101 | static struct proc_dir_entry *bus_proc_entry; | |
102 | static struct proc_dir_entry *dev_proc_entry; | |
103 | static struct proc_dir_entry *chipset_proc_entry; | |
104 | static struct proc_dir_entry *cycles_before_wait_proc_entry; | |
105 | static struct proc_dir_entry *reset_counts_proc_entry; | |
106 | static struct proc_dir_entry *smart_wakeup_proc_entry; | |
107 | static struct proc_dir_entry *disable_proc_entry; | |
108 | ||
109 | #define DIR_PROC_ENTRY "uislib" | |
110 | #define DIR_VBUS_PROC_ENTRY "vbus" | |
111 | #define VNIC_PROC_ENTRY_FN "vnic" /* Used to be "datachan" */ | |
112 | #define CTRLCHAN_PROC_ENTRY_FN "ctrlchan" | |
113 | #define PMEM_PROC_ENTRY_FN "phys_to_virt" | |
114 | #define INFO_PROC_ENTRY_FN "info" | |
115 | #define SWITCH_PROC_ENTRY_FN "switch" | |
116 | #define SWITCH_COUNT_PROC_ENTRY_FN "switch_count" | |
117 | #define EXTPORT_PROC_ENTRY_FN "extport" | |
118 | #define PLATFORMNUMBER_PROC_ENTRY_FN "platform" | |
119 | #define BUS_PROC_ENTRY_FN "bus" | |
120 | #define DEV_PROC_ENTRY_FN "device" | |
121 | #define CHIPSET_PROC_ENTRY_FN "chipset" | |
122 | #define CYCLES_BEFORE_WAIT_PROC_ENTRY_FN "cycles_before_wait" | |
123 | #define RESET_COUNTS_PROC_ENTRY_FN "reset_counts" | |
124 | #define SMART_WAKEUP_PROC_ENTRY_FN "smart_wakeup" | |
125 | #define CALLHOME_PROC_ENTRY_FN "callhome" | |
126 | #define CALLHOME_THROTTLED_PROC_ENTRY_FN "callhome_throttled" | |
127 | #define DISABLE_PROC_ENTRY_FN "switch_state" | |
128 | #ifdef UISLIB_TEST_PROC | |
129 | static struct proc_dir_entry *test_proc_entry; | |
130 | #define TEST_PROC_ENTRY_FN "test" | |
131 | #endif | |
a8d7f21d | 132 | static unsigned long long cycles_before_wait, wait_cycles; |
bac8a4d5 KC |
133 | |
134 | /*****************************************************/ | |
135 | /* local functions */ | |
136 | /*****************************************************/ | |
137 | ||
138 | static int proc_info_vbus_show(struct seq_file *m, void *v); | |
139 | static int | |
140 | proc_info_vbus_open(struct inode *inode, struct file *filp) | |
141 | { | |
142 | /* proc_info_vbus_show will grab this from seq_file.private: */ | |
143 | struct bus_info *bus = PDE_DATA(inode); | |
144 | return single_open(filp, proc_info_vbus_show, bus); | |
145 | } | |
146 | ||
147 | static const struct file_operations proc_info_vbus_fops = { | |
148 | .open = proc_info_vbus_open, | |
149 | .read = seq_read, | |
150 | .llseek = seq_lseek, | |
151 | .release = single_release, | |
152 | }; | |
153 | ||
154 | static ssize_t uislib_proc_read_writeonly(struct file *file, | |
155 | char __user *buffer, | |
156 | size_t count, loff_t *ppos); | |
157 | ||
158 | static ssize_t vnic_proc_write(struct file *file, const char __user *buffer, | |
159 | size_t count, loff_t *ppos); | |
160 | ||
161 | static const struct file_operations proc_vnic_fops = { | |
162 | .read = uislib_proc_read_writeonly, | |
163 | .write = vnic_proc_write, | |
164 | }; | |
165 | ||
166 | static ssize_t chipset_proc_write(struct file *file, const char __user *buffer, | |
167 | size_t count, loff_t *ppos); | |
168 | ||
169 | static const struct file_operations proc_chipset_fops = { | |
170 | .read = uislib_proc_read_writeonly, | |
171 | .write = chipset_proc_write, | |
172 | }; | |
173 | ||
174 | static ssize_t info_proc_read(struct file *file, char __user *buf, | |
175 | size_t len, loff_t *offset); | |
176 | static const struct file_operations proc_info_fops = { | |
177 | .read = info_proc_read, | |
178 | }; | |
179 | ||
180 | static ssize_t platformnumber_proc_read(struct file *file, char __user *buf, | |
181 | size_t len, loff_t *offset); | |
182 | static const struct file_operations proc_platformnumber_fops = { | |
183 | .read = platformnumber_proc_read, | |
184 | }; | |
185 | ||
186 | static ssize_t cycles_before_wait_proc_write(struct file *file, | |
187 | const char __user *buffer, | |
188 | size_t count, loff_t *ppos); | |
189 | static const struct file_operations proc_cycles_before_wait_fops = { | |
190 | .read = uislib_proc_read_writeonly, | |
191 | .write = cycles_before_wait_proc_write, | |
192 | }; | |
193 | ||
194 | static ssize_t reset_counts_proc_write(struct file *file, | |
195 | const char __user *buffer, | |
196 | size_t count, loff_t *ppos); | |
197 | static const struct file_operations proc_reset_counts_fops = { | |
198 | .read = uislib_proc_read_writeonly, | |
199 | .write = reset_counts_proc_write, | |
200 | }; | |
201 | ||
202 | static ssize_t smart_wakeup_proc_write(struct file *file, | |
203 | const char __user *buffer, | |
204 | size_t count, loff_t *ppos); | |
205 | static const struct file_operations proc_smart_wakeup_fops = { | |
206 | .read = uislib_proc_read_writeonly, | |
207 | .write = smart_wakeup_proc_write, | |
208 | }; | |
209 | ||
210 | static ssize_t test_proc_write(struct file *file, | |
211 | const char __user *buffer, | |
212 | size_t count, loff_t *ppos); | |
213 | static const struct file_operations proc_test_fops = { | |
214 | .read = uislib_proc_read_writeonly, | |
215 | .write = test_proc_write, | |
216 | }; | |
217 | ||
218 | static ssize_t bus_proc_write(struct file *file, | |
219 | const char __user *buffer, | |
220 | size_t count, loff_t *ppos); | |
221 | static const struct file_operations proc_bus_fops = { | |
222 | .read = uislib_proc_read_writeonly, | |
223 | .write = bus_proc_write, | |
224 | }; | |
225 | ||
226 | static ssize_t dev_proc_write(struct file *file, | |
227 | const char __user *buffer, | |
228 | size_t count, loff_t *ppos); | |
229 | static const struct file_operations proc_dev_fops = { | |
230 | .read = uislib_proc_read_writeonly, | |
231 | .write = dev_proc_write, | |
232 | }; | |
233 | ||
234 | static void | |
235 | init_msg_header(CONTROLVM_MESSAGE *msg, U32 id, uint rsp, uint svr) | |
236 | { | |
237 | memset(msg, 0, sizeof(CONTROLVM_MESSAGE)); | |
238 | msg->hdr.Id = id; | |
239 | msg->hdr.Flags.responseExpected = rsp; | |
240 | msg->hdr.Flags.server = svr; | |
241 | } | |
242 | ||
243 | static void | |
244 | create_bus_proc_entries(struct bus_info *bus) | |
245 | { | |
246 | bus->proc_dir = proc_mkdir(bus->name, uislib_proc_vbus_dir); | |
247 | if (!bus->proc_dir) { | |
248 | LOGERR("failed to create /proc/uislib/vbus/%s directory", | |
249 | bus->name); | |
250 | return; | |
251 | } | |
252 | bus->proc_info = proc_create_data("info", 0, bus->proc_dir, | |
253 | &proc_info_vbus_fops, bus); | |
254 | if (!bus->proc_info) { | |
255 | LOGERR("failed to create /proc/uislib/vbus/%s/info", bus->name); | |
256 | remove_proc_entry(bus->name, uislib_proc_vbus_dir); | |
257 | bus->proc_dir = NULL; | |
258 | return; | |
259 | } | |
260 | SET_PROC_OWNER(bus->proc_info, THIS_MODULE); | |
261 | ||
262 | } | |
263 | ||
a8d7f21d | 264 | static __iomem void * |
bac8a4d5 KC |
265 | init_vbus_channel(U64 channelAddr, U32 channelBytes, int isServer) |
266 | { | |
267 | void *rc = NULL; | |
a8d7f21d | 268 | void __iomem *pChan = uislib_ioremap_cache(channelAddr, channelBytes); |
bac8a4d5 KC |
269 | if (!pChan) { |
270 | LOGERR("CONTROLVM_BUS_CREATE error: ioremap_cache of channelAddr:%Lx for channelBytes:%llu failed", | |
271 | (unsigned long long) channelAddr, | |
272 | (unsigned long long) channelBytes); | |
d9355f89 KC |
273 | rc = NULL; |
274 | goto Away; | |
bac8a4d5 KC |
275 | } |
276 | if (isServer) { | |
a8d7f21d | 277 | memset_io(pChan, 0, channelBytes); |
bac8a4d5 KC |
278 | if (!ULTRA_VBUS_CHANNEL_OK_SERVER(channelBytes, NULL)) { |
279 | ERRDRV("%s channel cannot be used", __func__); | |
280 | uislib_iounmap(pChan); | |
d9355f89 KC |
281 | rc = NULL; |
282 | goto Away; | |
bac8a4d5 KC |
283 | } |
284 | ULTRA_VBUS_init_channel(pChan, channelBytes); | |
285 | } else { | |
286 | if (!ULTRA_VBUS_CHANNEL_OK_CLIENT(pChan, NULL)) { | |
287 | ERRDRV("%s channel cannot be used", __func__); | |
288 | uislib_iounmap(pChan); | |
d9355f89 KC |
289 | rc = NULL; |
290 | goto Away; | |
bac8a4d5 KC |
291 | } |
292 | } | |
d9355f89 | 293 | rc = pChan; |
bac8a4d5 KC |
294 | Away: |
295 | return rc; | |
296 | } | |
297 | ||
298 | static int | |
299 | create_bus(CONTROLVM_MESSAGE *msg, char *buf) | |
300 | { | |
301 | U32 busNo, deviceCount; | |
302 | struct bus_info *tmp, *bus; | |
303 | size_t size; | |
304 | ||
305 | if (MaxBusCount == BusListCount) { | |
306 | LOGERR("CONTROLVM_BUS_CREATE Failed: max buses:%d already created\n", | |
307 | MaxBusCount); | |
308 | POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, MaxBusCount, | |
309 | POSTCODE_SEVERITY_ERR); | |
310 | return CONTROLVM_RESP_ERROR_MAX_BUSES; | |
311 | } | |
312 | ||
313 | busNo = msg->cmd.createBus.busNo; | |
314 | deviceCount = msg->cmd.createBus.deviceCount; | |
315 | ||
316 | POSTCODE_LINUX_4(BUS_CREATE_ENTRY_PC, busNo, deviceCount, | |
317 | POSTCODE_SEVERITY_INFO); | |
318 | ||
319 | size = | |
320 | sizeof(struct bus_info) + | |
321 | (deviceCount * sizeof(struct device_info *)); | |
60140462 | 322 | bus = kzalloc(size, GFP_ATOMIC); |
bac8a4d5 KC |
323 | if (!bus) { |
324 | LOGERR("CONTROLVM_BUS_CREATE Failed: kmalloc for bus failed.\n"); | |
325 | POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, busNo, | |
326 | POSTCODE_SEVERITY_ERR); | |
327 | return CONTROLVM_RESP_ERROR_KMALLOC_FAILED; | |
328 | } | |
329 | ||
bac8a4d5 KC |
330 | /* Currently by default, the bus Number is the GuestHandle. |
331 | * Configure Bus message can override this. | |
332 | */ | |
333 | if (msg->hdr.Flags.testMessage) { | |
334 | /* This implies we're the IOVM so set guest handle to 0... */ | |
335 | bus->guestHandle = 0; | |
336 | bus->busNo = busNo; | |
337 | bus->localVnic = 1; | |
338 | } else | |
339 | bus->busNo = bus->guestHandle = busNo; | |
340 | sprintf(bus->name, "%d", (int) bus->busNo); | |
341 | bus->deviceCount = deviceCount; | |
342 | bus->device = | |
343 | (struct device_info **) ((char *) bus + sizeof(struct bus_info)); | |
344 | bus->busInstGuid = msg->cmd.createBus.busInstGuid; | |
345 | bus->busChannelBytes = 0; | |
346 | bus->pBusChannel = NULL; | |
347 | ||
348 | /* add bus to our bus list - but check for duplicates first */ | |
349 | read_lock(&BusListLock); | |
350 | for (tmp = BusListHead; tmp; tmp = tmp->next) { | |
351 | if (tmp->busNo == bus->busNo) | |
352 | break; | |
353 | } | |
354 | read_unlock(&BusListLock); | |
355 | if (tmp) { | |
356 | /* found a bus already in the list with same busNo - | |
357 | * reject add | |
358 | */ | |
359 | LOGERR("CONTROLVM_BUS_CREATE Failed: bus %d already exists.\n", | |
360 | bus->busNo); | |
361 | POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->busNo, | |
362 | POSTCODE_SEVERITY_ERR); | |
60140462 | 363 | kfree(bus); |
bac8a4d5 KC |
364 | return CONTROLVM_RESP_ERROR_ALREADY_DONE; |
365 | } | |
366 | if ((msg->cmd.createBus.channelAddr != 0) | |
367 | && (msg->cmd.createBus.channelBytes != 0)) { | |
368 | bus->busChannelBytes = msg->cmd.createBus.channelBytes; | |
369 | bus->pBusChannel = | |
370 | init_vbus_channel(msg->cmd.createBus.channelAddr, | |
371 | msg->cmd.createBus.channelBytes, | |
372 | msg->hdr.Flags.server); | |
373 | } | |
374 | /* the msg is bound for virtpci; send guest_msgs struct to callback */ | |
375 | if (!msg->hdr.Flags.server) { | |
376 | struct guest_msgs cmd; | |
377 | cmd.msgtype = GUEST_ADD_VBUS; | |
378 | cmd.add_vbus.busNo = busNo; | |
379 | cmd.add_vbus.chanptr = bus->pBusChannel; | |
380 | cmd.add_vbus.deviceCount = deviceCount; | |
381 | cmd.add_vbus.busTypeGuid = msg->cmd.createBus.busDataTypeGuid; | |
382 | cmd.add_vbus.busInstGuid = msg->cmd.createBus.busInstGuid; | |
383 | if (!VirtControlChanFunc) { | |
bac8a4d5 KC |
384 | LOGERR("CONTROLVM_BUS_CREATE Failed: virtpci callback not registered."); |
385 | POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->busNo, | |
386 | POSTCODE_SEVERITY_ERR); | |
d21bb450 | 387 | kfree(bus); |
bac8a4d5 KC |
388 | return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE; |
389 | } | |
390 | if (!VirtControlChanFunc(&cmd)) { | |
bac8a4d5 KC |
391 | LOGERR("CONTROLVM_BUS_CREATE Failed: virtpci GUEST_ADD_VBUS returned error."); |
392 | POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->busNo, | |
393 | POSTCODE_SEVERITY_ERR); | |
d21bb450 | 394 | kfree(bus); |
bac8a4d5 KC |
395 | return |
396 | CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR; | |
397 | } | |
398 | } | |
399 | create_bus_proc_entries(bus); | |
400 | ||
401 | /* add bus at the head of our list */ | |
402 | write_lock(&BusListLock); | |
403 | if (!BusListHead) | |
404 | BusListHead = bus; | |
405 | else { | |
406 | bus->next = BusListHead; | |
407 | BusListHead = bus; | |
408 | } | |
409 | BusListCount++; | |
410 | write_unlock(&BusListLock); | |
411 | ||
412 | POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, bus->busNo, | |
413 | POSTCODE_SEVERITY_INFO); | |
414 | return CONTROLVM_RESP_SUCCESS; | |
415 | } | |
416 | ||
417 | static int | |
418 | destroy_bus(CONTROLVM_MESSAGE *msg, char *buf) | |
419 | { | |
420 | int i; | |
421 | struct bus_info *bus, *prev = NULL; | |
422 | U32 busNo; | |
423 | ||
424 | busNo = msg->cmd.destroyBus.busNo; | |
425 | ||
426 | /* find and delete the bus */ | |
427 | read_lock(&BusListLock); | |
428 | for (bus = BusListHead; bus; prev = bus, bus = bus->next) { | |
429 | if (bus->busNo == busNo) { | |
430 | /* found the bus - ensure that all device | |
431 | * slots are NULL | |
432 | */ | |
433 | for (i = 0; i < bus->deviceCount; i++) { | |
434 | if (bus->device[i] != NULL) { | |
435 | LOGERR("CONTROLVM_BUS_DESTROY Failed: device %i attached to bus %d.", | |
436 | i, busNo); | |
437 | read_unlock(&BusListLock); | |
438 | return CONTROLVM_RESP_ERROR_BUS_DEVICE_ATTACHED; | |
439 | } | |
440 | } | |
441 | read_unlock(&BusListLock); | |
442 | /* the msg is bound for virtpci; send | |
443 | * guest_msgs struct to callback | |
444 | */ | |
445 | if (!msg->hdr.Flags.server) { | |
446 | struct guest_msgs cmd; | |
447 | cmd.msgtype = GUEST_DEL_VBUS; | |
448 | cmd.del_vbus.busNo = busNo; | |
449 | if (!VirtControlChanFunc) { | |
450 | LOGERR("CONTROLVM_BUS_DESTROY Failed: virtpci callback not registered."); | |
451 | return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE; | |
452 | } | |
453 | if (!VirtControlChanFunc(&cmd)) { | |
454 | LOGERR("CONTROLVM_BUS_DESTROY Failed: virtpci GUEST_DEL_VBUS returned error."); | |
455 | return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR; | |
456 | } | |
457 | } | |
458 | /* remove the bus from the list */ | |
459 | write_lock(&BusListLock); | |
460 | if (prev) /* not at head */ | |
461 | prev->next = bus->next; | |
462 | else | |
463 | BusListHead = bus->next; | |
464 | BusListCount--; | |
465 | write_unlock(&BusListLock); | |
466 | break; | |
467 | } | |
468 | } | |
469 | ||
470 | if (!bus) { | |
471 | LOGERR("CONTROLVM_BUS_DESTROY Failed: failed to find bus %d.\n", | |
472 | busNo); | |
473 | read_unlock(&BusListLock); | |
474 | return CONTROLVM_RESP_ERROR_ALREADY_DONE; | |
475 | } | |
476 | if (bus->proc_info) { | |
477 | remove_proc_entry("info", bus->proc_dir); | |
478 | bus->proc_info = NULL; | |
479 | } | |
480 | if (bus->proc_dir) { | |
481 | remove_proc_entry(bus->name, uislib_proc_vbus_dir); | |
482 | bus->proc_dir = NULL; | |
483 | } | |
484 | if (bus->pBusChannel) { | |
485 | uislib_iounmap(bus->pBusChannel); | |
486 | bus->pBusChannel = NULL; | |
487 | } | |
488 | ||
60140462 | 489 | kfree(bus); |
bac8a4d5 KC |
490 | return CONTROLVM_RESP_SUCCESS; |
491 | } | |
492 | ||
493 | static int | |
494 | create_device(CONTROLVM_MESSAGE *msg, char *buf) | |
495 | { | |
496 | struct device_info *dev; | |
497 | struct bus_info *bus; | |
498 | U32 busNo, devNo; | |
499 | int result = CONTROLVM_RESP_SUCCESS; | |
500 | U64 minSize = MIN_IO_CHANNEL_SIZE; | |
501 | ReqHandlerInfo_t *pReqHandler; | |
502 | ||
503 | busNo = msg->cmd.createDevice.busNo; | |
504 | devNo = msg->cmd.createDevice.devNo; | |
505 | ||
506 | POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, devNo, busNo, | |
507 | POSTCODE_SEVERITY_INFO); | |
508 | ||
60140462 | 509 | dev = kzalloc(sizeof(struct device_info), GFP_ATOMIC); |
bac8a4d5 KC |
510 | if (!dev) { |
511 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: kmalloc for dev failed.\n"); | |
512 | POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo, | |
513 | POSTCODE_SEVERITY_ERR); | |
514 | return CONTROLVM_RESP_ERROR_KMALLOC_FAILED; | |
515 | } | |
516 | ||
bac8a4d5 KC |
517 | dev->channelTypeGuid = msg->cmd.createDevice.dataTypeGuid; |
518 | dev->intr = msg->cmd.createDevice.intr; | |
519 | dev->channelAddr = msg->cmd.createDevice.channelAddr; | |
520 | dev->busNo = busNo; | |
521 | dev->devNo = devNo; | |
522 | sema_init(&dev->interrupt_callback_lock, 1); /* unlocked */ | |
523 | sprintf(dev->devid, "vbus%u:dev%u", (unsigned) busNo, (unsigned) devNo); | |
524 | /* map the channel memory for the device. */ | |
525 | if (msg->hdr.Flags.testMessage) | |
a8d7f21d | 526 | dev->chanptr = (void __iomem *)__va(dev->channelAddr); |
bac8a4d5 KC |
527 | else { |
528 | pReqHandler = ReqHandlerFind(dev->channelTypeGuid); | |
529 | if (pReqHandler) | |
530 | /* generic service handler registered for this | |
531 | * channel | |
532 | */ | |
533 | minSize = pReqHandler->min_channel_bytes; | |
534 | if (minSize > msg->cmd.createDevice.channelBytes) { | |
535 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: channel size is too small, channel size:0x%lx, required size:0x%lx", | |
536 | (ulong) msg->cmd.createDevice.channelBytes, | |
537 | (ulong) minSize); | |
538 | POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo, | |
539 | POSTCODE_SEVERITY_ERR); | |
540 | result = CONTROLVM_RESP_ERROR_CHANNEL_SIZE_TOO_SMALL; | |
541 | goto Away; | |
542 | } | |
543 | dev->chanptr = | |
544 | uislib_ioremap_cache(dev->channelAddr, | |
545 | msg->cmd.createDevice.channelBytes); | |
546 | if (!dev->chanptr) { | |
547 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: ioremap_cache of channelAddr:%Lx for channelBytes:%llu failed", | |
548 | dev->channelAddr, | |
549 | msg->cmd.createDevice.channelBytes); | |
550 | result = CONTROLVM_RESP_ERROR_IOREMAP_FAILED; | |
551 | POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo, | |
552 | POSTCODE_SEVERITY_ERR); | |
553 | goto Away; | |
554 | } | |
555 | } | |
556 | dev->devInstGuid = msg->cmd.createDevice.devInstGuid; | |
557 | dev->channelBytes = msg->cmd.createDevice.channelBytes; | |
558 | ||
559 | read_lock(&BusListLock); | |
560 | for (bus = BusListHead; bus; bus = bus->next) { | |
561 | if (bus->busNo == busNo) { | |
562 | /* make sure the device number is valid */ | |
563 | if (devNo >= bus->deviceCount) { | |
564 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: device (%d) >= deviceCount (%d).", | |
565 | devNo, bus->deviceCount); | |
566 | result = CONTROLVM_RESP_ERROR_MAX_DEVICES; | |
567 | POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, | |
568 | devNo, busNo, | |
569 | POSTCODE_SEVERITY_ERR); | |
570 | read_unlock(&BusListLock); | |
571 | goto Away; | |
572 | } | |
573 | /* make sure this device is not already set */ | |
574 | if (bus->device[devNo]) { | |
575 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: device %d is already exists.", | |
576 | devNo); | |
577 | POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, | |
578 | devNo, busNo, | |
579 | POSTCODE_SEVERITY_ERR); | |
580 | result = CONTROLVM_RESP_ERROR_ALREADY_DONE; | |
581 | read_unlock(&BusListLock); | |
582 | goto Away; | |
583 | } | |
584 | read_unlock(&BusListLock); | |
585 | /* the msg is bound for virtpci; send | |
586 | * guest_msgs struct to callback | |
587 | */ | |
588 | if (!msg->hdr.Flags.server) { | |
589 | struct guest_msgs cmd; | |
590 | if (!memcmp | |
591 | (&dev->channelTypeGuid, | |
592 | &UltraVhbaChannelProtocolGuid, | |
593 | sizeof(GUID))) { | |
594 | WAIT_FOR_VALID_GUID(((CHANNEL_HEADER | |
a8d7f21d | 595 | __iomem *) (dev-> |
bac8a4d5 KC |
596 | chanptr))-> |
597 | Type); | |
598 | if (!ULTRA_VHBA_CHANNEL_OK_CLIENT | |
599 | (dev->chanptr, NULL)) { | |
600 | LOGERR("CONTROLVM_DEVICE_CREATE Failed:[CLIENT]VHBA dev %d chan invalid.", | |
601 | devNo); | |
602 | POSTCODE_LINUX_4 | |
603 | (DEVICE_CREATE_FAILURE_PC, | |
604 | devNo, busNo, | |
605 | POSTCODE_SEVERITY_ERR); | |
606 | result = CONTROLVM_RESP_ERROR_CHANNEL_INVALID; | |
607 | goto Away; | |
608 | } | |
609 | cmd.msgtype = GUEST_ADD_VHBA; | |
610 | cmd.add_vhba.chanptr = dev->chanptr; | |
611 | cmd.add_vhba.busNo = busNo; | |
612 | cmd.add_vhba.deviceNo = devNo; | |
613 | cmd.add_vhba.devInstGuid = | |
614 | dev->devInstGuid; | |
615 | cmd.add_vhba.intr = dev->intr; | |
616 | } else | |
617 | if (!memcmp | |
618 | (&dev->channelTypeGuid, | |
619 | &UltraVnicChannelProtocolGuid, | |
620 | sizeof(GUID))) { | |
621 | WAIT_FOR_VALID_GUID(((CHANNEL_HEADER | |
a8d7f21d | 622 | __iomem *) (dev-> |
bac8a4d5 KC |
623 | chanptr))-> |
624 | Type); | |
625 | if (!ULTRA_VNIC_CHANNEL_OK_CLIENT | |
626 | (dev->chanptr, NULL)) { | |
627 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: VNIC[CLIENT] dev %d chan invalid.", | |
628 | devNo); | |
629 | POSTCODE_LINUX_4 | |
630 | (DEVICE_CREATE_FAILURE_PC, | |
631 | devNo, busNo, | |
632 | POSTCODE_SEVERITY_ERR); | |
633 | result = CONTROLVM_RESP_ERROR_CHANNEL_INVALID; | |
634 | goto Away; | |
635 | } | |
636 | cmd.msgtype = GUEST_ADD_VNIC; | |
637 | cmd.add_vnic.chanptr = dev->chanptr; | |
638 | cmd.add_vnic.busNo = busNo; | |
639 | cmd.add_vnic.deviceNo = devNo; | |
640 | cmd.add_vnic.devInstGuid = | |
641 | dev->devInstGuid; | |
642 | cmd.add_vhba.intr = dev->intr; | |
643 | } else { | |
644 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: unknown channelTypeGuid.\n"); | |
645 | POSTCODE_LINUX_4 | |
646 | (DEVICE_CREATE_FAILURE_PC, devNo, | |
647 | busNo, POSTCODE_SEVERITY_ERR); | |
648 | result = CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN; | |
649 | goto Away; | |
650 | } | |
651 | ||
652 | if (!VirtControlChanFunc) { | |
653 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: virtpci callback not registered."); | |
654 | POSTCODE_LINUX_4 | |
655 | (DEVICE_CREATE_FAILURE_PC, devNo, | |
656 | busNo, POSTCODE_SEVERITY_ERR); | |
657 | result = CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE; | |
658 | goto Away; | |
659 | } | |
660 | ||
661 | if (!VirtControlChanFunc(&cmd)) { | |
662 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: virtpci GUEST_ADD_[VHBA||VNIC] returned error."); | |
663 | POSTCODE_LINUX_4 | |
664 | (DEVICE_CREATE_FAILURE_PC, devNo, | |
665 | busNo, POSTCODE_SEVERITY_ERR); | |
666 | result = CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR; | |
667 | goto Away; | |
668 | } | |
669 | } | |
670 | bus->device[devNo] = dev; | |
671 | POSTCODE_LINUX_4(DEVICE_CREATE_SUCCESS_PC, devNo, busNo, | |
672 | POSTCODE_SEVERITY_INFO); | |
673 | return CONTROLVM_RESP_SUCCESS; | |
674 | } | |
675 | } | |
676 | read_unlock(&BusListLock); | |
677 | ||
678 | LOGERR("CONTROLVM_DEVICE_CREATE Failed: failed to find bus %d.", busNo); | |
679 | POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo, | |
680 | POSTCODE_SEVERITY_ERR); | |
681 | result = CONTROLVM_RESP_ERROR_BUS_INVALID; | |
682 | ||
683 | Away: | |
684 | if (!msg->hdr.Flags.testMessage) { | |
685 | uislib_iounmap(dev->chanptr); | |
686 | dev->chanptr = NULL; | |
687 | } | |
688 | ||
60140462 | 689 | kfree(dev); |
bac8a4d5 KC |
690 | return result; |
691 | } | |
692 | ||
693 | static int | |
694 | pause_device(CONTROLVM_MESSAGE *msg) | |
695 | { | |
696 | U32 busNo, devNo; | |
697 | struct bus_info *bus; | |
698 | struct device_info *dev; | |
699 | struct guest_msgs cmd; | |
700 | ||
701 | busNo = msg->cmd.deviceChangeState.busNo; | |
702 | devNo = msg->cmd.deviceChangeState.devNo; | |
703 | ||
704 | read_lock(&BusListLock); | |
705 | for (bus = BusListHead; bus; bus = bus->next) { | |
706 | if (bus->busNo == busNo) { | |
707 | /* make sure the device number is valid */ | |
708 | if (devNo >= bus->deviceCount) { | |
709 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: device(%d) >= deviceCount(%d).", | |
710 | devNo, bus->deviceCount); | |
711 | read_unlock(&BusListLock); | |
712 | return CONTROLVM_RESP_ERROR_DEVICE_INVALID; | |
713 | } | |
714 | /* make sure this device exists */ | |
715 | dev = bus->device[devNo]; | |
716 | if (!dev) { | |
717 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: device %d does not exist.", | |
718 | devNo); | |
719 | read_unlock(&BusListLock); | |
720 | return CONTROLVM_RESP_ERROR_ALREADY_DONE; | |
721 | } | |
722 | read_unlock(&BusListLock); | |
723 | /* the msg is bound for virtpci; send | |
724 | * guest_msgs struct to callback | |
725 | */ | |
726 | if (!memcmp | |
727 | (&dev->channelTypeGuid, | |
728 | &UltraVhbaChannelProtocolGuid, sizeof(GUID))) { | |
729 | cmd.msgtype = GUEST_PAUSE_VHBA; | |
730 | cmd.pause_vhba.chanptr = dev->chanptr; | |
731 | } else | |
732 | if (!memcmp | |
733 | (&dev->channelTypeGuid, | |
734 | &UltraVnicChannelProtocolGuid, | |
735 | sizeof(GUID))) { | |
736 | cmd.msgtype = GUEST_PAUSE_VNIC; | |
737 | cmd.pause_vnic.chanptr = dev->chanptr; | |
738 | } else { | |
739 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: unknown channelTypeGuid.\n"); | |
740 | return | |
741 | CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN; | |
742 | } | |
743 | ||
744 | if (!VirtControlChanFunc) { | |
745 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE Failed: virtpci callback not registered."); | |
746 | return | |
747 | CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE; | |
748 | } | |
749 | ||
750 | if (!VirtControlChanFunc(&cmd)) { | |
751 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: virtpci GUEST_PAUSE_[VHBA||VNIC] returned error."); | |
752 | return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR; | |
753 | } | |
754 | break; | |
755 | } | |
756 | } | |
757 | ||
758 | if (!bus) { | |
759 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: bus %d does not exist", | |
760 | busNo); | |
761 | read_unlock(&BusListLock); | |
762 | return CONTROLVM_RESP_ERROR_BUS_INVALID; | |
763 | } | |
764 | ||
765 | return CONTROLVM_RESP_SUCCESS; | |
766 | } | |
767 | ||
768 | static int | |
769 | resume_device(CONTROLVM_MESSAGE *msg) | |
770 | { | |
771 | U32 busNo, devNo; | |
772 | struct bus_info *bus; | |
773 | struct device_info *dev; | |
774 | struct guest_msgs cmd; | |
775 | ||
776 | busNo = msg->cmd.deviceChangeState.busNo; | |
777 | devNo = msg->cmd.deviceChangeState.devNo; | |
778 | ||
779 | read_lock(&BusListLock); | |
780 | for (bus = BusListHead; bus; bus = bus->next) { | |
781 | if (bus->busNo == busNo) { | |
782 | /* make sure the device number is valid */ | |
783 | if (devNo >= bus->deviceCount) { | |
784 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: device(%d) >= deviceCount(%d).", | |
785 | devNo, bus->deviceCount); | |
786 | read_unlock(&BusListLock); | |
787 | return CONTROLVM_RESP_ERROR_DEVICE_INVALID; | |
788 | } | |
789 | /* make sure this device exists */ | |
790 | dev = bus->device[devNo]; | |
791 | if (!dev) { | |
792 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: device %d does not exist.", | |
793 | devNo); | |
794 | read_unlock(&BusListLock); | |
795 | return CONTROLVM_RESP_ERROR_ALREADY_DONE; | |
796 | } | |
797 | read_unlock(&BusListLock); | |
798 | /* the msg is bound for virtpci; send | |
799 | * guest_msgs struct to callback | |
800 | */ | |
801 | if (!memcmp(&dev->channelTypeGuid, | |
802 | &UltraVhbaChannelProtocolGuid, | |
803 | sizeof(GUID))) { | |
804 | cmd.msgtype = GUEST_RESUME_VHBA; | |
805 | cmd.resume_vhba.chanptr = dev->chanptr; | |
806 | } else | |
807 | if (!memcmp(&dev->channelTypeGuid, | |
808 | &UltraVnicChannelProtocolGuid, | |
809 | sizeof(GUID))) { | |
810 | cmd.msgtype = GUEST_RESUME_VNIC; | |
811 | cmd.resume_vnic.chanptr = dev->chanptr; | |
812 | } else { | |
813 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: unknown channelTypeGuid.\n"); | |
814 | return | |
815 | CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN; | |
816 | } | |
817 | ||
818 | if (!VirtControlChanFunc) { | |
819 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE Failed: virtpci callback not registered."); | |
820 | return | |
821 | CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE; | |
822 | } | |
823 | ||
824 | if (!VirtControlChanFunc(&cmd)) { | |
825 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: virtpci GUEST_RESUME_[VHBA||VNIC] returned error."); | |
826 | return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR; | |
827 | } | |
828 | break; | |
829 | } | |
830 | } | |
831 | ||
832 | if (!bus) { | |
833 | LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: bus %d does not exist", | |
834 | busNo); | |
835 | read_unlock(&BusListLock); | |
836 | return CONTROLVM_RESP_ERROR_BUS_INVALID; | |
837 | } | |
838 | ||
839 | return CONTROLVM_RESP_SUCCESS; | |
840 | } | |
841 | ||
842 | static int | |
843 | destroy_device(CONTROLVM_MESSAGE *msg, char *buf) | |
844 | { | |
845 | U32 busNo, devNo; | |
846 | struct bus_info *bus; | |
847 | struct device_info *dev; | |
848 | struct guest_msgs cmd; | |
849 | ||
850 | busNo = msg->cmd.destroyDevice.busNo; | |
851 | devNo = msg->cmd.destroyDevice.devNo; | |
852 | ||
853 | read_lock(&BusListLock); | |
854 | LOGINF("destroy_device called for busNo=%u, devNo=%u", busNo, devNo); | |
855 | for (bus = BusListHead; bus; bus = bus->next) { | |
856 | if (bus->busNo == busNo) { | |
857 | /* make sure the device number is valid */ | |
858 | if (devNo >= bus->deviceCount) { | |
859 | LOGERR("CONTROLVM_DEVICE_DESTORY Failed: device(%d) >= deviceCount(%d).", | |
860 | devNo, bus->deviceCount); | |
861 | read_unlock(&BusListLock); | |
862 | return CONTROLVM_RESP_ERROR_DEVICE_INVALID; | |
863 | } | |
864 | /* make sure this device exists */ | |
865 | dev = bus->device[devNo]; | |
866 | if (!dev) { | |
867 | LOGERR("CONTROLVM_DEVICE_DESTROY Failed: device %d does not exist.", | |
868 | devNo); | |
869 | read_unlock(&BusListLock); | |
870 | return CONTROLVM_RESP_ERROR_ALREADY_DONE; | |
871 | } | |
872 | read_unlock(&BusListLock); | |
873 | /* the msg is bound for virtpci; send | |
874 | * guest_msgs struct to callback | |
875 | */ | |
876 | if (!memcmp | |
877 | (&dev->channelTypeGuid, | |
878 | &UltraVhbaChannelProtocolGuid, sizeof(GUID))) { | |
879 | cmd.msgtype = GUEST_DEL_VHBA; | |
880 | cmd.del_vhba.chanptr = dev->chanptr; | |
881 | } else | |
882 | if (!memcmp | |
883 | (&dev->channelTypeGuid, | |
884 | &UltraVnicChannelProtocolGuid, | |
885 | sizeof(GUID))) { | |
886 | cmd.msgtype = GUEST_DEL_VNIC; | |
887 | cmd.del_vnic.chanptr = dev->chanptr; | |
888 | } else { | |
889 | LOGERR("CONTROLVM_DEVICE_DESTROY Failed: unknown channelTypeGuid.\n"); | |
890 | return | |
891 | CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN; | |
892 | } | |
893 | ||
894 | if (!VirtControlChanFunc) { | |
895 | LOGERR("CONTROLVM_DEVICE_DESTORY Failed: virtpci callback not registered."); | |
896 | return | |
897 | CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE; | |
898 | } | |
899 | ||
900 | if (!VirtControlChanFunc(&cmd)) { | |
901 | LOGERR("CONTROLVM_DEVICE_DESTROY Failed: virtpci GUEST_DEL_[VHBA||VNIC] returned error."); | |
902 | return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR; | |
903 | } | |
904 | /* you must disable channel interrupts BEFORE you unmap the channel, | |
905 | * because if you unmap first, there may still be some activity going | |
906 | * on which accesses the channel and you will get a "unable to handle | |
907 | * kernel paging request" | |
908 | */ | |
909 | if (dev->polling) { | |
910 | LOGINF("calling uislib_disable_channel_interrupts"); | |
911 | uislib_disable_channel_interrupts(busNo, devNo); | |
912 | } | |
913 | /* unmap the channel memory for the device. */ | |
914 | if (!msg->hdr.Flags.testMessage) { | |
915 | LOGINF("destroy_device, doing iounmap"); | |
916 | uislib_iounmap(dev->chanptr); | |
917 | } | |
60140462 | 918 | kfree(dev); |
bac8a4d5 KC |
919 | bus->device[devNo] = NULL; |
920 | break; | |
921 | } | |
922 | } | |
923 | ||
924 | if (!bus) { | |
925 | LOGERR("CONTROLVM_DEVICE_DESTROY Failed: bus %d does not exist", | |
926 | busNo); | |
927 | read_unlock(&BusListLock); | |
928 | return CONTROLVM_RESP_ERROR_BUS_INVALID; | |
929 | } | |
930 | ||
931 | return CONTROLVM_RESP_SUCCESS; | |
932 | } | |
933 | ||
bac8a4d5 KC |
934 | static int |
935 | init_chipset(CONTROLVM_MESSAGE *msg, char *buf) | |
936 | { | |
937 | POSTCODE_LINUX_2(CHIPSET_INIT_ENTRY_PC, POSTCODE_SEVERITY_INFO); | |
938 | ||
939 | MaxBusCount = msg->cmd.initChipset.busCount; | |
940 | PlatformNumber = msg->cmd.initChipset.platformNumber; | |
941 | PhysicalDataChan = 0; | |
942 | ||
943 | /* We need to make sure we have our functions registered | |
944 | * before processing messages. If we are a test vehicle the | |
945 | * testMessage for init_chipset will be set. We can ignore the | |
946 | * waits for the callbacks, since this will be manually entered | |
947 | * from a user. If no testMessage is set, we will wait for the | |
948 | * functions. | |
949 | */ | |
950 | if (!msg->hdr.Flags.testMessage) | |
951 | WAIT_ON_CALLBACK(VirtControlChanFunc); | |
952 | ||
953 | chipset_inited = 1; | |
954 | POSTCODE_LINUX_2(CHIPSET_INIT_EXIT_PC, POSTCODE_SEVERITY_INFO); | |
955 | ||
956 | return CONTROLVM_RESP_SUCCESS; | |
957 | } | |
958 | ||
959 | static int | |
960 | stop_chipset(CONTROLVM_MESSAGE *msg, char *buf) | |
961 | { | |
962 | /* Check that all buses and switches have been torn down and | |
963 | * destroyed. | |
964 | */ | |
965 | if (BusListHead) { | |
966 | /* Buses still exist. */ | |
967 | LOGERR("CONTROLVM_CHIPSET_STOP: BusListHead is not NULL"); | |
968 | return CONTROLVM_RESP_ERROR_CHIPSET_STOP_FAILED_BUS; | |
969 | } | |
970 | if (BusListCount) { | |
971 | /* BusListHead is NULL, but BusListCount != 0 */ | |
972 | LOGERR("CONTROLVM_CHIPSET_STOP: BusListCount != 0"); | |
973 | return CONTROLVM_RESP_ERROR_CHIPSET_STOP_FAILED_BUS; | |
974 | } | |
975 | ||
976 | /* Buses are shut down. */ | |
977 | return visorchipset_chipset_notready(); | |
978 | } | |
979 | ||
980 | static int | |
981 | delete_bus_glue(U32 busNo) | |
982 | { | |
983 | CONTROLVM_MESSAGE msg; | |
984 | ||
985 | init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, 0); | |
986 | msg.cmd.destroyBus.busNo = busNo; | |
987 | if (destroy_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { | |
988 | LOGERR("destroy_bus failed. busNo=0x%x\n", busNo); | |
989 | return 0; | |
990 | } | |
991 | return 1; | |
992 | } | |
993 | ||
994 | static int | |
995 | delete_device_glue(U32 busNo, U32 devNo) | |
996 | { | |
997 | CONTROLVM_MESSAGE msg; | |
998 | ||
999 | init_msg_header(&msg, CONTROLVM_DEVICE_DESTROY, 0, 0); | |
1000 | msg.cmd.destroyDevice.busNo = busNo; | |
1001 | msg.cmd.destroyDevice.devNo = devNo; | |
1002 | if (destroy_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { | |
1003 | LOGERR("destroy_device failed. busNo=0x%x devNo=0x%x\n", busNo, | |
1004 | devNo); | |
1005 | return 0; | |
1006 | } | |
1007 | return 1; | |
1008 | } | |
1009 | ||
1010 | int | |
1011 | uislib_client_inject_add_bus(U32 busNo, GUID instGuid, | |
1012 | U64 channelAddr, ulong nChannelBytes) | |
1013 | { | |
1014 | CONTROLVM_MESSAGE msg; | |
1015 | ||
1016 | LOGINF("enter busNo=0x%x\n", busNo); | |
1017 | /* step 0: init the chipset */ | |
1018 | POSTCODE_LINUX_3(CHIPSET_INIT_ENTRY_PC, busNo, POSTCODE_SEVERITY_INFO); | |
1019 | ||
1020 | if (!chipset_inited) { | |
1021 | /* step: initialize the chipset */ | |
1022 | init_msg_header(&msg, CONTROLVM_CHIPSET_INIT, 0, 0); | |
1023 | /* this change is needed so that console will come up | |
1024 | * OK even when the bus 0 create comes in late. If the | |
1025 | * bus 0 create is the first create, then the add_vnic | |
1026 | * will work fine, but if the bus 0 create arrives | |
1027 | * after number 4, then the add_vnic will fail, and the | |
1028 | * ultraboot will fail. | |
1029 | */ | |
1030 | msg.cmd.initChipset.busCount = 23; | |
1031 | msg.cmd.initChipset.switchCount = 0; | |
1032 | if (init_chipset(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { | |
1033 | LOGERR("init_chipset failed.\n"); | |
1034 | return 0; | |
1035 | } | |
1036 | LOGINF("chipset initialized\n"); | |
1037 | POSTCODE_LINUX_3(CHIPSET_INIT_EXIT_PC, busNo, | |
1038 | POSTCODE_SEVERITY_INFO); | |
1039 | } | |
1040 | ||
1041 | /* step 1: create a bus */ | |
1042 | POSTCODE_LINUX_3(BUS_CREATE_ENTRY_PC, busNo, POSTCODE_SEVERITY_WARNING); | |
1043 | init_msg_header(&msg, CONTROLVM_BUS_CREATE, 0, 0); | |
1044 | msg.cmd.createBus.busNo = busNo; | |
1045 | msg.cmd.createBus.deviceCount = 23; /* devNo+1; */ | |
1046 | msg.cmd.createBus.channelAddr = channelAddr; | |
1047 | msg.cmd.createBus.channelBytes = nChannelBytes; | |
1048 | if (create_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { | |
1049 | LOGERR("create_bus failed.\n"); | |
1050 | POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, busNo, | |
1051 | POSTCODE_SEVERITY_ERR); | |
1052 | return 0; | |
1053 | } | |
1054 | POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, busNo, POSTCODE_SEVERITY_INFO); | |
1055 | ||
1056 | return 1; | |
1057 | } | |
1058 | EXPORT_SYMBOL_GPL(uislib_client_inject_add_bus); | |
1059 | ||
1060 | ||
1061 | int | |
1062 | uislib_client_inject_del_bus(U32 busNo) | |
1063 | { | |
1064 | return delete_bus_glue(busNo); | |
1065 | } | |
1066 | EXPORT_SYMBOL_GPL(uislib_client_inject_del_bus); | |
1067 | ||
1068 | int | |
1069 | uislib_client_inject_pause_vhba(U32 busNo, U32 devNo) | |
1070 | { | |
1071 | CONTROLVM_MESSAGE msg; | |
1072 | int rc; | |
1073 | ||
1074 | init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0); | |
1075 | msg.cmd.deviceChangeState.busNo = busNo; | |
1076 | msg.cmd.deviceChangeState.devNo = devNo; | |
1077 | msg.cmd.deviceChangeState.state = SegmentStateStandby; | |
1078 | rc = pause_device(&msg); | |
1079 | if (rc != CONTROLVM_RESP_SUCCESS) { | |
1080 | LOGERR("VHBA pause_device failed. busNo=0x%x devNo=0x%x\n", | |
1081 | busNo, devNo); | |
1082 | return rc; | |
1083 | } | |
1084 | return 0; | |
1085 | } | |
1086 | EXPORT_SYMBOL_GPL(uislib_client_inject_pause_vhba); | |
1087 | ||
1088 | int | |
1089 | uislib_client_inject_resume_vhba(U32 busNo, U32 devNo) | |
1090 | { | |
1091 | CONTROLVM_MESSAGE msg; | |
1092 | int rc; | |
1093 | ||
1094 | init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0); | |
1095 | msg.cmd.deviceChangeState.busNo = busNo; | |
1096 | msg.cmd.deviceChangeState.devNo = devNo; | |
1097 | msg.cmd.deviceChangeState.state = SegmentStateRunning; | |
1098 | rc = resume_device(&msg); | |
1099 | if (rc != CONTROLVM_RESP_SUCCESS) { | |
1100 | LOGERR("VHBA resume_device failed. busNo=0x%x devNo=0x%x\n", | |
1101 | busNo, devNo); | |
1102 | return rc; | |
1103 | } | |
1104 | return 0; | |
1105 | ||
1106 | } | |
1107 | EXPORT_SYMBOL_GPL(uislib_client_inject_resume_vhba); | |
1108 | ||
1109 | int | |
1110 | uislib_client_inject_add_vhba(U32 busNo, U32 devNo, | |
1111 | U64 phys_chan_addr, U32 chan_bytes, | |
1112 | int is_test_addr, GUID instGuid, | |
1113 | struct InterruptInfo *intr) | |
1114 | { | |
1115 | CONTROLVM_MESSAGE msg; | |
1116 | ||
1117 | LOGINF(" enter busNo=0x%x devNo=0x%x\n", busNo, devNo); | |
1118 | /* chipset init'ed with bus bus has been previously created - | |
1119 | * Verify it still exists step 2: create the VHBA device on the | |
1120 | * bus | |
1121 | */ | |
1122 | POSTCODE_LINUX_4(VHBA_CREATE_ENTRY_PC, devNo, busNo, | |
1123 | POSTCODE_SEVERITY_INFO); | |
1124 | ||
1125 | init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, 0); | |
1126 | if (is_test_addr) | |
1127 | /* signify that the physical channel address does NOT | |
1128 | * need to be ioremap()ed | |
1129 | */ | |
1130 | msg.hdr.Flags.testMessage = 1; | |
1131 | msg.cmd.createDevice.busNo = busNo; | |
1132 | msg.cmd.createDevice.devNo = devNo; | |
1133 | msg.cmd.createDevice.devInstGuid = instGuid; | |
1134 | if (intr) | |
1135 | msg.cmd.createDevice.intr = *intr; | |
1136 | else | |
1137 | memset(&msg.cmd.createDevice.intr, 0, | |
1138 | sizeof(struct InterruptInfo)); | |
1139 | msg.cmd.createDevice.channelAddr = phys_chan_addr; | |
1140 | if (chan_bytes < MIN_IO_CHANNEL_SIZE) { | |
1141 | LOGERR("wrong channel size.chan_bytes = 0x%x IO_CHANNEL_SIZE= 0x%x\n", | |
1142 | chan_bytes, (unsigned int) MIN_IO_CHANNEL_SIZE); | |
1143 | POSTCODE_LINUX_4(VHBA_CREATE_FAILURE_PC, chan_bytes, | |
1144 | MIN_IO_CHANNEL_SIZE, POSTCODE_SEVERITY_ERR); | |
1145 | return 0; | |
1146 | } | |
1147 | msg.cmd.createDevice.channelBytes = chan_bytes; | |
1148 | msg.cmd.createDevice.dataTypeGuid = UltraVhbaChannelProtocolGuid; | |
1149 | if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { | |
1150 | LOGERR("VHBA create_device failed.\n"); | |
1151 | POSTCODE_LINUX_4(VHBA_CREATE_FAILURE_PC, devNo, busNo, | |
1152 | POSTCODE_SEVERITY_ERR); | |
1153 | return 0; | |
1154 | } | |
1155 | POSTCODE_LINUX_4(VHBA_CREATE_SUCCESS_PC, devNo, busNo, | |
1156 | POSTCODE_SEVERITY_INFO); | |
1157 | return 1; | |
1158 | } | |
1159 | EXPORT_SYMBOL_GPL(uislib_client_inject_add_vhba); | |
1160 | ||
1161 | int | |
1162 | uislib_client_inject_del_vhba(U32 busNo, U32 devNo) | |
1163 | { | |
1164 | return delete_device_glue(busNo, devNo); | |
1165 | } | |
1166 | EXPORT_SYMBOL_GPL(uislib_client_inject_del_vhba); | |
1167 | ||
1168 | int | |
1169 | uislib_client_inject_add_vnic(U32 busNo, U32 devNo, | |
1170 | U64 phys_chan_addr, U32 chan_bytes, | |
1171 | int is_test_addr, GUID instGuid, | |
1172 | struct InterruptInfo *intr) | |
1173 | { | |
1174 | CONTROLVM_MESSAGE msg; | |
1175 | ||
1176 | LOGINF(" enter busNo=0x%x devNo=0x%x\n", busNo, devNo); | |
1177 | /* chipset init'ed with bus bus has been previously created - | |
1178 | * Verify it still exists step 2: create the VNIC device on the | |
1179 | * bus | |
1180 | */ | |
1181 | POSTCODE_LINUX_4(VNIC_CREATE_ENTRY_PC, devNo, busNo, | |
1182 | POSTCODE_SEVERITY_INFO); | |
1183 | ||
1184 | init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, 0); | |
1185 | if (is_test_addr) | |
1186 | /* signify that the physical channel address does NOT | |
1187 | * need to be ioremap()ed | |
1188 | */ | |
1189 | msg.hdr.Flags.testMessage = 1; | |
1190 | msg.cmd.createDevice.busNo = busNo; | |
1191 | msg.cmd.createDevice.devNo = devNo; | |
1192 | msg.cmd.createDevice.devInstGuid = instGuid; | |
1193 | if (intr) | |
1194 | msg.cmd.createDevice.intr = *intr; | |
1195 | else | |
1196 | memset(&msg.cmd.createDevice.intr, 0, | |
1197 | sizeof(struct InterruptInfo)); | |
1198 | msg.cmd.createDevice.channelAddr = phys_chan_addr; | |
1199 | if (chan_bytes < MIN_IO_CHANNEL_SIZE) { | |
1200 | LOGERR("wrong channel size.chan_bytes = 0x%x IO_CHANNEL_SIZE= 0x%x\n", | |
1201 | chan_bytes, (unsigned int) MIN_IO_CHANNEL_SIZE); | |
1202 | POSTCODE_LINUX_4(VNIC_CREATE_FAILURE_PC, chan_bytes, | |
1203 | MIN_IO_CHANNEL_SIZE, POSTCODE_SEVERITY_ERR); | |
1204 | return 0; | |
1205 | } | |
1206 | msg.cmd.createDevice.channelBytes = chan_bytes; | |
1207 | msg.cmd.createDevice.dataTypeGuid = UltraVnicChannelProtocolGuid; | |
1208 | if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { | |
1209 | LOGERR("VNIC create_device failed.\n"); | |
1210 | POSTCODE_LINUX_4(VNIC_CREATE_FAILURE_PC, devNo, busNo, | |
1211 | POSTCODE_SEVERITY_ERR); | |
1212 | return 0; | |
1213 | } | |
1214 | ||
1215 | POSTCODE_LINUX_4(VNIC_CREATE_SUCCESS_PC, devNo, busNo, | |
1216 | POSTCODE_SEVERITY_INFO); | |
1217 | return 1; | |
1218 | } | |
1219 | EXPORT_SYMBOL_GPL(uislib_client_inject_add_vnic); | |
1220 | ||
1221 | int | |
1222 | uislib_client_inject_pause_vnic(U32 busNo, U32 devNo) | |
1223 | { | |
1224 | CONTROLVM_MESSAGE msg; | |
1225 | int rc; | |
1226 | ||
1227 | init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0); | |
1228 | msg.cmd.deviceChangeState.busNo = busNo; | |
1229 | msg.cmd.deviceChangeState.devNo = devNo; | |
1230 | msg.cmd.deviceChangeState.state = SegmentStateStandby; | |
1231 | rc = pause_device(&msg); | |
1232 | if (rc != CONTROLVM_RESP_SUCCESS) { | |
1233 | LOGERR("VNIC pause_device failed. busNo=0x%x devNo=0x%x\n", | |
1234 | busNo, devNo); | |
1235 | return -1; | |
1236 | } | |
1237 | return 0; | |
1238 | } | |
1239 | EXPORT_SYMBOL_GPL(uislib_client_inject_pause_vnic); | |
1240 | ||
1241 | int | |
1242 | uislib_client_inject_resume_vnic(U32 busNo, U32 devNo) | |
1243 | { | |
1244 | CONTROLVM_MESSAGE msg; | |
1245 | int rc; | |
1246 | ||
1247 | init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0); | |
1248 | msg.cmd.deviceChangeState.busNo = busNo; | |
1249 | msg.cmd.deviceChangeState.devNo = devNo; | |
1250 | msg.cmd.deviceChangeState.state = SegmentStateRunning; | |
1251 | rc = resume_device(&msg); | |
1252 | if (rc != CONTROLVM_RESP_SUCCESS) { | |
1253 | LOGERR("VNIC resume_device failed. busNo=0x%x devNo=0x%x\n", | |
1254 | busNo, devNo); | |
1255 | return -1; | |
1256 | } | |
1257 | return 0; | |
1258 | ||
1259 | } | |
1260 | EXPORT_SYMBOL_GPL(uislib_client_inject_resume_vnic); | |
1261 | ||
1262 | int | |
1263 | uislib_client_inject_del_vnic(U32 busNo, U32 devNo) | |
1264 | { | |
1265 | return delete_device_glue(busNo, devNo); | |
1266 | } | |
1267 | EXPORT_SYMBOL_GPL(uislib_client_inject_del_vnic); | |
1268 | ||
a8d7f21d | 1269 | static int |
bac8a4d5 KC |
1270 | uislib_client_add_vnic(U32 busNo) |
1271 | { | |
1272 | BOOL busCreated = FALSE; | |
1273 | int devNo = 0; /* Default to 0, since only one device | |
1274 | * will be created for this bus... */ | |
1275 | GUID dummyGuid = GUID0; | |
1276 | CONTROLVM_MESSAGE msg; | |
1277 | ||
1278 | init_msg_header(&msg, CONTROLVM_BUS_CREATE, 0, 0); | |
1279 | msg.hdr.Flags.testMessage = 1; | |
1280 | msg.cmd.createBus.busNo = busNo; | |
1281 | msg.cmd.createBus.deviceCount = 4; | |
1282 | msg.cmd.createBus.channelAddr = 0; | |
1283 | msg.cmd.createBus.channelBytes = 0; | |
1284 | if (create_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { | |
1285 | LOGERR("client create_bus failed"); | |
1286 | return 0; | |
1287 | } | |
1288 | busCreated = TRUE; | |
1289 | ||
1290 | init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, 0); | |
1291 | msg.hdr.Flags.testMessage = 1; | |
1292 | msg.cmd.createDevice.busNo = busNo; | |
1293 | msg.cmd.createDevice.devNo = devNo; | |
1294 | msg.cmd.createDevice.devInstGuid = dummyGuid; | |
1295 | memset(&msg.cmd.createDevice.intr, 0, sizeof(struct InterruptInfo)); | |
1296 | msg.cmd.createDevice.channelAddr = PhysicalDataChan; | |
1297 | msg.cmd.createDevice.channelBytes = MIN_IO_CHANNEL_SIZE; | |
1298 | msg.cmd.createDevice.dataTypeGuid = UltraVnicChannelProtocolGuid; | |
1299 | if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { | |
1300 | LOGERR("client create_device failed"); | |
1301 | goto AwayCleanup; | |
1302 | } | |
1303 | ||
1304 | return 1; | |
1305 | ||
1306 | AwayCleanup: | |
1307 | if (busCreated) { | |
1308 | init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, 0); | |
1309 | msg.hdr.Flags.testMessage = 1; | |
1310 | msg.cmd.destroyBus.busNo = busNo; | |
1311 | if (destroy_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) | |
1312 | LOGERR("client destroy_bus failed.\n"); | |
1313 | } | |
1314 | ||
1315 | return 0; | |
1316 | } /* end uislib_client_add_vnic */ | |
1317 | EXPORT_SYMBOL_GPL(uislib_client_add_vnic); | |
1318 | ||
a8d7f21d | 1319 | static int |
bac8a4d5 KC |
1320 | uislib_client_delete_vnic(U32 busNo) |
1321 | { | |
1322 | int devNo = 0; /* Default to 0, since only one device | |
1323 | * will be created for this bus... */ | |
1324 | CONTROLVM_MESSAGE msg; | |
1325 | ||
1326 | init_msg_header(&msg, CONTROLVM_DEVICE_DESTROY, 0, 0); | |
1327 | msg.hdr.Flags.testMessage = 1; | |
1328 | msg.cmd.destroyDevice.busNo = busNo; | |
1329 | msg.cmd.destroyDevice.devNo = devNo; | |
1330 | if (destroy_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { | |
1331 | /* Don't error exit - try to see if bus can be destroyed... */ | |
1332 | LOGERR("client destroy_device failed.\n"); | |
1333 | } | |
1334 | ||
1335 | init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, 0); | |
1336 | msg.hdr.Flags.testMessage = 1; | |
1337 | msg.cmd.destroyBus.busNo = busNo; | |
1338 | if (destroy_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) | |
1339 | LOGERR("client destroy_bus failed.\n"); | |
1340 | ||
1341 | return 1; | |
1342 | } | |
1343 | EXPORT_SYMBOL_GPL(uislib_client_delete_vnic); | |
60140462 | 1344 | /* end client_delete_vnic */ |
bac8a4d5 KC |
1345 | |
1346 | void * | |
1347 | uislib_cache_alloc(struct kmem_cache *cur_pool, char *fn, int ln) | |
1348 | { | |
1349 | /* __GFP_NORETRY means "ok to fail", meaning kmalloc() can | |
1350 | * return NULL. If you do NOT specify __GFP_NORETRY, Linux | |
1351 | * will go to extreme measures to get memory for you (like, | |
1352 | * invoke oom killer), which will probably cripple the system. | |
1353 | */ | |
1354 | void *p = kmem_cache_alloc(cur_pool, GFP_ATOMIC | __GFP_NORETRY); | |
1355 | if (p == NULL) { | |
1356 | LOGERR("uislib_malloc failed to alloc uiscmdrsp @%s:%d", | |
1357 | fn, ln); | |
1358 | return NULL; | |
1359 | } | |
1360 | return p; | |
1361 | } | |
1362 | EXPORT_SYMBOL_GPL(uislib_cache_alloc); | |
1363 | ||
1364 | void | |
1365 | uislib_cache_free(struct kmem_cache *cur_pool, void *p, char *fn, int ln) | |
1366 | { | |
1367 | if (p == NULL) { | |
1368 | LOGERR("uislib_free NULL pointer @%s:%d", fn, ln); | |
1369 | return; | |
1370 | } | |
1371 | kmem_cache_free(cur_pool, p); | |
1372 | } | |
1373 | EXPORT_SYMBOL_GPL(uislib_cache_free); | |
1374 | ||
1375 | /*****************************************************/ | |
1376 | /* proc filesystem callback functions */ | |
1377 | /*****************************************************/ | |
1378 | ||
1379 | static ssize_t | |
1380 | vnic_proc_write(struct file *file, const char __user *buffer, | |
1381 | size_t count, loff_t *ppos) | |
1382 | { | |
1383 | int action = 0xffff, busNo = 0, i, result = 0; | |
61a0bc0f | 1384 | char buf[4]; |
bac8a4d5 KC |
1385 | char direction; |
1386 | /* GUID guid; */ | |
61a0bc0f KC |
1387 | if (count >= ARRAY_SIZE(buf)) |
1388 | return -EINVAL; | |
1389 | ||
bac8a4d5 KC |
1390 | if (copy_from_user(buf, buffer, count)) { |
1391 | LOGERR("echo > /proc/uislib/vnic copy_from_user ****FAILED.\n"); | |
1392 | return -EFAULT; | |
1393 | } | |
1394 | ||
1395 | i = sscanf(buf, "%d%c", &action, &direction); | |
1396 | if (i != 2) { | |
1397 | LOGERR("unable to parse vnic proc parameters.\n"); | |
1398 | return -EFAULT; | |
1399 | } | |
1400 | ||
1401 | if ((direction != '-') && (direction != '+')) { | |
1402 | LOGERR("unable to determine whether to add or delete vnic\n"); | |
1403 | return -EFAULT; | |
1404 | } | |
1405 | ||
1406 | /* if (i < 1), i.e., if we didn't even read the action field, | |
1407 | * then action will default to 0xffff and the code below will | |
1408 | * fall through the switch and print usage. | |
1409 | */ | |
1410 | switch (action) { | |
1411 | case 0: | |
1412 | /* call client method... */ | |
1413 | busNo = 0; /* All client drivers use bus value of 0... */ | |
1414 | if (direction == '+') | |
1415 | result = uislib_client_add_vnic(busNo); | |
1416 | else | |
1417 | result = uislib_client_delete_vnic(busNo); | |
1418 | if (!result) { | |
1419 | LOGERR("echo 0%c > /proc/uislib/vnic failed (client end)", | |
1420 | direction); | |
1421 | return -EFAULT; | |
1422 | } | |
1423 | return count; | |
1424 | ||
1425 | default: | |
1426 | break; | |
1427 | } | |
1428 | ||
1429 | LOGERR("USAGE: echo <action><direction (up/down)> > /proc/uislib/vnic"); | |
1430 | LOGERR(" "); | |
1431 | LOGERR("Client Syntax"); | |
1432 | LOGERR("-------------"); | |
1433 | LOGERR("0+ ==> add vnic"); | |
1434 | LOGERR("0- ==> delete vnic"); | |
1435 | LOGERR(" "); | |
1436 | return count; | |
1437 | } /* end vnic_proc_write */ | |
1438 | ||
1439 | static ssize_t | |
1440 | chipset_proc_write(struct file *file, const char __user *buffer, | |
1441 | size_t count, loff_t *ppos) | |
1442 | { | |
1443 | int i, action = 0xffff; | |
61a0bc0f | 1444 | char buf[4]; |
bac8a4d5 KC |
1445 | CONTROLVM_MESSAGE msg; |
1446 | ||
61a0bc0f KC |
1447 | if (count >= ARRAY_SIZE(buf)) |
1448 | return -EINVAL; | |
1449 | ||
bac8a4d5 KC |
1450 | memset(&msg, 0, sizeof(CONTROLVM_MESSAGE)); |
1451 | ||
1452 | if (copy_from_user(buf, buffer, count)) { | |
1453 | LOGERR("copy_from_user ****FAILED.\n"); | |
1454 | return -EFAULT; | |
1455 | } | |
1456 | ||
1457 | if (chipset_inited) { | |
1458 | LOGINF("Chipset already initialized\n"); | |
1459 | return -EFAULT; | |
1460 | } | |
1461 | i = sscanf(buf, "%x", &action); | |
1462 | ||
1463 | /* if (i < 1), i.e., if we didn't even read the action field, | |
1464 | * then action will default to 0xffff and the code below will | |
1465 | * fall through the switch and print usage. | |
1466 | */ | |
1467 | switch (action) { | |
1468 | case 1: | |
1469 | /* GUEST */ | |
1470 | /* step: initialize the chipset */ | |
1471 | init_msg_header(&msg, CONTROLVM_CHIPSET_INIT, 0, 0); | |
1472 | msg.hdr.Flags.testMessage = 0; | |
1473 | msg.cmd.initChipset.busCount = 23; | |
1474 | msg.cmd.initChipset.switchCount = 23; | |
1475 | ||
1476 | if (init_chipset(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { | |
1477 | LOGERR("init_chipset failed.\n"); | |
1478 | return 0; | |
1479 | } | |
1480 | return 1; | |
1481 | case 2: | |
1482 | /* BOTH */ | |
1483 | init_msg_header(&msg, CONTROLVM_CHIPSET_INIT, 0, 0); | |
1484 | msg.hdr.Flags.testMessage = 1; | |
1485 | msg.cmd.initChipset.busCount = 23; | |
1486 | msg.cmd.initChipset.switchCount = 23; | |
1487 | ||
1488 | if (init_chipset(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { | |
1489 | LOGERR("init_chipset failed.\n"); | |
1490 | return 0; | |
1491 | } | |
1492 | return 1; | |
1493 | ||
1494 | default: | |
1495 | break; | |
1496 | } | |
1497 | ||
1498 | LOGERR("usage: 1 ==> init_chipset client\n"); | |
1499 | LOGERR("usage: 2 ==> init_chipset test\n"); | |
1500 | return -EFAULT; | |
1501 | } | |
1502 | ||
27dd5548 KC |
1503 | #define PLINE(...) uisutil_add_proc_line_ex(&tot, buff, \ |
1504 | buff_len, __VA_ARGS__) | |
bac8a4d5 KC |
1505 | |
1506 | static int | |
1507 | info_proc_read_helper(char **buff, int *buff_len) | |
1508 | { | |
1509 | int i, tot = 0; | |
1510 | struct bus_info *bus; | |
1511 | ||
27dd5548 KC |
1512 | if (PLINE("\nBuses:\n") < 0) |
1513 | goto err_done; | |
bac8a4d5 KC |
1514 | |
1515 | read_lock(&BusListLock); | |
1516 | for (bus = BusListHead; bus; bus = bus->next) { | |
1517 | ||
27dd5548 KC |
1518 | if (PLINE(" bus=0x%p, busNo=%d, deviceCount=%d\n", |
1519 | bus, bus->busNo, bus->deviceCount) < 0) | |
1520 | goto err_done_unlock; | |
bac8a4d5 | 1521 | |
27dd5548 KC |
1522 | |
1523 | if (PLINE(" Devices:\n") < 0) | |
1524 | goto err_done_unlock; | |
bac8a4d5 KC |
1525 | |
1526 | for (i = 0; i < bus->deviceCount; i++) { | |
1527 | if (bus->device[i]) { | |
27dd5548 KC |
1528 | if (PLINE(" busNo %d, device[%i]: 0x%p, chanptr=0x%p, swtch=0x%p\n", |
1529 | bus->busNo, i, bus->device[i], | |
1530 | bus->device[i]->chanptr, | |
1531 | bus->device[i]->swtch) < 0) | |
1532 | goto err_done_unlock; | |
1533 | ||
1534 | if (PLINE(" first_busy_cnt=%llu, moved_to_tail_cnt=%llu, last_on_list_cnt=%llu\n", | |
1535 | bus->device[i]->first_busy_cnt, | |
1536 | bus->device[i]->moved_to_tail_cnt, | |
1537 | bus->device[i]->last_on_list_cnt) < 0) | |
1538 | goto err_done_unlock; | |
bac8a4d5 KC |
1539 | } |
1540 | } | |
1541 | } | |
1542 | read_unlock(&BusListLock); | |
1543 | ||
27dd5548 KC |
1544 | if (PLINE("UisUtils_Registered_Services: %d\n", |
1545 | atomic_read(&UisUtils_Registered_Services)) < 0) | |
1546 | goto err_done; | |
1547 | if (PLINE("cycles_before_wait %llu wait_cycles:%llu\n", | |
1548 | cycles_before_wait, wait_cycles) < 0) | |
1549 | goto err_done; | |
1550 | if (PLINE("tot_wakeup_cnt %llu:tot_wait_cnt %llu:tot_schedule_cnt %llu\n", | |
1551 | tot_wakeup_cnt, tot_wait_cnt, tot_schedule_cnt) < 0) | |
1552 | goto err_done; | |
1553 | if (PLINE("en_smart_wakeup %d\n", en_smart_wakeup) < 0) | |
1554 | goto err_done; | |
1555 | if (PLINE("tot_moved_to_tail_cnt %llu\n", tot_moved_to_tail_cnt) < 0) | |
1556 | goto err_done; | |
bac8a4d5 KC |
1557 | |
1558 | return tot; | |
bac8a4d5 | 1559 | |
27dd5548 KC |
1560 | err_done_unlock: |
1561 | read_unlock(&BusListLock); | |
1562 | err_done: | |
bac8a4d5 KC |
1563 | return -1; |
1564 | } | |
1565 | ||
1566 | static ssize_t | |
1567 | info_proc_read(struct file *file, char __user *buf, size_t len, loff_t *offset) | |
1568 | { | |
1569 | char *temp; | |
1570 | int totalBytes = 0; | |
1571 | int remaining_bytes = PROC_READ_BUFFER_SIZE; | |
1572 | ||
1573 | /* *start = buf; */ | |
1574 | if (ProcReadBuffer == NULL) { | |
1575 | DBGINF("ProcReadBuffer == NULL; allocating buffer.\n."); | |
60140462 | 1576 | ProcReadBuffer = vmalloc(PROC_READ_BUFFER_SIZE); |
bac8a4d5 KC |
1577 | |
1578 | if (ProcReadBuffer == NULL) { | |
1579 | LOGERR("failed to allocate buffer to provide proc data.\n"); | |
1580 | return -ENOMEM; | |
1581 | } | |
1582 | } | |
1583 | ||
1584 | temp = ProcReadBuffer; | |
1585 | ||
1586 | if ((*offset == 0) || (!ProcReadBufferValid)) { | |
1587 | DBGINF("calling info_proc_read_helper.\n"); | |
1588 | /* if the read fails, then -1 will be returned */ | |
1589 | totalBytes = info_proc_read_helper(&temp, &remaining_bytes); | |
1590 | ProcReadBufferValid = 1; | |
1591 | } else | |
1592 | totalBytes = strlen(ProcReadBuffer); | |
1593 | ||
1594 | return simple_read_from_buffer(buf, len, offset, | |
1595 | ProcReadBuffer, totalBytes); | |
1596 | } | |
1597 | ||
1598 | static ssize_t | |
1599 | platformnumber_proc_read(struct file *file, char __user *buf, | |
1600 | size_t len, loff_t *offset) | |
1601 | { | |
1602 | int length = 0; | |
1603 | char *vbuf; | |
1604 | loff_t pos = *offset; | |
1605 | ||
1606 | if (pos < 0) | |
1607 | return -EINVAL; | |
1608 | ||
1609 | if (pos > 0 || !len) | |
1610 | return 0; | |
1611 | ||
1612 | vbuf = kzalloc(len, GFP_KERNEL); | |
1613 | if (!vbuf) | |
1614 | return -ENOMEM; | |
1615 | ||
1616 | length = sprintf(vbuf, "%d\n", PlatformNumber); | |
1617 | ||
1618 | if (copy_to_user(buf, vbuf, length)) { | |
1619 | kfree(vbuf); | |
1620 | return -EFAULT; | |
1621 | } | |
1622 | ||
1623 | kfree(vbuf); | |
1624 | *offset += length; | |
1625 | return length; | |
1626 | } | |
1627 | ||
1628 | #ifdef UISLIB_TEST_PROC | |
1629 | ||
1630 | /* proc/uislib/vbus/<x>/info */ | |
1631 | static int | |
1632 | proc_info_vbus_show(struct seq_file *m, void *v) | |
1633 | { | |
1634 | struct bus_info *bus = m->private; | |
1635 | int i, devInfoCount, x; | |
1636 | char buf[999]; | |
1637 | ||
1638 | if (bus == NULL) | |
1639 | return 0; | |
1640 | seq_printf(m, "Client device / client driver info for %s partition (vbus #%d):\n", | |
1641 | bus->partitionName, bus->busNo); | |
1642 | if ((bus->busChannelBytes == 0) || (bus->pBusChannel == NULL)) | |
1643 | return 0; | |
1644 | devInfoCount = | |
1645 | (bus->busChannelBytes - | |
1646 | sizeof(ULTRA_VBUS_CHANNEL_PROTOCOL)) / | |
1647 | sizeof(ULTRA_VBUS_DEVICEINFO); | |
a8d7f21d | 1648 | x = VBUSCHANNEL_devInfoToStringBuffer(&bus->pBusChannel->ChpInfo, buf, |
bac8a4d5 KC |
1649 | sizeof(buf) - 1, -1); |
1650 | buf[x] = '\0'; | |
1651 | seq_printf(m, "%s", buf); | |
a8d7f21d | 1652 | x = VBUSCHANNEL_devInfoToStringBuffer(&bus->pBusChannel->BusInfo, |
bac8a4d5 KC |
1653 | buf, sizeof(buf) - 1, -1); |
1654 | buf[x] = '\0'; | |
1655 | seq_printf(m, "%s", buf); | |
1656 | for (i = 0; i < devInfoCount; i++) { | |
a8d7f21d | 1657 | x = VBUSCHANNEL_devInfoToStringBuffer(&bus->pBusChannel-> |
bac8a4d5 KC |
1658 | DevInfo[i], buf, |
1659 | sizeof(buf) - 1, i); | |
1660 | if (x > 0) { | |
1661 | buf[x] = '\0'; | |
1662 | seq_printf(m, "%s", buf); | |
1663 | } | |
1664 | } | |
1665 | return 0; | |
1666 | } | |
1667 | ||
1668 | static ssize_t | |
1669 | bus_proc_write(struct file *file, const char __user *buffer, | |
1670 | size_t count, loff_t *ppos) | |
1671 | { | |
1672 | int server_flag = 0; | |
1673 | int i, action = 0xffff, result; | |
61a0bc0f | 1674 | char buf[16]; |
bac8a4d5 KC |
1675 | CONTROLVM_MESSAGE msg; |
1676 | U32 busNo, deviceCount; | |
1677 | ||
61a0bc0f KC |
1678 | if (count >= ARRAY_SIZE(buf)) |
1679 | return -EINVAL; | |
1680 | ||
bac8a4d5 KC |
1681 | memset(&msg, 0, sizeof(CONTROLVM_MESSAGE)); |
1682 | ||
1683 | if (copy_from_user(buf, buffer, count)) { | |
1684 | LOGERR("echo > /proc/uislib/bus: copy_from_user ****FAILED."); | |
1685 | return -EFAULT; | |
1686 | } | |
1687 | ||
1688 | i = sscanf(buf, "%x-%d-%d", &action, &busNo, &deviceCount); | |
1689 | ||
1690 | /* if (i < 1), i.e., if we didn't even read the action field, | |
1691 | * then action will default to 0xffff and the code below will | |
1692 | * fall through the switch and print usage. | |
1693 | */ | |
1694 | switch (action) { | |
1695 | case 0: | |
1696 | /* destroy a bus */ | |
1697 | if (i != 2) | |
1698 | break; | |
1699 | init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, server_flag); | |
1700 | msg.cmd.destroyBus.busNo = busNo; | |
1701 | ||
1702 | result = destroy_bus(&msg, NULL); | |
1703 | ||
1704 | if (result != CONTROLVM_RESP_SUCCESS) { | |
1705 | LOGERR("echo 0-%d > /proc/uislib/bus {CONTROLVM_BUS_DESTROY Failed} Result(%d)", | |
1706 | busNo, result); | |
1707 | return -EFAULT; | |
1708 | } | |
1709 | return count; | |
1710 | case 1: | |
1711 | /* create a bus */ | |
1712 | if (i != 3) | |
1713 | break; | |
1714 | init_msg_header(&msg, CONTROLVM_BUS_CREATE, 0, server_flag); | |
1715 | msg.cmd.createBus.busNo = busNo; | |
1716 | msg.cmd.createBus.deviceCount = deviceCount; | |
1717 | ||
1718 | result = create_bus(&msg, NULL); | |
1719 | ||
1720 | if (result != CONTROLVM_RESP_SUCCESS) { | |
1721 | LOGERR("echo 1-%d-%d > /proc/uislib/bus {CONTROLVM_BUS_CREATE Failed} Result(%d)", | |
1722 | busNo, deviceCount, result); | |
1723 | return -EFAULT; | |
1724 | } | |
1725 | ||
1726 | return count; | |
1727 | default: | |
1728 | break; | |
1729 | } | |
1730 | ||
1731 | LOGERR("USAGE: echo <action>-<busNo>... > /proc/uislib/bus"); | |
1732 | LOGERR(" "); | |
1733 | LOGERR("Destruct Syntax ControlVM Message Id"); | |
1734 | LOGERR("--------------- ---------------------"); | |
1735 | LOGERR("0-<busNo> ==> CONTROLVM_BUS_DESTROY"); | |
1736 | LOGERR(" "); | |
1737 | LOGERR("Construct Syntax ControlVM Message Id"); | |
1738 | LOGERR("----------------------- -------------------- "); | |
1739 | LOGERR("1-<busNo>-<deviceCount> ==> CONTROLVM_BUS_CREATE"); | |
1740 | ||
1741 | return -EFAULT; | |
1742 | } | |
1743 | ||
1744 | static ssize_t | |
1745 | uislib_proc_read_writeonly(struct file *file, char __user *buffer, | |
1746 | size_t count, loff_t *ppos) | |
1747 | { | |
1748 | return 0; | |
1749 | } | |
1750 | ||
1751 | static ssize_t | |
1752 | dev_proc_write(struct file *file, const char __user *buffer, | |
1753 | size_t count, loff_t *ppos) | |
1754 | { | |
1755 | int server_flag = 0; | |
1756 | CONTROLVM_MESSAGE msg; | |
1757 | U32 busNo, devNo; | |
61a0bc0f | 1758 | char buf[32]; |
bac8a4d5 KC |
1759 | unsigned int chanptr; |
1760 | int type, i, action = 0xffff, result; | |
1761 | ||
61a0bc0f KC |
1762 | if (count >= ARRAY_SIZE(buf)) |
1763 | return -EINVAL; | |
1764 | ||
bac8a4d5 KC |
1765 | if (copy_from_user(buf, buffer, count)) { |
1766 | LOGERR("echo > /proc/uislib/device: copy_from_user ****FAILED."); | |
1767 | return -EFAULT; | |
1768 | } | |
1769 | ||
1770 | i = sscanf(buf, "%x-%d-%d-%x-%d", | |
1771 | &action, &busNo, &devNo, &chanptr, &type); | |
1772 | ||
1773 | switch (action) { | |
1774 | case 0: | |
1775 | if (i != 3) | |
1776 | break; | |
1777 | ||
1778 | /* destroy a device */ | |
1779 | init_msg_header(&msg, CONTROLVM_DEVICE_DESTROY, 0, server_flag); | |
1780 | msg.cmd.destroyDevice.busNo = busNo; | |
1781 | msg.cmd.destroyDevice.devNo = devNo; | |
1782 | ||
1783 | result = destroy_device(&msg, NULL); | |
1784 | ||
1785 | if (result != CONTROLVM_RESP_SUCCESS) { | |
1786 | LOGERR("echo 0-%d-%d > /proc/uislib/device {CONTROLVM_DEVICE_DESTROY Failed} Result(%d)", | |
1787 | busNo, devNo, result); | |
1788 | return -EFAULT; | |
1789 | } | |
1790 | ||
1791 | return count; | |
1792 | ||
1793 | case 1: | |
1794 | if (i != 5) | |
1795 | break; | |
1796 | ||
1797 | /* create a device */ | |
1798 | init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, server_flag); | |
1799 | msg.cmd.createDevice.busNo = busNo; | |
1800 | msg.cmd.createDevice.devNo = devNo; | |
1801 | msg.cmd.createDevice.channelAddr = __pa(chanptr); | |
1802 | msg.cmd.createDevice.channelBytes = MIN_IO_CHANNEL_SIZE; | |
1803 | ||
1804 | if (type == 0) | |
1805 | msg.cmd.createDevice.dataTypeGuid = | |
1806 | UltraVhbaChannelProtocolGuid; | |
1807 | else if (type == 1) | |
1808 | msg.cmd.createDevice.dataTypeGuid = | |
1809 | UltraVnicChannelProtocolGuid; | |
1810 | else { | |
1811 | LOGERR("echo 1-%d-%d-%x-<type> > /proc/uislib/devce failed: invalid device type %d.", | |
1812 | busNo, devNo, chanptr, type); | |
1813 | return -EFAULT; | |
1814 | } | |
1815 | ||
1816 | result = create_device(&msg, NULL); | |
1817 | ||
1818 | if (result != CONTROLVM_RESP_SUCCESS) { | |
1819 | if (type == 0) | |
1820 | LOGERR("echo 1-%d-%d-%x-0 > /proc/uislib/device {CONTROLVM_DEVICE_CREATE[vHBA] Failed} Result(%d)", | |
1821 | busNo, devNo, chanptr, result); | |
1822 | else | |
1823 | LOGERR("echo 1-%d-%d-%x-1 > /proc/uislib/device {CONTROLVM_DEVICE_CREATE[vNIC] Failed} Result(%d)", | |
1824 | busNo, devNo, chanptr, result); | |
1825 | return -EFAULT; | |
1826 | } | |
1827 | ||
1828 | default: | |
1829 | break; | |
1830 | } | |
1831 | ||
1832 | LOGERR("USAGE: echo <action>-<busNo>-<devNo>... > /proc/uislib/device"); | |
1833 | LOGERR(" "); | |
1834 | LOGERR("Destruct Syntax ControlVM Message Id"); | |
1835 | LOGERR("----------------- ------------------------"); | |
1836 | LOGERR("0-<busNo>-<devNo> ==> CONTROLVM_DEVICE_DESTROY"); | |
1837 | LOGERR(" "); | |
1838 | LOGERR("Construct Syntax ControlVM Message Id"); | |
1839 | LOGERR | |
1840 | ("---------------------------------- ----------------------- "); | |
1841 | LOGERR | |
1842 | ("1-<busNo>-<devNo>-<chanptr>-<type> ==> CONTROLVM_DEVICE_CREATE"); | |
1843 | LOGERR(" <type = 0>: vHBA"); | |
1844 | LOGERR(" <type = 1>: vNIC"); | |
1845 | LOGERR(" "); | |
1846 | ||
1847 | return -EFAULT; | |
1848 | } | |
1849 | ||
1850 | static ssize_t | |
1851 | cycles_before_wait_proc_write(struct file *file, const char __user *buffer, | |
1852 | size_t count, loff_t *ppos) | |
1853 | { | |
61a0bc0f | 1854 | char buf[16]; |
bac8a4d5 KC |
1855 | |
1856 | #define CYCLES_BEFORE_WAIT_USE_ERROR { \ | |
1857 | LOGERR("Incorrect Call Home Input.\n"); \ | |
1858 | pr_info("Please pass Call Home Event Parameters in the form:\n"); \ | |
1859 | pr_info("EventID Category Type[parameter1][parameter2][parameter3][parameter4][parameter5][parameter6]\n"); \ | |
1860 | return -EFAULT; \ | |
1861 | } | |
61a0bc0f KC |
1862 | if (count >= ARRAY_SIZE(buf)) |
1863 | return -EINVAL; | |
bac8a4d5 KC |
1864 | |
1865 | if (count == 0) | |
1866 | CYCLES_BEFORE_WAIT_USE_ERROR; | |
1867 | ||
1868 | if (copy_from_user(buf, buffer, count)) { | |
1869 | LOGERR("copy_from_user failed.\n"); | |
1870 | return -EFAULT; | |
1871 | } | |
1872 | buf[count - 1] = '\0'; /* Replace the LF at the end of the | |
1873 | * input with a NULL */ | |
1874 | /* Pull out the cycles_before_wait must be decimal integer */ | |
1875 | if (sscanf(buf, "%lld", &cycles_before_wait) != 1) | |
1876 | CYCLES_BEFORE_WAIT_USE_ERROR; | |
1877 | ||
1878 | return count; | |
1879 | } | |
1880 | ||
1881 | static ssize_t | |
1882 | reset_counts_proc_write(struct file *file, const char __user *buffer, | |
1883 | size_t count, loff_t *ppos) | |
1884 | { | |
61a0bc0f | 1885 | char buf[16]; |
bac8a4d5 KC |
1886 | unsigned long long new_value; |
1887 | struct bus_info *bus; | |
1888 | int i; | |
1889 | ||
1890 | #define RESET_COUNTS_USE_ERROR { \ | |
1891 | LOGERR("Incorrect reset_counts Input.\n"); \ | |
1892 | pr_info("Please pass the new value for the counters:\n"); \ | |
1893 | pr_info("e.g. echo 0 > reset_counts\n"); \ | |
1894 | return -EFAULT; \ | |
1895 | } | |
1896 | ||
61a0bc0f KC |
1897 | if (count >= ARRAY_SIZE(buf)) |
1898 | return -EINVAL; | |
1899 | ||
bac8a4d5 KC |
1900 | if (count == 0) |
1901 | RESET_COUNTS_USE_ERROR; | |
1902 | ||
1903 | if (copy_from_user(buf, buffer, count)) { | |
1904 | LOGERR("copy_from_user failed.\n"); | |
1905 | return -EFAULT; | |
1906 | } | |
1907 | buf[count - 1] = '\0'; /* Replace the LF at the end of the | |
1908 | * input with a NULL */ | |
1909 | /* Pull out the reset_counts must be decimal integer */ | |
1910 | if (sscanf(buf, "%llu", &new_value) != 1) | |
1911 | RESET_COUNTS_USE_ERROR; | |
1912 | read_lock(&BusListLock); | |
1913 | for (bus = BusListHead; bus; bus = bus->next) { | |
1914 | ||
1915 | for (i = 0; i < bus->deviceCount; i++) { | |
1916 | if (bus->device[i]) { | |
1917 | bus->device[i]->first_busy_cnt = new_value; | |
1918 | bus->device[i]->moved_to_tail_cnt = new_value; | |
1919 | bus->device[i]->last_on_list_cnt = new_value; | |
1920 | } | |
1921 | } | |
1922 | } | |
1923 | read_unlock(&BusListLock); | |
1924 | tot_moved_to_tail_cnt = new_value; | |
1925 | tot_wait_cnt = new_value; | |
1926 | tot_wakeup_cnt = new_value; | |
1927 | tot_schedule_cnt = new_value; | |
1928 | return count; | |
1929 | } | |
1930 | ||
1931 | static ssize_t | |
1932 | smart_wakeup_proc_write(struct file *file, const char __user *buffer, | |
1933 | size_t count, loff_t *ppos) | |
1934 | { | |
61a0bc0f | 1935 | char buf[16]; |
bac8a4d5 KC |
1936 | int new_value; |
1937 | ||
1938 | #define SMART_WAKEUP_USE_ERROR { \ | |
1939 | LOGERR("Incorrect smart_wakeup Input 0 disables smart_wakeup, and 1 enables smart_wakeup.\n"); \ | |
1940 | pr_info("echo 0 > smart_wakeup\n"); \ | |
1941 | pr_info("echo 1 > smart_wakeup\n"); \ | |
1942 | return -EFAULT; \ | |
1943 | } | |
1944 | ||
61a0bc0f KC |
1945 | if (count >= ARRAY_SIZE(buf)) |
1946 | return -EINVAL; | |
1947 | ||
bac8a4d5 KC |
1948 | if (count == 0) |
1949 | SMART_WAKEUP_USE_ERROR; | |
1950 | ||
1951 | if (copy_from_user(buf, buffer, count)) { | |
1952 | LOGERR("copy_from_user failed.\n"); | |
1953 | return -EFAULT; | |
1954 | } | |
1955 | buf[count - 1] = '\0'; /* Replace the LF at the end of the | |
1956 | * input with a NULL */ | |
1957 | /* Pull out the smart_wakeup must be decimal integer */ | |
1958 | if (sscanf(buf, "%d", &new_value) != 1) | |
1959 | SMART_WAKEUP_USE_ERROR; | |
1960 | en_smart_wakeup = new_value; | |
1961 | return count; | |
1962 | } | |
1963 | ||
1964 | static ssize_t | |
1965 | test_proc_write(struct file *file, const char __user *buffer, | |
1966 | size_t count, loff_t *ppos) | |
1967 | { | |
1968 | int i, action = 0xffff; | |
61a0bc0f | 1969 | char buf[16]; |
bac8a4d5 KC |
1970 | CONTROLVM_MESSAGE msg; |
1971 | S64 vrtc_offset; | |
1972 | ||
61a0bc0f KC |
1973 | if (count >= ARRAY_SIZE(buf)) |
1974 | return -EINVAL; | |
1975 | ||
bac8a4d5 KC |
1976 | memset(&msg, 0, sizeof(CONTROLVM_MESSAGE)); |
1977 | ||
1978 | if (copy_from_user(buf, buffer, count)) { | |
1979 | LOGERR("copy_from_user ****FAILED.\n"); | |
1980 | return -EFAULT; | |
1981 | } | |
1982 | ||
1983 | i = sscanf(buf, "%x", &action); | |
1984 | ||
1985 | /* if (i < 1), i.e., if we didn't even read the action field, | |
1986 | * then action will default to 0xffff and the code below will | |
1987 | * fall through the switch and print usage. */ | |
1988 | switch (action) { | |
1989 | case 6: | |
1990 | msg.hdr.Id = CONTROLVM_CHIPSET_STOP; | |
1991 | msg.hdr.Flags.responseExpected = 1; | |
1992 | stop_chipset(&msg, NULL); | |
1993 | break; | |
1994 | case 7: | |
1995 | vrtc_offset = 0; | |
1996 | LOGERR("about to issue QUERY vrtc_offset=%LX", vrtc_offset); | |
1997 | vrtc_offset = Issue_VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET(); | |
1998 | LOGERR("result is vrtc_offset=%LX", vrtc_offset); | |
1999 | break; | |
2000 | case 8: | |
2001 | vrtc_offset = 60; | |
2002 | LOGERR("about to increase physical time by 0x%LX seconds", | |
2003 | vrtc_offset); | |
2004 | vrtc_offset = Issue_VMCALL_UPDATE_PHYSICAL_TIME(vrtc_offset); | |
2005 | break; | |
2006 | case 9: | |
2007 | vrtc_offset = -60; | |
2008 | LOGERR("about to decrease physical time by 0x%LX seconds", | |
2009 | vrtc_offset); | |
2010 | vrtc_offset = Issue_VMCALL_UPDATE_PHYSICAL_TIME(vrtc_offset); | |
2011 | break; | |
2012 | default: | |
2013 | LOGERR("usage: 6 for CHIPSET_STOP\n"); | |
2014 | LOGERR(" 7 for VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET()\n"); | |
2015 | LOGERR(" 8 for VMCALL_UPDATE_PHYSICAL_TIME(60)\n"); | |
2016 | LOGERR(" 9 for VMCALL_UPDATE_PHYSICAL_TIME(-60)\n"); | |
2017 | return -EFAULT; | |
2018 | break; | |
2019 | } | |
2020 | return count; | |
2021 | } | |
2022 | ||
2023 | #endif /* UISLIB_TEST_PROC */ | |
2024 | static struct device_info * | |
2025 | find_dev(U32 busNo, U32 devNo) | |
2026 | { | |
2027 | struct bus_info *bus; | |
2028 | struct device_info *dev = NULL; | |
2029 | ||
2030 | read_lock(&BusListLock); | |
2031 | for (bus = BusListHead; bus; bus = bus->next) { | |
2032 | if (bus->busNo == busNo) { | |
2033 | /* make sure the device number is valid */ | |
2034 | if (devNo >= bus->deviceCount) { | |
2035 | LOGERR("%s bad busNo, devNo=%d,%d", | |
2036 | __func__, | |
2037 | (int) (busNo), (int) (devNo)); | |
2038 | goto Away; | |
2039 | } | |
2040 | dev = bus->device[devNo]; | |
2041 | if (!dev) | |
2042 | LOGERR("%s bad busNo, devNo=%d,%d", | |
2043 | __func__, | |
2044 | (int) (busNo), (int) (devNo)); | |
2045 | goto Away; | |
2046 | } | |
2047 | } | |
2048 | Away: | |
2049 | read_unlock(&BusListLock); | |
2050 | return dev; | |
2051 | } | |
2052 | ||
2053 | /* This thread calls the "interrupt" function for each device that has | |
2054 | * enabled such using uislib_enable_channel_interrupts(). The "interrupt" | |
2055 | * function typically reads and processes the devices's channel input | |
2056 | * queue. This thread repeatedly does this, until the thread is told to stop | |
2057 | * (via uisthread_stop()). Sleeping rules: | |
2058 | * - If we have called the "interrupt" function for all devices, and all of | |
2059 | * them have reported "nothing processed" (returned 0), then we will go to | |
2060 | * sleep for a maximum of POLLJIFFIES_NORMAL jiffies. | |
2061 | * - If anyone calls uislib_force_channel_interrupt(), the above jiffy | |
2062 | * sleep will be interrupted, and we will resume calling the "interrupt" | |
2063 | * function for all devices. | |
2064 | * - The list of devices is dynamically re-ordered in order to | |
2065 | * attempt to preserve fairness. Whenever we spin thru the list of | |
2066 | * devices and call the dev->interrupt() function, if we find | |
2067 | * devices which report that there is still more work to do, the | |
2068 | * the first such device we find is moved to the end of the device | |
2069 | * list. This ensures that extremely busy devices don't starve out | |
2070 | * less-busy ones. | |
2071 | * | |
2072 | */ | |
2073 | static int | |
2074 | Process_Incoming(void *v) | |
2075 | { | |
2076 | unsigned long long cur_cycles, old_cycles, idle_cycles, delta_cycles; | |
2077 | struct list_head *new_tail = NULL; | |
2078 | int i; | |
2079 | UIS_DAEMONIZE("dev_incoming"); | |
2080 | for (i = 0; i < 16; i++) { | |
2081 | old_cycles = get_cycles(); | |
2082 | wait_event_timeout(Wakeup_Polling_Device_Channels, | |
2083 | 0, POLLJIFFIES_NORMAL); | |
2084 | cur_cycles = get_cycles(); | |
2085 | if (wait_cycles == 0) { | |
2086 | wait_cycles = (cur_cycles - old_cycles); | |
2087 | } else { | |
2088 | if (wait_cycles < (cur_cycles - old_cycles)) | |
2089 | wait_cycles = (cur_cycles - old_cycles); | |
2090 | } | |
2091 | } | |
2092 | LOGINF("wait_cycles=%llu", wait_cycles); | |
2093 | cycles_before_wait = wait_cycles; | |
2094 | idle_cycles = 0; | |
2095 | Go_Polling_Device_Channels = 0; | |
2096 | while (1) { | |
2097 | struct list_head *lelt, *tmp; | |
2098 | struct device_info *dev = NULL; | |
2099 | ||
2100 | /* poll each channel for input */ | |
2101 | LOCKSEM_UNINTERRUPTIBLE(&Lock_Polling_Device_Channels); | |
2102 | new_tail = NULL; | |
2103 | list_for_each_safe(lelt, tmp, &List_Polling_Device_Channels) { | |
2104 | int rc = 0; | |
2105 | dev = list_entry(lelt, struct device_info, | |
2106 | list_polling_device_channels); | |
2107 | LOCKSEM_UNINTERRUPTIBLE(&dev->interrupt_callback_lock); | |
2108 | if (dev->interrupt) | |
2109 | rc = dev->interrupt(dev->interrupt_context); | |
2110 | else | |
2111 | continue; | |
2112 | UNLOCKSEM(&dev->interrupt_callback_lock); | |
2113 | if (rc) { | |
2114 | /* dev->interrupt returned, but there | |
2115 | * is still more work to do. | |
2116 | * Reschedule work to occur as soon as | |
2117 | * possible. */ | |
2118 | idle_cycles = 0; | |
2119 | if (new_tail == NULL) { | |
2120 | dev->first_busy_cnt++; | |
2121 | if (! | |
2122 | (list_is_last | |
2123 | (lelt, | |
2124 | &List_Polling_Device_Channels))) { | |
2125 | new_tail = lelt; | |
2126 | dev->moved_to_tail_cnt++; | |
2127 | } else | |
2128 | dev->last_on_list_cnt++; | |
2129 | } | |
2130 | ||
2131 | } | |
2132 | if (Incoming_ThreadInfo.should_stop) | |
2133 | break; | |
2134 | } | |
2135 | if (new_tail != NULL) { | |
2136 | tot_moved_to_tail_cnt++; | |
2137 | list_move_tail(new_tail, &List_Polling_Device_Channels); | |
2138 | } | |
2139 | UNLOCKSEM(&Lock_Polling_Device_Channels); | |
2140 | cur_cycles = get_cycles(); | |
2141 | delta_cycles = cur_cycles - old_cycles; | |
2142 | old_cycles = cur_cycles; | |
2143 | ||
2144 | /* At this point, we have scanned thru all of the | |
2145 | * channels, and at least one of the following is true: | |
2146 | * - there is no input waiting on any of the channels | |
2147 | * - we have received a signal to stop this thread | |
2148 | */ | |
2149 | if (Incoming_ThreadInfo.should_stop) | |
2150 | break; | |
2151 | if (en_smart_wakeup == 0xFF) { | |
2152 | LOGINF("en_smart_wakeup set to 0xff, to force exiting process_incoming"); | |
2153 | break; | |
2154 | } | |
2155 | /* wait for POLLJIFFIES_NORMAL jiffies, or until | |
2156 | * someone wakes up Wakeup_Polling_Device_Channels, | |
2157 | * whichever comes first only do a wait when we have | |
2158 | * been idle for cycles_before_wait cycles. | |
2159 | */ | |
2160 | if (idle_cycles > cycles_before_wait) { | |
2161 | Go_Polling_Device_Channels = 0; | |
2162 | tot_wait_cnt++; | |
2163 | wait_event_timeout(Wakeup_Polling_Device_Channels, | |
2164 | Go_Polling_Device_Channels, | |
2165 | POLLJIFFIES_NORMAL); | |
2166 | Go_Polling_Device_Channels = 1; | |
2167 | } else { | |
2168 | tot_schedule_cnt++; | |
2169 | schedule(); | |
2170 | idle_cycles = idle_cycles + delta_cycles; | |
2171 | } | |
2172 | } | |
2173 | DBGINF("exiting.\n"); | |
2174 | complete_and_exit(&Incoming_ThreadInfo.has_stopped, 0); | |
2175 | } | |
2176 | ||
2177 | static BOOL | |
2178 | Initialize_incoming_thread(void) | |
2179 | { | |
2180 | if (Incoming_Thread_Started) | |
2181 | return TRUE; | |
2182 | if (!uisthread_start(&Incoming_ThreadInfo, | |
2183 | &Process_Incoming, NULL, "dev_incoming")) { | |
2184 | LOGERR("uisthread_start Initialize_incoming_thread ****FAILED"); | |
2185 | return FALSE; | |
2186 | } | |
2187 | Incoming_Thread_Started = TRUE; | |
2188 | return TRUE; | |
2189 | } | |
2190 | ||
2191 | /* Add a new device/channel to the list being processed by | |
2192 | * Process_Incoming(). | |
2193 | * <interrupt> - indicates the function to call periodically. | |
2194 | * <interrupt_context> - indicates the data to pass to the <interrupt> | |
2195 | * function. | |
2196 | */ | |
2197 | void | |
2198 | uislib_enable_channel_interrupts(U32 busNo, U32 devNo, | |
2199 | int (*interrupt)(void *), | |
2200 | void *interrupt_context) | |
2201 | { | |
2202 | struct device_info *dev; | |
2203 | dev = find_dev(busNo, devNo); | |
2204 | if (!dev) { | |
2205 | LOGERR("%s busNo=%d, devNo=%d", __func__, (int) (busNo), | |
2206 | (int) (devNo)); | |
2207 | return; | |
2208 | } | |
2209 | LOCKSEM_UNINTERRUPTIBLE(&Lock_Polling_Device_Channels); | |
2210 | Initialize_incoming_thread(); | |
2211 | dev->interrupt = interrupt; | |
2212 | dev->interrupt_context = interrupt_context; | |
2213 | dev->polling = TRUE; | |
2214 | list_add_tail(&(dev->list_polling_device_channels), | |
2215 | &List_Polling_Device_Channels); | |
2216 | UNLOCKSEM(&Lock_Polling_Device_Channels); | |
2217 | } | |
2218 | EXPORT_SYMBOL_GPL(uislib_enable_channel_interrupts); | |
2219 | ||
2220 | /* Remove a device/channel from the list being processed by | |
2221 | * Process_Incoming(). | |
2222 | */ | |
2223 | void | |
2224 | uislib_disable_channel_interrupts(U32 busNo, U32 devNo) | |
2225 | { | |
2226 | struct device_info *dev; | |
2227 | dev = find_dev(busNo, devNo); | |
2228 | if (!dev) { | |
2229 | LOGERR("%s busNo=%d, devNo=%d", __func__, (int) (busNo), | |
2230 | (int) (devNo)); | |
2231 | return; | |
2232 | } | |
2233 | LOCKSEM_UNINTERRUPTIBLE(&Lock_Polling_Device_Channels); | |
2234 | list_del(&dev->list_polling_device_channels); | |
2235 | dev->polling = FALSE; | |
2236 | dev->interrupt = NULL; | |
2237 | UNLOCKSEM(&Lock_Polling_Device_Channels); | |
2238 | } | |
2239 | EXPORT_SYMBOL_GPL(uislib_disable_channel_interrupts); | |
2240 | ||
2241 | static void | |
2242 | do_wakeup_polling_device_channels(struct work_struct *dummy) | |
2243 | { | |
2244 | if (!Go_Polling_Device_Channels) { | |
2245 | Go_Polling_Device_Channels = 1; | |
2246 | wake_up(&Wakeup_Polling_Device_Channels); | |
2247 | } | |
2248 | } | |
2249 | ||
a8d7f21d KC |
2250 | static DECLARE_WORK(Work_wakeup_polling_device_channels, |
2251 | do_wakeup_polling_device_channels); | |
bac8a4d5 KC |
2252 | |
2253 | /* Call this function when you want to send a hint to Process_Incoming() that | |
2254 | * your device might have more requests. | |
2255 | */ | |
2256 | void | |
2257 | uislib_force_channel_interrupt(U32 busNo, U32 devNo) | |
2258 | { | |
2259 | if (en_smart_wakeup == 0) | |
2260 | return; | |
2261 | if (Go_Polling_Device_Channels) | |
2262 | return; | |
2263 | /* The point of using schedule_work() instead of just doing | |
2264 | * the work inline is to force a slight delay before waking up | |
2265 | * the Process_Incoming() thread. | |
2266 | */ | |
2267 | tot_wakeup_cnt++; | |
2268 | schedule_work(&Work_wakeup_polling_device_channels); | |
2269 | } | |
2270 | EXPORT_SYMBOL_GPL(uislib_force_channel_interrupt); | |
2271 | ||
2272 | /*****************************************************/ | |
2273 | /* Module Init & Exit functions */ | |
2274 | /*****************************************************/ | |
2275 | ||
2276 | static int __init | |
2277 | uislib_mod_init(void) | |
2278 | { | |
2279 | ||
2280 | LOGINF("MONITORAPIS"); | |
2281 | ||
2282 | LOGINF("sizeof(struct uiscmdrsp):%lu bytes\n", | |
2283 | (ulong) sizeof(struct uiscmdrsp)); | |
2284 | LOGINF("sizeof(struct phys_info):%lu\n", | |
2285 | (ulong) sizeof(struct phys_info)); | |
2286 | LOGINF("sizeof(uiscmdrsp_scsi):%lu\n", | |
2287 | (ulong) sizeof(struct uiscmdrsp_scsi)); | |
2288 | LOGINF("sizeof(uiscmdrsp_net):%lu\n", | |
2289 | (ulong) sizeof(struct uiscmdrsp_net)); | |
2290 | LOGINF("sizeof(CONTROLVM_MESSAGE):%lu bytes\n", | |
2291 | (ulong) sizeof(CONTROLVM_MESSAGE)); | |
2292 | LOGINF("sizeof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL):%lu bytes\n", | |
2293 | (ulong) sizeof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL)); | |
2294 | LOGINF("sizeof(CHANNEL_HEADER):%lu bytes\n", | |
2295 | (ulong) sizeof(CHANNEL_HEADER)); | |
2296 | LOGINF("sizeof(ULTRA_IO_CHANNEL_PROTOCOL):%lu bytes\n", | |
2297 | (ulong) sizeof(ULTRA_IO_CHANNEL_PROTOCOL)); | |
2298 | LOGINF("SIZEOF_CMDRSP:%lu bytes\n", SIZEOF_CMDRSP); | |
2299 | LOGINF("SIZEOF_PROTOCOL:%lu bytes\n", SIZEOF_PROTOCOL); | |
2300 | ||
2301 | /* initialize global pointers to NULL */ | |
2302 | BusListHead = NULL; | |
2303 | BusListCount = MaxBusCount = 0; | |
2304 | rwlock_init(&BusListLock); | |
2305 | VirtControlChanFunc = NULL; | |
2306 | ||
2307 | /* Issue VMCALL_GET_CONTROLVM_ADDR to get CtrlChanPhysAddr and | |
2308 | * then map this physical address to a virtual address. */ | |
2309 | POSTCODE_LINUX_2(DRIVER_ENTRY_PC, POSTCODE_SEVERITY_INFO); | |
2310 | ||
2311 | /* create the proc entries for the channels */ | |
2312 | uislib_proc_dir = proc_mkdir(DIR_PROC_ENTRY, NULL); | |
2313 | /* (e.g., for /proc/uislib/vbus/<x>/info) */ | |
2314 | uislib_proc_vbus_dir = proc_mkdir(DIR_VBUS_PROC_ENTRY, uislib_proc_dir); | |
2315 | ||
2316 | vnic_proc_entry = proc_create(VNIC_PROC_ENTRY_FN, 0, uislib_proc_dir, | |
2317 | &proc_vnic_fops); | |
2318 | SET_PROC_OWNER(vnic_proc_entry, THIS_MODULE); | |
2319 | ||
2320 | /* for testing purposes only, create the proc entries for | |
2321 | * enqueuing Control Channel messages */ | |
2322 | chipset_proc_entry = | |
2323 | proc_create(CHIPSET_PROC_ENTRY_FN, 0, uislib_proc_dir, | |
2324 | &proc_chipset_fops); | |
2325 | SET_PROC_OWNER(chipset_proc_entry, THIS_MODULE); | |
2326 | ||
2327 | info_proc_entry = proc_create(INFO_PROC_ENTRY_FN, 0, uislib_proc_dir, | |
2328 | &proc_info_fops); | |
2329 | SET_PROC_OWNER(info_proc_entry, THIS_MODULE); | |
2330 | ||
2331 | platformnumber_proc_entry = | |
2332 | proc_create(PLATFORMNUMBER_PROC_ENTRY_FN, 0, uislib_proc_dir, | |
2333 | &proc_platformnumber_fops); | |
2334 | SET_PROC_OWNER(platformnumberinfo_proc_entry, THIS_MODULE); | |
2335 | ||
2336 | cycles_before_wait_proc_entry = | |
2337 | proc_create(CYCLES_BEFORE_WAIT_PROC_ENTRY_FN, 0, uislib_proc_dir, | |
2338 | &proc_cycles_before_wait_fops); | |
2339 | SET_PROC_OWNER(cycles_before_wait_proc_entry, THIS_MODULE); | |
2340 | ||
2341 | reset_counts_proc_entry = | |
2342 | proc_create(RESET_COUNTS_PROC_ENTRY_FN, 0, uislib_proc_dir, | |
2343 | &proc_reset_counts_fops); | |
2344 | SET_PROC_OWNER(reset_counts_proc_entry, THIS_MODULE); | |
2345 | ||
2346 | smart_wakeup_proc_entry = | |
2347 | proc_create(SMART_WAKEUP_PROC_ENTRY_FN, 0, uislib_proc_dir, | |
2348 | &proc_smart_wakeup_fops); | |
2349 | SET_PROC_OWNER(smart_wakeup_proc_entry, THIS_MODULE); | |
2350 | ||
2351 | #ifdef UISLIB_TEST_PROC | |
2352 | test_proc_entry = proc_create(TEST_PROC_ENTRY_FN, 0, uislib_proc_dir, | |
2353 | &proc_test_fops); | |
2354 | SET_PROC_OWNER(test_proc_entry, THIS_MODULE); | |
2355 | ||
2356 | bus_proc_entry = proc_create(BUS_PROC_ENTRY_FN, 0, uislib_proc_dir, | |
2357 | &proc_bus_fops); | |
2358 | SET_PROC_OWNER(bus_proc_entry, THIS_MODULE); | |
2359 | ||
2360 | dev_proc_entry = proc_create(DEV_PROC_ENTRY_FN, 0, uislib_proc_dir, | |
2361 | &proc_dev_fops); | |
2362 | SET_PROC_OWNER(dev_proc_entry, THIS_MODULE); | |
2363 | #endif /* UISLIB_TEST_PROC */ | |
2364 | POSTCODE_LINUX_3(DRIVER_EXIT_PC, 0, POSTCODE_SEVERITY_INFO); | |
2365 | return 0; | |
2366 | } | |
2367 | ||
2368 | static void __exit | |
2369 | uislib_mod_exit(void) | |
2370 | { | |
2371 | if (disable_proc_entry) | |
2372 | remove_proc_entry(DISABLE_PROC_ENTRY_FN, uislib_proc_dir); | |
2373 | if (cycles_before_wait_proc_entry) | |
2374 | remove_proc_entry(CYCLES_BEFORE_WAIT_PROC_ENTRY_FN, | |
2375 | uislib_proc_dir); | |
2376 | if (reset_counts_proc_entry) | |
2377 | remove_proc_entry(RESET_COUNTS_PROC_ENTRY_FN, uislib_proc_dir); | |
2378 | if (smart_wakeup_proc_entry) | |
2379 | remove_proc_entry(SMART_WAKEUP_PROC_ENTRY_FN, uislib_proc_dir); | |
2380 | if (ctrlchan_proc_entry) | |
2381 | remove_proc_entry(CTRLCHAN_PROC_ENTRY_FN, uislib_proc_dir); | |
2382 | if (pmem_proc_entry) | |
2383 | remove_proc_entry(PMEM_PROC_ENTRY_FN, uislib_proc_dir); | |
2384 | if (info_proc_entry) | |
2385 | remove_proc_entry(INFO_PROC_ENTRY_FN, uislib_proc_dir); | |
2386 | if (switch_proc_entry) | |
2387 | remove_proc_entry(SWITCH_PROC_ENTRY_FN, uislib_proc_dir); | |
2388 | if (extport_proc_entry) | |
2389 | remove_proc_entry(EXTPORT_PROC_ENTRY_FN, uislib_proc_dir); | |
2390 | if (platformnumber_proc_entry) | |
2391 | remove_proc_entry(PLATFORMNUMBER_PROC_ENTRY_FN, | |
2392 | uislib_proc_dir); | |
2393 | if (bus_proc_entry) | |
2394 | remove_proc_entry(BUS_PROC_ENTRY_FN, uislib_proc_dir); | |
2395 | if (dev_proc_entry) | |
2396 | remove_proc_entry(DEV_PROC_ENTRY_FN, uislib_proc_dir); | |
2397 | if (vnic_proc_entry) | |
2398 | remove_proc_entry(VNIC_PROC_ENTRY_FN, uislib_proc_dir); | |
2399 | if (chipset_proc_entry) | |
2400 | remove_proc_entry(CHIPSET_PROC_ENTRY_FN, uislib_proc_dir); | |
2401 | if (uislib_proc_vbus_dir) | |
2402 | remove_proc_entry(DIR_VBUS_PROC_ENTRY, uislib_proc_dir); | |
2403 | if (uislib_proc_dir) | |
2404 | remove_proc_entry(DIR_PROC_ENTRY, NULL); | |
2405 | ||
2406 | if (ProcReadBuffer) { | |
60140462 | 2407 | vfree(ProcReadBuffer); |
bac8a4d5 KC |
2408 | ProcReadBuffer = NULL; |
2409 | } | |
2410 | ||
2411 | DBGINF("goodbye.\n"); | |
2412 | return; | |
2413 | } | |
2414 | ||
2415 | module_init(uislib_mod_init); | |
2416 | module_exit(uislib_mod_exit); | |
2417 | ||
bac8a4d5 KC |
2418 | MODULE_LICENSE("GPL"); |
2419 | MODULE_AUTHOR("Usha Srinivasan"); | |
2420 | MODULE_ALIAS("uislib"); | |
2421 | /* this is extracted during depmod and kept in modules.dep */ |