Whamcloud - gitweb
LU-9305 osd: do not release pages twice 50/27950/8
authorAlex Zhuravlev <alexey.zhuravlev@intel.com>
Sun, 9 Jul 2017 16:56:53 +0000 (12:56 -0400)
committerOleg Drokin <oleg.drokin@intel.com>
Mon, 10 Jul 2017 21:11:17 +0000 (21:11 +0000)
in case of blocksize mismatch dmu_assign_arcbuf() releases passed
abuf internally, including the pages. osd_bufs_put() can't detect
this and may call __free_page() on inappropriate pages (which can
be allocated to someone else already).

Change-Id: I454e56ee3de3d201a14e6ba7b4beabaad42d82ae
Signed-off-by: Alex Zhuravlev <alexey.zhuravlev@intel.com>
Reviewed-on: https://review.whamcloud.com/27950
Tested-by: Jenkins
Tested-by: Maloo <hpdd-maloo@intel.com>
Reviewed-by: Jinshan Xiong <jinshan.xiong@intel.com>
Reviewed-by: Nathaniel Clark <nathaniel.l.clark@intel.com>
Reviewed-by: Andreas Dilger <andreas.dilger@intel.com>
Reviewed-by: Oleg Drokin <oleg.drokin@intel.com>
lustre/osd-zfs/osd_io.c

index a74e58c..082d7dd 100644 (file)
@@ -265,6 +265,13 @@ static int osd_bufs_put(const struct lu_env *env, struct dt_object *dt,
                                dmu_buf_rele((void *)ptr, osd_0copy_tag);
                                atomic_dec(&osd->od_zerocopy_pin);
                        } else if (lnb[i].lnb_data != NULL) {
                                dmu_buf_rele((void *)ptr, osd_0copy_tag);
                                atomic_dec(&osd->od_zerocopy_pin);
                        } else if (lnb[i].lnb_data != NULL) {
+                               int j, apages, abufsz;
+                               abufsz = arc_buf_size(lnb[i].lnb_data);
+                               apages = abufsz / PAGE_SIZE;
+                               /* these references to pages must be invalidated
+                                * to prevent access in osd_bufs_put() */
+                               for (j = 0; j < apages; j++)
+                                       lnb[i + j].lnb_page = NULL;
                                dmu_return_arcbuf(lnb[i].lnb_data);
                                atomic_dec(&osd->od_zerocopy_loan);
                        }
                                dmu_return_arcbuf(lnb[i].lnb_data);
                                atomic_dec(&osd->od_zerocopy_loan);
                        }
@@ -797,17 +804,32 @@ static int osd_write_commit(const struct lu_env *env, struct dt_object *dt,
                        continue;
                }
 
                        continue;
                }
 
+               if (new_size < lnb[i].lnb_file_offset + lnb[i].lnb_len)
+                       new_size = lnb[i].lnb_file_offset + lnb[i].lnb_len;
+               if (lnb[i].lnb_page == NULL)
+                       continue;
+
                if (lnb[i].lnb_page->mapping == (void *)obj) {
                        osd_dmu_write(osd, obj->oo_dn, lnb[i].lnb_file_offset,
                                      lnb[i].lnb_len, kmap(lnb[i].lnb_page),
                                      oh->ot_tx);
                        kunmap(lnb[i].lnb_page);
                if (lnb[i].lnb_page->mapping == (void *)obj) {
                        osd_dmu_write(osd, obj->oo_dn, lnb[i].lnb_file_offset,
                                      lnb[i].lnb_len, kmap(lnb[i].lnb_page),
                                      oh->ot_tx);
                        kunmap(lnb[i].lnb_page);
+                       iosize += lnb[i].lnb_len;
                } else if (lnb[i].lnb_data) {
                } else if (lnb[i].lnb_data) {
+                       int j, apages, abufsz;
                        LASSERT(((unsigned long)lnb[i].lnb_data & 1) == 0);
                        /* buffer loaned for zerocopy, try to use it.
                         * notice that dmu_assign_arcbuf() is smart
                         * enough to recognize changed blocksize
                         * in this case it fallbacks to dmu_write() */
                        LASSERT(((unsigned long)lnb[i].lnb_data & 1) == 0);
                        /* buffer loaned for zerocopy, try to use it.
                         * notice that dmu_assign_arcbuf() is smart
                         * enough to recognize changed blocksize
                         * in this case it fallbacks to dmu_write() */
+                       abufsz = arc_buf_size(lnb[i].lnb_data);
+                       LASSERT(abufsz & PAGE_MASK);
+                       apages = abufsz / PAGE_SIZE;
+                       LASSERT(i + apages <= npages);
+                       /* these references to pages must be invalidated
+                        * to prevent access in osd_bufs_put() */
+                       for (j = 0; j < apages; j++)
+                               lnb[i + j].lnb_page = NULL;
                        dmu_assign_arcbuf(&obj->oo_dn->dn_bonus->db,
                                          lnb[i].lnb_file_offset,
                                          lnb[i].lnb_data, oh->ot_tx);
                        dmu_assign_arcbuf(&obj->oo_dn->dn_bonus->db,
                                          lnb[i].lnb_file_offset,
                                          lnb[i].lnb_data, oh->ot_tx);
@@ -815,11 +837,9 @@ static int osd_write_commit(const struct lu_env *env, struct dt_object *dt,
                         * will be releasing it - bad! */
                        lnb[i].lnb_data = NULL;
                        atomic_dec(&osd->od_zerocopy_loan);
                         * will be releasing it - bad! */
                        lnb[i].lnb_data = NULL;
                        atomic_dec(&osd->od_zerocopy_loan);
+                       iosize += abufsz;
                }
 
                }
 
-               if (new_size < lnb[i].lnb_file_offset + lnb[i].lnb_len)
-                       new_size = lnb[i].lnb_file_offset + lnb[i].lnb_len;
-               iosize += lnb[i].lnb_len;
        }
        up_read(&obj->oo_guard);
 
        }
        up_read(&obj->oo_guard);