fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / infiniband / hw / ipath / ipath_file_ops.c
index bbaa70e..b932bcb 100644 (file)
 #include <asm/pgtable.h>
 
 #include "ipath_kernel.h"
-#include "ipath_layer.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_close(struct inode *, struct file *);
 static ssize_t ipath_write(struct file *, const char __user *, size_t,
@@ -58,18 +63,35 @@ static struct file_operations ipath_file_ops = {
        .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)
 {
+       struct ipath_portdata *pd = port_fp(fp);
        int ret = 0;
        struct ipath_base_info *kinfo = NULL;
        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,
-                          "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;
                goto bail;
        }
@@ -96,7 +118,9 @@ static int ipath_get_base_info(struct ipath_portdata *pd,
        kinfo->spi_rcv_egrperchunk = pd->port_rcvegrbufs_perchunk;
        kinfo->spi_rcv_egrchunksize = kinfo->spi_rcv_egrbuftotlen /
                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
         * are are configured and present
@@ -119,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
         * full 64 bit address (upper bits all 1's).  So far, using the
         * 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_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_pioavailaddr = (u64) dd->ipath_pioavailregs_phys;
        kinfo->spi_status = (u64) kinfo->spi_pioavailaddr +
                (void *) dd->ipath_statusp -
                (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_qpair = IPATH_KD_QP;
        kinfo->spi_piosize = dd->ipath_ibmaxlen;
        kinfo->spi_mtu = dd->ipath_ibmaxlen;    /* maxlen, not ibmtu */
        kinfo->spi_port = pd->port_port;
+       kinfo->spi_subport = subport_fp(fp);
        kinfo->spi_sw_version = IPATH_KERN_SWVERSION;
        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)))
                ret = -EFAULT;
 
@@ -155,6 +223,7 @@ bail:
 /**
  * ipath_tid_update - update a port TID
  * @pd: the port
+ * @fp: the ipath device file
  * @ti: the TID information
  *
  * The new implementation as of Oct 2004 is that the driver assigns
@@ -177,11 +246,11 @@ bail:
  * virtually contiguous pages, that should change to improve
  * 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)
 {
        int ret = 0, ntids;
-       u32 tid, porttid, cnt, i, tidcnt;
+       u32 tid, porttid, cnt, i, tidcnt, tidoff;
        u16 *tidlist;
        struct ipath_devdata *dd = pd->port_dd;
        u64 physaddr;
@@ -189,6 +258,7 @@ static int ipath_tid_update(struct ipath_portdata *pd,
        u64 __iomem *tidbase;
        unsigned long tidmap[8];
        struct page **pagep = NULL;
+       unsigned subport = subport_fp(fp);
 
        if (!dd->ipath_pageshadow) {
                ret = -ENOMEM;
@@ -205,20 +275,34 @@ static int ipath_tid_update(struct ipath_portdata *pd,
                ret = -EFAULT;
                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 */
                dev_info(&dd->pcidev->dev, "Process tried to allocate %u "
                         "TIDs, only trying max (%u)\n", 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));
-       tid = pd->port_tidcursor;
        /* before decrement; chip actual # */
