Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  chan.c   Sprache: C

 
/*
 * Copyright 2012 Red Hat Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
 *
 * Authors: Ben Skeggs
 */

#include "chan.h"
#include "chid.h"
#include "cgrp.h"
#include "runl.h"
#include "priv.h"

#include <core/ramht.h>
#include <subdev/mmu.h>
#include <engine/dma.h>

#include <nvif/if0020.h>

const struct nvkm_event_func
nvkm_chan_event = {
};

void
nvkm_chan_cctx_bind(struct nvkm_chan *chan, struct nvkm_engn *engn, struct nvkm_cctx *cctx)
{
 struct nvkm_cgrp *cgrp = chan->cgrp;
 struct nvkm_runl *runl = cgrp->runl;
 struct nvkm_engine *engine = engn->engine;

 if (!engn->func->bind)
  return;

 CHAN_TRACE(chan, "%sbind cctx %d[%s]", cctx ? "" : "un", engn->id, engine->subdev.name);

 /* Prevent any channel in channel group from being rescheduled, kick them
 * off host and any engine(s) they're loaded on.
 */

 if (cgrp->hw)
  nvkm_runl_block(runl);
 else
  nvkm_chan_block(chan);
 nvkm_chan_preempt(chan, true);

 /* Update context pointer. */
 engn->func->bind(engn, cctx, chan);

 /* Resume normal operation. */
 if (cgrp->hw)
  nvkm_runl_allow(runl);
 else
  nvkm_chan_allow(chan);
}

void
nvkm_chan_cctx_put(struct nvkm_chan *chan, struct nvkm_cctx **pcctx)
{
 struct nvkm_cctx *cctx = *pcctx;

 if (cctx) {
  struct nvkm_engn *engn = cctx->vctx->ectx->engn;

  if (refcount_dec_and_mutex_lock(&cctx->refs, &chan->cgrp->mutex)) {
   CHAN_TRACE(chan, "dtor cctx %d[%s]", engn->id, engn->engine->subdev.name);
   nvkm_cgrp_vctx_put(chan->cgrp, &cctx->vctx);
   list_del(&cctx->head);
   kfree(cctx);
   mutex_unlock(&chan->cgrp->mutex);
  }

  *pcctx = NULL;
 }
}

int
nvkm_chan_cctx_get(struct nvkm_chan *chan, struct nvkm_engn *engn, struct nvkm_cctx **pcctx,
     struct nvkm_client *client)
{
 struct nvkm_cgrp *cgrp = chan->cgrp;
 struct nvkm_vctx *vctx;
 struct nvkm_cctx *cctx;
 int ret;

 /* Look for an existing channel context for this engine+VEID. */
 mutex_lock(&cgrp->mutex);
 cctx = nvkm_list_find(cctx, &chan->cctxs, head,
         cctx->vctx->ectx->engn == engn && cctx->vctx->vmm == chan->vmm);
 if (cctx) {
  refcount_inc(&cctx->refs);
  *pcctx = cctx;
  mutex_unlock(&cgrp->mutex);
  return 0;
 }

 /* Nope - create a fresh one.  But, sub-context first. */
 ret = nvkm_cgrp_vctx_get(cgrp, engn, chan, &vctx, client);
 if (ret) {
  CHAN_ERROR(chan, "vctx %d[%s]: %d", engn->id, engn->engine->subdev.name, ret);
  goto done;
 }

 /* Now, create the channel context - to track engine binding. */
 CHAN_TRACE(chan, "ctor cctx %d[%s]", engn->id, engn->engine->subdev.name);
 if (!(cctx = *pcctx = kzalloc(sizeof(*cctx), GFP_KERNEL))) {
  nvkm_cgrp_vctx_put(cgrp, &vctx);
  ret = -ENOMEM;
  goto done;
 }

 cctx->vctx = vctx;
 refcount_set(&cctx->refs, 1);
 refcount_set(&cctx->uses, 0);
 list_add_tail(&cctx->head, &chan->cctxs);
done:
 mutex_unlock(&cgrp->mutex);
 return ret;
}

