linux/drivers/infiniband/hw/ipath/ipath_user_pages.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.
   3 * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
   4 *
   5 * This software is available to you under a choice of one of two
   6 * licenses.  You may choose to be licensed under the terms of the GNU
   7 * General Public License (GPL) Version 2, available from the file
   8 * COPYING in the main directory of this source tree, or the
   9 * OpenIB.org BSD license below:
  10 *
  11 *     Redistribution and use in source and binary forms, with or
  12 *     without modification, are permitted provided that the following
  13 *     conditions are met:
  14 *
  15 *      - Redistributions of source code must retain the above
  16 *        copyright notice, this list of conditions and the following
  17 *        disclaimer.
  18 *
  19 *      - Redistributions in binary form must reproduce the above
  20 *        copyright notice, this list of conditions and the following
  21 *        disclaimer in the documentation and/or other materials
  22 *        provided with the distribution.
  23 *
  24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  31 * SOFTWARE.
  32 */
  33
  34#include <linux/mm.h>
  35#include <linux/device.h>
  36
  37#include "ipath_kernel.h"
  38
  39static void __ipath_release_user_pages(struct page **p, size_t num_pages,
  40                                   int dirty)
  41{
  42        size_t i;
  43
  44        for (i = 0; i < num_pages; i++) {
  45                ipath_cdbg(MM, "%lu/%lu put_page %p\n", (unsigned long) i,
  46                           (unsigned long) num_pages, p[i]);
  47                if (dirty)
  48                        set_page_dirty_lock(p[i]);
  49                put_page(p[i]);
  50        }
  51}
  52
  53/* call with current->mm->mmap_sem held */
  54static int __get_user_pages(unsigned long start_page, size_t num_pages,
  55                        struct page **p, struct vm_area_struct **vma)
  56{
  57        unsigned long lock_limit;
  58        size_t got;
  59        int ret;
  60
  61        lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur >>
  62                PAGE_SHIFT;
  63
  64        if (num_pages > lock_limit) {
  65                ret = -ENOMEM;
  66                goto bail;
  67        }
  68
  69        ipath_cdbg(VERBOSE, "pin %lx pages from vaddr %lx\n",
  70                   (unsigned long) num_pages, start_page);
  71
  72        for (got = 0; got < num_pages; got += ret) {
  73                ret = get_user_pages(current, current->mm,
  74                                     start_page + got * PAGE_SIZE,
  75                                     num_pages - got, 1, 1,
  76                                     p + got, vma);
  77                if (ret < 0)
  78                        goto bail_release;
  79        }
  80
  81        current->mm->locked_vm += num_pages;
  82
  83        ret = 0;
  84        goto bail;
  85
  86bail_release:
  87        __ipath_release_user_pages(p, got, 0);
  88bail:
  89        return ret;
  90}
  91
  92/**
  93 * ipath_map_page - a safety wrapper around pci_map_page()
  94 *
  95 * A dma_addr of all 0's is interpreted by the chip as "disabled".
  96 * Unfortunately, it can also be a valid dma_addr returned on some
  97 * architectures.
  98 *
  99 * The powerpc iommu assigns dma_addrs in ascending order, so we don't
 100 * have to bother with retries or mapping a dummy page to insure we
 101 * don't just get the same mapping again.
 102 *
 103 * I'm sure we won't be so lucky with other iommu's, so FIXME.
 104 */
 105dma_addr_t ipath_map_page(struct pci_dev *hwdev, struct page *page,
 106        unsigned long offset, size_t size, int direction)
 107{
 108        dma_addr_t phys;
 109
 110        phys = pci_map_page(hwdev, page, offset, size, direction);
 111
 112        if (phys == 0) {
 113                pci_unmap_page(hwdev, phys, size, direction);
 114                phys = pci_map_page(hwdev, page, offset, size, direction);
 115                /*
 116                 * FIXME: If we get 0 again, we should keep this page,
 117                 * map another, then free the 0 page.
 118                 */
 119        }
 120
 121        return phys;
 122}
 123
 124/**
 125 * ipath_map_single - a safety wrapper around pci_map_single()
 126 *
 127 * Same idea as ipath_map_page().
 128 */
 129dma_addr_t ipath_map_single(struct pci_dev *hwdev, void *ptr, size_t size,
 130        int direction)
 131{
 132        dma_addr_t phys;
 133
 134        phys = pci_map_single(hwdev, ptr, size, direction);
 135
 136        if (phys == 0) {
 137                pci_unmap_single(hwdev, phys, size, direction);
 138                phys = pci_map_single(hwdev, ptr, size, direction);
 139                /*
 140                 * FIXME: If we get 0 again, we should keep this page,
 141                 * map another, then free the 0 page.
 142                 */
 143        }
 144
 145        return phys;
 146}
 147
 148/**
 149 * ipath_get_user_pages - lock user pages into memory
 150 * @start_page: the start page
 151 * @num_pages: the number of pages
 152 * @p: the output page structures
 153 *
 154 * This function takes a given start page (page aligned user virtual
 155 * address) and pins it and the following specified number of pages.  For
 156 * now, num_pages is always 1, but that will probably change at some point
 157 * (because caller is doing expected sends on a single virtually contiguous
 158 * buffer, so we can do all pages at once).
 159 */
 160int ipath_get_user_pages(unsigned long start_page, size_t num_pages,
 161                         struct page **p)
 162{
 163        int ret;
 164
 165        down_write(&current->mm->mmap_sem);
 166
 167        ret = __get_user_pages(start_page, num_pages, p, NULL);
 168
 169        up_write(&current->mm->mmap_sem);
 170
 171        return ret;
 172}
 173
 174void ipath_release_user_pages(struct page **p, size_t num_pages)
 175{
 176        down_write(&current->mm->mmap_sem);
 177
 178        __ipath_release_user_pages(p, num_pages, 1);
 179
 180        current->mm->locked_vm -= num_pages;
 181
 182        up_write(&current->mm->mmap_sem);
 183}
 184
 185struct ipath_user_pages_work {
 186        struct work_struct work;
 187        struct mm_struct *mm;
 188        unsigned long num_pages;
 189};
 190
 191static void user_pages_account(struct work_struct *_work)
 192{
 193        struct ipath_user_pages_work *work =
 194                container_of(_work, struct ipath_user_pages_work, work);
 195
 196        down_write(&work->mm->mmap_sem);
 197        work->mm->locked_vm -= work->num_pages;
 198        up_write(&work->mm->mmap_sem);
 199        mmput(work->mm);
 200        kfree(work);
 201}
 202
 203void ipath_release_user_pages_on_close(struct page **p, size_t num_pages)
 204{
 205        struct ipath_user_pages_work *work;
 206        struct mm_struct *mm;
 207
 208        __ipath_release_user_pages(p, num_pages, 1);
 209
 210        mm = get_task_mm(current);
 211        if (!mm)
 212                goto bail;
 213
 214        work = kmalloc(sizeof(*work), GFP_KERNEL);
 215        if (!work)
 216                goto bail_mm;
 217
 218        goto bail;
 219
 220        INIT_WORK(&work->work, user_pages_account);
 221        work->mm = mm;
 222        work->num_pages = num_pages;
 223
 224bail_mm:
 225        mmput(mm);
 226bail:
 227        return;
 228}
 229
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.