-       porttid = pd->port_port * tidcnt;
        ntids = tidcnt;
        tidbase = (u64 __iomem *) (((char __iomem *) dd->ipath_kregbase) +
                                   dd->ipath_rcvtidbase +
@@ -275,16 +359,19 @@ static int ipath_tid_update(struct ipath_portdata *pd,
                        ret = -ENOMEM;
                        break;
                }
-               tidlist[i] = tid;
+               tidlist[i] = tid + tidoff;
                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 */
                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
                 */
                __set_bit(tid, tidmap);
-               physaddr = page_to_phys(pagep[i]);
+               physaddr = dd->ipath_physshadow[porttid + tid];
                ipath_stats.sps_pagelocks++;
                ipath_cdbg(VERBOSE,
                           "TID %u, vaddr %lx, physaddr %llx pgp %p\n",
@@ -318,6 +405,9 @@ static int ipath_tid_update(struct ipath_portdata *pd,
                                           tid);
                                dd->ipath_f_put_tid(dd, &tidbase[tid], 1,
                                                    dd->ipath_tidinvalid);
+                               pci_unmap_page(dd->pcidev,
+                                       dd->ipath_physshadow[porttid + tid],
+                                       PAGE_SIZE, PCI_DMA_FROMDEVICE);
                                dd->ipath_pageshadow[porttid + tid] = NULL;
                                ipath_stats.sps_pageunlocks++;
                        }
@@ -342,7 +432,10 @@ static int ipath_tid_update(struct ipath_portdata *pd,
                }
                if (tid == tidcnt)
                        tid = 0;
-               pd->port_tidcursor = tid;
+               if (!pd->port_subport_cnt)
+                       pd->port_tidcursor = tid;
+               else
+                       tidcursor_fp(fp) = tid;
        }
 
 done:
@@ -355,6 +448,7 @@ done:
 /**
  * ipath_tid_free - free a port TID
  * @pd: the port
+ * @subport: the subport
  * @ti: the TID info
  *
  * right now we are unlocking one page at a time, but since
@@ -368,7 +462,7 @@ done:
  * 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)
 {
        int ret = 0;
@@ -389,11 +483,20 @@ static int ipath_tid_free(struct ipath_portdata *pd,
        }
 
        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) +
                                   dd->ipath_rcvtidbase +
                                   porttid * sizeof(*tidbase));
 
-       tidcnt = dd->ipath_rcvtidcnt;
        limit = sizeof(tidmap) * BITS_PER_BYTE;
        if (limit > tidcnt)
                /* just in case size changes in future */
@@ -418,6 +521,9 @@ static int ipath_tid_free(struct ipath_portdata *pd,
                                   pd->port_pid, tid);
                        dd->ipath_f_put_tid(dd, &tidbase[tid], 1,
                                            dd->ipath_tidinvalid);
+                       pci_unmap_page(dd->pcidev,
+                               dd->ipath_physshadow[porttid + tid],
+                               PAGE_SIZE, PCI_DMA_FROMDEVICE);
                        ipath_release_user_pages(
                                &dd->ipath_pageshadow[porttid + tid], 1);
                        dd->ipath_pageshadow[porttid + tid] = NULL;
@@ -582,20 +688,23 @@ bail:
 /**
  * ipath_manage_rcvq - manage a port's receive queue
  * @pd: the port
+ * @subport: the subport
  * @start_stop: action to carry out
  *
  * start_stop == 0 disables receive on the port, for use in queue
  * overflow conditions.  start_stop==1 re-enables, to be used to
  * 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;
-       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,
-                  pd->port_port);
+                  pd->port_port, subport);
+       if (subport)
+               goto bail;
        /* atomically clear receive enable port. */
        if (start_stop) {
                /*
@@ -610,7 +719,7 @@ static int ipath_manage_rcvq(struct ipath_portdata *pd, int start_stop)
                 * updated and correct itself, even in the face of software
                 * bugs.
                 */
-               *pd->port_rcvhdrtail_kvaddr = 0;
+               *(volatile u64 *)pd->port_rcvhdrtail_kvaddr = 0;
                set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port,
                        &dd->ipath_rcvctrl);
        } else
@@ -619,7 +728,7 @@ static int ipath_manage_rcvq(struct ipath_portdata *pd, int start_stop)
        ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
                         dd->ipath_rcvctrl);
        /* now be sure chip saw it before we return */
-       tval = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
+       ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
        if (start_stop) {
                /*
                 * And try to be sure that tail reg update has happened too.
@@ -628,9 +737,10 @@ static int ipath_manage_rcvq(struct ipath_portdata *pd, int start_stop)
                 * in memory copy, since we could overwrite an update by the
                 * chip if we did.
                 */
-               tval = ipath_read_ureg32(dd, ur_rcvhdrtail, pd->port_port);
+               ipath_read_ureg32(dd, ur_rcvhdrtail, pd->port_port);
        }
        /* always; new head should be equal to new tail; see above */
