[SPARC64]: Add basic infrastructure for MD add/remove notification.
authorDavid S. Miller <davem@sunset.davemloft.net>
Wed, 18 Jul 2007 04:37:35 +0000 (21:37 -0700)
committerDavid S. Miller <davem@sunset.davemloft.net>
Wed, 18 Jul 2007 08:19:51 +0000 (01:19 -0700)
And add dummy handlers for the VIO device layer.  These will be filled
in with real code after the vdc, vnet, and ds drivers are reworked to
have simpler dependencies on the VIO device tree.

Signed-off-by: David S. Miller <davem@davemloft.net>
arch/sparc64/kernel/mdesc.c
arch/sparc64/kernel/vio.c
include/asm-sparc64/mdesc.h

index de5310f..302ba5e 100644 (file)
@@ -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)
index 49569b4..d487be0 100644 (file)
@@ -172,6 +172,36 @@ struct device_node *cdev_node;
 static struct vio_dev *root_vdev;
 static u64 cdev_cfg_handle;
 
+static void vio_add(struct mdesc_handle *hp, u64 node)
+{
+       const char *name = mdesc_get_property(hp, node, "name", NULL);
+       const u64 *id = mdesc_get_property(hp, node, "id", NULL);
+
+       printk(KERN_ERR "VIO: Device add (%s) ID[%lx]\n",
+              name, *id);
+}
+
+static void vio_remove(struct mdesc_handle *hp, u64 node)
+{
+       const char *name = mdesc_get_property(hp, node, "name", NULL);
+       const u64 *id = mdesc_get_property(hp, node, "id", NULL);
+
+       printk(KERN_ERR "VIO: Device remove (%s) ID[%lx]\n",
+              name, *id);
+}
+
+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",
+};
+
 static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp,
                                  struct vio_dev *vdev)
 {
@@ -381,6 +411,9 @@ static int __init vio_init(void)
 
        cdev_cfg_handle = *cfg_handle;
 
+       mdesc_register_notifier(&vio_device_notifier);
+       mdesc_register_notifier(&vio_ds_notifier);
+
        create_devices(hp, root);
 
        mdesc_release(hp);
index e97c431..1acc727 100644 (file)
@@ -61,6 +61,16 @@ extern u64 mdesc_arc_target(struct mdesc_handle *hp, u64 arc);
 
 extern void mdesc_update(void);
 
+struct mdesc_notifier_client {
+       void (*add)(struct mdesc_handle *handle, u64 node);
+       void (*remove)(struct mdesc_handle *handle, u64 node);
+
+       const char                      *node_name;
+       struct mdesc_notifier_client    *next;
+};
+
+extern void mdesc_register_notifier(struct mdesc_notifier_client *client);
+
 extern void mdesc_fill_in_cpu_data(cpumask_t mask);
 
 extern void sun4v_mdesc_init(void);