[SPARC64]: Fix setting of variables in LDOM guest.
authorDavid S. Miller <davem@sunset.davemloft.net>
Thu, 12 Jul 2007 22:55:55 +0000 (15:55 -0700)
committerDavid S. Miller <davem@sunset.davemloft.net>
Mon, 16 Jul 2007 11:04:36 +0000 (04:04 -0700)
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>
arch/sparc64/kernel/ds.c
arch/sparc64/prom/misc.c
arch/sparc64/prom/tree.c
include/asm-sparc64/ldc.h

index 9c8839d1cffd4f62f98dff2cac7e1fa82d0a6e37..4e20ef232c51ffe0683f0ca3b628722a5172dd3c 100644 (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;
index f3e0c14e9eef001ba157307375555e2adf5efc57..72d272c9de6b5aec988b9f99b9990e027c02f6dc 100644 (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);
 }
index 500f05e2cfcb655af8ba1deeb0ae224192b560b9..17b7ecfe7ca95aed84232c40020509a50f48ddf6 100644 (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), 
index 3c91f269f9dbeb9f4ee76c296322f0c34df72c29..a21996c6b15574818428891e68ef2f484a0116a7 100644 (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.