diff options
author | Govindarajulu Varadarajan <_govind@gmx.com> | 2014-06-23 06:38:00 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-06-23 17:32:19 -0400 |
commit | 631185273b6e1f8e0b5a00c1aca08650b2d18a57 (patch) | |
tree | 4ac92e09234d69b33ff4826af4eed9f94cc30dc5 /drivers | |
parent | 10cc88446cec4eee8e2efab24ad387d52ef1f4fb (diff) |
enic: devcmd for adding IP 5 tuple hardware filters
This patch adds interface to add and delete IP 5 tuple filter. This interface
is used by Accelerated RFS code to steer a flow to corresponding receive
queue.
As of now adaptor supports only ipv4 + tcp/udp packet steering.
Signed-off-by: Govindarajulu Varadarajan <_govind@gmx.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/ethernet/cisco/enic/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/cisco/enic/enic_clsf.c | 66 | ||||
-rw-r--r-- | drivers/net/ethernet/cisco/enic/enic_clsf.h | 10 | ||||
-rw-r--r-- | drivers/net/ethernet/cisco/enic/vnic_dev.c | 61 | ||||
-rw-r--r-- | drivers/net/ethernet/cisco/enic/vnic_dev.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/cisco/enic/vnic_devcmd.h | 5 |
6 files changed, 145 insertions, 1 deletions
diff --git a/drivers/net/ethernet/cisco/enic/Makefile b/drivers/net/ethernet/cisco/enic/Makefile index 239e1e46545d..aadcaf7876ce 100644 --- a/drivers/net/ethernet/cisco/enic/Makefile +++ b/drivers/net/ethernet/cisco/enic/Makefile | |||
@@ -2,5 +2,5 @@ obj-$(CONFIG_ENIC) := enic.o | |||
2 | 2 | ||
3 | enic-y := enic_main.o vnic_cq.o vnic_intr.o vnic_wq.o \ | 3 | enic-y := enic_main.o vnic_cq.o vnic_intr.o vnic_wq.o \ |
4 | enic_res.o enic_dev.o enic_pp.o vnic_dev.o vnic_rq.o vnic_vic.o \ | 4 | enic_res.o enic_dev.o enic_pp.o vnic_dev.o vnic_rq.o vnic_vic.o \ |
5 | enic_ethtool.o enic_api.o | 5 | enic_ethtool.o enic_api.o enic_clsf.o |
6 | 6 | ||
diff --git a/drivers/net/ethernet/cisco/enic/enic_clsf.c b/drivers/net/ethernet/cisco/enic/enic_clsf.c new file mode 100644 index 000000000000..f6703c4f76a9 --- /dev/null +++ b/drivers/net/ethernet/cisco/enic/enic_clsf.c | |||
@@ -0,0 +1,66 @@ | |||
1 | #include <linux/if.h> | ||
2 | #include <linux/if_ether.h> | ||
3 | #include <linux/if_link.h> | ||
4 | #include <linux/netdevice.h> | ||
5 | #include <linux/in.h> | ||
6 | #include <linux/types.h> | ||
7 | #include <linux/skbuff.h> | ||
8 | #include <net/flow_keys.h> | ||
9 | #include "enic_res.h" | ||
10 | #include "enic_clsf.h" | ||
11 | |||
12 | /* enic_addfltr_5t - Add ipv4 5tuple filter | ||
13 | * @enic: enic struct of vnic | ||
14 | * @keys: flow_keys of ipv4 5tuple | ||
15 | * @rq: rq number to steer to | ||
16 | * | ||
17 | * This function returns filter_id(hardware_id) of the filter | ||
18 | * added. In case of error it returns an negative number. | ||
19 | */ | ||
20 | int enic_addfltr_5t(struct enic *enic, struct flow_keys *keys, u16 rq) | ||
21 | { | ||
22 | int res; | ||
23 | struct filter data; | ||
24 | |||
25 | switch (keys->ip_proto) { | ||
26 | case IPPROTO_TCP: | ||
27 | data.u.ipv4.protocol = PROTO_TCP; | ||
28 | break; | ||
29 | case IPPROTO_UDP: | ||
30 | data.u.ipv4.protocol = PROTO_UDP; | ||
31 | break; | ||
32 | default: | ||
33 | return -EPROTONOSUPPORT; | ||
34 | }; | ||
35 | data.type = FILTER_IPV4_5TUPLE; | ||
36 | data.u.ipv4.src_addr = ntohl(keys->src); | ||
37 | data.u.ipv4.dst_addr = ntohl(keys->dst); | ||
38 | data.u.ipv4.src_port = ntohs(keys->port16[0]); | ||
39 | data.u.ipv4.dst_port = ntohs(keys->port16[1]); | ||
40 | data.u.ipv4.flags = FILTER_FIELDS_IPV4_5TUPLE; | ||
41 | |||
42 | spin_lock_bh(&enic->devcmd_lock); | ||
43 | res = vnic_dev_classifier(enic->vdev, CLSF_ADD, &rq, &data); | ||
44 | spin_unlock_bh(&enic->devcmd_lock); | ||
45 | res = (res == 0) ? rq : res; | ||
46 | |||
47 | return res; | ||
48 | } | ||
49 | |||
50 | /* enic_delfltr - Delete clsf filter | ||
51 | * @enic: enic struct of vnic | ||
52 | * @filter_id: filter_is(hardware_id) of filter to be deleted | ||
53 | * | ||
54 | * This function returns zero in case of success, negative number incase of | ||
55 | * error. | ||
56 | */ | ||
57 | int enic_delfltr(struct enic *enic, u16 filter_id) | ||
58 | { | ||
59 | int ret; | ||
60 | |||
61 | spin_lock_bh(&enic->devcmd_lock); | ||
62 | ret = vnic_dev_classifier(enic->vdev, CLSF_DEL, &filter_id, NULL); | ||
63 | spin_unlock_bh(&enic->devcmd_lock); | ||
64 | |||
65 | return ret; | ||
66 | } | ||
diff --git a/drivers/net/ethernet/cisco/enic/enic_clsf.h b/drivers/net/ethernet/cisco/enic/enic_clsf.h new file mode 100644 index 000000000000..b6925b368b77 --- /dev/null +++ b/drivers/net/ethernet/cisco/enic/enic_clsf.h | |||
@@ -0,0 +1,10 @@ | |||
1 | #ifndef _ENIC_CLSF_H_ | ||
2 | #define _ENIC_CLSF_H_ | ||
3 | |||
4 | #include "vnic_dev.h" | ||
5 | #include "enic.h" | ||
6 | |||
7 | int enic_addfltr_5t(struct enic *enic, struct flow_keys *keys, u16 rq); | ||
8 | int enic_delfltr(struct enic *enic, u16 filter_id); | ||
9 | |||
10 | #endif /* _ENIC_CLSF_H_ */ | ||
diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.c b/drivers/net/ethernet/cisco/enic/vnic_dev.c index 263081b8e636..5abc496bcf29 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_dev.c +++ b/drivers/net/ethernet/cisco/enic/vnic_dev.c | |||
@@ -1048,3 +1048,64 @@ int vnic_dev_set_mac_addr(struct vnic_dev *vdev, u8 *mac_addr) | |||
1048 | 1048 | ||
1049 | return vnic_dev_cmd(vdev, CMD_SET_MAC_ADDR, &a0, &a1, wait); | 1049 | return vnic_dev_cmd(vdev, CMD_SET_MAC_ADDR, &a0, &a1, wait); |
1050 | } | 1050 | } |
1051 | |||
1052 | /* vnic_dev_classifier: Add/Delete classifier entries | ||
1053 | * @vdev: vdev of the device | ||
1054 | * @cmd: CLSF_ADD for Add filter | ||
1055 | * CLSF_DEL for Delete filter | ||
1056 | * @entry: In case of ADD filter, the caller passes the RQ number in this | ||
1057 | * variable. | ||
1058 | * | ||
1059 | * This function stores the filter_id returned by the firmware in the | ||
1060 | * same variable before return; | ||
1061 | * | ||
1062 | * In case of DEL filter, the caller passes the RQ number. Return | ||
1063 | * value is irrelevant. | ||
1064 | * @data: filter data | ||
1065 | */ | ||
1066 | int vnic_dev_classifier(struct vnic_dev *vdev, u8 cmd, u16 *entry, | ||
1067 | struct filter *data) | ||
1068 | { | ||
1069 | u64 a0, a1; | ||
1070 | int wait = 1000; | ||
1071 | dma_addr_t tlv_pa; | ||
1072 | int ret = -EINVAL; | ||
1073 | struct filter_tlv *tlv, *tlv_va; | ||
1074 | struct filter_action *action; | ||
1075 | u64 tlv_size; | ||
1076 | |||
1077 | if (cmd == CLSF_ADD) { | ||
1078 | tlv_size = sizeof(struct filter) + | ||
1079 | sizeof(struct filter_action) + | ||
1080 | 2 * sizeof(struct filter_tlv); | ||
1081 | tlv_va = pci_alloc_consistent(vdev->pdev, tlv_size, &tlv_pa); | ||
1082 | if (!tlv_va) | ||
1083 | return -ENOMEM; | ||
1084 | tlv = tlv_va; | ||
1085 | a0 = tlv_pa; | ||
1086 | a1 = tlv_size; | ||
1087 | memset(tlv, 0, tlv_size); | ||
1088 | tlv->type = CLSF_TLV_FILTER; | ||
1089 | tlv->length = sizeof(struct filter); | ||
1090 | *(struct filter *)&tlv->val = *data; | ||
1091 | |||
1092 | tlv = (struct filter_tlv *)((char *)tlv + | ||
1093 | sizeof(struct filter_tlv) + | ||
1094 | sizeof(struct filter)); | ||
1095 | |||
1096 | tlv->type = CLSF_TLV_ACTION; | ||
1097 | tlv->length = sizeof(struct filter_action); | ||
1098 | action = (struct filter_action *)&tlv->val; | ||
1099 | action->type = FILTER_ACTION_RQ_STEERING; | ||
1100 | action->u.rq_idx = *entry; | ||
1101 | |||
1102 | ret = vnic_dev_cmd(vdev, CMD_ADD_FILTER, &a0, &a1, wait); | ||
1103 | *entry = (u16)a0; | ||
1104 | pci_free_consistent(vdev->pdev, tlv_size, tlv_va, tlv_pa); | ||
1105 | } else if (cmd == CLSF_DEL) { | ||
1106 | a0 = *entry; | ||
1107 | ret = vnic_dev_cmd(vdev, CMD_DEL_FILTER, &a0, &a1, wait); | ||
1108 | } | ||
1109 | |||
1110 | return ret; | ||
1111 | } | ||
diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.h b/drivers/net/ethernet/cisco/enic/vnic_dev.h index 1f3b301f8225..1fb214efceba 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_dev.h +++ b/drivers/net/ethernet/cisco/enic/vnic_dev.h | |||
@@ -133,5 +133,7 @@ int vnic_dev_enable2(struct vnic_dev *vdev, int active); | |||
133 | int vnic_dev_enable2_done(struct vnic_dev *vdev, int *status); | 133 | int vnic_dev_enable2_done(struct vnic_dev *vdev, int *status); |
134 | int vnic_dev_deinit_done(struct vnic_dev *vdev, int *status); | 134 | int vnic_dev_deinit_done(struct vnic_dev *vdev, int *status); |
135 | int vnic_dev_set_mac_addr(struct vnic_dev *vdev, u8 *mac_addr); | 135 | int vnic_dev_set_mac_addr(struct vnic_dev *vdev, u8 *mac_addr); |
136 | int vnic_dev_classifier(struct vnic_dev *vdev, u8 cmd, u16 *entry, | ||
137 | struct filter *data); | ||
136 | 138 | ||
137 | #endif /* _VNIC_DEV_H_ */ | 139 | #endif /* _VNIC_DEV_H_ */ |
diff --git a/drivers/net/ethernet/cisco/enic/vnic_devcmd.h b/drivers/net/ethernet/cisco/enic/vnic_devcmd.h index b9a0d78fd639..435d0cd96c22 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_devcmd.h +++ b/drivers/net/ethernet/cisco/enic/vnic_devcmd.h | |||
@@ -603,6 +603,11 @@ struct filter_tlv { | |||
603 | u_int32_t val[0]; | 603 | u_int32_t val[0]; |
604 | }; | 604 | }; |
605 | 605 | ||
606 | enum { | ||
607 | CLSF_ADD = 0, | ||
608 | CLSF_DEL = 1, | ||
609 | }; | ||
610 | |||
606 | /* | 611 | /* |
607 | * Writing cmd register causes STAT_BUSY to get set in status register. | 612 | * Writing cmd register causes STAT_BUSY to get set in status register. |
608 | * When cmd completes, STAT_BUSY will be cleared. | 613 | * When cmd completes, STAT_BUSY will be cleared. |