| /* |
| * Copyright (c) 2006, 2007, 2008 QLogic Corporation. 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 |
| * licenses. You may choose to be licensed under the terms of the GNU |
| * General Public License (GPL) Version 2, available from the file |
| * COPYING in the main directory of this source tree, or the |
| * OpenIB.org BSD license below: |
| * |
| * Redistribution and use in source and binary forms, with or |
| * without modification, are permitted provided that the following |
| * conditions are met: |
| * |
| * - Redistributions of source code must retain the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer. |
| * |
| * - Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials |
| * provided with the distribution. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| #include <linux/pci.h> |
| #include <linux/poll.h> |
| #include <linux/cdev.h> |
| #include <linux/swap.h> |
| #include <linux/vmalloc.h> |
| #include <linux/highmem.h> |
| #include <linux/io.h> |
| #include <linux/jiffies.h> |
| #include <asm/pgtable.h> |
| |
| #include "ipath_kernel.h" |
| #include "ipath_common.h" |
| #include "ipath_user_sdma.h" |
| |
| 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, |
| loff_t *); |
| static ssize_t ipath_writev(struct kiocb *, const struct iovec *, |
| unsigned long , loff_t); |
| static unsigned int ipath_poll(struct file *, struct poll_table_struct *); |
| static int ipath_mmap(struct file *, struct vm_area_struct *); |
| |
| static const struct file_operations ipath_file_ops = { |
| .owner = THIS_MODULE, |
| .write = ipath_write, |
| .aio_write = ipath_writev, |
| .open = ipath_open, |
| .release = ipath_close, |
| .poll = ipath_poll, |
| .mmap = ipath_mmap |
| }; |
| |
| /* |
| * Convert kernel virtual addresses to physical addresses so they don't |
| * potentially conflict with the chip addresses used as mmap offsets. |
| * It doesn't really matter what mmap offset we use as long as we can |
| * interpret it correctly. |
| */ |
| static u64 cvt_kvaddr(void *p) |
| { |
| struct page *page; |
| u64 paddr = 0; |
| |
| page = vmalloc_to_page(p); |
| if (page) |
| paddr = page_to_pfn(page) << PAGE_SHIFT; |
| |
| return paddr; |
| } |
| |
| 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); |
| } |
| |
| sz = sizeof(*kinfo); |
| /* If port sharing is not requested, allow the old size structure */ |
| if (!shared) |
| sz -= 7 * sizeof(u64); |
| if (ubase_size < sz) { |
| ipath_cdbg(PROC, |
| "Base size %zu, need %zu (version mismatch?)\n", |
| ubase_size, sz); |
| ret = -EINVAL; |
| goto bail; |
| } |
| |
| kinfo = kzalloc(sizeof(*kinfo), GFP_KERNEL); |
| if (kinfo == NULL) { |
| ret = -ENOMEM; |
| goto bail; |
| } |
| |
| ret = dd->ipath_f_get_base_info(pd, kinfo); |
| if (ret < 0) |
| goto bail; |
| |
| kinfo->spi_rcvhdr_cnt = dd->ipath_rcvhdrcnt; |
| kinfo->spi_rcvhdrent_size = dd->ipath_rcvhdrentsize; |
| kinfo->spi_tidegrcnt = dd->ipath_rcvegrcnt; |
| kinfo->spi_rcv_egrbufsize = dd->ipath_rcvegrbufsize; |
| /* |
| * have to mmap whole thing |
| */ |
| kinfo->spi_rcv_egrbuftotlen = |
| pd->port_rcvegrbuf_chunks * pd->port_rcvegrbuf_size; |
| 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 / 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 |
| */ |
| kinfo->spi_nports = dd->ipath_cfgports; |
| /* unit (chip/board) our port is on */ |
| kinfo->spi_unit = dd->ipath_unit; |
| /* for now, only a single page */ |
| kinfo->spi_tid_maxsize = PAGE_SIZE; |
| |
| /* |
| * Doing this per port, and based on the skip value, etc. This has |
| * to be the actual buffer size, since the protocol code treats it |
| * as an array. |
| * |
| * These have to be set to user addresses in the user code via mmap. |
| * These values are used on return to user code for the mmap target |
| * addresses only. For 32 bit, same 44 bit address problem, so use |
| * the physical address, not virtual. Before 2.6.11, using the |
| * page_address() macro worked, but in 2.6.11, even that returns the |
| * full 64 bit address (upper bits all 1's). So far, using the |
| * physical addresses (or chip offsets, for chip mapping) works, but |
| * 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; |
| if (!shared) { |
| kinfo->spi_piocnt = pd->port_piocnt; |
| kinfo->spi_piobufbase = (u64) pd->port_piobufs; |
| kinfo->__spi_uregbase = (u64) dd->ipath_uregbase + |
| dd->ipath_ureg_align * pd->port_port; |
| } else if (master) { |
| kinfo->spi_piocnt = (pd->port_piocnt / subport_cnt) + |
| (pd->port_piocnt % subport_cnt); |
| /* Master's PIO buffers are after all the slave's */ |
| kinfo->spi_piobufbase = (u64) pd->port_piobufs + |
| dd->ipath_palign * |
| (pd->port_piocnt - kinfo->spi_piocnt); |
| } else { |
| unsigned slave = subport_fp(fp) - 1; |
| |
| kinfo->spi_piocnt = pd->port_piocnt / subport_cnt; |
| kinfo->spi_piobufbase = (u64) pd->port_piobufs + |
| dd->ipath_palign * kinfo->spi_piocnt * slave; |
| } |
| |
| if (shared) { |
| kinfo->spi_port_uregbase = (u64) dd->ipath_uregbase + |
| dd->ipath_ureg_align * pd->port_port; |
| kinfo->spi_port_rcvegrbuf = kinfo->spi_rcv_egrbufs; |
| kinfo->spi_port_rcvhdr_base = kinfo->spi_rcvhdr_base; |
| kinfo->spi_port_rcvhdr_tailaddr = kinfo->spi_rcvhdr_tailaddr; |
| |
| kinfo->__spi_uregbase = cvt_kvaddr(pd->subport_uregbase + |
| PAGE_SIZE * subport_fp(fp)); |
| |
| kinfo->spi_rcvhdr_base = cvt_kvaddr(pd->subport_rcvhdr_base + |
| pd->port_rcvhdrq_size * subport_fp(fp)); |
| kinfo->spi_rcvhdr_tailaddr = 0; |
| kinfo->spi_rcv_egrbufs = cvt_kvaddr(pd->subport_rcvegrbuf + |
| pd->port_rcvegrbuf_chunks * pd->port_rcvegrbuf_size * |
| subport_fp(fp)); |
| |
| kinfo->spi_subport_uregbase = |
| cvt_kvaddr(pd->subport_uregbase); |
| kinfo->spi_subport_rcvegrbuf = |
| cvt_kvaddr(pd->subport_rcvegrbuf); |
| kinfo->spi_subport_rcvhdr_base = |
| cvt_kvaddr(pd->subport_rcvhdr_base); |
| 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); |
| } |
| |
| kinfo->spi_pioindex = (kinfo->spi_piobufbase - dd->ipath_piobufbase) / |
| dd->ipath_palign; |
| kinfo->spi_pioalign = dd->ipath_palign; |
| |
| kinfo->spi_qpair = IPATH_KD_QP; |
| /* |
| * user mode PIO buffers are always 2KB, even when 4KB can |
| * be received, and sent via the kernel; this is ibmaxlen |
| * for 2K MTU. |
| */ |
| kinfo->spi_piosize = dd->ipath_piosize2k - 2 * sizeof(u32); |
| 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; |
| } |
| |
| sz = (ubase_size < sizeof(*kinfo)) ? ubase_size : sizeof(*kinfo); |
| if (copy_to_user(ubase, kinfo, sz)) |
| ret = -EFAULT; |
| |
| bail: |
| kfree(kinfo); |
| return ret; |
| } |
| |
| /** |
| * 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 |
| * the tid and returns it to the caller. To make it easier to |
| * catch bugs, and to reduce search time, we keep a cursor for |
| * each port, walking the shadow tid array to find one that's not |
| * in use. |
| * |
| * For now, if we can't allocate the full list, we fail, although |
| * in the long run, we'll allocate as many as we can, and the |
| * caller will deal with that by trying the remaining pages later. |
| * That means that when we fail, we have to mark the tids as not in |
| * use again, in our shadow copy. |
| * |
| * It's up to the caller to free the tids when they are done. |
| * We'll unlock the pages as they free them. |
| * |
| * Also, right now we are locking one page at a time, but since |
| * the intended use of this routine is for a single group of |
| * virtually contiguous pages, that should change to improve |
| * performance. |
| */ |
| 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, tidoff; |
| u16 *tidlist; |
| struct ipath_devdata *dd = pd->port_dd; |
| u64 physaddr; |
| unsigned long vaddr; |
| u64 __iomem *tidbase; |
| unsigned long tidmap[8]; |
| struct page **pagep = NULL; |
| unsigned subport = subport_fp(fp); |
| |
| if (!dd->ipath_pageshadow) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| cnt = ti->tidcnt; |
| if (!cnt) { |
| ipath_dbg("After copyin, tidcnt 0, tidlist %llx\n", |
| (unsigned long long) ti->tidlist); |
| /* |
| * Should we treat as success? likely a bug |
| */ |
| ret = -EFAULT; |
| goto done; |
| } |
| 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)[tidoff]; |
| tidlist = &((u16 *) &pagep[dd->ipath_rcvtidcnt])[tidoff]; |
| |
| memset(tidmap, 0, sizeof(tidmap)); |
| /* before decrement; chip actual # */ |
| ntids = tidcnt; |
| tidbase = (u64 __iomem *) (((char __iomem *) dd->ipath_kregbase) + |
| dd->ipath_rcvtidbase + |
| porttid * sizeof(*tidbase)); |
| |
| ipath_cdbg(VERBOSE, "Port%u %u tids, cursor %u, tidbase %p\n", |
| pd->port_port, cnt, tid, tidbase); |
| |
| /* virtual address of first page in transfer */ |
| vaddr = ti->tidvaddr; |
| if (!access_ok(VERIFY_WRITE, (void __user *) vaddr, |
| cnt * PAGE_SIZE)) { |
| ipath_dbg("Fail vaddr %p, %u pages, !access_ok\n", |
| (void *)vaddr, cnt); |
| ret = -EFAULT; |
| goto done; |
| } |
| ret = ipath_get_user_pages(vaddr, cnt, pagep); |
| if (ret) { |
| if (ret == -EBUSY) { |
| ipath_dbg("Failed to lock addr %p, %u pages " |
| "(already locked)\n", |
| (void *) vaddr, cnt); |
| /* |
| * for now, continue, and see what happens but with |
| * the new implementation, this should never happen, |
| * unless perhaps the user has mpin'ed the pages |
| * themselves (something we need to test) |
| */ |
| ret = 0; |
| } else { |
| dev_info(&dd->pcidev->dev, |
| "Failed to lock addr %p, %u pages: " |
| "errno %d\n", (void *) vaddr, cnt, -ret); |
| goto done; |
| } |
| } |
| for (i = 0; i < cnt; i++, vaddr += PAGE_SIZE) { |
| for (; ntids--; tid++) { |
| if (tid == tidcnt) |
| tid = 0; |
| if (!dd->ipath_pageshadow[porttid + tid]) |
| break; |
| } |
| if (ntids < 0) { |
| /* |
| * oops, wrapped all the way through their TIDs, |
| * and didn't have enough free; see comments at |
| * start of routine |
| */ |
| ipath_dbg("Not enough free TIDs for %u pages " |
| "(index %d), failing\n", cnt, i); |
| i--; /* last tidlist[i] not filled in */ |
| ret = -ENOMEM; |
| break; |
| } |
| tidlist[i] = tid + tidoff; |
| ipath_cdbg(VERBOSE, "Updating idx %u to TID %u, " |
| "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 = dd->ipath_physshadow[porttid + tid]; |
| ipath_stats.sps_pagelocks++; |
| ipath_cdbg(VERBOSE, |
| "TID %u, vaddr %lx, physaddr %llx pgp %p\n", |
| tid, vaddr, (unsigned long long) physaddr, |
| pagep[i]); |
| dd->ipath_f_put_tid(dd, &tidbase[tid], RCVHQ_RCV_TYPE_EXPECTED, |
| physaddr); |
| /* |
| * don't check this tid in ipath_portshadow, since we |
| * just filled it in; start with the next one. |
| */ |
| tid++; |
| } |
| |
| if (ret) { |
| u32 limit; |
| cleanup: |
| /* jump here if copy out of updated info failed... */ |
| ipath_dbg("After failure (ret=%d), undo %d of %d entries\n", |
| -ret, i, cnt); |
| /* same code that's in ipath_free_tid() */ |
| limit = sizeof(tidmap) * BITS_PER_BYTE; |
| if (limit > tidcnt) |
| /* just in case size changes in future */ |
| limit = tidcnt; |
| tid = find_first_bit((const unsigned long *)tidmap, limit); |
| for (; tid < limit; tid++) { |
| if (!test_bit(tid, tidmap)) |
| continue; |
| if (dd->ipath_pageshadow[porttid + tid]) { |
| ipath_cdbg(VERBOSE, "Freeing TID %u\n", |
| tid); |
| dd->ipath_f_put_tid(dd, &tidbase[tid], |
| RCVHQ_RCV_TYPE_EXPECTED, |
| 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++; |
| } |
| } |
| ipath_release_user_pages(pagep, cnt); |
| } else { |
| /* |
| * Copy the updated array, with ipath_tid's filled in, back |
| * to user. Since we did the copy in already, this "should |
| * never fail" If it does, we have to clean up... |
| */ |
| if (copy_to_user((void __user *) |
| (unsigned long) ti->tidlist, |
| tidlist, cnt * sizeof(*tidlist))) { |
| ret = -EFAULT; |
| goto cleanup; |
| } |
| if (copy_to_user((void __user *) (unsigned long) ti->tidmap, |
| tidmap, sizeof tidmap)) { |
| ret = -EFAULT; |
| goto cleanup; |
| } |
| if (tid == tidcnt) |
| tid = 0; |
| if (!pd->port_subport_cnt) |
| pd->port_tidcursor = tid; |
| else |
| tidcursor_fp(fp) = tid; |
| } |
| |
| done: |
| if (ret) |
| ipath_dbg("Failed to map %u TID pages, failing with %d\n", |
| ti->tidcnt, -ret); |
| return ret; |
| } |
| |
| /** |
| * 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 |
| * the intended use of this routine is for a single group of |
| * virtually contiguous pages, that should change to improve |
| * performance. We check that the TID is in range for this port |
| * but otherwise don't check validity; if user has an error and |
| * frees the wrong tid, it's only their own data that can thereby |
| * be corrupted. We do check that the TID was in use, for sanity |
| * We always use our idea of the saved address, not the address that |
| * they pass in to us. |
| */ |
| |
| static int ipath_tid_free(struct ipath_portdata *pd, unsigned subport, |
| const struct ipath_tid_info *ti) |
| { |
| int ret = 0; |
| u32 tid, porttid, cnt, limit, tidcnt; |
| struct ipath_devdata *dd = pd->port_dd; |
| u64 __iomem *tidbase; |
| unsigned long tidmap[8]; |
| |
| if (!dd->ipath_pageshadow) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| if (copy_from_user(tidmap, (void __user *)(unsigned long)ti->tidmap, |
| sizeof tidmap)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| 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)); |
| |
| limit = sizeof(tidmap) * BITS_PER_BYTE; |
| if (limit > tidcnt) |
| /* just in case size changes in future */ |
| limit = tidcnt; |
| tid = find_first_bit(tidmap, limit); |
| ipath_cdbg(VERBOSE, "Port%u free %u tids; first bit (max=%d) " |
| "set is %d, porttid %u\n", pd->port_port, ti->tidcnt, |
| limit, tid, porttid); |
| for (cnt = 0; tid < limit; tid++) { |
| /* |
| * small optimization; if we detect a run of 3 or so without |
| * any set, use find_first_bit again. That's mainly to |
| * accelerate the case where we wrapped, so we have some at |
| * the beginning, and some at the end, and a big gap |
| * in the middle. |
| */ |
| if (!test_bit(tid, tidmap)) |
| continue; |
| cnt++; |
| if (dd->ipath_pageshadow[porttid + tid]) { |
| struct page *p; |
| p = dd->ipath_pageshadow[porttid + tid]; |
| dd->ipath_pageshadow[porttid + tid] = NULL; |
| ipath_cdbg(VERBOSE, "PID %u freeing TID %u\n", |
| pd->port_pid, tid); |
| dd->ipath_f_put_tid(dd, &tidbase[tid], |
| RCVHQ_RCV_TYPE_EXPECTED, |
| dd->ipath_tidinvalid); |
| pci_unmap_page(dd->pcidev, |
| dd->ipath_physshadow[porttid + tid], |
| PAGE_SIZE, PCI_DMA_FROMDEVICE); |
| ipath_release_user_pages(&p, 1); |
| ipath_stats.sps_pageunlocks++; |
| } else |
| ipath_dbg("Unused tid %u, ignoring\n", tid); |
| } |
| if (cnt != ti->tidcnt) |
| ipath_dbg("passed in tidcnt %d, only %d bits set in map\n", |
| ti->tidcnt, cnt); |
| done: |
| if (ret) |
| ipath_dbg("Failed to unmap %u TID pages, failing with %d\n", |
| ti->tidcnt, -ret); |
| return ret; |
| } |
| |
| /** |
| * ipath_set_part_key - set a partition key |
| * @pd: the port |
| * @key: the key |
| * |
| * We can have up to 4 active at a time (other than the default, which is |
| * always allowed). This is somewhat tricky, since multiple ports may set |
| * the same key, so we reference count them, and clean up at exit. All 4 |
| * partition keys are packed into a single infinipath register. It's an |
| * error for a process to set the same pkey multiple times. We provide no |
| * mechanism to de-allocate a pkey at this time, we may eventually need to |
| * do that. I've used the atomic operations, and no locking, and only make |
| * a single pass through what's available. This should be more than |
| * adequate for some time. I'll think about spinlocks or the like if and as |
| * it's necessary. |
| */ |
| static int ipath_set_part_key(struct ipath_portdata *pd, u16 key) |
| { |
| struct ipath_devdata *dd = pd->port_dd; |
| int i, any = 0, pidx = -1; |
| u16 lkey = key & 0x7FFF; |
| int ret; |
| |
| if (lkey == (IPATH_DEFAULT_P_KEY & 0x7FFF)) { |
| /* nothing to do; this key always valid */ |
| ret = 0; |
| goto bail; |
| } |
| |
| ipath_cdbg(VERBOSE, "p%u try to set pkey %hx, current keys " |
| "%hx:%x %hx:%x %hx:%x %hx:%x\n", |
| pd->port_port, key, dd->ipath_pkeys[0], |
| atomic_read(&dd->ipath_pkeyrefs[0]), dd->ipath_pkeys[1], |
| atomic_read(&dd->ipath_pkeyrefs[1]), dd->ipath_pkeys[2], |
| atomic_read(&dd->ipath_pkeyrefs[2]), dd->ipath_pkeys[3], |
| atomic_read(&dd->ipath_pkeyrefs[3])); |
| |
| if (!lkey) { |
| ipath_cdbg(PROC, "p%u tries to set key 0, not allowed\n", |
| pd->port_port); |
| ret = -EINVAL; |
| goto bail; |
| } |
| |
| /* |
| * Set the full membership bit, because it has to be |
| * set in the register or the packet, and it seems |
| * cleaner to set in the register than to force all |
| * callers to set it. (see bug 4331) |
| */ |
| key |= 0x8000; |
| |
| for (i = 0; i < ARRAY_SIZE(pd->port_pkeys); i++) { |
| if (!pd->port_pkeys[i] && pidx == -1) |
| pidx = i; |
| if (pd->port_pkeys[i] == key) { |
| ipath_cdbg(VERBOSE, "p%u tries to set same pkey " |
| "(%x) more than once\n", |
| pd->port_port, key); |
| ret = -EEXIST; |
| goto bail; |
| } |
| } |
| if (pidx == -1) { |
| ipath_dbg("All pkeys for port %u already in use, " |
| "can't set %x\n", pd->port_port, key); |
| ret = -EBUSY; |
| goto bail; |
| } |
| for (any = i = 0; i < ARRAY_SIZE(dd->ipath_pkeys); i++) { |
| if (!dd->ipath_pkeys[i]) { |
| any++; |
| continue; |
| } |
| if (dd->ipath_pkeys[i] == key) { |
| atomic_t *pkrefs = &dd->ipath_pkeyrefs[i]; |
| |
| if (atomic_inc_return(pkrefs) > 1) { |
| pd->port_pkeys[pidx] = key; |
| ipath_cdbg(VERBOSE, "p%u set key %x " |
| "matches #%d, count now %d\n", |
| pd->port_port, key, i, |
| atomic_read(pkrefs)); |
| ret = 0; |
| goto bail; |
| } else { |
| /* |
| * lost race, decrement count, catch below |
| */ |
| atomic_dec(pkrefs); |
| ipath_cdbg(VERBOSE, "Lost race, count was " |
| "0, after dec, it's %d\n", |
| atomic_read(pkrefs)); |
| any++; |
| } |
| } |
| if ((dd->ipath_pkeys[i] & 0x7FFF) == lkey) { |
| /* |
| * It makes no sense to have both the limited and |
| * full membership PKEY set at the same time since |
| * the unlimited one will disable the limited one. |
| */ |
| ret = -EEXIST; |
| goto bail; |
| } |
| } |
| if (!any) { |
| ipath_dbg("port %u, all pkeys already in use, " |
| "can't set %x\n", pd->port_port, key); |
| ret = -EBUSY; |
| goto bail; |
| } |
| for (any = i = 0; i < ARRAY_SIZE(dd->ipath_pkeys); i++) { |
| if (!dd->ipath_pkeys[i] && |
| atomic_inc_return(&dd->ipath_pkeyrefs[i]) == 1) { |
| u64 pkey; |
| |
| /* for ipathstats, etc. */ |
| ipath_stats.sps_pkeys[i] = lkey; |
| pd->port_pkeys[pidx] = dd->ipath_pkeys[i] = key; |
| pkey = |
| (u64) dd->ipath_pkeys[0] | |
| ((u64) dd->ipath_pkeys[1] << 16) | |
| ((u64) dd->ipath_pkeys[2] << 32) | |
| ((u64) dd->ipath_pkeys[3] << 48); |
| ipath_cdbg(PROC, "p%u set key %x in #%d, " |
| "portidx %d, new pkey reg %llx\n", |
| pd->port_port, key, i, pidx, |
| (unsigned long long) pkey); |
| ipath_write_kreg( |
| dd, dd->ipath_kregs->kr_partitionkey, pkey); |
| |
| ret = 0; |
| goto bail; |
| } |
| } |
| ipath_dbg("port %u, all pkeys already in use 2nd pass, " |
| "can't set %x\n", pd->port_port, key); |
| ret = -EBUSY; |
| |
| bail: |
| return ret; |
| } |
| |
| /** |
| * 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, unsigned subport, |
| int start_stop) |
| { |
| struct ipath_devdata *dd = pd->port_dd; |
| |
| ipath_cdbg(PROC, "%sabling rcv for unit %u port %u:%u\n", |
| start_stop ? "en" : "dis", dd->ipath_unit, |
| pd->port_port, subport); |
| if (subport) |
| goto bail; |
| /* atomically clear receive enable port. */ |
| if (start_stop) { |
| /* |
| * On enable, force in-memory copy of the tail register to |
| * 0, so that protocol code doesn't have to worry about |
| * whether or not the chip has yet updated the in-memory |
| * copy or not on return from the system call. The chip |
| * always resets it's tail register back to 0 on a |
| * transition from disabled to enabled. This could cause a |
| * problem if software was broken, and did the enable w/o |
| * the disable, but eventually the in-memory copy will be |
| * updated and correct itself, even in the face of software |
| * bugs. |
| */ |
| if (pd->port_rcvhdrtail_kvaddr) |
| ipath_clear_rcvhdrtail(pd); |
| set_bit(dd->ipath_r_portenable_shift + pd->port_port, |
| &dd->ipath_rcvctrl); |
| } else |
| clear_bit(dd->ipath_r_portenable_shift + pd->port_port, |
| &dd->ipath_rcvctrl); |
| ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, |
| dd->ipath_rcvctrl); |
| /* now be sure chip saw it before we return */ |
| ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch); |
| if (start_stop) { |
| /* |
| * And try to be sure that tail reg update has happened too. |
| * This should in theory interlock with the RXE changes to |
| * the tail register. Don't assign it to the tail register |
| * in memory copy, since we could overwrite an update by the |
| * chip if we did. |
| */ |
| ipath_read_ureg32(dd, ur_rcvhdrtail, pd->port_port); |
| } |
| /* always; new head should be equal to new tail; see above */ |
| bail: |
| return 0; |
| } |
| |
| static void ipath_clean_part_key(struct ipath_portdata *pd, |
| struct ipath_devdata *dd) |
| { |
| int i, j, pchanged = 0; |
| u64 oldpkey; |
| |
| /* for debugging only */ |
| oldpkey = (u64) dd->ipath_pkeys[0] | |
| ((u64) dd->ipath_pkeys[1] << 16) | |
| ((u64) dd->ipath_pkeys[2] << 32) | |
| ((u64) dd->ipath_pkeys[3] << 48); |
| |
| for (i = 0; i < ARRAY_SIZE(pd->port_pkeys); i++) { |
| if (!pd->port_pkeys[i]) |
| continue; |
| ipath_cdbg(VERBOSE, "look for key[%d] %hx in pkeys\n", i, |
| pd->port_pkeys[i]); |
| for (j = 0; j < ARRAY_SIZE(dd->ipath_pkeys); j++) { |
| /* check for match independent of the global bit */ |
| if ((dd->ipath_pkeys[j] & 0x7fff) != |
| (pd->port_pkeys[i] & 0x7fff)) |
| continue; |
| if (atomic_dec_and_test(&dd->ipath_pkeyrefs[j])) { |
| ipath_cdbg(VERBOSE, "p%u clear key " |
| "%x matches #%d\n", |
| pd->port_port, |
| pd->port_pkeys[i], j); |
| ipath_stats.sps_pkeys[j] = |
| dd->ipath_pkeys[j] = 0; |
| pchanged++; |
| } |
| else ipath_cdbg( |
| VERBOSE, "p%u key %x matches #%d, " |
| "but ref still %d\n", pd->port_port, |
| pd->port_pkeys[i], j, |
| atomic_read(&dd->ipath_pkeyrefs[j])); |
| break; |
| } |
| pd->port_pkeys[i] = 0; |
| } |
| if (pchanged) { |
| u64 pkey = (u64) dd->ipath_pkeys[0] | |
| ((u64) dd->ipath_pkeys[1] << 16) | |
| ((u64) dd->ipath_pkeys[2] << 32) | |
| ((u64) dd->ipath_pkeys[3] << 48); |
| ipath_cdbg(VERBOSE, "p%u old pkey reg %llx, " |
| "new pkey reg %llx\n", pd->port_port, |
| (unsigned long long) oldpkey, |
| (unsigned long long) pkey); |
| ipath_write_kreg(dd, dd->ipath_kregs->kr_partitionkey, |
| pkey); |
| } |
| } |
| |
| /* |
| * 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 |
| * |
| * This routine is now quite different for user and kernel, because |
| * the kernel uses skb's, for the accelerated network performance |
| * This is the user port version |
| * |
| * Allocate the eager TID buffers and program them into infinipath |
| * They are no longer completely contiguous, we do multiple allocation |
| * calls. |
| */ |
| static int ipath_create_user_egr(struct ipath_portdata *pd) |
| { |
| struct ipath_devdata *dd = pd->port_dd; |
| 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 */ |
| egroff = (pd->port_port - 1) * egrcnt + dd->ipath_p0_rcvegrcnt; |
| egrsize = dd->ipath_rcvegrbufsize; |
| ipath_cdbg(VERBOSE, "Allocating %d egr buffers, at egrtid " |
| "offset %x, egrsize %u\n", egrcnt, egroff, egrsize); |
| |
| 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 = |
| 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++) { |
| |
| pd->port_rcvegrbuf[e] = dma_alloc_coherent( |
| &dd->pcidev->dev, size, &pd->port_rcvegrbuf_phys[e], |
| gfp_flags); |
| |
| if (!pd->port_rcvegrbuf[e]) { |
| ret = -ENOMEM; |
| goto bail_rcvegrbuf_phys; |
| } |
| } |
| |
| pd->port_rcvegr_phys = pd->port_rcvegrbuf_phys[0]; |
| |
| for (e = chunk = 0; chunk < pd->port_rcvegrbuf_chunks; chunk++) { |
| dma_addr_t pa = pd->port_rcvegrbuf_phys[chunk]; |
| unsigned i; |
| |
| for (i = 0; e < egrcnt && i < egrperchunk; e++, i++) { |
| dd->ipath_f_put_tid(dd, e + egroff + |
| (u64 __iomem *) |
| ((char __iomem *) |
| dd->ipath_kregbase + |
| dd->ipath_rcvegrbase), |
| RCVHQ_RCV_TYPE_EAGER, pa); |
| pa += egrsize; |
| } |
| cond_resched(); /* don't hog the cpu */ |
| } |
| |
| ret = 0; |
| goto bail; |
| |
| bail_rcvegrbuf_phys: |
| for (e = 0; e < pd->port_rcvegrbuf_chunks && |
| pd->port_rcvegrbuf[e]; e++) { |
| dma_free_coherent(&dd->pcidev->dev, size, |
| pd->port_rcvegrbuf[e], |
| pd->port_rcvegrbuf_phys[e]); |
| |
| } |
| kfree(pd->port_rcvegrbuf_phys); |
| pd->port_rcvegrbuf_phys = NULL; |
| bail_rcvegrbuf: |
| kfree(pd->port_rcvegrbuf); |
| pd->port_rcvegrbuf = NULL; |
| bail: |
| 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, |
| void *kvaddr, char *what) |
| { |
| struct ipath_devdata *dd = pd->port_dd; |
| unsigned long pfn; |
| int ret; |
| |
| if ((vma->vm_end - vma->vm_start) > len) { |
| dev_info(&dd->pcidev->dev, |
| "FAIL on %s: len %lx > %x\n", what, |
| vma->vm_end - vma->vm_start, len); |
| ret = -EFAULT; |
| goto bail; |
| } |
| |
| 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; |
| } |
| |
| /* don't allow them to later change with mprotect */ |
| 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, |
| 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; |
| } |
| |
| static int mmap_ureg(struct vm_area_struct *vma, struct ipath_devdata *dd, |
| u64 ureg) |
| { |
| unsigned long phys; |
| int ret; |
| |
| /* |
| * 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); |
| ret = -EFAULT; |
| } else { |
| phys = dd->ipath_physaddr + ureg; |
| vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); |
| |
| vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND; |
| ret = io_remap_pfn_range(vma, vma->vm_start, |
| phys >> PAGE_SHIFT, |
| vma->vm_end - vma->vm_start, |
| vma->vm_page_prot); |
| } |
| return ret; |
| } |
| |
| static int mmap_piobufs(struct vm_area_struct *vma, |
| struct ipath_devdata *dd, |
| struct ipath_portdata *pd, |
| unsigned piobufs, unsigned piocnt) |
| { |
| unsigned long phys; |
| int ret; |
| |
| /* |
| * 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) > (piocnt * dd->ipath_palign)) { |
| dev_info(&dd->pcidev->dev, "FAIL mmap piobufs: " |
| "reqlen %lx > PAGE\n", |
| vma->vm_end - vma->vm_start); |
| ret = -EINVAL; |
| goto bail; |
| } |
| |
| phys = dd->ipath_physaddr + piobufs; |
| |
| #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 (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, |
| vma->vm_end - vma->vm_start, |
| vma->vm_page_prot); |
| bail: |
| return ret; |
| } |
| |
| static int mmap_rcvegrbufs(struct vm_area_struct *vma, |
| struct ipath_portdata *pd) |
| { |
| struct ipath_devdata *dd = pd->port_dd; |
| unsigned long start, size; |
| size_t total_size, i; |
| unsigned long pfn; |
| int ret; |
| |
| size = pd->port_rcvegrbuf_size; |
| total_size = pd->port_rcvegrbuf_chunks * size; |
| if ((vma->vm_end - vma->vm_start) > total_size) { |
| dev_info(&dd->pcidev->dev, "FAIL on egr bufs: " |
| "reqlen %lx > actual %lx\n", |
| vma->vm_end - vma->vm_start, |
| (unsigned long) total_size); |
| ret = -EINVAL; |
| goto bail; |
| } |
| |
| 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 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) { |
| 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; |
| } |
| ret = 0; |
| |
| bail: |
| return ret; |
| } |
| |
| /* |
| * ipath_file_vma_fault - handle a VMA page fault. |
| */ |
| static int ipath_file_vma_fault(struct vm_area_struct *vma, |
| struct vm_fault *vmf) |
| { |
| struct page *page; |
| |
| page = vmalloc_to_page((void *)(vmf->pgoff << PAGE_SHIFT)); |
| if (!page) |
| return VM_FAULT_SIGBUS; |
| get_page(page); |
| vmf->page = page; |
| |
| return 0; |
| } |
| |
| static struct vm_operations_struct ipath_file_vm_ops = { |
| .fault = ipath_file_vma_fault, |
| }; |
| |
| 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 = 0; |
| |
| /* If the port is not shared, all addresses should be physical */ |
| if (!pd->port_subport_cnt) |
| goto bail; |
| |
| dd = pd->port_dd; |
| size = pd->port_rcvegrbuf_chunks * pd->port_rcvegrbuf_size; |
| |
| /* |
| * Each process has all the subport uregbase, rcvhdrq, and |
| * rcvegrbufs mmapped - as an array for all the processes, |
| * and also separately for this process. |
| */ |
| if (pgaddr == cvt_kvaddr(pd->subport_uregbase)) { |
| addr = pd->subport_uregbase; |
| size = PAGE_SIZE * pd->port_subport_cnt; |
| } else if (pgaddr == cvt_kvaddr(pd->subport_rcvhdr_base)) { |
| addr = pd->subport_rcvhdr_base; |
| size = pd->port_rcvhdrq_size * pd->port_subport_cnt; |
| } else if (pgaddr == cvt_kvaddr(pd->subport_rcvegrbuf)) { |
| addr = pd->subport_rcvegrbuf; |
| size *= pd->port_subport_cnt; |
| } else if (pgaddr == cvt_kvaddr(pd->subport_uregbase + |
| PAGE_SIZE * subport)) { |
| addr = pd->subport_uregbase + PAGE_SIZE * subport; |
| size = PAGE_SIZE; |
| } else if (pgaddr == cvt_kvaddr(pd->subport_rcvhdr_base + |
| pd->port_rcvhdrq_size * subport)) { |
| addr = pd->subport_rcvhdr_base + |
| pd->port_rcvhdrq_size * subport; |
| size = pd->port_rcvhdrq_size; |
| } else if (pgaddr == cvt_kvaddr(pd->subport_rcvegrbuf + |
| size * subport)) { |
| addr = pd->subport_rcvegrbuf + size * subport; |
| /* 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 { |
| 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 = 1; |
| |
| bail: |
| return ret; |
| } |
| |
| /** |
| * ipath_mmap - mmap various structures into user space |
| * @fp: the file pointer |
| * @vma: the VM area |
| * |
| * We use this to have a shared buffer between the kernel and the user code |
| * for the rcvhdr queue, egr buffers, and the per-port user regs and pio |
| * buffers in the chip. We have the open and close entries so we can bump |
| * the ref count and keep the driver from being unloaded while still mapped. |
| */ |
| 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 |
| * 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; |
| |
| /* |
| * Check for 0 in case one of the allocations failed, but user |
| * called mmap anyway. |
| */ |
| if (!pgaddr) { |
| ret = -EINVAL; |
| goto bail; |
| } |
| |
| 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, 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. |
| */ |
| ret = mmap_kvaddr(vma, pgaddr, pd, subport_fp(fp)); |
| if (ret) { |
| if (ret > 0) |
| ret = 0; |
| goto bail; |
| } |
| |
| ureg = dd->ipath_uregbase + dd->ipath_ureg_align * pd->port_port; |
| if (!pd->port_subport_cnt) { |
| /* port is not shared */ |
| piocnt = pd->port_piocnt; |
| piobufs = pd->port_piobufs; |
| } else if (!subport_fp(fp)) { |
| /* caller is the master */ |
| piocnt = (pd->port_piocnt / pd->port_subport_cnt) + |
| (pd->port_piocnt % pd->port_subport_cnt); |
| piobufs = pd->port_piobufs + |
| dd->ipath_palign * (pd->port_piocnt - piocnt); |
| } else { |
| unsigned slave = subport_fp(fp) - 1; |
| |
| /* caller is a slave */ |
| piocnt = pd->port_piocnt / 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 == 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 (pgaddr == pd->port_rcvegr_phys) |
| ret = mmap_rcvegrbufs(vma, pd); |
| else if (pgaddr == (u64) pd->port_rcvhdrq_phys) |
| /* |
| * 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; |
| |
| vma->vm_private_data = NULL; |
| |
| if (ret < 0) |
| dev_info(&dd->pcidev->dev, |
| "Failure %d on off %llx len %lx\n", |
| -ret, (unsigned long long)pgaddr, |
| vma->vm_end - vma->vm_start); |
| bail: |
| return ret; |
| } |
| |
| static unsigned ipath_poll_hdrqfull(struct ipath_portdata *pd) |
| { |
| unsigned pollflag = 0; |
| |
| if ((pd->poll_type & IPATH_POLL_TYPE_OVERFLOW) && |
| pd->port_hdrqfull != pd->port_hdrqfull_poll) { |
| pollflag |= POLLIN | POLLRDNORM; |
| pd->port_hdrqfull_poll = pd->port_hdrqfull; |
| } |
| |
| return pollflag; |
| } |
| |
| static unsigned int ipath_poll_urgent(struct ipath_portdata *pd, |
| struct file *fp, |
| struct poll_table_struct *pt) |
| { |
| unsigned pollflag = 0; |
| struct ipath_devdata *dd; |
| |
| dd = pd->port_dd; |
| |
| /* variable access in ipath_poll_hdrqfull() needs this */ |
| rmb(); |
| pollflag = ipath_poll_hdrqfull(pd); |
| |
| if (pd->port_urgent != pd->port_urgent_poll) { |
| pollflag |= POLLIN | POLLRDNORM; |
| pd->port_urgent_poll = pd->port_urgent; |
| } |
| |
| if (!pollflag) { |
| /* this saves a spin_lock/unlock in interrupt handler... */ |
| set_bit(IPATH_PORT_WAITING_URG, &pd->port_flag); |
| /* flush waiting flag so don't miss an event... */ |
| wmb(); |
| poll_wait(fp, &pd->port_wait, pt); |
| } |
| |
| return pollflag; |
| } |
| |
| static unsigned int ipath_poll_next(struct ipath_portdata *pd, |
| struct file *fp, |
| struct poll_table_struct *pt) |
| { |
| u32 head; |
| u32 tail; |
| unsigned pollflag = 0; |
| struct ipath_devdata *dd; |
| |
| dd = pd->port_dd; |
| |
| /* variable access in ipath_poll_hdrqfull() needs this */ |
| rmb(); |
| pollflag = ipath_poll_hdrqfull(pd); |
| |
| head = ipath_read_ureg32(dd, ur_rcvhdrhead, pd->port_port); |
| if (pd->port_rcvhdrtail_kvaddr) |
| tail = ipath_get_rcvhdrtail(pd); |
| else |
| tail = ipath_read_ureg32(dd, ur_rcvhdrtail, pd->port_port); |
| |
| if (head != tail) |
| pollflag |= POLLIN | POLLRDNORM; |
| else { |
| /* this saves a spin_lock/unlock in interrupt handler */ |
| set_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag); |
| /* flush waiting flag so we don't miss an event */ |
| wmb(); |
| |
| set_bit(pd->port_port + dd->ipath_r_intravail_shift, |
| &dd->ipath_rcvctrl); |
| |
| ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, |
| dd->ipath_rcvctrl); |
| |
| if (dd->ipath_rhdrhead_intr_off) /* arm rcv interrupt */ |
| ipath_write_ureg(dd, ur_rcvhdrhead, |
| dd->ipath_rhdrhead_intr_off | head, |
| pd->port_port); |
| |
| poll_wait(fp, &pd->port_wait, pt); |
| } |
| |
| return pollflag; |
| } |
| |
| static unsigned int ipath_poll(struct file *fp, |
| struct poll_table_struct *pt) |
| { |
| struct ipath_portdata *pd; |
| unsigned pollflag; |
| |
| pd = port_fp(fp); |
| if (!pd) |
| pollflag = 0; |
| else if (pd->poll_type & IPATH_POLL_TYPE_URGENT) |
| pollflag = ipath_poll_urgent(pd, fp, pt); |
| else |
| pollflag = ipath_poll_next(pd, fp, pt); |
| |
| return pollflag; |
| } |
| |
| static int ipath_supports_subports(int user_swmajor, int user_swminor) |
| { |
| /* no subport implementation prior to software version 1.3 */ |
| return (user_swmajor > 1) || (user_swminor >= 3); |
| } |
| |
| static int ipath_compatible_subports(int user_swmajor, int user_swminor) |
| { |
| /* this code is written long-hand for clarity */ |
| if (IPATH_USER_SWMAJOR != user_swmajor) { |
| /* no promise of compatibility if major mismatch */ |
| return 0; |
| } |
| if (IPATH_USER_SWMAJOR == 1) { |
| switch (IPATH_USER_SWMINOR) { |
| case 0: |
| case 1: |
| case 2: |
| /* no subport implementation so cannot be compatible */ |
| return 0; |
| case 3: |
| /* 3 is only compatible with itself */ |
| return user_swminor == 3; |
| default: |
| /* >= 4 are compatible (or are expected to be) */ |
| return user_swminor >= 4; |
| } |
| } |
| /* make no promises yet for future major versions */ |
| return 0; |
| } |
| |
| static int init_subports(struct ipath_devdata *dd, |
| struct ipath_portdata *pd, |
| const struct ipath_user_info *uinfo) |
| { |
| int ret = 0; |
| unsigned num_subports; |
| size_t size; |
| |
| /* |
| * If the user is requesting zero subports, |
| * skip the subport allocation. |
| */ |
| if (uinfo->spu_subport_cnt <= 0) |
| goto bail; |
| |
| /* Self-consistency check for ipath_compatible_subports() */ |
| if (ipath_supports_subports(IPATH_USER_SWMAJOR, IPATH_USER_SWMINOR) && |
| !ipath_compatible_subports(IPATH_USER_SWMAJOR, |
| IPATH_USER_SWMINOR)) { |
| dev_info(&dd->pcidev->dev, |
| "Inconsistent ipath_compatible_subports()\n"); |
| goto bail; |
| } |
| |
| /* Check for subport compatibility */ |
| if (!ipath_compatible_subports(uinfo->spu_userversion >> 16, |
| uinfo->spu_userversion & 0xffff)) { |
| dev_info(&dd->pcidev->dev, |
| "Mismatched user version (%d.%d) and driver " |
| "version (%d.%d) while port sharing. Ensure " |
| "that driver and library are from the same " |
| "release.\n", |
| (int) (uinfo->spu_userversion >> 16), |
| (int) (uinfo->spu_userversion & 0xffff), |
| IPATH_USER_SWMAJOR, |
| IPATH_USER_SWMINOR); |
| goto bail; |
| } |
| if (uinfo->spu_subport_cnt > INFINIPATH_MAX_SUBPORT) { |
| ret = -EINVAL; |
| goto bail; |
| } |
| |
| num_subports = uinfo->spu_subport_cnt; |
| pd->subport_uregbase = vmalloc(PAGE_SIZE * num_subports); |
| 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_subports; |
| 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_subports); |
| 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; |
| set_bit(IPATH_PORT_MASTER_UNINIT, &pd->port_flag); |
| memset(pd->subport_uregbase, 0, PAGE_SIZE * num_subports); |
| memset(pd->subport_rcvhdr_base, 0, size); |
| memset(pd->subport_rcvegrbuf, 0, pd->port_rcvegrbuf_chunks * |
| pd->port_rcvegrbuf_size * |
| num_subports); |
| 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, |
| const struct ipath_user_info *uinfo) |
| { |
| struct ipath_portdata *pd; |
| int ret; |
| |
| if (!(pd = dd->ipath_pd[port])) { |
| void *ptmp; |
| |
| pd = kzalloc(sizeof(struct ipath_portdata), GFP_KERNEL); |
| |
| /* |
| * Allocate memory for use in ipath_tid_update() just once |
| * at open, not per call. Reduces cost of expected send |
| * setup. |
| */ |
| ptmp = kmalloc(dd->ipath_rcvtidcnt * sizeof(u16) + |
| dd->ipath_rcvtidcnt * sizeof(struct page **), |
| GFP_KERNEL); |
| if (!pd || !ptmp) { |
| ipath_dev_err(dd, "Unable to allocate portdata " |
| "memory, failing open\n"); |
| ret = -ENOMEM; |
| kfree(pd); |
| kfree(ptmp); |
| goto bail; |
| } |
| 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 (!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); |
| 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; |
| } else |
| ret = -EBUSY; |
| |
| bail: |
| return ret; |
| } |
| |
| static inline int usable(struct ipath_devdata *dd) |
| { |
| return dd && |
| (dd->ipath_flags & IPATH_PRESENT) && |
| dd->ipath_kregbase && |
| dd->ipath_lid && |
| !(dd->ipath_flags & (IPATH_LINKDOWN | IPATH_DISABLED |
| | IPATH_LINKUNK)); |
| } |
| |
| 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; |
| |
| if (!dd) { |
| ret = -ENODEV; |
| goto bail; |
| } |
| |
| if (!usable(dd)) { |
| ret = -ENETDOWN; |
| goto bail; |
| } |
| |
| for (i = 1; i < dd->ipath_cfgports; i++) { |
| ret = try_alloc_port(dd, i, fp, uinfo); |
| if (ret != -EBUSY) |
| goto bail; |
| } |
| ret = -EBUSY; |
| |
| bail: |
| return ret; |
| } |
| |
| 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; |
| |
| 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" |
| * 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 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. |
| */ |
| if (!cpus_empty(current->cpus_allowed) && |
| !cpus_full(current->cpus_allowed)) { |
| int ncpus = num_online_cpus(), curcpu = -1, nset = 0; |
| for (i = 0; i < ncpus; i++) |
| if (cpu_isset(i, current->cpus_allowed)) { |
| ipath_cdbg(PROC, "%s[%u] affinity set for " |
| "cpu %d/%d\n", current->comm, |
| current->pid, i, ncpus); |
| curcpu = i; |
| nset++; |
| } |
| if (curcpu != -1 && nset != ncpus) { |
| if (npresent) { |
| prefunit = curcpu / (ncpus / npresent); |
| ipath_cdbg(PROC,"%s[%u] %d chips, %d cpus, " |
| "%d cpus/chip, select unit %d\n", |
| current->comm, current->pid, |
| npresent, ncpus, ncpus / npresent, |
| prefunit); |
| } |
| } |
| } |
| |
| /* |
| * user ports start at 1, kernel port is 0 |
| * For now, we do round-robin access across all chips |
| */ |
| |
| if (prefunit != -1) |
| devmax = prefunit + 1; |
| recheck: |
| for (i = 1; i < maxofallports; i++) { |
| for (ndev = prefunit != -1 ? prefunit : 0; ndev < devmax; |
| ndev++) { |
| struct ipath_devdata *dd = ipath_lookup(ndev); |
| |
| if (!usable(dd)) |
| continue; /* can't use this unit */ |
| if (i >= dd->ipath_cfgports) |
| /* |
| * Maxed out on users of this unit. Try |
| * next. |
| */ |
| continue; |
| ret = try_alloc_port(dd, i, fp, uinfo); |
| if (!ret) |
| goto done; |
| } |
| } |
| |
| if (npresent) { |
| if (nup == 0) { |
| ret = -ENETDOWN; |
| ipath_dbg("No ports available (none initialized " |
| "and ready)\n"); |
| } else { |
| if (prefunit > 0) { |
| /* if started above 0, retry from 0 */ |
| ipath_cdbg(PROC, |
| "%s[%u] no ports on prefunit " |
| "%d, clear and re-check\n", |
| current->comm, current->pid, |
| prefunit); |
| devmax = ipath_count_units(NULL, NULL, |
| NULL); |
| prefunit = -1; |
| goto recheck; |
| } |
| ret = -EBUSY; |
| ipath_dbg("No ports available\n"); |
| } |
| } else { |
| ret = -ENXIO; |
| ipath_dbg("No boards found\n"); |
| } |
| |
| 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 (!usable(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++; |
| pd->port_subpid[subport_fp(fp)] = current->pid; |
| 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) |
| { |
| /* 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 swmajor, 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 */ |
| swmajor = uinfo->spu_userversion >> 16; |
| if (swmajor != 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); |
| |
| if (ipath_compatible_subports(swmajor, swminor) && |
| uinfo->spu_subport_cnt && |
| (ret = find_shared_port(fp, uinfo))) { |
| if (ret > 0) |
| ret = 0; |
| goto done_chk_sdma; |
| } |
| |
| i_minor = iminor(fp->f_path.dentry->d_inode) - IPATH_USER_MINOR_BASE; |
| ipath_cdbg(VERBOSE, "open on dev %lx (minor %d)\n", |
| (long)fp->f_path.dentry->d_inode->i_rdev, i_minor); |
| |
| if (i_minor) |
| ret = find_free_port(i_minor - 1, fp, uinfo); |
| else |
| ret = find_best_unit(fp, uinfo); |
| |
| done_chk_sdma: |
| if (!ret) { |
| struct ipath_filedata *fd = fp->private_data; |
| const struct ipath_portdata *pd = fd->pd; |
| const struct ipath_devdata *dd = pd->port_dd; |
| |
| fd->pq = ipath_user_sdma_queue_create(&dd->pcidev->dev, |
| dd->ipath_unit, |
| pd->port_port, |
| fd->subport); |
| |
| if (!fd->pq) |
| ret = -ENOMEM; |
| } |
| |
| 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 = port_fp(fp); |
| struct ipath_devdata *dd; |
| u32 head32; |
| |
| /* Subports don't need to initialize anything since master did it. */ |
| if (subport_fp(fp)) { |
| ret = wait_event_interruptible(pd->port_wait, |
| !test_bit(IPATH_PORT_MASTER_UNINIT, &pd->port_flag)); |
| goto done; |
| } |
| |
| 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 */ |
| |
| /* some ports may get extra buffers, calculate that here */ |
| if (pd->port_port <= dd->ipath_ports_extrabuf) |
| pd->port_piocnt = dd->ipath_pbufsport + 1; |
| else |
| pd->port_piocnt = dd->ipath_pbufsport; |
| |
| /* for right now, kernel piobufs are at end, so port 1 is at 0 */ |
| if (pd->port_port <= dd->ipath_ports_extrabuf) |
| pd->port_pio_base = (dd->ipath_pbufsport + 1) |
| * (pd->port_port - 1); |
| else |
| pd->port_pio_base = dd->ipath_ports_extrabuf + |
| dd->ipath_pbufsport * (pd->port_port - 1); |
| pd->port_piobufs = dd->ipath_piobufbase + |
| pd->port_pio_base * dd->ipath_palign; |
| ipath_cdbg(VERBOSE, "piobuf base for port %u is 0x%x, piocnt %u," |
| " first pio %u\n", pd->port_port, pd->port_piobufs, |
| pd->port_piocnt, pd->port_pio_base); |
| ipath_chg_pioavailkernel(dd, pd->port_pio_base, pd->port_piocnt, 0); |
| |
| /* |
| * 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); |
| pd->port_lastrcvhdrqtail = -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 */ |
| |
| /* initialize poll variables... */ |
| pd->port_urgent = 0; |
| pd->port_urgent_poll = 0; |
| pd->port_hdrqfull_poll = pd->port_hdrqfull; |
| |
| /* |
| * Now enable the port for receive. |
| * For chips that are set to DMA the tail register to memory |
| * when they change (and when the update bit transitions from |
| * 0 to 1. So for those chips, we turn it off and then back on. |
| * This will (very briefly) affect any other open ports, but the |
| * duration is very short, and therefore isn't an issue. We |
| * explictly set the in-memory tail copy to 0 beforehand, so we |
| * don't have to wait to be sure the DMA update has happened |
| * (chip resets head/tail to 0 on transition to enable). |
| */ |
| set_bit(dd->ipath_r_portenable_shift + pd->port_port, |
| &dd->ipath_rcvctrl); |
| if (!(dd->ipath_flags & IPATH_NODMA_RTAIL)) { |
| if (pd->port_rcvhdrtail_kvaddr) |
| ipath_clear_rcvhdrtail(pd); |
| ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, |
| dd->ipath_rcvctrl & |
| ~(1ULL << dd->ipath_r_tailupd_shift)); |
| } |
| ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, |
| dd->ipath_rcvctrl); |
| /* Notify any waiting slaves */ |
| if (pd->port_subport_cnt) { |
| clear_bit(IPATH_PORT_MASTER_UNINIT, &pd->port_flag); |
| wake_up(&pd->port_wait); |
| } |
| done: |
| return ret; |
| } |
| |
| /** |
| * unlock_exptid - unlock any expected TID entries port still had in use |
| * @pd: port |
| * |
| * We don't actually update the chip here, because we do a bulk update |
| * below, using ipath_f_clear_tids. |
| */ |
| static void unlock_expected_tids(struct ipath_portdata *pd) |
| { |
| struct ipath_devdata *dd = pd->port_dd; |
| int port_tidbase = pd->port_port * dd->ipath_rcvtidcnt; |
| int i, cnt = 0, maxtid = port_tidbase + dd->ipath_rcvtidcnt; |
| |
| ipath_cdbg(VERBOSE, "Port %u unlocking any locked expTID pages\n", |
| pd->port_port); |
| for (i = port_tidbase; i < maxtid; i++) { |
| struct page *ps = dd->ipath_pageshadow[i]; |
| |
| if (!ps) |
| continue; |
| |
| dd->ipath_pageshadow[i] = NULL; |
| pci_unmap_page(dd->pcidev, dd->ipath_physshadow[i], |
| PAGE_SIZE, PCI_DMA_FROMDEVICE); |
| ipath_release_user_pages_on_close(&ps, 1); |
| cnt++; |
| ipath_stats.sps_pageunlocks++; |
| } |
| if (cnt) |
| ipath_cdbg(VERBOSE, "Port %u locked %u expTID entries\n", |
| pd->port_port, cnt); |
| |
| if (ipath_stats.sps_pagelocks || ipath_stats.sps_pageunlocks) |
| ipath_cdbg(VERBOSE, "%llu pages locked, %llu unlocked\n", |
| (unsigned long long) ipath_stats.sps_pagelocks, |
| (unsigned long long) |
| ipath_stats.sps_pageunlocks); |
| } |
| |
| 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; |
| |
| ipath_cdbg(VERBOSE, "close on dev %lx, private data %p\n", |
| (long)in->i_rdev, fp->private_data); |
| |
| mutex_lock(&ipath_mutex); |
| |
| fd = (struct ipath_filedata *) fp->private_data; |
| fp->private_data = NULL; |
| pd = fd->pd; |
| if (!pd) { |
| mutex_unlock(&ipath_mutex); |
| goto bail; |
| } |
| |
| dd = pd->port_dd; |
| |
| /* drain user sdma queue */ |
| ipath_user_sdma_queue_drain(dd, fd->pq); |
| ipath_user_sdma_queue_destroy(fd->pq); |
| |
| 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); |
| pd->port_subpid[fd->subport] = 0; |
| mutex_unlock(&ipath_mutex); |
| goto bail; |
| } |
| port = pd->port_port; |
| |
| if (pd->port_hdrqfull) { |
| ipath_cdbg(PROC, "%s[%u] had %u rcvhdrqfull errors " |
| "during run\n", pd->port_comm, pd->port_pid, |
| pd->port_hdrqfull); |
| pd->port_hdrqfull = 0; |
| } |
| |
| if (pd->port_rcvwait_to || pd->port_piowait_to |
| || pd->port_rcvnowait || pd->port_pionowait) { |
| ipath_cdbg(VERBOSE, "port%u, %u rcv, %u pio wait timeo; " |
| "%u rcv %u, pio already\n", |
| pd->port_port, pd->port_rcvwait_to, |
| pd->port_piowait_to, pd->port_rcvnowait, |
| pd->port_pionowait); |
| pd->port_rcvwait_to = pd->port_piowait_to = |
| pd->port_rcvnowait = pd->port_pionowait = 0; |
| } |
| if (pd->port_flag) { |
| ipath_cdbg(PROC, "port %u port_flag set: 0x%lx\n", |
| pd->port_port, pd->port_flag); |
| pd->port_flag = 0; |
| } |
| |
| if (dd->ipath_kregbase) { |
| /* atomically clear receive enable port and intr avail. */ |
| clear_bit(dd->ipath_r_portenable_shift + port, |
| &dd->ipath_rcvctrl); |
| clear_bit(pd->port_port + dd->ipath_r_intravail_shift, |
| &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); |
| |
| ipath_disarm_piobufs(dd, pd->port_pio_base, pd->port_piocnt); |
| ipath_chg_pioavailkernel(dd, pd->port_pio_base, |
| pd->port_piocnt, 1); |
| |
| 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_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, 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 (ipath_supports_subports(pd->userversion >> 16, |
| pd->userversion & 0xffff)) { |
| /* 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, sz)) { |
| ret = -EFAULT; |
| goto bail; |
| } |
| ret = 0; |
| |
| 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 int ipath_sdma_get_inflight(struct ipath_user_sdma_queue *pq, |
| u32 __user *inflightp) |
| { |
| const u32 val = ipath_user_sdma_inflight_counter(pq); |
| |
| if (put_user(val, inflightp)) |
| return -EFAULT; |
| |
| return 0; |
| } |
| |
| static int ipath_sdma_get_complete(struct ipath_devdata *dd, |
| struct ipath_user_sdma_queue *pq, |
| u32 __user *completep) |
| { |
| u32 val; |
| int err; |
| |
| err = ipath_user_sdma_make_progress(dd, pq); |
| if (err < 0) |
| return err; |
| |
| val = ipath_user_sdma_complete_counter(pq); |
| if (put_user(val, completep)) |
| return -EFAULT; |
| |
| return 0; |
| } |
| |
| static ssize_t ipath_write(struct file *fp, const char __user *data, |
| size_t count, loff_t *off) |
| { |
| const struct ipath_cmd __user *ucmd; |
| struct ipath_portdata *pd; |
| const void __user *src; |
| size_t consumed, copy; |
| struct ipath_cmd cmd; |
| ssize_t ret = 0; |
| void *dest; |
| |
| if (count < sizeof(cmd.type)) { |
| ret = -EINVAL; |
| goto bail; |
| } |
| |
| ucmd = (const struct ipath_cmd __user *) data; |
| |
| if (copy_from_user(&cmd.type, &ucmd->type, sizeof(cmd.type))) { |
| ret = -EFAULT; |
| goto bail; |
| } |
| |
| 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; |
| src = &ucmd->cmd.user_info; |
| break; |
| case IPATH_CMD_RECV_CTRL: |
| copy = sizeof(cmd.cmd.recv_ctrl); |
| dest = &cmd.cmd.recv_ctrl; |
| src = &ucmd->cmd.recv_ctrl; |
| break; |
| case IPATH_CMD_PORT_INFO: |
| copy = sizeof(cmd.cmd.port_info); |
| dest = &cmd.cmd.port_info; |
| src = &ucmd->cmd.port_info; |
| break; |
| case IPATH_CMD_TID_UPDATE: |
| case IPATH_CMD_TID_FREE: |
| copy = sizeof(cmd.cmd.tid_info); |
| dest = &cmd.cmd.tid_info; |
| src = &ucmd->cmd.tid_info; |
| break; |
| case IPATH_CMD_SET_PART_KEY: |
| copy = sizeof(cmd.cmd.part_key); |
| 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; |
| case IPATH_CMD_PIOAVAILUPD: // force an update of PIOAvail reg |
| copy = 0; |
| src = NULL; |
| dest = NULL; |
| break; |
| case IPATH_CMD_POLL_TYPE: |
| copy = sizeof(cmd.cmd.poll_type); |
| dest = &cmd.cmd.poll_type; |
| src = &ucmd->cmd.poll_type; |
| break; |
| case IPATH_CMD_ARMLAUNCH_CTRL: |
| copy = sizeof(cmd.cmd.armlaunch_ctrl); |
| dest = &cmd.cmd.armlaunch_ctrl; |
| src = &ucmd->cmd.armlaunch_ctrl; |
| break; |
| case IPATH_CMD_SDMA_INFLIGHT: |
| copy = sizeof(cmd.cmd.sdma_inflight); |
| dest = &cmd.cmd.sdma_inflight; |
| src = &ucmd->cmd.sdma_inflight; |
| break; |
| case IPATH_CMD_SDMA_COMPLETE: |
| copy = sizeof(cmd.cmd.sdma_complete); |
| dest = &cmd.cmd.sdma_complete; |
| src = &ucmd->cmd.sdma_complete; |
| break; |
| default: |
| ret = -EINVAL; |
| goto bail; |
| } |
| |
| if (copy) { |
| if ((count - consumed) < copy) { |
| ret = -EINVAL; |
| goto bail; |
| } |
| |
| if (copy_from_user(dest, src, copy)) { |
| ret = -EFAULT; |
| goto bail; |
| } |
| |
| 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(fp, &cmd.cmd.user_info); |
| if (ret) |
| goto bail; |
| ret = ipath_get_base_info( |
| 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, subport_fp(fp), cmd.cmd.recv_ctrl); |
| break; |
| case IPATH_CMD_PORT_INFO: |
| 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, fp, &cmd.cmd.tid_info); |
| break; |
| case IPATH_CMD_TID_FREE: |
| 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; |
| case IPATH_CMD_PIOAVAILUPD: |
| ipath_force_pio_avail_update(pd->port_dd); |
| break; |
| case IPATH_CMD_POLL_TYPE: |
| pd->poll_type = cmd.cmd.poll_type; |
| break; |
| case IPATH_CMD_ARMLAUNCH_CTRL: |
| if (cmd.cmd.armlaunch_ctrl) |
| ipath_enable_armlaunch(pd->port_dd); |
| else |
| ipath_disable_armlaunch(pd->port_dd); |
| break; |
| case IPATH_CMD_SDMA_INFLIGHT: |
| ret = ipath_sdma_get_inflight(user_sdma_queue_fp(fp), |
| (u32 __user *) (unsigned long) |
| cmd.cmd.sdma_inflight); |
| break; |
| case IPATH_CMD_SDMA_COMPLETE: |
| ret = ipath_sdma_get_complete(pd->port_dd, |
| user_sdma_queue_fp(fp), |
| (u32 __user *) (unsigned long) |
| cmd.cmd.sdma_complete); |
| break; |
| } |
| |
| if (ret >= 0) |
| ret = consumed; |
| |
| bail: |
| return ret; |
| } |
| |
| static ssize_t ipath_writev(struct kiocb *iocb, const struct iovec *iov, |
| unsigned long dim, loff_t off) |
| { |
| struct file *filp = iocb->ki_filp; |
| struct ipath_filedata *fp = filp->private_data; |
| struct ipath_portdata *pd = port_fp(filp); |
| struct ipath_user_sdma_queue *pq = fp->pq; |
| |
| if (!dim) |
| return -EINVAL; |
| |
| return ipath_user_sdma_writev(pd->port_dd, pq, iov, dim); |
| } |
| |
| static struct class *ipath_class; |
| |
| static int init_cdev(int minor, char *name, const struct file_operations *fops, |
| struct cdev **cdevp, struct device **devp) |
| { |
| const dev_t dev = MKDEV(IPATH_MAJOR, minor); |
| struct cdev *cdev = NULL; |
| struct device *device = NULL; |
| int ret; |
| |
| cdev = cdev_alloc(); |
| if (!cdev) { |
| printk(KERN_ERR IPATH_DRV_NAME |
| ": Could not allocate cdev for minor %d, %s\n", |
| minor, name); |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| cdev->owner = THIS_MODULE; |
| cdev->ops = fops; |
| kobject_set_name(&cdev->kobj, name); |
| |
| ret = cdev_add(cdev, dev, 1); |
| if (ret < 0) { |
| printk(KERN_ERR IPATH_DRV_NAME |
| ": Could not add cdev for minor %d, %s (err %d)\n", |
| minor, name, -ret); |
| goto err_cdev; |
| } |
| |
| device = device_create(ipath_class, NULL, dev, name); |
| |
| if (IS_ERR(device)) { |
| ret = PTR_ERR(device); |
| printk(KERN_ERR IPATH_DRV_NAME ": Could not create " |
| "device for minor %d, %s (err %d)\n", |
| minor, name, -ret); |
| goto err_cdev; |
| } |
| |
| goto done; |
| |
| err_cdev: |
| cdev_del(cdev); |
| cdev = NULL; |
| |
| done: |
| if (ret >= 0) { |
| *cdevp = cdev; |
| *devp = device; |
| } else { |
| *cdevp = NULL; |
| *devp = NULL; |
| } |
| |
| return ret; |
| } |
| |
| int ipath_cdev_init(int minor, char *name, const struct file_operations *fops, |
| struct cdev **cdevp, struct device **devp) |
| { |
| return init_cdev(minor, name, fops, cdevp, devp); |
| } |
| |
| static void cleanup_cdev(struct cdev **cdevp, |
| struct device **devp) |
| { |
| struct device *dev = *devp; |
| |
| if (dev) { |
| device_unregister(dev); |
| *devp = NULL; |
| } |
| |
| if (*cdevp) { |
| cdev_del(*cdevp); |
| *cdevp = NULL; |
| } |
| } |
| |
| void ipath_cdev_cleanup(struct cdev **cdevp, |
| struct device **devp) |
| { |
| cleanup_cdev(cdevp, devp); |
| } |
| |
| static struct cdev *wildcard_cdev; |
| static struct device *wildcard_dev; |
| |
| static const dev_t dev = MKDEV(IPATH_MAJOR, 0); |
| |
| static int user_init(void) |
| { |
| int ret; |
| |
| ret = register_chrdev_region(dev, IPATH_NMINORS, IPATH_DRV_NAME); |
| if (ret < 0) { |
| printk(KERN_ERR IPATH_DRV_NAME ": Could not register " |
| "chrdev region (err %d)\n", -ret); |
| goto done; |
| } |
| |
| ipath_class = class_create(THIS_MODULE, IPATH_DRV_NAME); |
| |
| if (IS_ERR(ipath_class)) { |
| ret = PTR_ERR(ipath_class); |
| printk(KERN_ERR IPATH_DRV_NAME ": Could not create " |
| "device class (err %d)\n", -ret); |
| goto bail; |
| } |
| |
| goto done; |
| bail: |
| unregister_chrdev_region(dev, IPATH_NMINORS); |
| done: |
| return ret; |
| } |
| |
| static void user_cleanup(void) |
| { |
| if (ipath_class) { |
| class_destroy(ipath_class); |
| ipath_class = NULL; |
| } |
| |
| unregister_chrdev_region(dev, IPATH_NMINORS); |
| } |
| |
| static atomic_t user_count = ATOMIC_INIT(0); |
| static atomic_t user_setup = ATOMIC_INIT(0); |
| |
| int ipath_user_add(struct ipath_devdata *dd) |
| { |
| char name[10]; |
| int ret; |
| |
| if (atomic_inc_return(&user_count) == 1) { |
| ret = user_init(); |
| if (ret < 0) { |
| ipath_dev_err(dd, "Unable to set up user support: " |
| "error %d\n", -ret); |
| goto bail; |
| } |
| ret = init_cdev(0, "ipath", &ipath_file_ops, &wildcard_cdev, |
| &wildcard_dev); |
| if (ret < 0) { |
| ipath_dev_err(dd, "Could not create wildcard " |
| "minor: error %d\n", -ret); |
| goto bail_user; |
| } |
| |
| atomic_set(&user_setup, 1); |
| } |
| |
| snprintf(name, sizeof(name), "ipath%d", dd->ipath_unit); |
| |
| ret = init_cdev(dd->ipath_unit + 1, name, &ipath_file_ops, |
| &dd->user_cdev, &dd->user_dev); |
| if (ret < 0) |
| ipath_dev_err(dd, "Could not create user minor %d, %s\n", |
| dd->ipath_unit + 1, name); |
| |
| goto bail; |
| |
| bail_user: |
| user_cleanup(); |
| bail: |
| return ret; |
| } |
| |
| void ipath_user_remove(struct ipath_devdata *dd) |
| { |
| cleanup_cdev(&dd->user_cdev, &dd->user_dev); |
| |
| if (atomic_dec_return(&user_count) == 0) { |
| if (atomic_read(&user_setup) == 0) |
| goto bail; |
| |
| cleanup_cdev(&wildcard_cdev, &wildcard_dev); |
| user_cleanup(); |
| |
| atomic_set(&user_setup, 0); |
| } |
| bail: |
| return; |
| } |