Commit 9929b0fb authored by Bryan O'Sullivan's avatar Bryan O'Sullivan Committed by Roland Dreier

IB/ipath: Driver support for userspace sharing of HW contexts

This allows multiple userspace processes to share a single hardware
context in a master/slave arrangement.  It is backwards binary compatible
with existing userspace.
Signed-off-by: default avatarBryan O'Sullivan <bryan.osullivan@qlogic.com>
Signed-off-by: default avatarRoland Dreier <rolandd@cisco.com>
parent 221e3198
...@@ -185,6 +185,7 @@ typedef enum _ipath_ureg { ...@@ -185,6 +185,7 @@ typedef enum _ipath_ureg {
#define IPATH_RUNTIME_PCIE 0x2 #define IPATH_RUNTIME_PCIE 0x2
#define IPATH_RUNTIME_FORCE_WC_ORDER 0x4 #define IPATH_RUNTIME_FORCE_WC_ORDER 0x4
#define IPATH_RUNTIME_RCVHDR_COPY 0x8 #define IPATH_RUNTIME_RCVHDR_COPY 0x8
#define IPATH_RUNTIME_MASTER 0x10
/* /*
* This structure is returned by ipath_userinit() immediately after * This structure is returned by ipath_userinit() immediately after
...@@ -202,7 +203,8 @@ struct ipath_base_info { ...@@ -202,7 +203,8 @@ struct ipath_base_info {
/* version of software, for feature checking. */ /* version of software, for feature checking. */
__u32 spi_sw_version; __u32 spi_sw_version;
/* InfiniPath port assigned, goes into sent packets */ /* InfiniPath port assigned, goes into sent packets */
__u32 spi_port; __u16 spi_port;
__u16 spi_subport;
/* /*
* IB MTU, packets IB data must be less than this. * IB MTU, packets IB data must be less than this.
* The MTU is in bytes, and will be a multiple of 4 bytes. * The MTU is in bytes, and will be a multiple of 4 bytes.
...@@ -218,7 +220,7 @@ struct ipath_base_info { ...@@ -218,7 +220,7 @@ struct ipath_base_info {
__u32 spi_tidcnt; __u32 spi_tidcnt;
/* size of the TID Eager list in infinipath, in entries */ /* size of the TID Eager list in infinipath, in entries */
__u32 spi_tidegrcnt; __u32 spi_tidegrcnt;
/* size of a single receive header queue entry. */ /* size of a single receive header queue entry in words. */
__u32 spi_rcvhdrent_size; __u32 spi_rcvhdrent_size;
/* /*
* Count of receive header queue entries allocated. * Count of receive header queue entries allocated.
...@@ -310,6 +312,12 @@ struct ipath_base_info { ...@@ -310,6 +312,12 @@ struct ipath_base_info {
__u32 spi_filler_for_align; __u32 spi_filler_for_align;
/* address of readonly memory copy of the rcvhdrq tail register. */ /* address of readonly memory copy of the rcvhdrq tail register. */
__u64 spi_rcvhdr_tailaddr; __u64 spi_rcvhdr_tailaddr;
/* shared memory pages for subports if IPATH_RUNTIME_MASTER is set */
__u64 spi_subport_uregbase;
__u64 spi_subport_rcvegrbuf;
__u64 spi_subport_rcvhdr_base;
} __attribute__ ((aligned(8))); } __attribute__ ((aligned(8)));
...@@ -328,12 +336,12 @@ struct ipath_base_info { ...@@ -328,12 +336,12 @@ struct ipath_base_info {
/* /*
* Minor version differences are always compatible * Minor version differences are always compatible
* a within a major version, however if if user software is larger * a within a major version, however if user software is larger
* than driver software, some new features and/or structure fields * than driver software, some new features and/or structure fields
* may not be implemented; the user code must deal with this if it * may not be implemented; the user code must deal with this if it
* cares, or it must abort after initialization reports the difference * cares, or it must abort after initialization reports the difference.
*/ */
#define IPATH_USER_SWMINOR 2 #define IPATH_USER_SWMINOR 3
#define IPATH_USER_SWVERSION ((IPATH_USER_SWMAJOR<<16) | IPATH_USER_SWMINOR) #define IPATH_USER_SWVERSION ((IPATH_USER_SWMAJOR<<16) | IPATH_USER_SWMINOR)
...@@ -379,7 +387,16 @@ struct ipath_user_info { ...@@ -379,7 +387,16 @@ struct ipath_user_info {
*/ */
__u32 spu_rcvhdrsize; __u32 spu_rcvhdrsize;
__u64 spu_unused; /* kept for compatible layout */ /*
* If two or more processes wish to share a port, each process
* must set the spu_subport_cnt and spu_subport_id to the same
* values. The only restriction on the spu_subport_id is that
* it be unique for a given node.
*/
__u16 spu_subport_cnt;
__u16 spu_subport_id;
__u32 spu_unused; /* kept for compatible layout */
/* /*
* address of struct base_info to write to * address of struct base_info to write to
...@@ -398,13 +415,17 @@ struct ipath_user_info { ...@@ -398,13 +415,17 @@ struct ipath_user_info {
#define IPATH_CMD_TID_UPDATE 19 /* update expected TID entries */ #define IPATH_CMD_TID_UPDATE 19 /* update expected TID entries */
#define IPATH_CMD_TID_FREE 20 /* free expected TID entries */ #define IPATH_CMD_TID_FREE 20 /* free expected TID entries */
#define IPATH_CMD_SET_PART_KEY 21 /* add partition key */ #define IPATH_CMD_SET_PART_KEY 21 /* add partition key */
#define IPATH_CMD_SLAVE_INFO 22 /* return info on slave processes */
#define IPATH_CMD_MAX 21 #define IPATH_CMD_MAX 22
struct ipath_port_info { struct ipath_port_info {
__u32 num_active; /* number of active units */ __u32 num_active; /* number of active units */
__u32 unit; /* unit (chip) assigned to caller */ __u32 unit; /* unit (chip) assigned to caller */
__u32 port; /* port on unit assigned to caller */ __u16 port; /* port on unit assigned to caller */
__u16 subport; /* subport on unit assigned to caller */
__u16 num_ports; /* number of ports available on unit */
__u16 num_subports; /* number of subport slaves opened on port */
}; };
struct ipath_tid_info { struct ipath_tid_info {
...@@ -435,6 +456,8 @@ struct ipath_cmd { ...@@ -435,6 +456,8 @@ struct ipath_cmd {
__u32 recv_ctrl; __u32 recv_ctrl;
/* partition key to set */ /* partition key to set */
__u16 part_key; __u16 part_key;
/* user address of __u32 bitmask of active slaves */
__u64 slave_mask_addr;
} cmd; } cmd;
}; };
...@@ -596,6 +619,10 @@ struct infinipath_counters { ...@@ -596,6 +619,10 @@ struct infinipath_counters {
/* K_PktFlags bits */ /* K_PktFlags bits */
#define INFINIPATH_KPF_INTR 0x1 #define INFINIPATH_KPF_INTR 0x1
#define INFINIPATH_KPF_SUBPORT_MASK 0x3
#define INFINIPATH_KPF_SUBPORT_SHIFT 1
#define INFINIPATH_MAX_SUBPORT 4
/* SendPIO per-buffer control */ /* SendPIO per-buffer control */
#define INFINIPATH_SP_TEST 0x40 #define INFINIPATH_SP_TEST 0x40
...@@ -610,7 +637,7 @@ struct ipath_header { ...@@ -610,7 +637,7 @@ struct ipath_header {
/* /*
* Version - 4 bits, Port - 4 bits, TID - 10 bits and Offset - * Version - 4 bits, Port - 4 bits, TID - 10 bits and Offset -
* 14 bits before ECO change ~28 Dec 03. After that, Vers 4, * 14 bits before ECO change ~28 Dec 03. After that, Vers 4,
* Port 3, TID 11, offset 14. * Port 4, TID 11, offset 13.
*/ */
__le32 ver_port_tid_offset; __le32 ver_port_tid_offset;
__le16 chksum; __le16 chksum;
......
...@@ -1827,9 +1827,9 @@ void ipath_free_pddata(struct ipath_devdata *dd, struct ipath_portdata *pd) ...@@ -1827,9 +1827,9 @@ void ipath_free_pddata(struct ipath_devdata *dd, struct ipath_portdata *pd)
dma_free_coherent(&dd->pcidev->dev, size, dma_free_coherent(&dd->pcidev->dev, size,
base, pd->port_rcvegrbuf_phys[e]); base, pd->port_rcvegrbuf_phys[e]);
} }
vfree(pd->port_rcvegrbuf); kfree(pd->port_rcvegrbuf);
pd->port_rcvegrbuf = NULL; pd->port_rcvegrbuf = NULL;
vfree(pd->port_rcvegrbuf_phys); kfree(pd->port_rcvegrbuf_phys);
pd->port_rcvegrbuf_phys = NULL; pd->port_rcvegrbuf_phys = NULL;
pd->port_rcvegrbuf_chunks = 0; pd->port_rcvegrbuf_chunks = 0;
} else if (pd->port_port == 0 && dd->ipath_port0_skbs) { } else if (pd->port_port == 0 && dd->ipath_port0_skbs) {
...@@ -1845,6 +1845,9 @@ void ipath_free_pddata(struct ipath_devdata *dd, struct ipath_portdata *pd) ...@@ -1845,6 +1845,9 @@ void ipath_free_pddata(struct ipath_devdata *dd, struct ipath_portdata *pd)
vfree(skbs); vfree(skbs);
} }
kfree(pd->port_tid_pg_list); kfree(pd->port_tid_pg_list);
vfree(pd->subport_uregbase);
vfree(pd->subport_rcvegrbuf);
vfree(pd->subport_rcvhdr_base);
kfree(pd); kfree(pd);
} }
......
...@@ -41,6 +41,12 @@ ...@@ -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 = { ...@@ -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", "Base size %zu, need %zu (version mismatch?)\n",
(unsigned long) ubase_size, ubase_size, sz);
(unsigned long) sizeof(*kinfo));
ret = -EINVAL; ret = -EINVAL;
goto bail; goto bail;
} }
...@@ -95,7 +118,9 @@ static int ipath_get_base_info(struct ipath_portdata *pd, ...@@ -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,76 @@ static int ipath_get_base_info(struct ipath_portdata *pd, ...@@ -118,31 +143,76 @@ 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 * no doubt some future kernel release will change that, and we'll be
* on to yet another method of dealing with this * 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;
if (!shared) {
kinfo->spi_piocnt = dd->ipath_pbufsport;
kinfo->spi_piobufbase = (u64) pd->port_piobufs; kinfo->spi_piobufbase = (u64) pd->port_piobufs;
kinfo->__spi_uregbase = kinfo->__spi_uregbase = (u64) dd->ipath_uregbase +
dd->ipath_uregbase + dd->ipath_palign * pd->port_port; 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_pioindex = dd->ipath_pbufsport * (pd->port_port - 1); kinfo->spi_piocnt = dd->ipath_pbufsport / subport_cnt;
kinfo->spi_piocnt = dd->ipath_pbufsport; 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_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,
kinfo->spi_subport_uregbase,
kinfo->spi_subport_rcvegrbuf,
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 +224,7 @@ static int ipath_get_base_info(struct ipath_portdata *pd, ...@@ -154,6 +224,7 @@ static int ipath_get_base_info(struct ipath_portdata *pd,
/** /**
* 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 +247,11 @@ static int ipath_get_base_info(struct ipath_portdata *pd, ...@@ -176,11 +247,11 @@ static int ipath_get_base_info(struct ipath_portdata *pd,
* 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 +259,7 @@ static int ipath_tid_update(struct ipath_portdata *pd, ...@@ -188,6 +259,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 +276,34 @@ static int ipath_tid_update(struct ipath_portdata *pd, ...@@ -204,20 +276,34 @@ static int ipath_tid_update(struct ipath_portdata *pd,
ret = -EFAULT; ret = -EFAULT;
goto done; goto done;
} }
porttid = pd->port_port * dd->ipath_rcvtidcnt;
if (!pd->port_subport_cnt) {
tidcnt = dd->ipath_rcvtidcnt; tidcnt = dd->ipath_rcvtidcnt;
if (cnt >= tidcnt) { 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; pagep = &((struct page **) pd->port_tid_pg_list)[tidoff];
tidlist = (u16 *) (&pagep[cnt]); 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,9 +360,9 @@ static int ipath_tid_update(struct ipath_portdata *pd, ...@@ -274,9 +360,9 @@ 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];
/* /*
...@@ -341,7 +427,10 @@ static int ipath_tid_update(struct ipath_portdata *pd, ...@@ -341,7 +427,10 @@ static int ipath_tid_update(struct ipath_portdata *pd,
} }
if (tid == tidcnt) if (tid == tidcnt)
tid = 0; tid = 0;
if (!pd->port_subport_cnt)
pd->port_tidcursor = tid; pd->port_tidcursor = tid;
else
tidcursor_fp(fp) = tid;
} }
done: done:
...@@ -354,6 +443,7 @@ static int ipath_tid_update(struct ipath_portdata *pd, ...@@ -354,6 +443,7 @@ static int ipath_tid_update(struct ipath_portdata *pd,
/** /**
* 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 +457,7 @@ static int ipath_tid_update(struct ipath_portdata *pd, ...@@ -367,7 +457,7 @@ static int ipath_tid_update(struct ipath_portdata *pd,
* 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 +478,20 @@ static int ipath_tid_free(struct ipath_portdata *pd, ...@@ -388,11 +478,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 */
...@@ -581,20 +680,24 @@ static int ipath_set_part_key(struct ipath_portdata *pd, u16 key) ...@@ -581,20 +680,24 @@ static int ipath_set_part_key(struct ipath_portdata *pd, u16 key)
/** /**
* 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) {
/* /*
...@@ -630,6 +733,7 @@ static int ipath_manage_rcvq(struct ipath_portdata *pd, int start_stop) ...@@ -630,6 +733,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 +791,36 @@ static void ipath_clean_part_key(struct ipath_portdata *pd, ...@@ -687,6 +791,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 +836,7 @@ static void ipath_clean_part_key(struct ipath_portdata *pd, ...@@ -702,7 +836,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 +856,18 @@ static int ipath_create_user_egr(struct ipath_portdata *pd) ...@@ -722,31 +856,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);
/* chunk = pd->port_rcvegrbuf_chunks;
* to avoid wasting a lot of memory, we allocate 32KB chunks of egrperchunk = pd->port_rcvegrbufs_perchunk;
* physically contiguous memory, advance through it until used up size = pd->port_rcvegrbuf_size;
* and then allocate more. Of course, we need memory to store those pd->port_rcvegrbuf = kmalloc(chunk * sizeof(pd->port_rcvegrbuf[0]),
* extra pointers, now. Started out with 256KB, but under heavy GFP_KERNEL);
* 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]));
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,97 +912,15 @@ static int ipath_create_user_egr(struct ipath_portdata *pd) ...@@ -791,97 +912,15 @@ static int ipath_create_user_egr(struct ipath_portdata *pd)
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,
...@@ -957,7 +996,8 @@ static int mmap_ureg(struct vm_area_struct *vma, struct ipath_devdata *dd, ...@@ -957,7 +996,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 +1008,15 @@ static int mmap_piobufs(struct vm_area_struct *vma, ...@@ -968,16 +1008,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) > if ((vma->vm_end - vma->vm_start) > (piocnt * dd->ipath_palign)) {
(dd->ipath_pbufsport * 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
...@@ -1021,7 +1060,7 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma, ...@@ -1021,7 +1060,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;
} }
...@@ -1049,6 +1088,122 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma, ...@@ -1049,6 +1088,122 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma,
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 +1219,99 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma) ...@@ -1064,73 +1219,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 * file offset passed via mmap(). For shared ports, this is the
* space mapped. * 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, * Check for 0 in case one of the allocations failed, but user
* but we'll be paranoid. Check for 0 is mostly in case one of the * called mmap anyway.
* allocations failed, but user called mmap anyway. We want to catch
* that before it can match.
*/ */
if (!pgaddr || pgaddr >= (1ULL<<40)) { if (!pgaddr) {
ipath_dev_err(dd, "Bad phys addr %llx, start %lx, end %lx\n", ret = -EINVAL;
(unsigned long long)pgaddr, vma->vm_start, vma->vm_end); goto bail;
return -EINVAL;
} }
/* just the offset of the port user registers, not physical addr */ ipath_cdbg(MM, "pgaddr %llx vm_start=%lx len %lx port %u:%u:%u\n",
ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
ipath_cdbg(MM, "ushare: pgaddr %llx vm_start=%lx, vmlen %lx\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, * Physical addresses must fit in 40 bits for our hardware.
"vm_start not aligned: %lx, end=%lx phys %lx\n", * Check for kernel virtual addresses first, anything else must
vma->vm_start, vma->vm_end, (unsigned long)pgaddr); * match a HW or memory address.
ret = -EINVAL; */
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) else if (pgaddr == piobufs)
ret = mmap_piobufs(vma, dd, pd); ret = mmap_piobufs(vma, dd, pd, piobufs, piocnt);
else if (pgaddr == (u64) pd->port_rcvegr_phys) 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 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 = ret = ipath_mmap_mem(vma, pd, pd->port_rcvhdrq_size, 1,
ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize
* sizeof(u32), PAGE_SIZE);
ret = ipath_mmap_mem(vma, pd, total_size, 1,
pd->port_rcvhdrq_phys, pd->port_rcvhdrq_phys,
"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_rcvhdrqtailaddr_phys,
"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 +1319,10 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma) ...@@ -1138,9 +1319,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", "Failure %d on off %llx len %lx\n",
-ret, vma->vm_start, vma->vm_pgoff); -ret, (unsigned long long)pgaddr,
vma->vm_end - vma->vm_start);
bail:
return ret; return ret;
} }
...@@ -1154,6 +1336,8 @@ static unsigned int ipath_poll(struct file *fp, ...@@ -1154,6 +1336,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 +1360,7 @@ static unsigned int ipath_poll(struct file *fp, ...@@ -1176,7 +1360,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 +1384,80 @@ static unsigned int ipath_poll(struct file *fp, ...@@ -1200,18 +1384,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]) { if (!(pd = dd->ipath_pd[port])) {
void *p, *ptmp; 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,33 +1467,35 @@ static int try_alloc_port(struct ipath_devdata *dd, int port, ...@@ -1221,33 +1467,35 @@ 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) { if (!pd->port_cnt) {
dd->ipath_pd[port]->port_cnt = 1; pd->userversion = uinfo->spu_userversion;
fp->private_data = (void *) dd->ipath_pd[port]; 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; pd->port_cnt = 1;
strncpy(dd->ipath_pd[port]->port_comm, current->comm, port_fp(fp) = pd;
sizeof(dd->ipath_pd[port]->port_comm)); 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; } else
}
ret = -EBUSY; ret = -EBUSY;
bail: bail:
...@@ -1264,7 +1512,8 @@ static inline int usable(struct ipath_devdata *dd) ...@@ -1264,7 +1512,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 +1528,8 @@ static int find_free_port(int unit, struct file *fp) ...@@ -1279,8 +1528,8 @@ static int find_free_port(int unit, struct file *fp)
goto bail; goto bail;
} }
for (i = 0; i < dd->ipath_cfgports; i++) { for (i = 1; i < dd->ipath_cfgports; i++) {
ret = try_alloc_port(dd, i, fp); ret = try_alloc_port(dd, i, fp, uinfo);
if (ret != -EBUSY) if (ret != -EBUSY)
goto bail; goto bail;
} }
...@@ -1290,13 +1539,14 @@ static int find_free_port(int unit, struct file *fp) ...@@ -1290,13 +1539,14 @@ static int find_free_port(int unit, struct file *fp)
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 +1593,6 @@ static int find_best_unit(struct file *fp) ...@@ -1343,8 +1593,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 +1607,7 @@ static int find_best_unit(struct file *fp) ...@@ -1359,7 +1607,7 @@ static int find_best_unit(struct file *fp)
* 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 +1643,174 @@ static int find_best_unit(struct file *fp) ...@@ -1395,22 +1643,174 @@ static int find_best_unit(struct file *fp)
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_do_user_init() */
fp->private_data = kzalloc(sizeof(struct ipath_filedata), GFP_KERNEL);
return fp->private_data ? 0 : -ENOMEM;
}
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;
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) if (i_minor)
ret = find_free_port(user_minor - 1, fp); 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);
if (ret)
goto done;
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.
*/
*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;
} }
...@@ -1453,6 +1853,7 @@ static void unlock_expected_tids(struct ipath_portdata *pd) ...@@ -1453,6 +1853,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 +1863,24 @@ static int ipath_close(struct inode *in, struct file *fp) ...@@ -1462,9 +1863,24 @@ static int ipath_close(struct inode *in, struct file *fp)
mutex_lock(&ipath_mutex); mutex_lock(&ipath_mutex);
pd = port_fp(fp); fd = (struct ipath_filedata *) fp->private_data;
port = pd->port_port;
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 +1919,6 @@ static int ipath_close(struct inode *in, struct file *fp) ...@@ -1503,8 +1919,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,
...@@ -1533,29 +1947,39 @@ static int ipath_close(struct inode *in, struct file *fp) ...@@ -1533,29 +1947,39 @@ static int ipath_close(struct inode *in, struct file *fp)
dd->ipath_f_clear_tids(dd, pd->port_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 +1989,16 @@ static int ipath_port_info(struct ipath_portdata *pd, ...@@ -1565,6 +1989,16 @@ static int ipath_port_info(struct ipath_portdata *pd,
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)
{ {
...@@ -1617,6 +2051,11 @@ static ssize_t ipath_write(struct file *fp, const char __user *data, ...@@ -1617,6 +2051,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 +2073,43 @@ static ssize_t ipath_write(struct file *fp, const char __user *data, ...@@ -1634,34 +2073,43 @@ 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) {
ret = -EINVAL;
goto bail;
}
switch (cmd.type) { switch (cmd.type) {
case IPATH_CMD_USER_INIT: case IPATH_CMD_USER_INIT:
ret = ipath_do_user_init(pd, &cmd.cmd.user_info); ret = ipath_do_user_init(fp, &cmd.cmd.user_info);
if (ret < 0) 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 +2306,3 @@ void ipath_user_remove(struct ipath_devdata *dd) ...@@ -1858,4 +2306,3 @@ void ipath_user_remove(struct ipath_devdata *dd)
bail: bail:
return; return;
} }
...@@ -79,8 +79,8 @@ struct ipath_portdata { ...@@ -79,8 +79,8 @@ struct ipath_portdata {
dma_addr_t port_rcvhdrq_phys; dma_addr_t port_rcvhdrq_phys;
dma_addr_t port_rcvhdrqtailaddr_phys; dma_addr_t port_rcvhdrqtailaddr_phys;
/* /*
* number of opens on this instance (0 or 1; ignoring forks, dup, * number of opens (including slave subports) on this instance
* etc. for now) * (ignoring forks, dup, etc. for now)
*/ */
int port_cnt; int port_cnt;
/* /*
...@@ -89,6 +89,10 @@ struct ipath_portdata { ...@@ -89,6 +89,10 @@ struct ipath_portdata {
*/ */
/* instead of calculating it */ /* instead of calculating it */
unsigned port_port; unsigned port_port;
/* non-zero if port is being shared. */
u16 port_subport_cnt;
/* non-zero if port is being shared. */
u16 port_subport_id;
/* chip offset of PIO buffers for this port */ /* chip offset of PIO buffers for this port */
u32 port_piobufs; u32 port_piobufs;
/* how many alloc_pages() chunks in port_rcvegrbuf_pages */ /* how many alloc_pages() chunks in port_rcvegrbuf_pages */
...@@ -121,6 +125,16 @@ struct ipath_portdata { ...@@ -121,6 +125,16 @@ struct ipath_portdata {
u16 port_pkeys[4]; u16 port_pkeys[4];
/* so file ops can get at unit */ /* so file ops can get at unit */
struct ipath_devdata *port_dd; struct ipath_devdata *port_dd;
/* A page of memory for rcvhdrhead, rcvegrhead, rcvegrtail * N */
void *subport_uregbase;
/* An array of pages for the eager receive buffers * N */
void *subport_rcvegrbuf;
/* An array of pages for the eager header queue entries * N */
void *subport_rcvhdr_base;
/* The version of the library which opened this port */
u32 userversion;
/* Bitmask of active slaves */
u32 active_slaves;
}; };
struct sk_buff; struct sk_buff;
...@@ -512,6 +526,12 @@ struct ipath_devdata { ...@@ -512,6 +526,12 @@ struct ipath_devdata {
u32 ipath_lli_errors; u32 ipath_lli_errors;
}; };
/* Private data for file operations */
struct ipath_filedata {
struct ipath_portdata *pd;
unsigned subport;
unsigned tidcursor;
};
extern struct list_head ipath_dev_list; extern struct list_head ipath_dev_list;
extern spinlock_t ipath_devs_lock; extern spinlock_t ipath_devs_lock;
extern struct ipath_devdata *ipath_lookup(int unit); extern struct ipath_devdata *ipath_lookup(int unit);
...@@ -572,7 +592,11 @@ int ipath_set_lid(struct ipath_devdata *, u32, u8); ...@@ -572,7 +592,11 @@ int ipath_set_lid(struct ipath_devdata *, u32, u8);
int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv); int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv);
/* for use in system calls, where we want to know device type, etc. */ /* for use in system calls, where we want to know device type, etc. */
#define port_fp(fp) ((struct ipath_portdata *) (fp)->private_data) #define port_fp(fp) ((struct ipath_filedata *)(fp)->private_data)->pd
#define subport_fp(fp) \
((struct ipath_filedata *)(fp)->private_data)->subport
#define tidcursor_fp(fp) \
((struct ipath_filedata *)(fp)->private_data)->tidcursor
/* /*
* values for ipath_flags * values for ipath_flags
......
...@@ -297,6 +297,16 @@ static ssize_t show_nguid(struct device *dev, ...@@ -297,6 +297,16 @@ static ssize_t show_nguid(struct device *dev,
return scnprintf(buf, PAGE_SIZE, "%u\n", dd->ipath_nguid); return scnprintf(buf, PAGE_SIZE, "%u\n", dd->ipath_nguid);
} }
static ssize_t show_nports(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct ipath_devdata *dd = dev_get_drvdata(dev);
/* Return the number of user ports available. */
return scnprintf(buf, PAGE_SIZE, "%u\n", dd->ipath_cfgports - 1);
}
static ssize_t show_serial(struct device *dev, static ssize_t show_serial(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
...@@ -608,6 +618,7 @@ static DEVICE_ATTR(mlid, S_IWUSR | S_IRUGO, show_mlid, store_mlid); ...@@ -608,6 +618,7 @@ static DEVICE_ATTR(mlid, S_IWUSR | S_IRUGO, show_mlid, store_mlid);
static DEVICE_ATTR(mtu, S_IWUSR | S_IRUGO, show_mtu, store_mtu); static DEVICE_ATTR(mtu, S_IWUSR | S_IRUGO, show_mtu, store_mtu);
static DEVICE_ATTR(enabled, S_IWUSR | S_IRUGO, show_enabled, store_enabled); static DEVICE_ATTR(enabled, S_IWUSR | S_IRUGO, show_enabled, store_enabled);
static DEVICE_ATTR(nguid, S_IRUGO, show_nguid, NULL); static DEVICE_ATTR(nguid, S_IRUGO, show_nguid, NULL);
static DEVICE_ATTR(nports, S_IRUGO, show_nports, NULL);
static DEVICE_ATTR(reset, S_IWUSR, NULL, store_reset); static DEVICE_ATTR(reset, S_IWUSR, NULL, store_reset);
static DEVICE_ATTR(serial, S_IRUGO, show_serial, NULL); static DEVICE_ATTR(serial, S_IRUGO, show_serial, NULL);
static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
...@@ -623,6 +634,7 @@ static struct attribute *dev_attributes[] = { ...@@ -623,6 +634,7 @@ static struct attribute *dev_attributes[] = {
&dev_attr_mlid.attr, &dev_attr_mlid.attr,
&dev_attr_mtu.attr, &dev_attr_mtu.attr,
&dev_attr_nguid.attr, &dev_attr_nguid.attr,
&dev_attr_nports.attr,
&dev_attr_serial.attr, &dev_attr_serial.attr,
&dev_attr_status.attr, &dev_attr_status.attr,
&dev_attr_status_str.attr, &dev_attr_status_str.attr,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment