aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
diff options
context:
space:
mode:
authorVipul Pandya <vipul@chelsio.com>2013-07-04 06:40:46 -0400
committerRoland Dreier <roland@purestorage.com>2013-08-12 15:33:37 -0400
commit01bcca68c38300745ea8e2d4e9513111406fd923 (patch)
tree691143608d5203d7475c4c92f493f8af93a1b5be /drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
parent80f40c1f7a722d754a73027931a80746420c7c89 (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.c217
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
401static LIST_HEAD(adapter_list); 407static LIST_HEAD(adapter_list);
402static DEFINE_MUTEX(uld_mutex); 408static DEFINE_MUTEX(uld_mutex);
409/* Adapter list to be accessed from atomic context */
410static LIST_HEAD(adap_rcu_list);
411static DEFINE_SPINLOCK(adap_rcu_lock);
403static struct cxgb4_uld_info ulds[CXGB4_ULD_MAX]; 412static struct cxgb4_uld_info ulds[CXGB4_ULD_MAX];
404static const char *uld_str[] = { "RDMA", "iSCSI" }; 413static 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
3239static 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
3255static 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
3820static void notify_ulds(struct adapter *adap, enum cxgb4_state new_state) 3869static 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}
3879EXPORT_SYMBOL(cxgb4_unregister_uld); 3928EXPORT_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 */
3933static 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
3949static 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
3979static 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
4019static 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 */
4027static 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
4049static 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
4072static 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
5945static void __exit cxgb4_cleanup_module(void) 6161static 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);