int
nvkm_chan_preempt_locked(struct nvkm_chan *chan, bool wait)
{
 struct nvkm_runl *runl = chan->cgrp->runl;

 CHAN_TRACE(chan, "preempt");
 chan->func->preempt(chan);
 if (!wait)
  return 0;

 return nvkm_runl_preempt_wait(runl);
}

int
nvkm_chan_preempt(struct nvkm_chan *chan, bool wait)
{
 int ret;

 if (!chan->func->preempt)
  return 0;

 mutex_lock(&chan->cgrp->runl->mutex);
 ret = nvkm_chan_preempt_locked(chan, wait);
 mutex_unlock(&chan->cgrp->runl->mutex);
 return ret;
}

void
nvkm_chan_remove_locked(struct nvkm_chan *chan)
{
 struct nvkm_cgrp *cgrp = chan->cgrp;
 struct nvkm_runl *runl = cgrp->runl;

 if (list_empty(&chan->head))
  return;

 CHAN_TRACE(chan, "remove");
 if (!--cgrp->chan_nr) {
  runl->cgrp_nr--;
  list_del(&cgrp->head);
 }
 runl->chan_nr--;
 list_del_init(&chan->head);
 atomic_set(&runl->changed, 1);
}

void
nvkm_chan_remove(struct nvkm_chan *chan, bool preempt)
{
 struct nvkm_runl *runl = chan->cgrp->runl;

 mutex_lock(&runl->mutex);
 if (preempt && chan->func->preempt)
  nvkm_chan_preempt_locked(chan, true);
 nvkm_chan_remove_locked(chan);
 nvkm_runl_update_locked(runl, true);
 mutex_unlock(&runl->mutex);
}

void
nvkm_chan_insert(struct nvkm_chan *chan)
{
 struct nvkm_cgrp *cgrp = chan->cgrp;
 struct nvkm_runl *runl = cgrp->runl;

 mutex_lock(&runl->mutex);
 if (WARN_ON(!list_empty(&chan->head))) {
  mutex_unlock(&runl->mutex);
  return;
 }

 CHAN_TRACE(chan, "insert");
 list_add_tail(&chan->head, &cgrp->chans);
 runl->chan_nr++;
 if (!cgrp->chan_nr++) {
  list_add_tail(&cgrp->head, &cgrp->runl->cgrps);
  runl->cgrp_nr++;
 }
 atomic_set(&runl->changed, 1);
 nvkm_runl_update_locked(runl, true);
 mutex_unlock(&runl->mutex);
}

static void
nvkm_chan_block_locked(struct nvkm_chan *chan)
{
 CHAN_TRACE(chan, "block %d", atomic_read(&chan->blocked));
 if (atomic_inc_return(&chan->blocked) == 1)
  chan->func->stop(chan);
}

void
nvkm_chan_error(struct nvkm_chan *chan, bool preempt)
{
 unsigned long flags;

 spin_lock_irqsave(&chan->lock, flags);
 if (atomic_inc_return(&chan->errored) == 1) {
  CHAN_ERROR(chan, "errored - disabling channel");
  nvkm_chan_block_locked(chan);
  if (preempt)
   chan->func->preempt(chan);
  nvkm_event_ntfy(&chan->cgrp->runl->chid->event, chan->id, NVKM_CHAN_EVENT_ERRORED);
 }
 spin_unlock_irqrestore(&chan->lock, flags);
}

void
nvkm_chan_block(struct nvkm_chan *chan)
{
 spin_lock_irq(&chan->lock);
 nvkm_chan_block_locked(chan);
 spin_unlock_irq(&chan->lock);
}

void
nvkm_chan_allow(struct nvkm_chan *chan)
{
 spin_lock_irq(&chan->lock);
 CHAN_TRACE(chan, "allow %d", atomic_read(&chan->blocked));
 if (atomic_dec_and_test(&chan->blocked))
  chan->func->start(chan);
 spin_unlock_irq(&chan->lock);
}

