/* * Copy from client-core's address space to the buffers specified * by the iovec upto total_size bytes. * NOTE: the iovector can either contain addresses which * can futher be kernel-space or user-space addresses. * or it can pointers to struct page's */ static int postcopy_buffers(int buffer_index, struct iov_iter *iter, size_t total_size) { int ret = 0; /* * copy data to application/kernel by pushing it out to * the iovec. NOTE; target buffers can be addresses or * struct page pointers. */ if (total_size) { ret = orangefs_bufmap_copy_to_iovec(iter, buffer_index, total_size); if (ret < 0) gossip_err("%s: Failed to copy-out buffers. Please make sure that the pvfs2-client is running (%ld)\n", __func__, (long)ret); } return ret; }
/* * Post and wait for the I/O upcall to finish */ static ssize_t wait_for_direct_io(enum ORANGEFS_io_type type, struct inode *inode, loff_t *offset, struct iov_iter *iter, size_t total_size, loff_t readahead_size) { struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode); struct orangefs_khandle *handle = &orangefs_inode->refn.khandle; struct orangefs_kernel_op_s *new_op = NULL; int buffer_index = -1; ssize_t ret; new_op = op_alloc(ORANGEFS_VFS_OP_FILE_IO); if (!new_op) return -ENOMEM; /* synchronous I/O */ new_op->upcall.req.io.readahead_size = readahead_size; new_op->upcall.req.io.io_type = type; new_op->upcall.req.io.refn = orangefs_inode->refn; populate_shared_memory: /* get a shared buffer index */ buffer_index = orangefs_bufmap_get(); if (buffer_index < 0) { ret = buffer_index; gossip_debug(GOSSIP_FILE_DEBUG, "%s: orangefs_bufmap_get failure (%zd)\n", __func__, ret); goto out; } gossip_debug(GOSSIP_FILE_DEBUG, "%s(%pU): GET op %p -> buffer_index %d\n", __func__, handle, new_op, buffer_index); new_op->uses_shared_memory = 1; new_op->upcall.req.io.buf_index = buffer_index; new_op->upcall.req.io.count = total_size; new_op->upcall.req.io.offset = *offset; gossip_debug(GOSSIP_FILE_DEBUG, "%s(%pU): offset: %llu total_size: %zd\n", __func__, handle, llu(*offset), total_size); /* * Stage 1: copy the buffers into client-core's address space */ if (type == ORANGEFS_IO_WRITE && total_size) { ret = orangefs_bufmap_copy_from_iovec(iter, buffer_index, total_size); if (ret < 0) { gossip_err("%s: Failed to copy-in buffers. Please make sure that the pvfs2-client is running. %ld\n", __func__, (long)ret); goto out; } } gossip_debug(GOSSIP_FILE_DEBUG, "%s(%pU): Calling post_io_request with tag (%llu)\n", __func__, handle, llu(new_op->tag)); /* Stage 2: Service the I/O operation */ ret = service_operation(new_op, type == ORANGEFS_IO_WRITE ? "file_write" : "file_read", get_interruptible_flag(inode)); /* * If service_operation() returns -EAGAIN #and# the operation was * purged from orangefs_request_list or htable_ops_in_progress, then * we know that the client was restarted, causing the shared memory * area to be wiped clean. To restart a write operation in this * case, we must re-copy the data from the user's iovec to a NEW * shared memory location. To restart a read operation, we must get * a new shared memory location. */ if (ret == -EAGAIN && op_state_purged(new_op)) { orangefs_bufmap_put(buffer_index); buffer_index = -1; if (type == ORANGEFS_IO_WRITE) iov_iter_revert(iter, total_size); gossip_debug(GOSSIP_FILE_DEBUG, "%s:going to repopulate_shared_memory.\n", __func__); goto populate_shared_memory; } if (ret < 0) { if (ret == -EINTR) { /* * We can't return EINTR if any data was written, * it's not POSIX. It is minimally acceptable * to give a partial write, the way NFS does. * * It would be optimal to return all or nothing, * but if a userspace write is bigger than * an IO buffer, and the interrupt occurs * between buffer writes, that would not be * possible. */ switch (new_op->op_state - OP_VFS_STATE_GIVEN_UP) { /* * If the op was waiting when the interrupt * occurred, then the client-core did not * trigger the write. */ case OP_VFS_STATE_WAITING: if (*offset == 0) ret = -EINTR; else ret = 0; break; /* * If the op was in progress when the interrupt * occurred, then the client-core was able to * trigger the write. */ case OP_VFS_STATE_INPROGR: ret = total_size; break; default: gossip_err("%s: unexpected op state :%d:.\n", __func__, new_op->op_state); ret = 0; break; } gossip_debug(GOSSIP_FILE_DEBUG, "%s: got EINTR, state:%d: %p\n", __func__, new_op->op_state, new_op); } else { gossip_err("%s: error in %s handle %pU, returning %zd\n", __func__, type == ORANGEFS_IO_READ ? "read from" : "write to", handle, ret); } if (orangefs_cancel_op_in_progress(new_op)) return ret; goto out; } /* * Stage 3: Post copy buffers from client-core's address space */ if (type == ORANGEFS_IO_READ && new_op->downcall.resp.io.amt_complete) { /* * NOTE: the iovector can either contain addresses which * can futher be kernel-space or user-space addresses. * or it can pointers to struct page's */ ret = orangefs_bufmap_copy_to_iovec(iter, buffer_index, new_op->downcall.resp.io.amt_complete); if (ret < 0) { gossip_err("%s: Failed to copy-out buffers. Please make sure that the pvfs2-client is running (%ld)\n", __func__, (long)ret); goto out; } } gossip_debug(GOSSIP_FILE_DEBUG, "%s(%pU): Amount %s, returned by the sys-io call:%d\n", __func__, handle, type == ORANGEFS_IO_READ ? "read" : "written", (int)new_op->downcall.resp.io.amt_complete); ret = new_op->downcall.resp.io.amt_complete; out: if (buffer_index >= 0) { orangefs_bufmap_put(buffer_index); gossip_debug(GOSSIP_FILE_DEBUG, "%s(%pU): PUT buffer_index %d\n", __func__, handle, buffer_index); buffer_index = -1; } op_release(new_op); return ret; }