fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / infiniband / hw / ipath / ipath_file_ops.c
index ada267e..b932bcb 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (c) 2006 QLogic, Inc. All rights reserved.
  * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
 #include <asm/pgtable.h>
 
 #include "ipath_kernel.h"
-#include "ips_common.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 *);
@@ -57,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;
        }
@@ -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_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
@@ -118,30 +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_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;
 
@@ -153,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
@@ -175,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;
@@ -187,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;
@@ -203,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 +
@@ -273,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",
@@ -316,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++;
                        }
@@ -340,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:
@@ -353,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
@@ -366,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;
@@ -387,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 */
@@ -416,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;
@@ -456,7 +564,7 @@ static int ipath_set_part_key(struct ipath_portdata *pd, u16 key)
        u16 lkey = key & 0x7FFF;
        int ret;
 
-       if (lkey == (IPS_DEFAULT_P_KEY & 0x7FFF)) {
+       if (lkey == (IPATH_DEFAULT_P_KEY & 0x7FFF)) {
                /* nothing to do; this key always valid */
                ret = 0;
                goto bail;
@@ -580,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) {
                /*
@@ -608,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
@@ -617,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.
@@ -626,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;
 }
 
@@ -686,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
@@ -701,9 +843,18 @@ 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;
+
+       /*
+        * GFP_USER, but without GFP_FS, so buffer cache can be
+        * coalesced (we hope); otherwise, even at order 4,
+        * heavy filesystem activity makes these fail, and we can
+        * use compound pages.
+        */
+       gfp_flags = __GFP_WAIT | __GFP_IO | __GFP_COMP;
 
        egrcnt = dd->ipath_rcvegrcnt;
        /* TID number offset for this port */
@@ -712,44 +863,23 @@ 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 waiting
-        * more memory again.  It seems likely that the coalescing in
-        * free_pages, etc. still has issues (as it has had previously
-        * during 2.6.x development).
-        */
-       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;
        }
        for (e = 0; e < pd->port_rcvegrbuf_chunks; e++) {
-               /*
-                * GFP_USER, but without GFP_FS, so buffer cache can be
-                * coalesced (we hope); otherwise, even at order 4,
-                * heavy filesystem activity makes these fail
-                */
-               gfp_t gfp_flags = __GFP_WAIT | __GFP_IO | __GFP_COMP;
 
                pd->port_rcvegrbuf[e] = dma_alloc_coherent(
                        &dd->pcidev->dev, size, &pd->port_rcvegrbuf_phys[e],
@@ -783,154 +913,63 @@ static int ipath_create_user_egr(struct ipath_portdata *pd)
 
 bail_rcvegrbuf_phys:
        for (e = 0; e < pd->port_rcvegrbuf_chunks &&
-                    pd->port_rcvegrbuf[e]; e++)
+               pd->port_rcvegrbuf[e]; e++) {
                dma_free_coherent(&dd->pcidev->dev, size,
                                  pd->port_rcvegrbuf[e],
                                  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)
+
+/* 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,
+       void *kvaddr, char *what)
 {
-       int ret = 0;
        struct ipath_devdata *dd = pd->port_dd;
-       u64 physaddr, uaddr, off, atmp;
-       struct page *pagep;
-       u32 head32;
-       u64 head;
+       unsigned long pfn;
+       int ret;
 
-       /* for now, if major version is different, bail */
-       if ((uinfo->spu_userversion >> 16) != IPATH_USER_SWMAJOR) {
+       if ((vma->vm_end - vma->vm_start) > len) {
                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;
+                        "FAIL on %s: len %lx > %x\n", what,
+                        vma->vm_end - vma->vm_start, len);
+               ret = -EFAULT;
+               goto bail;
        }
 
-       /* for now we do nothing with rcvhdrcnt: uinfo->spu_rcvhdrcnt */
-
-       /* set up for the rcvhdr Q tail register writeback to user memory */
-       if (!uinfo->spu_rcvhdraddr ||
-           !access_ok(VERIFY_WRITE, (u64 __user *) (unsigned long)
-                      uinfo->spu_rcvhdraddr, sizeof(u64))) {
-               ipath_dbg("Port %d rcvhdrtail addr %llx not valid\n",
-                         pd->port_port,
-                         (unsigned long long) uinfo->spu_rcvhdraddr);
-               ret = -EINVAL;
-               goto done;
-       }
+       if (!write_ok) {
+               if (vma->vm_flags & VM_WRITE) {
+                       dev_info(&dd->pcidev->dev,
+                                "%s must be mapped readonly\n", what);
+                       ret = -EPERM;
+                       goto bail;
+               }
 
-       off = offset_in_page(uinfo->spu_rcvhdraddr);
-       uaddr = PAGE_MASK & (unsigned long) uinfo->spu_rcvhdraddr;
-       ret = ipath_get_user_pages_nocopy(uaddr, &pagep);
-       if (ret) {
-               dev_info(&dd->pcidev->dev, "Failed to lookup and lock "
-                        "address %llx for rcvhdrtail: errno %d\n",
-                        (unsigned long long) uinfo->spu_rcvhdraddr, -ret);
-               goto done;
+               /* don't allow them to later change with mprotect */
+               vma->vm_flags &= ~VM_MAYWRITE;
        }
-       ipath_stats.sps_pagelocks++;
-       pd->port_rcvhdrtail_uaddr = uaddr;
-       pd->port_rcvhdrtail_pagep = pagep;
-       pd->port_rcvhdrtail_kvaddr =
-               page_address(pagep);
-       pd->port_rcvhdrtail_kvaddr += off;
-       physaddr = page_to_phys(pagep) + off;
-       ipath_cdbg(VERBOSE, "port %d user addr %llx hdrtailaddr, %llx "
-                  "physical (off=%llx)\n",
-                  pd->port_port,
-                  (unsigned long long) uinfo->spu_rcvhdraddr,
-                  (unsigned long long) physaddr, (unsigned long long) off);
-       ipath_write_kreg_port(dd, dd->ipath_kregs->kr_rcvhdrtailaddr,
-                             pd->port_port, physaddr);
-       atmp = ipath_read_kreg64_port(dd,
-                                     dd->ipath_kregs->kr_rcvhdrtailaddr,
-                                     pd->port_port);
-       if (physaddr != atmp) {
-               ipath_dev_err(dd,
-                             "Catastrophic software error, "
-                             "RcvHdrTailAddr%u written as %llx, "
-                             "read back as %llx\n", pd->port_port,
-                             (unsigned long long) physaddr,
-                             (unsigned long long) atmp);
-               ret = -EINVAL;
-               goto done;
-       }
-
-       /* 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);
+       pfn = virt_to_phys(kvaddr) >> PAGE_SHIFT;
+       ret = remap_pfn_range(vma, vma->vm_start, pfn,
+                             len, vma->vm_page_prot);
        if (ret)
-               goto done;
-       /* enable receives now */
-       /* atomically set enable bit for this port */
-       set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port,
-               &dd->ipath_rcvctrl);
-
-       /*
-        * set the head registers 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_rcvhdrtail, pd->port_port);
-       head = (u64) head32;
-       ipath_write_ureg(dd, ur_rcvhdrhead, head, pd->port_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 head %llx, egrhead %x from "
-                  "tail regs\n", pd->port_port,
-                  (unsigned long long) head, 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.
-        */
-       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:
+               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, pfn, len,
+                          write_ok?'w':'o');
+bail:
        return ret;
 }
 
@@ -940,8 +979,11 @@ static int mmap_ureg(struct vm_area_struct *vma, struct ipath_devdata *dd,
        unsigned long phys;
        int ret;
 
-       /* it's the real hardware, so io_remap works */
-
+       /*
+        * This is real hardware, so use io_remap.  This is the mechanism
+        * for the user process to update the head registers for their port
+        * in the chip.
+        */
        if ((vma->vm_end - vma->vm_start) > PAGE_SIZE) {
                dev_info(&dd->pcidev->dev, "FAIL mmap userreg: reqlen "
                         "%lx > PAGE\n", vma->vm_end - vma->vm_start);
@@ -961,44 +1003,45 @@ 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;
 
        /*
-        * When we map the PIO buffers, we want to map them as writeonly, no
-        * read possible.
+        * When we map the PIO buffers in the chip, we want to map them as
+        * writeonly, no read possible.   This prevents access to previous
+        * 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;
+
        /*
-        * Do *NOT* mark this as non-cached (PWT bit), or we don't get the
+        * Don't mark this as non-cached, or we don't get the
         * write combining behavior we want on the PIO buffers!
-        * vma->vm_page_prot =
-        *        pgprot_noncached(vma->vm_page_prot);
         */
 
-       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;
-       }
-
-       /* don't allow them to later change to readable with mprotect */
+#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
 
-       vma->vm_flags &= ~VM_MAYWRITE;
+       /*
+        * 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;
 
        ret = io_remap_pfn_range(vma, vma->vm_start, phys >> PAGE_SHIFT,
@@ -1014,14 +1057,9 @@ 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;
 
-       if (!pd->port_rcvegrbuf) {
-               ret = -EFAULT;
-               goto bail;
-       }
-
        size = pd->port_rcvegrbuf_size;
        total_size = pd->port_rcvegrbuf_chunks * size;
        if ((vma->vm_end - vma->vm_start) > total_size) {
@@ -1029,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;
        }
 
@@ -1039,16 +1077,15 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma,
                ret = -EPERM;
                goto bail;
        }
-
-       start = vma->vm_start;
-       phys = pd->port_rcvegrbuf_phys;
-
        /* don't allow them to later change to writeable with mprotect */
        vma->vm_flags &= ~VM_MAYWRITE;
 
+       start = vma->vm_start;
+
        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;
        }
@@ -1058,74 +1095,118 @@ bail:
        return ret;
 }
 
