aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnirban Chakraborty <anirban.chakraborty@qlogic.com>2011-05-12 08:48:32 -0400
committerDavid S. Miller <davem@davemloft.net>2011-05-13 14:37:28 -0400
commit29dd54b72ba8c5ad0dd6dd33584449b5953f700b (patch)
tree6decce1d19849a67916219a7d918acb0001c78d3
parentbdc220da3209d50b8c295490dec94845c88670a2 (diff)
ethtool: Added support for FW dump
Added code to take FW dump via ethtool. Dump level can be controlled via setting the dump flag. A get function is provided to query the current setting of the dump flag. Dump data is obtained from the driver via a separate get function. Changes from v3: Fixed buffer length issue in ethtool_get_dump_data function. Updated kernel doc for ethtool_dump struct and get_dump_flag function. Changes from v2: Provided separate commands for get flag and data. Check for minimum of the two buffer length obtained via ethtool and driver and use that for dump buffer Pass up the driver return error codes up to the caller. Added kernel doc comments. Signed-off-by: Anirban Chakraborty <anirban.chakraborty@qlogic.com> Reviewed-by: Ben Hutchings <bhutchings@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/linux/ethtool.h31
-rw-r--r--net/core/ethtool.c90
2 files changed, 121 insertions, 0 deletions
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index bd0b50b85f06..c6a850ab2ec5 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -601,6 +601,26 @@ struct ethtool_flash {
601 char data[ETHTOOL_FLASH_MAX_FILENAME]; 601 char data[ETHTOOL_FLASH_MAX_FILENAME];
602}; 602};
603 603
604/**
605 * struct ethtool_dump - used for retrieving, setting device dump
606 * @cmd: Command number - %ETHTOOL_GET_DUMP_FLAG, %ETHTOOL_GET_DUMP_DATA, or
607 * %ETHTOOL_SET_DUMP
608 * @version: FW version of the dump, filled in by driver
609 * @flag: driver dependent flag for dump setting, filled in by driver during
610 * get and filled in by ethtool for set operation
611 * @len: length of dump data, used as the length of the user buffer on entry to
612 * %ETHTOOL_GET_DUMP_DATA and this is returned as dump length by driver
613 * for %ETHTOOL_GET_DUMP_FLAG command
614 * @data: data collected for get dump data operation
615 */
616struct ethtool_dump {
617 __u32 cmd;
618 __u32 version;
619 __u32 flag;
620 __u32 len;
621 __u8 data[0];
622};
623
604/* for returning and changing feature sets */ 624/* for returning and changing feature sets */
605 625
606/** 626/**
@@ -853,6 +873,10 @@ bool ethtool_invalid_flags(struct net_device *dev, u32 data, u32 supported);
853 * @get_channels: Get number of channels. 873 * @get_channels: Get number of channels.
854 * @set_channels: Set number of channels. Returns a negative error code or 874 * @set_channels: Set number of channels. Returns a negative error code or
855 * zero. 875 * zero.
876 * @get_dump_flag: Get dump flag indicating current dump length, version,
877 * and flag of the device.
878 * @get_dump_data: Get dump data.
879 * @set_dump: Set dump specific flags to the device.
856 * 880 *
857 * All operations are optional (i.e. the function pointer may be set 881 * All operations are optional (i.e. the function pointer may be set
858 * to %NULL) and callers must take this into account. Callers must 882 * to %NULL) and callers must take this into account. Callers must
@@ -927,6 +951,10 @@ struct ethtool_ops {
927 const struct ethtool_rxfh_indir *); 951 const struct ethtool_rxfh_indir *);
928 void (*get_channels)(struct net_device *, struct ethtool_channels *); 952 void (*get_channels)(struct net_device *, struct ethtool_channels *);
929 int (*set_channels)(struct net_device *, struct ethtool_channels *); 953 int (*set_channels)(struct net_device *, struct ethtool_channels *);
954 int (*get_dump_flag)(struct net_device *, struct ethtool_dump *);
955 int (*get_dump_data)(struct net_device *,
956 struct ethtool_dump *, void *);
957 int (*set_dump)(struct net_device *, struct ethtool_dump *);
930 958
931}; 959};
932#endif /* __KERNEL__ */ 960#endif /* __KERNEL__ */
@@ -998,6 +1026,9 @@ struct ethtool_ops {
998#define ETHTOOL_SFEATURES 0x0000003b /* Change device offload settings */ 1026#define ETHTOOL_SFEATURES 0x0000003b /* Change device offload settings */
999#define ETHTOOL_GCHANNELS 0x0000003c /* Get no of channels */ 1027#define ETHTOOL_GCHANNELS 0x0000003c /* Get no of channels */
1000#define ETHTOOL_SCHANNELS 0x0000003d /* Set no of channels */ 1028#define ETHTOOL_SCHANNELS 0x0000003d /* Set no of channels */
1029#define ETHTOOL_SET_DUMP 0x0000003e /* Set dump settings */
1030#define ETHTOOL_GET_DUMP_FLAG 0x0000003f /* Get dump settings */
1031#define ETHTOOL_GET_DUMP_DATA 0x00000040 /* Get dump data */
1001 1032
1002/* compatibility with older code */ 1033/* compatibility with older code */
1003#define SPARC_ETH_GSET ETHTOOL_GSET 1034#define SPARC_ETH_GSET ETHTOOL_GSET
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index b8c2b10f397a..b8c2bcfee6af 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -1823,6 +1823,87 @@ static noinline_for_stack int ethtool_flash_device(struct net_device *dev,
1823 return dev->ethtool_ops->flash_device(dev, &efl); 1823 return dev->ethtool_ops->flash_device(dev, &efl);
1824} 1824}
1825 1825
1826static int ethtool_set_dump(struct net_device *dev,
1827 void __user *useraddr)
1828{
1829 struct ethtool_dump dump;
1830
1831 if (!dev->ethtool_ops->set_dump)
1832 return -EOPNOTSUPP;
1833
1834 if (copy_from_user(&dump, useraddr, sizeof(dump)))
1835 return -EFAULT;
1836
1837 return dev->ethtool_ops->set_dump(dev, &dump);
1838}
1839
1840static int ethtool_get_dump_flag(struct net_device *dev,
1841 void __user *useraddr)
1842{
1843 int ret;
1844 struct ethtool_dump dump;
1845 const struct ethtool_ops *ops = dev->ethtool_ops;
1846
1847 if (!dev->ethtool_ops->get_dump_flag)
1848 return -EOPNOTSUPP;
1849
1850 if (copy_from_user(&dump, useraddr, sizeof(dump)))
1851 return -EFAULT;
1852
1853 ret = ops->get_dump_flag(dev, &dump);
1854 if (ret)
1855 return ret;
1856
1857 if (copy_to_user(useraddr, &dump, sizeof(dump)))
1858 return -EFAULT;
1859 return 0;
1860}
1861
1862static int ethtool_get_dump_data(struct net_device *dev,
1863 void __user *useraddr)
1864{
1865 int ret;
1866 __u32 len;
1867 struct ethtool_dump dump, tmp;
1868 const struct ethtool_ops *ops = dev->ethtool_ops;
1869 void *data = NULL;
1870
1871 if (!dev->ethtool_ops->get_dump_data ||
1872 !dev->ethtool_ops->get_dump_flag)
1873 return -EOPNOTSUPP;
1874
1875 if (copy_from_user(&dump, useraddr, sizeof(dump)))
1876 return -EFAULT;
1877
1878 memset(&tmp, 0, sizeof(tmp));
1879 tmp.cmd = ETHTOOL_GET_DUMP_FLAG;
1880 ret = ops->get_dump_flag(dev, &tmp);
1881 if (ret)
1882 return ret;
1883
1884 len = (tmp.len > dump.len) ? dump.len : tmp.len;
1885 if (!len)
1886 return -EFAULT;
1887
1888 data = vzalloc(tmp.len);
1889 if (!data)
1890 return -ENOMEM;
1891 ret = ops->get_dump_data(dev, &dump, data);
1892 if (ret)
1893 goto out;
1894
1895 if (copy_to_user(useraddr, &dump, sizeof(dump))) {
1896 ret = -EFAULT;
1897 goto out;
1898 }
1899 useraddr += offsetof(struct ethtool_dump, data);
1900 if (copy_to_user(useraddr, data, len))
1901 ret = -EFAULT;
1902out:
1903 vfree(data);
1904 return ret;
1905}
1906
1826/* The main entry point in this file. Called from net/core/dev.c */ 1907/* The main entry point in this file. Called from net/core/dev.c */
1827 1908
1828int dev_ethtool(struct net *net, struct ifreq *ifr) 1909int dev_ethtool(struct net *net, struct ifreq *ifr)
@@ -2039,6 +2120,15 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
2039 case ETHTOOL_SCHANNELS: 2120 case ETHTOOL_SCHANNELS:
2040 rc = ethtool_set_channels(dev, useraddr); 2121 rc = ethtool_set_channels(dev, useraddr);
2041 break; 2122 break;
2123 case ETHTOOL_SET_DUMP:
2124 rc = ethtool_set_dump(dev, useraddr);
2125 break;
2126 case ETHTOOL_GET_DUMP_FLAG:
2127 rc = ethtool_get_dump_flag(dev, useraddr);
2128 break;
2129 case ETHTOOL_GET_DUMP_DATA:
2130 rc = ethtool_get_dump_data(dev, useraddr);
2131 break;
2042 default: 2132 default:
2043 rc = -EOPNOTSUPP; 2133 rc = -EOPNOTSUPP;
2044 } 2134 }