void
nvkm_chan_del(struct nvkm_chan **pchan)
{
 struct nvkm_chan *chan = *pchan;

 if (!chan)
  return;

 if (chan->func->ramfc->clear)
  chan->func->ramfc->clear(chan);

 nvkm_ramht_del(&chan->ramht);
 nvkm_gpuobj_del(&chan->pgd);
 nvkm_gpuobj_del(&chan->eng);
 nvkm_gpuobj_del(&chan->cache);
 nvkm_gpuobj_del(&chan->ramfc);

 if (chan->cgrp) {
  nvkm_chid_put(chan->cgrp->runl->chid, chan->id, &chan->cgrp->lock);
  nvkm_cgrp_unref(&chan->cgrp);
 }

 nvkm_memory_unref(&chan->userd.mem);

 if (chan->vmm) {
  nvkm_vmm_part(chan->vmm, chan->inst->memory);
  nvkm_vmm_unref(&chan->vmm);
 }

 nvkm_gpuobj_del(&chan->push);
 nvkm_gpuobj_del(&chan->inst);
 kfree(chan);
}

void
nvkm_chan_put(struct nvkm_chan **pchan, unsigned long irqflags)
{
 struct nvkm_chan *chan = *pchan;

 if (!chan)
  return;

 *pchan = NULL;
 spin_unlock_irqrestore(&chan->cgrp->lock, irqflags);
}

struct nvkm_chan *
nvkm_chan_get_inst(struct nvkm_engine *engine, u64 inst, unsigned long *pirqflags)
{
 struct nvkm_fifo *fifo = engine->subdev.device->fifo;
 struct nvkm_runl *runl;
 struct nvkm_engn *engn;
 struct nvkm_chan *chan;

 nvkm_runl_foreach(runl, fifo) {
  nvkm_runl_foreach_engn(engn, runl) {
   if (engine == &fifo->engine || engn->engine == engine) {
    chan = nvkm_runl_chan_get_inst(runl, inst, pirqflags);
    if (chan || engn->engine == engine)
     return chan;
   }
  }
 }

 return NULL;
}

struct nvkm_chan *
nvkm_chan_get_chid(struct nvkm_engine *engine, int id, unsigned long *pirqflags)
{
 struct nvkm_fifo *fifo = engine->subdev.device->fifo;
 struct nvkm_runl *runl;
 struct nvkm_engn *engn;

 nvkm_runl_foreach(runl, fifo) {
  nvkm_runl_foreach_engn(engn, runl) {
   if (fifo->chid || engn->engine == engine)
    return nvkm_runl_chan_get_chid(runl, id, pirqflags);
  }
 }

 return NULL;
}

int
nvkm_chan_new_(const struct nvkm_chan_func *func, struct nvkm_runl *runl, int runq,
        struct nvkm_cgrp *cgrp, const char *name, bool priv, u32 devm, struct nvkm_vmm *vmm,
        struct nvkm_dmaobj *dmaobj, u64 offset, u64 length,
        struct nvkm_memory *userd, u64 ouserd, struct nvkm_chan **pchan)
{
 struct nvkm_fifo *fifo = runl->fifo;
 struct nvkm_device *device = fifo->engine.subdev.device;
 struct nvkm_chan *chan;
 int ret;

 /* Validate arguments against class requirements. */
 if ((runq && runq >= runl->func->runqs) ||
     (!func->inst->vmm != !vmm) ||
     (!func->userd->bar == !userd) ||
     (!func->ramfc->ctxdma != !dmaobj) ||
     ((func->ramfc->devm < devm) && devm != BIT(0)) ||
     (!func->ramfc->priv && priv)) {
  RUNL_DEBUG(runl, "args runq:%d:%d vmm:%d:%p userd:%d:%p "
     "push:%d:%p devm:%08x:%08x priv:%d:%d",
      runl->func->runqs, runq, func->inst->vmm, vmm,
      func->userd->bar, userd, func->ramfc->ctxdma, dmaobj,
      func->ramfc->devm, devm, func->ramfc->priv, priv);
  return -EINVAL;
 }

