mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-09-05 11:53:41 +00:00

The live migration driver will need to save and restore the Tx queue context state from the hardware registers. This state contains both static fields which do not change during Tx traffic as well as dynamic fields which may change during Tx traffic. Unlike the Rx context, the Tx queue context is accessed indirectly from GLCOMM_QTX_CNTX_CTL and GLCOMM_QTX_CNTX_DATA registers. These registers are shared by multiple PFs on the same PCIe card. Multiple PFs cannot safely access the registers simultaneously, and there is no hardware semaphore or logic to control access. To handle this, introduce the txq_ctx_lock to the ice_adapter structure. This is similar to the ptp_gltsyn_time_lock. All PFs on the same adapter share this structure, and use it to serialize access to the registers to prevent error. Add a new functions to get and set the Tx queue context through the GLCOMM_QTX_CNTX_CTL interface. The hardware context values are stored in the registers using the same packed format as the Admin Queue buffer. The hardware buffer is 40 bytes wide, as it contains an additional 18 bytes of internal state not sent with the Admin Queue buffer. For this reason, a separate typedef and packing function must be used. We can share the same packed fields definitions because we never need to unpack the internal state. This is preferred, as it ensures the internal state is zero'd when writing into HW, and avoids issues with reading by u32 registers into a buffer of 22 bytes in length. Thanks to the typedefs, misuse of the API with the wrong size buffer can easily be caught at compile time. Note reading this data from hardware is essential because the current Tx queue context may be different from the context as initially programmed by the driver during VF initialization. When migrating a VF we must ensure the target VF has identical context as the source VF did. Co-developed-by: Yahui Cao <yahui.cao@intel.com> Signed-off-by: Yahui Cao <yahui.cao@intel.com> Signed-off-by: Jacob Keller <jacob.e.keller@intel.com> Reviewed-by: Madhu Chittim <madhu.chittim@intel.com> Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com> Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
119 lines
3.0 KiB
C
119 lines
3.0 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
// SPDX-FileCopyrightText: Copyright Red Hat
|
|
|
|
#include <linux/cleanup.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/xarray.h>
|
|
#include "ice_adapter.h"
|
|
#include "ice.h"
|
|
|
|
static DEFINE_XARRAY(ice_adapters);
|
|
static DEFINE_MUTEX(ice_adapters_mutex);
|
|
|
|
static unsigned long ice_adapter_index(u64 dsn)
|
|
{
|
|
#if BITS_PER_LONG == 64
|
|
return dsn;
|
|
#else
|
|
return (u32)dsn ^ (u32)(dsn >> 32);
|
|
#endif
|
|
}
|
|
|
|
static struct ice_adapter *ice_adapter_new(u64 dsn)
|
|
{
|
|
struct ice_adapter *adapter;
|
|
|
|
adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
|
|
if (!adapter)
|
|
return NULL;
|
|
|
|
adapter->device_serial_number = dsn;
|
|
spin_lock_init(&adapter->ptp_gltsyn_time_lock);
|
|
spin_lock_init(&adapter->txq_ctx_lock);
|
|
refcount_set(&adapter->refcount, 1);
|
|
|
|
mutex_init(&adapter->ports.lock);
|
|
INIT_LIST_HEAD(&adapter->ports.ports);
|
|
|
|
return adapter;
|
|
}
|
|
|
|
static void ice_adapter_free(struct ice_adapter *adapter)
|
|
{
|
|
WARN_ON(!list_empty(&adapter->ports.ports));
|
|
mutex_destroy(&adapter->ports.lock);
|
|
|
|
kfree(adapter);
|
|
}
|
|
|
|
/**
|
|
* ice_adapter_get - Get a shared ice_adapter structure.
|
|
* @pdev: Pointer to the pci_dev whose driver is getting the ice_adapter.
|
|
*
|
|
* Gets a pointer to a shared ice_adapter structure. Physical functions (PFs)
|
|
* of the same multi-function PCI device share one ice_adapter structure.
|
|
* The ice_adapter is reference-counted. The PF driver must use ice_adapter_put
|
|
* to release its reference.
|
|
*
|
|
* Context: Process, may sleep.
|
|
* Return: Pointer to ice_adapter on success.
|
|
* ERR_PTR() on error. -ENOMEM is the only possible error.
|
|
*/
|
|
struct ice_adapter *ice_adapter_get(struct pci_dev *pdev)
|
|
{
|
|
u64 dsn = pci_get_dsn(pdev);
|
|
struct ice_adapter *adapter;
|
|
unsigned long index;
|
|
int err;
|
|
|
|
index = ice_adapter_index(dsn);
|
|
scoped_guard(mutex, &ice_adapters_mutex) {
|
|
err = xa_insert(&ice_adapters, index, NULL, GFP_KERNEL);
|
|
if (err == -EBUSY) {
|
|
adapter = xa_load(&ice_adapters, index);
|
|
refcount_inc(&adapter->refcount);
|
|
WARN_ON_ONCE(adapter->device_serial_number != dsn);
|
|
return adapter;
|
|
}
|
|
if (err)
|
|
return ERR_PTR(err);
|
|
|
|
adapter = ice_adapter_new(dsn);
|
|
if (!adapter)
|
|
return ERR_PTR(-ENOMEM);
|
|
xa_store(&ice_adapters, index, adapter, GFP_KERNEL);
|
|
}
|
|
return adapter;
|
|
}
|
|
|
|
/**
|
|
* ice_adapter_put - Release a reference to the shared ice_adapter structure.
|
|
* @pdev: Pointer to the pci_dev whose driver is releasing the ice_adapter.
|
|
*
|
|
* Releases the reference to ice_adapter previously obtained with
|
|
* ice_adapter_get.
|
|
*
|
|
* Context: Process, may sleep.
|
|
*/
|
|
void ice_adapter_put(struct pci_dev *pdev)
|
|
{
|
|
u64 dsn = pci_get_dsn(pdev);
|
|
struct ice_adapter *adapter;
|
|
unsigned long index;
|
|
|
|
index = ice_adapter_index(dsn);
|
|
scoped_guard(mutex, &ice_adapters_mutex) {
|
|
adapter = xa_load(&ice_adapters, index);
|
|
if (WARN_ON(!adapter))
|
|
return;
|
|
if (!refcount_dec_and_test(&adapter->refcount))
|
|
return;
|
|
|
|
WARN_ON(xa_erase(&ice_adapters, index) != adapter);
|
|
}
|
|
ice_adapter_free(adapter);
|
|
}
|