From cbb7e577e732f576b9f399bc2600bdc0626c68dc Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 11 Apr 2006 14:57:50 +0200 Subject: [PATCH] splice: pass offset around for ->splice_read() and ->splice_write() We need not use ->f_pos as the offset for the file input/output. If the user passed an offset pointer in through sys_splice(), just use that and leave ->f_pos alone. Signed-off-by: Jens Axboe --- include/linux/fs.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/fs.h b/include/linux/fs.h index 162c6e57307a..3de2bfb2410f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1039,8 +1039,8 @@ struct file_operations { int (*check_flags)(int); int (*dir_notify)(struct file *filp, unsigned long arg); int (*flock) (struct file *, int, struct file_lock *); - ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, size_t, unsigned int); - ssize_t (*splice_read)(struct file *, struct pipe_inode_info *, size_t, unsigned int); + ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); + ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); }; struct inode_operations { @@ -1613,13 +1613,13 @@ extern void do_generic_mapping_read(struct address_space *mapping, loff_t *, read_descriptor_t *, read_actor_t); /* fs/splice.c */ -extern ssize_t generic_file_splice_read(struct file *, +extern ssize_t generic_file_splice_read(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); extern ssize_t generic_file_splice_write(struct pipe_inode_info *, - struct file *, size_t, unsigned int); + struct file *, loff_t *, size_t, unsigned int); extern ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe, - struct file *out, size_t len, unsigned int flags); -extern long do_splice_direct(struct file *in, struct file *out, + struct file *out, loff_t *, size_t len, unsigned int flags); +extern long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, size_t len, unsigned int flags); extern void -- cgit v1.2.3 From 70524490ee2ea1bbf6cee6c106597b3ac25a3fc2 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 11 Apr 2006 15:51:17 +0200 Subject: [PATCH] splice: add support for sys_tee() Basically an in-kernel implementation of tee, which uses splice and the pipe buffers as an intelligent way to pass data around by reference. Where the user space tee consumes the input and produces a stdout and file output, this syscall merely duplicates the data inside a pipe to another pipe. No data is copied, the output just grabs a reference to the input pipe data. Signed-off-by: Jens Axboe --- arch/i386/kernel/syscall_table.S | 1 + arch/ia64/kernel/entry.S | 1 + arch/powerpc/kernel/systbl.S | 1 + fs/pipe.c | 7 ++ fs/splice.c | 186 +++++++++++++++++++++++++++++++++++++++ include/asm-i386/unistd.h | 3 +- include/asm-ia64/unistd.h | 3 +- include/asm-powerpc/unistd.h | 3 +- include/asm-x86_64/unistd.h | 4 +- include/linux/pipe_fs_i.h | 1 + include/linux/syscalls.h | 2 + 11 files changed, 208 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/arch/i386/kernel/syscall_table.S b/arch/i386/kernel/syscall_table.S index 4f58b9c0efe3..f48bef15b4f0 100644 --- a/arch/i386/kernel/syscall_table.S +++ b/arch/i386/kernel/syscall_table.S @@ -314,3 +314,4 @@ ENTRY(sys_call_table) .long sys_get_robust_list .long sys_splice .long sys_sync_file_range + .long sys_tee /* 315 */ diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S index 6e16f6b35bd3..e30798811216 100644 --- a/arch/ia64/kernel/entry.S +++ b/arch/ia64/kernel/entry.S @@ -1609,5 +1609,6 @@ sys_call_table: data8 sys_set_robust_list data8 sys_get_robust_list data8 sys_sync_file_range // 1300 + data8 sys_tee .org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls diff --git a/arch/powerpc/kernel/systbl.S b/arch/powerpc/kernel/systbl.S index 1424eab450ee..a14c96403840 100644 --- a/arch/powerpc/kernel/systbl.S +++ b/arch/powerpc/kernel/systbl.S @@ -323,3 +323,4 @@ COMPAT_SYS(pselect6) COMPAT_SYS(ppoll) SYSCALL(unshare) SYSCALL(splice) +SYSCALL(tee) diff --git a/fs/pipe.c b/fs/pipe.c index e984beb93a0e..7fefb10db8d9 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -131,12 +131,19 @@ static int anon_pipe_buf_steal(struct pipe_inode_info *pipe, return 0; } +static void anon_pipe_buf_get(struct pipe_inode_info *info, + struct pipe_buffer *buf) +{ + page_cache_get(buf->page); +} + static struct pipe_buf_operations anon_pipe_buf_ops = { .can_merge = 1, .map = anon_pipe_buf_map, .unmap = anon_pipe_buf_unmap, .release = anon_pipe_buf_release, .steal = anon_pipe_buf_steal, + .get = anon_pipe_buf_get, }; static ssize_t diff --git a/fs/splice.c b/fs/splice.c index 5d3eda64703b..8d57e89924a6 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -125,12 +125,19 @@ static void page_cache_pipe_buf_unmap(struct pipe_inode_info *info, kunmap(buf->page); } +static void page_cache_pipe_buf_get(struct pipe_inode_info *info, + struct pipe_buffer *buf) +{ + page_cache_get(buf->page); +} + static struct pipe_buf_operations page_cache_pipe_buf_ops = { .can_merge = 0, .map = page_cache_pipe_buf_map, .unmap = page_cache_pipe_buf_unmap, .release = page_cache_pipe_buf_release, .steal = page_cache_pipe_buf_steal, + .get = page_cache_pipe_buf_get, }; /* @@ -963,3 +970,182 @@ asmlinkage long sys_splice(int fd_in, loff_t __user *off_in, return error; } + +/* + * Link contents of ipipe to opipe. + */ +static int link_pipe(struct pipe_inode_info *ipipe, + struct pipe_inode_info *opipe, + size_t len, unsigned int flags) +{ + struct pipe_buffer *ibuf, *obuf; + int ret = 0, do_wakeup = 0, i; + + /* + * Potential ABBA deadlock, work around it by ordering lock + * grabbing by inode address. Otherwise two different processes + * could deadlock (one doing tee from A -> B, the other from B -> A). + */ + if (ipipe->inode < opipe->inode) { + mutex_lock(&ipipe->inode->i_mutex); + mutex_lock(&opipe->inode->i_mutex); + } else { + mutex_lock(&opipe->inode->i_mutex); + mutex_lock(&ipipe->inode->i_mutex); + } + + for (i = 0;; i++) { + if (!opipe->readers) { + send_sig(SIGPIPE, current, 0); + if (!ret) + ret = -EPIPE; + break; + } + if (ipipe->nrbufs - i) { + ibuf = ipipe->bufs + ((ipipe->curbuf + i) & (PIPE_BUFFERS - 1)); + + /* + * If we have room, fill this buffer + */ + if (opipe->nrbufs < PIPE_BUFFERS) { + int nbuf = (opipe->curbuf + opipe->nrbufs) & (PIPE_BUFFERS - 1); + + /* + * Get a reference to this pipe buffer, + * so we can copy the contents over. + */ + ibuf->ops->get(ipipe, ibuf); + + obuf = opipe->bufs + nbuf; + *obuf = *ibuf; + + if (obuf->len > len) + obuf->len = len; + + opipe->nrbufs++; + do_wakeup = 1; + ret += obuf->len; + len -= obuf->len; + + if (!len) + break; + if (opipe->nrbufs < PIPE_BUFFERS) + continue; + } + + /* + * We have input available, but no output room. + * If we already copied data, return that. + */ + if (flags & SPLICE_F_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + if (do_wakeup) { + smp_mb(); + if (waitqueue_active(&opipe->wait)) + wake_up_interruptible(&opipe->wait); + kill_fasync(&opipe->fasync_readers, SIGIO, POLL_IN); + do_wakeup = 0; + } + + opipe->waiting_writers++; + pipe_wait(opipe); + opipe->waiting_writers--; + continue; + } + + /* + * No input buffers, do the usual checks for available + * writers and blocking and wait if necessary + */ + if (!ipipe->writers) + break; + if (!ipipe->waiting_writers) { + if (ret) + break; + } + if (flags & SPLICE_F_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + + if (waitqueue_active(&ipipe->wait)) + wake_up_interruptible_sync(&ipipe->wait); + kill_fasync(&ipipe->fasync_writers, SIGIO, POLL_OUT); + + pipe_wait(ipipe); + } + + mutex_unlock(&ipipe->inode->i_mutex); + mutex_unlock(&opipe->inode->i_mutex); + + if (do_wakeup) { + smp_mb(); + if (waitqueue_active(&opipe->wait)) + wake_up_interruptible(&opipe->wait); + kill_fasync(&opipe->fasync_readers, SIGIO, POLL_IN); + } + + return ret; +} + +/* + * This is a tee(1) implementation that works on pipes. It doesn't copy + * any data, it simply references the 'in' pages on the 'out' pipe. + * The 'flags' used are the SPLICE_F_* variants, currently the only + * applicable one is SPLICE_F_NONBLOCK. + */ +static long do_tee(struct file *in, struct file *out, size_t len, + unsigned int flags) +{ + struct pipe_inode_info *ipipe = in->f_dentry->d_inode->i_pipe; + struct pipe_inode_info *opipe = out->f_dentry->d_inode->i_pipe; + + /* + * Link ipipe to the two output pipes, consuming as we go along. + */ + if (ipipe && opipe) + return link_pipe(ipipe, opipe, len, flags); + + return -EINVAL; +} + +asmlinkage long sys_tee(int fdin, int fdout, size_t len, unsigned int flags) +{ + struct file *in; + int error, fput_in; + + if (unlikely(!len)) + return 0; + + error = -EBADF; + in = fget_light(fdin, &fput_in); + if (in) { + if (in->f_mode & FMODE_READ) { + int fput_out; + struct file *out = fget_light(fdout, &fput_out); + + if (out) { + if (out->f_mode & FMODE_WRITE) + error = do_tee(in, out, len, flags); + fput_light(out, fput_out); + } + } + fput_light(in, fput_in); + } + + return error; +} diff --git a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h index 6a8dd83c350f..d81d6cfc1bb4 100644 --- a/include/asm-i386/unistd.h +++ b/include/asm-i386/unistd.h @@ -320,8 +320,9 @@ #define __NR_get_robust_list 312 #define __NR_splice 313 #define __NR_sync_file_range 314 +#define __NR_tee 315 -#define NR_syscalls 315 +#define NR_syscalls 316 /* * user-visible error numbers are in the range -1 - -128: see diff --git a/include/asm-ia64/unistd.h b/include/asm-ia64/unistd.h index 1c749acca021..a40ebec6aeeb 100644 --- a/include/asm-ia64/unistd.h +++ b/include/asm-ia64/unistd.h @@ -289,12 +289,13 @@ #define __NR_set_robust_list 1298 #define __NR_get_robust_list 1299 #define __NR_sync_file_range 1300 +#define __NR_tee 1301 #ifdef __KERNEL__ #include -#define NR_syscalls 277 /* length of syscall table */ +#define NR_syscalls 278 /* length of syscall table */ #define __ARCH_WANT_SYS_RT_SIGACTION diff --git a/include/asm-powerpc/unistd.h b/include/asm-powerpc/unistd.h index 536ba0873052..c612f1a62772 100644 --- a/include/asm-powerpc/unistd.h +++ b/include/asm-powerpc/unistd.h @@ -302,8 +302,9 @@ #define __NR_ppoll 281 #define __NR_unshare 282 #define __NR_splice 283 +#define __NR_tee 284 -#define __NR_syscalls 284 +#define __NR_syscalls 285 #ifdef __KERNEL__ #define __NR__exit __NR_exit diff --git a/include/asm-x86_64/unistd.h b/include/asm-x86_64/unistd.h index f21ff2c1e960..d86494e23b63 100644 --- a/include/asm-x86_64/unistd.h +++ b/include/asm-x86_64/unistd.h @@ -611,8 +611,10 @@ __SYSCALL(__NR_set_robust_list, sys_set_robust_list) __SYSCALL(__NR_get_robust_list, sys_get_robust_list) #define __NR_splice 275 __SYSCALL(__NR_splice, sys_splice) +#define __NR_tee 276 +__SYSCALL(__NR_tee, sys_tee) -#define __NR_syscall_max __NR_splice +#define __NR_syscall_max __NR_tee #ifndef __NO_STUBS diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 123a7c24bc72..ef7f33c0be19 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -21,6 +21,7 @@ struct pipe_buf_operations { void (*unmap)(struct pipe_inode_info *, struct pipe_buffer *); void (*release)(struct pipe_inode_info *, struct pipe_buffer *); int (*steal)(struct pipe_inode_info *, struct pipe_buffer *); + void (*get)(struct pipe_inode_info *, struct pipe_buffer *); }; struct pipe_inode_info { diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index f001bad28d9a..d3ebc0e68b2b 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -574,6 +574,8 @@ asmlinkage long sys_splice(int fd_in, loff_t __user *off_in, int fd_out, loff_t __user *off_out, 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, unsigned int flags); -- cgit v1.2.3 From 0b7018aae7e1798f55f736b9a77c201708aa0e33 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 11 Apr 2006 23:42:03 -0400 Subject: Input: ads7846 - debouncing and rudimentary sample filtering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some touchscreens seem to oscillate heavily for a while after touching the screen.  Implement support for sampling the screen until we get two consecutive values that are close enough. Signed-off-by: Imre Deak Signed-off-by: Juha Yrjola Signed-off-by: David Brownell Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 156 +++++++++++++++++++++++++++--------- include/linux/spi/ads7846.h | 3 + 2 files changed, 121 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 54d433477860..8670cd13bd5d 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -77,7 +77,13 @@ struct ads7846 { struct ts_event tc; struct spi_transfer xfer[10]; - struct spi_message msg; + struct spi_message msg[5]; + int msg_idx; + int read_cnt; + int last_read; + + u16 debounce_max; + u16 debounce_tol; spinlock_t lock; struct timer_list timer; /* P: lock */ @@ -167,7 +173,7 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) if (!req) return -ENOMEM; - INIT_LIST_HEAD(&req->msg.transfers); + spi_message_init(&req->msg); /* activate reference, so it has time to settle; */ req->ref_on = REF_ON; @@ -344,31 +350,76 @@ static void ads7846_rx(void *ads) spin_unlock_irqrestore(&ts->lock, flags); } +static void ads7846_debounce(void *ads) +{ + struct ads7846 *ts = ads; + struct spi_message *m; + struct spi_transfer *t; + u16 val; + int status; + + m = &ts->msg[ts->msg_idx]; + t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); + val = (*(u16 *)t->rx_buf) >> 3; + + if (!ts->read_cnt || (abs(ts->last_read - val) > ts->debounce_tol + && ts->read_cnt < ts->debounce_max)) { + /* Repeat it, if this was the first read or the read wasn't + * consistent enough + */ + ts->read_cnt++; + ts->last_read = val; + } else { + /* Go for the next read */ + ts->msg_idx++; + ts->read_cnt = 0; + m++; + } + status = spi_async(ts->spi, m); + if (status) + dev_err(&ts->spi->dev, "spi_async --> %d\n", + status); +} + static void ads7846_timer(unsigned long handle) { struct ads7846 *ts = (void *)handle; int status = 0; - unsigned long flags; + + ts->msg_idx = 0; + status = spi_async(ts->spi, &ts->msg[0]); + if (status) + dev_err(&ts->spi->dev, "spi_async --> %d\n", status); +} + +static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs) +{ + struct ads7846 *ts = handle; + unsigned long flags; + int r = IRQ_HANDLED; spin_lock_irqsave(&ts->lock, flags); - if (!ts->pending) { - ts->pending = 1; + if (ts->irq_disabled) + r = IRQ_HANDLED; + else { if (!ts->irq_disabled) { + /* REVISIT irq logic for many ARM chips has cloned a + * bug wherein disabling an irq in its handler won't + * work;(it's disabled lazily, and too late to work. + * until all their irq logic is fixed, we must shadow + * that state here. + */ ts->irq_disabled = 1; + disable_irq(ts->spi->irq); } - status = spi_async(ts->spi, &ts->msg); - if (status) - dev_err(&ts->spi->dev, "spi_async --> %d\n", - status); + if (!ts->pending) { + ts->pending = 1; + mod_timer(&ts->timer, jiffies); + } } spin_unlock_irqrestore(&ts->lock, flags); -} - -static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs) -{ - ads7846_timer((unsigned long) handle); - return IRQ_HANDLED; + return r; } /*--------------------------------------------------------------------------*/ @@ -426,6 +477,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) struct ads7846 *ts; struct input_dev *input_dev; struct ads7846_platform_data *pdata = spi->dev.platform_data; + struct spi_message *m; struct spi_transfer *x; int err; @@ -472,6 +524,8 @@ static int __devinit ads7846_probe(struct spi_device *spi) ts->model = pdata->model ? : 7846; ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; + ts->debounce_max = pdata->debounce_max ? : 1; + ts->debounce_tol = pdata->debounce_tol ? : 10; snprintf(ts->phys, sizeof(ts->phys), "%s/input0", spi->dev.bus_id); @@ -495,72 +549,98 @@ static int __devinit ads7846_probe(struct spi_device *spi) /* set up the transfers to read touchscreen state; this assumes we * use formula #2 for pressure, not #3. */ - INIT_LIST_HEAD(&ts->msg.transfers); + m = &ts->msg[0]; x = ts->xfer; + spi_message_init(m); + /* y- still on; turn on only y+ (and ADC) */ ts->read_y = READ_Y; x->tx_buf = &ts->read_y; x->len = 1; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); x++; x->rx_buf = &ts->tc.y; x->len = 2; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); + + m->complete = ads7846_debounce; + m->context = ts; + + m++; + spi_message_init(m); + + /* turn y- off, x+ on, then leave in lowpower */ + x++; + ts->read_x = READ_X; + x->tx_buf = &ts->read_x; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &ts->tc.x; + x->len = 2; + spi_message_add_tail(x, m); + + m->complete = ads7846_debounce; + m->context = ts; /* turn y+ off, x- on; we'll use formula #2 */ if (ts->model == 7846) { + m++; + spi_message_init(m); + x++; ts->read_z1 = READ_Z1; x->tx_buf = &ts->read_z1; x->len = 1; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); x++; x->rx_buf = &ts->tc.z1; x->len = 2; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); + + m->complete = ads7846_debounce; + m->context = ts; + + m++; + spi_message_init(m); x++; ts->read_z2 = READ_Z2; x->tx_buf = &ts->read_z2; x->len = 1; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); x++; x->rx_buf = &ts->tc.z2; x->len = 2; - spi_message_add_tail(x, &ts->msg); - } + spi_message_add_tail(x, m); - /* turn y- off, x+ on, then leave in lowpower */ - x++; - ts->read_x = READ_X; - x->tx_buf = &ts->read_x; - x->len = 1; - spi_message_add_tail(x, &ts->msg); - - x++; - x->rx_buf = &ts->tc.x; - x->len = 2; - spi_message_add_tail(x, &ts->msg); + m->complete = ads7846_debounce; + m->context = ts; + } /* power down */ + m++; + spi_message_init(m); + x++; ts->pwrdown = PWRDOWN; x->tx_buf = &ts->pwrdown; x->len = 1; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); x++; x->rx_buf = &ts->dummy; x->len = 2; CS_CHANGE(*x); - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); - ts->msg.complete = ads7846_rx; - ts->msg.context = ts; + m->complete = ads7846_rx; + m->context = ts; if (request_irq(spi->irq, ads7846_irq, SA_SAMPLE_RANDOM | SA_TRIGGER_FALLING, diff --git a/include/linux/spi/ads7846.h b/include/linux/spi/ads7846.h index 72261e0f2ac1..3f7664951256 100644 --- a/include/linux/spi/ads7846.h +++ b/include/linux/spi/ads7846.h @@ -14,5 +14,8 @@ struct ads7846_platform_data { u16 x_min, x_max; u16 y_min, y_max; u16 pressure_min, pressure_max; + + u16 debounce_max; /* max number of readings per sample */ + u16 debounce_tol; /* tolerance used for filtering */ }; -- cgit v1.2.3 From c9e617a563ad646239270fa2222cdb06966cf1fa Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 11 Apr 2006 23:44:05 -0400 Subject: Input: ads7846 - handle IRQs that were latched during disabled IRQs The pen down IRQ will toggle during each X,Y,Z measurement cycle. Even though the IRQ is disabled it will be latched and delivered when after enable_irq. Thus in the IRQ handler we must avoid starting a new measurement cycle when such an "unwanted" IRQ happens. Add a get_pendown_state platform function, which will probably determine this by reading the current GPIO level of the pen IRQ pin. Move the IRQ reenabling from the SPI RX function to the timer. After the last power down message the pen IRQ pin is still active for a while and get_pendown_state would report incorrectly a pen down state. When suspending we should check the ts->pending flag instead of ts->pendown, since the timer can be pending regardless of ts->pendown. Also if ts->pending is set we can be sure that the timer is running, so no need to rearm it. Similarly if ts->pending is not set we can be sure that the IRQ is enabled (and the timer is not). Signed-off-by: Imre Deak Signed-off-by: David Brownell Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 82 ++++++++++++++++++++++--------------- include/linux/spi/ads7846.h | 2 + 2 files changed, 50 insertions(+), 34 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index fec3b9b22309..e7cabf12c8dc 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -102,6 +102,8 @@ struct ads7846 { // FIXME remove "irq_disabled" unsigned irq_disabled:1; /* P: lock */ unsigned disabled:1; + + int (*get_pendown_state)(void); }; /* leave chip selected when we're done, for quicker re-select? */ @@ -175,6 +177,12 @@ struct ser_req { static void ads7846_enable(struct ads7846 *ts); static void ads7846_disable(struct ads7846 *ts); +static int device_suspended(struct device *dev) +{ + struct ads7846 *ts = dev_get_drvdata(dev); + return dev->power.power_state.event != PM_EVENT_ON || ts->disabled; +} + static int ads7846_read12_ser(struct device *dev, unsigned command) { struct spi_device *spi = to_spi_device(dev); @@ -227,8 +235,10 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) for (i = 0; i < 6; i++) spi_message_add_tail(&req->xfer[i], &req->msg); + ts->irq_disabled = 1; disable_irq(spi->irq); status = spi_sync(spi, &req->msg); + ts->irq_disabled = 0; enable_irq(spi->irq); if (req->msg.status) @@ -333,7 +343,7 @@ static void ads7846_rx(void *ads) if (x == MAX_12BIT) x = 0; - if (x && z1 && ts->spi->dev.power.power_state.event == PM_EVENT_ON) { + if (likely(x && z1 && !device_suspended(&ts->spi->dev))) { /* compute touch pressure resistance using equation #2 */ Rt = z2; Rt -= z1; @@ -377,20 +387,10 @@ static void ads7846_rx(void *ads) x, y, Rt, Rt ? "" : " UP"); #endif - /* don't retrigger while we're suspended */ spin_lock_irqsave(&ts->lock, flags); ts->pendown = (Rt != 0); - ts->pending = 0; - - if (ts->spi->dev.power.power_state.event == PM_EVENT_ON) { - if (ts->pendown) - mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD); - else if (ts->irq_disabled) { - ts->irq_disabled = 0; - enable_irq(ts->spi->irq); - } - } + mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD); spin_unlock_irqrestore(&ts->lock, flags); } @@ -431,10 +431,25 @@ static void ads7846_timer(unsigned long handle) struct ads7846 *ts = (void *)handle; int status = 0; - ts->msg_idx = 0; - status = spi_async(ts->spi, &ts->msg[0]); - if (status) - dev_err(&ts->spi->dev, "spi_async --> %d\n", status); + spin_lock_irq(&ts->lock); + + if (unlikely(ts->msg_idx && !ts->pendown)) { + /* measurment cycle ended */ + if (!device_suspended(&ts->spi->dev)) { + ts->irq_disabled = 0; + enable_irq(ts->spi->irq); + } + ts->pending = 0; + ts->msg_idx = 0; + } else { + /* pen is still down, continue with the measurement */ + ts->msg_idx = 0; + status = spi_async(ts->spi, &ts->msg[0]); + if (status) + dev_err(&ts->spi->dev, "spi_async --> %d\n", status); + } + + spin_unlock_irq(&ts->lock); } static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs) @@ -443,7 +458,7 @@ static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs) unsigned long flags; spin_lock_irqsave(&ts->lock, flags); - if (likely(!ts->irq_disabled && !ts->disabled)) { + if (likely(ts->get_pendown_state())) { if (!ts->irq_disabled) { /* REVISIT irq logic for many ARM chips has cloned a * bug wherein disabling an irq in its handler won't @@ -452,10 +467,7 @@ static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs) * that state here. */ ts->irq_disabled = 1; - disable_irq(ts->spi->irq); - } - if (!ts->pending) { ts->pending = 1; mod_timer(&ts->timer, jiffies); } @@ -473,20 +485,17 @@ static void ads7846_disable(struct ads7846 *ts) if (ts->disabled) return; + ts->disabled = 1; + /* are we waiting for IRQ, or polling? */ - if (!ts->pendown) { - if (!ts->irq_disabled) { - ts->irq_disabled = 1; - disable_irq(ts->spi->irq); - } + if (!ts->pending) { + ts->irq_disabled = 1; + disable_irq(ts->spi->irq); } else { - /* polling; force a final SPI completion; - * that will clean things up neatly + /* the timer will run at least once more, and + * leave everything in a clean state, IRQ disabled */ - if (!ts->pending) - mod_timer(&ts->timer, jiffies); - - while (ts->pendown || ts->pending) { + while (ts->pending) { spin_unlock_irq(&ts->lock); msleep(1); spin_lock_irq(&ts->lock); @@ -497,7 +506,6 @@ static void ads7846_disable(struct ads7846 *ts) * leave it that way after every request */ - ts->disabled = 1; } /* Must be called with ts->lock held */ @@ -566,6 +574,11 @@ static int __devinit ads7846_probe(struct spi_device *spi) return -EINVAL; } + if (pdata->get_pendown_state == NULL) { + dev_dbg(&spi->dev, "no get_pendown_state function?\n"); + return -EINVAL; + } + /* We'd set the wordsize to 12 bits ... except that some controllers * will then treat the 8 bit command words as 12 bits (and drop the * four MSBs of the 12 bit result). Result: inputs must be shifted @@ -596,6 +609,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; ts->debounce_max = pdata->debounce_max ? : 1; ts->debounce_tol = pdata->debounce_tol ? : 10; + ts->get_pendown_state = pdata->get_pendown_state; snprintf(ts->phys, sizeof(ts->phys), "%s/input0", spi->dev.bus_id); @@ -786,8 +800,8 @@ static int __devexit ads7846_remove(struct spi_device *spi) device_remove_file(&spi->dev, &dev_attr_vaux); free_irq(ts->spi->irq, ts); - if (ts->irq_disabled) - enable_irq(ts->spi->irq); + /* suspend left the IRQ disabled */ + enable_irq(ts->spi->irq); kfree(ts); diff --git a/include/linux/spi/ads7846.h b/include/linux/spi/ads7846.h index 3f7664951256..d8823e237e0b 100644 --- a/include/linux/spi/ads7846.h +++ b/include/linux/spi/ads7846.h @@ -17,5 +17,7 @@ struct ads7846_platform_data { u16 debounce_max; /* max number of readings per sample */ u16 debounce_tol; /* tolerance used for filtering */ + + int (*get_pendown_state)(void); }; -- cgit v1.2.3 From 9fc4831cc3e063019079581ff5062f9790d9b0c7 Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Sun, 2 Apr 2006 10:21:26 -0800 Subject: [PATCH] USB: linux/usb/net2280.h common definitions Move common definitions for NET2280 to , so that I can use them in prism54usb (it is not merged yet, but I plan to do it soon). Signed-off-by: Pete Zaitcev Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/net2280.h | 417 +--------------------------------------- include/linux/usb/net2280.h | 444 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 445 insertions(+), 416 deletions(-) create mode 100644 include/linux/usb/net2280.h (limited to 'include/linux') diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h index e195abec8d7f..957d6df34015 100644 --- a/drivers/usb/gadget/net2280.h +++ b/drivers/usb/gadget/net2280.h @@ -22,422 +22,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/*-------------------------------------------------------------------------*/ - -/* NET2280 MEMORY MAPPED REGISTERS - * - * The register layout came from the chip documentation, and the bit - * number definitions were extracted from chip specification. - * - * Use the shift operator ('<<') to build bit masks, with readl/writel - * to access the registers through PCI. - */ - -/* main registers, BAR0 + 0x0000 */ -struct net2280_regs { - // offset 0x0000 - u32 devinit; -#define LOCAL_CLOCK_FREQUENCY 8 -#define FORCE_PCI_RESET 7 -#define PCI_ID 6 -#define PCI_ENABLE 5 -#define FIFO_SOFT_RESET 4 -#define CFG_SOFT_RESET 3 -#define PCI_SOFT_RESET 2 -#define USB_SOFT_RESET 1 -#define M8051_RESET 0 - u32 eectl; -#define EEPROM_ADDRESS_WIDTH 23 -#define EEPROM_CHIP_SELECT_ACTIVE 22 -#define EEPROM_PRESENT 21 -#define EEPROM_VALID 20 -#define EEPROM_BUSY 19 -#define EEPROM_CHIP_SELECT_ENABLE 18 -#define EEPROM_BYTE_READ_START 17 -#define EEPROM_BYTE_WRITE_START 16 -#define EEPROM_READ_DATA 8 -#define EEPROM_WRITE_DATA 0 - u32 eeclkfreq; - u32 _unused0; - // offset 0x0010 - - u32 pciirqenb0; /* interrupt PCI master ... */ -#define SETUP_PACKET_INTERRUPT_ENABLE 7 -#define ENDPOINT_F_INTERRUPT_ENABLE 6 -#define ENDPOINT_E_INTERRUPT_ENABLE 5 -#define ENDPOINT_D_INTERRUPT_ENABLE 4 -#define ENDPOINT_C_INTERRUPT_ENABLE 3 -#define ENDPOINT_B_INTERRUPT_ENABLE 2 -#define ENDPOINT_A_INTERRUPT_ENABLE 1 -#define ENDPOINT_0_INTERRUPT_ENABLE 0 - u32 pciirqenb1; -#define PCI_INTERRUPT_ENABLE 31 -#define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27 -#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26 -#define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25 -#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20 -#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19 -#define PCI_TARGET_ABORT_ASSERTED_INTERRUPT_ENABLE 18 -#define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17 -#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16 -#define GPIO_INTERRUPT_ENABLE 13 -#define DMA_D_INTERRUPT_ENABLE 12 -#define DMA_C_INTERRUPT_ENABLE 11 -#define DMA_B_INTERRUPT_ENABLE 10 -#define DMA_A_INTERRUPT_ENABLE 9 -#define EEPROM_DONE_INTERRUPT_ENABLE 8 -#define VBUS_INTERRUPT_ENABLE 7 -#define CONTROL_STATUS_INTERRUPT_ENABLE 6 -#define ROOT_PORT_RESET_INTERRUPT_ENABLE 4 -#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3 -#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2 -#define RESUME_INTERRUPT_ENABLE 1 -#define SOF_INTERRUPT_ENABLE 0 - u32 cpu_irqenb0; /* ... or onboard 8051 */ -#define SETUP_PACKET_INTERRUPT_ENABLE 7 -#define ENDPOINT_F_INTERRUPT_ENABLE 6 -#define ENDPOINT_E_INTERRUPT_ENABLE 5 -#define ENDPOINT_D_INTERRUPT_ENABLE 4 -#define ENDPOINT_C_INTERRUPT_ENABLE 3 -#define ENDPOINT_B_INTERRUPT_ENABLE 2 -#define ENDPOINT_A_INTERRUPT_ENABLE 1 -#define ENDPOINT_0_INTERRUPT_ENABLE 0 - u32 cpu_irqenb1; -#define CPU_INTERRUPT_ENABLE 31 -#define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27 -#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26 -#define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25 -#define PCI_INTA_INTERRUPT_ENABLE 24 -#define PCI_PME_INTERRUPT_ENABLE 23 -#define PCI_SERR_INTERRUPT_ENABLE 22 -#define PCI_PERR_INTERRUPT_ENABLE 21 -#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20 -#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19 -#define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17 -#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16 -#define GPIO_INTERRUPT_ENABLE 13 -#define DMA_D_INTERRUPT_ENABLE 12 -#define DMA_C_INTERRUPT_ENABLE 11 -#define DMA_B_INTERRUPT_ENABLE 10 -#define DMA_A_INTERRUPT_ENABLE 9 -#define EEPROM_DONE_INTERRUPT_ENABLE 8 -#define VBUS_INTERRUPT_ENABLE 7 -#define CONTROL_STATUS_INTERRUPT_ENABLE 6 -#define ROOT_PORT_RESET_INTERRUPT_ENABLE 4 -#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3 -#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2 -#define RESUME_INTERRUPT_ENABLE 1 -#define SOF_INTERRUPT_ENABLE 0 - - // offset 0x0020 - u32 _unused1; - u32 usbirqenb1; -#define USB_INTERRUPT_ENABLE 31 -#define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27 -#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26 -#define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25 -#define PCI_INTA_INTERRUPT_ENABLE 24 -#define PCI_PME_INTERRUPT_ENABLE 23 -#define PCI_SERR_INTERRUPT_ENABLE 22 -#define PCI_PERR_INTERRUPT_ENABLE 21 -#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20 -#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19 -#define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17 -#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16 -#define GPIO_INTERRUPT_ENABLE 13 -#define DMA_D_INTERRUPT_ENABLE 12 -#define DMA_C_INTERRUPT_ENABLE 11 -#define DMA_B_INTERRUPT_ENABLE 10 -#define DMA_A_INTERRUPT_ENABLE 9 -#define EEPROM_DONE_INTERRUPT_ENABLE 8 -#define VBUS_INTERRUPT_ENABLE 7 -#define CONTROL_STATUS_INTERRUPT_ENABLE 6 -#define ROOT_PORT_RESET_INTERRUPT_ENABLE 4 -#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3 -#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2 -#define RESUME_INTERRUPT_ENABLE 1 -#define SOF_INTERRUPT_ENABLE 0 - u32 irqstat0; -#define INTA_ASSERTED 12 -#define SETUP_PACKET_INTERRUPT 7 -#define ENDPOINT_F_INTERRUPT 6 -#define ENDPOINT_E_INTERRUPT 5 -#define ENDPOINT_D_INTERRUPT 4 -#define ENDPOINT_C_INTERRUPT 3 -#define ENDPOINT_B_INTERRUPT 2 -#define ENDPOINT_A_INTERRUPT 1 -#define ENDPOINT_0_INTERRUPT 0 - u32 irqstat1; -#define POWER_STATE_CHANGE_INTERRUPT 27 -#define PCI_ARBITER_TIMEOUT_INTERRUPT 26 -#define PCI_PARITY_ERROR_INTERRUPT 25 -#define PCI_INTA_INTERRUPT 24 -#define PCI_PME_INTERRUPT 23 -#define PCI_SERR_INTERRUPT 22 -#define PCI_PERR_INTERRUPT 21 -#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT 20 -#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT 19 -#define PCI_RETRY_ABORT_INTERRUPT 17 -#define PCI_MASTER_CYCLE_DONE_INTERRUPT 16 -#define SOF_DOWN_INTERRUPT 14 -#define GPIO_INTERRUPT 13 -#define DMA_D_INTERRUPT 12 -#define DMA_C_INTERRUPT 11 -#define DMA_B_INTERRUPT 10 -#define DMA_A_INTERRUPT 9 -#define EEPROM_DONE_INTERRUPT 8 -#define VBUS_INTERRUPT 7 -#define CONTROL_STATUS_INTERRUPT 6 -#define ROOT_PORT_RESET_INTERRUPT 4 -#define SUSPEND_REQUEST_INTERRUPT 3 -#define SUSPEND_REQUEST_CHANGE_INTERRUPT 2 -#define RESUME_INTERRUPT 1 -#define SOF_INTERRUPT 0 - // offset 0x0030 - u32 idxaddr; - u32 idxdata; - u32 fifoctl; -#define PCI_BASE2_RANGE 16 -#define IGNORE_FIFO_AVAILABILITY 3 -#define PCI_BASE2_SELECT 2 -#define FIFO_CONFIGURATION_SELECT 0 - u32 _unused2; - // offset 0x0040 - u32 memaddr; -#define START 28 -#define DIRECTION 27 -#define FIFO_DIAGNOSTIC_SELECT 24 -#define MEMORY_ADDRESS 0 - u32 memdata0; - u32 memdata1; - u32 _unused3; - // offset 0x0050 - u32 gpioctl; -#define GPIO3_LED_SELECT 12 -#define GPIO3_INTERRUPT_ENABLE 11 -#define GPIO2_INTERRUPT_ENABLE 10 -#define GPIO1_INTERRUPT_ENABLE 9 -#define GPIO0_INTERRUPT_ENABLE 8 -#define GPIO3_OUTPUT_ENABLE 7 -#define GPIO2_OUTPUT_ENABLE 6 -#define GPIO1_OUTPUT_ENABLE 5 -#define GPIO0_OUTPUT_ENABLE 4 -#define GPIO3_DATA 3 -#define GPIO2_DATA 2 -#define GPIO1_DATA 1 -#define GPIO0_DATA 0 - u32 gpiostat; -#define GPIO3_INTERRUPT 3 -#define GPIO2_INTERRUPT 2 -#define GPIO1_INTERRUPT 1 -#define GPIO0_INTERRUPT 0 -} __attribute__ ((packed)); - -/* usb control, BAR0 + 0x0080 */ -struct net2280_usb_regs { - // offset 0x0080 - u32 stdrsp; -#define STALL_UNSUPPORTED_REQUESTS 31 -#define SET_TEST_MODE 16 -#define GET_OTHER_SPEED_CONFIGURATION 15 -#define GET_DEVICE_QUALIFIER 14 -#define SET_ADDRESS 13 -#define ENDPOINT_SET_CLEAR_HALT 12 -#define DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP 11 -#define GET_STRING_DESCRIPTOR_2 10 -#define GET_STRING_DESCRIPTOR_1 9 -#define GET_STRING_DESCRIPTOR_0 8 -#define GET_SET_INTERFACE 6 -#define GET_SET_CONFIGURATION 5 -#define GET_CONFIGURATION_DESCRIPTOR 4 -#define GET_DEVICE_DESCRIPTOR 3 -#define GET_ENDPOINT_STATUS 2 -#define GET_INTERFACE_STATUS 1 -#define GET_DEVICE_STATUS 0 - u32 prodvendid; -#define PRODUCT_ID 16 -#define VENDOR_ID 0 - u32 relnum; - u32 usbctl; -#define SERIAL_NUMBER_INDEX 16 -#define PRODUCT_ID_STRING_ENABLE 13 -#define VENDOR_ID_STRING_ENABLE 12 -#define USB_ROOT_PORT_WAKEUP_ENABLE 11 -#define VBUS_PIN 10 -#define TIMED_DISCONNECT 9 -#define SUSPEND_IMMEDIATELY 7 -#define SELF_POWERED_USB_DEVICE 6 -#define REMOTE_WAKEUP_SUPPORT 5 -#define PME_POLARITY 4 -#define USB_DETECT_ENABLE 3 -#define PME_WAKEUP_ENABLE 2 -#define DEVICE_REMOTE_WAKEUP_ENABLE 1 -#define SELF_POWERED_STATUS 0 - // offset 0x0090 - u32 usbstat; -#define HIGH_SPEED 7 -#define FULL_SPEED 6 -#define GENERATE_RESUME 5 -#define GENERATE_DEVICE_REMOTE_WAKEUP 4 - u32 xcvrdiag; -#define FORCE_HIGH_SPEED_MODE 31 -#define FORCE_FULL_SPEED_MODE 30 -#define USB_TEST_MODE 24 -#define LINE_STATE 16 -#define TRANSCEIVER_OPERATION_MODE 2 -#define TRANSCEIVER_SELECT 1 -#define TERMINATION_SELECT 0 - u32 setup0123; - u32 setup4567; - // offset 0x0090 - u32 _unused0; - u32 ouraddr; -#define FORCE_IMMEDIATE 7 -#define OUR_USB_ADDRESS 0 - u32 ourconfig; -} __attribute__ ((packed)); - -/* pci control, BAR0 + 0x0100 */ -struct net2280_pci_regs { - // offset 0x0100 - u32 pcimstctl; -#define PCI_ARBITER_PARK_SELECT 13 -#define PCI_MULTI LEVEL_ARBITER 12 -#define PCI_RETRY_ABORT_ENABLE 11 -#define DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE 10 -#define DMA_READ_MULTIPLE_ENABLE 9 -#define DMA_READ_LINE_ENABLE 8 -#define PCI_MASTER_COMMAND_SELECT 6 -#define MEM_READ_OR_WRITE 0 -#define IO_READ_OR_WRITE 1 -#define CFG_READ_OR_WRITE 2 -#define PCI_MASTER_START 5 -#define PCI_MASTER_READ_WRITE 4 -#define PCI_MASTER_WRITE 0 -#define PCI_MASTER_READ 1 -#define PCI_MASTER_BYTE_WRITE_ENABLES 0 - u32 pcimstaddr; - u32 pcimstdata; - u32 pcimststat; -#define PCI_ARBITER_CLEAR 2 -#define PCI_EXTERNAL_ARBITER 1 -#define PCI_HOST_MODE 0 -} __attribute__ ((packed)); - -/* dma control, BAR0 + 0x0180 ... array of four structs like this, - * for channels 0..3. see also struct net2280_dma: descriptor - * that can be loaded into some of these registers. - */ -struct net2280_dma_regs { /* [11.7] */ - // offset 0x0180, 0x01a0, 0x01c0, 0x01e0, - u32 dmactl; -#define DMA_SCATTER_GATHER_DONE_INTERRUPT_ENABLE 25 -#define DMA_CLEAR_COUNT_ENABLE 21 -#define DESCRIPTOR_POLLING_RATE 19 -#define POLL_CONTINUOUS 0 -#define POLL_1_USEC 1 -#define POLL_100_USEC 2 -#define POLL_1_MSEC 3 -#define DMA_VALID_BIT_POLLING_ENABLE 18 -#define DMA_VALID_BIT_ENABLE 17 -#define DMA_SCATTER_GATHER_ENABLE 16 -#define DMA_OUT_AUTO_START_ENABLE 4 -#define DMA_PREEMPT_ENABLE 3 -#define DMA_FIFO_VALIDATE 2 -#define DMA_ENABLE 1 -#define DMA_ADDRESS_HOLD 0 - u32 dmastat; -#define DMA_ABORT_DONE_INTERRUPT 27 -#define DMA_SCATTER_GATHER_DONE_INTERRUPT 25 -#define DMA_TRANSACTION_DONE_INTERRUPT 24 -#define DMA_ABORT 1 -#define DMA_START 0 - u32 _unused0 [2]; - // offset 0x0190, 0x01b0, 0x01d0, 0x01f0, - u32 dmacount; -#define VALID_BIT 31 -#define DMA_DIRECTION 30 -#define DMA_DONE_INTERRUPT_ENABLE 29 -#define END_OF_CHAIN 28 -#define DMA_BYTE_COUNT_MASK ((1<<24)-1) -#define DMA_BYTE_COUNT 0 - u32 dmaaddr; - u32 dmadesc; - u32 _unused1; -} __attribute__ ((packed)); - -/* dedicated endpoint registers, BAR0 + 0x0200 */ - -struct net2280_dep_regs { /* [11.8] */ - // offset 0x0200, 0x0210, 0x220, 0x230, 0x240 - u32 dep_cfg; - // offset 0x0204, 0x0214, 0x224, 0x234, 0x244 - u32 dep_rsp; - u32 _unused [2]; -} __attribute__ ((packed)); - -/* configurable endpoint registers, BAR0 + 0x0300 ... array of seven structs - * like this, for ep0 then the configurable endpoints A..F - * ep0 reserved for control; E and F have only 64 bytes of fifo - */ -struct net2280_ep_regs { /* [11.9] */ - // offset 0x0300, 0x0320, 0x0340, 0x0360, 0x0380, 0x03a0, 0x03c0 - u32 ep_cfg; -#define ENDPOINT_BYTE_COUNT 16 -#define ENDPOINT_ENABLE 10 -#define ENDPOINT_TYPE 8 -#define ENDPOINT_DIRECTION 7 -#define ENDPOINT_NUMBER 0 - u32 ep_rsp; -#define SET_NAK_OUT_PACKETS 15 -#define SET_EP_HIDE_STATUS_PHASE 14 -#define SET_EP_FORCE_CRC_ERROR 13 -#define SET_INTERRUPT_MODE 12 -#define SET_CONTROL_STATUS_PHASE_HANDSHAKE 11 -#define SET_NAK_OUT_PACKETS_MODE 10 -#define SET_ENDPOINT_TOGGLE 9 -#define SET_ENDPOINT_HALT 8 -#define CLEAR_NAK_OUT_PACKETS 7 -#define CLEAR_EP_HIDE_STATUS_PHASE 6 -#define CLEAR_EP_FORCE_CRC_ERROR 5 -#define CLEAR_INTERRUPT_MODE 4 -#define CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE 3 -#define CLEAR_NAK_OUT_PACKETS_MODE 2 -#define CLEAR_ENDPOINT_TOGGLE 1 -#define CLEAR_ENDPOINT_HALT 0 - u32 ep_irqenb; -#define SHORT_PACKET_OUT_DONE_INTERRUPT_ENABLE 6 -#define SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE 5 -#define DATA_PACKET_RECEIVED_INTERRUPT_ENABLE 3 -#define DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE 2 -#define DATA_OUT_PING_TOKEN_INTERRUPT_ENABLE 1 -#define DATA_IN_TOKEN_INTERRUPT_ENABLE 0 - u32 ep_stat; -#define FIFO_VALID_COUNT 24 -#define HIGH_BANDWIDTH_OUT_TRANSACTION_PID 22 -#define TIMEOUT 21 -#define USB_STALL_SENT 20 -#define USB_IN_NAK_SENT 19 -#define USB_IN_ACK_RCVD 18 -#define USB_OUT_PING_NAK_SENT 17 -#define USB_OUT_ACK_SENT 16 -#define FIFO_OVERFLOW 13 -#define FIFO_UNDERFLOW 12 -#define FIFO_FULL 11 -#define FIFO_EMPTY 10 -#define FIFO_FLUSH 9 -#define SHORT_PACKET_OUT_DONE_INTERRUPT 6 -#define SHORT_PACKET_TRANSFERRED_INTERRUPT 5 -#define NAK_OUT_PACKETS 4 -#define DATA_PACKET_RECEIVED_INTERRUPT 3 -#define DATA_PACKET_TRANSMITTED_INTERRUPT 2 -#define DATA_OUT_PING_TOKEN_INTERRUPT 1 -#define DATA_IN_TOKEN_INTERRUPT 0 - // offset 0x0310, 0x0330, 0x0350, 0x0370, 0x0390, 0x03b0, 0x03d0 - u32 ep_avail; - u32 ep_data; - u32 _unused0 [2]; -} __attribute__ ((packed)); +#include /*-------------------------------------------------------------------------*/ diff --git a/include/linux/usb/net2280.h b/include/linux/usb/net2280.h new file mode 100644 index 000000000000..c602f884f182 --- /dev/null +++ b/include/linux/usb/net2280.h @@ -0,0 +1,444 @@ +/* + * NetChip 2280 high/full speed USB device controller. + * Unlike many such controllers, this one talks PCI. + */ +#ifndef __LINUX_USB_NET2280_H +#define __LINUX_USB_NET2280_H + +/* + * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com) + * Copyright (C) 2003 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*-------------------------------------------------------------------------*/ + +/* NET2280 MEMORY MAPPED REGISTERS + * + * The register layout came from the chip documentation, and the bit + * number definitions were extracted from chip specification. + * + * Use the shift operator ('<<') to build bit masks, with readl/writel + * to access the registers through PCI. + */ + +/* main registers, BAR0 + 0x0000 */ +struct net2280_regs { + // offset 0x0000 + u32 devinit; +#define LOCAL_CLOCK_FREQUENCY 8 +#define FORCE_PCI_RESET 7 +#define PCI_ID 6 +#define PCI_ENABLE 5 +#define FIFO_SOFT_RESET 4 +#define CFG_SOFT_RESET 3 +#define PCI_SOFT_RESET 2 +#define USB_SOFT_RESET 1 +#define M8051_RESET 0 + u32 eectl; +#define EEPROM_ADDRESS_WIDTH 23 +#define EEPROM_CHIP_SELECT_ACTIVE 22 +#define EEPROM_PRESENT 21 +#define EEPROM_VALID 20 +#define EEPROM_BUSY 19 +#define EEPROM_CHIP_SELECT_ENABLE 18 +#define EEPROM_BYTE_READ_START 17 +#define EEPROM_BYTE_WRITE_START 16 +#define EEPROM_READ_DATA 8 +#define EEPROM_WRITE_DATA 0 + u32 eeclkfreq; + u32 _unused0; + // offset 0x0010 + + u32 pciirqenb0; /* interrupt PCI master ... */ +#define SETUP_PACKET_INTERRUPT_ENABLE 7 +#define ENDPOINT_F_INTERRUPT_ENABLE 6 +#define ENDPOINT_E_INTERRUPT_ENABLE 5 +#define ENDPOINT_D_INTERRUPT_ENABLE 4 +#define ENDPOINT_C_INTERRUPT_ENABLE 3 +#define ENDPOINT_B_INTERRUPT_ENABLE 2 +#define ENDPOINT_A_INTERRUPT_ENABLE 1 +#define ENDPOINT_0_INTERRUPT_ENABLE 0 + u32 pciirqenb1; +#define PCI_INTERRUPT_ENABLE 31 +#define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27 +#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26 +#define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25 +#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20 +#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19 +#define PCI_TARGET_ABORT_ASSERTED_INTERRUPT_ENABLE 18 +#define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17 +#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16 +#define GPIO_INTERRUPT_ENABLE 13 +#define DMA_D_INTERRUPT_ENABLE 12 +#define DMA_C_INTERRUPT_ENABLE 11 +#define DMA_B_INTERRUPT_ENABLE 10 +#define DMA_A_INTERRUPT_ENABLE 9 +#define EEPROM_DONE_INTERRUPT_ENABLE 8 +#define VBUS_INTERRUPT_ENABLE 7 +#define CONTROL_STATUS_INTERRUPT_ENABLE 6 +#define ROOT_PORT_RESET_INTERRUPT_ENABLE 4 +#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3 +#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2 +#define RESUME_INTERRUPT_ENABLE 1 +#define SOF_INTERRUPT_ENABLE 0 + u32 cpu_irqenb0; /* ... or onboard 8051 */ +#define SETUP_PACKET_INTERRUPT_ENABLE 7 +#define ENDPOINT_F_INTERRUPT_ENABLE 6 +#define ENDPOINT_E_INTERRUPT_ENABLE 5 +#define ENDPOINT_D_INTERRUPT_ENABLE 4 +#define ENDPOINT_C_INTERRUPT_ENABLE 3 +#define ENDPOINT_B_INTERRUPT_ENABLE 2 +#define ENDPOINT_A_INTERRUPT_ENABLE 1 +#define ENDPOINT_0_INTERRUPT_ENABLE 0 + u32 cpu_irqenb1; +#define CPU_INTERRUPT_ENABLE 31 +#define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27 +#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26 +#define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25 +#define PCI_INTA_INTERRUPT_ENABLE 24 +#define PCI_PME_INTERRUPT_ENABLE 23 +#define PCI_SERR_INTERRUPT_ENABLE 22 +#define PCI_PERR_INTERRUPT_ENABLE 21 +#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20 +#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19 +#define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17 +#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16 +#define GPIO_INTERRUPT_ENABLE 13 +#define DMA_D_INTERRUPT_ENABLE 12 +#define DMA_C_INTERRUPT_ENABLE 11 +#define DMA_B_INTERRUPT_ENABLE 10 +#define DMA_A_INTERRUPT_ENABLE 9 +#define EEPROM_DONE_INTERRUPT_ENABLE 8 +#define VBUS_INTERRUPT_ENABLE 7 +#define CONTROL_STATUS_INTERRUPT_ENABLE 6 +#define ROOT_PORT_RESET_INTERRUPT_ENABLE 4 +#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3 +#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2 +#define RESUME_INTERRUPT_ENABLE 1 +#define SOF_INTERRUPT_ENABLE 0 + + // offset 0x0020 + u32 _unused1; + u32 usbirqenb1; +#define USB_INTERRUPT_ENABLE 31 +#define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27 +#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26 +#define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25 +#define PCI_INTA_INTERRUPT_ENABLE 24 +#define PCI_PME_INTERRUPT_ENABLE 23 +#define PCI_SERR_INTERRUPT_ENABLE 22 +#define PCI_PERR_INTERRUPT_ENABLE 21 +#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20 +#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19 +#define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17 +#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16 +#define GPIO_INTERRUPT_ENABLE 13 +#define DMA_D_INTERRUPT_ENABLE 12 +#define DMA_C_INTERRUPT_ENABLE 11 +#define DMA_B_INTERRUPT_ENABLE 10 +#define DMA_A_INTERRUPT_ENABLE 9 +#define EEPROM_DONE_INTERRUPT_ENABLE 8 +#define VBUS_INTERRUPT_ENABLE 7 +#define CONTROL_STATUS_INTERRUPT_ENABLE 6 +#define ROOT_PORT_RESET_INTERRUPT_ENABLE 4 +#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3 +#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2 +#define RESUME_INTERRUPT_ENABLE 1 +#define SOF_INTERRUPT_ENABLE 0 + u32 irqstat0; +#define INTA_ASSERTED 12 +#define SETUP_PACKET_INTERRUPT 7 +#define ENDPOINT_F_INTERRUPT 6 +#define ENDPOINT_E_INTERRUPT 5 +#define ENDPOINT_D_INTERRUPT 4 +#define ENDPOINT_C_INTERRUPT 3 +#define ENDPOINT_B_INTERRUPT 2 +#define ENDPOINT_A_INTERRUPT 1 +#define ENDPOINT_0_INTERRUPT 0 + u32 irqstat1; +#define POWER_STATE_CHANGE_INTERRUPT 27 +#define PCI_ARBITER_TIMEOUT_INTERRUPT 26 +#define PCI_PARITY_ERROR_INTERRUPT 25 +#define PCI_INTA_INTERRUPT 24 +#define PCI_PME_INTERRUPT 23 +#define PCI_SERR_INTERRUPT 22 +#define PCI_PERR_INTERRUPT 21 +#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT 20 +#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT 19 +#define PCI_RETRY_ABORT_INTERRUPT 17 +#define PCI_MASTER_CYCLE_DONE_INTERRUPT 16 +#define SOF_DOWN_INTERRUPT 14 +#define GPIO_INTERRUPT 13 +#define DMA_D_INTERRUPT 12 +#define DMA_C_INTERRUPT 11 +#define DMA_B_INTERRUPT 10 +#define DMA_A_INTERRUPT 9 +#define EEPROM_DONE_INTERRUPT 8 +#define VBUS_INTERRUPT 7 +#define CONTROL_STATUS_INTERRUPT 6 +#define ROOT_PORT_RESET_INTERRUPT 4 +#define SUSPEND_REQUEST_INTERRUPT 3 +#define SUSPEND_REQUEST_CHANGE_INTERRUPT 2 +#define RESUME_INTERRUPT 1 +#define SOF_INTERRUPT 0 + // offset 0x0030 + u32 idxaddr; + u32 idxdata; + u32 fifoctl; +#define PCI_BASE2_RANGE 16 +#define IGNORE_FIFO_AVAILABILITY 3 +#define PCI_BASE2_SELECT 2 +#define FIFO_CONFIGURATION_SELECT 0 + u32 _unused2; + // offset 0x0040 + u32 memaddr; +#define START 28 +#define DIRECTION 27 +#define FIFO_DIAGNOSTIC_SELECT 24 +#define MEMORY_ADDRESS 0 + u32 memdata0; + u32 memdata1; + u32 _unused3; + // offset 0x0050 + u32 gpioctl; +#define GPIO3_LED_SELECT 12 +#define GPIO3_INTERRUPT_ENABLE 11 +#define GPIO2_INTERRUPT_ENABLE 10 +#define GPIO1_INTERRUPT_ENABLE 9 +#define GPIO0_INTERRUPT_ENABLE 8 +#define GPIO3_OUTPUT_ENABLE 7 +#define GPIO2_OUTPUT_ENABLE 6 +#define GPIO1_OUTPUT_ENABLE 5 +#define GPIO0_OUTPUT_ENABLE 4 +#define GPIO3_DATA 3 +#define GPIO2_DATA 2 +#define GPIO1_DATA 1 +#define GPIO0_DATA 0 + u32 gpiostat; +#define GPIO3_INTERRUPT 3 +#define GPIO2_INTERRUPT 2 +#define GPIO1_INTERRUPT 1 +#define GPIO0_INTERRUPT 0 +} __attribute__ ((packed)); + +/* usb control, BAR0 + 0x0080 */ +struct net2280_usb_regs { + // offset 0x0080 + u32 stdrsp; +#define STALL_UNSUPPORTED_REQUESTS 31 +#define SET_TEST_MODE 16 +#define GET_OTHER_SPEED_CONFIGURATION 15 +#define GET_DEVICE_QUALIFIER 14 +#define SET_ADDRESS 13 +#define ENDPOINT_SET_CLEAR_HALT 12 +#define DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP 11 +#define GET_STRING_DESCRIPTOR_2 10 +#define GET_STRING_DESCRIPTOR_1 9 +#define GET_STRING_DESCRIPTOR_0 8 +#define GET_SET_INTERFACE 6 +#define GET_SET_CONFIGURATION 5 +#define GET_CONFIGURATION_DESCRIPTOR 4 +#define GET_DEVICE_DESCRIPTOR 3 +#define GET_ENDPOINT_STATUS 2 +#define GET_INTERFACE_STATUS 1 +#define GET_DEVICE_STATUS 0 + u32 prodvendid; +#define PRODUCT_ID 16 +#define VENDOR_ID 0 + u32 relnum; + u32 usbctl; +#define SERIAL_NUMBER_INDEX 16 +#define PRODUCT_ID_STRING_ENABLE 13 +#define VENDOR_ID_STRING_ENABLE 12 +#define USB_ROOT_PORT_WAKEUP_ENABLE 11 +#define VBUS_PIN 10 +#define TIMED_DISCONNECT 9 +#define SUSPEND_IMMEDIATELY 7 +#define SELF_POWERED_USB_DEVICE 6 +#define REMOTE_WAKEUP_SUPPORT 5 +#define PME_POLARITY 4 +#define USB_DETECT_ENABLE 3 +#define PME_WAKEUP_ENABLE 2 +#define DEVICE_REMOTE_WAKEUP_ENABLE 1 +#define SELF_POWERED_STATUS 0 + // offset 0x0090 + u32 usbstat; +#define HIGH_SPEED 7 +#define FULL_SPEED 6 +#define GENERATE_RESUME 5 +#define GENERATE_DEVICE_REMOTE_WAKEUP 4 + u32 xcvrdiag; +#define FORCE_HIGH_SPEED_MODE 31 +#define FORCE_FULL_SPEED_MODE 30 +#define USB_TEST_MODE 24 +#define LINE_STATE 16 +#define TRANSCEIVER_OPERATION_MODE 2 +#define TRANSCEIVER_SELECT 1 +#define TERMINATION_SELECT 0 + u32 setup0123; + u32 setup4567; + // offset 0x0090 + u32 _unused0; + u32 ouraddr; +#define FORCE_IMMEDIATE 7 +#define OUR_USB_ADDRESS 0 + u32 ourconfig; +} __attribute__ ((packed)); + +/* pci control, BAR0 + 0x0100 */ +struct net2280_pci_regs { + // offset 0x0100 + u32 pcimstctl; +#define PCI_ARBITER_PARK_SELECT 13 +#define PCI_MULTI LEVEL_ARBITER 12 +#define PCI_RETRY_ABORT_ENABLE 11 +#define DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE 10 +#define DMA_READ_MULTIPLE_ENABLE 9 +#define DMA_READ_LINE_ENABLE 8 +#define PCI_MASTER_COMMAND_SELECT 6 +#define MEM_READ_OR_WRITE 0 +#define IO_READ_OR_WRITE 1 +#define CFG_READ_OR_WRITE 2 +#define PCI_MASTER_START 5 +#define PCI_MASTER_READ_WRITE 4 +#define PCI_MASTER_WRITE 0 +#define PCI_MASTER_READ 1 +#define PCI_MASTER_BYTE_WRITE_ENABLES 0 + u32 pcimstaddr; + u32 pcimstdata; + u32 pcimststat; +#define PCI_ARBITER_CLEAR 2 +#define PCI_EXTERNAL_ARBITER 1 +#define PCI_HOST_MODE 0 +} __attribute__ ((packed)); + +/* dma control, BAR0 + 0x0180 ... array of four structs like this, + * for channels 0..3. see also struct net2280_dma: descriptor + * that can be loaded into some of these registers. + */ +struct net2280_dma_regs { /* [11.7] */ + // offset 0x0180, 0x01a0, 0x01c0, 0x01e0, + u32 dmactl; +#define DMA_SCATTER_GATHER_DONE_INTERRUPT_ENABLE 25 +#define DMA_CLEAR_COUNT_ENABLE 21 +#define DESCRIPTOR_POLLING_RATE 19 +#define POLL_CONTINUOUS 0 +#define POLL_1_USEC 1 +#define POLL_100_USEC 2 +#define POLL_1_MSEC 3 +#define DMA_VALID_BIT_POLLING_ENABLE 18 +#define DMA_VALID_BIT_ENABLE 17 +#define DMA_SCATTER_GATHER_ENABLE 16 +#define DMA_OUT_AUTO_START_ENABLE 4 +#define DMA_PREEMPT_ENABLE 3 +#define DMA_FIFO_VALIDATE 2 +#define DMA_ENABLE 1 +#define DMA_ADDRESS_HOLD 0 + u32 dmastat; +#define DMA_ABORT_DONE_INTERRUPT 27 +#define DMA_SCATTER_GATHER_DONE_INTERRUPT 25 +#define DMA_TRANSACTION_DONE_INTERRUPT 24 +#define DMA_ABORT 1 +#define DMA_START 0 + u32 _unused0 [2]; + // offset 0x0190, 0x01b0, 0x01d0, 0x01f0, + u32 dmacount; +#define VALID_BIT 31 +#define DMA_DIRECTION 30 +#define DMA_DONE_INTERRUPT_ENABLE 29 +#define END_OF_CHAIN 28 +#define DMA_BYTE_COUNT_MASK ((1<<24)-1) +#define DMA_BYTE_COUNT 0 + u32 dmaaddr; + u32 dmadesc; + u32 _unused1; +} __attribute__ ((packed)); + +/* dedicated endpoint registers, BAR0 + 0x0200 */ + +struct net2280_dep_regs { /* [11.8] */ + // offset 0x0200, 0x0210, 0x220, 0x230, 0x240 + u32 dep_cfg; + // offset 0x0204, 0x0214, 0x224, 0x234, 0x244 + u32 dep_rsp; + u32 _unused [2]; +} __attribute__ ((packed)); + +/* configurable endpoint registers, BAR0 + 0x0300 ... array of seven structs + * like this, for ep0 then the configurable endpoints A..F + * ep0 reserved for control; E and F have only 64 bytes of fifo + */ +struct net2280_ep_regs { /* [11.9] */ + // offset 0x0300, 0x0320, 0x0340, 0x0360, 0x0380, 0x03a0, 0x03c0 + u32 ep_cfg; +#define ENDPOINT_BYTE_COUNT 16 +#define ENDPOINT_ENABLE 10 +#define ENDPOINT_TYPE 8 +#define ENDPOINT_DIRECTION 7 +#define ENDPOINT_NUMBER 0 + u32 ep_rsp; +#define SET_NAK_OUT_PACKETS 15 +#define SET_EP_HIDE_STATUS_PHASE 14 +#define SET_EP_FORCE_CRC_ERROR 13 +#define SET_INTERRUPT_MODE 12 +#define SET_CONTROL_STATUS_PHASE_HANDSHAKE 11 +#define SET_NAK_OUT_PACKETS_MODE 10 +#define SET_ENDPOINT_TOGGLE 9 +#define SET_ENDPOINT_HALT 8 +#define CLEAR_NAK_OUT_PACKETS 7 +#define CLEAR_EP_HIDE_STATUS_PHASE 6 +#define CLEAR_EP_FORCE_CRC_ERROR 5 +#define CLEAR_INTERRUPT_MODE 4 +#define CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE 3 +#define CLEAR_NAK_OUT_PACKETS_MODE 2 +#define CLEAR_ENDPOINT_TOGGLE 1 +#define CLEAR_ENDPOINT_HALT 0 + u32 ep_irqenb; +#define SHORT_PACKET_OUT_DONE_INTERRUPT_ENABLE 6 +#define SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE 5 +#define DATA_PACKET_RECEIVED_INTERRUPT_ENABLE 3 +#define DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE 2 +#define DATA_OUT_PING_TOKEN_INTERRUPT_ENABLE 1 +#define DATA_IN_TOKEN_INTERRUPT_ENABLE 0 + u32 ep_stat; +#define FIFO_VALID_COUNT 24 +#define HIGH_BANDWIDTH_OUT_TRANSACTION_PID 22 +#define TIMEOUT 21 +#define USB_STALL_SENT 20 +#define USB_IN_NAK_SENT 19 +#define USB_IN_ACK_RCVD 18 +#define USB_OUT_PING_NAK_SENT 17 +#define USB_OUT_ACK_SENT 16 +#define FIFO_OVERFLOW 13 +#define FIFO_UNDERFLOW 12 +#define FIFO_FULL 11 +#define FIFO_EMPTY 10 +#define FIFO_FLUSH 9 +#define SHORT_PACKET_OUT_DONE_INTERRUPT 6 +#define SHORT_PACKET_TRANSFERRED_INTERRUPT 5 +#define NAK_OUT_PACKETS 4 +#define DATA_PACKET_RECEIVED_INTERRUPT 3 +#define DATA_PACKET_TRANSMITTED_INTERRUPT 2 +#define DATA_OUT_PING_TOKEN_INTERRUPT 1 +#define DATA_IN_TOKEN_INTERRUPT 0 + // offset 0x0310, 0x0330, 0x0350, 0x0370, 0x0390, 0x03b0, 0x03d0 + u32 ep_avail; + u32 ep_data; + u32 _unused0 [2]; +} __attribute__ ((packed)); + +#endif /* __LINUX_USB_NET2280_H */ -- cgit v1.2.3 From 4508a7a734b111b8b7e39986237d84acb1168dd0 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 20 Mar 2006 17:53:53 +1100 Subject: [PATCH] sysfs: Allow sysfs attribute files to be pollable It works like this: Open the file Read all the contents. Call poll requesting POLLERR or POLLPRI (so select/exceptfds works) When poll returns, close the file and go to top of loop. or lseek to start of file and go back to the 'read'. Events are signaled by an object manager calling sysfs_notify(kobj, dir, attr); If the dir is non-NULL, it is used to find a subdirectory which contains the attribute (presumably created by sysfs_create_group). This has a cost of one int per attribute, one wait_queuehead per kobject, one int per open file. The name "sysfs_notify" may be confused with the inotify functionality. Maybe it would be nice to support inotify for sysfs attributes as well? This patch also uses sysfs_notify to allow /sys/block/md*/md/sync_action to be pollable Signed-off-by: Neil Brown Signed-off-by: Greg Kroah-Hartman --- drivers/md/md.c | 1 + fs/sysfs/dir.c | 1 + fs/sysfs/file.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/sysfs/sysfs.h | 1 + include/linux/kobject.h | 2 ++ include/linux/sysfs.h | 6 ++++ lib/kobject.c | 1 + 7 files changed, 88 insertions(+) (limited to 'include/linux') diff --git a/drivers/md/md.c b/drivers/md/md.c index 1ed5152db450..434ca39d19c1 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -163,6 +163,7 @@ void md_new_event(mddev_t *mddev) { atomic_inc(&md_event_count); wake_up(&md_event_waiters); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); } EXPORT_SYMBOL_GPL(md_new_event); diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 6cfdc9a87772..610b5bdbe75b 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -43,6 +43,7 @@ static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent * parent_sd, memset(sd, 0, sizeof(*sd)); atomic_set(&sd->s_count, 1); + atomic_set(&sd->s_event, 0); INIT_LIST_HEAD(&sd->s_children); list_add(&sd->s_sibling, &parent_sd->s_children); sd->s_element = element; diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index f1cb1ddde511..cf3786625bfa 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -57,6 +58,7 @@ struct sysfs_buffer { struct sysfs_ops * ops; struct semaphore sem; int needs_read_fill; + int event; }; @@ -72,6 +74,7 @@ struct sysfs_buffer { */ static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) { + struct sysfs_dirent * sd = dentry->d_fsdata; struct attribute * attr = to_attr(dentry); struct kobject * kobj = to_kobj(dentry->d_parent); struct sysfs_ops * ops = buffer->ops; @@ -83,6 +86,7 @@ static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer if (!buffer->page) return -ENOMEM; + buffer->event = atomic_read(&sd->s_event); count = ops->show(kobj,attr,buffer->page); buffer->needs_read_fill = 0; BUG_ON(count > (ssize_t)PAGE_SIZE); @@ -348,12 +352,84 @@ static int sysfs_release(struct inode * inode, struct file * filp) return 0; } +/* Sysfs attribute files are pollable. The idea is that you read + * the content and then you use 'poll' or 'select' to wait for + * the content to change. When the content changes (assuming the + * manager for the kobject supports notification), poll will + * return POLLERR|POLLPRI, and select will return the fd whether + * it is waiting for read, write, or exceptions. + * Once poll/select indicates that the value has changed, you + * need to close and re-open the file, as simply seeking and reading + * again will not get new data, or reset the state of 'poll'. + * Reminder: this only works for attributes which actively support + * it, and it is not possible to test an attribute from userspace + * to see if it supports poll (Nether 'poll' or 'select' return + * an appropriate error code). When in doubt, set a suitable timeout value. + */ +static unsigned int sysfs_poll(struct file *filp, poll_table *wait) +{ + struct sysfs_buffer * buffer = filp->private_data; + struct kobject * kobj = to_kobj(filp->f_dentry->d_parent); + struct sysfs_dirent * sd = filp->f_dentry->d_fsdata; + int res = 0; + + poll_wait(filp, &kobj->poll, wait); + + if (buffer->event != atomic_read(&sd->s_event)) { + res = POLLERR|POLLPRI; + buffer->needs_read_fill = 1; + } + + return res; +} + + +static struct dentry *step_down(struct dentry *dir, const char * name) +{ + struct dentry * de; + + if (dir == NULL || dir->d_inode == NULL) + return NULL; + + mutex_lock(&dir->d_inode->i_mutex); + de = lookup_one_len(name, dir, strlen(name)); + mutex_unlock(&dir->d_inode->i_mutex); + dput(dir); + if (IS_ERR(de)) + return NULL; + if (de->d_inode == NULL) { + dput(de); + return NULL; + } + return de; +} + +void sysfs_notify(struct kobject * k, char *dir, char *attr) +{ + struct dentry *de = k->dentry; + if (de) + dget(de); + if (de && dir) + de = step_down(de, dir); + if (de && attr) + de = step_down(de, attr); + if (de) { + struct sysfs_dirent * sd = de->d_fsdata; + if (sd) + atomic_inc(&sd->s_event); + wake_up_interruptible(&k->poll); + dput(de); + } +} +EXPORT_SYMBOL_GPL(sysfs_notify); + const struct file_operations sysfs_file_operations = { .read = sysfs_read_file, .write = sysfs_write_file, .llseek = generic_file_llseek, .open = sysfs_open_file, .release = sysfs_release, + .poll = sysfs_poll, }; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 32958a7c50e9..3651ffb5ec09 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -11,6 +11,7 @@ extern int sysfs_make_dirent(struct sysfs_dirent *, struct dentry *, void *, extern int sysfs_add_file(struct dentry *, const struct attribute *, int); extern void sysfs_hash_and_remove(struct dentry * dir, const char * name); +extern struct sysfs_dirent *sysfs_find(struct sysfs_dirent *dir, const char * name); extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **); extern void sysfs_remove_subdir(struct dentry *); diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 4cb1214ec290..dcd0623be892 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #define KOBJ_NAME_LEN 20 @@ -56,6 +57,7 @@ struct kobject { struct kset * kset; struct kobj_type * ktype; struct dentry * dentry; + wait_queue_head_t poll; }; extern int kobject_set_name(struct kobject *, const char *, ...) diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 392da5a6dacb..1ea5d3cda6ae 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -74,6 +74,7 @@ struct sysfs_dirent { umode_t s_mode; struct dentry * s_dentry; struct iattr * s_iattr; + atomic_t s_event; }; #define SYSFS_ROOT 0x0001 @@ -117,6 +118,7 @@ int sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr); int sysfs_create_group(struct kobject *, const struct attribute_group *); void sysfs_remove_group(struct kobject *, const struct attribute_group *); +void sysfs_notify(struct kobject * k, char *dir, char *attr); #else /* CONFIG_SYSFS */ @@ -185,6 +187,10 @@ static inline void sysfs_remove_group(struct kobject * k, const struct attribute ; } +static inline void sysfs_notify(struct kobject * k, char *dir, char *attr) +{ +} + #endif /* CONFIG_SYSFS */ #endif /* _SYSFS_H_ */ diff --git a/lib/kobject.c b/lib/kobject.c index 25204a41a9b0..01d957513940 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -128,6 +128,7 @@ void kobject_init(struct kobject * kobj) { kref_init(&kobj->kref); INIT_LIST_HEAD(&kobj->entry); + init_waitqueue_head(&kobj->poll); kobj->kset = kset_get(kobj->kset); } -- cgit v1.2.3 From d4d7e5dffc4844ef51fe11f497bd774c04413a00 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Fri, 24 Mar 2006 20:45:35 +0100 Subject: [PATCH] BLOCK: delay all uevents until partition table is scanned [BLOCK] delay all uevents until partition table is scanned Here we delay the annoucement of all block device events until the disk's partition table is scanned and all partition devices are already created and sysfs is populated. We have a bunch of old bugs for removable storage handling where we probe successfully for a filesystem on the raw disk, but at the same time the kernel recognizes a partition table and creates partition devices. Currently there is no sane way to tell if partitions will show up or not at the time the disk device is announced to userspace. With the delayed events we can simply skip any probe for a filesystem on the raw disk when we find already present partitions. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- fs/partitions/check.c | 38 ++++++++++++++++++++++++++++++-------- include/linux/genhd.h | 1 + 2 files changed, 31 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/fs/partitions/check.c b/fs/partitions/check.c index af0cb4b9e784..f3b6af071722 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -331,7 +331,9 @@ void delete_partition(struct gendisk *disk, int part) devfs_remove("%s/part%d", disk->devfs_name, part); if (p->holder_dir) kobject_unregister(p->holder_dir); - kobject_unregister(&p->kobj); + kobject_uevent(&p->kobj, KOBJ_REMOVE); + kobject_del(&p->kobj); + kobject_put(&p->kobj); } void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len) @@ -357,7 +359,10 @@ void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len) snprintf(p->kobj.name,KOBJ_NAME_LEN,"%s%d",disk->kobj.name,part); p->kobj.parent = &disk->kobj; p->kobj.ktype = &ktype_part; - kobject_register(&p->kobj); + kobject_init(&p->kobj); + kobject_add(&p->kobj); + if (!disk->part_uevent_suppress) + kobject_uevent(&p->kobj, KOBJ_ADD); partition_sysfs_add_subdir(p); disk->part[part-1] = p; } @@ -395,6 +400,8 @@ void register_disk(struct gendisk *disk) { struct block_device *bdev; char *s; + int i; + struct hd_struct *p; int err; strlcpy(disk->kobj.name,disk->disk_name,KOBJ_NAME_LEN); @@ -406,13 +413,12 @@ void register_disk(struct gendisk *disk) return; disk_sysfs_symlinks(disk); disk_sysfs_add_subdirs(disk); - kobject_uevent(&disk->kobj, KOBJ_ADD); /* No minors to use for partitions */ if (disk->minors == 1) { if (disk->devfs_name[0] != '\0') devfs_add_disk(disk); - return; + goto exit; } /* always add handle for the whole disk */ @@ -420,16 +426,32 @@ void register_disk(struct gendisk *disk) /* No such device (e.g., media were just removed) */ if (!get_capacity(disk)) - return; + goto exit; bdev = bdget_disk(disk, 0); if (!bdev) - return; + goto exit; + /* scan partition table, but suppress uevents */ bdev->bd_invalidated = 1; - if (blkdev_get(bdev, FMODE_READ, 0) < 0) - return; + disk->part_uevent_suppress = 1; + err = blkdev_get(bdev, FMODE_READ, 0); + disk->part_uevent_suppress = 0; + if (err < 0) + goto exit; blkdev_put(bdev); + +exit: + /* announce disk after possible partitions are already created */ + kobject_uevent(&disk->kobj, KOBJ_ADD); + + /* announce possible partitions */ + for (i = 1; i < disk->minors; i++) { + p = disk->part[i-1]; + if (!p || !p->nr_sects) + continue; + kobject_uevent(&p->kobj, KOBJ_ADD); + } } int rescan_partitions(struct gendisk *disk, struct block_device *bdev) diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 10a27f29d692..2ef845b35175 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -105,6 +105,7 @@ struct gendisk { * disks that can't be partitioned. */ char disk_name[32]; /* name of major driver */ struct hd_struct **part; /* [indexed by minor] */ + int part_uevent_suppress; struct block_device_operations *fops; struct request_queue *queue; void *private_data; -- cgit v1.2.3 From 026694920579590c73b5c56705d543568ed5ad41 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 23 Mar 2006 01:38:34 -0800 Subject: [PATCH] pm: print name of failed suspend function Print more diagnostic info to help identify the source of power management suspend failures. Example: usb_hcd_pci_suspend(): pci_set_power_state+0x0/0x1af() returns -22 pci_device_suspend(): usb_hcd_pci_suspend+0x0/0x11b() returns -22 suspend_device(): pci_device_suspend+0x0/0x34() returns -22 Work-in-progress. It needs lots more suspend_report_result() calls sprinkled everywhere. Cc: Patrick Mochel Cc: Pavel Machek Cc: Nigel Cunningham Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/suspend.c | 12 ++++++++++++ drivers/pci/pci-driver.c | 6 ++++-- drivers/pci/pci.c | 6 ++++-- drivers/usb/core/hcd-pci.c | 7 +++---- include/linux/pm.h | 8 ++++++++ 5 files changed, 31 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index bdb60663f2ef..662209d3f42d 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -10,6 +10,8 @@ #include #include +#include +#include #include "../base.h" #include "power.h" @@ -58,6 +60,7 @@ int suspend_device(struct device * dev, pm_message_t state) if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) { dev_dbg(dev, "suspending\n"); error = dev->bus->suspend(dev, state); + suspend_report_result(dev->bus->suspend, error); } up(&dev->sem); return error; @@ -169,3 +172,12 @@ int device_power_down(pm_message_t state) EXPORT_SYMBOL_GPL(device_power_down); +void __suspend_report_result(const char *function, void *fn, int ret) +{ + if (ret) { + printk(KERN_ERR "%s(): ", function); + print_fn_descriptor_symbol("%s() returns ", (unsigned long)fn); + printk("%d\n", ret); + } +} +EXPORT_SYMBOL_GPL(__suspend_report_result); diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index f22f69ac6445..1456759936c5 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -271,10 +271,12 @@ static int pci_device_suspend(struct device * dev, pm_message_t state) struct pci_driver * drv = pci_dev->driver; int i = 0; - if (drv && drv->suspend) + if (drv && drv->suspend) { i = drv->suspend(pci_dev, state); - else + suspend_report_result(drv->suspend, i); + } else { pci_save_state(pci_dev); + } return i; } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index bea1ad1ad5ba..042fa5265cf6 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -307,9 +307,11 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state) * Can enter D0 from any state, but if we can only go deeper * to sleep if we're already in a low power state */ - if (state != PCI_D0 && dev->current_state > state) + if (state != PCI_D0 && dev->current_state > state) { + printk(KERN_ERR "%s(): %s: state=%d, current state=%d\n", + __FUNCTION__, pci_name(dev), state, dev->current_state); return -EINVAL; - else if (dev->current_state == state) + } else if (dev->current_state == state) return 0; /* we're already there */ /* find PCI PM capability in list */ diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 0d2193b69235..66b78404ab34 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -213,11 +213,9 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) if (hcd->driver->suspend) { retval = hcd->driver->suspend(hcd, message); - if (retval) { - dev_dbg (&dev->dev, "PCI pre-suspend fail, %d\n", - retval); + suspend_report_result(hcd->driver->suspend, retval); + if (retval) goto done; - } } synchronize_irq(dev->irq); @@ -263,6 +261,7 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) * some device state (e.g. as part of clock reinit). */ retval = pci_set_power_state (dev, PCI_D3hot); + suspend_report_result(pci_set_power_state, retval); if (retval == 0) { int wake = device_can_wakeup(&hcd->self.root_hub->dev); diff --git a/include/linux/pm.h b/include/linux/pm.h index 6df2585c0169..66be58902b17 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -199,6 +199,12 @@ extern int device_suspend(pm_message_t state); extern int dpm_runtime_suspend(struct device *, pm_message_t); extern void dpm_runtime_resume(struct device *); +extern void __suspend_report_result(const char *function, void *fn, int ret); + +#define suspend_report_result(fn, ret) \ + do { \ + __suspend_report_result(__FUNCTION__, fn, ret); \ + } while (0) #else /* !CONFIG_PM */ @@ -219,6 +225,8 @@ static inline void dpm_runtime_resume(struct device * dev) { } +#define suspend_report_result(fn, ret) do { } while (0) + #endif /* changes to device_may_wakeup take effect on the next pm state change. -- cgit v1.2.3 From 41017f0cac925e4a6bcf3359b75e5538112d4216 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Wed, 8 Feb 2006 17:11:38 +0800 Subject: [PATCH] PCI: MSI(X) save/restore for suspend/resume Add MSI(X) configure sapce save/restore in generic PCI helper. Signed-off-by: Shaohua Li Signed-off-by: Greg Kroah-Hartman --- drivers/pci/msi.c | 227 +++++++++++++++++++++++++++++++++++++++++++++------- drivers/pci/pci.c | 6 ++ drivers/pci/pci.h | 11 +++ include/linux/pci.h | 31 +++++++ 4 files changed, 246 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index a77e79c8c82e..2087a397ef16 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -504,6 +504,201 @@ void pci_scan_msi_device(struct pci_dev *dev) nr_reserved_vectors++; } +#ifdef CONFIG_PM +int pci_save_msi_state(struct pci_dev *dev) +{ + int pos, i = 0; + u16 control; + struct pci_cap_saved_state *save_state; + u32 *cap; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + if (pos <= 0 || dev->no_msi) + return 0; + + pci_read_config_word(dev, msi_control_reg(pos), &control); + if (!(control & PCI_MSI_FLAGS_ENABLE)) + return 0; + + save_state = kzalloc(sizeof(struct pci_cap_saved_state) + sizeof(u32) * 5, + GFP_KERNEL); + if (!save_state) { + printk(KERN_ERR "Out of memory in pci_save_msi_state\n"); + return -ENOMEM; + } + cap = &save_state->data[0]; + + pci_read_config_dword(dev, pos, &cap[i++]); + control = cap[0] >> 16; + pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, &cap[i++]); + if (control & PCI_MSI_FLAGS_64BIT) { + pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, &cap[i++]); + pci_read_config_dword(dev, pos + PCI_MSI_DATA_64, &cap[i++]); + } else + pci_read_config_dword(dev, pos + PCI_MSI_DATA_32, &cap[i++]); + if (control & PCI_MSI_FLAGS_MASKBIT) + pci_read_config_dword(dev, pos + PCI_MSI_MASK_BIT, &cap[i++]); + disable_msi_mode(dev, pos, PCI_CAP_ID_MSI); + save_state->cap_nr = PCI_CAP_ID_MSI; + pci_add_saved_cap(dev, save_state); + return 0; +} + +void pci_restore_msi_state(struct pci_dev *dev) +{ + int i = 0, pos; + u16 control; + struct pci_cap_saved_state *save_state; + u32 *cap; + + save_state = pci_find_saved_cap(dev, PCI_CAP_ID_MSI); + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + if (!save_state || pos <= 0) + return; + cap = &save_state->data[0]; + + control = cap[i++] >> 16; + pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, cap[i++]); + if (control & PCI_MSI_FLAGS_64BIT) { + pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, cap[i++]); + pci_write_config_dword(dev, pos + PCI_MSI_DATA_64, cap[i++]); + } else + pci_write_config_dword(dev, pos + PCI_MSI_DATA_32, cap[i++]); + if (control & PCI_MSI_FLAGS_MASKBIT) + pci_write_config_dword(dev, pos + PCI_MSI_MASK_BIT, cap[i++]); + pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); + enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); + pci_remove_saved_cap(save_state); + kfree(save_state); +} + +int pci_save_msix_state(struct pci_dev *dev) +{ + int pos; + u16 control; + struct pci_cap_saved_state *save_state; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); + if (pos <= 0 || dev->no_msi) + return 0; + + pci_read_config_word(dev, msi_control_reg(pos), &control); + if (!(control & PCI_MSIX_FLAGS_ENABLE)) + return 0; + save_state = kzalloc(sizeof(struct pci_cap_saved_state) + sizeof(u16), + GFP_KERNEL); + if (!save_state) { + printk(KERN_ERR "Out of memory in pci_save_msix_state\n"); + return -ENOMEM; + } + *((u16 *)&save_state->data[0]) = control; + + disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); + save_state->cap_nr = PCI_CAP_ID_MSIX; + pci_add_saved_cap(dev, save_state); + return 0; +} + +void pci_restore_msix_state(struct pci_dev *dev) +{ + u16 save; + int pos; + int vector, head, tail = 0; + void __iomem *base; + int j; + struct msg_address address; + struct msg_data data; + struct msi_desc *entry; + int temp; + struct pci_cap_saved_state *save_state; + + save_state = pci_find_saved_cap(dev, PCI_CAP_ID_MSIX); + if (!save_state) + return; + save = *((u16 *)&save_state->data[0]); + pci_remove_saved_cap(save_state); + kfree(save_state); + + pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); + if (pos <= 0) + return; + + /* route the table */ + temp = dev->irq; + if (msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) + return; + vector = head = dev->irq; + while (head != tail) { + entry = msi_desc[vector]; + base = entry->mask_base; + j = entry->msi_attrib.entry_nr; + + msi_address_init(&address); + msi_data_init(&data, vector); + + address.lo_address.value &= MSI_ADDRESS_DEST_ID_MASK; + address.lo_address.value |= entry->msi_attrib.current_cpu << + MSI_TARGET_CPU_SHIFT; + + writel(address.lo_address.value, + base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); + writel(address.hi_address, + base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); + writel(*(u32*)&data, + base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_DATA_OFFSET); + + tail = msi_desc[vector]->link.tail; + vector = tail; + } + dev->irq = temp; + + pci_write_config_word(dev, msi_control_reg(pos), save); + enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); +} +#endif + +static void msi_register_init(struct pci_dev *dev, struct msi_desc *entry) +{ + struct msg_address address; + struct msg_data data; + int pos, vector = dev->irq; + u16 control; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + pci_read_config_word(dev, msi_control_reg(pos), &control); + /* Configure MSI capability structure */ + msi_address_init(&address); + msi_data_init(&data, vector); + entry->msi_attrib.current_cpu = ((address.lo_address.u.dest_id >> + MSI_TARGET_CPU_SHIFT) & MSI_TARGET_CPU_MASK); + pci_write_config_dword(dev, msi_lower_address_reg(pos), + address.lo_address.value); + if (is_64bit_address(control)) { + pci_write_config_dword(dev, + msi_upper_address_reg(pos), address.hi_address); + pci_write_config_word(dev, + msi_data_reg(pos, 1), *((u32*)&data)); + } else + pci_write_config_word(dev, + msi_data_reg(pos, 0), *((u32*)&data)); + if (entry->msi_attrib.maskbit) { + unsigned int maskbits, temp; + /* All MSIs are unmasked by default, Mask them all */ + pci_read_config_dword(dev, + msi_mask_bits_reg(pos, is_64bit_address(control)), + &maskbits); + temp = (1 << multi_msi_capable(control)); + temp = ((temp - 1) & ~temp); + maskbits |= temp; + pci_write_config_dword(dev, + msi_mask_bits_reg(pos, is_64bit_address(control)), + maskbits); + } +} + /** * msi_capability_init - configure device's MSI capability structure * @dev: pointer to the pci_dev data structure of MSI device function @@ -516,8 +711,6 @@ void pci_scan_msi_device(struct pci_dev *dev) static int msi_capability_init(struct pci_dev *dev) { struct msi_desc *entry; - struct msg_address address; - struct msg_data data; int pos, vector; u16 control; @@ -549,33 +742,8 @@ static int msi_capability_init(struct pci_dev *dev) /* Replace with MSI handler */ irq_handler_init(PCI_CAP_ID_MSI, vector, entry->msi_attrib.maskbit); /* Configure MSI capability structure */ - msi_address_init(&address); - msi_data_init(&data, vector); - entry->msi_attrib.current_cpu = ((address.lo_address.u.dest_id >> - MSI_TARGET_CPU_SHIFT) & MSI_TARGET_CPU_MASK); - pci_write_config_dword(dev, msi_lower_address_reg(pos), - address.lo_address.value); - if (is_64bit_address(control)) { - pci_write_config_dword(dev, - msi_upper_address_reg(pos), address.hi_address); - pci_write_config_word(dev, - msi_data_reg(pos, 1), *((u32*)&data)); - } else - pci_write_config_word(dev, - msi_data_reg(pos, 0), *((u32*)&data)); - if (entry->msi_attrib.maskbit) { - unsigned int maskbits, temp; - /* All MSIs are unmasked by default, Mask them all */ - pci_read_config_dword(dev, - msi_mask_bits_reg(pos, is_64bit_address(control)), - &maskbits); - temp = (1 << multi_msi_capable(control)); - temp = ((temp - 1) & ~temp); - maskbits |= temp; - pci_write_config_dword(dev, - msi_mask_bits_reg(pos, is_64bit_address(control)), - maskbits); - } + msi_register_init(dev, entry); + attach_msi_entry(entry, vector); /* Set MSI enabled bits */ enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); @@ -731,6 +899,7 @@ int pci_enable_msi(struct pci_dev* dev) vector_irq[dev->irq] = -1; nr_released_vectors--; spin_unlock_irqrestore(&msi_lock, flags); + msi_register_init(dev, msi_desc[dev->irq]); enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); return 0; } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index bea1ad1ad5ba..69a617d21824 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -444,6 +444,10 @@ pci_save_state(struct pci_dev *dev) /* XXX: 100% dword access ok here? */ for (i = 0; i < 16; i++) pci_read_config_dword(dev, i * 4,&dev->saved_config_space[i]); + if ((i = pci_save_msi_state(dev)) != 0) + return i; + if ((i = pci_save_msix_state(dev)) != 0) + return i; return 0; } @@ -458,6 +462,8 @@ pci_restore_state(struct pci_dev *dev) for (i = 0; i < 16; i++) pci_write_config_dword(dev,i * 4, dev->saved_config_space[i]); + pci_restore_msi_state(dev); + pci_restore_msix_state(dev); return 0; } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 8f3fb47ea671..30630cbe2fe3 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -55,6 +55,17 @@ void pci_no_msi(void); static inline void disable_msi_mode(struct pci_dev *dev, int pos, int type) { } static inline void pci_no_msi(void) { } #endif +#if defined(CONFIG_PCI_MSI) && defined(CONFIG_PM) +int pci_save_msi_state(struct pci_dev *dev); +int pci_save_msix_state(struct pci_dev *dev); +void pci_restore_msi_state(struct pci_dev *dev); +void pci_restore_msix_state(struct pci_dev *dev); +#else +static inline int pci_save_msi_state(struct pci_dev *dev) { return 0; } +static inline int pci_save_msix_state(struct pci_dev *dev) { return 0; } +static inline void pci_restore_msi_state(struct pci_dev *dev) {} +static inline void pci_restore_msix_state(struct pci_dev *dev) {} +#endif extern int pcie_mch_quirk; extern struct device_attribute pci_dev_attrs[]; diff --git a/include/linux/pci.h b/include/linux/pci.h index 0aad5a378e95..15e1675edef9 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -100,6 +100,12 @@ enum pci_bus_flags { PCI_BUS_FLAGS_NO_MSI = (pci_bus_flags_t) 1, }; +struct pci_cap_saved_state { + struct hlist_node next; + char cap_nr; + u32 data[0]; +}; + /* * The pci_dev structure is used to describe PCI devices. */ @@ -159,6 +165,7 @@ struct pci_dev { unsigned int block_ucfg_access:1; /* userspace config space access is blocked */ u32 saved_config_space[16]; /* config space saved at suspend time */ + struct hlist_head saved_cap_space; struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */ int rom_attr_enabled; /* has display of the rom attribute been enabled? */ struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */ @@ -169,6 +176,30 @@ struct pci_dev { #define to_pci_dev(n) container_of(n, struct pci_dev, dev) #define for_each_pci_dev(d) while ((d = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, d)) != NULL) +static inline struct pci_cap_saved_state *pci_find_saved_cap( + struct pci_dev *pci_dev,char cap) +{ + struct pci_cap_saved_state *tmp; + struct hlist_node *pos; + + hlist_for_each_entry(tmp, pos, &pci_dev->saved_cap_space, next) { + if (tmp->cap_nr == cap) + return tmp; + } + return NULL; +} + +static inline void pci_add_saved_cap(struct pci_dev *pci_dev, + struct pci_cap_saved_state *new_cap) +{ + hlist_add_head(&new_cap->next, &pci_dev->saved_cap_space); +} + +static inline void pci_remove_saved_cap(struct pci_cap_saved_state *cap) +{ + hlist_del(&cap->next); +} + /* * For PCI devices, the region numbers are assigned this way: * -- cgit v1.2.3 From 5da594b1c523dffa19ebe7630e1ca285f439bd03 Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Mon, 20 Mar 2006 14:33:56 -0500 Subject: [PATCH] pci_ids.h: correct naming of 1022:7450 (AMD 8131 Bridge) The naming of the constant defined for PCI ID 1022:7450 does not seem to match the information at http://pciids.sourceforge.net/: http://pci-ids.ucw.cz/iii/?i=1022 There 1022:7450 is listed as "AMD-8131 PCI-X Bridge" while 1022:7451 is listed as "AMD-8131 PCI-X IOAPIC". Yet, the current definition for 0x7450 is PCI_DEVICE_ID_AMD_8131_APIC. It seems to me like that name should map to 0x7451, while a name like PCI_DEVICE_ID_AMD_8131_BRIDGE should map to 0x7450. Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/pci/quirks.c | 2 +- include/linux/pci_ids.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 4970f47be72c..2158feffe24a 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -592,7 +592,7 @@ static void __init quirk_amd_8131_ioapic(struct pci_dev *dev) pci_write_config_byte( dev, AMD8131_MISC, tmp); } } -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_APIC, quirk_amd_8131_ioapic ); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic); static void __init quirk_svw_msi(struct pci_dev *dev) { diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 870fe38378b1..8d03e10212f5 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -497,7 +497,8 @@ #define PCI_DEVICE_ID_AMD_8111_SMBUS 0x746b #define PCI_DEVICE_ID_AMD_8111_AUDIO 0x746d #define PCI_DEVICE_ID_AMD_8151_0 0x7454 -#define PCI_DEVICE_ID_AMD_8131_APIC 0x7450 +#define PCI_DEVICE_ID_AMD_8131_BRIDGE 0x7450 +#define PCI_DEVICE_ID_AMD_8131_APIC 0x7451 #define PCI_DEVICE_ID_AMD_CS5536_ISA 0x2090 #define PCI_DEVICE_ID_AMD_CS5536_FLASH 0x2091 #define PCI_DEVICE_ID_AMD_CS5536_AUDIO 0x2093 -- cgit v1.2.3 From e778272dd547d53dedf92240e8b3dbdee44b87b6 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Thu, 30 Mar 2006 12:55:10 -0800 Subject: [PATCH] PCI: fix sparse warning about pci_bus_flags Sparse warns about casting to a __bitwise type. However, it's correct to do when defining the enum for pci_bus_flags_t, so add a __force to quiet the warnings. This will fix getting include/linux/pci.h:100:26: warning: cast to restricted type from sparse all over the build. Signed-off-by: Roland Dreier Signed-off-by: Greg Kroah-Hartman --- include/linux/pci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/pci.h b/include/linux/pci.h index 15e1675edef9..3a6a4e37a482 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -97,7 +97,7 @@ enum pci_channel_state { typedef unsigned short __bitwise pci_bus_flags_t; enum pci_bus_flags { - PCI_BUS_FLAGS_NO_MSI = (pci_bus_flags_t) 1, + PCI_BUS_FLAGS_NO_MSI = (__force pci_bus_flags_t) 1, }; struct pci_cap_saved_state { -- cgit v1.2.3 From 78a596b4490e17b9990d87b9d468ef5bb70daa10 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 31 Mar 2006 01:38:12 -0800 Subject: [PATCH] remove kernel/power/pm.c:pm_unregister() Since the last user is removed in -mm, we can now remove this long deprecated function. Signed-off-by: Adrian Bunk Cc: Pavel Machek Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- include/linux/pm_legacy.h | 7 ------- kernel/power/pm.c | 20 -------------------- 2 files changed, 27 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pm_legacy.h b/include/linux/pm_legacy.h index 1252b45face1..008932d73c35 100644 --- a/include/linux/pm_legacy.h +++ b/include/linux/pm_legacy.h @@ -15,11 +15,6 @@ extern int pm_active; struct pm_dev __deprecated * pm_register(pm_dev_t type, unsigned long id, pm_callback callback); -/* - * Unregister a device with power management - */ -void __deprecated pm_unregister(struct pm_dev *dev); - /* * Unregister all devices with matching callback */ @@ -41,8 +36,6 @@ static inline struct pm_dev *pm_register(pm_dev_t type, return NULL; } -static inline void pm_unregister(struct pm_dev *dev) {} - static inline void pm_unregister_all(pm_callback callback) {} static inline int pm_send_all(pm_request_t rqst, void *data) diff --git a/kernel/power/pm.c b/kernel/power/pm.c index 0f6908cce1dd..84063ac8fcfc 100644 --- a/kernel/power/pm.c +++ b/kernel/power/pm.c @@ -75,25 +75,6 @@ struct pm_dev *pm_register(pm_dev_t type, return dev; } -/** - * pm_unregister - unregister a device with power management - * @dev: device to unregister - * - * Remove a device from the power management notification lists. The - * dev passed must be a handle previously returned by pm_register. - */ - -void pm_unregister(struct pm_dev *dev) -{ - if (dev) { - mutex_lock(&pm_devs_lock); - list_del(&dev->entry); - mutex_unlock(&pm_devs_lock); - - kfree(dev); - } -} - static void __pm_unregister(struct pm_dev *dev) { if (dev) { @@ -258,7 +239,6 @@ int pm_send_all(pm_request_t rqst, void *data) } EXPORT_SYMBOL(pm_register); -EXPORT_SYMBOL(pm_unregister); EXPORT_SYMBOL(pm_unregister_all); EXPORT_SYMBOL(pm_send_all); EXPORT_SYMBOL(pm_active); -- cgit v1.2.3 From 64541d19702cfdb7ea946fdc20faee849f6874b1 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 14 Apr 2006 12:43:15 -0600 Subject: [PATCH] kill unushed __put_task_struct_cb Somehow in the midst of dotting i's and crossing t's during the merge up to rc1 we wound up keeping __put_task_struct_cb when it should have been killed as it no longer has any users. Sorry I probably should have caught this while it was still in the -mm tree. Having the old code there gets confusing when reading through the code and trying to understand what is happening. Signed-off-by: Eric W. Biederman Signed-off-by: Linus Torvalds --- include/linux/sched.h | 1 - kernel/fork.c | 6 ------ 2 files changed, 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index e3539c14e47e..b7d31e2e1729 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -911,7 +911,6 @@ static inline int pid_alive(struct task_struct *p) extern void free_task(struct task_struct *tsk); #define get_task_struct(tsk) do { atomic_inc(&(tsk)->usage); } while(0) -extern void __put_task_struct_cb(struct rcu_head *rhp); extern void __put_task_struct(struct task_struct *t); static inline void put_task_struct(struct task_struct *t) diff --git a/kernel/fork.c b/kernel/fork.c index 3384eb89cb1c..54b15f8cda53 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -124,12 +124,6 @@ void __put_task_struct(struct task_struct *tsk) free_task(tsk); } -void __put_task_struct_cb(struct rcu_head *rhp) -{ - struct task_struct *tsk = container_of(rhp, struct task_struct, rcu); - __put_task_struct(tsk); -} - void __init fork_init(unsigned long mempages) { #ifndef __HAVE_ARCH_TASK_STRUCT_ALLOCATOR -- cgit v1.2.3 From 13626a887fad4220bc7ca85f4b42ca8cfb805e11 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Sun, 2 Apr 2006 13:17:58 +0100 Subject: [MIPS] MV6434x: The name of the CPP symbol is __mips__, not __MIPS__. Signed-off-by: Ralf Baechle --- include/linux/mv643xx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/mv643xx.h b/include/linux/mv643xx.h index 955d3069d727..edfa012fad3a 100644 --- a/include/linux/mv643xx.h +++ b/include/linux/mv643xx.h @@ -13,7 +13,7 @@ #ifndef __ASM_MV643XX_H #define __ASM_MV643XX_H -#ifdef __MIPS__ +#ifdef __mips__ #include #include #endif -- cgit v1.2.3 From 5e85d4abe3f43bb5362f384bab0e20ef082ce0b5 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 18 Apr 2006 22:20:16 -0700 Subject: [PATCH] task: Make task list manipulations RCU safe While we can currently walk through thread groups, process groups, and sessions with just the rcu_read_lock, this opens the door to walking the entire task list. We already have all of the other RCU guarantees so there is no cost in doing this, this should be enough so that proc can stop taking the tasklist lock during readdir. prev_task was killed because it has no users, and using it will miss new tasks when doing an rcu traversal. Signed-off-by: Eric W. Biederman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 2 +- include/linux/sched.h | 3 +-- kernel/exit.c | 2 +- kernel/fork.c | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index 4121bb559739..3a79d97ac234 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -712,7 +712,7 @@ static int de_thread(struct task_struct *tsk) attach_pid(current, PIDTYPE_PID, current->pid); attach_pid(current, PIDTYPE_PGID, current->signal->pgrp); attach_pid(current, PIDTYPE_SID, current->signal->session); - list_add_tail(¤t->tasks, &init_task.tasks); + list_add_tail_rcu(¤t->tasks, &init_task.tasks); current->group_leader = current; leader->group_leader = current; diff --git a/include/linux/sched.h b/include/linux/sched.h index b7d31e2e1729..29b7d4f87d20 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1192,8 +1192,7 @@ extern void wait_task_inactive(task_t * p); #define remove_parent(p) list_del_init(&(p)->sibling) #define add_parent(p) list_add_tail(&(p)->sibling,&(p)->parent->children) -#define next_task(p) list_entry((p)->tasks.next, struct task_struct, tasks) -#define prev_task(p) list_entry((p)->tasks.prev, struct task_struct, tasks) +#define next_task(p) list_entry(rcu_dereference((p)->tasks.next), struct task_struct, tasks) #define for_each_process(p) \ for (p = &init_task ; (p = next_task(p)) != &init_task ; ) diff --git a/kernel/exit.c b/kernel/exit.c index 1a9787ac6173..f86434d7b3d1 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -56,7 +56,7 @@ static void __unhash_process(struct task_struct *p) detach_pid(p, PIDTYPE_PGID); detach_pid(p, PIDTYPE_SID); - list_del_init(&p->tasks); + list_del_rcu(&p->tasks); __get_cpu_var(process_counts)--; } list_del_rcu(&p->thread_group); diff --git a/kernel/fork.c b/kernel/fork.c index 54b15f8cda53..34515772611e 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1204,7 +1204,7 @@ static task_t *copy_process(unsigned long clone_flags, attach_pid(p, PIDTYPE_PGID, process_group(p)); attach_pid(p, PIDTYPE_SID, p->signal->session); - list_add_tail(&p->tasks, &init_task.tasks); + list_add_tail_rcu(&p->tasks, &init_task.tasks); __get_cpu_var(process_counts)++; } attach_pid(p, PIDTYPE_PID, p->pid); -- cgit v1.2.3 From 6e89280184e4990f5ea80d2504af89b6099523c4 Mon Sep 17 00:00:00 2001 From: Anatoli Antonovitch Date: Tue, 18 Apr 2006 22:22:05 -0700 Subject: [PATCH] ide: ATI SB600 IDE support Add support for the IDE device on ATI SB600 Signed-off-by: Felix Kuehling Acked-by: Bartlomiej Zolnierkiewicz Cc: Alan Cox Acked-by: Jeff Garzik Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/ide/pci/atiixp.c | 1 + include/linux/pci_ids.h | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'include/linux') diff --git a/drivers/ide/pci/atiixp.c b/drivers/ide/pci/atiixp.c index df9ee9a78435..900efd1da587 100644 --- a/drivers/ide/pci/atiixp.c +++ b/drivers/ide/pci/atiixp.c @@ -348,6 +348,7 @@ static struct pci_device_id atiixp_pci_tbl[] = { { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP200_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { 0, }, }; MODULE_DEVICE_TABLE(pci, atiixp_pci_tbl); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 8d03e10212f5..d6fe048376ab 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -356,6 +356,10 @@ #define PCI_DEVICE_ID_ATI_IXP300_SATA 0x436e #define PCI_DEVICE_ID_ATI_IXP400_IDE 0x4376 #define PCI_DEVICE_ID_ATI_IXP400_SATA 0x4379 +#define PCI_DEVICE_ID_ATI_IXP400_SATA2 0x437a +#define PCI_DEVICE_ID_ATI_IXP600_SATA 0x4380 +#define PCI_DEVICE_ID_ATI_IXP600_SRAID 0x4381 +#define PCI_DEVICE_ID_ATI_IXP600_IDE 0x438c #define PCI_VENDOR_ID_VLSI 0x1004 #define PCI_DEVICE_ID_VLSI_82C592 0x0005 -- cgit v1.2.3 From d3a7b202995421631f486313aacf9ab2ad48b2c8 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 18 Apr 2006 22:22:07 -0700 Subject: [PATCH] remove the obsolete IDEPCI_FLAG_FORCE_PDC Noted by Sergei Shtylylov Signed-off-by: Adrian Bunk Acked-by: Bartlomiej Zolnierkiewicz Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/ide/pci/pdc202xx_old.c | 2 -- drivers/ide/setup-pci.c | 13 ------------- include/linux/ide.h | 1 - 3 files changed, 16 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index 6f8f8645b02c..7ce5bf783688 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -798,7 +798,6 @@ static ide_pci_device_t pdc202xx_chipsets[] __devinitdata = { .autodma = AUTODMA, .bootable = OFF_BOARD, .extra = 48, - .flags = IDEPCI_FLAG_FORCE_PDC, },{ /* 2 */ .name = "PDC20263", .init_setup = init_setup_pdc202ata4, @@ -819,7 +818,6 @@ static ide_pci_device_t pdc202xx_chipsets[] __devinitdata = { .autodma = AUTODMA, .bootable = OFF_BOARD, .extra = 48, - .flags = IDEPCI_FLAG_FORCE_PDC, },{ /* 4 */ .name = "PDC20267", .init_setup = init_setup_pdc202xx, diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index 7ebf992e8c2f..462ed3006c30 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -580,7 +580,6 @@ void ide_pci_setup_ports(struct pci_dev *dev, ide_pci_device_t *d, int pciirq, a int port; int at_least_one_hwif_enabled = 0; ide_hwif_t *hwif, *mate = NULL; - static int secondpdc = 0; u8 tmp; index->all = 0xf0f0; @@ -592,21 +591,9 @@ void ide_pci_setup_ports(struct pci_dev *dev, ide_pci_device_t *d, int pciirq, a for (port = 0; port <= 1; ++port) { ide_pci_enablebit_t *e = &(d->enablebits[port]); - /* - * If this is a Promise FakeRaid controller, - * the 2nd controller will be marked as - * disabled while it is actually there and enabled - * by the bios for raid purposes. - * Skip the normal "is it enabled" test for those. - */ - if ((d->flags & IDEPCI_FLAG_FORCE_PDC) && - (secondpdc++==1) && (port==1)) - goto controller_ok; - if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) || (tmp & e->mask) != e->val)) continue; /* port not enabled */ -controller_ok: if (d->channels <= port) break; diff --git a/include/linux/ide.h b/include/linux/ide.h index 8d2db412ba9c..a8bef1d1371c 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1220,7 +1220,6 @@ typedef struct ide_pci_enablebit_s { enum { /* Uses ISA control ports not PCI ones. */ IDEPCI_FLAG_ISA_PORTS = (1 << 0), - IDEPCI_FLAG_FORCE_PDC = (1 << 1), }; typedef struct ide_pci_device_s { -- cgit v1.2.3 From 7866babad542bb5e1dc95deb5800b577abef58dd Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 18 Apr 2006 13:14:13 -0400 Subject: NFS: fix PROC_FS=n compile error fs/built-in.o: In function `nfs_show_stats':inode.c:(.text+0x15481a): undefined reference to `rpc_print_iostats' net/built-in.o: In function `rpc_destroy_client': undefined reference to `rpc_free_iostats' net/built-in.o: In function `rpc_clone_client': undefined reference to `rpc_alloc_iostats' net/built-in.o: In function `rpc_new_client': undefined reference to `rpc_alloc_iostats' net/built-in.o: In function `xprt_release': undefined reference to `rpc_count_iostats' make: *** [.tmp_vmlinux1] Error 1 Signed-off-by: Adrian Bunk Cc: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Trond Myklebust --- include/linux/sunrpc/metrics.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sunrpc/metrics.h b/include/linux/sunrpc/metrics.h index 8f96e9dc369a..77f78e56c481 100644 --- a/include/linux/sunrpc/metrics.h +++ b/include/linux/sunrpc/metrics.h @@ -69,9 +69,21 @@ struct rpc_clnt; /* * EXPORTed functions for managing rpc_iostats structures */ + +#ifdef CONFIG_PROC_FS + struct rpc_iostats * rpc_alloc_iostats(struct rpc_clnt *); void rpc_count_iostats(struct rpc_task *); void rpc_print_iostats(struct seq_file *, struct rpc_clnt *); void rpc_free_iostats(struct rpc_iostats *); +#else /* CONFIG_PROC_FS */ + +static inline struct rpc_iostats *rpc_alloc_iostats(struct rpc_clnt *clnt) { return NULL; } +static inline void rpc_count_iostats(struct rpc_task *task) {} +static inline void rpc_print_iostats(struct seq_file *seq, struct rpc_clnt *clnt) {} +static inline void rpc_free_iostats(struct rpc_iostats *stats) {} + +#endif /* CONFIG_PROC_FS */ + #endif /* _LINUX_SUNRPC_METRICS_H */ -- cgit v1.2.3 From e99170ff3b799a9fd43d538932a9231fac1de9d4 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 18 Apr 2006 13:21:42 -0400 Subject: NFS,SUNRPC: Fix compiler warnings if CONFIG_PROC_FS & CONFIG_SYSCTL are unset Signed-off-by: Trond Myklebust --- fs/nfs/direct.c | 8 +++----- fs/nfs/file.c | 5 ++--- include/linux/sunrpc/xprt.h | 1 + 3 files changed, 6 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 0f583cb16ddb..3c72b0c07283 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -112,10 +112,9 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode */ ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t pos, unsigned long nr_segs) { - struct dentry *dentry = iocb->ki_filp->f_dentry; - dprintk("NFS: nfs_direct_IO (%s) off/no(%Ld/%lu) EINVAL\n", - dentry->d_name.name, (long long) pos, nr_segs); + iocb->ki_filp->f_dentry->d_name.name, + (long long) pos, nr_segs); return -EINVAL; } @@ -468,7 +467,6 @@ static const struct rpc_call_ops nfs_commit_direct_ops = { static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq) { struct nfs_write_data *data = dreq->commit_data; - struct rpc_task *task = &data->task; data->inode = dreq->inode; data->cred = dreq->ctx->cred; @@ -489,7 +487,7 @@ static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq) /* Note: task.tk_ops->rpc_release will free dreq->commit_data */ dreq->commit_data = NULL; - dprintk("NFS: %5u initiated commit call\n", task->tk_pid); + dprintk("NFS: %5u initiated commit call\n", data->task.tk_pid); lock_kernel(); rpc_execute(&data->task); diff --git a/fs/nfs/file.c b/fs/nfs/file.c index f1df2c8d9259..fade02c15e6e 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -534,10 +534,9 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) */ static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) { - struct inode * inode = filp->f_mapping->host; - dprintk("NFS: nfs_flock(f=%s/%ld, t=%x, fl=%x)\n", - inode->i_sb->s_id, inode->i_ino, + filp->f_dentry->d_inode->i_sb->s_id, + filp->f_dentry->d_inode->i_ino, fl->fl_type, fl->fl_flags); /* diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 7eebbab7160b..e8bbe8118de8 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -53,6 +53,7 @@ struct rpc_timeout { struct rpc_task; struct rpc_xprt; +struct seq_file; /* * This describes a complete RPC request -- cgit v1.2.3 From dc6de33674608f978ec29f5c2f7e3af458c06f78 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 20 Apr 2006 00:10:50 -0700 Subject: [NET]: Add skb->truesize assertion checking. Add some sanity checking. truesize should be at least sizeof(struct sk_buff) plus the current packet length. If not, then truesize is seriously mangled and deserves a kernel log message. Currently we'll do the check for release of stream socket buffers. But we can add checks to more spots over time. Incorporating ideas from Herbert Xu. Signed-off-by: David S. Miller --- include/linux/skbuff.h | 7 +++++++ include/net/sock.h | 1 + net/core/skbuff.c | 8 ++++++++ net/core/stream.c | 1 + 4 files changed, 17 insertions(+) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index c4619a428d9b..f8f234708b98 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -344,6 +344,13 @@ extern void skb_over_panic(struct sk_buff *skb, int len, void *here); extern void skb_under_panic(struct sk_buff *skb, int len, void *here); +extern void skb_truesize_bug(struct sk_buff *skb); + +static inline void skb_truesize_check(struct sk_buff *skb) +{ + if (unlikely((int)skb->truesize < sizeof(struct sk_buff) + skb->len)) + skb_truesize_bug(skb); +} extern int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb, int getfrag(void *from, char *to, int offset, diff --git a/include/net/sock.h b/include/net/sock.h index af2b0544586e..ff8b0dad7b0f 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -454,6 +454,7 @@ static inline void sk_stream_set_owner_r(struct sk_buff *skb, struct sock *sk) static inline void sk_stream_free_skb(struct sock *sk, struct sk_buff *skb) { + skb_truesize_check(skb); sock_set_flag(sk, SOCK_QUEUE_SHRUNK); sk->sk_wmem_queued -= skb->truesize; sk->sk_forward_alloc += skb->truesize; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 09464fa8d72f..fb3770f9c094 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -112,6 +112,14 @@ void skb_under_panic(struct sk_buff *skb, int sz, void *here) BUG(); } +void skb_truesize_bug(struct sk_buff *skb) +{ + printk(KERN_ERR "SKB BUG: Invalid truesize (%u) " + "len=%u, sizeof(sk_buff)=%Zd\n", + skb->truesize, skb->len, sizeof(struct sk_buff)); +} +EXPORT_SYMBOL(skb_truesize_bug); + /* Allocate a new skbuff. We do this ourselves so we can fill in a few * 'private' fields and also do memory statistics to find all the * [BEEP] leaks. diff --git a/net/core/stream.c b/net/core/stream.c index 35e25259fd95..e9489696f694 100644 --- a/net/core/stream.c +++ b/net/core/stream.c @@ -176,6 +176,7 @@ void sk_stream_rfree(struct sk_buff *skb) { struct sock *sk = skb->sk; + skb_truesize_check(skb); atomic_sub(skb->truesize, &sk->sk_rmem_alloc); sk->sk_forward_alloc += skb->truesize; } -- cgit v1.2.3 From 72b38d436e4cd18185de11f4b48a6e62eb104644 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Thu, 20 Apr 2006 02:43:23 -0700 Subject: [PATCH] memory_hotplug.h cleanup We don't have to #if guard prototypes. This also fixes a bug observed by Randy Dunlap due to a misspelled option in the #if. Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memory_hotplug.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index 4ca3e6ad03ec..911206386171 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -99,10 +99,7 @@ static inline int __remove_pages(struct zone *zone, unsigned long start_pfn, return -ENOSYS; } -#if defined(CONFIG_MEMORY_HOTPLUG) || defined(CONFIG_ACPI_HOTPLUG_MEMORY) \ - || defined(CONFIG_ACPI_HOTPLUG_MEMORY_MODULE) extern int add_memory(u64 start, u64 size); extern int remove_memory(u64 start, u64 size); -#endif #endif /* __LINUX_MEMORY_HOTPLUG_H */ -- cgit v1.2.3 From 55fe5866366ae42f259f27ae5962eb267d9ce172 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 24 Apr 2006 17:16:28 -0700 Subject: [NETFILTER]: Fix compat_xt_counters alignment for non-x86 Some (?) non-x86 architectures require 8byte alignment for u_int64_t even when compiled for 32bit, using u_int32_t in compat_xt_counters breaks on these architectures, use u_int64_t for everything but x86. Reported by Andreas Schwab . Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter/x_tables.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index f6bdef82a322..38701454e197 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -361,7 +361,11 @@ struct compat_xt_entry_target struct compat_xt_counters { +#if defined(CONFIG_X86_64) || defined(CONFIG_IA64) u_int32_t cnt[4]; +#else + u_int64_t cnt[2]; +#endif }; struct compat_xt_counters_info -- cgit v1.2.3 From d5b415c95f0e6510451f1446cea832c1f77bd7ea Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 26 Apr 2006 00:13:18 -0400 Subject: Input: ads7846 - improve filtering for thumb press accuracy Providing more accurate coordinates for thumb press requires additional steps in the filtering logic: - Ignore samples found invalid by the debouncing logic, or the ones that have out of bound pressure value. - Add a parameter to repeat debouncing, so that more then two consecutive good readings are required for a valid sample. Signed-off-by: Imre Deak Acked-by: Juha Yrjola Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 72 +++++++++++++++++++++++++++++-------- include/linux/spi/ads7846.h | 6 ++-- 2 files changed, 61 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 1aaa153a2774..1494175ac6fe 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -71,6 +71,7 @@ struct ts_event { __be16 x; __be16 y; __be16 z1, z2; + int ignore; }; struct ads7846 { @@ -81,6 +82,7 @@ struct ads7846 { u16 model; u16 vref_delay_usecs; u16 x_plate_ohms; + u16 pressure_max; u8 read_x, read_y, read_z1, read_z2, pwrdown; u16 dummy; /* for the pwrdown read */ @@ -88,12 +90,15 @@ struct ads7846 { struct spi_transfer xfer[10]; struct spi_message msg[5]; + struct spi_message *last_msg; int msg_idx; int read_cnt; + int read_rep; int last_read; u16 debounce_max; u16 debounce_tol; + u16 debounce_rep; spinlock_t lock; struct timer_list timer; /* P: lock */ @@ -354,6 +359,14 @@ static void ads7846_rx(void *ads) } else Rt = 0; + /* Sample found inconsistent by debouncing or pressure is beyond + * the maximum. Don't report it to user space, repeat at least + * once more the measurement */ + if (ts->tc.ignore || Rt > ts->pressure_max) { + mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD); + return; + } + /* NOTE: "pendown" is inferred from pressure; we don't rely on * being able to check nPENIRQ status, or "friendly" trigger modes * (both-edges is much better than just-falling or low-level). @@ -402,25 +415,45 @@ static void ads7846_debounce(void *ads) struct ads7846 *ts = ads; struct spi_message *m; struct spi_transfer *t; - u16 val; + int val; int status; m = &ts->msg[ts->msg_idx]; t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); val = (*(u16 *)t->rx_buf) >> 3; - - if (!ts->read_cnt || (abs(ts->last_read - val) > ts->debounce_tol - && ts->read_cnt < ts->debounce_max)) { - /* Repeat it, if this was the first read or the read wasn't - * consistent enough - */ - ts->read_cnt++; - ts->last_read = val; + if (!ts->read_cnt || (abs(ts->last_read - val) > ts->debounce_tol)) { + /* Repeat it, if this was the first read or the read + * wasn't consistent enough. */ + if (ts->read_cnt < ts->debounce_max) { + ts->last_read = val; + ts->read_cnt++; + } else { + /* Maximum number of debouncing reached and still + * not enough number of consistent readings. Abort + * the whole sample, repeat it in the next sampling + * period. + */ + ts->tc.ignore = 1; + ts->read_cnt = 0; + /* Last message will contain ads7846_rx() as the + * completion function. + */ + m = ts->last_msg; + } + /* Start over collecting consistent readings. */ + ts->read_rep = 0; } else { - /* Go for the next read */ - ts->msg_idx++; - ts->read_cnt = 0; - m++; + if (++ts->read_rep > ts->debounce_rep) { + /* Got a good reading for this coordinate, + * go for the next one. */ + ts->tc.ignore = 0; + ts->msg_idx++; + ts->read_cnt = 0; + ts->read_rep = 0; + m++; + } else + /* Read more values that are consistent. */ + ts->read_cnt++; } status = spi_async(ts->spi, m); if (status) @@ -609,8 +642,15 @@ static int __devinit ads7846_probe(struct spi_device *spi) ts->model = pdata->model ? : 7846; ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; - ts->debounce_max = pdata->debounce_max ? : 1; - ts->debounce_tol = pdata->debounce_tol ? : 10; + ts->pressure_max = pdata->pressure_max ? : ~0; + if (pdata->debounce_max) { + ts->debounce_max = pdata->debounce_max; + ts->debounce_tol = pdata->debounce_tol; + ts->debounce_rep = pdata->debounce_rep; + if (ts->debounce_rep > ts->debounce_max + 1) + ts->debounce_rep = ts->debounce_max - 1; + } else + ts->debounce_tol = ~0; ts->get_pendown_state = pdata->get_pendown_state; snprintf(ts->phys, sizeof(ts->phys), "%s/input0", spi->dev.bus_id); @@ -728,6 +768,8 @@ static int __devinit ads7846_probe(struct spi_device *spi) m->complete = ads7846_rx; m->context = ts; + ts->last_msg = m; + if (request_irq(spi->irq, ads7846_irq, SA_SAMPLE_RANDOM | SA_TRIGGER_FALLING, spi->dev.bus_id, ts)) { diff --git a/include/linux/spi/ads7846.h b/include/linux/spi/ads7846.h index d8823e237e0b..adb3dafd33e9 100644 --- a/include/linux/spi/ads7846.h +++ b/include/linux/spi/ads7846.h @@ -15,9 +15,11 @@ struct ads7846_platform_data { u16 y_min, y_max; u16 pressure_min, pressure_max; - u16 debounce_max; /* max number of readings per sample */ + u16 debounce_max; /* max number of additional readings + * per sample */ u16 debounce_tol; /* tolerance used for filtering */ - + u16 debounce_rep; /* additional consecutive good readings + * required after the first two */ int (*get_pendown_state)(void); }; -- cgit v1.2.3 From 1a0ccece05efb8a9c04b1130c24a2652239f3bea Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 26 Apr 2006 00:13:57 -0400 Subject: Input: allow passing NULL to input_free_device() Many drivers rely on input_free_device() behaving like kfree(). Signed-off-by: Dmitry Torokhov --- include/linux/input.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/input.h b/include/linux/input.h index b0e612dda0cf..16c19d710a4d 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -1016,7 +1016,8 @@ static inline void input_put_device(struct input_dev *dev) static inline void input_free_device(struct input_dev *dev) { - input_put_device(dev); + if (dev) + input_put_device(dev); } int input_register_device(struct input_dev *); -- cgit v1.2.3 From ddc5d3414593e4d7ad7fbd33e7f7517fcc234544 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 26 Apr 2006 00:14:19 -0400 Subject: Input: move input_device_id to mod_devicetable.h Signed-off-by: Dmitry Torokhov --- drivers/input/input.c | 8 ++-- include/linux/input.h | 104 ++++++++++++++++++++-------------------- include/linux/mod_devicetable.h | 48 +++++++++++++++++++ scripts/mod/file2alias.c | 36 +++++++------- 4 files changed, 122 insertions(+), 74 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/input.c b/drivers/input/input.c index a935abeffffc..591c70d80cd8 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -286,19 +286,19 @@ static struct input_device_id *input_match_device(struct input_device_id *id, st for (; id->flags || id->driver_info; id++) { if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) - if (id->id.bustype != dev->id.bustype) + if (id->bustype != dev->id.bustype) continue; if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) - if (id->id.vendor != dev->id.vendor) + if (id->vendor != dev->id.vendor) continue; if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) - if (id->id.product != dev->id.product) + if (id->product != dev->id.product) continue; if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION) - if (id->id.version != dev->id.version) + if (id->version != dev->id.version) continue; MATCH_BIT(evbit, EV_MAX); diff --git a/include/linux/input.h b/include/linux/input.h index 16c19d710a4d..8298b4bf5a07 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -12,8 +12,6 @@ #ifdef __KERNEL__ #include #include -#include -#include #else #include #include @@ -577,15 +575,15 @@ struct input_absinfo { * Switch events */ -#define SW_0 0x00 -#define SW_1 0x01 -#define SW_2 0x02 -#define SW_3 0x03 -#define SW_4 0x04 -#define SW_5 0x05 -#define SW_6 0x06 -#define SW_7 0x07 -#define SW_MAX 0x0f +#define SW_0 0x00 +#define SW_1 0x01 +#define SW_2 0x02 +#define SW_3 0x03 +#define SW_4 0x04 +#define SW_5 0x05 +#define SW_6 0x06 +#define SW_7 0x07 +#define SW_MAX 0x0f /* * Misc events @@ -805,52 +803,16 @@ struct ff_effect { #define FF_MAX 0x7f -struct input_device_id { - - kernel_ulong_t flags; - - struct input_id id; - - kernel_ulong_t evbit[EV_MAX/BITS_PER_LONG+1]; - kernel_ulong_t keybit[KEY_MAX/BITS_PER_LONG+1]; - kernel_ulong_t relbit[REL_MAX/BITS_PER_LONG+1]; - kernel_ulong_t absbit[ABS_MAX/BITS_PER_LONG+1]; - kernel_ulong_t mscbit[MSC_MAX/BITS_PER_LONG+1]; - kernel_ulong_t ledbit[LED_MAX/BITS_PER_LONG+1]; - kernel_ulong_t sndbit[SND_MAX/BITS_PER_LONG+1]; - kernel_ulong_t ffbit[FF_MAX/BITS_PER_LONG+1]; - kernel_ulong_t swbit[SW_MAX/BITS_PER_LONG+1]; - - kernel_ulong_t driver_info; -}; - -/* - * Structure for hotplug & device<->driver matching. - */ - -#define INPUT_DEVICE_ID_MATCH_BUS 1 -#define INPUT_DEVICE_ID_MATCH_VENDOR 2 -#define INPUT_DEVICE_ID_MATCH_PRODUCT 4 -#define INPUT_DEVICE_ID_MATCH_VERSION 8 - -#define INPUT_DEVICE_ID_MATCH_EVBIT 0x010 -#define INPUT_DEVICE_ID_MATCH_KEYBIT 0x020 -#define INPUT_DEVICE_ID_MATCH_RELBIT 0x040 -#define INPUT_DEVICE_ID_MATCH_ABSBIT 0x080 -#define INPUT_DEVICE_ID_MATCH_MSCIT 0x100 -#define INPUT_DEVICE_ID_MATCH_LEDBIT 0x200 -#define INPUT_DEVICE_ID_MATCH_SNDBIT 0x400 -#define INPUT_DEVICE_ID_MATCH_FFBIT 0x800 -#define INPUT_DEVICE_ID_MATCH_SWBIT 0x1000 - #ifdef __KERNEL__ /* * In-kernel definitions. */ +#include #include #include +#include #define NBITS(x) (((x)/BITS_PER_LONG)+1) #define BIT(x) (1UL<<((x)%BITS_PER_LONG)) @@ -951,9 +913,49 @@ struct input_dev { }; #define to_input_dev(d) container_of(d, struct input_dev, cdev) -#define INPUT_DEVICE_ID_MATCH_DEVICE\ +/* + * Verify that we are in sync with input_device_id mod_devicetable.h #defines + */ + +#if EV_MAX != INPUT_DEVICE_ID_EV_MAX +#error "EV_MAX and INPUT_DEVICE_ID_EV_MAX do not match" +#endif + +#if KEY_MAX != INPUT_DEVICE_ID_KEY_MAX +#error "KEY_MAX and INPUT_DEVICE_ID_KEY_MAX do not match" +#endif + +#if REL_MAX != INPUT_DEVICE_ID_REL_MAX +#error "REL_MAX and INPUT_DEVICE_ID_REL_MAX do not match" +#endif + +#if ABS_MAX != INPUT_DEVICE_ID_ABS_MAX +#error "ABS_MAX and INPUT_DEVICE_ID_ABS_MAX do not match" +#endif + +#if MSC_MAX != INPUT_DEVICE_ID_MSC_MAX +#error "MSC_MAX and INPUT_DEVICE_ID_MSC_MAX do not match" +#endif + +#if LED_MAX != INPUT_DEVICE_ID_LED_MAX +#error "LED_MAX and INPUT_DEVICE_ID_LED_MAX do not match" +#endif + +#if SND_MAX != INPUT_DEVICE_ID_SND_MAX +#error "SND_MAX and INPUT_DEVICE_ID_SND_MAX do not match" +#endif + +#if FF_MAX != INPUT_DEVICE_ID_FF_MAX +#error "FF_MAX and INPUT_DEVICE_ID_FF_MAX do not match" +#endif + +#if SW_MAX != INPUT_DEVICE_ID_SW_MAX +#error "SW_MAX and INPUT_DEVICE_ID_SW_MAX do not match" +#endif + +#define INPUT_DEVICE_ID_MATCH_DEVICE \ (INPUT_DEVICE_ID_MATCH_BUS | INPUT_DEVICE_ID_MATCH_VENDOR | INPUT_DEVICE_ID_MATCH_PRODUCT) -#define INPUT_DEVICE_ID_MATCH_DEVICE_AND_VERSION\ +#define INPUT_DEVICE_ID_MATCH_DEVICE_AND_VERSION \ (INPUT_DEVICE_ID_MATCH_DEVICE | INPUT_DEVICE_ID_MATCH_VERSION) struct input_handle; diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 7b08c11ec4cc..f6977708585c 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -249,4 +249,52 @@ struct i2c_device_id { __u16 id; }; +/* Input */ +#define INPUT_DEVICE_ID_EV_MAX 0x1f +#define INPUT_DEVICE_ID_KEY_MAX 0x1ff +#define INPUT_DEVICE_ID_REL_MAX 0x0f +#define INPUT_DEVICE_ID_ABS_MAX 0x3f +#define INPUT_DEVICE_ID_MSC_MAX 0x07 +#define INPUT_DEVICE_ID_LED_MAX 0x0f +#define INPUT_DEVICE_ID_SND_MAX 0x07 +#define INPUT_DEVICE_ID_FF_MAX 0x7f +#define INPUT_DEVICE_ID_SW_MAX 0x0f + +#define INPUT_DEVICE_ID_MATCH_BUS 1 +#define INPUT_DEVICE_ID_MATCH_VENDOR 2 +#define INPUT_DEVICE_ID_MATCH_PRODUCT 4 +#define INPUT_DEVICE_ID_MATCH_VERSION 8 + +#define INPUT_DEVICE_ID_MATCH_EVBIT 0x0010 +#define INPUT_DEVICE_ID_MATCH_KEYBIT 0x0020 +#define INPUT_DEVICE_ID_MATCH_RELBIT 0x0040 +#define INPUT_DEVICE_ID_MATCH_ABSBIT 0x0080 +#define INPUT_DEVICE_ID_MATCH_MSCIT 0x0100 +#define INPUT_DEVICE_ID_MATCH_LEDBIT 0x0200 +#define INPUT_DEVICE_ID_MATCH_SNDBIT 0x0400 +#define INPUT_DEVICE_ID_MATCH_FFBIT 0x0800 +#define INPUT_DEVICE_ID_MATCH_SWBIT 0x1000 + +struct input_device_id { + + kernel_ulong_t flags; + + __u16 bustype; + __u16 vendor; + __u16 product; + __u16 version; + + kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1]; + kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1]; + kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1]; + kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1]; + kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1]; + kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1]; + kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1]; + kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1]; + kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1]; + + kernel_ulong_t driver_info; +}; + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 84e21201f3c0..37f67c23e11b 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -374,10 +374,10 @@ static void do_input(char *alias, kernel_ulong_t *arr, unsigned int min, unsigned int max) { unsigned int i; - for (i = min; i < max; i++) { - if (arr[i/BITS_PER_LONG] & (1 << (i%BITS_PER_LONG))) - sprintf(alias+strlen(alias), "%X,*", i); - } + + for (i = min; i < max; i++) + if (arr[i / BITS_PER_LONG] & (1 << (i%BITS_PER_LONG))) + sprintf(alias + strlen(alias), "%X,*", i); } /* input:b0v0p0e0-eXkXrXaXmXlXsXfXwX where X is comma-separated %02X. */ @@ -386,39 +386,37 @@ static int do_input_entry(const char *filename, struct input_device_id *id, { sprintf(alias, "input:"); - ADD(alias, "b", id->flags&INPUT_DEVICE_ID_MATCH_BUS, id->id.bustype); - ADD(alias, "v", id->flags&INPUT_DEVICE_ID_MATCH_VENDOR, id->id.vendor); - ADD(alias, "p", id->flags&INPUT_DEVICE_ID_MATCH_PRODUCT, - id->id.product); - ADD(alias, "e", id->flags&INPUT_DEVICE_ID_MATCH_VERSION, - id->id.version); + ADD(alias, "b", id->flags & INPUT_DEVICE_ID_MATCH_BUS, id->bustype); + ADD(alias, "v", id->flags & INPUT_DEVICE_ID_MATCH_VENDOR, id->vendor); + ADD(alias, "p", id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT, id->product); + ADD(alias, "e", id->flags & INPUT_DEVICE_ID_MATCH_VERSION, id->version); sprintf(alias + strlen(alias), "-e*"); - if (id->flags&INPUT_DEVICE_ID_MATCH_EVBIT) + if (id->flags & INPUT_DEVICE_ID_MATCH_EVBIT) do_input(alias, id->evbit, 0, EV_MAX); sprintf(alias + strlen(alias), "k*"); - if (id->flags&INPUT_DEVICE_ID_MATCH_KEYBIT) + if (id->flags & INPUT_DEVICE_ID_MATCH_KEYBIT) do_input(alias, id->keybit, KEY_MIN_INTERESTING, KEY_MAX); sprintf(alias + strlen(alias), "r*"); - if (id->flags&INPUT_DEVICE_ID_MATCH_RELBIT) + if (id->flags & INPUT_DEVICE_ID_MATCH_RELBIT) do_input(alias, id->relbit, 0, REL_MAX); sprintf(alias + strlen(alias), "a*"); - if (id->flags&INPUT_DEVICE_ID_MATCH_ABSBIT) + if (id->flags & INPUT_DEVICE_ID_MATCH_ABSBIT) do_input(alias, id->absbit, 0, ABS_MAX); sprintf(alias + strlen(alias), "m*"); - if (id->flags&INPUT_DEVICE_ID_MATCH_MSCIT) + if (id->flags & INPUT_DEVICE_ID_MATCH_MSCIT) do_input(alias, id->mscbit, 0, MSC_MAX); sprintf(alias + strlen(alias), "l*"); - if (id->flags&INPUT_DEVICE_ID_MATCH_LEDBIT) + if (id->flags & INPUT_DEVICE_ID_MATCH_LEDBIT) do_input(alias, id->ledbit, 0, LED_MAX); sprintf(alias + strlen(alias), "s*"); - if (id->flags&INPUT_DEVICE_ID_MATCH_SNDBIT) + if (id->flags & INPUT_DEVICE_ID_MATCH_SNDBIT) do_input(alias, id->sndbit, 0, SND_MAX); sprintf(alias + strlen(alias), "f*"); - if (id->flags&INPUT_DEVICE_ID_MATCH_FFBIT) + if (id->flags & INPUT_DEVICE_ID_MATCH_FFBIT) do_input(alias, id->ffbit, 0, FF_MAX); sprintf(alias + strlen(alias), "w*"); - if (id->flags&INPUT_DEVICE_ID_MATCH_SWBIT) + if (id->flags & INPUT_DEVICE_ID_MATCH_SWBIT) do_input(alias, id->swbit, 0, SW_MAX); return 1; } -- cgit v1.2.3 From 912d35f86781e64d73be1ef358f703c08905ac37 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 26 Apr 2006 10:59:21 +0200 Subject: [PATCH] Add support for the sys_vmsplice syscall sys_splice() moves data to/from pipes with a file input/output. sys_vmsplice() moves data to a pipe, with the input being a user address range instead. This uses an approach suggested by Linus, where we can hold partial ranges inside the pages[] map. Hopefully this will be useful for network receive support as well. Signed-off-by: Jens Axboe --- arch/ia64/kernel/entry.S | 1 + arch/powerpc/kernel/systbl.S | 1 + arch/powerpc/platforms/cell/spu_callbacks.c | 1 + fs/splice.c | 292 ++++++++++++++++++++++++---- include/asm-i386/unistd.h | 3 +- include/asm-ia64/unistd.h | 3 +- include/asm-powerpc/unistd.h | 3 +- include/asm-x86_64/unistd.h | 4 +- include/linux/syscalls.h | 3 + 9 files changed, 268 insertions(+), 43 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S index e30798811216..bcb80ca5cf40 100644 --- a/arch/ia64/kernel/entry.S +++ b/arch/ia64/kernel/entry.S @@ -1610,5 +1610,6 @@ sys_call_table: data8 sys_get_robust_list data8 sys_sync_file_range // 1300 data8 sys_tee + data8 sys_vmsplice .org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls diff --git a/arch/powerpc/kernel/systbl.S b/arch/powerpc/kernel/systbl.S index 8d1522690501..0b98eea73c5e 100644 --- a/arch/powerpc/kernel/systbl.S +++ b/arch/powerpc/kernel/systbl.S @@ -324,6 +324,7 @@ COMPAT_SYS(ppoll) SYSCALL(unshare) SYSCALL(splice) SYSCALL(tee) +SYSCALL(vmsplice) /* * please add new calls to arch/powerpc/platforms/cell/spu_callbacks.c diff --git a/arch/powerpc/platforms/cell/spu_callbacks.c b/arch/powerpc/platforms/cell/spu_callbacks.c index deb3afb94484..b283380a2a18 100644 --- a/arch/powerpc/platforms/cell/spu_callbacks.c +++ b/arch/powerpc/platforms/cell/spu_callbacks.c @@ -318,6 +318,7 @@ void *spu_syscall_table[] = { [__NR_unshare] sys_unshare, [__NR_splice] sys_splice, [__NR_tee] sys_tee, + [__NR_vmsplice] sys_vmsplice, }; long spu_sys_callback(struct spu_syscall_block *s) diff --git a/fs/splice.c b/fs/splice.c index 8c6030c762e2..0b2c1f060cae 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -27,6 +27,7 @@ #include #include #include +#include /* * Passed to the actors @@ -38,6 +39,22 @@ struct splice_desc { loff_t pos; /* file position */ }; +struct partial_page { + unsigned int offset; + unsigned int len; +}; + +/* + * Passed to move_to_pipe + */ +struct splice_pipe_desc { + 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 */ + struct pipe_buf_operations *ops;/* ops associated with output pipe */ +}; + /* * Attempt to steal a page from a pipe buffer. This should perhaps go into * a vm helper function, it's already simplified quite a bit by the @@ -128,6 +145,19 @@ static void page_cache_pipe_buf_unmap(struct pipe_inode_info *info, 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, struct pipe_buffer *buf) { @@ -143,19 +173,33 @@ static struct pipe_buf_operations page_cache_pipe_buf_ops = { .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 buffer operations. Otherwise very similar to the regular pipe_writev(). */ -static ssize_t move_to_pipe(struct pipe_inode_info *pipe, struct page **pages, - int nr_pages, unsigned long len, - unsigned int offset, unsigned int flags) +static ssize_t move_to_pipe(struct pipe_inode_info *pipe, + struct splice_pipe_desc *spd) { - int ret, do_wakeup, i; + int ret, do_wakeup, page_nr; ret = 0; do_wakeup = 0; - i = 0; + page_nr = 0; if (pipe->inode) mutex_lock(&pipe->inode->i_mutex); @@ -171,27 +215,19 @@ static ssize_t move_to_pipe(struct pipe_inode_info *pipe, struct page **pages, if (pipe->nrbufs < PIPE_BUFFERS) { int newbuf = (pipe->curbuf + pipe->nrbufs) & (PIPE_BUFFERS - 1); struct pipe_buffer *buf = pipe->bufs + newbuf; - struct page *page = pages[i++]; - unsigned long this_len; - this_len = PAGE_CACHE_SIZE - offset; - if (this_len > len) - this_len = len; - - buf->page = page; - buf->offset = offset; - buf->len = this_len; - buf->ops = &page_cache_pipe_buf_ops; + buf->page = spd->pages[page_nr]; + buf->offset = spd->partial[page_nr].offset; + buf->len = spd->partial[page_nr].len; + buf->ops = spd->ops; pipe->nrbufs++; + page_nr++; + ret += buf->len; + if (pipe->inode) do_wakeup = 1; - ret += this_len; - len -= this_len; - offset = 0; - if (!--nr_pages) - break; - if (!len) + if (!--spd->nr_pages) break; if (pipe->nrbufs < PIPE_BUFFERS) continue; @@ -199,7 +235,7 @@ static ssize_t move_to_pipe(struct pipe_inode_info *pipe, struct page **pages, break; } - if (flags & SPLICE_F_NONBLOCK) { + if (spd->flags & SPLICE_F_NONBLOCK) { if (!ret) ret = -EAGAIN; break; @@ -234,8 +270,8 @@ static ssize_t move_to_pipe(struct pipe_inode_info *pipe, struct page **pages, kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN); } - while (i < nr_pages) - page_cache_release(pages[i++]); + while (page_nr < spd->nr_pages) + page_cache_release(spd->pages[page_nr++]); return ret; } @@ -246,17 +282,24 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, unsigned int flags) { struct address_space *mapping = in->f_mapping; - unsigned int loff, offset, nr_pages; + unsigned int loff, nr_pages; struct page *pages[PIPE_BUFFERS]; + struct partial_page partial[PIPE_BUFFERS]; struct page *page; pgoff_t index, end_index; loff_t isize; - size_t bytes; - int i, error; + size_t total_len; + int error; + struct splice_pipe_desc spd = { + .pages = pages, + .partial = partial, + .flags = flags, + .ops = &page_cache_pipe_buf_ops, + }; index = *ppos >> PAGE_CACHE_SHIFT; - loff = offset = *ppos & ~PAGE_CACHE_MASK; - nr_pages = (len + offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + loff = *ppos & ~PAGE_CACHE_MASK; + nr_pages = (len + loff + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; if (nr_pages > PIPE_BUFFERS) nr_pages = PIPE_BUFFERS; @@ -266,15 +309,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 * chunk splice and the page is already there) for a single page. */ - if (!offset || nr_pages > 1) - do_page_cache_readahead(mapping, in, index, nr_pages); + if (!loff || spd.nr_pages > 1) + do_page_cache_readahead(mapping, in, index, spd.nr_pages); /* * Now fill in the holes: */ error = 0; - bytes = 0; - for (i = 0; i < nr_pages; i++, index++) { + total_len = 0; + for (spd.nr_pages = 0; spd.nr_pages < nr_pages; spd.nr_pages++, index++) { unsigned int this_len; if (!len) @@ -367,26 +410,29 @@ readpage: */ if (end_index == index) { loff = PAGE_CACHE_SIZE - (isize & ~PAGE_CACHE_MASK); - if (bytes + loff > isize) { + if (total_len + loff > isize) { page_cache_release(page); break; } /* * force quit after adding this page */ - nr_pages = i; + nr_pages = spd.nr_pages; this_len = min(this_len, loff); + loff = 0; } } fill_it: - pages[i] = page; - bytes += this_len; + pages[spd.nr_pages] = page; + partial[spd.nr_pages].offset = loff; + partial[spd.nr_pages].len = this_len; len -= this_len; + total_len += this_len; loff = 0; } - if (i) - return move_to_pipe(pipe, pages, i, bytes, offset, flags); + if (spd.nr_pages) + return move_to_pipe(pipe, &spd); return error; } @@ -1018,6 +1064,174 @@ static long do_splice(struct file *in, loff_t __user *off_in, 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(¤t->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(¤t->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 move_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, int fd_out, loff_t __user *off_out, size_t len, unsigned int flags) diff --git a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h index d81d6cfc1bb4..eb4b152c82fc 100644 --- a/include/asm-i386/unistd.h +++ b/include/asm-i386/unistd.h @@ -321,8 +321,9 @@ #define __NR_splice 313 #define __NR_sync_file_range 314 #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 diff --git a/include/asm-ia64/unistd.h b/include/asm-ia64/unistd.h index a40ebec6aeeb..7107763168bf 100644 --- a/include/asm-ia64/unistd.h +++ b/include/asm-ia64/unistd.h @@ -290,12 +290,13 @@ #define __NR_get_robust_list 1299 #define __NR_sync_file_range 1300 #define __NR_tee 1301 +#define __NR_vmsplice 1302 #ifdef __KERNEL__ #include -#define NR_syscalls 278 /* length of syscall table */ +#define NR_syscalls 279 /* length of syscall table */ #define __ARCH_WANT_SYS_RT_SIGACTION diff --git a/include/asm-powerpc/unistd.h b/include/asm-powerpc/unistd.h index c612f1a62772..34325e292596 100644 --- a/include/asm-powerpc/unistd.h +++ b/include/asm-powerpc/unistd.h @@ -303,8 +303,9 @@ #define __NR_unshare 282 #define __NR_splice 283 #define __NR_tee 284 +#define __NR_vmsplice 285 -#define __NR_syscalls 285 +#define __NR_syscalls 286 #ifdef __KERNEL__ #define __NR__exit __NR_exit diff --git a/include/asm-x86_64/unistd.h b/include/asm-x86_64/unistd.h index 98c36eae567c..feb77cb8c044 100644 --- a/include/asm-x86_64/unistd.h +++ b/include/asm-x86_64/unistd.h @@ -615,8 +615,10 @@ __SYSCALL(__NR_splice, sys_splice) __SYSCALL(__NR_tee, sys_tee) #define __NR_sync_file_range 277 __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 diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index d3ebc0e68b2b..3996960fc565 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -574,6 +574,9 @@ asmlinkage long sys_splice(int fd_in, loff_t __user *off_in, int fd_out, loff_t __user *off_out, 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_sync_file_range(int fd, loff_t offset, loff_t nbytes, -- cgit v1.2.3 From 734cbc363b159caee158d5a83408c72d98bcacf0 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 25 Apr 2006 10:58:50 -0700 Subject: [PATCH] sky2: reschedule if irq still pending This is a workaround for the case edge-triggered irq's. Several users seem to have broken configurations sharing edge-triggered irq's. To avoid losing IRQ's, reshedule if more work arrives. The changes to netdevice.h are to extract the part that puts device back in list into separate inline. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 20 ++++++++++++++++---- include/linux/netdevice.h | 18 ++++++++++-------- 2 files changed, 26 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 67b0eab16589..618fde8622ca 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2093,6 +2093,7 @@ static int sky2_poll(struct net_device *dev0, int *budget) int work_done = 0; u32 status = sky2_read32(hw, B0_Y2_SP_EISR); + restart_poll: if (unlikely(status & ~Y2_IS_STAT_BMU)) { if (status & Y2_IS_HW_ERR) sky2_hw_intr(hw); @@ -2123,7 +2124,7 @@ static int sky2_poll(struct net_device *dev0, int *budget) } if (status & Y2_IS_STAT_BMU) { - work_done = sky2_status_intr(hw, work_limit); + work_done += sky2_status_intr(hw, work_limit - work_done); *budget -= work_done; dev0->quota -= work_done; @@ -2133,9 +2134,22 @@ static int sky2_poll(struct net_device *dev0, int *budget) sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); } - netif_rx_complete(dev0); + local_irq_disable(); + __netif_rx_complete(dev0); status = sky2_read32(hw, B0_Y2_SP_LISR); + + if (unlikely(status)) { + /* More work pending, try and keep going */ + if (__netif_rx_schedule_prep(dev0)) { + __netif_rx_reschedule(dev0, work_done); + status = sky2_read32(hw, B0_Y2_SP_EISR); + local_irq_enable(); + goto restart_poll; + } + } + + local_irq_enable(); return 0; } @@ -2153,8 +2167,6 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) prefetch(&hw->st_le[hw->st_idx]); if (likely(__netif_rx_schedule_prep(dev0))) __netif_rx_schedule(dev0); - else - printk(KERN_DEBUG PFX "irq race detected\n"); return IRQ_HANDLED; } diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 40ccf8cc4239..01db7b88a2b1 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -829,19 +829,21 @@ static inline void netif_rx_schedule(struct net_device *dev) __netif_rx_schedule(dev); } -/* Try to reschedule poll. Called by dev->poll() after netif_rx_complete(). - * Do not inline this? - */ + +static inline void __netif_rx_reschedule(struct net_device *dev, int undo) +{ + dev->quota += undo; + list_add_tail(&dev->poll_list, &__get_cpu_var(softnet_data).poll_list); + __raise_softirq_irqoff(NET_RX_SOFTIRQ); +} + +/* Try to reschedule poll. Called by dev->poll() after netif_rx_complete(). */ static inline int netif_rx_reschedule(struct net_device *dev, int undo) { if (netif_rx_schedule_prep(dev)) { unsigned long flags; - - dev->quota += undo; - local_irq_save(flags); - list_add_tail(&dev->poll_list, &__get_cpu_var(softnet_data).poll_list); - __raise_softirq_irqoff(NET_RX_SOFTIRQ); + __netif_rx_reschedule(dev, undo); local_irq_restore(flags); return 1; } -- cgit v1.2.3 From 00522fb41a2a9bf0f98a007c0e2b516a3873148c Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 26 Apr 2006 14:39:29 +0200 Subject: [PATCH] splice: rearrange moving to/from pipe helpers We need these for people writing their own ->splice_read/write hooks. Signed-off-by: Jens Axboe --- fs/splice.c | 35 +++++++++++------------------------ include/linux/pipe_fs_i.h | 17 +++++++++++++++++ 2 files changed, 28 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/fs/splice.c b/fs/splice.c index 0b2c1f060cae..447ebc0a37f3 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -29,23 +29,13 @@ #include #include -/* - * 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 */ -}; - struct partial_page { unsigned int offset; unsigned int len; }; /* - * Passed to move_to_pipe + * Passed to splice_to_pipe */ struct splice_pipe_desc { struct page **pages; /* page map */ @@ -192,8 +182,8 @@ static struct pipe_buf_operations user_page_pipe_buf_ops = { * Pipe output worker. This sets up our pipe format with the page cache * pipe buffer operations. Otherwise very similar to the regular pipe_writev(). */ -static ssize_t move_to_pipe(struct pipe_inode_info *pipe, - struct splice_pipe_desc *spd) +static ssize_t splice_to_pipe(struct pipe_inode_info *pipe, + struct splice_pipe_desc *spd) { int ret, do_wakeup, page_nr; @@ -432,7 +422,7 @@ fill_it: } if (spd.nr_pages) - return move_to_pipe(pipe, &spd); + return splice_to_pipe(pipe, &spd); return error; } @@ -666,17 +656,14 @@ out_nomem: 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 * 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. */ -static ssize_t move_from_pipe(struct pipe_inode_info *pipe, struct file *out, - loff_t *ppos, size_t len, unsigned int flags, - splice_actor *actor) +ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags, + splice_actor *actor) { int ret, do_wakeup, err; struct splice_desc sd; @@ -795,7 +782,7 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out, struct address_space *mapping = out->f_mapping; 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) { struct inode *inode = mapping->host; @@ -837,7 +824,7 @@ EXPORT_SYMBOL(generic_file_splice_write); ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe, struct file *out, 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); @@ -924,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 - * 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 = 1; @@ -1210,7 +1197,7 @@ static long do_vmsplice(struct file *file, const struct iovec __user *iov, if (spd.nr_pages <= 0) return spd.nr_pages; - return move_to_pipe(pipe, &spd); + return splice_to_pipe(pipe, &spd); } asmlinkage long sys_vmsplice(int fd, const struct iovec __user *iov, diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index ef7f33c0be19..0008d4bd4059 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -61,4 +61,21 @@ void __free_pipe_info(struct pipe_inode_info *); /* from/to, of course */ #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 -- cgit v1.2.3 From ebf43500ef148a380bd132743c3fc530111ac620 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 27 Apr 2006 08:46:01 +0200 Subject: [PATCH] Add find_get_pages_contig(): contiguous variant of find_get_pages() find_get_pages_contig() will break out if we hit a hole in the page cache. From Andrew Morton, small modifications and documentation by me. Signed-off-by: Jens Axboe --- include/linux/pagemap.h | 2 ++ mm/filemap.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) (limited to 'include/linux') diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 9539efd4f7e6..7a1af574dedf 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -78,6 +78,8 @@ extern struct page * find_or_create_page(struct address_space *mapping, unsigned long index, gfp_t gfp_mask); unsigned find_get_pages(struct address_space *mapping, pgoff_t start, unsigned int nr_pages, struct page **pages); +unsigned find_get_pages_contig(struct address_space *mapping, pgoff_t start, + unsigned int nr_pages, struct page **pages); unsigned find_get_pages_tag(struct address_space *mapping, pgoff_t *index, int tag, unsigned int nr_pages, struct page **pages); diff --git a/mm/filemap.c b/mm/filemap.c index 3ef20739e725..fd57442186cb 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -697,6 +697,38 @@ unsigned find_get_pages(struct address_space *mapping, pgoff_t start, return ret; } +/** + * find_get_pages_contig - gang contiguous pagecache lookup + * @mapping: The address_space to search + * @index: The starting page index + * @nr_pages: The maximum number of pages + * @pages: Where the resulting pages are placed + * + * find_get_pages_contig() works exactly like find_get_pages(), except + * that the returned number of pages are guaranteed to be contiguous. + * + * find_get_pages_contig() returns the number of pages which were found. + */ +unsigned find_get_pages_contig(struct address_space *mapping, pgoff_t index, + unsigned int nr_pages, struct page **pages) +{ + unsigned int i; + unsigned int ret; + + read_lock_irq(&mapping->tree_lock); + ret = radix_tree_gang_lookup(&mapping->page_tree, + (void **)pages, index, nr_pages); + for (i = 0; i < ret; i++) { + if (pages[i]->mapping == NULL || pages[i]->index != index) + break; + + page_cache_get(pages[i]); + index++; + } + read_unlock_irq(&mapping->tree_lock); + return i; +} + /* * Like find_get_pages, except we only return pages which are tagged with * `tag'. We update *index to index the next page for the traversal. -- cgit v1.2.3 From 4d17ffda331ba6030bb8c233c73d6a87954d8ea7 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Tue, 25 Apr 2006 15:37:26 +0200 Subject: [PATCH] Kobject: fix build error This fixes a build error for various odd combinations of CONFIG_HOTPLUG and CONFIG_NET. Signed-off-by: Kay Sievers Cc: Nigel Cunningham Cc: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- include/linux/kobject.h | 2 +- lib/kobject_uevent.c | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kobject.h b/include/linux/kobject.h index dcd0623be892..e38bb357282a 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -259,7 +259,7 @@ struct subsys_attribute { extern int subsys_create_file(struct subsystem * , struct subsys_attribute *); extern void subsys_remove_file(struct subsystem * , struct subsys_attribute *); -#if defined(CONFIG_HOTPLUG) && defined(CONFIG_NET) +#if defined(CONFIG_HOTPLUG) void kobject_uevent(struct kobject *kobj, enum kobject_action action); int add_uevent_var(char **envp, int num_envp, int *cur_index, diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 982226daf939..7f20e7b857cb 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -25,11 +25,13 @@ #define BUFFER_SIZE 2048 /* buffer for the variables */ #define NUM_ENVP 32 /* number of env pointers */ -#if defined(CONFIG_HOTPLUG) && defined(CONFIG_NET) +#if defined(CONFIG_HOTPLUG) u64 uevent_seqnum; char uevent_helper[UEVENT_HELPER_PATH_LEN] = "/sbin/hotplug"; static DEFINE_SPINLOCK(sequence_lock); +#if defined(CONFIG_NET) static struct sock *uevent_sock; +#endif static char *action_to_string(enum kobject_action action) { @@ -155,6 +157,7 @@ void kobject_uevent(struct kobject *kobj, enum kobject_action action) spin_unlock(&sequence_lock); sprintf(seq_buff, "SEQNUM=%llu", (unsigned long long)seq); +#if defined(CONFIG_NET) /* send netlink message */ if (uevent_sock) { struct sk_buff *skb; @@ -179,6 +182,7 @@ void kobject_uevent(struct kobject *kobj, enum kobject_action action) netlink_broadcast(uevent_sock, skb, 0, 1, GFP_KERNEL); } } +#endif /* call uevent_helper, usually only enabled during early boot */ if (uevent_helper[0]) { @@ -249,6 +253,7 @@ int add_uevent_var(char **envp, int num_envp, int *cur_index, } EXPORT_SYMBOL_GPL(add_uevent_var); +#if defined(CONFIG_NET) static int __init kobject_uevent_init(void) { uevent_sock = netlink_kernel_create(NETLINK_KOBJECT_UEVENT, 1, NULL, @@ -264,5 +269,6 @@ static int __init kobject_uevent_init(void) } postcore_initcall(kobject_uevent_init); +#endif #endif /* CONFIG_HOTPLUG */ -- cgit v1.2.3 From bde11d794206ae8d72defd0e8a481181200f7dc4 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 18 Apr 2006 21:30:22 -0700 Subject: [PATCH] Fix OCFS2 warning when DEBUG_FS is not enabled Fix the following warning which happens when OCFS2_FS is enabled but DEBUG_FS isn't: fs/ocfs2/dlmglue.c: In function `ocfs2_dlm_init_debug': fs/ocfs2/dlmglue.c:2036: warning: passing arg 5 of `debugfs_create_file' discards qualifiers from pointer target type Signed-off-by: Jean Delvare Cc: Arjan van de Ven Cc: Joel Becker Acked-by: Mark Fasheh Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- include/linux/debugfs.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 176e2d371577..047567d34ca7 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -58,9 +58,8 @@ struct dentry *debugfs_create_blob(const char *name, mode_t mode, */ static inline struct dentry *debugfs_create_file(const char *name, mode_t mode, - struct dentry *parent, - void *data, - struct file_operations *fops) + struct dentry *parent, void *data, + const struct file_operations *fops) { return ERR_PTR(-ENODEV); } -- cgit v1.2.3 From 5b3ef14e3e9d745a512d65fcb4ef9be541226d80 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sat, 22 Apr 2006 12:14:44 +0200 Subject: [PATCH] Kobject: possible cleanups This patch contains the following possible cleanups: - #if 0 the following unused global function: - subsys_remove_file() - remove the following unused EXPORT_SYMBOL's: - kset_find_obj - subsystem_init - remove the following unused EXPORT_SYMBOL_GPL: - kobject_add_dir Signed-off-by: Adrian Bunk Signed-off-by: Greg Kroah-Hartman --- include/linux/kobject.h | 1 - lib/kobject.c | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kobject.h b/include/linux/kobject.h index e38bb357282a..c187c53cecd0 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -257,7 +257,6 @@ struct subsys_attribute { }; extern int subsys_create_file(struct subsystem * , struct subsys_attribute *); -extern void subsys_remove_file(struct subsystem * , struct subsys_attribute *); #if defined(CONFIG_HOTPLUG) void kobject_uevent(struct kobject *kobj, enum kobject_action action); diff --git a/lib/kobject.c b/lib/kobject.c index 01d957513940..b46350c27837 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -422,7 +422,6 @@ struct kobject *kobject_add_dir(struct kobject *parent, const char *name) return k; } -EXPORT_SYMBOL_GPL(kobject_add_dir); /** * kset_init - initialize a kset for use @@ -569,7 +568,7 @@ int subsys_create_file(struct subsystem * s, struct subsys_attribute * a) * @s: subsystem. * @a: attribute desciptor. */ - +#if 0 void subsys_remove_file(struct subsystem * s, struct subsys_attribute * a) { if (subsys_get(s)) { @@ -577,6 +576,7 @@ void subsys_remove_file(struct subsystem * s, struct subsys_attribute * a) subsys_put(s); } } +#endif /* 0 */ EXPORT_SYMBOL(kobject_init); EXPORT_SYMBOL(kobject_register); @@ -588,10 +588,7 @@ EXPORT_SYMBOL(kobject_del); EXPORT_SYMBOL(kset_register); EXPORT_SYMBOL(kset_unregister); -EXPORT_SYMBOL(kset_find_obj); -EXPORT_SYMBOL(subsystem_init); EXPORT_SYMBOL(subsystem_register); EXPORT_SYMBOL(subsystem_unregister); EXPORT_SYMBOL(subsys_create_file); -EXPORT_SYMBOL(subsys_remove_file); -- cgit v1.2.3 From 13e87ec68641fd54f3fa04eef3419d034ed2115a Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 27 Apr 2006 18:39:18 -0700 Subject: [PATCH] request_irq(): remove warnings from irq probing - Add new SA_PROBEIRQ which suppresses the new sharing-mismatch warning. Some drivers like to use request_irq() to find an unused interrupt slot. - Use it in i82365.c - Kill unused SA_PROBE. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/pcmcia/i82365.c | 7 ++++--- include/asm-xtensa/signal.h | 2 +- include/linux/signal.h | 4 +++- kernel/irq/manage.c | 6 ++++-- 4 files changed, 12 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c index bd0308e89815..a2f05f485156 100644 --- a/drivers/pcmcia/i82365.c +++ b/drivers/pcmcia/i82365.c @@ -509,7 +509,8 @@ static irqreturn_t i365_count_irq(int irq, void *dev, struct pt_regs *regs) static u_int __init test_irq(u_short sock, int irq) { debug(2, " testing ISA irq %d\n", irq); - if (request_irq(irq, i365_count_irq, 0, "scan", i365_count_irq) != 0) + if (request_irq(irq, i365_count_irq, SA_PROBEIRQ, "scan", + i365_count_irq) != 0) return 1; irq_hits = 0; irq_sock = sock; msleep(10); @@ -561,7 +562,7 @@ static u_int __init isa_scan(u_short sock, u_int mask0) } else { /* Fallback: just find interrupts that aren't in use */ for (i = 0; i < 16; i++) - if ((mask0 & (1 << i)) && (_check_irq(i, 0) == 0)) + if ((mask0 & (1 << i)) && (_check_irq(i, SA_PROBEIRQ) == 0)) mask1 |= (1 << i); printk("default"); /* If scan failed, default to polled status */ @@ -725,7 +726,7 @@ static void __init add_pcic(int ns, int type) u_int cs_mask = mask & ((cs_irq) ? (1< 0; cs_irq--) if ((cs_mask & (1 << cs_irq)) && - (_check_irq(cs_irq, 0) == 0)) + (_check_irq(cs_irq, SA_PROBEIRQ) == 0)) break; if (cs_irq) { grab_irq = 1; diff --git a/include/asm-xtensa/signal.h b/include/asm-xtensa/signal.h index 5d6fc9cdf58d..a99c9aec64ec 100644 --- a/include/asm-xtensa/signal.h +++ b/include/asm-xtensa/signal.h @@ -118,9 +118,9 @@ typedef struct { * SA_INTERRUPT is also used by the irq handling routines. * SA_SHIRQ is for shared interrupt support on PCI and EISA. */ -#define SA_PROBE SA_ONESHOT #define SA_SAMPLE_RANDOM SA_RESTART #define SA_SHIRQ 0x04000000 +#define SA_PROBEIRQ 0x08000000 #endif #define SIG_BLOCK 0 /* for blocking signals */ diff --git a/include/linux/signal.h b/include/linux/signal.h index 162a8fd10b29..70739f51a09f 100644 --- a/include/linux/signal.h +++ b/include/linux/signal.h @@ -14,10 +14,12 @@ * * SA_INTERRUPT is also used by the irq handling routines. * SA_SHIRQ is for shared interrupt support on PCI and EISA. + * SA_PROBEIRQ is set by callers when they expect sharing mismatches to occur */ -#define SA_PROBE SA_ONESHOT #define SA_SAMPLE_RANDOM SA_RESTART #define SA_SHIRQ 0x04000000 +#define SA_PROBEIRQ 0x08000000 + /* * As above, these correspond to the IORESOURCE_IRQ_* defines in * linux/ioport.h to select the interrupt line behaviour. When diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index ac766ad573e8..1279e3499534 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -246,8 +246,10 @@ int setup_irq(unsigned int irq, struct irqaction * new) mismatch: spin_unlock_irqrestore(&desc->lock, flags); - printk(KERN_ERR "%s: irq handler mismatch\n", __FUNCTION__); - dump_stack(); + if (!(new->flags & SA_PROBEIRQ)) { + printk(KERN_ERR "%s: irq handler mismatch\n", __FUNCTION__); + dump_stack(); + } return -EBUSY; } -- cgit v1.2.3 From 08791e5cf62b6952ca32106aebb79b6066005de4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 29 Apr 2006 01:13:21 -0400 Subject: Input: ressurect EVIOCGREP and EVIOCSREP While writing to an event device allows to set repeat rate for an individual input device there is no way to retrieve current settings so we need to ressurect EVIOCGREP. Also ressurect EVIOCSREP so we have a symmetrical interface. Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 21 +++++++++++++++++++++ include/linux/input.h | 2 ++ 2 files changed, 23 insertions(+) (limited to 'include/linux') diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index a34e3d91d9ed..ba325f16d077 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -403,6 +403,27 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd, case EVIOCGID: if (copy_to_user(p, &dev->id, sizeof(struct input_id))) return -EFAULT; + return 0; + + case EVIOCGREP: + if (!test_bit(EV_REP, dev->evbit)) + return -ENOSYS; + if (put_user(dev->rep[REP_DELAY], ip)) + return -EFAULT; + if (put_user(dev->rep[REP_PERIOD], ip + 1)) + return -EFAULT; + return 0; + + case EVIOCSREP: + if (!test_bit(EV_REP, dev->evbit)) + return -ENOSYS; + if (get_user(u, ip)) + return -EFAULT; + if (get_user(v, ip + 1)) + return -EFAULT; + + input_event(dev, EV_REP, REP_DELAY, u); + input_event(dev, EV_REP, REP_PERIOD, v); return 0; diff --git a/include/linux/input.h b/include/linux/input.h index 8298b4bf5a07..50e338d2ffda 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -56,6 +56,8 @@ struct input_absinfo { #define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */ #define EVIOCGID _IOR('E', 0x02, struct input_id) /* get device ID */ +#define EVIOCGREP _IOR('E', 0x03, int[2]) /* get repeat settings */ +#define EVIOCSREP _IOW('E', 0x03, int[2]) /* set repeat settings */ #define EVIOCGKEYCODE _IOR('E', 0x04, int[2]) /* get keycode */ #define EVIOCSKEYCODE _IOW('E', 0x04, int[2]) /* set keycode */ -- cgit v1.2.3 From da753beaeb1446aa87bcca7e8a0026633a8914f0 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 28 Apr 2006 15:21:23 -0700 Subject: [NET]: use hlist_unhashed() Use hlist_unhashed() rather than accessing inside data structure. Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: David S. Miller --- include/linux/list.h | 2 +- include/net/inet_timewait_sock.h | 2 +- include/net/sock.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/list.h b/include/linux/list.h index 67258b47e9ca..76f05718342c 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -619,7 +619,7 @@ static inline void hlist_del_rcu(struct hlist_node *n) static inline void hlist_del_init(struct hlist_node *n) { - if (n->pprev) { + if (!hlist_unhashed(n)) { __hlist_del(n); INIT_HLIST_NODE(n); } diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h index 1da294c47522..e837f98fdb50 100644 --- a/include/net/inet_timewait_sock.h +++ b/include/net/inet_timewait_sock.h @@ -150,7 +150,7 @@ static inline void inet_twsk_add_bind_node(struct inet_timewait_sock *tw, static inline int inet_twsk_dead_hashed(const struct inet_timewait_sock *tw) { - return tw->tw_death_node.pprev != NULL; + return !hlist_unhashed(&tw->tw_death_node); } static inline void inet_twsk_dead_node_init(struct inet_timewait_sock *tw) diff --git a/include/net/sock.h b/include/net/sock.h index ff8b0dad7b0f..c9fad6fb629b 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -279,7 +279,7 @@ static inline int sk_unhashed(const struct sock *sk) static inline int sk_hashed(const struct sock *sk) { - return sk->sk_node.pprev != NULL; + return !sk_unhashed(sk); } static __inline__ void sk_node_init(struct hlist_node *node) -- cgit v1.2.3 From 68ac64cd3fd89fdaa091701f6ab98a9065e9b1b5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 30 Apr 2006 11:13:50 +0100 Subject: [SERIAL] Clean up serial locking when obtaining a reference to a port The locking for the uart_port is over complicated, and can be simplified if we introduce a flag to indicate that a port is "dead" and will be removed. This also helps the validator because it removes a case of non-nested unlock ordering. Signed-off-by: Russell King Signed-off-by: Ingo Molnar Signed-off-by: Andrew Morton --- drivers/serial/serial_core.c | 114 +++++++++++++++++++++++-------------------- include/linux/serial_core.h | 1 + 2 files changed, 61 insertions(+), 54 deletions(-) (limited to 'include/linux') diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index fcd7744c4253..aeb8153ccf24 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -1500,20 +1500,18 @@ uart_block_til_ready(struct file *filp, struct uart_state *state) static struct uart_state *uart_get(struct uart_driver *drv, int line) { struct uart_state *state; + int ret = 0; - mutex_lock(&port_mutex); state = drv->state + line; if (mutex_lock_interruptible(&state->mutex)) { - state = ERR_PTR(-ERESTARTSYS); - goto out; + ret = -ERESTARTSYS; + goto err; } state->count++; - if (!state->port) { - state->count--; - mutex_unlock(&state->mutex); - state = ERR_PTR(-ENXIO); - goto out; + if (!state->port || state->port->flags & UPF_DEAD) { + ret = -ENXIO; + goto err_unlock; } if (!state->info) { @@ -1531,15 +1529,17 @@ static struct uart_state *uart_get(struct uart_driver *drv, int line) tasklet_init(&state->info->tlet, uart_tasklet_action, (unsigned long)state); } else { - state->count--; - mutex_unlock(&state->mutex); - state = ERR_PTR(-ENOMEM); + ret = -ENOMEM; + goto err_unlock; } } - - out: - mutex_unlock(&port_mutex); return state; + + err_unlock: + state->count--; + mutex_unlock(&state->mutex); + err: + return ERR_PTR(ret); } /* @@ -2085,45 +2085,6 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, } } -/* - * This reverses the effects of uart_configure_port, hanging up the - * port before removal. - */ -static void -uart_unconfigure_port(struct uart_driver *drv, struct uart_state *state) -{ - struct uart_port *port = state->port; - struct uart_info *info = state->info; - - if (info && info->tty) - tty_vhangup(info->tty); - - mutex_lock(&state->mutex); - - state->info = NULL; - - /* - * Free the port IO and memory resources, if any. - */ - if (port->type != PORT_UNKNOWN) - port->ops->release_port(port); - - /* - * Indicate that there isn't a port here anymore. - */ - port->type = PORT_UNKNOWN; - - /* - * Kill the tasklet, and free resources. - */ - if (info) { - tasklet_kill(&info->tlet); - kfree(info); - } - - mutex_unlock(&state->mutex); -} - static struct tty_operations uart_ops = { .open = uart_open, .close = uart_close, @@ -2270,6 +2231,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) state = drv->state + port->line; mutex_lock(&port_mutex); + mutex_lock(&state->mutex); if (state->port) { ret = -EINVAL; goto out; @@ -2304,7 +2266,13 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) port->cons && !(port->cons->flags & CON_ENABLED)) register_console(port->cons); + /* + * Ensure UPF_DEAD is not set. + */ + port->flags &= ~UPF_DEAD; + out: + mutex_unlock(&state->mutex); mutex_unlock(&port_mutex); return ret; @@ -2322,6 +2290,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) { struct uart_state *state = drv->state + port->line; + struct uart_info *info; BUG_ON(in_interrupt()); @@ -2331,12 +2300,49 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) mutex_lock(&port_mutex); + /* + * Mark the port "dead" - this prevents any opens from + * succeeding while we shut down the port. + */ + mutex_lock(&state->mutex); + port->flags |= UPF_DEAD; + mutex_unlock(&state->mutex); + /* * Remove the devices from devfs */ tty_unregister_device(drv->tty_driver, port->line); - uart_unconfigure_port(drv, state); + info = state->info; + if (info && info->tty) + tty_vhangup(info->tty); + + /* + * All users of this port should now be disconnected from + * this driver, and the port shut down. We should be the + * only thread fiddling with this port from now on. + */ + state->info = NULL; + + /* + * Free the port IO and memory resources, if any. + */ + if (port->type != PORT_UNKNOWN) + port->ops->release_port(port); + + /* + * Indicate that there isn't a port here anymore. + */ + port->type = PORT_UNKNOWN; + + /* + * Kill the tasklet, and free resources. + */ + if (info) { + tasklet_kill(&info->tlet); + kfree(info); + } + state->port = NULL; mutex_unlock(&port_mutex); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index c32e60e79dea..bd14858121ea 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -254,6 +254,7 @@ struct uart_port { #define UPF_CONS_FLOW ((__force upf_t) (1 << 23)) #define UPF_SHARE_IRQ ((__force upf_t) (1 << 24)) #define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28)) +#define UPF_DEAD ((__force upf_t) (1 << 30)) #define UPF_IOREMAP ((__force upf_t) (1 << 31)) #define UPF_CHANGE_MASK ((__force upf_t) (0x17fff)) -- cgit v1.2.3 From 5411be59db80333039386f3b1ccfe5eb9023a916 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 29 Mar 2006 20:23:36 -0500 Subject: [PATCH] drop task argument of audit_syscall_{entry,exit} ... it's always current, and that's a good thing - allows simpler locking. Signed-off-by: Al Viro --- arch/i386/kernel/ptrace.c | 7 +++---- arch/i386/kernel/vm86.c | 2 +- arch/ia64/kernel/ptrace.c | 4 ++-- arch/mips/kernel/ptrace.c | 4 ++-- arch/powerpc/kernel/ptrace.c | 5 ++--- arch/s390/kernel/ptrace.c | 5 ++--- arch/sparc64/kernel/ptrace.c | 5 ++--- arch/um/kernel/ptrace.c | 6 ++---- arch/x86_64/kernel/ptrace.c | 6 +++--- include/linux/audit.h | 8 ++++---- kernel/auditsc.c | 8 ++++---- 11 files changed, 27 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c index 506462ef36a0..fd7eaf7866e0 100644 --- a/arch/i386/kernel/ptrace.c +++ b/arch/i386/kernel/ptrace.c @@ -671,7 +671,7 @@ int do_syscall_trace(struct pt_regs *regs, int entryexit) if (unlikely(current->audit_context)) { if (entryexit) - audit_syscall_exit(current, AUDITSC_RESULT(regs->eax), + audit_syscall_exit(AUDITSC_RESULT(regs->eax), regs->eax); /* Debug traps, when using PTRACE_SINGLESTEP, must be sent only * on the syscall exit path. Normally, when TIF_SYSCALL_AUDIT is @@ -720,14 +720,13 @@ int do_syscall_trace(struct pt_regs *regs, int entryexit) ret = is_sysemu; out: if (unlikely(current->audit_context) && !entryexit) - audit_syscall_entry(current, AUDIT_ARCH_I386, regs->orig_eax, + audit_syscall_entry(AUDIT_ARCH_I386, regs->orig_eax, regs->ebx, regs->ecx, regs->edx, regs->esi); if (ret == 0) return 0; regs->orig_eax = -1; /* force skip of syscall restarting */ if (unlikely(current->audit_context)) - audit_syscall_exit(current, AUDITSC_RESULT(regs->eax), - regs->eax); + audit_syscall_exit(AUDITSC_RESULT(regs->eax), regs->eax); return 1; } diff --git a/arch/i386/kernel/vm86.c b/arch/i386/kernel/vm86.c index aee14fafd13d..00e0118e717c 100644 --- a/arch/i386/kernel/vm86.c +++ b/arch/i386/kernel/vm86.c @@ -312,7 +312,7 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk /*call audit_syscall_exit since we do not exit via the normal paths */ if (unlikely(current->audit_context)) - audit_syscall_exit(current, AUDITSC_RESULT(eax), eax); + audit_syscall_exit(AUDITSC_RESULT(eax), eax); __asm__ __volatile__( "movl %0,%%esp\n\t" diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index 9887c8787e7a..e61e15e28d8b 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -1644,7 +1644,7 @@ syscall_trace_enter (long arg0, long arg1, long arg2, long arg3, arch = AUDIT_ARCH_IA64; } - audit_syscall_entry(current, arch, syscall, arg0, arg1, arg2, arg3); + audit_syscall_entry(arch, syscall, arg0, arg1, arg2, arg3); } } @@ -1662,7 +1662,7 @@ syscall_trace_leave (long arg0, long arg1, long arg2, long arg3, if (success != AUDITSC_SUCCESS) result = -result; - audit_syscall_exit(current, success, result); + audit_syscall_exit(success, result); } if (test_thread_flag(TIF_SYSCALL_TRACE) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index f3106d0771b0..9b4733c12395 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -483,7 +483,7 @@ static inline int audit_arch(void) asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit) { if (unlikely(current->audit_context) && entryexit) - audit_syscall_exit(current, AUDITSC_RESULT(regs->regs[2]), + audit_syscall_exit(AUDITSC_RESULT(regs->regs[2]), regs->regs[2]); if (!(current->ptrace & PT_PTRACED)) @@ -507,7 +507,7 @@ asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit) } out: if (unlikely(current->audit_context) && !entryexit) - audit_syscall_entry(current, audit_arch(), regs->regs[2], + audit_syscall_entry(audit_arch(), regs->regs[2], regs->regs[4], regs->regs[5], regs->regs[6], regs->regs[7]); } diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index bcb83574335b..4a677d1bd4ef 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -538,7 +538,7 @@ void do_syscall_trace_enter(struct pt_regs *regs) do_syscall_trace(); if (unlikely(current->audit_context)) - audit_syscall_entry(current, + audit_syscall_entry( #ifdef CONFIG_PPC32 AUDIT_ARCH_PPC, #else @@ -556,8 +556,7 @@ void do_syscall_trace_leave(struct pt_regs *regs) #endif if (unlikely(current->audit_context)) - audit_syscall_exit(current, - (regs->ccr&0x1000)?AUDITSC_FAILURE:AUDITSC_SUCCESS, + audit_syscall_exit((regs->ccr&0x1000)?AUDITSC_FAILURE:AUDITSC_SUCCESS, regs->result); if ((test_thread_flag(TIF_SYSCALL_TRACE) diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index 37dfe33dab73..8f36504075ed 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -734,7 +734,7 @@ asmlinkage void syscall_trace(struct pt_regs *regs, int entryexit) { if (unlikely(current->audit_context) && entryexit) - audit_syscall_exit(current, AUDITSC_RESULT(regs->gprs[2]), regs->gprs[2]); + audit_syscall_exit(AUDITSC_RESULT(regs->gprs[2]), regs->gprs[2]); if (!test_thread_flag(TIF_SYSCALL_TRACE)) goto out; @@ -761,8 +761,7 @@ syscall_trace(struct pt_regs *regs, int entryexit) } out: if (unlikely(current->audit_context) && !entryexit) - audit_syscall_entry(current, - test_thread_flag(TIF_31BIT)?AUDIT_ARCH_S390:AUDIT_ARCH_S390X, + audit_syscall_entry(test_thread_flag(TIF_31BIT)?AUDIT_ARCH_S390:AUDIT_ARCH_S390X, regs->gprs[2], regs->orig_gpr2, regs->gprs[3], regs->gprs[4], regs->gprs[5]); } diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index 49e6dedd027d..d31975e6d6f6 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c @@ -653,7 +653,7 @@ asmlinkage void syscall_trace(struct pt_regs *regs, int syscall_exit_p) if (unlikely(tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) result = AUDITSC_FAILURE; - audit_syscall_exit(current, result, regs->u_regs[UREG_I0]); + audit_syscall_exit(result, regs->u_regs[UREG_I0]); } if (!(current->ptrace & PT_PTRACED)) @@ -677,8 +677,7 @@ asmlinkage void syscall_trace(struct pt_regs *regs, int syscall_exit_p) out: if (unlikely(current->audit_context) && !syscall_exit_p) - audit_syscall_entry(current, - (test_thread_flag(TIF_32BIT) ? + audit_syscall_entry((test_thread_flag(TIF_32BIT) ? AUDIT_ARCH_SPARC : AUDIT_ARCH_SPARC64), regs->u_regs[UREG_G1], diff --git a/arch/um/kernel/ptrace.c b/arch/um/kernel/ptrace.c index 60d2eda995c1..9a77fb3c269d 100644 --- a/arch/um/kernel/ptrace.c +++ b/arch/um/kernel/ptrace.c @@ -275,15 +275,13 @@ void syscall_trace(union uml_pt_regs *regs, int entryexit) if (unlikely(current->audit_context)) { if (!entryexit) - audit_syscall_entry(current, - HOST_AUDIT_ARCH, + audit_syscall_entry(HOST_AUDIT_ARCH, UPT_SYSCALL_NR(regs), UPT_SYSCALL_ARG1(regs), UPT_SYSCALL_ARG2(regs), UPT_SYSCALL_ARG3(regs), UPT_SYSCALL_ARG4(regs)); - else audit_syscall_exit(current, - AUDITSC_RESULT(UPT_SYSCALL_RET(regs)), + else audit_syscall_exit(AUDITSC_RESULT(UPT_SYSCALL_RET(regs)), UPT_SYSCALL_RET(regs)); } diff --git a/arch/x86_64/kernel/ptrace.c b/arch/x86_64/kernel/ptrace.c index da8e7903d817..2d50024c9f30 100644 --- a/arch/x86_64/kernel/ptrace.c +++ b/arch/x86_64/kernel/ptrace.c @@ -600,12 +600,12 @@ asmlinkage void syscall_trace_enter(struct pt_regs *regs) if (unlikely(current->audit_context)) { if (test_thread_flag(TIF_IA32)) { - audit_syscall_entry(current, AUDIT_ARCH_I386, + audit_syscall_entry(AUDIT_ARCH_I386, regs->orig_rax, regs->rbx, regs->rcx, regs->rdx, regs->rsi); } else { - audit_syscall_entry(current, AUDIT_ARCH_X86_64, + audit_syscall_entry(AUDIT_ARCH_X86_64, regs->orig_rax, regs->rdi, regs->rsi, regs->rdx, regs->r10); @@ -616,7 +616,7 @@ asmlinkage void syscall_trace_enter(struct pt_regs *regs) asmlinkage void syscall_trace_leave(struct pt_regs *regs) { if (unlikely(current->audit_context)) - audit_syscall_exit(current, AUDITSC_RESULT(regs->rax), regs->rax); + audit_syscall_exit(AUDITSC_RESULT(regs->rax), regs->rax); if ((test_thread_flag(TIF_SYSCALL_TRACE) || test_thread_flag(TIF_SINGLESTEP)) diff --git a/include/linux/audit.h b/include/linux/audit.h index 1c47c59058c1..39fef6ebb854 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -287,10 +287,10 @@ struct netlink_skb_parms; /* Public API */ extern int audit_alloc(struct task_struct *task); extern void audit_free(struct task_struct *task); -extern void audit_syscall_entry(struct task_struct *task, int arch, +extern void audit_syscall_entry(int arch, int major, unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3); -extern void audit_syscall_exit(struct task_struct *task, int failed, long return_code); +extern void audit_syscall_exit(int failed, long return_code); extern void audit_getname(const char *name); extern void audit_putname(const char *name); extern void __audit_inode(const char *name, const struct inode *inode, unsigned flags); @@ -323,8 +323,8 @@ extern int audit_set_macxattr(const char *name); #else #define audit_alloc(t) ({ 0; }) #define audit_free(t) do { ; } while (0) -#define audit_syscall_entry(t,ta,a,b,c,d,e) do { ; } while (0) -#define audit_syscall_exit(t,f,r) do { ; } while (0) +#define audit_syscall_entry(ta,a,b,c,d,e) do { ; } while (0) +#define audit_syscall_exit(f,r) do { ; } while (0) #define audit_getname(n) do { ; } while (0) #define audit_putname(n) do { ; } while (0) #define __audit_inode(n,i,f) do { ; } while (0) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index ba0ec1ba6698..7ed82b088e4b 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -736,10 +736,11 @@ void audit_free(struct task_struct *tsk) * will only be written if another part of the kernel requests that it * be written). */ -void audit_syscall_entry(struct task_struct *tsk, int arch, int major, +void audit_syscall_entry(int arch, int major, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4) { + struct task_struct *tsk = current; struct audit_context *context = tsk->audit_context; enum audit_state state; @@ -817,12 +818,11 @@ void audit_syscall_entry(struct task_struct *tsk, int arch, int major, * message), then write out the syscall information. In call cases, * free the names stored from getname(). */ -void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code) +void audit_syscall_exit(int valid, long return_code) { + struct task_struct *tsk = current; struct audit_context *context; - /* tsk == current */ - get_task_struct(tsk); task_lock(tsk); context = audit_get_context(tsk, valid, return_code); -- cgit v1.2.3 From 376bd9cb357ec945ac893feaeb63af7370a6e70b Mon Sep 17 00:00:00 2001 From: Darrel Goeddel Date: Fri, 24 Feb 2006 15:44:05 -0600 Subject: [PATCH] support for context based audit filtering The following patch provides selinux interfaces that will allow the audit system to perform filtering based on the process context (user, role, type, sensitivity, and clearance). These interfaces will allow the selinux module to perform efficient matches based on lower level selinux constructs, rather than relying on context retrievals and string comparisons within the audit module. It also allows for dominance checks on the mls portion of the contexts that are impossible with only string comparisons. Signed-off-by: Darrel Goeddel Signed-off-by: Al Viro --- include/linux/audit.h | 5 + include/linux/selinux.h | 112 ++++++++++++++++++++ security/selinux/Makefile | 2 +- security/selinux/avc.c | 13 +-- security/selinux/exports.c | 28 +++++ security/selinux/ss/mls.c | 30 +++++- security/selinux/ss/mls.h | 4 +- security/selinux/ss/services.c | 235 ++++++++++++++++++++++++++++++++++++++++- 8 files changed, 419 insertions(+), 10 deletions(-) create mode 100644 include/linux/selinux.h create mode 100644 security/selinux/exports.c (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index 39fef6ebb854..740f950397b7 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -145,6 +145,11 @@ #define AUDIT_PERS 10 #define AUDIT_ARCH 11 #define AUDIT_MSGTYPE 12 +#define AUDIT_SE_USER 13 /* security label user */ +#define AUDIT_SE_ROLE 14 /* security label role */ +#define AUDIT_SE_TYPE 15 /* security label type */ +#define AUDIT_SE_SEN 16 /* security label sensitivity label */ +#define AUDIT_SE_CLR 17 /* security label clearance label */ /* These are ONLY useful when checking * at syscall exit time (AUDIT_AT_EXIT). */ diff --git a/include/linux/selinux.h b/include/linux/selinux.h new file mode 100644 index 000000000000..9d684b1728b0 --- /dev/null +++ b/include/linux/selinux.h @@ -0,0 +1,112 @@ +/* + * SELinux services exported to the rest of the kernel. + * + * Author: James Morris + * + * Copyright (C) 2005 Red Hat, Inc., James Morris + * Copyright (C) 2006 Trusted Computer Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + */ +#ifndef _LINUX_SELINUX_H +#define _LINUX_SELINUX_H + +struct selinux_audit_rule; +struct audit_context; + +#ifdef CONFIG_SECURITY_SELINUX + +/** + * selinux_audit_rule_init - alloc/init an selinux audit rule structure. + * @field: the field this rule refers to + * @op: the operater the rule uses + * @rulestr: the text "target" of the rule + * @rule: pointer to the new rule structure returned via this + * + * Returns 0 if successful, -errno if not. On success, the rule structure + * will be allocated internally. The caller must free this structure with + * selinux_audit_rule_free() after use. + */ +int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, + struct selinux_audit_rule **rule); + +/** + * selinux_audit_rule_free - free an selinux audit rule structure. + * @rule: pointer to the audit rule to be freed + * + * This will free all memory associated with the given rule. + * If @rule is NULL, no operation is performed. + */ +void selinux_audit_rule_free(struct selinux_audit_rule *rule); + +/** + * selinux_audit_rule_match - determine if a context ID matches a rule. + * @ctxid: the context ID to check + * @field: the field this rule refers to + * @op: the operater the rule uses + * @rule: pointer to the audit rule to check against + * @actx: the audit context (can be NULL) associated with the check + * + * Returns 1 if the context id matches the rule, 0 if it does not, and + * -errno on failure. + */ +int selinux_audit_rule_match(u32 ctxid, u32 field, u32 op, + struct selinux_audit_rule *rule, + struct audit_context *actx); + +/** + * selinux_audit_set_callback - set the callback for policy reloads. + * @callback: the function to call when the policy is reloaded + * + * This sets the function callback function that will update the rules + * upon policy reloads. This callback should rebuild all existing rules + * using selinux_audit_rule_init(). + */ +void selinux_audit_set_callback(int (*callback)(void)); + +/** + * selinux_task_ctxid - determine a context ID for a process. + * @tsk: the task object + * @ctxid: ID value returned via this + * + * On return, ctxid will contain an ID for the context. This value + * should only be used opaquely. + */ +void selinux_task_ctxid(struct task_struct *tsk, u32 *ctxid); + +#else + +static inline int selinux_audit_rule_init(u32 field, u32 op, + char *rulestr, + struct selinux_audit_rule **rule) +{ + return -ENOTSUPP; +} + +static inline void selinux_audit_rule_free(struct selinux_audit_rule *rule) +{ + return; +} + +static inline int selinux_audit_rule_match(u32 ctxid, u32 field, u32 op, + struct selinux_audit_rule *rule, + struct audit_context *actx) +{ + return 0; +} + +static inline void selinux_audit_set_callback(int (*callback)(void)) +{ + return; +} + +static inline void selinux_task_ctxid(struct task_struct *tsk, u32 *ctxid) +{ + *ctxid = 0; +} + +#endif /* CONFIG_SECURITY_SELINUX */ + +#endif /* _LINUX_SELINUX_H */ diff --git a/security/selinux/Makefile b/security/selinux/Makefile index 688c0a267b62..faf2e02e4410 100644 --- a/security/selinux/Makefile +++ b/security/selinux/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_SELINUX) := selinux.o ss/ -selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o +selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o exports.o selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o diff --git a/security/selinux/avc.c b/security/selinux/avc.c index ac5d69bb3377..a300702da527 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -800,7 +800,7 @@ out: int avc_ss_reset(u32 seqno) { struct avc_callback_node *c; - int i, rc = 0; + int i, rc = 0, tmprc; unsigned long flag; struct avc_node *node; @@ -813,15 +813,16 @@ int avc_ss_reset(u32 seqno) for (c = avc_callbacks; c; c = c->next) { if (c->events & AVC_CALLBACK_RESET) { - rc = c->callback(AVC_CALLBACK_RESET, - 0, 0, 0, 0, NULL); - if (rc) - goto out; + tmprc = c->callback(AVC_CALLBACK_RESET, + 0, 0, 0, 0, NULL); + /* save the first error encountered for the return + value and continue processing the callbacks */ + if (!rc) + rc = tmprc; } } avc_latest_notif_update(seqno, 0); -out: return rc; } diff --git a/security/selinux/exports.c b/security/selinux/exports.c new file mode 100644 index 000000000000..333c4c7824d8 --- /dev/null +++ b/security/selinux/exports.c @@ -0,0 +1,28 @@ +/* + * SELinux services exported to the rest of the kernel. + * + * Author: James Morris + * + * Copyright (C) 2005 Red Hat, Inc., James Morris + * Copyright (C) 2006 Trusted Computer Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + */ +#include +#include +#include +#include + +#include "security.h" +#include "objsec.h" + +void selinux_task_ctxid(struct task_struct *tsk, u32 *ctxid) +{ + struct task_security_struct *tsec = tsk->security; + if (selinux_enabled) + *ctxid = tsec->sid; + else + *ctxid = 0; +} diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index 84047f69f9c1..7bc5b6440f70 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -8,7 +8,7 @@ * * Support for enhanced MLS infrastructure. * - * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. + * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. */ #include @@ -384,6 +384,34 @@ out: return rc; } +/* + * Set the MLS fields in the security context structure + * `context' based on the string representation in + * the string `str'. This function will allocate temporary memory with the + * given constraints of gfp_mask. + */ +int mls_from_string(char *str, struct context *context, gfp_t gfp_mask) +{ + char *tmpstr, *freestr; + int rc; + + if (!selinux_mls_enabled) + return -EINVAL; + + /* we need freestr because mls_context_to_sid will change + the value of tmpstr */ + tmpstr = freestr = kstrdup(str, gfp_mask); + if (!tmpstr) { + rc = -ENOMEM; + } else { + rc = mls_context_to_sid(':', &tmpstr, context, + NULL, SECSID_NULL); + kfree(freestr); + } + + return rc; +} + /* * Copies the effective MLS range from `src' into `dst'. */ diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h index 03de697c8058..fbb42f07dd7c 100644 --- a/security/selinux/ss/mls.h +++ b/security/selinux/ss/mls.h @@ -8,7 +8,7 @@ * * Support for enhanced MLS infrastructure. * - * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. + * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. */ #ifndef _SS_MLS_H_ @@ -27,6 +27,8 @@ int mls_context_to_sid(char oldc, struct sidtab *s, u32 def_sid); +int mls_from_string(char *str, struct context *context, gfp_t gfp_mask); + int mls_convert_context(struct policydb *oldp, struct policydb *newp, struct context *context); diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 61492485de84..7177e98df7f3 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -7,12 +7,13 @@ * Updated: Trusted Computer Solutions, Inc. * * Support for enhanced MLS infrastructure. + * Support for context based audit filters. * * Updated: Frank Mayer and Karl MacMillan * * Added conditional policy language extensions * - * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. + * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. * Copyright (C) 2003 - 2004 Tresys Technology, LLC * Copyright (C) 2003 Red Hat, Inc., James Morris * This program is free software; you can redistribute it and/or modify @@ -1811,3 +1812,235 @@ out: POLICY_RDUNLOCK; return rc; } + +struct selinux_audit_rule { + u32 au_seqno; + struct context au_ctxt; +}; + +void selinux_audit_rule_free(struct selinux_audit_rule *rule) +{ + if (rule) { + context_destroy(&rule->au_ctxt); + kfree(rule); + } +} + +int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, + struct selinux_audit_rule **rule) +{ + struct selinux_audit_rule *tmprule; + struct role_datum *roledatum; + struct type_datum *typedatum; + struct user_datum *userdatum; + int rc = 0; + + *rule = NULL; + + if (!ss_initialized) + return -ENOTSUPP; + + switch (field) { + case AUDIT_SE_USER: + case AUDIT_SE_ROLE: + case AUDIT_SE_TYPE: + /* only 'equals' and 'not equals' fit user, role, and type */ + if (op != AUDIT_EQUAL && op != AUDIT_NOT_EQUAL) + return -EINVAL; + break; + case AUDIT_SE_SEN: + case AUDIT_SE_CLR: + /* we do not allow a range, indicated by the presense of '-' */ + if (strchr(rulestr, '-')) + return -EINVAL; + break; + default: + /* only the above fields are valid */ + return -EINVAL; + } + + tmprule = kzalloc(sizeof(struct selinux_audit_rule), GFP_KERNEL); + if (!tmprule) + return -ENOMEM; + + context_init(&tmprule->au_ctxt); + + POLICY_RDLOCK; + + tmprule->au_seqno = latest_granting; + + switch (field) { + case AUDIT_SE_USER: + userdatum = hashtab_search(policydb.p_users.table, rulestr); + if (!userdatum) + rc = -EINVAL; + else + tmprule->au_ctxt.user = userdatum->value; + break; + case AUDIT_SE_ROLE: + roledatum = hashtab_search(policydb.p_roles.table, rulestr); + if (!roledatum) + rc = -EINVAL; + else + tmprule->au_ctxt.role = roledatum->value; + break; + case AUDIT_SE_TYPE: + typedatum = hashtab_search(policydb.p_types.table, rulestr); + if (!typedatum) + rc = -EINVAL; + else + tmprule->au_ctxt.type = typedatum->value; + break; + case AUDIT_SE_SEN: + case AUDIT_SE_CLR: + rc = mls_from_string(rulestr, &tmprule->au_ctxt, GFP_ATOMIC); + break; + } + + POLICY_RDUNLOCK; + + if (rc) { + selinux_audit_rule_free(tmprule); + tmprule = NULL; + } + + *rule = tmprule; + + return rc; +} + +int selinux_audit_rule_match(u32 ctxid, u32 field, u32 op, + struct selinux_audit_rule *rule, + struct audit_context *actx) +{ + struct context *ctxt; + struct mls_level *level; + int match = 0; + + if (!rule) { + audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR, + "selinux_audit_rule_match: missing rule\n"); + return -ENOENT; + } + + POLICY_RDLOCK; + + if (rule->au_seqno < latest_granting) { + audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR, + "selinux_audit_rule_match: stale rule\n"); + match = -ESTALE; + goto out; + } + + ctxt = sidtab_search(&sidtab, ctxid); + if (!ctxt) { + audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR, + "selinux_audit_rule_match: unrecognized SID %d\n", + ctxid); + match = -ENOENT; + goto out; + } + + /* a field/op pair that is not caught here will simply fall through + without a match */ + switch (field) { + case AUDIT_SE_USER: + switch (op) { + case AUDIT_EQUAL: + match = (ctxt->user == rule->au_ctxt.user); + break; + case AUDIT_NOT_EQUAL: + match = (ctxt->user != rule->au_ctxt.user); + break; + } + break; + case AUDIT_SE_ROLE: + switch (op) { + case AUDIT_EQUAL: + match = (ctxt->role == rule->au_ctxt.role); + break; + case AUDIT_NOT_EQUAL: + match = (ctxt->role != rule->au_ctxt.role); + break; + } + break; + case AUDIT_SE_TYPE: + switch (op) { + case AUDIT_EQUAL: + match = (ctxt->type == rule->au_ctxt.type); + break; + case AUDIT_NOT_EQUAL: + match = (ctxt->type != rule->au_ctxt.type); + break; + } + break; + case AUDIT_SE_SEN: + case AUDIT_SE_CLR: + level = (op == AUDIT_SE_SEN ? + &ctxt->range.level[0] : &ctxt->range.level[1]); + switch (op) { + case AUDIT_EQUAL: + match = mls_level_eq(&rule->au_ctxt.range.level[0], + level); + break; + case AUDIT_NOT_EQUAL: + match = !mls_level_eq(&rule->au_ctxt.range.level[0], + level); + break; + case AUDIT_LESS_THAN: + match = (mls_level_dom(&rule->au_ctxt.range.level[0], + level) && + !mls_level_eq(&rule->au_ctxt.range.level[0], + level)); + break; + case AUDIT_LESS_THAN_OR_EQUAL: + match = mls_level_dom(&rule->au_ctxt.range.level[0], + level); + break; + case AUDIT_GREATER_THAN: + match = (mls_level_dom(level, + &rule->au_ctxt.range.level[0]) && + !mls_level_eq(level, + &rule->au_ctxt.range.level[0])); + break; + case AUDIT_GREATER_THAN_OR_EQUAL: + match = mls_level_dom(level, + &rule->au_ctxt.range.level[0]); + break; + } + } + +out: + POLICY_RDUNLOCK; + return match; +} + +static int (*aurule_callback)(void) = NULL; + +static int aurule_avc_callback(u32 event, u32 ssid, u32 tsid, + u16 class, u32 perms, u32 *retained) +{ + int err = 0; + + if (event == AVC_CALLBACK_RESET && aurule_callback) + err = aurule_callback(); + return err; +} + +static int __init aurule_init(void) +{ + int err; + + err = avc_add_callback(aurule_avc_callback, AVC_CALLBACK_RESET, + SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0); + if (err) + panic("avc_add_callback() failed, error %d\n", err); + + return err; +} +__initcall(aurule_init); + +void selinux_audit_set_callback(int (*callback)(void)) +{ + aurule_callback = callback; +} -- cgit v1.2.3 From 1b50eed9cac0e8e5e4d3a522d8aa267f7f8f8acb Mon Sep 17 00:00:00 2001 From: Steve Grubb Date: Mon, 3 Apr 2006 14:06:13 -0400 Subject: [PATCH] audit inode patch Previously, we were gathering the context instead of the sid. Now in this patch, we gather just the sid and convert to context only if an audit event is being output. This patch brings the performance hit from 146% down to 23% Signed-off-by: Al Viro --- include/linux/selinux.h | 34 +++++++++++++++++++++++++++++ kernel/auditsc.c | 53 ++++++++++++++-------------------------------- security/selinux/exports.c | 24 +++++++++++++++++++++ 3 files changed, 74 insertions(+), 37 deletions(-) (limited to 'include/linux') diff --git a/include/linux/selinux.h b/include/linux/selinux.h index 9d684b1728b0..84a6c7404687 100644 --- a/include/linux/selinux.h +++ b/include/linux/selinux.h @@ -15,6 +15,7 @@ struct selinux_audit_rule; struct audit_context; +struct inode; #ifdef CONFIG_SECURITY_SELINUX @@ -76,6 +77,27 @@ void selinux_audit_set_callback(int (*callback)(void)); */ void selinux_task_ctxid(struct task_struct *tsk, u32 *ctxid); +/** + * selinux_ctxid_to_string - map a security context ID to a string + * @ctxid: security context ID to be converted. + * @ctx: address of context string to be returned + * @ctxlen: length of returned context string. + * + * Returns 0 if successful, -errno if not. On success, the context + * string will be allocated internally, and the caller must call + * kfree() on it after use. + */ +int selinux_ctxid_to_string(u32 ctxid, char **ctx, u32 *ctxlen); + +/** + * selinux_get_inode_sid - get the inode's security context ID + * @inode: inode structure to get the sid from. + * @sid: pointer to security context ID to be filled in. + * + * Returns nothing + */ +void selinux_get_inode_sid(const struct inode *inode, u32 *sid); + #else static inline int selinux_audit_rule_init(u32 field, u32 op, @@ -107,6 +129,18 @@ static inline void selinux_task_ctxid(struct task_struct *tsk, u32 *ctxid) *ctxid = 0; } +static inline int selinux_ctxid_to_string(u32 ctxid, char **ctx, u32 *ctxlen) +{ + *ctx = NULL; + *ctxlen = 0; + return 0; +} + +static inline void selinux_get_inode_sid(const struct inode *inode, u32 *sid) +{ + *sid = 0; +} + #endif /* CONFIG_SECURITY_SELINUX */ #endif /* _LINUX_SELINUX_H */ diff --git a/kernel/auditsc.c b/kernel/auditsc.c index d3d97d28b69a..2e123a8a0d60 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -90,7 +90,7 @@ struct audit_names { uid_t uid; gid_t gid; dev_t rdev; - char *ctx; + u32 osid; }; struct audit_aux_data { @@ -410,9 +410,6 @@ static inline void audit_free_names(struct audit_context *context) #endif for (i = 0; i < context->name_count; i++) { - char *p = context->names[i].ctx; - context->names[i].ctx = NULL; - kfree(p); if (context->names[i].name) __putname(context->names[i].name); } @@ -674,6 +671,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts } } for (i = 0; i < context->name_count; i++) { + int call_panic = 0; unsigned long ino = context->names[i].ino; unsigned long pino = context->names[i].pino; @@ -703,12 +701,22 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts context->names[i].gid, MAJOR(context->names[i].rdev), MINOR(context->names[i].rdev)); - if (context->names[i].ctx) { - audit_log_format(ab, " obj=%s", - context->names[i].ctx); + if (context->names[i].osid != 0) { + char *ctx = NULL; + u32 len; + if (selinux_ctxid_to_string( + context->names[i].osid, &ctx, &len)) { + audit_log_format(ab, " obj=%u", + context->names[i].osid); + call_panic = 1; + } else + audit_log_format(ab, " obj=%s", ctx); + kfree(ctx); } audit_log_end(ab); + if (call_panic) + audit_panic("error converting sid to string"); } } @@ -946,37 +954,8 @@ void audit_putname(const char *name) void audit_inode_context(int idx, const struct inode *inode) { struct audit_context *context = current->audit_context; - const char *suffix = security_inode_xattr_getsuffix(); - char *ctx = NULL; - int len = 0; - - if (!suffix) - goto ret; - - len = security_inode_getsecurity(inode, suffix, NULL, 0, 0); - if (len == -EOPNOTSUPP) - goto ret; - if (len < 0) - goto error_path; - - ctx = kmalloc(len, GFP_KERNEL); - if (!ctx) - goto error_path; - - len = security_inode_getsecurity(inode, suffix, ctx, len, 0); - if (len < 0) - goto error_path; - - kfree(context->names[idx].ctx); - context->names[idx].ctx = ctx; - goto ret; -error_path: - if (ctx) - kfree(ctx); - audit_panic("error in audit_inode_context"); -ret: - return; + selinux_get_inode_sid(inode, &context->names[idx].osid); } diff --git a/security/selinux/exports.c b/security/selinux/exports.c index 333c4c7824d8..07ddce7bf374 100644 --- a/security/selinux/exports.c +++ b/security/selinux/exports.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "security.h" #include "objsec.h" @@ -26,3 +27,26 @@ void selinux_task_ctxid(struct task_struct *tsk, u32 *ctxid) else *ctxid = 0; } + +int selinux_ctxid_to_string(u32 ctxid, char **ctx, u32 *ctxlen) +{ + if (selinux_enabled) + return security_sid_to_context(ctxid, ctx, ctxlen); + else { + *ctx = NULL; + *ctxlen = 0; + } + + return 0; +} + +void selinux_get_inode_sid(const struct inode *inode, u32 *sid) +{ + if (selinux_enabled) { + struct inode_security_struct *isec = inode->i_security; + *sid = isec->sid; + return; + } + *sid = 0; +} + -- cgit v1.2.3 From 9c7aa6aa74fa8a5cda36e54cbbe4fffe0214497d Mon Sep 17 00:00:00 2001 From: Steve Grubb Date: Fri, 31 Mar 2006 15:22:49 -0500 Subject: [PATCH] change lspp ipc auditing Hi, The patch below converts IPC auditing to collect sid's and convert to context string only if it needs to output an audit record. This patch depends on the inode audit change patch already being applied. Signed-off-by: Steve Grubb Signed-off-by: Al Viro --- include/linux/security.h | 16 ----------- include/linux/selinux.h | 15 ++++++++++ kernel/auditsc.c | 68 ++++++++++++++-------------------------------- security/dummy.c | 6 ---- security/selinux/exports.c | 11 ++++++++ security/selinux/hooks.c | 8 ------ 6 files changed, 47 insertions(+), 77 deletions(-) (limited to 'include/linux') diff --git a/include/linux/security.h b/include/linux/security.h index aaa0a5cdbf75..1bab48f6aeac 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -869,11 +869,6 @@ struct swap_info_struct; * @ipcp contains the kernel IPC permission structure * @flag contains the desired (requested) permission set * Return 0 if permission is granted. - * @ipc_getsecurity: - * Copy the security label associated with the ipc object into - * @buffer. @buffer may be NULL to request the size of the buffer - * required. @size indicates the size of @buffer in bytes. Return - * number of bytes used/required on success. * * Security hooks for individual messages held in System V IPC message queues * @msg_msg_alloc_security: @@ -1223,7 +1218,6 @@ struct security_operations { void (*task_to_inode)(struct task_struct *p, struct inode *inode); int (*ipc_permission) (struct kern_ipc_perm * ipcp, short flag); - int (*ipc_getsecurity)(struct kern_ipc_perm *ipcp, void *buffer, size_t size); int (*msg_msg_alloc_security) (struct msg_msg * msg); void (*msg_msg_free_security) (struct msg_msg * msg); @@ -1887,11 +1881,6 @@ static inline int security_ipc_permission (struct kern_ipc_perm *ipcp, return security_ops->ipc_permission (ipcp, flag); } -static inline int security_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size) -{ - return security_ops->ipc_getsecurity(ipcp, buffer, size); -} - static inline int security_msg_msg_alloc (struct msg_msg * msg) { return security_ops->msg_msg_alloc_security (msg); @@ -2532,11 +2521,6 @@ static inline int security_ipc_permission (struct kern_ipc_perm *ipcp, return 0; } -static inline int security_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size) -{ - return -EOPNOTSUPP; -} - static inline int security_msg_msg_alloc (struct msg_msg * msg) { return 0; diff --git a/include/linux/selinux.h b/include/linux/selinux.h index 84a6c7404687..413d66773b91 100644 --- a/include/linux/selinux.h +++ b/include/linux/selinux.h @@ -16,6 +16,7 @@ struct selinux_audit_rule; struct audit_context; struct inode; +struct kern_ipc_perm; #ifdef CONFIG_SECURITY_SELINUX @@ -98,6 +99,15 @@ int selinux_ctxid_to_string(u32 ctxid, char **ctx, u32 *ctxlen); */ void selinux_get_inode_sid(const struct inode *inode, u32 *sid); +/** + * selinux_get_ipc_sid - get the ipc security context ID + * @ipcp: ipc structure to get the sid from. + * @sid: pointer to security context ID to be filled in. + * + * Returns nothing + */ +void selinux_get_ipc_sid(const struct kern_ipc_perm *ipcp, u32 *sid); + #else static inline int selinux_audit_rule_init(u32 field, u32 op, @@ -141,6 +151,11 @@ static inline void selinux_get_inode_sid(const struct inode *inode, u32 *sid) *sid = 0; } +static inline void selinux_get_ipc_sid(const struct kern_ipc_perm *ipcp, u32 *sid) +{ + *sid = 0; +} + #endif /* CONFIG_SECURITY_SELINUX */ #endif /* _LINUX_SELINUX_H */ diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 2e123a8a0d60..b4f7223811fe 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -107,7 +107,7 @@ struct audit_aux_data_ipcctl { uid_t uid; gid_t gid; mode_t mode; - char *ctx; + u32 osid; }; struct audit_aux_data_socketcall { @@ -432,11 +432,6 @@ static inline void audit_free_aux(struct audit_context *context) dput(axi->dentry); mntput(axi->mnt); } - if ( aux->type == AUDIT_IPC ) { - struct audit_aux_data_ipcctl *axi = (void *)aux; - if (axi->ctx) - kfree(axi->ctx); - } context->aux = aux->next; kfree(aux); @@ -584,7 +579,7 @@ static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk static void audit_log_exit(struct audit_context *context, struct task_struct *tsk) { - int i; + int i, call_panic = 0; struct audit_buffer *ab; struct audit_aux_data *aux; const char *tty; @@ -635,8 +630,20 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts case AUDIT_IPC: { struct audit_aux_data_ipcctl *axi = (void *)aux; audit_log_format(ab, - " qbytes=%lx iuid=%u igid=%u mode=%x obj=%s", - axi->qbytes, axi->uid, axi->gid, axi->mode, axi->ctx); + " qbytes=%lx iuid=%u igid=%u mode=%x", + axi->qbytes, axi->uid, axi->gid, axi->mode); + if (axi->osid != 0) { + char *ctx = NULL; + u32 len; + if (selinux_ctxid_to_string( + axi->osid, &ctx, &len)) { + audit_log_format(ab, " obj=%u", + axi->osid); + call_panic = 1; + } else + audit_log_format(ab, " obj=%s", ctx); + kfree(ctx); + } break; } case AUDIT_SOCKETCALL: { @@ -671,7 +678,6 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts } } for (i = 0; i < context->name_count; i++) { - int call_panic = 0; unsigned long ino = context->names[i].ino; unsigned long pino = context->names[i].pino; @@ -708,16 +714,16 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts context->names[i].osid, &ctx, &len)) { audit_log_format(ab, " obj=%u", context->names[i].osid); - call_panic = 1; + call_panic = 2; } else audit_log_format(ab, " obj=%s", ctx); kfree(ctx); } audit_log_end(ab); - if (call_panic) - audit_panic("error converting sid to string"); } + if (call_panic) + audit_panic("error converting sid to string"); } /** @@ -951,7 +957,7 @@ void audit_putname(const char *name) #endif } -void audit_inode_context(int idx, const struct inode *inode) +static void audit_inode_context(int idx, const struct inode *inode) { struct audit_context *context = current->audit_context; @@ -1141,38 +1147,6 @@ uid_t audit_get_loginuid(struct audit_context *ctx) return ctx ? ctx->loginuid : -1; } -static char *audit_ipc_context(struct kern_ipc_perm *ipcp) -{ - struct audit_context *context = current->audit_context; - char *ctx = NULL; - int len = 0; - - if (likely(!context)) - return NULL; - - len = security_ipc_getsecurity(ipcp, NULL, 0); - if (len == -EOPNOTSUPP) - goto ret; - if (len < 0) - goto error_path; - - ctx = kmalloc(len, GFP_ATOMIC); - if (!ctx) - goto error_path; - - len = security_ipc_getsecurity(ipcp, ctx, len); - if (len < 0) - goto error_path; - - return ctx; - -error_path: - kfree(ctx); - audit_panic("error in audit_ipc_context"); -ret: - return NULL; -} - /** * audit_ipc_perms - record audit data for ipc * @qbytes: msgq bytes @@ -1198,7 +1172,7 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, str ax->uid = uid; ax->gid = gid; ax->mode = mode; - ax->ctx = audit_ipc_context(ipcp); + selinux_get_ipc_sid(ipcp, &ax->osid); ax->d.type = AUDIT_IPC; ax->d.next = context->aux; diff --git a/security/dummy.c b/security/dummy.c index fd99429278e9..8ccccccc12ac 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -563,11 +563,6 @@ static int dummy_ipc_permission (struct kern_ipc_perm *ipcp, short flag) return 0; } -static int dummy_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size) -{ - return -EOPNOTSUPP; -} - static int dummy_msg_msg_alloc_security (struct msg_msg *msg) { return 0; @@ -976,7 +971,6 @@ void security_fixup_ops (struct security_operations *ops) set_to_dummy_if_null(ops, task_reparent_to_init); set_to_dummy_if_null(ops, task_to_inode); set_to_dummy_if_null(ops, ipc_permission); - set_to_dummy_if_null(ops, ipc_getsecurity); set_to_dummy_if_null(ops, msg_msg_alloc_security); set_to_dummy_if_null(ops, msg_msg_free_security); set_to_dummy_if_null(ops, msg_queue_alloc_security); diff --git a/security/selinux/exports.c b/security/selinux/exports.c index 07ddce7bf374..7357cf247f60 100644 --- a/security/selinux/exports.c +++ b/security/selinux/exports.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "security.h" #include "objsec.h" @@ -50,3 +51,13 @@ void selinux_get_inode_sid(const struct inode *inode, u32 *sid) *sid = 0; } +void selinux_get_ipc_sid(const struct kern_ipc_perm *ipcp, u32 *sid) +{ + if (selinux_enabled) { + struct ipc_security_struct *isec = ipcp->security; + *sid = isec->sid; + return; + } + *sid = 0; +} + diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b61b9554bc27..3cf368a16448 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4052,13 +4052,6 @@ static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag) return ipc_has_perm(ipcp, av); } -static int selinux_ipc_getsecurity(struct kern_ipc_perm *ipcp, void *buffer, size_t size) -{ - struct ipc_security_struct *isec = ipcp->security; - - return selinux_getsecurity(isec->sid, buffer, size); -} - /* module stacking operations */ static int selinux_register_security (const char *name, struct security_operations *ops) { @@ -4321,7 +4314,6 @@ static struct security_operations selinux_ops = { .task_to_inode = selinux_task_to_inode, .ipc_permission = selinux_ipc_permission, - .ipc_getsecurity = selinux_ipc_getsecurity, .msg_msg_alloc_security = selinux_msg_msg_alloc_security, .msg_msg_free_security = selinux_msg_msg_free_security, -- cgit v1.2.3 From e7c3497013a7e5496ce3d5fd3c73b5cf5af7a56e Mon Sep 17 00:00:00 2001 From: Steve Grubb Date: Mon, 3 Apr 2006 09:08:13 -0400 Subject: [PATCH] Reworked patch for labels on user space messages The below patch should be applied after the inode and ipc sid patches. This patch is a reworking of Tim's patch that has been updated to match the inode and ipc patches since its similar. [updated: > Stephen Smalley also wanted to change a variable from isec to tsec in the > user sid patch. ] Signed-off-by: Steve Grubb Signed-off-by: Al Viro --- include/linux/netlink.h | 1 + include/linux/selinux.h | 16 ++++++++++++++++ kernel/audit.c | 22 +++++++++++++++++++--- net/netlink/af_netlink.c | 2 ++ security/selinux/exports.c | 11 +++++++++++ 5 files changed, 49 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netlink.h b/include/linux/netlink.h index f8f3d1c927f8..87b8a5703ebc 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -143,6 +143,7 @@ struct netlink_skb_parms __u32 dst_group; kernel_cap_t eff_cap; __u32 loginuid; /* Login (audit) uid */ + __u32 sid; /* SELinux security id */ }; #define NETLINK_CB(skb) (*(struct netlink_skb_parms*)&((skb)->cb)) diff --git a/include/linux/selinux.h b/include/linux/selinux.h index 413d66773b91..4047bcde4484 100644 --- a/include/linux/selinux.h +++ b/include/linux/selinux.h @@ -5,6 +5,7 @@ * * Copyright (C) 2005 Red Hat, Inc., James Morris * Copyright (C) 2006 Trusted Computer Solutions, Inc. + * Copyright (C) 2006 IBM Corporation, Timothy R. Chavez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -108,6 +109,16 @@ void selinux_get_inode_sid(const struct inode *inode, u32 *sid); */ void selinux_get_ipc_sid(const struct kern_ipc_perm *ipcp, u32 *sid); +/** + * selinux_get_task_sid - return the SID of task + * @tsk: the task whose SID will be returned + * @sid: pointer to security context ID to be filled in. + * + * Returns nothing + */ +void selinux_get_task_sid(struct task_struct *tsk, u32 *sid); + + #else static inline int selinux_audit_rule_init(u32 field, u32 op, @@ -156,6 +167,11 @@ static inline void selinux_get_ipc_sid(const struct kern_ipc_perm *ipcp, u32 *si *sid = 0; } +static inline void selinux_get_task_sid(struct task_struct *tsk, u32 *sid) +{ + *sid = 0; +} + #endif /* CONFIG_SECURITY_SELINUX */ #endif /* _LINUX_SELINUX_H */ diff --git a/kernel/audit.c b/kernel/audit.c index 9060be750c48..7ec9ccae1299 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -390,7 +390,7 @@ static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type) static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { - u32 uid, pid, seq; + u32 uid, pid, seq, sid; void *data; struct audit_status *status_get, status_set; int err; @@ -416,6 +416,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) pid = NETLINK_CREDS(skb)->pid; uid = NETLINK_CREDS(skb)->uid; loginuid = NETLINK_CB(skb).loginuid; + sid = NETLINK_CB(skb).sid; seq = nlh->nlmsg_seq; data = NLMSG_DATA(nlh); @@ -468,8 +469,23 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ab = audit_log_start(NULL, GFP_KERNEL, msg_type); if (ab) { audit_log_format(ab, - "user pid=%d uid=%u auid=%u msg='%.1024s'", - pid, uid, loginuid, (char *)data); + "user pid=%d uid=%u auid=%u", + pid, uid, loginuid); + if (sid) { + char *ctx = NULL; + u32 len; + if (selinux_ctxid_to_string( + sid, &ctx, &len)) { + audit_log_format(ab, + " subj=%u", sid); + /* Maybe call audit_panic? */ + } else + audit_log_format(ab, + " subj=%s", ctx); + kfree(ctx); + } + audit_log_format(ab, " msg='%.1024s'", + (char *)data); audit_set_pid(ab, pid); audit_log_end(ab); } diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 2a233ffcf618..09fbc4bc7088 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -1157,6 +1158,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, NETLINK_CB(skb).dst_pid = dst_pid; NETLINK_CB(skb).dst_group = dst_group; NETLINK_CB(skb).loginuid = audit_get_loginuid(current->audit_context); + selinux_get_task_sid(current, &(NETLINK_CB(skb).sid)); memcpy(NETLINK_CREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); /* What can I do? Netlink is asynchronous, so that diff --git a/security/selinux/exports.c b/security/selinux/exports.c index 7357cf247f60..ae4c73eb3085 100644 --- a/security/selinux/exports.c +++ b/security/selinux/exports.c @@ -5,6 +5,7 @@ * * Copyright (C) 2005 Red Hat, Inc., James Morris * Copyright (C) 2006 Trusted Computer Solutions, Inc. + * Copyright (C) 2006 IBM Corporation, Timothy R. Chavez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -61,3 +62,13 @@ void selinux_get_ipc_sid(const struct kern_ipc_perm *ipcp, u32 *sid) *sid = 0; } +void selinux_get_task_sid(struct task_struct *tsk, u32 *sid) +{ + if (selinux_enabled) { + struct task_security_struct *tsec = tsk->security; + *sid = tsec->sid; + return; + } + *sid = 0; +} + -- cgit v1.2.3 From ce29b682e228c70cdc91a1b2935c5adb2087bab8 Mon Sep 17 00:00:00 2001 From: Steve Grubb Date: Sat, 1 Apr 2006 18:29:34 -0500 Subject: [PATCH] More user space subject labels Hi, The patch below builds upon the patch sent earlier and adds subject label to all audit events generated via the netlink interface. It also cleans up a few other minor things. Signed-off-by: Steve Grubb Signed-off-by: Al Viro --- include/linux/audit.h | 2 +- kernel/audit.c | 132 ++++++++++++++++++++++++++++++++++++++------------ kernel/auditfilter.c | 44 ++++++++++++++--- kernel/auditsc.c | 4 +- 4 files changed, 142 insertions(+), 40 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index 740f950397b7..d5c40823e166 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -371,7 +371,7 @@ extern void audit_log_d_path(struct audit_buffer *ab, extern int audit_filter_user(struct netlink_skb_parms *cb, int type); extern int audit_filter_type(int type); extern int audit_receive_filter(int type, int pid, int uid, int seq, - void *data, size_t datasz, uid_t loginuid); + void *data, size_t datasz, uid_t loginuid, u32 sid); #else #define audit_log(c,g,t,f,...) do { ; } while (0) #define audit_log_start(c,g,t) ({ NULL; }) diff --git a/kernel/audit.c b/kernel/audit.c index 7ec9ccae1299..df57b493e1cb 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -230,49 +230,103 @@ void audit_log_lost(const char *message) } } -static int audit_set_rate_limit(int limit, uid_t loginuid) +static int audit_set_rate_limit(int limit, uid_t loginuid, u32 sid) { - int old = audit_rate_limit; - audit_rate_limit = limit; - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + int old = audit_rate_limit; + + if (sid) { + char *ctx = NULL; + u32 len; + int rc; + if ((rc = selinux_ctxid_to_string(sid, &ctx, &len))) + return rc; + else + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "audit_rate_limit=%d old=%d by auid=%u subj=%s", + limit, old, loginuid, ctx); + kfree(ctx); + } else + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, "audit_rate_limit=%d old=%d by auid=%u", - audit_rate_limit, old, loginuid); + limit, old, loginuid); + audit_rate_limit = limit; return old; } -static int audit_set_backlog_limit(int limit, uid_t loginuid) +static int audit_set_backlog_limit(int limit, uid_t loginuid, u32 sid) { - int old = audit_backlog_limit; - audit_backlog_limit = limit; - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + int old = audit_backlog_limit; + + if (sid) { + char *ctx = NULL; + u32 len; + int rc; + if ((rc = selinux_ctxid_to_string(sid, &ctx, &len))) + return rc; + else + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "audit_backlog_limit=%d old=%d by auid=%u subj=%s", + limit, old, loginuid, ctx); + kfree(ctx); + } else + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, "audit_backlog_limit=%d old=%d by auid=%u", - audit_backlog_limit, old, loginuid); + limit, old, loginuid); + audit_backlog_limit = limit; return old; } -static int audit_set_enabled(int state, uid_t loginuid) +static int audit_set_enabled(int state, uid_t loginuid, u32 sid) { - int old = audit_enabled; + int old = audit_enabled; + if (state != 0 && state != 1) return -EINVAL; - audit_enabled = state; - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + + if (sid) { + char *ctx = NULL; + u32 len; + int rc; + if ((rc = selinux_ctxid_to_string(sid, &ctx, &len))) + return rc; + else + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "audit_enabled=%d old=%d by auid=%u subj=%s", + state, old, loginuid, ctx); + kfree(ctx); + } else + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, "audit_enabled=%d old=%d by auid=%u", - audit_enabled, old, loginuid); + state, old, loginuid); + audit_enabled = state; return old; } -static int audit_set_failure(int state, uid_t loginuid) +static int audit_set_failure(int state, uid_t loginuid, u32 sid) { - int old = audit_failure; + int old = audit_failure; + if (state != AUDIT_FAIL_SILENT && state != AUDIT_FAIL_PRINTK && state != AUDIT_FAIL_PANIC) return -EINVAL; - audit_failure = state; - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + + if (sid) { + char *ctx = NULL; + u32 len; + int rc; + if ((rc = selinux_ctxid_to_string(sid, &ctx, &len))) + return rc; + else + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "audit_failure=%d old=%d by auid=%u subj=%s", + state, old, loginuid, ctx); + kfree(ctx); + } else + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, "audit_failure=%d old=%d by auid=%u", - audit_failure, old, loginuid); + state, old, loginuid); + audit_failure = state; return old; } @@ -437,25 +491,43 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) return -EINVAL; status_get = (struct audit_status *)data; if (status_get->mask & AUDIT_STATUS_ENABLED) { - err = audit_set_enabled(status_get->enabled, loginuid); + err = audit_set_enabled(status_get->enabled, + loginuid, sid); if (err < 0) return err; } if (status_get->mask & AUDIT_STATUS_FAILURE) { - err = audit_set_failure(status_get->failure, loginuid); + err = audit_set_failure(status_get->failure, + loginuid, sid); if (err < 0) return err; } if (status_get->mask & AUDIT_STATUS_PID) { int old = audit_pid; + if (sid) { + char *ctx = NULL; + u32 len; + int rc; + if ((rc = selinux_ctxid_to_string( + sid, &ctx, &len))) + return rc; + else + audit_log(NULL, GFP_KERNEL, + AUDIT_CONFIG_CHANGE, + "audit_pid=%d old=%d by auid=%u subj=%s", + status_get->pid, old, + loginuid, ctx); + kfree(ctx); + } else + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "audit_pid=%d old=%d by auid=%u", + status_get->pid, old, loginuid); audit_pid = status_get->pid; - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "audit_pid=%d old=%d by auid=%u", - audit_pid, old, loginuid); } if (status_get->mask & AUDIT_STATUS_RATE_LIMIT) - audit_set_rate_limit(status_get->rate_limit, loginuid); + audit_set_rate_limit(status_get->rate_limit, + loginuid, sid); if (status_get->mask & AUDIT_STATUS_BACKLOG_LIMIT) audit_set_backlog_limit(status_get->backlog_limit, - loginuid); + loginuid, sid); break; case AUDIT_USER: case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG: @@ -477,7 +549,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (selinux_ctxid_to_string( sid, &ctx, &len)) { audit_log_format(ab, - " subj=%u", sid); + " ssid=%u", sid); /* Maybe call audit_panic? */ } else audit_log_format(ab, @@ -499,7 +571,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) case AUDIT_LIST: err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, uid, seq, data, nlmsg_len(nlh), - loginuid); + loginuid, sid); break; case AUDIT_ADD_RULE: case AUDIT_DEL_RULE: @@ -509,7 +581,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) case AUDIT_LIST_RULES: err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, uid, seq, data, nlmsg_len(nlh), - loginuid); + loginuid, sid); break; case AUDIT_SIGNAL_INFO: sig_data.uid = audit_sig_uid; diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 85a7862143a1..7c134906d689 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -586,9 +586,10 @@ static int audit_list_rules(void *_dest) * @data: payload data * @datasz: size of payload data * @loginuid: loginuid of sender + * @sid: SE Linux Security ID of sender */ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, - size_t datasz, uid_t loginuid) + size_t datasz, uid_t loginuid, u32 sid) { struct task_struct *tsk; int *dest; @@ -631,9 +632,23 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, err = audit_add_rule(entry, &audit_filter_list[entry->rule.listnr]); - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u add rule to list=%d res=%d\n", - loginuid, entry->rule.listnr, !err); + if (sid) { + char *ctx = NULL; + u32 len; + if (selinux_ctxid_to_string(sid, &ctx, &len)) { + /* Maybe call audit_panic? */ + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "auid=%u ssid=%u add rule to list=%d res=%d", + loginuid, sid, entry->rule.listnr, !err); + } else + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "auid=%u subj=%s add rule to list=%d res=%d", + loginuid, ctx, entry->rule.listnr, !err); + kfree(ctx); + } else + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "auid=%u add rule to list=%d res=%d", + loginuid, entry->rule.listnr, !err); if (err) audit_free_rule(entry); @@ -649,9 +664,24 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, err = audit_del_rule(entry, &audit_filter_list[entry->rule.listnr]); - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u remove rule from list=%d res=%d\n", - loginuid, entry->rule.listnr, !err); + + if (sid) { + char *ctx = NULL; + u32 len; + if (selinux_ctxid_to_string(sid, &ctx, &len)) { + /* Maybe call audit_panic? */ + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "auid=%u ssid=%u remove rule from list=%d res=%d", + loginuid, sid, entry->rule.listnr, !err); + } else + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "auid=%u subj=%s remove rule from list=%d res=%d", + loginuid, ctx, entry->rule.listnr, !err); + kfree(ctx); + } else + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "auid=%u remove rule from list=%d res=%d", + loginuid, entry->rule.listnr, !err); audit_free_rule(entry); break; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index b4f7223811fe..d94e0404113c 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -637,7 +637,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts u32 len; if (selinux_ctxid_to_string( axi->osid, &ctx, &len)) { - audit_log_format(ab, " obj=%u", + audit_log_format(ab, " osid=%u", axi->osid); call_panic = 1; } else @@ -712,7 +712,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts u32 len; if (selinux_ctxid_to_string( context->names[i].osid, &ctx, &len)) { - audit_log_format(ab, " obj=%u", + audit_log_format(ab, " osid=%u", context->names[i].osid); call_panic = 2; } else -- cgit v1.2.3 From 073115d6b29c7910feaa08241c6484637f5ca958 Mon Sep 17 00:00:00 2001 From: Steve Grubb Date: Sun, 2 Apr 2006 17:07:33 -0400 Subject: [PATCH] Rework of IPC auditing 1) The audit_ipc_perms() function has been split into two different functions: - audit_ipc_obj() - audit_ipc_set_perm() There's a key shift here... The audit_ipc_obj() collects the uid, gid, mode, and SElinux context label of the current ipc object. This audit_ipc_obj() hook is now found in several places. Most notably, it is hooked in ipcperms(), which is called in various places around the ipc code permforming a MAC check. Additionally there are several places where *checkid() is used to validate that an operation is being performed on a valid object while not necessarily having a nearby ipcperms() call. In these locations, audit_ipc_obj() is called to ensure that the information is captured by the audit system. The audit_set_new_perm() function is called any time the permissions on the ipc object changes. In this case, the NEW permissions are recorded (and note that an audit_ipc_obj() call exists just a few lines before each instance). 2) Support for an AUDIT_IPC_SET_PERM audit message type. This allows for separate auxiliary audit records for normal operations on an IPC object and permissions changes. Note that the same struct audit_aux_data_ipcctl is used and populated, however there are separate audit_log_format statements based on the type of the message. Finally, the AUDIT_IPC block of code in audit_free_aux() was extended to handle aux messages of this new type. No more mem leaks I hope ;-) Signed-off-by: Al Viro --- include/linux/audit.h | 7 +++++-- ipc/msg.c | 11 ++++++++++- ipc/sem.c | 11 ++++++++++- ipc/shm.c | 19 +++++++++++++++--- ipc/util.c | 7 ++++++- kernel/auditsc.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++--- 6 files changed, 98 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index d5c40823e166..b74c148f14e3 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -83,6 +83,7 @@ #define AUDIT_CONFIG_CHANGE 1305 /* Audit system configuration change */ #define AUDIT_SOCKADDR 1306 /* sockaddr copied as syscall arg */ #define AUDIT_CWD 1307 /* Current working directory */ +#define AUDIT_IPC_SET_PERM 1311 /* IPC new permissions record type */ #define AUDIT_AVC 1400 /* SE Linux avc denial or grant */ #define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */ @@ -319,7 +320,8 @@ extern void auditsc_get_stamp(struct audit_context *ctx, struct timespec *t, unsigned int *serial); extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid); extern uid_t audit_get_loginuid(struct audit_context *ctx); -extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp); +extern int audit_ipc_obj(struct kern_ipc_perm *ipcp); +extern int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp); extern int audit_socketcall(int nargs, unsigned long *args); extern int audit_sockaddr(int len, void *addr); extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt); @@ -338,7 +340,8 @@ extern int audit_set_macxattr(const char *name); #define audit_inode_child(d,i,p) do { ; } while (0) #define auditsc_get_stamp(c,t,s) do { BUG(); } while (0) #define audit_get_loginuid(c) ({ -1; }) -#define audit_ipc_perms(q,u,g,m,i) ({ 0; }) +#define audit_ipc_obj(i) ({ 0; }) +#define audit_ipc_set_perm(q,u,g,m,i) ({ 0; }) #define audit_socketcall(n,a) ({ 0; }) #define audit_sockaddr(len, addr) ({ 0; }) #define audit_avc_path(dentry, mnt) ({ 0; }) diff --git a/ipc/msg.c b/ipc/msg.c index 48a7f17a7236..7d1340ccb16b 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -13,6 +13,9 @@ * mostly rewritten, threaded and wake-one semantics added * MSGMAX limit removed, sysctl's added * (c) 1999 Manfred Spraul + * + * support for audit of ipc object properties and permission changes + * Dustin Kirkland */ #include @@ -447,6 +450,11 @@ asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds __user *buf) if (msg_checkid(msq,msqid)) goto out_unlock_up; ipcp = &msq->q_perm; + + err = audit_ipc_obj(ipcp); + if (err) + goto out_unlock_up; + err = -EPERM; if (current->euid != ipcp->cuid && current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) @@ -460,7 +468,8 @@ asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds __user *buf) switch (cmd) { case IPC_SET: { - if ((err = audit_ipc_perms(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode, ipcp))) + err = audit_ipc_set_perm(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode, ipcp); + if (err) goto out_unlock_up; err = -EPERM; diff --git a/ipc/sem.c b/ipc/sem.c index 642659cd596b..7919f8ece6ba 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -61,6 +61,9 @@ * (c) 2001 Red Hat Inc * Lockless wakeup * (c) 2003 Manfred Spraul + * + * support for audit of ipc object properties and permission changes + * Dustin Kirkland */ #include @@ -820,6 +823,11 @@ static int semctl_down(int semid, int semnum, int cmd, int version, union semun goto out_unlock; } ipcp = &sma->sem_perm; + + err = audit_ipc_obj(ipcp); + if (err) + goto out_unlock; + if (current->euid != ipcp->cuid && current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) { err=-EPERM; @@ -836,7 +844,8 @@ static int semctl_down(int semid, int semnum, int cmd, int version, union semun err = 0; break; case IPC_SET: - if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode, ipcp))) + err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode, ipcp); + if (err) goto out_unlock; ipcp->uid = setbuf.uid; ipcp->gid = setbuf.gid; diff --git a/ipc/shm.c b/ipc/shm.c index 1c2faf62bc73..809896851902 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -13,6 +13,8 @@ * Shared /dev/zero support, Kanoj Sarcar * Move the mm functionality over to mm/shmem.c, Christoph Rohland * + * support for audit of ipc object properties and permission changes + * Dustin Kirkland */ #include @@ -542,6 +544,10 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) if(err) goto out_unlock; + err = audit_ipc_obj(&(shp->shm_perm)); + if (err) + goto out_unlock; + if (!capable(CAP_IPC_LOCK)) { err = -EPERM; if (current->euid != shp->shm_perm.uid && @@ -594,6 +600,10 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) if(err) goto out_unlock_up; + err = audit_ipc_obj(&(shp->shm_perm)); + if (err) + goto out_unlock_up; + if (current->euid != shp->shm_perm.uid && current->euid != shp->shm_perm.cuid && !capable(CAP_SYS_ADMIN)) { @@ -627,12 +637,15 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) err=-EINVAL; if(shp==NULL) goto out_up; - if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, - setbuf.mode, &(shp->shm_perm)))) - goto out_unlock_up; err = shm_checkid(shp,shmid); if(err) goto out_unlock_up; + err = audit_ipc_obj(&(shp->shm_perm)); + if (err) + goto out_unlock_up; + err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode, &(shp->shm_perm)); + if (err) + goto out_unlock_up; err=-EPERM; if (current->euid != shp->shm_perm.uid && current->euid != shp->shm_perm.cuid && diff --git a/ipc/util.c b/ipc/util.c index b3dcfad3b4f7..8193299f45f6 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -10,6 +10,8 @@ * Manfred Spraul * Oct 2002 - One lock per IPC id. RCU ipc_free for lock-free grow_ary(). * Mingming Cao + * Mar 2006 - support for audit of ipc object properties + * Dustin Kirkland */ #include @@ -27,6 +29,7 @@ #include #include #include +#include #include @@ -464,8 +467,10 @@ void ipc_rcu_putref(void *ptr) int ipcperms (struct kern_ipc_perm *ipcp, short flag) { /* flag will most probably be 0 or S_...UGO from */ - int requested_mode, granted_mode; + int requested_mode, granted_mode, err; + if (unlikely((err = audit_ipc_obj(ipcp)))) + return err; requested_mode = (flag >> 6) | (flag >> 3) | flag; granted_mode = ipcp->mode; if (current->euid == ipcp->cuid || current->euid == ipcp->uid) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index d94e0404113c..a300736ee037 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -646,6 +646,25 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts } break; } + case AUDIT_IPC_SET_PERM: { + struct audit_aux_data_ipcctl *axi = (void *)aux; + audit_log_format(ab, + " new qbytes=%lx new iuid=%u new igid=%u new mode=%x", + axi->qbytes, axi->uid, axi->gid, axi->mode); + if (axi->osid != 0) { + char *ctx = NULL; + u32 len; + if (selinux_ctxid_to_string( + axi->osid, &ctx, &len)) { + audit_log_format(ab, " osid=%u", + axi->osid); + call_panic = 1; + } else + audit_log_format(ab, " obj=%s", ctx); + kfree(ctx); + } + break; } + case AUDIT_SOCKETCALL: { int i; struct audit_aux_data_socketcall *axs = (void *)aux; @@ -1148,7 +1167,36 @@ uid_t audit_get_loginuid(struct audit_context *ctx) } /** - * audit_ipc_perms - record audit data for ipc + * audit_ipc_obj - record audit data for ipc object + * @ipcp: ipc permissions + * + * Returns 0 for success or NULL context or < 0 on error. + */ +int audit_ipc_obj(struct kern_ipc_perm *ipcp) +{ + struct audit_aux_data_ipcctl *ax; + struct audit_context *context = current->audit_context; + + if (likely(!context)) + return 0; + + ax = kmalloc(sizeof(*ax), GFP_ATOMIC); + if (!ax) + return -ENOMEM; + + ax->uid = ipcp->uid; + ax->gid = ipcp->gid; + ax->mode = ipcp->mode; + selinux_get_ipc_sid(ipcp, &ax->osid); + + ax->d.type = AUDIT_IPC; + ax->d.next = context->aux; + context->aux = (void *)ax; + return 0; +} + +/** + * audit_ipc_set_perm - record audit data for new ipc permissions * @qbytes: msgq bytes * @uid: msgq user id * @gid: msgq group id @@ -1156,7 +1204,7 @@ uid_t audit_get_loginuid(struct audit_context *ctx) * * Returns 0 for success or NULL context or < 0 on error. */ -int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp) +int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp) { struct audit_aux_data_ipcctl *ax; struct audit_context *context = current->audit_context; @@ -1174,7 +1222,7 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, str ax->mode = mode; selinux_get_ipc_sid(ipcp, &ax->osid); - ax->d.type = AUDIT_IPC; + ax->d.type = AUDIT_IPC_SET_PERM; ax->d.next = context->aux; context->aux = (void *)ax; return 0; -- cgit v1.2.3 From 0568b409c74f7a125d92a09a3f386785700ef688 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 1 May 2006 19:50:48 +0200 Subject: [PATCH] splice: fix bugs in pipe_to_file() Found by Oleg Nesterov , fixed by me. - Only allow full pages to go to the page cache. - Check page != buf->page instead of using PIPE_BUF_FLAG_STOLEN. - Remember to clear 'stolen' if add_to_page_cache() fails. And as a cleanup on that: - Make the bottom fall-through logic a little less convoluted. Also make the steal path hold an extra reference to the page, so we don't have to differentiate between stolen and non-stolen at the end. Signed-off-by: Jens Axboe --- fs/pipe.c | 3 --- fs/splice.c | 37 +++++++++++++++++++------------------ include/linux/pipe_fs_i.h | 3 +-- 3 files changed, 20 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/fs/pipe.c b/fs/pipe.c index 5a369273c51b..888f265011bf 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -99,8 +99,6 @@ static void anon_pipe_buf_release(struct pipe_inode_info *pipe, { struct page *page = buf->page; - buf->flags &= ~PIPE_BUF_FLAG_STOLEN; - /* * If nobody else uses this page, and we don't already have a * temporary page, let's keep track of it as a one-deep @@ -130,7 +128,6 @@ static int anon_pipe_buf_steal(struct pipe_inode_info *pipe, struct page *page = buf->page; if (page_count(page) == 1) { - buf->flags |= PIPE_BUF_FLAG_STOLEN; lock_page(page); return 0; } diff --git a/fs/splice.c b/fs/splice.c index 9df28d30efa0..1633778f3652 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -78,7 +78,7 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *info, return 1; } - buf->flags |= PIPE_BUF_FLAG_STOLEN | PIPE_BUF_FLAG_LRU; + buf->flags |= PIPE_BUF_FLAG_LRU; return 0; } @@ -87,7 +87,7 @@ static void page_cache_pipe_buf_release(struct pipe_inode_info *info, { page_cache_release(buf->page); buf->page = NULL; - buf->flags &= ~(PIPE_BUF_FLAG_STOLEN | PIPE_BUF_FLAG_LRU); + buf->flags &= ~PIPE_BUF_FLAG_LRU; } static void *page_cache_pipe_buf_map(struct file *file, @@ -587,9 +587,10 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, this_len = PAGE_CACHE_SIZE - offset; /* - * Reuse buf page, if SPLICE_F_MOVE is set. + * Reuse buf page, if SPLICE_F_MOVE is set and we are doing a full + * page. */ - if (sd->flags & SPLICE_F_MOVE) { + if ((sd->flags & SPLICE_F_MOVE) && this_len == PAGE_CACHE_SIZE) { /* * If steal succeeds, buf->page is now pruned from the vm * side (LRU and page cache) and we can reuse it. The page @@ -604,6 +605,8 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, goto find_page; } + page_cache_get(page); + if (!(buf->flags & PIPE_BUF_FLAG_LRU)) lru_cache_add(page); } else { @@ -662,7 +665,7 @@ find_page: } else if (ret) goto out; - if (!(buf->flags & PIPE_BUF_FLAG_STOLEN)) { + if (buf->page != page) { char *dst = kmap_atomic(page, KM_USER0); memcpy(dst + offset, src + buf->offset, this_len); @@ -671,22 +674,20 @@ find_page: } ret = mapping->a_ops->commit_write(file, page, offset, offset+this_len); - if (ret == AOP_TRUNCATED_PAGE) { + if (!ret) { + /* + * Return the number of bytes written and mark page as + * accessed, we are now done! + */ + ret = this_len; + mark_page_accessed(page); + balance_dirty_pages_ratelimited(mapping); + } else if (ret == AOP_TRUNCATED_PAGE) { page_cache_release(page); goto find_page; - } else if (ret) - goto out; - - /* - * Return the number of bytes written. - */ - ret = this_len; - mark_page_accessed(page); - balance_dirty_pages_ratelimited(mapping); + } out: - if (!(buf->flags & PIPE_BUF_FLAG_STOLEN)) - page_cache_release(page); - + page_cache_release(page); unlock_page(page); out_nomem: buf->ops->unmap(info, buf); diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 0008d4bd4059..3130977fc6ab 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -5,8 +5,7 @@ #define PIPE_BUFFERS (16) -#define PIPE_BUF_FLAG_STOLEN 0x01 -#define PIPE_BUF_FLAG_LRU 0x02 +#define PIPE_BUF_FLAG_LRU 0x01 struct pipe_buffer { struct page *page; -- cgit v1.2.3 From f84d751994441292593523c7069ed147176f6cab Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 1 May 2006 19:59:03 +0200 Subject: [PATCH] pipe: introduce ->pin() buffer operation The ->map() function is really expensive on highmem machines right now, since it has to use the slower kmap() instead of kmap_atomic(). Splice rarely needs to access the virtual address of a page, so it's a waste of time doing it. Introduce ->pin() to take over the responsibility of making sure the page data is valid. ->map() is then reduced to just kmap(). That way we can also share a most of the pipe buffer ops between pipe.c and splice.c Signed-off-by: Jens Axboe --- fs/pipe.c | 39 +++++++++++--------- fs/splice.c | 91 ++++++++++++++++------------------------------- include/linux/pipe_fs_i.h | 21 ++++++++++- 3 files changed, 73 insertions(+), 78 deletions(-) (limited to 'include/linux') diff --git a/fs/pipe.c b/fs/pipe.c index 888f265011bf..d9644fd9cc0d 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -110,14 +110,14 @@ static void anon_pipe_buf_release(struct pipe_inode_info *pipe, page_cache_release(page); } -static void * anon_pipe_buf_map(struct file *file, struct pipe_inode_info *pipe, - struct pipe_buffer *buf) +void *generic_pipe_buf_map(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) { return kmap(buf->page); } -static void anon_pipe_buf_unmap(struct pipe_inode_info *pipe, - struct pipe_buffer *buf) +void generic_pipe_buf_unmap(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) { kunmap(buf->page); } @@ -135,19 +135,24 @@ static int anon_pipe_buf_steal(struct pipe_inode_info *pipe, return 1; } -static void anon_pipe_buf_get(struct pipe_inode_info *info, - struct pipe_buffer *buf) +void generic_pipe_buf_get(struct pipe_inode_info *info, struct pipe_buffer *buf) { page_cache_get(buf->page); } +int generic_pipe_buf_pin(struct pipe_inode_info *info, struct pipe_buffer *buf) +{ + return 0; +} + static struct pipe_buf_operations anon_pipe_buf_ops = { .can_merge = 1, - .map = anon_pipe_buf_map, - .unmap = anon_pipe_buf_unmap, + .map = generic_pipe_buf_map, + .unmap = generic_pipe_buf_unmap, + .pin = generic_pipe_buf_pin, .release = anon_pipe_buf_release, .steal = anon_pipe_buf_steal, - .get = anon_pipe_buf_get, + .get = generic_pipe_buf_get, }; static ssize_t @@ -183,12 +188,14 @@ pipe_readv(struct file *filp, const struct iovec *_iov, if (chars > total_len) chars = total_len; - addr = ops->map(filp, pipe, buf); - if (IS_ERR(addr)) { + error = ops->pin(pipe, buf); + if (error) { if (!ret) - ret = PTR_ERR(addr); + error = ret; break; } + + addr = ops->map(pipe, buf); error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars); ops->unmap(pipe, buf); if (unlikely(error)) { @@ -300,11 +307,11 @@ pipe_writev(struct file *filp, const struct iovec *_iov, void *addr; int error; - addr = ops->map(filp, pipe, buf); - if (IS_ERR(addr)) { - error = PTR_ERR(addr); + error = ops->pin(pipe, buf); + if (error) goto out; - } + + addr = ops->map(pipe, buf); error = pipe_iov_copy_from_user(offset + addr, iov, chars); ops->unmap(pipe, buf); diff --git a/fs/splice.c b/fs/splice.c index 1633778f3652..d7538d83c367 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -90,9 +90,8 @@ static void page_cache_pipe_buf_release(struct pipe_inode_info *info, buf->flags &= ~PIPE_BUF_FLAG_LRU; } -static void *page_cache_pipe_buf_map(struct file *file, - struct pipe_inode_info *info, - struct pipe_buffer *buf) +static int page_cache_pipe_buf_pin(struct pipe_inode_info *info, + struct pipe_buffer *buf) { struct page *page = buf->page; int err; @@ -118,49 +117,25 @@ static void *page_cache_pipe_buf_map(struct file *file, } /* - * Page is ok afterall, fall through to mapping. + * Page is ok afterall, we are done. */ unlock_page(page); } - return kmap(page); + return 0; error: unlock_page(page); - return ERR_PTR(err); -} - -static void page_cache_pipe_buf_unmap(struct pipe_inode_info *info, - struct pipe_buffer *buf) -{ - 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, - struct pipe_buffer *buf) -{ - page_cache_get(buf->page); + return err; } static struct pipe_buf_operations page_cache_pipe_buf_ops = { .can_merge = 0, - .map = page_cache_pipe_buf_map, - .unmap = page_cache_pipe_buf_unmap, + .map = generic_pipe_buf_map, + .unmap = generic_pipe_buf_unmap, + .pin = page_cache_pipe_buf_pin, .release = page_cache_pipe_buf_release, .steal = page_cache_pipe_buf_steal, - .get = page_cache_pipe_buf_get, + .get = generic_pipe_buf_get, }; static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe, @@ -171,11 +146,12 @@ static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe, 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, + .map = generic_pipe_buf_map, + .unmap = generic_pipe_buf_unmap, + .pin = generic_pipe_buf_pin, .release = page_cache_pipe_buf_release, .steal = user_page_pipe_buf_steal, - .get = page_cache_pipe_buf_get, + .get = generic_pipe_buf_get, }; /* @@ -517,26 +493,16 @@ static int pipe_to_sendpage(struct pipe_inode_info *info, { struct file *file = sd->file; loff_t pos = sd->pos; - ssize_t ret; - void *ptr; - int more; + int ret, more; - /* - * Sub-optimal, but we are limited by the pipe ->map. We don't - * need a kmap'ed buffer here, we just want to make sure we - * have the page pinned if the pipe page originates from the - * page cache. - */ - ptr = buf->ops->map(file, info, buf); - if (IS_ERR(ptr)) - return PTR_ERR(ptr); - - more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len; + ret = buf->ops->pin(info, buf); + if (!ret) { + more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len; - ret = file->f_op->sendpage(file, buf->page, buf->offset, sd->len, - &pos, more); + ret = file->f_op->sendpage(file, buf->page, buf->offset, + sd->len, &pos, more); + } - buf->ops->unmap(info, buf); return ret; } @@ -569,15 +535,14 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, unsigned int offset, this_len; struct page *page; pgoff_t index; - char *src; int ret; /* * make sure the data in this buffer is uptodate */ - src = buf->ops->map(file, info, buf); - if (IS_ERR(src)) - return PTR_ERR(src); + ret = buf->ops->pin(info, buf); + if (unlikely(ret)) + return ret; index = sd->pos >> PAGE_CACHE_SHIFT; offset = sd->pos & ~PAGE_CACHE_MASK; @@ -666,11 +631,16 @@ find_page: goto out; if (buf->page != page) { - char *dst = kmap_atomic(page, KM_USER0); + /* + * Careful, ->map() uses KM_USER0! + */ + char *src = buf->ops->map(info, buf); + char *dst = kmap_atomic(page, KM_USER1); memcpy(dst + offset, src + buf->offset, this_len); flush_dcache_page(page); - kunmap_atomic(dst, KM_USER0); + kunmap_atomic(dst, KM_USER1); + buf->ops->unmap(info, buf); } ret = mapping->a_ops->commit_write(file, page, offset, offset+this_len); @@ -690,7 +660,6 @@ out: page_cache_release(page); unlock_page(page); out_nomem: - buf->ops->unmap(info, buf); return ret; } diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 3130977fc6ab..b8aae1fc5185 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -14,10 +14,23 @@ struct pipe_buffer { unsigned int flags; }; +/* + * Note on the nesting of these functions: + * + * ->pin() + * ->steal() + * ... + * ->map() + * ... + * ->unmap() + * + * That is, ->map() must be called on a pinned buffer, same goes for ->steal(). + */ struct pipe_buf_operations { int can_merge; - void * (*map)(struct file *, struct pipe_inode_info *, struct pipe_buffer *); + void * (*map)(struct pipe_inode_info *, struct pipe_buffer *); void (*unmap)(struct pipe_inode_info *, struct pipe_buffer *); + int (*pin)(struct pipe_inode_info *, struct pipe_buffer *); void (*release)(struct pipe_inode_info *, struct pipe_buffer *); int (*steal)(struct pipe_inode_info *, struct pipe_buffer *); void (*get)(struct pipe_inode_info *, struct pipe_buffer *); @@ -50,6 +63,12 @@ struct pipe_inode_info * alloc_pipe_info(struct inode * inode); void free_pipe_info(struct inode * inode); void __free_pipe_info(struct pipe_inode_info *); +/* Generic pipe buffer ops functions */ +void *generic_pipe_buf_map(struct pipe_inode_info *, struct pipe_buffer *); +void generic_pipe_buf_unmap(struct pipe_inode_info *, struct pipe_buffer *); +void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *); +int generic_pipe_buf_pin(struct pipe_inode_info *, struct pipe_buffer *); + /* * splice is tied to pipes as a transport (at least for now), so we'll just * add the splice flags here. -- cgit v1.2.3 From f6762b7ad8edd6abc802542ce845d3bc8adcb92f Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 1 May 2006 20:02:05 +0200 Subject: [PATCH] pipe: enable atomic copying of pipe data to/from user space The pipe ->map() method uses kmap() to virtually map the pages, which is both slow and has known scalability issues on SMP. This patch enables atomic copying of pipe pages, by pre-faulting data and using kmap_atomic() instead. lmbench bw_pipe and lat_pipe measurements agree this is a Good Thing. Here are results from that on a UP machine with highmem (1.5GiB of RAM), running first a UP kernel, SMP kernel, and SMP kernel patched. Vanilla-UP: Pipe bandwidth: 1622.28 MB/sec Pipe bandwidth: 1610.59 MB/sec Pipe bandwidth: 1608.30 MB/sec Pipe latency: 7.3275 microseconds Pipe latency: 7.2995 microseconds Pipe latency: 7.3097 microseconds Vanilla-SMP: Pipe bandwidth: 1382.19 MB/sec Pipe bandwidth: 1317.27 MB/sec Pipe bandwidth: 1355.61 MB/sec Pipe latency: 9.6402 microseconds Pipe latency: 9.6696 microseconds Pipe latency: 9.6153 microseconds Patched-SMP: Pipe bandwidth: 1578.70 MB/sec Pipe bandwidth: 1579.95 MB/sec Pipe bandwidth: 1578.63 MB/sec Pipe latency: 9.1654 microseconds Pipe latency: 9.2266 microseconds Pipe latency: 9.1527 microseconds Signed-off-by: Jens Axboe --- fs/pipe.c | 141 ++++++++++++++++++++++++++++++++++++++-------- fs/splice.c | 4 +- include/linux/pipe_fs_i.h | 11 ++-- 3 files changed, 126 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/fs/pipe.c b/fs/pipe.c index d9644fd9cc0d..3941a7f78b5d 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -55,7 +55,8 @@ void pipe_wait(struct pipe_inode_info *pipe) } static int -pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len) +pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len, + int atomic) { unsigned long copy; @@ -64,8 +65,13 @@ pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len) iov++; copy = min_t(unsigned long, len, iov->iov_len); - if (copy_from_user(to, iov->iov_base, copy)) - return -EFAULT; + if (atomic) { + if (__copy_from_user_inatomic(to, iov->iov_base, copy)) + return -EFAULT; + } else { + if (copy_from_user(to, iov->iov_base, copy)) + return -EFAULT; + } to += copy; len -= copy; iov->iov_base += copy; @@ -75,7 +81,8 @@ pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len) } static int -pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len) +pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len, + int atomic) { unsigned long copy; @@ -84,8 +91,13 @@ pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len) iov++; copy = min_t(unsigned long, len, iov->iov_len); - if (copy_to_user(iov->iov_base, from, copy)) - return -EFAULT; + 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; @@ -94,6 +106,47 @@ pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len) 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. + */ +static void iov_fault_in_pages_read(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); + fault_in_pages_readable(iov->iov_base, this_len); + len -= this_len; + iov++; + } +} + static void anon_pipe_buf_release(struct pipe_inode_info *pipe, struct pipe_buffer *buf) { @@ -111,15 +164,24 @@ static void anon_pipe_buf_release(struct pipe_inode_info *pipe, } void *generic_pipe_buf_map(struct pipe_inode_info *pipe, - struct pipe_buffer *buf) + struct pipe_buffer *buf, int atomic) { + if (atomic) { + buf->flags |= PIPE_BUF_FLAG_ATOMIC; + return kmap_atomic(buf->page, KM_USER0); + } + return kmap(buf->page); } void generic_pipe_buf_unmap(struct pipe_inode_info *pipe, - struct pipe_buffer *buf) + struct pipe_buffer *buf, void *map_data) { - kunmap(buf->page); + if (buf->flags & PIPE_BUF_FLAG_ATOMIC) { + buf->flags &= ~PIPE_BUF_FLAG_ATOMIC; + kunmap_atomic(map_data, KM_USER0); + } else + kunmap(buf->page); } static int anon_pipe_buf_steal(struct pipe_inode_info *pipe, @@ -183,7 +245,7 @@ pipe_readv(struct file *filp, const struct iovec *_iov, struct pipe_buf_operations *ops = buf->ops; void *addr; size_t chars = buf->len; - int error; + int error, atomic; if (chars > total_len) chars = total_len; @@ -195,12 +257,21 @@ pipe_readv(struct file *filp, const struct iovec *_iov, break; } - addr = ops->map(pipe, buf); - error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars); - ops->unmap(pipe, buf); + atomic = !iov_fault_in_pages_write(iov, chars); +redo: + 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) - ret = -EFAULT; + ret = error; break; } ret += chars; @@ -304,21 +375,28 @@ pipe_writev(struct file *filp, const struct iovec *_iov, int offset = buf->offset + buf->len; if (ops->can_merge && offset + chars <= PAGE_SIZE) { + int error, atomic = 1; void *addr; - int error; error = ops->pin(pipe, buf); if (error) goto out; - addr = ops->map(pipe, buf); + iov_fault_in_pages_read(iov, chars); +redo1: + addr = ops->map(pipe, buf, atomic); error = pipe_iov_copy_from_user(offset + addr, iov, - chars); - ops->unmap(pipe, buf); + chars, atomic); + ops->unmap(pipe, buf, addr); ret = error; do_wakeup = 1; - if (error) + if (error) { + if (atomic) { + atomic = 0; + goto redo1; + } goto out; + } buf->len += chars; total_len -= chars; ret = chars; @@ -341,7 +419,8 @@ pipe_writev(struct file *filp, const struct iovec *_iov, int newbuf = (pipe->curbuf + bufs) & (PIPE_BUFFERS-1); struct pipe_buffer *buf = pipe->bufs + newbuf; struct page *page = pipe->tmp_page; - int error; + char *src; + int error, atomic = 1; if (!page) { page = alloc_page(GFP_HIGHUSER); @@ -361,11 +440,27 @@ pipe_writev(struct file *filp, const struct iovec *_iov, if (chars > total_len) chars = total_len; - error = pipe_iov_copy_from_user(kmap(page), iov, chars); - kunmap(page); + iov_fault_in_pages_read(iov, chars); +redo2: + if (atomic) + src = kmap_atomic(page, KM_USER0); + else + src = kmap(page); + + error = pipe_iov_copy_from_user(src, iov, chars, + atomic); + if (atomic) + kunmap_atomic(src, KM_USER0); + else + kunmap(page); + if (unlikely(error)) { + if (atomic) { + atomic = 0; + goto redo2; + } if (!ret) - ret = -EFAULT; + ret = error; break; } ret += chars; diff --git a/fs/splice.c b/fs/splice.c index 0a6916423e7d..d4664a297bab 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -640,13 +640,13 @@ find_page: /* * Careful, ->map() uses KM_USER0! */ - char *src = buf->ops->map(info, buf); + char *src = buf->ops->map(info, buf, 1); char *dst = kmap_atomic(page, KM_USER1); memcpy(dst + offset, src + buf->offset, this_len); flush_dcache_page(page); kunmap_atomic(dst, KM_USER1); - buf->ops->unmap(info, buf); + buf->ops->unmap(info, buf, src); } ret = mapping->a_ops->commit_write(file, page, offset, offset+this_len); diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index b8aae1fc5185..4c054491e38e 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -5,7 +5,8 @@ #define PIPE_BUFFERS (16) -#define PIPE_BUF_FLAG_LRU 0x01 +#define PIPE_BUF_FLAG_LRU 0x01 /* page is on the LRU */ +#define PIPE_BUF_FLAG_ATOMIC 0x02 /* was atomically mapped */ struct pipe_buffer { struct page *page; @@ -28,8 +29,8 @@ struct pipe_buffer { */ struct pipe_buf_operations { int can_merge; - void * (*map)(struct pipe_inode_info *, struct pipe_buffer *); - void (*unmap)(struct pipe_inode_info *, struct pipe_buffer *); + void * (*map)(struct pipe_inode_info *, struct pipe_buffer *, int); + void (*unmap)(struct pipe_inode_info *, struct pipe_buffer *, void *); int (*pin)(struct pipe_inode_info *, struct pipe_buffer *); void (*release)(struct pipe_inode_info *, struct pipe_buffer *); int (*steal)(struct pipe_inode_info *, struct pipe_buffer *); @@ -64,8 +65,8 @@ void free_pipe_info(struct inode * inode); void __free_pipe_info(struct pipe_inode_info *); /* Generic pipe buffer ops functions */ -void *generic_pipe_buf_map(struct pipe_inode_info *, struct pipe_buffer *); -void generic_pipe_buf_unmap(struct pipe_inode_info *, struct pipe_buffer *); +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 *); int generic_pipe_buf_pin(struct pipe_inode_info *, struct pipe_buffer *); -- cgit v1.2.3 From 7afa6fd037e51e95d322990cb127bb2b1217251a Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 1 May 2006 20:02:33 +0200 Subject: [PATCH] vmsplice: allow user to pass in gift pages If SPLICE_F_GIFT is set, the user is basically giving this pages away to the kernel. That means we can steal them for eg page cache uses instead of copying it. The data must be properly page aligned and also a multiple of the page size in length. Signed-off-by: Jens Axboe --- fs/splice.c | 28 +++++++++++++++++++++++++--- include/linux/pipe_fs_i.h | 2 ++ 2 files changed, 27 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/splice.c b/fs/splice.c index d4664a297bab..b150493b6fc3 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -141,7 +141,10 @@ static struct pipe_buf_operations page_cache_pipe_buf_ops = { static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe, struct pipe_buffer *buf) { - return 1; + if (!(buf->flags & PIPE_BUF_FLAG_GIFT)) + return 1; + + return 0; } static struct pipe_buf_operations user_page_pipe_buf_ops = { @@ -186,6 +189,9 @@ static ssize_t splice_to_pipe(struct pipe_inode_info *pipe, buf->offset = spd->partial[page_nr].offset; buf->len = spd->partial[page_nr].len; buf->ops = spd->ops; + if (spd->flags & SPLICE_F_GIFT) + buf->flags |= PIPE_BUF_FLAG_GIFT; + pipe->nrbufs++; page_nr++; ret += buf->len; @@ -1073,7 +1079,7 @@ static long do_splice(struct file *in, loff_t __user *off_in, */ static int get_iovec_page_array(const struct iovec __user *iov, unsigned int nr_vecs, struct page **pages, - struct partial_page *partial) + struct partial_page *partial, int aligned) { int buffers = 0, error = 0; @@ -1113,6 +1119,15 @@ static int get_iovec_page_array(const struct iovec __user *iov, * in the user pages. */ off = (unsigned long) base & ~PAGE_MASK; + + /* + * If asked for alignment, the offset must be zero and the + * length a multiple of the PAGE_SIZE. + */ + error = -EINVAL; + if (aligned && (off || len & ~PAGE_MASK)) + break; + npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT; if (npages > PIPE_BUFFERS - buffers) npages = PIPE_BUFFERS - buffers; @@ -1206,7 +1221,8 @@ static long do_vmsplice(struct file *file, const struct iovec __user *iov, else if (unlikely(!nr_segs)) return 0; - spd.nr_pages = get_iovec_page_array(iov, nr_segs, pages, partial); + spd.nr_pages = get_iovec_page_array(iov, nr_segs, pages, partial, + flags & SPLICE_F_GIFT); if (spd.nr_pages <= 0) return spd.nr_pages; @@ -1314,6 +1330,12 @@ static int link_pipe(struct pipe_inode_info *ipipe, obuf = opipe->bufs + nbuf; *obuf = *ibuf; + /* + * Don't inherit the gift flag, we need to + * prevent multiple steals of this page. + */ + obuf->flags &= ~PIPE_BUF_FLAG_GIFT; + if (obuf->len > len) obuf->len = len; diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 4c054491e38e..df4d3fa7d3dc 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -7,6 +7,7 @@ #define PIPE_BUF_FLAG_LRU 0x01 /* page is on the LRU */ #define PIPE_BUF_FLAG_ATOMIC 0x02 /* was atomically mapped */ +#define PIPE_BUF_FLAG_GIFT 0x04 /* page is a gift */ struct pipe_buffer { struct page *page; @@ -79,6 +80,7 @@ int generic_pipe_buf_pin(struct pipe_inode_info *, struct pipe_buffer *); /* we may still block on the fd we splice */ /* from/to, of course */ #define SPLICE_F_MORE (0x04) /* expect more data */ +#define SPLICE_F_GIFT (0x08) /* pages passed in are a gift */ /* * Passed to the actors -- cgit v1.2.3 From 46c5ea3c9ae7fbc6e52a13c92e59d4fc7f4ca80a Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 2 May 2006 05:12:22 +0200 Subject: [NETFILTER] x_tables: fix compat related crash on non-x86 When iptables userspace adds an ipt_standard_target, it calculates the size of the entire entry as: sizeof(struct ipt_entry) + XT_ALIGN(sizeof(struct ipt_standard_target)) ipt_standard_target looks like this: struct xt_standard_target { struct xt_entry_target target; int verdict; }; xt_entry_target contains a pointer, so when compiled for 64 bit the structure gets an extra 4 byte of padding at the end. On 32 bit architectures where iptables aligns to 8 byte it will also have 4 byte padding at the end because it is only 36 bytes large. The compat_ipt_standard_fn in the kernel adjusts the offsets by sizeof(struct ipt_standard_target) - sizeof(struct compat_ipt_standard_target), which will always result in 4, even if the structure from userspace was already padded to a multiple of 8. On x86 this works out by accident because userspace only aligns to 4, on all other architectures this is broken and causes incorrect adjustments to the size and following offsets. Thanks to Linus for lots of debugging help and testing. Signed-off-by: Patrick McHardy Signed-off-by: Linus Torvalds --- include/linux/netfilter/x_tables.h | 8 ++++++++ net/ipv4/netfilter/ip_tables.c | 33 ++++++++++++++------------------- 2 files changed, 22 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 38701454e197..48cc32d83f77 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -337,6 +337,10 @@ struct compat_xt_entry_match char name[XT_FUNCTION_MAXNAMELEN - 1]; u_int8_t revision; } user; + struct { + u_int16_t match_size; + compat_uptr_t match; + } kernel; u_int16_t match_size; } u; unsigned char data[0]; @@ -350,6 +354,10 @@ struct compat_xt_entry_target char name[XT_FUNCTION_MAXNAMELEN - 1]; u_int8_t revision; } user; + struct { + u_int16_t target_size; + compat_uptr_t target; + } kernel; u_int16_t target_size; } u; unsigned char data[0]; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index d25ac8ba6eba..6d1c11563943 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -956,15 +956,16 @@ struct compat_ipt_standard_target compat_int_t verdict; }; -#define IPT_ST_OFFSET (sizeof(struct ipt_standard_target) - \ - sizeof(struct compat_ipt_standard_target)) - struct compat_ipt_standard { struct compat_ipt_entry entry; struct compat_ipt_standard_target target; }; +#define IPT_ST_LEN XT_ALIGN(sizeof(struct ipt_standard_target)) +#define IPT_ST_COMPAT_LEN COMPAT_XT_ALIGN(sizeof(struct compat_ipt_standard_target)) +#define IPT_ST_OFFSET (IPT_ST_LEN - IPT_ST_COMPAT_LEN) + static int compat_ipt_standard_fn(void *target, void **dstptr, int *size, int convert) { @@ -975,35 +976,29 @@ static int compat_ipt_standard_fn(void *target, ret = 0; switch (convert) { case COMPAT_TO_USER: - pst = (struct ipt_standard_target *)target; + pst = target; memcpy(&compat_st.target, &pst->target, - sizeof(struct ipt_entry_target)); + sizeof(compat_st.target)); compat_st.verdict = pst->verdict; if (compat_st.verdict > 0) compat_st.verdict -= compat_calc_jump(compat_st.verdict); - compat_st.target.u.user.target_size = - sizeof(struct compat_ipt_standard_target); - if (__copy_to_user(*dstptr, &compat_st, - sizeof(struct compat_ipt_standard_target))) + compat_st.target.u.user.target_size = IPT_ST_COMPAT_LEN; + if (copy_to_user(*dstptr, &compat_st, IPT_ST_COMPAT_LEN)) ret = -EFAULT; *size -= IPT_ST_OFFSET; - *dstptr += sizeof(struct compat_ipt_standard_target); + *dstptr += IPT_ST_COMPAT_LEN; break; case COMPAT_FROM_USER: - pcompat_st = - (struct compat_ipt_standard_target *)target; - memcpy(&st.target, &pcompat_st->target, - sizeof(struct ipt_entry_target)); + pcompat_st = target; + memcpy(&st.target, &pcompat_st->target, IPT_ST_COMPAT_LEN); st.verdict = pcompat_st->verdict; if (st.verdict > 0) st.verdict += compat_calc_jump(st.verdict); - st.target.u.user.target_size = - sizeof(struct ipt_standard_target); - memcpy(*dstptr, &st, - sizeof(struct ipt_standard_target)); + st.target.u.user.target_size = IPT_ST_LEN; + memcpy(*dstptr, &st, IPT_ST_LEN); *size += IPT_ST_OFFSET; - *dstptr += sizeof(struct ipt_standard_target); + *dstptr += IPT_ST_LEN; break; case COMPAT_CALC_SIZE: *size += IPT_ST_OFFSET; -- cgit v1.2.3 From a893b99be71f1d669b74f840e3a683dd077d007b Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 2 May 2006 15:03:27 +0200 Subject: [PATCH] splice: fix page LRU accounting Currently we rely on the PIPE_BUF_FLAG_LRU flag being set correctly to know whether we need to fiddle with page LRU state after stealing it, however for some origins we just don't know if the page is on the LRU list or not. So remove PIPE_BUF_FLAG_LRU and do this check/add manually in pipe_to_file() instead. Signed-off-by: Jens Axboe --- fs/splice.c | 31 +++++++++++++++++++++---------- include/linux/pipe_fs_i.h | 5 ++--- 2 files changed, 23 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/fs/splice.c b/fs/splice.c index b0c157d76948..3318b965c10b 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -78,7 +78,6 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *info, return 1; } - buf->flags |= PIPE_BUF_FLAG_LRU; return 0; } @@ -86,8 +85,6 @@ static void page_cache_pipe_buf_release(struct pipe_inode_info *info, struct pipe_buffer *buf) { page_cache_release(buf->page); - buf->page = NULL; - buf->flags &= ~PIPE_BUF_FLAG_LRU; } static int page_cache_pipe_buf_pin(struct pipe_inode_info *info, @@ -570,22 +567,36 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, if ((sd->flags & SPLICE_F_MOVE) && this_len == PAGE_CACHE_SIZE) { /* * If steal succeeds, buf->page is now pruned from the vm - * side (LRU and page cache) and we can reuse it. The page - * will also be looked on successful return. + * side (page cache) and we can reuse it. The page will also + * be locked on successful return. */ if (buf->ops->steal(info, buf)) goto find_page; page = buf->page; + page_cache_get(page); + + /* + * page must be on the LRU for adding to the pagecache. + * Check this without grabbing the zone lock, if it isn't + * the do grab the zone lock, recheck, and add if necessary. + */ + if (!PageLRU(page)) { + struct zone *zone = page_zone(page); + + spin_lock_irq(&zone->lru_lock); + if (!PageLRU(page)) { + SetPageLRU(page); + add_page_to_inactive_list(zone, page); + } + spin_unlock_irq(&zone->lru_lock); + } + if (add_to_page_cache(page, mapping, index, gfp_mask)) { + page_cache_release(page); unlock_page(page); goto find_page; } - - page_cache_get(page); - - if (!(buf->flags & PIPE_BUF_FLAG_LRU)) - lru_cache_add(page); } else { find_page: page = find_lock_page(mapping, index); diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index df4d3fa7d3dc..070954f05947 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -5,9 +5,8 @@ #define PIPE_BUFFERS (16) -#define PIPE_BUF_FLAG_LRU 0x01 /* page is on the LRU */ -#define PIPE_BUF_FLAG_ATOMIC 0x02 /* was atomically mapped */ -#define PIPE_BUF_FLAG_GIFT 0x04 /* page is a gift */ +#define PIPE_BUF_FLAG_ATOMIC 0x01 /* was atomically mapped */ +#define PIPE_BUF_FLAG_GIFT 0x02 /* page is a gift */ struct pipe_buffer { struct page *page; -- cgit v1.2.3 From 330ab71619bacc4d4494227a6cfc9b7f5500403d Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 2 May 2006 15:29:57 +0200 Subject: [PATCH] vmsplice: restrict stealing a little more Apply the same rules as the anon pipe pages, only allow stealing if no one else is using the page. Signed-off-by: Jens Axboe --- fs/pipe.c | 6 +++--- fs/splice.c | 2 +- include/linux/pipe_fs_i.h | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/fs/pipe.c b/fs/pipe.c index 3941a7f78b5d..5acd8954aaa0 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -184,8 +184,8 @@ void generic_pipe_buf_unmap(struct pipe_inode_info *pipe, kunmap(buf->page); } -static int anon_pipe_buf_steal(struct pipe_inode_info *pipe, - struct pipe_buffer *buf) +int generic_pipe_buf_steal(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) { struct page *page = buf->page; @@ -213,7 +213,7 @@ static struct pipe_buf_operations anon_pipe_buf_ops = { .unmap = generic_pipe_buf_unmap, .pin = generic_pipe_buf_pin, .release = anon_pipe_buf_release, - .steal = anon_pipe_buf_steal, + .steal = generic_pipe_buf_steal, .get = generic_pipe_buf_get, }; diff --git a/fs/splice.c b/fs/splice.c index 3318b965c10b..7fb04970c72d 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -141,7 +141,7 @@ static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe, if (!(buf->flags & PIPE_BUF_FLAG_GIFT)) return 1; - return 0; + return generic_pipe_buf_steal(pipe, buf); } static struct pipe_buf_operations user_page_pipe_buf_ops = { diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 070954f05947..ba73108cbf8b 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -69,6 +69,7 @@ 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 *); int generic_pipe_buf_pin(struct pipe_inode_info *, struct pipe_buffer *); +int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *); /* * splice is tied to pipes as a transport (at least for now), so we'll just -- cgit v1.2.3 From 37be4e7809e0581db85387e126ae4da68c3d6286 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 2 May 2006 17:24:59 +0100 Subject: [MMC] extend data timeout for writes The CSD contains a "read2write factor" which determines the multiplier to be applied to the read timeout to obtain the write timeout. We were ignoring this parameter, resulting in the possibility for writes being timed out too early. Signed-off-by: Russell King --- drivers/mmc/mmc.c | 2 ++ drivers/mmc/mmc_block.c | 6 ++++++ include/linux/mmc/card.h | 1 + 3 files changed, 9 insertions(+) (limited to 'include/linux') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index da6ddd910fc5..05aa4d6b4f24 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -549,6 +549,7 @@ static void mmc_decode_csd(struct mmc_card *card) csd->read_partial = UNSTUFF_BITS(resp, 79, 1); csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); + csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_partial = UNSTUFF_BITS(resp, 21, 1); } else { @@ -583,6 +584,7 @@ static void mmc_decode_csd(struct mmc_card *card) csd->read_partial = UNSTUFF_BITS(resp, 79, 1); csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); + csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_partial = UNSTUFF_BITS(resp, 21, 1); } diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c index 8eb2a2ede64b..06bd1f4cb9b1 100644 --- a/drivers/mmc/mmc_block.c +++ b/drivers/mmc/mmc_block.c @@ -187,6 +187,12 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) brq.cmd.opcode = MMC_WRITE_BLOCK; brq.data.flags |= MMC_DATA_WRITE; brq.data.blocks = 1; + + /* + * Scale up the timeout by the r2w factor + */ + brq.data.timeout_ns <<= card->csd.r2w_factor; + brq.data.timeout_clks <<= card->csd.r2w_factor; } if (brq.data.blocks > 1) { diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 30dd978c1ec8..991a37382a22 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -28,6 +28,7 @@ struct mmc_csd { unsigned short cmdclass; unsigned short tacc_clks; unsigned int tacc_ns; + unsigned int r2w_factor; unsigned int max_dtr; unsigned int read_blkbits; unsigned int write_blkbits; -- cgit v1.2.3 From 6e1976961c9bd9a3dc368139fab1883961efc879 Mon Sep 17 00:00:00 2001 From: Vitaly Bordug Date: Sat, 29 Apr 2006 23:06:00 +0400 Subject: [PATCH] ppc32 CPM_UART: fixes and improvements A number of small issues are fixed, and added the header file, missed from the original series. With this, driver should be pretty stable as tested among both platform-device-driven and "old way" boards. Also added missing GPL statement , and updated year field on existing ones to reflect code update. Signed-off-by: Vitaly Bordug Signed-off-by: Paul Mackerras --- arch/ppc/platforms/mpc866ads_setup.c | 2 +- drivers/serial/cpm_uart/cpm_uart.h | 19 ++++++++--- drivers/serial/cpm_uart/cpm_uart_core.c | 31 +++++++++++++---- drivers/serial/cpm_uart/cpm_uart_cpm1.c | 2 ++ drivers/serial/cpm_uart/cpm_uart_cpm2.c | 2 ++ include/linux/fs_uart_pd.h | 60 +++++++++++++++++++++++++++++++++ 6 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 include/linux/fs_uart_pd.h (limited to 'include/linux') diff --git a/arch/ppc/platforms/mpc866ads_setup.c b/arch/ppc/platforms/mpc866ads_setup.c index 6ce3b842defe..d919dab61347 100644 --- a/arch/ppc/platforms/mpc866ads_setup.c +++ b/arch/ppc/platforms/mpc866ads_setup.c @@ -378,7 +378,7 @@ int __init mpc866ads_init(void) ppc_sys_device_setfunc(MPC8xx_CPM_SMC1, PPC_SYS_FUNC_UART); #endif -#ifdef CONFIG_SERIAL_CPM_SMCer +#ifdef CONFIG_SERIAL_CPM_SMC ppc_sys_device_enable(MPC8xx_CPM_SMC2); ppc_sys_device_setfunc(MPC8xx_CPM_SMC2, PPC_SYS_FUNC_UART); #endif diff --git a/drivers/serial/cpm_uart/cpm_uart.h b/drivers/serial/cpm_uart/cpm_uart.h index aa5eb7ddeda9..3b35cb779539 100644 --- a/drivers/serial/cpm_uart/cpm_uart.h +++ b/drivers/serial/cpm_uart/cpm_uart.h @@ -5,6 +5,13 @@ * * Copyright (C) 2004 Freescale Semiconductor, Inc. * + * 2006 (c) MontaVista Software, Inc. + * Vitaly Bordug + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * */ #ifndef CPM_UART_H #define CPM_UART_H @@ -101,12 +108,13 @@ static inline unsigned long cpu2cpm_addr(void* addr, struct uart_cpm_port *pinfo int offset; u32 val = (u32)addr; /* sane check */ - if ((val >= (u32)pinfo->mem_addr) && + if (likely((val >= (u32)pinfo->mem_addr)) && (val<((u32)pinfo->mem_addr + pinfo->mem_size))) { offset = val - (u32)pinfo->mem_addr; return pinfo->dma_addr+offset; } - printk("%s(): address %x to translate out of range!\n", __FUNCTION__, val); + /* something nasty happened */ + BUG(); return 0; } @@ -115,12 +123,13 @@ static inline void *cpm2cpu_addr(unsigned long addr, struct uart_cpm_port *pinfo int offset; u32 val = addr; /* sane check */ - if ((val >= pinfo->dma_addr) && - (val<(pinfo->dma_addr + pinfo->mem_size))) { + if (likely((val >= pinfo->dma_addr) && + (val<(pinfo->dma_addr + pinfo->mem_size)))) { offset = val - (u32)pinfo->dma_addr; return (void*)(pinfo->mem_addr+offset); } - printk("%s(): address %x to translate out of range!\n", __FUNCTION__, val); + /* something nasty happened */ + BUG(); return 0; } diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c index b9d1185df20c..969f94900431 100644 --- a/drivers/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/serial/cpm_uart/cpm_uart_core.c @@ -12,7 +12,8 @@ * * Copyright (C) 2004 Freescale Semiconductor, Inc. * (C) 2004 Intracom, S.A. - * (C) 2005 MontaVista Software, Inc. by Vitaly Bordug + * (C) 2005-2006 MontaVista Software, Inc. + * Vitaly Bordug * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -81,7 +82,7 @@ early_uart_get_pdev(int index) } -void cpm_uart_count(void) +static void cpm_uart_count(void) { cpm_uart_nr = 0; #ifdef CONFIG_SERIAL_CPM_SMC1 @@ -104,6 +105,21 @@ void cpm_uart_count(void) #endif } +/* Get UART number by its id */ +static int cpm_uart_id2nr(int id) +{ + int i; + if (id < UART_NR) { + for (i=0; ifs_no; line++); + line = cpm_uart_id2nr(idx); + if(line < 0) { + printk(KERN_ERR"%s(): port %d is not registered", __FUNCTION__, idx); + return -1; + } pinfo = (struct uart_cpm_port *) &cpm_uart_ports[idx]; @@ -1245,8 +1265,7 @@ static int cpm_uart_drv_probe(struct device *dev) } pdata = pdev->dev.platform_data; - pr_debug("cpm_uart_drv_probe: Adding CPM UART %d\n", - cpm_uart_port_map[pdata->fs_no]); + pr_debug("cpm_uart_drv_probe: Adding CPM UART %d\n", cpm_uart_id2nr(pdata->fs_no)); if ((ret = cpm_uart_drv_get_platform_data(pdev, 0))) return ret; @@ -1265,7 +1284,7 @@ static int cpm_uart_drv_remove(struct device *dev) struct fs_uart_platform_info *pdata = pdev->dev.platform_data; pr_debug("cpm_uart_drv_remove: Removing CPM UART %d\n", - cpm_uart_port_map[pdata->fs_no]); + cpm_uart_id2nr(pdata->fs_no)); uart_remove_one_port(&cpm_reg, &cpm_uart_ports[pdata->fs_no].port); return 0; diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/serial/cpm_uart/cpm_uart_cpm1.c index a5a30622637a..17406a05ce1f 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm1.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm1.c @@ -8,6 +8,8 @@ * * Copyright (C) 2004 Freescale Semiconductor, Inc. * (C) 2004 Intracom, S.A. + * (C) 2006 MontaVista Software, Inc. + * Vitaly Bordug * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/serial/cpm_uart/cpm_uart_cpm2.c index 7c6b07aeea92..4b2de08f46d0 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm2.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm2.c @@ -8,6 +8,8 @@ * * Copyright (C) 2004 Freescale Semiconductor, Inc. * (C) 2004 Intracom, S.A. + * (C) 2006 MontaVista Software, Inc. + * Vitaly Bordug * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/include/linux/fs_uart_pd.h b/include/linux/fs_uart_pd.h new file mode 100644 index 000000000000..f5975126b712 --- /dev/null +++ b/include/linux/fs_uart_pd.h @@ -0,0 +1,60 @@ +/* + * Platform information definitions for the CPM Uart driver. + * + * 2006 (c) MontaVista Software, Inc. + * Vitaly Bordug + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef FS_UART_PD_H +#define FS_UART_PD_H + +#include +#include + +enum fs_uart_id { + fsid_smc1_uart, + fsid_smc2_uart, + fsid_scc1_uart, + fsid_scc2_uart, + fsid_scc3_uart, + fsid_scc4_uart, + fs_uart_nr, +}; + +static inline int fs_uart_id_scc2fsid(int id) +{ + return fsid_scc1_uart + id - 1; +} + +static inline int fs_uart_id_fsid2scc(int id) +{ + return id - fsid_scc1_uart + 1; +} + +static inline int fs_uart_id_smc2fsid(int id) +{ + return fsid_smc1_uart + id - 1; +} + +static inline int fs_uart_id_fsid2smc(int id) +{ + return id - fsid_smc1_uart + 1; +} + +struct fs_uart_platform_info { + void(*init_ioports)(void); + /* device specific information */ + int fs_no; /* controller index */ + u32 uart_clk; + u8 tx_num_fifo; + u8 tx_buf_size; + u8 rx_num_fifo; + u8 rx_buf_size; + u8 brg; +}; + +#endif -- cgit v1.2.3 From 1432873af7ae29d4bb3c56114c05b539d078ca62 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 3 May 2006 10:35:26 +0200 Subject: [PATCH] splice: LRU fixups Nick says that the current construct isn't safe. This goes back to the original, but sets PIPE_BUF_FLAG_LRU on user pages as well as they all seem to be on the LRU in the first place. Signed-off-by: Jens Axboe --- fs/splice.c | 33 +++++++++++---------------------- include/linux/pipe_fs_i.h | 5 +++-- 2 files changed, 14 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/fs/splice.c b/fs/splice.c index 27f5e3738a7b..0b202425b0b5 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -78,6 +78,7 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *info, return 1; } + buf->flags |= PIPE_BUF_FLAG_LRU; return 0; } @@ -85,6 +86,7 @@ static void page_cache_pipe_buf_release(struct pipe_inode_info *info, struct pipe_buffer *buf) { page_cache_release(buf->page); + buf->flags &= ~PIPE_BUF_FLAG_LRU; } static int page_cache_pipe_buf_pin(struct pipe_inode_info *info, @@ -141,6 +143,7 @@ static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe, if (!(buf->flags & PIPE_BUF_FLAG_GIFT)) return 1; + buf->flags |= PIPE_BUF_FLAG_LRU; return generic_pipe_buf_steal(pipe, buf); } @@ -566,37 +569,23 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, */ if ((sd->flags & SPLICE_F_MOVE) && this_len == PAGE_CACHE_SIZE) { /* - * If steal succeeds, buf->page is now pruned from the vm - * side (page cache) and we can reuse it. The page will also - * be locked on successful return. + * If steal succeeds, buf->page is now pruned from the + * pagecache and we can reuse it. The page will also be + * locked on successful return. */ if (buf->ops->steal(info, buf)) goto find_page; page = buf->page; - page_cache_get(page); - - /* - * page must be on the LRU for adding to the pagecache. - * Check this without grabbing the zone lock, if it isn't - * the do grab the zone lock, recheck, and add if necessary. - */ - if (!PageLRU(page)) { - struct zone *zone = page_zone(page); - - spin_lock_irq(&zone->lru_lock); - if (!PageLRU(page)) { - SetPageLRU(page); - add_page_to_inactive_list(zone, page); - } - spin_unlock_irq(&zone->lru_lock); - } - if (add_to_page_cache(page, mapping, index, gfp_mask)) { - page_cache_release(page); unlock_page(page); goto find_page; } + + page_cache_get(page); + + if (!(buf->flags & PIPE_BUF_FLAG_LRU)) + lru_cache_add(page); } else { find_page: page = find_lock_page(mapping, index); diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index ba73108cbf8b..ea4f7cd7bfd8 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -5,8 +5,9 @@ #define PIPE_BUFFERS (16) -#define PIPE_BUF_FLAG_ATOMIC 0x01 /* was atomically mapped */ -#define PIPE_BUF_FLAG_GIFT 0x02 /* page is a gift */ +#define PIPE_BUF_FLAG_LRU 0x01 /* page is on the LRU */ +#define PIPE_BUF_FLAG_ATOMIC 0x02 /* was atomically mapped */ +#define PIPE_BUF_FLAG_GIFT 0x04 /* page is a gift */ struct pipe_buffer { struct page *page; -- cgit v1.2.3 From 7582e9d17edbabab6cbe59467c5d1b5e8c04fca8 Mon Sep 17 00:00:00 2001 From: Jing Min Zhao Date: Wed, 3 May 2006 23:19:59 -0700 Subject: [NETFILTER]: H.323 helper: Change author's email address Signed-off-by: Jing Min Zhao Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter_ipv4/ip_conntrack_helper_h323_asn1.h | 2 +- net/ipv4/netfilter/ip_conntrack_helper_h323_asn1.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter_ipv4/ip_conntrack_helper_h323_asn1.h b/include/linux/netfilter_ipv4/ip_conntrack_helper_h323_asn1.h index 0bd828081c0c..c6e9a0b6d30b 100644 --- a/include/linux/netfilter_ipv4/ip_conntrack_helper_h323_asn1.h +++ b/include/linux/netfilter_ipv4/ip_conntrack_helper_h323_asn1.h @@ -2,7 +2,7 @@ * ip_conntrack_helper_h323_asn1.h - BER and PER decoding library for H.323 * conntrack/NAT module. * - * Copyright (c) 2006 by Jing Min Zhao + * Copyright (c) 2006 by Jing Min Zhao * * This source code is licensed under General Public License version 2. * diff --git a/net/ipv4/netfilter/ip_conntrack_helper_h323_asn1.c b/net/ipv4/netfilter/ip_conntrack_helper_h323_asn1.c index f52d2c45a6ae..355a53a5b6cd 100644 --- a/net/ipv4/netfilter/ip_conntrack_helper_h323_asn1.c +++ b/net/ipv4/netfilter/ip_conntrack_helper_h323_asn1.c @@ -2,7 +2,7 @@ * ip_conntrack_helper_h323_asn1.c - BER and PER decoding library for H.323 * conntrack/NAT module. * - * Copyright (c) 2006 by Jing Min Zhao + * Copyright (c) 2006 by Jing Min Zhao * * This source code is licensed under General Public License version 2. * -- cgit v1.2.3 From 1498221d51a43d5fa1a580618591497d90f957d9 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Sat, 6 May 2006 17:55:11 -0700 Subject: [CLASS DEVICE]: add attribute_group creation Extend the support of attribute groups in class_device's to allow groups to be created as part of the registration process. This allows network device's to avoid race between registration and creating groups. Note that unlike attributes that are a property of the class object, the groups are a property of the class_device object. This is done because there are different types of network devices (wireless for example). Signed-off-by: Stephen Hemminger Signed-off-by: Greg Kroah-Hartman Signed-off-by: David S. Miller --- drivers/base/class.c | 32 ++++++++++++++++++++++++++++++++ include/linux/device.h | 2 ++ 2 files changed, 34 insertions(+) (limited to 'include/linux') diff --git a/drivers/base/class.c b/drivers/base/class.c index 0e71dff327cd..b1ea4df85c7d 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -456,6 +456,35 @@ static void class_device_remove_attrs(struct class_device * cd) } } +static int class_device_add_groups(struct class_device * cd) +{ + int i; + int error = 0; + + if (cd->groups) { + for (i = 0; cd->groups[i]; i++) { + error = sysfs_create_group(&cd->kobj, cd->groups[i]); + if (error) { + while (--i >= 0) + sysfs_remove_group(&cd->kobj, cd->groups[i]); + goto out; + } + } + } +out: + return error; +} + +static void class_device_remove_groups(struct class_device * cd) +{ + int i; + if (cd->groups) { + for (i = 0; cd->groups[i]; i++) { + sysfs_remove_group(&cd->kobj, cd->groups[i]); + } + } +} + static ssize_t show_dev(struct class_device *class_dev, char *buf) { return print_dev_t(buf, class_dev->devt); @@ -559,6 +588,8 @@ int class_device_add(struct class_device *class_dev) class_name); } + class_device_add_groups(class_dev); + kobject_uevent(&class_dev->kobj, KOBJ_ADD); /* notify any interfaces this device is now here */ @@ -672,6 +703,7 @@ void class_device_del(struct class_device *class_dev) if (class_dev->devt_attr) class_device_remove_file(class_dev, class_dev->devt_attr); class_device_remove_attrs(class_dev); + class_device_remove_groups(class_dev); kobject_uevent(&class_dev->kobj, KOBJ_REMOVE); kobject_del(&class_dev->kobj); diff --git a/include/linux/device.h b/include/linux/device.h index f6e72a65a3f2..e8e53b9accc6 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -200,6 +200,7 @@ extern int class_device_create_file(struct class_device *, * @node: for internal use by the driver core only. * @kobj: for internal use by the driver core only. * @devt_attr: for internal use by the driver core only. + * @groups: optional additional groups to be created * @dev: if set, a symlink to the struct device is created in the sysfs * directory for this struct class device. * @class_data: pointer to whatever you want to store here for this struct @@ -228,6 +229,7 @@ struct class_device { struct device * dev; /* not necessary, but nice to have */ void * class_data; /* class-specific data */ struct class_device *parent; /* parent of this child device, if there is one */ + struct attribute_group ** groups; /* optional groups */ void (*release)(struct class_device *dev); int (*uevent)(struct class_device *dev, char **envp, -- cgit v1.2.3 From fe9925b551a95fae6ec61470c79f8b701a2fe928 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Sat, 6 May 2006 17:56:03 -0700 Subject: [NET]: Create netdev attribute_groups with class_device_add Atomically create attributes when class device is added. This avoids the race between registering class_device (which generates hotplug event), and the creation of attribute groups. Signed-off-by: Stephen Hemminger Signed-off-by: Greg Kroah-Hartman Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 ++ net/core/dev.c | 2 +- net/core/net-sysfs.c | 49 ++++++++++++----------------------------------- 3 files changed, 15 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 01db7b88a2b1..309f9190a922 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -506,6 +506,8 @@ struct net_device /* class/net/name entry */ struct class_device class_dev; + /* space for optional statistics and wireless sysfs groups */ + struct attribute_group *sysfs_groups[3]; }; #define NETDEV_ALIGN 32 diff --git a/net/core/dev.c b/net/core/dev.c index 3bad1afc89fa..9ab3cfa58466 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3043,11 +3043,11 @@ void netdev_run_todo(void) switch(dev->reg_state) { case NETREG_REGISTERING: - dev->reg_state = NETREG_REGISTERED; err = netdev_register_sysfs(dev); if (err) printk(KERN_ERR "%s: failed sysfs registration (%d)\n", dev->name, err); + dev->reg_state = NETREG_REGISTERED; break; case NETREG_UNREGISTERING: diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index c12990c9c603..47a6fceb6771 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -29,7 +29,7 @@ static const char fmt_ulong[] = "%lu\n"; static inline int dev_isalive(const struct net_device *dev) { - return dev->reg_state == NETREG_REGISTERED; + return dev->reg_state <= NETREG_REGISTERED; } /* use same locking rules as GIF* ioctl's */ @@ -445,58 +445,33 @@ static struct class net_class = { void netdev_unregister_sysfs(struct net_device * net) { - struct class_device * class_dev = &(net->class_dev); - - if (net->get_stats) - sysfs_remove_group(&class_dev->kobj, &netstat_group); - -#ifdef WIRELESS_EXT - if (net->get_wireless_stats || (net->wireless_handlers && - net->wireless_handlers->get_wireless_stats)) - sysfs_remove_group(&class_dev->kobj, &wireless_group); -#endif - class_device_del(class_dev); - + class_device_del(&(net->class_dev)); } /* Create sysfs entries for network device. */ int netdev_register_sysfs(struct net_device *net) { struct class_device *class_dev = &(net->class_dev); - int ret; + struct attribute_group **groups = net->sysfs_groups; + class_device_initialize(class_dev); class_dev->class = &net_class; class_dev->class_data = net; + class_dev->groups = groups; + BUILD_BUG_ON(BUS_ID_SIZE < IFNAMSIZ); strlcpy(class_dev->class_id, net->name, BUS_ID_SIZE); - if ((ret = class_device_register(class_dev))) - goto out; - if (net->get_stats && - (ret = sysfs_create_group(&class_dev->kobj, &netstat_group))) - goto out_unreg; + if (net->get_stats) + *groups++ = &netstat_group; #ifdef WIRELESS_EXT - if (net->get_wireless_stats || (net->wireless_handlers && - net->wireless_handlers->get_wireless_stats)) { - ret = sysfs_create_group(&class_dev->kobj, &wireless_group); - if (ret) - goto out_cleanup; - } - return 0; -out_cleanup: - if (net->get_stats) - sysfs_remove_group(&class_dev->kobj, &netstat_group); -#else - return 0; + if (net->get_wireless_stats + || (net->wireless_handlers && net->wireless_handlers->get_wireless_stats)) + *groups++ = &wireless_group; #endif -out_unreg: - printk(KERN_WARNING "%s: sysfs attribute registration failed %d\n", - net->name, ret); - class_device_unregister(class_dev); -out: - return ret; + return class_device_add(class_dev); } int netdev_sysfs_init(void) -- cgit v1.2.3 From d324031245abbb54e4e0321004430826052b6c37 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 8 May 2006 15:11:26 -0700 Subject: sky2: backout NAPI reschedule This is a backout of earlier patch. The whole rescheduling hack was a bad idea. It doesn't really solve the problem and it makes the code more complicated for no good reason. Signed-off-by: Stephen Hemminger --- drivers/net/sky2.c | 20 ++++---------------- include/linux/netdevice.h | 18 ++++++++---------- 2 files changed, 12 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 227df9876a2c..76da74fbe859 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2105,7 +2105,6 @@ static int sky2_poll(struct net_device *dev0, int *budget) int work_done = 0; u32 status = sky2_read32(hw, B0_Y2_SP_EISR); - restart_poll: if (unlikely(status & ~Y2_IS_STAT_BMU)) { if (status & Y2_IS_HW_ERR) sky2_hw_intr(hw); @@ -2136,7 +2135,7 @@ static int sky2_poll(struct net_device *dev0, int *budget) } if (status & Y2_IS_STAT_BMU) { - work_done += sky2_status_intr(hw, work_limit - work_done); + work_done = sky2_status_intr(hw, work_limit); *budget -= work_done; dev0->quota -= work_done; @@ -2148,22 +2147,9 @@ static int sky2_poll(struct net_device *dev0, int *budget) mod_timer(&hw->idle_timer, jiffies + HZ); - local_irq_disable(); - __netif_rx_complete(dev0); + netif_rx_complete(dev0); status = sky2_read32(hw, B0_Y2_SP_LISR); - - if (unlikely(status)) { - /* More work pending, try and keep going */ - if (__netif_rx_schedule_prep(dev0)) { - __netif_rx_reschedule(dev0, work_done); - status = sky2_read32(hw, B0_Y2_SP_EISR); - local_irq_enable(); - goto restart_poll; - } - } - - local_irq_enable(); return 0; } @@ -2181,6 +2167,8 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) prefetch(&hw->st_le[hw->st_idx]); if (likely(__netif_rx_schedule_prep(dev0))) __netif_rx_schedule(dev0); + else + printk(KERN_DEBUG PFX "irq race detected\n"); return IRQ_HANDLED; } diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 309f9190a922..a461b51d6076 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -831,21 +831,19 @@ static inline void netif_rx_schedule(struct net_device *dev) __netif_rx_schedule(dev); } - -static inline void __netif_rx_reschedule(struct net_device *dev, int undo) -{ - dev->quota += undo; - list_add_tail(&dev->poll_list, &__get_cpu_var(softnet_data).poll_list); - __raise_softirq_irqoff(NET_RX_SOFTIRQ); -} - -/* Try to reschedule poll. Called by dev->poll() after netif_rx_complete(). */ +/* Try to reschedule poll. Called by dev->poll() after netif_rx_complete(). + * Do not inline this? + */ static inline int netif_rx_reschedule(struct net_device *dev, int undo) { if (netif_rx_schedule_prep(dev)) { unsigned long flags; + + dev->quota += undo; + local_irq_save(flags); - __netif_rx_reschedule(dev, undo); + list_add_tail(&dev->poll_list, &__get_cpu_var(softnet_data).poll_list); + __raise_softirq_irqoff(NET_RX_SOFTIRQ); local_irq_restore(flags); return 1; } -- cgit v1.2.3 From b17a7c179dd3ce7d04373fddf660eda21efc9db9 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 10 May 2006 13:21:17 -0700 Subject: [NET]: Do sysfs registration as part of register_netdevice. The last step of netdevice registration was being done by a delayed call, but because it was delayed, it was impossible to return any error code if the class_device registration failed. Side effects: * one state in registration process is unnecessary. * register_netdevice can sleep inside class_device registration/hotplug * code in netdev_run_todo only does unregistration so it is simpler. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 +-- net/core/dev.c | 63 ++++++++++++++++++++--------------------------- 2 files changed, 28 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a461b51d6076..f4169bbb60eb 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -433,8 +433,7 @@ struct net_device /* register/unregister state machine */ enum { NETREG_UNINITIALIZED=0, - NETREG_REGISTERING, /* called register_netdevice */ - NETREG_REGISTERED, /* completed register todo */ + NETREG_REGISTERED, /* completed register_netdevice */ NETREG_UNREGISTERING, /* called unregister_netdevice */ NETREG_UNREGISTERED, /* completed unregister todo */ NETREG_RELEASED, /* called free_netdev */ diff --git a/net/core/dev.c b/net/core/dev.c index ced57430f6d8..2dce673a039b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2777,6 +2777,8 @@ int register_netdevice(struct net_device *dev) BUG_ON(dev_boot_phase); ASSERT_RTNL(); + might_sleep(); + /* When net_device's are persistent, this will be fatal. */ BUG_ON(dev->reg_state != NETREG_UNINITIALIZED); @@ -2863,6 +2865,11 @@ int register_netdevice(struct net_device *dev) if (!dev->rebuild_header) dev->rebuild_header = default_rebuild_header; + ret = netdev_register_sysfs(dev); + if (ret) + goto out_err; + dev->reg_state = NETREG_REGISTERED; + /* * Default initial state at registry is that the * device is present. @@ -2878,14 +2885,11 @@ int register_netdevice(struct net_device *dev) hlist_add_head(&dev->name_hlist, head); hlist_add_head(&dev->index_hlist, dev_index_hash(dev->ifindex)); dev_hold(dev); - dev->reg_state = NETREG_REGISTERING; write_unlock_bh(&dev_base_lock); /* Notify protocols, that a new device appeared. */ raw_notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev); - /* Finish registration after unlock */ - net_set_todo(dev); ret = 0; out: @@ -3008,7 +3012,7 @@ static void netdev_wait_allrefs(struct net_device *dev) * * We are invoked by rtnl_unlock() after it drops the semaphore. * This allows us to deal with problems: - * 1) We can create/delete sysfs objects which invoke hotplug + * 1) We can delete sysfs objects which invoke hotplug * without deadlocking with linkwatch via keventd. * 2) Since we run with the RTNL semaphore not held, we can sleep * safely in order to wait for the netdev refcnt to drop to zero. @@ -3017,8 +3021,6 @@ static DEFINE_MUTEX(net_todo_run_mutex); void netdev_run_todo(void) { struct list_head list = LIST_HEAD_INIT(list); - int err; - /* Need to guard against multiple cpu's getting out of order. */ mutex_lock(&net_todo_run_mutex); @@ -3041,40 +3043,29 @@ void netdev_run_todo(void) = list_entry(list.next, struct net_device, todo_list); list_del(&dev->todo_list); - switch(dev->reg_state) { - case NETREG_REGISTERING: - err = netdev_register_sysfs(dev); - if (err) - printk(KERN_ERR "%s: failed sysfs registration (%d)\n", - dev->name, err); - dev->reg_state = NETREG_REGISTERED; - break; - - case NETREG_UNREGISTERING: - netdev_unregister_sysfs(dev); - dev->reg_state = NETREG_UNREGISTERED; - - netdev_wait_allrefs(dev); + if (unlikely(dev->reg_state != NETREG_UNREGISTERING)) { + printk(KERN_ERR "network todo '%s' but state %d\n", + dev->name, dev->reg_state); + dump_stack(); + continue; + } - /* paranoia */ - BUG_ON(atomic_read(&dev->refcnt)); - BUG_TRAP(!dev->ip_ptr); - BUG_TRAP(!dev->ip6_ptr); - BUG_TRAP(!dev->dn_ptr); + netdev_unregister_sysfs(dev); + dev->reg_state = NETREG_UNREGISTERED; + netdev_wait_allrefs(dev); - /* It must be the very last action, - * after this 'dev' may point to freed up memory. - */ - if (dev->destructor) - dev->destructor(dev); - break; + /* paranoia */ + BUG_ON(atomic_read(&dev->refcnt)); + BUG_TRAP(!dev->ip_ptr); + BUG_TRAP(!dev->ip6_ptr); + BUG_TRAP(!dev->dn_ptr); - default: - printk(KERN_ERR "network todo '%s' but state %d\n", - dev->name, dev->reg_state); - break; - } + /* It must be the very last action, + * after this 'dev' may point to freed up memory. + */ + if (dev->destructor) + dev->destructor(dev); } out: -- cgit v1.2.3 From 4c1b46226ce4424a93b8ac544e37afb26c8a72c6 Mon Sep 17 00:00:00 2001 From: Francois Romieu Date: Wed, 10 May 2006 12:48:57 -0700 Subject: dl2k: use DMA_48BIT_MASK constant Typo will be harder with this one. Signed-off-by: Francois Romieu Signed-off-by: Stephen Hemminger --- drivers/net/dl2k.c | 12 ++++++------ include/linux/dma-mapping.h | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/dl2k.c b/drivers/net/dl2k.c index 1f3627470c95..1ddefd281213 100644 --- a/drivers/net/dl2k.c +++ b/drivers/net/dl2k.c @@ -765,7 +765,7 @@ rio_free_tx (struct net_device *dev, int irq) break; skb = np->tx_skbuff[entry]; pci_unmap_single (np->pdev, - np->tx_ring[entry].fraginfo & 0xffffffffffff, + np->tx_ring[entry].fraginfo & DMA_48BIT_MASK, skb->len, PCI_DMA_TODEVICE); if (irq) dev_kfree_skb_irq (skb); @@ -893,7 +893,7 @@ receive_packet (struct net_device *dev) /* Small skbuffs for short packets */ if (pkt_len > copy_thresh) { pci_unmap_single (np->pdev, - desc->fraginfo & 0xffffffffffff, + desc->fraginfo & DMA_48BIT_MASK, np->rx_buf_sz, PCI_DMA_FROMDEVICE); skb_put (skb = np->rx_skbuff[entry], pkt_len); @@ -901,7 +901,7 @@ receive_packet (struct net_device *dev) } else if ((skb = dev_alloc_skb (pkt_len + 2)) != NULL) { pci_dma_sync_single_for_cpu(np->pdev, desc->fraginfo & - 0xffffffffffff, + DMA_48BIT_MASK, np->rx_buf_sz, PCI_DMA_FROMDEVICE); skb->dev = dev; @@ -913,7 +913,7 @@ receive_packet (struct net_device *dev) skb_put (skb, pkt_len); pci_dma_sync_single_for_device(np->pdev, desc->fraginfo & - 0xffffffffffff, + DMA_48BIT_MASK, np->rx_buf_sz, PCI_DMA_FROMDEVICE); } @@ -1800,7 +1800,7 @@ rio_close (struct net_device *dev) skb = np->rx_skbuff[i]; if (skb) { pci_unmap_single(np->pdev, - np->rx_ring[i].fraginfo & 0xffffffffffff, + np->rx_ring[i].fraginfo & DMA_48BIT_MASK, skb->len, PCI_DMA_FROMDEVICE); dev_kfree_skb (skb); np->rx_skbuff[i] = NULL; @@ -1810,7 +1810,7 @@ rio_close (struct net_device *dev) skb = np->tx_skbuff[i]; if (skb) { pci_unmap_single(np->pdev, - np->tx_ring[i].fraginfo & 0xffffffffffff, + np->tx_ring[i].fraginfo & DMA_48BIT_MASK, skb->len, PCI_DMA_TODEVICE); dev_kfree_skb (skb); np->tx_skbuff[i] = NULL; diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index ff61817082fa..635690cf3e3d 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -14,6 +14,7 @@ enum dma_data_direction { }; #define DMA_64BIT_MASK 0xffffffffffffffffULL +#define DMA_48BIT_MASK 0x0000ffffffffffffULL #define DMA_40BIT_MASK 0x000000ffffffffffULL #define DMA_39BIT_MASK 0x0000007fffffffffULL #define DMA_32BIT_MASK 0x00000000ffffffffULL -- cgit v1.2.3 From cd41e28e2d0f198ad56840bf8ba13cb41b129bab Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Apr 2006 15:43:41 -0300 Subject: V4L/DVB (3774): Create V4L1 config options V4L1 API is depreciated and should be removed soon from kernel. This patch adds two new options, one to disable V4L1 drivers, and another to disable V4L1 compat module. This way, it would be easy to check what still depends on V4L1 stuff, allowing also to test if app works fine with V4L2 only support. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/Kconfig | 45 ++++++++++++++++--- drivers/media/common/Kconfig | 1 + drivers/media/dvb/ttpci/Kconfig | 12 +++--- drivers/media/radio/Kconfig | 30 ++++++------- drivers/media/video/Kconfig | 67 +++++++++++++++-------------- drivers/media/video/Makefile | 3 +- drivers/media/video/bt8xx/Kconfig | 2 +- drivers/media/video/cx88/cx88-video.c | 2 + drivers/media/video/em28xx/Kconfig | 2 +- drivers/media/video/et61x251/Kconfig | 2 +- drivers/media/video/pwc/Kconfig | 2 +- drivers/media/video/saa7134/saa7134-video.c | 2 + drivers/media/video/sn9c102/Kconfig | 2 +- drivers/media/video/usbvideo/Kconfig | 6 +-- drivers/media/video/vivi.c | 4 ++ drivers/media/video/zc0301/Kconfig | 2 +- include/linux/videodev2.h | 5 +++ 17 files changed, 118 insertions(+), 71 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index fffc711c260c..344d83aae3ec 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -8,22 +8,54 @@ config VIDEO_DEV tristate "Video For Linux" ---help--- Support for audio/video capture and overlay devices and FM radio - cards. The exact capabilities of each device vary. User tools for - this are available from - . + cards. The exact capabilities of each device vary. This kernel includes support for the new Video for Linux Two API, (V4L2) as well as the original system. Drivers and applications need to be rewritten to use V4L2, but drivers for popular cards and applications for most video capture functions already exist. - Documentation for the original API is included in the file - . Documentation for V4L2 is - available on the web at . + Additional info and docs are available on the web at + + + Documentation for V4L2 is also available on the web at + . To compile this driver as a module, choose M here: the module will be called videodev. +config VIDEO_V4L1 + boolean "Enable Video For Linux API 1 (DEPRECATED)" + depends on VIDEO_DEV + select VIDEO_V4L1_COMPAT + default y + ---help--- + Enables a compatibility API used by most V4L2 devices to allow + its usage with legacy applications that supports only V4L1 api. + + If you are unsure as to whether this is required, answer Y. + +config VIDEO_V4L1_COMPAT + boolean "Enable Video For Linux API 1 compatible Layer" + depends on VIDEO_DEV + default y + ---help--- + This api were developed to be used at Kernel 2.2 and 2.4, but + lacks support for several video standards. There are several + drivers at kernel that still depends on it. + + Documentation for the original API is included in the file + . + + User tools for this are available from + . + + If you are unsure as to whether this is required, answer Y. + +config VIDEO_V4L2 + tristate + default y + source "drivers/media/video/Kconfig" source "drivers/media/radio/Kconfig" @@ -65,4 +97,3 @@ config USB_DABUSB module will be called dabusb. endmenu - diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index 6a901a0268e1..9c45b983e0de 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -4,6 +4,7 @@ config VIDEO_SAA7146 config VIDEO_SAA7146_VV tristate + select VIDEO_V4L2 select VIDEO_BUF select VIDEO_VIDEOBUF select VIDEO_SAA7146 diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig index 5b2aadb8385c..c26e23291511 100644 --- a/drivers/media/dvb/ttpci/Kconfig +++ b/drivers/media/dvb/ttpci/Kconfig @@ -1,8 +1,7 @@ config DVB_AV7110 tristate "AV7110 cards" - depends on DVB_CORE && PCI + depends on DVB_CORE && PCI && VIDEO_V4L1 select FW_LOADER - select VIDEO_DEV select VIDEO_SAA7146_VV select DVB_VES1820 select DVB_VES1X93 @@ -59,7 +58,7 @@ config DVB_AV7110_OSD config DVB_BUDGET tristate "Budget cards" - depends on DVB_CORE && PCI + depends on DVB_CORE && PCI && VIDEO_V4L1 select VIDEO_SAA7146 select DVB_STV0299 select DVB_VES1X93 @@ -80,7 +79,7 @@ config DVB_BUDGET config DVB_BUDGET_CI tristate "Budget cards with onboard CI connector" - depends on DVB_CORE && PCI + depends on DVB_CORE && PCI && VIDEO_V4L1 select VIDEO_SAA7146 select DVB_STV0297 select DVB_STV0299 @@ -100,8 +99,7 @@ config DVB_BUDGET_CI config DVB_BUDGET_AV tristate "Budget cards with analog video inputs" - depends on DVB_CORE && PCI - select VIDEO_DEV + depends on DVB_CORE && PCI && VIDEO_V4L1 select VIDEO_SAA7146_VV select DVB_STV0299 select DVB_TDA1004X @@ -119,7 +117,7 @@ config DVB_BUDGET_AV config DVB_BUDGET_PATCH tristate "AV7110 cards with Budget Patch" - depends on DVB_CORE && DVB_BUDGET + depends on DVB_CORE && DVB_BUDGET && VIDEO_V4L1 select DVB_AV7110 select DVB_STV0299 select DVB_VES1X93 diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index d318be383de6..3fff75763693 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -7,7 +7,7 @@ menu "Radio Adapters" config RADIO_CADET tristate "ADS Cadet AM/FM Tuner" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these AM/FM radio cards, and then fill in the port address below. @@ -25,7 +25,7 @@ config RADIO_CADET config RADIO_RTRACK tristate "AIMSlab RadioTrack (aka RadioReveal) support" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address below. @@ -59,7 +59,7 @@ config RADIO_RTRACK_PORT config RADIO_RTRACK2 tristate "AIMSlab RadioTrack II support" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have this FM radio card, and then fill in the port address below. @@ -82,7 +82,7 @@ config RADIO_RTRACK2_PORT config RADIO_AZTECH tristate "Aztech/Packard Bell Radio" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address below. @@ -106,7 +106,7 @@ config RADIO_AZTECH_PORT config RADIO_GEMTEK tristate "GemTek Radio Card support" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have this FM radio card, and then fill in the port address below. @@ -131,7 +131,7 @@ config RADIO_GEMTEK_PORT config RADIO_GEMTEK_PCI tristate "GemTek PCI Radio Card support" - depends on VIDEO_DEV && PCI + depends on VIDEO_V4L1 && PCI ---help--- Choose Y here if you have this PCI FM radio card. @@ -145,7 +145,7 @@ config RADIO_GEMTEK_PCI config RADIO_MAXIRADIO tristate "Guillemot MAXI Radio FM 2000 radio" - depends on VIDEO_DEV && PCI + depends on VIDEO_V4L1 && PCI ---help--- Choose Y here if you have this radio card. This card may also be found as Gemtek PCI FM. @@ -160,7 +160,7 @@ config RADIO_MAXIRADIO config RADIO_MAESTRO tristate "Maestro on board radio" - depends on VIDEO_DEV + depends on VIDEO_V4L1 ---help--- Say Y here to directly support the on-board radio tuner on the Maestro 2 or 2E sound card. @@ -175,7 +175,7 @@ config RADIO_MAESTRO config RADIO_MIROPCM20 tristate "miroSOUND PCM20 radio" - depends on ISA && VIDEO_DEV && SOUND_ACI_MIXER + depends on ISA && VIDEO_V4L1 && SOUND_ACI_MIXER ---help--- Choose Y here if you have this FM radio card. You also need to say Y to "ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20 radio)" (in "Sound") @@ -208,7 +208,7 @@ config RADIO_MIROPCM20_RDS config RADIO_SF16FMI tristate "SF16FMI Radio" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these FM radio cards. If you compile the driver into the kernel and your card is not PnP one, you @@ -225,7 +225,7 @@ config RADIO_SF16FMI config RADIO_SF16FMR2 tristate "SF16FMR2 Radio" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these FM radio cards. @@ -239,7 +239,7 @@ config RADIO_SF16FMR2 config RADIO_TERRATEC tristate "TerraTec ActiveRadio ISA Standalone" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have this FM radio card, and then fill in the port address below. (TODO) @@ -268,7 +268,7 @@ config RADIO_TERRATEC_PORT config RADIO_TRUST tristate "Trust FM radio card" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 help This is a driver for the Trust FM radio cards. Say Y if you have such a card and want to use it under Linux. @@ -286,7 +286,7 @@ config RADIO_TRUST_PORT config RADIO_TYPHOON tristate "Typhoon Radio (a.k.a. EcoRadio)" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address and the frequency used for muting below. @@ -330,7 +330,7 @@ config RADIO_TYPHOON_MUTEFREQ config RADIO_ZOLTRIX tristate "Zoltrix Radio" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address below. diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 85888a8a93c9..bff9d8f51dab 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -2,10 +2,10 @@ # Multimedia Video device configuration # -menu "Video For Linux" +menu "Video Capture Adapters" depends on VIDEO_DEV -comment "Video Adapters" +comment "Video Capture Adapters" config VIDEO_ADV_DEBUG bool "Enable advanced debug functionality" @@ -20,7 +20,7 @@ source "drivers/media/video/bt8xx/Kconfig" config VIDEO_SAA6588 tristate "SAA6588 Radio Chip RDS decoder support on BT848 cards" - depends on VIDEO_DEV && I2C && VIDEO_BT848 + depends on I2C && VIDEO_BT848 help Support for Radio Data System (RDS) decoder. This allows seeing @@ -32,7 +32,7 @@ config VIDEO_SAA6588 config VIDEO_PMS tristate "Mediavision Pro Movie Studio Video For Linux" - depends on VIDEO_DEV && ISA + depends on ISA && VIDEO_V4L1 help Say Y if you have such a thing. @@ -41,7 +41,7 @@ config VIDEO_PMS config VIDEO_PLANB tristate "PlanB Video-In on PowerMac" - depends on PPC_PMAC && VIDEO_DEV && BROKEN + depends on PPC_PMAC && VIDEO_V4L1 && BROKEN help PlanB is the V4L driver for the PowerMac 7x00/8x00 series video input hardware. If you want to experiment with this, say Y. @@ -52,7 +52,7 @@ config VIDEO_PLANB config VIDEO_BWQCAM tristate "Quickcam BW Video For Linux" - depends on VIDEO_DEV && PARPORT + depends on PARPORT && VIDEO_V4L1 help Say Y have if you the black and white version of the QuickCam camera. See the next option for the color version. @@ -62,7 +62,7 @@ config VIDEO_BWQCAM config VIDEO_CQCAM tristate "QuickCam Colour Video For Linux (EXPERIMENTAL)" - depends on EXPERIMENTAL && VIDEO_DEV && PARPORT + depends on EXPERIMENTAL && PARPORT && VIDEO_V4L1 help This is the video4linux driver for the colour version of the Connectix QuickCam. If you have one of these cameras, say Y here, @@ -73,7 +73,7 @@ config VIDEO_CQCAM config VIDEO_W9966 tristate "W9966CF Webcam (FlyCam Supra and others) Video For Linux" - depends on PARPORT_1284 && VIDEO_DEV && PARPORT + depends on PARPORT_1284 && PARPORT && VIDEO_V4L1 help Video4linux driver for Winbond's w9966 based Webcams. Currently tested with the LifeView FlyCam Supra. @@ -86,7 +86,7 @@ config VIDEO_W9966 config VIDEO_CPIA tristate "CPiA Video For Linux" - depends on VIDEO_DEV + depends on VIDEO_V4L1 ---help--- This is the video4linux driver for cameras based on Vision's CPiA (Colour Processor Interface ASIC), such as the Creative Labs Video @@ -123,7 +123,7 @@ source "drivers/media/video/cpia2/Kconfig" config VIDEO_SAA5246A tristate "SAA5246A, SAA5281 Teletext processor" - depends on VIDEO_DEV && I2C + depends on I2C && VIDEO_V4L1 help Support for I2C bus based teletext using the SAA5246A or SAA5281 chip. Useful only if you live in Europe. @@ -150,7 +150,7 @@ config TUNER_3036 config VIDEO_VINO tristate "SGI Vino Video For Linux (EXPERIMENTAL)" - depends on VIDEO_DEV && I2C && SGI_IP22 && EXPERIMENTAL + depends on I2C && SGI_IP22 && EXPERIMENTAL && VIDEO_V4L1 select I2C_ALGO_SGI help Say Y here to build in support for the Vino video input system found @@ -158,7 +158,7 @@ config VIDEO_VINO config VIDEO_STRADIS tristate "Stradis 4:2:2 MPEG-2 video driver (EXPERIMENTAL)" - depends on EXPERIMENTAL && VIDEO_DEV && PCI + depends on EXPERIMENTAL && PCI && VIDEO_V4L1 help Say Y here to enable support for the Stradis 4:2:2 MPEG-2 video driver for PCI. There is a product page at @@ -166,7 +166,7 @@ config VIDEO_STRADIS config VIDEO_ZORAN tristate "Zoran ZR36057/36067 Video For Linux" - depends on VIDEO_DEV && PCI && I2C_ALGOBIT + depends on PCI && I2C_ALGOBIT && VIDEO_V4L1 help Say Y for support for MJPEG capture cards based on the Zoran 36057/36067 PCI controller chipset. This includes the Iomega @@ -214,7 +214,7 @@ config VIDEO_ZORAN_LML33R10 config VIDEO_ZR36120 tristate "Zoran ZR36120/36125 Video For Linux" - depends on VIDEO_DEV && PCI && I2C && BROKEN + depends on PCI && I2C && VIDEO_V4L1 && BROKEN help Support for ZR36120/ZR36125 based frame grabber/overlay boards. This includes the Victor II, WaveWatcher, Video Wonder, Maxi-TV, @@ -226,7 +226,7 @@ config VIDEO_ZR36120 config VIDEO_MEYE tristate "Sony Vaio Picturebook Motion Eye Video For Linux" - depends on VIDEO_DEV && PCI && SONYPI + depends on PCI && SONYPI && VIDEO_V4L1 ---help--- This is the video4linux driver for the Motion Eye camera found in the Vaio Picturebook laptops. Please read the material in @@ -242,7 +242,7 @@ source "drivers/media/video/saa7134/Kconfig" config VIDEO_MXB tristate "Siemens-Nixdorf 'Multimedia eXtension Board'" - depends on VIDEO_DEV && PCI + depends on PCI && VIDEO_V4L1 select VIDEO_SAA7146_VV select VIDEO_TUNER ---help--- @@ -254,8 +254,9 @@ config VIDEO_MXB config VIDEO_DPC tristate "Philips-Semiconductors 'dpc7146 demonstration board'" - depends on VIDEO_DEV && PCI + depends on PCI && VIDEO_V4L1 select VIDEO_SAA7146_VV + select VIDEO_V4L2 ---help--- This is a video4linux driver for the 'dpc7146 demonstration board' by Philips-Semiconductors. It's the reference design @@ -268,8 +269,9 @@ config VIDEO_DPC config VIDEO_HEXIUM_ORION tristate "Hexium HV-PCI6 and Orion frame grabber" - depends on VIDEO_DEV && PCI + depends on PCI && VIDEO_V4L1 select VIDEO_SAA7146_VV + select VIDEO_V4L2 ---help--- This is a video4linux driver for the Hexium HV-PCI6 and Orion frame grabber cards by Hexium. @@ -279,8 +281,9 @@ config VIDEO_HEXIUM_ORION config VIDEO_HEXIUM_GEMINI tristate "Hexium Gemini frame grabber" - depends on VIDEO_DEV && PCI + depends on PCI && VIDEO_V4L1 select VIDEO_SAA7146_VV + select VIDEO_V4L2 ---help--- This is a video4linux driver for the Hexium Gemini frame grabber card by Hexium. Please note that the Gemini Dual @@ -293,7 +296,7 @@ source "drivers/media/video/cx88/Kconfig" config VIDEO_OVCAMCHIP tristate "OmniVision Camera Chip support" - depends on VIDEO_DEV && I2C + depends on I2C && VIDEO_V4L1 ---help--- Support for the OmniVision OV6xxx and OV7xxx series of camera chips. This driver is intended to be used with the ov511 and w9968cf USB @@ -304,7 +307,7 @@ config VIDEO_OVCAMCHIP config VIDEO_M32R_AR tristate "AR devices" - depends on M32R + depends on M32R && VIDEO_V4L1 ---help--- This is a video4linux driver for the Renesas AR (Artificial Retina) camera module. @@ -365,17 +368,17 @@ config VIDEO_WM8739 source "drivers/media/video/cx25840/Kconfig" config VIDEO_SAA711X - tristate "Philips SAA7113/4/5 video decoders" - depends on VIDEO_DEV && I2C && EXPERIMENTAL + tristate "Philips SAA7113/4/5 video decoders (OBSOLETED)" + depends on VIDEO_V4L1 && I2C && EXPERIMENTAL ---help--- - Support for the Philips SAA7113/4/5 video decoders. + Old support for the Philips SAA7113/4 video decoders. To compile this driver as a module, choose M here: the module will be called saa7115. config VIDEO_SAA7127 tristate "Philips SAA7127/9 digital video encoders" - depends on VIDEO_DEV && I2C && EXPERIMENTAL + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL ---help--- Support for the Philips SAA7127/9 digital video encoders. @@ -384,7 +387,7 @@ config VIDEO_SAA7127 config VIDEO_UPD64031A tristate "NEC Electronics uPD64031A Ghost Reduction" - depends on VIDEO_DEV && I2C && EXPERIMENTAL + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL ---help--- Support for the NEC Electronics uPD64031A Ghost Reduction video chip. It is most often found in NTSC TV cards made for @@ -396,7 +399,7 @@ config VIDEO_UPD64031A config VIDEO_UPD64083 tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation" - depends on VIDEO_DEV && I2C && EXPERIMENTAL + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL ---help--- Support for the NEC Electronics uPD64083 3-Dimensional Y/C separation video chip. It is used to improve the quality of @@ -418,7 +421,7 @@ source "drivers/media/video/em28xx/Kconfig" config USB_DSBR tristate "D-Link USB FM radio support (EXPERIMENTAL)" - depends on USB && VIDEO_DEV && EXPERIMENTAL + depends on USB && VIDEO_V4L1 && EXPERIMENTAL ---help--- Say Y here if you want to connect this type of radio to your computer's USB port. Note that the audio is not digital, and @@ -434,7 +437,7 @@ source "drivers/media/video/et61x251/Kconfig" config USB_OV511 tristate "USB OV511 Camera support" - depends on USB && VIDEO_DEV + depends on USB && VIDEO_V4L1 ---help--- Say Y here if you want to connect this type of camera to your computer's USB port. See @@ -445,7 +448,7 @@ config USB_OV511 config USB_SE401 tristate "USB SE401 Camera support" - depends on USB && VIDEO_DEV + depends on USB && VIDEO_V4L1 ---help--- Say Y here if you want to connect this type of camera to your computer's USB port. See @@ -458,7 +461,7 @@ source "drivers/media/video/sn9c102/Kconfig" config USB_STV680 tristate "USB STV680 (Pencam) Camera support" - depends on USB && VIDEO_DEV + depends on USB && VIDEO_V4L1 ---help--- Say Y here if you want to connect this type of camera to your computer's USB port. This includes the Pencam line of cameras. @@ -470,7 +473,7 @@ config USB_STV680 config USB_W9968CF tristate "USB W996[87]CF JPEG Dual Mode Camera support" - depends on USB && VIDEO_DEV && I2C + depends on USB && VIDEO_V4L1 && I2C select VIDEO_OVCAMCHIP ---help--- Say Y here if you want support for cameras based on OV681 or diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index b3ea2d63db9b..11b06dac347b 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -10,7 +10,8 @@ tuner-objs := tuner-core.o tuner-types.o tuner-simple.o \ msp3400-objs := msp3400-driver.o msp3400-kthreads.o -obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o v4l1-compat.o compat_ioctl32.o +obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o compat_ioctl32.o +obj-$(CONFIG_VIDEO_V4L1_COMPAT) += v4l1-compat.o obj-$(CONFIG_VIDEO_BT848) += bt8xx/ obj-$(CONFIG_VIDEO_BT848) += tvaudio.o tda7432.o tda9875.o ir-kbd-i2c.o diff --git a/drivers/media/video/bt8xx/Kconfig b/drivers/media/video/bt8xx/Kconfig index 085477c12612..153f6a4a96c9 100644 --- a/drivers/media/video/bt8xx/Kconfig +++ b/drivers/media/video/bt8xx/Kconfig @@ -1,6 +1,6 @@ config VIDEO_BT848 tristate "BT848 Video For Linux" - depends on VIDEO_DEV && PCI && I2C + depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L2 select I2C_ALGOBIT select FW_LOADER select VIDEO_BTCX diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 72a417b31745..694d1d80ff3f 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -35,8 +35,10 @@ #include "cx88.h" #include +#ifdef CONFIG_VIDEO_V4L1_COMPAT /* Include V4L1 specific functions. Should be removed soon */ #include +#endif MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards"); MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig index 5a793ae7cc23..dfb15bfb83dc 100644 --- a/drivers/media/video/em28xx/Kconfig +++ b/drivers/media/video/em28xx/Kconfig @@ -1,6 +1,6 @@ config VIDEO_EM28XX tristate "Empia EM2800/2820/2840 USB video capture support" - depends on VIDEO_DEV && USB && I2C + depends on VIDEO_V4L1 && USB && I2C select VIDEO_BUF select VIDEO_TUNER select VIDEO_TVEEPROM diff --git a/drivers/media/video/et61x251/Kconfig b/drivers/media/video/et61x251/Kconfig index 6c43a90c6569..c6bff705688d 100644 --- a/drivers/media/video/et61x251/Kconfig +++ b/drivers/media/video/et61x251/Kconfig @@ -1,6 +1,6 @@ config USB_ET61X251 tristate "USB ET61X[12]51 PC Camera Controller support" - depends on USB && VIDEO_DEV + depends on USB && VIDEO_V4L1 ---help--- Say Y here if you want support for cameras based on Etoms ET61X151 or ET61X251 PC Camera Controllers. diff --git a/drivers/media/video/pwc/Kconfig b/drivers/media/video/pwc/Kconfig index 86376556f108..53cbc950f95c 100644 --- a/drivers/media/video/pwc/Kconfig +++ b/drivers/media/video/pwc/Kconfig @@ -1,6 +1,6 @@ config USB_PWC tristate "USB Philips Cameras" - depends on USB && VIDEO_DEV + depends on USB && VIDEO_V4L1 ---help--- Say Y or M here if you want to use one of these Philips & OEM webcams: diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index aeef80f88a6b..e4156ec9c6d7 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -31,8 +31,10 @@ #include "saa7134.h" #include +#ifdef CONFIG_VIDEO_V4L1_COMPAT /* Include V4L1 specific functions. Should be removed soon */ #include +#endif /* ------------------------------------------------------------------ */ diff --git a/drivers/media/video/sn9c102/Kconfig b/drivers/media/video/sn9c102/Kconfig index 55f2bc11964b..cf552e6b8ecf 100644 --- a/drivers/media/video/sn9c102/Kconfig +++ b/drivers/media/video/sn9c102/Kconfig @@ -1,6 +1,6 @@ config USB_SN9C102 tristate "USB SN9C10x PC Camera Controller support" - depends on USB && VIDEO_DEV + depends on USB && VIDEO_V4L1 ---help--- Say Y here if you want support for cameras based on SONiX SN9C101, SN9C102 or SN9C103 PC Camera Controllers. diff --git a/drivers/media/video/usbvideo/Kconfig b/drivers/media/video/usbvideo/Kconfig index 08a5d20bb2c0..39269a2c5635 100644 --- a/drivers/media/video/usbvideo/Kconfig +++ b/drivers/media/video/usbvideo/Kconfig @@ -3,7 +3,7 @@ config VIDEO_USBVIDEO config USB_VICAM tristate "USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)" - depends on USB && VIDEO_DEV && EXPERIMENTAL + depends on USB && VIDEO_V4L1 && EXPERIMENTAL select VIDEO_USBVIDEO ---help--- Say Y here if you have 3com homeconnect camera (vicam). @@ -13,7 +13,7 @@ config USB_VICAM config USB_IBMCAM tristate "USB IBM (Xirlink) C-it Camera support" - depends on USB && VIDEO_DEV + depends on USB && VIDEO_V4L1 select VIDEO_USBVIDEO ---help--- Say Y here if you want to connect a IBM "C-It" camera, also known as @@ -28,7 +28,7 @@ config USB_IBMCAM config USB_KONICAWC tristate "USB Konica Webcam support" - depends on USB && VIDEO_DEV + depends on USB && VIDEO_V4L1 select VIDEO_USBVIDEO ---help--- Say Y here if you want support for webcams based on a Konica diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 5e813404d068..9e42224b46b6 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -26,6 +26,10 @@ #include #include #include +#ifdef CONFIG_VIDEO_V4L1_COMPAT +/* Include V4L1 specific functions. Should be removed soon */ +#include +#endif #include #include #include diff --git a/drivers/media/video/zc0301/Kconfig b/drivers/media/video/zc0301/Kconfig index c3bf886b80cd..115833e4f4dd 100644 --- a/drivers/media/video/zc0301/Kconfig +++ b/drivers/media/video/zc0301/Kconfig @@ -1,6 +1,6 @@ config USB_ZC0301 tristate "USB ZC0301 Image Processor and Control Chip support" - depends on USB && VIDEO_DEV + depends on USB && VIDEO_V4L1 ---help--- Say Y here if you want support for cameras based on the ZC0301 Image Processor and Control Chip. diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index d7670ec1ec1e..ad7fa9c86c10 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1141,8 +1141,13 @@ extern char *v4l2_type_names[]; /* Compatibility layer interface -- v4l1-compat module */ typedef int (*v4l2_kioctl)(struct inode *inode, struct file *file, unsigned int cmd, void *arg); + +#ifdef CONFIG_VIDEO_V4L1_COMPAT int v4l_compat_translate_ioctl(struct inode *inode, struct file *file, int cmd, void *arg, v4l2_kioctl driver_ioctl); +#else +#define v4l_compat_translate_ioctl(inode,file,cmd,arg,ioctl) -EINVAL +#endif /* 32 Bits compatibility layer for 64 bits processors */ extern long v4l_compat_ioctl32(struct file *file, unsigned int cmd, -- cgit v1.2.3 From 986733e01d258c26107f1da9d8d47c718349ad2f Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 15 May 2006 09:43:58 -0700 Subject: [PATCH] RCU: introduce rcu_needs_cpu() interface With "Paul E. McKenney" Introduce rcu_needs_cpu() interface. This can be used to tell if there will be a new rcu batch on a cpu soon by looking at the curlist pointer. This can be used to avoid to enter a tickless idle state where the cpu would miss that a new batch is ready when rcu_start_batch would be called on a different cpu. Signed-off-by: Heiko Carstens Cc: "Paul E. McKenney" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/rcupdate.h | 1 + kernel/rcupdate.c | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) (limited to 'include/linux') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 5673008b61e1..970284f571a6 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -132,6 +132,7 @@ static inline void rcu_bh_qsctr_inc(int cpu) } extern int rcu_pending(int cpu); +extern int rcu_needs_cpu(int cpu); /** * rcu_read_lock - mark the beginning of an RCU read-side critical section. diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c index 6d32ff26f948..2058f88c7bbb 100644 --- a/kernel/rcupdate.c +++ b/kernel/rcupdate.c @@ -479,12 +479,31 @@ static int __rcu_pending(struct rcu_ctrlblk *rcp, struct rcu_data *rdp) return 0; } +/* + * Check to see if there is any immediate RCU-related work to be done + * by the current CPU, returning 1 if so. This function is part of the + * RCU implementation; it is -not- an exported member of the RCU API. + */ int rcu_pending(int cpu) { return __rcu_pending(&rcu_ctrlblk, &per_cpu(rcu_data, cpu)) || __rcu_pending(&rcu_bh_ctrlblk, &per_cpu(rcu_bh_data, cpu)); } +/* + * Check to see if any future RCU-related work will need to be done + * by the current CPU, even if none need be done immediately, returning + * 1 if so. This function is part of the RCU implementation; it is -not- + * an exported member of the RCU API. + */ +int rcu_needs_cpu(int cpu) +{ + struct rcu_data *rdp = &per_cpu(rcu_data, cpu); + struct rcu_data *rdp_bh = &per_cpu(rcu_bh_data, cpu); + + return (!!rdp->curlist || !!rdp_bh->curlist || rcu_pending(cpu)); +} + void rcu_check_callbacks(int cpu, int user) { if (user || -- cgit v1.2.3 From 5e376613899076396d0c97de67ad072587267370 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Mon, 15 May 2006 09:44:06 -0700 Subject: [PATCH] symbol_put_addr() locks kernel Even since a previous patch: Fix race between CONFIG_DEBUG_SLABALLOC and modules Sun, 27 Jun 2004 17:55:19 +0000 (17:55 +0000) http://www.kernel.org/git/?p=linux/kernel/git/torvalds/old-2.6-bkcvs.git;a=commit;h=92b3db26d31cf21b70e3c1eadc56c179506d8fbe The function symbol_put_addr() will deadlock the kernel. symbol_put_addr() would acquire modlist_lock, then while holding the lock call two functions kernel_text_address() and module_text_address() which also try to acquire the same lock. This deadlocks the kernel of course. This patch changes symbol_put_addr() to not acquire the modlist_lock, it doesn't need it since it never looks at the module list directly. Also, it now uses core_kernel_text() instead of kernel_text_address(). The latter has an additional check for addr inside a module, but we don't need to do that since we call module_text_address() (the same function kernel_text_address uses) ourselves. Signed-off-by: Trent Piepho Cc: Zwane Mwaikambo Acked-by: Rusty Russell Cc: Johannes Stezenbach Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kernel.h | 1 + kernel/extable.c | 2 +- kernel/module.c | 12 ++++++------ 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index e1bd0842f6a1..f4fc576ed4c4 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -124,6 +124,7 @@ extern int get_option(char **str, int *pint); extern char *get_options(const char *str, int nints, int *ints); extern unsigned long long memparse(char *ptr, char **retptr); +extern int core_kernel_text(unsigned long addr); extern int __kernel_text_address(unsigned long addr); extern int kernel_text_address(unsigned long addr); extern int session_of_pgrp(int pgrp); diff --git a/kernel/extable.c b/kernel/extable.c index 7501b531ceed..7fe262855317 100644 --- a/kernel/extable.c +++ b/kernel/extable.c @@ -40,7 +40,7 @@ const struct exception_table_entry *search_exception_tables(unsigned long addr) return e; } -static int core_kernel_text(unsigned long addr) +int core_kernel_text(unsigned long addr) { if (addr >= (unsigned long)_stext && addr <= (unsigned long)_etext) diff --git a/kernel/module.c b/kernel/module.c index d24deb0dbbc9..bbe04862e1b0 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -705,14 +705,14 @@ EXPORT_SYMBOL(__symbol_put); void symbol_put_addr(void *addr) { - unsigned long flags; + struct module *modaddr; - spin_lock_irqsave(&modlist_lock, flags); - if (!kernel_text_address((unsigned long)addr)) - BUG(); + if (core_kernel_text((unsigned long)addr)) + return; - module_put(module_text_address((unsigned long)addr)); - spin_unlock_irqrestore(&modlist_lock, flags); + if (!(modaddr = module_text_address((unsigned long)addr))) + BUG(); + module_put(modaddr); } EXPORT_SYMBOL_GPL(symbol_put_addr); -- cgit v1.2.3 From 39d24e64263cd3211705d3b61ea4171c65030921 Mon Sep 17 00:00:00 2001 From: Mike Kravetz Date: Mon, 15 May 2006 09:44:13 -0700 Subject: [PATCH] add slab_is_available() routine for boot code slab_is_available() indicates slab based allocators are available for use. SPARSEMEM code needs to know this as it can be called at various times during the boot process. Signed-off-by: Mike Kravetz Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/slab.h | 1 + mm/slab.c | 8 ++++++++ mm/sparse.c | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/slab.h b/include/linux/slab.h index 3af03b19c983..2d985d59c7b8 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -150,6 +150,7 @@ static inline void *kcalloc(size_t n, size_t size, gfp_t flags) extern void kfree(const void *); extern unsigned int ksize(const void *); +extern int slab_is_available(void); #ifdef CONFIG_NUMA extern void *kmem_cache_alloc_node(kmem_cache_t *, gfp_t flags, int node); diff --git a/mm/slab.c b/mm/slab.c index c32af7e7581e..b1d643b5238d 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -700,6 +700,14 @@ static enum { FULL } g_cpucache_up; +/* + * used by boot code to determine if it can use slab based allocator + */ +int slab_is_available(void) +{ + return g_cpucache_up == FULL; +} + static DEFINE_PER_CPU(struct work_struct, reap_work); static void free_block(struct kmem_cache *cachep, void **objpp, int len, diff --git a/mm/sparse.c b/mm/sparse.c index d7c32de99ee8..c5e89eb9ac8f 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -32,7 +32,7 @@ static struct mem_section *sparse_index_alloc(int nid) unsigned long array_size = SECTIONS_PER_ROOT * sizeof(struct mem_section); - if (system_state == SYSTEM_RUNNING) + if (slab_is_available()) section = kmalloc_node(array_size, GFP_KERNEL, nid); else section = alloc_bootmem_node(NODE_DATA(nid), array_size); -- cgit v1.2.3 From e6333fd4ddf7a583480017f535b9ea53c116ab81 Mon Sep 17 00:00:00 2001 From: Hua Zhong Date: Mon, 15 May 2006 09:44:22 -0700 Subject: [PATCH] fix can_share_swap_page() when !CONFIG_SWAP can_share_swap_page() is used to check if the page has the last reference. This avoids allocating a new page for COW if it's the last page. However, if CONFIG_SWAP is not set, can_share_swap_page() is defined as 0, thus always causes a copy for the last COW page. The below simple patch fixes it. Signed-off-by: Hua Zhong Cc: David Howells Cc: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/swap.h b/include/linux/swap.h index 5b1fdf1cff4f..f03c24719302 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -296,7 +296,7 @@ static inline void disable_swap_token(void) #define read_swap_cache_async(swp,vma,addr) NULL #define lookup_swap_cache(swp) NULL #define valid_swaphandles(swp, off) 0 -#define can_share_swap_page(p) 0 +#define can_share_swap_page(p) (page_mapcount(p) == 1) #define move_to_swap_cache(p, swp) 1 #define move_from_swap_cache(p, i, m) 1 #define __delete_from_swap_cache(p) /*NOTHING*/ -- cgit v1.2.3 From 4cff33f94fefcce1b3c01a9d1da6bb85fe3cbdfa Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 17 Feb 2006 10:02:18 -0800 Subject: [PATCH] SPI: per-transfer overrides for wordsize and clocking Some protocols (like one for some bitmap displays) require different clock speed or word size settings for each transfer in an SPI message. This adds those parameters to struct spi_transfer. They are to be used when they are nonzero; otherwise the defaults from spi_device are to be used. The patch also adds a setup_transfer callback to spi_bitbang, uses it for messages that use those overrides, and implements it so that the pure bitbanging code can help resolve any questions about how it should work. Signed-off-by: Imre Deak Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi_bitbang.c | 77 +++++++++++++++++++++++++++++++++-------- include/linux/spi/spi.h | 8 +++++ include/linux/spi/spi_bitbang.h | 6 ++++ 3 files changed, 77 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c index f037e5593269..0525c99118c3 100644 --- a/drivers/spi/spi_bitbang.c +++ b/drivers/spi/spi_bitbang.c @@ -138,6 +138,43 @@ static unsigned bitbang_txrx_32( return t->len - count; } +static int +bitbang_transfer_setup(struct spi_device *spi, struct spi_transfer *t) +{ + struct spi_bitbang_cs *cs = spi->controller_state; + u8 bits_per_word; + u32 hz; + + if (t) { + bits_per_word = t->bits_per_word; + hz = t->speed_hz; + } else { + bits_per_word = 0; + hz = 0; + } + + /* spi_transfer level calls that work per-word */ + if (!bits_per_word) + bits_per_word = spi->bits_per_word; + if (bits_per_word <= 8) + cs->txrx_bufs = bitbang_txrx_8; + else if (bits_per_word <= 16) + cs->txrx_bufs = bitbang_txrx_16; + else if (bits_per_word <= 32) + cs->txrx_bufs = bitbang_txrx_32; + else + return -EINVAL; + + /* nsecs = (clock period)/2 */ + if (!hz) + hz = spi->max_speed_hz; + cs->nsecs = (1000000000/2) / hz; + if (cs->nsecs > MAX_UDELAY_MS * 1000) + return -EINVAL; + + return 0; +} + /** * spi_bitbang_setup - default setup for per-word I/O loops */ @@ -145,6 +182,7 @@ int spi_bitbang_setup(struct spi_device *spi) { struct spi_bitbang_cs *cs = spi->controller_state; struct spi_bitbang *bitbang; + int retval; if (!spi->max_speed_hz) return -EINVAL; @@ -160,25 +198,14 @@ int spi_bitbang_setup(struct spi_device *spi) if (!spi->bits_per_word) spi->bits_per_word = 8; - /* spi_transfer level calls that work per-word */ - if (spi->bits_per_word <= 8) - cs->txrx_bufs = bitbang_txrx_8; - else if (spi->bits_per_word <= 16) - cs->txrx_bufs = bitbang_txrx_16; - else if (spi->bits_per_word <= 32) - cs->txrx_bufs = bitbang_txrx_32; - else - return -EINVAL; - /* per-word shift register access, in hardware or bitbanging */ cs->txrx_word = bitbang->txrx_word[spi->mode & (SPI_CPOL|SPI_CPHA)]; if (!cs->txrx_word) return -EINVAL; - /* nsecs = (clock period)/2 */ - cs->nsecs = (1000000000/2) / (spi->max_speed_hz); - if (cs->nsecs > MAX_UDELAY_MS * 1000) - return -EINVAL; + retval = bitbang_transfer_setup(spi, NULL); + if (retval < 0) + return retval; dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec\n", __FUNCTION__, spi->mode & (SPI_CPOL | SPI_CPHA), @@ -246,6 +273,8 @@ static void bitbang_work(void *_bitbang) unsigned tmp; unsigned cs_change; int status; + int (*setup_transfer)(struct spi_device *, + struct spi_transfer *); m = container_of(bitbang->queue.next, struct spi_message, queue); @@ -262,6 +291,7 @@ static void bitbang_work(void *_bitbang) tmp = 0; cs_change = 1; status = 0; + setup_transfer = NULL; list_for_each_entry (t, &m->transfers, transfer_list) { if (bitbang->shutdown) { @@ -269,6 +299,20 @@ static void bitbang_work(void *_bitbang) break; } + /* override or restore speed and wordsize */ + if (t->speed_hz || t->bits_per_word) { + setup_transfer = bitbang->setup_transfer; + if (!setup_transfer) { + status = -ENOPROTOOPT; + break; + } + } + if (setup_transfer) { + status = setup_transfer(spi, t); + if (status < 0) + break; + } + /* set up default clock polarity, and activate chip; * this implicitly updates clock and spi modes as * previously recorded for this device via setup(). @@ -325,6 +369,10 @@ static void bitbang_work(void *_bitbang) m->status = status; m->complete(m->context); + /* restore speed and wordsize */ + if (setup_transfer) + setup_transfer(spi, NULL); + /* normally deactivate chipselect ... unless no error and * cs_change has hinted that the next message will probably * be for this chip too. @@ -406,6 +454,7 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) bitbang->use_dma = 0; bitbang->txrx_bufs = spi_bitbang_bufs; if (!bitbang->master->setup) { + bitbang->setup_transfer = bitbang_transfer_setup; bitbang->master->setup = spi_bitbang_setup; bitbang->master->cleanup = spi_bitbang_cleanup; } diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index b05f1463a267..caa4665e3fa2 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -31,6 +31,7 @@ extern struct bus_type spi_bus_type; * @master: SPI controller used with the device. * @max_speed_hz: Maximum clock rate to be used with this chip * (on this board); may be changed by the device's driver. + * The spi_transfer.speed_hz can override this for each transfer. * @chip-select: Chipselect, distinguishing chips handled by "master". * @mode: The spi mode defines how data is clocked out and in. * This may be changed by the device's driver. @@ -38,6 +39,7 @@ extern struct bus_type spi_bus_type; * like eight or 12 bits are common. In-memory wordsizes are * powers of two bytes (e.g. 20 bit samples use 32 bits). * This may be changed by the device's driver. + * The spi_transfer.bits_per_word can override this for each transfer. * @irq: Negative, or the number passed to request_irq() to receive * interrupts from this device. * @controller_state: Controller's runtime state @@ -268,6 +270,10 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum); * @tx_dma: DMA address of tx_buf, if spi_message.is_dma_mapped * @rx_dma: DMA address of rx_buf, if spi_message.is_dma_mapped * @len: size of rx and tx buffers (in bytes) + * @speed_hz: Select a speed other then the device default for this + * transfer. If 0 the default (from spi_device) is used. + * @bits_per_word: select a bits_per_word other then the device default + * for this transfer. If 0 the default (from spi_device) is used. * @cs_change: affects chipselect after this transfer completes * @delay_usecs: microseconds to delay after this transfer before * (optionally) changing the chipselect status, then starting @@ -322,7 +328,9 @@ struct spi_transfer { dma_addr_t rx_dma; unsigned cs_change:1; + u8 bits_per_word; u16 delay_usecs; + u32 speed_hz; struct list_head transfer_list; }; diff --git a/include/linux/spi/spi_bitbang.h b/include/linux/spi/spi_bitbang.h index c961fe9bf3eb..c954557b757c 100644 --- a/include/linux/spi/spi_bitbang.h +++ b/include/linux/spi/spi_bitbang.h @@ -30,6 +30,12 @@ struct spi_bitbang { struct spi_master *master; + /* setup_transfer() changes clock and/or wordsize to match settings + * for this transfer; zeroes restore defaults from spi_device. + */ + int (*setup_transfer)(struct spi_device *spi, + struct spi_transfer *t); + void (*chipselect)(struct spi_device *spi, int is_on); #define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */ #define BITBANG_CS_INACTIVE 0 -- cgit v1.2.3 From 747d844ee9a183ff3067bb1181f2a25c50649538 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sun, 2 Apr 2006 10:33:37 -0800 Subject: [PATCH] SPI: spi whitespace fixes This removes superfluous whitespace in the header. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- include/linux/spi/spi.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index caa4665e3fa2..082006714b85 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -36,15 +36,15 @@ extern struct bus_type spi_bus_type; * @mode: The spi mode defines how data is clocked out and in. * This may be changed by the device's driver. * @bits_per_word: Data transfers involve one or more words; word sizes - * like eight or 12 bits are common. In-memory wordsizes are + * like eight or 12 bits are common. In-memory wordsizes are * powers of two bytes (e.g. 20 bit samples use 32 bits). * This may be changed by the device's driver. * The spi_transfer.bits_per_word can override this for each transfer. * @irq: Negative, or the number passed to request_irq() to receive - * interrupts from this device. + * interrupts from this device. * @controller_state: Controller's runtime state * @controller_data: Board-specific definitions for controller, such as - * FIFO initialization parameters; from board_info.controller_data + * FIFO initialization parameters; from board_info.controller_data * * An spi_device is used to interchange data between an SPI slave * (usually a discrete chip) and CPU memory. @@ -145,13 +145,13 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * struct spi_master - interface to SPI master controller * @cdev: class interface to this driver * @bus_num: board-specific (and often SOC-specific) identifier for a - * given SPI controller. + * given SPI controller. * @num_chipselect: chipselects are used to distinguish individual - * SPI slaves, and are numbered from zero to num_chipselects. - * each slave has a chipselect signal, but it's common that not - * every chipselect is connected to a slave. + * SPI slaves, and are numbered from zero to num_chipselects. + * each slave has a chipselect signal, but it's common that not + * every chipselect is connected to a slave. * @setup: updates the device mode and clocking records used by a - * device's SPI controller; protocol code may call this. + * device's SPI controller; protocol code may call this. * @transfer: adds a message to the controller's transfer queue. * @cleanup: frees controller-specific state * @@ -276,8 +276,8 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum); * for this transfer. If 0 the default (from spi_device) is used. * @cs_change: affects chipselect after this transfer completes * @delay_usecs: microseconds to delay after this transfer before - * (optionally) changing the chipselect status, then starting - * the next transfer or completing this spi_message. + * (optionally) changing the chipselect status, then starting + * the next transfer or completing this spi_message. * @transfer_list: transfers are sequenced through spi_message.transfers * * SPI transfers always write the same number of bytes as they read. @@ -364,7 +364,7 @@ struct spi_transfer { * and its transfers, ignore them until its completion callback. */ struct spi_message { - struct list_head transfers; + struct list_head transfers; struct spi_device *spi; @@ -382,7 +382,7 @@ struct spi_message { */ /* completion is reported through a callback */ - void (*complete)(void *context); + void (*complete)(void *context); void *context; unsigned actual_length; int status; -- cgit v1.2.3 From ff9f4771b5f017ee0f57629488b6cd7a6ef3d19b Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Sun, 2 Apr 2006 16:06:35 -0500 Subject: [PATCH] SPI: Renamed bitbang_transfer_setup to spi_bitbang_setup_transfer and export it Renamed bitbang_transfer_setup to follow convention of other exported symbols from spi-bitbang. Exported spi_bitbang_setup_transfer to allow users of spi-bitbang to use the function in their own setup_transfer. Signed-off-by: Kumar Gala Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi_bitbang.c | 10 ++++++---- include/linux/spi/spi_bitbang.h | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c index 0525c99118c3..6c3da64d609a 100644 --- a/drivers/spi/spi_bitbang.c +++ b/drivers/spi/spi_bitbang.c @@ -138,8 +138,7 @@ static unsigned bitbang_txrx_32( return t->len - count; } -static int -bitbang_transfer_setup(struct spi_device *spi, struct spi_transfer *t) +int spi_bitbang_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { struct spi_bitbang_cs *cs = spi->controller_state; u8 bits_per_word; @@ -174,6 +173,7 @@ bitbang_transfer_setup(struct spi_device *spi, struct spi_transfer *t) return 0; } +EXPORT_SYMBOL_GPL(spi_bitbang_setup_transfer); /** * spi_bitbang_setup - default setup for per-word I/O loops @@ -203,7 +203,7 @@ int spi_bitbang_setup(struct spi_device *spi) if (!cs->txrx_word) return -EINVAL; - retval = bitbang_transfer_setup(spi, NULL); + retval = spi_bitbang_setup_transfer(spi, NULL); if (retval < 0) return retval; @@ -454,7 +454,9 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) bitbang->use_dma = 0; bitbang->txrx_bufs = spi_bitbang_bufs; if (!bitbang->master->setup) { - bitbang->setup_transfer = bitbang_transfer_setup; + if (!bitbang->setup_transfer) + bitbang->setup_transfer = + spi_bitbang_setup_transfer; bitbang->master->setup = spi_bitbang_setup; bitbang->master->cleanup = spi_bitbang_cleanup; } diff --git a/include/linux/spi/spi_bitbang.h b/include/linux/spi/spi_bitbang.h index c954557b757c..16ce178f54d7 100644 --- a/include/linux/spi/spi_bitbang.h +++ b/include/linux/spi/spi_bitbang.h @@ -57,6 +57,8 @@ struct spi_bitbang { extern int spi_bitbang_setup(struct spi_device *spi); extern void spi_bitbang_cleanup(const struct spi_device *spi); extern int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m); +extern int spi_bitbang_setup_transfer(struct spi_device *spi, + struct spi_transfer *t); /* start or stop queue processing */ extern int spi_bitbang_start(struct spi_bitbang *spi); -- cgit v1.2.3 From ccf77cc4af5b048e20cfd9327fcc286cb69c34cc Mon Sep 17 00:00:00 2001 From: David Brownell Date: Mon, 3 Apr 2006 15:46:22 -0700 Subject: [PATCH] SPI: devices can require LSB-first encodings Add spi_device hook for LSB-first word encoding, and update all the (in-tree) controller drivers to reject such devices. Eventually, some controller drivers will be updated to support lsb-first encodings on the wire; no current drivers need this. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi_bitbang.c | 11 ++++++++++- include/linux/spi/spi.h | 7 +++++-- 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c index 6c3da64d609a..0f7f5c64391c 100644 --- a/drivers/spi/spi_bitbang.c +++ b/drivers/spi/spi_bitbang.c @@ -187,13 +187,22 @@ int spi_bitbang_setup(struct spi_device *spi) if (!spi->max_speed_hz) return -EINVAL; + bitbang = spi_master_get_devdata(spi->master); + + /* REVISIT: some systems will want to support devices using lsb-first + * bit encodings on the wire. In pure software that would be trivial, + * just bitbang_txrx_le_cphaX() routines shifting the other way, and + * some hardware controllers also have this support. + */ + if ((spi->mode & SPI_LSB_FIRST) != 0) + return -EINVAL; + if (!cs) { cs = kzalloc(sizeof *cs, SLAB_KERNEL); if (!cs) return -ENOMEM; spi->controller_state = cs; } - bitbang = spi_master_get_devdata(spi->master); if (!spi->bits_per_word) spi->bits_per_word = 8; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 082006714b85..77add901691d 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -35,10 +35,13 @@ extern struct bus_type spi_bus_type; * @chip-select: Chipselect, distinguishing chips handled by "master". * @mode: The spi mode defines how data is clocked out and in. * This may be changed by the device's driver. + * The "active low" default for chipselect mode can be overridden, + * as can the "MSB first" default for each word in a transfer. * @bits_per_word: Data transfers involve one or more words; word sizes * like eight or 12 bits are common. In-memory wordsizes are * powers of two bytes (e.g. 20 bit samples use 32 bits). - * This may be changed by the device's driver. + * This may be changed by the device's driver, or left at the + * default (0) indicating protocol words are eight bit bytes. * The spi_transfer.bits_per_word can override this for each transfer. * @irq: Negative, or the number passed to request_irq() to receive * interrupts from this device. @@ -67,6 +70,7 @@ struct spi_device { #define SPI_MODE_2 (SPI_CPOL|0) #define SPI_MODE_3 (SPI_CPOL|SPI_CPHA) #define SPI_CS_HIGH 0x04 /* chipselect active high? */ +#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */ u8 bits_per_word; int irq; void *controller_state; @@ -75,7 +79,6 @@ struct spi_device { // likely need more hooks for more protocol options affecting how // the controller talks to each chip, like: - // - bit order (default is wordwise msb-first) // - memory packing (12 bit samples into low bits, others zeroed) // - priority // - drop chipselect after each word -- cgit v1.2.3 From a020ed7521a9737bcf3e34eb880867c60c3c68d0 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Mon, 3 Apr 2006 15:49:04 -0700 Subject: [PATCH] SPI: busnum == 0 needs to work We need to be able to have a "SPI bus 0" matching chip numbering; but that number was wrongly used to flag dynamic allocation of a bus number. This patch resolves that issue; now negative numbers trigger dynamic alloc. It also updates the how-to-write-a-controller-driver overview to mention this stuff. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- Documentation/spi/spi-summary | 34 +++++++++++++++++++++++++++++++++- drivers/spi/spi.c | 4 ++-- include/linux/spi/spi.h | 6 +++--- 3 files changed, 38 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/Documentation/spi/spi-summary b/Documentation/spi/spi-summary index a5ffba33a351..068732d32276 100644 --- a/Documentation/spi/spi-summary +++ b/Documentation/spi/spi-summary @@ -414,7 +414,33 @@ to get the driver-private data allocated for that device. The driver will initialize the fields of that spi_master, including the bus number (maybe the same as the platform device ID) and three methods used to interact with the SPI core and SPI protocol drivers. It will -also initialize its own internal state. +also initialize its own internal state. (See below about bus numbering +and those methods.) + +After you initialize the spi_master, then use spi_register_master() to +publish it to the rest of the system. At that time, device nodes for +the controller and any predeclared spi devices will be made available, +and the driver model core will take care of binding them to drivers. + +If you need to remove your SPI controller driver, spi_unregister_master() +will reverse the effect of spi_register_master(). + + +BUS NUMBERING + +Bus numbering is important, since that's how Linux identifies a given +SPI bus (shared SCK, MOSI, MISO). Valid bus numbers start at zero. On +SOC systems, the bus numbers should match the numbers defined by the chip +manufacturer. For example, hardware controller SPI2 would be bus number 2, +and spi_board_info for devices connected to it would use that number. + +If you don't have such hardware-assigned bus number, and for some reason +you can't just assign them, then provide a negative bus number. That will +then be replaced by a dynamically assigned number. You'd then need to treat +this as a non-static configuration (see above). + + +SPI MASTER METHODS master->setup(struct spi_device *spi) This sets up the device clock rate, SPI mode, and word sizes. @@ -431,6 +457,9 @@ also initialize its own internal state. state it dynamically associates with that device. If you do that, be sure to provide the cleanup() method to free that state. + +SPI MESSAGE QUEUE + The bulk of the driver will be managing the I/O queue fed by transfer(). That queue could be purely conceptual. For example, a driver used only @@ -440,6 +469,9 @@ But the queue will probably be very real, using message->queue, PIO, often DMA (especially if the root filesystem is in SPI flash), and execution contexts like IRQ handlers, tasklets, or workqueues (such as keventd). Your driver can be as fancy, or as simple, as you need. +Such a transfer() method would normally just add the message to a +queue, and then start some asynchronous transfer engine (unless it's +already running). THANKS TO diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 1168ef015887..7a3f733051e9 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -395,7 +395,7 @@ EXPORT_SYMBOL_GPL(spi_alloc_master); int __init_or_module spi_register_master(struct spi_master *master) { - static atomic_t dyn_bus_id = ATOMIC_INIT(0); + static atomic_t dyn_bus_id = ATOMIC_INIT((1<<16) - 1); struct device *dev = master->cdev.dev; int status = -ENODEV; int dynamic = 0; @@ -404,7 +404,7 @@ spi_register_master(struct spi_master *master) return -ENODEV; /* convention: dynamically assigned bus IDs count down from the max */ - if (master->bus_num == 0) { + if (master->bus_num < 0) { master->bus_num = atomic_dec_return(&dyn_bus_id); dynamic = 1; } diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 77add901691d..e928c0dcc297 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -172,13 +172,13 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) struct spi_master { struct class_device cdev; - /* other than zero (== assign one dynamically), bus_num is fully + /* other than negative (== assign one dynamically), bus_num is fully * board-specific. usually that simplifies to being SOC-specific. - * example: one SOC has three SPI controllers, numbered 1..3, + * example: one SOC has three SPI controllers, numbered 0..2, * and one board's schematics might show it using SPI-2. software * would normally use bus_num=2 for that controller. */ - u16 bus_num; + s16 bus_num; /* chipselects will be integral to many controllers; some others * might use board-specific GPIOs. -- cgit v1.2.3 From 2c171bf13423dc5293188cea7f6c2da1720926e2 Mon Sep 17 00:00:00 2001 From: Pavel Pisa Date: Fri, 19 May 2006 21:48:03 +0100 Subject: [ARM] 3531/1: i.MX/MX1 SD/MMC ensure, that clock are stopped before new command and cleanups Patch from Pavel Pisa There has been problems that for some paths that clock are not stopped during new command programming and initiation. Result is issuing of incorrect command to the card. Some other problems are cleaned too. Noisy report of known ERRATUM #4 has been suppressed. Signed-off-by: Pavel Pisa Signed-off-by: Russell King --- drivers/mmc/au1xmmc.c | 6 +++--- drivers/mmc/imxmmc.c | 24 ++++++++++++++---------- drivers/mmc/mmc.c | 1 + drivers/mmc/mmc_block.c | 1 + drivers/mmc/pxamci.c | 4 ++-- drivers/mmc/wbsd.c | 8 ++++---- include/linux/mmc/mmc.h | 1 + 7 files changed, 26 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c index 914d62b24064..5dc4bee7abeb 100644 --- a/drivers/mmc/au1xmmc.c +++ b/drivers/mmc/au1xmmc.c @@ -310,7 +310,7 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status) } else data->bytes_xfered = - (data->blocks * (1 << data->blksz_bits)) - + (data->blocks * data->blksz) - host->pio.len; } @@ -575,7 +575,7 @@ static int au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data) { - int datalen = data->blocks * (1 << data->blksz_bits); + int datalen = data->blocks * data->blksz; if (dma != 0) host->flags |= HOST_F_DMA; @@ -596,7 +596,7 @@ au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data) if (host->dma.len == 0) return MMC_ERR_TIMEOUT; - au_writel((1 << data->blksz_bits) - 1, HOST_BLKSIZE(host)); + au_writel(data->blksz - 1, HOST_BLKSIZE(host)); if (host->flags & HOST_F_DMA) { int i; diff --git a/drivers/mmc/imxmmc.c b/drivers/mmc/imxmmc.c index 79358e223f57..a4eb1d0e7a71 100644 --- a/drivers/mmc/imxmmc.c +++ b/drivers/mmc/imxmmc.c @@ -218,8 +218,10 @@ static int imxmci_busy_wait_for_status(struct imxmci_host *host, if(!loops) return 0; - dev_info(mmc_dev(host->mmc), "busy wait for %d usec in %s, STATUS = 0x%x (0x%x)\n", - loops, where, *pstat, stat_mask); + /* The busy-wait is expected there for clock <8MHz due to SDHC hardware flaws */ + if(!(stat_mask & STATUS_END_CMD_RESP) || (host->mmc->ios.clock>=8000000)) + dev_info(mmc_dev(host->mmc), "busy wait for %d usec in %s, STATUS = 0x%x (0x%x)\n", + loops, where, *pstat, stat_mask); return loops; } @@ -333,6 +335,9 @@ static void imxmci_start_cmd(struct imxmci_host *host, struct mmc_command *cmd, WARN_ON(host->cmd != NULL); host->cmd = cmd; + /* Ensure, that clock are stopped else command programming and start fails */ + imxmci_stop_clock(host); + if (cmd->flags & MMC_RSP_BUSY) cmdat |= CMD_DAT_CONT_BUSY; @@ -553,7 +558,7 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat) int trans_done = 0; unsigned int stat = *pstat; - if(host->actual_bus_width == MMC_BUS_WIDTH_4) + if(host->actual_bus_width != MMC_BUS_WIDTH_4) burst_len = 16; else burst_len = 64; @@ -591,8 +596,7 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat) stat = MMC_STATUS; /* Flush extra bytes from FIFO */ - while(flush_len >= 2){ - flush_len -= 2; + while(flush_len && !(stat & STATUS_DATA_TRANS_DONE)){ i = MMC_BUFFER_ACCESS; stat = MMC_STATUS; stat &= ~STATUS_CRC_READ_ERR; /* Stupid but required there */ @@ -746,10 +750,6 @@ static void imxmci_tasklet_fnc(unsigned long data) data_dir_mask = STATUS_DATA_TRANS_DONE; } - imxmci_busy_wait_for_status(host, &stat, - data_dir_mask, - 50, "imxmci_tasklet_fnc data"); - if(stat & data_dir_mask) { clear_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events); imxmci_data_done(host, stat); @@ -865,7 +865,11 @@ static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) imxmci_stop_clock(host); MMC_CLK_RATE = (prescaler<<3) | clk; - imxmci_start_clock(host); + /* + * Under my understanding, clock should not be started there, because it would + * initiate SDHC sequencer and send last or random command into card + */ + /*imxmci_start_clock(host);*/ dev_dbg(mmc_dev(host->mmc), "MMC_CLK_RATE: 0x%08x\n", MMC_CLK_RATE); } else { diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 1ca2c8b9c9b5..6201f3086a02 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -951,6 +951,7 @@ static void mmc_read_scrs(struct mmc_host *host) data.timeout_ns = card->csd.tacc_ns * 10; data.timeout_clks = card->csd.tacc_clks * 10; data.blksz_bits = 3; + data.blksz = 1 << 3; data.blocks = 1; data.flags = MMC_DATA_READ; data.sg = &sg; diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c index 06bd1f4cb9b1..e39cc05c64c2 100644 --- a/drivers/mmc/mmc_block.c +++ b/drivers/mmc/mmc_block.c @@ -175,6 +175,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) brq.data.timeout_ns = card->csd.tacc_ns * 10; brq.data.timeout_clks = card->csd.tacc_clks * 10; brq.data.blksz_bits = md->block_bits; + brq.data.blksz = 1 << md->block_bits; brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); brq.stop.opcode = MMC_STOP_TRANSMISSION; brq.stop.arg = 0; diff --git a/drivers/mmc/pxamci.c b/drivers/mmc/pxamci.c index f97b472085cb..b49368fd96b8 100644 --- a/drivers/mmc/pxamci.c +++ b/drivers/mmc/pxamci.c @@ -119,7 +119,7 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) nob = 0xffff; writel(nob, host->base + MMC_NOB); - writel(1 << data->blksz_bits, host->base + MMC_BLKLEN); + writel(data->blksz, host->base + MMC_BLKLEN); clks = (unsigned long long)data->timeout_ns * CLOCKRATE; do_div(clks, 1000000000UL); @@ -283,7 +283,7 @@ static int pxamci_data_done(struct pxamci_host *host, unsigned int stat) * data blocks as being in error. */ if (data->error == MMC_ERR_NONE) - data->bytes_xfered = data->blocks << data->blksz_bits; + data->bytes_xfered = data->blocks * data->blksz; else data->bytes_xfered = 0; diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c index 39b3d97f891e..8167332d4013 100644 --- a/drivers/mmc/wbsd.c +++ b/drivers/mmc/wbsd.c @@ -662,14 +662,14 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) unsigned long dmaflags; DBGF("blksz %04x blks %04x flags %08x\n", - 1 << data->blksz_bits, data->blocks, data->flags); + data->blksz, data->blocks, data->flags); DBGF("tsac %d ms nsac %d clk\n", data->timeout_ns / 1000000, data->timeout_clks); /* * Calculate size. */ - host->size = data->blocks << data->blksz_bits; + host->size = data->blocks * data->blksz; /* * Check timeout values for overflow. @@ -696,12 +696,12 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) * Two bytes are needed for each data line. */ if (host->bus_width == MMC_BUS_WIDTH_1) { - blksize = (1 << data->blksz_bits) + 2; + blksize = data->blksz + 2; wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0); wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); } else if (host->bus_width == MMC_BUS_WIDTH_4) { - blksize = (1 << data->blksz_bits) + 2 * 4; + blksize = data->blksz + 2 * 4; wbsd_write_index(host, WBSD_IDX_PBSMSB, ((blksize >> 4) & 0xF0) | WBSD_DATA_WIDTH); diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index bdc556d88498..03a14a30c46a 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -69,6 +69,7 @@ struct mmc_data { unsigned int timeout_ns; /* data timeout (in ns, max 80ms) */ unsigned int timeout_clks; /* data timeout (in clocks) */ unsigned int blksz_bits; /* data block size */ + unsigned int blksz; /* data block size */ unsigned int blocks; /* number of blocks */ unsigned int error; /* data error */ unsigned int flags; -- cgit v1.2.3 From ccf06998fe179ae2cc9517ed1d75433dc0b5032d Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Sat, 20 May 2006 15:00:15 -0700 Subject: [PATCH] spi: add spi master driver for Freescale MPC83xx SPI controller This driver supports the SPI controller on the MPC83xx SoC devices from Freescale. Note, this driver supports only the simple shift register SPI controller and not the descriptor based CPM or QUICCEngine SPI controller. Signed-off-by: Kumar Gala Signed-off-by: David Brownell Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/Kconfig | 12 ++ drivers/spi/Makefile | 1 + drivers/spi/spi_mpc83xx.c | 483 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/fsl_devices.h | 11 + 4 files changed, 507 insertions(+) create mode 100644 drivers/spi/spi_mpc83xx.c (limited to 'include/linux') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 9ce1d01469b1..3867c6ef24f3 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -75,6 +75,18 @@ config SPI_BUTTERFLY inexpensive battery powered microcontroller evaluation board. This same cable can be used to flash new firmware. +config SPI_MPC83xx + tristate "Freescale MPC83xx SPI controller" + depends on SPI_MASTER && PPC_83xx && EXPERIMENTAL + select SPI_BITBANG + help + This enables using the Freescale MPC83xx SPI controller in master + mode. + + Note, this driver uniquely supports the SPI controller on the MPC83xx + family of PowerPC processors. The MPC83xx uses a simple set of shift + registers for data (opposed to the CPM based descriptor model). + config SPI_PXA2XX tristate "PXA2xx SSP SPI master" depends on SPI_MASTER && ARCH_PXA && EXPERIMENTAL diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 1bca5f95de25..5a410caa03c7 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_MASTER) += spi.o obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o +obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o # ... add above this line ... # SPI protocol drivers (device/link on bus) diff --git a/drivers/spi/spi_mpc83xx.c b/drivers/spi/spi_mpc83xx.c new file mode 100644 index 000000000000..5d92a7e5cb41 --- /dev/null +++ b/drivers/spi/spi_mpc83xx.c @@ -0,0 +1,483 @@ +/* + * MPC83xx SPI controller driver. + * + * Maintainer: Kumar Gala + * + * Copyright (C) 2006 Polycom, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* SPI Controller registers */ +struct mpc83xx_spi_reg { + u8 res1[0x20]; + __be32 mode; + __be32 event; + __be32 mask; + __be32 command; + __be32 transmit; + __be32 receive; +}; + +/* SPI Controller mode register definitions */ +#define SPMODE_CI_INACTIVEHIGH (1 << 29) +#define SPMODE_CP_BEGIN_EDGECLK (1 << 28) +#define SPMODE_DIV16 (1 << 27) +#define SPMODE_REV (1 << 26) +#define SPMODE_MS (1 << 25) +#define SPMODE_ENABLE (1 << 24) +#define SPMODE_LEN(x) ((x) << 20) +#define SPMODE_PM(x) ((x) << 16) + +/* + * Default for SPI Mode: + * SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk + */ +#define SPMODE_INIT_VAL (SPMODE_CI_INACTIVEHIGH | SPMODE_DIV16 | SPMODE_REV | \ + SPMODE_MS | SPMODE_LEN(7) | SPMODE_PM(0xf)) + +/* SPIE register values */ +#define SPIE_NE 0x00000200 /* Not empty */ +#define SPIE_NF 0x00000100 /* Not full */ + +/* SPIM register values */ +#define SPIM_NE 0x00000200 /* Not empty */ +#define SPIM_NF 0x00000100 /* Not full */ + +/* SPI Controller driver's private data. */ +struct mpc83xx_spi { + /* bitbang has to be first */ + struct spi_bitbang bitbang; + struct completion done; + + struct mpc83xx_spi_reg __iomem *base; + + /* rx & tx bufs from the spi_transfer */ + const void *tx; + void *rx; + + /* functions to deal with different sized buffers */ + void (*get_rx) (u32 rx_data, struct mpc83xx_spi *); + u32(*get_tx) (struct mpc83xx_spi *); + + unsigned int count; + u32 irq; + + unsigned nsecs; /* (clock cycle time)/2 */ + + u32 sysclk; + void (*activate_cs) (u8 cs, u8 polarity); + void (*deactivate_cs) (u8 cs, u8 polarity); +}; + +static inline void mpc83xx_spi_write_reg(__be32 __iomem * reg, u32 val) +{ + out_be32(reg, val); +} + +static inline u32 mpc83xx_spi_read_reg(__be32 __iomem * reg) +{ + return in_be32(reg); +} + +#define MPC83XX_SPI_RX_BUF(type) \ +void mpc83xx_spi_rx_buf_##type(u32 data, struct mpc83xx_spi *mpc83xx_spi) \ +{ \ + type * rx = mpc83xx_spi->rx; \ + *rx++ = (type)data; \ + mpc83xx_spi->rx = rx; \ +} + +#define MPC83XX_SPI_TX_BUF(type) \ +u32 mpc83xx_spi_tx_buf_##type(struct mpc83xx_spi *mpc83xx_spi) \ +{ \ + u32 data; \ + const type * tx = mpc83xx_spi->tx; \ + data = *tx++; \ + mpc83xx_spi->tx = tx; \ + return data; \ +} + +MPC83XX_SPI_RX_BUF(u8) +MPC83XX_SPI_RX_BUF(u16) +MPC83XX_SPI_RX_BUF(u32) +MPC83XX_SPI_TX_BUF(u8) +MPC83XX_SPI_TX_BUF(u16) +MPC83XX_SPI_TX_BUF(u32) + +static void mpc83xx_spi_chipselect(struct spi_device *spi, int value) +{ + struct mpc83xx_spi *mpc83xx_spi; + u8 pol = spi->mode & SPI_CS_HIGH ? 1 : 0; + + mpc83xx_spi = spi_master_get_devdata(spi->master); + + if (value == BITBANG_CS_INACTIVE) { + if (mpc83xx_spi->deactivate_cs) + mpc83xx_spi->deactivate_cs(spi->chip_select, pol); + } + + if (value == BITBANG_CS_ACTIVE) { + u32 regval = mpc83xx_spi_read_reg(&mpc83xx_spi->base->mode); + u32 len = spi->bits_per_word; + if (len == 32) + len = 0; + else + len = len - 1; + + /* mask out bits we are going to set */ + regval &= ~0x38ff0000; + + if (spi->mode & SPI_CPHA) + regval |= SPMODE_CP_BEGIN_EDGECLK; + if (spi->mode & SPI_CPOL) + regval |= SPMODE_CI_INACTIVEHIGH; + + regval |= SPMODE_LEN(len); + + if ((mpc83xx_spi->sysclk / spi->max_speed_hz) >= 64) { + u8 pm = mpc83xx_spi->sysclk / (spi->max_speed_hz * 64); + regval |= SPMODE_PM(pm) | SPMODE_DIV16; + } else { + u8 pm = mpc83xx_spi->sysclk / (spi->max_speed_hz * 4); + regval |= SPMODE_PM(pm); + } + + mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, regval); + if (mpc83xx_spi->activate_cs) + mpc83xx_spi->activate_cs(spi->chip_select, pol); + } +} + +static +int mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) +{ + struct mpc83xx_spi *mpc83xx_spi; + u32 regval; + u8 bits_per_word; + u32 hz; + + mpc83xx_spi = spi_master_get_devdata(spi->master); + + if (t) { + bits_per_word = t->bits_per_word; + hz = t->speed_hz; + } else { + bits_per_word = 0; + hz = 0; + } + + /* spi_transfer level calls that work per-word */ + if (!bits_per_word) + bits_per_word = spi->bits_per_word; + + /* Make sure its a bit width we support [4..16, 32] */ + if ((bits_per_word < 4) + || ((bits_per_word > 16) && (bits_per_word != 32))) + return -EINVAL; + + if (bits_per_word <= 8) { + mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u8; + mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u8; + } else if (bits_per_word <= 16) { + mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u16; + mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u16; + } else if (bits_per_word <= 32) { + mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u32; + mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u32; + } else + return -EINVAL; + + /* nsecs = (clock period)/2 */ + if (!hz) + hz = spi->max_speed_hz; + mpc83xx_spi->nsecs = (1000000000 / 2) / hz; + if (mpc83xx_spi->nsecs > MAX_UDELAY_MS * 1000) + return -EINVAL; + + if (bits_per_word == 32) + bits_per_word = 0; + else + bits_per_word = bits_per_word - 1; + + regval = mpc83xx_spi_read_reg(&mpc83xx_spi->base->mode); + + /* Mask out bits_per_wordgth */ + regval &= 0xff0fffff; + regval |= SPMODE_LEN(bits_per_word); + + mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, regval); + + return 0; +} + +static int mpc83xx_spi_setup(struct spi_device *spi) +{ + struct spi_bitbang *bitbang; + struct mpc83xx_spi *mpc83xx_spi; + int retval; + + if (!spi->max_speed_hz) + return -EINVAL; + + bitbang = spi_master_get_devdata(spi->master); + mpc83xx_spi = spi_master_get_devdata(spi->master); + + if (!spi->bits_per_word) + spi->bits_per_word = 8; + + retval = mpc83xx_spi_setup_transfer(spi, NULL); + if (retval < 0) + return retval; + + dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec\n", + __FUNCTION__, spi->mode & (SPI_CPOL | SPI_CPHA), + spi->bits_per_word, 2 * mpc83xx_spi->nsecs); + + /* NOTE we _need_ to call chipselect() early, ideally with adapter + * setup, unless the hardware defaults cooperate to avoid confusion + * between normal (active low) and inverted chipselects. + */ + + /* deselect chip (low or high) */ + spin_lock(&bitbang->lock); + if (!bitbang->busy) { + bitbang->chipselect(spi, BITBANG_CS_INACTIVE); + ndelay(mpc83xx_spi->nsecs); + } + spin_unlock(&bitbang->lock); + + return 0; +} + +static int mpc83xx_spi_bufs(struct spi_device *spi, struct spi_transfer *t) +{ + struct mpc83xx_spi *mpc83xx_spi; + u32 word; + + mpc83xx_spi = spi_master_get_devdata(spi->master); + + mpc83xx_spi->tx = t->tx_buf; + mpc83xx_spi->rx = t->rx_buf; + mpc83xx_spi->count = t->len; + INIT_COMPLETION(mpc83xx_spi->done); + + /* enable rx ints */ + mpc83xx_spi_write_reg(&mpc83xx_spi->base->mask, SPIM_NE); + + /* transmit word */ + word = mpc83xx_spi->get_tx(mpc83xx_spi); + mpc83xx_spi_write_reg(&mpc83xx_spi->base->transmit, word); + + wait_for_completion(&mpc83xx_spi->done); + + /* disable rx ints */ + mpc83xx_spi_write_reg(&mpc83xx_spi->base->mask, 0); + + return t->len - mpc83xx_spi->count; +} + +irqreturn_t mpc83xx_spi_irq(s32 irq, void *context_data, + struct pt_regs * ptregs) +{ + struct mpc83xx_spi *mpc83xx_spi = context_data; + u32 event; + irqreturn_t ret = IRQ_NONE; + + /* Get interrupt events(tx/rx) */ + event = mpc83xx_spi_read_reg(&mpc83xx_spi->base->event); + + /* We need handle RX first */ + if (event & SPIE_NE) { + u32 rx_data = mpc83xx_spi_read_reg(&mpc83xx_spi->base->receive); + + if (mpc83xx_spi->rx) + mpc83xx_spi->get_rx(rx_data, mpc83xx_spi); + + ret = IRQ_HANDLED; + } + + if ((event & SPIE_NF) == 0) + /* spin until TX is done */ + while (((event = + mpc83xx_spi_read_reg(&mpc83xx_spi->base->event)) & + SPIE_NF) == 0) + cpu_relax(); + + mpc83xx_spi->count -= 1; + if (mpc83xx_spi->count) { + if (mpc83xx_spi->tx) { + u32 word = mpc83xx_spi->get_tx(mpc83xx_spi); + mpc83xx_spi_write_reg(&mpc83xx_spi->base->transmit, + word); + } + } else { + complete(&mpc83xx_spi->done); + } + + /* Clear the events */ + mpc83xx_spi_write_reg(&mpc83xx_spi->base->event, event); + + return ret; +} + +static int __init mpc83xx_spi_probe(struct platform_device *dev) +{ + struct spi_master *master; + struct mpc83xx_spi *mpc83xx_spi; + struct fsl_spi_platform_data *pdata; + struct resource *r; + u32 regval; + int ret = 0; + + /* Get resources(memory, IRQ) associated with the device */ + master = spi_alloc_master(&dev->dev, sizeof(struct mpc83xx_spi)); + + if (master == NULL) { + ret = -ENOMEM; + goto err; + } + + platform_set_drvdata(dev, master); + pdata = dev->dev.platform_data; + + if (pdata == NULL) { + ret = -ENODEV; + goto free_master; + } + + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (r == NULL) { + ret = -ENODEV; + goto free_master; + } + + mpc83xx_spi = spi_master_get_devdata(master); + mpc83xx_spi->bitbang.master = spi_master_get(master); + mpc83xx_spi->bitbang.chipselect = mpc83xx_spi_chipselect; + mpc83xx_spi->bitbang.setup_transfer = mpc83xx_spi_setup_transfer; + mpc83xx_spi->bitbang.txrx_bufs = mpc83xx_spi_bufs; + mpc83xx_spi->sysclk = pdata->sysclk; + mpc83xx_spi->activate_cs = pdata->activate_cs; + mpc83xx_spi->deactivate_cs = pdata->deactivate_cs; + mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u8; + mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u8; + + mpc83xx_spi->bitbang.master->setup = mpc83xx_spi_setup; + init_completion(&mpc83xx_spi->done); + + mpc83xx_spi->base = ioremap(r->start, r->end - r->start + 1); + if (mpc83xx_spi->base == NULL) { + ret = -ENOMEM; + goto put_master; + } + + mpc83xx_spi->irq = platform_get_irq(dev, 0); + + if (mpc83xx_spi->irq < 0) { + ret = -ENXIO; + goto unmap_io; + } + + /* Register for SPI Interrupt */ + ret = request_irq(mpc83xx_spi->irq, mpc83xx_spi_irq, + 0, "mpc83xx_spi", mpc83xx_spi); + + if (ret != 0) + goto unmap_io; + + master->bus_num = pdata->bus_num; + master->num_chipselect = pdata->max_chipselect; + + /* SPI controller initializations */ + mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, 0); + mpc83xx_spi_write_reg(&mpc83xx_spi->base->mask, 0); + mpc83xx_spi_write_reg(&mpc83xx_spi->base->command, 0); + mpc83xx_spi_write_reg(&mpc83xx_spi->base->event, 0xffffffff); + + /* Enable SPI interface */ + regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE; + mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, regval); + + ret = spi_bitbang_start(&mpc83xx_spi->bitbang); + + if (ret != 0) + goto free_irq; + + printk(KERN_INFO + "%s: MPC83xx SPI Controller driver at 0x%p (irq = %d)\n", + dev->dev.bus_id, mpc83xx_spi->base, mpc83xx_spi->irq); + + return ret; + +free_irq: + free_irq(mpc83xx_spi->irq, mpc83xx_spi); +unmap_io: + iounmap(mpc83xx_spi->base); +put_master: + spi_master_put(master); +free_master: + kfree(master); +err: + return ret; +} + +static int __devexit mpc83xx_spi_remove(struct platform_device *dev) +{ + struct mpc83xx_spi *mpc83xx_spi; + struct spi_master *master; + + master = platform_get_drvdata(dev); + mpc83xx_spi = spi_master_get_devdata(master); + + spi_bitbang_stop(&mpc83xx_spi->bitbang); + free_irq(mpc83xx_spi->irq, mpc83xx_spi); + iounmap(mpc83xx_spi->base); + spi_master_put(mpc83xx_spi->bitbang.master); + + return 0; +} + +static struct platform_driver mpc83xx_spi_driver = { + .probe = mpc83xx_spi_probe, + .remove = __devexit_p(mpc83xx_spi_remove), + .driver = { + .name = "mpc83xx_spi", + }, +}; + +static int __init mpc83xx_spi_init(void) +{ + return platform_driver_register(&mpc83xx_spi_driver); +} + +static void __exit mpc83xx_spi_exit(void) +{ + platform_driver_unregister(&mpc83xx_spi_driver); +} + +module_init(mpc83xx_spi_init); +module_exit(mpc83xx_spi_exit); + +MODULE_AUTHOR("Kumar Gala"); +MODULE_DESCRIPTION("Simple MPC83xx SPI Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index a3a0e078f79d..16fbe59edeb1 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -110,5 +110,16 @@ struct fsl_usb2_platform_data { #define FSL_USB2_PORT0_ENABLED 0x00000001 #define FSL_USB2_PORT1_ENABLED 0x00000002 +struct fsl_spi_platform_data { + u32 initial_spmode; /* initial SPMODE value */ + u16 bus_num; + + /* board specific information */ + u16 max_chipselect; + void (*activate_cs)(u8 cs, u8 polarity); + void (*deactivate_cs)(u8 cs, u8 polarity); + u32 sysclk; +}; + #endif /* _FSL_DEVICE_H_ */ #endif /* __KERNEL__ */ -- cgit v1.2.3 From 1b81d6637d27a0e6a0506ecef65493b50d859cfc Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sat, 20 May 2006 15:00:16 -0700 Subject: [PATCH] drivers/base/firmware_class.c: cleanups - remove the following global function that is both unused and unimplemented: - register_firmware() - make the following needlessly global function static: - firmware_class_uevent() Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/firmware_class/README | 17 ---------- .../firmware_class/firmware_sample_driver.c | 11 ------ drivers/base/firmware_class.c | 39 +++++----------------- include/linux/firmware.h | 1 - 4 files changed, 8 insertions(+), 60 deletions(-) (limited to 'include/linux') diff --git a/Documentation/firmware_class/README b/Documentation/firmware_class/README index 43e836c07ae8..e9cc8bb26f7d 100644 --- a/Documentation/firmware_class/README +++ b/Documentation/firmware_class/README @@ -105,20 +105,3 @@ on the setup, so I think that the choice on what firmware to make persistent should be left to userspace. - - Why register_firmware()+__init can be useful: - - For boot devices needing firmware. - - To make the transition easier: - The firmware can be declared __init and register_firmware() - called on module_init. Then the firmware is warranted to be - there even if "firmware hotplug userspace" is not there yet or - it doesn't yet provide the needed firmware. - Once the firmware is widely available in userspace, it can be - removed from the kernel. Or made optional (CONFIG_.*_FIRMWARE). - - In either case, if firmware hotplug support is there, it can move the - firmware out of kernel memory into the real filesystem for later - usage. - - Note: If persistence is implemented on top of initramfs, - register_firmware() may not be appropriate. - diff --git a/Documentation/firmware_class/firmware_sample_driver.c b/Documentation/firmware_class/firmware_sample_driver.c index ad3edaba4533..87feccdb5c9f 100644 --- a/Documentation/firmware_class/firmware_sample_driver.c +++ b/Documentation/firmware_class/firmware_sample_driver.c @@ -5,8 +5,6 @@ * * Sample code on how to use request_firmware() from drivers. * - * Note that register_firmware() is currently useless. - * */ #include @@ -17,11 +15,6 @@ #include "linux/firmware.h" -#define WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE -#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE -char __init inkernel_firmware[] = "let's say that this is firmware\n"; -#endif - static struct device ghost_device = { .bus_id = "ghost0", }; @@ -104,10 +97,6 @@ static void sample_probe_async(void) static int sample_init(void) { -#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE - register_firmware("sample_driver_fw", inkernel_firmware, - sizeof(inkernel_firmware)); -#endif device_initialize(&ghost_device); /* since there is no real hardware insertion I just call the * sample probe functions here */ diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 472318205236..0c99ae6a3407 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -86,18 +86,9 @@ firmware_timeout_store(struct class *class, const char *buf, size_t count) static CLASS_ATTR(timeout, 0644, firmware_timeout_show, firmware_timeout_store); static void fw_class_dev_release(struct class_device *class_dev); -int firmware_class_uevent(struct class_device *dev, char **envp, - int num_envp, char *buffer, int buffer_size); -static struct class firmware_class = { - .name = "firmware", - .uevent = firmware_class_uevent, - .release = fw_class_dev_release, -}; - -int -firmware_class_uevent(struct class_device *class_dev, char **envp, - int num_envp, char *buffer, int buffer_size) +static int firmware_class_uevent(struct class_device *class_dev, char **envp, + int num_envp, char *buffer, int buffer_size) { struct firmware_priv *fw_priv = class_get_devdata(class_dev); int i = 0, len = 0; @@ -116,6 +107,12 @@ firmware_class_uevent(struct class_device *class_dev, char **envp, return 0; } +static struct class firmware_class = { + .name = "firmware", + .uevent = firmware_class_uevent, + .release = fw_class_dev_release, +}; + static ssize_t firmware_loading_show(struct class_device *class_dev, char *buf) { @@ -493,25 +490,6 @@ release_firmware(const struct firmware *fw) } } -/** - * register_firmware: - provide a firmware image for later usage - * @name: name of firmware image file - * @data: buffer pointer for the firmware image - * @size: size of the data buffer area - * - * Make sure that @data will be available by requesting firmware @name. - * - * Note: This will not be possible until some kind of persistence - * is available. - **/ -void -register_firmware(const char *name, const u8 *data, size_t size) -{ - /* This is meaningless without firmware caching, so until we - * decide if firmware caching is reasonable just leave it as a - * noop */ -} - /* Async support */ struct firmware_work { struct work_struct work; @@ -630,4 +608,3 @@ module_exit(firmware_class_exit); EXPORT_SYMBOL(release_firmware); EXPORT_SYMBOL(request_firmware); EXPORT_SYMBOL(request_firmware_nowait); -EXPORT_SYMBOL(register_firmware); diff --git a/include/linux/firmware.h b/include/linux/firmware.h index 2d716080be4a..33d8f2087b6e 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -19,5 +19,4 @@ int request_firmware_nowait( void (*cont)(const struct firmware *fw, void *context)); void release_firmware(const struct firmware *fw); -void register_firmware(const char *name, const u8 *data, size_t size); #endif -- cgit v1.2.3 From e984bb43f7450312ba66fe0e67a99efa6be3b246 Mon Sep 17 00:00:00 2001 From: Bob Picco Date: Sat, 20 May 2006 15:00:31 -0700 Subject: [PATCH] Align the node_mem_map endpoints to a MAX_ORDER boundary Andy added code to buddy allocator which does not require the zone's endpoints to be aligned to MAX_ORDER. An issue is that the buddy allocator requires the node_mem_map's endpoints to be MAX_ORDER aligned. Otherwise __page_find_buddy could compute a buddy not in node_mem_map for partial MAX_ORDER regions at zone's endpoints. page_is_buddy will detect that these pages at endpoints are not PG_buddy (they were zeroed out by bootmem allocator and not part of zone). Of course the negative here is we could waste a little memory but the positive is eliminating all the old checks for zone boundary conditions. SPARSEMEM won't encounter this issue because of MAX_ORDER size constraint when SPARSEMEM is configured. ia64 VIRTUAL_MEM_MAP doesn't need the logic either because the holes and endpoints are handled differently. This leaves checking alloc_remap and other arches which privately allocate for node_mem_map. Signed-off-by: Bob Picco Acked-by: Mel Gorman Cc: Dave Hansen Cc: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 1 + mm/page_alloc.c | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index b5c21122c299..36740354d4db 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -22,6 +22,7 @@ #else #define MAX_ORDER CONFIG_FORCE_MAX_ZONEORDER #endif +#define MAX_ORDER_NR_PAGES (1 << (MAX_ORDER - 1)) struct free_area { struct list_head free_list; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index bb3416932ab0..253a450c400d 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2125,14 +2125,22 @@ static void __init alloc_node_mem_map(struct pglist_data *pgdat) #ifdef CONFIG_FLAT_NODE_MEM_MAP /* ia64 gets its own node_mem_map, before this, without bootmem */ if (!pgdat->node_mem_map) { - unsigned long size; + unsigned long size, start, end; struct page *map; - size = (pgdat->node_spanned_pages + 1) * sizeof(struct page); + /* + * The zone's endpoints aren't required to be MAX_ORDER + * aligned but the node_mem_map endpoints must be in order + * for the buddy allocator to function correctly. + */ + start = pgdat->node_start_pfn & ~(MAX_ORDER_NR_PAGES - 1); + end = pgdat->node_start_pfn + pgdat->node_spanned_pages; + end = ALIGN(end, MAX_ORDER_NR_PAGES); + size = (end - start) * sizeof(struct page); map = alloc_remap(pgdat->node_id, size); if (!map) map = alloc_bootmem_node(pgdat, size); - pgdat->node_mem_map = map; + pgdat->node_mem_map = map + (pgdat->node_start_pfn - start); } #ifdef CONFIG_FLATMEM /* -- cgit v1.2.3 From 7dd1d9b85cfb63eebf48fa13d3c5d25a3deb3a25 Mon Sep 17 00:00:00 2001 From: Magnus Kessler Date: Mon, 22 May 2006 10:53:10 +0100 Subject: [AGPGART] VIA PT880 Ultra support. This patch enables agpgart on a Via "PT880 Ultra" based motherboard (Asus P4V800D-X). The PCI ID of the PT880 Ultra is 0x0308 instead of 0x0258 of the PT880. The patched via-agp passes testgart. Signed-off-by: Magnus Kessler Signed-off-by: Dave Jones --- drivers/char/agp/via-agp.c | 7 +++++++ include/linux/pci_ids.h | 1 + 2 files changed, 8 insertions(+) (limited to 'include/linux') diff --git a/drivers/char/agp/via-agp.c b/drivers/char/agp/via-agp.c index 97b0a890ba7f..b8ec25d17478 100644 --- a/drivers/char/agp/via-agp.c +++ b/drivers/char/agp/via-agp.c @@ -345,6 +345,12 @@ static struct agp_device_ids via_agp_device_ids[] __devinitdata = .chipset_name = "PT880", }, + /* PT880 Ultra */ + { + .device_id = PCI_DEVICE_ID_VIA_PT880ULTRA, + .chipset_name = "PT880 Ultra", + }, + /* PT890 */ { .device_id = PCI_DEVICE_ID_VIA_8783_0, @@ -511,6 +517,7 @@ static struct pci_device_id agp_via_pci_table[] = { ID(PCI_DEVICE_ID_VIA_8763_0), ID(PCI_DEVICE_ID_VIA_8378_0), ID(PCI_DEVICE_ID_VIA_PT880), + ID(PCI_DEVICE_ID_VIA_PT880ULTRA), ID(PCI_DEVICE_ID_VIA_8783_0), ID(PCI_DEVICE_ID_VIA_PX8X0_0), ID(PCI_DEVICE_ID_VIA_3269_0), diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index d6fe048376ab..590dc6dca315 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1231,6 +1231,7 @@ #define PCI_DEVICE_ID_VIA_8380_0 0x0204 #define PCI_DEVICE_ID_VIA_3238_0 0x0238 #define PCI_DEVICE_ID_VIA_PT880 0x0258 +#define PCI_DEVICE_ID_VIA_PT880ULTRA 0x0308 #define PCI_DEVICE_ID_VIA_PX8X0_0 0x0259 #define PCI_DEVICE_ID_VIA_3269_0 0x0269 #define PCI_DEVICE_ID_VIA_K8T800PRO_0 0x0282 -- cgit v1.2.3 From e46e490368f87032a6e54969194413339b35a385 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 22 May 2006 22:35:24 -0700 Subject: [PATCH] sys_sync_file_range(): move exported flags outside __KERNEL__ These flags are needed by userspace - move them outside __KERNEL__ (Pointed out by dwmw2) Cc: David Woodhouse Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/fs.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/fs.h b/include/linux/fs.h index 3de2bfb2410f..f813bc8266aa 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -213,6 +213,10 @@ extern int dir_notify_enable; #define FIBMAP _IO(0x00,1) /* bmap access */ #define FIGETBSZ _IO(0x00,2) /* get the block size used for bmap */ +#define SYNC_FILE_RANGE_WAIT_BEFORE 1 +#define SYNC_FILE_RANGE_WRITE 2 +#define SYNC_FILE_RANGE_WAIT_AFTER 4 + #ifdef __KERNEL__ #include @@ -758,9 +762,6 @@ extern int fcntl_setlease(unsigned int fd, struct file *filp, long arg); extern int fcntl_getlease(struct file *filp); /* fs/sync.c */ -#define SYNC_FILE_RANGE_WAIT_BEFORE 1 -#define SYNC_FILE_RANGE_WRITE 2 -#define SYNC_FILE_RANGE_WAIT_AFTER 4 extern int do_sync_file_range(struct file *file, loff_t offset, loff_t endbyte, unsigned int flags); -- cgit v1.2.3 From 0f0410823792ae0ecb45f2578598b115835ffdbb Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 23 May 2006 07:46:40 -0700 Subject: [PATCH] powerpc: wire up sys_[gs]et_robust_list Signed-off-by: David Woodhouse Cc: Benjamin Herrenschmidt Acked-by: Paul Mackerras Cc: Arnd Bergmann Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/powerpc/kernel/systbl.S | 2 ++ arch/powerpc/platforms/cell/spu_callbacks.c | 2 ++ include/asm-powerpc/unistd.h | 4 +++- include/linux/syscalls.h | 6 ++++++ 4 files changed, 13 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/arch/powerpc/kernel/systbl.S b/arch/powerpc/kernel/systbl.S index cf56a1d499ff..26ed1f5ef16e 100644 --- a/arch/powerpc/kernel/systbl.S +++ b/arch/powerpc/kernel/systbl.S @@ -338,6 +338,8 @@ SYSCALL(symlinkat) SYSCALL(readlinkat) SYSCALL(fchmodat) SYSCALL(faccessat) +COMPAT_SYS(get_robust_list) +COMPAT_SYS(set_robust_list) /* * please add new calls to arch/powerpc/platforms/cell/spu_callbacks.c diff --git a/arch/powerpc/platforms/cell/spu_callbacks.c b/arch/powerpc/platforms/cell/spu_callbacks.c index 5a0f9e3beb24..b47fcc5ddb78 100644 --- a/arch/powerpc/platforms/cell/spu_callbacks.c +++ b/arch/powerpc/platforms/cell/spu_callbacks.c @@ -333,6 +333,8 @@ void *spu_syscall_table[] = { [__NR_readlinkat] sys_readlinkat, [__NR_fchmodat] sys_fchmodat, [__NR_faccessat] sys_faccessat, + [__NR_get_robust_list] sys_get_robust_list, + [__NR_set_robust_list] sys_set_robust_list, }; long spu_sys_callback(struct spu_syscall_block *s) diff --git a/include/asm-powerpc/unistd.h b/include/asm-powerpc/unistd.h index 908acb44cb8a..edde2462bf52 100644 --- a/include/asm-powerpc/unistd.h +++ b/include/asm-powerpc/unistd.h @@ -321,8 +321,10 @@ #define __NR_readlinkat 296 #define __NR_fchmodat 297 #define __NR_faccessat 298 +#define __NR_get_robust_list 299 +#define __NR_set_robust_list 300 -#define __NR_syscalls 299 +#define __NR_syscalls 301 #ifdef __KERNEL__ #define __NR__exit __NR_exit diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 3996960fc565..60d49e5456e7 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -52,6 +52,7 @@ struct utimbuf; struct mq_attr; struct compat_stat; struct compat_timeval; +struct robust_list_head; #include #include @@ -581,5 +582,10 @@ 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, unsigned int flags); +asmlinkage long sys_get_robust_list(int pid, + struct robust_list_head __user **head_ptr, + size_t __user *len_ptr); +asmlinkage long sys_set_robust_list(struct robust_list_head __user *head, + size_t len); #endif -- cgit v1.2.3 From f39b25bed373cf11a2c0490bee8b0ac430aadff4 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 29 May 2006 23:27:39 -0400 Subject: Input: add KEY_BATTERY keycode Signed-off-by: Dmitry Torokhov --- include/linux/input.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/input.h b/include/linux/input.h index 50e338d2ffda..14036ee0749b 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -345,6 +345,8 @@ struct input_absinfo { #define KEY_SAVE 234 #define KEY_DOCUMENTS 235 +#define KEY_BATTERY 236 + #define KEY_UNKNOWN 240 #define BTN_MISC 0x100 -- cgit v1.2.3 From ed8f9e2f047de5d9b791e390269f230a101a6a4b Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Mon, 29 May 2006 23:31:03 -0400 Subject: Input: change from numbered to named switches Remove the numbered SW_* entries from the input system and assign names to the existing users. Signed-off-by: Richard Purdie Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/corgikbd.c | 12 ++++++------ drivers/input/keyboard/spitzkbd.c | 12 ++++++------ include/linux/input.h | 11 +++-------- 3 files changed, 15 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/keyboard/corgikbd.c b/drivers/input/keyboard/corgikbd.c index 96c6bf77248a..1f0e720267d7 100644 --- a/drivers/input/keyboard/corgikbd.c +++ b/drivers/input/keyboard/corgikbd.c @@ -245,9 +245,9 @@ static void corgikbd_hinge_timer(unsigned long data) if (hinge_count >= HINGE_STABLE_COUNT) { spin_lock_irqsave(&corgikbd_data->lock, flags); - input_report_switch(corgikbd_data->input, SW_0, ((sharpsl_hinge_state & CORGI_SCP_SWA) != 0)); - input_report_switch(corgikbd_data->input, SW_1, ((sharpsl_hinge_state & CORGI_SCP_SWB) != 0)); - input_report_switch(corgikbd_data->input, SW_2, (READ_GPIO_BIT(CORGI_GPIO_AK_INT) != 0)); + input_report_switch(corgikbd_data->input, SW_LID, ((sharpsl_hinge_state & CORGI_SCP_SWA) != 0)); + input_report_switch(corgikbd_data->input, SW_TABLET_MODE, ((sharpsl_hinge_state & CORGI_SCP_SWB) != 0)); + input_report_switch(corgikbd_data->input, SW_HEADPHONE_INSERT, (READ_GPIO_BIT(CORGI_GPIO_AK_INT) != 0)); input_sync(corgikbd_data->input); spin_unlock_irqrestore(&corgikbd_data->lock, flags); @@ -340,9 +340,9 @@ static int __init corgikbd_probe(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(corgikbd_keycode); i++) set_bit(corgikbd->keycode[i], input_dev->keybit); clear_bit(0, input_dev->keybit); - set_bit(SW_0, input_dev->swbit); - set_bit(SW_1, input_dev->swbit); - set_bit(SW_2, input_dev->swbit); + set_bit(SW_LID, input_dev->swbit); + set_bit(SW_TABLET_MODE, input_dev->swbit); + set_bit(SW_HEADPHONE_INSERT, input_dev->swbit); input_register_device(corgikbd->input); diff --git a/drivers/input/keyboard/spitzkbd.c b/drivers/input/keyboard/spitzkbd.c index 1d238a9d52d6..c5d03fb77bcb 100644 --- a/drivers/input/keyboard/spitzkbd.c +++ b/drivers/input/keyboard/spitzkbd.c @@ -299,9 +299,9 @@ static void spitzkbd_hinge_timer(unsigned long data) if (hinge_count >= HINGE_STABLE_COUNT) { spin_lock_irqsave(&spitzkbd_data->lock, flags); - input_report_switch(spitzkbd_data->input, SW_0, ((GPLR(SPITZ_GPIO_SWA) & GPIO_bit(SPITZ_GPIO_SWA)) != 0)); - input_report_switch(spitzkbd_data->input, SW_1, ((GPLR(SPITZ_GPIO_SWB) & GPIO_bit(SPITZ_GPIO_SWB)) != 0)); - input_report_switch(spitzkbd_data->input, SW_2, ((GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT)) != 0)); + input_report_switch(spitzkbd_data->input, SW_LID, ((GPLR(SPITZ_GPIO_SWA) & GPIO_bit(SPITZ_GPIO_SWA)) != 0)); + input_report_switch(spitzkbd_data->input, SW_TABLET_MODE, ((GPLR(SPITZ_GPIO_SWB) & GPIO_bit(SPITZ_GPIO_SWB)) != 0)); + input_report_switch(spitzkbd_data->input, SW_HEADPHONE_INSERT, ((GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT)) != 0)); input_sync(spitzkbd_data->input); spin_unlock_irqrestore(&spitzkbd_data->lock, flags); @@ -398,9 +398,9 @@ static int __init spitzkbd_probe(struct platform_device *dev) for (i = 0; i < ARRAY_SIZE(spitzkbd_keycode); i++) set_bit(spitzkbd->keycode[i], input_dev->keybit); clear_bit(0, input_dev->keybit); - set_bit(SW_0, input_dev->swbit); - set_bit(SW_1, input_dev->swbit); - set_bit(SW_2, input_dev->swbit); + set_bit(SW_LID, input_dev->swbit); + set_bit(SW_TABLET_MODE, input_dev->swbit); + set_bit(SW_HEADPHONE_INSERT, input_dev->swbit); input_register_device(input_dev); diff --git a/include/linux/input.h b/include/linux/input.h index 14036ee0749b..ce1a756c4c30 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -579,14 +579,9 @@ struct input_absinfo { * Switch events */ -#define SW_0 0x00 -#define SW_1 0x01 -#define SW_2 0x02 -#define SW_3 0x03 -#define SW_4 0x04 -#define SW_5 0x05 -#define SW_6 0x06 -#define SW_7 0x07 +#define SW_LID 0x00 /* set = lid shut */ +#define SW_TABLET_MODE 0x01 /* set = tablet mode */ +#define SW_HEADPHONE_INSERT 0x02 /* set = inserted */ #define SW_MAX 0x0f /* -- cgit v1.2.3 From 760f1fce030ccc620ec430a8aff8fc604e7891ed Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 30 May 2006 21:26:03 -0700 Subject: [PATCH] revert "swsusp add check for suspension of X controlled devices" From: Andrew Morton Revert commit ff4da2e262d2509fe1bacff70dd00934be569c66. It broke APM suspend, probably because APM doesn't switch back to a VT when suspending. Tracked down by Matt Mackall Rafael sayeth: "It only fixed the theoretical issue that a quick-handed user could switch to X after processes have been frozen and before the devices are suspended. With the current userland suspend tools it shouldn't be necessary." Cc: Pavel Machek Cc: "Rafael J. Wysocki" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/power/suspend.c | 5 +---- drivers/char/vt.c | 8 -------- include/linux/vt_kern.h | 5 ----- 3 files changed, 1 insertion(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index 662209d3f42d..2a769cc6f5f9 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -8,7 +8,6 @@ * */ -#include #include #include #include @@ -66,6 +65,7 @@ int suspend_device(struct device * dev, pm_message_t state) return error; } + /** * device_suspend - Save state and stop all devices in system. * @state: Power state to put each device in. @@ -85,9 +85,6 @@ int device_suspend(pm_message_t state) { int error = 0; - if (!is_console_suspend_safe()) - return -EINVAL; - down(&dpm_sem); down(&dpm_list_sem); while (!list_empty(&dpm_active) && error == 0) { diff --git a/drivers/char/vt.c b/drivers/char/vt.c index acc5d47844eb..6c94879e0b99 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -3238,14 +3238,6 @@ void vcs_scr_writew(struct vc_data *vc, u16 val, u16 *org) } } -int is_console_suspend_safe(void) -{ - /* It is unsafe to suspend devices while X has control of the - * hardware. Make sure we are running on a kernel-controlled console. - */ - return vc_cons[fg_console].d->vc_mode == KD_TEXT; -} - /* * Visible symbols for modules */ diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 530ae3f4248c..fab5aed8ca31 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -73,11 +73,6 @@ int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc); int vt_waitactive(int vt); void change_console(struct vc_data *new_vc); void reset_vc(struct vc_data *vc); -#ifdef CONFIG_VT -int is_console_suspend_safe(void); -#else -static inline int is_console_suspend_safe(void) { return 1; } -#endif /* * vc_screen.c shares this temporary buffer with the console write code so that -- cgit v1.2.3 From 93ff66bf1ef29881dffd6fdc344555dab03cdb42 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Sun, 4 Jun 2006 02:51:29 -0700 Subject: [PATCH] Sparsemem build fix From: Ralf Baechle uses PAGE_SIZE, PAGE_SHIFT from without including that header itself. For some sparsemem configurations this may result in build errors like: CC init/initramfs.o In file included from include/linux/gfp.h:4, from include/linux/slab.h:15, from include/linux/percpu.h:4, from include/linux/rcupdate.h:41, from include/linux/dcache.h:10, from include/linux/fs.h:226, from init/initramfs.c:2: include/linux/mmzone.h:498:22: warning: "PAGE_SHIFT" is not defined In file included from include/linux/gfp.h:4, from include/linux/slab.h:15, from include/linux/percpu.h:4, from include/linux/rcupdate.h:41, from include/linux/dcache.h:10, from include/linux/fs.h:226, from init/initramfs.c:2: include/linux/mmzone.h:526: error: `PAGE_SIZE' undeclared here (not in a function) include/linux/mmzone.h: In function `__pfn_to_section': include/linux/mmzone.h:573: error: `PAGE_SHIFT' undeclared (first use in this function) include/linux/mmzone.h:573: error: (Each undeclared identifier is reported only once include/linux/mmzone.h:573: error: for each function it appears in.) include/linux/mmzone.h: In function `pfn_valid': include/linux/mmzone.h:578: error: `PAGE_SHIFT' undeclared (first use in this function) make[1]: *** [init/initramfs.o] Error 1 make: *** [init] Error 2 Signed-off-by: Ralf Baechle Seems-reasonable-to: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 36740354d4db..2d8337150493 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -15,6 +15,7 @@ #include #include #include +#include /* Free memory management - zoned buddy allocator. */ #ifndef CONFIG_FORCE_MAX_ZONEORDER -- cgit v1.2.3 From 2d7b20c1884777e66009be1a533641c19c4705f6 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 4 Jun 2006 02:51:42 -0700 Subject: [PATCH] m48t86: ia64 build fix From: Andrew Morton drivers/rtc/rtc-m48t86.c: In function `m48t86_rtc_read_time': drivers/rtc/rtc-m48t86.c:51: error: structure has no member named `ia64_mv' drivers/rtc/rtc-m48t86.c:55: error: structure has no member named `ia64_mv' drivers/rtc/rtc-m48t86.c:56: error: structure has no member named `ia64_mv' drivers/rtc/rtc-m48t86.c:57: error: structure has no member named `ia64_mv' drivers/rtc/rtc-m48t86.c:58: error: structure has no member named `ia64_mv' drivers/rtc/rtc-m48t86.c:60: error: structure has no member named `ia64_mv' readb() and writeb() are macros on ia64. Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-m48t86.c | 72 ++++++++++++++++++++++++------------------------ include/linux/m48t86.h | 4 +-- 2 files changed, 38 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c index f6e7ee04f3dc..8c0d1a6739ad 100644 --- a/drivers/rtc/rtc-m48t86.c +++ b/drivers/rtc/rtc-m48t86.c @@ -48,33 +48,33 @@ static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm) struct platform_device *pdev = to_platform_device(dev); struct m48t86_ops *ops = pdev->dev.platform_data; - reg = ops->readb(M48T86_REG_B); + reg = ops->readbyte(M48T86_REG_B); if (reg & M48T86_REG_B_DM) { /* data (binary) mode */ - tm->tm_sec = ops->readb(M48T86_REG_SEC); - tm->tm_min = ops->readb(M48T86_REG_MIN); - tm->tm_hour = ops->readb(M48T86_REG_HOUR) & 0x3F; - tm->tm_mday = ops->readb(M48T86_REG_DOM); + tm->tm_sec = ops->readbyte(M48T86_REG_SEC); + tm->tm_min = ops->readbyte(M48T86_REG_MIN); + tm->tm_hour = ops->readbyte(M48T86_REG_HOUR) & 0x3F; + tm->tm_mday = ops->readbyte(M48T86_REG_DOM); /* tm_mon is 0-11 */ - tm->tm_mon = ops->readb(M48T86_REG_MONTH) - 1; - tm->tm_year = ops->readb(M48T86_REG_YEAR) + 100; - tm->tm_wday = ops->readb(M48T86_REG_DOW); + tm->tm_mon = ops->readbyte(M48T86_REG_MONTH) - 1; + tm->tm_year = ops->readbyte(M48T86_REG_YEAR) + 100; + tm->tm_wday = ops->readbyte(M48T86_REG_DOW); } else { /* bcd mode */ - tm->tm_sec = BCD2BIN(ops->readb(M48T86_REG_SEC)); - tm->tm_min = BCD2BIN(ops->readb(M48T86_REG_MIN)); - tm->tm_hour = BCD2BIN(ops->readb(M48T86_REG_HOUR) & 0x3F); - tm->tm_mday = BCD2BIN(ops->readb(M48T86_REG_DOM)); + tm->tm_sec = BCD2BIN(ops->readbyte(M48T86_REG_SEC)); + tm->tm_min = BCD2BIN(ops->readbyte(M48T86_REG_MIN)); + tm->tm_hour = BCD2BIN(ops->readbyte(M48T86_REG_HOUR) & 0x3F); + tm->tm_mday = BCD2BIN(ops->readbyte(M48T86_REG_DOM)); /* tm_mon is 0-11 */ - tm->tm_mon = BCD2BIN(ops->readb(M48T86_REG_MONTH)) - 1; - tm->tm_year = BCD2BIN(ops->readb(M48T86_REG_YEAR)) + 100; - tm->tm_wday = BCD2BIN(ops->readb(M48T86_REG_DOW)); + tm->tm_mon = BCD2BIN(ops->readbyte(M48T86_REG_MONTH)) - 1; + tm->tm_year = BCD2BIN(ops->readbyte(M48T86_REG_YEAR)) + 100; + tm->tm_wday = BCD2BIN(ops->readbyte(M48T86_REG_DOW)); } /* correct the hour if the clock is in 12h mode */ if (!(reg & M48T86_REG_B_H24)) - if (ops->readb(M48T86_REG_HOUR) & 0x80) + if (ops->readbyte(M48T86_REG_HOUR) & 0x80) tm->tm_hour += 12; return 0; @@ -86,35 +86,35 @@ static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm) struct platform_device *pdev = to_platform_device(dev); struct m48t86_ops *ops = pdev->dev.platform_data; - reg = ops->readb(M48T86_REG_B); + reg = ops->readbyte(M48T86_REG_B); /* update flag and 24h mode */ reg |= M48T86_REG_B_SET | M48T86_REG_B_H24; - ops->writeb(reg, M48T86_REG_B); + ops->writebyte(reg, M48T86_REG_B); if (reg & M48T86_REG_B_DM) { /* data (binary) mode */ - ops->writeb(tm->tm_sec, M48T86_REG_SEC); - ops->writeb(tm->tm_min, M48T86_REG_MIN); - ops->writeb(tm->tm_hour, M48T86_REG_HOUR); - ops->writeb(tm->tm_mday, M48T86_REG_DOM); - ops->writeb(tm->tm_mon + 1, M48T86_REG_MONTH); - ops->writeb(tm->tm_year % 100, M48T86_REG_YEAR); - ops->writeb(tm->tm_wday, M48T86_REG_DOW); + ops->writebyte(tm->tm_sec, M48T86_REG_SEC); + ops->writebyte(tm->tm_min, M48T86_REG_MIN); + ops->writebyte(tm->tm_hour, M48T86_REG_HOUR); + ops->writebyte(tm->tm_mday, M48T86_REG_DOM); + ops->writebyte(tm->tm_mon + 1, M48T86_REG_MONTH); + ops->writebyte(tm->tm_year % 100, M48T86_REG_YEAR); + ops->writebyte(tm->tm_wday, M48T86_REG_DOW); } else { /* bcd mode */ - ops->writeb(BIN2BCD(tm->tm_sec), M48T86_REG_SEC); - ops->writeb(BIN2BCD(tm->tm_min), M48T86_REG_MIN); - ops->writeb(BIN2BCD(tm->tm_hour), M48T86_REG_HOUR); - ops->writeb(BIN2BCD(tm->tm_mday), M48T86_REG_DOM); - ops->writeb(BIN2BCD(tm->tm_mon + 1), M48T86_REG_MONTH); - ops->writeb(BIN2BCD(tm->tm_year % 100), M48T86_REG_YEAR); - ops->writeb(BIN2BCD(tm->tm_wday), M48T86_REG_DOW); + ops->writebyte(BIN2BCD(tm->tm_sec), M48T86_REG_SEC); + ops->writebyte(BIN2BCD(tm->tm_min), M48T86_REG_MIN); + ops->writebyte(BIN2BCD(tm->tm_hour), M48T86_REG_HOUR); + ops->writebyte(BIN2BCD(tm->tm_mday), M48T86_REG_DOM); + ops->writebyte(BIN2BCD(tm->tm_mon + 1), M48T86_REG_MONTH); + ops->writebyte(BIN2BCD(tm->tm_year % 100), M48T86_REG_YEAR); + ops->writebyte(BIN2BCD(tm->tm_wday), M48T86_REG_DOW); } /* update ended */ reg &= ~M48T86_REG_B_SET; - ops->writeb(reg, M48T86_REG_B); + ops->writebyte(reg, M48T86_REG_B); return 0; } @@ -125,12 +125,12 @@ static int m48t86_rtc_proc(struct device *dev, struct seq_file *seq) struct platform_device *pdev = to_platform_device(dev); struct m48t86_ops *ops = pdev->dev.platform_data; - reg = ops->readb(M48T86_REG_B); + reg = ops->readbyte(M48T86_REG_B); seq_printf(seq, "mode\t\t: %s\n", (reg & M48T86_REG_B_DM) ? "binary" : "bcd"); - reg = ops->readb(M48T86_REG_D); + reg = ops->readbyte(M48T86_REG_D); seq_printf(seq, "battery\t\t: %s\n", (reg & M48T86_REG_D_VRT) ? "ok" : "exhausted"); @@ -157,7 +157,7 @@ static int __devinit m48t86_rtc_probe(struct platform_device *dev) platform_set_drvdata(dev, rtc); /* read battery status */ - reg = ops->readb(M48T86_REG_D); + reg = ops->readbyte(M48T86_REG_D); dev_info(&dev->dev, "battery %s\n", (reg & M48T86_REG_D_VRT) ? "ok" : "exhausted"); diff --git a/include/linux/m48t86.h b/include/linux/m48t86.h index 9065199319d0..915d6b4f0f89 100644 --- a/include/linux/m48t86.h +++ b/include/linux/m48t86.h @@ -11,6 +11,6 @@ struct m48t86_ops { - void (*writeb)(unsigned char value, unsigned long addr); - unsigned char (*readb)(unsigned long addr); + void (*writebyte)(unsigned char value, unsigned long addr); + unsigned char (*readbyte)(unsigned long addr); }; -- cgit v1.2.3 From 45b35a5ced474b9fbbbfcfd5cf346c432d28d9fd Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Thu, 8 Jun 2006 00:43:41 -0700 Subject: [PATCH] Fix mempolicy.h build error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: Ralf Baechle uses struct mm_struct and relies on a definition or declaration somehow magically being dragged in which may result in a build: [...] CC mm/mempolicy.o In file included from mm/mempolicy.c:69: include/linux/mempolicy.h:150: warning: ‘struct mm_struct’ declared inside parameter list include/linux/mempolicy.h:150: warning: its scope is only this definition or declaration, which is probably not what you want include/linux/mempolicy.h:175: warning: ‘struct mm_struct’ declared inside parameter list mm/mempolicy.c:622: error: conflicting types for ‘do_migrate_pages’ include/linux/mempolicy.h:175: error: previous declaration of ‘do_migrate_pages’ was here mm/mempolicy.c:1661: error: conflicting types for ‘mpol_rebind_mm’ include/linux/mempolicy.h:150: error: previous declaration of ‘mpol_rebind_mm’ was here make[1]: *** [mm/mempolicy.o] Error 1 make: *** [mm] Error 2 [ralf@denk linux-ip35]$ Including is a step into direction of include hell so fixed by adding a forward declaration of struct mm_struct instead. Signed-off-by: Ralf Baechle Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mempolicy.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/mempolicy.h b/include/linux/mempolicy.h index 6a7621b2b12b..f5fdca1d67e6 100644 --- a/include/linux/mempolicy.h +++ b/include/linux/mempolicy.h @@ -36,6 +36,7 @@ #include struct vm_area_struct; +struct mm_struct; #ifdef CONFIG_NUMA -- cgit v1.2.3 From bc1c116974a5c3f498112a6f175d3e4a8cd5bdbc Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 8 Jun 2006 08:49:06 +0200 Subject: [PATCH] elevator switching race There's a race between shutting down one io scheduler and firing up the next, in which a new io could enter and cause the io scheduler to be invoked with bad or NULL data. To fix this, we need to maintain the queue lock for a bit longer. Unfortunately we cannot do that, since the elevator init requires to be run without the lock held. This isn't easily fixable, without also changing the mempool API. So split the initialization into two parts, and alloc-init operation and an attach operation. Then we can preallocate the io scheduler and related structures, and run the attach inside the lock after we detach the old one. This patch has survived 30 minutes of 1 second io scheduler switching with a very busy io load. Signed-off-by: Jens Axboe Signed-off-by: Linus Torvalds --- block/as-iosched.c | 13 ++++++------ block/cfq-iosched.c | 10 ++++----- block/deadline-iosched.c | 13 ++++++------ block/elevator.c | 55 ++++++++++++++++++++++++++++++------------------ block/noop-iosched.c | 7 +++--- include/linux/elevator.h | 2 +- 6 files changed, 54 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/block/as-iosched.c b/block/as-iosched.c index e25a5d79ab27..a7caf35ca0c2 100644 --- a/block/as-iosched.c +++ b/block/as-iosched.c @@ -1648,17 +1648,17 @@ static void as_exit_queue(elevator_t *e) * initialize elevator private data (as_data), and alloc a arq for * each request on the free lists */ -static int as_init_queue(request_queue_t *q, elevator_t *e) +static void *as_init_queue(request_queue_t *q, elevator_t *e) { struct as_data *ad; int i; if (!arq_pool) - return -ENOMEM; + return NULL; ad = kmalloc_node(sizeof(*ad), GFP_KERNEL, q->node); if (!ad) - return -ENOMEM; + return NULL; memset(ad, 0, sizeof(*ad)); ad->q = q; /* Identify what queue the data belongs to */ @@ -1667,7 +1667,7 @@ static int as_init_queue(request_queue_t *q, elevator_t *e) GFP_KERNEL, q->node); if (!ad->hash) { kfree(ad); - return -ENOMEM; + return NULL; } ad->arq_pool = mempool_create_node(BLKDEV_MIN_RQ, mempool_alloc_slab, @@ -1675,7 +1675,7 @@ static int as_init_queue(request_queue_t *q, elevator_t *e) if (!ad->arq_pool) { kfree(ad->hash); kfree(ad); - return -ENOMEM; + return NULL; } /* anticipatory scheduling helpers */ @@ -1696,14 +1696,13 @@ static int as_init_queue(request_queue_t *q, elevator_t *e) ad->antic_expire = default_antic_expire; ad->batch_expire[REQ_SYNC] = default_read_batch_expire; ad->batch_expire[REQ_ASYNC] = default_write_batch_expire; - e->elevator_data = ad; ad->current_batch_expires = jiffies + ad->batch_expire[REQ_SYNC]; ad->write_batch_count = ad->batch_expire[REQ_ASYNC] / 10; if (ad->write_batch_count < 2) ad->write_batch_count = 2; - return 0; + return ad; } /* diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 8e9d84825e1c..a46d030e092a 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -2251,14 +2251,14 @@ static void cfq_exit_queue(elevator_t *e) kfree(cfqd); } -static int cfq_init_queue(request_queue_t *q, elevator_t *e) +static void *cfq_init_queue(request_queue_t *q, elevator_t *e) { struct cfq_data *cfqd; int i; cfqd = kmalloc(sizeof(*cfqd), GFP_KERNEL); if (!cfqd) - return -ENOMEM; + return NULL; memset(cfqd, 0, sizeof(*cfqd)); @@ -2288,8 +2288,6 @@ static int cfq_init_queue(request_queue_t *q, elevator_t *e) for (i = 0; i < CFQ_QHASH_ENTRIES; i++) INIT_HLIST_HEAD(&cfqd->cfq_hash[i]); - e->elevator_data = cfqd; - cfqd->queue = q; cfqd->max_queued = q->nr_requests / 4; @@ -2316,14 +2314,14 @@ static int cfq_init_queue(request_queue_t *q, elevator_t *e) cfqd->cfq_slice_async_rq = cfq_slice_async_rq; cfqd->cfq_slice_idle = cfq_slice_idle; - return 0; + return cfqd; out_crqpool: kfree(cfqd->cfq_hash); out_cfqhash: kfree(cfqd->crq_hash); out_crqhash: kfree(cfqd); - return -ENOMEM; + return NULL; } static void cfq_slab_kill(void) diff --git a/block/deadline-iosched.c b/block/deadline-iosched.c index 399fa1e60e1f..3bd0415a9828 100644 --- a/block/deadline-iosched.c +++ b/block/deadline-iosched.c @@ -613,24 +613,24 @@ static void deadline_exit_queue(elevator_t *e) * initialize elevator private data (deadline_data), and alloc a drq for * each request on the free lists */ -static int deadline_init_queue(request_queue_t *q, elevator_t *e) +static void *deadline_init_queue(request_queue_t *q, elevator_t *e) { struct deadline_data *dd; int i; if (!drq_pool) - return -ENOMEM; + return NULL; dd = kmalloc_node(sizeof(*dd), GFP_KERNEL, q->node); if (!dd) - return -ENOMEM; + return NULL; memset(dd, 0, sizeof(*dd)); dd->hash = kmalloc_node(sizeof(struct list_head)*DL_HASH_ENTRIES, GFP_KERNEL, q->node); if (!dd->hash) { kfree(dd); - return -ENOMEM; + return NULL; } dd->drq_pool = mempool_create_node(BLKDEV_MIN_RQ, mempool_alloc_slab, @@ -638,7 +638,7 @@ static int deadline_init_queue(request_queue_t *q, elevator_t *e) if (!dd->drq_pool) { kfree(dd->hash); kfree(dd); - return -ENOMEM; + return NULL; } for (i = 0; i < DL_HASH_ENTRIES; i++) @@ -653,8 +653,7 @@ static int deadline_init_queue(request_queue_t *q, elevator_t *e) dd->writes_starved = writes_starved; dd->front_merges = 1; dd->fifo_batch = fifo_batch; - e->elevator_data = dd; - return 0; + return dd; } static void deadline_put_request(request_queue_t *q, struct request *rq) diff --git a/block/elevator.c b/block/elevator.c index 8768a367fdde..a0afdd317cef 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -121,16 +121,16 @@ static struct elevator_type *elevator_get(const char *name) return e; } -static int elevator_attach(request_queue_t *q, struct elevator_queue *eq) +static void *elevator_init_queue(request_queue_t *q, struct elevator_queue *eq) { - int ret = 0; + return eq->ops->elevator_init_fn(q, eq); +} +static void elevator_attach(request_queue_t *q, struct elevator_queue *eq, + void *data) +{ q->elevator = eq; - - if (eq->ops->elevator_init_fn) - ret = eq->ops->elevator_init_fn(q, eq); - - return ret; + eq->elevator_data = data; } static char chosen_elevator[16]; @@ -181,6 +181,7 @@ int elevator_init(request_queue_t *q, char *name) struct elevator_type *e = NULL; struct elevator_queue *eq; int ret = 0; + void *data; INIT_LIST_HEAD(&q->queue_head); q->last_merge = NULL; @@ -202,10 +203,13 @@ int elevator_init(request_queue_t *q, char *name) if (!eq) return -ENOMEM; - ret = elevator_attach(q, eq); - if (ret) + data = elevator_init_queue(q, eq); + if (!data) { kobject_put(&eq->kobj); + return -ENOMEM; + } + elevator_attach(q, eq, data); return ret; } @@ -722,13 +726,16 @@ int elv_register_queue(struct request_queue *q) return error; } +static void __elv_unregister_queue(elevator_t *e) +{ + kobject_uevent(&e->kobj, KOBJ_REMOVE); + kobject_del(&e->kobj); +} + void elv_unregister_queue(struct request_queue *q) { - if (q) { - elevator_t *e = q->elevator; - kobject_uevent(&e->kobj, KOBJ_REMOVE); - kobject_del(&e->kobj); - } + if (q) + __elv_unregister_queue(q->elevator); } int elv_register(struct elevator_type *e) @@ -780,6 +787,7 @@ EXPORT_SYMBOL_GPL(elv_unregister); static int elevator_switch(request_queue_t *q, struct elevator_type *new_e) { elevator_t *old_elevator, *e; + void *data; /* * Allocate new elevator @@ -788,6 +796,12 @@ static int elevator_switch(request_queue_t *q, struct elevator_type *new_e) if (!e) return 0; + data = elevator_init_queue(q, e); + if (!data) { + kobject_put(&e->kobj); + return 0; + } + /* * Turn on BYPASS and drain all requests w/ elevator private data */ @@ -806,19 +820,19 @@ static int elevator_switch(request_queue_t *q, struct elevator_type *new_e) elv_drain_elevator(q); } - spin_unlock_irq(q->queue_lock); - /* - * unregister old elevator data + * Remember old elevator. */ - elv_unregister_queue(q); old_elevator = q->elevator; /* * attach and start new elevator */ - if (elevator_attach(q, e)) - goto fail; + elevator_attach(q, e, data); + + spin_unlock_irq(q->queue_lock); + + __elv_unregister_queue(old_elevator); if (elv_register_queue(q)) goto fail_register; @@ -837,7 +851,6 @@ fail_register: */ elevator_exit(e); e = NULL; -fail: q->elevator = old_elevator; elv_register_queue(q); clear_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags); diff --git a/block/noop-iosched.c b/block/noop-iosched.c index f370e4a7fe6d..56a7c620574f 100644 --- a/block/noop-iosched.c +++ b/block/noop-iosched.c @@ -65,16 +65,15 @@ noop_latter_request(request_queue_t *q, struct request *rq) return list_entry(rq->queuelist.next, struct request, queuelist); } -static int noop_init_queue(request_queue_t *q, elevator_t *e) +static void *noop_init_queue(request_queue_t *q, elevator_t *e) { struct noop_data *nd; nd = kmalloc(sizeof(*nd), GFP_KERNEL); if (!nd) - return -ENOMEM; + return NULL; INIT_LIST_HEAD(&nd->queue); - e->elevator_data = nd; - return 0; + return nd; } static void noop_exit_queue(elevator_t *e) diff --git a/include/linux/elevator.h b/include/linux/elevator.h index ad133fcfb239..1713ace808bf 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -21,7 +21,7 @@ typedef void (elevator_put_req_fn) (request_queue_t *, struct request *); typedef void (elevator_activate_req_fn) (request_queue_t *, struct request *); typedef void (elevator_deactivate_req_fn) (request_queue_t *, struct request *); -typedef int (elevator_init_fn) (request_queue_t *, elevator_t *); +typedef void *(elevator_init_fn) (request_queue_t *, elevator_t *); typedef void (elevator_exit_fn) (elevator_t *); struct elevator_ops -- cgit v1.2.3