Merge branch 'master' into upstream

This commit is contained in:
Jeff Garzik 2006-04-27 04:52:44 -04:00
commit 1a2e8a6f8e
51 changed files with 450 additions and 226 deletions

View file

@ -246,6 +246,7 @@ class/
devices/ devices/
firmware/ firmware/
net/ net/
fs/
devices/ contains a filesystem representation of the device tree. It maps devices/ contains a filesystem representation of the device tree. It maps
directly to the internal kernel device tree, which is a hierarchy of directly to the internal kernel device tree, which is a hierarchy of
@ -264,6 +265,10 @@ drivers/ contains a directory for each device driver that is loaded
for devices on that particular bus (this assumes that drivers do not for devices on that particular bus (this assumes that drivers do not
span multiple bus types). span multiple bus types).
fs/ contains a directory for some filesystems. Currently each
filesystem wanting to export attributes must create its own hierarchy
below fs/ (see ./fuse.txt for an example).
More information can driver-model specific features can be found in More information can driver-model specific features can be found in
Documentation/driver-model/. Documentation/driver-model/.

View file

@ -1,7 +1,7 @@
VERSION = 2 VERSION = 2
PATCHLEVEL = 6 PATCHLEVEL = 6
SUBLEVEL = 17 SUBLEVEL = 17
EXTRAVERSION =-rc2 EXTRAVERSION =-rc3
NAME=Sliding Snow Leopard NAME=Sliding Snow Leopard
# *DOCUMENTATION* # *DOCUMENTATION*

View file

@ -66,7 +66,7 @@ tune-$(CONFIG_CPU_XSC3) :=$(call cc-option,-mtune=xscale,-mtune=strongarm110) -
tune-$(CONFIG_CPU_V6) :=$(call cc-option,-mtune=arm1136j-s,-mtune=strongarm) tune-$(CONFIG_CPU_V6) :=$(call cc-option,-mtune=arm1136j-s,-mtune=strongarm)
ifeq ($(CONFIG_AEABI),y) ifeq ($(CONFIG_AEABI),y)
CFLAGS_ABI :=-mabi=aapcs -mno-thumb-interwork CFLAGS_ABI :=-mabi=aapcs-linux -mno-thumb-interwork
else else
CFLAGS_ABI :=$(call cc-option,-mapcs-32,-mabi=apcs-gnu) $(call cc-option,-mno-thumb-interwork,) CFLAGS_ABI :=$(call cc-option,-mapcs-32,-mabi=apcs-gnu) $(call cc-option,-mno-thumb-interwork,)
endif endif

View file

@ -29,7 +29,7 @@ ifneq ($(CONFIG_ARCH_EBSA110),y)
obj-y += io.o obj-y += io.o
endif endif
head-y := head.o head-y := head$(MMUEXT).o
obj-$(CONFIG_DEBUG_LL) += debug.o obj-$(CONFIG_DEBUG_LL) += debug.o
extra-y := $(head-y) init_task.o vmlinux.lds extra-y := $(head-y) init_task.o vmlinux.lds

View file

