root/drivers/misc/mic/host/mic_smpt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mic_system_page_mask
  2. mic_sys_addr_to_smpt
  3. mic_smpt_to_pa
  4. mic_smpt_offset
  5. mic_smpt_align_low
  6. mic_smpt_align_high
  7. mic_max_system_memory
  8. mic_max_system_addr
  9. mic_is_system_addr
  10. mic_add_smpt_entry
  11. mic_smpt_op
  12. mic_get_smpt_ref_count
  13. mic_to_dma_addr
  14. mic_map
  15. mic_unmap
  16. mic_map_single
  17. mic_unmap_single
  18. mic_smpt_init
  19. mic_smpt_uninit
  20. mic_smpt_restore

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Intel MIC Platform Software Stack (MPSS)
   4  *
   5  * Copyright(c) 2013 Intel Corporation.
   6  *
   7  * Intel MIC Host driver.
   8  */
   9 #include <linux/pci.h>
  10 
  11 #include "../common/mic_dev.h"
  12 #include "mic_device.h"
  13 #include "mic_smpt.h"
  14 
  15 static inline u64 mic_system_page_mask(struct mic_device *mdev)
  16 {
  17         return (1ULL << mdev->smpt->info.page_shift) - 1ULL;
  18 }
  19 
  20 static inline u8 mic_sys_addr_to_smpt(struct mic_device *mdev, dma_addr_t pa)
  21 {
  22         return (pa - mdev->smpt->info.base) >> mdev->smpt->info.page_shift;
  23 }
  24 
  25 static inline u64 mic_smpt_to_pa(struct mic_device *mdev, u8 index)
  26 {
  27         return mdev->smpt->info.base + (index * mdev->smpt->info.page_size);
  28 }
  29 
  30 static inline u64 mic_smpt_offset(struct mic_device *mdev, dma_addr_t pa)
  31 {
  32         return pa & mic_system_page_mask(mdev);
  33 }
  34 
  35 static inline u64 mic_smpt_align_low(struct mic_device *mdev, dma_addr_t pa)
  36 {
  37         return ALIGN(pa - mic_system_page_mask(mdev),
  38                 mdev->smpt->info.page_size);
  39 }
  40 
  41 static inline u64 mic_smpt_align_high(struct mic_device *mdev, dma_addr_t pa)
  42 {
  43         return ALIGN(pa, mdev->smpt->info.page_size);
  44 }
  45 
  46 /* Total Cumulative system memory accessible by MIC across all SMPT entries */
  47 static inline u64 mic_max_system_memory(struct mic_device *mdev)
  48 {
  49         return mdev->smpt->info.num_reg * mdev->smpt->info.page_size;
  50 }
  51 
  52 /* Maximum system memory address accessible by MIC */
  53 static inline u64 mic_max_system_addr(struct mic_device *mdev)
  54 {
  55         return mdev->smpt->info.base + mic_max_system_memory(mdev) - 1ULL;
  56 }
  57 
  58 /* Check if the DMA address is a MIC system memory address */
  59 static inline bool
  60 mic_is_system_addr(struct mic_device *mdev, dma_addr_t pa)
  61 {
  62         return pa >= mdev->smpt->info.base && pa <= mic_max_system_addr(mdev);
  63 }
  64 
  65 /* Populate an SMPT entry and update the reference counts. */
  66 static void mic_add_smpt_entry(int spt, s64 *ref, u64 addr,
  67                                int entries, struct mic_device *mdev)
  68 {
  69         struct mic_smpt_info *smpt_info = mdev->smpt;
  70         int i;
  71 
  72         for (i = spt; i < spt + entries; i++,
  73                 addr += smpt_info->info.page_size) {
  74                 if (!smpt_info->entry[i].ref_count &&
  75                     (smpt_info->entry[i].dma_addr != addr)) {
  76                         mdev->smpt_ops->set(mdev, addr, i);
  77                         smpt_info->entry[i].dma_addr = addr;
  78                 }
  79                 smpt_info->entry[i].ref_count += ref[i - spt];
  80         }
  81 }
  82 
  83 /*
  84  * Find an available MIC address in MIC SMPT address space
  85  * for a given DMA address and size.
  86  */
  87 static dma_addr_t mic_smpt_op(struct mic_device *mdev, u64 dma_addr,
  88                               int entries, s64 *ref, size_t size)
  89 {
  90         int spt;
  91         int ae = 0;
  92         int i;
  93         unsigned long flags;
  94         dma_addr_t mic_addr = 0;
  95         dma_addr_t addr = dma_addr;
  96         struct mic_smpt_info *smpt_info = mdev->smpt;
  97 
  98         spin_lock_irqsave(&smpt_info->smpt_lock, flags);
  99 
 100         /* find existing entries */
 101         for (i = 0; i < smpt_info->info.num_reg; i++) {
 102                 if (smpt_info->entry[i].dma_addr == addr) {
 103                         ae++;
 104                         addr += smpt_info->info.page_size;
 105                 } else if (ae) /* cannot find contiguous entries */
 106                         goto not_found;
 107 
 108                 if (ae == entries)
 109                         goto found;
 110         }
 111 
 112         /* find free entry */
 113         for (ae = 0, i = 0; i < smpt_info->info.num_reg; i++) {
 114                 ae = (smpt_info->entry[i].ref_count == 0) ? ae + 1 : 0;
 115                 if (ae == entries)
 116                         goto found;
 117         }
 118 
 119 not_found:
 120         spin_unlock_irqrestore(&smpt_info->smpt_lock, flags);
 121         return mic_addr;
 122 
 123 found:
 124         spt = i - entries + 1;
 125         mic_addr = mic_smpt_to_pa(mdev, spt);
 126         mic_add_smpt_entry(spt, ref, dma_addr, entries, mdev);
 127         smpt_info->map_count++;
 128         smpt_info->ref_count += (s64)size;
 129         spin_unlock_irqrestore(&smpt_info->smpt_lock, flags);
 130         return mic_addr;
 131 }
 132 
 133 /*
 134  * Returns number of smpt entries needed for dma_addr to dma_addr + size
 135  * also returns the reference count array for each of those entries
 136  * and the starting smpt address
 137  */
 138 static int mic_get_smpt_ref_count(struct mic_device *mdev, dma_addr_t dma_addr,
 139                                   size_t size, s64 *ref,  u64 *smpt_start)
 140 {
 141         u64 start =  dma_addr;
 142         u64 end = dma_addr + size;
 143         int i = 0;
 144 
 145         while (start < end) {
 146                 ref[i++] = min(mic_smpt_align_high(mdev, start + 1),
 147                         end) - start;
 148                 start = mic_smpt_align_high(mdev, start + 1);
 149         }
 150 
 151         if (smpt_start)
 152                 *smpt_start = mic_smpt_align_low(mdev, dma_addr);
 153 
 154         return i;
 155 }
 156 
 157 /*
 158  * mic_to_dma_addr - Converts a MIC address to a DMA address.
 159  *
 160  * @mdev: pointer to mic_device instance.
 161  * @mic_addr: MIC address.
 162  *
 163  * returns a DMA address.
 164  */
 165 dma_addr_t mic_to_dma_addr(struct mic_device *mdev, dma_addr_t mic_addr)
 166 {
 167         struct mic_smpt_info *smpt_info = mdev->smpt;
 168         int spt;
 169         dma_addr_t dma_addr;
 170 
 171         if (!mic_is_system_addr(mdev, mic_addr)) {
 172                 dev_err(&mdev->pdev->dev,
 173                         "mic_addr is invalid. mic_addr = 0x%llx\n", mic_addr);
 174                 return -EINVAL;
 175         }
 176         spt = mic_sys_addr_to_smpt(mdev, mic_addr);
 177         dma_addr = smpt_info->entry[spt].dma_addr +
 178                 mic_smpt_offset(mdev, mic_addr);
 179         return dma_addr;
 180 }
 181 
 182 /**
 183  * mic_map - Maps a DMA address to a MIC physical address.
 184  *
 185  * @mdev: pointer to mic_device instance.
 186  * @dma_addr: DMA address.
 187  * @size: Size of the region to be mapped.
 188  *
 189  * This API converts the DMA address provided to a DMA address understood
 190  * by MIC. Caller should check for errors by calling mic_map_error(..).
 191  *
 192  * returns DMA address as required by MIC.
 193  */
 194 dma_addr_t mic_map(struct mic_device *mdev, dma_addr_t dma_addr, size_t size)
 195 {
 196         dma_addr_t mic_addr = 0;
 197         int num_entries;
 198         s64 *ref;
 199         u64 smpt_start;
 200 
 201         if (!size || size > mic_max_system_memory(mdev))
 202                 return mic_addr;
 203 
 204         ref = kmalloc_array(mdev->smpt->info.num_reg, sizeof(s64), GFP_ATOMIC);
 205         if (!ref)
 206                 return mic_addr;
 207 
 208         num_entries = mic_get_smpt_ref_count(mdev, dma_addr, size,
 209                                              ref, &smpt_start);
 210 
 211         /* Set the smpt table appropriately and get 16G aligned mic address */
 212         mic_addr = mic_smpt_op(mdev, smpt_start, num_entries, ref, size);
 213 
 214         kfree(ref);
 215 
 216         /*
 217          * If mic_addr is zero then its an error case
 218          * since mic_addr can never be zero.
 219          * else generate mic_addr by adding the 16G offset in dma_addr
 220          */
 221         if (!mic_addr && MIC_FAMILY_X100 == mdev->family) {
 222                 dev_err(&mdev->pdev->dev,
 223                         "mic_map failed dma_addr 0x%llx size 0x%lx\n",
 224                         dma_addr, size);
 225                 return mic_addr;
 226         } else {
 227                 return mic_addr + mic_smpt_offset(mdev, dma_addr);
 228         }
 229 }
 230 
 231 /**
 232  * mic_unmap - Unmaps a MIC physical address.
 233  *
 234  * @mdev: pointer to mic_device instance.
 235  * @mic_addr: MIC physical address.
 236  * @size: Size of the region to be unmapped.
 237  *
 238  * This API unmaps the mappings created by mic_map(..).
 239  *
 240  * returns None.
 241  */
 242 void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size)
 243 {
 244         struct mic_smpt_info *smpt_info = mdev->smpt;
 245         s64 *ref;
 246         int num_smpt;
 247         int spt;
 248         int i;
 249         unsigned long flags;
 250 
 251         if (!size)
 252                 return;
 253 
 254         if (!mic_is_system_addr(mdev, mic_addr)) {
 255                 dev_err(&mdev->pdev->dev,
 256                         "invalid address: 0x%llx\n", mic_addr);
 257                 return;
 258         }
 259 
 260         spt = mic_sys_addr_to_smpt(mdev, mic_addr);
 261         ref = kmalloc_array(mdev->smpt->info.num_reg, sizeof(s64), GFP_ATOMIC);
 262         if (!ref)
 263                 return;
 264 
 265         /* Get number of smpt entries to be mapped, ref count array */
 266         num_smpt = mic_get_smpt_ref_count(mdev, mic_addr, size, ref, NULL);
 267 
 268         spin_lock_irqsave(&smpt_info->smpt_lock, flags);
 269         smpt_info->unmap_count++;
 270         smpt_info->ref_count -= (s64)size;
 271 
 272         for (i = spt; i < spt + num_smpt; i++) {
 273                 smpt_info->entry[i].ref_count -= ref[i - spt];
 274                 if (smpt_info->entry[i].ref_count < 0)
 275                         dev_warn(&mdev->pdev->dev,
 276                                  "ref count for entry %d is negative\n", i);
 277         }
 278         spin_unlock_irqrestore(&smpt_info->smpt_lock, flags);
 279         kfree(ref);
 280 }
 281 
 282 /**
 283  * mic_map_single - Maps a virtual address to a MIC physical address.
 284  *
 285  * @mdev: pointer to mic_device instance.
 286  * @va: Kernel direct mapped virtual address.
 287  * @size: Size of the region to be mapped.
 288  *
 289  * This API calls pci_map_single(..) for the direct mapped virtual address
 290  * and then converts the DMA address provided to a DMA address understood
 291  * by MIC. Caller should check for errors by calling mic_map_error(..).
 292  *
 293  * returns DMA address as required by MIC.
 294  */
 295 dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size)
 296 {
 297         dma_addr_t mic_addr = 0;
 298         struct pci_dev *pdev = mdev->pdev;
 299         dma_addr_t dma_addr =
 300                 pci_map_single(pdev, va, size, PCI_DMA_BIDIRECTIONAL);
 301 
 302         if (!pci_dma_mapping_error(pdev, dma_addr)) {
 303                 mic_addr = mic_map(mdev, dma_addr, size);
 304                 if (!mic_addr) {
 305                         dev_err(&mdev->pdev->dev,
 306                                 "mic_map failed dma_addr 0x%llx size 0x%lx\n",
 307                                 dma_addr, size);
 308                         pci_unmap_single(pdev, dma_addr,
 309                                          size, PCI_DMA_BIDIRECTIONAL);
 310                 }
 311         }
 312         return mic_addr;
 313 }
 314 
 315 /**
 316  * mic_unmap_single - Unmaps a MIC physical address.
 317  *
 318  * @mdev: pointer to mic_device instance.
 319  * @mic_addr: MIC physical address.
 320  * @size: Size of the region to be unmapped.
 321  *
 322  * This API unmaps the mappings created by mic_map_single(..).
 323  *
 324  * returns None.
 325  */
 326 void
 327 mic_unmap_single(struct mic_device *mdev, dma_addr_t mic_addr, size_t size)
 328 {
 329         struct pci_dev *pdev = mdev->pdev;
 330         dma_addr_t dma_addr = mic_to_dma_addr(mdev, mic_addr);
 331         mic_unmap(mdev, mic_addr, size);
 332         pci_unmap_single(pdev, dma_addr, size, PCI_DMA_BIDIRECTIONAL);
 333 }
 334 
 335 /**
 336  * mic_smpt_init - Initialize MIC System Memory Page Tables.
 337  *
 338  * @mdev: pointer to mic_device instance.
 339  *
 340  * returns 0 for success and -errno for error.
 341  */
 342 int mic_smpt_init(struct mic_device *mdev)
 343 {
 344         int i, err = 0;
 345         dma_addr_t dma_addr;
 346         struct mic_smpt_info *smpt_info;
 347 
 348         mdev->smpt = kmalloc(sizeof(*mdev->smpt), GFP_KERNEL);
 349         if (!mdev->smpt)
 350                 return -ENOMEM;
 351 
 352         smpt_info = mdev->smpt;
 353         mdev->smpt_ops->init(mdev);
 354         smpt_info->entry = kmalloc_array(smpt_info->info.num_reg,
 355                                          sizeof(*smpt_info->entry), GFP_KERNEL);
 356         if (!smpt_info->entry) {
 357                 err = -ENOMEM;
 358                 goto free_smpt;
 359         }
 360         spin_lock_init(&smpt_info->smpt_lock);
 361         for (i = 0; i < smpt_info->info.num_reg; i++) {
 362                 dma_addr = i * smpt_info->info.page_size;
 363                 smpt_info->entry[i].dma_addr = dma_addr;
 364                 smpt_info->entry[i].ref_count = 0;
 365                 mdev->smpt_ops->set(mdev, dma_addr, i);
 366         }
 367         smpt_info->ref_count = 0;
 368         smpt_info->map_count = 0;
 369         smpt_info->unmap_count = 0;
 370         return 0;
 371 free_smpt:
 372         kfree(smpt_info);
 373         return err;
 374 }
 375 
 376 /**
 377  * mic_smpt_uninit - UnInitialize MIC System Memory Page Tables.
 378  *
 379  * @mdev: pointer to mic_device instance.
 380  *
 381  * returns None.
 382  */
 383 void mic_smpt_uninit(struct mic_device *mdev)
 384 {
 385         struct mic_smpt_info *smpt_info = mdev->smpt;
 386         int i;
 387 
 388         dev_dbg(&mdev->pdev->dev,
 389                 "nodeid %d SMPT ref count %lld map %lld unmap %lld\n",
 390                 mdev->id, smpt_info->ref_count,
 391                 smpt_info->map_count, smpt_info->unmap_count);
 392 
 393         for (i = 0; i < smpt_info->info.num_reg; i++) {
 394                 dev_dbg(&mdev->pdev->dev,
 395                         "SMPT entry[%d] dma_addr = 0x%llx ref_count = %lld\n",
 396                         i, smpt_info->entry[i].dma_addr,
 397                         smpt_info->entry[i].ref_count);
 398                 if (smpt_info->entry[i].ref_count)
 399                         dev_warn(&mdev->pdev->dev,
 400                                  "ref count for entry %d is not zero\n", i);
 401         }
 402         kfree(smpt_info->entry);
 403         kfree(smpt_info);
 404 }
 405 
 406 /**
 407  * mic_smpt_restore - Restore MIC System Memory Page Tables.
 408  *
 409  * @mdev: pointer to mic_device instance.
 410  *
 411  * Restore the SMPT registers to values previously stored in the
 412  * SW data structures. Some MIC steppings lose register state
 413  * across resets and this API should be called for performing
 414  * a restore operation if required.
 415  *
 416  * returns None.
 417  */
 418 void mic_smpt_restore(struct mic_device *mdev)
 419 {
 420         int i;
 421         dma_addr_t dma_addr;
 422 
 423         for (i = 0; i < mdev->smpt->info.num_reg; i++) {
 424                 dma_addr = mdev->smpt->entry[i].dma_addr;
 425                 mdev->smpt_ops->set(mdev, dma_addr, i);
 426         }
 427 }

/* [<][>][^][v][top][bottom][index][help] */