* [PATCH rdma-core 4/5] libmlx5: Move to use CCAN list functionality
From: Yishai Hadas @ 2016-09-28 15:33 UTC (permalink / raw)
To: jgunthorpe-ePGOBjL8dl3ta4EC/59zMFaTQe2KTcn/
Cc: dledford-H+wXaHxf7aLQT0dZR+AlfA,
linux-rdma-u79uwXL29TY76Z2rM5mHXA, yishaih-VPRAkNaXOzVWk0Htik3J/w,
majd-VPRAkNaXOzVWk0Htik3J/w
In-Reply-To: <1475076789-14359-1-git-send-email-yishaih-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Move to use CCAN list functionality which its license meets
the project needs.
Signed-off-by: Yishai Hadas <yishaih-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
---
libmlx5/src/buf.c | 11 +-
libmlx5/src/list.h | 312 -----------------------------------------------------
libmlx5/src/mlx5.c | 2 +-
libmlx5/src/mlx5.h | 4 +-
4 files changed, 8 insertions(+), 321 deletions(-)
delete mode 100644 libmlx5/src/list.h
diff --git a/libmlx5/src/buf.c b/libmlx5/src/buf.c
index c827930..1a681c6 100644
--- a/libmlx5/src/buf.c
+++ b/libmlx5/src/buf.c
@@ -259,7 +259,6 @@ static int alloc_huge_buf(struct mlx5_context *mctx, struct mlx5_buf *buf,
size_t size, int page_size)
{
int found = 0;
- LIST_HEAD(slist);
int nchunk;
struct mlx5_hugetlb_mem *hmem;
int ret;
@@ -268,7 +267,7 @@ static int alloc_huge_buf(struct mlx5_context *mctx, struct mlx5_buf *buf,
nchunk = buf->length / MLX5_Q_CHUNK_SIZE;
mlx5_spin_lock(&mctx->hugetlb_lock);
- list_for_each_entry(hmem, &mctx->hugetlb_list, list) {
+ list_for_each(&mctx->hugetlb_list, hmem, entry) {
if (bitmap_avail(&hmem->bitmap)) {
buf->base = bitmap_alloc_range(&hmem->bitmap, nchunk, 1);
if (buf->base != -1) {
@@ -297,9 +296,9 @@ static int alloc_huge_buf(struct mlx5_context *mctx, struct mlx5_buf *buf,
mlx5_spin_lock(&mctx->hugetlb_lock);
if (bitmap_avail(&hmem->bitmap))
- list_add(&hmem->list, &mctx->hugetlb_list);
+ list_add(&mctx->hugetlb_list, &hmem->entry);
else
- list_add_tail(&hmem->list, &mctx->hugetlb_list);
+ list_add_tail(&mctx->hugetlb_list, &hmem->entry);
mlx5_spin_unlock(&mctx->hugetlb_lock);
}
@@ -318,7 +317,7 @@ out_fork:
mlx5_spin_lock(&mctx->hugetlb_lock);
bitmap_free_range(&hmem->bitmap, buf->base, nchunk);
if (bitmap_empty(&hmem->bitmap)) {
- list_del(&hmem->list);
+ list_del(&hmem->entry);
mlx5_spin_unlock(&mctx->hugetlb_lock);
free_huge_mem(hmem);
} else
@@ -335,7 +334,7 @@ static void free_huge_buf(struct mlx5_context *ctx, struct mlx5_buf *buf)
mlx5_spin_lock(&ctx->hugetlb_lock);
bitmap_free_range(&buf->hmem->bitmap, buf->base, nchunk);
if (bitmap_empty(&buf->hmem->bitmap)) {
- list_del(&buf->hmem->list);
+ list_del(&buf->hmem->entry);
mlx5_spin_unlock(&ctx->hugetlb_lock);
free_huge_mem(buf->hmem);
} else
diff --git a/libmlx5/src/list.h b/libmlx5/src/list.h
deleted file mode 100644
index 4f96482..0000000
--- a/libmlx5/src/list.h
+++ /dev/null
@@ -1,312 +0,0 @@
-#ifndef _LINUX_LIST_H
-#define _LINUX_LIST_H
-
-/*
- * These are non-NULL pointers that will result in page faults
- * under normal circumstances, used to verify that nobody uses
- * non-initialized list entries.
- */
-#define LIST_POISON1 ((void *) 0x00100100)
-#define LIST_POISON2 ((void *) 0x00200200)
-
-/*
- * Simple doubly linked list implementation.
- *
- * Some of the internal functions ("__xxx") are useful when
- * manipulating whole lists rather than single entries, as
- * sometimes we already know the next/prev entries and we can
- * generate better code by using them directly rather than
- * using the generic single-entry routines.
- */
-
-struct list_head {
- struct list_head *next, *prev;
-};
-
-#define LIST_HEAD_INIT(name) { &(name), &(name) }
-
-#define LIST_HEAD(name) \
- struct list_head name = LIST_HEAD_INIT(name)
-
-#define INIT_LIST_HEAD(ptr) do { \
- (ptr)->next = (ptr); (ptr)->prev = (ptr); \
-} while (0)
-
-/*
- * Insert a new entry between two known consecutive entries.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_add(struct list_head *new,
- struct list_head *prev,
- struct list_head *next)
-{
- next->prev = new;
- new->next = next;
- new->prev = prev;
- prev->next = new;
-}
-
-/**
- * list_add - add a new entry
- * @new: new entry to be added
- * @head: list head to add it after
- *
- * Insert a new entry after the specified head.
- * This is good for implementing stacks.
- */
-static inline void list_add(struct list_head *new, struct list_head *head)
-{
- __list_add(new, head, head->next);
-}
-
-/**
- * list_add_tail - add a new entry
- * @new: new entry to be added
- * @head: list head to add it before
- *
- * Insert a new entry before the specified head.
- * This is useful for implementing queues.
- */
-static inline void list_add_tail(struct list_head *new, struct list_head *head)
-{
- __list_add(new, head->prev, head);
-}
-
-/*
- * Delete a list entry by making the prev/next entries
- * point to each other.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_del(struct list_head *prev, struct list_head *next)
-{
- next->prev = prev;
- prev->next = next;
-}
-
-/**
- * list_del - deletes entry from list.
- * @entry: the element to delete from the list.
- * Note: list_empty on entry does not return true after this, the entry is
- * in an undefined state.
- */
-static inline void list_del(struct list_head *entry)
-{
- __list_del(entry->prev, entry->next);
- entry->next = LIST_POISON1;
- entry->prev = LIST_POISON2;
-}
-
-/**
- * list_del_init - deletes entry from list and reinitialize it.
- * @entry: the element to delete from the list.
- */
-static inline void list_del_init(struct list_head *entry)
-{
- __list_del(entry->prev, entry->next);
- INIT_LIST_HEAD(entry);
-}
-
-/**
- * list_move - delete from one list and add as another's head
- * @list: the entry to move
- * @head: the head that will precede our entry
- */
-static inline void list_move(struct list_head *list, struct list_head *head)
-{
- __list_del(list->prev, list->next);
- list_add(list, head);
-}
-
-/**
- * list_move_tail - delete from one list and add as another's tail
- * @list: the entry to move
- * @head: the head that will follow our entry
- */
-static inline void list_move_tail(struct list_head *list,
- struct list_head *head)
-{
- __list_del(list->prev, list->next);
- list_add_tail(list, head);
-}
-
-/**
- * list_empty - tests whether a list is empty
- * @head: the list to test.
- */
-static inline int list_empty(const struct list_head *head)
-{
- return head->next == head;
-}
-
-/**
- * list_empty_careful - tests whether a list is
- * empty _and_ checks that no other CPU might be
- * in the process of still modifying either member
- *
- * NOTE: using list_empty_careful() without synchronization
- * can only be safe if the only activity that can happen
- * to the list entry is list_del_init(). Eg. it cannot be used
- * if another CPU could re-list_add() it.
- *
- * @head: the list to test.
- */
-static inline int list_empty_careful(const struct list_head *head)
-{
- struct list_head *next = head->next;
- return (next == head) && (next == head->prev);
-}
-
-static inline void __list_splice(struct list_head *list,
- struct list_head *head)
-{
- struct list_head *first = list->next;
- struct list_head *last = list->prev;
- struct list_head *at = head->next;
-
- first->prev = head;
- head->next = first;
-
- last->next = at;
- at->prev = last;
-}
-
-/**
- * list_splice - join two lists
- * @list: the new list to add.
- * @head: the place to add it in the first list.
- */
-static inline void list_splice(struct list_head *list, struct list_head *head)
-{
- if (!list_empty(list))
- __list_splice(list, head);
-}
-
-/**
- * list_splice_init - join two lists and reinitialise the emptied list.
- * @list: the new list to add.
- * @head: the place to add it in the first list.
- *
- * The list at @list is reinitialised
- */
-static inline void list_splice_init(struct list_head *list,
- struct list_head *head)
-{
- if (!list_empty(list)) {
- __list_splice(list, head);
- INIT_LIST_HEAD(list);
- }
-}
-
-/**
- * list_entry - get the struct for this entry
- * @ptr: the &struct list_head pointer.
- * @type: the type of the struct this is embedded in.
- * @member: the name of the list_struct within the struct.
- */
-#define list_entry(ptr, type, member) \
- container_of(ptr, type, member)
-
-/**
- * list_for_each - iterate over a list
- * @pos: the &struct list_head to use as a loop counter.
- * @head: the head for your list.
- */
-#define list_for_each(pos, head) \
- for (pos = (head)->next; prefetch(pos->next), pos != (head); \
- pos->next)
-
-/**
- * __list_for_each - iterate over a list
- * @pos: the &struct list_head to use as a loop counter.
- * @head: the head for your list.
- *
- * This variant differs from list_for_each() in that it's the
- * simplest possible list iteration code, no prefetching is done.
- * Use this for code that knows the list to be very short (empty
- * or 1 entry) most of the time.
- */
-#define __list_for_each(pos, head) \
- for (pos = (head)->next; pos != (head); pos = pos->next)
-
-/**
- * list_for_each_prev - iterate over a list backwards
- * @pos: the &struct list_head to use as a loop counter.
- * @head: the head for your list.
- */
-#define list_for_each_prev(pos, head) \
- for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
- pos = pos->prev)
-
-/**
- * list_for_each_safe - iterate over a list safe against removal of list entry
- * @pos: the &struct list_head to use as a loop counter.
- * @n: another &struct list_head to use as temporary storage
- * @head: the head for your list.
- */
-#define list_for_each_safe(pos, n, head) \
- for (pos = (head)->next, n = pos->next; pos != (head); \
- pos = n, n = pos->next)
-
-/**
- * list_for_each_entry - iterate over list of given type
- * @pos: the type * to use as a loop counter.
- * @head: the head for your list.
- * @member: the name of the list_struct within the struct.
- */
-#define list_for_each_entry(pos, head, member) \
- for (pos = list_entry((head)->next, typeof(*pos), member); \
- &pos->member != (head); \
- pos = list_entry(pos->member.next, typeof(*pos), member))
-
-/**
- * list_for_each_entry_reverse - iterate backwards over list of given type.
- * @pos: the type * to use as a loop counter.
- * @head: the head for your list.
- * @member: the name of the list_struct within the struct.
- */
-#define list_for_each_entry_reverse(pos, head, member) \
- for (pos = list_entry((head)->prev, typeof(*pos), member); \
- prefetch(pos->member.prev), &pos->member != (head); \
- pos = list_entry(pos->member.prev, typeof(*pos), member))
-
-/**
- * list_prepare_entry - prepare a pos entry for use as a start point in
- * list_for_each_entry_continue
- * @pos: the type * to use as a start point
- * @head: the head of the list
- * @member: the name of the list_struct within the struct.
- */
-#define list_prepare_entry(pos, head, member) \
- ((pos) ? : list_entry(head, typeof(*pos), member))
-
-/**
- * list_for_each_entry_continue - iterate over list of given type
- * continuing after existing point
- * @pos: the type * to use as a loop counter.
- * @head: the head for your list.
- * @member: the name of the list_struct within the struct.
- */
-#define list_for_each_entry_continue(pos, head, member) \
- for (pos = list_entry(pos->member.next, typeof(*pos), member); \
- prefetch(pos->member.next), &pos->member != (head); \
- pos = list_entry(pos->member.next, typeof(*pos), member))
-
-/**
- * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
- * @pos: the type * to use as a loop counter.
- * @n: another type * to use as temporary storage
- * @head: the head for your list.
- * @member: the name of the list_struct within the struct.
- */
-#define list_for_each_entry_safe(pos, n, head, member) \
- for (pos = list_entry((head)->next, typeof(*pos), member), \
- n = list_entry(pos->member.next, typeof(*pos), member); \
- &pos->member != (head); \
- pos = n, n = list_entry(n->member.next, typeof(*n), member))
-
-#endif
-
diff --git a/libmlx5/src/mlx5.c b/libmlx5/src/mlx5.c
index 262a969..058b52f 100644
--- a/libmlx5/src/mlx5.c
+++ b/libmlx5/src/mlx5.c
@@ -730,7 +730,7 @@ static int mlx5_init_context(struct verbs_device *vdev,
mlx5_read_env(&vdev->device, context);
mlx5_spinlock_init(&context->hugetlb_lock);
- INIT_LIST_HEAD(&context->hugetlb_list);
+ list_head_init(&context->hugetlb_list);
context->ibv_ctx.ops = mlx5_ctx_ops;
diff --git a/libmlx5/src/mlx5.h b/libmlx5/src/mlx5.h
index 80c69a4..6c4e830 100644
--- a/libmlx5/src/mlx5.h
+++ b/libmlx5/src/mlx5.h
@@ -39,7 +39,7 @@
#include <infiniband/driver.h>
#include <infiniband/arch.h>
#include "mlx5-abi.h"
-#include "list.h"
+#include <ccan/list.h>
#include "bitmap.h"
#include <ccan/minmax.h>
@@ -354,7 +354,7 @@ struct mlx5_hugetlb_mem {
int shmid;
void *shmaddr;
struct mlx5_bitmap bitmap;
- struct list_head list;
+ struct list_node entry;
};
struct mlx5_buf {
--
1.8.3.1
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH rdma-core 3/5] ccan: Add list functionality
From: Yishai Hadas @ 2016-09-28 15:33 UTC (permalink / raw)
To: jgunthorpe-ePGOBjL8dl3ta4EC/59zMFaTQe2KTcn/
Cc: dledford-H+wXaHxf7aLQT0dZR+AlfA,
linux-rdma-u79uwXL29TY76Z2rM5mHXA, yishaih-VPRAkNaXOzVWk0Htik3J/w,
majd-VPRAkNaXOzVWk0Htik3J/w
In-Reply-To: <1475076789-14359-1-git-send-email-yishaih-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Add CCAN list functionality to be used by down stream
patches.
Signed-off-by: Yishai Hadas <yishaih-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
---
ccan/CMakeLists.txt | 5 +
ccan/check_type.h | 64 ++++
ccan/container_of.h | 146 +++++++++
ccan/list.h | 842 ++++++++++++++++++++++++++++++++++++++++++++++++++++
ccan/str.h | 228 ++++++++++++++
ccan/str_debug.h | 30 ++
6 files changed, 1315 insertions(+)
create mode 100644 ccan/check_type.h
create mode 100644 ccan/container_of.h
create mode 100644 ccan/list.h
create mode 100644 ccan/str.h
create mode 100644 ccan/str_debug.h
diff --git a/ccan/CMakeLists.txt b/ccan/CMakeLists.txt
index 8c5f750..70b5430 100644
--- a/ccan/CMakeLists.txt
+++ b/ccan/CMakeLists.txt
@@ -2,4 +2,9 @@ publish_internal_headers(ccan
minmax.h
build_assert.h
config.h
+ list.h
+ str.h
+ str_debug.h
+ check_type.h
+ container_of.h
)
diff --git a/ccan/check_type.h b/ccan/check_type.h
new file mode 100644
index 0000000..77501a9
--- /dev/null
+++ b/ccan/check_type.h
@@ -0,0 +1,64 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_CHECK_TYPE_H
+#define CCAN_CHECK_TYPE_H
+#include "config.h"
+
+/**
+ * check_type - issue a warning or build failure if type is not correct.
+ * @expr: the expression whose type we should check (not evaluated).
+ * @type: the exact type we expect the expression to be.
+ *
+ * This macro is usually used within other macros to try to ensure that a macro
+ * argument is of the expected type. No type promotion of the expression is
+ * done: an unsigned int is not the same as an int!
+ *
+ * check_type() always evaluates to 0.
+ *
+ * If your compiler does not support typeof, then the best we can do is fail
+ * to compile if the sizes of the types are unequal (a less complete check).
+ *
+ * Example:
+ * // They should always pass a 64-bit value to _set_some_value!
+ * #define set_some_value(expr) \
+ * _set_some_value((check_type((expr), uint64_t), (expr)))
+ */
+
+/**
+ * check_types_match - issue a warning or build failure if types are not same.
+ * @expr1: the first expression (not evaluated).
+ * @expr2: the second expression (not evaluated).
+ *
+ * This macro is usually used within other macros to try to ensure that
+ * arguments are of identical types. No type promotion of the expressions is
+ * done: an unsigned int is not the same as an int!
+ *
+ * check_types_match() always evaluates to 0.
+ *
+ * If your compiler does not support typeof, then the best we can do is fail
+ * to compile if the sizes of the types are unequal (a less complete check).
+ *
+ * Example:
+ * // Do subtraction to get to enclosing type, but make sure that
+ * // pointer is of correct type for that member.
+ * #define container_of(mbr_ptr, encl_type, mbr) \
+ * (check_types_match((mbr_ptr), &((encl_type *)0)->mbr), \
+ * ((encl_type *) \
+ * ((char *)(mbr_ptr) - offsetof(enclosing_type, mbr))))
+ */
+#if HAVE_TYPEOF
+#define check_type(expr, type) \
+ ((typeof(expr) *)0 != (type *)0)
+
+#define check_types_match(expr1, expr2) \
+ ((typeof(expr1) *)0 != (typeof(expr2) *)0)
+#else
+#include <ccan/build_assert/build_assert.h>
+/* Without typeof, we can only test the sizes. */
+#define check_type(expr, type) \
+ BUILD_ASSERT_OR_ZERO(sizeof(expr) == sizeof(type))
+
+#define check_types_match(expr1, expr2) \
+ BUILD_ASSERT_OR_ZERO(sizeof(expr1) == sizeof(expr2))
+#endif /* HAVE_TYPEOF */
+
+#endif /* CCAN_CHECK_TYPE_H */
diff --git a/ccan/container_of.h b/ccan/container_of.h
new file mode 100644
index 0000000..c91b909
--- /dev/null
+++ b/ccan/container_of.h
@@ -0,0 +1,146 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_CONTAINER_OF_H
+#define CCAN_CONTAINER_OF_H
+#include <stddef.h>
+
+#include "config.h"
+#include <ccan/check_type.h>
+
+/**
+ * container_of - get pointer to enclosing structure
+ * @member_ptr: pointer to the structure member
+ * @containing_type: the type this member is within
+ * @member: the name of this member within the structure.
+ *
+ * Given a pointer to a member of a structure, this macro does pointer
+ * subtraction to return the pointer to the enclosing type.
+ *
+ * Example:
+ * struct foo {
+ * int fielda, fieldb;
+ * // ...
+ * };
+ * struct info {
+ * int some_other_field;
+ * struct foo my_foo;
+ * };
+ *
+ * static struct info *foo_to_info(struct foo *foo)
+ * {
+ * return container_of(foo, struct info, my_foo);
+ * }
+ */
+#ifndef container_of
+#define container_of(member_ptr, containing_type, member) \
+ ((containing_type *) \
+ ((char *)(member_ptr) \
+ - container_off(containing_type, member)) \
+ + check_types_match(*(member_ptr), ((containing_type *)0)->member))
+#endif
+
+/**
+ * container_of_or_null - get pointer to enclosing structure, or NULL
+ * @member_ptr: pointer to the structure member
+ * @containing_type: the type this member is within
+ * @member: the name of this member within the structure.
+ *
+ * Given a pointer to a member of a structure, this macro does pointer
+ * subtraction to return the pointer to the enclosing type, unless it
+ * is given NULL, in which case it also returns NULL.
+ *
+ * Example:
+ * struct foo {
+ * int fielda, fieldb;
+ * // ...
+ * };
+ * struct info {
+ * int some_other_field;
+ * struct foo my_foo;
+ * };
+ *
+ * static struct info *foo_to_info_allowing_null(struct foo *foo)
+ * {
+ * return container_of_or_null(foo, struct info, my_foo);
+ * }
+ */
+static inline char *container_of_or_null_(void *member_ptr, size_t offset)
+{
+ return member_ptr ? (char *)member_ptr - offset : NULL;
+}
+#define container_of_or_null(member_ptr, containing_type, member) \
+ ((containing_type *) \
+ container_of_or_null_(member_ptr, \
+ container_off(containing_type, member)) \
+ + check_types_match(*(member_ptr), ((containing_type *)0)->member))
+
+/**
+ * container_off - get offset to enclosing structure
+ * @containing_type: the type this member is within
+ * @member: the name of this member within the structure.
+ *
+ * Given a pointer to a member of a structure, this macro does
+ * typechecking and figures out the offset to the enclosing type.
+ *
+ * Example:
+ * struct foo {
+ * int fielda, fieldb;
+ * // ...
+ * };
+ * struct info {
+ * int some_other_field;
+ * struct foo my_foo;
+ * };
+ *
+ * static struct info *foo_to_info(struct foo *foo)
+ * {
+ * size_t off = container_off(struct info, my_foo);
+ * return (void *)((char *)foo - off);
+ * }
+ */
+#define container_off(containing_type, member) \
+ offsetof(containing_type, member)
+
+/**
+ * container_of_var - get pointer to enclosing structure using a variable
+ * @member_ptr: pointer to the structure member
+ * @container_var: a pointer of same type as this member's container
+ * @member: the name of this member within the structure.
+ *
+ * Given a pointer to a member of a structure, this macro does pointer
+ * subtraction to return the pointer to the enclosing type.
+ *
+ * Example:
+ * static struct info *foo_to_i(struct foo *foo)
+ * {
+ * struct info *i = container_of_var(foo, i, my_foo);
+ * return i;
+ * }
+ */
+#if HAVE_TYPEOF
+#define container_of_var(member_ptr, container_var, member) \
+ container_of(member_ptr, typeof(*container_var), member)
+#else
+#define container_of_var(member_ptr, container_var, member) \
+ ((void *)((char *)(member_ptr) - \
+ container_off_var(container_var, member)))
+#endif
+
+/**
+ * container_off_var - get offset of a field in enclosing structure
+ * @container_var: a pointer to a container structure
+ * @member: the name of a member within the structure.
+ *
+ * Given (any) pointer to a structure and a its member name, this
+ * macro does pointer subtraction to return offset of member in a
+ * structure memory layout.
+ *
+ */
+#if HAVE_TYPEOF
+#define container_off_var(var, member) \
+ container_off(typeof(*var), member)
+#else
+#define container_off_var(var, member) \
+ ((const char *)&(var)->member - (const char *)(var))
+#endif
+
+#endif /* CCAN_CONTAINER_OF_H */
diff --git a/ccan/list.h b/ccan/list.h
new file mode 100644
index 0000000..5bc93ff
--- /dev/null
+++ b/ccan/list.h
@@ -0,0 +1,842 @@
+/* Licensed under BSD-MIT - see LICENSE file for details */
+#ifndef CCAN_LIST_H
+#define CCAN_LIST_H
+//#define CCAN_LIST_DEBUG 1
+#include <stdbool.h>
+#include <assert.h>
+#include <ccan/str.h>
+#include <ccan/container_of.h>
+#include <ccan/check_type.h>
+
+/**
+ * struct list_node - an entry in a doubly-linked list
+ * @next: next entry (self if empty)
+ * @prev: previous entry (self if empty)
+ *
+ * This is used as an entry in a linked list.
+ * Example:
+ * struct child {
+ * const char *name;
+ * // Linked list of all us children.
+ * struct list_node list;
+ * };
+ */
+struct list_node
+{
+ struct list_node *next, *prev;
+};
+
+/**
+ * struct list_head - the head of a doubly-linked list
+ * @h: the list_head (containing next and prev pointers)
+ *
+ * This is used as the head of a linked list.
+ * Example:
+ * struct parent {
+ * const char *name;
+ * struct list_head children;
+ * unsigned int num_children;
+ * };
+ */
+struct list_head
+{
+ struct list_node n;
+};
+
+/**
+ * list_check - check head of a list for consistency
+ * @h: the list_head
+ * @abortstr: the location to print on aborting, or NULL.
+ *
+ * Because list_nodes have redundant information, consistency checking between
+ * the back and forward links can be done. This is useful as a debugging check.
+ * If @abortstr is non-NULL, that will be printed in a diagnostic if the list
+ * is inconsistent, and the function will abort.
+ *
+ * Returns the list head if the list is consistent, NULL if not (it
+ * can never return NULL if @abortstr is set).
+ *
+ * See also: list_check_node()
+ *
+ * Example:
+ * static void dump_parent(struct parent *p)
+ * {
+ * struct child *c;
+ *
+ * printf("%s (%u children):\n", p->name, p->num_children);
+ * list_check(&p->children, "bad child list");
+ * list_for_each(&p->children, c, list)
+ * printf(" -> %s\n", c->name);
+ * }
+ */
+struct list_head *list_check(const struct list_head *h, const char *abortstr);
+
+/**
+ * list_check_node - check node of a list for consistency
+ * @n: the list_node
+ * @abortstr: the location to print on aborting, or NULL.
+ *
+ * Check consistency of the list node is in (it must be in one).
+ *
+ * See also: list_check()
+ *
+ * Example:
+ * static void dump_child(const struct child *c)
+ * {
+ * list_check_node(&c->list, "bad child list");
+ * printf("%s\n", c->name);
+ * }
+ */
+struct list_node *list_check_node(const struct list_node *n,
+ const char *abortstr);
+
+#define LIST_LOC __FILE__ ":" stringify(__LINE__)
+#ifdef CCAN_LIST_DEBUG
+#define list_debug(h, loc) list_check((h), loc)
+#define list_debug_node(n, loc) list_check_node((n), loc)
+#else
+#define list_debug(h, loc) ((void)loc, h)
+#define list_debug_node(n, loc) ((void)loc, n)
+#endif
+
+/**
+ * LIST_HEAD_INIT - initializer for an empty list_head
+ * @name: the name of the list.
+ *
+ * Explicit initializer for an empty list.
+ *
+ * See also:
+ * LIST_HEAD, list_head_init()
+ *
+ * Example:
+ * static struct list_head my_list = LIST_HEAD_INIT(my_list);
+ */
+#define LIST_HEAD_INIT(name) { { &(name).n, &(name).n } }
+
+/**
+ * LIST_HEAD - define and initialize an empty list_head
+ * @name: the name of the list.
+ *
+ * The LIST_HEAD macro defines a list_head and initializes it to an empty
+ * list. It can be prepended by "static" to define a static list_head.
+ *
+ * See also:
+ * LIST_HEAD_INIT, list_head_init()
+ *
+ * Example:
+ * static LIST_HEAD(my_global_list);
+ */
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+/**
+ * list_head_init - initialize a list_head
+ * @h: the list_head to set to the empty list
+ *
+ * Example:
+ * ...
+ * struct parent *parent = malloc(sizeof(*parent));
+ *
+ * list_head_init(&parent->children);
+ * parent->num_children = 0;
+ */
+static inline void list_head_init(struct list_head *h)
+{
+ h->n.next = h->n.prev = &h->n;
+}
+
+/**
+ * list_node_init - initialize a list_node
+ * @n: the list_node to link to itself.
+ *
+ * You don't need to use this normally! But it lets you list_del(@n)
+ * safely.
+ */
+static inline void list_node_init(struct list_node *n)
+{
+ n->next = n->prev = n;
+}
+
+/**
+ * list_add_after - add an entry after an existing node in a linked list
+ * @h: the list_head to add the node to (for debugging)
+ * @p: the existing list_node to add the node after
+ * @n: the new list_node to add to the list.
+ *
+ * The existing list_node must already be a member of the list.
+ * The new list_node does not need to be initialized; it will be overwritten.
+ *
+ * Example:
+ * struct child c1, c2, c3;
+ * LIST_HEAD(h);
+ *
+ * list_add_tail(&h, &c1.list);
+ * list_add_tail(&h, &c3.list);
+ * list_add_after(&h, &c1.list, &c2.list);
+ */
+#define list_add_after(h, p, n) list_add_after_(h, p, n, LIST_LOC)
+static inline void list_add_after_(struct list_head *h,
+ struct list_node *p,
+ struct list_node *n,
+ const char *abortstr)
+{
+ n->next = p->next;
+ n->prev = p;
+ p->next->prev = n;
+ p->next = n;
+ (void)list_debug(h, abortstr);
+}
+
+/**
+ * list_add - add an entry at the start of a linked list.
+ * @h: the list_head to add the node to
+ * @n: the list_node to add to the list.
+ *
+ * The list_node does not need to be initialized; it will be overwritten.
+ * Example:
+ * struct child *child = malloc(sizeof(*child));
+ *
+ * child->name = "marvin";
+ * list_add(&parent->children, &child->list);
+ * parent->num_children++;
+ */
+#define list_add(h, n) list_add_(h, n, LIST_LOC)
+static inline void list_add_(struct list_head *h,
+ struct list_node *n,
+ const char *abortstr)
+{
+ list_add_after_(h, &h->n, n, abortstr);
+}
+
+/**
+ * list_add_before - add an entry before an existing node in a linked list
+ * @h: the list_head to add the node to (for debugging)
+ * @p: the existing list_node to add the node before
+ * @n: the new list_node to add to the list.
+ *
+ * The existing list_node must already be a member of the list.
+ * The new list_node does not need to be initialized; it will be overwritten.
+ *
+ * Example:
+ * list_head_init(&h);
+ * list_add_tail(&h, &c1.list);
+ * list_add_tail(&h, &c3.list);
+ * list_add_before(&h, &c3.list, &c2.list);
+ */
+#define list_add_before(h, p, n) list_add_before_(h, p, n, LIST_LOC)
+static inline void list_add_before_(struct list_head *h,
+ struct list_node *p,
+ struct list_node *n,
+ const char *abortstr)
+{
+ n->next = p;
+ n->prev = p->prev;
+ p->prev->next = n;
+ p->prev = n;
+ (void)list_debug(h, abortstr);
+}
+
+/**
+ * list_add_tail - add an entry at the end of a linked list.
+ * @h: the list_head to add the node to
+ * @n: the list_node to add to the list.
+ *
+ * The list_node does not need to be initialized; it will be overwritten.
+ * Example:
+ * list_add_tail(&parent->children, &child->list);
+ * parent->num_children++;
+ */
+#define list_add_tail(h, n) list_add_tail_(h, n, LIST_LOC)
+static inline void list_add_tail_(struct list_head *h,
+ struct list_node *n,
+ const char *abortstr)
+{
+ list_add_before_(h, &h->n, n, abortstr);
+}
+
+/**
+ * list_empty - is a list empty?
+ * @h: the list_head
+ *
+ * If the list is empty, returns true.
+ *
+ * Example:
+ * assert(list_empty(&parent->children) == (parent->num_children == 0));
+ */
+#define list_empty(h) list_empty_(h, LIST_LOC)
+static inline bool list_empty_(const struct list_head *h, const char* abortstr)
+{
+ (void)list_debug(h, abortstr);
+ return h->n.next == &h->n;
+}
+
+/**
+ * list_empty_nodebug - is a list empty (and don't perform debug checks)?
+ * @h: the list_head
+ *
+ * If the list is empty, returns true.
+ * This differs from list_empty() in that if CCAN_LIST_DEBUG is set it
+ * will NOT perform debug checks. Only use this function if you REALLY
+ * know what you're doing.
+ *
+ * Example:
+ * assert(list_empty_nodebug(&parent->children) == (parent->num_children == 0));
+ */
+#ifndef CCAN_LIST_DEBUG
+#define list_empty_nodebug(h) list_empty(h)
+#else
+static inline bool list_empty_nodebug(const struct list_head *h)
+{
+ return h->n.next == &h->n;
+}
+#endif
+
+/**
+ * list_empty_nocheck - is a list empty?
+ * @h: the list_head
+ *
+ * If the list is empty, returns true. This doesn't perform any
+ * debug check for list consistency, so it can be called without
+ * locks, racing with the list being modified. This is ok for
+ * checks where an incorrect result is not an issue (optimized
+ * bail out path for example).
+ */
+static inline bool list_empty_nocheck(const struct list_head *h)
+{
+ return h->n.next == &h->n;
+}
+
+/**
+ * list_del - delete an entry from an (unknown) linked list.
+ * @n: the list_node to delete from the list.
+ *
+ * Note that this leaves @n in an undefined state; it can be added to
+ * another list, but not deleted again.
+ *
+ * See also:
+ * list_del_from(), list_del_init()
+ *
+ * Example:
+ * list_del(&child->list);
+ * parent->num_children--;
+ */
+#define list_del(n) list_del_(n, LIST_LOC)
+static inline void list_del_(struct list_node *n, const char* abortstr)
+{
+ (void)list_debug_node(n, abortstr);
+ n->next->prev = n->prev;
+ n->prev->next = n->next;
+#ifdef CCAN_LIST_DEBUG
+ /* Catch use-after-del. */
+ n->next = n->prev = NULL;
+#endif
+}
+
+/**
+ * list_del_init - delete a node, and reset it so it can be deleted again.
+ * @n: the list_node to be deleted.
+ *
+ * list_del(@n) or list_del_init() again after this will be safe,
+ * which can be useful in some cases.
+ *
+ * See also:
+ * list_del_from(), list_del()
+ *
+ * Example:
+ * list_del_init(&child->list);
+ * parent->num_children--;
+ */
+#define list_del_init(n) list_del_init_(n, LIST_LOC)
+static inline void list_del_init_(struct list_node *n, const char *abortstr)
+{
+ list_del_(n, abortstr);
+ list_node_init(n);
+}
+
+/**
+ * list_del_from - delete an entry from a known linked list.
+ * @h: the list_head the node is in.
+ * @n: the list_node to delete from the list.
+ *
+ * This explicitly indicates which list a node is expected to be in,
+ * which is better documentation and can catch more bugs.
+ *
+ * See also: list_del()
+ *
+ * Example:
+ * list_del_from(&parent->children, &child->list);
+ * parent->num_children--;
+ */
+static inline void list_del_from(struct list_head *h, struct list_node *n)
+{
+#ifdef CCAN_LIST_DEBUG
+ {
+ /* Thorough check: make sure it was in list! */
+ struct list_node *i;
+ for (i = h->n.next; i != n; i = i->next)
+ assert(i != &h->n);
+ }
+#endif /* CCAN_LIST_DEBUG */
+
+ /* Quick test that catches a surprising number of bugs. */
+ assert(!list_empty(h));
+ list_del(n);
+}
+
+/**
+ * list_swap - swap out an entry from an (unknown) linked list for a new one.
+ * @o: the list_node to replace from the list.
+ * @n: the list_node to insert in place of the old one.
+ *
+ * Note that this leaves @o in an undefined state; it can be added to
+ * another list, but not deleted/swapped again.
+ *
+ * See also:
+ * list_del()
+ *
+ * Example:
+ * struct child x1, x2;
+ * LIST_HEAD(xh);
+ *
+ * list_add(&xh, &x1.list);
+ * list_swap(&x1.list, &x2.list);
+ */
+#define list_swap(o, n) list_swap_(o, n, LIST_LOC)
+static inline void list_swap_(struct list_node *o,
+ struct list_node *n,
+ const char* abortstr)
+{
+ (void)list_debug_node(o, abortstr);
+ *n = *o;
+ n->next->prev = n;
+ n->prev->next = n;
+#ifdef CCAN_LIST_DEBUG
+ /* Catch use-after-del. */
+ o->next = o->prev = NULL;
+#endif
+}
+
+/**
+ * list_entry - convert a list_node back into the structure containing it.
+ * @n: the list_node
+ * @type: the type of the entry
+ * @member: the list_node member of the type
+ *
+ * Example:
+ * // First list entry is children.next; convert back to child.
+ * child = list_entry(parent->children.n.next, struct child, list);
+ *
+ * See Also:
+ * list_top(), list_for_each()
+ */
+#define list_entry(n, type, member) container_of(n, type, member)
+
+/**
+ * list_top - get the first entry in a list
+ * @h: the list_head
+ * @type: the type of the entry
+ * @member: the list_node member of the type
+ *
+ * If the list is empty, returns NULL.
+ *
+ * Example:
+ * struct child *first;
+ * first = list_top(&parent->children, struct child, list);
+ * if (!first)
+ * printf("Empty list!\n");
+ */
+#define list_top(h, type, member) \
+ ((type *)list_top_((h), list_off_(type, member)))
+
+static inline const void *list_top_(const struct list_head *h, size_t off)
+{
+ if (list_empty(h))
+ return NULL;
+ return (const char *)h->n.next - off;
+}
+
+/**
+ * list_pop - remove the first entry in a list
+ * @h: the list_head
+ * @type: the type of the entry
+ * @member: the list_node member of the type
+ *
+ * If the list is empty, returns NULL.
+ *
+ * Example:
+ * struct child *one;
+ * one = list_pop(&parent->children, struct child, list);
+ * if (!one)
+ * printf("Empty list!\n");
+ */
+#define list_pop(h, type, member) \
+ ((type *)list_pop_((h), list_off_(type, member)))
+
+static inline const void *list_pop_(const struct list_head *h, size_t off)
+{
+ struct list_node *n;
+
+ if (list_empty(h))
+ return NULL;
+ n = h->n.next;
+ list_del(n);
+ return (const char *)n - off;
+}
+
+/**
+ * list_tail - get the last entry in a list
+ * @h: the list_head
+ * @type: the type of the entry
+ * @member: the list_node member of the type
+ *
+ * If the list is empty, returns NULL.
+ *
+ * Example:
+ * struct child *last;
+ * last = list_tail(&parent->children, struct child, list);
+ * if (!last)
+ * printf("Empty list!\n");
+ */
+#define list_tail(h, type, member) \
+ ((type *)list_tail_((h), list_off_(type, member)))
+
+static inline const void *list_tail_(const struct list_head *h, size_t off)
+{
+ if (list_empty(h))
+ return NULL;
+ return (const char *)h->n.prev - off;
+}
+
+/**
+ * list_for_each - iterate through a list.
+ * @h: the list_head (warning: evaluated multiple times!)
+ * @i: the structure containing the list_node
+ * @member: the list_node member of the structure
+ *
+ * This is a convenient wrapper to iterate @i over the entire list. It's
+ * a for loop, so you can break and continue as normal.
+ *
+ * Example:
+ * list_for_each(&parent->children, child, list)
+ * printf("Name: %s\n", child->name);
+ */
+#define list_for_each(h, i, member) \
+ list_for_each_off(h, i, list_off_var_(i, member))
+
+/**
+ * list_for_each_rev - iterate through a list backwards.
+ * @h: the list_head
+ * @i: the structure containing the list_node
+ * @member: the list_node member of the structure
+ *
+ * This is a convenient wrapper to iterate @i over the entire list. It's
+ * a for loop, so you can break and continue as normal.
+ *
+ * Example:
+ * list_for_each_rev(&parent->children, child, list)
+ * printf("Name: %s\n", child->name);
+ */
+#define list_for_each_rev(h, i, member) \
+ list_for_each_rev_off(h, i, list_off_var_(i, member))
+
+/**
+ * list_for_each_rev_safe - iterate through a list backwards,
+ * maybe during deletion
+ * @h: the list_head
+ * @i: the structure containing the list_node
+ * @nxt: the structure containing the list_node
+ * @member: the list_node member of the structure
+ *
+ * This is a convenient wrapper to iterate @i over the entire list backwards.
+ * It's a for loop, so you can break and continue as normal. The extra
+ * variable * @nxt is used to hold the next element, so you can delete @i
+ * from the list.
+ *
+ * Example:
+ * struct child *next;
+ * list_for_each_rev_safe(&parent->children, child, next, list) {
+ * printf("Name: %s\n", child->name);
+ * }
+ */
+#define list_for_each_rev_safe(h, i, nxt, member) \
+ list_for_each_rev_safe_off(h, i, nxt, list_off_var_(i, member))
+
+/**
+ * list_for_each_safe - iterate through a list, maybe during deletion
+ * @h: the list_head
+ * @i: the structure containing the list_node
+ * @nxt: the structure containing the list_node
+ * @member: the list_node member of the structure
+ *
+ * This is a convenient wrapper to iterate @i over the entire list. It's
+ * a for loop, so you can break and continue as normal. The extra variable
+ * @nxt is used to hold the next element, so you can delete @i from the list.
+ *
+ * Example:
+ * list_for_each_safe(&parent->children, child, next, list) {
+ * list_del(&child->list);
+ * parent->num_children--;
+ * }
+ */
+#define list_for_each_safe(h, i, nxt, member) \
+ list_for_each_safe_off(h, i, nxt, list_off_var_(i, member))
+
+/**
+ * list_next - get the next entry in a list
+ * @h: the list_head
+ * @i: a pointer to an entry in the list.
+ * @member: the list_node member of the structure
+ *
+ * If @i was the last entry in the list, returns NULL.
+ *
+ * Example:
+ * struct child *second;
+ * second = list_next(&parent->children, first, list);
+ * if (!second)
+ * printf("No second child!\n");
+ */
+#define list_next(h, i, member) \
+ ((list_typeof(i))list_entry_or_null(list_debug(h, \
+ __FILE__ ":" stringify(__LINE__)), \
+ (i)->member.next, \
+ list_off_var_((i), member)))
+
+/**
+ * list_prev - get the previous entry in a list
+ * @h: the list_head
+ * @i: a pointer to an entry in the list.
+ * @member: the list_node member of the structure
+ *
+ * If @i was the first entry in the list, returns NULL.
+ *
+ * Example:
+ * first = list_prev(&parent->children, second, list);
+ * if (!first)
+ * printf("Can't go back to first child?!\n");
+ */
+#define list_prev(h, i, member) \
+ ((list_typeof(i))list_entry_or_null(list_debug(h, \
+ __FILE__ ":" stringify(__LINE__)), \
+ (i)->member.prev, \
+ list_off_var_((i), member)))
+
+/**
+ * list_append_list - empty one list onto the end of another.
+ * @to: the list to append into
+ * @from: the list to empty.
+ *
+ * This takes the entire contents of @from and moves it to the end of
+ * @to. After this @from will be empty.
+ *
+ * Example:
+ * struct list_head adopter;
+ *
+ * list_append_list(&adopter, &parent->children);
+ * assert(list_empty(&parent->children));
+ * parent->num_children = 0;
+ */
+#define list_append_list(t, f) list_append_list_(t, f, \
+ __FILE__ ":" stringify(__LINE__))
+static inline void list_append_list_(struct list_head *to,
+ struct list_head *from,
+ const char *abortstr)
+{
+ struct list_node *from_tail = list_debug(from, abortstr)->n.prev;
+ struct list_node *to_tail = list_debug(to, abortstr)->n.prev;
+
+ /* Sew in head and entire list. */
+ to->n.prev = from_tail;
+ from_tail->next = &to->n;
+ to_tail->next = &from->n;
+ from->n.prev = to_tail;
+
+ /* Now remove head. */
+ list_del(&from->n);
+ list_head_init(from);
+}
+
+/**
+ * list_prepend_list - empty one list into the start of another.
+ * @to: the list to prepend into
+ * @from: the list to empty.
+ *
+ * This takes the entire contents of @from and moves it to the start
+ * of @to. After this @from will be empty.
+ *
+ * Example:
+ * list_prepend_list(&adopter, &parent->children);
+ * assert(list_empty(&parent->children));
+ * parent->num_children = 0;
+ */
+#define list_prepend_list(t, f) list_prepend_list_(t, f, LIST_LOC)
+static inline void list_prepend_list_(struct list_head *to,
+ struct list_head *from,
+ const char *abortstr)
+{
+ struct list_node *from_tail = list_debug(from, abortstr)->n.prev;
+ struct list_node *to_head = list_debug(to, abortstr)->n.next;
+
+ /* Sew in head and entire list. */
+ to->n.next = &from->n;
+ from->n.prev = &to->n;
+ to_head->prev = from_tail;
+ from_tail->next = to_head;
+
+ /* Now remove head. */
+ list_del(&from->n);
+ list_head_init(from);
+}
+
+/* internal macros, do not use directly */
+#define list_for_each_off_dir_(h, i, off, dir) \
+ for (i = list_node_to_off_(list_debug(h, LIST_LOC)->n.dir, \
+ (off)); \
+ list_node_from_off_((void *)i, (off)) != &(h)->n; \
+ i = list_node_to_off_(list_node_from_off_((void *)i, (off))->dir, \
+ (off)))
+
+#define list_for_each_safe_off_dir_(h, i, nxt, off, dir) \
+ for (i = list_node_to_off_(list_debug(h, LIST_LOC)->n.dir, \
+ (off)), \
+ nxt = list_node_to_off_(list_node_from_off_(i, (off))->dir, \
+ (off)); \
+ list_node_from_off_(i, (off)) != &(h)->n; \
+ i = nxt, \
+ nxt = list_node_to_off_(list_node_from_off_(i, (off))->dir, \
+ (off)))
+
+/**
+ * list_for_each_off - iterate through a list of memory regions.
+ * @h: the list_head
+ * @i: the pointer to a memory region wich contains list node data.
+ * @off: offset(relative to @i) at which list node data resides.
+ *
+ * This is a low-level wrapper to iterate @i over the entire list, used to
+ * implement all oher, more high-level, for-each constructs. It's a for loop,
+ * so you can break and continue as normal.
+ *
+ * WARNING! Being the low-level macro that it is, this wrapper doesn't know
+ * nor care about the type of @i. The only assumtion made is that @i points
+ * to a chunk of memory that at some @offset, relative to @i, contains a
+ * properly filled `struct node_list' which in turn contains pointers to
+ * memory chunks and it's turtles all the way down. Whith all that in mind
+ * remember that given the wrong pointer/offset couple this macro will
+ * happilly churn all you memory untill SEGFAULT stops it, in other words
+ * caveat emptor.
+ *
+ * It is worth mentioning that one of legitimate use-cases for that wrapper
+ * is operation on opaque types with known offset for `struct list_node'
+ * member(preferably 0), because it allows you not to disclose the type of
+ * @i.
+ *
+ * Example:
+ * list_for_each_off(&parent->children, child,
+ * offsetof(struct child, list))
+ * printf("Name: %s\n", child->name);
+ */
+#define list_for_each_off(h, i, off) \
+ list_for_each_off_dir_((h),(i),(off),next)
+
+/**
+ * list_for_each_rev_off - iterate through a list of memory regions backwards
+ * @h: the list_head
+ * @i: the pointer to a memory region wich contains list node data.
+ * @off: offset(relative to @i) at which list node data resides.
+ *
+ * See list_for_each_off for details
+ */
+#define list_for_each_rev_off(h, i, off) \
+ list_for_each_off_dir_((h),(i),(off),prev)
+
+/**
+ * list_for_each_safe_off - iterate through a list of memory regions, maybe
+ * during deletion
+ * @h: the list_head
+ * @i: the pointer to a memory region wich contains list node data.
+ * @nxt: the structure containing the list_node
+ * @off: offset(relative to @i) at which list node data resides.
+ *
+ * For details see `list_for_each_off' and `list_for_each_safe'
+ * descriptions.
+ *
+ * Example:
+ * list_for_each_safe_off(&parent->children, child,
+ * next, offsetof(struct child, list))
+ * printf("Name: %s\n", child->name);
+ */
+#define list_for_each_safe_off(h, i, nxt, off) \
+ list_for_each_safe_off_dir_((h),(i),(nxt),(off),next)
+
+/**
+ * list_for_each_rev_safe_off - iterate backwards through a list of
+ * memory regions, maybe during deletion
+ * @h: the list_head
+ * @i: the pointer to a memory region wich contains list node data.
+ * @nxt: the structure containing the list_node
+ * @off: offset(relative to @i) at which list node data resides.
+ *
+ * For details see `list_for_each_rev_off' and `list_for_each_rev_safe'
+ * descriptions.
+ *
+ * Example:
+ * list_for_each_rev_safe_off(&parent->children, child,
+ * next, offsetof(struct child, list))
+ * printf("Name: %s\n", child->name);
+ */
+#define list_for_each_rev_safe_off(h, i, nxt, off) \
+ list_for_each_safe_off_dir_((h),(i),(nxt),(off),prev)
+
+/* Other -off variants. */
+#define list_entry_off(n, type, off) \
+ ((type *)list_node_from_off_((n), (off)))
+
+#define list_head_off(h, type, off) \
+ ((type *)list_head_off((h), (off)))
+
+#define list_tail_off(h, type, off) \
+ ((type *)list_tail_((h), (off)))
+
+#define list_add_off(h, n, off) \
+ list_add((h), list_node_from_off_((n), (off)))
+
+#define list_del_off(n, off) \
+ list_del(list_node_from_off_((n), (off)))
+
+#define list_del_from_off(h, n, off) \
+ list_del_from(h, list_node_from_off_((n), (off)))
+
+/* Offset helper functions so we only single-evaluate. */
+static inline void *list_node_to_off_(struct list_node *node, size_t off)
+{
+ return (void *)((char *)node - off);
+}
+static inline struct list_node *list_node_from_off_(void *ptr, size_t off)
+{
+ return (struct list_node *)((char *)ptr + off);
+}
+
+/* Get the offset of the member, but make sure it's a list_node. */
+#define list_off_(type, member) \
+ (container_off(type, member) + \
+ check_type(((type *)0)->member, struct list_node))
+
+#define list_off_var_(var, member) \
+ (container_off_var(var, member) + \
+ check_type(var->member, struct list_node))
+
+#if HAVE_TYPEOF
+#define list_typeof(var) typeof(var)
+#else
+#define list_typeof(var) void *
+#endif
+
+/* Returns member, or NULL if at end of list. */
+static inline void *list_entry_or_null(const struct list_head *h,
+ const struct list_node *n,
+ size_t off)
+{
+ if (n == &h->n)
+ return NULL;
+ return (char *)n - off;
+}
+#endif /* CCAN_LIST_H */
diff --git a/ccan/str.h b/ccan/str.h
new file mode 100644
index 0000000..4e268b3
--- /dev/null
+++ b/ccan/str.h
@@ -0,0 +1,228 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_STR_H
+#define CCAN_STR_H
+#include "config.h"
+#include <string.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <ctype.h>
+
+/**
+ * streq - Are two strings equal?
+ * @a: first string
+ * @b: first string
+ *
+ * This macro is arguably more readable than "!strcmp(a, b)".
+ *
+ * Example:
+ * if (streq(somestring, ""))
+ * printf("String is empty!\n");
+ */
+#define streq(a,b) (strcmp((a),(b)) == 0)
+
+/**
+ * strstarts - Does this string start with this prefix?
+ * @str: string to test
+ * @prefix: prefix to look for at start of str
+ *
+ * Example:
+ * if (strstarts(somestring, "foo"))
+ * printf("String %s begins with 'foo'!\n", somestring);
+ */
+#define strstarts(str,prefix) (strncmp((str),(prefix),strlen(prefix)) == 0)
+
+/**
+ * strends - Does this string end with this postfix?
+ * @str: string to test
+ * @postfix: postfix to look for at end of str
+ *
+ * Example:
+ * if (strends(somestring, "foo"))
+ * printf("String %s end with 'foo'!\n", somestring);
+ */
+static inline bool strends(const char *str, const char *postfix)
+{
+ if (strlen(str) < strlen(postfix))
+ return false;
+
+ return streq(str + strlen(str) - strlen(postfix), postfix);
+}
+
+/**
+ * stringify - Turn expression into a string literal
+ * @expr: any C expression
+ *
+ * Example:
+ * #define PRINT_COND_IF_FALSE(cond) \
+ * ((cond) || printf("%s is false!", stringify(cond)))
+ */
+#define stringify(expr) stringify_1(expr)
+/* Double-indirection required to stringify expansions */
+#define stringify_1(expr) #expr
+
+/**
+ * strcount - Count number of (non-overlapping) occurrences of a substring.
+ * @haystack: a C string
+ * @needle: a substring
+ *
+ * Example:
+ * assert(strcount("aaa aaa", "a") == 6);
+ * assert(strcount("aaa aaa", "ab") == 0);
+ * assert(strcount("aaa aaa", "aa") == 2);
+ */
+size_t strcount(const char *haystack, const char *needle);
+
+/**
+ * STR_MAX_CHARS - Maximum possible size of numeric string for this type.
+ * @type_or_expr: a pointer or integer type or expression.
+ *
+ * This provides enough space for a nul-terminated string which represents the
+ * largest possible value for the type or expression.
+ *
+ * Note: The implementation adds extra space so hex values or negative
+ * values will fit (eg. sprintf(... "%p"). )
+ *
+ * Example:
+ * char str[STR_MAX_CHARS(int)];
+ *
+ * sprintf(str, "%i", 7);
+ */
+#define STR_MAX_CHARS(type_or_expr) \
+ ((sizeof(type_or_expr) * CHAR_BIT + 8) / 9 * 3 + 2 \
+ + STR_MAX_CHARS_TCHECK_(type_or_expr))
+
+#if HAVE_TYPEOF
+/* Only a simple type can have 0 assigned, so test that. */
+#define STR_MAX_CHARS_TCHECK_(type_or_expr) \
+ ({ typeof(type_or_expr) x = 0; (void)x; 0; })
+#else
+#define STR_MAX_CHARS_TCHECK_(type_or_expr) 0
+#endif
+
+/**
+ * cisalnum - isalnum() which takes a char (and doesn't accept EOF)
+ * @c: a character
+ *
+ * Surprisingly, the standard ctype.h isalnum() takes an int, which
+ * must have the value of EOF (-1) or an unsigned char. This variant
+ * takes a real char, and doesn't accept EOF.
+ */
+static inline bool cisalnum(char c)
+{
+ return isalnum((unsigned char)c);
+}
+static inline bool cisalpha(char c)
+{
+ return isalpha((unsigned char)c);
+}
+static inline bool cisascii(char c)
+{
+ return isascii((unsigned char)c);
+}
+#if HAVE_ISBLANK
+static inline bool cisblank(char c)
+{
+ return isblank((unsigned char)c);
+}
+#endif
+static inline bool ciscntrl(char c)
+{
+ return iscntrl((unsigned char)c);
+}
+static inline bool cisdigit(char c)
+{
+ return isdigit((unsigned char)c);
+}
+static inline bool cisgraph(char c)
+{
+ return isgraph((unsigned char)c);
+}
+static inline bool cislower(char c)
+{
+ return islower((unsigned char)c);
+}
+static inline bool cisprint(char c)
+{
+ return isprint((unsigned char)c);
+}
+static inline bool cispunct(char c)
+{
+ return ispunct((unsigned char)c);
+}
+static inline bool cisspace(char c)
+{
+ return isspace((unsigned char)c);
+}
+static inline bool cisupper(char c)
+{
+ return isupper((unsigned char)c);
+}
+static inline bool cisxdigit(char c)
+{
+ return isxdigit((unsigned char)c);
+}
+
+#include <ccan/str_debug.h>
+
+/* These checks force things out of line, hence they are under DEBUG. */
+#ifdef CCAN_STR_DEBUG
+#include <ccan/build_assert.h>
+
+/* These are commonly misused: they take -1 or an *unsigned* char value. */
+#undef isalnum
+#undef isalpha
+#undef isascii
+#undef isblank
+#undef iscntrl
+#undef isdigit
+#undef isgraph
+#undef islower
+#undef isprint
+#undef ispunct
+#undef isspace
+#undef isupper
+#undef isxdigit
+
+/* You can use a char if char is unsigned. */
+#if HAVE_BUILTIN_TYPES_COMPATIBLE_P && HAVE_TYPEOF
+#define str_check_arg_(i) \
+ ((i) + BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(typeof(i), \
+ char) \
+ || (char)255 > 0))
+#else
+#define str_check_arg_(i) (i)
+#endif
+
+#define isalnum(i) str_isalnum(str_check_arg_(i))
+#define isalpha(i) str_isalpha(str_check_arg_(i))
+#define isascii(i) str_isascii(str_check_arg_(i))
+#if HAVE_ISBLANK
+#define isblank(i) str_isblank(str_check_arg_(i))
+#endif
+#define iscntrl(i) str_iscntrl(str_check_arg_(i))
+#define isdigit(i) str_isdigit(str_check_arg_(i))
+#define isgraph(i) str_isgraph(str_check_arg_(i))
+#define islower(i) str_islower(str_check_arg_(i))
+#define isprint(i) str_isprint(str_check_arg_(i))
+#define ispunct(i) str_ispunct(str_check_arg_(i))
+#define isspace(i) str_isspace(str_check_arg_(i))
+#define isupper(i) str_isupper(str_check_arg_(i))
+#define isxdigit(i) str_isxdigit(str_check_arg_(i))
+
+#if HAVE_TYPEOF
+/* With GNU magic, we can make const-respecting standard string functions. */
+#undef strstr
+#undef strchr
+#undef strrchr
+
+/* + 0 is needed to decay array into pointer. */
+#define strstr(haystack, needle) \
+ ((typeof((haystack) + 0))str_strstr((haystack), (needle)))
+#define strchr(haystack, c) \
+ ((typeof((haystack) + 0))str_strchr((haystack), (c)))
+#define strrchr(haystack, c) \
+ ((typeof((haystack) + 0))str_strrchr((haystack), (c)))
+#endif
+#endif /* CCAN_STR_DEBUG */
+
+#endif /* CCAN_STR_H */
diff --git a/ccan/str_debug.h b/ccan/str_debug.h
new file mode 100644
index 0000000..92c10c4
--- /dev/null
+++ b/ccan/str_debug.h
@@ -0,0 +1,30 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_STR_DEBUG_H
+#define CCAN_STR_DEBUG_H
+
+/* #define CCAN_STR_DEBUG 1 */
+
+#ifdef CCAN_STR_DEBUG
+/* Because we mug the real ones with macros, we need our own wrappers. */
+int str_isalnum(int i);
+int str_isalpha(int i);
+int str_isascii(int i);
+#if HAVE_ISBLANK
+int str_isblank(int i);
+#endif
+int str_iscntrl(int i);
+int str_isdigit(int i);
+int str_isgraph(int i);
+int str_islower(int i);
+int str_isprint(int i);
+int str_ispunct(int i);
+int str_isspace(int i);
+int str_isupper(int i);
+int str_isxdigit(int i);
+
+char *str_strstr(const char *haystack, const char *needle);
+char *str_strchr(const char *s, int c);
+char *str_strrchr(const char *s, int c);
+#endif /* CCAN_STR_DEBUG */
+
+#endif /* CCAN_STR_DEBUG_H */
--
1.8.3.1
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH rdma-core 2/5] ccan: Add CCAN min and max functionality
From: Yishai Hadas @ 2016-09-28 15:33 UTC (permalink / raw)
To: jgunthorpe-ePGOBjL8dl3ta4EC/59zMFaTQe2KTcn/
Cc: dledford-H+wXaHxf7aLQT0dZR+AlfA,
linux-rdma-u79uwXL29TY76Z2rM5mHXA, yishaih-VPRAkNaXOzVWk0Htik3J/w,
majd-VPRAkNaXOzVWk0Htik3J/w
In-Reply-To: <1475076789-14359-1-git-send-email-yishaih-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Add CCAN min/max functionality and use it among the project.
Signed-off-by: Yishai Hadas <yishaih-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
---
CMakeLists.txt | 1 +
| 18 ++++++++---
ccan/CMakeLists.txt | 5 +++
ccan/build_assert.h | 40 ++++++++++++++++++++++++
ccan/config.h | 2 ++
ccan/minmax.h | 65 +++++++++++++++++++++++++++++++++++++++
ibacm/linux/osd.h | 5 ++-
libibverbs/examples/rc_pingpong.c | 8 +----
libmlx5/src/mlx5.h | 15 +--------
librdmacm/src/cma.h | 5 ++-
10 files changed, 133 insertions(+), 31 deletions(-)
create mode 100644 ccan/CMakeLists.txt
create mode 100644 ccan/build_assert.h
create mode 100644 ccan/config.h
create mode 100644 ccan/minmax.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a829cd4..34a21d1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -221,6 +221,7 @@ configure_file("${BUILDLIB}/config.h.in" "${BUILD_INCLUDE}/config.h" ESCAPE_QUOT
#-------------------------
# Sub-directories
+add_subdirectory(ccan)
# Libraries
add_subdirectory(libibumad/src)
add_subdirectory(libibumad/man)
--git a/buildlib/publish_headers.cmake b/buildlib/publish_headers.cmake
index ccd2296..c1909d6 100644
--- a/buildlib/publish_headers.cmake
+++ b/buildlib/publish_headers.cmake
@@ -1,10 +1,9 @@
# COPYRIGHT (c) 2016 Obsidian Research Corporation. See COPYING file
-# Copy headers from the source directory to the proper place in the
-# build/include directory
-function(PUBLISH_HEADERS DEST)
+# Same as publish_headers but does not install them during the install phase
+function(publish_internal_headers DEST)
if(NOT ARGN)
- message(SEND_ERROR "Error: PUBLISH_HEADERS called without any files")
+ message(SEND_ERROR "Error: publish_internal_headers called without any files")
return()
endif()
@@ -15,6 +14,17 @@ function(PUBLISH_HEADERS DEST)
get_filename_component(FIL ${SFIL} NAME)
execute_process(COMMAND "ln" "-Tsf"
"${CMAKE_CURRENT_SOURCE_DIR}/${SFIL}" "${DDIR}/${FIL}")
+ endforeach()
+endfunction()
+
+# Copy headers from the source directory to the proper place in the
+# build/include directory. This also installs them into /usr/include/xx during
+# the install phase
+function(publish_headers DEST)
+ publish_internal_headers("${DEST}" ${ARGN})
+
+ foreach(SFIL ${ARGN})
+ get_filename_component(FIL ${SFIL} NAME)
install(FILES "${SFIL}" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${DEST}/" RENAME "${FIL}")
endforeach()
endfunction()
diff --git a/ccan/CMakeLists.txt b/ccan/CMakeLists.txt
new file mode 100644
index 0000000..8c5f750
--- /dev/null
+++ b/ccan/CMakeLists.txt
@@ -0,0 +1,5 @@
+publish_internal_headers(ccan
+ minmax.h
+ build_assert.h
+ config.h
+ )
diff --git a/ccan/build_assert.h b/ccan/build_assert.h
new file mode 100644
index 0000000..b9ecd84
--- /dev/null
+++ b/ccan/build_assert.h
@@ -0,0 +1,40 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_BUILD_ASSERT_H
+#define CCAN_BUILD_ASSERT_H
+
+/**
+ * BUILD_ASSERT - assert a build-time dependency.
+ * @cond: the compile-time condition which must be true.
+ *
+ * Your compile will fail if the condition isn't true, or can't be evaluated
+ * by the compiler. This can only be used within a function.
+ *
+ * Example:
+ * #include <stddef.h>
+ * ...
+ * static char *foo_to_char(struct foo *foo)
+ * {
+ * // This code needs string to be at start of foo.
+ * BUILD_ASSERT(offsetof(struct foo, string) == 0);
+ * return (char *)foo;
+ * }
+ */
+#define BUILD_ASSERT(cond) \
+ do { (void) sizeof(char [1 - 2*!(cond)]); } while(0)
+
+/**
+ * BUILD_ASSERT_OR_ZERO - assert a build-time dependency, as an expression.
+ * @cond: the compile-time condition which must be true.
+ *
+ * Your compile will fail if the condition isn't true, or can't be evaluated
+ * by the compiler. This can be used in an expression: its value is "0".
+ *
+ * Example:
+ * #define foo_to_char(foo) \
+ * ((char *)(foo) \
+ * + BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0))
+ */
+#define BUILD_ASSERT_OR_ZERO(cond) \
+ (sizeof(char [1 - 2*!(cond)]) - 1)
+
+#endif /* CCAN_BUILD_ASSERT_H */
diff --git a/ccan/config.h b/ccan/config.h
new file mode 100644
index 0000000..b89ac94
--- /dev/null
+++ b/ccan/config.h
@@ -0,0 +1,2 @@
+#define HAVE_STATEMENT_EXPR 1
+#define HAVE_TYPEOF 1
diff --git a/ccan/minmax.h b/ccan/minmax.h
new file mode 100644
index 0000000..5642f65
--- /dev/null
+++ b/ccan/minmax.h
@@ -0,0 +1,65 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_MINMAX_H
+#define CCAN_MINMAX_H
+
+#include "config.h"
+
+#include <ccan/build_assert.h>
+
+#if !HAVE_STATEMENT_EXPR || !HAVE_TYPEOF
+/*
+ * Without these, there's no way to avoid unsafe double evaluation of
+ * the arguments
+ */
+#error Sorry, minmax module requires statement expressions and typeof
+#endif
+
+#if HAVE_BUILTIN_TYPES_COMPATIBLE_P
+#define MINMAX_ASSERT_COMPATIBLE(a, b) \
+ BUILD_ASSERT(__builtin_types_compatible_p(a, b))
+#else
+#define MINMAX_ASSERT_COMPATIBLE(a, b) \
+ do { } while (0)
+#endif
+
+#define min(a, b) \
+ ({ \
+ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ MINMAX_ASSERT_COMPATIBLE(typeof(_a), typeof(_b)); \
+ _a < _b ? _a : _b; \
+ })
+
+#define max(a, b) \
+ ({ \
+ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ MINMAX_ASSERT_COMPATIBLE(typeof(_a), typeof(_b)); \
+ _a > _b ? _a : _b; \
+ })
+
+#define clamp(v, f, c) (max(min((v), (c)), (f)))
+
+
+#define min_t(t, a, b) \
+ ({ \
+ t _ta = (a); \
+ t _tb = (b); \
+ min(_ta, _tb); \
+ })
+#define max_t(t, a, b) \
+ ({ \
+ t _ta = (a); \
+ t _tb = (b); \
+ max(_ta, _tb); \
+ })
+
+#define clamp_t(t, v, f, c) \
+ ({ \
+ t _tv = (v); \
+ t _tf = (f); \
+ t _tc = (c); \
+ clamp(_tv, _tf, _tc); \
+ })
+
+#endif /* CCAN_MINMAX_H */
diff --git a/ibacm/linux/osd.h b/ibacm/linux/osd.h
index 0113d10..f03980f 100644
--- a/ibacm/linux/osd.h
+++ b/ibacm/linux/osd.h
@@ -46,6 +46,8 @@
#include <sys/time.h>
#include <netinet/in.h>
+#include <ccan/minmax.h>
+
#define ACM_CONF_DIR IBACM_CONFIG_PATH
#define ACM_ADDR_FILE "ibacm_addr.cfg"
#define ACM_OPTS_FILE "ibacm_opts.cfg"
@@ -53,9 +55,6 @@
#define LIB_DESTRUCTOR __attribute__((destructor))
#define CDECL_FUNC
-#define min(a, b) (a < b ? a : b)
-#define max(a, b) (a > b ? a : b)
-
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define htonll(x) bswap_64(x)
#else
diff --git a/libibverbs/examples/rc_pingpong.c b/libibverbs/examples/rc_pingpong.c
index 9676783..3ea4670 100644
--- a/libibverbs/examples/rc_pingpong.c
+++ b/libibverbs/examples/rc_pingpong.c
@@ -49,13 +49,7 @@
#include "pingpong.h"
-#ifndef max
-#define max(x, y) (((x) > (y)) ? (x) : (y))
-#endif
-
-#ifndef min
-#define min(x, y) (((x) < (y)) ? (x) : (y))
-#endif
+#include <ccan/minmax.h>
enum {
PINGPONG_RECV_WRID = 1,
diff --git a/libmlx5/src/mlx5.h b/libmlx5/src/mlx5.h
index dc90892..80c69a4 100644
--- a/libmlx5/src/mlx5.h
+++ b/libmlx5/src/mlx5.h
@@ -41,6 +41,7 @@
#include "mlx5-abi.h"
#include "list.h"
#include "bitmap.h"
+#include <ccan/minmax.h>
#ifdef __GNUC__
#define likely(x) __builtin_expect((x), 1)
@@ -91,20 +92,6 @@
#endif
-#ifndef min
-#define min(a, b) \
- ({ typeof(a) _a = (a); \
- typeof(b) _b = (b); \
- _a < _b ? _a : _b; })
-#endif
-
-#ifndef max
-#define max(a, b) \
- ({ typeof(a) _a = (a); \
- typeof(b) _b = (b); \
- _a > _b ? _a : _b; })
-#endif
-
#define HIDDEN __attribute__((visibility("hidden")))
#ifdef HAVE_FUNC_ATTRIBUTE_ALWAYS_INLINE
diff --git a/librdmacm/src/cma.h b/librdmacm/src/cma.h
index e773591..68abe54 100644
--- a/librdmacm/src/cma.h
+++ b/librdmacm/src/cma.h
@@ -47,6 +47,8 @@
#include <rdma/rdma_cma.h>
#include <infiniband/ib.h>
+#include <ccan/minmax.h>
+
#ifdef INCLUDE_VALGRIND
# include <valgrind/memcheck.h>
# ifndef VALGRIND_MAKE_MEM_DEFINED
@@ -68,9 +70,6 @@ static inline uint64_t htonll(uint64_t x) { return x; }
static inline uint64_t ntohll(uint64_t x) { return x; }
#endif
-#define max(a, b) ((a) > (b) ? a : b)
-#define min(a, b) ((a) < (b) ? a : b)
-
/*
* Fast synchronization for low contention locking.
*/
--
1.8.3.1
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH rdma-core 1/5] Remove container_of and offset local declarations
From: Yishai Hadas @ 2016-09-28 15:33 UTC (permalink / raw)
To: jgunthorpe-ePGOBjL8dl3ta4EC/59zMFaTQe2KTcn/
Cc: dledford-H+wXaHxf7aLQT0dZR+AlfA,
linux-rdma-u79uwXL29TY76Z2rM5mHXA, yishaih-VPRAkNaXOzVWk0Htik3J/w,
majd-VPRAkNaXOzVWk0Htik3J/w
In-Reply-To: <1475076789-14359-1-git-send-email-yishaih-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
This tree uses the version of container_of in infinband/verbs.h,
and requires stddef.h to declare offsetof.
Signed-off-by: Yishai Hadas <yishaih-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
---
ibacm/linux/osd.h | 3 ---
libibcm/src/cm.c | 5 -----
libmlx5/src/list.h | 19 -------------------
librdmacm/src/cma.h | 6 ------
4 files changed, 33 deletions(-)
diff --git a/ibacm/linux/osd.h b/ibacm/linux/osd.h
index 5ca4c6f..0113d10 100644
--- a/ibacm/linux/osd.h
+++ b/ibacm/linux/osd.h
@@ -53,9 +53,6 @@
#define LIB_DESTRUCTOR __attribute__((destructor))
#define CDECL_FUNC
-#define container_of(ptr, type, field) \
- ((type *) ((void *) ptr - offsetof(type, field)))
-
#define min(a, b) (a < b ? a : b)
#define max(a, b) (a > b ? a : b)
diff --git a/libibcm/src/cm.c b/libibcm/src/cm.c
index 5c56381..f5318f0 100644
--- a/libibcm/src/cm.c
+++ b/libibcm/src/cm.c
@@ -121,11 +121,6 @@ struct cm_id_private {
pthread_mutex_t mut;
};
-#ifndef container_of
-#define container_of(ptr, type, field) \
- ((type *) ((void *)ptr - offsetof(type, field)))
-#endif
-
static int check_abi_version(void)
{
char value[8];
diff --git a/libmlx5/src/list.h b/libmlx5/src/list.h
index cd7d25b..4f96482 100644
--- a/libmlx5/src/list.h
+++ b/libmlx5/src/list.h
@@ -201,25 +201,6 @@ static inline void list_splice_init(struct list_head *list,
}
}
-#ifndef offsetof
-#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
-#endif
-
-/**
- * container_of - cast a member of a structure out to the containing structure
- *
- * @ptr: the pointer to the member.
- * @type: the type of the container struct this is embedded in.
- * @member: the name of the member within the struct.
- *
- */
-#ifndef container_of
-#define container_of(ptr, type, member) ({ \
- const typeof(((type *)0)->member)*__mptr = (ptr); \
- (type *)((char *)__mptr - offsetof(type, member)); })
-#endif
-
-
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
diff --git a/librdmacm/src/cma.h b/librdmacm/src/cma.h
index 98eba8d..e773591 100644
--- a/librdmacm/src/cma.h
+++ b/librdmacm/src/cma.h
@@ -71,12 +71,6 @@ static inline uint64_t ntohll(uint64_t x) { return x; }
#define max(a, b) ((a) > (b) ? a : b)
#define min(a, b) ((a) < (b) ? a : b)
-#ifndef container_of
-#define container_of(ptr, type, field) \
- ((type *) ((void *)ptr - offsetof(type, field)))
-#endif
-
-
/*
* Fast synchronization for low contention locking.
*/
--
1.8.3.1
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH rdma-core 0/5] Licensing and cleanup issues
From: Yishai Hadas @ 2016-09-28 15:33 UTC (permalink / raw)
To: jgunthorpe-ePGOBjL8dl3ta4EC/59zMFaTQe2KTcn/
Cc: dledford-H+wXaHxf7aLQT0dZR+AlfA,
linux-rdma-u79uwXL29TY76Z2rM5mHXA, yishaih-VPRAkNaXOzVWk0Htik3J/w,
majd-VPRAkNaXOzVWk0Htik3J/w
This series comes to solve some potential licensing issue around list.h
and ocrdma_list.h by using the CCAN functionality which has BSD license.
The CCAN required functionality was taken as is with minor touches as of
flattening the directory structure, its original header
with the licensing note was preserved.
In addition, the series drops some local definitions and use shared code instead.
Pull request was sent as well[1]
Jason,
Look at ccan/config.h I just hard coded few required HAVE_XXX which
expects to be available in Linux/distros environment.
In case you think that it should be changed to be set dynamically with cmake please
make the relevant changes as you are more familiar by that time with cmake.
[1] https://github.com/linux-rdma/rdma-core/pull/5
Yishai
Yishai Hadas (5):
Remove container_of and offset local declarations
ccan: Add CCAN min and max functionality
ccan: Add list functionality
libmlx5: Move to use CCAN list functionality
libocrdma: Move to use CCAN list functionality
CMakeLists.txt | 1 +
buildlib/publish_headers.cmake | 18 +-
ccan/CMakeLists.txt | 10 +
ccan/build_assert.h | 40 ++
ccan/check_type.h | 64 +++
ccan/config.h | 2 +
ccan/container_of.h | 146 +++++++
ccan/list.h | 842 ++++++++++++++++++++++++++++++++++++++
ccan/minmax.h | 65 +++
ccan/str.h | 228 +++++++++++
ccan/str_debug.h | 30 ++
ibacm/linux/osd.h | 8 +-
libibcm/src/cm.c | 5 -
libibverbs/examples/rc_pingpong.c | 8 +-
libmlx5/src/buf.c | 11 +-
libmlx5/src/list.h | 331 ---------------
libmlx5/src/mlx5.c | 2 +-
libmlx5/src/mlx5.h | 19 +-
libocrdma/src/ocrdma_list.h | 104 -----
libocrdma/src/ocrdma_main.c | 24 +-
libocrdma/src/ocrdma_main.h | 12 +-
libocrdma/src/ocrdma_verbs.c | 33 +-
librdmacm/src/cma.h | 11 +-
23 files changed, 1489 insertions(+), 525 deletions(-)
create mode 100644 ccan/CMakeLists.txt
create mode 100644 ccan/build_assert.h
create mode 100644 ccan/check_type.h
create mode 100644 ccan/config.h
create mode 100644 ccan/container_of.h
create mode 100644 ccan/list.h
create mode 100644 ccan/minmax.h
create mode 100644 ccan/str.h
create mode 100644 ccan/str_debug.h
delete mode 100644 libmlx5/src/list.h
delete mode 100644 libocrdma/src/ocrdma_list.h
--
1.8.3.1
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [RFC ABI V4 7/7] IB/mlx5: Implement common uverb objects
From: Matan Barak @ 2016-09-28 15:10 UTC (permalink / raw)
To: linux-rdma-u79uwXL29TY76Z2rM5mHXA
Cc: Doug Ledford, Jason Gunthorpe, Sean Hefty, Liran Liss,
Haggai Eran, Matan Barak, Majd Dibbiny, Christoph Lameter,
Leon Romanovsky
In-Reply-To: <1475075417-30189-1-git-send-email-matanb-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
This patch simply tells mlx5 to use the uverb objects declared by
the common layer.
Signed-off-by: Matan Barak <matanb-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
---
drivers/infiniband/hw/mlx5/main.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index c72797c..003042a 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -49,6 +49,7 @@
#include <linux/mlx5/vport.h>
#include <rdma/ib_smi.h>
#include <rdma/ib_umem.h>
+#include <rdma/uverbs_ioctl_cmd.h>
#include <linux/in.h>
#include <linux/etherdevice.h>
#include <linux/mlx5/fs.h>
@@ -2467,6 +2468,8 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
if (err)
goto err_rsrc;
+ dev->ib_dev.types_group = &uverbs_types_group;
+
err = ib_register_device(&dev->ib_dev, NULL);
if (err)
goto err_odp;
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [RFC ABI V4 6/7] RDMA/core: Add uverbs types, actions, handlers and attributes
From: Matan Barak @ 2016-09-28 15:10 UTC (permalink / raw)
To: linux-rdma-u79uwXL29TY76Z2rM5mHXA
Cc: Doug Ledford, Jason Gunthorpe, Sean Hefty, Liran Liss,
Haggai Eran, Matan Barak, Majd Dibbiny, Christoph Lameter,
Leon Romanovsky
In-Reply-To: <1475075417-30189-1-git-send-email-matanb-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
We add the common (core) code for init context, query device,
reg_mr, create_cq, create_comp_channel and init_pd.
This includes the following parts:
* Macros for defining commands and validators
* For each command
* type declarations
- destruction order
- free function
- uverbs action group
* actions
* handlers
* attributes
Drivers could use the these attributes, actions or types when they
want to alter or add a new type. The could use the uverbs handler
directly in the action (or just wrap it in the driver's custom code).
Currently we use ib_udata to pass vendor specific information to the
driver. This should probably be refactored in the future.
Signed-off-by: Matan Barak <matanb-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Signed-off-by: Haggai Eran <haggaie-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Signed-off-by: Leon Romanovsky <leonro-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
---
drivers/infiniband/core/uverbs.h | 6 +
drivers/infiniband/core/uverbs_cmd.c | 7 +-
drivers/infiniband/core/uverbs_ioctl_cmd.c | 573 +++++++++++++++++++++++++++++
drivers/infiniband/core/uverbs_main.c | 37 ++
include/rdma/uverbs_ioctl.h | 147 ++++++++
include/rdma/uverbs_ioctl_cmd.h | 152 ++++++++
include/uapi/rdma/ib_user_verbs.h | 13 +
7 files changed, 931 insertions(+), 4 deletions(-)
diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h
index 206fff2..8b08f83 100644
--- a/drivers/infiniband/core/uverbs.h
+++ b/drivers/infiniband/core/uverbs.h
@@ -174,6 +174,8 @@ struct ib_ucq_object {
u32 async_events_reported;
};
+extern const struct file_operations uverbs_refactored_event_fops;
+
struct file *ib_uverbs_alloc_event_file(struct ib_uverbs_file *uverbs_file,
struct ib_device *ib_dev,
int is_async);
@@ -196,6 +198,10 @@ void ib_uverbs_event_handler(struct ib_event_handler *handler,
void ib_uverbs_dealloc_xrcd(struct ib_uverbs_device *dev, struct ib_xrcd *xrcd);
int uverbs_dealloc_mw(struct ib_mw *mw);
+void uverbs_copy_query_dev_fields(struct ib_device *ib_dev,
+ struct ib_uverbs_query_device_resp *resp,
+ struct ib_device_attr *attr);
+
void ib_uverbs_release_ucq(struct ib_uverbs_file *file,
struct ib_uverbs_event_file *ev_file,
struct ib_ucq_object *uobj);
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index 9648b80..e913dc0 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -385,8 +385,7 @@ err:
return ret;
}
-static void copy_query_dev_fields(struct ib_uverbs_file *file,
- struct ib_device *ib_dev,
+void uverbs_copy_query_dev_fields(struct ib_device *ib_dev,
struct ib_uverbs_query_device_resp *resp,
struct ib_device_attr *attr)
{
@@ -447,7 +446,7 @@ ssize_t ib_uverbs_query_device(struct ib_uverbs_file *file,
return -EFAULT;
memset(&resp, 0, sizeof resp);
- copy_query_dev_fields(file, ib_dev, &resp, &ib_dev->attrs);
+ uverbs_copy_query_dev_fields(ib_dev, &resp, &ib_dev->attrs);
if (copy_to_user((void __user *) (unsigned long) cmd.response,
&resp, sizeof resp))
@@ -3623,7 +3622,7 @@ int ib_uverbs_ex_query_device(struct ib_uverbs_file *file,
if (err)
return err;
- copy_query_dev_fields(file, ib_dev, &resp.base, &attr);
+ uverbs_copy_query_dev_fields(ib_dev, &resp.base, &attr);
if (ucore->outlen < resp.response_length + sizeof(resp.odp_caps))
goto end;
diff --git a/drivers/infiniband/core/uverbs_ioctl_cmd.c b/drivers/infiniband/core/uverbs_ioctl_cmd.c
index 6677dec..d713591 100644
--- a/drivers/infiniband/core/uverbs_ioctl_cmd.c
+++ b/drivers/infiniband/core/uverbs_ioctl_cmd.c
@@ -182,3 +182,576 @@ void uverbs_free_event_file(const struct uverbs_type_alloc_action *type_alloc_ac
};
EXPORT_SYMBOL(uverbs_free_event_file);
+enum {
+ UVERBS_UHW_IN,
+ UVERBS_UHW_OUT,
+};
+
+DECLARE_UVERBS_ATTR_SPEC(
+ uverbs_uhw_compat_spec,
+ UVERBS_ATTR_PTR_IN(UVERBS_UHW_IN, 0),
+ UVERBS_ATTR_PTR_OUT(UVERBS_UHW_OUT, 0));
+EXPORT_SYMBOL(uverbs_uhw_compat_spec);
+
+static void create_udata(struct uverbs_attr_array *vendor,
+ struct ib_udata *udata)
+{
+ /*
+ * This is for ease of conversion. The purpose is to convert all vendors
+ * to use uverbs_attr_array instead of ib_udata.
+ * Assume attr == 0 is input and attr == 1 is output.
+ */
+ void * __user inbuf;
+ size_t inbuf_len = 0;
+ void * __user outbuf;
+ size_t outbuf_len = 0;
+
+ if (vendor) {
+ WARN_ON(vendor->num_attrs > 2);
+
+ if (vendor->attrs[0].valid) {
+ inbuf = vendor->attrs[0].cmd_attr.ptr;
+ inbuf_len = vendor->attrs[0].cmd_attr.len;
+ }
+
+ if (vendor->num_attrs == 2 && vendor->attrs[1].valid) {
+ outbuf = vendor->attrs[1].cmd_attr.ptr;
+ outbuf_len = vendor->attrs[1].cmd_attr.len;
+ }
+ }
+ INIT_UDATA_BUF_OR_NULL(udata, inbuf, outbuf, inbuf_len, outbuf_len);
+}
+
+DECLARE_UVERBS_ATTR_SPEC(
+ uverbs_get_context_spec,
+ UVERBS_ATTR_PTR_OUT(GET_CONTEXT_RESP,
+ sizeof(struct ib_uverbs_get_context_resp)));
+EXPORT_SYMBOL(uverbs_get_context_spec);
+
+int uverbs_get_context(struct ib_device *ib_dev,
+ struct ib_uverbs_file *file,
+ struct uverbs_attr_array *common,
+ struct uverbs_attr_array *vendor,
+ void *priv)
+{
+ struct ib_udata uhw;
+ struct ib_uverbs_get_context_resp resp;
+ struct ib_ucontext *ucontext;
+ struct file *filp;
+ int ret;
+
+ if (!common->attrs[GET_CONTEXT_RESP].valid)
+ return -EINVAL;
+
+ /* Temporary, only until vendors get the new uverbs_attr_array */
+ create_udata(vendor, &uhw);
+
+ mutex_lock(&file->mutex);
+
+ if (file->ucontext) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ucontext = ib_dev->alloc_ucontext(ib_dev, &uhw);
+ if (IS_ERR(ucontext)) {
+ ret = PTR_ERR(ucontext);
+ goto err;
+ }
+
+ ucontext->device = ib_dev;
+ ret = ib_uverbs_uobject_type_initialize_ucontext(ucontext);
+ if (ret)
+ goto err_ctx;
+
+ rcu_read_lock();
+ ucontext->tgid = get_task_pid(current->group_leader, PIDTYPE_PID);
+ rcu_read_unlock();
+ ucontext->closing = 0;
+
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+ ucontext->umem_tree = RB_ROOT;
+ init_rwsem(&ucontext->umem_rwsem);
+ ucontext->odp_mrs_count = 0;
+ INIT_LIST_HEAD(&ucontext->no_private_counters);
+
+ if (!(ib_dev->attrs.device_cap_flags & IB_DEVICE_ON_DEMAND_PAGING))
+ ucontext->invalidate_range = NULL;
+
+#endif
+
+ resp.num_comp_vectors = file->device->num_comp_vectors;
+
+ ret = get_unused_fd_flags(O_CLOEXEC);
+ if (ret < 0)
+ goto err_free;
+ resp.async_fd = ret;
+
+ filp = ib_uverbs_alloc_event_file(file, ib_dev, 1);
+ if (IS_ERR(filp)) {
+ ret = PTR_ERR(filp);
+ goto err_fd;
+ }
+
+ if (copy_to_user(common->attrs[GET_CONTEXT_RESP].cmd_attr.ptr,
+ &resp, sizeof(resp))) {
+ ret = -EFAULT;
+ goto err_file;
+ }
+
+ file->ucontext = ucontext;
+ ucontext->ufile = file;
+
+ fd_install(resp.async_fd, filp);
+
+ mutex_unlock(&file->mutex);
+
+ return 0;
+
+err_file:
+ ib_uverbs_free_async_event_file(file);
+ fput(filp);
+
+err_fd:
+ put_unused_fd(resp.async_fd);
+
+err_free:
+ put_pid(ucontext->tgid);
+ ib_uverbs_uobject_type_release_ucontext(ucontext);
+
+err_ctx:
+ ib_dev->dealloc_ucontext(ucontext);
+err:
+ mutex_unlock(&file->mutex);
+ return ret;
+}
+EXPORT_SYMBOL(uverbs_get_context);
+DECLARE_UVERBS_CTX_ACTION(uverbs_action_get_context, uverbs_get_context, NULL,
+ &uverbs_get_context_spec, &uverbs_uhw_compat_spec);
+EXPORT_SYMBOL(uverbs_action_get_context);
+
+DECLARE_UVERBS_ATTR_SPEC(
+ uverbs_query_device_spec,
+ UVERBS_ATTR_PTR_OUT(QUERY_DEVICE_RESP, sizeof(struct ib_uverbs_query_device_resp)),
+ UVERBS_ATTR_PTR_OUT(QUERY_DEVICE_ODP, sizeof(struct ib_uverbs_odp_caps)),
+ UVERBS_ATTR_PTR_OUT(QUERY_DEVICE_TIMESTAMP_MASK, sizeof(__u64)),
+ UVERBS_ATTR_PTR_OUT(QUERY_DEVICE_HCA_CORE_CLOCK, sizeof(__u64)),
+ UVERBS_ATTR_PTR_OUT(QUERY_DEVICE_CAP_FLAGS, sizeof(__u64)));
+EXPORT_SYMBOL(uverbs_query_device_spec);
+
+int uverbs_query_device_handler(struct ib_device *ib_dev,
+ struct ib_ucontext *ucontext,
+ struct uverbs_attr_array *common,
+ struct uverbs_attr_array *vendor,
+ void *priv)
+{
+ struct ib_device_attr attr = {};
+ struct ib_udata uhw;
+ int err;
+
+ /* Temporary, only until vendors get the new uverbs_attr_array */
+ create_udata(vendor, &uhw);
+
+ err = ib_dev->query_device(ib_dev, &attr, &uhw);
+ if (err)
+ return err;
+
+ if (common->attrs[QUERY_DEVICE_RESP].valid) {
+ struct ib_uverbs_query_device_resp resp = {};
+
+ uverbs_copy_query_dev_fields(ib_dev, &resp, &attr);
+ if (copy_to_user(common->attrs[QUERY_DEVICE_RESP].cmd_attr.ptr,
+ &resp, sizeof(resp)))
+ return -EFAULT;
+ }
+
+#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
+ if (common->attrs[QUERY_DEVICE_ODP].valid) {
+ struct ib_uverbs_odp_caps odp_caps;
+
+ odp_caps.general_caps = attr.odp_caps.general_caps;
+ odp_caps.per_transport_caps.rc_odp_caps =
+ attr.odp_caps.per_transport_caps.rc_odp_caps;
+ odp_caps.per_transport_caps.uc_odp_caps =
+ attr.odp_caps.per_transport_caps.uc_odp_caps;
+ odp_caps.per_transport_caps.ud_odp_caps =
+ attr.odp_caps.per_transport_caps.ud_odp_caps;
+
+ if (copy_to_user(common->attrs[QUERY_DEVICE_ODP].cmd_attr.ptr,
+ &odp_caps, sizeof(odp_caps)))
+ return -EFAULT;
+ }
+#endif
+ if (UVERBS_COPY_TO(common, QUERY_DEVICE_TIMESTAMP_MASK,
+ &attr.timestamp_mask) == -EFAULT)
+ return -EFAULT;
+
+ if (UVERBS_COPY_TO(common, QUERY_DEVICE_HCA_CORE_CLOCK,
+ &attr.hca_core_clock) == -EFAULT)
+ return -EFAULT;
+
+ if (UVERBS_COPY_TO(common, QUERY_DEVICE_CAP_FLAGS,
+ &attr.device_cap_flags) == -EFAULT)
+ return -EFAULT;
+
+ return 0;
+}
+EXPORT_SYMBOL(uverbs_query_device_handler);
+DECLARE_UVERBS_ACTION(uverbs_action_query_device, uverbs_query_device_handler,
+ NULL, &uverbs_query_device_spec, &uverbs_uhw_compat_spec);
+EXPORT_SYMBOL(uverbs_action_query_device);
+
+DECLARE_UVERBS_ATTR_SPEC(
+ uverbs_alloc_pd_spec,
+ UVERBS_ATTR_IDR(ALLOC_PD_HANDLE, UVERBS_TYPE_PD,
+ UVERBS_IDR_ACCESS_NEW));
+EXPORT_SYMBOL(uverbs_alloc_pd_spec);
+
+int uverbs_alloc_pd_handler(struct ib_device *ib_dev,
+ struct ib_ucontext *ucontext,
+ struct uverbs_attr_array *common,
+ struct uverbs_attr_array *vendor,
+ void *priv)
+{
+ struct ib_udata uhw;
+ struct ib_uobject *uobject;
+ struct ib_pd *pd;
+
+ if (!common->attrs[ALLOC_PD_HANDLE].valid)
+ return -EINVAL;
+
+ /* Temporary, only until vendors get the new uverbs_attr_array */
+ create_udata(vendor, &uhw);
+
+ pd = ib_dev->alloc_pd(ib_dev, ucontext, &uhw);
+ if (IS_ERR(pd))
+ return PTR_ERR(pd);
+
+ uobject = common->attrs[ALLOC_PD_HANDLE].obj_attr.uobject;
+ pd->device = ib_dev;
+ pd->uobject = uobject;
+ uobject->object = pd;
+ pd->local_mr = NULL;
+ atomic_set(&pd->usecnt, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL(uverbs_alloc_pd_handler);
+DECLARE_UVERBS_ACTION(uverbs_action_alloc_pd, uverbs_alloc_pd_handler, NULL,
+ &uverbs_alloc_pd_spec, &uverbs_uhw_compat_spec);
+EXPORT_SYMBOL(uverbs_action_alloc_pd);
+
+DECLARE_UVERBS_ATTR_SPEC(
+ uverbs_reg_mr_spec,
+ UVERBS_ATTR_IDR(REG_MR_HANDLE, UVERBS_TYPE_MR, UVERBS_IDR_ACCESS_NEW),
+ UVERBS_ATTR_IDR(REG_MR_PD_HANDLE, UVERBS_TYPE_PD, UVERBS_IDR_ACCESS_READ),
+ UVERBS_ATTR_PTR_IN(REG_MR_CMD, sizeof(struct ib_uverbs_ioctl_reg_mr)),
+ UVERBS_ATTR_PTR_OUT(REG_MR_RESP, sizeof(struct ib_uverbs_ioctl_reg_mr_resp)));
+EXPORT_SYMBOL(uverbs_reg_mr_spec);
+
+int uverbs_reg_mr_handler(struct ib_device *ib_dev,
+ struct ib_ucontext *ucontext,
+ struct uverbs_attr_array *common,
+ struct uverbs_attr_array *vendor,
+ void *priv)
+{
+ struct ib_uverbs_ioctl_reg_mr cmd;
+ struct ib_uverbs_ioctl_reg_mr_resp resp;
+ struct ib_udata uhw;
+ struct ib_uobject *uobject;
+ struct ib_pd *pd;
+ struct ib_mr *mr;
+ int ret;
+
+ if (!common->attrs[REG_MR_HANDLE].valid ||
+ !common->attrs[REG_MR_PD_HANDLE].valid ||
+ !common->attrs[REG_MR_CMD].valid ||
+ !common->attrs[REG_MR_RESP].valid)
+ return -EINVAL;
+
+ if (copy_from_user(&cmd, common->attrs[REG_MR_CMD].cmd_attr.ptr, sizeof(cmd)))
+ return -EFAULT;
+
+ if ((cmd.start & ~PAGE_MASK) != (cmd.hca_va & ~PAGE_MASK))
+ return -EINVAL;
+
+ ret = ib_check_mr_access(cmd.access_flags);
+ if (ret)
+ return ret;
+
+ /* Temporary, only until vendors get the new uverbs_attr_array */
+ create_udata(vendor, &uhw);
+
+ uobject = common->attrs[REG_MR_HANDLE].obj_attr.uobject;
+ pd = common->attrs[REG_MR_PD_HANDLE].obj_attr.uobject->object;
+
+ if (cmd.access_flags & IB_ACCESS_ON_DEMAND) {
+ if (!(pd->device->attrs.device_cap_flags &
+ IB_DEVICE_ON_DEMAND_PAGING)) {
+ pr_debug("ODP support not available\n");
+ return -EINVAL;
+ }
+ }
+
+ mr = pd->device->reg_user_mr(pd, cmd.start, cmd.length, cmd.hca_va,
+ cmd.access_flags, &uhw);
+ if (IS_ERR(mr))
+ return PTR_ERR(mr);
+
+ mr->device = pd->device;
+ mr->pd = pd;
+ mr->uobject = uobject;
+ atomic_inc(&pd->usecnt);
+ uobject->object = mr;
+
+ resp.lkey = mr->lkey;
+ resp.rkey = mr->rkey;
+
+ if (copy_to_user(common->attrs[REG_MR_RESP].cmd_attr.ptr,
+ &resp, sizeof(resp))) {
+ ret = -EFAULT;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ ib_dereg_mr(mr);
+ return ret;
+}
+EXPORT_SYMBOL(uverbs_reg_mr_handler);
+
+DECLARE_UVERBS_ACTION(uverbs_action_reg_mr, uverbs_reg_mr_handler, NULL,
+ &uverbs_reg_mr_spec, &uverbs_uhw_compat_spec);
+EXPORT_SYMBOL(uverbs_action_reg_mr);
+
+DECLARE_UVERBS_ATTR_SPEC(
+ uverbs_dereg_mr_spec,
+ UVERBS_ATTR_IDR(DEREG_MR_HANDLE, UVERBS_TYPE_MR, UVERBS_IDR_ACCESS_DESTROY));
+EXPORT_SYMBOL(uverbs_dereg_mr_spec);
+
+int uverbs_dereg_mr_handler(struct ib_device *ib_dev,
+ struct ib_ucontext *ucontext,
+ struct uverbs_attr_array *common,
+ struct uverbs_attr_array *vendor,
+ void *priv)
+{
+ struct ib_mr *mr;
+
+ if (!common->attrs[REG_MR_HANDLE].valid)
+ return -EINVAL;
+
+ mr = common->attrs[DEREG_MR_HANDLE].obj_attr.uobject->object;
+
+ /* dereg_mr doesn't support vendor data */
+ return ib_dereg_mr(mr);
+};
+EXPORT_SYMBOL(uverbs_dereg_mr_handler);
+
+DECLARE_UVERBS_ACTION(uverbs_action_dereg_mr, uverbs_dereg_mr_handler, NULL,
+ &uverbs_dereg_mr_spec);
+EXPORT_SYMBOL(uverbs_action_dereg_mr);
+
+DECLARE_UVERBS_ATTR_SPEC(
+ uverbs_create_comp_channel_spec,
+ UVERBS_ATTR_FD(CREATE_COMP_CHANNEL_FD, UVERBS_TYPE_COMP_CHANNEL, UVERBS_IDR_ACCESS_NEW));
+EXPORT_SYMBOL(uverbs_create_comp_channel_spec);
+
+int uverbs_create_comp_channel_handler(struct ib_device *ib_dev,
+ struct ib_ucontext *ucontext,
+ struct uverbs_attr_array *common,
+ struct uverbs_attr_array *vendor,
+ void *priv)
+{
+ struct ib_uverbs_event_file *ev_file;
+
+ if (!common->attrs[CREATE_COMP_CHANNEL_FD].valid)
+ return -EINVAL;
+
+ ev_file = uverbs_fd_to_priv(common->attrs[CREATE_COMP_CHANNEL_FD].obj_attr.uobject);
+ kref_init(&ev_file->ref);
+ spin_lock_init(&ev_file->lock);
+ INIT_LIST_HEAD(&ev_file->event_list);
+ init_waitqueue_head(&ev_file->poll_wait);
+ ev_file->async_queue = NULL;
+ ev_file->is_closed = 0;
+
+ /*
+ * The original code puts the handle in an event list....
+ * Currently, it's on our context
+ */
+
+ return 0;
+}
+EXPORT_SYMBOL(uverbs_create_comp_channel_handler);
+
+DECLARE_UVERBS_ACTION(uverbs_action_create_comp_channel, uverbs_create_comp_channel_handler, NULL,
+ &uverbs_create_comp_channel_spec);
+EXPORT_SYMBOL(uverbs_action_create_comp_channel);
+
+DECLARE_UVERBS_ATTR_SPEC(
+ uverbs_create_cq_spec,
+ UVERBS_ATTR_IDR(CREATE_CQ_HANDLE, UVERBS_TYPE_CQ, UVERBS_IDR_ACCESS_NEW),
+ UVERBS_ATTR_PTR_IN(CREATE_CQ_CQE, sizeof(__u32)),
+ UVERBS_ATTR_PTR_IN(CREATE_CQ_USER_HANDLE, sizeof(__u64)),
+ UVERBS_ATTR_FD(CREATE_CQ_COMP_CHANNEL, UVERBS_TYPE_COMP_CHANNEL, UVERBS_IDR_ACCESS_READ),
+ UVERBS_ATTR_PTR_IN(CREATE_CQ_COMP_VECTOR, sizeof(__u32)),
+ UVERBS_ATTR_PTR_IN(CREATE_CQ_FLAGS, sizeof(__u32)),
+ UVERBS_ATTR_PTR_OUT(CREATE_CQ_RESP_CQE, sizeof(__u32)));
+EXPORT_SYMBOL(uverbs_create_cq_spec);
+
+int uverbs_create_cq_handler(struct ib_device *ib_dev,
+ struct ib_ucontext *ucontext,
+ struct uverbs_attr_array *common,
+ struct uverbs_attr_array *vendor,
+ void *priv)
+{
+ struct ib_ucq_object *obj;
+ struct ib_udata uhw;
+ int ret;
+ __u64 user_handle = 0;
+ struct ib_cq_init_attr attr = {};
+ struct ib_cq *cq;
+ struct ib_uverbs_event_file *ev_file = NULL;
+
+ /*
+ * Currently, COMP_VECTOR is mandatory, but that could be lifted in the
+ * future.
+ */
+ if (!common->attrs[CREATE_CQ_HANDLE].valid ||
+ !common->attrs[CREATE_CQ_RESP_CQE].valid)
+ return -EINVAL;
+
+ ret = UVERBS_COPY_FROM(&attr.comp_vector, common, CREATE_CQ_COMP_VECTOR);
+ if (!ret)
+ ret = UVERBS_COPY_FROM(&attr.cqe, common, CREATE_CQ_CQE);
+ if (ret)
+ return ret;
+
+ /* Optional params */
+ if (UVERBS_COPY_FROM(&attr.flags, common, CREATE_CQ_FLAGS) == -EFAULT ||
+ UVERBS_COPY_FROM(&user_handle, common, CREATE_CQ_USER_HANDLE) == -EFAULT)
+ return -EFAULT;
+
+ if (common->attrs[CREATE_CQ_COMP_CHANNEL].valid) {
+ ev_file = uverbs_fd_to_priv(common->attrs[CREATE_CQ_COMP_CHANNEL].obj_attr.uobject);
+ kref_get(&ev_file->ref);
+ }
+
+ if (attr.comp_vector >= ucontext->ufile->device->num_comp_vectors)
+ return -EINVAL;
+
+ obj = container_of(common->attrs[CREATE_CQ_HANDLE].obj_attr.uobject,
+ typeof(*obj), uobject);
+ obj->uverbs_file = ucontext->ufile;
+ obj->comp_events_reported = 0;
+ obj->async_events_reported = 0;
+ INIT_LIST_HEAD(&obj->comp_list);
+ INIT_LIST_HEAD(&obj->async_list);
+
+ /* Temporary, only until vendors get the new uverbs_attr_array */
+ create_udata(vendor, &uhw);
+
+ cq = ib_dev->create_cq(ib_dev, &attr, ucontext, &uhw);
+ if (IS_ERR(cq))
+ return PTR_ERR(cq);
+
+ cq->device = ib_dev;
+ cq->uobject = &obj->uobject;
+ cq->comp_handler = ib_uverbs_comp_handler;
+ cq->event_handler = ib_uverbs_cq_event_handler;
+ cq->cq_context = ev_file;
+ obj->uobject.object = cq;
+ obj->uobject.user_handle = user_handle;
+ atomic_set(&cq->usecnt, 0);
+
+ ret = UVERBS_COPY_TO(common, CREATE_CQ_RESP_CQE, &cq->cqe);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ ib_destroy_cq(cq);
+ return ret;
+};
+EXPORT_SYMBOL(uverbs_create_cq_handler);
+
+DECLARE_UVERBS_ACTION(uverbs_action_create_cq, uverbs_create_cq_handler, NULL,
+ &uverbs_create_cq_spec, &uverbs_uhw_compat_spec);
+EXPORT_SYMBOL(uverbs_action_create_cq);
+
+DECLARE_UVERBS_ACTIONS(
+ uverbs_actions_comp_channel,
+ ADD_UVERBS_ACTION_PTR(UVERBS_COMP_CHANNEL_CREATE, &uverbs_action_create_comp_channel),
+);
+EXPORT_SYMBOL(uverbs_actions_comp_channel);
+
+DECLARE_UVERBS_ACTIONS(
+ uverbs_actions_cq,
+ ADD_UVERBS_ACTION_PTR(UVERBS_CQ_CREATE, &uverbs_action_create_cq),
+);
+EXPORT_SYMBOL(uverbs_actions_cq);
+
+DECLARE_UVERBS_ACTIONS(
+ uverbs_actions_mr,
+ ADD_UVERBS_ACTION_PTR(UVERBS_MR_REG, &uverbs_action_reg_mr),
+ ADD_UVERBS_ACTION_PTR(UVERBS_MR_DEREG, &uverbs_action_dereg_mr),
+);
+EXPORT_SYMBOL(uverbs_actions_mr);
+
+DECLARE_UVERBS_ACTIONS(
+ uverbs_actions_pd,
+ ADD_UVERBS_ACTION_PTR(UVERBS_PD_ALLOC, &uverbs_action_alloc_pd),
+);
+EXPORT_SYMBOL(uverbs_actions_pd);
+
+DECLARE_UVERBS_ACTIONS(
+ uverbs_actions_device,
+ ADD_UVERBS_ACTION_PTR(UVERBS_DEVICE_QUERY, &uverbs_action_query_device),
+ ADD_UVERBS_ACTION_PTR(UVERBS_DEVICE_ALLOC_CONTEXT, &uverbs_action_get_context),
+);
+EXPORT_SYMBOL(uverbs_actions_device);
+
+DECLARE_UVERBS_TYPE(uverbs_type_comp_channel,
+ /* 1 is used in order to free the comp_channel after the CQs */
+ &UVERBS_TYPE_ALLOC_FD(1, sizeof(struct ib_uobject) + sizeof(struct ib_uverbs_event_file),
+ uverbs_free_event_file,
+ &uverbs_refactored_event_fops,
+ "[infinibandevent]", O_RDONLY),
+ &uverbs_actions_comp_channel);
+EXPORT_SYMBOL(uverbs_type_comp_channel);
+
+DECLARE_UVERBS_TYPE(uverbs_type_cq,
+ /* 1 is used in order to free the MR after all the MWs */
+ &UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_ucq_object), 0,
+ uverbs_free_cq),
+ &uverbs_actions_cq);
+EXPORT_SYMBOL(uverbs_type_cq);
+
+DECLARE_UVERBS_TYPE(uverbs_type_mr,
+ /* 1 is used in order to free the MR after all the MWs */
+ &UVERBS_TYPE_ALLOC_IDR(1, uverbs_free_mr),
+ &uverbs_actions_mr);
+EXPORT_SYMBOL(uverbs_type_mr);
+
+DECLARE_UVERBS_TYPE(uverbs_type_pd,
+ /* 2 is used in order to free the PD after all objects */
+ &UVERBS_TYPE_ALLOC_IDR(2, uverbs_free_pd),
+ &uverbs_actions_pd);
+EXPORT_SYMBOL(uverbs_type_pd);
+
+DECLARE_UVERBS_TYPE(uverbs_type_device, NULL, &uverbs_actions_device);
+EXPORT_SYMBOL(uverbs_type_device);
+
+DECLARE_UVERBS_TYPES(uverbs_types,
+ ADD_UVERBS_TYPE(UVERBS_TYPE_DEVICE, uverbs_type_device),
+ ADD_UVERBS_TYPE(UVERBS_TYPE_PD, uverbs_type_pd),
+ ADD_UVERBS_TYPE(UVERBS_TYPE_MR, uverbs_type_mr),
+ ADD_UVERBS_TYPE(UVERBS_TYPE_COMP_CHANNEL, uverbs_type_comp_channel),
+ ADD_UVERBS_TYPE(UVERBS_TYPE_CQ, uverbs_type_cq),
+);
+EXPORT_SYMBOL(uverbs_types);
+
+DECLARE_UVERBS_TYPES_GROUP(uverbs_types_group, &uverbs_types);
+EXPORT_SYMBOL(uverbs_types_group);
+
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index c4a094d..680a89a 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -364,6 +364,43 @@ static int ib_uverbs_event_close(struct inode *inode, struct file *filp)
return 0;
}
+static void ib_uverbs_release_refactored_event_file(struct kref *ref)
+{
+ struct ib_uverbs_event_file *file =
+ container_of(ref, struct ib_uverbs_event_file, ref);
+
+ ib_uverbs_cleanup_fd(file);
+}
+
+/* TODO: REFACTOR */
+static int ib_uverbs_event_refactored_close(struct inode *inode, struct file *filp)
+{
+ struct ib_uverbs_event_file *file = filp->private_data;
+ struct ib_uverbs_event *entry, *tmp;
+
+ spin_lock_irq(&file->lock);
+ list_for_each_entry_safe(entry, tmp, &file->event_list, list) {
+ if (entry->counter)
+ list_del(&entry->obj_list);
+ kfree(entry);
+ }
+ spin_unlock_irq(&file->lock);
+
+ ib_uverbs_close_fd(filp);
+ kref_put(&file->ref, ib_uverbs_release_refactored_event_file);
+
+ return 0;
+}
+
+const struct file_operations uverbs_refactored_event_fops = {
+ .owner = THIS_MODULE,
+ .read = ib_uverbs_event_read,
+ .poll = ib_uverbs_event_poll,
+ .release = ib_uverbs_event_refactored_close,
+ .fasync = ib_uverbs_event_fasync,
+ .llseek = no_llseek,
+};
+
static const struct file_operations uverbs_event_fops = {
.owner = THIS_MODULE,
.read = ib_uverbs_event_read,
diff --git a/include/rdma/uverbs_ioctl.h b/include/rdma/uverbs_ioctl.h
index 2f50045..12642cc 100644
--- a/include/rdma/uverbs_ioctl.h
+++ b/include/rdma/uverbs_ioctl.h
@@ -134,6 +134,153 @@ struct uverbs_types_group {
void *priv;
};
+#define UVERBS_ATTR(_id, _len, _type) \
+ [_id] = {.len = _len, .type = _type}
+#define UVERBS_ATTR_PTR_IN(_id, _len) \
+ UVERBS_ATTR(_id, _len, UVERBS_ATTR_TYPE_PTR_IN)
+#define UVERBS_ATTR_PTR_OUT(_id, _len) \
+ UVERBS_ATTR(_id, _len, UVERBS_ATTR_TYPE_PTR_OUT)
+#define UVERBS_ATTR_IDR(_id, _idr_type, _access) \
+ [_id] = {.type = UVERBS_ATTR_TYPE_IDR, \
+ .obj = {.obj_type = _idr_type, \
+ .access = _access \
+ } }
+#define UVERBS_ATTR_FD(_id, _fd_type, _access) \
+ [_id] = {.type = UVERBS_ATTR_TYPE_FD, \
+ .obj = {.obj_type = _fd_type, \
+ .access = _access + BUILD_BUG_ON_ZERO( \
+ _access != UVERBS_IDR_ACCESS_NEW && \
+ _access != UVERBS_IDR_ACCESS_READ) \
+ } }
+#define _UVERBS_ATTR_SPEC_SZ(...) \
+ (sizeof((const struct uverbs_attr_spec[]){__VA_ARGS__}) / \
+ sizeof(const struct uverbs_attr_spec))
+#define UVERBS_ATTR_SPEC(...) \
+ ((const struct uverbs_attr_group_spec) \
+ {.attrs = (struct uverbs_attr_spec[]){__VA_ARGS__}, \
+ .num_attrs = _UVERBS_ATTR_SPEC_SZ(__VA_ARGS__)})
+#define DECLARE_UVERBS_ATTR_SPEC(name, ...) \
+ const struct uverbs_attr_group_spec name = \
+ UVERBS_ATTR_SPEC(__VA_ARGS__)
+#define _UVERBS_ATTR_ACTION_SPEC_SZ(...) \
+ (sizeof((const struct uverbs_attr_group_spec *[]){__VA_ARGS__}) / \
+ sizeof(const struct uverbs_attr_group_spec *))
+#define _UVERBS_ATTR_ACTION_SPEC(_distfn, _priv, ...) \
+ {.dist = _distfn, \
+ .priv = _priv, \
+ .num_groups = _UVERBS_ATTR_ACTION_SPEC_SZ(__VA_ARGS__), \
+ .attr_groups = (const struct uverbs_attr_group_spec *[]){__VA_ARGS__} }
+#define UVERBS_ACTION_SPEC(...) \
+ _UVERBS_ATTR_ACTION_SPEC(ib_uverbs_std_dist, \
+ (void *)_UVERBS_ATTR_ACTION_SPEC_SZ(__VA_ARGS__),\
+ __VA_ARGS__)
+#define UVERBS_ACTION(_handler, _priv, ...) \
+ ((const struct uverbs_action) { \
+ .priv = &(struct uverbs_action_std_handler) \
+ {.handler = _handler, \
+ .priv = _priv}, \
+ .handler = uverbs_action_std_handle, \
+ .spec = UVERBS_ACTION_SPEC(__VA_ARGS__)})
+#define UVERBS_CTX_ACTION(_handler, _priv, ...) \
+ ((const struct uverbs_action){ \
+ .priv = &(struct uverbs_action_std_ctx_handler) \
+ {.handler = _handler, \
+ .priv = _priv}, \
+ .handler = uverbs_action_std_ctx_handle, \
+ .spec = UVERBS_ACTION_SPEC(__VA_ARGS__)})
+#define _UVERBS_ACTIONS_SZ(...) \
+ (sizeof((const struct uverbs_action *[]){__VA_ARGS__}) / \
+ sizeof(const struct uverbs_action *))
+#define ADD_UVERBS_ACTION(action_idx, _handler, _priv, ...) \
+ [action_idx] = &UVERBS_ACTION(_handler, _priv, __VA_ARGS__)
+#define DECLARE_UVERBS_ACTION(name, _handler, _priv, ...) \
+ const struct uverbs_action name = \
+ UVERBS_ACTION(_handler, _priv, __VA_ARGS__)
+#define ADD_UVERBS_CTX_ACTION(action_idx, _handler, _priv, ...) \
+ [action_idx] = &UVERBS_CTX_ACTION(_handler, _priv, __VA_ARGS__)
+#define DECLARE_UVERBS_CTX_ACTION(name, _handler, _priv, ...) \
+ const struct uverbs_action name = \
+ UVERBS_CTX_ACTION(_handler, _priv, __VA_ARGS__)
+#define ADD_UVERBS_ACTION_PTR(idx, ptr) \
+ [idx] = ptr
+#define UVERBS_ACTIONS(...) \
+ ((const struct uverbs_type_actions_group) \
+ {.num_actions = _UVERBS_ACTIONS_SZ(__VA_ARGS__), \
+ .actions = (const struct uverbs_action *[]){__VA_ARGS__} })
+#define DECLARE_UVERBS_ACTIONS(name, ...) \
+ const struct uverbs_type_actions_group name = \
+ UVERBS_ACTIONS(__VA_ARGS__)
+#define _UVERBS_ACTIONS_GROUP_SZ(...) \
+ (sizeof((const struct uverbs_type_actions_group*[]){__VA_ARGS__}) / \
+ sizeof(const struct uverbs_type_actions_group *))
+#define UVERBS_TYPE_ALLOC_FD(_order, _obj_size, _free_fn, _fops, _name, _flags)\
+ ((const struct uverbs_type_alloc_action) \
+ {.type = UVERBS_ATTR_TYPE_FD, \
+ .order = _order, \
+ .obj_size = _obj_size, \
+ .free_fn = _free_fn, \
+ .fd = {.fops = _fops, \
+ .name = _name, \
+ .flags = _flags} })
+#define UVERBS_TYPE_ALLOC_IDR_SZ(_size, _order, _free_fn) \
+ ((const struct uverbs_type_alloc_action) \
+ {.type = UVERBS_ATTR_TYPE_IDR, \
+ .order = _order, \
+ .free_fn = _free_fn, \
+ .obj_size = _size,})
+#define UVERBS_TYPE_ALLOC_IDR(_order, _free_fn) \
+ UVERBS_TYPE_ALLOC_IDR_SZ(sizeof(struct ib_uobject), _order, _free_fn)
+#define _DECLARE_UVERBS_TYPE(name, _alloc, _dist, _priv, ...) \
+ const struct uverbs_type name = { \
+ .alloc = _alloc, \
+ .dist = _dist, \
+ .priv = _priv, \
+ .num_groups = _UVERBS_ACTIONS_GROUP_SZ(__VA_ARGS__), \
+ .action_groups = (const struct uverbs_type_actions_group *[]){__VA_ARGS__} \
+ }
+#define DECLARE_UVERBS_TYPE(name, _alloc, ...) \
+ _DECLARE_UVERBS_TYPE(name, _alloc, ib_uverbs_std_dist, NULL, \
+ __VA_ARGS__)
+#define _UVERBS_TYPE_SZ(...) \
+ (sizeof((const struct uverbs_type *[]){__VA_ARGS__}) / \
+ sizeof(const struct uverbs_type *))
+#define ADD_UVERBS_TYPE_ACTIONS(type_idx, ...) \
+ [type_idx] = &UVERBS_ACTIONS(__VA_ARGS__)
+#define ADD_UVERBS_TYPE(type_idx, type_ptr) \
+ [type_idx] = ((const struct uverbs_type * const)&type_ptr)
+#define UVERBS_TYPES(...) ((const struct uverbs_types) \
+ {.num_types = _UVERBS_TYPE_SZ(__VA_ARGS__), \
+ .types = (const struct uverbs_type *[]){__VA_ARGS__} })
+#define DECLARE_UVERBS_TYPES(name, ...) \
+ const struct uverbs_types name = UVERBS_TYPES(__VA_ARGS__)
+
+#define _UVERBS_TYPES_SZ(...) \
+ (sizeof((const struct uverbs_types *[]){__VA_ARGS__}) / \
+ sizeof(const struct uverbs_types *))
+
+#define UVERBS_TYPES_GROUP(_dist, _priv, ...) \
+ ((const struct uverbs_types_group){ \
+ .dist = _dist, \
+ .priv = _priv, \
+ .type_groups = (const struct uverbs_types *[]){__VA_ARGS__},\
+ .num_groups = _UVERBS_TYPES_SZ(__VA_ARGS__)})
+#define _DECLARE_UVERBS_TYPES_GROUP(name, _dist, _priv, ...) \
+ const struct uverbs_types_group name = UVERBS_TYPES_GROUP(_dist, _priv,\
+ __VA_ARGS__)
+#define DECLARE_UVERBS_TYPES_GROUP(name, ...) \
+ _DECLARE_UVERBS_TYPES_GROUP(name, ib_uverbs_std_dist, NULL, __VA_ARGS__)
+
+#define UVERBS_COPY_TO(attr_array, idx, from) \
+ ((attr_array)->attrs[idx].valid ? \
+ (copy_to_user((attr_array)->attrs[idx].cmd_attr.ptr, (from), \
+ (attr_array)->attrs[idx].cmd_attr.len) ? \
+ -EFAULT : 0) : -ENOENT)
+#define UVERBS_COPY_FROM(to, attr_array, idx) \
+ ((attr_array)->attrs[idx].valid ? \
+ (copy_from_user((to), (attr_array)->attrs[idx].cmd_attr.ptr, \
+ (attr_array)->attrs[idx].cmd_attr.len) ? \
+ -EFAULT : 0) : -ENOENT)
+
/* =================================================
* Parsing infrastructure
* =================================================
diff --git a/include/rdma/uverbs_ioctl_cmd.h b/include/rdma/uverbs_ioctl_cmd.h
index eef9418..bd17d3f 100644
--- a/include/rdma/uverbs_ioctl_cmd.h
+++ b/include/rdma/uverbs_ioctl_cmd.h
@@ -98,5 +98,157 @@ enum uverbs_common_types {
UVERBS_TYPE_LAST,
};
+enum uverbs_create_qp_cmd_attr {
+ CREATE_QP_CMD,
+ CREATE_QP_RESP,
+ CREATE_QP_QP,
+ CREATE_QP_PD,
+ CREATE_QP_RECV_CQ,
+ CREATE_QP_SEND_CQ,
+};
+
+enum uverbs_destroy_qp_cmd_attr {
+ DESTROY_QP_RESP,
+ DESTROY_QP_QP,
+};
+
+enum uverbs_create_cq_cmd_attr {
+ CREATE_CQ_HANDLE,
+ CREATE_CQ_CQE,
+ CREATE_CQ_USER_HANDLE,
+ CREATE_CQ_COMP_CHANNEL,
+ CREATE_CQ_COMP_VECTOR,
+ CREATE_CQ_FLAGS,
+ CREATE_CQ_RESP_CQE,
+};
+
+enum uverbs_create_comp_channel_cmd_attr {
+ CREATE_COMP_CHANNEL_FD,
+};
+
+enum uverbs_get_context {
+ GET_CONTEXT_RESP,
+};
+
+enum uverbs_query_device {
+ QUERY_DEVICE_RESP,
+ QUERY_DEVICE_ODP,
+ QUERY_DEVICE_TIMESTAMP_MASK,
+ QUERY_DEVICE_HCA_CORE_CLOCK,
+ QUERY_DEVICE_CAP_FLAGS,
+};
+
+enum uverbs_alloc_pd {
+ ALLOC_PD_HANDLE,
+};
+
+enum uverbs_reg_mr {
+ REG_MR_HANDLE,
+ REG_MR_PD_HANDLE,
+ REG_MR_CMD,
+ REG_MR_RESP
+};
+
+enum uverbs_dereg_mr {
+ DEREG_MR_HANDLE,
+};
+
+extern const struct uverbs_attr_group_spec uverbs_uhw_compat_spec;
+extern const struct uverbs_attr_group_spec uverbs_get_context_spec;
+extern const struct uverbs_attr_group_spec uverbs_query_device_spec;
+extern const struct uverbs_attr_group_spec uverbs_alloc_pd_spec;
+extern const struct uverbs_attr_group_spec uverbs_reg_mr_spec;
+extern const struct uverbs_attr_group_spec uverbs_dereg_mr_spec;
+
+int uverbs_get_context(struct ib_device *ib_dev,
+ struct ib_uverbs_file *file,
+ struct uverbs_attr_array *common,
+ struct uverbs_attr_array *vendor,
+ void *priv);
+
+int uverbs_query_device_handler(struct ib_device *ib_dev,
+ struct ib_ucontext *ucontext,
+ struct uverbs_attr_array *common,
+ struct uverbs_attr_array *vendor,
+ void *priv);
+
+int uverbs_alloc_pd_handler(struct ib_device *ib_dev,
+ struct ib_ucontext *ucontext,
+ struct uverbs_attr_array *common,
+ struct uverbs_attr_array *vendor,
+ void *priv);
+
+int uverbs_reg_mr_handler(struct ib_device *ib_dev,
+ struct ib_ucontext *ucontext,
+ struct uverbs_attr_array *common,
+ struct uverbs_attr_array *vendor,
+ void *priv);
+
+int uverbs_dereg_mr_handler(struct ib_device *ib_dev,
+ struct ib_ucontext *ucontext,
+ struct uverbs_attr_array *common,
+ struct uverbs_attr_array *vendor,
+ void *priv);
+
+int uverbs_create_comp_channel_handler(struct ib_device *ib_dev,
+ struct ib_ucontext *ucontext,
+ struct uverbs_attr_array *common,
+ struct uverbs_attr_array *vendor,
+ void *priv);
+
+int uverbs_create_cq_handler(struct ib_device *ib_dev,
+ struct ib_ucontext *ucontext,
+ struct uverbs_attr_array *common,
+ struct uverbs_attr_array *vendor,
+ void *priv);
+
+extern const struct uverbs_action uverbs_action_get_context;
+extern const struct uverbs_action uverbs_action_create_cq;
+extern const struct uverbs_action uverbs_action_create_comp_channel;
+extern const struct uverbs_action uverbs_action_query_device;
+extern const struct uverbs_action uverbs_action_alloc_pd;
+extern const struct uverbs_action uverbs_action_reg_mr;
+extern const struct uverbs_action uverbs_action_dereg_mr;
+
+enum uverbs_actions_mr_ops {
+ UVERBS_MR_REG,
+ UVERBS_MR_DEREG,
+};
+
+extern const struct uverbs_type_actions_group uverbs_actions_mr;
+
+enum uverbs_actions_comp_channel_ops {
+ UVERBS_COMP_CHANNEL_CREATE,
+};
+
+extern const struct uverbs_type_actions_group uverbs_actions_comp_channel;
+
+enum uverbs_actions_cq_ops {
+ UVERBS_CQ_CREATE,
+};
+
+extern const struct uverbs_type_actions_group uverbs_actions_cq;
+
+enum uverbs_actions_pd_ops {
+ UVERBS_PD_ALLOC
+};
+
+extern const struct uverbs_type_actions_group uverbs_actions_pd;
+
+enum uverbs_actions_device_ops {
+ UVERBS_DEVICE_ALLOC_CONTEXT,
+ UVERBS_DEVICE_QUERY,
+};
+
+extern const struct uverbs_type_actions_group uverbs_actions_device;
+
+extern const struct uverbs_type uverbs_type_cq;
+extern const struct uverbs_type uverbs_type_comp_channel;
+extern const struct uverbs_type uverbs_type_mr;
+extern const struct uverbs_type uverbs_type_pd;
+extern const struct uverbs_type uverbs_type_device;
+
+extern const struct uverbs_types uverbs_common_types;
+extern const struct uverbs_types_group uverbs_types_group;
#endif
diff --git a/include/uapi/rdma/ib_user_verbs.h b/include/uapi/rdma/ib_user_verbs.h
index b6543d7..8650ac6 100644
--- a/include/uapi/rdma/ib_user_verbs.h
+++ b/include/uapi/rdma/ib_user_verbs.h
@@ -298,12 +298,25 @@ struct ib_uverbs_reg_mr {
__u64 driver_data[0];
};
+struct ib_uverbs_ioctl_reg_mr {
+ __u64 start;
+ __u64 length;
+ __u64 hca_va;
+ __u32 access_flags;
+ __u32 reserved;
+};
+
struct ib_uverbs_reg_mr_resp {
__u32 mr_handle;
__u32 lkey;
__u32 rkey;
};
+struct ib_uverbs_ioctl_reg_mr_resp {
+ __u32 lkey;
+ __u32 rkey;
+};
+
struct ib_uverbs_rereg_mr {
__u64 response;
__u32 mr_handle;
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [RFC ABI V4 5/7] RDMA/core: Add initialize and cleanup of common types
From: Matan Barak @ 2016-09-28 15:10 UTC (permalink / raw)
To: linux-rdma-u79uwXL29TY76Z2rM5mHXA
Cc: Doug Ledford, Jason Gunthorpe, Sean Hefty, Liran Liss,
Haggai Eran, Matan Barak, Majd Dibbiny, Christoph Lameter,
Leon Romanovsky
In-Reply-To: <1475075417-30189-1-git-send-email-matanb-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
The new ABI infrastructure introduced types per driver. Since current
drivers use common types, we mimics the current release ucontext
process using the new process.
Signed-off-by: Matan Barak <matanb-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Signed-off-by: Haggai Eran <haggaie-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Signed-off-by: Leon Romanovsky <leonro-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
---
drivers/infiniband/core/uverbs.h | 9 ++-
drivers/infiniband/core/uverbs_ioctl_cmd.c | 105 +++++++++++++++++++++++++++++
drivers/infiniband/core/uverbs_main.c | 102 ++--------------------------
include/rdma/uverbs_ioctl_cmd.h | 34 ++++++++++
4 files changed, 151 insertions(+), 99 deletions(-)
diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h
index aa783fd..206fff2 100644
--- a/drivers/infiniband/core/uverbs.h
+++ b/drivers/infiniband/core/uverbs.h
@@ -174,8 +174,6 @@ struct ib_ucq_object {
u32 async_events_reported;
};
-void idr_remove_uobj(struct ib_uobject *uobj);
-
struct file *ib_uverbs_alloc_event_file(struct ib_uverbs_file *uverbs_file,
struct ib_device *ib_dev,
int is_async);
@@ -198,6 +196,13 @@ void ib_uverbs_event_handler(struct ib_event_handler *handler,
void ib_uverbs_dealloc_xrcd(struct ib_uverbs_device *dev, struct ib_xrcd *xrcd);
int uverbs_dealloc_mw(struct ib_mw *mw);
+void ib_uverbs_release_ucq(struct ib_uverbs_file *file,
+ struct ib_uverbs_event_file *ev_file,
+ struct ib_ucq_object *uobj);
+void ib_uverbs_release_uevent(struct ib_uverbs_file *file,
+ struct ib_uevent_object *uobj);
+void ib_uverbs_detach_umcast(struct ib_qp *qp,
+ struct ib_uqp_object *uobj);
struct ib_uverbs_flow_spec {
union {
diff --git a/drivers/infiniband/core/uverbs_ioctl_cmd.c b/drivers/infiniband/core/uverbs_ioctl_cmd.c
index d84866d..6677dec 100644
--- a/drivers/infiniband/core/uverbs_ioctl_cmd.c
+++ b/drivers/infiniband/core/uverbs_ioctl_cmd.c
@@ -31,8 +31,11 @@
*/
#include <rdma/uverbs_ioctl_cmd.h>
+#include <rdma/ib_user_verbs.h>
#include <rdma/ib_verbs.h>
#include <linux/bug.h>
+#include <linux/file.h>
+#include "rdma_core.h"
#include "uverbs.h"
#define IB_UVERBS_VENDOR_FLAG 0x8000
@@ -77,3 +80,105 @@ int uverbs_action_std_ctx_handle(struct ib_device *ib_dev,
}
EXPORT_SYMBOL(uverbs_action_std_ctx_handle);
+void uverbs_free_ah(const struct uverbs_type_alloc_action *uobject_type,
+ struct ib_uobject *uobject)
+{
+ ib_destroy_ah((struct ib_ah *)uobject->object);
+}
+EXPORT_SYMBOL(uverbs_free_ah);
+
+void uverbs_free_flow(const struct uverbs_type_alloc_action *type_alloc_action,
+ struct ib_uobject *uobject)
+{
+ ib_destroy_flow((struct ib_flow *)uobject->object);
+}
+EXPORT_SYMBOL(uverbs_free_flow);
+
+void uverbs_free_mw(const struct uverbs_type_alloc_action *type_alloc_action,
+ struct ib_uobject *uobject)
+{
+ uverbs_dealloc_mw((struct ib_mw *)uobject->object);
+}
+EXPORT_SYMBOL(uverbs_free_mw);
+
+void uverbs_free_qp(const struct uverbs_type_alloc_action *type_alloc_action,
+ struct ib_uobject *uobject)
+{
+ struct ib_qp *qp = uobject->object;
+ struct ib_uqp_object *uqp =
+ container_of(uobject, struct ib_uqp_object, uevent.uobject);
+
+ if (qp != qp->real_qp) {
+ ib_close_qp(qp);
+ } else {
+ ib_uverbs_detach_umcast(qp, uqp);
+ ib_destroy_qp(qp);
+ }
+ ib_uverbs_release_uevent(uobject->context->ufile, &uqp->uevent);
+}
+EXPORT_SYMBOL(uverbs_free_qp);
+
+void uverbs_free_srq(const struct uverbs_type_alloc_action *type_alloc_action,
+ struct ib_uobject *uobject)
+{
+ struct ib_srq *srq = uobject->object;
+ struct ib_uevent_object *uevent =
+ container_of(uobject, struct ib_uevent_object, uobject);
+
+ ib_destroy_srq(srq);
+ ib_uverbs_release_uevent(uobject->context->ufile, uevent);
+}
+EXPORT_SYMBOL(uverbs_free_srq);
+
+void uverbs_free_cq(const struct uverbs_type_alloc_action *type_alloc_action,
+ struct ib_uobject *uobject)
+{
+ struct ib_cq *cq = uobject->object;
+ struct ib_uverbs_event_file *ev_file = cq->cq_context;
+ struct ib_ucq_object *ucq =
+ container_of(uobject, struct ib_ucq_object, uobject);
+
+ ib_destroy_cq(cq);
+ ib_uverbs_release_ucq(uobject->context->ufile, ev_file, ucq);
+}
+EXPORT_SYMBOL(uverbs_free_cq);
+
+void uverbs_free_mr(const struct uverbs_type_alloc_action *type_alloc_action,
+ struct ib_uobject *uobject)
+{
+ ib_dereg_mr((struct ib_mr *)uobject->object);
+}
+EXPORT_SYMBOL(uverbs_free_mr);
+
+void uverbs_free_xrcd(const struct uverbs_type_alloc_action *type_alloc_action,
+ struct ib_uobject *uobject)
+{
+ struct ib_xrcd *xrcd = uobject->object;
+
+ mutex_lock(&uobject->context->ufile->device->xrcd_tree_mutex);
+ ib_uverbs_dealloc_xrcd(uobject->context->ufile->device, xrcd);
+ mutex_unlock(&uobject->context->ufile->device->xrcd_tree_mutex);
+}
+EXPORT_SYMBOL(uverbs_free_xrcd);
+
+void uverbs_free_pd(const struct uverbs_type_alloc_action *type_alloc_action,
+ struct ib_uobject *uobject)
+{
+ ib_dealloc_pd((struct ib_pd *)uobject->object);
+}
+EXPORT_SYMBOL(uverbs_free_pd);
+
+void uverbs_free_event_file(const struct uverbs_type_alloc_action *type_alloc_action,
+ struct ib_uobject *uobject)
+{
+ struct ib_uverbs_event_file *event_file = (void *)(uobject + 1);
+
+ spin_lock_irq(&event_file->lock);
+ event_file->is_closed = 1;
+ spin_unlock_irq(&event_file->lock);
+
+ wake_up_interruptible(&event_file->poll_wait);
+ kill_fasync(&event_file->async_queue, SIGIO, POLL_IN);
+};
+EXPORT_SYMBOL(uverbs_free_event_file);
+
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index 80582c8..c4a094d 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -52,6 +52,7 @@
#include <rdma/rdma_ioctl.h>
#include "uverbs.h"
+#include "rdma_core.h"
MODULE_AUTHOR("Roland Dreier");
MODULE_DESCRIPTION("InfiniBand userspace verbs access");
@@ -195,8 +196,8 @@ void ib_uverbs_release_uevent(struct ib_uverbs_file *file,
spin_unlock_irq(&file->async_file->lock);
}
-static void ib_uverbs_detach_umcast(struct ib_qp *qp,
- struct ib_uqp_object *uobj)
+void ib_uverbs_detach_umcast(struct ib_qp *qp,
+ struct ib_uqp_object *uobj)
{
struct ib_uverbs_mcast_entry *mcast, *tmp;
@@ -210,103 +211,10 @@ static void ib_uverbs_detach_umcast(struct ib_qp *qp,
static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
struct ib_ucontext *context)
{
- struct ib_uobject *uobj, *tmp;
-
down_write(&file->close_sem);
context->closing = 1;
-
- list_for_each_entry_safe(uobj, tmp, &context->ah_list, list) {
- struct ib_ah *ah = uobj->object;
-
- idr_remove_uobj(uobj);
- ib_destroy_ah(ah);
- kfree(uobj);
- }
-
- /* Remove MWs before QPs, in order to support type 2A MWs. */
- list_for_each_entry_safe(uobj, tmp, &context->mw_list, list) {
- struct ib_mw *mw = uobj->object;
-
- idr_remove_uobj(uobj);
- uverbs_dealloc_mw(mw);
- kfree(uobj);
- }
-
- list_for_each_entry_safe(uobj, tmp, &context->rule_list, list) {
- struct ib_flow *flow_id = uobj->object;
-
- idr_remove_uobj(uobj);
- ib_destroy_flow(flow_id);
- kfree(uobj);
- }
-
- list_for_each_entry_safe(uobj, tmp, &context->qp_list, list) {
- struct ib_qp *qp = uobj->object;
- struct ib_uqp_object *uqp =
- container_of(uobj, struct ib_uqp_object, uevent.uobject);
-
- idr_remove_uobj(uobj);
- if (qp != qp->real_qp) {
- ib_close_qp(qp);
- } else {
- ib_uverbs_detach_umcast(qp, uqp);
- ib_destroy_qp(qp);
- }
- ib_uverbs_release_uevent(file, &uqp->uevent);
- kfree(uqp);
- }
-
- list_for_each_entry_safe(uobj, tmp, &context->srq_list, list) {
- struct ib_srq *srq = uobj->object;
- struct ib_uevent_object *uevent =
- container_of(uobj, struct ib_uevent_object, uobject);
-
- idr_remove_uobj(uobj);
- ib_destroy_srq(srq);
- ib_uverbs_release_uevent(file, uevent);
- kfree(uevent);
- }
-
- list_for_each_entry_safe(uobj, tmp, &context->cq_list, list) {
- struct ib_cq *cq = uobj->object;
- struct ib_uverbs_event_file *ev_file = cq->cq_context;
- struct ib_ucq_object *ucq =
- container_of(uobj, struct ib_ucq_object, uobject);
-
- idr_remove_uobj(uobj);
- ib_destroy_cq(cq);
- ib_uverbs_release_ucq(file, ev_file, ucq);
- kfree(ucq);
- }
-
- list_for_each_entry_safe(uobj, tmp, &context->mr_list, list) {
- struct ib_mr *mr = uobj->object;
-
- idr_remove_uobj(uobj);
- ib_dereg_mr(mr);
- kfree(uobj);
- }
-
- mutex_lock(&file->device->xrcd_tree_mutex);
- list_for_each_entry_safe(uobj, tmp, &context->xrcd_list, list) {
- struct ib_xrcd *xrcd = uobj->object;
- struct ib_uxrcd_object *uxrcd =
- container_of(uobj, struct ib_uxrcd_object, uobject);
-
- idr_remove_uobj(uobj);
- ib_uverbs_dealloc_xrcd(file->device, xrcd);
- kfree(uxrcd);
- }
- mutex_unlock(&file->device->xrcd_tree_mutex);
-
- list_for_each_entry_safe(uobj, tmp, &context->pd_list, list) {
- struct ib_pd *pd = uobj->object;
-
- idr_remove_uobj(uobj);
- ib_dealloc_pd(pd);
- kfree(uobj);
- }
-
+ ib_uverbs_uobject_type_cleanup_ucontext(context,
+ context->device->types_group);
put_pid(context->tgid);
up_write(&file->close_sem);
diff --git a/include/rdma/uverbs_ioctl_cmd.h b/include/rdma/uverbs_ioctl_cmd.h
index 19806df..eef9418 100644
--- a/include/rdma/uverbs_ioctl_cmd.h
+++ b/include/rdma/uverbs_ioctl_cmd.h
@@ -64,5 +64,39 @@ struct uverbs_action_std_ctx_handler {
void *priv;
};
+void uverbs_free_ah(const struct uverbs_type_alloc_action *type_alloc_action,
+ struct ib_uobject *uobject);
+void uverbs_free_flow(const struct uverbs_type_alloc_action *type_alloc_action,
+ struct ib_uobject *uobject);
+void uverbs_free_mw(const struct uverbs_type_alloc_action *type_alloc_action,
+ struct ib_uobject *uobject);
+void uverbs_free_qp(const struct uverbs_type_alloc_action *type_alloc_action,
+ struct ib_uobject *uobject);
+void uverbs_free_srq(const struct uverbs_type_alloc_action *type_alloc_action,
+ struct ib_uobject *uobject);
+void uverbs_free_cq(const struct uverbs_type_alloc_action *type_alloc_action,
+ struct ib_uobject *uobject);
+void uverbs_free_mr(const struct uverbs_type_alloc_action *type_alloc_action,
+ struct ib_uobject *uobject);
+void uverbs_free_xrcd(const struct uverbs_type_alloc_action *type_alloc_action,
+ struct ib_uobject *uobject);
+void uverbs_free_pd(const struct uverbs_type_alloc_action *type_alloc_action,
+ struct ib_uobject *uobject);
+
+enum uverbs_common_types {
+ UVERBS_TYPE_DEVICE, /* Don't use IDRs here */
+ UVERBS_TYPE_PD,
+ UVERBS_TYPE_COMP_CHANNEL,
+ UVERBS_TYPE_CQ,
+ UVERBS_TYPE_QP,
+ UVERBS_TYPE_SRQ,
+ UVERBS_TYPE_AH,
+ UVERBS_TYPE_MR,
+ UVERBS_TYPE_MW,
+ UVERBS_TYPE_FLOW,
+ UVERBS_TYPE_XRCD,
+ UVERBS_TYPE_LAST,
+};
+
#endif
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [RFC ABI V4 4/7] RDMA/core: Add new ioctl interface
From: Matan Barak @ 2016-09-28 15:10 UTC (permalink / raw)
To: linux-rdma-u79uwXL29TY76Z2rM5mHXA
Cc: Doug Ledford, Jason Gunthorpe, Sean Hefty, Liran Liss,
Haggai Eran, Matan Barak, Majd Dibbiny, Christoph Lameter,
Leon Romanovsky
In-Reply-To: <1475075417-30189-1-git-send-email-matanb-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
In this proposed ioctl interface, processing the command starts from
properties of the command and fetching the appropriate user objects
before calling the handler.
Parsing and validation is done according to a specifier declared by
the driver's code. In the driver, all supported types are declared.
These types are separated to different type groups, each could be
declared in a different place (for example, common types and driver
specific types).
For each type we list all supported actions. Similarly to types,
actions are separated to actions groups too. Each group is declared
separately. This could be used in order to add actions to an existing
type.
Each action has a specifies a handler, which could be either be a
standard command or a driver specific command.
Along with the handler, a group of attributes is specified as well.
This group lists all supported attributes and is used for automatically
fetching and validating the command, response and its related objects.
When a group of elements is used, a distribution function is also
specified at the higher level (i.e - type specifies a distribution
function for the actions it contains). The distribution function
chooses the group which should be used and maps the element from
the kABI language (which is driver specific) to common kernel
language. The standard distribution function just uses the upper
bit for that.
A group of attributes is actually an array of attributes. Each
attribute has a type (PTR_IN, PTR_OUT, IDR and in the future maybe
FD, which could be used for completion channel) and a length.
Future work here might add validation of mandatory attributes
(i.e - make sure a specific attribute was given).
If an IDR/fd attribute is specified, the kernel also states the object
type and the required access (NEW, WRITE, READ or DESTROY).
All uobject/fd management is done automatically by the infrastructure,
meaning - the infrastructure will fail concurrent commands that at
least one of them requires concurrent access (WRITE/DESTROY),
synchronize actions with device removals (dissociate context events)
and take care of reference counting (increase/decrease) for concurrent
actions invocation. The reference counts on the actual kernel objects
shall be handled by the handlers.
types
+--------+
| |
| | actions +--------+
| | group action action_spec +-----+ |len |
+--------+ +------+[d]+-------+ +----------------+[d]+------------+ |attr1+-> |type |
| type +> |action+-> | spec +-> + attr_groups +-> |common_chain+--> +-----+ |idr_type|
+--------+ +------+ |handler| | | +------------+ |attr2| |access |
| | | | +-------+ +----------------+ |vendor chain| +-----+ +--------+
| | | | +------------+
| | +------+
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
+--------+
[d] = distribution function used
The right types table is also chosen via using a distribution function
over uverbs_types_groups.
Once validation and object fetching (or creation) completed, we call
the handler:
int (*handler)(struct ib_device *ib_dev, struct ib_ucontext *ucontext,
struct uverbs_attr_array *ctx, size_t num, void *priv);
Where ctx is an array of uverbs_attr_array. Each element in this array
is an array of attributes which corresponds to one group of attributes.
For example, in the usually used case:
ctx core
+----------------------------+ +------------+
| core: uverbs_attr_array +---> | valid |
+----------------------------+ | cmd_attr |
| driver: uverbs_attr_array | +------------+
|----------------------------+--+ | valid |
| | cmd_attr |
| +------------+
| | valid |
| | obj_attr |
| +------------+
|
| vendor
| +------------+
+> | valid |
| cmd_attr |
+------------+
| valid |
| cmd_attr |
+------------+
| valid |
| obj_attr |
+------------+
Ctx array's indices corresponds to the attributes groups order. The indices
of core and driver corresponds to the attributes name spaces of each
group. Thus, we could think of the following as one object:
1. Set of attribute specification (with their attribute IDs)
2. Attribute group which owns (1) specifications
3. A function which could handle this attributes which the handler
could call
4. The allocation descriptor of this type uverbs_type_alloc_action.
That means that core and driver are the validated function (3)
parameters and their types exist in this function namespace.
Since the frequent used case is groups of common and vendor specific
elements, we propose a wrapper that allows a definition of the
following handler:
int (*handler)(struct ib_device *ib_dev, struct ib_ucontext *ucontext,
struct uverbs_attr_array *common,
struct uverbs_attr_array *vendor,
void *priv);
Upon success, reference count of uobjects and use count will be a
updated automatically according to the specification.
Signed-off-by: Matan Barak <matanb-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Signed-off-by: Haggai Eran <haggaie-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Signed-off-by: Leon Romanovsky <leonro-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Signed-off-by: Matan Barak <matanb-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
---
drivers/infiniband/core/Makefile | 2 +-
drivers/infiniband/core/device.c | 3 +
drivers/infiniband/core/rdma_core.c | 16 ++
drivers/infiniband/core/rdma_core.h | 2 +
drivers/infiniband/core/uverbs.h | 4 +
drivers/infiniband/core/uverbs_cmd.c | 1 +
drivers/infiniband/core/uverbs_ioctl.c | 306 +++++++++++++++++++++++++++++
drivers/infiniband/core/uverbs_ioctl_cmd.c | 79 ++++++++
drivers/infiniband/core/uverbs_main.c | 6 +
include/rdma/ib_verbs.h | 3 +-
include/rdma/uverbs_ioctl_cmd.h | 68 +++++++
include/uapi/rdma/rdma_user_ioctl.h | 23 +++
12 files changed, 511 insertions(+), 2 deletions(-)
create mode 100644 drivers/infiniband/core/uverbs_ioctl.c
create mode 100644 drivers/infiniband/core/uverbs_ioctl_cmd.c
create mode 100644 include/rdma/uverbs_ioctl_cmd.h
diff --git a/drivers/infiniband/core/Makefile b/drivers/infiniband/core/Makefile
index 1819623..769a299 100644
--- a/drivers/infiniband/core/Makefile
+++ b/drivers/infiniband/core/Makefile
@@ -29,4 +29,4 @@ ib_umad-y := user_mad.o
ib_ucm-y := ucm.o
ib_uverbs-y := uverbs_main.o uverbs_cmd.o uverbs_marshall.o \
- rdma_core.o
+ rdma_core.o uverbs_ioctl.o uverbs_ioctl_cmd.o
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
index 5f09c40..b2e2ee2 100644
--- a/drivers/infiniband/core/device.c
+++ b/drivers/infiniband/core/device.c
@@ -245,6 +245,9 @@ struct ib_device *ib_alloc_device(size_t size)
INIT_LIST_HEAD(&device->port_list);
INIT_LIST_HEAD(&device->type_list);
+ /* TODO: don't forget to initialize device->driver_id, so verbs handshake between
+ * user space<->kernel space will work for other values than driver_id == 0.
+ */
return device;
}
EXPORT_SYMBOL(ib_alloc_device);
diff --git a/drivers/infiniband/core/rdma_core.c b/drivers/infiniband/core/rdma_core.c
index 337abc2..bf8e741 100644
--- a/drivers/infiniband/core/rdma_core.c
+++ b/drivers/infiniband/core/rdma_core.c
@@ -55,6 +55,22 @@ const struct uverbs_type *uverbs_get_type(const struct ib_device *ibdev,
return types->types[type];
}
+const struct uverbs_action *uverbs_get_action(const struct uverbs_type *type,
+ uint16_t action)
+{
+ const struct uverbs_type_actions_group *actions_group;
+ int ret = type->dist(&action, type->priv);
+
+ if (ret >= type->num_groups)
+ return NULL;
+
+ actions_group = type->action_groups[ret];
+ if (action >= actions_group->num_actions)
+ return NULL;
+
+ return actions_group->actions[action];
+}
+
static int uverbs_lock_object(struct ib_uobject *uobj,
enum uverbs_idr_access access)
{
diff --git a/drivers/infiniband/core/rdma_core.h b/drivers/infiniband/core/rdma_core.h
index 8990115..f3661ef 100644
--- a/drivers/infiniband/core/rdma_core.h
+++ b/drivers/infiniband/core/rdma_core.h
@@ -44,6 +44,8 @@
const struct uverbs_type *uverbs_get_type(const struct ib_device *ibdev,
uint16_t type);
+const struct uverbs_action *uverbs_get_action(const struct uverbs_type *type,
+ uint16_t action);
struct ib_uobject *uverbs_get_type_from_idr(const struct uverbs_type_alloc_action *type,
struct ib_ucontext *ucontext,
enum uverbs_idr_access access,
diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h
index f6ccd7b..aa783fd 100644
--- a/drivers/infiniband/core/uverbs.h
+++ b/drivers/infiniband/core/uverbs.h
@@ -41,6 +41,7 @@
#include <linux/mutex.h>
#include <linux/completion.h>
#include <linux/cdev.h>
+#include <linux/rwsem.h>
#include <rdma/ib_verbs.h>
#include <rdma/ib_umem.h>
@@ -83,6 +84,8 @@
* released when the CQ is destroyed.
*/
+long ib_uverbs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+
struct ib_uverbs_device {
atomic_t refcount;
int num_comp_vectors;
@@ -121,6 +124,7 @@ struct ib_uverbs_file {
struct ib_uverbs_event_file *async_file;
struct list_head list;
int is_closed;
+ struct rw_semaphore close_sem;
};
struct ib_uverbs_event {
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index 6af2c29..9648b80 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -361,6 +361,7 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file,
}
file->ucontext = ucontext;
+ ucontext->ufile = file;
fd_install(resp.async_fd, filp);
diff --git a/drivers/infiniband/core/uverbs_ioctl.c b/drivers/infiniband/core/uverbs_ioctl.c
new file mode 100644
index 0000000..40efd4b
--- /dev/null
+++ b/drivers/infiniband/core/uverbs_ioctl.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2016, Mellanox Technologies inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <rdma/rdma_ioctl.h>
+#include <rdma/uverbs_ioctl.h>
+#include "rdma_core.h"
+#include "uverbs.h"
+
+static int uverbs_validate_attr(struct ib_device *ibdev,
+ struct ib_ucontext *ucontext,
+ const struct ib_uverbs_attr *uattr,
+ u16 attr_id,
+ const struct uverbs_attr_group_spec *group_spec,
+ struct uverbs_attr *elements,
+ struct ib_uverbs_attr __user *uattr_ptr)
+{
+ const struct uverbs_attr_spec *spec;
+ struct uverbs_attr *e;
+ const struct uverbs_type *type;
+ struct uverbs_obj_attr *o_attr;
+
+ if (uattr->reserved)
+ return -EINVAL;
+
+ if (attr_id >= group_spec->num_attrs)
+ return -EINVAL;
+
+ spec = &group_spec->attrs[attr_id];
+ e = &elements[attr_id];
+
+ if (e->valid)
+ return -EINVAL;
+
+ switch (spec->type) {
+ case UVERBS_ATTR_TYPE_PTR_IN:
+ case UVERBS_ATTR_TYPE_PTR_OUT:
+ /* If spec->len is zero, don't validate (flexible) */
+ if (spec->len && uattr->len != spec->len)
+ return -EINVAL;
+ e->cmd_attr.ptr = (void * __user)uattr->ptr_idr;
+ e->cmd_attr.len = uattr->len;
+ break;
+
+ case UVERBS_ATTR_TYPE_IDR:
+ case UVERBS_ATTR_TYPE_FD:
+ if (uattr->len != 0 || (uattr->ptr_idr >> 32) || (!ucontext))
+ return -EINVAL;
+
+ o_attr = &e->obj_attr;
+ o_attr->val = spec;
+ type = uverbs_get_type(ibdev, spec->obj.obj_type);
+ if (!type)
+ return -EINVAL;
+ o_attr->type = type->alloc;
+ o_attr->uattr = uattr_ptr;
+
+ if (spec->type == UVERBS_ATTR_TYPE_IDR) {
+ o_attr->uobj.idr = (uint32_t)uattr->ptr_idr;
+ o_attr->uobject = uverbs_get_type_from_idr(o_attr->type,
+ ucontext,
+ spec->obj.access,
+ o_attr->uobj.idr);
+ } else {
+ o_attr->fd.fd = (int)uattr->ptr_idr;
+ o_attr->uobject = uverbs_get_type_from_fd(o_attr->type,
+ ucontext,
+ spec->obj.access,
+ o_attr->fd.fd);
+ }
+
+ if (IS_ERR(o_attr->uobject))
+ return -EINVAL;
+
+ if (spec->obj.access == UVERBS_IDR_ACCESS_NEW) {
+ __u64 idr = o_attr->uobject->id;
+
+ if (put_user(idr, &o_attr->uattr->ptr_idr)) {
+ uverbs_unlock_object(o_attr->uobject,
+ UVERBS_IDR_ACCESS_NEW,
+ false);
+ return -EFAULT;
+ }
+ }
+
+ break;
+ };
+
+ e->valid = 1;
+ return 0;
+}
+
+static int uverbs_validate(struct ib_device *ibdev,
+ struct ib_ucontext *ucontext,
+ const struct ib_uverbs_attr *uattrs,
+ size_t num_attrs,
+ const struct uverbs_action_spec *action_spec,
+ struct uverbs_attr_array *attr_array,
+ struct ib_uverbs_attr __user *uattr_ptr)
+{
+ size_t i;
+ int ret;
+ int n_val = -1;
+
+ for (i = 0; i < num_attrs; i++) {
+ const struct ib_uverbs_attr *uattr = &uattrs[i];
+ __u16 attr_id = uattr->attr_id;
+ const struct uverbs_attr_group_spec *group_spec;
+
+ ret = action_spec->dist(&attr_id, action_spec->priv);
+ if (ret < 0)
+ return ret;
+
+ if (ret > n_val)
+ n_val = ret;
+
+ group_spec = action_spec->attr_groups[ret];
+ ret = uverbs_validate_attr(ibdev, ucontext, uattr, attr_id,
+ group_spec, attr_array[ret].attrs,
+ uattr_ptr++);
+ if (ret)
+ return ret;
+ }
+
+ return n_val >= 0 ? n_val + 1 : n_val;
+}
+
+static int uverbs_handle_action(struct ib_uverbs_attr __user *uattr_ptr,
+ const struct ib_uverbs_attr *uattrs,
+ size_t num_attrs,
+ struct ib_device *ibdev,
+ struct ib_uverbs_file *ufile,
+ const struct uverbs_action *handler,
+ struct uverbs_attr_array *attr_array)
+{
+ int ret;
+ int n_val;
+
+ n_val = uverbs_validate(ibdev, ufile->ucontext, uattrs, num_attrs,
+ &handler->spec, attr_array, uattr_ptr);
+ if (n_val <= 0)
+ return n_val;
+
+ ret = handler->handler(ibdev, ufile, attr_array, n_val,
+ handler->priv);
+ uverbs_unlock_objects(attr_array, n_val, &handler->spec, !ret);
+
+ return ret;
+}
+
+static long ib_uverbs_cmd_verbs(struct ib_device *ib_dev,
+ struct ib_uverbs_file *file,
+ struct ib_uverbs_ioctl_hdr *hdr,
+ void __user *buf)
+{
+ const struct uverbs_type *type;
+ const struct uverbs_action *action;
+ const struct uverbs_action_spec *action_spec;
+ long err = 0;
+ unsigned int num_specs = 0;
+ unsigned int i;
+ struct {
+ struct ib_uverbs_attr *uattrs;
+ struct uverbs_attr_array *uverbs_attr_array;
+ } *ctx = NULL;
+ struct uverbs_attr *curr_attr;
+ size_t ctx_size;
+
+ if (!ib_dev)
+ return -EIO;
+
+ if (ib_dev->driver_id != hdr->driver_id)
+ return -EINVAL;
+
+ type = uverbs_get_type(ib_dev, hdr->object_type);
+ if (!type)
+ return -EOPNOTSUPP;
+
+ action = uverbs_get_action(type, hdr->action);
+ if (!action)
+ return -EOPNOTSUPP;
+
+ action_spec = &action->spec;
+ for (i = 0; i < action_spec->num_groups;
+ num_specs += action_spec->attr_groups[i]->num_attrs, i++)
+ ;
+
+ ctx_size = sizeof(*ctx->uattrs) * hdr->num_attrs +
+ sizeof(*ctx->uverbs_attr_array->attrs) * num_specs +
+ sizeof(struct uverbs_attr_array) * action_spec->num_groups +
+ sizeof(*ctx);
+
+ ctx = kzalloc(ctx_size, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->uverbs_attr_array = (void *)ctx + sizeof(*ctx);
+ ctx->uattrs = (void *)(ctx->uverbs_attr_array +
+ action_spec->num_groups);
+ curr_attr = (void *)(ctx->uattrs + hdr->num_attrs);
+ for (i = 0; i < action_spec->num_groups; i++) {
+ ctx->uverbs_attr_array[i].attrs = curr_attr;
+ ctx->uverbs_attr_array[i].num_attrs =
+ action_spec->attr_groups[i]->num_attrs;
+ curr_attr += action_spec->attr_groups[i]->num_attrs;
+ }
+
+ err = copy_from_user(ctx->uattrs, buf,
+ sizeof(*ctx->uattrs) * hdr->num_attrs);
+ if (err) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ err = uverbs_handle_action(buf, ctx->uattrs, hdr->num_attrs, ib_dev,
+ file, action, ctx->uverbs_attr_array);
+out:
+ kfree(ctx);
+ return err;
+}
+
+#define IB_UVERBS_MAX_CMD_SZ 4096
+
+long ib_uverbs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct ib_uverbs_file *file = filp->private_data;
+ struct ib_uverbs_ioctl_hdr __user *user_hdr =
+ (struct ib_uverbs_ioctl_hdr __user *)arg;
+ struct ib_uverbs_ioctl_hdr hdr;
+ struct ib_device *ib_dev;
+ int srcu_key;
+ long err;
+
+ srcu_key = srcu_read_lock(&file->device->disassociate_srcu);
+ ib_dev = srcu_dereference(file->device->ib_dev,
+ &file->device->disassociate_srcu);
+ if (!ib_dev) {
+ err = -EIO;
+ goto out;
+ }
+
+ if (cmd == RDMA_DIRECT_IOCTL) {
+ /* TODO? */
+ err = -ENOSYS;
+ goto out;
+ } else {
+ if (cmd != RDMA_VERBS_IOCTL) {
+ err = -ENOIOCTLCMD;
+ goto out;
+ }
+
+ err = copy_from_user(&hdr, user_hdr, sizeof(hdr));
+
+ if (err || hdr.length > IB_UVERBS_MAX_CMD_SZ ||
+ hdr.length <= sizeof(hdr) ||
+ hdr.length != sizeof(hdr) + hdr.num_attrs * sizeof(struct ib_uverbs_attr)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* currently there are no flags supported */
+ if (hdr.flags) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /* We're closing, fail all commands */
+ if (!down_read_trylock(&file->close_sem))
+ return -EIO;
+ err = ib_uverbs_cmd_verbs(ib_dev, file, &hdr,
+ (__user void *)arg + sizeof(hdr));
+ up_read(&file->close_sem);
+ }
+out:
+ srcu_read_unlock(&file->device->disassociate_srcu, srcu_key);
+
+ return err;
+}
diff --git a/drivers/infiniband/core/uverbs_ioctl_cmd.c b/drivers/infiniband/core/uverbs_ioctl_cmd.c
new file mode 100644
index 0000000..d84866d
--- /dev/null
+++ b/drivers/infiniband/core/uverbs_ioctl_cmd.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2016, Mellanox Technologies inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <rdma/uverbs_ioctl_cmd.h>
+#include <rdma/ib_verbs.h>
+#include <linux/bug.h>
+#include "uverbs.h"
+
+#define IB_UVERBS_VENDOR_FLAG 0x8000
+
+int ib_uverbs_std_dist(__u16 *id, void *priv)
+{
+ if (*id & IB_UVERBS_VENDOR_FLAG) {
+ *id &= ~IB_UVERBS_VENDOR_FLAG;
+ return 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(ib_uverbs_std_dist);
+
+int uverbs_action_std_handle(struct ib_device *ib_dev,
+ struct ib_uverbs_file *ufile,
+ struct uverbs_attr_array *ctx, size_t num,
+ void *_priv)
+{
+ struct uverbs_action_std_handler *priv = _priv;
+
+ if (!ufile->ucontext)
+ return -EINVAL;
+
+ WARN_ON((num != 1) && (num != 2));
+ return priv->handler(ib_dev, ufile->ucontext, &ctx[0],
+ (num == 2 ? &ctx[1] : NULL),
+ priv->priv);
+}
+EXPORT_SYMBOL(uverbs_action_std_handle);
+
+int uverbs_action_std_ctx_handle(struct ib_device *ib_dev,
+ struct ib_uverbs_file *ufile,
+ struct uverbs_attr_array *ctx, size_t num,
+ void *_priv)
+{
+ struct uverbs_action_std_ctx_handler *priv = _priv;
+
+ WARN_ON((num != 1) && (num != 2));
+ return priv->handler(ib_dev, ufile, &ctx[0],
+ (num == 2 ? &ctx[1] : NULL), priv->priv);
+}
+EXPORT_SYMBOL(uverbs_action_std_ctx_handle);
+
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index b3aa733..80582c8 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -49,6 +49,7 @@
#include <asm/uaccess.h>
#include <rdma/ib.h>
+#include <rdma/rdma_ioctl.h>
#include "uverbs.h"
@@ -211,6 +212,7 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
{
struct ib_uobject *uobj, *tmp;
+ down_write(&file->close_sem);
context->closing = 1;
list_for_each_entry_safe(uobj, tmp, &context->ah_list, list) {
@@ -306,6 +308,7 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
}
put_pid(context->tgid);
+ up_write(&file->close_sem);
return context->device->dealloc_ucontext(context);
}
@@ -915,6 +918,7 @@ static int ib_uverbs_open(struct inode *inode, struct file *filp)
goto err;
}
+ init_rwsem(&file->close_sem);
file->device = dev;
file->ucontext = NULL;
file->async_file = NULL;
@@ -973,6 +977,7 @@ static const struct file_operations uverbs_fops = {
.open = ib_uverbs_open,
.release = ib_uverbs_close,
.llseek = no_llseek,
+ .unlocked_ioctl = ib_uverbs_ioctl,
};
static const struct file_operations uverbs_mmap_fops = {
@@ -982,6 +987,7 @@ static const struct file_operations uverbs_mmap_fops = {
.open = ib_uverbs_open,
.release = ib_uverbs_close,
.llseek = no_llseek,
+ .unlocked_ioctl = ib_uverbs_ioctl,
};
static struct ib_client uverbs_client = {
diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h
index 77e21b5..dd0bd42 100644
--- a/include/rdma/ib_verbs.h
+++ b/include/rdma/ib_verbs.h
@@ -1981,7 +1981,8 @@ struct ib_device {
int (*get_port_immutable)(struct ib_device *, u8, struct ib_port_immutable *);
struct list_head type_list;
- const struct uverbs_types_group *types_group;
+ u16 driver_id;
+ const struct uverbs_types_group *types_group;
};
struct ib_client {
diff --git a/include/rdma/uverbs_ioctl_cmd.h b/include/rdma/uverbs_ioctl_cmd.h
new file mode 100644
index 0000000..19806df
--- /dev/null
+++ b/include/rdma/uverbs_ioctl_cmd.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2016, Mellanox Technologies inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _UVERBS_IOCTL_CMD_
+#define _UVERBS_IOCTL_CMD_
+
+#include <rdma/uverbs_ioctl.h>
+
+int ib_uverbs_std_dist(__u16 *attr_id, void *priv);
+
+/* common validators */
+
+int uverbs_action_std_handle(struct ib_device *ib_dev,
+ struct ib_uverbs_file *ufile,
+ struct uverbs_attr_array *ctx, size_t num,
+ void *_priv);
+int uverbs_action_std_ctx_handle(struct ib_device *ib_dev,
+ struct ib_uverbs_file *ufile,
+ struct uverbs_attr_array *ctx, size_t num,
+ void *_priv);
+
+struct uverbs_action_std_handler {
+ int (*handler)(struct ib_device *ib_dev, struct ib_ucontext *ucontext,
+ struct uverbs_attr_array *common,
+ struct uverbs_attr_array *vendor,
+ void *priv);
+ void *priv;
+};
+
+struct uverbs_action_std_ctx_handler {
+ int (*handler)(struct ib_device *ib_dev, struct ib_uverbs_file *ufile,
+ struct uverbs_attr_array *common,
+ struct uverbs_attr_array *vendor,
+ void *priv);
+ void *priv;
+};
+
+#endif
+
diff --git a/include/uapi/rdma/rdma_user_ioctl.h b/include/uapi/rdma/rdma_user_ioctl.h
index 5c1117a..f6927fc 100644
--- a/include/uapi/rdma/rdma_user_ioctl.h
+++ b/include/uapi/rdma/rdma_user_ioctl.h
@@ -42,6 +42,29 @@
*/
#define IB_IOCTL_MAGIC RDMA_IOCTL_MAGIC
+#define RDMA_VERBS_IOCTL \
+ _IOWR(RDMA_IOCTL_MAGIC, 1, struct ib_uverbs_ioctl_hdr)
+
+#define RDMA_DIRECT_IOCTL \
+ _IOWR(RDMA_IOCTL_MAGIC, 2, struct ib_uverbs_ioctl_hdr)
+
+struct ib_uverbs_attr {
+ __u16 attr_id; /* command specific type attribute */
+ __u16 len; /* NA for idr */
+ __u32 reserved;
+ __u64 ptr_idr; /* ptr typeo command/idr handle */
+};
+
+struct ib_uverbs_ioctl_hdr {
+ __u16 length;
+ __u16 flags;
+ __u16 object_type;
+ __u16 driver_id;
+ __u16 action;
+ __u16 num_attrs;
+ struct ib_uverbs_attr attrs[0];
+};
+
/* Legacy part
* !!!! NOTE: It uses the same command index as VERBS
*/
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [RFC ABI V4 3/7] RDMA/core: Add support for custom types
From: Matan Barak @ 2016-09-28 15:10 UTC (permalink / raw)
To: linux-rdma-u79uwXL29TY76Z2rM5mHXA
Cc: Doug Ledford, Jason Gunthorpe, Sean Hefty, Liran Liss,
Haggai Eran, Matan Barak, Majd Dibbiny, Christoph Lameter,
Leon Romanovsky
In-Reply-To: <1475075417-30189-1-git-send-email-matanb-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
The new ioctl infrastructure supports driver specific objects.
Each such object type has a free function, allocation size and an
order of destruction. This information is embedded in the same
table describing the various action allowed on the object, similarly
to object oriented programming.
When a ucontext is created, a new list is created in this ib_ucontext.
This list contains all objects created under this ib_ucontext.
When a ib_ucontext is destroyed, we traverse this list several time
destroying the various objects by the order mentioned in the object
type description. If few object types have the same destruction order,
they are destroyed in an order opposite to their creation order.
Adding an object is done in two parts.
First, an object is allocated and added to IDR/fd table. Then, the
command's handlers (in downstream patches) could work on this object
and fill in its required details.
After a successful command, ib_uverbs_uobject_enable is called and
this user objects becomes ucontext visible.
Removing an uboject is done by calling ib_uverbs_uobject_remove.
We should make sure IDR (per-device) and list (per-ucontext) could
be accessed concurrently without corrupting them.
Signed-off-by: Matan Barak <matanb-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Signed-off-by: Haggai Eran <haggaie-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Signed-off-by: Leon Romanovsky <leonro-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
---
drivers/infiniband/core/Makefile | 3 +-
drivers/infiniband/core/device.c | 1 +
drivers/infiniband/core/rdma_core.c | 489 ++++++++++++++++++++++++++++++++++
drivers/infiniband/core/rdma_core.h | 75 ++++++
drivers/infiniband/core/uverbs.h | 1 +
drivers/infiniband/core/uverbs_main.c | 2 +-
include/rdma/ib_verbs.h | 28 +-
include/rdma/uverbs_ioctl.h | 195 ++++++++++++++
8 files changed, 789 insertions(+), 5 deletions(-)
create mode 100644 drivers/infiniband/core/rdma_core.c
create mode 100644 drivers/infiniband/core/rdma_core.h
create mode 100644 include/rdma/uverbs_ioctl.h
diff --git a/drivers/infiniband/core/Makefile b/drivers/infiniband/core/Makefile
index edaae9f..1819623 100644
--- a/drivers/infiniband/core/Makefile
+++ b/drivers/infiniband/core/Makefile
@@ -28,4 +28,5 @@ ib_umad-y := user_mad.o
ib_ucm-y := ucm.o
-ib_uverbs-y := uverbs_main.o uverbs_cmd.o uverbs_marshall.o
+ib_uverbs-y := uverbs_main.o uverbs_cmd.o uverbs_marshall.o \
+ rdma_core.o
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
index b0135c2..5f09c40 100644
--- a/drivers/infiniband/core/device.c
+++ b/drivers/infiniband/core/device.c
@@ -243,6 +243,7 @@ struct ib_device *ib_alloc_device(size_t size)
spin_lock_init(&device->client_data_lock);
INIT_LIST_HEAD(&device->client_data_list);
INIT_LIST_HEAD(&device->port_list);
+ INIT_LIST_HEAD(&device->type_list);
return device;
}
diff --git a/drivers/infiniband/core/rdma_core.c b/drivers/infiniband/core/rdma_core.c
new file mode 100644
index 0000000..337abc2
--- /dev/null
+++ b/drivers/infiniband/core/rdma_core.c
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 2016, Mellanox Technologies inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/file.h>
+#include <linux/anon_inodes.h>
+#include <rdma/ib_verbs.h>
+#include "uverbs.h"
+#include "rdma_core.h"
+#include <rdma/uverbs_ioctl.h>
+
+const struct uverbs_type *uverbs_get_type(const struct ib_device *ibdev,
+ uint16_t type)
+{
+ const struct uverbs_types_group *groups = ibdev->types_group;
+ const struct uverbs_types *types;
+ int ret = groups->dist(&type, groups->priv);
+
+ if (ret >= groups->num_groups)
+ return NULL;
+
+ types = groups->type_groups[ret];
+
+ if (type >= types->num_types)
+ return NULL;
+
+ return types->types[type];
+}
+
+static int uverbs_lock_object(struct ib_uobject *uobj,
+ enum uverbs_idr_access access)
+{
+ if (access == UVERBS_IDR_ACCESS_READ)
+ return down_read_trylock(&uobj->usecnt) == 1 ? 0 : -EBUSY;
+
+ /* lock is either WRITE or DESTROY - should be exclusive */
+ return down_write_trylock(&uobj->usecnt) == 1 ? 0 : -EBUSY;
+}
+
+static struct ib_uobject *get_uobj(int id, struct ib_ucontext *context)
+{
+ struct ib_uobject *uobj;
+
+ rcu_read_lock();
+ uobj = idr_find(&context->device->idr, id);
+ if (uobj && uobj->live) {
+ if (uobj->context != context)
+ uobj = NULL;
+ }
+ rcu_read_unlock();
+
+ return uobj;
+}
+
+struct ib_ucontext_lock {
+ struct kref ref;
+ /* locking the uobjects_list */
+ struct mutex lock;
+};
+
+static void init_uobjects_list_lock(struct ib_ucontext_lock *lock)
+{
+ mutex_init(&lock->lock);
+ kref_init(&lock->ref);
+}
+
+static void release_uobjects_list_lock(struct kref *ref)
+{
+ struct ib_ucontext_lock *lock = container_of(ref,
+ struct ib_ucontext_lock,
+ ref);
+
+ kfree(lock);
+}
+
+static void init_uobj(struct ib_uobject *uobj, u64 user_handle,
+ struct ib_ucontext *context)
+{
+ init_rwsem(&uobj->usecnt);
+ uobj->user_handle = user_handle;
+ uobj->context = context;
+ uobj->live = 0;
+}
+
+static int add_uobj(struct ib_uobject *uobj)
+{
+ int ret;
+
+ idr_preload(GFP_KERNEL);
+ spin_lock(&uobj->context->device->idr_lock);
+
+ ret = idr_alloc(&uobj->context->device->idr, uobj, 0, 0, GFP_NOWAIT);
+ if (ret >= 0)
+ uobj->id = ret;
+
+ spin_unlock(&uobj->context->device->idr_lock);
+ idr_preload_end();
+
+ return ret < 0 ? ret : 0;
+}
+
+static void remove_uobj(struct ib_uobject *uobj)
+{
+ spin_lock(&uobj->context->device->idr_lock);
+ idr_remove(&uobj->context->device->idr, uobj->id);
+ spin_unlock(&uobj->context->device->idr_lock);
+}
+
+static void put_uobj(struct ib_uobject *uobj)
+{
+ kfree_rcu(uobj, rcu);
+}
+
+static struct ib_uobject *get_uobject_from_context(struct ib_ucontext *ucontext,
+ const struct uverbs_type_alloc_action *type,
+ u32 idr,
+ enum uverbs_idr_access access)
+{
+ struct ib_uobject *uobj;
+ int ret;
+
+ rcu_read_lock();
+ uobj = get_uobj(idr, ucontext);
+ if (!uobj)
+ goto free;
+
+ if (uobj->type != type) {
+ uobj = NULL;
+ goto free;
+ }
+
+ ret = uverbs_lock_object(uobj, access);
+ if (ret)
+ uobj = ERR_PTR(ret);
+free:
+ rcu_read_unlock();
+ return uobj;
+
+ return NULL;
+}
+
+static int ib_uverbs_uobject_add(struct ib_uobject *uobject,
+ const struct uverbs_type_alloc_action *uobject_type)
+{
+ uobject->type = uobject_type;
+ return add_uobj(uobject);
+}
+
+struct ib_uobject *uverbs_get_type_from_idr(const struct uverbs_type_alloc_action *type,
+ struct ib_ucontext *ucontext,
+ enum uverbs_idr_access access,
+ uint32_t idr)
+{
+ struct ib_uobject *uobj;
+ int ret;
+
+ if (access == UVERBS_IDR_ACCESS_NEW) {
+ uobj = kmalloc(type->obj_size, GFP_KERNEL);
+ if (!uobj)
+ return ERR_PTR(-ENOMEM);
+
+ init_uobj(uobj, 0, ucontext);
+
+ /* lock idr */
+ ret = ib_uverbs_uobject_add(uobj, type);
+ if (ret) {
+ kfree(uobj);
+ return ERR_PTR(ret);
+ }
+
+ } else {
+ uobj = get_uobject_from_context(ucontext, type, idr,
+ access);
+
+ if (!uobj)
+ return ERR_PTR(-ENOENT);
+ }
+
+ return uobj;
+}
+
+struct ib_uobject *uverbs_get_type_from_fd(const struct uverbs_type_alloc_action *type,
+ struct ib_ucontext *ucontext,
+ enum uverbs_idr_access access,
+ int fd)
+{
+ if (access == UVERBS_IDR_ACCESS_NEW) {
+ int _fd;
+ struct ib_uobject *uobj = NULL;
+ struct file *filp;
+
+ _fd = get_unused_fd_flags(O_CLOEXEC);
+ if (_fd < 0 || WARN_ON(type->obj_size < sizeof(struct ib_uobject)))
+ return ERR_PTR(_fd);
+
+ uobj = kmalloc(type->obj_size, GFP_KERNEL);
+ init_uobj(uobj, 0, ucontext);
+
+ if (!uobj)
+ return ERR_PTR(-ENOMEM);
+
+ filp = anon_inode_getfile(type->fd.name, type->fd.fops,
+ uobj + 1, type->fd.flags);
+ if (IS_ERR(filp)) {
+ put_unused_fd(_fd);
+ kfree(uobj);
+ return (void *)filp;
+ }
+
+ uobj->type = type;
+ uobj->id = _fd;
+ uobj->object = filp;
+
+ return uobj;
+ } else if (access == UVERBS_IDR_ACCESS_READ) {
+ struct file *f = fget(fd);
+ struct ib_uobject *uobject;
+
+ if (!f)
+ return ERR_PTR(-EBADF);
+
+ uobject = f->private_data - sizeof(struct ib_uobject);
+ if (f->f_op != type->fd.fops ||
+ !uobject->live) {
+ fput(f);
+ return ERR_PTR(-EBADF);
+ }
+
+ /*
+ * No need to protect it with a ref count, as fget increases
+ * f_count.
+ */
+ return uobject;
+ } else {
+ return ERR_PTR(-EOPNOTSUPP);
+ }
+}
+
+static void ib_uverbs_uobject_enable(struct ib_uobject *uobject)
+{
+ mutex_lock(&uobject->context->uobjects_lock->lock);
+ list_add(&uobject->list, &uobject->context->uobjects);
+ mutex_unlock(&uobject->context->uobjects_lock->lock);
+ uobject->live = 1;
+}
+
+static void ib_uverbs_uobject_remove(struct ib_uobject *uobject, bool lock)
+{
+ /*
+ * Calling remove requires exclusive access, so it's not possible
+ * another thread will use our object.
+ */
+ uobject->live = 0;
+ uobject->type->free_fn(uobject->type, uobject);
+ if (lock)
+ mutex_lock(&uobject->context->uobjects_lock->lock);
+ list_del(&uobject->list);
+ if (lock)
+ mutex_unlock(&uobject->context->uobjects_lock->lock);
+ remove_uobj(uobject);
+ put_uobj(uobject);
+}
+
+static void uverbs_unlock_idr(struct ib_uobject *uobj,
+ enum uverbs_idr_access access,
+ bool success)
+{
+ switch (access) {
+ case UVERBS_IDR_ACCESS_READ:
+ up_read(&uobj->usecnt);
+ break;
+ case UVERBS_IDR_ACCESS_NEW:
+ if (success) {
+ ib_uverbs_uobject_enable(uobj);
+ } else {
+ remove_uobj(uobj);
+ put_uobj(uobj);
+ }
+ break;
+ case UVERBS_IDR_ACCESS_WRITE:
+ up_write(&uobj->usecnt);
+ break;
+ case UVERBS_IDR_ACCESS_DESTROY:
+ if (success)
+ ib_uverbs_uobject_remove(uobj, true);
+ else
+ up_write(&uobj->usecnt);
+ break;
+ }
+}
+
+static void uverbs_unlock_fd(struct ib_uobject *uobj,
+ enum uverbs_idr_access access,
+ bool success)
+{
+ struct file *filp = uobj->object;
+
+ if (access == UVERBS_IDR_ACCESS_NEW) {
+ if (success) {
+ kref_get(&uobj->context->ufile->ref);
+ uobj->uobjects_lock = uobj->context->uobjects_lock;
+ kref_get(&uobj->uobjects_lock->ref);
+ ib_uverbs_uobject_enable(uobj);
+ fd_install(uobj->id, uobj->object);
+ } else {
+ fput(uobj->object);
+ put_unused_fd(uobj->id);
+ kfree(uobj);
+ }
+ } else {
+ fput(filp);
+ }
+}
+
+void uverbs_unlock_object(struct ib_uobject *uobj,
+ enum uverbs_idr_access access,
+ bool success)
+{
+ if (uobj->type->type == UVERBS_ATTR_TYPE_IDR)
+ uverbs_unlock_idr(uobj, access, success);
+ else if (uobj->type->type == UVERBS_ATTR_TYPE_FD)
+ uverbs_unlock_fd(uobj, access, success);
+ else
+ WARN_ON(true);
+}
+
+static void ib_uverbs_remove_fd(struct ib_uobject *uobject)
+{
+ /*
+ * user should release the uobject in the release
+ * callback.
+ */
+ if (uobject->live) {
+ uobject->live = 0;
+ list_del(&uobject->list);
+ uobject->type->free_fn(uobject->type, uobject);
+ kref_put(&uobject->context->ufile->ref, ib_uverbs_release_file);
+ uobject->context = NULL;
+ }
+}
+
+void ib_uverbs_close_fd(struct file *f)
+{
+ struct ib_uobject *uobject = f->private_data - sizeof(struct ib_uobject);
+
+ mutex_lock(&uobject->uobjects_lock->lock);
+ if (uobject->live) {
+ uobject->live = 0;
+ list_del(&uobject->list);
+ kref_put(&uobject->context->ufile->ref, ib_uverbs_release_file);
+ uobject->context = NULL;
+ }
+ mutex_unlock(&uobject->uobjects_lock->lock);
+ kref_put(&uobject->uobjects_lock->ref, release_uobjects_list_lock);
+}
+
+void ib_uverbs_cleanup_fd(void *private_data)
+{
+ struct ib_uboject *uobject = private_data - sizeof(struct ib_uobject);
+
+ kfree(uobject);
+}
+
+void uverbs_unlock_objects(struct uverbs_attr_array *attr_array,
+ size_t num,
+ const struct uverbs_action_spec *spec,
+ bool success)
+{
+ unsigned int i;
+
+ for (i = 0; i < num; i++) {
+ struct uverbs_attr_array *attr_spec_array = &attr_array[i];
+ const struct uverbs_attr_group_spec *group_spec =
+ spec->attr_groups[i];
+ unsigned int j;
+
+ for (j = 0; j < attr_spec_array->num_attrs; j++) {
+ struct uverbs_attr *attr = &attr_spec_array->attrs[j];
+ struct uverbs_attr_spec *spec = &group_spec->attrs[j];
+
+ if (!attr->valid)
+ continue;
+
+ if (spec->type == UVERBS_ATTR_TYPE_IDR ||
+ spec->type == UVERBS_ATTR_TYPE_FD)
+ /*
+ * refcounts should be handled at the object
+ * level and not at the uobject level.
+ */
+ uverbs_unlock_object(attr->obj_attr.uobject,
+ spec->obj.access, success);
+ }
+ }
+}
+
+static unsigned int get_type_orders(const struct uverbs_types_group *types_group)
+{
+ unsigned int i;
+ unsigned int max = 0;
+
+ for (i = 0; i < types_group->num_groups; i++) {
+ unsigned int j;
+ const struct uverbs_types *types = types_group->type_groups[i];
+
+ for (j = 0; j < types->num_types; j++) {
+ if (!types->types[j] || !types->types[j]->alloc)
+ continue;
+ if (types->types[j]->alloc->order > max)
+ max = types->types[j]->alloc->order;
+ }
+ }
+
+ return max;
+}
+
+void ib_uverbs_uobject_type_cleanup_ucontext(struct ib_ucontext *ucontext,
+ const struct uverbs_types_group *types_group)
+{
+ unsigned int num_orders = get_type_orders(types_group);
+ unsigned int i;
+
+ for (i = 0; i <= num_orders; i++) {
+ struct ib_uobject *obj, *next_obj;
+
+ /*
+ * No need to take lock here, as cleanup should be called
+ * after all commands finished executing. Newly executed
+ * commands should fail.
+ */
+ mutex_lock(&ucontext->uobjects_lock->lock);
+ list_for_each_entry_safe(obj, next_obj, &ucontext->uobjects,
+ list)
+ if (obj->type->order == i) {
+ if (obj->type->type == UVERBS_ATTR_TYPE_IDR)
+ ib_uverbs_uobject_remove(obj, false);
+ else
+ ib_uverbs_remove_fd(obj);
+ }
+ mutex_unlock(&ucontext->uobjects_lock->lock);
+ }
+ kref_put(&ucontext->uobjects_lock->ref, release_uobjects_list_lock);
+}
+
+int ib_uverbs_uobject_type_initialize_ucontext(struct ib_ucontext *ucontext)
+{
+ ucontext->uobjects_lock = kmalloc(sizeof(*ucontext->uobjects_lock),
+ GFP_KERNEL);
+ if (!ucontext->uobjects_lock)
+ return -ENOMEM;
+
+ init_uobjects_list_lock(ucontext->uobjects_lock);
+ INIT_LIST_HEAD(&ucontext->uobjects);
+
+ return 0;
+}
+
+void ib_uverbs_uobject_type_release_ucontext(struct ib_ucontext *ucontext)
+{
+ kfree(ucontext->uobjects_lock);
+}
+
diff --git a/drivers/infiniband/core/rdma_core.h b/drivers/infiniband/core/rdma_core.h
new file mode 100644
index 0000000..8990115
--- /dev/null
+++ b/drivers/infiniband/core/rdma_core.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2005 Topspin Communications. All rights reserved.
+ * Copyright (c) 2005, 2006 Cisco Systems. All rights reserved.
+ * Copyright (c) 2005-2016 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2005 Voltaire, Inc. All rights reserved.
+ * Copyright (c) 2005 PathScale, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef UOBJECT_H
+#define UOBJECT_H
+
+#include <linux/idr.h>
+#include <rdma/uverbs_ioctl.h>
+#include <rdma/ib_verbs.h>
+#include <linux/mutex.h>
+
+const struct uverbs_type *uverbs_get_type(const struct ib_device *ibdev,
+ uint16_t type);
+struct ib_uobject *uverbs_get_type_from_idr(const struct uverbs_type_alloc_action *type,
+ struct ib_ucontext *ucontext,
+ enum uverbs_idr_access access,
+ uint32_t idr);
+struct ib_uobject *uverbs_get_type_from_fd(const struct uverbs_type_alloc_action *type,
+ struct ib_ucontext *ucontext,
+ enum uverbs_idr_access access,
+ int fd);
+void uverbs_unlock_object(struct ib_uobject *uobj,
+ enum uverbs_idr_access access,
+ bool success);
+void uverbs_unlock_objects(struct uverbs_attr_array *attr_array,
+ size_t num,
+ const struct uverbs_action_spec *spec,
+ bool success);
+
+void ib_uverbs_uobject_type_cleanup_ucontext(struct ib_ucontext *ucontext,
+ const struct uverbs_types_group *types_group);
+int ib_uverbs_uobject_type_initialize_ucontext(struct ib_ucontext *ucontext);
+void ib_uverbs_uobject_type_release_ucontext(struct ib_ucontext *ucontext);
+void ib_uverbs_close_fd(struct file *f);
+void ib_uverbs_cleanup_fd(void *private_data);
+
+static inline void *uverbs_fd_to_priv(struct ib_uobject *uobj)
+{
+ return uobj + 1;
+}
+
+#endif /* UIDR_H */
diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h
index 60d7c3d..f6ccd7b 100644
--- a/drivers/infiniband/core/uverbs.h
+++ b/drivers/infiniband/core/uverbs.h
@@ -175,6 +175,7 @@ void idr_remove_uobj(struct ib_uobject *uobj);
struct file *ib_uverbs_alloc_event_file(struct ib_uverbs_file *uverbs_file,
struct ib_device *ib_dev,
int is_async);
+void ib_uverbs_release_file(struct kref *ref);
void ib_uverbs_free_async_event_file(struct ib_uverbs_file *uverbs_file);
struct ib_uverbs_event_file *ib_uverbs_lookup_comp_file(int fd);
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index 333ed70..b3aa733 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -315,7 +315,7 @@ static void ib_uverbs_comp_dev(struct ib_uverbs_device *dev)
complete(&dev->comp);
}
-static void ib_uverbs_release_file(struct kref *ref)
+void ib_uverbs_release_file(struct kref *ref)
{
struct ib_uverbs_file *file =
container_of(ref, struct ib_uverbs_file, ref);
diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h
index 14bfe3b..77e21b5 100644
--- a/include/rdma/ib_verbs.h
+++ b/include/rdma/ib_verbs.h
@@ -1312,8 +1312,11 @@ struct ib_fmr_attr {
struct ib_umem;
+struct ib_ucontext_lock;
+
struct ib_ucontext {
struct ib_device *device;
+ struct ib_uverbs_file *ufile;
struct list_head pd_list;
struct list_head mr_list;
struct list_head mw_list;
@@ -1325,6 +1328,10 @@ struct ib_ucontext {
struct list_head rule_list;
int closing;
+ /* lock for uobjects list */
+ struct ib_ucontext_lock *uobjects_lock;
+ struct list_head uobjects;
+
struct pid *tgid;
#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
struct rb_root umem_tree;
@@ -1344,16 +1351,28 @@ struct ib_ucontext {
#endif
};
+struct uverbs_object_list;
+
+#define OLD_ABI_COMPAT
+
struct ib_uobject {
u64 user_handle; /* handle given to us by userspace */
struct ib_ucontext *context; /* associated user context */
void *object; /* containing object */
struct list_head list; /* link to context's list */
- int id; /* index into kernel idr */
- struct kref ref;
- struct rw_semaphore mutex; /* protects .live */
+ int id; /* index into kernel idr/fd */
+#ifdef OLD_ABI_COMPAT
+ struct kref ref;
+#endif
+ struct rw_semaphore usecnt; /* protects exclusive access */
+#ifdef OLD_ABI_COMPAT
+ struct rw_semaphore mutex; /* protects .live */
+#endif
struct rcu_head rcu; /* kfree_rcu() overhead */
int live;
+
+ const struct uverbs_type_alloc_action *type;
+ struct ib_ucontext_lock *uobjects_lock;
};
struct ib_udata {
@@ -1960,6 +1979,9 @@ struct ib_device {
* in fast paths.
*/
int (*get_port_immutable)(struct ib_device *, u8, struct ib_port_immutable *);
+ struct list_head type_list;
+
+ const struct uverbs_types_group *types_group;
};
struct ib_client {
diff --git a/include/rdma/uverbs_ioctl.h b/include/rdma/uverbs_ioctl.h
new file mode 100644
index 0000000..2f50045
--- /dev/null
+++ b/include/rdma/uverbs_ioctl.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2016, Mellanox Technologies inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _UVERBS_IOCTL_
+#define _UVERBS_IOCTL_
+
+#include <linux/kernel.h>
+
+struct uverbs_object_type;
+struct ib_ucontext;
+struct ib_uobject;
+struct ib_device;
+struct uverbs_uobject_type;
+
+/*
+ * =======================================
+ * Verbs action specifications
+ * =======================================
+ */
+
+enum uverbs_attr_type {
+ UVERBS_ATTR_TYPE_PTR_IN,
+ UVERBS_ATTR_TYPE_PTR_OUT,
+ UVERBS_ATTR_TYPE_IDR,
+ UVERBS_ATTR_TYPE_FD,
+};
+
+enum uverbs_idr_access {
+ UVERBS_IDR_ACCESS_READ,
+ UVERBS_IDR_ACCESS_WRITE,
+ UVERBS_IDR_ACCESS_NEW,
+ UVERBS_IDR_ACCESS_DESTROY
+};
+
+struct uverbs_attr_spec {
+ u16 len;
+ enum uverbs_attr_type type;
+ struct {
+ u16 obj_type;
+ u8 access;
+ } obj;
+};
+
+struct uverbs_attr_group_spec {
+ struct uverbs_attr_spec *attrs;
+ size_t num_attrs;
+};
+
+struct uverbs_action_spec {
+ const struct uverbs_attr_group_spec **attr_groups;
+ /* if > 0 -> validator, otherwise, error */
+ int (*dist)(__u16 *attr_id, void *priv);
+ void *priv;
+ size_t num_groups;
+};
+
+struct uverbs_attr_array;
+struct ib_uverbs_file;
+
+struct uverbs_action {
+ struct uverbs_action_spec spec;
+ void *priv;
+ int (*handler)(struct ib_device *ib_dev, struct ib_uverbs_file *ufile,
+ struct uverbs_attr_array *ctx, size_t num, void *priv);
+};
+
+struct uverbs_type_alloc_action;
+typedef void (*free_type)(const struct uverbs_type_alloc_action *uobject_type,
+ struct ib_uobject *uobject);
+
+struct uverbs_type_alloc_action {
+ enum uverbs_attr_type type;
+ int order;
+ size_t obj_size;
+ free_type free_fn;
+ struct {
+ const struct file_operations *fops;
+ const char *name;
+ int flags;
+ } fd;
+};
+
+struct uverbs_type_actions_group {
+ size_t num_actions;
+ const struct uverbs_action **actions;
+};
+
+struct uverbs_type {
+ size_t num_groups;
+ const struct uverbs_type_actions_group **action_groups;
+ const struct uverbs_type_alloc_action *alloc;
+ int (*dist)(__u16 *action_id, void *priv);
+ void *priv;
+};
+
+struct uverbs_types {
+ size_t num_types;
+ const struct uverbs_type **types;
+};
+
+struct uverbs_types_group {
+ const struct uverbs_types **type_groups;
+ size_t num_groups;
+ int (*dist)(__u16 *type_id, void *priv);
+ void *priv;
+};
+
+/* =================================================
+ * Parsing infrastructure
+ * =================================================
+ */
+
+struct uverbs_ptr_attr {
+ void * __user ptr;
+ __u16 len;
+};
+
+struct uverbs_fd_attr {
+ int fd;
+};
+
+struct uverbs_uobj_attr {
+ /* idr handle */
+ __u32 idr;
+};
+
+struct uverbs_obj_attr {
+ /* pointer to the kernel descriptor -> type, access, etc */
+ const struct uverbs_attr_spec *val;
+ struct ib_uverbs_attr __user *uattr;
+ const struct uverbs_type_alloc_action *type;
+ struct ib_uobject *uobject;
+ union {
+ struct uverbs_fd_attr fd;
+ struct uverbs_uobj_attr uobj;
+ };
+};
+
+struct uverbs_attr {
+ bool valid;
+ union {
+ struct uverbs_ptr_attr cmd_attr;
+ struct uverbs_obj_attr obj_attr;
+ };
+};
+
+/* output of one validator */
+struct uverbs_attr_array {
+ size_t num_attrs;
+ /* arrays of attrubytes, index is the id i.e SEND_CQ */
+ struct uverbs_attr *attrs;
+};
+
+/* =================================================
+ * Types infrastructure
+ * =================================================
+ */
+
+int ib_uverbs_uobject_type_add(struct list_head *head,
+ void (*free)(struct uverbs_uobject_type *type,
+ struct ib_uobject *uobject,
+ struct ib_ucontext *ucontext),
+ uint16_t obj_type);
+void ib_uverbs_uobject_types_remove(struct ib_device *ib_dev);
+
+#endif
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [RFC ABI V4 2/7] RDMA/core: Refactor IDR to be per-device
From: Matan Barak @ 2016-09-28 15:10 UTC (permalink / raw)
To: linux-rdma-u79uwXL29TY76Z2rM5mHXA
Cc: Doug Ledford, Jason Gunthorpe, Sean Hefty, Liran Liss,
Haggai Eran, Matan Barak, Majd Dibbiny, Christoph Lameter,
Leon Romanovsky
In-Reply-To: <1475075417-30189-1-git-send-email-matanb-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
From: Leon Romanovsky <leonro-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
The current code creates an IDR per type. Since types are currently
common for all vendors and known in advance, this was good enough.
However, the proposed ioctl based infrastructure allows each vendor
to declare only some of the common types and declare its own specific
types.
Thus, we decided to implement IDR to be per device and refactor it to
use a new file.
Signed-off-by: Matan Barak <matanb-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Signed-off-by: Haggai Eran <haggaie-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Signed-off-by: Leon Romanovsky <leonro-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
---
drivers/infiniband/core/device.c | 14 ++++
drivers/infiniband/core/uverbs.h | 14 +---
drivers/infiniband/core/uverbs_cmd.c | 149 ++++++++++++++++------------------
drivers/infiniband/core/uverbs_main.c | 36 ++------
include/rdma/ib_verbs.h | 4 +
5 files changed, 100 insertions(+), 117 deletions(-)
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
index 5516fb0..b0135c2 100644
--- a/drivers/infiniband/core/device.c
+++ b/drivers/infiniband/core/device.c
@@ -168,11 +168,23 @@ static int alloc_name(char *name)
return 0;
}
+static void ib_device_allocate_idrs(struct ib_device *device)
+{
+ spin_lock_init(&device->idr_lock);
+ idr_init(&device->idr);
+}
+
+static void ib_device_destroy_idrs(struct ib_device *device)
+{
+ idr_destroy(&device->idr);
+}
+
static void ib_device_release(struct device *device)
{
struct ib_device *dev = container_of(device, struct ib_device, dev);
ib_cache_release_one(dev);
+ ib_device_destroy_idrs(dev);
kfree(dev->port_immutable);
kfree(dev);
}
@@ -219,6 +231,8 @@ struct ib_device *ib_alloc_device(size_t size)
if (!device)
return NULL;
+ ib_device_allocate_idrs(device);
+
device->dev.class = &ib_class;
device_initialize(&device->dev);
diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h
index 612ccfd..60d7c3d 100644
--- a/drivers/infiniband/core/uverbs.h
+++ b/drivers/infiniband/core/uverbs.h
@@ -38,7 +38,6 @@
#define UVERBS_H
#include <linux/kref.h>
-#include <linux/idr.h>
#include <linux/mutex.h>
#include <linux/completion.h>
#include <linux/cdev.h>
@@ -171,18 +170,7 @@ struct ib_ucq_object {
u32 async_events_reported;
};
-extern spinlock_t ib_uverbs_idr_lock;
-extern struct idr ib_uverbs_pd_idr;
-extern struct idr ib_uverbs_mr_idr;
-extern struct idr ib_uverbs_mw_idr;
-extern struct idr ib_uverbs_ah_idr;
-extern struct idr ib_uverbs_cq_idr;
-extern struct idr ib_uverbs_qp_idr;
-extern struct idr ib_uverbs_srq_idr;
-extern struct idr ib_uverbs_xrcd_idr;
-extern struct idr ib_uverbs_rule_idr;
-
-void idr_remove_uobj(struct idr *idp, struct ib_uobject *uobj);
+void idr_remove_uobj(struct ib_uobject *uobj);
struct file *ib_uverbs_alloc_event_file(struct ib_uverbs_file *uverbs_file,
struct ib_device *ib_dev,
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index 1a8babb..6af2c29 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -118,37 +118,36 @@ static void put_uobj_write(struct ib_uobject *uobj)
put_uobj(uobj);
}
-static int idr_add_uobj(struct idr *idr, struct ib_uobject *uobj)
+int idr_add_uobj(struct ib_uobject *uobj)
{
int ret;
idr_preload(GFP_KERNEL);
- spin_lock(&ib_uverbs_idr_lock);
+ spin_lock(&uobj->context->device->idr_lock);
- ret = idr_alloc(idr, uobj, 0, 0, GFP_NOWAIT);
+ ret = idr_alloc(&uobj->context->device->idr, uobj, 0, 0, GFP_NOWAIT);
if (ret >= 0)
uobj->id = ret;
- spin_unlock(&ib_uverbs_idr_lock);
+ spin_unlock(&uobj->context->device->idr_lock);
idr_preload_end();
return ret < 0 ? ret : 0;
}
-void idr_remove_uobj(struct idr *idr, struct ib_uobject *uobj)
+void idr_remove_uobj(struct ib_uobject *uobj)
{
- spin_lock(&ib_uverbs_idr_lock);
- idr_remove(idr, uobj->id);
- spin_unlock(&ib_uverbs_idr_lock);
+ spin_lock(&uobj->context->device->idr_lock);
+ idr_remove(&uobj->context->device->idr, uobj->id);
+ spin_unlock(&uobj->context->device->idr_lock);
}
-static struct ib_uobject *__idr_get_uobj(struct idr *idr, int id,
- struct ib_ucontext *context)
+static struct ib_uobject *__idr_get_uobj(int id, struct ib_ucontext *context)
{
struct ib_uobject *uobj;
rcu_read_lock();
- uobj = idr_find(idr, id);
+ uobj = idr_find(&context->device->idr, id);
if (uobj) {
if (uobj->context == context)
kref_get(&uobj->ref);
@@ -160,12 +159,12 @@ static struct ib_uobject *__idr_get_uobj(struct idr *idr, int id,
return uobj;
}
-static struct ib_uobject *idr_read_uobj(struct idr *idr, int id,
- struct ib_ucontext *context, int nested)
+static struct ib_uobject *idr_read_uobj(int id, struct ib_ucontext *context,
+ int nested)
{
struct ib_uobject *uobj;
- uobj = __idr_get_uobj(idr, id, context);
+ uobj = __idr_get_uobj(id, context);
if (!uobj)
return NULL;
@@ -181,12 +180,11 @@ static struct ib_uobject *idr_read_uobj(struct idr *idr, int id,
return uobj;
}
-static struct ib_uobject *idr_write_uobj(struct idr *idr, int id,
- struct ib_ucontext *context)
+struct ib_uobject *idr_write_uobj(int id, struct ib_ucontext *context)
{
struct ib_uobject *uobj;
- uobj = __idr_get_uobj(idr, id, context);
+ uobj = __idr_get_uobj(id, context);
if (!uobj)
return NULL;
@@ -199,18 +197,18 @@ static struct ib_uobject *idr_write_uobj(struct idr *idr, int id,
return uobj;
}
-static void *idr_read_obj(struct idr *idr, int id, struct ib_ucontext *context,
+static void *idr_read_obj(int id, struct ib_ucontext *context,
int nested)
{
struct ib_uobject *uobj;
- uobj = idr_read_uobj(idr, id, context, nested);
+ uobj = idr_read_uobj(id, context, nested);
return uobj ? uobj->object : NULL;
}
-static struct ib_pd *idr_read_pd(int pd_handle, struct ib_ucontext *context)
+struct ib_pd *idr_read_pd(int pd_handle, struct ib_ucontext *context)
{
- return idr_read_obj(&ib_uverbs_pd_idr, pd_handle, context, 0);
+ return idr_read_obj(pd_handle, context, 0);
}
static void put_pd_read(struct ib_pd *pd)
@@ -220,7 +218,7 @@ static void put_pd_read(struct ib_pd *pd)
static struct ib_cq *idr_read_cq(int cq_handle, struct ib_ucontext *context, int nested)
{
- return idr_read_obj(&ib_uverbs_cq_idr, cq_handle, context, nested);
+ return idr_read_obj(cq_handle, context, nested);
}
static void put_cq_read(struct ib_cq *cq)
@@ -228,26 +226,26 @@ static void put_cq_read(struct ib_cq *cq)
put_uobj_read(cq->uobject);
}
-static struct ib_ah *idr_read_ah(int ah_handle, struct ib_ucontext *context)
+static void put_ah_read(struct ib_ah *ah)
{
- return idr_read_obj(&ib_uverbs_ah_idr, ah_handle, context, 0);
+ put_uobj_read(ah->uobject);
}
-static void put_ah_read(struct ib_ah *ah)
+struct ib_ah *idr_read_ah(int ah_handle, struct ib_ucontext *context)
{
- put_uobj_read(ah->uobject);
+ return idr_read_obj(ah_handle, context, 0);
}
-static struct ib_qp *idr_read_qp(int qp_handle, struct ib_ucontext *context)
+struct ib_qp *idr_read_qp(int qp_handle, struct ib_ucontext *context)
{
- return idr_read_obj(&ib_uverbs_qp_idr, qp_handle, context, 0);
+ return idr_read_obj(qp_handle, context, 0);
}
-static struct ib_qp *idr_write_qp(int qp_handle, struct ib_ucontext *context)
+struct ib_qp *idr_write_qp(int qp_handle, struct ib_ucontext *context)
{
struct ib_uobject *uobj;
- uobj = idr_write_uobj(&ib_uverbs_qp_idr, qp_handle, context);
+ uobj = idr_write_uobj(qp_handle, context);
return uobj ? uobj->object : NULL;
}
@@ -261,9 +259,9 @@ static void put_qp_write(struct ib_qp *qp)
put_uobj_write(qp->uobject);
}
-static struct ib_srq *idr_read_srq(int srq_handle, struct ib_ucontext *context)
+struct ib_srq *idr_read_srq(int srq_handle, struct ib_ucontext *context)
{
- return idr_read_obj(&ib_uverbs_srq_idr, srq_handle, context, 0);
+ return idr_read_obj(srq_handle, context, 0);
}
static void put_srq_read(struct ib_srq *srq)
@@ -271,10 +269,10 @@ static void put_srq_read(struct ib_srq *srq)
put_uobj_read(srq->uobject);
}
-static struct ib_xrcd *idr_read_xrcd(int xrcd_handle, struct ib_ucontext *context,
- struct ib_uobject **uobj)
+struct ib_xrcd *idr_read_xrcd(int xrcd_handle, struct ib_ucontext *context,
+ struct ib_uobject **uobj)
{
- *uobj = idr_read_uobj(&ib_uverbs_xrcd_idr, xrcd_handle, context, 0);
+ *uobj = idr_read_uobj(xrcd_handle, context, 0);
return *uobj ? (*uobj)->object : NULL;
}
@@ -282,7 +280,6 @@ static void put_xrcd_read(struct ib_uobject *uobj)
{
put_uobj_read(uobj);
}
-
ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file,
struct ib_device *ib_dev,
const char __user *buf,
@@ -550,7 +547,7 @@ ssize_t ib_uverbs_alloc_pd(struct ib_uverbs_file *file,
atomic_set(&pd->usecnt, 0);
uobj->object = pd;
- ret = idr_add_uobj(&ib_uverbs_pd_idr, uobj);
+ ret = idr_add_uobj(uobj);
if (ret)
goto err_idr;
@@ -574,7 +571,7 @@ ssize_t ib_uverbs_alloc_pd(struct ib_uverbs_file *file,
return in_len;
err_copy:
- idr_remove_uobj(&ib_uverbs_pd_idr, uobj);
+ idr_remove_uobj(uobj);
err_idr:
ib_dealloc_pd(pd);
@@ -597,7 +594,7 @@ ssize_t ib_uverbs_dealloc_pd(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- uobj = idr_write_uobj(&ib_uverbs_pd_idr, cmd.pd_handle, file->ucontext);
+ uobj = idr_write_uobj(cmd.pd_handle, file->ucontext);
if (!uobj)
return -EINVAL;
pd = uobj->object;
@@ -615,7 +612,7 @@ ssize_t ib_uverbs_dealloc_pd(struct ib_uverbs_file *file,
uobj->live = 0;
put_uobj_write(uobj);
- idr_remove_uobj(&ib_uverbs_pd_idr, uobj);
+ idr_remove_uobj(uobj);
mutex_lock(&file->mutex);
list_del(&uobj->list);
@@ -791,7 +788,7 @@ ssize_t ib_uverbs_open_xrcd(struct ib_uverbs_file *file,
atomic_set(&obj->refcnt, 0);
obj->uobject.object = xrcd;
- ret = idr_add_uobj(&ib_uverbs_xrcd_idr, &obj->uobject);
+ ret = idr_add_uobj(&obj->uobject);
if (ret)
goto err_idr;
@@ -835,7 +832,7 @@ err_copy:
}
err_insert_xrcd:
- idr_remove_uobj(&ib_uverbs_xrcd_idr, &obj->uobject);
+ idr_remove_uobj(&obj->uobject);
err_idr:
ib_dealloc_xrcd(xrcd);
@@ -869,7 +866,7 @@ ssize_t ib_uverbs_close_xrcd(struct ib_uverbs_file *file,
return -EFAULT;
mutex_lock(&file->device->xrcd_tree_mutex);
- uobj = idr_write_uobj(&ib_uverbs_xrcd_idr, cmd.xrcd_handle, file->ucontext);
+ uobj = idr_write_uobj(cmd.xrcd_handle, file->ucontext);
if (!uobj) {
ret = -EINVAL;
goto out;
@@ -902,7 +899,7 @@ ssize_t ib_uverbs_close_xrcd(struct ib_uverbs_file *file,
if (inode && !live)
xrcd_table_delete(file->device, inode);
- idr_remove_uobj(&ib_uverbs_xrcd_idr, uobj);
+ idr_remove_uobj(uobj);
mutex_lock(&file->mutex);
list_del(&uobj->list);
mutex_unlock(&file->mutex);
@@ -995,7 +992,7 @@ ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file,
atomic_inc(&pd->usecnt);
uobj->object = mr;
- ret = idr_add_uobj(&ib_uverbs_mr_idr, uobj);
+ ret = idr_add_uobj(uobj);
if (ret)
goto err_unreg;
@@ -1023,7 +1020,7 @@ ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file,
return in_len;
err_copy:
- idr_remove_uobj(&ib_uverbs_mr_idr, uobj);
+ idr_remove_uobj(uobj);
err_unreg:
ib_dereg_mr(mr);
@@ -1068,8 +1065,7 @@ ssize_t ib_uverbs_rereg_mr(struct ib_uverbs_file *file,
(cmd.start & ~PAGE_MASK) != (cmd.hca_va & ~PAGE_MASK)))
return -EINVAL;
- uobj = idr_write_uobj(&ib_uverbs_mr_idr, cmd.mr_handle,
- file->ucontext);
+ uobj = idr_write_uobj(cmd.mr_handle, file->ucontext);
if (!uobj)
return -EINVAL;
@@ -1138,7 +1134,7 @@ ssize_t ib_uverbs_dereg_mr(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- uobj = idr_write_uobj(&ib_uverbs_mr_idr, cmd.mr_handle, file->ucontext);
+ uobj = idr_write_uobj(cmd.mr_handle, file->ucontext);
if (!uobj)
return -EINVAL;
@@ -1153,7 +1149,7 @@ ssize_t ib_uverbs_dereg_mr(struct ib_uverbs_file *file,
if (ret)
return ret;
- idr_remove_uobj(&ib_uverbs_mr_idr, uobj);
+ idr_remove_uobj(uobj);
mutex_lock(&file->mutex);
list_del(&uobj->list);
@@ -1213,7 +1209,7 @@ ssize_t ib_uverbs_alloc_mw(struct ib_uverbs_file *file,
atomic_inc(&pd->usecnt);
uobj->object = mw;
- ret = idr_add_uobj(&ib_uverbs_mw_idr, uobj);
+ ret = idr_add_uobj(uobj);
if (ret)
goto err_unalloc;
@@ -1240,7 +1236,7 @@ ssize_t ib_uverbs_alloc_mw(struct ib_uverbs_file *file,
return in_len;
err_copy:
- idr_remove_uobj(&ib_uverbs_mw_idr, uobj);
+ idr_remove_uobj(uobj);
err_unalloc:
uverbs_dealloc_mw(mw);
@@ -1266,7 +1262,7 @@ ssize_t ib_uverbs_dealloc_mw(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof(cmd)))
return -EFAULT;
- uobj = idr_write_uobj(&ib_uverbs_mw_idr, cmd.mw_handle, file->ucontext);
+ uobj = idr_write_uobj(cmd.mw_handle, file->ucontext);
if (!uobj)
return -EINVAL;
@@ -1281,7 +1277,7 @@ ssize_t ib_uverbs_dealloc_mw(struct ib_uverbs_file *file,
if (ret)
return ret;
- idr_remove_uobj(&ib_uverbs_mw_idr, uobj);
+ idr_remove_uobj(uobj);
mutex_lock(&file->mutex);
list_del(&uobj->list);
@@ -1395,7 +1391,7 @@ static struct ib_ucq_object *create_cq(struct ib_uverbs_file *file,
atomic_set(&cq->usecnt, 0);
obj->uobject.object = cq;
- ret = idr_add_uobj(&ib_uverbs_cq_idr, &obj->uobject);
+ ret = idr_add_uobj(&obj->uobject);
if (ret)
goto err_free;
@@ -1421,7 +1417,7 @@ static struct ib_ucq_object *create_cq(struct ib_uverbs_file *file,
return obj;
err_cb:
- idr_remove_uobj(&ib_uverbs_cq_idr, &obj->uobject);
+ idr_remove_uobj(&obj->uobject);
err_free:
ib_destroy_cq(cq);
@@ -1691,7 +1687,7 @@ ssize_t ib_uverbs_destroy_cq(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- uobj = idr_write_uobj(&ib_uverbs_cq_idr, cmd.cq_handle, file->ucontext);
+ uobj = idr_write_uobj(cmd.cq_handle, file->ucontext);
if (!uobj)
return -EINVAL;
cq = uobj->object;
@@ -1707,7 +1703,7 @@ ssize_t ib_uverbs_destroy_cq(struct ib_uverbs_file *file,
if (ret)
return ret;
- idr_remove_uobj(&ib_uverbs_cq_idr, uobj);
+ idr_remove_uobj(uobj);
mutex_lock(&file->mutex);
list_del(&uobj->list);
@@ -1878,7 +1874,7 @@ static int create_qp(struct ib_uverbs_file *file,
qp->uobject = &obj->uevent.uobject;
obj->uevent.uobject.object = qp;
- ret = idr_add_uobj(&ib_uverbs_qp_idr, &obj->uevent.uobject);
+ ret = idr_add_uobj(&obj->uevent.uobject);
if (ret)
goto err_destroy;
@@ -1924,7 +1920,7 @@ static int create_qp(struct ib_uverbs_file *file,
return 0;
err_cb:
- idr_remove_uobj(&ib_uverbs_qp_idr, &obj->uevent.uobject);
+ idr_remove_uobj(&obj->uevent.uobject);
err_destroy:
ib_destroy_qp(qp);
@@ -2108,7 +2104,7 @@ ssize_t ib_uverbs_open_qp(struct ib_uverbs_file *file,
qp->uobject = &obj->uevent.uobject;
obj->uevent.uobject.object = qp;
- ret = idr_add_uobj(&ib_uverbs_qp_idr, &obj->uevent.uobject);
+ ret = idr_add_uobj(&obj->uevent.uobject);
if (ret)
goto err_destroy;
@@ -2137,7 +2133,7 @@ ssize_t ib_uverbs_open_qp(struct ib_uverbs_file *file,
return in_len;
err_remove:
- idr_remove_uobj(&ib_uverbs_qp_idr, &obj->uevent.uobject);
+ idr_remove_uobj(&obj->uevent.uobject);
err_destroy:
ib_destroy_qp(qp);
@@ -2377,7 +2373,7 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file,
memset(&resp, 0, sizeof resp);
- uobj = idr_write_uobj(&ib_uverbs_qp_idr, cmd.qp_handle, file->ucontext);
+ uobj = idr_write_uobj(cmd.qp_handle, file->ucontext);
if (!uobj)
return -EINVAL;
qp = uobj->object;
@@ -2400,7 +2396,7 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file,
if (obj->uxrcd)
atomic_dec(&obj->uxrcd->refcnt);
- idr_remove_uobj(&ib_uverbs_qp_idr, uobj);
+ idr_remove_uobj(uobj);
mutex_lock(&file->mutex);
list_del(&uobj->list);
@@ -2852,7 +2848,7 @@ ssize_t ib_uverbs_create_ah(struct ib_uverbs_file *file,
ah->uobject = uobj;
uobj->object = ah;
- ret = idr_add_uobj(&ib_uverbs_ah_idr, uobj);
+ ret = idr_add_uobj(uobj);
if (ret)
goto err_destroy;
@@ -2877,7 +2873,7 @@ ssize_t ib_uverbs_create_ah(struct ib_uverbs_file *file,
return in_len;
err_copy:
- idr_remove_uobj(&ib_uverbs_ah_idr, uobj);
+ idr_remove_uobj(uobj);
err_destroy:
ib_destroy_ah(ah);
@@ -2902,7 +2898,7 @@ ssize_t ib_uverbs_destroy_ah(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- uobj = idr_write_uobj(&ib_uverbs_ah_idr, cmd.ah_handle, file->ucontext);
+ uobj = idr_write_uobj(cmd.ah_handle, file->ucontext);
if (!uobj)
return -EINVAL;
ah = uobj->object;
@@ -2916,7 +2912,7 @@ ssize_t ib_uverbs_destroy_ah(struct ib_uverbs_file *file,
if (ret)
return ret;
- idr_remove_uobj(&ib_uverbs_ah_idr, uobj);
+ idr_remove_uobj(uobj);
mutex_lock(&file->mutex);
list_del(&uobj->list);
@@ -3184,7 +3180,7 @@ int ib_uverbs_ex_create_flow(struct ib_uverbs_file *file,
flow_id->uobject = uobj;
uobj->object = flow_id;
- err = idr_add_uobj(&ib_uverbs_rule_idr, uobj);
+ err = idr_add_uobj(uobj);
if (err)
goto destroy_flow;
@@ -3209,7 +3205,7 @@ int ib_uverbs_ex_create_flow(struct ib_uverbs_file *file,
kfree(kern_flow_attr);
return 0;
err_copy:
- idr_remove_uobj(&ib_uverbs_rule_idr, uobj);
+ idr_remove_uobj(uobj);
destroy_flow:
ib_destroy_flow(flow_id);
err_free:
@@ -3244,8 +3240,7 @@ int ib_uverbs_ex_destroy_flow(struct ib_uverbs_file *file,
if (cmd.comp_mask)
return -EINVAL;
- uobj = idr_write_uobj(&ib_uverbs_rule_idr, cmd.flow_handle,
- file->ucontext);
+ uobj = idr_write_uobj(cmd.flow_handle, file->ucontext);
if (!uobj)
return -EINVAL;
flow_id = uobj->object;
@@ -3256,7 +3251,7 @@ int ib_uverbs_ex_destroy_flow(struct ib_uverbs_file *file,
put_uobj_write(uobj);
- idr_remove_uobj(&ib_uverbs_rule_idr, uobj);
+ idr_remove_uobj(uobj);
mutex_lock(&file->mutex);
list_del(&uobj->list);
@@ -3344,7 +3339,7 @@ static int __uverbs_create_xsrq(struct ib_uverbs_file *file,
atomic_set(&srq->usecnt, 0);
obj->uevent.uobject.object = srq;
- ret = idr_add_uobj(&ib_uverbs_srq_idr, &obj->uevent.uobject);
+ ret = idr_add_uobj(&obj->uevent.uobject);
if (ret)
goto err_destroy;
@@ -3378,7 +3373,7 @@ static int __uverbs_create_xsrq(struct ib_uverbs_file *file,
return 0;
err_copy:
- idr_remove_uobj(&ib_uverbs_srq_idr, &obj->uevent.uobject);
+ idr_remove_uobj(&obj->uevent.uobject);
err_destroy:
ib_destroy_srq(srq);
@@ -3554,7 +3549,7 @@ ssize_t ib_uverbs_destroy_srq(struct ib_uverbs_file *file,
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- uobj = idr_write_uobj(&ib_uverbs_srq_idr, cmd.srq_handle, file->ucontext);
+ uobj = idr_write_uobj(cmd.srq_handle, file->ucontext);
if (!uobj)
return -EINVAL;
srq = uobj->object;
@@ -3575,7 +3570,7 @@ ssize_t ib_uverbs_destroy_srq(struct ib_uverbs_file *file,
atomic_dec(&us->uxrcd->refcnt);
}
- idr_remove_uobj(&ib_uverbs_srq_idr, uobj);
+ idr_remove_uobj(uobj);
mutex_lock(&file->mutex);
list_del(&uobj->list);
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index 31f422a..333ed70 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -66,17 +66,6 @@ enum {
static struct class *uverbs_class;
-DEFINE_SPINLOCK(ib_uverbs_idr_lock);
-DEFINE_IDR(ib_uverbs_pd_idr);
-DEFINE_IDR(ib_uverbs_mr_idr);
-DEFINE_IDR(ib_uverbs_mw_idr);
-DEFINE_IDR(ib_uverbs_ah_idr);
-DEFINE_IDR(ib_uverbs_cq_idr);
-DEFINE_IDR(ib_uverbs_qp_idr);
-DEFINE_IDR(ib_uverbs_srq_idr);
-DEFINE_IDR(ib_uverbs_xrcd_idr);
-DEFINE_IDR(ib_uverbs_rule_idr);
-
static DEFINE_SPINLOCK(map_lock);
static DECLARE_BITMAP(dev_map, IB_UVERBS_MAX_DEVICES);
@@ -227,7 +216,7 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
list_for_each_entry_safe(uobj, tmp, &context->ah_list, list) {
struct ib_ah *ah = uobj->object;
- idr_remove_uobj(&ib_uverbs_ah_idr, uobj);
+ idr_remove_uobj(uobj);
ib_destroy_ah(ah);
kfree(uobj);
}
@@ -236,7 +225,7 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
list_for_each_entry_safe(uobj, tmp, &context->mw_list, list) {
struct ib_mw *mw = uobj->object;
- idr_remove_uobj(&ib_uverbs_mw_idr, uobj);
+ idr_remove_uobj(uobj);
uverbs_dealloc_mw(mw);
kfree(uobj);
}
@@ -244,7 +233,7 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
list_for_each_entry_safe(uobj, tmp, &context->rule_list, list) {
struct ib_flow *flow_id = uobj->object;
- idr_remove_uobj(&ib_uverbs_rule_idr, uobj);
+ idr_remove_uobj(uobj);
ib_destroy_flow(flow_id);
kfree(uobj);
}
@@ -254,7 +243,7 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
struct ib_uqp_object *uqp =
container_of(uobj, struct ib_uqp_object, uevent.uobject);
- idr_remove_uobj(&ib_uverbs_qp_idr, uobj);
+ idr_remove_uobj(uobj);
if (qp != qp->real_qp) {
ib_close_qp(qp);
} else {
@@ -270,7 +259,7 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
struct ib_uevent_object *uevent =
container_of(uobj, struct ib_uevent_object, uobject);
- idr_remove_uobj(&ib_uverbs_srq_idr, uobj);
+ idr_remove_uobj(uobj);
ib_destroy_srq(srq);
ib_uverbs_release_uevent(file, uevent);
kfree(uevent);
@@ -282,7 +271,7 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
struct ib_ucq_object *ucq =
container_of(uobj, struct ib_ucq_object, uobject);
- idr_remove_uobj(&ib_uverbs_cq_idr, uobj);
+ idr_remove_uobj(uobj);
ib_destroy_cq(cq);
ib_uverbs_release_ucq(file, ev_file, ucq);
kfree(ucq);
@@ -291,7 +280,7 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
list_for_each_entry_safe(uobj, tmp, &context->mr_list, list) {
struct ib_mr *mr = uobj->object;
- idr_remove_uobj(&ib_uverbs_mr_idr, uobj);
+ idr_remove_uobj(uobj);
ib_dereg_mr(mr);
kfree(uobj);
}
@@ -302,7 +291,7 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
struct ib_uxrcd_object *uxrcd =
container_of(uobj, struct ib_uxrcd_object, uobject);
- idr_remove_uobj(&ib_uverbs_xrcd_idr, uobj);
+ idr_remove_uobj(uobj);
ib_uverbs_dealloc_xrcd(file->device, xrcd);
kfree(uxrcd);
}
@@ -311,7 +300,7 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
list_for_each_entry_safe(uobj, tmp, &context->pd_list, list) {
struct ib_pd *pd = uobj->object;
- idr_remove_uobj(&ib_uverbs_pd_idr, uobj);
+ idr_remove_uobj(uobj);
ib_dealloc_pd(pd);
kfree(uobj);
}
@@ -1326,13 +1315,6 @@ static void __exit ib_uverbs_cleanup(void)
unregister_chrdev_region(IB_UVERBS_BASE_DEV, IB_UVERBS_MAX_DEVICES);
if (overflow_maj)
unregister_chrdev_region(overflow_maj, IB_UVERBS_MAX_DEVICES);
- idr_destroy(&ib_uverbs_pd_idr);
- idr_destroy(&ib_uverbs_mr_idr);
- idr_destroy(&ib_uverbs_mw_idr);
- idr_destroy(&ib_uverbs_ah_idr);
- idr_destroy(&ib_uverbs_cq_idr);
- idr_destroy(&ib_uverbs_qp_idr);
- idr_destroy(&ib_uverbs_srq_idr);
}
module_init(ib_uverbs_init);
diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h
index 432bed5..14bfe3b 100644
--- a/include/rdma/ib_verbs.h
+++ b/include/rdma/ib_verbs.h
@@ -1706,6 +1706,10 @@ struct ib_device {
struct iw_cm_verbs *iwcm;
+ struct idr idr;
+ /* Global lock in use to safely release device IDR */
+ spinlock_t idr_lock;
+
/**
* alloc_hw_stats - Allocate a struct rdma_hw_stats and fill in the
* driver initialized data. The struct is kfree()'ed by the sysfs
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [RFC ABI V4 1/7] RDMA/core: Export RDMA IOCTL declarations
From: Matan Barak @ 2016-09-28 15:10 UTC (permalink / raw)
To: linux-rdma-u79uwXL29TY76Z2rM5mHXA
Cc: Doug Ledford, Jason Gunthorpe, Sean Hefty, Liran Liss,
Haggai Eran, Matan Barak, Majd Dibbiny, Christoph Lameter,
Leon Romanovsky
In-Reply-To: <1475075417-30189-1-git-send-email-matanb-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
From: Leon Romanovsky <leonro-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Place all external RDMA IOCTL declarations into one UAPI exported
header file and move all legacy MAD commands to that file.
Signed-off-by: Matan Barak <matanb-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Signed-off-by: Haggai Eran <haggaie-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Signed-off-by: Leon Romanovsky <leonro-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
---
drivers/infiniband/core/user_mad.c | 2 +-
include/rdma/rdma_ioctl.h | 38 ++++++++++++++++++++++++
include/uapi/rdma/Kbuild | 1 +
include/uapi/rdma/ib_user_mad.h | 12 --------
include/uapi/rdma/rdma_user_ioctl.h | 59 +++++++++++++++++++++++++++++++++++++
5 files changed, 99 insertions(+), 13 deletions(-)
create mode 100644 include/rdma/rdma_ioctl.h
create mode 100644 include/uapi/rdma/rdma_user_ioctl.h
diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c
index 415a318..70e974e 100644
--- a/drivers/infiniband/core/user_mad.c
+++ b/drivers/infiniband/core/user_mad.c
@@ -52,8 +52,8 @@
#include <asm/uaccess.h>
+#include <rdma/rdma_ioctl.h>
#include <rdma/ib_mad.h>
-#include <rdma/ib_user_mad.h>
MODULE_AUTHOR("Roland Dreier");
MODULE_DESCRIPTION("InfiniBand userspace MAD packet access");
diff --git a/include/rdma/rdma_ioctl.h b/include/rdma/rdma_ioctl.h
new file mode 100644
index 0000000..f62a359
--- /dev/null
+++ b/include/rdma/rdma_ioctl.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 Mellanox Technologies, LTD. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef RDMA_IOCTL_H
+#define RDMA_IOCTL_H
+
+#include <rdma/rdma_user_ioctl.h>
+
+#endif /* RDMA_IOCTL_H */
diff --git a/include/uapi/rdma/Kbuild b/include/uapi/rdma/Kbuild
index 231901b..537e40e 100644
--- a/include/uapi/rdma/Kbuild
+++ b/include/uapi/rdma/Kbuild
@@ -1,5 +1,6 @@
# UAPI Header export list
header-y += ib_user_cm.h
+header-y += rdma_user_ioctl.h
header-y += ib_user_mad.h
header-y += ib_user_sa.h
header-y += ib_user_verbs.h
diff --git a/include/uapi/rdma/ib_user_mad.h b/include/uapi/rdma/ib_user_mad.h
index 09f809f..7e722e7 100644
--- a/include/uapi/rdma/ib_user_mad.h
+++ b/include/uapi/rdma/ib_user_mad.h
@@ -230,16 +230,4 @@ struct ib_user_mad_reg_req2 {
__u8 reserved[3];
};
-#define IB_IOCTL_MAGIC 0x1b
-
-#define IB_USER_MAD_REGISTER_AGENT _IOWR(IB_IOCTL_MAGIC, 1, \
- struct ib_user_mad_reg_req)
-
-#define IB_USER_MAD_UNREGISTER_AGENT _IOW(IB_IOCTL_MAGIC, 2, __u32)
-
-#define IB_USER_MAD_ENABLE_PKEY _IO(IB_IOCTL_MAGIC, 3)
-
-#define IB_USER_MAD_REGISTER_AGENT2 _IOWR(IB_IOCTL_MAGIC, 4, \
- struct ib_user_mad_reg_req2)
-
#endif /* IB_USER_MAD_H */
diff --git a/include/uapi/rdma/rdma_user_ioctl.h b/include/uapi/rdma/rdma_user_ioctl.h
new file mode 100644
index 0000000..5c1117a
--- /dev/null
+++ b/include/uapi/rdma/rdma_user_ioctl.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016 Mellanox Technologies, LTD. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef RDMA_USER_IOCTL_H
+#define RDMA_USER_IOCTL_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define RDMA_IOCTL_MAGIC 0x1b
+/*
+ * For hfi1 driver
+ */
+#define IB_IOCTL_MAGIC RDMA_IOCTL_MAGIC
+
+/* Legacy part
+ * !!!! NOTE: It uses the same command index as VERBS
+ */
+#include <rdma/ib_user_mad.h>
+#define IB_USER_MAD_REGISTER_AGENT _IOWR(RDMA_IOCTL_MAGIC, 1, \
+ struct ib_user_mad_reg_req)
+
+#define IB_USER_MAD_UNREGISTER_AGENT _IOW(RDMA_IOCTL_MAGIC, 2, __u32)
+
+#define IB_USER_MAD_ENABLE_PKEY _IO(RDMA_IOCTL_MAGIC, 3)
+
+#define IB_USER_MAD_REGISTER_AGENT2 _IOWR(RDMA_IOCTL_MAGIC, 4, \
+ struct ib_user_mad_reg_req2)
+
+#endif /* RDMA_USER_IOCTL_H */
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [RFC ABI V4 0/7] SG-based RDMA ABI Proposal
From: Matan Barak @ 2016-09-28 15:10 UTC (permalink / raw)
To: linux-rdma-u79uwXL29TY76Z2rM5mHXA
Cc: Doug Ledford, Jason Gunthorpe, Sean Hefty, Liran Liss,
Haggai Eran, Matan Barak, Majd Dibbiny, Christoph Lameter,
Leon Romanovsky
The following patch set comes to enrich security model as a follow up
to commit e6bd18f57aad ('IB/security: Restrict use of the write() interface').
DISCLAIMER:
These patches are far from being completed. They present working init_ucontext
and query_device (both regular and extended version). In addition, they are
given as a basis of discussions.
NOT ALL COMMENTS GIVEN ON PREVIOUS VERSIONS ARE HANDLED IN THIS SERIES,
SOME OF THEM WILL BE HANDLED IN THE FUTURE.
The ideas presented here are based on our V1/V2/V3 series in addition to some
ideas presented in OFVWG and Sean's series.
This patch series add ioctl() interface to the existing write() interface and
provide an easy route to backport this change to legacy supported systems.
Analyzing the current uverbs role in dispatching and parsing commands, we find
that:
(a) uverbs validates the basic properties of the command
(b) uverbs is responsible of doing all the IDR and uobject management and
locking. It's also responsible of handling completion FDs.
(c) uverbs transforms the user<-->kernel ABI to kernel API.
(a) and (b) are valid for every kABI. Although the nature of commands could
change, they still have to be validated and transform to kernel pointers.
In order to avoid duplications between the various drivers, we would like to
keep (a) and (b) as shared code.
In addition, this is a good time to expand the ABI to be more scalable, so we
added a few goals:
(1) Command's attributes shall be extensible in an easy one. Either by allowing
drivers to have their own extensible set of attributes or core code
extensible attributes. Moreover, driver's specific attributes could some
day become core's standard attributes. We would like to still support
old user-space while avoid duplicating the code in kernel.
(2) Each driver may have specific type system (i.e QP, CQ, ....). It may
or may not even implement the standard type system. It could extend this
type system in the future. Try to avoid duplicating existing types or
actions.
(3) Do not change or recompile driver libraries and don't copy their data.
(4) Efficient dispatching.
Thus, in order to allow this flexibility, we decide giving (a) and (b) as a
common infrastructure, but use per-driver guidelines in order to do that
parsing and uobject management. Handlers are also set by the drivers
themselves (though they can point to either shared common code) or
driver specific code.
Since types are no longer enforced by the common infrastructure, there is no
point of pre-allocating common IDR types in the common code. Instead, we
provide an API for driver to add new types. We use one IDR per driver
for all its types. The driver declared all its supported types, their
free function and release order. After that, all uboject, exclusive access
and types are handled automatically for the driver by the infrastructure.
Scatter gather was chosen in order to allow us not to recompile user space
drivers. By using pointers to driver specific data, we could just use it
without introduce copying data and without changing the user-space driver at
all.
We chose to go with non blocking lock user objects. When exclusive
(WRITE or DESTROY) access is required, we dispatch the action if and only if
no other action needs this object as well. Otherwise, -EBUSY is returned to
the user-space. Device removal is synced with SRCU as of today.
If we were using locks, we would have need to sort the given user-space handles.
Otherwise, a user-space application may result in causing a deadlock.
Moving to a non blocking lock based behaviour, the dispatching in kernel
becomes more efficient.
Further uverbs related subsystem (such as RDMA-CM) may use other fds or use
other ioctl codes.
Note, we might switch to submitting one task (i.e - change locking schema) once
the concepts are more mature.
Regards,
Liran, Haggai, Leon and Matan
TODO:
1. Check other models for implementing FDs (as suggested in OFVWG).
2. Currently, this code only works with the new ioctl based libibverbs.
Make this compatible with the old version.
3. Rebase over latest kernel bits.
Changes from V3:
1. Add create_cq and create_comp_channel.
2. Add FD as ib_uobject into the type system
Changes from V2:
1. Use types declerations in order to declare release order and free function
2. Allow the driver to extend and use existing building blocks in any level:
a. Add more types
b. Add actions to exsiting types
c. Add attributes to existing actions (existed in V2)
Such a driver will only duplicate structs which it actually changed.
3. Fixed bugs in ucontext teardown and type allocation/locking.
4. Add reg_mr and init_pd
Changes from V1:
1. Refined locking system
a. try_read_lock and write lock to sync exclusive access
b. SRCU to sync device removal from commands execution
c. Future rwsem to sync close context from commands execution
2. Added temporary udata usage for vendor's data
3. Add query_device and init_ucontext command with mlx5 implementation
4. Fixed bugs in ioctl dispatching
5. Change callbacks to get ib_uverbs_file instead of ucontext
6. Add general types initialization and cleanups
Leon Romanovsky (2):
RDMA/core: Export RDMA IOCTL declarations
RDMA/core: Refactor IDR to be per-device
Matan Barak (5):
RDMA/core: Add support for custom types
RDMA/core: Add new ioctl interface
RDMA/core: Add initialize and cleanup of common types
RDMA/core: Add uverbs types, actions, handlers and attributes
IB/mlx5: Implement common uverb objects
drivers/infiniband/core/Makefile | 3 +-
drivers/infiniband/core/device.c | 18 +
drivers/infiniband/core/rdma_core.c | 505 +++++++++++++++++++
drivers/infiniband/core/rdma_core.h | 77 +++
drivers/infiniband/core/user_mad.c | 2 +-
drivers/infiniband/core/uverbs.h | 30 +-
drivers/infiniband/core/uverbs_cmd.c | 157 +++---
drivers/infiniband/core/uverbs_ioctl.c | 306 ++++++++++++
drivers/infiniband/core/uverbs_ioctl_cmd.c | 757 +++++++++++++++++++++++++++++
drivers/infiniband/core/uverbs_main.c | 165 ++-----
drivers/infiniband/hw/mlx5/main.c | 3 +
include/rdma/ib_verbs.h | 33 +-
include/rdma/rdma_ioctl.h | 38 ++
include/rdma/uverbs_ioctl.h | 342 +++++++++++++
include/rdma/uverbs_ioctl_cmd.h | 254 ++++++++++
include/uapi/rdma/Kbuild | 1 +
include/uapi/rdma/ib_user_mad.h | 12 -
include/uapi/rdma/ib_user_verbs.h | 13 +
include/uapi/rdma/rdma_user_ioctl.h | 82 ++++
19 files changed, 2571 insertions(+), 227 deletions(-)
create mode 100644 drivers/infiniband/core/rdma_core.c
create mode 100644 drivers/infiniband/core/rdma_core.h
create mode 100644 drivers/infiniband/core/uverbs_ioctl.c
create mode 100644 drivers/infiniband/core/uverbs_ioctl_cmd.c
create mode 100644 include/rdma/rdma_ioctl.h
create mode 100644 include/rdma/uverbs_ioctl.h
create mode 100644 include/rdma/uverbs_ioctl_cmd.h
create mode 100644 include/uapi/rdma/rdma_user_ioctl.h
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH for-next 0/8] Bug Fixes and Code Improvement in HNS driver
From: Doug Ledford @ 2016-09-28 14:57 UTC (permalink / raw)
To: Salil Mehta
Cc: xavier.huwei-hv44wF8Li93QT0dZR+AlfA@public.gmane.org,
oulijun-hv44wF8Li93QT0dZR+AlfA@public.gmane.org,
yisen.zhuang-hv44wF8Li93QT0dZR+AlfA@public.gmane.org,
mehta.salil.lnk-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
linux-rdma-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linuxarm-hv44wF8Li93QT0dZR+AlfA@public.gmane.org
In-Reply-To: <20160910040930.25988-1-salil.mehta-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
[-- Attachment #1.1: Type: text/plain, Size: 3155 bytes --]
On 9/10/16 12:09 AM, Salil Mehta wrote:
> This patch-set introduces fix to some Bugs, potential problems
> and code improvements identified during internal review and
> testing of Hisilicon Network Subsystem driver.
>
> Hi Doug,
> These are few HNS patches which are not related to HNS RoCE driver but
> are being sent through your repository hns-roce (as requested) to mitigate
> any conflicts with HNS RoCE driver for the 4.9 merge window. In future
> i.e. after 4.9 we will go by more formal git-pull request method once
> HNS RoCE base driver becomes part of 4.9.
Hi Salil,
I've chatted with Dave Miller about this series. Here's where we stand.
First, the review feedback from Dave:
---
In patch #7, their comments are mis-formatted and these
hns guys do this a lot.
/*fix hardware broadcast/multicast packets queue loopback */
They seem to have a hard time putting an initial space in the comment,
and properly capitalizing and punctuating their sentences.
Also in that new function, they need to order local variables from
longest to shortest line (reverse christmas tree format).
Patch #6 has the local variable ordering issue as well as does patch
#3.
---
Please address those issues and resubmit.
As for the method of resubmission, please send the set to netdev@ and
also linux-rdma@, make note that, as a one off thing, these will need to
go through my tree to avoid conflicts with patches already in my tree,
and that future submissions will follow the Mellanox model of submitting
shared code to both mine and David's tree that is needed to avoid
conflicts for that merge window's submissions. After the netdev people
have reviewed it, and Dave acks it, either he can pick it up and sort
the conflicts out, or I can pick it up.
>
> Daode Huang (6):
> net: hns: bug fix about setting coalsecs-usecs to 0
> net: hns: add fini_process for v2 napi process
> net: hns: delete repeat read fbd num after while
> net: hns: fix the bug of forwarding table
> net: hns: bug fix about broadcast/multicast packets
> net: hns: delete redundant broadcast packet filter process
>
> Kejian Yan (1):
> net: hns: fix port not available after testing loopback
>
> lipeng (1):
> net: hns: fix port unavailable after hnae_reserve_buffer_map fail
>
> drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c | 11 ++-
> drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c | 13 ++-
> drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h | 2 +
> drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c | 10 --
> drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h | 1 -
> drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c | 16 ++++
> drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h | 4 +
> drivers/net/ethernet/hisilicon/hns/hns_enet.c | 106 ++++++++++++++-------
> drivers/net/ethernet/hisilicon/hns/hns_ethtool.c | 7 ++
> 9 files changed, 117 insertions(+), 53 deletions(-)
>
--
Doug Ledford <dledford-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> GPG Key ID: 0E572FDD
Red Hat, Inc.
100 E. Davie St
Raleigh, NC 27601 USA
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 907 bytes --]
^ permalink raw reply
* RE: [PATCH 9/9] [RFC] nvme: Fix a race condition
From: Steve Wise @ 2016-09-28 14:23 UTC (permalink / raw)
To: 'Bart Van Assche', 'James Bottomley',
'Jens Axboe'
Cc: linux-block-u79uwXL29TY76Z2rM5mHXA, 'Martin K. Petersen',
'Mike Snitzer', linux-rdma-u79uwXL29TY76Z2rM5mHXA,
linux-nvme-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
'Keith Busch', 'Doug Ledford',
linux-scsi-u79uwXL29TY76Z2rM5mHXA, 'Christoph Hellwig'
In-Reply-To: <3fb86a78-de3a-b764-a493-cb5bc5b6d8b6-XdAiOPVOjttBDgjK7y7TUQ@public.gmane.org>
>
> Hello James and Steve,
>
> I will add a comment.
>
> Please note that the above patch does not change the behavior of
> nvme_stop_queues() except that it causes nvme_stop_queues() to wait
> until any ongoing nvme_queue_rq() calls have finished.
> blk_resume_queue() does not affect the value of the BLK_MQ_S_STOPPED bit
> that has been set by blk_mq_stop_hw_queues(). All it does is to resume
> pending blk_queue_enter() calls and to ensure that future
> blk_queue_enter() calls do not block. Even after blk_resume_queue() has
> been called if a new request is queued queue_rq() won't be invoked
> because the BLK_MQ_S_STOPPED bit is still set. Patch "dm: Fix a race
> condition related to stopping and starting queues" realizes a similar
> change in the dm driver and that change has been tested extensively.
>
Thanks for the detailed explanation! I think your code, then, is correct as-is. And this series doesn't fix the issue I'm hitting, so I'll keep digging. :)
Steve.
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* RE: [PATCH 0/2] cxgb4 FR_NSMR_TPTE_WR support
From: Steve Wise @ 2016-09-27 22:03 UTC (permalink / raw)
To: 'Doug Ledford'
Cc: netdev-u79uwXL29TY76Z2rM5mHXA, linux-rdma-u79uwXL29TY76Z2rM5mHXA,
'David Miller'
In-Reply-To: <20160919.102920.1126387243122900012.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
>
> >
> >>
> >> From: Steve Wise <swise-7bPotxP6k4+P2YhJcF5u+vpXobYPEAuW@public.gmane.org>
> >> Date: Fri, 16 Sep 2016 07:54:55 -0700
> >>
> >> > This series enables a new work request to optimize small REG_MR
> >> > operations. This is intended for 4.9. If everyone agrees, I suggest
> >> > Doug take both the cxgb4 and iw_cxgb4 patches through his tree.
> >>
> >> I'm assuming this mean that I do _not_ apply these to my tree.
> >
> > Yes, if you're ok with that.
>
> I am.
Doug, please include this for 4.9 if it looks good to you. Let me know.
Thanks,
Steve.
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v5 13/16] IB/pvrdma: Add the main driver module for PVRDMA
From: Adit Ranadive @ 2016-09-27 18:50 UTC (permalink / raw)
To: David Laight, Yuval Shaia
Cc: dledford@redhat.com, linux-rdma@vger.kernel.org,
pv-drivers@vmware.com, netdev@vger.kernel.org,
linux-pci@vger.kernel.org, jhansen@vmware.com,
asarwade@vmware.com, georgezhang@vmware.com, bryantan@vmware.com
In-Reply-To: <063D6719AE5E284EB5DD2968C1650D6DB010A6EA@AcuExch.aculab.com>
On Tue, Sep 27, 2016 at 09:21:27AM +0000, David Laight wrote:
> From: Adit Ranadive
> > Sent: 26 September 2016 19:15
> > On Mon, Sep 26, 2016 at 00:27:40AM -0700, Yuval Shaia wrote:
> > > On Sat, Sep 24, 2016 at 04:21:37PM -0700, Adit Ranadive wrote:
> > > > +
> > > > + /* Currently, the driver only supports RoCE mode. */
> > > > + if (dev->dsr->caps.mode != PVRDMA_DEVICE_MODE_ROCE) {
> > > > + dev_err(&pdev->dev, "unsupported transport %d\n",
> > > > + dev->dsr->caps.mode);
> > > > + ret = -EINVAL;
> > >
> > > This is some fatal error with the device, not that something wrong with the
> > > function's argument.
> > > Suggesting to replace with -EFAULT.
> > >
> >
> > Thanks, will fix this one and the others here.
>
> Won't EFAULT generate SIGSEGV ?
Since this is called at module load time, wouldn't the module load fail with
this error rather than generate a SIGSEGV?
I'm slightly unclear about what would if it is compiled into the kernel though
I think it should fail with the error.
The only other error value to return here that could make sense is EIO.
^ permalink raw reply
* Re: [PATCH rdma-core 4/5] libocrdma: Move ocrdma's list implementation into common directory
From: Jason Gunthorpe @ 2016-09-27 18:03 UTC (permalink / raw)
To: Yishai Hadas
Cc: Christoph Hellwig, Leon Romanovsky,
dledford-H+wXaHxf7aLQT0dZR+AlfA,
linux-rdma-u79uwXL29TY76Z2rM5mHXA, yishaih-VPRAkNaXOzVWk0Htik3J/w
In-Reply-To: <ff764b75-19af-340b-7228-328462c524ae-LDSdmyG8hGV8YrgS2mwiifqBs+8SCbDb@public.gmane.org>
On Tue, Sep 27, 2016 at 07:14:09PM +0300, Yishai Hadas wrote:
> Leon is out of office for a week, I can take it from here and come to the
> list in coming days with a new candidate series based on previous notes.
Right, I forgot..
> Few notes to sync on:
> 1) Looking at ocrdma_list.h it extends the list default functionality to use
> an internal mutex, see list_lock & list_unlock calls. This is not a standard
> usage of a list. It may require some logic outside list.h to replace the
> usage without introducing the mutex in the shared new H file.
I noticed that. It doesn't look too bad, there are only two call sites
to list_lock, so I'd just move the mutex from the list head to the
ocrdma_dev_list.
> 2) Taking list.h from the 'CCAN' URL requires taking as well few other H
> files that it uses internally (e.g. build_assert.h). In few cases I don't
> see any reason to take the full file into rdma-core (e.g. ccan/str/str.h for
> stringify) will take only the needed functionality into list.h.
Hum. We have various other places using stringify and other
macros. Nothing in str.h looks bad at my first glance, so I'd just
take the whole thing.
It will be easier to work with ccan going forward if you minimize the
changes made to their stuff.
I suggest putting into a top level ccan/ (or util/ccan?) directory,
flatten the directory structure and we will compile the C component of
it into libccan.a and libccan_pic.a and link everything to it.
> 3) Need to clean up the 'CCAN' files from its include to "config.h", in
> addition, need to consider the functionality that need to be taken when
> there are some #if HAVE_XXX internally. (see #if HAVE_TYPEOF in list.h).
If you can get the code ported with a hardwired config.h (just add
stuff to buildlib/config.h.in) and whatever else, send it to me and
I'll get everything sorted out for cmake.
Generally speaking though, as code that targets only Linux, and only
Linux distros of a certain age, we can safely make a lot of
assumptions.
> 4) Re the licensing disclaimer in each H file, what do you suggest to put ?
> for example minmax.h uses licenses/CC0 see
> https://github.com/rustyrussell/ccan/blob/master/ccan/minmax/LICENSE which
> is different comparing list.h which is ../../licenses/BSD-MIT.
If we go with the ccan/ top level then add the the files as
ccan/COPYING.CCO and ccan/COPYING.MIT, etc and update the comments
accordingly.
So far all the options we've found require accepting single-licened
code. I view this as OK since those two licenses are widely regarded
to be GPLv2 compatible, and specifically OK'd by the FSF. I'd prefer
this situation to the current situation of having potentially
GPLv2-only code...
Jason
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 9/9] [RFC] nvme: Fix a race condition
From: Bart Van Assche @ 2016-09-27 17:09 UTC (permalink / raw)
To: James Bottomley, Steve Wise, 'Jens Axboe'
Cc: linux-block-u79uwXL29TY76Z2rM5mHXA, 'Martin K. Petersen',
'Mike Snitzer', linux-rdma-u79uwXL29TY76Z2rM5mHXA,
linux-nvme-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
'Keith Busch', 'Doug Ledford',
linux-scsi-u79uwXL29TY76Z2rM5mHXA, 'Christoph Hellwig'
In-Reply-To: <1474995360.2716.19.camel-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
On 09/27/2016 09:56 AM, James Bottomley wrote:
> On Tue, 2016-09-27 at 09:43 -0700, Bart Van Assche wrote:
>> On 09/27/2016 09:31 AM, Steve Wise wrote:
>>>> @@ -2079,11 +2075,15 @@ EXPORT_SYMBOL_GPL(nvme_kill_queues);
>>>> void nvme_stop_queues(struct nvme_ctrl *ctrl)
>>>> {
>>>> struct nvme_ns *ns;
>>>> + struct request_queue *q;
>>>>
>>>> mutex_lock(&ctrl->namespaces_mutex);
>>>> list_for_each_entry(ns, &ctrl->namespaces, list) {
>>>> - blk_mq_cancel_requeue_work(ns->queue);
>>>> - blk_mq_stop_hw_queues(ns->queue);
>>>> + q = ns->queue;
>>>> + blk_quiesce_queue(q);
>>>> + blk_mq_cancel_requeue_work(q);
>>>> + blk_mq_stop_hw_queues(q);
>>>> + blk_resume_queue(q);
>>>> }
>>>> mutex_unlock(&ctrl->namespaces_mutex);
>>>
>>> Hey Bart, should nvme_stop_queues() really be resuming the blk
>>> queue?
>>
>> Hello Steve,
>>
>> Would you perhaps prefer that blk_resume_queue(q) is called from
>> nvme_start_queues()? I think that would make the NVMe code harder to
>> review. The above code won't cause any unexpected side effects if an
>> NVMe namespace is removed after nvme_stop_queues() has been called
>> and before nvme_start_queues() is called. Moving the
>> blk_resume_queue(q) call into nvme_start_queues() will only work as
>> expected if no namespaces are added nor removed between the
>> nvme_stop_queues() and nvme_start_queues() calls. I'm not familiar
>> enough with the NVMe code to know whether or not this change is safe
>> ...
>
> It's something that looks obviously wrong, so explain why you need to
> do it, preferably in a comment above the function.
Hello James and Steve,
I will add a comment.
Please note that the above patch does not change the behavior of
nvme_stop_queues() except that it causes nvme_stop_queues() to wait
until any ongoing nvme_queue_rq() calls have finished.
blk_resume_queue() does not affect the value of the BLK_MQ_S_STOPPED bit
that has been set by blk_mq_stop_hw_queues(). All it does is to resume
pending blk_queue_enter() calls and to ensure that future
blk_queue_enter() calls do not block. Even after blk_resume_queue() has
been called if a new request is queued queue_rq() won't be invoked
because the BLK_MQ_S_STOPPED bit is still set. Patch "dm: Fix a race
condition related to stopping and starting queues" realizes a similar
change in the dm driver and that change has been tested extensively.
Bart.
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* RE: [PATCH 9/9] [RFC] nvme: Fix a race condition
From: Steve Wise @ 2016-09-27 16:56 UTC (permalink / raw)
To: 'Bart Van Assche', 'Jens Axboe'
Cc: linux-block, 'James Bottomley',
'Martin K. Petersen', 'Mike Snitzer', linux-rdma,
linux-nvme, 'Keith Busch', 'Doug Ledford',
linux-scsi, 'Christoph Hellwig'
In-Reply-To: <9d8e0f32-6703-cf23-d424-bcecb65c2a26@sandisk.com>
> On 09/27/2016 09:31 AM, Steve Wise wrote:
> >> @@ -2079,11 +2075,15 @@ EXPORT_SYMBOL_GPL(nvme_kill_queues);
> >> void nvme_stop_queues(struct nvme_ctrl *ctrl)
> >> {
> >> struct nvme_ns *ns;
> >> + struct request_queue *q;
> >>
> >> mutex_lock(&ctrl->namespaces_mutex);
> >> list_for_each_entry(ns, &ctrl->namespaces, list) {
> >> - blk_mq_cancel_requeue_work(ns->queue);
> >> - blk_mq_stop_hw_queues(ns->queue);
> >> + q = ns->queue;
> >> + blk_quiesce_queue(q);
> >> + blk_mq_cancel_requeue_work(q);
> >> + blk_mq_stop_hw_queues(q);
> >> + blk_resume_queue(q);
> >> }
> >> mutex_unlock(&ctrl->namespaces_mutex);
> >
> > Hey Bart, should nvme_stop_queues() really be resuming the blk queue?
>
> Hello Steve,
>
> Would you perhaps prefer that blk_resume_queue(q) is called from
> nvme_start_queues()? I think that would make the NVMe code harder to
> review.
I'm still learning the blk code (and nvme code :)), but I would think
blk_resume_queue() would cause requests to start being submit on the NVME
queues, which I believe shouldn't happen when they are stopped. I'm currently
debugging a problem where requests are submitted to the nvme-rdma driver while
it has supposedly stopped all the nvme and blk mqs. I tried your series at
Christoph's request to see if it resolved my problem, but it didn't.
> The above code won't cause any unexpected side effects if an
> NVMe namespace is removed after nvme_stop_queues() has been called and
> before nvme_start_queues() is called. Moving the blk_resume_queue(q)
> call into nvme_start_queues() will only work as expected if no
> namespaces are added nor removed between the nvme_stop_queues() and
> nvme_start_queues() calls. I'm not familiar enough with the NVMe code to
> know whether or not this change is safe ...
>
I'll have to look and see if new namespaces can be added/deleted while a nvme
controller is in the RECONNECTING state. In the meantime, I'm going to move
the blk_resume_queue() to nvme_start_queues() and see if it helps my problem.
Christoph: Thoughts?
Steve.
^ permalink raw reply
* Re: [PATCH 9/9] [RFC] nvme: Fix a race condition
From: James Bottomley @ 2016-09-27 16:56 UTC (permalink / raw)
To: Bart Van Assche, Steve Wise, 'Jens Axboe'
Cc: linux-block, 'Martin K. Petersen', 'Mike Snitzer',
linux-rdma, linux-nvme, 'Keith Busch',
'Doug Ledford', linux-scsi, 'Christoph Hellwig'
In-Reply-To: <9d8e0f32-6703-cf23-d424-bcecb65c2a26@sandisk.com>
On Tue, 2016-09-27 at 09:43 -0700, Bart Van Assche wrote:
> On 09/27/2016 09:31 AM, Steve Wise wrote:
> > > @@ -2079,11 +2075,15 @@ EXPORT_SYMBOL_GPL(nvme_kill_queues);
> > > void nvme_stop_queues(struct nvme_ctrl *ctrl)
> > > {
> > > struct nvme_ns *ns;
> > > + struct request_queue *q;
> > >
> > > mutex_lock(&ctrl->namespaces_mutex);
> > > list_for_each_entry(ns, &ctrl->namespaces, list) {
> > > - blk_mq_cancel_requeue_work(ns->queue);
> > > - blk_mq_stop_hw_queues(ns->queue);
> > > + q = ns->queue;
> > > + blk_quiesce_queue(q);
> > > + blk_mq_cancel_requeue_work(q);
> > > + blk_mq_stop_hw_queues(q);
> > > + blk_resume_queue(q);
> > > }
> > > mutex_unlock(&ctrl->namespaces_mutex);
> >
> > Hey Bart, should nvme_stop_queues() really be resuming the blk
> > queue?
>
> Hello Steve,
>
> Would you perhaps prefer that blk_resume_queue(q) is called from
> nvme_start_queues()? I think that would make the NVMe code harder to
> review. The above code won't cause any unexpected side effects if an
> NVMe namespace is removed after nvme_stop_queues() has been called
> and before nvme_start_queues() is called. Moving the
> blk_resume_queue(q) call into nvme_start_queues() will only work as
> expected if no namespaces are added nor removed between the
> nvme_stop_queues() and nvme_start_queues() calls. I'm not familiar
> enough with the NVMe code to know whether or not this change is safe
> ...
It's something that looks obviously wrong, so explain why you need to
do it, preferably in a comment above the function.
James
^ permalink raw reply
* Re: [PATCH 9/9] [RFC] nvme: Fix a race condition
From: Bart Van Assche @ 2016-09-27 16:43 UTC (permalink / raw)
To: Steve Wise, 'Jens Axboe'
Cc: linux-block, 'James Bottomley',
'Martin K. Petersen', 'Mike Snitzer', linux-rdma,
linux-nvme, 'Keith Busch', 'Doug Ledford',
linux-scsi, 'Christoph Hellwig'
In-Reply-To: <013c01d218dc$8a5406c0$9efc1440$@opengridcomputing.com>
On 09/27/2016 09:31 AM, Steve Wise wrote:
>> @@ -2079,11 +2075,15 @@ EXPORT_SYMBOL_GPL(nvme_kill_queues);
>> void nvme_stop_queues(struct nvme_ctrl *ctrl)
>> {
>> struct nvme_ns *ns;
>> + struct request_queue *q;
>>
>> mutex_lock(&ctrl->namespaces_mutex);
>> list_for_each_entry(ns, &ctrl->namespaces, list) {
>> - blk_mq_cancel_requeue_work(ns->queue);
>> - blk_mq_stop_hw_queues(ns->queue);
>> + q = ns->queue;
>> + blk_quiesce_queue(q);
>> + blk_mq_cancel_requeue_work(q);
>> + blk_mq_stop_hw_queues(q);
>> + blk_resume_queue(q);
>> }
>> mutex_unlock(&ctrl->namespaces_mutex);
>
> Hey Bart, should nvme_stop_queues() really be resuming the blk queue?
Hello Steve,
Would you perhaps prefer that blk_resume_queue(q) is called from
nvme_start_queues()? I think that would make the NVMe code harder to
review. The above code won't cause any unexpected side effects if an
NVMe namespace is removed after nvme_stop_queues() has been called and
before nvme_start_queues() is called. Moving the blk_resume_queue(q)
call into nvme_start_queues() will only work as expected if no
namespaces are added nor removed between the nvme_stop_queues() and
nvme_start_queues() calls. I'm not familiar enough with the NVMe code to
know whether or not this change is safe ...
Bart.
^ permalink raw reply
* RE: [PATCH 9/9] [RFC] nvme: Fix a race condition
From: Steve Wise @ 2016-09-27 16:31 UTC (permalink / raw)
To: 'Bart Van Assche', 'Jens Axboe'
Cc: linux-block, 'James Bottomley',
'Martin K. Petersen', 'Mike Snitzer', linux-rdma,
linux-nvme, 'Keith Busch', 'Doug Ledford',
linux-scsi, 'Christoph Hellwig'
In-Reply-To: <9c372b04-a194-58c4-a64f-b155b52a5244@sandisk.com>
> @@ -2079,11 +2075,15 @@ EXPORT_SYMBOL_GPL(nvme_kill_queues);
> void nvme_stop_queues(struct nvme_ctrl *ctrl)
> {
> struct nvme_ns *ns;
> + struct request_queue *q;
>
> mutex_lock(&ctrl->namespaces_mutex);
> list_for_each_entry(ns, &ctrl->namespaces, list) {
> - blk_mq_cancel_requeue_work(ns->queue);
> - blk_mq_stop_hw_queues(ns->queue);
> + q = ns->queue;
> + blk_quiesce_queue(q);
> + blk_mq_cancel_requeue_work(q);
> + blk_mq_stop_hw_queues(q);
> + blk_resume_queue(q);
> }
> mutex_unlock(&ctrl->namespaces_mutex);
Hey Bart, should nvme_stop_queues() really be resuming the blk queue?
^ permalink raw reply
* Re: [PATCH rdma-core 4/5] libocrdma: Move ocrdma's list implementation into common directory
From: Yishai Hadas @ 2016-09-27 16:14 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: Christoph Hellwig, Leon Romanovsky,
dledford-H+wXaHxf7aLQT0dZR+AlfA,
linux-rdma-u79uwXL29TY76Z2rM5mHXA, yishaih-VPRAkNaXOzVWk0Htik3J/w
In-Reply-To: <20160926222319.GA2358-ePGOBjL8dl3ta4EC/59zMFaTQe2KTcn/@public.gmane.org>
On 9/27/2016 1:23 AM, Jason Gunthorpe wrote:
> On Mon, Sep 26, 2016 at 03:14:40PM -0700, Christoph Hellwig wrote:
>> On Mon, Sep 26, 2016 at 11:40:57AM -0600, Jason Gunthorpe wrote:
>>> Copyright isn't a patent,
>>
>> I know very well, thanks..
>
> Right, mainly for others..
>
>>> assuming freebsd didn't copy any code and
>>> just implemented the same API independently (eg it is an Independent
>>> Creation) they should be OK from a copyright perspective.
>>
>> That's a big IF, as I'm honestly not sure it is. I don't want to blame
>> anywone because I really don't know but if I had to decided to use it
>> or not I would error on the safe side.
>
> Sounds reasonable.
>
>>> As should be
>>> Rusty's version in CCAN.
>>
>> That's a much safer choice. It's a lightly different API, though.
>
> Leon? Does this seem doable to you?
Hi Jason,
Leon is out of office for a week, I can take it from here and come to
the list in coming days with a new candidate series based on previous notes.
Few notes to sync on:
1) Looking at ocrdma_list.h it extends the list default functionality to
use an internal mutex, see list_lock & list_unlock calls. This is not a
standard usage of a list. It may require some logic outside list.h to
replace the usage without introducing the mutex in the shared new H file.
2) Taking list.h from the 'CCAN' URL requires taking as well few other H
files that it uses internally (e.g. build_assert.h). In few cases I
don't see any reason to take the full file into rdma-core (e.g.
ccan/str/str.h for stringify) will take only the needed functionality
into list.h.
3) Need to clean up the 'CCAN' files from its include to "config.h", in
addition, need to consider the functionality that need to be taken when
there are some #if HAVE_XXX internally. (see #if HAVE_TYPEOF in list.h).
4) Re the licensing disclaimer in each H file, what do you suggest to
put ? for example minmax.h uses licenses/CC0 see
https://github.com/rustyrussell/ccan/blob/master/ccan/minmax/LICENSE
which is different comparing list.h which is ../../licenses/BSD-MIT.
Yishai
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 5/9] block: Extend blk_freeze_queue_start() to the non-blk-mq path
From: Bart Van Assche @ 2016-09-27 15:55 UTC (permalink / raw)
To: Ming Lei
Cc: Jens Axboe, Christoph Hellwig, James Bottomley,
Martin K. Petersen, Mike Snitzer, Doug Ledford, Keith Busch,
linux-block-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-scsi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-rdma-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-nvme-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
In-Reply-To: <a8e8796e-7266-4c8a-a4eb-31cedf73ec8a-XdAiOPVOjttBDgjK7y7TUQ@public.gmane.org>
On 09/27/2016 07:42 AM, Bart Van Assche wrote:
> Jens, regarding non-blk-mq mode and q_usage_counter: do you prefer that
> I rework patch 8/9 such that blk_quiesce_queue() and blk_resume_queue()
> are only used in blk-mq mode or are you OK with adding a
> blk_queue_enter() call in get_request() and a blk_queue_exit() call to
> __blk_put_request()?
(replying to my own e-mail)
Although it is easy to make q_usage_counter count non-blk-mq requests,
extending the blk_quiesce_queue() waiting mechanism to the non-blk-mq
path is non-trivial. To limit the number of changes in this patch series
I will drop the non-blk-mq changes from this patch series.
Bart.
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox