diff options
author | Hadar Hen Zion <hadarh@mellanox.com> | 2013-08-14 06:58:30 -0400 |
---|---|---|
committer | Roland Dreier <roland@purestorage.com> | 2013-08-28 12:53:14 -0400 |
commit | 436f2ad05a0b65b1467ddf51bc68171c381bf844 (patch) | |
tree | a013d7f58085a5a89b237172354e011206107eae /drivers/infiniband | |
parent | 400dbc96583ff3b8ad4c09bd7e9dcd35a6215922 (diff) |
IB/core: Export ib_create/destroy_flow through uverbs
Implement ib_uverbs_create_flow() and ib_uverbs_destroy_flow() to
support flow steering for user space applications.
Signed-off-by: Hadar Hen Zion <hadarh@mellanox.com>
Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com>
Signed-off-by: Roland Dreier <roland@purestorage.com>
Diffstat (limited to 'drivers/infiniband')
-rw-r--r-- | drivers/infiniband/core/uverbs.h | 3 | ||||
-rw-r--r-- | drivers/infiniband/core/uverbs_cmd.c | 214 | ||||
-rw-r--r-- | drivers/infiniband/core/uverbs_main.c | 13 |
3 files changed, 229 insertions, 1 deletions
diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h index 0fcd7aa26fa2..ad9d1028102d 100644 --- a/drivers/infiniband/core/uverbs.h +++ b/drivers/infiniband/core/uverbs.h | |||
@@ -155,6 +155,7 @@ extern struct idr ib_uverbs_cq_idr; | |||
155 | extern struct idr ib_uverbs_qp_idr; | 155 | extern struct idr ib_uverbs_qp_idr; |
156 | extern struct idr ib_uverbs_srq_idr; | 156 | extern struct idr ib_uverbs_srq_idr; |
157 | extern struct idr ib_uverbs_xrcd_idr; | 157 | extern struct idr ib_uverbs_xrcd_idr; |
158 | extern struct idr ib_uverbs_rule_idr; | ||
158 | 159 | ||
159 | void idr_remove_uobj(struct idr *idp, struct ib_uobject *uobj); | 160 | void idr_remove_uobj(struct idr *idp, struct ib_uobject *uobj); |
160 | 161 | ||
@@ -215,5 +216,7 @@ IB_UVERBS_DECLARE_CMD(destroy_srq); | |||
215 | IB_UVERBS_DECLARE_CMD(create_xsrq); | 216 | IB_UVERBS_DECLARE_CMD(create_xsrq); |
216 | IB_UVERBS_DECLARE_CMD(open_xrcd); | 217 | IB_UVERBS_DECLARE_CMD(open_xrcd); |
217 | IB_UVERBS_DECLARE_CMD(close_xrcd); | 218 | IB_UVERBS_DECLARE_CMD(close_xrcd); |
219 | IB_UVERBS_DECLARE_CMD(create_flow); | ||
220 | IB_UVERBS_DECLARE_CMD(destroy_flow); | ||
218 | 221 | ||
219 | #endif /* UVERBS_H */ | 222 | #endif /* UVERBS_H */ |
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index b3c07b0c9f26..6e98df929e29 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c | |||
@@ -54,6 +54,7 @@ static struct uverbs_lock_class qp_lock_class = { .name = "QP-uobj" }; | |||
54 | static struct uverbs_lock_class ah_lock_class = { .name = "AH-uobj" }; | 54 | static struct uverbs_lock_class ah_lock_class = { .name = "AH-uobj" }; |
55 | static struct uverbs_lock_class srq_lock_class = { .name = "SRQ-uobj" }; | 55 | static struct uverbs_lock_class srq_lock_class = { .name = "SRQ-uobj" }; |
56 | static struct uverbs_lock_class xrcd_lock_class = { .name = "XRCD-uobj" }; | 56 | static struct uverbs_lock_class xrcd_lock_class = { .name = "XRCD-uobj" }; |
57 | static struct uverbs_lock_class rule_lock_class = { .name = "RULE-uobj" }; | ||
57 | 58 | ||
58 | #define INIT_UDATA(udata, ibuf, obuf, ilen, olen) \ | 59 | #define INIT_UDATA(udata, ibuf, obuf, ilen, olen) \ |
59 | do { \ | 60 | do { \ |
@@ -330,6 +331,7 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file, | |||
330 | INIT_LIST_HEAD(&ucontext->srq_list); | 331 | INIT_LIST_HEAD(&ucontext->srq_list); |
331 | INIT_LIST_HEAD(&ucontext->ah_list); | 332 | INIT_LIST_HEAD(&ucontext->ah_list); |
332 | INIT_LIST_HEAD(&ucontext->xrcd_list); | 333 | INIT_LIST_HEAD(&ucontext->xrcd_list); |
334 | INIT_LIST_HEAD(&ucontext->rule_list); | ||
333 | ucontext->closing = 0; | 335 | ucontext->closing = 0; |
334 | 336 | ||
335 | resp.num_comp_vectors = file->device->num_comp_vectors; | 337 | resp.num_comp_vectors = file->device->num_comp_vectors; |
@@ -2587,6 +2589,218 @@ out_put: | |||
2587 | return ret ? ret : in_len; | 2589 | return ret ? ret : in_len; |
2588 | } | 2590 | } |
2589 | 2591 | ||
2592 | static int kern_spec_to_ib_spec(struct ib_kern_spec *kern_spec, | ||
2593 | union ib_flow_spec *ib_spec) | ||
2594 | { | ||
2595 | ib_spec->type = kern_spec->type; | ||
2596 | |||
2597 | switch (ib_spec->type) { | ||
2598 | case IB_FLOW_SPEC_ETH: | ||
2599 | ib_spec->eth.size = sizeof(struct ib_flow_spec_eth); | ||
2600 | if (ib_spec->eth.size != kern_spec->eth.size) | ||
2601 | return -EINVAL; | ||
2602 | memcpy(&ib_spec->eth.val, &kern_spec->eth.val, | ||
2603 | sizeof(struct ib_flow_eth_filter)); | ||
2604 | memcpy(&ib_spec->eth.mask, &kern_spec->eth.mask, | ||
2605 | sizeof(struct ib_flow_eth_filter)); | ||
2606 | break; | ||
2607 | case IB_FLOW_SPEC_IPV4: | ||
2608 | ib_spec->ipv4.size = sizeof(struct ib_flow_spec_ipv4); | ||
2609 | if (ib_spec->ipv4.size != kern_spec->ipv4.size) | ||
2610 | return -EINVAL; | ||
2611 | memcpy(&ib_spec->ipv4.val, &kern_spec->ipv4.val, | ||
2612 | sizeof(struct ib_flow_ipv4_filter)); | ||
2613 | memcpy(&ib_spec->ipv4.mask, &kern_spec->ipv4.mask, | ||
2614 | sizeof(struct ib_flow_ipv4_filter)); | ||
2615 | break; | ||
2616 | case IB_FLOW_SPEC_TCP: | ||
2617 | case IB_FLOW_SPEC_UDP: | ||
2618 | ib_spec->tcp_udp.size = sizeof(struct ib_flow_spec_tcp_udp); | ||
2619 | if (ib_spec->tcp_udp.size != kern_spec->tcp_udp.size) | ||
2620 | return -EINVAL; | ||
2621 | memcpy(&ib_spec->tcp_udp.val, &kern_spec->tcp_udp.val, | ||
2622 | sizeof(struct ib_flow_tcp_udp_filter)); | ||
2623 | memcpy(&ib_spec->tcp_udp.mask, &kern_spec->tcp_udp.mask, | ||
2624 | sizeof(struct ib_flow_tcp_udp_filter)); | ||
2625 | break; | ||
2626 | default: | ||
2627 | return -EINVAL; | ||
2628 | } | ||
2629 | return 0; | ||
2630 | } | ||
2631 | |||
2632 | ssize_t ib_uverbs_create_flow(struct ib_uverbs_file *file, | ||
2633 | const char __user *buf, int in_len, | ||
2634 | int out_len) | ||
2635 | { | ||
2636 | struct ib_uverbs_create_flow cmd; | ||
2637 | struct ib_uverbs_create_flow_resp resp; | ||
2638 | struct ib_uobject *uobj; | ||
2639 | struct ib_flow *flow_id; | ||
2640 | struct ib_kern_flow_attr *kern_flow_attr; | ||
2641 | struct ib_flow_attr *flow_attr; | ||
2642 | struct ib_qp *qp; | ||
2643 | int err = 0; | ||
2644 | void *kern_spec; | ||
2645 | void *ib_spec; | ||
2646 | int i; | ||
2647 | int kern_attr_size; | ||
2648 | |||
2649 | if (out_len < sizeof(resp)) | ||
2650 | return -ENOSPC; | ||
2651 | |||
2652 | if (copy_from_user(&cmd, buf, sizeof(cmd))) | ||
2653 | return -EFAULT; | ||
2654 | |||
2655 | if ((cmd.flow_attr.type == IB_FLOW_ATTR_SNIFFER && | ||
2656 | !capable(CAP_NET_ADMIN)) || !capable(CAP_NET_RAW)) | ||
2657 | return -EPERM; | ||
2658 | |||
2659 | if (cmd.flow_attr.num_of_specs) { | ||
2660 | kern_flow_attr = kmalloc(cmd.flow_attr.size, GFP_KERNEL); | ||
2661 | if (!kern_flow_attr) | ||
2662 | return -ENOMEM; | ||
2663 | |||
2664 | memcpy(kern_flow_attr, &cmd.flow_attr, sizeof(*kern_flow_attr)); | ||
2665 | kern_attr_size = cmd.flow_attr.size - sizeof(cmd) - sizeof(struct ib_uverbs_cmd_hdr_ex); | ||
2666 | if (copy_from_user(kern_flow_attr + 1, buf + sizeof(cmd), | ||
2667 | kern_attr_size)) { | ||
2668 | err = -EFAULT; | ||
2669 | goto err_free_attr; | ||
2670 | } | ||
2671 | } else { | ||
2672 | kern_flow_attr = &cmd.flow_attr; | ||
2673 | kern_attr_size = sizeof(cmd.flow_attr); | ||
2674 | } | ||
2675 | |||
2676 | uobj = kmalloc(sizeof(*uobj), GFP_KERNEL); | ||
2677 | if (!uobj) { | ||
2678 | err = -ENOMEM; | ||
2679 | goto err_free_attr; | ||
2680 | } | ||
2681 | init_uobj(uobj, 0, file->ucontext, &rule_lock_class); | ||
2682 | down_write(&uobj->mutex); | ||
2683 | |||
2684 | qp = idr_read_qp(cmd.qp_handle, file->ucontext); | ||
2685 | if (!qp) { | ||
2686 | err = -EINVAL; | ||
2687 | goto err_uobj; | ||
2688 | } | ||
2689 | |||
2690 | flow_attr = kmalloc(cmd.flow_attr.size, GFP_KERNEL); | ||
2691 | if (!flow_attr) { | ||
2692 | err = -ENOMEM; | ||
2693 | goto err_put; | ||
2694 | } | ||
2695 | |||
2696 | flow_attr->type = kern_flow_attr->type; | ||
2697 | flow_attr->priority = kern_flow_attr->priority; | ||
2698 | flow_attr->num_of_specs = kern_flow_attr->num_of_specs; | ||
2699 | flow_attr->port = kern_flow_attr->port; | ||
2700 | flow_attr->flags = kern_flow_attr->flags; | ||
2701 | flow_attr->size = sizeof(*flow_attr); | ||
2702 | |||
2703 | kern_spec = kern_flow_attr + 1; | ||
2704 | ib_spec = flow_attr + 1; | ||
2705 | for (i = 0; i < flow_attr->num_of_specs && kern_attr_size > 0; i++) { | ||
2706 | err = kern_spec_to_ib_spec(kern_spec, ib_spec); | ||
2707 | if (err) | ||
2708 | goto err_free; | ||
2709 | flow_attr->size += | ||
2710 | ((union ib_flow_spec *) ib_spec)->size; | ||
2711 | kern_attr_size -= ((struct ib_kern_spec *) kern_spec)->size; | ||
2712 | kern_spec += ((struct ib_kern_spec *) kern_spec)->size; | ||
2713 | ib_spec += ((union ib_flow_spec *) ib_spec)->size; | ||
2714 | } | ||
2715 | if (kern_attr_size) { | ||
2716 | pr_warn("create flow failed, %d bytes left from uverb cmd\n", | ||
2717 | kern_attr_size); | ||
2718 | goto err_free; | ||
2719 | } | ||
2720 | flow_id = ib_create_flow(qp, flow_attr, IB_FLOW_DOMAIN_USER); | ||
2721 | if (IS_ERR(flow_id)) { | ||
2722 | err = PTR_ERR(flow_id); | ||
2723 | goto err_free; | ||
2724 | } | ||
2725 | flow_id->qp = qp; | ||
2726 | flow_id->uobject = uobj; | ||
2727 | uobj->object = flow_id; | ||
2728 | |||
2729 | err = idr_add_uobj(&ib_uverbs_rule_idr, uobj); | ||
2730 | if (err) | ||
2731 | goto destroy_flow; | ||
2732 | |||
2733 | memset(&resp, 0, sizeof(resp)); | ||
2734 | resp.flow_handle = uobj->id; | ||
2735 | |||
2736 | if (copy_to_user((void __user *)(unsigned long) cmd.response, | ||
2737 | &resp, sizeof(resp))) { | ||
2738 | err = -EFAULT; | ||
2739 | goto err_copy; | ||
2740 | } | ||
2741 | |||
2742 | put_qp_read(qp); | ||
2743 | mutex_lock(&file->mutex); | ||
2744 | list_add_tail(&uobj->list, &file->ucontext->rule_list); | ||
2745 | mutex_unlock(&file->mutex); | ||
2746 | |||
2747 | uobj->live = 1; | ||
2748 | |||
2749 | up_write(&uobj->mutex); | ||
2750 | kfree(flow_attr); | ||
2751 | if (cmd.flow_attr.num_of_specs) | ||
2752 | kfree(kern_flow_attr); | ||
2753 | return in_len; | ||
2754 | err_copy: | ||
2755 | idr_remove_uobj(&ib_uverbs_rule_idr, uobj); | ||
2756 | destroy_flow: | ||
2757 | ib_destroy_flow(flow_id); | ||
2758 | err_free: | ||
2759 | kfree(flow_attr); | ||
2760 | err_put: | ||
2761 | put_qp_read(qp); | ||
2762 | err_uobj: | ||
2763 | put_uobj_write(uobj); | ||
2764 | err_free_attr: | ||
2765 | if (cmd.flow_attr.num_of_specs) | ||
2766 | kfree(kern_flow_attr); | ||
2767 | return err; | ||
2768 | } | ||
2769 | |||
2770 | ssize_t ib_uverbs_destroy_flow(struct ib_uverbs_file *file, | ||
2771 | const char __user *buf, int in_len, | ||
2772 | int out_len) { | ||
2773 | struct ib_uverbs_destroy_flow cmd; | ||
2774 | struct ib_flow *flow_id; | ||
2775 | struct ib_uobject *uobj; | ||
2776 | int ret; | ||
2777 | |||
2778 | if (copy_from_user(&cmd, buf, sizeof(cmd))) | ||
2779 | return -EFAULT; | ||
2780 | |||
2781 | uobj = idr_write_uobj(&ib_uverbs_rule_idr, cmd.flow_handle, | ||
2782 | file->ucontext); | ||
2783 | if (!uobj) | ||
2784 | return -EINVAL; | ||
2785 | flow_id = uobj->object; | ||
2786 | |||
2787 | ret = ib_destroy_flow(flow_id); | ||
2788 | if (!ret) | ||
2789 | uobj->live = 0; | ||
2790 | |||
2791 | put_uobj_write(uobj); | ||
2792 | |||
2793 | idr_remove_uobj(&ib_uverbs_rule_idr, uobj); | ||
2794 | |||
2795 | mutex_lock(&file->mutex); | ||
2796 | list_del(&uobj->list); | ||
2797 | mutex_unlock(&file->mutex); | ||
2798 | |||
2799 | put_uobj(uobj); | ||
2800 | |||
2801 | return ret ? ret : in_len; | ||
2802 | } | ||
2803 | |||
2590 | static int __uverbs_create_xsrq(struct ib_uverbs_file *file, | 2804 | static int __uverbs_create_xsrq(struct ib_uverbs_file *file, |
2591 | struct ib_uverbs_create_xsrq *cmd, | 2805 | struct ib_uverbs_create_xsrq *cmd, |
2592 | struct ib_udata *udata) | 2806 | struct ib_udata *udata) |
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index e4e7b2449d19..75ad86c4abf8 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c | |||
@@ -73,6 +73,7 @@ DEFINE_IDR(ib_uverbs_cq_idr); | |||
73 | DEFINE_IDR(ib_uverbs_qp_idr); | 73 | DEFINE_IDR(ib_uverbs_qp_idr); |
74 | DEFINE_IDR(ib_uverbs_srq_idr); | 74 | DEFINE_IDR(ib_uverbs_srq_idr); |
75 | DEFINE_IDR(ib_uverbs_xrcd_idr); | 75 | DEFINE_IDR(ib_uverbs_xrcd_idr); |
76 | DEFINE_IDR(ib_uverbs_rule_idr); | ||
76 | 77 | ||
77 | static DEFINE_SPINLOCK(map_lock); | 78 | static DEFINE_SPINLOCK(map_lock); |
78 | static DECLARE_BITMAP(dev_map, IB_UVERBS_MAX_DEVICES); | 79 | static DECLARE_BITMAP(dev_map, IB_UVERBS_MAX_DEVICES); |
@@ -113,7 +114,9 @@ static ssize_t (*uverbs_cmd_table[])(struct ib_uverbs_file *file, | |||
113 | [IB_USER_VERBS_CMD_OPEN_XRCD] = ib_uverbs_open_xrcd, | 114 | [IB_USER_VERBS_CMD_OPEN_XRCD] = ib_uverbs_open_xrcd, |
114 | [IB_USER_VERBS_CMD_CLOSE_XRCD] = ib_uverbs_close_xrcd, | 115 | [IB_USER_VERBS_CMD_CLOSE_XRCD] = ib_uverbs_close_xrcd, |
115 | [IB_USER_VERBS_CMD_CREATE_XSRQ] = ib_uverbs_create_xsrq, | 116 | [IB_USER_VERBS_CMD_CREATE_XSRQ] = ib_uverbs_create_xsrq, |
116 | [IB_USER_VERBS_CMD_OPEN_QP] = ib_uverbs_open_qp | 117 | [IB_USER_VERBS_CMD_OPEN_QP] = ib_uverbs_open_qp, |
118 | [IB_USER_VERBS_CMD_CREATE_FLOW] = ib_uverbs_create_flow, | ||
119 | [IB_USER_VERBS_CMD_DESTROY_FLOW] = ib_uverbs_destroy_flow | ||
117 | }; | 120 | }; |
118 | 121 | ||
119 | static void ib_uverbs_add_one(struct ib_device *device); | 122 | static void ib_uverbs_add_one(struct ib_device *device); |
@@ -212,6 +215,14 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file, | |||
212 | kfree(uobj); | 215 | kfree(uobj); |
213 | } | 216 | } |
214 | 217 | ||
218 | list_for_each_entry_safe(uobj, tmp, &context->rule_list, list) { | ||
219 | struct ib_flow *flow_id = uobj->object; | ||
220 | |||
221 | idr_remove_uobj(&ib_uverbs_rule_idr, uobj); | ||
222 | ib_destroy_flow(flow_id); | ||
223 | kfree(uobj); | ||
224 | } | ||
225 | |||
215 | list_for_each_entry_safe(uobj, tmp, &context->qp_list, list) { | 226 | list_for_each_entry_safe(uobj, tmp, &context->qp_list, list) { |
216 | struct ib_qp *qp = uobj->object; | 227 | struct ib_qp *qp = uobj->object; |
217 | struct ib_uqp_object *uqp = | 228 | struct ib_uqp_object *uqp = |