diff options
| author | David S. Miller <davem@sunset.davemloft.net> | 2007-07-12 18:55:55 -0400 |
|---|---|---|
| committer | David S. Miller <davem@sunset.davemloft.net> | 2007-07-16 07:04:36 -0400 |
| commit | b3e13fbeb9ac1eb8e7b0791bf56e1775c692972b (patch) | |
| tree | 06539dfe2332c98c4d8b83450fe1e6055680ddc0 | |
| parent | 83292e0a9c3f1c326b28fbf8cb70a8ce81a98163 (diff) | |
[SPARC64]: Fix setting of variables in LDOM guest.
There is a special domain services capability for setting
variables in the OBP options node. Guests don't have permanent
store for the OBP variables like a normal system, so they are
instead maintained in the LDOM control node or in the SC.
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | arch/sparc64/kernel/ds.c | 187 | ||||
| -rw-r--r-- | arch/sparc64/prom/misc.c | 5 | ||||
| -rw-r--r-- | arch/sparc64/prom/tree.c | 13 | ||||
| -rw-r--r-- | include/asm-sparc64/ldc.h | 2 |
4 files changed, 192 insertions, 15 deletions
diff --git a/arch/sparc64/kernel/ds.c b/arch/sparc64/kernel/ds.c index 9c8839d1cffd..4e20ef232c51 100644 --- a/arch/sparc64/kernel/ds.c +++ b/arch/sparc64/kernel/ds.c | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include <linux/slab.h> | 11 | #include <linux/slab.h> |
| 12 | #include <linux/sched.h> | 12 | #include <linux/sched.h> |
| 13 | #include <linux/delay.h> | 13 | #include <linux/delay.h> |
| 14 | #include <linux/mutex.h> | ||
| 14 | 15 | ||
| 15 | #include <asm/ldc.h> | 16 | #include <asm/ldc.h> |
| 16 | #include <asm/vio.h> | 17 | #include <asm/vio.h> |
| @@ -171,7 +172,7 @@ static void md_update_data(struct ldc_channel *lp, | |||
| 171 | 172 | ||
| 172 | rp = (struct ds_md_update_req *) (dpkt + 1); | 173 | rp = (struct ds_md_update_req *) (dpkt + 1); |
| 173 | 174 | ||
| 174 | printk(KERN_ERR PFX "Machine description update.\n"); | 175 | printk(KERN_INFO PFX "Machine description update.\n"); |
| 175 | 176 | ||
| 176 | memset(&pkt, 0, sizeof(pkt)); | 177 | memset(&pkt, 0, sizeof(pkt)); |
| 177 | pkt.data.tag.type = DS_DATA; | 178 | pkt.data.tag.type = DS_DATA; |
| @@ -248,8 +249,8 @@ static void domain_panic_data(struct ldc_channel *lp, | |||
| 248 | 249 | ||
| 249 | rp = (struct ds_panic_req *) (dpkt + 1); | 250 | rp = (struct ds_panic_req *) (dpkt + 1); |
| 250 | 251 | ||
| 251 | printk(KERN_ERR PFX "Panic REQ [%lx], len=%d\n", | 252 | printk(KERN_ALERT PFX "Panic request from " |
| 252 | rp->req_num, len); | 253 | "LDOM manager received.\n"); |
| 253 | 254 | ||
| 254 | memset(&pkt, 0, sizeof(pkt)); | 255 | memset(&pkt, 0, sizeof(pkt)); |
| 255 | pkt.data.tag.type = DS_DATA; | 256 | pkt.data.tag.type = DS_DATA; |
| @@ -313,10 +314,60 @@ static void ds_pri_data(struct ldc_channel *lp, | |||
| 313 | 314 | ||
| 314 | rp = (struct ds_pri_msg *) (dpkt + 1); | 315 | rp = (struct ds_pri_msg *) (dpkt + 1); |
| 315 | 316 | ||
| 316 | printk(KERN_ERR PFX "PRI REQ [%lx:%lx], len=%d\n", | 317 | printk(KERN_INFO PFX "PRI REQ [%lx:%lx], len=%d\n", |
| 317 | rp->req_num, rp->type, len); | 318 | rp->req_num, rp->type, len); |
| 318 | } | 319 | } |
| 319 | 320 | ||
| 321 | struct ds_var_hdr { | ||
| 322 | __u32 type; | ||
| 323 | #define DS_VAR_SET_REQ 0x00 | ||
| 324 | #define DS_VAR_DELETE_REQ 0x01 | ||
| 325 | #define DS_VAR_SET_RESP 0x02 | ||
| 326 | #define DS_VAR_DELETE_RESP 0x03 | ||
| 327 | }; | ||
| 328 | |||
| 329 | struct ds_var_set_msg { | ||
| 330 | struct ds_var_hdr hdr; | ||
| 331 | char name_and_value[0]; | ||
| 332 | }; | ||
| 333 | |||
| 334 | struct ds_var_delete_msg { | ||
| 335 | struct ds_var_hdr hdr; | ||
| 336 | char name[0]; | ||
| 337 | }; | ||
| 338 | |||
| 339 | struct ds_var_resp { | ||
| 340 | struct ds_var_hdr hdr; | ||
| 341 | __u32 result; | ||
| 342 | #define DS_VAR_SUCCESS 0x00 | ||
| 343 | #define DS_VAR_NO_SPACE 0x01 | ||
| 344 | #define DS_VAR_INVALID_VAR 0x02 | ||
| 345 | #define DS_VAR_INVALID_VAL 0x03 | ||
| 346 | #define DS_VAR_NOT_PRESENT 0x04 | ||
| 347 | }; | ||
| 348 | |||
| 349 | static DEFINE_MUTEX(ds_var_mutex); | ||
| 350 | static int ds_var_doorbell; | ||
| 351 | static int ds_var_response; | ||
| 352 | |||
| 353 | static void ds_var_data(struct ldc_channel *lp, | ||
| 354 | struct ds_cap_state *dp, | ||
| 355 | void *buf, int len) | ||
| 356 | { | ||
| 357 | struct ds_data *dpkt = buf; | ||
| 358 | struct ds_var_resp *rp; | ||
| 359 | |||
| 360 | rp = (struct ds_var_resp *) (dpkt + 1); | ||
| 361 | |||
| 362 | if (rp->hdr.type != DS_VAR_SET_RESP && | ||
| 363 | rp->hdr.type != DS_VAR_DELETE_RESP) | ||
| 364 | return; | ||
| 365 | |||
| 366 | ds_var_response = rp->result; | ||
| 367 | wmb(); | ||
| 368 | ds_var_doorbell = 1; | ||
| 369 | } | ||
| 370 | |||
| 320 | struct ds_cap_state ds_states[] = { | 371 | struct ds_cap_state ds_states[] = { |
| 321 | { | 372 | { |
| 322 | .service_id = "md-update", | 373 | .service_id = "md-update", |
| @@ -338,17 +389,16 @@ struct ds_cap_state ds_states[] = { | |||
| 338 | .service_id = "pri", | 389 | .service_id = "pri", |
| 339 | .data = ds_pri_data, | 390 | .data = ds_pri_data, |
| 340 | }, | 391 | }, |
| 392 | { | ||
| 393 | .service_id = "var-config", | ||
| 394 | .data = ds_var_data, | ||
| 395 | }, | ||
| 396 | { | ||
| 397 | .service_id = "var-config-backup", | ||
| 398 | .data = ds_var_data, | ||
| 399 | }, | ||
| 341 | }; | 400 | }; |
| 342 | 401 | ||
| 343 | static struct ds_cap_state *find_cap(u64 handle) | ||
| 344 | { | ||
| 345 | unsigned int index = handle >> 32; | ||
| 346 | |||
| 347 | if (index >= ARRAY_SIZE(ds_states)) | ||
| 348 | return NULL; | ||
| 349 | return &ds_states[index]; | ||
| 350 | } | ||
| 351 | |||
| 352 | static DEFINE_SPINLOCK(ds_lock); | 402 | static DEFINE_SPINLOCK(ds_lock); |
| 353 | 403 | ||
| 354 | struct ds_info { | 404 | struct ds_info { |
| @@ -361,6 +411,115 @@ struct ds_info { | |||
| 361 | int rcv_buf_len; | 411 | int rcv_buf_len; |
| 362 | }; | 412 | }; |
| 363 | 413 | ||
| 414 | static struct ds_info *ds_info; | ||
| 415 | |||
| 416 | static struct ds_cap_state *find_cap(u64 handle) | ||
| 417 | { | ||
| 418 | unsigned int index = handle >> 32; | ||
| 419 | |||
| 420 | if (index >= ARRAY_SIZE(ds_states)) | ||
| 421 | return NULL; | ||
| 422 | return &ds_states[index]; | ||
| 423 | } | ||
| 424 | |||
| 425 | static struct ds_cap_state *find_cap_by_string(const char *name) | ||
| 426 | { | ||
| 427 | int i; | ||
| 428 | |||
| 429 | for (i = 0; i < ARRAY_SIZE(ds_states); i++) { | ||
| 430 | if (strcmp(ds_states[i].service_id, name)) | ||
| 431 | continue; | ||
| 432 | |||
| 433 | return &ds_states[i]; | ||
| 434 | } | ||
| 435 | return NULL; | ||
| 436 | } | ||
| 437 | |||
| 438 | void ldom_set_var(const char *var, const char *value) | ||
| 439 | { | ||
| 440 | struct ds_info *dp = ds_info; | ||
| 441 | struct ds_cap_state *cp; | ||
| 442 | |||
| 443 | cp = find_cap_by_string("var-config"); | ||
| 444 | if (cp->state != CAP_STATE_REGISTERED) | ||
| 445 | cp = find_cap_by_string("var-config-backup"); | ||
| 446 | |||
| 447 | if (cp->state == CAP_STATE_REGISTERED) { | ||
| 448 | union { | ||
| 449 | struct { | ||
| 450 | struct ds_data data; | ||
| 451 | struct ds_var_set_msg msg; | ||
| 452 | } header; | ||
| 453 | char all[512]; | ||
| 454 | } pkt; | ||
| 455 | unsigned long flags; | ||
| 456 | char *base, *p; | ||
| 457 | int msg_len, loops; | ||
| 458 | |||
| 459 | memset(&pkt, 0, sizeof(pkt)); | ||
| 460 | pkt.header.data.tag.type = DS_DATA; | ||
| 461 | pkt.header.data.handle = cp->handle; | ||
| 462 | pkt.header.msg.hdr.type = DS_VAR_SET_REQ; | ||
| 463 | base = p = &pkt.header.msg.name_and_value[0]; | ||
| 464 | strcpy(p, var); | ||
| 465 | p += strlen(var) + 1; | ||
| 466 | strcpy(p, value); | ||
| 467 | p += strlen(value) + 1; | ||
| 468 | |||
| 469 | msg_len = (sizeof(struct ds_data) + | ||
| 470 | sizeof(struct ds_var_set_msg) + | ||
| 471 | (p - base)); | ||
| 472 | msg_len = (msg_len + 3) & ~3; | ||
| 473 | pkt.header.data.tag.len = msg_len - sizeof(struct ds_msg_tag); | ||
| 474 | |||
| 475 | mutex_lock(&ds_var_mutex); | ||
| 476 | |||
| 477 | spin_lock_irqsave(&ds_lock, flags); | ||
| 478 | ds_var_doorbell = 0; | ||
| 479 | ds_var_response = -1; | ||
| 480 | |||
| 481 | ds_send(dp->lp, &pkt, msg_len); | ||
| 482 | spin_unlock_irqrestore(&ds_lock, flags); | ||
| 483 | |||
| 484 | loops = 1000; | ||
| 485 | while (ds_var_doorbell == 0) { | ||
| 486 | if (loops-- < 0) | ||
| 487 | break; | ||
| 488 | barrier(); | ||
| 489 | udelay(100); | ||
| 490 | } | ||
| 491 | |||
| 492 | mutex_unlock(&ds_var_mutex); | ||
| 493 | |||
| 494 | if (ds_var_doorbell == 0 || | ||
| 495 | ds_var_response != DS_VAR_SUCCESS) | ||
| 496 | printk(KERN_ERR PFX "var-config [%s:%s] " | ||
| 497 | "failed, response(%d).\n", | ||
| 498 | var, value, | ||
| 499 | ds_var_response); | ||
| 500 | } else { | ||
| 501 | printk(KERN_ERR PFX "var-config not registered so " | ||
| 502 | "could not set (%s) variable to (%s).\n", | ||
| 503 | var, value); | ||
| 504 | } | ||
| 505 | } | ||
| 506 | |||
| 507 | void ldom_reboot(const char *boot_command) | ||
| 508 | { | ||
| 509 | /* Don't bother with any of this if the boot_command | ||
| 510 | * is empty. | ||
| 511 | */ | ||
| 512 | if (boot_command && strlen(boot_command)) { | ||
| 513 | char full_boot_str[256]; | ||
| 514 | |||
| 515 | strcpy(full_boot_str, "boot "); | ||
| 516 | strcpy(full_boot_str + strlen("boot "), boot_command); | ||
| 517 | |||
| 518 | ldom_set_var("reboot-command", full_boot_str); | ||
| 519 | } | ||
| 520 | sun4v_mach_sir(); | ||
| 521 | } | ||
| 522 | |||
| 364 | static void ds_conn_reset(struct ds_info *dp) | 523 | static void ds_conn_reset(struct ds_info *dp) |
| 365 | { | 524 | { |
| 366 | printk(KERN_ERR PFX "ds_conn_reset() from %p\n", | 525 | printk(KERN_ERR PFX "ds_conn_reset() from %p\n", |
| @@ -594,6 +753,8 @@ static int __devinit ds_probe(struct vio_dev *vdev, | |||
| 594 | if (err) | 753 | if (err) |
| 595 | goto out_free_ldc; | 754 | goto out_free_ldc; |
| 596 | 755 | ||
| 756 | ds_info = dp; | ||
| 757 | |||
| 597 | start_powerd(); | 758 | start_powerd(); |
| 598 | 759 | ||
| 599 | return err; | 760 | return err; |
diff --git a/arch/sparc64/prom/misc.c b/arch/sparc64/prom/misc.c index f3e0c14e9eef..72d272c9de6b 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 | } |
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/include/asm-sparc64/ldc.h b/include/asm-sparc64/ldc.h index 3c91f269f9db..a21996c6b155 100644 --- a/include/asm-sparc64/ldc.h +++ b/include/asm-sparc64/ldc.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | #include <asm/hypervisor.h> | 4 | #include <asm/hypervisor.h> |
| 5 | 5 | ||
| 6 | extern int ldom_domaining_enabled; | 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); | ||
| 7 | 9 | ||
| 8 | /* The event handler will be evoked when link state changes | 10 | /* The event handler will be evoked when link state changes |
| 9 | * or data becomes available on the receive side. | 11 | * or data becomes available on the receive side. |