+bail:
        return 0;
 }
 
@@ -688,6 +798,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
  * @pd: the port to allocate TID buffers for
@@ -703,7 +843,7 @@ static void ipath_clean_part_key(struct ipath_portdata *pd,
 static int ipath_create_user_egr(struct ipath_portdata *pd)
 {
        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;
        int ret;
        gfp_t gfp_flags;
@@ -723,31 +863,18 @@ static int ipath_create_user_egr(struct ipath_portdata *pd)
        ipath_cdbg(VERBOSE, "Allocating %d egr buffers, at egrtid "
                   "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) {
                ret = -ENOMEM;
                goto bail;
        }
        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) {
                ret = -ENOMEM;
                goto bail_rcvegrbuf;
@@ -792,105 +919,23 @@ bail_rcvegrbuf_phys:
                                  pd->port_rcvegrbuf_phys[e]);
 
        }
-       vfree(pd->port_rcvegrbuf_phys);
+       kfree(pd->port_rcvegrbuf_phys);
        pd->port_rcvegrbuf_phys = NULL;
 bail_rcvegrbuf:
-       vfree(pd->port_rcvegrbuf);
+       kfree(pd->port_rcvegrbuf);
        pd->port_rcvegrbuf = NULL;
 bail:
        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 */
 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;
-       unsigned pfn = (unsigned long)addr >> PAGE_SHIFT;
+       unsigned long pfn;
        int ret;
 
        if ((vma->vm_end - vma->vm_start) > len) {
@@ -913,17 +958,17 @@ static int ipath_mmap_mem(struct vm_area_struct *vma,
                vma->vm_flags &= ~VM_MAYWRITE;
        }
 
+       pfn = virt_to_phys(kvaddr) >> PAGE_SHIFT;
        ret = remap_pfn_range(vma, vma->vm_start, pfn,
                              len, vma->vm_page_prot);
        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
-               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:
        return ret;
 }
@@ -958,7 +1003,8 @@ static int mmap_ureg(struct vm_area_struct *vma, struct ipath_devdata *dd,
 
 static int mmap_piobufs(struct vm_area_struct *vma,
                        struct ipath_devdata *dd,
-                       struct ipath_portdata *pd)
+                       struct ipath_portdata *pd,
+                       unsigned piobufs, unsigned piocnt)
 {
        unsigned long phys;
        int ret;
@@ -969,31 +1015,32 @@ static int mmap_piobufs(struct vm_area_struct *vma,
         * process data, and catches users who might try to read the i/o
         * 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: "
                         "reqlen %lx > PAGE\n",
                         vma->vm_end - vma->vm_start);
-               ret = -EFAULT;
+               ret = -EINVAL;
                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
         * write combining behavior we want on the PIO buffers!
         */
 
-       if (vma->vm_flags & VM_READ) {
-               dev_info(&dd->pcidev->dev,
-                        "Can't map piobufs as readable (flags=%lx)\n",
-                        vma->vm_flags);
-               ret = -EPERM;
-               goto bail;
-       }
+#if defined(__powerpc__)
+       /* There isn't a generic way to specify writethrough mappings */
+       pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE;
+       pgprot_val(vma->vm_page_prot) |= _PAGE_WRITETHRU;
+       pgprot_val(vma->vm_page_prot) &= ~_PAGE_GUARDED;
+#endif
 
-       /* don't allow them to later change to readable with mprotect */
+       /*
+        * don't allow them to later change to readable with mprotect (for when
+        * not initially mapped readable, as is normally the case)
+        */
        vma->vm_flags &= ~VM_MAYREAD;
        vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND;
 
@@ -1010,7 +1057,7 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma,
        struct ipath_devdata *dd = pd->port_dd;
        unsigned long start, size;
        size_t total_size, i;
-       dma_addr_t *phys;
+       unsigned long pfn;
        int ret;
 
        size = pd->port_rcvegrbuf_size;
@@ -1020,7 +1067,7 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma,
                         "reqlen %lx > actual %lx\n",
                         vma->vm_end - vma->vm_start,
                         (unsigned long) total_size);
