pushbuf.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  1. /*
  2. * Copyright 2012 Red Hat Inc.
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  17. * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18. * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19. * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20. * OTHER DEALINGS IN THE SOFTWARE.
  21. *
  22. * Authors: Ben Skeggs
  23. */
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <stdint.h>
  27. #include <stdbool.h>
  28. #include <string.h>
  29. #include <assert.h>
  30. #include <errno.h>
  31. #include <inttypes.h>
  32. #include <xf86drm.h>
  33. #include <xf86atomic.h>
  34. #include "libdrm_lists.h"
  35. #include "nouveau_drm.h"
  36. #include "nouveau.h"
  37. #include "private.h"
  38. struct nouveau_pushbuf_krec {
  39. struct nouveau_pushbuf_krec *next;
  40. struct drm_nouveau_gem_pushbuf_bo buffer[NOUVEAU_GEM_MAX_BUFFERS];
  41. struct drm_nouveau_gem_pushbuf_reloc reloc[NOUVEAU_GEM_MAX_RELOCS];
  42. struct drm_nouveau_gem_pushbuf_push push[NOUVEAU_GEM_MAX_PUSH];
  43. int nr_buffer;
  44. int nr_reloc;
  45. int nr_push;
  46. uint64_t vram_used;
  47. uint64_t gart_used;
  48. };
  49. struct nouveau_pushbuf_priv {
  50. struct nouveau_pushbuf base;
  51. struct nouveau_pushbuf_krec *list;
  52. struct nouveau_pushbuf_krec *krec;
  53. struct nouveau_list bctx_list;
  54. struct nouveau_bo *bo;
  55. uint32_t type;
  56. uint32_t suffix0;
  57. uint32_t suffix1;
  58. uint32_t *ptr;
  59. uint32_t *bgn;
  60. int bo_next;
  61. int bo_nr;
  62. struct nouveau_bo *bos[];
  63. };
  64. static inline struct nouveau_pushbuf_priv *
  65. nouveau_pushbuf(struct nouveau_pushbuf *push)
  66. {
  67. return (struct nouveau_pushbuf_priv *)push;
  68. }
  69. static int pushbuf_validate(struct nouveau_pushbuf *, bool);
  70. static int pushbuf_flush(struct nouveau_pushbuf *);
  71. static bool
  72. pushbuf_kref_fits(struct nouveau_pushbuf *push, struct nouveau_bo *bo,
  73. uint32_t *domains)
  74. {
  75. struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
  76. struct nouveau_pushbuf_krec *krec = nvpb->krec;
  77. struct nouveau_device *dev = push->client->device;
  78. struct nouveau_bo *kbo;
  79. struct drm_nouveau_gem_pushbuf_bo *kref;
  80. int i;
  81. /* VRAM is the only valid domain. GART and VRAM|GART buffers
  82. * are all accounted to GART, so if this doesn't fit in VRAM
  83. * straight up, a flush is needed.
  84. */
  85. if (*domains == NOUVEAU_GEM_DOMAIN_VRAM) {
  86. if (krec->vram_used + bo->size > dev->vram_limit)
  87. return false;
  88. krec->vram_used += bo->size;
  89. return true;
  90. }
  91. /* GART or VRAM|GART buffer. Account both of these buffer types
  92. * to GART only for the moment, which simplifies things. If the
  93. * buffer can fit already, we're done here.
  94. */
  95. if (krec->gart_used + bo->size <= dev->gart_limit) {
  96. krec->gart_used += bo->size;
  97. return true;
  98. }
  99. /* Ran out of GART space, if it's a VRAM|GART buffer and it'll
  100. * fit into available VRAM, turn it into a VRAM buffer
  101. */
  102. if ((*domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
  103. krec->vram_used + bo->size <= dev->vram_limit) {
  104. *domains &= NOUVEAU_GEM_DOMAIN_VRAM;
  105. krec->vram_used += bo->size;
  106. return true;
  107. }
  108. /* Still couldn't fit the buffer in anywhere, so as a last resort;
  109. * scan the buffer list for VRAM|GART buffers and turn them into
  110. * VRAM buffers until we have enough space in GART for this one
  111. */
  112. kref = krec->buffer;
  113. for (i = 0; i < krec->nr_buffer; i++, kref++) {
  114. if (!(kref->valid_domains & NOUVEAU_GEM_DOMAIN_GART))
  115. continue;
  116. kbo = (void *)(unsigned long)kref->user_priv;
  117. if (!(kref->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) ||
  118. krec->vram_used + kbo->size > dev->vram_limit)
  119. continue;
  120. kref->valid_domains &= NOUVEAU_GEM_DOMAIN_VRAM;
  121. krec->gart_used -= kbo->size;
  122. krec->vram_used += kbo->size;
  123. if (krec->gart_used + bo->size <= dev->gart_limit) {
  124. krec->gart_used += bo->size;
  125. return true;
  126. }
  127. }
  128. /* Couldn't resolve a placement, need to force a flush */
  129. return false;
  130. }
  131. static struct drm_nouveau_gem_pushbuf_bo *
  132. pushbuf_kref(struct nouveau_pushbuf *push, struct nouveau_bo *bo,
  133. uint32_t flags)
  134. {
  135. struct nouveau_device *dev = push->client->device;
  136. struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
  137. struct nouveau_pushbuf_krec *krec = nvpb->krec;
  138. struct nouveau_pushbuf *fpush;
  139. struct drm_nouveau_gem_pushbuf_bo *kref;
  140. uint32_t domains, domains_wr, domains_rd;
  141. domains = 0;
  142. if (flags & NOUVEAU_BO_VRAM)
  143. domains |= NOUVEAU_GEM_DOMAIN_VRAM;
  144. if (flags & NOUVEAU_BO_GART)
  145. domains |= NOUVEAU_GEM_DOMAIN_GART;
  146. domains_wr = domains * !!(flags & NOUVEAU_BO_WR);
  147. domains_rd = domains * !!(flags & NOUVEAU_BO_RD);
  148. /* if buffer is referenced on another pushbuf that is owned by the
  149. * same client, we need to flush the other pushbuf first to ensure
  150. * the correct ordering of commands
  151. */
  152. fpush = cli_push_get(push->client, bo);
  153. if (fpush && fpush != push)
  154. pushbuf_flush(fpush);
  155. kref = cli_kref_get(push->client, bo);
  156. if (kref) {
  157. /* possible conflict in memory types - flush and retry */
  158. if (!(kref->valid_domains & domains))
  159. return NULL;
  160. /* VRAM|GART buffer turning into a VRAM buffer. Make sure
  161. * it'll fit in VRAM and force a flush if not.
  162. */
  163. if ((kref->valid_domains & NOUVEAU_GEM_DOMAIN_GART) &&
  164. ( domains == NOUVEAU_GEM_DOMAIN_VRAM)) {
  165. if (krec->vram_used + bo->size > dev->vram_limit)
  166. return NULL;
  167. krec->vram_used += bo->size;
  168. krec->gart_used -= bo->size;
  169. }
  170. kref->valid_domains &= domains;
  171. kref->write_domains |= domains_wr;
  172. kref->read_domains |= domains_rd;
  173. } else {
  174. if (krec->nr_buffer == NOUVEAU_GEM_MAX_BUFFERS ||
  175. !pushbuf_kref_fits(push, bo, &domains))
  176. return NULL;
  177. kref = &krec->buffer[krec->nr_buffer++];
  178. kref->user_priv = (unsigned long)bo;
  179. kref->handle = bo->handle;
  180. kref->valid_domains = domains;
  181. kref->write_domains = domains_wr;
  182. kref->read_domains = domains_rd;
  183. kref->presumed.valid = 1;
  184. kref->presumed.offset = bo->offset;
  185. if (bo->flags & NOUVEAU_BO_VRAM)
  186. kref->presumed.domain = NOUVEAU_GEM_DOMAIN_VRAM;
  187. else
  188. kref->presumed.domain = NOUVEAU_GEM_DOMAIN_GART;
  189. cli_kref_set(push->client, bo, kref, push);
  190. atomic_inc(&nouveau_bo(bo)->refcnt);
  191. }
  192. return kref;
  193. }
  194. static uint32_t
  195. pushbuf_krel(struct nouveau_pushbuf *push, struct nouveau_bo *bo,
  196. uint32_t data, uint32_t flags, uint32_t vor, uint32_t tor)
  197. {
  198. struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
  199. struct nouveau_pushbuf_krec *krec = nvpb->krec;
  200. struct drm_nouveau_gem_pushbuf_reloc *krel;
  201. struct drm_nouveau_gem_pushbuf_bo *pkref;
  202. struct drm_nouveau_gem_pushbuf_bo *bkref;
  203. uint32_t reloc = data;
  204. pkref = cli_kref_get(push->client, nvpb->bo);
  205. bkref = cli_kref_get(push->client, bo);
  206. krel = &krec->reloc[krec->nr_reloc++];
  207. assert(pkref);
  208. assert(bkref);
  209. krel->reloc_bo_index = pkref - krec->buffer;
  210. krel->reloc_bo_offset = (push->cur - nvpb->ptr) * 4;
  211. krel->bo_index = bkref - krec->buffer;
  212. krel->flags = 0;
  213. krel->data = data;
  214. krel->vor = vor;
  215. krel->tor = tor;
  216. if (flags & NOUVEAU_BO_LOW) {
  217. reloc = (bkref->presumed.offset + data);
  218. krel->flags |= NOUVEAU_GEM_RELOC_LOW;
  219. } else
  220. if (flags & NOUVEAU_BO_HIGH) {
  221. reloc = (bkref->presumed.offset + data) >> 32;
  222. krel->flags |= NOUVEAU_GEM_RELOC_HIGH;
  223. }
  224. if (flags & NOUVEAU_BO_OR) {
  225. if (bkref->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM)
  226. reloc |= vor;
  227. else
  228. reloc |= tor;
  229. krel->flags |= NOUVEAU_GEM_RELOC_OR;
  230. }
  231. return reloc;
  232. }
  233. static void
  234. pushbuf_dump(struct nouveau_pushbuf_krec *krec, int krec_id, int chid)
  235. {
  236. struct drm_nouveau_gem_pushbuf_reloc *krel;
  237. struct drm_nouveau_gem_pushbuf_push *kpsh;
  238. struct drm_nouveau_gem_pushbuf_bo *kref;
  239. struct nouveau_bo *bo;
  240. uint32_t *bgn, *end;
  241. int i;
  242. err("ch%d: krec %d pushes %d bufs %d relocs %d\n", chid,
  243. krec_id, krec->nr_push, krec->nr_buffer, krec->nr_reloc);
  244. kref = krec->buffer;
  245. for (i = 0; i < krec->nr_buffer; i++, kref++) {
  246. bo = (void *)(uintptr_t)kref->user_priv;
  247. err("ch%d: buf %08x %08x %08x %08x %08x %p 0x%"PRIx64" 0x%"PRIx64"\n", chid, i,
  248. kref->handle, kref->valid_domains,
  249. kref->read_domains, kref->write_domains, bo->map, bo->offset, bo->size);
  250. }
  251. krel = krec->reloc;
  252. for (i = 0; i < krec->nr_reloc; i++, krel++) {
  253. err("ch%d: rel %08x %08x %08x %08x %08x %08x %08x\n",
  254. chid, krel->reloc_bo_index, krel->reloc_bo_offset,
  255. krel->bo_index, krel->flags, krel->data,
  256. krel->vor, krel->tor);
  257. }
  258. kpsh = krec->push;
  259. for (i = 0; i < krec->nr_push; i++, kpsh++) {
  260. kref = krec->buffer + kpsh->bo_index;
  261. bo = (void *)(unsigned long)kref->user_priv;
  262. bgn = (uint32_t *)((char *)bo->map + kpsh->offset);
  263. end = bgn + ((kpsh->length & 0x7fffff) /4);
  264. err("ch%d: psh %s%08x %010llx %010llx\n", chid,
  265. bo->map ? "" : "(unmapped) ", kpsh->bo_index,
  266. (unsigned long long)kpsh->offset,
  267. (unsigned long long)(kpsh->offset + kpsh->length));
  268. if (!bo->map)
  269. continue;
  270. while (bgn < end)
  271. err("\t0x%08x\n", *bgn++);
  272. }
  273. }
  274. static int
  275. pushbuf_submit(struct nouveau_pushbuf *push, struct nouveau_object *chan)
  276. {
  277. struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
  278. struct nouveau_pushbuf_krec *krec = nvpb->list;
  279. struct nouveau_device *dev = push->client->device;
  280. struct nouveau_drm *drm = nouveau_drm(&dev->object);
  281. struct drm_nouveau_gem_pushbuf_bo_presumed *info;
  282. struct drm_nouveau_gem_pushbuf_bo *kref;
  283. struct drm_nouveau_gem_pushbuf req;
  284. struct nouveau_fifo *fifo = chan->data;
  285. struct nouveau_bo *bo;
  286. int krec_id = 0;
  287. int ret = 0, i;
  288. if (chan->oclass != NOUVEAU_FIFO_CHANNEL_CLASS)
  289. return -EINVAL;
  290. if (push->kick_notify)
  291. push->kick_notify(push);
  292. nouveau_pushbuf_data(push, NULL, 0, 0);
  293. while (krec && krec->nr_push) {
  294. req.channel = fifo->channel;
  295. req.nr_buffers = krec->nr_buffer;
  296. req.buffers = (uint64_t)(unsigned long)krec->buffer;
  297. req.nr_relocs = krec->nr_reloc;
  298. req.nr_push = krec->nr_push;
  299. req.relocs = (uint64_t)(unsigned long)krec->reloc;
  300. req.push = (uint64_t)(unsigned long)krec->push;
  301. req.suffix0 = nvpb->suffix0;
  302. req.suffix1 = nvpb->suffix1;
  303. req.vram_available = 0; /* for valgrind */
  304. if (dbg_on(1))
  305. req.vram_available |= NOUVEAU_GEM_PUSHBUF_SYNC;
  306. req.gart_available = 0;
  307. if (dbg_on(0))
  308. pushbuf_dump(krec, krec_id++, fifo->channel);
  309. #ifndef SIMULATE
  310. ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_PUSHBUF,
  311. &req, sizeof(req));
  312. nvpb->suffix0 = req.suffix0;
  313. nvpb->suffix1 = req.suffix1;
  314. dev->vram_limit = (req.vram_available *
  315. nouveau_device(dev)->vram_limit_percent) / 100;
  316. dev->gart_limit = (req.gart_available *
  317. nouveau_device(dev)->gart_limit_percent) / 100;
  318. #else
  319. if (dbg_on(31))
  320. ret = -EINVAL;
  321. #endif
  322. if (ret) {
  323. err("kernel rejected pushbuf: %s\n", strerror(-ret));
  324. pushbuf_dump(krec, krec_id++, fifo->channel);
  325. break;
  326. }
  327. kref = krec->buffer;
  328. for (i = 0; i < krec->nr_buffer; i++, kref++) {
  329. bo = (void *)(unsigned long)kref->user_priv;
  330. info = &kref->presumed;
  331. if (!info->valid) {
  332. bo->flags &= ~NOUVEAU_BO_APER;
  333. if (info->domain == NOUVEAU_GEM_DOMAIN_VRAM)
  334. bo->flags |= NOUVEAU_BO_VRAM;
  335. else
  336. bo->flags |= NOUVEAU_BO_GART;
  337. bo->offset = info->offset;
  338. }
  339. if (kref->write_domains)
  340. nouveau_bo(bo)->access |= NOUVEAU_BO_WR;
  341. if (kref->read_domains)
  342. nouveau_bo(bo)->access |= NOUVEAU_BO_RD;
  343. }
  344. krec = krec->next;
  345. }
  346. return ret;
  347. }
  348. static int
  349. pushbuf_flush(struct nouveau_pushbuf *push)
  350. {
  351. struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
  352. struct nouveau_pushbuf_krec *krec = nvpb->krec;
  353. struct drm_nouveau_gem_pushbuf_bo *kref;
  354. struct nouveau_bufctx *bctx, *btmp;
  355. struct nouveau_bo *bo;
  356. int ret = 0, i;
  357. if (push->channel) {
  358. ret = pushbuf_submit(push, push->channel);
  359. } else {
  360. nouveau_pushbuf_data(push, NULL, 0, 0);
  361. krec->next = malloc(sizeof(*krec));
  362. nvpb->krec = krec->next;
  363. }
  364. kref = krec->buffer;
  365. for (i = 0; i < krec->nr_buffer; i++, kref++) {
  366. bo = (void *)(unsigned long)kref->user_priv;
  367. cli_kref_set(push->client, bo, NULL, NULL);
  368. if (push->channel)
  369. nouveau_bo_ref(NULL, &bo);
  370. }
  371. krec = nvpb->krec;
  372. krec->vram_used = 0;
  373. krec->gart_used = 0;
  374. krec->nr_buffer = 0;
  375. krec->nr_reloc = 0;
  376. krec->nr_push = 0;
  377. DRMLISTFOREACHENTRYSAFE(bctx, btmp, &nvpb->bctx_list, head) {
  378. DRMLISTJOIN(&bctx->current, &bctx->pending);
  379. DRMINITLISTHEAD(&bctx->current);
  380. DRMLISTDELINIT(&bctx->head);
  381. }
  382. return ret;
  383. }
  384. static void
  385. pushbuf_refn_fail(struct nouveau_pushbuf *push, int sref, int srel)
  386. {
  387. struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
  388. struct nouveau_pushbuf_krec *krec = nvpb->krec;
  389. struct drm_nouveau_gem_pushbuf_bo *kref;
  390. kref = krec->buffer + sref;
  391. while (krec->nr_buffer-- > sref) {
  392. struct nouveau_bo *bo = (void *)(unsigned long)kref->user_priv;
  393. cli_kref_set(push->client, bo, NULL, NULL);
  394. nouveau_bo_ref(NULL, &bo);
  395. kref++;
  396. }
  397. krec->nr_buffer = sref;
  398. krec->nr_reloc = srel;
  399. }
  400. static int
  401. pushbuf_refn(struct nouveau_pushbuf *push, bool retry,
  402. struct nouveau_pushbuf_refn *refs, int nr)
  403. {
  404. struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
  405. struct nouveau_pushbuf_krec *krec = nvpb->krec;
  406. struct drm_nouveau_gem_pushbuf_bo *kref;
  407. int sref = krec->nr_buffer;
  408. int ret = 0, i;
  409. for (i = 0; i < nr; i++) {
  410. kref = pushbuf_kref(push, refs[i].bo, refs[i].flags);
  411. if (!kref) {
  412. ret = -ENOSPC;
  413. break;
  414. }
  415. }
  416. if (ret) {
  417. pushbuf_refn_fail(push, sref, krec->nr_reloc);
  418. if (retry) {
  419. pushbuf_flush(push);
  420. nouveau_pushbuf_space(push, 0, 0, 0);
  421. return pushbuf_refn(push, false, refs, nr);
  422. }
  423. }
  424. return ret;
  425. }
  426. static int
  427. pushbuf_validate(struct nouveau_pushbuf *push, bool retry)
  428. {
  429. struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
  430. struct nouveau_pushbuf_krec *krec = nvpb->krec;
  431. struct drm_nouveau_gem_pushbuf_bo *kref;
  432. struct nouveau_bufctx *bctx = push->bufctx;
  433. struct nouveau_bufref *bref;
  434. int relocs = bctx ? bctx->relocs * 2: 0;
  435. int sref, srel, ret;
  436. ret = nouveau_pushbuf_space(push, relocs, relocs, 0);
  437. if (ret || bctx == NULL)
  438. return ret;
  439. sref = krec->nr_buffer;
  440. srel = krec->nr_reloc;
  441. DRMLISTDEL(&bctx->head);
  442. DRMLISTADD(&bctx->head, &nvpb->bctx_list);
  443. DRMLISTFOREACHENTRY(bref, &bctx->pending, thead) {
  444. kref = pushbuf_kref(push, bref->bo, bref->flags);
  445. if (!kref) {
  446. ret = -ENOSPC;
  447. break;
  448. }
  449. if (bref->packet) {
  450. pushbuf_krel(push, bref->bo, bref->packet, 0, 0, 0);
  451. *push->cur++ = 0;
  452. pushbuf_krel(push, bref->bo, bref->data, bref->flags,
  453. bref->vor, bref->tor);
  454. *push->cur++ = 0;
  455. }
  456. }
  457. DRMLISTJOIN(&bctx->pending, &bctx->current);
  458. DRMINITLISTHEAD(&bctx->pending);
  459. if (ret) {
  460. pushbuf_refn_fail(push, sref, srel);
  461. if (retry) {
  462. pushbuf_flush(push);
  463. return pushbuf_validate(push, false);
  464. }
  465. }
  466. return ret;
  467. }
  468. drm_public int
  469. nouveau_pushbuf_new(struct nouveau_client *client, struct nouveau_object *chan,
  470. int nr, uint32_t size, bool immediate,
  471. struct nouveau_pushbuf **ppush)
  472. {
  473. struct nouveau_drm *drm = nouveau_drm(&client->device->object);
  474. struct nouveau_fifo *fifo = chan->data;
  475. struct nouveau_pushbuf_priv *nvpb;
  476. struct nouveau_pushbuf *push;
  477. struct drm_nouveau_gem_pushbuf req = {};
  478. int ret;
  479. if (chan->oclass != NOUVEAU_FIFO_CHANNEL_CLASS)
  480. return -EINVAL;
  481. /* nop pushbuf call, to get the current "return to main" sequence
  482. * we need to append to the pushbuf on early chipsets
  483. */
  484. req.channel = fifo->channel;
  485. req.nr_push = 0;
  486. ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_PUSHBUF,
  487. &req, sizeof(req));
  488. if (ret)
  489. return ret;
  490. nvpb = calloc(1, sizeof(*nvpb) + nr * sizeof(*nvpb->bos));
  491. if (!nvpb)
  492. return -ENOMEM;
  493. #ifndef SIMULATE
  494. nvpb->suffix0 = req.suffix0;
  495. nvpb->suffix1 = req.suffix1;
  496. #else
  497. nvpb->suffix0 = 0xffffffff;
  498. nvpb->suffix1 = 0xffffffff;
  499. #endif
  500. nvpb->krec = calloc(1, sizeof(*nvpb->krec));
  501. nvpb->list = nvpb->krec;
  502. if (!nvpb->krec) {
  503. free(nvpb);
  504. return -ENOMEM;
  505. }
  506. push = &nvpb->base;
  507. push->client = client;
  508. push->channel = immediate ? chan : NULL;
  509. push->flags = NOUVEAU_BO_RD;
  510. if (fifo->pushbuf & NOUVEAU_GEM_DOMAIN_GART) {
  511. push->flags |= NOUVEAU_BO_GART;
  512. nvpb->type = NOUVEAU_BO_GART;
  513. } else
  514. if (fifo->pushbuf & NOUVEAU_GEM_DOMAIN_VRAM) {
  515. push->flags |= NOUVEAU_BO_VRAM;
  516. nvpb->type = NOUVEAU_BO_VRAM;
  517. }
  518. nvpb->type |= NOUVEAU_BO_MAP;
  519. for (nvpb->bo_nr = 0; nvpb->bo_nr < nr; nvpb->bo_nr++) {
  520. ret = nouveau_bo_new(client->device, nvpb->type, 0, size,
  521. NULL, &nvpb->bos[nvpb->bo_nr]);
  522. if (ret) {
  523. nouveau_pushbuf_del(&push);
  524. return ret;
  525. }
  526. }
  527. DRMINITLISTHEAD(&nvpb->bctx_list);
  528. *ppush = push;
  529. return 0;
  530. }
  531. drm_public void
  532. nouveau_pushbuf_del(struct nouveau_pushbuf **ppush)
  533. {
  534. struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(*ppush);
  535. if (nvpb) {
  536. struct drm_nouveau_gem_pushbuf_bo *kref;
  537. struct nouveau_pushbuf_krec *krec;
  538. while ((krec = nvpb->list)) {
  539. kref = krec->buffer;
  540. while (krec->nr_buffer--) {
  541. unsigned long priv = kref++->user_priv;
  542. struct nouveau_bo *bo = (void *)priv;
  543. cli_kref_set(nvpb->base.client, bo, NULL, NULL);
  544. nouveau_bo_ref(NULL, &bo);
  545. }
  546. nvpb->list = krec->next;
  547. free(krec);
  548. }
  549. while (nvpb->bo_nr--)
  550. nouveau_bo_ref(NULL, &nvpb->bos[nvpb->bo_nr]);
  551. nouveau_bo_ref(NULL, &nvpb->bo);
  552. free(nvpb);
  553. }
  554. *ppush = NULL;
  555. }
  556. drm_public struct nouveau_bufctx *
  557. nouveau_pushbuf_bufctx(struct nouveau_pushbuf *push, struct nouveau_bufctx *ctx)
  558. {
  559. struct nouveau_bufctx *prev = push->bufctx;
  560. push->bufctx = ctx;
  561. return prev;
  562. }
  563. drm_public int
  564. nouveau_pushbuf_space(struct nouveau_pushbuf *push,
  565. uint32_t dwords, uint32_t relocs, uint32_t pushes)
  566. {
  567. struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
  568. struct nouveau_pushbuf_krec *krec = nvpb->krec;
  569. struct nouveau_client *client = push->client;
  570. struct nouveau_bo *bo = NULL;
  571. bool flushed = false;
  572. int ret = 0;
  573. /* switch to next buffer if insufficient space in the current one */
  574. if (push->cur + dwords >= push->end) {
  575. if (nvpb->bo_next < nvpb->bo_nr) {
  576. nouveau_bo_ref(nvpb->bos[nvpb->bo_next++], &bo);
  577. if (nvpb->bo_next == nvpb->bo_nr && push->channel)
  578. nvpb->bo_next = 0;
  579. } else {
  580. ret = nouveau_bo_new(client->device, nvpb->type, 0,
  581. nvpb->bos[0]->size, NULL, &bo);
  582. if (ret)
  583. return ret;
  584. }
  585. }
  586. /* make sure there's always enough space to queue up the pending
  587. * data in the pushbuf proper
  588. */
  589. pushes++;
  590. /* need to flush if we've run out of space on an immediate pushbuf,
  591. * if the new buffer won't fit, or if the kernel push/reloc limits
  592. * have been hit
  593. */
  594. if ((bo && ( push->channel ||
  595. !pushbuf_kref(push, bo, push->flags))) ||
  596. krec->nr_reloc + relocs >= NOUVEAU_GEM_MAX_RELOCS ||
  597. krec->nr_push + pushes >= NOUVEAU_GEM_MAX_PUSH) {
  598. if (nvpb->bo && krec->nr_buffer)
  599. pushbuf_flush(push);
  600. flushed = true;
  601. }
  602. /* if necessary, switch to new buffer */
  603. if (bo) {
  604. ret = nouveau_bo_map(bo, NOUVEAU_BO_WR, push->client);
  605. if (ret)
  606. return ret;
  607. nouveau_pushbuf_data(push, NULL, 0, 0);
  608. nouveau_bo_ref(bo, &nvpb->bo);
  609. nouveau_bo_ref(NULL, &bo);
  610. nvpb->bgn = nvpb->bo->map;
  611. nvpb->ptr = nvpb->bgn;
  612. push->cur = nvpb->bgn;
  613. push->end = push->cur + (nvpb->bo->size / 4);
  614. push->end -= 2 + push->rsvd_kick; /* space for suffix */
  615. }
  616. pushbuf_kref(push, nvpb->bo, push->flags);
  617. return flushed ? pushbuf_validate(push, false) : 0;
  618. }
  619. drm_public void
  620. nouveau_pushbuf_data(struct nouveau_pushbuf *push, struct nouveau_bo *bo,
  621. uint64_t offset, uint64_t length)
  622. {
  623. struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(push);
  624. struct nouveau_pushbuf_krec *krec = nvpb->krec;
  625. struct drm_nouveau_gem_pushbuf_push *kpsh;
  626. struct drm_nouveau_gem_pushbuf_bo *kref;
  627. if (bo != nvpb->bo && nvpb->bgn != push->cur) {
  628. if (nvpb->suffix0 || nvpb->suffix1) {
  629. *push->cur++ = nvpb->suffix0;
  630. *push->cur++ = nvpb->suffix1;
  631. }
  632. nouveau_pushbuf_data(push, nvpb->bo,
  633. (nvpb->bgn - nvpb->ptr) * 4,
  634. (push->cur - nvpb->bgn) * 4);
  635. nvpb->bgn = push->cur;
  636. }
  637. if (bo) {
  638. kref = cli_kref_get(push->client, bo);
  639. assert(kref);
  640. kpsh = &krec->push[krec->nr_push++];
  641. kpsh->bo_index = kref - krec->buffer;
  642. kpsh->offset = offset;
  643. kpsh->length = length;
  644. }
  645. }
  646. drm_public int
  647. nouveau_pushbuf_refn(struct nouveau_pushbuf *push,
  648. struct nouveau_pushbuf_refn *refs, int nr)
  649. {
  650. return pushbuf_refn(push, true, refs, nr);
  651. }
  652. drm_public void
  653. nouveau_pushbuf_reloc(struct nouveau_pushbuf *push, struct nouveau_bo *bo,
  654. uint32_t data, uint32_t flags, uint32_t vor, uint32_t tor)
  655. {
  656. *push->cur = pushbuf_krel(push, bo, data, flags, vor, tor);
  657. push->cur++;
  658. }
  659. drm_public int
  660. nouveau_pushbuf_validate(struct nouveau_pushbuf *push)
  661. {
  662. return pushbuf_validate(push, true);
  663. }
  664. drm_public uint32_t
  665. nouveau_pushbuf_refd(struct nouveau_pushbuf *push, struct nouveau_bo *bo)
  666. {
  667. struct drm_nouveau_gem_pushbuf_bo *kref;
  668. uint32_t flags = 0;
  669. if (cli_push_get(push->client, bo) == push) {
  670. kref = cli_kref_get(push->client, bo);
  671. assert(kref);
  672. if (kref->read_domains)
  673. flags |= NOUVEAU_BO_RD;
  674. if (kref->write_domains)
  675. flags |= NOUVEAU_BO_WR;
  676. }
  677. return flags;
  678. }
  679. drm_public int
  680. nouveau_pushbuf_kick(struct nouveau_pushbuf *push, struct nouveau_object *chan)
  681. {
  682. if (!push->channel)
  683. return pushbuf_submit(push, chan);
  684. pushbuf_flush(push);
  685. return pushbuf_validate(push, false);
  686. }
  687. drm_public bool
  688. nouveau_check_dead_channel(struct nouveau_drm *drm, struct nouveau_object *chan)
  689. {
  690. struct drm_nouveau_gem_pushbuf req = {};
  691. struct nouveau_fifo *fifo = chan->data;
  692. int ret;
  693. req.channel = fifo->channel;
  694. req.nr_push = 0;
  695. ret = drmCommandWriteRead(drm->fd, DRM_NOUVEAU_GEM_PUSHBUF,
  696. &req, sizeof(req));
  697. /* nouveau returns ENODEV once the channel was killed */
  698. return ret == -ENODEV;
  699. }