return ret;
 }
+
+int uverbs_finalize_objects(struct uverbs_attr_bundle *attrs_bundle,
+                           struct uverbs_attr_spec_hash * const *spec_hash,
+                           size_t num,
+                           bool commit)
+{
+       unsigned int i;
+       int ret = 0;
+
+       for (i = 0; i < num; i++) {
+               struct uverbs_attr_bundle_hash *curr_bundle =
+                       &attrs_bundle->hash[i];
+               const struct uverbs_attr_spec_hash *curr_spec_bucket =
+                       spec_hash[i];
+               unsigned int j;
+
+               for (j = 0; j < curr_bundle->num_attrs; j++) {
+                       struct uverbs_attr *attr;
+                       const struct uverbs_attr_spec *spec;
+
+                       if (!uverbs_attr_is_valid_in_hash(curr_bundle, j))
+                               continue;
+
+                       attr = &curr_bundle->attrs[j];
+                       spec = &curr_spec_bucket->attrs[j];
+
+                       if (spec->type == UVERBS_ATTR_TYPE_IDR ||
+                           spec->type == UVERBS_ATTR_TYPE_FD) {
+                               int current_ret;
+
+                               current_ret = uverbs_finalize_object(attr->obj_attr.uobject,
+                                                                    spec->obj.access,
+                                                                    commit);
+                               if (!ret)
+                                       ret = current_ret;
+                       }
+               }
+       }
+       return ret;
+}
 
  * applicable.
  * This function could create (access == NEW), destroy (access == DESTROY)
  * or unlock (access == READ || access == WRITE) objects if required.
- * The action will be finalized only when uverbs_finalize_object is called.
+ * The action will be finalized only when uverbs_finalize_object or
+ * uverbs_finalize_objects are called.
  */
 struct ib_uobject *uverbs_get_uobject_from_context(const struct uverbs_obj_type *type_attrs,
                                                   struct ib_ucontext *ucontext,
 int uverbs_finalize_object(struct ib_uobject *uobj,
                           enum uverbs_obj_access access,
                           bool commit);
+/*
+ * Note that certain finalize stages could return a status:
+ *   (a) alloc_commit could return a failure if the object is committed at the
+ *       same time when the context is destroyed.
+ *   (b) remove_commit could fail if the object wasn't destroyed successfully.
+ * Since multiple objects could be finalized in one transaction, it is very NOT
+ * recommended to have several finalize actions which have side effects.
+ * For example, it's NOT recommended to have a certain action which has both
+ * a commit action and a destroy action or two destroy objects in the same
+ * action. The rule of thumb is to have one destroy or commit action with
+ * multiple lookups.
+ * The first non zero return value of finalize_object is returned from this
+ * function. For example, this could happen when we couldn't destroy an
+ * object.
+ */
+int uverbs_finalize_objects(struct uverbs_attr_bundle *attrs_bundle,
+                           struct uverbs_attr_spec_hash * const *spec_hash,
+                           size_t num,
+                           bool commit);
 
 #endif /* RDMA_CORE_H */
 
  * =======================================
  */
 
+enum uverbs_attr_type {
+       UVERBS_ATTR_TYPE_NA,
+       UVERBS_ATTR_TYPE_IDR,
+       UVERBS_ATTR_TYPE_FD,
+};
+
 enum uverbs_obj_access {
        UVERBS_ACCESS_READ,
        UVERBS_ACCESS_WRITE,
        UVERBS_ACCESS_DESTROY
 };
 
+struct uverbs_attr_spec {
+       enum uverbs_attr_type           type;
+       struct {
+               /*
+                * higher bits mean the namespace and lower bits mean
+                * the type id within the namespace.
+                */
+               u16                     obj_type;
+               u8                      access;
+       } obj;
+};
+
+struct uverbs_attr_spec_hash {
+       size_t                          num_attrs;
+       struct uverbs_attr_spec         attrs[0];
+};
+
+struct uverbs_obj_attr {
+       struct ib_uobject               *uobject;
+};
+
+struct uverbs_attr {
+       struct uverbs_obj_attr  obj_attr;
+};
+
+struct uverbs_attr_bundle_hash {
+       /* if bit i is set, it means attrs[i] contains valid information */
+       unsigned long *valid_bitmap;
+       size_t num_attrs;
+       /*
+        * arrays of attributes, each element corresponds to the specification
+        * of the attribute in the same index.
+        */
+       struct uverbs_attr *attrs;
+};
+
+struct uverbs_attr_bundle {
+       size_t                          num_buckets;
+       struct uverbs_attr_bundle_hash  hash[];
+};
+
+static inline bool uverbs_attr_is_valid_in_hash(const struct uverbs_attr_bundle_hash *attrs_hash,
+                                               unsigned int idx)
+{
+       return test_bit(idx, attrs_hash->valid_bitmap);
+}
+
 #endif