Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 01bcca68 authored by Vipul Pandya's avatar Vipul Pandya Committed by Roland Dreier
Browse files

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: default avatarVipul Pandya <vipul@chelsio.com>
Signed-off-by: default avatarRoland Dreier <roland@purestorage.com>
parent 80f40c1f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -576,6 +576,7 @@ struct adapter {
	struct l2t_data *l2t;
	void *uld_handle[CXGB4_ULD_MAX];
	struct list_head list_node;
	struct list_head rcu_node;

	struct tid_info tids;
	void **tid_release_head;
+217 −0
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@
#include <linux/workqueue.h>
#include <net/neighbour.h>
#include <net/netevent.h>
#include <net/addrconf.h>
#include <asm/uaccess.h>

#include "cxgb4.h"
@@ -68,6 +69,11 @@
#include "t4fw_api.h"
#include "l2t.h"

#include <../drivers/net/bonding/bonding.h>

#ifdef DRV_VERSION
#undef DRV_VERSION
#endif
#define DRV_VERSION "2.0.0-ko"
#define DRV_DESC "Chelsio T4/T5 Network Driver"

@@ -400,6 +406,9 @@ static struct dentry *cxgb4_debugfs_root;

static LIST_HEAD(adapter_list);
static DEFINE_MUTEX(uld_mutex);
/* Adapter list to be accessed from atomic context */
static LIST_HEAD(adap_rcu_list);
static DEFINE_SPINLOCK(adap_rcu_lock);
static struct cxgb4_uld_info ulds[CXGB4_ULD_MAX];
static const char *uld_str[] = { "RDMA", "iSCSI" };

@@ -3227,6 +3236,38 @@ static int tid_init(struct tid_info *t)
	return 0;
}

static int cxgb4_clip_get(const struct net_device *dev,
			  const struct in6_addr *lip)
{
	struct adapter *adap;
	struct fw_clip_cmd c;

	adap = netdev2adap(dev);
	memset(&c, 0, sizeof(c));
	c.op_to_write = htonl(FW_CMD_OP(FW_CLIP_CMD) |
			FW_CMD_REQUEST | FW_CMD_WRITE);
	c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_ALLOC | FW_LEN16(c));
	*(__be64 *)&c.ip_hi = *(__be64 *)(lip->s6_addr);
	*(__be64 *)&c.ip_lo = *(__be64 *)(lip->s6_addr + 8);
	return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
}

static int cxgb4_clip_release(const struct net_device *dev,
			      const struct in6_addr *lip)
{
	struct adapter *adap;
	struct fw_clip_cmd c;

	adap = netdev2adap(dev);
	memset(&c, 0, sizeof(c));
	c.op_to_write = htonl(FW_CMD_OP(FW_CLIP_CMD) |
			FW_CMD_REQUEST | FW_CMD_READ);
	c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_FREE | FW_LEN16(c));
	*(__be64 *)&c.ip_hi = *(__be64 *)(lip->s6_addr);
	*(__be64 *)&c.ip_lo = *(__be64 *)(lip->s6_addr + 8);
	return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
}

/**
 *	cxgb4_create_server - create an IP server
 *	@dev: the device
@@ -3790,6 +3831,10 @@ static void attach_ulds(struct adapter *adap)
{
	unsigned int i;

	spin_lock(&adap_rcu_lock);
	list_add_tail_rcu(&adap->rcu_node, &adap_rcu_list);
	spin_unlock(&adap_rcu_lock);

	mutex_lock(&uld_mutex);
	list_add_tail(&adap->list_node, &adapter_list);
	for (i = 0; i < CXGB4_ULD_MAX; i++)
@@ -3815,6 +3860,10 @@ static void detach_ulds(struct adapter *adap)
		netevent_registered = false;
	}
	mutex_unlock(&uld_mutex);

	spin_lock(&adap_rcu_lock);
	list_del_rcu(&adap->rcu_node);
	spin_unlock(&adap_rcu_lock);
}

static void notify_ulds(struct adapter *adap, enum cxgb4_state new_state)
@@ -3878,6 +3927,169 @@ int cxgb4_unregister_uld(enum cxgb4_uld type)
}
EXPORT_SYMBOL(cxgb4_unregister_uld);

/* Check if netdev on which event is occured belongs to us or not. Return
 * suceess (1) if it belongs otherwise failure (0).
 */
