Skip to content

Commit

Permalink
liquidio: fix insmod failure when multiple NICs are plugged in
Browse files Browse the repository at this point in the history
When multiple liquidio NICs are plugged in, the first insmod of the PF
driver succeeds.  But after an rmmod, a subsequent insmod fails.  Reason is
during rmmod, the PF driver resets the Octeon of only one of the NICs; it
neglects to reset the Octeons of the other NICs.

Fix the insmod failure by adding the missing Octeon resets at rmmod.  Keep
a per-NIC refcount that indicates the number of active PFs in a given NIC.
When the refcount goes to zero, then reset the Octeon of that NIC.

Signed-off-by: Rick Farrington <ricardo.farrington@cavium.com>
Signed-off-by: Felix Manlunas <felix.manlunas@cavium.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Rick Farrington authored and David S. Miller committed May 17, 2017
1 parent 8b0d3ea commit e1e3ce6
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 10 deletions.
21 changes: 17 additions & 4 deletions drivers/net/ethernet/cavium/liquidio/lio_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1421,7 +1421,7 @@ static bool fw_type_is_none(void)
*/
static void octeon_destroy_resources(struct octeon_device *oct)
{
int i;
int i, refcount;
struct msix_entry *msix_entries;
struct octeon_device_priv *oct_priv =
(struct octeon_device_priv *)oct->priv;
Expand Down Expand Up @@ -1556,10 +1556,14 @@ static void octeon_destroy_resources(struct octeon_device *oct)

/* fallthrough */
case OCT_DEV_PCI_MAP_DONE:
refcount = octeon_deregister_device(oct);

if (!fw_type_is_none()) {
/* Soft reset the octeon device before exiting */
if (!OCTEON_CN23XX_PF(oct) ||
(OCTEON_CN23XX_PF(oct) && !oct->octeon_id))
/* Soft reset the octeon device before exiting.
* Implementation note: here, we reset the device
* if it is a CN6XXX OR the last CN23XX device.
*/
if (OCTEON_CN6XXX(oct) || !refcount)
oct->fn_list.soft_reset(oct);
}

Expand Down Expand Up @@ -4511,6 +4515,15 @@ static int octeon_device_init(struct octeon_device *octeon_dev)

atomic_set(&octeon_dev->status, OCT_DEV_PCI_MAP_DONE);

/* Only add a reference after setting status 'OCT_DEV_PCI_MAP_DONE',
* since that is what is required for the reference to be removed
* during de-initialization (see 'octeon_destroy_resources').
*/
octeon_register_device(octeon_dev, octeon_dev->pci_dev->bus->number,
PCI_SLOT(octeon_dev->pci_dev->devfn),
PCI_FUNC(octeon_dev->pci_dev->devfn),
true);

octeon_dev->app_mode = CVM_DRV_INVALID_APP;

if (OCTEON_CN23XX_PF(octeon_dev)) {
Expand Down
87 changes: 81 additions & 6 deletions drivers/net/ethernet/cavium/liquidio/octeon_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,11 @@ static char oct_dev_app_str[CVM_DRV_APP_COUNT + 1][32] = {
"BASE", "NIC", "UNKNOWN"};

static struct octeon_device *octeon_device[MAX_OCTEON_DEVICES];
static atomic_t adapter_refcounts[MAX_OCTEON_DEVICES];

static u32 octeon_device_count;
/* locks device array (i.e. octeon_device[]) */
spinlock_t octeon_devices_lock;

static struct octeon_core_setup core_setup[MAX_OCTEON_DEVICES];

Expand All @@ -561,6 +565,7 @@ void octeon_init_device_list(int conf_type)
memset(octeon_device, 0, (sizeof(void *) * MAX_OCTEON_DEVICES));
for (i = 0; i < MAX_OCTEON_DEVICES; i++)
oct_set_config_info(i, conf_type);
spin_lock_init(&octeon_devices_lock);
}

static void *__retrieve_octeon_config_info(struct octeon_device *oct,
Expand Down Expand Up @@ -720,30 +725,100 @@ struct octeon_device *octeon_allocate_device(u32 pci_id,
u32 oct_idx = 0;
struct octeon_device *oct = NULL;

spin_lock(&octeon_devices_lock);

for (oct_idx = 0; oct_idx < MAX_OCTEON_DEVICES; oct_idx++)
if (!octeon_device[oct_idx])
break;

if (oct_idx == MAX_OCTEON_DEVICES)
return NULL;
if (oct_idx < MAX_OCTEON_DEVICES) {
oct = octeon_allocate_device_mem(pci_id, priv_size);
if (oct) {
octeon_device_count++;
octeon_device[oct_idx] = oct;
}
}

oct = octeon_allocate_device_mem(pci_id, priv_size);
spin_unlock(&octeon_devices_lock);
if (!oct)
return NULL;

spin_lock_init(&oct->pci_win_lock);
spin_lock_init(&oct->mem_access_lock);

octeon_device_count++;
octeon_device[oct_idx] = oct;

oct->octeon_id = oct_idx;
snprintf(oct->device_name, sizeof(oct->device_name),
"LiquidIO%d", (oct->octeon_id));

return oct;
}

/** Register a device's bus location at initialization time.
* @param octeon_dev - pointer to the octeon device structure.
* @param bus - PCIe bus #
* @param dev - PCIe device #
* @param func - PCIe function #
* @param is_pf - TRUE for PF, FALSE for VF
* @return reference count of device's adapter
*/
int octeon_register_device(struct octeon_device *oct,
int bus, int dev, int func, int is_pf)
{
int idx, refcount;

oct->loc.bus = bus;
oct->loc.dev = dev;
oct->loc.func = func;

oct->adapter_refcount = &adapter_refcounts[oct->octeon_id];
atomic_set(oct->adapter_refcount, 0);

spin_lock(&octeon_devices_lock);
for (idx = (int)oct->octeon_id - 1; idx >= 0; idx--) {
if (!octeon_device[idx]) {
dev_err(&oct->pci_dev->dev,
"%s: Internal driver error, missing dev",
__func__);
spin_unlock(&octeon_devices_lock);
atomic_inc(oct->adapter_refcount);
return 1; /* here, refcount is guaranteed to be 1 */
}
/* if another device is at same bus/dev, use its refcounter */
if ((octeon_device[idx]->loc.bus == bus) &&
(octeon_device[idx]->loc.dev == dev)) {
oct->adapter_refcount =
octeon_device[idx]->adapter_refcount;
break;
}
}
spin_unlock(&octeon_devices_lock);

atomic_inc(oct->adapter_refcount);
refcount = atomic_read(oct->adapter_refcount);

dev_dbg(&oct->pci_dev->dev, "%s: %02x:%02x:%d refcount %u", __func__,
oct->loc.bus, oct->loc.dev, oct->loc.func, refcount);

return refcount;
}

/** Deregister a device at de-initialization time.
* @param octeon_dev - pointer to the octeon device structure.
* @return reference count of device's adapter
*/
int octeon_deregister_device(struct octeon_device *oct)
{
int refcount;

atomic_dec(oct->adapter_refcount);
refcount = atomic_read(oct->adapter_refcount);

dev_dbg(&oct->pci_dev->dev, "%s: %04d:%02d:%d refcount %u", __func__,
oct->loc.bus, oct->loc.dev, oct->loc.func, refcount);

return refcount;
}

int
octeon_allocate_ioq_vector(struct octeon_device *oct)
{
Expand Down
25 changes: 25 additions & 0 deletions drivers/net/ethernet/cavium/liquidio/octeon_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,14 @@ struct octeon_device {
u32 tx_max_coalesced_frames;

bool cores_crashed;

struct {
int bus;
int dev;
int func;
} loc;

atomic_t *adapter_refcount; /* reference count of adapter */
};

#define OCT_DRV_ONLINE 1
Expand Down Expand Up @@ -572,6 +580,23 @@ void octeon_free_device_mem(struct octeon_device *oct);
struct octeon_device *octeon_allocate_device(u32 pci_id,
u32 priv_size);

/** Register a device's bus location at initialization time.
* @param octeon_dev - pointer to the octeon device structure.
* @param bus - PCIe bus #
* @param dev - PCIe device #
* @param func - PCIe function #
* @param is_pf - TRUE for PF, FALSE for VF
* @return reference count of device's adapter
*/
int octeon_register_device(struct octeon_device *oct,
int bus, int dev, int func, int is_pf);

/** Deregister a device at de-initialization time.
* @param octeon_dev - pointer to the octeon device structure.
* @return reference count of device's adapter
*/
int octeon_deregister_device(struct octeon_device *oct);

/** Initialize the driver's dispatch list which is a mix of a hash table
* and a linked list. This is done at driver load time.
* @param octeon_dev - pointer to the octeon device structure.
Expand Down

0 comments on commit e1e3ce6

Please sign in to comment.