idpf supports the header split feature and that feature is always
enabled by default.
However, for flexibility reasons and to simplify some scenarios, it
would be useful to have the support for switching the header split
off (and on) from the userspace.
Address that need by adding the user config parameter, the functions
for disabling (or enabling) the header split feature, and calls to
them from the Ethtool ringparam callbacks.
It still is enabled by default if supported by the hardware.
Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Signed-off-by: Michal Kubiak <michal.kubiak@intel.com>
Co-developed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Signed-off-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Link: https://lore.kernel.org/r/20231212142752.935000-3-aleksander.lobakin@intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
#include <linux/pci.h>
#include <linux/bitfield.h>
#include <linux/sctp.h>
-#include <linux/ethtool.h>
+#include <linux/ethtool_netlink.h>
#include <net/gro.h>
#include <linux/dim.h>
/**
* enum idpf_user_flags
+ * @__IDPF_USER_FLAG_HSPLIT: header split state
* @__IDPF_PROMISC_UC: Unicast promiscuous mode
* @__IDPF_PROMISC_MC: Multicast promiscuous mode
* @__IDPF_USER_FLAGS_NBITS: Must be last
*/
enum idpf_user_flags {
+ __IDPF_USER_FLAG_HSPLIT = 0U,
__IDPF_PROMISC_UC = 32,
__IDPF_PROMISC_MC,
int idpf_send_set_sriov_vfs_msg(struct idpf_adapter *adapter, u16 num_vfs);
int idpf_sriov_configure(struct pci_dev *pdev, int num_vfs);
+u8 idpf_vport_get_hsplit(const struct idpf_vport *vport);
+bool idpf_vport_set_hsplit(const struct idpf_vport *vport, u8 val);
+
#endif /* !_IDPF_H_ */
ring->rx_pending = vport->rxq_desc_count;
ring->tx_pending = vport->txq_desc_count;
+ kring->tcp_data_split = idpf_vport_get_hsplit(vport);
+
idpf_vport_ctrl_unlock(netdev);
}
new_rx_count == vport->rxq_desc_count)
goto unlock_mutex;
+ if (!idpf_vport_set_hsplit(vport, kring->tcp_data_split)) {
+ NL_SET_ERR_MSG_MOD(ext_ack,
+ "setting TCP data split is not supported");
+ err = -EOPNOTSUPP;
+
+ goto unlock_mutex;
+ }
+
config_data = &vport->adapter->vport_config[idx]->user_config;
config_data->num_req_txq_desc = new_tx_count;
config_data->num_req_rxq_desc = new_rx_count;
static const struct ethtool_ops idpf_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_USE_ADAPTIVE,
+ .supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT,
.get_msglevel = idpf_get_msglevel,
.set_msglevel = idpf_set_msglevel,
.get_link = ethtool_op_get_link,
adapter->next_vport = idpf_get_free_slot(adapter);
}
+/**
+ * idpf_is_hsplit_supported - check whether the header split is supported
+ * @vport: virtual port to check the capability for
+ *
+ * Return: true if it's supported by the HW/FW, false if not.
+ */
+static bool idpf_is_hsplit_supported(const struct idpf_vport *vport)
+{
+ return idpf_is_queue_model_split(vport->rxq_model) &&
+ idpf_is_cap_ena_all(vport->adapter, IDPF_HSPLIT_CAPS,
+ IDPF_CAP_HSPLIT);
+}
+
+/**
+ * idpf_vport_get_hsplit - get the current header split feature state
+ * @vport: virtual port to query the state for
+ *
+ * Return: ``ETHTOOL_TCP_DATA_SPLIT_UNKNOWN`` if not supported,
+ * ``ETHTOOL_TCP_DATA_SPLIT_DISABLED`` if disabled,
+ * ``ETHTOOL_TCP_DATA_SPLIT_ENABLED`` if active.
+ */
+u8 idpf_vport_get_hsplit(const struct idpf_vport *vport)
+{
+ const struct idpf_vport_user_config_data *config;
+
+ if (!idpf_is_hsplit_supported(vport))
+ return ETHTOOL_TCP_DATA_SPLIT_UNKNOWN;
+
+ config = &vport->adapter->vport_config[vport->idx]->user_config;
+
+ return test_bit(__IDPF_USER_FLAG_HSPLIT, config->user_flags) ?
+ ETHTOOL_TCP_DATA_SPLIT_ENABLED :
+ ETHTOOL_TCP_DATA_SPLIT_DISABLED;
+}
+
+/**
+ * idpf_vport_set_hsplit - enable or disable header split on a given vport
+ * @vport: virtual port to configure
+ * @val: Ethtool flag controlling the header split state
+ *
+ * Return: true on success, false if not supported by the HW.
+ */
+bool idpf_vport_set_hsplit(const struct idpf_vport *vport, u8 val)
+{
+ struct idpf_vport_user_config_data *config;
+
+ if (!idpf_is_hsplit_supported(vport))
+ return val == ETHTOOL_TCP_DATA_SPLIT_UNKNOWN;
+
+ config = &vport->adapter->vport_config[vport->idx]->user_config;
+
+ switch (val) {
+ case ETHTOOL_TCP_DATA_SPLIT_UNKNOWN:
+ /* Default is to enable */
+ case ETHTOOL_TCP_DATA_SPLIT_ENABLED:
+ __set_bit(__IDPF_USER_FLAG_HSPLIT, config->user_flags);
+ return true;
+ case ETHTOOL_TCP_DATA_SPLIT_DISABLED:
+ __clear_bit(__IDPF_USER_FLAG_HSPLIT, config->user_flags);
+ return true;
+ default:
+ return false;
+ }
+}
+
/**
* idpf_vport_alloc - Allocates the next available struct vport in the adapter
* @adapter: board private structure
struct idpf_adapter *adapter = vport->adapter;
struct idpf_queue *q;
int i, k, err = 0;
+ bool hs;
vport->rxq_grps = kcalloc(vport->num_rxq_grp,
sizeof(struct idpf_rxq_group), GFP_KERNEL);
if (!vport->rxq_grps)
return -ENOMEM;
+ hs = idpf_vport_get_hsplit(vport) == ETHTOOL_TCP_DATA_SPLIT_ENABLED;
+
for (i = 0; i < vport->num_rxq_grp; i++) {
struct idpf_rxq_group *rx_qgrp = &vport->rxq_grps[i];
int j;
q->rx_buf_size = vport->bufq_size[j];
q->rx_buffer_low_watermark = IDPF_LOW_WATERMARK;
q->rx_buf_stride = IDPF_RX_BUF_STRIDE;
- if (idpf_is_cap_ena_all(adapter, IDPF_HSPLIT_CAPS,
- IDPF_CAP_HSPLIT) &&
- idpf_is_queue_model_split(vport->rxq_model)) {
+
+ if (hs) {
q->rx_hsplit_en = true;
q->rx_hbuf_size = IDPF_HDR_BUF_SIZE;
}
rx_qgrp->splitq.rxq_sets[j]->refillq1 =
&rx_qgrp->splitq.bufq_sets[1].refillqs[j];
- if (idpf_is_cap_ena_all(adapter, IDPF_HSPLIT_CAPS,
- IDPF_CAP_HSPLIT) &&
- idpf_is_queue_model_split(vport->rxq_model)) {
+ if (hs) {
q->rx_hsplit_en = true;
q->rx_hbuf_size = IDPF_HDR_BUF_SIZE;
}
memcpy(vport->rx_itr_profile, rx_itr, IDPF_DIM_PROFILE_SLOTS);
memcpy(vport->tx_itr_profile, tx_itr, IDPF_DIM_PROFILE_SLOTS);
+ idpf_vport_set_hsplit(vport, ETHTOOL_TCP_DATA_SPLIT_ENABLED);
+
idpf_vport_init_num_qs(vport, vport_msg);
idpf_vport_calc_num_q_desc(vport);
idpf_vport_calc_num_q_groups(vport);