lib/scatterlist: add check when merging zone device pages
[linux-2.6-block.git] / lib / scatterlist.c
index c8c3d675845c37705f958d5beb0d772f4a2e5fca..a0ad2a7959b5d24d7e892fc69aabed60686ec782 100644 (file)
@@ -410,6 +410,15 @@ static struct scatterlist *get_next_sg(struct sg_append_table *table,
        return new_sg;
 }
 
+static bool pages_are_mergeable(struct page *a, struct page *b)
+{
+       if (page_to_pfn(a) != page_to_pfn(b) + 1)
+               return false;
+       if (!zone_device_pages_have_same_pgmap(a, b))
+               return false;
+       return true;
+}
+
 /**
  * sg_alloc_append_table_from_pages - Allocate and initialize an append sg
  *                                    table from an array of pages
@@ -447,6 +456,7 @@ int sg_alloc_append_table_from_pages(struct sg_append_table *sgt_append,
        unsigned int chunks, cur_page, seg_len, i, prv_len = 0;
        unsigned int added_nents = 0;
        struct scatterlist *s = sgt_append->prv;
+       struct page *last_pg;
 
        /*
         * The algorithm below requires max_segment to be aligned to PAGE_SIZE
@@ -460,21 +470,17 @@ int sg_alloc_append_table_from_pages(struct sg_append_table *sgt_append,
                return -EOPNOTSUPP;
 
        if (sgt_append->prv) {
-               unsigned long paddr =
-                       (page_to_pfn(sg_page(sgt_append->prv)) * PAGE_SIZE +
-                        sgt_append->prv->offset + sgt_append->prv->length) /
-                       PAGE_SIZE;
-
                if (WARN_ON(offset))
                        return -EINVAL;
 
                /* Merge contiguous pages into the last SG */
                prv_len = sgt_append->prv->length;
-               while (n_pages && page_to_pfn(pages[0]) == paddr) {
+               last_pg = sg_page(sgt_append->prv);
+               while (n_pages && pages_are_mergeable(last_pg, pages[0])) {
                        if (sgt_append->prv->length + PAGE_SIZE > max_segment)
                                break;
                        sgt_append->prv->length += PAGE_SIZE;
-                       paddr++;
+                       last_pg = pages[0];
                        pages++;
                        n_pages--;
                }
@@ -488,7 +494,7 @@ int sg_alloc_append_table_from_pages(struct sg_append_table *sgt_append,
        for (i = 1; i < n_pages; i++) {
                seg_len += PAGE_SIZE;
                if (seg_len >= max_segment ||
-                   page_to_pfn(pages[i]) != page_to_pfn(pages[i - 1]) + 1) {
+                   !pages_are_mergeable(pages[i], pages[i - 1])) {
                        chunks++;
                        seg_len = 0;
                }
@@ -504,8 +510,7 @@ int sg_alloc_append_table_from_pages(struct sg_append_table *sgt_append,
                for (j = cur_page + 1; j < n_pages; j++) {
                        seg_len += PAGE_SIZE;
                        if (seg_len >= max_segment ||
-                           page_to_pfn(pages[j]) !=
-                           page_to_pfn(pages[j - 1]) + 1)
+                           !pages_are_mergeable(pages[j], pages[j - 1]))
                                break;
                }