@ -20,10 +20,11 @@
#include <asm/mach-types.h> #include <asm/mach-types.h>
#include <asm/procinfo.h> #include <asm/procinfo.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include <asm/constants.h> #include <asm/thread_info.h>
#include <asm/system.h> #include <asm/system.h>
#define PROCINFO_INITFUNC 12 #define PROCINFO_INITFUNC 12
#define MACHINFO_TYPE 0
/* /*
* Kernel startup entry point. * Kernel startup entry point.
@ -79,5 +80,6 @@ __after_proc_init:
mov pc, r13 @ clear the BSS and jump mov pc, r13 @ clear the BSS and jump
@ to start_kernel @ to start_kernel
.ltorg
#include "head-common.S" #include "head-common.S"

View file

@ -197,7 +197,7 @@ u32 vfp_double_normaliseround(int dd, struct vfp_double *vd, u32 fpscr, u32 exce
dd, d, exceptions); dd, d, exceptions);
vfp_put_double(dd, d); vfp_put_double(dd, d);
} }
return exceptions & ~VFP_NAN_FLAG; return exceptions;
} }
/* /*

View file

@ -180,7 +180,7 @@ static u32 vfp_emulate_instruction(u32 inst, u32 fpscr, struct pt_regs *regs)
* emulate it. * emulate it.
*/ */
} }
return exceptions; return exceptions & ~VFP_NAN_FLAG;
} }
/* /*

View file

@ -203,7 +203,7 @@ u32 vfp_single_normaliseround(int sd, struct vfp_single *vs, u32 fpscr, u32 exce
vfp_put_float(sd, d); vfp_put_float(sd, d);
} }
return exceptions & ~VFP_NAN_FLAG; return exceptions;
} }
/* /*

View file

@ -642,7 +642,7 @@ static void __cpuexit cache_remove_dev(struct sys_device * sys_dev)
return; return;
} }
static int __cpuinit cacheinfo_cpu_callback(struct notifier_block *nfb, static int cacheinfo_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu) unsigned long action, void *hcpu)
{ {
unsigned int cpu = (unsigned long)hcpu; unsigned int cpu = (unsigned long)hcpu;

View file

@ -1610,5 +1610,6 @@ sys_call_table:
data8 sys_get_robust_list data8 sys_get_robust_list
data8 sys_sync_file_range // 1300 data8 sys_sync_file_range // 1300
data8 sys_tee data8 sys_tee
data8 sys_vmsplice
.org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls .org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls

View file

@ -959,7 +959,7 @@ remove_palinfo_proc_entries(unsigned int hcpu)
} }
} }
static int __devinit palinfo_cpu_callback(struct notifier_block *nfb, static int palinfo_cpu_callback(struct notifier_block *nfb,
unsigned long action, unsigned long action,
void *hcpu) void *hcpu)
{ {

View file

@ -572,7 +572,7 @@ static struct file_operations salinfo_data_fops = {
}; };
#ifdef CONFIG_HOTPLUG_CPU #ifdef CONFIG_HOTPLUG_CPU
static int __devinit static int
salinfo_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu) salinfo_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu)
{ {
unsigned int i, cpu = (unsigned long)hcpu; unsigned int i, cpu = (unsigned long)hcpu;

View file

@ -429,7 +429,7 @@ static int __cpuinit cache_remove_dev(struct sys_device * sys_dev)
* When a cpu is hot-plugged, do a check and initiate * When a cpu is hot-plugged, do a check and initiate
* cache kobject if necessary * cache kobject if necessary
*/ */
static int __cpuinit cache_cpu_callback(struct notifier_block *nfb, static int cache_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu) unsigned long action, void *hcpu)
{ {
unsigned int cpu = (unsigned long)hcpu; unsigned int cpu = (unsigned long)hcpu;

View file

@ -356,73 +356,13 @@ asmlinkage int sys32_llseek(unsigned int fd, unsigned int offset_high,
asmlinkage ssize_t sys32_pread(unsigned int fd, char __user * buf, asmlinkage ssize_t sys32_pread(unsigned int fd, char __user * buf,
size_t count, u32 unused, u64 a4, u64 a5) size_t count, u32 unused, u64 a4, u64 a5)
{ {
ssize_t ret; return sys_pread64(fd, buf, count, merge_64(a4, a5));
struct file * file;
ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
loff_t pos;
ret = -EBADF;
file = fget(fd);
if (!file)
goto bad_file;
if (!(file->f_mode & FMODE_READ))
goto out;
pos = merge_64(a4, a5);
ret = rw_verify_area(READ, file, &pos, count);
if (ret < 0)
goto out;
ret = -EINVAL;
if (!file->f_op || !(read = file->f_op->read))
goto out;
if (pos < 0)
goto out;
ret = -ESPIPE;
if (!(file->f_mode & FMODE_PREAD))
goto out;
ret = read(file, buf, count, &pos);
if (ret > 0)
dnotify_parent(file->f_dentry, DN_ACCESS);
out:
fput(file);
bad_file:
return ret;
} }
asmlinkage ssize_t sys32_pwrite(unsigned int fd, const char __user * buf, asmlinkage ssize_t sys32_pwrite(unsigned int fd, const char __user * buf,
size_t count, u32 unused, u64 a4, u64 a5) size_t count, u32 unused, u64 a4, u64 a5)
{ {
ssize_t ret; return sys_pwrite64(fd, buf, count, merge_64(a4, a5));
struct file * file;
ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);
loff_t pos;
ret = -EBADF;
file = fget(fd);
if (!file)
goto bad_file;
if (!(file->f_mode & FMODE_WRITE))
goto out;
pos = merge_64(a4, a5);
ret = rw_verify_area(WRITE, file, &pos, count);
if (ret < 0)
goto out;
ret = -EINVAL;
if (!file->f_op || !(write = file->f_op->write))
goto out;
if (pos < 0)
goto out;
ret = -ESPIPE;
if (!(file->f_mode & FMODE_PWRITE))
goto out;
ret = write(file, buf, count, &pos);
if (ret > 0)
dnotify_parent(file->f_dentry, DN_MODIFY);
out:
fput(file);
bad_file:
return ret;
} }
asmlinkage int sys32_sched_rr_get_interval(compat_pid_t pid, asmlinkage int sys32_sched_rr_get_interval(compat_pid_t pid,

View file

@ -279,7 +279,7 @@ static void unregister_cpu_online(unsigned int cpu)
} }
#endif /* CONFIG_HOTPLUG_CPU */ #endif /* CONFIG_HOTPLUG_CPU */
static int __devinit sysfs_cpu_notify(struct notifier_block *self, static int sysfs_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu) unsigned long action, void *hcpu)
{ {
unsigned int cpu = (unsigned int)(long)hcpu; unsigned int cpu = (unsigned int)(long)hcpu;
@ -297,7 +297,7 @@ static int __devinit sysfs_cpu_notify(struct notifier_block *self,
return NOTIFY_OK; return NOTIFY_OK;
} }
static struct notifier_block __devinitdata sysfs_cpu_nb = { static struct notifier_block sysfs_cpu_nb = {
.notifier_call = sysfs_cpu_notify, .notifier_call = sysfs_cpu_notify,
}; };

View file

@ -324,6 +324,7 @@ COMPAT_SYS(ppoll)
SYSCALL(unshare) SYSCALL(unshare)
SYSCALL(splice) SYSCALL(splice)
SYSCALL(tee) SYSCALL(tee)
SYSCALL(vmsplice)
/* /*
* please add new calls to arch/powerpc/platforms/cell/spu_callbacks.c * please add new calls to arch/powerpc/platforms/cell/spu_callbacks.c

View file

@ -318,6 +318,7 @@ void *spu_syscall_table[] = {
[__NR_unshare] sys_unshare, [__NR_unshare] sys_unshare,
[__NR_splice] sys_splice, [__NR_splice] sys_splice,
[__NR_tee] sys_tee, [__NR_tee] sys_tee,
[__NR_vmsplice] sys_vmsplice,
}; };
long spu_sys_callback(struct spu_syscall_block *s) long spu_sys_callback(struct spu_syscall_block *s)

View file

@ -652,7 +652,7 @@ appldata_cpu_notify(struct notifier_block *self,
return NOTIFY_OK; return NOTIFY_OK;
} }
static struct notifier_block __devinitdata appldata_nb = { static struct notifier_block appldata_nb = {
.notifier_call = appldata_cpu_notify, .notifier_call = appldata_cpu_notify,
}; };

View file

@ -629,7 +629,7 @@ static __cpuinit void mce_remove_device(unsigned int cpu)
#endif #endif
/* Get notified when a cpu comes on/off. Be hotplug friendly. */ /* Get notified when a cpu comes on/off. Be hotplug friendly. */
static __cpuinit int static int
mce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) mce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
{ {
unsigned int cpu = (unsigned long)hcpu; unsigned int cpu = (unsigned long)hcpu;

View file

@ -482,7 +482,7 @@ static void threshold_remove_device(unsigned int cpu)
#endif #endif
/* get notified when a cpu comes on/off */ /* get notified when a cpu comes on/off */
static __cpuinit int threshold_cpu_callback(struct notifier_block *nfb, static int threshold_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu) unsigned long action, void *hcpu)
{ {
/* cpu was unsigned int to begin with */ /* cpu was unsigned int to begin with */

View file

@ -3385,7 +3385,7 @@ static int blk_cpu_notify(struct notifier_block *self, unsigned long action,
} }
static struct notifier_block __devinitdata blk_cpu_notifier = { static struct notifier_block blk_cpu_notifier = {
.notifier_call = blk_cpu_notify, .notifier_call = blk_cpu_notify,
}; };

View file

@ -107,7 +107,7 @@ static int __cpuinit topology_remove_dev(struct sys_device * sys_dev)
return 0; return 0;
} }
static int __cpuinit topology_cpu_callback(struct notifier_block *nfb, static int topology_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu) unsigned long action, void *hcpu)
{ {
unsigned int cpu = (unsigned long)hcpu; unsigned int cpu = (unsigned long)hcpu;

View file

@ -27,6 +27,7 @@
#include <linux/crash_dump.h> #include <linux/crash_dump.h>
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
#include <linux/bootmem.h> #include <linux/bootmem.h>
#include <linux/pipe_fs_i.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/io.h> #include <asm/io.h>
@ -578,6 +579,18 @@ static ssize_t write_null(struct file * file, const char __user * buf,
return count; return count;
} }
static int pipe_to_null(struct pipe_inode_info *info, struct pipe_buffer *buf,
struct splice_desc *sd)
{
return sd->len;
}
static ssize_t splice_write_null(struct pipe_inode_info *pipe,struct file *out,
loff_t *ppos, size_t len, unsigned int flags)
{
return splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_null);
}
#ifdef CONFIG_MMU #ifdef CONFIG_MMU
/* /*
* For fun, we are using the MMU for this. * For fun, we are using the MMU for this.
@ -785,6 +798,7 @@ static struct file_operations null_fops = {
.llseek = null_lseek, .llseek = null_lseek,
.read = read_null, .read = read_null,
.write = write_null, .write = write_null,
.splice_write = splice_write_null,
}; };
#if defined(CONFIG_ISA) || !defined(__mc68000__) #if defined(CONFIG_ISA) || !defined(__mc68000__)

View file

@ -1497,7 +1497,7 @@ int cpufreq_update_policy(unsigned int cpu)
} }
EXPORT_SYMBOL(cpufreq_update_policy); EXPORT_SYMBOL(cpufreq_update_policy);
static int __cpuinit cpufreq_cpu_callback(struct notifier_block *nfb, static int cpufreq_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu) unsigned long action, void *hcpu)
{ {
unsigned int cpu = (unsigned long)hcpu; unsigned int cpu = (unsigned long)hcpu;

View file

@ -65,11 +65,6 @@ struct pxamci_host {
unsigned int dma_dir; unsigned int dma_dir;
}; };
static inline unsigned int ns_to_clocks(unsigned int ns)
{
return (ns * (CLOCKRATE / 1000000) + 999) / 1000;
}
static void pxamci_stop_clock(struct pxamci_host *host) static void pxamci_stop_clock(struct pxamci_host *host)
{ {
if (readl(host->base + MMC_STAT) & STAT_CLK_EN) { if (readl(host->base + MMC_STAT) & STAT_CLK_EN) {
@ -113,6 +108,7 @@ static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask)
static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
{ {
unsigned int nob = data->blocks; unsigned int nob = data->blocks;
unsigned long long clks;
unsigned int timeout; unsigned int timeout;
u32 dcmd; u32 dcmd;
int i; int i;
@ -125,7 +121,9 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
writel(nob, host->base + MMC_NOB); writel(nob, host->base + MMC_NOB);
writel(1 << data->blksz_bits, host->base + MMC_BLKLEN); writel(1 << data->blksz_bits, host->base + MMC_BLKLEN);
timeout = ns_to_clocks(data->timeout_ns) + data->timeout_clks; clks = (unsigned long long)data->timeout_ns * CLOCKRATE;
do_div(clks, 1000000000UL);
timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt);
writel((timeout + 255) / 256, host->base + MMC_RDTO); writel((timeout + 255) / 256, host->base + MMC_RDTO);
if (data->flags & MMC_DATA_READ) { if (data->flags & MMC_DATA_READ) {

View file

@ -1614,6 +1614,7 @@ static int activate_ep_files (struct dev_data *dev)
data, &ep_config_operations, data, &ep_config_operations,
&data->dentry); &data->dentry);
if (!data->inode) { if (!data->inode) {
usb_ep_free_request(ep, data->req);
kfree (data); kfree (data);
goto enomem; goto enomem;
} }

View file

@ -1217,6 +1217,10 @@ static ssize_t compat_do_readv_writev(int type, struct file *file,
if (ret < 0) if (ret < 0)
goto out; goto out;
ret = security_file_permission(file, type == READ ? MAY_READ:MAY_WRITE);
if (ret)
goto out;
fnv = NULL; fnv = NULL;
if (type == READ) { if (type == READ) {
fn = file->f_op->read; fn = file->f_op->read;

View file

@ -48,6 +48,7 @@ int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
if (!S_ISDIR(inode->i_mode)) if (!S_ISDIR(inode->i_mode))
flags &= ~EXT3_DIRSYNC_FL; flags &= ~EXT3_DIRSYNC_FL;
mutex_lock(&inode->i_mutex);
oldflags = ei->i_flags; oldflags = ei->i_flags;
/* The JOURNAL_DATA flag is modifiable only by root */ /* The JOURNAL_DATA flag is modifiable only by root */
@ -60,8 +61,10 @@ int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
* This test looks nicer. Thanks to Pauline Middelink * This test looks nicer. Thanks to Pauline Middelink
*/ */
if ((flags ^ oldflags) & (EXT3_APPEND_FL | EXT3_IMMUTABLE_FL)) { if ((flags ^ oldflags) & (EXT3_APPEND_FL | EXT3_IMMUTABLE_FL)) {
if (!capable(CAP_LINUX_IMMUTABLE)) if (!capable(CAP_LINUX_IMMUTABLE)) {
mutex_unlock(&inode->i_mutex);
return -EPERM; return -EPERM;
}
} }
/* /*
@ -69,14 +72,18 @@ int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
* the relevant capability. * the relevant capability.
*/ */
if ((jflag ^ oldflags) & (EXT3_JOURNAL_DATA_FL)) { if ((jflag ^ oldflags) & (EXT3_JOURNAL_DATA_FL)) {
if (!capable(CAP_SYS_RESOURCE)) if (!capable(CAP_SYS_RESOURCE)) {
mutex_unlock(&inode->i_mutex);
return -EPERM; return -EPERM;
}
} }
handle = ext3_journal_start(inode, 1); handle = ext3_journal_start(inode, 1);
if (IS_ERR(handle)) if (IS_ERR(handle)) {
mutex_unlock(&inode->i_mutex);
return PTR_ERR(handle); return PTR_ERR(handle);
}
if (IS_SYNC(inode)) if (IS_SYNC(inode))
handle->h_sync = 1; handle->h_sync = 1;
err = ext3_reserve_inode_write(handle, inode, &iloc); err = ext3_reserve_inode_write(handle, inode, &iloc);
@ -93,11 +100,14 @@ int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
err = ext3_mark_iloc_dirty(handle, inode, &iloc); err = ext3_mark_iloc_dirty(handle, inode, &iloc);
flags_err: flags_err:
ext3_journal_stop(handle); ext3_journal_stop(handle);
if (err) if (err) {
mutex_unlock(&inode->i_mutex);
return err; return err;
}
if ((jflag ^ oldflags) & (EXT3_JOURNAL_DATA_FL)) if ((jflag ^ oldflags) & (EXT3_JOURNAL_DATA_FL))
err = ext3_change_inode_journal_flag(inode, jflag); err = ext3_change_inode_journal_flag(inode, jflag);
mutex_unlock(&inode->i_mutex);
return err; return err;
} }
case EXT3_IOC_GETVERSION: case EXT3_IOC_GETVERSION:

