root/drivers/gpu/host1x/job.c

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

DEFINITIONS

This source file includes following definitions.
  1. host1x_job_alloc
  2. host1x_job_get
  3. job_free
  4. host1x_job_put
  5. host1x_job_add_gather
  6. pin_job
  7. do_relocs
  8. check_reloc
  9. check_register
  10. check_class
  11. check_mask
  12. check_incr
  13. check_nonincr
  14. validate
  15. copy_gathers
  16. host1x_job_pin
  17. host1x_job_unpin
  18. host1x_job_dump

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Tegra host1x Job
   4  *
   5  * Copyright (c) 2010-2015, NVIDIA Corporation.
   6  */
   7 
   8 #include <linux/dma-mapping.h>
   9 #include <linux/err.h>
  10 #include <linux/host1x.h>
  11 #include <linux/kref.h>
  12 #include <linux/module.h>
  13 #include <linux/scatterlist.h>
  14 #include <linux/slab.h>
  15 #include <linux/vmalloc.h>
  16 #include <trace/events/host1x.h>
  17 
  18 #include "channel.h"
  19 #include "dev.h"
  20 #include "job.h"
  21 #include "syncpt.h"
  22 
  23 #define HOST1X_WAIT_SYNCPT_OFFSET 0x8
  24 
  25 struct host1x_job *host1x_job_alloc(struct host1x_channel *ch,
  26                                     u32 num_cmdbufs, u32 num_relocs)
  27 {
  28         struct host1x_job *job = NULL;
  29         unsigned int num_unpins = num_cmdbufs + num_relocs;
  30         u64 total;
  31         void *mem;
  32 
  33         /* Check that we're not going to overflow */
  34         total = sizeof(struct host1x_job) +
  35                 (u64)num_relocs * sizeof(struct host1x_reloc) +
  36                 (u64)num_unpins * sizeof(struct host1x_job_unpin_data) +
  37                 (u64)num_cmdbufs * sizeof(struct host1x_job_gather) +
  38                 (u64)num_unpins * sizeof(dma_addr_t) +
  39                 (u64)num_unpins * sizeof(u32 *);
  40         if (total > ULONG_MAX)
  41                 return NULL;
  42 
  43         mem = job = kzalloc(total, GFP_KERNEL);
  44         if (!job)
  45                 return NULL;
  46 
  47         kref_init(&job->ref);
  48         job->channel = ch;
  49 
  50         /* Redistribute memory to the structs  */
  51         mem += sizeof(struct host1x_job);
  52         job->relocs = num_relocs ? mem : NULL;
  53         mem += num_relocs * sizeof(struct host1x_reloc);
  54         job->unpins = num_unpins ? mem : NULL;
  55         mem += num_unpins * sizeof(struct host1x_job_unpin_data);
  56         job->gathers = num_cmdbufs ? mem : NULL;
  57         mem += num_cmdbufs * sizeof(struct host1x_job_gather);
  58         job->addr_phys = num_unpins ? mem : NULL;
  59 
  60         job->reloc_addr_phys = job->addr_phys;
  61         job->gather_addr_phys = &job->addr_phys[num_relocs];
  62 
  63         return job;
  64 }
  65 EXPORT_SYMBOL(host1x_job_alloc);
  66 
  67 struct host1x_job *host1x_job_get(struct host1x_job *job)
  68 {
  69         kref_get(&job->ref);
  70         return job;
  71 }
  72 EXPORT_SYMBOL(host1x_job_get);
  73 
  74 static void job_free(struct kref *ref)
  75 {
  76         struct host1x_job *job = container_of(ref, struct host1x_job, ref);
  77 
  78         kfree(job);
  79 }
  80 
  81 void host1x_job_put(struct host1x_job *job)
  82 {
  83         kref_put(&job->ref, job_free);
  84 }
  85 EXPORT_SYMBOL(host1x_job_put);
  86 
  87 void host1x_job_add_gather(struct host1x_job *job, struct host1x_bo *bo,
  88                            unsigned int words, unsigned int offset)
  89 {
  90         struct host1x_job_gather *gather = &job->gathers[job->num_gathers];
  91 
  92         gather->words = words;
  93         gather->bo = bo;
  94         gather->offset = offset;
  95 
  96         job->num_gathers++;
  97 }
  98 EXPORT_SYMBOL(host1x_job_add_gather);
  99 
 100 static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
 101 {
 102         unsigned int i;
 103         int err;
 104 
 105         job->num_unpins = 0;
 106 
 107         for (i = 0; i < job->num_relocs; i++) {
 108                 struct host1x_reloc *reloc = &job->relocs[i];
 109                 struct sg_table *sgt;
 110                 dma_addr_t phys_addr;
 111 
 112                 reloc->target.bo = host1x_bo_get(reloc->target.bo);
 113                 if (!reloc->target.bo) {
 114                         err = -EINVAL;
 115                         goto unpin;
 116                 }
 117 
 118                 phys_addr = host1x_bo_pin(reloc->target.bo, &sgt);
 119 
 120                 job->addr_phys[job->num_unpins] = phys_addr;
 121                 job->unpins[job->num_unpins].bo = reloc->target.bo;
 122                 job->unpins[job->num_unpins].sgt = sgt;
 123                 job->num_unpins++;
 124         }
 125 
 126         for (i = 0; i < job->num_gathers; i++) {
 127                 struct host1x_job_gather *g = &job->gathers[i];
 128                 size_t gather_size = 0;
 129                 struct scatterlist *sg;
 130                 struct sg_table *sgt;
 131                 dma_addr_t phys_addr;
 132                 unsigned long shift;
 133                 struct iova *alloc;
 134                 unsigned int j;
 135 
 136                 g->bo = host1x_bo_get(g->bo);
 137                 if (!g->bo) {
 138                         err = -EINVAL;
 139                         goto unpin;
 140                 }
 141 
 142                 phys_addr = host1x_bo_pin(g->bo, &sgt);
 143 
 144                 if (!IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL) && host->domain) {
 145                         for_each_sg(sgt->sgl, sg, sgt->nents, j)
 146                                 gather_size += sg->length;
 147                         gather_size = iova_align(&host->iova, gather_size);
 148 
 149                         shift = iova_shift(&host->iova);
 150                         alloc = alloc_iova(&host->iova, gather_size >> shift,
 151                                            host->iova_end >> shift, true);
 152                         if (!alloc) {
 153                                 err = -ENOMEM;
 154                                 goto unpin;
 155                         }
 156 
 157                         err = iommu_map_sg(host->domain,
 158                                         iova_dma_addr(&host->iova, alloc),
 159                                         sgt->sgl, sgt->nents, IOMMU_READ);
 160                         if (err == 0) {
 161                                 __free_iova(&host->iova, alloc);
 162                                 err = -EINVAL;
 163                                 goto unpin;
 164                         }
 165 
 166                         job->addr_phys[job->num_unpins] =
 167                                 iova_dma_addr(&host->iova, alloc);
 168                         job->unpins[job->num_unpins].size = gather_size;
 169                 } else {
 170                         job->addr_phys[job->num_unpins] = phys_addr;
 171                 }
 172 
 173                 job->gather_addr_phys[i] = job->addr_phys[job->num_unpins];
 174 
 175                 job->unpins[job->num_unpins].bo = g->bo;
 176                 job->unpins[job->num_unpins].sgt = sgt;
 177                 job->num_unpins++;
 178         }
 179 
 180         return 0;
 181 
 182 unpin:
 183         host1x_job_unpin(job);
 184         return err;
 185 }
 186 
 187 static int do_relocs(struct host1x_job *job, struct host1x_job_gather *g)
 188 {
 189         u32 last_page = ~0;
 190         void *cmdbuf_page_addr = NULL;
 191         struct host1x_bo *cmdbuf = g->bo;
 192         unsigned int i;
 193 
 194         /* pin & patch the relocs for one gather */
 195         for (i = 0; i < job->num_relocs; i++) {
 196                 struct host1x_reloc *reloc = &job->relocs[i];
 197                 u32 reloc_addr = (job->reloc_addr_phys[i] +
 198                                   reloc->target.offset) >> reloc->shift;
 199                 u32 *target;
 200 
 201                 /* skip all other gathers */
 202                 if (cmdbuf != reloc->cmdbuf.bo)
 203                         continue;
 204 
 205                 if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) {
 206                         target = (u32 *)job->gather_copy_mapped +
 207                                         reloc->cmdbuf.offset / sizeof(u32) +
 208                                                 g->offset / sizeof(u32);
 209                         goto patch_reloc;
 210                 }
 211 
 212                 if (last_page != reloc->cmdbuf.offset >> PAGE_SHIFT) {
 213                         if (cmdbuf_page_addr)
 214                                 host1x_bo_kunmap(cmdbuf, last_page,
 215                                                  cmdbuf_page_addr);
 216 
 217                         cmdbuf_page_addr = host1x_bo_kmap(cmdbuf,
 218                                         reloc->cmdbuf.offset >> PAGE_SHIFT);
 219                         last_page = reloc->cmdbuf.offset >> PAGE_SHIFT;
 220 
 221                         if (unlikely(!cmdbuf_page_addr)) {
 222                                 pr_err("Could not map cmdbuf for relocation\n");
 223                                 return -ENOMEM;
 224                         }
 225                 }
 226 
 227                 target = cmdbuf_page_addr + (reloc->cmdbuf.offset & ~PAGE_MASK);
 228 patch_reloc:
 229                 *target = reloc_addr;
 230         }
 231 
 232         if (cmdbuf_page_addr)
 233                 host1x_bo_kunmap(cmdbuf, last_page, cmdbuf_page_addr);
 234 
 235         return 0;
 236 }
 237 
 238 static bool check_reloc(struct host1x_reloc *reloc, struct host1x_bo *cmdbuf,
 239                         unsigned int offset)
 240 {
 241         offset *= sizeof(u32);
 242 
 243         if (reloc->cmdbuf.bo != cmdbuf || reloc->cmdbuf.offset != offset)
 244                 return false;
 245 
 246         /* relocation shift value validation isn't implemented yet */
 247         if (reloc->shift)
 248                 return false;
 249 
 250         return true;
 251 }
 252 
 253 struct host1x_firewall {
 254         struct host1x_job *job;
 255         struct device *dev;
 256 
 257         unsigned int num_relocs;
 258         struct host1x_reloc *reloc;
 259 
 260         struct host1x_bo *cmdbuf;
 261         unsigned int offset;
 262 
 263         u32 words;
 264         u32 class;
 265         u32 reg;
 266         u32 mask;
 267         u32 count;
 268 };
 269 
 270 static int check_register(struct host1x_firewall *fw, unsigned long offset)
 271 {
 272         if (!fw->job->is_addr_reg)
 273                 return 0;
 274 
 275         if (fw->job->is_addr_reg(fw->dev, fw->class, offset)) {
 276                 if (!fw->num_relocs)
 277                         return -EINVAL;
 278 
 279                 if (!check_reloc(fw->reloc, fw->cmdbuf, fw->offset))
 280                         return -EINVAL;
 281 
 282                 fw->num_relocs--;
 283                 fw->reloc++;
 284         }
 285 
 286         return 0;
 287 }
 288 
 289 static int check_class(struct host1x_firewall *fw, u32 class)
 290 {
 291         if (!fw->job->is_valid_class) {
 292                 if (fw->class != class)
 293                         return -EINVAL;
 294         } else {
 295                 if (!fw->job->is_valid_class(fw->class))
 296                         return -EINVAL;
 297         }
 298 
 299         return 0;
 300 }
 301 
 302 static int check_mask(struct host1x_firewall *fw)
 303 {
 304         u32 mask = fw->mask;
 305         u32 reg = fw->reg;
 306         int ret;
 307 
 308         while (mask) {
 309                 if (fw->words == 0)
 310                         return -EINVAL;
 311 
 312                 if (mask & 1) {
 313                         ret = check_register(fw, reg);
 314                         if (ret < 0)
 315                                 return ret;
 316 
 317                         fw->words--;
 318                         fw->offset++;
 319                 }
 320                 mask >>= 1;
 321                 reg++;
 322         }
 323 
 324         return 0;
 325 }
 326 
 327 static int check_incr(struct host1x_firewall *fw)
 328 {
 329         u32 count = fw->count;
 330         u32 reg = fw->reg;
 331         int ret;
 332 
 333         while (count) {
 334                 if (fw->words == 0)
 335                         return -EINVAL;
 336 
 337                 ret = check_register(fw, reg);
 338                 if (ret < 0)
 339                         return ret;
 340 
 341                 reg++;
 342                 fw->words--;
 343                 fw->offset++;
 344                 count--;
 345         }
 346 
 347         return 0;
 348 }
 349 
 350 static int check_nonincr(struct host1x_firewall *fw)
 351 {
 352         u32 count = fw->count;
 353         int ret;
 354 
 355         while (count) {
 356                 if (fw->words == 0)
 357                         return -EINVAL;
 358 
 359                 ret = check_register(fw, fw->reg);
 360                 if (ret < 0)
 361                         return ret;
 362 
 363                 fw->words--;
 364                 fw->offset++;
 365                 count--;
 366         }
 367 
 368         return 0;
 369 }
 370 
 371 static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g)
 372 {
 373         u32 *cmdbuf_base = (u32 *)fw->job->gather_copy_mapped +
 374                 (g->offset / sizeof(u32));
 375         u32 job_class = fw->class;
 376         int err = 0;
 377 
 378         fw->words = g->words;
 379         fw->cmdbuf = g->bo;
 380         fw->offset = 0;
 381 
 382         while (fw->words && !err) {
 383                 u32 word = cmdbuf_base[fw->offset];
 384                 u32 opcode = (word & 0xf0000000) >> 28;
 385 
 386                 fw->mask = 0;
 387                 fw->reg = 0;
 388                 fw->count = 0;
 389                 fw->words--;
 390                 fw->offset++;
 391 
 392                 switch (opcode) {
 393                 case 0:
 394                         fw->class = word >> 6 & 0x3ff;
 395                         fw->mask = word & 0x3f;
 396                         fw->reg = word >> 16 & 0xfff;
 397                         err = check_class(fw, job_class);
 398                         if (!err)
 399                                 err = check_mask(fw);
 400                         if (err)
 401                                 goto out;
 402                         break;
 403                 case 1:
 404                         fw->reg = word >> 16 & 0xfff;
 405                         fw->count = word & 0xffff;
 406                         err = check_incr(fw);
 407                         if (err)
 408                                 goto out;
 409                         break;
 410 
 411                 case 2:
 412                         fw->reg = word >> 16 & 0xfff;
 413                         fw->count = word & 0xffff;
 414                         err = check_nonincr(fw);
 415                         if (err)
 416                                 goto out;
 417                         break;
 418 
 419                 case 3:
 420                         fw->mask = word & 0xffff;
 421                         fw->reg = word >> 16 & 0xfff;
 422                         err = check_mask(fw);
 423                         if (err)
 424                                 goto out;
 425                         break;
 426                 case 4:
 427                 case 14:
 428                         break;
 429                 default:
 430                         err = -EINVAL;
 431                         break;
 432                 }
 433         }
 434 
 435 out:
 436         return err;
 437 }
 438 
 439 static inline int copy_gathers(struct device *host, struct host1x_job *job,
 440                                struct device *dev)
 441 {
 442         struct host1x_firewall fw;
 443         size_t size = 0;
 444         size_t offset = 0;
 445         unsigned int i;
 446 
 447         fw.job = job;
 448         fw.dev = dev;
 449         fw.reloc = job->relocs;
 450         fw.num_relocs = job->num_relocs;
 451         fw.class = job->class;
 452 
 453         for (i = 0; i < job->num_gathers; i++) {
 454                 struct host1x_job_gather *g = &job->gathers[i];
 455 
 456                 size += g->words * sizeof(u32);
 457         }
 458 
 459         /*
 460          * Try a non-blocking allocation from a higher priority pools first,
 461          * as awaiting for the allocation here is a major performance hit.
 462          */
 463         job->gather_copy_mapped = dma_alloc_wc(host, size, &job->gather_copy,
 464                                                GFP_NOWAIT);
 465 
 466         /* the higher priority allocation failed, try the generic-blocking */
 467         if (!job->gather_copy_mapped)
 468                 job->gather_copy_mapped = dma_alloc_wc(host, size,
 469                                                        &job->gather_copy,
 470                                                        GFP_KERNEL);
 471         if (!job->gather_copy_mapped)
 472                 return -ENOMEM;
 473 
 474         job->gather_copy_size = size;
 475 
 476         for (i = 0; i < job->num_gathers; i++) {
 477                 struct host1x_job_gather *g = &job->gathers[i];
 478                 void *gather;
 479 
 480                 /* Copy the gather */
 481                 gather = host1x_bo_mmap(g->bo);
 482                 memcpy(job->gather_copy_mapped + offset, gather + g->offset,
 483                        g->words * sizeof(u32));
 484                 host1x_bo_munmap(g->bo, gather);
 485 
 486                 /* Store the location in the buffer */
 487                 g->base = job->gather_copy;
 488                 g->offset = offset;
 489 
 490                 /* Validate the job */
 491                 if (validate(&fw, g))
 492                         return -EINVAL;
 493 
 494                 offset += g->words * sizeof(u32);
 495         }
 496 
 497         /* No relocs should remain at this point */
 498         if (fw.num_relocs)
 499                 return -EINVAL;
 500 
 501         return 0;
 502 }
 503 
 504 int host1x_job_pin(struct host1x_job *job, struct device *dev)
 505 {
 506         int err;
 507         unsigned int i, j;
 508         struct host1x *host = dev_get_drvdata(dev->parent);
 509 
 510         /* pin memory */
 511         err = pin_job(host, job);
 512         if (err)
 513                 goto out;
 514 
 515         if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) {
 516                 err = copy_gathers(host->dev, job, dev);
 517                 if (err)
 518                         goto out;
 519         }
 520 
 521         /* patch gathers */
 522         for (i = 0; i < job->num_gathers; i++) {
 523                 struct host1x_job_gather *g = &job->gathers[i];
 524 
 525                 /* process each gather mem only once */
 526                 if (g->handled)
 527                         continue;
 528 
 529                 /* copy_gathers() sets gathers base if firewall is enabled */
 530                 if (!IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL))
 531                         g->base = job->gather_addr_phys[i];
 532 
 533                 for (j = i + 1; j < job->num_gathers; j++) {
 534                         if (job->gathers[j].bo == g->bo) {
 535                                 job->gathers[j].handled = true;
 536                                 job->gathers[j].base = g->base;
 537                         }
 538                 }
 539 
 540                 err = do_relocs(job, g);
 541                 if (err)
 542                         break;
 543         }
 544 
 545 out:
 546         if (err)
 547                 host1x_job_unpin(job);
 548         wmb();
 549 
 550         return err;
 551 }
 552 EXPORT_SYMBOL(host1x_job_pin);
 553 
 554 void host1x_job_unpin(struct host1x_job *job)
 555 {
 556         struct host1x *host = dev_get_drvdata(job->channel->dev->parent);
 557         unsigned int i;
 558 
 559         for (i = 0; i < job->num_unpins; i++) {
 560                 struct host1x_job_unpin_data *unpin = &job->unpins[i];
 561 
 562                 if (!IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL) &&
 563                     unpin->size && host->domain) {
 564                         iommu_unmap(host->domain, job->addr_phys[i],
 565                                     unpin->size);
 566                         free_iova(&host->iova,
 567                                 iova_pfn(&host->iova, job->addr_phys[i]));
 568                 }
 569 
 570                 host1x_bo_unpin(unpin->bo, unpin->sgt);
 571                 host1x_bo_put(unpin->bo);
 572         }
 573 
 574         job->num_unpins = 0;
 575 
 576         if (job->gather_copy_size)
 577                 dma_free_wc(host->dev, job->gather_copy_size,
 578                             job->gather_copy_mapped, job->gather_copy);
 579 }
 580 EXPORT_SYMBOL(host1x_job_unpin);
 581 
 582 /*
 583  * Debug routine used to dump job entries
 584  */
 585 void host1x_job_dump(struct device *dev, struct host1x_job *job)
 586 {
 587         dev_dbg(dev, "    SYNCPT_ID   %d\n", job->syncpt_id);
 588         dev_dbg(dev, "    SYNCPT_VAL  %d\n", job->syncpt_end);
 589         dev_dbg(dev, "    FIRST_GET   0x%x\n", job->first_get);
 590         dev_dbg(dev, "    TIMEOUT     %d\n", job->timeout);
 591         dev_dbg(dev, "    NUM_SLOTS   %d\n", job->num_slots);
 592         dev_dbg(dev, "    NUM_HANDLES %d\n", job->num_unpins);
 593 }

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