-               ret = -EFAULT;
+               ret = -EINVAL;
                goto bail;
        }
 
@@ -1034,11 +1081,11 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma,
        vma->vm_flags &= ~VM_MAYWRITE;
 
        start = vma->vm_start;
-       phys = pd->port_rcvegrbuf_phys;
 
        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)
                        goto bail;
        }
@@ -1048,6 +1095,122 @@ bail:
        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
  * @fp: the file pointer
@@ -1063,73 +1226,99 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma)
        struct ipath_portdata *pd;
        struct ipath_devdata *dd;
        u64 pgaddr, ureg;
+       unsigned piobufs, piocnt;
        int ret;
 
        pd = port_fp(fp);
+       if (!pd) {
+               ret = -EINVAL;
+               goto bail;
+       }
        dd = pd->port_dd;
 
        /*
         * 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
-        * 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;
 
        /*
-        * 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,
-                  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;
+       }
+
+       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;
        }
-       else if (pgaddr == ureg)
+
+       if (pgaddr == 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);
-       else if (pgaddr == (u64) pd->port_rcvhdrq_phys) {
+       else if (pgaddr == (u64) pd->port_rcvhdrq_phys)
                /*
-                * The rcvhdrq itself; readonly except on HT-400 (so have
+                * The rcvhdrq itself; readonly except on HT (so have
                 * to allow writable mapping), multiple pages, contiguous
                 * 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");
-       }
-       else if (pgaddr == (u64)pd->port_rcvhdrqtailaddr_phys)
+       else if (pgaddr == (u64) pd->port_rcvhdrqtailaddr_phys)
                /* in-memory copy of rcvhdrq tail register */
                ret = ipath_mmap_mem(vma, pd, PAGE_SIZE, 0,
-                                    pd->port_rcvhdrqtailaddr_phys,
+                                    pd->port_rcvhdrtail_kvaddr,
                                     "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
                ret = -EINVAL;
 
@@ -1137,9 +1326,10 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma)
 
        if (ret < 0)
                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;
 }
 
@@ -1149,9 +1339,12 @@ static unsigned int ipath_poll(struct file *fp,
        struct ipath_portdata *pd;
        u32 head, tail;
        int bit;
+       unsigned pollflag = 0;
        struct ipath_devdata *dd;
 
        pd = port_fp(fp);
+       if (!pd)
+               goto bail;
        dd = pd->port_dd;
 
        bit = pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT;
@@ -1174,7 +1367,7 @@ static unsigned int ipath_poll(struct file *fp,
 
        if (tail == head) {
                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,
                                               dd->ipath_rhdrhead_intr_off
                                               | head, pd->port_port);
@@ -1185,9 +1378,12 @@ static unsigned int ipath_poll(struct file *fp,
                        clear_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag);
                        pd->port_rcvwait_to++;
                }
+               else
+                       pollflag = POLLIN | POLLRDNORM;
        }
        else {
                /* it's already happened; don't do wait_event overhead */
+               pollflag = POLLIN | POLLRDNORM;
                pd->port_rcvnowait++;
        }
 
@@ -1195,18 +1391,80 @@ static unsigned int ipath_poll(struct file *fp,
        ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
                         dd->ipath_rcvctrl);
 
