Example #1
0
File: rw26.c Project: rread/lustre
static int ll_write_begin(struct file *file, struct address_space *mapping,
                          loff_t pos, unsigned len, unsigned flags,
                          struct page **pagep, void **fsdata)
{
    struct ll_cl_context *lcc;
    const struct lu_env  *env = NULL;
    struct cl_io   *io;
    struct cl_page *page = NULL;

    struct cl_object *clob = ll_i2info(mapping->host)->lli_clob;
    pgoff_t index = pos >> PAGE_SHIFT;
    struct page *vmpage = NULL;
    unsigned from = pos & (PAGE_SIZE - 1);
    unsigned to = from + len;
    int result = 0;
    ENTRY;

    CDEBUG(D_VFSTRACE, "Writing %lu of %d to %d bytes\n", index, from, len);

    lcc = ll_cl_find(file);
    if (lcc == NULL) {
        io = NULL;
        GOTO(out, result = -EIO);
    }

    env = lcc->lcc_env;
    io  = lcc->lcc_io;

    /* To avoid deadlock, try to lock page first. */
    vmpage = grab_cache_page_nowait(mapping, index);

    if (unlikely(vmpage == NULL ||
                 PageDirty(vmpage) || PageWriteback(vmpage))) {
        struct vvp_io *vio = vvp_env_io(env);
        struct cl_page_list *plist = &vio->u.write.vui_queue;

        /* if the page is already in dirty cache, we have to commit
        * the pages right now; otherwise, it may cause deadlock
        	 * because it holds page lock of a dirty page and request for
        	 * more grants. It's okay for the dirty page to be the first
        	 * one in commit page list, though. */
        if (vmpage != NULL && plist->pl_nr > 0) {
            unlock_page(vmpage);
            put_page(vmpage);
            vmpage = NULL;
        }

        /* commit pages and then wait for page lock */
        result = vvp_io_write_commit(env, io);
        if (result < 0)
            GOTO(out, result);

        if (vmpage == NULL) {
            vmpage = grab_cache_page_write_begin(mapping, index,
                                                 flags);
            if (vmpage == NULL)
                GOTO(out, result = -ENOMEM);
        }
    }

    page = cl_page_find(env, clob, vmpage->index, vmpage, CPT_CACHEABLE);
    if (IS_ERR(page))
        GOTO(out, result = PTR_ERR(page));

    lcc->lcc_page = page;
    lu_ref_add(&page->cp_reference, "cl_io", io);

    cl_page_assume(env, io, page);
    if (!PageUptodate(vmpage)) {
        /*
         * We're completely overwriting an existing page,
         * so _don't_ set it up to date until commit_write
         */
        if (from == 0 && to == PAGE_SIZE) {
            CL_PAGE_HEADER(D_PAGE, env, page, "full page write\n");
            POISON_PAGE(vmpage, 0x11);
        } else {
            /* TODO: can be optimized at OSC layer to check if it
             * is a lockless IO. In that case, it's not necessary
             * to read the data. */
            result = ll_prepare_partial_page(env, io, page);
            if (result == 0)
                SetPageUptodate(vmpage);
        }
    }
    if (result < 0)
        cl_page_unassume(env, io, page);
    EXIT;
out:
    if (result < 0) {
        if (vmpage != NULL) {
            unlock_page(vmpage);
            put_page(vmpage);
        }
        if (!IS_ERR_OR_NULL(page)) {
            lu_ref_del(&page->cp_reference, "cl_io", io);
            cl_page_put(env, page);
        }
        if (io)
            io->ci_result = result;
    } else {
        *pagep = vmpage;
        *fsdata = lcc;
    }
    RETURN(result);
}
Example #2
0
static int llu_queue_pio(const struct lu_env *env, struct cl_io *io,
                         struct llu_io_group *group,
                         char *buf, size_t count, loff_t pos)
{
        struct cl_object *obj = io->ci_obj;
        struct inode *inode = ccc_object_inode(obj);
        struct intnl_stat *st = llu_i2stat(inode);
        struct obd_export *exp = llu_i2obdexp(inode);
        struct page *page;
        int  rc = 0, ret_bytes = 0;
        struct cl_page *clp;
        struct cl_2queue *queue;
        ENTRY;

        if (!exp)
                RETURN(-EINVAL);

        queue = &io->ci_queue;
        cl_2queue_init(queue);


        /* prepare the pages array */
        do {
                unsigned long index, offset, bytes;

                offset = (pos & ~CFS_PAGE_MASK);
		index = pos >> PAGE_CACHE_SHIFT;
		bytes = PAGE_CACHE_SIZE - offset;
                if (bytes > count)
                        bytes = count;

                /* prevent read beyond file range */
                if (/* local_lock && */
                    io->ci_type == CIT_READ && pos + bytes >= st->st_size) {
                        if (pos >= st->st_size)
                                break;
                        bytes = st->st_size - pos;
                }

                /* prepare page for this index */
                page = llu_get_user_page(index, buf - offset, offset, bytes);
                if (!page) {
                        rc = -ENOMEM;
                        break;
                }

                clp = cl_page_find(env, obj,
                                   cl_index(obj, pos),
                                   page, CPT_TRANSIENT);

                if (IS_ERR(clp)) {
                        rc = PTR_ERR(clp);
                        break;
                }

                rc = cl_page_own(env, io, clp);
                if (rc) {
                        LASSERT(clp->cp_state == CPS_FREEING);
                        cl_page_put(env, clp);
                        break;
                }

                cl_2queue_add(queue, clp);

                /* drop the reference count for cl_page_find, so that the page
                 * will be freed in cl_2queue_fini. */
                cl_page_put(env, clp);

                cl_page_clip(env, clp, offset, offset+bytes);

                count -= bytes;
                pos += bytes;
                buf += bytes;

                group->lig_rwcount += bytes;
                ret_bytes += bytes;
                page++;
        } while (count);

        if (rc == 0) {
                enum cl_req_type iot;
                iot = io->ci_type == CIT_READ ? CRT_READ : CRT_WRITE;
		rc = cl_io_submit_sync(env, io, iot, queue, 0);
        }

        group->lig_rc = rc;

        cl_2queue_discard(env, io, queue);
        cl_2queue_disown(env, io, queue);
        cl_2queue_fini(env, queue);

        RETURN(ret_bytes);
}
Example #3
0
File: rw26.c Project: rread/lustre
ssize_t ll_direct_rw_pages(const struct lu_env *env, struct cl_io *io,
                           int rw, struct inode *inode,
                           struct ll_dio_pages *pv)
{
    struct cl_page    *clp;
    struct cl_2queue  *queue;
    struct cl_object  *obj = io->ci_obj;
    int i;
    ssize_t rc = 0;
    loff_t file_offset  = pv->ldp_start_offset;
    size_t size         = pv->ldp_size;
    int page_count      = pv->ldp_nr;
    struct page **pages = pv->ldp_pages;
    size_t page_size    = cl_page_size(obj);
    bool do_io;
    int  io_pages       = 0;
    ENTRY;

