aboutsummaryrefslogtreecommitdiff
path: root/arch/sparc64/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sparc64/kernel')
-rw-r--r--arch/sparc64/kernel/ds.c19
-rw-r--r--arch/sparc64/kernel/mdesc.c78
-rw-r--r--arch/sparc64/kernel/vio.c94
-rw-r--r--arch/sparc64/kernel/viohs.c30
4 files changed, 185 insertions, 36 deletions
diff --git a/arch/sparc64/kernel/ds.c b/arch/sparc64/kernel/ds.c
index ba01533f4e0..fa1f04d756a 100644
--- a/arch/sparc64/kernel/ds.c
+++ b/arch/sparc64/kernel/ds.c
@@ -1013,6 +1013,19 @@ static void ds_up(struct ds_info *dp)
dp->hs_state = DS_HS_START;
}
+static void ds_reset(struct ds_info *dp)
+{
+ int i;
+
+ dp->hs_state = 0;
+
+ for (i = 0; i < ARRAY_SIZE(ds_states); i++) {
+ struct ds_cap_state *cp = &ds_states[i];
+
+ cp->state = CAP_STATE_UNKNOWN;
+ }
+}
+
static void ds_event(void *arg, int event)
{
struct ds_info *dp = arg;
@@ -1028,6 +1041,12 @@ static void ds_event(void *arg, int event)
return;
}
+ if (event == LDC_EVENT_RESET) {
+ ds_reset(dp);
+ spin_unlock_irqrestore(&ds_lock, flags);
+ return;
+ }
+
if (event != LDC_EVENT_DATA_READY) {
printk(KERN_WARNING PFX "Unexpected LDC event %d\n", event);
spin_unlock_irqrestore(&ds_lock, flags);
diff --git a/arch/sparc64/kernel/mdesc.c b/arch/sparc64/kernel/mdesc.c
index de5310ffdb4..302ba5e5a0b 100644
--- a/arch/sparc64/kernel/mdesc.c
+++ b/arch/sparc64/kernel/mdesc.c
@@ -137,7 +137,7 @@ static struct mdesc_handle *mdesc_kmalloc(unsigned int mdesc_size)
sizeof(struct mdesc_hdr) +
mdesc_size);
- base = kmalloc(handle_size + 15, GFP_KERNEL);
+ base = kmalloc(handle_size + 15, GFP_KERNEL | __GFP_NOFAIL);
if (base) {
struct mdesc_handle *hp;
unsigned long addr;
@@ -214,18 +214,83 @@ void mdesc_release(struct mdesc_handle *hp)
}
EXPORT_SYMBOL(mdesc_release);
+static DEFINE_MUTEX(mdesc_mutex);
+static struct mdesc_notifier_client *client_list;
+
+void mdesc_register_notifier(struct mdesc_notifier_client *client)
+{
+ u64 node;
+
+ mutex_lock(&mdesc_mutex);
+ client->next = client_list;
+ client_list = client;
+
+ mdesc_for_each_node_by_name(cur_mdesc, node, client->node_name)
+ client->add(cur_mdesc, node);
+
+ mutex_unlock(&mdesc_mutex);
+}
+
+/* Run 'func' on nodes which are in A but not in B. */
+static void invoke_on_missing(const char *name,
+ struct mdesc_handle *a,
+ struct mdesc_handle *b,
+ void (*func)(struct mdesc_handle *, u64))
+{
+ u64 node;
+
+ mdesc_for_each_node_by_name(a, node, name) {
+ const u64 *id = mdesc_get_property(a, node, "id", NULL);
+ int found = 0;
+ u64 fnode;
+
+ mdesc_for_each_node_by_name(b, fnode, name) {
+ const u64 *fid = mdesc_get_property(b, fnode,
+ "id", NULL);
+
+ if (*id == *fid) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ func(a, node);
+ }
+}
+
+static void notify_one(struct mdesc_notifier_client *p,
+ struct mdesc_handle *old_hp,
+ struct mdesc_handle *new_hp)
+{
+ invoke_on_missing(p->node_name, old_hp, new_hp, p->remove);
+ invoke_on_missing(p->node_name, new_hp, old_hp, p->add);
+}
+
+static void mdesc_notify_clients(struct mdesc_handle *old_hp,
+ struct mdesc_handle *new_hp)
+{
+ struct mdesc_notifier_client *p = client_list;
+
+ while (p) {
+ notify_one(p, old_hp, new_hp);
+ p = p->next;
+ }
+}
+
void mdesc_update(void)
{
unsigned long len, real_len, status;
struct mdesc_handle *hp, *orig_hp;
unsigned long flags;
+ mutex_lock(&mdesc_mutex);
+
(void) sun4v_mach_desc(0UL, 0UL, &len);
hp = mdesc_alloc(len, &kmalloc_mdesc_memops);
if (!hp) {
printk(KERN_ERR "MD: mdesc alloc fails\n");
- return;
+ goto out;
}
status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len);
@@ -234,18 +299,25 @@ void mdesc_update(void)
status);
atomic_dec(&hp->refcnt);
mdesc_free(hp);
- return;
+ goto out;
}
spin_lock_irqsave(&mdesc_lock, flags);
orig_hp = cur_mdesc;
cur_mdesc = hp;
+ spin_unlock_irqrestore(&mdesc_lock, flags);
+ mdesc_notify_clients(orig_hp, hp);
+
+ spin_lock_irqsave(&mdesc_lock, flags);
if (atomic_dec_and_test(&orig_hp->refcnt))
mdesc_free(orig_hp);
else
list_add(&orig_hp->list, &mdesc_zombie_list);
spin_unlock_irqrestore(&mdesc_lock, flags);
+
+out:
+ mutex_unlock(&mdesc_mutex);
}
static struct mdesc_elem *node_block(struct mdesc_hdr *mdesc)
diff --git a/arch/sparc64/kernel/vio.c b/arch/sparc64/kernel/vio.c
index 49569b44ea1..8d3cc4fdb55 100644
--- a/arch/sparc64/kernel/vio.c
+++ b/arch/sparc64/kernel/vio.c
@@ -201,10 +201,11 @@ static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp,
static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
struct device *parent)
{
- const char *type, *compat;
+ const char *type, *compat, *bus_id_name;
struct device_node *dp;
struct vio_dev *vdev;
int err, tlen, clen;
+ const u64 *id;
type = mdesc_get_property(hp, mp, "device-type", &tlen);
if (!type) {
@@ -220,6 +221,16 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
return NULL;
}
+ bus_id_name = type;
+ if (!strcmp(type, "domain-services-port"))
+ bus_id_name = "ds";
+
+ if (strlen(bus_id_name) >= KOBJ_NAME_LEN - 4) {
+ printk(KERN_ERR "VIO: bus_id_name [%s] is too long.\n",
+ bus_id_name);
+ return NULL;
+ }
+
compat = mdesc_get_property(hp, mp, "device-type", &clen);
if (!compat) {
clen = 0;
@@ -249,7 +260,14 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
vio_fill_channel_info(hp, mp, vdev);
- snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%lx", mp);
+ id = mdesc_get_property(hp, mp, "id", NULL);
+ if (!id)
+ snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%s",
+ bus_id_name);
+ else
+ snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%s-%lu",
+ bus_id_name, *id);
+
vdev->dev.parent = parent;
vdev->dev.bus = &vio_bus_type;
vdev->dev.release = vio_dev_release;
@@ -269,6 +287,8 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
}
vdev->dp = dp;
+ printk(KERN_ERR "VIO: Adding device %s\n", vdev->dev.bus_id);
+
err = device_register(&vdev->dev);
if (err) {
printk(KERN_ERR "VIO: Could not register device %s, err=%d\n",
@@ -283,46 +303,46 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
return vdev;
}
-static void walk_tree(struct mdesc_handle *hp, u64 n, struct vio_dev *parent)
+static void vio_add(struct mdesc_handle *hp, u64 node)
{
- u64 a;
-
- mdesc_for_each_arc(a, hp, n, MDESC_ARC_TYPE_FWD) {
- struct vio_dev *vdev;
- u64 target;
-
- target = mdesc_arc_target(hp, a);
- vdev = vio_create_one(hp, target, &parent->dev);
- if (vdev)
- walk_tree(hp, target, vdev);
- }
+ (void) vio_create_one(hp, node, &root_vdev->dev);
}
-static void create_devices(struct mdesc_handle *hp, u64 root)
+static int vio_md_node_match(struct device *dev, void *arg)
{
- u64 mp;
+ struct vio_dev *vdev = to_vio_dev(dev);
- root_vdev = vio_create_one(hp, root, NULL);
- if (!root_vdev) {
- printk(KERN_ERR "VIO: Coult not create root device.\n");
- return;
- }
+ if (vdev->mp == (u64) arg)
+ return 1;
- walk_tree(hp, root, root_vdev);
+ return 0;
+}
+
+static void vio_remove(struct mdesc_handle *hp, u64 node)
+{
+ struct device *dev;
- /* Domain services is odd as it doesn't sit underneath the
- * channel-devices node, so we plug it in manually.
- */
- mp = mdesc_node_by_name(hp, MDESC_NODE_NULL, "domain-services");
- if (mp != MDESC_NODE_NULL) {
- struct vio_dev *parent = vio_create_one(hp, mp,
- &root_vdev->dev);
+ dev = device_find_child(&root_vdev->dev, (void *) node,
+ vio_md_node_match);
+ if (dev) {
+ printk(KERN_INFO "VIO: Removing device %s\n", dev->bus_id);
- if (parent)
- walk_tree(hp, mp, parent);
+ device_unregister(dev);
}
}
+static struct mdesc_notifier_client vio_device_notifier = {
+ .add = vio_add,
+ .remove = vio_remove,
+ .node_name = "virtual-device-port",
+};
+
+static struct mdesc_notifier_client vio_ds_notifier = {
+ .add = vio_add,
+ .remove = vio_remove,
+ .node_name = "domain-services-port",
+};
+
const char *channel_devices_node = "channel-devices";
const char *channel_devices_compat = "SUNW,sun4v-channel-devices";
const char *cfg_handle_prop = "cfg-handle";
@@ -381,11 +401,19 @@ static int __init vio_init(void)
cdev_cfg_handle = *cfg_handle;
- create_devices(hp, root);
+ root_vdev = vio_create_one(hp, root, NULL);
+ err = -ENODEV;
+ if (!root_vdev) {
+ printk(KERN_ERR "VIO: Coult not create root device.\n");
+ goto out_release;
+ }
+
+ mdesc_register_notifier(&vio_device_notifier);
+ mdesc_register_notifier(&vio_ds_notifier);
mdesc_release(hp);
- return 0;
+ return err;
out_release:
mdesc_release(hp);
diff --git a/arch/sparc64/kernel/viohs.c b/arch/sparc64/kernel/viohs.c
index 15613add45d..09126fc338b 100644
--- a/arch/sparc64/kernel/viohs.c
+++ b/arch/sparc64/kernel/viohs.c
@@ -78,6 +78,24 @@ static int start_handshake(struct vio_driver_state *vio)
return 0;
}
+static void flush_rx_dring(struct vio_driver_state *vio)
+{
+ struct vio_dring_state *dr;
+ u64 ident;
+
+ BUG_ON(!(vio->dr_state & VIO_DR_STATE_RXREG));
+
+ dr = &vio->drings[VIO_DRIVER_RX_RING];
+ ident = dr->ident;
+
+ BUG_ON(!vio->desc_buf);
+ kfree(vio->desc_buf);
+ vio->desc_buf = NULL;
+
+ memset(dr, 0, sizeof(*dr));
+ dr->ident = ident;
+}
+
void vio_link_state_change(struct vio_driver_state *vio, int event)
{
if (event == LDC_EVENT_UP) {
@@ -98,6 +116,16 @@ void vio_link_state_change(struct vio_driver_state *vio, int event)
break;
}
start_handshake(vio);
+ } else if (event == LDC_EVENT_RESET) {
+ vio->hs_state = VIO_HS_INVALID;
+
+ if (vio->dr_state & VIO_DR_STATE_RXREG)
+ flush_rx_dring(vio);
+
+ vio->dr_state = 0x00;
+ memset(&vio->ver, 0, sizeof(vio->ver));
+
+ ldc_disconnect(vio->lp);
}
}
EXPORT_SYMBOL(vio_link_state_change);
@@ -396,6 +424,8 @@ static int process_dreg_info(struct vio_driver_state *vio,
if (vio->dr_state & VIO_DR_STATE_RXREG)
goto send_nack;
+ BUG_ON(vio->desc_buf);
+
vio->desc_buf = kzalloc(pkt->descr_size, GFP_ATOMIC);
if (!vio->desc_buf)
goto send_nack;