View file

@ -213,7 +213,7 @@ static int setup_new_group_blocks(struct super_block *sb,
goto exit_bh; goto exit_bh;
} }
lock_buffer(bh); lock_buffer(bh);
memcpy(gdb->b_data, sbi->s_group_desc[i], bh->b_size); memcpy(gdb->b_data, sbi->s_group_desc[i]->b_data, bh->b_size);
set_buffer_uptodate(gdb); set_buffer_uptodate(gdb);
unlock_buffer(bh); unlock_buffer(bh);
ext3_journal_dirty_metadata(handle, gdb); ext3_journal_dirty_metadata(handle, gdb);

View file

@ -128,14 +128,24 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
} }
} }
void fuse_remove_background(struct fuse_conn *fc, struct fuse_req *req) /*
* Called with sbput_sem held for read (request_end) or write
* (fuse_put_super). By the time fuse_put_super() is finished, all
* inodes belonging to background requests must be released, so the
* iputs have to be done within the locked region.
*/
void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req)
{ {
list_del_init(&req->bg_entry); iput(req->inode);
iput(req->inode2);
spin_lock(&fc->lock);
list_del(&req->bg_entry);
if (fc->num_background == FUSE_MAX_BACKGROUND) { if (fc->num_background == FUSE_MAX_BACKGROUND) {
fc->blocked = 0; fc->blocked = 0;
wake_up_all(&fc->blocked_waitq); wake_up_all(&fc->blocked_waitq);
} }
fc->num_background--; fc->num_background--;
spin_unlock(&fc->lock);
} }
/* /*
@ -165,27 +175,22 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
wake_up(&req->waitq); wake_up(&req->waitq);
fuse_put_request(fc, req); fuse_put_request(fc, req);
} else { } else {
struct inode *inode = req->inode;
struct inode *inode2 = req->inode2;
struct file *file = req->file;
void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; void (*end) (struct fuse_conn *, struct fuse_req *) = req->end;
req->end = NULL; req->end = NULL;
req->inode = NULL;
req->inode2 = NULL;
req->file = NULL;
if (!list_empty(&req->bg_entry))
fuse_remove_background(fc, req);
spin_unlock(&fc->lock); spin_unlock(&fc->lock);
down_read(&fc->sbput_sem);
if (fc->mounted)
fuse_release_background(fc, req);
up_read(&fc->sbput_sem);
/* fput must go outside sbput_sem, otherwise it can deadlock */
if (req->file)
fput(req->file);
if (end) if (end)
end(fc, req); end(fc, req);
else else
fuse_put_request(fc, req); fuse_put_request(fc, req);
if (file)
fput(file);
iput(inode);
iput(inode2);
} }
} }