-static int mmap_rcvhdrq(struct vm_area_struct *vma,
-                       struct ipath_portdata *pd)
+/*
+ * 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)
 {
-       struct ipath_devdata *dd = pd->port_dd;
-       size_t total_size;
-       int ret;
+       unsigned long offset = address - vma->vm_start;
+       struct page *page = NOPAGE_SIGBUS;
+       void *pageptr;
 
        /*
-        * kmalloc'ed memory, physically contiguous; this is from
-        * spi_rcvhdr_base; we allow user to map read-write so they can
-        * write hdrq entries to allow protocol code to directly poll
-        * whether a hdrq entry has been written.
+        * Convert the vmalloc address into a struct page.
         */
-       total_size = ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize *
-                          sizeof(u32), PAGE_SIZE);
-       if ((vma->vm_end - vma->vm_start) > total_size) {
-               dev_info(&dd->pcidev->dev,
-                        "FAIL on rcvhdrq: reqlen %lx > actual %lx\n",
-                        vma->vm_end - vma->vm_start,
-                        (unsigned long) total_size);
-               ret = -EFAULT;
-               goto bail;
-       }
-
-       ret = remap_pfn_range(vma, vma->vm_start,
-                             pd->port_rcvhdrq_phys >> PAGE_SHIFT,
-                             vma->vm_end - vma->vm_start,
-                             vma->vm_page_prot);
-bail:
-       return ret;
+       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 int mmap_pioavailregs(struct vm_area_struct *vma,
-                            struct ipath_portdata *pd)
+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)
 {
-       struct ipath_devdata *dd = pd->port_dd;
+       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;
+
        /*
-        * when we map the PIO bufferavail registers, we want to map them as
-        * readonly, no write possible.
-        *
-        * kmalloc'ed memory, physically contiguous, one page only, readonly
+        * Master has all the slave uregbase, rcvhdrq, and
+        * rcvegrbufs mmapped.
         */
-
-       if ((vma->vm_end - vma->vm_start) > PAGE_SIZE) {
-               dev_info(&dd->pcidev->dev, "FAIL on pioavailregs_dma: "
-                        "reqlen %lx > actual %lx\n",
-                        vma->vm_end - vma->vm_start,
-                        (unsigned long) PAGE_SIZE);
-               ret = -EFAULT;
+       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;
        }
-
-       if (vma->vm_flags & VM_WRITE) {
-               dev_info(&dd->pcidev->dev,
-                        "Can't map pioavailregs as writable (flags=%lx)\n",
-                        vma->vm_flags);
-               ret = -EPERM;
+       len = vma->vm_end - vma->vm_start;
+       if (len > size) {
+               ipath_cdbg(MM, "FAIL: reqlen %lx > %zx\n", len, size);
+               ret = -EINVAL;
                goto bail;
        }
 
-       /* don't allow them to later change with mprotect */
-       vma->vm_flags &= ~VM_MAYWRITE;
+       vma->vm_pgoff = (unsigned long) addr >> PAGE_SHIFT;
+       vma->vm_ops = &ipath_file_vm_ops;
+       vma->vm_flags |= VM_RESERVED | VM_DONTEXPAND;
+       ret = 0;
 
-       ret = remap_pfn_range(vma, vma->vm_start,
-                             dd->ipath_pioavailregs_phys >> PAGE_SHIFT,
-                             PAGE_SIZE, vma->vm_page_prot);
 bail:
        return ret;
 }
@@ -1145,41 +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;
 
        /*
-        * note that ureg does *NOT* have the kregvirt as part of it, to be
-        * sure that for 32 bit programs, we don't end up trying to map a >
-        * 44 address.  Has to match ipath_get_base_info() code that sets
-        * __spi_uregbase
+        * Check for 0 in case one of the allocations failed, but user
+        * called mmap anyway.
         */
+       if (!pgaddr)  {
+               ret = -EINVAL;
+               goto bail;
+       }
 
-       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));
+
+       /*
+        * 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;
+       }
 
        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)
-               ret = mmap_rcvhdrq(vma, pd);
-       else if (pgaddr == dd->ipath_pioavailregs_phys)
-               ret = mmap_pioavailregs(vma, pd);
+               /*
+                * The rcvhdrq itself; readonly except on HT (so have
+                * to allow writable mapping), multiple pages, contiguous
+                * from an i/o perspective.
+                */
+               ret = ipath_mmap_mem(vma, pd, pd->port_rcvhdrq_size, 1,
+                                    pd->port_rcvhdrq,
+                                    "rcvhdrq");
+       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_rcvhdrtail_kvaddr,
+                                    "rcvhdrq tail");
        else
                ret = -EINVAL;
 