    queue = &io->ci_queue;
    cl_2queue_init(queue);
    for (i = 0; i < page_count; i++) {
        if (pv->ldp_offsets)
            file_offset = pv->ldp_offsets[i];

        LASSERT(!(file_offset & (page_size - 1)));
        clp = cl_page_find(env, obj, cl_index(obj, file_offset),
                           pv->ldp_pages[i], CPT_TRANSIENT);
        if (IS_ERR(clp)) {
            rc = PTR_ERR(clp);
            break;
        }

        rc = cl_page_own(env, io, clp);
        if (rc) {
            LASSERT(clp->cp_state == CPS_FREEING);
            cl_page_put(env, clp);
            break;
        }

        do_io = true;

        /* check the page type: if the page is a host page, then do
         * write directly */
        if (clp->cp_type == CPT_CACHEABLE) {
            struct page *vmpage = cl_page_vmpage(clp);
            struct page *src_page;
            struct page *dst_page;
            void       *src;
            void       *dst;

            src_page = (rw == WRITE) ? pages[i] : vmpage;
            dst_page = (rw == WRITE) ? vmpage : pages[i];

            src = ll_kmap_atomic(src_page, KM_USER0);
            dst = ll_kmap_atomic(dst_page, KM_USER1);
            memcpy(dst, src, min(page_size, size));
            ll_kunmap_atomic(dst, KM_USER1);
            ll_kunmap_atomic(src, KM_USER0);

            /* make sure page will be added to the transfer by
             * cl_io_submit()->...->vvp_page_prep_write(). */
            if (rw == WRITE)
                set_page_dirty(vmpage);

            if (rw == READ) {
                /* do not issue the page for read, since it
                 * may reread a ra page which has NOT uptodate
                 * bit set. */
                cl_page_disown(env, io, clp);
                do_io = false;
            }
        }

        if (likely(do_io)) {
            cl_2queue_add(queue, clp);

            /*
             * Set page clip to tell transfer formation engine
             * that page has to be sent even if it is beyond KMS.
             */
            cl_page_clip(env, clp, 0, min(size, page_size));

            ++io_pages;
        }

        /* drop the reference count for cl_page_find */
        cl_page_put(env, clp);
        size -= page_size;
        file_offset += page_size;
    }

    if (rc == 0 && io_pages) {
        rc = cl_io_submit_sync(env, io,
                               rw == READ ? CRT_READ : CRT_WRITE,
                               queue, 0);
    }
    if (rc == 0)
        rc = pv->ldp_size;