View file

@ -258,9 +258,15 @@ struct fuse_conn {
/** waitq for blocked connection */ /** waitq for blocked connection */
wait_queue_head_t blocked_waitq; wait_queue_head_t blocked_waitq;
/** RW semaphore for exclusion with fuse_put_super() */
struct rw_semaphore sbput_sem;
/** The next unique request id */ /** The next unique request id */
u64 reqctr; u64 reqctr;
/** Mount is active */
unsigned mounted;
/** Connection established, cleared on umount, connection /** Connection established, cleared on umount, connection
abort and device release */ abort and device release */
unsigned connected; unsigned connected;
@ -471,11 +477,11 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req);
void request_send_background(struct fuse_conn *fc, struct fuse_req *req); void request_send_background(struct fuse_conn *fc, struct fuse_req *req);
/** /**
* Remove request from the the background list * Release inodes and file associated with background request
*/ */
void fuse_remove_background(struct fuse_conn *fc, struct fuse_req *req); void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req);
/** Abort all requests */ /* Abort all requests */
void fuse_abort_conn(struct fuse_conn *fc); void fuse_abort_conn(struct fuse_conn *fc);
/** /**

View file

@ -204,26 +204,17 @@ static void fuse_put_super(struct super_block *sb)
{ {
struct fuse_conn *fc = get_fuse_conn_super(sb); struct fuse_conn *fc = get_fuse_conn_super(sb);
down_write(&fc->sbput_sem);
while (!list_empty(&fc->background))
fuse_release_background(fc,
list_entry(fc->background.next,
struct fuse_req, bg_entry));
spin_lock(&fc->lock); spin_lock(&fc->lock);
fc->mounted = 0;
fc->connected = 0; fc->connected = 0;
while (!list_empty(&fc->background)) {
struct fuse_req *req = list_entry(fc->background.next,
struct fuse_req, bg_entry);
struct inode *inode = req->inode;
struct inode *inode2 = req->inode2;
/* File would hold a reference to vfsmount */
BUG_ON(req->file);
req->inode = NULL;
req->inode2 = NULL;
fuse_remove_background(fc, req);
spin_unlock(&fc->lock);
iput(inode);
iput(inode2);
spin_lock(&fc->lock);
}
spin_unlock(&fc->lock); spin_unlock(&fc->lock);
up_write(&fc->sbput_sem);
/* Flush all readers on this fs */ /* Flush all readers on this fs */
kill_fasync(&fc->fasync, SIGIO, POLL_IN); kill_fasync(&fc->fasync, SIGIO, POLL_IN);
wake_up_all(&fc->waitq); wake_up_all(&fc->waitq);
@ -395,6 +386,7 @@ static struct fuse_conn *new_conn(void)
INIT_LIST_HEAD(&fc->processing); INIT_LIST_HEAD(&fc->processing);
INIT_LIST_HEAD(&fc->io); INIT_LIST_HEAD(&fc->io);
INIT_LIST_HEAD(&fc->background); INIT_LIST_HEAD(&fc->background);
init_rwsem(&fc->sbput_sem);
kobj_set_kset_s(fc, connections_subsys); kobj_set_kset_s(fc, connections_subsys);
kobject_init(&fc->kobj); kobject_init(&fc->kobj);
atomic_set(&fc->num_waiting, 0); atomic_set(&fc->num_waiting, 0);
@ -508,11 +500,6 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
if (file->f_op != &fuse_dev_operations) if (file->f_op != &fuse_dev_operations)
return -EINVAL; return -EINVAL;
/* Setting file->private_data can't race with other mount()
instances, since BKL is held for ->get_sb() */
if (file->private_data)
return -EINVAL;
fc = new_conn(); fc = new_conn();
if (!fc) if (!fc)
return -ENOMEM; return -ENOMEM;
@ -548,7 +535,14 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
if (err) if (err)
goto err_free_req; goto err_free_req;
/* Setting file->private_data can't race with other mount()
instances, since BKL is held for ->get_sb() */
err = -EINVAL;
if (file->private_data)
goto err_kobject_del;
sb->s_root = root_dentry; sb->s_root = root_dentry;
fc->mounted = 1;
fc->connected = 1; fc->connected = 1;
kobject_get(&fc->kobj); kobject_get(&fc->kobj);
file->private_data = fc; file->private_data = fc;
@ -563,6 +557,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
return 0; return 0;
err_kobject_del:
kobject_del(&fc->kobj);
err_free_req: err_free_req:
fuse_request_free(init_req); fuse_request_free(init_req);
err_put_root: err_put_root:

View file