@@ -1187,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;
 }
 
@@ -1199,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;
@@ -1224,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);
@@ -1235,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++;
        }
 
@@ -1245,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
@@ -1266,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;
@@ -1309,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;
@@ -1324,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;
        }
@@ -1335,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.
@@ -1388,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;
@@ -1404,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;
                }
@@ -1440,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, 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);
 
-       minor = iminor(in);
+       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, minor);
+                  (long)fp->f_path.dentry->d_inode->i_rdev, i_minor);
 
-       if (minor)
-               ret = find_free_port(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;
 }
 
@@ -1478,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;
@@ -1498,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;
@@ -1507,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) {
@@ -1536,70 +1925,79 @@ static int ipath_close(struct inode *in, struct file *fp)
        }
 
        if (dd->ipath_kregbase) {
-               if (pd->port_rcvhdrtail_uaddr) {
-                       pd->port_rcvhdrtail_uaddr = 0;
-                       pd->port_rcvhdrtail_kvaddr = NULL;
-                       ipath_release_user_pages_on_close(
-                               &pd->port_rcvhdrtail_pagep, 1);
-                       pd->port_rcvhdrtail_pagep = NULL;
-                       ipath_stats.sps_pageunlocks++;
-               }
-               ipath_write_kreg_port(
-                       dd, dd->ipath_kregs->kr_rcvhdrtailaddr,
-                       port, 0ULL);
-               ipath_write_kreg_port(
-                       dd, dd->ipath_kregs->kr_rcvhdraddr,
-                       pd->port_port, 0);
+               int i;
+               /* atomically clear receive enable port. */
+               clear_bit(INFINIPATH_R_PORTENABLE_SHIFT + port,
+                         &dd->ipath_rcvctrl);
+               ipath_write_kreg( dd, dd->ipath_kregs->kr_rcvctrl,
+                       dd->ipath_rcvctrl);
+               /* and read back from chip to be sure that nothing
+                * else is in flight when we do the rest */
+               (void)ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
 
                /* 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,
+                * rcvhdraddr points to a large chunk of memory, so this
+                * could still trash things, but at least it won't trash
+                * page 0, and by disabling the port, it should stop "soon",
+                * even if a packet or two is in already in flight after we
+                * disabled the port.
+                */
+               ipath_write_kreg_port(dd,
+                       dd->ipath_kregs->kr_rcvhdrtailaddr, port,
+                       dd->ipath_dummy_hdrq_phys);
+               ipath_write_kreg_port(dd, dd->ipath_kregs->kr_rcvhdraddr,
+                       pd->port_port, dd->ipath_dummy_hdrq_phys);
 
-               if (port < dd->ipath_cfgports) {
-                       int i = dd->ipath_pbufsport * (port - 1);
-                       ipath_disarm_piobufs(dd, i, dd->ipath_pbufsport);
+               i = dd->ipath_pbufsport * (port - 1);
+               ipath_disarm_piobufs(dd, i, dd->ipath_pbufsport);
 
-                       /* atomically clear receive enable port. */
-                       clear_bit(INFINIPATH_R_PORTENABLE_SHIFT + port,
-                                 &dd->ipath_rcvctrl);
-                       ipath_write_kreg(
-                               dd,
-                               dd->ipath_kregs->kr_rcvctrl,
-                               dd->ipath_rcvctrl);
-
-                       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);
+
+               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);
        }
 
