mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-01 10:43:43 +00:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs updates from Al Viro: "The first vfs pile, with deep apologies for being very late in this window. Assorted cleanups and fixes, plus a large preparatory part of iov_iter work. There's a lot more of that, but it'll probably go into the next merge window - it *does* shape up nicely, removes a lot of boilerplate, gets rid of locking inconsistencie between aio_write and splice_write and I hope to get Kent's direct-io rewrite merged into the same queue, but some of the stuff after this point is having (mostly trivial) conflicts with the things already merged into mainline and with some I want more testing. This one passes LTP and xfstests without regressions, in addition to usual beating. BTW, readahead02 in ltp syscalls testsuite has started giving failures since "mm/readahead.c: fix readahead failure for memoryless NUMA nodes and limit readahead pages" - might be a false positive, might be a real regression..." * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (63 commits) missing bits of "splice: fix racy pipe->buffers uses" cifs: fix the race in cifs_writev() ceph_sync_{,direct_}write: fix an oops on ceph_osdc_new_request() failure kill generic_file_buffered_write() ocfs2_file_aio_write(): switch to generic_perform_write() ceph_aio_write(): switch to generic_perform_write() xfs_file_buffered_aio_write(): switch to generic_perform_write() export generic_perform_write(), start getting rid of generic_file_buffer_write() generic_file_direct_write(): get rid of ppos argument btrfs_file_aio_write(): get rid of ppos kill the 5th argument of generic_file_buffered_write() kill the 4th argument of __generic_file_aio_write() lustre: don't open-code kernel_recvmsg() ocfs2: don't open-code kernel_recvmsg() drbd: don't open-code kernel_recvmsg() constify blk_rq_map_user_iov() and friends lustre: switch to kernel_sendmsg() ocfs2: don't open-code kernel_sendmsg() take iov_iter stuff to mm/iov_iter.c process_vm_access: tidy up a bit ...
This commit is contained in:
commit
5166701b36
@ -202,7 +202,7 @@ prototypes:
|
|||||||
unsigned long *);
|
unsigned long *);
|
||||||
int (*migratepage)(struct address_space *, struct page *, struct page *);
|
int (*migratepage)(struct address_space *, struct page *, struct page *);
|
||||||
int (*launder_page)(struct page *);
|
int (*launder_page)(struct page *);
|
||||||
int (*is_partially_uptodate)(struct page *, read_descriptor_t *, unsigned long);
|
int (*is_partially_uptodate)(struct page *, unsigned long, unsigned long);
|
||||||
int (*error_remove_page)(struct address_space *, struct page *);
|
int (*error_remove_page)(struct address_space *, struct page *);
|
||||||
int (*swap_activate)(struct file *);
|
int (*swap_activate)(struct file *);
|
||||||
int (*swap_deactivate)(struct file *);
|
int (*swap_deactivate)(struct file *);
|
||||||
|
@ -596,7 +596,7 @@ struct address_space_operations {
|
|||||||
/* migrate the contents of a page to the specified target */
|
/* migrate the contents of a page to the specified target */
|
||||||
int (*migratepage) (struct page *, struct page *);
|
int (*migratepage) (struct page *, struct page *);
|
||||||
int (*launder_page) (struct page *);
|
int (*launder_page) (struct page *);
|
||||||
int (*is_partially_uptodate) (struct page *, read_descriptor_t *,
|
int (*is_partially_uptodate) (struct page *, unsigned long,
|
||||||
unsigned long);
|
unsigned long);
|
||||||
void (*is_dirty_writeback) (struct page *, bool *, bool *);
|
void (*is_dirty_writeback) (struct page *, bool *, bool *);
|
||||||
int (*error_remove_page) (struct mapping *mapping, struct page *page);
|
int (*error_remove_page) (struct mapping *mapping, struct page *page);
|
||||||
|
@ -70,7 +70,7 @@ static inline void kunmap(struct page *page)
|
|||||||
* be used in IRQ contexts, so in some (very limited) cases we need
|
* be used in IRQ contexts, so in some (very limited) cases we need
|
||||||
* it.
|
* it.
|
||||||
*/
|
*/
|
||||||
static inline unsigned long kmap_atomic(struct page *page)
|
static inline void *kmap_atomic(struct page *page)
|
||||||
{
|
{
|
||||||
unsigned long vaddr;
|
unsigned long vaddr;
|
||||||
int idx, type;
|
int idx, type;
|
||||||
@ -89,7 +89,7 @@ static inline unsigned long kmap_atomic(struct page *page)
|
|||||||
set_pte(kmap_pte - idx, mk_pte(page, kmap_prot));
|
set_pte(kmap_pte - idx, mk_pte(page, kmap_prot));
|
||||||
local_flush_tlb_one(vaddr);
|
local_flush_tlb_one(vaddr);
|
||||||
|
|
||||||
return vaddr;
|
return (void *)vaddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void __kunmap_atomic(unsigned long vaddr)
|
static inline void __kunmap_atomic(unsigned long vaddr)
|
||||||
|
@ -1244,7 +1244,6 @@ CONFIG_DEBUG_SPINLOCK_SLEEP=y
|
|||||||
CONFIG_DEBUG_HIGHMEM=y
|
CONFIG_DEBUG_HIGHMEM=y
|
||||||
CONFIG_DEBUG_INFO=y
|
CONFIG_DEBUG_INFO=y
|
||||||
CONFIG_DEBUG_VM=y
|
CONFIG_DEBUG_VM=y
|
||||||
CONFIG_DEBUG_WRITECOUNT=y
|
|
||||||
CONFIG_DEBUG_LIST=y
|
CONFIG_DEBUG_LIST=y
|
||||||
CONFIG_DEBUG_SG=y
|
CONFIG_DEBUG_SG=y
|
||||||
# CONFIG_RCU_CPU_STALL_DETECTOR is not set
|
# CONFIG_RCU_CPU_STALL_DETECTOR is not set
|
||||||
|
@ -174,7 +174,6 @@ CONFIG_DETECT_HUNG_TASK=y
|
|||||||
CONFIG_PROVE_LOCKING=y
|
CONFIG_PROVE_LOCKING=y
|
||||||
CONFIG_DEBUG_LOCKDEP=y
|
CONFIG_DEBUG_LOCKDEP=y
|
||||||
CONFIG_DEBUG_INFO=y
|
CONFIG_DEBUG_INFO=y
|
||||||
CONFIG_DEBUG_WRITECOUNT=y
|
|
||||||
CONFIG_DEBUG_MEMORY_INIT=y
|
CONFIG_DEBUG_MEMORY_INIT=y
|
||||||
CONFIG_DEBUG_LIST=y
|
CONFIG_DEBUG_LIST=y
|
||||||
CONFIG_RCU_CPU_STALL_TIMEOUT=60
|
CONFIG_RCU_CPU_STALL_TIMEOUT=60
|
||||||
|
@ -581,7 +581,6 @@ CONFIG_LOCK_STAT=y
|
|||||||
CONFIG_DEBUG_LOCKDEP=y
|
CONFIG_DEBUG_LOCKDEP=y
|
||||||
CONFIG_DEBUG_ATOMIC_SLEEP=y
|
CONFIG_DEBUG_ATOMIC_SLEEP=y
|
||||||
CONFIG_DEBUG_LOCKING_API_SELFTESTS=y
|
CONFIG_DEBUG_LOCKING_API_SELFTESTS=y
|
||||||
CONFIG_DEBUG_WRITECOUNT=y
|
|
||||||
CONFIG_DEBUG_LIST=y
|
CONFIG_DEBUG_LIST=y
|
||||||
CONFIG_DEBUG_SG=y
|
CONFIG_DEBUG_SG=y
|
||||||
CONFIG_DEBUG_NOTIFIERS=y
|
CONFIG_DEBUG_NOTIFIERS=y
|
||||||
|
@ -128,7 +128,6 @@ CONFIG_DEBUG_MUTEXES=y
|
|||||||
CONFIG_DEBUG_SPINLOCK_SLEEP=y
|
CONFIG_DEBUG_SPINLOCK_SLEEP=y
|
||||||
CONFIG_DEBUG_INFO=y
|
CONFIG_DEBUG_INFO=y
|
||||||
CONFIG_DEBUG_VM=y
|
CONFIG_DEBUG_VM=y
|
||||||
CONFIG_DEBUG_WRITECOUNT=y
|
|
||||||
CONFIG_DEBUG_LIST=y
|
CONFIG_DEBUG_LIST=y
|
||||||
CONFIG_DEBUG_SG=y
|
CONFIG_DEBUG_SG=y
|
||||||
CONFIG_FRAME_POINTER=y
|
CONFIG_FRAME_POINTER=y
|
||||||
|
@ -627,7 +627,6 @@ CONFIG_SCHED_DEBUG=y
|
|||||||
# CONFIG_DEBUG_KOBJECT is not set
|
# CONFIG_DEBUG_KOBJECT is not set
|
||||||
# CONFIG_DEBUG_INFO is not set
|
# CONFIG_DEBUG_INFO is not set
|
||||||
# CONFIG_DEBUG_VM is not set
|
# CONFIG_DEBUG_VM is not set
|
||||||
# CONFIG_DEBUG_WRITECOUNT is not set
|
|
||||||
# CONFIG_DEBUG_MEMORY_INIT is not set
|
# CONFIG_DEBUG_MEMORY_INIT is not set
|
||||||
# CONFIG_DEBUG_LIST is not set
|
# CONFIG_DEBUG_LIST is not set
|
||||||
# CONFIG_DEBUG_SG is not set
|
# CONFIG_DEBUG_SG is not set
|
||||||
|
@ -569,7 +569,6 @@ CONFIG_DEBUG_SPINLOCK_SLEEP=y
|
|||||||
# CONFIG_DEBUG_INFO is not set
|
# CONFIG_DEBUG_INFO is not set
|
||||||
# CONFIG_DEBUG_VM is not set
|
# CONFIG_DEBUG_VM is not set
|
||||||
CONFIG_DEBUG_NOMMU_REGIONS=y
|
CONFIG_DEBUG_NOMMU_REGIONS=y
|
||||||
# CONFIG_DEBUG_WRITECOUNT is not set
|
|
||||||
# CONFIG_DEBUG_MEMORY_INIT is not set
|
# CONFIG_DEBUG_MEMORY_INIT is not set
|
||||||
# CONFIG_DEBUG_LIST is not set
|
# CONFIG_DEBUG_LIST is not set
|
||||||
# CONFIG_DEBUG_SG is not set
|
# CONFIG_DEBUG_SG is not set
|
||||||
|
@ -188,7 +188,7 @@ EXPORT_SYMBOL(blk_rq_map_user);
|
|||||||
* unmapping.
|
* unmapping.
|
||||||
*/
|
*/
|
||||||
int blk_rq_map_user_iov(struct request_queue *q, struct request *rq,
|
int blk_rq_map_user_iov(struct request_queue *q, struct request *rq,
|
||||||
struct rq_map_data *map_data, struct sg_iovec *iov,
|
struct rq_map_data *map_data, const struct sg_iovec *iov,
|
||||||
int iov_count, unsigned int len, gfp_t gfp_mask)
|
int iov_count, unsigned int len, gfp_t gfp_mask)
|
||||||
{
|
{
|
||||||
struct bio *bio;
|
struct bio *bio;
|
||||||
|
@ -469,24 +469,14 @@ static void drbd_wait_ee_list_empty(struct drbd_device *device,
|
|||||||
|
|
||||||
static int drbd_recv_short(struct socket *sock, void *buf, size_t size, int flags)
|
static int drbd_recv_short(struct socket *sock, void *buf, size_t size, int flags)
|
||||||
{
|
{
|
||||||
mm_segment_t oldfs;
|
|
||||||
struct kvec iov = {
|
struct kvec iov = {
|
||||||
.iov_base = buf,
|
.iov_base = buf,
|
||||||
.iov_len = size,
|
.iov_len = size,
|
||||||
};
|
};
|
||||||
struct msghdr msg = {
|
struct msghdr msg = {
|
||||||
.msg_iovlen = 1,
|
|
||||||
.msg_iov = (struct iovec *)&iov,
|
|
||||||
.msg_flags = (flags ? flags : MSG_WAITALL | MSG_NOSIGNAL)
|
.msg_flags = (flags ? flags : MSG_WAITALL | MSG_NOSIGNAL)
|
||||||
};
|
};
|
||||||
int rv;
|
return kernel_recvmsg(sock, &msg, &iov, 1, size, msg.msg_flags);
|
||||||
|
|
||||||
oldfs = get_fs();
|
|
||||||
set_fs(KERNEL_DS);
|
|
||||||
rv = sock_recvmsg(sock, &msg, size, msg.msg_flags);
|
|
||||||
set_fs(oldfs);
|
|
||||||
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int drbd_recv(struct drbd_connection *connection, void *buf, size_t size)
|
static int drbd_recv(struct drbd_connection *connection, void *buf, size_t size)
|
||||||
|
@ -630,37 +630,29 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
case NBD_CLEAR_SOCK: {
|
case NBD_CLEAR_SOCK: {
|
||||||
struct file *file;
|
struct socket *sock = nbd->sock;
|
||||||
|
|
||||||
nbd->sock = NULL;
|
nbd->sock = NULL;
|
||||||
file = nbd->file;
|
|
||||||
nbd->file = NULL;
|
|
||||||
nbd_clear_que(nbd);
|
nbd_clear_que(nbd);
|
||||||
BUG_ON(!list_empty(&nbd->queue_head));
|
BUG_ON(!list_empty(&nbd->queue_head));
|
||||||
BUG_ON(!list_empty(&nbd->waiting_queue));
|
BUG_ON(!list_empty(&nbd->waiting_queue));
|
||||||
kill_bdev(bdev);
|
kill_bdev(bdev);
|
||||||
if (file)
|
if (sock)
|
||||||
fput(file);
|
sockfd_put(sock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
case NBD_SET_SOCK: {
|
case NBD_SET_SOCK: {
|
||||||
struct file *file;
|
struct socket *sock;
|
||||||
if (nbd->file)
|
int err;
|
||||||
|
if (nbd->sock)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
file = fget(arg);
|
sock = sockfd_lookup(arg, &err);
|
||||||
if (file) {
|
if (sock) {
|
||||||
struct inode *inode = file_inode(file);
|
nbd->sock = sock;
|
||||||
if (S_ISSOCK(inode->i_mode)) {
|
if (max_part > 0)
|
||||||
nbd->file = file;
|
bdev->bd_invalidated = 1;
|
||||||
nbd->sock = SOCKET_I(inode);
|
nbd->disconnect = 0; /* we're connected now */
|
||||||
if (max_part > 0)
|
return 0;
|
||||||
bdev->bd_invalidated = 1;
|
|
||||||
nbd->disconnect = 0; /* we're connected now */
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
fput(file);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@ -697,12 +689,12 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
|
|||||||
|
|
||||||
case NBD_DO_IT: {
|
case NBD_DO_IT: {
|
||||||
struct task_struct *thread;
|
struct task_struct *thread;
|
||||||
struct file *file;
|
struct socket *sock;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (nbd->pid)
|
if (nbd->pid)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
if (!nbd->file)
|
if (!nbd->sock)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
mutex_unlock(&nbd->tx_lock);
|
mutex_unlock(&nbd->tx_lock);
|
||||||
@ -731,15 +723,15 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
|
|||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
sock_shutdown(nbd, 0);
|
sock_shutdown(nbd, 0);
|
||||||
file = nbd->file;
|
sock = nbd->sock;
|
||||||
nbd->file = NULL;
|
nbd->sock = NULL;
|
||||||
nbd_clear_que(nbd);
|
nbd_clear_que(nbd);
|
||||||
dev_warn(disk_to_dev(nbd->disk), "queue cleared\n");
|
dev_warn(disk_to_dev(nbd->disk), "queue cleared\n");
|
||||||
kill_bdev(bdev);
|
kill_bdev(bdev);
|
||||||
queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, nbd->disk->queue);
|
queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, nbd->disk->queue);
|
||||||
set_device_ro(bdev, false);
|
set_device_ro(bdev, false);
|
||||||
if (file)
|
if (sock)
|
||||||
fput(file);
|
sockfd_put(sock);
|
||||||
nbd->flags = 0;
|
nbd->flags = 0;
|
||||||
nbd->bytesize = 0;
|
nbd->bytesize = 0;
|
||||||
bdev->bd_inode->i_size = 0;
|
bdev->bd_inode->i_size = 0;
|
||||||
@ -875,9 +867,7 @@ static int __init nbd_init(void)
|
|||||||
|
|
||||||
for (i = 0; i < nbds_max; i++) {
|
for (i = 0; i < nbds_max; i++) {
|
||||||
struct gendisk *disk = nbd_dev[i].disk;
|
struct gendisk *disk = nbd_dev[i].disk;
|
||||||
nbd_dev[i].file = NULL;
|
|
||||||
nbd_dev[i].magic = NBD_MAGIC;
|
nbd_dev[i].magic = NBD_MAGIC;
|
||||||
nbd_dev[i].flags = 0;
|
|
||||||
INIT_LIST_HEAD(&nbd_dev[i].waiting_queue);
|
INIT_LIST_HEAD(&nbd_dev[i].waiting_queue);
|
||||||
spin_lock_init(&nbd_dev[i].queue_lock);
|
spin_lock_init(&nbd_dev[i].queue_lock);
|
||||||
INIT_LIST_HEAD(&nbd_dev[i].queue_head);
|
INIT_LIST_HEAD(&nbd_dev[i].queue_head);
|
||||||
|
@ -901,9 +901,9 @@ static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
|
|||||||
if (len + offset > PAGE_SIZE)
|
if (len + offset > PAGE_SIZE)
|
||||||
len = PAGE_SIZE - offset;
|
len = PAGE_SIZE - offset;
|
||||||
|
|
||||||
src = buf->ops->map(pipe, buf, 1);
|
src = kmap_atomic(buf->page);
|
||||||
memcpy(page_address(page) + offset, src + buf->offset, len);
|
memcpy(page_address(page) + offset, src + buf->offset, len);
|
||||||
buf->ops->unmap(pipe, buf, src);
|
kunmap_atomic(src);
|
||||||
|
|
||||||
sg_set_page(&(sgl->sg[sgl->n]), page, len, offset);
|
sg_set_page(&(sgl->sg[sgl->n]), page, len, offset);
|
||||||
}
|
}
|
||||||
|
@ -99,16 +99,7 @@ ksocknal_lib_send_iov (ksock_conn_t *conn, ksock_tx_t *tx)
|
|||||||
struct iovec *scratchiov = conn->ksnc_scheduler->kss_scratch_iov;
|
struct iovec *scratchiov = conn->ksnc_scheduler->kss_scratch_iov;
|
||||||
unsigned int niov = tx->tx_niov;
|
unsigned int niov = tx->tx_niov;
|
||||||
#endif
|
#endif
|
||||||
struct msghdr msg = {
|
struct msghdr msg = {.msg_flags = MSG_DONTWAIT};
|
||||||
.msg_name = NULL,
|
|
||||||
.msg_namelen = 0,
|
|
||||||
.msg_iov = scratchiov,
|
|
||||||
.msg_iovlen = niov,
|
|
||||||
.msg_control = NULL,
|
|
||||||
.msg_controllen = 0,
|
|
||||||
.msg_flags = MSG_DONTWAIT
|
|
||||||
};
|
|
||||||
mm_segment_t oldmm = get_fs();
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (nob = i = 0; i < niov; i++) {
|
for (nob = i = 0; i < niov; i++) {
|
||||||
@ -120,9 +111,7 @@ ksocknal_lib_send_iov (ksock_conn_t *conn, ksock_tx_t *tx)
|
|||||||
nob < tx->tx_resid)
|
nob < tx->tx_resid)
|
||||||
msg.msg_flags |= MSG_MORE;
|
msg.msg_flags |= MSG_MORE;
|
||||||
|
|
||||||
set_fs (KERNEL_DS);
|
rc = kernel_sendmsg(sock, &msg, (struct kvec *)scratchiov, niov, nob);
|
||||||
rc = sock_sendmsg(sock, &msg, nob);
|
|
||||||
set_fs (oldmm);
|
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@ -174,16 +163,7 @@ ksocknal_lib_send_kiov (ksock_conn_t *conn, ksock_tx_t *tx)
|
|||||||
struct iovec *scratchiov = conn->ksnc_scheduler->kss_scratch_iov;
|
struct iovec *scratchiov = conn->ksnc_scheduler->kss_scratch_iov;
|
||||||
unsigned int niov = tx->tx_nkiov;
|
unsigned int niov = tx->tx_nkiov;
|
||||||
#endif
|
#endif
|
||||||
struct msghdr msg = {
|
struct msghdr msg = {.msg_flags = MSG_DONTWAIT};
|
||||||
.msg_name = NULL,
|
|
||||||
.msg_namelen = 0,
|
|
||||||
.msg_iov = scratchiov,
|
|
||||||
.msg_iovlen = niov,
|
|
||||||
.msg_control = NULL,
|
|
||||||
.msg_controllen = 0,
|
|
||||||
.msg_flags = MSG_DONTWAIT
|
|
||||||
};
|
|
||||||
mm_segment_t oldmm = get_fs();
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (nob = i = 0; i < niov; i++) {
|
for (nob = i = 0; i < niov; i++) {
|
||||||
@ -196,9 +176,7 @@ ksocknal_lib_send_kiov (ksock_conn_t *conn, ksock_tx_t *tx)
|
|||||||
nob < tx->tx_resid)
|
nob < tx->tx_resid)
|
||||||
msg.msg_flags |= MSG_MORE;
|
msg.msg_flags |= MSG_MORE;
|
||||||
|
|
||||||
set_fs (KERNEL_DS);
|
rc = kernel_sendmsg(sock, &msg, (struct kvec *)scratchiov, niov, nob);
|
||||||
rc = sock_sendmsg(sock, &msg, nob);
|
|
||||||
set_fs (oldmm);
|
|
||||||
|
|
||||||
for (i = 0; i < niov; i++)
|
for (i = 0; i < niov; i++)
|
||||||
kunmap(kiov[i].kiov_page);
|
kunmap(kiov[i].kiov_page);
|
||||||
@ -237,15 +215,8 @@ ksocknal_lib_recv_iov (ksock_conn_t *conn)
|
|||||||
#endif
|
#endif
|
||||||
struct iovec *iov = conn->ksnc_rx_iov;
|
struct iovec *iov = conn->ksnc_rx_iov;
|
||||||
struct msghdr msg = {
|
struct msghdr msg = {
|
||||||
.msg_name = NULL,
|
|
||||||
.msg_namelen = 0,
|
|
||||||
.msg_iov = scratchiov,
|
|
||||||
.msg_iovlen = niov,
|
|
||||||
.msg_control = NULL,
|
|
||||||
.msg_controllen = 0,
|
|
||||||
.msg_flags = 0
|
.msg_flags = 0
|
||||||
};
|
};
|
||||||
mm_segment_t oldmm = get_fs();
|
|
||||||
int nob;
|
int nob;
|
||||||
int i;
|
int i;
|
||||||
int rc;
|
int rc;
|
||||||
@ -263,10 +234,8 @@ ksocknal_lib_recv_iov (ksock_conn_t *conn)
|
|||||||
}
|
}
|
||||||
LASSERT (nob <= conn->ksnc_rx_nob_wanted);
|
LASSERT (nob <= conn->ksnc_rx_nob_wanted);
|
||||||
|
|
||||||
set_fs (KERNEL_DS);
|
rc = kernel_recvmsg(conn->ksnc_sock, &msg,
|
||||||
rc = sock_recvmsg (conn->ksnc_sock, &msg, nob, MSG_DONTWAIT);
|
(struct kvec *)scratchiov, niov, nob, MSG_DONTWAIT);
|
||||||
/* NB this is just a boolean..........................^ */
|
|
||||||
set_fs (oldmm);
|
|
||||||
|
|
||||||
saved_csum = 0;
|
saved_csum = 0;
|
||||||
if (conn->ksnc_proto == &ksocknal_protocol_v2x) {
|
if (conn->ksnc_proto == &ksocknal_protocol_v2x) {
|
||||||
@ -355,14 +324,8 @@ ksocknal_lib_recv_kiov (ksock_conn_t *conn)
|
|||||||
#endif
|
#endif
|
||||||
lnet_kiov_t *kiov = conn->ksnc_rx_kiov;
|
lnet_kiov_t *kiov = conn->ksnc_rx_kiov;
|
||||||
struct msghdr msg = {
|
struct msghdr msg = {
|
||||||
.msg_name = NULL,
|
|
||||||
.msg_namelen = 0,
|
|
||||||
.msg_iov = scratchiov,
|
|
||||||
.msg_control = NULL,
|
|
||||||
.msg_controllen = 0,
|
|
||||||
.msg_flags = 0
|
.msg_flags = 0
|
||||||
};
|
};
|
||||||
mm_segment_t oldmm = get_fs();
|
|
||||||
int nob;
|
int nob;
|
||||||
int i;
|
int i;
|
||||||
int rc;
|
int rc;
|
||||||
@ -370,13 +333,14 @@ ksocknal_lib_recv_kiov (ksock_conn_t *conn)
|
|||||||
void *addr;
|
void *addr;
|
||||||
int sum;
|
int sum;
|
||||||
int fragnob;
|
int fragnob;
|
||||||
|
int n;
|
||||||
|
|
||||||
/* NB we can't trust socket ops to either consume our iovs
|
/* NB we can't trust socket ops to either consume our iovs
|
||||||
* or leave them alone. */
|
* or leave them alone. */
|
||||||
addr = ksocknal_lib_kiov_vmap(kiov, niov, scratchiov, pages);
|
addr = ksocknal_lib_kiov_vmap(kiov, niov, scratchiov, pages);
|
||||||
if (addr != NULL) {
|
if (addr != NULL) {
|
||||||
nob = scratchiov[0].iov_len;
|
nob = scratchiov[0].iov_len;
|
||||||
msg.msg_iovlen = 1;
|
n = 1;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
for (nob = i = 0; i < niov; i++) {
|
for (nob = i = 0; i < niov; i++) {
|
||||||
@ -384,15 +348,13 @@ ksocknal_lib_recv_kiov (ksock_conn_t *conn)
|
|||||||
scratchiov[i].iov_base = kmap(kiov[i].kiov_page) +
|
scratchiov[i].iov_base = kmap(kiov[i].kiov_page) +
|
||||||
kiov[i].kiov_offset;
|
kiov[i].kiov_offset;
|
||||||
}
|
}
|
||||||
msg.msg_iovlen = niov;
|
n = niov;
|
||||||
}
|
}
|
||||||
|
|
||||||
LASSERT (nob <= conn->ksnc_rx_nob_wanted);
|
LASSERT (nob <= conn->ksnc_rx_nob_wanted);
|
||||||
|
|
||||||
set_fs (KERNEL_DS);
|
rc = kernel_recvmsg(conn->ksnc_sock, &msg,
|
||||||
rc = sock_recvmsg (conn->ksnc_sock, &msg, nob, MSG_DONTWAIT);
|
(struct kvec *)scratchiov, n, nob, MSG_DONTWAIT);
|
||||||
/* NB this is just a boolean.......................^ */
|
|
||||||
set_fs (oldmm);
|
|
||||||
|
|
||||||
if (conn->ksnc_msg.ksm_csum != 0) {
|
if (conn->ksnc_msg.ksm_csum != 0) {
|
||||||
for (i = 0, sum = rc; sum > 0; i++, sum -= fragnob) {
|
for (i = 0, sum = rc; sum > 0; i++, sum -= fragnob) {
|
||||||
|
@ -265,17 +265,11 @@ libcfs_sock_write (struct socket *sock, void *buffer, int nob, int timeout)
|
|||||||
* empty enough to take the whole message immediately */
|
* empty enough to take the whole message immediately */
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
struct iovec iov = {
|
struct kvec iov = {
|
||||||
.iov_base = buffer,
|
.iov_base = buffer,
|
||||||
.iov_len = nob
|
.iov_len = nob
|
||||||
};
|
};
|
||||||
struct msghdr msg = {
|
struct msghdr msg = {
|
||||||
.msg_name = NULL,
|
|
||||||
.msg_namelen = 0,
|
|
||||||
.msg_iov = &iov,
|
|
||||||
.msg_iovlen = 1,
|
|
||||||
.msg_control = NULL,
|
|
||||||
.msg_controllen = 0,
|
|
||||||
.msg_flags = (timeout == 0) ? MSG_DONTWAIT : 0
|
.msg_flags = (timeout == 0) ? MSG_DONTWAIT : 0
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -297,11 +291,9 @@ libcfs_sock_write (struct socket *sock, void *buffer, int nob, int timeout)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set_fs (KERNEL_DS);
|
|
||||||
then = jiffies;
|
then = jiffies;
|
||||||
rc = sock_sendmsg (sock, &msg, iov.iov_len);
|
rc = kernel_sendmsg(sock, &msg, &iov, 1, nob);
|
||||||
ticks -= jiffies - then;
|
ticks -= jiffies - then;
|
||||||
set_fs (oldmm);
|
|
||||||
|
|
||||||
if (rc == nob)
|
if (rc == nob)
|
||||||
return 0;
|
return 0;
|
||||||
@ -338,17 +330,11 @@ libcfs_sock_read (struct socket *sock, void *buffer, int nob, int timeout)
|
|||||||
LASSERT (ticks > 0);
|
LASSERT (ticks > 0);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
struct iovec iov = {
|
struct kvec iov = {
|
||||||
.iov_base = buffer,
|
.iov_base = buffer,
|
||||||
.iov_len = nob
|
.iov_len = nob
|
||||||
};
|
};
|
||||||
struct msghdr msg = {
|
struct msghdr msg = {
|
||||||
.msg_name = NULL,
|
|
||||||
.msg_namelen = 0,
|
|
||||||
.msg_iov = &iov,
|
|
||||||
.msg_iovlen = 1,
|
|
||||||
.msg_control = NULL,
|
|
||||||
.msg_controllen = 0,
|
|
||||||
.msg_flags = 0
|
.msg_flags = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -367,11 +353,9 @@ libcfs_sock_read (struct socket *sock, void *buffer, int nob, int timeout)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_fs(KERNEL_DS);
|
|
||||||
then = jiffies;
|
then = jiffies;
|
||||||
rc = sock_recvmsg(sock, &msg, iov.iov_len, 0);
|
rc = kernel_recvmsg(sock, &msg, &iov, 1, nob, 0);
|
||||||
ticks -= jiffies - then;
|
ticks -= jiffies - then;
|
||||||
set_fs(oldmm);
|
|
||||||
|
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -115,27 +115,6 @@ static int ll_readlink_internal(struct inode *inode,
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ll_readlink(struct dentry *dentry, char *buffer, int buflen)
|
|
||||||
{
|
|
||||||
struct inode *inode = dentry->d_inode;
|
|
||||||
struct ptlrpc_request *request;
|
|
||||||
char *symname;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
CDEBUG(D_VFSTRACE, "VFS Op\n");
|
|
||||||
|
|
||||||
ll_inode_size_lock(inode);
|
|
||||||
rc = ll_readlink_internal(inode, &request, &symname);
|
|
||||||
if (rc)
|
|
||||||
GOTO(out, rc);
|
|
||||||
|
|
||||||
rc = vfs_readlink(dentry, buffer, buflen, symname);
|
|
||||||
out:
|
|
||||||
ptlrpc_req_finished(request);
|
|
||||||
ll_inode_size_unlock(inode);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *ll_follow_link(struct dentry *dentry, struct nameidata *nd)
|
static void *ll_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||||
{
|
{
|
||||||
struct inode *inode = dentry->d_inode;
|
struct inode *inode = dentry->d_inode;
|
||||||
@ -175,7 +154,7 @@ static void ll_put_link(struct dentry *dentry, struct nameidata *nd, void *cooki
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct inode_operations ll_fast_symlink_inode_operations = {
|
struct inode_operations ll_fast_symlink_inode_operations = {
|
||||||
.readlink = ll_readlink,
|
.readlink = generic_readlink,
|
||||||
.setattr = ll_setattr,
|
.setattr = ll_setattr,
|
||||||
.follow_link = ll_follow_link,
|
.follow_link = ll_follow_link,
|
||||||
.put_link = ll_put_link,
|
.put_link = ll_put_link,
|
||||||
|
@ -86,7 +86,6 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
|
|||||||
struct stub_device *sdev = dev_get_drvdata(dev);
|
struct stub_device *sdev = dev_get_drvdata(dev);
|
||||||
int sockfd = 0;
|
int sockfd = 0;
|
||||||
struct socket *socket;
|
struct socket *socket;
|
||||||
ssize_t err = -EINVAL;
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
if (!sdev) {
|
if (!sdev) {
|
||||||
@ -99,6 +98,7 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (sockfd != -1) {
|
if (sockfd != -1) {
|
||||||
|
int err;
|
||||||
dev_info(dev, "stub up\n");
|
dev_info(dev, "stub up\n");
|
||||||
|
|
||||||
spin_lock_irq(&sdev->ud.lock);
|
spin_lock_irq(&sdev->ud.lock);
|
||||||
@ -108,7 +108,7 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
socket = sockfd_to_socket(sockfd);
|
socket = sockfd_lookup(sockfd, &err);
|
||||||
if (!socket)
|
if (!socket)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
|
|||||||
|
|
||||||
err:
|
err:
|
||||||
spin_unlock_irq(&sdev->ud.lock);
|
spin_unlock_irq(&sdev->ud.lock);
|
||||||
return err;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd);
|
static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd);
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ static void stub_shutdown_connection(struct usbip_device *ud)
|
|||||||
* not touch NULL socket.
|
* not touch NULL socket.
|
||||||
*/
|
*/
|
||||||
if (ud->tcp_socket) {
|
if (ud->tcp_socket) {
|
||||||
fput(ud->tcp_socket->file);
|
sockfd_put(ud->tcp_socket);
|
||||||
ud->tcp_socket = NULL;
|
ud->tcp_socket = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,31 +382,6 @@ int usbip_recv(struct socket *sock, void *buf, int size)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(usbip_recv);
|
EXPORT_SYMBOL_GPL(usbip_recv);
|
||||||
|
|
||||||
struct socket *sockfd_to_socket(unsigned int sockfd)
|
|
||||||
{
|
|
||||||
struct socket *socket;
|
|
||||||
struct file *file;
|
|
||||||
struct inode *inode;
|
|
||||||
|
|
||||||
file = fget(sockfd);
|
|
||||||
if (!file) {
|
|
||||||
pr_err("invalid sockfd\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
inode = file_inode(file);
|
|
||||||
|
|
||||||
if (!inode || !S_ISSOCK(inode->i_mode)) {
|
|
||||||
fput(file);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
socket = SOCKET_I(inode);
|
|
||||||
|
|
||||||
return socket;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(sockfd_to_socket);
|
|
||||||
|
|
||||||
/* there may be more cases to tweak the flags. */
|
/* there may be more cases to tweak the flags. */
|
||||||
static unsigned int tweak_transfer_flags(unsigned int flags)
|
static unsigned int tweak_transfer_flags(unsigned int flags)
|
||||||
{
|
{
|
||||||
|
@ -299,7 +299,6 @@ void usbip_dump_urb(struct urb *purb);
|
|||||||
void usbip_dump_header(struct usbip_header *pdu);
|
void usbip_dump_header(struct usbip_header *pdu);
|
||||||
|
|
||||||
int usbip_recv(struct socket *sock, void *buf, int size);
|
int usbip_recv(struct socket *sock, void *buf, int size);
|
||||||
struct socket *sockfd_to_socket(unsigned int sockfd);
|
|
||||||
|
|
||||||
void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd,
|
void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd,
|
||||||
int pack);
|
int pack);
|
||||||
|
@ -788,7 +788,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
|
|||||||
|
|
||||||
/* active connection is closed */
|
/* active connection is closed */
|
||||||
if (vdev->ud.tcp_socket) {
|
if (vdev->ud.tcp_socket) {
|
||||||
fput(vdev->ud.tcp_socket->file);
|
sockfd_put(vdev->ud.tcp_socket);
|
||||||
vdev->ud.tcp_socket = NULL;
|
vdev->ud.tcp_socket = NULL;
|
||||||
}
|
}
|
||||||
pr_info("release socket\n");
|
pr_info("release socket\n");
|
||||||
@ -835,7 +835,7 @@ static void vhci_device_reset(struct usbip_device *ud)
|
|||||||
vdev->udev = NULL;
|
vdev->udev = NULL;
|
||||||
|
|
||||||
if (ud->tcp_socket) {
|
if (ud->tcp_socket) {
|
||||||
fput(ud->tcp_socket->file);
|
sockfd_put(ud->tcp_socket);
|
||||||
ud->tcp_socket = NULL;
|
ud->tcp_socket = NULL;
|
||||||
}
|
}
|
||||||
ud->status = VDEV_ST_NULL;
|
ud->status = VDEV_ST_NULL;
|
||||||
|
@ -176,6 +176,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
|
|||||||
struct socket *socket;
|
struct socket *socket;
|
||||||
int sockfd = 0;
|
int sockfd = 0;
|
||||||
__u32 rhport = 0, devid = 0, speed = 0;
|
__u32 rhport = 0, devid = 0, speed = 0;
|
||||||
|
int err;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @rhport: port number of vhci_hcd
|
* @rhport: port number of vhci_hcd
|
||||||
@ -194,8 +195,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* Extract socket from fd. */
|
/* Extract socket from fd. */
|
||||||
/* The correct way to clean this up is to fput(socket->file). */
|
socket = sockfd_lookup(sockfd, &err);
|
||||||
socket = sockfd_to_socket(sockfd);
|
|
||||||
if (!socket)
|
if (!socket)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
|
|||||||
spin_unlock(&vdev->ud.lock);
|
spin_unlock(&vdev->ud.lock);
|
||||||
spin_unlock(&the_controller->lock);
|
spin_unlock(&the_controller->lock);
|
||||||
|
|
||||||
fput(socket->file);
|
sockfd_put(socket);
|
||||||
|
|
||||||
dev_err(dev, "port %d already used\n", rhport);
|
dev_err(dev, "port %d already used\n", rhport);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -818,9 +818,9 @@ static int vhost_net_release(struct inode *inode, struct file *f)
|
|||||||
vhost_dev_cleanup(&n->dev, false);
|
vhost_dev_cleanup(&n->dev, false);
|
||||||
vhost_net_vq_reset(n);
|
vhost_net_vq_reset(n);
|
||||||
if (tx_sock)
|
if (tx_sock)
|
||||||
fput(tx_sock->file);
|
sockfd_put(tx_sock);
|
||||||
if (rx_sock)
|
if (rx_sock)
|
||||||
fput(rx_sock->file);
|
sockfd_put(rx_sock);
|
||||||
/* Make sure no callbacks are outstanding */
|
/* Make sure no callbacks are outstanding */
|
||||||
synchronize_rcu_bh();
|
synchronize_rcu_bh();
|
||||||
/* We do an extra flush before freeing memory,
|
/* We do an extra flush before freeing memory,
|
||||||
@ -860,7 +860,7 @@ static struct socket *get_raw_socket(int fd)
|
|||||||
}
|
}
|
||||||
return sock;
|
return sock;
|
||||||
err:
|
err:
|
||||||
fput(sock->file);
|
sockfd_put(sock);
|
||||||
return ERR_PTR(r);
|
return ERR_PTR(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -966,7 +966,7 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
|
|||||||
|
|
||||||
if (oldsock) {
|
if (oldsock) {
|
||||||
vhost_net_flush_vq(n, index);
|
vhost_net_flush_vq(n, index);
|
||||||
fput(oldsock->file);
|
sockfd_put(oldsock);
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&n->dev.mutex);
|
mutex_unlock(&n->dev.mutex);
|
||||||
@ -978,7 +978,7 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
|
|||||||
if (ubufs)
|
if (ubufs)
|
||||||
vhost_net_ubuf_put_wait_and_free(ubufs);
|
vhost_net_ubuf_put_wait_and_free(ubufs);
|
||||||
err_ubufs:
|
err_ubufs:
|
||||||
fput(sock->file);
|
sockfd_put(sock);
|
||||||
err_vq:
|
err_vq:
|
||||||
mutex_unlock(&vq->mutex);
|
mutex_unlock(&vq->mutex);
|
||||||
err:
|
err:
|
||||||
@ -1009,9 +1009,9 @@ static long vhost_net_reset_owner(struct vhost_net *n)
|
|||||||
done:
|
done:
|
||||||
mutex_unlock(&n->dev.mutex);
|
mutex_unlock(&n->dev.mutex);
|
||||||
if (tx_sock)
|
if (tx_sock)
|
||||||
fput(tx_sock->file);
|
sockfd_put(tx_sock);
|
||||||
if (rx_sock)
|
if (rx_sock)
|
||||||
fput(rx_sock->file);
|
sockfd_put(rx_sock);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
fs/bio.c
10
fs/bio.c
@ -1002,7 +1002,7 @@ struct bio_map_data {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void bio_set_map_data(struct bio_map_data *bmd, struct bio *bio,
|
static void bio_set_map_data(struct bio_map_data *bmd, struct bio *bio,
|
||||||
struct sg_iovec *iov, int iov_count,
|
const struct sg_iovec *iov, int iov_count,
|
||||||
int is_our_pages)
|
int is_our_pages)
|
||||||
{
|
{
|
||||||
memcpy(bmd->sgvecs, iov, sizeof(struct sg_iovec) * iov_count);
|
memcpy(bmd->sgvecs, iov, sizeof(struct sg_iovec) * iov_count);
|
||||||
@ -1022,7 +1022,7 @@ static struct bio_map_data *bio_alloc_map_data(int nr_segs,
|
|||||||
sizeof(struct sg_iovec) * iov_count, gfp_mask);
|
sizeof(struct sg_iovec) * iov_count, gfp_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __bio_copy_iov(struct bio *bio, struct sg_iovec *iov, int iov_count,
|
static int __bio_copy_iov(struct bio *bio, const struct sg_iovec *iov, int iov_count,
|
||||||
int to_user, int from_user, int do_free_page)
|
int to_user, int from_user, int do_free_page)
|
||||||
{
|
{
|
||||||
int ret = 0, i;
|
int ret = 0, i;
|
||||||
@ -1120,7 +1120,7 @@ EXPORT_SYMBOL(bio_uncopy_user);
|
|||||||
*/
|
*/
|
||||||
struct bio *bio_copy_user_iov(struct request_queue *q,
|
struct bio *bio_copy_user_iov(struct request_queue *q,
|
||||||
struct rq_map_data *map_data,
|
struct rq_map_data *map_data,
|
||||||
struct sg_iovec *iov, int iov_count,
|
const struct sg_iovec *iov, int iov_count,
|
||||||
int write_to_vm, gfp_t gfp_mask)
|
int write_to_vm, gfp_t gfp_mask)
|
||||||
{
|
{
|
||||||
struct bio_map_data *bmd;
|
struct bio_map_data *bmd;
|
||||||
@ -1259,7 +1259,7 @@ EXPORT_SYMBOL(bio_copy_user);
|
|||||||
|
|
||||||
static struct bio *__bio_map_user_iov(struct request_queue *q,
|
static struct bio *__bio_map_user_iov(struct request_queue *q,
|
||||||
struct block_device *bdev,
|
struct block_device *bdev,
|
||||||
struct sg_iovec *iov, int iov_count,
|
const struct sg_iovec *iov, int iov_count,
|
||||||
int write_to_vm, gfp_t gfp_mask)
|
int write_to_vm, gfp_t gfp_mask)
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j;
|
||||||
@ -1407,7 +1407,7 @@ EXPORT_SYMBOL(bio_map_user);
|
|||||||
* device. Returns an error pointer in case of error.
|
* device. Returns an error pointer in case of error.
|
||||||
*/
|
*/
|
||||||
struct bio *bio_map_user_iov(struct request_queue *q, struct block_device *bdev,
|
struct bio *bio_map_user_iov(struct request_queue *q, struct block_device *bdev,
|
||||||
struct sg_iovec *iov, int iov_count,
|
const struct sg_iovec *iov, int iov_count,
|
||||||
int write_to_vm, gfp_t gfp_mask)
|
int write_to_vm, gfp_t gfp_mask)
|
||||||
{
|
{
|
||||||
struct bio *bio;
|
struct bio *bio;
|
||||||
|
@ -1518,7 +1518,7 @@ ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
|||||||
BUG_ON(iocb->ki_pos != pos);
|
BUG_ON(iocb->ki_pos != pos);
|
||||||
|
|
||||||
blk_start_plug(&plug);
|
blk_start_plug(&plug);
|
||||||
ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
|
ret = __generic_file_aio_write(iocb, iov, nr_segs);
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
ssize_t err;
|
ssize_t err;
|
||||||
|
|
||||||
|
@ -425,13 +425,8 @@ static noinline int btrfs_copy_from_user(loff_t pos, int num_pages,
|
|||||||
struct page *page = prepared_pages[pg];
|
struct page *page = prepared_pages[pg];
|
||||||
/*
|
/*
|
||||||
* Copy data from userspace to the current page
|
* Copy data from userspace to the current page
|
||||||
*
|
|
||||||
* Disable pagefault to avoid recursive lock since
|
|
||||||
* the pages are already locked
|
|
||||||
*/
|
*/
|
||||||
pagefault_disable();
|
|
||||||
copied = iov_iter_copy_from_user_atomic(page, i, offset, count);
|
copied = iov_iter_copy_from_user_atomic(page, i, offset, count);
|
||||||
pagefault_enable();
|
|
||||||
|
|
||||||
/* Flush processor's dcache for this page */
|
/* Flush processor's dcache for this page */
|
||||||
flush_dcache_page(page);
|
flush_dcache_page(page);
|
||||||
@ -1665,7 +1660,7 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
|
|||||||
static ssize_t __btrfs_direct_write(struct kiocb *iocb,
|
static ssize_t __btrfs_direct_write(struct kiocb *iocb,
|
||||||
const struct iovec *iov,
|
const struct iovec *iov,
|
||||||
unsigned long nr_segs, loff_t pos,
|
unsigned long nr_segs, loff_t pos,
|
||||||
loff_t *ppos, size_t count, size_t ocount)
|
size_t count, size_t ocount)
|
||||||
{
|
{
|
||||||
struct file *file = iocb->ki_filp;
|
struct file *file = iocb->ki_filp;
|
||||||
struct iov_iter i;
|
struct iov_iter i;
|
||||||
@ -1674,7 +1669,7 @@ static ssize_t __btrfs_direct_write(struct kiocb *iocb,
|
|||||||
loff_t endbyte;
|
loff_t endbyte;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
written = generic_file_direct_write(iocb, iov, &nr_segs, pos, ppos,
|
written = generic_file_direct_write(iocb, iov, &nr_segs, pos,
|
||||||
count, ocount);
|
count, ocount);
|
||||||
|
|
||||||
if (written < 0 || written == count)
|
if (written < 0 || written == count)
|
||||||
@ -1693,7 +1688,7 @@ static ssize_t __btrfs_direct_write(struct kiocb *iocb,
|
|||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
written += written_buffered;
|
written += written_buffered;
|
||||||
*ppos = pos + written_buffered;
|
iocb->ki_pos = pos + written_buffered;
|
||||||
invalidate_mapping_pages(file->f_mapping, pos >> PAGE_CACHE_SHIFT,
|
invalidate_mapping_pages(file->f_mapping, pos >> PAGE_CACHE_SHIFT,
|
||||||
endbyte >> PAGE_CACHE_SHIFT);
|
endbyte >> PAGE_CACHE_SHIFT);
|
||||||
out:
|
out:
|
||||||
@ -1725,7 +1720,6 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb,
|
|||||||
struct file *file = iocb->ki_filp;
|
struct file *file = iocb->ki_filp;
|
||||||
struct inode *inode = file_inode(file);
|
struct inode *inode = file_inode(file);
|
||||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||||
loff_t *ppos = &iocb->ki_pos;
|
|
||||||
u64 start_pos;
|
u64 start_pos;
|
||||||
u64 end_pos;
|
u64 end_pos;
|
||||||
ssize_t num_written = 0;
|
ssize_t num_written = 0;
|
||||||
@ -1796,7 +1790,7 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb,
|
|||||||
|
|
||||||
if (unlikely(file->f_flags & O_DIRECT)) {
|
if (unlikely(file->f_flags & O_DIRECT)) {
|
||||||
num_written = __btrfs_direct_write(iocb, iov, nr_segs,
|
num_written = __btrfs_direct_write(iocb, iov, nr_segs,
|
||||||
pos, ppos, count, ocount);
|
pos, count, ocount);
|
||||||
} else {
|
} else {
|
||||||
struct iov_iter i;
|
struct iov_iter i;
|
||||||
|
|
||||||
@ -1804,7 +1798,7 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb,
|
|||||||
|
|
||||||
num_written = __btrfs_buffered_write(file, &i, pos);
|
num_written = __btrfs_buffered_write(file, &i, pos);
|
||||||
if (num_written > 0)
|
if (num_written > 0)
|
||||||
*ppos = pos + num_written;
|
iocb->ki_pos = pos + num_written;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&inode->i_mutex);
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
@ -2114,8 +2114,8 @@ EXPORT_SYMBOL(generic_write_end);
|
|||||||
* Returns true if all buffers which correspond to a file portion
|
* Returns true if all buffers which correspond to a file portion
|
||||||
* we want to read are uptodate.
|
* we want to read are uptodate.
|
||||||
*/
|
*/
|
||||||
int block_is_partially_uptodate(struct page *page, read_descriptor_t *desc,
|
int block_is_partially_uptodate(struct page *page, unsigned long from,
|
||||||
unsigned long from)
|
unsigned long count)
|
||||||
{
|
{
|
||||||
unsigned block_start, block_end, blocksize;
|
unsigned block_start, block_end, blocksize;
|
||||||
unsigned to;
|
unsigned to;
|
||||||
@ -2127,7 +2127,7 @@ int block_is_partially_uptodate(struct page *page, read_descriptor_t *desc,
|
|||||||
|
|
||||||
head = page_buffers(page);
|
head = page_buffers(page);
|
||||||
blocksize = head->b_size;
|
blocksize = head->b_size;
|
||||||
to = min_t(unsigned, PAGE_CACHE_SIZE - from, desc->count);
|
to = min_t(unsigned, PAGE_CACHE_SIZE - from, count);
|
||||||
to = from + to;
|
to = from + to;
|
||||||
if (from < blocksize && to > PAGE_CACHE_SIZE - blocksize)
|
if (from < blocksize && to > PAGE_CACHE_SIZE - blocksize)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -124,7 +124,6 @@ static int cachefiles_daemon_add_cache(struct cachefiles_cache *cache)
|
|||||||
/* check parameters */
|
/* check parameters */
|
||||||
ret = -EOPNOTSUPP;
|
ret = -EOPNOTSUPP;
|
||||||
if (!root->d_inode ||
|
if (!root->d_inode ||
|
||||||
!root->d_inode->i_op ||
|
|
||||||
!root->d_inode->i_op->lookup ||
|
!root->d_inode->i_op->lookup ||
|
||||||
!root->d_inode->i_op->mkdir ||
|
!root->d_inode->i_op->mkdir ||
|
||||||
!root->d_inode->i_op->setxattr ||
|
!root->d_inode->i_op->setxattr ||
|
||||||
|
@ -779,8 +779,7 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = -EPERM;
|
ret = -EPERM;
|
||||||
if (!subdir->d_inode->i_op ||
|
if (!subdir->d_inode->i_op->setxattr ||
|
||||||
!subdir->d_inode->i_op->setxattr ||
|
|
||||||
!subdir->d_inode->i_op->getxattr ||
|
!subdir->d_inode->i_op->getxattr ||
|
||||||
!subdir->d_inode->i_op->lookup ||
|
!subdir->d_inode->i_op->lookup ||
|
||||||
!subdir->d_inode->i_op->mkdir ||
|
!subdir->d_inode->i_op->mkdir ||
|
||||||
|
@ -601,7 +601,7 @@ ceph_sync_direct_write(struct kiocb *iocb, const struct iovec *iov,
|
|||||||
false);
|
false);
|
||||||
if (IS_ERR(req)) {
|
if (IS_ERR(req)) {
|
||||||
ret = PTR_ERR(req);
|
ret = PTR_ERR(req);
|
||||||
goto out;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
num_pages = calc_pages_for(page_align, len);
|
num_pages = calc_pages_for(page_align, len);
|
||||||
@ -719,7 +719,7 @@ static ssize_t ceph_sync_write(struct kiocb *iocb, const struct iovec *iov,
|
|||||||
false);
|
false);
|
||||||
if (IS_ERR(req)) {
|
if (IS_ERR(req)) {
|
||||||
ret = PTR_ERR(req);
|
ret = PTR_ERR(req);
|
||||||
goto out;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -972,6 +972,7 @@ static ssize_t ceph_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
loff_t old_size = inode->i_size;
|
loff_t old_size = inode->i_size;
|
||||||
|
struct iov_iter from;
|
||||||
/*
|
/*
|
||||||
* No need to acquire the i_truncate_mutex. Because
|
* No need to acquire the i_truncate_mutex. Because
|
||||||
* the MDS revokes Fwb caps before sending truncate
|
* the MDS revokes Fwb caps before sending truncate
|
||||||
@ -979,9 +980,10 @@ static ssize_t ceph_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
|||||||
* are pending vmtruncate. So write and vmtruncate
|
* are pending vmtruncate. So write and vmtruncate
|
||||||
* can not run at the same time
|
* can not run at the same time
|
||||||
*/
|
*/
|
||||||
written = generic_file_buffered_write(iocb, iov, nr_segs,
|
iov_iter_init(&from, iov, nr_segs, count, 0);
|
||||||
pos, &iocb->ki_pos,
|
written = generic_perform_write(file, &from, pos);
|
||||||
count, 0);
|
if (likely(written >= 0))
|
||||||
|
iocb->ki_pos = pos + written;
|
||||||
if (inode->i_size > old_size)
|
if (inode->i_size > old_size)
|
||||||
ceph_fscache_update_objectsize(inode);
|
ceph_fscache_update_objectsize(inode);
|
||||||
mutex_unlock(&inode->i_mutex);
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
@ -850,7 +850,6 @@ const struct inode_operations cifs_file_inode_ops = {
|
|||||||
/* revalidate:cifs_revalidate, */
|
/* revalidate:cifs_revalidate, */
|
||||||
.setattr = cifs_setattr,
|
.setattr = cifs_setattr,
|
||||||
.getattr = cifs_getattr, /* do we need this anymore? */
|
.getattr = cifs_getattr, /* do we need this anymore? */
|
||||||
.rename = cifs_rename,
|
|
||||||
.permission = cifs_permission,
|
.permission = cifs_permission,
|
||||||
#ifdef CONFIG_CIFS_XATTR
|
#ifdef CONFIG_CIFS_XATTR
|
||||||
.setxattr = cifs_setxattr,
|
.setxattr = cifs_setxattr,
|
||||||
|
128
fs/cifs/file.c
128
fs/cifs/file.c
@ -2579,19 +2579,32 @@ cifs_writev(struct kiocb *iocb, const struct iovec *iov,
|
|||||||
struct cifsInodeInfo *cinode = CIFS_I(inode);
|
struct cifsInodeInfo *cinode = CIFS_I(inode);
|
||||||
struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
|
struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
|
||||||
ssize_t rc = -EACCES;
|
ssize_t rc = -EACCES;
|
||||||
loff_t lock_pos = pos;
|
loff_t lock_pos = iocb->ki_pos;
|
||||||
|
|
||||||
if (file->f_flags & O_APPEND)
|
|
||||||
lock_pos = i_size_read(inode);
|
|
||||||
/*
|
/*
|
||||||
* We need to hold the sem to be sure nobody modifies lock list
|
* We need to hold the sem to be sure nobody modifies lock list
|
||||||
* with a brlock that prevents writing.
|
* with a brlock that prevents writing.
|
||||||
*/
|
*/
|
||||||
down_read(&cinode->lock_sem);
|
down_read(&cinode->lock_sem);
|
||||||
|
mutex_lock(&inode->i_mutex);
|
||||||
|
if (file->f_flags & O_APPEND)
|
||||||
|
lock_pos = i_size_read(inode);
|
||||||
if (!cifs_find_lock_conflict(cfile, lock_pos, iov_length(iov, nr_segs),
|
if (!cifs_find_lock_conflict(cfile, lock_pos, iov_length(iov, nr_segs),
|
||||||
server->vals->exclusive_lock_type, NULL,
|
server->vals->exclusive_lock_type, NULL,
|
||||||
CIFS_WRITE_OP))
|
CIFS_WRITE_OP)) {
|
||||||
rc = generic_file_aio_write(iocb, iov, nr_segs, pos);
|
rc = __generic_file_aio_write(iocb, iov, nr_segs);
|
||||||
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
|
||||||
|
if (rc > 0) {
|
||||||
|
ssize_t err;
|
||||||
|
|
||||||
|
err = generic_write_sync(file, iocb->ki_pos - rc, rc);
|
||||||
|
if (rc < 0)
|
||||||
|
rc = err;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
}
|
||||||
up_read(&cinode->lock_sem);
|
up_read(&cinode->lock_sem);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@ -2727,56 +2740,27 @@ cifs_retry_async_readv(struct cifs_readdata *rdata)
|
|||||||
/**
|
/**
|
||||||
* cifs_readdata_to_iov - copy data from pages in response to an iovec
|
* cifs_readdata_to_iov - copy data from pages in response to an iovec
|
||||||
* @rdata: the readdata response with list of pages holding data
|
* @rdata: the readdata response with list of pages holding data
|
||||||
* @iov: vector in which we should copy the data
|
* @iter: destination for our data
|
||||||
* @nr_segs: number of segments in vector
|
|
||||||
* @offset: offset into file of the first iovec
|
|
||||||
* @copied: used to return the amount of data copied to the iov
|
|
||||||
*
|
*
|
||||||
* This function copies data from a list of pages in a readdata response into
|
* This function copies data from a list of pages in a readdata response into
|
||||||
* an array of iovecs. It will first calculate where the data should go
|
* an array of iovecs. It will first calculate where the data should go
|
||||||
* based on the info in the readdata and then copy the data into that spot.
|
* based on the info in the readdata and then copy the data into that spot.
|
||||||
*/
|
*/
|
||||||
static ssize_t
|
static int
|
||||||
cifs_readdata_to_iov(struct cifs_readdata *rdata, const struct iovec *iov,
|
cifs_readdata_to_iov(struct cifs_readdata *rdata, struct iov_iter *iter)
|
||||||
unsigned long nr_segs, loff_t offset, ssize_t *copied)
|
|
||||||
{
|
{
|
||||||
int rc = 0;
|
size_t remaining = rdata->bytes;
|
||||||
struct iov_iter ii;
|
|
||||||
size_t pos = rdata->offset - offset;
|
|
||||||
ssize_t remaining = rdata->bytes;
|
|
||||||
unsigned char *pdata;
|
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
/* set up iov_iter and advance to the correct offset */
|
|
||||||
iov_iter_init(&ii, iov, nr_segs, iov_length(iov, nr_segs), 0);
|
|
||||||
iov_iter_advance(&ii, pos);
|
|
||||||
|
|
||||||
*copied = 0;
|
|
||||||
for (i = 0; i < rdata->nr_pages; i++) {
|
for (i = 0; i < rdata->nr_pages; i++) {
|
||||||
ssize_t copy;
|
|
||||||
struct page *page = rdata->pages[i];
|
struct page *page = rdata->pages[i];
|
||||||
|
size_t copy = min(remaining, PAGE_SIZE);
|
||||||
/* copy a whole page or whatever's left */
|
size_t written = copy_page_to_iter(page, 0, copy, iter);
|
||||||
copy = min_t(ssize_t, remaining, PAGE_SIZE);
|
remaining -= written;
|
||||||
|
if (written < copy && iov_iter_count(iter) > 0)
|
||||||
/* ...but limit it to whatever space is left in the iov */
|
break;
|
||||||
copy = min_t(ssize_t, copy, iov_iter_count(&ii));
|
|
||||||
|
|
||||||
/* go while there's data to be copied and no errors */
|
|
||||||
if (copy && !rc) {
|
|
||||||
pdata = kmap(page);
|
|
||||||
rc = memcpy_toiovecend(ii.iov, pdata, ii.iov_offset,
|
|
||||||
(int)copy);
|
|
||||||
kunmap(page);
|
|
||||||
if (!rc) {
|
|
||||||
*copied += copy;
|
|
||||||
remaining -= copy;
|
|
||||||
iov_iter_advance(&ii, copy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return remaining ? -EFAULT : 0;
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -2837,20 +2821,21 @@ cifs_uncached_read_into_pages(struct TCP_Server_Info *server,
|
|||||||
return total_read > 0 ? total_read : result;
|
return total_read > 0 ? total_read : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t
|
ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov,
|
||||||
cifs_iovec_read(struct file *file, const struct iovec *iov,
|
unsigned long nr_segs, loff_t pos)
|
||||||
unsigned long nr_segs, loff_t *poffset)
|
|
||||||
{
|
{
|
||||||
|
struct file *file = iocb->ki_filp;
|
||||||
ssize_t rc;
|
ssize_t rc;
|
||||||
size_t len, cur_len;
|
size_t len, cur_len;
|
||||||
ssize_t total_read = 0;
|
ssize_t total_read = 0;
|
||||||
loff_t offset = *poffset;
|
loff_t offset = pos;
|
||||||
unsigned int npages;
|
unsigned int npages;
|
||||||
struct cifs_sb_info *cifs_sb;
|
struct cifs_sb_info *cifs_sb;
|
||||||
struct cifs_tcon *tcon;
|
struct cifs_tcon *tcon;
|
||||||
struct cifsFileInfo *open_file;
|
struct cifsFileInfo *open_file;
|
||||||
struct cifs_readdata *rdata, *tmp;
|
struct cifs_readdata *rdata, *tmp;
|
||||||
struct list_head rdata_list;
|
struct list_head rdata_list;
|
||||||
|
struct iov_iter to;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
if (!nr_segs)
|
if (!nr_segs)
|
||||||
@ -2860,6 +2845,8 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
|
|||||||
if (!len)
|
if (!len)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
iov_iter_init(&to, iov, nr_segs, len, 0);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&rdata_list);
|
INIT_LIST_HEAD(&rdata_list);
|
||||||
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
||||||
open_file = file->private_data;
|
open_file = file->private_data;
|
||||||
@ -2917,55 +2904,44 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
|
|||||||
if (!list_empty(&rdata_list))
|
if (!list_empty(&rdata_list))
|
||||||
rc = 0;
|
rc = 0;
|
||||||
|
|
||||||
|
len = iov_iter_count(&to);
|
||||||
/* the loop below should proceed in the order of increasing offsets */
|
/* the loop below should proceed in the order of increasing offsets */
|
||||||
restart_loop:
|
|
||||||
list_for_each_entry_safe(rdata, tmp, &rdata_list, list) {
|
list_for_each_entry_safe(rdata, tmp, &rdata_list, list) {
|
||||||
|
again:
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
ssize_t copied;
|
|
||||||
|
|
||||||
/* FIXME: freezable sleep too? */
|
/* FIXME: freezable sleep too? */
|
||||||
rc = wait_for_completion_killable(&rdata->done);
|
rc = wait_for_completion_killable(&rdata->done);
|
||||||
if (rc)
|
if (rc)
|
||||||
rc = -EINTR;
|
rc = -EINTR;
|
||||||
else if (rdata->result)
|
else if (rdata->result) {
|
||||||
rc = rdata->result;
|
rc = rdata->result;
|
||||||
else {
|
/* resend call if it's a retryable error */
|
||||||
rc = cifs_readdata_to_iov(rdata, iov,
|
if (rc == -EAGAIN) {
|
||||||
nr_segs, *poffset,
|
rc = cifs_retry_async_readv(rdata);
|
||||||
&copied);
|
goto again;
|
||||||
total_read += copied;
|
}
|
||||||
|
} else {
|
||||||
|
rc = cifs_readdata_to_iov(rdata, &to);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* resend call if it's a retryable error */
|
|
||||||
if (rc == -EAGAIN) {
|
|
||||||
rc = cifs_retry_async_readv(rdata);
|
|
||||||
goto restart_loop;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
list_del_init(&rdata->list);
|
list_del_init(&rdata->list);
|
||||||
kref_put(&rdata->refcount, cifs_uncached_readdata_release);
|
kref_put(&rdata->refcount, cifs_uncached_readdata_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
total_read = len - iov_iter_count(&to);
|
||||||
|
|
||||||
cifs_stats_bytes_read(tcon, total_read);
|
cifs_stats_bytes_read(tcon, total_read);
|
||||||
*poffset += total_read;
|
|
||||||
|
|
||||||
/* mask nodata case */
|
/* mask nodata case */
|
||||||
if (rc == -ENODATA)
|
if (rc == -ENODATA)
|
||||||
rc = 0;
|
rc = 0;
|
||||||
|
|
||||||
return total_read ? total_read : rc;
|
if (total_read) {
|
||||||
}
|
iocb->ki_pos = pos + total_read;
|
||||||
|
return total_read;
|
||||||
ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov,
|
}
|
||||||
unsigned long nr_segs, loff_t pos)
|
return rc;
|
||||||
{
|
|
||||||
ssize_t read;
|
|
||||||
|
|
||||||
read = cifs_iovec_read(iocb->ki_filp, iov, nr_segs, &pos);
|
|
||||||
if (read > 0)
|
|
||||||
iocb->ki_pos = pos;
|
|
||||||
|
|
||||||
return read;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t
|
ssize_t
|
||||||
|
@ -813,7 +813,7 @@ EXPORT_SYMBOL(kernel_read);
|
|||||||
|
|
||||||
ssize_t read_code(struct file *file, unsigned long addr, loff_t pos, size_t len)
|
ssize_t read_code(struct file *file, unsigned long addr, loff_t pos, size_t len)
|
||||||
{
|
{
|
||||||
ssize_t res = file->f_op->read(file, (void __user *)addr, len, &pos);
|
ssize_t res = vfs_read(file, (void __user *)addr, len, &pos);
|
||||||
if (res > 0)
|
if (res > 0)
|
||||||
flush_icache_range(addr, addr + len);
|
flush_icache_range(addr, addr + len);
|
||||||
return res;
|
return res;
|
||||||
|
@ -146,7 +146,7 @@ ext4_file_dio_write(struct kiocb *iocb, const struct iovec *iov,
|
|||||||
overwrite = 1;
|
overwrite = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
|
ret = __generic_file_aio_write(iocb, iov, nr_segs);
|
||||||
mutex_unlock(&inode->i_mutex);
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
|
11
fs/file.c
11
fs/file.c
@ -25,7 +25,10 @@
|
|||||||
|
|
||||||
int sysctl_nr_open __read_mostly = 1024*1024;
|
int sysctl_nr_open __read_mostly = 1024*1024;
|
||||||
int sysctl_nr_open_min = BITS_PER_LONG;
|
int sysctl_nr_open_min = BITS_PER_LONG;
|
||||||
int sysctl_nr_open_max = 1024 * 1024; /* raised later */
|
/* our max() is unusable in constant expressions ;-/ */
|
||||||
|
#define __const_max(x, y) ((x) < (y) ? (x) : (y))
|
||||||
|
int sysctl_nr_open_max = __const_max(INT_MAX, ~(size_t)0/sizeof(void *)) &
|
||||||
|
-BITS_PER_LONG;
|
||||||
|
|
||||||
static void *alloc_fdmem(size_t size)
|
static void *alloc_fdmem(size_t size)
|
||||||
{
|
{
|
||||||
@ -429,12 +432,6 @@ void exit_files(struct task_struct *tsk)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void __init files_defer_init(void)
|
|
||||||
{
|
|
||||||
sysctl_nr_open_max = min((size_t)INT_MAX, ~(size_t)0/sizeof(void *)) &
|
|
||||||
-BITS_PER_LONG;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct files_struct init_files = {
|
struct files_struct init_files = {
|
||||||
.count = ATOMIC_INIT(1),
|
.count = ATOMIC_INIT(1),
|
||||||
.fdt = &init_files.fdtab,
|
.fdt = &init_files.fdtab,
|
||||||
|
@ -52,7 +52,6 @@ static void file_free_rcu(struct rcu_head *head)
|
|||||||
static inline void file_free(struct file *f)
|
static inline void file_free(struct file *f)
|
||||||
{
|
{
|
||||||
percpu_counter_dec(&nr_files);
|
percpu_counter_dec(&nr_files);
|
||||||
file_check_state(f);
|
|
||||||
call_rcu(&f->f_u.fu_rcuhead, file_free_rcu);
|
call_rcu(&f->f_u.fu_rcuhead, file_free_rcu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,47 +177,12 @@ struct file *alloc_file(struct path *path, fmode_t mode,
|
|||||||
file->f_mapping = path->dentry->d_inode->i_mapping;
|
file->f_mapping = path->dentry->d_inode->i_mapping;
|
||||||
file->f_mode = mode;
|
file->f_mode = mode;
|
||||||
file->f_op = fop;
|
file->f_op = fop;
|
||||||
|
|
||||||
/*
|
|
||||||
* These mounts don't really matter in practice
|
|
||||||
* for r/o bind mounts. They aren't userspace-
|
|
||||||
* visible. We do this for consistency, and so
|
|
||||||
* that we can do debugging checks at __fput()
|
|
||||||
*/
|
|
||||||
if ((mode & FMODE_WRITE) && !special_file(path->dentry->d_inode->i_mode)) {
|
|
||||||
file_take_write(file);
|
|
||||||
WARN_ON(mnt_clone_write(path->mnt));
|
|
||||||
}
|
|
||||||
if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
|
if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
|
||||||
i_readcount_inc(path->dentry->d_inode);
|
i_readcount_inc(path->dentry->d_inode);
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(alloc_file);
|
EXPORT_SYMBOL(alloc_file);
|
||||||
|
|
||||||
/**
|
|
||||||
* drop_file_write_access - give up ability to write to a file
|
|
||||||
* @file: the file to which we will stop writing
|
|
||||||
*
|
|
||||||
* This is a central place which will give up the ability
|
|
||||||
* to write to @file, along with access to write through
|
|
||||||
* its vfsmount.
|
|
||||||
*/
|
|
||||||
static void drop_file_write_access(struct file *file)
|
|
||||||
{
|
|
||||||
struct vfsmount *mnt = file->f_path.mnt;
|
|
||||||
struct dentry *dentry = file->f_path.dentry;
|
|
||||||
struct inode *inode = dentry->d_inode;
|
|
||||||
|
|
||||||
put_write_access(inode);
|
|
||||||
|
|
||||||
if (special_file(inode->i_mode))
|
|
||||||
return;
|
|
||||||
if (file_check_writeable(file) != 0)
|
|
||||||
return;
|
|
||||||
__mnt_drop_write(mnt);
|
|
||||||
file_release_write(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* the real guts of fput() - releasing the last reference to file
|
/* the real guts of fput() - releasing the last reference to file
|
||||||
*/
|
*/
|
||||||
static void __fput(struct file *file)
|
static void __fput(struct file *file)
|
||||||
@ -253,8 +217,10 @@ static void __fput(struct file *file)
|
|||||||
put_pid(file->f_owner.pid);
|
put_pid(file->f_owner.pid);
|
||||||
if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
|
if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
|
||||||
i_readcount_dec(inode);
|
i_readcount_dec(inode);
|
||||||
if (file->f_mode & FMODE_WRITE)
|
if (file->f_mode & FMODE_WRITER) {
|
||||||
drop_file_write_access(file);
|
put_write_access(inode);
|
||||||
|
__mnt_drop_write(mnt);
|
||||||
|
}
|
||||||
file->f_path.dentry = NULL;
|
file->f_path.dentry = NULL;
|
||||||
file->f_path.mnt = NULL;
|
file->f_path.mnt = NULL;
|
||||||
file->f_inode = NULL;
|
file->f_inode = NULL;
|
||||||
@ -359,6 +325,5 @@ void __init files_init(unsigned long mempages)
|
|||||||
|
|
||||||
n = (mempages * (PAGE_SIZE / 1024)) / 10;
|
n = (mempages * (PAGE_SIZE / 1024)) / 10;
|
||||||
files_stat.max_files = max_t(unsigned long, n, NR_FILE);
|
files_stat.max_files = max_t(unsigned long, n, NR_FILE);
|
||||||
files_defer_init();
|
|
||||||
percpu_counter_init(&nr_files, 0);
|
percpu_counter_init(&nr_files, 0);
|
||||||
}
|
}
|
||||||
|
@ -667,15 +667,15 @@ static void fuse_copy_finish(struct fuse_copy_state *cs)
|
|||||||
struct pipe_buffer *buf = cs->currbuf;
|
struct pipe_buffer *buf = cs->currbuf;
|
||||||
|
|
||||||
if (!cs->write) {
|
if (!cs->write) {
|
||||||
buf->ops->unmap(cs->pipe, buf, cs->mapaddr);
|
kunmap_atomic(cs->mapaddr);
|
||||||
} else {
|
} else {
|
||||||
kunmap(buf->page);
|
kunmap_atomic(cs->mapaddr);
|
||||||
buf->len = PAGE_SIZE - cs->len;
|
buf->len = PAGE_SIZE - cs->len;
|
||||||
}
|
}
|
||||||
cs->currbuf = NULL;
|
cs->currbuf = NULL;
|
||||||
cs->mapaddr = NULL;
|
cs->mapaddr = NULL;
|
||||||
} else if (cs->mapaddr) {
|
} else if (cs->mapaddr) {
|
||||||
kunmap(cs->pg);
|
kunmap_atomic(cs->mapaddr);
|
||||||
if (cs->write) {
|
if (cs->write) {
|
||||||
flush_dcache_page(cs->pg);
|
flush_dcache_page(cs->pg);
|
||||||
set_page_dirty_lock(cs->pg);
|
set_page_dirty_lock(cs->pg);
|
||||||
@ -706,7 +706,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs)
|
|||||||
|
|
||||||
BUG_ON(!cs->nr_segs);
|
BUG_ON(!cs->nr_segs);
|
||||||
cs->currbuf = buf;
|
cs->currbuf = buf;
|
||||||
cs->mapaddr = buf->ops->map(cs->pipe, buf, 0);
|
cs->mapaddr = kmap_atomic(buf->page);
|
||||||
cs->len = buf->len;
|
cs->len = buf->len;
|
||||||
cs->buf = cs->mapaddr + buf->offset;
|
cs->buf = cs->mapaddr + buf->offset;
|
||||||
cs->pipebufs++;
|
cs->pipebufs++;
|
||||||
@ -726,7 +726,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs)
|
|||||||
buf->len = 0;
|
buf->len = 0;
|
||||||
|
|
||||||
cs->currbuf = buf;
|
cs->currbuf = buf;
|
||||||
cs->mapaddr = kmap(page);
|
cs->mapaddr = kmap_atomic(page);
|
||||||
cs->buf = cs->mapaddr;
|
cs->buf = cs->mapaddr;
|
||||||
cs->len = PAGE_SIZE;
|
cs->len = PAGE_SIZE;
|
||||||
cs->pipebufs++;
|
cs->pipebufs++;
|
||||||
@ -745,7 +745,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs)
|
|||||||
return err;
|
return err;
|
||||||
BUG_ON(err != 1);
|
BUG_ON(err != 1);
|
||||||
offset = cs->addr % PAGE_SIZE;
|
offset = cs->addr % PAGE_SIZE;
|
||||||
cs->mapaddr = kmap(cs->pg);
|
cs->mapaddr = kmap_atomic(cs->pg);
|
||||||
cs->buf = cs->mapaddr + offset;
|
cs->buf = cs->mapaddr + offset;
|
||||||
cs->len = min(PAGE_SIZE - offset, cs->seglen);
|
cs->len = min(PAGE_SIZE - offset, cs->seglen);
|
||||||
cs->seglen -= cs->len;
|
cs->seglen -= cs->len;
|
||||||
@ -874,7 +874,7 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
|
|||||||
out_fallback_unlock:
|
out_fallback_unlock:
|
||||||
unlock_page(newpage);
|
unlock_page(newpage);
|
||||||
out_fallback:
|
out_fallback:
|
||||||
cs->mapaddr = buf->ops->map(cs->pipe, buf, 1);
|
cs->mapaddr = kmap_atomic(buf->page);
|
||||||
cs->buf = cs->mapaddr + buf->offset;
|
cs->buf = cs->mapaddr + buf->offset;
|
||||||
|
|
||||||
err = lock_request(cs->fc, cs->req);
|
err = lock_request(cs->fc, cs->req);
|
||||||
|
@ -1086,9 +1086,7 @@ static ssize_t fuse_fill_write_pages(struct fuse_req *req,
|
|||||||
if (mapping_writably_mapped(mapping))
|
if (mapping_writably_mapped(mapping))
|
||||||
flush_dcache_page(page);
|
flush_dcache_page(page);
|
||||||
|
|
||||||
pagefault_disable();
|
|
||||||
tmp = iov_iter_copy_from_user_atomic(page, ii, offset, bytes);
|
tmp = iov_iter_copy_from_user_atomic(page, ii, offset, bytes);
|
||||||
pagefault_enable();
|
|
||||||
flush_dcache_page(page);
|
flush_dcache_page(page);
|
||||||
|
|
||||||
mark_page_accessed(page);
|
mark_page_accessed(page);
|
||||||
@ -1237,8 +1235,7 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (file->f_flags & O_DIRECT) {
|
if (file->f_flags & O_DIRECT) {
|
||||||
written = generic_file_direct_write(iocb, iov, &nr_segs,
|
written = generic_file_direct_write(iocb, iov, &nr_segs, pos,
|
||||||
pos, &iocb->ki_pos,
|
|
||||||
count, ocount);
|
count, ocount);
|
||||||
if (written < 0 || written == count)
|
if (written < 0 || written == count)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -10,7 +10,7 @@ struct mnt_namespace {
|
|||||||
struct user_namespace *user_ns;
|
struct user_namespace *user_ns;
|
||||||
u64 seq; /* Sequence number to prevent loops */
|
u64 seq; /* Sequence number to prevent loops */
|
||||||
wait_queue_head_t poll;
|
wait_queue_head_t poll;
|
||||||
int event;
|
u64 event;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mnt_pcp {
|
struct mnt_pcp {
|
||||||
@ -104,6 +104,9 @@ struct proc_mounts {
|
|||||||
struct mnt_namespace *ns;
|
struct mnt_namespace *ns;
|
||||||
struct path root;
|
struct path root;
|
||||||
int (*show)(struct seq_file *, struct vfsmount *);
|
int (*show)(struct seq_file *, struct vfsmount *);
|
||||||
|
void *cached_mount;
|
||||||
|
u64 cached_event;
|
||||||
|
loff_t cached_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define proc_mounts(p) (container_of((p), struct proc_mounts, m))
|
#define proc_mounts(p) (container_of((p), struct proc_mounts, m))
|
||||||
|
67
fs/namei.c
67
fs/namei.c
@ -358,6 +358,7 @@ int generic_permission(struct inode *inode, int mask)
|
|||||||
|
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(generic_permission);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We _really_ want to just do "generic_permission()" without
|
* We _really_ want to just do "generic_permission()" without
|
||||||
@ -455,6 +456,7 @@ int inode_permission(struct inode *inode, int mask)
|
|||||||
return retval;
|
return retval;
|
||||||
return __inode_permission(inode, mask);
|
return __inode_permission(inode, mask);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(inode_permission);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* path_get - get a reference to a path
|
* path_get - get a reference to a path
|
||||||
@ -924,6 +926,7 @@ int follow_up(struct path *path)
|
|||||||
path->mnt = &parent->mnt;
|
path->mnt = &parent->mnt;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(follow_up);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Perform an automount
|
* Perform an automount
|
||||||
@ -1085,6 +1088,7 @@ int follow_down_one(struct path *path)
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(follow_down_one);
|
||||||
|
|
||||||
static inline bool managed_dentry_might_block(struct dentry *dentry)
|
static inline bool managed_dentry_might_block(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
@ -1223,6 +1227,7 @@ int follow_down(struct path *path)
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(follow_down);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Skip to top of mountpoint pile in refwalk mode for follow_dotdot()
|
* Skip to top of mountpoint pile in refwalk mode for follow_dotdot()
|
||||||
@ -2025,6 +2030,7 @@ int kern_path(const char *name, unsigned int flags, struct path *path)
|
|||||||
*path = nd.path;
|
*path = nd.path;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(kern_path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* vfs_path_lookup - lookup a file path relative to a dentry-vfsmount pair
|
* vfs_path_lookup - lookup a file path relative to a dentry-vfsmount pair
|
||||||
@ -2049,6 +2055,7 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
|
|||||||
*path = nd.path;
|
*path = nd.path;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(vfs_path_lookup);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Restricted form of lookup. Doesn't follow links, single-component only,
|
* Restricted form of lookup. Doesn't follow links, single-component only,
|
||||||
@ -2111,6 +2118,7 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
|
|||||||
|
|
||||||
return __lookup_hash(&this, base, 0);
|
return __lookup_hash(&this, base, 0);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(lookup_one_len);
|
||||||
|
|
||||||
int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
|
int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
|
||||||
struct path *path, int *empty)
|
struct path *path, int *empty)
|
||||||
@ -2135,6 +2143,7 @@ int user_path_at(int dfd, const char __user *name, unsigned flags,
|
|||||||
{
|
{
|
||||||
return user_path_at_empty(dfd, name, flags, path, NULL);
|
return user_path_at_empty(dfd, name, flags, path, NULL);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(user_path_at);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NB: most callers don't do anything directly with the reference to the
|
* NB: most callers don't do anything directly with the reference to the
|
||||||
@ -2477,6 +2486,7 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
|
|||||||
mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD);
|
mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(lock_rename);
|
||||||
|
|
||||||
void unlock_rename(struct dentry *p1, struct dentry *p2)
|
void unlock_rename(struct dentry *p1, struct dentry *p2)
|
||||||
{
|
{
|
||||||
@ -2486,6 +2496,7 @@ void unlock_rename(struct dentry *p1, struct dentry *p2)
|
|||||||
mutex_unlock(&p1->d_inode->i_sb->s_vfs_rename_mutex);
|
mutex_unlock(&p1->d_inode->i_sb->s_vfs_rename_mutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(unlock_rename);
|
||||||
|
|
||||||
int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||||
bool want_excl)
|
bool want_excl)
|
||||||
@ -2506,6 +2517,7 @@ int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
|||||||
fsnotify_create(dir, dentry);
|
fsnotify_create(dir, dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(vfs_create);
|
||||||
|
|
||||||
static int may_open(struct path *path, int acc_mode, int flag)
|
static int may_open(struct path *path, int acc_mode, int flag)
|
||||||
{
|
{
|
||||||
@ -3375,6 +3387,7 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
|
|||||||
fsnotify_create(dir, dentry);
|
fsnotify_create(dir, dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(vfs_mknod);
|
||||||
|
|
||||||
static int may_mknod(umode_t mode)
|
static int may_mknod(umode_t mode)
|
||||||
{
|
{
|
||||||
@ -3464,6 +3477,7 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||||||
fsnotify_mkdir(dir, dentry);
|
fsnotify_mkdir(dir, dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(vfs_mkdir);
|
||||||
|
|
||||||
SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
|
SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
|
||||||
{
|
{
|
||||||
@ -3518,6 +3532,7 @@ void dentry_unhash(struct dentry *dentry)
|
|||||||
__d_drop(dentry);
|
__d_drop(dentry);
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(dentry_unhash);
|
||||||
|
|
||||||
int vfs_rmdir(struct inode *dir, struct dentry *dentry)
|
int vfs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
@ -3555,6 +3570,7 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
|
|||||||
d_delete(dentry);
|
d_delete(dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(vfs_rmdir);
|
||||||
|
|
||||||
static long do_rmdir(int dfd, const char __user *pathname)
|
static long do_rmdir(int dfd, const char __user *pathname)
|
||||||
{
|
{
|
||||||
@ -3672,6 +3688,7 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegate
|
|||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(vfs_unlink);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure that the actual truncation of the file will occur outside its
|
* Make sure that the actual truncation of the file will occur outside its
|
||||||
@ -3785,6 +3802,7 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
|
|||||||
fsnotify_create(dir, dentry);
|
fsnotify_create(dir, dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(vfs_symlink);
|
||||||
|
|
||||||
SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
|
SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
|
||||||
int, newdfd, const char __user *, newname)
|
int, newdfd, const char __user *, newname)
|
||||||
@ -3893,6 +3911,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
|
|||||||
fsnotify_link(dir, inode, new_dentry);
|
fsnotify_link(dir, inode, new_dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(vfs_link);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hardlinks are often used in delicate situations. We avoid
|
* Hardlinks are often used in delicate situations. We avoid
|
||||||
@ -4152,6 +4171,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(vfs_rename);
|
||||||
|
|
||||||
SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
|
SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
|
||||||
int, newdfd, const char __user *, newname, unsigned int, flags)
|
int, newdfd, const char __user *, newname, unsigned int, flags)
|
||||||
@ -4304,11 +4324,9 @@ SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newna
|
|||||||
return sys_renameat2(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
|
return sys_renameat2(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen, const char *link)
|
int readlink_copy(char __user *buffer, int buflen, const char *link)
|
||||||
{
|
{
|
||||||
int len;
|
int len = PTR_ERR(link);
|
||||||
|
|
||||||
len = PTR_ERR(link);
|
|
||||||
if (IS_ERR(link))
|
if (IS_ERR(link))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@ -4320,6 +4338,7 @@ int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen, const c
|
|||||||
out:
|
out:
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(readlink_copy);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A helper for ->readlink(). This should be used *ONLY* for symlinks that
|
* A helper for ->readlink(). This should be used *ONLY* for symlinks that
|
||||||
@ -4337,11 +4356,12 @@ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
|
|||||||
if (IS_ERR(cookie))
|
if (IS_ERR(cookie))
|
||||||
return PTR_ERR(cookie);
|
return PTR_ERR(cookie);
|
||||||
|
|
||||||
res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd));
|
res = readlink_copy(buffer, buflen, nd_get_link(&nd));
|
||||||
if (dentry->d_inode->i_op->put_link)
|
if (dentry->d_inode->i_op->put_link)
|
||||||
dentry->d_inode->i_op->put_link(dentry, &nd, cookie);
|
dentry->d_inode->i_op->put_link(dentry, &nd, cookie);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(generic_readlink);
|
||||||
|
|
||||||
/* get the link contents into pagecache */
|
/* get the link contents into pagecache */
|
||||||
static char *page_getlink(struct dentry * dentry, struct page **ppage)
|
static char *page_getlink(struct dentry * dentry, struct page **ppage)
|
||||||
@ -4361,14 +4381,14 @@ static char *page_getlink(struct dentry * dentry, struct page **ppage)
|
|||||||
int page_readlink(struct dentry *dentry, char __user *buffer, int buflen)
|
int page_readlink(struct dentry *dentry, char __user *buffer, int buflen)
|
||||||
{
|
{
|
||||||
struct page *page = NULL;
|
struct page *page = NULL;
|
||||||
char *s = page_getlink(dentry, &page);
|
int res = readlink_copy(buffer, buflen, page_getlink(dentry, &page));
|
||||||
int res = vfs_readlink(dentry,buffer,buflen,s);
|
|
||||||
if (page) {
|
if (page) {
|
||||||
kunmap(page);
|
kunmap(page);
|
||||||
page_cache_release(page);
|
page_cache_release(page);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(page_readlink);
|
||||||
|
|
||||||
void *page_follow_link_light(struct dentry *dentry, struct nameidata *nd)
|
void *page_follow_link_light(struct dentry *dentry, struct nameidata *nd)
|
||||||
{
|
{
|
||||||
@ -4376,6 +4396,7 @@ void *page_follow_link_light(struct dentry *dentry, struct nameidata *nd)
|
|||||||
nd_set_link(nd, page_getlink(dentry, &page));
|
nd_set_link(nd, page_getlink(dentry, &page));
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(page_follow_link_light);
|
||||||
|
|
||||||
void page_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
|
void page_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
|
||||||
{
|
{
|
||||||
@ -4386,6 +4407,7 @@ void page_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
|
|||||||
page_cache_release(page);
|
page_cache_release(page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(page_put_link);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The nofs argument instructs pagecache_write_begin to pass AOP_FLAG_NOFS
|
* The nofs argument instructs pagecache_write_begin to pass AOP_FLAG_NOFS
|
||||||
@ -4423,45 +4445,18 @@ int __page_symlink(struct inode *inode, const char *symname, int len, int nofs)
|
|||||||
fail:
|
fail:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(__page_symlink);
|
||||||
|
|
||||||
int page_symlink(struct inode *inode, const char *symname, int len)
|
int page_symlink(struct inode *inode, const char *symname, int len)
|
||||||
{
|
{
|
||||||
return __page_symlink(inode, symname, len,
|
return __page_symlink(inode, symname, len,
|
||||||
!(mapping_gfp_mask(inode->i_mapping) & __GFP_FS));
|
!(mapping_gfp_mask(inode->i_mapping) & __GFP_FS));
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(page_symlink);
|
||||||
|
|
||||||
const struct inode_operations page_symlink_inode_operations = {
|
const struct inode_operations page_symlink_inode_operations = {
|
||||||
.readlink = generic_readlink,
|
.readlink = generic_readlink,
|
||||||
.follow_link = page_follow_link_light,
|
.follow_link = page_follow_link_light,
|
||||||
.put_link = page_put_link,
|
.put_link = page_put_link,
|
||||||
};
|
};
|
||||||
|
|
||||||
EXPORT_SYMBOL(user_path_at);
|
|
||||||
EXPORT_SYMBOL(follow_down_one);
|
|
||||||
EXPORT_SYMBOL(follow_down);
|
|
||||||
EXPORT_SYMBOL(follow_up);
|
|
||||||
EXPORT_SYMBOL(get_write_access); /* nfsd */
|
|
||||||
EXPORT_SYMBOL(lock_rename);
|
|
||||||
EXPORT_SYMBOL(lookup_one_len);
|
|
||||||
EXPORT_SYMBOL(page_follow_link_light);
|
|
||||||
EXPORT_SYMBOL(page_put_link);
|
|
||||||
EXPORT_SYMBOL(page_readlink);
|
|
||||||
EXPORT_SYMBOL(__page_symlink);
|
|
||||||
EXPORT_SYMBOL(page_symlink);
|
|
||||||
EXPORT_SYMBOL(page_symlink_inode_operations);
|
EXPORT_SYMBOL(page_symlink_inode_operations);
|
||||||
EXPORT_SYMBOL(kern_path);
|
|
||||||
EXPORT_SYMBOL(vfs_path_lookup);
|
|
||||||
EXPORT_SYMBOL(inode_permission);
|
|
||||||
EXPORT_SYMBOL(unlock_rename);
|
|
||||||
EXPORT_SYMBOL(vfs_create);
|
|
||||||
EXPORT_SYMBOL(vfs_link);
|
|
||||||
EXPORT_SYMBOL(vfs_mkdir);
|
|
||||||
EXPORT_SYMBOL(vfs_mknod);
|
|
||||||
EXPORT_SYMBOL(generic_permission);
|
|
||||||
EXPORT_SYMBOL(vfs_readlink);
|
|
||||||
EXPORT_SYMBOL(vfs_rename);
|
|
||||||
EXPORT_SYMBOL(vfs_rmdir);
|
|
||||||
EXPORT_SYMBOL(vfs_symlink);
|
|
||||||
EXPORT_SYMBOL(vfs_unlink);
|
|
||||||
EXPORT_SYMBOL(dentry_unhash);
|
|
||||||
EXPORT_SYMBOL(generic_readlink);
|
|
||||||
|
@ -52,7 +52,7 @@ static int __init set_mphash_entries(char *str)
|
|||||||
}
|
}
|
||||||
__setup("mphash_entries=", set_mphash_entries);
|
__setup("mphash_entries=", set_mphash_entries);
|
||||||
|
|
||||||
static int event;
|
static u64 event;
|
||||||
static DEFINE_IDA(mnt_id_ida);
|
static DEFINE_IDA(mnt_id_ida);
|
||||||
static DEFINE_IDA(mnt_group_ida);
|
static DEFINE_IDA(mnt_group_ida);
|
||||||
static DEFINE_SPINLOCK(mnt_id_lock);
|
static DEFINE_SPINLOCK(mnt_id_lock);
|
||||||
@ -414,9 +414,7 @@ EXPORT_SYMBOL_GPL(mnt_clone_write);
|
|||||||
*/
|
*/
|
||||||
int __mnt_want_write_file(struct file *file)
|
int __mnt_want_write_file(struct file *file)
|
||||||
{
|
{
|
||||||
struct inode *inode = file_inode(file);
|
if (!(file->f_mode & FMODE_WRITER))
|
||||||
|
|
||||||
if (!(file->f_mode & FMODE_WRITE) || special_file(inode->i_mode))
|
|
||||||
return __mnt_want_write(file->f_path.mnt);
|
return __mnt_want_write(file->f_path.mnt);
|
||||||
else
|
else
|
||||||
return mnt_clone_write(file->f_path.mnt);
|
return mnt_clone_write(file->f_path.mnt);
|
||||||
@ -570,13 +568,17 @@ int sb_prepare_remount_readonly(struct super_block *sb)
|
|||||||
static void free_vfsmnt(struct mount *mnt)
|
static void free_vfsmnt(struct mount *mnt)
|
||||||
{
|
{
|
||||||
kfree(mnt->mnt_devname);
|
kfree(mnt->mnt_devname);
|
||||||
mnt_free_id(mnt);
|
|
||||||
#ifdef CONFIG_SMP
|
#ifdef CONFIG_SMP
|
||||||
free_percpu(mnt->mnt_pcp);
|
free_percpu(mnt->mnt_pcp);
|
||||||
#endif
|
#endif
|
||||||
kmem_cache_free(mnt_cache, mnt);
|
kmem_cache_free(mnt_cache, mnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void delayed_free_vfsmnt(struct rcu_head *head)
|
||||||
|
{
|
||||||
|
free_vfsmnt(container_of(head, struct mount, mnt_rcu));
|
||||||
|
}
|
||||||
|
|
||||||
/* call under rcu_read_lock */
|
/* call under rcu_read_lock */
|
||||||
bool legitimize_mnt(struct vfsmount *bastard, unsigned seq)
|
bool legitimize_mnt(struct vfsmount *bastard, unsigned seq)
|
||||||
{
|
{
|
||||||
@ -848,6 +850,7 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
|
|||||||
|
|
||||||
root = mount_fs(type, flags, name, data);
|
root = mount_fs(type, flags, name, data);
|
||||||
if (IS_ERR(root)) {
|
if (IS_ERR(root)) {
|
||||||
|
mnt_free_id(mnt);
|
||||||
free_vfsmnt(mnt);
|
free_vfsmnt(mnt);
|
||||||
return ERR_CAST(root);
|
return ERR_CAST(root);
|
||||||
}
|
}
|
||||||
@ -885,7 +888,7 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
|
|||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~MNT_WRITE_HOLD;
|
mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~(MNT_WRITE_HOLD|MNT_MARKED);
|
||||||
/* Don't allow unprivileged users to change mount flags */
|
/* Don't allow unprivileged users to change mount flags */
|
||||||
if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY))
|
if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY))
|
||||||
mnt->mnt.mnt_flags |= MNT_LOCK_READONLY;
|
mnt->mnt.mnt_flags |= MNT_LOCK_READONLY;
|
||||||
@ -928,20 +931,11 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
|
|||||||
return mnt;
|
return mnt;
|
||||||
|
|
||||||
out_free:
|
out_free:
|
||||||
|
mnt_free_id(mnt);
|
||||||
free_vfsmnt(mnt);
|
free_vfsmnt(mnt);
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void delayed_free(struct rcu_head *head)
|
|
||||||
{
|
|
||||||
struct mount *mnt = container_of(head, struct mount, mnt_rcu);
|
|
||||||
kfree(mnt->mnt_devname);
|
|
||||||
#ifdef CONFIG_SMP
|
|
||||||
free_percpu(mnt->mnt_pcp);
|
|
||||||
#endif
|
|
||||||
kmem_cache_free(mnt_cache, mnt);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mntput_no_expire(struct mount *mnt)
|
static void mntput_no_expire(struct mount *mnt)
|
||||||
{
|
{
|
||||||
put_again:
|
put_again:
|
||||||
@ -991,7 +985,7 @@ static void mntput_no_expire(struct mount *mnt)
|
|||||||
dput(mnt->mnt.mnt_root);
|
dput(mnt->mnt.mnt_root);
|
||||||
deactivate_super(mnt->mnt.mnt_sb);
|
deactivate_super(mnt->mnt.mnt_sb);
|
||||||
mnt_free_id(mnt);
|
mnt_free_id(mnt);
|
||||||
call_rcu(&mnt->mnt_rcu, delayed_free);
|
call_rcu(&mnt->mnt_rcu, delayed_free_vfsmnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mntput(struct vfsmount *mnt)
|
void mntput(struct vfsmount *mnt)
|
||||||
@ -1100,14 +1094,29 @@ static void *m_start(struct seq_file *m, loff_t *pos)
|
|||||||
struct proc_mounts *p = proc_mounts(m);
|
struct proc_mounts *p = proc_mounts(m);
|
||||||
|
|
||||||
down_read(&namespace_sem);
|
down_read(&namespace_sem);
|
||||||
return seq_list_start(&p->ns->list, *pos);
|
if (p->cached_event == p->ns->event) {
|
||||||
|
void *v = p->cached_mount;
|
||||||
|
if (*pos == p->cached_index)
|
||||||
|
return v;
|
||||||
|
if (*pos == p->cached_index + 1) {
|
||||||
|
v = seq_list_next(v, &p->ns->list, &p->cached_index);
|
||||||
|
return p->cached_mount = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p->cached_event = p->ns->event;
|
||||||
|
p->cached_mount = seq_list_start(&p->ns->list, *pos);
|
||||||
|
p->cached_index = *pos;
|
||||||
|
return p->cached_mount;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *m_next(struct seq_file *m, void *v, loff_t *pos)
|
static void *m_next(struct seq_file *m, void *v, loff_t *pos)
|
||||||
{
|
{
|
||||||
struct proc_mounts *p = proc_mounts(m);
|
struct proc_mounts *p = proc_mounts(m);
|
||||||
|
|
||||||
return seq_list_next(v, &p->ns->list, pos);
|
p->cached_mount = seq_list_next(v, &p->ns->list, pos);
|
||||||
|
p->cached_index = *pos;
|
||||||
|
return p->cached_mount;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void m_stop(struct seq_file *m, void *v)
|
static void m_stop(struct seq_file *m, void *v)
|
||||||
@ -1661,9 +1670,9 @@ static int attach_recursive_mnt(struct mount *source_mnt,
|
|||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
err = propagate_mnt(dest_mnt, dest_mp, source_mnt, &tree_list);
|
err = propagate_mnt(dest_mnt, dest_mp, source_mnt, &tree_list);
|
||||||
|
lock_mount_hash();
|
||||||
if (err)
|
if (err)
|
||||||
goto out_cleanup_ids;
|
goto out_cleanup_ids;
|
||||||
lock_mount_hash();
|
|
||||||
for (p = source_mnt; p; p = next_mnt(p, source_mnt))
|
for (p = source_mnt; p; p = next_mnt(p, source_mnt))
|
||||||
set_mnt_shared(p);
|
set_mnt_shared(p);
|
||||||
} else {
|
} else {
|
||||||
@ -1690,6 +1699,11 @@ static int attach_recursive_mnt(struct mount *source_mnt,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_cleanup_ids:
|
out_cleanup_ids:
|
||||||
|
while (!hlist_empty(&tree_list)) {
|
||||||
|
child = hlist_entry(tree_list.first, struct mount, mnt_hash);
|
||||||
|
umount_tree(child, 0);
|
||||||
|
}
|
||||||
|
unlock_mount_hash();
|
||||||
cleanup_group_ids(source_mnt, NULL);
|
cleanup_group_ids(source_mnt, NULL);
|
||||||
out:
|
out:
|
||||||
return err;
|
return err;
|
||||||
@ -2044,7 +2058,7 @@ static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags)
|
|||||||
struct mount *parent;
|
struct mount *parent;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
mnt_flags &= ~(MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL | MNT_DOOMED | MNT_SYNC_UMOUNT);
|
mnt_flags &= ~MNT_INTERNAL_FLAGS;
|
||||||
|
|
||||||
mp = lock_mount(path);
|
mp = lock_mount(path);
|
||||||
if (IS_ERR(mp))
|
if (IS_ERR(mp))
|
||||||
|
@ -470,9 +470,7 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
|
|||||||
{
|
{
|
||||||
struct ncp_mount_data_kernel data;
|
struct ncp_mount_data_kernel data;
|
||||||
struct ncp_server *server;
|
struct ncp_server *server;
|
||||||
struct file *ncp_filp;
|
|
||||||
struct inode *root_inode;
|
struct inode *root_inode;
|
||||||
struct inode *sock_inode;
|
|
||||||
struct socket *sock;
|
struct socket *sock;
|
||||||
int error;
|
int error;
|
||||||
int default_bufsize;
|
int default_bufsize;
|
||||||
@ -541,18 +539,10 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
|
|||||||
if (!uid_valid(data.mounted_uid) || !uid_valid(data.uid) ||
|
if (!uid_valid(data.mounted_uid) || !uid_valid(data.uid) ||
|
||||||
!gid_valid(data.gid))
|
!gid_valid(data.gid))
|
||||||
goto out;
|
goto out;
|
||||||
error = -EBADF;
|
sock = sockfd_lookup(data.ncp_fd, &error);
|
||||||
ncp_filp = fget(data.ncp_fd);
|
|
||||||
if (!ncp_filp)
|
|
||||||
goto out;
|
|
||||||
error = -ENOTSOCK;
|
|
||||||
sock_inode = file_inode(ncp_filp);
|
|
||||||
if (!S_ISSOCK(sock_inode->i_mode))
|
|
||||||
goto out_fput;
|
|
||||||
sock = SOCKET_I(sock_inode);
|
|
||||||
if (!sock)
|
if (!sock)
|
||||||
goto out_fput;
|
goto out;
|
||||||
|
|
||||||
if (sock->type == SOCK_STREAM)
|
if (sock->type == SOCK_STREAM)
|
||||||
default_bufsize = 0xF000;
|
default_bufsize = 0xF000;
|
||||||
else
|
else
|
||||||
@ -574,27 +564,16 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
|
|||||||
if (error)
|
if (error)
|
||||||
goto out_fput;
|
goto out_fput;
|
||||||
|
|
||||||
server->ncp_filp = ncp_filp;
|
|
||||||
server->ncp_sock = sock;
|
server->ncp_sock = sock;
|
||||||
|
|
||||||
if (data.info_fd != -1) {
|
if (data.info_fd != -1) {
|
||||||
struct socket *info_sock;
|
struct socket *info_sock = sockfd_lookup(data.info_fd, &error);
|
||||||
|
|
||||||
error = -EBADF;
|
|
||||||
server->info_filp = fget(data.info_fd);
|
|
||||||
if (!server->info_filp)
|
|
||||||
goto out_bdi;
|
|
||||||
error = -ENOTSOCK;
|
|
||||||
sock_inode = file_inode(server->info_filp);
|
|
||||||
if (!S_ISSOCK(sock_inode->i_mode))
|
|
||||||
goto out_fput2;
|
|
||||||
info_sock = SOCKET_I(sock_inode);
|
|
||||||
if (!info_sock)
|
if (!info_sock)
|
||||||
goto out_fput2;
|
goto out_bdi;
|
||||||
|
server->info_sock = info_sock;
|
||||||
error = -EBADFD;
|
error = -EBADFD;
|
||||||
if (info_sock->type != SOCK_STREAM)
|
if (info_sock->type != SOCK_STREAM)
|
||||||
goto out_fput2;
|
goto out_fput2;
|
||||||
server->info_sock = info_sock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* server->lock = 0; */
|
/* server->lock = 0; */
|
||||||
@ -766,17 +745,12 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
|
|||||||
mutex_destroy(&server->root_setup_lock);
|
mutex_destroy(&server->root_setup_lock);
|
||||||
mutex_destroy(&server->mutex);
|
mutex_destroy(&server->mutex);
|
||||||
out_fput2:
|
out_fput2:
|
||||||
if (server->info_filp)
|
if (server->info_sock)
|
||||||
fput(server->info_filp);
|
sockfd_put(server->info_sock);
|
||||||
out_bdi:
|
out_bdi:
|
||||||
bdi_destroy(&server->bdi);
|
bdi_destroy(&server->bdi);
|
||||||
out_fput:
|
out_fput:
|
||||||
/* 23/12/1998 Marcin Dalecki <dalecki@cs.net.pl>:
|
sockfd_put(sock);
|
||||||
*
|
|
||||||
* The previously used put_filp(ncp_filp); was bogus, since
|
|
||||||
* it doesn't perform proper unlocking.
|
|
||||||
*/
|
|
||||||
fput(ncp_filp);
|
|
||||||
out:
|
out:
|
||||||
put_pid(data.wdog_pid);
|
put_pid(data.wdog_pid);
|
||||||
sb->s_fs_info = NULL;
|
sb->s_fs_info = NULL;
|
||||||
@ -809,9 +783,9 @@ static void ncp_put_super(struct super_block *sb)
|
|||||||
mutex_destroy(&server->root_setup_lock);
|
mutex_destroy(&server->root_setup_lock);
|
||||||
mutex_destroy(&server->mutex);
|
mutex_destroy(&server->mutex);
|
||||||
|
|
||||||
if (server->info_filp)
|
if (server->info_sock)
|
||||||
fput(server->info_filp);
|
sockfd_put(server->info_sock);
|
||||||
fput(server->ncp_filp);
|
sockfd_put(server->ncp_sock);
|
||||||
kill_pid(server->m.wdog_pid, SIGTERM, 1);
|
kill_pid(server->m.wdog_pid, SIGTERM, 1);
|
||||||
put_pid(server->m.wdog_pid);
|
put_pid(server->m.wdog_pid);
|
||||||
|
|
||||||
|
@ -45,9 +45,7 @@ struct ncp_server {
|
|||||||
|
|
||||||
__u8 name_space[NCP_NUMBER_OF_VOLUMES + 2];
|
__u8 name_space[NCP_NUMBER_OF_VOLUMES + 2];
|
||||||
|
|
||||||
struct file *ncp_filp; /* File pointer to ncp socket */
|
|
||||||
struct socket *ncp_sock;/* ncp socket */
|
struct socket *ncp_sock;/* ncp socket */
|
||||||
struct file *info_filp;
|
|
||||||
struct socket *info_sock;
|
struct socket *info_sock;
|
||||||
|
|
||||||
u8 sequence;
|
u8 sequence;
|
||||||
|
@ -1704,8 +1704,6 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
|
|||||||
iput(bvi);
|
iput(bvi);
|
||||||
skip_large_index_stuff:
|
skip_large_index_stuff:
|
||||||
/* Setup the operations for this index inode. */
|
/* Setup the operations for this index inode. */
|
||||||
vi->i_op = NULL;
|
|
||||||
vi->i_fop = NULL;
|
|
||||||
vi->i_mapping->a_ops = &ntfs_mst_aops;
|
vi->i_mapping->a_ops = &ntfs_mst_aops;
|
||||||
vi->i_blocks = ni->allocated_size >> 9;
|
vi->i_blocks = ni->allocated_size >> 9;
|
||||||
/*
|
/*
|
||||||
|
@ -916,57 +916,30 @@ static struct o2net_msg_handler *o2net_handler_get(u32 msg_type, u32 key)
|
|||||||
|
|
||||||
static int o2net_recv_tcp_msg(struct socket *sock, void *data, size_t len)
|
static int o2net_recv_tcp_msg(struct socket *sock, void *data, size_t len)
|
||||||
{
|
{
|
||||||
int ret;
|
struct kvec vec = { .iov_len = len, .iov_base = data, };
|
||||||
mm_segment_t oldfs;
|
struct msghdr msg = { .msg_flags = MSG_DONTWAIT, };
|
||||||
struct kvec vec = {
|
return kernel_recvmsg(sock, &msg, &vec, 1, len, msg.msg_flags);
|
||||||
.iov_len = len,
|
|
||||||
.iov_base = data,
|
|
||||||
};
|
|
||||||
struct msghdr msg = {
|
|
||||||
.msg_iovlen = 1,
|
|
||||||
.msg_iov = (struct iovec *)&vec,
|
|
||||||
.msg_flags = MSG_DONTWAIT,
|
|
||||||
};
|
|
||||||
|
|
||||||
oldfs = get_fs();
|
|
||||||
set_fs(get_ds());
|
|
||||||
ret = sock_recvmsg(sock, &msg, len, msg.msg_flags);
|
|
||||||
set_fs(oldfs);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int o2net_send_tcp_msg(struct socket *sock, struct kvec *vec,
|
static int o2net_send_tcp_msg(struct socket *sock, struct kvec *vec,
|
||||||
size_t veclen, size_t total)
|
size_t veclen, size_t total)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
mm_segment_t oldfs;
|
struct msghdr msg;
|
||||||
struct msghdr msg = {
|
|
||||||
.msg_iov = (struct iovec *)vec,
|
|
||||||
.msg_iovlen = veclen,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (sock == NULL) {
|
if (sock == NULL) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
oldfs = get_fs();
|
ret = kernel_sendmsg(sock, &msg, vec, veclen, total);
|
||||||
set_fs(get_ds());
|
if (likely(ret == total))
|
||||||
ret = sock_sendmsg(sock, &msg, total);
|
return 0;
|
||||||
set_fs(oldfs);
|
mlog(ML_ERROR, "sendmsg returned %d instead of %zu\n", ret, total);
|
||||||
if (ret != total) {
|
if (ret >= 0)
|
||||||
mlog(ML_ERROR, "sendmsg returned %d instead of %zu\n", ret,
|
ret = -EPIPE; /* should be smarter, I bet */
|
||||||
total);
|
|
||||||
if (ret >= 0)
|
|
||||||
ret = -EPIPE; /* should be smarter, I bet */
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
out:
|
out:
|
||||||
if (ret < 0)
|
mlog(0, "returning error: %d\n", ret);
|
||||||
mlog(0, "returning error: %d\n", ret);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2367,15 +2367,18 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb,
|
|||||||
|
|
||||||
if (direct_io) {
|
if (direct_io) {
|
||||||
written = generic_file_direct_write(iocb, iov, &nr_segs, *ppos,
|
written = generic_file_direct_write(iocb, iov, &nr_segs, *ppos,
|
||||||
ppos, count, ocount);
|
count, ocount);
|
||||||
if (written < 0) {
|
if (written < 0) {
|
||||||
ret = written;
|
ret = written;
|
||||||
goto out_dio;
|
goto out_dio;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
struct iov_iter from;
|
||||||
|
iov_iter_init(&from, iov, nr_segs, count, 0);
|
||||||
current->backing_dev_info = file->f_mapping->backing_dev_info;
|
current->backing_dev_info = file->f_mapping->backing_dev_info;
|
||||||
written = generic_file_buffered_write(iocb, iov, nr_segs, *ppos,
|
written = generic_perform_write(file, &from, *ppos);
|
||||||
ppos, count, 0);
|
if (likely(written >= 0))
|
||||||
|
iocb->ki_pos = *ppos + written;
|
||||||
current->backing_dev_info = NULL;
|
current->backing_dev_info = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
68
fs/open.c
68
fs/open.c
@ -655,35 +655,6 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd, uid_t, user, gid_t, group)
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* You have to be very careful that these write
|
|
||||||
* counts get cleaned up in error cases and
|
|
||||||
* upon __fput(). This should probably never
|
|
||||||
* be called outside of __dentry_open().
|
|
||||||
*/
|
|
||||||
static inline int __get_file_write_access(struct inode *inode,
|
|
||||||
struct vfsmount *mnt)
|
|
||||||
{
|
|
||||||
int error;
|
|
||||||
error = get_write_access(inode);
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
/*
|
|
||||||
* Do not take mount writer counts on
|
|
||||||
* special files since no writes to
|
|
||||||
* the mount itself will occur.
|
|
||||||
*/
|
|
||||||
if (!special_file(inode->i_mode)) {
|
|
||||||
/*
|
|
||||||
* Balanced in __fput()
|
|
||||||
*/
|
|
||||||
error = __mnt_want_write(mnt);
|
|
||||||
if (error)
|
|
||||||
put_write_access(inode);
|
|
||||||
}
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
int open_check_o_direct(struct file *f)
|
int open_check_o_direct(struct file *f)
|
||||||
{
|
{
|
||||||
/* NB: we're sure to have correct a_ops only after f_op->open */
|
/* NB: we're sure to have correct a_ops only after f_op->open */
|
||||||
@ -708,26 +679,28 @@ static int do_dentry_open(struct file *f,
|
|||||||
f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK |
|
f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK |
|
||||||
FMODE_PREAD | FMODE_PWRITE;
|
FMODE_PREAD | FMODE_PWRITE;
|
||||||
|
|
||||||
if (unlikely(f->f_flags & O_PATH))
|
|
||||||
f->f_mode = FMODE_PATH;
|
|
||||||
|
|
||||||
path_get(&f->f_path);
|
path_get(&f->f_path);
|
||||||
inode = f->f_inode = f->f_path.dentry->d_inode;
|
inode = f->f_inode = f->f_path.dentry->d_inode;
|
||||||
if (f->f_mode & FMODE_WRITE) {
|
|
||||||
error = __get_file_write_access(inode, f->f_path.mnt);
|
|
||||||
if (error)
|
|
||||||
goto cleanup_file;
|
|
||||||
if (!special_file(inode->i_mode))
|
|
||||||
file_take_write(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
f->f_mapping = inode->i_mapping;
|
f->f_mapping = inode->i_mapping;
|
||||||
|
|
||||||
if (unlikely(f->f_mode & FMODE_PATH)) {
|
if (unlikely(f->f_flags & O_PATH)) {
|
||||||
|
f->f_mode = FMODE_PATH;
|
||||||
f->f_op = &empty_fops;
|
f->f_op = &empty_fops;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) {
|
||||||
|
error = get_write_access(inode);
|
||||||
|
if (unlikely(error))
|
||||||
|
goto cleanup_file;
|
||||||
|
error = __mnt_want_write(f->f_path.mnt);
|
||||||
|
if (unlikely(error)) {
|
||||||
|
put_write_access(inode);
|
||||||
|
goto cleanup_file;
|
||||||
|
}
|
||||||
|
f->f_mode |= FMODE_WRITER;
|
||||||
|
}
|
||||||
|
|
||||||
/* POSIX.1-2008/SUSv4 Section XSI 2.9.7 */
|
/* POSIX.1-2008/SUSv4 Section XSI 2.9.7 */
|
||||||
if (S_ISREG(inode->i_mode))
|
if (S_ISREG(inode->i_mode))
|
||||||
f->f_mode |= FMODE_ATOMIC_POS;
|
f->f_mode |= FMODE_ATOMIC_POS;
|
||||||
@ -764,18 +737,9 @@ static int do_dentry_open(struct file *f,
|
|||||||
|
|
||||||
cleanup_all:
|
cleanup_all:
|
||||||
fops_put(f->f_op);
|
fops_put(f->f_op);
|
||||||
if (f->f_mode & FMODE_WRITE) {
|
if (f->f_mode & FMODE_WRITER) {
|
||||||
put_write_access(inode);
|
put_write_access(inode);
|
||||||
if (!special_file(inode->i_mode)) {
|
__mnt_drop_write(f->f_path.mnt);
|
||||||
/*
|
|
||||||
* We don't consider this a real
|
|
||||||
* mnt_want/drop_write() pair
|
|
||||||
* because it all happenend right
|
|
||||||
* here, so just reset the state.
|
|
||||||
*/
|
|
||||||
file_reset_write(f);
|
|
||||||
__mnt_drop_write(f->f_path.mnt);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
cleanup_file:
|
cleanup_file:
|
||||||
path_put(&f->f_path);
|
path_put(&f->f_path);
|
||||||
|
133
fs/pipe.c
133
fs/pipe.c
@ -142,55 +142,6 @@ pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len,
|
|
||||||
int atomic)
|
|
||||||
{
|
|
||||||
unsigned long copy;
|
|
||||||
|
|
||||||
while (len > 0) {
|
|
||||||
while (!iov->iov_len)
|
|
||||||
iov++;
|
|
||||||
copy = min_t(unsigned long, len, iov->iov_len);
|
|
||||||
|
|
||||||
if (atomic) {
|
|
||||||
if (__copy_to_user_inatomic(iov->iov_base, from, copy))
|
|
||||||
return -EFAULT;
|
|
||||||
} else {
|
|
||||||
if (copy_to_user(iov->iov_base, from, copy))
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
from += copy;
|
|
||||||
len -= copy;
|
|
||||||
iov->iov_base += copy;
|
|
||||||
iov->iov_len -= copy;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Attempt to pre-fault in the user memory, so we can use atomic copies.
|
|
||||||
* Returns the number of bytes not faulted in.
|
|
||||||
*/
|
|
||||||
static int iov_fault_in_pages_write(struct iovec *iov, unsigned long len)
|
|
||||||
{
|
|
||||||
while (!iov->iov_len)
|
|
||||||
iov++;
|
|
||||||
|
|
||||||
while (len > 0) {
|
|
||||||
unsigned long this_len;
|
|
||||||
|
|
||||||
this_len = min_t(unsigned long, len, iov->iov_len);
|
|
||||||
if (fault_in_pages_writeable(iov->iov_base, this_len))
|
|
||||||
break;
|
|
||||||
|
|
||||||
len -= this_len;
|
|
||||||
iov++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pre-fault in the user memory, so we can use atomic copies.
|
* Pre-fault in the user memory, so we can use atomic copies.
|
||||||
*/
|
*/
|
||||||
@ -225,52 +176,6 @@ static void anon_pipe_buf_release(struct pipe_inode_info *pipe,
|
|||||||
page_cache_release(page);
|
page_cache_release(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* generic_pipe_buf_map - virtually map a pipe buffer
|
|
||||||
* @pipe: the pipe that the buffer belongs to
|
|
||||||
* @buf: the buffer that should be mapped
|
|
||||||
* @atomic: whether to use an atomic map
|
|
||||||
*
|
|
||||||
* Description:
|
|
||||||
* This function returns a kernel virtual address mapping for the
|
|
||||||
* pipe_buffer passed in @buf. If @atomic is set, an atomic map is provided
|
|
||||||
* and the caller has to be careful not to fault before calling
|
|
||||||
* the unmap function.
|
|
||||||
*
|
|
||||||
* Note that this function calls kmap_atomic() if @atomic != 0.
|
|
||||||
*/
|
|
||||||
void *generic_pipe_buf_map(struct pipe_inode_info *pipe,
|
|
||||||
struct pipe_buffer *buf, int atomic)
|
|
||||||
{
|
|
||||||
if (atomic) {
|
|
||||||
buf->flags |= PIPE_BUF_FLAG_ATOMIC;
|
|
||||||
return kmap_atomic(buf->page);
|
|
||||||
}
|
|
||||||
|
|
||||||
return kmap(buf->page);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(generic_pipe_buf_map);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* generic_pipe_buf_unmap - unmap a previously mapped pipe buffer
|
|
||||||
* @pipe: the pipe that the buffer belongs to
|
|
||||||
* @buf: the buffer that should be unmapped
|
|
||||||
* @map_data: the data that the mapping function returned
|
|
||||||
*
|
|
||||||
* Description:
|
|
||||||
* This function undoes the mapping that ->map() provided.
|
|
||||||
*/
|
|
||||||
void generic_pipe_buf_unmap(struct pipe_inode_info *pipe,
|
|
||||||
struct pipe_buffer *buf, void *map_data)
|
|
||||||
{
|
|
||||||
if (buf->flags & PIPE_BUF_FLAG_ATOMIC) {
|
|
||||||
buf->flags &= ~PIPE_BUF_FLAG_ATOMIC;
|
|
||||||
kunmap_atomic(map_data);
|
|
||||||
} else
|
|
||||||
kunmap(buf->page);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(generic_pipe_buf_unmap);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* generic_pipe_buf_steal - attempt to take ownership of a &pipe_buffer
|
* generic_pipe_buf_steal - attempt to take ownership of a &pipe_buffer
|
||||||
* @pipe: the pipe that the buffer belongs to
|
* @pipe: the pipe that the buffer belongs to
|
||||||
@ -351,8 +256,6 @@ EXPORT_SYMBOL(generic_pipe_buf_release);
|
|||||||
|
|
||||||
static const struct pipe_buf_operations anon_pipe_buf_ops = {
|
static const struct pipe_buf_operations anon_pipe_buf_ops = {
|
||||||
.can_merge = 1,
|
.can_merge = 1,
|
||||||
.map = generic_pipe_buf_map,
|
|
||||||
.unmap = generic_pipe_buf_unmap,
|
|
||||||
.confirm = generic_pipe_buf_confirm,
|
.confirm = generic_pipe_buf_confirm,
|
||||||
.release = anon_pipe_buf_release,
|
.release = anon_pipe_buf_release,
|
||||||
.steal = generic_pipe_buf_steal,
|
.steal = generic_pipe_buf_steal,
|
||||||
@ -361,8 +264,6 @@ static const struct pipe_buf_operations anon_pipe_buf_ops = {
|
|||||||
|
|
||||||
static const struct pipe_buf_operations packet_pipe_buf_ops = {
|
static const struct pipe_buf_operations packet_pipe_buf_ops = {
|
||||||
.can_merge = 0,
|
.can_merge = 0,
|
||||||
.map = generic_pipe_buf_map,
|
|
||||||
.unmap = generic_pipe_buf_unmap,
|
|
||||||
.confirm = generic_pipe_buf_confirm,
|
.confirm = generic_pipe_buf_confirm,
|
||||||
.release = anon_pipe_buf_release,
|
.release = anon_pipe_buf_release,
|
||||||
.steal = generic_pipe_buf_steal,
|
.steal = generic_pipe_buf_steal,
|
||||||
@ -379,12 +280,15 @@ pipe_read(struct kiocb *iocb, const struct iovec *_iov,
|
|||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
struct iovec *iov = (struct iovec *)_iov;
|
struct iovec *iov = (struct iovec *)_iov;
|
||||||
size_t total_len;
|
size_t total_len;
|
||||||
|
struct iov_iter iter;
|
||||||
|
|
||||||
total_len = iov_length(iov, nr_segs);
|
total_len = iov_length(iov, nr_segs);
|
||||||
/* Null read succeeds. */
|
/* Null read succeeds. */
|
||||||
if (unlikely(total_len == 0))
|
if (unlikely(total_len == 0))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
iov_iter_init(&iter, iov, nr_segs, total_len, 0);
|
||||||
|
|
||||||
do_wakeup = 0;
|
do_wakeup = 0;
|
||||||
ret = 0;
|
ret = 0;
|
||||||
__pipe_lock(pipe);
|
__pipe_lock(pipe);
|
||||||
@ -394,9 +298,9 @@ pipe_read(struct kiocb *iocb, const struct iovec *_iov,
|
|||||||
int curbuf = pipe->curbuf;
|
int curbuf = pipe->curbuf;
|
||||||
struct pipe_buffer *buf = pipe->bufs + curbuf;
|
struct pipe_buffer *buf = pipe->bufs + curbuf;
|
||||||
const struct pipe_buf_operations *ops = buf->ops;
|
const struct pipe_buf_operations *ops = buf->ops;
|
||||||
void *addr;
|
|
||||||
size_t chars = buf->len;
|
size_t chars = buf->len;
|
||||||
int error, atomic;
|
size_t written;
|
||||||
|
int error;
|
||||||
|
|
||||||
if (chars > total_len)
|
if (chars > total_len)
|
||||||
chars = total_len;
|
chars = total_len;
|
||||||
@ -408,21 +312,10 @@ pipe_read(struct kiocb *iocb, const struct iovec *_iov,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic = !iov_fault_in_pages_write(iov, chars);
|
written = copy_page_to_iter(buf->page, buf->offset, chars, &iter);
|
||||||
redo:
|
if (unlikely(written < chars)) {
|
||||||
addr = ops->map(pipe, buf, atomic);
|
|
||||||
error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars, atomic);
|
|
||||||
ops->unmap(pipe, buf, addr);
|
|
||||||
if (unlikely(error)) {
|
|
||||||
/*
|
|
||||||
* Just retry with the slow path if we failed.
|
|
||||||
*/
|
|
||||||
if (atomic) {
|
|
||||||
atomic = 0;
|
|
||||||
goto redo;
|
|
||||||
}
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
ret = error;
|
ret = -EFAULT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ret += chars;
|
ret += chars;
|
||||||
@ -538,10 +431,16 @@ pipe_write(struct kiocb *iocb, const struct iovec *_iov,
|
|||||||
|
|
||||||
iov_fault_in_pages_read(iov, chars);
|
iov_fault_in_pages_read(iov, chars);
|
||||||
redo1:
|
redo1:
|
||||||
addr = ops->map(pipe, buf, atomic);
|
if (atomic)
|
||||||
|
addr = kmap_atomic(buf->page);
|
||||||
|
else
|
||||||
|
addr = kmap(buf->page);
|
||||||
error = pipe_iov_copy_from_user(offset + addr, iov,
|
error = pipe_iov_copy_from_user(offset + addr, iov,
|
||||||
chars, atomic);
|
chars, atomic);
|
||||||
ops->unmap(pipe, buf, addr);
|
if (atomic)
|
||||||
|
kunmap_atomic(addr);
|
||||||
|
else
|
||||||
|
kunmap(buf->page);
|
||||||
ret = error;
|
ret = error;
|
||||||
do_wakeup = 1;
|
do_wakeup = 1;
|
||||||
if (error) {
|
if (error) {
|
||||||
|
198
fs/pnode.c
198
fs/pnode.c
@ -164,46 +164,94 @@ static struct mount *propagation_next(struct mount *m,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static struct mount *next_group(struct mount *m, struct mount *origin)
|
||||||
* return the source mount to be used for cloning
|
|
||||||
*
|
|
||||||
* @dest the current destination mount
|
|
||||||
* @last_dest the last seen destination mount
|
|
||||||
* @last_src the last seen source mount
|
|
||||||
* @type return CL_SLAVE if the new mount has to be
|
|
||||||
* cloned as a slave.
|
|
||||||
*/
|
|
||||||
static struct mount *get_source(struct mount *dest,
|
|
||||||
struct mount *last_dest,
|
|
||||||
struct mount *last_src,
|
|
||||||
int *type)
|
|
||||||
{
|
{
|
||||||
struct mount *p_last_src = NULL;
|
while (1) {
|
||||||
struct mount *p_last_dest = NULL;
|
while (1) {
|
||||||
|
struct mount *next;
|
||||||
while (last_dest != dest->mnt_master) {
|
if (!IS_MNT_NEW(m) && !list_empty(&m->mnt_slave_list))
|
||||||
p_last_dest = last_dest;
|
return first_slave(m);
|
||||||
p_last_src = last_src;
|
next = next_peer(m);
|
||||||
last_dest = last_dest->mnt_master;
|
if (m->mnt_group_id == origin->mnt_group_id) {
|
||||||
last_src = last_src->mnt_master;
|
if (next == origin)
|
||||||
}
|
return NULL;
|
||||||
|
} else if (m->mnt_slave.next != &next->mnt_slave)
|
||||||
if (p_last_dest) {
|
break;
|
||||||
do {
|
m = next;
|
||||||
p_last_dest = next_peer(p_last_dest);
|
|
||||||
} while (IS_MNT_NEW(p_last_dest));
|
|
||||||
/* is that a peer of the earlier? */
|
|
||||||
if (dest == p_last_dest) {
|
|
||||||
*type = CL_MAKE_SHARED;
|
|
||||||
return p_last_src;
|
|
||||||
}
|
}
|
||||||
|
/* m is the last peer */
|
||||||
|
while (1) {
|
||||||
|
struct mount *master = m->mnt_master;
|
||||||
|
if (m->mnt_slave.next != &master->mnt_slave_list)
|
||||||
|
return next_slave(m);
|
||||||
|
m = next_peer(master);
|
||||||
|
if (master->mnt_group_id == origin->mnt_group_id)
|
||||||
|
break;
|
||||||
|
if (master->mnt_slave.next == &m->mnt_slave)
|
||||||
|
break;
|
||||||
|
m = master;
|
||||||
|
}
|
||||||
|
if (m == origin)
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
/* slave of the earlier, then */
|
}
|
||||||
*type = CL_SLAVE;
|
|
||||||
/* beginning of peer group among the slaves? */
|
/* all accesses are serialized by namespace_sem */
|
||||||
if (IS_MNT_SHARED(dest))
|
static struct user_namespace *user_ns;
|
||||||
*type |= CL_MAKE_SHARED;
|
static struct mount *last_dest, *last_source, *dest_master;
|
||||||
return last_src;
|
static struct mountpoint *mp;
|
||||||
|
static struct hlist_head *list;
|
||||||
|
|
||||||
|
static int propagate_one(struct mount *m)
|
||||||
|
{
|
||||||
|
struct mount *child;
|
||||||
|
int type;
|
||||||
|
/* skip ones added by this propagate_mnt() */
|
||||||
|
if (IS_MNT_NEW(m))
|
||||||
|
return 0;
|
||||||
|
/* skip if mountpoint isn't covered by it */
|
||||||
|
if (!is_subdir(mp->m_dentry, m->mnt.mnt_root))
|
||||||
|
return 0;
|
||||||
|
if (m->mnt_group_id == last_dest->mnt_group_id) {
|
||||||
|
type = CL_MAKE_SHARED;
|
||||||
|
} else {
|
||||||
|
struct mount *n, *p;
|
||||||
|
for (n = m; ; n = p) {
|
||||||
|
p = n->mnt_master;
|
||||||
|
if (p == dest_master || IS_MNT_MARKED(p)) {
|
||||||
|
while (last_dest->mnt_master != p) {
|
||||||
|
last_source = last_source->mnt_master;
|
||||||
|
last_dest = last_source->mnt_parent;
|
||||||
|
}
|
||||||
|
if (n->mnt_group_id != last_dest->mnt_group_id) {
|
||||||
|
last_source = last_source->mnt_master;
|
||||||
|
last_dest = last_source->mnt_parent;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type = CL_SLAVE;
|
||||||
|
/* beginning of peer group among the slaves? */
|
||||||
|
if (IS_MNT_SHARED(m))
|
||||||
|
type |= CL_MAKE_SHARED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Notice when we are propagating across user namespaces */
|
||||||
|
if (m->mnt_ns->user_ns != user_ns)
|
||||||
|
type |= CL_UNPRIVILEGED;
|
||||||
|
child = copy_tree(last_source, last_source->mnt.mnt_root, type);
|
||||||
|
if (IS_ERR(child))
|
||||||
|
return PTR_ERR(child);
|
||||||
|
mnt_set_mountpoint(m, mp, child);
|
||||||
|
last_dest = m;
|
||||||
|
last_source = child;
|
||||||
|
if (m->mnt_master != dest_master) {
|
||||||
|
read_seqlock_excl(&mount_lock);
|
||||||
|
SET_MNT_MARK(m->mnt_master);
|
||||||
|
read_sequnlock_excl(&mount_lock);
|
||||||
|
}
|
||||||
|
hlist_add_head(&child->mnt_hash, list);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -222,56 +270,48 @@ static struct mount *get_source(struct mount *dest,
|
|||||||
int propagate_mnt(struct mount *dest_mnt, struct mountpoint *dest_mp,
|
int propagate_mnt(struct mount *dest_mnt, struct mountpoint *dest_mp,
|
||||||
struct mount *source_mnt, struct hlist_head *tree_list)
|
struct mount *source_mnt, struct hlist_head *tree_list)
|
||||||
{
|
{
|
||||||
struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns;
|
struct mount *m, *n;
|
||||||
struct mount *m, *child;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct mount *prev_dest_mnt = dest_mnt;
|
|
||||||
struct mount *prev_src_mnt = source_mnt;
|
|
||||||
HLIST_HEAD(tmp_list);
|
|
||||||
|
|
||||||
for (m = propagation_next(dest_mnt, dest_mnt); m;
|
/*
|
||||||
m = propagation_next(m, dest_mnt)) {
|
* we don't want to bother passing tons of arguments to
|
||||||
int type;
|
* propagate_one(); everything is serialized by namespace_sem,
|
||||||
struct mount *source;
|
* so globals will do just fine.
|
||||||
|
*/
|
||||||
|
user_ns = current->nsproxy->mnt_ns->user_ns;
|
||||||
|
last_dest = dest_mnt;
|
||||||
|
last_source = source_mnt;
|
||||||
|
mp = dest_mp;
|
||||||
|
list = tree_list;
|
||||||
|
dest_master = dest_mnt->mnt_master;
|
||||||
|
|
||||||
if (IS_MNT_NEW(m))
|
/* all peers of dest_mnt, except dest_mnt itself */
|
||||||
continue;
|
for (n = next_peer(dest_mnt); n != dest_mnt; n = next_peer(n)) {
|
||||||
|
ret = propagate_one(n);
|
||||||
source = get_source(m, prev_dest_mnt, prev_src_mnt, &type);
|
if (ret)
|
||||||
|
|
||||||
/* Notice when we are propagating across user namespaces */
|
|
||||||
if (m->mnt_ns->user_ns != user_ns)
|
|
||||||
type |= CL_UNPRIVILEGED;
|
|
||||||
|
|
||||||
child = copy_tree(source, source->mnt.mnt_root, type);
|
|
||||||
if (IS_ERR(child)) {
|
|
||||||
ret = PTR_ERR(child);
|
|
||||||
tmp_list = *tree_list;
|
|
||||||
tmp_list.first->pprev = &tmp_list.first;
|
|
||||||
INIT_HLIST_HEAD(tree_list);
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_subdir(dest_mp->m_dentry, m->mnt.mnt_root)) {
|
/* all slave groups */
|
||||||
mnt_set_mountpoint(m, dest_mp, child);
|
for (m = next_group(dest_mnt, dest_mnt); m;
|
||||||
hlist_add_head(&child->mnt_hash, tree_list);
|
m = next_group(m, dest_mnt)) {
|
||||||
} else {
|
/* everything in that slave group */
|
||||||
/*
|
n = m;
|
||||||
* This can happen if the parent mount was bind mounted
|
do {
|
||||||
* on some subdirectory of a shared/slave mount.
|
ret = propagate_one(n);
|
||||||
*/
|
if (ret)
|
||||||
hlist_add_head(&child->mnt_hash, &tmp_list);
|
goto out;
|
||||||
}
|
n = next_peer(n);
|
||||||
prev_dest_mnt = m;
|
} while (n != m);
|
||||||
prev_src_mnt = child;
|
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
lock_mount_hash();
|
read_seqlock_excl(&mount_lock);
|
||||||
while (!hlist_empty(&tmp_list)) {
|
hlist_for_each_entry(n, tree_list, mnt_hash) {
|
||||||
child = hlist_entry(tmp_list.first, struct mount, mnt_hash);
|
m = n->mnt_parent;
|
||||||
umount_tree(child, 0);
|
if (m->mnt_master != dest_mnt->mnt_master)
|
||||||
|
CLEAR_MNT_MARK(m->mnt_master);
|
||||||
}
|
}
|
||||||
unlock_mount_hash();
|
read_sequnlock_excl(&mount_lock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,9 @@
|
|||||||
#define IS_MNT_NEW(m) (!(m)->mnt_ns)
|
#define IS_MNT_NEW(m) (!(m)->mnt_ns)
|
||||||
#define CLEAR_MNT_SHARED(m) ((m)->mnt.mnt_flags &= ~MNT_SHARED)
|
#define CLEAR_MNT_SHARED(m) ((m)->mnt.mnt_flags &= ~MNT_SHARED)
|
||||||
#define IS_MNT_UNBINDABLE(m) ((m)->mnt.mnt_flags & MNT_UNBINDABLE)
|
#define IS_MNT_UNBINDABLE(m) ((m)->mnt.mnt_flags & MNT_UNBINDABLE)
|
||||||
|
#define IS_MNT_MARKED(m) ((m)->mnt.mnt_flags & MNT_MARKED)
|
||||||
|
#define SET_MNT_MARK(m) ((m)->mnt.mnt_flags |= MNT_MARKED)
|
||||||
|
#define CLEAR_MNT_MARK(m) ((m)->mnt.mnt_flags &= ~MNT_MARKED)
|
||||||
|
|
||||||
#define CL_EXPIRE 0x01
|
#define CL_EXPIRE 0x01
|
||||||
#define CL_SLAVE 0x02
|
#define CL_SLAVE 0x02
|
||||||
|
@ -146,7 +146,7 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl
|
|||||||
struct task_struct *task;
|
struct task_struct *task;
|
||||||
void *ns;
|
void *ns;
|
||||||
char name[50];
|
char name[50];
|
||||||
int len = -EACCES;
|
int res = -EACCES;
|
||||||
|
|
||||||
task = get_proc_task(inode);
|
task = get_proc_task(inode);
|
||||||
if (!task)
|
if (!task)
|
||||||
@ -155,24 +155,18 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl
|
|||||||
if (!ptrace_may_access(task, PTRACE_MODE_READ))
|
if (!ptrace_may_access(task, PTRACE_MODE_READ))
|
||||||
goto out_put_task;
|
goto out_put_task;
|
||||||
|
|
||||||
len = -ENOENT;
|
res = -ENOENT;
|
||||||
ns = ns_ops->get(task);
|
ns = ns_ops->get(task);
|
||||||
if (!ns)
|
if (!ns)
|
||||||
goto out_put_task;
|
goto out_put_task;
|
||||||
|
|
||||||
snprintf(name, sizeof(name), "%s:[%u]", ns_ops->name, ns_ops->inum(ns));
|
snprintf(name, sizeof(name), "%s:[%u]", ns_ops->name, ns_ops->inum(ns));
|
||||||
len = strlen(name);
|
res = readlink_copy(buffer, buflen, name);
|
||||||
|
|
||||||
if (len > buflen)
|
|
||||||
len = buflen;
|
|
||||||
if (copy_to_user(buffer, name, len))
|
|
||||||
len = -EFAULT;
|
|
||||||
|
|
||||||
ns_ops->put(ns);
|
ns_ops->put(ns);
|
||||||
out_put_task:
|
out_put_task:
|
||||||
put_task_struct(task);
|
put_task_struct(task);
|
||||||
out:
|
out:
|
||||||
return len;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct inode_operations proc_ns_link_inode_operations = {
|
static const struct inode_operations proc_ns_link_inode_operations = {
|
||||||
|
@ -16,7 +16,7 @@ static int proc_self_readlink(struct dentry *dentry, char __user *buffer,
|
|||||||
if (!tgid)
|
if (!tgid)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
sprintf(tmp, "%d", tgid);
|
sprintf(tmp, "%d", tgid);
|
||||||
return vfs_readlink(dentry,buffer,buflen,tmp);
|
return readlink_copy(buffer, buflen, tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *proc_self_follow_link(struct dentry *dentry, struct nameidata *nd)
|
static void *proc_self_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||||
|
@ -267,6 +267,7 @@ static int mounts_open_common(struct inode *inode, struct file *file,
|
|||||||
p->root = root;
|
p->root = root;
|
||||||
p->m.poll_event = ns->event;
|
p->m.poll_event = ns->event;
|
||||||
p->show = show;
|
p->show = show;
|
||||||
|
p->cached_event = ~0ULL;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
130
fs/splice.c
130
fs/splice.c
@ -136,8 +136,6 @@ static int page_cache_pipe_buf_confirm(struct pipe_inode_info *pipe,
|
|||||||
|
|
||||||
const struct pipe_buf_operations page_cache_pipe_buf_ops = {
|
const struct pipe_buf_operations page_cache_pipe_buf_ops = {
|
||||||
.can_merge = 0,
|
.can_merge = 0,
|
||||||
.map = generic_pipe_buf_map,
|
|
||||||
.unmap = generic_pipe_buf_unmap,
|
|
||||||
.confirm = page_cache_pipe_buf_confirm,
|
.confirm = page_cache_pipe_buf_confirm,
|
||||||
.release = page_cache_pipe_buf_release,
|
.release = page_cache_pipe_buf_release,
|
||||||
.steal = page_cache_pipe_buf_steal,
|
.steal = page_cache_pipe_buf_steal,
|
||||||
@ -156,8 +154,6 @@ static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe,
|
|||||||
|
|
||||||
static const struct pipe_buf_operations user_page_pipe_buf_ops = {
|
static const struct pipe_buf_operations user_page_pipe_buf_ops = {
|
||||||
.can_merge = 0,
|
.can_merge = 0,
|
||||||
.map = generic_pipe_buf_map,
|
|
||||||
.unmap = generic_pipe_buf_unmap,
|
|
||||||
.confirm = generic_pipe_buf_confirm,
|
.confirm = generic_pipe_buf_confirm,
|
||||||
.release = page_cache_pipe_buf_release,
|
.release = page_cache_pipe_buf_release,
|
||||||
.steal = user_page_pipe_buf_steal,
|
.steal = user_page_pipe_buf_steal,
|
||||||
@ -547,8 +543,6 @@ EXPORT_SYMBOL(generic_file_splice_read);
|
|||||||
|
|
||||||
static const struct pipe_buf_operations default_pipe_buf_ops = {
|
static const struct pipe_buf_operations default_pipe_buf_ops = {
|
||||||
.can_merge = 0,
|
.can_merge = 0,
|
||||||
.map = generic_pipe_buf_map,
|
|
||||||
.unmap = generic_pipe_buf_unmap,
|
|
||||||
.confirm = generic_pipe_buf_confirm,
|
.confirm = generic_pipe_buf_confirm,
|
||||||
.release = generic_pipe_buf_release,
|
.release = generic_pipe_buf_release,
|
||||||
.steal = generic_pipe_buf_steal,
|
.steal = generic_pipe_buf_steal,
|
||||||
@ -564,8 +558,6 @@ static int generic_pipe_buf_nosteal(struct pipe_inode_info *pipe,
|
|||||||
/* Pipe buffer operations for a socket and similar. */
|
/* Pipe buffer operations for a socket and similar. */
|
||||||
const struct pipe_buf_operations nosteal_pipe_buf_ops = {
|
const struct pipe_buf_operations nosteal_pipe_buf_ops = {
|
||||||
.can_merge = 0,
|
.can_merge = 0,
|
||||||
.map = generic_pipe_buf_map,
|
|
||||||
.unmap = generic_pipe_buf_unmap,
|
|
||||||
.confirm = generic_pipe_buf_confirm,
|
.confirm = generic_pipe_buf_confirm,
|
||||||
.release = generic_pipe_buf_release,
|
.release = generic_pipe_buf_release,
|
||||||
.steal = generic_pipe_buf_nosteal,
|
.steal = generic_pipe_buf_nosteal,
|
||||||
@ -767,13 +759,13 @@ int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (buf->page != page) {
|
if (buf->page != page) {
|
||||||
char *src = buf->ops->map(pipe, buf, 1);
|
char *src = kmap_atomic(buf->page);
|
||||||
char *dst = kmap_atomic(page);
|
char *dst = kmap_atomic(page);
|
||||||
|
|
||||||
memcpy(dst + offset, src + buf->offset, this_len);
|
memcpy(dst + offset, src + buf->offset, this_len);
|
||||||
flush_dcache_page(page);
|
flush_dcache_page(page);
|
||||||
kunmap_atomic(dst);
|
kunmap_atomic(dst);
|
||||||
buf->ops->unmap(pipe, buf, src);
|
kunmap_atomic(src);
|
||||||
}
|
}
|
||||||
ret = pagecache_write_end(file, mapping, sd->pos, this_len, this_len,
|
ret = pagecache_write_end(file, mapping, sd->pos, this_len, this_len,
|
||||||
page, fsdata);
|
page, fsdata);
|
||||||
@ -1067,9 +1059,9 @@ static int write_pipe_buf(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
|
|||||||
void *data;
|
void *data;
|
||||||
loff_t tmp = sd->pos;
|
loff_t tmp = sd->pos;
|
||||||
|
|
||||||
data = buf->ops->map(pipe, buf, 0);
|
data = kmap(buf->page);
|
||||||
ret = __kernel_write(sd->u.file, data + buf->offset, sd->len, &tmp);
|
ret = __kernel_write(sd->u.file, data + buf->offset, sd->len, &tmp);
|
||||||
buf->ops->unmap(pipe, buf, data);
|
kunmap(buf->page);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -1528,116 +1520,48 @@ static int get_iovec_page_array(const struct iovec __user *iov,
|
|||||||
static int pipe_to_user(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
|
static int pipe_to_user(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
|
||||||
struct splice_desc *sd)
|
struct splice_desc *sd)
|
||||||
{
|
{
|
||||||
char *src;
|
int n = copy_page_to_iter(buf->page, buf->offset, sd->len, sd->u.data);
|
||||||
int ret;
|
return n == sd->len ? n : -EFAULT;
|
||||||
|
|
||||||
/*
|
|
||||||
* See if we can use the atomic maps, by prefaulting in the
|
|
||||||
* pages and doing an atomic copy
|
|
||||||
*/
|
|
||||||
if (!fault_in_pages_writeable(sd->u.userptr, sd->len)) {
|
|
||||||
src = buf->ops->map(pipe, buf, 1);
|
|
||||||
ret = __copy_to_user_inatomic(sd->u.userptr, src + buf->offset,
|
|
||||||
sd->len);
|
|
||||||
buf->ops->unmap(pipe, buf, src);
|
|
||||||
if (!ret) {
|
|
||||||
ret = sd->len;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* No dice, use slow non-atomic map and copy
|
|
||||||
*/
|
|
||||||
src = buf->ops->map(pipe, buf, 0);
|
|
||||||
|
|
||||||
ret = sd->len;
|
|
||||||
if (copy_to_user(sd->u.userptr, src + buf->offset, sd->len))
|
|
||||||
ret = -EFAULT;
|
|
||||||
|
|
||||||
buf->ops->unmap(pipe, buf, src);
|
|
||||||
out:
|
|
||||||
if (ret > 0)
|
|
||||||
sd->u.userptr += ret;
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For lack of a better implementation, implement vmsplice() to userspace
|
* For lack of a better implementation, implement vmsplice() to userspace
|
||||||
* as a simple copy of the pipes pages to the user iov.
|
* as a simple copy of the pipes pages to the user iov.
|
||||||
*/
|
*/
|
||||||
static long vmsplice_to_user(struct file *file, const struct iovec __user *iov,
|
static long vmsplice_to_user(struct file *file, const struct iovec __user *uiov,
|
||||||
unsigned long nr_segs, unsigned int flags)
|
unsigned long nr_segs, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct pipe_inode_info *pipe;
|
struct pipe_inode_info *pipe;
|
||||||
struct splice_desc sd;
|
struct splice_desc sd;
|
||||||
ssize_t size;
|
|
||||||
int error;
|
|
||||||
long ret;
|
long ret;
|
||||||
|
struct iovec iovstack[UIO_FASTIOV];
|
||||||
|
struct iovec *iov = iovstack;
|
||||||
|
struct iov_iter iter;
|
||||||
|
ssize_t count = 0;
|
||||||
|
|
||||||
pipe = get_pipe_info(file);
|
pipe = get_pipe_info(file);
|
||||||
if (!pipe)
|
if (!pipe)
|
||||||
return -EBADF;
|
return -EBADF;
|
||||||
|
|
||||||
|
ret = rw_copy_check_uvector(READ, uiov, nr_segs,
|
||||||
|
ARRAY_SIZE(iovstack), iovstack, &iov);
|
||||||
|
if (ret <= 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
iov_iter_init(&iter, iov, nr_segs, count, 0);
|
||||||
|
|
||||||
|
sd.len = 0;
|
||||||
|
sd.total_len = count;
|
||||||
|
sd.flags = flags;
|
||||||
|
sd.u.data = &iter;
|
||||||
|
sd.pos = 0;
|
||||||
|
|
||||||
pipe_lock(pipe);
|
pipe_lock(pipe);
|
||||||
|
ret = __splice_from_pipe(pipe, &sd, pipe_to_user);
|
||||||
error = ret = 0;
|
|
||||||
while (nr_segs) {
|
|
||||||
void __user *base;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get user address base and length for this iovec.
|
|
||||||
*/
|
|
||||||
error = get_user(base, &iov->iov_base);
|
|
||||||
if (unlikely(error))
|
|
||||||
break;
|
|
||||||
error = get_user(len, &iov->iov_len);
|
|
||||||
if (unlikely(error))
|
|
||||||
break;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sanity check this iovec. 0 read succeeds.
|
|
||||||
*/
|
|
||||||
if (unlikely(!len))
|
|
||||||
break;
|
|
||||||
if (unlikely(!base)) {
|
|
||||||
error = -EFAULT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unlikely(!access_ok(VERIFY_WRITE, base, len))) {
|
|
||||||
error = -EFAULT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sd.len = 0;
|
|
||||||
sd.total_len = len;
|
|
||||||
sd.flags = flags;
|
|
||||||
sd.u.userptr = base;
|
|
||||||
sd.pos = 0;
|
|
||||||
|
|
||||||
size = __splice_from_pipe(pipe, &sd, pipe_to_user);
|
|
||||||
if (size < 0) {
|
|
||||||
if (!ret)
|
|
||||||
ret = size;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret += size;
|
|
||||||
|
|
||||||
if (size < len)
|
|
||||||
break;
|
|
||||||
|
|
||||||
nr_segs--;
|
|
||||||
iov++;
|
|
||||||
}
|
|
||||||
|
|
||||||
pipe_unlock(pipe);
|
pipe_unlock(pipe);
|
||||||
|
|
||||||
if (!ret)
|
if (iov != iovstack)
|
||||||
ret = error;
|
kfree(iov);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -171,7 +171,7 @@ static ssize_t udf_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
|||||||
} else
|
} else
|
||||||
up_write(&iinfo->i_data_sem);
|
up_write(&iinfo->i_data_sem);
|
||||||
|
|
||||||
retval = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
|
retval = __generic_file_aio_write(iocb, iov, nr_segs);
|
||||||
mutex_unlock(&inode->i_mutex);
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
|
||||||
if (retval > 0) {
|
if (retval > 0) {
|
||||||
|
@ -699,7 +699,7 @@ xfs_file_dio_aio_write(
|
|||||||
|
|
||||||
trace_xfs_file_direct_write(ip, count, iocb->ki_pos, 0);
|
trace_xfs_file_direct_write(ip, count, iocb->ki_pos, 0);
|
||||||
ret = generic_file_direct_write(iocb, iovp,
|
ret = generic_file_direct_write(iocb, iovp,
|
||||||
&nr_segs, pos, &iocb->ki_pos, count, ocount);
|
&nr_segs, pos, count, ocount);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
xfs_rw_iunlock(ip, iolock);
|
xfs_rw_iunlock(ip, iolock);
|
||||||
@ -715,7 +715,7 @@ xfs_file_buffered_aio_write(
|
|||||||
const struct iovec *iovp,
|
const struct iovec *iovp,
|
||||||
unsigned long nr_segs,
|
unsigned long nr_segs,
|
||||||
loff_t pos,
|
loff_t pos,
|
||||||
size_t ocount)
|
size_t count)
|
||||||
{
|
{
|
||||||
struct file *file = iocb->ki_filp;
|
struct file *file = iocb->ki_filp;
|
||||||
struct address_space *mapping = file->f_mapping;
|
struct address_space *mapping = file->f_mapping;
|
||||||
@ -724,7 +724,7 @@ xfs_file_buffered_aio_write(
|
|||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
int enospc = 0;
|
int enospc = 0;
|
||||||
int iolock = XFS_IOLOCK_EXCL;
|
int iolock = XFS_IOLOCK_EXCL;
|
||||||
size_t count = ocount;
|
struct iov_iter from;
|
||||||
|
|
||||||
xfs_rw_ilock(ip, iolock);
|
xfs_rw_ilock(ip, iolock);
|
||||||
|
|
||||||
@ -732,14 +732,15 @@ xfs_file_buffered_aio_write(
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
iov_iter_init(&from, iovp, nr_segs, count, 0);
|
||||||
/* We can write back this queue in page reclaim */
|
/* We can write back this queue in page reclaim */
|
||||||
current->backing_dev_info = mapping->backing_dev_info;
|
current->backing_dev_info = mapping->backing_dev_info;
|
||||||
|
|
||||||
write_retry:
|
write_retry:
|
||||||
trace_xfs_file_buffered_write(ip, count, iocb->ki_pos, 0);
|
trace_xfs_file_buffered_write(ip, count, iocb->ki_pos, 0);
|
||||||
ret = generic_file_buffered_write(iocb, iovp, nr_segs,
|
ret = generic_perform_write(file, &from, pos);
|
||||||
pos, &iocb->ki_pos, count, 0);
|
if (likely(ret >= 0))
|
||||||
|
iocb->ki_pos = pos + ret;
|
||||||
/*
|
/*
|
||||||
* If we just got an ENOSPC, try to write back all dirty inodes to
|
* If we just got an ENOSPC, try to write back all dirty inodes to
|
||||||
* convert delalloc space to free up some of the excess reserved
|
* convert delalloc space to free up some of the excess reserved
|
||||||
|
@ -271,32 +271,6 @@ xfs_open_by_handle(
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* This is a copy from fs/namei.c:vfs_readlink(), except for removing it's
|
|
||||||
* unused first argument.
|
|
||||||
*/
|
|
||||||
STATIC int
|
|
||||||
do_readlink(
|
|
||||||
char __user *buffer,
|
|
||||||
int buflen,
|
|
||||||
const char *link)
|
|
||||||
{
|
|
||||||
int len;
|
|
||||||
|
|
||||||
len = PTR_ERR(link);
|
|
||||||
if (IS_ERR(link))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
len = strlen(link);
|
|
||||||
if (len > (unsigned) buflen)
|
|
||||||
len = buflen;
|
|
||||||
if (copy_to_user(buffer, link, len))
|
|
||||||
len = -EFAULT;
|
|
||||||
out:
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
xfs_readlink_by_handle(
|
xfs_readlink_by_handle(
|
||||||
struct file *parfilp,
|
struct file *parfilp,
|
||||||
@ -334,7 +308,7 @@ xfs_readlink_by_handle(
|
|||||||
error = -xfs_readlink(XFS_I(dentry->d_inode), link);
|
error = -xfs_readlink(XFS_I(dentry->d_inode), link);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_kfree;
|
goto out_kfree;
|
||||||
error = do_readlink(hreq->ohandle, olen, link);
|
error = readlink_copy(hreq->ohandle, olen, link);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_kfree;
|
goto out_kfree;
|
||||||
|
|
||||||
|
@ -388,7 +388,7 @@ struct sg_iovec;
|
|||||||
struct rq_map_data;
|
struct rq_map_data;
|
||||||
extern struct bio *bio_map_user_iov(struct request_queue *,
|
extern struct bio *bio_map_user_iov(struct request_queue *,
|
||||||
struct block_device *,
|
struct block_device *,
|
||||||
struct sg_iovec *, int, int, gfp_t);
|
const struct sg_iovec *, int, int, gfp_t);
|
||||||
extern void bio_unmap_user(struct bio *);
|
extern void bio_unmap_user(struct bio *);
|
||||||
extern struct bio *bio_map_kern(struct request_queue *, void *, unsigned int,
|
extern struct bio *bio_map_kern(struct request_queue *, void *, unsigned int,
|
||||||
gfp_t);
|
gfp_t);
|
||||||
@ -414,7 +414,8 @@ extern int bio_alloc_pages(struct bio *bio, gfp_t gfp);
|
|||||||
extern struct bio *bio_copy_user(struct request_queue *, struct rq_map_data *,
|
extern struct bio *bio_copy_user(struct request_queue *, struct rq_map_data *,
|
||||||
unsigned long, unsigned int, int, gfp_t);
|
unsigned long, unsigned int, int, gfp_t);
|
||||||
extern struct bio *bio_copy_user_iov(struct request_queue *,
|
extern struct bio *bio_copy_user_iov(struct request_queue *,
|
||||||
struct rq_map_data *, struct sg_iovec *,
|
struct rq_map_data *,
|
||||||
|
const struct sg_iovec *,
|
||||||
int, int, gfp_t);
|
int, int, gfp_t);
|
||||||
extern int bio_uncopy_user(struct bio *);
|
extern int bio_uncopy_user(struct bio *);
|
||||||
void zero_fill_bio(struct bio *bio);
|
void zero_fill_bio(struct bio *bio);
|
||||||
|
@ -835,8 +835,8 @@ extern int blk_rq_map_user(struct request_queue *, struct request *,
|
|||||||
extern int blk_rq_unmap_user(struct bio *);
|
extern int blk_rq_unmap_user(struct bio *);
|
||||||
extern int blk_rq_map_kern(struct request_queue *, struct request *, void *, unsigned int, gfp_t);
|
extern int blk_rq_map_kern(struct request_queue *, struct request *, void *, unsigned int, gfp_t);
|
||||||
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
|
extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
|
||||||
struct rq_map_data *, struct sg_iovec *, int,
|
struct rq_map_data *, const struct sg_iovec *,
|
||||||
unsigned int, gfp_t);
|
int, unsigned int, gfp_t);
|
||||||
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
|
extern int blk_execute_rq(struct request_queue *, struct gendisk *,
|
||||||
struct request *, int);
|
struct request *, int);
|
||||||
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
|
extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
|
||||||
|
@ -210,8 +210,8 @@ int block_write_full_page(struct page *page, get_block_t *get_block,
|
|||||||
int block_write_full_page_endio(struct page *page, get_block_t *get_block,
|
int block_write_full_page_endio(struct page *page, get_block_t *get_block,
|
||||||
struct writeback_control *wbc, bh_end_io_t *handler);
|
struct writeback_control *wbc, bh_end_io_t *handler);
|
||||||
int block_read_full_page(struct page*, get_block_t*);
|
int block_read_full_page(struct page*, get_block_t*);
|
||||||
int block_is_partially_uptodate(struct page *page, read_descriptor_t *desc,
|
int block_is_partially_uptodate(struct page *page, unsigned long from,
|
||||||
unsigned long from);
|
unsigned long count);
|
||||||
int block_write_begin(struct address_space *mapping, loff_t pos, unsigned len,
|
int block_write_begin(struct address_space *mapping, loff_t pos, unsigned len,
|
||||||
unsigned flags, struct page **pagep, get_block_t *get_block);
|
unsigned flags, struct page **pagep, get_block_t *get_block);
|
||||||
int __block_write_begin(struct page *page, loff_t pos, unsigned len,
|
int __block_write_begin(struct page *page, loff_t pos, unsigned len,
|
||||||
|
@ -63,8 +63,6 @@ struct file_operations;
|
|||||||
struct vfsmount;
|
struct vfsmount;
|
||||||
struct dentry;
|
struct dentry;
|
||||||
|
|
||||||
extern void __init files_defer_init(void);
|
|
||||||
|
|
||||||
#define rcu_dereference_check_fdtable(files, fdtfd) \
|
#define rcu_dereference_check_fdtable(files, fdtfd) \
|
||||||
rcu_dereference_check((fdtfd), lockdep_is_held(&(files)->file_lock))
|
rcu_dereference_check((fdtfd), lockdep_is_held(&(files)->file_lock))
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ struct cred;
|
|||||||
struct swap_info_struct;
|
struct swap_info_struct;
|
||||||
struct seq_file;
|
struct seq_file;
|
||||||
struct workqueue_struct;
|
struct workqueue_struct;
|
||||||
|
struct iov_iter;
|
||||||
|
|
||||||
extern void __init inode_init(void);
|
extern void __init inode_init(void);
|
||||||
extern void __init inode_init_early(void);
|
extern void __init inode_init_early(void);
|
||||||
@ -125,6 +126,8 @@ typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
|
|||||||
|
|
||||||
/* File needs atomic accesses to f_pos */
|
/* File needs atomic accesses to f_pos */
|
||||||
#define FMODE_ATOMIC_POS ((__force fmode_t)0x8000)
|
#define FMODE_ATOMIC_POS ((__force fmode_t)0x8000)
|
||||||
|
/* Write access to underlying fs */
|
||||||
|
#define FMODE_WRITER ((__force fmode_t)0x10000)
|
||||||
|
|
||||||
/* File was opened by fanotify and shouldn't generate fanotify events */
|
/* File was opened by fanotify and shouldn't generate fanotify events */
|
||||||
#define FMODE_NONOTIFY ((__force fmode_t)0x1000000)
|
#define FMODE_NONOTIFY ((__force fmode_t)0x1000000)
|
||||||
@ -293,38 +296,6 @@ struct page;
|
|||||||
struct address_space;
|
struct address_space;
|
||||||
struct writeback_control;
|
struct writeback_control;
|
||||||
|
|
||||||
struct iov_iter {
|
|
||||||
const struct iovec *iov;
|
|
||||||
unsigned long nr_segs;
|
|
||||||
size_t iov_offset;
|
|
||||||
size_t count;
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t iov_iter_copy_from_user_atomic(struct page *page,
|
|
||||||
struct iov_iter *i, unsigned long offset, size_t bytes);
|
|
||||||
size_t iov_iter_copy_from_user(struct page *page,
|
|
||||||
struct iov_iter *i, unsigned long offset, size_t bytes);
|
|
||||||
void iov_iter_advance(struct iov_iter *i, size_t bytes);
|
|
||||||
int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes);
|
|
||||||
size_t iov_iter_single_seg_count(const struct iov_iter *i);
|
|
||||||
|
|
||||||
static inline void iov_iter_init(struct iov_iter *i,
|
|
||||||
const struct iovec *iov, unsigned long nr_segs,
|
|
||||||
size_t count, size_t written)
|
|
||||||
{
|
|
||||||
i->iov = iov;
|
|
||||||
i->nr_segs = nr_segs;
|
|
||||||
i->iov_offset = 0;
|
|
||||||
i->count = count + written;
|
|
||||||
|
|
||||||
iov_iter_advance(i, written);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline size_t iov_iter_count(struct iov_iter *i)
|
|
||||||
{
|
|
||||||
return i->count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "descriptor" for what we're up to with a read.
|
* "descriptor" for what we're up to with a read.
|
||||||
* This allows us to use the same read code yet
|
* This allows us to use the same read code yet
|
||||||
@ -383,7 +354,7 @@ struct address_space_operations {
|
|||||||
int (*migratepage) (struct address_space *,
|
int (*migratepage) (struct address_space *,
|
||||||
struct page *, struct page *, enum migrate_mode);
|
struct page *, struct page *, enum migrate_mode);
|
||||||
int (*launder_page) (struct page *);
|
int (*launder_page) (struct page *);
|
||||||
int (*is_partially_uptodate) (struct page *, read_descriptor_t *,
|
int (*is_partially_uptodate) (struct page *, unsigned long,
|
||||||
unsigned long);
|
unsigned long);
|
||||||
void (*is_dirty_writeback) (struct page *, bool *, bool *);
|
void (*is_dirty_writeback) (struct page *, bool *, bool *);
|
||||||
int (*error_remove_page)(struct address_space *, struct page *);
|
int (*error_remove_page)(struct address_space *, struct page *);
|
||||||
@ -770,9 +741,6 @@ static inline int ra_has_index(struct file_ra_state *ra, pgoff_t index)
|
|||||||
index < ra->start + ra->size);
|
index < ra->start + ra->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define FILE_MNT_WRITE_TAKEN 1
|
|
||||||
#define FILE_MNT_WRITE_RELEASED 2
|
|
||||||
|
|
||||||
struct file {
|
struct file {
|
||||||
union {
|
union {
|
||||||
struct llist_node fu_llist;
|
struct llist_node fu_llist;
|
||||||
@ -810,9 +778,6 @@ struct file {
|
|||||||
struct list_head f_tfile_llink;
|
struct list_head f_tfile_llink;
|
||||||
#endif /* #ifdef CONFIG_EPOLL */
|
#endif /* #ifdef CONFIG_EPOLL */
|
||||||
struct address_space *f_mapping;
|
struct address_space *f_mapping;
|
||||||
#ifdef CONFIG_DEBUG_WRITECOUNT
|
|
||||||
unsigned long f_mnt_write_state;
|
|
||||||
#endif
|
|
||||||
} __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
|
} __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
|
||||||
|
|
||||||
struct file_handle {
|
struct file_handle {
|
||||||
@ -830,49 +795,6 @@ static inline struct file *get_file(struct file *f)
|
|||||||
#define fput_atomic(x) atomic_long_add_unless(&(x)->f_count, -1, 1)
|
#define fput_atomic(x) atomic_long_add_unless(&(x)->f_count, -1, 1)
|
||||||
#define file_count(x) atomic_long_read(&(x)->f_count)
|
#define file_count(x) atomic_long_read(&(x)->f_count)
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_WRITECOUNT
|
|
||||||
static inline void file_take_write(struct file *f)
|
|
||||||
{
|
|
||||||
WARN_ON(f->f_mnt_write_state != 0);
|
|
||||||
f->f_mnt_write_state = FILE_MNT_WRITE_TAKEN;
|
|
||||||
}
|
|
||||||
static inline void file_release_write(struct file *f)
|
|
||||||
{
|
|
||||||
f->f_mnt_write_state |= FILE_MNT_WRITE_RELEASED;
|
|
||||||
}
|
|
||||||
static inline void file_reset_write(struct file *f)
|
|
||||||
{
|
|
||||||
f->f_mnt_write_state = 0;
|
|
||||||
}
|
|
||||||
static inline void file_check_state(struct file *f)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* At this point, either both or neither of these bits
|
|
||||||
* should be set.
|
|
||||||
*/
|
|
||||||
WARN_ON(f->f_mnt_write_state == FILE_MNT_WRITE_TAKEN);
|
|
||||||
WARN_ON(f->f_mnt_write_state == FILE_MNT_WRITE_RELEASED);
|
|
||||||
}
|
|
||||||
static inline int file_check_writeable(struct file *f)
|
|
||||||
{
|
|
||||||
if (f->f_mnt_write_state == FILE_MNT_WRITE_TAKEN)
|
|
||||||
return 0;
|
|
||||||
printk(KERN_WARNING "writeable file with no "
|
|
||||||
"mnt_want_write()\n");
|
|
||||||
WARN_ON(1);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
#else /* !CONFIG_DEBUG_WRITECOUNT */
|
|
||||||
static inline void file_take_write(struct file *filp) {}
|
|
||||||
static inline void file_release_write(struct file *filp) {}
|
|
||||||
static inline void file_reset_write(struct file *filp) {}
|
|
||||||
static inline void file_check_state(struct file *filp) {}
|
|
||||||
static inline int file_check_writeable(struct file *filp)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_DEBUG_WRITECOUNT */
|
|
||||||
|
|
||||||
#define MAX_NON_LFS ((1UL<<31) - 1)
|
#define MAX_NON_LFS ((1UL<<31) - 1)
|
||||||
|
|
||||||
/* Page cache limit. The filesystems should put that into their s_maxbytes
|
/* Page cache limit. The filesystems should put that into their s_maxbytes
|
||||||
@ -2481,16 +2403,13 @@ extern int generic_file_mmap(struct file *, struct vm_area_struct *);
|
|||||||
extern int generic_file_readonly_mmap(struct file *, struct vm_area_struct *);
|
extern int generic_file_readonly_mmap(struct file *, struct vm_area_struct *);
|
||||||
extern int generic_file_remap_pages(struct vm_area_struct *, unsigned long addr,
|
extern int generic_file_remap_pages(struct vm_area_struct *, unsigned long addr,
|
||||||
unsigned long size, pgoff_t pgoff);
|
unsigned long size, pgoff_t pgoff);
|
||||||
extern int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size);
|
|
||||||
int generic_write_checks(struct file *file, loff_t *pos, size_t *count, int isblk);
|
int generic_write_checks(struct file *file, loff_t *pos, size_t *count, int isblk);
|
||||||
extern ssize_t generic_file_aio_read(struct kiocb *, const struct iovec *, unsigned long, loff_t);
|
extern ssize_t generic_file_aio_read(struct kiocb *, const struct iovec *, unsigned long, loff_t);
|
||||||
extern ssize_t __generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long,
|
extern ssize_t __generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long);
|
||||||
loff_t *);
|
|
||||||
extern ssize_t generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long, loff_t);
|
extern ssize_t generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long, loff_t);
|
||||||
extern ssize_t generic_file_direct_write(struct kiocb *, const struct iovec *,
|
extern ssize_t generic_file_direct_write(struct kiocb *, const struct iovec *,
|
||||||
unsigned long *, loff_t, loff_t *, size_t, size_t);
|
unsigned long *, loff_t, size_t, size_t);
|
||||||
extern ssize_t generic_file_buffered_write(struct kiocb *, const struct iovec *,
|
extern ssize_t generic_perform_write(struct file *, struct iov_iter *, loff_t);
|
||||||
unsigned long, loff_t, loff_t *, size_t, ssize_t);
|
|
||||||
extern ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos);
|
extern ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos);
|
||||||
extern ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos);
|
extern ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos);
|
||||||
extern int generic_segment_checks(const struct iovec *iov,
|
extern int generic_segment_checks(const struct iovec *iov,
|
||||||
@ -2582,7 +2501,7 @@ extern const struct file_operations generic_ro_fops;
|
|||||||
|
|
||||||
#define special_file(m) (S_ISCHR(m)||S_ISBLK(m)||S_ISFIFO(m)||S_ISSOCK(m))
|
#define special_file(m) (S_ISCHR(m)||S_ISBLK(m)||S_ISFIFO(m)||S_ISSOCK(m))
|
||||||
|
|
||||||
extern int vfs_readlink(struct dentry *, char __user *, int, const char *);
|
extern int readlink_copy(char __user *, int, const char *);
|
||||||
extern int page_readlink(struct dentry *, char __user *, int);
|
extern int page_readlink(struct dentry *, char __user *, int);
|
||||||
extern void *page_follow_link_light(struct dentry *, struct nameidata *);
|
extern void *page_follow_link_light(struct dentry *, struct nameidata *);
|
||||||
extern void page_put_link(struct dentry *, struct nameidata *, void *);
|
extern void page_put_link(struct dentry *, struct nameidata *, void *);
|
||||||
|
@ -44,6 +44,8 @@ struct mnt_namespace;
|
|||||||
#define MNT_SHARED_MASK (MNT_UNBINDABLE)
|
#define MNT_SHARED_MASK (MNT_UNBINDABLE)
|
||||||
#define MNT_PROPAGATION_MASK (MNT_SHARED | MNT_UNBINDABLE)
|
#define MNT_PROPAGATION_MASK (MNT_SHARED | MNT_UNBINDABLE)
|
||||||
|
|
||||||
|
#define MNT_INTERNAL_FLAGS (MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL | \
|
||||||
|
MNT_DOOMED | MNT_SYNC_UMOUNT | MNT_MARKED)
|
||||||
|
|
||||||
#define MNT_INTERNAL 0x4000
|
#define MNT_INTERNAL 0x4000
|
||||||
|
|
||||||
@ -51,6 +53,7 @@ struct mnt_namespace;
|
|||||||
#define MNT_LOCKED 0x800000
|
#define MNT_LOCKED 0x800000
|
||||||
#define MNT_DOOMED 0x1000000
|
#define MNT_DOOMED 0x1000000
|
||||||
#define MNT_SYNC_UMOUNT 0x2000000
|
#define MNT_SYNC_UMOUNT 0x2000000
|
||||||
|
#define MNT_MARKED 0x4000000
|
||||||
|
|
||||||
struct vfsmount {
|
struct vfsmount {
|
||||||
struct dentry *mnt_root; /* root of the mounted tree */
|
struct dentry *mnt_root; /* root of the mounted tree */
|
||||||
|
@ -24,8 +24,7 @@ struct request;
|
|||||||
struct nbd_device {
|
struct nbd_device {
|
||||||
int flags;
|
int flags;
|
||||||
int harderror; /* Code of hard error */
|
int harderror; /* Code of hard error */
|
||||||
struct socket * sock;
|
struct socket * sock; /* If == NULL, device is not ready, yet */
|
||||||
struct file * file; /* If == NULL, device is not ready, yet */
|
|
||||||
int magic;
|
int magic;
|
||||||
|
|
||||||
spinlock_t queue_lock;
|
spinlock_t queue_lock;
|
||||||
|
@ -82,23 +82,6 @@ struct pipe_buf_operations {
|
|||||||
*/
|
*/
|
||||||
int can_merge;
|
int can_merge;
|
||||||
|
|
||||||
/*
|
|
||||||
* ->map() returns a virtual address mapping of the pipe buffer.
|
|
||||||
* The last integer flag reflects whether this should be an atomic
|
|
||||||
* mapping or not. The atomic map is faster, however you can't take
|
|
||||||
* page faults before calling ->unmap() again. So if you need to eg
|
|
||||||
* access user data through copy_to/from_user(), then you must get
|
|
||||||
* a non-atomic map. ->map() uses the kmap_atomic slot for
|
|
||||||
* atomic maps, you have to be careful if mapping another page as
|
|
||||||
* source or destination for a copy.
|
|
||||||
*/
|
|
||||||
void * (*map)(struct pipe_inode_info *, struct pipe_buffer *, int);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Undoes ->map(), finishes the virtual mapping of the pipe buffer.
|
|
||||||
*/
|
|
||||||
void (*unmap)(struct pipe_inode_info *, struct pipe_buffer *, void *);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ->confirm() verifies that the data in the pipe buffer is there
|
* ->confirm() verifies that the data in the pipe buffer is there
|
||||||
* and that the contents are good. If the pages in the pipe belong
|
* and that the contents are good. If the pages in the pipe belong
|
||||||
@ -150,8 +133,6 @@ struct pipe_inode_info *alloc_pipe_info(void);
|
|||||||
void free_pipe_info(struct pipe_inode_info *);
|
void free_pipe_info(struct pipe_inode_info *);
|
||||||
|
|
||||||
/* Generic pipe buffer ops functions */
|
/* Generic pipe buffer ops functions */
|
||||||
void *generic_pipe_buf_map(struct pipe_inode_info *, struct pipe_buffer *, int);
|
|
||||||
void generic_pipe_buf_unmap(struct pipe_inode_info *, struct pipe_buffer *, void *);
|
|
||||||
void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *);
|
void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *);
|
||||||
int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *);
|
int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *);
|
||||||
int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *);
|
int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *);
|
||||||
|
@ -9,14 +9,23 @@
|
|||||||
#ifndef __LINUX_UIO_H
|
#ifndef __LINUX_UIO_H
|
||||||
#define __LINUX_UIO_H
|
#define __LINUX_UIO_H
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
#include <uapi/linux/uio.h>
|
#include <uapi/linux/uio.h>
|
||||||
|
|
||||||
|
struct page;
|
||||||
|
|
||||||
struct kvec {
|
struct kvec {
|
||||||
void *iov_base; /* and that should *never* hold a userland pointer */
|
void *iov_base; /* and that should *never* hold a userland pointer */
|
||||||
size_t iov_len;
|
size_t iov_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct iov_iter {
|
||||||
|
const struct iovec *iov;
|
||||||
|
unsigned long nr_segs;
|
||||||
|
size_t iov_offset;
|
||||||
|
size_t count;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Total number of bytes covered by an iovec.
|
* Total number of bytes covered by an iovec.
|
||||||
*
|
*
|
||||||
@ -34,8 +43,51 @@ static inline size_t iov_length(const struct iovec *iov, unsigned long nr_segs)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline struct iovec iov_iter_iovec(const struct iov_iter *iter)
|
||||||
|
{
|
||||||
|
return (struct iovec) {
|
||||||
|
.iov_base = iter->iov->iov_base + iter->iov_offset,
|
||||||
|
.iov_len = min(iter->count,
|
||||||
|
iter->iov->iov_len - iter->iov_offset),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#define iov_for_each(iov, iter, start) \
|
||||||
|
for (iter = (start); \
|
||||||
|
(iter).count && \
|
||||||
|
((iov = iov_iter_iovec(&(iter))), 1); \
|
||||||
|
iov_iter_advance(&(iter), (iov).iov_len))
|
||||||
|
|
||||||
unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to);
|
unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to);
|
||||||
|
|
||||||
|
size_t iov_iter_copy_from_user_atomic(struct page *page,
|
||||||
|
struct iov_iter *i, unsigned long offset, size_t bytes);
|
||||||
|
size_t iov_iter_copy_from_user(struct page *page,
|
||||||
|
struct iov_iter *i, unsigned long offset, size_t bytes);
|
||||||
|
void iov_iter_advance(struct iov_iter *i, size_t bytes);
|
||||||
|
int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes);
|
||||||
|
size_t iov_iter_single_seg_count(const struct iov_iter *i);
|
||||||
|
size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
|
||||||
|
struct iov_iter *i);
|
||||||
|
|
||||||
|
static inline void iov_iter_init(struct iov_iter *i,
|
||||||
|
const struct iovec *iov, unsigned long nr_segs,
|
||||||
|
size_t count, size_t written)
|
||||||
|
{
|
||||||
|
i->iov = iov;
|
||||||
|
i->nr_segs = nr_segs;
|
||||||
|
i->iov_offset = 0;
|
||||||
|
i->count = count + written;
|
||||||
|
|
||||||
|
iov_iter_advance(i, written);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t iov_iter_count(struct iov_iter *i)
|
||||||
|
{
|
||||||
|
return i->count;
|
||||||
|
}
|
||||||
|
|
||||||
int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len);
|
int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len);
|
||||||
int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len);
|
int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1195,8 +1195,6 @@ static void relay_pipe_buf_release(struct pipe_inode_info *pipe,
|
|||||||
|
|
||||||
static const struct pipe_buf_operations relay_pipe_buf_ops = {
|
static const struct pipe_buf_operations relay_pipe_buf_ops = {
|
||||||
.can_merge = 0,
|
.can_merge = 0,
|
||||||
.map = generic_pipe_buf_map,
|
|
||||||
.unmap = generic_pipe_buf_unmap,
|
|
||||||
.confirm = generic_pipe_buf_confirm,
|
.confirm = generic_pipe_buf_confirm,
|
||||||
.release = relay_pipe_buf_release,
|
.release = relay_pipe_buf_release,
|
||||||
.steal = generic_pipe_buf_steal,
|
.steal = generic_pipe_buf_steal,
|
||||||
@ -1253,7 +1251,7 @@ static ssize_t subbuf_splice_actor(struct file *in,
|
|||||||
subbuf_pages = rbuf->chan->alloc_size >> PAGE_SHIFT;
|
subbuf_pages = rbuf->chan->alloc_size >> PAGE_SHIFT;
|
||||||
pidx = (read_start / PAGE_SIZE) % subbuf_pages;
|
pidx = (read_start / PAGE_SIZE) % subbuf_pages;
|
||||||
poff = read_start & ~PAGE_MASK;
|
poff = read_start & ~PAGE_MASK;
|
||||||
nr_pages = min_t(unsigned int, subbuf_pages, pipe->buffers);
|
nr_pages = min_t(unsigned int, subbuf_pages, spd.nr_pages_max);
|
||||||
|
|
||||||
for (total_len = 0; spd.nr_pages < nr_pages; spd.nr_pages++) {
|
for (total_len = 0; spd.nr_pages < nr_pages; spd.nr_pages++) {
|
||||||
unsigned int this_len, this_end, private;
|
unsigned int this_len, this_end, private;
|
||||||
|
@ -4392,8 +4392,6 @@ static void tracing_spd_release_pipe(struct splice_pipe_desc *spd,
|
|||||||
|
|
||||||
static const struct pipe_buf_operations tracing_pipe_buf_ops = {
|
static const struct pipe_buf_operations tracing_pipe_buf_ops = {
|
||||||
.can_merge = 0,
|
.can_merge = 0,
|
||||||
.map = generic_pipe_buf_map,
|
|
||||||
.unmap = generic_pipe_buf_unmap,
|
|
||||||
.confirm = generic_pipe_buf_confirm,
|
.confirm = generic_pipe_buf_confirm,
|
||||||
.release = generic_pipe_buf_release,
|
.release = generic_pipe_buf_release,
|
||||||
.steal = generic_pipe_buf_steal,
|
.steal = generic_pipe_buf_steal,
|
||||||
@ -4488,7 +4486,7 @@ static ssize_t tracing_splice_read_pipe(struct file *filp,
|
|||||||
trace_access_lock(iter->cpu_file);
|
trace_access_lock(iter->cpu_file);
|
||||||
|
|
||||||
/* Fill as many pages as possible. */
|
/* Fill as many pages as possible. */
|
||||||
for (i = 0, rem = len; i < pipe->buffers && rem; i++) {
|
for (i = 0, rem = len; i < spd.nr_pages_max && rem; i++) {
|
||||||
spd.pages[i] = alloc_page(GFP_KERNEL);
|
spd.pages[i] = alloc_page(GFP_KERNEL);
|
||||||
if (!spd.pages[i])
|
if (!spd.pages[i])
|
||||||
break;
|
break;
|
||||||
@ -5281,8 +5279,6 @@ static void buffer_pipe_buf_get(struct pipe_inode_info *pipe,
|
|||||||
/* Pipe buffer operations for a buffer. */
|
/* Pipe buffer operations for a buffer. */
|
||||||
static const struct pipe_buf_operations buffer_pipe_buf_ops = {
|
static const struct pipe_buf_operations buffer_pipe_buf_ops = {
|
||||||
.can_merge = 0,
|
.can_merge = 0,
|
||||||
.map = generic_pipe_buf_map,
|
|
||||||
.unmap = generic_pipe_buf_unmap,
|
|
||||||
.confirm = generic_pipe_buf_confirm,
|
.confirm = generic_pipe_buf_confirm,
|
||||||
.release = buffer_pipe_buf_release,
|
.release = buffer_pipe_buf_release,
|
||||||
.steal = generic_pipe_buf_steal,
|
.steal = generic_pipe_buf_steal,
|
||||||
@ -5358,7 +5354,7 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos,
|
|||||||
trace_access_lock(iter->cpu_file);
|
trace_access_lock(iter->cpu_file);
|
||||||
entries = ring_buffer_entries_cpu(iter->trace_buffer->buffer, iter->cpu_file);
|
entries = ring_buffer_entries_cpu(iter->trace_buffer->buffer, iter->cpu_file);
|
||||||
|
|
||||||
for (i = 0; i < pipe->buffers && len && entries; i++, len -= PAGE_SIZE) {
|
for (i = 0; i < spd.nr_pages_max && len && entries; i++, len -= PAGE_SIZE) {
|
||||||
struct page *page;
|
struct page *page;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
@ -1045,16 +1045,6 @@ config DEBUG_BUGVERBOSE
|
|||||||
of the BUG call as well as the EIP and oops trace. This aids
|
of the BUG call as well as the EIP and oops trace. This aids
|
||||||
debugging but costs about 70-100K of memory.
|
debugging but costs about 70-100K of memory.
|
||||||
|
|
||||||
config DEBUG_WRITECOUNT
|
|
||||||
bool "Debug filesystem writers count"
|
|
||||||
depends on DEBUG_KERNEL
|
|
||||||
help
|
|
||||||
Enable this to catch wrong use of the writers count in struct
|
|
||||||
vfsmount. This will increase the size of each file struct by
|
|
||||||
32 bits.
|
|
||||||
|
|
||||||
If unsure, say N.
|
|
||||||
|
|
||||||
config DEBUG_LIST
|
config DEBUG_LIST
|
||||||
bool "Debug linked list manipulation"
|
bool "Debug linked list manipulation"
|
||||||
depends on DEBUG_KERNEL
|
depends on DEBUG_KERNEL
|
||||||
|
@ -17,7 +17,8 @@ obj-y := filemap.o mempool.o oom_kill.o fadvise.o \
|
|||||||
util.o mmzone.o vmstat.o backing-dev.o \
|
util.o mmzone.o vmstat.o backing-dev.o \
|
||||||
mm_init.o mmu_context.o percpu.o slab_common.o \
|
mm_init.o mmu_context.o percpu.o slab_common.o \
|
||||||
compaction.o balloon_compaction.o vmacache.o \
|
compaction.o balloon_compaction.o vmacache.o \
|
||||||
interval_tree.o list_lru.o workingset.o $(mmu-y)
|
interval_tree.o list_lru.o workingset.o \
|
||||||
|
iov_iter.o $(mmu-y)
|
||||||
|
|
||||||
obj-y += init-mm.o
|
obj-y += init-mm.o
|
||||||
|
|
||||||
|
344
mm/filemap.c
344
mm/filemap.c
@ -77,7 +77,7 @@
|
|||||||
* ->mmap_sem
|
* ->mmap_sem
|
||||||
* ->lock_page (access_process_vm)
|
* ->lock_page (access_process_vm)
|
||||||
*
|
*
|
||||||
* ->i_mutex (generic_file_buffered_write)
|
* ->i_mutex (generic_perform_write)
|
||||||
* ->mmap_sem (fault_in_pages_readable->do_page_fault)
|
* ->mmap_sem (fault_in_pages_readable->do_page_fault)
|
||||||
*
|
*
|
||||||
* bdi->wb.list_lock
|
* bdi->wb.list_lock
|
||||||
@ -1428,7 +1428,8 @@ static void shrink_readahead_size_eio(struct file *filp,
|
|||||||
* do_generic_file_read - generic file read routine
|
* do_generic_file_read - generic file read routine
|
||||||
* @filp: the file to read
|
* @filp: the file to read
|
||||||
* @ppos: current file position
|
* @ppos: current file position
|
||||||
* @desc: read_descriptor
|
* @iter: data destination
|
||||||
|
* @written: already copied
|
||||||
*
|
*
|
||||||
* This is a generic file read routine, and uses the
|
* This is a generic file read routine, and uses the
|
||||||
* mapping->a_ops->readpage() function for the actual low-level stuff.
|
* mapping->a_ops->readpage() function for the actual low-level stuff.
|
||||||
@ -1436,8 +1437,8 @@ static void shrink_readahead_size_eio(struct file *filp,
|
|||||||
* This is really ugly. But the goto's actually try to clarify some
|
* This is really ugly. But the goto's actually try to clarify some
|
||||||
* of the logic when it comes to error handling etc.
|
* of the logic when it comes to error handling etc.
|
||||||
*/
|
*/
|
||||||
static void do_generic_file_read(struct file *filp, loff_t *ppos,
|
static ssize_t do_generic_file_read(struct file *filp, loff_t *ppos,
|
||||||
read_descriptor_t *desc)
|
struct iov_iter *iter, ssize_t written)
|
||||||
{
|
{
|
||||||
struct address_space *mapping = filp->f_mapping;
|
struct address_space *mapping = filp->f_mapping;
|
||||||
struct inode *inode = mapping->host;
|
struct inode *inode = mapping->host;
|
||||||
@ -1447,12 +1448,12 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos,
|
|||||||
pgoff_t prev_index;
|
pgoff_t prev_index;
|
||||||
unsigned long offset; /* offset into pagecache page */
|
unsigned long offset; /* offset into pagecache page */
|
||||||
unsigned int prev_offset;
|
unsigned int prev_offset;
|
||||||
int error;
|
int error = 0;
|
||||||
|
|
||||||
index = *ppos >> PAGE_CACHE_SHIFT;
|
index = *ppos >> PAGE_CACHE_SHIFT;
|
||||||
prev_index = ra->prev_pos >> PAGE_CACHE_SHIFT;
|
prev_index = ra->prev_pos >> PAGE_CACHE_SHIFT;
|
||||||
prev_offset = ra->prev_pos & (PAGE_CACHE_SIZE-1);
|
prev_offset = ra->prev_pos & (PAGE_CACHE_SIZE-1);
|
||||||
last_index = (*ppos + desc->count + PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT;
|
last_index = (*ppos + iter->count + PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT;
|
||||||
offset = *ppos & ~PAGE_CACHE_MASK;
|
offset = *ppos & ~PAGE_CACHE_MASK;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@ -1487,7 +1488,7 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos,
|
|||||||
if (!page->mapping)
|
if (!page->mapping)
|
||||||
goto page_not_up_to_date_locked;
|
goto page_not_up_to_date_locked;
|
||||||
if (!mapping->a_ops->is_partially_uptodate(page,
|
if (!mapping->a_ops->is_partially_uptodate(page,
|
||||||
desc, offset))
|
offset, iter->count))
|
||||||
goto page_not_up_to_date_locked;
|
goto page_not_up_to_date_locked;
|
||||||
unlock_page(page);
|
unlock_page(page);
|
||||||
}
|
}
|
||||||
@ -1537,24 +1538,23 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos,
|
|||||||
/*
|
/*
|
||||||
* Ok, we have the page, and it's up-to-date, so
|
* Ok, we have the page, and it's up-to-date, so
|
||||||
* now we can copy it to user space...
|
* now we can copy it to user space...
|
||||||
*
|
|
||||||
* The file_read_actor routine returns how many bytes were
|
|
||||||
* actually used..
|
|
||||||
* NOTE! This may not be the same as how much of a user buffer
|
|
||||||
* we filled up (we may be padding etc), so we can only update
|
|
||||||
* "pos" here (the actor routine has to update the user buffer
|
|
||||||
* pointers and the remaining count).
|
|
||||||
*/
|
*/
|
||||||
ret = file_read_actor(desc, page, offset, nr);
|
|
||||||
|
ret = copy_page_to_iter(page, offset, nr, iter);
|
||||||
offset += ret;
|
offset += ret;
|
||||||
index += offset >> PAGE_CACHE_SHIFT;
|
index += offset >> PAGE_CACHE_SHIFT;
|
||||||
offset &= ~PAGE_CACHE_MASK;
|
offset &= ~PAGE_CACHE_MASK;
|
||||||
prev_offset = offset;
|
prev_offset = offset;
|
||||||
|
|
||||||
page_cache_release(page);
|
page_cache_release(page);
|
||||||
if (ret == nr && desc->count)
|
written += ret;
|
||||||
continue;
|
if (!iov_iter_count(iter))
|
||||||
goto out;
|
goto out;
|
||||||
|
if (ret < nr) {
|
||||||
|
error = -EFAULT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
|
||||||
page_not_up_to_date:
|
page_not_up_to_date:
|
||||||
/* Get exclusive access to the page ... */
|
/* Get exclusive access to the page ... */
|
||||||
@ -1589,6 +1589,7 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos,
|
|||||||
if (unlikely(error)) {
|
if (unlikely(error)) {
|
||||||
if (error == AOP_TRUNCATED_PAGE) {
|
if (error == AOP_TRUNCATED_PAGE) {
|
||||||
page_cache_release(page);
|
page_cache_release(page);
|
||||||
|
error = 0;
|
||||||
goto find_page;
|
goto find_page;
|
||||||
}
|
}
|
||||||
goto readpage_error;
|
goto readpage_error;
|
||||||
@ -1619,7 +1620,6 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos,
|
|||||||
|
|
||||||
readpage_error:
|
readpage_error:
|
||||||
/* UHHUH! A synchronous read error occurred. Report it */
|
/* UHHUH! A synchronous read error occurred. Report it */
|
||||||
desc->error = error;
|
|
||||||
page_cache_release(page);
|
page_cache_release(page);
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@ -1630,16 +1630,17 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos,
|
|||||||
*/
|
*/
|
||||||
page = page_cache_alloc_cold(mapping);
|
page = page_cache_alloc_cold(mapping);
|
||||||
if (!page) {
|
if (!page) {
|
||||||
desc->error = -ENOMEM;
|
error = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
error = add_to_page_cache_lru(page, mapping,
|
error = add_to_page_cache_lru(page, mapping,
|
||||||
index, GFP_KERNEL);
|
index, GFP_KERNEL);
|
||||||
if (error) {
|
if (error) {
|
||||||
page_cache_release(page);
|
page_cache_release(page);
|
||||||
if (error == -EEXIST)
|
if (error == -EEXIST) {
|
||||||
|
error = 0;
|
||||||
goto find_page;
|
goto find_page;
|
||||||
desc->error = error;
|
}
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
goto readpage;
|
goto readpage;
|
||||||
@ -1652,44 +1653,7 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos,
|
|||||||
|
|
||||||
*ppos = ((loff_t)index << PAGE_CACHE_SHIFT) + offset;
|
*ppos = ((loff_t)index << PAGE_CACHE_SHIFT) + offset;
|
||||||
file_accessed(filp);
|
file_accessed(filp);
|
||||||
}
|
return written ? written : error;
|
||||||
|
|
||||||
int file_read_actor(read_descriptor_t *desc, struct page *page,
|
|
||||||
unsigned long offset, unsigned long size)
|
|
||||||
{
|
|
||||||
char *kaddr;
|
|
||||||
unsigned long left, count = desc->count;
|
|
||||||
|
|
||||||
if (size > count)
|
|
||||||
size = count;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Faults on the destination of a read are common, so do it before
|
|
||||||
* taking the kmap.
|
|
||||||
*/
|
|
||||||
if (!fault_in_pages_writeable(desc->arg.buf, size)) {
|
|
||||||
kaddr = kmap_atomic(page);
|
|
||||||
left = __copy_to_user_inatomic(desc->arg.buf,
|
|
||||||
kaddr + offset, size);
|
|
||||||
kunmap_atomic(kaddr);
|
|
||||||
if (left == 0)
|
|
||||||
goto success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do it the slow way */
|
|
||||||
kaddr = kmap(page);
|
|
||||||
left = __copy_to_user(desc->arg.buf, kaddr + offset, size);
|
|
||||||
kunmap(page);
|
|
||||||
|
|
||||||
if (left) {
|
|
||||||
size -= left;
|
|
||||||
desc->error = -EFAULT;
|
|
||||||
}
|
|
||||||
success:
|
|
||||||
desc->count = count - size;
|
|
||||||
desc->written += size;
|
|
||||||
desc->arg.buf += size;
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1747,14 +1711,15 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
|
|||||||
{
|
{
|
||||||
struct file *filp = iocb->ki_filp;
|
struct file *filp = iocb->ki_filp;
|
||||||
ssize_t retval;
|
ssize_t retval;
|
||||||
unsigned long seg = 0;
|
|
||||||
size_t count;
|
size_t count;
|
||||||
loff_t *ppos = &iocb->ki_pos;
|
loff_t *ppos = &iocb->ki_pos;
|
||||||
|
struct iov_iter i;
|
||||||
|
|
||||||
count = 0;
|
count = 0;
|
||||||
retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE);
|
retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE);
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
iov_iter_init(&i, iov, nr_segs, count, 0);
|
||||||
|
|
||||||
/* coalesce the iovecs and go direct-to-BIO for O_DIRECT */
|
/* coalesce the iovecs and go direct-to-BIO for O_DIRECT */
|
||||||
if (filp->f_flags & O_DIRECT) {
|
if (filp->f_flags & O_DIRECT) {
|
||||||
@ -1776,6 +1741,11 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
|
|||||||
if (retval > 0) {
|
if (retval > 0) {
|
||||||
*ppos = pos + retval;
|
*ppos = pos + retval;
|
||||||
count -= retval;
|
count -= retval;
|
||||||
|
/*
|
||||||
|
* If we did a short DIO read we need to skip the
|
||||||
|
* section of the iov that we've already read data into.
|
||||||
|
*/
|
||||||
|
iov_iter_advance(&i, retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1792,39 +1762,7 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
count = retval;
|
retval = do_generic_file_read(filp, ppos, &i, retval);
|
||||||
for (seg = 0; seg < nr_segs; seg++) {
|
|
||||||
read_descriptor_t desc;
|
|
||||||
loff_t offset = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we did a short DIO read we need to skip the section of the
|
|
||||||
* iov that we've already read data into.
|
|
||||||
*/
|
|
||||||
if (count) {
|
|
||||||
if (count > iov[seg].iov_len) {
|
|
||||||
count -= iov[seg].iov_len;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
offset = count;
|
|
||||||
count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
desc.written = 0;
|
|
||||||
desc.arg.buf = iov[seg].iov_base + offset;
|
|
||||||
desc.count = iov[seg].iov_len - offset;
|
|
||||||
if (desc.count == 0)
|
|
||||||
continue;
|
|
||||||
desc.error = 0;
|
|
||||||
do_generic_file_read(filp, ppos, &desc);
|
|
||||||
retval += desc.written;
|
|
||||||
if (desc.error) {
|
|
||||||
retval = retval ?: desc.error;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (desc.count > 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
out:
|
out:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
@ -2335,150 +2273,6 @@ struct page *read_cache_page_gfp(struct address_space *mapping,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(read_cache_page_gfp);
|
EXPORT_SYMBOL(read_cache_page_gfp);
|
||||||
|
|
||||||
static size_t __iovec_copy_from_user_inatomic(char *vaddr,
|
|
||||||
const struct iovec *iov, size_t base, size_t bytes)
|
|
||||||
{
|
|
||||||
size_t copied = 0, left = 0;
|
|
||||||
|
|
||||||
while (bytes) {
|
|
||||||
char __user *buf = iov->iov_base + base;
|
|
||||||
int copy = min(bytes, iov->iov_len - base);
|
|
||||||
|
|
||||||
base = 0;
|
|
||||||
left = __copy_from_user_inatomic(vaddr, buf, copy);
|
|
||||||
copied += copy;
|
|
||||||
bytes -= copy;
|
|
||||||
vaddr += copy;
|
|
||||||
iov++;
|
|
||||||
|
|
||||||
if (unlikely(left))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return copied - left;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy as much as we can into the page and return the number of bytes which
|
|
||||||
* were successfully copied. If a fault is encountered then return the number of
|
|
||||||
* bytes which were copied.
|
|
||||||
*/
|
|
||||||
size_t iov_iter_copy_from_user_atomic(struct page *page,
|
|
||||||
struct iov_iter *i, unsigned long offset, size_t bytes)
|
|
||||||
{
|
|
||||||
char *kaddr;
|
|
||||||
size_t copied;
|
|
||||||
|
|
||||||
BUG_ON(!in_atomic());
|
|
||||||
kaddr = kmap_atomic(page);
|
|
||||||
if (likely(i->nr_segs == 1)) {
|
|
||||||
int left;
|
|
||||||
char __user *buf = i->iov->iov_base + i->iov_offset;
|
|
||||||
left = __copy_from_user_inatomic(kaddr + offset, buf, bytes);
|
|
||||||
copied = bytes - left;
|
|
||||||
} else {
|
|
||||||
copied = __iovec_copy_from_user_inatomic(kaddr + offset,
|
|
||||||
i->iov, i->iov_offset, bytes);
|
|
||||||
}
|
|
||||||
kunmap_atomic(kaddr);
|
|
||||||
|
|
||||||
return copied;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(iov_iter_copy_from_user_atomic);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This has the same sideeffects and return value as
|
|
||||||
* iov_iter_copy_from_user_atomic().
|
|
||||||
* The difference is that it attempts to resolve faults.
|
|
||||||
* Page must not be locked.
|
|
||||||
*/
|
|
||||||
size_t iov_iter_copy_from_user(struct page *page,
|
|
||||||
struct iov_iter *i, unsigned long offset, size_t bytes)
|
|
||||||
{
|
|
||||||
char *kaddr;
|
|
||||||
size_t copied;
|
|
||||||
|
|
||||||
kaddr = kmap(page);
|
|
||||||
if (likely(i->nr_segs == 1)) {
|
|
||||||
int left;
|
|
||||||
char __user *buf = i->iov->iov_base + i->iov_offset;
|
|
||||||
left = __copy_from_user(kaddr + offset, buf, bytes);
|
|
||||||
copied = bytes - left;
|
|
||||||
} else {
|
|
||||||
copied = __iovec_copy_from_user_inatomic(kaddr + offset,
|
|
||||||
i->iov, i->iov_offset, bytes);
|
|
||||||
}
|
|
||||||
kunmap(page);
|
|
||||||
return copied;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(iov_iter_copy_from_user);
|
|
||||||
|
|
||||||
void iov_iter_advance(struct iov_iter *i, size_t bytes)
|
|
||||||
{
|
|
||||||
BUG_ON(i->count < bytes);
|
|
||||||
|
|
||||||
if (likely(i->nr_segs == 1)) {
|
|
||||||
i->iov_offset += bytes;
|
|
||||||
i->count -= bytes;
|
|
||||||
} else {
|
|
||||||
const struct iovec *iov = i->iov;
|
|
||||||
size_t base = i->iov_offset;
|
|
||||||
unsigned long nr_segs = i->nr_segs;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The !iov->iov_len check ensures we skip over unlikely
|
|
||||||
* zero-length segments (without overruning the iovec).
|
|
||||||
*/
|
|
||||||
while (bytes || unlikely(i->count && !iov->iov_len)) {
|
|
||||||
int copy;
|
|
||||||
|
|
||||||
copy = min(bytes, iov->iov_len - base);
|
|
||||||
BUG_ON(!i->count || i->count < copy);
|
|
||||||
i->count -= copy;
|
|
||||||
bytes -= copy;
|
|
||||||
base += copy;
|
|
||||||
if (iov->iov_len == base) {
|
|
||||||
iov++;
|
|
||||||
nr_segs--;
|
|
||||||
base = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i->iov = iov;
|
|
||||||
i->iov_offset = base;
|
|
||||||
i->nr_segs = nr_segs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(iov_iter_advance);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fault in the first iovec of the given iov_iter, to a maximum length
|
|
||||||
* of bytes. Returns 0 on success, or non-zero if the memory could not be
|
|
||||||
* accessed (ie. because it is an invalid address).
|
|
||||||
*
|
|
||||||
* writev-intensive code may want this to prefault several iovecs -- that
|
|
||||||
* would be possible (callers must not rely on the fact that _only_ the
|
|
||||||
* first iovec will be faulted with the current implementation).
|
|
||||||
*/
|
|
||||||
int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes)
|
|
||||||
{
|
|
||||||
char __user *buf = i->iov->iov_base + i->iov_offset;
|
|
||||||
bytes = min(bytes, i->iov->iov_len - i->iov_offset);
|
|
||||||
return fault_in_pages_readable(buf, bytes);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(iov_iter_fault_in_readable);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return the count of just the current iov_iter segment.
|
|
||||||
*/
|
|
||||||
size_t iov_iter_single_seg_count(const struct iov_iter *i)
|
|
||||||
{
|
|
||||||
const struct iovec *iov = i->iov;
|
|
||||||
if (i->nr_segs == 1)
|
|
||||||
return i->count;
|
|
||||||
else
|
|
||||||
return min(i->count, iov->iov_len - i->iov_offset);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(iov_iter_single_seg_count);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Performs necessary checks before doing a write
|
* Performs necessary checks before doing a write
|
||||||
*
|
*
|
||||||
@ -2585,7 +2379,7 @@ EXPORT_SYMBOL(pagecache_write_end);
|
|||||||
|
|
||||||
ssize_t
|
ssize_t
|
||||||
generic_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
|
generic_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
|
||||||
unsigned long *nr_segs, loff_t pos, loff_t *ppos,
|
unsigned long *nr_segs, loff_t pos,
|
||||||
size_t count, size_t ocount)
|
size_t count, size_t ocount)
|
||||||
{
|
{
|
||||||
struct file *file = iocb->ki_filp;
|
struct file *file = iocb->ki_filp;
|
||||||
@ -2646,7 +2440,7 @@ generic_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
|
|||||||
i_size_write(inode, pos);
|
i_size_write(inode, pos);
|
||||||
mark_inode_dirty(inode);
|
mark_inode_dirty(inode);
|
||||||
}
|
}
|
||||||
*ppos = pos;
|
iocb->ki_pos = pos;
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
return written;
|
return written;
|
||||||
@ -2692,7 +2486,7 @@ struct page *grab_cache_page_write_begin(struct address_space *mapping,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(grab_cache_page_write_begin);
|
EXPORT_SYMBOL(grab_cache_page_write_begin);
|
||||||
|
|
||||||
static ssize_t generic_perform_write(struct file *file,
|
ssize_t generic_perform_write(struct file *file,
|
||||||
struct iov_iter *i, loff_t pos)
|
struct iov_iter *i, loff_t pos)
|
||||||
{
|
{
|
||||||
struct address_space *mapping = file->f_mapping;
|
struct address_space *mapping = file->f_mapping;
|
||||||
@ -2742,9 +2536,7 @@ static ssize_t generic_perform_write(struct file *file,
|
|||||||
if (mapping_writably_mapped(mapping))
|
if (mapping_writably_mapped(mapping))
|
||||||
flush_dcache_page(page);
|
flush_dcache_page(page);
|
||||||
|
|
||||||
pagefault_disable();
|
|
||||||
copied = iov_iter_copy_from_user_atomic(page, i, offset, bytes);
|
copied = iov_iter_copy_from_user_atomic(page, i, offset, bytes);
|
||||||
pagefault_enable();
|
|
||||||
flush_dcache_page(page);
|
flush_dcache_page(page);
|
||||||
|
|
||||||
mark_page_accessed(page);
|
mark_page_accessed(page);
|
||||||
@ -2782,27 +2574,7 @@ static ssize_t generic_perform_write(struct file *file,
|
|||||||
|
|
||||||
return written ? written : status;
|
return written ? written : status;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(generic_perform_write);
|
||||||
ssize_t
|
|
||||||
generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov,
|
|
||||||
unsigned long nr_segs, loff_t pos, loff_t *ppos,
|
|
||||||
size_t count, ssize_t written)
|
|
||||||
{
|
|
||||||
struct file *file = iocb->ki_filp;
|
|
||||||
ssize_t status;
|
|
||||||
struct iov_iter i;
|
|
||||||
|
|
||||||
iov_iter_init(&i, iov, nr_segs, count, written);
|
|
||||||
status = generic_perform_write(file, &i, pos);
|
|
||||||
|
|
||||||
if (likely(status >= 0)) {
|
|
||||||
written += status;
|
|
||||||
*ppos = pos + status;
|
|
||||||
}
|
|
||||||
|
|
||||||
return written ? written : status;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(generic_file_buffered_write);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __generic_file_aio_write - write data to a file
|
* __generic_file_aio_write - write data to a file
|
||||||
@ -2824,16 +2596,18 @@ EXPORT_SYMBOL(generic_file_buffered_write);
|
|||||||
* avoid syncing under i_mutex.
|
* avoid syncing under i_mutex.
|
||||||
*/
|
*/
|
||||||
ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
||||||
unsigned long nr_segs, loff_t *ppos)
|
unsigned long nr_segs)
|
||||||
{
|
{
|
||||||
struct file *file = iocb->ki_filp;
|
struct file *file = iocb->ki_filp;
|
||||||
struct address_space * mapping = file->f_mapping;
|
struct address_space * mapping = file->f_mapping;
|
||||||
size_t ocount; /* original count */
|
size_t ocount; /* original count */
|
||||||
size_t count; /* after file limit checks */
|
size_t count; /* after file limit checks */
|
||||||
struct inode *inode = mapping->host;
|
struct inode *inode = mapping->host;
|
||||||
loff_t pos;
|
loff_t pos = iocb->ki_pos;
|
||||||
ssize_t written;
|
ssize_t written = 0;
|
||||||
ssize_t err;
|
ssize_t err;
|
||||||
|
ssize_t status;
|
||||||
|
struct iov_iter from;
|
||||||
|
|
||||||
ocount = 0;
|
ocount = 0;
|
||||||
err = generic_segment_checks(iov, &nr_segs, &ocount, VERIFY_READ);
|
err = generic_segment_checks(iov, &nr_segs, &ocount, VERIFY_READ);
|
||||||
@ -2841,12 +2615,9 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
|||||||
return err;
|
return err;
|
||||||
|
|
||||||
count = ocount;
|
count = ocount;
|
||||||
pos = *ppos;
|
|
||||||
|
|
||||||
/* We can write back this queue in page reclaim */
|
/* We can write back this queue in page reclaim */
|
||||||
current->backing_dev_info = mapping->backing_dev_info;
|
current->backing_dev_info = mapping->backing_dev_info;
|
||||||
written = 0;
|
|
||||||
|
|
||||||
err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode));
|
err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode));
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
@ -2862,45 +2633,47 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
|||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
iov_iter_init(&from, iov, nr_segs, count, 0);
|
||||||
|
|
||||||
/* coalesce the iovecs and go direct-to-BIO for O_DIRECT */
|
/* coalesce the iovecs and go direct-to-BIO for O_DIRECT */
|
||||||
if (unlikely(file->f_flags & O_DIRECT)) {
|
if (unlikely(file->f_flags & O_DIRECT)) {
|
||||||
loff_t endbyte;
|
loff_t endbyte;
|
||||||
ssize_t written_buffered;
|
|
||||||
|
|
||||||
written = generic_file_direct_write(iocb, iov, &nr_segs, pos,
|
written = generic_file_direct_write(iocb, iov, &from.nr_segs, pos,
|
||||||
ppos, count, ocount);
|
count, ocount);
|
||||||
if (written < 0 || written == count)
|
if (written < 0 || written == count)
|
||||||
goto out;
|
goto out;
|
||||||
|
iov_iter_advance(&from, written);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* direct-io write to a hole: fall through to buffered I/O
|
* direct-io write to a hole: fall through to buffered I/O
|
||||||
* for completing the rest of the request.
|
* for completing the rest of the request.
|
||||||
*/
|
*/
|
||||||
pos += written;
|
pos += written;
|
||||||
count -= written;
|
count -= written;
|
||||||
written_buffered = generic_file_buffered_write(iocb, iov,
|
|
||||||
nr_segs, pos, ppos, count,
|
status = generic_perform_write(file, &from, pos);
|
||||||
written);
|
|
||||||
/*
|
/*
|
||||||
* If generic_file_buffered_write() retuned a synchronous error
|
* If generic_perform_write() returned a synchronous error
|
||||||
* then we want to return the number of bytes which were
|
* then we want to return the number of bytes which were
|
||||||
* direct-written, or the error code if that was zero. Note
|
* direct-written, or the error code if that was zero. Note
|
||||||
* that this differs from normal direct-io semantics, which
|
* that this differs from normal direct-io semantics, which
|
||||||
* will return -EFOO even if some bytes were written.
|
* will return -EFOO even if some bytes were written.
|
||||||
*/
|
*/
|
||||||
if (written_buffered < 0) {
|
if (unlikely(status < 0) && !written) {
|
||||||
err = written_buffered;
|
err = status;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
iocb->ki_pos = pos + status;
|
||||||
/*
|
/*
|
||||||
* We need to ensure that the page cache pages are written to
|
* We need to ensure that the page cache pages are written to
|
||||||
* disk and invalidated to preserve the expected O_DIRECT
|
* disk and invalidated to preserve the expected O_DIRECT
|
||||||
* semantics.
|
* semantics.
|
||||||
*/
|
*/
|
||||||
endbyte = pos + written_buffered - written - 1;
|
endbyte = pos + status - 1;
|
||||||
err = filemap_write_and_wait_range(file->f_mapping, pos, endbyte);
|
err = filemap_write_and_wait_range(file->f_mapping, pos, endbyte);
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
written = written_buffered;
|
written += status;
|
||||||
invalidate_mapping_pages(mapping,
|
invalidate_mapping_pages(mapping,
|
||||||
pos >> PAGE_CACHE_SHIFT,
|
pos >> PAGE_CACHE_SHIFT,
|
||||||
endbyte >> PAGE_CACHE_SHIFT);
|
endbyte >> PAGE_CACHE_SHIFT);
|
||||||
@ -2911,8 +2684,9 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
written = generic_file_buffered_write(iocb, iov, nr_segs,
|
written = generic_perform_write(file, &from, pos);
|
||||||
pos, ppos, count, written);
|
if (likely(written >= 0))
|
||||||
|
iocb->ki_pos = pos + written;
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
current->backing_dev_info = NULL;
|
current->backing_dev_info = NULL;
|
||||||
@ -2941,7 +2715,7 @@ ssize_t generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
|||||||
BUG_ON(iocb->ki_pos != pos);
|
BUG_ON(iocb->ki_pos != pos);
|
||||||
|
|
||||||
mutex_lock(&inode->i_mutex);
|
mutex_lock(&inode->i_mutex);
|
||||||
ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
|
ret = __generic_file_aio_write(iocb, iov, nr_segs);
|
||||||
mutex_unlock(&inode->i_mutex);
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
|
224
mm/iov_iter.c
Normal file
224
mm/iov_iter.c
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/uio.h>
|
||||||
|
#include <linux/pagemap.h>
|
||||||
|
|
||||||
|
size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
|
||||||
|
struct iov_iter *i)
|
||||||
|
{
|
||||||
|
size_t skip, copy, left, wanted;
|
||||||
|
const struct iovec *iov;
|
||||||
|
char __user *buf;
|
||||||
|
void *kaddr, *from;
|
||||||
|
|
||||||
|
if (unlikely(bytes > i->count))
|
||||||
|
bytes = i->count;
|
||||||
|
|
||||||
|
if (unlikely(!bytes))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
wanted = bytes;
|
||||||
|
iov = i->iov;
|
||||||
|
skip = i->iov_offset;
|
||||||
|
buf = iov->iov_base + skip;
|
||||||
|
copy = min(bytes, iov->iov_len - skip);
|
||||||
|
|
||||||
|
if (!fault_in_pages_writeable(buf, copy)) {
|
||||||
|
kaddr = kmap_atomic(page);
|
||||||
|
from = kaddr + offset;
|
||||||
|
|
||||||
|
/* first chunk, usually the only one */
|
||||||
|
left = __copy_to_user_inatomic(buf, from, copy);
|
||||||
|
copy -= left;
|
||||||
|
skip += copy;
|
||||||
|
from += copy;
|
||||||
|
bytes -= copy;
|
||||||
|
|
||||||
|
while (unlikely(!left && bytes)) {
|
||||||
|
iov++;
|
||||||
|
buf = iov->iov_base;
|
||||||
|
copy = min(bytes, iov->iov_len);
|
||||||
|
left = __copy_to_user_inatomic(buf, from, copy);
|
||||||
|
copy -= left;
|
||||||
|
skip = copy;
|
||||||
|
from += copy;
|
||||||
|
bytes -= copy;
|
||||||
|
}
|
||||||
|
if (likely(!bytes)) {
|
||||||
|
kunmap_atomic(kaddr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
offset = from - kaddr;
|
||||||
|
buf += copy;
|
||||||
|
kunmap_atomic(kaddr);
|
||||||
|
copy = min(bytes, iov->iov_len - skip);
|
||||||
|
}
|
||||||
|
/* Too bad - revert to non-atomic kmap */
|
||||||
|
kaddr = kmap(page);
|
||||||
|
from = kaddr + offset;
|
||||||
|
left = __copy_to_user(buf, from, copy);
|
||||||
|
copy -= left;
|
||||||
|
skip += copy;
|
||||||
|
from += copy;
|
||||||
|
bytes -= copy;
|
||||||
|
while (unlikely(!left && bytes)) {
|
||||||
|
iov++;
|
||||||
|
buf = iov->iov_base;
|
||||||
|
copy = min(bytes, iov->iov_len);
|
||||||
|
left = __copy_to_user(buf, from, copy);
|
||||||
|
copy -= left;
|
||||||
|
skip = copy;
|
||||||
|
from += copy;
|
||||||
|
bytes -= copy;
|
||||||
|
}
|
||||||
|
kunmap(page);
|
||||||
|
done:
|
||||||
|
i->count -= wanted - bytes;
|
||||||
|
i->nr_segs -= iov - i->iov;
|
||||||
|
i->iov = iov;
|
||||||
|
i->iov_offset = skip;
|
||||||
|
return wanted - bytes;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(copy_page_to_iter);
|
||||||
|
|
||||||
|
static size_t __iovec_copy_from_user_inatomic(char *vaddr,
|
||||||
|
const struct iovec *iov, size_t base, size_t bytes)
|
||||||
|
{
|
||||||
|
size_t copied = 0, left = 0;
|
||||||
|
|
||||||
|
while (bytes) {
|
||||||
|
char __user *buf = iov->iov_base + base;
|
||||||
|
int copy = min(bytes, iov->iov_len - base);
|
||||||
|
|
||||||
|
base = 0;
|
||||||
|
left = __copy_from_user_inatomic(vaddr, buf, copy);
|
||||||
|
copied += copy;
|
||||||
|
bytes -= copy;
|
||||||
|
vaddr += copy;
|
||||||
|
iov++;
|
||||||
|
|
||||||
|
if (unlikely(left))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return copied - left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy as much as we can into the page and return the number of bytes which
|
||||||
|
* were successfully copied. If a fault is encountered then return the number of
|
||||||
|
* bytes which were copied.
|
||||||
|
*/
|
||||||
|
size_t iov_iter_copy_from_user_atomic(struct page *page,
|
||||||
|
struct iov_iter *i, unsigned long offset, size_t bytes)
|
||||||
|
{
|
||||||
|
char *kaddr;
|
||||||
|
size_t copied;
|
||||||
|
|
||||||
|
kaddr = kmap_atomic(page);
|
||||||
|
if (likely(i->nr_segs == 1)) {
|
||||||
|
int left;
|
||||||
|
char __user *buf = i->iov->iov_base + i->iov_offset;
|
||||||
|
left = __copy_from_user_inatomic(kaddr + offset, buf, bytes);
|
||||||
|
copied = bytes - left;
|
||||||
|
} else {
|
||||||
|
copied = __iovec_copy_from_user_inatomic(kaddr + offset,
|
||||||
|
i->iov, i->iov_offset, bytes);
|
||||||
|
}
|
||||||
|
kunmap_atomic(kaddr);
|
||||||
|
|
||||||
|
return copied;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(iov_iter_copy_from_user_atomic);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This has the same sideeffects and return value as
|
||||||
|
* iov_iter_copy_from_user_atomic().
|
||||||
|
* The difference is that it attempts to resolve faults.
|
||||||
|
* Page must not be locked.
|
||||||
|
*/
|
||||||
|
size_t iov_iter_copy_from_user(struct page *page,
|
||||||
|
struct iov_iter *i, unsigned long offset, size_t bytes)
|
||||||
|
{
|
||||||
|
char *kaddr;
|
||||||
|
size_t copied;
|
||||||
|
|
||||||
|
kaddr = kmap(page);
|
||||||
|
if (likely(i->nr_segs == 1)) {
|
||||||
|
int left;
|
||||||
|
char __user *buf = i->iov->iov_base + i->iov_offset;
|
||||||
|
left = __copy_from_user(kaddr + offset, buf, bytes);
|
||||||
|
copied = bytes - left;
|
||||||
|
} else {
|
||||||
|
copied = __iovec_copy_from_user_inatomic(kaddr + offset,
|
||||||
|
i->iov, i->iov_offset, bytes);
|
||||||
|
}
|
||||||
|
kunmap(page);
|
||||||
|
return copied;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(iov_iter_copy_from_user);
|
||||||
|
|
||||||
|
void iov_iter_advance(struct iov_iter *i, size_t bytes)
|
||||||
|
{
|
||||||
|
BUG_ON(i->count < bytes);
|
||||||
|
|
||||||
|
if (likely(i->nr_segs == 1)) {
|
||||||
|
i->iov_offset += bytes;
|
||||||
|
i->count -= bytes;
|
||||||
|
} else {
|
||||||
|
const struct iovec *iov = i->iov;
|
||||||
|
size_t base = i->iov_offset;
|
||||||
|
unsigned long nr_segs = i->nr_segs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The !iov->iov_len check ensures we skip over unlikely
|
||||||
|
* zero-length segments (without overruning the iovec).
|
||||||
|
*/
|
||||||
|
while (bytes || unlikely(i->count && !iov->iov_len)) {
|
||||||
|
int copy;
|
||||||
|
|
||||||
|
copy = min(bytes, iov->iov_len - base);
|
||||||
|
BUG_ON(!i->count || i->count < copy);
|
||||||
|
i->count -= copy;
|
||||||
|
bytes -= copy;
|
||||||
|
base += copy;
|
||||||
|
if (iov->iov_len == base) {
|
||||||
|
iov++;
|
||||||
|
nr_segs--;
|
||||||
|
base = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i->iov = iov;
|
||||||
|
i->iov_offset = base;
|
||||||
|
i->nr_segs = nr_segs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(iov_iter_advance);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fault in the first iovec of the given iov_iter, to a maximum length
|
||||||
|
* of bytes. Returns 0 on success, or non-zero if the memory could not be
|
||||||
|
* accessed (ie. because it is an invalid address).
|
||||||
|
*
|
||||||
|
* writev-intensive code may want this to prefault several iovecs -- that
|
||||||
|
* would be possible (callers must not rely on the fact that _only_ the
|
||||||
|
* first iovec will be faulted with the current implementation).
|
||||||
|
*/
|
||||||
|
int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes)
|
||||||
|
{
|
||||||
|
char __user *buf = i->iov->iov_base + i->iov_offset;
|
||||||
|
bytes = min(bytes, i->iov->iov_len - i->iov_offset);
|
||||||
|
return fault_in_pages_readable(buf, bytes);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(iov_iter_fault_in_readable);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the count of just the current iov_iter segment.
|
||||||
|
*/
|
||||||
|
size_t iov_iter_single_seg_count(const struct iov_iter *i)
|
||||||
|
{
|
||||||
|
const struct iovec *iov = i->iov;
|
||||||
|
if (i->nr_segs == 1)
|
||||||
|
return i->count;
|
||||||
|
else
|
||||||
|
return min(i->count, iov->iov_len - i->iov_offset);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(iov_iter_single_seg_count);
|
@ -23,129 +23,44 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* process_vm_rw_pages - read/write pages from task specified
|
* process_vm_rw_pages - read/write pages from task specified
|
||||||
* @task: task to read/write from
|
* @pages: array of pointers to pages we want to copy
|
||||||
* @mm: mm for task
|
|
||||||
* @process_pages: struct pages area that can store at least
|
|
||||||
* nr_pages_to_copy struct page pointers
|
|
||||||
* @pa: address of page in task to start copying from/to
|
|
||||||
* @start_offset: offset in page to start copying from/to
|
* @start_offset: offset in page to start copying from/to
|
||||||
* @len: number of bytes to copy
|
* @len: number of bytes to copy
|
||||||
* @lvec: iovec array specifying where to copy to/from
|
* @iter: where to copy to/from locally
|
||||||
* @lvec_cnt: number of elements in iovec array
|
|
||||||
* @lvec_current: index in iovec array we are up to
|
|
||||||
* @lvec_offset: offset in bytes from current iovec iov_base we are up to
|
|
||||||
* @vm_write: 0 means copy from, 1 means copy to
|
* @vm_write: 0 means copy from, 1 means copy to
|
||||||
* @nr_pages_to_copy: number of pages to copy
|
|
||||||
* @bytes_copied: returns number of bytes successfully copied
|
|
||||||
* Returns 0 on success, error code otherwise
|
* Returns 0 on success, error code otherwise
|
||||||
*/
|
*/
|
||||||
static int process_vm_rw_pages(struct task_struct *task,
|
static int process_vm_rw_pages(struct page **pages,
|
||||||
struct mm_struct *mm,
|
unsigned offset,
|
||||||
struct page **process_pages,
|
size_t len,
|
||||||
unsigned long pa,
|
struct iov_iter *iter,
|
||||||
unsigned long start_offset,
|
int vm_write)
|
||||||
unsigned long len,
|
|
||||||
const struct iovec *lvec,
|
|
||||||
unsigned long lvec_cnt,
|
|
||||||
unsigned long *lvec_current,
|
|
||||||
size_t *lvec_offset,
|
|
||||||
int vm_write,
|
|
||||||
unsigned int nr_pages_to_copy,
|
|
||||||
ssize_t *bytes_copied)
|
|
||||||
{
|
{
|
||||||
int pages_pinned;
|
|
||||||
void *target_kaddr;
|
|
||||||
int pgs_copied = 0;
|
|
||||||
int j;
|
|
||||||
int ret;
|
|
||||||
ssize_t bytes_to_copy;
|
|
||||||
ssize_t rc = 0;
|
|
||||||
|
|
||||||
*bytes_copied = 0;
|
|
||||||
|
|
||||||
/* Get the pages we're interested in */
|
|
||||||
down_read(&mm->mmap_sem);
|
|
||||||
pages_pinned = get_user_pages(task, mm, pa,
|
|
||||||
nr_pages_to_copy,
|
|
||||||
vm_write, 0, process_pages, NULL);
|
|
||||||
up_read(&mm->mmap_sem);
|
|
||||||
|
|
||||||
if (pages_pinned != nr_pages_to_copy) {
|
|
||||||
rc = -EFAULT;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do the copy for each page */
|
/* Do the copy for each page */
|
||||||
for (pgs_copied = 0;
|
while (len && iov_iter_count(iter)) {
|
||||||
(pgs_copied < nr_pages_to_copy) && (*lvec_current < lvec_cnt);
|
struct page *page = *pages++;
|
||||||
pgs_copied++) {
|
size_t copy = PAGE_SIZE - offset;
|
||||||
/* Make sure we have a non zero length iovec */
|
size_t copied;
|
||||||
while (*lvec_current < lvec_cnt
|
|
||||||
&& lvec[*lvec_current].iov_len == 0)
|
|
||||||
(*lvec_current)++;
|
|
||||||
if (*lvec_current == lvec_cnt)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/*
|
if (copy > len)
|
||||||
* Will copy smallest of:
|
copy = len;
|
||||||
* - bytes remaining in page
|
|
||||||
* - bytes remaining in destination iovec
|
|
||||||
*/
|
|
||||||
bytes_to_copy = min_t(ssize_t, PAGE_SIZE - start_offset,
|
|
||||||
len - *bytes_copied);
|
|
||||||
bytes_to_copy = min_t(ssize_t, bytes_to_copy,
|
|
||||||
lvec[*lvec_current].iov_len
|
|
||||||
- *lvec_offset);
|
|
||||||
|
|
||||||
target_kaddr = kmap(process_pages[pgs_copied]) + start_offset;
|
if (vm_write) {
|
||||||
|
if (copy > iov_iter_count(iter))
|
||||||
if (vm_write)
|
copy = iov_iter_count(iter);
|
||||||
ret = copy_from_user(target_kaddr,
|
copied = iov_iter_copy_from_user(page, iter,
|
||||||
lvec[*lvec_current].iov_base
|
offset, copy);
|
||||||
+ *lvec_offset,
|
iov_iter_advance(iter, copied);
|
||||||
bytes_to_copy);
|
set_page_dirty_lock(page);
|
||||||
else
|
|
||||||
ret = copy_to_user(lvec[*lvec_current].iov_base
|
|
||||||
+ *lvec_offset,
|
|
||||||
target_kaddr, bytes_to_copy);
|
|
||||||
kunmap(process_pages[pgs_copied]);
|
|
||||||
if (ret) {
|
|
||||||
*bytes_copied += bytes_to_copy - ret;
|
|
||||||
pgs_copied++;
|
|
||||||
rc = -EFAULT;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
*bytes_copied += bytes_to_copy;
|
|
||||||
*lvec_offset += bytes_to_copy;
|
|
||||||
if (*lvec_offset == lvec[*lvec_current].iov_len) {
|
|
||||||
/*
|
|
||||||
* Need to copy remaining part of page into the
|
|
||||||
* next iovec if there are any bytes left in page
|
|
||||||
*/
|
|
||||||
(*lvec_current)++;
|
|
||||||
*lvec_offset = 0;
|
|
||||||
start_offset = (start_offset + bytes_to_copy)
|
|
||||||
% PAGE_SIZE;
|
|
||||||
if (start_offset)
|
|
||||||
pgs_copied--;
|
|
||||||
} else {
|
} else {
|
||||||
start_offset = 0;
|
copied = copy_page_to_iter(page, offset, copy, iter);
|
||||||
}
|
}
|
||||||
|
len -= copied;
|
||||||
|
if (copied < copy && iov_iter_count(iter))
|
||||||
|
return -EFAULT;
|
||||||
|
offset = 0;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
end:
|
|
||||||
if (vm_write) {
|
|
||||||
for (j = 0; j < pages_pinned; j++) {
|
|
||||||
if (j < pgs_copied)
|
|
||||||
set_page_dirty_lock(process_pages[j]);
|
|
||||||
put_page(process_pages[j]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (j = 0; j < pages_pinned; j++)
|
|
||||||
put_page(process_pages[j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Maximum number of pages kmalloc'd to hold struct page's during copy */
|
/* Maximum number of pages kmalloc'd to hold struct page's during copy */
|
||||||
@ -155,67 +70,60 @@ static int process_vm_rw_pages(struct task_struct *task,
|
|||||||
* process_vm_rw_single_vec - read/write pages from task specified
|
* process_vm_rw_single_vec - read/write pages from task specified
|
||||||
* @addr: start memory address of target process
|
* @addr: start memory address of target process
|
||||||
* @len: size of area to copy to/from
|
* @len: size of area to copy to/from
|
||||||
* @lvec: iovec array specifying where to copy to/from locally
|
* @iter: where to copy to/from locally
|
||||||
* @lvec_cnt: number of elements in iovec array
|
|
||||||
* @lvec_current: index in iovec array we are up to
|
|
||||||
* @lvec_offset: offset in bytes from current iovec iov_base we are up to
|
|
||||||
* @process_pages: struct pages area that can store at least
|
* @process_pages: struct pages area that can store at least
|
||||||
* nr_pages_to_copy struct page pointers
|
* nr_pages_to_copy struct page pointers
|
||||||
* @mm: mm for task
|
* @mm: mm for task
|
||||||
* @task: task to read/write from
|
* @task: task to read/write from
|
||||||
* @vm_write: 0 means copy from, 1 means copy to
|
* @vm_write: 0 means copy from, 1 means copy to
|
||||||
* @bytes_copied: returns number of bytes successfully copied
|
|
||||||
* Returns 0 on success or on failure error code
|
* Returns 0 on success or on failure error code
|
||||||
*/
|
*/
|
||||||
static int process_vm_rw_single_vec(unsigned long addr,
|
static int process_vm_rw_single_vec(unsigned long addr,
|
||||||
unsigned long len,
|
unsigned long len,
|
||||||
const struct iovec *lvec,
|
struct iov_iter *iter,
|
||||||
unsigned long lvec_cnt,
|
|
||||||
unsigned long *lvec_current,
|
|
||||||
size_t *lvec_offset,
|
|
||||||
struct page **process_pages,
|
struct page **process_pages,
|
||||||
struct mm_struct *mm,
|
struct mm_struct *mm,
|
||||||
struct task_struct *task,
|
struct task_struct *task,
|
||||||
int vm_write,
|
int vm_write)
|
||||||
ssize_t *bytes_copied)
|
|
||||||
{
|
{
|
||||||
unsigned long pa = addr & PAGE_MASK;
|
unsigned long pa = addr & PAGE_MASK;
|
||||||
unsigned long start_offset = addr - pa;
|
unsigned long start_offset = addr - pa;
|
||||||
unsigned long nr_pages;
|
unsigned long nr_pages;
|
||||||
ssize_t bytes_copied_loop;
|
|
||||||
ssize_t rc = 0;
|
ssize_t rc = 0;
|
||||||
unsigned long nr_pages_copied = 0;
|
|
||||||
unsigned long nr_pages_to_copy;
|
|
||||||
unsigned long max_pages_per_loop = PVM_MAX_KMALLOC_PAGES
|
unsigned long max_pages_per_loop = PVM_MAX_KMALLOC_PAGES
|
||||||
/ sizeof(struct pages *);
|
/ sizeof(struct pages *);
|
||||||
|
|
||||||
*bytes_copied = 0;
|
|
||||||
|
|
||||||
/* Work out address and page range required */
|
/* Work out address and page range required */
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
return 0;
|
return 0;
|
||||||
nr_pages = (addr + len - 1) / PAGE_SIZE - addr / PAGE_SIZE + 1;
|
nr_pages = (addr + len - 1) / PAGE_SIZE - addr / PAGE_SIZE + 1;
|
||||||
|
|
||||||
while ((nr_pages_copied < nr_pages) && (*lvec_current < lvec_cnt)) {
|
while (!rc && nr_pages && iov_iter_count(iter)) {
|
||||||
nr_pages_to_copy = min(nr_pages - nr_pages_copied,
|
int pages = min(nr_pages, max_pages_per_loop);
|
||||||
max_pages_per_loop);
|
size_t bytes;
|
||||||
|
|
||||||
rc = process_vm_rw_pages(task, mm, process_pages, pa,
|
/* Get the pages we're interested in */
|
||||||
start_offset, len,
|
down_read(&mm->mmap_sem);
|
||||||
lvec, lvec_cnt,
|
pages = get_user_pages(task, mm, pa, pages,
|
||||||
lvec_current, lvec_offset,
|
vm_write, 0, process_pages, NULL);
|
||||||
vm_write, nr_pages_to_copy,
|
up_read(&mm->mmap_sem);
|
||||||
&bytes_copied_loop);
|
|
||||||
|
if (pages <= 0)
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
bytes = pages * PAGE_SIZE - start_offset;
|
||||||
|
if (bytes > len)
|
||||||
|
bytes = len;
|
||||||
|
|
||||||
|
rc = process_vm_rw_pages(process_pages,
|
||||||
|
start_offset, bytes, iter,
|
||||||
|
vm_write);
|
||||||
|
len -= bytes;
|
||||||
start_offset = 0;
|
start_offset = 0;
|
||||||
*bytes_copied += bytes_copied_loop;
|
nr_pages -= pages;
|
||||||
|
pa += pages * PAGE_SIZE;
|
||||||
if (rc < 0) {
|
while (pages)
|
||||||
return rc;
|
put_page(process_pages[--pages]);
|
||||||
} else {
|
|
||||||
len -= bytes_copied_loop;
|
|
||||||
nr_pages_copied += nr_pages_to_copy;
|
|
||||||
pa += nr_pages_to_copy * PAGE_SIZE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
@ -228,8 +136,7 @@ static int process_vm_rw_single_vec(unsigned long addr,
|
|||||||
/**
|
/**
|
||||||
* process_vm_rw_core - core of reading/writing pages from task specified
|
* process_vm_rw_core - core of reading/writing pages from task specified
|
||||||
* @pid: PID of process to read/write from/to
|
* @pid: PID of process to read/write from/to
|
||||||
* @lvec: iovec array specifying where to copy to/from locally
|
* @iter: where to copy to/from locally
|
||||||
* @liovcnt: size of lvec array
|
|
||||||
* @rvec: iovec array specifying where to copy to/from in the other process
|
* @rvec: iovec array specifying where to copy to/from in the other process
|
||||||
* @riovcnt: size of rvec array
|
* @riovcnt: size of rvec array
|
||||||
* @flags: currently unused
|
* @flags: currently unused
|
||||||
@ -238,8 +145,7 @@ static int process_vm_rw_single_vec(unsigned long addr,
|
|||||||
* return less bytes than expected if an error occurs during the copying
|
* return less bytes than expected if an error occurs during the copying
|
||||||
* process.
|
* process.
|
||||||
*/
|
*/
|
||||||
static ssize_t process_vm_rw_core(pid_t pid, const struct iovec *lvec,
|
static ssize_t process_vm_rw_core(pid_t pid, struct iov_iter *iter,
|
||||||
unsigned long liovcnt,
|
|
||||||
const struct iovec *rvec,
|
const struct iovec *rvec,
|
||||||
unsigned long riovcnt,
|
unsigned long riovcnt,
|
||||||
unsigned long flags, int vm_write)
|
unsigned long flags, int vm_write)
|
||||||
@ -250,13 +156,10 @@ static ssize_t process_vm_rw_core(pid_t pid, const struct iovec *lvec,
|
|||||||
struct mm_struct *mm;
|
struct mm_struct *mm;
|
||||||
unsigned long i;
|
unsigned long i;
|
||||||
ssize_t rc = 0;
|
ssize_t rc = 0;
|
||||||
ssize_t bytes_copied_loop;
|
|
||||||
ssize_t bytes_copied = 0;
|
|
||||||
unsigned long nr_pages = 0;
|
unsigned long nr_pages = 0;
|
||||||
unsigned long nr_pages_iov;
|
unsigned long nr_pages_iov;
|
||||||
unsigned long iov_l_curr_idx = 0;
|
|
||||||
size_t iov_l_curr_offset = 0;
|
|
||||||
ssize_t iov_len;
|
ssize_t iov_len;
|
||||||
|
size_t total_len = iov_iter_count(iter);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Work out how many pages of struct pages we're going to need
|
* Work out how many pages of struct pages we're going to need
|
||||||
@ -310,24 +213,20 @@ static ssize_t process_vm_rw_core(pid_t pid, const struct iovec *lvec,
|
|||||||
goto put_task_struct;
|
goto put_task_struct;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < riovcnt && iov_l_curr_idx < liovcnt; i++) {
|
for (i = 0; i < riovcnt && iov_iter_count(iter) && !rc; i++)
|
||||||
rc = process_vm_rw_single_vec(
|
rc = process_vm_rw_single_vec(
|
||||||
(unsigned long)rvec[i].iov_base, rvec[i].iov_len,
|
(unsigned long)rvec[i].iov_base, rvec[i].iov_len,
|
||||||
lvec, liovcnt, &iov_l_curr_idx, &iov_l_curr_offset,
|
iter, process_pages, mm, task, vm_write);
|
||||||
process_pages, mm, task, vm_write, &bytes_copied_loop);
|
|
||||||
bytes_copied += bytes_copied_loop;
|
/* copied = space before - space after */
|
||||||
if (rc != 0) {
|
total_len -= iov_iter_count(iter);
|
||||||
/* If we have managed to copy any data at all then
|
|
||||||
we return the number of bytes copied. Otherwise
|
/* If we have managed to copy any data at all then
|
||||||
we return the error code */
|
we return the number of bytes copied. Otherwise
|
||||||
if (bytes_copied)
|
we return the error code */
|
||||||
rc = bytes_copied;
|
if (total_len)
|
||||||
goto put_mm;
|
rc = total_len;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = bytes_copied;
|
|
||||||
put_mm:
|
|
||||||
mmput(mm);
|
mmput(mm);
|
||||||
|
|
||||||
put_task_struct:
|
put_task_struct:
|
||||||
@ -363,6 +262,7 @@ static ssize_t process_vm_rw(pid_t pid,
|
|||||||
struct iovec iovstack_r[UIO_FASTIOV];
|
struct iovec iovstack_r[UIO_FASTIOV];
|
||||||
struct iovec *iov_l = iovstack_l;
|
struct iovec *iov_l = iovstack_l;
|
||||||
struct iovec *iov_r = iovstack_r;
|
struct iovec *iov_r = iovstack_r;
|
||||||
|
struct iov_iter iter;
|
||||||
ssize_t rc;
|
ssize_t rc;
|
||||||
|
|
||||||
if (flags != 0)
|
if (flags != 0)
|
||||||
@ -378,13 +278,14 @@ static ssize_t process_vm_rw(pid_t pid,
|
|||||||
if (rc <= 0)
|
if (rc <= 0)
|
||||||
goto free_iovecs;
|
goto free_iovecs;
|
||||||
|
|
||||||
|
iov_iter_init(&iter, iov_l, liovcnt, rc, 0);
|
||||||
|
|
||||||
rc = rw_copy_check_uvector(CHECK_IOVEC_ONLY, rvec, riovcnt, UIO_FASTIOV,
|
rc = rw_copy_check_uvector(CHECK_IOVEC_ONLY, rvec, riovcnt, UIO_FASTIOV,
|
||||||
iovstack_r, &iov_r);
|
iovstack_r, &iov_r);
|
||||||
if (rc <= 0)
|
if (rc <= 0)
|
||||||
goto free_iovecs;
|
goto free_iovecs;
|
||||||
|
|
||||||
rc = process_vm_rw_core(pid, iov_l, liovcnt, iov_r, riovcnt, flags,
|
rc = process_vm_rw_core(pid, &iter, iov_r, riovcnt, flags, vm_write);
|
||||||
vm_write);
|
|
||||||
|
|
||||||
free_iovecs:
|
free_iovecs:
|
||||||
if (iov_r != iovstack_r)
|
if (iov_r != iovstack_r)
|
||||||
@ -424,6 +325,7 @@ compat_process_vm_rw(compat_pid_t pid,
|
|||||||
struct iovec iovstack_r[UIO_FASTIOV];
|
struct iovec iovstack_r[UIO_FASTIOV];
|
||||||
struct iovec *iov_l = iovstack_l;
|
struct iovec *iov_l = iovstack_l;
|
||||||
struct iovec *iov_r = iovstack_r;
|
struct iovec *iov_r = iovstack_r;
|
||||||
|
struct iov_iter iter;
|
||||||
ssize_t rc = -EFAULT;
|
ssize_t rc = -EFAULT;
|
||||||
|
|
||||||
if (flags != 0)
|
if (flags != 0)
|
||||||
@ -439,14 +341,14 @@ compat_process_vm_rw(compat_pid_t pid,
|
|||||||
&iov_l);
|
&iov_l);
|
||||||
if (rc <= 0)
|
if (rc <= 0)
|
||||||
goto free_iovecs;
|
goto free_iovecs;
|
||||||
|
iov_iter_init(&iter, iov_l, liovcnt, rc, 0);
|
||||||
rc = compat_rw_copy_check_uvector(CHECK_IOVEC_ONLY, rvec, riovcnt,
|
rc = compat_rw_copy_check_uvector(CHECK_IOVEC_ONLY, rvec, riovcnt,
|
||||||
UIO_FASTIOV, iovstack_r,
|
UIO_FASTIOV, iovstack_r,
|
||||||
&iov_r);
|
&iov_r);
|
||||||
if (rc <= 0)
|
if (rc <= 0)
|
||||||
goto free_iovecs;
|
goto free_iovecs;
|
||||||
|
|
||||||
rc = process_vm_rw_core(pid, iov_l, liovcnt, iov_r, riovcnt, flags,
|
rc = process_vm_rw_core(pid, &iter, iov_r, riovcnt, flags, vm_write);
|
||||||
vm_write);
|
|
||||||
|
|
||||||
free_iovecs:
|
free_iovecs:
|
||||||
if (iov_r != iovstack_r)
|
if (iov_r != iovstack_r)
|
||||||
|
79
mm/shmem.c
79
mm/shmem.c
@ -1402,13 +1402,25 @@ shmem_write_end(struct file *file, struct address_space *mapping,
|
|||||||
return copied;
|
return copied;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_shmem_file_read(struct file *filp, loff_t *ppos, read_descriptor_t *desc, read_actor_t actor)
|
static ssize_t shmem_file_aio_read(struct kiocb *iocb,
|
||||||
|
const struct iovec *iov, unsigned long nr_segs, loff_t pos)
|
||||||
{
|
{
|
||||||
struct inode *inode = file_inode(filp);
|
struct file *file = iocb->ki_filp;
|
||||||
|
struct inode *inode = file_inode(file);
|
||||||
struct address_space *mapping = inode->i_mapping;
|
struct address_space *mapping = inode->i_mapping;
|
||||||
pgoff_t index;
|
pgoff_t index;
|
||||||
unsigned long offset;
|
unsigned long offset;
|
||||||
enum sgp_type sgp = SGP_READ;
|
enum sgp_type sgp = SGP_READ;
|
||||||
|
int error;
|
||||||
|
ssize_t retval;
|
||||||
|
size_t count;
|
||||||
|
loff_t *ppos = &iocb->ki_pos;
|
||||||
|
struct iov_iter iter;
|
||||||
|
|
||||||
|
retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
iov_iter_init(&iter, iov, nr_segs, count, 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Might this read be for a stacking filesystem? Then when reading
|
* Might this read be for a stacking filesystem? Then when reading
|
||||||
@ -1436,10 +1448,10 @@ static void do_shmem_file_read(struct file *filp, loff_t *ppos, read_descriptor_
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
desc->error = shmem_getpage(inode, index, &page, sgp, NULL);
|
error = shmem_getpage(inode, index, &page, sgp, NULL);
|
||||||
if (desc->error) {
|
if (error) {
|
||||||
if (desc->error == -EINVAL)
|
if (error == -EINVAL)
|
||||||
desc->error = 0;
|
error = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (page)
|
if (page)
|
||||||
@ -1483,61 +1495,26 @@ static void do_shmem_file_read(struct file *filp, loff_t *ppos, read_descriptor_
|
|||||||
/*
|
/*
|
||||||
* Ok, we have the page, and it's up-to-date, so
|
* Ok, we have the page, and it's up-to-date, so
|
||||||
* now we can copy it to user space...
|
* now we can copy it to user space...
|
||||||
*
|
|
||||||
* The actor routine returns how many bytes were actually used..
|
|
||||||
* NOTE! This may not be the same as how much of a user buffer
|
|
||||||
* we filled up (we may be padding etc), so we can only update
|
|
||||||
* "pos" here (the actor routine has to update the user buffer
|
|
||||||
* pointers and the remaining count).
|
|
||||||
*/
|
*/
|
||||||
ret = actor(desc, page, offset, nr);
|
ret = copy_page_to_iter(page, offset, nr, &iter);
|
||||||
|
retval += ret;
|
||||||
offset += ret;
|
offset += ret;
|
||||||
index += offset >> PAGE_CACHE_SHIFT;
|
index += offset >> PAGE_CACHE_SHIFT;
|
||||||
offset &= ~PAGE_CACHE_MASK;
|
offset &= ~PAGE_CACHE_MASK;
|
||||||
|
|
||||||
page_cache_release(page);
|
page_cache_release(page);
|
||||||
if (ret != nr || !desc->count)
|
if (!iov_iter_count(&iter))
|
||||||
break;
|
break;
|
||||||
|
if (ret < nr) {
|
||||||
|
error = -EFAULT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
cond_resched();
|
cond_resched();
|
||||||
}
|
}
|
||||||
|
|
||||||
*ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset;
|
*ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset;
|
||||||
file_accessed(filp);
|
file_accessed(file);
|
||||||
}
|
return retval ? retval : error;
|
||||||
|
|
||||||
static ssize_t shmem_file_aio_read(struct kiocb *iocb,
|
|
||||||
const struct iovec *iov, unsigned long nr_segs, loff_t pos)
|
|
||||||
{
|
|
||||||
struct file *filp = iocb->ki_filp;
|
|
||||||
ssize_t retval;
|
|
||||||
unsigned long seg;
|
|
||||||
size_t count;
|
|
||||||
loff_t *ppos = &iocb->ki_pos;
|
|
||||||
|
|
||||||
retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
for (seg = 0; seg < nr_segs; seg++) {
|
|
||||||
read_descriptor_t desc;
|
|
||||||
|
|
||||||
desc.written = 0;
|
|
||||||
desc.arg.buf = iov[seg].iov_base;
|
|
||||||
desc.count = iov[seg].iov_len;
|
|
||||||
if (desc.count == 0)
|
|
||||||
continue;
|
|
||||||
desc.error = 0;
|
|
||||||
do_shmem_file_read(filp, ppos, &desc, file_read_actor);
|
|
||||||
retval += desc.written;
|
|
||||||
if (desc.error) {
|
|
||||||
retval = retval ?: desc.error;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (desc.count > 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos,
|
static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos,
|
||||||
@ -1576,7 +1553,7 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos,
|
|||||||
index = *ppos >> PAGE_CACHE_SHIFT;
|
index = *ppos >> PAGE_CACHE_SHIFT;
|
||||||
loff = *ppos & ~PAGE_CACHE_MASK;
|
loff = *ppos & ~PAGE_CACHE_MASK;
|
||||||
req_pages = (len + loff + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
req_pages = (len + loff + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
||||||
nr_pages = min(req_pages, pipe->buffers);
|
nr_pages = min(req_pages, spd.nr_pages_max);
|
||||||
|
|
||||||
spd.nr_pages = find_get_pages_contig(mapping, index,
|
spd.nr_pages = find_get_pages_contig(mapping, index,
|
||||||
nr_pages, spd.pages);
|
nr_pages, spd.pages);
|
||||||
|
@ -139,7 +139,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
|
|||||||
int error;
|
int error;
|
||||||
int size;
|
int size;
|
||||||
|
|
||||||
if (!inode->i_op || !inode->i_op->getxattr)
|
if (!inode->i_op->getxattr)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
desc = init_desc(type);
|
desc = init_desc(type);
|
||||||
if (IS_ERR(desc))
|
if (IS_ERR(desc))
|
||||||
|
@ -64,7 +64,7 @@ static int evm_find_protected_xattrs(struct dentry *dentry)
|
|||||||
int error;
|
int error;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
if (!inode->i_op || !inode->i_op->getxattr)
|
if (!inode->i_op->getxattr)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
for (xattr = evm_config_xattrnames; *xattr != NULL; xattr++) {
|
for (xattr = evm_config_xattrnames; *xattr != NULL; xattr++) {
|
||||||
|
@ -173,7 +173,7 @@ static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer,
|
|||||||
* Use filesystem name if filesystem does not support rename()
|
* Use filesystem name if filesystem does not support rename()
|
||||||
* operation.
|
* operation.
|
||||||
*/
|
*/
|
||||||
if (inode->i_op && !inode->i_op->rename)
|
if (!inode->i_op->rename)
|
||||||
goto prepend_filesystem_name;
|
goto prepend_filesystem_name;
|
||||||
}
|
}
|
||||||
/* Prepend device name. */
|
/* Prepend device name. */
|
||||||
@ -282,7 +282,7 @@ char *tomoyo_realpath_from_path(struct path *path)
|
|||||||
* Get local name for filesystems without rename() operation
|
* Get local name for filesystems without rename() operation
|
||||||
* or dentry without vfsmount.
|
* or dentry without vfsmount.
|
||||||
*/
|
*/
|
||||||
if (!path->mnt || (inode->i_op && !inode->i_op->rename))
|
if (!path->mnt || !inode->i_op->rename)
|
||||||
pos = tomoyo_get_local_path(path->dentry, buf,
|
pos = tomoyo_get_local_path(path->dentry, buf,
|
||||||
buf_len - 1);
|
buf_len - 1);
|
||||||
/* Get absolute name for the rest. */
|
/* Get absolute name for the rest. */
|
||||||
|
Loading…
Reference in New Issue
Block a user