On Tue, Dec 17, 2019 at 08:31:07PM +0000, Jason Gunthorpe wrote:
On Fri, Dec 13, 2019 at 01:56:07PM -0800, Niranjana Vishwanathapura wrote:
+static struct i915_svm *vm_get_svm(struct i915_address_space *vm)
+{
+ struct i915_svm *svm = vm->svm;
+
+ mutex_lock(&vm->svm_mutex);
+ if (svm && !kref_get_unless_zero(&svm->ref))
+ svm = NULL;
Quite strange to see a get_unless_zero under a lock..
Thanks.
Yah I agree (construct taken from a differnt place).
It should go away once I swith to mmu_notifier_get/put.
+static void release_svm(struct kref *ref)
+{
+ struct i915_svm *svm = container_of(ref, typeof(*svm), ref);
+ struct i915_address_space *vm = svm->vm;
+
+ mmu_notifier_unregister(&svm->notifier, svm->notifier.mm);
This would be a lot better to use mmu_notifier_put to manage the
reference and deallocation.
Yah, have that in mind, will use that.
+static u32 i915_svm_build_sg(struct i915_address_space *vm,
+ struct hmm_range *range,
+ struct sg_table *st)
+{
+ struct scatterlist *sg;
+ u32 sg_page_sizes = 0;
+ u64 i, npages;
+
+ sg = NULL;
+ st->nents = 0;
+ npages = (range->end - range->start) / PAGE_SIZE;
+
+ /*
+ * No need to dma map the host pages and later unmap it, as
+ * GPU is not allowed to access it with SVM.
+ * XXX: Need to dma map host pages for integrated graphics while
+ * extending SVM support there.
+ */
+ for (i = 0; i < npages; i++) {
+ u64 addr = range->pfns[i] & ~((1UL << range->pfn_shift) - 1);
+
+ if (sg && (addr == (sg_dma_address(sg) + sg->length))) {
+ sg->length += PAGE_SIZE;
+ sg_dma_len(sg) += PAGE_SIZE;
+ continue;
+ }
+
+ if (sg)
+ sg_page_sizes |= sg->length;
+
+ sg = sg ? __sg_next(sg) : st->sgl;
+ sg_dma_address(sg) = addr;
+ sg_dma_len(sg) = PAGE_SIZE;
This still can't be like this - assigning pfn to 'dma_address' is
fundamentally wrong.
Whatever explanation you had, this needs to be fixed up first before we get
to this patch.
The pfn is converted into a device address which goes into sg_dma_address.
Ok, let me think about what else we can do here.
As device addresses are not dma mapped, may be we can look at it as direct
mapped (__phys_to_dma())? Or perhaps define our own sgl.
Not sure, will look into it.
+static int i915_range_fault(struct svm_notifier *sn,
+ struct drm_i915_gem_vm_bind *args,
+ struct sg_table *st, u64 *pfns)
+{
+ unsigned long timeout =
+ jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
+ /* Have HMM fault pages within the fault window to the GPU. */
+ struct hmm_range range = {
+ .notifier = &sn->notifier,
+ .start = sn->notifier.interval_tree.start,
+ .end = sn->notifier.interval_tree.last + 1,
+ .pfns = pfns,
+ .pfn_shift = PAGE_SHIFT,
+ .flags = i915_range_flags,
+ .values = i915_range_values,
+ .default_flags = (range.flags[HMM_PFN_VALID] |
+ ((args->flags & I915_GEM_VM_BIND_READONLY) ?
+ 0 : range.flags[HMM_PFN_WRITE])),
+ .pfn_flags_mask = 0,
+
+ };
+ struct i915_svm *svm = sn->svm;
+ struct mm_struct *mm = sn->notifier.mm;
+ struct i915_address_space *vm = svm->vm;
+ u32 sg_page_sizes;
+ u64 flags;
+ long ret;
+
+ while (true) {
+ if (time_after(jiffies, timeout))
+ return -EBUSY;
+
+ range.notifier_seq = mmu_interval_read_begin(range.notifier);
+ down_read(&mm->mmap_sem);
+ ret = hmm_range_fault(&range, 0);
+ up_read(&mm->mmap_sem);
+ if (ret <= 0) {
+ if (ret == 0 || ret == -EBUSY)
+ continue;
+ return ret;
+ }
+
+ sg_page_sizes = i915_svm_build_sg(vm, &range, st);
+ mutex_lock(&svm->mutex);
+ if (mmu_interval_read_retry(range.notifier,
+ range.notifier_seq)) {
+ mutex_unlock(&svm->mutex);
+ continue;
+ }
+ break;
+ }
+
+ flags = (args->flags & I915_GEM_VM_BIND_READONLY) ?
+ I915_GTT_SVM_READONLY : 0;
+ ret = svm_bind_addr_commit(vm, args->start, args->length,
+ flags, st, sg_page_sizes);
+ mutex_unlock(&svm->mutex);
This looks better..
+int i915_gem_vm_bind_svm_buffer(struct i915_address_space *vm,
+ struct drm_i915_gem_vm_bind *args)
+{
+ struct svm_notifier sn;
+ struct i915_svm *svm;
+ struct mm_struct *mm;
+ struct sg_table st;
+ u64 npages, *pfns;
+ int ret = 0;
+
+ svm = vm_get_svm(vm);
+ if (!svm)
+ return -EINVAL;
+
+ mm = svm->notifier.mm;
+ if (mm != current->mm) {
+ ret = -EPERM;
+ goto bind_done;
+ }
Bit strange, we have APIs now to make it fairly easy to deal with
multiple processe, (ie the get/put scheme) why have this restriction?
Nothing particular, just thought of supporting it later if required.
+
+ args->length += (args->start & ~PAGE_MASK);
+ args->start &= PAGE_MASK;
+ DRM_DEBUG_DRIVER("%sing start 0x%llx length 0x%llx vm_id 0x%x\n",
+ (args->flags & I915_GEM_VM_BIND_UNBIND) ?
+ "Unbind" : "Bind", args->start, args->length,
+ args->vm_id);
+ if (args->flags & I915_GEM_VM_BIND_UNBIND) {
+ i915_gem_vm_unbind_svm_buffer(vm, args->start, args->length);
+ goto bind_done;
+ }
+
+ npages = args->length / PAGE_SIZE;
+ if (unlikely(sg_alloc_table(&st, npages, GFP_KERNEL))) {
+ ret = -ENOMEM;
+ goto bind_done;
+ }
+
+ pfns = kvmalloc_array(npages, sizeof(uint64_t), GFP_KERNEL);
+ if (unlikely(!pfns)) {
+ ret = -ENOMEM;
+ goto range_done;
+ }
+
+ ret = svm_bind_addr_prepare(vm, args->start, args->length);
+ if (unlikely(ret))
+ goto prepare_done;
+
+ sn.svm = svm;
+ ret = mmu_interval_notifier_insert(&sn.notifier, mm,
+ args->start, args->length,
+ &i915_svm_mni_ops);
+ if (!ret) {
+ ret = i915_range_fault(&sn, args, &st, pfns);
+ mmu_interval_notifier_remove(&sn.notifier);
+ }
success oriented flow...
Ok.
+static int
+i915_svm_invalidate_range_start(struct mmu_notifier *mn,
+ const struct mmu_notifier_range *update)
+{
+ struct i915_svm *svm = container_of(mn, struct i915_svm, notifier);
+ unsigned long length = update->end - update->start;
+
+ DRM_DEBUG_DRIVER("start 0x%lx length 0x%lx\n", update->start, length);
+ if (!mmu_notifier_range_blockable(update))
+ return -EAGAIN;
+
+ i915_gem_vm_unbind_svm_buffer(svm->vm, update->start, length);
+ return 0;
+}
I still think you should strive for a better design than putting a
notifier across the entire address space..
Yah, thought it could be later optimization.
If I think about it, it has be be a new user API to set the range,
or an intermediate data structure for tracking the bound ranges.
Will look into it.
Thanks,
Niranjana
+
+#if defined(CONFIG_DRM_I915_SVM)
+struct i915_svm {
+ /* i915 address space */
+ struct i915_address_space *vm;
+
+ struct mmu_notifier notifier;
+ struct mutex mutex; /* protects svm operations */
+ /*
+ * XXX: Probably just make use of mmu_notifier's reference
+ * counting (get/put) instead of our own.
+ */
Yes
Jason
_______________________________________________
dri-devel mailing list
dri-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.freedesktop.org/mailman/listinfo/dri-devel