|
@@ -115,6 +115,7 @@ static int init_sub_crqs(struct ibmvnic_adapter *);
|
|
|
static int init_sub_crq_irqs(struct ibmvnic_adapter *adapter);
|
|
|
static int ibmvnic_init(struct ibmvnic_adapter *);
|
|
|
static void release_crq_queue(struct ibmvnic_adapter *);
|
|
|
+static int __ibmvnic_set_mac(struct net_device *netdev, struct sockaddr *p);
|
|
|
|
|
|
struct ibmvnic_stat {
|
|
|
char name[ETH_GSTRING_LEN];
|
|
@@ -926,6 +927,11 @@ static int ibmvnic_open(struct net_device *netdev)
|
|
|
|
|
|
mutex_lock(&adapter->reset_lock);
|
|
|
|
|
|
+ if (adapter->mac_change_pending) {
|
|
|
+ __ibmvnic_set_mac(netdev, &adapter->desired.mac);
|
|
|
+ adapter->mac_change_pending = false;
|
|
|
+ }
|
|
|
+
|
|
|
if (adapter->state != VNIC_CLOSED) {
|
|
|
rc = ibmvnic_login(netdev);
|
|
|
if (rc) {
|
|
@@ -1426,7 +1432,7 @@ static void ibmvnic_set_multi(struct net_device *netdev)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static int ibmvnic_set_mac(struct net_device *netdev, void *p)
|
|
|
+static int __ibmvnic_set_mac(struct net_device *netdev, struct sockaddr *p)
|
|
|
{
|
|
|
struct ibmvnic_adapter *adapter = netdev_priv(netdev);
|
|
|
struct sockaddr *addr = p;
|
|
@@ -1444,6 +1450,22 @@ static int ibmvnic_set_mac(struct net_device *netdev, void *p)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int ibmvnic_set_mac(struct net_device *netdev, void *p)
|
|
|
+{
|
|
|
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
|
|
|
+ struct sockaddr *addr = p;
|
|
|
+
|
|
|
+ if (adapter->state != VNIC_OPEN) {
|
|
|
+ memcpy(&adapter->desired.mac, addr, sizeof(struct sockaddr));
|
|
|
+ adapter->mac_change_pending = true;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ __ibmvnic_set_mac(netdev, addr);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* do_reset returns zero if we are able to keep processing reset events, or
|
|
|
* non-zero if we hit a fatal error and must halt.
|
|
@@ -1470,6 +1492,13 @@ static int do_reset(struct ibmvnic_adapter *adapter,
|
|
|
if (rc)
|
|
|
return rc;
|
|
|
|
|
|
+ if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM ||
|
|
|
+ adapter->wait_for_reset) {
|
|
|
+ release_resources(adapter);
|
|
|
+ release_sub_crqs(adapter);
|
|
|
+ release_crq_queue(adapter);
|
|
|
+ }
|
|
|
+
|
|
|
if (adapter->reset_reason != VNIC_RESET_NON_FATAL) {
|
|
|
/* remove the closed state so when we call open it appears
|
|
|
* we are coming from the probed state.
|
|
@@ -1478,7 +1507,7 @@ static int do_reset(struct ibmvnic_adapter *adapter,
|
|
|
|
|
|
rc = ibmvnic_init(adapter);
|
|
|
if (rc)
|
|
|
- return 0;
|
|
|
+ return IBMVNIC_INIT_FAILED;
|
|
|
|
|
|
/* If the adapter was in PROBE state prior to the reset,
|
|
|
* exit here.
|
|
@@ -1492,16 +1521,23 @@ static int do_reset(struct ibmvnic_adapter *adapter,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- rc = reset_tx_pools(adapter);
|
|
|
- if (rc)
|
|
|
- return rc;
|
|
|
+ if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM ||
|
|
|
+ adapter->wait_for_reset) {
|
|
|
+ rc = init_resources(adapter);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
+ } else {
|
|
|
+ rc = reset_tx_pools(adapter);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
|
|
|
- rc = reset_rx_pools(adapter);
|
|
|
- if (rc)
|
|
|
- return rc;
|
|
|
+ rc = reset_rx_pools(adapter);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
|
|
|
- if (reset_state == VNIC_CLOSED)
|
|
|
- return 0;
|
|
|
+ if (reset_state == VNIC_CLOSED)
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
rc = __ibmvnic_open(netdev);
|
|
@@ -1561,7 +1597,7 @@ static void __ibmvnic_reset(struct work_struct *work)
|
|
|
struct ibmvnic_adapter *adapter;
|
|
|
struct net_device *netdev;
|
|
|
u32 reset_state;
|
|
|
- int rc;
|
|
|
+ int rc = 0;
|
|
|
|
|
|
adapter = container_of(work, struct ibmvnic_adapter, ibmvnic_reset);
|
|
|
netdev = adapter->netdev;
|
|
@@ -1574,12 +1610,18 @@ static void __ibmvnic_reset(struct work_struct *work)
|
|
|
while (rwi) {
|
|
|
rc = do_reset(adapter, rwi, reset_state);
|
|
|
kfree(rwi);
|
|
|
- if (rc)
|
|
|
+ if (rc && rc != IBMVNIC_INIT_FAILED)
|
|
|
break;
|
|
|
|
|
|
rwi = get_next_rwi(adapter);
|
|
|
}
|
|
|
|
|
|
+ if (adapter->wait_for_reset) {
|
|
|
+ adapter->wait_for_reset = false;
|
|
|
+ adapter->reset_done_rc = rc;
|
|
|
+ complete(&adapter->reset_done);
|
|
|
+ }
|
|
|
+
|
|
|
if (rc) {
|
|
|
netdev_dbg(adapter->netdev, "Reset failed\n");
|
|
|
free_all_rwi(adapter);
|
|
@@ -1759,9 +1801,42 @@ static void ibmvnic_netpoll_controller(struct net_device *dev)
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
+static int wait_for_reset(struct ibmvnic_adapter *adapter)
|
|
|
+{
|
|
|
+ adapter->fallback.mtu = adapter->req_mtu;
|
|
|
+ adapter->fallback.rx_queues = adapter->req_rx_queues;
|
|
|
+ adapter->fallback.tx_queues = adapter->req_tx_queues;
|
|
|
+ adapter->fallback.rx_entries = adapter->req_rx_add_entries_per_subcrq;
|
|
|
+ adapter->fallback.tx_entries = adapter->req_tx_entries_per_subcrq;
|
|
|
+
|
|
|
+ init_completion(&adapter->reset_done);
|
|
|
+ ibmvnic_reset(adapter, VNIC_RESET_CHANGE_PARAM);
|
|
|
+ adapter->wait_for_reset = true;
|
|
|
+ wait_for_completion(&adapter->reset_done);
|
|
|
+
|
|
|
+ if (adapter->reset_done_rc) {
|
|
|
+ adapter->desired.mtu = adapter->fallback.mtu;
|
|
|
+ adapter->desired.rx_queues = adapter->fallback.rx_queues;
|
|
|
+ adapter->desired.tx_queues = adapter->fallback.tx_queues;
|
|
|
+ adapter->desired.rx_entries = adapter->fallback.rx_entries;
|
|
|
+ adapter->desired.tx_entries = adapter->fallback.tx_entries;
|
|
|
+
|
|
|
+ init_completion(&adapter->reset_done);
|
|
|
+ ibmvnic_reset(adapter, VNIC_RESET_CHANGE_PARAM);
|
|
|
+ wait_for_completion(&adapter->reset_done);
|
|
|
+ }
|
|
|
+ adapter->wait_for_reset = false;
|
|
|
+
|
|
|
+ return adapter->reset_done_rc;
|
|
|
+}
|
|
|
+
|
|
|
static int ibmvnic_change_mtu(struct net_device *netdev, int new_mtu)
|
|
|
{
|
|
|
- return -EOPNOTSUPP;
|
|
|
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
|
|
|
+
|
|
|
+ adapter->desired.mtu = new_mtu + ETH_HLEN;
|
|
|
+
|
|
|
+ return wait_for_reset(adapter);
|
|
|
}
|
|
|
|
|
|
static const struct net_device_ops ibmvnic_netdev_ops = {
|
|
@@ -1849,6 +1924,27 @@ static void ibmvnic_get_ringparam(struct net_device *netdev,
|
|
|
ring->rx_jumbo_pending = 0;
|
|
|
}
|
|
|
|
|
|
+static int ibmvnic_set_ringparam(struct net_device *netdev,
|
|
|
+ struct ethtool_ringparam *ring)
|
|
|
+{
|
|
|
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
|
|
|
+
|
|
|
+ if (ring->rx_pending > adapter->max_rx_add_entries_per_subcrq ||
|
|
|
+ ring->tx_pending > adapter->max_tx_entries_per_subcrq) {
|
|
|
+ netdev_err(netdev, "Invalid request.\n");
|
|
|
+ netdev_err(netdev, "Max tx buffers = %llu\n",
|
|
|
+ adapter->max_rx_add_entries_per_subcrq);
|
|
|
+ netdev_err(netdev, "Max rx buffers = %llu\n",
|
|
|
+ adapter->max_tx_entries_per_subcrq);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ adapter->desired.rx_entries = ring->rx_pending;
|
|
|
+ adapter->desired.tx_entries = ring->tx_pending;
|
|
|
+
|
|
|
+ return wait_for_reset(adapter);
|
|
|
+}
|
|
|
+
|
|
|
static void ibmvnic_get_channels(struct net_device *netdev,
|
|
|
struct ethtool_channels *channels)
|
|
|
{
|
|
@@ -1864,6 +1960,17 @@ static void ibmvnic_get_channels(struct net_device *netdev,
|
|
|
channels->combined_count = 0;
|
|
|
}
|
|
|
|
|
|
+static int ibmvnic_set_channels(struct net_device *netdev,
|
|
|
+ struct ethtool_channels *channels)
|
|
|
+{
|
|
|
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
|
|
|
+
|
|
|
+ adapter->desired.rx_queues = channels->rx_count;
|
|
|
+ adapter->desired.tx_queues = channels->tx_count;
|
|
|
+
|
|
|
+ return wait_for_reset(adapter);
|
|
|
+}
|
|
|
+
|
|
|
static void ibmvnic_get_strings(struct net_device *dev, u32 stringset, u8 *data)
|
|
|
{
|
|
|
struct ibmvnic_adapter *adapter = netdev_priv(dev);
|
|
@@ -1960,7 +2067,9 @@ static const struct ethtool_ops ibmvnic_ethtool_ops = {
|
|
|
.set_msglevel = ibmvnic_set_msglevel,
|
|
|
.get_link = ibmvnic_get_link,
|
|
|
.get_ringparam = ibmvnic_get_ringparam,
|
|
|
+ .set_ringparam = ibmvnic_set_ringparam,
|
|
|
.get_channels = ibmvnic_get_channels,
|
|
|
+ .set_channels = ibmvnic_set_channels,
|
|
|
.get_strings = ibmvnic_get_strings,
|
|
|
.get_sset_count = ibmvnic_get_sset_count,
|
|
|
.get_ethtool_stats = ibmvnic_get_ethtool_stats,
|
|
@@ -2426,6 +2535,7 @@ static void ibmvnic_send_req_caps(struct ibmvnic_adapter *adapter, int retry)
|
|
|
{
|
|
|
struct device *dev = &adapter->vdev->dev;
|
|
|
union ibmvnic_crq crq;
|
|
|
+ int max_entries;
|
|
|
|
|
|
if (!retry) {
|
|
|
/* Sub-CRQ entries are 32 byte long */
|
|
@@ -2437,21 +2547,60 @@ static void ibmvnic_send_req_caps(struct ibmvnic_adapter *adapter, int retry)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- /* Get the minimum between the queried max and the entries
|
|
|
- * that fit in our PAGE_SIZE
|
|
|
- */
|
|
|
- adapter->req_tx_entries_per_subcrq =
|
|
|
- adapter->max_tx_entries_per_subcrq > entries_page ?
|
|
|
- entries_page : adapter->max_tx_entries_per_subcrq;
|
|
|
- adapter->req_rx_add_entries_per_subcrq =
|
|
|
- adapter->max_rx_add_entries_per_subcrq > entries_page ?
|
|
|
- entries_page : adapter->max_rx_add_entries_per_subcrq;
|
|
|
-
|
|
|
- adapter->req_tx_queues = adapter->opt_tx_comp_sub_queues;
|
|
|
- adapter->req_rx_queues = adapter->opt_rx_comp_queues;
|
|
|
- adapter->req_rx_add_queues = adapter->max_rx_add_queues;
|
|
|
+ if (adapter->desired.mtu)
|
|
|
+ adapter->req_mtu = adapter->desired.mtu;
|
|
|
+ else
|
|
|
+ adapter->req_mtu = adapter->netdev->mtu + ETH_HLEN;
|
|
|
+
|
|
|
+ if (!adapter->desired.tx_entries)
|
|
|
+ adapter->desired.tx_entries =
|
|
|
+ adapter->max_tx_entries_per_subcrq;
|
|
|
+ if (!adapter->desired.rx_entries)
|
|
|
+ adapter->desired.rx_entries =
|
|
|
+ adapter->max_rx_add_entries_per_subcrq;
|
|
|
+
|
|
|
+ max_entries = IBMVNIC_MAX_LTB_SIZE /
|
|
|
+ (adapter->req_mtu + IBMVNIC_BUFFER_HLEN);
|
|
|
+
|
|
|
+ if ((adapter->req_mtu + IBMVNIC_BUFFER_HLEN) *
|
|
|
+ adapter->desired.tx_entries > IBMVNIC_MAX_LTB_SIZE) {
|
|
|
+ adapter->desired.tx_entries = max_entries;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((adapter->req_mtu + IBMVNIC_BUFFER_HLEN) *
|
|
|
+ adapter->desired.rx_entries > IBMVNIC_MAX_LTB_SIZE) {
|
|
|
+ adapter->desired.rx_entries = max_entries;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (adapter->desired.tx_entries)
|
|
|
+ adapter->req_tx_entries_per_subcrq =
|
|
|
+ adapter->desired.tx_entries;
|
|
|
+ else
|
|
|
+ adapter->req_tx_entries_per_subcrq =
|
|
|
+ adapter->max_tx_entries_per_subcrq;
|
|
|
+
|
|
|
+ if (adapter->desired.rx_entries)
|
|
|
+ adapter->req_rx_add_entries_per_subcrq =
|
|
|
+ adapter->desired.rx_entries;
|
|
|
+ else
|
|
|
+ adapter->req_rx_add_entries_per_subcrq =
|
|
|
+ adapter->max_rx_add_entries_per_subcrq;
|
|
|
+
|
|
|
+ if (adapter->desired.tx_queues)
|
|
|
+ adapter->req_tx_queues =
|
|
|
+ adapter->desired.tx_queues;
|
|
|
+ else
|
|
|
+ adapter->req_tx_queues =
|
|
|
+ adapter->opt_tx_comp_sub_queues;
|
|
|
+
|
|
|
+ if (adapter->desired.rx_queues)
|
|
|
+ adapter->req_rx_queues =
|
|
|
+ adapter->desired.rx_queues;
|
|
|
+ else
|
|
|
+ adapter->req_rx_queues =
|
|
|
+ adapter->opt_rx_comp_queues;
|
|
|
|
|
|
- adapter->req_mtu = adapter->netdev->mtu + ETH_HLEN;
|
|
|
+ adapter->req_rx_add_queues = adapter->max_rx_add_queues;
|
|
|
}
|
|
|
|
|
|
memset(&crq, 0, sizeof(crq));
|
|
@@ -3272,6 +3421,7 @@ static int handle_login_rsp(union ibmvnic_crq *login_rsp_crq,
|
|
|
struct ibmvnic_adapter *adapter)
|
|
|
{
|
|
|
struct device *dev = &adapter->vdev->dev;
|
|
|
+ struct net_device *netdev = adapter->netdev;
|
|
|
struct ibmvnic_login_rsp_buffer *login_rsp = adapter->login_rsp_buf;
|
|
|
struct ibmvnic_login_buffer *login = adapter->login_buf;
|
|
|
int i;
|
|
@@ -3291,6 +3441,8 @@ static int handle_login_rsp(union ibmvnic_crq *login_rsp_crq,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+ netdev->mtu = adapter->req_mtu - ETH_HLEN;
|
|
|
+
|
|
|
netdev_dbg(adapter->netdev, "Login Response Buffer:\n");
|
|
|
for (i = 0; i < (adapter->login_rsp_buf_sz - 1) / 8 + 1; i++) {
|
|
|
netdev_dbg(adapter->netdev, "%016lx\n",
|
|
@@ -3846,7 +3998,7 @@ static int ibmvnic_init(struct ibmvnic_adapter *adapter)
|
|
|
unsigned long timeout = msecs_to_jiffies(30000);
|
|
|
int rc;
|
|
|
|
|
|
- if (adapter->resetting) {
|
|
|
+ if (adapter->resetting && !adapter->wait_for_reset) {
|
|
|
rc = ibmvnic_reset_crq(adapter);
|
|
|
if (!rc)
|
|
|
rc = vio_enable_interrupts(adapter->vdev);
|
|
@@ -3880,7 +4032,7 @@ static int ibmvnic_init(struct ibmvnic_adapter *adapter)
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
- if (adapter->resetting)
|
|
|
+ if (adapter->resetting && !adapter->wait_for_reset)
|
|
|
rc = reset_sub_crq_queues(adapter);
|
|
|
else
|
|
|
rc = init_sub_crqs(adapter);
|
|
@@ -3949,6 +4101,8 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
|
|
|
mutex_init(&adapter->rwi_lock);
|
|
|
adapter->resetting = false;
|
|
|
|
|
|
+ adapter->mac_change_pending = false;
|
|
|
+
|
|
|
do {
|
|
|
rc = ibmvnic_init(adapter);
|
|
|
if (rc && rc != EAGAIN)
|
|
@@ -3956,6 +4110,8 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
|
|
|
} while (rc == EAGAIN);
|
|
|
|
|
|
netdev->mtu = adapter->req_mtu - ETH_HLEN;
|
|
|
+ netdev->min_mtu = adapter->min_mtu - ETH_HLEN;
|
|
|
+ netdev->max_mtu = adapter->max_mtu - ETH_HLEN;
|
|
|
|
|
|
rc = device_create_file(&dev->dev, &dev_attr_failover);
|
|
|
if (rc)
|
|
@@ -3970,6 +4126,9 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
|
|
|
dev_info(&dev->dev, "ibmvnic registered\n");
|
|
|
|
|
|
adapter->state = VNIC_PROBED;
|
|
|
+
|
|
|
+ adapter->wait_for_reset = false;
|
|
|
+
|
|
|
return 0;
|
|
|
|
|
|
ibmvnic_register_fail:
|