struct ib_update_work {
        struct work_struct work;
-       struct ib_device  *device;
-       u8                 port_num;
-       bool               enforce_security;
+       struct ib_event event;
+       bool enforce_security;
 };
 
 union ib_gid zgid;
        event.element.port_num  = port;
        event.event             = IB_EVENT_GID_CHANGE;
 
-       ib_dispatch_event(&event);
+       ib_dispatch_event_clients(&event);
 }
 
 static const char * const gid_type_str[] = {
        return ret;
 }
 
-static void ib_cache_update(struct ib_device *device,
-                           u8                port,
-                           bool              enforce_security)
+static int
+ib_cache_update(struct ib_device *device, u8 port, bool enforce_security)
 {
        struct ib_port_attr       *tprops = NULL;
        struct ib_pkey_cache      *pkey_cache = NULL, *old_pkey_cache;
        int                        ret;
 
        if (!rdma_is_port_valid(device, port))
-               return;
+               return -EINVAL;
 
        tprops = kmalloc(sizeof *tprops, GFP_KERNEL);
        if (!tprops)
-               return;
+               return -ENOMEM;
 
        ret = ib_query_port(device, port, tprops);
        if (ret) {
        pkey_cache = kmalloc(struct_size(pkey_cache, table,
                                         tprops->pkey_tbl_len),
                             GFP_KERNEL);
-       if (!pkey_cache)
+       if (!pkey_cache) {
+               ret = -ENOMEM;
                goto err;
+       }
 
        pkey_cache->table_len = tprops->pkey_tbl_len;
 
 
        kfree(old_pkey_cache);
        kfree(tprops);
-       return;
+       return 0;
 
 err:
        kfree(pkey_cache);
        kfree(tprops);
+       return ret;
+}
+
+static void ib_cache_event_task(struct work_struct *_work)
+{
+       struct ib_update_work *work =
+               container_of(_work, struct ib_update_work, work);
+       int ret;
+
+       /* Before distributing the cache update event, first sync
+        * the cache.
+        */
+       ret = ib_cache_update(work->event.device, work->event.element.port_num,
+                             work->enforce_security);
+
+       /* GID event is notified already for individual GID entries by
+        * dispatch_gid_change_event(). Hence, notifiy for rest of the
+        * events.
+        */
+       if (!ret && work->event.event != IB_EVENT_GID_CHANGE)
+               ib_dispatch_event_clients(&work->event);
+
+       kfree(work);
 }
 
-static void ib_cache_task(struct work_struct *_work)
+static void ib_generic_event_task(struct work_struct *_work)
 {
        struct ib_update_work *work =
                container_of(_work, struct ib_update_work, work);
 
-       ib_cache_update(work->device,
-                       work->port_num,
-                       work->enforce_security);
+       ib_dispatch_event_clients(&work->event);
        kfree(work);
 }
 
-static void ib_cache_event(struct ib_event_handler *handler,
-                          struct ib_event *event)
+static bool is_cache_update_event(const struct ib_event *event)
+{
+       return (event->event == IB_EVENT_PORT_ERR    ||
+               event->event == IB_EVENT_PORT_ACTIVE ||
+               event->event == IB_EVENT_LID_CHANGE  ||
+               event->event == IB_EVENT_PKEY_CHANGE ||
+               event->event == IB_EVENT_CLIENT_REREGISTER ||
+               event->event == IB_EVENT_GID_CHANGE);
+}
+
+/**
+ * ib_dispatch_event - Dispatch an asynchronous event
+ * @event:Event to dispatch
+ *
+ * Low-level drivers must call ib_dispatch_event() to dispatch the
+ * event to all registered event handlers when an asynchronous event
+ * occurs.
+ */
+void ib_dispatch_event(const struct ib_event *event)
 {
        struct ib_update_work *work;
 
-       if (event->event == IB_EVENT_PORT_ERR    ||
-           event->event == IB_EVENT_PORT_ACTIVE ||
-           event->event == IB_EVENT_LID_CHANGE  ||
-           event->event == IB_EVENT_PKEY_CHANGE ||
-           event->event == IB_EVENT_CLIENT_REREGISTER ||
-           event->event == IB_EVENT_GID_CHANGE) {
-               work = kmalloc(sizeof *work, GFP_ATOMIC);
-               if (work) {
-                       INIT_WORK(&work->work, ib_cache_task);
-                       work->device   = event->device;
-                       work->port_num = event->element.port_num;
-                       if (event->event == IB_EVENT_PKEY_CHANGE ||
-                           event->event == IB_EVENT_GID_CHANGE)
-                               work->enforce_security = true;
-                       else
-                               work->enforce_security = false;
-
-                       queue_work(ib_wq, &work->work);
-               }
-       }
+       work = kzalloc(sizeof(*work), GFP_ATOMIC);
+       if (!work)
+               return;
+
+       if (is_cache_update_event(event))
+               INIT_WORK(&work->work, ib_cache_event_task);
+       else
+               INIT_WORK(&work->work, ib_generic_event_task);
+
+       work->event = *event;
+       if (event->event == IB_EVENT_PKEY_CHANGE ||
+           event->event == IB_EVENT_GID_CHANGE)
+               work->enforce_security = true;
+
+       queue_work(ib_wq, &work->work);
 }
+EXPORT_SYMBOL(ib_dispatch_event);
 
 int ib_cache_setup_one(struct ib_device *device)
 {
        rdma_for_each_port (device, p)
                ib_cache_update(device, p, true);
 
-       INIT_IB_EVENT_HANDLER(&device->cache.event_handler,
-                             device, ib_cache_event);
-       ib_register_event_handler(&device->cache.event_handler);
        return 0;
 }
 
 
 void ib_cache_cleanup_one(struct ib_device *device)
 {
-       /* The cleanup function unregisters the event handler,
-        * waits for all in-progress workqueue elements and cleans
-        * up the GID cache. This function should be called after
-        * the device was removed from the devices list and all
-        * clients were removed, so the cache exists but is
+       /* The cleanup function waits for all in-progress workqueue
+        * elements and cleans up the GID cache. This function should be
+        * called after the device was removed from the devices list and
+        * all clients were removed, so the cache exists but is
         * non-functional and shouldn't be updated anymore.
         */
-       ib_unregister_event_handler(&device->cache.event_handler);
        flush_workqueue(ib_wq);
        gid_table_cleanup_one(device);
 
 
 
        INIT_LIST_HEAD(&device->event_handler_list);
        spin_lock_init(&device->event_handler_lock);
+       init_rwsem(&device->event_handler_rwsem);
        mutex_init(&device->unregistration_lock);
        /*
         * client_data needs to be alloc because we don't want our mark to be
  *
  * ib_register_event_handler() registers an event handler that will be
  * called back when asynchronous IB events occur (as defined in
- * chapter 11 of the InfiniBand Architecture Specification).  This
- * callback may occur in interrupt context.
+ * chapter 11 of the InfiniBand Architecture Specification). This
+ * callback occurs in workqueue context.
  */
 void ib_register_event_handler(struct ib_event_handler *event_handler)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&event_handler->device->event_handler_lock, flags);
+       down_write(&event_handler->device->event_handler_rwsem);
        list_add_tail(&event_handler->list,
                      &event_handler->device->event_handler_list);
-       spin_unlock_irqrestore(&event_handler->device->event_handler_lock, flags);
+       up_write(&event_handler->device->event_handler_rwsem);
 }
 EXPORT_SYMBOL(ib_register_event_handler);
 
  */
 void ib_unregister_event_handler(struct ib_event_handler *event_handler)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&event_handler->device->event_handler_lock, flags);
