[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>
This commit is contained in:
David S. Miller 2007-07-12 15:55:55 -07:00
parent 83292e0a9c
commit b3e13fbeb9
4 changed files with 192 additions and 15 deletions

View File

@ -11,6 +11,7 @@
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <asm/ldc.h>
#include <asm/vio.h>
@ -171,7 +172,7 @@ static void md_update_data(struct ldc_channel *lp,
rp = (struct ds_md_update_req *) (dpkt + 1);
printk(KERN_ERR PFX "Machine description update.\n");
printk(KERN_INFO PFX "Machine description update.\n");
memset(&pkt, 0, sizeof(pkt));
pkt.data.tag.type = DS_DATA;
@ -248,8 +249,8 @@ static void domain_panic_data(struct ldc_channel *lp,
rp = (struct ds_panic_req *) (dpkt + 1);
printk(KERN_ERR PFX "Panic REQ [%lx], len=%d\n",
rp->req_num, len);
printk(KERN_ALERT PFX "Panic request from "
"LDOM manager received.\n");
memset(&pkt, 0, sizeof(pkt));
pkt.data.tag.type = DS_DATA;
@ -313,10 +314,60 @@ static void ds_pri_data(struct ldc_channel *lp,
rp = (struct ds_pri_msg *) (dpkt + 1);
printk(KERN_ERR PFX "PRI REQ [%lx:%lx], len=%d\n",
printk(KERN_INFO PFX "PRI REQ [%lx:%lx], len=%d\n",
rp->req_num, rp->type, len);
}
struct ds_var_hdr {
__u32 type;
#define DS_VAR_SET_REQ 0x00
#define DS_VAR_DELETE_REQ 0x01
#define DS_VAR_SET_RESP 0x02
#define DS_VAR_DELETE_RESP 0x03
};
struct ds_var_set_msg {
struct ds_var_hdr hdr;
char name_and_value[0];
};
struct ds_var_delete_msg {
struct ds_var_hdr hdr;
char name[0];
};
struct ds_var_resp {
struct ds_var_hdr hdr;
__u32 result;
#define DS_VAR_SUCCESS 0x00
#define DS_VAR_NO_SPACE 0x01
#define DS_VAR_INVALID_VAR 0x02
#define DS_VAR_INVALID_VAL 0x03
#define DS_VAR_NOT_PRESENT 0x04
};
static DEFINE_MUTEX(ds_var_mutex);
static int ds_var_doorbell;
static int ds_var_response;
static void ds_var_data(struct ldc_channel *lp,
struct ds_cap_state *dp,
void *buf, int len)
{
struct ds_data *dpkt = buf;
struct ds_var_resp *rp;
rp = (struct ds_var_resp *) (dpkt + 1);
if (rp->hdr.type != DS_VAR_SET_RESP &&
rp->hdr.type != DS_VAR_DELETE_RESP)
return;
ds_var_response = rp->result;
wmb();
ds_var_doorbell = 1;
}
struct ds_cap_state ds_states[] = {
{
.service_id = "md-update",
@ -338,17 +389,16 @@ struct ds_cap_state ds_states[] = {
.service_id = "pri",
.data = ds_pri_data,
},
{
.service_id = "var-config",
.data = ds_var_data,
},
{
.service_id = "var-config-backup",
.data = ds_var_data,
},
};
static struct ds_cap_state *find_cap(u64 handle)
{
unsigned int index = handle >> 32;
if (index >= ARRAY_SIZE(ds_states))
return NULL;
return &ds_states[index];
}
static DEFINE_SPINLOCK(ds_lock);
struct ds_info {
@ -361,6 +411,115 @@ struct ds_info {
int rcv_buf_len;
};
static struct ds_info *ds_info;
static struct ds_cap_state *find_cap(u64 handle)
{
unsigned int index = handle >> 32;
if (index >= ARRAY_SIZE(ds_states))
return NULL;
return &ds_states[index];
}
static struct ds_cap_state *find_cap_by_string(const char *name)
{
int i;
for (i = 0; i < ARRAY_SIZE(ds_states); i++) {
if (strcmp(ds_states[i].service_id, name))
continue;
return &ds_states[i];
}
return NULL;
}
void ldom_set_var(const char *var, const char *value)
{
struct ds_info *dp = ds_info;
struct ds_cap_state *cp;
cp = find_cap_by_string("var-config");
if (cp->state != CAP_STATE_REGISTERED)
cp = find_cap_by_string("var-config-backup");
if (cp->state == CAP_STATE_REGISTERED) {
union {
struct {
struct ds_data data;
struct ds_var_set_msg msg;
} header;
char all[512];
} pkt;
unsigned long flags;
char *base, *p;
int msg_len, loops;
memset(&pkt, 0, sizeof(pkt));
pkt.header.data.tag.type = DS_DATA;
pkt.header.data.handle = cp->handle;
pkt.header.msg.hdr.type = DS_VAR_SET_REQ;
base = p = &pkt.header.msg.name_and_value[0];
strcpy(p, var);
p += strlen(var) + 1;
strcpy(p, value);
p += strlen(value) + 1;
msg_len = (sizeof(struct ds_data) +
sizeof(struct ds_var_set_msg) +
(p - base));
msg_len = (msg_len + 3) & ~3;
pkt.header.data.tag.len = msg_len - sizeof(struct ds_msg_tag);
mutex_lock(&ds_var_mutex);
spin_lock_irqsave(&ds_lock, flags);
ds_var_doorbell = 0;
ds_var_response = -1;
ds_send(dp->lp, &pkt, msg_len);
spin_unlock_irqrestore(&ds_lock, flags);
loops = 1000;
while (ds_var_doorbell == 0) {
if (loops-- < 0)
break;
barrier();
udelay(100);
}
mutex_unlock(&ds_var_mutex);
if (ds_var_doorbell == 0 ||
ds_var_response != DS_VAR_SUCCESS)
printk(KERN_ERR PFX "var-config [%s:%s] "
"failed, response(%d).\n",
var, value,
ds_var_response);
} else {
printk(KERN_ERR PFX "var-config not registered so "
"could not set (%s) variable to (%s).\n",
var, value);
}
}
void ldom_reboot(const char *boot_command)
{
/* Don't bother with any of this if the boot_command
* is empty.
*/
if (boot_command && strlen(boot_command)) {
char full_boot_str[256];
strcpy(full_boot_str, "boot ");
strcpy(full_boot_str + strlen("boot "), boot_command);
ldom_set_var("reboot-command", full_boot_str);
}
sun4v_mach_sir();
}
static void ds_conn_reset(struct ds_info *dp)
{
printk(KERN_ERR PFX "ds_conn_reset() from %p\n",
@ -594,6 +753,8 @@ static int __devinit ds_probe(struct vio_dev *vdev,
if (err)
goto out_free_ldc;
ds_info = dp;
start_powerd();
return err;

View File

@ -14,6 +14,7 @@
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/system.h>
#include <asm/ldc.h>
int prom_service_exists(const char *service_name)
{
@ -37,6 +38,10 @@ void prom_sun4v_guest_soft_state(void)
/* Reset and reboot the machine with the command 'bcommand'. */
void prom_reboot(const char *bcommand)
{
#ifdef CONFIG_SUN_LDOMS
if (ldom_domaining_enabled)
ldom_reboot(bcommand);
#endif
p1275_cmd("boot", P1275_ARG(0, P1275_ARG_IN_STRING) |
P1275_INOUT(1, 0), bcommand);
}

View File

@ -13,6 +13,7 @@
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/ldc.h>
/* Return the child of node 'node' or zero if no this node has no
* direct descendent.
@ -261,9 +262,17 @@ int prom_node_has_property(int node, const char *prop)
int
prom_setprop(int node, const char *pname, char *value, int size)
{
if(size == 0) return 0;
if((pname == 0) || (value == 0)) return 0;
if (size == 0)
return 0;
if ((pname == 0) || (value == 0))
return 0;
#ifdef CONFIG_SUN_LDOMS
if (ldom_domaining_enabled) {
ldom_set_var(pname, value);
return 0;
}
#endif
return p1275_cmd ("setprop", P1275_ARG(1,P1275_ARG_IN_STRING)|
P1275_ARG(2,P1275_ARG_IN_BUF)|
P1275_INOUT(4, 1),

View File

@ -4,6 +4,8 @@
#include <asm/hypervisor.h>
extern int ldom_domaining_enabled;
extern void ldom_set_var(const char *var, const char *value);
extern void ldom_reboot(const char *boot_command);
/* The event handler will be evoked when link state changes
* or data becomes available on the receive side.