-       return 0;
+bail:
+       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,
-                         struct file *fp)
+                         struct file *fp,
+                         const struct ipath_user_info *uinfo)
 {
+       struct ipath_portdata *pd;
        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
@@ -1216,34 +1474,36 @@ static int try_alloc_port(struct ipath_devdata *dd, int port,
                ptmp = kmalloc(dd->ipath_rcvtidcnt * sizeof(u16) +
                               dd->ipath_rcvtidcnt * sizeof(struct page **),
                               GFP_KERNEL);
-               if (!p || !ptmp) {
+               if (!pd || !ptmp) {
                        ipath_dev_err(dd, "Unable to allocate portdata "
                                      "memory, failing open\n");
                        ret = -ENOMEM;
-                       kfree(p);
+                       kfree(pd);
                        kfree(ptmp);
                        goto bail;
                }
-               dd->ipath_pd[port] = p;
+               dd->ipath_pd[port] = pd;
                dd->ipath_pd[port]->port_port = port;
                dd->ipath_pd[port]->port_dd = dd;
                dd->ipath_pd[port]->port_tid_pg_list = ptmp;
                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",
                           current->comm, current->pid, dd->ipath_unit,
                           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++;
                ret = 0;
-               goto bail;
-       }
-       ret = -EBUSY;
+       } else
+               ret = -EBUSY;
 
 bail:
        return ret;
@@ -1259,7 +1519,8 @@ static inline int usable(struct ipath_devdata *dd)
                                     | 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);
        int ret, i;
@@ -1274,8 +1535,8 @@ static int find_free_port(int unit, struct file *fp)
                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)
                        goto bail;
        }
@@ -1285,26 +1546,27 @@ bail:
        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 maxofallports, npresent, nup;
        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
         * specify the layout of processes to processors before opening
         * this driver, and then we'll assign the process to the "closest"
-        * HT-400 to that processor (we assume reasonable connectivity,
+        * InfiniPath chip to that processor (we assume reasonable connectivity,
         * for now).  This code assumes that if affinity has been set
         * before this point, that at most one cpu is set; for now this
         * is reasonable.  I check for both cpus_empty() and cpus_full(),
         * in case some kernel variant sets none of the bits when no
         * affinity is set.  2.6.11 and 12 kernels have all present
         * cpus set.  Some day we'll have to fix it up further to handle
-        * a cpu subset.  This algorithm fails for two HT-400's connected
+        * a cpu subset.  This algorithm fails for two HT chips connected
         * in tunnel fashion.  Eventually this needs real topology
         * information.  There may be some issues with dual core numbering
         * as well.  This needs more work prior to release.
@@ -1338,8 +1600,6 @@ static int find_best_unit(struct file *fp)
 
        if (prefunit != -1)
                devmax = prefunit + 1;
-       else
-               devmax = ipath_count_units(NULL, NULL, NULL);
 recheck:
        for (i = 1; i < maxofallports; i++) {
                for (ndev = prefunit != -1 ? prefunit : 0; ndev < devmax;
@@ -1354,7 +1614,7 @@ recheck:
                                 * next.
                                 */
                                continue;
-                       ret = try_alloc_port(dd, i, fp);
+                       ret = try_alloc_port(dd, i, fp, uinfo);
                        if (!ret)
                                goto done;
                }
@@ -1390,22 +1650,183 @@ done:
        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)
 {
-       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);
 
-       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_path.dentry->d_inode) - IPATH_USER_MINOR_BASE;
        ipath_cdbg(VERBOSE, "open on dev %lx (minor %d)\n",
-                  (long)in->i_rdev, user_minor);
+                  (long)fp->f_path.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
-               ret = find_best_unit(fp);
+               ret = find_best_unit(fp, uinfo);
 
        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;
 }
 
@@ -1428,6 +1849,8 @@ static void unlock_expected_tids(struct ipath_portdata *pd)
                if (!dd->ipath_pageshadow[i])
                        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],
                                                  1);
                dd->ipath_pageshadow[i] = NULL;
