/*
- * Copyright (c) 2006 QLogic, Inc. All rights reserved.
* Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
#include <linux/vmalloc.h>
#include "ipath_kernel.h"
+#include "ips_common.h"
#include "ipath_layer.h"
-#include "ipath_common.h"
static void ipath_update_pio_bufs(struct ipath_devdata *);
EXPORT_SYMBOL_GPL(ipath_get_unit_name);
-#define DRIVER_LOAD_MSG "QLogic " IPATH_DRV_NAME " loaded: "
+#define DRIVER_LOAD_MSG "PathScale " IPATH_DRV_NAME " loaded: "
#define PFX IPATH_DRV_NAME ": "
/*
EXPORT_SYMBOL_GPL(ipath_debug);
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("QLogic <support@pathscale.com>");
-MODULE_DESCRIPTION("QLogic InfiniPath driver");
+MODULE_AUTHOR("PathScale <support@pathscale.com>");
+MODULE_DESCRIPTION("Pathscale InfiniPath driver");
const char *ipath_ibcstatus_str[] = {
"Disabled",
.id_table = ipath_pci_tbl,
};
+/*
+ * This is where port 0's rcvhdrtail register is written back; we also
+ * want nothing else sharing the cache line, so make it a cache line
+ * in size. Used for all units.
+ */
+volatile __le64 *ipath_port0_rcvhdrtail;
+dma_addr_t ipath_port0_rcvhdrtail_dma;
+static int port0_rcvhdrtail_refs;
static inline void read_bars(struct ipath_devdata *dd, struct pci_dev *dev,
u32 *bar0, u32 *bar1)
list_del(&dd->ipath_list);
spin_unlock_irqrestore(&ipath_devs_lock, flags);
}
- vfree(dd);
+ dma_free_coherent(&pdev->dev, sizeof(*dd), dd, dd->ipath_dma_addr);
}
static struct ipath_devdata *ipath_alloc_devdata(struct pci_dev *pdev)
{
unsigned long flags;
struct ipath_devdata *dd;
+ dma_addr_t dma_addr;
int ret;
if (!idr_pre_get(&unit_table, GFP_KERNEL)) {
goto bail;
}
- dd = vmalloc(sizeof(*dd));
+ dd = dma_alloc_coherent(&pdev->dev, sizeof(*dd), &dma_addr,
+ GFP_KERNEL);
+
if (!dd) {
dd = ERR_PTR(-ENOMEM);
goto bail;
}
- memset(dd, 0, sizeof(*dd));
+
+ dd->ipath_dma_addr = dma_addr;
dd->ipath_unit = -1;
spin_lock_irqsave(&ipath_devs_lock, flags);
return nunits;
}
+static int init_port0_rcvhdrtail(struct pci_dev *pdev)
+{
+ int ret;
+
+ mutex_lock(&ipath_mutex);
+
+ if (!ipath_port0_rcvhdrtail) {
+ ipath_port0_rcvhdrtail =
+ dma_alloc_coherent(&pdev->dev,
+ IPATH_PORT0_RCVHDRTAIL_SIZE,
+ &ipath_port0_rcvhdrtail_dma,
+ GFP_KERNEL);
+
+ if (!ipath_port0_rcvhdrtail) {
+ ret = -ENOMEM;
+ goto bail;
+ }
+ }
+ port0_rcvhdrtail_refs++;
+ ret = 0;
+
+bail:
+ mutex_unlock(&ipath_mutex);
+
+ return ret;
+}
+
+static void cleanup_port0_rcvhdrtail(struct pci_dev *pdev)
+{
+ mutex_lock(&ipath_mutex);
+
+ if (!--port0_rcvhdrtail_refs) {
+ dma_free_coherent(&pdev->dev, IPATH_PORT0_RCVHDRTAIL_SIZE,
+ (void *) ipath_port0_rcvhdrtail,
+ ipath_port0_rcvhdrtail_dma);
+ ipath_port0_rcvhdrtail = NULL;
+ }
+
+ mutex_unlock(&ipath_mutex);
+}
+
/*
* These next two routines are placeholders in case we don't have per-arch
* code for controlling write combining. If explicit control of write
u32 bar0 = 0, bar1 = 0;
u8 rev;
+ ret = init_port0_rcvhdrtail(pdev);
+ if (ret < 0) {
+ printk(KERN_ERR IPATH_DRV_NAME
+ ": Could not allocate port0_rcvhdrtail: error %d\n",
+ -ret);
+ goto bail;
+ }
+
dd = ipath_alloc_devdata(pdev);
if (IS_ERR(dd)) {
ret = PTR_ERR(dd);
printk(KERN_ERR IPATH_DRV_NAME
": Could not allocate devdata: error %d\n", -ret);
- goto bail;
+ goto bail_rcvhdrtail;
}
ipath_cdbg(VERBOSE, "initializing unit #%u\n", dd->ipath_unit);
*/
ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
if (ret) {
- dev_info(&pdev->dev,
- "Unable to set DMA mask for unit %u: %d\n",
- dd->ipath_unit, ret);
+ dev_info(&pdev->dev, "pci_set_dma_mask unit %u "
+ "fails: %d\n", dd->ipath_unit, ret);
goto bail_regions;
}
- else {
+ else
ipath_dbg("No 64bit DMA mask, used 32 bit mask\n");
- ret = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
- if (ret)
- dev_info(&pdev->dev,
- "Unable to set DMA consistent mask "
- "for unit %u: %d\n",
- dd->ipath_unit, ret);
-
- }
- }
- else {
- ret = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
- if (ret)
- dev_info(&pdev->dev,
- "Unable to set DMA consistent mask "
- "for unit %u: %d\n",
- dd->ipath_unit, ret);
}
pci_set_master(pdev);
ipath_init_pe800_funcs(dd);
break;
default:
- ipath_dev_err(dd, "Found unknown QLogic deviceid 0x%x, "
+ ipath_dev_err(dd, "Found unknown PathScale deviceid 0x%x, "
"failing\n", ent->device);
return -ENODEV;
}
for (j = 0; j < 6; j++) {
if (!pdev->resource[j].start)
continue;
- ipath_cdbg(VERBOSE, "BAR %d start %llx, end %llx, len %llx\n",
- j, (unsigned long long)pdev->resource[j].start,
- (unsigned long long)pdev->resource[j].end,
- (unsigned long long)pci_resource_len(pdev, j));
+ ipath_cdbg(VERBOSE, "BAR %d start %lx, end %lx, len %lx\n",
+ j, pdev->resource[j].start,
+ pdev->resource[j].end,
+ pci_resource_len(pdev, j));
}
if (!addr) {
((void __iomem *)dd->ipath_kregbase + len);
dd->ipath_physaddr = addr; /* used for io_remap, etc. */
/* for user mmap */
- ipath_cdbg(VERBOSE, "mapped io addr %llx to kregbase %p\n",
- addr, dd->ipath_kregbase);
+ dd->ipath_kregvirt = (u64 __iomem *) phys_to_virt(addr);
+ ipath_cdbg(VERBOSE, "mapped io addr %llx to kregbase %p "
+ "kregvirt %p\n", addr, dd->ipath_kregbase,
+ dd->ipath_kregvirt);
/*
* clear ipath_flags here instead of in ipath_init_chip as it is set
* by ipath_setup_htconfig.
*/
dd->ipath_flags = 0;
- dd->ipath_lli_counter = 0;
- dd->ipath_lli_errors = 0;
if (dd->ipath_f_bus(dd, pdev))
ipath_dev_err(dd, "Failed to setup config space; "
"continuing anyway\n");
/*
- * set up our interrupt handler; IRQF_SHARED probably not needed,
+ * set up our interrupt handler; SA_SHIRQ probably not needed,
* since MSI interrupts shouldn't be shared but won't hurt for now.
* check 0 irq after we return from chip-specific bus setup, since
* that can affect this due to setup
ipath_dev_err(dd, "irq is 0, BIOS error? Interrupts won't "
"work\n");
else {
- ret = request_irq(pdev->irq, ipath_intr, IRQF_SHARED,
+ ret = request_irq(pdev->irq, ipath_intr, SA_SHIRQ,
IPATH_DRV_NAME, dd);
if (ret) {
ipath_dev_err(dd, "Couldn't setup irq handler, "
ipath_device_create_group(&pdev->dev, dd);
ipathfs_add_device(dd);
ipath_user_add(dd);
- ipath_diag_add(dd);
ipath_layer_add(dd);
goto bail;
bail_devdata:
ipath_free_devdata(pdev, dd);
+bail_rcvhdrtail:
+ cleanup_port0_rcvhdrtail(pdev);
+
bail:
return ret;
}
return;
dd = pci_get_drvdata(pdev);
- ipath_layer_remove(dd);
- ipath_diag_remove(dd);
- ipath_user_remove(dd);
+ ipath_layer_del(dd);
+ ipath_user_del(dd);
ipathfs_remove_device(dd);
ipath_device_remove_group(&pdev->dev, dd);
ipath_cdbg(VERBOSE, "Releasing pci memory regions, dd %p, "
pci_disable_device(pdev);
ipath_free_devdata(pdev, dd);
+ cleanup_port0_rcvhdrtail(pdev);
}
/* general driver use */
u8 pad, *bthbytes;
struct sk_buff *skb, *nskb;
- if (dd->ipath_port0_skbs &&
- hdr->sub_opcode == IPATH_ITH4X_OPCODE_ENCAP) {
+ if (dd->ipath_port0_skbs && hdr->sub_opcode == OPCODE_ENCAP) {
/*
* Allocate a new sk_buff to replace the one we give
* to the network stack.
/* another ether packet received */
ipath_stats.sps_ether_rpkts++;
}
- else if (hdr->sub_opcode == IPATH_ITH4X_OPCODE_LID_ARP)
+ else if (hdr->sub_opcode == OPCODE_LID_ARP)
__ipath_layer_rcv_lid(dd, hdr);
}
-static void ipath_rcv_hdrerr(struct ipath_devdata *dd,
- u32 eflags,
- u32 l,
- u32 etail,
- u64 *rc)
-{
- char emsg[128];
- struct ipath_message_header *hdr;
-
- get_rhf_errstring(eflags, emsg, sizeof emsg);
- hdr = (struct ipath_message_header *)&rc[1];
- ipath_cdbg(PKT, "RHFerrs %x hdrqtail=%x typ=%u "
- "tlen=%x opcode=%x egridx=%x: %s\n",
- eflags, l,
- ipath_hdrget_rcv_type((__le32 *) rc),
- ipath_hdrget_length_in_bytes((__le32 *) rc),
- be32_to_cpu(hdr->bth[0]) >> 24,
- etail, emsg);
-
- /* Count local link integrity errors. */
- if (eflags & (INFINIPATH_RHF_H_ICRCERR | INFINIPATH_RHF_H_VCRCERR)) {
- u8 n = (dd->ipath_ibcctrl >>
- INFINIPATH_IBCC_PHYERRTHRESHOLD_SHIFT) &
- INFINIPATH_IBCC_PHYERRTHRESHOLD_MASK;
-
- if (++dd->ipath_lli_counter > n) {
- dd->ipath_lli_counter = 0;
- dd->ipath_lli_errors++;
- }
- }
-}
-
/*
* ipath_kreceive - receive a packet
* @dd: the infinipath device
const u32 rsize = dd->ipath_rcvhdrentsize; /* words */
const u32 maxcnt = dd->ipath_rcvhdrcnt * rsize; /* words */
u32 etail = -1, l, hdrqtail;
- struct ipath_message_header *hdr;
- u32 eflags, i, etype, tlen, pkttot = 0, updegr=0, reloop=0;
+ struct ips_message_header *hdr;
+ u32 eflags, i, etype, tlen, pkttot = 0;
static u64 totcalls; /* stats, may eventually remove */
+ char emsg[128];
if (!dd->ipath_hdrqtailptr) {
ipath_dev_err(dd,
if (test_and_set_bit(0, &dd->ipath_rcv_pending))
goto bail;
- l = dd->ipath_port0head;
- hdrqtail = (u32) le64_to_cpu(*dd->ipath_hdrqtailptr);
- if (l == hdrqtail)
+ if (dd->ipath_port0head ==
+ (u32)le64_to_cpu(*dd->ipath_hdrqtailptr))
goto done;
-reloop:
- for (i = 0; l != hdrqtail; i++) {
+gotmore:
+ /*
+ * read only once at start. If in flood situation, this helps
+ * performance slightly. If more arrive while we are processing,
+ * we'll come back here and do them
+ */
+ hdrqtail = (u32)le64_to_cpu(*dd->ipath_hdrqtailptr);
+
+ for (i = 0, l = dd->ipath_port0head; l != hdrqtail; i++) {
u32 qp;
u8 *bthbytes;
rc = (u64 *) (dd->ipath_pd[0]->port_rcvhdrq + (l << 2));
- hdr = (struct ipath_message_header *)&rc[1];
+ hdr = (struct ips_message_header *)&rc[1];
/*
* could make a network order version of IPATH_KD_QP, and
* do the obvious shift before masking to speed this up.
qp = ntohl(hdr->bth[1]) & 0xffffff;
bthbytes = (u8 *) hdr->bth;
- eflags = ipath_hdrget_err_flags((__le32 *) rc);
- etype = ipath_hdrget_rcv_type((__le32 *) rc);
+ eflags = ips_get_hdr_err_flags((__le32 *) rc);
+ etype = ips_get_rcv_type((__le32 *) rc);
/* total length */
- tlen = ipath_hdrget_length_in_bytes((__le32 *) rc);
+ tlen = ips_get_length_in_bytes((__le32 *) rc);
ebuf = NULL;
if (etype != RCVHQ_RCV_TYPE_EXPECTED) {
/*
* set ebuf (so we try to copy data) unless the
* length requires it.
*/
- etail = ipath_hdrget_index((__le32 *) rc);
+ etail = ips_get_index((__le32 *) rc);
if (tlen > sizeof(*hdr) ||
etype == RCVHQ_RCV_TYPE_NON_KD)
ebuf = ipath_get_egrbuf(dd, etail, 0);
*/
if (etype != RCVHQ_RCV_TYPE_NON_KD && etype !=
- RCVHQ_RCV_TYPE_ERROR && ipath_hdrget_ipath_ver(
+ RCVHQ_RCV_TYPE_ERROR && ips_get_ipath_ver(
hdr->iph.ver_port_tid_offset) !=
IPS_PROTO_VERSION) {
ipath_cdbg(PKT, "Bad InfiniPath protocol version "
"%x\n", etype);
}
- if (unlikely(eflags))
- ipath_rcv_hdrerr(dd, eflags, l, etail, rc);
- else if (etype == RCVHQ_RCV_TYPE_NON_KD) {
+ if (eflags & ~(INFINIPATH_RHF_H_TIDERR |
+ INFINIPATH_RHF_H_IHDRERR)) {
+ get_rhf_errstring(eflags, emsg, sizeof emsg);
+ ipath_cdbg(PKT, "RHFerrs %x hdrqtail=%x typ=%u "
+ "tlen=%x opcode=%x egridx=%x: %s\n",
+ eflags, l, etype, tlen, bthbytes[0],
+ ips_get_index((__le32 *) rc), emsg);
+ } else if (etype == RCVHQ_RCV_TYPE_NON_KD) {
int ret = __ipath_verbs_rcv(dd, rc + 1,
ebuf, tlen);
if (ret == -ENODEV)
ipath_cdbg(VERBOSE,
"received IB packet, "
"not SMA (QP=%x)\n", qp);
- if (dd->ipath_lli_counter)
- dd->ipath_lli_counter--;
-
} else if (etype == RCVHQ_RCV_TYPE_EAGER) {
if (qp == IPATH_KD_QP &&
bthbytes[0] == ipath_layer_rcv_opcode &&
else if (etype == RCVHQ_RCV_TYPE_EXPECTED)
ipath_dbg("Bug: Expected TID, opcode %x; ignored\n",
be32_to_cpu(hdr->bth[0]) & 0xff);
- else {
+ else if (eflags & (INFINIPATH_RHF_H_TIDERR |
+ INFINIPATH_RHF_H_IHDRERR)) {
+ /*
+ * This is a type 3 packet, only the LRH is in the
+ * rcvhdrq, the rest of the header is in the eager
+ * buffer.
+ */
+ u8 opcode;
+ if (ebuf) {
+ bthbytes = (u8 *) ebuf;
+ opcode = *bthbytes;
+ }
+ else
+ opcode = 0;
+ get_rhf_errstring(eflags, emsg, sizeof emsg);
+ ipath_dbg("Err %x (%s), opcode %x, egrbuf %x, "
+ "len %x\n", eflags, emsg, opcode, etail,
+ tlen);
+ } else {
/*
* error packet, type of error unknown.
* Probably type 3, but we don't know, so don't
l += rsize;
if (l >= maxcnt)
l = 0;
- if (etype != RCVHQ_RCV_TYPE_EXPECTED)
- updegr = 1;
/*
- * update head regs on last packet, and every 16 packets.
- * Reduce bus traffic, while still trying to prevent
- * rcvhdrq overflows, for when the queue is nearly full
+ * update for each packet, to help prevent overflows if we
+ * have lots of packets.
*/
- if (l == hdrqtail || (i && !(i&0xf))) {
- u64 lval;
- if (l == hdrqtail) /* PE-800 interrupt only on last */
- lval = dd->ipath_rhdrhead_intr_off | l;
- else
- lval = l;
- (void)ipath_write_ureg(dd, ur_rcvhdrhead, lval, 0);
- if (updegr) {
- (void)ipath_write_ureg(dd, ur_rcvegrindexhead,
- etail, 0);
- updegr = 0;
- }
- }
- }
-
- if (!dd->ipath_rhdrhead_intr_off && !reloop) {
- /* HT-400 workaround; we can have a race clearing chip
- * interrupt with another interrupt about to be delivered,
- * and can clear it before it is delivered on the GPIO
- * workaround. By doing the extra check here for the
- * in-memory tail register updating while we were doing
- * earlier packets, we "almost" guarantee we have covered
- * that case.
- */
- u32 hqtail = (u32)le64_to_cpu(*dd->ipath_hdrqtailptr);
- if (hqtail != hdrqtail) {
- hdrqtail = hqtail;
- reloop = 1; /* loop 1 extra time at most */
- goto reloop;
- }
+ (void)ipath_write_ureg(dd, ur_rcvhdrhead,
+ dd->ipath_rhdrhead_intr_off | l, 0);
+ if (etype != RCVHQ_RCV_TYPE_EXPECTED)
+ (void)ipath_write_ureg(dd, ur_rcvegrindexhead,
+ etail, 0);
}
pkttot += i;
dd->ipath_port0head = l;
+ if (hdrqtail != (u32)le64_to_cpu(*dd->ipath_hdrqtailptr))
+ /* more arrived while we handled first batch */
+ goto gotmore;
+
if (pkttot > ipath_stats.sps_maxpkts_call)
ipath_stats.sps_maxpkts_call = pkttot;
ipath_stats.sps_port0pkts += pkttot;
* @dd: the infinipath device
* @pd: the port data
*
- * this must be contiguous memory (from an i/o perspective), and must be
- * DMA'able (which means for some systems, it will go through an IOMMU,
- * or be forced into a low address range).
+ * this *must* be physically contiguous memory, and for now,
+ * that limits it to what kmalloc can do.
*/
int ipath_create_rcvhdrq(struct ipath_devdata *dd,
struct ipath_portdata *pd)
{
- int ret = 0;
+ int ret = 0, amt;
+ amt = ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize *
+ sizeof(u32), PAGE_SIZE);
if (!pd->port_rcvhdrq) {
- dma_addr_t phys_hdrqtail;
+ /*
+ * not using REPEAT isn't viable; at 128KB, we can easily
+ * fail this. The problem with REPEAT is we can block here
+ * "forever". There isn't an inbetween, unfortunately. We
+ * could reduce the risk by never freeing the rcvhdrq except
+ * at unload, but even then, the first time a port is used,
+ * we could delay for some time...
+ */
gfp_t gfp_flags = GFP_USER | __GFP_COMP;
- int amt = ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize *
- sizeof(u32), PAGE_SIZE);
pd->port_rcvhdrq = dma_alloc_coherent(
&dd->pcidev->dev, amt, &pd->port_rcvhdrq_phys,
ret = -ENOMEM;
goto bail;
}
- pd->port_rcvhdrtail_kvaddr = dma_alloc_coherent(
- &dd->pcidev->dev, PAGE_SIZE, &phys_hdrqtail, GFP_KERNEL);
- if (!pd->port_rcvhdrtail_kvaddr) {
- ipath_dev_err(dd, "attempt to allocate 1 page "
- "for port %u rcvhdrqtailaddr failed\n",
- pd->port_port);
- ret = -ENOMEM;
- goto bail;
- }
- pd->port_rcvhdrqtailaddr_phys = phys_hdrqtail;
pd->port_rcvhdrq_size = amt;
(unsigned long) pd->port_rcvhdrq_phys,
(unsigned long) pd->port_rcvhdrq_size,
pd->port_port);
-
- ipath_cdbg(VERBOSE, "port %d hdrtailaddr, %llx physical\n",
- pd->port_port,
- (unsigned long long) phys_hdrqtail);
+ } else {
+ /*
+ * clear for security, sanity, and/or debugging, each
+ * time we reuse
+ */
+ memset(pd->port_rcvhdrq, 0, amt);
}
- else
- ipath_cdbg(VERBOSE, "reuse port %d rcvhdrq @%p %llx phys; "
- "hdrtailaddr@%p %llx physical\n",
- pd->port_port, pd->port_rcvhdrq,
- pd->port_rcvhdrq_phys, pd->port_rcvhdrtail_kvaddr,
- (unsigned long long)pd->port_rcvhdrqtailaddr_phys);
-
- /* clear for security and sanity on each use */
- memset(pd->port_rcvhdrq, 0, pd->port_rcvhdrq_size);
- memset((void *)pd->port_rcvhdrtail_kvaddr, 0, PAGE_SIZE);
/*
* tell chip each time we init it, even if we are re-using previous
- * memory (we zero the register at process close)
+ * memory (we zero it at process close)
*/
- ipath_write_kreg_port(dd, dd->ipath_kregs->kr_rcvhdrtailaddr,
- pd->port_port, pd->port_rcvhdrqtailaddr_phys);
+ ipath_cdbg(VERBOSE, "writing port %d rcvhdraddr as %lx\n",
+ pd->port_port, (unsigned long) pd->port_rcvhdrq_phys);
ipath_write_kreg_port(dd, dd->ipath_kregs->kr_rcvhdraddr,
pd->port_port, pd->port_rcvhdrq_phys);
[INFINIPATH_IBCC_LINKCMD_ARMED] = "ARMED",
[INFINIPATH_IBCC_LINKCMD_ACTIVE] = "ACTIVE"
};
- int linkcmd = (which >> INFINIPATH_IBCC_LINKCMD_SHIFT) &
- INFINIPATH_IBCC_LINKCMD_MASK;
-
ipath_cdbg(SMA, "Trying to move unit %u to %s, current ltstate "
"is %s\n", dd->ipath_unit,
- what[linkcmd],
+ what[(which >> INFINIPATH_IBCC_LINKCMD_SHIFT) &
+ INFINIPATH_IBCC_LINKCMD_MASK],
ipath_ibcstatus_str[
(ipath_read_kreg64
(dd, dd->ipath_kregs->kr_ibcstatus) >>
INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) &
INFINIPATH_IBCS_LINKTRAININGSTATE_MASK]);
- /* flush all queued sends when going to DOWN or INIT, to be sure that
- * they don't block SMA and other MAD packets */
- if (!linkcmd || linkcmd == INFINIPATH_IBCC_LINKCMD_INIT) {
- ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl,
- INFINIPATH_S_ABORT);
- ipath_disarm_piobufs(dd, dd->ipath_lastport_piobuf,
- (unsigned)(dd->ipath_piobcnt2k +
- dd->ipath_piobcnt4k) -
- dd->ipath_lastport_piobuf);
- }
ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl,
dd->ipath_ibcctrl | which);
/* disable IBC */
dd->ipath_control &= ~INFINIPATH_C_LINKENABLE;
ipath_write_kreg(dd, dd->ipath_kregs->kr_control,
- dd->ipath_control | INFINIPATH_C_FREEZEMODE);
+ dd->ipath_control);
/*
* clear SerdesEnable and turn the leds off; do this here because
/**
* ipath_free_pddata - free a port's allocated data
* @dd: the infinipath device
- * @pd: the portdata structure
+ * @port: the port
+ * @freehdrq: free the port data structure if true
*
- * free up any allocated data for a port
- * This should not touch anything that would affect a simultaneous
- * re-allocation of port data, because it is called after ipath_mutex
- * is released (and can be called from reinit as well).
- * It should never change any chip state, or global driver state.
- * (The only exception to global state is freeing the port0 port0_skbs.)
+ * when closing, free up any allocated data for a port, if the
+ * reference count goes to zero
+ * Note: this also optionally frees the portdata itself!
+ * Any changes here have to be matched up with the reinit case
+ * of ipath_init_chip(), which calls this routine on reinit after reset.
*/
-void ipath_free_pddata(struct ipath_devdata *dd, struct ipath_portdata *pd)
+void ipath_free_pddata(struct ipath_devdata *dd, u32 port, int freehdrq)
{
+ struct ipath_portdata *pd = dd->ipath_pd[port];
+
if (!pd)
return;
-
- if (pd->port_rcvhdrq) {
+ if (freehdrq)
+ /*
+ * only clear and free portdata if we are going to also
+ * release the hdrq, otherwise we leak the hdrq on each
+ * open/close cycle
+ */
+ dd->ipath_pd[port] = NULL;
+ if (freehdrq && pd->port_rcvhdrq) {
ipath_cdbg(VERBOSE, "free closed port %d rcvhdrq @ %p "
"(size=%lu)\n", pd->port_port, pd->port_rcvhdrq,
(unsigned long) pd->port_rcvhdrq_size);
dma_free_coherent(&dd->pcidev->dev, pd->port_rcvhdrq_size,
pd->port_rcvhdrq, pd->port_rcvhdrq_phys);
pd->port_rcvhdrq = NULL;
- if (pd->port_rcvhdrtail_kvaddr) {
- dma_free_coherent(&dd->pcidev->dev, PAGE_SIZE,
- (void *)pd->port_rcvhdrtail_kvaddr,
- pd->port_rcvhdrqtailaddr_phys);
- pd->port_rcvhdrtail_kvaddr = NULL;
- }
}
- if (pd->port_port && pd->port_rcvegrbuf) {
- unsigned e;
-
- for (e = 0; e < pd->port_rcvegrbuf_chunks; e++) {
- void *base = pd->port_rcvegrbuf[e];
- size_t size = pd->port_rcvegrbuf_size;
-
- ipath_cdbg(VERBOSE, "egrbuf free(%p, %lu), "
- "chunk %u/%u\n", base,
- (unsigned long) size,
- e, pd->port_rcvegrbuf_chunks);
- dma_free_coherent(&dd->pcidev->dev, size,
- base, pd->port_rcvegrbuf_phys[e]);
+ if (port && pd->port_rcvegrbuf) {
+ /* always free this */
+ if (pd->port_rcvegrbuf) {
+ unsigned e;
+
+ for (e = 0; e < pd->port_rcvegrbuf_chunks; e++) {
+ void *base = pd->port_rcvegrbuf[e];
+ size_t size = pd->port_rcvegrbuf_size;
+
+ ipath_cdbg(VERBOSE, "egrbuf free(%p, %lu), "
+ "chunk %u/%u\n", base,
+ (unsigned long) size,
+ e, pd->port_rcvegrbuf_chunks);
+ dma_free_coherent(
+ &dd->pcidev->dev, size, base,
+ pd->port_rcvegrbuf_phys[e]);
+ }
+ vfree(pd->port_rcvegrbuf);
+ pd->port_rcvegrbuf = NULL;
+ vfree(pd->port_rcvegrbuf_phys);
+ pd->port_rcvegrbuf_phys = NULL;
}
- vfree(pd->port_rcvegrbuf);
- pd->port_rcvegrbuf = NULL;
- vfree(pd->port_rcvegrbuf_phys);
- pd->port_rcvegrbuf_phys = NULL;
pd->port_rcvegrbuf_chunks = 0;
- } else if (pd->port_port == 0 && dd->ipath_port0_skbs) {
+ } else if (port == 0 && dd->ipath_port0_skbs) {
unsigned e;
struct sk_buff **skbs = dd->ipath_port0_skbs;
dev_kfree_skb(skbs[e]);
vfree(skbs);
}
- kfree(pd->port_tid_pg_list);
- kfree(pd);
+ if (freehdrq) {
+ kfree(pd->port_tid_pg_list);
+ kfree(pd);
+ }
}
static int __init infinipath_init(void)
* re-init
*/
dd->ipath_kregbase = NULL;
+ dd->ipath_kregvirt = NULL;
dd->ipath_uregbase = 0;
dd->ipath_sregbase = 0;
dd->ipath_cregbase = 0;
dd->ipath_pioavailregs_phys);
dd->ipath_pioavailregs_dma = NULL;
}
- if (dd->ipath_dummy_hdrq) {
- dma_free_coherent(&dd->pcidev->dev,
- dd->ipath_pd[0]->port_rcvhdrq_size,
- dd->ipath_dummy_hdrq, dd->ipath_dummy_hdrq_phys);
- dd->ipath_dummy_hdrq = NULL;
- }
if (dd->ipath_pageshadow) {
struct page **tmpp = dd->ipath_pageshadow;
/*
* free any resources still in use (usually just kernel ports)
- * at unload; we do for portcnt, not cfgports, because cfgports
- * could have changed while we were loaded.
+ * at unload
*/
- for (port = 0; port < dd->ipath_portcnt; port++) {
- struct ipath_portdata *pd = dd->ipath_pd[port];
- dd->ipath_pd[port] = NULL;
- ipath_free_pddata(dd, pd);
- }
+ for (port = 0; port < dd->ipath_cfgports; port++)
+ ipath_free_pddata(dd, port, 1);
kfree(dd->ipath_pd);
/*
* debuggability, in case some cleanup path tries to use it