diff options
author | Vipul Pandya <vipul@chelsio.com> | 2013-07-04 06:40:46 -0400 |
---|---|---|
committer | Roland Dreier <roland@purestorage.com> | 2013-08-12 15:33:37 -0400 |
commit | 01bcca68c38300745ea8e2d4e9513111406fd923 (patch) | |
tree | 691143608d5203d7475c4c92f493f8af93a1b5be /drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | |
parent | 80f40c1f7a722d754a73027931a80746420c7c89 (diff) |
cxgb4: Add CLIP support to store compressed IPv6 address
The Compressed LIP region is used to hold a limited number of Local
IPv6 addresses. This region is primarily used to reduce the TCAM
space consumed for an IPv6 offloaded connection. A 128-bit LIP will
be reduced to 13-bit and stored in the TCAM if there is a match
between the IPv6 tuple's LIP and the one stored in the CLIP region.
Signed-off-by: Vipul Pandya <vipul@chelsio.com>
Signed-off-by: Roland Dreier <roland@purestorage.com>
Diffstat (limited to 'drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c')
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index d1d6ff722dc9..038df4b96139 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | |||
@@ -60,6 +60,7 @@ | |||
60 | #include <linux/workqueue.h> | 60 | #include <linux/workqueue.h> |
61 | #include <net/neighbour.h> | 61 | #include <net/neighbour.h> |
62 | #include <net/netevent.h> | 62 | #include <net/netevent.h> |
63 | #include <net/addrconf.h> | ||
63 | #include <asm/uaccess.h> | 64 | #include <asm/uaccess.h> |
64 | 65 | ||
65 | #include "cxgb4.h" | 66 | #include "cxgb4.h" |
@@ -68,6 +69,11 @@ | |||
68 | #include "t4fw_api.h" | 69 | #include "t4fw_api.h" |
69 | #include "l2t.h" | 70 | #include "l2t.h" |
70 | 71 | ||
72 | #include <../drivers/net/bonding/bonding.h> | ||
73 | |||
74 | #ifdef DRV_VERSION | ||
75 | #undef DRV_VERSION | ||
76 | #endif | ||
71 | #define DRV_VERSION "2.0.0-ko" | 77 | #define DRV_VERSION "2.0.0-ko" |
72 | #define DRV_DESC "Chelsio T4/T5 Network Driver" | 78 | #define DRV_DESC "Chelsio T4/T5 Network Driver" |
73 | 79 | ||
@@ -400,6 +406,9 @@ static struct dentry *cxgb4_debugfs_root; | |||
400 | 406 | ||
401 | static LIST_HEAD(adapter_list); | 407 | static LIST_HEAD(adapter_list); |
402 | static DEFINE_MUTEX(uld_mutex); | 408 | static DEFINE_MUTEX(uld_mutex); |
409 | /* Adapter list to be accessed from atomic context */ | ||
410 | static LIST_HEAD(adap_rcu_list); | ||
411 | static DEFINE_SPINLOCK(adap_rcu_lock); | ||
403 | static struct cxgb4_uld_info ulds[CXGB4_ULD_MAX]; | 412 | static struct cxgb4_uld_info ulds[CXGB4_ULD_MAX]; |
404 | static const char *uld_str[] = { "RDMA", "iSCSI" }; | 413 | static const char *uld_str[] = { "RDMA", "iSCSI" }; |
405 | 414 | ||
@@ -3227,6 +3236,38 @@ static int tid_init(struct tid_info *t) | |||
3227 | return 0; | 3236 | return 0; |
3228 | } | 3237 | } |
3229 | 3238 | ||
3239 | static int cxgb4_clip_get(const struct net_device *dev, | ||
3240 | const struct in6_addr *lip) | ||
3241 | { | ||
3242 | struct adapter *adap; | ||
3243 | struct fw_clip_cmd c; | ||
3244 | |||
3245 | adap = netdev2adap(dev); | ||
3246 | memset(&c, 0, sizeof(c)); | ||
3247 | c.op_to_write = htonl(FW_CMD_OP(FW_CLIP_CMD) | | ||
3248 | FW_CMD_REQUEST | FW_CMD_WRITE); | ||
3249 | c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_ALLOC | FW_LEN16(c)); | ||
3250 | *(__be64 *)&c.ip_hi = *(__be64 *)(lip->s6_addr); | ||
3251 | *(__be64 *)&c.ip_lo = *(__be64 *)(lip->s6_addr + 8); | ||
3252 | return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false); | ||
3253 | } | ||
3254 | |||
3255 | static int cxgb4_clip_release(const struct net_device *dev, | ||
3256 | const struct in6_addr *lip) | ||
3257 | { | ||
3258 | struct adapter *adap; | ||
3259 | struct fw_clip_cmd c; | ||
3260 | |||
3261 | adap = netdev2adap(dev); | ||
3262 | memset(&c, 0, sizeof(c)); | ||
3263 | c.op_to_write = htonl(FW_CMD_OP(FW_CLIP_CMD) | | ||
3264 | FW_CMD_REQUEST | FW_CMD_READ); | ||
3265 | c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_FREE | FW_LEN16(c)); | ||
3266 | *(__be64 *)&c.ip_hi = *(__be64 *)(lip->s6_addr); | ||
3267 | *(__be64 *)&c.ip_lo = *(__be64 *)(lip->s6_addr + 8); | ||
3268 | return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false); | ||
3269 | } | ||
3270 | |||
3230 | /** | 3271 | /** |
3231 | * cxgb4_create_server - create an IP server | 3272 | * cxgb4_create_server - create an IP server |
3232 | * @dev: the device | 3273 | * @dev: the device |
@@ -3790,6 +3831,10 @@ static void attach_ulds(struct adapter *adap) | |||
3790 | { | 3831 | { |
3791 | unsigned int i; | 3832 | unsigned int i; |
3792 | 3833 | ||
3834 | spin_lock(&adap_rcu_lock); | ||
3835 | list_add_tail_rcu(&adap->rcu_node, &adap_rcu_list); | ||
3836 | spin_unlock(&adap_rcu_lock); | ||
3837 | |||
3793 | mutex_lock(&uld_mutex); | 3838 | mutex_lock(&uld_mutex); |
3794 | list_add_tail(&adap->list_node, &adapter_list); | 3839 | list_add_tail(&adap->list_node, &adapter_list); |
3795 | for (i = 0; i < CXGB4_ULD_MAX; i++) | 3840 | for (i = 0; i < CXGB4_ULD_MAX; i++) |
@@ -3815,6 +3860,10 @@ static void detach_ulds(struct adapter *adap) | |||
3815 | netevent_registered = false; | 3860 | netevent_registered = false; |
3816 | } | 3861 | } |
3817 | mutex_unlock(&uld_mutex); | 3862 | mutex_unlock(&uld_mutex); |
3863 | |||
3864 | spin_lock(&adap_rcu_lock); | ||
3865 | list_del_rcu(&adap->rcu_node); | ||
3866 | spin_unlock(&adap_rcu_lock); | ||
3818 | } | 3867 | } |
3819 | 3868 | ||
3820 | static void notify_ulds(struct adapter *adap, enum cxgb4_state new_state) | 3869 | static void notify_ulds(struct adapter *adap, enum cxgb4_state new_state) |
@@ -3878,6 +3927,169 @@ int cxgb4_unregister_uld(enum cxgb4_uld type) | |||
3878 | } | 3927 | } |
3879 | EXPORT_SYMBOL(cxgb4_unregister_uld); | 3928 | EXPORT_SYMBOL(cxgb4_unregister_uld); |
3880 | 3929 | ||
3930 | /* Check if netdev on which event is occured belongs to us or not. Return | ||
3931 | * suceess (1) if it belongs otherwise failure (0). | ||
3932 | */ | ||
3933 | static int cxgb4_netdev(struct net_device *netdev) | ||
3934 | { | ||
3935 | struct adapter *adap; | ||
3936 | int i; | ||
3937 | |||
3938 | spin_lock(&adap_rcu_lock); | ||
3939 | list_for_each_entry_rcu(adap, &adap_rcu_list, rcu_node) | ||
3940 | for (i = 0; i < MAX_NPORTS; i++) | ||
3941 | if (adap->port[i] == netdev) { | ||
3942 | spin_unlock(&adap_rcu_lock); | ||
3943 | return 1; | ||
3944 | } | ||
3945 | spin_unlock(&adap_rcu_lock); | ||
3946 | return 0; | ||
3947 | } | ||
3948 | |||
3949 | static int clip_add(struct net_device *event_dev, struct inet6_ifaddr *ifa, | ||
3950 | unsigned long event) | ||
3951 | { | ||
3952 | int ret = NOTIFY_DONE; | ||
3953 | |||
3954 | rcu_read_lock(); | ||
3955 | if (cxgb4_netdev(event_dev)) { | ||
3956 | switch (event) { | ||
3957 | case NETDEV_UP: | ||
3958 | ret = cxgb4_clip_get(event_dev, | ||
3959 | (const struct in6_addr *)ifa->addr.s6_addr); | ||
3960 | if (ret < 0) { | ||
3961 | rcu_read_unlock(); | ||
3962 | return ret; | ||
3963 | } | ||
3964 | ret = NOTIFY_OK; | ||
3965 | break; | ||
3966 | case NETDEV_DOWN: | ||
3967 | cxgb4_clip_release(event_dev, | ||
3968 | (const struct in6_addr *)ifa->addr.s6_addr); | ||
3969 | ret = NOTIFY_OK; | ||
3970 | break; | ||
3971 | default: | ||
3972 | break; | ||
3973 | } | ||
3974 | } | ||
3975 | rcu_read_unlock(); | ||
3976 | return ret; | ||
3977 | } | ||
3978 | |||
3979 | static int cxgb4_inet6addr_handler(struct notifier_block *this, | ||
3980 | unsigned long event, void *data) | ||
3981 | { | ||
3982 | struct inet6_ifaddr *ifa = data; | ||
3983 | struct net_device *event_dev; | ||
3984 | int ret = NOTIFY_DONE; | ||
3985 | int cnt; | ||
3986 | struct bonding *bond = netdev_priv(ifa->idev->dev); | ||
3987 | struct slave *slave; | ||
3988 | struct pci_dev *first_pdev = NULL; | ||
3989 | |||
3990 | if (ifa->idev->dev->priv_flags & IFF_802_1Q_VLAN) { | ||
3991 | event_dev = vlan_dev_real_dev(ifa->idev->dev); | ||
3992 | ret = clip_add(event_dev, ifa, event); | ||
3993 | } else if (ifa->idev->dev->flags & IFF_MASTER) { | ||
3994 | /* It is possible that two different adapters are bonded in one | ||
3995 | * bond. We need to find such different adapters and add clip | ||
3996 | * in all of them only once. | ||
3997 | */ | ||
3998 | read_lock(&bond->lock); | ||
3999 | bond_for_each_slave(bond, slave, cnt) { | ||
4000 | if (!first_pdev) { | ||
4001 | ret = clip_add(slave->dev, ifa, event); | ||
4002 | /* If clip_add is success then only initialize | ||
4003 | * first_pdev since it means it is our device | ||
4004 | */ | ||
4005 | if (ret == NOTIFY_OK) | ||
4006 | first_pdev = to_pci_dev( | ||
4007 | slave->dev->dev.parent); | ||
4008 | } else if (first_pdev != | ||
4009 | to_pci_dev(slave->dev->dev.parent)) | ||
4010 | ret = clip_add(slave->dev, ifa, event); | ||
4011 | } | ||
4012 | read_unlock(&bond->lock); | ||
4013 | } else | ||
4014 | ret = clip_add(ifa->idev->dev, ifa, event); | ||
4015 | |||
4016 | return ret; | ||
4017 | } | ||
4018 | |||
4019 | static struct notifier_block cxgb4_inet6addr_notifier = { | ||
4020 | .notifier_call = cxgb4_inet6addr_handler | ||
4021 | }; | ||
4022 | |||
4023 | /* Retrieves IPv6 addresses from a root device (bond, vlan) associated with | ||
4024 | * a physical device. | ||
4025 | * The physical device reference is needed to send the actul CLIP command. | ||
4026 | */ | ||
4027 | static int update_dev_clip(struct net_device *root_dev, struct net_device *dev) | ||
4028 | { | ||
4029 | struct inet6_dev *idev = NULL; | ||
4030 | struct inet6_ifaddr *ifa; | ||
4031 | int ret = 0; | ||
4032 | |||
4033 | idev = __in6_dev_get(root_dev); | ||
4034 | if (!idev) | ||
4035 | return ret; | ||
4036 | |||
4037 | read_lock_bh(&idev->lock); | ||
4038 | list_for_each_entry(ifa, &idev->addr_list, if_list) { | ||
4039 | ret = cxgb4_clip_get(dev, | ||
4040 | (const struct in6_addr *)ifa->addr.s6_addr); | ||
4041 | if (ret < 0) | ||
4042 | break; | ||
4043 | } | ||
4044 | read_unlock_bh(&idev->lock); | ||
4045 | |||
4046 | return ret; | ||
4047 | } | ||
4048 | |||
4049 | static int update_root_dev_clip(struct net_device *dev) | ||
4050 | { | ||
4051 | struct net_device *root_dev = NULL; | ||
4052 | int i, ret = 0; | ||
4053 | |||
4054 | /* First populate the real net device's IPv6 addresses */ | ||
4055 | ret = update_dev_clip(dev, dev); | ||
4056 | if (ret) | ||
4057 | return ret; | ||
4058 | |||
4059 | /* Parse all bond and vlan devices layered on top of the physical dev */ | ||
4060 | for (i = 0; i < VLAN_N_VID; i++) { | ||
4061 | root_dev = __vlan_find_dev_deep(dev, htons(ETH_P_8021Q), i); | ||
4062 | if (!root_dev) | ||
4063 | continue; | ||
4064 | |||
4065 | ret = update_dev_clip(root_dev, dev); | ||
4066 | if (ret) | ||
4067 | break; | ||
4068 | } | ||
4069 | return ret; | ||
4070 | } | ||
4071 | |||
4072 | static void update_clip(const struct adapter *adap) | ||
4073 | { | ||
4074 | int i; | ||
4075 | struct net_device *dev; | ||
4076 | int ret; | ||
4077 | |||
4078 | rcu_read_lock(); | ||
4079 | |||
4080 | for (i = 0; i < MAX_NPORTS; i++) { | ||
4081 | dev = adap->port[i]; | ||
4082 | ret = 0; | ||
4083 | |||
4084 | if (dev) | ||
4085 | ret = update_root_dev_clip(dev); | ||
4086 | |||
4087 | if (ret < 0) | ||
4088 | break; | ||
4089 | } | ||
4090 | rcu_read_unlock(); | ||
4091 | } | ||
4092 | |||
3881 | /** | 4093 | /** |
3882 | * cxgb_up - enable the adapter | 4094 | * cxgb_up - enable the adapter |
3883 | * @adap: adapter being enabled | 4095 | * @adap: adapter being enabled |
@@ -3923,6 +4135,7 @@ static int cxgb_up(struct adapter *adap) | |||
3923 | t4_intr_enable(adap); | 4135 | t4_intr_enable(adap); |
3924 | adap->flags |= FULL_INIT_DONE; | 4136 | adap->flags |= FULL_INIT_DONE; |
3925 | notify_ulds(adap, CXGB4_STATE_UP); | 4137 | notify_ulds(adap, CXGB4_STATE_UP); |
4138 | update_clip(adap); | ||
3926 | out: | 4139 | out: |
3927 | return err; | 4140 | return err; |
3928 | irq_err: | 4141 | irq_err: |
@@ -5939,11 +6152,15 @@ static int __init cxgb4_init_module(void) | |||
5939 | ret = pci_register_driver(&cxgb4_driver); | 6152 | ret = pci_register_driver(&cxgb4_driver); |
5940 | if (ret < 0) | 6153 | if (ret < 0) |
5941 | debugfs_remove(cxgb4_debugfs_root); | 6154 | debugfs_remove(cxgb4_debugfs_root); |
6155 | |||
6156 | register_inet6addr_notifier(&cxgb4_inet6addr_notifier); | ||
6157 | |||
5942 | return ret; | 6158 | return ret; |
5943 | } | 6159 | } |
5944 | 6160 | ||
5945 | static void __exit cxgb4_cleanup_module(void) | 6161 | static void __exit cxgb4_cleanup_module(void) |
5946 | { | 6162 | { |
6163 | unregister_inet6addr_notifier(&cxgb4_inet6addr_notifier); | ||
5947 | pci_unregister_driver(&cxgb4_driver); | 6164 | pci_unregister_driver(&cxgb4_driver); |
5948 | debugfs_remove(cxgb4_debugfs_root); /* NULL ok */ | 6165 | debugfs_remove(cxgb4_debugfs_root); /* NULL ok */ |
5949 | flush_workqueue(workq); | 6166 | flush_workqueue(workq); |