On 2/27/2023 10:14 AM, Stanislaw Gruszka wrote:
On Fri, Feb 24, 2023 at 12:36:51PM -0700, Jeffrey Hugo wrote:
+static int reserve_pages(unsigned long start_pfn, unsigned long nr_pages,
+                        bool reserve)
+{
+       unsigned long pfn;
+       unsigned long end_pfn = start_pfn + nr_pages;
+       struct page *page;
+
+       for (pfn = start_pfn; pfn < end_pfn; pfn++) {
+               if (!pfn_valid(pfn))
+                       return -EINVAL;
+               page =  pfn_to_page(pfn);
+               if (reserve)
+                       SetPageReserved(page);
+               else
+                       ClearPageReserved(page);

It is needed? Looks like taken from some legacy code.

Required for remap_pfn_range().

PG_reserved is not required any longer for remap_pfn_range(), here
is excerpt from comment from include/linux/page-flags.h :

  * Some PG_reserved pages will be excluded from the hibernation image.
  * PG_reserved does in general not hinder anybody from dumping or swapping
  * and is no longer required for remap_pfn_range(). ioremap might require it.
  * Consequently, PG_reserved for a page mapped into user space can indicate
  * the zero page, the vDSO, MMIO pages or device memory.

I clearly missed that and was relying on other documentation. Thank you for pointing this out. Will remove.


+static int copy_sgt(struct qaic_device *qdev, struct sg_table **sgt_out,
+                   struct sg_table *sgt_in, u64 size, u64 offset)
+{
+       int total_len, len, nents, offf = 0, offl = 0;
+       struct scatterlist *sg, *sgn, *sgf, *sgl;
+       struct sg_table *sgt;
+       int ret, j;
+
+       /* find out number of relevant nents needed for this mem */
+       total_len = 0;
+       sgf = NULL;
+       sgl = NULL;
+       nents = 0;
+
+       size = size ? size : PAGE_SIZE;
+       for (sg = sgt_in->sgl; sg; sg = sg_next(sg)) {
+               len = sg_dma_len(sg);
+
+               if (!len)
+                       continue;
+               if (offset >= total_len && offset < total_len + len) {
+                       sgf = sg;
+                       offf = offset - total_len;
+               }
+               if (sgf)
+                       nents++;
+               if (offset + size >= total_len &&
+                   offset + size <= total_len + len) {
+                       sgl = sg;
+                       offl = offset + size - total_len;
+                       break;
+               }
+               total_len += len;
+       }
+
+       if (!sgf || !sgl) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+       if (!sgt) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       ret = sg_alloc_table(sgt, nents, GFP_KERNEL);
+       if (ret)
+               goto free_sgt;
+
+       /* copy relevant sg node and fix page and length */
+       sgn = sgf;
+       for_each_sgtable_sg(sgt, sg, j) {
+               memcpy(sg, sgn, sizeof(*sg));
+               if (sgn == sgf) {
+                       sg_dma_address(sg) += offf;

This looks a bit suspicious. Are you sure you can modify
sg->dma_address and still use it as valid value ?

A single entry in the sg table is a contiguous mapping of memory. If it wasn't contiguous, it would have to be broken up into multiple entries. In the simple case, a driver is going to take the dma_address/len pair and hand that directly to the device. Then the device is going to access every address in that range.

If the device can access every address from dma_address to dma_address + len, why can't it access a subset of that?

+                       sg_dma_len(sg) -= offf;
+                       sg_set_page(sg, sg_page(sgn),
+                                   sg_dma_len(sg), offf);
+               } else {
+                       offf = 0;
+               }
+               if (sgn == sgl) {
+                       sg_dma_len(sg) = offl - offf;
+                       sg_set_page(sg, sg_page(sgn),
+                                   offl - offf, offf);
+                       sg_mark_end(sg);
+                       break;
+               }
+               sgn = sg_next(sgn);
+       }

Why not use sg_copy_table() ? Overall copy_sgt() seems to be something
that could be replaced by generic helper or at least simplify.

I don't see "sg_copy_table" defined in 6.2.

Because there is no such function in any kernel source. It was only my
imagination, not sure right now how I came up with this function name :-/
Sorry about confusion.

There are only sg_copy_{to,from}_buffer(), but not really useful in
this case.

Are you suggesting renaming
this function?  I guess I'm not quite understanding your comment here. Can
you elaborate?

Renaming would be nice. I was thinking by simplifying it, not sure
now if that's easy achievable, though.

Ok.  I'll think on this.

Reply via email to