182c24c18SEli Cohen /*
282c24c18SEli Cohen  * Copyright (c) 2007 Mellanox Technologies. All rights reserved.
382c24c18SEli Cohen  *
482c24c18SEli Cohen  * This software is available to you under a choice of one of two
582c24c18SEli Cohen  * licenses.  You may choose to be licensed under the terms of the GNU
682c24c18SEli Cohen  * General Public License (GPL) Version 2, available from the file
782c24c18SEli Cohen  * COPYING in the main directory of this source tree, or the
882c24c18SEli Cohen  * OpenIB.org BSD license below:
982c24c18SEli Cohen  *
1082c24c18SEli Cohen  *     Redistribution and use in source and binary forms, with or
1182c24c18SEli Cohen  *     without modification, are permitted provided that the following
1282c24c18SEli Cohen  *     conditions are met:
1382c24c18SEli Cohen  *
1482c24c18SEli Cohen  *      - Redistributions of source code must retain the above
1582c24c18SEli Cohen  *        copyright notice, this list of conditions and the following
1682c24c18SEli Cohen  *        disclaimer.
1782c24c18SEli Cohen  *
1882c24c18SEli Cohen  *      - Redistributions in binary form must reproduce the above
1982c24c18SEli Cohen  *        copyright notice, this list of conditions and the following
2082c24c18SEli Cohen  *        disclaimer in the documentation and/or other materials
2182c24c18SEli Cohen  *        provided with the distribution.
2282c24c18SEli Cohen  *
2382c24c18SEli Cohen  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2482c24c18SEli Cohen  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2582c24c18SEli Cohen  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2682c24c18SEli Cohen  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
2782c24c18SEli Cohen  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
2882c24c18SEli Cohen  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
2982c24c18SEli Cohen  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3082c24c18SEli Cohen  * SOFTWARE.
3182c24c18SEli Cohen  */
3282c24c18SEli Cohen 
3382c24c18SEli Cohen #include <linux/kernel.h>
3482c24c18SEli Cohen #include <linux/ethtool.h>
3582c24c18SEli Cohen #include <linux/netdevice.h>
3682c24c18SEli Cohen 
3782c24c18SEli Cohen #include "ipoib.h"
3882c24c18SEli Cohen 
3982c24c18SEli Cohen static void ipoib_get_drvinfo(struct net_device *netdev,
4082c24c18SEli Cohen 			      struct ethtool_drvinfo *drvinfo)
4182c24c18SEli Cohen {
4282c24c18SEli Cohen 	strncpy(drvinfo->driver, "ipoib", sizeof(drvinfo->driver) - 1);
4382c24c18SEli Cohen }
4482c24c18SEli Cohen 
4570c9c0dbSOr Gerlitz static u32 ipoib_get_rx_csum(struct net_device *dev)
4670c9c0dbSOr Gerlitz {
4770c9c0dbSOr Gerlitz 	struct ipoib_dev_priv *priv = netdev_priv(dev);
4870c9c0dbSOr Gerlitz 	return test_bit(IPOIB_FLAG_CSUM, &priv->flags) &&
4970c9c0dbSOr Gerlitz 		!test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags);
5070c9c0dbSOr Gerlitz }
5170c9c0dbSOr Gerlitz 
5228d52b3cSEli Cohen static int ipoib_get_coalesce(struct net_device *dev,
5328d52b3cSEli Cohen 			      struct ethtool_coalesce *coal)
5428d52b3cSEli Cohen {
5528d52b3cSEli Cohen 	struct ipoib_dev_priv *priv = netdev_priv(dev);
5628d52b3cSEli Cohen 
5728d52b3cSEli Cohen 	coal->rx_coalesce_usecs = priv->ethtool.coalesce_usecs;
5828d52b3cSEli Cohen 	coal->rx_max_coalesced_frames = priv->ethtool.max_coalesced_frames;
5928d52b3cSEli Cohen 
6028d52b3cSEli Cohen 	return 0;
6128d52b3cSEli Cohen }
6228d52b3cSEli Cohen 
6328d52b3cSEli Cohen static int ipoib_set_coalesce(struct net_device *dev,
6428d52b3cSEli Cohen 			      struct ethtool_coalesce *coal)
6528d52b3cSEli Cohen {
6628d52b3cSEli Cohen 	struct ipoib_dev_priv *priv = netdev_priv(dev);
6728d52b3cSEli Cohen 	int ret;
6828d52b3cSEli Cohen 
6928d52b3cSEli Cohen 	/*
70757bebb3SOr Gerlitz 	 * These values are saved in the private data and returned
71757bebb3SOr Gerlitz 	 * when ipoib_get_coalesce() is called
7228d52b3cSEli Cohen 	 */
7328d52b3cSEli Cohen 	if (coal->rx_coalesce_usecs       > 0xffff ||
7428d52b3cSEli Cohen 	    coal->rx_max_coalesced_frames > 0xffff)
7528d52b3cSEli Cohen 		return -EINVAL;
7628d52b3cSEli Cohen 
77f56bcd80SEli Cohen 	ret = ib_modify_cq(priv->recv_cq, coal->rx_max_coalesced_frames,
7828d52b3cSEli Cohen 			   coal->rx_coalesce_usecs);
7928d52b3cSEli Cohen 	if (ret && ret != -ENOSYS) {
8028d52b3cSEli Cohen 		ipoib_warn(priv, "failed modifying CQ (%d)\n", ret);
8128d52b3cSEli Cohen 		return ret;
8228d52b3cSEli Cohen 	}
8328d52b3cSEli Cohen 
8428d52b3cSEli Cohen 	priv->ethtool.coalesce_usecs       = coal->rx_coalesce_usecs;
8528d52b3cSEli Cohen 	priv->ethtool.max_coalesced_frames = coal->rx_max_coalesced_frames;
8628d52b3cSEli Cohen 
8728d52b3cSEli Cohen 	return 0;
8828d52b3cSEli Cohen }
8928d52b3cSEli Cohen 
90af40da89SVladimir Sokolovsky static const char ipoib_stats_keys[][ETH_GSTRING_LEN] = {
91af40da89SVladimir Sokolovsky 	"LRO aggregated", "LRO flushed",
92af40da89SVladimir Sokolovsky 	"LRO avg aggr", "LRO no desc"
93af40da89SVladimir Sokolovsky };
94af40da89SVladimir Sokolovsky 
95af40da89SVladimir Sokolovsky static void ipoib_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
96af40da89SVladimir Sokolovsky {
97af40da89SVladimir Sokolovsky 	switch (stringset) {
98af40da89SVladimir Sokolovsky 	case ETH_SS_STATS:
99af40da89SVladimir Sokolovsky 		memcpy(data, *ipoib_stats_keys,	sizeof(ipoib_stats_keys));
100af40da89SVladimir Sokolovsky 		break;
101af40da89SVladimir Sokolovsky 	}
102af40da89SVladimir Sokolovsky }
103af40da89SVladimir Sokolovsky 
104af40da89SVladimir Sokolovsky static int ipoib_get_sset_count(struct net_device *dev, int sset)
105af40da89SVladimir Sokolovsky {
106af40da89SVladimir Sokolovsky 	switch (sset) {
107af40da89SVladimir Sokolovsky 	case ETH_SS_STATS:
108af40da89SVladimir Sokolovsky 		return ARRAY_SIZE(ipoib_stats_keys);
109af40da89SVladimir Sokolovsky 	default:
110af40da89SVladimir Sokolovsky 		return -EOPNOTSUPP;
111af40da89SVladimir Sokolovsky 	}
112af40da89SVladimir Sokolovsky }
113af40da89SVladimir Sokolovsky 
114af40da89SVladimir Sokolovsky static void ipoib_get_ethtool_stats(struct net_device *dev,
115af40da89SVladimir Sokolovsky 				struct ethtool_stats *stats, uint64_t *data)
116af40da89SVladimir Sokolovsky {
117af40da89SVladimir Sokolovsky 	struct ipoib_dev_priv *priv = netdev_priv(dev);
118af40da89SVladimir Sokolovsky 	int index = 0;
119af40da89SVladimir Sokolovsky 
120af40da89SVladimir Sokolovsky 	/* Get LRO statistics */
121af40da89SVladimir Sokolovsky 	data[index++] = priv->lro.lro_mgr.stats.aggregated;
122af40da89SVladimir Sokolovsky 	data[index++] = priv->lro.lro_mgr.stats.flushed;
123af40da89SVladimir Sokolovsky 	if (priv->lro.lro_mgr.stats.flushed)
124af40da89SVladimir Sokolovsky 		data[index++] = priv->lro.lro_mgr.stats.aggregated /
125af40da89SVladimir Sokolovsky 				priv->lro.lro_mgr.stats.flushed;
126af40da89SVladimir Sokolovsky 	else
127af40da89SVladimir Sokolovsky 		data[index++] = 0;
128af40da89SVladimir Sokolovsky 	data[index++] = priv->lro.lro_mgr.stats.no_desc;
129af40da89SVladimir Sokolovsky }
130af40da89SVladimir Sokolovsky 
13182c24c18SEli Cohen static const struct ethtool_ops ipoib_ethtool_ops = {
13282c24c18SEli Cohen 	.get_drvinfo		= ipoib_get_drvinfo,
13370c9c0dbSOr Gerlitz 	.get_rx_csum		= ipoib_get_rx_csum,
13428d52b3cSEli Cohen 	.get_coalesce		= ipoib_get_coalesce,
13528d52b3cSEli Cohen 	.set_coalesce		= ipoib_set_coalesce,
136af40da89SVladimir Sokolovsky 	.get_flags		= ethtool_op_get_flags,
137af40da89SVladimir Sokolovsky 	.set_flags		= ethtool_op_set_flags,
138af40da89SVladimir Sokolovsky 	.get_strings		= ipoib_get_strings,
139af40da89SVladimir Sokolovsky 	.get_sset_count		= ipoib_get_sset_count,
140af40da89SVladimir Sokolovsky 	.get_ethtool_stats	= ipoib_get_ethtool_stats,
14182c24c18SEli Cohen };
14282c24c18SEli Cohen 
14382c24c18SEli Cohen void ipoib_set_ethtool_ops(struct net_device *dev)
14482c24c18SEli Cohen {
14582c24c18SEli Cohen 	SET_ETHTOOL_OPS(dev, &ipoib_ethtool_ops);
14682c24c18SEli Cohen }
147