<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
  <body>
    Am 12.04.21 um 20:18 schrieb Andrey Grodzovsky:<br>
    <blockquote type="cite" cite="mid:ecf465a2-d4fc-1cbf-a9d5-39c3844f23bb@amd.com">
      
      <p>On 2021-04-12 2:05 p.m., Christian König wrote:<br>
      </p>
      <blockquote type="cite" cite="mid:a970101f-89f1-8bdf-51d9-4a4e5e0f9e9a@amd.com"> Am
        12.04.21 um 20:01 schrieb Andrey Grodzovsky:<br>
        <blockquote type="cite" cite="mid:aaa2b266-f091-dd9c-e49d-5e528decfbd7@amd.com">
          <p>On 2021-04-12 1:44 p.m., Christian König wrote:<br>
          </p>
          <blockquote type="cite" cite="mid:cd94e02c-11c8-0198-ab70-0ceee54d437b@amd.com"> <br>
            Am 12.04.21 um 19:27 schrieb Andrey Grodzovsky:<br>
            <blockquote type="cite" cite="mid:80713dbe-411c-d79b-34ba-b67bc3a50dc5@amd.com">
              <div class="moz-cite-prefix">On 2021-04-10 1:34 p.m.,
                Christian König wrote:<br>
              </div>
              <blockquote type="cite" cite="mid:b6a24d3f-4fe6-c642-b478-36e386aa906d@gmail.com">Hi
                Andrey, <br>
                <br>
                Am 09.04.21 um 20:18 schrieb Andrey Grodzovsky: <br>
                <blockquote type="cite">[SNIP] <br>
                  <blockquote type="cite">
                    <blockquote type="cite"> <br>
                      If we use a list and a flag called 'emit_allowed'
                      under a lock such that in amdgpu_fence_emit we
                      lock the list, check the flag and if true add the
                      new HW fence to list and proceed to HW emition as
                      normal, otherwise return with -ENODEV. In
                      amdgpu_pci_remove we take the lock, set the flag
                      to false, and then iterate the list and force
                      signal it. Will this not prevent any new HW fence
                      creation from now on from any place trying to do
                      so ? <br>
                    </blockquote>
                    <br>
                    Way to much overhead. The fence processing is
                    intentionally lock free to avoid cache line bouncing
                    because the IRQ can move from CPU to CPU. <br>
                    <br>
                    We need something which at least the processing of
                    fences in the interrupt handler doesn't affect at
                    all. <br>
                  </blockquote>
                  <br>
                  <br>
                  As far as I see in the code, amdgpu_fence_emit is only
                  called from task context. Also, we can skip this list
                  I proposed and just use
                  amdgpu_fence_driver_force_completion for each ring to
                  signal all created HW fences. <br>
                </blockquote>
                <br>
                Ah, wait a second this gave me another idea. <br>
                <br>
                See amdgpu_fence_driver_force_completion(): <br>
                <br>
                amdgpu_fence_write(ring, ring->fence_drv.sync_seq); <br>
                <br>
                If we change that to something like: <br>
                <br>
                amdgpu_fence_write(ring, ring->fence_drv.sync_seq +
                0x3FFFFFFF); <br>
                <br>
                Not only the currently submitted, but also the next
                0x3FFFFFFF fences will be considered signaled. <br>
                <br>
                This basically solves out problem of making sure that
                new fences are also signaled without any additional
                overhead whatsoever.</blockquote>
              <p><br>
              </p>
              <p>Problem with this is that the act of setting the
                sync_seq to some MAX value alone is not enough, you
                actually have to call amdgpu_fence_process to iterate
                and signal the fences currently stored in
                ring->fence_drv.fences array and to guarantee that
                once you done your signalling no more HW fences will be
                added to that array anymore. I was thinking to do
                something like bellow:</p>
            </blockquote>
            <br>
            Well we could implement the is_signaled callback once more,
            but I'm not sure if that is a good idea.<br>
          </blockquote>
          <p><br>
          </p>
          <p>This indeed could save the explicit signaling I am doing
            bellow but I also set an error code there which might be
            helpful to propagate to users</p>
          <p><br>
          </p>
          <blockquote type="cite" cite="mid:cd94e02c-11c8-0198-ab70-0ceee54d437b@amd.com"> <br>
            <blockquote type="cite" cite="mid:80713dbe-411c-d79b-34ba-b67bc3a50dc5@amd.com">
              <p>amdgpu_fence_emit()</p>
              <p>{</p>
              <p>    dma_fence_init(fence);<br>
              </p>
              <p>    srcu_read_lock(amdgpu_unplug_srcu)</p>
              <p>    if (!adev->unplug)) {</p>
              <p>        seq = ++ring->fence_drv.sync_seq;<br>
                        emit_fence(fence);</p>
              <p>        <b>/* We can't wait forever as the HW might be
                  gone at any point*/</b><b><br>
                         dma_fence_wait_timeout(old_fence, 5S);</b><br>
              </p>
            </blockquote>
            <br>
            You can pretty much ignore this wait here. It is only as a
            last resort so that we never overwrite the ring buffers.<br>
          </blockquote>
          <p><br>
          </p>
          <p>If device is present how can I ignore this ?</p>
        </blockquote>
      </blockquote>
      <p><br>
      </p>
      <p>I think you missed my question here <br>
      </p>
    </blockquote>
    <br>
    Sorry I thought I answered that below.<br>
    <br>
    See this is just the last resort so that we don't need to worry
    about ring buffer overflows during testing.<br>
    <br>
    We should not get here in practice and if we get here generating a
    deadlock might actually be the best handling.<br>
    <br>
    The alternative would be to call BUG().<br>
    <br>
    <blockquote type="cite" cite="mid:ecf465a2-d4fc-1cbf-a9d5-39c3844f23bb@amd.com">
      <p> </p>
      <blockquote type="cite" cite="mid:a970101f-89f1-8bdf-51d9-4a4e5e0f9e9a@amd.com">
        <blockquote type="cite" cite="mid:aaa2b266-f091-dd9c-e49d-5e528decfbd7@amd.com">
          <p><br>
          </p>
          <blockquote type="cite" cite="mid:cd94e02c-11c8-0198-ab70-0ceee54d437b@amd.com"> <br>
            But it should not have a timeout as far as I can see.<br>
          </blockquote>
          <p><br>
          </p>
          <p>Without timeout wait the who approach falls apart as I
            can't call srcu_synchronize on this scope because once
            device is physically gone the wait here will be forever</p>
        </blockquote>
        <br>
        Yeah, but this is intentional. The only alternative to avoid
        corruption is to wait with a timeout and call BUG() if that
        triggers. That isn't much better.<br>
        <br>
        <blockquote type="cite" cite="mid:aaa2b266-f091-dd9c-e49d-5e528decfbd7@amd.com">
          <p><br>
          </p>
          <blockquote type="cite" cite="mid:cd94e02c-11c8-0198-ab70-0ceee54d437b@amd.com"> <br>
            <blockquote type="cite" cite="mid:80713dbe-411c-d79b-34ba-b67bc3a50dc5@amd.com">
              <p>         ring->fence_drv.fences[seq &
                ring->fence_drv.num_fences_mask] = fence;<br>
              </p>
              <p>    } else {</p>
              <p>        dma_fence_set_error(fence, -ENODEV);<br>
                        DMA_fence_signal(fence)    <br>
              </p>
              <p>    }   <br>
              </p>
              <p>    srcu_read_unlock(amdgpu_unplug_srcu)<br>
                    return fence;<br>
              </p>
              <p>}</p>
              <p>amdgpu_pci_remove <br>
              </p>
              <p>{</p>
              <p>    adev->unplug = true;<br>
                    synchronize_srcu(amdgpu_unplug_srcu) <br>
              </p>
            </blockquote>
            <br>
            Well that is just duplicating what drm_dev_unplug() should
            be doing on a different level.<br>
          </blockquote>
          <p><br>
          </p>
          <p>drm_dev_unplug is on a much wider scope, for everything in
            the device including 'flushing' in flight IOCTLs, this deals
            specifically with the issue of force signalling HW fences</p>
        </blockquote>
        <br>
        Yeah, but it adds the same overhead as the device srcu.<br>
        <br>
        Christian.<br>
      </blockquote>
      <p><br>
      </p>
      <p>So what's the right approach ? How we guarantee that when
        running amdgpu_fence_driver_force_completion we will signal all
        the HW fences and not racing against some more fences insertion
        into that array ?</p>
    </blockquote>
    <br>
    Well I would still say the best approach would be to insert this
    between the front end and the backend and not rely on signaling
    fences while holding the device srcu.<br>
    <br>
    BTW: Could it be that the device SRCU protects more than one device
    and we deadlock because of this?<br>
    <br>
    Christian.<br>
    <br>
    <blockquote type="cite" cite="mid:ecf465a2-d4fc-1cbf-a9d5-39c3844f23bb@amd.com">
      <p>Andrey<br>
      </p>
      <p><br>
      </p>
      <blockquote type="cite" cite="mid:a970101f-89f1-8bdf-51d9-4a4e5e0f9e9a@amd.com"> <br>
        <blockquote type="cite" cite="mid:aaa2b266-f091-dd9c-e49d-5e528decfbd7@amd.com">
          <p>Andrey</p>
          <p><br>
          </p>
          <blockquote type="cite" cite="mid:cd94e02c-11c8-0198-ab70-0ceee54d437b@amd.com"> <br>
            Christian.<br>
            <br>
            <blockquote type="cite" cite="mid:80713dbe-411c-d79b-34ba-b67bc3a50dc5@amd.com">
              <p>    /* Past this point no more fence are submitted to
                HW ring and hence we can safely call force signal on all
                that are currently there. <br>
                     * Any subsequently created  HW fences will be
                returned signaled with an error code right away <br>
                     */<br>
              </p>
              <p>    for_each_ring(adev)<br>
                        amdgpu_fence_process(ring)</p>
              <p>    drm_dev_unplug(dev);<br>
                    Stop schedulers<br>
                    cancel_sync(all timers and queued works);<br>
                    hw_fini<br>
                    unmap_mmio<br>
              </p>
              <p>}</p>
              <p><br>
              </p>
              <p>Andrey</p>
              <p><br>
              </p>
              <blockquote type="cite" cite="mid:b6a24d3f-4fe6-c642-b478-36e386aa906d@gmail.com">
                <br>
                <br>
                <blockquote type="cite"> <br>
                  <blockquote type="cite">
                    <blockquote type="cite">
                      <blockquote type="cite"> <br>
                        Alternatively grabbing the reset write side and
                        stopping and then restarting the scheduler could
                        work as well. <br>
                        <br>
                        Christian. <br>
                      </blockquote>
                      <br>
                      <br>
                      I didn't get the above and I don't see why I need
                      to reuse the GPU reset rw_lock. I rely on the SRCU
                      unplug flag for unplug. Also, not clear to me why
                      are we focusing on the scheduler threads, any code
                      patch to generate HW fences should be covered, so
                      any code leading to amdgpu_fence_emit needs to be
                      taken into account such as, direct IB submissions,
                      VM flushes e.t.c <br>
                    </blockquote>
                    <br>
                    You need to work together with the reset lock
                    anyway, cause a hotplug could run at the same time
                    as a reset. <br>
                  </blockquote>
                  <br>
                  <br>
                  For going my way indeed now I see now that I have to
                  take reset write side lock during HW fences signalling
                  in order to protect against scheduler/HW fences
                  detachment and reattachment during schedulers
                  stop/restart. But if we go with your approach  then
                  calling drm_dev_unplug and scoping amdgpu_job_timeout
                  with drm_dev_enter/exit should be enough to prevent
                  any concurrent GPU resets during unplug. In fact I
                  already do it anyway - <a class="moz-txt-link-freetext" href="https://nam11.safelinks.protection.outlook.com/?url=https:%2F%2Fcgit.freedesktop.org%2F~agrodzov%2Flinux%2Fcommit%2F%3Fh%3Ddrm-misc-next%26id%3Def0ea4dd29ef44d2649c5eda16c8f4869acc36b1&amp;data=04%7C01%7Candrey.grodzovsky%40amd.com%7Ceefa9c90ed8c405ec3b708d8fc46daaa%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637536728550884740%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=UiNaJE%2BH45iYmbwSDnMSKZS5z0iak0fNlbbfYqKS2Jo%3D&amp;reserved=0" moz-do-not-send="true">https://nam11.safelinks.protection.outlook.com/?url=https:%2F%2Fcgit.freedesktop.org%2F~agrodzov%2Flinux%2Fcommit%2F%3Fh%3Ddrm-misc-next%26id%3Def0ea4dd29ef44d2649c5eda16c8f4869acc36b1&amp;data=04%7C01%7Candrey.grodzovsky%40amd.com%7Ceefa9c90ed8c405ec3b708d8fc46daaa%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637536728550884740%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=UiNaJE%2BH45iYmbwSDnMSKZS5z0iak0fNlbbfYqKS2Jo%3D&amp;reserved=0</a><br>
                </blockquote>
                <br>
                Yes, good point as well. <br>
                <br>
                Christian. <br>
                <br>
                <blockquote type="cite"> <br>
                  Andrey <br>
                  <br>
                  <br>
                  <blockquote type="cite"> <br>
                    <br>
                    Christian. <br>
                    <br>
                    <blockquote type="cite"> <br>
                      Andrey <br>
                      <br>
                      <br>
                      <blockquote type="cite"> <br>
                        <blockquote type="cite"> <br>
                          Christian. <br>
                          <br>
                          <blockquote type="cite"> <br>
                            Andrey <br>
                            <br>
                            <br>
                            <blockquote type="cite"> <br>
                              <blockquote type="cite">Andrey <br>
                                <br>
                                <br>
                              </blockquote>
                              <br>
                            </blockquote>
                          </blockquote>
                          <br>
                        </blockquote>
                        <br>
                      </blockquote>
                    </blockquote>
                    <br>
                  </blockquote>
                </blockquote>
                <br>
              </blockquote>
            </blockquote>
            <br>
          </blockquote>
        </blockquote>
        <br>
      </blockquote>
    </blockquote>
    <br>
  </body>
</html>