|
@@ -41,6 +41,12 @@
|
|
#include "ipath_kernel.h"
|
|
#include "ipath_kernel.h"
|
|
#include "ipath_common.h"
|
|
#include "ipath_common.h"
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * mmap64 doesn't allow all 64 bits for 32-bit applications
|
|
|
|
+ * so only use the low 43 bits.
|
|
|
|
+ */
|
|
|
|
+#define MMAP64_MASK 0x7FFFFFFFFFFUL
|
|
|
|
+
|
|
static int ipath_open(struct inode *, struct file *);
|
|
static int ipath_open(struct inode *, struct file *);
|
|
static int ipath_close(struct inode *, struct file *);
|
|
static int ipath_close(struct inode *, struct file *);
|
|
static ssize_t ipath_write(struct file *, const char __user *, size_t,
|
|
static ssize_t ipath_write(struct file *, const char __user *, size_t,
|
|
@@ -57,18 +63,35 @@ static struct file_operations ipath_file_ops = {
|
|
.mmap = ipath_mmap
|
|
.mmap = ipath_mmap
|
|
};
|
|
};
|
|
|
|
|
|
-static int ipath_get_base_info(struct ipath_portdata *pd,
|
|
|
|
|
|
+static int ipath_get_base_info(struct file *fp,
|
|
void __user *ubase, size_t ubase_size)
|
|
void __user *ubase, size_t ubase_size)
|
|
{
|
|
{
|
|
|
|
+ struct ipath_portdata *pd = port_fp(fp);
|
|
int ret = 0;
|
|
int ret = 0;
|
|
struct ipath_base_info *kinfo = NULL;
|
|
struct ipath_base_info *kinfo = NULL;
|
|
struct ipath_devdata *dd = pd->port_dd;
|
|
struct ipath_devdata *dd = pd->port_dd;
|
|
|
|
+ unsigned subport_cnt;
|
|
|
|
+ int shared, master;
|
|
|
|
+ size_t sz;
|
|
|
|
+
|
|
|
|
+ subport_cnt = pd->port_subport_cnt;
|
|
|
|
+ if (!subport_cnt) {
|
|
|
|
+ shared = 0;
|
|
|
|
+ master = 0;
|
|
|
|
+ subport_cnt = 1;
|
|
|
|
+ } else {
|
|
|
|
+ shared = 1;
|
|
|
|
+ master = !subport_fp(fp);
|
|
|
|
+ }
|
|
|
|
|
|
- if (ubase_size < sizeof(*kinfo)) {
|
|
|
|
|
|
+ sz = sizeof(*kinfo);
|
|
|
|
+ /* If port sharing is not requested, allow the old size structure */
|
|
|
|
+ if (!shared)
|
|
|
|
+ sz -= 3 * sizeof(u64);
|
|
|
|
+ if (ubase_size < sz) {
|
|
ipath_cdbg(PROC,
|
|
ipath_cdbg(PROC,
|
|
- "Base size %lu, need %lu (version mismatch?)\n",
|
|
|
|
- (unsigned long) ubase_size,
|
|
|
|
- (unsigned long) sizeof(*kinfo));
|
|
|
|
|
|
+ "Base size %zu, need %zu (version mismatch?)\n",
|
|
|
|
+ ubase_size, sz);
|
|
ret = -EINVAL;
|
|
ret = -EINVAL;
|
|
goto bail;
|
|
goto bail;
|
|
}
|
|
}
|
|
@@ -95,7 +118,9 @@ static int ipath_get_base_info(struct ipath_portdata *pd,
|
|
kinfo->spi_rcv_egrperchunk = pd->port_rcvegrbufs_perchunk;
|
|
kinfo->spi_rcv_egrperchunk = pd->port_rcvegrbufs_perchunk;
|
|
kinfo->spi_rcv_egrchunksize = kinfo->spi_rcv_egrbuftotlen /
|
|
kinfo->spi_rcv_egrchunksize = kinfo->spi_rcv_egrbuftotlen /
|
|
pd->port_rcvegrbuf_chunks;
|
|
pd->port_rcvegrbuf_chunks;
|
|
- kinfo->spi_tidcnt = dd->ipath_rcvtidcnt;
|
|
|
|
|
|
+ kinfo->spi_tidcnt = dd->ipath_rcvtidcnt / subport_cnt;
|
|
|
|
+ if (master)
|
|
|
|
+ kinfo->spi_tidcnt += dd->ipath_rcvtidcnt % subport_cnt;
|
|
/*
|
|
/*
|
|
* for this use, may be ipath_cfgports summed over all chips that
|
|
* for this use, may be ipath_cfgports summed over all chips that
|
|
* are are configured and present
|
|
* are are configured and present
|
|
@@ -118,31 +143,75 @@ static int ipath_get_base_info(struct ipath_portdata *pd,
|
|
* page_address() macro worked, but in 2.6.11, even that returns the
|
|
* page_address() macro worked, but in 2.6.11, even that returns the
|
|
* full 64 bit address (upper bits all 1's). So far, using the
|
|
* full 64 bit address (upper bits all 1's). So far, using the
|
|
* physical addresses (or chip offsets, for chip mapping) works, but
|
|
* physical addresses (or chip offsets, for chip mapping) works, but
|
|
- * no doubt some future kernel release will chang that, and we'll be
|
|
|
|
- * on to yet another method of dealing with this
|
|
|
|
|
|
+ * no doubt some future kernel release will change that, and we'll be
|
|
|
|
+ * on to yet another method of dealing with this.
|
|
*/
|
|
*/
|
|
kinfo->spi_rcvhdr_base = (u64) pd->port_rcvhdrq_phys;
|
|
kinfo->spi_rcvhdr_base = (u64) pd->port_rcvhdrq_phys;
|
|
- kinfo->spi_rcvhdr_tailaddr = (u64)pd->port_rcvhdrqtailaddr_phys;
|
|
|
|
|
|
+ kinfo->spi_rcvhdr_tailaddr = (u64) pd->port_rcvhdrqtailaddr_phys;
|
|
kinfo->spi_rcv_egrbufs = (u64) pd->port_rcvegr_phys;
|
|
kinfo->spi_rcv_egrbufs = (u64) pd->port_rcvegr_phys;
|
|
kinfo->spi_pioavailaddr = (u64) dd->ipath_pioavailregs_phys;
|
|
kinfo->spi_pioavailaddr = (u64) dd->ipath_pioavailregs_phys;
|
|
kinfo->spi_status = (u64) kinfo->spi_pioavailaddr +
|
|
kinfo->spi_status = (u64) kinfo->spi_pioavailaddr +
|
|
(void *) dd->ipath_statusp -
|
|
(void *) dd->ipath_statusp -
|
|
(void *) dd->ipath_pioavailregs_dma;
|
|
(void *) dd->ipath_pioavailregs_dma;
|
|
- kinfo->spi_piobufbase = (u64) pd->port_piobufs;
|
|
|
|
- kinfo->__spi_uregbase =
|
|
|
|
- dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
|
|
|
|
|
|
+ if (!shared) {
|
|
|
|
+ kinfo->spi_piocnt = dd->ipath_pbufsport;
|
|
|
|
+ kinfo->spi_piobufbase = (u64) pd->port_piobufs;
|
|
|
|
+ kinfo->__spi_uregbase = (u64) dd->ipath_uregbase +
|
|
|
|
+ dd->ipath_palign * pd->port_port;
|
|
|
|
+ } else if (master) {
|
|
|
|
+ kinfo->spi_piocnt = (dd->ipath_pbufsport / subport_cnt) +
|
|
|
|
+ (dd->ipath_pbufsport % subport_cnt);
|
|
|
|
+ /* Master's PIO buffers are after all the slave's */
|
|
|
|
+ kinfo->spi_piobufbase = (u64) pd->port_piobufs +
|
|
|
|
+ dd->ipath_palign *
|
|
|
|
+ (dd->ipath_pbufsport - kinfo->spi_piocnt);
|
|
|
|
+ kinfo->__spi_uregbase = (u64) dd->ipath_uregbase +
|
|
|
|
+ dd->ipath_palign * pd->port_port;
|
|
|
|
+ } else {
|
|
|
|
+ unsigned slave = subport_fp(fp) - 1;
|
|
|
|
+
|
|
|
|
+ kinfo->spi_piocnt = dd->ipath_pbufsport / subport_cnt;
|
|
|
|
+ kinfo->spi_piobufbase = (u64) pd->port_piobufs +
|
|
|
|
+ dd->ipath_palign * kinfo->spi_piocnt * slave;
|
|
|
|
+ kinfo->__spi_uregbase = ((u64) pd->subport_uregbase +
|
|
|
|
+ PAGE_SIZE * slave) & MMAP64_MASK;
|
|
|
|
|
|
- kinfo->spi_pioindex = dd->ipath_pbufsport * (pd->port_port - 1);
|
|
|
|
- kinfo->spi_piocnt = dd->ipath_pbufsport;
|
|
|
|
|
|
+ kinfo->spi_rcvhdr_base = ((u64) pd->subport_rcvhdr_base +
|
|
|
|
+ pd->port_rcvhdrq_size * slave) & MMAP64_MASK;
|
|
|
|
+ kinfo->spi_rcvhdr_tailaddr =
|
|
|
|
+ (u64) pd->port_rcvhdrqtailaddr_phys & MMAP64_MASK;
|
|
|
|
+ kinfo->spi_rcv_egrbufs = ((u64) pd->subport_rcvegrbuf +
|
|
|
|
+ dd->ipath_rcvegrcnt * dd->ipath_rcvegrbufsize * slave) &
|
|
|
|
+ MMAP64_MASK;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ kinfo->spi_pioindex = (kinfo->spi_piobufbase - dd->ipath_piobufbase) /
|
|
|
|
+ dd->ipath_palign;
|
|
kinfo->spi_pioalign = dd->ipath_palign;
|
|
kinfo->spi_pioalign = dd->ipath_palign;
|
|
|
|
|
|
kinfo->spi_qpair = IPATH_KD_QP;
|
|
kinfo->spi_qpair = IPATH_KD_QP;
|
|
kinfo->spi_piosize = dd->ipath_ibmaxlen;
|
|
kinfo->spi_piosize = dd->ipath_ibmaxlen;
|
|
kinfo->spi_mtu = dd->ipath_ibmaxlen; /* maxlen, not ibmtu */
|
|
kinfo->spi_mtu = dd->ipath_ibmaxlen; /* maxlen, not ibmtu */
|
|
kinfo->spi_port = pd->port_port;
|
|
kinfo->spi_port = pd->port_port;
|
|
|
|
+ kinfo->spi_subport = subport_fp(fp);
|
|
kinfo->spi_sw_version = IPATH_KERN_SWVERSION;
|
|
kinfo->spi_sw_version = IPATH_KERN_SWVERSION;
|
|
kinfo->spi_hw_version = dd->ipath_revision;
|
|
kinfo->spi_hw_version = dd->ipath_revision;
|
|
|
|
|
|
|
|
+ if (master) {
|
|
|
|
+ kinfo->spi_runtime_flags |= IPATH_RUNTIME_MASTER;
|
|
|
|
+ kinfo->spi_subport_uregbase =
|
|
|
|
+ (u64) pd->subport_uregbase & MMAP64_MASK;
|
|
|
|
+ kinfo->spi_subport_rcvegrbuf =
|
|
|
|
+ (u64) pd->subport_rcvegrbuf & MMAP64_MASK;
|
|
|
|
+ kinfo->spi_subport_rcvhdr_base =
|
|
|
|
+ (u64) pd->subport_rcvhdr_base & MMAP64_MASK;
|
|
|
|
+ ipath_cdbg(PROC, "port %u flags %x %llx %llx %llx\n",
|
|
|
|
+ kinfo->spi_port, kinfo->spi_runtime_flags,
|
|
|
|
+ (unsigned long long) kinfo->spi_subport_uregbase,
|
|
|
|
+ (unsigned long long) kinfo->spi_subport_rcvegrbuf,
|
|
|
|
+ (unsigned long long) kinfo->spi_subport_rcvhdr_base);
|
|
|
|
+ }
|
|
|
|
+
|
|
if (copy_to_user(ubase, kinfo, sizeof(*kinfo)))
|
|
if (copy_to_user(ubase, kinfo, sizeof(*kinfo)))
|
|
ret = -EFAULT;
|
|
ret = -EFAULT;
|
|
|
|
|
|
@@ -154,6 +223,7 @@ bail:
|
|
/**
|
|
/**
|
|
* ipath_tid_update - update a port TID
|
|
* ipath_tid_update - update a port TID
|
|
* @pd: the port
|
|
* @pd: the port
|
|
|
|
+ * @fp: the ipath device file
|
|
* @ti: the TID information
|
|
* @ti: the TID information
|
|
*
|
|
*
|
|
* The new implementation as of Oct 2004 is that the driver assigns
|
|
* The new implementation as of Oct 2004 is that the driver assigns
|
|
@@ -176,11 +246,11 @@ bail:
|
|
* virtually contiguous pages, that should change to improve
|
|
* virtually contiguous pages, that should change to improve
|
|
* performance.
|
|
* performance.
|
|
*/
|
|
*/
|
|
-static int ipath_tid_update(struct ipath_portdata *pd,
|
|
|
|
|
|
+static int ipath_tid_update(struct ipath_portdata *pd, struct file *fp,
|
|
const struct ipath_tid_info *ti)
|
|
const struct ipath_tid_info *ti)
|
|
{
|
|
{
|
|
int ret = 0, ntids;
|
|
int ret = 0, ntids;
|
|
- u32 tid, porttid, cnt, i, tidcnt;
|
|
|
|
|
|
+ u32 tid, porttid, cnt, i, tidcnt, tidoff;
|
|
u16 *tidlist;
|
|
u16 *tidlist;
|
|
struct ipath_devdata *dd = pd->port_dd;
|
|
struct ipath_devdata *dd = pd->port_dd;
|
|
u64 physaddr;
|
|
u64 physaddr;
|
|
@@ -188,6 +258,7 @@ static int ipath_tid_update(struct ipath_portdata *pd,
|
|
u64 __iomem *tidbase;
|
|
u64 __iomem *tidbase;
|
|
unsigned long tidmap[8];
|
|
unsigned long tidmap[8];
|
|
struct page **pagep = NULL;
|
|
struct page **pagep = NULL;
|
|
|
|
+ unsigned subport = subport_fp(fp);
|
|
|
|
|
|
if (!dd->ipath_pageshadow) {
|
|
if (!dd->ipath_pageshadow) {
|
|
ret = -ENOMEM;
|
|
ret = -ENOMEM;
|
|
@@ -204,20 +275,34 @@ static int ipath_tid_update(struct ipath_portdata *pd,
|
|
ret = -EFAULT;
|
|
ret = -EFAULT;
|
|
goto done;
|
|
goto done;
|
|
}
|
|
}
|
|
- tidcnt = dd->ipath_rcvtidcnt;
|
|
|
|
- if (cnt >= tidcnt) {
|
|
|
|
|
|
+ porttid = pd->port_port * dd->ipath_rcvtidcnt;
|
|
|
|
+ if (!pd->port_subport_cnt) {
|
|
|
|
+ tidcnt = dd->ipath_rcvtidcnt;
|
|
|
|
+ tid = pd->port_tidcursor;
|
|
|
|
+ tidoff = 0;
|
|
|
|
+ } else if (!subport) {
|
|
|
|
+ tidcnt = (dd->ipath_rcvtidcnt / pd->port_subport_cnt) +
|
|
|
|
+ (dd->ipath_rcvtidcnt % pd->port_subport_cnt);
|
|
|
|
+ tidoff = dd->ipath_rcvtidcnt - tidcnt;
|
|
|
|
+ porttid += tidoff;
|
|
|
|
+ tid = tidcursor_fp(fp);
|
|
|
|
+ } else {
|
|
|
|
+ tidcnt = dd->ipath_rcvtidcnt / pd->port_subport_cnt;
|
|
|
|
+ tidoff = tidcnt * (subport - 1);
|
|
|
|
+ porttid += tidoff;
|
|
|
|
+ tid = tidcursor_fp(fp);
|
|
|
|
+ }
|
|
|
|
+ if (cnt > tidcnt) {
|
|
/* make sure it all fits in port_tid_pg_list */
|
|
/* make sure it all fits in port_tid_pg_list */
|
|
dev_info(&dd->pcidev->dev, "Process tried to allocate %u "
|
|
dev_info(&dd->pcidev->dev, "Process tried to allocate %u "
|
|
"TIDs, only trying max (%u)\n", cnt, tidcnt);
|
|
"TIDs, only trying max (%u)\n", cnt, tidcnt);
|
|
cnt = tidcnt;
|
|
cnt = tidcnt;
|
|
}
|
|
}
|
|
- pagep = (struct page **)pd->port_tid_pg_list;
|
|
|
|
- tidlist = (u16 *) (&pagep[cnt]);
|
|
|
|
|
|
+ pagep = &((struct page **) pd->port_tid_pg_list)[tidoff];
|
|
|
|
+ tidlist = &((u16 *) &pagep[dd->ipath_rcvtidcnt])[tidoff];
|
|
|
|
|
|
memset(tidmap, 0, sizeof(tidmap));
|
|
memset(tidmap, 0, sizeof(tidmap));
|
|
- tid = pd->port_tidcursor;
|
|
|
|
/* before decrement; chip actual # */
|
|
/* before decrement; chip actual # */
|
|
- porttid = pd->port_port * tidcnt;
|
|
|
|
ntids = tidcnt;
|
|
ntids = tidcnt;
|
|
tidbase = (u64 __iomem *) (((char __iomem *) dd->ipath_kregbase) +
|
|
tidbase = (u64 __iomem *) (((char __iomem *) dd->ipath_kregbase) +
|
|
dd->ipath_rcvtidbase +
|
|
dd->ipath_rcvtidbase +
|
|
@@ -274,16 +359,19 @@ static int ipath_tid_update(struct ipath_portdata *pd,
|
|
ret = -ENOMEM;
|
|
ret = -ENOMEM;
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
- tidlist[i] = tid;
|
|
|
|
|
|
+ tidlist[i] = tid + tidoff;
|
|
ipath_cdbg(VERBOSE, "Updating idx %u to TID %u, "
|
|
ipath_cdbg(VERBOSE, "Updating idx %u to TID %u, "
|
|
- "vaddr %lx\n", i, tid, vaddr);
|
|
|
|
|
|
+ "vaddr %lx\n", i, tid + tidoff, vaddr);
|
|
/* we "know" system pages and TID pages are same size */
|
|
/* we "know" system pages and TID pages are same size */
|
|
dd->ipath_pageshadow[porttid + tid] = pagep[i];
|
|
dd->ipath_pageshadow[porttid + tid] = pagep[i];
|
|
|
|
+ dd->ipath_physshadow[porttid + tid] = ipath_map_page(
|
|
|
|
+ dd->pcidev, pagep[i], 0, PAGE_SIZE,
|
|
|
|
+ PCI_DMA_FROMDEVICE);
|
|
/*
|
|
/*
|
|
* don't need atomic or it's overhead
|
|
* don't need atomic or it's overhead
|
|
*/
|
|
*/
|
|
__set_bit(tid, tidmap);
|
|
__set_bit(tid, tidmap);
|
|
- physaddr = page_to_phys(pagep[i]);
|
|
|
|
|
|
+ physaddr = dd->ipath_physshadow[porttid + tid];
|
|
ipath_stats.sps_pagelocks++;
|
|
ipath_stats.sps_pagelocks++;
|
|
ipath_cdbg(VERBOSE,
|
|
ipath_cdbg(VERBOSE,
|
|
"TID %u, vaddr %lx, physaddr %llx pgp %p\n",
|
|
"TID %u, vaddr %lx, physaddr %llx pgp %p\n",
|
|
@@ -317,6 +405,9 @@ static int ipath_tid_update(struct ipath_portdata *pd,
|
|
tid);
|
|
tid);
|
|
dd->ipath_f_put_tid(dd, &tidbase[tid], 1,
|
|
dd->ipath_f_put_tid(dd, &tidbase[tid], 1,
|
|
dd->ipath_tidinvalid);
|
|
dd->ipath_tidinvalid);
|
|
|
|
+ pci_unmap_page(dd->pcidev,
|
|
|
|
+ dd->ipath_physshadow[porttid + tid],
|
|
|
|
+ PAGE_SIZE, PCI_DMA_FROMDEVICE);
|
|
dd->ipath_pageshadow[porttid + tid] = NULL;
|
|
dd->ipath_pageshadow[porttid + tid] = NULL;
|
|
ipath_stats.sps_pageunlocks++;
|
|
ipath_stats.sps_pageunlocks++;
|
|
}
|
|
}
|
|
@@ -341,7 +432,10 @@ static int ipath_tid_update(struct ipath_portdata *pd,
|
|
}
|
|
}
|
|
if (tid == tidcnt)
|
|
if (tid == tidcnt)
|
|
tid = 0;
|
|
tid = 0;
|
|
- pd->port_tidcursor = tid;
|
|
|
|
|
|
+ if (!pd->port_subport_cnt)
|
|
|
|
+ pd->port_tidcursor = tid;
|
|
|
|
+ else
|
|
|
|
+ tidcursor_fp(fp) = tid;
|
|
}
|
|
}
|
|
|
|
|
|
done:
|
|
done:
|
|
@@ -354,6 +448,7 @@ done:
|
|
/**
|
|
/**
|
|
* ipath_tid_free - free a port TID
|
|
* ipath_tid_free - free a port TID
|
|
* @pd: the port
|
|
* @pd: the port
|
|
|
|
+ * @subport: the subport
|
|
* @ti: the TID info
|
|
* @ti: the TID info
|
|
*
|
|
*
|
|
* right now we are unlocking one page at a time, but since
|
|
* right now we are unlocking one page at a time, but since
|
|
@@ -367,7 +462,7 @@ done:
|
|
* they pass in to us.
|
|
* they pass in to us.
|
|
*/
|
|
*/
|
|
|
|
|
|
-static int ipath_tid_free(struct ipath_portdata *pd,
|
|
|
|
|
|
+static int ipath_tid_free(struct ipath_portdata *pd, unsigned subport,
|
|
const struct ipath_tid_info *ti)
|
|
const struct ipath_tid_info *ti)
|
|
{
|
|
{
|
|
int ret = 0;
|
|
int ret = 0;
|
|
@@ -388,11 +483,20 @@ static int ipath_tid_free(struct ipath_portdata *pd,
|
|
}
|
|
}
|
|
|
|
|
|
porttid = pd->port_port * dd->ipath_rcvtidcnt;
|
|
porttid = pd->port_port * dd->ipath_rcvtidcnt;
|
|
|
|
+ if (!pd->port_subport_cnt)
|
|
|
|
+ tidcnt = dd->ipath_rcvtidcnt;
|
|
|
|
+ else if (!subport) {
|
|
|
|
+ tidcnt = (dd->ipath_rcvtidcnt / pd->port_subport_cnt) +
|
|
|
|
+ (dd->ipath_rcvtidcnt % pd->port_subport_cnt);
|
|
|
|
+ porttid += dd->ipath_rcvtidcnt - tidcnt;
|
|
|
|
+ } else {
|
|
|
|
+ tidcnt = dd->ipath_rcvtidcnt / pd->port_subport_cnt;
|
|
|
|
+ porttid += tidcnt * (subport - 1);
|
|
|
|
+ }
|
|
tidbase = (u64 __iomem *) ((char __iomem *)(dd->ipath_kregbase) +
|
|
tidbase = (u64 __iomem *) ((char __iomem *)(dd->ipath_kregbase) +
|
|
dd->ipath_rcvtidbase +
|
|
dd->ipath_rcvtidbase +
|
|
porttid * sizeof(*tidbase));
|
|
porttid * sizeof(*tidbase));
|
|
|
|
|
|
- tidcnt = dd->ipath_rcvtidcnt;
|
|
|
|
limit = sizeof(tidmap) * BITS_PER_BYTE;
|
|
limit = sizeof(tidmap) * BITS_PER_BYTE;
|
|
if (limit > tidcnt)
|
|
if (limit > tidcnt)
|
|
/* just in case size changes in future */
|
|
/* just in case size changes in future */
|
|
@@ -417,6 +521,9 @@ static int ipath_tid_free(struct ipath_portdata *pd,
|
|
pd->port_pid, tid);
|
|
pd->port_pid, tid);
|
|
dd->ipath_f_put_tid(dd, &tidbase[tid], 1,
|
|
dd->ipath_f_put_tid(dd, &tidbase[tid], 1,
|
|
dd->ipath_tidinvalid);
|
|
dd->ipath_tidinvalid);
|
|
|
|
+ pci_unmap_page(dd->pcidev,
|
|
|
|
+ dd->ipath_physshadow[porttid + tid],
|
|
|
|
+ PAGE_SIZE, PCI_DMA_FROMDEVICE);
|
|
ipath_release_user_pages(
|
|
ipath_release_user_pages(
|
|
&dd->ipath_pageshadow[porttid + tid], 1);
|
|
&dd->ipath_pageshadow[porttid + tid], 1);
|
|
dd->ipath_pageshadow[porttid + tid] = NULL;
|
|
dd->ipath_pageshadow[porttid + tid] = NULL;
|
|
@@ -581,20 +688,24 @@ bail:
|
|
/**
|
|
/**
|
|
* ipath_manage_rcvq - manage a port's receive queue
|
|
* ipath_manage_rcvq - manage a port's receive queue
|
|
* @pd: the port
|
|
* @pd: the port
|
|
|
|
+ * @subport: the subport
|
|
* @start_stop: action to carry out
|
|
* @start_stop: action to carry out
|
|
*
|
|
*
|
|
* start_stop == 0 disables receive on the port, for use in queue
|
|
* start_stop == 0 disables receive on the port, for use in queue
|
|
* overflow conditions. start_stop==1 re-enables, to be used to
|
|
* overflow conditions. start_stop==1 re-enables, to be used to
|
|
* re-init the software copy of the head register
|
|
* re-init the software copy of the head register
|
|
*/
|
|
*/
|
|
-static int ipath_manage_rcvq(struct ipath_portdata *pd, int start_stop)
|
|
|
|
|
|
+static int ipath_manage_rcvq(struct ipath_portdata *pd, unsigned subport,
|
|
|
|
+ int start_stop)
|
|
{
|
|
{
|
|
struct ipath_devdata *dd = pd->port_dd;
|
|
struct ipath_devdata *dd = pd->port_dd;
|
|
u64 tval;
|
|
u64 tval;
|
|
|
|
|
|
- ipath_cdbg(PROC, "%sabling rcv for unit %u port %u\n",
|
|
|
|
|
|
+ ipath_cdbg(PROC, "%sabling rcv for unit %u port %u:%u\n",
|
|
start_stop ? "en" : "dis", dd->ipath_unit,
|
|
start_stop ? "en" : "dis", dd->ipath_unit,
|
|
- pd->port_port);
|
|
|
|
|
|
+ pd->port_port, subport);
|
|
|
|
+ if (subport)
|
|
|
|
+ goto bail;
|
|
/* atomically clear receive enable port. */
|
|
/* atomically clear receive enable port. */
|
|
if (start_stop) {
|
|
if (start_stop) {
|
|
/*
|
|
/*
|
|
@@ -609,7 +720,7 @@ static int ipath_manage_rcvq(struct ipath_portdata *pd, int start_stop)
|
|
* updated and correct itself, even in the face of software
|
|
* updated and correct itself, even in the face of software
|
|
* bugs.
|
|
* bugs.
|
|
*/
|
|
*/
|
|
- *pd->port_rcvhdrtail_kvaddr = 0;
|
|
|
|
|
|
+ *(volatile u64 *)pd->port_rcvhdrtail_kvaddr = 0;
|
|
set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port,
|
|
set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port,
|
|
&dd->ipath_rcvctrl);
|
|
&dd->ipath_rcvctrl);
|
|
} else
|
|
} else
|
|
@@ -630,6 +741,7 @@ static int ipath_manage_rcvq(struct ipath_portdata *pd, int start_stop)
|
|
tval = ipath_read_ureg32(dd, ur_rcvhdrtail, pd->port_port);
|
|
tval = ipath_read_ureg32(dd, ur_rcvhdrtail, pd->port_port);
|
|
}
|
|
}
|
|
/* always; new head should be equal to new tail; see above */
|
|
/* always; new head should be equal to new tail; see above */
|
|
|
|
+bail:
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -687,6 +799,36 @@ static void ipath_clean_part_key(struct ipath_portdata *pd,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Initialize the port data with the receive buffer sizes
|
|
|
|
+ * so this can be done while the master port is locked.
|
|
|
|
+ * Otherwise, there is a race with a slave opening the port
|
|
|
|
+ * and seeing these fields uninitialized.
|
|
|
|
+ */
|
|
|
|
+static void init_user_egr_sizes(struct ipath_portdata *pd)
|
|
|
|
+{
|
|
|
|
+ struct ipath_devdata *dd = pd->port_dd;
|
|
|
|
+ unsigned egrperchunk, egrcnt, size;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * to avoid wasting a lot of memory, we allocate 32KB chunks of
|
|
|
|
+ * physically contiguous memory, advance through it until used up
|
|
|
|
+ * and then allocate more. Of course, we need memory to store those
|
|
|
|
+ * extra pointers, now. Started out with 256KB, but under heavy
|
|
|
|
+ * memory pressure (creating large files and then copying them over
|
|
|
|
+ * NFS while doing lots of MPI jobs), we hit some allocation
|
|
|
|
+ * failures, even though we can sleep... (2.6.10) Still get
|
|
|
|
+ * failures at 64K. 32K is the lowest we can go without wasting
|
|
|
|
+ * additional memory.
|
|
|
|
+ */
|
|
|
|
+ size = 0x8000;
|
|
|
|
+ egrperchunk = size / dd->ipath_rcvegrbufsize;
|
|
|
|
+ egrcnt = dd->ipath_rcvegrcnt;
|
|
|
|
+ pd->port_rcvegrbuf_chunks = (egrcnt + egrperchunk - 1) / egrperchunk;
|
|
|
|
+ pd->port_rcvegrbufs_perchunk = egrperchunk;
|
|
|
|
+ pd->port_rcvegrbuf_size = size;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* ipath_create_user_egr - allocate eager TID buffers
|
|
* ipath_create_user_egr - allocate eager TID buffers
|
|
* @pd: the port to allocate TID buffers for
|
|
* @pd: the port to allocate TID buffers for
|
|
@@ -702,7 +844,7 @@ static void ipath_clean_part_key(struct ipath_portdata *pd,
|
|
static int ipath_create_user_egr(struct ipath_portdata *pd)
|
|
static int ipath_create_user_egr(struct ipath_portdata *pd)
|
|
{
|
|
{
|
|
struct ipath_devdata *dd = pd->port_dd;
|
|
struct ipath_devdata *dd = pd->port_dd;
|
|
- unsigned e, egrcnt, alloced, egrperchunk, chunk, egrsize, egroff;
|
|
|
|
|
|
+ unsigned e, egrcnt, egrperchunk, chunk, egrsize, egroff;
|
|
size_t size;
|
|
size_t size;
|
|
int ret;
|
|
int ret;
|
|
gfp_t gfp_flags;
|
|
gfp_t gfp_flags;
|
|
@@ -722,31 +864,18 @@ static int ipath_create_user_egr(struct ipath_portdata *pd)
|
|
ipath_cdbg(VERBOSE, "Allocating %d egr buffers, at egrtid "
|
|
ipath_cdbg(VERBOSE, "Allocating %d egr buffers, at egrtid "
|
|
"offset %x, egrsize %u\n", egrcnt, egroff, egrsize);
|
|
"offset %x, egrsize %u\n", egrcnt, egroff, egrsize);
|
|
|
|
|
|
- /*
|
|
|
|
- * to avoid wasting a lot of memory, we allocate 32KB chunks of
|
|
|
|
- * physically contiguous memory, advance through it until used up
|
|
|
|
- * and then allocate more. Of course, we need memory to store those
|
|
|
|
- * extra pointers, now. Started out with 256KB, but under heavy
|
|
|
|
- * memory pressure (creating large files and then copying them over
|
|
|
|
- * NFS while doing lots of MPI jobs), we hit some allocation
|
|
|
|
- * failures, even though we can sleep... (2.6.10) Still get
|
|
|
|
- * failures at 64K. 32K is the lowest we can go without wasting
|
|
|
|
- * additional memory.
|
|
|
|
- */
|
|
|
|
- size = 0x8000;
|
|
|
|
- alloced = ALIGN(egrsize * egrcnt, size);
|
|
|
|
- egrperchunk = size / egrsize;
|
|
|
|
- chunk = (egrcnt + egrperchunk - 1) / egrperchunk;
|
|
|
|
- pd->port_rcvegrbuf_chunks = chunk;
|
|
|
|
- pd->port_rcvegrbufs_perchunk = egrperchunk;
|
|
|
|
- pd->port_rcvegrbuf_size = size;
|
|
|
|
- pd->port_rcvegrbuf = vmalloc(chunk * sizeof(pd->port_rcvegrbuf[0]));
|
|
|
|
|
|
+ chunk = pd->port_rcvegrbuf_chunks;
|
|
|
|
+ egrperchunk = pd->port_rcvegrbufs_perchunk;
|
|
|
|
+ size = pd->port_rcvegrbuf_size;
|
|
|
|
+ pd->port_rcvegrbuf = kmalloc(chunk * sizeof(pd->port_rcvegrbuf[0]),
|
|
|
|
+ GFP_KERNEL);
|
|
if (!pd->port_rcvegrbuf) {
|
|
if (!pd->port_rcvegrbuf) {
|
|
ret = -ENOMEM;
|
|
ret = -ENOMEM;
|
|
goto bail;
|
|
goto bail;
|
|
}
|
|
}
|
|
pd->port_rcvegrbuf_phys =
|
|
pd->port_rcvegrbuf_phys =
|
|
- vmalloc(chunk * sizeof(pd->port_rcvegrbuf_phys[0]));
|
|
|
|
|
|
+ kmalloc(chunk * sizeof(pd->port_rcvegrbuf_phys[0]),
|
|
|
|
+ GFP_KERNEL);
|
|
if (!pd->port_rcvegrbuf_phys) {
|
|
if (!pd->port_rcvegrbuf_phys) {
|
|
ret = -ENOMEM;
|
|
ret = -ENOMEM;
|
|
goto bail_rcvegrbuf;
|
|
goto bail_rcvegrbuf;
|
|
@@ -791,105 +920,23 @@ bail_rcvegrbuf_phys:
|
|
pd->port_rcvegrbuf_phys[e]);
|
|
pd->port_rcvegrbuf_phys[e]);
|
|
|
|
|
|
}
|
|
}
|
|
- vfree(pd->port_rcvegrbuf_phys);
|
|
|
|
|
|
+ kfree(pd->port_rcvegrbuf_phys);
|
|
pd->port_rcvegrbuf_phys = NULL;
|
|
pd->port_rcvegrbuf_phys = NULL;
|
|
bail_rcvegrbuf:
|
|
bail_rcvegrbuf:
|
|
- vfree(pd->port_rcvegrbuf);
|
|
|
|
|
|
+ kfree(pd->port_rcvegrbuf);
|
|
pd->port_rcvegrbuf = NULL;
|
|
pd->port_rcvegrbuf = NULL;
|
|
bail:
|
|
bail:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
-static int ipath_do_user_init(struct ipath_portdata *pd,
|
|
|
|
- const struct ipath_user_info *uinfo)
|
|
|
|
-{
|
|
|
|
- int ret = 0;
|
|
|
|
- struct ipath_devdata *dd = pd->port_dd;
|
|
|
|
- u32 head32;
|
|
|
|
-
|
|
|
|
- /* for now, if major version is different, bail */
|
|
|
|
- if ((uinfo->spu_userversion >> 16) != IPATH_USER_SWMAJOR) {
|
|
|
|
- dev_info(&dd->pcidev->dev,
|
|
|
|
- "User major version %d not same as driver "
|
|
|
|
- "major %d\n", uinfo->spu_userversion >> 16,
|
|
|
|
- IPATH_USER_SWMAJOR);
|
|
|
|
- ret = -ENODEV;
|
|
|
|
- goto done;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if ((uinfo->spu_userversion & 0xffff) != IPATH_USER_SWMINOR)
|
|
|
|
- ipath_dbg("User minor version %d not same as driver "
|
|
|
|
- "minor %d\n", uinfo->spu_userversion & 0xffff,
|
|
|
|
- IPATH_USER_SWMINOR);
|
|
|
|
-
|
|
|
|
- if (uinfo->spu_rcvhdrsize) {
|
|
|
|
- ret = ipath_setrcvhdrsize(dd, uinfo->spu_rcvhdrsize);
|
|
|
|
- if (ret)
|
|
|
|
- goto done;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* for now we do nothing with rcvhdrcnt: uinfo->spu_rcvhdrcnt */
|
|
|
|
-
|
|
|
|
- /* for right now, kernel piobufs are at end, so port 1 is at 0 */
|
|
|
|
- pd->port_piobufs = dd->ipath_piobufbase +
|
|
|
|
- dd->ipath_pbufsport * (pd->port_port -
|
|
|
|
- 1) * dd->ipath_palign;
|
|
|
|
- ipath_cdbg(VERBOSE, "Set base of piobufs for port %u to 0x%x\n",
|
|
|
|
- pd->port_port, pd->port_piobufs);
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * Now allocate the rcvhdr Q and eager TIDs; skip the TID
|
|
|
|
- * array for time being. If pd->port_port > chip-supported,
|
|
|
|
- * we need to do extra stuff here to handle by handling overflow
|
|
|
|
- * through port 0, someday
|
|
|
|
- */
|
|
|
|
- ret = ipath_create_rcvhdrq(dd, pd);
|
|
|
|
- if (!ret)
|
|
|
|
- ret = ipath_create_user_egr(pd);
|
|
|
|
- if (ret)
|
|
|
|
- goto done;
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * set the eager head register for this port to the current values
|
|
|
|
- * of the tail pointers, since we don't know if they were
|
|
|
|
- * updated on last use of the port.
|
|
|
|
- */
|
|
|
|
- head32 = ipath_read_ureg32(dd, ur_rcvegrindextail, pd->port_port);
|
|
|
|
- ipath_write_ureg(dd, ur_rcvegrindexhead, head32, pd->port_port);
|
|
|
|
- dd->ipath_lastegrheads[pd->port_port] = -1;
|
|
|
|
- dd->ipath_lastrcvhdrqtails[pd->port_port] = -1;
|
|
|
|
- ipath_cdbg(VERBOSE, "Wrote port%d egrhead %x from tail regs\n",
|
|
|
|
- pd->port_port, head32);
|
|
|
|
- pd->port_tidcursor = 0; /* start at beginning after open */
|
|
|
|
- /*
|
|
|
|
- * now enable the port; the tail registers will be written to memory
|
|
|
|
- * by the chip as soon as it sees the write to
|
|
|
|
- * dd->ipath_kregs->kr_rcvctrl. The update only happens on
|
|
|
|
- * transition from 0 to 1, so clear it first, then set it as part of
|
|
|
|
- * enabling the port. This will (very briefly) affect any other
|
|
|
|
- * open ports, but it shouldn't be long enough to be an issue.
|
|
|
|
- * We explictly set the in-memory copy to 0 beforehand, so we don't
|
|
|
|
- * have to wait to be sure the DMA update has happened.
|
|
|
|
- */
|
|
|
|
- *pd->port_rcvhdrtail_kvaddr = 0ULL;
|
|
|
|
- set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port,
|
|
|
|
- &dd->ipath_rcvctrl);
|
|
|
|
- ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
|
|
|
|
- dd->ipath_rcvctrl & ~INFINIPATH_R_TAILUPD);
|
|
|
|
- ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
|
|
|
|
- dd->ipath_rcvctrl);
|
|
|
|
-done:
|
|
|
|
- return ret;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
|
|
|
|
/* common code for the mappings on dma_alloc_coherent mem */
|
|
/* common code for the mappings on dma_alloc_coherent mem */
|
|
static int ipath_mmap_mem(struct vm_area_struct *vma,
|
|
static int ipath_mmap_mem(struct vm_area_struct *vma,
|
|
- struct ipath_portdata *pd, unsigned len,
|
|
|
|
- int write_ok, dma_addr_t addr, char *what)
|
|
|
|
|
|
+ struct ipath_portdata *pd, unsigned len, int write_ok,
|
|
|
|
+ void *kvaddr, char *what)
|
|
{
|
|
{
|
|
struct ipath_devdata *dd = pd->port_dd;
|
|
struct ipath_devdata *dd = pd->port_dd;
|
|
- unsigned pfn = (unsigned long)addr >> PAGE_SHIFT;
|
|
|
|
|
|
+ unsigned long pfn;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
if ((vma->vm_end - vma->vm_start) > len) {
|
|
if ((vma->vm_end - vma->vm_start) > len) {
|
|
@@ -912,17 +959,17 @@ static int ipath_mmap_mem(struct vm_area_struct *vma,
|
|
vma->vm_flags &= ~VM_MAYWRITE;
|
|
vma->vm_flags &= ~VM_MAYWRITE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ pfn = virt_to_phys(kvaddr) >> PAGE_SHIFT;
|
|
ret = remap_pfn_range(vma, vma->vm_start, pfn,
|
|
ret = remap_pfn_range(vma, vma->vm_start, pfn,
|
|
len, vma->vm_page_prot);
|
|
len, vma->vm_page_prot);
|
|
if (ret)
|
|
if (ret)
|
|
- dev_info(&dd->pcidev->dev,
|
|
|
|
- "%s port%u mmap of %lx, %x bytes r%c failed: %d\n",
|
|
|
|
- what, pd->port_port, (unsigned long)addr, len,
|
|
|
|
- write_ok?'w':'o', ret);
|
|
|
|
|
|
+ dev_info(&dd->pcidev->dev, "%s port%u mmap of %lx, %x "
|
|
|
|
+ "bytes r%c failed: %d\n", what, pd->port_port,
|
|
|
|
+ pfn, len, write_ok?'w':'o', ret);
|
|
else
|
|
else
|
|
- ipath_cdbg(VERBOSE, "%s port%u mmaped %lx, %x bytes r%c\n",
|
|
|
|
- what, pd->port_port, (unsigned long)addr, len,
|
|
|
|
- write_ok?'w':'o');
|
|
|
|
|
|
+ ipath_cdbg(VERBOSE, "%s port%u mmaped %lx, %x bytes "
|
|
|
|
+ "r%c\n", what, pd->port_port, pfn, len,
|
|
|
|
+ write_ok?'w':'o');
|
|
bail:
|
|
bail:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -957,7 +1004,8 @@ static int mmap_ureg(struct vm_area_struct *vma, struct ipath_devdata *dd,
|
|
|
|
|
|
static int mmap_piobufs(struct vm_area_struct *vma,
|
|
static int mmap_piobufs(struct vm_area_struct *vma,
|
|
struct ipath_devdata *dd,
|
|
struct ipath_devdata *dd,
|
|
- struct ipath_portdata *pd)
|
|
|
|
|
|
+ struct ipath_portdata *pd,
|
|
|
|
+ unsigned piobufs, unsigned piocnt)
|
|
{
|
|
{
|
|
unsigned long phys;
|
|
unsigned long phys;
|
|
int ret;
|
|
int ret;
|
|
@@ -968,16 +1016,15 @@ static int mmap_piobufs(struct vm_area_struct *vma,
|
|
* process data, and catches users who might try to read the i/o
|
|
* process data, and catches users who might try to read the i/o
|
|
* space due to a bug.
|
|
* space due to a bug.
|
|
*/
|
|
*/
|
|
- if ((vma->vm_end - vma->vm_start) >
|
|
|
|
- (dd->ipath_pbufsport * dd->ipath_palign)) {
|
|
|
|
|
|
+ if ((vma->vm_end - vma->vm_start) > (piocnt * dd->ipath_palign)) {
|
|
dev_info(&dd->pcidev->dev, "FAIL mmap piobufs: "
|
|
dev_info(&dd->pcidev->dev, "FAIL mmap piobufs: "
|
|
"reqlen %lx > PAGE\n",
|
|
"reqlen %lx > PAGE\n",
|
|
vma->vm_end - vma->vm_start);
|
|
vma->vm_end - vma->vm_start);
|
|
- ret = -EFAULT;
|
|
|
|
|
|
+ ret = -EINVAL;
|
|
goto bail;
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
|
|
- phys = dd->ipath_physaddr + pd->port_piobufs;
|
|
|
|
|
|
+ phys = dd->ipath_physaddr + piobufs;
|
|
|
|
|
|
/*
|
|
/*
|
|
* Don't mark this as non-cached, or we don't get the
|
|
* Don't mark this as non-cached, or we don't get the
|
|
@@ -1011,7 +1058,7 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma,
|
|
struct ipath_devdata *dd = pd->port_dd;
|
|
struct ipath_devdata *dd = pd->port_dd;
|
|
unsigned long start, size;
|
|
unsigned long start, size;
|
|
size_t total_size, i;
|
|
size_t total_size, i;
|
|
- dma_addr_t *phys;
|
|
|
|
|
|
+ unsigned long pfn;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
size = pd->port_rcvegrbuf_size;
|
|
size = pd->port_rcvegrbuf_size;
|
|
@@ -1021,7 +1068,7 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma,
|
|
"reqlen %lx > actual %lx\n",
|
|
"reqlen %lx > actual %lx\n",
|
|
vma->vm_end - vma->vm_start,
|
|
vma->vm_end - vma->vm_start,
|
|
(unsigned long) total_size);
|
|
(unsigned long) total_size);
|
|
- ret = -EFAULT;
|
|
|
|
|
|
+ ret = -EINVAL;
|
|
goto bail;
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1035,11 +1082,11 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma,
|
|
vma->vm_flags &= ~VM_MAYWRITE;
|
|
vma->vm_flags &= ~VM_MAYWRITE;
|
|
|
|
|
|
start = vma->vm_start;
|
|
start = vma->vm_start;
|
|
- phys = pd->port_rcvegrbuf_phys;
|
|
|
|
|
|
|
|
for (i = 0; i < pd->port_rcvegrbuf_chunks; i++, start += size) {
|
|
for (i = 0; i < pd->port_rcvegrbuf_chunks; i++, start += size) {
|
|
- ret = remap_pfn_range(vma, start, phys[i] >> PAGE_SHIFT,
|
|
|
|
- size, vma->vm_page_prot);
|
|
|
|
|
|
+ pfn = virt_to_phys(pd->port_rcvegrbuf[i]) >> PAGE_SHIFT;
|
|
|
|
+ ret = remap_pfn_range(vma, start, pfn, size,
|
|
|
|
+ vma->vm_page_prot);
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
goto bail;
|
|
goto bail;
|
|
}
|
|
}
|
|
@@ -1049,6 +1096,122 @@ bail:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * ipath_file_vma_nopage - handle a VMA page fault.
|
|
|
|
+ */
|
|
|
|
+static struct page *ipath_file_vma_nopage(struct vm_area_struct *vma,
|
|
|
|
+ unsigned long address, int *type)
|
|
|
|
+{
|
|
|
|
+ unsigned long offset = address - vma->vm_start;
|
|
|
|
+ struct page *page = NOPAGE_SIGBUS;
|
|
|
|
+ void *pageptr;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Convert the vmalloc address into a struct page.
|
|
|
|
+ */
|
|
|
|
+ pageptr = (void *)(offset + (vma->vm_pgoff << PAGE_SHIFT));
|
|
|
|
+ page = vmalloc_to_page(pageptr);
|
|
|
|
+ if (!page)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ /* Increment the reference count. */
|
|
|
|
+ get_page(page);
|
|
|
|
+ if (type)
|
|
|
|
+ *type = VM_FAULT_MINOR;
|
|
|
|
+out:
|
|
|
|
+ return page;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct vm_operations_struct ipath_file_vm_ops = {
|
|
|
|
+ .nopage = ipath_file_vma_nopage,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int mmap_kvaddr(struct vm_area_struct *vma, u64 pgaddr,
|
|
|
|
+ struct ipath_portdata *pd, unsigned subport)
|
|
|
|
+{
|
|
|
|
+ unsigned long len;
|
|
|
|
+ struct ipath_devdata *dd;
|
|
|
|
+ void *addr;
|
|
|
|
+ size_t size;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ /* If the port is not shared, all addresses should be physical */
|
|
|
|
+ if (!pd->port_subport_cnt) {
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto bail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dd = pd->port_dd;
|
|
|
|
+ size = pd->port_rcvegrbuf_chunks * pd->port_rcvegrbuf_size;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Master has all the slave uregbase, rcvhdrq, and
|
|
|
|
+ * rcvegrbufs mmapped.
|
|
|
|
+ */
|
|
|
|
+ if (subport == 0) {
|
|
|
|
+ unsigned num_slaves = pd->port_subport_cnt - 1;
|
|
|
|
+
|
|
|
|
+ if (pgaddr == ((u64) pd->subport_uregbase & MMAP64_MASK)) {
|
|
|
|
+ addr = pd->subport_uregbase;
|
|
|
|
+ size = PAGE_SIZE * num_slaves;
|
|
|
|
+ } else if (pgaddr == ((u64) pd->subport_rcvhdr_base &
|
|
|
|
+ MMAP64_MASK)) {
|
|
|
|
+ addr = pd->subport_rcvhdr_base;
|
|
|
|
+ size = pd->port_rcvhdrq_size * num_slaves;
|
|
|
|
+ } else if (pgaddr == ((u64) pd->subport_rcvegrbuf &
|
|
|
|
+ MMAP64_MASK)) {
|
|
|
|
+ addr = pd->subport_rcvegrbuf;
|
|
|
|
+ size *= num_slaves;
|
|
|
|
+ } else {
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto bail;
|
|
|
|
+ }
|
|
|
|
+ } else if (pgaddr == (((u64) pd->subport_uregbase +
|
|
|
|
+ PAGE_SIZE * (subport - 1)) & MMAP64_MASK)) {
|
|
|
|
+ addr = pd->subport_uregbase + PAGE_SIZE * (subport - 1);
|
|
|
|
+ size = PAGE_SIZE;
|
|
|
|
+ } else if (pgaddr == (((u64) pd->subport_rcvhdr_base +
|
|
|
|
+ pd->port_rcvhdrq_size * (subport - 1)) &
|
|
|
|
+ MMAP64_MASK)) {
|
|
|
|
+ addr = pd->subport_rcvhdr_base +
|
|
|
|
+ pd->port_rcvhdrq_size * (subport - 1);
|
|
|
|
+ size = pd->port_rcvhdrq_size;
|
|
|
|
+ } else if (pgaddr == (((u64) pd->subport_rcvegrbuf +
|
|
|
|
+ size * (subport - 1)) & MMAP64_MASK)) {
|
|
|
|
+ addr = pd->subport_rcvegrbuf + size * (subport - 1);
|
|
|
|
+ /* rcvegrbufs are read-only on the slave */
|
|
|
|
+ if (vma->vm_flags & VM_WRITE) {
|
|
|
|
+ dev_info(&dd->pcidev->dev,
|
|
|
|
+ "Can't map eager buffers as "
|
|
|
|
+ "writable (flags=%lx)\n", vma->vm_flags);
|
|
|
|
+ ret = -EPERM;
|
|
|
|
+ goto bail;
|
|
|
|
+ }
|
|
|
|
+ /*
|
|
|
|
+ * Don't allow permission to later change to writeable
|
|
|
|
+ * with mprotect.
|
|
|
|
+ */
|
|
|
|
+ vma->vm_flags &= ~VM_MAYWRITE;
|
|
|
|
+ } else {
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto bail;
|
|
|
|
+ }
|
|
|
|
+ len = vma->vm_end - vma->vm_start;
|
|
|
|
+ if (len > size) {
|
|
|
|
+ ipath_cdbg(MM, "FAIL: reqlen %lx > %zx\n", len, size);
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto bail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ vma->vm_pgoff = (unsigned long) addr >> PAGE_SHIFT;
|
|
|
|
+ vma->vm_ops = &ipath_file_vm_ops;
|
|
|
|
+ vma->vm_flags |= VM_RESERVED | VM_DONTEXPAND;
|
|
|
|
+ ret = 0;
|
|
|
|
+
|
|
|
|
+bail:
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* ipath_mmap - mmap various structures into user space
|
|
* ipath_mmap - mmap various structures into user space
|
|
* @fp: the file pointer
|
|
* @fp: the file pointer
|
|
@@ -1064,73 +1227,99 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma)
|
|
struct ipath_portdata *pd;
|
|
struct ipath_portdata *pd;
|
|
struct ipath_devdata *dd;
|
|
struct ipath_devdata *dd;
|
|
u64 pgaddr, ureg;
|
|
u64 pgaddr, ureg;
|
|
|
|
+ unsigned piobufs, piocnt;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
pd = port_fp(fp);
|
|
pd = port_fp(fp);
|
|
|
|
+ if (!pd) {
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto bail;
|
|
|
|
+ }
|
|
dd = pd->port_dd;
|
|
dd = pd->port_dd;
|
|
|
|
|
|
/*
|
|
/*
|
|
* This is the ipath_do_user_init() code, mapping the shared buffers
|
|
* This is the ipath_do_user_init() code, mapping the shared buffers
|
|
* into the user process. The address referred to by vm_pgoff is the
|
|
* into the user process. The address referred to by vm_pgoff is the
|
|
- * virtual, not physical, address; we only do one mmap for each
|
|
|
|
- * space mapped.
|
|
|
|
|
|
+ * file offset passed via mmap(). For shared ports, this is the
|
|
|
|
+ * kernel vmalloc() address of the pages to share with the master.
|
|
|
|
+ * For non-shared or master ports, this is a physical address.
|
|
|
|
+ * We only do one mmap for each space mapped.
|
|
*/
|
|
*/
|
|
pgaddr = vma->vm_pgoff << PAGE_SHIFT;
|
|
pgaddr = vma->vm_pgoff << PAGE_SHIFT;
|
|
|
|
|
|
/*
|
|
/*
|
|
- * Must fit in 40 bits for our hardware; some checked elsewhere,
|
|
|
|
- * but we'll be paranoid. Check for 0 is mostly in case one of the
|
|
|
|
- * allocations failed, but user called mmap anyway. We want to catch
|
|
|
|
- * that before it can match.
|
|
|
|
|
|
+ * Check for 0 in case one of the allocations failed, but user
|
|
|
|
+ * called mmap anyway.
|
|
*/
|
|
*/
|
|
- if (!pgaddr || pgaddr >= (1ULL<<40)) {
|
|
|
|
- ipath_dev_err(dd, "Bad phys addr %llx, start %lx, end %lx\n",
|
|
|
|
- (unsigned long long)pgaddr, vma->vm_start, vma->vm_end);
|
|
|
|
- return -EINVAL;
|
|
|
|
|
|
+ if (!pgaddr) {
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto bail;
|
|
}
|
|
}
|
|
|
|
|
|
- /* just the offset of the port user registers, not physical addr */
|
|
|
|
- ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
|
|
|
|
-
|
|
|
|
- ipath_cdbg(MM, "ushare: pgaddr %llx vm_start=%lx, vmlen %lx\n",
|
|
|
|
|
|
+ ipath_cdbg(MM, "pgaddr %llx vm_start=%lx len %lx port %u:%u:%u\n",
|
|
(unsigned long long) pgaddr, vma->vm_start,
|
|
(unsigned long long) pgaddr, vma->vm_start,
|
|
- vma->vm_end - vma->vm_start);
|
|
|
|
|
|
+ vma->vm_end - vma->vm_start, dd->ipath_unit,
|
|
|
|
+ pd->port_port, subport_fp(fp));
|
|
|
|
|
|
- if (vma->vm_start & (PAGE_SIZE-1)) {
|
|
|
|
- ipath_dev_err(dd,
|
|
|
|
- "vm_start not aligned: %lx, end=%lx phys %lx\n",
|
|
|
|
- vma->vm_start, vma->vm_end, (unsigned long)pgaddr);
|
|
|
|
- ret = -EINVAL;
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Physical addresses must fit in 40 bits for our hardware.
|
|
|
|
+ * Check for kernel virtual addresses first, anything else must
|
|
|
|
+ * match a HW or memory address.
|
|
|
|
+ */
|
|
|
|
+ if (pgaddr >= (1ULL<<40)) {
|
|
|
|
+ ret = mmap_kvaddr(vma, pgaddr, pd, subport_fp(fp));
|
|
|
|
+ goto bail;
|
|
}
|
|
}
|
|
- else if (pgaddr == ureg)
|
|
|
|
|
|
+
|
|
|
|
+ if (!pd->port_subport_cnt) {
|
|
|
|
+ /* port is not shared */
|
|
|
|
+ ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
|
|
|
|
+ piocnt = dd->ipath_pbufsport;
|
|
|
|
+ piobufs = pd->port_piobufs;
|
|
|
|
+ } else if (!subport_fp(fp)) {
|
|
|
|
+ /* caller is the master */
|
|
|
|
+ ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
|
|
|
|
+ piocnt = (dd->ipath_pbufsport / pd->port_subport_cnt) +
|
|
|
|
+ (dd->ipath_pbufsport % pd->port_subport_cnt);
|
|
|
|
+ piobufs = pd->port_piobufs +
|
|
|
|
+ dd->ipath_palign * (dd->ipath_pbufsport - piocnt);
|
|
|
|
+ } else {
|
|
|
|
+ unsigned slave = subport_fp(fp) - 1;
|
|
|
|
+
|
|
|
|
+ /* caller is a slave */
|
|
|
|
+ ureg = 0;
|
|
|
|
+ piocnt = dd->ipath_pbufsport / pd->port_subport_cnt;
|
|
|
|
+ piobufs = pd->port_piobufs + dd->ipath_palign * piocnt * slave;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (pgaddr == ureg)
|
|
ret = mmap_ureg(vma, dd, ureg);
|
|
ret = mmap_ureg(vma, dd, ureg);
|
|
- else if (pgaddr == pd->port_piobufs)
|
|
|
|
- ret = mmap_piobufs(vma, dd, pd);
|
|
|
|
- else if (pgaddr == (u64) pd->port_rcvegr_phys)
|
|
|
|
|
|
+ else if (pgaddr == piobufs)
|
|
|
|
+ ret = mmap_piobufs(vma, dd, pd, piobufs, piocnt);
|
|
|
|
+ else if (pgaddr == dd->ipath_pioavailregs_phys)
|
|
|
|
+ /* in-memory copy of pioavail registers */
|
|
|
|
+ ret = ipath_mmap_mem(vma, pd, PAGE_SIZE, 0,
|
|
|
|
+ (void *) dd->ipath_pioavailregs_dma,
|
|
|
|
+ "pioavail registers");
|
|
|
|
+ else if (subport_fp(fp))
|
|
|
|
+ /* Subports don't mmap the physical receive buffers */
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ else if (pgaddr == pd->port_rcvegr_phys)
|
|
ret = mmap_rcvegrbufs(vma, pd);
|
|
ret = mmap_rcvegrbufs(vma, pd);
|
|
- else if (pgaddr == (u64) pd->port_rcvhdrq_phys) {
|
|
|
|
|
|
+ else if (pgaddr == (u64) pd->port_rcvhdrq_phys)
|
|
/*
|
|
/*
|
|
* The rcvhdrq itself; readonly except on HT (so have
|
|
* The rcvhdrq itself; readonly except on HT (so have
|
|
* to allow writable mapping), multiple pages, contiguous
|
|
* to allow writable mapping), multiple pages, contiguous
|
|
* from an i/o perspective.
|
|
* from an i/o perspective.
|
|
*/
|
|
*/
|
|
- unsigned total_size =
|
|
|
|
- ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize
|
|
|
|
- * sizeof(u32), PAGE_SIZE);
|
|
|
|
- ret = ipath_mmap_mem(vma, pd, total_size, 1,
|
|
|
|
- pd->port_rcvhdrq_phys,
|
|
|
|
|
|
+ ret = ipath_mmap_mem(vma, pd, pd->port_rcvhdrq_size, 1,
|
|
|
|
+ pd->port_rcvhdrq,
|
|
"rcvhdrq");
|
|
"rcvhdrq");
|
|
- }
|
|
|
|
- else if (pgaddr == (u64)pd->port_rcvhdrqtailaddr_phys)
|
|
|
|
|
|
+ else if (pgaddr == (u64) pd->port_rcvhdrqtailaddr_phys)
|
|
/* in-memory copy of rcvhdrq tail register */
|
|
/* in-memory copy of rcvhdrq tail register */
|
|
ret = ipath_mmap_mem(vma, pd, PAGE_SIZE, 0,
|
|
ret = ipath_mmap_mem(vma, pd, PAGE_SIZE, 0,
|
|
- pd->port_rcvhdrqtailaddr_phys,
|
|
|
|
|
|
+ pd->port_rcvhdrtail_kvaddr,
|
|
"rcvhdrq tail");
|
|
"rcvhdrq tail");
|
|
- else if (pgaddr == dd->ipath_pioavailregs_phys)
|
|
|
|
- /* in-memory copy of pioavail registers */
|
|
|
|
- ret = ipath_mmap_mem(vma, pd, PAGE_SIZE, 0,
|
|
|
|
- dd->ipath_pioavailregs_phys,
|
|
|
|
- "pioavail registers");
|
|
|
|
else
|
|
else
|
|
ret = -EINVAL;
|
|
ret = -EINVAL;
|
|
|
|
|
|
@@ -1138,9 +1327,10 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma)
|
|
|
|
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
dev_info(&dd->pcidev->dev,
|
|
dev_info(&dd->pcidev->dev,
|
|
- "Failure %d on addr %lx, off %lx\n",
|
|
|
|
- -ret, vma->vm_start, vma->vm_pgoff);
|
|
|
|
-
|
|
|
|
|
|
+ "Failure %d on off %llx len %lx\n",
|
|
|
|
+ -ret, (unsigned long long)pgaddr,
|
|
|
|
+ vma->vm_end - vma->vm_start);
|
|
|
|
+bail:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1154,6 +1344,8 @@ static unsigned int ipath_poll(struct file *fp,
|
|
struct ipath_devdata *dd;
|
|
struct ipath_devdata *dd;
|
|
|
|
|
|
pd = port_fp(fp);
|
|
pd = port_fp(fp);
|
|
|
|
+ if (!pd)
|
|
|
|
+ goto bail;
|
|
dd = pd->port_dd;
|
|
dd = pd->port_dd;
|
|
|
|
|
|
bit = pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT;
|
|
bit = pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT;
|
|
@@ -1176,7 +1368,7 @@ static unsigned int ipath_poll(struct file *fp,
|
|
|
|
|
|
if (tail == head) {
|
|
if (tail == head) {
|
|
set_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag);
|
|
set_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag);
|
|
- if(dd->ipath_rhdrhead_intr_off) /* arm rcv interrupt */
|
|
|
|
|
|
+ if (dd->ipath_rhdrhead_intr_off) /* arm rcv interrupt */
|
|
(void)ipath_write_ureg(dd, ur_rcvhdrhead,
|
|
(void)ipath_write_ureg(dd, ur_rcvhdrhead,
|
|
dd->ipath_rhdrhead_intr_off
|
|
dd->ipath_rhdrhead_intr_off
|
|
| head, pd->port_port);
|
|
| head, pd->port_port);
|
|
@@ -1200,18 +1392,80 @@ static unsigned int ipath_poll(struct file *fp,
|
|
ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
|
|
ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
|
|
dd->ipath_rcvctrl);
|
|
dd->ipath_rcvctrl);
|
|
|
|
|
|
|
|
+bail:
|
|
return pollflag;
|
|
return pollflag;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int init_subports(struct ipath_devdata *dd,
|
|
|
|
+ struct ipath_portdata *pd,
|
|
|
|
+ const struct ipath_user_info *uinfo)
|
|
|
|
+{
|
|
|
|
+ int ret = 0;
|
|
|
|
+ unsigned num_slaves;
|
|
|
|
+ size_t size;
|
|
|
|
+
|
|
|
|
+ /* Old user binaries don't know about subports */
|
|
|
|
+ if ((uinfo->spu_userversion & 0xffff) != IPATH_USER_SWMINOR)
|
|
|
|
+ goto bail;
|
|
|
|
+ /*
|
|
|
|
+ * If the user is requesting zero or one port,
|
|
|
|
+ * skip the subport allocation.
|
|
|
|
+ */
|
|
|
|
+ if (uinfo->spu_subport_cnt <= 1)
|
|
|
|
+ goto bail;
|
|
|
|
+ if (uinfo->spu_subport_cnt > 4) {
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto bail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ num_slaves = uinfo->spu_subport_cnt - 1;
|
|
|
|
+ pd->subport_uregbase = vmalloc(PAGE_SIZE * num_slaves);
|
|
|
|
+ if (!pd->subport_uregbase) {
|
|
|
|
+ ret = -ENOMEM;
|
|
|
|
+ goto bail;
|
|
|
|
+ }
|
|
|
|
+ /* Note: pd->port_rcvhdrq_size isn't initialized yet. */
|
|
|
|
+ size = ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize *
|
|
|
|
+ sizeof(u32), PAGE_SIZE) * num_slaves;
|
|
|
|
+ pd->subport_rcvhdr_base = vmalloc(size);
|
|
|
|
+ if (!pd->subport_rcvhdr_base) {
|
|
|
|
+ ret = -ENOMEM;
|
|
|
|
+ goto bail_ureg;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pd->subport_rcvegrbuf = vmalloc(pd->port_rcvegrbuf_chunks *
|
|
|
|
+ pd->port_rcvegrbuf_size *
|
|
|
|
+ num_slaves);
|
|
|
|
+ if (!pd->subport_rcvegrbuf) {
|
|
|
|
+ ret = -ENOMEM;
|
|
|
|
+ goto bail_rhdr;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pd->port_subport_cnt = uinfo->spu_subport_cnt;
|
|
|
|
+ pd->port_subport_id = uinfo->spu_subport_id;
|
|
|
|
+ pd->active_slaves = 1;
|
|
|
|
+ goto bail;
|
|
|
|
+
|
|
|
|
+bail_rhdr:
|
|
|
|
+ vfree(pd->subport_rcvhdr_base);
|
|
|
|
+bail_ureg:
|
|
|
|
+ vfree(pd->subport_uregbase);
|
|
|
|
+ pd->subport_uregbase = NULL;
|
|
|
|
+bail:
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
static int try_alloc_port(struct ipath_devdata *dd, int port,
|
|
static int try_alloc_port(struct ipath_devdata *dd, int port,
|
|
- struct file *fp)
|
|
|
|
|
|
+ struct file *fp,
|
|
|
|
+ const struct ipath_user_info *uinfo)
|
|
{
|
|
{
|
|
|
|
+ struct ipath_portdata *pd;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
- if (!dd->ipath_pd[port]) {
|
|
|
|
- void *p, *ptmp;
|
|
|
|
|
|
+ if (!(pd = dd->ipath_pd[port])) {
|
|
|
|
+ void *ptmp;
|
|
|
|
|
|
- p = kzalloc(sizeof(struct ipath_portdata), GFP_KERNEL);
|
|
|
|
|
|
+ pd = kzalloc(sizeof(struct ipath_portdata), GFP_KERNEL);
|
|
|
|
|
|
/*
|
|
/*
|
|
* Allocate memory for use in ipath_tid_update() just once
|
|
* Allocate memory for use in ipath_tid_update() just once
|
|
@@ -1221,34 +1475,36 @@ static int try_alloc_port(struct ipath_devdata *dd, int port,
|
|
ptmp = kmalloc(dd->ipath_rcvtidcnt * sizeof(u16) +
|
|
ptmp = kmalloc(dd->ipath_rcvtidcnt * sizeof(u16) +
|
|
dd->ipath_rcvtidcnt * sizeof(struct page **),
|
|
dd->ipath_rcvtidcnt * sizeof(struct page **),
|
|
GFP_KERNEL);
|
|
GFP_KERNEL);
|
|
- if (!p || !ptmp) {
|
|
|
|
|
|
+ if (!pd || !ptmp) {
|
|
ipath_dev_err(dd, "Unable to allocate portdata "
|
|
ipath_dev_err(dd, "Unable to allocate portdata "
|
|
"memory, failing open\n");
|
|
"memory, failing open\n");
|
|
ret = -ENOMEM;
|
|
ret = -ENOMEM;
|
|
- kfree(p);
|
|
|
|
|
|
+ kfree(pd);
|
|
kfree(ptmp);
|
|
kfree(ptmp);
|
|
goto bail;
|
|
goto bail;
|
|
}
|
|
}
|
|
- dd->ipath_pd[port] = p;
|
|
|
|
|
|
+ dd->ipath_pd[port] = pd;
|
|
dd->ipath_pd[port]->port_port = port;
|
|
dd->ipath_pd[port]->port_port = port;
|
|
dd->ipath_pd[port]->port_dd = dd;
|
|
dd->ipath_pd[port]->port_dd = dd;
|
|
dd->ipath_pd[port]->port_tid_pg_list = ptmp;
|
|
dd->ipath_pd[port]->port_tid_pg_list = ptmp;
|
|
init_waitqueue_head(&dd->ipath_pd[port]->port_wait);
|
|
init_waitqueue_head(&dd->ipath_pd[port]->port_wait);
|
|
}
|
|
}
|
|
- if (!dd->ipath_pd[port]->port_cnt) {
|
|
|
|
- dd->ipath_pd[port]->port_cnt = 1;
|
|
|
|
- fp->private_data = (void *) dd->ipath_pd[port];
|
|
|
|
|
|
+ if (!pd->port_cnt) {
|
|
|
|
+ pd->userversion = uinfo->spu_userversion;
|
|
|
|
+ init_user_egr_sizes(pd);
|
|
|
|
+ if ((ret = init_subports(dd, pd, uinfo)) != 0)
|
|
|
|
+ goto bail;
|
|
ipath_cdbg(PROC, "%s[%u] opened unit:port %u:%u\n",
|
|
ipath_cdbg(PROC, "%s[%u] opened unit:port %u:%u\n",
|
|
current->comm, current->pid, dd->ipath_unit,
|
|
current->comm, current->pid, dd->ipath_unit,
|
|
port);
|
|
port);
|
|
- dd->ipath_pd[port]->port_pid = current->pid;
|
|
|
|
- strncpy(dd->ipath_pd[port]->port_comm, current->comm,
|
|
|
|
- sizeof(dd->ipath_pd[port]->port_comm));
|
|
|
|
|
|
+ pd->port_cnt = 1;
|
|
|
|
+ port_fp(fp) = pd;
|
|
|
|
+ pd->port_pid = current->pid;
|
|
|
|
+ strncpy(pd->port_comm, current->comm, sizeof(pd->port_comm));
|
|
ipath_stats.sps_ports++;
|
|
ipath_stats.sps_ports++;
|
|
ret = 0;
|
|
ret = 0;
|
|
- goto bail;
|
|
|
|
- }
|
|
|
|
- ret = -EBUSY;
|
|
|
|
|
|
+ } else
|
|
|
|
+ ret = -EBUSY;
|
|
|
|
|
|
bail:
|
|
bail:
|
|
return ret;
|
|
return ret;
|
|
@@ -1264,7 +1520,8 @@ static inline int usable(struct ipath_devdata *dd)
|
|
| IPATH_LINKUNK));
|
|
| IPATH_LINKUNK));
|
|
}
|
|
}
|
|
|
|
|
|
-static int find_free_port(int unit, struct file *fp)
|
|
|
|
|
|
+static int find_free_port(int unit, struct file *fp,
|
|
|
|
+ const struct ipath_user_info *uinfo)
|
|
{
|
|
{
|
|
struct ipath_devdata *dd = ipath_lookup(unit);
|
|
struct ipath_devdata *dd = ipath_lookup(unit);
|
|
int ret, i;
|
|
int ret, i;
|
|
@@ -1279,8 +1536,8 @@ static int find_free_port(int unit, struct file *fp)
|
|
goto bail;
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
|
|
- for (i = 0; i < dd->ipath_cfgports; i++) {
|
|
|
|
- ret = try_alloc_port(dd, i, fp);
|
|
|
|
|
|
+ for (i = 1; i < dd->ipath_cfgports; i++) {
|
|
|
|
+ ret = try_alloc_port(dd, i, fp, uinfo);
|
|
if (ret != -EBUSY)
|
|
if (ret != -EBUSY)
|
|
goto bail;
|
|
goto bail;
|
|
}
|
|
}
|
|
@@ -1290,13 +1547,14 @@ bail:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
-static int find_best_unit(struct file *fp)
|
|
|
|
|
|
+static int find_best_unit(struct file *fp,
|
|
|
|
+ const struct ipath_user_info *uinfo)
|
|
{
|
|
{
|
|
int ret = 0, i, prefunit = -1, devmax;
|
|
int ret = 0, i, prefunit = -1, devmax;
|
|
int maxofallports, npresent, nup;
|
|
int maxofallports, npresent, nup;
|
|
int ndev;
|
|
int ndev;
|
|
|
|
|
|
- (void) ipath_count_units(&npresent, &nup, &maxofallports);
|
|
|
|
|
|
+ devmax = ipath_count_units(&npresent, &nup, &maxofallports);
|
|
|
|
|
|
/*
|
|
/*
|
|
* This code is present to allow a knowledgeable person to
|
|
* This code is present to allow a knowledgeable person to
|
|
@@ -1343,8 +1601,6 @@ static int find_best_unit(struct file *fp)
|
|
|
|
|
|
if (prefunit != -1)
|
|
if (prefunit != -1)
|
|
devmax = prefunit + 1;
|
|
devmax = prefunit + 1;
|
|
- else
|
|
|
|
- devmax = ipath_count_units(NULL, NULL, NULL);
|
|
|
|
recheck:
|
|
recheck:
|
|
for (i = 1; i < maxofallports; i++) {
|
|
for (i = 1; i < maxofallports; i++) {
|
|
for (ndev = prefunit != -1 ? prefunit : 0; ndev < devmax;
|
|
for (ndev = prefunit != -1 ? prefunit : 0; ndev < devmax;
|
|
@@ -1359,7 +1615,7 @@ recheck:
|
|
* next.
|
|
* next.
|
|
*/
|
|
*/
|
|
continue;
|
|
continue;
|
|
- ret = try_alloc_port(dd, i, fp);
|
|
|
|
|
|
+ ret = try_alloc_port(dd, i, fp, uinfo);
|
|
if (!ret)
|
|
if (!ret)
|
|
goto done;
|
|
goto done;
|
|
}
|
|
}
|
|
@@ -1395,22 +1651,183 @@ done:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int find_shared_port(struct file *fp,
|
|
|
|
+ const struct ipath_user_info *uinfo)
|
|
|
|
+{
|
|
|
|
+ int devmax, ndev, i;
|
|
|
|
+ int ret = 0;
|
|
|
|
+
|
|
|
|
+ devmax = ipath_count_units(NULL, NULL, NULL);
|
|
|
|
+
|
|
|
|
+ for (ndev = 0; ndev < devmax; ndev++) {
|
|
|
|
+ struct ipath_devdata *dd = ipath_lookup(ndev);
|
|
|
|
+
|
|
|
|
+ if (!dd)
|
|
|
|
+ continue;
|
|
|
|
+ for (i = 1; i < dd->ipath_cfgports; i++) {
|
|
|
|
+ struct ipath_portdata *pd = dd->ipath_pd[i];
|
|
|
|
+
|
|
|
|
+ /* Skip ports which are not yet open */
|
|
|
|
+ if (!pd || !pd->port_cnt)
|
|
|
|
+ continue;
|
|
|
|
+ /* Skip port if it doesn't match the requested one */
|
|
|
|
+ if (pd->port_subport_id != uinfo->spu_subport_id)
|
|
|
|
+ continue;
|
|
|
|
+ /* Verify the sharing process matches the master */
|
|
|
|
+ if (pd->port_subport_cnt != uinfo->spu_subport_cnt ||
|
|
|
|
+ pd->userversion != uinfo->spu_userversion ||
|
|
|
|
+ pd->port_cnt >= pd->port_subport_cnt) {
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto done;
|
|
|
|
+ }
|
|
|
|
+ port_fp(fp) = pd;
|
|
|
|
+ subport_fp(fp) = pd->port_cnt++;
|
|
|
|
+ tidcursor_fp(fp) = 0;
|
|
|
|
+ pd->active_slaves |= 1 << subport_fp(fp);
|
|
|
|
+ ipath_cdbg(PROC,
|
|
|
|
+ "%s[%u] %u sharing %s[%u] unit:port %u:%u\n",
|
|
|
|
+ current->comm, current->pid,
|
|
|
|
+ subport_fp(fp),
|
|
|
|
+ pd->port_comm, pd->port_pid,
|
|
|
|
+ dd->ipath_unit, pd->port_port);
|
|
|
|
+ ret = 1;
|
|
|
|
+ goto done;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+done:
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
static int ipath_open(struct inode *in, struct file *fp)
|
|
static int ipath_open(struct inode *in, struct file *fp)
|
|
{
|
|
{
|
|
- int ret, user_minor;
|
|
|
|
|
|
+ /* The real work is performed later in ipath_assign_port() */
|
|
|
|
+ fp->private_data = kzalloc(sizeof(struct ipath_filedata), GFP_KERNEL);
|
|
|
|
+ return fp->private_data ? 0 : -ENOMEM;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* Get port early, so can set affinity prior to memory allocation */
|
|
|
|
+static int ipath_assign_port(struct file *fp,
|
|
|
|
+ const struct ipath_user_info *uinfo)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+ int i_minor;
|
|
|
|
+ unsigned swminor;
|
|
|
|
+
|
|
|
|
+ /* Check to be sure we haven't already initialized this file */
|
|
|
|
+ if (port_fp(fp)) {
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto done;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* for now, if major version is different, bail */
|
|
|
|
+ if ((uinfo->spu_userversion >> 16) != IPATH_USER_SWMAJOR) {
|
|
|
|
+ ipath_dbg("User major version %d not same as driver "
|
|
|
|
+ "major %d\n", uinfo->spu_userversion >> 16,
|
|
|
|
+ IPATH_USER_SWMAJOR);
|
|
|
|
+ ret = -ENODEV;
|
|
|
|
+ goto done;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ swminor = uinfo->spu_userversion & 0xffff;
|
|
|
|
+ if (swminor != IPATH_USER_SWMINOR)
|
|
|
|
+ ipath_dbg("User minor version %d not same as driver "
|
|
|
|
+ "minor %d\n", swminor, IPATH_USER_SWMINOR);
|
|
|
|
|
|
mutex_lock(&ipath_mutex);
|
|
mutex_lock(&ipath_mutex);
|
|
|
|
|
|
- user_minor = iminor(in) - IPATH_USER_MINOR_BASE;
|
|
|
|
|
|
+ if (swminor == IPATH_USER_SWMINOR && uinfo->spu_subport_cnt &&
|
|
|
|
+ (ret = find_shared_port(fp, uinfo))) {
|
|
|
|
+ mutex_unlock(&ipath_mutex);
|
|
|
|
+ if (ret > 0)
|
|
|
|
+ ret = 0;
|
|
|
|
+ goto done;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ i_minor = iminor(fp->f_dentry->d_inode) - IPATH_USER_MINOR_BASE;
|
|
ipath_cdbg(VERBOSE, "open on dev %lx (minor %d)\n",
|
|
ipath_cdbg(VERBOSE, "open on dev %lx (minor %d)\n",
|
|
- (long)in->i_rdev, user_minor);
|
|
|
|
|
|
+ (long)fp->f_dentry->d_inode->i_rdev, i_minor);
|
|
|
|
|
|
- if (user_minor)
|
|
|
|
- ret = find_free_port(user_minor - 1, fp);
|
|
|
|
|
|
+ if (i_minor)
|
|
|
|
+ ret = find_free_port(i_minor - 1, fp, uinfo);
|
|
else
|
|
else
|
|
- ret = find_best_unit(fp);
|
|
|
|
|
|
+ ret = find_best_unit(fp, uinfo);
|
|
|
|
|
|
mutex_unlock(&ipath_mutex);
|
|
mutex_unlock(&ipath_mutex);
|
|
|
|
+
|
|
|
|
+done:
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static int ipath_do_user_init(struct file *fp,
|
|
|
|
+ const struct ipath_user_info *uinfo)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+ struct ipath_portdata *pd;
|
|
|
|
+ struct ipath_devdata *dd;
|
|
|
|
+ u32 head32;
|
|
|
|
+
|
|
|
|
+ pd = port_fp(fp);
|
|
|
|
+ dd = pd->port_dd;
|
|
|
|
+
|
|
|
|
+ if (uinfo->spu_rcvhdrsize) {
|
|
|
|
+ ret = ipath_setrcvhdrsize(dd, uinfo->spu_rcvhdrsize);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto done;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* for now we do nothing with rcvhdrcnt: uinfo->spu_rcvhdrcnt */
|
|
|
|
+
|
|
|
|
+ /* for right now, kernel piobufs are at end, so port 1 is at 0 */
|
|
|
|
+ pd->port_piobufs = dd->ipath_piobufbase +
|
|
|
|
+ dd->ipath_pbufsport * (pd->port_port - 1) * dd->ipath_palign;
|
|
|
|
+ ipath_cdbg(VERBOSE, "Set base of piobufs for port %u to 0x%x\n",
|
|
|
|
+ pd->port_port, pd->port_piobufs);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Now allocate the rcvhdr Q and eager TIDs; skip the TID
|
|
|
|
+ * array for time being. If pd->port_port > chip-supported,
|
|
|
|
+ * we need to do extra stuff here to handle by handling overflow
|
|
|
|
+ * through port 0, someday
|
|
|
|
+ */
|
|
|
|
+ ret = ipath_create_rcvhdrq(dd, pd);
|
|
|
|
+ if (!ret)
|
|
|
|
+ ret = ipath_create_user_egr(pd);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto done;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * set the eager head register for this port to the current values
|
|
|
|
+ * of the tail pointers, since we don't know if they were
|
|
|
|
+ * updated on last use of the port.
|
|
|
|
+ */
|
|
|
|
+ head32 = ipath_read_ureg32(dd, ur_rcvegrindextail, pd->port_port);
|
|
|
|
+ ipath_write_ureg(dd, ur_rcvegrindexhead, head32, pd->port_port);
|
|
|
|
+ dd->ipath_lastegrheads[pd->port_port] = -1;
|
|
|
|
+ dd->ipath_lastrcvhdrqtails[pd->port_port] = -1;
|
|
|
|
+ ipath_cdbg(VERBOSE, "Wrote port%d egrhead %x from tail regs\n",
|
|
|
|
+ pd->port_port, head32);
|
|
|
|
+ pd->port_tidcursor = 0; /* start at beginning after open */
|
|
|
|
+ /*
|
|
|
|
+ * now enable the port; the tail registers will be written to memory
|
|
|
|
+ * by the chip as soon as it sees the write to
|
|
|
|
+ * dd->ipath_kregs->kr_rcvctrl. The update only happens on
|
|
|
|
+ * transition from 0 to 1, so clear it first, then set it as part of
|
|
|
|
+ * enabling the port. This will (very briefly) affect any other
|
|
|
|
+ * open ports, but it shouldn't be long enough to be an issue.
|
|
|
|
+ * We explictly set the in-memory copy to 0 beforehand, so we don't
|
|
|
|
+ * have to wait to be sure the DMA update has happened.
|
|
|
|
+ */
|
|
|
|
+ *(volatile u64 *)pd->port_rcvhdrtail_kvaddr = 0ULL;
|
|
|
|
+ set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port,
|
|
|
|
+ &dd->ipath_rcvctrl);
|
|
|
|
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
|
|
|
|
+ dd->ipath_rcvctrl & ~INFINIPATH_R_TAILUPD);
|
|
|
|
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
|
|
|
|
+ dd->ipath_rcvctrl);
|
|
|
|
+done:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1433,6 +1850,8 @@ static void unlock_expected_tids(struct ipath_portdata *pd)
|
|
if (!dd->ipath_pageshadow[i])
|
|
if (!dd->ipath_pageshadow[i])
|
|
continue;
|
|
continue;
|
|
|
|
|
|
|
|
+ pci_unmap_page(dd->pcidev, dd->ipath_physshadow[i],
|
|
|
|
+ PAGE_SIZE, PCI_DMA_FROMDEVICE);
|
|
ipath_release_user_pages_on_close(&dd->ipath_pageshadow[i],
|
|
ipath_release_user_pages_on_close(&dd->ipath_pageshadow[i],
|
|
1);
|
|
1);
|
|
dd->ipath_pageshadow[i] = NULL;
|
|
dd->ipath_pageshadow[i] = NULL;
|
|
@@ -1453,6 +1872,7 @@ static void unlock_expected_tids(struct ipath_portdata *pd)
|
|
static int ipath_close(struct inode *in, struct file *fp)
|
|
static int ipath_close(struct inode *in, struct file *fp)
|
|
{
|
|
{
|
|
int ret = 0;
|
|
int ret = 0;
|
|
|
|
+ struct ipath_filedata *fd;
|
|
struct ipath_portdata *pd;
|
|
struct ipath_portdata *pd;
|
|
struct ipath_devdata *dd;
|
|
struct ipath_devdata *dd;
|
|
unsigned port;
|
|
unsigned port;
|
|
@@ -1462,9 +1882,24 @@ static int ipath_close(struct inode *in, struct file *fp)
|
|
|
|
|
|
mutex_lock(&ipath_mutex);
|
|
mutex_lock(&ipath_mutex);
|
|
|
|
|
|
- pd = port_fp(fp);
|
|
|
|
- port = pd->port_port;
|
|
|
|
|
|
+ fd = (struct ipath_filedata *) fp->private_data;
|
|
fp->private_data = NULL;
|
|
fp->private_data = NULL;
|
|
|
|
+ pd = fd->pd;
|
|
|
|
+ if (!pd) {
|
|
|
|
+ mutex_unlock(&ipath_mutex);
|
|
|
|
+ goto bail;
|
|
|
|
+ }
|
|
|
|
+ if (--pd->port_cnt) {
|
|
|
|
+ /*
|
|
|
|
+ * XXX If the master closes the port before the slave(s),
|
|
|
|
+ * revoke the mmap for the eager receive queue so
|
|
|
|
+ * the slave(s) don't wait for receive data forever.
|
|
|
|
+ */
|
|
|
|
+ pd->active_slaves &= ~(1 << fd->subport);
|
|
|
|
+ mutex_unlock(&ipath_mutex);
|
|
|
|
+ goto bail;
|
|
|
|
+ }
|
|
|
|
+ port = pd->port_port;
|
|
dd = pd->port_dd;
|
|
dd = pd->port_dd;
|
|
|
|
|
|
if (pd->port_hdrqfull) {
|
|
if (pd->port_hdrqfull) {
|
|
@@ -1503,8 +1938,6 @@ static int ipath_close(struct inode *in, struct file *fp)
|
|
|
|
|
|
/* clean up the pkeys for this port user */
|
|
/* clean up the pkeys for this port user */
|
|
ipath_clean_part_key(pd, dd);
|
|
ipath_clean_part_key(pd, dd);
|
|
-
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* be paranoid, and never write 0's to these, just use an
|
|
* be paranoid, and never write 0's to these, just use an
|
|
* unused part of the port 0 tail page. Of course,
|
|
* unused part of the port 0 tail page. Of course,
|
|
@@ -1523,39 +1956,49 @@ static int ipath_close(struct inode *in, struct file *fp)
|
|
i = dd->ipath_pbufsport * (port - 1);
|
|
i = dd->ipath_pbufsport * (port - 1);
|
|
ipath_disarm_piobufs(dd, i, dd->ipath_pbufsport);
|
|
ipath_disarm_piobufs(dd, i, dd->ipath_pbufsport);
|
|
|
|
|
|
|
|
+ dd->ipath_f_clear_tids(dd, pd->port_port);
|
|
|
|
+
|
|
if (dd->ipath_pageshadow)
|
|
if (dd->ipath_pageshadow)
|
|
unlock_expected_tids(pd);
|
|
unlock_expected_tids(pd);
|
|
ipath_stats.sps_ports--;
|
|
ipath_stats.sps_ports--;
|
|
ipath_cdbg(PROC, "%s[%u] closed port %u:%u\n",
|
|
ipath_cdbg(PROC, "%s[%u] closed port %u:%u\n",
|
|
pd->port_comm, pd->port_pid,
|
|
pd->port_comm, pd->port_pid,
|
|
dd->ipath_unit, port);
|
|
dd->ipath_unit, port);
|
|
-
|
|
|
|
- dd->ipath_f_clear_tids(dd, pd->port_port);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- pd->port_cnt = 0;
|
|
|
|
pd->port_pid = 0;
|
|
pd->port_pid = 0;
|
|
-
|
|
|
|
dd->ipath_pd[pd->port_port] = NULL; /* before releasing mutex */
|
|
dd->ipath_pd[pd->port_port] = NULL; /* before releasing mutex */
|
|
mutex_unlock(&ipath_mutex);
|
|
mutex_unlock(&ipath_mutex);
|
|
ipath_free_pddata(dd, pd); /* after releasing the mutex */
|
|
ipath_free_pddata(dd, pd); /* after releasing the mutex */
|
|
|
|
|
|
|
|
+bail:
|
|
|
|
+ kfree(fd);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
-static int ipath_port_info(struct ipath_portdata *pd,
|
|
|
|
|
|
+static int ipath_port_info(struct ipath_portdata *pd, u16 subport,
|
|
struct ipath_port_info __user *uinfo)
|
|
struct ipath_port_info __user *uinfo)
|
|
{
|
|
{
|
|
struct ipath_port_info info;
|
|
struct ipath_port_info info;
|
|
int nup;
|
|
int nup;
|
|
int ret;
|
|
int ret;
|
|
|
|
+ size_t sz;
|
|
|
|
|
|
(void) ipath_count_units(NULL, &nup, NULL);
|
|
(void) ipath_count_units(NULL, &nup, NULL);
|
|
info.num_active = nup;
|
|
info.num_active = nup;
|
|
info.unit = pd->port_dd->ipath_unit;
|
|
info.unit = pd->port_dd->ipath_unit;
|
|
info.port = pd->port_port;
|
|
info.port = pd->port_port;
|
|
|
|
+ info.subport = subport;
|
|
|
|
+ /* Don't return new fields if old library opened the port. */
|
|
|
|
+ if ((pd->userversion & 0xffff) == IPATH_USER_SWMINOR) {
|
|
|
|
+ /* Number of user ports available for this device. */
|
|
|
|
+ info.num_ports = pd->port_dd->ipath_cfgports - 1;
|
|
|
|
+ info.num_subports = pd->port_subport_cnt;
|
|
|
|
+ sz = sizeof(info);
|
|
|
|
+ } else
|
|
|
|
+ sz = sizeof(info) - 2 * sizeof(u16);
|
|
|
|
|
|
- if (copy_to_user(uinfo, &info, sizeof(info))) {
|
|
|
|
|
|
+ if (copy_to_user(uinfo, &info, sz)) {
|
|
ret = -EFAULT;
|
|
ret = -EFAULT;
|
|
goto bail;
|
|
goto bail;
|
|
}
|
|
}
|
|
@@ -1565,6 +2008,16 @@ bail:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int ipath_get_slave_info(struct ipath_portdata *pd,
|
|
|
|
+ void __user *slave_mask_addr)
|
|
|
|
+{
|
|
|
|
+ int ret = 0;
|
|
|
|
+
|
|
|
|
+ if (copy_to_user(slave_mask_addr, &pd->active_slaves, sizeof(u32)))
|
|
|
|
+ ret = -EFAULT;
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
static ssize_t ipath_write(struct file *fp, const char __user *data,
|
|
static ssize_t ipath_write(struct file *fp, const char __user *data,
|
|
size_t count, loff_t *off)
|
|
size_t count, loff_t *off)
|
|
{
|
|
{
|
|
@@ -1591,6 +2044,8 @@ static ssize_t ipath_write(struct file *fp, const char __user *data,
|
|
consumed = sizeof(cmd.type);
|
|
consumed = sizeof(cmd.type);
|
|
|
|
|
|
switch (cmd.type) {
|
|
switch (cmd.type) {
|
|
|
|
+ case IPATH_CMD_ASSIGN_PORT:
|
|
|
|
+ case __IPATH_CMD_USER_INIT:
|
|
case IPATH_CMD_USER_INIT:
|
|
case IPATH_CMD_USER_INIT:
|
|
copy = sizeof(cmd.cmd.user_info);
|
|
copy = sizeof(cmd.cmd.user_info);
|
|
dest = &cmd.cmd.user_info;
|
|
dest = &cmd.cmd.user_info;
|
|
@@ -1617,6 +2072,11 @@ static ssize_t ipath_write(struct file *fp, const char __user *data,
|
|
dest = &cmd.cmd.part_key;
|
|
dest = &cmd.cmd.part_key;
|
|
src = &ucmd->cmd.part_key;
|
|
src = &ucmd->cmd.part_key;
|
|
break;
|
|
break;
|
|
|
|
+ case IPATH_CMD_SLAVE_INFO:
|
|
|
|
+ copy = sizeof(cmd.cmd.slave_mask_addr);
|
|
|
|
+ dest = &cmd.cmd.slave_mask_addr;
|
|
|
|
+ src = &ucmd->cmd.slave_mask_addr;
|
|
|
|
+ break;
|
|
default:
|
|
default:
|
|
ret = -EINVAL;
|
|
ret = -EINVAL;
|
|
goto bail;
|
|
goto bail;
|
|
@@ -1634,34 +2094,55 @@ static ssize_t ipath_write(struct file *fp, const char __user *data,
|
|
|
|
|
|
consumed += copy;
|
|
consumed += copy;
|
|
pd = port_fp(fp);
|
|
pd = port_fp(fp);
|
|
|
|
+ if (!pd && cmd.type != __IPATH_CMD_USER_INIT &&
|
|
|
|
+ cmd.type != IPATH_CMD_ASSIGN_PORT) {
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto bail;
|
|
|
|
+ }
|
|
|
|
|
|
switch (cmd.type) {
|
|
switch (cmd.type) {
|
|
|
|
+ case IPATH_CMD_ASSIGN_PORT:
|
|
|
|
+ ret = ipath_assign_port(fp, &cmd.cmd.user_info);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto bail;
|
|
|
|
+ break;
|
|
|
|
+ case __IPATH_CMD_USER_INIT:
|
|
|
|
+ /* backwards compatibility, get port first */
|
|
|
|
+ ret = ipath_assign_port(fp, &cmd.cmd.user_info);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto bail;
|
|
|
|
+ /* and fall through to current version. */
|
|
case IPATH_CMD_USER_INIT:
|
|
case IPATH_CMD_USER_INIT:
|
|
- ret = ipath_do_user_init(pd, &cmd.cmd.user_info);
|
|
|
|
- if (ret < 0)
|
|
|
|
|
|
+ ret = ipath_do_user_init(fp, &cmd.cmd.user_info);
|
|
|
|
+ if (ret)
|
|
goto bail;
|
|
goto bail;
|
|
ret = ipath_get_base_info(
|
|
ret = ipath_get_base_info(
|
|
- pd, (void __user *) (unsigned long)
|
|
|
|
|
|
+ fp, (void __user *) (unsigned long)
|
|
cmd.cmd.user_info.spu_base_info,
|
|
cmd.cmd.user_info.spu_base_info,
|
|
cmd.cmd.user_info.spu_base_info_size);
|
|
cmd.cmd.user_info.spu_base_info_size);
|
|
break;
|
|
break;
|
|
case IPATH_CMD_RECV_CTRL:
|
|
case IPATH_CMD_RECV_CTRL:
|
|
- ret = ipath_manage_rcvq(pd, cmd.cmd.recv_ctrl);
|
|
|
|
|
|
+ ret = ipath_manage_rcvq(pd, subport_fp(fp), cmd.cmd.recv_ctrl);
|
|
break;
|
|
break;
|
|
case IPATH_CMD_PORT_INFO:
|
|
case IPATH_CMD_PORT_INFO:
|
|
- ret = ipath_port_info(pd,
|
|
|
|
|
|
+ ret = ipath_port_info(pd, subport_fp(fp),
|
|
(struct ipath_port_info __user *)
|
|
(struct ipath_port_info __user *)
|
|
(unsigned long) cmd.cmd.port_info);
|
|
(unsigned long) cmd.cmd.port_info);
|
|
break;
|
|
break;
|
|
case IPATH_CMD_TID_UPDATE:
|
|
case IPATH_CMD_TID_UPDATE:
|
|
- ret = ipath_tid_update(pd, &cmd.cmd.tid_info);
|
|
|
|
|
|
+ ret = ipath_tid_update(pd, fp, &cmd.cmd.tid_info);
|
|
break;
|
|
break;
|
|
case IPATH_CMD_TID_FREE:
|
|
case IPATH_CMD_TID_FREE:
|
|
- ret = ipath_tid_free(pd, &cmd.cmd.tid_info);
|
|
|
|
|
|
+ ret = ipath_tid_free(pd, subport_fp(fp), &cmd.cmd.tid_info);
|
|
break;
|
|
break;
|
|
case IPATH_CMD_SET_PART_KEY:
|
|
case IPATH_CMD_SET_PART_KEY:
|
|
ret = ipath_set_part_key(pd, cmd.cmd.part_key);
|
|
ret = ipath_set_part_key(pd, cmd.cmd.part_key);
|
|
break;
|
|
break;
|
|
|
|
+ case IPATH_CMD_SLAVE_INFO:
|
|
|
|
+ ret = ipath_get_slave_info(pd,
|
|
|
|
+ (void __user *) (unsigned long)
|
|
|
|
+ cmd.cmd.slave_mask_addr);
|
|
|
|
+ break;
|
|
}
|
|
}
|
|
|
|
|
|
if (ret >= 0)
|
|
if (ret >= 0)
|
|
@@ -1858,4 +2339,3 @@ void ipath_user_remove(struct ipath_devdata *dd)
|
|
bail:
|
|
bail:
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
-
|
|
|