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;
 }
 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);
                       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)
 
 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)
 {
 
        cdev_cfg_handle = *cfg_handle;
 
+       mdesc_register_notifier(&vio_device_notifier);
+       mdesc_register_notifier(&vio_ds_notifier);
+
        create_devices(hp, root);
 
        mdesc_release(hp);