Whamcloud - gitweb
LU-4357 libcfs: restore __GFP_WAIT flag to memalloc calls
[fs/lustre-release.git] / lustre / osc / osc_io.c
index d1be316..5778a4b 100644 (file)
@@ -67,14 +67,18 @@ static struct osc_io *cl2osc_io(const struct lu_env *env,
         return oio;
 }
 
-static struct osc_page *osc_cl_page_osc(struct cl_page *page)
+static struct osc_page *osc_cl_page_osc(struct cl_page *page,
+                                       struct osc_object *osc)
 {
-        const struct cl_page_slice *slice;
+       const struct cl_page_slice *slice;
 
-        slice = cl_page_at(page, &osc_device_type);
-        LASSERT(slice != NULL);
+       if (osc != NULL)
+               slice = cl_object_page_slice(&osc->oo_cl, page);
+       else
+               slice = cl_page_at(page, &osc_device_type);
+       LASSERT(slice != NULL);
 
-        return cl2osc_page(slice);
+       return cl2osc_page(slice);
 }
 
 
@@ -137,8 +141,8 @@ static int osc_io_submit(const struct lu_env *env,
                 io = page->cp_owner;
                 LASSERT(io != NULL);
 
-                opg = osc_cl_page_osc(page);
-                oap = &opg->ops_oap;
+               opg = osc_cl_page_osc(page, osc);
+               oap = &opg->ops_oap;
                LASSERT(osc == oap->oap_obj);
 
                if (!cfs_list_empty(&oap->oap_pending_item) ||
@@ -187,6 +191,13 @@ static int osc_io_submit(const struct lu_env *env,
        return qout->pl_nr > 0 ? 0 : result;
 }
 
+/**
+ * This is called when a page is accessed within file in a way that creates
+ * new page, if one were missing (i.e., if there were a hole at that place in
+ * the file, or accessed page is beyond the current file size).
+ *
+ * Expand stripe KMS if necessary.
+ */
 static void osc_page_touch_at(const struct lu_env *env,
                               struct cl_object *obj, pgoff_t idx, unsigned to)
 {
@@ -210,7 +221,8 @@ static void osc_page_touch_at(const struct lu_env *env,
                kms > loi->loi_kms ? "" : "not ", loi->loi_kms, kms,
                loi->loi_lvb.lvb_size);
 
-        valid = 0;
+       attr->cat_mtime = attr->cat_ctime = LTIME_S(CFS_CURRENT_TIME);
+       valid = CAT_MTIME | CAT_CTIME;
         if (kms > loi->loi_kms) {
                 attr->cat_kms = kms;
                 valid |= CAT_KMS;
@@ -223,92 +235,127 @@ static void osc_page_touch_at(const struct lu_env *env,
         cl_object_attr_unlock(obj);
 }
 
-/**
- * This is called when a page is accessed within file in a way that creates
- * new page, if one were missing (i.e., if there were a hole at that place in
- * the file, or accessed page is beyond the current file size). Examples:
- * ->commit_write() and ->nopage() methods.
- *
- * Expand stripe KMS if necessary.
- */
-static void osc_page_touch(const struct lu_env *env,
-                           struct osc_page *opage, unsigned to)
+static int osc_io_commit_async(const struct lu_env *env,
+                               const struct cl_io_slice *ios,
+                               struct cl_page_list *qin, int from, int to,
+                               cl_commit_cbt cb)
 {
-        struct cl_page    *page = opage->ops_cl.cpl_page;
-        struct cl_object  *obj  = opage->ops_cl.cpl_obj;
+       struct cl_io    *io = ios->cis_io;
+       struct osc_io   *oio = cl2osc_io(env, ios);
+       struct osc_object *osc = cl2osc(ios->cis_obj);
+       struct cl_page  *page;
+       struct cl_page  *last_page;
+       struct osc_page *opg;
+       int result = 0;
+       ENTRY;
 
-        osc_page_touch_at(env, obj, page->cp_index, to);
-}
+       LASSERT(qin->pl_nr > 0);
 
-/**
- * Implements cl_io_operations::cio_prepare_write() method for osc layer.
- *
- * \retval -EIO transfer initiated against this osc will most likely fail
- * \retval 0    transfer initiated against this osc will most likely succeed.
- *
- * The reason for this check is to immediately return an error to the caller
- * in the case of a deactivated import. Note, that import can be deactivated
- * later, while pages, dirtied by this IO, are still in the cache, but this is
- * irrelevant, because that would still return an error to the application (if
- * it does fsync), but many applications don't do fsync because of performance
- * issues, and we wanted to return an -EIO at write time to notify the
- * application.
- */
-static int osc_io_prepare_write(const struct lu_env *env,
-                                const struct cl_io_slice *ios,
-                                const struct cl_page_slice *slice,
-                                unsigned from, unsigned to)
-{
-        struct osc_device *dev = lu2osc_dev(slice->cpl_obj->co_lu.lo_dev);
-        struct obd_import *imp = class_exp2cliimp(dev->od_exp);
-        struct osc_io     *oio = cl2osc_io(env, ios);
-        int result = 0;
-        ENTRY;
+       /* Handle partial page cases */
+       last_page = cl_page_list_last(qin);
+       if (oio->oi_lockless) {
+               page = cl_page_list_first(qin);
+               if (page == last_page) {
+                       cl_page_clip(env, page, from, to);
+               } else {
+                       if (from != 0)
+                               cl_page_clip(env, page, from, PAGE_SIZE);
+                       if (to != PAGE_SIZE)
+                               cl_page_clip(env, last_page, 0, to);
+               }
+       }
 
-        /*
-         * This implements OBD_BRW_CHECK logic from old client.
-         */
+       while (qin->pl_nr > 0) {
+               struct osc_async_page *oap;
 
-        if (imp == NULL || imp->imp_invalid)
-                result = -EIO;
-        if (result == 0 && oio->oi_lockless)
-                /* this page contains `invalid' data, but who cares?
-                 * nobody can access the invalid data.
-                 * in osc_io_commit_write(), we're going to write exact
-                 * [from, to) bytes of this page to OST. -jay */
-                cl_page_export(env, slice->cpl_page, 1);
+               page = cl_page_list_first(qin);
+               opg = osc_cl_page_osc(page, osc);
+               oap = &opg->ops_oap;
 
-        RETURN(result);
+               if (!cfs_list_empty(&oap->oap_rpc_item)) {
+                       CDEBUG(D_CACHE, "Busy oap %p page %p for submit.\n",
+                              oap, opg);
+                       result = -EBUSY;
+                       break;
+               }
+
+               /* The page may be already in dirty cache. */
+               if (cfs_list_empty(&oap->oap_pending_item)) {
+                       result = osc_page_cache_add(env, &opg->ops_cl, io);
+                       if (result != 0)
+                               break;
+               }
+
+               osc_page_touch_at(env, osc2cl(osc), osc_index(opg),
+                                 page == last_page ? to : PAGE_SIZE);
+
+               cl_page_list_del(env, qin, page);
+
+               (*cb)(env, io, page);
+               /* Can't access page any more. Page can be in transfer and
+                * complete at any time. */
+       }
+
+       /* for sync write, kernel will wait for this page to be flushed before
+        * osc_io_end() is called, so release it earlier.
+        * for mkwrite(), it's known there is no further pages. */
+       if (cl_io_is_sync_write(io) && oio->oi_active != NULL) {
+               osc_extent_release(env, oio->oi_active);
+               oio->oi_active = NULL;
+       }
+
+       CDEBUG(D_INFO, "%d %d\n", qin->pl_nr, result);
+       RETURN(result);
 }
 
-static int osc_io_commit_write(const struct lu_env *env,
-                               const struct cl_io_slice *ios,
-                               const struct cl_page_slice *slice,
-                               unsigned from, unsigned to)
+static int osc_io_rw_iter_init(const struct lu_env *env,
+                               const struct cl_io_slice *ios)
 {
-        struct osc_io         *oio = cl2osc_io(env, ios);
-        struct osc_page       *opg = cl2osc_page(slice);
-        struct osc_object     *obj = cl2osc(opg->ops_cl.cpl_obj);
-        struct osc_async_page *oap = &opg->ops_oap;
-        ENTRY;
+       struct cl_io *io = ios->cis_io;
+       struct osc_io *oio = osc_env_io(env);
+       struct osc_object *osc = cl2osc(ios->cis_obj);
+       struct client_obd *cli = osc_cli(osc);
+       unsigned long c;
+       unsigned int npages;
+       unsigned int max_pages;
+       ENTRY;
 
-        LASSERT(to > 0);
-        /*
-         * XXX instead of calling osc_page_touch() here and in
-         * osc_io_fault_start() it might be more logical to introduce
-         * cl_page_touch() method, that generic cl_io_commit_write() and page
-         * fault code calls.
-         */
-       osc_page_touch(env, cl2osc_page(slice), to);
-       if (!client_is_remote(osc_export(obj)) &&
-           cfs_capable(CAP_SYS_RESOURCE))
-               oap->oap_brw_flags |= OBD_BRW_NOQUOTA;
+       if (cl_io_is_append(io))
+               RETURN(0);
 
-        if (oio->oi_lockless)
-                /* see osc_io_prepare_write() for lockless io handling. */
-                cl_page_clip(env, slice->cpl_page, from, to);
+       npages = io->u.ci_rw.crw_count >> PAGE_CACHE_SHIFT;
+       if (io->u.ci_rw.crw_pos & ~CFS_PAGE_MASK)
+               ++npages;
 
-        RETURN(0);
+       max_pages = cli->cl_max_pages_per_rpc * cli->cl_max_rpcs_in_flight;
+       if (npages > max_pages)
+               npages = max_pages;
+
+       c = atomic_read(cli->cl_lru_left);
+       if (c < npages && osc_lru_reclaim(cli) > 0)
+               c = atomic_read(cli->cl_lru_left);
+       while (c >= npages) {
+               if (c == atomic_cmpxchg(cli->cl_lru_left, c, c - npages)) {
+                       oio->oi_lru_reserved = npages;
+                       break;
+               }
+               c = atomic_read(cli->cl_lru_left);
+       }
+
+       RETURN(0);
+}
+
+static void osc_io_rw_iter_fini(const struct lu_env *env,
+                               const struct cl_io_slice *ios)
+{
+       struct osc_io *oio = osc_env_io(env);
+       struct osc_object *osc = cl2osc(ios->cis_obj);
+       struct client_obd *cli = osc_cli(osc);
+
+       if (oio->oi_lru_reserved > 0) {
+               atomic_add(oio->oi_lru_reserved, cli->cl_lru_left);
+               oio->oi_lru_reserved = 0;
+       }
 }
 
 static int osc_io_fault_start(const struct lu_env *env,
@@ -348,31 +395,22 @@ static int osc_async_upcall(void *a, int rc)
  * Checks that there are no pages being written in the extent being truncated.
  */
 static int trunc_check_cb(const struct lu_env *env, struct cl_io *io,
-                         struct cl_page *page, void *cbdata)
+                         struct osc_page *ops , void *cbdata)
 {
-       const struct cl_page_slice *slice;
-       struct osc_page *ops;
+       struct cl_page *page = ops->ops_cl.cpl_page;
        struct osc_async_page *oap;
        __u64 start = *(__u64 *)cbdata;
 
-       slice = cl_page_at(page, &osc_device_type);
-       LASSERT(slice != NULL);
-       ops = cl2osc_page(slice);
        oap = &ops->ops_oap;
-
        if (oap->oap_cmd & OBD_BRW_WRITE &&
            !cfs_list_empty(&oap->oap_pending_item))
                CL_PAGE_DEBUG(D_ERROR, env, page, "exists " LPU64 "/%s.\n",
                                start, current->comm);
 
 #ifdef __linux__
-       {
-               struct page *vmpage = cl_page_vmpage(env, page);
-               if (PageLocked(vmpage))
-                       CDEBUG(D_CACHE, "page %p index %lu locked for %d.\n",
-                              ops, page->cp_index,
-                              (oap->oap_cmd & OBD_BRW_RWMASK));
-       }
+       if (PageLocked(page->cp_vmpage))
+               CDEBUG(D_CACHE, "page %p index %lu locked for %d.\n",
+                      ops, osc_index(ops), oap->oap_cmd & OBD_BRW_RWMASK);
 #endif
 
        return CLP_GANG_OKAY;
@@ -392,8 +430,9 @@ static void osc_trunc_check(const struct lu_env *env, struct cl_io *io,
         /*
          * Complain if there are pages in the truncated region.
          */
-       cl_page_gang_lookup(env, clob, io, start + partial, CL_PAGE_EOF,
-                           trunc_check_cb, (void *)&size);
+       osc_page_gang_lookup(env, io, cl2osc(clob),
+                               start + partial, CL_PAGE_EOF,
+                               trunc_check_cb, (void *)&size);
 }
 #else /* __KERNEL__ */
 static void osc_trunc_check(const struct lu_env *env, struct cl_io *io,
@@ -671,21 +710,23 @@ static void osc_io_end(const struct lu_env *env,
 }
 
 static const struct cl_io_operations osc_io_ops = {
-        .op = {
-                [CIT_READ] = {
-                        .cio_start  = osc_io_read_start,
-                        .cio_fini   = osc_io_fini
-                },
-                [CIT_WRITE] = {
-                        .cio_start  = osc_io_write_start,
+       .op = {
+               [CIT_READ] = {
+                       .cio_start  = osc_io_read_start,
+                       .cio_fini   = osc_io_fini
+               },
+               [CIT_WRITE] = {
+                       .cio_iter_init = osc_io_rw_iter_init,
+                       .cio_iter_fini = osc_io_rw_iter_fini,
+                       .cio_start  = osc_io_write_start,
                        .cio_end    = osc_io_end,
-                        .cio_fini   = osc_io_fini
-                },
-                [CIT_SETATTR] = {
-                        .cio_start  = osc_io_setattr_start,
-                        .cio_end    = osc_io_setattr_end
-                },
-                [CIT_FAULT] = {
+                       .cio_fini   = osc_io_fini
+               },
+               [CIT_SETATTR] = {
+                       .cio_start  = osc_io_setattr_start,
+                       .cio_end    = osc_io_setattr_end
+               },
+               [CIT_FAULT] = {
                        .cio_start  = osc_io_fault_start,
                        .cio_end    = osc_io_end,
                        .cio_fini   = osc_io_fini
@@ -695,20 +736,12 @@ static const struct cl_io_operations osc_io_ops = {
                        .cio_end    = osc_io_fsync_end,
                        .cio_fini   = osc_io_fini
                },
-                [CIT_MISC] = {
-                        .cio_fini   = osc_io_fini
-                }
-        },
-        .req_op = {
-                 [CRT_READ] = {
-                         .cio_submit    = osc_io_submit
-                 },
-                 [CRT_WRITE] = {
-                         .cio_submit    = osc_io_submit
-                 }
-         },
-        .cio_prepare_write = osc_io_prepare_write,
-        .cio_commit_write  = osc_io_commit_write
+               [CIT_MISC] = {
+                       .cio_fini   = osc_io_fini
+               }
+       },
+       .cio_submit                 = osc_io_submit,
+       .cio_commit_async           = osc_io_commit_async
 };
 
 /*****************************************************************************
@@ -776,18 +809,21 @@ static void osc_req_attr_set(const struct lu_env *env,
                oa->o_valid |= OBD_MD_FLID;
        }
        if (flags & OBD_MD_FLHANDLE) {
-                clerq = slice->crs_req;
-                LASSERT(!cfs_list_empty(&clerq->crq_pages));
-                apage = container_of(clerq->crq_pages.next,
-                                     struct cl_page, cp_flight);
-                opg = osc_cl_page_osc(apage);
-                apage = opg->ops_cl.cpl_page; /* now apage is a sub-page */
-                lock = cl_lock_at_page(env, apage->cp_obj, apage, NULL, 1, 1);
-                if (lock == NULL) {
-                        struct cl_object_header *head;
-                        struct cl_lock          *scan;
-
-                        head = cl_object_header(apage->cp_obj);
+               struct cl_object *subobj;
+
+               clerq = slice->crs_req;
+               LASSERT(!cfs_list_empty(&clerq->crq_pages));
+               apage = container_of(clerq->crq_pages.next,
+                                    struct cl_page, cp_flight);
+               opg = osc_cl_page_osc(apage, NULL);
+               subobj = opg->ops_cl.cpl_obj;
+               lock = cl_lock_at_pgoff(env, subobj, osc_index(opg),
+                                       NULL, 1, 1);
+               if (lock == NULL) {
+                       struct cl_object_header *head;
+                       struct cl_lock          *scan;
+
+                       head = cl_object_header(subobj);
                         cfs_list_for_each_entry(scan, &head->coh_locks,
                                                 cll_linkage)
                                 CL_LOCK_DEBUG(D_ERROR, env, scan,
@@ -828,18 +864,18 @@ int osc_io_init(const struct lu_env *env,
 }
 
 int osc_req_init(const struct lu_env *env, struct cl_device *dev,
-                 struct cl_req *req)
+                struct cl_req *req)
 {
-        struct osc_req *or;
-        int result;
-
-       OBD_SLAB_ALLOC_PTR_GFP(or, osc_req_kmem, __GFP_IO);
-        if (or != NULL) {
-                cl_req_slice_add(req, &or->or_cl, dev, &osc_req_ops);
-                result = 0;
-        } else
-                result = -ENOMEM;
-        return result;
+       struct osc_req *or;
+       int result;
+
+       OBD_SLAB_ALLOC_PTR_GFP(or, osc_req_kmem, GFP_NOFS);
+       if (or != NULL) {
+               cl_req_slice_add(req, &or->or_cl, dev, &osc_req_ops);
+               result = 0;
+       } else
+               result = -ENOMEM;
+       return result;
 }
 
 /** @} osc */