@ -27,15 +27,22 @@
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/uio.h>
struct partial_page {
unsigned int offset;
unsigned int len;
};
/* /*
* Passed to the actors * Passed to splice_to_pipe
*/ */
struct splice_desc { struct splice_pipe_desc {
unsigned int len, total_len; /* current and remaining length */ struct page **pages; /* page map */
struct partial_page *partial; /* pages[] may not be contig */
int nr_pages; /* number of pages in map */
unsigned int flags; /* splice flags */ unsigned int flags; /* splice flags */
struct file *file; /* file to read/write */ struct pipe_buf_operations *ops;/* ops associated with output pipe */
loff_t pos; /* file position */
}; };
/* /*
@ -128,6 +135,19 @@ static void page_cache_pipe_buf_unmap(struct pipe_inode_info *info,
kunmap(buf->page); kunmap(buf->page);
} }
static void *user_page_pipe_buf_map(struct file *file,
struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
return kmap(buf->page);
}
static void user_page_pipe_buf_unmap(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
kunmap(buf->page);
}
static void page_cache_pipe_buf_get(struct pipe_inode_info *info, static void page_cache_pipe_buf_get(struct pipe_inode_info *info,
struct pipe_buffer *buf) struct pipe_buffer *buf)
{ {
@ -143,19 +163,33 @@ static struct pipe_buf_operations page_cache_pipe_buf_ops = {
.get = page_cache_pipe_buf_get, .get = page_cache_pipe_buf_get,
}; };
static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
return 1;
}
static struct pipe_buf_operations user_page_pipe_buf_ops = {
.can_merge = 0,
.map = user_page_pipe_buf_map,
.unmap = user_page_pipe_buf_unmap,
.release = page_cache_pipe_buf_release,
.steal = user_page_pipe_buf_steal,
.get = page_cache_pipe_buf_get,
};
/* /*
* Pipe output worker. This sets up our pipe format with the page cache * Pipe output worker. This sets up our pipe format with the page cache
* pipe buffer operations. Otherwise very similar to the regular pipe_writev(). * pipe buffer operations. Otherwise very similar to the regular pipe_writev().
*/ */
static ssize_t move_to_pipe(struct pipe_inode_info *pipe, struct page **pages, static ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
int nr_pages, unsigned long len, struct splice_pipe_desc *spd)
unsigned int offset, unsigned int flags)
{ {
int ret, do_wakeup, i; int ret, do_wakeup, page_nr;
ret = 0; ret = 0;
do_wakeup = 0; do_wakeup = 0;
i = 0; page_nr = 0;
if (pipe->inode) if (pipe->inode)
mutex_lock(&pipe->inode->i_mutex); mutex_lock(&pipe->inode->i_mutex);
@ -171,27 +205,19 @@ static ssize_t move_to_pipe(struct pipe_inode_info *pipe, struct page **pages,
if (pipe->nrbufs < PIPE_BUFFERS) { if (pipe->nrbufs < PIPE_BUFFERS) {
int newbuf = (pipe->curbuf + pipe->nrbufs) & (PIPE_BUFFERS - 1); int newbuf = (pipe->curbuf + pipe->nrbufs) & (PIPE_BUFFERS - 1);
struct pipe_buffer *buf = pipe->bufs + newbuf; struct pipe_buffer *buf = pipe->bufs + newbuf;
struct page *page = pages[i++];
unsigned long this_len;
this_len = PAGE_CACHE_SIZE - offset; buf->page = spd->pages[page_nr];
if (this_len > len) buf->offset = spd->partial[page_nr].offset;
this_len = len; buf->len = spd->partial[page_nr].len;
buf->ops = spd->ops;
buf->page = page;
buf->offset = offset;
buf->len = this_len;
buf->ops = &page_cache_pipe_buf_ops;
pipe->nrbufs++; pipe->nrbufs++;
page_nr++;
ret += buf->len;
if (pipe->inode) if (pipe->inode)
do_wakeup = 1; do_wakeup = 1;
ret += this_len; if (!--spd->nr_pages)
len -= this_len;
offset = 0;
if (!--nr_pages)
break;
if (!len)
break; break;
if (pipe->nrbufs < PIPE_BUFFERS) if (pipe->nrbufs < PIPE_BUFFERS)
continue; continue;
@ -199,7 +225,7 @@ static ssize_t move_to_pipe(struct pipe_inode_info *pipe, struct page **pages,
break; break;
} }
if (flags & SPLICE_F_NONBLOCK) { if (spd->flags & SPLICE_F_NONBLOCK) {
if (!ret) if (!ret)
ret = -EAGAIN; ret = -EAGAIN;
break; break;
@ -234,8 +260,8 @@ static ssize_t move_to_pipe(struct pipe_inode_info *pipe, struct page **pages,
kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN); kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
} }
while (i < nr_pages) while (page_nr < spd->nr_pages)
page_cache_release(pages[i++]); page_cache_release(spd->pages[page_nr++]);
return ret; return ret;
} }
@ -246,17 +272,24 @@ __generic_file_splice_read(struct file *in, loff_t *ppos,
unsigned int flags) unsigned int flags)
{ {
struct address_space *mapping = in->f_mapping; struct address_space *mapping = in->f_mapping;
unsigned int loff, offset, nr_pages; unsigned int loff, nr_pages;
struct page *pages[PIPE_BUFFERS]; struct page *pages[PIPE_BUFFERS];
struct partial_page partial[PIPE_BUFFERS];
struct page *page; struct page *page;
pgoff_t index, end_index; pgoff_t index, end_index;
loff_t isize; loff_t isize;
size_t bytes; size_t total_len;
int i, error; int error;
struct splice_pipe_desc spd = {
.pages = pages,
.partial = partial,
.flags = flags,
.ops = &page_cache_pipe_buf_ops,
};
index = *ppos >> PAGE_CACHE_SHIFT; index = *ppos >> PAGE_CACHE_SHIFT;
loff = offset = *ppos & ~PAGE_CACHE_MASK; loff = *ppos & ~PAGE_CACHE_MASK;
nr_pages = (len + offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; nr_pages = (len + loff + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
if (nr_pages > PIPE_BUFFERS) if (nr_pages > PIPE_BUFFERS)
nr_pages = PIPE_BUFFERS; nr_pages = PIPE_BUFFERS;
@ -266,15 +299,15 @@ __generic_file_splice_read(struct file *in, loff_t *ppos,
* read-ahead if this is a non-zero offset (we are likely doing small * read-ahead if this is a non-zero offset (we are likely doing small
* chunk splice and the page is already there) for a single page. * chunk splice and the page is already there) for a single page.
*/ */
if (!offset || nr_pages > 1) if (!loff || spd.nr_pages > 1)
do_page_cache_readahead(mapping, in, index, nr_pages); do_page_cache_readahead(mapping, in, index, spd.nr_pages);
/* /*
* Now fill in the holes: * Now fill in the holes:
*/ */
error = 0; error = 0;
bytes = 0; total_len = 0;
for (i = 0; i < nr_pages; i++, index++) { for (spd.nr_pages = 0; spd.nr_pages < nr_pages; spd.nr_pages++, index++) {
unsigned int this_len; unsigned int this_len;
if (!len) if (!len)
@ -283,7 +316,7 @@ __generic_file_splice_read(struct file *in, loff_t *ppos,
/* /*
* this_len is the max we'll use from this page * this_len is the max we'll use from this page
*/ */
this_len = min(len, PAGE_CACHE_SIZE - loff); this_len = min_t(unsigned long, len, PAGE_CACHE_SIZE - loff);
find_page: find_page:
/* /*
* lookup the page for this index * lookup the page for this index
@ -367,26 +400,29 @@ readpage:
*/ */
if (end_index == index) { if (end_index == index) {
loff = PAGE_CACHE_SIZE - (isize & ~PAGE_CACHE_MASK); loff = PAGE_CACHE_SIZE - (isize & ~PAGE_CACHE_MASK);
if (bytes + loff > isize) { if (total_len + loff > isize) {
page_cache_release(page); page_cache_release(page);
break; break;
} }
/* /*
* force quit after adding this page * force quit after adding this page
*/ */
nr_pages = i; nr_pages = spd.nr_pages;
this_len = min(this_len, loff); this_len = min(this_len, loff);
loff = 0;
} }
} }
fill_it: fill_it:
pages[i] = page; pages[spd.nr_pages] = page;
bytes += this_len; partial[spd.nr_pages].offset = loff;
partial[spd.nr_pages].len = this_len;
len -= this_len; len -= this_len;
total_len += this_len;
loff = 0; loff = 0;
} }
if (i) if (spd.nr_pages)
return move_to_pipe(pipe, pages, i, bytes, offset, flags); return splice_to_pipe(pipe, &spd);
return error; return error;
} }
@ -439,14 +475,13 @@ EXPORT_SYMBOL(generic_file_splice_read);
/* /*
* Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos' * Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos'
* using sendpage(). * using sendpage(). Return the number of bytes sent.
*/ */
static int pipe_to_sendpage(struct pipe_inode_info *info, static int pipe_to_sendpage(struct pipe_inode_info *info,
struct pipe_buffer *buf, struct splice_desc *sd) struct pipe_buffer *buf, struct splice_desc *sd)
{ {
struct file *file = sd->file; struct file *file = sd->file;
loff_t pos = sd->pos; loff_t pos = sd->pos;
unsigned int offset;
ssize_t ret; ssize_t ret;
void *ptr; void *ptr;
int more; int more;
@ -461,16 +496,13 @@ static int pipe_to_sendpage(struct pipe_inode_info *info,
if (IS_ERR(ptr)) if (IS_ERR(ptr))
return PTR_ERR(ptr); return PTR_ERR(ptr);
offset = pos & ~PAGE_CACHE_MASK;
more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len; more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len;
ret = file->f_op->sendpage(file, buf->page, offset, sd->len, &pos,more); ret = file->f_op->sendpage(file, buf->page, buf->offset, sd->len,
&pos, more);
buf->ops->unmap(info, buf); buf->ops->unmap(info, buf);
if (ret == sd->len) return ret;
return 0;
return -EIO;
} }
/* /*
@ -499,7 +531,7 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
struct file *file = sd->file; struct file *file = sd->file;
struct address_space *mapping = file->f_mapping; struct address_space *mapping = file->f_mapping;
gfp_t gfp_mask = mapping_gfp_mask(mapping); gfp_t gfp_mask = mapping_gfp_mask(mapping);
unsigned int offset; unsigned int offset, this_len;
struct page *page; struct page *page;
pgoff_t index; pgoff_t index;
char *src; char *src;
@ -515,6 +547,10 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
index = sd->pos >> PAGE_CACHE_SHIFT; index = sd->pos >> PAGE_CACHE_SHIFT;
offset = sd->pos & ~PAGE_CACHE_MASK; offset = sd->pos & ~PAGE_CACHE_MASK;
this_len = sd->len;
if (this_len + offset > PAGE_CACHE_SIZE)
this_len = PAGE_CACHE_SIZE - offset;
/* /*
* Reuse buf page, if SPLICE_F_MOVE is set. * Reuse buf page, if SPLICE_F_MOVE is set.
*/ */
@ -558,7 +594,7 @@ find_page:
* the full page. * the full page.
*/ */
if (!PageUptodate(page)) { if (!PageUptodate(page)) {
if (sd->len < PAGE_CACHE_SIZE) { if (this_len < PAGE_CACHE_SIZE) {
ret = mapping->a_ops->readpage(file, page); ret = mapping->a_ops->readpage(file, page);
if (unlikely(ret)) if (unlikely(ret))
goto out; goto out;
@ -582,7 +618,7 @@ find_page:
} }
} }
ret = mapping->a_ops->prepare_write(file, page, 0, sd->len); ret = mapping->a_ops->prepare_write(file, page, offset, offset+this_len);
if (ret == AOP_TRUNCATED_PAGE) { if (ret == AOP_TRUNCATED_PAGE) {
page_cache_release(page); page_cache_release(page);
goto find_page; goto find_page;
@ -592,18 +628,22 @@ find_page:
if (!(buf->flags & PIPE_BUF_FLAG_STOLEN)) { if (!(buf->flags & PIPE_BUF_FLAG_STOLEN)) {
char *dst = kmap_atomic(page, KM_USER0); char *dst = kmap_atomic(page, KM_USER0);
memcpy(dst + offset, src + buf->offset, sd->len); memcpy(dst + offset, src + buf->offset, this_len);
flush_dcache_page(page); flush_dcache_page(page);
kunmap_atomic(dst, KM_USER0); kunmap_atomic(dst, KM_USER0);
} }
ret = mapping->a_ops->commit_write(file, page, 0, sd->len); ret = mapping->a_ops->commit_write(file, page, offset, offset+this_len);
if (ret == AOP_TRUNCATED_PAGE) { if (ret == AOP_TRUNCATED_PAGE) {
page_cache_release(page); page_cache_release(page);
goto find_page; goto find_page;
} else if (ret) } else if (ret)
goto out; goto out;
/*
* Return the number of bytes written.
*/
ret = this_len;
mark_page_accessed(page); mark_page_accessed(page);
balance_dirty_pages_ratelimited(mapping); balance_dirty_pages_ratelimited(mapping);
out: out:
@ -616,17 +656,14 @@ out_nomem:
return ret; return ret;
} }
typedef int (splice_actor)(struct pipe_inode_info *, struct pipe_buffer *,
struct splice_desc *);
/* /*
* Pipe input worker. Most of this logic works like a regular pipe, the * Pipe input worker. Most of this logic works like a regular pipe, the
* key here is the 'actor' worker passed in that actually moves the data * key here is the 'actor' worker passed in that actually moves the data
* to the wanted destination. See pipe_to_file/pipe_to_sendpage above. * to the wanted destination. See pipe_to_file/pipe_to_sendpage above.
*/ */
static ssize_t move_from_pipe(struct pipe_inode_info *pipe, struct file *out, ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out,
loff_t *ppos, size_t len, unsigned int flags, loff_t *ppos, size_t len, unsigned int flags,
splice_actor *actor) splice_actor *actor)
{ {
int ret, do_wakeup, err; int ret, do_wakeup, err;
struct splice_desc sd; struct splice_desc sd;
@ -652,16 +689,22 @@ static ssize_t move_from_pipe(struct pipe_inode_info *pipe, struct file *out,
sd.len = sd.total_len; sd.len = sd.total_len;
err = actor(pipe, buf, &sd); err = actor(pipe, buf, &sd);
if (err) { if (err <= 0) {
if (!ret && err != -ENODATA) if (!ret && err != -ENODATA)
ret = err; ret = err;
break; break;
} }
ret += sd.len; ret += err;
buf->offset += sd.len; buf->offset += err;
buf->len -= sd.len; buf->len -= err;
sd.len -= err;
sd.pos += err;
sd.total_len -= err;
if (sd.len)
continue;
if (!buf->len) { if (!buf->len) {
buf->ops = NULL; buf->ops = NULL;
@ -672,8 +715,6 @@ static ssize_t move_from_pipe(struct pipe_inode_info *pipe, struct file *out,
do_wakeup = 1; do_wakeup = 1;
} }
sd.pos += sd.len;
sd.total_len -= sd.len;
if (!sd.total_len) if (!sd.total_len)
break; break;
} }
@ -741,7 +782,7 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
struct address_space *mapping = out->f_mapping; struct address_space *mapping = out->f_mapping;
ssize_t ret; ssize_t ret;
ret = move_from_pipe(pipe, out, ppos, len, flags, pipe_to_file); ret = splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_file);
if (ret > 0) { if (ret > 0) {
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
@ -783,7 +824,7 @@ EXPORT_SYMBOL(generic_file_splice_write);
ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe, struct file *out, ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe, struct file *out,
loff_t *ppos, size_t len, unsigned int flags) loff_t *ppos, size_t len, unsigned int flags)
{ {
return move_from_pipe(pipe, out, ppos, len, flags, pipe_to_sendpage); return splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_sendpage);
} }
EXPORT_SYMBOL(generic_splice_sendpage); EXPORT_SYMBOL(generic_splice_sendpage);
@ -870,7 +911,7 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
/* /*
* We don't have an immediate reader, but we'll read the stuff * We don't have an immediate reader, but we'll read the stuff
* out of the pipe right after the move_to_pipe(). So set * out of the pipe right after the splice_to_pipe(). So set
* PIPE_READERS appropriately. * PIPE_READERS appropriately.
*/ */
pipe->readers = 1; pipe->readers = 1;
@ -1010,6 +1051,174 @@ static long do_splice(struct file *in, loff_t __user *off_in,
return -EINVAL; return -EINVAL;
} }
/*
* Map an iov into an array of pages and offset/length tupples. With the
* partial_page structure, we can map several non-contiguous ranges into
* our ones pages[] map instead of splitting that operation into pieces.
* Could easily be exported as a generic helper for other users, in which
* case one would probably want to add a 'max_nr_pages' parameter as well.
*/
static int get_iovec_page_array(const struct iovec __user *iov,
unsigned int nr_vecs, struct page **pages,
struct partial_page *partial)
{
int buffers = 0, error = 0;
/*
* It's ok to take the mmap_sem for reading, even
* across a "get_user()".
*/
down_read(&current->mm->mmap_sem);
while (nr_vecs) {
unsigned long off, npages;
void __user *base;
size_t len;
int i;
/*
* 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;
error = -EFAULT;
if (unlikely(!base))
break;
/*
* Get this base offset and number of pages, then map
* in the user pages.
*/
off = (unsigned long) base & ~PAGE_MASK;
npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
if (npages > PIPE_BUFFERS - buffers)
npages = PIPE_BUFFERS - buffers;
error = get_user_pages(current, current->mm,
(unsigned long) base, npages, 0, 0,
&pages[buffers], NULL);
if (unlikely(error <= 0))
break;
/*
* Fill this contiguous range into the partial page map.
*/
for (i = 0; i < error; i++) {
const int plen = min_t(size_t, len, PAGE_SIZE) - off;
partial[buffers].offset = off;
partial[buffers].len = plen;
off = 0;
len -= plen;
buffers++;
}
/*
* We didn't complete this iov, stop here since it probably
* means we have to move some of this into a pipe to
* be able to continue.
*/
if (len)
break;
/*
* Don't continue if we mapped fewer pages than we asked for,
* or if we mapped the max number of pages that we have
* room for.
*/
if (error < npages || buffers == PIPE_BUFFERS)
break;
nr_vecs--;
iov++;
}
up_read(&current->mm->mmap_sem);
if (buffers)
return buffers;
return error;
}
/*
* vmsplice splices a user address range into a pipe. It can be thought of
* as splice-from-memory, where the regular splice is splice-from-file (or
* to file). In both cases the output is a pipe, naturally.
*
* Note that vmsplice only supports splicing _from_ user memory to a pipe,
* not the other way around. Splicing from user memory is a simple operation
* that can be supported without any funky alignment restrictions or nasty
* vm tricks. We simply map in the user memory and fill them into a pipe.
* The reverse isn't quite as easy, though. There are two possible solutions
* for that:
*
* - memcpy() the data internally, at which point we might as well just
* do a regular read() on the buffer anyway.
* - Lots of nasty vm tricks, that are neither fast nor flexible (it
* has restriction limitations on both ends of the pipe).
*
* Alas, it isn't here.
*
*/
static long do_vmsplice(struct file *file, const struct iovec __user *iov,
unsigned long nr_segs, unsigned int flags)
{
struct pipe_inode_info *pipe = file->f_dentry->d_inode->i_pipe;
struct page *pages[PIPE_BUFFERS];
struct partial_page partial[PIPE_BUFFERS];
struct splice_pipe_desc spd = {
.pages = pages,
.partial = partial,
.flags = flags,
.ops = &user_page_pipe_buf_ops,
};
if (unlikely(!pipe))
return -EBADF;
if (unlikely(nr_segs > UIO_MAXIOV))
return -EINVAL;
else if (unlikely(!nr_segs))
return 0;
spd.nr_pages = get_iovec_page_array(iov, nr_segs, pages, partial);
if (spd.nr_pages <= 0)
return spd.nr_pages;
return splice_to_pipe(pipe, &spd);
}
asmlinkage long sys_vmsplice(int fd, const struct iovec __user *iov,
unsigned long nr_segs, unsigned int flags)
{
struct file *file;
long error;
int fput;
error = -EBADF;
file = fget_light(fd, &fput);
if (file) {
if (file->f_mode & FMODE_WRITE)
error = do_vmsplice(file, iov, nr_segs, flags);
fput_light(file, fput);
}
return error;
}
asmlinkage long sys_splice(int fd_in, loff_t __user *off_in, asmlinkage long sys_splice(int fd_in, loff_t __user *off_in,
int fd_out, loff_t __user *off_out, int fd_out, loff_t __user *off_out,
size_t len, unsigned int flags) size_t len, unsigned int flags)

View file

@ -321,8 +321,9 @@
#define __NR_splice 313 #define __NR_splice 313
#define __NR_sync_file_range 314 #define __NR_sync_file_range 314
#define __NR_tee 315 #define __NR_tee 315
#define __NR_vmsplice 316
#define NR_syscalls 316 #define NR_syscalls 317
/* /*
* user-visible error numbers are in the range -1 - -128: see * user-visible error numbers are in the range -1 - -128: see

View file

@ -290,12 +290,13 @@
#define __NR_get_robust_list 1299 #define __NR_get_robust_list 1299
#define __NR_sync_file_range 1300 #define __NR_sync_file_range 1300
#define __NR_tee 1301 #define __NR_tee 1301
#define __NR_vmsplice 1302
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/config.h> #include <linux/config.h>
#define NR_syscalls 278 /* length of syscall table */ #define NR_syscalls 279 /* length of syscall table */
#define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_RT_SIGACTION

View file

@ -303,8 +303,9 @@
#define __NR_unshare 282 #define __NR_unshare 282
#define __NR_splice 283 #define __NR_splice 283
#define __NR_tee 284 #define __NR_tee 284
#define __NR_vmsplice 285
#define __NR_syscalls 285 #define __NR_syscalls 286
#ifdef __KERNEL__ #ifdef __KERNEL__
#define __NR__exit __NR_exit #define __NR__exit __NR_exit

View file

@ -615,8 +615,10 @@ __SYSCALL(__NR_splice, sys_splice)
__SYSCALL(__NR_tee, sys_tee) __SYSCALL(__NR_tee, sys_tee)
#define __NR_sync_file_range 277 #define __NR_sync_file_range 277
__SYSCALL(__NR_sync_file_range, sys_sync_file_range) __SYSCALL(__NR_sync_file_range, sys_sync_file_range)
#define __NR_vmsplice 278
__SYSCALL(__NR_vmsplice, sys_vmsplice)
#define __NR_syscall_max __NR_sync_file_range #define __NR_syscall_max __NR_vmsplice
#ifndef __NO_STUBS #ifndef __NO_STUBS

View file

@ -61,4 +61,21 @@ void __free_pipe_info(struct pipe_inode_info *);
/* from/to, of course */ /* from/to, of course */
#define SPLICE_F_MORE (0x04) /* expect more data */ #define SPLICE_F_MORE (0x04) /* expect more data */
/*
* Passed to the actors
*/
struct splice_desc {
unsigned int len, total_len; /* current and remaining length */
unsigned int flags; /* splice flags */
struct file *file; /* file to read/write */
loff_t pos; /* file position */
};
typedef int (splice_actor)(struct pipe_inode_info *, struct pipe_buffer *,
struct splice_desc *);
extern ssize_t splice_from_pipe(struct pipe_inode_info *, struct file *,
loff_t *, size_t, unsigned int,
splice_actor *);
#endif #endif

View file

@ -574,6 +574,9 @@ asmlinkage long sys_splice(int fd_in, loff_t __user *off_in,
int fd_out, loff_t __user *off_out, int fd_out, loff_t __user *off_out,
size_t len, unsigned int flags); size_t len, unsigned int flags);
asmlinkage long sys_vmsplice(int fd, const struct iovec __user *iov,
unsigned long nr_segs, unsigned int flags);
asmlinkage long sys_tee(int fdin, int fdout, size_t len, unsigned int flags); asmlinkage long sys_tee(int fdin, int fdout, size_t len, unsigned int flags);
asmlinkage long sys_sync_file_range(int fd, loff_t offset, loff_t nbytes, asmlinkage long sys_sync_file_range(int fd, loff_t offset, loff_t nbytes,

View file

@ -836,7 +836,7 @@ static void migrate_hrtimers(int cpu)
} }
#endif /* CONFIG_HOTPLUG_CPU */ #endif /* CONFIG_HOTPLUG_CPU */
static int __devinit hrtimer_cpu_notify(struct notifier_block *self, static int hrtimer_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu) unsigned long action, void *hcpu)
{ {
long cpu = (long)hcpu; long cpu = (long)hcpu;
@ -860,7 +860,7 @@ static int __devinit hrtimer_cpu_notify(struct notifier_block *self,
return NOTIFY_OK; return NOTIFY_OK;
} }
static struct notifier_block __devinitdata hrtimers_nb = { static struct notifier_block hrtimers_nb = {
.notifier_call = hrtimer_cpu_notify, .notifier_call = hrtimer_cpu_notify,
}; };

View file

@ -299,7 +299,7 @@ out:
} }
#ifdef CONFIG_HOTPLUG_CPU #ifdef CONFIG_HOTPLUG_CPU
static int __devinit profile_cpu_callback(struct notifier_block *info, static int profile_cpu_callback(struct notifier_block *info,
unsigned long action, void *__cpu) unsigned long action, void *__cpu)
{ {
int node, cpu = (unsigned long)__cpu; int node, cpu = (unsigned long)__cpu;

View file

@ -520,7 +520,7 @@ static void __devinit rcu_online_cpu(int cpu)
tasklet_init(&per_cpu(rcu_tasklet, cpu), rcu_process_callbacks, 0UL); tasklet_init(&per_cpu(rcu_tasklet, cpu), rcu_process_callbacks, 0UL);
} }
static int __devinit rcu_cpu_notify(struct notifier_block *self, static int rcu_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu) unsigned long action, void *hcpu)
{ {
long cpu = (long)hcpu; long cpu = (long)hcpu;
@ -537,7 +537,7 @@ static int __devinit rcu_cpu_notify(struct notifier_block *self,
return NOTIFY_OK; return NOTIFY_OK;
} }
static struct notifier_block __devinitdata rcu_nb = { static struct notifier_block rcu_nb = {
.notifier_call = rcu_cpu_notify, .notifier_call = rcu_cpu_notify,
}; };

View file

@ -4814,7 +4814,7 @@ static int migration_call(struct notifier_block *nfb, unsigned long action,
/* Register at highest priority so that task migration (migrate_all_tasks) /* Register at highest priority so that task migration (migrate_all_tasks)
* happens before everything else. * happens before everything else.
*/ */
static struct notifier_block __devinitdata migration_notifier = { static struct notifier_block migration_notifier = {
.notifier_call = migration_call, .notifier_call = migration_call,
.priority = 10 .priority = 10
}; };

View file

@ -446,7 +446,7 @@ static void takeover_tasklets(unsigned int cpu)
} }
#endif /* CONFIG_HOTPLUG_CPU */ #endif /* CONFIG_HOTPLUG_CPU */
static int __devinit cpu_callback(struct notifier_block *nfb, static int cpu_callback(struct notifier_block *nfb,
unsigned long action, unsigned long action,
void *hcpu) void *hcpu)
{ {
@ -484,7 +484,7 @@ static int __devinit cpu_callback(struct notifier_block *nfb,
return NOTIFY_OK; return NOTIFY_OK;
} }
static struct notifier_block __devinitdata cpu_nfb = { static struct notifier_block cpu_nfb = {
.notifier_call = cpu_callback .notifier_call = cpu_callback
}; };

View file

@ -104,7 +104,7 @@ static int watchdog(void * __bind_cpu)
/* /*
* Create/destroy watchdog threads as CPUs come and go: * Create/destroy watchdog threads as CPUs come and go:
*/ */
static int __devinit static int
cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
{ {
int hotcpu = (unsigned long)hcpu; int hotcpu = (unsigned long)hcpu;
@ -140,7 +140,7 @@ cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
return NOTIFY_OK; return NOTIFY_OK;
} }
static struct notifier_block __devinitdata cpu_nfb = { static struct notifier_block cpu_nfb = {
.notifier_call = cpu_callback .notifier_call = cpu_callback
}; };

View file

@ -1314,7 +1314,7 @@ static void __devinit migrate_timers(int cpu)
} }
#endif /* CONFIG_HOTPLUG_CPU */ #endif /* CONFIG_HOTPLUG_CPU */
static int __devinit timer_cpu_notify(struct notifier_block *self, static int timer_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu) unsigned long action, void *hcpu)
{ {
long cpu = (long)hcpu; long cpu = (long)hcpu;
@ -1334,7 +1334,7 @@ static int __devinit timer_cpu_notify(struct notifier_block *self,
return NOTIFY_OK; return NOTIFY_OK;
} }
static struct notifier_block __devinitdata timers_nb = { static struct notifier_block timers_nb = {
.notifier_call = timer_cpu_notify, .notifier_call = timer_cpu_notify,
}; };

View file

@ -547,7 +547,7 @@ static void take_over_work(struct workqueue_struct *wq, unsigned int cpu)
} }
/* We're holding the cpucontrol mutex here */ /* We're holding the cpucontrol mutex here */
static int __devinit workqueue_cpu_callback(struct notifier_block *nfb, static int workqueue_cpu_callback(struct notifier_block *nfb,
unsigned long action, unsigned long action,
void *hcpu) void *hcpu)
{ {

View file

@ -1962,7 +1962,7 @@ static inline void free_zone_pagesets(int cpu)
} }
} }
static int __cpuinit pageset_cpuup_callback(struct notifier_block *nfb, static int pageset_cpuup_callback(struct notifier_block *nfb,
unsigned long action, unsigned long action,
void *hcpu) void *hcpu)
{ {

View file

@ -1036,7 +1036,7 @@ static inline void free_alien_cache(struct array_cache **ac_ptr)
#endif #endif
static int __devinit cpuup_callback(struct notifier_block *nfb, static int cpuup_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu) unsigned long action, void *hcpu)
{ {
long cpu = (long)hcpu; long cpu = (long)hcpu;

View file

@ -1328,7 +1328,7 @@ repeat:
not required for correctness. So if the last cpu in a node goes not required for correctness. So if the last cpu in a node goes
away, we get changed to run anywhere: as the first one comes back, away, we get changed to run anywhere: as the first one comes back,
restore their cpu bindings. */ restore their cpu bindings. */
static int __devinit cpu_callback(struct notifier_block *nfb, static int cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu) unsigned long action, void *hcpu)
{ {
pg_data_t *pgdat; pg_data_t *pgdat;

View file

@ -16,6 +16,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/if_vlan.h>
#include <linux/netfilter_bridge.h> #include <linux/netfilter_bridge.h>
#include "br_private.h" #include "br_private.h"
@ -29,10 +30,15 @@ static inline int should_deliver(const struct net_bridge_port *p,
return 1; return 1;
} }
static inline unsigned packet_length(const struct sk_buff *skb)
{
return skb->len - (skb->protocol == htons(ETH_P_8021Q) ? VLAN_HLEN : 0);
}
int br_dev_queue_push_xmit(struct sk_buff *skb) int br_dev_queue_push_xmit(struct sk_buff *skb)
{ {
/* drop mtu oversized packets except tso */ /* drop mtu oversized packets except tso */
if (skb->len > skb->dev->mtu && !skb_shinfo(skb)->tso_size) if (packet_length(skb) > skb->dev->mtu && !skb_shinfo(skb)->tso_size)
kfree_skb(skb); kfree_skb(skb);
else { else {
#ifdef CONFIG_BRIDGE_NETFILTER #ifdef CONFIG_BRIDGE_NETFILTER