diff options
| author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-16 13:45:23 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-16 13:45:23 -0400 |
| commit | 02b2318e07f98a7cdf7089a4457a8d62424aa824 (patch) | |
| tree | b40353a9ee6b034e21192ceb5df445fbc5fbdd32 | |
| parent | b91cba52e9b7b3f1c0037908a192d93a869ca9e5 (diff) | |
| parent | d54bc2793ec3405c6b8f217568a82b87bd8a591b (diff) | |
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/sparc-2.6
* 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/sparc-2.6: (26 commits)
[SPARC64]: Fix UP build.
[SPARC64]: dr-cpu unconfigure support.
[SERIAL]: Fix console write locking in sparc drivers.
[SPARC64]: Give more accurate errors in dr_cpu_configure().
[SPARC64]: Clear cpu_{core,sibling}_map[] in smp_fill_in_sib_core_maps()
[SPARC64]: Fix leak when DR added cpu does not bootup.
[SPARC64]: Add ->set_affinity IRQ handlers.
[SPARC64]: Process dr-cpu events in a kthread instead of workqueue.
[SPARC64]: More sensible udelay implementation.
[SPARC64]: SMP build fixes.
[SPARC64]: mdesc.c needs linux/mm.h
[SPARC64]: Fix build regressions added by dr-cpu changes.
[SPARC64]: Unconditionally register vio_bus_type.
[SPARC64]: Initial LDOM cpu hotplug support.
[SPARC64]: Fix setting of variables in LDOM guest.
[SPARC64]: Fix MD property lifetime bugs.
[SPARC64]: Abstract out mdesc accesses for better MD update handling.
[SPARC64]: Use more mearningful names for IRQ registry.
[SPARC64]: Initial domain-services driver.
[SPARC64]: Export powerd facilities for external entities.
...
45 files changed, 8609 insertions, 537 deletions
diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig index 6566d13db04f..b84b6af1241e 100644 --- a/arch/sparc64/Kconfig +++ b/arch/sparc64/Kconfig | |||
| @@ -108,6 +108,15 @@ config SECCOMP | |||
| 108 | 108 | ||
| 109 | source kernel/Kconfig.hz | 109 | source kernel/Kconfig.hz |
| 110 | 110 | ||
| 111 | config HOTPLUG_CPU | ||
| 112 | bool "Support for hot-pluggable CPUs" | ||
| 113 | depends on SMP | ||
| 114 | select HOTPLUG | ||
| 115 | ---help--- | ||
| 116 | Say Y here to experiment with turning CPUs off and on. CPUs | ||
| 117 | can be controlled through /sys/devices/system/cpu/cpu#. | ||
| 118 | Say N if you want to disable CPU hotplug. | ||
| 119 | |||
| 111 | source "init/Kconfig" | 120 | source "init/Kconfig" |
| 112 | 121 | ||
| 113 | config SYSVIPC_COMPAT | 122 | config SYSVIPC_COMPAT |
| @@ -305,6 +314,12 @@ config SUN_IO | |||
| 305 | bool | 314 | bool |
| 306 | default y | 315 | default y |
| 307 | 316 | ||
| 317 | config SUN_LDOMS | ||
| 318 | bool "Sun Logical Domains support" | ||
| 319 | help | ||
| 320 | Say Y here is you want to support virtual devices via | ||
| 321 | Logical Domains. | ||
| 322 | |||
| 308 | config PCI | 323 | config PCI |
| 309 | bool "PCI support" | 324 | bool "PCI support" |
| 310 | select ARCH_SUPPORTS_MSI | 325 | select ARCH_SUPPORTS_MSI |
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index f964bf28d21a..b66876bf410c 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile | |||
| @@ -18,7 +18,7 @@ obj-$(CONFIG_STACKTRACE) += stacktrace.o | |||
| 18 | obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \ | 18 | obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \ |
| 19 | pci_psycho.o pci_sabre.o pci_schizo.o \ | 19 | pci_psycho.o pci_sabre.o pci_schizo.o \ |
| 20 | pci_sun4v.o pci_sun4v_asm.o pci_fire.o | 20 | pci_sun4v.o pci_sun4v_asm.o pci_fire.o |
| 21 | obj-$(CONFIG_SMP) += smp.o trampoline.o | 21 | obj-$(CONFIG_SMP) += smp.o trampoline.o hvtramp.o |
| 22 | obj-$(CONFIG_SPARC32_COMPAT) += sys32.o sys_sparc32.o signal32.o | 22 | obj-$(CONFIG_SPARC32_COMPAT) += sys32.o sys_sparc32.o signal32.o |
| 23 | obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o | 23 | obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o |
| 24 | obj-$(CONFIG_BINFMT_AOUT32) += binfmt_aout32.o | 24 | obj-$(CONFIG_BINFMT_AOUT32) += binfmt_aout32.o |
| @@ -26,6 +26,7 @@ obj-$(CONFIG_MODULES) += module.o | |||
| 26 | obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o | 26 | obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o |
| 27 | obj-$(CONFIG_US2E_FREQ) += us2e_cpufreq.o | 27 | obj-$(CONFIG_US2E_FREQ) += us2e_cpufreq.o |
| 28 | obj-$(CONFIG_KPROBES) += kprobes.o | 28 | obj-$(CONFIG_KPROBES) += kprobes.o |
| 29 | obj-$(CONFIG_SUN_LDOMS) += ldc.o vio.o viohs.o ds.o | ||
| 29 | obj-$(CONFIG_AUDIT) += audit.o | 30 | obj-$(CONFIG_AUDIT) += audit.o |
| 30 | obj-$(CONFIG_AUDIT)$(CONFIG_SPARC32_COMPAT) += compat_audit.o | 31 | obj-$(CONFIG_AUDIT)$(CONFIG_SPARC32_COMPAT) += compat_audit.o |
| 31 | obj-y += $(obj-yy) | 32 | obj-y += $(obj-yy) |
diff --git a/arch/sparc64/kernel/ds.c b/arch/sparc64/kernel/ds.c new file mode 100644 index 000000000000..1c587107cef0 --- /dev/null +++ b/arch/sparc64/kernel/ds.c | |||
| @@ -0,0 +1,1158 @@ | |||
| 1 | /* ds.c: Domain Services driver for Logical Domains | ||
| 2 | * | ||
| 3 | * Copyright (C) 2007 David S. Miller <davem@davemloft.net> | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <linux/kernel.h> | ||
| 7 | #include <linux/module.h> | ||
| 8 | #include <linux/types.h> | ||
| 9 | #include <linux/module.h> | ||
| 10 | #include <linux/string.h> | ||
| 11 | #include <linux/slab.h> | ||
| 12 | #include <linux/sched.h> | ||
| 13 | #include <linux/delay.h> | ||
| 14 | #include <linux/mutex.h> | ||
| 15 | #include <linux/kthread.h> | ||
| 16 | #include <linux/cpu.h> | ||
| 17 | |||
| 18 | #include <asm/ldc.h> | ||
| 19 | #include <asm/vio.h> | ||
| 20 | #include <asm/power.h> | ||
| 21 | #include <asm/mdesc.h> | ||
| 22 | #include <asm/head.h> | ||
| 23 | #include <asm/irq.h> | ||
| 24 | |||
| 25 | #define DRV_MODULE_NAME "ds" | ||
| 26 | #define PFX DRV_MODULE_NAME ": " | ||
| 27 | #define DRV_MODULE_VERSION "1.0" | ||
| 28 | #define DRV_MODULE_RELDATE "Jul 11, 2007" | ||
| 29 | |||
| 30 | static char version[] __devinitdata = | ||
| 31 | DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; | ||
| 32 | MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); | ||
| 33 | MODULE_DESCRIPTION("Sun LDOM domain services driver"); | ||
| 34 | MODULE_LICENSE("GPL"); | ||
| 35 | MODULE_VERSION(DRV_MODULE_VERSION); | ||
| 36 | |||
| 37 | struct ds_msg_tag { | ||
| 38 | __u32 type; | ||
| 39 | #define DS_INIT_REQ 0x00 | ||
| 40 | #define DS_INIT_ACK 0x01 | ||
| 41 | #define DS_INIT_NACK 0x02 | ||
| 42 | #define DS_REG_REQ 0x03 | ||
| 43 | #define DS_REG_ACK 0x04 | ||
| 44 | #define DS_REG_NACK 0x05 | ||
| 45 | #define DS_UNREG_REQ 0x06 | ||
| 46 | #define DS_UNREG_ACK 0x07 | ||
| 47 | #define DS_UNREG_NACK 0x08 | ||
| 48 | #define DS_DATA 0x09 | ||
| 49 | #define DS_NACK 0x0a | ||
| 50 | |||
| 51 | __u32 len; | ||
| 52 | }; | ||
| 53 | |||
| 54 | /* Result codes */ | ||
| 55 | #define DS_OK 0x00 | ||
| 56 | #define DS_REG_VER_NACK 0x01 | ||
| 57 | #define DS_REG_DUP 0x02 | ||
| 58 | #define DS_INV_HDL 0x03 | ||
| 59 | #define DS_TYPE_UNKNOWN 0x04 | ||
| 60 | |||
| 61 | struct ds_version { | ||
| 62 | __u16 major; | ||
| 63 | __u16 minor; | ||
| 64 | }; | ||
| 65 | |||
| 66 | struct ds_ver_req { | ||
| 67 | struct ds_msg_tag tag; | ||
| 68 | struct ds_version ver; | ||
| 69 | }; | ||
| 70 | |||
| 71 | struct ds_ver_ack { | ||
| 72 | struct ds_msg_tag tag; | ||
| 73 | __u16 minor; | ||
| 74 | }; | ||
| 75 | |||
| 76 | struct ds_ver_nack { | ||
| 77 | struct ds_msg_tag tag; | ||
| 78 | __u16 major; | ||
| 79 | }; | ||
| 80 | |||
| 81 | struct ds_reg_req { | ||
| 82 | struct ds_msg_tag tag; | ||
| 83 | __u64 handle; | ||
| 84 | __u16 major; | ||
| 85 | __u16 minor; | ||
| 86 | char svc_id[0]; | ||
| 87 | }; | ||
| 88 | |||
| 89 | struct ds_reg_ack { | ||
| 90 | struct ds_msg_tag tag; | ||
| 91 | __u64 handle; | ||
| 92 | __u16 minor; | ||
| 93 | }; | ||
| 94 | |||
| 95 | struct ds_reg_nack { | ||
| 96 | struct ds_msg_tag tag; | ||
| 97 | __u64 handle; | ||
| 98 | __u16 major; | ||
| 99 | }; | ||
| 100 | |||
| 101 | struct ds_unreg_req { | ||
| 102 | struct ds_msg_tag tag; | ||
| 103 | __u64 handle; | ||
| 104 | }; | ||
| 105 | |||
| 106 | struct ds_unreg_ack { | ||
| 107 | struct ds_msg_tag tag; | ||
| 108 | __u64 handle; | ||
| 109 | }; | ||
| 110 | |||
| 111 | struct ds_unreg_nack { | ||
| 112 | struct ds_msg_tag tag; | ||
| 113 | __u64 handle; | ||
| 114 | }; | ||
| 115 | |||
| 116 | struct ds_data { | ||
| 117 | struct ds_msg_tag tag; | ||
| 118 | __u64 handle; | ||
| 119 | }; | ||
| 120 | |||
| 121 | struct ds_data_nack { | ||
| 122 | struct ds_msg_tag tag; | ||
| 123 | __u64 handle; | ||
| 124 | __u64 result; | ||
| 125 | }; | ||
| 126 | |||
| 127 | struct ds_cap_state { | ||
| 128 | __u64 handle; | ||
| 129 | |||
| 130 | void (*data)(struct ldc_channel *lp, | ||
| 131 | struct ds_cap_state *cp, | ||
| 132 | void *buf, int len); | ||
| 133 | |||
| 134 | const char *service_id; | ||
| 135 | |||
| 136 | u8 state; | ||
| 137 | #define CAP_STATE_UNKNOWN 0x00 | ||
| 138 | #define CAP_STATE_REG_SENT 0x01 | ||
| 139 | #define CAP_STATE_REGISTERED 0x02 | ||
| 140 | }; | ||
| 141 | |||
| 142 | static void md_update_data(struct ldc_channel *lp, struct ds_cap_state *cp, | ||
| 143 | void *buf, int len); | ||
| 144 | static void domain_shutdown_data(struct ldc_channel *lp, | ||
| 145 | struct ds_cap_state *cp, | ||
| 146 | void *buf, int len); | ||
| 147 | static void domain_panic_data(struct ldc_channel *lp, | ||
| 148 | struct ds_cap_state *cp, | ||
| 149 | void *buf, int len); | ||
| 150 | #ifdef CONFIG_HOTPLUG_CPU | ||
| 151 | static void dr_cpu_data(struct ldc_channel *lp, | ||
| 152 | struct ds_cap_state *cp, | ||
| 153 | void *buf, int len); | ||
| 154 | #endif | ||
| 155 | static void ds_pri_data(struct ldc_channel *lp, | ||
| 156 | struct ds_cap_state *cp, | ||
| 157 | void *buf, int len); | ||
| 158 | static void ds_var_data(struct ldc_channel *lp, | ||
| 159 | struct ds_cap_state *cp, | ||
| 160 | void *buf, int len); | ||
| 161 | |||
| 162 | struct ds_cap_state ds_states[] = { | ||
| 163 | { | ||
| 164 | .service_id = "md-update", | ||
| 165 | .data = md_update_data, | ||
| 166 | }, | ||
| 167 | { | ||
| 168 | .service_id = "domain-shutdown", | ||
| 169 | .data = domain_shutdown_data, | ||
| 170 | }, | ||
| 171 | { | ||
| 172 | .service_id = "domain-panic", | ||
| 173 | .data = domain_panic_data, | ||
| 174 | }, | ||
| 175 | #ifdef CONFIG_HOTPLUG_CPU | ||
| 176 | { | ||
| 177 | .service_id = "dr-cpu", | ||
| 178 | .data = dr_cpu_data, | ||
| 179 | }, | ||
| 180 | #endif | ||
| 181 | { | ||
| 182 | .service_id = "pri", | ||
| 183 | .data = ds_pri_data, | ||
| 184 | }, | ||
| 185 | { | ||
| 186 | .service_id = "var-config", | ||
| 187 | .data = ds_var_data, | ||
| 188 | }, | ||
| 189 | { | ||
| 190 | .service_id = "var-config-backup", | ||
| 191 | .data = ds_var_data, | ||
| 192 | }, | ||
| 193 | }; | ||
| 194 | |||
| 195 | static DEFINE_SPINLOCK(ds_lock); | ||
| 196 | |||
| 197 | struct ds_info { | ||
| 198 | struct ldc_channel *lp; | ||
| 199 | u8 hs_state; | ||
| 200 | #define DS_HS_START 0x01 | ||
| 201 | #define DS_HS_DONE 0x02 | ||
| 202 | |||
| 203 | void *rcv_buf; | ||
| 204 | int rcv_buf_len; | ||
| 205 | }; | ||
| 206 | |||
| 207 | static struct ds_info *ds_info; | ||
| 208 | |||
| 209 | static struct ds_cap_state *find_cap(u64 handle) | ||
| 210 | { | ||
| 211 | unsigned int index = handle >> 32; | ||
| 212 | |||
| 213 | if (index >= ARRAY_SIZE(ds_states)) | ||
| 214 | return NULL; | ||
| 215 | return &ds_states[index]; | ||
| 216 | } | ||
| 217 | |||
| 218 | static struct ds_cap_state *find_cap_by_string(const char *name) | ||
| 219 | { | ||
| 220 | int i; | ||
| 221 | |||
| 222 | for (i = 0; i < ARRAY_SIZE(ds_states); i++) { | ||
| 223 | if (strcmp(ds_states[i].service_id, name)) | ||
| 224 | continue; | ||
| 225 | |||
| 226 | return &ds_states[i]; | ||
| 227 | } | ||
| 228 | return NULL; | ||
| 229 | } | ||
| 230 | |||
| 231 | static int ds_send(struct ldc_channel *lp, void *data, int len) | ||
| 232 | { | ||
| 233 | int err, limit = 1000; | ||
| 234 | |||
| 235 | err = -EINVAL; | ||
| 236 | while (limit-- > 0) { | ||
| 237 | err = ldc_write(lp, data, len); | ||
| 238 | if (!err || (err != -EAGAIN)) | ||
| 239 | break; | ||
| 240 | udelay(1); | ||
| 241 | } | ||
| 242 | |||
| 243 | return err; | ||
| 244 | } | ||
| 245 | |||
| 246 | struct ds_md_update_req { | ||
| 247 | __u64 req_num; | ||
| 248 | }; | ||
| 249 | |||
| 250 | struct ds_md_update_res { | ||
| 251 | __u64 req_num; | ||
| 252 | __u32 result; | ||
| 253 | }; | ||
| 254 | |||
| 255 | static void md_update_data(struct ldc_channel *lp, | ||
| 256 | struct ds_cap_state *dp, | ||
| 257 | void *buf, int len) | ||
| 258 | { | ||
| 259 | struct ds_data *dpkt = buf; | ||
| 260 | struct ds_md_update_req *rp; | ||
| 261 | struct { | ||
| 262 | struct ds_data data; | ||
| 263 | struct ds_md_update_res res; | ||
| 264 | } pkt; | ||
| 265 | |||
| 266 | rp = (struct ds_md_update_req *) (dpkt + 1); | ||
| 267 | |||
| 268 | printk(KERN_INFO PFX "Machine description update.\n"); | ||
| 269 | |||
| 270 | memset(&pkt, 0, sizeof(pkt)); | ||
| 271 | pkt.data.tag.type = DS_DATA; | ||
| 272 | pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag); | ||
| 273 | pkt.data.handle = dp->handle; | ||
| 274 | pkt.res.req_num = rp->req_num; | ||
| 275 | pkt.res.result = DS_OK; | ||
| 276 | |||
| 277 | ds_send(lp, &pkt, sizeof(pkt)); | ||
| 278 | |||
| 279 | mdesc_update(); | ||
| 280 | } | ||
| 281 | |||
| 282 | struct ds_shutdown_req { | ||
| 283 | __u64 req_num; | ||
| 284 | __u32 ms_delay; | ||
| 285 | }; | ||
| 286 | |||
| 287 | struct ds_shutdown_res { | ||
| 288 | __u64 req_num; | ||
| 289 | __u32 result; | ||
| 290 | char reason[1]; | ||
| 291 | }; | ||
| 292 | |||
| 293 | static void domain_shutdown_data(struct ldc_channel *lp, | ||
| 294 | struct ds_cap_state *dp, | ||
| 295 | void *buf, int len) | ||
| 296 | { | ||
| 297 | struct ds_data *dpkt = buf; | ||
| 298 | struct ds_shutdown_req *rp; | ||
| 299 | struct { | ||
| 300 | struct ds_data data; | ||
| 301 | struct ds_shutdown_res res; | ||
| 302 | } pkt; | ||
| 303 | |||
| 304 | rp = (struct ds_shutdown_req *) (dpkt + 1); | ||
| 305 | |||
| 306 | printk(KERN_ALERT PFX "Shutdown request from " | ||
| 307 | "LDOM manager received.\n"); | ||
| 308 | |||
| 309 | memset(&pkt, 0, sizeof(pkt)); | ||
| 310 | pkt.data.tag.type = DS_DATA; | ||
| 311 | pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag); | ||
| 312 | pkt.data.handle = dp->handle; | ||
| 313 | pkt.res.req_num = rp->req_num; | ||
| 314 | pkt.res.result = DS_OK; | ||
| 315 | pkt.res.reason[0] = 0; | ||
| 316 | |||
| 317 | ds_send(lp, &pkt, sizeof(pkt)); | ||
| 318 | |||
| 319 | wake_up_powerd(); | ||
| 320 | } | ||
| 321 | |||
| 322 | struct ds_panic_req { | ||
| 323 | __u64 req_num; | ||
| 324 | }; | ||
| 325 | |||
| 326 | struct ds_panic_res { | ||
| 327 | __u64 req_num; | ||
| 328 | __u32 result; | ||
| 329 | char reason[1]; | ||
| 330 | }; | ||
| 331 | |||
| 332 | static void domain_panic_data(struct ldc_channel *lp, | ||
| 333 | struct ds_cap_state *dp, | ||
| 334 | void *buf, int len) | ||
| 335 | { | ||
| 336 | struct ds_data *dpkt = buf; | ||
| 337 | struct ds_panic_req *rp; | ||
| 338 | struct { | ||
| 339 | struct ds_data data; | ||
| 340 | struct ds_panic_res res; | ||
| 341 | } pkt; | ||
| 342 | |||
| 343 | rp = (struct ds_panic_req *) (dpkt + 1); | ||
| 344 | |||
| 345 | printk(KERN_ALERT PFX "Panic request from " | ||
| 346 | "LDOM manager received.\n"); | ||
| 347 | |||
| 348 | memset(&pkt, 0, sizeof(pkt)); | ||
| 349 | pkt.data.tag.type = DS_DATA; | ||
| 350 | pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag); | ||
| 351 | pkt.data.handle = dp->handle; | ||
| 352 | pkt.res.req_num = rp->req_num; | ||
| 353 | pkt.res.result = DS_OK; | ||
| 354 | pkt.res.reason[0] = 0; | ||
| 355 | |||
| 356 | ds_send(lp, &pkt, sizeof(pkt)); | ||
| 357 | |||
| 358 | panic("PANIC requested by LDOM manager."); | ||
| 359 | } | ||
| 360 | |||
| 361 | #ifdef CONFIG_HOTPLUG_CPU | ||
| 362 | struct dr_cpu_tag { | ||
| 363 | __u64 req_num; | ||
| 364 | __u32 type; | ||
| 365 | #define DR_CPU_CONFIGURE 0x43 | ||
| 366 | #define DR_CPU_UNCONFIGURE 0x55 | ||
| 367 | #define DR_CPU_FORCE_UNCONFIGURE 0x46 | ||
| 368 | #define DR_CPU_STATUS 0x53 | ||
| 369 | |||
| 370 | /* Responses */ | ||
| 371 | #define DR_CPU_OK 0x6f | ||
| 372 | #define DR_CPU_ERROR 0x65 | ||
| 373 | |||
| 374 | __u32 num_records; | ||
| 375 | }; | ||
| 376 | |||
| 377 | struct dr_cpu_resp_entry { | ||
| 378 | __u32 cpu; | ||
| 379 | __u32 result; | ||
| 380 | #define DR_CPU_RES_OK 0x00 | ||
| 381 | #define DR_CPU_RES_FAILURE 0x01 | ||
| 382 | #define DR_CPU_RES_BLOCKED 0x02 | ||
| 383 | #define DR_CPU_RES_CPU_NOT_RESPONDING 0x03 | ||
| 384 | #define DR_CPU_RES_NOT_IN_MD 0x04 | ||
| 385 | |||
| 386 | __u32 stat; | ||
| 387 | #define DR_CPU_STAT_NOT_PRESENT 0x00 | ||
| 388 | #define DR_CPU_STAT_UNCONFIGURED 0x01 | ||
| 389 | #define DR_CPU_STAT_CONFIGURED 0x02 | ||
| 390 | |||
| 391 | __u32 str_off; | ||
| 392 | }; | ||
| 393 | |||
| 394 | /* DR cpu requests get queued onto the work list by the | ||
| 395 | * dr_cpu_data() callback. The list is protected by | ||
| 396 | * ds_lock, and processed by dr_cpu_process() in order. | ||
| 397 | */ | ||
| 398 | static LIST_HEAD(dr_cpu_work_list); | ||
| 399 | static DECLARE_WAIT_QUEUE_HEAD(dr_cpu_wait); | ||
| 400 | |||
| 401 | struct dr_cpu_queue_entry { | ||
| 402 | struct list_head list; | ||
| 403 | char req[0]; | ||
| 404 | }; | ||
| 405 | |||
| 406 | static void __dr_cpu_send_error(struct ds_cap_state *cp, struct ds_data *data) | ||
| 407 | { | ||
| 408 | struct dr_cpu_tag *tag = (struct dr_cpu_tag *) (data + 1); | ||
| 409 | struct ds_info *dp = ds_info; | ||
| 410 | struct { | ||
| 411 | struct ds_data data; | ||
| 412 | struct dr_cpu_tag tag; | ||
| 413 | } pkt; | ||
| 414 | int msg_len; | ||
| 415 | |||
| 416 | memset(&pkt, 0, sizeof(pkt)); | ||
| 417 | pkt.data.tag.type = DS_DATA; | ||
| 418 | pkt.data.handle = cp->handle; | ||
| 419 | pkt.tag.req_num = tag->req_num; | ||
| 420 | pkt.tag.type = DR_CPU_ERROR; | ||
| 421 | pkt.tag.num_records = 0; | ||
| 422 | |||
| 423 | msg_len = (sizeof(struct ds_data) + | ||
| 424 | sizeof(struct dr_cpu_tag)); | ||
| 425 | |||
| 426 | pkt.data.tag.len = msg_len - sizeof(struct ds_msg_tag); | ||
| 427 | |||
| 428 | ds_send(dp->lp, &pkt, msg_len); | ||
| 429 | } | ||
| 430 | |||
| 431 | static void dr_cpu_send_error(struct ds_cap_state *cp, struct ds_data *data) | ||
| 432 | { | ||
| 433 | unsigned long flags; | ||
| 434 | |||
| 435 | spin_lock_irqsave(&ds_lock, flags); | ||
| 436 | __dr_cpu_send_error(cp, data); | ||
| 437 | spin_unlock_irqrestore(&ds_lock, flags); | ||
| 438 | } | ||
| 439 | |||
| 440 | #define CPU_SENTINEL 0xffffffff | ||
| 441 | |||
| 442 | static void purge_dups(u32 *list, u32 num_ents) | ||
| 443 | { | ||
| 444 | unsigned int i; | ||
| 445 | |||
| 446 | for (i = 0; i < num_ents; i++) { | ||
| 447 | u32 cpu = list[i]; | ||
| 448 | unsigned int j; | ||
| 449 | |||
| 450 | if (cpu == CPU_SENTINEL) | ||
| 451 | continue; | ||
| 452 | |||
| 453 | for (j = i + 1; j < num_ents; j++) { | ||
| 454 | if (list[j] == cpu) | ||
| 455 | list[j] = CPU_SENTINEL; | ||
| 456 | } | ||
| 457 | } | ||
| 458 | } | ||
| 459 | |||
| 460 | static int dr_cpu_size_response(int ncpus) | ||
| 461 | { | ||
| 462 | return (sizeof(struct ds_data) + | ||
| 463 | sizeof(struct dr_cpu_tag) + | ||
| 464 | (sizeof(struct dr_cpu_resp_entry) * ncpus)); | ||
| 465 | } | ||
| 466 | |||
| 467 | static void dr_cpu_init_response(struct ds_data *resp, u64 req_num, | ||
| 468 | u64 handle, int resp_len, int ncpus, | ||
| 469 | cpumask_t *mask, u32 default_stat) | ||
| 470 | { | ||
| 471 | struct dr_cpu_resp_entry *ent; | ||
| 472 | struct dr_cpu_tag *tag; | ||
| 473 | int i, cpu; | ||
| 474 | |||
| 475 | tag = (struct dr_cpu_tag *) (resp + 1); | ||
| 476 | ent = (struct dr_cpu_resp_entry *) (tag + 1); | ||
| 477 | |||
| 478 | resp->tag.type = DS_DATA; | ||
| 479 | resp->tag.len = resp_len - sizeof(struct ds_msg_tag); | ||
| 480 | resp->handle = handle; | ||
| 481 | tag->req_num = req_num; | ||
| 482 | tag->type = DR_CPU_OK; | ||
| 483 | tag->num_records = ncpus; | ||
| 484 | |||
| 485 | i = 0; | ||
| 486 | for_each_cpu_mask(cpu, *mask) { | ||
| 487 | ent[i].cpu = cpu; | ||
| 488 | ent[i].result = DR_CPU_RES_OK; | ||
| 489 | ent[i].stat = default_stat; | ||
| 490 | i++; | ||
| 491 | } | ||
| 492 | BUG_ON(i != ncpus); | ||
| 493 | } | ||
| 494 | |||
| 495 | static void dr_cpu_mark(struct ds_data *resp, int cpu, int ncpus, | ||
| 496 | u32 res, u32 stat) | ||
| 497 | { | ||
| 498 | struct dr_cpu_resp_entry *ent; | ||
| 499 | struct dr_cpu_tag *tag; | ||
| 500 | int i; | ||
| 501 | |||
| 502 | tag = (struct dr_cpu_tag *) (resp + 1); | ||
| 503 | ent = (struct dr_cpu_resp_entry *) (tag + 1); | ||
| 504 | |||
| 505 | for (i = 0; i < ncpus; i++) { | ||
| 506 | if (ent[i].cpu != cpu) | ||
| 507 | continue; | ||
| 508 | ent[i].result = res; | ||
| 509 | ent[i].stat = stat; | ||
| 510 | break; | ||
| 511 | } | ||
| 512 | } | ||
| 513 | |||
| 514 | static int dr_cpu_configure(struct ds_cap_state *cp, u64 req_num, | ||
| 515 | cpumask_t *mask) | ||
| 516 | { | ||
| 517 | struct ds_data *resp; | ||
| 518 | int resp_len, ncpus, cpu; | ||
| 519 | unsigned long flags; | ||
| 520 | |||
| 521 | ncpus = cpus_weight(*mask); | ||
| 522 | resp_len = dr_cpu_size_response(ncpus); | ||
| 523 | resp = kzalloc(resp_len, GFP_KERNEL); | ||
| 524 | if (!resp) | ||
| 525 | return -ENOMEM; | ||
| 526 | |||
| 527 | dr_cpu_init_response(resp, req_num, cp->handle, | ||
| 528 | resp_len, ncpus, mask, | ||
| 529 | DR_CPU_STAT_CONFIGURED); | ||
| 530 | |||
| 531 | mdesc_fill_in_cpu_data(*mask); | ||
| 532 | |||
| 533 | for_each_cpu_mask(cpu, *mask) { | ||
| 534 | int err; | ||
| 535 | |||
| 536 | printk(KERN_INFO PFX "Starting cpu %d...\n", cpu); | ||
| 537 | err = cpu_up(cpu); | ||
| 538 | if (err) { | ||
| 539 | __u32 res = DR_CPU_RES_FAILURE; | ||
| 540 | __u32 stat = DR_CPU_STAT_UNCONFIGURED; | ||
| 541 | |||
| 542 | if (!cpu_present(cpu)) { | ||
| 543 | /* CPU not present in MD */ | ||
| 544 | res = DR_CPU_RES_NOT_IN_MD; | ||
| 545 | stat = DR_CPU_STAT_NOT_PRESENT; | ||
| 546 | } else if (err == -ENODEV) { | ||
| 547 | /* CPU did not call in successfully */ | ||
| 548 | res = DR_CPU_RES_CPU_NOT_RESPONDING; | ||
| 549 | } | ||
| 550 | |||
| 551 | printk(KERN_INFO PFX "CPU startup failed err=%d\n", | ||
| 552 | err); | ||
| 553 | dr_cpu_mark(resp, cpu, ncpus, res, stat); | ||
| 554 | } | ||
| 555 | } | ||
| 556 | |||
| 557 | spin_lock_irqsave(&ds_lock, flags); | ||
| 558 | ds_send(ds_info->lp, resp, resp_len); | ||
| 559 | spin_unlock_irqrestore(&ds_lock, flags); | ||
| 560 | |||
| 561 | kfree(resp); | ||
| 562 | |||
| 563 | /* Redistribute IRQs, taking into account the new cpus. */ | ||
| 564 | fixup_irqs(); | ||
| 565 | |||
| 566 | return 0; | ||
| 567 | } | ||
| 568 | |||
| 569 | static int dr_cpu_unconfigure(struct ds_cap_state *cp, u64 req_num, | ||
| 570 | cpumask_t *mask) | ||
| 571 | { | ||
| 572 | struct ds_data *resp; | ||
| 573 | int resp_len, ncpus, cpu; | ||
| 574 | unsigned long flags; | ||
| 575 | |||
| 576 | ncpus = cpus_weight(*mask); | ||
| 577 | resp_len = dr_cpu_size_response(ncpus); | ||
| 578 | resp = kzalloc(resp_len, GFP_KERNEL); | ||
| 579 | if (!resp) | ||
| 580 | return -ENOMEM; | ||
| 581 | |||
| 582 | dr_cpu_init_response(resp, req_num, cp->handle, | ||
| 583 | resp_len, ncpus, mask, | ||
| 584 | DR_CPU_STAT_UNCONFIGURED); | ||
| 585 | |||
| 586 | for_each_cpu_mask(cpu, *mask) { | ||
| 587 | int err; | ||
| 588 | |||
| 589 | printk(KERN_INFO PFX "CPU[%d]: Shutting down cpu %d...\n", | ||
| 590 | smp_processor_id(), cpu); | ||
| 591 | err = cpu_down(cpu); | ||
| 592 | if (err) | ||
| 593 | dr_cpu_mark(resp, cpu, ncpus, | ||
| 594 | DR_CPU_RES_FAILURE, | ||
| 595 | DR_CPU_STAT_CONFIGURED); | ||
| 596 | } | ||
| 597 | |||
| 598 | spin_lock_irqsave(&ds_lock, flags); | ||
| 599 | ds_send(ds_info->lp, resp, resp_len); | ||
| 600 | spin_unlock_irqrestore(&ds_lock, flags); | ||
| 601 | |||
| 602 | kfree(resp); | ||
| 603 | |||
| 604 | return 0; | ||
| 605 | } | ||
| 606 | |||
| 607 | static void process_dr_cpu_list(struct ds_cap_state *cp) | ||
| 608 | { | ||
| 609 | struct dr_cpu_queue_entry *qp, *tmp; | ||
| 610 | unsigned long flags; | ||
| 611 | LIST_HEAD(todo); | ||
| 612 | cpumask_t mask; | ||
| 613 | |||
| 614 | spin_lock_irqsave(&ds_lock, flags); | ||
| 615 | list_splice(&dr_cpu_work_list, &todo); | ||
| 616 | INIT_LIST_HEAD(&dr_cpu_work_list); | ||
| 617 | spin_unlock_irqrestore(&ds_lock, flags); | ||
| 618 | |||
| 619 | list_for_each_entry_safe(qp, tmp, &todo, list) { | ||
| 620 | struct ds_data *data = (struct ds_data *) qp->req; | ||
| 621 | struct dr_cpu_tag *tag = (struct dr_cpu_tag *) (data + 1); | ||
| 622 | u32 *cpu_list = (u32 *) (tag + 1); | ||
| 623 | u64 req_num = tag->req_num; | ||
| 624 | unsigned int i; | ||
| 625 | int err; | ||
| 626 | |||
| 627 | switch (tag->type) { | ||
| 628 | case DR_CPU_CONFIGURE: | ||
| 629 | case DR_CPU_UNCONFIGURE: | ||
| 630 | case DR_CPU_FORCE_UNCONFIGURE: | ||
| 631 | break; | ||
| 632 | |||
| 633 | default: | ||
| 634 | dr_cpu_send_error(cp, data); | ||
| 635 | goto next; | ||
| 636 | } | ||
| 637 | |||
| 638 | purge_dups(cpu_list, tag->num_records); | ||
| 639 | |||
| 640 | cpus_clear(mask); | ||
| 641 | for (i = 0; i < tag->num_records; i++) { | ||
| 642 | if (cpu_list[i] == CPU_SENTINEL) | ||
| 643 | continue; | ||
| 644 | |||
| 645 | if (cpu_list[i] < NR_CPUS) | ||
| 646 | cpu_set(cpu_list[i], mask); | ||
| 647 | } | ||
| 648 | |||
| 649 | if (tag->type == DR_CPU_CONFIGURE) | ||
| 650 | err = dr_cpu_configure(cp, req_num, &mask); | ||
| 651 | else | ||
| 652 | err = dr_cpu_unconfigure(cp, req_num, &mask); | ||
| 653 | |||
| 654 | if (err) | ||
| 655 | dr_cpu_send_error(cp, data); | ||
| 656 | |||
| 657 | next: | ||
| 658 | list_del(&qp->list); | ||
| 659 | kfree(qp); | ||
| 660 | } | ||
| 661 | } | ||
| 662 | |||
| 663 | static int dr_cpu_thread(void *__unused) | ||
| 664 | { | ||
| 665 | struct ds_cap_state *cp; | ||
| 666 | DEFINE_WAIT(wait); | ||
| 667 | |||
| 668 | cp = find_cap_by_string("dr-cpu"); | ||
| 669 | |||
| 670 | while (1) { | ||
| 671 | prepare_to_wait(&dr_cpu_wait, &wait, TASK_INTERRUPTIBLE); | ||
| 672 | if (list_empty(&dr_cpu_work_list)) | ||
| 673 | schedule(); | ||
| 674 | finish_wait(&dr_cpu_wait, &wait); | ||
| 675 | |||
| 676 | if (kthread_should_stop()) | ||
| 677 | break; | ||
| 678 | |||
| 679 | process_dr_cpu_list(cp); | ||
| 680 | } | ||
| 681 | |||
| 682 | return 0; | ||
| 683 | } | ||
| 684 | |||
| 685 | static void dr_cpu_data(struct ldc_channel *lp, | ||
| 686 | struct ds_cap_state *dp, | ||
| 687 | void *buf, int len) | ||
| 688 | { | ||
| 689 | struct dr_cpu_queue_entry *qp; | ||
| 690 | struct ds_data *dpkt = buf; | ||
| 691 | struct dr_cpu_tag *rp; | ||
| 692 | |||
| 693 | rp = (struct dr_cpu_tag *) (dpkt + 1); | ||
| 694 | |||
| 695 | qp = kmalloc(sizeof(struct dr_cpu_queue_entry) + len, GFP_ATOMIC); | ||
| 696 | if (!qp) { | ||
| 697 | struct ds_cap_state *cp; | ||
| 698 | |||
| 699 | cp = find_cap_by_string("dr-cpu"); | ||
| 700 | __dr_cpu_send_error(cp, dpkt); | ||
| 701 | } else { | ||
| 702 | memcpy(&qp->req, buf, len); | ||
| 703 | list_add_tail(&qp->list, &dr_cpu_work_list); | ||
| 704 | wake_up(&dr_cpu_wait); | ||
| 705 | } | ||
| 706 | } | ||
| 707 | #endif | ||
| 708 | |||
| 709 | struct ds_pri_msg { | ||
| 710 | __u64 req_num; | ||
| 711 | __u64 type; | ||
| 712 | #define DS_PRI_REQUEST 0x00 | ||
| 713 | #define DS_PRI_DATA 0x01 | ||
| 714 | #define DS_PRI_UPDATE 0x02 | ||
| 715 | }; | ||
| 716 | |||
| 717 | static void ds_pri_data(struct ldc_channel *lp, | ||
| 718 | struct ds_cap_state *dp, | ||
| 719 | void *buf, int len) | ||
| 720 | { | ||
| 721 | struct ds_data *dpkt = buf; | ||
| 722 | struct ds_pri_msg *rp; | ||
| 723 | |||
| 724 | rp = (struct ds_pri_msg *) (dpkt + 1); | ||
| 725 | |||
| 726 | printk(KERN_INFO PFX "PRI REQ [%lx:%lx], len=%d\n", | ||
| 727 | rp->req_num, rp->type, len); | ||
| 728 | } | ||
| 729 | |||
| 730 | struct ds_var_hdr { | ||
| 731 | __u32 type; | ||
| 732 | #define DS_VAR_SET_REQ 0x00 | ||
| 733 | #define DS_VAR_DELETE_REQ 0x01 | ||
| 734 | #define DS_VAR_SET_RESP 0x02 | ||
| 735 | #define DS_VAR_DELETE_RESP 0x03 | ||
| 736 | }; | ||
| 737 | |||
| 738 | struct ds_var_set_msg { | ||
| 739 | struct ds_var_hdr hdr; | ||
| 740 | char name_and_value[0]; | ||
| 741 | }; | ||
| 742 | |||
| 743 | struct ds_var_delete_msg { | ||
| 744 | struct ds_var_hdr hdr; | ||
| 745 | char name[0]; | ||
| 746 | }; | ||
| 747 | |||
| 748 | struct ds_var_resp { | ||
| 749 | struct ds_var_hdr hdr; | ||
| 750 | __u32 result; | ||
| 751 | #define DS_VAR_SUCCESS 0x00 | ||
| 752 | #define DS_VAR_NO_SPACE 0x01 | ||
| 753 | #define DS_VAR_INVALID_VAR 0x02 | ||
| 754 | #define DS_VAR_INVALID_VAL 0x03 | ||
| 755 | #define DS_VAR_NOT_PRESENT 0x04 | ||
| 756 | }; | ||
| 757 | |||
| 758 | static DEFINE_MUTEX(ds_var_mutex); | ||
| 759 | static int ds_var_doorbell; | ||
| 760 | static int ds_var_response; | ||
| 761 | |||
| 762 | static void ds_var_data(struct ldc_channel *lp, | ||
| 763 | struct ds_cap_state *dp, | ||
| 764 | void *buf, int len) | ||
| 765 | { | ||
| 766 | struct ds_data *dpkt = buf; | ||
| 767 | struct ds_var_resp *rp; | ||
| 768 | |||
| 769 | rp = (struct ds_var_resp *) (dpkt + 1); | ||
| 770 | |||
| 771 | if (rp->hdr.type != DS_VAR_SET_RESP && | ||
| 772 | rp->hdr.type != DS_VAR_DELETE_RESP) | ||
| 773 | return; | ||
| 774 | |||
| 775 | ds_var_response = rp->result; | ||
| 776 | wmb(); | ||
| 777 | ds_var_doorbell = 1; | ||
| 778 | } | ||
| 779 | |||
| 780 | void ldom_set_var(const char *var, const char *value) | ||
| 781 | { | ||
| 782 | struct ds_info *dp = ds_info; | ||
| 783 | struct ds_cap_state *cp; | ||
| 784 | |||
| 785 | cp = find_cap_by_string("var-config"); | ||
| 786 | if (cp->state != CAP_STATE_REGISTERED) | ||
| 787 | cp = find_cap_by_string("var-config-backup"); | ||
| 788 | |||
| 789 | if (cp->state == CAP_STATE_REGISTERED) { | ||
| 790 | union { | ||
| 791 | struct { | ||
| 792 | struct ds_data data; | ||
| 793 | struct ds_var_set_msg msg; | ||
| 794 | } header; | ||
| 795 | char all[512]; | ||
| 796 | } pkt; | ||
| 797 | unsigned long flags; | ||
| 798 | char *base, *p; | ||
| 799 | int msg_len, loops; | ||
| 800 | |||
| 801 | memset(&pkt, 0, sizeof(pkt)); | ||
| 802 | pkt.header.data.tag.type = DS_DATA; | ||
| 803 | pkt.header.data.handle = cp->handle; | ||
| 804 | pkt.header.msg.hdr.type = DS_VAR_SET_REQ; | ||
| 805 | base = p = &pkt.header.msg.name_and_value[0]; | ||
| 806 | strcpy(p, var); | ||
| 807 | p += strlen(var) + 1; | ||
| 808 | strcpy(p, value); | ||
| 809 | p += strlen(value) + 1; | ||
| 810 | |||
| 811 | msg_len = (sizeof(struct ds_data) + | ||
| 812 | sizeof(struct ds_var_set_msg) + | ||
| 813 | (p - base)); | ||
| 814 | msg_len = (msg_len + 3) & ~3; | ||
| 815 | pkt.header.data.tag.len = msg_len - sizeof(struct ds_msg_tag); | ||
| 816 | |||
| 817 | mutex_lock(&ds_var_mutex); | ||
| 818 | |||
| 819 | spin_lock_irqsave(&ds_lock, flags); | ||
| 820 | ds_var_doorbell = 0; | ||
| 821 | ds_var_response = -1; | ||
| 822 | |||
| 823 | ds_send(dp->lp, &pkt, msg_len); | ||
| 824 | spin_unlock_irqrestore(&ds_lock, flags); | ||
| 825 | |||
| 826 | loops = 1000; | ||
| 827 | while (ds_var_doorbell == 0) { | ||
| 828 | if (loops-- < 0) | ||
| 829 | break; | ||
| 830 | barrier(); | ||
| 831 | udelay(100); | ||
| 832 | } | ||
| 833 | |||
| 834 | mutex_unlock(&ds_var_mutex); | ||
| 835 | |||
| 836 | if (ds_var_doorbell == 0 || | ||
| 837 | ds_var_response != DS_VAR_SUCCESS) | ||
| 838 | printk(KERN_ERR PFX "var-config [%s:%s] " | ||
| 839 | "failed, response(%d).\n", | ||
| 840 | var, value, | ||
| 841 | ds_var_response); | ||
| 842 | } else { | ||
| 843 | printk(KERN_ERR PFX "var-config not registered so " | ||
| 844 | "could not set (%s) variable to (%s).\n", | ||
| 845 | var, value); | ||
| 846 | } | ||
| 847 | } | ||
| 848 | |||
| 849 | void ldom_reboot(const char *boot_command) | ||
| 850 | { | ||
| 851 | /* Don't bother with any of this if the boot_command | ||
| 852 | * is empty. | ||
| 853 | */ | ||
| 854 | if (boot_command && strlen(boot_command)) { | ||
| 855 | char full_boot_str[256]; | ||
| 856 | |||
| 857 | strcpy(full_boot_str, "boot "); | ||
| 858 | strcpy(full_boot_str + strlen("boot "), boot_command); | ||
| 859 | |||
| 860 | ldom_set_var("reboot-command", full_boot_str); | ||
| 861 | } | ||
| 862 | sun4v_mach_sir(); | ||
| 863 | } | ||
| 864 | |||
| 865 | void ldom_power_off(void) | ||
| 866 | { | ||
| 867 | sun4v_mach_exit(0); | ||
| 868 | } | ||
| 869 | |||
| 870 | static void ds_conn_reset(struct ds_info *dp) | ||
| 871 | { | ||
| 872 | printk(KERN_ERR PFX "ds_conn_reset() from %p\n", | ||
| 873 | __builtin_return_address(0)); | ||
| 874 | } | ||
| 875 | |||
| 876 | static int register_services(struct ds_info *dp) | ||
| 877 | { | ||
| 878 | struct ldc_channel *lp = dp->lp; | ||
| 879 | int i; | ||
| 880 | |||
| 881 | for (i = 0; i < ARRAY_SIZE(ds_states); i++) { | ||
| 882 | struct { | ||
| 883 | struct ds_reg_req req; | ||
| 884 | u8 id_buf[256]; | ||
| 885 | } pbuf; | ||
| 886 | struct ds_cap_state *cp = &ds_states[i]; | ||
| 887 | int err, msg_len; | ||
| 888 | u64 new_count; | ||
| 889 | |||
| 890 | if (cp->state == CAP_STATE_REGISTERED) | ||
| 891 | continue; | ||
| 892 | |||
| 893 | new_count = sched_clock() & 0xffffffff; | ||
| 894 | cp->handle = ((u64) i << 32) | new_count; | ||
| 895 | |||
| 896 | msg_len = (sizeof(struct ds_reg_req) + | ||
| 897 | strlen(cp->service_id)); | ||
| 898 | |||
| 899 | memset(&pbuf, 0, sizeof(pbuf)); | ||
| 900 | pbuf.req.tag.type = DS_REG_REQ; | ||
| 901 | pbuf.req.tag.len = (msg_len - sizeof(struct ds_msg_tag)); | ||
| 902 | pbuf.req.handle = cp->handle; | ||
| 903 | pbuf.req.major = 1; | ||
| 904 | pbuf.req.minor = 0; | ||
| 905 | strcpy(pbuf.req.svc_id, cp->service_id); | ||
| 906 | |||
| 907 | err = ds_send(lp, &pbuf, msg_len); | ||
| 908 | if (err > 0) | ||
| 909 | cp->state = CAP_STATE_REG_SENT; | ||
| 910 | } | ||
| 911 | return 0; | ||
| 912 | } | ||
| 913 | |||
| 914 | static int ds_handshake(struct ds_info *dp, struct ds_msg_tag *pkt) | ||
| 915 | { | ||
| 916 | |||
| 917 | if (dp->hs_state == DS_HS_START) { | ||
| 918 | if (pkt->type != DS_INIT_ACK) | ||
| 919 | goto conn_reset; | ||
| 920 | |||
| 921 | dp->hs_state = DS_HS_DONE; | ||
| 922 | |||
| 923 | return register_services(dp); | ||
| 924 | } | ||
| 925 | |||
| 926 | if (dp->hs_state != DS_HS_DONE) | ||
| 927 | goto conn_reset; | ||
| 928 | |||
| 929 | if (pkt->type == DS_REG_ACK) { | ||
| 930 | struct ds_reg_ack *ap = (struct ds_reg_ack *) pkt; | ||
| 931 | struct ds_cap_state *cp = find_cap(ap->handle); | ||
| 932 | |||
| 933 | if (!cp) { | ||
| 934 | printk(KERN_ERR PFX "REG ACK for unknown handle %lx\n", | ||
| 935 | ap->handle); | ||
| 936 | return 0; | ||
| 937 | } | ||
| 938 | printk(KERN_INFO PFX "Registered %s service.\n", | ||
| 939 | cp->service_id); | ||
| 940 | cp->state = CAP_STATE_REGISTERED; | ||
| 941 | } else if (pkt->type == DS_REG_NACK) { | ||
| 942 | struct ds_reg_nack *np = (struct ds_reg_nack *) pkt; | ||
| 943 | struct ds_cap_state *cp = find_cap(np->handle); | ||
| 944 | |||
| 945 | if (!cp) { | ||
| 946 | printk(KERN_ERR PFX "REG NACK for " | ||
| 947 | "unknown handle %lx\n", | ||
| 948 | np->handle); | ||
| 949 | return 0; | ||
| 950 | } | ||
| 951 | printk(KERN_INFO PFX "Could not register %s service\n", | ||
| 952 | cp->service_id); | ||
| 953 | cp->state = CAP_STATE_UNKNOWN; | ||
| 954 | } | ||
| 955 | |||
| 956 | return 0; | ||
| 957 | |||
| 958 | conn_reset: | ||
| 959 | ds_conn_reset(dp); | ||
| 960 | return -ECONNRESET; | ||
| 961 | } | ||
| 962 | |||
| 963 | static int ds_data(struct ds_info *dp, struct ds_msg_tag *pkt, int len) | ||
| 964 | { | ||
| 965 | struct ds_data *dpkt = (struct ds_data *) pkt; | ||
| 966 | struct ds_cap_state *cp = find_cap(dpkt->handle); | ||
| 967 | |||
| 968 | if (!cp) { | ||
| 969 | struct ds_data_nack nack = { | ||
| 970 | .tag = { | ||
| 971 | .type = DS_NACK, | ||
| 972 | .len = (sizeof(struct ds_data_nack) - | ||
| 973 | sizeof(struct ds_msg_tag)), | ||
| 974 | }, | ||
| 975 | .handle = dpkt->handle, | ||
| 976 | .result = DS_INV_HDL, | ||
| 977 | }; | ||
| 978 | |||
| 979 | printk(KERN_ERR PFX "Data for unknown handle %lu\n", | ||
| 980 | dpkt->handle); | ||
| 981 | ds_send(dp->lp, &nack, sizeof(nack)); | ||
| 982 | } else { | ||
| 983 | cp->data(dp->lp, cp, dpkt, len); | ||
| 984 | } | ||
| 985 | return 0; | ||
| 986 | } | ||
| 987 | |||
| 988 | static void ds_up(struct ds_info *dp) | ||
| 989 | { | ||
| 990 | struct ldc_channel *lp = dp->lp; | ||
| 991 | struct ds_ver_req req; | ||
| 992 | int err; | ||
| 993 | |||
| 994 | req.tag.type = DS_INIT_REQ; | ||
| 995 | req.tag.len = sizeof(req) - sizeof(struct ds_msg_tag); | ||
| 996 | req.ver.major = 1; | ||
| 997 | req.ver.minor = 0; | ||
| 998 | |||
| 999 | err = ds_send(lp, &req, sizeof(req)); | ||
| 1000 | if (err > 0) | ||
| 1001 | dp->hs_state = DS_HS_START; | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | static void ds_event(void *arg, int event) | ||
| 1005 | { | ||
| 1006 | struct ds_info *dp = arg; | ||
| 1007 | struct ldc_channel *lp = dp->lp; | ||
| 1008 | unsigned long flags; | ||
| 1009 | int err; | ||
| 1010 | |||
| 1011 | spin_lock_irqsave(&ds_lock, flags); | ||
| 1012 | |||
| 1013 | if (event == LDC_EVENT_UP) { | ||
| 1014 | ds_up(dp); | ||
| 1015 | spin_unlock_irqrestore(&ds_lock, flags); | ||
| 1016 | return; | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | if (event != LDC_EVENT_DATA_READY) { | ||
| 1020 | printk(KERN_WARNING PFX "Unexpected LDC event %d\n", event); | ||
| 1021 | spin_unlock_irqrestore(&ds_lock, flags); | ||
| 1022 | return; | ||
| 1023 | } | ||
| 1024 | |||
| 1025 | err = 0; | ||
| 1026 | while (1) { | ||
| 1027 | struct ds_msg_tag *tag; | ||
| 1028 | |||
| 1029 | err = ldc_read(lp, dp->rcv_buf, sizeof(*tag)); | ||
| 1030 | |||
| 1031 | if (unlikely(err < 0)) { | ||
| 1032 | if (err == -ECONNRESET) | ||
| 1033 | ds_conn_reset(dp); | ||
| 1034 | break; | ||
| 1035 | } | ||
| 1036 | if (err == 0) | ||
| 1037 | break; | ||
| 1038 | |||
| 1039 | tag = dp->rcv_buf; | ||
| 1040 | err = ldc_read(lp, tag + 1, tag->len); | ||
| 1041 | |||
| 1042 | if (unlikely(err < 0)) { | ||
| 1043 | if (err == -ECONNRESET) | ||
| 1044 | ds_conn_reset(dp); | ||
| 1045 | break; | ||
| 1046 | } | ||
| 1047 | if (err < tag->len) | ||
| 1048 | break; | ||
| 1049 | |||
| 1050 | if (tag->type < DS_DATA) | ||
| 1051 | err = ds_handshake(dp, dp->rcv_buf); | ||
| 1052 | else | ||
| 1053 | err = ds_data(dp, dp->rcv_buf, | ||
| 1054 | sizeof(*tag) + err); | ||
| 1055 | if (err == -ECONNRESET) | ||
| 1056 | break; | ||
| 1057 | } | ||
| 1058 | |||
| 1059 | spin_unlock_irqrestore(&ds_lock, flags); | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | static int __devinit ds_probe(struct vio_dev *vdev, | ||
| 1063 | const struct vio_device_id *id) | ||
| 1064 | { | ||
| 1065 | static int ds_version_printed; | ||
| 1066 | struct ldc_channel_config ds_cfg = { | ||
| 1067 | .event = ds_event, | ||
| 1068 | .mtu = 4096, | ||
| 1069 | .mode = LDC_MODE_STREAM, | ||
| 1070 | }; | ||
| 1071 | struct ldc_channel *lp; | ||
| 1072 | struct ds_info *dp; | ||
| 1073 | int err; | ||
| 1074 | |||
| 1075 | if (ds_version_printed++ == 0) | ||
| 1076 | printk(KERN_INFO "%s", version); | ||
| 1077 | |||
| 1078 | dp = kzalloc(sizeof(*dp), GFP_KERNEL); | ||
| 1079 | err = -ENOMEM; | ||
| 1080 | if (!dp) | ||
| 1081 | goto out_err; | ||
| 1082 | |||
| 1083 | dp->rcv_buf = kzalloc(4096, GFP_KERNEL); | ||
| 1084 | if (!dp->rcv_buf) | ||
| 1085 | goto out_free_dp; | ||
| 1086 | |||
| 1087 | dp->rcv_buf_len = 4096; | ||
| 1088 | |||
| 1089 | ds_cfg.tx_irq = vdev->tx_irq; | ||
| 1090 | ds_cfg.rx_irq = vdev->rx_irq; | ||
| 1091 | |||
| 1092 | lp = ldc_alloc(vdev->channel_id, &ds_cfg, dp); | ||
| 1093 | if (IS_ERR(lp)) { | ||
| 1094 | err = PTR_ERR(lp); | ||
| 1095 | goto out_free_rcv_buf; | ||
| 1096 | } | ||
| 1097 | dp->lp = lp; | ||
| 1098 | |||
| 1099 | err = ldc_bind(lp, "DS"); | ||
| 1100 | if (err) | ||
| 1101 | goto out_free_ldc; | ||
| 1102 | |||
| 1103 | ds_info = dp; | ||
| 1104 | |||
| 1105 | start_powerd(); | ||
| 1106 | |||
| 1107 | return err; | ||
| 1108 | |||
| 1109 | out_free_ldc: | ||
| 1110 | ldc_free(dp->lp); | ||
| 1111 | |||
| 1112 | out_free_rcv_buf: | ||
| 1113 | kfree(dp->rcv_buf); | ||
| 1114 | |||
| 1115 | out_free_dp: | ||
| 1116 | kfree(dp); | ||
| 1117 | |||
| 1118 | out_err: | ||
| 1119 | return err; | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | static int ds_remove(struct vio_dev *vdev) | ||
| 1123 | { | ||
| 1124 | return 0; | ||
| 1125 | } | ||
| 1126 | |||
| 1127 | static struct vio_device_id ds_match[] = { | ||
| 1128 | { | ||
| 1129 | .type = "domain-services-port", | ||
| 1130 | }, | ||
| 1131 | {}, | ||
| 1132 | }; | ||
| 1133 | |||
| 1134 | static struct vio_driver ds_driver = { | ||
| 1135 | .id_table = ds_match, | ||
| 1136 | .probe = ds_probe, | ||
| 1137 | .remove = ds_remove, | ||
| 1138 | .driver = { | ||
| 1139 | .name = "ds", | ||
| 1140 | .owner = THIS_MODULE, | ||
| 1141 | } | ||
| 1142 | }; | ||
| 1143 | |||
| 1144 | static int __init ds_init(void) | ||
| 1145 | { | ||
| 1146 | int i; | ||
| 1147 | |||
| 1148 | for (i = 0; i < ARRAY_SIZE(ds_states); i++) | ||
| 1149 | ds_states[i].handle = ((u64)i << 32); | ||
| 1150 | |||
| 1151 | #ifdef CONFIG_HOTPLUG_CPU | ||
| 1152 | kthread_run(dr_cpu_thread, NULL, "kdrcpud"); | ||
| 1153 | #endif | ||
| 1154 | |||
| 1155 | return vio_register_driver(&ds_driver); | ||
| 1156 | } | ||
| 1157 | |||
| 1158 | subsys_initcall(ds_init); | ||
diff --git a/arch/sparc64/kernel/hvtramp.S b/arch/sparc64/kernel/hvtramp.S new file mode 100644 index 000000000000..76a090e2c2a8 --- /dev/null +++ b/arch/sparc64/kernel/hvtramp.S | |||
| @@ -0,0 +1,139 @@ | |||
| 1 | /* hvtramp.S: Hypervisor start-cpu trampoline code. | ||
| 2 | * | ||
| 3 | * Copyright (C) 2007 David S. Miller <davem@davemloft.net> | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <asm/thread_info.h> | ||
| 7 | #include <asm/hypervisor.h> | ||
| 8 | #include <asm/scratchpad.h> | ||
| 9 | #include <asm/spitfire.h> | ||
| 10 | #include <asm/hvtramp.h> | ||
| 11 | #include <asm/pstate.h> | ||
| 12 | #include <asm/ptrace.h> | ||
| 13 | #include <asm/asi.h> | ||
| 14 | |||
| 15 | .text | ||
| 16 | .align 8 | ||
| 17 | .globl hv_cpu_startup, hv_cpu_startup_end | ||
| 18 | |||
| 19 | /* This code executes directly out of the hypervisor | ||
| 20 | * with physical addressing (va==pa). %o0 contains | ||
| 21 | * our client argument which for Linux points to | ||
| 22 | * a descriptor data structure which defines the | ||
| 23 | * MMU entries we need to load up. | ||
| 24 | * | ||
| 25 | * After we set things up we enable the MMU and call | ||
| 26 | * into the kernel. | ||
| 27 | * | ||
| 28 | * First setup basic privileged cpu state. | ||
| 29 | */ | ||
| 30 | hv_cpu_startup: | ||
| 31 | wrpr %g0, 0, %gl | ||
| 32 | wrpr %g0, 15, %pil | ||
| 33 | wrpr %g0, 0, %canrestore | ||
| 34 | wrpr %g0, 0, %otherwin | ||
| 35 | wrpr %g0, 6, %cansave | ||
| 36 | wrpr %g0, 6, %cleanwin | ||
| 37 | wrpr %g0, 0, %cwp | ||
| 38 | wrpr %g0, 0, %wstate | ||
| 39 | wrpr %g0, 0, %tl | ||
| 40 | |||
| 41 | sethi %hi(sparc64_ttable_tl0), %g1 | ||
| 42 | wrpr %g1, %tba | ||
| 43 | |||
| 44 | mov %o0, %l0 | ||
| 45 | |||
| 46 | lduw [%l0 + HVTRAMP_DESCR_CPU], %g1 | ||
| 47 | mov SCRATCHPAD_CPUID, %g2 | ||
| 48 | stxa %g1, [%g2] ASI_SCRATCHPAD | ||
| 49 | |||
| 50 | ldx [%l0 + HVTRAMP_DESCR_FAULT_INFO_VA], %g2 | ||
| 51 | stxa %g2, [%g0] ASI_SCRATCHPAD | ||
| 52 | |||
| 53 | mov 0, %l1 | ||
| 54 | lduw [%l0 + HVTRAMP_DESCR_NUM_MAPPINGS], %l2 | ||
| 55 | add %l0, HVTRAMP_DESCR_MAPS, %l3 | ||
| 56 | |||
| 57 | 1: ldx [%l3 + HVTRAMP_MAPPING_VADDR], %o0 | ||
| 58 | clr %o1 | ||
| 59 | ldx [%l3 + HVTRAMP_MAPPING_TTE], %o2 | ||
| 60 | mov HV_MMU_IMMU | HV_MMU_DMMU, %o3 | ||
| 61 | mov HV_FAST_MMU_MAP_PERM_ADDR, %o5 | ||
| 62 | ta HV_FAST_TRAP | ||
| 63 | |||
| 64 | brnz,pn %o0, 80f | ||
| 65 | nop | ||
| 66 | |||
| 67 | add %l1, 1, %l1 | ||
| 68 | cmp %l1, %l2 | ||
| 69 | blt,a,pt %xcc, 1b | ||
| 70 | add %l3, HVTRAMP_MAPPING_SIZE, %l3 | ||
| 71 | |||
| 72 | ldx [%l0 + HVTRAMP_DESCR_FAULT_INFO_PA], %o0 | ||
| 73 | mov HV_FAST_MMU_FAULT_AREA_CONF, %o5 | ||
| 74 | ta HV_FAST_TRAP | ||
| 75 | |||
| 76 | brnz,pn %o0, 80f | ||
| 77 | nop | ||
| 78 | |||
| 79 | wrpr %g0, (PSTATE_PRIV | PSTATE_PEF), %pstate | ||
| 80 | |||
| 81 | ldx [%l0 + HVTRAMP_DESCR_THREAD_REG], %l6 | ||
| 82 | |||
| 83 | mov 1, %o0 | ||
| 84 | set 1f, %o1 | ||
| 85 | mov HV_FAST_MMU_ENABLE, %o5 | ||
| 86 | ta HV_FAST_TRAP | ||
| 87 | |||
| 88 | ba,pt %xcc, 80f | ||
| 89 | nop | ||
| 90 | |||
| 91 | 1: | ||
| 92 | wr %g0, 0, %fprs | ||
| 93 | wr %g0, ASI_P, %asi | ||
| 94 | |||
| 95 | mov PRIMARY_CONTEXT, %g7 | ||
| 96 | stxa %g0, [%g7] ASI_MMU | ||
| 97 | membar #Sync | ||
| 98 | |||
| 99 | mov SECONDARY_CONTEXT, %g7 | ||
| 100 | stxa %g0, [%g7] ASI_MMU | ||
| 101 | membar #Sync | ||
| 102 | |||
| 103 | mov %l6, %g6 | ||
| 104 | ldx [%g6 + TI_TASK], %g4 | ||
| 105 | |||
| 106 | mov 1, %g5 | ||
| 107 | sllx %g5, THREAD_SHIFT, %g5 | ||
| 108 | sub %g5, (STACKFRAME_SZ + STACK_BIAS), %g5 | ||
| 109 | add %g6, %g5, %sp | ||
| 110 | mov 0, %fp | ||
| 111 | |||
| 112 | call init_irqwork_curcpu | ||
| 113 | nop | ||
| 114 | call hard_smp_processor_id | ||
| 115 | nop | ||
| 116 | |||
| 117 | mov %o0, %o1 | ||
| 118 | mov 0, %o0 | ||
| 119 | mov 0, %o2 | ||
| 120 | call sun4v_init_mondo_queues | ||
| 121 | mov 1, %o3 | ||
| 122 | |||
| 123 | call init_cur_cpu_trap | ||
| 124 | mov %g6, %o0 | ||
| 125 | |||
| 126 | wrpr %g0, (PSTATE_PRIV | PSTATE_PEF | PSTATE_IE), %pstate | ||
| 127 | |||
| 128 | call smp_callin | ||
| 129 | nop | ||
| 130 | call cpu_idle | ||
| 131 | mov 0, %o0 | ||
| 132 | call cpu_panic | ||
| 133 | nop | ||
| 134 | |||
| 135 | 80: ba,pt %xcc, 80b | ||
| 136 | nop | ||
| 137 | |||
| 138 | .align 8 | ||
| 139 | hv_cpu_startup_end: | ||
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index 6b6165d36fd8..8cb3358674f5 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c | |||
| @@ -293,6 +293,11 @@ static void sun4u_irq_enable(unsigned int virt_irq) | |||
| 293 | } | 293 | } |
| 294 | } | 294 | } |
| 295 | 295 | ||
| 296 | static void sun4u_set_affinity(unsigned int virt_irq, cpumask_t mask) | ||
| 297 | { | ||
| 298 | sun4u_irq_enable(virt_irq); | ||
| 299 | } | ||
| 300 | |||
| 296 | static void sun4u_irq_disable(unsigned int virt_irq) | 301 | static void sun4u_irq_disable(unsigned int virt_irq) |
| 297 | { | 302 | { |
| 298 | struct irq_handler_data *data = get_irq_chip_data(virt_irq); | 303 | struct irq_handler_data *data = get_irq_chip_data(virt_irq); |
| @@ -309,6 +314,10 @@ static void sun4u_irq_disable(unsigned int virt_irq) | |||
| 309 | static void sun4u_irq_end(unsigned int virt_irq) | 314 | static void sun4u_irq_end(unsigned int virt_irq) |
| 310 | { | 315 | { |
| 311 | struct irq_handler_data *data = get_irq_chip_data(virt_irq); | 316 | struct irq_handler_data *data = get_irq_chip_data(virt_irq); |
| 317 | struct irq_desc *desc = irq_desc + virt_irq; | ||
| 318 | |||
| 319 | if (unlikely(desc->status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
| 320 | return; | ||
| 312 | 321 | ||
| 313 | if (likely(data)) | 322 | if (likely(data)) |
| 314 | upa_writeq(ICLR_IDLE, data->iclr); | 323 | upa_writeq(ICLR_IDLE, data->iclr); |
| @@ -340,6 +349,24 @@ static void sun4v_irq_enable(unsigned int virt_irq) | |||
| 340 | } | 349 | } |
| 341 | } | 350 | } |
| 342 | 351 | ||
| 352 | static void sun4v_set_affinity(unsigned int virt_irq, cpumask_t mask) | ||
| 353 | { | ||
| 354 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); | ||
| 355 | unsigned int ino = bucket - &ivector_table[0]; | ||
| 356 | |||
| 357 | if (likely(bucket)) { | ||
| 358 | unsigned long cpuid; | ||
| 359 | int err; | ||
| 360 | |||
| 361 | cpuid = irq_choose_cpu(virt_irq); | ||
| 362 | |||
| 363 | err = sun4v_intr_settarget(ino, cpuid); | ||
| 364 | if (err != HV_EOK) | ||
| 365 | printk("sun4v_intr_settarget(%x,%lu): err(%d)\n", | ||
| 366 | ino, cpuid, err); | ||
| 367 | } | ||
| 368 | } | ||
| 369 | |||
| 343 | static void sun4v_irq_disable(unsigned int virt_irq) | 370 | static void sun4v_irq_disable(unsigned int virt_irq) |
| 344 | { | 371 | { |
| 345 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); | 372 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); |
| @@ -373,6 +400,10 @@ static void sun4v_irq_end(unsigned int virt_irq) | |||
| 373 | { | 400 | { |
| 374 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); | 401 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); |
| 375 | unsigned int ino = bucket - &ivector_table[0]; | 402 | unsigned int ino = bucket - &ivector_table[0]; |
| 403 | struct irq_desc *desc = irq_desc + virt_irq; | ||
| 404 | |||
| 405 | if (unlikely(desc->status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
| 406 | return; | ||
| 376 | 407 | ||
| 377 | if (likely(bucket)) { | 408 | if (likely(bucket)) { |
| 378 | int err; | 409 | int err; |
| @@ -418,6 +449,28 @@ static void sun4v_virq_enable(unsigned int virt_irq) | |||
| 418 | } | 449 | } |
| 419 | } | 450 | } |
| 420 | 451 | ||
| 452 | static void sun4v_virt_set_affinity(unsigned int virt_irq, cpumask_t mask) | ||
| 453 | { | ||
| 454 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); | ||
| 455 | unsigned int ino = bucket - &ivector_table[0]; | ||
| 456 | |||
| 457 | if (likely(bucket)) { | ||
| 458 | unsigned long cpuid, dev_handle, dev_ino; | ||
| 459 | int err; | ||
| 460 | |||
| 461 | cpuid = irq_choose_cpu(virt_irq); | ||
| 462 | |||
| 463 | dev_handle = ino & IMAP_IGN; | ||
| 464 | dev_ino = ino & IMAP_INO; | ||
| 465 | |||
| 466 | err = sun4v_vintr_set_target(dev_handle, dev_ino, cpuid); | ||
| 467 | if (err != HV_EOK) | ||
| 468 | printk("sun4v_vintr_set_target(%lx,%lx,%lu): " | ||
| 469 | "err(%d)\n", | ||
| 470 | dev_handle, dev_ino, cpuid, err); | ||
| 471 | } | ||
| 472 | } | ||
| 473 | |||
| 421 | static void sun4v_virq_disable(unsigned int virt_irq) | 474 | static void sun4v_virq_disable(unsigned int virt_irq) |
| 422 | { | 475 | { |
| 423 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); | 476 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); |
| @@ -443,6 +496,10 @@ static void sun4v_virq_end(unsigned int virt_irq) | |||
| 443 | { | 496 | { |
| 444 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); | 497 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); |
| 445 | unsigned int ino = bucket - &ivector_table[0]; | 498 | unsigned int ino = bucket - &ivector_table[0]; |
| 499 | struct irq_desc *desc = irq_desc + virt_irq; | ||
| 500 | |||
| 501 | if (unlikely(desc->status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
| 502 | return; | ||
| 446 | 503 | ||
| 447 | if (likely(bucket)) { | 504 | if (likely(bucket)) { |
| 448 | unsigned long dev_handle, dev_ino; | 505 | unsigned long dev_handle, dev_ino; |
| @@ -477,6 +534,7 @@ static struct irq_chip sun4u_irq = { | |||
| 477 | .enable = sun4u_irq_enable, | 534 | .enable = sun4u_irq_enable, |
| 478 | .disable = sun4u_irq_disable, | 535 | .disable = sun4u_irq_disable, |
| 479 | .end = sun4u_irq_end, | 536 | .end = sun4u_irq_end, |
| 537 | .set_affinity = sun4u_set_affinity, | ||
| 480 | }; | 538 | }; |
| 481 | 539 | ||
| 482 | static struct irq_chip sun4u_irq_ack = { | 540 | static struct irq_chip sun4u_irq_ack = { |
| @@ -485,6 +543,7 @@ static struct irq_chip sun4u_irq_ack = { | |||
| 485 | .disable = sun4u_irq_disable, | 543 | .disable = sun4u_irq_disable, |
| 486 | .ack = run_pre_handler, | 544 | .ack = run_pre_handler, |
| 487 | .end = sun4u_irq_end, | 545 | .end = sun4u_irq_end, |
| 546 | .set_affinity = sun4u_set_affinity, | ||
| 488 | }; | 547 | }; |
| 489 | 548 | ||
| 490 | static struct irq_chip sun4v_irq = { | 549 | static struct irq_chip sun4v_irq = { |
| @@ -492,6 +551,7 @@ static struct irq_chip sun4v_irq = { | |||
| 492 | .enable = sun4v_irq_enable, | 551 | .enable = sun4v_irq_enable, |
| 493 | .disable = sun4v_irq_disable, | 552 | .disable = sun4v_irq_disable, |
| 494 | .end = sun4v_irq_end, | 553 | .end = sun4v_irq_end, |
| 554 | .set_affinity = sun4v_set_affinity, | ||
| 495 | }; | 555 | }; |
| 496 | 556 | ||
| 497 | static struct irq_chip sun4v_irq_ack = { | 557 | static struct irq_chip sun4v_irq_ack = { |
| @@ -500,6 +560,7 @@ static struct irq_chip sun4v_irq_ack = { | |||
| 500 | .disable = sun4v_irq_disable, | 560 | .disable = sun4v_irq_disable, |
| 501 | .ack = run_pre_handler, | 561 | .ack = run_pre_handler, |
| 502 | .end = sun4v_irq_end, | 562 | .end = sun4v_irq_end, |
| 563 | .set_affinity = sun4v_set_affinity, | ||
| 503 | }; | 564 | }; |
| 504 | 565 | ||
| 505 | #ifdef CONFIG_PCI_MSI | 566 | #ifdef CONFIG_PCI_MSI |
| @@ -511,6 +572,7 @@ static struct irq_chip sun4v_msi = { | |||
| 511 | .disable = sun4v_msi_disable, | 572 | .disable = sun4v_msi_disable, |
| 512 | .ack = run_pre_handler, | 573 | .ack = run_pre_handler, |
| 513 | .end = sun4v_irq_end, | 574 | .end = sun4v_irq_end, |
| 575 | .set_affinity = sun4v_set_affinity, | ||
| 514 | }; | 576 | }; |
| 515 | #endif | 577 | #endif |
| 516 | 578 | ||
| @@ -519,6 +581,7 @@ static struct irq_chip sun4v_virq = { | |||
| 519 | .enable = sun4v_virq_enable, | 581 | .enable = sun4v_virq_enable, |
| 520 | .disable = sun4v_virq_disable, | 582 | .disable = sun4v_virq_disable, |
| 521 | .end = sun4v_virq_end, | 583 | .end = sun4v_virq_end, |
| 584 | .set_affinity = sun4v_virt_set_affinity, | ||
| 522 | }; | 585 | }; |
| 523 | 586 | ||
| 524 | static struct irq_chip sun4v_virq_ack = { | 587 | static struct irq_chip sun4v_virq_ack = { |
| @@ -527,6 +590,7 @@ static struct irq_chip sun4v_virq_ack = { | |||
| 527 | .disable = sun4v_virq_disable, | 590 | .disable = sun4v_virq_disable, |
| 528 | .ack = run_pre_handler, | 591 | .ack = run_pre_handler, |
| 529 | .end = sun4v_virq_end, | 592 | .end = sun4v_virq_end, |
| 593 | .set_affinity = sun4v_virt_set_affinity, | ||
| 530 | }; | 594 | }; |
| 531 | 595 | ||
| 532 | void irq_install_pre_handler(int virt_irq, | 596 | void irq_install_pre_handler(int virt_irq, |
| @@ -739,6 +803,26 @@ void handler_irq(int irq, struct pt_regs *regs) | |||
| 739 | set_irq_regs(old_regs); | 803 | set_irq_regs(old_regs); |
| 740 | } | 804 | } |
| 741 | 805 | ||
| 806 | #ifdef CONFIG_HOTPLUG_CPU | ||
| 807 | void fixup_irqs(void) | ||
| 808 | { | ||
| 809 | unsigned int irq; | ||
| 810 | |||
| 811 | for (irq = 0; irq < NR_IRQS; irq++) { | ||
| 812 | unsigned long flags; | ||
| 813 | |||
| 814 | spin_lock_irqsave(&irq_desc[irq].lock, flags); | ||
| 815 | if (irq_desc[irq].action && | ||
| 816 | !(irq_desc[irq].status & IRQ_PER_CPU)) { | ||
| 817 | if (irq_desc[irq].chip->set_affinity) | ||
| 818 | irq_desc[irq].chip->set_affinity(irq, | ||
| 819 | irq_desc[irq].affinity); | ||
| 820 | } | ||
| 821 | spin_unlock_irqrestore(&irq_desc[irq].lock, flags); | ||
| 822 | } | ||
| 823 | } | ||
| 824 | #endif | ||
| 825 | |||
| 742 | struct sun5_timer { | 826 | struct sun5_timer { |
| 743 | u64 count0; | 827 | u64 count0; |
| 744 | u64 limit0; | 828 | u64 limit0; |
diff --git a/arch/sparc64/kernel/ldc.c b/arch/sparc64/kernel/ldc.c new file mode 100644 index 000000000000..85a2be0b0962 --- /dev/null +++ b/arch/sparc64/kernel/ldc.c | |||
| @@ -0,0 +1,2373 @@ | |||
| 1 | /* ldc.c: Logical Domain Channel link-layer protocol driver. | ||
| 2 | * | ||
| 3 | * Copyright (C) 2007 David S. Miller <davem@davemloft.net> | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <linux/kernel.h> | ||
| 7 | #include <linux/module.h> | ||
| 8 | #include <linux/slab.h> | ||
| 9 | #include <linux/spinlock.h> | ||
| 10 | #include <linux/delay.h> | ||
| 11 | #include <linux/errno.h> | ||
| 12 | #include <linux/string.h> | ||
| 13 | #include <linux/scatterlist.h> | ||
| 14 | #include <linux/interrupt.h> | ||
| 15 | #include <linux/list.h> | ||
| 16 | #include <linux/init.h> | ||
| 17 | |||
| 18 | #include <asm/hypervisor.h> | ||
| 19 | #include <asm/iommu.h> | ||
| 20 | #include <asm/page.h> | ||
| 21 | #include <asm/ldc.h> | ||
| 22 | #include <asm/mdesc.h> | ||
| 23 | |||
| 24 | #define DRV_MODULE_NAME "ldc" | ||
| 25 | #define PFX DRV_MODULE_NAME ": " | ||
| 26 | #define DRV_MODULE_VERSION "1.0" | ||
| 27 | #define DRV_MODULE_RELDATE "June 25, 2007" | ||
| 28 | |||
| 29 | static char version[] __devinitdata = | ||
| 30 | DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; | ||
| 31 | #define LDC_PACKET_SIZE 64 | ||
| 32 | |||
| 33 | /* Packet header layout for unreliable and reliable mode frames. | ||
| 34 | * When in RAW mode, packets are simply straight 64-byte payloads | ||
| 35 | * with no headers. | ||
| 36 | */ | ||
| 37 | struct ldc_packet { | ||
| 38 | u8 type; | ||
| 39 | #define LDC_CTRL 0x01 | ||
| 40 | #define LDC_DATA 0x02 | ||
| 41 | #define LDC_ERR 0x10 | ||
| 42 | |||
| 43 | u8 stype; | ||
| 44 | #define LDC_INFO 0x01 | ||
| 45 | #define LDC_ACK 0x02 | ||
| 46 | #define LDC_NACK 0x04 | ||
| 47 | |||
| 48 | u8 ctrl; | ||
| 49 | #define LDC_VERS 0x01 /* Link Version */ | ||
| 50 | #define LDC_RTS 0x02 /* Request To Send */ | ||
| 51 | #define LDC_RTR 0x03 /* Ready To Receive */ | ||
| 52 | #define LDC_RDX 0x04 /* Ready for Data eXchange */ | ||
| 53 | #define LDC_CTRL_MSK 0x0f | ||
| 54 | |||
| 55 | u8 env; | ||
| 56 | #define LDC_LEN 0x3f | ||
| 57 | #define LDC_FRAG_MASK 0xc0 | ||
| 58 | #define LDC_START 0x40 | ||
| 59 | #define LDC_STOP 0x80 | ||
| 60 | |||
| 61 | u32 seqid; | ||
| 62 | |||
| 63 | union { | ||
| 64 | u8 u_data[LDC_PACKET_SIZE - 8]; | ||
| 65 | struct { | ||
| 66 | u32 pad; | ||
| 67 | u32 ackid; | ||
| 68 | u8 r_data[LDC_PACKET_SIZE - 8 - 8]; | ||
| 69 | } r; | ||
| 70 | } u; | ||
| 71 | }; | ||
| 72 | |||
| 73 | struct ldc_version { | ||
| 74 | u16 major; | ||
| 75 | u16 minor; | ||
| 76 | }; | ||
| 77 | |||
| 78 | /* Ordered from largest major to lowest. */ | ||
| 79 | static struct ldc_version ver_arr[] = { | ||
| 80 | { .major = 1, .minor = 0 }, | ||
| 81 | }; | ||
| 82 | |||
| 83 | #define LDC_DEFAULT_MTU (4 * LDC_PACKET_SIZE) | ||
| 84 | #define LDC_DEFAULT_NUM_ENTRIES (PAGE_SIZE / LDC_PACKET_SIZE) | ||
| 85 | |||
| 86 | struct ldc_channel; | ||
| 87 | |||
| 88 | struct ldc_mode_ops { | ||
| 89 | int (*write)(struct ldc_channel *, const void *, unsigned int); | ||
| 90 | int (*read)(struct ldc_channel *, void *, unsigned int); | ||
| 91 | }; | ||
| 92 | |||
| 93 | static const struct ldc_mode_ops raw_ops; | ||
| 94 | static const struct ldc_mode_ops nonraw_ops; | ||
| 95 | static const struct ldc_mode_ops stream_ops; | ||
| 96 | |||
| 97 | int ldom_domaining_enabled; | ||
| 98 | |||
| 99 | struct ldc_iommu { | ||
| 100 | /* Protects arena alloc/free. */ | ||
| 101 | spinlock_t lock; | ||
| 102 | struct iommu_arena arena; | ||
| 103 | struct ldc_mtable_entry *page_table; | ||
| 104 | }; | ||
| 105 | |||
| 106 | struct ldc_channel { | ||
| 107 | /* Protects all operations that depend upon channel state. */ | ||
| 108 | spinlock_t lock; | ||
| 109 | |||
| 110 | unsigned long id; | ||
| 111 | |||
| 112 | u8 *mssbuf; | ||
| 113 | u32 mssbuf_len; | ||
| 114 | u32 mssbuf_off; | ||
| 115 | |||
| 116 | struct ldc_packet *tx_base; | ||
| 117 | unsigned long tx_head; | ||
| 118 | unsigned long tx_tail; | ||
| 119 | unsigned long tx_num_entries; | ||
| 120 | unsigned long tx_ra; | ||
| 121 | |||
| 122 | unsigned long tx_acked; | ||
| 123 | |||
| 124 | struct ldc_packet *rx_base; | ||
| 125 | unsigned long rx_head; | ||
| 126 | unsigned long rx_tail; | ||
| 127 | unsigned long rx_num_entries; | ||
| 128 | unsigned long rx_ra; | ||
| 129 | |||
| 130 | u32 rcv_nxt; | ||
| 131 | u32 snd_nxt; | ||
| 132 | |||
| 133 | unsigned long chan_state; | ||
| 134 | |||
| 135 | struct ldc_channel_config cfg; | ||
| 136 | void *event_arg; | ||
| 137 | |||
| 138 | const struct ldc_mode_ops *mops; | ||
| 139 | |||
| 140 | struct ldc_iommu iommu; | ||
| 141 | |||
| 142 | struct ldc_version ver; | ||
| 143 | |||
| 144 | u8 hs_state; | ||
| 145 | #define LDC_HS_CLOSED 0x00 | ||
| 146 | #define LDC_HS_OPEN 0x01 | ||
| 147 | #define LDC_HS_GOTVERS 0x02 | ||
| 148 | #define LDC_HS_SENTRTR 0x03 | ||
| 149 | #define LDC_HS_GOTRTR 0x04 | ||
| 150 | #define LDC_HS_COMPLETE 0x10 | ||
| 151 | |||
| 152 | u8 flags; | ||
| 153 | #define LDC_FLAG_ALLOCED_QUEUES 0x01 | ||
| 154 | #define LDC_FLAG_REGISTERED_QUEUES 0x02 | ||
| 155 | #define LDC_FLAG_REGISTERED_IRQS 0x04 | ||
| 156 | #define LDC_FLAG_RESET 0x10 | ||
| 157 | |||
| 158 | u8 mss; | ||
| 159 | u8 state; | ||
| 160 | |||
| 161 | #define LDC_IRQ_NAME_MAX 32 | ||
| 162 | char rx_irq_name[LDC_IRQ_NAME_MAX]; | ||
| 163 | char tx_irq_name[LDC_IRQ_NAME_MAX]; | ||
| 164 | |||
| 165 | struct hlist_head mh_list; | ||
| 166 | |||
| 167 | struct hlist_node list; | ||
| 168 | }; | ||
| 169 | |||
| 170 | #define ldcdbg(TYPE, f, a...) \ | ||
| 171 | do { if (lp->cfg.debug & LDC_DEBUG_##TYPE) \ | ||
| 172 | printk(KERN_INFO PFX "ID[%lu] " f, lp->id, ## a); \ | ||
| 173 | } while (0) | ||
| 174 | |||
| 175 | static const char *state_to_str(u8 state) | ||
| 176 | { | ||
| 177 | switch (state) { | ||
| 178 | case LDC_STATE_INVALID: | ||
| 179 | return "INVALID"; | ||
| 180 | case LDC_STATE_INIT: | ||
| 181 | return "INIT"; | ||
| 182 | case LDC_STATE_BOUND: | ||
| 183 | return "BOUND"; | ||
| 184 | case LDC_STATE_READY: | ||
| 185 | return "READY"; | ||
| 186 | case LDC_STATE_CONNECTED: | ||
| 187 | return "CONNECTED"; | ||
| 188 | default: | ||
| 189 | return "<UNKNOWN>"; | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | static void ldc_set_state(struct ldc_channel *lp, u8 state) | ||
| 194 | { | ||
| 195 | ldcdbg(STATE, "STATE (%s) --> (%s)\n", | ||
| 196 | state_to_str(lp->state), | ||
| 197 | state_to_str(state)); | ||
| 198 | |||
| 199 | lp->state = state; | ||
| 200 | } | ||
| 201 | |||
| 202 | static unsigned long __advance(unsigned long off, unsigned long num_entries) | ||
| 203 | { | ||
| 204 | off += LDC_PACKET_SIZE; | ||
| 205 | if (off == (num_entries * LDC_PACKET_SIZE)) | ||
| 206 | off = 0; | ||
| 207 | |||
| 208 | return off; | ||
| 209 | } | ||
| 210 | |||
| 211 | static unsigned long rx_advance(struct ldc_channel *lp, unsigned long off) | ||
| 212 | { | ||
| 213 | return __advance(off, lp->rx_num_entries); | ||
| 214 | } | ||
| 215 | |||
| 216 | static unsigned long tx_advance(struct ldc_channel *lp, unsigned long off) | ||
| 217 | { | ||
| 218 | return __advance(off, lp->tx_num_entries); | ||
| 219 | } | ||
| 220 | |||
| 221 | static struct ldc_packet *handshake_get_tx_packet(struct ldc_channel *lp, | ||
| 222 | unsigned long *new_tail) | ||
| 223 | { | ||
| 224 | struct ldc_packet *p; | ||
| 225 | unsigned long t; | ||
| 226 | |||
| 227 | t = tx_advance(lp, lp->tx_tail); | ||
| 228 | if (t == lp->tx_head) | ||
| 229 | return NULL; | ||
| 230 | |||
| 231 | *new_tail = t; | ||
| 232 | |||
| 233 | p = lp->tx_base; | ||
| 234 | return p + (lp->tx_tail / LDC_PACKET_SIZE); | ||
| 235 | } | ||
| 236 | |||
| 237 | /* When we are in reliable or stream mode, have to track the next packet | ||
| 238 | * we haven't gotten an ACK for in the TX queue using tx_acked. We have | ||
| 239 | * to be careful not to stomp over the queue past that point. During | ||
| 240 | * the handshake, we don't have TX data packets pending in the queue | ||
| 241 | * and that's why handshake_get_tx_packet() need not be mindful of | ||
| 242 | * lp->tx_acked. | ||
| 243 | */ | ||
| 244 | static unsigned long head_for_data(struct ldc_channel *lp) | ||
| 245 | { | ||
| 246 | if (lp->cfg.mode == LDC_MODE_STREAM) | ||
| 247 | return lp->tx_acked; | ||
| 248 | return lp->tx_head; | ||
| 249 | } | ||
| 250 | |||
| 251 | static int tx_has_space_for(struct ldc_channel *lp, unsigned int size) | ||
| 252 | { | ||
| 253 | unsigned long limit, tail, new_tail, diff; | ||
| 254 | unsigned int mss; | ||
| 255 | |||
| 256 | limit = head_for_data(lp); | ||
| 257 | tail = lp->tx_tail; | ||
| 258 | new_tail = tx_advance(lp, tail); | ||
| 259 | if (new_tail == limit) | ||
| 260 | return 0; | ||
| 261 | |||
| 262 | if (limit > new_tail) | ||
| 263 | diff = limit - new_tail; | ||
| 264 | else | ||
| 265 | diff = (limit + | ||
| 266 | ((lp->tx_num_entries * LDC_PACKET_SIZE) - new_tail)); | ||
| 267 | diff /= LDC_PACKET_SIZE; | ||
| 268 | mss = lp->mss; | ||
| 269 | |||
| 270 | if (diff * mss < size) | ||
| 271 | return 0; | ||
| 272 | |||
| 273 | return 1; | ||
| 274 | } | ||
| 275 | |||
| 276 | static struct ldc_packet *data_get_tx_packet(struct ldc_channel *lp, | ||
| 277 | unsigned long *new_tail) | ||
| 278 | { | ||
| 279 | struct ldc_packet *p; | ||
| 280 | unsigned long h, t; | ||
| 281 | |||
| 282 | h = head_for_data(lp); | ||
| 283 | t = tx_advance(lp, lp->tx_tail); | ||
| 284 | if (t == h) | ||
| 285 | return NULL; | ||
| 286 | |||
| 287 | *new_tail = t; | ||
| 288 | |||
| 289 | p = lp->tx_base; | ||
| 290 | return p + (lp->tx_tail / LDC_PACKET_SIZE); | ||
| 291 | } | ||
| 292 | |||
| 293 | static int set_tx_tail(struct ldc_channel *lp, unsigned long tail) | ||
| 294 | { | ||
| 295 | unsigned long orig_tail = lp->tx_tail; | ||
| 296 | int limit = 1000; | ||
| 297 | |||
| 298 | lp->tx_tail = tail; | ||
| 299 | while (limit-- > 0) { | ||
| 300 | unsigned long err; | ||
| 301 | |||
| 302 | err = sun4v_ldc_tx_set_qtail(lp->id, tail); | ||
| 303 | if (!err) | ||
| 304 | return 0; | ||
| 305 | |||
| 306 | if (err != HV_EWOULDBLOCK) { | ||
| 307 | lp->tx_tail = orig_tail; | ||
| 308 | return -EINVAL; | ||
| 309 | } | ||
| 310 | udelay(1); | ||
| 311 | } | ||
| 312 | |||
| 313 | lp->tx_tail = orig_tail; | ||
| 314 | return -EBUSY; | ||
| 315 | } | ||
| 316 | |||
| 317 | /* This just updates the head value in the hypervisor using | ||
| 318 | * a polling loop with a timeout. The caller takes care of | ||
| 319 | * upating software state representing the head change, if any. | ||
| 320 | */ | ||
| 321 | static int __set_rx_head(struct ldc_channel *lp, unsigned long head) | ||
| 322 | { | ||
| 323 | int limit = 1000; | ||
| 324 | |||
| 325 | while (limit-- > 0) { | ||
| 326 | unsigned long err; | ||
| 327 | |||
| 328 | err = sun4v_ldc_rx_set_qhead(lp->id, head); | ||
| 329 | if (!err) | ||
| 330 | return 0; | ||
| 331 | |||
| 332 | if (err != HV_EWOULDBLOCK) | ||
| 333 | return -EINVAL; | ||
| 334 | |||
| 335 | udelay(1); | ||
| 336 | } | ||
| 337 | |||
| 338 | return -EBUSY; | ||
| 339 | } | ||
| 340 | |||
| 341 | static int send_tx_packet(struct ldc_channel *lp, | ||
| 342 | struct ldc_packet *p, | ||
| 343 | unsigned long new_tail) | ||
| 344 | { | ||
| 345 | BUG_ON(p != (lp->tx_base + (lp->tx_tail / LDC_PACKET_SIZE))); | ||
| 346 | |||
| 347 | return set_tx_tail(lp, new_tail); | ||
| 348 | } | ||
| 349 | |||
| 350 | static struct ldc_packet *handshake_compose_ctrl(struct ldc_channel *lp, | ||
| 351 | u8 stype, u8 ctrl, | ||
| 352 | void *data, int dlen, | ||
| 353 | unsigned long *new_tail) | ||
| 354 | { | ||
| 355 | struct ldc_packet *p = handshake_get_tx_packet(lp, new_tail); | ||
| 356 | |||
| 357 | if (p) { | ||
| 358 | memset(p, 0, sizeof(*p)); | ||
| 359 | p->type = LDC_CTRL; | ||
| 360 | p->stype = stype; | ||
| 361 | p->ctrl = ctrl; | ||
| 362 | if (data) | ||
| 363 | memcpy(p->u.u_data, data, dlen); | ||
| 364 | } | ||
| 365 | return p; | ||
| 366 | } | ||
| 367 | |||
| 368 | static int start_handshake(struct ldc_channel *lp) | ||
| 369 | { | ||
| 370 | struct ldc_packet *p; | ||
| 371 | struct ldc_version *ver; | ||
| 372 | unsigned long new_tail; | ||
| 373 | |||
| 374 | ver = &ver_arr[0]; | ||
| 375 | |||
| 376 | ldcdbg(HS, "SEND VER INFO maj[%u] min[%u]\n", | ||
| 377 | ver->major, ver->minor); | ||
| 378 | |||
| 379 | p = handshake_compose_ctrl(lp, LDC_INFO, LDC_VERS, | ||
| 380 | ver, sizeof(*ver), &new_tail); | ||
| 381 | if (p) { | ||
| 382 | int err = send_tx_packet(lp, p, new_tail); | ||
| 383 | if (!err) | ||
| 384 | lp->flags &= ~LDC_FLAG_RESET; | ||
| 385 | return err; | ||
| 386 | } | ||
| 387 | return -EBUSY; | ||
| 388 | } | ||
| 389 | |||
| 390 | static int send_version_nack(struct ldc_channel *lp, | ||
| 391 | u16 major, u16 minor) | ||
| 392 | { | ||
| 393 | struct ldc_packet *p; | ||
| 394 | struct ldc_version ver; | ||
| 395 | unsigned long new_tail; | ||
| 396 | |||
| 397 | ver.major = major; | ||
| 398 | ver.minor = minor; | ||
| 399 | |||
| 400 | p = handshake_compose_ctrl(lp, LDC_NACK, LDC_VERS, | ||
| 401 | &ver, sizeof(ver), &new_tail); | ||
| 402 | if (p) { | ||
| 403 | ldcdbg(HS, "SEND VER NACK maj[%u] min[%u]\n", | ||
| 404 | ver.major, ver.minor); | ||
| 405 | |||
| 406 | return send_tx_packet(lp, p, new_tail); | ||
| 407 | } | ||
| 408 | return -EBUSY; | ||
| 409 | } | ||
| 410 | |||
| 411 | static int send_version_ack(struct ldc_channel *lp, | ||
| 412 | struct ldc_version *vp) | ||
| 413 | { | ||
| 414 | struct ldc_packet *p; | ||
| 415 | unsigned long new_tail; | ||
| 416 | |||
| 417 | p = handshake_compose_ctrl(lp, LDC_ACK, LDC_VERS, | ||
| 418 | vp, sizeof(*vp), &new_tail); | ||
| 419 | if (p) { | ||
| 420 | ldcdbg(HS, "SEND VER ACK maj[%u] min[%u]\n", | ||
| 421 | vp->major, vp->minor); | ||
| 422 | |||
| 423 | return send_tx_packet(lp, p, new_tail); | ||
| 424 | } | ||
| 425 | return -EBUSY; | ||
| 426 | } | ||
| 427 | |||
| 428 | static int send_rts(struct ldc_channel *lp) | ||
| 429 | { | ||
| 430 | struct ldc_packet *p; | ||
| 431 | unsigned long new_tail; | ||
| 432 | |||
| 433 | p = handshake_compose_ctrl(lp, LDC_INFO, LDC_RTS, NULL, 0, | ||
| 434 | &new_tail); | ||
| 435 | if (p) { | ||
| 436 | p->env = lp->cfg.mode; | ||
| 437 | p->seqid = 0; | ||
| 438 | lp->rcv_nxt = 0; | ||
| 439 | |||
| 440 | ldcdbg(HS, "SEND RTS env[0x%x] seqid[0x%x]\n", | ||
| 441 | p->env, p->seqid); | ||
| 442 | |||
| 443 | return send_tx_packet(lp, p, new_tail); | ||
| 444 | } | ||
| 445 | return -EBUSY; | ||
| 446 | } | ||
| 447 | |||
| 448 | static int send_rtr(struct ldc_channel *lp) | ||
| 449 | { | ||
| 450 | struct ldc_packet *p; | ||
| 451 | unsigned long new_tail; | ||
| 452 | |||
| 453 | p = handshake_compose_ctrl(lp, LDC_INFO, LDC_RTR, NULL, 0, | ||
| 454 | &new_tail); | ||
| 455 | if (p) { | ||
| 456 | p->env = lp->cfg.mode; | ||
| 457 | p->seqid = 0; | ||
| 458 | |||
| 459 | ldcdbg(HS, "SEND RTR env[0x%x] seqid[0x%x]\n", | ||
| 460 | p->env, p->seqid); | ||
| 461 | |||
| 462 | return send_tx_packet(lp, p, new_tail); | ||
| 463 | } | ||
| 464 | return -EBUSY; | ||
| 465 | } | ||
| 466 | |||
| 467 | static int send_rdx(struct ldc_channel *lp) | ||
| 468 | { | ||
| 469 | struct ldc_packet *p; | ||
| 470 | unsigned long new_tail; | ||
| 471 | |||
| 472 | p = handshake_compose_ctrl(lp, LDC_INFO, LDC_RDX, NULL, 0, | ||
| 473 | &new_tail); | ||
| 474 | if (p) { | ||
| 475 | p->env = 0; | ||
| 476 | p->seqid = ++lp->snd_nxt; | ||
| 477 | p->u.r.ackid = lp->rcv_nxt; | ||
| 478 | |||
| 479 | ldcdbg(HS, "SEND RDX env[0x%x] seqid[0x%x] ackid[0x%x]\n", | ||
| 480 | p->env, p->seqid, p->u.r.ackid); | ||
| 481 | |||
| 482 | return send_tx_packet(lp, p, new_tail); | ||
| 483 | } | ||
| 484 | return -EBUSY; | ||
| 485 | } | ||
| 486 | |||
| 487 | static int send_data_nack(struct ldc_channel *lp, struct ldc_packet *data_pkt) | ||
| 488 | { | ||
| 489 | struct ldc_packet *p; | ||
| 490 | unsigned long new_tail; | ||
| 491 | int err; | ||
| 492 | |||
| 493 | p = data_get_tx_packet(lp, &new_tail); | ||
| 494 | if (!p) | ||
| 495 | return -EBUSY; | ||
| 496 | memset(p, 0, sizeof(*p)); | ||
| 497 | p->type = data_pkt->type; | ||
| 498 | p->stype = LDC_NACK; | ||
| 499 | p->ctrl = data_pkt->ctrl & LDC_CTRL_MSK; | ||
| 500 | p->seqid = lp->snd_nxt + 1; | ||
| 501 | p->u.r.ackid = lp->rcv_nxt; | ||
| 502 | |||
| 503 | ldcdbg(HS, "SEND DATA NACK type[0x%x] ctl[0x%x] seq[0x%x] ack[0x%x]\n", | ||
| 504 | p->type, p->ctrl, p->seqid, p->u.r.ackid); | ||
| 505 | |||
| 506 | err = send_tx_packet(lp, p, new_tail); | ||
| 507 | if (!err) | ||
| 508 | lp->snd_nxt++; | ||
| 509 | |||
| 510 | return err; | ||
| 511 | } | ||
| 512 | |||
| 513 | static int ldc_abort(struct ldc_channel *lp) | ||
| 514 | { | ||
| 515 | unsigned long hv_err; | ||
| 516 | |||
| 517 | ldcdbg(STATE, "ABORT\n"); | ||
| 518 | |||
| 519 | /* We report but do not act upon the hypervisor errors because | ||
| 520 | * there really isn't much we can do if they fail at this point. | ||
| 521 | */ | ||
| 522 | hv_err = sun4v_ldc_tx_qconf(lp->id, lp->tx_ra, lp->tx_num_entries); | ||
| 523 | if (hv_err) | ||
| 524 | printk(KERN_ERR PFX "ldc_abort: " | ||
| 525 | "sun4v_ldc_tx_qconf(%lx,%lx,%lx) failed, err=%lu\n", | ||
| 526 | lp->id, lp->tx_ra, lp->tx_num_entries, hv_err); | ||
| 527 | |||
| 528 | hv_err = sun4v_ldc_tx_get_state(lp->id, | ||
| 529 | &lp->tx_head, | ||
| 530 | &lp->tx_tail, | ||
| 531 | &lp->chan_state); | ||
| 532 | if (hv_err) | ||
| 533 | printk(KERN_ERR PFX "ldc_abort: " | ||
| 534 | "sun4v_ldc_tx_get_state(%lx,...) failed, err=%lu\n", | ||
| 535 | lp->id, hv_err); | ||
| 536 | |||
| 537 | hv_err = sun4v_ldc_rx_qconf(lp->id, lp->rx_ra, lp->rx_num_entries); | ||
| 538 | if (hv_err) | ||
| 539 | printk(KERN_ERR PFX "ldc_abort: " | ||
| 540 | "sun4v_ldc_rx_qconf(%lx,%lx,%lx) failed, err=%lu\n", | ||
| 541 | lp->id, lp->rx_ra, lp->rx_num_entries, hv_err); | ||
| 542 | |||
| 543 | /* Refetch the RX queue state as well, because we could be invoked | ||
| 544 | * here in the queue processing context. | ||
| 545 | */ | ||
| 546 | hv_err = sun4v_ldc_rx_get_state(lp->id, | ||
| 547 | &lp->rx_head, | ||
| 548 | &lp->rx_tail, | ||
| 549 | &lp->chan_state); | ||
| 550 | if (hv_err) | ||
| 551 | printk(KERN_ERR PFX "ldc_abort: " | ||
| 552 | "sun4v_ldc_rx_get_state(%lx,...) failed, err=%lu\n", | ||
| 553 | lp->id, hv_err); | ||
| 554 | |||
| 555 | return -ECONNRESET; | ||
| 556 | } | ||
| 557 | |||
| 558 | static struct ldc_version *find_by_major(u16 major) | ||
| 559 | { | ||
| 560 | struct ldc_version *ret = NULL; | ||
| 561 | int i; | ||
| 562 | |||
| 563 | for (i = 0; i < ARRAY_SIZE(ver_arr); i++) { | ||
| 564 | struct ldc_version *v = &ver_arr[i]; | ||
| 565 | if (v->major <= major) { | ||
| 566 | ret = v; | ||
| 567 | break; | ||
| 568 | } | ||
| 569 | } | ||
| 570 | return ret; | ||
| 571 | } | ||
| 572 | |||
| 573 | static int process_ver_info(struct ldc_channel *lp, struct ldc_version *vp) | ||
| 574 | { | ||
| 575 | struct ldc_version *vap; | ||
| 576 | int err; | ||
| 577 | |||
| 578 | ldcdbg(HS, "GOT VERSION INFO major[%x] minor[%x]\n", | ||
| 579 | vp->major, vp->minor); | ||
| 580 | |||
| 581 | if (lp->hs_state == LDC_HS_GOTVERS) { | ||
| 582 | lp->hs_state = LDC_HS_OPEN; | ||
| 583 | memset(&lp->ver, 0, sizeof(lp->ver)); | ||
| 584 | } | ||
| 585 | |||
| 586 | vap = find_by_major(vp->major); | ||
| 587 | if (!vap) { | ||
| 588 | err = send_version_nack(lp, 0, 0); | ||
| 589 | } else if (vap->major != vp->major) { | ||
| 590 | err = send_version_nack(lp, vap->major, vap->minor); | ||
| 591 | } else { | ||
| 592 | struct ldc_version ver = *vp; | ||
| 593 | if (ver.minor > vap->minor) | ||
| 594 | ver.minor = vap->minor; | ||
| 595 | err = send_version_ack(lp, &ver); | ||
| 596 | if (!err) { | ||
| 597 | lp->ver = ver; | ||
| 598 | lp->hs_state = LDC_HS_GOTVERS; | ||
| 599 | } | ||
| 600 | } | ||
| 601 | if (err) | ||
| 602 | return ldc_abort(lp); | ||
| 603 | |||
| 604 | return 0; | ||
| 605 | } | ||
| 606 | |||
| 607 | static int process_ver_ack(struct ldc_channel *lp, struct ldc_version *vp) | ||
| 608 | { | ||
| 609 | ldcdbg(HS, "GOT VERSION ACK major[%x] minor[%x]\n", | ||
| 610 | vp->major, vp->minor); | ||
| 611 | |||
| 612 | if (lp->hs_state == LDC_HS_GOTVERS) { | ||
| 613 | if (lp->ver.major != vp->major || | ||
| 614 | lp->ver.minor != vp->minor) | ||
| 615 | return ldc_abort(lp); | ||
| 616 | } else { | ||
| 617 | lp->ver = *vp; | ||
| 618 | lp->hs_state = LDC_HS_GOTVERS; | ||
| 619 | } | ||
| 620 | if (send_rts(lp)) | ||
| 621 | return ldc_abort(lp); | ||
| 622 | return 0; | ||
| 623 | } | ||
| 624 | |||
| 625 | static int process_ver_nack(struct ldc_channel *lp, struct ldc_version *vp) | ||
| 626 | { | ||
| 627 | struct ldc_version *vap; | ||
| 628 | |||
| 629 | if ((vp->major == 0 && vp->minor == 0) || | ||
| 630 | !(vap = find_by_major(vp->major))) { | ||
| 631 | return ldc_abort(lp); | ||
| 632 | } else { | ||
| 633 | struct ldc_packet *p; | ||
| 634 | unsigned long new_tail; | ||
| 635 | |||
| 636 | p = handshake_compose_ctrl(lp, LDC_INFO, LDC_VERS, | ||
| 637 | vap, sizeof(*vap), | ||
| 638 | &new_tail); | ||
| 639 | if (p) | ||
| 640 | return send_tx_packet(lp, p, new_tail); | ||
| 641 | else | ||
| 642 | return ldc_abort(lp); | ||
| 643 | } | ||
| 644 | } | ||
| 645 | |||
| 646 | static int process_version(struct ldc_channel *lp, | ||
| 647 | struct ldc_packet *p) | ||
| 648 | { | ||
| 649 | struct ldc_version *vp; | ||
| 650 | |||
| 651 | vp = (struct ldc_version *) p->u.u_data; | ||
| 652 | |||
| 653 | switch (p->stype) { | ||
| 654 | case LDC_INFO: | ||
| 655 | return process_ver_info(lp, vp); | ||
| 656 | |||
| 657 | case LDC_ACK: | ||
| 658 | return process_ver_ack(lp, vp); | ||
| 659 | |||
| 660 | case LDC_NACK: | ||
| 661 | return process_ver_nack(lp, vp); | ||
| 662 | |||
| 663 | default: | ||
| 664 | return ldc_abort(lp); | ||
| 665 | } | ||
| 666 | } | ||
| 667 | |||
| 668 | static int process_rts(struct ldc_channel *lp, | ||
| 669 | struct ldc_packet *p) | ||
| 670 | { | ||
| 671 | ldcdbg(HS, "GOT RTS stype[%x] seqid[%x] env[%x]\n", | ||
| 672 | p->stype, p->seqid, p->env); | ||
| 673 | |||
| 674 | if (p->stype != LDC_INFO || | ||
| 675 | lp->hs_state != LDC_HS_GOTVERS || | ||
| 676 | p->env != lp->cfg.mode) | ||
| 677 | return ldc_abort(lp); | ||
| 678 | |||
| 679 | lp->snd_nxt = p->seqid; | ||
| 680 | lp->rcv_nxt = p->seqid; | ||
| 681 | lp->hs_state = LDC_HS_SENTRTR; | ||
| 682 | if (send_rtr(lp)) | ||
| 683 | return ldc_abort(lp); | ||
| 684 | |||
| 685 | return 0; | ||
| 686 | } | ||
| 687 | |||
| 688 | static int process_rtr(struct ldc_channel *lp, | ||
| 689 | struct ldc_packet *p) | ||
| 690 | { | ||
| 691 | ldcdbg(HS, "GOT RTR stype[%x] seqid[%x] env[%x]\n", | ||
| 692 | p->stype, p->seqid, p->env); | ||
| 693 | |||
| 694 | if (p->stype != LDC_INFO || | ||
| 695 | p->env != lp->cfg.mode) | ||
| 696 | return ldc_abort(lp); | ||
| 697 | |||
| 698 | lp->snd_nxt = p->seqid; | ||
| 699 | lp->hs_state = LDC_HS_COMPLETE; | ||
| 700 | ldc_set_state(lp, LDC_STATE_CONNECTED); | ||
| 701 | send_rdx(lp); | ||
| 702 | |||
| 703 | return LDC_EVENT_UP; | ||
| 704 | } | ||
| 705 | |||
| 706 | static int rx_seq_ok(struct ldc_channel *lp, u32 seqid) | ||
| 707 | { | ||
| 708 | return lp->rcv_nxt + 1 == seqid; | ||
| 709 | } | ||
| 710 | |||
| 711 | static int process_rdx(struct ldc_channel *lp, | ||
| 712 | struct ldc_packet *p) | ||
| 713 | { | ||
| 714 | ldcdbg(HS, "GOT RDX stype[%x] seqid[%x] env[%x] ackid[%x]\n", | ||
| 715 | p->stype, p->seqid, p->env, p->u.r.ackid); | ||
| 716 | |||
| 717 | if (p->stype != LDC_INFO || | ||
| 718 | !(rx_seq_ok(lp, p->seqid))) | ||
| 719 | return ldc_abort(lp); | ||
| 720 | |||
| 721 | lp->rcv_nxt = p->seqid; | ||
| 722 | |||
| 723 | lp->hs_state = LDC_HS_COMPLETE; | ||
| 724 | ldc_set_state(lp, LDC_STATE_CONNECTED); | ||
| 725 | |||
| 726 | return LDC_EVENT_UP; | ||
| 727 | } | ||
| 728 | |||
| 729 | static int process_control_frame(struct ldc_channel *lp, | ||
| 730 | struct ldc_packet *p) | ||
| 731 | { | ||
| 732 | switch (p->ctrl) { | ||
| 733 | case LDC_VERS: | ||
| 734 | return process_version(lp, p); | ||
| 735 | |||
| 736 | case LDC_RTS: | ||
| 737 | return process_rts(lp, p); | ||
| 738 | |||
| 739 | case LDC_RTR: | ||
| 740 | return process_rtr(lp, p); | ||
| 741 | |||
| 742 | case LDC_RDX: | ||
| 743 | return process_rdx(lp, p); | ||
| 744 | |||
| 745 | default: | ||
| 746 | return ldc_abort(lp); | ||
| 747 | } | ||
| 748 | } | ||
| 749 | |||
| 750 | static int process_error_frame(struct ldc_channel *lp, | ||
| 751 | struct ldc_packet *p) | ||
| 752 | { | ||
| 753 | return ldc_abort(lp); | ||
| 754 | } | ||
| 755 | |||
| 756 | static int process_data_ack(struct ldc_channel *lp, | ||
| 757 | struct ldc_packet *ack) | ||
| 758 | { | ||
| 759 | unsigned long head = lp->tx_acked; | ||
| 760 | u32 ackid = ack->u.r.ackid; | ||
| 761 | |||
| 762 | while (1) { | ||
| 763 | struct ldc_packet *p = lp->tx_base + (head / LDC_PACKET_SIZE); | ||
| 764 | |||
| 765 | head = tx_advance(lp, head); | ||
| 766 | |||
| 767 | if (p->seqid == ackid) { | ||
| 768 | lp->tx_acked = head; | ||
| 769 | return 0; | ||
| 770 | } | ||
| 771 | if (head == lp->tx_tail) | ||
| 772 | return ldc_abort(lp); | ||
| 773 | } | ||
| 774 | |||
| 775 | return 0; | ||
| 776 | } | ||
| 777 | |||
| 778 | static void send_events(struct ldc_channel *lp, unsigned int event_mask) | ||
| 779 | { | ||
| 780 | if (event_mask & LDC_EVENT_RESET) | ||
| 781 | lp->cfg.event(lp->event_arg, LDC_EVENT_RESET); | ||
| 782 | if (event_mask & LDC_EVENT_UP) | ||
| 783 | lp->cfg.event(lp->event_arg, LDC_EVENT_UP); | ||
| 784 | if (event_mask & LDC_EVENT_DATA_READY) | ||
| 785 | lp->cfg.event(lp->event_arg, LDC_EVENT_DATA_READY); | ||
| 786 | } | ||
| 787 | |||
| 788 | static irqreturn_t ldc_rx(int irq, void *dev_id) | ||
| 789 | { | ||
| 790 | struct ldc_channel *lp = dev_id; | ||
| 791 | unsigned long orig_state, hv_err, flags; | ||
| 792 | unsigned int event_mask; | ||
| 793 | |||
| 794 | spin_lock_irqsave(&lp->lock, flags); | ||
| 795 | |||
| 796 | orig_state = lp->chan_state; | ||
| 797 | hv_err = sun4v_ldc_rx_get_state(lp->id, | ||
| 798 | &lp->rx_head, | ||
| 799 | &lp->rx_tail, | ||
| 800 | &lp->chan_state); | ||
| 801 | |||
| 802 | ldcdbg(RX, "RX state[0x%02lx:0x%02lx] head[0x%04lx] tail[0x%04lx]\n", | ||
| 803 | orig_state, lp->chan_state, lp->rx_head, lp->rx_tail); | ||
| 804 | |||
| 805 | event_mask = 0; | ||
| 806 | |||
| 807 | if (lp->cfg.mode == LDC_MODE_RAW && | ||
| 808 | lp->chan_state == LDC_CHANNEL_UP) { | ||
| 809 | lp->hs_state = LDC_HS_COMPLETE; | ||
| 810 | ldc_set_state(lp, LDC_STATE_CONNECTED); | ||
| 811 | |||
| 812 | event_mask |= LDC_EVENT_UP; | ||
| 813 | |||
| 814 | orig_state = lp->chan_state; | ||
| 815 | } | ||
| 816 | |||
| 817 | /* If we are in reset state, flush the RX queue and ignore | ||
| 818 | * everything. | ||
| 819 | */ | ||
| 820 | if (lp->flags & LDC_FLAG_RESET) { | ||
| 821 | (void) __set_rx_head(lp, lp->rx_tail); | ||
| 822 | goto out; | ||
| 823 | } | ||
| 824 | |||
| 825 | /* Once we finish the handshake, we let the ldc_read() | ||
| 826 | * paths do all of the control frame and state management. | ||
| 827 | * Just trigger the callback. | ||
| 828 | */ | ||
| 829 | if (lp->hs_state == LDC_HS_COMPLETE) { | ||
| 830 | handshake_complete: | ||
| 831 | if (lp->chan_state != orig_state) { | ||
| 832 | unsigned int event = LDC_EVENT_RESET; | ||
| 833 | |||
| 834 | if (lp->chan_state == LDC_CHANNEL_UP) | ||
| 835 | event = LDC_EVENT_UP; | ||
| 836 | |||
| 837 | event_mask |= event; | ||
| 838 | } | ||
| 839 | if (lp->rx_head != lp->rx_tail) | ||
| 840 | event_mask |= LDC_EVENT_DATA_READY; | ||
| 841 | |||
| 842 | goto out; | ||
| 843 | } | ||
| 844 | |||
| 845 | if (lp->chan_state != orig_state) | ||
| 846 | goto out; | ||
| 847 | |||
| 848 | while (lp->rx_head != lp->rx_tail) { | ||
| 849 | struct ldc_packet *p; | ||
| 850 | unsigned long new; | ||
| 851 | int err; | ||
| 852 | |||
| 853 | p = lp->rx_base + (lp->rx_head / LDC_PACKET_SIZE); | ||
| 854 | |||
| 855 | switch (p->type) { | ||
| 856 | case LDC_CTRL: | ||
| 857 | err = process_control_frame(lp, p); | ||
| 858 | if (err > 0) | ||
| 859 | event_mask |= err; | ||
| 860 | break; | ||
| 861 | |||
| 862 | case LDC_DATA: | ||
| 863 | event_mask |= LDC_EVENT_DATA_READY; | ||
| 864 | err = 0; | ||
| 865 | break; | ||
| 866 | |||
| 867 | case LDC_ERR: | ||
| 868 | err = process_error_frame(lp, p); | ||
| 869 | break; | ||
| 870 | |||
| 871 | default: | ||
| 872 | err = ldc_abort(lp); | ||
| 873 | break; | ||
| 874 | } | ||
| 875 | |||
| 876 | if (err < 0) | ||
| 877 | break; | ||
| 878 | |||
| 879 | new = lp->rx_head; | ||
| 880 | new += LDC_PACKET_SIZE; | ||
| 881 | if (new == (lp->rx_num_entries * LDC_PACKET_SIZE)) | ||
| 882 | new = 0; | ||
| 883 | lp->rx_head = new; | ||
| 884 | |||
| 885 | err = __set_rx_head(lp, new); | ||
| 886 | if (err < 0) { | ||
| 887 | (void) ldc_abort(lp); | ||
| 888 | break; | ||
| 889 | } | ||
| 890 | if (lp->hs_state == LDC_HS_COMPLETE) | ||
| 891 | goto handshake_complete; | ||
| 892 | } | ||
| 893 | |||
| 894 | out: | ||
| 895 | spin_unlock_irqrestore(&lp->lock, flags); | ||
| 896 | |||
| 897 | send_events(lp, event_mask); | ||
| 898 | |||
| 899 | return IRQ_HANDLED; | ||
| 900 | } | ||
| 901 | |||
| 902 | static irqreturn_t ldc_tx(int irq, void *dev_id) | ||
| 903 | { | ||
| 904 | struct ldc_channel *lp = dev_id; | ||
| 905 | unsigned long flags, hv_err, orig_state; | ||
| 906 | unsigned int event_mask = 0; | ||
| 907 | |||
| 908 | spin_lock_irqsave(&lp->lock, flags); | ||
| 909 | |||
| 910 | orig_state = lp->chan_state; | ||
| 911 | hv_err = sun4v_ldc_tx_get_state(lp->id, | ||
| 912 | &lp->tx_head, | ||
| 913 | &lp->tx_tail, | ||
| 914 | &lp->chan_state); | ||
| 915 | |||
| 916 | ldcdbg(TX, " TX state[0x%02lx:0x%02lx] head[0x%04lx] tail[0x%04lx]\n", | ||
| 917 | orig_state, lp->chan_state, lp->tx_head, lp->tx_tail); | ||
| 918 | |||
| 919 | if (lp->cfg.mode == LDC_MODE_RAW && | ||
| 920 | lp->chan_state == LDC_CHANNEL_UP) { | ||
| 921 | lp->hs_state = LDC_HS_COMPLETE; | ||
| 922 | ldc_set_state(lp, LDC_STATE_CONNECTED); | ||
| 923 | |||
| 924 | event_mask |= LDC_EVENT_UP; | ||
| 925 | } | ||
| 926 | |||
| 927 | spin_unlock_irqrestore(&lp->lock, flags); | ||
| 928 | |||
| 929 | send_events(lp, event_mask); | ||
| 930 | |||
| 931 | return IRQ_HANDLED; | ||
| 932 | } | ||
| 933 | |||
| 934 | /* XXX ldc_alloc() and ldc_free() needs to run under a mutex so | ||
| 935 | * XXX that addition and removal from the ldc_channel_list has | ||
| 936 | * XXX atomicity, otherwise the __ldc_channel_exists() check is | ||
| 937 | * XXX totally pointless as another thread can slip into ldc_alloc() | ||
| 938 | * XXX and add a channel with the same ID. There also needs to be | ||
| 939 | * XXX a spinlock for ldc_channel_list. | ||
| 940 | */ | ||
| 941 | static HLIST_HEAD(ldc_channel_list); | ||
| 942 | |||
| 943 | static int __ldc_channel_exists(unsigned long id) | ||
| 944 | { | ||
| 945 | struct ldc_channel *lp; | ||
| 946 | struct hlist_node *n; | ||
| 947 | |||
| 948 | hlist_for_each_entry(lp, n, &ldc_channel_list, list) { | ||
| 949 | if (lp->id == id) | ||
| 950 | return 1; | ||
| 951 | } | ||
| 952 | return 0; | ||
| 953 | } | ||
| 954 | |||
| 955 | static int alloc_queue(const char *name, unsigned long num_entries, | ||
| 956 | struct ldc_packet **base, unsigned long *ra) | ||
| 957 | { | ||
| 958 | unsigned long size, order; | ||
| 959 | void *q; | ||
| 960 | |||
| 961 | size = num_entries * LDC_PACKET_SIZE; | ||
| 962 | order = get_order(size); | ||
| 963 | |||
| 964 | q = (void *) __get_free_pages(GFP_KERNEL, order); | ||
| 965 | if (!q) { | ||
| 966 | printk(KERN_ERR PFX "Alloc of %s queue failed with " | ||
| 967 | "size=%lu order=%lu\n", name, size, order); | ||
| 968 | return -ENOMEM; | ||
| 969 | } | ||
| 970 | |||
| 971 | memset(q, 0, PAGE_SIZE << order); | ||
| 972 | |||
| 973 | *base = q; | ||
| 974 | *ra = __pa(q); | ||
| 975 | |||
| 976 | return 0; | ||
| 977 | } | ||
| 978 | |||
| 979 | static void free_queue(unsigned long num_entries, struct ldc_packet *q) | ||
| 980 | { | ||
| 981 | unsigned long size, order; | ||
| 982 | |||
| 983 | if (!q) | ||
| 984 | return; | ||
| 985 | |||
| 986 | size = num_entries * LDC_PACKET_SIZE; | ||
| 987 | order = get_order(size); | ||
| 988 | |||
| 989 | free_pages((unsigned long)q, order); | ||
| 990 | } | ||
| 991 | |||
| 992 | /* XXX Make this configurable... XXX */ | ||
| 993 | #define LDC_IOTABLE_SIZE (8 * 1024) | ||
| 994 | |||
| 995 | static int ldc_iommu_init(struct ldc_channel *lp) | ||
| 996 | { | ||
| 997 | unsigned long sz, num_tsb_entries, tsbsize, order; | ||
| 998 | struct ldc_iommu *iommu = &lp->iommu; | ||
| 999 | struct ldc_mtable_entry *table; | ||
| 1000 | unsigned long hv_err; | ||
| 1001 | int err; | ||
| 1002 | |||
| 1003 | num_tsb_entries = LDC_IOTABLE_SIZE; | ||
| 1004 | tsbsize = num_tsb_entries * sizeof(struct ldc_mtable_entry); | ||
| 1005 | |||
| 1006 | spin_lock_init(&iommu->lock); | ||
| 1007 | |||
| 1008 | sz = num_tsb_entries / 8; | ||
| 1009 | sz = (sz + 7UL) & ~7UL; | ||
| 1010 | iommu->arena.map = kzalloc(sz, GFP_KERNEL); | ||
| 1011 | if (!iommu->arena.map) { | ||
| 1012 | printk(KERN_ERR PFX "Alloc of arena map failed, sz=%lu\n", sz); | ||
| 1013 | return -ENOMEM; | ||
| 1014 | } | ||
| 1015 | |||
| 1016 | iommu->arena.limit = num_tsb_entries; | ||
| 1017 | |||
| 1018 | order = get_order(tsbsize); | ||
| 1019 | |||
| 1020 | table = (struct ldc_mtable_entry *) | ||
| 1021 | __get_free_pages(GFP_KERNEL, order); | ||
| 1022 | err = -ENOMEM; | ||
| 1023 | if (!table) { | ||
| 1024 | printk(KERN_ERR PFX "Alloc of MTE table failed, " | ||
| 1025 | "size=%lu order=%lu\n", tsbsize, order); | ||
| 1026 | goto out_free_map; | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | memset(table, 0, PAGE_SIZE << order); | ||
| 1030 | |||
| 1031 | iommu->page_table = table; | ||
| 1032 | |||
| 1033 | hv_err = sun4v_ldc_set_map_table(lp->id, __pa(table), | ||
| 1034 | num_tsb_entries); | ||
| 1035 | err = -EINVAL; | ||
| 1036 | if (hv_err) | ||
| 1037 | goto out_free_table; | ||
| 1038 | |||
| 1039 | return 0; | ||
| 1040 | |||
| 1041 | out_free_table: | ||
| 1042 | free_pages((unsigned long) table, order); | ||
| 1043 | iommu->page_table = NULL; | ||
| 1044 | |||
| 1045 | out_free_map: | ||
| 1046 | kfree(iommu->arena.map); | ||
| 1047 | iommu->arena.map = NULL; | ||
| 1048 | |||
| 1049 | return err; | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | static void ldc_iommu_release(struct ldc_channel *lp) | ||
| 1053 | { | ||
| 1054 | struct ldc_iommu *iommu = &lp->iommu; | ||
| 1055 | unsigned long num_tsb_entries, tsbsize, order; | ||
| 1056 | |||
| 1057 | (void) sun4v_ldc_set_map_table(lp->id, 0, 0); | ||
| 1058 | |||
| 1059 | num_tsb_entries = iommu->arena.limit; | ||
| 1060 | tsbsize = num_tsb_entries * sizeof(struct ldc_mtable_entry); | ||
| 1061 | order = get_order(tsbsize); | ||
| 1062 | |||
| 1063 | free_pages((unsigned long) iommu->page_table, order); | ||
| 1064 | iommu->page_table = NULL; | ||
| 1065 | |||
| 1066 | kfree(iommu->arena.map); | ||
| 1067 | iommu->arena.map = NULL; | ||
| 1068 | } | ||
| 1069 | |||
| 1070 | struct ldc_channel *ldc_alloc(unsigned long id, | ||
| 1071 | const struct ldc_channel_config *cfgp, | ||
| 1072 | void *event_arg) | ||
| 1073 | { | ||
| 1074 | struct ldc_channel *lp; | ||
| 1075 | const struct ldc_mode_ops *mops; | ||
| 1076 | unsigned long dummy1, dummy2, hv_err; | ||
| 1077 | u8 mss, *mssbuf; | ||
| 1078 | int err; | ||
| 1079 | |||
| 1080 | err = -ENODEV; | ||
| 1081 | if (!ldom_domaining_enabled) | ||
| 1082 | goto out_err; | ||
| 1083 | |||
| 1084 | err = -EINVAL; | ||
| 1085 | if (!cfgp) | ||
| 1086 | goto out_err; | ||
| 1087 | |||
| 1088 | switch (cfgp->mode) { | ||
| 1089 | case LDC_MODE_RAW: | ||
| 1090 | mops = &raw_ops; | ||
| 1091 | mss = LDC_PACKET_SIZE; | ||
| 1092 | break; | ||
| 1093 | |||
| 1094 | case LDC_MODE_UNRELIABLE: | ||
| 1095 | mops = &nonraw_ops; | ||
| 1096 | mss = LDC_PACKET_SIZE - 8; | ||
| 1097 | break; | ||
| 1098 | |||
| 1099 | case LDC_MODE_STREAM: | ||
| 1100 | mops = &stream_ops; | ||
| 1101 | mss = LDC_PACKET_SIZE - 8 - 8; | ||
| 1102 | break; | ||
| 1103 | |||
| 1104 | default: | ||
| 1105 | goto out_err; | ||
| 1106 | } | ||
| 1107 | |||
| 1108 | if (!cfgp->event || !event_arg || !cfgp->rx_irq || !cfgp->tx_irq) | ||
| 1109 | goto out_err; | ||
| 1110 | |||
| 1111 | hv_err = sun4v_ldc_tx_qinfo(id, &dummy1, &dummy2); | ||
| 1112 | err = -ENODEV; | ||
| 1113 | if (hv_err == HV_ECHANNEL) | ||
| 1114 | goto out_err; | ||
| 1115 | |||
| 1116 | err = -EEXIST; | ||
| 1117 | if (__ldc_channel_exists(id)) | ||
| 1118 | goto out_err; | ||
| 1119 | |||
| 1120 | mssbuf = NULL; | ||
| 1121 | |||
| 1122 | lp = kzalloc(sizeof(*lp), GFP_KERNEL); | ||
| 1123 | err = -ENOMEM; | ||
| 1124 | if (!lp) | ||
| 1125 | goto out_err; | ||
| 1126 | |||
| 1127 | spin_lock_init(&lp->lock); | ||
| 1128 | |||
| 1129 | lp->id = id; | ||
| 1130 | |||
| 1131 | err = ldc_iommu_init(lp); | ||
| 1132 | if (err) | ||
| 1133 | goto out_free_ldc; | ||
| 1134 | |||
| 1135 | lp->mops = mops; | ||
| 1136 | lp->mss = mss; | ||
| 1137 | |||
| 1138 | lp->cfg = *cfgp; | ||
| 1139 | if (!lp->cfg.mtu) | ||
| 1140 | lp->cfg.mtu = LDC_DEFAULT_MTU; | ||
| 1141 | |||
| 1142 | if (lp->cfg.mode == LDC_MODE_STREAM) { | ||
| 1143 | mssbuf = kzalloc(lp->cfg.mtu, GFP_KERNEL); | ||
| 1144 | if (!mssbuf) { | ||
| 1145 | err = -ENOMEM; | ||
| 1146 | goto out_free_iommu; | ||
| 1147 | } | ||
| 1148 | lp->mssbuf = mssbuf; | ||
| 1149 | } | ||
| 1150 | |||
| 1151 | lp->event_arg = event_arg; | ||
| 1152 | |||
| 1153 | /* XXX allow setting via ldc_channel_config to override defaults | ||
| 1154 | * XXX or use some formula based upon mtu | ||
| 1155 | */ | ||
| 1156 | lp->tx_num_entries = LDC_DEFAULT_NUM_ENTRIES; | ||
| 1157 | lp->rx_num_entries = LDC_DEFAULT_NUM_ENTRIES; | ||
| 1158 | |||
| 1159 | err = alloc_queue("TX", lp->tx_num_entries, | ||
| 1160 | &lp->tx_base, &lp->tx_ra); | ||
| 1161 | if (err) | ||
| 1162 | goto out_free_mssbuf; | ||
| 1163 | |||
| 1164 | err = alloc_queue("RX", lp->rx_num_entries, | ||
| 1165 | &lp->rx_base, &lp->rx_ra); | ||
| 1166 | if (err) | ||
| 1167 | goto out_free_txq; | ||
| 1168 | |||
| 1169 | lp->flags |= LDC_FLAG_ALLOCED_QUEUES; | ||
| 1170 | |||
| 1171 | lp->hs_state = LDC_HS_CLOSED; | ||
| 1172 | ldc_set_state(lp, LDC_STATE_INIT); | ||
| 1173 | |||
| 1174 | INIT_HLIST_NODE(&lp->list); | ||
| 1175 | hlist_add_head(&lp->list, &ldc_channel_list); | ||
| 1176 | |||
| 1177 | INIT_HLIST_HEAD(&lp->mh_list); | ||
| 1178 | |||
| 1179 | return lp; | ||
| 1180 | |||
| 1181 | out_free_txq: | ||
| 1182 | free_queue(lp->tx_num_entries, lp->tx_base); | ||
| 1183 | |||
| 1184 | out_free_mssbuf: | ||
| 1185 | if (mssbuf) | ||
| 1186 | kfree(mssbuf); | ||
| 1187 | |||
| 1188 | out_free_iommu: | ||
| 1189 | ldc_iommu_release(lp); | ||
| 1190 | |||
| 1191 | out_free_ldc: | ||
| 1192 | kfree(lp); | ||
| 1193 | |||
| 1194 | out_err: | ||
| 1195 | return ERR_PTR(err); | ||
| 1196 | } | ||
| 1197 | EXPORT_SYMBOL(ldc_alloc); | ||
| 1198 | |||
| 1199 | void ldc_free(struct ldc_channel *lp) | ||
| 1200 | { | ||
| 1201 | if (lp->flags & LDC_FLAG_REGISTERED_IRQS) { | ||
| 1202 | free_irq(lp->cfg.rx_irq, lp); | ||
| 1203 | free_irq(lp->cfg.tx_irq, lp); | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | if (lp->flags & LDC_FLAG_REGISTERED_QUEUES) { | ||
| 1207 | sun4v_ldc_tx_qconf(lp->id, 0, 0); | ||
| 1208 | sun4v_ldc_rx_qconf(lp->id, 0, 0); | ||
| 1209 | lp->flags &= ~LDC_FLAG_REGISTERED_QUEUES; | ||
| 1210 | } | ||
| 1211 | if (lp->flags & LDC_FLAG_ALLOCED_QUEUES) { | ||
| 1212 | free_queue(lp->tx_num_entries, lp->tx_base); | ||
| 1213 | free_queue(lp->rx_num_entries, lp->rx_base); | ||
| 1214 | lp->flags &= ~LDC_FLAG_ALLOCED_QUEUES; | ||
| 1215 | } | ||
| 1216 | |||
| 1217 | hlist_del(&lp->list); | ||
| 1218 | |||
| 1219 | if (lp->mssbuf) | ||
| 1220 | kfree(lp->mssbuf); | ||
| 1221 | |||
| 1222 | ldc_iommu_release(lp); | ||
| 1223 | |||
| 1224 | kfree(lp); | ||
| 1225 | } | ||
| 1226 | EXPORT_SYMBOL(ldc_free); | ||
| 1227 | |||
| 1228 | /* Bind the channel. This registers the LDC queues with | ||
| 1229 | * the hypervisor and puts the channel into a pseudo-listening | ||
| 1230 | * state. This does not initiate a handshake, ldc_connect() does | ||
| 1231 | * that. | ||
| 1232 | */ | ||
| 1233 | int ldc_bind(struct ldc_channel *lp, const char *name) | ||
| 1234 | { | ||
| 1235 | unsigned long hv_err, flags; | ||
| 1236 | int err = -EINVAL; | ||
| 1237 | |||
| 1238 | spin_lock_irqsave(&lp->lock, flags); | ||
| 1239 | |||
| 1240 | if (!name) | ||
| 1241 | goto out_err; | ||
| 1242 | |||
| 1243 | if (lp->state != LDC_STATE_INIT) | ||
| 1244 | goto out_err; | ||
| 1245 | |||
| 1246 | snprintf(lp->rx_irq_name, LDC_IRQ_NAME_MAX, "%s RX", name); | ||
| 1247 | snprintf(lp->tx_irq_name, LDC_IRQ_NAME_MAX, "%s TX", name); | ||
| 1248 | |||
| 1249 | err = request_irq(lp->cfg.rx_irq, ldc_rx, | ||
| 1250 | IRQF_SAMPLE_RANDOM | IRQF_SHARED, | ||
| 1251 | lp->rx_irq_name, lp); | ||
| 1252 | if (err) | ||
| 1253 | goto out_err; | ||
| 1254 | |||
| 1255 | err = request_irq(lp->cfg.tx_irq, ldc_tx, | ||
| 1256 | IRQF_SAMPLE_RANDOM | IRQF_SHARED, | ||
| 1257 | lp->tx_irq_name, lp); | ||
| 1258 | if (err) | ||
| 1259 | goto out_free_rx_irq; | ||
| 1260 | |||
| 1261 | |||
| 1262 | lp->flags |= LDC_FLAG_REGISTERED_IRQS; | ||
| 1263 | |||
| 1264 | err = -ENODEV; | ||
| 1265 | hv_err = sun4v_ldc_tx_qconf(lp->id, 0, 0); | ||
| 1266 | if (hv_err) | ||
| 1267 | goto out_free_tx_irq; | ||
| 1268 | |||
| 1269 | hv_err = sun4v_ldc_tx_qconf(lp->id, lp->tx_ra, lp->tx_num_entries); | ||
| 1270 | if (hv_err) | ||
| 1271 | goto out_free_tx_irq; | ||
| 1272 | |||
| 1273 | hv_err = sun4v_ldc_rx_qconf(lp->id, 0, 0); | ||
| 1274 | if (hv_err) | ||
| 1275 | goto out_unmap_tx; | ||
| 1276 | |||
| 1277 | hv_err = sun4v_ldc_rx_qconf(lp->id, lp->rx_ra, lp->rx_num_entries); | ||
| 1278 | if (hv_err) | ||
| 1279 | goto out_unmap_tx; | ||
| 1280 | |||
| 1281 | lp->flags |= LDC_FLAG_REGISTERED_QUEUES; | ||
| 1282 | |||
| 1283 | hv_err = sun4v_ldc_tx_get_state(lp->id, | ||
| 1284 | &lp->tx_head, | ||
| 1285 | &lp->tx_tail, | ||
| 1286 | &lp->chan_state); | ||
| 1287 | err = -EBUSY; | ||
| 1288 | if (hv_err) | ||
| 1289 | goto out_unmap_rx; | ||
| 1290 | |||
| 1291 | lp->tx_acked = lp->tx_head; | ||
| 1292 | |||
| 1293 | lp->hs_state = LDC_HS_OPEN; | ||
| 1294 | ldc_set_state(lp, LDC_STATE_BOUND); | ||
| 1295 | |||
| 1296 | spin_unlock_irqrestore(&lp->lock, flags); | ||
| 1297 | |||
| 1298 | return 0; | ||
| 1299 | |||
| 1300 | out_unmap_rx: | ||
| 1301 | lp->flags &= ~LDC_FLAG_REGISTERED_QUEUES; | ||
| 1302 | sun4v_ldc_rx_qconf(lp->id, 0, 0); | ||
| 1303 | |||
| 1304 | out_unmap_tx: | ||
| 1305 | sun4v_ldc_tx_qconf(lp->id, 0, 0); | ||
| 1306 | |||
| 1307 | out_free_tx_irq: | ||
| 1308 | lp->flags &= ~LDC_FLAG_REGISTERED_IRQS; | ||
| 1309 | free_irq(lp->cfg.tx_irq, lp); | ||
| 1310 | |||
| 1311 | out_free_rx_irq: | ||
| 1312 | free_irq(lp->cfg.rx_irq, lp); | ||
| 1313 | |||
| 1314 | out_err: | ||
| 1315 | spin_unlock_irqrestore(&lp->lock, flags); | ||
| 1316 | |||
| 1317 | return err; | ||
| 1318 | } | ||
| 1319 | EXPORT_SYMBOL(ldc_bind); | ||
| 1320 | |||
| 1321 | int ldc_connect(struct ldc_channel *lp) | ||
| 1322 | { | ||
| 1323 | unsigned long flags; | ||
| 1324 | int err; | ||
| 1325 | |||
| 1326 | if (lp->cfg.mode == LDC_MODE_RAW) | ||
| 1327 | return -EINVAL; | ||
| 1328 | |||
| 1329 | spin_lock_irqsave(&lp->lock, flags); | ||
| 1330 | |||
| 1331 | if (!(lp->flags & LDC_FLAG_ALLOCED_QUEUES) || | ||
| 1332 | !(lp->flags & LDC_FLAG_REGISTERED_QUEUES) || | ||
| 1333 | lp->hs_state != LDC_HS_OPEN) | ||
| 1334 | err = -EINVAL; | ||
| 1335 | else | ||
| 1336 | err = start_handshake(lp); | ||
| 1337 | |||
| 1338 | spin_unlock_irqrestore(&lp->lock, flags); | ||
| 1339 | |||
| 1340 | return err; | ||
| 1341 | } | ||
| 1342 | EXPORT_SYMBOL(ldc_connect); | ||
| 1343 | |||
| 1344 | int ldc_disconnect(struct ldc_channel *lp) | ||
| 1345 | { | ||
| 1346 | unsigned long hv_err, flags; | ||
| 1347 | int err; | ||
| 1348 | |||
| 1349 | if (lp->cfg.mode == LDC_MODE_RAW) | ||
| 1350 | return -EINVAL; | ||
| 1351 | |||
| 1352 | if (!(lp->flags & LDC_FLAG_ALLOCED_QUEUES) || | ||
| 1353 | !(lp->flags & LDC_FLAG_REGISTERED_QUEUES)) | ||
| 1354 | return -EINVAL; | ||
| 1355 | |||
| 1356 | spin_lock_irqsave(&lp->lock, flags); | ||
| 1357 | |||
| 1358 | err = -ENODEV; | ||
| 1359 | hv_err = sun4v_ldc_tx_qconf(lp->id, 0, 0); | ||
| 1360 | if (hv_err) | ||
| 1361 | goto out_err; | ||
| 1362 | |||
| 1363 | hv_err = sun4v_ldc_tx_qconf(lp->id, lp->tx_ra, lp->tx_num_entries); | ||
| 1364 | if (hv_err) | ||
| 1365 | goto out_err; | ||
| 1366 | |||
| 1367 | hv_err = sun4v_ldc_rx_qconf(lp->id, 0, 0); | ||
| 1368 | if (hv_err) | ||
| 1369 | goto out_err; | ||
| 1370 | |||
| 1371 | hv_err = sun4v_ldc_rx_qconf(lp->id, lp->rx_ra, lp->rx_num_entries); | ||
| 1372 | if (hv_err) | ||
| 1373 | goto out_err; | ||
| 1374 | |||
| 1375 | ldc_set_state(lp, LDC_STATE_BOUND); | ||
| 1376 | lp->hs_state = LDC_HS_OPEN; | ||
| 1377 | lp->flags |= LDC_FLAG_RESET; | ||
| 1378 | |||
| 1379 | spin_unlock_irqrestore(&lp->lock, flags); | ||
| 1380 | |||
| 1381 | return 0; | ||
| 1382 | |||
| 1383 | out_err: | ||
| 1384 | sun4v_ldc_tx_qconf(lp->id, 0, 0); | ||
| 1385 | sun4v_ldc_rx_qconf(lp->id, 0, 0); | ||
| 1386 | free_irq(lp->cfg.tx_irq, lp); | ||
| 1387 | free_irq(lp->cfg.rx_irq, lp); | ||
| 1388 | lp->flags &= ~(LDC_FLAG_REGISTERED_IRQS | | ||
| 1389 | LDC_FLAG_REGISTERED_QUEUES); | ||
| 1390 | ldc_set_state(lp, LDC_STATE_INIT); | ||
| 1391 | |||
| 1392 | spin_unlock_irqrestore(&lp->lock, flags); | ||
| 1393 | |||
| 1394 | return err; | ||
| 1395 | } | ||
| 1396 | EXPORT_SYMBOL(ldc_disconnect); | ||
| 1397 | |||
| 1398 | int ldc_state(struct ldc_channel *lp) | ||
| 1399 | { | ||
| 1400 | return lp->state; | ||
| 1401 | } | ||
| 1402 | EXPORT_SYMBOL(ldc_state); | ||
| 1403 | |||
| 1404 | static int write_raw(struct ldc_channel *lp, const void *buf, unsigned int size) | ||
| 1405 | { | ||
| 1406 | struct ldc_packet *p; | ||
| 1407 | unsigned long new_tail; | ||
| 1408 | int err; | ||
| 1409 | |||
| 1410 | if (size > LDC_PACKET_SIZE) | ||
| 1411 | return -EMSGSIZE; | ||
| 1412 | |||
| 1413 | p = data_get_tx_packet(lp, &new_tail); | ||
| 1414 | if (!p) | ||
| 1415 | return -EAGAIN; | ||
| 1416 | |||
| 1417 | memcpy(p, buf, size); | ||
| 1418 | |||
| 1419 | err = send_tx_packet(lp, p, new_tail); | ||
| 1420 | if (!err) | ||
| 1421 | err = size; | ||
| 1422 | |||
| 1423 | return err; | ||
| 1424 | } | ||
| 1425 | |||
| 1426 | static int read_raw(struct ldc_channel *lp, void *buf, unsigned int size) | ||
| 1427 | { | ||
| 1428 | struct ldc_packet *p; | ||
| 1429 | unsigned long hv_err, new; | ||
| 1430 | int err; | ||
| 1431 | |||
| 1432 | if (size < LDC_PACKET_SIZE) | ||
| 1433 | return -EINVAL; | ||
| 1434 | |||
| 1435 | hv_err = sun4v_ldc_rx_get_state(lp->id, | ||
| 1436 | &lp->rx_head, | ||
| 1437 | &lp->rx_tail, | ||
| 1438 | &lp->chan_state); | ||
| 1439 | if (hv_err) | ||
| 1440 | return ldc_abort(lp); | ||
| 1441 | |||
| 1442 | if (lp->chan_state == LDC_CHANNEL_DOWN || | ||
| 1443 | lp->chan_state == LDC_CHANNEL_RESETTING) | ||
| 1444 | return -ECONNRESET; | ||
| 1445 | |||
| 1446 | if (lp->rx_head == lp->rx_tail) | ||
| 1447 | return 0; | ||
| 1448 | |||
| 1449 | p = lp->rx_base + (lp->rx_head / LDC_PACKET_SIZE); | ||
| 1450 | memcpy(buf, p, LDC_PACKET_SIZE); | ||
| 1451 | |||
| 1452 | new = rx_advance(lp, lp->rx_head); | ||
| 1453 | lp->rx_head = new; | ||
| 1454 | |||
| 1455 | err = __set_rx_head(lp, new); | ||
| 1456 | if (err < 0) | ||
| 1457 | err = -ECONNRESET; | ||
| 1458 | else | ||
| 1459 | err = LDC_PACKET_SIZE; | ||
| 1460 | |||
| 1461 | return err; | ||
| 1462 | } | ||
| 1463 | |||
| 1464 | static const struct ldc_mode_ops raw_ops = { | ||
| 1465 | .write = write_raw, | ||
| 1466 | .read = read_raw, | ||
| 1467 | }; | ||
| 1468 | |||
| 1469 | static int write_nonraw(struct ldc_channel *lp, const void *buf, | ||
| 1470 | unsigned int size) | ||
| 1471 | { | ||
| 1472 | unsigned long hv_err, tail; | ||
| 1473 | unsigned int copied; | ||
| 1474 | u32 seq; | ||
| 1475 | int err; | ||
| 1476 | |||
| 1477 | hv_err = sun4v_ldc_tx_get_state(lp->id, &lp->tx_head, &lp->tx_tail, | ||
| 1478 | &lp->chan_state); | ||
| 1479 | if (unlikely(hv_err)) | ||
| 1480 | return -EBUSY; | ||
| 1481 | |||
| 1482 | if (unlikely(lp->chan_state != LDC_CHANNEL_UP)) | ||
| 1483 | return ldc_abort(lp); | ||
| 1484 | |||
| 1485 | if (!tx_has_space_for(lp, size)) | ||
| 1486 | return -EAGAIN; | ||
| 1487 | |||
| 1488 | seq = lp->snd_nxt; | ||
| 1489 | copied = 0; | ||
| 1490 | tail = lp->tx_tail; | ||
| 1491 | while (copied < size) { | ||
| 1492 | struct ldc_packet *p = lp->tx_base + (tail / LDC_PACKET_SIZE); | ||
| 1493 | u8 *data = ((lp->cfg.mode == LDC_MODE_UNRELIABLE) ? | ||
| 1494 | p->u.u_data : | ||
| 1495 | p->u.r.r_data); | ||
| 1496 | int data_len; | ||
| 1497 | |||
| 1498 | p->type = LDC_DATA; | ||
| 1499 | p->stype = LDC_INFO; | ||
| 1500 | p->ctrl = 0; | ||
| 1501 | |||
| 1502 | data_len = size - copied; | ||
| 1503 | if (data_len > lp->mss) | ||
| 1504 | data_len = lp->mss; | ||
| 1505 | |||
| 1506 | BUG_ON(data_len > LDC_LEN); | ||
| 1507 | |||
| 1508 | p->env = (data_len | | ||
| 1509 | (copied == 0 ? LDC_START : 0) | | ||
| 1510 | (data_len == size - copied ? LDC_STOP : 0)); | ||
| 1511 | |||
| 1512 | p->seqid = ++seq; | ||
| 1513 | |||
| 1514 | ldcdbg(DATA, "SENT DATA [%02x:%02x:%02x:%02x:%08x]\n", | ||
| 1515 | p->type, | ||
| 1516 | p->stype, | ||
| 1517 | p->ctrl, | ||
| 1518 | p->env, | ||
| 1519 | p->seqid); | ||
| 1520 | |||
| 1521 | memcpy(data, buf, data_len); | ||
| 1522 | buf += data_len; | ||
| 1523 | copied += data_len; | ||
| 1524 | |||
| 1525 | tail = tx_advance(lp, tail); | ||
| 1526 | } | ||
| 1527 | |||
| 1528 | err = set_tx_tail(lp, tail); | ||
| 1529 | if (!err) { | ||
| 1530 | lp->snd_nxt = seq; | ||
| 1531 | err = size; | ||
| 1532 | } | ||
| 1533 | |||
| 1534 | return err; | ||
| 1535 | } | ||
| 1536 | |||
| 1537 | static int rx_bad_seq(struct ldc_channel *lp, struct ldc_packet *p, | ||
| 1538 | struct ldc_packet *first_frag) | ||
| 1539 | { | ||
| 1540 | int err; | ||
| 1541 | |||
| 1542 | if (first_frag) | ||
| 1543 | lp->rcv_nxt = first_frag->seqid - 1; | ||
| 1544 | |||
| 1545 | err = send_data_nack(lp, p); | ||
| 1546 | if (err) | ||
| 1547 | return err; | ||
| 1548 | |||
| 1549 | err = __set_rx_head(lp, lp->rx_tail); | ||
| 1550 | if (err < 0) | ||
| 1551 | return ldc_abort(lp); | ||
| 1552 | |||
| 1553 | return 0; | ||
| 1554 | } | ||
| 1555 | |||
| 1556 | static int data_ack_nack(struct ldc_channel *lp, struct ldc_packet *p) | ||
| 1557 | { | ||
| 1558 | if (p->stype & LDC_ACK) { | ||
| 1559 | int err = process_data_ack(lp, p); | ||
| 1560 | if (err) | ||
| 1561 | return err; | ||
| 1562 | } | ||
| 1563 | if (p->stype & LDC_NACK) | ||
| 1564 | return ldc_abort(lp); | ||
| 1565 | |||
| 1566 | return 0; | ||
| 1567 | } | ||
| 1568 | |||
| 1569 | static int rx_data_wait(struct ldc_channel *lp, unsigned long cur_head) | ||
| 1570 | { | ||
| 1571 | unsigned long dummy; | ||
| 1572 | int limit = 1000; | ||
| 1573 | |||
| 1574 | ldcdbg(DATA, "DATA WAIT cur_head[%lx] rx_head[%lx] rx_tail[%lx]\n", | ||
| 1575 | cur_head, lp->rx_head, lp->rx_tail); | ||
| 1576 | while (limit-- > 0) { | ||
| 1577 | unsigned long hv_err; | ||
| 1578 | |||
| 1579 | hv_err = sun4v_ldc_rx_get_state(lp->id, | ||
| 1580 | &dummy, | ||
| 1581 | &lp->rx_tail, | ||
| 1582 | &lp->chan_state); | ||
| 1583 | if (hv_err) | ||
| 1584 | return ldc_abort(lp); | ||
| 1585 | |||
| 1586 | if (lp->chan_state == LDC_CHANNEL_DOWN || | ||
| 1587 | lp->chan_state == LDC_CHANNEL_RESETTING) | ||
| 1588 | return -ECONNRESET; | ||
| 1589 | |||
| 1590 | if (cur_head != lp->rx_tail) { | ||
| 1591 | ldcdbg(DATA, "DATA WAIT DONE " | ||
| 1592 | "head[%lx] tail[%lx] chan_state[%lx]\n", | ||
| 1593 | dummy, lp->rx_tail, lp->chan_state); | ||
| 1594 | return 0; | ||
| 1595 | } | ||
| 1596 | |||
| 1597 | udelay(1); | ||
| 1598 | } | ||
| 1599 | return -EAGAIN; | ||
| 1600 | } | ||
| 1601 | |||
| 1602 | static int rx_set_head(struct ldc_channel *lp, unsigned long head) | ||
| 1603 | { | ||
| 1604 | int err = __set_rx_head(lp, head); | ||
| 1605 | |||
| 1606 | if (err < 0) | ||
| 1607 | return ldc_abort(lp); | ||
| 1608 | |||
| 1609 | lp->rx_head = head; | ||
| 1610 | return 0; | ||
| 1611 | } | ||
| 1612 | |||
| 1613 | static void send_data_ack(struct ldc_channel *lp) | ||
| 1614 | { | ||
| 1615 | unsigned long new_tail; | ||
| 1616 | struct ldc_packet *p; | ||
| 1617 | |||
| 1618 | p = data_get_tx_packet(lp, &new_tail); | ||
| 1619 | if (likely(p)) { | ||
| 1620 | int err; | ||
| 1621 | |||
| 1622 | memset(p, 0, sizeof(*p)); | ||
| 1623 | p->type = LDC_DATA; | ||
| 1624 | p->stype = LDC_ACK; | ||
| 1625 | p->ctrl = 0; | ||
| 1626 | p->seqid = lp->snd_nxt + 1; | ||
| 1627 | p->u.r.ackid = lp->rcv_nxt; | ||
| 1628 | |||
| 1629 | err = send_tx_packet(lp, p, new_tail); | ||
| 1630 | if (!err) | ||
| 1631 | lp->snd_nxt++; | ||
| 1632 | } | ||
| 1633 | } | ||
| 1634 | |||
| 1635 | static int read_nonraw(struct ldc_channel *lp, void *buf, unsigned int size) | ||
| 1636 | { | ||
| 1637 | struct ldc_packet *first_frag; | ||
| 1638 | unsigned long hv_err, new; | ||
| 1639 | int err, copied; | ||
| 1640 | |||
| 1641 | hv_err = sun4v_ldc_rx_get_state(lp->id, | ||
| 1642 | &lp->rx_head, | ||
| 1643 | &lp->rx_tail, | ||
| 1644 | &lp->chan_state); | ||
| 1645 | if (hv_err) | ||
| 1646 | return ldc_abort(lp); | ||
| 1647 | |||
| 1648 | if (lp->chan_state == LDC_CHANNEL_DOWN || | ||
| 1649 | lp->chan_state == LDC_CHANNEL_RESETTING) | ||
| 1650 | return -ECONNRESET; | ||
| 1651 | |||
| 1652 | if (lp->rx_head == lp->rx_tail) | ||
| 1653 | return 0; | ||
| 1654 | |||
| 1655 | first_frag = NULL; | ||
| 1656 | copied = err = 0; | ||
| 1657 | new = lp->rx_head; | ||
| 1658 | while (1) { | ||
| 1659 | struct ldc_packet *p; | ||
| 1660 | int pkt_len; | ||
| 1661 | |||
| 1662 | BUG_ON(new == lp->rx_tail); | ||
| 1663 | p = lp->rx_base + (new / LDC_PACKET_SIZE); | ||
| 1664 | |||
| 1665 | ldcdbg(RX, "RX read pkt[%02x:%02x:%02x:%02x:%08x:%08x] " | ||
| 1666 | "rcv_nxt[%08x]\n", | ||
| 1667 | p->type, | ||
| 1668 | p->stype, | ||
| 1669 | p->ctrl, | ||
| 1670 | p->env, | ||
| 1671 | p->seqid, | ||
| 1672 | p->u.r.ackid, | ||
| 1673 | lp->rcv_nxt); | ||
| 1674 | |||
| 1675 | if (unlikely(!rx_seq_ok(lp, p->seqid))) { | ||
| 1676 | err = rx_bad_seq(lp, p, first_frag); | ||
| 1677 | copied = 0; | ||
| 1678 | break; | ||
| 1679 | } | ||
| 1680 | |||
| 1681 | if (p->type & LDC_CTRL) { | ||
| 1682 | err = process_control_frame(lp, p); | ||
| 1683 | if (err < 0) | ||
| 1684 | break; | ||
| 1685 | err = 0; | ||
| 1686 | } | ||
| 1687 | |||
| 1688 | lp->rcv_nxt = p->seqid; | ||
| 1689 | |||
| 1690 | if (!(p->type & LDC_DATA)) { | ||
| 1691 | new = rx_advance(lp, new); | ||
| 1692 | goto no_data; | ||
| 1693 | } | ||
| 1694 | if (p->stype & (LDC_ACK | LDC_NACK)) { | ||
| 1695 | err = data_ack_nack(lp, p); | ||
| 1696 | if (err) | ||
| 1697 | break; | ||
| 1698 | } | ||
| 1699 | if (!(p->stype & LDC_INFO)) { | ||
| 1700 | new = rx_advance(lp, new); | ||
| 1701 | err = rx_set_head(lp, new); | ||
| 1702 | if (err) | ||
| 1703 | break; | ||
| 1704 | goto no_data; | ||
| 1705 | } | ||
| 1706 | |||
| 1707 | pkt_len = p->env & LDC_LEN; | ||
| 1708 | |||
| 1709 | /* Every initial packet starts with the START bit set. | ||
| 1710 | * | ||
| 1711 | * Singleton packets will have both START+STOP set. | ||
| 1712 | * | ||
| 1713 | * Fragments will have START set in the first frame, STOP | ||
| 1714 | * set in the last frame, and neither bit set in middle | ||
| 1715 | * frames of the packet. | ||
| 1716 | * | ||
| 1717 | * Therefore if we are at the beginning of a packet and | ||
| 1718 | * we don't see START, or we are in the middle of a fragmented | ||
| 1719 | * packet and do see START, we are unsynchronized and should | ||
| 1720 | * flush the RX queue. | ||
| 1721 | */ | ||
| 1722 | if ((first_frag == NULL && !(p->env & LDC_START)) || | ||
| 1723 | (first_frag != NULL && (p->env & LDC_START))) { | ||
| 1724 | if (!first_frag) | ||
| 1725 | new = rx_advance(lp, new); | ||
| 1726 | |||
| 1727 | err = rx_set_head(lp, new); | ||
| 1728 | if (err) | ||
| 1729 | break; | ||
| 1730 | |||
| 1731 | if (!first_frag) | ||
| 1732 | goto no_data; | ||
| 1733 | } | ||
| 1734 | if (!first_frag) | ||
| 1735 | first_frag = p; | ||
| 1736 | |||
| 1737 | if (pkt_len > size - copied) { | ||
| 1738 | /* User didn't give us a big enough buffer, | ||
| 1739 | * what to do? This is a pretty serious error. | ||
| 1740 | * | ||
| 1741 | * Since we haven't updated the RX ring head to | ||
| 1742 | * consume any of the packets, signal the error | ||
| 1743 | * to the user and just leave the RX ring alone. | ||
| 1744 | * | ||
| 1745 | * This seems the best behavior because this allows | ||
| 1746 | * a user of the LDC layer to start with a small | ||
| 1747 | * RX buffer for ldc_read() calls and use -EMSGSIZE | ||
| 1748 | * as a cue to enlarge it's read buffer. | ||
| 1749 | */ | ||
| 1750 | err = -EMSGSIZE; | ||
| 1751 | break; | ||
| 1752 | } | ||
| 1753 | |||
| 1754 | /* Ok, we are gonna eat this one. */ | ||
| 1755 | new = rx_advance(lp, new); | ||
| 1756 | |||
| 1757 | memcpy(buf, | ||
| 1758 | (lp->cfg.mode == LDC_MODE_UNRELIABLE ? | ||
| 1759 | p->u.u_data : p->u.r.r_data), pkt_len); | ||
| 1760 | buf += pkt_len; | ||
| 1761 | copied += pkt_len; | ||
| 1762 | |||
| 1763 | if (p->env & LDC_STOP) | ||
| 1764 | break; | ||
| 1765 | |||
| 1766 | no_data: | ||
| 1767 | if (new == lp->rx_tail) { | ||
| 1768 | err = rx_data_wait(lp, new); | ||
| 1769 | if (err) | ||
| 1770 | break; | ||
| 1771 | } | ||
| 1772 | } | ||
| 1773 | |||
| 1774 | if (!err) | ||
| 1775 | err = rx_set_head(lp, new); | ||
| 1776 | |||
| 1777 | if (err && first_frag) | ||
| 1778 | lp->rcv_nxt = first_frag->seqid - 1; | ||
| 1779 | |||
| 1780 | if (!err) { | ||
| 1781 | err = copied; | ||
| 1782 | if (err > 0 && lp->cfg.mode != LDC_MODE_UNRELIABLE) | ||
| 1783 | send_data_ack(lp); | ||
| 1784 | } | ||
| 1785 | |||
| 1786 | return err; | ||
| 1787 | } | ||
| 1788 | |||
| 1789 | static const struct ldc_mode_ops nonraw_ops = { | ||
| 1790 | .write = write_nonraw, | ||
| 1791 | .read = read_nonraw, | ||
| 1792 | }; | ||
| 1793 | |||
| 1794 | static int write_stream(struct ldc_channel *lp, const void *buf, | ||
| 1795 | unsigned int size) | ||
| 1796 | { | ||
| 1797 | if (size > lp->cfg.mtu) | ||
| 1798 | size = lp->cfg.mtu; | ||
| 1799 | return write_nonraw(lp, buf, size); | ||
| 1800 | } | ||
| 1801 | |||
| 1802 | static int read_stream(struct ldc_channel *lp, void *buf, unsigned int size) | ||
| 1803 | { | ||
| 1804 | if (!lp->mssbuf_len) { | ||
| 1805 | int err = read_nonraw(lp, lp->mssbuf, lp->cfg.mtu); | ||
| 1806 | if (err < 0) | ||
| 1807 | return err; | ||
| 1808 | |||
| 1809 | lp->mssbuf_len = err; | ||
| 1810 | lp->mssbuf_off = 0; | ||
| 1811 | } | ||
| 1812 | |||
| 1813 | if (size > lp->mssbuf_len) | ||
| 1814 | size = lp->mssbuf_len; | ||
| 1815 | memcpy(buf, lp->mssbuf + lp->mssbuf_off, size); | ||
| 1816 | |||
| 1817 | lp->mssbuf_off += size; | ||
| 1818 | lp->mssbuf_len -= size; | ||
| 1819 | |||
| 1820 | return size; | ||
| 1821 | } | ||
| 1822 | |||
| 1823 | static const struct ldc_mode_ops stream_ops = { | ||
| 1824 | .write = write_stream, | ||
| 1825 | .read = read_stream, | ||
| 1826 | }; | ||
| 1827 | |||
| 1828 | int ldc_write(struct ldc_channel *lp, const void *buf, unsigned int size) | ||
| 1829 | { | ||
| 1830 | unsigned long flags; | ||
| 1831 | int err; | ||
| 1832 | |||
| 1833 | if (!buf) | ||
| 1834 | return -EINVAL; | ||
| 1835 | |||
| 1836 | if (!size) | ||
| 1837 | return 0; | ||
| 1838 | |||
| 1839 | spin_lock_irqsave(&lp->lock, flags); | ||
| 1840 | |||
| 1841 | if (lp->hs_state != LDC_HS_COMPLETE) | ||
| 1842 | err = -ENOTCONN; | ||
| 1843 | else | ||
| 1844 | err = lp->mops->write(lp, buf, size); | ||
| 1845 | |||
| 1846 | spin_unlock_irqrestore(&lp->lock, flags); | ||
| 1847 | |||
| 1848 | return err; | ||
| 1849 | } | ||
| 1850 | EXPORT_SYMBOL(ldc_write); | ||
| 1851 | |||
| 1852 | int ldc_read(struct ldc_channel *lp, void *buf, unsigned int size) | ||
| 1853 | { | ||
| 1854 | unsigned long flags; | ||
| 1855 | int err; | ||
| 1856 | |||
| 1857 | if (!buf) | ||
| 1858 | return -EINVAL; | ||
| 1859 | |||
| 1860 | if (!size) | ||
| 1861 | return 0; | ||
| 1862 | |||
| 1863 | spin_lock_irqsave(&lp->lock, flags); | ||
| 1864 | |||
| 1865 | if (lp->hs_state != LDC_HS_COMPLETE) | ||
| 1866 | err = -ENOTCONN; | ||
| 1867 | else | ||
| 1868 | err = lp->mops->read(lp, buf, size); | ||
| 1869 | |||
| 1870 | spin_unlock_irqrestore(&lp->lock, flags); | ||
| 1871 | |||
| 1872 | return err; | ||
| 1873 | } | ||
| 1874 | EXPORT_SYMBOL(ldc_read); | ||
| 1875 | |||
| 1876 | static long arena_alloc(struct ldc_iommu *iommu, unsigned long npages) | ||
| 1877 | { | ||
| 1878 | struct iommu_arena *arena = &iommu->arena; | ||
| 1879 | unsigned long n, i, start, end, limit; | ||
| 1880 | int pass; | ||
| 1881 | |||
| 1882 | limit = arena->limit; | ||
| 1883 | start = arena->hint; | ||
| 1884 | pass = 0; | ||
| 1885 | |||
| 1886 | again: | ||
| 1887 | n = find_next_zero_bit(arena->map, limit, start); | ||
| 1888 | end = n + npages; | ||
| 1889 | if (unlikely(end >= limit)) { | ||
| 1890 | if (likely(pass < 1)) { | ||
| 1891 | limit = start; | ||
| 1892 | start = 0; | ||
| 1893 | pass++; | ||
| 1894 | goto again; | ||
| 1895 | } else { | ||
| 1896 | /* Scanned the whole thing, give up. */ | ||
| 1897 | return -1; | ||
| 1898 | } | ||
| 1899 | } | ||
| 1900 | |||
| 1901 | for (i = n; i < end; i++) { | ||
| 1902 | if (test_bit(i, arena->map)) { | ||
| 1903 | start = i + 1; | ||
| 1904 | goto again; | ||
| 1905 | } | ||
| 1906 | } | ||
| 1907 | |||
| 1908 | for (i = n; i < end; i++) | ||
| 1909 | __set_bit(i, arena->map); | ||
| 1910 | |||
| 1911 | arena->hint = end; | ||
| 1912 | |||
| 1913 | return n; | ||
| 1914 | } | ||
| 1915 | |||
| 1916 | #define COOKIE_PGSZ_CODE 0xf000000000000000ULL | ||
| 1917 | #define COOKIE_PGSZ_CODE_SHIFT 60ULL | ||
| 1918 | |||
| 1919 | static u64 pagesize_code(void) | ||
| 1920 | { | ||
| 1921 | switch (PAGE_SIZE) { | ||
| 1922 | default: | ||
| 1923 | case (8ULL * 1024ULL): | ||
| 1924 | return 0; | ||
| 1925 | case (64ULL * 1024ULL): | ||
| 1926 | return 1; | ||
| 1927 | case (512ULL * 1024ULL): | ||
| 1928 | return 2; | ||
| 1929 | case (4ULL * 1024ULL * 1024ULL): | ||
| 1930 | return 3; | ||
| 1931 | case (32ULL * 1024ULL * 1024ULL): | ||
| 1932 | return 4; | ||
| 1933 | case (256ULL * 1024ULL * 1024ULL): | ||
| 1934 | return 5; | ||
| 1935 | } | ||
| 1936 | } | ||
| 1937 | |||
| 1938 | static u64 make_cookie(u64 index, u64 pgsz_code, u64 page_offset) | ||
| 1939 | { | ||
| 1940 | return ((pgsz_code << COOKIE_PGSZ_CODE_SHIFT) | | ||
| 1941 | (index << PAGE_SHIFT) | | ||
| 1942 | page_offset); | ||
| 1943 | } | ||
| 1944 | |||
| 1945 | static u64 cookie_to_index(u64 cookie, unsigned long *shift) | ||
| 1946 | { | ||
| 1947 | u64 szcode = cookie >> COOKIE_PGSZ_CODE_SHIFT; | ||
| 1948 | |||
| 1949 | cookie &= ~COOKIE_PGSZ_CODE; | ||
| 1950 | |||
| 1951 | *shift = szcode * 3; | ||
| 1952 | |||
| 1953 | return (cookie >> (13ULL + (szcode * 3ULL))); | ||
| 1954 | } | ||
| 1955 | |||
| 1956 | static struct ldc_mtable_entry *alloc_npages(struct ldc_iommu *iommu, | ||
| 1957 | unsigned long npages) | ||
| 1958 | { | ||
| 1959 | long entry; | ||
| 1960 | |||
| 1961 | entry = arena_alloc(iommu, npages); | ||
| 1962 | if (unlikely(entry < 0)) | ||
| 1963 | return NULL; | ||
| 1964 | |||
| 1965 | return iommu->page_table + entry; | ||
| 1966 | } | ||
| 1967 | |||
| 1968 | static u64 perm_to_mte(unsigned int map_perm) | ||
| 1969 | { | ||
| 1970 | u64 mte_base; | ||
| 1971 | |||
| 1972 | mte_base = pagesize_code(); | ||
| 1973 | |||
| 1974 | if (map_perm & LDC_MAP_SHADOW) { | ||
| 1975 | if (map_perm & LDC_MAP_R) | ||
| 1976 | mte_base |= LDC_MTE_COPY_R; | ||
| 1977 | if (map_perm & LDC_MAP_W) | ||
| 1978 | mte_base |= LDC_MTE_COPY_W; | ||
| 1979 | } | ||
| 1980 | if (map_perm & LDC_MAP_DIRECT) { | ||
| 1981 | if (map_perm & LDC_MAP_R) | ||
| 1982 | mte_base |= LDC_MTE_READ; | ||
| 1983 | if (map_perm & LDC_MAP_W) | ||
| 1984 | mte_base |= LDC_MTE_WRITE; | ||
| 1985 | if (map_perm & LDC_MAP_X) | ||
| 1986 | mte_base |= LDC_MTE_EXEC; | ||
| 1987 | } | ||
| 1988 | if (map_perm & LDC_MAP_IO) { | ||
| 1989 | if (map_perm & LDC_MAP_R) | ||
| 1990 | mte_base |= LDC_MTE_IOMMU_R; | ||
| 1991 | if (map_perm & LDC_MAP_W) | ||
| 1992 | mte_base |= LDC_MTE_IOMMU_W; | ||
| 1993 | } | ||
| 1994 | |||
| 1995 | return mte_base; | ||
| 1996 | } | ||
| 1997 | |||
| 1998 | static int pages_in_region(unsigned long base, long len) | ||
| 1999 | { | ||
| 2000 | int count = 0; | ||
| 2001 | |||
| 2002 | do { | ||
| 2003 | unsigned long new = (base + PAGE_SIZE) & PAGE_MASK; | ||
| 2004 | |||
| 2005 | len -= (new - base); | ||
| 2006 | base = new; | ||
| 2007 | count++; | ||
| 2008 | } while (len > 0); | ||
| 2009 | |||
| 2010 | return count; | ||
| 2011 | } | ||
| 2012 | |||
| 2013 | struct cookie_state { | ||
| 2014 | struct ldc_mtable_entry *page_table; | ||
| 2015 | struct ldc_trans_cookie *cookies; | ||
| 2016 | u64 mte_base; | ||
| 2017 | u64 prev_cookie; | ||
| 2018 | u32 pte_idx; | ||
| 2019 | u32 nc; | ||
| 2020 | }; | ||
| 2021 | |||
| 2022 | static void fill_cookies(struct cookie_state *sp, unsigned long pa, | ||
| 2023 | unsigned long off, unsigned long len) | ||
| 2024 | { | ||
| 2025 | do { | ||
| 2026 | unsigned long tlen, new = pa + PAGE_SIZE; | ||
| 2027 | u64 this_cookie; | ||
| 2028 | |||
| 2029 | sp->page_table[sp->pte_idx].mte = sp->mte_base | pa; | ||
| 2030 | |||
| 2031 | tlen = PAGE_SIZE; | ||
| 2032 | if (off) | ||
| 2033 | tlen = PAGE_SIZE - off; | ||
| 2034 | if (tlen > len) | ||
| 2035 | tlen = len; | ||
| 2036 | |||
| 2037 | this_cookie = make_cookie(sp->pte_idx, | ||
| 2038 | pagesize_code(), off); | ||
| 2039 | |||
| 2040 | off = 0; | ||
| 2041 | |||
| 2042 | if (this_cookie == sp->prev_cookie) { | ||
| 2043 | sp->cookies[sp->nc - 1].cookie_size += tlen; | ||
| 2044 | } else { | ||
| 2045 | sp->cookies[sp->nc].cookie_addr = this_cookie; | ||
| 2046 | sp->cookies[sp->nc].cookie_size = tlen; | ||
| 2047 | sp->nc++; | ||
| 2048 | } | ||
| 2049 | sp->prev_cookie = this_cookie + tlen; | ||
| 2050 | |||
| 2051 | sp->pte_idx++; | ||
| 2052 | |||
| 2053 | len -= tlen; | ||
| 2054 | pa = new; | ||
| 2055 | } while (len > 0); | ||
| 2056 | } | ||
| 2057 | |||
| 2058 | static int sg_count_one(struct scatterlist *sg) | ||
| 2059 | { | ||
| 2060 | unsigned long base = page_to_pfn(sg->page) << PAGE_SHIFT; | ||
| 2061 | long len = sg->length; | ||
| 2062 | |||
| 2063 | if ((sg->offset | len) & (8UL - 1)) | ||
| 2064 | return -EFAULT; | ||
| 2065 | |||
| 2066 | return pages_in_region(base + sg->offset, len); | ||
| 2067 | } | ||
| 2068 | |||
| 2069 | static int sg_count_pages(struct scatterlist *sg, int num_sg) | ||
| 2070 | { | ||
| 2071 | int count; | ||
| 2072 | int i; | ||
| 2073 | |||
| 2074 | count = 0; | ||
| 2075 | for (i = 0; i < num_sg; i++) { | ||
| 2076 | int err = sg_count_one(sg + i); | ||
| 2077 | if (err < 0) | ||
| 2078 | return err; | ||
| 2079 | count += err; | ||
| 2080 | } | ||
| 2081 | |||
| 2082 | return count; | ||
| 2083 | } | ||
| 2084 | |||
| 2085 | int ldc_map_sg(struct ldc_channel *lp, | ||
| 2086 | struct scatterlist *sg, int num_sg, | ||
| 2087 | struct ldc_trans_cookie *cookies, int ncookies, | ||
| 2088 | unsigned int map_perm) | ||
| 2089 | { | ||
| 2090 | unsigned long i, npages, flags; | ||
| 2091 | struct ldc_mtable_entry *base; | ||
| 2092 | struct cookie_state state; | ||
| 2093 | struct ldc_iommu *iommu; | ||
| 2094 | int err; | ||
| 2095 | |||
| 2096 | if (map_perm & ~LDC_MAP_ALL) | ||
| 2097 | return -EINVAL; | ||
| 2098 | |||
| 2099 | err = sg_count_pages(sg, num_sg); | ||
| 2100 | if (err < 0) | ||
| 2101 | return err; | ||
| 2102 | |||
| 2103 | npages = err; | ||
| 2104 | if (err > ncookies) | ||
| 2105 | return -EMSGSIZE; | ||
| 2106 | |||
| 2107 | iommu = &lp->iommu; | ||
| 2108 | |||
| 2109 | spin_lock_irqsave(&iommu->lock, flags); | ||
| 2110 | base = alloc_npages(iommu, npages); | ||
| 2111 | spin_unlock_irqrestore(&iommu->lock, flags); | ||
| 2112 | |||
| 2113 | if (!base) | ||
| 2114 | return -ENOMEM; | ||
| 2115 | |||
| 2116 | state.page_table = iommu->page_table; | ||
| 2117 | state.cookies = cookies; | ||
| 2118 | state.mte_base = perm_to_mte(map_perm); | ||
| 2119 | state.prev_cookie = ~(u64)0; | ||
| 2120 | state.pte_idx = (base - iommu->page_table); | ||
| 2121 | state.nc = 0; | ||
| 2122 | |||
| 2123 | for (i = 0; i < num_sg; i++) | ||
| 2124 | fill_cookies(&state, page_to_pfn(sg[i].page) << PAGE_SHIFT, | ||
| 2125 | sg[i].offset, sg[i].length); | ||
| 2126 | |||
| 2127 | return state.nc; | ||
| 2128 | } | ||
| 2129 | EXPORT_SYMBOL(ldc_map_sg); | ||
| 2130 | |||
| 2131 | int ldc_map_single(struct ldc_channel *lp, | ||
| 2132 | void *buf, unsigned int len, | ||
| 2133 | struct ldc_trans_cookie *cookies, int ncookies, | ||
| 2134 | unsigned int map_perm) | ||
| 2135 | { | ||
| 2136 | unsigned long npages, pa, flags; | ||
| 2137 | struct ldc_mtable_entry *base; | ||
| 2138 | struct cookie_state state; | ||
| 2139 | struct ldc_iommu *iommu; | ||
| 2140 | |||
| 2141 | if ((map_perm & ~LDC_MAP_ALL) || (ncookies < 1)) | ||
| 2142 | return -EINVAL; | ||
| 2143 | |||
| 2144 | pa = __pa(buf); | ||
| 2145 | if ((pa | len) & (8UL - 1)) | ||
| 2146 | return -EFAULT; | ||
| 2147 | |||
| 2148 | npages = pages_in_region(pa, len); | ||
| 2149 | |||
| 2150 | iommu = &lp->iommu; | ||
| 2151 | |||
| 2152 | spin_lock_irqsave(&iommu->lock, flags); | ||
| 2153 | base = alloc_npages(iommu, npages); | ||
| 2154 | spin_unlock_irqrestore(&iommu->lock, flags); | ||
| 2155 | |||
| 2156 | if (!base) | ||
| 2157 | return -ENOMEM; | ||
| 2158 | |||
| 2159 | state.page_table = iommu->page_table; | ||
| 2160 | state.cookies = cookies; | ||
| 2161 | state.mte_base = perm_to_mte(map_perm); | ||
| 2162 | state.prev_cookie = ~(u64)0; | ||
| 2163 | state.pte_idx = (base - iommu->page_table); | ||
| 2164 | state.nc = 0; | ||
| 2165 | fill_cookies(&state, (pa & PAGE_MASK), (pa & ~PAGE_MASK), len); | ||
| 2166 | BUG_ON(state.nc != 1); | ||
| 2167 | |||
| 2168 | return state.nc; | ||
| 2169 | } | ||
| 2170 | EXPORT_SYMBOL(ldc_map_single); | ||
| 2171 | |||
| 2172 | static void free_npages(unsigned long id, struct ldc_iommu *iommu, | ||
| 2173 | u64 cookie, u64 size) | ||
| 2174 | { | ||
| 2175 | struct iommu_arena *arena = &iommu->arena; | ||
| 2176 | unsigned long i, shift, index, npages; | ||
| 2177 | struct ldc_mtable_entry *base; | ||
| 2178 | |||
| 2179 | npages = PAGE_ALIGN(((cookie & ~PAGE_MASK) + size)) >> PAGE_SHIFT; | ||
| 2180 | index = cookie_to_index(cookie, &shift); | ||
| 2181 | base = iommu->page_table + index; | ||
| 2182 | |||
| 2183 | BUG_ON(index > arena->limit || | ||
| 2184 | (index + npages) > arena->limit); | ||
| 2185 | |||
| 2186 | for (i = 0; i < npages; i++) { | ||
| 2187 | if (base->cookie) | ||
| 2188 | sun4v_ldc_revoke(id, cookie + (i << shift), | ||
| 2189 | base->cookie); | ||
| 2190 | base->mte = 0; | ||
| 2191 | __clear_bit(index + i, arena->map); | ||
| 2192 | } | ||
| 2193 | } | ||
| 2194 | |||
| 2195 | void ldc_unmap(struct ldc_channel *lp, struct ldc_trans_cookie *cookies, | ||
| 2196 | int ncookies) | ||
| 2197 | { | ||
| 2198 | struct ldc_iommu *iommu = &lp->iommu; | ||
| 2199 | unsigned long flags; | ||
| 2200 | int i; | ||
| 2201 | |||
| 2202 | spin_lock_irqsave(&iommu->lock, flags); | ||
| 2203 | for (i = 0; i < ncookies; i++) { | ||
| 2204 | u64 addr = cookies[i].cookie_addr; | ||
| 2205 | u64 size = cookies[i].cookie_size; | ||
| 2206 | |||
| 2207 | free_npages(lp->id, iommu, addr, size); | ||
| 2208 | } | ||
| 2209 | spin_unlock_irqrestore(&iommu->lock, flags); | ||
| 2210 | } | ||
| 2211 | EXPORT_SYMBOL(ldc_unmap); | ||
| 2212 | |||
| 2213 | int ldc_copy(struct ldc_channel *lp, int copy_dir, | ||
| 2214 | void *buf, unsigned int len, unsigned long offset, | ||
| 2215 | struct ldc_trans_cookie *cookies, int ncookies) | ||
| 2216 | { | ||
| 2217 | unsigned int orig_len; | ||
| 2218 | unsigned long ra; | ||
| 2219 | int i; | ||
| 2220 | |||
| 2221 | if (copy_dir != LDC_COPY_IN && copy_dir != LDC_COPY_OUT) { | ||
| 2222 | printk(KERN_ERR PFX "ldc_copy: ID[%lu] Bad copy_dir[%d]\n", | ||
| 2223 | lp->id, copy_dir); | ||
| 2224 | return -EINVAL; | ||
| 2225 | } | ||
| 2226 | |||
| 2227 | ra = __pa(buf); | ||
| 2228 | if ((ra | len | offset) & (8UL - 1)) { | ||
| 2229 | printk(KERN_ERR PFX "ldc_copy: ID[%lu] Unaligned buffer " | ||
| 2230 | "ra[%lx] len[%x] offset[%lx]\n", | ||
| 2231 | lp->id, ra, len, offset); | ||
| 2232 | return -EFAULT; | ||
| 2233 | } | ||
| 2234 | |||
| 2235 | if (lp->hs_state != LDC_HS_COMPLETE || | ||
| 2236 | (lp->flags & LDC_FLAG_RESET)) { | ||
| 2237 | printk(KERN_ERR PFX "ldc_copy: ID[%lu] Link down hs_state[%x] " | ||
| 2238 | "flags[%x]\n", lp->id, lp->hs_state, lp->flags); | ||
| 2239 | return -ECONNRESET; | ||
| 2240 | } | ||
| 2241 | |||
| 2242 | orig_len = len; | ||
| 2243 | for (i = 0; i < ncookies; i++) { | ||
| 2244 | unsigned long cookie_raddr = cookies[i].cookie_addr; | ||
| 2245 | unsigned long this_len = cookies[i].cookie_size; | ||
| 2246 | unsigned long actual_len; | ||
| 2247 | |||
| 2248 | if (unlikely(offset)) { | ||
| 2249 | unsigned long this_off = offset; | ||
| 2250 | |||
| 2251 | if (this_off > this_len) | ||
| 2252 | this_off = this_len; | ||
| 2253 | |||
| 2254 | offset -= this_off; | ||
| 2255 | this_len -= this_off; | ||
| 2256 | if (!this_len) | ||
| 2257 | continue; | ||
| 2258 | cookie_raddr += this_off; | ||
| 2259 | } | ||
| 2260 | |||
| 2261 | if (this_len > len) | ||
| 2262 | this_len = len; | ||
| 2263 | |||
| 2264 | while (1) { | ||
| 2265 | unsigned long hv_err; | ||
| 2266 | |||
| 2267 | hv_err = sun4v_ldc_copy(lp->id, copy_dir, | ||
| 2268 | cookie_raddr, ra, | ||
| 2269 | this_len, &actual_len); | ||
| 2270 | if (unlikely(hv_err)) { | ||
| 2271 | printk(KERN_ERR PFX "ldc_copy: ID[%lu] " | ||
| 2272 | "HV error %lu\n", | ||
| 2273 | lp->id, hv_err); | ||
| 2274 | if (lp->hs_state != LDC_HS_COMPLETE || | ||
| 2275 | (lp->flags & LDC_FLAG_RESET)) | ||
| 2276 | return -ECONNRESET; | ||
| 2277 | else | ||
| 2278 | return -EFAULT; | ||
| 2279 | } | ||
| 2280 | |||
| 2281 | cookie_raddr += actual_len; | ||
| 2282 | ra += actual_len; | ||
| 2283 | len -= actual_len; | ||
| 2284 | if (actual_len == this_len) | ||
| 2285 | break; | ||
| 2286 | |||
| 2287 | this_len -= actual_len; | ||
| 2288 | } | ||
| 2289 | |||
| 2290 | if (!len) | ||
| 2291 | break; | ||
| 2292 | } | ||
| 2293 | |||
| 2294 | /* It is caller policy what to do about short copies. | ||
| 2295 | * For example, a networking driver can declare the | ||
| 2296 | * packet a runt and drop it. | ||
| 2297 | */ | ||
| 2298 | |||
| 2299 | return orig_len - len; | ||
| 2300 | } | ||
| 2301 | EXPORT_SYMBOL(ldc_copy); | ||
| 2302 | |||
| 2303 | void *ldc_alloc_exp_dring(struct ldc_channel *lp, unsigned int len, | ||
| 2304 | struct ldc_trans_cookie *cookies, int *ncookies, | ||
| 2305 | unsigned int map_perm) | ||
| 2306 | { | ||
| 2307 | void *buf; | ||
| 2308 | int err; | ||
| 2309 | |||
| 2310 | if (len & (8UL - 1)) | ||
| 2311 | return ERR_PTR(-EINVAL); | ||
| 2312 | |||
| 2313 | buf = kzalloc(len, GFP_KERNEL); | ||
| 2314 | if (!buf) | ||
| 2315 | return ERR_PTR(-ENOMEM); | ||
| 2316 | |||
| 2317 | err = ldc_map_single(lp, buf, len, cookies, *ncookies, map_perm); | ||
| 2318 | if (err < 0) { | ||
| 2319 | kfree(buf); | ||
| 2320 | return ERR_PTR(err); | ||
| 2321 | } | ||
| 2322 | *ncookies = err; | ||
| 2323 | |||
| 2324 | return buf; | ||
| 2325 | } | ||
| 2326 | EXPORT_SYMBOL(ldc_alloc_exp_dring); | ||
| 2327 | |||
| 2328 | void ldc_free_exp_dring(struct ldc_channel *lp, void *buf, unsigned int len, | ||
| 2329 | struct ldc_trans_cookie *cookies, int ncookies) | ||
| 2330 | { | ||
| 2331 | ldc_unmap(lp, cookies, ncookies); | ||
| 2332 | kfree(buf); | ||
| 2333 | } | ||
| 2334 | EXPORT_SYMBOL(ldc_free_exp_dring); | ||
| 2335 | |||
| 2336 | static int __init ldc_init(void) | ||
| 2337 | { | ||
| 2338 | unsigned long major, minor; | ||
| 2339 | struct mdesc_handle *hp; | ||
| 2340 | const u64 *v; | ||
| 2341 | u64 mp; | ||
| 2342 | |||
| 2343 | hp = mdesc_grab(); | ||
| 2344 | if (!hp) | ||
| 2345 | return -ENODEV; | ||
| 2346 | |||
| 2347 | mp = mdesc_node_by_name(hp, MDESC_NODE_NULL, "platform"); | ||
| 2348 | if (mp == MDESC_NODE_NULL) | ||
| 2349 | return -ENODEV; | ||
| 2350 | |||
| 2351 | v = mdesc_get_property(hp, mp, "domaining-enabled", NULL); | ||
| 2352 | if (!v) | ||
| 2353 | return -ENODEV; | ||
| 2354 | |||
| 2355 | major = 1; | ||
| 2356 | minor = 0; | ||
| 2357 | if (sun4v_hvapi_register(HV_GRP_LDOM, major, &minor)) { | ||
| 2358 | printk(KERN_INFO PFX "Could not register LDOM hvapi.\n"); | ||
| 2359 | return -ENODEV; | ||
| 2360 | } | ||
| 2361 | |||
| 2362 | printk(KERN_INFO "%s", version); | ||
| 2363 | |||
| 2364 | if (!*v) { | ||
| 2365 | printk(KERN_INFO PFX "Domaining disabled.\n"); | ||
| 2366 | return -ENODEV; | ||
| 2367 | } | ||
| 2368 | ldom_domaining_enabled = 1; | ||
| 2369 | |||
| 2370 | return 0; | ||
| 2371 | } | ||
| 2372 | |||
| 2373 | core_initcall(ldc_init); | ||
diff --git a/arch/sparc64/kernel/mdesc.c b/arch/sparc64/kernel/mdesc.c index f0e16045fb16..62a389793949 100644 --- a/arch/sparc64/kernel/mdesc.c +++ b/arch/sparc64/kernel/mdesc.c | |||
| @@ -6,6 +6,9 @@ | |||
| 6 | #include <linux/types.h> | 6 | #include <linux/types.h> |
| 7 | #include <linux/bootmem.h> | 7 | #include <linux/bootmem.h> |
| 8 | #include <linux/log2.h> | 8 | #include <linux/log2.h> |
| 9 | #include <linux/list.h> | ||
| 10 | #include <linux/slab.h> | ||
| 11 | #include <linux/mm.h> | ||
| 9 | 12 | ||
| 10 | #include <asm/hypervisor.h> | 13 | #include <asm/hypervisor.h> |
| 11 | #include <asm/mdesc.h> | 14 | #include <asm/mdesc.h> |
| @@ -29,7 +32,7 @@ struct mdesc_hdr { | |||
| 29 | u32 node_sz; /* node block size */ | 32 | u32 node_sz; /* node block size */ |
| 30 | u32 name_sz; /* name block size */ | 33 | u32 name_sz; /* name block size */ |
| 31 | u32 data_sz; /* data block size */ | 34 | u32 data_sz; /* data block size */ |
| 32 | }; | 35 | } __attribute__((aligned(16))); |
| 33 | 36 | ||
| 34 | struct mdesc_elem { | 37 | struct mdesc_elem { |
| 35 | u8 tag; | 38 | u8 tag; |
| @@ -53,306 +56,402 @@ struct mdesc_elem { | |||
| 53 | } d; | 56 | } d; |
| 54 | }; | 57 | }; |
| 55 | 58 | ||
| 56 | static struct mdesc_hdr *main_mdesc; | 59 | struct mdesc_mem_ops { |
| 57 | static struct mdesc_node *allnodes; | 60 | struct mdesc_handle *(*alloc)(unsigned int mdesc_size); |
| 58 | 61 | void (*free)(struct mdesc_handle *handle); | |
| 59 | static struct mdesc_node *allnodes_tail; | 62 | }; |
| 60 | static unsigned int unique_id; | ||
| 61 | 63 | ||
| 62 | static struct mdesc_node **mdesc_hash; | 64 | struct mdesc_handle { |
| 63 | static unsigned int mdesc_hash_size; | 65 | struct list_head list; |
| 66 | struct mdesc_mem_ops *mops; | ||
| 67 | void *self_base; | ||
| 68 | atomic_t refcnt; | ||
| 69 | unsigned int handle_size; | ||
| 70 | struct mdesc_hdr mdesc; | ||
| 71 | }; | ||
| 64 | 72 | ||
| 65 | static inline unsigned int node_hashfn(u64 node) | 73 | static void mdesc_handle_init(struct mdesc_handle *hp, |
| 74 | unsigned int handle_size, | ||
| 75 | void *base) | ||
| 66 | { | 76 | { |
| 67 | return ((unsigned int) (node ^ (node >> 8) ^ (node >> 16))) | 77 | BUG_ON(((unsigned long)&hp->mdesc) & (16UL - 1)); |
| 68 | & (mdesc_hash_size - 1); | 78 | |
| 79 | memset(hp, 0, handle_size); | ||
| 80 | INIT_LIST_HEAD(&hp->list); | ||
| 81 | hp->self_base = base; | ||
| 82 | atomic_set(&hp->refcnt, 1); | ||
| 83 | hp->handle_size = handle_size; | ||
| 69 | } | 84 | } |
| 70 | 85 | ||
| 71 | static inline void hash_node(struct mdesc_node *mp) | 86 | static struct mdesc_handle *mdesc_bootmem_alloc(unsigned int mdesc_size) |
| 72 | { | 87 | { |
| 73 | struct mdesc_node **head = &mdesc_hash[node_hashfn(mp->node)]; | 88 | struct mdesc_handle *hp; |
| 89 | unsigned int handle_size, alloc_size; | ||
| 74 | 90 | ||
| 75 | mp->hash_next = *head; | 91 | handle_size = (sizeof(struct mdesc_handle) - |
| 76 | *head = mp; | 92 | sizeof(struct mdesc_hdr) + |
| 93 | mdesc_size); | ||
| 94 | alloc_size = PAGE_ALIGN(handle_size); | ||
| 77 | 95 | ||
| 78 | if (allnodes_tail) { | 96 | hp = __alloc_bootmem(alloc_size, PAGE_SIZE, 0UL); |
| 79 | allnodes_tail->allnodes_next = mp; | 97 | if (hp) |
| 80 | allnodes_tail = mp; | 98 | mdesc_handle_init(hp, handle_size, hp); |
| 81 | } else { | 99 | |
| 82 | allnodes = allnodes_tail = mp; | 100 | return hp; |
| 83 | } | ||
| 84 | } | 101 | } |
| 85 | 102 | ||
| 86 | static struct mdesc_node *find_node(u64 node) | 103 | static void mdesc_bootmem_free(struct mdesc_handle *hp) |
| 87 | { | 104 | { |
| 88 | struct mdesc_node *mp = mdesc_hash[node_hashfn(node)]; | 105 | unsigned int alloc_size, handle_size = hp->handle_size; |
| 106 | unsigned long start, end; | ||
| 107 | |||
| 108 | BUG_ON(atomic_read(&hp->refcnt) != 0); | ||
| 109 | BUG_ON(!list_empty(&hp->list)); | ||
| 89 | 110 | ||
| 90 | while (mp) { | 111 | alloc_size = PAGE_ALIGN(handle_size); |
| 91 | if (mp->node == node) | ||
| 92 | return mp; | ||
| 93 | 112 | ||
| 94 | mp = mp->hash_next; | 113 | start = (unsigned long) hp; |
| 114 | end = start + alloc_size; | ||
| 115 | |||
| 116 | while (start < end) { | ||
| 117 | struct page *p; | ||
| 118 | |||
| 119 | p = virt_to_page(start); | ||
| 120 | ClearPageReserved(p); | ||
| 121 | __free_page(p); | ||
| 122 | start += PAGE_SIZE; | ||
| 95 | } | 123 | } |
| 96 | return NULL; | ||
| 97 | } | 124 | } |
| 98 | 125 | ||
| 99 | struct property *md_find_property(const struct mdesc_node *mp, | 126 | static struct mdesc_mem_ops bootmem_mdesc_memops = { |
| 100 | const char *name, | 127 | .alloc = mdesc_bootmem_alloc, |
| 101 | int *lenp) | 128 | .free = mdesc_bootmem_free, |
| 129 | }; | ||
| 130 | |||
| 131 | static struct mdesc_handle *mdesc_kmalloc(unsigned int mdesc_size) | ||
| 102 | { | 132 | { |
| 103 | struct property *pp; | 133 | unsigned int handle_size; |
| 134 | void *base; | ||
| 104 | 135 | ||
| 105 | for (pp = mp->properties; pp != 0; pp = pp->next) { | 136 | handle_size = (sizeof(struct mdesc_handle) - |
| 106 | if (strcasecmp(pp->name, name) == 0) { | 137 | sizeof(struct mdesc_hdr) + |
| 107 | if (lenp) | 138 | mdesc_size); |
| 108 | *lenp = pp->length; | 139 | |
| 109 | break; | 140 | base = kmalloc(handle_size + 15, GFP_KERNEL); |
| 110 | } | 141 | if (base) { |
| 142 | struct mdesc_handle *hp; | ||
| 143 | unsigned long addr; | ||
| 144 | |||
| 145 | addr = (unsigned long)base; | ||
| 146 | addr = (addr + 15UL) & ~15UL; | ||
| 147 | hp = (struct mdesc_handle *) addr; | ||
| 148 | |||
| 149 | mdesc_handle_init(hp, handle_size, base); | ||
| 150 | return hp; | ||
| 111 | } | 151 | } |
| 112 | return pp; | 152 | |
| 153 | return NULL; | ||
| 113 | } | 154 | } |
| 114 | EXPORT_SYMBOL(md_find_property); | ||
| 115 | 155 | ||
| 116 | /* | 156 | static void mdesc_kfree(struct mdesc_handle *hp) |
| 117 | * Find a property with a given name for a given node | ||
| 118 | * and return the value. | ||
| 119 | */ | ||
| 120 | const void *md_get_property(const struct mdesc_node *mp, const char *name, | ||
| 121 | int *lenp) | ||
| 122 | { | 157 | { |
| 123 | struct property *pp = md_find_property(mp, name, lenp); | 158 | BUG_ON(atomic_read(&hp->refcnt) != 0); |
| 124 | return pp ? pp->value : NULL; | 159 | BUG_ON(!list_empty(&hp->list)); |
| 160 | |||
| 161 | kfree(hp->self_base); | ||
| 125 | } | 162 | } |
| 126 | EXPORT_SYMBOL(md_get_property); | ||
| 127 | 163 | ||
| 128 | struct mdesc_node *md_find_node_by_name(struct mdesc_node *from, | 164 | static struct mdesc_mem_ops kmalloc_mdesc_memops = { |
| 129 | const char *name) | 165 | .alloc = mdesc_kmalloc, |
| 166 | .free = mdesc_kfree, | ||
| 167 | }; | ||
| 168 | |||
| 169 | static struct mdesc_handle *mdesc_alloc(unsigned int mdesc_size, | ||
| 170 | struct mdesc_mem_ops *mops) | ||
| 130 | { | 171 | { |
| 131 | struct mdesc_node *mp; | 172 | struct mdesc_handle *hp = mops->alloc(mdesc_size); |
| 132 | 173 | ||
| 133 | mp = from ? from->allnodes_next : allnodes; | 174 | if (hp) |
| 134 | for (; mp != NULL; mp = mp->allnodes_next) { | 175 | hp->mops = mops; |
| 135 | if (strcmp(mp->name, name) == 0) | ||
| 136 | break; | ||
| 137 | } | ||
| 138 | return mp; | ||
| 139 | } | ||
| 140 | EXPORT_SYMBOL(md_find_node_by_name); | ||
| 141 | 176 | ||
| 142 | static unsigned int mdesc_early_allocated; | 177 | return hp; |
| 178 | } | ||
| 143 | 179 | ||
| 144 | static void * __init mdesc_early_alloc(unsigned long size) | 180 | static void mdesc_free(struct mdesc_handle *hp) |
| 145 | { | 181 | { |
| 146 | void *ret; | 182 | hp->mops->free(hp); |
| 183 | } | ||
| 147 | 184 | ||
| 148 | ret = __alloc_bootmem(size, SMP_CACHE_BYTES, 0UL); | 185 | static struct mdesc_handle *cur_mdesc; |
| 149 | if (ret == NULL) { | 186 | static LIST_HEAD(mdesc_zombie_list); |
| 150 | prom_printf("MDESC: alloc of %lu bytes failed.\n", size); | 187 | static DEFINE_SPINLOCK(mdesc_lock); |
| 151 | prom_halt(); | ||
| 152 | } | ||
| 153 | 188 | ||
| 154 | memset(ret, 0, size); | 189 | struct mdesc_handle *mdesc_grab(void) |
| 190 | { | ||
| 191 | struct mdesc_handle *hp; | ||
| 192 | unsigned long flags; | ||
| 155 | 193 | ||
| 156 | mdesc_early_allocated += size; | 194 | spin_lock_irqsave(&mdesc_lock, flags); |
| 195 | hp = cur_mdesc; | ||
| 196 | if (hp) | ||
| 197 | atomic_inc(&hp->refcnt); | ||
| 198 | spin_unlock_irqrestore(&mdesc_lock, flags); | ||
| 157 | 199 | ||
| 158 | return ret; | 200 | return hp; |
| 159 | } | 201 | } |
| 202 | EXPORT_SYMBOL(mdesc_grab); | ||
| 160 | 203 | ||
| 161 | static unsigned int __init count_arcs(struct mdesc_elem *ep) | 204 | void mdesc_release(struct mdesc_handle *hp) |
| 162 | { | 205 | { |
| 163 | unsigned int ret = 0; | 206 | unsigned long flags; |
| 164 | 207 | ||
| 165 | ep++; | 208 | spin_lock_irqsave(&mdesc_lock, flags); |
| 166 | while (ep->tag != MD_NODE_END) { | 209 | if (atomic_dec_and_test(&hp->refcnt)) { |
| 167 | if (ep->tag == MD_PROP_ARC) | 210 | list_del_init(&hp->list); |
| 168 | ret++; | 211 | hp->mops->free(hp); |
| 169 | ep++; | ||
| 170 | } | 212 | } |
| 171 | return ret; | 213 | spin_unlock_irqrestore(&mdesc_lock, flags); |
| 172 | } | 214 | } |
| 215 | EXPORT_SYMBOL(mdesc_release); | ||
| 173 | 216 | ||
| 174 | static void __init mdesc_node_alloc(u64 node, struct mdesc_elem *ep, const char *names) | 217 | static void do_mdesc_update(struct work_struct *work) |
| 175 | { | 218 | { |
| 176 | unsigned int num_arcs = count_arcs(ep); | 219 | unsigned long len, real_len, status; |
| 177 | struct mdesc_node *mp; | 220 | struct mdesc_handle *hp, *orig_hp; |
| 221 | unsigned long flags; | ||
| 222 | |||
| 223 | (void) sun4v_mach_desc(0UL, 0UL, &len); | ||
| 224 | |||
| 225 | hp = mdesc_alloc(len, &kmalloc_mdesc_memops); | ||
| 226 | if (!hp) { | ||
| 227 | printk(KERN_ERR "MD: mdesc alloc fails\n"); | ||
| 228 | return; | ||
| 229 | } | ||
| 230 | |||
| 231 | status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len); | ||
| 232 | if (status != HV_EOK || real_len > len) { | ||
| 233 | printk(KERN_ERR "MD: mdesc reread fails with %lu\n", | ||
| 234 | status); | ||
| 235 | atomic_dec(&hp->refcnt); | ||
| 236 | mdesc_free(hp); | ||
| 237 | return; | ||
| 238 | } | ||
| 178 | 239 | ||
| 179 | mp = mdesc_early_alloc(sizeof(*mp) + | 240 | spin_lock_irqsave(&mdesc_lock, flags); |
| 180 | (num_arcs * sizeof(struct mdesc_arc))); | 241 | orig_hp = cur_mdesc; |
| 181 | mp->name = names + ep->name_offset; | 242 | cur_mdesc = hp; |
| 182 | mp->node = node; | ||
| 183 | mp->unique_id = unique_id++; | ||
| 184 | mp->num_arcs = num_arcs; | ||
| 185 | 243 | ||
| 186 | hash_node(mp); | 244 | if (atomic_dec_and_test(&orig_hp->refcnt)) |
| 245 | mdesc_free(orig_hp); | ||
| 246 | else | ||
| 247 | list_add(&orig_hp->list, &mdesc_zombie_list); | ||
| 248 | spin_unlock_irqrestore(&mdesc_lock, flags); | ||
| 187 | } | 249 | } |
| 188 | 250 | ||
| 189 | static inline struct mdesc_elem *node_block(struct mdesc_hdr *mdesc) | 251 | static DECLARE_WORK(mdesc_update_work, do_mdesc_update); |
| 252 | |||
| 253 | void mdesc_update(void) | ||
| 254 | { | ||
| 255 | schedule_work(&mdesc_update_work); | ||
| 256 | } | ||
| 257 | |||
| 258 | static struct mdesc_elem *node_block(struct mdesc_hdr *mdesc) | ||
| 190 | { | 259 | { |
| 191 | return (struct mdesc_elem *) (mdesc + 1); | 260 | return (struct mdesc_elem *) (mdesc + 1); |
| 192 | } | 261 | } |
| 193 | 262 | ||
| 194 | static inline void *name_block(struct mdesc_hdr *mdesc) | 263 | static void *name_block(struct mdesc_hdr *mdesc) |
| 195 | { | 264 | { |
| 196 | return ((void *) node_block(mdesc)) + mdesc->node_sz; | 265 | return ((void *) node_block(mdesc)) + mdesc->node_sz; |
| 197 | } | 266 | } |
| 198 | 267 | ||
| 199 | static inline void *data_block(struct mdesc_hdr *mdesc) | 268 | static void *data_block(struct mdesc_hdr *mdesc) |
| 200 | { | 269 | { |
| 201 | return ((void *) name_block(mdesc)) + mdesc->name_sz; | 270 | return ((void *) name_block(mdesc)) + mdesc->name_sz; |
| 202 | } | 271 | } |
| 203 | 272 | ||
| 204 | /* In order to avoid recursion (the graph can be very deep) we use a | 273 | u64 mdesc_node_by_name(struct mdesc_handle *hp, |
| 205 | * two pass algorithm. First we allocate all the nodes and hash them. | 274 | u64 from_node, const char *name) |
| 206 | * Then we iterate over each node, filling in the arcs and properties. | ||
| 207 | */ | ||
| 208 | static void __init build_all_nodes(struct mdesc_hdr *mdesc) | ||
| 209 | { | 275 | { |
| 210 | struct mdesc_elem *start, *ep; | 276 | struct mdesc_elem *ep = node_block(&hp->mdesc); |
| 211 | struct mdesc_node *mp; | 277 | const char *names = name_block(&hp->mdesc); |
| 212 | const char *names; | 278 | u64 last_node = hp->mdesc.node_sz / 16; |
| 213 | void *data; | 279 | u64 ret; |
| 214 | u64 last_node; | 280 | |
| 215 | 281 | if (from_node == MDESC_NODE_NULL) | |
| 216 | start = ep = node_block(mdesc); | 282 | from_node = 0; |
| 217 | last_node = mdesc->node_sz / 16; | 283 | |
| 284 | if (from_node >= last_node) | ||
| 285 | return MDESC_NODE_NULL; | ||
| 286 | |||
| 287 | ret = ep[from_node].d.val; | ||
| 288 | while (ret < last_node) { | ||
| 289 | if (ep[ret].tag != MD_NODE) | ||
| 290 | return MDESC_NODE_NULL; | ||
| 291 | if (!strcmp(names + ep[ret].name_offset, name)) | ||
| 292 | break; | ||
| 293 | ret = ep[ret].d.val; | ||
| 294 | } | ||
| 295 | if (ret >= last_node) | ||
| 296 | ret = MDESC_NODE_NULL; | ||
| 297 | return ret; | ||
| 298 | } | ||
| 299 | EXPORT_SYMBOL(mdesc_node_by_name); | ||
| 218 | 300 | ||
| 219 | names = name_block(mdesc); | 301 | const void *mdesc_get_property(struct mdesc_handle *hp, u64 node, |
| 302 | const char *name, int *lenp) | ||
| 303 | { | ||
| 304 | const char *names = name_block(&hp->mdesc); | ||
| 305 | u64 last_node = hp->mdesc.node_sz / 16; | ||
| 306 | void *data = data_block(&hp->mdesc); | ||
| 307 | struct mdesc_elem *ep; | ||
| 220 | 308 | ||
| 221 | while (1) { | 309 | if (node == MDESC_NODE_NULL || node >= last_node) |
| 222 | u64 node = ep - start; | 310 | return NULL; |
| 223 | 311 | ||
| 224 | if (ep->tag == MD_LIST_END) | 312 | ep = node_block(&hp->mdesc) + node; |
| 313 | ep++; | ||
| 314 | for (; ep->tag != MD_NODE_END; ep++) { | ||
| 315 | void *val = NULL; | ||
| 316 | int len = 0; | ||
| 317 | |||
| 318 | switch (ep->tag) { | ||
| 319 | case MD_PROP_VAL: | ||
| 320 | val = &ep->d.val; | ||
| 321 | len = 8; | ||
| 225 | break; | 322 | break; |
| 226 | 323 | ||
| 227 | if (ep->tag != MD_NODE) { | 324 | case MD_PROP_STR: |
| 228 | prom_printf("MDESC: Inconsistent element list.\n"); | 325 | case MD_PROP_DATA: |
| 229 | prom_halt(); | 326 | val = data + ep->d.data.data_offset; |
| 230 | } | 327 | len = ep->d.data.data_len; |
| 231 | 328 | break; | |
| 232 | mdesc_node_alloc(node, ep, names); | ||
| 233 | 329 | ||
| 234 | if (ep->d.val >= last_node) { | 330 | default: |
| 235 | printk("MDESC: Warning, early break out of node scan.\n"); | ||
| 236 | printk("MDESC: Next node [%lu] last_node [%lu].\n", | ||
| 237 | node, last_node); | ||
| 238 | break; | 331 | break; |
| 239 | } | 332 | } |
| 333 | if (!val) | ||
| 334 | continue; | ||
| 240 | 335 | ||
| 241 | ep = start + ep->d.val; | 336 | if (!strcmp(names + ep->name_offset, name)) { |
| 337 | if (lenp) | ||
| 338 | *lenp = len; | ||
| 339 | return val; | ||
| 340 | } | ||
| 242 | } | 341 | } |
| 243 | 342 | ||
| 244 | data = data_block(mdesc); | 343 | return NULL; |
| 245 | for (mp = allnodes; mp; mp = mp->allnodes_next) { | 344 | } |
| 246 | struct mdesc_elem *ep = start + mp->node; | 345 | EXPORT_SYMBOL(mdesc_get_property); |
| 247 | struct property **link = &mp->properties; | ||
| 248 | unsigned int this_arc = 0; | ||
| 249 | |||
| 250 | ep++; | ||
| 251 | while (ep->tag != MD_NODE_END) { | ||
| 252 | switch (ep->tag) { | ||
| 253 | case MD_PROP_ARC: { | ||
| 254 | struct mdesc_node *target; | ||
| 255 | |||
| 256 | if (this_arc >= mp->num_arcs) { | ||
| 257 | prom_printf("MDESC: ARC overrun [%u:%u]\n", | ||
| 258 | this_arc, mp->num_arcs); | ||
| 259 | prom_halt(); | ||
| 260 | } | ||
| 261 | target = find_node(ep->d.val); | ||
| 262 | if (!target) { | ||
| 263 | printk("MDESC: Warning, arc points to " | ||
| 264 | "missing node, ignoring.\n"); | ||
| 265 | break; | ||
| 266 | } | ||
| 267 | mp->arcs[this_arc].name = | ||
| 268 | (names + ep->name_offset); | ||
| 269 | mp->arcs[this_arc].arc = target; | ||
| 270 | this_arc++; | ||
| 271 | break; | ||
| 272 | } | ||
| 273 | 346 | ||
| 274 | case MD_PROP_VAL: | 347 | u64 mdesc_next_arc(struct mdesc_handle *hp, u64 from, const char *arc_type) |
| 275 | case MD_PROP_STR: | 348 | { |
| 276 | case MD_PROP_DATA: { | 349 | struct mdesc_elem *ep, *base = node_block(&hp->mdesc); |
| 277 | struct property *p = mdesc_early_alloc(sizeof(*p)); | 350 | const char *names = name_block(&hp->mdesc); |
| 278 | 351 | u64 last_node = hp->mdesc.node_sz / 16; | |
| 279 | p->unique_id = unique_id++; | ||
| 280 | p->name = (char *) names + ep->name_offset; | ||
| 281 | if (ep->tag == MD_PROP_VAL) { | ||
| 282 | p->value = &ep->d.val; | ||
| 283 | p->length = 8; | ||
| 284 | } else { | ||
| 285 | p->value = data + ep->d.data.data_offset; | ||
| 286 | p->length = ep->d.data.data_len; | ||
| 287 | } | ||
| 288 | *link = p; | ||
| 289 | link = &p->next; | ||
| 290 | break; | ||
| 291 | } | ||
| 292 | 352 | ||
| 293 | case MD_NOOP: | 353 | if (from == MDESC_NODE_NULL || from >= last_node) |
| 294 | break; | 354 | return MDESC_NODE_NULL; |
| 295 | 355 | ||
| 296 | default: | 356 | ep = base + from; |
| 297 | printk("MDESC: Warning, ignoring unknown tag type %02x\n", | 357 | |
| 298 | ep->tag); | 358 | ep++; |
| 299 | } | 359 | for (; ep->tag != MD_NODE_END; ep++) { |
| 300 | ep++; | 360 | if (ep->tag != MD_PROP_ARC) |
| 301 | } | 361 | continue; |
| 362 | |||
| 363 | if (strcmp(names + ep->name_offset, arc_type)) | ||
| 364 | continue; | ||
| 365 | |||
| 366 | return ep - base; | ||
| 302 | } | 367 | } |
| 368 | |||
| 369 | return MDESC_NODE_NULL; | ||
| 303 | } | 370 | } |
| 371 | EXPORT_SYMBOL(mdesc_next_arc); | ||
| 304 | 372 | ||
| 305 | static unsigned int __init count_nodes(struct mdesc_hdr *mdesc) | 373 | u64 mdesc_arc_target(struct mdesc_handle *hp, u64 arc) |
| 306 | { | 374 | { |
| 307 | struct mdesc_elem *ep = node_block(mdesc); | 375 | struct mdesc_elem *ep, *base = node_block(&hp->mdesc); |
| 308 | struct mdesc_elem *end; | 376 | |
| 309 | unsigned int cnt = 0; | 377 | ep = base + arc; |
| 310 | 378 | ||
| 311 | end = ((void *)ep) + mdesc->node_sz; | 379 | return ep->d.val; |
| 312 | while (ep < end) { | 380 | } |
| 313 | if (ep->tag == MD_NODE) | 381 | EXPORT_SYMBOL(mdesc_arc_target); |
| 314 | cnt++; | 382 | |
| 315 | ep++; | 383 | const char *mdesc_node_name(struct mdesc_handle *hp, u64 node) |
| 316 | } | 384 | { |
| 317 | return cnt; | 385 | struct mdesc_elem *ep, *base = node_block(&hp->mdesc); |
| 386 | const char *names = name_block(&hp->mdesc); | ||
| 387 | u64 last_node = hp->mdesc.node_sz / 16; | ||
| 388 | |||
| 389 | if (node == MDESC_NODE_NULL || node >= last_node) | ||
| 390 | return NULL; | ||
| 391 | |||
| 392 | ep = base + node; | ||
| 393 | if (ep->tag != MD_NODE) | ||
| 394 | return NULL; | ||
| 395 | |||
| 396 | return names + ep->name_offset; | ||
| 318 | } | 397 | } |
| 398 | EXPORT_SYMBOL(mdesc_node_name); | ||
| 319 | 399 | ||
| 320 | static void __init report_platform_properties(void) | 400 | static void __init report_platform_properties(void) |
| 321 | { | 401 | { |
| 322 | struct mdesc_node *pn = md_find_node_by_name(NULL, "platform"); | 402 | struct mdesc_handle *hp = mdesc_grab(); |
| 403 | u64 pn = mdesc_node_by_name(hp, MDESC_NODE_NULL, "platform"); | ||
| 323 | const char *s; | 404 | const char *s; |
| 324 | const u64 *v; | 405 | const u64 *v; |
| 325 | 406 | ||
| 326 | if (!pn) { | 407 | if (pn == MDESC_NODE_NULL) { |
| 327 | prom_printf("No platform node in machine-description.\n"); | 408 | prom_printf("No platform node in machine-description.\n"); |
| 328 | prom_halt(); | 409 | prom_halt(); |
| 329 | } | 410 | } |
| 330 | 411 | ||
| 331 | s = md_get_property(pn, "banner-name", NULL); | 412 | s = mdesc_get_property(hp, pn, "banner-name", NULL); |
| 332 | printk("PLATFORM: banner-name [%s]\n", s); | 413 | printk("PLATFORM: banner-name [%s]\n", s); |
| 333 | s = md_get_property(pn, "name", NULL); | 414 | s = mdesc_get_property(hp, pn, "name", NULL); |
| 334 | printk("PLATFORM: name [%s]\n", s); | 415 | printk("PLATFORM: name [%s]\n", s); |
| 335 | 416 | ||
| 336 | v = md_get_property(pn, "hostid", NULL); | 417 | v = mdesc_get_property(hp, pn, "hostid", NULL); |
| 337 | if (v) | 418 | if (v) |
| 338 | printk("PLATFORM: hostid [%08lx]\n", *v); | 419 | printk("PLATFORM: hostid [%08lx]\n", *v); |
| 339 | v = md_get_property(pn, "serial#", NULL); | 420 | v = mdesc_get_property(hp, pn, "serial#", NULL); |
| 340 | if (v) | 421 | if (v) |
| 341 | printk("PLATFORM: serial# [%08lx]\n", *v); | 422 | printk("PLATFORM: serial# [%08lx]\n", *v); |
| 342 | v = md_get_property(pn, "stick-frequency", NULL); | 423 | v = mdesc_get_property(hp, pn, "stick-frequency", NULL); |
| 343 | printk("PLATFORM: stick-frequency [%08lx]\n", *v); | 424 | printk("PLATFORM: stick-frequency [%08lx]\n", *v); |
| 344 | v = md_get_property(pn, "mac-address", NULL); | 425 | v = mdesc_get_property(hp, pn, "mac-address", NULL); |
| 345 | if (v) | 426 | if (v) |
| 346 | printk("PLATFORM: mac-address [%lx]\n", *v); | 427 | printk("PLATFORM: mac-address [%lx]\n", *v); |
| 347 | v = md_get_property(pn, "watchdog-resolution", NULL); | 428 | v = mdesc_get_property(hp, pn, "watchdog-resolution", NULL); |
| 348 | if (v) | 429 | if (v) |
| 349 | printk("PLATFORM: watchdog-resolution [%lu ms]\n", *v); | 430 | printk("PLATFORM: watchdog-resolution [%lu ms]\n", *v); |
| 350 | v = md_get_property(pn, "watchdog-max-timeout", NULL); | 431 | v = mdesc_get_property(hp, pn, "watchdog-max-timeout", NULL); |
| 351 | if (v) | 432 | if (v) |
| 352 | printk("PLATFORM: watchdog-max-timeout [%lu ms]\n", *v); | 433 | printk("PLATFORM: watchdog-max-timeout [%lu ms]\n", *v); |
| 353 | v = md_get_property(pn, "max-cpus", NULL); | 434 | v = mdesc_get_property(hp, pn, "max-cpus", NULL); |
| 354 | if (v) | 435 | if (v) |
| 355 | printk("PLATFORM: max-cpus [%lu]\n", *v); | 436 | printk("PLATFORM: max-cpus [%lu]\n", *v); |
| 437 | |||
| 438 | #ifdef CONFIG_SMP | ||
| 439 | { | ||
| 440 | int max_cpu, i; | ||
| 441 | |||
| 442 | if (v) { | ||
| 443 | max_cpu = *v; | ||
| 444 | if (max_cpu > NR_CPUS) | ||
| 445 | max_cpu = NR_CPUS; | ||
| 446 | } else { | ||
| 447 | max_cpu = NR_CPUS; | ||
| 448 | } | ||
| 449 | for (i = 0; i < max_cpu; i++) | ||
| 450 | cpu_set(i, cpu_possible_map); | ||
| 451 | } | ||
| 452 | #endif | ||
| 453 | |||
| 454 | mdesc_release(hp); | ||
| 356 | } | 455 | } |
| 357 | 456 | ||
| 358 | static int inline find_in_proplist(const char *list, const char *match, int len) | 457 | static int inline find_in_proplist(const char *list, const char *match, int len) |
| @@ -369,15 +468,17 @@ static int inline find_in_proplist(const char *list, const char *match, int len) | |||
| 369 | return 0; | 468 | return 0; |
| 370 | } | 469 | } |
| 371 | 470 | ||
| 372 | static void __init fill_in_one_cache(cpuinfo_sparc *c, struct mdesc_node *mp) | 471 | static void __devinit fill_in_one_cache(cpuinfo_sparc *c, |
| 472 | struct mdesc_handle *hp, | ||
| 473 | u64 mp) | ||
| 373 | { | 474 | { |
| 374 | const u64 *level = md_get_property(mp, "level", NULL); | 475 | const u64 *level = mdesc_get_property(hp, mp, "level", NULL); |
| 375 | const u64 *size = md_get_property(mp, "size", NULL); | 476 | const u64 *size = mdesc_get_property(hp, mp, "size", NULL); |
| 376 | const u64 *line_size = md_get_property(mp, "line-size", NULL); | 477 | const u64 *line_size = mdesc_get_property(hp, mp, "line-size", NULL); |
| 377 | const char *type; | 478 | const char *type; |
| 378 | int type_len; | 479 | int type_len; |
| 379 | 480 | ||
| 380 | type = md_get_property(mp, "type", &type_len); | 481 | type = mdesc_get_property(hp, mp, "type", &type_len); |
| 381 | 482 | ||
| 382 | switch (*level) { | 483 | switch (*level) { |
| 383 | case 1: | 484 | case 1: |
| @@ -400,48 +501,45 @@ static void __init fill_in_one_cache(cpuinfo_sparc *c, struct mdesc_node *mp) | |||
| 400 | } | 501 | } |
| 401 | 502 | ||
| 402 | if (*level == 1) { | 503 | if (*level == 1) { |
| 403 | unsigned int i; | 504 | u64 a; |
| 404 | |||
| 405 | for (i = 0; i < mp->num_arcs; i++) { | ||
| 406 | struct mdesc_node *t = mp->arcs[i].arc; | ||
| 407 | 505 | ||
| 408 | if (strcmp(mp->arcs[i].name, "fwd")) | 506 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) { |
| 409 | continue; | 507 | u64 target = mdesc_arc_target(hp, a); |
| 508 | const char *name = mdesc_node_name(hp, target); | ||
| 410 | 509 | ||
| 411 | if (!strcmp(t->name, "cache")) | 510 | if (!strcmp(name, "cache")) |
| 412 | fill_in_one_cache(c, t); | 511 | fill_in_one_cache(c, hp, target); |
| 413 | } | 512 | } |
| 414 | } | 513 | } |
| 415 | } | 514 | } |
| 416 | 515 | ||
| 417 | static void __init mark_core_ids(struct mdesc_node *mp, int core_id) | 516 | static void __devinit mark_core_ids(struct mdesc_handle *hp, u64 mp, |
| 517 | int core_id) | ||
| 418 | { | 518 | { |
| 419 | unsigned int i; | 519 | u64 a; |
| 420 | 520 | ||
| 421 | for (i = 0; i < mp->num_arcs; i++) { | 521 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) { |
| 422 | struct mdesc_node *t = mp->arcs[i].arc; | 522 | u64 t = mdesc_arc_target(hp, a); |
| 523 | const char *name; | ||
| 423 | const u64 *id; | 524 | const u64 *id; |
| 424 | 525 | ||
| 425 | if (strcmp(mp->arcs[i].name, "back")) | 526 | name = mdesc_node_name(hp, t); |
| 426 | continue; | 527 | if (!strcmp(name, "cpu")) { |
| 427 | 528 | id = mdesc_get_property(hp, t, "id", NULL); | |
| 428 | if (!strcmp(t->name, "cpu")) { | ||
| 429 | id = md_get_property(t, "id", NULL); | ||
| 430 | if (*id < NR_CPUS) | 529 | if (*id < NR_CPUS) |
| 431 | cpu_data(*id).core_id = core_id; | 530 | cpu_data(*id).core_id = core_id; |
| 432 | } else { | 531 | } else { |
| 433 | unsigned int j; | 532 | u64 j; |
| 434 | 533 | ||
| 435 | for (j = 0; j < t->num_arcs; j++) { | 534 | mdesc_for_each_arc(j, hp, t, MDESC_ARC_TYPE_BACK) { |
| 436 | struct mdesc_node *n = t->arcs[j].arc; | 535 | u64 n = mdesc_arc_target(hp, j); |
| 536 | const char *n_name; | ||
| 437 | 537 | ||
| 438 | if (strcmp(t->arcs[j].name, "back")) | 538 | n_name = mdesc_node_name(hp, n); |
| 539 | if (strcmp(n_name, "cpu")) | ||
| 439 | continue; | 540 | continue; |
| 440 | 541 | ||
| 441 | if (strcmp(n->name, "cpu")) | 542 | id = mdesc_get_property(hp, n, "id", NULL); |
| 442 | continue; | ||
| 443 | |||
| 444 | id = md_get_property(n, "id", NULL); | ||
| 445 | if (*id < NR_CPUS) | 543 | if (*id < NR_CPUS) |
| 446 | cpu_data(*id).core_id = core_id; | 544 | cpu_data(*id).core_id = core_id; |
| 447 | } | 545 | } |
| @@ -449,78 +547,81 @@ static void __init mark_core_ids(struct mdesc_node *mp, int core_id) | |||
| 449 | } | 547 | } |
| 450 | } | 548 | } |
| 451 | 549 | ||
| 452 | static void __init set_core_ids(void) | 550 | static void __devinit set_core_ids(struct mdesc_handle *hp) |
| 453 | { | 551 | { |
| 454 | struct mdesc_node *mp; | ||
| 455 | int idx; | 552 | int idx; |
| 553 | u64 mp; | ||
| 456 | 554 | ||
| 457 | idx = 1; | 555 | idx = 1; |
| 458 | md_for_each_node_by_name(mp, "cache") { | 556 | mdesc_for_each_node_by_name(hp, mp, "cache") { |
| 459 | const u64 *level = md_get_property(mp, "level", NULL); | 557 | const u64 *level; |
| 460 | const char *type; | 558 | const char *type; |
| 461 | int len; | 559 | int len; |
| 462 | 560 | ||
| 561 | level = mdesc_get_property(hp, mp, "level", NULL); | ||
| 463 | if (*level != 1) | 562 | if (*level != 1) |
| 464 | continue; | 563 | continue; |
| 465 | 564 | ||
| 466 | type = md_get_property(mp, "type", &len); | 565 | type = mdesc_get_property(hp, mp, "type", &len); |
| 467 | if (!find_in_proplist(type, "instn", len)) | 566 | if (!find_in_proplist(type, "instn", len)) |
| 468 | continue; | 567 | continue; |
| 469 | 568 | ||
| 470 | mark_core_ids(mp, idx); | 569 | mark_core_ids(hp, mp, idx); |
| 471 | 570 | ||
| 472 | idx++; | 571 | idx++; |
| 473 | } | 572 | } |
| 474 | } | 573 | } |
| 475 | 574 | ||
| 476 | static void __init mark_proc_ids(struct mdesc_node *mp, int proc_id) | 575 | static void __devinit mark_proc_ids(struct mdesc_handle *hp, u64 mp, |
| 576 | int proc_id) | ||
| 477 | { | 577 | { |
| 478 | int i; | 578 | u64 a; |
| 479 | 579 | ||
| 480 | for (i = 0; i < mp->num_arcs; i++) { | 580 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) { |
| 481 | struct mdesc_node *t = mp->arcs[i].arc; | 581 | u64 t = mdesc_arc_target(hp, a); |
| 582 | const char *name; | ||
| 482 | const u64 *id; | 583 | const u64 *id; |
| 483 | 584 | ||
| 484 | if (strcmp(mp->arcs[i].name, "back")) | 585 | name = mdesc_node_name(hp, t); |
| 485 | continue; | 586 | if (strcmp(name, "cpu")) |
| 486 | |||
| 487 | if (strcmp(t->name, "cpu")) | ||
| 488 | continue; | 587 | continue; |
| 489 | 588 | ||
| 490 | id = md_get_property(t, "id", NULL); | 589 | id = mdesc_get_property(hp, t, "id", NULL); |
| 491 | if (*id < NR_CPUS) | 590 | if (*id < NR_CPUS) |
| 492 | cpu_data(*id).proc_id = proc_id; | 591 | cpu_data(*id).proc_id = proc_id; |
| 493 | } | 592 | } |
| 494 | } | 593 | } |
| 495 | 594 | ||
| 496 | static void __init __set_proc_ids(const char *exec_unit_name) | 595 | static void __devinit __set_proc_ids(struct mdesc_handle *hp, |
| 596 | const char *exec_unit_name) | ||
| 497 | { | 597 | { |
| 498 | struct mdesc_node *mp; | ||
| 499 | int idx; | 598 | int idx; |
| 599 | u64 mp; | ||
| 500 | 600 | ||
| 501 | idx = 0; | 601 | idx = 0; |
| 502 | md_for_each_node_by_name(mp, exec_unit_name) { | 602 | mdesc_for_each_node_by_name(hp, mp, exec_unit_name) { |
| 503 | const char *type; | 603 | const char *type; |
| 504 | int len; | 604 | int len; |
| 505 | 605 | ||
| 506 | type = md_get_property(mp, "type", &len); | 606 | type = mdesc_get_property(hp, mp, "type", &len); |
| 507 | if (!find_in_proplist(type, "int", len) && | 607 | if (!find_in_proplist(type, "int", len) && |
| 508 | !find_in_proplist(type, "integer", len)) | 608 | !find_in_proplist(type, "integer", len)) |
| 509 | continue; | 609 | continue; |
| 510 | 610 | ||
| 511 | mark_proc_ids(mp, idx); | 611 | mark_proc_ids(hp, mp, idx); |
| 512 | 612 | ||
| 513 | idx++; | 613 | idx++; |
| 514 | } | 614 | } |
| 515 | } | 615 | } |
| 516 | 616 | ||
| 517 | static void __init set_proc_ids(void) | 617 | static void __devinit set_proc_ids(struct mdesc_handle *hp) |
| 518 | { | 618 | { |
| 519 | __set_proc_ids("exec_unit"); | 619 | __set_proc_ids(hp, "exec_unit"); |
| 520 | __set_proc_ids("exec-unit"); | 620 | __set_proc_ids(hp, "exec-unit"); |
| 521 | } | 621 | } |
| 522 | 622 | ||
| 523 | static void __init get_one_mondo_bits(const u64 *p, unsigned int *mask, unsigned char def) | 623 | static void __devinit get_one_mondo_bits(const u64 *p, unsigned int *mask, |
| 624 | unsigned char def) | ||
| 524 | { | 625 | { |
| 525 | u64 val; | 626 | u64 val; |
| 526 | 627 | ||
| @@ -538,35 +639,37 @@ use_default: | |||
| 538 | *mask = ((1U << def) * 64U) - 1U; | 639 | *mask = ((1U << def) * 64U) - 1U; |
| 539 | } | 640 | } |
| 540 | 641 | ||
| 541 | static void __init get_mondo_data(struct mdesc_node *mp, struct trap_per_cpu *tb) | 642 | static void __devinit get_mondo_data(struct mdesc_handle *hp, u64 mp, |
| 643 | struct trap_per_cpu *tb) | ||
| 542 | { | 644 | { |
| 543 | const u64 *val; | 645 | const u64 *val; |
| 544 | 646 | ||
| 545 | val = md_get_property(mp, "q-cpu-mondo-#bits", NULL); | 647 | val = mdesc_get_property(hp, mp, "q-cpu-mondo-#bits", NULL); |
| 546 | get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7); | 648 | get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7); |
| 547 | 649 | ||
| 548 | val = md_get_property(mp, "q-dev-mondo-#bits", NULL); | 650 | val = mdesc_get_property(hp, mp, "q-dev-mondo-#bits", NULL); |
| 549 | get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7); | 651 | get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7); |
| 550 | 652 | ||
| 551 | val = md_get_property(mp, "q-resumable-#bits", NULL); | 653 | val = mdesc_get_property(hp, mp, "q-resumable-#bits", NULL); |
| 552 | get_one_mondo_bits(val, &tb->resum_qmask, 6); | 654 | get_one_mondo_bits(val, &tb->resum_qmask, 6); |
| 553 | 655 | ||
| 554 | val = md_get_property(mp, "q-nonresumable-#bits", NULL); | 656 | val = mdesc_get_property(hp, mp, "q-nonresumable-#bits", NULL); |
| 555 | get_one_mondo_bits(val, &tb->nonresum_qmask, 2); | 657 | get_one_mondo_bits(val, &tb->nonresum_qmask, 2); |
| 556 | } | 658 | } |
| 557 | 659 | ||
| 558 | static void __init mdesc_fill_in_cpu_data(void) | 660 | void __devinit mdesc_fill_in_cpu_data(cpumask_t mask) |
| 559 | { | 661 | { |
| 560 | struct mdesc_node *mp; | 662 | struct mdesc_handle *hp = mdesc_grab(); |
| 663 | u64 mp; | ||
| 561 | 664 | ||
| 562 | ncpus_probed = 0; | 665 | ncpus_probed = 0; |
| 563 | md_for_each_node_by_name(mp, "cpu") { | 666 | mdesc_for_each_node_by_name(hp, mp, "cpu") { |
| 564 | const u64 *id = md_get_property(mp, "id", NULL); | 667 | const u64 *id = mdesc_get_property(hp, mp, "id", NULL); |
| 565 | const u64 *cfreq = md_get_property(mp, "clock-frequency", NULL); | 668 | const u64 *cfreq = mdesc_get_property(hp, mp, "clock-frequency", NULL); |
| 566 | struct trap_per_cpu *tb; | 669 | struct trap_per_cpu *tb; |
| 567 | cpuinfo_sparc *c; | 670 | cpuinfo_sparc *c; |
| 568 | unsigned int i; | ||
| 569 | int cpuid; | 671 | int cpuid; |
| 672 | u64 a; | ||
| 570 | 673 | ||
| 571 | ncpus_probed++; | 674 | ncpus_probed++; |
| 572 | 675 | ||
| @@ -575,6 +678,8 @@ static void __init mdesc_fill_in_cpu_data(void) | |||
| 575 | #ifdef CONFIG_SMP | 678 | #ifdef CONFIG_SMP |
| 576 | if (cpuid >= NR_CPUS) | 679 | if (cpuid >= NR_CPUS) |
| 577 | continue; | 680 | continue; |
| 681 | if (!cpu_isset(cpuid, mask)) | ||
| 682 | continue; | ||
| 578 | #else | 683 | #else |
| 579 | /* On uniprocessor we only want the values for the | 684 | /* On uniprocessor we only want the values for the |
| 580 | * real physical cpu the kernel booted onto, however | 685 | * real physical cpu the kernel booted onto, however |
| @@ -589,35 +694,30 @@ static void __init mdesc_fill_in_cpu_data(void) | |||
| 589 | c->clock_tick = *cfreq; | 694 | c->clock_tick = *cfreq; |
| 590 | 695 | ||
| 591 | tb = &trap_block[cpuid]; | 696 | tb = &trap_block[cpuid]; |
| 592 | get_mondo_data(mp, tb); | 697 | get_mondo_data(hp, mp, tb); |
| 593 | |||
| 594 | for (i = 0; i < mp->num_arcs; i++) { | ||
| 595 | struct mdesc_node *t = mp->arcs[i].arc; | ||
| 596 | unsigned int j; | ||
| 597 | 698 | ||
| 598 | if (strcmp(mp->arcs[i].name, "fwd")) | 699 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) { |
| 599 | continue; | 700 | u64 j, t = mdesc_arc_target(hp, a); |
| 701 | const char *t_name; | ||
| 600 | 702 | ||
| 601 | if (!strcmp(t->name, "cache")) { | 703 | t_name = mdesc_node_name(hp, t); |
| 602 | fill_in_one_cache(c, t); | 704 | if (!strcmp(t_name, "cache")) { |
| 705 | fill_in_one_cache(c, hp, t); | ||
| 603 | continue; | 706 | continue; |
| 604 | } | 707 | } |
| 605 | 708 | ||
| 606 | for (j = 0; j < t->num_arcs; j++) { | 709 | mdesc_for_each_arc(j, hp, t, MDESC_ARC_TYPE_FWD) { |
| 607 | struct mdesc_node *n; | 710 | u64 n = mdesc_arc_target(hp, j); |
| 711 | const char *n_name; | ||
| 608 | 712 | ||
| 609 | n = t->arcs[j].arc; | 713 | n_name = mdesc_node_name(hp, n); |
| 610 | if (strcmp(t->arcs[j].name, "fwd")) | 714 | if (!strcmp(n_name, "cache")) |
| 611 | continue; | 715 | fill_in_one_cache(c, hp, n); |
| 612 | |||
| 613 | if (!strcmp(n->name, "cache")) | ||
| 614 | fill_in_one_cache(c, n); | ||
| 615 | } | 716 | } |
| 616 | } | 717 | } |
| 617 | 718 | ||
| 618 | #ifdef CONFIG_SMP | 719 | #ifdef CONFIG_SMP |
| 619 | cpu_set(cpuid, cpu_present_map); | 720 | cpu_set(cpuid, cpu_present_map); |
| 620 | cpu_set(cpuid, phys_cpu_present_map); | ||
| 621 | #endif | 721 | #endif |
| 622 | 722 | ||
| 623 | c->core_id = 0; | 723 | c->core_id = 0; |
| @@ -628,45 +728,43 @@ static void __init mdesc_fill_in_cpu_data(void) | |||
| 628 | sparc64_multi_core = 1; | 728 | sparc64_multi_core = 1; |
| 629 | #endif | 729 | #endif |
| 630 | 730 | ||
| 631 | set_core_ids(); | 731 | set_core_ids(hp); |
| 632 | set_proc_ids(); | 732 | set_proc_ids(hp); |
| 633 | 733 | ||
| 634 | smp_fill_in_sib_core_maps(); | 734 | smp_fill_in_sib_core_maps(); |
| 735 | |||
| 736 | mdesc_release(hp); | ||
| 635 | } | 737 | } |
| 636 | 738 | ||
| 637 | void __init sun4v_mdesc_init(void) | 739 | void __init sun4v_mdesc_init(void) |
| 638 | { | 740 | { |
| 741 | struct mdesc_handle *hp; | ||
| 639 | unsigned long len, real_len, status; | 742 | unsigned long len, real_len, status; |
| 743 | cpumask_t mask; | ||
| 640 | 744 | ||
| 641 | (void) sun4v_mach_desc(0UL, 0UL, &len); | 745 | (void) sun4v_mach_desc(0UL, 0UL, &len); |
| 642 | 746 | ||
| 643 | printk("MDESC: Size is %lu bytes.\n", len); | 747 | printk("MDESC: Size is %lu bytes.\n", len); |
| 644 | 748 | ||
| 645 | main_mdesc = mdesc_early_alloc(len); | 749 | hp = mdesc_alloc(len, &bootmem_mdesc_memops); |
| 750 | if (hp == NULL) { | ||
| 751 | prom_printf("MDESC: alloc of %lu bytes failed.\n", len); | ||
| 752 | prom_halt(); | ||
| 753 | } | ||
| 646 | 754 | ||
| 647 | status = sun4v_mach_desc(__pa(main_mdesc), len, &real_len); | 755 | status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len); |
| 648 | if (status != HV_EOK || real_len > len) { | 756 | if (status != HV_EOK || real_len > len) { |
| 649 | prom_printf("sun4v_mach_desc fails, err(%lu), " | 757 | prom_printf("sun4v_mach_desc fails, err(%lu), " |
| 650 | "len(%lu), real_len(%lu)\n", | 758 | "len(%lu), real_len(%lu)\n", |
| 651 | status, len, real_len); | 759 | status, len, real_len); |
| 760 | mdesc_free(hp); | ||
| 652 | prom_halt(); | 761 | prom_halt(); |
| 653 | } | 762 | } |
| 654 | 763 | ||
| 655 | len = count_nodes(main_mdesc); | 764 | cur_mdesc = hp; |
| 656 | printk("MDESC: %lu nodes.\n", len); | ||
| 657 | |||
| 658 | len = roundup_pow_of_two(len); | ||
| 659 | |||
| 660 | mdesc_hash = mdesc_early_alloc(len * sizeof(struct mdesc_node *)); | ||
| 661 | mdesc_hash_size = len; | ||
| 662 | |||
| 663 | printk("MDESC: Hash size %lu entries.\n", len); | ||
| 664 | |||
| 665 | build_all_nodes(main_mdesc); | ||
| 666 | |||
| 667 | printk("MDESC: Built graph with %u bytes of memory.\n", | ||
| 668 | mdesc_early_allocated); | ||
| 669 | 765 | ||
| 670 | report_platform_properties(); | 766 | report_platform_properties(); |
| 671 | mdesc_fill_in_cpu_data(); | 767 | |
| 768 | cpus_setall(mask); | ||
| 769 | mdesc_fill_in_cpu_data(mask); | ||
| 672 | } | 770 | } |
diff --git a/arch/sparc64/kernel/power.c b/arch/sparc64/kernel/power.c index 5d6adea3967f..8dd4294ad21e 100644 --- a/arch/sparc64/kernel/power.c +++ b/arch/sparc64/kernel/power.c | |||
| @@ -1,7 +1,6 @@ | |||
| 1 | /* $Id: power.c,v 1.10 2001/12/11 01:57:16 davem Exp $ | 1 | /* power.c: Power management driver. |
| 2 | * power.c: Power management driver. | ||
| 3 | * | 2 | * |
| 4 | * Copyright (C) 1999 David S. Miller (davem@redhat.com) | 3 | * Copyright (C) 1999, 2007 David S. Miller (davem@davemloft.net) |
| 5 | */ | 4 | */ |
| 6 | 5 | ||
| 7 | #include <linux/kernel.h> | 6 | #include <linux/kernel.h> |
| @@ -19,6 +18,7 @@ | |||
| 19 | #include <asm/prom.h> | 18 | #include <asm/prom.h> |
| 20 | #include <asm/of_device.h> | 19 | #include <asm/of_device.h> |
| 21 | #include <asm/io.h> | 20 | #include <asm/io.h> |
| 21 | #include <asm/power.h> | ||
| 22 | #include <asm/sstate.h> | 22 | #include <asm/sstate.h> |
| 23 | 23 | ||
| 24 | #include <linux/unistd.h> | 24 | #include <linux/unistd.h> |
| @@ -29,24 +29,26 @@ | |||
| 29 | */ | 29 | */ |
| 30 | int scons_pwroff = 1; | 30 | int scons_pwroff = 1; |
| 31 | 31 | ||
| 32 | #ifdef CONFIG_PCI | ||
| 33 | #include <linux/pci.h> | ||
| 34 | static void __iomem *power_reg; | 32 | static void __iomem *power_reg; |
| 35 | 33 | ||
| 36 | static DECLARE_WAIT_QUEUE_HEAD(powerd_wait); | 34 | static DECLARE_WAIT_QUEUE_HEAD(powerd_wait); |
| 37 | static int button_pressed; | 35 | static int button_pressed; |
| 38 | 36 | ||
| 39 | static irqreturn_t power_handler(int irq, void *dev_id) | 37 | void wake_up_powerd(void) |
| 40 | { | 38 | { |
| 41 | if (button_pressed == 0) { | 39 | if (button_pressed == 0) { |
| 42 | button_pressed = 1; | 40 | button_pressed = 1; |
| 43 | wake_up(&powerd_wait); | 41 | wake_up(&powerd_wait); |
| 44 | } | 42 | } |
| 43 | } | ||
| 44 | |||
| 45 | static irqreturn_t power_handler(int irq, void *dev_id) | ||
| 46 | { | ||
| 47 | wake_up_powerd(); | ||
| 45 | 48 | ||
| 46 | /* FIXME: Check registers for status... */ | 49 | /* FIXME: Check registers for status... */ |
| 47 | return IRQ_HANDLED; | 50 | return IRQ_HANDLED; |
| 48 | } | 51 | } |
| 49 | #endif /* CONFIG_PCI */ | ||
| 50 | 52 | ||
| 51 | extern void machine_halt(void); | 53 | extern void machine_halt(void); |
| 52 | extern void machine_alt_power_off(void); | 54 | extern void machine_alt_power_off(void); |
| @@ -56,19 +58,18 @@ void machine_power_off(void) | |||
| 56 | { | 58 | { |
| 57 | sstate_poweroff(); | 59 | sstate_poweroff(); |
| 58 | if (!serial_console || scons_pwroff) { | 60 | if (!serial_console || scons_pwroff) { |
| 59 | #ifdef CONFIG_PCI | ||
| 60 | if (power_reg) { | 61 | if (power_reg) { |
| 61 | /* Both register bits seem to have the | 62 | /* Both register bits seem to have the |
| 62 | * same effect, so until I figure out | 63 | * same effect, so until I figure out |
| 63 | * what the difference is... | 64 | * what the difference is... |
| 64 | */ | 65 | */ |
| 65 | writel(AUXIO_PCIO_CPWR_OFF | AUXIO_PCIO_SPWR_OFF, power_reg); | 66 | writel(AUXIO_PCIO_CPWR_OFF | AUXIO_PCIO_SPWR_OFF, power_reg); |
| 66 | } else | 67 | } else { |
| 67 | #endif /* CONFIG_PCI */ | ||
| 68 | if (poweroff_method != NULL) { | 68 | if (poweroff_method != NULL) { |
| 69 | poweroff_method(); | 69 | poweroff_method(); |
| 70 | /* not reached */ | 70 | /* not reached */ |
| 71 | } | 71 | } |
| 72 | } | ||
| 72 | } | 73 | } |
| 73 | machine_halt(); | 74 | machine_halt(); |
| 74 | } | 75 | } |
| @@ -76,7 +77,6 @@ void machine_power_off(void) | |||
| 76 | void (*pm_power_off)(void) = machine_power_off; | 77 | void (*pm_power_off)(void) = machine_power_off; |
| 77 | EXPORT_SYMBOL(pm_power_off); | 78 | EXPORT_SYMBOL(pm_power_off); |
| 78 | 79 | ||
| 79 | #ifdef CONFIG_PCI | ||
| 80 | static int powerd(void *__unused) | 80 | static int powerd(void *__unused) |
| 81 | { | 81 | { |
| 82 | static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; | 82 | static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; |
| @@ -86,7 +86,7 @@ static int powerd(void *__unused) | |||
| 86 | daemonize("powerd"); | 86 | daemonize("powerd"); |
| 87 | 87 | ||
| 88 | add_wait_queue(&powerd_wait, &wait); | 88 | add_wait_queue(&powerd_wait, &wait); |
| 89 | again: | 89 | |
| 90 | for (;;) { | 90 | for (;;) { |
| 91 | set_task_state(current, TASK_INTERRUPTIBLE); | 91 | set_task_state(current, TASK_INTERRUPTIBLE); |
| 92 | if (button_pressed) | 92 | if (button_pressed) |
| @@ -100,16 +100,28 @@ again: | |||
| 100 | /* Ok, down we go... */ | 100 | /* Ok, down we go... */ |
| 101 | button_pressed = 0; | 101 | button_pressed = 0; |
| 102 | if (kernel_execve("/sbin/shutdown", argv, envp) < 0) { | 102 | if (kernel_execve("/sbin/shutdown", argv, envp) < 0) { |
| 103 | printk("powerd: shutdown execution failed\n"); | 103 | printk(KERN_ERR "powerd: shutdown execution failed\n"); |
| 104 | add_wait_queue(&powerd_wait, &wait); | 104 | machine_power_off(); |
| 105 | goto again; | ||
| 106 | } | 105 | } |
| 107 | return 0; | 106 | return 0; |
| 108 | } | 107 | } |
| 109 | 108 | ||
| 109 | int start_powerd(void) | ||
| 110 | { | ||
| 111 | int err; | ||
| 112 | |||
| 113 | err = kernel_thread(powerd, NULL, CLONE_FS); | ||
| 114 | if (err < 0) | ||
| 115 | printk(KERN_ERR "power: Failed to start power daemon.\n"); | ||
| 116 | else | ||
| 117 | printk(KERN_INFO "power: powerd running.\n"); | ||
| 118 | |||
| 119 | return err; | ||
| 120 | } | ||
| 121 | |||
| 110 | static int __init has_button_interrupt(unsigned int irq, struct device_node *dp) | 122 | static int __init has_button_interrupt(unsigned int irq, struct device_node *dp) |
| 111 | { | 123 | { |
| 112 | if (irq == PCI_IRQ_NONE) | 124 | if (irq == 0xffffffff) |
| 113 | return 0; | 125 | return 0; |
| 114 | if (!of_find_property(dp, "button", NULL)) | 126 | if (!of_find_property(dp, "button", NULL)) |
| 115 | return 0; | 127 | return 0; |
| @@ -130,17 +142,14 @@ static int __devinit power_probe(struct of_device *op, const struct of_device_id | |||
| 130 | poweroff_method = machine_halt; /* able to use the standard halt */ | 142 | poweroff_method = machine_halt; /* able to use the standard halt */ |
| 131 | 143 | ||
| 132 | if (has_button_interrupt(irq, op->node)) { | 144 | if (has_button_interrupt(irq, op->node)) { |
| 133 | if (kernel_thread(powerd, NULL, CLONE_FS) < 0) { | 145 | if (start_powerd() < 0) |
| 134 | printk("Failed to start power daemon.\n"); | ||
| 135 | return 0; | 146 | return 0; |
| 136 | } | ||
| 137 | printk("powerd running.\n"); | ||
| 138 | 147 | ||
| 139 | if (request_irq(irq, | 148 | if (request_irq(irq, |
| 140 | power_handler, 0, "power", NULL) < 0) | 149 | power_handler, 0, "power", NULL) < 0) |
| 141 | printk("power: Error, cannot register IRQ handler.\n"); | 150 | printk(KERN_ERR "power: Cannot setup IRQ handler.\n"); |
| 142 | } else { | 151 | } else { |
| 143 | printk("not using powerd.\n"); | 152 | printk(KERN_INFO "power: Not using powerd.\n"); |
| 144 | } | 153 | } |
| 145 | 154 | ||
| 146 | return 0; | 155 | return 0; |
| @@ -164,4 +173,3 @@ void __init power_init(void) | |||
| 164 | of_register_driver(&power_driver, &of_bus_type); | 173 | of_register_driver(&power_driver, &of_bus_type); |
| 165 | return; | 174 | return; |
| 166 | } | 175 | } |
| 167 | #endif /* CONFIG_PCI */ | ||
diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index f5f97e2c669c..93557507ec9f 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c | |||
| @@ -29,6 +29,7 @@ | |||
| 29 | #include <linux/compat.h> | 29 | #include <linux/compat.h> |
| 30 | #include <linux/tick.h> | 30 | #include <linux/tick.h> |
| 31 | #include <linux/init.h> | 31 | #include <linux/init.h> |
| 32 | #include <linux/cpu.h> | ||
| 32 | 33 | ||
| 33 | #include <asm/oplib.h> | 34 | #include <asm/oplib.h> |
| 34 | #include <asm/uaccess.h> | 35 | #include <asm/uaccess.h> |
| @@ -49,7 +50,7 @@ | |||
| 49 | 50 | ||
| 50 | /* #define VERBOSE_SHOWREGS */ | 51 | /* #define VERBOSE_SHOWREGS */ |
| 51 | 52 | ||
| 52 | static void sparc64_yield(void) | 53 | static void sparc64_yield(int cpu) |
| 53 | { | 54 | { |
| 54 | if (tlb_type != hypervisor) | 55 | if (tlb_type != hypervisor) |
| 55 | return; | 56 | return; |
| @@ -57,7 +58,7 @@ static void sparc64_yield(void) | |||
| 57 | clear_thread_flag(TIF_POLLING_NRFLAG); | 58 | clear_thread_flag(TIF_POLLING_NRFLAG); |
| 58 | smp_mb__after_clear_bit(); | 59 | smp_mb__after_clear_bit(); |
| 59 | 60 | ||
| 60 | while (!need_resched()) { | 61 | while (!need_resched() && !cpu_is_offline(cpu)) { |
| 61 | unsigned long pstate; | 62 | unsigned long pstate; |
| 62 | 63 | ||
| 63 | /* Disable interrupts. */ | 64 | /* Disable interrupts. */ |
| @@ -68,7 +69,7 @@ static void sparc64_yield(void) | |||
| 68 | : "=&r" (pstate) | 69 | : "=&r" (pstate) |
| 69 | : "i" (PSTATE_IE)); | 70 | : "i" (PSTATE_IE)); |
| 70 | 71 | ||
| 71 | if (!need_resched()) | 72 | if (!need_resched() && !cpu_is_offline(cpu)) |
| 72 | sun4v_cpu_yield(); | 73 | sun4v_cpu_yield(); |
| 73 | 74 | ||
| 74 | /* Re-enable interrupts. */ | 75 | /* Re-enable interrupts. */ |
| @@ -86,15 +87,25 @@ static void sparc64_yield(void) | |||
| 86 | /* The idle loop on sparc64. */ | 87 | /* The idle loop on sparc64. */ |
| 87 | void cpu_idle(void) | 88 | void cpu_idle(void) |
| 88 | { | 89 | { |
| 90 | int cpu = smp_processor_id(); | ||
| 91 | |||
| 89 | set_thread_flag(TIF_POLLING_NRFLAG); | 92 | set_thread_flag(TIF_POLLING_NRFLAG); |
| 90 | 93 | ||
| 91 | while(1) { | 94 | while(1) { |
| 92 | tick_nohz_stop_sched_tick(); | 95 | tick_nohz_stop_sched_tick(); |
| 93 | while (!need_resched()) | 96 | |
| 94 | sparc64_yield(); | 97 | while (!need_resched() && !cpu_is_offline(cpu)) |
| 98 | sparc64_yield(cpu); | ||
| 99 | |||
| 95 | tick_nohz_restart_sched_tick(); | 100 | tick_nohz_restart_sched_tick(); |
| 96 | 101 | ||
| 97 | preempt_enable_no_resched(); | 102 | preempt_enable_no_resched(); |
| 103 | |||
| 104 | #ifdef CONFIG_HOTPLUG_CPU | ||
| 105 | if (cpu_is_offline(cpu)) | ||
| 106 | cpu_play_dead(); | ||
| 107 | #endif | ||
| 108 | |||
| 98 | schedule(); | 109 | schedule(); |
| 99 | preempt_disable(); | 110 | preempt_disable(); |
| 100 | } | 111 | } |
diff --git a/arch/sparc64/kernel/prom.c b/arch/sparc64/kernel/prom.c index 61036b346664..5d220302cd50 100644 --- a/arch/sparc64/kernel/prom.c +++ b/arch/sparc64/kernel/prom.c | |||
| @@ -1808,7 +1808,7 @@ static void __init of_fill_in_cpu_data(void) | |||
| 1808 | 1808 | ||
| 1809 | #ifdef CONFIG_SMP | 1809 | #ifdef CONFIG_SMP |
| 1810 | cpu_set(cpuid, cpu_present_map); | 1810 | cpu_set(cpuid, cpu_present_map); |
| 1811 | cpu_set(cpuid, phys_cpu_present_map); | 1811 | cpu_set(cpuid, cpu_possible_map); |
| 1812 | #endif | 1812 | #endif |
| 1813 | } | 1813 | } |
| 1814 | 1814 | ||
diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c index 7490cc670a53..dc928e49e341 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c | |||
| @@ -442,7 +442,6 @@ static int show_cpuinfo(struct seq_file *m, void *__unused) | |||
| 442 | "D$ parity tl1\t: %u\n" | 442 | "D$ parity tl1\t: %u\n" |
| 443 | "I$ parity tl1\t: %u\n" | 443 | "I$ parity tl1\t: %u\n" |
| 444 | #ifndef CONFIG_SMP | 444 | #ifndef CONFIG_SMP |
| 445 | "Cpu0Bogo\t: %lu.%02lu\n" | ||
| 446 | "Cpu0ClkTck\t: %016lx\n" | 445 | "Cpu0ClkTck\t: %016lx\n" |
| 447 | #endif | 446 | #endif |
| 448 | , | 447 | , |
| @@ -455,10 +454,8 @@ static int show_cpuinfo(struct seq_file *m, void *__unused) | |||
| 455 | ncpus_probed, | 454 | ncpus_probed, |
| 456 | num_online_cpus(), | 455 | num_online_cpus(), |
| 457 | dcache_parity_tl1_occurred, | 456 | dcache_parity_tl1_occurred, |
| 458 | icache_parity_tl1_occurred | 457 | icache_parity_tl1_occurred, |
| 459 | #ifndef CONFIG_SMP | 458 | #ifndef CONFIG_SMP |
| 460 | , cpu_data(0).udelay_val/(500000/HZ), | ||
| 461 | (cpu_data(0).udelay_val/(5000/HZ)) % 100, | ||
| 462 | cpu_data(0).clock_tick | 459 | cpu_data(0).clock_tick |
| 463 | #endif | 460 | #endif |
| 464 | ); | 461 | ); |
diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index 40e40f968d61..b448d33321c6 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | /* smp.c: Sparc64 SMP support. | 1 | /* smp.c: Sparc64 SMP support. |
| 2 | * | 2 | * |
| 3 | * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) | 3 | * Copyright (C) 1997, 2007 David S. Miller (davem@davemloft.net) |
| 4 | */ | 4 | */ |
| 5 | 5 | ||
| 6 | #include <linux/module.h> | 6 | #include <linux/module.h> |
| @@ -28,6 +28,8 @@ | |||
| 28 | #include <asm/tlbflush.h> | 28 | #include <asm/tlbflush.h> |
| 29 | #include <asm/mmu_context.h> | 29 | #include <asm/mmu_context.h> |
| 30 | #include <asm/cpudata.h> | 30 | #include <asm/cpudata.h> |
| 31 | #include <asm/hvtramp.h> | ||
| 32 | #include <asm/io.h> | ||
| 31 | 33 | ||
| 32 | #include <asm/irq.h> | 34 | #include <asm/irq.h> |
| 33 | #include <asm/irq_regs.h> | 35 | #include <asm/irq_regs.h> |
| @@ -41,22 +43,26 @@ | |||
| 41 | #include <asm/sections.h> | 43 | #include <asm/sections.h> |
| 42 | #include <asm/prom.h> | 44 | #include <asm/prom.h> |
| 43 | #include <asm/mdesc.h> | 45 | #include <asm/mdesc.h> |
| 46 | #include <asm/ldc.h> | ||
| 47 | #include <asm/hypervisor.h> | ||
| 44 | 48 | ||
| 45 | extern void calibrate_delay(void); | 49 | extern void calibrate_delay(void); |
| 46 | 50 | ||
| 47 | int sparc64_multi_core __read_mostly; | 51 | int sparc64_multi_core __read_mostly; |
| 48 | 52 | ||
| 49 | /* Please don't make this stuff initdata!!! --DaveM */ | 53 | cpumask_t cpu_possible_map __read_mostly = CPU_MASK_NONE; |
| 50 | unsigned char boot_cpu_id; | ||
| 51 | |||
| 52 | cpumask_t cpu_online_map __read_mostly = CPU_MASK_NONE; | 54 | cpumask_t cpu_online_map __read_mostly = CPU_MASK_NONE; |
| 53 | cpumask_t phys_cpu_present_map __read_mostly = CPU_MASK_NONE; | ||
| 54 | cpumask_t cpu_sibling_map[NR_CPUS] __read_mostly = | 55 | cpumask_t cpu_sibling_map[NR_CPUS] __read_mostly = |
| 55 | { [0 ... NR_CPUS-1] = CPU_MASK_NONE }; | 56 | { [0 ... NR_CPUS-1] = CPU_MASK_NONE }; |
| 56 | cpumask_t cpu_core_map[NR_CPUS] __read_mostly = | 57 | cpumask_t cpu_core_map[NR_CPUS] __read_mostly = |
| 57 | { [0 ... NR_CPUS-1] = CPU_MASK_NONE }; | 58 | { [0 ... NR_CPUS-1] = CPU_MASK_NONE }; |
| 59 | |||
| 60 | EXPORT_SYMBOL(cpu_possible_map); | ||
| 61 | EXPORT_SYMBOL(cpu_online_map); | ||
| 62 | EXPORT_SYMBOL(cpu_sibling_map); | ||
| 63 | EXPORT_SYMBOL(cpu_core_map); | ||
| 64 | |||
| 58 | static cpumask_t smp_commenced_mask; | 65 | static cpumask_t smp_commenced_mask; |
| 59 | static cpumask_t cpu_callout_map; | ||
| 60 | 66 | ||
| 61 | void smp_info(struct seq_file *m) | 67 | void smp_info(struct seq_file *m) |
| 62 | { | 68 | { |
| @@ -73,18 +79,17 @@ void smp_bogo(struct seq_file *m) | |||
| 73 | 79 | ||
| 74 | for_each_online_cpu(i) | 80 | for_each_online_cpu(i) |
| 75 | seq_printf(m, | 81 | seq_printf(m, |
| 76 | "Cpu%dBogo\t: %lu.%02lu\n" | ||
| 77 | "Cpu%dClkTck\t: %016lx\n", | 82 | "Cpu%dClkTck\t: %016lx\n", |
| 78 | i, cpu_data(i).udelay_val / (500000/HZ), | ||
| 79 | (cpu_data(i).udelay_val / (5000/HZ)) % 100, | ||
| 80 | i, cpu_data(i).clock_tick); | 83 | i, cpu_data(i).clock_tick); |
| 81 | } | 84 | } |
| 82 | 85 | ||
| 86 | static __cacheline_aligned_in_smp DEFINE_SPINLOCK(call_lock); | ||
| 87 | |||
| 83 | extern void setup_sparc64_timer(void); | 88 | extern void setup_sparc64_timer(void); |
| 84 | 89 | ||
| 85 | static volatile unsigned long callin_flag = 0; | 90 | static volatile unsigned long callin_flag = 0; |
| 86 | 91 | ||
| 87 | void __init smp_callin(void) | 92 | void __devinit smp_callin(void) |
| 88 | { | 93 | { |
| 89 | int cpuid = hard_smp_processor_id(); | 94 | int cpuid = hard_smp_processor_id(); |
| 90 | 95 | ||
| @@ -102,8 +107,6 @@ void __init smp_callin(void) | |||
| 102 | 107 | ||
| 103 | local_irq_enable(); | 108 | local_irq_enable(); |
| 104 | 109 | ||
| 105 | calibrate_delay(); | ||
| 106 | cpu_data(cpuid).udelay_val = loops_per_jiffy; | ||
| 107 | callin_flag = 1; | 110 | callin_flag = 1; |
| 108 | __asm__ __volatile__("membar #Sync\n\t" | 111 | __asm__ __volatile__("membar #Sync\n\t" |
| 109 | "flush %%g6" : : : "memory"); | 112 | "flush %%g6" : : : "memory"); |
| @@ -120,7 +123,9 @@ void __init smp_callin(void) | |||
| 120 | while (!cpu_isset(cpuid, smp_commenced_mask)) | 123 | while (!cpu_isset(cpuid, smp_commenced_mask)) |
| 121 | rmb(); | 124 | rmb(); |
| 122 | 125 | ||
| 126 | spin_lock(&call_lock); | ||
| 123 | cpu_set(cpuid, cpu_online_map); | 127 | cpu_set(cpuid, cpu_online_map); |
| 128 | spin_unlock(&call_lock); | ||
| 124 | 129 | ||
| 125 | /* idle thread is expected to have preempt disabled */ | 130 | /* idle thread is expected to have preempt disabled */ |
| 126 | preempt_disable(); | 131 | preempt_disable(); |
| @@ -268,6 +273,67 @@ static void smp_synchronize_one_tick(int cpu) | |||
| 268 | spin_unlock_irqrestore(&itc_sync_lock, flags); | 273 | spin_unlock_irqrestore(&itc_sync_lock, flags); |
| 269 | } | 274 | } |
| 270 | 275 | ||
| 276 | #if defined(CONFIG_SUN_LDOMS) && defined(CONFIG_HOTPLUG_CPU) | ||
| 277 | /* XXX Put this in some common place. XXX */ | ||
| 278 | static unsigned long kimage_addr_to_ra(void *p) | ||
| 279 | { | ||
| 280 | unsigned long val = (unsigned long) p; | ||
| 281 | |||
| 282 | return kern_base + (val - KERNBASE); | ||
| 283 | } | ||
| 284 | |||
| 285 | static void ldom_startcpu_cpuid(unsigned int cpu, unsigned long thread_reg) | ||
| 286 | { | ||
| 287 | extern unsigned long sparc64_ttable_tl0; | ||
| 288 | extern unsigned long kern_locked_tte_data; | ||
| 289 | extern int bigkernel; | ||
| 290 | struct hvtramp_descr *hdesc; | ||
| 291 | unsigned long trampoline_ra; | ||
| 292 | struct trap_per_cpu *tb; | ||
| 293 | u64 tte_vaddr, tte_data; | ||
| 294 | unsigned long hv_err; | ||
| 295 | |||
| 296 | hdesc = kzalloc(sizeof(*hdesc), GFP_KERNEL); | ||
| 297 | if (!hdesc) { | ||
| 298 | printk(KERN_ERR "ldom_startcpu_cpuid: Cannot allocate " | ||
| 299 | "hvtramp_descr.\n"); | ||
| 300 | return; | ||
| 301 | } | ||
| 302 | |||
| 303 | hdesc->cpu = cpu; | ||
| 304 | hdesc->num_mappings = (bigkernel ? 2 : 1); | ||
| 305 | |||
| 306 | tb = &trap_block[cpu]; | ||
| 307 | tb->hdesc = hdesc; | ||
| 308 | |||
| 309 | hdesc->fault_info_va = (unsigned long) &tb->fault_info; | ||
| 310 | hdesc->fault_info_pa = kimage_addr_to_ra(&tb->fault_info); | ||
| 311 | |||
| 312 | hdesc->thread_reg = thread_reg; | ||
| 313 | |||
| 314 | tte_vaddr = (unsigned long) KERNBASE; | ||
| 315 | tte_data = kern_locked_tte_data; | ||
| 316 | |||
| 317 | hdesc->maps[0].vaddr = tte_vaddr; | ||
| 318 | hdesc->maps[0].tte = tte_data; | ||
| 319 | if (bigkernel) { | ||
| 320 | tte_vaddr += 0x400000; | ||
| 321 | tte_data += 0x400000; | ||
| 322 | hdesc->maps[1].vaddr = tte_vaddr; | ||
| 323 | hdesc->maps[1].tte = tte_data; | ||
| 324 | } | ||
| 325 | |||
| 326 | trampoline_ra = kimage_addr_to_ra(hv_cpu_startup); | ||
| 327 | |||
| 328 | hv_err = sun4v_cpu_start(cpu, trampoline_ra, | ||
| 329 | kimage_addr_to_ra(&sparc64_ttable_tl0), | ||
| 330 | __pa(hdesc)); | ||
| 331 | if (hv_err) | ||
| 332 | printk(KERN_ERR "ldom_startcpu_cpuid: sun4v_cpu_start() " | ||
| 333 | "gives error %lu\n", hv_err); | ||
| 334 | } | ||
| 335 | #endif | ||
| 336 | |||
| 271 | extern void sun4v_init_mondo_queues(int use_bootmem, int cpu, int alloc, int load); | 337 | extern void sun4v_init_mondo_queues(int use_bootmem, int cpu, int alloc, int load); |
| 272 | 338 | ||
| 273 | extern unsigned long sparc64_cpu_startup; | 339 | extern unsigned long sparc64_cpu_startup; |
| @@ -280,6 +346,7 @@ static struct thread_info *cpu_new_thread = NULL; | |||
| 280 | 346 | ||
| 281 | static int __devinit smp_boot_one_cpu(unsigned int cpu) | 347 | static int __devinit smp_boot_one_cpu(unsigned int cpu) |
| 282 | { | 348 | { |
| 349 | struct trap_per_cpu *tb = &trap_block[cpu]; | ||
| 283 | unsigned long entry = | 350 | unsigned long entry = |
| 284 | (unsigned long)(&sparc64_cpu_startup); | 351 | (unsigned long)(&sparc64_cpu_startup); |
| 285 | unsigned long cookie = | 352 | unsigned long cookie = |
| @@ -290,20 +357,25 @@ static int __devinit smp_boot_one_cpu(unsigned int cpu) | |||
| 290 | p = fork_idle(cpu); | 357 | p = fork_idle(cpu); |
| 291 | callin_flag = 0; | 358 | callin_flag = 0; |
| 292 | cpu_new_thread = task_thread_info(p); | 359 | cpu_new_thread = task_thread_info(p); |
| 293 | cpu_set(cpu, cpu_callout_map); | ||
| 294 | 360 | ||
| 295 | if (tlb_type == hypervisor) { | 361 | if (tlb_type == hypervisor) { |
| 296 | /* Alloc the mondo queues, cpu will load them. */ | 362 | /* Alloc the mondo queues, cpu will load them. */ |
| 297 | sun4v_init_mondo_queues(0, cpu, 1, 0); | 363 | sun4v_init_mondo_queues(0, cpu, 1, 0); |
| 298 | 364 | ||
| 299 | prom_startcpu_cpuid(cpu, entry, cookie); | 365 | #if defined(CONFIG_SUN_LDOMS) && defined(CONFIG_HOTPLUG_CPU) |
| 366 | if (ldom_domaining_enabled) | ||
| 367 | ldom_startcpu_cpuid(cpu, | ||
| 368 | (unsigned long) cpu_new_thread); | ||
| 369 | else | ||
| 370 | #endif | ||
| 371 | prom_startcpu_cpuid(cpu, entry, cookie); | ||
| 300 | } else { | 372 | } else { |
| 301 | struct device_node *dp = of_find_node_by_cpuid(cpu); | 373 | struct device_node *dp = of_find_node_by_cpuid(cpu); |
| 302 | 374 | ||
| 303 | prom_startcpu(dp->node, entry, cookie); | 375 | prom_startcpu(dp->node, entry, cookie); |
| 304 | } | 376 | } |
| 305 | 377 | ||
| 306 | for (timeout = 0; timeout < 5000000; timeout++) { | 378 | for (timeout = 0; timeout < 50000; timeout++) { |
| 307 | if (callin_flag) | 379 | if (callin_flag) |
| 308 | break; | 380 | break; |
| 309 | udelay(100); | 381 | udelay(100); |
| @@ -313,11 +385,15 @@ static int __devinit smp_boot_one_cpu(unsigned int cpu) | |||
| 313 | ret = 0; | 385 | ret = 0; |
| 314 | } else { | 386 | } else { |
| 315 | printk("Processor %d is stuck.\n", cpu); | 387 | printk("Processor %d is stuck.\n", cpu); |
| 316 | cpu_clear(cpu, cpu_callout_map); | ||
| 317 | ret = -ENODEV; | 388 | ret = -ENODEV; |
| 318 | } | 389 | } |
| 319 | cpu_new_thread = NULL; | 390 | cpu_new_thread = NULL; |
| 320 | 391 | ||
| 392 | if (tb->hdesc) { | ||
| 393 | kfree(tb->hdesc); | ||
| 394 | tb->hdesc = NULL; | ||
| 395 | } | ||
| 396 | |||
| 321 | return ret; | 397 | return ret; |
| 322 | } | 398 | } |
| 323 | 399 | ||
| @@ -720,7 +796,6 @@ struct call_data_struct { | |||
| 720 | int wait; | 796 | int wait; |
| 721 | }; | 797 | }; |
| 722 | 798 | ||
| 723 | static __cacheline_aligned_in_smp DEFINE_SPINLOCK(call_lock); | ||
| 724 | static struct call_data_struct *call_data; | 799 | static struct call_data_struct *call_data; |
| 725 | 800 | ||
| 726 | extern unsigned long xcall_call_function; | 801 | extern unsigned long xcall_call_function; |
| @@ -1152,34 +1227,14 @@ void smp_penguin_jailcell(int irq, struct pt_regs *regs) | |||
| 1152 | preempt_enable(); | 1227 | preempt_enable(); |
| 1153 | } | 1228 | } |
| 1154 | 1229 | ||
| 1155 | void __init smp_tick_init(void) | ||
| 1156 | { | ||
| 1157 | boot_cpu_id = hard_smp_processor_id(); | ||
| 1158 | } | ||
| 1159 | |||
| 1160 | /* /proc/profile writes can call this, don't __init it please. */ | 1230 | /* /proc/profile writes can call this, don't __init it please. */ |
| 1161 | int setup_profiling_timer(unsigned int multiplier) | 1231 | int setup_profiling_timer(unsigned int multiplier) |
| 1162 | { | 1232 | { |
| 1163 | return -EINVAL; | 1233 | return -EINVAL; |
| 1164 | } | 1234 | } |
| 1165 | 1235 | ||
| 1166 | /* Constrain the number of cpus to max_cpus. */ | ||
| 1167 | void __init smp_prepare_cpus(unsigned int max_cpus) | 1236 | void __init smp_prepare_cpus(unsigned int max_cpus) |
| 1168 | { | 1237 | { |
| 1169 | int i; | ||
| 1170 | |||
| 1171 | if (num_possible_cpus() > max_cpus) { | ||
| 1172 | for_each_possible_cpu(i) { | ||
| 1173 | if (i != boot_cpu_id) { | ||
| 1174 | cpu_clear(i, phys_cpu_present_map); | ||
| 1175 | cpu_clear(i, cpu_present_map); | ||
| 1176 | if (num_possible_cpus() <= max_cpus) | ||
| 1177 | break; | ||
| 1178 | } | ||
| 1179 | } | ||
| 1180 | } | ||
| 1181 | |||
| 1182 | cpu_data(boot_cpu_id).udelay_val = loops_per_jiffy; | ||
| 1183 | } | 1238 | } |
| 1184 | 1239 | ||
| 1185 | void __devinit smp_prepare_boot_cpu(void) | 1240 | void __devinit smp_prepare_boot_cpu(void) |
| @@ -1190,30 +1245,32 @@ void __devinit smp_fill_in_sib_core_maps(void) | |||
| 1190 | { | 1245 | { |
| 1191 | unsigned int i; | 1246 | unsigned int i; |
| 1192 | 1247 | ||
| 1193 | for_each_possible_cpu(i) { | 1248 | for_each_present_cpu(i) { |
| 1194 | unsigned int j; | 1249 | unsigned int j; |
| 1195 | 1250 | ||
| 1251 | cpus_clear(cpu_core_map[i]); | ||
| 1196 | if (cpu_data(i).core_id == 0) { | 1252 | if (cpu_data(i).core_id == 0) { |
| 1197 | cpu_set(i, cpu_core_map[i]); | 1253 | cpu_set(i, cpu_core_map[i]); |
| 1198 | continue; | 1254 | continue; |
| 1199 | } | 1255 | } |
| 1200 | 1256 | ||
| 1201 | for_each_possible_cpu(j) { | 1257 | for_each_present_cpu(j) { |
| 1202 | if (cpu_data(i).core_id == | 1258 | if (cpu_data(i).core_id == |
| 1203 | cpu_data(j).core_id) | 1259 | cpu_data(j).core_id) |
| 1204 | cpu_set(j, cpu_core_map[i]); | 1260 | cpu_set(j, cpu_core_map[i]); |
| 1205 | } | 1261 | } |
| 1206 | } | 1262 | } |
| 1207 | 1263 | ||
| 1208 | for_each_possible_cpu(i) { | 1264 | for_each_present_cpu(i) { |
| 1209 | unsigned int j; | 1265 | unsigned int j; |
| 1210 | 1266 | ||
| 1267 | cpus_clear(cpu_sibling_map[i]); | ||
| 1211 | if (cpu_data(i).proc_id == -1) { | 1268 | if (cpu_data(i).proc_id == -1) { |
| 1212 | cpu_set(i, cpu_sibling_map[i]); | 1269 | cpu_set(i, cpu_sibling_map[i]); |
| 1213 | continue; | 1270 | continue; |
| 1214 | } | 1271 | } |
| 1215 | 1272 | ||
| 1216 | for_each_possible_cpu(j) { | 1273 | for_each_present_cpu(j) { |
| 1217 | if (cpu_data(i).proc_id == | 1274 | if (cpu_data(i).proc_id == |
| 1218 | cpu_data(j).proc_id) | 1275 | cpu_data(j).proc_id) |
| 1219 | cpu_set(j, cpu_sibling_map[i]); | 1276 | cpu_set(j, cpu_sibling_map[i]); |
| @@ -1242,18 +1299,112 @@ int __cpuinit __cpu_up(unsigned int cpu) | |||
| 1242 | return ret; | 1299 | return ret; |
| 1243 | } | 1300 | } |
| 1244 | 1301 | ||
| 1245 | void __init smp_cpus_done(unsigned int max_cpus) | 1302 | #ifdef CONFIG_HOTPLUG_CPU |
| 1303 | void cpu_play_dead(void) | ||
| 1304 | { | ||
| 1305 | int cpu = smp_processor_id(); | ||
| 1306 | unsigned long pstate; | ||
| 1307 | |||
| 1308 | idle_task_exit(); | ||
| 1309 | |||
| 1310 | if (tlb_type == hypervisor) { | ||
| 1311 | struct trap_per_cpu *tb = &trap_block[cpu]; | ||
| 1312 | |||
| 1313 | sun4v_cpu_qconf(HV_CPU_QUEUE_CPU_MONDO, | ||
| 1314 | tb->cpu_mondo_pa, 0); | ||
| 1315 | sun4v_cpu_qconf(HV_CPU_QUEUE_DEVICE_MONDO, | ||
| 1316 | tb->dev_mondo_pa, 0); | ||
| 1317 | sun4v_cpu_qconf(HV_CPU_QUEUE_RES_ERROR, | ||
| 1318 | tb->resum_mondo_pa, 0); | ||
| 1319 | sun4v_cpu_qconf(HV_CPU_QUEUE_NONRES_ERROR, | ||
| 1320 | tb->nonresum_mondo_pa, 0); | ||
| 1321 | } | ||
| 1322 | |||
| 1323 | cpu_clear(cpu, smp_commenced_mask); | ||
| 1324 | membar_safe("#Sync"); | ||
| 1325 | |||
| 1326 | local_irq_disable(); | ||
| 1327 | |||
| 1328 | __asm__ __volatile__( | ||
| 1329 | "rdpr %%pstate, %0\n\t" | ||
| 1330 | "wrpr %0, %1, %%pstate" | ||
| 1331 | : "=r" (pstate) | ||
| 1332 | : "i" (PSTATE_IE)); | ||
| 1333 | |||
| 1334 | while (1) | ||
| 1335 | barrier(); | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | int __cpu_disable(void) | ||
| 1246 | { | 1339 | { |
| 1247 | unsigned long bogosum = 0; | 1340 | int cpu = smp_processor_id(); |
| 1341 | cpuinfo_sparc *c; | ||
| 1248 | int i; | 1342 | int i; |
| 1249 | 1343 | ||
| 1250 | for_each_online_cpu(i) | 1344 | for_each_cpu_mask(i, cpu_core_map[cpu]) |
| 1251 | bogosum += cpu_data(i).udelay_val; | 1345 | cpu_clear(cpu, cpu_core_map[i]); |
| 1252 | printk("Total of %ld processors activated " | 1346 | cpus_clear(cpu_core_map[cpu]); |
| 1253 | "(%lu.%02lu BogoMIPS).\n", | 1347 | |
| 1254 | (long) num_online_cpus(), | 1348 | for_each_cpu_mask(i, cpu_sibling_map[cpu]) |
| 1255 | bogosum/(500000/HZ), | 1349 | cpu_clear(cpu, cpu_sibling_map[i]); |
| 1256 | (bogosum/(5000/HZ))%100); | 1350 | cpus_clear(cpu_sibling_map[cpu]); |
| 1351 | |||
| 1352 | c = &cpu_data(cpu); | ||
| 1353 | |||
| 1354 | c->core_id = 0; | ||
| 1355 | c->proc_id = -1; | ||
| 1356 | |||
| 1357 | spin_lock(&call_lock); | ||
| 1358 | cpu_clear(cpu, cpu_online_map); | ||
| 1359 | spin_unlock(&call_lock); | ||
| 1360 | |||
| 1361 | smp_wmb(); | ||
| 1362 | |||
| 1363 | /* Make sure no interrupts point to this cpu. */ | ||
| 1364 | fixup_irqs(); | ||
| 1365 | |||
| 1366 | local_irq_enable(); | ||
| 1367 | mdelay(1); | ||
| 1368 | local_irq_disable(); | ||
| 1369 | |||
| 1370 | return 0; | ||
| 1371 | } | ||
| 1372 | |||
| 1373 | void __cpu_die(unsigned int cpu) | ||
| 1374 | { | ||
| 1375 | int i; | ||
| 1376 | |||
| 1377 | for (i = 0; i < 100; i++) { | ||
| 1378 | smp_rmb(); | ||
| 1379 | if (!cpu_isset(cpu, smp_commenced_mask)) | ||
| 1380 | break; | ||
| 1381 | msleep(100); | ||
| 1382 | } | ||
| 1383 | if (cpu_isset(cpu, smp_commenced_mask)) { | ||
| 1384 | printk(KERN_ERR "CPU %u didn't die...\n", cpu); | ||
| 1385 | } else { | ||
| 1386 | #if defined(CONFIG_SUN_LDOMS) | ||
| 1387 | unsigned long hv_err; | ||
| 1388 | int limit = 100; | ||
| 1389 | |||
| 1390 | do { | ||
| 1391 | hv_err = sun4v_cpu_stop(cpu); | ||
| 1392 | if (hv_err == HV_EOK) { | ||
| 1393 | cpu_clear(cpu, cpu_present_map); | ||
| 1394 | break; | ||
| 1395 | } | ||
| 1396 | } while (--limit > 0); | ||
| 1397 | if (limit <= 0) { | ||
| 1398 | printk(KERN_ERR "sun4v_cpu_stop() fails err=%lu\n", | ||
| 1399 | hv_err); | ||
| 1400 | } | ||
| 1401 | #endif | ||
| 1402 | } | ||
| 1403 | } | ||
| 1404 | #endif | ||
| 1405 | |||
| 1406 | void __init smp_cpus_done(unsigned int max_cpus) | ||
| 1407 | { | ||
| 1257 | } | 1408 | } |
| 1258 | 1409 | ||
| 1259 | void smp_send_reschedule(int cpu) | 1410 | void smp_send_reschedule(int cpu) |
diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index 6fa761612899..719d676c2ddc 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c | |||
| @@ -1,7 +1,6 @@ | |||
| 1 | /* $Id: sparc64_ksyms.c,v 1.121 2002/02/09 19:49:31 davem Exp $ | 1 | /* arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support. |
| 2 | * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support. | ||
| 3 | * | 2 | * |
| 4 | * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) | 3 | * Copyright (C) 1996, 2007 David S. Miller (davem@davemloft.net) |
| 5 | * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) | 4 | * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) |
| 6 | * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz) | 5 | * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz) |
| 7 | */ | 6 | */ |
| @@ -28,7 +27,6 @@ | |||
| 28 | #include <net/compat.h> | 27 | #include <net/compat.h> |
| 29 | 28 | ||
| 30 | #include <asm/oplib.h> | 29 | #include <asm/oplib.h> |
| 31 | #include <asm/delay.h> | ||
| 32 | #include <asm/system.h> | 30 | #include <asm/system.h> |
| 33 | #include <asm/auxio.h> | 31 | #include <asm/auxio.h> |
| 34 | #include <asm/pgtable.h> | 32 | #include <asm/pgtable.h> |
| @@ -124,10 +122,6 @@ EXPORT_SYMBOL(__write_lock); | |||
| 124 | EXPORT_SYMBOL(__write_unlock); | 122 | EXPORT_SYMBOL(__write_unlock); |
| 125 | EXPORT_SYMBOL(__write_trylock); | 123 | EXPORT_SYMBOL(__write_trylock); |
| 126 | 124 | ||
| 127 | /* CPU online map and active count. */ | ||
| 128 | EXPORT_SYMBOL(cpu_online_map); | ||
| 129 | EXPORT_SYMBOL(phys_cpu_present_map); | ||
| 130 | |||
| 131 | EXPORT_SYMBOL(smp_call_function); | 125 | EXPORT_SYMBOL(smp_call_function); |
| 132 | #endif /* CONFIG_SMP */ | 126 | #endif /* CONFIG_SMP */ |
| 133 | 127 | ||
| @@ -330,12 +324,6 @@ EXPORT_SYMBOL(memset); | |||
| 330 | EXPORT_SYMBOL(memmove); | 324 | EXPORT_SYMBOL(memmove); |
| 331 | EXPORT_SYMBOL(strncmp); | 325 | EXPORT_SYMBOL(strncmp); |
| 332 | 326 | ||
| 333 | /* Delay routines. */ | ||
| 334 | EXPORT_SYMBOL(__udelay); | ||
| 335 | EXPORT_SYMBOL(__ndelay); | ||
| 336 | EXPORT_SYMBOL(__const_udelay); | ||
| 337 | EXPORT_SYMBOL(__delay); | ||
| 338 | |||
| 339 | void VISenter(void); | 327 | void VISenter(void); |
| 340 | /* RAID code needs this */ | 328 | /* RAID code needs this */ |
| 341 | EXPORT_SYMBOL(VISenter); | 329 | EXPORT_SYMBOL(VISenter); |
diff --git a/arch/sparc64/kernel/sysfs.c b/arch/sparc64/kernel/sysfs.c index cdb1477af89f..52816c7be0b9 100644 --- a/arch/sparc64/kernel/sysfs.c +++ b/arch/sparc64/kernel/sysfs.c | |||
| @@ -193,7 +193,6 @@ static ssize_t show_##NAME(struct sys_device *dev, char *buf) \ | |||
| 193 | } | 193 | } |
| 194 | 194 | ||
| 195 | SHOW_CPUDATA_ULONG_NAME(clock_tick, clock_tick); | 195 | SHOW_CPUDATA_ULONG_NAME(clock_tick, clock_tick); |
| 196 | SHOW_CPUDATA_ULONG_NAME(udelay_val, udelay_val); | ||
| 197 | SHOW_CPUDATA_UINT_NAME(l1_dcache_size, dcache_size); | 196 | SHOW_CPUDATA_UINT_NAME(l1_dcache_size, dcache_size); |
| 198 | SHOW_CPUDATA_UINT_NAME(l1_dcache_line_size, dcache_line_size); | 197 | SHOW_CPUDATA_UINT_NAME(l1_dcache_line_size, dcache_line_size); |
| 199 | SHOW_CPUDATA_UINT_NAME(l1_icache_size, icache_size); | 198 | SHOW_CPUDATA_UINT_NAME(l1_icache_size, icache_size); |
| @@ -203,7 +202,6 @@ SHOW_CPUDATA_UINT_NAME(l2_cache_line_size, ecache_line_size); | |||
| 203 | 202 | ||
| 204 | static struct sysdev_attribute cpu_core_attrs[] = { | 203 | static struct sysdev_attribute cpu_core_attrs[] = { |
| 205 | _SYSDEV_ATTR(clock_tick, 0444, show_clock_tick, NULL), | 204 | _SYSDEV_ATTR(clock_tick, 0444, show_clock_tick, NULL), |
| 206 | _SYSDEV_ATTR(udelay_val, 0444, show_udelay_val, NULL), | ||
| 207 | _SYSDEV_ATTR(l1_dcache_size, 0444, show_l1_dcache_size, NULL), | 205 | _SYSDEV_ATTR(l1_dcache_size, 0444, show_l1_dcache_size, NULL), |
| 208 | _SYSDEV_ATTR(l1_dcache_line_size, 0444, show_l1_dcache_line_size, NULL), | 206 | _SYSDEV_ATTR(l1_dcache_line_size, 0444, show_l1_dcache_line_size, NULL), |
| 209 | _SYSDEV_ATTR(l1_icache_size, 0444, show_l1_icache_size, NULL), | 207 | _SYSDEV_ATTR(l1_icache_size, 0444, show_l1_icache_size, NULL), |
diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index a31a0439244f..62e316ab1339 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c | |||
| @@ -849,9 +849,6 @@ static unsigned long sparc64_init_timers(void) | |||
| 849 | { | 849 | { |
| 850 | struct device_node *dp; | 850 | struct device_node *dp; |
| 851 | unsigned long clock; | 851 | unsigned long clock; |
| 852 | #ifdef CONFIG_SMP | ||
| 853 | extern void smp_tick_init(void); | ||
| 854 | #endif | ||
| 855 | 852 | ||
| 856 | dp = of_find_node_by_path("/"); | 853 | dp = of_find_node_by_path("/"); |
| 857 | if (tlb_type == spitfire) { | 854 | if (tlb_type == spitfire) { |
| @@ -874,10 +871,6 @@ static unsigned long sparc64_init_timers(void) | |||
| 874 | clock = of_getintprop_default(dp, "stick-frequency", 0); | 871 | clock = of_getintprop_default(dp, "stick-frequency", 0); |
| 875 | } | 872 | } |
| 876 | 873 | ||
| 877 | #ifdef CONFIG_SMP | ||
| 878 | smp_tick_init(); | ||
| 879 | #endif | ||
| 880 | |||
| 881 | return clock; | 874 | return clock; |
| 882 | } | 875 | } |
| 883 | 876 | ||
| @@ -1038,10 +1031,31 @@ static void __init setup_clockevent_multiplier(unsigned long hz) | |||
| 1038 | sparc64_clockevent.mult = mult; | 1031 | sparc64_clockevent.mult = mult; |
| 1039 | } | 1032 | } |
| 1040 | 1033 | ||
| 1034 | static unsigned long tb_ticks_per_usec __read_mostly; | ||
| 1035 | |||
| 1036 | void __delay(unsigned long loops) | ||
| 1037 | { | ||
| 1038 | unsigned long bclock, now; | ||
| 1039 | |||
| 1040 | bclock = tick_ops->get_tick(); | ||
| 1041 | do { | ||
| 1042 | now = tick_ops->get_tick(); | ||
| 1043 | } while ((now-bclock) < loops); | ||
| 1044 | } | ||
| 1045 | EXPORT_SYMBOL(__delay); | ||
| 1046 | |||
| 1047 | void udelay(unsigned long usecs) | ||
| 1048 | { | ||
| 1049 | __delay(tb_ticks_per_usec * usecs); | ||
| 1050 | } | ||
| 1051 | EXPORT_SYMBOL(udelay); | ||
| 1052 | |||
| 1041 | void __init time_init(void) | 1053 | void __init time_init(void) |
| 1042 | { | 1054 | { |
| 1043 | unsigned long clock = sparc64_init_timers(); | 1055 | unsigned long clock = sparc64_init_timers(); |
| 1044 | 1056 | ||
| 1057 | tb_ticks_per_usec = clock / USEC_PER_SEC; | ||
| 1058 | |||
| 1045 | timer_ticks_per_nsec_quotient = | 1059 | timer_ticks_per_nsec_quotient = |
| 1046 | clocksource_hz2mult(clock, SPARC64_NSEC_PER_CYC_SHIFT); | 1060 | clocksource_hz2mult(clock, SPARC64_NSEC_PER_CYC_SHIFT); |
| 1047 | 1061 | ||
diff --git a/arch/sparc64/kernel/vio.c b/arch/sparc64/kernel/vio.c new file mode 100644 index 000000000000..49569b44ea1f --- /dev/null +++ b/arch/sparc64/kernel/vio.c | |||
| @@ -0,0 +1,395 @@ | |||
| 1 | /* vio.c: Virtual I/O channel devices probing infrastructure. | ||
| 2 | * | ||
| 3 | * Copyright (c) 2003-2005 IBM Corp. | ||
| 4 | * Dave Engebretsen engebret@us.ibm.com | ||
| 5 | * Santiago Leon santil@us.ibm.com | ||
| 6 | * Hollis Blanchard <hollisb@us.ibm.com> | ||
| 7 | * Stephen Rothwell | ||
| 8 | * | ||
| 9 | * Adapted to sparc64 by David S. Miller davem@davemloft.net | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/kernel.h> | ||
| 13 | #include <linux/irq.h> | ||
| 14 | #include <linux/init.h> | ||
| 15 | |||
| 16 | #include <asm/mdesc.h> | ||
| 17 | #include <asm/vio.h> | ||
| 18 | |||
| 19 | static inline int find_in_proplist(const char *list, const char *match, | ||
| 20 | int len) | ||
| 21 | { | ||
| 22 | while (len > 0) { | ||
| 23 | int l; | ||
| 24 | |||
| 25 | if (!strcmp(list, match)) | ||
| 26 | return 1; | ||
| 27 | l = strlen(list) + 1; | ||
| 28 | list += l; | ||
| 29 | len -= l; | ||
| 30 | } | ||
| 31 | return 0; | ||
| 32 | } | ||
| 33 | |||
| 34 | static const struct vio_device_id *vio_match_device( | ||
| 35 | const struct vio_device_id *matches, | ||
| 36 | const struct vio_dev *dev) | ||
| 37 | { | ||
| 38 | const char *type, *compat; | ||
| 39 | int len; | ||
| 40 | |||
| 41 | type = dev->type; | ||
| 42 | compat = dev->compat; | ||
| 43 | len = dev->compat_len; | ||
| 44 | |||
| 45 | while (matches->type[0] || matches->compat[0]) { | ||
| 46 | int match = 1; | ||
| 47 | if (matches->type[0]) | ||
| 48 | match &= !strcmp(matches->type, type); | ||
| 49 | |||
| 50 | if (matches->compat[0]) { | ||
| 51 | match &= len && | ||
| 52 | find_in_proplist(compat, matches->compat, len); | ||
| 53 | } | ||
| 54 | if (match) | ||
| 55 | return matches; | ||
| 56 | matches++; | ||
| 57 | } | ||
| 58 | return NULL; | ||
| 59 | } | ||
| 60 | |||
| 61 | static int vio_bus_match(struct device *dev, struct device_driver *drv) | ||
| 62 | { | ||
| 63 | struct vio_dev *vio_dev = to_vio_dev(dev); | ||
| 64 | struct vio_driver *vio_drv = to_vio_driver(drv); | ||
| 65 | const struct vio_device_id *matches = vio_drv->id_table; | ||
| 66 | |||
| 67 | if (!matches) | ||
| 68 | return 0; | ||
| 69 | |||
| 70 | return vio_match_device(matches, vio_dev) != NULL; | ||
| 71 | } | ||
| 72 | |||
| 73 | static int vio_device_probe(struct device *dev) | ||
| 74 | { | ||
| 75 | struct vio_dev *vdev = to_vio_dev(dev); | ||
| 76 | struct vio_driver *drv = to_vio_driver(dev->driver); | ||
| 77 | const struct vio_device_id *id; | ||
| 78 | int error = -ENODEV; | ||
| 79 | |||
| 80 | if (drv->probe) { | ||
| 81 | id = vio_match_device(drv->id_table, vdev); | ||
| 82 | if (id) | ||
| 83 | error = drv->probe(vdev, id); | ||
| 84 | } | ||
| 85 | |||
| 86 | return error; | ||
| 87 | } | ||
| 88 | |||
| 89 | static int vio_device_remove(struct device *dev) | ||
| 90 | { | ||
| 91 | struct vio_dev *vdev = to_vio_dev(dev); | ||
| 92 | struct vio_driver *drv = to_vio_driver(dev->driver); | ||
| 93 | |||
| 94 | if (drv->remove) | ||
| 95 | return drv->remove(vdev); | ||
| 96 | |||
| 97 | return 1; | ||
| 98 | } | ||
| 99 | |||
| 100 | static ssize_t devspec_show(struct device *dev, | ||
| 101 | struct device_attribute *attr, char *buf) | ||
| 102 | { | ||
| 103 | struct vio_dev *vdev = to_vio_dev(dev); | ||
| 104 | const char *str = "none"; | ||
| 105 | |||
| 106 | if (!strcmp(vdev->type, "network")) | ||
| 107 | str = "vnet"; | ||
| 108 | else if (!strcmp(vdev->type, "block")) | ||
| 109 | str = "vdisk"; | ||
| 110 | |||
| 111 | return sprintf(buf, "%s\n", str); | ||
| 112 | } | ||
| 113 | |||
| 114 | static ssize_t type_show(struct device *dev, | ||
| 115 | struct device_attribute *attr, char *buf) | ||
| 116 | { | ||
| 117 | struct vio_dev *vdev = to_vio_dev(dev); | ||
| 118 | return sprintf(buf, "%s\n", vdev->type); | ||
| 119 | } | ||
| 120 | |||
| 121 | static struct device_attribute vio_dev_attrs[] = { | ||
| 122 | __ATTR_RO(devspec), | ||
| 123 | __ATTR_RO(type), | ||
| 124 | __ATTR_NULL | ||
| 125 | }; | ||
| 126 | |||
| 127 | static struct bus_type vio_bus_type = { | ||
| 128 | .name = "vio", | ||
| 129 | .dev_attrs = vio_dev_attrs, | ||
| 130 | .match = vio_bus_match, | ||
| 131 | .probe = vio_device_probe, | ||
| 132 | .remove = vio_device_remove, | ||
| 133 | }; | ||
| 134 | |||
| 135 | int vio_register_driver(struct vio_driver *viodrv) | ||
| 136 | { | ||
| 137 | viodrv->driver.bus = &vio_bus_type; | ||
| 138 | |||
| 139 | return driver_register(&viodrv->driver); | ||
| 140 | } | ||
| 141 | EXPORT_SYMBOL(vio_register_driver); | ||
| 142 | |||
| 143 | void vio_unregister_driver(struct vio_driver *viodrv) | ||
| 144 | { | ||
| 145 | driver_unregister(&viodrv->driver); | ||
| 146 | } | ||
| 147 | EXPORT_SYMBOL(vio_unregister_driver); | ||
| 148 | |||
| 149 | static void __devinit vio_dev_release(struct device *dev) | ||
| 150 | { | ||
| 151 | kfree(to_vio_dev(dev)); | ||
| 152 | } | ||
| 153 | |||
| 154 | static ssize_t | ||
| 155 | show_pciobppath_attr(struct device *dev, struct device_attribute *attr, | ||
| 156 | char *buf) | ||
| 157 | { | ||
| 158 | struct vio_dev *vdev; | ||
| 159 | struct device_node *dp; | ||
| 160 | |||
| 161 | vdev = to_vio_dev(dev); | ||
| 162 | dp = vdev->dp; | ||
| 163 | |||
| 164 | return snprintf (buf, PAGE_SIZE, "%s\n", dp->full_name); | ||
| 165 | } | ||
| 166 | |||
| 167 | static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH, | ||
| 168 | show_pciobppath_attr, NULL); | ||
| 169 | |||
| 170 | struct device_node *cdev_node; | ||
| 171 | |||
| 172 | static struct vio_dev *root_vdev; | ||
| 173 | static u64 cdev_cfg_handle; | ||
| 174 | |||
| 175 | static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp, | ||
| 176 | struct vio_dev *vdev) | ||
| 177 | { | ||
| 178 | u64 a; | ||
| 179 | |||
| 180 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) { | ||
| 181 | const u64 *chan_id; | ||
| 182 | const u64 *irq; | ||
| 183 | u64 target; | ||
| 184 | |||
| 185 | target = mdesc_arc_target(hp, a); | ||
| 186 | |||
| 187 | irq = mdesc_get_property(hp, target, "tx-ino", NULL); | ||
| 188 | if (irq) | ||
| 189 | vdev->tx_irq = sun4v_build_virq(cdev_cfg_handle, *irq); | ||
| 190 | |||
| 191 | irq = mdesc_get_property(hp, target, "rx-ino", NULL); | ||
| 192 | if (irq) | ||
| 193 | vdev->rx_irq = sun4v_build_virq(cdev_cfg_handle, *irq); | ||
| 194 | |||
| 195 | chan_id = mdesc_get_property(hp, target, "id", NULL); | ||
| 196 | if (chan_id) | ||
| 197 | vdev->channel_id = *chan_id; | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, | ||
| 202 | struct device *parent) | ||
| 203 | { | ||
| 204 | const char *type, *compat; | ||
| 205 | struct device_node *dp; | ||
| 206 | struct vio_dev *vdev; | ||
| 207 | int err, tlen, clen; | ||
| 208 | |||
| 209 | type = mdesc_get_property(hp, mp, "device-type", &tlen); | ||
| 210 | if (!type) { | ||
| 211 | type = mdesc_get_property(hp, mp, "name", &tlen); | ||
| 212 | if (!type) { | ||
| 213 | type = mdesc_node_name(hp, mp); | ||
| 214 | tlen = strlen(type) + 1; | ||
| 215 | } | ||
| 216 | } | ||
| 217 | if (tlen > VIO_MAX_TYPE_LEN) { | ||
| 218 | printk(KERN_ERR "VIO: Type string [%s] is too long.\n", | ||
| 219 | type); | ||
| 220 | return NULL; | ||
| 221 | } | ||
| 222 | |||
| 223 | compat = mdesc_get_property(hp, mp, "device-type", &clen); | ||
| 224 | if (!compat) { | ||
| 225 | clen = 0; | ||
| 226 | } else if (clen > VIO_MAX_COMPAT_LEN) { | ||
| 227 | printk(KERN_ERR "VIO: Compat len %d for [%s] is too long.\n", | ||
| 228 | clen, type); | ||
| 229 | return NULL; | ||
| 230 | } | ||
| 231 | |||
| 232 | vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); | ||
| 233 | if (!vdev) { | ||
| 234 | printk(KERN_ERR "VIO: Could not allocate vio_dev\n"); | ||
| 235 | return NULL; | ||
| 236 | } | ||
| 237 | |||
| 238 | vdev->mp = mp; | ||
| 239 | memcpy(vdev->type, type, tlen); | ||
| 240 | if (compat) | ||
| 241 | memcpy(vdev->compat, compat, clen); | ||
| 242 | else | ||
| 243 | memset(vdev->compat, 0, sizeof(vdev->compat)); | ||
| 244 | vdev->compat_len = clen; | ||
| 245 | |||
| 246 | vdev->channel_id = ~0UL; | ||
| 247 | vdev->tx_irq = ~0; | ||
| 248 | vdev->rx_irq = ~0; | ||
| 249 | |||
| 250 | vio_fill_channel_info(hp, mp, vdev); | ||
| 251 | |||
| 252 | snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%lx", mp); | ||
| 253 | vdev->dev.parent = parent; | ||
| 254 | vdev->dev.bus = &vio_bus_type; | ||
| 255 | vdev->dev.release = vio_dev_release; | ||
| 256 | |||
| 257 | if (parent == NULL) { | ||
| 258 | dp = cdev_node; | ||
| 259 | } else if (to_vio_dev(parent) == root_vdev) { | ||
| 260 | dp = of_get_next_child(cdev_node, NULL); | ||
| 261 | while (dp) { | ||
| 262 | if (!strcmp(dp->type, type)) | ||
| 263 | break; | ||
| 264 | |||
| 265 | dp = of_get_next_child(cdev_node, dp); | ||
| 266 | } | ||
| 267 | } else { | ||
| 268 | dp = to_vio_dev(parent)->dp; | ||
| 269 | } | ||
| 270 | vdev->dp = dp; | ||
| 271 | |||
| 272 | err = device_register(&vdev->dev); | ||
| 273 | if (err) { | ||
| 274 | printk(KERN_ERR "VIO: Could not register device %s, err=%d\n", | ||
| 275 | vdev->dev.bus_id, err); | ||
| 276 | kfree(vdev); | ||
| 277 | return NULL; | ||
| 278 | } | ||
| 279 | if (vdev->dp) | ||
| 280 | err = sysfs_create_file(&vdev->dev.kobj, | ||
| 281 | &dev_attr_obppath.attr); | ||
| 282 | |||
| 283 | return vdev; | ||
| 284 | } | ||
| 285 | |||
| 286 | static void walk_tree(struct mdesc_handle *hp, u64 n, struct vio_dev *parent) | ||
| 287 | { | ||
| 288 | u64 a; | ||
| 289 | |||
| 290 | mdesc_for_each_arc(a, hp, n, MDESC_ARC_TYPE_FWD) { | ||
| 291 | struct vio_dev *vdev; | ||
| 292 | u64 target; | ||
| 293 | |||
| 294 | target = mdesc_arc_target(hp, a); | ||
| 295 | vdev = vio_create_one(hp, target, &parent->dev); | ||
| 296 | if (vdev) | ||
| 297 | walk_tree(hp, target, vdev); | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | static void create_devices(struct mdesc_handle *hp, u64 root) | ||
| 302 | { | ||
| 303 | u64 mp; | ||
| 304 | |||
| 305 | root_vdev = vio_create_one(hp, root, NULL); | ||
| 306 | if (!root_vdev) { | ||
| 307 | printk(KERN_ERR "VIO: Coult not create root device.\n"); | ||
| 308 | return; | ||
| 309 | } | ||
| 310 | |||
| 311 | walk_tree(hp, root, root_vdev); | ||
| 312 | |||
| 313 | /* Domain services is odd as it doesn't sit underneath the | ||
| 314 | * channel-devices node, so we plug it in manually. | ||
| 315 | */ | ||
| 316 | mp = mdesc_node_by_name(hp, MDESC_NODE_NULL, "domain-services"); | ||
| 317 | if (mp != MDESC_NODE_NULL) { | ||
| 318 | struct vio_dev *parent = vio_create_one(hp, mp, | ||
| 319 | &root_vdev->dev); | ||
| 320 | |||
| 321 | if (parent) | ||
| 322 | walk_tree(hp, mp, parent); | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 326 | const char *channel_devices_node = "channel-devices"; | ||
| 327 | const char *channel_devices_compat = "SUNW,sun4v-channel-devices"; | ||
| 328 | const char *cfg_handle_prop = "cfg-handle"; | ||
| 329 | |||
| 330 | static int __init vio_init(void) | ||
| 331 | { | ||
| 332 | struct mdesc_handle *hp; | ||
| 333 | const char *compat; | ||
| 334 | const u64 *cfg_handle; | ||
| 335 | int err, len; | ||
| 336 | u64 root; | ||
| 337 | |||
| 338 | err = bus_register(&vio_bus_type); | ||
| 339 | if (err) { | ||
| 340 | printk(KERN_ERR "VIO: Could not register bus type err=%d\n", | ||
| 341 | err); | ||
| 342 | return err; | ||
| 343 | } | ||
| 344 | |||
| 345 | hp = mdesc_grab(); | ||
| 346 | if (!hp) | ||
| 347 | return 0; | ||
| 348 | |||
| 349 | root = mdesc_node_by_name(hp, MDESC_NODE_NULL, channel_devices_node); | ||
| 350 | if (root == MDESC_NODE_NULL) { | ||
| 351 | printk(KERN_INFO "VIO: No channel-devices MDESC node.\n"); | ||
| 352 | mdesc_release(hp); | ||
| 353 | return 0; | ||
| 354 | } | ||
| 355 | |||
| 356 | cdev_node = of_find_node_by_name(NULL, "channel-devices"); | ||
| 357 | err = -ENODEV; | ||
| 358 | if (!cdev_node) { | ||
| 359 | printk(KERN_INFO "VIO: No channel-devices OBP node.\n"); | ||
| 360 | goto out_release; | ||
| 361 | } | ||
| 362 | |||
| 363 | compat = mdesc_get_property(hp, root, "compatible", &len); | ||
| 364 | if (!compat) { | ||
| 365 | printk(KERN_ERR "VIO: Channel devices lacks compatible " | ||
| 366 | "property\n"); | ||
| 367 | goto out_release; | ||
| 368 | } | ||
| 369 | if (!find_in_proplist(compat, channel_devices_compat, len)) { | ||
| 370 | printk(KERN_ERR "VIO: Channel devices node lacks (%s) " | ||
| 371 | "compat entry.\n", channel_devices_compat); | ||
| 372 | goto out_release; | ||
| 373 | } | ||
| 374 | |||
| 375 | cfg_handle = mdesc_get_property(hp, root, cfg_handle_prop, NULL); | ||
| 376 | if (!cfg_handle) { | ||
| 377 | printk(KERN_ERR "VIO: Channel devices lacks %s property\n", | ||
| 378 | cfg_handle_prop); | ||
| 379 | goto out_release; | ||
| 380 | } | ||
| 381 | |||
| 382 | cdev_cfg_handle = *cfg_handle; | ||
| 383 | |||
| 384 | create_devices(hp, root); | ||
| 385 | |||
| 386 | mdesc_release(hp); | ||
| 387 | |||
| 388 | return 0; | ||
| 389 | |||
| 390 | out_release: | ||
| 391 | mdesc_release(hp); | ||
| 392 | return err; | ||
| 393 | } | ||
| 394 | |||
| 395 | postcore_initcall(vio_init); | ||
diff --git a/arch/sparc64/kernel/viohs.c b/arch/sparc64/kernel/viohs.c new file mode 100644 index 000000000000..15613add45d1 --- /dev/null +++ b/arch/sparc64/kernel/viohs.c | |||
| @@ -0,0 +1,792 @@ | |||
| 1 | /* viohs.c: LDOM Virtual I/O handshake helper layer. | ||
| 2 | * | ||
| 3 | * Copyright (C) 2007 David S. Miller <davem@davemloft.net> | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <linux/kernel.h> | ||
| 7 | #include <linux/module.h> | ||
| 8 | #include <linux/string.h> | ||
| 9 | #include <linux/delay.h> | ||
| 10 | #include <linux/sched.h> | ||
| 11 | #include <linux/slab.h> | ||
| 12 | |||
| 13 | #include <asm/ldc.h> | ||
| 14 | #include <asm/vio.h> | ||
| 15 | |||
| 16 | int vio_ldc_send(struct vio_driver_state *vio, void *data, int len) | ||
| 17 | { | ||
| 18 | int err, limit = 1000; | ||
| 19 | |||
| 20 | err = -EINVAL; | ||
| 21 | while (limit-- > 0) { | ||
| 22 | err = ldc_write(vio->lp, data, len); | ||
| 23 | if (!err || (err != -EAGAIN)) | ||
| 24 | break; | ||
| 25 | udelay(1); | ||
| 26 | } | ||
| 27 | |||
| 28 | return err; | ||
| 29 | } | ||
| 30 | EXPORT_SYMBOL(vio_ldc_send); | ||
| 31 | |||
| 32 | static int send_ctrl(struct vio_driver_state *vio, | ||
| 33 | struct vio_msg_tag *tag, int len) | ||
| 34 | { | ||
| 35 | tag->sid = vio_send_sid(vio); | ||
| 36 | return vio_ldc_send(vio, tag, len); | ||
| 37 | } | ||
| 38 | |||
| 39 | static void init_tag(struct vio_msg_tag *tag, u8 type, u8 stype, u16 stype_env) | ||
| 40 | { | ||
| 41 | tag->type = type; | ||
| 42 | tag->stype = stype; | ||
| 43 | tag->stype_env = stype_env; | ||
| 44 | } | ||
| 45 | |||
| 46 | static int send_version(struct vio_driver_state *vio, u16 major, u16 minor) | ||
| 47 | { | ||
| 48 | struct vio_ver_info pkt; | ||
| 49 | |||
| 50 | vio->_local_sid = (u32) sched_clock(); | ||
| 51 | |||
| 52 | memset(&pkt, 0, sizeof(pkt)); | ||
| 53 | init_tag(&pkt.tag, VIO_TYPE_CTRL, VIO_SUBTYPE_INFO, VIO_VER_INFO); | ||
| 54 | pkt.major = major; | ||
| 55 | pkt.minor = minor; | ||
| 56 | pkt.dev_class = vio->dev_class; | ||
| 57 | |||
| 58 | viodbg(HS, "SEND VERSION INFO maj[%u] min[%u] devclass[%u]\n", | ||
| 59 | major, minor, vio->dev_class); | ||
| 60 | |||
| 61 | return send_ctrl(vio, &pkt.tag, sizeof(pkt)); | ||
| 62 | } | ||
| 63 | |||
| 64 | static int start_handshake(struct vio_driver_state *vio) | ||
| 65 | { | ||
| 66 | int err; | ||
| 67 | |||
| 68 | viodbg(HS, "START HANDSHAKE\n"); | ||
| 69 | |||
| 70 | vio->hs_state = VIO_HS_INVALID; | ||
| 71 | |||
| 72 | err = send_version(vio, | ||
| 73 | vio->ver_table[0].major, | ||
| 74 | vio->ver_table[0].minor); | ||
| 75 | if (err < 0) | ||
| 76 | return err; | ||
| 77 | |||
| 78 | return 0; | ||
| 79 | } | ||
| 80 | |||
| 81 | void vio_link_state_change(struct vio_driver_state *vio, int event) | ||
| 82 | { | ||
| 83 | if (event == LDC_EVENT_UP) { | ||
| 84 | vio->hs_state = VIO_HS_INVALID; | ||
| 85 | |||
| 86 | switch (vio->dev_class) { | ||
| 87 | case VDEV_NETWORK: | ||
| 88 | case VDEV_NETWORK_SWITCH: | ||
| 89 | vio->dr_state = (VIO_DR_STATE_TXREQ | | ||
| 90 | VIO_DR_STATE_RXREQ); | ||
| 91 | break; | ||
| 92 | |||
| 93 | case VDEV_DISK: | ||
| 94 | vio->dr_state = VIO_DR_STATE_TXREQ; | ||
| 95 | break; | ||
| 96 | case VDEV_DISK_SERVER: | ||
| 97 | vio->dr_state = VIO_DR_STATE_RXREQ; | ||
| 98 | break; | ||
| 99 | } | ||
| 100 | start_handshake(vio); | ||
| 101 | } | ||
| 102 | } | ||
| 103 | EXPORT_SYMBOL(vio_link_state_change); | ||
| 104 | |||
| 105 | static int handshake_failure(struct vio_driver_state *vio) | ||
| 106 | { | ||
| 107 | struct vio_dring_state *dr; | ||
| 108 | |||
| 109 | /* XXX Put policy here... Perhaps start a timer to fire | ||
| 110 | * XXX in 100 ms, which will bring the link up and retry | ||
| 111 | * XXX the handshake. | ||
| 112 | */ | ||
| 113 | |||
| 114 | viodbg(HS, "HANDSHAKE FAILURE\n"); | ||
| 115 | |||
| 116 | vio->dr_state &= ~(VIO_DR_STATE_TXREG | | ||
| 117 | VIO_DR_STATE_RXREG); | ||
| 118 | |||
| 119 | dr = &vio->drings[VIO_DRIVER_RX_RING]; | ||
| 120 | memset(dr, 0, sizeof(*dr)); | ||
| 121 | |||
| 122 | kfree(vio->desc_buf); | ||
| 123 | vio->desc_buf = NULL; | ||
| 124 | vio->desc_buf_len = 0; | ||
| 125 | |||
| 126 | vio->hs_state = VIO_HS_INVALID; | ||
| 127 | |||
| 128 | return -ECONNRESET; | ||
| 129 | } | ||
| 130 | |||
| 131 | static int process_unknown(struct vio_driver_state *vio, void *arg) | ||
| 132 | { | ||
| 133 | struct vio_msg_tag *pkt = arg; | ||
| 134 | |||
| 135 | viodbg(HS, "UNKNOWN CONTROL [%02x:%02x:%04x:%08x]\n", | ||
| 136 | pkt->type, pkt->stype, pkt->stype_env, pkt->sid); | ||
| 137 | |||
| 138 | printk(KERN_ERR "vio: ID[%lu] Resetting connection.\n", | ||
| 139 | vio->vdev->channel_id); | ||
| 140 | |||
| 141 | ldc_disconnect(vio->lp); | ||
| 142 | |||
| 143 | return -ECONNRESET; | ||
| 144 | } | ||
| 145 | |||
| 146 | static int send_dreg(struct vio_driver_state *vio) | ||
| 147 | { | ||
| 148 | struct vio_dring_state *dr = &vio->drings[VIO_DRIVER_TX_RING]; | ||
| 149 | union { | ||
| 150 | struct vio_dring_register pkt; | ||
| 151 | char all[sizeof(struct vio_dring_register) + | ||
| 152 | (sizeof(struct ldc_trans_cookie) * | ||
| 153 | dr->ncookies)]; | ||
| 154 | } u; | ||
| 155 | int i; | ||
| 156 | |||
| 157 | memset(&u, 0, sizeof(u)); | ||
| 158 | init_tag(&u.pkt.tag, VIO_TYPE_CTRL, VIO_SUBTYPE_INFO, VIO_DRING_REG); | ||
| 159 | u.pkt.dring_ident = 0; | ||
| 160 | u.pkt.num_descr = dr->num_entries; | ||
| 161 | u.pkt.descr_size = dr->entry_size; | ||
| 162 | u.pkt.options = VIO_TX_DRING; | ||
| 163 | u.pkt.num_cookies = dr->ncookies; | ||
| 164 | |||
| 165 | viodbg(HS, "SEND DRING_REG INFO ndesc[%u] dsz[%u] opt[0x%x] " | ||
| 166 | "ncookies[%u]\n", | ||
| 167 | u.pkt.num_descr, u.pkt.descr_size, u.pkt.options, | ||
| 168 | u.pkt.num_cookies); | ||
| 169 | |||
| 170 | for (i = 0; i < dr->ncookies; i++) { | ||
| 171 | u.pkt.cookies[i] = dr->cookies[i]; | ||
| 172 | |||
| 173 | viodbg(HS, "DRING COOKIE(%d) [%016llx:%016llx]\n", | ||
| 174 | i, | ||
| 175 | (unsigned long long) u.pkt.cookies[i].cookie_addr, | ||
| 176 | (unsigned long long) u.pkt.cookies[i].cookie_size); | ||
| 177 | } | ||
| 178 | |||
| 179 | return send_ctrl(vio, &u.pkt.tag, sizeof(u)); | ||
| 180 | } | ||
| 181 | |||
| 182 | static int send_rdx(struct vio_driver_state *vio) | ||
| 183 | { | ||
| 184 | struct vio_rdx pkt; | ||
| 185 | |||
| 186 | memset(&pkt, 0, sizeof(pkt)); | ||
| 187 | |||
| 188 | init_tag(&pkt.tag, VIO_TYPE_CTRL, VIO_SUBTYPE_INFO, VIO_RDX); | ||
| 189 | |||
| 190 | viodbg(HS, "SEND RDX INFO\n"); | ||
| 191 | |||
| 192 | return send_ctrl(vio, &pkt.tag, sizeof(pkt)); | ||
| 193 | } | ||
| 194 | |||
| 195 | static int send_attr(struct vio_driver_state *vio) | ||
| 196 | { | ||
| 197 | return vio->ops->send_attr(vio); | ||
| 198 | } | ||
| 199 | |||
| 200 | static struct vio_version *find_by_major(struct vio_driver_state *vio, | ||
| 201 | u16 major) | ||
| 202 | { | ||
| 203 | struct vio_version *ret = NULL; | ||
| 204 | int i; | ||
| 205 | |||
| 206 | for (i = 0; i < vio->ver_table_entries; i++) { | ||
| 207 | struct vio_version *v = &vio->ver_table[i]; | ||
| 208 | if (v->major <= major) { | ||
| 209 | ret = v; | ||
| 210 | break; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | return ret; | ||
| 214 | } | ||
| 215 | |||
| 216 | static int process_ver_info(struct vio_driver_state *vio, | ||
| 217 | struct vio_ver_info *pkt) | ||
| 218 | { | ||
| 219 | struct vio_version *vap; | ||
| 220 | int err; | ||
| 221 | |||
| 222 | viodbg(HS, "GOT VERSION INFO maj[%u] min[%u] devclass[%u]\n", | ||
| 223 | pkt->major, pkt->minor, pkt->dev_class); | ||
| 224 | |||
| 225 | if (vio->hs_state != VIO_HS_INVALID) { | ||
| 226 | /* XXX Perhaps invoke start_handshake? XXX */ | ||
| 227 | memset(&vio->ver, 0, sizeof(vio->ver)); | ||
| 228 | vio->hs_state = VIO_HS_INVALID; | ||
| 229 | } | ||
| 230 | |||
| 231 | vap = find_by_major(vio, pkt->major); | ||
| 232 | |||
| 233 | vio->_peer_sid = pkt->tag.sid; | ||
| 234 | |||
| 235 | if (!vap) { | ||
| 236 | pkt->tag.stype = VIO_SUBTYPE_NACK; | ||
| 237 | pkt->major = 0; | ||
| 238 | pkt->minor = 0; | ||
| 239 | viodbg(HS, "SEND VERSION NACK maj[0] min[0]\n"); | ||
| 240 | err = send_ctrl(vio, &pkt->tag, sizeof(*pkt)); | ||
| 241 | } else if (vap->major != pkt->major) { | ||
| 242 | pkt->tag.stype = VIO_SUBTYPE_NACK; | ||
| 243 | pkt->major = vap->major; | ||
| 244 | pkt->minor = vap->minor; | ||
| 245 | viodbg(HS, "SEND VERSION NACK maj[%u] min[%u]\n", | ||
| 246 | pkt->major, pkt->minor); | ||
| 247 | err = send_ctrl(vio, &pkt->tag, sizeof(*pkt)); | ||
| 248 | } else { | ||
| 249 | struct vio_version ver = { | ||
| 250 | .major = pkt->major, | ||
| 251 | .minor = pkt->minor, | ||
| 252 | }; | ||
| 253 | if (ver.minor > vap->minor) | ||
| 254 | ver.minor = vap->minor; | ||
| 255 | pkt->minor = ver.minor; | ||
| 256 | pkt->tag.stype = VIO_SUBTYPE_ACK; | ||
| 257 | viodbg(HS, "SEND VERSION ACK maj[%u] min[%u]\n", | ||
| 258 | pkt->major, pkt->minor); | ||
| 259 | err = send_ctrl(vio, &pkt->tag, sizeof(*pkt)); | ||
| 260 | if (err > 0) { | ||
| 261 | vio->ver = ver; | ||
| 262 | vio->hs_state = VIO_HS_GOTVERS; | ||
| 263 | } | ||
| 264 | } | ||
| 265 | if (err < 0) | ||
| 266 | return handshake_failure(vio); | ||
| 267 | |||
| 268 | return 0; | ||
| 269 | } | ||
| 270 | |||
| 271 | static int process_ver_ack(struct vio_driver_state *vio, | ||
| 272 | struct vio_ver_info *pkt) | ||
| 273 | { | ||
| 274 | viodbg(HS, "GOT VERSION ACK maj[%u] min[%u] devclass[%u]\n", | ||
| 275 | pkt->major, pkt->minor, pkt->dev_class); | ||
| 276 | |||
| 277 | if (vio->hs_state & VIO_HS_GOTVERS) { | ||
| 278 | if (vio->ver.major != pkt->major || | ||
| 279 | vio->ver.minor != pkt->minor) { | ||
| 280 | pkt->tag.stype = VIO_SUBTYPE_NACK; | ||
| 281 | (void) send_ctrl(vio, &pkt->tag, sizeof(*pkt)); | ||
| 282 | return handshake_failure(vio); | ||
| 283 | } | ||
| 284 | } else { | ||
| 285 | vio->ver.major = pkt->major; | ||
| 286 | vio->ver.minor = pkt->minor; | ||
| 287 | vio->hs_state = VIO_HS_GOTVERS; | ||
| 288 | } | ||
| 289 | |||
| 290 | switch (vio->dev_class) { | ||
| 291 | case VDEV_NETWORK: | ||
| 292 | case VDEV_DISK: | ||
| 293 | if (send_attr(vio) < 0) | ||
| 294 | return handshake_failure(vio); | ||
| 295 | break; | ||
| 296 | |||
| 297 | default: | ||
| 298 | break; | ||
| 299 | } | ||
| 300 | |||
| 301 | return 0; | ||
| 302 | } | ||
| 303 | |||
| 304 | static int process_ver_nack(struct vio_driver_state *vio, | ||
| 305 | struct vio_ver_info *pkt) | ||
| 306 | { | ||
| 307 | struct vio_version *nver; | ||
| 308 | |||
| 309 | viodbg(HS, "GOT VERSION NACK maj[%u] min[%u] devclass[%u]\n", | ||
| 310 | pkt->major, pkt->minor, pkt->dev_class); | ||
| 311 | |||
| 312 | if ((pkt->major == 0 && pkt->minor == 0) || | ||
| 313 | !(nver = find_by_major(vio, pkt->major))) | ||
| 314 | return handshake_failure(vio); | ||
| 315 | |||
| 316 | if (send_version(vio, nver->major, nver->minor) < 0) | ||
| 317 | return handshake_failure(vio); | ||
| 318 | |||
| 319 | return 0; | ||
| 320 | } | ||
| 321 | |||
| 322 | static int process_ver(struct vio_driver_state *vio, struct vio_ver_info *pkt) | ||
| 323 | { | ||
| 324 | switch (pkt->tag.stype) { | ||
| 325 | case VIO_SUBTYPE_INFO: | ||
| 326 | return process_ver_info(vio, pkt); | ||
| 327 | |||
| 328 | case VIO_SUBTYPE_ACK: | ||
| 329 | return process_ver_ack(vio, pkt); | ||
| 330 | |||
| 331 | case VIO_SUBTYPE_NACK: | ||
| 332 | return process_ver_nack(vio, pkt); | ||
| 333 | |||
| 334 | default: | ||
| 335 | return handshake_failure(vio); | ||
| 336 | }; | ||
| 337 | } | ||
| 338 | |||
| 339 | static int process_attr(struct vio_driver_state *vio, void *pkt) | ||
| 340 | { | ||
| 341 | int err; | ||
| 342 | |||
| 343 | if (!(vio->hs_state & VIO_HS_GOTVERS)) | ||
| 344 | return handshake_failure(vio); | ||
| 345 | |||
| 346 | err = vio->ops->handle_attr(vio, pkt); | ||
| 347 | if (err < 0) { | ||
| 348 | return handshake_failure(vio); | ||
| 349 | } else { | ||
| 350 | vio->hs_state |= VIO_HS_GOT_ATTR; | ||
| 351 | |||
| 352 | if ((vio->dr_state & VIO_DR_STATE_TXREQ) && | ||
| 353 | !(vio->hs_state & VIO_HS_SENT_DREG)) { | ||
| 354 | if (send_dreg(vio) < 0) | ||
| 355 | return handshake_failure(vio); | ||
| 356 | |||
| 357 | vio->hs_state |= VIO_HS_SENT_DREG; | ||
| 358 | } | ||
| 359 | } | ||
| 360 | return 0; | ||
| 361 | } | ||
| 362 | |||
| 363 | static int all_drings_registered(struct vio_driver_state *vio) | ||
| 364 | { | ||
| 365 | int need_rx, need_tx; | ||
| 366 | |||
| 367 | need_rx = (vio->dr_state & VIO_DR_STATE_RXREQ); | ||
| 368 | need_tx = (vio->dr_state & VIO_DR_STATE_TXREQ); | ||
| 369 | |||
| 370 | if (need_rx && | ||
| 371 | !(vio->dr_state & VIO_DR_STATE_RXREG)) | ||
| 372 | return 0; | ||
| 373 | |||
| 374 | if (need_tx && | ||
| 375 | !(vio->dr_state & VIO_DR_STATE_TXREG)) | ||
| 376 | return 0; | ||
| 377 | |||
| 378 | return 1; | ||
| 379 | } | ||
| 380 | |||
| 381 | static int process_dreg_info(struct vio_driver_state *vio, | ||
| 382 | struct vio_dring_register *pkt) | ||
| 383 | { | ||
| 384 | struct vio_dring_state *dr; | ||
| 385 | int i, len; | ||
| 386 | |||
| 387 | viodbg(HS, "GOT DRING_REG INFO ident[%llx] " | ||
| 388 | "ndesc[%u] dsz[%u] opt[0x%x] ncookies[%u]\n", | ||
| 389 | (unsigned long long) pkt->dring_ident, | ||
| 390 | pkt->num_descr, pkt->descr_size, pkt->options, | ||
| 391 | pkt->num_cookies); | ||
| 392 | |||
| 393 | if (!(vio->dr_state & VIO_DR_STATE_RXREQ)) | ||
| 394 | goto send_nack; | ||
| 395 | |||
| 396 | if (vio->dr_state & VIO_DR_STATE_RXREG) | ||
| 397 | goto send_nack; | ||
| 398 | |||
| 399 | vio->desc_buf = kzalloc(pkt->descr_size, GFP_ATOMIC); | ||
| 400 | if (!vio->desc_buf) | ||
| 401 | goto send_nack; | ||
| 402 | |||
| 403 | vio->desc_buf_len = pkt->descr_size; | ||
| 404 | |||
| 405 | dr = &vio->drings[VIO_DRIVER_RX_RING]; | ||
| 406 | |||
| 407 | dr->num_entries = pkt->num_descr; | ||
| 408 | dr->entry_size = pkt->descr_size; | ||
| 409 | dr->ncookies = pkt->num_cookies; | ||
| 410 | for (i = 0; i < dr->ncookies; i++) { | ||
| 411 | dr->cookies[i] = pkt->cookies[i]; | ||
| 412 | |||
| 413 | viodbg(HS, "DRING COOKIE(%d) [%016llx:%016llx]\n", | ||
| 414 | i, | ||
| 415 | (unsigned long long) | ||
| 416 | pkt->cookies[i].cookie_addr, | ||
| 417 | (unsigned long long) | ||
| 418 | pkt->cookies[i].cookie_size); | ||
| 419 | } | ||
| 420 | |||
| 421 | pkt->tag.stype = VIO_SUBTYPE_ACK; | ||
| 422 | pkt->dring_ident = ++dr->ident; | ||
| 423 | |||
| 424 | viodbg(HS, "SEND DRING_REG ACK ident[%llx]\n", | ||
| 425 | (unsigned long long) pkt->dring_ident); | ||
| 426 | |||
| 427 | len = (sizeof(*pkt) + | ||
| 428 | (dr->ncookies * sizeof(struct ldc_trans_cookie))); | ||
| 429 | if (send_ctrl(vio, &pkt->tag, len) < 0) | ||
| 430 | goto send_nack; | ||
| 431 | |||
| 432 | vio->dr_state |= VIO_DR_STATE_RXREG; | ||
| 433 | |||
| 434 | return 0; | ||
| 435 | |||
| 436 | send_nack: | ||
| 437 | pkt->tag.stype = VIO_SUBTYPE_NACK; | ||
| 438 | viodbg(HS, "SEND DRING_REG NACK\n"); | ||
| 439 | (void) send_ctrl(vio, &pkt->tag, sizeof(*pkt)); | ||
| 440 | |||
| 441 | return handshake_failure(vio); | ||
| 442 | } | ||
| 443 | |||
| 444 | static int process_dreg_ack(struct vio_driver_state *vio, | ||
| 445 | struct vio_dring_register *pkt) | ||
| 446 | { | ||
| 447 | struct vio_dring_state *dr; | ||
| 448 | |||
| 449 | viodbg(HS, "GOT DRING_REG ACK ident[%llx] " | ||
| 450 | "ndesc[%u] dsz[%u] opt[0x%x] ncookies[%u]\n", | ||
| 451 | (unsigned long long) pkt->dring_ident, | ||
| 452 | pkt->num_descr, pkt->descr_size, pkt->options, | ||
| 453 | pkt->num_cookies); | ||
| 454 | |||
| 455 | dr = &vio->drings[VIO_DRIVER_TX_RING]; | ||
| 456 | |||
| 457 | if (!(vio->dr_state & VIO_DR_STATE_TXREQ)) | ||
| 458 | return handshake_failure(vio); | ||
| 459 | |||
| 460 | dr->ident = pkt->dring_ident; | ||
| 461 | vio->dr_state |= VIO_DR_STATE_TXREG; | ||
| 462 | |||
| 463 | if (all_drings_registered(vio)) { | ||
| 464 | if (send_rdx(vio) < 0) | ||
| 465 | return handshake_failure(vio); | ||
| 466 | vio->hs_state = VIO_HS_SENT_RDX; | ||
| 467 | } | ||
| 468 | return 0; | ||
| 469 | } | ||
| 470 | |||
| 471 | static int process_dreg_nack(struct vio_driver_state *vio, | ||
| 472 | struct vio_dring_register *pkt) | ||
| 473 | { | ||
| 474 | viodbg(HS, "GOT DRING_REG NACK ident[%llx] " | ||
| 475 | "ndesc[%u] dsz[%u] opt[0x%x] ncookies[%u]\n", | ||
| 476 | (unsigned long long) pkt->dring_ident, | ||
| 477 | pkt->num_descr, pkt->descr_size, pkt->options, | ||
| 478 | pkt->num_cookies); | ||
| 479 | |||
| 480 | return handshake_failure(vio); | ||
| 481 | } | ||
| 482 | |||
| 483 | static int process_dreg(struct vio_driver_state *vio, | ||
| 484 | struct vio_dring_register *pkt) | ||
| 485 | { | ||
| 486 | if (!(vio->hs_state & VIO_HS_GOTVERS)) | ||
| 487 | return handshake_failure(vio); | ||
| 488 | |||
| 489 | switch (pkt->tag.stype) { | ||
| 490 | case VIO_SUBTYPE_INFO: | ||
| 491 | return process_dreg_info(vio, pkt); | ||
| 492 | |||
| 493 | case VIO_SUBTYPE_ACK: | ||
| 494 | return process_dreg_ack(vio, pkt); | ||
| 495 | |||
| 496 | case VIO_SUBTYPE_NACK: | ||
| 497 | return process_dreg_nack(vio, pkt); | ||
| 498 | |||
| 499 | default: | ||
| 500 | return handshake_failure(vio); | ||
| 501 | } | ||
| 502 | } | ||
| 503 | |||
| 504 | static int process_dunreg(struct vio_driver_state *vio, | ||
| 505 | struct vio_dring_unregister *pkt) | ||
| 506 | { | ||
| 507 | struct vio_dring_state *dr = &vio->drings[VIO_DRIVER_RX_RING]; | ||
| 508 | |||
| 509 | viodbg(HS, "GOT DRING_UNREG\n"); | ||
| 510 | |||
| 511 | if (pkt->dring_ident != dr->ident) | ||
| 512 | return 0; | ||
| 513 | |||
| 514 | vio->dr_state &= ~VIO_DR_STATE_RXREG; | ||
| 515 | |||
| 516 | memset(dr, 0, sizeof(*dr)); | ||
| 517 | |||
| 518 | kfree(vio->desc_buf); | ||
| 519 | vio->desc_buf = NULL; | ||
| 520 | vio->desc_buf_len = 0; | ||
| 521 | |||
| 522 | return 0; | ||
| 523 | } | ||
| 524 | |||
| 525 | static int process_rdx_info(struct vio_driver_state *vio, struct vio_rdx *pkt) | ||
| 526 | { | ||
| 527 | viodbg(HS, "GOT RDX INFO\n"); | ||
| 528 | |||
| 529 | pkt->tag.stype = VIO_SUBTYPE_ACK; | ||
| 530 | viodbg(HS, "SEND RDX ACK\n"); | ||
| 531 | if (send_ctrl(vio, &pkt->tag, sizeof(*pkt)) < 0) | ||
| 532 | return handshake_failure(vio); | ||
| 533 | |||
| 534 | vio->hs_state |= VIO_HS_SENT_RDX_ACK; | ||
| 535 | return 0; | ||
| 536 | } | ||
| 537 | |||
| 538 | static int process_rdx_ack(struct vio_driver_state *vio, struct vio_rdx *pkt) | ||
| 539 | { | ||
| 540 | viodbg(HS, "GOT RDX ACK\n"); | ||
| 541 | |||
| 542 | if (!(vio->hs_state & VIO_HS_SENT_RDX)) | ||
| 543 | return handshake_failure(vio); | ||
| 544 | |||
| 545 | vio->hs_state |= VIO_HS_GOT_RDX_ACK; | ||
| 546 | return 0; | ||
| 547 | } | ||
| 548 | |||
| 549 | static int process_rdx_nack(struct vio_driver_state *vio, struct vio_rdx *pkt) | ||
| 550 | { | ||
| 551 | viodbg(HS, "GOT RDX NACK\n"); | ||
| 552 | |||
| 553 | return handshake_failure(vio); | ||
| 554 | } | ||
| 555 | |||
| 556 | static int process_rdx(struct vio_driver_state *vio, struct vio_rdx *pkt) | ||
| 557 | { | ||
| 558 | if (!all_drings_registered(vio)) | ||
| 559 | handshake_failure(vio); | ||
| 560 | |||
| 561 | switch (pkt->tag.stype) { | ||
| 562 | case VIO_SUBTYPE_INFO: | ||
| 563 | return process_rdx_info(vio, pkt); | ||
| 564 | |||
| 565 | case VIO_SUBTYPE_ACK: | ||
| 566 | return process_rdx_ack(vio, pkt); | ||
| 567 | |||
| 568 | case VIO_SUBTYPE_NACK: | ||
| 569 | return process_rdx_nack(vio, pkt); | ||
| 570 | |||
| 571 | default: | ||
| 572 | return handshake_failure(vio); | ||
| 573 | } | ||
| 574 | } | ||
| 575 | |||
| 576 | int vio_control_pkt_engine(struct vio_driver_state *vio, void *pkt) | ||
| 577 | { | ||
| 578 | struct vio_msg_tag *tag = pkt; | ||
| 579 | u8 prev_state = vio->hs_state; | ||
| 580 | int err; | ||
| 581 | |||
| 582 | switch (tag->stype_env) { | ||
| 583 | case VIO_VER_INFO: | ||
| 584 | err = process_ver(vio, pkt); | ||
| 585 | break; | ||
| 586 | |||
| 587 | case VIO_ATTR_INFO: | ||
| 588 | err = process_attr(vio, pkt); | ||
| 589 | break; | ||
| 590 | |||
| 591 | case VIO_DRING_REG: | ||
| 592 | err = process_dreg(vio, pkt); | ||
| 593 | break; | ||
| 594 | |||
| 595 | case VIO_DRING_UNREG: | ||
| 596 | err = process_dunreg(vio, pkt); | ||
| 597 | break; | ||
| 598 | |||
| 599 | case VIO_RDX: | ||
| 600 | err = process_rdx(vio, pkt); | ||
| 601 | break; | ||
| 602 | |||
| 603 | default: | ||
| 604 | err = process_unknown(vio, pkt); | ||
| 605 | break; | ||
| 606 | } | ||
| 607 | if (!err && | ||
| 608 | vio->hs_state != prev_state && | ||
| 609 | (vio->hs_state & VIO_HS_COMPLETE)) | ||
| 610 | vio->ops->handshake_complete(vio); | ||
| 611 | |||
| 612 | return err; | ||
| 613 | } | ||
| 614 | EXPORT_SYMBOL(vio_control_pkt_engine); | ||
| 615 | |||
| 616 | void vio_conn_reset(struct vio_driver_state *vio) | ||
| 617 | { | ||
| 618 | } | ||
| 619 | EXPORT_SYMBOL(vio_conn_reset); | ||
| 620 | |||
| 621 | /* The issue is that the Solaris virtual disk server just mirrors the | ||
| 622 | * SID values it gets from the client peer. So we work around that | ||
| 623 | * here in vio_{validate,send}_sid() so that the drivers don't need | ||
| 624 | * to be aware of this crap. | ||
| 625 | */ | ||
| 626 | int vio_validate_sid(struct vio_driver_state *vio, struct vio_msg_tag *tp) | ||
| 627 | { | ||
| 628 | u32 sid; | ||
| 629 | |||
| 630 | /* Always let VERSION+INFO packets through unchecked, they | ||
| 631 | * define the new SID. | ||
| 632 | */ | ||
| 633 | if (tp->type == VIO_TYPE_CTRL && | ||
| 634 | tp->stype == VIO_SUBTYPE_INFO && | ||
| 635 | tp->stype_env == VIO_VER_INFO) | ||
| 636 | return 0; | ||
| 637 | |||
| 638 | /* Ok, now figure out which SID to use. */ | ||
| 639 | switch (vio->dev_class) { | ||
| 640 | case VDEV_NETWORK: | ||
| 641 | case VDEV_NETWORK_SWITCH: | ||
| 642 | case VDEV_DISK_SERVER: | ||
| 643 | default: | ||
| 644 | sid = vio->_peer_sid; | ||
| 645 | break; | ||
| 646 | |||
| 647 | case VDEV_DISK: | ||
| 648 | sid = vio->_local_sid; | ||
| 649 | break; | ||
| 650 | } | ||
| 651 | |||
| 652 | if (sid == tp->sid) | ||
| 653 | return 0; | ||
| 654 | viodbg(DATA, "BAD SID tag->sid[%08x] peer_sid[%08x] local_sid[%08x]\n", | ||
| 655 | tp->sid, vio->_peer_sid, vio->_local_sid); | ||
| 656 | return -EINVAL; | ||
| 657 | } | ||
| 658 | EXPORT_SYMBOL(vio_validate_sid); | ||
| 659 | |||
| 660 | u32 vio_send_sid(struct vio_driver_state *vio) | ||
| 661 | { | ||
| 662 | switch (vio->dev_class) { | ||
| 663 | case VDEV_NETWORK: | ||
| 664 | case VDEV_NETWORK_SWITCH: | ||
| 665 | case VDEV_DISK: | ||
| 666 | default: | ||
| 667 | return vio->_local_sid; | ||
| 668 | |||
| 669 | case VDEV_DISK_SERVER: | ||
| 670 | return vio->_peer_sid; | ||
| 671 | } | ||
| 672 | } | ||
| 673 | EXPORT_SYMBOL(vio_send_sid); | ||
| 674 | |||
| 675 | extern int vio_ldc_alloc(struct vio_driver_state *vio, | ||
| 676 | struct ldc_channel_config *base_cfg, | ||
| 677 | void *event_arg) | ||
| 678 | { | ||
| 679 | struct ldc_channel_config cfg = *base_cfg; | ||
| 680 | struct ldc_channel *lp; | ||
| 681 | |||
| 682 | cfg.tx_irq = vio->vdev->tx_irq; | ||
| 683 | cfg.rx_irq = vio->vdev->rx_irq; | ||
| 684 | |||
| 685 | lp = ldc_alloc(vio->vdev->channel_id, &cfg, event_arg); | ||
| 686 | if (IS_ERR(lp)) | ||
| 687 | return PTR_ERR(lp); | ||
| 688 | |||
| 689 | vio->lp = lp; | ||
| 690 | |||
| 691 | return 0; | ||
| 692 | } | ||
| 693 | EXPORT_SYMBOL(vio_ldc_alloc); | ||
| 694 | |||
| 695 | void vio_ldc_free(struct vio_driver_state *vio) | ||
| 696 | { | ||
| 697 | ldc_free(vio->lp); | ||
| 698 | vio->lp = NULL; | ||
| 699 | |||
| 700 | kfree(vio->desc_buf); | ||
| 701 | vio->desc_buf = NULL; | ||
| 702 | vio->desc_buf_len = 0; | ||
| 703 | } | ||
| 704 | EXPORT_SYMBOL(vio_ldc_free); | ||
| 705 | |||
| 706 | void vio_port_up(struct vio_driver_state *vio) | ||
| 707 | { | ||
| 708 | unsigned long flags; | ||
| 709 | int err, state; | ||
| 710 | |||
| 711 | spin_lock_irqsave(&vio->lock, flags); | ||
| 712 | |||
| 713 | state = ldc_state(vio->lp); | ||
| 714 | |||
| 715 | err = 0; | ||
| 716 | if (state == LDC_STATE_INIT) { | ||
| 717 | err = ldc_bind(vio->lp, vio->name); | ||
| 718 | if (err) | ||
| 719 | printk(KERN_WARNING "%s: Port %lu bind failed, " | ||
| 720 | "err=%d\n", | ||
| 721 | vio->name, vio->vdev->channel_id, err); | ||
| 722 | } | ||
| 723 | |||
| 724 | if (!err) { | ||
| 725 | err = ldc_connect(vio->lp); | ||
| 726 | if (err) | ||
| 727 | printk(KERN_WARNING "%s: Port %lu connect failed, " | ||
| 728 | "err=%d\n", | ||
| 729 | vio->name, vio->vdev->channel_id, err); | ||
| 730 | } | ||
| 731 | if (err) { | ||
| 732 | unsigned long expires = jiffies + HZ; | ||
| 733 | |||
| 734 | expires = round_jiffies(expires); | ||
| 735 | mod_timer(&vio->timer, expires); | ||
| 736 | } | ||
| 737 | |||
| 738 | spin_unlock_irqrestore(&vio->lock, flags); | ||
| 739 | } | ||
| 740 | EXPORT_SYMBOL(vio_port_up); | ||
| 741 | |||
| 742 | static void vio_port_timer(unsigned long _arg) | ||
| 743 | { | ||
| 744 | struct vio_driver_state *vio = (struct vio_driver_state *) _arg; | ||
| 745 | |||
| 746 | vio_port_up(vio); | ||
| 747 | } | ||
| 748 | |||
| 749 | int vio_driver_init(struct vio_driver_state *vio, struct vio_dev *vdev, | ||
| 750 | u8 dev_class, struct vio_version *ver_table, | ||
| 751 | int ver_table_size, struct vio_driver_ops *ops, | ||
| 752 | char *name) | ||
| 753 | { | ||
| 754 | switch (dev_class) { | ||
| 755 | case VDEV_NETWORK: | ||
| 756 | case VDEV_NETWORK_SWITCH: | ||
| 757 | case VDEV_DISK: | ||
| 758 | case VDEV_DISK_SERVER: | ||
| 759 | break; | ||
| 760 | |||
| 761 | default: | ||
| 762 | return -EINVAL; | ||
| 763 | } | ||
| 764 | |||
| 765 | if (!ops->send_attr || | ||
| 766 | !ops->handle_attr || | ||
| 767 | !ops->handshake_complete) | ||
| 768 | return -EINVAL; | ||
| 769 | |||
| 770 | if (!ver_table || ver_table_size < 0) | ||
| 771 | return -EINVAL; | ||
| 772 | |||
| 773 | if (!name) | ||
| 774 | return -EINVAL; | ||
| 775 | |||
| 776 | spin_lock_init(&vio->lock); | ||
| 777 | |||
| 778 | vio->name = name; | ||
| 779 | |||
| 780 | vio->dev_class = dev_class; | ||
| 781 | vio->vdev = vdev; | ||
| 782 | |||
| 783 | vio->ver_table = ver_table; | ||
| 784 | vio->ver_table_entries = ver_table_size; | ||
| 785 | |||
| 786 | vio->ops = ops; | ||
| 787 | |||
| 788 | setup_timer(&vio->timer, vio_port_timer, (unsigned long) vio); | ||
| 789 | |||
| 790 | return 0; | ||
| 791 | } | ||
| 792 | EXPORT_SYMBOL(vio_driver_init); | ||
diff --git a/arch/sparc64/lib/Makefile b/arch/sparc64/lib/Makefile index 4a725d8985f1..c4a6d6e7d03c 100644 --- a/arch/sparc64/lib/Makefile +++ b/arch/sparc64/lib/Makefile | |||
| @@ -14,6 +14,6 @@ lib-y := PeeCeeI.o copy_page.o clear_page.o strlen.o strncmp.o \ | |||
| 14 | NGmemcpy.o NGcopy_from_user.o NGcopy_to_user.o NGpatch.o \ | 14 | NGmemcpy.o NGcopy_from_user.o NGcopy_to_user.o NGpatch.o \ |
| 15 | NGpage.o NGbzero.o \ | 15 | NGpage.o NGbzero.o \ |
| 16 | copy_in_user.o user_fixup.o memmove.o \ | 16 | copy_in_user.o user_fixup.o memmove.o \ |
| 17 | mcount.o ipcsum.o rwsem.o xor.o delay.o | 17 | mcount.o ipcsum.o rwsem.o xor.o |
| 18 | 18 | ||
| 19 | obj-y += iomap.o | 19 | obj-y += iomap.o |
diff --git a/arch/sparc64/lib/delay.c b/arch/sparc64/lib/delay.c deleted file mode 100644 index fb27e54a03ee..000000000000 --- a/arch/sparc64/lib/delay.c +++ /dev/null | |||
| @@ -1,46 +0,0 @@ | |||
| 1 | /* delay.c: Delay loops for sparc64 | ||
| 2 | * | ||
| 3 | * Copyright (C) 2004, 2006 David S. Miller <davem@davemloft.net> | ||
| 4 | * | ||
| 5 | * Based heavily upon x86 variant which is: | ||
| 6 | * Copyright (C) 1993 Linus Torvalds | ||
| 7 | * Copyright (C) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz> | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <linux/delay.h> | ||
| 11 | #include <asm/timer.h> | ||
| 12 | |||
| 13 | void __delay(unsigned long loops) | ||
| 14 | { | ||
| 15 | unsigned long bclock, now; | ||
| 16 | |||
| 17 | bclock = tick_ops->get_tick(); | ||
| 18 | do { | ||
| 19 | now = tick_ops->get_tick(); | ||
| 20 | } while ((now-bclock) < loops); | ||
| 21 | } | ||
| 22 | |||
| 23 | /* We used to multiply by HZ after shifting down by 32 bits | ||
| 24 | * but that runs into problems for higher values of HZ and | ||
| 25 | * slow cpus. | ||
| 26 | */ | ||
| 27 | void __const_udelay(unsigned long n) | ||
| 28 | { | ||
| 29 | n *= 4; | ||
| 30 | |||
| 31 | n *= (cpu_data(raw_smp_processor_id()).udelay_val * (HZ/4)); | ||
| 32 | n >>= 32; | ||
| 33 | |||
| 34 | __delay(n + 1); | ||
| 35 | } | ||
| 36 | |||
| 37 | void __udelay(unsigned long n) | ||
| 38 | { | ||
| 39 | __const_udelay(n * 0x10c7UL); | ||
| 40 | } | ||
| 41 | |||
| 42 | |||
| 43 | void __ndelay(unsigned long n) | ||
| 44 | { | ||
| 45 | __const_udelay(n * 0x5UL); | ||
| 46 | } | ||
diff --git a/arch/sparc64/prom/misc.c b/arch/sparc64/prom/misc.c index f3e0c14e9eef..33c5b7da31e5 100644 --- a/arch/sparc64/prom/misc.c +++ b/arch/sparc64/prom/misc.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <asm/openprom.h> | 14 | #include <asm/openprom.h> |
| 15 | #include <asm/oplib.h> | 15 | #include <asm/oplib.h> |
| 16 | #include <asm/system.h> | 16 | #include <asm/system.h> |
| 17 | #include <asm/ldc.h> | ||
| 17 | 18 | ||
| 18 | int prom_service_exists(const char *service_name) | 19 | int prom_service_exists(const char *service_name) |
| 19 | { | 20 | { |
| @@ -37,6 +38,10 @@ void prom_sun4v_guest_soft_state(void) | |||
| 37 | /* Reset and reboot the machine with the command 'bcommand'. */ | 38 | /* Reset and reboot the machine with the command 'bcommand'. */ |
| 38 | void prom_reboot(const char *bcommand) | 39 | void prom_reboot(const char *bcommand) |
| 39 | { | 40 | { |
| 41 | #ifdef CONFIG_SUN_LDOMS | ||
| 42 | if (ldom_domaining_enabled) | ||
| 43 | ldom_reboot(bcommand); | ||
| 44 | #endif | ||
| 40 | p1275_cmd("boot", P1275_ARG(0, P1275_ARG_IN_STRING) | | 45 | p1275_cmd("boot", P1275_ARG(0, P1275_ARG_IN_STRING) | |
| 41 | P1275_INOUT(1, 0), bcommand); | 46 | P1275_INOUT(1, 0), bcommand); |
| 42 | } | 47 | } |
| @@ -91,6 +96,10 @@ void prom_cmdline(void) | |||
| 91 | */ | 96 | */ |
| 92 | void prom_halt(void) | 97 | void prom_halt(void) |
| 93 | { | 98 | { |
| 99 | #ifdef CONFIG_SUN_LDOMS | ||
| 100 | if (ldom_domaining_enabled) | ||
| 101 | ldom_power_off(); | ||
| 102 | #endif | ||
| 94 | again: | 103 | again: |
| 95 | p1275_cmd("exit", P1275_INOUT(0, 0)); | 104 | p1275_cmd("exit", P1275_INOUT(0, 0)); |
| 96 | goto again; /* PROM is out to get me -DaveM */ | 105 | goto again; /* PROM is out to get me -DaveM */ |
| @@ -98,6 +107,10 @@ again: | |||
| 98 | 107 | ||
| 99 | void prom_halt_power_off(void) | 108 | void prom_halt_power_off(void) |
| 100 | { | 109 | { |
| 110 | #ifdef CONFIG_SUN_LDOMS | ||
| 111 | if (ldom_domaining_enabled) | ||
| 112 | ldom_power_off(); | ||
| 113 | #endif | ||
| 101 | p1275_cmd("SUNW,power-off", P1275_INOUT(0, 0)); | 114 | p1275_cmd("SUNW,power-off", P1275_INOUT(0, 0)); |
| 102 | 115 | ||
| 103 | /* if nothing else helps, we just halt */ | 116 | /* if nothing else helps, we just halt */ |
diff --git a/arch/sparc64/prom/p1275.c b/arch/sparc64/prom/p1275.c index 2b32c489860c..7fcccc0e19cf 100644 --- a/arch/sparc64/prom/p1275.c +++ b/arch/sparc64/prom/p1275.c | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include <asm/system.h> | 16 | #include <asm/system.h> |
| 17 | #include <asm/spitfire.h> | 17 | #include <asm/spitfire.h> |
| 18 | #include <asm/pstate.h> | 18 | #include <asm/pstate.h> |
| 19 | #include <asm/ldc.h> | ||
| 19 | 20 | ||
| 20 | struct { | 21 | struct { |
| 21 | long prom_callback; /* 0x00 */ | 22 | long prom_callback; /* 0x00 */ |
diff --git a/arch/sparc64/prom/tree.c b/arch/sparc64/prom/tree.c index 500f05e2cfcb..17b7ecfe7ca9 100644 --- a/arch/sparc64/prom/tree.c +++ b/arch/sparc64/prom/tree.c | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | #include <asm/openprom.h> | 14 | #include <asm/openprom.h> |
| 15 | #include <asm/oplib.h> | 15 | #include <asm/oplib.h> |
| 16 | #include <asm/ldc.h> | ||
| 16 | 17 | ||
| 17 | /* Return the child of node 'node' or zero if no this node has no | 18 | /* Return the child of node 'node' or zero if no this node has no |
| 18 | * direct descendent. | 19 | * direct descendent. |
| @@ -261,9 +262,17 @@ int prom_node_has_property(int node, const char *prop) | |||
| 261 | int | 262 | int |
| 262 | prom_setprop(int node, const char *pname, char *value, int size) | 263 | prom_setprop(int node, const char *pname, char *value, int size) |
| 263 | { | 264 | { |
| 264 | if(size == 0) return 0; | 265 | if (size == 0) |
| 265 | if((pname == 0) || (value == 0)) return 0; | 266 | return 0; |
| 267 | if ((pname == 0) || (value == 0)) | ||
| 268 | return 0; | ||
| 266 | 269 | ||
| 270 | #ifdef CONFIG_SUN_LDOMS | ||
| 271 | if (ldom_domaining_enabled) { | ||
| 272 | ldom_set_var(pname, value); | ||
| 273 | return 0; | ||
| 274 | } | ||
| 275 | #endif | ||
| 267 | return p1275_cmd ("setprop", P1275_ARG(1,P1275_ARG_IN_STRING)| | 276 | return p1275_cmd ("setprop", P1275_ARG(1,P1275_ARG_IN_STRING)| |
| 268 | P1275_ARG(2,P1275_ARG_IN_BUF)| | 277 | P1275_ARG(2,P1275_ARG_IN_BUF)| |
| 269 | P1275_INOUT(4, 1), | 278 | P1275_INOUT(4, 1), |
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index fd7a53bdcb63..e49162b15578 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig | |||
| @@ -423,6 +423,13 @@ config ATA_OVER_ETH | |||
| 423 | This driver provides Support for ATA over Ethernet block | 423 | This driver provides Support for ATA over Ethernet block |
| 424 | devices like the Coraid EtherDrive (R) Storage Blade. | 424 | devices like the Coraid EtherDrive (R) Storage Blade. |
| 425 | 425 | ||
| 426 | config SUNVDC | ||
| 427 | tristate "Sun Virtual Disk Client support" | ||
| 428 | depends on SUN_LDOMS | ||
| 429 | help | ||
| 430 | Support for virtual disk devices as a client under Sun | ||
| 431 | Logical Domains. | ||
| 432 | |||
| 426 | source "drivers/s390/block/Kconfig" | 433 | source "drivers/s390/block/Kconfig" |
| 427 | 434 | ||
| 428 | endif # BLK_DEV | 435 | endif # BLK_DEV |
diff --git a/drivers/block/Makefile b/drivers/block/Makefile index e5f98acc5d52..43371c59623e 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile | |||
| @@ -19,6 +19,7 @@ obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o | |||
| 19 | obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o | 19 | obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o |
| 20 | obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o | 20 | obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o |
| 21 | obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o | 21 | obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o |
| 22 | obj-$(CONFIG_SUNVDC) += sunvdc.o | ||
| 22 | 23 | ||
| 23 | obj-$(CONFIG_BLK_DEV_UMEM) += umem.o | 24 | obj-$(CONFIG_BLK_DEV_UMEM) += umem.o |
| 24 | obj-$(CONFIG_BLK_DEV_NBD) += nbd.o | 25 | obj-$(CONFIG_BLK_DEV_NBD) += nbd.o |
diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c new file mode 100644 index 000000000000..0f5e3caf85d7 --- /dev/null +++ b/drivers/block/sunvdc.c | |||
| @@ -0,0 +1,972 @@ | |||
| 1 | /* sunvdc.c: Sun LDOM Virtual Disk Client. | ||
| 2 | * | ||
| 3 | * Copyright (C) 2007 David S. Miller <davem@davemloft.net> | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <linux/module.h> | ||
| 7 | #include <linux/kernel.h> | ||
| 8 | #include <linux/types.h> | ||
| 9 | #include <linux/blkdev.h> | ||
| 10 | #include <linux/hdreg.h> | ||
| 11 | #include <linux/genhd.h> | ||
| 12 | #include <linux/slab.h> | ||
| 13 | #include <linux/spinlock.h> | ||
| 14 | #include <linux/completion.h> | ||
| 15 | #include <linux/delay.h> | ||
| 16 | #include <linux/init.h> | ||
| 17 | #include <linux/list.h> | ||
| 18 | |||
| 19 | #include <asm/vio.h> | ||
| 20 | #include <asm/ldc.h> | ||
| 21 | |||
| 22 | #define DRV_MODULE_NAME "sunvdc" | ||
| 23 | #define PFX DRV_MODULE_NAME ": " | ||
| 24 | #define DRV_MODULE_VERSION "1.0" | ||
| 25 | #define DRV_MODULE_RELDATE "June 25, 2007" | ||
| 26 | |||
| 27 | static char version[] __devinitdata = | ||
| 28 | DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; | ||
| 29 | MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); | ||
| 30 | MODULE_DESCRIPTION("Sun LDOM virtual disk client driver"); | ||
| 31 | MODULE_LICENSE("GPL"); | ||
| 32 | MODULE_VERSION(DRV_MODULE_VERSION); | ||
| 33 | |||
| 34 | #define VDC_TX_RING_SIZE 256 | ||
| 35 | |||
| 36 | #define WAITING_FOR_LINK_UP 0x01 | ||
| 37 | #define WAITING_FOR_TX_SPACE 0x02 | ||
| 38 | #define WAITING_FOR_GEN_CMD 0x04 | ||
| 39 | #define WAITING_FOR_ANY -1 | ||
| 40 | |||
| 41 | struct vdc_req_entry { | ||
| 42 | struct request *req; | ||
| 43 | }; | ||
| 44 | |||
| 45 | struct vdc_port { | ||
| 46 | struct vio_driver_state vio; | ||
| 47 | |||
| 48 | struct vdc *vp; | ||
| 49 | |||
| 50 | struct gendisk *disk; | ||
| 51 | |||
| 52 | struct vdc_completion *cmp; | ||
| 53 | |||
| 54 | u64 req_id; | ||
| 55 | u64 seq; | ||
| 56 | struct vdc_req_entry rq_arr[VDC_TX_RING_SIZE]; | ||
| 57 | |||
| 58 | unsigned long ring_cookies; | ||
| 59 | |||
| 60 | u64 max_xfer_size; | ||
| 61 | u32 vdisk_block_size; | ||
| 62 | |||
| 63 | /* The server fills these in for us in the disk attribute | ||
| 64 | * ACK packet. | ||
| 65 | */ | ||
| 66 | u64 operations; | ||
| 67 | u32 vdisk_size; | ||
| 68 | u8 vdisk_type; | ||
| 69 | u8 dev_no; | ||
| 70 | |||
| 71 | char disk_name[32]; | ||
| 72 | |||
| 73 | struct vio_disk_geom geom; | ||
| 74 | struct vio_disk_vtoc label; | ||
| 75 | |||
| 76 | struct list_head list; | ||
| 77 | }; | ||
| 78 | |||
| 79 | static inline struct vdc_port *to_vdc_port(struct vio_driver_state *vio) | ||
| 80 | { | ||
| 81 | return container_of(vio, struct vdc_port, vio); | ||
| 82 | } | ||
| 83 | |||
| 84 | struct vdc { | ||
| 85 | /* Protects prot_list. */ | ||
| 86 | spinlock_t lock; | ||
| 87 | |||
| 88 | struct vio_dev *dev; | ||
| 89 | |||
| 90 | struct list_head port_list; | ||
| 91 | }; | ||
| 92 | |||
| 93 | /* Ordered from largest major to lowest */ | ||
| 94 | static struct vio_version vdc_versions[] = { | ||
| 95 | { .major = 1, .minor = 0 }, | ||
| 96 | }; | ||
| 97 | |||
| 98 | #define VDCBLK_NAME "vdisk" | ||
| 99 | static int vdc_major; | ||
| 100 | #define PARTITION_SHIFT 3 | ||
| 101 | |||
| 102 | static inline u32 vdc_tx_dring_avail(struct vio_dring_state *dr) | ||
| 103 | { | ||
| 104 | return vio_dring_avail(dr, VDC_TX_RING_SIZE); | ||
| 105 | } | ||
| 106 | |||
| 107 | static int vdc_getgeo(struct block_device *bdev, struct hd_geometry *geo) | ||
| 108 | { | ||
| 109 | struct gendisk *disk = bdev->bd_disk; | ||
| 110 | struct vdc_port *port = disk->private_data; | ||
| 111 | |||
| 112 | geo->heads = (u8) port->geom.num_hd; | ||
| 113 | geo->sectors = (u8) port->geom.num_sec; | ||
| 114 | geo->cylinders = port->geom.num_cyl; | ||
| 115 | |||
| 116 | return 0; | ||
| 117 | } | ||
| 118 | |||
| 119 | static struct block_device_operations vdc_fops = { | ||
| 120 | .owner = THIS_MODULE, | ||
| 121 | .getgeo = vdc_getgeo, | ||
| 122 | }; | ||
| 123 | |||
| 124 | static void vdc_finish(struct vio_driver_state *vio, int err, int waiting_for) | ||
| 125 | { | ||
| 126 | if (vio->cmp && | ||
| 127 | (waiting_for == -1 || | ||
| 128 | vio->cmp->waiting_for == waiting_for)) { | ||
| 129 | vio->cmp->err = err; | ||
| 130 | complete(&vio->cmp->com); | ||
| 131 | vio->cmp = NULL; | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | static void vdc_handshake_complete(struct vio_driver_state *vio) | ||
| 136 | { | ||
| 137 | vdc_finish(vio, 0, WAITING_FOR_LINK_UP); | ||
| 138 | } | ||
| 139 | |||
| 140 | static int vdc_handle_unknown(struct vdc_port *port, void *arg) | ||
| 141 | { | ||
| 142 | struct vio_msg_tag *pkt = arg; | ||
| 143 | |||
| 144 | printk(KERN_ERR PFX "Received unknown msg [%02x:%02x:%04x:%08x]\n", | ||
| 145 | pkt->type, pkt->stype, pkt->stype_env, pkt->sid); | ||
| 146 | printk(KERN_ERR PFX "Resetting connection.\n"); | ||
| 147 | |||
| 148 | ldc_disconnect(port->vio.lp); | ||
| 149 | |||
| 150 | return -ECONNRESET; | ||
| 151 | } | ||
| 152 | |||
| 153 | static int vdc_send_attr(struct vio_driver_state *vio) | ||
| 154 | { | ||
| 155 | struct vdc_port *port = to_vdc_port(vio); | ||
| 156 | struct vio_disk_attr_info pkt; | ||
| 157 | |||
| 158 | memset(&pkt, 0, sizeof(pkt)); | ||
| 159 | |||
| 160 | pkt.tag.type = VIO_TYPE_CTRL; | ||
| 161 | pkt.tag.stype = VIO_SUBTYPE_INFO; | ||
| 162 | pkt.tag.stype_env = VIO_ATTR_INFO; | ||
| 163 | pkt.tag.sid = vio_send_sid(vio); | ||
| 164 | |||
| 165 | pkt.xfer_mode = VIO_DRING_MODE; | ||
| 166 | pkt.vdisk_block_size = port->vdisk_block_size; | ||
| 167 | pkt.max_xfer_size = port->max_xfer_size; | ||
| 168 | |||
| 169 | viodbg(HS, "SEND ATTR xfer_mode[0x%x] blksz[%u] max_xfer[%lu]\n", | ||
| 170 | pkt.xfer_mode, pkt.vdisk_block_size, pkt.max_xfer_size); | ||
| 171 | |||
| 172 | return vio_ldc_send(&port->vio, &pkt, sizeof(pkt)); | ||
| 173 | } | ||
| 174 | |||
| 175 | static int vdc_handle_attr(struct vio_driver_state *vio, void *arg) | ||
| 176 | { | ||
| 177 | struct vdc_port *port = to_vdc_port(vio); | ||
| 178 | struct vio_disk_attr_info *pkt = arg; | ||
| 179 | |||
| 180 | viodbg(HS, "GOT ATTR stype[0x%x] ops[%lx] disk_size[%lu] disk_type[%x] " | ||
| 181 | "xfer_mode[0x%x] blksz[%u] max_xfer[%lu]\n", | ||
| 182 | pkt->tag.stype, pkt->operations, | ||
| 183 | pkt->vdisk_size, pkt->vdisk_type, | ||
| 184 | pkt->xfer_mode, pkt->vdisk_block_size, | ||
| 185 | pkt->max_xfer_size); | ||
| 186 | |||
| 187 | if (pkt->tag.stype == VIO_SUBTYPE_ACK) { | ||
| 188 | switch (pkt->vdisk_type) { | ||
| 189 | case VD_DISK_TYPE_DISK: | ||
| 190 | case VD_DISK_TYPE_SLICE: | ||
| 191 | break; | ||
| 192 | |||
| 193 | default: | ||
| 194 | printk(KERN_ERR PFX "%s: Bogus vdisk_type 0x%x\n", | ||
| 195 | vio->name, pkt->vdisk_type); | ||
| 196 | return -ECONNRESET; | ||
| 197 | } | ||
| 198 | |||
| 199 | if (pkt->vdisk_block_size > port->vdisk_block_size) { | ||
| 200 | printk(KERN_ERR PFX "%s: BLOCK size increased " | ||
| 201 | "%u --> %u\n", | ||
| 202 | vio->name, | ||
| 203 | port->vdisk_block_size, pkt->vdisk_block_size); | ||
| 204 | return -ECONNRESET; | ||
| 205 | } | ||
| 206 | |||
| 207 | port->operations = pkt->operations; | ||
| 208 | port->vdisk_size = pkt->vdisk_size; | ||
| 209 | port->vdisk_type = pkt->vdisk_type; | ||
| 210 | if (pkt->max_xfer_size < port->max_xfer_size) | ||
| 211 | port->max_xfer_size = pkt->max_xfer_size; | ||
| 212 | port->vdisk_block_size = pkt->vdisk_block_size; | ||
| 213 | return 0; | ||
| 214 | } else { | ||
| 215 | printk(KERN_ERR PFX "%s: Attribute NACK\n", vio->name); | ||
| 216 | |||
| 217 | return -ECONNRESET; | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | static void vdc_end_special(struct vdc_port *port, struct vio_disk_desc *desc) | ||
| 222 | { | ||
| 223 | int err = desc->status; | ||
| 224 | |||
| 225 | vdc_finish(&port->vio, -err, WAITING_FOR_GEN_CMD); | ||
| 226 | } | ||
| 227 | |||
| 228 | static void vdc_end_request(struct request *req, int uptodate, int num_sectors) | ||
| 229 | { | ||
| 230 | if (end_that_request_first(req, uptodate, num_sectors)) | ||
| 231 | return; | ||
| 232 | add_disk_randomness(req->rq_disk); | ||
| 233 | end_that_request_last(req, uptodate); | ||
| 234 | } | ||
| 235 | |||
| 236 | static void vdc_end_one(struct vdc_port *port, struct vio_dring_state *dr, | ||
| 237 | unsigned int index) | ||
| 238 | { | ||
| 239 | struct vio_disk_desc *desc = vio_dring_entry(dr, index); | ||
| 240 | struct vdc_req_entry *rqe = &port->rq_arr[index]; | ||
| 241 | struct request *req; | ||
| 242 | |||
| 243 | if (unlikely(desc->hdr.state != VIO_DESC_DONE)) | ||
| 244 | return; | ||
| 245 | |||
| 246 | ldc_unmap(port->vio.lp, desc->cookies, desc->ncookies); | ||
| 247 | desc->hdr.state = VIO_DESC_FREE; | ||
| 248 | dr->cons = (index + 1) & (VDC_TX_RING_SIZE - 1); | ||
| 249 | |||
| 250 | req = rqe->req; | ||
| 251 | if (req == NULL) { | ||
| 252 | vdc_end_special(port, desc); | ||
| 253 | return; | ||
| 254 | } | ||
| 255 | |||
| 256 | rqe->req = NULL; | ||
| 257 | |||
| 258 | vdc_end_request(req, !desc->status, desc->size >> 9); | ||
| 259 | |||
| 260 | if (blk_queue_stopped(port->disk->queue)) | ||
| 261 | blk_start_queue(port->disk->queue); | ||
| 262 | } | ||
| 263 | |||
| 264 | static int vdc_ack(struct vdc_port *port, void *msgbuf) | ||
| 265 | { | ||
| 266 | struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; | ||
| 267 | struct vio_dring_data *pkt = msgbuf; | ||
| 268 | |||
| 269 | if (unlikely(pkt->dring_ident != dr->ident || | ||
| 270 | pkt->start_idx != pkt->end_idx || | ||
| 271 | pkt->start_idx >= VDC_TX_RING_SIZE)) | ||
| 272 | return 0; | ||
| 273 | |||
| 274 | vdc_end_one(port, dr, pkt->start_idx); | ||
| 275 | |||
| 276 | return 0; | ||
| 277 | } | ||
| 278 | |||
| 279 | static int vdc_nack(struct vdc_port *port, void *msgbuf) | ||
| 280 | { | ||
| 281 | /* XXX Implement me XXX */ | ||
| 282 | return 0; | ||
| 283 | } | ||
| 284 | |||
| 285 | static void vdc_event(void *arg, int event) | ||
| 286 | { | ||
| 287 | struct vdc_port *port = arg; | ||
| 288 | struct vio_driver_state *vio = &port->vio; | ||
| 289 | unsigned long flags; | ||
| 290 | int err; | ||
| 291 | |||
| 292 | spin_lock_irqsave(&vio->lock, flags); | ||
| 293 | |||
| 294 | if (unlikely(event == LDC_EVENT_RESET || | ||
| 295 | event == LDC_EVENT_UP)) { | ||
| 296 | vio_link_state_change(vio, event); | ||
| 297 | spin_unlock_irqrestore(&vio->lock, flags); | ||
| 298 | return; | ||
| 299 | } | ||
| 300 | |||
| 301 | if (unlikely(event != LDC_EVENT_DATA_READY)) { | ||
| 302 | printk(KERN_WARNING PFX "Unexpected LDC event %d\n", event); | ||
| 303 | spin_unlock_irqrestore(&vio->lock, flags); | ||
| 304 | return; | ||
| 305 | } | ||
| 306 | |||
| 307 | err = 0; | ||
| 308 | while (1) { | ||
| 309 | union { | ||
| 310 | struct vio_msg_tag tag; | ||
| 311 | u64 raw[8]; | ||
| 312 | } msgbuf; | ||
| 313 | |||
| 314 | err = ldc_read(vio->lp, &msgbuf, sizeof(msgbuf)); | ||
| 315 | if (unlikely(err < 0)) { | ||
| 316 | if (err == -ECONNRESET) | ||
| 317 | vio_conn_reset(vio); | ||
| 318 | break; | ||
| 319 | } | ||
| 320 | if (err == 0) | ||
| 321 | break; | ||
| 322 | viodbg(DATA, "TAG [%02x:%02x:%04x:%08x]\n", | ||
| 323 | msgbuf.tag.type, | ||
| 324 | msgbuf.tag.stype, | ||
| 325 | msgbuf.tag.stype_env, | ||
| 326 | msgbuf.tag.sid); | ||
| 327 | err = vio_validate_sid(vio, &msgbuf.tag); | ||
| 328 | if (err < 0) | ||
| 329 | break; | ||
| 330 | |||
| 331 | if (likely(msgbuf.tag.type == VIO_TYPE_DATA)) { | ||
| 332 | if (msgbuf.tag.stype == VIO_SUBTYPE_ACK) | ||
| 333 | err = vdc_ack(port, &msgbuf); | ||
| 334 | else if (msgbuf.tag.stype == VIO_SUBTYPE_NACK) | ||
| 335 | err = vdc_nack(port, &msgbuf); | ||
| 336 | else | ||
| 337 | err = vdc_handle_unknown(port, &msgbuf); | ||
| 338 | } else if (msgbuf.tag.type == VIO_TYPE_CTRL) { | ||
| 339 | err = vio_control_pkt_engine(vio, &msgbuf); | ||
| 340 | } else { | ||
| 341 | err = vdc_handle_unknown(port, &msgbuf); | ||
| 342 | } | ||
| 343 | if (err < 0) | ||
| 344 | break; | ||
| 345 | } | ||
| 346 | if (err < 0) | ||
| 347 | vdc_finish(&port->vio, err, WAITING_FOR_ANY); | ||
| 348 | spin_unlock_irqrestore(&vio->lock, flags); | ||
| 349 | } | ||
| 350 | |||
| 351 | static int __vdc_tx_trigger(struct vdc_port *port) | ||
| 352 | { | ||
| 353 | struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; | ||
| 354 | struct vio_dring_data hdr = { | ||
| 355 | .tag = { | ||
| 356 | .type = VIO_TYPE_DATA, | ||
| 357 | .stype = VIO_SUBTYPE_INFO, | ||
| 358 | .stype_env = VIO_DRING_DATA, | ||
| 359 | .sid = vio_send_sid(&port->vio), | ||
| 360 | }, | ||
| 361 | .dring_ident = dr->ident, | ||
| 362 | .start_idx = dr->prod, | ||
| 363 | .end_idx = dr->prod, | ||
| 364 | }; | ||
| 365 | int err, delay; | ||
| 366 | |||
| 367 | hdr.seq = dr->snd_nxt; | ||
| 368 | delay = 1; | ||
| 369 | do { | ||
| 370 | err = vio_ldc_send(&port->vio, &hdr, sizeof(hdr)); | ||
| 371 | if (err > 0) { | ||
| 372 | dr->snd_nxt++; | ||
| 373 | break; | ||
| 374 | } | ||
| 375 | udelay(delay); | ||
| 376 | if ((delay <<= 1) > 128) | ||
| 377 | delay = 128; | ||
| 378 | } while (err == -EAGAIN); | ||
| 379 | |||
| 380 | return err; | ||
| 381 | } | ||
| 382 | |||
| 383 | static int __send_request(struct request *req) | ||
| 384 | { | ||
| 385 | struct vdc_port *port = req->rq_disk->private_data; | ||
| 386 | struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; | ||
| 387 | struct scatterlist sg[port->ring_cookies]; | ||
| 388 | struct vdc_req_entry *rqe; | ||
| 389 | struct vio_disk_desc *desc; | ||
| 390 | unsigned int map_perm; | ||
| 391 | int nsg, err, i; | ||
| 392 | u64 len; | ||
| 393 | u8 op; | ||
| 394 | |||
| 395 | map_perm = LDC_MAP_SHADOW | LDC_MAP_DIRECT | LDC_MAP_IO; | ||
| 396 | |||
| 397 | if (rq_data_dir(req) == READ) { | ||
| 398 | map_perm |= LDC_MAP_W; | ||
| 399 | op = VD_OP_BREAD; | ||
| 400 | } else { | ||
| 401 | map_perm |= LDC_MAP_R; | ||
| 402 | op = VD_OP_BWRITE; | ||
| 403 | } | ||
| 404 | |||
| 405 | nsg = blk_rq_map_sg(req->q, req, sg); | ||
| 406 | |||
| 407 | len = 0; | ||
| 408 | for (i = 0; i < nsg; i++) | ||
| 409 | len += sg[i].length; | ||
| 410 | |||
| 411 | if (unlikely(vdc_tx_dring_avail(dr) < 1)) { | ||
| 412 | blk_stop_queue(port->disk->queue); | ||
| 413 | err = -ENOMEM; | ||
| 414 | goto out; | ||
| 415 | } | ||
| 416 | |||
| 417 | desc = vio_dring_cur(dr); | ||
| 418 | |||
| 419 | err = ldc_map_sg(port->vio.lp, sg, nsg, | ||
| 420 | desc->cookies, port->ring_cookies, | ||
| 421 | map_perm); | ||
| 422 | if (err < 0) { | ||
| 423 | printk(KERN_ERR PFX "ldc_map_sg() failure, err=%d.\n", err); | ||
| 424 | return err; | ||
| 425 | } | ||
| 426 | |||
| 427 | rqe = &port->rq_arr[dr->prod]; | ||
| 428 | rqe->req = req; | ||
| 429 | |||
| 430 | desc->hdr.ack = VIO_ACK_ENABLE; | ||
| 431 | desc->req_id = port->req_id; | ||
| 432 | desc->operation = op; | ||
| 433 | if (port->vdisk_type == VD_DISK_TYPE_DISK) { | ||
| 434 | desc->slice = 2; | ||
| 435 | } else { | ||
| 436 | desc->slice = 0; | ||
| 437 | } | ||
| 438 | desc->status = ~0; | ||
| 439 | desc->offset = (req->sector << 9) / port->vdisk_block_size; | ||
| 440 | desc->size = len; | ||
| 441 | desc->ncookies = err; | ||
| 442 | |||
| 443 | /* This has to be a non-SMP write barrier because we are writing | ||
| 444 | * to memory which is shared with the peer LDOM. | ||
| 445 | */ | ||
| 446 | wmb(); | ||
| 447 | desc->hdr.state = VIO_DESC_READY; | ||
| 448 | |||
| 449 | err = __vdc_tx_trigger(port); | ||
| 450 | if (err < 0) { | ||
| 451 | printk(KERN_ERR PFX "vdc_tx_trigger() failure, err=%d\n", err); | ||
| 452 | } else { | ||
| 453 | port->req_id++; | ||
| 454 | dr->prod = (dr->prod + 1) & (VDC_TX_RING_SIZE - 1); | ||
| 455 | } | ||
| 456 | out: | ||
| 457 | |||
| 458 | return err; | ||
| 459 | } | ||
| 460 | |||
| 461 | static void do_vdc_request(request_queue_t *q) | ||
| 462 | { | ||
| 463 | while (1) { | ||
| 464 | struct request *req = elv_next_request(q); | ||
| 465 | |||
| 466 | if (!req) | ||
| 467 | break; | ||
| 468 | |||
| 469 | blkdev_dequeue_request(req); | ||
| 470 | if (__send_request(req) < 0) | ||
| 471 | vdc_end_request(req, 0, req->hard_nr_sectors); | ||
| 472 | } | ||
| 473 | } | ||
| 474 | |||
| 475 | static int generic_request(struct vdc_port *port, u8 op, void *buf, int len) | ||
| 476 | { | ||
| 477 | struct vio_dring_state *dr; | ||
| 478 | struct vio_completion comp; | ||
| 479 | struct vio_disk_desc *desc; | ||
| 480 | unsigned int map_perm; | ||
| 481 | unsigned long flags; | ||
| 482 | int op_len, err; | ||
| 483 | void *req_buf; | ||
| 484 | |||
| 485 | if (!(((u64)1 << ((u64)op - 1)) & port->operations)) | ||
| 486 | return -EOPNOTSUPP; | ||
| 487 | |||
| 488 | switch (op) { | ||
| 489 | case VD_OP_BREAD: | ||
| 490 | case VD_OP_BWRITE: | ||
| 491 | default: | ||
| 492 | return -EINVAL; | ||
| 493 | |||
| 494 | case VD_OP_FLUSH: | ||
| 495 | op_len = 0; | ||
| 496 | map_perm = 0; | ||
| 497 | break; | ||
| 498 | |||
| 499 | case VD_OP_GET_WCE: | ||
| 500 | op_len = sizeof(u32); | ||
| 501 | map_perm = LDC_MAP_W; | ||
| 502 | break; | ||
| 503 | |||
| 504 | case VD_OP_SET_WCE: | ||
| 505 | op_len = sizeof(u32); | ||
| 506 | map_perm = LDC_MAP_R; | ||
| 507 | break; | ||
| 508 | |||
| 509 | case VD_OP_GET_VTOC: | ||
| 510 | op_len = sizeof(struct vio_disk_vtoc); | ||
| 511 | map_perm = LDC_MAP_W; | ||
| 512 | break; | ||
| 513 | |||
| 514 | case VD_OP_SET_VTOC: | ||
| 515 | op_len = sizeof(struct vio_disk_vtoc); | ||
| 516 | map_perm = LDC_MAP_R; | ||
| 517 | break; | ||
| 518 | |||
| 519 | case VD_OP_GET_DISKGEOM: | ||
| 520 | op_len = sizeof(struct vio_disk_geom); | ||
| 521 | map_perm = LDC_MAP_W; | ||
| 522 | break; | ||
| 523 | |||
| 524 | case VD_OP_SET_DISKGEOM: | ||
| 525 | op_len = sizeof(struct vio_disk_geom); | ||
| 526 | map_perm = LDC_MAP_R; | ||
| 527 | break; | ||
| 528 | |||
| 529 | case VD_OP_SCSICMD: | ||
| 530 | op_len = 16; | ||
| 531 | map_perm = LDC_MAP_RW; | ||
| 532 | break; | ||
| 533 | |||
| 534 | case VD_OP_GET_DEVID: | ||
| 535 | op_len = sizeof(struct vio_disk_devid); | ||
| 536 | map_perm = LDC_MAP_W; | ||
| 537 | break; | ||
| 538 | |||
| 539 | case VD_OP_GET_EFI: | ||
| 540 | case VD_OP_SET_EFI: | ||
| 541 | return -EOPNOTSUPP; | ||
| 542 | break; | ||
| 543 | }; | ||
| 544 | |||
| 545 | map_perm |= LDC_MAP_SHADOW | LDC_MAP_DIRECT | LDC_MAP_IO; | ||
| 546 | |||
| 547 | op_len = (op_len + 7) & ~7; | ||
| 548 | req_buf = kzalloc(op_len, GFP_KERNEL); | ||
| 549 | if (!req_buf) | ||
| 550 | return -ENOMEM; | ||
| 551 | |||
| 552 | if (len > op_len) | ||
| 553 | len = op_len; | ||
| 554 | |||
| 555 | if (map_perm & LDC_MAP_R) | ||
| 556 | memcpy(req_buf, buf, len); | ||
| 557 | |||
| 558 | spin_lock_irqsave(&port->vio.lock, flags); | ||
| 559 | |||
| 560 | dr = &port->vio.drings[VIO_DRIVER_TX_RING]; | ||
| 561 | |||
| 562 | /* XXX If we want to use this code generically we have to | ||
| 563 | * XXX handle TX ring exhaustion etc. | ||
| 564 | */ | ||
| 565 | desc = vio_dring_cur(dr); | ||
| 566 | |||
| 567 | err = ldc_map_single(port->vio.lp, req_buf, op_len, | ||
| 568 | desc->cookies, port->ring_cookies, | ||
| 569 | map_perm); | ||
| 570 | if (err < 0) { | ||
| 571 | spin_unlock_irqrestore(&port->vio.lock, flags); | ||
| 572 | kfree(req_buf); | ||
| 573 | return err; | ||
| 574 | } | ||
| 575 | |||
| 576 | init_completion(&comp.com); | ||
| 577 | comp.waiting_for = WAITING_FOR_GEN_CMD; | ||
| 578 | port->vio.cmp = ∁ | ||
| 579 | |||
| 580 | desc->hdr.ack = VIO_ACK_ENABLE; | ||
| 581 | desc->req_id = port->req_id; | ||
| 582 | desc->operation = op; | ||
| 583 | desc->slice = 0; | ||
| 584 | desc->status = ~0; | ||
| 585 | desc->offset = 0; | ||
| 586 | desc->size = op_len; | ||
| 587 | desc->ncookies = err; | ||
| 588 | |||
| 589 | /* This has to be a non-SMP write barrier because we are writing | ||
| 590 | * to memory which is shared with the peer LDOM. | ||
| 591 | */ | ||
| 592 | wmb(); | ||
| 593 | desc->hdr.state = VIO_DESC_READY; | ||
| 594 | |||
| 595 | err = __vdc_tx_trigger(port); | ||
| 596 | if (err >= 0) { | ||
| 597 | port->req_id++; | ||
| 598 | dr->prod = (dr->prod + 1) & (VDC_TX_RING_SIZE - 1); | ||
| 599 | spin_unlock_irqrestore(&port->vio.lock, flags); | ||
| 600 | |||
| 601 | wait_for_completion(&comp.com); | ||
| 602 | err = comp.err; | ||
| 603 | } else { | ||
| 604 | port->vio.cmp = NULL; | ||
| 605 | spin_unlock_irqrestore(&port->vio.lock, flags); | ||
| 606 | } | ||
| 607 | |||
| 608 | if (map_perm & LDC_MAP_W) | ||
| 609 | memcpy(buf, req_buf, len); | ||
| 610 | |||
| 611 | kfree(req_buf); | ||
| 612 | |||
| 613 | return err; | ||
| 614 | } | ||
| 615 | |||
| 616 | static int __devinit vdc_alloc_tx_ring(struct vdc_port *port) | ||
| 617 | { | ||
| 618 | struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; | ||
| 619 | unsigned long len, entry_size; | ||
| 620 | int ncookies; | ||
| 621 | void *dring; | ||
| 622 | |||
| 623 | entry_size = sizeof(struct vio_disk_desc) + | ||
| 624 | (sizeof(struct ldc_trans_cookie) * port->ring_cookies); | ||
| 625 | len = (VDC_TX_RING_SIZE * entry_size); | ||
| 626 | |||
| 627 | ncookies = VIO_MAX_RING_COOKIES; | ||
| 628 | dring = ldc_alloc_exp_dring(port->vio.lp, len, | ||
| 629 | dr->cookies, &ncookies, | ||
| 630 | (LDC_MAP_SHADOW | | ||
| 631 | LDC_MAP_DIRECT | | ||
| 632 | LDC_MAP_RW)); | ||
| 633 | if (IS_ERR(dring)) | ||
| 634 | return PTR_ERR(dring); | ||
| 635 | |||
| 636 | dr->base = dring; | ||
| 637 | dr->entry_size = entry_size; | ||
| 638 | dr->num_entries = VDC_TX_RING_SIZE; | ||
| 639 | dr->prod = dr->cons = 0; | ||
| 640 | dr->pending = VDC_TX_RING_SIZE; | ||
| 641 | dr->ncookies = ncookies; | ||
| 642 | |||
| 643 | return 0; | ||
| 644 | } | ||
| 645 | |||
| 646 | static void vdc_free_tx_ring(struct vdc_port *port) | ||
| 647 | { | ||
| 648 | struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; | ||
| 649 | |||
| 650 | if (dr->base) { | ||
| 651 | ldc_free_exp_dring(port->vio.lp, dr->base, | ||
| 652 | (dr->entry_size * dr->num_entries), | ||
| 653 | dr->cookies, dr->ncookies); | ||
| 654 | dr->base = NULL; | ||
| 655 | dr->entry_size = 0; | ||
| 656 | dr->num_entries = 0; | ||
| 657 | dr->pending = 0; | ||
| 658 | dr->ncookies = 0; | ||
| 659 | } | ||
| 660 | } | ||
| 661 | |||
| 662 | static int probe_disk(struct vdc_port *port) | ||
| 663 | { | ||
| 664 | struct vio_completion comp; | ||
| 665 | struct request_queue *q; | ||
| 666 | struct gendisk *g; | ||
| 667 | int err; | ||
| 668 | |||
| 669 | init_completion(&comp.com); | ||
| 670 | comp.err = 0; | ||
| 671 | comp.waiting_for = WAITING_FOR_LINK_UP; | ||
| 672 | port->vio.cmp = ∁ | ||
| 673 | |||
| 674 | vio_port_up(&port->vio); | ||
| 675 | |||
| 676 | wait_for_completion(&comp.com); | ||
| 677 | if (comp.err) | ||
| 678 | return comp.err; | ||
| 679 | |||
| 680 | err = generic_request(port, VD_OP_GET_VTOC, | ||
| 681 | &port->label, sizeof(port->label)); | ||
| 682 | if (err < 0) { | ||
| 683 | printk(KERN_ERR PFX "VD_OP_GET_VTOC returns error %d\n", err); | ||
| 684 | return err; | ||
| 685 | } | ||
| 686 | |||
| 687 | err = generic_request(port, VD_OP_GET_DISKGEOM, | ||
| 688 | &port->geom, sizeof(port->geom)); | ||
| 689 | if (err < 0) { | ||
| 690 | printk(KERN_ERR PFX "VD_OP_GET_DISKGEOM returns " | ||
| 691 | "error %d\n", err); | ||
| 692 | return err; | ||
| 693 | } | ||
| 694 | |||
| 695 | port->vdisk_size = ((u64)port->geom.num_cyl * | ||
| 696 | (u64)port->geom.num_hd * | ||
| 697 | (u64)port->geom.num_sec); | ||
| 698 | |||
| 699 | q = blk_init_queue(do_vdc_request, &port->vio.lock); | ||
| 700 | if (!q) { | ||
| 701 | printk(KERN_ERR PFX "%s: Could not allocate queue.\n", | ||
| 702 | port->vio.name); | ||
| 703 | return -ENOMEM; | ||
| 704 | } | ||
| 705 | g = alloc_disk(1 << PARTITION_SHIFT); | ||
| 706 | if (!g) { | ||
| 707 | printk(KERN_ERR PFX "%s: Could not allocate gendisk.\n", | ||
| 708 | port->vio.name); | ||
| 709 | blk_cleanup_queue(q); | ||
| 710 | return -ENOMEM; | ||
| 711 | } | ||
| 712 | |||
| 713 | port->disk = g; | ||
| 714 | |||
| 715 | blk_queue_max_hw_segments(q, port->ring_cookies); | ||
| 716 | blk_queue_max_phys_segments(q, port->ring_cookies); | ||
| 717 | blk_queue_max_sectors(q, port->max_xfer_size); | ||
| 718 | g->major = vdc_major; | ||
| 719 | g->first_minor = port->dev_no << PARTITION_SHIFT; | ||
| 720 | strcpy(g->disk_name, port->disk_name); | ||
| 721 | |||
| 722 | g->fops = &vdc_fops; | ||
| 723 | g->queue = q; | ||
| 724 | g->private_data = port; | ||
| 725 | g->driverfs_dev = &port->vio.vdev->dev; | ||
| 726 | |||
| 727 | set_capacity(g, port->vdisk_size); | ||
| 728 | |||
| 729 | printk(KERN_INFO PFX "%s: %u sectors (%u MB)\n", | ||
| 730 | g->disk_name, | ||
| 731 | port->vdisk_size, (port->vdisk_size >> (20 - 9))); | ||
| 732 | |||
| 733 | add_disk(g); | ||
| 734 | |||
| 735 | return 0; | ||
| 736 | } | ||
| 737 | |||
| 738 | static struct ldc_channel_config vdc_ldc_cfg = { | ||
| 739 | .event = vdc_event, | ||
| 740 | .mtu = 64, | ||
| 741 | .mode = LDC_MODE_UNRELIABLE, | ||
| 742 | }; | ||
| 743 | |||
| 744 | static struct vio_driver_ops vdc_vio_ops = { | ||
| 745 | .send_attr = vdc_send_attr, | ||
| 746 | .handle_attr = vdc_handle_attr, | ||
| 747 | .handshake_complete = vdc_handshake_complete, | ||
| 748 | }; | ||
| 749 | |||
| 750 | static int __devinit vdc_port_probe(struct vio_dev *vdev, | ||
| 751 | const struct vio_device_id *id) | ||
| 752 | { | ||
| 753 | struct mdesc_handle *hp; | ||
| 754 | struct vdc_port *port; | ||
| 755 | unsigned long flags; | ||
| 756 | struct vdc *vp; | ||
| 757 | const u64 *port_id; | ||
| 758 | int err; | ||
| 759 | |||
| 760 | vp = dev_get_drvdata(vdev->dev.parent); | ||
| 761 | if (!vp) { | ||
| 762 | printk(KERN_ERR PFX "Cannot find port parent vdc.\n"); | ||
| 763 | return -ENODEV; | ||
| 764 | } | ||
| 765 | |||
| 766 | hp = mdesc_grab(); | ||
| 767 | |||
| 768 | port_id = mdesc_get_property(hp, vdev->mp, "id", NULL); | ||
| 769 | err = -ENODEV; | ||
| 770 | if (!port_id) { | ||
| 771 | printk(KERN_ERR PFX "Port lacks id property.\n"); | ||
| 772 | goto err_out_release_mdesc; | ||
| 773 | } | ||
| 774 | if ((*port_id << PARTITION_SHIFT) & ~(u64)MINORMASK) { | ||
| 775 | printk(KERN_ERR PFX "Port id [%lu] too large.\n", *port_id); | ||
| 776 | goto err_out_release_mdesc; | ||
| 777 | } | ||
| 778 | |||
| 779 | port = kzalloc(sizeof(*port), GFP_KERNEL); | ||
| 780 | err = -ENOMEM; | ||
| 781 | if (!port) { | ||
| 782 | printk(KERN_ERR PFX "Cannot allocate vdc_port.\n"); | ||
| 783 | goto err_out_release_mdesc; | ||
| 784 | } | ||
| 785 | |||
| 786 | port->vp = vp; | ||
| 787 | port->dev_no = *port_id; | ||
| 788 | |||
| 789 | if (port->dev_no >= 26) | ||
| 790 | snprintf(port->disk_name, sizeof(port->disk_name), | ||
| 791 | VDCBLK_NAME "%c%c", | ||
| 792 | 'a' + (port->dev_no / 26) - 1, | ||
| 793 | 'a' + (port->dev_no % 26)); | ||
| 794 | else | ||
| 795 | snprintf(port->disk_name, sizeof(port->disk_name), | ||
| 796 | VDCBLK_NAME "%c", 'a' + (port->dev_no % 26)); | ||
| 797 | |||
| 798 | err = vio_driver_init(&port->vio, vdev, VDEV_DISK, | ||
| 799 | vdc_versions, ARRAY_SIZE(vdc_versions), | ||
| 800 | &vdc_vio_ops, port->disk_name); | ||
| 801 | if (err) | ||
| 802 | goto err_out_free_port; | ||
| 803 | |||
| 804 | port->vdisk_block_size = 512; | ||
| 805 | port->max_xfer_size = ((128 * 1024) / port->vdisk_block_size); | ||
| 806 | port->ring_cookies = ((port->max_xfer_size * | ||
| 807 | port->vdisk_block_size) / PAGE_SIZE) + 2; | ||
| 808 | |||
| 809 | err = vio_ldc_alloc(&port->vio, &vdc_ldc_cfg, port); | ||
| 810 | if (err) | ||
| 811 | goto err_out_free_port; | ||
| 812 | |||
| 813 | err = vdc_alloc_tx_ring(port); | ||
| 814 | if (err) | ||
| 815 | goto err_out_free_ldc; | ||
| 816 | |||
| 817 | err = probe_disk(port); | ||
| 818 | if (err) | ||
| 819 | goto err_out_free_tx_ring; | ||
| 820 | |||
| 821 | INIT_LIST_HEAD(&port->list); | ||
| 822 | |||
| 823 | spin_lock_irqsave(&vp->lock, flags); | ||
| 824 | list_add(&port->list, &vp->port_list); | ||
| 825 | spin_unlock_irqrestore(&vp->lock, flags); | ||
| 826 | |||
| 827 | dev_set_drvdata(&vdev->dev, port); | ||
| 828 | |||
| 829 | mdesc_release(hp); | ||
| 830 | |||
| 831 | return 0; | ||
| 832 | |||
| 833 | err_out_free_tx_ring: | ||
| 834 | vdc_free_tx_ring(port); | ||
| 835 | |||
| 836 | err_out_free_ldc: | ||
| 837 | vio_ldc_free(&port->vio); | ||
| 838 | |||
| 839 | err_out_free_port: | ||
| 840 | kfree(port); | ||
| 841 | |||
| 842 | err_out_release_mdesc: | ||
| 843 | mdesc_release(hp); | ||
| 844 | return err; | ||
| 845 | } | ||
| 846 | |||
| 847 | static int vdc_port_remove(struct vio_dev *vdev) | ||
| 848 | { | ||
| 849 | struct vdc_port *port = dev_get_drvdata(&vdev->dev); | ||
| 850 | |||
| 851 | if (port) { | ||
| 852 | del_timer_sync(&port->vio.timer); | ||
| 853 | |||
| 854 | vdc_free_tx_ring(port); | ||
| 855 | vio_ldc_free(&port->vio); | ||
| 856 | |||
| 857 | dev_set_drvdata(&vdev->dev, NULL); | ||
| 858 | |||
| 859 | kfree(port); | ||
| 860 | } | ||
| 861 | return 0; | ||
| 862 | } | ||
| 863 | |||
| 864 | static struct vio_device_id vdc_port_match[] = { | ||
| 865 | { | ||
| 866 | .type = "vdc-port", | ||
| 867 | }, | ||
| 868 | {}, | ||
| 869 | }; | ||
| 870 | MODULE_DEVICE_TABLE(vio, vdc_match); | ||
| 871 | |||
| 872 | static struct vio_driver vdc_port_driver = { | ||
| 873 | .id_table = vdc_port_match, | ||
| 874 | .probe = vdc_port_probe, | ||
| 875 | .remove = vdc_port_remove, | ||
| 876 | .driver = { | ||
| 877 | .name = "vdc_port", | ||
| 878 | .owner = THIS_MODULE, | ||
| 879 | } | ||
| 880 | }; | ||
| 881 | |||
| 882 | static int __devinit vdc_probe(struct vio_dev *vdev, | ||
| 883 | const struct vio_device_id *id) | ||
| 884 | { | ||
| 885 | static int vdc_version_printed; | ||
| 886 | struct vdc *vp; | ||
| 887 | |||
| 888 | if (vdc_version_printed++ == 0) | ||
| 889 | printk(KERN_INFO "%s", version); | ||
| 890 | |||
| 891 | vp = kzalloc(sizeof(struct vdc), GFP_KERNEL); | ||
| 892 | if (!vp) | ||
| 893 | return -ENOMEM; | ||
| 894 | |||
| 895 | spin_lock_init(&vp->lock); | ||
| 896 | vp->dev = vdev; | ||
| 897 | INIT_LIST_HEAD(&vp->port_list); | ||
| 898 | |||
| 899 | dev_set_drvdata(&vdev->dev, vp); | ||
| 900 | |||
| 901 | return 0; | ||
| 902 | } | ||
| 903 | |||
| 904 | static int vdc_remove(struct vio_dev *vdev) | ||
| 905 | { | ||
| 906 | |||
| 907 | struct vdc *vp = dev_get_drvdata(&vdev->dev); | ||
| 908 | |||
| 909 | if (vp) { | ||
| 910 | kfree(vp); | ||
| 911 | dev_set_drvdata(&vdev->dev, NULL); | ||
| 912 | } | ||
| 913 | return 0; | ||
| 914 | } | ||
| 915 | |||
| 916 | static struct vio_device_id vdc_match[] = { | ||
| 917 | { | ||
| 918 | .type = "block", | ||
| 919 | }, | ||
| 920 | {}, | ||
| 921 | }; | ||
| 922 | MODULE_DEVICE_TABLE(vio, vdc_match); | ||
| 923 | |||
| 924 | static struct vio_driver vdc_driver = { | ||
| 925 | .id_table = vdc_match, | ||
| 926 | .probe = vdc_probe, | ||
| 927 | .remove = vdc_remove, | ||
| 928 | .driver = { | ||
| 929 | .name = "vdc", | ||
| 930 | .owner = THIS_MODULE, | ||
| 931 | } | ||
| 932 | }; | ||
| 933 | |||
| 934 | static int __init vdc_init(void) | ||
| 935 | { | ||
| 936 | int err; | ||
| 937 | |||
| 938 | err = register_blkdev(0, VDCBLK_NAME); | ||
| 939 | if (err < 0) | ||
| 940 | goto out_err; | ||
| 941 | |||
| 942 | vdc_major = err; | ||
| 943 | err = vio_register_driver(&vdc_driver); | ||
| 944 | if (err) | ||
| 945 | goto out_unregister_blkdev; | ||
| 946 | |||
| 947 | err = vio_register_driver(&vdc_port_driver); | ||
| 948 | if (err) | ||
| 949 | goto out_unregister_vdc; | ||
| 950 | |||
| 951 | return 0; | ||
| 952 | |||
| 953 | out_unregister_vdc: | ||
| 954 | vio_unregister_driver(&vdc_driver); | ||
| 955 | |||
| 956 | out_unregister_blkdev: | ||
| 957 | unregister_blkdev(vdc_major, VDCBLK_NAME); | ||
| 958 | vdc_major = 0; | ||
| 959 | |||
| 960 | out_err: | ||
| 961 | return err; | ||
| 962 | } | ||
| 963 | |||
| 964 | static void __exit vdc_exit(void) | ||
| 965 | { | ||
| 966 | vio_unregister_driver(&vdc_port_driver); | ||
| 967 | vio_unregister_driver(&vdc_driver); | ||
| 968 | unregister_blkdev(vdc_major, VDCBLK_NAME); | ||
| 969 | } | ||
| 970 | |||
| 971 | module_init(vdc_init); | ||
| 972 | module_exit(vdc_exit); | ||
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index d17d64eb7065..7903f9c7839e 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig | |||
| @@ -604,6 +604,12 @@ config CASSINI | |||
| 604 | Support for the Sun Cassini chip, aka Sun GigaSwift Ethernet. See also | 604 | Support for the Sun Cassini chip, aka Sun GigaSwift Ethernet. See also |
| 605 | <http://www.sun.com/products-n-solutions/hardware/docs/pdf/817-4341-10.pdf> | 605 | <http://www.sun.com/products-n-solutions/hardware/docs/pdf/817-4341-10.pdf> |
| 606 | 606 | ||
| 607 | config SUNVNET | ||
| 608 | tristate "Sun Virtual Network support" | ||
| 609 | depends on SUN_LDOMS | ||
| 610 | help | ||
| 611 | Support for virtual network devices under Sun Logical Domains. | ||
| 612 | |||
| 607 | config NET_VENDOR_3COM | 613 | config NET_VENDOR_3COM |
| 608 | bool "3COM cards" | 614 | bool "3COM cards" |
| 609 | depends on ISA || EISA || MCA || PCI | 615 | depends on ISA || EISA || MCA || PCI |
diff --git a/drivers/net/Makefile b/drivers/net/Makefile index c26b8674213c..b95b1b237a26 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile | |||
| @@ -34,6 +34,7 @@ obj-$(CONFIG_SUNBMAC) += sunbmac.o | |||
| 34 | obj-$(CONFIG_MYRI_SBUS) += myri_sbus.o | 34 | obj-$(CONFIG_MYRI_SBUS) += myri_sbus.o |
| 35 | obj-$(CONFIG_SUNGEM) += sungem.o sungem_phy.o | 35 | obj-$(CONFIG_SUNGEM) += sungem.o sungem_phy.o |
| 36 | obj-$(CONFIG_CASSINI) += cassini.o | 36 | obj-$(CONFIG_CASSINI) += cassini.o |
| 37 | obj-$(CONFIG_SUNVNET) += sunvnet.o | ||
| 37 | 38 | ||
| 38 | obj-$(CONFIG_MACE) += mace.o | 39 | obj-$(CONFIG_MACE) += mace.o |
| 39 | obj-$(CONFIG_BMAC) += bmac.o | 40 | obj-$(CONFIG_BMAC) += bmac.o |
diff --git a/drivers/net/sunvnet.c b/drivers/net/sunvnet.c new file mode 100644 index 000000000000..8a667c13faef --- /dev/null +++ b/drivers/net/sunvnet.c | |||
| @@ -0,0 +1,1164 @@ | |||
| 1 | /* sunvnet.c: Sun LDOM Virtual Network Driver. | ||
| 2 | * | ||
| 3 | * Copyright (C) 2007 David S. Miller <davem@davemloft.net> | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <linux/module.h> | ||
| 7 | #include <linux/kernel.h> | ||
| 8 | #include <linux/types.h> | ||
| 9 | #include <linux/slab.h> | ||
| 10 | #include <linux/delay.h> | ||
| 11 | #include <linux/init.h> | ||
| 12 | #include <linux/netdevice.h> | ||
| 13 | #include <linux/ethtool.h> | ||
| 14 | #include <linux/etherdevice.h> | ||
| 15 | |||
| 16 | #include <asm/vio.h> | ||
| 17 | #include <asm/ldc.h> | ||
| 18 | |||
| 19 | #include "sunvnet.h" | ||
| 20 | |||
| 21 | #define DRV_MODULE_NAME "sunvnet" | ||
| 22 | #define PFX DRV_MODULE_NAME ": " | ||
| 23 | #define DRV_MODULE_VERSION "1.0" | ||
| 24 | #define DRV_MODULE_RELDATE "June 25, 2007" | ||
| 25 | |||
| 26 | static char version[] __devinitdata = | ||
| 27 | DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; | ||
| 28 | MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); | ||
| 29 | MODULE_DESCRIPTION("Sun LDOM virtual network driver"); | ||
| 30 | MODULE_LICENSE("GPL"); | ||
| 31 | MODULE_VERSION(DRV_MODULE_VERSION); | ||
| 32 | |||
| 33 | /* Ordered from largest major to lowest */ | ||
| 34 | static struct vio_version vnet_versions[] = { | ||
| 35 | { .major = 1, .minor = 0 }, | ||
| 36 | }; | ||
| 37 | |||
| 38 | static inline u32 vnet_tx_dring_avail(struct vio_dring_state *dr) | ||
| 39 | { | ||
| 40 | return vio_dring_avail(dr, VNET_TX_RING_SIZE); | ||
| 41 | } | ||
| 42 | |||
| 43 | static int vnet_handle_unknown(struct vnet_port *port, void *arg) | ||
| 44 | { | ||
| 45 | struct vio_msg_tag *pkt = arg; | ||
| 46 | |||
| 47 | printk(KERN_ERR PFX "Received unknown msg [%02x:%02x:%04x:%08x]\n", | ||
| 48 | pkt->type, pkt->stype, pkt->stype_env, pkt->sid); | ||
| 49 | printk(KERN_ERR PFX "Resetting connection.\n"); | ||
| 50 | |||
| 51 | ldc_disconnect(port->vio.lp); | ||
| 52 | |||
| 53 | return -ECONNRESET; | ||
| 54 | } | ||
| 55 | |||
| 56 | static int vnet_send_attr(struct vio_driver_state *vio) | ||
| 57 | { | ||
| 58 | struct vnet_port *port = to_vnet_port(vio); | ||
| 59 | struct net_device *dev = port->vp->dev; | ||
| 60 | struct vio_net_attr_info pkt; | ||
| 61 | int i; | ||
| 62 | |||
| 63 | memset(&pkt, 0, sizeof(pkt)); | ||
| 64 | pkt.tag.type = VIO_TYPE_CTRL; | ||
| 65 | pkt.tag.stype = VIO_SUBTYPE_INFO; | ||
| 66 | pkt.tag.stype_env = VIO_ATTR_INFO; | ||
| 67 | pkt.tag.sid = vio_send_sid(vio); | ||
| 68 | pkt.xfer_mode = VIO_DRING_MODE; | ||
| 69 | pkt.addr_type = VNET_ADDR_ETHERMAC; | ||
| 70 | pkt.ack_freq = 0; | ||
| 71 | for (i = 0; i < 6; i++) | ||
| 72 | pkt.addr |= (u64)dev->dev_addr[i] << ((5 - i) * 8); | ||
| 73 | pkt.mtu = ETH_FRAME_LEN; | ||
| 74 | |||
| 75 | viodbg(HS, "SEND NET ATTR xmode[0x%x] atype[0x%x] addr[%llx] " | ||
| 76 | "ackfreq[%u] mtu[%llu]\n", | ||
| 77 | pkt.xfer_mode, pkt.addr_type, | ||
| 78 | (unsigned long long) pkt.addr, | ||
| 79 | pkt.ack_freq, | ||
| 80 | (unsigned long long) pkt.mtu); | ||
| 81 | |||
| 82 | return vio_ldc_send(vio, &pkt, sizeof(pkt)); | ||
| 83 | } | ||
| 84 | |||
| 85 | static int handle_attr_info(struct vio_driver_state *vio, | ||
| 86 | struct vio_net_attr_info *pkt) | ||
| 87 | { | ||
| 88 | viodbg(HS, "GOT NET ATTR INFO xmode[0x%x] atype[0x%x] addr[%llx] " | ||
| 89 | "ackfreq[%u] mtu[%llu]\n", | ||
| 90 | pkt->xfer_mode, pkt->addr_type, | ||
| 91 | (unsigned long long) pkt->addr, | ||
| 92 | pkt->ack_freq, | ||
| 93 | (unsigned long long) pkt->mtu); | ||
| 94 | |||
| 95 | pkt->tag.sid = vio_send_sid(vio); | ||
| 96 | |||
| 97 | if (pkt->xfer_mode != VIO_DRING_MODE || | ||
| 98 | pkt->addr_type != VNET_ADDR_ETHERMAC || | ||
| 99 | pkt->mtu != ETH_FRAME_LEN) { | ||
| 100 | viodbg(HS, "SEND NET ATTR NACK\n"); | ||
| 101 | |||
| 102 | pkt->tag.stype = VIO_SUBTYPE_NACK; | ||
| 103 | |||
| 104 | (void) vio_ldc_send(vio, pkt, sizeof(*pkt)); | ||
| 105 | |||
| 106 | return -ECONNRESET; | ||
| 107 | } else { | ||
| 108 | viodbg(HS, "SEND NET ATTR ACK\n"); | ||
| 109 | |||
| 110 | pkt->tag.stype = VIO_SUBTYPE_ACK; | ||
| 111 | |||
| 112 | return vio_ldc_send(vio, pkt, sizeof(*pkt)); | ||
| 113 | } | ||
| 114 | |||
| 115 | } | ||
| 116 | |||
| 117 | static int handle_attr_ack(struct vio_driver_state *vio, | ||
| 118 | struct vio_net_attr_info *pkt) | ||
| 119 | { | ||
| 120 | viodbg(HS, "GOT NET ATTR ACK\n"); | ||
| 121 | |||
| 122 | return 0; | ||
| 123 | } | ||
| 124 | |||
| 125 | static int handle_attr_nack(struct vio_driver_state *vio, | ||
| 126 | struct vio_net_attr_info *pkt) | ||
| 127 | { | ||
| 128 | viodbg(HS, "GOT NET ATTR NACK\n"); | ||
| 129 | |||
| 130 | return -ECONNRESET; | ||
| 131 | } | ||
| 132 | |||
| 133 | static int vnet_handle_attr(struct vio_driver_state *vio, void *arg) | ||
| 134 | { | ||
| 135 | struct vio_net_attr_info *pkt = arg; | ||
| 136 | |||
| 137 | switch (pkt->tag.stype) { | ||
| 138 | case VIO_SUBTYPE_INFO: | ||
| 139 | return handle_attr_info(vio, pkt); | ||
| 140 | |||
| 141 | case VIO_SUBTYPE_ACK: | ||
| 142 | return handle_attr_ack(vio, pkt); | ||
| 143 | |||
| 144 | case VIO_SUBTYPE_NACK: | ||
| 145 | return handle_attr_nack(vio, pkt); | ||
| 146 | |||
| 147 | default: | ||
| 148 | return -ECONNRESET; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | static void vnet_handshake_complete(struct vio_driver_state *vio) | ||
| 153 | { | ||
| 154 | struct vio_dring_state *dr; | ||
| 155 | |||
| 156 | dr = &vio->drings[VIO_DRIVER_RX_RING]; | ||
| 157 | dr->snd_nxt = dr->rcv_nxt = 1; | ||
| 158 | |||
| 159 | dr = &vio->drings[VIO_DRIVER_TX_RING]; | ||
| 160 | dr->snd_nxt = dr->rcv_nxt = 1; | ||
| 161 | } | ||
| 162 | |||
| 163 | /* The hypervisor interface that implements copying to/from imported | ||
| 164 | * memory from another domain requires that copies are done to 8-byte | ||
| 165 | * aligned buffers, and that the lengths of such copies are also 8-byte | ||
| 166 | * multiples. | ||
| 167 | * | ||
| 168 | * So we align skb->data to an 8-byte multiple and pad-out the data | ||
| 169 | * area so we can round the copy length up to the next multiple of | ||
| 170 | * 8 for the copy. | ||
| 171 | * | ||
| 172 | * The transmitter puts the actual start of the packet 6 bytes into | ||
| 173 | * the buffer it sends over, so that the IP headers after the ethernet | ||
| 174 | * header are aligned properly. These 6 bytes are not in the descriptor | ||
| 175 | * length, they are simply implied. This offset is represented using | ||
| 176 | * the VNET_PACKET_SKIP macro. | ||
| 177 | */ | ||
| 178 | static struct sk_buff *alloc_and_align_skb(struct net_device *dev, | ||
| 179 | unsigned int len) | ||
| 180 | { | ||
| 181 | struct sk_buff *skb = netdev_alloc_skb(dev, len+VNET_PACKET_SKIP+8+8); | ||
| 182 | unsigned long addr, off; | ||
| 183 | |||
| 184 | if (unlikely(!skb)) | ||
| 185 | return NULL; | ||
| 186 | |||
| 187 | addr = (unsigned long) skb->data; | ||
| 188 | off = ((addr + 7UL) & ~7UL) - addr; | ||
| 189 | if (off) | ||
| 190 | skb_reserve(skb, off); | ||
| 191 | |||
| 192 | return skb; | ||
| 193 | } | ||
| 194 | |||
| 195 | static int vnet_rx_one(struct vnet_port *port, unsigned int len, | ||
| 196 | struct ldc_trans_cookie *cookies, int ncookies) | ||
| 197 | { | ||
| 198 | struct net_device *dev = port->vp->dev; | ||
| 199 | unsigned int copy_len; | ||
| 200 | struct sk_buff *skb; | ||
| 201 | int err; | ||
| 202 | |||
| 203 | err = -EMSGSIZE; | ||
| 204 | if (unlikely(len < ETH_ZLEN || len > ETH_FRAME_LEN)) { | ||
| 205 | dev->stats.rx_length_errors++; | ||
| 206 | goto out_dropped; | ||
| 207 | } | ||
| 208 | |||
| 209 | skb = alloc_and_align_skb(dev, len); | ||
| 210 | err = -ENOMEM; | ||
| 211 | if (unlikely(!skb)) { | ||
| 212 | dev->stats.rx_missed_errors++; | ||
| 213 | goto out_dropped; | ||
| 214 | } | ||
| 215 | |||
| 216 | copy_len = (len + VNET_PACKET_SKIP + 7U) & ~7U; | ||
| 217 | skb_put(skb, copy_len); | ||
| 218 | err = ldc_copy(port->vio.lp, LDC_COPY_IN, | ||
| 219 | skb->data, copy_len, 0, | ||
| 220 | cookies, ncookies); | ||
| 221 | if (unlikely(err < 0)) { | ||
| 222 | dev->stats.rx_frame_errors++; | ||
| 223 | goto out_free_skb; | ||
| 224 | } | ||
| 225 | |||
| 226 | skb_pull(skb, VNET_PACKET_SKIP); | ||
| 227 | skb_trim(skb, len); | ||
| 228 | skb->protocol = eth_type_trans(skb, dev); | ||
| 229 | |||
| 230 | dev->stats.rx_packets++; | ||
| 231 | dev->stats.rx_bytes += len; | ||
| 232 | |||
| 233 | netif_rx(skb); | ||
| 234 | |||
| 235 | return 0; | ||
| 236 | |||
| 237 | out_free_skb: | ||
| 238 | kfree_skb(skb); | ||
| 239 | |||
| 240 | out_dropped: | ||
| 241 | dev->stats.rx_dropped++; | ||
| 242 | return err; | ||
| 243 | } | ||
| 244 | |||
| 245 | static int vnet_send_ack(struct vnet_port *port, struct vio_dring_state *dr, | ||
| 246 | u32 start, u32 end, u8 vio_dring_state) | ||
| 247 | { | ||
| 248 | struct vio_dring_data hdr = { | ||
| 249 | .tag = { | ||
| 250 | .type = VIO_TYPE_DATA, | ||
| 251 | .stype = VIO_SUBTYPE_ACK, | ||
| 252 | .stype_env = VIO_DRING_DATA, | ||
| 253 | .sid = vio_send_sid(&port->vio), | ||
| 254 | }, | ||
| 255 | .dring_ident = dr->ident, | ||
| 256 | .start_idx = start, | ||
| 257 | .end_idx = end, | ||
| 258 | .state = vio_dring_state, | ||
| 259 | }; | ||
| 260 | int err, delay; | ||
| 261 | |||
| 262 | hdr.seq = dr->snd_nxt; | ||
| 263 | delay = 1; | ||
| 264 | do { | ||
| 265 | err = vio_ldc_send(&port->vio, &hdr, sizeof(hdr)); | ||
| 266 | if (err > 0) { | ||
| 267 | dr->snd_nxt++; | ||
| 268 | break; | ||
| 269 | } | ||
| 270 | udelay(delay); | ||
| 271 | if ((delay <<= 1) > 128) | ||
| 272 | delay = 128; | ||
| 273 | } while (err == -EAGAIN); | ||
| 274 | |||
| 275 | return err; | ||
| 276 | } | ||
| 277 | |||
| 278 | static u32 next_idx(u32 idx, struct vio_dring_state *dr) | ||
| 279 | { | ||
| 280 | if (++idx == dr->num_entries) | ||
| 281 | idx = 0; | ||
| 282 | return idx; | ||
| 283 | } | ||
| 284 | |||
| 285 | static u32 prev_idx(u32 idx, struct vio_dring_state *dr) | ||
| 286 | { | ||
| 287 | if (idx == 0) | ||
| 288 | idx = dr->num_entries - 1; | ||
| 289 | else | ||
| 290 | idx--; | ||
| 291 | |||
| 292 | return idx; | ||
| 293 | } | ||
| 294 | |||
| 295 | static struct vio_net_desc *get_rx_desc(struct vnet_port *port, | ||
| 296 | struct vio_dring_state *dr, | ||
| 297 | u32 index) | ||
| 298 | { | ||
| 299 | struct vio_net_desc *desc = port->vio.desc_buf; | ||
| 300 | int err; | ||
| 301 | |||
| 302 | err = ldc_get_dring_entry(port->vio.lp, desc, dr->entry_size, | ||
| 303 | (index * dr->entry_size), | ||
| 304 | dr->cookies, dr->ncookies); | ||
| 305 | if (err < 0) | ||
| 306 | return ERR_PTR(err); | ||
| 307 | |||
| 308 | return desc; | ||
| 309 | } | ||
| 310 | |||
| 311 | static int put_rx_desc(struct vnet_port *port, | ||
| 312 | struct vio_dring_state *dr, | ||
| 313 | struct vio_net_desc *desc, | ||
| 314 | u32 index) | ||
| 315 | { | ||
| 316 | int err; | ||
| 317 | |||
| 318 | err = ldc_put_dring_entry(port->vio.lp, desc, dr->entry_size, | ||
| 319 | (index * dr->entry_size), | ||
| 320 | dr->cookies, dr->ncookies); | ||
| 321 | if (err < 0) | ||
| 322 | return err; | ||
| 323 | |||
| 324 | return 0; | ||
| 325 | } | ||
| 326 | |||
| 327 | static int vnet_walk_rx_one(struct vnet_port *port, | ||
| 328 | struct vio_dring_state *dr, | ||
| 329 | u32 index, int *needs_ack) | ||
| 330 | { | ||
| 331 | struct vio_net_desc *desc = get_rx_desc(port, dr, index); | ||
| 332 | struct vio_driver_state *vio = &port->vio; | ||
| 333 | int err; | ||
| 334 | |||
| 335 | if (IS_ERR(desc)) | ||
| 336 | return PTR_ERR(desc); | ||
| 337 | |||
| 338 | viodbg(DATA, "vio_walk_rx_one desc[%02x:%02x:%08x:%08x:%lx:%lx]\n", | ||
| 339 | desc->hdr.state, desc->hdr.ack, | ||
| 340 | desc->size, desc->ncookies, | ||
| 341 | desc->cookies[0].cookie_addr, | ||
| 342 | desc->cookies[0].cookie_size); | ||
| 343 | |||
| 344 | if (desc->hdr.state != VIO_DESC_READY) | ||
| 345 | return 1; | ||
| 346 | err = vnet_rx_one(port, desc->size, desc->cookies, desc->ncookies); | ||
| 347 | if (err == -ECONNRESET) | ||
| 348 | return err; | ||
| 349 | desc->hdr.state = VIO_DESC_DONE; | ||
| 350 | err = put_rx_desc(port, dr, desc, index); | ||
| 351 | if (err < 0) | ||
| 352 | return err; | ||
| 353 | *needs_ack = desc->hdr.ack; | ||
| 354 | return 0; | ||
| 355 | } | ||
| 356 | |||
| 357 | static int vnet_walk_rx(struct vnet_port *port, struct vio_dring_state *dr, | ||
| 358 | u32 start, u32 end) | ||
| 359 | { | ||
| 360 | struct vio_driver_state *vio = &port->vio; | ||
| 361 | int ack_start = -1, ack_end = -1; | ||
| 362 | |||
| 363 | end = (end == (u32) -1) ? prev_idx(start, dr) : next_idx(end, dr); | ||
| 364 | |||
| 365 | viodbg(DATA, "vnet_walk_rx start[%08x] end[%08x]\n", start, end); | ||
| 366 | |||
| 367 | while (start != end) { | ||
| 368 | int ack = 0, err = vnet_walk_rx_one(port, dr, start, &ack); | ||
| 369 | if (err == -ECONNRESET) | ||
| 370 | return err; | ||
| 371 | if (err != 0) | ||
| 372 | break; | ||
| 373 | if (ack_start == -1) | ||
| 374 | ack_start = start; | ||
| 375 | ack_end = start; | ||
| 376 | start = next_idx(start, dr); | ||
| 377 | if (ack && start != end) { | ||
| 378 | err = vnet_send_ack(port, dr, ack_start, ack_end, | ||
| 379 | VIO_DRING_ACTIVE); | ||
| 380 | if (err == -ECONNRESET) | ||
| 381 | return err; | ||
| 382 | ack_start = -1; | ||
| 383 | } | ||
| 384 | } | ||
| 385 | if (unlikely(ack_start == -1)) | ||
| 386 | ack_start = ack_end = prev_idx(start, dr); | ||
| 387 | return vnet_send_ack(port, dr, ack_start, ack_end, VIO_DRING_STOPPED); | ||
| 388 | } | ||
| 389 | |||
| 390 | static int vnet_rx(struct vnet_port *port, void *msgbuf) | ||
| 391 | { | ||
| 392 | struct vio_dring_data *pkt = msgbuf; | ||
| 393 | struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_RX_RING]; | ||
| 394 | struct vio_driver_state *vio = &port->vio; | ||
| 395 | |||
| 396 | viodbg(DATA, "vnet_rx stype_env[%04x] seq[%016lx] rcv_nxt[%016lx]\n", | ||
| 397 | pkt->tag.stype_env, pkt->seq, dr->rcv_nxt); | ||
| 398 | |||
| 399 | if (unlikely(pkt->tag.stype_env != VIO_DRING_DATA)) | ||
| 400 | return 0; | ||
| 401 | if (unlikely(pkt->seq != dr->rcv_nxt)) { | ||
| 402 | printk(KERN_ERR PFX "RX out of sequence seq[0x%lx] " | ||
| 403 | "rcv_nxt[0x%lx]\n", pkt->seq, dr->rcv_nxt); | ||
| 404 | return 0; | ||
| 405 | } | ||
| 406 | |||
| 407 | dr->rcv_nxt++; | ||
| 408 | |||
| 409 | /* XXX Validate pkt->start_idx and pkt->end_idx XXX */ | ||
| 410 | |||
| 411 | return vnet_walk_rx(port, dr, pkt->start_idx, pkt->end_idx); | ||
| 412 | } | ||
| 413 | |||
| 414 | static int idx_is_pending(struct vio_dring_state *dr, u32 end) | ||
| 415 | { | ||
| 416 | u32 idx = dr->cons; | ||
| 417 | int found = 0; | ||
| 418 | |||
| 419 | while (idx != dr->prod) { | ||
| 420 | if (idx == end) { | ||
| 421 | found = 1; | ||
| 422 | break; | ||
| 423 | } | ||
| 424 | idx = next_idx(idx, dr); | ||
| 425 | } | ||
| 426 | return found; | ||
| 427 | } | ||
| 428 | |||
| 429 | static int vnet_ack(struct vnet_port *port, void *msgbuf) | ||
| 430 | { | ||
| 431 | struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; | ||
| 432 | struct vio_dring_data *pkt = msgbuf; | ||
| 433 | struct net_device *dev; | ||
| 434 | struct vnet *vp; | ||
| 435 | u32 end; | ||
| 436 | |||
| 437 | if (unlikely(pkt->tag.stype_env != VIO_DRING_DATA)) | ||
| 438 | return 0; | ||
| 439 | |||
| 440 | end = pkt->end_idx; | ||
| 441 | if (unlikely(!idx_is_pending(dr, end))) | ||
| 442 | return 0; | ||
| 443 | |||
| 444 | dr->cons = next_idx(end, dr); | ||
| 445 | |||
| 446 | vp = port->vp; | ||
| 447 | dev = vp->dev; | ||
| 448 | if (unlikely(netif_queue_stopped(dev) && | ||
| 449 | vnet_tx_dring_avail(dr) >= VNET_TX_WAKEUP_THRESH(dr))) | ||
| 450 | return 1; | ||
| 451 | |||
| 452 | return 0; | ||
| 453 | } | ||
| 454 | |||
| 455 | static int vnet_nack(struct vnet_port *port, void *msgbuf) | ||
| 456 | { | ||
| 457 | /* XXX just reset or similar XXX */ | ||
| 458 | return 0; | ||
| 459 | } | ||
| 460 | |||
| 461 | static void maybe_tx_wakeup(struct vnet *vp) | ||
| 462 | { | ||
| 463 | struct net_device *dev = vp->dev; | ||
| 464 | |||
| 465 | netif_tx_lock(dev); | ||
| 466 | if (likely(netif_queue_stopped(dev))) { | ||
| 467 | struct vnet_port *port; | ||
| 468 | int wake = 1; | ||
| 469 | |||
| 470 | list_for_each_entry(port, &vp->port_list, list) { | ||
| 471 | struct vio_dring_state *dr; | ||
| 472 | |||
| 473 | dr = &port->vio.drings[VIO_DRIVER_TX_RING]; | ||
| 474 | if (vnet_tx_dring_avail(dr) < | ||
| 475 | VNET_TX_WAKEUP_THRESH(dr)) { | ||
| 476 | wake = 0; | ||
| 477 | break; | ||
| 478 | } | ||
| 479 | } | ||
| 480 | if (wake) | ||
| 481 | netif_wake_queue(dev); | ||
| 482 | } | ||
| 483 | netif_tx_unlock(dev); | ||
| 484 | } | ||
| 485 | |||
| 486 | static void vnet_event(void *arg, int event) | ||
| 487 | { | ||
| 488 | struct vnet_port *port = arg; | ||
| 489 | struct vio_driver_state *vio = &port->vio; | ||
| 490 | unsigned long flags; | ||
| 491 | int tx_wakeup, err; | ||
| 492 | |||
| 493 | spin_lock_irqsave(&vio->lock, flags); | ||
| 494 | |||
| 495 | if (unlikely(event == LDC_EVENT_RESET || | ||
| 496 | event == LDC_EVENT_UP)) { | ||
| 497 | vio_link_state_change(vio, event); | ||
| 498 | spin_unlock_irqrestore(&vio->lock, flags); | ||
| 499 | |||
| 500 | return; | ||
| 501 | } | ||
| 502 | |||
| 503 | if (unlikely(event != LDC_EVENT_DATA_READY)) { | ||
| 504 | printk(KERN_WARNING PFX "Unexpected LDC event %d\n", event); | ||
| 505 | spin_unlock_irqrestore(&vio->lock, flags); | ||
| 506 | return; | ||
| 507 | } | ||
| 508 | |||
| 509 | tx_wakeup = err = 0; | ||
| 510 | while (1) { | ||
| 511 | union { | ||
| 512 | struct vio_msg_tag tag; | ||
| 513 | u64 raw[8]; | ||
| 514 | } msgbuf; | ||
| 515 | |||
| 516 | err = ldc_read(vio->lp, &msgbuf, sizeof(msgbuf)); | ||
| 517 | if (unlikely(err < 0)) { | ||
| 518 | if (err == -ECONNRESET) | ||
| 519 | vio_conn_reset(vio); | ||
| 520 | break; | ||
| 521 | } | ||
| 522 | if (err == 0) | ||
| 523 | break; | ||
| 524 | viodbg(DATA, "TAG [%02x:%02x:%04x:%08x]\n", | ||
| 525 | msgbuf.tag.type, | ||
| 526 | msgbuf.tag.stype, | ||
| 527 | msgbuf.tag.stype_env, | ||
| 528 | msgbuf.tag.sid); | ||
| 529 | err = vio_validate_sid(vio, &msgbuf.tag); | ||
| 530 | if (err < 0) | ||
| 531 | break; | ||
| 532 | |||
| 533 | if (likely(msgbuf.tag.type == VIO_TYPE_DATA)) { | ||
| 534 | if (msgbuf.tag.stype == VIO_SUBTYPE_INFO) { | ||
| 535 | err = vnet_rx(port, &msgbuf); | ||
| 536 | } else if (msgbuf.tag.stype == VIO_SUBTYPE_ACK) { | ||
| 537 | err = vnet_ack(port, &msgbuf); | ||
| 538 | if (err > 0) | ||
| 539 | tx_wakeup |= err; | ||
| 540 | } else if (msgbuf.tag.stype == VIO_SUBTYPE_NACK) { | ||
| 541 | err = vnet_nack(port, &msgbuf); | ||
| 542 | } | ||
| 543 | } else if (msgbuf.tag.type == VIO_TYPE_CTRL) { | ||
| 544 | err = vio_control_pkt_engine(vio, &msgbuf); | ||
| 545 | if (err) | ||
| 546 | break; | ||
| 547 | } else { | ||
| 548 | err = vnet_handle_unknown(port, &msgbuf); | ||
| 549 | } | ||
| 550 | if (err == -ECONNRESET) | ||
| 551 | break; | ||
| 552 | } | ||
| 553 | spin_unlock(&vio->lock); | ||
| 554 | if (unlikely(tx_wakeup && err != -ECONNRESET)) | ||
| 555 | maybe_tx_wakeup(port->vp); | ||
| 556 | local_irq_restore(flags); | ||
| 557 | } | ||
| 558 | |||
| 559 | static int __vnet_tx_trigger(struct vnet_port *port) | ||
| 560 | { | ||
| 561 | struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; | ||
| 562 | struct vio_dring_data hdr = { | ||
| 563 | .tag = { | ||
| 564 | .type = VIO_TYPE_DATA, | ||
| 565 | .stype = VIO_SUBTYPE_INFO, | ||
| 566 | .stype_env = VIO_DRING_DATA, | ||
| 567 | .sid = vio_send_sid(&port->vio), | ||
| 568 | }, | ||
| 569 | .dring_ident = dr->ident, | ||
| 570 | .start_idx = dr->prod, | ||
| 571 | .end_idx = (u32) -1, | ||
| 572 | }; | ||
| 573 | int err, delay; | ||
| 574 | |||
| 575 | hdr.seq = dr->snd_nxt; | ||
| 576 | delay = 1; | ||
| 577 | do { | ||
| 578 | err = vio_ldc_send(&port->vio, &hdr, sizeof(hdr)); | ||
| 579 | if (err > 0) { | ||
| 580 | dr->snd_nxt++; | ||
| 581 | break; | ||
| 582 | } | ||
| 583 | udelay(delay); | ||
| 584 | if ((delay <<= 1) > 128) | ||
| 585 | delay = 128; | ||
| 586 | } while (err == -EAGAIN); | ||
| 587 | |||
| 588 | return err; | ||
| 589 | } | ||
| 590 | |||
| 591 | struct vnet_port *__tx_port_find(struct vnet *vp, struct sk_buff *skb) | ||
| 592 | { | ||
| 593 | unsigned int hash = vnet_hashfn(skb->data); | ||
| 594 | struct hlist_head *hp = &vp->port_hash[hash]; | ||
| 595 | struct hlist_node *n; | ||
| 596 | struct vnet_port *port; | ||
| 597 | |||
| 598 | hlist_for_each_entry(port, n, hp, hash) { | ||
| 599 | if (!compare_ether_addr(port->raddr, skb->data)) | ||
| 600 | return port; | ||
| 601 | } | ||
| 602 | port = NULL; | ||
| 603 | if (!list_empty(&vp->port_list)) | ||
| 604 | port = list_entry(vp->port_list.next, struct vnet_port, list); | ||
| 605 | |||
| 606 | return port; | ||
| 607 | } | ||
| 608 | |||
| 609 | struct vnet_port *tx_port_find(struct vnet *vp, struct sk_buff *skb) | ||
| 610 | { | ||
| 611 | struct vnet_port *ret; | ||
| 612 | unsigned long flags; | ||
| 613 | |||
| 614 | spin_lock_irqsave(&vp->lock, flags); | ||
| 615 | ret = __tx_port_find(vp, skb); | ||
| 616 | spin_unlock_irqrestore(&vp->lock, flags); | ||
| 617 | |||
| 618 | return ret; | ||
| 619 | } | ||
| 620 | |||
| 621 | static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev) | ||
| 622 | { | ||
| 623 | struct vnet *vp = netdev_priv(dev); | ||
| 624 | struct vnet_port *port = tx_port_find(vp, skb); | ||
| 625 | struct vio_dring_state *dr; | ||
| 626 | struct vio_net_desc *d; | ||
| 627 | unsigned long flags; | ||
| 628 | unsigned int len; | ||
| 629 | void *tx_buf; | ||
| 630 | int i, err; | ||
| 631 | |||
| 632 | if (unlikely(!port)) | ||
| 633 | goto out_dropped; | ||
| 634 | |||
| 635 | spin_lock_irqsave(&port->vio.lock, flags); | ||
| 636 | |||
| 637 | dr = &port->vio.drings[VIO_DRIVER_TX_RING]; | ||
| 638 | if (unlikely(vnet_tx_dring_avail(dr) < 2)) { | ||
| 639 | if (!netif_queue_stopped(dev)) { | ||
| 640 | netif_stop_queue(dev); | ||
| 641 | |||
| 642 | /* This is a hard error, log it. */ | ||
| 643 | printk(KERN_ERR PFX "%s: BUG! Tx Ring full when " | ||
| 644 | "queue awake!\n", dev->name); | ||
| 645 | dev->stats.tx_errors++; | ||
| 646 | } | ||
| 647 | spin_unlock_irqrestore(&port->vio.lock, flags); | ||
| 648 | return NETDEV_TX_BUSY; | ||
| 649 | } | ||
| 650 | |||
| 651 | d = vio_dring_cur(dr); | ||
| 652 | |||
| 653 | tx_buf = port->tx_bufs[dr->prod].buf; | ||
| 654 | skb_copy_from_linear_data(skb, tx_buf + VNET_PACKET_SKIP, skb->len); | ||
| 655 | |||
| 656 | len = skb->len; | ||
| 657 | if (len < ETH_ZLEN) { | ||
| 658 | len = ETH_ZLEN; | ||
| 659 | memset(tx_buf+VNET_PACKET_SKIP+skb->len, 0, len - skb->len); | ||
| 660 | } | ||
| 661 | |||
| 662 | d->hdr.ack = VIO_ACK_ENABLE; | ||
| 663 | d->size = len; | ||
| 664 | d->ncookies = port->tx_bufs[dr->prod].ncookies; | ||
| 665 | for (i = 0; i < d->ncookies; i++) | ||
| 666 | d->cookies[i] = port->tx_bufs[dr->prod].cookies[i]; | ||
| 667 | |||
| 668 | /* This has to be a non-SMP write barrier because we are writing | ||
| 669 | * to memory which is shared with the peer LDOM. | ||
| 670 | */ | ||
| 671 | wmb(); | ||
| 672 | |||
| 673 | d->hdr.state = VIO_DESC_READY; | ||
| 674 | |||
| 675 | err = __vnet_tx_trigger(port); | ||
| 676 | if (unlikely(err < 0)) { | ||
| 677 | printk(KERN_INFO PFX "%s: TX trigger error %d\n", | ||
| 678 | dev->name, err); | ||
| 679 | d->hdr.state = VIO_DESC_FREE; | ||
| 680 | dev->stats.tx_carrier_errors++; | ||
| 681 | goto out_dropped_unlock; | ||
| 682 | } | ||
| 683 | |||
| 684 | dev->stats.tx_packets++; | ||
| 685 | dev->stats.tx_bytes += skb->len; | ||
| 686 | |||
| 687 | dr->prod = (dr->prod + 1) & (VNET_TX_RING_SIZE - 1); | ||
| 688 | if (unlikely(vnet_tx_dring_avail(dr) < 2)) { | ||
| 689 | netif_stop_queue(dev); | ||
| 690 | if (vnet_tx_dring_avail(dr) > VNET_TX_WAKEUP_THRESH(dr)) | ||
| 691 | netif_wake_queue(dev); | ||
| 692 | } | ||
| 693 | |||
| 694 | spin_unlock_irqrestore(&port->vio.lock, flags); | ||
| 695 | |||
| 696 | dev_kfree_skb(skb); | ||
| 697 | |||
| 698 | dev->trans_start = jiffies; | ||
| 699 | return NETDEV_TX_OK; | ||
| 700 | |||
| 701 | out_dropped_unlock: | ||
| 702 | spin_unlock_irqrestore(&port->vio.lock, flags); | ||
| 703 | |||
| 704 | out_dropped: | ||
| 705 | dev_kfree_skb(skb); | ||
| 706 | dev->stats.tx_dropped++; | ||
| 707 | return NETDEV_TX_OK; | ||
| 708 | } | ||
| 709 | |||
| 710 | static void vnet_tx_timeout(struct net_device *dev) | ||
| 711 | { | ||
| 712 | /* XXX Implement me XXX */ | ||
| 713 | } | ||
| 714 | |||
| 715 | static int vnet_open(struct net_device *dev) | ||
| 716 | { | ||
| 717 | netif_carrier_on(dev); | ||
| 718 | netif_start_queue(dev); | ||
| 719 | |||
| 720 | return 0; | ||
| 721 | } | ||
| 722 | |||
| 723 | static int vnet_close(struct net_device *dev) | ||
| 724 | { | ||
| 725 | netif_stop_queue(dev); | ||
| 726 | netif_carrier_off(dev); | ||
| 727 | |||
| 728 | return 0; | ||
| 729 | } | ||
| 730 | |||
| 731 | static void vnet_set_rx_mode(struct net_device *dev) | ||
| 732 | { | ||
| 733 | /* XXX Implement multicast support XXX */ | ||
| 734 | } | ||
| 735 | |||
| 736 | static int vnet_change_mtu(struct net_device *dev, int new_mtu) | ||
| 737 | { | ||
| 738 | if (new_mtu != ETH_DATA_LEN) | ||
| 739 | return -EINVAL; | ||
| 740 | |||
| 741 | dev->mtu = new_mtu; | ||
| 742 | return 0; | ||
| 743 | } | ||
| 744 | |||
| 745 | static int vnet_set_mac_addr(struct net_device *dev, void *p) | ||
| 746 | { | ||
| 747 | return -EINVAL; | ||
| 748 | } | ||
| 749 | |||
| 750 | static void vnet_get_drvinfo(struct net_device *dev, | ||
| 751 | struct ethtool_drvinfo *info) | ||
| 752 | { | ||
| 753 | strcpy(info->driver, DRV_MODULE_NAME); | ||
| 754 | strcpy(info->version, DRV_MODULE_VERSION); | ||
| 755 | } | ||
| 756 | |||
| 757 | static u32 vnet_get_msglevel(struct net_device *dev) | ||
| 758 | { | ||
| 759 | struct vnet *vp = netdev_priv(dev); | ||
| 760 | return vp->msg_enable; | ||
| 761 | } | ||
| 762 | |||
| 763 | static void vnet_set_msglevel(struct net_device *dev, u32 value) | ||
| 764 | { | ||
| 765 | struct vnet *vp = netdev_priv(dev); | ||
| 766 | vp->msg_enable = value; | ||
| 767 | } | ||
| 768 | |||
| 769 | static const struct ethtool_ops vnet_ethtool_ops = { | ||
| 770 | .get_drvinfo = vnet_get_drvinfo, | ||
| 771 | .get_msglevel = vnet_get_msglevel, | ||
| 772 | .set_msglevel = vnet_set_msglevel, | ||
| 773 | .get_link = ethtool_op_get_link, | ||
| 774 | .get_perm_addr = ethtool_op_get_perm_addr, | ||
| 775 | }; | ||
| 776 | |||
| 777 | static void vnet_port_free_tx_bufs(struct vnet_port *port) | ||
| 778 | { | ||
| 779 | struct vio_dring_state *dr; | ||
| 780 | int i; | ||
| 781 | |||
| 782 | dr = &port->vio.drings[VIO_DRIVER_TX_RING]; | ||
| 783 | if (dr->base) { | ||
| 784 | ldc_free_exp_dring(port->vio.lp, dr->base, | ||
| 785 | (dr->entry_size * dr->num_entries), | ||
| 786 | dr->cookies, dr->ncookies); | ||
| 787 | dr->base = NULL; | ||
| 788 | dr->entry_size = 0; | ||
| 789 | dr->num_entries = 0; | ||
| 790 | dr->pending = 0; | ||
| 791 | dr->ncookies = 0; | ||
| 792 | } | ||
| 793 | |||
| 794 | for (i = 0; i < VNET_TX_RING_SIZE; i++) { | ||
| 795 | void *buf = port->tx_bufs[i].buf; | ||
| 796 | |||
| 797 | if (!buf) | ||
| 798 | continue; | ||
| 799 | |||
| 800 | ldc_unmap(port->vio.lp, | ||
| 801 | port->tx_bufs[i].cookies, | ||
| 802 | port->tx_bufs[i].ncookies); | ||
| 803 | |||
| 804 | kfree(buf); | ||
| 805 | port->tx_bufs[i].buf = NULL; | ||
| 806 | } | ||
| 807 | } | ||
| 808 | |||
| 809 | static int __devinit vnet_port_alloc_tx_bufs(struct vnet_port *port) | ||
| 810 | { | ||
| 811 | struct vio_dring_state *dr; | ||
| 812 | unsigned long len; | ||
| 813 | int i, err, ncookies; | ||
| 814 | void *dring; | ||
| 815 | |||
| 816 | for (i = 0; i < VNET_TX_RING_SIZE; i++) { | ||
| 817 | void *buf = kzalloc(ETH_FRAME_LEN + 8, GFP_KERNEL); | ||
| 818 | int map_len = (ETH_FRAME_LEN + 7) & ~7; | ||
| 819 | |||
| 820 | err = -ENOMEM; | ||
| 821 | if (!buf) { | ||
| 822 | printk(KERN_ERR "TX buffer allocation failure\n"); | ||
| 823 | goto err_out; | ||
| 824 | } | ||
| 825 | err = -EFAULT; | ||
| 826 | if ((unsigned long)buf & (8UL - 1)) { | ||
| 827 | printk(KERN_ERR "TX buffer misaligned\n"); | ||
| 828 | kfree(buf); | ||
| 829 | goto err_out; | ||
| 830 | } | ||
| 831 | |||
| 832 | err = ldc_map_single(port->vio.lp, buf, map_len, | ||
| 833 | port->tx_bufs[i].cookies, 2, | ||
| 834 | (LDC_MAP_SHADOW | | ||
| 835 | LDC_MAP_DIRECT | | ||
| 836 | LDC_MAP_RW)); | ||
| 837 | if (err < 0) { | ||
| 838 | kfree(buf); | ||
| 839 | goto err_out; | ||
| 840 | } | ||
| 841 | port->tx_bufs[i].buf = buf; | ||
| 842 | port->tx_bufs[i].ncookies = err; | ||
| 843 | } | ||
| 844 | |||
| 845 | dr = &port->vio.drings[VIO_DRIVER_TX_RING]; | ||
| 846 | |||
| 847 | len = (VNET_TX_RING_SIZE * | ||
| 848 | (sizeof(struct vio_net_desc) + | ||
| 849 | (sizeof(struct ldc_trans_cookie) * 2))); | ||
| 850 | |||
| 851 | ncookies = VIO_MAX_RING_COOKIES; | ||
| 852 | dring = ldc_alloc_exp_dring(port->vio.lp, len, | ||
| 853 | dr->cookies, &ncookies, | ||
| 854 | (LDC_MAP_SHADOW | | ||
| 855 | LDC_MAP_DIRECT | | ||
| 856 | LDC_MAP_RW)); | ||
| 857 | if (IS_ERR(dring)) { | ||
| 858 | err = PTR_ERR(dring); | ||
| 859 | goto err_out; | ||
| 860 | } | ||
| 861 | |||
| 862 | dr->base = dring; | ||
| 863 | dr->entry_size = (sizeof(struct vio_net_desc) + | ||
| 864 | (sizeof(struct ldc_trans_cookie) * 2)); | ||
| 865 | dr->num_entries = VNET_TX_RING_SIZE; | ||
| 866 | dr->prod = dr->cons = 0; | ||
| 867 | dr->pending = VNET_TX_RING_SIZE; | ||
| 868 | dr->ncookies = ncookies; | ||
| 869 | |||
| 870 | return 0; | ||
| 871 | |||
| 872 | err_out: | ||
| 873 | vnet_port_free_tx_bufs(port); | ||
| 874 | |||
| 875 | return err; | ||
| 876 | } | ||
| 877 | |||
| 878 | static struct ldc_channel_config vnet_ldc_cfg = { | ||
| 879 | .event = vnet_event, | ||
| 880 | .mtu = 64, | ||
| 881 | .mode = LDC_MODE_UNRELIABLE, | ||
| 882 | }; | ||
| 883 | |||
| 884 | static struct vio_driver_ops vnet_vio_ops = { | ||
| 885 | .send_attr = vnet_send_attr, | ||
| 886 | .handle_attr = vnet_handle_attr, | ||
| 887 | .handshake_complete = vnet_handshake_complete, | ||
| 888 | }; | ||
| 889 | |||
| 890 | const char *remote_macaddr_prop = "remote-mac-address"; | ||
| 891 | |||
| 892 | static int __devinit vnet_port_probe(struct vio_dev *vdev, | ||
| 893 | const struct vio_device_id *id) | ||
| 894 | { | ||
| 895 | struct mdesc_handle *hp; | ||
| 896 | struct vnet_port *port; | ||
| 897 | unsigned long flags; | ||
| 898 | struct vnet *vp; | ||
| 899 | const u64 *rmac; | ||
| 900 | int len, i, err, switch_port; | ||
| 901 | |||
| 902 | vp = dev_get_drvdata(vdev->dev.parent); | ||
| 903 | if (!vp) { | ||
| 904 | printk(KERN_ERR PFX "Cannot find port parent vnet.\n"); | ||
| 905 | return -ENODEV; | ||
| 906 | } | ||
| 907 | |||
| 908 | hp = mdesc_grab(); | ||
| 909 | |||
| 910 | rmac = mdesc_get_property(hp, vdev->mp, remote_macaddr_prop, &len); | ||
| 911 | err = -ENODEV; | ||
| 912 | if (!rmac) { | ||
| 913 | printk(KERN_ERR PFX "Port lacks %s property.\n", | ||
| 914 | remote_macaddr_prop); | ||
| 915 | goto err_out_put_mdesc; | ||
| 916 | } | ||
| 917 | |||
| 918 | port = kzalloc(sizeof(*port), GFP_KERNEL); | ||
| 919 | err = -ENOMEM; | ||
| 920 | if (!port) { | ||
| 921 | printk(KERN_ERR PFX "Cannot allocate vnet_port.\n"); | ||
| 922 | goto err_out_put_mdesc; | ||
| 923 | } | ||
| 924 | |||
| 925 | for (i = 0; i < ETH_ALEN; i++) | ||
| 926 | port->raddr[i] = (*rmac >> (5 - i) * 8) & 0xff; | ||
| 927 | |||
| 928 | port->vp = vp; | ||
| 929 | |||
| 930 | err = vio_driver_init(&port->vio, vdev, VDEV_NETWORK, | ||
| 931 | vnet_versions, ARRAY_SIZE(vnet_versions), | ||
| 932 | &vnet_vio_ops, vp->dev->name); | ||
| 933 | if (err) | ||
| 934 | goto err_out_free_port; | ||
| 935 | |||
| 936 | err = vio_ldc_alloc(&port->vio, &vnet_ldc_cfg, port); | ||
| 937 | if (err) | ||
| 938 | goto err_out_free_port; | ||
| 939 | |||
| 940 | err = vnet_port_alloc_tx_bufs(port); | ||
| 941 | if (err) | ||
| 942 | goto err_out_free_ldc; | ||
| 943 | |||
| 944 | INIT_HLIST_NODE(&port->hash); | ||
| 945 | INIT_LIST_HEAD(&port->list); | ||
| 946 | |||
| 947 | switch_port = 0; | ||
| 948 | if (mdesc_get_property(hp, vdev->mp, "switch-port", NULL) != NULL) | ||
| 949 | switch_port = 1; | ||
| 950 | |||
| 951 | spin_lock_irqsave(&vp->lock, flags); | ||
| 952 | if (switch_port) | ||
| 953 | list_add(&port->list, &vp->port_list); | ||
| 954 | else | ||
| 955 | list_add_tail(&port->list, &vp->port_list); | ||
| 956 | hlist_add_head(&port->hash, &vp->port_hash[vnet_hashfn(port->raddr)]); | ||
| 957 | spin_unlock_irqrestore(&vp->lock, flags); | ||
| 958 | |||
| 959 | dev_set_drvdata(&vdev->dev, port); | ||
| 960 | |||
| 961 | printk(KERN_INFO "%s: PORT ( remote-mac ", vp->dev->name); | ||
| 962 | for (i = 0; i < 6; i++) | ||
| 963 | printk("%2.2x%c", port->raddr[i], i == 5 ? ' ' : ':'); | ||
| 964 | if (switch_port) | ||
| 965 | printk("switch-port "); | ||
| 966 | printk(")\n"); | ||
| 967 | |||
| 968 | vio_port_up(&port->vio); | ||
| 969 | |||
| 970 | mdesc_release(hp); | ||
| 971 | |||
| 972 | return 0; | ||
| 973 | |||
| 974 | err_out_free_ldc: | ||
| 975 | vio_ldc_free(&port->vio); | ||
| 976 | |||
| 977 | err_out_free_port: | ||
| 978 | kfree(port); | ||
| 979 | |||
| 980 | err_out_put_mdesc: | ||
| 981 | mdesc_release(hp); | ||
| 982 | return err; | ||
| 983 | } | ||
| 984 | |||
| 985 | static int vnet_port_remove(struct vio_dev *vdev) | ||
| 986 | { | ||
| 987 | struct vnet_port *port = dev_get_drvdata(&vdev->dev); | ||
| 988 | |||
| 989 | if (port) { | ||
| 990 | struct vnet *vp = port->vp; | ||
| 991 | unsigned long flags; | ||
| 992 | |||
| 993 | del_timer_sync(&port->vio.timer); | ||
| 994 | |||
| 995 | spin_lock_irqsave(&vp->lock, flags); | ||
| 996 | list_del(&port->list); | ||
| 997 | hlist_del(&port->hash); | ||
| 998 | spin_unlock_irqrestore(&vp->lock, flags); | ||
| 999 | |||
| 1000 | vnet_port_free_tx_bufs(port); | ||
| 1001 | vio_ldc_free(&port->vio); | ||
| 1002 | |||
| 1003 | dev_set_drvdata(&vdev->dev, NULL); | ||
| 1004 | |||
| 1005 | kfree(port); | ||
| 1006 | } | ||
| 1007 | return 0; | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | static struct vio_device_id vnet_port_match[] = { | ||
| 1011 | { | ||
| 1012 | .type = "vnet-port", | ||
| 1013 | }, | ||
| 1014 | {}, | ||
| 1015 | }; | ||
| 1016 | MODULE_DEVICE_TABLE(vio, vnet_match); | ||
| 1017 | |||
| 1018 | static struct vio_driver vnet_port_driver = { | ||
| 1019 | .id_table = vnet_port_match, | ||
| 1020 | .probe = vnet_port_probe, | ||
| 1021 | .remove = vnet_port_remove, | ||
| 1022 | .driver = { | ||
| 1023 | .name = "vnet_port", | ||
| 1024 | .owner = THIS_MODULE, | ||
| 1025 | } | ||
| 1026 | }; | ||
| 1027 | |||
| 1028 | const char *local_mac_prop = "local-mac-address"; | ||
| 1029 | |||
| 1030 | static int __devinit vnet_probe(struct vio_dev *vdev, | ||
| 1031 | const struct vio_device_id *id) | ||
| 1032 | { | ||
| 1033 | static int vnet_version_printed; | ||
| 1034 | struct mdesc_handle *hp; | ||
| 1035 | struct net_device *dev; | ||
| 1036 | struct vnet *vp; | ||
| 1037 | const u64 *mac; | ||
| 1038 | int err, i, len; | ||
| 1039 | |||
| 1040 | if (vnet_version_printed++ == 0) | ||
| 1041 | printk(KERN_INFO "%s", version); | ||
| 1042 | |||
| 1043 | hp = mdesc_grab(); | ||
| 1044 | |||
| 1045 | mac = mdesc_get_property(hp, vdev->mp, local_mac_prop, &len); | ||
| 1046 | if (!mac) { | ||
| 1047 | printk(KERN_ERR PFX "vnet lacks %s property.\n", | ||
| 1048 | local_mac_prop); | ||
| 1049 | err = -ENODEV; | ||
| 1050 | goto err_out; | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | dev = alloc_etherdev(sizeof(*vp)); | ||
| 1054 | if (!dev) { | ||
| 1055 | printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n"); | ||
| 1056 | err = -ENOMEM; | ||
| 1057 | goto err_out; | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | for (i = 0; i < ETH_ALEN; i++) | ||
| 1061 | dev->dev_addr[i] = (*mac >> (5 - i) * 8) & 0xff; | ||
| 1062 | |||
| 1063 | memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); | ||
| 1064 | |||
| 1065 | SET_NETDEV_DEV(dev, &vdev->dev); | ||
| 1066 | |||
| 1067 | vp = netdev_priv(dev); | ||
| 1068 | |||
| 1069 | spin_lock_init(&vp->lock); | ||
| 1070 | vp->dev = dev; | ||
| 1071 | vp->vdev = vdev; | ||
| 1072 | |||
| 1073 | INIT_LIST_HEAD(&vp->port_list); | ||
| 1074 | for (i = 0; i < VNET_PORT_HASH_SIZE; i++) | ||
| 1075 | INIT_HLIST_HEAD(&vp->port_hash[i]); | ||
| 1076 | |||
| 1077 | dev->open = vnet_open; | ||
| 1078 | dev->stop = vnet_close; | ||
| 1079 | dev->set_multicast_list = vnet_set_rx_mode; | ||
| 1080 | dev->set_mac_address = vnet_set_mac_addr; | ||
| 1081 | dev->tx_timeout = vnet_tx_timeout; | ||
| 1082 | dev->ethtool_ops = &vnet_ethtool_ops; | ||
| 1083 | dev->watchdog_timeo = VNET_TX_TIMEOUT; | ||
| 1084 | dev->change_mtu = vnet_change_mtu; | ||
| 1085 | dev->hard_start_xmit = vnet_start_xmit; | ||
| 1086 | |||
| 1087 | err = register_netdev(dev); | ||
| 1088 | if (err) { | ||
| 1089 | printk(KERN_ERR PFX "Cannot register net device, " | ||
| 1090 | "aborting.\n"); | ||
| 1091 | goto err_out_free_dev; | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | printk(KERN_INFO "%s: Sun LDOM vnet ", dev->name); | ||
| 1095 | |||
| 1096 | for (i = 0; i < 6; i++) | ||
| 1097 | printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); | ||
| 1098 | |||
| 1099 | dev_set_drvdata(&vdev->dev, vp); | ||
| 1100 | |||
| 1101 | mdesc_release(hp); | ||
| 1102 | |||
| 1103 | return 0; | ||
| 1104 | |||
| 1105 | err_out_free_dev: | ||
| 1106 | free_netdev(dev); | ||
| 1107 | |||
| 1108 | err_out: | ||
| 1109 | mdesc_release(hp); | ||
| 1110 | return err; | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | static int vnet_remove(struct vio_dev *vdev) | ||
| 1114 | { | ||
| 1115 | |||
| 1116 | struct vnet *vp = dev_get_drvdata(&vdev->dev); | ||
| 1117 | |||
| 1118 | if (vp) { | ||
| 1119 | /* XXX unregister port, or at least check XXX */ | ||
| 1120 | unregister_netdevice(vp->dev); | ||
| 1121 | dev_set_drvdata(&vdev->dev, NULL); | ||
| 1122 | } | ||
| 1123 | return 0; | ||
| 1124 | } | ||
| 1125 | |||
| 1126 | static struct vio_device_id vnet_match[] = { | ||
| 1127 | { | ||
| 1128 | .type = "network", | ||
| 1129 | }, | ||
| 1130 | {}, | ||
| 1131 | }; | ||
| 1132 | MODULE_DEVICE_TABLE(vio, vnet_match); | ||
| 1133 | |||
| 1134 | static struct vio_driver vnet_driver = { | ||
| 1135 | .id_table = vnet_match, | ||
| 1136 | .probe = vnet_probe, | ||
| 1137 | .remove = vnet_remove, | ||
| 1138 | .driver = { | ||
| 1139 | .name = "vnet", | ||
| 1140 | .owner = THIS_MODULE, | ||
| 1141 | } | ||
| 1142 | }; | ||
| 1143 | |||
| 1144 | static int __init vnet_init(void) | ||
| 1145 | { | ||
| 1146 | int err = vio_register_driver(&vnet_driver); | ||
| 1147 | |||
| 1148 | if (!err) { | ||
| 1149 | err = vio_register_driver(&vnet_port_driver); | ||
| 1150 | if (err) | ||
| 1151 | vio_unregister_driver(&vnet_driver); | ||
| 1152 | } | ||
| 1153 | |||
| 1154 | return err; | ||
| 1155 | } | ||
| 1156 | |||
| 1157 | static void __exit vnet_exit(void) | ||
| 1158 | { | ||
| 1159 | vio_unregister_driver(&vnet_port_driver); | ||
| 1160 | vio_unregister_driver(&vnet_driver); | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | module_init(vnet_init); | ||
| 1164 | module_exit(vnet_exit); | ||
diff --git a/drivers/net/sunvnet.h b/drivers/net/sunvnet.h new file mode 100644 index 000000000000..1c887302d46d --- /dev/null +++ b/drivers/net/sunvnet.h | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | #ifndef _SUNVNET_H | ||
| 2 | #define _SUNVNET_H | ||
| 3 | |||
| 4 | #define DESC_NCOOKIES(entry_size) \ | ||
| 5 | ((entry_size) - sizeof(struct vio_net_desc)) | ||
| 6 | |||
| 7 | /* length of time before we decide the hardware is borked, | ||
| 8 | * and dev->tx_timeout() should be called to fix the problem | ||
| 9 | */ | ||
| 10 | #define VNET_TX_TIMEOUT (5 * HZ) | ||
| 11 | |||
| 12 | #define VNET_TX_RING_SIZE 512 | ||
| 13 | #define VNET_TX_WAKEUP_THRESH(dr) ((dr)->pending / 4) | ||
| 14 | |||
| 15 | /* VNET packets are sent in buffers with the first 6 bytes skipped | ||
| 16 | * so that after the ethernet header the IPv4/IPv6 headers are aligned | ||
| 17 | * properly. | ||
| 18 | */ | ||
| 19 | #define VNET_PACKET_SKIP 6 | ||
| 20 | |||
| 21 | struct vnet_tx_entry { | ||
| 22 | void *buf; | ||
| 23 | unsigned int ncookies; | ||
| 24 | struct ldc_trans_cookie cookies[2]; | ||
| 25 | }; | ||
| 26 | |||
| 27 | struct vnet; | ||
| 28 | struct vnet_port { | ||
| 29 | struct vio_driver_state vio; | ||
| 30 | |||
| 31 | struct hlist_node hash; | ||
| 32 | u8 raddr[ETH_ALEN]; | ||
| 33 | |||
| 34 | struct vnet *vp; | ||
| 35 | |||
| 36 | struct vnet_tx_entry tx_bufs[VNET_TX_RING_SIZE]; | ||
| 37 | |||
| 38 | struct list_head list; | ||
| 39 | }; | ||
| 40 | |||
| 41 | static inline struct vnet_port *to_vnet_port(struct vio_driver_state *vio) | ||
| 42 | { | ||
| 43 | return container_of(vio, struct vnet_port, vio); | ||
| 44 | } | ||
| 45 | |||
| 46 | #define VNET_PORT_HASH_SIZE 16 | ||
| 47 | #define VNET_PORT_HASH_MASK (VNET_PORT_HASH_SIZE - 1) | ||
| 48 | |||
| 49 | static inline unsigned int vnet_hashfn(u8 *mac) | ||
| 50 | { | ||
| 51 | unsigned int val = mac[4] ^ mac[5]; | ||
| 52 | |||
| 53 | return val & (VNET_PORT_HASH_MASK); | ||
| 54 | } | ||
| 55 | |||
| 56 | struct vnet { | ||
| 57 | /* Protects port_list and port_hash. */ | ||
| 58 | spinlock_t lock; | ||
| 59 | |||
| 60 | struct net_device *dev; | ||
| 61 | |||
| 62 | u32 msg_enable; | ||
| 63 | struct vio_dev *vdev; | ||
| 64 | |||
| 65 | struct list_head port_list; | ||
| 66 | |||
| 67 | struct hlist_head port_hash[VNET_PORT_HASH_SIZE]; | ||
| 68 | }; | ||
| 69 | |||
| 70 | #endif /* _SUNVNET_H */ | ||
diff --git a/drivers/serial/sunhv.c b/drivers/serial/sunhv.c index 96557e6dba60..17bcca53d6a1 100644 --- a/drivers/serial/sunhv.c +++ b/drivers/serial/sunhv.c | |||
| @@ -440,8 +440,16 @@ static void sunhv_console_write_paged(struct console *con, const char *s, unsign | |||
| 440 | { | 440 | { |
| 441 | struct uart_port *port = sunhv_port; | 441 | struct uart_port *port = sunhv_port; |
| 442 | unsigned long flags; | 442 | unsigned long flags; |
| 443 | int locked = 1; | ||
| 444 | |||
| 445 | local_irq_save(flags); | ||
| 446 | if (port->sysrq) { | ||
| 447 | locked = 0; | ||
| 448 | } else if (oops_in_progress) { | ||
| 449 | locked = spin_trylock(&port->lock); | ||
| 450 | } else | ||
| 451 | spin_lock(&port->lock); | ||
| 443 | 452 | ||
| 444 | spin_lock_irqsave(&port->lock, flags); | ||
| 445 | while (n > 0) { | 453 | while (n > 0) { |
| 446 | unsigned long ra = __pa(con_write_page); | 454 | unsigned long ra = __pa(con_write_page); |
| 447 | unsigned long page_bytes; | 455 | unsigned long page_bytes; |
| @@ -469,7 +477,10 @@ static void sunhv_console_write_paged(struct console *con, const char *s, unsign | |||
| 469 | ra += written; | 477 | ra += written; |
| 470 | } | 478 | } |
| 471 | } | 479 | } |
| 472 | spin_unlock_irqrestore(&port->lock, flags); | 480 | |
| 481 | if (locked) | ||
| 482 | spin_unlock(&port->lock); | ||
| 483 | local_irq_restore(flags); | ||
| 473 | } | 484 | } |
| 474 | 485 | ||
| 475 | static inline void sunhv_console_putchar(struct uart_port *port, char c) | 486 | static inline void sunhv_console_putchar(struct uart_port *port, char c) |
| @@ -488,7 +499,15 @@ static void sunhv_console_write_bychar(struct console *con, const char *s, unsig | |||
| 488 | { | 499 | { |
| 489 | struct uart_port *port = sunhv_port; | 500 | struct uart_port *port = sunhv_port; |
| 490 | unsigned long flags; | 501 | unsigned long flags; |
| 491 | int i; | 502 | int i, locked = 1; |
| 503 | |||
| 504 | local_irq_save(flags); | ||
| 505 | if (port->sysrq) { | ||
| 506 | locked = 0; | ||
| 507 | } else if (oops_in_progress) { | ||
| 508 | locked = spin_trylock(&port->lock); | ||
| 509 | } else | ||
| 510 | spin_lock(&port->lock); | ||
| 492 | 511 | ||
| 493 | spin_lock_irqsave(&port->lock, flags); | 512 | spin_lock_irqsave(&port->lock, flags); |
| 494 | for (i = 0; i < n; i++) { | 513 | for (i = 0; i < n; i++) { |
| @@ -496,7 +515,10 @@ static void sunhv_console_write_bychar(struct console *con, const char *s, unsig | |||
| 496 | sunhv_console_putchar(port, '\r'); | 515 | sunhv_console_putchar(port, '\r'); |
| 497 | sunhv_console_putchar(port, *s++); | 516 | sunhv_console_putchar(port, *s++); |
| 498 | } | 517 | } |
| 499 | spin_unlock_irqrestore(&port->lock, flags); | 518 | |
| 519 | if (locked) | ||
| 520 | spin_unlock(&port->lock); | ||
| 521 | local_irq_restore(flags); | ||
| 500 | } | 522 | } |
| 501 | 523 | ||
| 502 | static struct console sunhv_console = { | 524 | static struct console sunhv_console = { |
diff --git a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c index deb9ab4b5a0b..8a0f9e4408d4 100644 --- a/drivers/serial/sunsab.c +++ b/drivers/serial/sunsab.c | |||
| @@ -860,22 +860,31 @@ static int num_channels; | |||
| 860 | static void sunsab_console_putchar(struct uart_port *port, int c) | 860 | static void sunsab_console_putchar(struct uart_port *port, int c) |
| 861 | { | 861 | { |
| 862 | struct uart_sunsab_port *up = (struct uart_sunsab_port *)port; | 862 | struct uart_sunsab_port *up = (struct uart_sunsab_port *)port; |
| 863 | unsigned long flags; | ||
| 864 | |||
| 865 | spin_lock_irqsave(&up->port.lock, flags); | ||
| 866 | 863 | ||
| 867 | sunsab_tec_wait(up); | 864 | sunsab_tec_wait(up); |
| 868 | writeb(c, &up->regs->w.tic); | 865 | writeb(c, &up->regs->w.tic); |
| 869 | |||
| 870 | spin_unlock_irqrestore(&up->port.lock, flags); | ||
| 871 | } | 866 | } |
| 872 | 867 | ||
| 873 | static void sunsab_console_write(struct console *con, const char *s, unsigned n) | 868 | static void sunsab_console_write(struct console *con, const char *s, unsigned n) |
| 874 | { | 869 | { |
| 875 | struct uart_sunsab_port *up = &sunsab_ports[con->index]; | 870 | struct uart_sunsab_port *up = &sunsab_ports[con->index]; |
| 871 | unsigned long flags; | ||
| 872 | int locked = 1; | ||
| 873 | |||
| 874 | local_irq_save(flags); | ||
| 875 | if (up->port.sysrq) { | ||
| 876 | locked = 0; | ||
| 877 | } else if (oops_in_progress) { | ||
| 878 | locked = spin_trylock(&up->port.lock); | ||
| 879 | } else | ||
| 880 | spin_lock(&up->port.lock); | ||
| 876 | 881 | ||
| 877 | uart_console_write(&up->port, s, n, sunsab_console_putchar); | 882 | uart_console_write(&up->port, s, n, sunsab_console_putchar); |
| 878 | sunsab_tec_wait(up); | 883 | sunsab_tec_wait(up); |
| 884 | |||
| 885 | if (locked) | ||
| 886 | spin_unlock(&up->port.lock); | ||
| 887 | local_irq_restore(flags); | ||
| 879 | } | 888 | } |
| 880 | 889 | ||
| 881 | static int sunsab_console_setup(struct console *con, char *options) | 890 | static int sunsab_console_setup(struct console *con, char *options) |
diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c index 2a63cdba3208..26d720baf88c 100644 --- a/drivers/serial/sunsu.c +++ b/drivers/serial/sunsu.c | |||
| @@ -1288,7 +1288,17 @@ static void sunsu_console_write(struct console *co, const char *s, | |||
| 1288 | unsigned int count) | 1288 | unsigned int count) |
| 1289 | { | 1289 | { |
| 1290 | struct uart_sunsu_port *up = &sunsu_ports[co->index]; | 1290 | struct uart_sunsu_port *up = &sunsu_ports[co->index]; |
| 1291 | unsigned long flags; | ||
| 1291 | unsigned int ier; | 1292 | unsigned int ier; |
| 1293 | int locked = 1; | ||
| 1294 | |||
| 1295 | local_irq_save(flags); | ||
| 1296 | if (up->port.sysrq) { | ||
| 1297 | locked = 0; | ||
| 1298 | } else if (oops_in_progress) { | ||
| 1299 | locked = spin_trylock(&up->port.lock); | ||
| 1300 | } else | ||
| 1301 | spin_lock(&up->port.lock); | ||
| 1292 | 1302 | ||
| 1293 | /* | 1303 | /* |
| 1294 | * First save the UER then disable the interrupts | 1304 | * First save the UER then disable the interrupts |
| @@ -1304,6 +1314,10 @@ static void sunsu_console_write(struct console *co, const char *s, | |||
| 1304 | */ | 1314 | */ |
| 1305 | wait_for_xmitr(up); | 1315 | wait_for_xmitr(up); |
| 1306 | serial_out(up, UART_IER, ier); | 1316 | serial_out(up, UART_IER, ier); |
| 1317 | |||
| 1318 | if (locked) | ||
| 1319 | spin_unlock(&up->port.lock); | ||
| 1320 | local_irq_restore(flags); | ||
| 1307 | } | 1321 | } |
| 1308 | 1322 | ||
| 1309 | /* | 1323 | /* |
diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c index 15b6e1cb040b..0a3e10a4a35d 100644 --- a/drivers/serial/sunzilog.c +++ b/drivers/serial/sunzilog.c | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | * C. Dost, Pete Zaitcev, Ted Ts'o and Alex Buell for their | 9 | * C. Dost, Pete Zaitcev, Ted Ts'o and Alex Buell for their |
| 10 | * work there. | 10 | * work there. |
| 11 | * | 11 | * |
| 12 | * Copyright (C) 2002, 2006 David S. Miller (davem@davemloft.net) | 12 | * Copyright (C) 2002, 2006, 2007 David S. Miller (davem@davemloft.net) |
| 13 | */ | 13 | */ |
| 14 | 14 | ||
| 15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
| @@ -1151,11 +1151,22 @@ sunzilog_console_write(struct console *con, const char *s, unsigned int count) | |||
| 1151 | { | 1151 | { |
| 1152 | struct uart_sunzilog_port *up = &sunzilog_port_table[con->index]; | 1152 | struct uart_sunzilog_port *up = &sunzilog_port_table[con->index]; |
| 1153 | unsigned long flags; | 1153 | unsigned long flags; |
| 1154 | int locked = 1; | ||
| 1155 | |||
| 1156 | local_irq_save(flags); | ||
| 1157 | if (up->port.sysrq) { | ||
| 1158 | locked = 0; | ||
| 1159 | } else if (oops_in_progress) { | ||
| 1160 | locked = spin_trylock(&up->port.lock); | ||
| 1161 | } else | ||
| 1162 | spin_lock(&up->port.lock); | ||
| 1154 | 1163 | ||
| 1155 | spin_lock_irqsave(&up->port.lock, flags); | ||
| 1156 | uart_console_write(&up->port, s, count, sunzilog_putchar); | 1164 | uart_console_write(&up->port, s, count, sunzilog_putchar); |
| 1157 | udelay(2); | 1165 | udelay(2); |
| 1158 | spin_unlock_irqrestore(&up->port.lock, flags); | 1166 | |
| 1167 | if (locked) | ||
| 1168 | spin_unlock(&up->port.lock); | ||
| 1169 | local_irq_restore(flags); | ||
| 1159 | } | 1170 | } |
| 1160 | 1171 | ||
| 1161 | static int __init sunzilog_console_setup(struct console *con, char *options) | 1172 | static int __init sunzilog_console_setup(struct console *con, char *options) |
diff --git a/include/asm-sparc64/bugs.h b/include/asm-sparc64/bugs.h index bf39d86c0c9e..11ade6841971 100644 --- a/include/asm-sparc64/bugs.h +++ b/include/asm-sparc64/bugs.h | |||
| @@ -4,12 +4,7 @@ | |||
| 4 | */ | 4 | */ |
| 5 | #include <asm/sstate.h> | 5 | #include <asm/sstate.h> |
| 6 | 6 | ||
| 7 | extern unsigned long loops_per_jiffy; | ||
| 8 | |||
| 9 | static void __init check_bugs(void) | 7 | static void __init check_bugs(void) |
| 10 | { | 8 | { |
| 11 | #ifndef CONFIG_SMP | ||
| 12 | cpu_data(0).udelay_val = loops_per_jiffy; | ||
| 13 | #endif | ||
| 14 | sstate_running(); | 9 | sstate_running(); |
| 15 | } | 10 | } |
diff --git a/include/asm-sparc64/cpudata.h b/include/asm-sparc64/cpudata.h index 445026fbec35..98a6e609163e 100644 --- a/include/asm-sparc64/cpudata.h +++ b/include/asm-sparc64/cpudata.h | |||
| @@ -19,7 +19,7 @@ typedef struct { | |||
| 19 | unsigned int __softirq_pending; /* must be 1st, see rtrap.S */ | 19 | unsigned int __softirq_pending; /* must be 1st, see rtrap.S */ |
| 20 | unsigned int __pad0; | 20 | unsigned int __pad0; |
| 21 | unsigned long clock_tick; /* %tick's per second */ | 21 | unsigned long clock_tick; /* %tick's per second */ |
| 22 | unsigned long udelay_val; | 22 | unsigned long __pad; |
| 23 | unsigned int __pad1; | 23 | unsigned int __pad1; |
| 24 | unsigned int __pad2; | 24 | unsigned int __pad2; |
| 25 | 25 | ||
| @@ -80,7 +80,8 @@ struct trap_per_cpu { | |||
| 80 | unsigned int dev_mondo_qmask; | 80 | unsigned int dev_mondo_qmask; |
| 81 | unsigned int resum_qmask; | 81 | unsigned int resum_qmask; |
| 82 | unsigned int nonresum_qmask; | 82 | unsigned int nonresum_qmask; |
| 83 | unsigned int __pad2[3]; | 83 | unsigned int __pad2[1]; |
| 84 | void *hdesc; | ||
| 84 | } __attribute__((aligned(64))); | 85 | } __attribute__((aligned(64))); |
| 85 | extern struct trap_per_cpu trap_block[NR_CPUS]; | 86 | extern struct trap_per_cpu trap_block[NR_CPUS]; |
| 86 | extern void init_cur_cpu_trap(struct thread_info *); | 87 | extern void init_cur_cpu_trap(struct thread_info *); |
diff --git a/include/asm-sparc64/delay.h b/include/asm-sparc64/delay.h index a4aae6f80627..a77aa622d762 100644 --- a/include/asm-sparc64/delay.h +++ b/include/asm-sparc64/delay.h | |||
| @@ -1,37 +1,17 @@ | |||
| 1 | /* delay.h: Linux delay routines on sparc64. | 1 | /* delay.h: Linux delay routines on sparc64. |
| 2 | * | 2 | * |
| 3 | * Copyright (C) 1996, 2004 David S. Miller (davem@davemloft.net). | 3 | * Copyright (C) 1996, 2004, 2007 David S. Miller (davem@davemloft.net). |
| 4 | * | ||
| 5 | * Based heavily upon x86 variant which is: | ||
| 6 | * Copyright (C) 1993 Linus Torvalds | ||
| 7 | * | ||
| 8 | * Delay routines calling functions in arch/sparc64/lib/delay.c | ||
| 9 | */ | 4 | */ |
| 10 | 5 | ||
| 11 | #ifndef __SPARC64_DELAY_H | 6 | #ifndef _SPARC64_DELAY_H |
| 12 | #define __SPARC64_DELAY_H | 7 | #define _SPARC64_DELAY_H |
| 13 | |||
| 14 | #include <linux/param.h> | ||
| 15 | #include <asm/cpudata.h> | ||
| 16 | 8 | ||
| 17 | #ifndef __ASSEMBLY__ | 9 | #ifndef __ASSEMBLY__ |
| 18 | 10 | ||
| 19 | extern void __bad_udelay(void); | ||
| 20 | extern void __bad_ndelay(void); | ||
| 21 | |||
| 22 | extern void __udelay(unsigned long usecs); | ||
| 23 | extern void __ndelay(unsigned long nsecs); | ||
| 24 | extern void __const_udelay(unsigned long usecs); | ||
| 25 | extern void __delay(unsigned long loops); | 11 | extern void __delay(unsigned long loops); |
| 26 | 12 | extern void udelay(unsigned long usecs); | |
| 27 | #define udelay(n) (__builtin_constant_p(n) ? \ | 13 | #define mdelay(n) udelay((n) * 1000) |
| 28 | ((n) > 20000 ? __bad_udelay() : __const_udelay((n) * 0x10c7ul)) : \ | ||
| 29 | __udelay(n)) | ||
| 30 | |||
| 31 | #define ndelay(n) (__builtin_constant_p(n) ? \ | ||
| 32 | ((n) > 20000 ? __bad_ndelay() : __const_udelay((n) * 5ul)) : \ | ||
| 33 | __ndelay(n)) | ||
| 34 | 14 | ||
| 35 | #endif /* !__ASSEMBLY__ */ | 15 | #endif /* !__ASSEMBLY__ */ |
| 36 | 16 | ||
| 37 | #endif /* defined(__SPARC64_DELAY_H) */ | 17 | #endif /* _SPARC64_DELAY_H */ |
diff --git a/include/asm-sparc64/hvtramp.h b/include/asm-sparc64/hvtramp.h new file mode 100644 index 000000000000..c7dd6ad056df --- /dev/null +++ b/include/asm-sparc64/hvtramp.h | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | #ifndef _SPARC64_HVTRAP_H | ||
| 2 | #define _SPARC64_HVTRAP_H | ||
| 3 | |||
| 4 | #ifndef __ASSEMBLY__ | ||
| 5 | |||
| 6 | #include <linux/types.h> | ||
| 7 | |||
| 8 | struct hvtramp_mapping { | ||
| 9 | __u64 vaddr; | ||
| 10 | __u64 tte; | ||
| 11 | }; | ||
| 12 | |||
| 13 | struct hvtramp_descr { | ||
| 14 | __u32 cpu; | ||
| 15 | __u32 num_mappings; | ||
| 16 | __u64 fault_info_va; | ||
| 17 | __u64 fault_info_pa; | ||
| 18 | __u64 thread_reg; | ||
| 19 | struct hvtramp_mapping maps[2]; | ||
| 20 | }; | ||
| 21 | |||
| 22 | extern void hv_cpu_startup(unsigned long hvdescr_pa); | ||
| 23 | |||
| 24 | #endif | ||
| 25 | |||
| 26 | #define HVTRAMP_DESCR_CPU 0x00 | ||
| 27 | #define HVTRAMP_DESCR_NUM_MAPPINGS 0x04 | ||
| 28 | #define HVTRAMP_DESCR_FAULT_INFO_VA 0x08 | ||
| 29 | #define HVTRAMP_DESCR_FAULT_INFO_PA 0x10 | ||
| 30 | #define HVTRAMP_DESCR_THREAD_REG 0x18 | ||
| 31 | #define HVTRAMP_DESCR_MAPS 0x20 | ||
| 32 | |||
| 33 | #define HVTRAMP_MAPPING_VADDR 0x00 | ||
| 34 | #define HVTRAMP_MAPPING_TTE 0x08 | ||
| 35 | #define HVTRAMP_MAPPING_SIZE 0x10 | ||
| 36 | |||
| 37 | #endif /* _SPARC64_HVTRAP_H */ | ||
diff --git a/include/asm-sparc64/hypervisor.h b/include/asm-sparc64/hypervisor.h index db2130a95d68..524d49835dfd 100644 --- a/include/asm-sparc64/hypervisor.h +++ b/include/asm-sparc64/hypervisor.h | |||
| @@ -98,7 +98,7 @@ | |||
| 98 | #define HV_FAST_MACH_EXIT 0x00 | 98 | #define HV_FAST_MACH_EXIT 0x00 |
| 99 | 99 | ||
| 100 | #ifndef __ASSEMBLY__ | 100 | #ifndef __ASSEMBLY__ |
| 101 | extern void sun4v_mach_exit(unsigned long exit_core); | 101 | extern void sun4v_mach_exit(unsigned long exit_code); |
| 102 | #endif | 102 | #endif |
| 103 | 103 | ||
| 104 | /* Domain services. */ | 104 | /* Domain services. */ |
diff --git a/include/asm-sparc64/irq.h b/include/asm-sparc64/irq.h index 90781e34a95c..e6c436ef9356 100644 --- a/include/asm-sparc64/irq.h +++ b/include/asm-sparc64/irq.h | |||
| @@ -53,6 +53,8 @@ extern unsigned int sun4v_build_msi(u32 devhandle, unsigned int *virt_irq_p, | |||
| 53 | extern void sun4v_destroy_msi(unsigned int virt_irq); | 53 | extern void sun4v_destroy_msi(unsigned int virt_irq); |
| 54 | extern unsigned int sbus_build_irq(void *sbus, unsigned int ino); | 54 | extern unsigned int sbus_build_irq(void *sbus, unsigned int ino); |
| 55 | 55 | ||
| 56 | extern void fixup_irqs(void); | ||
| 57 | |||
| 56 | static __inline__ void set_softint(unsigned long bits) | 58 | static __inline__ void set_softint(unsigned long bits) |
| 57 | { | 59 | { |
| 58 | __asm__ __volatile__("wr %0, 0x0, %%set_softint" | 60 | __asm__ __volatile__("wr %0, 0x0, %%set_softint" |
diff --git a/include/asm-sparc64/ldc.h b/include/asm-sparc64/ldc.h new file mode 100644 index 000000000000..bdb524a7b814 --- /dev/null +++ b/include/asm-sparc64/ldc.h | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | #ifndef _SPARC64_LDC_H | ||
| 2 | #define _SPARC64_LDC_H | ||
| 3 | |||
| 4 | #include <asm/hypervisor.h> | ||
| 5 | |||
| 6 | extern int ldom_domaining_enabled; | ||
| 7 | extern void ldom_set_var(const char *var, const char *value); | ||
| 8 | extern void ldom_reboot(const char *boot_command); | ||
| 9 | extern void ldom_power_off(void); | ||
| 10 | |||
| 11 | /* The event handler will be evoked when link state changes | ||
| 12 | * or data becomes available on the receive side. | ||
| 13 | * | ||
| 14 | * For non-RAW links, if the LDC_EVENT_RESET event arrives the | ||
| 15 | * driver should reset all of it's internal state and reinvoke | ||
| 16 | * ldc_connect() to try and bring the link up again. | ||
| 17 | * | ||
| 18 | * For RAW links, ldc_connect() is not used. Instead the driver | ||
| 19 | * just waits for the LDC_EVENT_UP event. | ||
| 20 | */ | ||
| 21 | struct ldc_channel_config { | ||
| 22 | void (*event)(void *arg, int event); | ||
| 23 | |||
| 24 | u32 mtu; | ||
| 25 | unsigned int rx_irq; | ||
| 26 | unsigned int tx_irq; | ||
| 27 | u8 mode; | ||
| 28 | #define LDC_MODE_RAW 0x00 | ||
| 29 | #define LDC_MODE_UNRELIABLE 0x01 | ||
| 30 | #define LDC_MODE_RESERVED 0x02 | ||
| 31 | #define LDC_MODE_STREAM 0x03 | ||
| 32 | |||
| 33 | u8 debug; | ||
| 34 | #define LDC_DEBUG_HS 0x01 | ||
| 35 | #define LDC_DEBUG_STATE 0x02 | ||
| 36 | #define LDC_DEBUG_RX 0x04 | ||
| 37 | #define LDC_DEBUG_TX 0x08 | ||
| 38 | #define LDC_DEBUG_DATA 0x10 | ||
| 39 | }; | ||
| 40 | |||
| 41 | #define LDC_EVENT_RESET 0x01 | ||
| 42 | #define LDC_EVENT_UP 0x02 | ||
| 43 | #define LDC_EVENT_DATA_READY 0x04 | ||
| 44 | |||
| 45 | #define LDC_STATE_INVALID 0x00 | ||
| 46 | #define LDC_STATE_INIT 0x01 | ||
| 47 | #define LDC_STATE_BOUND 0x02 | ||
| 48 | #define LDC_STATE_READY 0x03 | ||
| 49 | #define LDC_STATE_CONNECTED 0x04 | ||
| 50 | |||
| 51 | struct ldc_channel; | ||
| 52 | |||
| 53 | /* Allocate state for a channel. */ | ||
| 54 | extern struct ldc_channel *ldc_alloc(unsigned long id, | ||
| 55 | const struct ldc_channel_config *cfgp, | ||
| 56 | void *event_arg); | ||
| 57 | |||
| 58 | /* Shut down and free state for a channel. */ | ||
| 59 | extern void ldc_free(struct ldc_channel *lp); | ||
| 60 | |||
| 61 | /* Register TX and RX queues of the link with the hypervisor. */ | ||
| 62 | extern int ldc_bind(struct ldc_channel *lp, const char *name); | ||
| 63 | |||
| 64 | /* For non-RAW protocols we need to complete a handshake before | ||
| 65 | * communication can proceed. ldc_connect() does that, if the | ||
| 66 | * handshake completes successfully, an LDC_EVENT_UP event will | ||
| 67 | * be sent up to the driver. | ||
| 68 | */ | ||
| 69 | extern int ldc_connect(struct ldc_channel *lp); | ||
| 70 | extern int ldc_disconnect(struct ldc_channel *lp); | ||
| 71 | |||
| 72 | extern int ldc_state(struct ldc_channel *lp); | ||
| 73 | |||
| 74 | /* Read and write operations. Only valid when the link is up. */ | ||
| 75 | extern int ldc_write(struct ldc_channel *lp, const void *buf, | ||
| 76 | unsigned int size); | ||
| 77 | extern int ldc_read(struct ldc_channel *lp, void *buf, unsigned int size); | ||
| 78 | |||
| 79 | #define LDC_MAP_SHADOW 0x01 | ||
| 80 | #define LDC_MAP_DIRECT 0x02 | ||
| 81 | #define LDC_MAP_IO 0x04 | ||
| 82 | #define LDC_MAP_R 0x08 | ||
| 83 | #define LDC_MAP_W 0x10 | ||
| 84 | #define LDC_MAP_X 0x20 | ||
| 85 | #define LDC_MAP_RW (LDC_MAP_R | LDC_MAP_W) | ||
| 86 | #define LDC_MAP_RWX (LDC_MAP_R | LDC_MAP_W | LDC_MAP_X) | ||
| 87 | #define LDC_MAP_ALL 0x03f | ||
| 88 | |||
| 89 | struct ldc_trans_cookie { | ||
| 90 | u64 cookie_addr; | ||
| 91 | u64 cookie_size; | ||
| 92 | }; | ||
| 93 | |||
| 94 | struct scatterlist; | ||
| 95 | extern int ldc_map_sg(struct ldc_channel *lp, | ||
| 96 | struct scatterlist *sg, int num_sg, | ||
| 97 | struct ldc_trans_cookie *cookies, int ncookies, | ||
| 98 | unsigned int map_perm); | ||
| 99 | |||
| 100 | extern int ldc_map_single(struct ldc_channel *lp, | ||
| 101 | void *buf, unsigned int len, | ||
| 102 | struct ldc_trans_cookie *cookies, int ncookies, | ||
| 103 | unsigned int map_perm); | ||
| 104 | |||
| 105 | extern void ldc_unmap(struct ldc_channel *lp, struct ldc_trans_cookie *cookies, | ||
| 106 | int ncookies); | ||
| 107 | |||
| 108 | extern int ldc_copy(struct ldc_channel *lp, int copy_dir, | ||
| 109 | void *buf, unsigned int len, unsigned long offset, | ||
| 110 | struct ldc_trans_cookie *cookies, int ncookies); | ||
| 111 | |||
| 112 | static inline int ldc_get_dring_entry(struct ldc_channel *lp, | ||
| 113 | void *buf, unsigned int len, | ||
| 114 | unsigned long offset, | ||
| 115 | struct ldc_trans_cookie *cookies, | ||
| 116 | int ncookies) | ||
| 117 | { | ||
| 118 | return ldc_copy(lp, LDC_COPY_IN, buf, len, offset, cookies, ncookies); | ||
| 119 | } | ||
| 120 | |||
| 121 | static inline int ldc_put_dring_entry(struct ldc_channel *lp, | ||
| 122 | void *buf, unsigned int len, | ||
| 123 | unsigned long offset, | ||
| 124 | struct ldc_trans_cookie *cookies, | ||
| 125 | int ncookies) | ||
| 126 | { | ||
| 127 | return ldc_copy(lp, LDC_COPY_OUT, buf, len, offset, cookies, ncookies); | ||
| 128 | } | ||
| 129 | |||
| 130 | extern void *ldc_alloc_exp_dring(struct ldc_channel *lp, unsigned int len, | ||
| 131 | struct ldc_trans_cookie *cookies, | ||
| 132 | int *ncookies, unsigned int map_perm); | ||
| 133 | |||
| 134 | extern void ldc_free_exp_dring(struct ldc_channel *lp, void *buf, | ||
| 135 | unsigned int len, | ||
| 136 | struct ldc_trans_cookie *cookies, int ncookies); | ||
| 137 | |||
| 138 | #endif /* _SPARC64_LDC_H */ | ||
diff --git a/include/asm-sparc64/mdesc.h b/include/asm-sparc64/mdesc.h index c6383982b53d..e97c43133752 100644 --- a/include/asm-sparc64/mdesc.h +++ b/include/asm-sparc64/mdesc.h | |||
| @@ -2,38 +2,66 @@ | |||
| 2 | #define _SPARC64_MDESC_H | 2 | #define _SPARC64_MDESC_H |
| 3 | 3 | ||
| 4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
| 5 | #include <linux/cpumask.h> | ||
| 5 | #include <asm/prom.h> | 6 | #include <asm/prom.h> |
| 6 | 7 | ||
| 7 | struct mdesc_node; | 8 | struct mdesc_handle; |
| 8 | struct mdesc_arc { | 9 | |
| 9 | const char *name; | 10 | /* Machine description operations are to be surrounded by grab and |
| 10 | struct mdesc_node *arc; | 11 | * release calls. The mdesc_handle returned from the grab is |
| 11 | }; | 12 | * the first argument to all of the operational calls that work |
| 12 | 13 | * on mdescs. | |
| 13 | struct mdesc_node { | 14 | */ |
| 14 | const char *name; | 15 | extern struct mdesc_handle *mdesc_grab(void); |
| 15 | u64 node; | 16 | extern void mdesc_release(struct mdesc_handle *); |
| 16 | unsigned int unique_id; | 17 | |
| 17 | unsigned int num_arcs; | 18 | #define MDESC_NODE_NULL (~(u64)0) |
| 18 | unsigned int irqs[2]; | 19 | |
| 19 | struct property *properties; | 20 | extern u64 mdesc_node_by_name(struct mdesc_handle *handle, |
| 20 | struct mdesc_node *hash_next; | 21 | u64 from_node, const char *name); |
| 21 | struct mdesc_node *allnodes_next; | 22 | #define mdesc_for_each_node_by_name(__hdl, __node, __name) \ |
| 22 | struct mdesc_arc arcs[0]; | 23 | for (__node = mdesc_node_by_name(__hdl, MDESC_NODE_NULL, __name); \ |
| 23 | }; | 24 | (__node) != MDESC_NODE_NULL; \ |
| 24 | 25 | __node = mdesc_node_by_name(__hdl, __node, __name)) | |
| 25 | extern struct mdesc_node *md_find_node_by_name(struct mdesc_node *from, | 26 | |
| 26 | const char *name); | 27 | /* Access to property values returned from mdesc_get_property() are |
| 27 | #define md_for_each_node_by_name(__mn, __name) \ | 28 | * only valid inside of a mdesc_grab()/mdesc_release() sequence. |
| 28 | for (__mn = md_find_node_by_name(NULL, __name); __mn; \ | 29 | * Once mdesc_release() is called, the memory backed up by these |
| 29 | __mn = md_find_node_by_name(__mn, __name)) | 30 | * pointers may reference freed up memory. |
| 30 | 31 | * | |
| 31 | extern struct property *md_find_property(const struct mdesc_node *mp, | 32 | * Therefore callers must make copies of any property values |
| 32 | const char *name, | 33 | * they need. |
| 33 | int *lenp); | 34 | * |
| 34 | extern const void *md_get_property(const struct mdesc_node *mp, | 35 | * These same rules apply to mdesc_node_name(). |
| 35 | const char *name, | 36 | */ |
| 36 | int *lenp); | 37 | extern const void *mdesc_get_property(struct mdesc_handle *handle, |
| 38 | u64 node, const char *name, int *lenp); | ||
| 39 | extern const char *mdesc_node_name(struct mdesc_handle *hp, u64 node); | ||
| 40 | |||
| 41 | /* MD arc iteration, the standard sequence is: | ||
| 42 | * | ||
| 43 | * unsigned long arc; | ||
| 44 | * mdesc_for_each_arc(arc, handle, node, MDESC_ARC_TYPE_{FWD,BACK}) { | ||
| 45 | * unsigned long target = mdesc_arc_target(handle, arc); | ||
| 46 | * ... | ||
| 47 | * } | ||
| 48 | */ | ||
| 49 | |||
| 50 | #define MDESC_ARC_TYPE_FWD "fwd" | ||
| 51 | #define MDESC_ARC_TYPE_BACK "back" | ||
| 52 | |||
| 53 | extern u64 mdesc_next_arc(struct mdesc_handle *handle, u64 from, | ||
| 54 | const char *arc_type); | ||
| 55 | #define mdesc_for_each_arc(__arc, __hdl, __node, __type) \ | ||
| 56 | for (__arc = mdesc_next_arc(__hdl, __node, __type); \ | ||
| 57 | (__arc) != MDESC_NODE_NULL; \ | ||
| 58 | __arc = mdesc_next_arc(__hdl, __arc, __type)) | ||
| 59 | |||
| 60 | extern u64 mdesc_arc_target(struct mdesc_handle *hp, u64 arc); | ||
| 61 | |||
| 62 | extern void mdesc_update(void); | ||
| 63 | |||
| 64 | extern void mdesc_fill_in_cpu_data(cpumask_t mask); | ||
| 37 | 65 | ||
| 38 | extern void sun4v_mdesc_init(void); | 66 | extern void sun4v_mdesc_init(void); |
| 39 | 67 | ||
diff --git a/include/asm-sparc64/mmu_context.h b/include/asm-sparc64/mmu_context.h index 8d129032013e..9fc225ed5500 100644 --- a/include/asm-sparc64/mmu_context.h +++ b/include/asm-sparc64/mmu_context.h | |||
| @@ -76,6 +76,9 @@ static inline void switch_mm(struct mm_struct *old_mm, struct mm_struct *mm, str | |||
| 76 | unsigned long ctx_valid, flags; | 76 | unsigned long ctx_valid, flags; |
| 77 | int cpu; | 77 | int cpu; |
| 78 | 78 | ||
| 79 | if (unlikely(mm == &init_mm)) | ||
| 80 | return; | ||
| 81 | |||
| 79 | spin_lock_irqsave(&mm->context.lock, flags); | 82 | spin_lock_irqsave(&mm->context.lock, flags); |
| 80 | ctx_valid = CTX_VALID(mm->context); | 83 | ctx_valid = CTX_VALID(mm->context); |
| 81 | if (!ctx_valid) | 84 | if (!ctx_valid) |
diff --git a/include/asm-sparc64/power.h b/include/asm-sparc64/power.h new file mode 100644 index 000000000000..94495c1ac4f6 --- /dev/null +++ b/include/asm-sparc64/power.h | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #ifndef _SPARC64_POWER_H | ||
| 2 | #define _SPARC64_POWER_H | ||
| 3 | |||
| 4 | extern void wake_up_powerd(void); | ||
| 5 | extern int start_powerd(void); | ||
| 6 | |||
| 7 | #endif /* !(_SPARC64_POWER_H) */ | ||
diff --git a/include/asm-sparc64/smp.h b/include/asm-sparc64/smp.h index 4fb8c4bfb848..e8a96a31761b 100644 --- a/include/asm-sparc64/smp.h +++ b/include/asm-sparc64/smp.h | |||
| @@ -29,9 +29,6 @@ | |||
| 29 | #include <asm/bitops.h> | 29 | #include <asm/bitops.h> |
| 30 | #include <asm/atomic.h> | 30 | #include <asm/atomic.h> |
| 31 | 31 | ||
| 32 | extern cpumask_t phys_cpu_present_map; | ||
| 33 | #define cpu_possible_map phys_cpu_present_map | ||
| 34 | |||
| 35 | extern cpumask_t cpu_sibling_map[NR_CPUS]; | 32 | extern cpumask_t cpu_sibling_map[NR_CPUS]; |
| 36 | extern cpumask_t cpu_core_map[NR_CPUS]; | 33 | extern cpumask_t cpu_core_map[NR_CPUS]; |
| 37 | extern int sparc64_multi_core; | 34 | extern int sparc64_multi_core; |
| @@ -44,7 +41,12 @@ extern int hard_smp_processor_id(void); | |||
| 44 | #define raw_smp_processor_id() (current_thread_info()->cpu) | 41 | #define raw_smp_processor_id() (current_thread_info()->cpu) |
| 45 | 42 | ||
| 46 | extern void smp_fill_in_sib_core_maps(void); | 43 | extern void smp_fill_in_sib_core_maps(void); |
| 47 | extern unsigned char boot_cpu_id; | 44 | extern void cpu_play_dead(void); |
| 45 | |||
| 46 | #ifdef CONFIG_HOTPLUG_CPU | ||
| 47 | extern int __cpu_disable(void); | ||
| 48 | extern void __cpu_die(unsigned int cpu); | ||
| 49 | #endif | ||
| 48 | 50 | ||
| 49 | #endif /* !(__ASSEMBLY__) */ | 51 | #endif /* !(__ASSEMBLY__) */ |
| 50 | 52 | ||
| @@ -52,7 +54,6 @@ extern unsigned char boot_cpu_id; | |||
| 52 | 54 | ||
| 53 | #define hard_smp_processor_id() 0 | 55 | #define hard_smp_processor_id() 0 |
| 54 | #define smp_fill_in_sib_core_maps() do { } while (0) | 56 | #define smp_fill_in_sib_core_maps() do { } while (0) |
| 55 | #define boot_cpu_id (0) | ||
| 56 | 57 | ||
| 57 | #endif /* !(CONFIG_SMP) */ | 58 | #endif /* !(CONFIG_SMP) */ |
| 58 | 59 | ||
diff --git a/include/asm-sparc64/vio.h b/include/asm-sparc64/vio.h new file mode 100644 index 000000000000..83c96422e9d6 --- /dev/null +++ b/include/asm-sparc64/vio.h | |||
| @@ -0,0 +1,404 @@ | |||
| 1 | #ifndef _SPARC64_VIO_H | ||
| 2 | #define _SPARC64_VIO_H | ||
| 3 | |||
| 4 | #include <linux/kernel.h> | ||
| 5 | #include <linux/device.h> | ||
| 6 | #include <linux/mod_devicetable.h> | ||
| 7 | #include <linux/timer.h> | ||
| 8 | #include <linux/spinlock.h> | ||
| 9 | #include <linux/completion.h> | ||
| 10 | #include <linux/list.h> | ||
| 11 | |||
| 12 | #include <asm/ldc.h> | ||
| 13 | #include <asm/mdesc.h> | ||
| 14 | |||
| 15 | struct vio_msg_tag { | ||
| 16 | u8 type; | ||
| 17 | #define VIO_TYPE_CTRL 0x01 | ||
| 18 | #define VIO_TYPE_DATA 0x02 | ||
| 19 | #define VIO_TYPE_ERR 0x04 | ||
| 20 | |||
| 21 | u8 stype; | ||
| 22 | #define VIO_SUBTYPE_INFO 0x01 | ||
| 23 | #define VIO_SUBTYPE_ACK 0x02 | ||
| 24 | #define VIO_SUBTYPE_NACK 0x04 | ||
| 25 | |||
| 26 | u16 stype_env; | ||
| 27 | #define VIO_VER_INFO 0x0001 | ||
| 28 | #define VIO_ATTR_INFO 0x0002 | ||
| 29 | #define VIO_DRING_REG 0x0003 | ||
| 30 | #define VIO_DRING_UNREG 0x0004 | ||
| 31 | #define VIO_RDX 0x0005 | ||
| 32 | #define VIO_PKT_DATA 0x0040 | ||
| 33 | #define VIO_DESC_DATA 0x0041 | ||
| 34 | #define VIO_DRING_DATA 0x0042 | ||
| 35 | #define VNET_MCAST_INFO 0x0101 | ||
| 36 | |||
| 37 | u32 sid; | ||
| 38 | }; | ||
| 39 | |||
| 40 | struct vio_rdx { | ||
| 41 | struct vio_msg_tag tag; | ||
| 42 | u64 resv[6]; | ||
| 43 | }; | ||
| 44 | |||
| 45 | struct vio_ver_info { | ||
| 46 | struct vio_msg_tag tag; | ||
| 47 | u16 major; | ||
| 48 | u16 minor; | ||
| 49 | u8 dev_class; | ||
| 50 | #define VDEV_NETWORK 0x01 | ||
| 51 | #define VDEV_NETWORK_SWITCH 0x02 | ||
| 52 | #define VDEV_DISK 0x03 | ||
| 53 | #define VDEV_DISK_SERVER 0x04 | ||
| 54 | |||
| 55 | u8 resv1[3]; | ||
| 56 | u64 resv2[5]; | ||
| 57 | }; | ||
| 58 | |||
| 59 | struct vio_dring_register { | ||
| 60 | struct vio_msg_tag tag; | ||
| 61 | u64 dring_ident; | ||
| 62 | u32 num_descr; | ||
| 63 | u32 descr_size; | ||
| 64 | u16 options; | ||
| 65 | #define VIO_TX_DRING 0x0001 | ||
| 66 | #define VIO_RX_DRING 0x0002 | ||
| 67 | u16 resv; | ||
| 68 | u32 num_cookies; | ||
| 69 | struct ldc_trans_cookie cookies[0]; | ||
| 70 | }; | ||
| 71 | |||
| 72 | struct vio_dring_unregister { | ||
| 73 | struct vio_msg_tag tag; | ||
| 74 | u64 dring_ident; | ||
| 75 | u64 resv[5]; | ||
| 76 | }; | ||
| 77 | |||
| 78 | /* Data transfer modes */ | ||
| 79 | #define VIO_PKT_MODE 0x01 /* Packet based transfer */ | ||
| 80 | #define VIO_DESC_MODE 0x02 /* In-band descriptors */ | ||
| 81 | #define VIO_DRING_MODE 0x03 /* Descriptor rings */ | ||
| 82 | |||
| 83 | struct vio_dring_data { | ||
| 84 | struct vio_msg_tag tag; | ||
| 85 | u64 seq; | ||
| 86 | u64 dring_ident; | ||
| 87 | u32 start_idx; | ||
| 88 | u32 end_idx; | ||
| 89 | u8 state; | ||
| 90 | #define VIO_DRING_ACTIVE 0x01 | ||
| 91 | #define VIO_DRING_STOPPED 0x02 | ||
| 92 | |||
| 93 | u8 __pad1; | ||
| 94 | u16 __pad2; | ||
| 95 | u32 __pad3; | ||
| 96 | u64 __par4[2]; | ||
| 97 | }; | ||
| 98 | |||
| 99 | struct vio_dring_hdr { | ||
| 100 | u8 state; | ||
| 101 | #define VIO_DESC_FREE 0x01 | ||
| 102 | #define VIO_DESC_READY 0x02 | ||
| 103 | #define VIO_DESC_ACCEPTED 0x03 | ||
| 104 | #define VIO_DESC_DONE 0x04 | ||
| 105 | u8 ack; | ||
| 106 | #define VIO_ACK_ENABLE 0x01 | ||
| 107 | #define VIO_ACK_DISABLE 0x00 | ||
| 108 | |||
| 109 | u16 __pad1; | ||
| 110 | u32 __pad2; | ||
| 111 | }; | ||
| 112 | |||
| 113 | /* VIO disk specific structures and defines */ | ||
| 114 | struct vio_disk_attr_info { | ||
| 115 | struct vio_msg_tag tag; | ||
| 116 | u8 xfer_mode; | ||
| 117 | u8 vdisk_type; | ||
| 118 | #define VD_DISK_TYPE_SLICE 0x01 /* Slice in block device */ | ||
| 119 | #define VD_DISK_TYPE_DISK 0x02 /* Entire block device */ | ||
| 120 | u16 resv1; | ||
| 121 | u32 vdisk_block_size; | ||
| 122 | u64 operations; | ||
| 123 | u64 vdisk_size; | ||
| 124 | u64 max_xfer_size; | ||
| 125 | u64 resv2[2]; | ||
| 126 | }; | ||
| 127 | |||
| 128 | struct vio_disk_desc { | ||
| 129 | struct vio_dring_hdr hdr; | ||
| 130 | u64 req_id; | ||
| 131 | u8 operation; | ||
| 132 | #define VD_OP_BREAD 0x01 /* Block read */ | ||
| 133 | #define VD_OP_BWRITE 0x02 /* Block write */ | ||
| 134 | #define VD_OP_FLUSH 0x03 /* Flush disk contents */ | ||
| 135 | #define VD_OP_GET_WCE 0x04 /* Get write-cache status */ | ||
| 136 | #define VD_OP_SET_WCE 0x05 /* Enable/disable write-cache */ | ||
| 137 | #define VD_OP_GET_VTOC 0x06 /* Get VTOC */ | ||
| 138 | #define VD_OP_SET_VTOC 0x07 /* Set VTOC */ | ||
| 139 | #define VD_OP_GET_DISKGEOM 0x08 /* Get disk geometry */ | ||
| 140 | #define VD_OP_SET_DISKGEOM 0x09 /* Set disk geometry */ | ||
| 141 | #define VD_OP_SCSICMD 0x0a /* SCSI control command */ | ||
| 142 | #define VD_OP_GET_DEVID 0x0b /* Get device ID */ | ||
| 143 | #define VD_OP_GET_EFI 0x0c /* Get EFI */ | ||
| 144 | #define VD_OP_SET_EFI 0x0d /* Set EFI */ | ||
| 145 | u8 slice; | ||
| 146 | u16 resv1; | ||
| 147 | u32 status; | ||
| 148 | u64 offset; | ||
| 149 | u64 size; | ||
| 150 | u32 ncookies; | ||
| 151 | u32 resv2; | ||
| 152 | struct ldc_trans_cookie cookies[0]; | ||
| 153 | }; | ||
| 154 | |||
| 155 | #define VIO_DISK_VNAME_LEN 8 | ||
| 156 | #define VIO_DISK_ALABEL_LEN 128 | ||
| 157 | #define VIO_DISK_NUM_PART 8 | ||
| 158 | |||
| 159 | struct vio_disk_vtoc { | ||
| 160 | u8 volume_name[VIO_DISK_VNAME_LEN]; | ||
| 161 | u16 sector_size; | ||
| 162 | u16 num_partitions; | ||
| 163 | u8 ascii_label[VIO_DISK_ALABEL_LEN]; | ||
| 164 | struct { | ||
| 165 | u16 id; | ||
| 166 | u16 perm_flags; | ||
| 167 | u32 resv; | ||
| 168 | u64 start_block; | ||
| 169 | u64 num_blocks; | ||
| 170 | } partitions[VIO_DISK_NUM_PART]; | ||
| 171 | }; | ||
| 172 | |||
| 173 | struct vio_disk_geom { | ||
| 174 | u16 num_cyl; /* Num data cylinders */ | ||
| 175 | u16 alt_cyl; /* Num alternate cylinders */ | ||
| 176 | u16 beg_cyl; /* Cyl off of fixed head area */ | ||
| 177 | u16 num_hd; /* Num heads */ | ||
| 178 | u16 num_sec; /* Num sectors */ | ||
| 179 | u16 ifact; /* Interleave factor */ | ||
| 180 | u16 apc; /* Alts per cylinder (SCSI) */ | ||
| 181 | u16 rpm; /* Revolutions per minute */ | ||
| 182 | u16 phy_cyl; /* Num physical cylinders */ | ||
| 183 | u16 wr_skip; /* Num sects to skip, writes */ | ||
| 184 | u16 rd_skip; /* Num sects to skip, writes */ | ||
| 185 | }; | ||
| 186 | |||
| 187 | struct vio_disk_devid { | ||
| 188 | u16 resv; | ||
| 189 | u16 type; | ||
| 190 | u32 len; | ||
| 191 | char id[0]; | ||
| 192 | }; | ||
| 193 | |||
| 194 | struct vio_disk_efi { | ||
| 195 | u64 lba; | ||
| 196 | u64 len; | ||
| 197 | char data[0]; | ||
| 198 | }; | ||
| 199 | |||
| 200 | /* VIO net specific structures and defines */ | ||
| 201 | struct vio_net_attr_info { | ||
| 202 | struct vio_msg_tag tag; | ||
| 203 | u8 xfer_mode; | ||
| 204 | u8 addr_type; | ||
| 205 | #define VNET_ADDR_ETHERMAC 0x01 | ||
| 206 | u16 ack_freq; | ||
| 207 | u32 resv1; | ||
| 208 | u64 addr; | ||
| 209 | u64 mtu; | ||
| 210 | u64 resv2[3]; | ||
| 211 | }; | ||
| 212 | |||
| 213 | #define VNET_NUM_MCAST 7 | ||
| 214 | |||
| 215 | struct vio_net_mcast_info { | ||
| 216 | struct vio_msg_tag tag; | ||
| 217 | u8 set; | ||
| 218 | u8 count; | ||
| 219 | u8 mcast_addr[VNET_NUM_MCAST * 6]; | ||
| 220 | u32 resv; | ||
| 221 | }; | ||
| 222 | |||
| 223 | struct vio_net_desc { | ||
| 224 | struct vio_dring_hdr hdr; | ||
| 225 | u32 size; | ||
| 226 | u32 ncookies; | ||
| 227 | struct ldc_trans_cookie cookies[0]; | ||
| 228 | }; | ||
| 229 | |||
| 230 | #define VIO_MAX_RING_COOKIES 24 | ||
| 231 | |||
| 232 | struct vio_dring_state { | ||
| 233 | u64 ident; | ||
| 234 | void *base; | ||
| 235 | u64 snd_nxt; | ||
| 236 | u64 rcv_nxt; | ||
| 237 | u32 entry_size; | ||
| 238 | u32 num_entries; | ||
| 239 | u32 prod; | ||
| 240 | u32 cons; | ||
| 241 | u32 pending; | ||
| 242 | int ncookies; | ||
| 243 | struct ldc_trans_cookie cookies[VIO_MAX_RING_COOKIES]; | ||
| 244 | }; | ||
| 245 | |||
| 246 | static inline void *vio_dring_cur(struct vio_dring_state *dr) | ||
| 247 | { | ||
| 248 | return dr->base + (dr->entry_size * dr->prod); | ||
| 249 | } | ||
| 250 | |||
| 251 | static inline void *vio_dring_entry(struct vio_dring_state *dr, | ||
| 252 | unsigned int index) | ||
| 253 | { | ||
| 254 | return dr->base + (dr->entry_size * index); | ||
| 255 | } | ||
| 256 | |||
| 257 | static inline u32 vio_dring_avail(struct vio_dring_state *dr, | ||
| 258 | unsigned int ring_size) | ||
| 259 | { | ||
| 260 | /* Ensure build-time power-of-2. */ | ||
| 261 | BUILD_BUG_ON(ring_size & (ring_size - 1)); | ||
| 262 | |||
| 263 | return (dr->pending - | ||
| 264 | ((dr->prod - dr->cons) & (ring_size - 1))); | ||
| 265 | } | ||
| 266 | |||
| 267 | #define VIO_MAX_TYPE_LEN 64 | ||
| 268 | #define VIO_MAX_COMPAT_LEN 64 | ||
| 269 | |||
| 270 | struct vio_dev { | ||
| 271 | u64 mp; | ||
| 272 | struct device_node *dp; | ||
| 273 | |||
| 274 | char type[VIO_MAX_TYPE_LEN]; | ||
| 275 | char compat[VIO_MAX_COMPAT_LEN]; | ||
| 276 | int compat_len; | ||
| 277 | |||
| 278 | unsigned long channel_id; | ||
| 279 | |||
| 280 | unsigned int tx_irq; | ||
| 281 | unsigned int rx_irq; | ||
| 282 | |||
| 283 | struct device dev; | ||
| 284 | }; | ||
| 285 | |||
| 286 | struct vio_driver { | ||
| 287 | struct list_head node; | ||
| 288 | const struct vio_device_id *id_table; | ||
| 289 | int (*probe)(struct vio_dev *dev, const struct vio_device_id *id); | ||
| 290 | int (*remove)(struct vio_dev *dev); | ||
| 291 | void (*shutdown)(struct vio_dev *dev); | ||
| 292 | unsigned long driver_data; | ||
| 293 | struct device_driver driver; | ||
| 294 | }; | ||
| 295 | |||
| 296 | struct vio_version { | ||
| 297 | u16 major; | ||
| 298 | u16 minor; | ||
| 299 | }; | ||
| 300 | |||
| 301 | struct vio_driver_state; | ||
| 302 | struct vio_driver_ops { | ||
| 303 | int (*send_attr)(struct vio_driver_state *vio); | ||
| 304 | int (*handle_attr)(struct vio_driver_state *vio, void *pkt); | ||
| 305 | void (*handshake_complete)(struct vio_driver_state *vio); | ||
| 306 | }; | ||
| 307 | |||
| 308 | struct vio_completion { | ||
| 309 | struct completion com; | ||
| 310 | int err; | ||
| 311 | int waiting_for; | ||
| 312 | }; | ||
| 313 | |||
| 314 | struct vio_driver_state { | ||
| 315 | /* Protects VIO handshake and, optionally, driver private state. */ | ||
| 316 | spinlock_t lock; | ||
| 317 | |||
| 318 | struct ldc_channel *lp; | ||
| 319 | |||
| 320 | u32 _peer_sid; | ||
| 321 | u32 _local_sid; | ||
| 322 | struct vio_dring_state drings[2]; | ||
| 323 | #define VIO_DRIVER_TX_RING 0 | ||
| 324 | #define VIO_DRIVER_RX_RING 1 | ||
| 325 | |||
| 326 | u8 hs_state; | ||
| 327 | #define VIO_HS_INVALID 0x00 | ||
| 328 | #define VIO_HS_GOTVERS 0x01 | ||
| 329 | #define VIO_HS_GOT_ATTR 0x04 | ||
| 330 | #define VIO_HS_SENT_DREG 0x08 | ||
| 331 | #define VIO_HS_SENT_RDX 0x10 | ||
| 332 | #define VIO_HS_GOT_RDX_ACK 0x20 | ||
| 333 | #define VIO_HS_GOT_RDX 0x40 | ||
| 334 | #define VIO_HS_SENT_RDX_ACK 0x80 | ||
| 335 | #define VIO_HS_COMPLETE (VIO_HS_GOT_RDX_ACK | VIO_HS_SENT_RDX_ACK) | ||
| 336 | |||
| 337 | u8 dev_class; | ||
| 338 | |||
| 339 | u8 dr_state; | ||
| 340 | #define VIO_DR_STATE_TXREG 0x01 | ||
| 341 | #define VIO_DR_STATE_RXREG 0x02 | ||
| 342 | #define VIO_DR_STATE_TXREQ 0x10 | ||
| 343 | #define VIO_DR_STATE_RXREQ 0x20 | ||
| 344 | |||
| 345 | u8 debug; | ||
| 346 | #define VIO_DEBUG_HS 0x01 | ||
| 347 | #define VIO_DEBUG_DATA 0x02 | ||
| 348 | |||
| 349 | void *desc_buf; | ||
| 350 | unsigned int desc_buf_len; | ||
| 351 | |||
| 352 | struct vio_completion *cmp; | ||
| 353 | |||
| 354 | struct vio_dev *vdev; | ||
| 355 | |||
| 356 | struct timer_list timer; | ||
| 357 | |||
| 358 | struct vio_version ver; | ||
| 359 | |||
| 360 | struct vio_version *ver_table; | ||
| 361 | int ver_table_entries; | ||
| 362 | |||
| 363 | char *name; | ||
| 364 | |||
| 365 | struct vio_driver_ops *ops; | ||
| 366 | }; | ||
| 367 | |||
| 368 | #define viodbg(TYPE, f, a...) \ | ||
| 369 | do { if (vio->debug & VIO_DEBUG_##TYPE) \ | ||
| 370 | printk(KERN_INFO "vio: ID[%lu] " f, \ | ||
| 371 | vio->vdev->channel_id, ## a); \ | ||
| 372 | } while (0) | ||
| 373 | |||
| 374 | extern int vio_register_driver(struct vio_driver *drv); | ||
| 375 | extern void vio_unregister_driver(struct vio_driver *drv); | ||
| 376 | |||
| 377 | static inline struct vio_driver *to_vio_driver(struct device_driver *drv) | ||
| 378 | { | ||
| 379 | return container_of(drv, struct vio_driver, driver); | ||
| 380 | } | ||
| 381 | |||
| 382 | static inline struct vio_dev *to_vio_dev(struct device *dev) | ||
| 383 | { | ||
| 384 | return container_of(dev, struct vio_dev, dev); | ||
| 385 | } | ||
| 386 | |||
| 387 | extern int vio_ldc_send(struct vio_driver_state *vio, void *data, int len); | ||
| 388 | extern void vio_link_state_change(struct vio_driver_state *vio, int event); | ||
| 389 | extern void vio_conn_reset(struct vio_driver_state *vio); | ||
| 390 | extern int vio_control_pkt_engine(struct vio_driver_state *vio, void *pkt); | ||
| 391 | extern int vio_validate_sid(struct vio_driver_state *vio, | ||
| 392 | struct vio_msg_tag *tp); | ||
| 393 | extern u32 vio_send_sid(struct vio_driver_state *vio); | ||
| 394 | extern int vio_ldc_alloc(struct vio_driver_state *vio, | ||
| 395 | struct ldc_channel_config *base_cfg, void *event_arg); | ||
| 396 | extern void vio_ldc_free(struct vio_driver_state *vio); | ||
| 397 | extern int vio_driver_init(struct vio_driver_state *vio, struct vio_dev *vdev, | ||
| 398 | u8 dev_class, struct vio_version *ver_table, | ||
| 399 | int ver_table_size, struct vio_driver_ops *ops, | ||
| 400 | char *name); | ||
| 401 | |||
| 402 | extern void vio_port_up(struct vio_driver_state *vio); | ||
| 403 | |||
| 404 | #endif /* _SPARC64_VIO_H */ | ||