-       pd->port_cnt = 0;
        pd->port_pid = 0;
-
-       dd->ipath_f_clear_tids(dd, pd->port_port);
-
-       ipath_free_pddata(dd, pd->port_port, 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;
        }
@@ -1609,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)
 {
@@ -1635,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;
@@ -1661,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;
@@ -1678,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)
@@ -1859,19 +2295,12 @@ int ipath_user_add(struct ipath_devdata *dd)
                                      "error %d\n", -ret);
                        goto bail;
                }
-               ret = ipath_diag_init();
-               if (ret < 0) {
-                       ipath_dev_err(dd, "Unable to set up diag support: "
-                                     "error %d\n", -ret);
-                       goto bail_sma;
-               }
-
                ret = init_cdev(0, "ipath", &ipath_file_ops, &wildcard_cdev,
                                &wildcard_class_dev);
                if (ret < 0) {
                        ipath_dev_err(dd, "Could not create wildcard "
                                      "minor: error %d\n", -ret);
-                       goto bail_diag;
+                       goto bail_user;
                }
 
                atomic_set(&user_setup, 1);
@@ -1880,31 +2309,28 @@ int ipath_user_add(struct ipath_devdata *dd)
        snprintf(name, sizeof(name), "ipath%d", dd->ipath_unit);
 
        ret = init_cdev(dd->ipath_unit + 1, name, &ipath_file_ops,
-                       &dd->cdev, &dd->class_dev);
+                       &dd->user_cdev, &dd->user_class_dev);
        if (ret < 0)
                ipath_dev_err(dd, "Could not create user minor %d, %s\n",
                              dd->ipath_unit + 1, name);
 
        goto bail;
 
-bail_diag:
-       ipath_diag_cleanup();
-bail_sma:
+bail_user:
        user_cleanup();
 bail:
        return ret;
 }
 
-void ipath_user_del(struct ipath_devdata *dd)
+void ipath_user_remove(struct ipath_devdata *dd)
 {
-       cleanup_cdev(&dd->cdev, &dd->class_dev);
+       cleanup_cdev(&dd->user_cdev, &dd->user_class_dev);
 
        if (atomic_dec_return(&user_count) == 0) {
                if (atomic_read(&user_setup) == 0)
                        goto bail;
 
                cleanup_cdev(&wildcard_cdev, &wildcard_class_dev);
-               ipath_diag_cleanup();
                user_cleanup();
 
                atomic_set(&user_setup, 0);