    cl_2queue_discard(env, io, queue);
    cl_2queue_disown(env, io, queue);
    cl_2queue_fini(env, queue);
    RETURN(rc);
}
Example #4
0
static int vvp_io_fault_start(const struct lu_env *env,
                              const struct cl_io_slice *ios)
{
	struct vvp_io       *vio     = cl2vvp_io(env, ios);
	struct cl_io        *io      = ios->cis_io;
	struct cl_object    *obj     = io->ci_obj;
	struct inode        *inode   = ccc_object_inode(obj);
	struct cl_fault_io  *fio     = &io->u.ci_fault;
	struct vvp_fault_io *cfio    = &vio->u.fault;
	loff_t               offset;
	int                  result  = 0;
	struct page          *vmpage  = NULL;
	struct cl_page      *page;
	loff_t               size;
	pgoff_t              last; /* last page in a file data region */

        if (fio->ft_executable &&
            LTIME_S(inode->i_mtime) != vio->u.fault.ft_mtime)
                CWARN("binary "DFID
                      " changed while waiting for the page fault lock\n",
                      PFID(lu_object_fid(&obj->co_lu)));

        /* offset of the last byte on the page */
        offset = cl_offset(obj, fio->ft_index + 1) - 1;
        LASSERT(cl_index(obj, offset) == fio->ft_index);
        result = ccc_prep_size(env, obj, io, 0, offset + 1, NULL);
        if (result != 0)
                return result;

	/* must return locked page */
	if (fio->ft_mkwrite) {
		LASSERT(cfio->ft_vmpage != NULL);
		lock_page(cfio->ft_vmpage);
	} else {
		result = vvp_io_kernel_fault(cfio);
		if (result != 0)
			return result;
	}

	vmpage = cfio->ft_vmpage;
	LASSERT(PageLocked(vmpage));

	if (OBD_FAIL_CHECK(OBD_FAIL_LLITE_FAULT_TRUNC_RACE))
		ll_invalidate_page(vmpage);

	size = i_size_read(inode);
        /* Though we have already held a cl_lock upon this page, but
         * it still can be truncated locally. */
	if (unlikely((vmpage->mapping != inode->i_mapping) ||
		     (page_offset(vmpage) > size))) {
                CDEBUG(D_PAGE, "llite: fault and truncate race happened!\n");

                /* return +1 to stop cl_io_loop() and ll_fault() will catch
                 * and retry. */
                GOTO(out, result = +1);
        }


	if (fio->ft_mkwrite ) {
		pgoff_t last_index;
		/*
		 * Capture the size while holding the lli_trunc_sem from above
		 * we want to make sure that we complete the mkwrite action
		 * while holding this lock. We need to make sure that we are
		 * not past the end of the file.
		 */
		last_index = cl_index(obj, size - 1);
		if (last_index < fio->ft_index) {
			CDEBUG(D_PAGE,
				"llite: mkwrite and truncate race happened: "
				"%p: 0x%lx 0x%lx\n",
				vmpage->mapping,fio->ft_index,last_index);
			/*
			 * We need to return if we are
			 * passed the end of the file. This will propagate
			 * up the call stack to ll_page_mkwrite where
			 * we will return VM_FAULT_NOPAGE. Any non-negative
			 * value returned here will be silently
			 * converted to 0. If the vmpage->mapping is null
			 * the error code would be converted back to ENODATA
			 * in ll_page_mkwrite0. Thus we return -ENODATA
			 * to handle both cases
			 */
			GOTO(out, result = -ENODATA);
		}
	}

        page = cl_page_find(env, obj, fio->ft_index, vmpage, CPT_CACHEABLE);
        if (IS_ERR(page))
                GOTO(out, result = PTR_ERR(page));

        /* if page is going to be written, we should add this page into cache
         * earlier. */
        if (fio->ft_mkwrite) {
                wait_on_page_writeback(vmpage);
                if (set_page_dirty(vmpage)) {
                        struct ccc_page *cp;

                        /* vvp_page_assume() calls wait_on_page_writeback(). */
                        cl_page_assume(env, io, page);

                        cp = cl2ccc_page(cl_page_at(page, &vvp_device_type));
                        vvp_write_pending(cl2ccc(obj), cp);

                        /* Do not set Dirty bit here so that in case IO is
                         * started before the page is really made dirty, we
                         * still have chance to detect it. */
                        result = cl_page_cache_add(env, io, page, CRT_WRITE);
			LASSERT(cl_page_is_owned(page, io));

			vmpage = NULL;
			if (result < 0) {
				cl_page_unmap(env, io, page);
				cl_page_discard(env, io, page);
				cl_page_disown(env, io, page);

				cl_page_put(env, page);

				/* we're in big trouble, what can we do now? */
				if (result == -EDQUOT)
					result = -ENOSPC;
				GOTO(out, result);
			} else
				cl_page_disown(env, io, page);
		}
	}

	last = cl_index(obj, size - 1);
	/*
	 * The ft_index is only used in the case of
	 * a mkwrite action. We need to check
	 * our assertions are correct, since
	 * we should have caught this above
	 */
	LASSERT(!fio->ft_mkwrite || fio->ft_index <= last);
        if (fio->ft_index == last)
                /*
                 * Last page is mapped partially.
                 */
                fio->ft_nob = size - cl_offset(obj, fio->ft_index);
        else
                fio->ft_nob = cl_page_size(obj);

        lu_ref_add(&page->cp_reference, "fault", io);
        fio->ft_page = page;
        EXIT;

out:
	/* return unlocked vmpage to avoid deadlocking */
	if (vmpage != NULL)
		unlock_page(vmpage);
	cfio->fault.ft_flags &= ~VM_FAULT_LOCKED;
	return result;
}