@@ -1448,6 +1871,7 @@ static void unlock_expected_tids(struct ipath_portdata *pd)
 static int ipath_close(struct inode *in, struct file *fp)
 {
        int ret = 0;
+       struct ipath_filedata *fd;
        struct ipath_portdata *pd;
        struct ipath_devdata *dd;
        unsigned port;
@@ -1457,9 +1881,24 @@ static int ipath_close(struct inode *in, struct file *fp)
 
        mutex_lock(&ipath_mutex);
 
-       pd = port_fp(fp);
-       port = pd->port_port;
+       fd = (struct ipath_filedata *) fp->private_data;
        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;
 
        if (pd->port_hdrqfull) {
@@ -1498,8 +1937,6 @@ static int ipath_close(struct inode *in, struct file *fp)
 
                /* clean up the pkeys for this port user */
                ipath_clean_part_key(pd, dd);
-
-
                /*
                 * be paranoid, and never write 0's to these, just use an
                 * unused part of the port 0 tail page.  Of course,
@@ -1518,39 +1955,49 @@ static int ipath_close(struct inode *in, struct file *fp)
                i = dd->ipath_pbufsport * (port - 1);
                ipath_disarm_piobufs(dd, i, dd->ipath_pbufsport);
 
+               dd->ipath_f_clear_tids(dd, pd->port_port);
+
                if (dd->ipath_pageshadow)
                        unlock_expected_tids(pd);
                ipath_stats.sps_ports--;
                ipath_cdbg(PROC, "%s[%u] closed port %u:%u\n",
                           pd->port_comm, pd->port_pid,
                           dd->ipath_unit, port);
-
-               dd->ipath_f_clear_tids(dd, pd->port_port);
        }
 
-       pd->port_cnt = 0;
        pd->port_pid = 0;
-
        dd->ipath_pd[pd->port_port] = NULL; /* before releasing mutex */
        mutex_unlock(&ipath_mutex);
        ipath_free_pddata(dd, pd); /* after releasing the mutex */
 
+bail:
+       kfree(fd);
        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 info;
        int nup;
        int ret;
+       size_t sz;
 
        (void) ipath_count_units(NULL, &nup, NULL);
        info.num_active = nup;
        info.unit = pd->port_dd->ipath_unit;
        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;
                goto bail;
        }
@@ -1560,6 +2007,16 @@ bail:
        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,
                           size_t count, loff_t *off)
 {
@@ -1586,6 +2043,8 @@ static ssize_t ipath_write(struct file *fp, const char __user *data,
        consumed = sizeof(cmd.type);
 
        switch (cmd.type) {
+       case IPATH_CMD_ASSIGN_PORT:
+       case __IPATH_CMD_USER_INIT:
        case IPATH_CMD_USER_INIT:
                copy = sizeof(cmd.cmd.user_info);
                dest = &cmd.cmd.user_info;
@@ -1612,6 +2071,11 @@ static ssize_t ipath_write(struct file *fp, const char __user *data,
                dest = &cmd.cmd.part_key;
                src = &ucmd->cmd.part_key;
                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:
                ret = -EINVAL;
                goto bail;
@@ -1629,34 +2093,55 @@ static ssize_t ipath_write(struct file *fp, const char __user *data,
 
        consumed += copy;
        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) {
+       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:
-               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;
                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_size);
                break;
        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;
        case IPATH_CMD_PORT_INFO:
-               ret = ipath_port_info(pd,
+               ret = ipath_port_info(pd, subport_fp(fp),
                                      (struct ipath_port_info __user *)
                                      (unsigned long) cmd.cmd.port_info);
                break;
        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;
        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;
        case IPATH_CMD_SET_PART_KEY:
                ret = ipath_set_part_key(pd, cmd.cmd.part_key);
                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)
@@ -1815,7 +2300,7 @@ int ipath_user_add(struct ipath_devdata *dd)
                if (ret < 0) {
                        ipath_dev_err(dd, "Could not create wildcard "
                                      "minor: error %d\n", -ret);
-                       goto bail_sma;
+                       goto bail_user;
                }
 
                atomic_set(&user_setup, 1);
@@ -1831,7 +2316,7 @@ int ipath_user_add(struct ipath_devdata *dd)
 
        goto bail;
 
-bail_sma:
+bail_user:
        user_cleanup();
 bail:
        return ret;
@@ -1853,4 +2338,3 @@ void ipath_user_remove(struct ipath_devdata *dd)
 bail:
        return;
 }
-