static int cxgb4_netdev(struct net_device *netdev)
{
	struct adapter *adap;
	int i;

	spin_lock(&adap_rcu_lock);
	list_for_each_entry_rcu(adap, &adap_rcu_list, rcu_node)
		for (i = 0; i < MAX_NPORTS; i++)
			if (adap->port[i] == netdev) {
				spin_unlock(&adap_rcu_lock);
				return 1;
			}
	spin_unlock(&adap_rcu_lock);
	return 0;
}

static int clip_add(struct net_device *event_dev, struct inet6_ifaddr *ifa,
		    unsigned long event)
{
	int ret = NOTIFY_DONE;

	rcu_read_lock();
	if (cxgb4_netdev(event_dev)) {
		switch (event) {
		case NETDEV_UP:
			ret = cxgb4_clip_get(event_dev,
				(const struct in6_addr *)ifa->addr.s6_addr);
			if (ret < 0) {
				rcu_read_unlock();
				return ret;
			}
			ret = NOTIFY_OK;
			break;
		case NETDEV_DOWN:
			cxgb4_clip_release(event_dev,
				(const struct in6_addr *)ifa->addr.s6_addr);
			ret = NOTIFY_OK;
			break;
		default:
			break;
		}
	}
	rcu_read_unlock();
	return ret;
}

static int cxgb4_inet6addr_handler(struct notifier_block *this,
		unsigned long event, void *data)
{
	struct inet6_ifaddr *ifa = data;
	struct net_device *event_dev;
	int ret = NOTIFY_DONE;
	int cnt;
	struct bonding *bond = netdev_priv(ifa->idev->dev);
	struct slave *slave;
	struct pci_dev *first_pdev = NULL;

	if (ifa->idev->dev->priv_flags & IFF_802_1Q_VLAN) {
		event_dev = vlan_dev_real_dev(ifa->idev->dev);
		ret = clip_add(event_dev, ifa, event);
	} else if (ifa->idev->dev->flags & IFF_MASTER) {
		/* It is possible that two different adapters are bonded in one
		 * bond. We need to find such different adapters and add clip
		 * in all of them only once.
		 */
		read_lock(&bond->lock);
		bond_for_each_slave(bond, slave, cnt) {
			if (!first_pdev) {
				ret = clip_add(slave->dev, ifa, event);
				/* If clip_add is success then only initialize
				 * first_pdev since it means it is our device
				 */
				if (ret == NOTIFY_OK)
					first_pdev = to_pci_dev(
							slave->dev->dev.parent);
			} else if (first_pdev !=
				   to_pci_dev(slave->dev->dev.parent))
					ret = clip_add(slave->dev, ifa, event);
		}
		read_unlock(&bond->lock);
	} else
		ret = clip_add(ifa->idev->dev, ifa, event);

	return ret;
}

static struct notifier_block cxgb4_inet6addr_notifier = {
	.notifier_call = cxgb4_inet6addr_handler
};

/* Retrieves IPv6 addresses from a root device (bond, vlan) associated with
 * a physical device.
 * The physical device reference is needed to send the actul CLIP command.
 */
static int update_dev_clip(struct net_device *root_dev, struct net_device *dev)
{
	struct inet6_dev *idev = NULL;
	struct inet6_ifaddr *ifa;
	int ret = 0;

	idev = __in6_dev_get(root_dev);
	if (!idev)
		return ret;

	read_lock_bh(&idev->lock);
	list_for_each_entry(ifa, &idev->addr_list, if_list) {
		ret = cxgb4_clip_get(dev,
				(const struct in6_addr *)ifa->addr.s6_addr);
		if (ret < 0)
			break;
	}
	read_unlock_bh(&idev->lock);

	return ret;
}