 if (!(chan = *pchan = kzalloc(sizeof(*chan), GFP_KERNEL)))
  return -ENOMEM;

 chan->func = func;
 strscpy(chan->name, name, sizeof(chan->name));
 chan->runq = runq;
 chan->id = -1;
 spin_lock_init(&chan->lock);
 atomic_set(&chan->blocked, 1);
 atomic_set(&chan->errored, 0);
 INIT_LIST_HEAD(&chan->cctxs);
 INIT_LIST_HEAD(&chan->head);

 /* Join channel group.
 *
 * GK110 and newer support channel groups (aka TSGs), where individual channels
 * share a timeslice, and, engine context(s).
 *
 * As such, engine contexts are tracked in nvkm_cgrp and we need them even when
 * channels aren't in an API channel group, and on HW that doesn't support TSGs.
 */

 if (!cgrp) {
  ret = nvkm_cgrp_new(runl, chan->name, vmm, fifo->func->cgrp.force, &chan->cgrp);
  if (ret) {
   RUNL_DEBUG(runl, "cgrp %d", ret);
   return ret;
  }

  cgrp = chan->cgrp;
 } else {
  if (cgrp->runl != runl || cgrp->vmm != vmm) {
   RUNL_DEBUG(runl, "cgrp %d %d", cgrp->runl != runl, cgrp->vmm != vmm);
   return -EINVAL;
  }

  chan->cgrp = nvkm_cgrp_ref(cgrp);
 }

 /* Allocate instance block. */
 ret = nvkm_gpuobj_new(device, func->inst->size, 0x1000, func->inst->zero, NULL,
         &chan->inst);
 if (ret) {
  RUNL_DEBUG(runl, "inst %d", ret);
  return ret;
 }

 /* Initialise virtual address-space. */
 if (func->inst->vmm) {
  if (WARN_ON(vmm->mmu != device->mmu))
   return -EINVAL;

  ret = nvkm_vmm_join(vmm, chan->inst->memory);
  if (ret) {
   RUNL_DEBUG(runl, "vmm %d", ret);
   return ret;
  }

  chan->vmm = nvkm_vmm_ref(vmm);
 }

 /* Allocate HW ctxdma for push buffer. */
 if (func->ramfc->ctxdma) {
  ret = nvkm_object_bind(&dmaobj->object, chan->inst, -16, &chan->push);
  if (ret) {
   RUNL_DEBUG(runl, "bind %d", ret);
   return ret;
  }
 }

 /* Allocate channel ID. */
 chan->id = nvkm_chid_get(runl->chid, chan);
 if (chan->id >= 0) {
  if (!func->userd->bar) {
   if (ouserd + chan->func->userd->size >=
    nvkm_memory_size(userd)) {
    RUNL_DEBUG(runl, "ouserd %llx", ouserd);
    return -EINVAL;
   }

   ret = nvkm_memory_kmap(userd, &chan->userd.mem);
   if (ret) {
    RUNL_DEBUG(runl, "userd %d", ret);
    return ret;
   }

   chan->userd.base = ouserd;
  } else {
   chan->userd.mem = nvkm_memory_ref(fifo->userd.mem);
   chan->userd.base = chan->id * chan->func->userd->size;
  }
 }

 if (chan->id < 0) {
  RUNL_ERROR(runl, "!chids");
  return -ENOSPC;
 }

 if (cgrp->id < 0)
  cgrp->id = chan->id;

 /* Initialise USERD. */
 if (chan->func->userd->clear)
  chan->func->userd->clear(chan);

 /* Initialise RAMFC. */
 ret = chan->func->ramfc->write(chan, offset, length, devm, priv);
 if (ret) {
  RUNL_DEBUG(runl, "ramfc %d", ret);
  return ret;
 }

 return 0;
}

Messung V0.5
C=96 H=95 G=95

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge