| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303 |
- /*
- * Copyright © 2016 Intel Corporation
- *
- * 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 (including the next
- * paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS 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.
- *
- */
- #include <linux/pm_qos.h>
- #include <linux/prime_numbers.h>
- #include <linux/sort.h>
- #include <drm/drm_print.h>
- #include "gem/i915_gem_internal.h"
- #include "gem/i915_gem_pm.h"
- #include "gem/selftests/mock_context.h"
- #include "gt/intel_engine_heartbeat.h"
- #include "gt/intel_engine_pm.h"
- #include "gt/intel_engine_user.h"
- #include "gt/intel_gt.h"
- #include "gt/intel_gt_clock_utils.h"
- #include "gt/intel_gt_requests.h"
- #include "gt/selftest_engine_heartbeat.h"
- #include "i915_random.h"
- #include "i915_selftest.h"
- #include "i915_wait_util.h"
- #include "igt_flush_test.h"
- #include "igt_live_test.h"
- #include "igt_spinner.h"
- #include "lib_sw_fence.h"
- #include "mock_drm.h"
- #include "mock_gem_device.h"
- static unsigned int num_uabi_engines(struct drm_i915_private *i915)
- {
- struct intel_engine_cs *engine;
- unsigned int count;
- count = 0;
- for_each_uabi_engine(engine, i915)
- count++;
- return count;
- }
- static struct intel_engine_cs *rcs0(struct drm_i915_private *i915)
- {
- return intel_engine_lookup_user(i915, I915_ENGINE_CLASS_RENDER, 0);
- }
- static int igt_add_request(void *arg)
- {
- struct drm_i915_private *i915 = arg;
- struct i915_request *request;
- /* Basic preliminary test to create a request and let it loose! */
- request = mock_request(rcs0(i915)->kernel_context, HZ / 10);
- if (IS_ERR(request))
- return PTR_ERR(request);
- i915_request_add(request);
- return 0;
- }
- static int igt_wait_request(void *arg)
- {
- const long T = HZ / 4;
- struct drm_i915_private *i915 = arg;
- struct i915_request *request;
- int err = -EINVAL;
- /* Submit a request, then wait upon it */
- request = mock_request(rcs0(i915)->kernel_context, T);
- if (IS_ERR(request))
- return PTR_ERR(request);
- i915_request_get(request);
- if (i915_request_wait(request, 0, 0) != -ETIME) {
- pr_err("request wait (busy query) succeeded (expected timeout before submit!)\n");
- goto out_request;
- }
- if (i915_request_wait(request, 0, T) != -ETIME) {
- pr_err("request wait succeeded (expected timeout before submit!)\n");
- goto out_request;
- }
- if (i915_request_completed(request)) {
- pr_err("request completed before submit!!\n");
- goto out_request;
- }
- i915_request_add(request);
- if (i915_request_wait(request, 0, 0) != -ETIME) {
- pr_err("request wait (busy query) succeeded (expected timeout after submit!)\n");
- goto out_request;
- }
- if (i915_request_completed(request)) {
- pr_err("request completed immediately!\n");
- goto out_request;
- }
- if (i915_request_wait(request, 0, T / 2) != -ETIME) {
- pr_err("request wait succeeded (expected timeout!)\n");
- goto out_request;
- }
- if (i915_request_wait(request, 0, T) == -ETIME) {
- pr_err("request wait timed out!\n");
- goto out_request;
- }
- if (!i915_request_completed(request)) {
- pr_err("request not complete after waiting!\n");
- goto out_request;
- }
- if (i915_request_wait(request, 0, T) == -ETIME) {
- pr_err("request wait timed out when already complete!\n");
- goto out_request;
- }
- err = 0;
- out_request:
- i915_request_put(request);
- mock_device_flush(i915);
- return err;
- }
- static int igt_fence_wait(void *arg)
- {
- const long T = HZ / 4;
- struct drm_i915_private *i915 = arg;
- struct i915_request *request;
- int err = -EINVAL;
- /* Submit a request, treat it as a fence and wait upon it */
- request = mock_request(rcs0(i915)->kernel_context, T);
- if (IS_ERR(request))
- return PTR_ERR(request);
- if (dma_fence_wait_timeout(&request->fence, false, T) != -ETIME) {
- pr_err("fence wait success before submit (expected timeout)!\n");
- goto out;
- }
- i915_request_add(request);
- if (dma_fence_is_signaled(&request->fence)) {
- pr_err("fence signaled immediately!\n");
- goto out;
- }
- if (dma_fence_wait_timeout(&request->fence, false, T / 2) != -ETIME) {
- pr_err("fence wait success after submit (expected timeout)!\n");
- goto out;
- }
- if (dma_fence_wait_timeout(&request->fence, false, T) <= 0) {
- pr_err("fence wait timed out (expected success)!\n");
- goto out;
- }
- if (!dma_fence_is_signaled(&request->fence)) {
- pr_err("fence unsignaled after waiting!\n");
- goto out;
- }
- if (dma_fence_wait_timeout(&request->fence, false, T) <= 0) {
- pr_err("fence wait timed out when complete (expected success)!\n");
- goto out;
- }
- err = 0;
- out:
- mock_device_flush(i915);
- return err;
- }
- static int igt_request_rewind(void *arg)
- {
- struct drm_i915_private *i915 = arg;
- struct i915_request *request, *vip;
- struct i915_gem_context *ctx[2];
- struct intel_context *ce;
- int err = -EINVAL;
- ctx[0] = mock_context(i915, "A");
- if (!ctx[0]) {
- err = -ENOMEM;
- goto err_ctx_0;
- }
- ce = i915_gem_context_get_engine(ctx[0], RCS0);
- GEM_BUG_ON(IS_ERR(ce));
- request = mock_request(ce, 2 * HZ);
- intel_context_put(ce);
- if (IS_ERR(request)) {
- err = PTR_ERR(request);
- goto err_context_0;
- }
- i915_request_get(request);
- i915_request_add(request);
- ctx[1] = mock_context(i915, "B");
- if (!ctx[1]) {
- err = -ENOMEM;
- goto err_ctx_1;
- }
- ce = i915_gem_context_get_engine(ctx[1], RCS0);
- GEM_BUG_ON(IS_ERR(ce));
- vip = mock_request(ce, 0);
- intel_context_put(ce);
- if (IS_ERR(vip)) {
- err = PTR_ERR(vip);
- goto err_context_1;
- }
- /* Simulate preemption by manual reordering */
- if (!mock_cancel_request(request)) {
- pr_err("failed to cancel request (already executed)!\n");
- i915_request_add(vip);
- goto err_context_1;
- }
- i915_request_get(vip);
- i915_request_add(vip);
- rcu_read_lock();
- request->engine->submit_request(request);
- rcu_read_unlock();
- if (i915_request_wait(vip, 0, HZ) == -ETIME) {
- pr_err("timed out waiting for high priority request\n");
- goto err;
- }
- if (i915_request_completed(request)) {
- pr_err("low priority request already completed\n");
- goto err;
- }
- err = 0;
- err:
- i915_request_put(vip);
- err_context_1:
- mock_context_close(ctx[1]);
- err_ctx_1:
- i915_request_put(request);
- err_context_0:
- mock_context_close(ctx[0]);
- err_ctx_0:
- mock_device_flush(i915);
- return err;
- }
- struct smoketest {
- struct intel_engine_cs *engine;
- struct i915_gem_context **contexts;
- atomic_long_t num_waits, num_fences;
- int ncontexts, max_batch;
- struct i915_request *(*request_alloc)(struct intel_context *ce);
- };
- static struct i915_request *
- __mock_request_alloc(struct intel_context *ce)
- {
- return mock_request(ce, 0);
- }
- static struct i915_request *
- __live_request_alloc(struct intel_context *ce)
- {
- return intel_context_create_request(ce);
- }
- struct smoke_thread {
- struct kthread_worker *worker;
- struct kthread_work work;
- struct smoketest *t;
- bool stop;
- int result;
- };
- static void __igt_breadcrumbs_smoketest(struct kthread_work *work)
- {
- struct smoke_thread *thread = container_of(work, typeof(*thread), work);
- struct smoketest *t = thread->t;
- const unsigned int max_batch = min(t->ncontexts, t->max_batch) - 1;
- const unsigned int total = 4 * t->ncontexts + 1;
- unsigned int num_waits = 0, num_fences = 0;
- struct i915_request **requests;
- I915_RND_STATE(prng);
- unsigned int *order;
- int err = 0;
- /*
- * A very simple test to catch the most egregious of list handling bugs.
- *
- * At its heart, we simply create oodles of requests running across
- * multiple kthreads and enable signaling on them, for the sole purpose
- * of stressing our breadcrumb handling. The only inspection we do is
- * that the fences were marked as signaled.
- */
- requests = kzalloc_objs(*requests, total);
- if (!requests) {
- thread->result = -ENOMEM;
- return;
- }
- order = i915_random_order(total, &prng);
- if (!order) {
- err = -ENOMEM;
- goto out_requests;
- }
- while (!READ_ONCE(thread->stop)) {
- struct i915_sw_fence *submit, *wait;
- unsigned int n, count;
- submit = heap_fence_create(GFP_KERNEL);
- if (!submit) {
- err = -ENOMEM;
- break;
- }
- wait = heap_fence_create(GFP_KERNEL);
- if (!wait) {
- i915_sw_fence_commit(submit);
- heap_fence_put(submit);
- err = -ENOMEM;
- break;
- }
- i915_random_reorder(order, total, &prng);
- count = 1 + i915_prandom_u32_max_state(max_batch, &prng);
- for (n = 0; n < count; n++) {
- struct i915_gem_context *ctx =
- t->contexts[order[n] % t->ncontexts];
- struct i915_request *rq;
- struct intel_context *ce;
- ce = i915_gem_context_get_engine(ctx, t->engine->legacy_idx);
- GEM_BUG_ON(IS_ERR(ce));
- rq = t->request_alloc(ce);
- intel_context_put(ce);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- count = n;
- break;
- }
- err = i915_sw_fence_await_sw_fence_gfp(&rq->submit,
- submit,
- GFP_KERNEL);
- requests[n] = i915_request_get(rq);
- i915_request_add(rq);
- if (err >= 0)
- err = i915_sw_fence_await_dma_fence(wait,
- &rq->fence,
- 0,
- GFP_KERNEL);
- if (err < 0) {
- i915_request_put(rq);
- count = n;
- break;
- }
- }
- i915_sw_fence_commit(submit);
- i915_sw_fence_commit(wait);
- if (!wait_event_timeout(wait->wait,
- i915_sw_fence_done(wait),
- 5 * HZ)) {
- struct i915_request *rq = requests[count - 1];
- pr_err("waiting for %d/%d fences (last %llx:%lld) on %s timed out!\n",
- atomic_read(&wait->pending), count,
- rq->fence.context, rq->fence.seqno,
- t->engine->name);
- GEM_TRACE_DUMP();
- intel_gt_set_wedged(t->engine->gt);
- GEM_BUG_ON(!i915_request_completed(rq));
- i915_sw_fence_wait(wait);
- err = -EIO;
- }
- for (n = 0; n < count; n++) {
- struct i915_request *rq = requests[n];
- if (!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT,
- &rq->fence.flags)) {
- pr_err("%llu:%llu was not signaled!\n",
- rq->fence.context, rq->fence.seqno);
- err = -EINVAL;
- }
- i915_request_put(rq);
- }
- heap_fence_put(wait);
- heap_fence_put(submit);
- if (err < 0)
- break;
- num_fences += count;
- num_waits++;
- cond_resched();
- }
- atomic_long_add(num_fences, &t->num_fences);
- atomic_long_add(num_waits, &t->num_waits);
- kfree(order);
- out_requests:
- kfree(requests);
- thread->result = err;
- }
- static int mock_breadcrumbs_smoketest(void *arg)
- {
- struct drm_i915_private *i915 = arg;
- struct smoketest t = {
- .engine = rcs0(i915),
- .ncontexts = 1024,
- .max_batch = 1024,
- .request_alloc = __mock_request_alloc
- };
- unsigned int ncpus = num_online_cpus();
- struct smoke_thread *threads;
- unsigned int n;
- int ret = 0;
- /*
- * Smoketest our breadcrumb/signal handling for requests across multiple
- * threads. A very simple test to only catch the most egregious of bugs.
- * See __igt_breadcrumbs_smoketest();
- */
- threads = kzalloc_objs(*threads, ncpus);
- if (!threads)
- return -ENOMEM;
- t.contexts = kzalloc_objs(*t.contexts, t.ncontexts);
- if (!t.contexts) {
- ret = -ENOMEM;
- goto out_threads;
- }
- for (n = 0; n < t.ncontexts; n++) {
- t.contexts[n] = mock_context(t.engine->i915, "mock");
- if (!t.contexts[n]) {
- ret = -ENOMEM;
- goto out_contexts;
- }
- }
- for (n = 0; n < ncpus; n++) {
- struct kthread_worker *worker;
- worker = kthread_run_worker(0, "igt/%d", n);
- if (IS_ERR(worker)) {
- ret = PTR_ERR(worker);
- ncpus = n;
- break;
- }
- threads[n].worker = worker;
- threads[n].t = &t;
- threads[n].stop = false;
- threads[n].result = 0;
- kthread_init_work(&threads[n].work,
- __igt_breadcrumbs_smoketest);
- kthread_queue_work(worker, &threads[n].work);
- }
- msleep(jiffies_to_msecs(i915_selftest.timeout_jiffies));
- for (n = 0; n < ncpus; n++) {
- int err;
- WRITE_ONCE(threads[n].stop, true);
- kthread_flush_work(&threads[n].work);
- err = READ_ONCE(threads[n].result);
- if (err < 0 && !ret)
- ret = err;
- kthread_destroy_worker(threads[n].worker);
- }
- pr_info("Completed %lu waits for %lu fence across %d cpus\n",
- atomic_long_read(&t.num_waits),
- atomic_long_read(&t.num_fences),
- ncpus);
- out_contexts:
- for (n = 0; n < t.ncontexts; n++) {
- if (!t.contexts[n])
- break;
- mock_context_close(t.contexts[n]);
- }
- kfree(t.contexts);
- out_threads:
- kfree(threads);
- return ret;
- }
- int i915_request_mock_selftests(void)
- {
- static const struct i915_subtest tests[] = {
- SUBTEST(igt_add_request),
- SUBTEST(igt_wait_request),
- SUBTEST(igt_fence_wait),
- SUBTEST(igt_request_rewind),
- SUBTEST(mock_breadcrumbs_smoketest),
- };
- struct drm_i915_private *i915;
- intel_wakeref_t wakeref;
- int err = 0;
- i915 = mock_gem_device();
- if (!i915)
- return -ENOMEM;
- with_intel_runtime_pm(&i915->runtime_pm, wakeref)
- err = i915_subtests(tests, i915);
- mock_destroy_device(i915);
- return err;
- }
- static int live_nop_request(void *arg)
- {
- struct drm_i915_private *i915 = arg;
- struct intel_engine_cs *engine;
- struct igt_live_test t;
- int err = -ENODEV;
- /*
- * Submit various sized batches of empty requests, to each engine
- * (individually), and wait for the batch to complete. We can check
- * the overhead of submitting requests to the hardware.
- */
- for_each_uabi_engine(engine, i915) {
- unsigned long n, prime;
- IGT_TIMEOUT(end_time);
- ktime_t times[2] = {};
- err = igt_live_test_begin(&t, i915, __func__, engine->name);
- if (err)
- return err;
- intel_engine_pm_get(engine);
- for_each_prime_number_from(prime, 1, 8192) {
- struct i915_request *request = NULL;
- times[1] = ktime_get_raw();
- for (n = 0; n < prime; n++) {
- i915_request_put(request);
- request = i915_request_create(engine->kernel_context);
- if (IS_ERR(request))
- return PTR_ERR(request);
- /*
- * This space is left intentionally blank.
- *
- * We do not actually want to perform any
- * action with this request, we just want
- * to measure the latency in allocation
- * and submission of our breadcrumbs -
- * ensuring that the bare request is sufficient
- * for the system to work (i.e. proper HEAD
- * tracking of the rings, interrupt handling,
- * etc). It also gives us the lowest bounds
- * for latency.
- */
- i915_request_get(request);
- i915_request_add(request);
- }
- i915_request_wait(request, 0, MAX_SCHEDULE_TIMEOUT);
- i915_request_put(request);
- times[1] = ktime_sub(ktime_get_raw(), times[1]);
- if (prime == 1)
- times[0] = times[1];
- if (__igt_timeout(end_time, NULL))
- break;
- }
- intel_engine_pm_put(engine);
- err = igt_live_test_end(&t);
- if (err)
- return err;
- pr_info("Request latencies on %s: 1 = %lluns, %lu = %lluns\n",
- engine->name,
- ktime_to_ns(times[0]),
- prime, div64_u64(ktime_to_ns(times[1]), prime));
- }
- return err;
- }
- static int __cancel_inactive(struct intel_engine_cs *engine)
- {
- struct intel_context *ce;
- struct igt_spinner spin;
- struct i915_request *rq;
- int err = 0;
- if (igt_spinner_init(&spin, engine->gt))
- return -ENOMEM;
- ce = intel_context_create(engine);
- if (IS_ERR(ce)) {
- err = PTR_ERR(ce);
- goto out_spin;
- }
- rq = igt_spinner_create_request(&spin, ce, MI_ARB_CHECK);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- goto out_ce;
- }
- pr_debug("%s: Cancelling inactive request\n", engine->name);
- i915_request_cancel(rq, -EINTR);
- i915_request_get(rq);
- i915_request_add(rq);
- if (i915_request_wait(rq, 0, HZ / 5) < 0) {
- struct drm_printer p = drm_info_printer(engine->i915->drm.dev);
- pr_err("%s: Failed to cancel inactive request\n", engine->name);
- intel_engine_dump(engine, &p, "%s\n", engine->name);
- err = -ETIME;
- goto out_rq;
- }
- if (rq->fence.error != -EINTR) {
- pr_err("%s: fence not cancelled (%u)\n",
- engine->name, rq->fence.error);
- err = -EINVAL;
- }
- out_rq:
- i915_request_put(rq);
- out_ce:
- intel_context_put(ce);
- out_spin:
- igt_spinner_fini(&spin);
- if (err)
- pr_err("%s: %s error %d\n", __func__, engine->name, err);
- return err;
- }
- static int __cancel_active(struct intel_engine_cs *engine)
- {
- struct intel_context *ce;
- struct igt_spinner spin;
- struct i915_request *rq;
- int err = 0;
- if (igt_spinner_init(&spin, engine->gt))
- return -ENOMEM;
- ce = intel_context_create(engine);
- if (IS_ERR(ce)) {
- err = PTR_ERR(ce);
- goto out_spin;
- }
- rq = igt_spinner_create_request(&spin, ce, MI_ARB_CHECK);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- goto out_ce;
- }
- pr_debug("%s: Cancelling active request\n", engine->name);
- i915_request_get(rq);
- i915_request_add(rq);
- if (!igt_wait_for_spinner(&spin, rq)) {
- struct drm_printer p = drm_info_printer(engine->i915->drm.dev);
- pr_err("Failed to start spinner on %s\n", engine->name);
- intel_engine_dump(engine, &p, "%s\n", engine->name);
- err = -ETIME;
- goto out_rq;
- }
- i915_request_cancel(rq, -EINTR);
- if (i915_request_wait(rq, 0, HZ / 5) < 0) {
- struct drm_printer p = drm_info_printer(engine->i915->drm.dev);
- pr_err("%s: Failed to cancel active request\n", engine->name);
- intel_engine_dump(engine, &p, "%s\n", engine->name);
- err = -ETIME;
- goto out_rq;
- }
- if (rq->fence.error != -EINTR) {
- pr_err("%s: fence not cancelled (%u)\n",
- engine->name, rq->fence.error);
- err = -EINVAL;
- }
- out_rq:
- i915_request_put(rq);
- out_ce:
- intel_context_put(ce);
- out_spin:
- igt_spinner_fini(&spin);
- if (err)
- pr_err("%s: %s error %d\n", __func__, engine->name, err);
- return err;
- }
- static int __cancel_completed(struct intel_engine_cs *engine)
- {
- struct intel_context *ce;
- struct igt_spinner spin;
- struct i915_request *rq;
- int err = 0;
- if (igt_spinner_init(&spin, engine->gt))
- return -ENOMEM;
- ce = intel_context_create(engine);
- if (IS_ERR(ce)) {
- err = PTR_ERR(ce);
- goto out_spin;
- }
- rq = igt_spinner_create_request(&spin, ce, MI_ARB_CHECK);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- goto out_ce;
- }
- igt_spinner_end(&spin);
- i915_request_get(rq);
- i915_request_add(rq);
- if (i915_request_wait(rq, 0, HZ / 5) < 0) {
- err = -ETIME;
- goto out_rq;
- }
- pr_debug("%s: Cancelling completed request\n", engine->name);
- i915_request_cancel(rq, -EINTR);
- if (rq->fence.error) {
- pr_err("%s: fence not cancelled (%u)\n",
- engine->name, rq->fence.error);
- err = -EINVAL;
- }
- out_rq:
- i915_request_put(rq);
- out_ce:
- intel_context_put(ce);
- out_spin:
- igt_spinner_fini(&spin);
- if (err)
- pr_err("%s: %s error %d\n", __func__, engine->name, err);
- return err;
- }
- /*
- * Test to prove a non-preemptable request can be cancelled and a subsequent
- * request on the same context can successfully complete after cancellation.
- *
- * Testing methodology is to create a non-preemptible request and submit it,
- * wait for spinner to start, create a NOP request and submit it, cancel the
- * spinner, wait for spinner to complete and verify it failed with an error,
- * finally wait for NOP request to complete verify it succeeded without an
- * error. Preemption timeout also reduced / restored so test runs in a timely
- * maner.
- */
- static int __cancel_reset(struct drm_i915_private *i915,
- struct intel_engine_cs *engine)
- {
- struct intel_context *ce;
- struct igt_spinner spin;
- struct i915_request *rq, *nop;
- unsigned long preempt_timeout_ms;
- int err = 0;
- if (!CONFIG_DRM_I915_PREEMPT_TIMEOUT ||
- !intel_has_reset_engine(engine->gt))
- return 0;
- preempt_timeout_ms = engine->props.preempt_timeout_ms;
- engine->props.preempt_timeout_ms = 100;
- if (igt_spinner_init(&spin, engine->gt))
- goto out_restore;
- ce = intel_context_create(engine);
- if (IS_ERR(ce)) {
- err = PTR_ERR(ce);
- goto out_spin;
- }
- rq = igt_spinner_create_request(&spin, ce, MI_NOOP);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- goto out_ce;
- }
- pr_debug("%s: Cancelling active non-preemptable request\n",
- engine->name);
- i915_request_get(rq);
- i915_request_add(rq);
- if (!igt_wait_for_spinner(&spin, rq)) {
- struct drm_printer p = drm_info_printer(engine->i915->drm.dev);
- pr_err("Failed to start spinner on %s\n", engine->name);
- intel_engine_dump(engine, &p, "%s\n", engine->name);
- err = -ETIME;
- goto out_rq;
- }
- nop = intel_context_create_request(ce);
- if (IS_ERR(nop))
- goto out_rq;
- i915_request_get(nop);
- i915_request_add(nop);
- i915_request_cancel(rq, -EINTR);
- if (i915_request_wait(rq, 0, HZ) < 0) {
- struct drm_printer p = drm_info_printer(engine->i915->drm.dev);
- pr_err("%s: Failed to cancel hung request\n", engine->name);
- intel_engine_dump(engine, &p, "%s\n", engine->name);
- err = -ETIME;
- goto out_nop;
- }
- if (rq->fence.error != -EINTR) {
- pr_err("%s: fence not cancelled (%u)\n",
- engine->name, rq->fence.error);
- err = -EINVAL;
- goto out_nop;
- }
- if (i915_request_wait(nop, 0, HZ) < 0) {
- struct drm_printer p = drm_info_printer(engine->i915->drm.dev);
- pr_err("%s: Failed to complete nop request\n", engine->name);
- intel_engine_dump(engine, &p, "%s\n", engine->name);
- err = -ETIME;
- goto out_nop;
- }
- if (nop->fence.error != 0) {
- pr_err("%s: Nop request errored (%u)\n",
- engine->name, nop->fence.error);
- err = -EINVAL;
- }
- out_nop:
- i915_request_put(nop);
- out_rq:
- i915_request_put(rq);
- out_ce:
- intel_context_put(ce);
- out_spin:
- igt_spinner_fini(&spin);
- out_restore:
- engine->props.preempt_timeout_ms = preempt_timeout_ms;
- if (err)
- pr_err("%s: %s error %d\n", __func__, engine->name, err);
- return err;
- }
- static int live_cancel_request(void *arg)
- {
- struct drm_i915_private *i915 = arg;
- struct intel_engine_cs *engine;
- /*
- * Check cancellation of requests. We expect to be able to immediately
- * cancel active requests, even if they are currently on the GPU.
- */
- for_each_uabi_engine(engine, i915) {
- struct igt_live_test t;
- int err, err2;
- if (!intel_engine_has_preemption(engine))
- continue;
- err = igt_live_test_begin(&t, i915, __func__, engine->name);
- if (err)
- return err;
- err = __cancel_inactive(engine);
- if (err == 0)
- err = __cancel_active(engine);
- if (err == 0)
- err = __cancel_completed(engine);
- err2 = igt_live_test_end(&t);
- if (err)
- return err;
- if (err2)
- return err2;
- /* Expects reset so call outside of igt_live_test_* */
- err = __cancel_reset(i915, engine);
- if (err)
- return err;
- if (igt_flush_test(i915))
- return -EIO;
- }
- return 0;
- }
- static struct i915_vma *empty_batch(struct intel_gt *gt)
- {
- struct drm_i915_gem_object *obj;
- struct i915_vma *vma;
- u32 *cmd;
- int err;
- obj = i915_gem_object_create_internal(gt->i915, PAGE_SIZE);
- if (IS_ERR(obj))
- return ERR_CAST(obj);
- cmd = i915_gem_object_pin_map_unlocked(obj, I915_MAP_WC);
- if (IS_ERR(cmd)) {
- err = PTR_ERR(cmd);
- goto err;
- }
- *cmd = MI_BATCH_BUFFER_END;
- __i915_gem_object_flush_map(obj, 0, 64);
- i915_gem_object_unpin_map(obj);
- intel_gt_chipset_flush(gt);
- vma = i915_vma_instance(obj, gt->vm, NULL);
- if (IS_ERR(vma)) {
- err = PTR_ERR(vma);
- goto err;
- }
- err = i915_vma_pin(vma, 0, 0, PIN_USER);
- if (err)
- goto err;
- /* Force the wait now to avoid including it in the benchmark */
- err = i915_vma_sync(vma);
- if (err)
- goto err_pin;
- return vma;
- err_pin:
- i915_vma_unpin(vma);
- err:
- i915_gem_object_put(obj);
- return ERR_PTR(err);
- }
- static int emit_bb_start(struct i915_request *rq, struct i915_vma *batch)
- {
- return rq->engine->emit_bb_start(rq,
- i915_vma_offset(batch),
- i915_vma_size(batch),
- 0);
- }
- static struct i915_request *
- empty_request(struct intel_engine_cs *engine,
- struct i915_vma *batch)
- {
- struct i915_request *request;
- int err;
- request = i915_request_create(engine->kernel_context);
- if (IS_ERR(request))
- return request;
- err = emit_bb_start(request, batch);
- if (err)
- goto out_request;
- i915_request_get(request);
- out_request:
- i915_request_add(request);
- return err ? ERR_PTR(err) : request;
- }
- static int live_empty_request(void *arg)
- {
- struct drm_i915_private *i915 = arg;
- struct intel_engine_cs *engine;
- struct igt_live_test t;
- int err;
- /*
- * Submit various sized batches of empty requests, to each engine
- * (individually), and wait for the batch to complete. We can check
- * the overhead of submitting requests to the hardware.
- */
- for_each_uabi_engine(engine, i915) {
- IGT_TIMEOUT(end_time);
- struct i915_request *request;
- struct i915_vma *batch;
- unsigned long n, prime;
- ktime_t times[2] = {};
- batch = empty_batch(engine->gt);
- if (IS_ERR(batch))
- return PTR_ERR(batch);
- err = igt_live_test_begin(&t, i915, __func__, engine->name);
- if (err)
- goto out_batch;
- intel_engine_pm_get(engine);
- /* Warmup / preload */
- request = empty_request(engine, batch);
- if (IS_ERR(request)) {
- err = PTR_ERR(request);
- intel_engine_pm_put(engine);
- goto out_batch;
- }
- i915_request_wait(request, 0, MAX_SCHEDULE_TIMEOUT);
- for_each_prime_number_from(prime, 1, 8192) {
- times[1] = ktime_get_raw();
- for (n = 0; n < prime; n++) {
- i915_request_put(request);
- request = empty_request(engine, batch);
- if (IS_ERR(request)) {
- err = PTR_ERR(request);
- intel_engine_pm_put(engine);
- goto out_batch;
- }
- }
- i915_request_wait(request, 0, MAX_SCHEDULE_TIMEOUT);
- times[1] = ktime_sub(ktime_get_raw(), times[1]);
- if (prime == 1)
- times[0] = times[1];
- if (__igt_timeout(end_time, NULL))
- break;
- }
- i915_request_put(request);
- intel_engine_pm_put(engine);
- err = igt_live_test_end(&t);
- if (err)
- goto out_batch;
- pr_info("Batch latencies on %s: 1 = %lluns, %lu = %lluns\n",
- engine->name,
- ktime_to_ns(times[0]),
- prime, div64_u64(ktime_to_ns(times[1]), prime));
- out_batch:
- i915_vma_unpin(batch);
- i915_vma_put(batch);
- if (err)
- break;
- }
- return err;
- }
- static struct i915_vma *recursive_batch(struct intel_gt *gt)
- {
- struct drm_i915_gem_object *obj;
- const int ver = GRAPHICS_VER(gt->i915);
- struct i915_vma *vma;
- u32 *cmd;
- int err;
- obj = i915_gem_object_create_internal(gt->i915, PAGE_SIZE);
- if (IS_ERR(obj))
- return ERR_CAST(obj);
- vma = i915_vma_instance(obj, gt->vm, NULL);
- if (IS_ERR(vma)) {
- err = PTR_ERR(vma);
- goto err;
- }
- err = i915_vma_pin(vma, 0, 0, PIN_USER);
- if (err)
- goto err;
- cmd = i915_gem_object_pin_map_unlocked(obj, I915_MAP_WC);
- if (IS_ERR(cmd)) {
- err = PTR_ERR(cmd);
- goto err;
- }
- if (ver >= 8) {
- *cmd++ = MI_BATCH_BUFFER_START | 1 << 8 | 1;
- *cmd++ = lower_32_bits(i915_vma_offset(vma));
- *cmd++ = upper_32_bits(i915_vma_offset(vma));
- } else if (ver >= 6) {
- *cmd++ = MI_BATCH_BUFFER_START | 1 << 8;
- *cmd++ = lower_32_bits(i915_vma_offset(vma));
- } else {
- *cmd++ = MI_BATCH_BUFFER_START | MI_BATCH_GTT;
- *cmd++ = lower_32_bits(i915_vma_offset(vma));
- }
- *cmd++ = MI_BATCH_BUFFER_END; /* terminate early in case of error */
- __i915_gem_object_flush_map(obj, 0, 64);
- i915_gem_object_unpin_map(obj);
- intel_gt_chipset_flush(gt);
- return vma;
- err:
- i915_gem_object_put(obj);
- return ERR_PTR(err);
- }
- static int recursive_batch_resolve(struct i915_vma *batch)
- {
- u32 *cmd;
- cmd = i915_gem_object_pin_map_unlocked(batch->obj, I915_MAP_WC);
- if (IS_ERR(cmd))
- return PTR_ERR(cmd);
- *cmd = MI_BATCH_BUFFER_END;
- __i915_gem_object_flush_map(batch->obj, 0, sizeof(*cmd));
- i915_gem_object_unpin_map(batch->obj);
- intel_gt_chipset_flush(batch->vm->gt);
- return 0;
- }
- static int live_all_engines(void *arg)
- {
- struct drm_i915_private *i915 = arg;
- const unsigned int nengines = num_uabi_engines(i915);
- struct intel_engine_cs *engine;
- struct i915_request **request;
- struct igt_live_test t;
- unsigned int idx;
- int err;
- /*
- * Check we can submit requests to all engines simultaneously. We
- * send a recursive batch to each engine - checking that we don't
- * block doing so, and that they don't complete too soon.
- */
- request = kzalloc_objs(*request, nengines);
- if (!request)
- return -ENOMEM;
- err = igt_live_test_begin(&t, i915, __func__, "");
- if (err)
- goto out_free;
- idx = 0;
- for_each_uabi_engine(engine, i915) {
- struct i915_vma *batch;
- batch = recursive_batch(engine->gt);
- if (IS_ERR(batch)) {
- err = PTR_ERR(batch);
- pr_err("%s: Unable to create batch, err=%d\n",
- __func__, err);
- goto out_free;
- }
- i915_vma_lock(batch);
- request[idx] = intel_engine_create_kernel_request(engine);
- if (IS_ERR(request[idx])) {
- err = PTR_ERR(request[idx]);
- pr_err("%s: Request allocation failed with err=%d\n",
- __func__, err);
- goto out_unlock;
- }
- GEM_BUG_ON(request[idx]->context->vm != batch->vm);
- err = i915_vma_move_to_active(batch, request[idx], 0);
- GEM_BUG_ON(err);
- err = emit_bb_start(request[idx], batch);
- GEM_BUG_ON(err);
- request[idx]->batch = batch;
- i915_request_get(request[idx]);
- i915_request_add(request[idx]);
- idx++;
- out_unlock:
- i915_vma_unlock(batch);
- if (err)
- goto out_request;
- }
- idx = 0;
- for_each_uabi_engine(engine, i915) {
- if (i915_request_completed(request[idx])) {
- pr_err("%s(%s): request completed too early!\n",
- __func__, engine->name);
- err = -EINVAL;
- goto out_request;
- }
- idx++;
- }
- idx = 0;
- for_each_uabi_engine(engine, i915) {
- err = recursive_batch_resolve(request[idx]->batch);
- if (err) {
- pr_err("%s: failed to resolve batch, err=%d\n",
- __func__, err);
- goto out_request;
- }
- idx++;
- }
- idx = 0;
- for_each_uabi_engine(engine, i915) {
- struct i915_request *rq = request[idx];
- long timeout;
- timeout = i915_request_wait(rq, 0,
- MAX_SCHEDULE_TIMEOUT);
- if (timeout < 0) {
- err = timeout;
- pr_err("%s: error waiting for request on %s, err=%d\n",
- __func__, engine->name, err);
- goto out_request;
- }
- GEM_BUG_ON(!i915_request_completed(rq));
- i915_vma_unpin(rq->batch);
- i915_vma_put(rq->batch);
- i915_request_put(rq);
- request[idx] = NULL;
- idx++;
- }
- err = igt_live_test_end(&t);
- out_request:
- idx = 0;
- for_each_uabi_engine(engine, i915) {
- struct i915_request *rq = request[idx];
- if (!rq)
- continue;
- if (rq->batch) {
- i915_vma_unpin(rq->batch);
- i915_vma_put(rq->batch);
- }
- i915_request_put(rq);
- idx++;
- }
- out_free:
- kfree(request);
- return err;
- }
- static int live_sequential_engines(void *arg)
- {
- struct drm_i915_private *i915 = arg;
- const unsigned int nengines = num_uabi_engines(i915);
- struct i915_request **request;
- struct i915_request *prev = NULL;
- struct intel_engine_cs *engine;
- struct igt_live_test t;
- unsigned int idx;
- int err;
- /*
- * Check we can submit requests to all engines sequentially, such
- * that each successive request waits for the earlier ones. This
- * tests that we don't execute requests out of order, even though
- * they are running on independent engines.
- */
- request = kzalloc_objs(*request, nengines);
- if (!request)
- return -ENOMEM;
- err = igt_live_test_begin(&t, i915, __func__, "");
- if (err)
- goto out_free;
- idx = 0;
- for_each_uabi_engine(engine, i915) {
- struct i915_vma *batch;
- batch = recursive_batch(engine->gt);
- if (IS_ERR(batch)) {
- err = PTR_ERR(batch);
- pr_err("%s: Unable to create batch for %s, err=%d\n",
- __func__, engine->name, err);
- goto out_free;
- }
- i915_vma_lock(batch);
- request[idx] = intel_engine_create_kernel_request(engine);
- if (IS_ERR(request[idx])) {
- err = PTR_ERR(request[idx]);
- pr_err("%s: Request allocation failed for %s with err=%d\n",
- __func__, engine->name, err);
- goto out_unlock;
- }
- GEM_BUG_ON(request[idx]->context->vm != batch->vm);
- if (prev) {
- err = i915_request_await_dma_fence(request[idx],
- &prev->fence);
- if (err) {
- i915_request_add(request[idx]);
- pr_err("%s: Request await failed for %s with err=%d\n",
- __func__, engine->name, err);
- goto out_unlock;
- }
- }
- err = i915_vma_move_to_active(batch, request[idx], 0);
- GEM_BUG_ON(err);
- err = emit_bb_start(request[idx], batch);
- GEM_BUG_ON(err);
- request[idx]->batch = batch;
- i915_request_get(request[idx]);
- i915_request_add(request[idx]);
- prev = request[idx];
- idx++;
- out_unlock:
- i915_vma_unlock(batch);
- if (err)
- goto out_request;
- }
- idx = 0;
- for_each_uabi_engine(engine, i915) {
- long timeout;
- if (i915_request_completed(request[idx])) {
- pr_err("%s(%s): request completed too early!\n",
- __func__, engine->name);
- err = -EINVAL;
- goto out_request;
- }
- err = recursive_batch_resolve(request[idx]->batch);
- if (err) {
- pr_err("%s: failed to resolve batch, err=%d\n",
- __func__, err);
- goto out_request;
- }
- timeout = i915_request_wait(request[idx], 0,
- MAX_SCHEDULE_TIMEOUT);
- if (timeout < 0) {
- err = timeout;
- pr_err("%s: error waiting for request on %s, err=%d\n",
- __func__, engine->name, err);
- goto out_request;
- }
- GEM_BUG_ON(!i915_request_completed(request[idx]));
- idx++;
- }
- err = igt_live_test_end(&t);
- out_request:
- idx = 0;
- for_each_uabi_engine(engine, i915) {
- u32 *cmd;
- if (!request[idx])
- break;
- cmd = i915_gem_object_pin_map_unlocked(request[idx]->batch->obj,
- I915_MAP_WC);
- if (!IS_ERR(cmd)) {
- *cmd = MI_BATCH_BUFFER_END;
- __i915_gem_object_flush_map(request[idx]->batch->obj,
- 0, sizeof(*cmd));
- i915_gem_object_unpin_map(request[idx]->batch->obj);
- intel_gt_chipset_flush(engine->gt);
- }
- i915_vma_put(request[idx]->batch);
- i915_request_put(request[idx]);
- idx++;
- }
- out_free:
- kfree(request);
- return err;
- }
- struct parallel_thread {
- struct kthread_worker *worker;
- struct kthread_work work;
- struct intel_engine_cs *engine;
- int result;
- };
- static void __live_parallel_engine1(struct kthread_work *work)
- {
- struct parallel_thread *thread =
- container_of(work, typeof(*thread), work);
- struct intel_engine_cs *engine = thread->engine;
- IGT_TIMEOUT(end_time);
- unsigned long count;
- int err = 0;
- count = 0;
- intel_engine_pm_get(engine);
- do {
- struct i915_request *rq;
- rq = i915_request_create(engine->kernel_context);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- break;
- }
- i915_request_get(rq);
- i915_request_add(rq);
- err = 0;
- if (i915_request_wait(rq, 0, HZ) < 0)
- err = -ETIME;
- i915_request_put(rq);
- if (err)
- break;
- count++;
- } while (!__igt_timeout(end_time, NULL));
- intel_engine_pm_put(engine);
- pr_info("%s: %lu request + sync\n", engine->name, count);
- thread->result = err;
- }
- static void __live_parallel_engineN(struct kthread_work *work)
- {
- struct parallel_thread *thread =
- container_of(work, typeof(*thread), work);
- struct intel_engine_cs *engine = thread->engine;
- IGT_TIMEOUT(end_time);
- unsigned long count;
- int err = 0;
- count = 0;
- intel_engine_pm_get(engine);
- do {
- struct i915_request *rq;
- rq = i915_request_create(engine->kernel_context);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- break;
- }
- i915_request_add(rq);
- count++;
- } while (!__igt_timeout(end_time, NULL));
- intel_engine_pm_put(engine);
- pr_info("%s: %lu requests\n", engine->name, count);
- thread->result = err;
- }
- static bool wake_all(struct drm_i915_private *i915)
- {
- if (atomic_dec_and_test(&i915->selftest.counter)) {
- wake_up_var(&i915->selftest.counter);
- return true;
- }
- return false;
- }
- static int wait_for_all(struct drm_i915_private *i915)
- {
- if (wake_all(i915))
- return 0;
- if (wait_var_event_timeout(&i915->selftest.counter,
- !atomic_read(&i915->selftest.counter),
- i915_selftest.timeout_jiffies))
- return 0;
- return -ETIME;
- }
- static void __live_parallel_spin(struct kthread_work *work)
- {
- struct parallel_thread *thread =
- container_of(work, typeof(*thread), work);
- struct intel_engine_cs *engine = thread->engine;
- struct igt_spinner spin;
- struct i915_request *rq;
- int err = 0;
- /*
- * Create a spinner running for eternity on each engine. If a second
- * spinner is incorrectly placed on the same engine, it will not be
- * able to start in time.
- */
- if (igt_spinner_init(&spin, engine->gt)) {
- wake_all(engine->i915);
- thread->result = -ENOMEM;
- return;
- }
- intel_engine_pm_get(engine);
- rq = igt_spinner_create_request(&spin,
- engine->kernel_context,
- MI_NOOP); /* no preemption */
- intel_engine_pm_put(engine);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- if (err == -ENODEV)
- err = 0;
- wake_all(engine->i915);
- goto out_spin;
- }
- i915_request_get(rq);
- i915_request_add(rq);
- if (igt_wait_for_spinner(&spin, rq)) {
- /* Occupy this engine for the whole test */
- err = wait_for_all(engine->i915);
- } else {
- pr_err("Failed to start spinner on %s\n", engine->name);
- err = -EINVAL;
- }
- igt_spinner_end(&spin);
- if (err == 0 && i915_request_wait(rq, 0, HZ) < 0)
- err = -EIO;
- i915_request_put(rq);
- out_spin:
- igt_spinner_fini(&spin);
- thread->result = err;
- }
- static int live_parallel_engines(void *arg)
- {
- struct drm_i915_private *i915 = arg;
- static void (* const func[])(struct kthread_work *) = {
- __live_parallel_engine1,
- __live_parallel_engineN,
- __live_parallel_spin,
- NULL,
- };
- const unsigned int nengines = num_uabi_engines(i915);
- struct parallel_thread *threads;
- struct intel_engine_cs *engine;
- void (* const *fn)(struct kthread_work *);
- int err = 0;
- /*
- * Check we can submit requests to all engines concurrently. This
- * tests that we load up the system maximally.
- */
- threads = kzalloc_objs(*threads, nengines);
- if (!threads)
- return -ENOMEM;
- for (fn = func; !err && *fn; fn++) {
- char name[KSYM_NAME_LEN];
- struct igt_live_test t;
- unsigned int idx;
- snprintf(name, sizeof(name), "%ps", *fn);
- err = igt_live_test_begin(&t, i915, __func__, name);
- if (err)
- break;
- atomic_set(&i915->selftest.counter, nengines);
- idx = 0;
- for_each_uabi_engine(engine, i915) {
- struct kthread_worker *worker;
- worker = kthread_run_worker(0, "igt/parallel:%s",
- engine->name);
- if (IS_ERR(worker)) {
- err = PTR_ERR(worker);
- break;
- }
- threads[idx].worker = worker;
- threads[idx].result = 0;
- threads[idx].engine = engine;
- kthread_init_work(&threads[idx].work, *fn);
- kthread_queue_work(worker, &threads[idx].work);
- idx++;
- }
- idx = 0;
- for_each_uabi_engine(engine, i915) {
- int status;
- if (!threads[idx].worker)
- break;
- kthread_flush_work(&threads[idx].work);
- status = READ_ONCE(threads[idx].result);
- if (status && !err)
- err = status;
- kthread_destroy_worker(threads[idx++].worker);
- }
- if (igt_live_test_end(&t))
- err = -EIO;
- }
- kfree(threads);
- return err;
- }
- static int
- max_batches(struct i915_gem_context *ctx, struct intel_engine_cs *engine)
- {
- struct i915_request *rq;
- int ret;
- /*
- * Before execlists, all contexts share the same ringbuffer. With
- * execlists, each context/engine has a separate ringbuffer and
- * for the purposes of this test, inexhaustible.
- *
- * For the global ringbuffer though, we have to be very careful
- * that we do not wrap while preventing the execution of requests
- * with a unsignaled fence.
- */
- if (HAS_EXECLISTS(ctx->i915))
- return INT_MAX;
- rq = igt_request_alloc(ctx, engine);
- if (IS_ERR(rq)) {
- ret = PTR_ERR(rq);
- } else {
- int sz;
- ret = rq->ring->size - rq->reserved_space;
- i915_request_add(rq);
- sz = rq->ring->emit - rq->head;
- if (sz < 0)
- sz += rq->ring->size;
- ret /= sz;
- ret /= 2; /* leave half spare, in case of emergency! */
- }
- return ret;
- }
- static int live_breadcrumbs_smoketest(void *arg)
- {
- struct drm_i915_private *i915 = arg;
- const unsigned int nengines = num_uabi_engines(i915);
- const unsigned int ncpus = /* saturate with nengines * ncpus */
- max_t(int, 2, DIV_ROUND_UP(num_online_cpus(), nengines));
- unsigned long num_waits, num_fences;
- struct intel_engine_cs *engine;
- struct smoke_thread *threads;
- struct igt_live_test live;
- intel_wakeref_t wakeref;
- struct smoketest *smoke;
- unsigned int n, idx;
- struct file *file;
- int ret = 0;
- /*
- * Smoketest our breadcrumb/signal handling for requests across multiple
- * threads. A very simple test to only catch the most egregious of bugs.
- * See __igt_breadcrumbs_smoketest();
- *
- * On real hardware this time.
- */
- wakeref = intel_runtime_pm_get(&i915->runtime_pm);
- file = mock_file(i915);
- if (IS_ERR(file)) {
- ret = PTR_ERR(file);
- goto out_rpm;
- }
- smoke = kzalloc_objs(*smoke, nengines);
- if (!smoke) {
- ret = -ENOMEM;
- goto out_file;
- }
- threads = kzalloc_objs(*threads, ncpus * nengines);
- if (!threads) {
- ret = -ENOMEM;
- goto out_smoke;
- }
- smoke[0].request_alloc = __live_request_alloc;
- smoke[0].ncontexts = 64;
- smoke[0].contexts = kzalloc_objs(*smoke[0].contexts, smoke[0].ncontexts);
- if (!smoke[0].contexts) {
- ret = -ENOMEM;
- goto out_threads;
- }
- for (n = 0; n < smoke[0].ncontexts; n++) {
- smoke[0].contexts[n] = live_context(i915, file);
- if (IS_ERR(smoke[0].contexts[n])) {
- ret = PTR_ERR(smoke[0].contexts[n]);
- goto out_contexts;
- }
- }
- ret = igt_live_test_begin(&live, i915, __func__, "");
- if (ret)
- goto out_contexts;
- idx = 0;
- for_each_uabi_engine(engine, i915) {
- smoke[idx] = smoke[0];
- smoke[idx].engine = engine;
- smoke[idx].max_batch =
- max_batches(smoke[0].contexts[0], engine);
- if (smoke[idx].max_batch < 0) {
- ret = smoke[idx].max_batch;
- goto out_flush;
- }
- /* One ring interleaved between requests from all cpus */
- smoke[idx].max_batch /= ncpus + 1;
- pr_debug("Limiting batches to %d requests on %s\n",
- smoke[idx].max_batch, engine->name);
- for (n = 0; n < ncpus; n++) {
- unsigned int i = idx * ncpus + n;
- struct kthread_worker *worker;
- worker = kthread_run_worker(0, "igt/%d.%d", idx, n);
- if (IS_ERR(worker)) {
- ret = PTR_ERR(worker);
- goto out_flush;
- }
- threads[i].worker = worker;
- threads[i].t = &smoke[idx];
- kthread_init_work(&threads[i].work,
- __igt_breadcrumbs_smoketest);
- kthread_queue_work(worker, &threads[i].work);
- }
- idx++;
- }
- msleep(jiffies_to_msecs(i915_selftest.timeout_jiffies));
- out_flush:
- idx = 0;
- num_waits = 0;
- num_fences = 0;
- for_each_uabi_engine(engine, i915) {
- for (n = 0; n < ncpus; n++) {
- unsigned int i = idx * ncpus + n;
- int err;
- if (!threads[i].worker)
- continue;
- WRITE_ONCE(threads[i].stop, true);
- kthread_flush_work(&threads[i].work);
- err = READ_ONCE(threads[i].result);
- if (err < 0 && !ret)
- ret = err;
- kthread_destroy_worker(threads[i].worker);
- }
- num_waits += atomic_long_read(&smoke[idx].num_waits);
- num_fences += atomic_long_read(&smoke[idx].num_fences);
- idx++;
- }
- pr_info("Completed %lu waits for %lu fences across %d engines and %d cpus\n",
- num_waits, num_fences, idx, ncpus);
- ret = igt_live_test_end(&live) ?: ret;
- out_contexts:
- kfree(smoke[0].contexts);
- out_threads:
- kfree(threads);
- out_smoke:
- kfree(smoke);
- out_file:
- fput(file);
- out_rpm:
- intel_runtime_pm_put(&i915->runtime_pm, wakeref);
- return ret;
- }
- int i915_request_live_selftests(struct drm_i915_private *i915)
- {
- static const struct i915_subtest tests[] = {
- SUBTEST(live_nop_request),
- SUBTEST(live_all_engines),
- SUBTEST(live_sequential_engines),
- SUBTEST(live_parallel_engines),
- SUBTEST(live_empty_request),
- SUBTEST(live_cancel_request),
- SUBTEST(live_breadcrumbs_smoketest),
- };
- if (intel_gt_is_wedged(to_gt(i915)))
- return 0;
- return i915_live_subtests(tests, i915);
- }
- static int switch_to_kernel_sync(struct intel_context *ce, int err)
- {
- struct i915_request *rq;
- struct dma_fence *fence;
- rq = intel_engine_create_kernel_request(ce->engine);
- if (IS_ERR(rq))
- return PTR_ERR(rq);
- fence = i915_active_fence_get(&ce->timeline->last_request);
- if (fence) {
- i915_request_await_dma_fence(rq, fence);
- dma_fence_put(fence);
- }
- rq = i915_request_get(rq);
- i915_request_add(rq);
- if (i915_request_wait(rq, 0, HZ / 2) < 0 && !err)
- err = -ETIME;
- i915_request_put(rq);
- while (!err && !intel_engine_is_idle(ce->engine))
- intel_engine_flush_submission(ce->engine);
- return err;
- }
- struct perf_stats {
- struct intel_engine_cs *engine;
- unsigned long count;
- ktime_t time;
- ktime_t busy;
- u64 runtime;
- };
- struct perf_series {
- struct drm_i915_private *i915;
- unsigned int nengines;
- struct intel_context *ce[] __counted_by(nengines);
- };
- static int cmp_u32(const void *A, const void *B)
- {
- const u32 *a = A, *b = B;
- return *a - *b;
- }
- static u32 trifilter(u32 *a)
- {
- u64 sum;
- #define TF_COUNT 5
- sort(a, TF_COUNT, sizeof(*a), cmp_u32, NULL);
- sum = mul_u32_u32(a[2], 2);
- sum += a[1];
- sum += a[3];
- GEM_BUG_ON(sum > U32_MAX);
- return sum;
- #define TF_BIAS 2
- }
- static u64 cycles_to_ns(struct intel_engine_cs *engine, u32 cycles)
- {
- u64 ns = intel_gt_clock_interval_to_ns(engine->gt, cycles);
- return DIV_ROUND_CLOSEST(ns, 1 << TF_BIAS);
- }
- static u32 *emit_timestamp_store(u32 *cs, struct intel_context *ce, u32 offset)
- {
- *cs++ = MI_STORE_REGISTER_MEM_GEN8 | MI_USE_GGTT;
- *cs++ = i915_mmio_reg_offset(RING_TIMESTAMP((ce->engine->mmio_base)));
- *cs++ = offset;
- *cs++ = 0;
- return cs;
- }
- static u32 *emit_store_dw(u32 *cs, u32 offset, u32 value)
- {
- *cs++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT;
- *cs++ = offset;
- *cs++ = 0;
- *cs++ = value;
- return cs;
- }
- static u32 *emit_semaphore_poll(u32 *cs, u32 mode, u32 value, u32 offset)
- {
- *cs++ = MI_SEMAPHORE_WAIT |
- MI_SEMAPHORE_GLOBAL_GTT |
- MI_SEMAPHORE_POLL |
- mode;
- *cs++ = value;
- *cs++ = offset;
- *cs++ = 0;
- return cs;
- }
- static u32 *emit_semaphore_poll_until(u32 *cs, u32 offset, u32 value)
- {
- return emit_semaphore_poll(cs, MI_SEMAPHORE_SAD_EQ_SDD, value, offset);
- }
- static void semaphore_set(u32 *sema, u32 value)
- {
- WRITE_ONCE(*sema, value);
- wmb(); /* flush the update to the cache, and beyond */
- }
- static u32 *hwsp_scratch(const struct intel_context *ce)
- {
- return memset32(ce->engine->status_page.addr + 1000, 0, 21);
- }
- static u32 hwsp_offset(const struct intel_context *ce, u32 *dw)
- {
- return (i915_ggtt_offset(ce->engine->status_page.vma) +
- offset_in_page(dw));
- }
- static int measure_semaphore_response(struct intel_context *ce)
- {
- u32 *sema = hwsp_scratch(ce);
- const u32 offset = hwsp_offset(ce, sema);
- u32 elapsed[TF_COUNT], cycles;
- struct i915_request *rq;
- u32 *cs;
- int err;
- int i;
- /*
- * Measure how many cycles it takes for the HW to detect the change
- * in a semaphore value.
- *
- * A: read CS_TIMESTAMP from CPU
- * poke semaphore
- * B: read CS_TIMESTAMP on GPU
- *
- * Semaphore latency: B - A
- */
- semaphore_set(sema, -1);
- rq = i915_request_create(ce);
- if (IS_ERR(rq))
- return PTR_ERR(rq);
- cs = intel_ring_begin(rq, 4 + 12 * ARRAY_SIZE(elapsed));
- if (IS_ERR(cs)) {
- i915_request_add(rq);
- err = PTR_ERR(cs);
- goto err;
- }
- cs = emit_store_dw(cs, offset, 0);
- for (i = 1; i <= ARRAY_SIZE(elapsed); i++) {
- cs = emit_semaphore_poll_until(cs, offset, i);
- cs = emit_timestamp_store(cs, ce, offset + i * sizeof(u32));
- cs = emit_store_dw(cs, offset, 0);
- }
- intel_ring_advance(rq, cs);
- i915_request_add(rq);
- if (wait_for(READ_ONCE(*sema) == 0, 50)) {
- err = -EIO;
- goto err;
- }
- for (i = 1; i <= ARRAY_SIZE(elapsed); i++) {
- preempt_disable();
- cycles = ENGINE_READ_FW(ce->engine, RING_TIMESTAMP);
- semaphore_set(sema, i);
- preempt_enable();
- if (wait_for(READ_ONCE(*sema) == 0, 50)) {
- err = -EIO;
- goto err;
- }
- elapsed[i - 1] = sema[i] - cycles;
- }
- cycles = trifilter(elapsed);
- pr_info("%s: semaphore response %d cycles, %lluns\n",
- ce->engine->name, cycles >> TF_BIAS,
- cycles_to_ns(ce->engine, cycles));
- return intel_gt_wait_for_idle(ce->engine->gt, HZ);
- err:
- intel_gt_set_wedged(ce->engine->gt);
- return err;
- }
- static int measure_idle_dispatch(struct intel_context *ce)
- {
- u32 *sema = hwsp_scratch(ce);
- const u32 offset = hwsp_offset(ce, sema);
- u32 elapsed[TF_COUNT], cycles;
- u32 *cs;
- int err;
- int i;
- /*
- * Measure how long it takes for us to submit a request while the
- * engine is idle, but is resting in our context.
- *
- * A: read CS_TIMESTAMP from CPU
- * submit request
- * B: read CS_TIMESTAMP on GPU
- *
- * Submission latency: B - A
- */
- for (i = 0; i < ARRAY_SIZE(elapsed); i++) {
- struct i915_request *rq;
- err = intel_gt_wait_for_idle(ce->engine->gt, HZ / 2);
- if (err)
- return err;
- rq = i915_request_create(ce);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- goto err;
- }
- cs = intel_ring_begin(rq, 4);
- if (IS_ERR(cs)) {
- i915_request_add(rq);
- err = PTR_ERR(cs);
- goto err;
- }
- cs = emit_timestamp_store(cs, ce, offset + i * sizeof(u32));
- intel_ring_advance(rq, cs);
- preempt_disable();
- local_bh_disable();
- elapsed[i] = ENGINE_READ_FW(ce->engine, RING_TIMESTAMP);
- i915_request_add(rq);
- local_bh_enable();
- preempt_enable();
- }
- err = intel_gt_wait_for_idle(ce->engine->gt, HZ / 2);
- if (err)
- goto err;
- for (i = 0; i < ARRAY_SIZE(elapsed); i++)
- elapsed[i] = sema[i] - elapsed[i];
- cycles = trifilter(elapsed);
- pr_info("%s: idle dispatch latency %d cycles, %lluns\n",
- ce->engine->name, cycles >> TF_BIAS,
- cycles_to_ns(ce->engine, cycles));
- return intel_gt_wait_for_idle(ce->engine->gt, HZ);
- err:
- intel_gt_set_wedged(ce->engine->gt);
- return err;
- }
- static int measure_busy_dispatch(struct intel_context *ce)
- {
- u32 *sema = hwsp_scratch(ce);
- const u32 offset = hwsp_offset(ce, sema);
- u32 elapsed[TF_COUNT + 1], cycles;
- u32 *cs;
- int err;
- int i;
- /*
- * Measure how long it takes for us to submit a request while the
- * engine is busy, polling on a semaphore in our context. With
- * direct submission, this will include the cost of a lite restore.
- *
- * A: read CS_TIMESTAMP from CPU
- * submit request
- * B: read CS_TIMESTAMP on GPU
- *
- * Submission latency: B - A
- */
- for (i = 1; i <= ARRAY_SIZE(elapsed); i++) {
- struct i915_request *rq;
- rq = i915_request_create(ce);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- goto err;
- }
- cs = intel_ring_begin(rq, 12);
- if (IS_ERR(cs)) {
- i915_request_add(rq);
- err = PTR_ERR(cs);
- goto err;
- }
- cs = emit_store_dw(cs, offset + i * sizeof(u32), -1);
- cs = emit_semaphore_poll_until(cs, offset, i);
- cs = emit_timestamp_store(cs, ce, offset + i * sizeof(u32));
- intel_ring_advance(rq, cs);
- if (i > 1 && wait_for(READ_ONCE(sema[i - 1]), 500)) {
- err = -EIO;
- goto err;
- }
- preempt_disable();
- local_bh_disable();
- elapsed[i - 1] = ENGINE_READ_FW(ce->engine, RING_TIMESTAMP);
- i915_request_add(rq);
- local_bh_enable();
- semaphore_set(sema, i - 1);
- preempt_enable();
- }
- wait_for(READ_ONCE(sema[i - 1]), 500);
- semaphore_set(sema, i - 1);
- for (i = 1; i <= TF_COUNT; i++) {
- GEM_BUG_ON(sema[i] == -1);
- elapsed[i - 1] = sema[i] - elapsed[i];
- }
- cycles = trifilter(elapsed);
- pr_info("%s: busy dispatch latency %d cycles, %lluns\n",
- ce->engine->name, cycles >> TF_BIAS,
- cycles_to_ns(ce->engine, cycles));
- return intel_gt_wait_for_idle(ce->engine->gt, HZ);
- err:
- intel_gt_set_wedged(ce->engine->gt);
- return err;
- }
- static int plug(struct intel_engine_cs *engine, u32 *sema, u32 mode, int value)
- {
- const u32 offset =
- i915_ggtt_offset(engine->status_page.vma) +
- offset_in_page(sema);
- struct i915_request *rq;
- u32 *cs;
- rq = i915_request_create(engine->kernel_context);
- if (IS_ERR(rq))
- return PTR_ERR(rq);
- cs = intel_ring_begin(rq, 4);
- if (IS_ERR(cs)) {
- i915_request_add(rq);
- return PTR_ERR(cs);
- }
- cs = emit_semaphore_poll(cs, mode, value, offset);
- intel_ring_advance(rq, cs);
- i915_request_add(rq);
- return 0;
- }
- static int measure_inter_request(struct intel_context *ce)
- {
- u32 *sema = hwsp_scratch(ce);
- const u32 offset = hwsp_offset(ce, sema);
- u32 elapsed[TF_COUNT + 1], cycles;
- struct i915_sw_fence *submit;
- int i, err;
- /*
- * Measure how long it takes to advance from one request into the
- * next. Between each request we flush the GPU caches to memory,
- * update the breadcrumbs, and then invalidate those caches.
- * We queue up all the requests to be submitted in one batch so
- * it should be one set of contiguous measurements.
- *
- * A: read CS_TIMESTAMP on GPU
- * advance request
- * B: read CS_TIMESTAMP on GPU
- *
- * Request latency: B - A
- */
- err = plug(ce->engine, sema, MI_SEMAPHORE_SAD_NEQ_SDD, 0);
- if (err)
- return err;
- submit = heap_fence_create(GFP_KERNEL);
- if (!submit) {
- semaphore_set(sema, 1);
- return -ENOMEM;
- }
- intel_engine_flush_submission(ce->engine);
- for (i = 1; i <= ARRAY_SIZE(elapsed); i++) {
- struct i915_request *rq;
- u32 *cs;
- rq = i915_request_create(ce);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- goto err_submit;
- }
- err = i915_sw_fence_await_sw_fence_gfp(&rq->submit,
- submit,
- GFP_KERNEL);
- if (err < 0) {
- i915_request_add(rq);
- goto err_submit;
- }
- cs = intel_ring_begin(rq, 4);
- if (IS_ERR(cs)) {
- i915_request_add(rq);
- err = PTR_ERR(cs);
- goto err_submit;
- }
- cs = emit_timestamp_store(cs, ce, offset + i * sizeof(u32));
- intel_ring_advance(rq, cs);
- i915_request_add(rq);
- }
- i915_sw_fence_commit(submit);
- intel_engine_flush_submission(ce->engine);
- heap_fence_put(submit);
- semaphore_set(sema, 1);
- err = intel_gt_wait_for_idle(ce->engine->gt, HZ / 2);
- if (err)
- goto err;
- for (i = 1; i <= TF_COUNT; i++)
- elapsed[i - 1] = sema[i + 1] - sema[i];
- cycles = trifilter(elapsed);
- pr_info("%s: inter-request latency %d cycles, %lluns\n",
- ce->engine->name, cycles >> TF_BIAS,
- cycles_to_ns(ce->engine, cycles));
- return intel_gt_wait_for_idle(ce->engine->gt, HZ);
- err_submit:
- i915_sw_fence_commit(submit);
- heap_fence_put(submit);
- semaphore_set(sema, 1);
- err:
- intel_gt_set_wedged(ce->engine->gt);
- return err;
- }
- static int measure_context_switch(struct intel_context *ce)
- {
- u32 *sema = hwsp_scratch(ce);
- const u32 offset = hwsp_offset(ce, sema);
- struct i915_request *fence = NULL;
- u32 elapsed[TF_COUNT + 1], cycles;
- int i, j, err;
- u32 *cs;
- /*
- * Measure how long it takes to advance from one request in one
- * context to a request in another context. This allows us to
- * measure how long the context save/restore take, along with all
- * the inter-context setup we require.
- *
- * A: read CS_TIMESTAMP on GPU
- * switch context
- * B: read CS_TIMESTAMP on GPU
- *
- * Context switch latency: B - A
- */
- err = plug(ce->engine, sema, MI_SEMAPHORE_SAD_NEQ_SDD, 0);
- if (err)
- return err;
- for (i = 1; i <= ARRAY_SIZE(elapsed); i++) {
- struct intel_context *arr[] = {
- ce, ce->engine->kernel_context
- };
- u32 addr = offset + ARRAY_SIZE(arr) * i * sizeof(u32);
- for (j = 0; j < ARRAY_SIZE(arr); j++) {
- struct i915_request *rq;
- rq = i915_request_create(arr[j]);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- goto err_fence;
- }
- if (fence) {
- err = i915_request_await_dma_fence(rq,
- &fence->fence);
- if (err) {
- i915_request_add(rq);
- goto err_fence;
- }
- }
- cs = intel_ring_begin(rq, 4);
- if (IS_ERR(cs)) {
- i915_request_add(rq);
- err = PTR_ERR(cs);
- goto err_fence;
- }
- cs = emit_timestamp_store(cs, ce, addr);
- addr += sizeof(u32);
- intel_ring_advance(rq, cs);
- i915_request_put(fence);
- fence = i915_request_get(rq);
- i915_request_add(rq);
- }
- }
- i915_request_put(fence);
- intel_engine_flush_submission(ce->engine);
- semaphore_set(sema, 1);
- err = intel_gt_wait_for_idle(ce->engine->gt, HZ / 2);
- if (err)
- goto err;
- for (i = 1; i <= TF_COUNT; i++)
- elapsed[i - 1] = sema[2 * i + 2] - sema[2 * i + 1];
- cycles = trifilter(elapsed);
- pr_info("%s: context switch latency %d cycles, %lluns\n",
- ce->engine->name, cycles >> TF_BIAS,
- cycles_to_ns(ce->engine, cycles));
- return intel_gt_wait_for_idle(ce->engine->gt, HZ);
- err_fence:
- i915_request_put(fence);
- semaphore_set(sema, 1);
- err:
- intel_gt_set_wedged(ce->engine->gt);
- return err;
- }
- static int measure_preemption(struct intel_context *ce)
- {
- u32 *sema = hwsp_scratch(ce);
- const u32 offset = hwsp_offset(ce, sema);
- u32 elapsed[TF_COUNT], cycles;
- u32 *cs;
- int err;
- int i;
- /*
- * We measure two latencies while triggering preemption. The first
- * latency is how long it takes for us to submit a preempting request.
- * The second latency is how it takes for us to return from the
- * preemption back to the original context.
- *
- * A: read CS_TIMESTAMP from CPU
- * submit preemption
- * B: read CS_TIMESTAMP on GPU (in preempting context)
- * context switch
- * C: read CS_TIMESTAMP on GPU (in original context)
- *
- * Preemption dispatch latency: B - A
- * Preemption switch latency: C - B
- */
- if (!intel_engine_has_preemption(ce->engine))
- return 0;
- for (i = 1; i <= ARRAY_SIZE(elapsed); i++) {
- u32 addr = offset + 2 * i * sizeof(u32);
- struct i915_request *rq;
- rq = i915_request_create(ce);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- goto err;
- }
- cs = intel_ring_begin(rq, 12);
- if (IS_ERR(cs)) {
- i915_request_add(rq);
- err = PTR_ERR(cs);
- goto err;
- }
- cs = emit_store_dw(cs, addr, -1);
- cs = emit_semaphore_poll_until(cs, offset, i);
- cs = emit_timestamp_store(cs, ce, addr + sizeof(u32));
- intel_ring_advance(rq, cs);
- i915_request_add(rq);
- if (wait_for(READ_ONCE(sema[2 * i]) == -1, 500)) {
- err = -EIO;
- goto err;
- }
- rq = i915_request_create(ce->engine->kernel_context);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- goto err;
- }
- cs = intel_ring_begin(rq, 8);
- if (IS_ERR(cs)) {
- i915_request_add(rq);
- err = PTR_ERR(cs);
- goto err;
- }
- cs = emit_timestamp_store(cs, ce, addr);
- cs = emit_store_dw(cs, offset, i);
- intel_ring_advance(rq, cs);
- rq->sched.attr.priority = I915_PRIORITY_BARRIER;
- elapsed[i - 1] = ENGINE_READ_FW(ce->engine, RING_TIMESTAMP);
- i915_request_add(rq);
- }
- if (wait_for(READ_ONCE(sema[2 * i - 2]) != -1, 500)) {
- err = -EIO;
- goto err;
- }
- for (i = 1; i <= TF_COUNT; i++)
- elapsed[i - 1] = sema[2 * i + 0] - elapsed[i - 1];
- cycles = trifilter(elapsed);
- pr_info("%s: preemption dispatch latency %d cycles, %lluns\n",
- ce->engine->name, cycles >> TF_BIAS,
- cycles_to_ns(ce->engine, cycles));
- for (i = 1; i <= TF_COUNT; i++)
- elapsed[i - 1] = sema[2 * i + 1] - sema[2 * i + 0];
- cycles = trifilter(elapsed);
- pr_info("%s: preemption switch latency %d cycles, %lluns\n",
- ce->engine->name, cycles >> TF_BIAS,
- cycles_to_ns(ce->engine, cycles));
- return intel_gt_wait_for_idle(ce->engine->gt, HZ);
- err:
- intel_gt_set_wedged(ce->engine->gt);
- return err;
- }
- struct signal_cb {
- struct dma_fence_cb base;
- bool seen;
- };
- static void signal_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
- {
- struct signal_cb *s = container_of(cb, typeof(*s), base);
- smp_store_mb(s->seen, true); /* be safe, be strong */
- }
- static int measure_completion(struct intel_context *ce)
- {
- u32 *sema = hwsp_scratch(ce);
- const u32 offset = hwsp_offset(ce, sema);
- u32 elapsed[TF_COUNT], cycles;
- u32 *cs;
- int err;
- int i;
- /*
- * Measure how long it takes for the signal (interrupt) to be
- * sent from the GPU to be processed by the CPU.
- *
- * A: read CS_TIMESTAMP on GPU
- * signal
- * B: read CS_TIMESTAMP from CPU
- *
- * Completion latency: B - A
- */
- for (i = 1; i <= ARRAY_SIZE(elapsed); i++) {
- struct signal_cb cb = { .seen = false };
- struct i915_request *rq;
- rq = i915_request_create(ce);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- goto err;
- }
- cs = intel_ring_begin(rq, 12);
- if (IS_ERR(cs)) {
- i915_request_add(rq);
- err = PTR_ERR(cs);
- goto err;
- }
- cs = emit_store_dw(cs, offset + i * sizeof(u32), -1);
- cs = emit_semaphore_poll_until(cs, offset, i);
- cs = emit_timestamp_store(cs, ce, offset + i * sizeof(u32));
- intel_ring_advance(rq, cs);
- dma_fence_add_callback(&rq->fence, &cb.base, signal_cb);
- i915_request_add(rq);
- intel_engine_flush_submission(ce->engine);
- if (wait_for(READ_ONCE(sema[i]) == -1, 50)) {
- err = -EIO;
- goto err;
- }
- preempt_disable();
- semaphore_set(sema, i);
- while (!READ_ONCE(cb.seen))
- cpu_relax();
- elapsed[i - 1] = ENGINE_READ_FW(ce->engine, RING_TIMESTAMP);
- preempt_enable();
- }
- err = intel_gt_wait_for_idle(ce->engine->gt, HZ / 2);
- if (err)
- goto err;
- for (i = 0; i < ARRAY_SIZE(elapsed); i++) {
- GEM_BUG_ON(sema[i + 1] == -1);
- elapsed[i] = elapsed[i] - sema[i + 1];
- }
- cycles = trifilter(elapsed);
- pr_info("%s: completion latency %d cycles, %lluns\n",
- ce->engine->name, cycles >> TF_BIAS,
- cycles_to_ns(ce->engine, cycles));
- return intel_gt_wait_for_idle(ce->engine->gt, HZ);
- err:
- intel_gt_set_wedged(ce->engine->gt);
- return err;
- }
- static void rps_pin(struct intel_gt *gt)
- {
- /* Pin the frequency to max */
- atomic_inc(>->rps.num_waiters);
- intel_uncore_forcewake_get(gt->uncore, FORCEWAKE_ALL);
- mutex_lock(>->rps.lock);
- intel_rps_set(>->rps, gt->rps.max_freq);
- mutex_unlock(>->rps.lock);
- }
- static void rps_unpin(struct intel_gt *gt)
- {
- intel_uncore_forcewake_put(gt->uncore, FORCEWAKE_ALL);
- atomic_dec(>->rps.num_waiters);
- }
- static int perf_request_latency(void *arg)
- {
- struct drm_i915_private *i915 = arg;
- struct intel_engine_cs *engine;
- struct pm_qos_request qos;
- int err = 0;
- if (GRAPHICS_VER(i915) < 8) /* per-engine CS timestamp, semaphores */
- return 0;
- cpu_latency_qos_add_request(&qos, 0); /* disable cstates */
- for_each_uabi_engine(engine, i915) {
- struct intel_context *ce;
- ce = intel_context_create(engine);
- if (IS_ERR(ce)) {
- err = PTR_ERR(ce);
- goto out;
- }
- err = intel_context_pin(ce);
- if (err) {
- intel_context_put(ce);
- goto out;
- }
- st_engine_heartbeat_disable(engine);
- rps_pin(engine->gt);
- if (err == 0)
- err = measure_semaphore_response(ce);
- if (err == 0)
- err = measure_idle_dispatch(ce);
- if (err == 0)
- err = measure_busy_dispatch(ce);
- if (err == 0)
- err = measure_inter_request(ce);
- if (err == 0)
- err = measure_context_switch(ce);
- if (err == 0)
- err = measure_preemption(ce);
- if (err == 0)
- err = measure_completion(ce);
- rps_unpin(engine->gt);
- st_engine_heartbeat_enable(engine);
- intel_context_unpin(ce);
- intel_context_put(ce);
- if (err)
- goto out;
- }
- out:
- if (igt_flush_test(i915))
- err = -EIO;
- cpu_latency_qos_remove_request(&qos);
- return err;
- }
- static int s_sync0(void *arg)
- {
- struct perf_series *ps = arg;
- IGT_TIMEOUT(end_time);
- unsigned int idx = 0;
- int err = 0;
- GEM_BUG_ON(!ps->nengines);
- do {
- struct i915_request *rq;
- rq = i915_request_create(ps->ce[idx]);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- break;
- }
- i915_request_get(rq);
- i915_request_add(rq);
- if (i915_request_wait(rq, 0, HZ / 5) < 0)
- err = -ETIME;
- i915_request_put(rq);
- if (err)
- break;
- if (++idx == ps->nengines)
- idx = 0;
- } while (!__igt_timeout(end_time, NULL));
- return err;
- }
- static int s_sync1(void *arg)
- {
- struct perf_series *ps = arg;
- struct i915_request *prev = NULL;
- IGT_TIMEOUT(end_time);
- unsigned int idx = 0;
- int err = 0;
- GEM_BUG_ON(!ps->nengines);
- do {
- struct i915_request *rq;
- rq = i915_request_create(ps->ce[idx]);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- break;
- }
- i915_request_get(rq);
- i915_request_add(rq);
- if (prev && i915_request_wait(prev, 0, HZ / 5) < 0)
- err = -ETIME;
- i915_request_put(prev);
- prev = rq;
- if (err)
- break;
- if (++idx == ps->nengines)
- idx = 0;
- } while (!__igt_timeout(end_time, NULL));
- i915_request_put(prev);
- return err;
- }
- static int s_many(void *arg)
- {
- struct perf_series *ps = arg;
- IGT_TIMEOUT(end_time);
- unsigned int idx = 0;
- GEM_BUG_ON(!ps->nengines);
- do {
- struct i915_request *rq;
- rq = i915_request_create(ps->ce[idx]);
- if (IS_ERR(rq))
- return PTR_ERR(rq);
- i915_request_add(rq);
- if (++idx == ps->nengines)
- idx = 0;
- } while (!__igt_timeout(end_time, NULL));
- return 0;
- }
- static int perf_series_engines(void *arg)
- {
- struct drm_i915_private *i915 = arg;
- static int (* const func[])(void *arg) = {
- s_sync0,
- s_sync1,
- s_many,
- NULL,
- };
- const unsigned int nengines = num_uabi_engines(i915);
- struct intel_engine_cs *engine;
- int (* const *fn)(void *arg);
- struct pm_qos_request qos;
- struct perf_stats *stats;
- struct perf_series *ps;
- unsigned int idx;
- int err = 0;
- stats = kzalloc_objs(*stats, nengines);
- if (!stats)
- return -ENOMEM;
- ps = kzalloc_flex(*ps, ce, nengines);
- if (!ps) {
- kfree(stats);
- return -ENOMEM;
- }
- cpu_latency_qos_add_request(&qos, 0); /* disable cstates */
- ps->i915 = i915;
- ps->nengines = nengines;
- idx = 0;
- for_each_uabi_engine(engine, i915) {
- struct intel_context *ce;
- ce = intel_context_create(engine);
- if (IS_ERR(ce)) {
- err = PTR_ERR(ce);
- goto out;
- }
- err = intel_context_pin(ce);
- if (err) {
- intel_context_put(ce);
- goto out;
- }
- ps->ce[idx++] = ce;
- }
- GEM_BUG_ON(idx != ps->nengines);
- for (fn = func; *fn && !err; fn++) {
- char name[KSYM_NAME_LEN];
- struct igt_live_test t;
- snprintf(name, sizeof(name), "%ps", *fn);
- err = igt_live_test_begin(&t, i915, __func__, name);
- if (err)
- break;
- for (idx = 0; idx < nengines; idx++) {
- struct perf_stats *p =
- memset(&stats[idx], 0, sizeof(stats[idx]));
- struct intel_context *ce = ps->ce[idx];
- p->engine = ps->ce[idx]->engine;
- intel_engine_pm_get(p->engine);
- if (intel_engine_supports_stats(p->engine))
- p->busy = intel_engine_get_busy_time(p->engine,
- &p->time) + 1;
- else
- p->time = ktime_get();
- p->runtime = -intel_context_get_total_runtime_ns(ce);
- }
- err = (*fn)(ps);
- if (igt_live_test_end(&t))
- err = -EIO;
- for (idx = 0; idx < nengines; idx++) {
- struct perf_stats *p = &stats[idx];
- struct intel_context *ce = ps->ce[idx];
- int integer, decimal;
- u64 busy, dt, now;
- if (p->busy)
- p->busy = ktime_sub(intel_engine_get_busy_time(p->engine,
- &now),
- p->busy - 1);
- else
- now = ktime_get();
- p->time = ktime_sub(now, p->time);
- err = switch_to_kernel_sync(ce, err);
- p->runtime += intel_context_get_total_runtime_ns(ce);
- intel_engine_pm_put(p->engine);
- busy = 100 * ktime_to_ns(p->busy);
- dt = ktime_to_ns(p->time);
- if (dt) {
- integer = div64_u64(busy, dt);
- busy -= integer * dt;
- decimal = div64_u64(100 * busy, dt);
- } else {
- integer = 0;
- decimal = 0;
- }
- pr_info("%s %5s: { seqno:%d, busy:%d.%02d%%, runtime:%lldms, walltime:%lldms }\n",
- name, p->engine->name, ce->timeline->seqno,
- integer, decimal,
- div_u64(p->runtime, 1000 * 1000),
- div_u64(ktime_to_ns(p->time), 1000 * 1000));
- }
- }
- out:
- for (idx = 0; idx < nengines; idx++) {
- if (IS_ERR_OR_NULL(ps->ce[idx]))
- break;
- intel_context_unpin(ps->ce[idx]);
- intel_context_put(ps->ce[idx]);
- }
- kfree(ps);
- cpu_latency_qos_remove_request(&qos);
- kfree(stats);
- return err;
- }
- struct p_thread {
- struct perf_stats p;
- struct kthread_worker *worker;
- struct kthread_work work;
- struct intel_engine_cs *engine;
- int result;
- };
- static void p_sync0(struct kthread_work *work)
- {
- struct p_thread *thread = container_of(work, typeof(*thread), work);
- struct perf_stats *p = &thread->p;
- struct intel_engine_cs *engine = p->engine;
- struct intel_context *ce;
- IGT_TIMEOUT(end_time);
- unsigned long count;
- bool busy;
- int err = 0;
- ce = intel_context_create(engine);
- if (IS_ERR(ce)) {
- thread->result = PTR_ERR(ce);
- return;
- }
- err = intel_context_pin(ce);
- if (err) {
- intel_context_put(ce);
- thread->result = err;
- return;
- }
- if (intel_engine_supports_stats(engine)) {
- p->busy = intel_engine_get_busy_time(engine, &p->time);
- busy = true;
- } else {
- p->time = ktime_get();
- busy = false;
- }
- count = 0;
- do {
- struct i915_request *rq;
- rq = i915_request_create(ce);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- break;
- }
- i915_request_get(rq);
- i915_request_add(rq);
- err = 0;
- if (i915_request_wait(rq, 0, HZ) < 0)
- err = -ETIME;
- i915_request_put(rq);
- if (err)
- break;
- count++;
- } while (!__igt_timeout(end_time, NULL));
- if (busy) {
- ktime_t now;
- p->busy = ktime_sub(intel_engine_get_busy_time(engine, &now),
- p->busy);
- p->time = ktime_sub(now, p->time);
- } else {
- p->time = ktime_sub(ktime_get(), p->time);
- }
- err = switch_to_kernel_sync(ce, err);
- p->runtime = intel_context_get_total_runtime_ns(ce);
- p->count = count;
- intel_context_unpin(ce);
- intel_context_put(ce);
- thread->result = err;
- }
- static void p_sync1(struct kthread_work *work)
- {
- struct p_thread *thread = container_of(work, typeof(*thread), work);
- struct perf_stats *p = &thread->p;
- struct intel_engine_cs *engine = p->engine;
- struct i915_request *prev = NULL;
- struct intel_context *ce;
- IGT_TIMEOUT(end_time);
- unsigned long count;
- bool busy;
- int err = 0;
- ce = intel_context_create(engine);
- if (IS_ERR(ce)) {
- thread->result = PTR_ERR(ce);
- return;
- }
- err = intel_context_pin(ce);
- if (err) {
- intel_context_put(ce);
- thread->result = err;
- return;
- }
- if (intel_engine_supports_stats(engine)) {
- p->busy = intel_engine_get_busy_time(engine, &p->time);
- busy = true;
- } else {
- p->time = ktime_get();
- busy = false;
- }
- count = 0;
- do {
- struct i915_request *rq;
- rq = i915_request_create(ce);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- break;
- }
- i915_request_get(rq);
- i915_request_add(rq);
- err = 0;
- if (prev && i915_request_wait(prev, 0, HZ) < 0)
- err = -ETIME;
- i915_request_put(prev);
- prev = rq;
- if (err)
- break;
- count++;
- } while (!__igt_timeout(end_time, NULL));
- i915_request_put(prev);
- if (busy) {
- ktime_t now;
- p->busy = ktime_sub(intel_engine_get_busy_time(engine, &now),
- p->busy);
- p->time = ktime_sub(now, p->time);
- } else {
- p->time = ktime_sub(ktime_get(), p->time);
- }
- err = switch_to_kernel_sync(ce, err);
- p->runtime = intel_context_get_total_runtime_ns(ce);
- p->count = count;
- intel_context_unpin(ce);
- intel_context_put(ce);
- thread->result = err;
- }
- static void p_many(struct kthread_work *work)
- {
- struct p_thread *thread = container_of(work, typeof(*thread), work);
- struct perf_stats *p = &thread->p;
- struct intel_engine_cs *engine = p->engine;
- struct intel_context *ce;
- IGT_TIMEOUT(end_time);
- unsigned long count;
- int err = 0;
- bool busy;
- ce = intel_context_create(engine);
- if (IS_ERR(ce)) {
- thread->result = PTR_ERR(ce);
- return;
- }
- err = intel_context_pin(ce);
- if (err) {
- intel_context_put(ce);
- thread->result = err;
- return;
- }
- if (intel_engine_supports_stats(engine)) {
- p->busy = intel_engine_get_busy_time(engine, &p->time);
- busy = true;
- } else {
- p->time = ktime_get();
- busy = false;
- }
- count = 0;
- do {
- struct i915_request *rq;
- rq = i915_request_create(ce);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- break;
- }
- i915_request_add(rq);
- count++;
- } while (!__igt_timeout(end_time, NULL));
- if (busy) {
- ktime_t now;
- p->busy = ktime_sub(intel_engine_get_busy_time(engine, &now),
- p->busy);
- p->time = ktime_sub(now, p->time);
- } else {
- p->time = ktime_sub(ktime_get(), p->time);
- }
- err = switch_to_kernel_sync(ce, err);
- p->runtime = intel_context_get_total_runtime_ns(ce);
- p->count = count;
- intel_context_unpin(ce);
- intel_context_put(ce);
- thread->result = err;
- }
- static int perf_parallel_engines(void *arg)
- {
- struct drm_i915_private *i915 = arg;
- static void (* const func[])(struct kthread_work *) = {
- p_sync0,
- p_sync1,
- p_many,
- NULL,
- };
- const unsigned int nengines = num_uabi_engines(i915);
- void (* const *fn)(struct kthread_work *);
- struct intel_engine_cs *engine;
- struct pm_qos_request qos;
- struct p_thread *engines;
- int err = 0;
- engines = kzalloc_objs(*engines, nengines);
- if (!engines)
- return -ENOMEM;
- cpu_latency_qos_add_request(&qos, 0);
- for (fn = func; *fn; fn++) {
- char name[KSYM_NAME_LEN];
- struct igt_live_test t;
- unsigned int idx;
- snprintf(name, sizeof(name), "%ps", *fn);
- err = igt_live_test_begin(&t, i915, __func__, name);
- if (err)
- break;
- atomic_set(&i915->selftest.counter, nengines);
- idx = 0;
- for_each_uabi_engine(engine, i915) {
- struct kthread_worker *worker;
- intel_engine_pm_get(engine);
- memset(&engines[idx].p, 0, sizeof(engines[idx].p));
- worker = kthread_run_worker(0, "igt:%s",
- engine->name);
- if (IS_ERR(worker)) {
- err = PTR_ERR(worker);
- intel_engine_pm_put(engine);
- break;
- }
- engines[idx].worker = worker;
- engines[idx].result = 0;
- engines[idx].p.engine = engine;
- engines[idx].engine = engine;
- kthread_init_work(&engines[idx].work, *fn);
- kthread_queue_work(worker, &engines[idx].work);
- idx++;
- }
- idx = 0;
- for_each_uabi_engine(engine, i915) {
- int status;
- if (!engines[idx].worker)
- break;
- kthread_flush_work(&engines[idx].work);
- status = READ_ONCE(engines[idx].result);
- if (status && !err)
- err = status;
- intel_engine_pm_put(engine);
- kthread_destroy_worker(engines[idx].worker);
- idx++;
- }
- if (igt_live_test_end(&t))
- err = -EIO;
- if (err)
- break;
- idx = 0;
- for_each_uabi_engine(engine, i915) {
- struct perf_stats *p = &engines[idx].p;
- u64 busy = 100 * ktime_to_ns(p->busy);
- u64 dt = ktime_to_ns(p->time);
- int integer, decimal;
- if (dt) {
- integer = div64_u64(busy, dt);
- busy -= integer * dt;
- decimal = div64_u64(100 * busy, dt);
- } else {
- integer = 0;
- decimal = 0;
- }
- GEM_BUG_ON(engine != p->engine);
- pr_info("%s %5s: { count:%lu, busy:%d.%02d%%, runtime:%lldms, walltime:%lldms }\n",
- name, engine->name, p->count, integer, decimal,
- div_u64(p->runtime, 1000 * 1000),
- div_u64(ktime_to_ns(p->time), 1000 * 1000));
- idx++;
- }
- }
- cpu_latency_qos_remove_request(&qos);
- kfree(engines);
- return err;
- }
- int i915_request_perf_selftests(struct drm_i915_private *i915)
- {
- static const struct i915_subtest tests[] = {
- SUBTEST(perf_request_latency),
- SUBTEST(perf_series_engines),
- SUBTEST(perf_parallel_engines),
- };
- if (intel_gt_is_wedged(to_gt(i915)))
- return 0;
- return i915_subtests(tests, i915);
- }
|