static int update_root_dev_clip(struct net_device *dev)
{
	struct net_device *root_dev = NULL;
	int i, ret = 0;

	/* First populate the real net device's IPv6 addresses */
	ret = update_dev_clip(dev, dev);
	if (ret)
		return ret;

	/* Parse all bond and vlan devices layered on top of the physical dev */
	for (i = 0; i < VLAN_N_VID; i++) {
		root_dev = __vlan_find_dev_deep(dev, htons(ETH_P_8021Q), i);
		if (!root_dev)
			continue;

		ret = update_dev_clip(root_dev, dev);
		if (ret)
			break;
	}
	return ret;
}

static void update_clip(const struct adapter *adap)
{
	int i;
	struct net_device *dev;
	int ret;

	rcu_read_lock();

	for (i = 0; i < MAX_NPORTS; i++) {
		dev = adap->port[i];
		ret = 0;

		if (dev)
			ret = update_root_dev_clip(dev);

		if (ret < 0)
			break;
	}
	rcu_read_unlock();
}

/**
 *	cxgb_up - enable the adapter
 *	@adap: adapter being enabled
@@ -3923,6 +4135,7 @@ static int cxgb_up(struct adapter *adap)
	t4_intr_enable(adap);
	adap->flags |= FULL_INIT_DONE;
	notify_ulds(adap, CXGB4_STATE_UP);
	update_clip(adap);
 out:
	return err;
 irq_err:
@@ -5939,11 +6152,15 @@ static int __init cxgb4_init_module(void)
	ret = pci_register_driver(&cxgb4_driver);
	if (ret < 0)
		debugfs_remove(cxgb4_debugfs_root);

	register_inet6addr_notifier(&cxgb4_inet6addr_notifier);

	return ret;
}

static void __exit cxgb4_cleanup_module(void)
{
	unregister_inet6addr_notifier(&cxgb4_inet6addr_notifier);
	pci_unregister_driver(&cxgb4_driver);
	debugfs_remove(cxgb4_debugfs_root);  /* NULL ok */
	flush_workqueue(workq);
+23 −0
Original line number Diff line number Diff line
@@ -616,6 +616,7 @@ enum fw_cmd_opcodes {
	FW_RSS_IND_TBL_CMD             = 0x20,
	FW_RSS_GLB_CONFIG_CMD          = 0x22,
	FW_RSS_VI_CONFIG_CMD           = 0x23,
	FW_CLIP_CMD                    = 0x28,
	FW_LASTC2E_CMD                 = 0x40,
	FW_ERROR_CMD                   = 0x80,
	FW_DEBUG_CMD                   = 0x81,
@@ -2062,6 +2063,28 @@ struct fw_rss_vi_config_cmd {
	} u;
};

struct fw_clip_cmd {
	__be32 op_to_write;
	__be32 alloc_to_len16;
	__be64 ip_hi;
	__be64 ip_lo;
	__be32 r4[2];
};

#define S_FW_CLIP_CMD_ALLOC     31
#define M_FW_CLIP_CMD_ALLOC     0x1
#define V_FW_CLIP_CMD_ALLOC(x)  ((x) << S_FW_CLIP_CMD_ALLOC)
#define G_FW_CLIP_CMD_ALLOC(x)  \
	(((x) >> S_FW_CLIP_CMD_ALLOC) & M_FW_CLIP_CMD_ALLOC)
#define F_FW_CLIP_CMD_ALLOC     V_FW_CLIP_CMD_ALLOC(1U)

#define S_FW_CLIP_CMD_FREE      30
#define M_FW_CLIP_CMD_FREE      0x1
#define V_FW_CLIP_CMD_FREE(x)   ((x) << S_FW_CLIP_CMD_FREE)
#define G_FW_CLIP_CMD_FREE(x)   \
	(((x) >> S_FW_CLIP_CMD_FREE) & M_FW_CLIP_CMD_FREE)
#define F_FW_CLIP_CMD_FREE      V_FW_CLIP_CMD_FREE(1U)

enum fw_error_type {
	FW_ERROR_TYPE_EXCEPTION		= 0x0,
	FW_ERROR_TYPE_HWMODULE		= 0x1,