+       down_write(&event_handler->device->event_handler_rwsem);
        list_del(&event_handler->list);
-       spin_unlock_irqrestore(&event_handler->device->event_handler_lock, flags);
+       up_write(&event_handler->device->event_handler_rwsem);
 }
 EXPORT_SYMBOL(ib_unregister_event_handler);
 
-/**
- * ib_dispatch_event - Dispatch an asynchronous event
- * @event:Event to dispatch
- *
- * Low-level drivers must call ib_dispatch_event() to dispatch the
- * event to all registered event handlers when an asynchronous event
- * occurs.
- */
-void ib_dispatch_event(struct ib_event *event)
+void ib_dispatch_event_clients(struct ib_event *event)
 {
-       unsigned long flags;
        struct ib_event_handler *handler;
 
-       spin_lock_irqsave(&event->device->event_handler_lock, flags);
+       down_read(&event->device->event_handler_rwsem);
 
        list_for_each_entry(handler, &event->device->event_handler_list, list)
                handler->handler(handler, event);
 
-       spin_unlock_irqrestore(&event->device->event_handler_lock, flags);
+       up_read(&event->device->event_handler_rwsem);
 }
-EXPORT_SYMBOL(ib_dispatch_event);
 
 static int iw_query_port(struct ib_device *device,
                           u8 port_num,